@agent-inspect/mcp-server 6.1.0 → 6.4.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/dist/index.cjs +1216 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +1215 -46
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import readline from 'readline';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
4
|
-
import 'crypto';
|
|
4
|
+
import crypto from 'crypto';
|
|
5
5
|
import { readdir, stat, readFile } from 'fs/promises';
|
|
6
6
|
import os from 'os';
|
|
7
7
|
import 'nanoid';
|
|
@@ -36,6 +36,183 @@ function extractCorrelationMetadata(record) {
|
|
|
36
36
|
}
|
|
37
37
|
return found ? out : void 0;
|
|
38
38
|
}
|
|
39
|
+
var DEFAULT_REDACT_KEYS = [
|
|
40
|
+
"authorization",
|
|
41
|
+
"cookie",
|
|
42
|
+
"token",
|
|
43
|
+
"apiKey",
|
|
44
|
+
"password",
|
|
45
|
+
"secret",
|
|
46
|
+
"email"
|
|
47
|
+
];
|
|
48
|
+
function isRecord(v) {
|
|
49
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
50
|
+
}
|
|
51
|
+
function toKey(s) {
|
|
52
|
+
return s.toLowerCase();
|
|
53
|
+
}
|
|
54
|
+
function stableHash(value) {
|
|
55
|
+
const h = crypto.createHash("sha256").update(value, "utf8").digest("hex");
|
|
56
|
+
return h.slice(0, 8);
|
|
57
|
+
}
|
|
58
|
+
function compileRules(rules, extraKeys) {
|
|
59
|
+
const out = /* @__PURE__ */ new Map();
|
|
60
|
+
const set = (r) => {
|
|
61
|
+
const k = toKey(r.key);
|
|
62
|
+
out.set(k, { ...r, key: k });
|
|
63
|
+
};
|
|
64
|
+
for (const k of DEFAULT_REDACT_KEYS) {
|
|
65
|
+
set({ key: k, strategy: "full" });
|
|
66
|
+
}
|
|
67
|
+
for (const k of extraKeys ?? []) {
|
|
68
|
+
if (typeof k === "string" && k.length > 0) {
|
|
69
|
+
set({ key: k, strategy: "full" });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const r of rules ?? []) {
|
|
73
|
+
if (typeof r === "string") {
|
|
74
|
+
set({ key: r, strategy: "full" });
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
const key = r.key;
|
|
78
|
+
if (r.strategy === "full") set({ key, strategy: "full" });
|
|
79
|
+
if (r.strategy === "hash") set({ key, strategy: "hash" });
|
|
80
|
+
if (r.strategy === "prefix") {
|
|
81
|
+
set({ key, strategy: "prefix", keep: typeof r.keep === "number" ? r.keep : 8 });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return [...out.values()];
|
|
85
|
+
}
|
|
86
|
+
var Redactor = class {
|
|
87
|
+
#rules;
|
|
88
|
+
constructor(options) {
|
|
89
|
+
this.#rules = compileRules(options?.rules, options?.extraKeys);
|
|
90
|
+
}
|
|
91
|
+
redactValue(key, value) {
|
|
92
|
+
const k = toKey(key);
|
|
93
|
+
const rule = this.#rules.find((r) => r.key === k);
|
|
94
|
+
if (!rule) {
|
|
95
|
+
return this.#redactNested(value);
|
|
96
|
+
}
|
|
97
|
+
if (rule.strategy === "full") return "[REDACTED]";
|
|
98
|
+
const asString = typeof value === "string" ? value : typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" ? String(value) : void 0;
|
|
99
|
+
if (rule.strategy === "prefix") {
|
|
100
|
+
if (asString === void 0) return "[REDACTED]";
|
|
101
|
+
const keep = Math.max(0, Math.floor(rule.keep));
|
|
102
|
+
return asString.length <= keep ? `${asString}\u2026` : `${asString.slice(0, keep)}\u2026`;
|
|
103
|
+
}
|
|
104
|
+
if (rule.strategy === "hash") {
|
|
105
|
+
if (asString === void 0) return "[HASH:unknown]";
|
|
106
|
+
return `[HASH:${stableHash(asString)}]`;
|
|
107
|
+
}
|
|
108
|
+
return this.#redactNested(value);
|
|
109
|
+
}
|
|
110
|
+
redactRecord(record) {
|
|
111
|
+
const out = {};
|
|
112
|
+
for (const [k, v] of Object.entries(record)) {
|
|
113
|
+
out[k] = this.redactValue(k, v);
|
|
114
|
+
}
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
117
|
+
#redactNested(value) {
|
|
118
|
+
if (Array.isArray(value)) {
|
|
119
|
+
return value.map((v) => this.#redactNested(v));
|
|
120
|
+
}
|
|
121
|
+
if (isRecord(value)) {
|
|
122
|
+
const out = {};
|
|
123
|
+
for (const [k, v] of Object.entries(value)) {
|
|
124
|
+
out[k] = this.redactValue(k, v);
|
|
125
|
+
}
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// packages/core/src/redaction-profiles.ts
|
|
133
|
+
var SHARE_PROFILE_EXTRA_KEYS = [
|
|
134
|
+
"userEmail",
|
|
135
|
+
"customerEmail",
|
|
136
|
+
"phone",
|
|
137
|
+
"phoneNumber",
|
|
138
|
+
"address",
|
|
139
|
+
"ip",
|
|
140
|
+
"ipAddress",
|
|
141
|
+
"sessionId",
|
|
142
|
+
"requestId",
|
|
143
|
+
"correlationId",
|
|
144
|
+
"decisionId",
|
|
145
|
+
"groupId",
|
|
146
|
+
"customerId",
|
|
147
|
+
"userId",
|
|
148
|
+
"accountId",
|
|
149
|
+
"tenantId",
|
|
150
|
+
"orgId",
|
|
151
|
+
"organizationId",
|
|
152
|
+
"traceId",
|
|
153
|
+
"spanId",
|
|
154
|
+
"parentSpanId"
|
|
155
|
+
];
|
|
156
|
+
var STRICT_PROFILE_EXTRA_KEYS = [
|
|
157
|
+
"prompt",
|
|
158
|
+
"completion",
|
|
159
|
+
"input",
|
|
160
|
+
"output",
|
|
161
|
+
"inputPreview",
|
|
162
|
+
"outputPreview",
|
|
163
|
+
"message",
|
|
164
|
+
"messages",
|
|
165
|
+
"transcript",
|
|
166
|
+
"context",
|
|
167
|
+
"document",
|
|
168
|
+
"documents",
|
|
169
|
+
"chunk",
|
|
170
|
+
"chunks",
|
|
171
|
+
"retrieval",
|
|
172
|
+
"query"
|
|
173
|
+
];
|
|
174
|
+
function resolveRedactionProfile(profile = "local") {
|
|
175
|
+
switch (profile) {
|
|
176
|
+
case "local":
|
|
177
|
+
return { profile: "local", extraKeys: [] };
|
|
178
|
+
case "share":
|
|
179
|
+
return {
|
|
180
|
+
profile: "share",
|
|
181
|
+
extraKeys: SHARE_PROFILE_EXTRA_KEYS,
|
|
182
|
+
maxMetadataValueLengthCap: 500,
|
|
183
|
+
maxPreviewLengthCap: 200
|
|
184
|
+
};
|
|
185
|
+
case "strict":
|
|
186
|
+
return {
|
|
187
|
+
profile: "strict",
|
|
188
|
+
extraKeys: [...SHARE_PROFILE_EXTRA_KEYS, ...STRICT_PROFILE_EXTRA_KEYS],
|
|
189
|
+
maxMetadataValueLengthCap: 200,
|
|
190
|
+
maxPreviewLengthCap: 80
|
|
191
|
+
};
|
|
192
|
+
default:
|
|
193
|
+
return { profile: "local", extraKeys: [] };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function isPreviewKey(key) {
|
|
197
|
+
return key.toLowerCase().includes("preview");
|
|
198
|
+
}
|
|
199
|
+
function applyProfileMetadataCaps(maxMetadataValueLength, maxPreviewLength, resolved) {
|
|
200
|
+
let meta = maxMetadataValueLength;
|
|
201
|
+
let preview = maxPreviewLength;
|
|
202
|
+
if (resolved.maxMetadataValueLengthCap !== void 0) {
|
|
203
|
+
meta = Math.min(meta, resolved.maxMetadataValueLengthCap);
|
|
204
|
+
}
|
|
205
|
+
if (resolved.maxPreviewLengthCap !== void 0) {
|
|
206
|
+
preview = Math.min(preview, resolved.maxPreviewLengthCap);
|
|
207
|
+
}
|
|
208
|
+
return { maxMetadataValueLength: meta, maxPreviewLength: preview };
|
|
209
|
+
}
|
|
210
|
+
function truncateStringForProfile(value, key, maxMetadataValueLength, maxPreviewLength) {
|
|
211
|
+
const max = isPreviewKey(key) ? maxPreviewLength : maxMetadataValueLength;
|
|
212
|
+
if (max <= 0) return "\u2026";
|
|
213
|
+
if (value.length <= max) return value;
|
|
214
|
+
return `${value.slice(0, max)}\u2026`;
|
|
215
|
+
}
|
|
39
216
|
|
|
40
217
|
// packages/core/src/types.ts
|
|
41
218
|
var STEP_TYPES = [
|
|
@@ -47,14 +224,14 @@ var STEP_TYPES = [
|
|
|
47
224
|
"state",
|
|
48
225
|
"custom"
|
|
49
226
|
];
|
|
50
|
-
function
|
|
227
|
+
function isRecord2(value) {
|
|
51
228
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
52
229
|
}
|
|
53
230
|
function isStepType(value) {
|
|
54
231
|
return typeof value === "string" && STEP_TYPES.includes(value);
|
|
55
232
|
}
|
|
56
233
|
function isTraceEvent(value) {
|
|
57
|
-
if (!
|
|
234
|
+
if (!isRecord2(value)) return false;
|
|
58
235
|
if (value.schemaVersion !== "0.1") return false;
|
|
59
236
|
if (typeof value.timestamp !== "number") return false;
|
|
60
237
|
if (typeof value.event !== "string") return false;
|
|
@@ -114,7 +291,7 @@ var PERSISTED_EVENT_STATUSES = [
|
|
|
114
291
|
"error",
|
|
115
292
|
"unknown"
|
|
116
293
|
];
|
|
117
|
-
function
|
|
294
|
+
function isRecord3(value) {
|
|
118
295
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
119
296
|
}
|
|
120
297
|
function isString(value) {
|
|
@@ -145,21 +322,21 @@ function isPersistedEventStatus(value) {
|
|
|
145
322
|
return typeof value === "string" && PERSISTED_EVENT_STATUSES.includes(value);
|
|
146
323
|
}
|
|
147
324
|
function isPersistedEventSource(value) {
|
|
148
|
-
if (!
|
|
325
|
+
if (!isRecord3(value)) return false;
|
|
149
326
|
if (!isPersistedEventSourceType(value.type)) return false;
|
|
150
327
|
if (!isOptionalString(value.name)) return false;
|
|
151
328
|
if (!isOptionalString(value.version)) return false;
|
|
152
329
|
return true;
|
|
153
330
|
}
|
|
154
331
|
function isPersistedInspectError(value) {
|
|
155
|
-
if (!
|
|
332
|
+
if (!isRecord3(value)) return false;
|
|
156
333
|
if (!isNonEmptyString2(value.message)) return false;
|
|
157
334
|
if (!isOptionalString(value.name)) return false;
|
|
158
335
|
if (!isOptionalString(value.code)) return false;
|
|
159
336
|
return true;
|
|
160
337
|
}
|
|
161
338
|
function isPersistedTokenUsage(value) {
|
|
162
|
-
if (!
|
|
339
|
+
if (!isRecord3(value)) return false;
|
|
163
340
|
if (!isOptionalNonNegativeNumber(value.input)) return false;
|
|
164
341
|
if (!isOptionalNonNegativeNumber(value.output)) return false;
|
|
165
342
|
if (!isOptionalNonNegativeNumber(value.total)) return false;
|
|
@@ -167,14 +344,14 @@ function isPersistedTokenUsage(value) {
|
|
|
167
344
|
return true;
|
|
168
345
|
}
|
|
169
346
|
function isPersistedTraceContext(value) {
|
|
170
|
-
if (!
|
|
347
|
+
if (!isRecord3(value)) return false;
|
|
171
348
|
if (!isOptionalString(value.traceId)) return false;
|
|
172
349
|
if (!isOptionalString(value.spanId)) return false;
|
|
173
350
|
if (!isOptionalString(value.parentSpanId)) return false;
|
|
174
351
|
return true;
|
|
175
352
|
}
|
|
176
353
|
function isPersistedInspectEvent(value) {
|
|
177
|
-
if (!
|
|
354
|
+
if (!isRecord3(value)) return false;
|
|
178
355
|
if (value.schemaVersion !== "0.2" && value.schemaVersion !== "1.0") {
|
|
179
356
|
return false;
|
|
180
357
|
}
|
|
@@ -196,7 +373,7 @@ function isPersistedInspectEvent(value) {
|
|
|
196
373
|
if (value.durationMs !== void 0 && !isNonNegativeNumber(value.durationMs)) {
|
|
197
374
|
return false;
|
|
198
375
|
}
|
|
199
|
-
if (value.attributes !== void 0 && !
|
|
376
|
+
if (value.attributes !== void 0 && !isRecord3(value.attributes)) {
|
|
200
377
|
return false;
|
|
201
378
|
}
|
|
202
379
|
if (value.error !== void 0 && !isPersistedInspectError(value.error)) {
|
|
@@ -548,6 +725,24 @@ function parseDuration(duration) {
|
|
|
548
725
|
}
|
|
549
726
|
}
|
|
550
727
|
}
|
|
728
|
+
function formatDuration(ms) {
|
|
729
|
+
if (!Number.isFinite(ms)) {
|
|
730
|
+
return "0ms";
|
|
731
|
+
}
|
|
732
|
+
if (ms < 0) {
|
|
733
|
+
throw new Error(`formatDuration: ms must be non-negative (got ${ms})`);
|
|
734
|
+
}
|
|
735
|
+
if (ms < 1e3) {
|
|
736
|
+
return `${Math.floor(ms)}ms`;
|
|
737
|
+
}
|
|
738
|
+
if (ms < 6e4) {
|
|
739
|
+
return `${(ms / 1e3).toFixed(2)}s`;
|
|
740
|
+
}
|
|
741
|
+
if (ms < 36e5) {
|
|
742
|
+
return `${(ms / 6e4).toFixed(1)}m`;
|
|
743
|
+
}
|
|
744
|
+
return `${(ms / 36e5).toFixed(1)}h`;
|
|
745
|
+
}
|
|
551
746
|
|
|
552
747
|
// packages/core/src/utils.ts
|
|
553
748
|
var DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
|
|
@@ -557,6 +752,9 @@ var FALLBACK_TRACE_DIR = path.join(
|
|
|
557
752
|
"agent-inspect",
|
|
558
753
|
RUNS_DIR_NAME
|
|
559
754
|
);
|
|
755
|
+
function formatDuration2(ms) {
|
|
756
|
+
return formatDuration(ms);
|
|
757
|
+
}
|
|
560
758
|
function getDefaultTraceDir() {
|
|
561
759
|
const envDir = process.env.AGENT_INSPECT_TRACE_DIR;
|
|
562
760
|
if (typeof envDir === "string" && envDir.trim() !== "") {
|
|
@@ -611,11 +809,11 @@ function warn(message, error) {
|
|
|
611
809
|
}
|
|
612
810
|
|
|
613
811
|
// packages/core/src/read-trace.ts
|
|
614
|
-
function
|
|
812
|
+
function isRecord4(value) {
|
|
615
813
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
616
814
|
}
|
|
617
815
|
function detectLineFormat(parsed) {
|
|
618
|
-
if (!
|
|
816
|
+
if (!isRecord4(parsed)) return "unknown";
|
|
619
817
|
if (parsed.schemaVersion === "0.1") return "0.1";
|
|
620
818
|
if (parsed.schemaVersion === "0.2") return "0.2";
|
|
621
819
|
if (parsed.schemaVersion === "1.0") return "1.0";
|
|
@@ -687,7 +885,7 @@ function parseTraceJsonl(raw, options = {}) {
|
|
|
687
885
|
}
|
|
688
886
|
|
|
689
887
|
// packages/core/src/storage.ts
|
|
690
|
-
function
|
|
888
|
+
function isRecord5(value) {
|
|
691
889
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
692
890
|
}
|
|
693
891
|
function nonEmptyString(value) {
|
|
@@ -698,7 +896,7 @@ function finiteNumber(value) {
|
|
|
698
896
|
}
|
|
699
897
|
function optionalErrorInfo(value) {
|
|
700
898
|
if (value === void 0) return true;
|
|
701
|
-
if (!
|
|
899
|
+
if (!isRecord5(value)) return false;
|
|
702
900
|
if (typeof value.message !== "string") return false;
|
|
703
901
|
if ("stack" in value && value.stack !== void 0) {
|
|
704
902
|
if (typeof value.stack !== "string") return false;
|
|
@@ -706,7 +904,7 @@ function optionalErrorInfo(value) {
|
|
|
706
904
|
return true;
|
|
707
905
|
}
|
|
708
906
|
function validateEvent(event) {
|
|
709
|
-
if (!
|
|
907
|
+
if (!isRecord5(event)) return false;
|
|
710
908
|
if (event.schemaVersion !== "0.1") return false;
|
|
711
909
|
if (!finiteNumber(event.timestamp)) return false;
|
|
712
910
|
if (typeof event.event !== "string") return false;
|
|
@@ -715,7 +913,7 @@ function validateEvent(event) {
|
|
|
715
913
|
if (!nonEmptyString(event.runId) || !nonEmptyString(event.name) || !finiteNumber(event.startTime)) {
|
|
716
914
|
return false;
|
|
717
915
|
}
|
|
718
|
-
if (event.metadata !== void 0 && !
|
|
916
|
+
if (event.metadata !== void 0 && !isRecord5(event.metadata)) {
|
|
719
917
|
return false;
|
|
720
918
|
}
|
|
721
919
|
return true;
|
|
@@ -730,7 +928,7 @@ function validateEvent(event) {
|
|
|
730
928
|
if (event.parentId !== void 0 && typeof event.parentId !== "string") {
|
|
731
929
|
return false;
|
|
732
930
|
}
|
|
733
|
-
if (event.metadata !== void 0 && !
|
|
931
|
+
if (event.metadata !== void 0 && !isRecord5(event.metadata)) {
|
|
734
932
|
return false;
|
|
735
933
|
}
|
|
736
934
|
return true;
|
|
@@ -952,6 +1150,129 @@ async function extractMetadata(filePath, _quickScan) {
|
|
|
952
1150
|
createdAt: stats.birthtime
|
|
953
1151
|
};
|
|
954
1152
|
}
|
|
1153
|
+
function isNonNegativeFiniteNumber(value) {
|
|
1154
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1155
|
+
}
|
|
1156
|
+
function buildRunSummary(events) {
|
|
1157
|
+
const started = events.find(
|
|
1158
|
+
(e) => e.event === "run_started"
|
|
1159
|
+
);
|
|
1160
|
+
const completed = events.filter(
|
|
1161
|
+
(e) => e.event === "run_completed"
|
|
1162
|
+
);
|
|
1163
|
+
const lastCompleted = completed[completed.length - 1];
|
|
1164
|
+
const runId = started?.runId ?? events.find((e) => typeof e.runId === "string")?.runId ?? "unknown-run";
|
|
1165
|
+
const name = typeof started?.name === "string" && started.name.trim() !== "" ? started.name : void 0;
|
|
1166
|
+
const status = lastCompleted ? lastCompleted.status : started ? "running" : "unknown";
|
|
1167
|
+
const durationMs = lastCompleted && isFiniteNumber(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
1168
|
+
started && isFiniteNumber(started.startTime) ? started.startTime : void 0;
|
|
1169
|
+
const steps = /* @__PURE__ */ new Map();
|
|
1170
|
+
for (const e of events) {
|
|
1171
|
+
if (e.event === "step_started") {
|
|
1172
|
+
const s = e;
|
|
1173
|
+
steps.set(s.stepId, {
|
|
1174
|
+
type: s.type,
|
|
1175
|
+
name: s.name,
|
|
1176
|
+
status: "running",
|
|
1177
|
+
parentId: s.parentId,
|
|
1178
|
+
tokensInput: isNonNegativeFiniteNumber(s.metadata?.tokens?.input) ? s.metadata.tokens.input : void 0,
|
|
1179
|
+
tokensOutput: isNonNegativeFiniteNumber(s.metadata?.tokens?.output) ? s.metadata.tokens.output : void 0,
|
|
1180
|
+
tokensTotal: isNonNegativeFiniteNumber(s.metadata?.tokens?.total) ? s.metadata.tokens.total : void 0,
|
|
1181
|
+
tokensCached: isNonNegativeFiniteNumber(s.metadata?.tokens?.cached) ? s.metadata.tokens.cached : void 0
|
|
1182
|
+
});
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
for (const e of events) {
|
|
1186
|
+
if (e.event === "step_completed") {
|
|
1187
|
+
const c = e;
|
|
1188
|
+
const existing = steps.get(c.stepId);
|
|
1189
|
+
if (!existing) continue;
|
|
1190
|
+
existing.status = c.status;
|
|
1191
|
+
existing.durationMs = c.durationMs;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
let totalSteps = 0;
|
|
1195
|
+
let llmSteps = 0;
|
|
1196
|
+
let toolSteps = 0;
|
|
1197
|
+
let logicSteps = 0;
|
|
1198
|
+
let errorSteps = 0;
|
|
1199
|
+
let maxDepth = 0;
|
|
1200
|
+
let longestStep;
|
|
1201
|
+
let totalTokensInput = 0;
|
|
1202
|
+
let totalTokensOutput = 0;
|
|
1203
|
+
let totalTokensTotal = 0;
|
|
1204
|
+
let totalTokensCached = 0;
|
|
1205
|
+
let tokenBearingSteps = 0;
|
|
1206
|
+
let stepsWithKnownTotal = 0;
|
|
1207
|
+
let hasCachedTokens = false;
|
|
1208
|
+
const depthCache = /* @__PURE__ */ new Map();
|
|
1209
|
+
const computeDepth = (stepId) => {
|
|
1210
|
+
const cached = depthCache.get(stepId);
|
|
1211
|
+
if (cached !== void 0) return cached;
|
|
1212
|
+
const node = steps.get(stepId);
|
|
1213
|
+
if (!node) return 0;
|
|
1214
|
+
const parent = node.parentId;
|
|
1215
|
+
if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
|
|
1216
|
+
depthCache.set(stepId, 0);
|
|
1217
|
+
return 0;
|
|
1218
|
+
}
|
|
1219
|
+
const d = Math.min(1e3, computeDepth(parent) + 1);
|
|
1220
|
+
depthCache.set(stepId, d);
|
|
1221
|
+
return d;
|
|
1222
|
+
};
|
|
1223
|
+
for (const [id, s] of steps.entries()) {
|
|
1224
|
+
totalSteps += 1;
|
|
1225
|
+
if (s.type === "llm") llmSteps += 1;
|
|
1226
|
+
else if (s.type === "tool") toolSteps += 1;
|
|
1227
|
+
else logicSteps += 1;
|
|
1228
|
+
if (s.status === "error") errorSteps += 1;
|
|
1229
|
+
const depth = computeDepth(id);
|
|
1230
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
1231
|
+
if (typeof s.durationMs === "number" && Number.isFinite(s.durationMs)) {
|
|
1232
|
+
if (!longestStep || s.durationMs > longestStep.durationMs) {
|
|
1233
|
+
longestStep = { name: s.name, durationMs: s.durationMs, type: s.type };
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
if (s.tokensInput !== void 0 || s.tokensOutput !== void 0 || s.tokensTotal !== void 0 || s.tokensCached !== void 0) {
|
|
1237
|
+
tokenBearingSteps += 1;
|
|
1238
|
+
if (s.tokensInput !== void 0) totalTokensInput += s.tokensInput;
|
|
1239
|
+
if (s.tokensOutput !== void 0) totalTokensOutput += s.tokensOutput;
|
|
1240
|
+
if (s.tokensTotal !== void 0) {
|
|
1241
|
+
totalTokensTotal += s.tokensTotal;
|
|
1242
|
+
stepsWithKnownTotal += 1;
|
|
1243
|
+
} else if (s.tokensInput !== void 0 && s.tokensOutput !== void 0) {
|
|
1244
|
+
totalTokensTotal += s.tokensInput + s.tokensOutput;
|
|
1245
|
+
stepsWithKnownTotal += 1;
|
|
1246
|
+
}
|
|
1247
|
+
if (s.tokensCached !== void 0) {
|
|
1248
|
+
totalTokensCached += s.tokensCached;
|
|
1249
|
+
hasCachedTokens = true;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
const summary = {
|
|
1254
|
+
runId,
|
|
1255
|
+
name,
|
|
1256
|
+
status,
|
|
1257
|
+
durationMs,
|
|
1258
|
+
totalSteps,
|
|
1259
|
+
llmSteps,
|
|
1260
|
+
toolSteps,
|
|
1261
|
+
logicSteps,
|
|
1262
|
+
errorSteps,
|
|
1263
|
+
maxDepth,
|
|
1264
|
+
...longestStep ? { longestStep } : {},
|
|
1265
|
+
...tokenBearingSteps > 0 ? {
|
|
1266
|
+
totalTokens: {
|
|
1267
|
+
input: totalTokensInput,
|
|
1268
|
+
output: totalTokensOutput,
|
|
1269
|
+
...stepsWithKnownTotal === tokenBearingSteps ? { total: totalTokensTotal } : {},
|
|
1270
|
+
...hasCachedTokens ? { cached: totalTokensCached } : {}
|
|
1271
|
+
}
|
|
1272
|
+
} : {}
|
|
1273
|
+
};
|
|
1274
|
+
return summary;
|
|
1275
|
+
}
|
|
955
1276
|
|
|
956
1277
|
// packages/core/src/trace-filter.ts
|
|
957
1278
|
function toLower(s) {
|
|
@@ -1110,6 +1431,145 @@ function buildRunTimeline(events, options = {}) {
|
|
|
1110
1431
|
};
|
|
1111
1432
|
}
|
|
1112
1433
|
|
|
1434
|
+
// packages/core/src/what.ts
|
|
1435
|
+
function pickCorrelation2(metadata) {
|
|
1436
|
+
if (!metadata) return void 0;
|
|
1437
|
+
const out = {};
|
|
1438
|
+
for (const key of [
|
|
1439
|
+
"correlationId",
|
|
1440
|
+
"requestId",
|
|
1441
|
+
"decisionId",
|
|
1442
|
+
"groupId"
|
|
1443
|
+
]) {
|
|
1444
|
+
const value = metadata[key];
|
|
1445
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1446
|
+
out[key] = value;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
1450
|
+
}
|
|
1451
|
+
function stepMixLine(summary) {
|
|
1452
|
+
const parts = [];
|
|
1453
|
+
if (summary.llmSteps > 0) parts.push(`${summary.llmSteps} LLM`);
|
|
1454
|
+
if (summary.toolSteps > 0) parts.push(`${summary.toolSteps} tool`);
|
|
1455
|
+
if (summary.logicSteps > 0) parts.push(`${summary.logicSteps} logic`);
|
|
1456
|
+
return parts.length > 0 ? parts.join(", ") : "none";
|
|
1457
|
+
}
|
|
1458
|
+
function outcomeLine(summary) {
|
|
1459
|
+
if (summary.status === "success") {
|
|
1460
|
+
return summary.errorSteps > 0 ? "Completed with step errors recorded." : "Completed successfully.";
|
|
1461
|
+
}
|
|
1462
|
+
if (summary.status === "error") {
|
|
1463
|
+
if (summary.failedStepNames.length > 0) {
|
|
1464
|
+
const names = summary.failedStepNames.slice(0, 3).join(", ");
|
|
1465
|
+
const suffix = summary.failedStepNames.length > 3 ? ` (+${summary.failedStepNames.length - 3} more)` : "";
|
|
1466
|
+
return `Failed at step(s): ${names}${suffix}.`;
|
|
1467
|
+
}
|
|
1468
|
+
if (summary.runErrorMessage) {
|
|
1469
|
+
return `Run failed: ${summary.runErrorMessage}`;
|
|
1470
|
+
}
|
|
1471
|
+
return "Run failed.";
|
|
1472
|
+
}
|
|
1473
|
+
if (summary.status === "running") {
|
|
1474
|
+
return "Run is still in progress (no run_completed).";
|
|
1475
|
+
}
|
|
1476
|
+
return "Outcome unknown \u2014 inspect events may be incomplete.";
|
|
1477
|
+
}
|
|
1478
|
+
function buildRunWhatSummary(events) {
|
|
1479
|
+
const base = buildRunSummary(events);
|
|
1480
|
+
const started = events.find(
|
|
1481
|
+
(e) => e.event === "run_started"
|
|
1482
|
+
);
|
|
1483
|
+
const completed = events.filter(
|
|
1484
|
+
(e) => e.event === "run_completed"
|
|
1485
|
+
);
|
|
1486
|
+
const lastCompleted = completed[completed.length - 1];
|
|
1487
|
+
const failedStepNames = [];
|
|
1488
|
+
const stepNames = /* @__PURE__ */ new Map();
|
|
1489
|
+
for (const e of events) {
|
|
1490
|
+
if (e.event === "step_started") {
|
|
1491
|
+
const s = e;
|
|
1492
|
+
stepNames.set(s.stepId, s.name);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
for (const e of events) {
|
|
1496
|
+
if (e.event === "step_completed") {
|
|
1497
|
+
const sc = e;
|
|
1498
|
+
if (sc.status === "error") {
|
|
1499
|
+
failedStepNames.push(stepNames.get(sc.stepId) ?? sc.stepId);
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
return {
|
|
1504
|
+
runId: base.runId,
|
|
1505
|
+
name: base.name,
|
|
1506
|
+
status: base.status,
|
|
1507
|
+
durationMs: base.durationMs,
|
|
1508
|
+
totalSteps: base.totalSteps,
|
|
1509
|
+
llmSteps: base.llmSteps,
|
|
1510
|
+
toolSteps: base.toolSteps,
|
|
1511
|
+
logicSteps: base.logicSteps,
|
|
1512
|
+
errorSteps: base.errorSteps,
|
|
1513
|
+
maxDepth: base.maxDepth,
|
|
1514
|
+
longestStep: base.longestStep,
|
|
1515
|
+
totalTokens: base.totalTokens,
|
|
1516
|
+
correlation: pickCorrelation2(started?.metadata),
|
|
1517
|
+
failedStepNames,
|
|
1518
|
+
runErrorMessage: lastCompleted?.error?.message
|
|
1519
|
+
};
|
|
1520
|
+
}
|
|
1521
|
+
function renderRunWhat(summary, options = {}) {
|
|
1522
|
+
const showCorrelation = options.correlation !== false;
|
|
1523
|
+
const lines = [];
|
|
1524
|
+
const label = summary.name ?? summary.runId;
|
|
1525
|
+
lines.push(`What: ${label}`);
|
|
1526
|
+
const duration = summary.durationMs !== void 0 ? formatDuration2(summary.durationMs) : "\u2014";
|
|
1527
|
+
lines.push(
|
|
1528
|
+
`Status: ${summary.status} \xB7 Duration: ${duration} \xB7 Steps: ${summary.totalSteps} (${stepMixLine(summary)})`
|
|
1529
|
+
);
|
|
1530
|
+
if (summary.totalTokens) {
|
|
1531
|
+
const tokenParts = [
|
|
1532
|
+
`${summary.totalTokens.input} in`,
|
|
1533
|
+
`${summary.totalTokens.output} out`
|
|
1534
|
+
];
|
|
1535
|
+
if (summary.totalTokens.total !== void 0) {
|
|
1536
|
+
tokenParts.push(`${summary.totalTokens.total} total`);
|
|
1537
|
+
}
|
|
1538
|
+
if (summary.totalTokens.cached !== void 0) {
|
|
1539
|
+
tokenParts.push(`${summary.totalTokens.cached} cached`);
|
|
1540
|
+
}
|
|
1541
|
+
lines.push(`Tokens: ${tokenParts.join(" / ")}`);
|
|
1542
|
+
}
|
|
1543
|
+
if (showCorrelation && summary.correlation) {
|
|
1544
|
+
const parts = [];
|
|
1545
|
+
if (summary.correlation.correlationId) {
|
|
1546
|
+
parts.push(`correlationId=${summary.correlation.correlationId}`);
|
|
1547
|
+
}
|
|
1548
|
+
if (summary.correlation.requestId) {
|
|
1549
|
+
parts.push(`requestId=${summary.correlation.requestId}`);
|
|
1550
|
+
}
|
|
1551
|
+
if (summary.correlation.decisionId) {
|
|
1552
|
+
parts.push(`decisionId=${summary.correlation.decisionId}`);
|
|
1553
|
+
}
|
|
1554
|
+
if (summary.correlation.groupId) {
|
|
1555
|
+
parts.push(`groupId=${summary.correlation.groupId}`);
|
|
1556
|
+
}
|
|
1557
|
+
if (parts.length > 0) {
|
|
1558
|
+
lines.push(`Correlation: ${parts.join(", ")}`);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
lines.push(`Outcome: ${outcomeLine(summary)}`);
|
|
1562
|
+
if (summary.longestStep && summary.totalSteps > 0) {
|
|
1563
|
+
lines.push(
|
|
1564
|
+
`Slowest: ${summary.longestStep.name} (${formatDuration2(summary.longestStep.durationMs)}, ${summary.longestStep.type})`
|
|
1565
|
+
);
|
|
1566
|
+
}
|
|
1567
|
+
if (summary.maxDepth > 0) {
|
|
1568
|
+
lines.push(`Max depth: ${summary.maxDepth}`);
|
|
1569
|
+
}
|
|
1570
|
+
return lines.join("\n");
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1113
1573
|
// packages/core/src/search.ts
|
|
1114
1574
|
function parseDurationFilter(expr) {
|
|
1115
1575
|
const raw = expr.trim();
|
|
@@ -1329,6 +1789,39 @@ async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
|
|
|
1329
1789
|
return metas;
|
|
1330
1790
|
}
|
|
1331
1791
|
|
|
1792
|
+
// packages/core/src/bundle/safety-status.ts
|
|
1793
|
+
function aggregateBundleSafeStatus(statuses) {
|
|
1794
|
+
if (statuses.length === 0) return "UNKNOWN";
|
|
1795
|
+
if (statuses.some((status) => status === "UNSAFE")) return "UNSAFE";
|
|
1796
|
+
if (statuses.some((status) => status === "UNKNOWN")) return "UNKNOWN";
|
|
1797
|
+
if (statuses.some((status) => status === "SAFE WITH WARNINGS")) return "SAFE WITH WARNINGS";
|
|
1798
|
+
return "SAFE";
|
|
1799
|
+
}
|
|
1800
|
+
function toMetadataSafeStatus(status) {
|
|
1801
|
+
if (status === "SAFE WITH WARNINGS") return "SAFE_WITH_WARNINGS";
|
|
1802
|
+
return status;
|
|
1803
|
+
}
|
|
1804
|
+
|
|
1805
|
+
// packages/core/src/bundle/manifest.ts
|
|
1806
|
+
var BUNDLE_NOTE = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification. Review before sharing.";
|
|
1807
|
+
function buildBundleMetadata(parts) {
|
|
1808
|
+
const aggregate = aggregateBundleSafeStatus(
|
|
1809
|
+
parts.checks.runs.map((run) => run.status)
|
|
1810
|
+
);
|
|
1811
|
+
return {
|
|
1812
|
+
createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1813
|
+
agentInspectVersion: parts.agentInspectVersion,
|
|
1814
|
+
redactionProfile: parts.profile,
|
|
1815
|
+
sourceTraceCount: parts.resolve.runIds.length,
|
|
1816
|
+
runIds: [...parts.resolve.runIds],
|
|
1817
|
+
safeStatus: toMetadataSafeStatus(aggregate),
|
|
1818
|
+
files: [...parts.files].sort((a, b) => a.localeCompare(b)),
|
|
1819
|
+
note: BUNDLE_NOTE,
|
|
1820
|
+
...parts.resolve.sessionId !== void 0 ? { sessionId: parts.resolve.sessionId } : {},
|
|
1821
|
+
...parts.resolve.since !== void 0 ? { since: parts.resolve.since } : {}
|
|
1822
|
+
};
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1332
1825
|
// packages/core/src/checks/index.ts
|
|
1333
1826
|
var SEVERITY_RANK = {
|
|
1334
1827
|
error: 0,
|
|
@@ -1637,14 +2130,14 @@ function runTraceChecks(input, options = {}) {
|
|
|
1637
2130
|
}
|
|
1638
2131
|
|
|
1639
2132
|
// packages/core/src/persisted/token-usage.ts
|
|
1640
|
-
function
|
|
2133
|
+
function isRecord6(value) {
|
|
1641
2134
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1642
2135
|
}
|
|
1643
2136
|
function nonNegativeFinite(value) {
|
|
1644
2137
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1645
2138
|
}
|
|
1646
2139
|
function normalizeTokenUsage(value) {
|
|
1647
|
-
if (!
|
|
2140
|
+
if (!isRecord6(value)) return void 0;
|
|
1648
2141
|
const input = nonNegativeFinite(value.input);
|
|
1649
2142
|
const output = nonNegativeFinite(value.output);
|
|
1650
2143
|
const suppliedTotal = nonNegativeFinite(value.total);
|
|
@@ -2402,7 +2895,7 @@ function persistedEventsForParsedTrace(parsed) {
|
|
|
2402
2895
|
sourceName: "agent-inspect-jsonl-reader"
|
|
2403
2896
|
});
|
|
2404
2897
|
}
|
|
2405
|
-
function
|
|
2898
|
+
function isRecord7(value) {
|
|
2406
2899
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2407
2900
|
}
|
|
2408
2901
|
function isNonEmptyString3(value) {
|
|
@@ -2417,13 +2910,13 @@ function readStringField(record, keys) {
|
|
|
2417
2910
|
}
|
|
2418
2911
|
function readRecordField(record, key) {
|
|
2419
2912
|
const value = record[key];
|
|
2420
|
-
return
|
|
2913
|
+
return isRecord7(value) ? value : void 0;
|
|
2421
2914
|
}
|
|
2422
2915
|
function parseJsonDocument(content) {
|
|
2423
2916
|
return JSON.parse(content);
|
|
2424
2917
|
}
|
|
2425
2918
|
function looksLikeOpenInferenceSpan(value) {
|
|
2426
|
-
if (!
|
|
2919
|
+
if (!isRecord7(value)) return false;
|
|
2427
2920
|
const attributes = readRecordField(value, "attributes");
|
|
2428
2921
|
return readStringField(value, ["trace_id", "traceId"]) !== void 0 && readStringField(value, ["span_id", "spanId"]) !== void 0 && (readStringField(value, ["name"]) !== void 0 || attributes?.["openinference.span.kind"] !== void 0);
|
|
2429
2922
|
}
|
|
@@ -2448,7 +2941,7 @@ function extractOpenInferenceDocument(root) {
|
|
|
2448
2941
|
unsupportedFields
|
|
2449
2942
|
};
|
|
2450
2943
|
}
|
|
2451
|
-
if (!
|
|
2944
|
+
if (!isRecord7(root)) return void 0;
|
|
2452
2945
|
const rootFormat = root.format;
|
|
2453
2946
|
const rootCompatibility = root.compatibility;
|
|
2454
2947
|
const version = typeof root.version === "string" && root.version.trim() !== "" ? root.version : void 0;
|
|
@@ -2588,7 +3081,7 @@ function summarizeAttributeValue(value) {
|
|
|
2588
3081
|
if (Array.isArray(value)) {
|
|
2589
3082
|
return { type: "array", length: value.length };
|
|
2590
3083
|
}
|
|
2591
|
-
if (
|
|
3084
|
+
if (isRecord7(value)) {
|
|
2592
3085
|
return { type: "object", keyCount: Object.keys(value).length };
|
|
2593
3086
|
}
|
|
2594
3087
|
if (value === null) {
|
|
@@ -2675,7 +3168,7 @@ function mapOpenInferenceKind(span, attributes, pathPrefix) {
|
|
|
2675
3168
|
}
|
|
2676
3169
|
}
|
|
2677
3170
|
function mapOpenInferenceStatus(status) {
|
|
2678
|
-
if (!
|
|
3171
|
+
if (!isRecord7(status)) return void 0;
|
|
2679
3172
|
const rawCode = status.code;
|
|
2680
3173
|
if (typeof rawCode !== "string") return void 0;
|
|
2681
3174
|
switch (rawCode.toUpperCase()) {
|
|
@@ -2775,7 +3268,7 @@ function mapOpenInferenceSpan(span, index, version) {
|
|
|
2775
3268
|
warnings.push(...kindWarnings);
|
|
2776
3269
|
const status = mapOpenInferenceStatus(span.status);
|
|
2777
3270
|
const tokenUsage = readOpenInferenceTokenUsage(rawAttributes);
|
|
2778
|
-
const errorMessage =
|
|
3271
|
+
const errorMessage = isRecord7(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
2779
3272
|
const event = {
|
|
2780
3273
|
schemaVersion: "0.2",
|
|
2781
3274
|
eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
|
|
@@ -2921,7 +3414,7 @@ var openInferenceJsonReader = {
|
|
|
2921
3414
|
}
|
|
2922
3415
|
};
|
|
2923
3416
|
function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
|
|
2924
|
-
if (!
|
|
3417
|
+
if (!isRecord7(value)) {
|
|
2925
3418
|
unsupportedFields.push(field);
|
|
2926
3419
|
warnings.push({
|
|
2927
3420
|
code: "otlp_attribute_value_invalid",
|
|
@@ -2943,15 +3436,15 @@ function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
|
|
|
2943
3436
|
if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
|
|
2944
3437
|
return value.doubleValue;
|
|
2945
3438
|
}
|
|
2946
|
-
if (
|
|
3439
|
+
if (isRecord7(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
|
|
2947
3440
|
return value.arrayValue.values.map(
|
|
2948
3441
|
(item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
|
|
2949
3442
|
);
|
|
2950
3443
|
}
|
|
2951
|
-
if (
|
|
3444
|
+
if (isRecord7(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
|
|
2952
3445
|
const out = {};
|
|
2953
3446
|
for (const [index, item] of value.kvlistValue.values.entries()) {
|
|
2954
|
-
if (!
|
|
3447
|
+
if (!isRecord7(item) || typeof item.key !== "string") {
|
|
2955
3448
|
unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
|
|
2956
3449
|
continue;
|
|
2957
3450
|
}
|
|
@@ -3002,7 +3495,7 @@ function parseOtlpAttributes(value, pathPrefix) {
|
|
|
3002
3495
|
}
|
|
3003
3496
|
for (const [index, item] of value.entries()) {
|
|
3004
3497
|
const field = `${pathPrefix}[${index}]`;
|
|
3005
|
-
if (!
|
|
3498
|
+
if (!isRecord7(item) || typeof item.key !== "string") {
|
|
3006
3499
|
unsupportedFields.push(field);
|
|
3007
3500
|
warnings.push({
|
|
3008
3501
|
code: "otlp_attribute_invalid",
|
|
@@ -3025,16 +3518,16 @@ function parseOtlpAttributes(value, pathPrefix) {
|
|
|
3025
3518
|
return { attributes, warnings, unsupportedFields };
|
|
3026
3519
|
}
|
|
3027
3520
|
function looksLikeOtlpSpan(value) {
|
|
3028
|
-
return
|
|
3521
|
+
return isRecord7(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
|
|
3029
3522
|
}
|
|
3030
3523
|
function extractOtlpDocument(root) {
|
|
3031
|
-
if (!
|
|
3524
|
+
if (!isRecord7(root) || !Array.isArray(root.resourceSpans)) return void 0;
|
|
3032
3525
|
const spans = [];
|
|
3033
3526
|
const warnings = [];
|
|
3034
3527
|
const unsupportedFields = [];
|
|
3035
3528
|
for (const [resourceIndex, resourceSpan] of root.resourceSpans.entries()) {
|
|
3036
3529
|
const resourcePath = `resourceSpans[${resourceIndex}]`;
|
|
3037
|
-
if (!
|
|
3530
|
+
if (!isRecord7(resourceSpan)) {
|
|
3038
3531
|
unsupportedFields.push(resourcePath);
|
|
3039
3532
|
continue;
|
|
3040
3533
|
}
|
|
@@ -3057,7 +3550,7 @@ function extractOtlpDocument(root) {
|
|
|
3057
3550
|
}
|
|
3058
3551
|
for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
|
|
3059
3552
|
const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
|
|
3060
|
-
if (!
|
|
3553
|
+
if (!isRecord7(scopeSpan)) {
|
|
3061
3554
|
unsupportedFields.push(scopePath);
|
|
3062
3555
|
continue;
|
|
3063
3556
|
}
|
|
@@ -3124,7 +3617,7 @@ function extractOtlpDocument(root) {
|
|
|
3124
3617
|
};
|
|
3125
3618
|
}
|
|
3126
3619
|
function mapOtlpStatus(status) {
|
|
3127
|
-
if (!
|
|
3620
|
+
if (!isRecord7(status)) return void 0;
|
|
3128
3621
|
const rawCode = status.code;
|
|
3129
3622
|
if (typeof rawCode !== "string") return void 0;
|
|
3130
3623
|
switch (rawCode.toUpperCase()) {
|
|
@@ -3224,7 +3717,7 @@ function mapOtlpEvents(value, pathPrefix) {
|
|
|
3224
3717
|
const events = [];
|
|
3225
3718
|
for (const [index, event] of value.entries()) {
|
|
3226
3719
|
const eventPath = `${pathPrefix}[${index}]`;
|
|
3227
|
-
if (!
|
|
3720
|
+
if (!isRecord7(event)) {
|
|
3228
3721
|
unsupportedFields.push(eventPath);
|
|
3229
3722
|
continue;
|
|
3230
3723
|
}
|
|
@@ -3362,7 +3855,7 @@ function mapOtlpSpan(context) {
|
|
|
3362
3855
|
warnings.push(...kindWarnings);
|
|
3363
3856
|
const status = mapOtlpStatus(span.status);
|
|
3364
3857
|
const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
|
|
3365
|
-
const errorMessage =
|
|
3858
|
+
const errorMessage = isRecord7(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
3366
3859
|
const event = {
|
|
3367
3860
|
schemaVersion: "0.2",
|
|
3368
3861
|
eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
|
|
@@ -3735,6 +4228,9 @@ function safeString(value, maxLength) {
|
|
|
3735
4228
|
function escapeMarkdown(value) {
|
|
3736
4229
|
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
3737
4230
|
}
|
|
4231
|
+
function escapeHtml(value) {
|
|
4232
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4233
|
+
}
|
|
3738
4234
|
function sortKeysDeep(input) {
|
|
3739
4235
|
if (input === null || typeof input !== "object") return input;
|
|
3740
4236
|
if (Array.isArray(input)) return input.map(sortKeysDeep);
|
|
@@ -3752,14 +4248,15 @@ function stableJson(value, pretty) {
|
|
|
3752
4248
|
function compactAttributes3(attrs, options) {
|
|
3753
4249
|
if (attrs === void 0) return {};
|
|
3754
4250
|
const maxLen = options?.maxLength ?? 500;
|
|
4251
|
+
const redacted = options?.redacted ?? true;
|
|
3755
4252
|
const out = {};
|
|
3756
4253
|
for (const key of Object.keys(attrs).sort()) {
|
|
3757
|
-
if (shouldRedactKey(key)) {
|
|
4254
|
+
if (redacted && shouldRedactKey(key)) {
|
|
3758
4255
|
out[key] = "[REDACTED]";
|
|
3759
4256
|
continue;
|
|
3760
4257
|
}
|
|
3761
4258
|
const v = attrs[key];
|
|
3762
|
-
out[key] = compactValue(v, maxLen);
|
|
4259
|
+
out[key] = compactValue(v, maxLen, redacted);
|
|
3763
4260
|
}
|
|
3764
4261
|
return out;
|
|
3765
4262
|
}
|
|
@@ -3768,15 +4265,15 @@ function compactValue(value, maxLen, redacted) {
|
|
|
3768
4265
|
return typeof value === "string" ? safeString(value, maxLen) : value;
|
|
3769
4266
|
}
|
|
3770
4267
|
if (Array.isArray(value)) {
|
|
3771
|
-
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen));
|
|
4268
|
+
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
|
|
3772
4269
|
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
3773
4270
|
return arr;
|
|
3774
4271
|
}
|
|
3775
4272
|
const o = value;
|
|
3776
4273
|
const inner = {};
|
|
3777
4274
|
for (const k of Object.keys(o)) {
|
|
3778
|
-
if (shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
3779
|
-
else inner[k] = compactValue(o[k], maxLen);
|
|
4275
|
+
if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
4276
|
+
else inner[k] = compactValue(o[k], maxLen, redacted);
|
|
3780
4277
|
}
|
|
3781
4278
|
return inner;
|
|
3782
4279
|
}
|
|
@@ -4150,6 +4647,274 @@ function diffRuns(left, right, options) {
|
|
|
4150
4647
|
return { summary, differences };
|
|
4151
4648
|
}
|
|
4152
4649
|
|
|
4650
|
+
// packages/core/src/exporters/types.ts
|
|
4651
|
+
var EXPORT_PAYLOAD_VERSION = "0.1.2";
|
|
4652
|
+
|
|
4653
|
+
// packages/core/src/exporters/redact-export.ts
|
|
4654
|
+
function isRecord8(value) {
|
|
4655
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4656
|
+
}
|
|
4657
|
+
function deepClone(value) {
|
|
4658
|
+
if (value === null || typeof value !== "object") {
|
|
4659
|
+
return value;
|
|
4660
|
+
}
|
|
4661
|
+
if (Array.isArray(value)) {
|
|
4662
|
+
return value.map((item) => deepClone(item));
|
|
4663
|
+
}
|
|
4664
|
+
const out = {};
|
|
4665
|
+
for (const [k, v] of Object.entries(value)) {
|
|
4666
|
+
out[k] = deepClone(v);
|
|
4667
|
+
}
|
|
4668
|
+
return out;
|
|
4669
|
+
}
|
|
4670
|
+
function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
4671
|
+
if (depth > 32) {
|
|
4672
|
+
return { truncated: true, reason: "maxDepth" };
|
|
4673
|
+
}
|
|
4674
|
+
const out = {};
|
|
4675
|
+
for (const [key, value] of Object.entries(record)) {
|
|
4676
|
+
out[key] = boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth);
|
|
4677
|
+
}
|
|
4678
|
+
return out;
|
|
4679
|
+
}
|
|
4680
|
+
function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
4681
|
+
if (value === null || typeof value !== "object") {
|
|
4682
|
+
if (typeof value === "string") {
|
|
4683
|
+
return truncateStringForProfile(
|
|
4684
|
+
value,
|
|
4685
|
+
key,
|
|
4686
|
+
maxMetadataValueLength,
|
|
4687
|
+
maxPreviewLength
|
|
4688
|
+
);
|
|
4689
|
+
}
|
|
4690
|
+
return value;
|
|
4691
|
+
}
|
|
4692
|
+
if (seen.has(value)) return "[Circular]";
|
|
4693
|
+
seen.add(value);
|
|
4694
|
+
if (Array.isArray(value)) {
|
|
4695
|
+
return value.slice(0, 50).map(
|
|
4696
|
+
(item, index) => boundValue(
|
|
4697
|
+
item,
|
|
4698
|
+
String(index),
|
|
4699
|
+
maxMetadataValueLength,
|
|
4700
|
+
maxPreviewLength,
|
|
4701
|
+
seen,
|
|
4702
|
+
depth + 1
|
|
4703
|
+
)
|
|
4704
|
+
);
|
|
4705
|
+
}
|
|
4706
|
+
return boundAttributeValues(
|
|
4707
|
+
value,
|
|
4708
|
+
maxMetadataValueLength,
|
|
4709
|
+
maxPreviewLength,
|
|
4710
|
+
seen,
|
|
4711
|
+
depth + 1
|
|
4712
|
+
);
|
|
4713
|
+
}
|
|
4714
|
+
function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPreviewLength) {
|
|
4715
|
+
if (!attrs || Object.keys(attrs).length === 0) {
|
|
4716
|
+
return attrs;
|
|
4717
|
+
}
|
|
4718
|
+
const redacted = redactor.redactRecord(attrs);
|
|
4719
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
4720
|
+
const bounded = boundAttributeValues(
|
|
4721
|
+
redacted,
|
|
4722
|
+
maxMetadataValueLength,
|
|
4723
|
+
maxPreviewLength,
|
|
4724
|
+
seen,
|
|
4725
|
+
0
|
|
4726
|
+
);
|
|
4727
|
+
const err = bounded.error;
|
|
4728
|
+
if (isRecord8(err) && typeof err.message === "string") {
|
|
4729
|
+
bounded.error = {
|
|
4730
|
+
...err,
|
|
4731
|
+
message: truncateStringForProfile(
|
|
4732
|
+
err.message,
|
|
4733
|
+
"message",
|
|
4734
|
+
maxMetadataValueLength,
|
|
4735
|
+
maxPreviewLength
|
|
4736
|
+
),
|
|
4737
|
+
...typeof err.stack === "string" ? {
|
|
4738
|
+
stack: truncateStringForProfile(
|
|
4739
|
+
err.stack,
|
|
4740
|
+
"stack",
|
|
4741
|
+
maxMetadataValueLength,
|
|
4742
|
+
maxPreviewLength
|
|
4743
|
+
)
|
|
4744
|
+
} : {}
|
|
4745
|
+
};
|
|
4746
|
+
}
|
|
4747
|
+
return bounded;
|
|
4748
|
+
}
|
|
4749
|
+
function redactRunTreeForExport(tree, options) {
|
|
4750
|
+
const profile = options?.redactionProfile ?? "local";
|
|
4751
|
+
if (profile === "local") {
|
|
4752
|
+
return deepClone(tree);
|
|
4753
|
+
}
|
|
4754
|
+
const resolved = resolveRedactionProfile(profile);
|
|
4755
|
+
const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
|
|
4756
|
+
2e3,
|
|
4757
|
+
500,
|
|
4758
|
+
resolved
|
|
4759
|
+
);
|
|
4760
|
+
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
4761
|
+
const clone = deepClone(tree);
|
|
4762
|
+
function walk(nodes) {
|
|
4763
|
+
for (const node of nodes) {
|
|
4764
|
+
if (node.event.attributes !== void 0) {
|
|
4765
|
+
node.event.attributes = redactEventAttributes(
|
|
4766
|
+
node.event.attributes,
|
|
4767
|
+
redactor,
|
|
4768
|
+
maxMetadataValueLength,
|
|
4769
|
+
maxPreviewLength
|
|
4770
|
+
);
|
|
4771
|
+
}
|
|
4772
|
+
if (node.children.length > 0) {
|
|
4773
|
+
walk(node.children);
|
|
4774
|
+
}
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
walk(clone.children);
|
|
4778
|
+
return clone;
|
|
4779
|
+
}
|
|
4780
|
+
|
|
4781
|
+
// packages/core/src/exporters/html-exporter.ts
|
|
4782
|
+
function renderTreeHtml(nodes, ulClass = "tree") {
|
|
4783
|
+
if (nodes.length === 0) return "";
|
|
4784
|
+
const parts = [`<ul class="${ulClass}">`];
|
|
4785
|
+
for (const n of nodes) {
|
|
4786
|
+
const ev = n.event;
|
|
4787
|
+
const status = ev.status ?? "?";
|
|
4788
|
+
const dur = ev.durationMs !== void 0 && Number.isFinite(ev.durationMs) ? `${ev.durationMs}ms` : "-";
|
|
4789
|
+
parts.push("<li>");
|
|
4790
|
+
parts.push(
|
|
4791
|
+
`<span class="nm">${escapeHtml(ev.name)}</span> <span class="meta">[${escapeHtml(ev.kind)}] ${escapeHtml(status)} (${escapeHtml(dur)})</span>`
|
|
4792
|
+
);
|
|
4793
|
+
if (n.children.length > 0) {
|
|
4794
|
+
parts.push(renderTreeHtml(n.children, "tree nested"));
|
|
4795
|
+
}
|
|
4796
|
+
parts.push("</li>");
|
|
4797
|
+
}
|
|
4798
|
+
parts.push("</ul>");
|
|
4799
|
+
return parts.join("");
|
|
4800
|
+
}
|
|
4801
|
+
function exportHtml(tree, options) {
|
|
4802
|
+
const warnings = [];
|
|
4803
|
+
const includeMetadata = options?.includeMetadata ?? true;
|
|
4804
|
+
const includeAttributes = options?.includeAttributes ?? false;
|
|
4805
|
+
const includeErrors = options?.includeErrors ?? true;
|
|
4806
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
4807
|
+
const redacted = options?.redacted;
|
|
4808
|
+
const titleName = escapeHtml(tree.name ?? tree.runId);
|
|
4809
|
+
const summaryRows = [];
|
|
4810
|
+
summaryRows.push(
|
|
4811
|
+
`<tr><th scope="row">runId</th><td><code>${escapeHtml(tree.runId)}</code></td></tr>`
|
|
4812
|
+
);
|
|
4813
|
+
if (tree.name !== void 0) {
|
|
4814
|
+
summaryRows.push(`<tr><th scope="row">name</th><td>${escapeHtml(tree.name)}</td></tr>`);
|
|
4815
|
+
}
|
|
4816
|
+
summaryRows.push(
|
|
4817
|
+
`<tr><th scope="row">status</th><td>${escapeHtml(String(tree.status ?? "unknown"))}</td></tr>`
|
|
4818
|
+
);
|
|
4819
|
+
summaryRows.push(
|
|
4820
|
+
`<tr><th scope="row">durationMs</th><td>${tree.durationMs !== void 0 ? escapeHtml(String(tree.durationMs)) : "\u2014"}</td></tr>`
|
|
4821
|
+
);
|
|
4822
|
+
summaryRows.push(
|
|
4823
|
+
`<tr><th scope="row">startedAt</th><td>${tree.startedAt !== void 0 ? escapeHtml(String(tree.startedAt)) : "\u2014"}</td></tr>`
|
|
4824
|
+
);
|
|
4825
|
+
summaryRows.push(
|
|
4826
|
+
`<tr><th scope="row">endedAt</th><td>${tree.endedAt !== void 0 ? escapeHtml(String(tree.endedAt)) : "\u2014"}</td></tr>`
|
|
4827
|
+
);
|
|
4828
|
+
summaryRows.push(
|
|
4829
|
+
`<tr><th scope="row">totalEvents</th><td>${escapeHtml(String(tree.metadata.totalEvents))}</td></tr>`
|
|
4830
|
+
);
|
|
4831
|
+
let confidenceHtml = "";
|
|
4832
|
+
if (includeMetadata) {
|
|
4833
|
+
const cb = tree.metadata.confidenceBreakdown;
|
|
4834
|
+
confidenceHtml += "<h3>Confidence breakdown</h3><table><thead><tr><th>bucket</th><th>count</th></tr></thead><tbody>";
|
|
4835
|
+
for (const k of Object.keys(cb).sort()) {
|
|
4836
|
+
const key = k;
|
|
4837
|
+
confidenceHtml += `<tr><td>${escapeHtml(key)}</td><td>${cb[key]}</td></tr>`;
|
|
4838
|
+
}
|
|
4839
|
+
confidenceHtml += "</tbody></table>";
|
|
4840
|
+
confidenceHtml += "<h3>Kind breakdown</h3><table><thead><tr><th>kind</th><th>count</th></tr></thead><tbody>";
|
|
4841
|
+
for (const k of Object.keys(tree.metadata.kinds).sort()) {
|
|
4842
|
+
const key = k;
|
|
4843
|
+
const c = tree.metadata.kinds[key];
|
|
4844
|
+
if (c > 0) confidenceHtml += `<tr><td>${escapeHtml(key)}</td><td>${c}</td></tr>`;
|
|
4845
|
+
}
|
|
4846
|
+
confidenceHtml += "</tbody></table>";
|
|
4847
|
+
}
|
|
4848
|
+
const flat = flattenTree(tree);
|
|
4849
|
+
const errors = flat.filter((n) => n.event.status === "error");
|
|
4850
|
+
let errorsHtml = "";
|
|
4851
|
+
if (includeErrors && errors.length > 0) {
|
|
4852
|
+
errorsHtml += "<h2>Errors</h2><ul>";
|
|
4853
|
+
for (const n of errors) {
|
|
4854
|
+
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
|
|
4855
|
+
n.event.attributes.error.message,
|
|
4856
|
+
maxLen
|
|
4857
|
+
) : "";
|
|
4858
|
+
errorsHtml += `<li><strong>${escapeHtml(n.event.name)}</strong> (${escapeHtml(n.event.eventId)}): ${escapeHtml(msg || "error")}</li>`;
|
|
4859
|
+
}
|
|
4860
|
+
errorsHtml += "</ul>";
|
|
4861
|
+
}
|
|
4862
|
+
let attrsHtml = "";
|
|
4863
|
+
if (includeAttributes) {
|
|
4864
|
+
attrsHtml += "<h2>Attributes (bounded)</h2>";
|
|
4865
|
+
for (const n of flat) {
|
|
4866
|
+
if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
|
|
4867
|
+
const compact = compactAttributes3(n.event.attributes, {
|
|
4868
|
+
maxLength: maxLen,
|
|
4869
|
+
redacted
|
|
4870
|
+
});
|
|
4871
|
+
attrsHtml += `<h3>${escapeHtml(n.event.name)}</h3><pre class="json">${escapeHtml(stableJson(compact, true))}</pre>`;
|
|
4872
|
+
}
|
|
4873
|
+
warnings.push(
|
|
4874
|
+
"Attributes may still contain sensitive data; review exports before sharing."
|
|
4875
|
+
);
|
|
4876
|
+
}
|
|
4877
|
+
const css = `
|
|
4878
|
+
body{font-family:system-ui,sans-serif;line-height:1.5;margin:1.5rem;max-width:960px;color:#111}
|
|
4879
|
+
h1{font-size:1.35rem}
|
|
4880
|
+
h2{font-size:1.1rem;margin-top:1.5rem}
|
|
4881
|
+
table{border-collapse:collapse;margin:0.75rem 0}
|
|
4882
|
+
th,td{border:1px solid #ccc;padding:0.35rem 0.6rem;text-align:left}
|
|
4883
|
+
th{background:#f5f5f5}
|
|
4884
|
+
pre.json{background:#f8f8f8;padding:0.75rem;overflow:auto;font-size:0.85rem}
|
|
4885
|
+
ul.tree{list-style:none;padding-left:1rem}
|
|
4886
|
+
ul.tree.nested{padding-left:1.25rem;border-left:1px solid #ddd;margin:0.25rem 0}
|
|
4887
|
+
.nm{font-weight:600}
|
|
4888
|
+
.meta{color:#555;font-size:0.9rem}
|
|
4889
|
+
footer{margin-top:2rem;font-size:0.85rem;color:#555}
|
|
4890
|
+
`.trim();
|
|
4891
|
+
const html = `<!doctype html>
|
|
4892
|
+
<html lang="en">
|
|
4893
|
+
<head>
|
|
4894
|
+
<meta charset="utf-8"/>
|
|
4895
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
4896
|
+
<title>${titleName}</title>
|
|
4897
|
+
<style>${css}</style>
|
|
4898
|
+
</head>
|
|
4899
|
+
<body>
|
|
4900
|
+
<header><h1>AgentInspect Run: ${titleName}</h1></header>
|
|
4901
|
+
<p class="note">Generated locally by AgentInspect.</p>
|
|
4902
|
+
${includeMetadata ? `<section class="summary"><h2>Summary</h2><table>${summaryRows.join("")}</table>${confidenceHtml}</section>` : ""}
|
|
4903
|
+
<section class="tree"><h2>Execution tree</h2>${tree.children.length > 0 ? renderTreeHtml(tree.children) : "<p>No steps recorded.</p>"}</section>
|
|
4904
|
+
${errorsHtml}
|
|
4905
|
+
${attrsHtml}
|
|
4906
|
+
<footer>Generated locally by AgentInspect. Review for sensitive data before sharing.</footer>
|
|
4907
|
+
</body>
|
|
4908
|
+
</html>`;
|
|
4909
|
+
return {
|
|
4910
|
+
format: "html",
|
|
4911
|
+
content: html,
|
|
4912
|
+
contentType: "text/html",
|
|
4913
|
+
fileExtension: ".html",
|
|
4914
|
+
warnings
|
|
4915
|
+
};
|
|
4916
|
+
}
|
|
4917
|
+
|
|
4153
4918
|
// packages/core/src/exporters/markdown-exporter.ts
|
|
4154
4919
|
function renderTreeAscii(nodes, indent = "") {
|
|
4155
4920
|
const lines = [];
|
|
@@ -4175,6 +4940,7 @@ function exportMarkdown(tree, options) {
|
|
|
4175
4940
|
const includeAttributes = options?.includeAttributes ?? false;
|
|
4176
4941
|
const includeErrors = options?.includeErrors ?? true;
|
|
4177
4942
|
const maxLen = options?.maxAttributeLength ?? 500;
|
|
4943
|
+
const redacted = options?.redacted ?? true;
|
|
4178
4944
|
const titleName = tree.name ?? tree.runId;
|
|
4179
4945
|
const lines = [];
|
|
4180
4946
|
lines.push(`# AgentInspect Run: ${escapeMarkdown(titleName)}`);
|
|
@@ -4250,7 +5016,9 @@ function exportMarkdown(tree, options) {
|
|
|
4250
5016
|
for (const n of flat) {
|
|
4251
5017
|
if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
|
|
4252
5018
|
const compact = compactAttributes3(n.event.attributes, {
|
|
4253
|
-
maxLength: maxLen
|
|
5019
|
+
maxLength: maxLen,
|
|
5020
|
+
redacted
|
|
5021
|
+
});
|
|
4254
5022
|
lines.push(`### ${escapeMarkdown(n.event.name)}`);
|
|
4255
5023
|
lines.push("");
|
|
4256
5024
|
lines.push("```json");
|
|
@@ -4270,6 +5038,295 @@ function exportMarkdown(tree, options) {
|
|
|
4270
5038
|
warnings
|
|
4271
5039
|
};
|
|
4272
5040
|
}
|
|
5041
|
+
function hexFrom(seed, byteLen) {
|
|
5042
|
+
return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
5043
|
+
}
|
|
5044
|
+
function mapInspectKindToOI(kind, warnings) {
|
|
5045
|
+
switch (kind) {
|
|
5046
|
+
case "LLM":
|
|
5047
|
+
return { openInferenceKind: "LLM" };
|
|
5048
|
+
case "TOOL":
|
|
5049
|
+
return { openInferenceKind: "TOOL" };
|
|
5050
|
+
case "CHAIN":
|
|
5051
|
+
return { openInferenceKind: "CHAIN" };
|
|
5052
|
+
case "RETRIEVER":
|
|
5053
|
+
return { openInferenceKind: "RETRIEVER" };
|
|
5054
|
+
case "AGENT":
|
|
5055
|
+
return { openInferenceKind: "AGENT" };
|
|
5056
|
+
case "DECISION":
|
|
5057
|
+
warnings.push(
|
|
5058
|
+
`Ambiguous kind DECISION mapped to CHAIN for span compatibility (${EXPORT_PAYLOAD_VERSION}).`
|
|
5059
|
+
);
|
|
5060
|
+
return { openInferenceKind: "CHAIN" };
|
|
5061
|
+
case "RESULT":
|
|
5062
|
+
warnings.push(
|
|
5063
|
+
`Ambiguous kind RESULT mapped to UNKNOWN for span compatibility (${EXPORT_PAYLOAD_VERSION}).`
|
|
5064
|
+
);
|
|
5065
|
+
return { openInferenceKind: "UNKNOWN" };
|
|
5066
|
+
case "ERROR":
|
|
5067
|
+
warnings.push(`ERROR kind mapped to CHAIN for span compatibility.`);
|
|
5068
|
+
return { openInferenceKind: "CHAIN" };
|
|
5069
|
+
case "LOG":
|
|
5070
|
+
case "LOGIC":
|
|
5071
|
+
case "RUN":
|
|
5072
|
+
warnings.push(`${kind} mapped to CHAIN for span compatibility.`);
|
|
5073
|
+
return { openInferenceKind: "CHAIN" };
|
|
5074
|
+
default:
|
|
5075
|
+
warnings.push(`Unhandled InspectKind ${kind} mapped to UNKNOWN.`);
|
|
5076
|
+
return { openInferenceKind: "UNKNOWN" };
|
|
5077
|
+
}
|
|
5078
|
+
}
|
|
5079
|
+
function exportOpenInference(tree, options) {
|
|
5080
|
+
const warnings = [
|
|
5081
|
+
"OpenInference-compatible JSON export is experimental until verified against specific backends.",
|
|
5082
|
+
"This file was generated locally and not sent anywhere."
|
|
5083
|
+
];
|
|
5084
|
+
const traceId = hexFrom(`trace:${tree.runId}`, 16);
|
|
5085
|
+
const includeAttributes = options?.includeAttributes ?? false;
|
|
5086
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
5087
|
+
const pretty = options?.pretty ?? true;
|
|
5088
|
+
const spans = [];
|
|
5089
|
+
for (const n of flattenTree(tree)) {
|
|
5090
|
+
const ev = n.event;
|
|
5091
|
+
const spanId = hexFrom(`${tree.runId}:${ev.eventId}`, 8);
|
|
5092
|
+
const parentSpanHex = ev.parentId ? hexFrom(`${tree.runId}:${ev.parentId}`, 8) : void 0;
|
|
5093
|
+
const startNs = Math.round(ev.timestamp * 1e6);
|
|
5094
|
+
let endNs;
|
|
5095
|
+
if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
|
|
5096
|
+
endNs = startNs + Math.round(ev.durationMs * 1e6);
|
|
5097
|
+
}
|
|
5098
|
+
const { openInferenceKind } = mapInspectKindToOI(ev.kind, warnings);
|
|
5099
|
+
const attrs = {
|
|
5100
|
+
"openinference.span.kind": openInferenceKind,
|
|
5101
|
+
"agent_inspect.kind": ev.kind,
|
|
5102
|
+
"agent_inspect.confidence": ev.confidence,
|
|
5103
|
+
"agent_inspect.source.type": ev.source.type,
|
|
5104
|
+
"agent_inspect.run_id": tree.runId,
|
|
5105
|
+
"agent_inspect.event_id": ev.eventId,
|
|
5106
|
+
"agent_inspect.status": ev.status ?? "unset"
|
|
5107
|
+
};
|
|
5108
|
+
if (ev.durationMs !== void 0) {
|
|
5109
|
+
attrs["agent_inspect.duration_ms"] = ev.durationMs;
|
|
5110
|
+
}
|
|
5111
|
+
const meta = ev.attributes;
|
|
5112
|
+
if (meta?.model !== void 0 && typeof meta.model === "string") {
|
|
5113
|
+
attrs["llm.model_name"] = meta.model;
|
|
5114
|
+
}
|
|
5115
|
+
const tokens = meta?.tokens;
|
|
5116
|
+
if (tokens && typeof tokens === "object" && tokens !== null) {
|
|
5117
|
+
const inp = tokens.input;
|
|
5118
|
+
const outp = tokens.output;
|
|
5119
|
+
if (typeof inp === "number") attrs["llm.token_count.prompt"] = inp;
|
|
5120
|
+
if (typeof outp === "number") attrs["llm.token_count.completion"] = outp;
|
|
5121
|
+
}
|
|
5122
|
+
if (includeAttributes && meta && typeof meta === "object") {
|
|
5123
|
+
for (const [k, v] of Object.entries(meta)) {
|
|
5124
|
+
if (k === "tokens" || k === "model") continue;
|
|
5125
|
+
if (v !== void 0 && v !== null && typeof v !== "object") {
|
|
5126
|
+
attrs[`agent_inspect.preview.${k}`] = typeof v === "string" ? v.slice(0, maxLen) : v;
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
let status;
|
|
5131
|
+
if (ev.status === "error") {
|
|
5132
|
+
const msg = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error") : "error";
|
|
5133
|
+
status = { code: "ERROR", message: msg.slice(0, maxLen) };
|
|
5134
|
+
} else if (ev.status === "ok") {
|
|
5135
|
+
status = { code: "OK" };
|
|
5136
|
+
} else {
|
|
5137
|
+
status = { code: "UNSET" };
|
|
5138
|
+
}
|
|
5139
|
+
spans.push({
|
|
5140
|
+
trace_id: traceId,
|
|
5141
|
+
span_id: spanId,
|
|
5142
|
+
parent_span_id: parentSpanHex,
|
|
5143
|
+
name: ev.name,
|
|
5144
|
+
start_time_unix_nano: startNs,
|
|
5145
|
+
end_time_unix_nano: endNs,
|
|
5146
|
+
attributes: attrs,
|
|
5147
|
+
status
|
|
5148
|
+
});
|
|
5149
|
+
}
|
|
5150
|
+
const payload = {
|
|
5151
|
+
exporter: "agent-inspect",
|
|
5152
|
+
format: "openinference",
|
|
5153
|
+
compatibility: "openinference-compatible",
|
|
5154
|
+
version: EXPORT_PAYLOAD_VERSION,
|
|
5155
|
+
trace_id: traceId,
|
|
5156
|
+
spans,
|
|
5157
|
+
warnings
|
|
5158
|
+
};
|
|
5159
|
+
return {
|
|
5160
|
+
format: "openinference",
|
|
5161
|
+
content: JSON.stringify(payload, null, pretty ? 2 : void 0),
|
|
5162
|
+
contentType: "application/json",
|
|
5163
|
+
fileExtension: ".openinference.json",
|
|
5164
|
+
warnings
|
|
5165
|
+
};
|
|
5166
|
+
}
|
|
5167
|
+
function hexFrom2(seed, byteLen) {
|
|
5168
|
+
return crypto.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
5169
|
+
}
|
|
5170
|
+
function stringAttr(key, value) {
|
|
5171
|
+
return { key, value: { stringValue: value } };
|
|
5172
|
+
}
|
|
5173
|
+
function intAttr(key, value) {
|
|
5174
|
+
return { key, value: { intValue: String(value) } };
|
|
5175
|
+
}
|
|
5176
|
+
function genAiOperationName(kind) {
|
|
5177
|
+
switch (kind) {
|
|
5178
|
+
case "LLM":
|
|
5179
|
+
return "generate_content";
|
|
5180
|
+
case "TOOL":
|
|
5181
|
+
return "execute_tool";
|
|
5182
|
+
case "AGENT":
|
|
5183
|
+
return "invoke_agent";
|
|
5184
|
+
default:
|
|
5185
|
+
return void 0;
|
|
5186
|
+
}
|
|
5187
|
+
}
|
|
5188
|
+
function exportOtlpJson(tree, options) {
|
|
5189
|
+
const warnings = [
|
|
5190
|
+
"OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
|
|
5191
|
+
"Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload."
|
|
5192
|
+
];
|
|
5193
|
+
const traceId = hexFrom2(`trace:${tree.runId}`, 16);
|
|
5194
|
+
const includeAttributes = options?.includeAttributes ?? false;
|
|
5195
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
5196
|
+
const pretty = options?.pretty ?? true;
|
|
5197
|
+
const flat = flattenTree(tree);
|
|
5198
|
+
const spans = [];
|
|
5199
|
+
for (const n of flat) {
|
|
5200
|
+
const ev = n.event;
|
|
5201
|
+
const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
|
|
5202
|
+
const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
|
|
5203
|
+
const startNs = String(Math.round(ev.timestamp * 1e6));
|
|
5204
|
+
let endNs;
|
|
5205
|
+
if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
|
|
5206
|
+
endNs = String(Math.round(ev.timestamp * 1e6 + ev.durationMs * 1e6));
|
|
5207
|
+
}
|
|
5208
|
+
const attrs = [
|
|
5209
|
+
stringAttr("agent_inspect.kind", ev.kind),
|
|
5210
|
+
stringAttr("agent_inspect.confidence", ev.confidence),
|
|
5211
|
+
stringAttr("agent_inspect.source.type", ev.source.type),
|
|
5212
|
+
stringAttr("agent_inspect.run_id", tree.runId),
|
|
5213
|
+
stringAttr("agent_inspect.event_id", ev.eventId),
|
|
5214
|
+
stringAttr("agent_inspect.status", ev.status ?? "unset")
|
|
5215
|
+
];
|
|
5216
|
+
if (ev.durationMs !== void 0) {
|
|
5217
|
+
attrs.push(intAttr("agent_inspect.duration_ms", ev.durationMs));
|
|
5218
|
+
}
|
|
5219
|
+
const op = genAiOperationName(ev.kind);
|
|
5220
|
+
if (op !== void 0) {
|
|
5221
|
+
attrs.push(stringAttr("gen_ai.operation.name", op));
|
|
5222
|
+
}
|
|
5223
|
+
const meta = ev.attributes;
|
|
5224
|
+
if (meta?.model !== void 0 && typeof meta.model === "string") {
|
|
5225
|
+
attrs.push(stringAttr("gen_ai.request.model", meta.model.slice(0, maxLen)));
|
|
5226
|
+
}
|
|
5227
|
+
const tokens = meta?.tokens;
|
|
5228
|
+
if (tokens && typeof tokens === "object" && tokens !== null) {
|
|
5229
|
+
const inp = tokens.input;
|
|
5230
|
+
const outp = tokens.output;
|
|
5231
|
+
if (typeof inp === "number") attrs.push(intAttr("gen_ai.usage.input_tokens", inp));
|
|
5232
|
+
if (typeof outp === "number") attrs.push(intAttr("gen_ai.usage.output_tokens", outp));
|
|
5233
|
+
}
|
|
5234
|
+
if (includeAttributes && meta && typeof meta === "object") {
|
|
5235
|
+
for (const [k, v] of Object.entries(meta)) {
|
|
5236
|
+
if (k === "tokens" || k === "model") continue;
|
|
5237
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
5238
|
+
attrs.push(
|
|
5239
|
+
stringAttr(
|
|
5240
|
+
`agent_inspect.preview.${k}`,
|
|
5241
|
+
typeof v === "string" ? v.slice(0, maxLen) : String(v)
|
|
5242
|
+
)
|
|
5243
|
+
);
|
|
5244
|
+
}
|
|
5245
|
+
}
|
|
5246
|
+
}
|
|
5247
|
+
let statusCode = "STATUS_CODE_UNSET";
|
|
5248
|
+
let statusMessage;
|
|
5249
|
+
if (ev.status === "error") {
|
|
5250
|
+
statusCode = "STATUS_CODE_ERROR";
|
|
5251
|
+
statusMessage = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error").slice(0, maxLen) : "error";
|
|
5252
|
+
} else if (ev.status === "ok") {
|
|
5253
|
+
statusCode = "STATUS_CODE_OK";
|
|
5254
|
+
}
|
|
5255
|
+
const spanJson = {
|
|
5256
|
+
traceId,
|
|
5257
|
+
spanId,
|
|
5258
|
+
name: ev.name,
|
|
5259
|
+
kind: "SPAN_KIND_INTERNAL",
|
|
5260
|
+
startTimeUnixNano: startNs,
|
|
5261
|
+
attributes: attrs,
|
|
5262
|
+
status: {
|
|
5263
|
+
code: statusCode,
|
|
5264
|
+
...statusMessage !== void 0 ? { message: statusMessage } : {}
|
|
5265
|
+
}
|
|
5266
|
+
};
|
|
5267
|
+
if (parentSpanId !== void 0) {
|
|
5268
|
+
spanJson.parentSpanId = parentSpanId;
|
|
5269
|
+
}
|
|
5270
|
+
if (endNs !== void 0) {
|
|
5271
|
+
spanJson.endTimeUnixNano = endNs;
|
|
5272
|
+
}
|
|
5273
|
+
spans.push(spanJson);
|
|
5274
|
+
}
|
|
5275
|
+
const payload = {
|
|
5276
|
+
resourceSpans: [
|
|
5277
|
+
{
|
|
5278
|
+
resource: {
|
|
5279
|
+
attributes: [stringAttr("service.name", "agent-inspect")]
|
|
5280
|
+
},
|
|
5281
|
+
scopeSpans: [
|
|
5282
|
+
{
|
|
5283
|
+
scope: { name: "agent-inspect" },
|
|
5284
|
+
spans
|
|
5285
|
+
}
|
|
5286
|
+
]
|
|
5287
|
+
}
|
|
5288
|
+
]
|
|
5289
|
+
};
|
|
5290
|
+
return {
|
|
5291
|
+
format: "otlp-json",
|
|
5292
|
+
content: JSON.stringify(payload, null, pretty ? 2 : void 0),
|
|
5293
|
+
contentType: "application/json",
|
|
5294
|
+
fileExtension: ".otlp.json",
|
|
5295
|
+
warnings
|
|
5296
|
+
};
|
|
5297
|
+
}
|
|
5298
|
+
|
|
5299
|
+
// packages/core/src/exporters/index.ts
|
|
5300
|
+
function mergeExportDefaults(options) {
|
|
5301
|
+
return {
|
|
5302
|
+
format: options.format,
|
|
5303
|
+
includeMetadata: options.includeMetadata ?? true,
|
|
5304
|
+
includeAttributes: options.includeAttributes ?? false,
|
|
5305
|
+
includeErrors: options.includeErrors ?? true,
|
|
5306
|
+
pretty: options.pretty ?? true,
|
|
5307
|
+
redacted: options.redacted,
|
|
5308
|
+
maxAttributeLength: options.maxAttributeLength ?? 500,
|
|
5309
|
+
redactionProfile: options.redactionProfile ?? "local"
|
|
5310
|
+
};
|
|
5311
|
+
}
|
|
5312
|
+
function exportRunTree(tree, options) {
|
|
5313
|
+
const opts = mergeExportDefaults(options);
|
|
5314
|
+
const exportTree = opts.redactionProfile === "local" ? tree : redactRunTreeForExport(tree, { redactionProfile: opts.redactionProfile });
|
|
5315
|
+
switch (opts.format) {
|
|
5316
|
+
case "markdown":
|
|
5317
|
+
return exportMarkdown(exportTree, opts);
|
|
5318
|
+
case "html":
|
|
5319
|
+
return exportHtml(exportTree, opts);
|
|
5320
|
+
case "openinference":
|
|
5321
|
+
return exportOpenInference(exportTree, opts);
|
|
5322
|
+
case "otlp-json":
|
|
5323
|
+
return exportOtlpJson(exportTree, opts);
|
|
5324
|
+
default: {
|
|
5325
|
+
const _x = opts.format;
|
|
5326
|
+
throw new Error(`Unsupported export format: ${String(_x)}`);
|
|
5327
|
+
}
|
|
5328
|
+
}
|
|
5329
|
+
}
|
|
4273
5330
|
|
|
4274
5331
|
// packages/mcp-server/src/tools.ts
|
|
4275
5332
|
var READ_ONLY_TOOLS = [
|
|
@@ -4343,6 +5400,42 @@ var READ_ONLY_TOOLS = [
|
|
|
4343
5400
|
properties: { runId: { type: "string" } },
|
|
4344
5401
|
required: ["runId"]
|
|
4345
5402
|
}
|
|
5403
|
+
},
|
|
5404
|
+
{
|
|
5405
|
+
name: "summarize_failed_run",
|
|
5406
|
+
description: "Summarize a failed run with step errors and correlation metadata.",
|
|
5407
|
+
inputSchema: {
|
|
5408
|
+
type: "object",
|
|
5409
|
+
properties: { runId: { type: "string" } },
|
|
5410
|
+
required: ["runId"]
|
|
5411
|
+
}
|
|
5412
|
+
},
|
|
5413
|
+
{
|
|
5414
|
+
name: "retrieve_decision_notes",
|
|
5415
|
+
description: "List decision steps and decision metadata for one run.",
|
|
5416
|
+
inputSchema: {
|
|
5417
|
+
type: "object",
|
|
5418
|
+
properties: { runId: { type: "string" } },
|
|
5419
|
+
required: ["runId"]
|
|
5420
|
+
}
|
|
5421
|
+
},
|
|
5422
|
+
{
|
|
5423
|
+
name: "find_failed_observation",
|
|
5424
|
+
description: "Find failed observed outcomes in one run.",
|
|
5425
|
+
inputSchema: {
|
|
5426
|
+
type: "object",
|
|
5427
|
+
properties: { runId: { type: "string" } },
|
|
5428
|
+
required: ["runId"]
|
|
5429
|
+
}
|
|
5430
|
+
},
|
|
5431
|
+
{
|
|
5432
|
+
name: "create_share_safe_bundle",
|
|
5433
|
+
description: "Create an in-memory share-safe bundle manifest and redacted exports.",
|
|
5434
|
+
inputSchema: {
|
|
5435
|
+
type: "object",
|
|
5436
|
+
properties: { runId: { type: "string" } },
|
|
5437
|
+
required: ["runId"]
|
|
5438
|
+
}
|
|
4346
5439
|
}
|
|
4347
5440
|
];
|
|
4348
5441
|
function textResult(payload) {
|
|
@@ -4377,6 +5470,19 @@ async function openRunTrace(context, runId) {
|
|
|
4377
5470
|
function legacyTraceEvents(events) {
|
|
4378
5471
|
return persistedInspectEventsToTraceEvents(events);
|
|
4379
5472
|
}
|
|
5473
|
+
function redactionProfileForExport(context) {
|
|
5474
|
+
return context.redactionProfile === "local" ? "share" : context.redactionProfile;
|
|
5475
|
+
}
|
|
5476
|
+
function decisionNotes(events) {
|
|
5477
|
+
return events.filter(
|
|
5478
|
+
(event) => event.kind === "DECISION" || typeof event.attributes?.decisionId === "string" && event.attributes.decisionId !== ""
|
|
5479
|
+
).slice(0, 50).map((event) => ({
|
|
5480
|
+
name: event.name,
|
|
5481
|
+
kind: event.kind,
|
|
5482
|
+
status: event.status,
|
|
5483
|
+
decisionId: typeof event.attributes?.decisionId === "string" ? event.attributes.decisionId : void 0
|
|
5484
|
+
}));
|
|
5485
|
+
}
|
|
4380
5486
|
async function callReadOnlyTool(context, name, args = {}) {
|
|
4381
5487
|
switch (name) {
|
|
4382
5488
|
case "list_traces": {
|
|
@@ -4477,10 +5583,73 @@ async function callReadOnlyTool(context, name, args = {}) {
|
|
|
4477
5583
|
const { read } = await openRunTrace(context, runId);
|
|
4478
5584
|
const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
|
|
4479
5585
|
if (!run) return errorResult2(`Run tree not found: ${runId}`);
|
|
5586
|
+
const profile = redactionProfileForExport(context);
|
|
4480
5587
|
const markdown = exportMarkdown(run, {
|
|
4481
|
-
|
|
5588
|
+
redacted: true});
|
|
5589
|
+
return textResult({ runId, profile, markdown: markdown.content });
|
|
5590
|
+
}
|
|
5591
|
+
case "summarize_failed_run": {
|
|
5592
|
+
const runId = String(args.runId ?? "");
|
|
5593
|
+
const { read } = await openRunTrace(context, runId);
|
|
5594
|
+
const traceEvents = legacyTraceEvents(read.events);
|
|
5595
|
+
const summary = buildRunWhatSummary(traceEvents);
|
|
5596
|
+
return textResult({
|
|
5597
|
+
runId,
|
|
5598
|
+
status: summary.status,
|
|
5599
|
+
summary: renderRunWhat(summary),
|
|
5600
|
+
failedStepNames: summary.failedStepNames,
|
|
5601
|
+
correlation: summary.correlation ?? null
|
|
5602
|
+
});
|
|
5603
|
+
}
|
|
5604
|
+
case "retrieve_decision_notes": {
|
|
5605
|
+
const runId = String(args.runId ?? "");
|
|
5606
|
+
const { read } = await openRunTrace(context, runId);
|
|
5607
|
+
const notes = decisionNotes(read.events);
|
|
5608
|
+
return textResult({ runId, decisions: notes, count: notes.length });
|
|
5609
|
+
}
|
|
5610
|
+
case "find_failed_observation": {
|
|
5611
|
+
const runId = String(args.runId ?? "");
|
|
5612
|
+
const { read } = await openRunTrace(context, runId);
|
|
5613
|
+
const outcomes = extractOutcomesFromTraceEvents(legacyTraceEvents(read.events));
|
|
5614
|
+
const failed = outcomes.filter((outcome) => outcome.status === "failed");
|
|
5615
|
+
return textResult({
|
|
5616
|
+
runId,
|
|
5617
|
+
failed,
|
|
5618
|
+
count: failed.length
|
|
5619
|
+
});
|
|
5620
|
+
}
|
|
5621
|
+
case "create_share_safe_bundle": {
|
|
5622
|
+
const runId = String(args.runId ?? "");
|
|
5623
|
+
const { read } = await openRunTrace(context, runId);
|
|
5624
|
+
const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
|
|
5625
|
+
if (!run) return errorResult2(`Run tree not found: ${runId}`);
|
|
5626
|
+
const profile = redactionProfileForExport(context);
|
|
5627
|
+
const markdown = exportMarkdown(run, {
|
|
5628
|
+
redacted: true});
|
|
5629
|
+
const tree = exportRunTree(run, {
|
|
5630
|
+
format: "openinference",
|
|
5631
|
+
redacted: true,
|
|
5632
|
+
redactionProfile: profile
|
|
5633
|
+
});
|
|
5634
|
+
const metadata = buildBundleMetadata({
|
|
5635
|
+
agentInspectVersion: "mcp-server",
|
|
5636
|
+
profile,
|
|
5637
|
+
resolve: { runIds: [runId] },
|
|
5638
|
+
checks: {
|
|
5639
|
+
aggregateStatus: "SAFE",
|
|
5640
|
+
runs: [{ runId, status: "SAFE", errors: 0, warnings: 0, findings: 0 }]
|
|
5641
|
+
},
|
|
5642
|
+
files: ["report.md", "tree.json"]
|
|
5643
|
+
});
|
|
5644
|
+
return textResult({
|
|
5645
|
+
runId,
|
|
5646
|
+
profile,
|
|
5647
|
+
metadata,
|
|
5648
|
+
files: {
|
|
5649
|
+
"report.md": markdown.content,
|
|
5650
|
+
"tree.json": tree.content
|
|
5651
|
+
}
|
|
4482
5652
|
});
|
|
4483
|
-
return textResult({ runId, profile: context.redactionProfile, markdown: markdown.content });
|
|
4484
5653
|
}
|
|
4485
5654
|
default:
|
|
4486
5655
|
return errorResult2(`Unknown tool: ${name}`);
|