agent-inspect 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +35 -0
- package/README.md +12 -4
- package/docs/API.md +124 -9
- package/docs/ARCHITECTURE.md +4 -0
- package/docs/CLI.md +41 -12
- package/docs/KNOWN-ISSUES.md +8 -1
- package/docs/LIMITATIONS.md +11 -2
- package/docs/SCHEMA.md +16 -6
- package/package.json +21 -1
- 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
|
@@ -22,6 +22,9 @@ var os__default = /*#__PURE__*/_interopDefault(os);
|
|
|
22
22
|
var process2__default = /*#__PURE__*/_interopDefault(process2);
|
|
23
23
|
var tty__default = /*#__PURE__*/_interopDefault(tty);
|
|
24
24
|
|
|
25
|
+
// package.json
|
|
26
|
+
var version = "1.6.0";
|
|
27
|
+
|
|
25
28
|
// packages/core/src/types.ts
|
|
26
29
|
var STEP_TYPES = [
|
|
27
30
|
"run",
|
|
@@ -144,6 +147,7 @@ function isPersistedTokenUsage(value) {
|
|
|
144
147
|
if (!isOptionalNonNegativeNumber(value.input)) return false;
|
|
145
148
|
if (!isOptionalNonNegativeNumber(value.output)) return false;
|
|
146
149
|
if (!isOptionalNonNegativeNumber(value.total)) return false;
|
|
150
|
+
if (!isOptionalNonNegativeNumber(value.cached)) return false;
|
|
147
151
|
return true;
|
|
148
152
|
}
|
|
149
153
|
function isPersistedTraceContext(value) {
|
|
@@ -189,8 +193,405 @@ function isPersistedInspectEvent(value) {
|
|
|
189
193
|
return true;
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
// packages/core/src/
|
|
196
|
+
// packages/core/src/correlation-metadata.ts
|
|
197
|
+
var TRACE_CORRELATION_KEYS = [
|
|
198
|
+
"correlationId",
|
|
199
|
+
"requestId",
|
|
200
|
+
"decisionId",
|
|
201
|
+
"groupId"
|
|
202
|
+
];
|
|
203
|
+
function isNonEmptyString2(value) {
|
|
204
|
+
return typeof value === "string" && value.length > 0;
|
|
205
|
+
}
|
|
206
|
+
function extractCorrelationMetadata(record) {
|
|
207
|
+
if (!record) {
|
|
208
|
+
return void 0;
|
|
209
|
+
}
|
|
210
|
+
const out = {};
|
|
211
|
+
let found = false;
|
|
212
|
+
for (const key of TRACE_CORRELATION_KEYS) {
|
|
213
|
+
const value = record[key];
|
|
214
|
+
if (isNonEmptyString2(value)) {
|
|
215
|
+
out[key] = value;
|
|
216
|
+
found = true;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return found ? out : void 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// packages/core/src/persisted/token-usage.ts
|
|
223
|
+
function isRecord3(value) {
|
|
224
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
225
|
+
}
|
|
226
|
+
function nonNegativeFinite(value) {
|
|
227
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
228
|
+
}
|
|
229
|
+
function normalizeTokenUsage(value) {
|
|
230
|
+
if (!isRecord3(value)) return void 0;
|
|
231
|
+
const input3 = nonNegativeFinite(value.input);
|
|
232
|
+
const output2 = nonNegativeFinite(value.output);
|
|
233
|
+
const suppliedTotal = nonNegativeFinite(value.total);
|
|
234
|
+
const cached = nonNegativeFinite(value.cached);
|
|
235
|
+
const derivedTotal = input3 !== void 0 && output2 !== void 0 && Number.isFinite(input3 + output2) ? input3 + output2 : void 0;
|
|
236
|
+
const total = suppliedTotal ?? derivedTotal;
|
|
237
|
+
if (input3 === void 0 && output2 === void 0 && total === void 0 && cached === void 0) {
|
|
238
|
+
return void 0;
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
...input3 !== void 0 ? { input: input3 } : {},
|
|
242
|
+
...output2 !== void 0 ? { output: output2 } : {},
|
|
243
|
+
...total !== void 0 ? { total } : {},
|
|
244
|
+
...cached !== void 0 ? { cached } : {}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// packages/core/src/persisted/from-trace-event.ts
|
|
249
|
+
function sanitizeIdPart(value) {
|
|
250
|
+
return value.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
251
|
+
}
|
|
252
|
+
function nodeIdForEvent(event) {
|
|
253
|
+
switch (event.event) {
|
|
254
|
+
case "run_started":
|
|
255
|
+
case "run_completed":
|
|
256
|
+
return event.runId;
|
|
257
|
+
case "step_started":
|
|
258
|
+
case "step_completed":
|
|
259
|
+
return event.stepId;
|
|
260
|
+
default:
|
|
261
|
+
return "unknown";
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function createPersistedEventId(event, eventIndex) {
|
|
265
|
+
const runId = sanitizeIdPart(event.runId);
|
|
266
|
+
const ev = sanitizeIdPart(event.event);
|
|
267
|
+
const node = sanitizeIdPart(nodeIdForEvent(event));
|
|
268
|
+
return `manual:${runId}:${ev}:${node}:${eventIndex}`;
|
|
269
|
+
}
|
|
270
|
+
function toIsoTimestamp(ms) {
|
|
271
|
+
if (typeof ms !== "number" || !Number.isFinite(ms)) {
|
|
272
|
+
return { iso: (/* @__PURE__ */ new Date(0)).toISOString(), invalidTimestamp: true };
|
|
273
|
+
}
|
|
274
|
+
return { iso: new Date(ms).toISOString(), invalidTimestamp: false };
|
|
275
|
+
}
|
|
276
|
+
function buildSource(options) {
|
|
277
|
+
return {
|
|
278
|
+
type: "manual",
|
|
279
|
+
name: options?.sourceName ?? "trace-event",
|
|
280
|
+
version: options?.sourceVersion ?? "0.1"
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function mapStepTypeToInspectKind(type) {
|
|
284
|
+
switch (type) {
|
|
285
|
+
case "run":
|
|
286
|
+
return "RUN";
|
|
287
|
+
case "llm":
|
|
288
|
+
return "LLM";
|
|
289
|
+
case "tool":
|
|
290
|
+
return "TOOL";
|
|
291
|
+
case "decision":
|
|
292
|
+
return "DECISION";
|
|
293
|
+
case "logic":
|
|
294
|
+
case "state":
|
|
295
|
+
case "custom":
|
|
296
|
+
return "LOGIC";
|
|
297
|
+
default:
|
|
298
|
+
return "LOGIC";
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function mapRunOrStepStatus(status) {
|
|
302
|
+
return status === "success" ? "ok" : "error";
|
|
303
|
+
}
|
|
304
|
+
function mapErrorInfo(error) {
|
|
305
|
+
if (!error?.message) {
|
|
306
|
+
return {};
|
|
307
|
+
}
|
|
308
|
+
const out = {
|
|
309
|
+
persisted: {
|
|
310
|
+
message: error.message,
|
|
311
|
+
name: "Error"
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
if (typeof error.stack === "string" && error.stack.length > 0) {
|
|
315
|
+
out.errorStack = error.stack;
|
|
316
|
+
}
|
|
317
|
+
return out;
|
|
318
|
+
}
|
|
319
|
+
function mapTokenUsageFromMetadata(metadata) {
|
|
320
|
+
return normalizeTokenUsage(metadata?.tokens);
|
|
321
|
+
}
|
|
322
|
+
function compactAttributes(entries) {
|
|
323
|
+
const out = {};
|
|
324
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
325
|
+
if (value !== void 0) {
|
|
326
|
+
out[key] = value;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
330
|
+
}
|
|
331
|
+
function traceEventToPersistedInspectEvent(event, options) {
|
|
332
|
+
const eventIndex = options?.eventIndex ?? 0;
|
|
333
|
+
const eventId = createPersistedEventId(event, eventIndex);
|
|
334
|
+
const source = buildSource(options);
|
|
335
|
+
const tsMain = toIsoTimestamp(event.timestamp);
|
|
336
|
+
switch (event.event) {
|
|
337
|
+
case "run_started": {
|
|
338
|
+
const tsStart = toIsoTimestamp(event.startTime);
|
|
339
|
+
const correlation = extractCorrelationMetadata(event.metadata);
|
|
340
|
+
const attributes = compactAttributes({
|
|
341
|
+
legacyEvent: "run_started",
|
|
342
|
+
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
343
|
+
correlationId: correlation?.correlationId,
|
|
344
|
+
requestId: correlation?.requestId,
|
|
345
|
+
decisionId: correlation?.decisionId,
|
|
346
|
+
groupId: correlation?.groupId,
|
|
347
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
348
|
+
});
|
|
349
|
+
return {
|
|
350
|
+
schemaVersion: "0.2",
|
|
351
|
+
eventId,
|
|
352
|
+
runId: event.runId,
|
|
353
|
+
kind: "RUN",
|
|
354
|
+
name: event.name,
|
|
355
|
+
status: "running",
|
|
356
|
+
timestamp: tsMain.iso,
|
|
357
|
+
startedAt: tsStart.iso,
|
|
358
|
+
confidence: "explicit",
|
|
359
|
+
source,
|
|
360
|
+
attributes
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
case "run_completed": {
|
|
364
|
+
const tsEnd = toIsoTimestamp(event.endTime);
|
|
365
|
+
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
366
|
+
const attributes = compactAttributes({
|
|
367
|
+
legacyEvent: "run_completed",
|
|
368
|
+
errorStack,
|
|
369
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
370
|
+
});
|
|
371
|
+
return {
|
|
372
|
+
schemaVersion: "0.2",
|
|
373
|
+
eventId,
|
|
374
|
+
runId: event.runId,
|
|
375
|
+
kind: "RUN",
|
|
376
|
+
name: "run",
|
|
377
|
+
status: mapRunOrStepStatus(event.status),
|
|
378
|
+
timestamp: tsMain.iso,
|
|
379
|
+
endedAt: tsEnd.iso,
|
|
380
|
+
durationMs: event.durationMs,
|
|
381
|
+
confidence: "explicit",
|
|
382
|
+
source,
|
|
383
|
+
attributes,
|
|
384
|
+
error
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
case "step_started": {
|
|
388
|
+
const tsStart = toIsoTimestamp(event.startTime);
|
|
389
|
+
const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
|
|
390
|
+
const attributes = compactAttributes({
|
|
391
|
+
legacyEvent: "step_started",
|
|
392
|
+
stepId: event.stepId,
|
|
393
|
+
stepType: event.type,
|
|
394
|
+
metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
|
|
395
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
|
|
396
|
+
});
|
|
397
|
+
const out = {
|
|
398
|
+
schemaVersion: "0.2",
|
|
399
|
+
eventId,
|
|
400
|
+
runId: event.runId,
|
|
401
|
+
kind: mapStepTypeToInspectKind(event.type),
|
|
402
|
+
name: event.name,
|
|
403
|
+
status: "running",
|
|
404
|
+
timestamp: tsMain.iso,
|
|
405
|
+
startedAt: tsStart.iso,
|
|
406
|
+
confidence: "explicit",
|
|
407
|
+
source,
|
|
408
|
+
attributes
|
|
409
|
+
};
|
|
410
|
+
if (event.parentId !== void 0) {
|
|
411
|
+
out.parentId = event.parentId;
|
|
412
|
+
}
|
|
413
|
+
if (tokenUsage !== void 0) {
|
|
414
|
+
out.tokenUsage = tokenUsage;
|
|
415
|
+
}
|
|
416
|
+
return out;
|
|
417
|
+
}
|
|
418
|
+
case "step_completed": {
|
|
419
|
+
const tsEnd = toIsoTimestamp(event.endTime);
|
|
420
|
+
const { persisted: error, errorStack } = mapErrorInfo(event.error);
|
|
421
|
+
const attributes = compactAttributes({
|
|
422
|
+
legacyEvent: "step_completed",
|
|
423
|
+
stepId: event.stepId,
|
|
424
|
+
errorStack,
|
|
425
|
+
invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
|
|
426
|
+
});
|
|
427
|
+
return {
|
|
428
|
+
schemaVersion: "0.2",
|
|
429
|
+
eventId,
|
|
430
|
+
runId: event.runId,
|
|
431
|
+
kind: "LOGIC",
|
|
432
|
+
name: event.stepId,
|
|
433
|
+
status: mapRunOrStepStatus(event.status),
|
|
434
|
+
timestamp: tsMain.iso,
|
|
435
|
+
endedAt: tsEnd.iso,
|
|
436
|
+
durationMs: event.durationMs,
|
|
437
|
+
confidence: "explicit",
|
|
438
|
+
source,
|
|
439
|
+
attributes,
|
|
440
|
+
error
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
default: {
|
|
444
|
+
const _exhaustive = event;
|
|
445
|
+
throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
function traceEventsToPersistedInspectEvents(events, options) {
|
|
450
|
+
return events.map(
|
|
451
|
+
(event, index) => traceEventToPersistedInspectEvent(event, { ...options, eventIndex: index })
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// packages/core/src/persisted/to-inspect-event.ts
|
|
456
|
+
function compactAttributes2(entries) {
|
|
457
|
+
const out = {};
|
|
458
|
+
for (const [key, value] of Object.entries(entries)) {
|
|
459
|
+
if (value !== void 0) {
|
|
460
|
+
out[key] = value;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
464
|
+
}
|
|
193
465
|
function parseIsoToMs(iso) {
|
|
466
|
+
const parsed = Date.parse(iso);
|
|
467
|
+
if (!Number.isFinite(parsed)) {
|
|
468
|
+
return { ms: 0, invalidTimestamp: true };
|
|
469
|
+
}
|
|
470
|
+
return { ms: parsed, invalidTimestamp: false };
|
|
471
|
+
}
|
|
472
|
+
function mapPersistedSourceToInspect(event) {
|
|
473
|
+
const attrs = event.attributes ?? {};
|
|
474
|
+
const sourceName = event.source.name;
|
|
475
|
+
if (sourceName === "pino") {
|
|
476
|
+
return {
|
|
477
|
+
type: "pino",
|
|
478
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
479
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
480
|
+
};
|
|
481
|
+
}
|
|
482
|
+
if (sourceName === "winston") {
|
|
483
|
+
return {
|
|
484
|
+
type: "winston",
|
|
485
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
486
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
const mapType = (t) => {
|
|
490
|
+
switch (t) {
|
|
491
|
+
case "manual":
|
|
492
|
+
return "manual";
|
|
493
|
+
case "json-log":
|
|
494
|
+
return "json-log";
|
|
495
|
+
case "log4js":
|
|
496
|
+
return "log4js";
|
|
497
|
+
case "adapter":
|
|
498
|
+
case "ai-sdk":
|
|
499
|
+
case "otel":
|
|
500
|
+
return "adapter";
|
|
501
|
+
default:
|
|
502
|
+
return "json-log";
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
return {
|
|
506
|
+
type: mapType(event.source.type),
|
|
507
|
+
file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
|
|
508
|
+
line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
function buildInspectAttributes(event) {
|
|
512
|
+
const attrs = event.attributes !== void 0 ? { ...event.attributes } : {};
|
|
513
|
+
if (event.inputSummary !== void 0) {
|
|
514
|
+
attrs.inputSummary = event.inputSummary;
|
|
515
|
+
}
|
|
516
|
+
if (event.outputSummary !== void 0) {
|
|
517
|
+
attrs.outputSummary = event.outputSummary;
|
|
518
|
+
}
|
|
519
|
+
if (event.error) {
|
|
520
|
+
if (event.error.name !== void 0) {
|
|
521
|
+
attrs.errorName = event.error.name;
|
|
522
|
+
}
|
|
523
|
+
attrs.errorMessage = event.error.message;
|
|
524
|
+
if (event.error.code !== void 0) {
|
|
525
|
+
attrs.errorCode = event.error.code;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
if (event.tokenUsage) {
|
|
529
|
+
attrs.tokens = { ...event.tokenUsage };
|
|
530
|
+
}
|
|
531
|
+
if (event.source.type === "ai-sdk" || event.source.type === "otel") {
|
|
532
|
+
attrs.originalSourceType = event.source.type;
|
|
533
|
+
}
|
|
534
|
+
if (event.source.name !== void 0) {
|
|
535
|
+
attrs.sourceName = event.source.name;
|
|
536
|
+
}
|
|
537
|
+
if (event.source.version !== void 0) {
|
|
538
|
+
attrs.sourceVersion = event.source.version;
|
|
539
|
+
}
|
|
540
|
+
return attrs;
|
|
541
|
+
}
|
|
542
|
+
function persistedInspectEventToInspectEvent(event) {
|
|
543
|
+
if (!isPersistedInspectEvent(event)) {
|
|
544
|
+
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
545
|
+
}
|
|
546
|
+
const ts = parseIsoToMs(event.timestamp);
|
|
547
|
+
const attrs = buildInspectAttributes(event);
|
|
548
|
+
if (ts.invalidTimestamp) {
|
|
549
|
+
attrs.invalidTimestamp = true;
|
|
550
|
+
}
|
|
551
|
+
let status;
|
|
552
|
+
if (event.status === "running" || event.status === "ok" || event.status === "error") {
|
|
553
|
+
status = event.status;
|
|
554
|
+
} else if (event.status === "unknown") {
|
|
555
|
+
attrs.persistedStatus = "unknown";
|
|
556
|
+
}
|
|
557
|
+
const out = {
|
|
558
|
+
eventId: event.eventId,
|
|
559
|
+
runId: event.runId,
|
|
560
|
+
name: event.name,
|
|
561
|
+
kind: event.kind,
|
|
562
|
+
timestamp: ts.ms,
|
|
563
|
+
confidence: event.confidence,
|
|
564
|
+
source: mapPersistedSourceToInspect(event),
|
|
565
|
+
attributes: compactAttributes2(attrs)
|
|
566
|
+
};
|
|
567
|
+
if (event.parentId !== void 0) {
|
|
568
|
+
out.parentId = event.parentId;
|
|
569
|
+
}
|
|
570
|
+
if (status !== void 0) {
|
|
571
|
+
out.status = status;
|
|
572
|
+
}
|
|
573
|
+
if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
|
|
574
|
+
out.durationMs = event.durationMs;
|
|
575
|
+
}
|
|
576
|
+
return out;
|
|
577
|
+
}
|
|
578
|
+
function persistedInspectEventsToInspectEvents(events, options) {
|
|
579
|
+
const skipInvalid = options?.skipInvalid === true;
|
|
580
|
+
const out = [];
|
|
581
|
+
for (const event of events) {
|
|
582
|
+
if (!isPersistedInspectEvent(event)) {
|
|
583
|
+
if (skipInvalid) {
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
|
|
587
|
+
}
|
|
588
|
+
out.push(persistedInspectEventToInspectEvent(event));
|
|
589
|
+
}
|
|
590
|
+
return out;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// packages/core/src/persisted/to-trace-event.ts
|
|
594
|
+
function parseIsoToMs2(iso) {
|
|
194
595
|
const parsed = Date.parse(iso);
|
|
195
596
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
196
597
|
}
|
|
@@ -232,10 +633,10 @@ function mapPersistedStatusToRunStatus(status) {
|
|
|
232
633
|
return void 0;
|
|
233
634
|
}
|
|
234
635
|
}
|
|
235
|
-
function mapPersistedError(error) {
|
|
636
|
+
function mapPersistedError(error, attributes) {
|
|
236
637
|
if (!error?.message) return void 0;
|
|
237
638
|
const out = { message: error.message };
|
|
238
|
-
const stack = typeof
|
|
639
|
+
const stack = typeof attributes?.errorStack === "string" && attributes.errorStack.length > 0 ? attributes.errorStack : void 0;
|
|
239
640
|
if (stack) {
|
|
240
641
|
out.stack = stack;
|
|
241
642
|
}
|
|
@@ -249,7 +650,9 @@ function mapTokenUsageToMetadata(tokenUsage, attributes) {
|
|
|
249
650
|
if (tokenUsage) {
|
|
250
651
|
metadata.tokens = {
|
|
251
652
|
...tokenUsage.input !== void 0 ? { input: tokenUsage.input } : {},
|
|
252
|
-
...tokenUsage.output !== void 0 ? { output: tokenUsage.output } : {}
|
|
653
|
+
...tokenUsage.output !== void 0 ? { output: tokenUsage.output } : {},
|
|
654
|
+
...tokenUsage.total !== void 0 ? { total: tokenUsage.total } : {},
|
|
655
|
+
...tokenUsage.cached !== void 0 ? { cached: tokenUsage.cached } : {}
|
|
253
656
|
};
|
|
254
657
|
}
|
|
255
658
|
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
@@ -288,9 +691,9 @@ function resolveStepType(event) {
|
|
|
288
691
|
return mapInspectKindToStepType(event.kind);
|
|
289
692
|
}
|
|
290
693
|
function resolveTimes(event) {
|
|
291
|
-
const timestamp =
|
|
292
|
-
const startTime = event.startedAt !== void 0 ?
|
|
293
|
-
let endTime = event.endedAt !== void 0 ?
|
|
694
|
+
const timestamp = parseIsoToMs2(event.timestamp);
|
|
695
|
+
const startTime = event.startedAt !== void 0 ? parseIsoToMs2(event.startedAt) : timestamp;
|
|
696
|
+
let endTime = event.endedAt !== void 0 ? parseIsoToMs2(event.endedAt) : timestamp;
|
|
294
697
|
if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0 && event.endedAt === void 0) {
|
|
295
698
|
endTime = startTime + event.durationMs;
|
|
296
699
|
}
|
|
@@ -322,7 +725,7 @@ function fromLegacyRunCompleted(event) {
|
|
|
322
725
|
endTime,
|
|
323
726
|
durationMs: event.durationMs ?? Math.max(0, endTime - timestamp)
|
|
324
727
|
};
|
|
325
|
-
const error = mapPersistedError(event.error);
|
|
728
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
326
729
|
if (error) out.error = error;
|
|
327
730
|
return out;
|
|
328
731
|
}
|
|
@@ -356,7 +759,7 @@ function fromLegacyStepCompleted(event) {
|
|
|
356
759
|
endTime,
|
|
357
760
|
durationMs: event.durationMs ?? Math.max(0, endTime - timestamp)
|
|
358
761
|
};
|
|
359
|
-
const error = mapPersistedError(event.error);
|
|
762
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
360
763
|
if (error) out.error = error;
|
|
361
764
|
return out;
|
|
362
765
|
}
|
|
@@ -387,7 +790,7 @@ function fromNativeRun(event) {
|
|
|
387
790
|
endTime,
|
|
388
791
|
durationMs: event.durationMs ?? Math.max(0, endTime - startTime)
|
|
389
792
|
};
|
|
390
|
-
const error = mapPersistedError(event.error);
|
|
793
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
391
794
|
if (error) completed.error = error;
|
|
392
795
|
out.push(completed);
|
|
393
796
|
}
|
|
@@ -429,7 +832,7 @@ function fromNativeStep(event) {
|
|
|
429
832
|
endTime,
|
|
430
833
|
durationMs: event.durationMs ?? Math.max(0, endTime - startTime)
|
|
431
834
|
};
|
|
432
|
-
const error = mapPersistedError(event.error);
|
|
835
|
+
const error = mapPersistedError(event.error, event.attributes);
|
|
433
836
|
if (error) completed.error = error;
|
|
434
837
|
out.push(completed);
|
|
435
838
|
}
|
|
@@ -541,7 +944,15 @@ var TreeBuilder = class {
|
|
|
541
944
|
return out;
|
|
542
945
|
}
|
|
543
946
|
};
|
|
544
|
-
|
|
947
|
+
|
|
948
|
+
// packages/core/src/persisted/tree-bridge.ts
|
|
949
|
+
function persistedInspectEventsToRunTrees(events, options) {
|
|
950
|
+
const inspectEvents = persistedInspectEventsToInspectEvents(events, {
|
|
951
|
+
skipInvalid: options?.skipInvalid
|
|
952
|
+
});
|
|
953
|
+
return new TreeBuilder().build(inspectEvents);
|
|
954
|
+
}
|
|
955
|
+
function isRecord4(v) {
|
|
545
956
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
546
957
|
}
|
|
547
958
|
function isNonEmptyStringArray(v) {
|
|
@@ -553,7 +964,7 @@ function validateRedact(redact) {
|
|
|
553
964
|
}
|
|
554
965
|
for (const r of redact) {
|
|
555
966
|
if (typeof r === "string") continue;
|
|
556
|
-
if (!
|
|
967
|
+
if (!isRecord4(r)) {
|
|
557
968
|
throw new Error("Invalid config: redact entries must be strings or objects");
|
|
558
969
|
}
|
|
559
970
|
if (typeof r.key !== "string" || r.key.trim() === "") {
|
|
@@ -572,7 +983,7 @@ function validateRedact(redact) {
|
|
|
572
983
|
}
|
|
573
984
|
}
|
|
574
985
|
function validateMappings(mappings) {
|
|
575
|
-
if (!
|
|
986
|
+
if (!isRecord4(mappings)) {
|
|
576
987
|
throw new Error("Invalid config: mappings must be an object");
|
|
577
988
|
}
|
|
578
989
|
}
|
|
@@ -622,7 +1033,7 @@ async function loadLogIngestConfig(configPath) {
|
|
|
622
1033
|
const msg = e instanceof Error ? e.message : String(e);
|
|
623
1034
|
throw new Error(`Invalid JSON in config file: ${configPath} (${msg})`);
|
|
624
1035
|
}
|
|
625
|
-
if (!
|
|
1036
|
+
if (!isRecord4(parsed)) {
|
|
626
1037
|
throw new Error("Invalid config: expected a JSON object at top-level");
|
|
627
1038
|
}
|
|
628
1039
|
const user = parsed;
|
|
@@ -659,7 +1070,7 @@ async function loadLogIngestConfig(configPath) {
|
|
|
659
1070
|
}
|
|
660
1071
|
return mergeLogIngestConfig(DEFAULT_LOG_INGEST_CONFIG, user);
|
|
661
1072
|
}
|
|
662
|
-
function
|
|
1073
|
+
function isRecord5(v) {
|
|
663
1074
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
664
1075
|
}
|
|
665
1076
|
var JsonLogParser = class {
|
|
@@ -684,7 +1095,7 @@ var JsonLogParser = class {
|
|
|
684
1095
|
});
|
|
685
1096
|
continue;
|
|
686
1097
|
}
|
|
687
|
-
if (!
|
|
1098
|
+
if (!isRecord5(parsed)) {
|
|
688
1099
|
warnings.push({
|
|
689
1100
|
code: "MALFORMED_JSON",
|
|
690
1101
|
message: "JSON log line must be an object",
|
|
@@ -715,7 +1126,7 @@ var JsonLogParser = class {
|
|
|
715
1126
|
return this.parseLines(lines, filePath);
|
|
716
1127
|
}
|
|
717
1128
|
};
|
|
718
|
-
function
|
|
1129
|
+
function isRecord6(v) {
|
|
719
1130
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
720
1131
|
}
|
|
721
1132
|
function findLastJsonObjectSubstring(line) {
|
|
@@ -791,7 +1202,7 @@ var Log4jsParser = class {
|
|
|
791
1202
|
});
|
|
792
1203
|
continue;
|
|
793
1204
|
}
|
|
794
|
-
if (!
|
|
1205
|
+
if (!isRecord6(parsed)) {
|
|
795
1206
|
warnings.push({
|
|
796
1207
|
code: "UNSUPPORTED_LOG4JS_PAYLOAD",
|
|
797
1208
|
message: "Embedded JSON payload must be an object",
|
|
@@ -869,7 +1280,7 @@ var DEFAULT_REDACT_KEYS = [
|
|
|
869
1280
|
"secret",
|
|
870
1281
|
"email"
|
|
871
1282
|
];
|
|
872
|
-
function
|
|
1283
|
+
function isRecord7(v) {
|
|
873
1284
|
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
874
1285
|
}
|
|
875
1286
|
function toKey(s) {
|
|
@@ -942,7 +1353,7 @@ var Redactor = class {
|
|
|
942
1353
|
if (Array.isArray(value)) {
|
|
943
1354
|
return value.map((v) => this.#redactNested(v));
|
|
944
1355
|
}
|
|
945
|
-
if (
|
|
1356
|
+
if (isRecord7(value)) {
|
|
946
1357
|
const out = {};
|
|
947
1358
|
for (const [k, v] of Object.entries(value)) {
|
|
948
1359
|
out[k] = this.redactValue(k, v);
|
|
@@ -1699,66 +2110,78 @@ function truncateStringForProfile(value, key, maxMetadataValueLength, maxPreview
|
|
|
1699
2110
|
}
|
|
1700
2111
|
|
|
1701
2112
|
// packages/core/src/read-trace.ts
|
|
1702
|
-
function
|
|
2113
|
+
function isRecord8(value) {
|
|
1703
2114
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1704
2115
|
}
|
|
1705
2116
|
function detectLineFormat(parsed) {
|
|
1706
|
-
if (!
|
|
2117
|
+
if (!isRecord8(parsed)) return "unknown";
|
|
1707
2118
|
if (parsed.schemaVersion === "0.1") return "0.1";
|
|
1708
2119
|
if (parsed.schemaVersion === "0.2") return "0.2";
|
|
1709
2120
|
return "unknown";
|
|
1710
2121
|
}
|
|
1711
2122
|
function parseTraceJsonl(raw, options = {}) {
|
|
1712
2123
|
const validate = options.validate ?? isTraceEvent;
|
|
2124
|
+
const emitWarning = (message) => {
|
|
2125
|
+
if (options.warnings !== false) warn(message);
|
|
2126
|
+
};
|
|
1713
2127
|
const persisted = [];
|
|
1714
2128
|
const traceEvents = [];
|
|
2129
|
+
const rows = [];
|
|
2130
|
+
let sourceEventCount = 0;
|
|
1715
2131
|
let saw01 = false;
|
|
1716
2132
|
let saw02 = false;
|
|
2133
|
+
let lineNumber = 0;
|
|
1717
2134
|
for (const line of raw.split(/\r?\n/)) {
|
|
2135
|
+
lineNumber += 1;
|
|
1718
2136
|
const trimmed = line.trim();
|
|
1719
2137
|
if (trimmed === "") continue;
|
|
1720
2138
|
let parsed;
|
|
1721
2139
|
try {
|
|
1722
2140
|
parsed = JSON.parse(trimmed);
|
|
1723
2141
|
} catch {
|
|
1724
|
-
|
|
2142
|
+
emitWarning("Skipped invalid JSON line in trace file");
|
|
1725
2143
|
continue;
|
|
1726
2144
|
}
|
|
1727
2145
|
const format2 = detectLineFormat(parsed);
|
|
1728
2146
|
if (format2 === "0.1") {
|
|
1729
2147
|
saw01 = true;
|
|
1730
2148
|
if (validate(parsed)) {
|
|
2149
|
+
sourceEventCount += 1;
|
|
1731
2150
|
traceEvents.push(parsed);
|
|
2151
|
+
rows.push({ format: "0.1", event: parsed, sourceLine: lineNumber });
|
|
1732
2152
|
} else {
|
|
1733
|
-
|
|
2153
|
+
emitWarning("Skipped invalid trace event line in trace file");
|
|
1734
2154
|
}
|
|
1735
2155
|
continue;
|
|
1736
2156
|
}
|
|
1737
2157
|
if (format2 === "0.2") {
|
|
1738
2158
|
saw02 = true;
|
|
1739
2159
|
if (isPersistedInspectEvent(parsed)) {
|
|
2160
|
+
sourceEventCount += 1;
|
|
1740
2161
|
persisted.push(parsed);
|
|
2162
|
+
rows.push({ format: "0.2", event: parsed, sourceLine: lineNumber });
|
|
2163
|
+
traceEvents.push(...persistedInspectEventToTraceEvents(parsed));
|
|
1741
2164
|
} else {
|
|
1742
|
-
|
|
2165
|
+
emitWarning("Skipped invalid persisted inspect event line in trace file");
|
|
1743
2166
|
}
|
|
1744
2167
|
continue;
|
|
1745
2168
|
}
|
|
1746
|
-
|
|
2169
|
+
emitWarning("Skipped trace line with unknown schemaVersion");
|
|
1747
2170
|
}
|
|
1748
2171
|
if (saw01 && saw02) {
|
|
1749
|
-
|
|
2172
|
+
emitWarning(
|
|
2173
|
+
"Trace file mixes schemaVersion 0.1 and 0.2 lines; normalizing all rows"
|
|
2174
|
+
);
|
|
1750
2175
|
}
|
|
1751
|
-
const converted = persisted.length > 0 ? persistedInspectEventsToTraceEvents(persisted) : [];
|
|
1752
|
-
const events = [...traceEvents, ...converted];
|
|
1753
2176
|
let format = "empty";
|
|
1754
2177
|
if (saw01 && saw02) format = "mixed";
|
|
1755
2178
|
else if (saw01) format = "0.1";
|
|
1756
2179
|
else if (saw02) format = "0.2";
|
|
1757
|
-
return { format, events, persisted };
|
|
2180
|
+
return { format, sourceEventCount, events: traceEvents, persisted, rows };
|
|
1758
2181
|
}
|
|
1759
2182
|
|
|
1760
2183
|
// packages/core/src/storage.ts
|
|
1761
|
-
function
|
|
2184
|
+
function isRecord9(value) {
|
|
1762
2185
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1763
2186
|
}
|
|
1764
2187
|
function nonEmptyString(value) {
|
|
@@ -1769,7 +2192,7 @@ function finiteNumber(value) {
|
|
|
1769
2192
|
}
|
|
1770
2193
|
function optionalErrorInfo(value) {
|
|
1771
2194
|
if (value === void 0) return true;
|
|
1772
|
-
if (!
|
|
2195
|
+
if (!isRecord9(value)) return false;
|
|
1773
2196
|
if (typeof value.message !== "string") return false;
|
|
1774
2197
|
if ("stack" in value && value.stack !== void 0) {
|
|
1775
2198
|
if (typeof value.stack !== "string") return false;
|
|
@@ -1777,7 +2200,7 @@ function optionalErrorInfo(value) {
|
|
|
1777
2200
|
return true;
|
|
1778
2201
|
}
|
|
1779
2202
|
function validateEvent(event) {
|
|
1780
|
-
if (!
|
|
2203
|
+
if (!isRecord9(event)) return false;
|
|
1781
2204
|
if (event.schemaVersion !== "0.1") return false;
|
|
1782
2205
|
if (!finiteNumber(event.timestamp)) return false;
|
|
1783
2206
|
if (typeof event.event !== "string") return false;
|
|
@@ -1786,7 +2209,7 @@ function validateEvent(event) {
|
|
|
1786
2209
|
if (!nonEmptyString(event.runId) || !nonEmptyString(event.name) || !finiteNumber(event.startTime)) {
|
|
1787
2210
|
return false;
|
|
1788
2211
|
}
|
|
1789
|
-
if (event.metadata !== void 0 && !
|
|
2212
|
+
if (event.metadata !== void 0 && !isRecord9(event.metadata)) {
|
|
1790
2213
|
return false;
|
|
1791
2214
|
}
|
|
1792
2215
|
return true;
|
|
@@ -1801,7 +2224,7 @@ function validateEvent(event) {
|
|
|
1801
2224
|
if (event.parentId !== void 0 && typeof event.parentId !== "string") {
|
|
1802
2225
|
return false;
|
|
1803
2226
|
}
|
|
1804
|
-
if (event.metadata !== void 0 && !
|
|
2227
|
+
if (event.metadata !== void 0 && !isRecord9(event.metadata)) {
|
|
1805
2228
|
return false;
|
|
1806
2229
|
}
|
|
1807
2230
|
return true;
|
|
@@ -1859,27 +2282,15 @@ async function writeTraceEvent(event, traceDir) {
|
|
|
1859
2282
|
}
|
|
1860
2283
|
warn("Failed to append trace event to fallback directory");
|
|
1861
2284
|
}
|
|
1862
|
-
async function
|
|
2285
|
+
async function readTraceEventsFromFile(filePath) {
|
|
1863
2286
|
try {
|
|
1864
|
-
const
|
|
1865
|
-
return
|
|
2287
|
+
const raw = await promises.readFile(filePath, "utf-8");
|
|
2288
|
+
return parseTraceJsonl(raw, { validate: validateEvent }).events;
|
|
1866
2289
|
} catch (e) {
|
|
1867
2290
|
if (e && typeof e === "object" && "code" in e && e.code === "ENOENT") {
|
|
1868
|
-
return void 0;
|
|
1869
|
-
}
|
|
1870
|
-
warn("Unexpected error reading trace file", e);
|
|
1871
|
-
return void 0;
|
|
1872
|
-
}
|
|
1873
|
-
}
|
|
1874
|
-
async function readTraceEvents(runId, traceDir) {
|
|
1875
|
-
try {
|
|
1876
|
-
const raw = await readTraceFile(runId, traceDir);
|
|
1877
|
-
if (raw === void 0) {
|
|
1878
2291
|
return [];
|
|
1879
2292
|
}
|
|
1880
|
-
|
|
1881
|
-
} catch (e) {
|
|
1882
|
-
warn("Failed to read trace events", e);
|
|
2293
|
+
warn("Failed to read trace events from file", e);
|
|
1883
2294
|
return [];
|
|
1884
2295
|
}
|
|
1885
2296
|
}
|
|
@@ -1888,7 +2299,7 @@ async function readTraceEvents(runId, traceDir) {
|
|
|
1888
2299
|
var DEFAULT_MAX_METADATA_VALUE_LENGTH = 2e3;
|
|
1889
2300
|
var DEFAULT_MAX_PREVIEW_LENGTH = 500;
|
|
1890
2301
|
var DEFAULT_MAX_EVENT_BYTES = 65536;
|
|
1891
|
-
function
|
|
2302
|
+
function isRecord10(value) {
|
|
1892
2303
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1893
2304
|
}
|
|
1894
2305
|
function isPreviewKey2(key) {
|
|
@@ -1908,21 +2319,10 @@ function resolveTraceSafetyOptions(options) {
|
|
|
1908
2319
|
{
|
|
1909
2320
|
redactEnabled = true;
|
|
1910
2321
|
}
|
|
1911
|
-
const profile =
|
|
2322
|
+
const profile = "local";
|
|
1912
2323
|
const resolvedProfile = resolveRedactionProfile(profile);
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
let maxMetadataValueLength = userMaxMetadata ?? DEFAULT_MAX_METADATA_VALUE_LENGTH;
|
|
1916
|
-
let maxPreviewLength = userMaxPreview ?? DEFAULT_MAX_PREVIEW_LENGTH;
|
|
1917
|
-
if (redactEnabled && profile !== "local") {
|
|
1918
|
-
const capped = applyProfileMetadataCaps(
|
|
1919
|
-
maxMetadataValueLength,
|
|
1920
|
-
maxPreviewLength,
|
|
1921
|
-
resolvedProfile
|
|
1922
|
-
);
|
|
1923
|
-
maxMetadataValueLength = capped.maxMetadataValueLength;
|
|
1924
|
-
maxPreviewLength = capped.maxPreviewLength;
|
|
1925
|
-
}
|
|
2324
|
+
let maxMetadataValueLength = DEFAULT_MAX_METADATA_VALUE_LENGTH;
|
|
2325
|
+
let maxPreviewLength = DEFAULT_MAX_PREVIEW_LENGTH;
|
|
1926
2326
|
return {
|
|
1927
2327
|
redactEnabled,
|
|
1928
2328
|
redactionRules,
|
|
@@ -1930,11 +2330,26 @@ function resolveTraceSafetyOptions(options) {
|
|
|
1930
2330
|
profileExtraKeys: redactEnabled ? resolvedProfile.extraKeys : [],
|
|
1931
2331
|
maxMetadataValueLength,
|
|
1932
2332
|
maxPreviewLength,
|
|
1933
|
-
maxEventBytes:
|
|
2333
|
+
maxEventBytes: DEFAULT_MAX_EVENT_BYTES
|
|
1934
2334
|
};
|
|
1935
2335
|
}
|
|
1936
2336
|
function boundMetadataValue(key, value, opts, seen, depth) {
|
|
1937
2337
|
if (depth > 32) return "[MaxDepth]";
|
|
2338
|
+
if (typeof value === "bigint") {
|
|
2339
|
+
return `${value.toString()}n`;
|
|
2340
|
+
}
|
|
2341
|
+
if (typeof value === "function") {
|
|
2342
|
+
return "[Function]";
|
|
2343
|
+
}
|
|
2344
|
+
if (typeof value === "symbol") {
|
|
2345
|
+
return "[Symbol]";
|
|
2346
|
+
}
|
|
2347
|
+
if (typeof value === "number" && !Number.isFinite(value)) {
|
|
2348
|
+
return String(value);
|
|
2349
|
+
}
|
|
2350
|
+
if (value === void 0) {
|
|
2351
|
+
return null;
|
|
2352
|
+
}
|
|
1938
2353
|
if (value === null || typeof value !== "object") {
|
|
1939
2354
|
if (typeof value === "string") {
|
|
1940
2355
|
const max = isPreviewKey2(key) ? opts.maxPreviewLength : opts.maxMetadataValueLength;
|
|
@@ -1956,8 +2371,12 @@ function boundMetadataValue(key, value, opts, seen, depth) {
|
|
|
1956
2371
|
}
|
|
1957
2372
|
const record = value;
|
|
1958
2373
|
const out = {};
|
|
1959
|
-
|
|
1960
|
-
|
|
2374
|
+
try {
|
|
2375
|
+
for (const [k, v] of Object.entries(record)) {
|
|
2376
|
+
out[k] = boundMetadataValue(k, v, opts, seen, depth + 1);
|
|
2377
|
+
}
|
|
2378
|
+
} catch {
|
|
2379
|
+
return { truncated: true, reason: "metadataEnumerationFailed" };
|
|
1961
2380
|
}
|
|
1962
2381
|
return out;
|
|
1963
2382
|
}
|
|
@@ -1971,16 +2390,25 @@ function redactMetadata(metadata, opts) {
|
|
|
1971
2390
|
}
|
|
1972
2391
|
function prepareMetadataForDisk(metadata, opts) {
|
|
1973
2392
|
try {
|
|
1974
|
-
const
|
|
1975
|
-
|
|
2393
|
+
const preBounded = boundMetadataValue(
|
|
2394
|
+
"metadata",
|
|
2395
|
+
metadata,
|
|
2396
|
+
opts,
|
|
2397
|
+
/* @__PURE__ */ new WeakSet(),
|
|
2398
|
+
0
|
|
2399
|
+
);
|
|
2400
|
+
const redacted = redactMetadata(
|
|
2401
|
+
isRecord10(preBounded) ? preBounded : {},
|
|
2402
|
+
opts
|
|
2403
|
+
);
|
|
1976
2404
|
const bounded = boundMetadataValue(
|
|
1977
2405
|
"metadata",
|
|
1978
2406
|
redacted,
|
|
1979
2407
|
opts,
|
|
1980
|
-
|
|
2408
|
+
/* @__PURE__ */ new WeakSet(),
|
|
1981
2409
|
0
|
|
1982
2410
|
);
|
|
1983
|
-
return
|
|
2411
|
+
return isRecord10(bounded) ? bounded : {};
|
|
1984
2412
|
} catch {
|
|
1985
2413
|
return { truncated: true, reason: "metadataPreparationFailed" };
|
|
1986
2414
|
}
|
|
@@ -2230,12 +2658,10 @@ var TraceDirectory = class {
|
|
|
2230
2658
|
function isFiniteNumber2(v) {
|
|
2231
2659
|
return typeof v === "number" && Number.isFinite(v);
|
|
2232
2660
|
}
|
|
2233
|
-
function
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
return void 0;
|
|
2238
|
-
}
|
|
2661
|
+
function parseIsoToMs3(value) {
|
|
2662
|
+
if (value === void 0) return void 0;
|
|
2663
|
+
const parsed = Date.parse(value);
|
|
2664
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
2239
2665
|
}
|
|
2240
2666
|
async function extractMetadata(filePath, _quickScan) {
|
|
2241
2667
|
const stats = await promises.stat(filePath);
|
|
@@ -2244,8 +2670,7 @@ async function extractMetadata(filePath, _quickScan) {
|
|
|
2244
2670
|
runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
|
|
2245
2671
|
}
|
|
2246
2672
|
const raw = await promises.readFile(filePath, "utf-8");
|
|
2247
|
-
const
|
|
2248
|
-
let eventCount = 0;
|
|
2673
|
+
const parsedTrace = parseTraceJsonl(raw, { warnings: false });
|
|
2249
2674
|
let runId;
|
|
2250
2675
|
let name;
|
|
2251
2676
|
let startedAt;
|
|
@@ -2255,16 +2680,32 @@ async function extractMetadata(filePath, _quickScan) {
|
|
|
2255
2680
|
let hasRunCompleted = false;
|
|
2256
2681
|
let runCompletedStatus;
|
|
2257
2682
|
let anyStepError = false;
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2683
|
+
const anyKnownEvent = parsedTrace.sourceEventCount > 0;
|
|
2684
|
+
let persistedStatus;
|
|
2685
|
+
const persistedRun = parsedTrace.persisted.find(
|
|
2686
|
+
(event) => event.kind === "RUN"
|
|
2687
|
+
);
|
|
2688
|
+
if (persistedRun) {
|
|
2689
|
+
runId = persistedRun.runId;
|
|
2690
|
+
if (persistedRun.name.trim() !== "") {
|
|
2691
|
+
name = persistedRun.name;
|
|
2692
|
+
}
|
|
2693
|
+
startedAt = parseIsoToMs3(persistedRun.startedAt) ?? parseIsoToMs3(persistedRun.timestamp);
|
|
2694
|
+
endedAt = parseIsoToMs3(persistedRun.endedAt);
|
|
2695
|
+
if (isFiniteNumber2(persistedRun.durationMs)) {
|
|
2696
|
+
explicitDurationMs = persistedRun.durationMs;
|
|
2697
|
+
if (endedAt === void 0 && startedAt !== void 0) {
|
|
2698
|
+
endedAt = startedAt + persistedRun.durationMs;
|
|
2699
|
+
}
|
|
2700
|
+
}
|
|
2701
|
+
if (persistedRun.status === "ok") persistedStatus = "success";
|
|
2702
|
+
else if (persistedRun.status === "error") persistedStatus = "error";
|
|
2703
|
+
else if (persistedRun.status === "running") persistedStatus = "running";
|
|
2704
|
+
else if (persistedRun.status === "unknown") persistedStatus = "unknown";
|
|
2705
|
+
} else {
|
|
2706
|
+
runId = parsedTrace.persisted[0]?.runId;
|
|
2707
|
+
}
|
|
2708
|
+
for (const e of parsedTrace.events) {
|
|
2268
2709
|
if (runId === void 0 && typeof e.runId === "string") {
|
|
2269
2710
|
runId = e.runId;
|
|
2270
2711
|
}
|
|
@@ -2301,6 +2742,8 @@ async function extractMetadata(filePath, _quickScan) {
|
|
|
2301
2742
|
status = runCompletedStatus;
|
|
2302
2743
|
} else if (anyStepError) {
|
|
2303
2744
|
status = "error";
|
|
2745
|
+
} else if (persistedStatus !== void 0) {
|
|
2746
|
+
status = persistedStatus;
|
|
2304
2747
|
} else if (hasRunStarted && !hasRunCompleted) {
|
|
2305
2748
|
status = "running";
|
|
2306
2749
|
} else if (anyKnownEvent) {
|
|
@@ -2316,12 +2759,15 @@ async function extractMetadata(filePath, _quickScan) {
|
|
|
2316
2759
|
startedAt,
|
|
2317
2760
|
endedAt,
|
|
2318
2761
|
durationMs,
|
|
2319
|
-
eventCount,
|
|
2762
|
+
eventCount: parsedTrace.sourceEventCount,
|
|
2320
2763
|
filePath,
|
|
2321
2764
|
fileSize: stats.size,
|
|
2322
2765
|
createdAt: stats.birthtime
|
|
2323
2766
|
};
|
|
2324
2767
|
}
|
|
2768
|
+
function isNonNegativeFiniteNumber(value) {
|
|
2769
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
2770
|
+
}
|
|
2325
2771
|
function buildRunSummary(events) {
|
|
2326
2772
|
const started = events.find(
|
|
2327
2773
|
(e) => e.event === "run_started"
|
|
@@ -2344,8 +2790,10 @@ function buildRunSummary(events) {
|
|
|
2344
2790
|
name: s.name,
|
|
2345
2791
|
status: "running",
|
|
2346
2792
|
parentId: s.parentId,
|
|
2347
|
-
tokensInput:
|
|
2348
|
-
tokensOutput:
|
|
2793
|
+
tokensInput: isNonNegativeFiniteNumber(s.metadata?.tokens?.input) ? s.metadata.tokens.input : void 0,
|
|
2794
|
+
tokensOutput: isNonNegativeFiniteNumber(s.metadata?.tokens?.output) ? s.metadata.tokens.output : void 0,
|
|
2795
|
+
tokensTotal: isNonNegativeFiniteNumber(s.metadata?.tokens?.total) ? s.metadata.tokens.total : void 0,
|
|
2796
|
+
tokensCached: isNonNegativeFiniteNumber(s.metadata?.tokens?.cached) ? s.metadata.tokens.cached : void 0
|
|
2349
2797
|
});
|
|
2350
2798
|
}
|
|
2351
2799
|
}
|
|
@@ -2367,7 +2815,11 @@ function buildRunSummary(events) {
|
|
|
2367
2815
|
let longestStep;
|
|
2368
2816
|
let totalTokensInput = 0;
|
|
2369
2817
|
let totalTokensOutput = 0;
|
|
2370
|
-
let
|
|
2818
|
+
let totalTokensTotal = 0;
|
|
2819
|
+
let totalTokensCached = 0;
|
|
2820
|
+
let tokenBearingSteps = 0;
|
|
2821
|
+
let stepsWithKnownTotal = 0;
|
|
2822
|
+
let hasCachedTokens = false;
|
|
2371
2823
|
const depthCache = /* @__PURE__ */ new Map();
|
|
2372
2824
|
const computeDepth = (stepId) => {
|
|
2373
2825
|
const cached = depthCache.get(stepId);
|
|
@@ -2396,10 +2848,21 @@ function buildRunSummary(events) {
|
|
|
2396
2848
|
longestStep = { name: s.name, durationMs: s.durationMs, type: s.type };
|
|
2397
2849
|
}
|
|
2398
2850
|
}
|
|
2399
|
-
if (
|
|
2400
|
-
|
|
2401
|
-
if (
|
|
2402
|
-
if (
|
|
2851
|
+
if (s.tokensInput !== void 0 || s.tokensOutput !== void 0 || s.tokensTotal !== void 0 || s.tokensCached !== void 0) {
|
|
2852
|
+
tokenBearingSteps += 1;
|
|
2853
|
+
if (s.tokensInput !== void 0) totalTokensInput += s.tokensInput;
|
|
2854
|
+
if (s.tokensOutput !== void 0) totalTokensOutput += s.tokensOutput;
|
|
2855
|
+
if (s.tokensTotal !== void 0) {
|
|
2856
|
+
totalTokensTotal += s.tokensTotal;
|
|
2857
|
+
stepsWithKnownTotal += 1;
|
|
2858
|
+
} else if (s.tokensInput !== void 0 && s.tokensOutput !== void 0) {
|
|
2859
|
+
totalTokensTotal += s.tokensInput + s.tokensOutput;
|
|
2860
|
+
stepsWithKnownTotal += 1;
|
|
2861
|
+
}
|
|
2862
|
+
if (s.tokensCached !== void 0) {
|
|
2863
|
+
totalTokensCached += s.tokensCached;
|
|
2864
|
+
hasCachedTokens = true;
|
|
2865
|
+
}
|
|
2403
2866
|
}
|
|
2404
2867
|
}
|
|
2405
2868
|
const summary = {
|
|
@@ -2414,7 +2877,14 @@ function buildRunSummary(events) {
|
|
|
2414
2877
|
errorSteps,
|
|
2415
2878
|
maxDepth,
|
|
2416
2879
|
...longestStep ? { longestStep } : {},
|
|
2417
|
-
...
|
|
2880
|
+
...tokenBearingSteps > 0 ? {
|
|
2881
|
+
totalTokens: {
|
|
2882
|
+
input: totalTokensInput,
|
|
2883
|
+
output: totalTokensOutput,
|
|
2884
|
+
...stepsWithKnownTotal === tokenBearingSteps ? { total: totalTokensTotal } : {},
|
|
2885
|
+
...hasCachedTokens ? { cached: totalTokensCached } : {}
|
|
2886
|
+
}
|
|
2887
|
+
} : {}
|
|
2418
2888
|
};
|
|
2419
2889
|
return summary;
|
|
2420
2890
|
}
|
|
@@ -2719,9 +3189,17 @@ function renderRunWhat(summary, options = {}) {
|
|
|
2719
3189
|
`Status: ${summary.status} \xB7 Duration: ${duration} \xB7 Steps: ${summary.totalSteps} (${stepMixLine(summary)})`
|
|
2720
3190
|
);
|
|
2721
3191
|
if (summary.totalTokens) {
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
3192
|
+
const tokenParts = [
|
|
3193
|
+
`${summary.totalTokens.input} in`,
|
|
3194
|
+
`${summary.totalTokens.output} out`
|
|
3195
|
+
];
|
|
3196
|
+
if (summary.totalTokens.total !== void 0) {
|
|
3197
|
+
tokenParts.push(`${summary.totalTokens.total} total`);
|
|
3198
|
+
}
|
|
3199
|
+
if (summary.totalTokens.cached !== void 0) {
|
|
3200
|
+
tokenParts.push(`${summary.totalTokens.cached} cached`);
|
|
3201
|
+
}
|
|
3202
|
+
lines.push(`Tokens: ${tokenParts.join(" / ")}`);
|
|
2725
3203
|
}
|
|
2726
3204
|
if (showCorrelation && summary.correlation) {
|
|
2727
3205
|
const parts = [];
|
|
@@ -2801,7 +3279,7 @@ function stableJson(value, pretty) {
|
|
|
2801
3279
|
const sorted = sortKeysDeep(value);
|
|
2802
3280
|
return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
|
|
2803
3281
|
}
|
|
2804
|
-
function
|
|
3282
|
+
function compactAttributes3(attrs, options) {
|
|
2805
3283
|
if (attrs === void 0) return {};
|
|
2806
3284
|
const maxLen = options?.maxLength ?? 500;
|
|
2807
3285
|
const redacted = options?.redacted ?? true;
|
|
@@ -2946,7 +3424,7 @@ function exportHtml(tree, options) {
|
|
|
2946
3424
|
attrsHtml += "<h2>Attributes (bounded)</h2>";
|
|
2947
3425
|
for (const n of flat) {
|
|
2948
3426
|
if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
|
|
2949
|
-
const compact =
|
|
3427
|
+
const compact = compactAttributes3(n.event.attributes, {
|
|
2950
3428
|
maxLength: maxLen,
|
|
2951
3429
|
redacted
|
|
2952
3430
|
});
|
|
@@ -3097,7 +3575,7 @@ function exportMarkdown(tree, options) {
|
|
|
3097
3575
|
lines.push("");
|
|
3098
3576
|
for (const n of flat) {
|
|
3099
3577
|
if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
|
|
3100
|
-
const compact =
|
|
3578
|
+
const compact = compactAttributes3(n.event.attributes, {
|
|
3101
3579
|
maxLength: maxLen,
|
|
3102
3580
|
redacted
|
|
3103
3581
|
});
|
|
@@ -3260,7 +3738,7 @@ function manualTraceEventsToRunTree(events) {
|
|
|
3260
3738
|
}
|
|
3261
3739
|
|
|
3262
3740
|
// packages/core/src/exporters/redact-export.ts
|
|
3263
|
-
function
|
|
3741
|
+
function isRecord11(value) {
|
|
3264
3742
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3265
3743
|
}
|
|
3266
3744
|
function deepClone(value) {
|
|
@@ -3334,7 +3812,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
|
|
|
3334
3812
|
0
|
|
3335
3813
|
);
|
|
3336
3814
|
const err = bounded.error;
|
|
3337
|
-
if (
|
|
3815
|
+
if (isRecord11(err) && typeof err.message === "string") {
|
|
3338
3816
|
bounded.error = {
|
|
3339
3817
|
...err,
|
|
3340
3818
|
message: truncateStringForProfile(
|
|
@@ -3355,6 +3833,89 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
|
|
|
3355
3833
|
}
|
|
3356
3834
|
return bounded;
|
|
3357
3835
|
}
|
|
3836
|
+
function redactErrorInfo(error, redactor, maxMetadataValueLength, maxPreviewLength) {
|
|
3837
|
+
if (error === void 0) return void 0;
|
|
3838
|
+
const record = redactEventAttributes(
|
|
3839
|
+
{ error },
|
|
3840
|
+
redactor,
|
|
3841
|
+
maxMetadataValueLength,
|
|
3842
|
+
maxPreviewLength
|
|
3843
|
+
);
|
|
3844
|
+
const redacted = record?.error;
|
|
3845
|
+
if (!isRecord11(redacted) || typeof redacted.message !== "string") {
|
|
3846
|
+
return void 0;
|
|
3847
|
+
}
|
|
3848
|
+
return {
|
|
3849
|
+
message: redacted.message,
|
|
3850
|
+
...typeof redacted.stack === "string" ? { stack: redacted.stack } : {}
|
|
3851
|
+
};
|
|
3852
|
+
}
|
|
3853
|
+
function redactTraceEventsForReport(events, options) {
|
|
3854
|
+
const profile = options?.redactionProfile ?? "local";
|
|
3855
|
+
if (profile === "local") {
|
|
3856
|
+
return deepClone(events);
|
|
3857
|
+
}
|
|
3858
|
+
const resolved = resolveRedactionProfile(profile);
|
|
3859
|
+
const { maxMetadataValueLength, maxPreviewLength } = applyProfileMetadataCaps(
|
|
3860
|
+
2e3,
|
|
3861
|
+
500,
|
|
3862
|
+
resolved
|
|
3863
|
+
);
|
|
3864
|
+
const redactor = new Redactor({ extraKeys: resolved.extraKeys });
|
|
3865
|
+
return events.map((event) => {
|
|
3866
|
+
switch (event.event) {
|
|
3867
|
+
case "run_started":
|
|
3868
|
+
return {
|
|
3869
|
+
...event,
|
|
3870
|
+
name: truncateStringForProfile(
|
|
3871
|
+
event.name,
|
|
3872
|
+
"name",
|
|
3873
|
+
maxMetadataValueLength,
|
|
3874
|
+
maxPreviewLength
|
|
3875
|
+
),
|
|
3876
|
+
...event.metadata !== void 0 ? {
|
|
3877
|
+
metadata: redactEventAttributes(
|
|
3878
|
+
event.metadata,
|
|
3879
|
+
redactor,
|
|
3880
|
+
maxMetadataValueLength,
|
|
3881
|
+
maxPreviewLength
|
|
3882
|
+
)
|
|
3883
|
+
} : {}
|
|
3884
|
+
};
|
|
3885
|
+
case "step_started":
|
|
3886
|
+
return {
|
|
3887
|
+
...event,
|
|
3888
|
+
name: truncateStringForProfile(
|
|
3889
|
+
event.name,
|
|
3890
|
+
"name",
|
|
3891
|
+
maxMetadataValueLength,
|
|
3892
|
+
maxPreviewLength
|
|
3893
|
+
),
|
|
3894
|
+
...event.metadata !== void 0 ? {
|
|
3895
|
+
metadata: redactEventAttributes(
|
|
3896
|
+
event.metadata,
|
|
3897
|
+
redactor,
|
|
3898
|
+
maxMetadataValueLength,
|
|
3899
|
+
maxPreviewLength
|
|
3900
|
+
)
|
|
3901
|
+
} : {}
|
|
3902
|
+
};
|
|
3903
|
+
case "run_completed":
|
|
3904
|
+
case "step_completed":
|
|
3905
|
+
return {
|
|
3906
|
+
...event,
|
|
3907
|
+
...event.error !== void 0 ? {
|
|
3908
|
+
error: redactErrorInfo(
|
|
3909
|
+
event.error,
|
|
3910
|
+
redactor,
|
|
3911
|
+
maxMetadataValueLength,
|
|
3912
|
+
maxPreviewLength
|
|
3913
|
+
)
|
|
3914
|
+
} : {}
|
|
3915
|
+
};
|
|
3916
|
+
}
|
|
3917
|
+
});
|
|
3918
|
+
}
|
|
3358
3919
|
function redactRunTreeForExport(tree, options) {
|
|
3359
3920
|
const profile = options?.redactionProfile ?? "local";
|
|
3360
3921
|
if (profile === "local") {
|
|
@@ -3418,12 +3979,15 @@ footer{margin-top:2rem;font-size:0.85rem;color:#555}
|
|
|
3418
3979
|
`.trim();
|
|
3419
3980
|
function buildRunReport(events, options) {
|
|
3420
3981
|
const profile = options.redactionProfile ?? "local";
|
|
3421
|
-
const
|
|
3982
|
+
const safeEvents = redactTraceEventsForReport(events, {
|
|
3983
|
+
redactionProfile: profile
|
|
3984
|
+
});
|
|
3985
|
+
const whatSummary = buildRunWhatSummary(safeEvents);
|
|
3422
3986
|
const whatText = renderRunWhat(whatSummary, {
|
|
3423
3987
|
correlation: options.correlation !== false
|
|
3424
3988
|
});
|
|
3425
|
-
const timelineText = renderTimeline(buildRunTimeline(
|
|
3426
|
-
const tree = resolveTree(
|
|
3989
|
+
const timelineText = renderTimeline(buildRunTimeline(safeEvents));
|
|
3990
|
+
const tree = resolveTree(safeEvents, profile);
|
|
3427
3991
|
const exportOpts = {
|
|
3428
3992
|
includeMetadata: false,
|
|
3429
3993
|
includeAttributes: options.includeAttributes === true,
|
|
@@ -3504,6 +4068,8 @@ ${attrsSection}
|
|
|
3504
4068
|
fileExtension: ".html"
|
|
3505
4069
|
};
|
|
3506
4070
|
}
|
|
4071
|
+
|
|
4072
|
+
// packages/core/src/stats.ts
|
|
3507
4073
|
function percentile(sorted, p) {
|
|
3508
4074
|
if (sorted.length === 0) return void 0;
|
|
3509
4075
|
const idx = Math.min(
|
|
@@ -3514,19 +4080,10 @@ function percentile(sorted, p) {
|
|
|
3514
4080
|
}
|
|
3515
4081
|
async function readRunStartedMetadata(filePath) {
|
|
3516
4082
|
try {
|
|
3517
|
-
const
|
|
3518
|
-
for (const
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
let parsed;
|
|
3522
|
-
try {
|
|
3523
|
-
parsed = JSON.parse(trimmed);
|
|
3524
|
-
} catch {
|
|
3525
|
-
continue;
|
|
3526
|
-
}
|
|
3527
|
-
if (!isTraceEvent(parsed)) continue;
|
|
3528
|
-
if (parsed.event !== "run_started") continue;
|
|
3529
|
-
const rs = parsed;
|
|
4083
|
+
const events = await readTraceEventsFromFile(filePath);
|
|
4084
|
+
for (const event of events) {
|
|
4085
|
+
if (event.event !== "run_started") continue;
|
|
4086
|
+
const rs = event;
|
|
3530
4087
|
if (rs.metadata && typeof rs.metadata === "object") {
|
|
3531
4088
|
return rs.metadata;
|
|
3532
4089
|
}
|
|
@@ -3585,7 +4142,7 @@ async function buildTraceStats(metas, options) {
|
|
|
3585
4142
|
});
|
|
3586
4143
|
}
|
|
3587
4144
|
try {
|
|
3588
|
-
const events = await
|
|
4145
|
+
const events = await readTraceEventsFromFile(m.filePath);
|
|
3589
4146
|
if (events.length === 0) continue;
|
|
3590
4147
|
const summary = buildRunSummary(events);
|
|
3591
4148
|
totalSteps += summary.totalSteps;
|
|
@@ -3773,7 +4330,7 @@ async function searchTraces(metas, options) {
|
|
|
3773
4330
|
if (options.status && m.status !== options.status) continue;
|
|
3774
4331
|
let events = [];
|
|
3775
4332
|
try {
|
|
3776
|
-
events = await
|
|
4333
|
+
events = await readTraceEventsFromFile(m.filePath);
|
|
3777
4334
|
} catch {
|
|
3778
4335
|
continue;
|
|
3779
4336
|
}
|
|
@@ -3909,7 +4466,7 @@ var KNOWN_EVENTS = /* @__PURE__ */ new Set([
|
|
|
3909
4466
|
"step_started",
|
|
3910
4467
|
"step_completed"
|
|
3911
4468
|
]);
|
|
3912
|
-
function
|
|
4469
|
+
function isRecord12(value) {
|
|
3913
4470
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3914
4471
|
}
|
|
3915
4472
|
function safeParse(line) {
|
|
@@ -3931,7 +4488,7 @@ async function isAgentInspectTrace(filePath) {
|
|
|
3931
4488
|
if (trimmed === "") continue;
|
|
3932
4489
|
const parsed = safeParse(trimmed);
|
|
3933
4490
|
if (!parsed) continue;
|
|
3934
|
-
if (!
|
|
4491
|
+
if (!isRecord12(parsed)) continue;
|
|
3935
4492
|
checked += 1;
|
|
3936
4493
|
if (isTraceEvent(parsed)) return true;
|
|
3937
4494
|
const ev = parsed.event;
|
|
@@ -3946,9 +4503,1572 @@ async function isAgentInspectTrace(filePath) {
|
|
|
3946
4503
|
return false;
|
|
3947
4504
|
}
|
|
3948
4505
|
}
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
4506
|
+
resolveTraceSafetyOptions();
|
|
4507
|
+
var DEFAULT_MAX_TRACE_INPUT_BYTES = 10 * 1024 * 1024;
|
|
4508
|
+
var MIN_DETECTION_CONFIDENCE = 0.5;
|
|
4509
|
+
var AMBIGUOUS_CONFIDENCE_DELTA = 0.05;
|
|
4510
|
+
var resolvedInputCache = /* @__PURE__ */ new WeakMap();
|
|
4511
|
+
var OPENINFERENCE_READER_FORMAT = "openinference-json";
|
|
4512
|
+
var OTLP_READER_FORMAT = "otlp-json";
|
|
4513
|
+
var OPENINFERENCE_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
4514
|
+
"trace_id",
|
|
4515
|
+
"traceId",
|
|
4516
|
+
"span_id",
|
|
4517
|
+
"spanId",
|
|
4518
|
+
"parent_span_id",
|
|
4519
|
+
"parentSpanId",
|
|
4520
|
+
"name",
|
|
4521
|
+
"start_time_unix_nano",
|
|
4522
|
+
"startTimeUnixNano",
|
|
4523
|
+
"end_time_unix_nano",
|
|
4524
|
+
"endTimeUnixNano",
|
|
4525
|
+
"start_time",
|
|
4526
|
+
"startTime",
|
|
4527
|
+
"end_time",
|
|
4528
|
+
"endTime",
|
|
4529
|
+
"attributes",
|
|
4530
|
+
"status",
|
|
4531
|
+
"kind",
|
|
4532
|
+
"span_kind",
|
|
4533
|
+
"spanKind"
|
|
4534
|
+
]);
|
|
4535
|
+
var OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS = [
|
|
4536
|
+
"input.value",
|
|
4537
|
+
"output.value",
|
|
4538
|
+
"input.mime_type",
|
|
4539
|
+
"output.mime_type",
|
|
4540
|
+
"llm.input_messages",
|
|
4541
|
+
"llm.output_messages",
|
|
4542
|
+
"llm.prompts",
|
|
4543
|
+
"llm.completions",
|
|
4544
|
+
"retrieval.documents",
|
|
4545
|
+
"reranker.input_documents",
|
|
4546
|
+
"reranker.output_documents",
|
|
4547
|
+
"document.content",
|
|
4548
|
+
"gen_ai.prompt",
|
|
4549
|
+
"gen_ai.completion",
|
|
4550
|
+
"gen_ai.input.messages",
|
|
4551
|
+
"gen_ai.output.messages"
|
|
4552
|
+
];
|
|
4553
|
+
var OTLP_SPAN_KEYS = /* @__PURE__ */ new Set([
|
|
4554
|
+
"traceId",
|
|
4555
|
+
"spanId",
|
|
4556
|
+
"parentSpanId",
|
|
4557
|
+
"name",
|
|
4558
|
+
"kind",
|
|
4559
|
+
"startTimeUnixNano",
|
|
4560
|
+
"endTimeUnixNano",
|
|
4561
|
+
"attributes",
|
|
4562
|
+
"events",
|
|
4563
|
+
"status",
|
|
4564
|
+
"droppedAttributesCount",
|
|
4565
|
+
"droppedEventsCount",
|
|
4566
|
+
"droppedLinksCount",
|
|
4567
|
+
"links",
|
|
4568
|
+
"flags"
|
|
4569
|
+
]);
|
|
4570
|
+
var TraceReadError = class extends Error {
|
|
4571
|
+
code;
|
|
4572
|
+
warnings;
|
|
4573
|
+
constructor(code, message, warnings = []) {
|
|
4574
|
+
super(message);
|
|
4575
|
+
this.name = "TraceReadError";
|
|
4576
|
+
this.code = code;
|
|
4577
|
+
this.warnings = warnings;
|
|
4578
|
+
}
|
|
4579
|
+
};
|
|
4580
|
+
function normalizeCandidate(reader, candidate) {
|
|
4581
|
+
const confidence = Number.isFinite(candidate.confidence) ? Math.max(0, Math.min(1, candidate.confidence)) : 0;
|
|
4582
|
+
return {
|
|
4583
|
+
...candidate,
|
|
4584
|
+
format: candidate.format || reader.format,
|
|
4585
|
+
confidence,
|
|
4586
|
+
readerName: candidate.readerName ?? reader.name
|
|
4587
|
+
};
|
|
4588
|
+
}
|
|
4589
|
+
function sortCandidates(candidates) {
|
|
4590
|
+
return [...candidates].sort((a, b) => {
|
|
4591
|
+
if (b.confidence !== a.confidence) return b.confidence - a.confidence;
|
|
4592
|
+
return a.format.localeCompare(b.format);
|
|
4593
|
+
});
|
|
4594
|
+
}
|
|
4595
|
+
function collectWarnings(candidates) {
|
|
4596
|
+
return candidates.flatMap((candidate) => candidate.warnings ?? []);
|
|
4597
|
+
}
|
|
4598
|
+
function dedupeWarnings(warnings) {
|
|
4599
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4600
|
+
const out = [];
|
|
4601
|
+
for (const warning of warnings) {
|
|
4602
|
+
const key = [
|
|
4603
|
+
warning.code,
|
|
4604
|
+
warning.message,
|
|
4605
|
+
warning.severity ?? "",
|
|
4606
|
+
warning.sourceFile ?? "",
|
|
4607
|
+
warning.line ?? "",
|
|
4608
|
+
warning.field ?? ""
|
|
4609
|
+
].join("\0");
|
|
4610
|
+
if (seen.has(key)) continue;
|
|
4611
|
+
seen.add(key);
|
|
4612
|
+
out.push(warning);
|
|
4613
|
+
}
|
|
4614
|
+
return out;
|
|
4615
|
+
}
|
|
4616
|
+
function attachSingleSourceFile(warnings, resolved) {
|
|
4617
|
+
if (resolved.sourceFiles.length !== 1) return [...warnings];
|
|
4618
|
+
const [sourceFile] = resolved.sourceFiles;
|
|
4619
|
+
return warnings.map((warning) => ({
|
|
4620
|
+
...warning,
|
|
4621
|
+
sourceFile: warning.sourceFile ?? sourceFile
|
|
4622
|
+
}));
|
|
4623
|
+
}
|
|
4624
|
+
function findReaderByFormat(format, readers) {
|
|
4625
|
+
return readers.find((reader) => reader.format === format);
|
|
4626
|
+
}
|
|
4627
|
+
async function jsonlFilesInDirectory(dirPath) {
|
|
4628
|
+
const entries = await promises.readdir(dirPath, { withFileTypes: true });
|
|
4629
|
+
return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
|
|
4630
|
+
}
|
|
4631
|
+
async function resolveInput(input3) {
|
|
4632
|
+
const cached = resolvedInputCache.get(input3);
|
|
4633
|
+
if (cached) return cached;
|
|
4634
|
+
const promise = resolveInputUncached(input3);
|
|
4635
|
+
resolvedInputCache.set(input3, promise);
|
|
4636
|
+
return promise;
|
|
4637
|
+
}
|
|
4638
|
+
function assertInputWithinBounds(content, sourceFile) {
|
|
4639
|
+
const bytes = Buffer.byteLength(content, "utf8");
|
|
4640
|
+
if (bytes <= DEFAULT_MAX_TRACE_INPUT_BYTES) return;
|
|
4641
|
+
throw new TraceReadError("unsupported_format", "Trace input exceeds the local reader size limit.", [
|
|
4642
|
+
{
|
|
4643
|
+
code: "input_too_large",
|
|
4644
|
+
message: `Trace input is ${bytes} bytes; max is ${DEFAULT_MAX_TRACE_INPUT_BYTES} bytes.`,
|
|
4645
|
+
severity: "error",
|
|
4646
|
+
...sourceFile !== void 0 ? { sourceFile } : {}
|
|
4647
|
+
}
|
|
4648
|
+
]);
|
|
4649
|
+
}
|
|
4650
|
+
async function resolveInputUncached(input3) {
|
|
4651
|
+
if (input3.type === "string") {
|
|
4652
|
+
assertInputWithinBounds(input3.content);
|
|
4653
|
+
return { content: input3.content, sourceFiles: [] };
|
|
4654
|
+
}
|
|
4655
|
+
if (input3.type === "buffer") {
|
|
4656
|
+
const content = input3.content.toString("utf-8");
|
|
4657
|
+
assertInputWithinBounds(content);
|
|
4658
|
+
return { content, sourceFiles: [] };
|
|
4659
|
+
}
|
|
4660
|
+
if (input3.type === "file") {
|
|
4661
|
+
const content = await promises.readFile(input3.path, "utf-8");
|
|
4662
|
+
assertInputWithinBounds(content, input3.path);
|
|
4663
|
+
return { content, sourceFiles: [input3.path] };
|
|
4664
|
+
}
|
|
4665
|
+
if (input3.type === "directory") {
|
|
4666
|
+
const files = await jsonlFilesInDirectory(input3.path);
|
|
4667
|
+
const parts = await Promise.all(
|
|
4668
|
+
files.map(async (file) => (await promises.readFile(file, "utf-8")).trimEnd())
|
|
4669
|
+
);
|
|
4670
|
+
const content = parts.filter((part) => part.trim() !== "").join("\n");
|
|
4671
|
+
assertInputWithinBounds(content, input3.path);
|
|
4672
|
+
return {
|
|
4673
|
+
content,
|
|
4674
|
+
sourceFiles: files
|
|
4675
|
+
};
|
|
4676
|
+
}
|
|
4677
|
+
return void 0;
|
|
4678
|
+
}
|
|
4679
|
+
function detectJsonlFormat(content) {
|
|
4680
|
+
let saw01 = false;
|
|
4681
|
+
let saw02 = false;
|
|
4682
|
+
let validRows = 0;
|
|
4683
|
+
let invalidJsonRows = 0;
|
|
4684
|
+
let unknownSchemaRows = 0;
|
|
4685
|
+
let firstInvalidJsonLine;
|
|
4686
|
+
let firstUnknownSchemaLine;
|
|
4687
|
+
let lineNumber = 0;
|
|
4688
|
+
for (const line of content.split(/\r?\n/)) {
|
|
4689
|
+
lineNumber += 1;
|
|
4690
|
+
const trimmed = line.trim();
|
|
4691
|
+
if (trimmed === "") continue;
|
|
4692
|
+
let parsed;
|
|
4693
|
+
try {
|
|
4694
|
+
parsed = JSON.parse(trimmed);
|
|
4695
|
+
} catch {
|
|
4696
|
+
invalidJsonRows += 1;
|
|
4697
|
+
firstInvalidJsonLine ??= lineNumber;
|
|
4698
|
+
continue;
|
|
4699
|
+
}
|
|
4700
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "schemaVersion" in parsed) {
|
|
4701
|
+
const version2 = parsed.schemaVersion;
|
|
4702
|
+
if (version2 === "0.1") {
|
|
4703
|
+
saw01 = true;
|
|
4704
|
+
validRows += 1;
|
|
4705
|
+
continue;
|
|
4706
|
+
}
|
|
4707
|
+
if (version2 === "0.2") {
|
|
4708
|
+
saw02 = true;
|
|
4709
|
+
validRows += 1;
|
|
4710
|
+
continue;
|
|
4711
|
+
}
|
|
4712
|
+
}
|
|
4713
|
+
unknownSchemaRows += 1;
|
|
4714
|
+
firstUnknownSchemaLine ??= lineNumber;
|
|
4715
|
+
}
|
|
4716
|
+
const warnings = [];
|
|
4717
|
+
if (invalidJsonRows > 0) {
|
|
4718
|
+
warnings.push({
|
|
4719
|
+
code: "invalid_jsonl_rows",
|
|
4720
|
+
message: `Skipped ${invalidJsonRows} invalid JSONL row(s) during format detection.`,
|
|
4721
|
+
severity: "warning",
|
|
4722
|
+
...firstInvalidJsonLine !== void 0 ? { line: firstInvalidJsonLine } : {}
|
|
4723
|
+
});
|
|
4724
|
+
}
|
|
4725
|
+
if (unknownSchemaRows > 0) {
|
|
4726
|
+
warnings.push({
|
|
4727
|
+
code: "unknown_schema_rows",
|
|
4728
|
+
message: `Skipped ${unknownSchemaRows} row(s) with unknown schemaVersion during format detection.`,
|
|
4729
|
+
severity: "warning",
|
|
4730
|
+
...firstUnknownSchemaLine !== void 0 ? { line: firstUnknownSchemaLine } : {}
|
|
4731
|
+
});
|
|
4732
|
+
}
|
|
4733
|
+
let format = "empty";
|
|
4734
|
+
if (saw01 && saw02) format = "mixed";
|
|
4735
|
+
else if (saw01) format = "0.1";
|
|
4736
|
+
else if (saw02) format = "0.2";
|
|
4737
|
+
return { format, validRows, warnings };
|
|
4738
|
+
}
|
|
4739
|
+
function agentInspectFormatLabel(format) {
|
|
4740
|
+
switch (format) {
|
|
4741
|
+
case "0.1":
|
|
4742
|
+
return "agent-inspect-v0.1-jsonl";
|
|
4743
|
+
case "0.2":
|
|
4744
|
+
return "agent-inspect-v0.2-jsonl";
|
|
4745
|
+
case "mixed":
|
|
4746
|
+
return "agent-inspect-mixed-jsonl";
|
|
4747
|
+
default:
|
|
4748
|
+
return "agent-inspect-jsonl";
|
|
4749
|
+
}
|
|
4750
|
+
}
|
|
4751
|
+
function persistedEventsForParsedTrace(parsed) {
|
|
4752
|
+
if (parsed.format === "0.2" && parsed.persisted.length > 0) {
|
|
4753
|
+
return [...parsed.persisted];
|
|
4754
|
+
}
|
|
4755
|
+
if (parsed.format === "mixed" && parsed.rows.length > 0) {
|
|
4756
|
+
return parsed.rows.map((row, index) => {
|
|
4757
|
+
if (row.format === "0.2") return row.event;
|
|
4758
|
+
return traceEventToPersistedInspectEvent(row.event, {
|
|
4759
|
+
eventIndex: index,
|
|
4760
|
+
sourceName: "agent-inspect-jsonl-reader"
|
|
4761
|
+
});
|
|
4762
|
+
});
|
|
4763
|
+
}
|
|
4764
|
+
return traceEventsToPersistedInspectEvents(parsed.events, {
|
|
4765
|
+
sourceName: "agent-inspect-jsonl-reader"
|
|
4766
|
+
});
|
|
4767
|
+
}
|
|
4768
|
+
function isRecord13(value) {
|
|
4769
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4770
|
+
}
|
|
4771
|
+
function isNonEmptyString3(value) {
|
|
4772
|
+
return typeof value === "string" && value.trim() !== "";
|
|
4773
|
+
}
|
|
4774
|
+
function readStringField(record, keys) {
|
|
4775
|
+
for (const key of keys) {
|
|
4776
|
+
const value = record[key];
|
|
4777
|
+
if (isNonEmptyString3(value)) return value;
|
|
4778
|
+
}
|
|
4779
|
+
return void 0;
|
|
4780
|
+
}
|
|
4781
|
+
function readRecordField(record, key) {
|
|
4782
|
+
const value = record[key];
|
|
4783
|
+
return isRecord13(value) ? value : void 0;
|
|
4784
|
+
}
|
|
4785
|
+
function parseJsonDocument(content) {
|
|
4786
|
+
return JSON.parse(content);
|
|
4787
|
+
}
|
|
4788
|
+
function looksLikeOpenInferenceSpan(value) {
|
|
4789
|
+
if (!isRecord13(value)) return false;
|
|
4790
|
+
const attributes = readRecordField(value, "attributes");
|
|
4791
|
+
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);
|
|
4792
|
+
}
|
|
4793
|
+
function extractOpenInferenceDocument(root) {
|
|
4794
|
+
const warnings = [];
|
|
4795
|
+
const unsupportedFields = [];
|
|
4796
|
+
if (Array.isArray(root)) {
|
|
4797
|
+
const spans = root.filter(looksLikeOpenInferenceSpan);
|
|
4798
|
+
if (spans.length === 0) return void 0;
|
|
4799
|
+
if (spans.length !== root.length) {
|
|
4800
|
+
warnings.push({
|
|
4801
|
+
code: "openinference_skipped_items",
|
|
4802
|
+
message: "Skipped non-span item(s) in OpenInference span array.",
|
|
4803
|
+
severity: "warning"
|
|
4804
|
+
});
|
|
4805
|
+
}
|
|
4806
|
+
return {
|
|
4807
|
+
spans,
|
|
4808
|
+
confidence: 0.82,
|
|
4809
|
+
description: "OpenInference span array",
|
|
4810
|
+
warnings,
|
|
4811
|
+
unsupportedFields
|
|
4812
|
+
};
|
|
4813
|
+
}
|
|
4814
|
+
if (!isRecord13(root)) return void 0;
|
|
4815
|
+
const rootFormat = root.format;
|
|
4816
|
+
const rootCompatibility = root.compatibility;
|
|
4817
|
+
const version2 = typeof root.version === "string" && root.version.trim() !== "" ? root.version : void 0;
|
|
4818
|
+
if (Array.isArray(root.spans)) {
|
|
4819
|
+
const spans = root.spans.filter(looksLikeOpenInferenceSpan);
|
|
4820
|
+
if (spans.length === 0 && (rootFormat === "openinference" || rootCompatibility === "openinference-compatible")) {
|
|
4821
|
+
warnings.push({
|
|
4822
|
+
code: "openinference_no_valid_spans",
|
|
4823
|
+
message: "OpenInference document did not contain any valid spans.",
|
|
4824
|
+
severity: "error"
|
|
4825
|
+
});
|
|
4826
|
+
return {
|
|
4827
|
+
spans,
|
|
4828
|
+
confidence: 0.7,
|
|
4829
|
+
description: "Malformed OpenInference document",
|
|
4830
|
+
version: version2,
|
|
4831
|
+
warnings,
|
|
4832
|
+
unsupportedFields
|
|
4833
|
+
};
|
|
4834
|
+
}
|
|
4835
|
+
if (spans.length === 0) return void 0;
|
|
4836
|
+
if (spans.length !== root.spans.length) {
|
|
4837
|
+
warnings.push({
|
|
4838
|
+
code: "openinference_skipped_spans",
|
|
4839
|
+
message: "Skipped invalid OpenInference span item(s).",
|
|
4840
|
+
severity: "warning"
|
|
4841
|
+
});
|
|
4842
|
+
}
|
|
4843
|
+
return {
|
|
4844
|
+
spans,
|
|
4845
|
+
confidence: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? 0.9 : 0.84,
|
|
4846
|
+
description: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? "OpenInference document" : "OpenInference spans document",
|
|
4847
|
+
version: version2,
|
|
4848
|
+
warnings,
|
|
4849
|
+
unsupportedFields
|
|
4850
|
+
};
|
|
4851
|
+
}
|
|
4852
|
+
if (Array.isArray(root.data)) {
|
|
4853
|
+
const spans = root.data.filter(looksLikeOpenInferenceSpan);
|
|
4854
|
+
if (spans.length === 0) return void 0;
|
|
4855
|
+
if (spans.length !== root.data.length) {
|
|
4856
|
+
warnings.push({
|
|
4857
|
+
code: "openinference_skipped_data_items",
|
|
4858
|
+
message: "Skipped non-span item(s) in OpenInference data array.",
|
|
4859
|
+
severity: "warning"
|
|
4860
|
+
});
|
|
4861
|
+
}
|
|
4862
|
+
return {
|
|
4863
|
+
spans,
|
|
4864
|
+
confidence: 0.8,
|
|
4865
|
+
description: "OpenInference data document",
|
|
4866
|
+
version: version2,
|
|
4867
|
+
warnings,
|
|
4868
|
+
unsupportedFields
|
|
4869
|
+
};
|
|
4870
|
+
}
|
|
4871
|
+
if (looksLikeOpenInferenceSpan(root)) {
|
|
4872
|
+
return {
|
|
4873
|
+
spans: [root],
|
|
4874
|
+
confidence: 0.76,
|
|
4875
|
+
description: "OpenInference single span",
|
|
4876
|
+
version: version2,
|
|
4877
|
+
warnings,
|
|
4878
|
+
unsupportedFields
|
|
4879
|
+
};
|
|
4880
|
+
}
|
|
4881
|
+
if (rootFormat === "openinference" || rootCompatibility === "openinference-compatible") {
|
|
4882
|
+
warnings.push({
|
|
4883
|
+
code: "openinference_missing_spans",
|
|
4884
|
+
message: "OpenInference document is missing a spans array.",
|
|
4885
|
+
severity: "error"
|
|
4886
|
+
});
|
|
4887
|
+
return {
|
|
4888
|
+
spans: [],
|
|
4889
|
+
confidence: 0.7,
|
|
4890
|
+
description: "Malformed OpenInference document",
|
|
4891
|
+
version: version2,
|
|
4892
|
+
warnings,
|
|
4893
|
+
unsupportedFields
|
|
4894
|
+
};
|
|
4895
|
+
}
|
|
4896
|
+
return void 0;
|
|
4897
|
+
}
|
|
4898
|
+
function parseUnixNanoToIso(value) {
|
|
4899
|
+
if (typeof value === "bigint" && value >= 0n) {
|
|
4900
|
+
return new Date(Number(value / 1000000n)).toISOString();
|
|
4901
|
+
}
|
|
4902
|
+
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
4903
|
+
return new Date(Math.floor(value / 1e6)).toISOString();
|
|
4904
|
+
}
|
|
4905
|
+
if (typeof value === "string" && /^\d+$/.test(value)) {
|
|
4906
|
+
return new Date(Number(BigInt(value) / 1000000n)).toISOString();
|
|
4907
|
+
}
|
|
4908
|
+
return void 0;
|
|
4909
|
+
}
|
|
4910
|
+
function parseIsoTime(value) {
|
|
4911
|
+
if (!isNonEmptyString3(value)) return void 0;
|
|
4912
|
+
const ms = Date.parse(value);
|
|
4913
|
+
if (!Number.isFinite(ms)) return void 0;
|
|
4914
|
+
return new Date(ms).toISOString();
|
|
4915
|
+
}
|
|
4916
|
+
function readOpenInferenceTimestamp(span, nanoKeys, isoKeys) {
|
|
4917
|
+
for (const key of nanoKeys) {
|
|
4918
|
+
const iso = parseUnixNanoToIso(span[key]);
|
|
4919
|
+
if (iso !== void 0) return iso;
|
|
4920
|
+
}
|
|
4921
|
+
for (const key of isoKeys) {
|
|
4922
|
+
const iso = parseIsoTime(span[key]);
|
|
4923
|
+
if (iso !== void 0) return iso;
|
|
4924
|
+
}
|
|
4925
|
+
return void 0;
|
|
4926
|
+
}
|
|
4927
|
+
function durationBetweenIso(startedAt, endedAt) {
|
|
4928
|
+
if (startedAt === void 0 || endedAt === void 0) return void 0;
|
|
4929
|
+
const startMs = Date.parse(startedAt);
|
|
4930
|
+
const endMs = Date.parse(endedAt);
|
|
4931
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs < startMs) {
|
|
4932
|
+
return void 0;
|
|
4933
|
+
}
|
|
4934
|
+
return endMs - startMs;
|
|
4935
|
+
}
|
|
4936
|
+
function isSensitiveOpenInferenceAttribute(key) {
|
|
4937
|
+
return OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS.some(
|
|
4938
|
+
(sensitiveKey) => key === sensitiveKey || key.startsWith(`${sensitiveKey}.`) || key.endsWith(".message.content") || key.endsWith(".document.content")
|
|
4939
|
+
);
|
|
4940
|
+
}
|
|
4941
|
+
function summarizeAttributeValue(value) {
|
|
4942
|
+
if (typeof value === "string") {
|
|
4943
|
+
return { type: "string", length: value.length };
|
|
4944
|
+
}
|
|
4945
|
+
if (typeof value === "number") {
|
|
4946
|
+
return { type: "number", finite: Number.isFinite(value) };
|
|
4947
|
+
}
|
|
4948
|
+
if (typeof value === "boolean") {
|
|
4949
|
+
return { type: "boolean" };
|
|
4950
|
+
}
|
|
4951
|
+
if (Array.isArray(value)) {
|
|
4952
|
+
return { type: "array", length: value.length };
|
|
4953
|
+
}
|
|
4954
|
+
if (isRecord13(value)) {
|
|
4955
|
+
return { type: "object", keyCount: Object.keys(value).length };
|
|
4956
|
+
}
|
|
4957
|
+
if (value === null) {
|
|
4958
|
+
return { type: "null" };
|
|
4959
|
+
}
|
|
4960
|
+
return { type: typeof value };
|
|
4961
|
+
}
|
|
4962
|
+
function sanitizeOpenInferenceAttributes(attributes, pathPrefix) {
|
|
4963
|
+
const out = {};
|
|
4964
|
+
const warnings = [];
|
|
4965
|
+
const unsupportedFields = [];
|
|
4966
|
+
const summarizedKeys = [];
|
|
4967
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
4968
|
+
if (isSensitiveOpenInferenceAttribute(key)) {
|
|
4969
|
+
summarizedKeys.push(key);
|
|
4970
|
+
out[`${key}.summary`] = summarizeAttributeValue(value);
|
|
4971
|
+
unsupportedFields.push(`${pathPrefix}.attributes.${key}`);
|
|
4972
|
+
continue;
|
|
4973
|
+
}
|
|
4974
|
+
out[key] = value;
|
|
4975
|
+
}
|
|
4976
|
+
if (summarizedKeys.length > 0) {
|
|
4977
|
+
out["openinference.summarized_attributes"] = summarizedKeys;
|
|
4978
|
+
warnings.push({
|
|
4979
|
+
code: "openinference_sensitive_attribute_summarized",
|
|
4980
|
+
message: "OpenInference prompt/output/document attribute(s) were summarized instead of copied verbatim.",
|
|
4981
|
+
severity: "warning"
|
|
4982
|
+
});
|
|
4983
|
+
}
|
|
4984
|
+
return { attributes: out, warnings, unsupportedFields };
|
|
4985
|
+
}
|
|
4986
|
+
function mapOpenInferenceKind(span, attributes, pathPrefix) {
|
|
4987
|
+
const warnings = [];
|
|
4988
|
+
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
4989
|
+
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
|
|
4990
|
+
return { kind: agentInspectKind, warnings };
|
|
4991
|
+
}
|
|
4992
|
+
const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
|
|
4993
|
+
const normalized = rawKind?.toUpperCase();
|
|
4994
|
+
switch (normalized) {
|
|
4995
|
+
case "LLM":
|
|
4996
|
+
return { kind: "LLM", warnings };
|
|
4997
|
+
case "TOOL":
|
|
4998
|
+
return { kind: "TOOL", warnings };
|
|
4999
|
+
case "CHAIN":
|
|
5000
|
+
return { kind: "CHAIN", warnings };
|
|
5001
|
+
case "RETRIEVER":
|
|
5002
|
+
return { kind: "RETRIEVER", warnings };
|
|
5003
|
+
case "AGENT":
|
|
5004
|
+
return { kind: "AGENT", warnings };
|
|
5005
|
+
case "EMBEDDING":
|
|
5006
|
+
warnings.push({
|
|
5007
|
+
code: "openinference_kind_semantic_loss",
|
|
5008
|
+
message: "OpenInference EMBEDDING span kind mapped to AgentInspect LLM.",
|
|
5009
|
+
severity: "warning",
|
|
5010
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
5011
|
+
});
|
|
5012
|
+
return { kind: "LLM", warnings };
|
|
5013
|
+
case "RERANKER":
|
|
5014
|
+
warnings.push({
|
|
5015
|
+
code: "openinference_kind_semantic_loss",
|
|
5016
|
+
message: "OpenInference RERANKER span kind mapped to AgentInspect RETRIEVER.",
|
|
5017
|
+
severity: "warning",
|
|
5018
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
5019
|
+
});
|
|
5020
|
+
return { kind: "RETRIEVER", warnings };
|
|
5021
|
+
case "UNKNOWN":
|
|
5022
|
+
case void 0:
|
|
5023
|
+
warnings.push({
|
|
5024
|
+
code: "openinference_kind_unknown",
|
|
5025
|
+
message: "OpenInference span kind was missing or unknown; mapped to AgentInspect LOGIC.",
|
|
5026
|
+
severity: "warning",
|
|
5027
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
5028
|
+
});
|
|
5029
|
+
return { kind: "LOGIC", warnings };
|
|
5030
|
+
default:
|
|
5031
|
+
warnings.push({
|
|
5032
|
+
code: "openinference_kind_unsupported",
|
|
5033
|
+
message: `Unsupported OpenInference span kind "${rawKind}" mapped to AgentInspect LOGIC.`,
|
|
5034
|
+
severity: "warning",
|
|
5035
|
+
field: `${pathPrefix}.attributes.openinference.span.kind`
|
|
5036
|
+
});
|
|
5037
|
+
return { kind: "LOGIC", warnings };
|
|
5038
|
+
}
|
|
5039
|
+
}
|
|
5040
|
+
function mapOpenInferenceStatus(status) {
|
|
5041
|
+
if (!isRecord13(status)) return void 0;
|
|
5042
|
+
const rawCode = status.code;
|
|
5043
|
+
if (typeof rawCode !== "string") return void 0;
|
|
5044
|
+
switch (rawCode.toUpperCase()) {
|
|
5045
|
+
case "OK":
|
|
5046
|
+
return "ok";
|
|
5047
|
+
case "ERROR":
|
|
5048
|
+
return "error";
|
|
5049
|
+
case "UNSET":
|
|
5050
|
+
return "unknown";
|
|
5051
|
+
default:
|
|
5052
|
+
return "unknown";
|
|
5053
|
+
}
|
|
5054
|
+
}
|
|
5055
|
+
function readOpenInferenceTokenUsage(attributes) {
|
|
5056
|
+
const prompt = attributes["llm.token_count.prompt"];
|
|
5057
|
+
const completion = attributes["llm.token_count.completion"];
|
|
5058
|
+
const total = attributes["llm.token_count.total"];
|
|
5059
|
+
const cached = attributes["llm.token_count.prompt_details.cache_read"];
|
|
5060
|
+
const usage = {};
|
|
5061
|
+
if (typeof prompt === "number" && Number.isFinite(prompt) && prompt >= 0) {
|
|
5062
|
+
usage.input = prompt;
|
|
5063
|
+
}
|
|
5064
|
+
if (typeof completion === "number" && Number.isFinite(completion) && completion >= 0) {
|
|
5065
|
+
usage.output = completion;
|
|
5066
|
+
}
|
|
5067
|
+
if (typeof total === "number" && Number.isFinite(total) && total >= 0) {
|
|
5068
|
+
usage.total = total;
|
|
5069
|
+
}
|
|
5070
|
+
if (typeof cached === "number" && Number.isFinite(cached) && cached >= 0) {
|
|
5071
|
+
usage.cached = cached;
|
|
5072
|
+
}
|
|
5073
|
+
if (usage.total === void 0 && usage.input !== void 0 && usage.output !== void 0) {
|
|
5074
|
+
usage.total = usage.input + usage.output;
|
|
5075
|
+
}
|
|
5076
|
+
return Object.keys(usage).length > 0 ? usage : void 0;
|
|
5077
|
+
}
|
|
5078
|
+
function readOpenInferenceConfidence(attributes) {
|
|
5079
|
+
const confidence = attributes["agent_inspect.confidence"];
|
|
5080
|
+
if (confidence === "explicit" || confidence === "correlated" || confidence === "heuristic" || confidence === "unknown") {
|
|
5081
|
+
return confidence;
|
|
5082
|
+
}
|
|
5083
|
+
return "correlated";
|
|
5084
|
+
}
|
|
5085
|
+
function mapOpenInferenceSpan(span, index, version2) {
|
|
5086
|
+
const pathPrefix = `spans[${index}]`;
|
|
5087
|
+
const warnings = [];
|
|
5088
|
+
const unsupportedFields = [];
|
|
5089
|
+
const rawAttributes = readRecordField(span, "attributes") ?? {};
|
|
5090
|
+
const sanitized = sanitizeOpenInferenceAttributes(rawAttributes, pathPrefix);
|
|
5091
|
+
warnings.push(...sanitized.warnings);
|
|
5092
|
+
unsupportedFields.push(...sanitized.unsupportedFields);
|
|
5093
|
+
const attributes = { ...sanitized.attributes };
|
|
5094
|
+
for (const [key, value] of Object.entries(span)) {
|
|
5095
|
+
if (OPENINFERENCE_SPAN_KEYS.has(key)) continue;
|
|
5096
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
5097
|
+
if (value === null || typeof value !== "object") {
|
|
5098
|
+
attributes[`openinference.${key}`] = value;
|
|
5099
|
+
} else {
|
|
5100
|
+
attributes[`openinference.${key}.summary`] = summarizeAttributeValue(value);
|
|
5101
|
+
warnings.push({
|
|
5102
|
+
code: "openinference_unsupported_field_summarized",
|
|
5103
|
+
message: `Unsupported OpenInference span field "${key}" was summarized.`,
|
|
5104
|
+
severity: "warning",
|
|
5105
|
+
field: `${pathPrefix}.${key}`
|
|
5106
|
+
});
|
|
5107
|
+
}
|
|
5108
|
+
}
|
|
5109
|
+
const traceId = readStringField(span, ["trace_id", "traceId"]) ?? `trace-${index}`;
|
|
5110
|
+
const spanId = readStringField(span, ["span_id", "spanId"]) ?? `span-${index}`;
|
|
5111
|
+
const parentSpanId = readStringField(span, ["parent_span_id", "parentSpanId"]);
|
|
5112
|
+
const name = readStringField(span, ["name"]) ?? spanId;
|
|
5113
|
+
const startedAt = readOpenInferenceTimestamp(
|
|
5114
|
+
span,
|
|
5115
|
+
["start_time_unix_nano", "startTimeUnixNano"],
|
|
5116
|
+
["start_time", "startTime"]
|
|
5117
|
+
);
|
|
5118
|
+
const endedAt = readOpenInferenceTimestamp(
|
|
5119
|
+
span,
|
|
5120
|
+
["end_time_unix_nano", "endTimeUnixNano"],
|
|
5121
|
+
["end_time", "endTime"]
|
|
5122
|
+
);
|
|
5123
|
+
const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
|
|
5124
|
+
if (startedAt === void 0) {
|
|
5125
|
+
warnings.push({
|
|
5126
|
+
code: "openinference_missing_start_time",
|
|
5127
|
+
message: "OpenInference span is missing a valid start time; using Unix epoch.",
|
|
5128
|
+
severity: "warning",
|
|
5129
|
+
field: `${pathPrefix}.start_time_unix_nano`
|
|
5130
|
+
});
|
|
5131
|
+
unsupportedFields.push(`${pathPrefix}.start_time_unix_nano`);
|
|
5132
|
+
}
|
|
5133
|
+
const { kind, warnings: kindWarnings } = mapOpenInferenceKind(
|
|
5134
|
+
span,
|
|
5135
|
+
rawAttributes,
|
|
5136
|
+
pathPrefix
|
|
5137
|
+
);
|
|
5138
|
+
warnings.push(...kindWarnings);
|
|
5139
|
+
const status = mapOpenInferenceStatus(span.status);
|
|
5140
|
+
const tokenUsage = readOpenInferenceTokenUsage(rawAttributes);
|
|
5141
|
+
const errorMessage = isRecord13(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
5142
|
+
const event = {
|
|
5143
|
+
schemaVersion: "0.2",
|
|
5144
|
+
eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
|
|
5145
|
+
runId: typeof rawAttributes["agent_inspect.run_id"] === "string" ? rawAttributes["agent_inspect.run_id"] : traceId,
|
|
5146
|
+
kind,
|
|
5147
|
+
name,
|
|
5148
|
+
timestamp,
|
|
5149
|
+
confidence: readOpenInferenceConfidence(rawAttributes),
|
|
5150
|
+
source: {
|
|
5151
|
+
type: "otel",
|
|
5152
|
+
name: "openinference",
|
|
5153
|
+
...version2 !== void 0 ? { version: version2 } : {}
|
|
5154
|
+
},
|
|
5155
|
+
attributes,
|
|
5156
|
+
trace: {
|
|
5157
|
+
traceId,
|
|
5158
|
+
spanId,
|
|
5159
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
5160
|
+
}
|
|
5161
|
+
};
|
|
5162
|
+
if (status !== void 0) {
|
|
5163
|
+
event.status = status;
|
|
5164
|
+
}
|
|
5165
|
+
if (startedAt !== void 0) {
|
|
5166
|
+
event.startedAt = startedAt;
|
|
5167
|
+
}
|
|
5168
|
+
if (endedAt !== void 0) {
|
|
5169
|
+
event.endedAt = endedAt;
|
|
5170
|
+
}
|
|
5171
|
+
const durationMs = durationBetweenIso(startedAt, endedAt);
|
|
5172
|
+
if (durationMs !== void 0) {
|
|
5173
|
+
event.durationMs = durationMs;
|
|
5174
|
+
}
|
|
5175
|
+
if (tokenUsage !== void 0) {
|
|
5176
|
+
event.tokenUsage = tokenUsage;
|
|
5177
|
+
}
|
|
5178
|
+
if (status === "error") {
|
|
5179
|
+
event.error = {
|
|
5180
|
+
message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OpenInference span error"
|
|
5181
|
+
};
|
|
5182
|
+
}
|
|
5183
|
+
return {
|
|
5184
|
+
event,
|
|
5185
|
+
warnings,
|
|
5186
|
+
unsupportedFields,
|
|
5187
|
+
spanId,
|
|
5188
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
5189
|
+
};
|
|
5190
|
+
}
|
|
5191
|
+
function mapOpenInferenceEvents(document) {
|
|
5192
|
+
const mapped = document.spans.map(
|
|
5193
|
+
(span, index) => mapOpenInferenceSpan(span, index, document.version)
|
|
5194
|
+
);
|
|
5195
|
+
const spanIdToEventId = new Map(
|
|
5196
|
+
mapped.map((span) => [span.spanId, span.event.eventId])
|
|
5197
|
+
);
|
|
5198
|
+
for (const span of mapped) {
|
|
5199
|
+
if (span.parentSpanId === void 0) continue;
|
|
5200
|
+
span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
|
|
5201
|
+
}
|
|
5202
|
+
return {
|
|
5203
|
+
events: mapped.map((span) => span.event),
|
|
5204
|
+
warnings: mapped.flatMap((span) => span.warnings),
|
|
5205
|
+
unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
|
|
5206
|
+
};
|
|
5207
|
+
}
|
|
5208
|
+
var openInferenceJsonReader = {
|
|
5209
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
5210
|
+
name: "OpenInference JSON",
|
|
5211
|
+
async detect(input3) {
|
|
5212
|
+
const resolved = await resolveInput(input3);
|
|
5213
|
+
if (!resolved) return void 0;
|
|
5214
|
+
let parsed;
|
|
5215
|
+
try {
|
|
5216
|
+
parsed = parseJsonDocument(resolved.content);
|
|
5217
|
+
} catch {
|
|
5218
|
+
return void 0;
|
|
5219
|
+
}
|
|
5220
|
+
const document = extractOpenInferenceDocument(parsed);
|
|
5221
|
+
if (!document) return void 0;
|
|
5222
|
+
return {
|
|
5223
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
5224
|
+
confidence: document.confidence,
|
|
5225
|
+
readerName: "OpenInference JSON",
|
|
5226
|
+
description: document.description,
|
|
5227
|
+
warnings: attachSingleSourceFile(document.warnings, resolved)
|
|
5228
|
+
};
|
|
5229
|
+
},
|
|
5230
|
+
async read(input3) {
|
|
5231
|
+
const resolved = await resolveInput(input3);
|
|
5232
|
+
if (!resolved) {
|
|
5233
|
+
throw new TraceReadError(
|
|
5234
|
+
"unsupported_format",
|
|
5235
|
+
"OpenInference JSON reader requires file, string, or buffer input."
|
|
5236
|
+
);
|
|
5237
|
+
}
|
|
5238
|
+
let parsed;
|
|
5239
|
+
try {
|
|
5240
|
+
parsed = parseJsonDocument(resolved.content);
|
|
5241
|
+
} catch {
|
|
5242
|
+
throw new TraceReadError("unsupported_format", "OpenInference JSON input is not valid JSON.", [
|
|
5243
|
+
{
|
|
5244
|
+
code: "openinference_invalid_json",
|
|
5245
|
+
message: "OpenInference JSON reader could not parse the input as JSON.",
|
|
5246
|
+
severity: "error"
|
|
5247
|
+
}
|
|
5248
|
+
]);
|
|
5249
|
+
}
|
|
5250
|
+
const document = extractOpenInferenceDocument(parsed);
|
|
5251
|
+
if (!document || document.spans.length === 0) {
|
|
5252
|
+
throw new TraceReadError(
|
|
5253
|
+
"unsupported_format",
|
|
5254
|
+
"No valid OpenInference spans found.",
|
|
5255
|
+
attachSingleSourceFile(
|
|
5256
|
+
document?.warnings ?? [
|
|
5257
|
+
{
|
|
5258
|
+
code: "openinference_no_valid_spans",
|
|
5259
|
+
message: "OpenInference JSON input did not contain valid spans.",
|
|
5260
|
+
severity: "error"
|
|
5261
|
+
}
|
|
5262
|
+
],
|
|
5263
|
+
resolved
|
|
5264
|
+
)
|
|
5265
|
+
);
|
|
5266
|
+
}
|
|
5267
|
+
const mapped = mapOpenInferenceEvents(document);
|
|
5268
|
+
const warnings = attachSingleSourceFile(
|
|
5269
|
+
[...document.warnings, ...mapped.warnings],
|
|
5270
|
+
resolved
|
|
5271
|
+
);
|
|
5272
|
+
const unsupportedFields = [
|
|
5273
|
+
...document.unsupportedFields,
|
|
5274
|
+
...mapped.unsupportedFields
|
|
5275
|
+
].sort((a, b) => a.localeCompare(b));
|
|
5276
|
+
return {
|
|
5277
|
+
format: OPENINFERENCE_READER_FORMAT,
|
|
5278
|
+
events: mapped.events,
|
|
5279
|
+
runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
|
|
5280
|
+
warnings,
|
|
5281
|
+
unsupportedFields,
|
|
5282
|
+
sourceFiles: resolved.sourceFiles
|
|
5283
|
+
};
|
|
5284
|
+
}
|
|
5285
|
+
};
|
|
5286
|
+
function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
|
|
5287
|
+
if (!isRecord13(value)) {
|
|
5288
|
+
unsupportedFields.push(field);
|
|
5289
|
+
warnings.push({
|
|
5290
|
+
code: "otlp_attribute_value_invalid",
|
|
5291
|
+
message: "OTLP attribute value was not an AnyValue object.",
|
|
5292
|
+
severity: "warning",
|
|
5293
|
+
field
|
|
5294
|
+
});
|
|
5295
|
+
return void 0;
|
|
5296
|
+
}
|
|
5297
|
+
if (typeof value.stringValue === "string") return value.stringValue;
|
|
5298
|
+
if (typeof value.boolValue === "boolean") return value.boolValue;
|
|
5299
|
+
if (typeof value.intValue === "number" && Number.isFinite(value.intValue)) {
|
|
5300
|
+
return value.intValue;
|
|
5301
|
+
}
|
|
5302
|
+
if (typeof value.intValue === "string" && value.intValue.trim() !== "") {
|
|
5303
|
+
const n = Number(value.intValue);
|
|
5304
|
+
if (Number.isFinite(n)) return n;
|
|
5305
|
+
}
|
|
5306
|
+
if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
|
|
5307
|
+
return value.doubleValue;
|
|
5308
|
+
}
|
|
5309
|
+
if (isRecord13(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
|
|
5310
|
+
return value.arrayValue.values.map(
|
|
5311
|
+
(item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
|
|
5312
|
+
);
|
|
5313
|
+
}
|
|
5314
|
+
if (isRecord13(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
|
|
5315
|
+
const out = {};
|
|
5316
|
+
for (const [index, item] of value.kvlistValue.values.entries()) {
|
|
5317
|
+
if (!isRecord13(item) || typeof item.key !== "string") {
|
|
5318
|
+
unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
|
|
5319
|
+
continue;
|
|
5320
|
+
}
|
|
5321
|
+
out[item.key] = parseOtlpAnyValue(
|
|
5322
|
+
item.value,
|
|
5323
|
+
`${field}.kvlistValue.values[${index}].value`,
|
|
5324
|
+
warnings,
|
|
5325
|
+
unsupportedFields
|
|
5326
|
+
);
|
|
5327
|
+
}
|
|
5328
|
+
return out;
|
|
5329
|
+
}
|
|
5330
|
+
if (typeof value.bytesValue === "string") {
|
|
5331
|
+
unsupportedFields.push(field);
|
|
5332
|
+
warnings.push({
|
|
5333
|
+
code: "otlp_bytes_value_summarized",
|
|
5334
|
+
message: "OTLP bytesValue attribute was summarized instead of decoded.",
|
|
5335
|
+
severity: "warning",
|
|
5336
|
+
field
|
|
5337
|
+
});
|
|
5338
|
+
return { type: "bytes", length: value.bytesValue.length };
|
|
5339
|
+
}
|
|
5340
|
+
unsupportedFields.push(field);
|
|
5341
|
+
warnings.push({
|
|
5342
|
+
code: "otlp_attribute_value_unsupported",
|
|
5343
|
+
message: "OTLP attribute value used an unsupported AnyValue shape.",
|
|
5344
|
+
severity: "warning",
|
|
5345
|
+
field
|
|
5346
|
+
});
|
|
5347
|
+
return void 0;
|
|
5348
|
+
}
|
|
5349
|
+
function parseOtlpAttributes(value, pathPrefix) {
|
|
5350
|
+
const attributes = {};
|
|
5351
|
+
const warnings = [];
|
|
5352
|
+
const unsupportedFields = [];
|
|
5353
|
+
if (value === void 0) {
|
|
5354
|
+
return { attributes, warnings, unsupportedFields };
|
|
5355
|
+
}
|
|
5356
|
+
if (!Array.isArray(value)) {
|
|
5357
|
+
unsupportedFields.push(pathPrefix);
|
|
5358
|
+
warnings.push({
|
|
5359
|
+
code: "otlp_attributes_invalid",
|
|
5360
|
+
message: "OTLP attributes field was not an array.",
|
|
5361
|
+
severity: "warning",
|
|
5362
|
+
field: pathPrefix
|
|
5363
|
+
});
|
|
5364
|
+
return { attributes, warnings, unsupportedFields };
|
|
5365
|
+
}
|
|
5366
|
+
for (const [index, item] of value.entries()) {
|
|
5367
|
+
const field = `${pathPrefix}[${index}]`;
|
|
5368
|
+
if (!isRecord13(item) || typeof item.key !== "string") {
|
|
5369
|
+
unsupportedFields.push(field);
|
|
5370
|
+
warnings.push({
|
|
5371
|
+
code: "otlp_attribute_invalid",
|
|
5372
|
+
message: "Skipped OTLP attribute without a string key.",
|
|
5373
|
+
severity: "warning",
|
|
5374
|
+
field
|
|
5375
|
+
});
|
|
5376
|
+
continue;
|
|
5377
|
+
}
|
|
5378
|
+
const parsed = parseOtlpAnyValue(
|
|
5379
|
+
item.value,
|
|
5380
|
+
`${field}.value`,
|
|
5381
|
+
warnings,
|
|
5382
|
+
unsupportedFields
|
|
5383
|
+
);
|
|
5384
|
+
if (parsed !== void 0) {
|
|
5385
|
+
attributes[item.key] = parsed;
|
|
5386
|
+
}
|
|
5387
|
+
}
|
|
5388
|
+
return { attributes, warnings, unsupportedFields };
|
|
5389
|
+
}
|
|
5390
|
+
function looksLikeOtlpSpan(value) {
|
|
5391
|
+
return isRecord13(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
|
|
5392
|
+
}
|
|
5393
|
+
function extractOtlpDocument(root) {
|
|
5394
|
+
if (!isRecord13(root) || !Array.isArray(root.resourceSpans)) return void 0;
|
|
5395
|
+
const spans = [];
|
|
5396
|
+
const warnings = [];
|
|
5397
|
+
const unsupportedFields = [];
|
|
5398
|
+
for (const [resourceIndex, resourceSpan] of root.resourceSpans.entries()) {
|
|
5399
|
+
const resourcePath = `resourceSpans[${resourceIndex}]`;
|
|
5400
|
+
if (!isRecord13(resourceSpan)) {
|
|
5401
|
+
unsupportedFields.push(resourcePath);
|
|
5402
|
+
continue;
|
|
5403
|
+
}
|
|
5404
|
+
const resource = readRecordField(resourceSpan, "resource");
|
|
5405
|
+
const resourceParsed = parseOtlpAttributes(
|
|
5406
|
+
resource?.attributes,
|
|
5407
|
+
`${resourcePath}.resource.attributes`
|
|
5408
|
+
);
|
|
5409
|
+
warnings.push(...resourceParsed.warnings);
|
|
5410
|
+
unsupportedFields.push(...resourceParsed.unsupportedFields);
|
|
5411
|
+
if (!Array.isArray(resourceSpan.scopeSpans)) {
|
|
5412
|
+
unsupportedFields.push(`${resourcePath}.scopeSpans`);
|
|
5413
|
+
warnings.push({
|
|
5414
|
+
code: "otlp_scope_spans_missing",
|
|
5415
|
+
message: "OTLP resourceSpans entry did not contain a scopeSpans array.",
|
|
5416
|
+
severity: "warning",
|
|
5417
|
+
field: `${resourcePath}.scopeSpans`
|
|
5418
|
+
});
|
|
5419
|
+
continue;
|
|
5420
|
+
}
|
|
5421
|
+
for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
|
|
5422
|
+
const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
|
|
5423
|
+
if (!isRecord13(scopeSpan)) {
|
|
5424
|
+
unsupportedFields.push(scopePath);
|
|
5425
|
+
continue;
|
|
5426
|
+
}
|
|
5427
|
+
const scope = readRecordField(scopeSpan, "scope");
|
|
5428
|
+
const scopeParsed = parseOtlpAttributes(
|
|
5429
|
+
scope?.attributes,
|
|
5430
|
+
`${scopePath}.scope.attributes`
|
|
5431
|
+
);
|
|
5432
|
+
warnings.push(...scopeParsed.warnings);
|
|
5433
|
+
unsupportedFields.push(...scopeParsed.unsupportedFields);
|
|
5434
|
+
if (!Array.isArray(scopeSpan.spans)) {
|
|
5435
|
+
unsupportedFields.push(`${scopePath}.spans`);
|
|
5436
|
+
warnings.push({
|
|
5437
|
+
code: "otlp_spans_missing",
|
|
5438
|
+
message: "OTLP scopeSpans entry did not contain a spans array.",
|
|
5439
|
+
severity: "warning",
|
|
5440
|
+
field: `${scopePath}.spans`
|
|
5441
|
+
});
|
|
5442
|
+
continue;
|
|
5443
|
+
}
|
|
5444
|
+
for (const [spanIndex, span] of scopeSpan.spans.entries()) {
|
|
5445
|
+
const spanPath = `${scopePath}.spans[${spanIndex}]`;
|
|
5446
|
+
if (!looksLikeOtlpSpan(span)) {
|
|
5447
|
+
unsupportedFields.push(spanPath);
|
|
5448
|
+
warnings.push({
|
|
5449
|
+
code: "otlp_invalid_span",
|
|
5450
|
+
message: "Skipped OTLP span without required traceId, spanId, or name.",
|
|
5451
|
+
severity: "warning",
|
|
5452
|
+
field: spanPath
|
|
5453
|
+
});
|
|
5454
|
+
continue;
|
|
5455
|
+
}
|
|
5456
|
+
spans.push({
|
|
5457
|
+
span,
|
|
5458
|
+
resourceAttributes: resourceParsed.attributes,
|
|
5459
|
+
scopeAttributes: scopeParsed.attributes,
|
|
5460
|
+
scopeName: readStringField(scope ?? {}, ["name"]),
|
|
5461
|
+
scopeVersion: readStringField(scope ?? {}, ["version"]),
|
|
5462
|
+
pathPrefix: spanPath
|
|
5463
|
+
});
|
|
5464
|
+
}
|
|
5465
|
+
}
|
|
5466
|
+
}
|
|
5467
|
+
if (spans.length === 0) {
|
|
5468
|
+
warnings.push({
|
|
5469
|
+
code: "otlp_no_valid_spans",
|
|
5470
|
+
message: "OTLP JSON payload did not contain any valid spans.",
|
|
5471
|
+
severity: "error"
|
|
5472
|
+
});
|
|
5473
|
+
return {
|
|
5474
|
+
spans,
|
|
5475
|
+
confidence: 0.7,
|
|
5476
|
+
description: "Malformed OTLP JSON trace payload",
|
|
5477
|
+
warnings,
|
|
5478
|
+
unsupportedFields
|
|
5479
|
+
};
|
|
5480
|
+
}
|
|
5481
|
+
return {
|
|
5482
|
+
spans,
|
|
5483
|
+
confidence: 0.93,
|
|
5484
|
+
description: "OTLP JSON trace payload",
|
|
5485
|
+
warnings,
|
|
5486
|
+
unsupportedFields
|
|
5487
|
+
};
|
|
5488
|
+
}
|
|
5489
|
+
function mapOtlpStatus(status) {
|
|
5490
|
+
if (!isRecord13(status)) return void 0;
|
|
5491
|
+
const rawCode = status.code;
|
|
5492
|
+
if (typeof rawCode !== "string") return void 0;
|
|
5493
|
+
switch (rawCode.toUpperCase()) {
|
|
5494
|
+
case "STATUS_CODE_OK":
|
|
5495
|
+
case "OK":
|
|
5496
|
+
return "ok";
|
|
5497
|
+
case "STATUS_CODE_ERROR":
|
|
5498
|
+
case "ERROR":
|
|
5499
|
+
return "error";
|
|
5500
|
+
case "STATUS_CODE_UNSET":
|
|
5501
|
+
case "UNSET":
|
|
5502
|
+
return "unknown";
|
|
5503
|
+
default:
|
|
5504
|
+
return "unknown";
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5507
|
+
function readOtlpKind(attributes, pathPrefix) {
|
|
5508
|
+
const warnings = [];
|
|
5509
|
+
const agentInspectKind = attributes["agent_inspect.kind"];
|
|
5510
|
+
if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
|
|
5511
|
+
return { kind: agentInspectKind, warnings };
|
|
5512
|
+
}
|
|
5513
|
+
const operation = attributes["gen_ai.operation.name"];
|
|
5514
|
+
if (typeof operation === "string") {
|
|
5515
|
+
switch (operation) {
|
|
5516
|
+
case "generate_content":
|
|
5517
|
+
case "chat":
|
|
5518
|
+
return { kind: "LLM", warnings };
|
|
5519
|
+
case "execute_tool":
|
|
5520
|
+
return { kind: "TOOL", warnings };
|
|
5521
|
+
case "invoke_agent":
|
|
5522
|
+
return { kind: "AGENT", warnings };
|
|
5523
|
+
default:
|
|
5524
|
+
warnings.push({
|
|
5525
|
+
code: "otlp_gen_ai_operation_semantic_loss",
|
|
5526
|
+
message: `OTLP GenAI operation "${operation}" mapped to AgentInspect LOGIC.`,
|
|
5527
|
+
severity: "warning",
|
|
5528
|
+
field: `${pathPrefix}.attributes.gen_ai.operation.name`
|
|
5529
|
+
});
|
|
5530
|
+
return { kind: "LOGIC", warnings };
|
|
5531
|
+
}
|
|
5532
|
+
}
|
|
5533
|
+
warnings.push({
|
|
5534
|
+
code: "otlp_kind_unknown",
|
|
5535
|
+
message: "OTLP span had no AgentInspect kind or GenAI operation; mapped to LOGIC.",
|
|
5536
|
+
severity: "warning",
|
|
5537
|
+
field: `${pathPrefix}.attributes`
|
|
5538
|
+
});
|
|
5539
|
+
return { kind: "LOGIC", warnings };
|
|
5540
|
+
}
|
|
5541
|
+
function readOtlpTokenUsage(attributes) {
|
|
5542
|
+
const input3 = attributes["gen_ai.usage.input_tokens"];
|
|
5543
|
+
const output2 = attributes["gen_ai.usage.output_tokens"];
|
|
5544
|
+
const usage = {};
|
|
5545
|
+
if (typeof input3 === "number" && Number.isFinite(input3) && input3 >= 0) {
|
|
5546
|
+
usage.input = input3;
|
|
5547
|
+
}
|
|
5548
|
+
if (typeof output2 === "number" && Number.isFinite(output2) && output2 >= 0) {
|
|
5549
|
+
usage.output = output2;
|
|
5550
|
+
}
|
|
5551
|
+
if (usage.input !== void 0 && usage.output !== void 0) {
|
|
5552
|
+
usage.total = usage.input + usage.output;
|
|
5553
|
+
}
|
|
5554
|
+
return Object.keys(usage).length > 0 ? usage : void 0;
|
|
5555
|
+
}
|
|
5556
|
+
function readOtlpConfidence(attributes) {
|
|
5557
|
+
return readOpenInferenceConfidence(attributes);
|
|
5558
|
+
}
|
|
5559
|
+
function sanitizeOtlpAttributes(attributes, pathPrefix) {
|
|
5560
|
+
const ownerPath = pathPrefix.endsWith(".attributes") ? pathPrefix.slice(0, -".attributes".length) : pathPrefix;
|
|
5561
|
+
const sanitized = sanitizeOpenInferenceAttributes(attributes, ownerPath);
|
|
5562
|
+
return {
|
|
5563
|
+
...sanitized,
|
|
5564
|
+
warnings: sanitized.warnings.map(
|
|
5565
|
+
(warning) => warning.code === "openinference_sensitive_attribute_summarized" ? {
|
|
5566
|
+
...warning,
|
|
5567
|
+
code: "otlp_sensitive_attribute_summarized",
|
|
5568
|
+
message: "OTLP prompt/output/document attribute(s) were summarized instead of copied verbatim."
|
|
5569
|
+
} : warning
|
|
5570
|
+
)
|
|
5571
|
+
};
|
|
5572
|
+
}
|
|
5573
|
+
function mapOtlpEvents(value, pathPrefix) {
|
|
5574
|
+
const warnings = [];
|
|
5575
|
+
const unsupportedFields = [];
|
|
5576
|
+
if (value === void 0) return { warnings, unsupportedFields };
|
|
5577
|
+
if (!Array.isArray(value)) {
|
|
5578
|
+
unsupportedFields.push(pathPrefix);
|
|
5579
|
+
warnings.push({
|
|
5580
|
+
code: "otlp_events_invalid",
|
|
5581
|
+
message: "OTLP events field was not an array.",
|
|
5582
|
+
severity: "warning",
|
|
5583
|
+
field: pathPrefix
|
|
5584
|
+
});
|
|
5585
|
+
return { warnings, unsupportedFields };
|
|
5586
|
+
}
|
|
5587
|
+
const events = [];
|
|
5588
|
+
for (const [index, event] of value.entries()) {
|
|
5589
|
+
const eventPath = `${pathPrefix}[${index}]`;
|
|
5590
|
+
if (!isRecord13(event)) {
|
|
5591
|
+
unsupportedFields.push(eventPath);
|
|
5592
|
+
continue;
|
|
5593
|
+
}
|
|
5594
|
+
const parsedAttributes = parseOtlpAttributes(
|
|
5595
|
+
event.attributes,
|
|
5596
|
+
`${eventPath}.attributes`
|
|
5597
|
+
);
|
|
5598
|
+
warnings.push(...parsedAttributes.warnings);
|
|
5599
|
+
unsupportedFields.push(...parsedAttributes.unsupportedFields);
|
|
5600
|
+
const sanitized = sanitizeOtlpAttributes(
|
|
5601
|
+
parsedAttributes.attributes,
|
|
5602
|
+
`${eventPath}.attributes`
|
|
5603
|
+
);
|
|
5604
|
+
warnings.push(...sanitized.warnings);
|
|
5605
|
+
unsupportedFields.push(...sanitized.unsupportedFields);
|
|
5606
|
+
const out = {};
|
|
5607
|
+
const name = readStringField(event, ["name"]);
|
|
5608
|
+
if (name !== void 0) {
|
|
5609
|
+
out.name = name;
|
|
5610
|
+
}
|
|
5611
|
+
const timestamp = parseUnixNanoToIso(event.timeUnixNano);
|
|
5612
|
+
if (timestamp !== void 0) {
|
|
5613
|
+
out.timestamp = timestamp;
|
|
5614
|
+
} else if (event.timeUnixNano !== void 0) {
|
|
5615
|
+
unsupportedFields.push(`${eventPath}.timeUnixNano`);
|
|
5616
|
+
warnings.push({
|
|
5617
|
+
code: "otlp_event_timestamp_invalid",
|
|
5618
|
+
message: "OTLP event timeUnixNano could not be parsed.",
|
|
5619
|
+
severity: "warning",
|
|
5620
|
+
field: `${eventPath}.timeUnixNano`
|
|
5621
|
+
});
|
|
5622
|
+
}
|
|
5623
|
+
if (Object.keys(sanitized.attributes).length > 0) {
|
|
5624
|
+
out.attributes = sanitized.attributes;
|
|
5625
|
+
}
|
|
5626
|
+
events.push(out);
|
|
5627
|
+
}
|
|
5628
|
+
return {
|
|
5629
|
+
events: events.length > 0 ? events : void 0,
|
|
5630
|
+
warnings,
|
|
5631
|
+
unsupportedFields
|
|
5632
|
+
};
|
|
5633
|
+
}
|
|
5634
|
+
function mapOtlpSpan(context) {
|
|
5635
|
+
const { span, pathPrefix } = context;
|
|
5636
|
+
const warnings = [];
|
|
5637
|
+
const unsupportedFields = [];
|
|
5638
|
+
const parsedSpanAttributes = parseOtlpAttributes(
|
|
5639
|
+
span.attributes,
|
|
5640
|
+
`${pathPrefix}.attributes`
|
|
5641
|
+
);
|
|
5642
|
+
warnings.push(...parsedSpanAttributes.warnings);
|
|
5643
|
+
unsupportedFields.push(...parsedSpanAttributes.unsupportedFields);
|
|
5644
|
+
const sanitizedSpanAttributes = sanitizeOtlpAttributes(
|
|
5645
|
+
parsedSpanAttributes.attributes,
|
|
5646
|
+
`${pathPrefix}.attributes`
|
|
5647
|
+
);
|
|
5648
|
+
warnings.push(...sanitizedSpanAttributes.warnings);
|
|
5649
|
+
unsupportedFields.push(...sanitizedSpanAttributes.unsupportedFields);
|
|
5650
|
+
const attributes = {
|
|
5651
|
+
...sanitizedSpanAttributes.attributes
|
|
5652
|
+
};
|
|
5653
|
+
for (const [key, value] of Object.entries(context.resourceAttributes)) {
|
|
5654
|
+
attributes[`resource.${key}`] = value;
|
|
5655
|
+
}
|
|
5656
|
+
for (const [key, value] of Object.entries(context.scopeAttributes)) {
|
|
5657
|
+
attributes[`scope.${key}`] = value;
|
|
5658
|
+
}
|
|
5659
|
+
if (context.scopeName !== void 0) {
|
|
5660
|
+
attributes["scope.name"] = context.scopeName;
|
|
5661
|
+
}
|
|
5662
|
+
if (context.scopeVersion !== void 0) {
|
|
5663
|
+
attributes["scope.version"] = context.scopeVersion;
|
|
5664
|
+
}
|
|
5665
|
+
for (const [key, value] of Object.entries(span)) {
|
|
5666
|
+
if (OTLP_SPAN_KEYS.has(key)) continue;
|
|
5667
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
5668
|
+
if (value === null || typeof value !== "object") {
|
|
5669
|
+
attributes[`otlp.${key}`] = value;
|
|
5670
|
+
} else {
|
|
5671
|
+
attributes[`otlp.${key}.summary`] = summarizeAttributeValue(value);
|
|
5672
|
+
warnings.push({
|
|
5673
|
+
code: "otlp_unsupported_field_summarized",
|
|
5674
|
+
message: `Unsupported OTLP span field "${key}" was summarized.`,
|
|
5675
|
+
severity: "warning",
|
|
5676
|
+
field: `${pathPrefix}.${key}`
|
|
5677
|
+
});
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
5680
|
+
for (const key of [
|
|
5681
|
+
"droppedAttributesCount",
|
|
5682
|
+
"droppedEventsCount",
|
|
5683
|
+
"droppedLinksCount",
|
|
5684
|
+
"links"
|
|
5685
|
+
]) {
|
|
5686
|
+
if (span[key] !== void 0) {
|
|
5687
|
+
unsupportedFields.push(`${pathPrefix}.${key}`);
|
|
5688
|
+
warnings.push({
|
|
5689
|
+
code: "otlp_span_field_not_mapped",
|
|
5690
|
+
message: `OTLP span field "${key}" is not represented in AgentInspect events.`,
|
|
5691
|
+
severity: "warning",
|
|
5692
|
+
field: `${pathPrefix}.${key}`
|
|
5693
|
+
});
|
|
5694
|
+
}
|
|
5695
|
+
}
|
|
5696
|
+
const events = mapOtlpEvents(span.events, `${pathPrefix}.events`);
|
|
5697
|
+
warnings.push(...events.warnings);
|
|
5698
|
+
unsupportedFields.push(...events.unsupportedFields);
|
|
5699
|
+
if (events.events !== void 0) {
|
|
5700
|
+
attributes["otlp.events"] = events.events;
|
|
5701
|
+
}
|
|
5702
|
+
const traceId = readStringField(span, ["traceId"]) ?? "trace-unknown";
|
|
5703
|
+
const spanId = readStringField(span, ["spanId"]) ?? "span-unknown";
|
|
5704
|
+
const parentSpanId = readStringField(span, ["parentSpanId"]);
|
|
5705
|
+
const startedAt = readOpenInferenceTimestamp(
|
|
5706
|
+
span,
|
|
5707
|
+
["startTimeUnixNano"],
|
|
5708
|
+
[]
|
|
5709
|
+
);
|
|
5710
|
+
const endedAt = readOpenInferenceTimestamp(span, ["endTimeUnixNano"], []);
|
|
5711
|
+
const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
|
|
5712
|
+
if (startedAt === void 0) {
|
|
5713
|
+
unsupportedFields.push(`${pathPrefix}.startTimeUnixNano`);
|
|
5714
|
+
warnings.push({
|
|
5715
|
+
code: "otlp_missing_start_time",
|
|
5716
|
+
message: "OTLP span is missing a valid startTimeUnixNano; using Unix epoch.",
|
|
5717
|
+
severity: "warning",
|
|
5718
|
+
field: `${pathPrefix}.startTimeUnixNano`
|
|
5719
|
+
});
|
|
5720
|
+
}
|
|
5721
|
+
const { kind, warnings: kindWarnings } = readOtlpKind(
|
|
5722
|
+
parsedSpanAttributes.attributes,
|
|
5723
|
+
pathPrefix
|
|
5724
|
+
);
|
|
5725
|
+
warnings.push(...kindWarnings);
|
|
5726
|
+
const status = mapOtlpStatus(span.status);
|
|
5727
|
+
const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
|
|
5728
|
+
const errorMessage = isRecord13(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
|
|
5729
|
+
const event = {
|
|
5730
|
+
schemaVersion: "0.2",
|
|
5731
|
+
eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
|
|
5732
|
+
runId: typeof parsedSpanAttributes.attributes["agent_inspect.run_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.run_id"] : traceId,
|
|
5733
|
+
kind,
|
|
5734
|
+
name: readStringField(span, ["name"]) ?? spanId,
|
|
5735
|
+
timestamp,
|
|
5736
|
+
confidence: readOtlpConfidence(parsedSpanAttributes.attributes),
|
|
5737
|
+
source: {
|
|
5738
|
+
type: "otel",
|
|
5739
|
+
name: context.scopeName ?? (typeof context.resourceAttributes["service.name"] === "string" ? context.resourceAttributes["service.name"] : "otlp-json"),
|
|
5740
|
+
...context.scopeVersion !== void 0 ? { version: context.scopeVersion } : {}
|
|
5741
|
+
},
|
|
5742
|
+
attributes,
|
|
5743
|
+
trace: {
|
|
5744
|
+
traceId,
|
|
5745
|
+
spanId,
|
|
5746
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
5747
|
+
}
|
|
5748
|
+
};
|
|
5749
|
+
if (status !== void 0) {
|
|
5750
|
+
event.status = status;
|
|
5751
|
+
}
|
|
5752
|
+
if (startedAt !== void 0) {
|
|
5753
|
+
event.startedAt = startedAt;
|
|
5754
|
+
}
|
|
5755
|
+
if (endedAt !== void 0) {
|
|
5756
|
+
event.endedAt = endedAt;
|
|
5757
|
+
}
|
|
5758
|
+
const durationMs = durationBetweenIso(startedAt, endedAt);
|
|
5759
|
+
if (durationMs !== void 0) {
|
|
5760
|
+
event.durationMs = durationMs;
|
|
5761
|
+
}
|
|
5762
|
+
if (tokenUsage !== void 0) {
|
|
5763
|
+
event.tokenUsage = tokenUsage;
|
|
5764
|
+
}
|
|
5765
|
+
if (status === "error") {
|
|
5766
|
+
event.error = {
|
|
5767
|
+
message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OTLP span error"
|
|
5768
|
+
};
|
|
5769
|
+
}
|
|
5770
|
+
return {
|
|
5771
|
+
event,
|
|
5772
|
+
warnings,
|
|
5773
|
+
unsupportedFields,
|
|
5774
|
+
spanId,
|
|
5775
|
+
...parentSpanId !== void 0 ? { parentSpanId } : {}
|
|
5776
|
+
};
|
|
5777
|
+
}
|
|
5778
|
+
function mapOtlpEventsToPersisted(document) {
|
|
5779
|
+
const mapped = document.spans.map((span) => mapOtlpSpan(span));
|
|
5780
|
+
const spanIdToEventId = new Map(
|
|
5781
|
+
mapped.map((span) => [span.spanId, span.event.eventId])
|
|
5782
|
+
);
|
|
5783
|
+
for (const span of mapped) {
|
|
5784
|
+
if (span.parentSpanId === void 0) continue;
|
|
5785
|
+
span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
|
|
5786
|
+
}
|
|
5787
|
+
return {
|
|
5788
|
+
events: mapped.map((span) => span.event),
|
|
5789
|
+
warnings: mapped.flatMap((span) => span.warnings),
|
|
5790
|
+
unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
|
|
5791
|
+
};
|
|
5792
|
+
}
|
|
5793
|
+
var otlpJsonReader = {
|
|
5794
|
+
format: OTLP_READER_FORMAT,
|
|
5795
|
+
name: "OTLP JSON",
|
|
5796
|
+
async detect(input3) {
|
|
5797
|
+
const resolved = await resolveInput(input3);
|
|
5798
|
+
if (!resolved) return void 0;
|
|
5799
|
+
let parsed;
|
|
5800
|
+
try {
|
|
5801
|
+
parsed = parseJsonDocument(resolved.content);
|
|
5802
|
+
} catch {
|
|
5803
|
+
return void 0;
|
|
5804
|
+
}
|
|
5805
|
+
const document = extractOtlpDocument(parsed);
|
|
5806
|
+
if (!document) return void 0;
|
|
5807
|
+
return {
|
|
5808
|
+
format: OTLP_READER_FORMAT,
|
|
5809
|
+
confidence: document.confidence,
|
|
5810
|
+
readerName: "OTLP JSON",
|
|
5811
|
+
description: document.description,
|
|
5812
|
+
warnings: attachSingleSourceFile(document.warnings, resolved)
|
|
5813
|
+
};
|
|
5814
|
+
},
|
|
5815
|
+
async read(input3) {
|
|
5816
|
+
const resolved = await resolveInput(input3);
|
|
5817
|
+
if (!resolved) {
|
|
5818
|
+
throw new TraceReadError(
|
|
5819
|
+
"unsupported_format",
|
|
5820
|
+
"OTLP JSON reader requires file, string, or buffer input."
|
|
5821
|
+
);
|
|
5822
|
+
}
|
|
5823
|
+
let parsed;
|
|
5824
|
+
try {
|
|
5825
|
+
parsed = parseJsonDocument(resolved.content);
|
|
5826
|
+
} catch {
|
|
5827
|
+
throw new TraceReadError("unsupported_format", "OTLP JSON input is not valid JSON.", [
|
|
5828
|
+
{
|
|
5829
|
+
code: "otlp_invalid_json",
|
|
5830
|
+
message: "OTLP JSON reader could not parse the input as JSON.",
|
|
5831
|
+
severity: "error"
|
|
5832
|
+
}
|
|
5833
|
+
]);
|
|
5834
|
+
}
|
|
5835
|
+
const document = extractOtlpDocument(parsed);
|
|
5836
|
+
if (!document || document.spans.length === 0) {
|
|
5837
|
+
throw new TraceReadError(
|
|
5838
|
+
"unsupported_format",
|
|
5839
|
+
"No valid OTLP spans found.",
|
|
5840
|
+
attachSingleSourceFile(
|
|
5841
|
+
document?.warnings ?? [
|
|
5842
|
+
{
|
|
5843
|
+
code: "otlp_no_valid_spans",
|
|
5844
|
+
message: "OTLP JSON input did not contain valid spans.",
|
|
5845
|
+
severity: "error"
|
|
5846
|
+
}
|
|
5847
|
+
],
|
|
5848
|
+
resolved
|
|
5849
|
+
)
|
|
5850
|
+
);
|
|
5851
|
+
}
|
|
5852
|
+
const mapped = mapOtlpEventsToPersisted(document);
|
|
5853
|
+
const warnings = attachSingleSourceFile(
|
|
5854
|
+
[...document.warnings, ...mapped.warnings],
|
|
5855
|
+
resolved
|
|
5856
|
+
);
|
|
5857
|
+
const unsupportedFields = [
|
|
5858
|
+
...document.unsupportedFields,
|
|
5859
|
+
...mapped.unsupportedFields
|
|
5860
|
+
].sort((a, b) => a.localeCompare(b));
|
|
5861
|
+
return {
|
|
5862
|
+
format: OTLP_READER_FORMAT,
|
|
5863
|
+
events: mapped.events,
|
|
5864
|
+
runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
|
|
5865
|
+
warnings,
|
|
5866
|
+
unsupportedFields,
|
|
5867
|
+
sourceFiles: resolved.sourceFiles
|
|
5868
|
+
};
|
|
5869
|
+
}
|
|
5870
|
+
};
|
|
5871
|
+
var agentInspectJsonlReader = {
|
|
5872
|
+
format: "agent-inspect-jsonl",
|
|
5873
|
+
name: "AgentInspect JSONL",
|
|
5874
|
+
async detect(input3) {
|
|
5875
|
+
const resolved = await resolveInput(input3);
|
|
5876
|
+
if (!resolved) return void 0;
|
|
5877
|
+
const detected = detectJsonlFormat(resolved.content);
|
|
5878
|
+
if (detected.validRows === 0 || detected.format === "empty") {
|
|
5879
|
+
return void 0;
|
|
5880
|
+
}
|
|
5881
|
+
return {
|
|
5882
|
+
format: "agent-inspect-jsonl",
|
|
5883
|
+
confidence: 0.95,
|
|
5884
|
+
readerName: "AgentInspect JSONL",
|
|
5885
|
+
description: agentInspectFormatLabel(detected.format),
|
|
5886
|
+
warnings: attachSingleSourceFile(detected.warnings, resolved)
|
|
5887
|
+
};
|
|
5888
|
+
},
|
|
5889
|
+
async read(input3) {
|
|
5890
|
+
const resolved = await resolveInput(input3);
|
|
5891
|
+
if (!resolved) {
|
|
5892
|
+
throw new Error("AgentInspect JSONL reader requires file, directory, string, or buffer input.");
|
|
5893
|
+
}
|
|
5894
|
+
const parsed = parseTraceJsonl(resolved.content, { warnings: false });
|
|
5895
|
+
if (parsed.sourceEventCount === 0) {
|
|
5896
|
+
throw new Error("No valid AgentInspect JSONL events found.");
|
|
5897
|
+
}
|
|
5898
|
+
const events = persistedEventsForParsedTrace(parsed);
|
|
5899
|
+
return {
|
|
5900
|
+
format: agentInspectFormatLabel(parsed.format),
|
|
5901
|
+
events,
|
|
5902
|
+
runs: persistedInspectEventsToRunTrees(events, { skipInvalid: true }),
|
|
5903
|
+
warnings: parsed.format === "mixed" ? attachSingleSourceFile(
|
|
5904
|
+
[
|
|
5905
|
+
{
|
|
5906
|
+
code: "mixed_agent_inspect_jsonl",
|
|
5907
|
+
message: "Trace input mixes schemaVersion 0.1 and 0.2 rows; events were normalized for reading.",
|
|
5908
|
+
severity: "warning"
|
|
5909
|
+
}
|
|
5910
|
+
],
|
|
5911
|
+
resolved
|
|
5912
|
+
) : [],
|
|
5913
|
+
unsupportedFields: [],
|
|
5914
|
+
sourceFiles: resolved.sourceFiles
|
|
5915
|
+
};
|
|
5916
|
+
}
|
|
5917
|
+
};
|
|
5918
|
+
var DEFAULT_TRACE_READERS = [
|
|
5919
|
+
agentInspectJsonlReader,
|
|
5920
|
+
openInferenceJsonReader,
|
|
5921
|
+
otlpJsonReader
|
|
5922
|
+
];
|
|
5923
|
+
async function detectTraceFormat(input3, options = {}) {
|
|
5924
|
+
const readers = options.readers ?? DEFAULT_TRACE_READERS;
|
|
5925
|
+
if (options.format !== void 0) {
|
|
5926
|
+
const reader = findReaderByFormat(options.format, readers);
|
|
5927
|
+
if (!reader) {
|
|
5928
|
+
return {
|
|
5929
|
+
status: "unsupported",
|
|
5930
|
+
candidates: [],
|
|
5931
|
+
warnings: [
|
|
5932
|
+
{
|
|
5933
|
+
code: "unsupported_format",
|
|
5934
|
+
message: `No trace reader is registered for format "${options.format}".`,
|
|
5935
|
+
severity: "error"
|
|
5936
|
+
}
|
|
5937
|
+
]
|
|
5938
|
+
};
|
|
5939
|
+
}
|
|
5940
|
+
return {
|
|
5941
|
+
status: "detected",
|
|
5942
|
+
format: reader.format,
|
|
5943
|
+
candidates: [
|
|
5944
|
+
{
|
|
5945
|
+
format: reader.format,
|
|
5946
|
+
confidence: 1,
|
|
5947
|
+
readerName: reader.name,
|
|
5948
|
+
description: "Explicit format override"
|
|
5949
|
+
}
|
|
5950
|
+
],
|
|
5951
|
+
warnings: []
|
|
5952
|
+
};
|
|
5953
|
+
}
|
|
5954
|
+
const candidates = [];
|
|
5955
|
+
const warnings = [];
|
|
5956
|
+
for (const reader of readers) {
|
|
5957
|
+
try {
|
|
5958
|
+
const candidate = await reader.detect(input3);
|
|
5959
|
+
if (candidate !== void 0) {
|
|
5960
|
+
candidates.push(normalizeCandidate(reader, candidate));
|
|
5961
|
+
}
|
|
5962
|
+
} catch (error) {
|
|
5963
|
+
if (error instanceof TraceReadError) {
|
|
5964
|
+
warnings.push(...error.warnings);
|
|
5965
|
+
continue;
|
|
5966
|
+
}
|
|
5967
|
+
warnings.push({
|
|
5968
|
+
code: "reader_detect_failed",
|
|
5969
|
+
message: error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed during detection.`,
|
|
5970
|
+
severity: "warning"
|
|
5971
|
+
});
|
|
5972
|
+
}
|
|
5973
|
+
}
|
|
5974
|
+
const sorted = sortCandidates(
|
|
5975
|
+
candidates.filter((candidate) => candidate.confidence >= MIN_DETECTION_CONFIDENCE)
|
|
5976
|
+
);
|
|
5977
|
+
const candidateWarnings = collectWarnings(sorted);
|
|
5978
|
+
const lowConfidenceWarnings = candidates.length > sorted.length ? [
|
|
5979
|
+
{
|
|
5980
|
+
code: "low_confidence_candidates",
|
|
5981
|
+
message: `Ignored ${candidates.length - sorted.length} low-confidence format candidate(s).`,
|
|
5982
|
+
severity: "info"
|
|
5983
|
+
}
|
|
5984
|
+
] : [];
|
|
5985
|
+
const allWarnings = dedupeWarnings([
|
|
5986
|
+
...warnings,
|
|
5987
|
+
...candidateWarnings,
|
|
5988
|
+
...lowConfidenceWarnings
|
|
5989
|
+
]);
|
|
5990
|
+
if (sorted.length === 0) {
|
|
5991
|
+
return {
|
|
5992
|
+
status: "unsupported",
|
|
5993
|
+
candidates: [],
|
|
5994
|
+
warnings: allWarnings
|
|
5995
|
+
};
|
|
5996
|
+
}
|
|
5997
|
+
const [best, second] = sorted;
|
|
5998
|
+
if (second !== void 0 && best.confidence - second.confidence <= AMBIGUOUS_CONFIDENCE_DELTA) {
|
|
5999
|
+
return {
|
|
6000
|
+
status: "ambiguous",
|
|
6001
|
+
candidates: sorted,
|
|
6002
|
+
warnings: [
|
|
6003
|
+
...allWarnings,
|
|
6004
|
+
{
|
|
6005
|
+
code: "ambiguous_format_candidates",
|
|
6006
|
+
message: `Top trace format candidates are within ${AMBIGUOUS_CONFIDENCE_DELTA} confidence.`,
|
|
6007
|
+
severity: "warning"
|
|
6008
|
+
}
|
|
6009
|
+
]
|
|
6010
|
+
};
|
|
6011
|
+
}
|
|
6012
|
+
return {
|
|
6013
|
+
status: "detected",
|
|
6014
|
+
format: best.format,
|
|
6015
|
+
candidates: sorted,
|
|
6016
|
+
warnings: allWarnings
|
|
6017
|
+
};
|
|
6018
|
+
}
|
|
6019
|
+
async function readTrace(input3, options = {}) {
|
|
6020
|
+
const readers = options.readers ?? DEFAULT_TRACE_READERS;
|
|
6021
|
+
const detection = await detectTraceFormat(input3, options);
|
|
6022
|
+
if (detection.status === "unsupported" || detection.format === void 0) {
|
|
6023
|
+
throw new TraceReadError(
|
|
6024
|
+
"unsupported_format",
|
|
6025
|
+
"No trace reader could detect the input format.",
|
|
6026
|
+
detection.warnings
|
|
6027
|
+
);
|
|
6028
|
+
}
|
|
6029
|
+
if (detection.status === "ambiguous") {
|
|
6030
|
+
throw new TraceReadError(
|
|
6031
|
+
"ambiguous_format",
|
|
6032
|
+
"Multiple trace readers matched the input with equal confidence.",
|
|
6033
|
+
detection.warnings
|
|
6034
|
+
);
|
|
6035
|
+
}
|
|
6036
|
+
const reader = findReaderByFormat(detection.format, readers);
|
|
6037
|
+
if (!reader) {
|
|
6038
|
+
throw new TraceReadError(
|
|
6039
|
+
"unsupported_format",
|
|
6040
|
+
`No trace reader is registered for format "${detection.format}".`,
|
|
6041
|
+
detection.warnings
|
|
6042
|
+
);
|
|
6043
|
+
}
|
|
6044
|
+
try {
|
|
6045
|
+
const result = await reader.read(input3, { format: detection.format });
|
|
6046
|
+
return {
|
|
6047
|
+
...result,
|
|
6048
|
+
format: result.format || detection.format,
|
|
6049
|
+
warnings: [...detection.warnings, ...result.warnings]
|
|
6050
|
+
};
|
|
6051
|
+
} catch (error) {
|
|
6052
|
+
if (error instanceof TraceReadError) {
|
|
6053
|
+
throw new TraceReadError(
|
|
6054
|
+
error.code,
|
|
6055
|
+
error.message,
|
|
6056
|
+
dedupeWarnings([...detection.warnings, ...error.warnings])
|
|
6057
|
+
);
|
|
6058
|
+
}
|
|
6059
|
+
throw new TraceReadError(
|
|
6060
|
+
"reader_failed",
|
|
6061
|
+
error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed.`,
|
|
6062
|
+
detection.warnings
|
|
6063
|
+
);
|
|
6064
|
+
}
|
|
6065
|
+
}
|
|
6066
|
+
function openTrace(input3, options = {}) {
|
|
6067
|
+
return readTrace(input3, options);
|
|
6068
|
+
}
|
|
6069
|
+
|
|
6070
|
+
// packages/core/src/diff/comparable.ts
|
|
6071
|
+
function extractOutputPreview(meta) {
|
|
3952
6072
|
if (meta === void 0) return void 0;
|
|
3953
6073
|
if ("outputPreview" in meta) return meta.outputPreview;
|
|
3954
6074
|
if ("resultPreview" in meta) return meta.resultPreview;
|
|
@@ -4086,13 +6206,13 @@ function pairSteps(left, right) {
|
|
|
4086
6206
|
return pairs;
|
|
4087
6207
|
}
|
|
4088
6208
|
function compareLeafSteps(L, R, segments, opts, out) {
|
|
4089
|
-
const
|
|
6209
|
+
const path10 = buildPath(segments);
|
|
4090
6210
|
if (L.name !== R.name) {
|
|
4091
6211
|
out.push({
|
|
4092
6212
|
kind: "structure",
|
|
4093
6213
|
severity: "warning",
|
|
4094
6214
|
message: "Step name differs",
|
|
4095
|
-
path:
|
|
6215
|
+
path: path10,
|
|
4096
6216
|
left: L.name,
|
|
4097
6217
|
right: R.name
|
|
4098
6218
|
});
|
|
@@ -4102,7 +6222,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4102
6222
|
kind: "step-type",
|
|
4103
6223
|
severity: "warning",
|
|
4104
6224
|
message: "Step type differs",
|
|
4105
|
-
path:
|
|
6225
|
+
path: path10,
|
|
4106
6226
|
left: L.type,
|
|
4107
6227
|
right: R.type
|
|
4108
6228
|
});
|
|
@@ -4112,7 +6232,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4112
6232
|
kind: "step-status",
|
|
4113
6233
|
severity: "warning",
|
|
4114
6234
|
message: "Step status differs",
|
|
4115
|
-
path:
|
|
6235
|
+
path: path10,
|
|
4116
6236
|
left: L.status,
|
|
4117
6237
|
right: R.status
|
|
4118
6238
|
});
|
|
@@ -4124,7 +6244,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4124
6244
|
kind: "error",
|
|
4125
6245
|
severity: "error",
|
|
4126
6246
|
message: "Step error message differs",
|
|
4127
|
-
path:
|
|
6247
|
+
path: path10,
|
|
4128
6248
|
left: le || void 0,
|
|
4129
6249
|
right: re || void 0
|
|
4130
6250
|
});
|
|
@@ -4142,7 +6262,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4142
6262
|
kind: "duration",
|
|
4143
6263
|
severity: "info",
|
|
4144
6264
|
message: "Step duration differs",
|
|
4145
|
-
path:
|
|
6265
|
+
path: path10,
|
|
4146
6266
|
left: ld,
|
|
4147
6267
|
right: rd
|
|
4148
6268
|
});
|
|
@@ -4155,7 +6275,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4155
6275
|
kind: "metadata",
|
|
4156
6276
|
severity: "info",
|
|
4157
6277
|
message: "Step metadata differs",
|
|
4158
|
-
path:
|
|
6278
|
+
path: path10,
|
|
4159
6279
|
left: L.metadata,
|
|
4160
6280
|
right: R.metadata
|
|
4161
6281
|
});
|
|
@@ -4167,7 +6287,7 @@ function compareLeafSteps(L, R, segments, opts, out) {
|
|
|
4167
6287
|
kind: "output",
|
|
4168
6288
|
severity: "info",
|
|
4169
6289
|
message: "Output preview differs",
|
|
4170
|
-
path:
|
|
6290
|
+
path: path10,
|
|
4171
6291
|
left: L.outputPreview,
|
|
4172
6292
|
right: R.outputPreview
|
|
4173
6293
|
});
|
|
@@ -4605,10 +6725,10 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
4605
6725
|
return 3;
|
|
4606
6726
|
}
|
|
4607
6727
|
if ("TERM_PROGRAM" in env) {
|
|
4608
|
-
const
|
|
6728
|
+
const version2 = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
|
|
4609
6729
|
switch (env.TERM_PROGRAM) {
|
|
4610
6730
|
case "iTerm.app": {
|
|
4611
|
-
return
|
|
6731
|
+
return version2 >= 3 ? 3 : 2;
|
|
4612
6732
|
}
|
|
4613
6733
|
case "Apple_Terminal": {
|
|
4614
6734
|
return 2;
|
|
@@ -4817,11 +6937,11 @@ createChalk({ level: stderrColor ? stderrColor.level : 0 });
|
|
|
4817
6937
|
var source_default = chalk;
|
|
4818
6938
|
|
|
4819
6939
|
// packages/core/src/diff/renderer.ts
|
|
4820
|
-
function formatPath(
|
|
4821
|
-
if (
|
|
6940
|
+
function formatPath(path10) {
|
|
6941
|
+
if (path10 === void 0 || path10.path.length === 0) {
|
|
4822
6942
|
return "(run)";
|
|
4823
6943
|
}
|
|
4824
|
-
return
|
|
6944
|
+
return path10.path.map((s) => s.name).join(" > ");
|
|
4825
6945
|
}
|
|
4826
6946
|
function formatValue(v, verbose) {
|
|
4827
6947
|
if (v === void 0) return "(undefined)";
|
|
@@ -5728,18 +7848,38 @@ async function clean(options = {}) {
|
|
|
5728
7848
|
process.exitCode = 1;
|
|
5729
7849
|
}
|
|
5730
7850
|
}
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
7851
|
+
function mapReaderFormat(format) {
|
|
7852
|
+
switch (format) {
|
|
7853
|
+
case "agent-inspect-v0.1-jsonl":
|
|
7854
|
+
return "0.1";
|
|
7855
|
+
case "agent-inspect-v0.2-jsonl":
|
|
7856
|
+
return "0.2";
|
|
7857
|
+
case "agent-inspect-mixed-jsonl":
|
|
7858
|
+
return "mixed";
|
|
7859
|
+
default:
|
|
7860
|
+
return "empty";
|
|
5737
7861
|
}
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
7862
|
+
}
|
|
7863
|
+
function isMissingFileError(error) {
|
|
7864
|
+
return error !== null && typeof error === "object" && "code" in error && error.code === "ENOENT";
|
|
7865
|
+
}
|
|
7866
|
+
async function readRunTraceEvents(runId, traceDir) {
|
|
7867
|
+
const filePath = getTraceFilePath(runId, traceDir);
|
|
7868
|
+
try {
|
|
7869
|
+
await promises.access(filePath);
|
|
7870
|
+
const result = await openTrace(
|
|
7871
|
+
{ type: "file", path: filePath },
|
|
7872
|
+
{ format: "agent-inspect-jsonl" }
|
|
7873
|
+
);
|
|
7874
|
+
const events = persistedInspectEventsToTraceEvents(result.events);
|
|
7875
|
+
return {
|
|
7876
|
+
events,
|
|
7877
|
+
format: events.length > 0 ? mapReaderFormat(result.format) : "empty"
|
|
7878
|
+
};
|
|
7879
|
+
} catch (error) {
|
|
7880
|
+
if (isMissingFileError(error)) return void 0;
|
|
7881
|
+
throw error;
|
|
5741
7882
|
}
|
|
5742
|
-
return { events: parsed.events, format: parsed.format };
|
|
5743
7883
|
}
|
|
5744
7884
|
|
|
5745
7885
|
// packages/cli/src/view.ts
|
|
@@ -6810,6 +8950,149 @@ async function reportCommand(runId, options = {}) {
|
|
|
6810
8950
|
console.log(result.content);
|
|
6811
8951
|
}
|
|
6812
8952
|
}
|
|
8953
|
+
async function readStdin(stdin) {
|
|
8954
|
+
stdin.setEncoding("utf8");
|
|
8955
|
+
let content = "";
|
|
8956
|
+
for await (const chunk of stdin) {
|
|
8957
|
+
content += typeof chunk === "string" ? chunk : String(chunk);
|
|
8958
|
+
}
|
|
8959
|
+
return content;
|
|
8960
|
+
}
|
|
8961
|
+
async function inputFromPathOrStdin(input3, stdin) {
|
|
8962
|
+
if (input3 === void 0 || input3 === "-") {
|
|
8963
|
+
return { type: "string", content: await readStdin(stdin) };
|
|
8964
|
+
}
|
|
8965
|
+
const stats = await promises.stat(input3);
|
|
8966
|
+
if (stats.isDirectory()) return { type: "directory", path: input3 };
|
|
8967
|
+
return { type: "file", path: input3 };
|
|
8968
|
+
}
|
|
8969
|
+
function printWarnings(result) {
|
|
8970
|
+
if (result.warnings.length === 0 && result.unsupportedFields.length === 0) {
|
|
8971
|
+
return;
|
|
8972
|
+
}
|
|
8973
|
+
console.log("");
|
|
8974
|
+
console.log("Diagnostics");
|
|
8975
|
+
for (const warning of result.warnings) {
|
|
8976
|
+
const where = [
|
|
8977
|
+
warning.sourceFile,
|
|
8978
|
+
warning.line !== void 0 ? `line ${warning.line}` : void 0,
|
|
8979
|
+
warning.field
|
|
8980
|
+
].filter((value) => value !== void 0).join(" ");
|
|
8981
|
+
console.log(
|
|
8982
|
+
`- ${warning.code}: ${warning.message}${where !== "" ? ` (${where})` : ""}`
|
|
8983
|
+
);
|
|
8984
|
+
}
|
|
8985
|
+
for (const field of result.unsupportedFields) {
|
|
8986
|
+
console.log(`- unsupported: ${field}`);
|
|
8987
|
+
}
|
|
8988
|
+
}
|
|
8989
|
+
function printNode(node, depth) {
|
|
8990
|
+
const ev = node.event;
|
|
8991
|
+
const status = ev.status !== void 0 ? ` ${ev.status}` : "";
|
|
8992
|
+
const duration = ev.durationMs !== void 0 ? ` ${formatDuration2(ev.durationMs)}` : "";
|
|
8993
|
+
console.log(`${getIndent(depth)}${ev.kind.toLowerCase()}: ${ev.name}${status}${duration}`);
|
|
8994
|
+
for (const child of node.children) {
|
|
8995
|
+
printNode(child, depth + 1);
|
|
8996
|
+
}
|
|
8997
|
+
}
|
|
8998
|
+
function printRun(result, run) {
|
|
8999
|
+
console.log(`Format: ${result.format}`);
|
|
9000
|
+
console.log(`Run: ${run.runId}`);
|
|
9001
|
+
if (run.name !== void 0) console.log(`Name: ${run.name}`);
|
|
9002
|
+
if (run.status !== void 0) console.log(`Status: ${run.status}`);
|
|
9003
|
+
if (run.startedAt !== void 0) {
|
|
9004
|
+
console.log(`Started: ${formatTimestamp(run.startedAt)}`);
|
|
9005
|
+
}
|
|
9006
|
+
if (run.durationMs !== void 0) {
|
|
9007
|
+
console.log(`Duration: ${formatDuration2(run.durationMs)}`);
|
|
9008
|
+
}
|
|
9009
|
+
console.log(`Events: ${run.metadata.totalEvents}`);
|
|
9010
|
+
for (const node of run.children) {
|
|
9011
|
+
printNode(node, 0);
|
|
9012
|
+
}
|
|
9013
|
+
}
|
|
9014
|
+
function selectRun(result, runId) {
|
|
9015
|
+
if (runId !== void 0) {
|
|
9016
|
+
return result.runs.find((run) => run.runId === runId);
|
|
9017
|
+
}
|
|
9018
|
+
return result.runs.length === 1 ? result.runs[0] : void 0;
|
|
9019
|
+
}
|
|
9020
|
+
function printMultipleRuns(result) {
|
|
9021
|
+
console.error(
|
|
9022
|
+
`Trace contains ${result.runs.length} runs. Re-run with --run <run-id>.`
|
|
9023
|
+
);
|
|
9024
|
+
for (const run of result.runs) {
|
|
9025
|
+
const bits = [
|
|
9026
|
+
run.runId,
|
|
9027
|
+
run.name !== void 0 ? `name=${run.name}` : void 0,
|
|
9028
|
+
run.status !== void 0 ? `status=${run.status}` : void 0,
|
|
9029
|
+
`events=${run.metadata.totalEvents}`
|
|
9030
|
+
].filter((value) => value !== void 0);
|
|
9031
|
+
console.error(`- ${bits.join(" ")}`);
|
|
9032
|
+
}
|
|
9033
|
+
}
|
|
9034
|
+
function writeJson(output2) {
|
|
9035
|
+
console.log(JSON.stringify(output2, null, 2));
|
|
9036
|
+
}
|
|
9037
|
+
async function openCommand(input3, options = {}, stdin = process.stdin) {
|
|
9038
|
+
try {
|
|
9039
|
+
const traceInput = await inputFromPathOrStdin(input3, stdin);
|
|
9040
|
+
const result = await openTrace(traceInput, {
|
|
9041
|
+
...options.format !== void 0 ? { format: options.format } : {}
|
|
9042
|
+
});
|
|
9043
|
+
const selected = selectRun(result, options.run);
|
|
9044
|
+
if (selected === void 0) {
|
|
9045
|
+
process.exitCode = 1;
|
|
9046
|
+
const message = options.run !== void 0 ? `Run not found: ${options.run}` : `Trace contains ${result.runs.length} runs. Specify --run <run-id>.`;
|
|
9047
|
+
if (options.json) {
|
|
9048
|
+
writeJson({
|
|
9049
|
+
format: result.format,
|
|
9050
|
+
sourceFiles: result.sourceFiles,
|
|
9051
|
+
warnings: result.warnings,
|
|
9052
|
+
unsupportedFields: result.unsupportedFields,
|
|
9053
|
+
runs: result.runs,
|
|
9054
|
+
error: { message }
|
|
9055
|
+
});
|
|
9056
|
+
} else if (options.run !== void 0) {
|
|
9057
|
+
console.error(message);
|
|
9058
|
+
} else {
|
|
9059
|
+
printMultipleRuns(result);
|
|
9060
|
+
}
|
|
9061
|
+
return;
|
|
9062
|
+
}
|
|
9063
|
+
if (options.json) {
|
|
9064
|
+
writeJson({
|
|
9065
|
+
format: result.format,
|
|
9066
|
+
sourceFiles: result.sourceFiles,
|
|
9067
|
+
warnings: result.warnings,
|
|
9068
|
+
unsupportedFields: result.unsupportedFields,
|
|
9069
|
+
selectedRunId: selected.runId,
|
|
9070
|
+
runs: [selected]
|
|
9071
|
+
});
|
|
9072
|
+
return;
|
|
9073
|
+
}
|
|
9074
|
+
printRun(result, selected);
|
|
9075
|
+
if (options.diagnostics) {
|
|
9076
|
+
printWarnings(result);
|
|
9077
|
+
}
|
|
9078
|
+
} catch (error) {
|
|
9079
|
+
process.exitCode = 1;
|
|
9080
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
9081
|
+
const code = error instanceof TraceReadError ? error.code : void 0;
|
|
9082
|
+
const warnings = error instanceof TraceReadError ? error.warnings : [];
|
|
9083
|
+
if (options.json) {
|
|
9084
|
+
writeJson({
|
|
9085
|
+
warnings,
|
|
9086
|
+
error: { ...code !== void 0 ? { code } : {}, message }
|
|
9087
|
+
});
|
|
9088
|
+
return;
|
|
9089
|
+
}
|
|
9090
|
+
console.error(message);
|
|
9091
|
+
if (options.diagnostics) {
|
|
9092
|
+
printWarnings({ warnings, unsupportedFields: [] });
|
|
9093
|
+
}
|
|
9094
|
+
}
|
|
9095
|
+
}
|
|
6813
9096
|
|
|
6814
9097
|
// packages/cli/src/index.ts
|
|
6815
9098
|
function runCommand(action) {
|
|
@@ -6820,7 +9103,7 @@ function runCommand(action) {
|
|
|
6820
9103
|
});
|
|
6821
9104
|
}
|
|
6822
9105
|
function createCliProgram() {
|
|
6823
|
-
const program = new commander.Command("agent-inspect").description("Local-first execution-tree debugger for AI agents").version(
|
|
9106
|
+
const program = new commander.Command("agent-inspect").description("Local-first execution-tree debugger for AI agents").version(version);
|
|
6824
9107
|
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(
|
|
6825
9108
|
new commander.Option("--status <status>", "filter by run status").choices([
|
|
6826
9109
|
"running",
|
|
@@ -6903,6 +9186,15 @@ function createCliProgram() {
|
|
|
6903
9186
|
).action((runId, opts) => {
|
|
6904
9187
|
runCommand(() => exportCommand(runId, opts));
|
|
6905
9188
|
});
|
|
9189
|
+
program.command("open").description("Open any supported local trace through the reader pipeline").argument("[input]", "trace file, directory, or - for stdin").addOption(
|
|
9190
|
+
new commander.Option("--format <format>", "trace input format").choices([
|
|
9191
|
+
"agent-inspect-jsonl",
|
|
9192
|
+
"openinference-json",
|
|
9193
|
+
"otlp-json"
|
|
9194
|
+
])
|
|
9195
|
+
).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) => {
|
|
9196
|
+
runCommand(() => openCommand(input3, opts));
|
|
9197
|
+
});
|
|
6906
9198
|
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(
|
|
6907
9199
|
"--duration-threshold <duration>",
|
|
6908
9200
|
"ignore duration deltas at or below this (e.g. 500ms, 2s, 1m)"
|
|
@@ -6958,7 +9250,7 @@ function createCliProgram() {
|
|
|
6958
9250
|
).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(
|
|
6959
9251
|
new commander.Option(
|
|
6960
9252
|
"--redaction-profile <profile>",
|
|
6961
|
-
"redaction profile for
|
|
9253
|
+
"redaction profile for entire report: local, share, strict (default: local)"
|
|
6962
9254
|
).choices(["local", "share", "strict"])
|
|
6963
9255
|
).action((runId, opts) => {
|
|
6964
9256
|
runCommand(() => reportCommand(runId, opts));
|