@agent-inspect/mcp-server 6.1.0 → 6.3.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.cjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var readline = require('readline');
|
|
4
4
|
var path = require('path');
|
|
5
5
|
var async_hooks = require('async_hooks');
|
|
6
|
-
require('crypto');
|
|
6
|
+
var crypto = require('crypto');
|
|
7
7
|
var promises = require('fs/promises');
|
|
8
8
|
var os = require('os');
|
|
9
9
|
require('nanoid');
|
|
@@ -15,6 +15,7 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
|
15
15
|
|
|
16
16
|
var readline__default = /*#__PURE__*/_interopDefault(readline);
|
|
17
17
|
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
18
|
+
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
18
19
|
var os__default = /*#__PURE__*/_interopDefault(os);
|
|
19
20
|
|
|
20
21
|
// packages/mcp-server/src/index.ts
|
|
@@ -44,6 +45,183 @@ function extractCorrelationMetadata(record) {
|
|
|
44
45
|
}
|
|
45
46
|
return found ? out : void 0;
|
|
46
47
|
}
|
|
48
|
+
var DEFAULT_REDACT_KEYS = [
|
|
49
|
+
"authorization",
|
|
50
|
+
"cookie",
|
|
51
|
+
"token",
|
|
52
|
+
"apiKey",
|
|
53
|
+
"password",
|
|
54
|
+
"secret",
|
|
55
|
+
"email"
|
|
56
|
+
];
|
|
57
|
+
function isRecord(v) {
|
|
58
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
59
|
+
}
|
|
60
|
+
function toKey(s) {
|
|
61
|
+
return s.toLowerCase();
|
|
62
|
+
}
|
|
63
|
+
function stableHash(value) {
|
|
64
|
+
const h = crypto__default.default.createHash("sha256").update(value, "utf8").digest("hex");
|
|
65
|
+
return h.slice(0, 8);
|
|
66
|
+
}
|
|
67
|
+
function compileRules(rules, extraKeys) {
|
|
68
|
+
const out = /* @__PURE__ */ new Map();
|
|
69
|
+
const set = (r) => {
|
|
70
|
+
const k = toKey(r.key);
|
|
71
|
+
out.set(k, { ...r, key: k });
|
|
72
|
+
};
|
|
73
|
+
for (const k of DEFAULT_REDACT_KEYS) {
|
|
74
|
+
set({ key: k, strategy: "full" });
|
|
75
|
+
}
|
|
76
|
+
for (const k of extraKeys ?? []) {
|
|
77
|
+
if (typeof k === "string" && k.length > 0) {
|
|
78
|
+
set({ key: k, strategy: "full" });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
for (const r of rules ?? []) {
|
|
82
|
+
if (typeof r === "string") {
|
|
83
|
+
set({ key: r, strategy: "full" });
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const key = r.key;
|
|
87
|
+
if (r.strategy === "full") set({ key, strategy: "full" });
|
|
88
|
+
if (r.strategy === "hash") set({ key, strategy: "hash" });
|
|
89
|
+
if (r.strategy === "prefix") {
|
|
90
|
+
set({ key, strategy: "prefix", keep: typeof r.keep === "number" ? r.keep : 8 });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return [...out.values()];
|
|
94
|
+
}
|
|
95
|
+
var Redactor = class {
|
|
96
|
+
#rules;
|
|
97
|
+
constructor(options) {
|
|
98
|
+
this.#rules = compileRules(options?.rules, options?.extraKeys);
|
|
99
|
+
}
|
|
100
|
+
redactValue(key, value) {
|
|
101
|
+
const k = toKey(key);
|
|
102
|
+
const rule = this.#rules.find((r) => r.key === k);
|
|
103
|
+
if (!rule) {
|
|
104
|
+
return this.#redactNested(value);
|
|
105
|
+
}
|
|
106
|
+
if (rule.strategy === "full") return "[REDACTED]";
|
|
107
|
+
const asString = typeof value === "string" ? value : typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" ? String(value) : void 0;
|
|
108
|
+
if (rule.strategy === "prefix") {
|
|
109
|
+
if (asString === void 0) return "[REDACTED]";
|
|
110
|
+
const keep = Math.max(0, Math.floor(rule.keep));
|
|
111
|
+
return asString.length <= keep ? `${asString}\u2026` : `${asString.slice(0, keep)}\u2026`;
|
|
112
|
+
}
|
|
113
|
+
if (rule.strategy === "hash") {
|
|
114
|
+
if (asString === void 0) return "[HASH:unknown]";
|
|
115
|
+
return `[HASH:${stableHash(asString)}]`;
|
|
116
|
+
}
|
|
117
|
+
return this.#redactNested(value);
|
|
118
|
+
}
|
|
119
|
+
redactRecord(record) {
|
|
120
|
+
const out = {};
|
|
121
|
+
for (const [k, v] of Object.entries(record)) {
|
|
122
|
+
out[k] = this.redactValue(k, v);
|
|
123
|
+
}
|
|
124
|
+
return out;
|
|
125
|
+
}
|
|
126
|
+
#redactNested(value) {
|
|
127
|
+
if (Array.isArray(value)) {
|
|
128
|
+
return value.map((v) => this.#redactNested(v));
|
|
129
|
+
}
|
|
130
|
+
if (isRecord(value)) {
|
|
131
|
+
const out = {};
|
|
132
|
+
for (const [k, v] of Object.entries(value)) {
|
|
133
|
+
out[k] = this.redactValue(k, v);
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
return value;
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// packages/core/src/redaction-profiles.ts
|
|
142
|
+
var SHARE_PROFILE_EXTRA_KEYS = [
|
|
143
|
+
"userEmail",
|
|
144
|
+
"customerEmail",
|
|
145
|
+
"phone",
|
|
146
|
+
"phoneNumber",
|
|
147
|
+
"address",
|
|
148
|
+
"ip",
|
|
149
|
+
"ipAddress",
|
|
150
|
+
"sessionId",
|
|
151
|
+
"requestId",
|
|
152
|
+
"correlationId",
|
|
153
|
+
"decisionId",
|
|
154
|
+
"groupId",
|
|
155
|
+
"customerId",
|
|
156
|
+
"userId",
|
|
157
|
+
"accountId",
|
|
158
|
+
"tenantId",
|
|
159
|
+
"orgId",
|
|
160
|
+
"organizationId",
|
|
161
|
+
"traceId",
|
|
162
|
+
"spanId",
|
|
163
|
+
"parentSpanId"
|
|
164
|
+
];
|
|
165
|
+
var STRICT_PROFILE_EXTRA_KEYS = [
|
|
166
|
+
"prompt",
|
|
167
|
+
"completion",
|
|
168
|
+
"input",
|
|
169
|
+
"output",
|
|
170
|
+
"inputPreview",
|
|
171
|
+
"outputPreview",
|
|
172
|
+
"message",
|
|
173
|
+
"messages",
|
|
174
|
+
"transcript",
|
|
175
|
+
"context",
|
|
176
|
+
"document",
|
|
177
|
+
"documents",
|
|
178
|
+
"chunk",
|
|
179
|
+
"chunks",
|
|
180
|
+
"retrieval",
|
|
181
|
+
"query"
|
|
182
|
+
];
|
|
183
|
+
function resolveRedactionProfile(profile = "local") {
|
|
184
|
+
switch (profile) {
|
|
185
|
+
case "local":
|
|
186
|
+
return { profile: "local", extraKeys: [] };
|
|
187
|
+
case "share":
|
|
188
|
+
return {
|
|
189
|
+
profile: "share",
|
|
190
|
+
extraKeys: SHARE_PROFILE_EXTRA_KEYS,
|
|
191
|
+
maxMetadataValueLengthCap: 500,
|
|
192
|
+
maxPreviewLengthCap: 200
|
|
193
|
+
};
|
|
194
|
+
case "strict":
|
|
195
|
+
return {
|
|
196
|
+
profile: "strict",
|
|
197
|
+
extraKeys: [...SHARE_PROFILE_EXTRA_KEYS, ...STRICT_PROFILE_EXTRA_KEYS],
|
|
198
|
+
maxMetadataValueLengthCap: 200,
|
|
199
|
+
maxPreviewLengthCap: 80
|
|
200
|
+
};
|
|
201
|
+
default:
|
|
202
|
+
return { profile: "local", extraKeys: [] };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
function isPreviewKey(key) {
|
|
206
|
+
return key.toLowerCase().includes("preview");
|
|
207
|
+
}
|
|
208
|
+
function applyProfileMetadataCaps(maxMetadataValueLength, maxPreviewLength, resolved) {
|
|
209
|
+
let meta = maxMetadataValueLength;
|
|
210
|
+
let preview = maxPreviewLength;
|
|
211
|
+
if (resolved.maxMetadataValueLengthCap !== void 0) {
|
|
212
|
+
meta = Math.min(meta, resolved.maxMetadataValueLengthCap);
|
|
213
|
+
}
|
|
214
|
+
if (resolved.maxPreviewLengthCap !== void 0) {
|
|
215
|
+
preview = Math.min(preview, resolved.maxPreviewLengthCap);
|
|
216
|
+
}
|
|
217
|
+
return { maxMetadataValueLength: meta, maxPreviewLength: preview };
|
|
218
|
+
}
|
|
219
|
+
function truncateStringForProfile(value, key, maxMetadataValueLength, maxPreviewLength) {
|
|
220
|
+
const max = isPreviewKey(key) ? maxPreviewLength : maxMetadataValueLength;
|
|
221
|
+
if (max <= 0) return "\u2026";
|
|
222
|
+
if (value.length <= max) return value;
|
|
223
|
+
return `${value.slice(0, max)}\u2026`;
|
|
224
|
+
}
|
|
47
225
|
|
|
48
226
|
// packages/core/src/types.ts
|
|
49
227
|
var STEP_TYPES = [
|
|
@@ -55,14 +233,14 @@ var STEP_TYPES = [
|
|
|
55
233
|
"state",
|
|
56
234
|
"custom"
|
|
57
235
|
];
|
|
58
|
-
function
|
|
236
|
+
function isRecord2(value) {
|
|
59
237
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
60
238
|
}
|
|
61
239
|
function isStepType(value) {
|
|
62
240
|
return typeof value === "string" && STEP_TYPES.includes(value);
|
|
63
241
|
}
|
|
64
242
|
function isTraceEvent(value) {
|
|
65
|
-
if (!
|
|
243
|
+
if (!isRecord2(value)) return false;
|
|
66
244
|
if (value.schemaVersion !== "0.1") return false;
|
|
67
245
|
if (typeof value.timestamp !== "number") return false;
|
|
68
246
|
if (typeof value.event !== "string") return false;
|
|
@@ -122,7 +300,7 @@ var PERSISTED_EVENT_STATUSES = [
|
|
|
122
300
|
"error",
|
|
123
301
|
"unknown"
|
|
124
302
|
];
|
|
125
|
-
function
|
|
303
|
+
function isRecord3(value) {
|
|
126
304
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
127
305
|
}
|
|
128
306
|
function isString(value) {
|
|
@@ -153,21 +331,21 @@ function isPersistedEventStatus(value) {
|
|
|
153
331
|
return typeof value === "string" && PERSISTED_EVENT_STATUSES.includes(value);
|
|
154
332
|
}
|
|
155
333
|
function isPersistedEventSource(value) {
|
|
156
|
-
if (!
|
|
334
|
+
if (!isRecord3(value)) return false;
|
|
157
335
|
if (!isPersistedEventSourceType(value.type)) return false;
|
|
158
336
|
if (!isOptionalString(value.name)) return false;
|
|
159
337
|
if (!isOptionalString(value.version)) return false;
|
|
160
338
|
return true;
|
|
161
339
|
}
|
|
162
340
|
function isPersistedInspectError(value) {
|
|
163
|
-
if (!
|
|
341
|
+
if (!isRecord3(value)) return false;
|
|
164
342
|
if (!isNonEmptyString2(value.message)) return false;
|
|
165
343
|
if (!isOptionalString(value.name)) return false;
|
|
166
344
|
if (!isOptionalString(value.code)) return false;
|
|
167
345
|
return true;
|
|
168
346
|
}
|
|
169
347
|
function isPersistedTokenUsage(value) {
|
|
170
|
-
if (!
|
|
348
|
+
if (!isRecord3(value)) return false;
|
|
171
349
|
if (!isOptionalNonNegativeNumber(value.input)) return false;
|
|
172
350
|
if (!isOptionalNonNegativeNumber(value.output)) return false;
|
|
173
351
|
if (!isOptionalNonNegativeNumber(value.total)) return false;
|
|
@@ -175,14 +353,14 @@ function isPersistedTokenUsage(value) {
|
|
|
175
353
|
return true;
|
|
176
354
|
}
|
|
177
355
|
function isPersistedTraceContext(value) {
|
|
178
|
-
if (!
|
|
356
|
+
if (!isRecord3(value)) return false;
|
|
179
357
|
if (!isOptionalString(value.traceId)) return false;
|
|
180
358
|
if (!isOptionalString(value.spanId)) return false;
|
|
181
359
|
if (!isOptionalString(value.parentSpanId)) return false;
|
|
182
360
|
return true;
|
|
183
361
|
}
|
|
184
362
|
function isPersistedInspectEvent(value) {
|
|
185
|
-
if (!
|
|
363
|
+
if (!isRecord3(value)) return false;
|
|
186
364
|
if (value.schemaVersion !== "0.2" && value.schemaVersion !== "1.0") {
|
|
187
365
|
return false;
|
|
188
366
|
}
|
|
@@ -204,7 +382,7 @@ function isPersistedInspectEvent(value) {
|
|
|
204
382
|
if (value.durationMs !== void 0 && !isNonNegativeNumber(value.durationMs)) {
|
|
205
383
|
return false;
|
|
206
384
|
}
|
|
207
|
-
if (value.attributes !== void 0 && !
|
|
385
|
+
if (value.attributes !== void 0 && !isRecord3(value.attributes)) {
|
|
208
386
|
return false;
|
|
209
387
|
}
|
|
210
388
|
if (value.error !== void 0 && !isPersistedInspectError(value.error)) {
|
|
@@ -556,6 +734,24 @@ function parseDuration(duration) {
|
|
|
556
734
|
}
|
|
557
735
|
}
|
|
558
736
|
}
|
|
737
|
+
function formatDuration(ms) {
|
|
738
|
+
if (!Number.isFinite(ms)) {
|
|
739
|
+
return "0ms";
|
|
740
|
+
}
|
|
741
|
+
if (ms < 0) {
|
|
742
|
+
throw new Error(`formatDuration: ms must be non-negative (got ${ms})`);
|
|
743
|
+
}
|
|
744
|
+
if (ms < 1e3) {
|
|
745
|
+
return `${Math.floor(ms)}ms`;
|
|
746
|
+
}
|
|
747
|
+
if (ms < 6e4) {
|
|
748
|
+
return `${(ms / 1e3).toFixed(2)}s`;
|
|
749
|
+
}
|
|
750
|
+
if (ms < 36e5) {
|
|
751
|
+
return `${(ms / 6e4).toFixed(1)}m`;
|
|
752
|
+
}
|
|
753
|
+
return `${(ms / 36e5).toFixed(1)}h`;
|
|
754
|
+
}
|
|
559
755
|
|
|
560
756
|
// packages/core/src/utils.ts
|
|
561
757
|
var DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
|
|
@@ -565,6 +761,9 @@ var FALLBACK_TRACE_DIR = path__default.default.join(
|
|
|
565
761
|
"agent-inspect",
|
|
566
762
|
RUNS_DIR_NAME
|
|
567
763
|
);
|
|
764
|
+
function formatDuration2(ms) {
|
|
765
|
+
return formatDuration(ms);
|
|
766
|
+
}
|
|
568
767
|
function getDefaultTraceDir() {
|
|
569
768
|
const envDir = process.env.AGENT_INSPECT_TRACE_DIR;
|
|
570
769
|
if (typeof envDir === "string" && envDir.trim() !== "") {
|
|
@@ -619,11 +818,11 @@ function warn(message, error) {
|
|
|
619
818
|
}
|
|
620
819
|
|
|
621
820
|
// packages/core/src/read-trace.ts
|
|
622
|
-
function
|
|
821
|
+
function isRecord4(value) {
|
|
623
822
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
624
823
|
}
|
|
625
824
|
function detectLineFormat(parsed) {
|
|
626
|
-
if (!
|
|
825
|
+
if (!isRecord4(parsed)) return "unknown";
|
|
627
826
|
if (parsed.schemaVersion === "0.1") return "0.1";
|
|
628
827
|
if (parsed.schemaVersion === "0.2") return "0.2";
|
|
629
828
|
if (parsed.schemaVersion === "1.0") return "1.0";
|
|
@@ -695,7 +894,7 @@ function parseTraceJsonl(raw, options = {}) {
|
|
|
695
894
|
}
|
|
696
895
|
|
|
697
896
|
// packages/core/src/storage.ts
|
|
698
|
-
function
|
|
897
|
+
function isRecord5(value) {
|
|
699
898
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
700
899
|
}
|
|
701
900
|
function nonEmptyString(value) {
|
|
@@ -706,7 +905,7 @@ function finiteNumber(value) {
|
|
|
706
905
|
}
|
|
707
906
|
function optionalErrorInfo(value) {
|
|
708
907
|
if (value === void 0) return true;
|
|
709
|
-
if (!
|
|
908
|
+
if (!isRecord5(value)) return false;
|
|
710
909
|
if (typeof value.message !== "string") return false;
|
|
711
910
|
if ("stack" in value && value.stack !== void 0) {
|
|
712
911
|
if (typeof value.stack !== "string") return false;
|
|
@@ -714,7 +913,7 @@ function optionalErrorInfo(value) {
|
|
|
714
913
|
return true;
|
|
715
914
|
}
|
|
716
915
|
function validateEvent(event) {
|
|
717
|
-
if (!
|
|
916
|
+
if (!isRecord5(event)) return false;
|
|
718
917
|
if (event.schemaVersion !== "0.1") return false;
|
|
719
918
|
if (!finiteNumber(event.timestamp)) return false;
|
|
720
919
|
if (typeof event.event !== "string") return false;
|
|
@@ -723,7 +922,7 @@ function validateEvent(event) {
|
|
|
723
922
|
if (!nonEmptyString(event.runId) || !nonEmptyString(event.name) || !finiteNumber(event.startTime)) {
|
|
724
923
|
return false;
|
|
725
924
|
}
|
|
726
|
-
if (event.metadata !== void 0 && !
|
|
925
|
+
if (event.metadata !== void 0 && !isRecord5(event.metadata)) {
|
|
727
926
|
return false;
|
|
728
927
|
}
|
|
729
928
|
return true;
|
|
@@ -738,7 +937,7 @@ function validateEvent(event) {
|
|
|
738
937
|
if (event.parentId !== void 0 && typeof event.parentId !== "string") {
|
|
739
938
|
return false;
|
|
740
939
|
}
|
|
741
|
-
if (event.metadata !== void 0 && !
|
|
940
|
+
if (event.metadata !== void 0 && !isRecord5(event.metadata)) {
|
|
742
941
|
return false;
|
|
743
942
|
}
|
|
744
943
|
return true;
|
|
@@ -960,6 +1159,129 @@ async function extractMetadata(filePath, _quickScan) {
|
|
|
960
1159
|
createdAt: stats.birthtime
|
|
961
1160
|
};
|
|
962
1161
|
}
|
|
1162
|
+
function isNonNegativeFiniteNumber(value) {
|
|
1163
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
1164
|
+
}
|
|
1165
|
+
function buildRunSummary(events) {
|
|
1166
|
+
const started = events.find(
|
|
1167
|
+
(e) => e.event === "run_started"
|
|
1168
|
+
);
|
|
1169
|
+
const completed = events.filter(
|
|
1170
|
+
(e) => e.event === "run_completed"
|
|
1171
|
+
);
|
|
1172
|
+
const lastCompleted = completed[completed.length - 1];
|
|
1173
|
+
const runId = started?.runId ?? events.find((e) => typeof e.runId === "string")?.runId ?? "unknown-run";
|
|
1174
|
+
const name = typeof started?.name === "string" && started.name.trim() !== "" ? started.name : void 0;
|
|
1175
|
+
const status = lastCompleted ? lastCompleted.status : started ? "running" : "unknown";
|
|
1176
|
+
const durationMs = lastCompleted && isFiniteNumber(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
|
|
1177
|
+
started && isFiniteNumber(started.startTime) ? started.startTime : void 0;
|
|
1178
|
+
const steps = /* @__PURE__ */ new Map();
|
|
1179
|
+
for (const e of events) {
|
|
1180
|
+
if (e.event === "step_started") {
|
|
1181
|
+
const s = e;
|
|
1182
|
+
steps.set(s.stepId, {
|
|
1183
|
+
type: s.type,
|
|
1184
|
+
name: s.name,
|
|
1185
|
+
status: "running",
|
|
1186
|
+
parentId: s.parentId,
|
|
1187
|
+
tokensInput: isNonNegativeFiniteNumber(s.metadata?.tokens?.input) ? s.metadata.tokens.input : void 0,
|
|
1188
|
+
tokensOutput: isNonNegativeFiniteNumber(s.metadata?.tokens?.output) ? s.metadata.tokens.output : void 0,
|
|
1189
|
+
tokensTotal: isNonNegativeFiniteNumber(s.metadata?.tokens?.total) ? s.metadata.tokens.total : void 0,
|
|
1190
|
+
tokensCached: isNonNegativeFiniteNumber(s.metadata?.tokens?.cached) ? s.metadata.tokens.cached : void 0
|
|
1191
|
+
});
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
for (const e of events) {
|
|
1195
|
+
if (e.event === "step_completed") {
|
|
1196
|
+
const c = e;
|
|
1197
|
+
const existing = steps.get(c.stepId);
|
|
1198
|
+
if (!existing) continue;
|
|
1199
|
+
existing.status = c.status;
|
|
1200
|
+
existing.durationMs = c.durationMs;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
let totalSteps = 0;
|
|
1204
|
+
let llmSteps = 0;
|
|
1205
|
+
let toolSteps = 0;
|
|
1206
|
+
let logicSteps = 0;
|
|
1207
|
+
let errorSteps = 0;
|
|
1208
|
+
let maxDepth = 0;
|
|
1209
|
+
let longestStep;
|
|
1210
|
+
let totalTokensInput = 0;
|
|
1211
|
+
let totalTokensOutput = 0;
|
|
1212
|
+
let totalTokensTotal = 0;
|
|
1213
|
+
let totalTokensCached = 0;
|
|
1214
|
+
let tokenBearingSteps = 0;
|
|
1215
|
+
let stepsWithKnownTotal = 0;
|
|
1216
|
+
let hasCachedTokens = false;
|
|
1217
|
+
const depthCache = /* @__PURE__ */ new Map();
|
|
1218
|
+
const computeDepth = (stepId) => {
|
|
1219
|
+
const cached = depthCache.get(stepId);
|
|
1220
|
+
if (cached !== void 0) return cached;
|
|
1221
|
+
const node = steps.get(stepId);
|
|
1222
|
+
if (!node) return 0;
|
|
1223
|
+
const parent = node.parentId;
|
|
1224
|
+
if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
|
|
1225
|
+
depthCache.set(stepId, 0);
|
|
1226
|
+
return 0;
|
|
1227
|
+
}
|
|
1228
|
+
const d = Math.min(1e3, computeDepth(parent) + 1);
|
|
1229
|
+
depthCache.set(stepId, d);
|
|
1230
|
+
return d;
|
|
1231
|
+
};
|
|
1232
|
+
for (const [id, s] of steps.entries()) {
|
|
1233
|
+
totalSteps += 1;
|
|
1234
|
+
if (s.type === "llm") llmSteps += 1;
|
|
1235
|
+
else if (s.type === "tool") toolSteps += 1;
|
|
1236
|
+
else logicSteps += 1;
|
|
1237
|
+
if (s.status === "error") errorSteps += 1;
|
|
1238
|
+
const depth = computeDepth(id);
|
|
1239
|
+
if (depth > maxDepth) maxDepth = depth;
|
|
1240
|
+
if (typeof s.durationMs === "number" && Number.isFinite(s.durationMs)) {
|
|
1241
|
+
if (!longestStep || s.durationMs > longestStep.durationMs) {
|
|
1242
|
+
longestStep = { name: s.name, durationMs: s.durationMs, type: s.type };
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
if (s.tokensInput !== void 0 || s.tokensOutput !== void 0 || s.tokensTotal !== void 0 || s.tokensCached !== void 0) {
|
|
1246
|
+
tokenBearingSteps += 1;
|
|
1247
|
+
if (s.tokensInput !== void 0) totalTokensInput += s.tokensInput;
|
|
1248
|
+
if (s.tokensOutput !== void 0) totalTokensOutput += s.tokensOutput;
|
|
1249
|
+
if (s.tokensTotal !== void 0) {
|
|
1250
|
+
totalTokensTotal += s.tokensTotal;
|
|
1251
|
+
stepsWithKnownTotal += 1;
|
|
1252
|
+
} else if (s.tokensInput !== void 0 && s.tokensOutput !== void 0) {
|
|
1253
|
+
totalTokensTotal += s.tokensInput + s.tokensOutput;
|
|
1254
|
+
stepsWithKnownTotal += 1;
|
|
1255
|
+
}
|
|
1256
|
+
if (s.tokensCached !== void 0) {
|
|
1257
|
+
totalTokensCached += s.tokensCached;
|
|
1258
|
+
hasCachedTokens = true;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
const summary = {
|
|
1263
|
+
runId,
|
|
1264
|
+
name,
|
|
1265
|
+
status,
|
|
1266
|
+
durationMs,
|
|
1267
|
+
totalSteps,
|
|
1268
|
+
llmSteps,
|
|
1269
|
+
toolSteps,
|
|
1270
|
+
logicSteps,
|
|
1271
|
+
errorSteps,
|
|
1272
|
+
maxDepth,
|
|
1273
|
+
...longestStep ? { longestStep } : {},
|
|
1274
|
+
...tokenBearingSteps > 0 ? {
|
|
1275
|
+
totalTokens: {
|
|
1276
|
+
input: totalTokensInput,
|
|
1277
|
+
output: totalTokensOutput,
|
|
1278
|
+
...stepsWithKnownTotal === tokenBearingSteps ? { total: totalTokensTotal } : {},
|
|
1279
|
+
...hasCachedTokens ? { cached: totalTokensCached } : {}
|
|
1280
|
+
}
|
|
1281
|
+
} : {}
|
|
1282
|
+
};
|
|
1283
|
+
return summary;
|
|
1284
|
+
}
|
|
963
1285
|
|
|
964
1286
|
// packages/core/src/trace-filter.ts
|
|
965
1287
|
function toLower(s) {
|
|
@@ -1118,6 +1440,145 @@ function buildRunTimeline(events, options = {}) {
|
|
|
1118
1440
|
};
|
|
1119
1441
|
}
|
|
1120
1442
|
|
|
1443
|
+
// packages/core/src/what.ts
|
|
1444
|
+
function pickCorrelation2(metadata) {
|
|
1445
|
+
if (!metadata) return void 0;
|
|
1446
|
+
const out = {};
|
|
1447
|
+
for (const key of [
|
|
1448
|
+
"correlationId",
|
|
1449
|
+
"requestId",
|
|
1450
|
+
"decisionId",
|
|
1451
|
+
"groupId"
|
|
1452
|
+
]) {
|
|
1453
|
+
const value = metadata[key];
|
|
1454
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1455
|
+
out[key] = value;
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
1459
|
+
}
|
|
1460
|
+
function stepMixLine(summary) {
|
|
1461
|
+
const parts = [];
|
|
1462
|
+
if (summary.llmSteps > 0) parts.push(`${summary.llmSteps} LLM`);
|
|
1463
|
+
if (summary.toolSteps > 0) parts.push(`${summary.toolSteps} tool`);
|
|
1464
|
+
if (summary.logicSteps > 0) parts.push(`${summary.logicSteps} logic`);
|
|
1465
|
+
return parts.length > 0 ? parts.join(", ") : "none";
|
|
1466
|
+
}
|
|
1467
|
+
function outcomeLine(summary) {
|
|
1468
|
+
if (summary.status === "success") {
|
|
1469
|
+
return summary.errorSteps > 0 ? "Completed with step errors recorded." : "Completed successfully.";
|
|
1470
|
+
}
|
|
1471
|
+
if (summary.status === "error") {
|
|
1472
|
+
if (summary.failedStepNames.length > 0) {
|
|
1473
|
+
const names = summary.failedStepNames.slice(0, 3).join(", ");
|
|
1474
|
+
const suffix = summary.failedStepNames.length > 3 ? ` (+${summary.failedStepNames.length - 3} more)` : "";
|
|
1475
|
+
return `Failed at step(s): ${names}${suffix}.`;
|
|
1476
|
+
}
|
|
1477
|
+
if (summary.runErrorMessage) {
|
|
1478
|
+
return `Run failed: ${summary.runErrorMessage}`;
|
|
1479
|
+
}
|
|
1480
|
+
return "Run failed.";
|
|
1481
|
+
}
|
|
1482
|
+
if (summary.status === "running") {
|
|
1483
|
+
return "Run is still in progress (no run_completed).";
|
|
1484
|
+
}
|
|
1485
|
+
return "Outcome unknown \u2014 inspect events may be incomplete.";
|
|
1486
|
+
}
|
|
1487
|
+
function buildRunWhatSummary(events) {
|
|
1488
|
+
const base = buildRunSummary(events);
|
|
1489
|
+
const started = events.find(
|
|
1490
|
+
(e) => e.event === "run_started"
|
|
1491
|
+
);
|
|
1492
|
+
const completed = events.filter(
|
|
1493
|
+
(e) => e.event === "run_completed"
|
|
1494
|
+
);
|
|
1495
|
+
const lastCompleted = completed[completed.length - 1];
|
|
1496
|
+
const failedStepNames = [];
|
|
1497
|
+
const stepNames = /* @__PURE__ */ new Map();
|
|
1498
|
+
for (const e of events) {
|
|
1499
|
+
if (e.event === "step_started") {
|
|
1500
|
+
const s = e;
|
|
1501
|
+
stepNames.set(s.stepId, s.name);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
for (const e of events) {
|
|
1505
|
+
if (e.event === "step_completed") {
|
|
1506
|
+
const sc = e;
|
|
1507
|
+
if (sc.status === "error") {
|
|
1508
|
+
failedStepNames.push(stepNames.get(sc.stepId) ?? sc.stepId);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return {
|
|
1513
|
+
runId: base.runId,
|
|
1514
|
+
name: base.name,
|
|
1515
|
+
status: base.status,
|
|
1516
|
+
durationMs: base.durationMs,
|
|
1517
|
+
totalSteps: base.totalSteps,
|
|
1518
|
+
llmSteps: base.llmSteps,
|
|
1519
|
+
toolSteps: base.toolSteps,
|
|
1520
|
+
logicSteps: base.logicSteps,
|
|
1521
|
+
errorSteps: base.errorSteps,
|
|
1522
|
+
maxDepth: base.maxDepth,
|
|
1523
|
+
longestStep: base.longestStep,
|
|
1524
|
+
totalTokens: base.totalTokens,
|
|
1525
|
+
correlation: pickCorrelation2(started?.metadata),
|
|
1526
|
+
failedStepNames,
|
|
1527
|
+
runErrorMessage: lastCompleted?.error?.message
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
function renderRunWhat(summary, options = {}) {
|
|
1531
|
+
const showCorrelation = options.correlation !== false;
|
|
1532
|
+
const lines = [];
|
|
1533
|
+
const label = summary.name ?? summary.runId;
|
|
1534
|
+
lines.push(`What: ${label}`);
|
|
1535
|
+
const duration = summary.durationMs !== void 0 ? formatDuration2(summary.durationMs) : "\u2014";
|
|
1536
|
+
lines.push(
|
|
1537
|
+
`Status: ${summary.status} \xB7 Duration: ${duration} \xB7 Steps: ${summary.totalSteps} (${stepMixLine(summary)})`
|
|
1538
|
+
);
|
|
1539
|
+
if (summary.totalTokens) {
|
|
1540
|
+
const tokenParts = [
|
|
1541
|
+
`${summary.totalTokens.input} in`,
|
|
1542
|
+
`${summary.totalTokens.output} out`
|
|
1543
|
+
];
|
|
1544
|
+
if (summary.totalTokens.total !== void 0) {
|
|
1545
|
+
tokenParts.push(`${summary.totalTokens.total} total`);
|
|
1546
|
+
}
|
|
1547
|
+
if (summary.totalTokens.cached !== void 0) {
|
|
1548
|
+
tokenParts.push(`${summary.totalTokens.cached} cached`);
|
|
1549
|
+
}
|
|
1550
|
+
lines.push(`Tokens: ${tokenParts.join(" / ")}`);
|
|
1551
|
+
}
|
|
1552
|
+
if (showCorrelation && summary.correlation) {
|
|
1553
|
+
const parts = [];
|
|
1554
|
+
if (summary.correlation.correlationId) {
|
|
1555
|
+
parts.push(`correlationId=${summary.correlation.correlationId}`);
|
|
1556
|
+
}
|
|
1557
|
+
if (summary.correlation.requestId) {
|
|
1558
|
+
parts.push(`requestId=${summary.correlation.requestId}`);
|
|
1559
|
+
}
|
|
1560
|
+
if (summary.correlation.decisionId) {
|
|
1561
|
+
parts.push(`decisionId=${summary.correlation.decisionId}`);
|
|
1562
|
+
}
|
|
1563
|
+
if (summary.correlation.groupId) {
|
|
1564
|
+
parts.push(`groupId=${summary.correlation.groupId}`);
|
|
1565
|
+
}
|
|
1566
|
+
if (parts.length > 0) {
|
|
1567
|
+
lines.push(`Correlation: ${parts.join(", ")}`);
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
lines.push(`Outcome: ${outcomeLine(summary)}`);
|
|
1571
|
+
if (summary.longestStep && summary.totalSteps > 0) {
|
|
1572
|
+
lines.push(
|
|
1573
|
+
`Slowest: ${summary.longestStep.name} (${formatDuration2(summary.longestStep.durationMs)}, ${summary.longestStep.type})`
|
|
1574
|
+
);
|
|
1575
|
+
}
|
|
1576
|
+
if (summary.maxDepth > 0) {
|
|
1577
|
+
lines.push(`Max depth: ${summary.maxDepth}`);
|
|
1578
|
+
}
|
|
1579
|
+
return lines.join("\n");
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1121
1582
|
// packages/core/src/search.ts
|
|
1122
1583
|
function parseDurationFilter(expr) {
|
|
1123
1584
|
const raw = expr.trim();
|
|
@@ -1337,6 +1798,39 @@ async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
|
|
|
1337
1798
|
return metas;
|
|
1338
1799
|
}
|
|
1339
1800
|
|
|
1801
|
+
// packages/core/src/bundle/safety-status.ts
|
|
1802
|
+
function aggregateBundleSafeStatus(statuses) {
|
|
1803
|
+
if (statuses.length === 0) return "UNKNOWN";
|
|
1804
|
+
if (statuses.some((status) => status === "UNSAFE")) return "UNSAFE";
|
|
1805
|
+
if (statuses.some((status) => status === "UNKNOWN")) return "UNKNOWN";
|
|
1806
|
+
if (statuses.some((status) => status === "SAFE WITH WARNINGS")) return "SAFE WITH WARNINGS";
|
|
1807
|
+
return "SAFE";
|
|
1808
|
+
}
|
|
1809
|
+
function toMetadataSafeStatus(status) {
|
|
1810
|
+
if (status === "SAFE WITH WARNINGS") return "SAFE_WITH_WARNINGS";
|
|
1811
|
+
return status;
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1814
|
+
// packages/core/src/bundle/manifest.ts
|
|
1815
|
+
var BUNDLE_NOTE = "Generated locally by AgentInspect. Bundles are derived copies for review \u2014 not compliance or security certification. Review before sharing.";
|
|
1816
|
+
function buildBundleMetadata(parts) {
|
|
1817
|
+
const aggregate = aggregateBundleSafeStatus(
|
|
1818
|
+
parts.checks.runs.map((run) => run.status)
|
|
1819
|
+
);
|
|
1820
|
+
return {
|
|
1821
|
+
createdAt: parts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1822
|
+
agentInspectVersion: parts.agentInspectVersion,
|
|
1823
|
+
redactionProfile: parts.profile,
|
|
1824
|
+
sourceTraceCount: parts.resolve.runIds.length,
|
|
1825
|
+
runIds: [...parts.resolve.runIds],
|
|
1826
|
+
safeStatus: toMetadataSafeStatus(aggregate),
|
|
1827
|
+
files: [...parts.files].sort((a, b) => a.localeCompare(b)),
|
|
1828
|
+
note: BUNDLE_NOTE,
|
|
1829
|
+
...parts.resolve.sessionId !== void 0 ? { sessionId: parts.resolve.sessionId } : {},
|
|
1830
|
+
...parts.resolve.since !== void 0 ? { since: parts.resolve.since } : {}
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1340
1834
|
// packages/core/src/checks/index.ts
|
|
1341
1835
|
var SEVERITY_RANK = {
|
|
1342
1836
|
error: 0,
|
|
@@ -1645,14 +2139,14 @@ function runTraceChecks(input, options = {}) {
|
|
|
1645
2139
|
}
|
|
1646
2140
|
|
|
1647
2141
|
// packages/core/src/persisted/token-usage.ts
|
|
1648
|
-
function
|
|
2142
|
+
function isRecord6(value) {
|
|
1649
2143
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1650
2144
|
}
|
|
1651
2145
|
function nonNegativeFinite(value) {
|
|
1652
2146
|
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
1653
2147
|
}
|
|
1654
2148
|
function normalizeTokenUsage(value) {
|
|
1655
|
-
if (!
|
|
2149
|
+
if (!isRecord6(value)) return void 0;
|
|
1656
2150
|
const input = nonNegativeFinite(value.input);
|
|
1657
2151
|
const output = nonNegativeFinite(value.output);
|
|
1658
2152
|
const suppliedTotal = nonNegativeFinite(value.total);
|
|
@@ -2410,7 +2904,7 @@ function persistedEventsForParsedTrace(parsed) {
|
|
|
2410
2904
|
sourceName: "agent-inspect-jsonl-reader"
|
|
2411
2905
|
});
|
|
2412
2906
|
}
|
|
2413
|
-
function
|
|
2907
|
+
function isRecord7(value) {
|
|
2414
2908
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2415
2909
|
}
|
|
2416
2910
|
function isNonEmptyString3(value) {
|
|
@@ -2425,13 +2919,13 @@ function readStringField(record, keys) {
|
|
|
2425
2919
|
}
|
|
2426
2920
|
function readRecordField(record, key) {
|
|
2427
2921
|
const value = record[key];
|
|
2428
|
-
return
|
|
2922
|
+
return isRecord7(value) ? value : void 0;
|
|
2429
2923
|
}
|
|
2430
2924
|
function parseJsonDocument(content) {
|
|
2431
2925
|
return JSON.parse(content);
|
|
2432
2926
|
}
|
|
2433
2927
|
function looksLikeOpenInferenceSpan(value) {
|
|
2434
|
-
if (!
|
|
2928
|
+
if (!isRecord7(value)) return false;
|
|
2435
2929
|
const attributes = readRecordField(value, "attributes");
|
|
2436
2930
|
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);
|
|
2437
2931
|
}
|
|
@@ -2456,7 +2950,7 @@ function extractOpenInferenceDocument(root) {
|
|
|
2456
2950
|
unsupportedFields
|
|
2457
2951
|
};
|
|
2458
2952
|
}
|
|
2459
|
-
if (!
|
|
2953
|
+
if (!isRecord7(root)) return void 0;
|
|
2460
2954
|
const rootFormat = root.format;
|
|
2461
2955
|
const rootCompatibility = root.compatibility;
|
|
2462
2956
|
const version = typeof root.version === "string" && root.version.trim() !== "" ? root.version : void 0;
|
|
@@ -2596,7 +3090,7 @@ function summarizeAttributeValue(value) {
|
|
|
2596
3090
|
if (Array.isArray(value)) {
|
|
2597
3091
|
return { type: "array", length: value.length };
|
|
2598
3092
|
}
|
|
2599
|
-
if (
|
|
3093
|
+
if (isRecord7(value)) {
|
|
2600
3094
|
return { type: "object", keyCount: Object.keys(value).length };
|
|
2601
3095
|
}
|
|
2602
3096
|
if (value === null) {
|
|
@@ -2683,7 +3177,7 @@ function mapOpenInferenceKind(span, attributes, pathPrefix) {
|
|
|
2683
3177
|
}
|
|
2684
3178
|
}
|
|
2685
3179
|
function mapOpenInferenceStatus(status) {
|
|
2686
|
-
if (!
|
|
3180
|
+
if (!isRecord7(status)) return void 0;
|
|
2687
3181
|
const rawCode = status.code;
|
|
2688
3182
|
if (typeof rawCode !== "string") return void 0;
|
|
2689
3183
|
switch (rawCode.toUpperCase()) {
|
|
@@ -2783,7 +3277,7 @@ function mapOpenInferenceSpan(span, index, version) {
|
|
|
2783
3277
|
warnings.push(...kindWarnings);
|
|
2784
3278
|
const status = mapOpenInferenceStatus(span.status);
|
|
2785
3279
|
const tokenUsage = readOpenInferenceTokenUsage(rawAttributes);
|
|
2786
|
-
const errorMessage =
|
|
3280
|
+
const errorMessage = isRecord7(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
2787
3281
|
const event = {
|
|
2788
3282
|
schemaVersion: "0.2",
|
|
2789
3283
|
eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
|
|
@@ -2929,7 +3423,7 @@ var openInferenceJsonReader = {
|
|
|
2929
3423
|
}
|
|
2930
3424
|
};
|
|
2931
3425
|
function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
|
|
2932
|
-
if (!
|
|
3426
|
+
if (!isRecord7(value)) {
|
|
2933
3427
|
unsupportedFields.push(field);
|
|
2934
3428
|
warnings.push({
|
|
2935
3429
|
code: "otlp_attribute_value_invalid",
|
|
@@ -2951,15 +3445,15 @@ function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
|
|
|
2951
3445
|
if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
|
|
2952
3446
|
return value.doubleValue;
|
|
2953
3447
|
}
|
|
2954
|
-
if (
|
|
3448
|
+
if (isRecord7(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
|
|
2955
3449
|
return value.arrayValue.values.map(
|
|
2956
3450
|
(item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
|
|
2957
3451
|
);
|
|
2958
3452
|
}
|
|
2959
|
-
if (
|
|
3453
|
+
if (isRecord7(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
|
|
2960
3454
|
const out = {};
|
|
2961
3455
|
for (const [index, item] of value.kvlistValue.values.entries()) {
|
|
2962
|
-
if (!
|
|
3456
|
+
if (!isRecord7(item) || typeof item.key !== "string") {
|
|
2963
3457
|
unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
|
|
2964
3458
|
continue;
|
|
2965
3459
|
}
|
|
@@ -3010,7 +3504,7 @@ function parseOtlpAttributes(value, pathPrefix) {
|
|
|
3010
3504
|
}
|
|
3011
3505
|
for (const [index, item] of value.entries()) {
|
|
3012
3506
|
const field = `${pathPrefix}[${index}]`;
|
|
3013
|
-
if (!
|
|
3507
|
+
if (!isRecord7(item) || typeof item.key !== "string") {
|
|
3014
3508
|
unsupportedFields.push(field);
|
|
3015
3509
|
warnings.push({
|
|
3016
3510
|
code: "otlp_attribute_invalid",
|
|
@@ -3033,16 +3527,16 @@ function parseOtlpAttributes(value, pathPrefix) {
|
|
|
3033
3527
|
return { attributes, warnings, unsupportedFields };
|
|
3034
3528
|
}
|
|
3035
3529
|
function looksLikeOtlpSpan(value) {
|
|
3036
|
-
return
|
|
3530
|
+
return isRecord7(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
|
|
3037
3531
|
}
|
|
3038
3532
|
function extractOtlpDocument(root) {
|
|
3039
|
-
if (!
|
|
3533
|
+
if (!isRecord7(root) || !Array.isArray(root.resourceSpans)) return void 0;
|
|
3040
3534
|
const spans = [];
|
|
3041
3535
|
const warnings = [];
|
|
3042
3536
|
const unsupportedFields = [];
|
|
3043
3537
|
for (const [resourceIndex, resourceSpan] of root.resourceSpans.entries()) {
|
|
3044
3538
|
const resourcePath = `resourceSpans[${resourceIndex}]`;
|
|
3045
|
-
if (!
|
|
3539
|
+
if (!isRecord7(resourceSpan)) {
|
|
3046
3540
|
unsupportedFields.push(resourcePath);
|
|
3047
3541
|
continue;
|
|
3048
3542
|
}
|
|
@@ -3065,7 +3559,7 @@ function extractOtlpDocument(root) {
|
|
|
3065
3559
|
}
|
|
3066
3560
|
for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
|
|
3067
3561
|
const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
|
|
3068
|
-
if (!
|
|
3562
|
+
if (!isRecord7(scopeSpan)) {
|
|
3069
3563
|
unsupportedFields.push(scopePath);
|
|
3070
3564
|
continue;
|
|
3071
3565
|
}
|
|
@@ -3132,7 +3626,7 @@ function extractOtlpDocument(root) {
|
|
|
3132
3626
|
};
|
|
3133
3627
|
}
|
|
3134
3628
|
function mapOtlpStatus(status) {
|
|
3135
|
-
if (!
|
|
3629
|
+
if (!isRecord7(status)) return void 0;
|
|
3136
3630
|
const rawCode = status.code;
|
|
3137
3631
|
if (typeof rawCode !== "string") return void 0;
|
|
3138
3632
|
switch (rawCode.toUpperCase()) {
|
|
@@ -3232,7 +3726,7 @@ function mapOtlpEvents(value, pathPrefix) {
|
|
|
3232
3726
|
const events = [];
|
|
3233
3727
|
for (const [index, event] of value.entries()) {
|
|
3234
3728
|
const eventPath = `${pathPrefix}[${index}]`;
|
|
3235
|
-
if (!
|
|
3729
|
+
if (!isRecord7(event)) {
|
|
3236
3730
|
unsupportedFields.push(eventPath);
|
|
3237
3731
|
continue;
|
|
3238
3732
|
}
|
|
@@ -3370,7 +3864,7 @@ function mapOtlpSpan(context) {
|
|
|
3370
3864
|
warnings.push(...kindWarnings);
|
|
3371
3865
|
const status = mapOtlpStatus(span.status);
|
|
3372
3866
|
const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
|
|
3373
|
-
const errorMessage =
|
|
3867
|
+
const errorMessage = isRecord7(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
3374
3868
|
const event = {
|
|
3375
3869
|
schemaVersion: "0.2",
|
|
3376
3870
|
eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
|
|
@@ -3743,6 +4237,9 @@ function safeString(value, maxLength) {
|
|
|
3743
4237
|
function escapeMarkdown(value) {
|
|
3744
4238
|
return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
|
|
3745
4239
|
}
|
|
4240
|
+
function escapeHtml(value) {
|
|
4241
|
+
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
4242
|
+
}
|
|
3746
4243
|
function sortKeysDeep(input) {
|
|
3747
4244
|
if (input === null || typeof input !== "object") return input;
|
|
3748
4245
|
if (Array.isArray(input)) return input.map(sortKeysDeep);
|
|
@@ -3760,14 +4257,15 @@ function stableJson(value, pretty) {
|
|
|
3760
4257
|
function compactAttributes3(attrs, options) {
|
|
3761
4258
|
if (attrs === void 0) return {};
|
|
3762
4259
|
const maxLen = options?.maxLength ?? 500;
|
|
4260
|
+
const redacted = options?.redacted ?? true;
|
|
3763
4261
|
const out = {};
|
|
3764
4262
|
for (const key of Object.keys(attrs).sort()) {
|
|
3765
|
-
if (shouldRedactKey(key)) {
|
|
4263
|
+
if (redacted && shouldRedactKey(key)) {
|
|
3766
4264
|
out[key] = "[REDACTED]";
|
|
3767
4265
|
continue;
|
|
3768
4266
|
}
|
|
3769
4267
|
const v = attrs[key];
|
|
3770
|
-
out[key] = compactValue(v, maxLen);
|
|
4268
|
+
out[key] = compactValue(v, maxLen, redacted);
|
|
3771
4269
|
}
|
|
3772
4270
|
return out;
|
|
3773
4271
|
}
|
|
@@ -3776,15 +4274,15 @@ function compactValue(value, maxLen, redacted) {
|
|
|
3776
4274
|
return typeof value === "string" ? safeString(value, maxLen) : value;
|
|
3777
4275
|
}
|
|
3778
4276
|
if (Array.isArray(value)) {
|
|
3779
|
-
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen));
|
|
4277
|
+
const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen, redacted));
|
|
3780
4278
|
if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
|
|
3781
4279
|
return arr;
|
|
3782
4280
|
}
|
|
3783
4281
|
const o = value;
|
|
3784
4282
|
const inner = {};
|
|
3785
4283
|
for (const k of Object.keys(o)) {
|
|
3786
|
-
if (shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
3787
|
-
else inner[k] = compactValue(o[k], maxLen);
|
|
4284
|
+
if (redacted && shouldRedactKey(k)) inner[k] = "[REDACTED]";
|
|
4285
|
+
else inner[k] = compactValue(o[k], maxLen, redacted);
|
|
3788
4286
|
}
|
|
3789
4287
|
return inner;
|
|
3790
4288
|
}
|
|
@@ -4158,6 +4656,274 @@ function diffRuns(left, right, options) {
|
|
|
4158
4656
|
return { summary, differences };
|
|
4159
4657
|
}
|
|
4160
4658
|
|
|
4659
|
+
// packages/core/src/exporters/types.ts
|
|
4660
|
+
var EXPORT_PAYLOAD_VERSION = "0.1.2";
|
|
4661
|
+
|
|
4662
|
+
// packages/core/src/exporters/redact-export.ts
|
|
4663
|
+
function isRecord8(value) {
|
|
4664
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4665
|
+
}
|
|
4666
|
+
function deepClone(value) {
|
|
4667
|
+
if (value === null || typeof value !== "object") {
|
|
4668
|
+
return value;
|
|
4669
|
+
}
|
|
4670
|
+
if (Array.isArray(value)) {
|
|
4671
|
+
return value.map((item) => deepClone(item));
|
|
4672
|
+
}
|
|
4673
|
+
const out = {};
|
|
4674
|
+
for (const [k, v] of Object.entries(value)) {
|
|
4675
|
+
out[k] = deepClone(v);
|
|
4676
|
+
}
|
|
4677
|
+
return out;
|
|
4678
|
+
}
|
|
4679
|
+
function boundAttributeValues(record, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
4680
|
+
if (depth > 32) {
|
|
4681
|
+
return { truncated: true, reason: "maxDepth" };
|
|
4682
|
+
}
|
|
4683
|
+
const out = {};
|
|
4684
|
+
for (const [key, value] of Object.entries(record)) {
|
|
4685
|
+
out[key] = boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth);
|
|
4686
|
+
}
|
|
4687
|
+
return out;
|
|
4688
|
+
}
|
|
4689
|
+
function boundValue(value, key, maxMetadataValueLength, maxPreviewLength, seen, depth) {
|
|
4690
|
+
if (value === null || typeof value !== "object") {
|
|
4691
|
+
if (typeof value === "string") {
|
|
4692
|
+
return truncateStringForProfile(
|
|
4693
|
+
value,
|
|
4694
|
+
key,
|
|
4695
|
+
maxMetadataValueLength,
|
|
4696
|
+
maxPreviewLength
|
|
4697
|
+
);
|
|
4698
|
+
}
|
|
4699
|
+
return value;
|
|
4700
|
+
}
|
|
4701
|
+
if (seen.has(value)) return "[Circular]";
|
|
4702
|
+
seen.add(value);
|
|
4703
|
+
if (Array.isArray(value)) {
|
|
4704
|
+
return value.slice(0, 50).map(
|
|
4705
|
+
(item, index) => boundValue(
|
|
4706
|
+
item,
|
|
4707
|
+
String(index),
|
|
4708
|
+
maxMetadataValueLength,
|
|
4709
|
+
maxPreviewLength,
|
|
4710
|
+
seen,
|
|
4711
|
+
depth + 1
|
|
4712
|
+
)
|
|
4713
|
+
);
|
|
4714
|
+
}
|
|
4715
|
+
return boundAttributeValues(
|
|
4716
|
+
value,
|
|
4717
|
+
maxMetadataValueLength,
|
|
4718
|
+
maxPreviewLength,
|
|
4719
|
+
seen,
|
|
4720
|
+
depth + 1
|
|
4721
|
+
);
|
|
4722
|
+
}
|
|
4723
|
+
function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPreviewLength) {
|
|
4724
|
+
if (!attrs || Object.keys(attrs).length === 0) {
|
|
4725
|
+
return attrs;
|
|
4726
|
+
}
|
|
4727
|
+
const redacted = redactor.redactRecord(attrs);
|
|
4728
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
4729
|
+
const bounded = boundAttributeValues(
|
|
4730
|
+
redacted,
|
|
4731
|
+
maxMetadataValueLength,
|
|
4732
|
+
maxPreviewLength,
|
|
4733
|
+
seen,
|
|
4734
|
+
0
|
|
4735
|
+
);
|
|
4736
|
+
const err = bounded.error;
|
|
4737
|
+
if (isRecord8(err) && typeof err.message === "string") {
|
|
4738
|
+
bounded.error = {
|
|
4739
|
+
...err,
|
|
4740
|
+
message: truncateStringForProfile(
|
|
4741
|
+
err.message,
|
|
4742
|
+
"message",
|
|
4743
|
+
maxMetadataValueLength,
|
|
4744
|
+
maxPreviewLength
|
|
4745
|
+
),
|
|
4746
|
+
...typeof err.stack === "string" ? {
|
|
4747
|
+
stack: truncateStringForProfile(
|
|
4748
|
+
err.stack,
|
|
4749
|
+
"stack",
|
|
4750
|
+
maxMetadataValueLength,
|
|
4751
|
+
maxPreviewLength
|
|
4752
|
+
)
|
|
4753
|
+
} : {}
|
|
4754
|
+
};
|
|
4755
|
+
}
|
|
4756
|
+
return bounded;
|
|
4757
|
+
}
|
|
4758
|
+
function redactRunTreeForExport(tree, options) {
|
|
4759
|
+
const profile = options?.redactionProfile ?? "local";
|
|
4760
|
+
if (profile === "local") {
|
|
4761
|
+
return deepClone(tree);
|
|
4762
|
+
}
|
|
4763
|
+
const resolved = resolveRedactionProfile(profile);
|
|
4764
|
+
const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
|
|
4765
|
+
2e3,
|
|
4766
|
+
500,
|
|
4767
|
+
resolved
|
|
4768
|
+
);
|
|
4769
|
+
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
4770
|
+
const clone = deepClone(tree);
|
|
4771
|
+
function walk(nodes) {
|
|
4772
|
+
for (const node of nodes) {
|
|
4773
|
+
if (node.event.attributes !== void 0) {
|
|
4774
|
+
node.event.attributes = redactEventAttributes(
|
|
4775
|
+
node.event.attributes,
|
|
4776
|
+
redactor,
|
|
4777
|
+
maxMetadataValueLength,
|
|
4778
|
+
maxPreviewLength
|
|
4779
|
+
);
|
|
4780
|
+
}
|
|
4781
|
+
if (node.children.length > 0) {
|
|
4782
|
+
walk(node.children);
|
|
4783
|
+
}
|
|
4784
|
+
}
|
|
4785
|
+
}
|
|
4786
|
+
walk(clone.children);
|
|
4787
|
+
return clone;
|
|
4788
|
+
}
|
|
4789
|
+
|
|
4790
|
+
// packages/core/src/exporters/html-exporter.ts
|
|
4791
|
+
function renderTreeHtml(nodes, ulClass = "tree") {
|
|
4792
|
+
if (nodes.length === 0) return "";
|
|
4793
|
+
const parts = [`<ul class="${ulClass}">`];
|
|
4794
|
+
for (const n of nodes) {
|
|
4795
|
+
const ev = n.event;
|
|
4796
|
+
const status = ev.status ?? "?";
|
|
4797
|
+
const dur = ev.durationMs !== void 0 && Number.isFinite(ev.durationMs) ? `${ev.durationMs}ms` : "-";
|
|
4798
|
+
parts.push("<li>");
|
|
4799
|
+
parts.push(
|
|
4800
|
+
`<span class="nm">${escapeHtml(ev.name)}</span> <span class="meta">[${escapeHtml(ev.kind)}] ${escapeHtml(status)} (${escapeHtml(dur)})</span>`
|
|
4801
|
+
);
|
|
4802
|
+
if (n.children.length > 0) {
|
|
4803
|
+
parts.push(renderTreeHtml(n.children, "tree nested"));
|
|
4804
|
+
}
|
|
4805
|
+
parts.push("</li>");
|
|
4806
|
+
}
|
|
4807
|
+
parts.push("</ul>");
|
|
4808
|
+
return parts.join("");
|
|
4809
|
+
}
|
|
4810
|
+
function exportHtml(tree, options) {
|
|
4811
|
+
const warnings = [];
|
|
4812
|
+
const includeMetadata = options?.includeMetadata ?? true;
|
|
4813
|
+
const includeAttributes = options?.includeAttributes ?? false;
|
|
4814
|
+
const includeErrors = options?.includeErrors ?? true;
|
|
4815
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
4816
|
+
const redacted = options?.redacted;
|
|
4817
|
+
const titleName = escapeHtml(tree.name ?? tree.runId);
|
|
4818
|
+
const summaryRows = [];
|
|
4819
|
+
summaryRows.push(
|
|
4820
|
+
`<tr><th scope="row">runId</th><td><code>${escapeHtml(tree.runId)}</code></td></tr>`
|
|
4821
|
+
);
|
|
4822
|
+
if (tree.name !== void 0) {
|
|
4823
|
+
summaryRows.push(`<tr><th scope="row">name</th><td>${escapeHtml(tree.name)}</td></tr>`);
|
|
4824
|
+
}
|
|
4825
|
+
summaryRows.push(
|
|
4826
|
+
`<tr><th scope="row">status</th><td>${escapeHtml(String(tree.status ?? "unknown"))}</td></tr>`
|
|
4827
|
+
);
|
|
4828
|
+
summaryRows.push(
|
|
4829
|
+
`<tr><th scope="row">durationMs</th><td>${tree.durationMs !== void 0 ? escapeHtml(String(tree.durationMs)) : "\u2014"}</td></tr>`
|
|
4830
|
+
);
|
|
4831
|
+
summaryRows.push(
|
|
4832
|
+
`<tr><th scope="row">startedAt</th><td>${tree.startedAt !== void 0 ? escapeHtml(String(tree.startedAt)) : "\u2014"}</td></tr>`
|
|
4833
|
+
);
|
|
4834
|
+
summaryRows.push(
|
|
4835
|
+
`<tr><th scope="row">endedAt</th><td>${tree.endedAt !== void 0 ? escapeHtml(String(tree.endedAt)) : "\u2014"}</td></tr>`
|
|
4836
|
+
);
|
|
4837
|
+
summaryRows.push(
|
|
4838
|
+
`<tr><th scope="row">totalEvents</th><td>${escapeHtml(String(tree.metadata.totalEvents))}</td></tr>`
|
|
4839
|
+
);
|
|
4840
|
+
let confidenceHtml = "";
|
|
4841
|
+
if (includeMetadata) {
|
|
4842
|
+
const cb = tree.metadata.confidenceBreakdown;
|
|
4843
|
+
confidenceHtml += "<h3>Confidence breakdown</h3><table><thead><tr><th>bucket</th><th>count</th></tr></thead><tbody>";
|
|
4844
|
+
for (const k of Object.keys(cb).sort()) {
|
|
4845
|
+
const key = k;
|
|
4846
|
+
confidenceHtml += `<tr><td>${escapeHtml(key)}</td><td>${cb[key]}</td></tr>`;
|
|
4847
|
+
}
|
|
4848
|
+
confidenceHtml += "</tbody></table>";
|
|
4849
|
+
confidenceHtml += "<h3>Kind breakdown</h3><table><thead><tr><th>kind</th><th>count</th></tr></thead><tbody>";
|
|
4850
|
+
for (const k of Object.keys(tree.metadata.kinds).sort()) {
|
|
4851
|
+
const key = k;
|
|
4852
|
+
const c = tree.metadata.kinds[key];
|
|
4853
|
+
if (c > 0) confidenceHtml += `<tr><td>${escapeHtml(key)}</td><td>${c}</td></tr>`;
|
|
4854
|
+
}
|
|
4855
|
+
confidenceHtml += "</tbody></table>";
|
|
4856
|
+
}
|
|
4857
|
+
const flat = flattenTree(tree);
|
|
4858
|
+
const errors = flat.filter((n) => n.event.status === "error");
|
|
4859
|
+
let errorsHtml = "";
|
|
4860
|
+
if (includeErrors && errors.length > 0) {
|
|
4861
|
+
errorsHtml += "<h2>Errors</h2><ul>";
|
|
4862
|
+
for (const n of errors) {
|
|
4863
|
+
const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
|
|
4864
|
+
n.event.attributes.error.message,
|
|
4865
|
+
maxLen
|
|
4866
|
+
) : "";
|
|
4867
|
+
errorsHtml += `<li><strong>${escapeHtml(n.event.name)}</strong> (${escapeHtml(n.event.eventId)}): ${escapeHtml(msg || "error")}</li>`;
|
|
4868
|
+
}
|
|
4869
|
+
errorsHtml += "</ul>";
|
|
4870
|
+
}
|
|
4871
|
+
let attrsHtml = "";
|
|
4872
|
+
if (includeAttributes) {
|
|
4873
|
+
attrsHtml += "<h2>Attributes (bounded)</h2>";
|
|
4874
|
+
for (const n of flat) {
|
|
4875
|
+
if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
|
|
4876
|
+
const compact = compactAttributes3(n.event.attributes, {
|
|
4877
|
+
maxLength: maxLen,
|
|
4878
|
+
redacted
|
|
4879
|
+
});
|
|
4880
|
+
attrsHtml += `<h3>${escapeHtml(n.event.name)}</h3><pre class="json">${escapeHtml(stableJson(compact, true))}</pre>`;
|
|
4881
|
+
}
|
|
4882
|
+
warnings.push(
|
|
4883
|
+
"Attributes may still contain sensitive data; review exports before sharing."
|
|
4884
|
+
);
|
|
4885
|
+
}
|
|
4886
|
+
const css = `
|
|
4887
|
+
body{font-family:system-ui,sans-serif;line-height:1.5;margin:1.5rem;max-width:960px;color:#111}
|
|
4888
|
+
h1{font-size:1.35rem}
|
|
4889
|
+
h2{font-size:1.1rem;margin-top:1.5rem}
|
|
4890
|
+
table{border-collapse:collapse;margin:0.75rem 0}
|
|
4891
|
+
th,td{border:1px solid #ccc;padding:0.35rem 0.6rem;text-align:left}
|
|
4892
|
+
th{background:#f5f5f5}
|
|
4893
|
+
pre.json{background:#f8f8f8;padding:0.75rem;overflow:auto;font-size:0.85rem}
|
|
4894
|
+
ul.tree{list-style:none;padding-left:1rem}
|
|
4895
|
+
ul.tree.nested{padding-left:1.25rem;border-left:1px solid #ddd;margin:0.25rem 0}
|
|
4896
|
+
.nm{font-weight:600}
|
|
4897
|
+
.meta{color:#555;font-size:0.9rem}
|
|
4898
|
+
footer{margin-top:2rem;font-size:0.85rem;color:#555}
|
|
4899
|
+
`.trim();
|
|
4900
|
+
const html = `<!doctype html>
|
|
4901
|
+
<html lang="en">
|
|
4902
|
+
<head>
|
|
4903
|
+
<meta charset="utf-8"/>
|
|
4904
|
+
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
|
4905
|
+
<title>${titleName}</title>
|
|
4906
|
+
<style>${css}</style>
|
|
4907
|
+
</head>
|
|
4908
|
+
<body>
|
|
4909
|
+
<header><h1>AgentInspect Run: ${titleName}</h1></header>
|
|
4910
|
+
<p class="note">Generated locally by AgentInspect.</p>
|
|
4911
|
+
${includeMetadata ? `<section class="summary"><h2>Summary</h2><table>${summaryRows.join("")}</table>${confidenceHtml}</section>` : ""}
|
|
4912
|
+
<section class="tree"><h2>Execution tree</h2>${tree.children.length > 0 ? renderTreeHtml(tree.children) : "<p>No steps recorded.</p>"}</section>
|
|
4913
|
+
${errorsHtml}
|
|
4914
|
+
${attrsHtml}
|
|
4915
|
+
<footer>Generated locally by AgentInspect. Review for sensitive data before sharing.</footer>
|
|
4916
|
+
</body>
|
|
4917
|
+
</html>`;
|
|
4918
|
+
return {
|
|
4919
|
+
format: "html",
|
|
4920
|
+
content: html,
|
|
4921
|
+
contentType: "text/html",
|
|
4922
|
+
fileExtension: ".html",
|
|
4923
|
+
warnings
|
|
4924
|
+
};
|
|
4925
|
+
}
|
|
4926
|
+
|
|
4161
4927
|
// packages/core/src/exporters/markdown-exporter.ts
|
|
4162
4928
|
function renderTreeAscii(nodes, indent = "") {
|
|
4163
4929
|
const lines = [];
|
|
@@ -4183,6 +4949,7 @@ function exportMarkdown(tree, options) {
|
|
|
4183
4949
|
const includeAttributes = options?.includeAttributes ?? false;
|
|
4184
4950
|
const includeErrors = options?.includeErrors ?? true;
|
|
4185
4951
|
const maxLen = options?.maxAttributeLength ?? 500;
|
|
4952
|
+
const redacted = options?.redacted ?? true;
|
|
4186
4953
|
const titleName = tree.name ?? tree.runId;
|
|
4187
4954
|
const lines = [];
|
|
4188
4955
|
lines.push(`# AgentInspect Run: ${escapeMarkdown(titleName)}`);
|
|
@@ -4258,7 +5025,9 @@ function exportMarkdown(tree, options) {
|
|
|
4258
5025
|
for (const n of flat) {
|
|
4259
5026
|
if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
|
|
4260
5027
|
const compact = compactAttributes3(n.event.attributes, {
|
|
4261
|
-
maxLength: maxLen
|
|
5028
|
+
maxLength: maxLen,
|
|
5029
|
+
redacted
|
|
5030
|
+
});
|
|
4262
5031
|
lines.push(`### ${escapeMarkdown(n.event.name)}`);
|
|
4263
5032
|
lines.push("");
|
|
4264
5033
|
lines.push("```json");
|
|
@@ -4278,6 +5047,295 @@ function exportMarkdown(tree, options) {
|
|
|
4278
5047
|
warnings
|
|
4279
5048
|
};
|
|
4280
5049
|
}
|
|
5050
|
+
function hexFrom(seed, byteLen) {
|
|
5051
|
+
return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
5052
|
+
}
|
|
5053
|
+
function mapInspectKindToOI(kind, warnings) {
|
|
5054
|
+
switch (kind) {
|
|
5055
|
+
case "LLM":
|
|
5056
|
+
return { openInferenceKind: "LLM" };
|
|
5057
|
+
case "TOOL":
|
|
5058
|
+
return { openInferenceKind: "TOOL" };
|
|
5059
|
+
case "CHAIN":
|
|
5060
|
+
return { openInferenceKind: "CHAIN" };
|
|
5061
|
+
case "RETRIEVER":
|
|
5062
|
+
return { openInferenceKind: "RETRIEVER" };
|
|
5063
|
+
case "AGENT":
|
|
5064
|
+
return { openInferenceKind: "AGENT" };
|
|
5065
|
+
case "DECISION":
|
|
5066
|
+
warnings.push(
|
|
5067
|
+
`Ambiguous kind DECISION mapped to CHAIN for span compatibility (${EXPORT_PAYLOAD_VERSION}).`
|
|
5068
|
+
);
|
|
5069
|
+
return { openInferenceKind: "CHAIN" };
|
|
5070
|
+
case "RESULT":
|
|
5071
|
+
warnings.push(
|
|
5072
|
+
`Ambiguous kind RESULT mapped to UNKNOWN for span compatibility (${EXPORT_PAYLOAD_VERSION}).`
|
|
5073
|
+
);
|
|
5074
|
+
return { openInferenceKind: "UNKNOWN" };
|
|
5075
|
+
case "ERROR":
|
|
5076
|
+
warnings.push(`ERROR kind mapped to CHAIN for span compatibility.`);
|
|
5077
|
+
return { openInferenceKind: "CHAIN" };
|
|
5078
|
+
case "LOG":
|
|
5079
|
+
case "LOGIC":
|
|
5080
|
+
case "RUN":
|
|
5081
|
+
warnings.push(`${kind} mapped to CHAIN for span compatibility.`);
|
|
5082
|
+
return { openInferenceKind: "CHAIN" };
|
|
5083
|
+
default:
|
|
5084
|
+
warnings.push(`Unhandled InspectKind ${kind} mapped to UNKNOWN.`);
|
|
5085
|
+
return { openInferenceKind: "UNKNOWN" };
|
|
5086
|
+
}
|
|
5087
|
+
}
|
|
5088
|
+
function exportOpenInference(tree, options) {
|
|
5089
|
+
const warnings = [
|
|
5090
|
+
"OpenInference-compatible JSON export is experimental until verified against specific backends.",
|
|
5091
|
+
"This file was generated locally and not sent anywhere."
|
|
5092
|
+
];
|
|
5093
|
+
const traceId = hexFrom(`trace:${tree.runId}`, 16);
|
|
5094
|
+
const includeAttributes = options?.includeAttributes ?? false;
|
|
5095
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
5096
|
+
const pretty = options?.pretty ?? true;
|
|
5097
|
+
const spans = [];
|
|
5098
|
+
for (const n of flattenTree(tree)) {
|
|
5099
|
+
const ev = n.event;
|
|
5100
|
+
const spanId = hexFrom(`${tree.runId}:${ev.eventId}`, 8);
|
|
5101
|
+
const parentSpanHex = ev.parentId ? hexFrom(`${tree.runId}:${ev.parentId}`, 8) : void 0;
|
|
5102
|
+
const startNs = Math.round(ev.timestamp * 1e6);
|
|
5103
|
+
let endNs;
|
|
5104
|
+
if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
|
|
5105
|
+
endNs = startNs + Math.round(ev.durationMs * 1e6);
|
|
5106
|
+
}
|
|
5107
|
+
const { openInferenceKind } = mapInspectKindToOI(ev.kind, warnings);
|
|
5108
|
+
const attrs = {
|
|
5109
|
+
"openinference.span.kind": openInferenceKind,
|
|
5110
|
+
"agent_inspect.kind": ev.kind,
|
|
5111
|
+
"agent_inspect.confidence": ev.confidence,
|
|
5112
|
+
"agent_inspect.source.type": ev.source.type,
|
|
5113
|
+
"agent_inspect.run_id": tree.runId,
|
|
5114
|
+
"agent_inspect.event_id": ev.eventId,
|
|
5115
|
+
"agent_inspect.status": ev.status ?? "unset"
|
|
5116
|
+
};
|
|
5117
|
+
if (ev.durationMs !== void 0) {
|
|
5118
|
+
attrs["agent_inspect.duration_ms"] = ev.durationMs;
|
|
5119
|
+
}
|
|
5120
|
+
const meta = ev.attributes;
|
|
5121
|
+
if (meta?.model !== void 0 && typeof meta.model === "string") {
|
|
5122
|
+
attrs["llm.model_name"] = meta.model;
|
|
5123
|
+
}
|
|
5124
|
+
const tokens = meta?.tokens;
|
|
5125
|
+
if (tokens && typeof tokens === "object" && tokens !== null) {
|
|
5126
|
+
const inp = tokens.input;
|
|
5127
|
+
const outp = tokens.output;
|
|
5128
|
+
if (typeof inp === "number") attrs["llm.token_count.prompt"] = inp;
|
|
5129
|
+
if (typeof outp === "number") attrs["llm.token_count.completion"] = outp;
|
|
5130
|
+
}
|
|
5131
|
+
if (includeAttributes && meta && typeof meta === "object") {
|
|
5132
|
+
for (const [k, v] of Object.entries(meta)) {
|
|
5133
|
+
if (k === "tokens" || k === "model") continue;
|
|
5134
|
+
if (v !== void 0 && v !== null && typeof v !== "object") {
|
|
5135
|
+
attrs[`agent_inspect.preview.${k}`] = typeof v === "string" ? v.slice(0, maxLen) : v;
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5138
|
+
}
|
|
5139
|
+
let status;
|
|
5140
|
+
if (ev.status === "error") {
|
|
5141
|
+
const msg = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error") : "error";
|
|
5142
|
+
status = { code: "ERROR", message: msg.slice(0, maxLen) };
|
|
5143
|
+
} else if (ev.status === "ok") {
|
|
5144
|
+
status = { code: "OK" };
|
|
5145
|
+
} else {
|
|
5146
|
+
status = { code: "UNSET" };
|
|
5147
|
+
}
|
|
5148
|
+
spans.push({
|
|
5149
|
+
trace_id: traceId,
|
|
5150
|
+
span_id: spanId,
|
|
5151
|
+
parent_span_id: parentSpanHex,
|
|
5152
|
+
name: ev.name,
|
|
5153
|
+
start_time_unix_nano: startNs,
|
|
5154
|
+
end_time_unix_nano: endNs,
|
|
5155
|
+
attributes: attrs,
|
|
5156
|
+
status
|
|
5157
|
+
});
|
|
5158
|
+
}
|
|
5159
|
+
const payload = {
|
|
5160
|
+
exporter: "agent-inspect",
|
|
5161
|
+
format: "openinference",
|
|
5162
|
+
compatibility: "openinference-compatible",
|
|
5163
|
+
version: EXPORT_PAYLOAD_VERSION,
|
|
5164
|
+
trace_id: traceId,
|
|
5165
|
+
spans,
|
|
5166
|
+
warnings
|
|
5167
|
+
};
|
|
5168
|
+
return {
|
|
5169
|
+
format: "openinference",
|
|
5170
|
+
content: JSON.stringify(payload, null, pretty ? 2 : void 0),
|
|
5171
|
+
contentType: "application/json",
|
|
5172
|
+
fileExtension: ".openinference.json",
|
|
5173
|
+
warnings
|
|
5174
|
+
};
|
|
5175
|
+
}
|
|
5176
|
+
function hexFrom2(seed, byteLen) {
|
|
5177
|
+
return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
|
|
5178
|
+
}
|
|
5179
|
+
function stringAttr(key, value) {
|
|
5180
|
+
return { key, value: { stringValue: value } };
|
|
5181
|
+
}
|
|
5182
|
+
function intAttr(key, value) {
|
|
5183
|
+
return { key, value: { intValue: String(value) } };
|
|
5184
|
+
}
|
|
5185
|
+
function genAiOperationName(kind) {
|
|
5186
|
+
switch (kind) {
|
|
5187
|
+
case "LLM":
|
|
5188
|
+
return "generate_content";
|
|
5189
|
+
case "TOOL":
|
|
5190
|
+
return "execute_tool";
|
|
5191
|
+
case "AGENT":
|
|
5192
|
+
return "invoke_agent";
|
|
5193
|
+
default:
|
|
5194
|
+
return void 0;
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
function exportOtlpJson(tree, options) {
|
|
5198
|
+
const warnings = [
|
|
5199
|
+
"OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
|
|
5200
|
+
"Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload."
|
|
5201
|
+
];
|
|
5202
|
+
const traceId = hexFrom2(`trace:${tree.runId}`, 16);
|
|
5203
|
+
const includeAttributes = options?.includeAttributes ?? false;
|
|
5204
|
+
const maxLen = options?.maxAttributeLength ?? 500;
|
|
5205
|
+
const pretty = options?.pretty ?? true;
|
|
5206
|
+
const flat = flattenTree(tree);
|
|
5207
|
+
const spans = [];
|
|
5208
|
+
for (const n of flat) {
|
|
5209
|
+
const ev = n.event;
|
|
5210
|
+
const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
|
|
5211
|
+
const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
|
|
5212
|
+
const startNs = String(Math.round(ev.timestamp * 1e6));
|
|
5213
|
+
let endNs;
|
|
5214
|
+
if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
|
|
5215
|
+
endNs = String(Math.round(ev.timestamp * 1e6 + ev.durationMs * 1e6));
|
|
5216
|
+
}
|
|
5217
|
+
const attrs = [
|
|
5218
|
+
stringAttr("agent_inspect.kind", ev.kind),
|
|
5219
|
+
stringAttr("agent_inspect.confidence", ev.confidence),
|
|
5220
|
+
stringAttr("agent_inspect.source.type", ev.source.type),
|
|
5221
|
+
stringAttr("agent_inspect.run_id", tree.runId),
|
|
5222
|
+
stringAttr("agent_inspect.event_id", ev.eventId),
|
|
5223
|
+
stringAttr("agent_inspect.status", ev.status ?? "unset")
|
|
5224
|
+
];
|
|
5225
|
+
if (ev.durationMs !== void 0) {
|
|
5226
|
+
attrs.push(intAttr("agent_inspect.duration_ms", ev.durationMs));
|
|
5227
|
+
}
|
|
5228
|
+
const op = genAiOperationName(ev.kind);
|
|
5229
|
+
if (op !== void 0) {
|
|
5230
|
+
attrs.push(stringAttr("gen_ai.operation.name", op));
|
|
5231
|
+
}
|
|
5232
|
+
const meta = ev.attributes;
|
|
5233
|
+
if (meta?.model !== void 0 && typeof meta.model === "string") {
|
|
5234
|
+
attrs.push(stringAttr("gen_ai.request.model", meta.model.slice(0, maxLen)));
|
|
5235
|
+
}
|
|
5236
|
+
const tokens = meta?.tokens;
|
|
5237
|
+
if (tokens && typeof tokens === "object" && tokens !== null) {
|
|
5238
|
+
const inp = tokens.input;
|
|
5239
|
+
const outp = tokens.output;
|
|
5240
|
+
if (typeof inp === "number") attrs.push(intAttr("gen_ai.usage.input_tokens", inp));
|
|
5241
|
+
if (typeof outp === "number") attrs.push(intAttr("gen_ai.usage.output_tokens", outp));
|
|
5242
|
+
}
|
|
5243
|
+
if (includeAttributes && meta && typeof meta === "object") {
|
|
5244
|
+
for (const [k, v] of Object.entries(meta)) {
|
|
5245
|
+
if (k === "tokens" || k === "model") continue;
|
|
5246
|
+
if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
|
|
5247
|
+
attrs.push(
|
|
5248
|
+
stringAttr(
|
|
5249
|
+
`agent_inspect.preview.${k}`,
|
|
5250
|
+
typeof v === "string" ? v.slice(0, maxLen) : String(v)
|
|
5251
|
+
)
|
|
5252
|
+
);
|
|
5253
|
+
}
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
5256
|
+
let statusCode = "STATUS_CODE_UNSET";
|
|
5257
|
+
let statusMessage;
|
|
5258
|
+
if (ev.status === "error") {
|
|
5259
|
+
statusCode = "STATUS_CODE_ERROR";
|
|
5260
|
+
statusMessage = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error").slice(0, maxLen) : "error";
|
|
5261
|
+
} else if (ev.status === "ok") {
|
|
5262
|
+
statusCode = "STATUS_CODE_OK";
|
|
5263
|
+
}
|
|
5264
|
+
const spanJson = {
|
|
5265
|
+
traceId,
|
|
5266
|
+
spanId,
|
|
5267
|
+
name: ev.name,
|
|
5268
|
+
kind: "SPAN_KIND_INTERNAL",
|
|
5269
|
+
startTimeUnixNano: startNs,
|
|
5270
|
+
attributes: attrs,
|
|
5271
|
+
status: {
|
|
5272
|
+
code: statusCode,
|
|
5273
|
+
...statusMessage !== void 0 ? { message: statusMessage } : {}
|
|
5274
|
+
}
|
|
5275
|
+
};
|
|
5276
|
+
if (parentSpanId !== void 0) {
|
|
5277
|
+
spanJson.parentSpanId = parentSpanId;
|
|
5278
|
+
}
|
|
5279
|
+
if (endNs !== void 0) {
|
|
5280
|
+
spanJson.endTimeUnixNano = endNs;
|
|
5281
|
+
}
|
|
5282
|
+
spans.push(spanJson);
|
|
5283
|
+
}
|
|
5284
|
+
const payload = {
|
|
5285
|
+
resourceSpans: [
|
|
5286
|
+
{
|
|
5287
|
+
resource: {
|
|
5288
|
+
attributes: [stringAttr("service.name", "agent-inspect")]
|
|
5289
|
+
},
|
|
5290
|
+
scopeSpans: [
|
|
5291
|
+
{
|
|
5292
|
+
scope: { name: "agent-inspect" },
|
|
5293
|
+
spans
|
|
5294
|
+
}
|
|
5295
|
+
]
|
|
5296
|
+
}
|
|
5297
|
+
]
|
|
5298
|
+
};
|
|
5299
|
+
return {
|
|
5300
|
+
format: "otlp-json",
|
|
5301
|
+
content: JSON.stringify(payload, null, pretty ? 2 : void 0),
|
|
5302
|
+
contentType: "application/json",
|
|
5303
|
+
fileExtension: ".otlp.json",
|
|
5304
|
+
warnings
|
|
5305
|
+
};
|
|
5306
|
+
}
|
|
5307
|
+
|
|
5308
|
+
// packages/core/src/exporters/index.ts
|
|
5309
|
+
function mergeExportDefaults(options) {
|
|
5310
|
+
return {
|
|
5311
|
+
format: options.format,
|
|
5312
|
+
includeMetadata: options.includeMetadata ?? true,
|
|
5313
|
+
includeAttributes: options.includeAttributes ?? false,
|
|
5314
|
+
includeErrors: options.includeErrors ?? true,
|
|
5315
|
+
pretty: options.pretty ?? true,
|
|
5316
|
+
redacted: options.redacted,
|
|
5317
|
+
maxAttributeLength: options.maxAttributeLength ?? 500,
|
|
5318
|
+
redactionProfile: options.redactionProfile ?? "local"
|
|
5319
|
+
};
|
|
5320
|
+
}
|
|
5321
|
+
function exportRunTree(tree, options) {
|
|
5322
|
+
const opts = mergeExportDefaults(options);
|
|
5323
|
+
const exportTree = opts.redactionProfile === "local" ? tree : redactRunTreeForExport(tree, { redactionProfile: opts.redactionProfile });
|
|
5324
|
+
switch (opts.format) {
|
|
5325
|
+
case "markdown":
|
|
5326
|
+
return exportMarkdown(exportTree, opts);
|
|
5327
|
+
case "html":
|
|
5328
|
+
return exportHtml(exportTree, opts);
|
|
5329
|
+
case "openinference":
|
|
5330
|
+
return exportOpenInference(exportTree, opts);
|
|
5331
|
+
case "otlp-json":
|
|
5332
|
+
return exportOtlpJson(exportTree, opts);
|
|
5333
|
+
default: {
|
|
5334
|
+
const _x = opts.format;
|
|
5335
|
+
throw new Error(`Unsupported export format: ${String(_x)}`);
|
|
5336
|
+
}
|
|
5337
|
+
}
|
|
5338
|
+
}
|
|
4281
5339
|
|
|
4282
5340
|
// packages/mcp-server/src/tools.ts
|
|
4283
5341
|
var READ_ONLY_TOOLS = [
|
|
@@ -4351,6 +5409,42 @@ var READ_ONLY_TOOLS = [
|
|
|
4351
5409
|
properties: { runId: { type: "string" } },
|
|
4352
5410
|
required: ["runId"]
|
|
4353
5411
|
}
|
|
5412
|
+
},
|
|
5413
|
+
{
|
|
5414
|
+
name: "summarize_failed_run",
|
|
5415
|
+
description: "Summarize a failed run with step errors and correlation metadata.",
|
|
5416
|
+
inputSchema: {
|
|
5417
|
+
type: "object",
|
|
5418
|
+
properties: { runId: { type: "string" } },
|
|
5419
|
+
required: ["runId"]
|
|
5420
|
+
}
|
|
5421
|
+
},
|
|
5422
|
+
{
|
|
5423
|
+
name: "retrieve_decision_notes",
|
|
5424
|
+
description: "List decision steps and decision metadata for one run.",
|
|
5425
|
+
inputSchema: {
|
|
5426
|
+
type: "object",
|
|
5427
|
+
properties: { runId: { type: "string" } },
|
|
5428
|
+
required: ["runId"]
|
|
5429
|
+
}
|
|
5430
|
+
},
|
|
5431
|
+
{
|
|
5432
|
+
name: "find_failed_observation",
|
|
5433
|
+
description: "Find failed observed outcomes in one run.",
|
|
5434
|
+
inputSchema: {
|
|
5435
|
+
type: "object",
|
|
5436
|
+
properties: { runId: { type: "string" } },
|
|
5437
|
+
required: ["runId"]
|
|
5438
|
+
}
|
|
5439
|
+
},
|
|
5440
|
+
{
|
|
5441
|
+
name: "create_share_safe_bundle",
|
|
5442
|
+
description: "Create an in-memory share-safe bundle manifest and redacted exports.",
|
|
5443
|
+
inputSchema: {
|
|
5444
|
+
type: "object",
|
|
5445
|
+
properties: { runId: { type: "string" } },
|
|
5446
|
+
required: ["runId"]
|
|
5447
|
+
}
|
|
4354
5448
|
}
|
|
4355
5449
|
];
|
|
4356
5450
|
function textResult(payload) {
|
|
@@ -4385,6 +5479,19 @@ async function openRunTrace(context, runId) {
|
|
|
4385
5479
|
function legacyTraceEvents(events) {
|
|
4386
5480
|
return persistedInspectEventsToTraceEvents(events);
|
|
4387
5481
|
}
|
|
5482
|
+
function redactionProfileForExport(context) {
|
|
5483
|
+
return context.redactionProfile === "local" ? "share" : context.redactionProfile;
|
|
5484
|
+
}
|
|
5485
|
+
function decisionNotes(events) {
|
|
5486
|
+
return events.filter(
|
|
5487
|
+
(event) => event.kind === "DECISION" || typeof event.attributes?.decisionId === "string" && event.attributes.decisionId !== ""
|
|
5488
|
+
).slice(0, 50).map((event) => ({
|
|
5489
|
+
name: event.name,
|
|
5490
|
+
kind: event.kind,
|
|
5491
|
+
status: event.status,
|
|
5492
|
+
decisionId: typeof event.attributes?.decisionId === "string" ? event.attributes.decisionId : void 0
|
|
5493
|
+
}));
|
|
5494
|
+
}
|
|
4388
5495
|
async function callReadOnlyTool(context, name, args = {}) {
|
|
4389
5496
|
switch (name) {
|
|
4390
5497
|
case "list_traces": {
|
|
@@ -4485,10 +5592,73 @@ async function callReadOnlyTool(context, name, args = {}) {
|
|
|
4485
5592
|
const { read } = await openRunTrace(context, runId);
|
|
4486
5593
|
const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
|
|
4487
5594
|
if (!run) return errorResult2(`Run tree not found: ${runId}`);
|
|
5595
|
+
const profile = redactionProfileForExport(context);
|
|
4488
5596
|
const markdown = exportMarkdown(run, {
|
|
4489
|
-
|
|
5597
|
+
redacted: true});
|
|
5598
|
+
return textResult({ runId, profile, markdown: markdown.content });
|
|
5599
|
+
}
|
|
5600
|
+
case "summarize_failed_run": {
|
|
5601
|
+
const runId = String(args.runId ?? "");
|
|
5602
|
+
const { read } = await openRunTrace(context, runId);
|
|
5603
|
+
const traceEvents = legacyTraceEvents(read.events);
|
|
5604
|
+
const summary = buildRunWhatSummary(traceEvents);
|
|
5605
|
+
return textResult({
|
|
5606
|
+
runId,
|
|
5607
|
+
status: summary.status,
|
|
5608
|
+
summary: renderRunWhat(summary),
|
|
5609
|
+
failedStepNames: summary.failedStepNames,
|
|
5610
|
+
correlation: summary.correlation ?? null
|
|
5611
|
+
});
|
|
5612
|
+
}
|
|
5613
|
+
case "retrieve_decision_notes": {
|
|
5614
|
+
const runId = String(args.runId ?? "");
|
|
5615
|
+
const { read } = await openRunTrace(context, runId);
|
|
5616
|
+
const notes = decisionNotes(read.events);
|
|
5617
|
+
return textResult({ runId, decisions: notes, count: notes.length });
|
|
5618
|
+
}
|
|
5619
|
+
case "find_failed_observation": {
|
|
5620
|
+
const runId = String(args.runId ?? "");
|
|
5621
|
+
const { read } = await openRunTrace(context, runId);
|
|
5622
|
+
const outcomes = extractOutcomesFromTraceEvents(legacyTraceEvents(read.events));
|
|
5623
|
+
const failed = outcomes.filter((outcome) => outcome.status === "failed");
|
|
5624
|
+
return textResult({
|
|
5625
|
+
runId,
|
|
5626
|
+
failed,
|
|
5627
|
+
count: failed.length
|
|
5628
|
+
});
|
|
5629
|
+
}
|
|
5630
|
+
case "create_share_safe_bundle": {
|
|
5631
|
+
const runId = String(args.runId ?? "");
|
|
5632
|
+
const { read } = await openRunTrace(context, runId);
|
|
5633
|
+
const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
|
|
5634
|
+
if (!run) return errorResult2(`Run tree not found: ${runId}`);
|
|
5635
|
+
const profile = redactionProfileForExport(context);
|
|
5636
|
+
const markdown = exportMarkdown(run, {
|
|
5637
|
+
redacted: true});
|
|
5638
|
+
const tree = exportRunTree(run, {
|
|
5639
|
+
format: "openinference",
|
|
5640
|
+
redacted: true,
|
|
5641
|
+
redactionProfile: profile
|
|
5642
|
+
});
|
|
5643
|
+
const metadata = buildBundleMetadata({
|
|
5644
|
+
agentInspectVersion: "mcp-server",
|
|
5645
|
+
profile,
|
|
5646
|
+
resolve: { runIds: [runId] },
|
|
5647
|
+
checks: {
|
|
5648
|
+
aggregateStatus: "SAFE",
|
|
5649
|
+
runs: [{ runId, status: "SAFE", errors: 0, warnings: 0, findings: 0 }]
|
|
5650
|
+
},
|
|
5651
|
+
files: ["report.md", "tree.json"]
|
|
5652
|
+
});
|
|
5653
|
+
return textResult({
|
|
5654
|
+
runId,
|
|
5655
|
+
profile,
|
|
5656
|
+
metadata,
|
|
5657
|
+
files: {
|
|
5658
|
+
"report.md": markdown.content,
|
|
5659
|
+
"tree.json": tree.content
|
|
5660
|
+
}
|
|
4490
5661
|
});
|
|
4491
|
-
return textResult({ runId, profile: context.redactionProfile, markdown: markdown.content });
|
|
4492
5662
|
}
|
|
4493
5663
|
default:
|
|
4494
5664
|
return errorResult2(`Unknown tool: ${name}`);
|