agent-inspect 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/README.md +14 -4
- package/docs/ADAPTER-CONFORMANCE.md +35 -0
- package/docs/ADAPTERS.md +79 -1
- package/docs/API.md +184 -10
- package/docs/ARCHITECTURE.md +4 -0
- package/docs/CLI.md +41 -12
- package/docs/KNOWN-ISSUES.md +11 -1
- package/docs/LIMITATIONS.md +19 -2
- package/docs/SCHEMA.md +17 -7
- package/package.json +23 -2
- package/packages/cli/dist/index.cjs +2449 -157
- package/packages/cli/dist/index.cjs.map +1 -1
- package/packages/cli/dist/index.mjs +2450 -158
- package/packages/cli/dist/index.mjs.map +1 -1
- package/packages/core/dist/advanced.cjs +839 -18
- package/packages/core/dist/advanced.cjs.map +1 -1
- package/packages/core/dist/advanced.d.cts +98 -3
- package/packages/core/dist/advanced.d.ts +98 -3
- package/packages/core/dist/advanced.mjs +7 -4
- package/packages/core/dist/chunk-57S5D6HR.mjs +655 -0
- package/packages/core/dist/chunk-57S5D6HR.mjs.map +1 -0
- package/packages/core/dist/chunk-6QSLZCBJ.mjs +743 -0
- package/packages/core/dist/chunk-6QSLZCBJ.mjs.map +1 -0
- package/packages/core/dist/chunk-6SZPTECC.mjs +342 -0
- package/packages/core/dist/chunk-6SZPTECC.mjs.map +1 -0
- package/packages/core/dist/{chunk-QX3ZMPUF.mjs → chunk-74XZ6N7Q.mjs} +13 -55
- package/packages/core/dist/chunk-74XZ6N7Q.mjs.map +1 -0
- package/packages/core/dist/{chunk-QPAU2TPA.mjs → chunk-HR7G62IE.mjs} +4 -4
- package/packages/core/dist/{chunk-QPAU2TPA.mjs.map → chunk-HR7G62IE.mjs.map} +1 -1
- package/packages/core/dist/chunk-S4YWKV4G.mjs +48 -0
- package/packages/core/dist/chunk-S4YWKV4G.mjs.map +1 -0
- package/packages/core/dist/chunk-TFLPUZ56.mjs +1571 -0
- package/packages/core/dist/chunk-TFLPUZ56.mjs.map +1 -0
- package/packages/core/dist/{chunk-Q6EPNB3V.mjs → chunk-TZISEVLQ.mjs} +34 -183
- package/packages/core/dist/chunk-TZISEVLQ.mjs.map +1 -0
- package/packages/core/dist/chunk-U2BGPESY.mjs +150 -0
- package/packages/core/dist/chunk-U2BGPESY.mjs.map +1 -0
- package/packages/core/dist/chunk-VTIB5MDK.mjs +304 -0
- package/packages/core/dist/chunk-VTIB5MDK.mjs.map +1 -0
- package/packages/core/dist/{chunk-5EMIZZXD.mjs → chunk-Y56BPA3B.mjs} +87 -4
- package/packages/core/dist/chunk-Y56BPA3B.mjs.map +1 -0
- package/packages/core/dist/diff.d.cts +3 -2
- package/packages/core/dist/diff.d.ts +3 -2
- package/packages/core/dist/exporters.cjs.map +1 -1
- package/packages/core/dist/exporters.d.cts +3 -2
- package/packages/core/dist/exporters.d.ts +3 -2
- package/packages/core/dist/exporters.mjs +2 -2
- package/packages/core/dist/index.cjs +2975 -229
- package/packages/core/dist/index.cjs.map +1 -1
- package/packages/core/dist/index.d.cts +27 -6
- package/packages/core/dist/index.d.ts +27 -6
- package/packages/core/dist/index.mjs +113 -60
- package/packages/core/dist/index.mjs.map +1 -1
- package/packages/core/dist/{log-config-BzGmDYum.d.cts → inspect-event-Des4JDHo.d.cts} +1 -31
- package/packages/core/dist/{log-config-BzGmDYum.d.ts → inspect-event-Des4JDHo.d.ts} +1 -31
- package/packages/core/dist/log-config-BnH8Ykcb.d.cts +33 -0
- package/packages/core/dist/log-config-C1GcJPIM.d.ts +33 -0
- package/packages/core/dist/logs.d.cts +3 -2
- package/packages/core/dist/logs.d.ts +3 -2
- package/packages/core/dist/logs.mjs +3 -3
- package/packages/core/dist/persisted-inspect-event-0kaRADsp.d.cts +56 -0
- package/packages/core/dist/persisted-inspect-event-DiFto0K2.d.ts +56 -0
- package/packages/core/dist/persisted.cjs +38 -40
- package/packages/core/dist/persisted.cjs.map +1 -1
- package/packages/core/dist/persisted.d.cts +6 -55
- package/packages/core/dist/persisted.d.ts +6 -55
- package/packages/core/dist/persisted.mjs +4 -2
- package/packages/core/dist/readers.cjs +2590 -0
- package/packages/core/dist/readers.cjs.map +1 -0
- package/packages/core/dist/readers.d.cts +80 -0
- package/packages/core/dist/readers.d.ts +80 -0
- package/packages/core/dist/readers.mjs +9 -0
- package/packages/core/dist/readers.mjs.map +1 -0
- package/packages/core/dist/{types-CNbheSdk.d.cts → types-DB8jB6Jg.d.cts} +7 -1
- package/packages/core/dist/{types-Bkt7LS01.d.ts → types-tSix7tfv.d.ts} +7 -1
- package/packages/core/dist/writers.cjs +997 -0
- package/packages/core/dist/writers.cjs.map +1 -0
- package/packages/core/dist/writers.d.cts +62 -0
- package/packages/core/dist/writers.d.ts +62 -0
- package/packages/core/dist/writers.mjs +9 -0
- package/packages/core/dist/writers.mjs.map +1 -0
- package/packages/core/dist/chunk-5EMIZZXD.mjs.map +0 -1
- package/packages/core/dist/chunk-Q6EPNB3V.mjs.map +0 -1
- package/packages/core/dist/chunk-QX3ZMPUF.mjs.map +0 -1
- package/packages/core/dist/chunk-XDBND27A.mjs +0 -975
- package/packages/core/dist/chunk-XDBND27A.mjs.map +0 -1
|
@@ -3,7 +3,7 @@ import { realpathSync, createReadStream } from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { Command, Option } from 'commander';
|
|
6
|
-
import { unlink, stat, mkdir, writeFile, appendFile, readdir, readFile, open } from 'fs/promises';
|
|
6
|
+
import { unlink, stat, mkdir, writeFile, appendFile, readdir, readFile, access, open } from 'fs/promises';
|
|
7
7
|
import crypto, { webcrypto } from 'crypto';
|
|
8
8
|
import os from 'os';
|
|
9
9
|
import { AsyncLocalStorage } from 'async_hooks';
|
|
@@ -11,6 +11,9 @@ import { createInterface } from 'readline';
|
|
|
11
11
|
import process2, { stdin, stdout } from 'process';
|
|
12
12
|
import tty from 'tty';
|
|
13
13
|
|
|
14
|
+
// package.json
|
|
15
|
+
var version = "1.7.0";
|
|
16
|
+
|
|
14
17
|
// packages/core/src/types.ts
|
|
15
18
|
var STEP_TYPES = [
|
|
16
19
|
"run",
|
|
@@ -133,6 +136,7 @@ function isPersistedTokenUsage(value) {
|
|
|
133
136
|
if (!isOptionalNonNegativeNumber(value.input)) return false;
|
|
134
137
|
if (!isOptionalNonNegativeNumber(value.output)) return false;
|
|
135
138
|
if (!isOptionalNonNegativeNumber(value.total)) return false;
|
|
139
|
+
if (!isOptionalNonNegativeNumber(value.cached)) return false;
|
|
136
140
|
return true;
|
|
137
141
|
}
|
|
138
142
|
function isPersistedTraceContext(value) {
|
|
@@ -178,8 +182,405 @@ function isPersistedInspectEvent(value) {
|
|
|
178
182
|
return true;
|
|
179
183
|
}
|
|
180
184
|
|
|
181
|
-
// packages/core/src/
|
|
185
|
+
// packages/core/src/correlation-metadata.ts
|
|
186
|
+
var TRACE_CORRELATION_KEYS = [
|
|
187
|
+
"correlationId",
|
|
188
|
+
"requestId",
|
|
189
|
+
"decisionId",
|
|
190
|
+
"groupId"
|
|
191
|
+
];
|
|
192
|
+
function isNonEmptyString2(value) {
|
|
193
|
+
return typeof value === "string" && value.length > 0;
|
|
194
|
+
}
|
|
195
|
+
function extractCorrelationMetadata(record) {
|
|
196
|
+
if (!record) {
|
|
197
|
+
return void 0;
|
|
198
|
+
}
|
|
199
|
+
const out = {};
|
|
200
|
+
let found = false;
|
|
201
|
+
for (const key of TRACE_CORRELATION_KEYS) {
|
|
202
|
+
const value = record[key];
|
|
203
|
+
if (isNonEmptyString2(value)) {
|
|
204
|
+
out[key] = value;
|
|
205
|
+
found = true;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return found ? out : void 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// packages/core/src/persisted/token-usage.ts
|
|
212
|
+
function isRecord3(value) {
|
|
213
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
214
|
+
}
|
|
215
|
+
function nonNegativeFinite(value) {
|
|
216
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
217
|
+
}
|
|
218
|
+
function normalizeTokenUsage(value) {
|
|
219
|
+
if (!isRecord3(value)) return void 0;
|
|
220
|
+
const input3 = nonNegativeFinite(value.input);
|
|
221
|
+
const output2 = nonNegativeFinite(value.output);
|
|
222
|
+
const suppliedTotal = nonNegativeFinite(value.total);
|
|
223
|
+
const cached = nonNegativeFinite(value.cached);
|
|
224
|
+
const derivedTotal = input3 !== void 0 && output2 !== void 0 && Number.isFinite(input3 + output2) ? input3 + output2 : void 0;
|
|
225
|
+
const total = suppliedTotal ?? derivedTotal;
|
|
226
|
+
if (input3 === void 0 && output2 === void 0 && total === void 0 && cached === void 0) {
|
|
227
|
+
return void 0;
|
|
228
|
+
}
|
|
229
|
+
return {
|
|
230
|
+
...input3 !== void 0 ? { input: input3 } : {},
|
|
231
|
+
...output2 !== void 0 ? { output: output2 } : {},
|
|
232
|
+
...total !== void 0 ? { total } : {},
|
|
233
|
+
...cached !== void 0 ? { cached } : {}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// packages/core/src/persisted/from-trace-event.ts
|
|
238
|
+
function sanitizeIdPart(value) {
|
|
239
|
+
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
240
|
+
}
|
|
241
|
+
function nodeIdForEvent(event) {
|
|
242
|
+
switch (event.event) {
|
|
243
|
+
case "run_started":
|
|
244
|
+
case "run_completed":
|
|
245
|
+
return event.runId;
|
|
246
|
+
case "step_started":
|
|
247
|
+
case "step_completed":
|
|
248
|
+
return event.stepId;
|
|
249
|
+
default:
|
|
250
|
+
return "unknown";
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function createPersistedEventId(event, eventIndex) {
|
|
254
|
+
const runId = sanitizeIdPart(event.runId);
|
|
255
|
+
const ev = sanitizeIdPart(event.event);
|
|
256
|
+
const node = sanitizeIdPart(nodeIdForEvent(event));
|
|
257
|
+
return `manual:${runId}:${ev}:${node}:${eventIndex}`;
|
|
258
|
+
}
|
|
259
|
+
function toIsoTimestamp(ms) {
|
|
260
|
+
if (typeof ms !== "number" || !Number.isFinite(ms)) {
|
|
261
|
+
return { iso: (/* @__PURE__ */ new Date(0)).toISOString(), invalidTimestamp: true };
|
|
262
|
+
}
|
|
263
|
+
return { iso: new Date(ms).toISOString(), invalidTimestamp: false };
|
|
264
|
+
}
|
|
265
|
+
function buildSource(options) {
|
|
266
|
+
return {
|
|
267
|
+
type: "manual",
|
|
268
|
+
name: options?.sourceName ?? "trace-event",
|
|
269
|
+
version: options?.sourceVersion ?? "0.1"
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function mapStepTypeToInspectKind(type) {
|
|
273
|
+
switch (type) {
|
|
274
|
+
case "run":
|
|
275
|
+
return "RUN";
|
|
276
|
+
case "llm":
|
|
277
|
+
return "LLM";
|
|
278
|
+
case "tool":
|
|
279
|
+
return "TOOL";
|
|
280
|
+
case "decision":
|
|
281
|
+
return "DECISION";
|
|
282
|
+
case "logic":
|
|
283
|
+
case "state":
|
|
284
|
+
case "custom":
|
|
285
|
+
return "LOGIC";
|
|
286
|
+
default:
|
|
287
|
+
return "LOGIC";
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function mapRunOrStepStatus(status) {
|
|
291
|
+
return status === "success" ? "ok" : "error";
|
|
292
|
+
}
|
|
293
|
+
function mapErrorInfo(error) {
|
|
294
|
+
if (!error?.message) {
|
|
295
|
+
return {};
|
|
296
|
+
}
|
|
297
|
+
const out = {
|
|
298
|
+
persisted: {
|
|
299
|
+
message: error.message,
|
|
300
|
+
name: "Error"
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
if (typeof error.stack === "string" && error.stack.length > 0) {
|
|
304
|
+
out.errorStack = error.stack;
|
|
305
|
+
}
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
function mapTokenUsageFromMetadata(metadata) {
|
|
309
|
+
return normalizeTokenUsage(metadata?.tokens);
|
|
310
|
+
}
|
|
311
|
+
function compactAttributes(entries) {
|
|
312
|
+
const out = {};
|
|
313
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
314
|
+
if (value !== void 0) {
|
|
315
|
+
out[key] = value;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
319
|
+
}
|
|
320
|
+
function traceEventToPersistedInspectEvent(event, options) {
|
|
321
|
+
const eventIndex = options?.eventIndex ?? 0;
|
|
322
|
+
const eventId = createPersistedEventId(event, eventIndex);
|
|
323
|
+
const source = buildSource(options);
|
|
324
|
+
const tsMain = toIsoTimestamp(event.timestamp);
|
|
325
|
+
switch (event.event) {
|
|
326
|
+
case "run_started": {
|
|
327
|
+
const tsStart = toIsoTimestamp(event.startTime);
|
|
328
|
+
const correlation = extractCorrelationMetadata(event.metadata);
|
|
329
|
+
const attributes = compactAttributes({
|
|
330
|
+
legacyEvent: "run_started",
|
|
331
|
+
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
332
|
+
correlationId: correlation?.correlationId,
|
|
333
|
+
requestId: correlation?.requestId,
|
|
334
|
+
decisionId: correlation?.decisionId,
|
|
335
|
+
groupId: correlation?.groupId,
|
|
336
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
337
|
+
});
|
|
338
|
+
return {
|
|
339
|
+
schemaVersion: "0.2",
|
|
340
|
+
eventId,
|
|
341
|
+
runId: event.runId,
|
|
342
|
+
kind: "RUN",
|
|
343
|
+
name: event.name,
|
|
344
|
+
status: "running",
|
|
345
|
+
timestamp: tsMain.iso,
|
|
346
|
+
startedAt: tsStart.iso,
|
|
347
|
+
confidence: "explicit",
|
|
348
|
+
source,
|
|
349
|
+
attributes
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
case "run_completed": {
|
|
353
|
+
const tsEnd = toIsoTimestamp(event.endTime);
|
|
354
|
+
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
355
|
+
const attributes = compactAttributes({
|
|
356
|
+
legacyEvent: "run_completed",
|
|
357
|
+
errorStack,
|
|
358
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
359
|
+
});
|
|
360
|
+
return {
|
|
361
|
+
schemaVersion: "0.2",
|
|
362
|
+
eventId,
|
|
363
|
+
runId: event.runId,
|
|
364
|
+
kind: "RUN",
|
|
365
|
+
name: "run",
|
|
366
|
+
status: mapRunOrStepStatus(event.status),
|
|
367
|
+
timestamp: tsMain.iso,
|
|
368
|
+
endedAt: tsEnd.iso,
|
|
369
|
+
durationMs: event.durationMs,
|
|
370
|
+
confidence: "explicit",
|
|
371
|
+
source,
|
|
372
|
+
attributes,
|
|
373
|
+
error
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
case "step_started": {
|
|
377
|
+
const tsStart = toIsoTimestamp(event.startTime);
|
|
378
|
+
const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
|
|
379
|
+
const attributes = compactAttributes({
|
|
380
|
+
legacyEvent: "step_started",
|
|
381
|
+
stepId: event.stepId,
|
|
382
|
+
stepType: event.type,
|
|
383
|
+
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
384
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
385
|
+
});
|
|
386
|
+
const out = {
|
|
387
|
+
schemaVersion: "0.2",
|
|
388
|
+
eventId,
|
|
389
|
+
runId: event.runId,
|
|
390
|
+
kind: mapStepTypeToInspectKind(event.type),
|
|
391
|
+
name: event.name,
|
|
392
|
+
status: "running",
|
|
393
|
+
timestamp: tsMain.iso,
|
|
394
|
+
startedAt: tsStart.iso,
|
|
395
|
+
confidence: "explicit",
|
|
396
|
+
source,
|
|
397
|
+
attributes
|
|
398
|
+
};
|
|
399
|
+
if (event.parentId !== void 0) {
|
|
400
|
+
out.parentId = event.parentId;
|
|
401
|
+
}
|
|
402
|
+
if (tokenUsage !== void 0) {
|
|
403
|
+
out.tokenUsage = tokenUsage;
|
|
404
|
+
}
|
|
405
|
+
return out;
|
|
406
|
+
}
|
|
407
|
+
case "step_completed": {
|
|
408
|
+
const tsEnd = toIsoTimestamp(event.endTime);
|
|
409
|
+
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
410
|
+
const attributes = compactAttributes({
|
|
411
|
+
legacyEvent: "step_completed",
|
|
412
|
+
stepId: event.stepId,
|
|
413
|
+
errorStack,
|
|
414
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
415
|
+
});
|
|
416
|
+
return {
|
|
417
|
+
schemaVersion: "0.2",
|
|
418
|
+
eventId,
|
|
419
|
+
runId: event.runId,
|
|
420
|
+
kind: "LOGIC",
|
|
421
|
+
name: event.stepId,
|
|
422
|
+
status: mapRunOrStepStatus(event.status),
|
|
423
|
+
timestamp: tsMain.iso,
|
|
424
|
+
endedAt: tsEnd.iso,
|
|
425
|
+
durationMs: event.durationMs,
|
|
426
|
+
confidence: "explicit",
|
|
427
|
+
source,
|
|
428
|
+
attributes,
|
|
429
|
+
error
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
default: {
|
|
433
|
+
const _exhaustive = event;
|
|
434
|
+
throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
function traceEventsToPersistedInspectEvents(events, options) {
|
|
439
|
+
return events.map(
|
|
440
|
+
(event, index) => traceEventToPersistedInspectEvent(event, { ...options, eventIndex: index })
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// packages/core/src/persisted/to-inspect-event.ts
|
|
445
|
+
function compactAttributes2(entries) {
|
|
446
|
+
const out = {};
|
|
447
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
448
|
+
if (value !== void 0) {
|
|
449
|
+
out[key] = value;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
453
|
+
}
|
|
182
454
|
function parseIsoToMs(iso) {
|
|
455
|
+
const parsed = Date.parse(iso);
|
|
456
|
+
if (!Number.isFinite(parsed)) {
|
|
457
|
+
return { ms: 0, invalidTimestamp: true };
|
|
458
|
+
}
|
|
459
|
+
return { ms: parsed, invalidTimestamp: false };
|
|
460
|
+
}
|
|
461
|
+
function mapPersistedSourceToInspect(event) {
|
|
462
|
+
const attrs = event.attributes ?? {};
|
|
463
|
+
const sourceName = event.source.name;
|
|
464
|
+
if (sourceName === "pino") {
|
|
465
|
+
return {
|
|
466
|
+
type: "pino",
|
|
467
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
468
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
if (sourceName === "winston") {
|
|
472
|
+
return {
|
|
473
|
+
type: "winston",
|
|
474
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
475
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
const mapType = (t) => {
|
|
479
|
+
switch (t) {
|
|
480
|
+
case "manual":
|
|
481
|
+
return "manual";
|
|
482
|
+
case "json-log":
|
|
483
|
+
return "json-log";
|
|
484
|
+
case "log4js":
|
|
485
|
+
return "log4js";
|
|
486
|
+
case "adapter":
|
|
487
|
+
case "ai-sdk":
|
|
488
|
+
case "otel":
|
|
489
|
+
return "adapter";
|
|
490
|
+
default:
|
|
491
|
+
return "json-log";
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
return {
|
|
495
|
+
type: mapType(event.source.type),
|
|
496
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
497
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
function buildInspectAttributes(event) {
|
|
501
|
+
const attrs = event.attributes !== void 0 ? { ...event.attributes } : {};
|
|
502
|
+
if (event.inputSummary !== void 0) {
|
|
503
|
+
attrs.inputSummary = event.inputSummary;
|
|
504
|
+
}
|
|
505
|
+
if (event.outputSummary !== void 0) {
|
|
506
|
+
attrs.outputSummary = event.outputSummary;
|
|
507
|
+
}
|
|
508
|
+
if (event.error) {
|
|
509
|
+
if (event.error.name !== void 0) {
|
|
510
|
+
attrs.errorName = event.error.name;
|
|
511
|
+
}
|
|
512
|
+
attrs.errorMessage = event.error.message;
|
|
513
|
+
if (event.error.code !== void 0) {
|
|
514
|
+
attrs.errorCode = event.error.code;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (event.tokenUsage) {
|
|
518
|
+
attrs.tokens = { ...event.tokenUsage };
|
|
519
|
+
}
|
|
520
|
+
if (event.source.type === "ai-sdk" || event.source.type === "otel") {
|
|
521
|
+
attrs.originalSourceType = event.source.type;
|
|
522
|
+
}
|
|
523
|
+
if (event.source.name !== void 0) {
|
|
524
|
+
attrs.sourceName = event.source.name;
|
|
525
|
+
}
|
|
526
|
+
if (event.source.version !== void 0) {
|
|
527
|
+
attrs.sourceVersion = event.source.version;
|
|
528
|
+
}
|
|
529
|
+
return attrs;
|
|
530
|
+
}
|
|
531
|
+
function persistedInspectEventToInspectEvent(event) {
|
|
532
|
+
if (!isPersistedInspectEvent(event)) {
|
|
533
|
+
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
534
|
+
}
|
|
535
|
+
const ts = parseIsoToMs(event.timestamp);
|
|
536
|
+
const attrs = buildInspectAttributes(event);
|
|
537
|
+
if (ts.invalidTimestamp) {
|
|
538
|
+
attrs.invalidTimestamp = true;
|
|
539
|
+
}
|
|
540
|
+
let status;
|
|
541
|
+
if (event.status === "running" || event.status === "ok" || event.status === "error") {
|
|
542
|
+
status = event.status;
|
|
543
|
+
} else if (event.status === "unknown") {
|
|
544
|
+
attrs.persistedStatus = "unknown";
|
|
545
|
+
}
|
|
546
|
+
const out = {
|
|
547
|
+
eventId: event.eventId,
|
|
548
|
+
runId: event.runId,
|
|
549
|
+
name: event.name,
|
|
550
|
+
kind: event.kind,
|
|
551
|
+
timestamp: ts.ms,
|
|
552
|
+
confidence: event.confidence,
|
|
553
|
+
source: mapPersistedSourceToInspect(event),
|
|
554
|
+
attributes: compactAttributes2(attrs)
|
|
555
|
+
};
|
|
556
|
+
if (event.parentId !== void 0) {
|
|
557
|
+
out.parentId = event.parentId;
|
|
558
|
+
}
|
|
559
|
+
if (status !== void 0) {
|
|
560
|
+
out.status = status;
|
|
561
|
+
}
|
|
562
|
+
if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
|
|
563
|
+
out.durationMs = event.durationMs;
|
|
564
|
+
}
|
|
565
|
+
return out;
|
|
566
|
+
}
|
|
567
|
+
function persistedInspectEventsToInspectEvents(events, options) {
|
|
568
|
+
const skipInvalid = options?.skipInvalid === true;
|
|
569
|
+
const out = [];
|
|
570
|
+
for (const event of events) {
|
|
571
|
+
if (!isPersistedInspectEvent(event)) {
|
|
572
|
+
if (skipInvalid) {
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
576
|
+
}
|
|
577
|
+
out.push(persistedInspectEventToInspectEvent(event));
|
|
578
|
+
}
|
|
579
|
+
return out;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// packages/core/src/persisted/to-trace-event.ts
|
|
583
|
+
function parseIsoToMs2(iso) {
|
|
183
584
|
const parsed = Date.parse(iso);
|
|
184
585
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
185
586
|
}
|
|
@@ -221,10 +622,10 @@ function mapPersistedStatusToRunStatus(status) {
|
|
|
221
622
|
return void 0;
|
|
222
623
|
}
|
|
223
624
|
}
|
|
224
|
-
function mapPersistedError(error) {
|
|
625
|
+
function mapPersistedError(error, attributes) {
|
|
225
626
|
if (!error?.message) return void 0;
|
|
226
627
|
const out = { message: error.message };
|
|
227
|
-
const stack = typeof
|
|
628
|
+
const stack = typeof attributes?.errorStack === "string" && attributes.errorStack.length > 0 ? attributes.errorStack : void 0;
|
|
228
629
|
if (stack) {
|
|
229
630
|
out.stack = stack;
|
|
230
631
|
}
|
|
@@ -238,7 +639,9 @@ function mapTokenUsageToMetadata(tokenUsage, attributes) {
|
|
|
238
639
|
if (tokenUsage) {
|
|
239
640
|
metadata.tokens = {
|
|
240
641
|
...tokenUsage.input !== void 0 ? { input: tokenUsage.input } : {},
|
|
241
|
-
...tokenUsage.output !== void 0 ? { output: tokenUsage.output } : {}
|
|
642
|
+
...tokenUsage.output !== void 0 ? { output: tokenUsage.output } : {},
|
|
643
|
+
...tokenUsage.total !== void 0 ? { total: tokenUsage.total } : {},
|
|
644
|
+
...tokenUsage.cached !== void 0 ? { cached: tokenUsage.cached } : {}
|
|
242
645
|
};
|
|
243
646
|
}
|
|
244
647
|
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
@@ -277,9 +680,9 @@ function resolveStepType(event) {
|
|
|
277
680
|
return mapInspectKindToStepType(event.kind);
|
|
278
681
|
}
|
|
279
682
|
function resolveTimes(event) {
|
|
280
|
-
const timestamp =
|
|
281
|
-
const startTime = event.startedAt !== void 0 ?
|
|
282
|
-
let endTime = event.endedAt !== void 0 ?
|
|
683
|
+
const timestamp = parseIsoToMs2(event.timestamp);
|
|
684
|
+
const startTime = event.startedAt !== void 0 ? parseIsoToMs2(event.startedAt) : timestamp;
|
|
685
|
+
let endTime = event.endedAt !== void 0 ? parseIsoToMs2(event.endedAt) : timestamp;
|
|
283
686
|
if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0 && event.endedAt === void 0) {
|
|
284
687
|
endTime = startTime + event.durationMs;
|
|
285
688
|
}
|
|
@@ -311,7 +714,7 @@ function fromLegacyRunCompleted(event) {
|
|
|
311
714
|
endTime,
|
|
312
715
|
durationMs: event.durationMs ?? Math.max(0, endTime - timestamp)
|
|
313
716
|
};
|
|
314
|
-
const error = mapPersistedError(event.error);
|
|
717
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
315
718
|
if (error) out.error = error;
|
|
316
719
|
return out;
|
|
317
720
|
}
|
|
@@ -345,7 +748,7 @@ function fromLegacyStepCompleted(event) {
|
|
|
345
748
|
endTime,
|
|
346
749
|
durationMs: event.durationMs ?? Math.max(0, endTime - timestamp)
|
|
347
750
|
};
|
|
348
|
-
const error = mapPersistedError(event.error);
|
|
751
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
349
752
|
if (error) out.error = error;
|
|
350
753
|
return out;
|
|
351
754
|
}
|
|
@@ -376,7 +779,7 @@ function fromNativeRun(event) {
|
|
|
376
779
|
endTime,
|
|
377
780
|
durationMs: event.durationMs ?? Math.max(0, endTime - startTime)
|
|
378
781
|
};
|
|
379
|
-
const error = mapPersistedError(event.error);
|
|
782
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
380
783
|
if (error) completed.error = error;
|
|
381
784
|
out.push(completed);
|
|
382
785
|
}
|
|
@@ -418,7 +821,7 @@ function fromNativeStep(event) {
|
|
|
418
821
|
endTime,
|
|
419
822
|
durationMs: event.durationMs ?? Math.max(0, endTime - startTime)
|
|
420
823
|
};
|
|
421
|
-
const error = mapPersistedError(event.error);
|
|
824
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
422
825
|
if (error) completed.error = error;
|
|
423
826
|
out.push(completed);
|
|
424
827
|
}
|
|
@@ -530,7 +933,15 @@ var TreeBuilder = class {
|
|
|
530
933
|
return out;
|
|
531
934
|
}
|
|
532
935
|
};
|
|
533
|
-
|
|
936
|
+
|
|
937
|
+
// packages/core/src/persisted/tree-bridge.ts
|
|
938
|
+
function persistedInspectEventsToRunTrees(events, options) {
|
|
939
|
+
const inspectEvents = persistedInspectEventsToInspectEvents(events, {
|
|
940
|
+
skipInvalid: options?.skipInvalid
|
|
941
|
+
});
|
|
942
|
+
return new TreeBuilder().build(inspectEvents);
|
|
943
|
+
}
|
|
944
|
+
function isRecord4(v) {
|
|
534
945
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
535
946
|
}
|
|
536
947
|
function isNonEmptyStringArray(v) {
|
|
@@ -542,7 +953,7 @@ function validateRedact(redact) {
|
|
|
542
953
|
}
|
|
543
954
|
for (const r of redact) {
|
|
544
955
|
if (typeof r === "string") continue;
|
|
545
|
-
if (!
|
|
956
|
+
if (!isRecord4(r)) {
|
|
546
957
|
throw new Error("Invalid config: redact entries must be strings or objects");
|
|
547
958
|
}
|
|
548
959
|
if (typeof r.key !== "string" || r.key.trim() === "") {
|
|
@@ -561,7 +972,7 @@ function validateRedact(redact) {
|
|
|
561
972
|
}
|
|
562
973
|
}
|
|
563
974
|
function validateMappings(mappings) {
|
|
564
|
-
if (!
|
|
975
|
+
if (!isRecord4(mappings)) {
|
|
565
976
|
throw new Error("Invalid config: mappings must be an object");
|
|
566
977
|
}
|
|
567
978
|
}
|
|
@@ -611,7 +1022,7 @@ async function loadLogIngestConfig(configPath) {
|
|
|
611
1022
|
const msg = e instanceof Error ? e.message : String(e);
|
|
612
1023
|
throw new Error(`Invalid JSON in config file: ${configPath} (${msg})`);
|
|
613
1024
|
}
|
|
614
|
-
if (!
|
|
1025
|
+
if (!isRecord4(parsed)) {
|
|
615
1026
|
throw new Error("Invalid config: expected a JSON object at top-level");
|
|
616
1027
|
}
|
|
617
1028
|
const user = parsed;
|
|
@@ -648,7 +1059,7 @@ async function loadLogIngestConfig(configPath) {
|
|
|
648
1059
|
}
|
|
649
1060
|
return mergeLogIngestConfig(DEFAULT_LOG_INGEST_CONFIG, user);
|
|
650
1061
|
}
|
|
651
|
-
function
|
|
1062
|
+
function isRecord5(v) {
|
|
652
1063
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
653
1064
|
}
|
|
654
1065
|
var JsonLogParser = class {
|
|
@@ -673,7 +1084,7 @@ var JsonLogParser = class {
|
|
|
673
1084
|
});
|
|
674
1085
|
continue;
|
|
675
1086
|
}
|
|
676
|
-
if (!
|
|
1087
|
+
if (!isRecord5(parsed)) {
|
|
677
1088
|
warnings.push({
|
|
678
1089
|
code: "MALFORMED_JSON",
|
|
679
1090
|
message: "JSON log line must be an object",
|
|
@@ -704,7 +1115,7 @@ var JsonLogParser = class {
|
|
|
704
1115
|
return this.parseLines(lines, filePath);
|
|
705
1116
|
}
|
|
706
1117
|
};
|
|
707
|
-
function
|
|
1118
|
+
function isRecord6(v) {
|
|
708
1119
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
709
1120
|
}
|
|
710
1121
|
function findLastJsonObjectSubstring(line) {
|
|
@@ -780,7 +1191,7 @@ var Log4jsParser = class {
|
|
|
780
1191
|
});
|
|
781
1192
|
continue;
|
|
782
1193
|
}
|
|
783
|
-
if (!
|
|
1194
|
+
if (!isRecord6(parsed)) {
|
|
784
1195
|
warnings.push({
|
|
785
1196
|
code: "UNSUPPORTED_LOG4JS_PAYLOAD",
|
|
786
1197
|
message: "Embedded JSON payload must be an object",
|
|
@@ -858,7 +1269,7 @@ var DEFAULT_REDACT_KEYS = [
|
|
|
858
1269
|
"secret",
|
|
859
1270
|
"email"
|
|
860
1271
|
];
|
|
861
|
-
function
|
|
1272
|
+
function isRecord7(v) {
|
|
862
1273
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
863
1274
|
}
|
|
864
1275
|
function toKey(s) {
|
|
@@ -931,7 +1342,7 @@ var Redactor = class {
|
|
|
931
1342
|
if (Array.isArray(value)) {
|
|
932
1343
|
return value.map((v) => this.#redactNested(v));
|
|
933
1344
|
}
|
|
934
|
-
if (
|
|
1345
|
+
if (isRecord7(value)) {
|
|
935
1346
|
const out = {};
|
|
936
1347
|
for (const [k, v] of Object.entries(value)) {
|
|
937
1348
|
out[k] = this.redactValue(k, v);
|
|
@@ -1688,66 +2099,78 @@ function truncateStringForProfile(value, key, maxMetadataValueLength, maxPreview
|
|
|
1688
2099
|
}
|
|
1689
2100
|
|
|
1690
2101
|
// packages/core/src/read-trace.ts
|
|
1691
|
-
function
|
|
2102
|
+
function isRecord8(value) {
|
|
1692
2103
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1693
2104
|
}
|
|
1694
2105
|
function detectLineFormat(parsed) {
|
|
1695
|
-
if (!
|
|
2106
|
+
if (!isRecord8(parsed)) return "unknown";
|
|
1696
2107
|
if (parsed.schemaVersion === "0.1") return "0.1";
|
|
1697
2108
|
if (parsed.schemaVersion === "0.2") return "0.2";
|
|
1698
2109
|
return "unknown";
|
|
1699
2110
|
}
|
|
1700
2111
|
function parseTraceJsonl(raw, options = {}) {
|
|
1701
2112
|
const validate = options.validate ?? isTraceEvent;
|
|
2113
|
+
const emitWarning = (message) => {
|
|
2114
|
+
if (options.warnings !== false) warn(message);
|
|
2115
|
+
};
|
|
1702
2116
|
const persisted = [];
|
|
1703
2117
|
const traceEvents = [];
|
|
2118
|
+
const rows = [];
|
|
2119
|
+
let sourceEventCount = 0;
|
|
1704
2120
|
let saw01 = false;
|
|
1705
2121
|
let saw02 = false;
|
|
2122
|
+
let lineNumber = 0;
|
|
1706
2123
|
for (const line of raw.split(/\r?\n/)) {
|
|
2124
|
+
lineNumber += 1;
|
|
1707
2125
|
const trimmed = line.trim();
|
|
1708
2126
|
if (trimmed === "") continue;
|
|
1709
2127
|
let parsed;
|
|
1710
2128
|
try {
|
|
1711
2129
|
parsed = JSON.parse(trimmed);
|
|
1712
2130
|
} catch {
|
|
1713
|
-
|
|
2131
|
+
emitWarning("Skipped invalid JSON line in trace file");
|
|
1714
2132
|
continue;
|
|
1715
2133
|
}
|
|
1716
2134
|
const format2 = detectLineFormat(parsed);
|
|
1717
2135
|
if (format2 === "0.1") {
|
|
1718
2136
|
saw01 = true;
|
|
1719
2137
|
if (validate(parsed)) {
|
|
2138
|
+
sourceEventCount += 1;
|
|
1720
2139
|
traceEvents.push(parsed);
|
|
2140
|
+
rows.push({ format: "0.1", event: parsed, sourceLine: lineNumber });
|
|
1721
2141
|
} else {
|
|
1722
|
-
|
|
2142
|
+
emitWarning("Skipped invalid trace event line in trace file");
|
|
1723
2143
|
}
|
|
1724
2144
|
continue;
|
|
1725
2145
|
}
|
|
1726
2146
|
if (format2 === "0.2") {
|
|
1727
2147
|
saw02 = true;
|
|
1728
2148
|
if (isPersistedInspectEvent(parsed)) {
|
|
2149
|
+
sourceEventCount += 1;
|
|
1729
2150
|
persisted.push(parsed);
|
|
2151
|
+
rows.push({ format: "0.2", event: parsed, sourceLine: lineNumber });
|
|
2152
|
+
traceEvents.push(...persistedInspectEventToTraceEvents(parsed));
|
|
1730
2153
|
} else {
|
|
1731
|
-
|
|
2154
|
+
emitWarning("Skipped invalid persisted inspect event line in trace file");
|
|
1732
2155
|
}
|
|
1733
2156
|
continue;
|
|
1734
2157
|
}
|
|
1735
|
-
|
|
2158
|
+
emitWarning("Skipped trace line with unknown schemaVersion");
|
|
1736
2159
|
}
|
|
1737
2160
|
if (saw01 && saw02) {
|
|
1738
|
-
|
|
2161
|
+
emitWarning(
|
|
2162
|
+
"Trace file mixes schemaVersion 0.1 and 0.2 lines; normalizing all rows"
|
|
2163
|
+
);
|
|
1739
2164
|
}
|
|
1740
|
-
const converted = persisted.length > 0 ? persistedInspectEventsToTraceEvents(persisted) : [];
|
|
1741
|
-
const events = [...traceEvents, ...converted];
|
|
1742
2165
|
let format = "empty";
|
|
1743
2166
|
if (saw01 && saw02) format = "mixed";
|
|
1744
2167
|
else if (saw01) format = "0.1";
|
|
1745
2168
|
else if (saw02) format = "0.2";
|
|
1746
|
-
return { format, events, persisted };
|
|
2169
|
+
return { format, sourceEventCount, events: traceEvents, persisted, rows };
|
|
1747
2170
|
}
|
|
1748
2171
|
|
|
1749
2172
|
// packages/core/src/storage.ts
|
|
1750
|
-
function
|
|
2173
|
+
function isRecord9(value) {
|
|
1751
2174
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1752
2175
|
}
|
|
1753
2176
|
function nonEmptyString(value) {
|
|
@@ -1758,7 +2181,7 @@ function finiteNumber(value) {
|
|
|
1758
2181
|
}
|
|
1759
2182
|
function optionalErrorInfo(value) {
|
|
1760
2183
|
if (value === void 0) return true;
|
|
1761
|
-
if (!
|
|
2184
|
+
if (!isRecord9(value)) return false;
|
|
1762
2185
|
if (typeof value.message !== "string") return false;
|
|
1763
2186
|
if ("stack" in value && value.stack !== void 0) {
|
|
1764
2187
|
if (typeof value.stack !== "string") return false;
|
|
@@ -1766,7 +2189,7 @@ function optionalErrorInfo(value) {
|
|
|
1766
2189
|
return true;
|
|
1767
2190
|
}
|
|
1768
2191
|
function validateEvent(event) {
|
|
1769
|
-
if (!
|
|
2192
|
+
if (!isRecord9(event)) return false;
|
|
1770
2193
|
if (event.schemaVersion !== "0.1") return false;
|
|
1771
2194
|
if (!finiteNumber(event.timestamp)) return false;
|
|
1772
2195
|
if (typeof event.event !== "string") return false;
|
|
@@ -1775,7 +2198,7 @@ function validateEvent(event) {
|
|
|
1775
2198
|
if (!nonEmptyString(event.runId) || !nonEmptyString(event.name) || !finiteNumber(event.startTime)) {
|
|
1776
2199
|
return false;
|
|
1777
2200
|
}
|
|
1778
|
-
if (event.metadata !== void 0 && !
|
|
2201
|
+
if (event.metadata !== void 0 && !isRecord9(event.metadata)) {
|
|
1779
2202
|
return false;
|
|
1780
2203
|
}
|
|
1781
2204
|
return true;
|
|
@@ -1790,7 +2213,7 @@ function validateEvent(event) {
|
|
|
1790
2213
|
if (event.parentId !== void 0 && typeof event.parentId !== "string") {
|
|
1791
2214
|
return false;
|
|
1792
2215
|
}
|
|
1793
|
-
if (event.metadata !== void 0 && !
|
|
2216
|
+
if (event.metadata !== void 0 && !isRecord9(event.metadata)) {
|
|
1794
2217
|
return false;
|
|
1795
2218
|
}
|
|
1796
2219
|
return true;
|
|
@@ -1848,27 +2271,15 @@ async function writeTraceEvent(event, traceDir) {
|
|
|
1848
2271
|
}
|
|
1849
2272
|
warn("Failed to append trace event to fallback directory");
|
|
1850
2273
|
}
|
|
1851
|
-
async function
|
|
2274
|
+
async function readTraceEventsFromFile(filePath) {
|
|
1852
2275
|
try {
|
|
1853
|
-
const
|
|
1854
|
-
return
|
|
2276
|
+
const raw = await readFile(filePath, "utf-8");
|
|
2277
|
+
return parseTraceJsonl(raw, { validate: validateEvent }).events;
|
|
1855
2278
|
} catch (e) {
|
|
1856
2279
|
if (e && typeof e === "object" && "code" in e && e.code === "ENOENT") {
|
|
1857
|
-
return void 0;
|
|
1858
|
-
}
|
|
1859
|
-
warn("Unexpected error reading trace file", e);
|
|
1860
|
-
return void 0;
|
|
1861
|
-
}
|
|
1862
|
-
}
|
|
1863
|
-
async function readTraceEvents(runId, traceDir) {
|
|
1864
|
-
try {
|
|
1865
|
-
const raw = await readTraceFile(runId, traceDir);
|
|
1866
|
-
if (raw === void 0) {
|
|
1867
2280
|
return [];
|
|
1868
2281
|
}
|
|
1869
|
-
|
|
1870
|
-
} catch (e) {
|
|
1871
|
-
warn("Failed to read trace events", e);
|
|
2282
|
+
warn("Failed to read trace events from file", e);
|
|
1872
2283
|
return [];
|
|
1873
2284
|
}
|
|
1874
2285
|
}
|
|
@@ -1877,7 +2288,7 @@ async function readTraceEvents(runId, traceDir) {
|
|
|
1877
2288
|
var DEFAULT_MAX_METADATA_VALUE_LENGTH = 2e3;
|
|
1878
2289
|
var DEFAULT_MAX_PREVIEW_LENGTH = 500;
|
|
1879
2290
|
var DEFAULT_MAX_EVENT_BYTES = 65536;
|
|
1880
|
-
function
|
|
2291
|
+
function isRecord10(value) {
|
|
1881
2292
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1882
2293
|
}
|
|
1883
2294
|
function isPreviewKey2(key) {
|
|
@@ -1897,21 +2308,10 @@ function resolveTraceSafetyOptions(options) {
|
|
|
1897
2308
|
{
|
|
1898
2309
|
redactEnabled = true;
|
|
1899
2310
|
}
|
|
1900
|
-
const profile =
|
|
2311
|
+
const profile = "local";
|
|
1901
2312
|
const resolvedProfile = resolveRedactionProfile(profile);
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
let maxMetadataValueLength = userMaxMetadata ?? DEFAULT_MAX_METADATA_VALUE_LENGTH;
|
|
1905
|
-
let maxPreviewLength = userMaxPreview ?? DEFAULT_MAX_PREVIEW_LENGTH;
|
|
1906
|
-
if (redactEnabled && profile !== "local") {
|
|
1907
|
-
const capped = applyProfileMetadataCaps(
|
|
1908
|
-
maxMetadataValueLength,
|
|
1909
|
-
maxPreviewLength,
|
|
1910
|
-
resolvedProfile
|
|
1911
|
-
);
|
|
1912
|
-
maxMetadataValueLength = capped.maxMetadataValueLength;
|
|
1913
|
-
maxPreviewLength = capped.maxPreviewLength;
|
|
1914
|
-
}
|
|
2313
|
+
let maxMetadataValueLength = DEFAULT_MAX_METADATA_VALUE_LENGTH;
|
|
2314
|
+
let maxPreviewLength = DEFAULT_MAX_PREVIEW_LENGTH;
|
|
1915
2315
|
return {
|
|
1916
2316
|
redactEnabled,
|
|
1917
2317
|
redactionRules,
|
|
@@ -1919,11 +2319,26 @@ function resolveTraceSafetyOptions(options) {
|
|
|
1919
2319
|
profileExtraKeys: redactEnabled ? resolvedProfile.extraKeys : [],
|
|
1920
2320
|
maxMetadataValueLength,
|
|
1921
2321
|
maxPreviewLength,
|
|
1922
|
-
maxEventBytes:
|
|
2322
|
+
maxEventBytes: DEFAULT_MAX_EVENT_BYTES
|
|
1923
2323
|
};
|
|
1924
2324
|
}
|
|
1925
2325
|
function boundMetadataValue(key, value, opts, seen, depth) {
|
|
1926
2326
|
if (depth > 32) return "[MaxDepth]";
|
|
2327
|
+
if (typeof value === "bigint") {
|
|
2328
|
+
return `${value.toString()}n`;
|
|
2329
|
+
}
|
|
2330
|
+
if (typeof value === "function") {
|
|
2331
|
+
return "[Function]";
|
|
2332
|
+
}
|
|
2333
|
+
if (typeof value === "symbol") {
|
|
2334
|
+
return "[Symbol]";
|
|
2335
|
+
}
|
|
2336
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
2337
|
+
return String(value);
|
|
2338
|
+
}
|
|
2339
|
+
if (value === void 0) {
|
|
2340
|
+
return null;
|
|
2341
|
+
}
|
|
1927
2342
|
if (value === null || typeof value !== "object") {
|
|
1928
2343
|
if (typeof value === "string") {
|
|
1929
2344
|
const max = isPreviewKey2(key) ? opts.maxPreviewLength : opts.maxMetadataValueLength;
|
|
@@ -1945,8 +2360,12 @@ function boundMetadataValue(key, value, opts, seen, depth) {
|
|
|
1945
2360
|
}
|
|
1946
2361
|
const record = value;
|
|
1947
2362
|
const out = {};
|
|
1948
|
-
|
|
1949
|
-
|
|
2363
|
+
try {
|
|
2364
|
+
for (const [k, v] of Object.entries(record)) {
|
|
2365
|
+
out[k] = boundMetadataValue(k, v, opts, seen, depth + 1);
|
|
2366
|
+
}
|
|
2367
|
+
} catch {
|
|
2368
|
+
return { truncated: true, reason: "metadataEnumerationFailed" };
|
|
1950
2369
|
}
|
|
1951
2370
|
return out;
|
|
1952
2371
|
}
|
|
@@ -1960,16 +2379,25 @@ function redactMetadata(metadata, opts) {
|
|
|
1960
2379
|
}
|
|
1961
2380
|
function prepareMetadataForDisk(metadata, opts) {
|
|
1962
2381
|
try {
|
|
1963
|
-
const
|
|
1964
|
-
|
|
2382
|
+
const preBounded = boundMetadataValue(
|
|
2383
|
+
"metadata",
|
|
2384
|
+
metadata,
|
|
2385
|
+
opts,
|
|
2386
|
+
/* @__PURE__ */ new WeakSet(),
|
|
2387
|
+
0
|
|
2388
|
+
);
|
|
2389
|
+
const redacted = redactMetadata(
|
|
2390
|
+
isRecord10(preBounded) ? preBounded : {},
|
|
2391
|
+
opts
|
|
2392
|
+
);
|
|
1965
2393
|
const bounded = boundMetadataValue(
|
|
1966
2394
|
"metadata",
|
|
1967
2395
|
redacted,
|
|
1968
2396
|
opts,
|
|
1969
|
-
|
|
2397
|
+
/* @__PURE__ */ new WeakSet(),
|
|
1970
2398
|
0
|
|
1971
2399
|
);
|
|
1972
|
-
return
|
|
2400
|
+
return isRecord10(bounded) ? bounded : {};
|
|
1973
2401
|
} catch {
|
|
1974
2402
|
return { truncated: true, reason: "metadataPreparationFailed" };
|
|
1975
2403
|
}
|
|
@@ -2219,12 +2647,10 @@ var TraceDirectory = class {
|
|
|
2219
2647
|
function isFiniteNumber2(v) {
|
|
2220
2648
|
return typeof v === "number" && Number.isFinite(v);
|
|
2221
2649
|
}
|
|
2222
|
-
function
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
return void 0;
|
|
2227
|
-
}
|
|
2650
|
+
function parseIsoToMs3(value) {
|
|
2651
|
+
if (value === void 0) return void 0;
|
|
2652
|
+
const parsed = Date.parse(value);
|
|
2653
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2228
2654
|
}
|
|
2229
2655
|
async function extractMetadata(filePath, _quickScan) {
|
|
2230
2656
|
const stats = await stat(filePath);
|
|
@@ -2233,8 +2659,7 @@ async function extractMetadata(filePath, _quickScan) {
|
|
|
2233
2659
|
runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
|
|
2234
2660
|
}
|
|
2235
2661
|
const raw = await readFile(filePath, "utf-8");
|
|
2236
|
-
const
|
|
2237
|
-
let eventCount = 0;
|
|
2662
|
+
const parsedTrace = parseTraceJsonl(raw, { warnings: false });
|
|
2238
2663
|
let runId;
|
|
2239
2664
|
let name;
|
|
2240
2665
|
let startedAt;
|
|
@@ -2244,16 +2669,32 @@ async function extractMetadata(filePath, _quickScan) {
|
|
|
2244
2669
|
let hasRunCompleted = false;
|
|
2245
2670
|
let runCompletedStatus;
|
|
2246
2671
|
let anyStepError = false;
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2672
|
+
const anyKnownEvent = parsedTrace.sourceEventCount > 0;
|
|
2673
|
+
let persistedStatus;
|
|
2674
|
+
const persistedRun = parsedTrace.persisted.find(
|
|
2675
|
+
(event) => event.kind === "RUN"
|
|
2676
|
+
);
|
|
2677
|
+
if (persistedRun) {
|
|
2678
|
+
runId = persistedRun.runId;
|
|
2679
|
+
if (persistedRun.name.trim() !== "") {
|
|
2680
|
+
name = persistedRun.name;
|
|
2681
|
+
}
|
|
2682
|
+
startedAt = parseIsoToMs3(persistedRun.startedAt) ?? parseIsoToMs3(persistedRun.timestamp);
|
|
2683
|
+
endedAt = parseIsoToMs3(persistedRun.endedAt);
|
|
2684
|
+
if (isFiniteNumber2(persistedRun.durationMs)) {
|
|
2685
|
+
explicitDurationMs = persistedRun.durationMs;
|
|
2686
|
+
if (endedAt === void 0 && startedAt !== void 0) {
|
|
2687
|
+
endedAt = startedAt + persistedRun.durationMs;
|
|
2688
|
+
}
|
|
2689
|
+
}
|
|
2690
|
+
if (persistedRun.status === "ok") persistedStatus = "success";
|
|
2691
|
+
else if (persistedRun.status === "error") persistedStatus = "error";
|
|
2692
|
+
else if (persistedRun.status === "running") persistedStatus = "running";
|
|
2693
|
+
else if (persistedRun.status === "unknown") persistedStatus = "unknown";
|
|
2694
|
+
} else {
|
|
2695
|
+
runId = parsedTrace.persisted[0]?.runId;
|
|
2696
|
+
}
|
|
2697
|
+
for (const e of parsedTrace.events) {
|
|
2257
2698
|
if (runId === void 0 && typeof e.runId === "string") {
|
|
2258
2699
|
runId = e.runId;
|
|
2259
2700
|
}
|
|
@@ -2290,6 +2731,8 @@ async function extractMetadata(filePath, _quickScan) {
|
|
|
2290
2731
|
status = runCompletedStatus;
|
|
2291
2732
|
} else if (anyStepError) {
|
|
2292
2733
|
status = "error";
|
|
2734
|
+
} else if (persistedStatus !== void 0) {
|
|
2735
|
+
status = persistedStatus;
|
|
2293
2736
|
} else if (hasRunStarted && !hasRunCompleted) {
|
|
2294
2737
|
status = "running";
|
|
2295
2738
|
} else if (anyKnownEvent) {
|
|
@@ -2305,12 +2748,15 @@ async function extractMetadata(filePath, _quickScan) {
|
|
|
2305
2748
|
startedAt,
|
|
2306
2749
|
endedAt,
|
|
2307
2750
|
durationMs,
|
|
2308
|
-
eventCount,
|
|
2751
|
+
eventCount: parsedTrace.sourceEventCount,
|
|
2309
2752
|
filePath,
|
|
2310
2753
|
fileSize: stats.size,
|
|
2311
2754
|
createdAt: stats.birthtime
|
|
2312
2755
|
};
|
|
2313
2756
|
}
|
|
2757
|
+
function isNonNegativeFiniteNumber(value) {
|
|
2758
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
2759
|
+
}
|
|
2314
2760
|
function buildRunSummary(events) {
|
|
2315
2761
|
const started = events.find(
|
|
2316
2762
|
(e) => e.event === "run_started"
|
|
@@ -2333,8 +2779,10 @@ function buildRunSummary(events) {
|
|
|
2333
2779
|
name: s.name,
|
|
2334
2780
|
status: "running",
|
|
2335
2781
|
parentId: s.parentId,
|
|
2336
|
-
tokensInput:
|
|
2337
|
-
tokensOutput:
|
|
2782
|
+
tokensInput: isNonNegativeFiniteNumber(s.metadata?.tokens?.input) ? s.metadata.tokens.input : void 0,
|
|
2783
|
+
tokensOutput: isNonNegativeFiniteNumber(s.metadata?.tokens?.output) ? s.metadata.tokens.output : void 0,
|
|
2784
|
+
tokensTotal: isNonNegativeFiniteNumber(s.metadata?.tokens?.total) ? s.metadata.tokens.total : void 0,
|
|
2785
|
+
tokensCached: isNonNegativeFiniteNumber(s.metadata?.tokens?.cached) ? s.metadata.tokens.cached : void 0
|
|
2338
2786
|
});
|
|
2339
2787
|
}
|
|
2340
2788
|
}
|
|
@@ -2356,7 +2804,11 @@ function buildRunSummary(events) {
|
|
|
2356
2804
|
let longestStep;
|
|
2357
2805
|
let totalTokensInput = 0;
|
|
2358
2806
|
let totalTokensOutput = 0;
|
|
2359
|
-
let
|
|
2807
|
+
let totalTokensTotal = 0;
|
|
2808
|
+
let totalTokensCached = 0;
|
|
2809
|
+
let tokenBearingSteps = 0;
|
|
2810
|
+
let stepsWithKnownTotal = 0;
|
|
2811
|
+
let hasCachedTokens = false;
|
|
2360
2812
|
const depthCache = /* @__PURE__ */ new Map();
|
|
2361
2813
|
const computeDepth = (stepId) => {
|
|
2362
2814
|
const cached = depthCache.get(stepId);
|
|
@@ -2385,10 +2837,21 @@ function buildRunSummary(events) {
|
|
|
2385
2837
|
longestStep = { name: s.name, durationMs: s.durationMs, type: s.type };
|
|
2386
2838
|
}
|
|
2387
2839
|
}
|
|
2388
|
-
if (
|
|
2389
|
-
|
|
2390
|
-
if (
|
|
2391
|
-
if (
|
|
2840
|
+
if (s.tokensInput !== void 0 || s.tokensOutput !== void 0 || s.tokensTotal !== void 0 || s.tokensCached !== void 0) {
|
|
2841
|
+
tokenBearingSteps += 1;
|
|
2842
|
+
if (s.tokensInput !== void 0) totalTokensInput += s.tokensInput;
|
|
2843
|
+
if (s.tokensOutput !== void 0) totalTokensOutput += s.tokensOutput;
|
|
2844
|
+
if (s.tokensTotal !== void 0) {
|
|
2845
|
+
totalTokensTotal += s.tokensTotal;
|
|
2846
|
+
stepsWithKnownTotal += 1;
|
|
2847
|
+
} else if (s.tokensInput !== void 0 && s.tokensOutput !== void 0) {
|
|
2848
|
+
totalTokensTotal += s.tokensInput + s.tokensOutput;
|
|
2849
|
+
stepsWithKnownTotal += 1;
|
|
2850
|
+
}
|
|
2851
|
+
if (s.tokensCached !== void 0) {
|
|
2852
|
+
totalTokensCached += s.tokensCached;
|
|
2853
|
+
hasCachedTokens = true;
|
|
2854
|
+
}
|
|
2392
2855
|
}
|
|
2393
2856
|
}
|
|
2394
2857
|
const summary = {
|
|
@@ -2403,7 +2866,14 @@ function buildRunSummary(events) {
|
|
|
2403
2866
|
errorSteps,
|
|
2404
2867
|
maxDepth,
|
|
2405
2868
|
...longestStep ? { longestStep } : {},
|
|
2406
|
-
...
|
|
2869
|
+
...tokenBearingSteps > 0 ? {
|
|
2870
|
+
totalTokens: {
|
|
2871
|
+
input: totalTokensInput,
|
|
2872
|
+
output: totalTokensOutput,
|
|
2873
|
+
...stepsWithKnownTotal === tokenBearingSteps ? { total: totalTokensTotal } : {},
|
|
2874
|
+
...hasCachedTokens ? { cached: totalTokensCached } : {}
|
|
2875
|
+
}
|
|
2876
|
+
} : {}
|
|
2407
2877
|
};
|
|
2408
2878
|
return summary;
|
|
2409
2879
|
}
|
|
@@ -2708,9 +3178,17 @@ function renderRunWhat(summary, options = {}) {
|
|
|
2708
3178
|
`Status: ${summary.status} \xB7 Duration: ${duration} \xB7 Steps: ${summary.totalSteps} (${stepMixLine(summary)})`
|
|
2709
3179
|
);
|
|
2710
3180
|
if (summary.totalTokens) {
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
3181
|
+
const tokenParts = [
|
|
3182
|
+
`${summary.totalTokens.input} in`,
|
|
3183
|
+
`${summary.totalTokens.output} out`
|
|
3184
|
+
];
|
|
3185
|
+
if (summary.totalTokens.total !== void 0) {
|
|
3186
|
+
tokenParts.push(`${summary.totalTokens.total} total`);
|
|
3187
|
+
}
|
|
3188
|
+
if (summary.totalTokens.cached !== void 0) {
|
|
3189
|
+
tokenParts.push(`${summary.totalTokens.cached} cached`);
|
|
3190
|
+
}
|
|
3191
|
+
lines.push(`Tokens: ${tokenParts.join(" / ")}`);
|
|
2714
3192
|
}
|
|
2715
3193
|
if (showCorrelation && summary.correlation) {
|
|
2716
3194
|
const parts = [];
|
|
@@ -2790,7 +3268,7 @@ function stableJson(value, pretty) {
|
|
|
2790
3268
|
const sorted = sortKeysDeep(value);
|
|
2791
3269
|
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
2792
3270
|
}
|
|
2793
|
-
function
|
|
3271
|
+
function compactAttributes3(attrs, options) {
|
|
2794
3272
|
if (attrs === void 0) return {};
|
|
2795
3273
|
const maxLen = options?.maxLength ?? 500;
|
|
2796
3274
|
const redacted = options?.redacted ?? true;
|
|
@@ -2935,7 +3413,7 @@ function exportHtml(tree, options) {
|
|
|
2935
3413
|
attrsHtml += "<h2>Attributes (bounded)</h2>";
|
|
2936
3414
|
for (const n of flat) {
|
|
2937
3415
|
if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
|
|
2938
|
-
const compact =
|
|
3416
|
+
const compact = compactAttributes3(n.event.attributes, {
|
|
2939
3417
|
maxLength: maxLen,
|
|
2940
3418
|
redacted
|
|
2941
3419
|
});
|
|
@@ -3086,7 +3564,7 @@ function exportMarkdown(tree, options) {
|
|
|
3086
3564
|
lines.push("");
|
|
3087
3565
|
for (const n of flat) {
|
|
3088
3566
|
if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
|
|
3089
|
-
const compact =
|
|
3567
|
+
const compact = compactAttributes3(n.event.attributes, {
|
|
3090
3568
|
maxLength: maxLen,
|
|
3091
3569
|
redacted
|
|
3092
3570
|
});
|
|
@@ -3249,7 +3727,7 @@ function manualTraceEventsToRunTree(events) {
|
|
|
3249
3727
|
}
|
|
3250
3728
|
|
|
3251
3729
|
// packages/core/src/exporters/redact-export.ts
|
|
3252
|
-
function
|
|
3730
|
+
function isRecord11(value) {
|
|
3253
3731
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3254
3732
|
}
|
|
3255
3733
|
function deepClone(value) {
|
|
@@ -3323,7 +3801,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
|
|
|
3323
3801
|
0
|
|
3324
3802
|
);
|
|
3325
3803
|
const err = bounded.error;
|
|
3326
|
-
if (
|
|
3804
|
+
if (isRecord11(err) && typeof err.message === "string") {
|
|
3327
3805
|
bounded.error = {
|
|
3328
3806
|
...err,
|
|
3329
3807
|
message: truncateStringForProfile(
|
|
@@ -3344,6 +3822,89 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
|
|
|
3344
3822
|
}
|
|
3345
3823
|
return bounded;
|
|
3346
3824
|
}
|
|
3825
|
+
function redactErrorInfo(error, redactor, maxMetadataValueLength, maxPreviewLength) {
|
|
3826
|
+
if (error === void 0) return void 0;
|
|
3827
|
+
const record = redactEventAttributes(
|
|
3828
|
+
{ error },
|
|
3829
|
+
redactor,
|
|
3830
|
+
maxMetadataValueLength,
|
|
3831
|
+
maxPreviewLength
|
|
3832
|
+
);
|
|
3833
|
+
const redacted = record?.error;
|
|
3834
|
+
if (!isRecord11(redacted) || typeof redacted.message !== "string") {
|
|
3835
|
+
return void 0;
|
|
3836
|
+
}
|
|
3837
|
+
return {
|
|
3838
|
+
message: redacted.message,
|
|
3839
|
+
...typeof redacted.stack === "string" ? { stack: redacted.stack } : {}
|
|
3840
|
+
};
|
|
3841
|
+
}
|
|
3842
|
+
function redactTraceEventsForReport(events, options) {
|
|
3843
|
+
const profile = options?.redactionProfile ?? "local";
|
|
3844
|
+
if (profile === "local") {
|
|
3845
|
+
return deepClone(events);
|
|
3846
|
+
}
|
|
3847
|
+
const resolved = resolveRedactionProfile(profile);
|
|
3848
|
+
const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
|
|
3849
|
+
2e3,
|
|
3850
|
+
500,
|
|
3851
|
+
resolved
|
|
3852
|
+
);
|
|
3853
|
+
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
3854
|
+
return events.map((event) => {
|
|
3855
|
+
switch (event.event) {
|
|
3856
|
+
case "run_started":
|
|
3857
|
+
return {
|
|
3858
|
+
...event,
|
|
3859
|
+
name: truncateStringForProfile(
|
|
3860
|
+
event.name,
|
|
3861
|
+
"name",
|
|
3862
|
+
maxMetadataValueLength,
|
|
3863
|
+
maxPreviewLength
|
|
3864
|
+
),
|
|
3865
|
+
...event.metadata !== void 0 ? {
|
|
3866
|
+
metadata: redactEventAttributes(
|
|
3867
|
+
event.metadata,
|
|
3868
|
+
redactor,
|
|
3869
|
+
maxMetadataValueLength,
|
|
3870
|
+
maxPreviewLength
|
|
3871
|
+
)
|
|
3872
|
+
} : {}
|
|
3873
|
+
};
|
|
3874
|
+
case "step_started":
|
|
3875
|
+
return {
|
|
3876
|
+
...event,
|
|
3877
|
+
name: truncateStringForProfile(
|
|
3878
|
+
event.name,
|
|
3879
|
+
"name",
|
|
3880
|
+
maxMetadataValueLength,
|
|
3881
|
+
maxPreviewLength
|
|
3882
|
+
),
|
|
3883
|
+
...event.metadata !== void 0 ? {
|
|
3884
|
+
metadata: redactEventAttributes(
|
|
3885
|
+
event.metadata,
|
|
3886
|
+
redactor,
|
|
3887
|
+
maxMetadataValueLength,
|
|
3888
|
+
maxPreviewLength
|
|
3889
|
+
)
|
|
3890
|
+
} : {}
|
|
3891
|
+
};
|
|
3892
|
+
case "run_completed":
|
|
3893
|
+
case "step_completed":
|
|
3894
|
+
return {
|
|
3895
|
+
...event,
|
|
3896
|
+
...event.error !== void 0 ? {
|
|
3897
|
+
error: redactErrorInfo(
|
|
3898
|
+
event.error,
|
|
3899
|
+
redactor,
|
|
3900
|
+
maxMetadataValueLength,
|
|
3901
|
+
maxPreviewLength
|
|
3902
|
+
)
|
|
3903
|
+
} : {}
|
|
3904
|
+
};
|
|
3905
|
+
}
|
|
3906
|
+
});
|
|
3907
|
+
}
|
|
3347
3908
|
function redactRunTreeForExport(tree, options) {
|
|
3348
3909
|
const profile = options?.redactionProfile ?? "local";
|
|
3349
3910
|
if (profile === "local") {
|
|
@@ -3407,12 +3968,15 @@ footer{margin-top:2rem;font-size:0.85rem;color:#555}
|
|
|
3407
3968
|
`.trim();
|
|
3408
3969
|
function buildRunReport(events, options) {
|
|
3409
3970
|
const profile = options.redactionProfile ?? "local";
|
|
3410
|
-
const
|
|
3971
|
+
const safeEvents = redactTraceEventsForReport(events, {
|
|
3972
|
+
redactionProfile: profile
|
|
3973
|
+
});
|
|
3974
|
+
const whatSummary = buildRunWhatSummary(safeEvents);
|
|
3411
3975
|
const whatText = renderRunWhat(whatSummary, {
|
|
3412
3976
|
correlation: options.correlation !== false
|
|
3413
3977
|
});
|
|
3414
|
-
const timelineText = renderTimeline(buildRunTimeline(
|
|
3415
|
-
const tree = resolveTree(
|
|
3978
|
+
const timelineText = renderTimeline(buildRunTimeline(safeEvents));
|
|
3979
|
+
const tree = resolveTree(safeEvents, profile);
|
|
3416
3980
|
const exportOpts = {
|
|
3417
3981
|
includeMetadata: false,
|
|
3418
3982
|
includeAttributes: options.includeAttributes === true,
|
|
@@ -3493,6 +4057,8 @@ ${attrsSection}
|
|
|
3493
4057
|
fileExtension: ".html"
|
|
3494
4058
|
};
|
|
3495
4059
|
}
|
|
4060
|
+
|
|
4061
|
+
// packages/core/src/stats.ts
|
|
3496
4062
|
function percentile(sorted, p) {
|
|
3497
4063
|
if (sorted.length === 0) return void 0;
|
|
3498
4064
|
const idx = Math.min(
|
|
@@ -3503,19 +4069,10 @@ function percentile(sorted, p) {
|
|
|
3503
4069
|
}
|
|
3504
4070
|
async function readRunStartedMetadata(filePath) {
|
|
3505
4071
|
try {
|
|
3506
|
-
const
|
|
3507
|
-
for (const
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
let parsed;
|
|
3511
|
-
try {
|
|
3512
|
-
parsed = JSON.parse(trimmed);
|
|
3513
|
-
} catch {
|
|
3514
|
-
continue;
|
|
3515
|
-
}
|
|
3516
|
-
if (!isTraceEvent(parsed)) continue;
|
|
3517
|
-
if (parsed.event !== "run_started") continue;
|
|
3518
|
-
const rs = parsed;
|
|
4072
|
+
const events = await readTraceEventsFromFile(filePath);
|
|
4073
|
+
for (const event of events) {
|
|
4074
|
+
if (event.event !== "run_started") continue;
|
|
4075
|
+
const rs = event;
|
|
3519
4076
|
if (rs.metadata && typeof rs.metadata === "object") {
|
|
3520
4077
|
return rs.metadata;
|
|
3521
4078
|
}
|
|
@@ -3574,7 +4131,7 @@ async function buildTraceStats(metas, options) {
|
|
|
3574
4131
|
});
|
|
3575
4132
|
}
|
|
3576
4133
|
try {
|
|
3577
|
-
const events = await
|
|
4134
|
+
const events = await readTraceEventsFromFile(m.filePath);
|
|
3578
4135
|
if (events.length === 0) continue;
|
|
3579
4136
|
const summary = buildRunSummary(events);
|
|
3580
4137
|
totalSteps += summary.totalSteps;
|
|
@@ -3762,7 +4319,7 @@ async function searchTraces(metas, options) {
|
|
|
3762
4319
|
if (options.status && m.status !== options.status) continue;
|
|
3763
4320
|
let events = [];
|
|
3764
4321
|
try {
|
|
3765
|
-
events = await
|
|
4322
|
+
events = await readTraceEventsFromFile(m.filePath);
|
|
3766
4323
|
} catch {
|
|
3767
4324
|
continue;
|
|
3768
4325
|
}
|
|
@@ -3898,7 +4455,7 @@ var KNOWN_EVENTS = /* @__PURE__ */ new Set([
|
|
|
3898
4455
|
"step_started",
|
|
3899
4456
|
"step_completed"
|
|
3900
4457
|
]);
|
|
3901
|
-
function
|
|
4458
|
+
function isRecord12(value) {
|
|
3902
4459
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3903
4460
|
}
|
|
3904
4461
|
function safeParse(line) {
|
|
@@ -3920,7 +4477,7 @@ async function isAgentInspectTrace(filePath) {
|
|
|
3920
4477
|
if (trimmed === "") continue;
|
|
3921
4478
|
const parsed = safeParse(trimmed);
|
|
3922
4479
|
if (!parsed) continue;
|
|
3923
|
-
if (!
|
|
4480
|
+
if (!isRecord12(parsed)) continue;
|
|
3924
4481
|
checked += 1;
|
|
3925
4482
|
if (isTraceEvent(parsed)) return true;
|
|
3926
4483
|
const ev = parsed.event;
|
|
@@ -3935,9 +4492,1572 @@ async function isAgentInspectTrace(filePath) {
|
|
|
3935
4492
|
return false;
|
|
3936
4493
|
}
|
|
3937
4494
|
}
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
4495
|
+
resolveTraceSafetyOptions();
|
|
4496
|
+
var DEFAULT_MAX_TRACE_INPUT_BYTES = 10 * 1024 * 1024;
|
|
4497
|
+
var MIN_DETECTION_CONFIDENCE = 0.5;
|
|
4498
|
+
var AMBIGUOUS_CONFIDENCE_DELTA = 0.05;
|
|
4499
|
+
var resolvedInputCache = /* @__PURE__ */ new WeakMap();
|
|
4500
|
+
var OPENINFERENCE_READER_FORMAT = "openinference-json";
|
|
4501
|
+
var OTLP_READER_FORMAT = "otlp-json";
|
|
4502
|
+
var OPENINFERENCE_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
4503
|
+
"trace_id",
|
|
4504
|
+
"traceId",
|
|
4505
|
+
"span_id",
|
|
4506
|
+
"spanId",
|
|
4507
|
+
"parent_span_id",
|
|
4508
|
+
"parentSpanId",
|
|
4509
|
+
"name",
|
|
4510
|
+
"start_time_unix_nano",
|
|
4511
|
+
"startTimeUnixNano",
|
|
4512
|
+
"end_time_unix_nano",
|
|
4513
|
+
"endTimeUnixNano",
|
|
4514
|
+
"start_time",
|
|
4515
|
+
"startTime",
|
|
4516
|
+
"end_time",
|
|
4517
|
+
"endTime",
|
|
4518
|
+
"attributes",
|
|
4519
|
+
"status",
|
|
4520
|
+
"kind",
|
|
4521
|
+
"span_kind",
|
|
4522
|
+
"spanKind"
|
|
4523
|
+
]);
|
|
4524
|
+
var OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS = [
|
|
4525
|
+
"input.value",
|
|
4526
|
+
"output.value",
|
|
4527
|
+
"input.mime_type",
|
|
4528
|
+
"output.mime_type",
|
|
4529
|
+
"llm.input_messages",
|
|
4530
|
+
"llm.output_messages",
|
|
4531
|
+
"llm.prompts",
|
|
4532
|
+
"llm.completions",
|
|
4533
|
+
"retrieval.documents",
|
|
4534
|
+
"reranker.input_documents",
|
|
4535
|
+
"reranker.output_documents",
|
|
4536
|
+
"document.content",
|
|
4537
|
+
"gen_ai.prompt",
|
|
4538
|
+
"gen_ai.completion",
|
|
4539
|
+
"gen_ai.input.messages",
|
|
4540
|
+
"gen_ai.output.messages"
|
|
4541
|
+
];
|
|
4542
|
+
var OTLP_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
4543
|
+
"traceId",
|
|
4544
|
+
"spanId",
|
|
4545
|
+
"parentSpanId",
|
|
4546
|
+
"name",
|
|
4547
|
+
"kind",
|
|
4548
|
+
"startTimeUnixNano",
|
|
4549
|
+
"endTimeUnixNano",
|
|
4550
|
+
"attributes",
|
|
4551
|
+
"events",
|
|
4552
|
+
"status",
|
|
4553
|
+
"droppedAttributesCount",
|
|
4554
|
+
"droppedEventsCount",
|
|
4555
|
+
"droppedLinksCount",
|
|
4556
|
+
"links",
|
|
4557
|
+
"flags"
|
|
4558
|
+
]);
|
|
4559
|
+
var TraceReadError = class extends Error {
|
|
4560
|
+
code;
|
|
4561
|
+
warnings;
|
|
4562
|
+
constructor(code, message, warnings = []) {
|
|
4563
|
+
super(message);
|
|
4564
|
+
this.name = "TraceReadError";
|
|
4565
|
+
this.code = code;
|
|
4566
|
+
this.warnings = warnings;
|
|
4567
|
+
}
|
|
4568
|
+
};
|
|
4569
|
+
function normalizeCandidate(reader, candidate) {
|
|
4570
|
+
const confidence = Number.isFinite(candidate.confidence) ? Math.max(0, Math.min(1, candidate.confidence)) : 0;
|
|
4571
|
+
return {
|
|
4572
|
+
...candidate,
|
|
4573
|
+
format: candidate.format || reader.format,
|
|
4574
|
+
confidence,
|
|
4575
|
+
readerName: candidate.readerName ?? reader.name
|
|
4576
|
+
};
|
|
4577
|
+
}
|
|
4578
|
+
function sortCandidates(candidates) {
|
|
4579
|
+
return [...candidates].sort((a, b) => {
|
|
4580
|
+
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
4581
|
+
return a.format.localeCompare(b.format);
|
|
4582
|
+
});
|
|
4583
|
+
}
|
|
4584
|
+
function collectWarnings(candidates) {
|
|
4585
|
+
return candidates.flatMap((candidate) => candidate.warnings ?? []);
|
|
4586
|
+
}
|
|
4587
|
+
function dedupeWarnings(warnings) {
|
|
4588
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4589
|
+
const out = [];
|
|
4590
|
+
for (const warning of warnings) {
|
|
4591
|
+
const key = [
|
|
4592
|
+
warning.code,
|
|
4593
|
+
warning.message,
|
|
4594
|
+
warning.severity ?? "",
|
|
4595
|
+
warning.sourceFile ?? "",
|
|
4596
|
+
warning.line ?? "",
|
|
4597
|
+
warning.field ?? ""
|
|
4598
|
+
].join("\0");
|
|
4599
|
+
if (seen.has(key)) continue;
|
|
4600
|
+
seen.add(key);
|
|
4601
|
+
out.push(warning);
|
|
4602
|
+
}
|
|
4603
|
+
return out;
|
|
4604
|
+
}
|
|
4605
|
+
function attachSingleSourceFile(warnings, resolved) {
|
|
4606
|
+
if (resolved.sourceFiles.length !== 1) return [...warnings];
|
|
4607
|
+
const [sourceFile] = resolved.sourceFiles;
|
|
4608
|
+
return warnings.map((warning) => ({
|
|
4609
|
+
...warning,
|
|
4610
|
+
sourceFile: warning.sourceFile ?? sourceFile
|
|
4611
|
+
}));
|
|
4612
|
+
}
|
|
4613
|
+
function findReaderByFormat(format, readers) {
|
|
4614
|
+
return readers.find((reader) => reader.format === format);
|
|
4615
|
+
}
|
|
4616
|
+
async function jsonlFilesInDirectory(dirPath) {
|
|
4617
|
+
const entries = await readdir(dirPath, { withFileTypes: true });
|
|
4618
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
|
|
4619
|
+
}
|
|
4620
|
+
async function resolveInput(input3) {
|
|
4621
|
+
const cached = resolvedInputCache.get(input3);
|
|
4622
|
+
if (cached) return cached;
|
|
4623
|
+
const promise = resolveInputUncached(input3);
|
|
4624
|
+
resolvedInputCache.set(input3, promise);
|
|
4625
|
+
return promise;
|
|
4626
|
+
}
|
|
4627
|
+
function assertInputWithinBounds(content, sourceFile) {
|
|
4628
|
+
const bytes = Buffer.byteLength(content, "utf8");
|
|
4629
|
+
if (bytes <= DEFAULT_MAX_TRACE_INPUT_BYTES) return;
|
|
4630
|
+
throw new TraceReadError("unsupported_format", "Trace input exceeds the local reader size limit.", [
|
|
4631
|
+
{
|
|
4632
|
+
code: "input_too_large",
|
|
4633
|
+
message: `Trace input is ${bytes} bytes; max is ${DEFAULT_MAX_TRACE_INPUT_BYTES} bytes.`,
|
|
4634
|
+
severity: "error",
|
|
4635
|
+
...sourceFile !== void 0 ? { sourceFile } : {}
|
|
4636
|
+
}
|
|
4637
|
+
]);
|
|
4638
|
+
}
|
|
4639
|
+
async function resolveInputUncached(input3) {
|
|
4640
|
+
if (input3.type === "string") {
|
|
4641
|
+
assertInputWithinBounds(input3.content);
|
|
4642
|
+
return { content: input3.content, sourceFiles: [] };
|
|
4643
|
+
}
|
|
4644
|
+
if (input3.type === "buffer") {
|
|
4645
|
+
const content = input3.content.toString("utf-8");
|
|
4646
|
+
assertInputWithinBounds(content);
|
|
4647
|
+
return { content, sourceFiles: [] };
|
|
4648
|
+
}
|
|
4649
|
+
if (input3.type === "file") {
|
|
4650
|
+
const content = await readFile(input3.path, "utf-8");
|
|
4651
|
+
assertInputWithinBounds(content, input3.path);
|
|
4652
|
+
return { content, sourceFiles: [input3.path] };
|
|
4653
|
+
}
|
|
4654
|
+
if (input3.type === "directory") {
|
|
4655
|
+
const files = await jsonlFilesInDirectory(input3.path);
|
|
4656
|
+
const parts = await Promise.all(
|
|
4657
|
+
files.map(async (file) => (await readFile(file, "utf-8")).trimEnd())
|
|
4658
|
+
);
|
|
4659
|
+
const content = parts.filter((part) => part.trim() !== "").join("\n");
|
|
4660
|
+
assertInputWithinBounds(content, input3.path);
|
|
4661
|
+
return {
|
|
4662
|
+
content,
|
|
4663
|
+
sourceFiles: files
|
|
4664
|
+
};
|
|
4665
|
+
}
|
|
4666
|
+
return void 0;
|
|
4667
|
+
}
|
|
4668
|
+
function detectJsonlFormat(content) {
|
|
4669
|
+
let saw01 = false;
|
|
4670
|
+
let saw02 = false;
|
|
4671
|
+
let validRows = 0;
|
|
4672
|
+
let invalidJsonRows = 0;
|
|
4673
|
+
let unknownSchemaRows = 0;
|
|
4674
|
+
let firstInvalidJsonLine;
|
|
4675
|
+
let firstUnknownSchemaLine;
|
|
4676
|
+
let lineNumber = 0;
|
|
4677
|
+
for (const line of content.split(/\r?\n/)) {
|
|
4678
|
+
lineNumber += 1;
|
|
4679
|
+
const trimmed = line.trim();
|
|
4680
|
+
if (trimmed === "") continue;
|
|
4681
|
+
let parsed;
|
|
4682
|
+
try {
|
|
4683
|
+
parsed = JSON.parse(trimmed);
|
|
4684
|
+
} catch {
|
|
4685
|
+
invalidJsonRows += 1;
|
|
4686
|
+
firstInvalidJsonLine ??= lineNumber;
|
|
4687
|
+
continue;
|
|
4688
|
+
}
|
|
4689
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "schemaVersion" in parsed) {
|
|
4690
|
+
const version2 = parsed.schemaVersion;
|
|
4691
|
+
if (version2 === "0.1") {
|
|
4692
|
+
saw01 = true;
|
|
4693
|
+
validRows += 1;
|
|
4694
|
+
continue;
|
|
4695
|
+
}
|
|
4696
|
+
if (version2 === "0.2") {
|
|
4697
|
+
saw02 = true;
|
|
4698
|
+
validRows += 1;
|
|
4699
|
+
continue;
|
|
4700
|
+
}
|
|
4701
|
+
}
|
|
4702
|
+
unknownSchemaRows += 1;
|
|
4703
|
+
firstUnknownSchemaLine ??= lineNumber;
|
|
4704
|
+
}
|
|
4705
|
+
const warnings = [];
|
|
4706
|
+
if (invalidJsonRows > 0) {
|
|
4707
|
+
warnings.push({
|
|
4708
|
+
code: "invalid_jsonl_rows",
|
|
4709
|
+
message: `Skipped ${invalidJsonRows} invalid JSONL row(s) during format detection.`,
|
|
4710
|
+
severity: "warning",
|
|
4711
|
+
...firstInvalidJsonLine !== void 0 ? { line: firstInvalidJsonLine } : {}
|
|
4712
|
+
});
|
|
4713
|
+
}
|
|
4714
|
+
if (unknownSchemaRows > 0) {
|
|
4715
|
+
warnings.push({
|
|
4716
|
+
code: "unknown_schema_rows",
|
|
4717
|
+
message: `Skipped ${unknownSchemaRows} row(s) with unknown schemaVersion during format detection.`,
|
|
4718
|
+
severity: "warning",
|
|
4719
|
+
...firstUnknownSchemaLine !== void 0 ? { line: firstUnknownSchemaLine } : {}
|
|
4720
|
+
});
|
|
4721
|
+
}
|
|
4722
|
+
let format = "empty";
|
|
4723
|
+
if (saw01 && saw02) format = "mixed";
|
|
4724
|
+
else if (saw01) format = "0.1";
|
|
4725
|
+
else if (saw02) format = "0.2";
|
|
4726
|
+
return { format, validRows, warnings };
|
|
4727
|
+
}
|
|
4728
|
+
function agentInspectFormatLabel(format) {
|
|
4729
|
+
switch (format) {
|
|
4730
|
+
case "0.1":
|
|
4731
|
+
return "agent-inspect-v0.1-jsonl";
|
|
4732
|
+
case "0.2":
|
|
4733
|
+
return "agent-inspect-v0.2-jsonl";
|
|
4734
|
+
case "mixed":
|
|
4735
|
+
return "agent-inspect-mixed-jsonl";
|
|
4736
|
+
default:
|
|
4737
|
+
return "agent-inspect-jsonl";
|
|
4738
|
+
}
|
|
4739
|
+
}
|
|
4740
|
+
function persistedEventsForParsedTrace(parsed) {
|
|
4741
|
+
if (parsed.format === "0.2" && parsed.persisted.length > 0) {
|
|
4742
|
+
return [...parsed.persisted];
|
|
4743
|
+
}
|
|
4744
|
+
if (parsed.format === "mixed" && parsed.rows.length > 0) {
|
|
4745
|
+
return parsed.rows.map((row, index) => {
|
|
4746
|
+
if (row.format === "0.2") return row.event;
|
|
4747
|
+
return traceEventToPersistedInspectEvent(row.event, {
|
|
4748
|
+
eventIndex: index,
|
|
4749
|
+
sourceName: "agent-inspect-jsonl-reader"
|
|
4750
|
+
});
|
|
4751
|
+
});
|
|
4752
|
+
}
|
|
4753
|
+
return traceEventsToPersistedInspectEvents(parsed.events, {
|
|
4754
|
+
sourceName: "agent-inspect-jsonl-reader"
|
|
4755
|
+
});
|
|
4756
|
+
}
|
|
4757
|
+
function isRecord13(value) {
|
|
4758
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4759
|
+
}
|
|
4760
|
+
function isNonEmptyString3(value) {
|
|
4761
|
+
return typeof value === "string" && value.trim() !== "";
|
|
4762
|
+
}
|
|
4763
|
+
function readStringField(record, keys) {
|
|
4764
|
+
for (const key of keys) {
|
|
4765
|
+
const value = record[key];
|
|
4766
|
+
if (isNonEmptyString3(value)) return value;
|
|
4767
|
+
}
|
|
4768
|
+
return void 0;
|
|
4769
|
+
}
|
|
4770
|
+
function readRecordField(record, key) {
|
|
4771
|
+
const value = record[key];
|
|
4772
|
+
return isRecord13(value) ? value : void 0;
|
|
4773
|
+
}
|
|
4774
|
+
function parseJsonDocument(content) {
|
|
4775
|
+
return JSON.parse(content);
|
|
4776
|
+
}
|
|
4777
|
+
function looksLikeOpenInferenceSpan(value) {
|
|
4778
|
+
if (!isRecord13(value)) return false;
|
|
4779
|
+
const attributes = readRecordField(value, "attributes");
|
|
4780
|
+
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);
|
|
4781
|
+
}
|
|
4782
|
+
function extractOpenInferenceDocument(root) {
|
|
4783
|
+
const warnings = [];
|
|
4784
|
+
const unsupportedFields = [];
|
|
4785
|
+
if (Array.isArray(root)) {
|
|
4786
|
+
const spans = root.filter(looksLikeOpenInferenceSpan);
|
|
4787
|
+
if (spans.length === 0) return void 0;
|
|
4788
|
+
if (spans.length !== root.length) {
|
|
4789
|
+
warnings.push({
|
|
4790
|
+
code: "openinference_skipped_items",
|
|
4791
|
+
message: "Skipped non-span item(s) in OpenInference span array.",
|
|
4792
|
+
severity: "warning"
|
|
4793
|
+
});
|
|
4794
|
+
}
|
|
4795
|
+
return {
|
|
4796
|
+
spans,
|
|
4797
|
+
confidence: 0.82,
|
|
4798
|
+
description: "OpenInference span array",
|
|
4799
|
+
warnings,
|
|
4800
|
+
unsupportedFields
|
|
4801
|
+
};
|
|
4802
|
+
}
|
|
4803
|
+
if (!isRecord13(root)) return void 0;
|
|
4804
|
+
const rootFormat = root.format;
|
|
4805
|
+
const rootCompatibility = root.compatibility;
|
|
4806
|
+
const version2 = typeof root.version === "string" && root.version.trim() !== "" ? root.version : void 0;
|
|
4807
|
+
if (Array.isArray(root.spans)) {
|
|
4808
|
+
const spans = root.spans.filter(looksLikeOpenInferenceSpan);
|
|
4809
|
+
if (spans.length === 0 && (rootFormat === "openinference" || rootCompatibility === "openinference-compatible")) {
|
|
4810
|
+
warnings.push({
|
|
4811
|
+
code: "openinference_no_valid_spans",
|
|
4812
|
+
message: "OpenInference document did not contain any valid spans.",
|
|
4813
|
+
severity: "error"
|
|
4814
|
+
});
|
|
4815
|
+
return {
|
|
4816
|
+
spans,
|
|
4817
|
+
confidence: 0.7,
|
|
4818
|
+
description: "Malformed OpenInference document",
|
|
4819
|
+
version: version2,
|
|
4820
|
+
warnings,
|
|
4821
|
+
unsupportedFields
|
|
4822
|
+
};
|
|
4823
|
+
}
|
|
4824
|
+
if (spans.length === 0) return void 0;
|
|
4825
|
+
if (spans.length !== root.spans.length) {
|
|
4826
|
+
warnings.push({
|
|
4827
|
+
code: "openinference_skipped_spans",
|
|
4828
|
+
message: "Skipped invalid OpenInference span item(s).",
|
|
4829
|
+
severity: "warning"
|
|
4830
|
+
});
|
|
4831
|
+
}
|
|
4832
|
+
return {
|
|
4833
|
+
spans,
|
|
4834
|
+
confidence: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? 0.9 : 0.84,
|
|
4835
|
+
description: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? "OpenInference document" : "OpenInference spans document",
|
|
4836
|
+
version: version2,
|
|
4837
|
+
warnings,
|
|
4838
|
+
unsupportedFields
|
|
4839
|
+
};
|
|
4840
|
+
}
|
|
4841
|
+
if (Array.isArray(root.data)) {
|
|
4842
|
+
const spans = root.data.filter(looksLikeOpenInferenceSpan);
|
|
4843
|
+
if (spans.length === 0) return void 0;
|
|
4844
|
+
if (spans.length !== root.data.length) {
|
|
4845
|
+
warnings.push({
|
|
4846
|
+
code: "openinference_skipped_data_items",
|
|
4847
|
+
message: "Skipped non-span item(s) in OpenInference data array.",
|
|
4848
|
+
severity: "warning"
|
|
4849
|
+
});
|
|
4850
|
+
}
|
|
4851
|
+
return {
|
|
4852
|
+
spans,
|
|
4853
|
+
confidence: 0.8,
|
|
4854
|
+
description: "OpenInference data document",
|
|
4855
|
+
version: version2,
|
|
4856
|
+
warnings,
|
|
4857
|
+
unsupportedFields
|
|
4858
|
+
};
|
|
4859
|
+
}
|
|
4860
|
+
if (looksLikeOpenInferenceSpan(root)) {
|
|
4861
|
+
return {
|
|
4862
|
+
spans: [root],
|
|
4863
|
+
confidence: 0.76,
|
|
4864
|
+
description: "OpenInference single span",
|
|
4865
|
+
version: version2,
|
|
4866
|
+
warnings,
|
|
4867
|
+
unsupportedFields
|
|
4868
|
+
};
|
|
4869
|
+
}
|
|
4870
|
+
if (rootFormat === "openinference" || rootCompatibility === "openinference-compatible") {
|
|
4871
|
+
warnings.push({
|
|
4872
|
+
code: "openinference_missing_spans",
|
|
4873
|
+
message: "OpenInference document is missing a spans array.",
|
|
4874
|
+
severity: "error"
|
|
4875
|
+
});
|
|
4876
|
+
return {
|
|
4877
|
+
spans: [],
|
|
4878
|
+
confidence: 0.7,
|
|
4879
|
+
description: "Malformed OpenInference document",
|
|
4880
|
+
version: version2,
|
|
4881
|
+
warnings,
|
|
4882
|
+
unsupportedFields
|
|
4883
|
+
};
|
|
4884
|
+
}
|
|
4885
|
+
return void 0;
|
|
4886
|
+
}
|
|
4887
|
+
function parseUnixNanoToIso(value) {
|
|
4888
|
+
if (typeof value === "bigint" && value >= 0n) {
|
|
4889
|
+
return new Date(Number(value / 1000000n)).toISOString();
|
|
4890
|
+
}
|
|
4891
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
4892
|
+
return new Date(Math.floor(value / 1e6)).toISOString();
|
|
4893
|
+
}
|
|
4894
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
4895
|
+
return new Date(Number(BigInt(value) / 1000000n)).toISOString();
|
|
4896
|
+
}
|
|
4897
|
+
return void 0;
|
|
4898
|
+
}
|
|
4899
|
+
function parseIsoTime(value) {
|
|
4900
|
+
if (!isNonEmptyString3(value)) return void 0;
|
|
4901
|
+
const ms = Date.parse(value);
|
|
4902
|
+
if (!Number.isFinite(ms)) return void 0;
|
|
4903
|
+
return new Date(ms).toISOString();
|
|
4904
|
+
}
|
|
4905
|
+
function readOpenInferenceTimestamp(span, nanoKeys, isoKeys) {
|
|
4906
|
+
for (const key of nanoKeys) {
|
|
4907
|
+
const iso = parseUnixNanoToIso(span[key]);
|
|
4908
|
+
if (iso !== void 0) return iso;
|
|
4909
|
+
}
|
|
4910
|
+
for (const key of isoKeys) {
|
|
4911
|
+
const iso = parseIsoTime(span[key]);
|
|
4912
|
+
if (iso !== void 0) return iso;
|
|
4913
|
+
}
|
|
4914
|
+
return void 0;
|
|
4915
|
+
}
|
|
4916
|
+
function durationBetweenIso(startedAt, endedAt) {
|
|
4917
|
+
if (startedAt === void 0 || endedAt === void 0) return void 0;
|
|
4918
|
+
const startMs = Date.parse(startedAt);
|
|
4919
|
+
const endMs = Date.parse(endedAt);
|
|
4920
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs < startMs) {
|
|
4921
|
+
return void 0;
|
|
4922
|
+
}
|
|
4923
|
+
return endMs - startMs;
|
|
4924
|
+
}
|
|
4925
|
+
function isSensitiveOpenInferenceAttribute(key) {
|
|
4926
|
+
return OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS.some(
|
|
4927
|
+
(sensitiveKey) => key === sensitiveKey || key.startsWith(`${sensitiveKey}.`) || key.endsWith(".message.content") || key.endsWith(".document.content")
|
|
4928
|
+
);
|
|
4929
|
+
}
|
|
4930
|
+
function summarizeAttributeValue(value) {
|
|
4931
|
+
if (typeof value === "string") {
|
|
4932
|
+
return { type: "string", length: value.length };
|
|
4933
|
+
}
|
|
4934
|
+
if (typeof value === "number") {
|
|
4935
|
+
return { type: "number", finite: Number.isFinite(value) };
|
|
4936
|
+
}
|
|
4937
|
+
if (typeof value === "boolean") {
|
|
4938
|
+
return { type: "boolean" };
|
|
4939
|
+
}
|
|
4940
|
+
if (Array.isArray(value)) {
|
|
4941
|
+
return { type: "array", length: value.length };
|
|
4942
|
+
}
|
|
4943
|
+
if (isRecord13(value)) {
|
|
4944
|
+
return { type: "object", keyCount: Object.keys(value).length };
|
|
4945
|
+
}
|
|
4946
|
+
if (value === null) {
|
|
4947
|
+
return { type: "null" };
|
|
4948
|
+
}
|
|
4949
|
+
return { type: typeof value };
|
|
4950
|
+
}
|
|
4951
|
+
function sanitizeOpenInferenceAttributes(attributes, pathPrefix) {
|
|
4952
|
+
const out = {};
|
|
4953
|
+
const warnings = [];
|
|
4954
|
+
const unsupportedFields = [];
|
|
4955
|
+
const summarizedKeys = [];
|
|
4956
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
4957
|
+
if (isSensitiveOpenInferenceAttribute(key)) {
|
|
4958
|
+
summarizedKeys.push(key);
|
|
4959
|
+
out[`${key}.summary`] = summarizeAttributeValue(value);
|
|
4960
|
+
unsupportedFields.push(`${pathPrefix}.attributes.${key}`);
|
|
4961
|
+
continue;
|
|
4962
|
+
}
|
|
4963
|
+
out[key] = value;
|
|
4964
|
+
}
|
|
4965
|
+
if (summarizedKeys.length > 0) {
|
|
4966
|
+
out["openinference.summarized_attributes"] = summarizedKeys;
|
|
4967
|
+
warnings.push({
|
|
4968
|
+
code: "openinference_sensitive_attribute_summarized",
|
|
4969
|
+
message: "OpenInference prompt/output/document attribute(s) were summarized instead of copied verbatim.",
|
|
4970
|
+
severity: "warning"
|
|
4971
|
+
});
|
|
4972
|
+
}
|
|
4973
|
+
return { attributes: out, warnings, unsupportedFields };
|
|
4974
|
+
}
|
|
4975
|
+
function mapOpenInferenceKind(span, attributes, pathPrefix) {
|
|
4976
|
+
const warnings = [];
|
|
4977
|
+
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
4978
|
+
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
|
|
4979
|
+
return { kind: agentInspectKind, warnings };
|
|
4980
|
+
}
|
|
4981
|
+
const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
|
|
4982
|
+
const normalized = rawKind?.toUpperCase();
|
|
4983
|
+
switch (normalized) {
|
|
4984
|
+
case "LLM":
|
|
4985
|
+
return { kind: "LLM", warnings };
|
|
4986
|
+
case "TOOL":
|
|
4987
|
+
return { kind: "TOOL", warnings };
|
|
4988
|
+
case "CHAIN":
|
|
4989
|
+
return { kind: "CHAIN", warnings };
|
|
4990
|
+
case "RETRIEVER":
|
|
4991
|
+
return { kind: "RETRIEVER", warnings };
|
|
4992
|
+
case "AGENT":
|
|
4993
|
+
return { kind: "AGENT", warnings };
|
|
4994
|
+
case "EMBEDDING":
|
|
4995
|
+
warnings.push({
|
|
4996
|
+
code: "openinference_kind_semantic_loss",
|
|
4997
|
+
message: "OpenInference EMBEDDING span kind mapped to AgentInspect LLM.",
|
|
4998
|
+
severity: "warning",
|
|
4999
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
5000
|
+
});
|
|
5001
|
+
return { kind: "LLM", warnings };
|
|
5002
|
+
case "RERANKER":
|
|
5003
|
+
warnings.push({
|
|
5004
|
+
code: "openinference_kind_semantic_loss",
|
|
5005
|
+
message: "OpenInference RERANKER span kind mapped to AgentInspect RETRIEVER.",
|
|
5006
|
+
severity: "warning",
|
|
5007
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
5008
|
+
});
|
|
5009
|
+
return { kind: "RETRIEVER", warnings };
|
|
5010
|
+
case "UNKNOWN":
|
|
5011
|
+
case void 0:
|
|
5012
|
+
warnings.push({
|
|
5013
|
+
code: "openinference_kind_unknown",
|
|
5014
|
+
message: "OpenInference span kind was missing or unknown; mapped to AgentInspect LOGIC.",
|
|
5015
|
+
severity: "warning",
|
|
5016
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
5017
|
+
});
|
|
5018
|
+
return { kind: "LOGIC", warnings };
|
|
5019
|
+
default:
|
|
5020
|
+
warnings.push({
|
|
5021
|
+
code: "openinference_kind_unsupported",
|
|
5022
|
+
message: `Unsupported OpenInference span kind "${rawKind}" mapped to AgentInspect LOGIC.`,
|
|
5023
|
+
severity: "warning",
|
|
5024
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
5025
|
+
});
|
|
5026
|
+
return { kind: "LOGIC", warnings };
|
|
5027
|
+
}
|
|
5028
|
+
}
|
|
5029
|
+
function mapOpenInferenceStatus(status) {
|
|
5030
|
+
if (!isRecord13(status)) return void 0;
|
|
5031
|
+
const rawCode = status.code;
|
|
5032
|
+
if (typeof rawCode !== "string") return void 0;
|
|
5033
|
+
switch (rawCode.toUpperCase()) {
|
|
5034
|
+
case "OK":
|
|
5035
|
+
return "ok";
|
|
5036
|
+
case "ERROR":
|
|
5037
|
+
return "error";
|
|
5038
|
+
case "UNSET":
|
|
5039
|
+
return "unknown";
|
|
5040
|
+
default:
|
|
5041
|
+
return "unknown";
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
function readOpenInferenceTokenUsage(attributes) {
|
|
5045
|
+
const prompt = attributes["llm.token_count.prompt"];
|
|
5046
|
+
const completion = attributes["llm.token_count.completion"];
|
|
5047
|
+
const total = attributes["llm.token_count.total"];
|
|
5048
|
+
const cached = attributes["llm.token_count.prompt_details.cache_read"];
|
|
5049
|
+
const usage = {};
|
|
5050
|
+
if (typeof prompt === "number" && Number.isFinite(prompt) && prompt >= 0) {
|
|
5051
|
+
usage.input = prompt;
|
|
5052
|
+
}
|
|
5053
|
+
if (typeof completion === "number" && Number.isFinite(completion) && completion >= 0) {
|
|
5054
|
+
usage.output = completion;
|
|
5055
|
+
}
|
|
5056
|
+
if (typeof total === "number" && Number.isFinite(total) && total >= 0) {
|
|
5057
|
+
usage.total = total;
|
|
5058
|
+
}
|
|
5059
|
+
if (typeof cached === "number" && Number.isFinite(cached) && cached >= 0) {
|
|
5060
|
+
usage.cached = cached;
|
|
5061
|
+
}
|
|
5062
|
+
if (usage.total === void 0 && usage.input !== void 0 && usage.output !== void 0) {
|
|
5063
|
+
usage.total = usage.input + usage.output;
|
|
5064
|
+
}
|
|
5065
|
+
return Object.keys(usage).length > 0 ? usage : void 0;
|
|
5066
|
+
}
|
|
5067
|
+
function readOpenInferenceConfidence(attributes) {
|
|
5068
|
+
const confidence = attributes["agent_inspect.confidence"];
|
|
5069
|
+
if (confidence === "explicit" || confidence === "correlated" || confidence === "heuristic" || confidence === "unknown") {
|
|
5070
|
+
return confidence;
|
|
5071
|
+
}
|
|
5072
|
+
return "correlated";
|
|
5073
|
+
}
|
|
5074
|
+
function mapOpenInferenceSpan(span, index, version2) {
|
|
5075
|
+
const pathPrefix = `spans[${index}]`;
|
|
5076
|
+
const warnings = [];
|
|
5077
|
+
const unsupportedFields = [];
|
|
5078
|
+
const rawAttributes = readRecordField(span, "attributes") ?? {};
|
|
5079
|
+
const sanitized = sanitizeOpenInferenceAttributes(rawAttributes, pathPrefix);
|
|
5080
|
+
warnings.push(...sanitized.warnings);
|
|
5081
|
+
unsupportedFields.push(...sanitized.unsupportedFields);
|
|
5082
|
+
const attributes = { ...sanitized.attributes };
|
|
5083
|
+
for (const [key, value] of Object.entries(span)) {
|
|
5084
|
+
if (OPENINFERENCE_SPAN_KEYS.has(key)) continue;
|
|
5085
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
5086
|
+
if (value === null || typeof value !== "object") {
|
|
5087
|
+
attributes[`openinference.${key}`] = value;
|
|
5088
|
+
} else {
|
|
5089
|
+
attributes[`openinference.${key}.summary`] = summarizeAttributeValue(value);
|
|
5090
|
+
warnings.push({
|
|
5091
|
+
code: "openinference_unsupported_field_summarized",
|
|
5092
|
+
message: `Unsupported OpenInference span field "${key}" was summarized.`,
|
|
5093
|
+
severity: "warning",
|
|
5094
|
+
field: `${pathPrefix}.${key}`
|
|
5095
|
+
});
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
const traceId = readStringField(span, ["trace_id", "traceId"]) ?? `trace-${index}`;
|
|
5099
|
+
const spanId = readStringField(span, ["span_id", "spanId"]) ?? `span-${index}`;
|
|
5100
|
+
const parentSpanId = readStringField(span, ["parent_span_id", "parentSpanId"]);
|
|
5101
|
+
const name = readStringField(span, ["name"]) ?? spanId;
|
|
5102
|
+
const startedAt = readOpenInferenceTimestamp(
|
|
5103
|
+
span,
|
|
5104
|
+
["start_time_unix_nano", "startTimeUnixNano"],
|
|
5105
|
+
["start_time", "startTime"]
|
|
5106
|
+
);
|
|
5107
|
+
const endedAt = readOpenInferenceTimestamp(
|
|
5108
|
+
span,
|
|
5109
|
+
["end_time_unix_nano", "endTimeUnixNano"],
|
|
5110
|
+
["end_time", "endTime"]
|
|
5111
|
+
);
|
|
5112
|
+
const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
|
|
5113
|
+
if (startedAt === void 0) {
|
|
5114
|
+
warnings.push({
|
|
5115
|
+
code: "openinference_missing_start_time",
|
|
5116
|
+
message: "OpenInference span is missing a valid start time; using Unix epoch.",
|
|
5117
|
+
severity: "warning",
|
|
5118
|
+
field: `${pathPrefix}.start_time_unix_nano`
|
|
5119
|
+
});
|
|
5120
|
+
unsupportedFields.push(`${pathPrefix}.start_time_unix_nano`);
|
|
5121
|
+
}
|
|
5122
|
+
const { kind, warnings: kindWarnings } = mapOpenInferenceKind(
|
|
5123
|
+
span,
|
|
5124
|
+
rawAttributes,
|
|
5125
|
+
pathPrefix
|
|
5126
|
+
);
|
|
5127
|
+
warnings.push(...kindWarnings);
|
|
5128
|
+
const status = mapOpenInferenceStatus(span.status);
|
|
5129
|
+
const tokenUsage = readOpenInferenceTokenUsage(rawAttributes);
|
|
5130
|
+
const errorMessage = isRecord13(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
5131
|
+
const event = {
|
|
5132
|
+
schemaVersion: "0.2",
|
|
5133
|
+
eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
|
|
5134
|
+
runId: typeof rawAttributes["agent_inspect.run_id"] === "string" ? rawAttributes["agent_inspect.run_id"] : traceId,
|
|
5135
|
+
kind,
|
|
5136
|
+
name,
|
|
5137
|
+
timestamp,
|
|
5138
|
+
confidence: readOpenInferenceConfidence(rawAttributes),
|
|
5139
|
+
source: {
|
|
5140
|
+
type: "otel",
|
|
5141
|
+
name: "openinference",
|
|
5142
|
+
...version2 !== void 0 ? { version: version2 } : {}
|
|
5143
|
+
},
|
|
5144
|
+
attributes,
|
|
5145
|
+
trace: {
|
|
5146
|
+
traceId,
|
|
5147
|
+
spanId,
|
|
5148
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
5149
|
+
}
|
|
5150
|
+
};
|
|
5151
|
+
if (status !== void 0) {
|
|
5152
|
+
event.status = status;
|
|
5153
|
+
}
|
|
5154
|
+
if (startedAt !== void 0) {
|
|
5155
|
+
event.startedAt = startedAt;
|
|
5156
|
+
}
|
|
5157
|
+
if (endedAt !== void 0) {
|
|
5158
|
+
event.endedAt = endedAt;
|
|
5159
|
+
}
|
|
5160
|
+
const durationMs = durationBetweenIso(startedAt, endedAt);
|
|
5161
|
+
if (durationMs !== void 0) {
|
|
5162
|
+
event.durationMs = durationMs;
|
|
5163
|
+
}
|
|
5164
|
+
if (tokenUsage !== void 0) {
|
|
5165
|
+
event.tokenUsage = tokenUsage;
|
|
5166
|
+
}
|
|
5167
|
+
if (status === "error") {
|
|
5168
|
+
event.error = {
|
|
5169
|
+
message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OpenInference span error"
|
|
5170
|
+
};
|
|
5171
|
+
}
|
|
5172
|
+
return {
|
|
5173
|
+
event,
|
|
5174
|
+
warnings,
|
|
5175
|
+
unsupportedFields,
|
|
5176
|
+
spanId,
|
|
5177
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
5178
|
+
};
|
|
5179
|
+
}
|
|
5180
|
+
function mapOpenInferenceEvents(document) {
|
|
5181
|
+
const mapped = document.spans.map(
|
|
5182
|
+
(span, index) => mapOpenInferenceSpan(span, index, document.version)
|
|
5183
|
+
);
|
|
5184
|
+
const spanIdToEventId = new Map(
|
|
5185
|
+
mapped.map((span) => [span.spanId, span.event.eventId])
|
|
5186
|
+
);
|
|
5187
|
+
for (const span of mapped) {
|
|
5188
|
+
if (span.parentSpanId === void 0) continue;
|
|
5189
|
+
span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
|
|
5190
|
+
}
|
|
5191
|
+
return {
|
|
5192
|
+
events: mapped.map((span) => span.event),
|
|
5193
|
+
warnings: mapped.flatMap((span) => span.warnings),
|
|
5194
|
+
unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
|
|
5195
|
+
};
|
|
5196
|
+
}
|
|
5197
|
+
var openInferenceJsonReader = {
|
|
5198
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
5199
|
+
name: "OpenInference JSON",
|
|
5200
|
+
async detect(input3) {
|
|
5201
|
+
const resolved = await resolveInput(input3);
|
|
5202
|
+
if (!resolved) return void 0;
|
|
5203
|
+
let parsed;
|
|
5204
|
+
try {
|
|
5205
|
+
parsed = parseJsonDocument(resolved.content);
|
|
5206
|
+
} catch {
|
|
5207
|
+
return void 0;
|
|
5208
|
+
}
|
|
5209
|
+
const document = extractOpenInferenceDocument(parsed);
|
|
5210
|
+
if (!document) return void 0;
|
|
5211
|
+
return {
|
|
5212
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
5213
|
+
confidence: document.confidence,
|
|
5214
|
+
readerName: "OpenInference JSON",
|
|
5215
|
+
description: document.description,
|
|
5216
|
+
warnings: attachSingleSourceFile(document.warnings, resolved)
|
|
5217
|
+
};
|
|
5218
|
+
},
|
|
5219
|
+
async read(input3) {
|
|
5220
|
+
const resolved = await resolveInput(input3);
|
|
5221
|
+
if (!resolved) {
|
|
5222
|
+
throw new TraceReadError(
|
|
5223
|
+
"unsupported_format",
|
|
5224
|
+
"OpenInference JSON reader requires file, string, or buffer input."
|
|
5225
|
+
);
|
|
5226
|
+
}
|
|
5227
|
+
let parsed;
|
|
5228
|
+
try {
|
|
5229
|
+
parsed = parseJsonDocument(resolved.content);
|
|
5230
|
+
} catch {
|
|
5231
|
+
throw new TraceReadError("unsupported_format", "OpenInference JSON input is not valid JSON.", [
|
|
5232
|
+
{
|
|
5233
|
+
code: "openinference_invalid_json",
|
|
5234
|
+
message: "OpenInference JSON reader could not parse the input as JSON.",
|
|
5235
|
+
severity: "error"
|
|
5236
|
+
}
|
|
5237
|
+
]);
|
|
5238
|
+
}
|
|
5239
|
+
const document = extractOpenInferenceDocument(parsed);
|
|
5240
|
+
if (!document || document.spans.length === 0) {
|
|
5241
|
+
throw new TraceReadError(
|
|
5242
|
+
"unsupported_format",
|
|
5243
|
+
"No valid OpenInference spans found.",
|
|
5244
|
+
attachSingleSourceFile(
|
|
5245
|
+
document?.warnings ?? [
|
|
5246
|
+
{
|
|
5247
|
+
code: "openinference_no_valid_spans",
|
|
5248
|
+
message: "OpenInference JSON input did not contain valid spans.",
|
|
5249
|
+
severity: "error"
|
|
5250
|
+
}
|
|
5251
|
+
],
|
|
5252
|
+
resolved
|
|
5253
|
+
)
|
|
5254
|
+
);
|
|
5255
|
+
}
|
|
5256
|
+
const mapped = mapOpenInferenceEvents(document);
|
|
5257
|
+
const warnings = attachSingleSourceFile(
|
|
5258
|
+
[...document.warnings, ...mapped.warnings],
|
|
5259
|
+
resolved
|
|
5260
|
+
);
|
|
5261
|
+
const unsupportedFields = [
|
|
5262
|
+
...document.unsupportedFields,
|
|
5263
|
+
...mapped.unsupportedFields
|
|
5264
|
+
].sort((a, b) => a.localeCompare(b));
|
|
5265
|
+
return {
|
|
5266
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
5267
|
+
events: mapped.events,
|
|
5268
|
+
runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
|
|
5269
|
+
warnings,
|
|
5270
|
+
unsupportedFields,
|
|
5271
|
+
sourceFiles: resolved.sourceFiles
|
|
5272
|
+
};
|
|
5273
|
+
}
|
|
5274
|
+
};
|
|
5275
|
+
function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
|
|
5276
|
+
if (!isRecord13(value)) {
|
|
5277
|
+
unsupportedFields.push(field);
|
|
5278
|
+
warnings.push({
|
|
5279
|
+
code: "otlp_attribute_value_invalid",
|
|
5280
|
+
message: "OTLP attribute value was not an AnyValue object.",
|
|
5281
|
+
severity: "warning",
|
|
5282
|
+
field
|
|
5283
|
+
});
|
|
5284
|
+
return void 0;
|
|
5285
|
+
}
|
|
5286
|
+
if (typeof value.stringValue === "string") return value.stringValue;
|
|
5287
|
+
if (typeof value.boolValue === "boolean") return value.boolValue;
|
|
5288
|
+
if (typeof value.intValue === "number" && Number.isFinite(value.intValue)) {
|
|
5289
|
+
return value.intValue;
|
|
5290
|
+
}
|
|
5291
|
+
if (typeof value.intValue === "string" && value.intValue.trim() !== "") {
|
|
5292
|
+
const n = Number(value.intValue);
|
|
5293
|
+
if (Number.isFinite(n)) return n;
|
|
5294
|
+
}
|
|
5295
|
+
if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
|
|
5296
|
+
return value.doubleValue;
|
|
5297
|
+
}
|
|
5298
|
+
if (isRecord13(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
|
|
5299
|
+
return value.arrayValue.values.map(
|
|
5300
|
+
(item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
|
|
5301
|
+
);
|
|
5302
|
+
}
|
|
5303
|
+
if (isRecord13(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
|
|
5304
|
+
const out = {};
|
|
5305
|
+
for (const [index, item] of value.kvlistValue.values.entries()) {
|
|
5306
|
+
if (!isRecord13(item) || typeof item.key !== "string") {
|
|
5307
|
+
unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
|
|
5308
|
+
continue;
|
|
5309
|
+
}
|
|
5310
|
+
out[item.key] = parseOtlpAnyValue(
|
|
5311
|
+
item.value,
|
|
5312
|
+
`${field}.kvlistValue.values[${index}].value`,
|
|
5313
|
+
warnings,
|
|
5314
|
+
unsupportedFields
|
|
5315
|
+
);
|
|
5316
|
+
}
|
|
5317
|
+
return out;
|
|
5318
|
+
}
|
|
5319
|
+
if (typeof value.bytesValue === "string") {
|
|
5320
|
+
unsupportedFields.push(field);
|
|
5321
|
+
warnings.push({
|
|
5322
|
+
code: "otlp_bytes_value_summarized",
|
|
5323
|
+
message: "OTLP bytesValue attribute was summarized instead of decoded.",
|
|
5324
|
+
severity: "warning",
|
|
5325
|
+
field
|
|
5326
|
+
});
|
|
5327
|
+
return { type: "bytes", length: value.bytesValue.length };
|
|
5328
|
+
}
|
|
5329
|
+
unsupportedFields.push(field);
|
|
5330
|
+
warnings.push({
|
|
5331
|
+
code: "otlp_attribute_value_unsupported",
|
|
5332
|
+
message: "OTLP attribute value used an unsupported AnyValue shape.",
|
|
5333
|
+
severity: "warning",
|
|
5334
|
+
field
|
|
5335
|
+
});
|
|
5336
|
+
return void 0;
|
|
5337
|
+
}
|
|
5338
|
+
function parseOtlpAttributes(value, pathPrefix) {
|
|
5339
|
+
const attributes = {};
|
|
5340
|
+
const warnings = [];
|
|
5341
|
+
const unsupportedFields = [];
|
|
5342
|
+
if (value === void 0) {
|
|
5343
|
+
return { attributes, warnings, unsupportedFields };
|
|
5344
|
+
}
|
|
5345
|
+
if (!Array.isArray(value)) {
|
|
5346
|
+
unsupportedFields.push(pathPrefix);
|
|
5347
|
+
warnings.push({
|
|
5348
|
+
code: "otlp_attributes_invalid",
|
|
5349
|
+
message: "OTLP attributes field was not an array.",
|
|
5350
|
+
severity: "warning",
|
|
5351
|
+
field: pathPrefix
|
|
5352
|
+
});
|
|
5353
|
+
return { attributes, warnings, unsupportedFields };
|
|
5354
|
+
}
|
|
5355
|
+
for (const [index, item] of value.entries()) {
|
|
5356
|
+
const field = `${pathPrefix}[${index}]`;
|
|
5357
|
+
if (!isRecord13(item) || typeof item.key !== "string") {
|
|
5358
|
+
unsupportedFields.push(field);
|
|
5359
|
+
warnings.push({
|
|
5360
|
+
code: "otlp_attribute_invalid",
|
|
5361
|
+
message: "Skipped OTLP attribute without a string key.",
|
|
5362
|
+
severity: "warning",
|
|
5363
|
+
field
|
|
5364
|
+
});
|
|
5365
|
+
continue;
|
|
5366
|
+
}
|
|
5367
|
+
const parsed = parseOtlpAnyValue(
|
|
5368
|
+
item.value,
|
|
5369
|
+
`${field}.value`,
|
|
5370
|
+
warnings,
|
|
5371
|
+
unsupportedFields
|
|
5372
|
+
);
|
|
5373
|
+
if (parsed !== void 0) {
|
|
5374
|
+
attributes[item.key] = parsed;
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
return { attributes, warnings, unsupportedFields };
|
|
5378
|
+
}
|
|
5379
|
+
function looksLikeOtlpSpan(value) {
|
|
5380
|
+
return isRecord13(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
|
|
5381
|
+
}
|
|
5382
|
+
function extractOtlpDocument(root) {
|
|
5383
|
+
if (!isRecord13(root) || !Array.isArray(root.resourceSpans)) return void 0;
|
|
5384
|
+
const spans = [];
|
|
5385
|
+
const warnings = [];
|
|
5386
|
+
const unsupportedFields = [];
|
|
5387
|
+
for (const [resourceIndex, resourceSpan] of root.resourceSpans.entries()) {
|
|
5388
|
+
const resourcePath = `resourceSpans[${resourceIndex}]`;
|
|
5389
|
+
if (!isRecord13(resourceSpan)) {
|
|
5390
|
+
unsupportedFields.push(resourcePath);
|
|
5391
|
+
continue;
|
|
5392
|
+
}
|
|
5393
|
+
const resource = readRecordField(resourceSpan, "resource");
|
|
5394
|
+
const resourceParsed = parseOtlpAttributes(
|
|
5395
|
+
resource?.attributes,
|
|
5396
|
+
`${resourcePath}.resource.attributes`
|
|
5397
|
+
);
|
|
5398
|
+
warnings.push(...resourceParsed.warnings);
|
|
5399
|
+
unsupportedFields.push(...resourceParsed.unsupportedFields);
|
|
5400
|
+
if (!Array.isArray(resourceSpan.scopeSpans)) {
|
|
5401
|
+
unsupportedFields.push(`${resourcePath}.scopeSpans`);
|
|
5402
|
+
warnings.push({
|
|
5403
|
+
code: "otlp_scope_spans_missing",
|
|
5404
|
+
message: "OTLP resourceSpans entry did not contain a scopeSpans array.",
|
|
5405
|
+
severity: "warning",
|
|
5406
|
+
field: `${resourcePath}.scopeSpans`
|
|
5407
|
+
});
|
|
5408
|
+
continue;
|
|
5409
|
+
}
|
|
5410
|
+
for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
|
|
5411
|
+
const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
|
|
5412
|
+
if (!isRecord13(scopeSpan)) {
|
|
5413
|
+
unsupportedFields.push(scopePath);
|
|
5414
|
+
continue;
|
|
5415
|
+
}
|
|
5416
|
+
const scope = readRecordField(scopeSpan, "scope");
|
|
5417
|
+
const scopeParsed = parseOtlpAttributes(
|
|
5418
|
+
scope?.attributes,
|
|
5419
|
+
`${scopePath}.scope.attributes`
|
|
5420
|
+
);
|
|
5421
|
+
warnings.push(...scopeParsed.warnings);
|
|
5422
|
+
unsupportedFields.push(...scopeParsed.unsupportedFields);
|
|
5423
|
+
if (!Array.isArray(scopeSpan.spans)) {
|
|
5424
|
+
unsupportedFields.push(`${scopePath}.spans`);
|
|
5425
|
+
warnings.push({
|
|
5426
|
+
code: "otlp_spans_missing",
|
|
5427
|
+
message: "OTLP scopeSpans entry did not contain a spans array.",
|
|
5428
|
+
severity: "warning",
|
|
5429
|
+
field: `${scopePath}.spans`
|
|
5430
|
+
});
|
|
5431
|
+
continue;
|
|
5432
|
+
}
|
|
5433
|
+
for (const [spanIndex, span] of scopeSpan.spans.entries()) {
|
|
5434
|
+
const spanPath = `${scopePath}.spans[${spanIndex}]`;
|
|
5435
|
+
if (!looksLikeOtlpSpan(span)) {
|
|
5436
|
+
unsupportedFields.push(spanPath);
|
|
5437
|
+
warnings.push({
|
|
5438
|
+
code: "otlp_invalid_span",
|
|
5439
|
+
message: "Skipped OTLP span without required traceId, spanId, or name.",
|
|
5440
|
+
severity: "warning",
|
|
5441
|
+
field: spanPath
|
|
5442
|
+
});
|
|
5443
|
+
continue;
|
|
5444
|
+
}
|
|
5445
|
+
spans.push({
|
|
5446
|
+
span,
|
|
5447
|
+
resourceAttributes: resourceParsed.attributes,
|
|
5448
|
+
scopeAttributes: scopeParsed.attributes,
|
|
5449
|
+
scopeName: readStringField(scope ?? {}, ["name"]),
|
|
5450
|
+
scopeVersion: readStringField(scope ?? {}, ["version"]),
|
|
5451
|
+
pathPrefix: spanPath
|
|
5452
|
+
});
|
|
5453
|
+
}
|
|
5454
|
+
}
|
|
5455
|
+
}
|
|
5456
|
+
if (spans.length === 0) {
|
|
5457
|
+
warnings.push({
|
|
5458
|
+
code: "otlp_no_valid_spans",
|
|
5459
|
+
message: "OTLP JSON payload did not contain any valid spans.",
|
|
5460
|
+
severity: "error"
|
|
5461
|
+
});
|
|
5462
|
+
return {
|
|
5463
|
+
spans,
|
|
5464
|
+
confidence: 0.7,
|
|
5465
|
+
description: "Malformed OTLP JSON trace payload",
|
|
5466
|
+
warnings,
|
|
5467
|
+
unsupportedFields
|
|
5468
|
+
};
|
|
5469
|
+
}
|
|
5470
|
+
return {
|
|
5471
|
+
spans,
|
|
5472
|
+
confidence: 0.93,
|
|
5473
|
+
description: "OTLP JSON trace payload",
|
|
5474
|
+
warnings,
|
|
5475
|
+
unsupportedFields
|
|
5476
|
+
};
|
|
5477
|
+
}
|
|
5478
|
+
function mapOtlpStatus(status) {
|
|
5479
|
+
if (!isRecord13(status)) return void 0;
|
|
5480
|
+
const rawCode = status.code;
|
|
5481
|
+
if (typeof rawCode !== "string") return void 0;
|
|
5482
|
+
switch (rawCode.toUpperCase()) {
|
|
5483
|
+
case "STATUS_CODE_OK":
|
|
5484
|
+
case "OK":
|
|
5485
|
+
return "ok";
|
|
5486
|
+
case "STATUS_CODE_ERROR":
|
|
5487
|
+
case "ERROR":
|
|
5488
|
+
return "error";
|
|
5489
|
+
case "STATUS_CODE_UNSET":
|
|
5490
|
+
case "UNSET":
|
|
5491
|
+
return "unknown";
|
|
5492
|
+
default:
|
|
5493
|
+
return "unknown";
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5496
|
+
function readOtlpKind(attributes, pathPrefix) {
|
|
5497
|
+
const warnings = [];
|
|
5498
|
+
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
5499
|
+
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
|
|
5500
|
+
return { kind: agentInspectKind, warnings };
|
|
5501
|
+
}
|
|
5502
|
+
const operation = attributes["gen_ai.operation.name"];
|
|
5503
|
+
if (typeof operation === "string") {
|
|
5504
|
+
switch (operation) {
|
|
5505
|
+
case "generate_content":
|
|
5506
|
+
case "chat":
|
|
5507
|
+
return { kind: "LLM", warnings };
|
|
5508
|
+
case "execute_tool":
|
|
5509
|
+
return { kind: "TOOL", warnings };
|
|
5510
|
+
case "invoke_agent":
|
|
5511
|
+
return { kind: "AGENT", warnings };
|
|
5512
|
+
default:
|
|
5513
|
+
warnings.push({
|
|
5514
|
+
code: "otlp_gen_ai_operation_semantic_loss",
|
|
5515
|
+
message: `OTLP GenAI operation "${operation}" mapped to AgentInspect LOGIC.`,
|
|
5516
|
+
severity: "warning",
|
|
5517
|
+
field: `${pathPrefix}.attributes.gen_ai.operation.name`
|
|
5518
|
+
});
|
|
5519
|
+
return { kind: "LOGIC", warnings };
|
|
5520
|
+
}
|
|
5521
|
+
}
|
|
5522
|
+
warnings.push({
|
|
5523
|
+
code: "otlp_kind_unknown",
|
|
5524
|
+
message: "OTLP span had no AgentInspect kind or GenAI operation; mapped to LOGIC.",
|
|
5525
|
+
severity: "warning",
|
|
5526
|
+
field: `${pathPrefix}.attributes`
|
|
5527
|
+
});
|
|
5528
|
+
return { kind: "LOGIC", warnings };
|
|
5529
|
+
}
|
|
5530
|
+
function readOtlpTokenUsage(attributes) {
|
|
5531
|
+
const input3 = attributes["gen_ai.usage.input_tokens"];
|
|
5532
|
+
const output2 = attributes["gen_ai.usage.output_tokens"];
|
|
5533
|
+
const usage = {};
|
|
5534
|
+
if (typeof input3 === "number" && Number.isFinite(input3) && input3 >= 0) {
|
|
5535
|
+
usage.input = input3;
|
|
5536
|
+
}
|
|
5537
|
+
if (typeof output2 === "number" && Number.isFinite(output2) && output2 >= 0) {
|
|
5538
|
+
usage.output = output2;
|
|
5539
|
+
}
|
|
5540
|
+
if (usage.input !== void 0 && usage.output !== void 0) {
|
|
5541
|
+
usage.total = usage.input + usage.output;
|
|
5542
|
+
}
|
|
5543
|
+
return Object.keys(usage).length > 0 ? usage : void 0;
|
|
5544
|
+
}
|
|
5545
|
+
function readOtlpConfidence(attributes) {
|
|
5546
|
+
return readOpenInferenceConfidence(attributes);
|
|
5547
|
+
}
|
|
5548
|
+
function sanitizeOtlpAttributes(attributes, pathPrefix) {
|
|
5549
|
+
const ownerPath = pathPrefix.endsWith(".attributes") ? pathPrefix.slice(0, -".attributes".length) : pathPrefix;
|
|
5550
|
+
const sanitized = sanitizeOpenInferenceAttributes(attributes, ownerPath);
|
|
5551
|
+
return {
|
|
5552
|
+
...sanitized,
|
|
5553
|
+
warnings: sanitized.warnings.map(
|
|
5554
|
+
(warning) => warning.code === "openinference_sensitive_attribute_summarized" ? {
|
|
5555
|
+
...warning,
|
|
5556
|
+
code: "otlp_sensitive_attribute_summarized",
|
|
5557
|
+
message: "OTLP prompt/output/document attribute(s) were summarized instead of copied verbatim."
|
|
5558
|
+
} : warning
|
|
5559
|
+
)
|
|
5560
|
+
};
|
|
5561
|
+
}
|
|
5562
|
+
function mapOtlpEvents(value, pathPrefix) {
|
|
5563
|
+
const warnings = [];
|
|
5564
|
+
const unsupportedFields = [];
|
|
5565
|
+
if (value === void 0) return { warnings, unsupportedFields };
|
|
5566
|
+
if (!Array.isArray(value)) {
|
|
5567
|
+
unsupportedFields.push(pathPrefix);
|
|
5568
|
+
warnings.push({
|
|
5569
|
+
code: "otlp_events_invalid",
|
|
5570
|
+
message: "OTLP events field was not an array.",
|
|
5571
|
+
severity: "warning",
|
|
5572
|
+
field: pathPrefix
|
|
5573
|
+
});
|
|
5574
|
+
return { warnings, unsupportedFields };
|
|
5575
|
+
}
|
|
5576
|
+
const events = [];
|
|
5577
|
+
for (const [index, event] of value.entries()) {
|
|
5578
|
+
const eventPath = `${pathPrefix}[${index}]`;
|
|
5579
|
+
if (!isRecord13(event)) {
|
|
5580
|
+
unsupportedFields.push(eventPath);
|
|
5581
|
+
continue;
|
|
5582
|
+
}
|
|
5583
|
+
const parsedAttributes = parseOtlpAttributes(
|
|
5584
|
+
event.attributes,
|
|
5585
|
+
`${eventPath}.attributes`
|
|
5586
|
+
);
|
|
5587
|
+
warnings.push(...parsedAttributes.warnings);
|
|
5588
|
+
unsupportedFields.push(...parsedAttributes.unsupportedFields);
|
|
5589
|
+
const sanitized = sanitizeOtlpAttributes(
|
|
5590
|
+
parsedAttributes.attributes,
|
|
5591
|
+
`${eventPath}.attributes`
|
|
5592
|
+
);
|
|
5593
|
+
warnings.push(...sanitized.warnings);
|
|
5594
|
+
unsupportedFields.push(...sanitized.unsupportedFields);
|
|
5595
|
+
const out = {};
|
|
5596
|
+
const name = readStringField(event, ["name"]);
|
|
5597
|
+
if (name !== void 0) {
|
|
5598
|
+
out.name = name;
|
|
5599
|
+
}
|
|
5600
|
+
const timestamp = parseUnixNanoToIso(event.timeUnixNano);
|
|
5601
|
+
if (timestamp !== void 0) {
|
|
5602
|
+
out.timestamp = timestamp;
|
|
5603
|
+
} else if (event.timeUnixNano !== void 0) {
|
|
5604
|
+
unsupportedFields.push(`${eventPath}.timeUnixNano`);
|
|
5605
|
+
warnings.push({
|
|
5606
|
+
code: "otlp_event_timestamp_invalid",
|
|
5607
|
+
message: "OTLP event timeUnixNano could not be parsed.",
|
|
5608
|
+
severity: "warning",
|
|
5609
|
+
field: `${eventPath}.timeUnixNano`
|
|
5610
|
+
});
|
|
5611
|
+
}
|
|
5612
|
+
if (Object.keys(sanitized.attributes).length > 0) {
|
|
5613
|
+
out.attributes = sanitized.attributes;
|
|
5614
|
+
}
|
|
5615
|
+
events.push(out);
|
|
5616
|
+
}
|
|
5617
|
+
return {
|
|
5618
|
+
events: events.length > 0 ? events : void 0,
|
|
5619
|
+
warnings,
|
|
5620
|
+
unsupportedFields
|
|
5621
|
+
};
|
|
5622
|
+
}
|
|
5623
|
+
function mapOtlpSpan(context) {
|
|
5624
|
+
const { span, pathPrefix } = context;
|
|
5625
|
+
const warnings = [];
|
|
5626
|
+
const unsupportedFields = [];
|
|
5627
|
+
const parsedSpanAttributes = parseOtlpAttributes(
|
|
5628
|
+
span.attributes,
|
|
5629
|
+
`${pathPrefix}.attributes`
|
|
5630
|
+
);
|
|
5631
|
+
warnings.push(...parsedSpanAttributes.warnings);
|
|
5632
|
+
unsupportedFields.push(...parsedSpanAttributes.unsupportedFields);
|
|
5633
|
+
const sanitizedSpanAttributes = sanitizeOtlpAttributes(
|
|
5634
|
+
parsedSpanAttributes.attributes,
|
|
5635
|
+
`${pathPrefix}.attributes`
|
|
5636
|
+
);
|
|
5637
|
+
warnings.push(...sanitizedSpanAttributes.warnings);
|
|
5638
|
+
unsupportedFields.push(...sanitizedSpanAttributes.unsupportedFields);
|
|
5639
|
+
const attributes = {
|
|
5640
|
+
...sanitizedSpanAttributes.attributes
|
|
5641
|
+
};
|
|
5642
|
+
for (const [key, value] of Object.entries(context.resourceAttributes)) {
|
|
5643
|
+
attributes[`resource.${key}`] = value;
|
|
5644
|
+
}
|
|
5645
|
+
for (const [key, value] of Object.entries(context.scopeAttributes)) {
|
|
5646
|
+
attributes[`scope.${key}`] = value;
|
|
5647
|
+
}
|
|
5648
|
+
if (context.scopeName !== void 0) {
|
|
5649
|
+
attributes["scope.name"] = context.scopeName;
|
|
5650
|
+
}
|
|
5651
|
+
if (context.scopeVersion !== void 0) {
|
|
5652
|
+
attributes["scope.version"] = context.scopeVersion;
|
|
5653
|
+
}
|
|
5654
|
+
for (const [key, value] of Object.entries(span)) {
|
|
5655
|
+
if (OTLP_SPAN_KEYS.has(key)) continue;
|
|
5656
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
5657
|
+
if (value === null || typeof value !== "object") {
|
|
5658
|
+
attributes[`otlp.${key}`] = value;
|
|
5659
|
+
} else {
|
|
5660
|
+
attributes[`otlp.${key}.summary`] = summarizeAttributeValue(value);
|
|
5661
|
+
warnings.push({
|
|
5662
|
+
code: "otlp_unsupported_field_summarized",
|
|
5663
|
+
message: `Unsupported OTLP span field "${key}" was summarized.`,
|
|
5664
|
+
severity: "warning",
|
|
5665
|
+
field: `${pathPrefix}.${key}`
|
|
5666
|
+
});
|
|
5667
|
+
}
|
|
5668
|
+
}
|
|
5669
|
+
for (const key of [
|
|
5670
|
+
"droppedAttributesCount",
|
|
5671
|
+
"droppedEventsCount",
|
|
5672
|
+
"droppedLinksCount",
|
|
5673
|
+
"links"
|
|
5674
|
+
]) {
|
|
5675
|
+
if (span[key] !== void 0) {
|
|
5676
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
5677
|
+
warnings.push({
|
|
5678
|
+
code: "otlp_span_field_not_mapped",
|
|
5679
|
+
message: `OTLP span field "${key}" is not represented in AgentInspect events.`,
|
|
5680
|
+
severity: "warning",
|
|
5681
|
+
field: `${pathPrefix}.${key}`
|
|
5682
|
+
});
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5685
|
+
const events = mapOtlpEvents(span.events, `${pathPrefix}.events`);
|
|
5686
|
+
warnings.push(...events.warnings);
|
|
5687
|
+
unsupportedFields.push(...events.unsupportedFields);
|
|
5688
|
+
if (events.events !== void 0) {
|
|
5689
|
+
attributes["otlp.events"] = events.events;
|
|
5690
|
+
}
|
|
5691
|
+
const traceId = readStringField(span, ["traceId"]) ?? "trace-unknown";
|
|
5692
|
+
const spanId = readStringField(span, ["spanId"]) ?? "span-unknown";
|
|
5693
|
+
const parentSpanId = readStringField(span, ["parentSpanId"]);
|
|
5694
|
+
const startedAt = readOpenInferenceTimestamp(
|
|
5695
|
+
span,
|
|
5696
|
+
["startTimeUnixNano"],
|
|
5697
|
+
[]
|
|
5698
|
+
);
|
|
5699
|
+
const endedAt = readOpenInferenceTimestamp(span, ["endTimeUnixNano"], []);
|
|
5700
|
+
const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
|
|
5701
|
+
if (startedAt === void 0) {
|
|
5702
|
+
unsupportedFields.push(`${pathPrefix}.startTimeUnixNano`);
|
|
5703
|
+
warnings.push({
|
|
5704
|
+
code: "otlp_missing_start_time",
|
|
5705
|
+
message: "OTLP span is missing a valid startTimeUnixNano; using Unix epoch.",
|
|
5706
|
+
severity: "warning",
|
|
5707
|
+
field: `${pathPrefix}.startTimeUnixNano`
|
|
5708
|
+
});
|
|
5709
|
+
}
|
|
5710
|
+
const { kind, warnings: kindWarnings } = readOtlpKind(
|
|
5711
|
+
parsedSpanAttributes.attributes,
|
|
5712
|
+
pathPrefix
|
|
5713
|
+
);
|
|
5714
|
+
warnings.push(...kindWarnings);
|
|
5715
|
+
const status = mapOtlpStatus(span.status);
|
|
5716
|
+
const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
|
|
5717
|
+
const errorMessage = isRecord13(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
5718
|
+
const event = {
|
|
5719
|
+
schemaVersion: "0.2",
|
|
5720
|
+
eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
|
|
5721
|
+
runId: typeof parsedSpanAttributes.attributes["agent_inspect.run_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.run_id"] : traceId,
|
|
5722
|
+
kind,
|
|
5723
|
+
name: readStringField(span, ["name"]) ?? spanId,
|
|
5724
|
+
timestamp,
|
|
5725
|
+
confidence: readOtlpConfidence(parsedSpanAttributes.attributes),
|
|
5726
|
+
source: {
|
|
5727
|
+
type: "otel",
|
|
5728
|
+
name: context.scopeName ?? (typeof context.resourceAttributes["service.name"] === "string" ? context.resourceAttributes["service.name"] : "otlp-json"),
|
|
5729
|
+
...context.scopeVersion !== void 0 ? { version: context.scopeVersion } : {}
|
|
5730
|
+
},
|
|
5731
|
+
attributes,
|
|
5732
|
+
trace: {
|
|
5733
|
+
traceId,
|
|
5734
|
+
spanId,
|
|
5735
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
5736
|
+
}
|
|
5737
|
+
};
|
|
5738
|
+
if (status !== void 0) {
|
|
5739
|
+
event.status = status;
|
|
5740
|
+
}
|
|
5741
|
+
if (startedAt !== void 0) {
|
|
5742
|
+
event.startedAt = startedAt;
|
|
5743
|
+
}
|
|
5744
|
+
if (endedAt !== void 0) {
|
|
5745
|
+
event.endedAt = endedAt;
|
|
5746
|
+
}
|
|
5747
|
+
const durationMs = durationBetweenIso(startedAt, endedAt);
|
|
5748
|
+
if (durationMs !== void 0) {
|
|
5749
|
+
event.durationMs = durationMs;
|
|
5750
|
+
}
|
|
5751
|
+
if (tokenUsage !== void 0) {
|
|
5752
|
+
event.tokenUsage = tokenUsage;
|
|
5753
|
+
}
|
|
5754
|
+
if (status === "error") {
|
|
5755
|
+
event.error = {
|
|
5756
|
+
message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OTLP span error"
|
|
5757
|
+
};
|
|
5758
|
+
}
|
|
5759
|
+
return {
|
|
5760
|
+
event,
|
|
5761
|
+
warnings,
|
|
5762
|
+
unsupportedFields,
|
|
5763
|
+
spanId,
|
|
5764
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
5765
|
+
};
|
|
5766
|
+
}
|
|
5767
|
+
function mapOtlpEventsToPersisted(document) {
|
|
5768
|
+
const mapped = document.spans.map((span) => mapOtlpSpan(span));
|
|
5769
|
+
const spanIdToEventId = new Map(
|
|
5770
|
+
mapped.map((span) => [span.spanId, span.event.eventId])
|
|
5771
|
+
);
|
|
5772
|
+
for (const span of mapped) {
|
|
5773
|
+
if (span.parentSpanId === void 0) continue;
|
|
5774
|
+
span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
|
|
5775
|
+
}
|
|
5776
|
+
return {
|
|
5777
|
+
events: mapped.map((span) => span.event),
|
|
5778
|
+
warnings: mapped.flatMap((span) => span.warnings),
|
|
5779
|
+
unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
|
|
5780
|
+
};
|
|
5781
|
+
}
|
|
5782
|
+
var otlpJsonReader = {
|
|
5783
|
+
format: OTLP_READER_FORMAT,
|
|
5784
|
+
name: "OTLP JSON",
|
|
5785
|
+
async detect(input3) {
|
|
5786
|
+
const resolved = await resolveInput(input3);
|
|
5787
|
+
if (!resolved) return void 0;
|
|
5788
|
+
let parsed;
|
|
5789
|
+
try {
|
|
5790
|
+
parsed = parseJsonDocument(resolved.content);
|
|
5791
|
+
} catch {
|
|
5792
|
+
return void 0;
|
|
5793
|
+
}
|
|
5794
|
+
const document = extractOtlpDocument(parsed);
|
|
5795
|
+
if (!document) return void 0;
|
|
5796
|
+
return {
|
|
5797
|
+
format: OTLP_READER_FORMAT,
|
|
5798
|
+
confidence: document.confidence,
|
|
5799
|
+
readerName: "OTLP JSON",
|
|
5800
|
+
description: document.description,
|
|
5801
|
+
warnings: attachSingleSourceFile(document.warnings, resolved)
|
|
5802
|
+
};
|
|
5803
|
+
},
|
|
5804
|
+
async read(input3) {
|
|
5805
|
+
const resolved = await resolveInput(input3);
|
|
5806
|
+
if (!resolved) {
|
|
5807
|
+
throw new TraceReadError(
|
|
5808
|
+
"unsupported_format",
|
|
5809
|
+
"OTLP JSON reader requires file, string, or buffer input."
|
|
5810
|
+
);
|
|
5811
|
+
}
|
|
5812
|
+
let parsed;
|
|
5813
|
+
try {
|
|
5814
|
+
parsed = parseJsonDocument(resolved.content);
|
|
5815
|
+
} catch {
|
|
5816
|
+
throw new TraceReadError("unsupported_format", "OTLP JSON input is not valid JSON.", [
|
|
5817
|
+
{
|
|
5818
|
+
code: "otlp_invalid_json",
|
|
5819
|
+
message: "OTLP JSON reader could not parse the input as JSON.",
|
|
5820
|
+
severity: "error"
|
|
5821
|
+
}
|
|
5822
|
+
]);
|
|
5823
|
+
}
|
|
5824
|
+
const document = extractOtlpDocument(parsed);
|
|
5825
|
+
if (!document || document.spans.length === 0) {
|
|
5826
|
+
throw new TraceReadError(
|
|
5827
|
+
"unsupported_format",
|
|
5828
|
+
"No valid OTLP spans found.",
|
|
5829
|
+
attachSingleSourceFile(
|
|
5830
|
+
document?.warnings ?? [
|
|
5831
|
+
{
|
|
5832
|
+
code: "otlp_no_valid_spans",
|
|
5833
|
+
message: "OTLP JSON input did not contain valid spans.",
|
|
5834
|
+
severity: "error"
|
|
5835
|
+
}
|
|
5836
|
+
],
|
|
5837
|
+
resolved
|
|
5838
|
+
)
|
|
5839
|
+
);
|
|
5840
|
+
}
|
|
5841
|
+
const mapped = mapOtlpEventsToPersisted(document);
|
|
5842
|
+
const warnings = attachSingleSourceFile(
|
|
5843
|
+
[...document.warnings, ...mapped.warnings],
|
|
5844
|
+
resolved
|
|
5845
|
+
);
|
|
5846
|
+
const unsupportedFields = [
|
|
5847
|
+
...document.unsupportedFields,
|
|
5848
|
+
...mapped.unsupportedFields
|
|
5849
|
+
].sort((a, b) => a.localeCompare(b));
|
|
5850
|
+
return {
|
|
5851
|
+
format: OTLP_READER_FORMAT,
|
|
5852
|
+
events: mapped.events,
|
|
5853
|
+
runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
|
|
5854
|
+
warnings,
|
|
5855
|
+
unsupportedFields,
|
|
5856
|
+
sourceFiles: resolved.sourceFiles
|
|
5857
|
+
};
|
|
5858
|
+
}
|
|
5859
|
+
};
|
|
5860
|
+
var agentInspectJsonlReader = {
|
|
5861
|
+
format: "agent-inspect-jsonl",
|
|
5862
|
+
name: "AgentInspect JSONL",
|
|
5863
|
+
async detect(input3) {
|
|
5864
|
+
const resolved = await resolveInput(input3);
|
|
5865
|
+
if (!resolved) return void 0;
|
|
5866
|
+
const detected = detectJsonlFormat(resolved.content);
|
|
5867
|
+
if (detected.validRows === 0 || detected.format === "empty") {
|
|
5868
|
+
return void 0;
|
|
5869
|
+
}
|
|
5870
|
+
return {
|
|
5871
|
+
format: "agent-inspect-jsonl",
|
|
5872
|
+
confidence: 0.95,
|
|
5873
|
+
readerName: "AgentInspect JSONL",
|
|
5874
|
+
description: agentInspectFormatLabel(detected.format),
|
|
5875
|
+
warnings: attachSingleSourceFile(detected.warnings, resolved)
|
|
5876
|
+
};
|
|
5877
|
+
},
|
|
5878
|
+
async read(input3) {
|
|
5879
|
+
const resolved = await resolveInput(input3);
|
|
5880
|
+
if (!resolved) {
|
|
5881
|
+
throw new Error("AgentInspect JSONL reader requires file, directory, string, or buffer input.");
|
|
5882
|
+
}
|
|
5883
|
+
const parsed = parseTraceJsonl(resolved.content, { warnings: false });
|
|
5884
|
+
if (parsed.sourceEventCount === 0) {
|
|
5885
|
+
throw new Error("No valid AgentInspect JSONL events found.");
|
|
5886
|
+
}
|
|
5887
|
+
const events = persistedEventsForParsedTrace(parsed);
|
|
5888
|
+
return {
|
|
5889
|
+
format: agentInspectFormatLabel(parsed.format),
|
|
5890
|
+
events,
|
|
5891
|
+
runs: persistedInspectEventsToRunTrees(events, { skipInvalid: true }),
|
|
5892
|
+
warnings: parsed.format === "mixed" ? attachSingleSourceFile(
|
|
5893
|
+
[
|
|
5894
|
+
{
|
|
5895
|
+
code: "mixed_agent_inspect_jsonl",
|
|
5896
|
+
message: "Trace input mixes schemaVersion 0.1 and 0.2 rows; events were normalized for reading.",
|
|
5897
|
+
severity: "warning"
|
|
5898
|
+
}
|
|
5899
|
+
],
|
|
5900
|
+
resolved
|
|
5901
|
+
) : [],
|
|
5902
|
+
unsupportedFields: [],
|
|
5903
|
+
sourceFiles: resolved.sourceFiles
|
|
5904
|
+
};
|
|
5905
|
+
}
|
|
5906
|
+
};
|
|
5907
|
+
var DEFAULT_TRACE_READERS = [
|
|
5908
|
+
agentInspectJsonlReader,
|
|
5909
|
+
openInferenceJsonReader,
|
|
5910
|
+
otlpJsonReader
|
|
5911
|
+
];
|
|
5912
|
+
async function detectTraceFormat(input3, options = {}) {
|
|
5913
|
+
const readers = options.readers ?? DEFAULT_TRACE_READERS;
|
|
5914
|
+
if (options.format !== void 0) {
|
|
5915
|
+
const reader = findReaderByFormat(options.format, readers);
|
|
5916
|
+
if (!reader) {
|
|
5917
|
+
return {
|
|
5918
|
+
status: "unsupported",
|
|
5919
|
+
candidates: [],
|
|
5920
|
+
warnings: [
|
|
5921
|
+
{
|
|
5922
|
+
code: "unsupported_format",
|
|
5923
|
+
message: `No trace reader is registered for format "${options.format}".`,
|
|
5924
|
+
severity: "error"
|
|
5925
|
+
}
|
|
5926
|
+
]
|
|
5927
|
+
};
|
|
5928
|
+
}
|
|
5929
|
+
return {
|
|
5930
|
+
status: "detected",
|
|
5931
|
+
format: reader.format,
|
|
5932
|
+
candidates: [
|
|
5933
|
+
{
|
|
5934
|
+
format: reader.format,
|
|
5935
|
+
confidence: 1,
|
|
5936
|
+
readerName: reader.name,
|
|
5937
|
+
description: "Explicit format override"
|
|
5938
|
+
}
|
|
5939
|
+
],
|
|
5940
|
+
warnings: []
|
|
5941
|
+
};
|
|
5942
|
+
}
|
|
5943
|
+
const candidates = [];
|
|
5944
|
+
const warnings = [];
|
|
5945
|
+
for (const reader of readers) {
|
|
5946
|
+
try {
|
|
5947
|
+
const candidate = await reader.detect(input3);
|
|
5948
|
+
if (candidate !== void 0) {
|
|
5949
|
+
candidates.push(normalizeCandidate(reader, candidate));
|
|
5950
|
+
}
|
|
5951
|
+
} catch (error) {
|
|
5952
|
+
if (error instanceof TraceReadError) {
|
|
5953
|
+
warnings.push(...error.warnings);
|
|
5954
|
+
continue;
|
|
5955
|
+
}
|
|
5956
|
+
warnings.push({
|
|
5957
|
+
code: "reader_detect_failed",
|
|
5958
|
+
message: error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed during detection.`,
|
|
5959
|
+
severity: "warning"
|
|
5960
|
+
});
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5963
|
+
const sorted = sortCandidates(
|
|
5964
|
+
candidates.filter((candidate) => candidate.confidence >= MIN_DETECTION_CONFIDENCE)
|
|
5965
|
+
);
|
|
5966
|
+
const candidateWarnings = collectWarnings(sorted);
|
|
5967
|
+
const lowConfidenceWarnings = candidates.length > sorted.length ? [
|
|
5968
|
+
{
|
|
5969
|
+
code: "low_confidence_candidates",
|
|
5970
|
+
message: `Ignored ${candidates.length - sorted.length} low-confidence format candidate(s).`,
|
|
5971
|
+
severity: "info"
|
|
5972
|
+
}
|
|
5973
|
+
] : [];
|
|
5974
|
+
const allWarnings = dedupeWarnings([
|
|
5975
|
+
...warnings,
|
|
5976
|
+
...candidateWarnings,
|
|
5977
|
+
...lowConfidenceWarnings
|
|
5978
|
+
]);
|
|
5979
|
+
if (sorted.length === 0) {
|
|
5980
|
+
return {
|
|
5981
|
+
status: "unsupported",
|
|
5982
|
+
candidates: [],
|
|
5983
|
+
warnings: allWarnings
|
|
5984
|
+
};
|
|
5985
|
+
}
|
|
5986
|
+
const [best, second] = sorted;
|
|
5987
|
+
if (second !== void 0 && best.confidence - second.confidence <= AMBIGUOUS_CONFIDENCE_DELTA) {
|
|
5988
|
+
return {
|
|
5989
|
+
status: "ambiguous",
|
|
5990
|
+
candidates: sorted,
|
|
5991
|
+
warnings: [
|
|
5992
|
+
...allWarnings,
|
|
5993
|
+
{
|
|
5994
|
+
code: "ambiguous_format_candidates",
|
|
5995
|
+
message: `Top trace format candidates are within ${AMBIGUOUS_CONFIDENCE_DELTA} confidence.`,
|
|
5996
|
+
severity: "warning"
|
|
5997
|
+
}
|
|
5998
|
+
]
|
|
5999
|
+
};
|
|
6000
|
+
}
|
|
6001
|
+
return {
|
|
6002
|
+
status: "detected",
|
|
6003
|
+
format: best.format,
|
|
6004
|
+
candidates: sorted,
|
|
6005
|
+
warnings: allWarnings
|
|
6006
|
+
};
|
|
6007
|
+
}
|
|
6008
|
+
async function readTrace(input3, options = {}) {
|
|
6009
|
+
const readers = options.readers ?? DEFAULT_TRACE_READERS;
|
|
6010
|
+
const detection = await detectTraceFormat(input3, options);
|
|
6011
|
+
if (detection.status === "unsupported" || detection.format === void 0) {
|
|
6012
|
+
throw new TraceReadError(
|
|
6013
|
+
"unsupported_format",
|
|
6014
|
+
"No trace reader could detect the input format.",
|
|
6015
|
+
detection.warnings
|
|
6016
|
+
);
|
|
6017
|
+
}
|
|
6018
|
+
if (detection.status === "ambiguous") {
|
|
6019
|
+
throw new TraceReadError(
|
|
6020
|
+
"ambiguous_format",
|
|
6021
|
+
"Multiple trace readers matched the input with equal confidence.",
|
|
6022
|
+
detection.warnings
|
|
6023
|
+
);
|
|
6024
|
+
}
|
|
6025
|
+
const reader = findReaderByFormat(detection.format, readers);
|
|
6026
|
+
if (!reader) {
|
|
6027
|
+
throw new TraceReadError(
|
|
6028
|
+
"unsupported_format",
|
|
6029
|
+
`No trace reader is registered for format "${detection.format}".`,
|
|
6030
|
+
detection.warnings
|
|
6031
|
+
);
|
|
6032
|
+
}
|
|
6033
|
+
try {
|
|
6034
|
+
const result = await reader.read(input3, { format: detection.format });
|
|
6035
|
+
return {
|
|
6036
|
+
...result,
|
|
6037
|
+
format: result.format || detection.format,
|
|
6038
|
+
warnings: [...detection.warnings, ...result.warnings]
|
|
6039
|
+
};
|
|
6040
|
+
} catch (error) {
|
|
6041
|
+
if (error instanceof TraceReadError) {
|
|
6042
|
+
throw new TraceReadError(
|
|
6043
|
+
error.code,
|
|
6044
|
+
error.message,
|
|
6045
|
+
dedupeWarnings([...detection.warnings, ...error.warnings])
|
|
6046
|
+
);
|
|
6047
|
+
}
|
|
6048
|
+
throw new TraceReadError(
|
|
6049
|
+
"reader_failed",
|
|
6050
|
+
error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed.`,
|
|
6051
|
+
detection.warnings
|
|
6052
|
+
);
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
function openTrace(input3, options = {}) {
|
|
6056
|
+
return readTrace(input3, options);
|
|
6057
|
+
}
|
|
6058
|
+
|
|
6059
|
+
// packages/core/src/diff/comparable.ts
|
|
6060
|
+
function extractOutputPreview(meta) {
|
|
3941
6061
|
if (meta === void 0) return void 0;
|
|
3942
6062
|
if ("outputPreview" in meta) return meta.outputPreview;
|
|
3943
6063
|
if ("resultPreview" in meta) return meta.resultPreview;
|
|
@@ -4075,13 +6195,13 @@ function pairSteps(left, right) {
|
|
|
4075
6195
|
return pairs;
|
|
4076
6196
|
}
|
|
4077
6197
|
function compareLeafSteps(L, R, segments, opts, out) {
|
|
4078
|
-
const
|
|
6198
|
+
const path10 = buildPath(segments);
|
|
4079
6199
|
if (L.name !== R.name) {
|
|
4080
6200
|
out.push({
|
|
4081
6201
|
kind: "structure",
|
|
4082
6202
|
severity: "warning",
|
|
4083
6203
|
message: "Step name differs",
|
|
4084
|
-
path:
|
|
6204
|
+
path: path10,
|
|
4085
6205
|
left: L.name,
|
|
4086
6206
|
right: R.name
|
|
4087
6207
|
});
|
|
@@ -4091,7 +6211,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4091
6211
|
kind: "step-type",
|
|
4092
6212
|
severity: "warning",
|
|
4093
6213
|
message: "Step type differs",
|
|
4094
|
-
path:
|
|
6214
|
+
path: path10,
|
|
4095
6215
|
left: L.type,
|
|
4096
6216
|
right: R.type
|
|
4097
6217
|
});
|
|
@@ -4101,7 +6221,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4101
6221
|
kind: "step-status",
|
|
4102
6222
|
severity: "warning",
|
|
4103
6223
|
message: "Step status differs",
|
|
4104
|
-
path:
|
|
6224
|
+
path: path10,
|
|
4105
6225
|
left: L.status,
|
|
4106
6226
|
right: R.status
|
|
4107
6227
|
});
|
|
@@ -4113,7 +6233,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4113
6233
|
kind: "error",
|
|
4114
6234
|
severity: "error",
|
|
4115
6235
|
message: "Step error message differs",
|
|
4116
|
-
path:
|
|
6236
|
+
path: path10,
|
|
4117
6237
|
left: le || void 0,
|
|
4118
6238
|
right: re || void 0
|
|
4119
6239
|
});
|
|
@@ -4131,7 +6251,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4131
6251
|
kind: "duration",
|
|
4132
6252
|
severity: "info",
|
|
4133
6253
|
message: "Step duration differs",
|
|
4134
|
-
path:
|
|
6254
|
+
path: path10,
|
|
4135
6255
|
left: ld,
|
|
4136
6256
|
right: rd
|
|
4137
6257
|
});
|
|
@@ -4144,7 +6264,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4144
6264
|
kind: "metadata",
|
|
4145
6265
|
severity: "info",
|
|
4146
6266
|
message: "Step metadata differs",
|
|
4147
|
-
path:
|
|
6267
|
+
path: path10,
|
|
4148
6268
|
left: L.metadata,
|
|
4149
6269
|
right: R.metadata
|
|
4150
6270
|
});
|
|
@@ -4156,7 +6276,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4156
6276
|
kind: "output",
|
|
4157
6277
|
severity: "info",
|
|
4158
6278
|
message: "Output preview differs",
|
|
4159
|
-
path:
|
|
6279
|
+
path: path10,
|
|
4160
6280
|
left: L.outputPreview,
|
|
4161
6281
|
right: R.outputPreview
|
|
4162
6282
|
});
|
|
@@ -4594,10 +6714,10 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
4594
6714
|
return 3;
|
|
4595
6715
|
}
|
|
4596
6716
|
if ("TERM_PROGRAM" in env) {
|
|
4597
|
-
const
|
|
6717
|
+
const version2 = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
|
|
4598
6718
|
switch (env.TERM_PROGRAM) {
|
|
4599
6719
|
case "iTerm.app": {
|
|
4600
|
-
return
|
|
6720
|
+
return version2 >= 3 ? 3 : 2;
|
|
4601
6721
|
}
|
|
4602
6722
|
case "Apple_Terminal": {
|
|
4603
6723
|
return 2;
|
|
@@ -4806,11 +6926,11 @@ createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
4806
6926
|
var source_default = chalk;
|
|
4807
6927
|
|
|
4808
6928
|
// packages/core/src/diff/renderer.ts
|
|
4809
|
-
function formatPath(
|
|
4810
|
-
if (
|
|
6929
|
+
function formatPath(path10) {
|
|
6930
|
+
if (path10 === void 0 || path10.path.length === 0) {
|
|
4811
6931
|
return "(run)";
|
|
4812
6932
|
}
|
|
4813
|
-
return
|
|
6933
|
+
return path10.path.map((s) => s.name).join(" > ");
|
|
4814
6934
|
}
|
|
4815
6935
|
function formatValue(v, verbose) {
|
|
4816
6936
|
if (v === void 0) return "(undefined)";
|
|
@@ -5717,18 +7837,38 @@ async function clean(options = {}) {
|
|
|
5717
7837
|
process.exitCode = 1;
|
|
5718
7838
|
}
|
|
5719
7839
|
}
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
7840
|
+
function mapReaderFormat(format) {
|
|
7841
|
+
switch (format) {
|
|
7842
|
+
case "agent-inspect-v0.1-jsonl":
|
|
7843
|
+
return "0.1";
|
|
7844
|
+
case "agent-inspect-v0.2-jsonl":
|
|
7845
|
+
return "0.2";
|
|
7846
|
+
case "agent-inspect-mixed-jsonl":
|
|
7847
|
+
return "mixed";
|
|
7848
|
+
default:
|
|
7849
|
+
return "empty";
|
|
5726
7850
|
}
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
7851
|
+
}
|
|
7852
|
+
function isMissingFileError(error) {
|
|
7853
|
+
return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
7854
|
+
}
|
|
7855
|
+
async function readRunTraceEvents(runId, traceDir) {
|
|
7856
|
+
const filePath = getTraceFilePath(runId, traceDir);
|
|
7857
|
+
try {
|
|
7858
|
+
await access(filePath);
|
|
7859
|
+
const result = await openTrace(
|
|
7860
|
+
{ type: "file", path: filePath },
|
|
7861
|
+
{ format: "agent-inspect-jsonl" }
|
|
7862
|
+
);
|
|
7863
|
+
const events = persistedInspectEventsToTraceEvents(result.events);
|
|
7864
|
+
return {
|
|
7865
|
+
events,
|
|
7866
|
+
format: events.length > 0 ? mapReaderFormat(result.format) : "empty"
|
|
7867
|
+
};
|
|
7868
|
+
} catch (error) {
|
|
7869
|
+
if (isMissingFileError(error)) return void 0;
|
|
7870
|
+
throw error;
|
|
5730
7871
|
}
|
|
5731
|
-
return { events: parsed.events, format: parsed.format };
|
|
5732
7872
|
}
|
|
5733
7873
|
|
|
5734
7874
|
// packages/cli/src/view.ts
|
|
@@ -6799,6 +8939,149 @@ async function reportCommand(runId, options = {}) {
|
|
|
6799
8939
|
console.log(result.content);
|
|
6800
8940
|
}
|
|
6801
8941
|
}
|
|
8942
|
+
async function readStdin(stdin) {
|
|
8943
|
+
stdin.setEncoding("utf8");
|
|
8944
|
+
let content = "";
|
|
8945
|
+
for await (const chunk of stdin) {
|
|
8946
|
+
content += typeof chunk === "string" ? chunk : String(chunk);
|
|
8947
|
+
}
|
|
8948
|
+
return content;
|
|
8949
|
+
}
|
|
8950
|
+
async function inputFromPathOrStdin(input3, stdin) {
|
|
8951
|
+
if (input3 === void 0 || input3 === "-") {
|
|
8952
|
+
return { type: "string", content: await readStdin(stdin) };
|
|
8953
|
+
}
|
|
8954
|
+
const stats = await stat(input3);
|
|
8955
|
+
if (stats.isDirectory()) return { type: "directory", path: input3 };
|
|
8956
|
+
return { type: "file", path: input3 };
|
|
8957
|
+
}
|
|
8958
|
+
function printWarnings(result) {
|
|
8959
|
+
if (result.warnings.length === 0 && result.unsupportedFields.length === 0) {
|
|
8960
|
+
return;
|
|
8961
|
+
}
|
|
8962
|
+
console.log("");
|
|
8963
|
+
console.log("Diagnostics");
|
|
8964
|
+
for (const warning of result.warnings) {
|
|
8965
|
+
const where = [
|
|
8966
|
+
warning.sourceFile,
|
|
8967
|
+
warning.line !== void 0 ? `line ${warning.line}` : void 0,
|
|
8968
|
+
warning.field
|
|
8969
|
+
].filter((value) => value !== void 0).join(" ");
|
|
8970
|
+
console.log(
|
|
8971
|
+
`- ${warning.code}: ${warning.message}${where !== "" ? ` (${where})` : ""}`
|
|
8972
|
+
);
|
|
8973
|
+
}
|
|
8974
|
+
for (const field of result.unsupportedFields) {
|
|
8975
|
+
console.log(`- unsupported: ${field}`);
|
|
8976
|
+
}
|
|
8977
|
+
}
|
|
8978
|
+
function printNode(node, depth) {
|
|
8979
|
+
const ev = node.event;
|
|
8980
|
+
const status = ev.status !== void 0 ? ` ${ev.status}` : "";
|
|
8981
|
+
const duration = ev.durationMs !== void 0 ? ` ${formatDuration2(ev.durationMs)}` : "";
|
|
8982
|
+
console.log(`${getIndent(depth)}${ev.kind.toLowerCase()}: ${ev.name}${status}${duration}`);
|
|
8983
|
+
for (const child of node.children) {
|
|
8984
|
+
printNode(child, depth + 1);
|
|
8985
|
+
}
|
|
8986
|
+
}
|
|
8987
|
+
function printRun(result, run) {
|
|
8988
|
+
console.log(`Format: ${result.format}`);
|
|
8989
|
+
console.log(`Run: ${run.runId}`);
|
|
8990
|
+
if (run.name !== void 0) console.log(`Name: ${run.name}`);
|
|
8991
|
+
if (run.status !== void 0) console.log(`Status: ${run.status}`);
|
|
8992
|
+
if (run.startedAt !== void 0) {
|
|
8993
|
+
console.log(`Started: ${formatTimestamp(run.startedAt)}`);
|
|
8994
|
+
}
|
|
8995
|
+
if (run.durationMs !== void 0) {
|
|
8996
|
+
console.log(`Duration: ${formatDuration2(run.durationMs)}`);
|
|
8997
|
+
}
|
|
8998
|
+
console.log(`Events: ${run.metadata.totalEvents}`);
|
|
8999
|
+
for (const node of run.children) {
|
|
9000
|
+
printNode(node, 0);
|
|
9001
|
+
}
|
|
9002
|
+
}
|
|
9003
|
+
function selectRun(result, runId) {
|
|
9004
|
+
if (runId !== void 0) {
|
|
9005
|
+
return result.runs.find((run) => run.runId === runId);
|
|
9006
|
+
}
|
|
9007
|
+
return result.runs.length === 1 ? result.runs[0] : void 0;
|
|
9008
|
+
}
|
|
9009
|
+
function printMultipleRuns(result) {
|
|
9010
|
+
console.error(
|
|
9011
|
+
`Trace contains ${result.runs.length} runs. Re-run with --run <run-id>.`
|
|
9012
|
+
);
|
|
9013
|
+
for (const run of result.runs) {
|
|
9014
|
+
const bits = [
|
|
9015
|
+
run.runId,
|
|
9016
|
+
run.name !== void 0 ? `name=${run.name}` : void 0,
|
|
9017
|
+
run.status !== void 0 ? `status=${run.status}` : void 0,
|
|
9018
|
+
`events=${run.metadata.totalEvents}`
|
|
9019
|
+
].filter((value) => value !== void 0);
|
|
9020
|
+
console.error(`- ${bits.join(" ")}`);
|
|
9021
|
+
}
|
|
9022
|
+
}
|
|
9023
|
+
function writeJson(output2) {
|
|
9024
|
+
console.log(JSON.stringify(output2, null, 2));
|
|
9025
|
+
}
|
|
9026
|
+
async function openCommand(input3, options = {}, stdin = process.stdin) {
|
|
9027
|
+
try {
|
|
9028
|
+
const traceInput = await inputFromPathOrStdin(input3, stdin);
|
|
9029
|
+
const result = await openTrace(traceInput, {
|
|
9030
|
+
...options.format !== void 0 ? { format: options.format } : {}
|
|
9031
|
+
});
|
|
9032
|
+
const selected = selectRun(result, options.run);
|
|
9033
|
+
if (selected === void 0) {
|
|
9034
|
+
process.exitCode = 1;
|
|
9035
|
+
const message = options.run !== void 0 ? `Run not found: ${options.run}` : `Trace contains ${result.runs.length} runs. Specify --run <run-id>.`;
|
|
9036
|
+
if (options.json) {
|
|
9037
|
+
writeJson({
|
|
9038
|
+
format: result.format,
|
|
9039
|
+
sourceFiles: result.sourceFiles,
|
|
9040
|
+
warnings: result.warnings,
|
|
9041
|
+
unsupportedFields: result.unsupportedFields,
|
|
9042
|
+
runs: result.runs,
|
|
9043
|
+
error: { message }
|
|
9044
|
+
});
|
|
9045
|
+
} else if (options.run !== void 0) {
|
|
9046
|
+
console.error(message);
|
|
9047
|
+
} else {
|
|
9048
|
+
printMultipleRuns(result);
|
|
9049
|
+
}
|
|
9050
|
+
return;
|
|
9051
|
+
}
|
|
9052
|
+
if (options.json) {
|
|
9053
|
+
writeJson({
|
|
9054
|
+
format: result.format,
|
|
9055
|
+
sourceFiles: result.sourceFiles,
|
|
9056
|
+
warnings: result.warnings,
|
|
9057
|
+
unsupportedFields: result.unsupportedFields,
|
|
9058
|
+
selectedRunId: selected.runId,
|
|
9059
|
+
runs: [selected]
|
|
9060
|
+
});
|
|
9061
|
+
return;
|
|
9062
|
+
}
|
|
9063
|
+
printRun(result, selected);
|
|
9064
|
+
if (options.diagnostics) {
|
|
9065
|
+
printWarnings(result);
|
|
9066
|
+
}
|
|
9067
|
+
} catch (error) {
|
|
9068
|
+
process.exitCode = 1;
|
|
9069
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9070
|
+
const code = error instanceof TraceReadError ? error.code : void 0;
|
|
9071
|
+
const warnings = error instanceof TraceReadError ? error.warnings : [];
|
|
9072
|
+
if (options.json) {
|
|
9073
|
+
writeJson({
|
|
9074
|
+
warnings,
|
|
9075
|
+
error: { ...code !== void 0 ? { code } : {}, message }
|
|
9076
|
+
});
|
|
9077
|
+
return;
|
|
9078
|
+
}
|
|
9079
|
+
console.error(message);
|
|
9080
|
+
if (options.diagnostics) {
|
|
9081
|
+
printWarnings({ warnings, unsupportedFields: [] });
|
|
9082
|
+
}
|
|
9083
|
+
}
|
|
9084
|
+
}
|
|
6802
9085
|
|
|
6803
9086
|
// packages/cli/src/index.ts
|
|
6804
9087
|
function runCommand(action) {
|
|
@@ -6809,7 +9092,7 @@ function runCommand(action) {
|
|
|
6809
9092
|
});
|
|
6810
9093
|
}
|
|
6811
9094
|
function createCliProgram() {
|
|
6812
|
-
const program = new Command("agent-inspect").description("Local-first execution-tree debugger for AI agents").version(
|
|
9095
|
+
const program = new Command("agent-inspect").description("Local-first execution-tree debugger for AI agents").version(version);
|
|
6813
9096
|
program.command("list").description("List recent AgentInspect runs").option("--dir <path>", "trace directory").option("--limit <number>", "max runs to show (default 20, max 100)").addOption(
|
|
6814
9097
|
new Option("--status <status>", "filter by run status").choices([
|
|
6815
9098
|
"running",
|
|
@@ -6892,6 +9175,15 @@ function createCliProgram() {
|
|
|
6892
9175
|
).action((runId, opts) => {
|
|
6893
9176
|
runCommand(() => exportCommand(runId, opts));
|
|
6894
9177
|
});
|
|
9178
|
+
program.command("open").description("Open any supported local trace through the reader pipeline").argument("[input]", "trace file, directory, or - for stdin").addOption(
|
|
9179
|
+
new Option("--format <format>", "trace input format").choices([
|
|
9180
|
+
"agent-inspect-jsonl",
|
|
9181
|
+
"openinference-json",
|
|
9182
|
+
"otlp-json"
|
|
9183
|
+
])
|
|
9184
|
+
).option("--json", "print result as JSON").option("--diagnostics", "print reader warnings and unsupported fields").option("--run <run-id>", "select a run when the trace contains multiple runs").action((input3, opts) => {
|
|
9185
|
+
runCommand(() => openCommand(input3, opts));
|
|
9186
|
+
});
|
|
6895
9187
|
program.command("diff").description("Compare two local AgentInspect JSONL traces (read-only)").argument("<left-run-id>", "first run id").argument("<right-run-id>", "second run id").option("--dir <path>", "trace directory").option("--json", "print diff result as JSON").option("--ignore-duration", "omit duration comparisons").option(
|
|
6896
9188
|
"--duration-threshold <duration>",
|
|
6897
9189
|
"ignore duration deltas at or below this (e.g. 500ms, 2s, 1m)"
|
|
@@ -6947,7 +9239,7 @@ function createCliProgram() {
|
|
|
6947
9239
|
).option("-o, --output <path>", "write report to file (creates parent dirs)").option("--json", "emit JSON wrapper (includes content when writing to stdout)").option("--include-attributes", "include bounded step attributes in tree section").option("--no-errors", "omit error details from tree section").option("--no-correlation", "omit correlation metadata from what section").addOption(
|
|
6948
9240
|
new Option(
|
|
6949
9241
|
"--redaction-profile <profile>",
|
|
6950
|
-
"redaction profile for
|
|
9242
|
+
"redaction profile for entire report: local, share, strict (default: local)"
|
|
6951
9243
|
).choices(["local", "share", "strict"])
|
|
6952
9244
|
).action((runId, opts) => {
|
|
6953
9245
|
runCommand(() => reportCommand(runId, opts));
|