@agent-inspect/viewer 2.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/dist/index.cjs ADDED
@@ -0,0 +1,3862 @@
1
+ 'use strict';
2
+
3
+ var http = require('http');
4
+ var path6 = require('path');
5
+ var async_hooks = require('async_hooks');
6
+ require('crypto');
7
+ var promises = require('fs/promises');
8
+ var os = require('os');
9
+ require('nanoid');
10
+ require('chalk');
11
+ require('fs');
12
+ require('readline');
13
+
14
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
+
16
+ var path6__default = /*#__PURE__*/_interopDefault(path6);
17
+ var os__default = /*#__PURE__*/_interopDefault(os);
18
+
19
+ // packages/viewer/src/server.ts
20
+
21
+ // packages/core/src/correlation-metadata.ts
22
+ var TRACE_CORRELATION_KEYS = [
23
+ "correlationId",
24
+ "requestId",
25
+ "decisionId",
26
+ "groupId"
27
+ ];
28
+ function isNonEmptyString(value) {
29
+ return typeof value === "string" && value.length > 0;
30
+ }
31
+ function extractCorrelationMetadata(record) {
32
+ if (!record) {
33
+ return void 0;
34
+ }
35
+ const out = {};
36
+ let found = false;
37
+ for (const key of TRACE_CORRELATION_KEYS) {
38
+ const value = record[key];
39
+ if (isNonEmptyString(value)) {
40
+ out[key] = value;
41
+ found = true;
42
+ }
43
+ }
44
+ return found ? out : void 0;
45
+ }
46
+
47
+ // packages/core/src/types.ts
48
+ var STEP_TYPES = [
49
+ "run",
50
+ "llm",
51
+ "tool",
52
+ "decision",
53
+ "logic",
54
+ "state",
55
+ "custom"
56
+ ];
57
+ function isRecord(value) {
58
+ return typeof value === "object" && value !== null && !Array.isArray(value);
59
+ }
60
+ function isStepType(value) {
61
+ return typeof value === "string" && STEP_TYPES.includes(value);
62
+ }
63
+ function isTraceEvent(value) {
64
+ if (!isRecord(value)) return false;
65
+ if (value.schemaVersion !== "0.1") return false;
66
+ if (typeof value.timestamp !== "number") return false;
67
+ if (typeof value.event !== "string") return false;
68
+ switch (value.event) {
69
+ case "run_started": {
70
+ return typeof value.runId === "string" && typeof value.name === "string" && typeof value.startTime === "number";
71
+ }
72
+ case "run_completed": {
73
+ return typeof value.runId === "string" && (value.status === "success" || value.status === "error") && typeof value.endTime === "number" && typeof value.durationMs === "number";
74
+ }
75
+ case "step_started": {
76
+ return typeof value.runId === "string" && typeof value.stepId === "string" && typeof value.name === "string" && isStepType(value.type) && typeof value.startTime === "number";
77
+ }
78
+ case "step_completed": {
79
+ return typeof value.runId === "string" && typeof value.stepId === "string" && (value.status === "success" || value.status === "error") && typeof value.endTime === "number" && typeof value.durationMs === "number";
80
+ }
81
+ default:
82
+ return false;
83
+ }
84
+ }
85
+
86
+ // packages/core/src/types/persisted-inspect-event.ts
87
+ var INSPECT_KINDS = [
88
+ "RUN",
89
+ "AGENT",
90
+ "LLM",
91
+ "TOOL",
92
+ "CHAIN",
93
+ "RETRIEVER",
94
+ "DECISION",
95
+ "RESULT",
96
+ "ERROR",
97
+ "LOGIC",
98
+ "LOG"
99
+ ];
100
+ var ATTRIBUTION_CONFIDENCES = [
101
+ "explicit",
102
+ "correlated",
103
+ "heuristic",
104
+ "unknown"
105
+ ];
106
+ var PERSISTED_EVENT_SOURCE_TYPES = [
107
+ "manual",
108
+ "json-log",
109
+ "log4js",
110
+ "adapter",
111
+ "ai-sdk",
112
+ "otel"
113
+ ];
114
+ var PERSISTED_EVENT_STATUSES = [
115
+ "running",
116
+ "ok",
117
+ "error",
118
+ "unknown"
119
+ ];
120
+ function isRecord2(value) {
121
+ return typeof value === "object" && value !== null && !Array.isArray(value);
122
+ }
123
+ function isString(value) {
124
+ return typeof value === "string";
125
+ }
126
+ function isNonEmptyString2(value) {
127
+ return typeof value === "string" && value.length > 0;
128
+ }
129
+ function isOptionalString(value) {
130
+ return value === void 0 || isString(value);
131
+ }
132
+ function isNonNegativeNumber(value) {
133
+ return typeof value === "number" && Number.isFinite(value) && value >= 0;
134
+ }
135
+ function isOptionalNonNegativeNumber(value) {
136
+ return value === void 0 || isNonNegativeNumber(value);
137
+ }
138
+ function isInspectKind(value) {
139
+ return typeof value === "string" && INSPECT_KINDS.includes(value);
140
+ }
141
+ function isAttributionConfidence(value) {
142
+ return typeof value === "string" && ATTRIBUTION_CONFIDENCES.includes(value);
143
+ }
144
+ function isPersistedEventSourceType(value) {
145
+ return typeof value === "string" && PERSISTED_EVENT_SOURCE_TYPES.includes(value);
146
+ }
147
+ function isPersistedEventStatus(value) {
148
+ return typeof value === "string" && PERSISTED_EVENT_STATUSES.includes(value);
149
+ }
150
+ function isPersistedEventSource(value) {
151
+ if (!isRecord2(value)) return false;
152
+ if (!isPersistedEventSourceType(value.type)) return false;
153
+ if (!isOptionalString(value.name)) return false;
154
+ if (!isOptionalString(value.version)) return false;
155
+ return true;
156
+ }
157
+ function isPersistedInspectError(value) {
158
+ if (!isRecord2(value)) return false;
159
+ if (!isNonEmptyString2(value.message)) return false;
160
+ if (!isOptionalString(value.name)) return false;
161
+ if (!isOptionalString(value.code)) return false;
162
+ return true;
163
+ }
164
+ function isPersistedTokenUsage(value) {
165
+ if (!isRecord2(value)) return false;
166
+ if (!isOptionalNonNegativeNumber(value.input)) return false;
167
+ if (!isOptionalNonNegativeNumber(value.output)) return false;
168
+ if (!isOptionalNonNegativeNumber(value.total)) return false;
169
+ if (!isOptionalNonNegativeNumber(value.cached)) return false;
170
+ return true;
171
+ }
172
+ function isPersistedTraceContext(value) {
173
+ if (!isRecord2(value)) return false;
174
+ if (!isOptionalString(value.traceId)) return false;
175
+ if (!isOptionalString(value.spanId)) return false;
176
+ if (!isOptionalString(value.parentSpanId)) return false;
177
+ return true;
178
+ }
179
+ function isPersistedInspectEvent(value) {
180
+ if (!isRecord2(value)) return false;
181
+ if (value.schemaVersion !== "0.2" && value.schemaVersion !== "1.0") {
182
+ return false;
183
+ }
184
+ if (!isNonEmptyString2(value.eventId)) return false;
185
+ if (!isNonEmptyString2(value.runId)) return false;
186
+ if (!isInspectKind(value.kind)) return false;
187
+ if (!isNonEmptyString2(value.name)) return false;
188
+ if (!isNonEmptyString2(value.timestamp)) return false;
189
+ if (!isAttributionConfidence(value.confidence)) return false;
190
+ if (!isPersistedEventSource(value.source)) return false;
191
+ if (value.parentId !== void 0 && !isNonEmptyString2(value.parentId)) {
192
+ return false;
193
+ }
194
+ if (value.status !== void 0 && !isPersistedEventStatus(value.status)) {
195
+ return false;
196
+ }
197
+ if (!isOptionalString(value.startedAt)) return false;
198
+ if (!isOptionalString(value.endedAt)) return false;
199
+ if (value.durationMs !== void 0 && !isNonNegativeNumber(value.durationMs)) {
200
+ return false;
201
+ }
202
+ if (value.attributes !== void 0 && !isRecord2(value.attributes)) {
203
+ return false;
204
+ }
205
+ if (value.error !== void 0 && !isPersistedInspectError(value.error)) {
206
+ return false;
207
+ }
208
+ if (value.tokenUsage !== void 0 && !isPersistedTokenUsage(value.tokenUsage)) {
209
+ return false;
210
+ }
211
+ if (value.trace !== void 0 && !isPersistedTraceContext(value.trace)) {
212
+ return false;
213
+ }
214
+ return true;
215
+ }
216
+
217
+ // packages/core/src/persisted/to-trace-event.ts
218
+ function parseIsoToMs(iso) {
219
+ const parsed = Date.parse(iso);
220
+ return Number.isFinite(parsed) ? parsed : 0;
221
+ }
222
+ function mapInspectKindToStepType(kind) {
223
+ switch (kind) {
224
+ case "LLM":
225
+ return "llm";
226
+ case "TOOL":
227
+ return "tool";
228
+ case "DECISION":
229
+ return "decision";
230
+ case "RUN":
231
+ return "run";
232
+ default:
233
+ return "logic";
234
+ }
235
+ }
236
+ function mapPersistedStatusToStepStatus(status) {
237
+ switch (status) {
238
+ case "ok":
239
+ return "success";
240
+ case "error":
241
+ return "error";
242
+ case "running":
243
+ return "running";
244
+ default:
245
+ return void 0;
246
+ }
247
+ }
248
+ function mapPersistedStatusToRunStatus(status) {
249
+ switch (status) {
250
+ case "ok":
251
+ return "success";
252
+ case "error":
253
+ return "error";
254
+ case "running":
255
+ return "running";
256
+ default:
257
+ return void 0;
258
+ }
259
+ }
260
+ function mapPersistedError(error, attributes) {
261
+ if (!error?.message) return void 0;
262
+ const out = { message: error.message };
263
+ const stack = typeof attributes?.errorStack === "string" && attributes.errorStack.length > 0 ? attributes.errorStack : void 0;
264
+ if (stack) {
265
+ out.stack = stack;
266
+ }
267
+ return out;
268
+ }
269
+ function mapTokenUsageToMetadata(tokenUsage, attributes) {
270
+ const metadata = {};
271
+ if (attributes?.metadata && typeof attributes.metadata === "object") {
272
+ Object.assign(metadata, attributes.metadata);
273
+ }
274
+ if (tokenUsage) {
275
+ metadata.tokens = {
276
+ ...tokenUsage.input !== void 0 ? { input: tokenUsage.input } : {},
277
+ ...tokenUsage.output !== void 0 ? { output: tokenUsage.output } : {},
278
+ ...tokenUsage.total !== void 0 ? { total: tokenUsage.total } : {},
279
+ ...tokenUsage.cached !== void 0 ? { cached: tokenUsage.cached } : {}
280
+ };
281
+ }
282
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
283
+ }
284
+ function pickRunMetadata(attributes) {
285
+ if (!attributes) return void 0;
286
+ const metadata = attributes.metadata && typeof attributes.metadata === "object" ? { ...attributes.metadata } : {};
287
+ for (const key of [
288
+ "correlationId",
289
+ "requestId",
290
+ "decisionId",
291
+ "groupId"
292
+ ]) {
293
+ const value = attributes[key];
294
+ if (typeof value === "string" && value.trim() !== "") {
295
+ metadata[key] = value;
296
+ }
297
+ }
298
+ return Object.keys(metadata).length > 0 ? metadata : void 0;
299
+ }
300
+ function resolveStepId(event) {
301
+ const attrs = event.attributes;
302
+ if (attrs && typeof attrs.stepId === "string" && attrs.stepId.trim() !== "") {
303
+ return attrs.stepId;
304
+ }
305
+ return event.eventId;
306
+ }
307
+ function resolveStepType(event) {
308
+ const attrs = event.attributes;
309
+ if (attrs && typeof attrs.stepType === "string") {
310
+ const t = attrs.stepType;
311
+ if (t === "run" || t === "llm" || t === "tool" || t === "decision" || t === "logic" || t === "state" || t === "custom") {
312
+ return t;
313
+ }
314
+ }
315
+ return mapInspectKindToStepType(event.kind);
316
+ }
317
+ function resolveTimes(event) {
318
+ const timestamp = parseIsoToMs(event.timestamp);
319
+ const startTime = event.startedAt !== void 0 ? parseIsoToMs(event.startedAt) : timestamp;
320
+ let endTime = event.endedAt !== void 0 ? parseIsoToMs(event.endedAt) : timestamp;
321
+ if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0 && event.endedAt === void 0) {
322
+ endTime = startTime + event.durationMs;
323
+ }
324
+ return { timestamp, startTime, endTime };
325
+ }
326
+ function fromLegacyRunStarted(event) {
327
+ const { timestamp, startTime } = resolveTimes(event);
328
+ const out = {
329
+ schemaVersion: "0.1",
330
+ event: "run_started",
331
+ timestamp,
332
+ runId: event.runId,
333
+ name: event.name,
334
+ startTime
335
+ };
336
+ const metadata = pickRunMetadata(event.attributes);
337
+ if (metadata) out.metadata = metadata;
338
+ return out;
339
+ }
340
+ function fromLegacyRunCompleted(event) {
341
+ const { timestamp, endTime } = resolveTimes(event);
342
+ const status = mapPersistedStatusToRunStatus(event.status) ?? "success";
343
+ const out = {
344
+ schemaVersion: "0.1",
345
+ event: "run_completed",
346
+ timestamp,
347
+ runId: event.runId,
348
+ status: status === "running" ? "success" : status,
349
+ endTime,
350
+ durationMs: event.durationMs ?? Math.max(0, endTime - timestamp)
351
+ };
352
+ const error = mapPersistedError(event.error, event.attributes);
353
+ if (error) out.error = error;
354
+ return out;
355
+ }
356
+ function fromLegacyStepStarted(event) {
357
+ const { timestamp, startTime } = resolveTimes(event);
358
+ const out = {
359
+ schemaVersion: "0.1",
360
+ event: "step_started",
361
+ timestamp,
362
+ runId: event.runId,
363
+ stepId: resolveStepId(event),
364
+ name: event.name,
365
+ type: resolveStepType(event),
366
+ startTime
367
+ };
368
+ if (event.parentId !== void 0) out.parentId = event.parentId;
369
+ const metadata = mapTokenUsageToMetadata(event.tokenUsage, event.attributes);
370
+ if (metadata) out.metadata = metadata;
371
+ return out;
372
+ }
373
+ function fromLegacyStepCompleted(event) {
374
+ const { timestamp, endTime } = resolveTimes(event);
375
+ const status = mapPersistedStatusToStepStatus(event.status) ?? "success";
376
+ const out = {
377
+ schemaVersion: "0.1",
378
+ event: "step_completed",
379
+ timestamp,
380
+ runId: event.runId,
381
+ stepId: resolveStepId(event),
382
+ status: status === "running" ? "success" : status,
383
+ endTime,
384
+ durationMs: event.durationMs ?? Math.max(0, endTime - timestamp)
385
+ };
386
+ const error = mapPersistedError(event.error, event.attributes);
387
+ if (error) out.error = error;
388
+ return out;
389
+ }
390
+ function fromNativeRun(event) {
391
+ const { timestamp, startTime, endTime } = resolveTimes(event);
392
+ const runStatus = mapPersistedStatusToRunStatus(event.status);
393
+ const out = [];
394
+ if (runStatus === "running" || event.startedAt !== void 0) {
395
+ const started = {
396
+ schemaVersion: "0.1",
397
+ event: "run_started",
398
+ timestamp,
399
+ runId: event.runId,
400
+ name: event.name,
401
+ startTime
402
+ };
403
+ const metadata = pickRunMetadata(event.attributes);
404
+ if (metadata) started.metadata = metadata;
405
+ out.push(started);
406
+ }
407
+ if (runStatus === "success" || runStatus === "error" || event.endedAt !== void 0) {
408
+ const completed = {
409
+ schemaVersion: "0.1",
410
+ event: "run_completed",
411
+ timestamp,
412
+ runId: event.runId,
413
+ status: runStatus === "error" ? "error" : "success",
414
+ endTime,
415
+ durationMs: event.durationMs ?? Math.max(0, endTime - startTime)
416
+ };
417
+ const error = mapPersistedError(event.error, event.attributes);
418
+ if (error) completed.error = error;
419
+ out.push(completed);
420
+ }
421
+ if (out.length === 0) {
422
+ out.push(fromLegacyRunStarted(event));
423
+ }
424
+ return out;
425
+ }
426
+ function fromNativeStep(event) {
427
+ const { timestamp, startTime, endTime } = resolveTimes(event);
428
+ const stepStatus = mapPersistedStatusToStepStatus(event.status);
429
+ const stepId = resolveStepId(event);
430
+ const out = [];
431
+ const shouldEmitStarted = stepStatus === "running" || event.startedAt !== void 0 || stepStatus === "success" || stepStatus === "error";
432
+ if (shouldEmitStarted) {
433
+ const started = {
434
+ schemaVersion: "0.1",
435
+ event: "step_started",
436
+ timestamp,
437
+ runId: event.runId,
438
+ stepId,
439
+ name: event.name,
440
+ type: resolveStepType(event),
441
+ startTime
442
+ };
443
+ if (event.parentId !== void 0) started.parentId = event.parentId;
444
+ const metadata = mapTokenUsageToMetadata(event.tokenUsage, event.attributes);
445
+ if (metadata) started.metadata = metadata;
446
+ out.push(started);
447
+ }
448
+ if (stepStatus === "success" || stepStatus === "error" || event.endedAt !== void 0 || event.durationMs !== void 0) {
449
+ const completed = {
450
+ schemaVersion: "0.1",
451
+ event: "step_completed",
452
+ timestamp,
453
+ runId: event.runId,
454
+ stepId,
455
+ status: stepStatus === "error" ? "error" : "success",
456
+ endTime,
457
+ durationMs: event.durationMs ?? Math.max(0, endTime - startTime)
458
+ };
459
+ const error = mapPersistedError(event.error, event.attributes);
460
+ if (error) completed.error = error;
461
+ out.push(completed);
462
+ }
463
+ if (out.length === 0) {
464
+ out.push(fromLegacyStepStarted(event));
465
+ }
466
+ return out;
467
+ }
468
+ function persistedInspectEventToTraceEvents(event) {
469
+ if (!isPersistedInspectEvent(event)) {
470
+ throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
471
+ }
472
+ const legacyEvent = event.attributes?.legacyEvent;
473
+ if (legacyEvent === "run_started") return [fromLegacyRunStarted(event)];
474
+ if (legacyEvent === "run_completed") return [fromLegacyRunCompleted(event)];
475
+ if (legacyEvent === "step_started") return [fromLegacyStepStarted(event)];
476
+ if (legacyEvent === "step_completed") return [fromLegacyStepCompleted(event)];
477
+ if (event.kind === "RUN") {
478
+ return fromNativeRun(event);
479
+ }
480
+ return fromNativeStep(event);
481
+ }
482
+ function persistedInspectEventsToTraceEvents(events, options) {
483
+ const out = [];
484
+ events.forEach((event, index) => {
485
+ const rows = persistedInspectEventToTraceEvents(event);
486
+ if (rows.length === 0 && options?.eventIndex !== void 0) ;
487
+ out.push(...rows);
488
+ });
489
+ return out;
490
+ }
491
+ var DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
492
+ var RUNS_DIR_NAME = "runs";
493
+ var FALLBACK_TRACE_DIR = path6__default.default.join(
494
+ os__default.default.tmpdir(),
495
+ "agent-inspect",
496
+ RUNS_DIR_NAME
497
+ );
498
+ function getDefaultTraceDir() {
499
+ const envDir = process.env.AGENT_INSPECT_TRACE_DIR;
500
+ if (typeof envDir === "string" && envDir.trim() !== "") {
501
+ return envDir.trim();
502
+ }
503
+ try {
504
+ const home = os__default.default.homedir();
505
+ if (typeof home !== "string" || home.trim() === "") {
506
+ return FALLBACK_TRACE_DIR;
507
+ }
508
+ return path6__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
509
+ } catch {
510
+ return FALLBACK_TRACE_DIR;
511
+ }
512
+ }
513
+ function formatError(error) {
514
+ if (error instanceof Error) {
515
+ const out = { message: error.message };
516
+ if (typeof error.stack === "string" && error.stack.length > 0) {
517
+ out.stack = error.stack;
518
+ }
519
+ return out;
520
+ }
521
+ if (typeof error === "string") {
522
+ return { message: error };
523
+ }
524
+ if (error === null) {
525
+ return { message: "Unknown error: null" };
526
+ }
527
+ if (error === void 0) {
528
+ return { message: "Unknown error: undefined" };
529
+ }
530
+ if (typeof error === "number" || typeof error === "boolean" || typeof error === "bigint") {
531
+ return { message: String(error) };
532
+ }
533
+ if (typeof error === "object") {
534
+ try {
535
+ return { message: JSON.stringify(error) };
536
+ } catch {
537
+ return { message: "Unknown error" };
538
+ }
539
+ }
540
+ return { message: "Unknown error" };
541
+ }
542
+ function warn(message, error) {
543
+ const base = `[AgentInspect] ${message}`;
544
+ if (error === void 0) {
545
+ console.warn(base);
546
+ return;
547
+ }
548
+ console.warn(`${base}: ${formatError(error).message}`);
549
+ }
550
+
551
+ // packages/core/src/read-trace.ts
552
+ function isRecord3(value) {
553
+ return typeof value === "object" && value !== null && !Array.isArray(value);
554
+ }
555
+ function detectLineFormat(parsed) {
556
+ if (!isRecord3(parsed)) return "unknown";
557
+ if (parsed.schemaVersion === "0.1") return "0.1";
558
+ if (parsed.schemaVersion === "0.2") return "0.2";
559
+ if (parsed.schemaVersion === "1.0") return "1.0";
560
+ return "unknown";
561
+ }
562
+ function parseTraceJsonl(raw, options = {}) {
563
+ const validate = options.validate ?? isTraceEvent;
564
+ const emitWarning = (message) => {
565
+ if (options.warnings !== false) warn(message);
566
+ };
567
+ const persisted = [];
568
+ const traceEvents = [];
569
+ const rows = [];
570
+ let sourceEventCount = 0;
571
+ let saw01 = false;
572
+ let saw02 = false;
573
+ let saw10 = false;
574
+ let lineNumber = 0;
575
+ for (const line of raw.split(/\r?\n/)) {
576
+ lineNumber += 1;
577
+ const trimmed = line.trim();
578
+ if (trimmed === "") continue;
579
+ let parsed;
580
+ try {
581
+ parsed = JSON.parse(trimmed);
582
+ } catch {
583
+ emitWarning("Skipped invalid JSON line in trace file");
584
+ continue;
585
+ }
586
+ const format2 = detectLineFormat(parsed);
587
+ if (format2 === "0.1") {
588
+ saw01 = true;
589
+ if (validate(parsed)) {
590
+ sourceEventCount += 1;
591
+ traceEvents.push(parsed);
592
+ rows.push({ format: "0.1", event: parsed, sourceLine: lineNumber });
593
+ } else {
594
+ emitWarning("Skipped invalid trace event line in trace file");
595
+ }
596
+ continue;
597
+ }
598
+ if (format2 === "0.2" || format2 === "1.0") {
599
+ if (format2 === "0.2") saw02 = true;
600
+ else saw10 = true;
601
+ if (isPersistedInspectEvent(parsed)) {
602
+ sourceEventCount += 1;
603
+ persisted.push(parsed);
604
+ rows.push({ format: format2, event: parsed, sourceLine: lineNumber });
605
+ traceEvents.push(...persistedInspectEventToTraceEvents(parsed));
606
+ } else {
607
+ emitWarning("Skipped invalid persisted inspect event line in trace file");
608
+ }
609
+ continue;
610
+ }
611
+ emitWarning("Skipped trace line with unknown schemaVersion");
612
+ }
613
+ const seenFormats = [saw01, saw02, saw10].filter(Boolean).length;
614
+ if (seenFormats > 1) {
615
+ emitWarning(
616
+ "Trace file mixes AgentInspect schemaVersion rows; normalizing all rows"
617
+ );
618
+ }
619
+ let format = "empty";
620
+ if (seenFormats > 1) format = "mixed";
621
+ else if (saw01) format = "0.1";
622
+ else if (saw02) format = "0.2";
623
+ else if (saw10) format = "1.0";
624
+ return { format, sourceEventCount, events: traceEvents, persisted, rows };
625
+ }
626
+
627
+ // packages/core/src/storage.ts
628
+ function isRecord4(value) {
629
+ return typeof value === "object" && value !== null && !Array.isArray(value);
630
+ }
631
+ function nonEmptyString(value) {
632
+ return typeof value === "string" && value.trim() !== "";
633
+ }
634
+ function finiteNumber(value) {
635
+ return typeof value === "number" && Number.isFinite(value);
636
+ }
637
+ function optionalErrorInfo(value) {
638
+ if (value === void 0) return true;
639
+ if (!isRecord4(value)) return false;
640
+ if (typeof value.message !== "string") return false;
641
+ if ("stack" in value && value.stack !== void 0) {
642
+ if (typeof value.stack !== "string") return false;
643
+ }
644
+ return true;
645
+ }
646
+ function validateEvent(event) {
647
+ if (!isRecord4(event)) return false;
648
+ if (event.schemaVersion !== "0.1") return false;
649
+ if (!finiteNumber(event.timestamp)) return false;
650
+ if (typeof event.event !== "string") return false;
651
+ switch (event.event) {
652
+ case "run_started": {
653
+ if (!nonEmptyString(event.runId) || !nonEmptyString(event.name) || !finiteNumber(event.startTime)) {
654
+ return false;
655
+ }
656
+ if (event.metadata !== void 0 && !isRecord4(event.metadata)) {
657
+ return false;
658
+ }
659
+ return true;
660
+ }
661
+ case "run_completed": {
662
+ return nonEmptyString(event.runId) && (event.status === "success" || event.status === "error") && finiteNumber(event.endTime) && finiteNumber(event.durationMs) && optionalErrorInfo(event.error);
663
+ }
664
+ case "step_started": {
665
+ if (!nonEmptyString(event.runId) || !nonEmptyString(event.stepId) || !nonEmptyString(event.name) || !isStepType(event.type) || !finiteNumber(event.startTime)) {
666
+ return false;
667
+ }
668
+ if (event.parentId !== void 0 && typeof event.parentId !== "string") {
669
+ return false;
670
+ }
671
+ if (event.metadata !== void 0 && !isRecord4(event.metadata)) {
672
+ return false;
673
+ }
674
+ return true;
675
+ }
676
+ case "step_completed": {
677
+ return nonEmptyString(event.runId) && nonEmptyString(event.stepId) && (event.status === "success" || event.status === "error") && finiteNumber(event.endTime) && finiteNumber(event.durationMs) && optionalErrorInfo(event.error);
678
+ }
679
+ default:
680
+ return false;
681
+ }
682
+ }
683
+ async function readTraceEventsFromFile(filePath) {
684
+ try {
685
+ const raw = await promises.readFile(filePath, "utf-8");
686
+ return parseTraceJsonl(raw, { validate: validateEvent }).events;
687
+ } catch (e) {
688
+ if (e && typeof e === "object" && "code" in e && e.code === "ENOENT") {
689
+ return [];
690
+ }
691
+ warn("Failed to read trace events from file", e);
692
+ return [];
693
+ }
694
+ }
695
+
696
+ // packages/core/src/context.ts
697
+ new async_hooks.AsyncLocalStorage();
698
+ function resolveTraceDir(options = {}) {
699
+ if (typeof options.dir === "string" && options.dir.trim() !== "") {
700
+ return options.dir.trim();
701
+ }
702
+ const envDir = process.env.AGENT_INSPECT_TRACE_DIR;
703
+ if (typeof envDir === "string" && envDir.trim() !== "") {
704
+ return envDir.trim();
705
+ }
706
+ return getDefaultTraceDir();
707
+ }
708
+ var TraceDirectory = class {
709
+ #dir;
710
+ constructor(options = {}) {
711
+ this.#dir = resolveTraceDir(options);
712
+ }
713
+ getPath(filename) {
714
+ return filename ? path6__default.default.join(this.#dir, filename) : this.#dir;
715
+ }
716
+ async list() {
717
+ try {
718
+ const files = await promises.readdir(this.#dir);
719
+ return files.filter((f) => f.endsWith(".jsonl"));
720
+ } catch (e) {
721
+ if (e && typeof e === "object" && "code" in e && e.code === "ENOENT") {
722
+ return [];
723
+ }
724
+ throw e;
725
+ }
726
+ }
727
+ async getFileStats(filename) {
728
+ return await promises.stat(this.getPath(filename));
729
+ }
730
+ };
731
+ function isFiniteNumber(v) {
732
+ return typeof v === "number" && Number.isFinite(v);
733
+ }
734
+ function parseIsoToMs2(value) {
735
+ if (value === void 0) return void 0;
736
+ const parsed = Date.parse(value);
737
+ return Number.isFinite(parsed) ? parsed : void 0;
738
+ }
739
+ async function extractMetadata(filePath, _quickScan) {
740
+ const stats = await promises.stat(filePath);
741
+ let runIdFromFile = path6__default.default.basename(filePath);
742
+ if (runIdFromFile.endsWith(".jsonl")) {
743
+ runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
744
+ }
745
+ const raw = await promises.readFile(filePath, "utf-8");
746
+ const parsedTrace = parseTraceJsonl(raw, { warnings: false });
747
+ let runId;
748
+ let name;
749
+ let startedAt;
750
+ let endedAt;
751
+ let explicitDurationMs;
752
+ let hasRunStarted = false;
753
+ let hasRunCompleted = false;
754
+ let runCompletedStatus;
755
+ let anyStepError = false;
756
+ const anyKnownEvent = parsedTrace.sourceEventCount > 0;
757
+ let persistedStatus;
758
+ const persistedRun = parsedTrace.persisted.find(
759
+ (event) => event.kind === "RUN"
760
+ );
761
+ if (persistedRun) {
762
+ runId = persistedRun.runId;
763
+ if (persistedRun.name.trim() !== "") {
764
+ name = persistedRun.name;
765
+ }
766
+ startedAt = parseIsoToMs2(persistedRun.startedAt) ?? parseIsoToMs2(persistedRun.timestamp);
767
+ endedAt = parseIsoToMs2(persistedRun.endedAt);
768
+ if (isFiniteNumber(persistedRun.durationMs)) {
769
+ explicitDurationMs = persistedRun.durationMs;
770
+ if (endedAt === void 0 && startedAt !== void 0) {
771
+ endedAt = startedAt + persistedRun.durationMs;
772
+ }
773
+ }
774
+ if (persistedRun.status === "ok") persistedStatus = "success";
775
+ else if (persistedRun.status === "error") persistedStatus = "error";
776
+ else if (persistedRun.status === "running") persistedStatus = "running";
777
+ else if (persistedRun.status === "unknown") persistedStatus = "unknown";
778
+ } else {
779
+ runId = parsedTrace.persisted[0]?.runId;
780
+ }
781
+ for (const e of parsedTrace.events) {
782
+ if (runId === void 0 && typeof e.runId === "string") {
783
+ runId = e.runId;
784
+ }
785
+ if (e.event === "run_started") {
786
+ hasRunStarted = true;
787
+ const rs = e;
788
+ if (typeof rs.name === "string" && rs.name.trim() !== "") {
789
+ name = rs.name;
790
+ }
791
+ if (isFiniteNumber(rs.startTime)) {
792
+ startedAt = rs.startTime;
793
+ } else if (isFiniteNumber(rs.timestamp)) {
794
+ startedAt = rs.timestamp;
795
+ }
796
+ }
797
+ if (e.event === "run_completed") {
798
+ hasRunCompleted = true;
799
+ const rc = e;
800
+ runCompletedStatus = rc.status;
801
+ if (isFiniteNumber(rc.endTime)) endedAt = rc.endTime;
802
+ else if (isFiniteNumber(rc.timestamp)) endedAt = rc.timestamp;
803
+ if (isFiniteNumber(rc.durationMs)) explicitDurationMs = rc.durationMs;
804
+ }
805
+ if (e.event === "step_completed") {
806
+ const sc = e;
807
+ if (sc.status === "error") {
808
+ anyStepError = true;
809
+ }
810
+ }
811
+ }
812
+ const resolvedRunId = runId ?? runIdFromFile;
813
+ let status = "unknown";
814
+ if (hasRunCompleted && (runCompletedStatus === "success" || runCompletedStatus === "error")) {
815
+ status = runCompletedStatus;
816
+ } else if (anyStepError) {
817
+ status = "error";
818
+ } else if (persistedStatus !== void 0) {
819
+ status = persistedStatus;
820
+ } else if (hasRunStarted && !hasRunCompleted) {
821
+ status = "running";
822
+ } else if (anyKnownEvent) {
823
+ status = "unknown";
824
+ } else {
825
+ status = "unknown";
826
+ }
827
+ const durationMs = explicitDurationMs ?? (startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt ? endedAt - startedAt : void 0);
828
+ return {
829
+ runId: resolvedRunId,
830
+ name,
831
+ status,
832
+ startedAt,
833
+ endedAt,
834
+ durationMs,
835
+ eventCount: parsedTrace.sourceEventCount,
836
+ filePath,
837
+ fileSize: stats.size,
838
+ createdAt: stats.birthtime
839
+ };
840
+ }
841
+
842
+ // packages/core/src/timeline.ts
843
+ function finite(n) {
844
+ return typeof n === "number" && Number.isFinite(n);
845
+ }
846
+ function pickStreamingMeta(metadata) {
847
+ if (!metadata || typeof metadata !== "object") return void 0;
848
+ const chunkCount = metadata.chunkCount;
849
+ const streamDurationMs = metadata.streamDurationMs;
850
+ const streamedCharCount = metadata.streamedCharCount;
851
+ if (!finite(chunkCount) && !finite(streamDurationMs) && !finite(streamedCharCount)) {
852
+ return void 0;
853
+ }
854
+ return {
855
+ ...finite(chunkCount) ? { chunkCount } : {},
856
+ ...finite(streamDurationMs) ? { streamDurationMs } : {},
857
+ ...finite(streamedCharCount) ? { streamedCharCount } : {}
858
+ };
859
+ }
860
+ function pickCorrelation(metadata) {
861
+ if (!metadata || typeof metadata !== "object") return void 0;
862
+ const out = {};
863
+ for (const key of [
864
+ "correlationId",
865
+ "requestId",
866
+ "decisionId",
867
+ "groupId"
868
+ ]) {
869
+ const v = metadata[key];
870
+ if (typeof v === "string" && v.trim() !== "") {
871
+ out[key] = v;
872
+ }
873
+ }
874
+ return Object.keys(out).length > 0 ? out : void 0;
875
+ }
876
+ function buildRunTimeline(events, options = {}) {
877
+ const started = events.find(
878
+ (e) => e.event === "run_started"
879
+ );
880
+ const completed = events.filter(
881
+ (e) => e.event === "run_completed"
882
+ );
883
+ const lastCompleted = completed[completed.length - 1];
884
+ const runId = started?.runId ?? events.find((e) => typeof e.runId === "string")?.runId ?? "unknown-run";
885
+ const runStart = started && finite(started.startTime) ? started.startTime : started && finite(started.timestamp) ? started.timestamp : void 0;
886
+ const status = lastCompleted ? lastCompleted.status : started ? "running" : "unknown";
887
+ const steps = /* @__PURE__ */ new Map();
888
+ for (const e of events) {
889
+ if (e.event === "step_started") {
890
+ const s = e;
891
+ steps.set(s.stepId, {
892
+ name: s.name,
893
+ type: s.type,
894
+ parentId: s.parentId,
895
+ startedAt: finite(s.startTime) ? s.startTime : s.timestamp,
896
+ status: "running",
897
+ metadata: s.metadata
898
+ });
899
+ }
900
+ }
901
+ for (const e of events) {
902
+ if (e.event !== "step_completed") continue;
903
+ const c = e;
904
+ const node = steps.get(c.stepId);
905
+ if (!node) continue;
906
+ node.status = c.status;
907
+ if (finite(c.durationMs)) node.durationMs = c.durationMs;
908
+ }
909
+ const depthCache = /* @__PURE__ */ new Map();
910
+ const computeDepth = (stepId) => {
911
+ const cached = depthCache.get(stepId);
912
+ if (cached !== void 0) return cached;
913
+ const node = steps.get(stepId);
914
+ if (!node) return 0;
915
+ const parent = node.parentId;
916
+ if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
917
+ depthCache.set(stepId, 0);
918
+ return 0;
919
+ }
920
+ const d = Math.min(1e3, computeDepth(parent) + 1);
921
+ depthCache.set(stepId, d);
922
+ return d;
923
+ };
924
+ const entries = [];
925
+ for (const [stepId, s] of steps.entries()) {
926
+ const offsetMs = runStart !== void 0 && finite(s.startedAt) ? Math.max(0, s.startedAt - runStart) : 0;
927
+ entries.push({
928
+ stepId,
929
+ name: s.name,
930
+ type: s.type,
931
+ status: s.status,
932
+ depth: computeDepth(stepId),
933
+ startedAt: s.startedAt,
934
+ offsetMs,
935
+ durationMs: s.durationMs,
936
+ isError: s.status === "error",
937
+ streaming: pickStreamingMeta(s.metadata)
938
+ });
939
+ }
940
+ entries.sort((a, b) => a.startedAt - b.startedAt);
941
+ const slowTopN = options.slowTopN ?? 3;
942
+ if (options.focus === "slow" && entries.length > 0) {
943
+ const ranked = [...entries].filter((e) => finite(e.durationMs)).sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0));
944
+ const slowIds = new Set(
945
+ ranked.slice(0, slowTopN).map((e) => e.stepId)
946
+ );
947
+ for (const e of entries) {
948
+ if (slowIds.has(e.stepId)) e.slow = true;
949
+ }
950
+ }
951
+ return {
952
+ runId,
953
+ name: typeof started?.name === "string" && started.name.trim() !== "" ? started.name : void 0,
954
+ status,
955
+ startedAt: runStart,
956
+ endedAt: lastCompleted && finite(lastCompleted.endTime) ? lastCompleted.endTime : void 0,
957
+ durationMs: lastCompleted && finite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0,
958
+ correlation: pickCorrelation(
959
+ started?.metadata
960
+ ),
961
+ entries
962
+ };
963
+ }
964
+
965
+ // packages/core/src/search.ts
966
+ async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
967
+ const metas = [];
968
+ for (const fileName of fileNames) {
969
+ try {
970
+ const filePath = getPath(fileName);
971
+ const meta = await extractMetadata(filePath);
972
+ metas.push(meta);
973
+ } catch {
974
+ }
975
+ }
976
+ return metas;
977
+ }
978
+
979
+ // packages/core/src/sessions/metadata.ts
980
+ function isNonEmptyString3(value) {
981
+ return typeof value === "string" && value.trim() !== "";
982
+ }
983
+ function finitePositiveInt(value) {
984
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 1) {
985
+ return void 0;
986
+ }
987
+ return Math.trunc(value);
988
+ }
989
+ function extractSessionWorkflowMetadata(record) {
990
+ if (!record) return void 0;
991
+ const out = {};
992
+ let found = false;
993
+ const assignString = (key, value) => {
994
+ if (isNonEmptyString3(value)) {
995
+ out[key] = value.trim();
996
+ found = true;
997
+ }
998
+ };
999
+ assignString("sessionId", record.sessionId);
1000
+ assignString("conversationId", record.conversationId);
1001
+ assignString("groupId", record.groupId);
1002
+ assignString("parentGroupId", record.parentGroupId);
1003
+ assignString("retryOf", record.retryOf);
1004
+ assignString("retryReason", record.retryReason);
1005
+ assignString("handoffFrom", record.handoffFrom);
1006
+ assignString("handoffTo", record.handoffTo);
1007
+ assignString("subAgentId", record.subAgentId);
1008
+ assignString("subAgentName", record.subAgentName);
1009
+ assignString("jobId", record.jobId);
1010
+ assignString("queueName", record.queueName);
1011
+ assignString("workflowName", record.workflowName);
1012
+ assignString("workflowStep", record.workflowStep);
1013
+ assignString("toolCallId", record.toolCallId);
1014
+ assignString("mcpToolCallId", record.mcpToolCallId);
1015
+ assignString("linkedStepId", record.linkedStepId);
1016
+ assignString("correlationId", record.correlationId);
1017
+ assignString("requestId", record.requestId);
1018
+ assignString("decisionId", record.decisionId);
1019
+ const attempt = finitePositiveInt(record.attempt);
1020
+ if (attempt !== void 0) {
1021
+ out.attempt = attempt;
1022
+ found = true;
1023
+ }
1024
+ return found ? out : void 0;
1025
+ }
1026
+ function sessionKeyForRun(meta, options) {
1027
+ if (meta?.sessionId) return meta.sessionId;
1028
+ if (options?.correlateByGroupId && meta?.groupId) {
1029
+ return `group:${meta.groupId}`;
1030
+ }
1031
+ return void 0;
1032
+ }
1033
+
1034
+ // packages/core/src/sessions/load.ts
1035
+ async function enrichSessionRunRecord(meta) {
1036
+ let metadata;
1037
+ try {
1038
+ const events = await readTraceEventsFromFile(meta.filePath);
1039
+ for (const event of events) {
1040
+ if (event.event !== "run_started") continue;
1041
+ if (event.metadata && typeof event.metadata === "object") {
1042
+ metadata = event.metadata;
1043
+ }
1044
+ break;
1045
+ }
1046
+ } catch {
1047
+ }
1048
+ return {
1049
+ runId: meta.runId,
1050
+ name: meta.name,
1051
+ status: meta.status,
1052
+ startedAt: meta.startedAt,
1053
+ endedAt: meta.endedAt,
1054
+ durationMs: meta.durationMs,
1055
+ filePath: meta.filePath,
1056
+ metadata
1057
+ };
1058
+ }
1059
+ async function loadSessionRunRecords(metas) {
1060
+ const out = [];
1061
+ for (const meta of metas) {
1062
+ out.push(await enrichSessionRunRecord(meta));
1063
+ }
1064
+ return out;
1065
+ }
1066
+
1067
+ // packages/core/src/sessions/index.ts
1068
+ function compareRuns(a, b) {
1069
+ const aStart = a.startedAt ?? 0;
1070
+ const bStart = b.startedAt ?? 0;
1071
+ if (aStart !== bStart) return aStart - bStart;
1072
+ return a.runId.localeCompare(b.runId);
1073
+ }
1074
+ function buildGroups(runIds, metaByRunId) {
1075
+ const byGroup = /* @__PURE__ */ new Map();
1076
+ for (const runId of runIds) {
1077
+ const groupId = metaByRunId.get(runId)?.groupId;
1078
+ if (!groupId) continue;
1079
+ const existing = byGroup.get(groupId);
1080
+ if (existing) {
1081
+ existing.runIds.push(runId);
1082
+ } else {
1083
+ byGroup.set(groupId, {
1084
+ groupId,
1085
+ parentGroupId: metaByRunId.get(runId)?.parentGroupId,
1086
+ runIds: [runId]
1087
+ });
1088
+ }
1089
+ }
1090
+ return [...byGroup.values()].map((group) => ({
1091
+ ...group,
1092
+ runIds: [...group.runIds].sort((a, b) => a.localeCompare(b))
1093
+ }));
1094
+ }
1095
+ function buildHandoffs(runIds, metaByRunId, warnings, sessionId) {
1096
+ const edges = [];
1097
+ const seen = /* @__PURE__ */ new Set();
1098
+ const pushEdge = (edge) => {
1099
+ const key = `${edge.from}->${edge.to}:${edge.confidence}`;
1100
+ if (seen.has(key)) return;
1101
+ seen.add(key);
1102
+ edges.push(edge);
1103
+ };
1104
+ for (const runId of runIds) {
1105
+ const meta = metaByRunId.get(runId);
1106
+ if (!meta) continue;
1107
+ if (meta.handoffFrom && meta.handoffTo) {
1108
+ pushEdge({
1109
+ from: meta.handoffFrom,
1110
+ to: meta.handoffTo,
1111
+ source: "manual",
1112
+ confidence: "explicit"
1113
+ });
1114
+ continue;
1115
+ }
1116
+ if (meta.handoffFrom) {
1117
+ pushEdge({
1118
+ from: meta.handoffFrom,
1119
+ to: runId,
1120
+ source: "manual",
1121
+ confidence: "explicit"
1122
+ });
1123
+ }
1124
+ if (meta.handoffTo) {
1125
+ pushEdge({
1126
+ from: runId,
1127
+ to: meta.handoffTo,
1128
+ source: "manual",
1129
+ confidence: "explicit"
1130
+ });
1131
+ }
1132
+ if (meta.subAgentId && meta.parentGroupId && !meta.handoffFrom && !meta.handoffTo) {
1133
+ pushEdge({
1134
+ from: meta.parentGroupId,
1135
+ to: meta.subAgentId,
1136
+ source: "inferred",
1137
+ confidence: "correlated"
1138
+ });
1139
+ warnings.push({
1140
+ code: "ambiguous-handoff-endpoints",
1141
+ message: "Handoff inferred from parentGroupId and subAgentId without explicit handoffFrom/handoffTo.",
1142
+ runId,
1143
+ sessionId
1144
+ });
1145
+ }
1146
+ }
1147
+ return edges.sort((a, b) => {
1148
+ const from = a.from.localeCompare(b.from);
1149
+ if (from !== 0) return from;
1150
+ return a.to.localeCompare(b.to);
1151
+ });
1152
+ }
1153
+ function buildRetries(runIds, metaByRunId, warnings, sessionId) {
1154
+ const retries = [];
1155
+ for (const runId of runIds) {
1156
+ const meta = metaByRunId.get(runId);
1157
+ if (!meta) continue;
1158
+ if (meta.retryOf) {
1159
+ retries.push({
1160
+ runId,
1161
+ retryOf: meta.retryOf,
1162
+ attempt: meta.attempt,
1163
+ source: "manual",
1164
+ confidence: "explicit"
1165
+ });
1166
+ continue;
1167
+ }
1168
+ if (meta.attempt !== void 0 && meta.attempt > 1) {
1169
+ retries.push({
1170
+ runId,
1171
+ attempt: meta.attempt,
1172
+ source: "inferred",
1173
+ confidence: "correlated"
1174
+ });
1175
+ warnings.push({
1176
+ code: "ambiguous-retry-link",
1177
+ message: "attempt > 1 without retryOf; retry link is correlated only.",
1178
+ runId,
1179
+ sessionId
1180
+ });
1181
+ }
1182
+ }
1183
+ return retries.sort((a, b) => a.runId.localeCompare(b.runId));
1184
+ }
1185
+ function buildCriticalPath(runs, handoffs) {
1186
+ const runById = new Map(runs.map((run) => [run.runId, run]));
1187
+ const explicitTargets = new Set(
1188
+ handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.to)
1189
+ );
1190
+ const explicitSources = new Set(
1191
+ handoffs.filter((edge) => edge.confidence === "explicit").map((edge) => edge.from)
1192
+ );
1193
+ const ordered = [...runs].sort(compareRuns);
1194
+ const path7 = [];
1195
+ const visited = /* @__PURE__ */ new Set();
1196
+ const pushRun = (run, confidence, source) => {
1197
+ if (visited.has(run.runId)) return;
1198
+ visited.add(run.runId);
1199
+ path7.push({
1200
+ runId: run.runId,
1201
+ name: run.name,
1202
+ startedAt: run.startedAt,
1203
+ durationMs: run.durationMs,
1204
+ confidence,
1205
+ source
1206
+ });
1207
+ };
1208
+ for (const edge of handoffs) {
1209
+ if (edge.confidence !== "explicit") continue;
1210
+ const fromRun = [...runById.values()].find(
1211
+ (run) => run.runId === edge.from || metaRunIdMatches(run, edge.from, runById)
1212
+ );
1213
+ const toRun = [...runById.values()].find(
1214
+ (run) => run.runId === edge.to || metaRunIdMatches(run, edge.to, runById)
1215
+ );
1216
+ if (fromRun) pushRun(fromRun, "explicit", "manual");
1217
+ if (toRun) pushRun(toRun, "explicit", "manual");
1218
+ }
1219
+ for (const run of ordered) {
1220
+ if (visited.has(run.runId)) continue;
1221
+ const confidence = explicitTargets.has(run.runId) || explicitSources.has(run.runId) ? "explicit" : "correlated";
1222
+ pushRun(run, confidence, confidence === "explicit" ? "manual" : "inferred");
1223
+ }
1224
+ return path7;
1225
+ }
1226
+ function metaRunIdMatches(run, token, runById) {
1227
+ const meta = extractSessionWorkflowMetadata(run.metadata);
1228
+ return meta?.subAgentId === token || meta?.groupId === token || runById.has(token);
1229
+ }
1230
+ function buildSessionIndex(inputRuns, options = {}) {
1231
+ const warnings = [];
1232
+ const runs = [...inputRuns].sort(compareRuns);
1233
+ const metaByRunId = /* @__PURE__ */ new Map();
1234
+ for (const run of runs) {
1235
+ metaByRunId.set(run.runId, extractSessionWorkflowMetadata(run.metadata));
1236
+ }
1237
+ const sessionsByKey = /* @__PURE__ */ new Map();
1238
+ const unscopedRunIds = [];
1239
+ for (const run of runs) {
1240
+ const meta = metaByRunId.get(run.runId);
1241
+ const key = sessionKeyForRun(meta, {
1242
+ correlateByGroupId: options.correlateByGroupId === true
1243
+ });
1244
+ if (!key) {
1245
+ unscopedRunIds.push(run.runId);
1246
+ continue;
1247
+ }
1248
+ const bucket = sessionsByKey.get(key) ?? [];
1249
+ bucket.push(run);
1250
+ sessionsByKey.set(key, bucket);
1251
+ }
1252
+ const sessions = [...sessionsByKey.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([sessionId, sessionRuns]) => {
1253
+ const runIds = sessionRuns.map((run) => run.runId).sort();
1254
+ const handoffs = buildHandoffs(runIds, metaByRunId, warnings, sessionId);
1255
+ const retries = buildRetries(runIds, metaByRunId, warnings, sessionId);
1256
+ const groups = buildGroups(runIds, metaByRunId);
1257
+ const criticalPath = buildCriticalPath(sessionRuns, handoffs);
1258
+ const confidences = new Set(handoffs.map((edge) => edge.confidence));
1259
+ if (confidences.has("explicit") && confidences.has("correlated")) {
1260
+ warnings.push({
1261
+ code: "mixed-confidence-group",
1262
+ message: "Session aggregates explicit and correlated handoff edges.",
1263
+ sessionId
1264
+ });
1265
+ }
1266
+ return {
1267
+ sessionId,
1268
+ runIds,
1269
+ groups,
1270
+ handoffs,
1271
+ retries,
1272
+ criticalPath
1273
+ };
1274
+ });
1275
+ if (sessions.length === 0 && runs.length > 0) {
1276
+ warnings.push({
1277
+ code: "missing-session-id",
1278
+ message: "No sessionId (or correlated groupId) found on input runs."
1279
+ });
1280
+ }
1281
+ warnings.sort((a, b) => {
1282
+ const code = a.code.localeCompare(b.code);
1283
+ if (code !== 0) return code;
1284
+ return (a.runId ?? "").localeCompare(b.runId ?? "");
1285
+ });
1286
+ return {
1287
+ runs,
1288
+ sessions,
1289
+ unscopedRunIds: unscopedRunIds.sort(),
1290
+ warnings
1291
+ };
1292
+ }
1293
+
1294
+ // packages/core/src/checks/index.ts
1295
+ var SEVERITY_RANK = {
1296
+ error: 0,
1297
+ warning: 1,
1298
+ info: 2
1299
+ };
1300
+ var STATUS_RANK = {
1301
+ fail: 0,
1302
+ warning: 1,
1303
+ pass: 2
1304
+ };
1305
+ function compareStrings(a, b) {
1306
+ return (a ?? "").localeCompare(b ?? "");
1307
+ }
1308
+ function diagnostic(code, message, ruleId) {
1309
+ return {
1310
+ code,
1311
+ message,
1312
+ severity: "error",
1313
+ ...ruleId ? { ruleId } : {}
1314
+ };
1315
+ }
1316
+ function emptySummary() {
1317
+ return {
1318
+ passed: 0,
1319
+ failed: 0,
1320
+ warnings: 0,
1321
+ errors: 0
1322
+ };
1323
+ }
1324
+ function errorResult(input, diagnostics, selectedRun) {
1325
+ return {
1326
+ ok: false,
1327
+ status: "error",
1328
+ format: input.read.format,
1329
+ ...selectedRun ? { runId: selectedRun.runId } : {},
1330
+ summary: {
1331
+ ...emptySummary(),
1332
+ errors: diagnostics.filter((item) => item.severity === "error").length
1333
+ },
1334
+ findings: [],
1335
+ diagnostics: [...diagnostics]
1336
+ };
1337
+ }
1338
+ function flattenNodes(nodes) {
1339
+ return nodes.flatMap((node) => [node, ...flattenNodes(node.children)]);
1340
+ }
1341
+ function buildFacts(input, selectedRun) {
1342
+ const scopedRuns = selectedRun ? [selectedRun] : input.read.runs;
1343
+ const scopedRunIds = new Set(scopedRuns.map((run) => run.runId));
1344
+ const scopedEvents = selectedRun === void 0 ? input.read.events : input.read.events.filter((event) => scopedRunIds.has(event.runId));
1345
+ const nodes = flattenNodes(scopedRuns.flatMap((run) => run.children));
1346
+ const nodesByEventId = /* @__PURE__ */ new Map();
1347
+ const childrenByParentId = /* @__PURE__ */ new Map();
1348
+ for (const node of nodes) {
1349
+ nodesByEventId.set(node.event.eventId, node);
1350
+ const parentId = node.event.parentId;
1351
+ if (parentId) {
1352
+ const children = childrenByParentId.get(parentId) ?? [];
1353
+ children.push(node);
1354
+ childrenByParentId.set(parentId, children);
1355
+ }
1356
+ }
1357
+ return {
1358
+ format: input.read.format,
1359
+ runs: Object.freeze([...input.read.runs]),
1360
+ events: Object.freeze([...scopedEvents]),
1361
+ readerWarnings: Object.freeze([...input.read.warnings]),
1362
+ unsupportedFields: Object.freeze([...input.read.unsupportedFields]),
1363
+ sourceFiles: Object.freeze([...input.read.sourceFiles]),
1364
+ nodesByEventId,
1365
+ childrenByParentId,
1366
+ rootNodes: Object.freeze(scopedRuns.flatMap((run) => run.children))
1367
+ };
1368
+ }
1369
+ function resolveSelectedRun(input, runId) {
1370
+ if (input.selectedRun) {
1371
+ if (runId && input.selectedRun.runId !== runId) {
1372
+ return {
1373
+ diagnostics: [
1374
+ diagnostic(
1375
+ "AI_CHECK_INVALID_ARGUMENTS",
1376
+ `Selected run ${input.selectedRun.runId} does not match requested run ${runId}.`
1377
+ )
1378
+ ]
1379
+ };
1380
+ }
1381
+ return { run: input.selectedRun, diagnostics: [] };
1382
+ }
1383
+ if (runId) {
1384
+ const run = input.read.runs.find((candidate) => candidate.runId === runId);
1385
+ if (!run) {
1386
+ return {
1387
+ diagnostics: [
1388
+ diagnostic("AI_CHECK_RUN_SELECTION_REQUIRED", `Run not found: ${runId}.`)
1389
+ ]
1390
+ };
1391
+ }
1392
+ return { run, diagnostics: [] };
1393
+ }
1394
+ if (input.read.runs.length === 1) {
1395
+ return { run: input.read.runs[0], diagnostics: [] };
1396
+ }
1397
+ if (input.read.runs.length === 0) {
1398
+ return {
1399
+ diagnostics: [
1400
+ diagnostic("AI_CHECK_RUN_SELECTION_REQUIRED", "No runs are available for checks.")
1401
+ ]
1402
+ };
1403
+ }
1404
+ return {
1405
+ diagnostics: [
1406
+ diagnostic(
1407
+ "AI_CHECK_RUN_SELECTION_REQUIRED",
1408
+ "Multiple runs are available; select a run before executing checks."
1409
+ )
1410
+ ]
1411
+ };
1412
+ }
1413
+ function selectRules(rules, selectedIds) {
1414
+ const diagnostics = [];
1415
+ const byId = /* @__PURE__ */ new Map();
1416
+ for (const rule of rules) {
1417
+ if (byId.has(rule.id)) {
1418
+ diagnostics.push(
1419
+ diagnostic("AI_CHECK_INVALID_CONFIG", `Duplicate trace check rule id: ${rule.id}.`, rule.id)
1420
+ );
1421
+ continue;
1422
+ }
1423
+ byId.set(rule.id, rule);
1424
+ }
1425
+ if (selectedIds && selectedIds.length > 0) {
1426
+ const selected = new Set(selectedIds);
1427
+ for (const id of selected) {
1428
+ if (!byId.has(id)) {
1429
+ diagnostics.push(
1430
+ diagnostic("AI_CHECK_INVALID_CONFIG", `Unknown trace check rule id: ${id}.`, id)
1431
+ );
1432
+ }
1433
+ }
1434
+ return {
1435
+ rules: [...byId.values()].filter((rule) => selected.has(rule.id)).sort(compareRules),
1436
+ diagnostics
1437
+ };
1438
+ }
1439
+ return { rules: [...byId.values()].sort(compareRules), diagnostics };
1440
+ }
1441
+ function compareRules(a, b) {
1442
+ return a.id.localeCompare(b.id);
1443
+ }
1444
+ function eventTimestamp(finding, eventById) {
1445
+ const eventId = finding.evidence[0]?.eventId;
1446
+ return eventId ? eventById.get(eventId)?.timestamp ?? "" : "";
1447
+ }
1448
+ function compareFindings(eventById) {
1449
+ return (a, b) => {
1450
+ if (SEVERITY_RANK[a.severity] !== SEVERITY_RANK[b.severity]) {
1451
+ return SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
1452
+ }
1453
+ const byRule = a.ruleId.localeCompare(b.ruleId);
1454
+ if (byRule !== 0) return byRule;
1455
+ if (STATUS_RANK[a.status] !== STATUS_RANK[b.status]) {
1456
+ return STATUS_RANK[a.status] - STATUS_RANK[b.status];
1457
+ }
1458
+ const byRun = compareStrings(a.evidence[0]?.runId, b.evidence[0]?.runId);
1459
+ if (byRun !== 0) return byRun;
1460
+ const byTime = eventTimestamp(a, eventById).localeCompare(eventTimestamp(b, eventById));
1461
+ if (byTime !== 0) return byTime;
1462
+ const byEvent = compareStrings(a.evidence[0]?.eventId, b.evidence[0]?.eventId);
1463
+ if (byEvent !== 0) return byEvent;
1464
+ return compareStrings(a.evidence[0]?.path, b.evidence[0]?.path);
1465
+ };
1466
+ }
1467
+ function normalizeFinding(rule, finding) {
1468
+ return {
1469
+ ruleId: finding.ruleId || rule.id,
1470
+ severity: finding.severity ?? rule.defaultSeverity,
1471
+ status: finding.status,
1472
+ message: finding.message,
1473
+ ...finding.expected !== void 0 ? { expected: finding.expected } : {},
1474
+ ...finding.actual !== void 0 ? { actual: finding.actual } : {},
1475
+ evidence: [...finding.evidence ?? []]
1476
+ };
1477
+ }
1478
+ function summarize(findings, diagnostics) {
1479
+ return {
1480
+ passed: findings.filter((finding) => finding.status === "pass").length,
1481
+ failed: findings.filter(
1482
+ (finding) => finding.status === "fail" && finding.severity === "error"
1483
+ ).length,
1484
+ warnings: findings.filter(
1485
+ (finding) => finding.status === "warning" || finding.severity === "warning"
1486
+ ).length,
1487
+ errors: diagnostics.filter((item) => item.severity === "error").length
1488
+ };
1489
+ }
1490
+ function eventEvidence(event, path7) {
1491
+ return {
1492
+ runId: event.runId,
1493
+ eventId: event.eventId,
1494
+ parentId: event.parentId,
1495
+ traceId: event.trace?.traceId,
1496
+ spanId: event.trace?.spanId,
1497
+ kind: event.kind,
1498
+ name: event.name,
1499
+ status: event.status,
1500
+ ...{}
1501
+ };
1502
+ }
1503
+ function runEvidence(run) {
1504
+ return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
1505
+ }
1506
+ function failFinding(ruleId, message, evidence, expected, actual) {
1507
+ return {
1508
+ ruleId,
1509
+ severity: "error",
1510
+ status: "fail",
1511
+ message,
1512
+ ...expected !== void 0 ? { expected } : {},
1513
+ ...actual !== void 0 ? { actual } : {},
1514
+ evidence: [...evidence]
1515
+ };
1516
+ }
1517
+ function createRunStatusRule(options = {}) {
1518
+ const expected = options.expected ?? "ok";
1519
+ const allowIncomplete = options.allowIncomplete === true;
1520
+ return {
1521
+ id: "run.status",
1522
+ category: "run",
1523
+ defaultSeverity: "error",
1524
+ evaluate(context) {
1525
+ const findings = [];
1526
+ const actual = context.selectedRun?.status ?? "unknown";
1527
+ if (actual !== expected) {
1528
+ findings.push(
1529
+ failFinding(
1530
+ "run.status",
1531
+ `Run status ${actual} did not match expected ${expected}.`,
1532
+ runEvidence(context.selectedRun),
1533
+ expected,
1534
+ actual
1535
+ )
1536
+ );
1537
+ }
1538
+ if (!allowIncomplete) {
1539
+ const running = context.events.filter((event) => event.status === "running");
1540
+ if (running.length > 0) {
1541
+ findings.push(
1542
+ failFinding(
1543
+ "run.status",
1544
+ "Run contains incomplete running events.",
1545
+ running.map((event) => eventEvidence(event)),
1546
+ "no running events",
1547
+ running.length
1548
+ )
1549
+ );
1550
+ }
1551
+ }
1552
+ return findings;
1553
+ }
1554
+ };
1555
+ }
1556
+ function runTraceChecks(input, options = {}) {
1557
+ const selected = resolveSelectedRun(input, options.runId);
1558
+ if (selected.diagnostics.length > 0) {
1559
+ return errorResult(input, selected.diagnostics, selected.run);
1560
+ }
1561
+ const rules = selectRules(options.rules ?? [], options.select);
1562
+ if (rules.diagnostics.length > 0) {
1563
+ return errorResult(input, rules.diagnostics, selected.run);
1564
+ }
1565
+ const facts = buildFacts(input, selected.run);
1566
+ const context = {
1567
+ ...facts,
1568
+ ...selected.run ? { selectedRun: selected.run } : {},
1569
+ ...input.sourceLabel ? { sourceLabel: input.sourceLabel } : {}
1570
+ };
1571
+ const diagnostics = [];
1572
+ const findings = [];
1573
+ for (const rule of rules.rules) {
1574
+ try {
1575
+ findings.push(...rule.evaluate(context).map((finding) => normalizeFinding(rule, finding)));
1576
+ } catch (error) {
1577
+ const message = error instanceof Error ? error.message : String(error);
1578
+ diagnostics.push(
1579
+ diagnostic("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
1580
+ );
1581
+ }
1582
+ }
1583
+ if (diagnostics.length > 0) {
1584
+ return errorResult(input, diagnostics, selected.run);
1585
+ }
1586
+ const eventById = new Map(input.read.events.map((event) => [event.eventId, event]));
1587
+ const sortedFindings = findings.sort(compareFindings(eventById));
1588
+ const summary = summarize(sortedFindings, diagnostics);
1589
+ const status = summary.failed > 0 ? "fail" : "pass";
1590
+ return {
1591
+ ok: status === "pass",
1592
+ status,
1593
+ format: input.read.format,
1594
+ ...selected.run ? { runId: selected.run.runId } : {},
1595
+ summary,
1596
+ findings: sortedFindings,
1597
+ diagnostics
1598
+ };
1599
+ }
1600
+
1601
+ // packages/core/src/persisted/token-usage.ts
1602
+ function isRecord5(value) {
1603
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1604
+ }
1605
+ function nonNegativeFinite(value) {
1606
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1607
+ }
1608
+ function normalizeTokenUsage(value) {
1609
+ if (!isRecord5(value)) return void 0;
1610
+ const input = nonNegativeFinite(value.input);
1611
+ const output = nonNegativeFinite(value.output);
1612
+ const suppliedTotal = nonNegativeFinite(value.total);
1613
+ const cached = nonNegativeFinite(value.cached);
1614
+ const derivedTotal = input !== void 0 && output !== void 0 && Number.isFinite(input + output) ? input + output : void 0;
1615
+ const total = suppliedTotal ?? derivedTotal;
1616
+ if (input === void 0 && output === void 0 && total === void 0 && cached === void 0) {
1617
+ return void 0;
1618
+ }
1619
+ return {
1620
+ ...input !== void 0 ? { input } : {},
1621
+ ...output !== void 0 ? { output } : {},
1622
+ ...total !== void 0 ? { total } : {},
1623
+ ...cached !== void 0 ? { cached } : {}
1624
+ };
1625
+ }
1626
+
1627
+ // packages/core/src/persisted/from-trace-event.ts
1628
+ function sanitizeIdPart(value) {
1629
+ return value.replace(/[^a-zA-Z0-9_-]/g, "_");
1630
+ }
1631
+ function nodeIdForEvent(event) {
1632
+ switch (event.event) {
1633
+ case "run_started":
1634
+ case "run_completed":
1635
+ return event.runId;
1636
+ case "step_started":
1637
+ case "step_completed":
1638
+ return event.stepId;
1639
+ default:
1640
+ return "unknown";
1641
+ }
1642
+ }
1643
+ function createPersistedEventId(event, eventIndex) {
1644
+ const runId = sanitizeIdPart(event.runId);
1645
+ const ev = sanitizeIdPart(event.event);
1646
+ const node = sanitizeIdPart(nodeIdForEvent(event));
1647
+ return `manual:${runId}:${ev}:${node}:${eventIndex}`;
1648
+ }
1649
+ function toIsoTimestamp(ms) {
1650
+ if (typeof ms !== "number" || !Number.isFinite(ms)) {
1651
+ return { iso: (/* @__PURE__ */ new Date(0)).toISOString(), invalidTimestamp: true };
1652
+ }
1653
+ return { iso: new Date(ms).toISOString(), invalidTimestamp: false };
1654
+ }
1655
+ function buildSource(options) {
1656
+ return {
1657
+ type: "manual",
1658
+ name: options?.sourceName ?? "trace-event",
1659
+ version: options?.sourceVersion ?? "0.1"
1660
+ };
1661
+ }
1662
+ function mapStepTypeToInspectKind(type) {
1663
+ switch (type) {
1664
+ case "run":
1665
+ return "RUN";
1666
+ case "llm":
1667
+ return "LLM";
1668
+ case "tool":
1669
+ return "TOOL";
1670
+ case "decision":
1671
+ return "DECISION";
1672
+ case "logic":
1673
+ case "state":
1674
+ case "custom":
1675
+ return "LOGIC";
1676
+ default:
1677
+ return "LOGIC";
1678
+ }
1679
+ }
1680
+ function mapRunOrStepStatus(status) {
1681
+ return status === "success" ? "ok" : "error";
1682
+ }
1683
+ function mapErrorInfo(error) {
1684
+ if (!error?.message) {
1685
+ return {};
1686
+ }
1687
+ const out = {
1688
+ persisted: {
1689
+ message: error.message,
1690
+ name: "Error"
1691
+ }
1692
+ };
1693
+ if (typeof error.stack === "string" && error.stack.length > 0) {
1694
+ out.errorStack = error.stack;
1695
+ }
1696
+ return out;
1697
+ }
1698
+ function mapTokenUsageFromMetadata(metadata) {
1699
+ return normalizeTokenUsage(metadata?.tokens);
1700
+ }
1701
+ function compactAttributes(entries) {
1702
+ const out = {};
1703
+ for (const [key, value] of Object.entries(entries)) {
1704
+ if (value !== void 0) {
1705
+ out[key] = value;
1706
+ }
1707
+ }
1708
+ return Object.keys(out).length > 0 ? out : void 0;
1709
+ }
1710
+ function traceEventToPersistedInspectEvent(event, options) {
1711
+ const eventIndex = options?.eventIndex ?? 0;
1712
+ const eventId = createPersistedEventId(event, eventIndex);
1713
+ const source = buildSource(options);
1714
+ const tsMain = toIsoTimestamp(event.timestamp);
1715
+ switch (event.event) {
1716
+ case "run_started": {
1717
+ const tsStart = toIsoTimestamp(event.startTime);
1718
+ const correlation = extractCorrelationMetadata(event.metadata);
1719
+ const attributes = compactAttributes({
1720
+ legacyEvent: "run_started",
1721
+ metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
1722
+ correlationId: correlation?.correlationId,
1723
+ requestId: correlation?.requestId,
1724
+ decisionId: correlation?.decisionId,
1725
+ groupId: correlation?.groupId,
1726
+ invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
1727
+ });
1728
+ return {
1729
+ schemaVersion: "0.2",
1730
+ eventId,
1731
+ runId: event.runId,
1732
+ kind: "RUN",
1733
+ name: event.name,
1734
+ status: "running",
1735
+ timestamp: tsMain.iso,
1736
+ startedAt: tsStart.iso,
1737
+ confidence: "explicit",
1738
+ source,
1739
+ attributes
1740
+ };
1741
+ }
1742
+ case "run_completed": {
1743
+ const tsEnd = toIsoTimestamp(event.endTime);
1744
+ const { persisted: error, errorStack } = mapErrorInfo(event.error);
1745
+ const attributes = compactAttributes({
1746
+ legacyEvent: "run_completed",
1747
+ errorStack,
1748
+ invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
1749
+ });
1750
+ return {
1751
+ schemaVersion: "0.2",
1752
+ eventId,
1753
+ runId: event.runId,
1754
+ kind: "RUN",
1755
+ name: "run",
1756
+ status: mapRunOrStepStatus(event.status),
1757
+ timestamp: tsMain.iso,
1758
+ endedAt: tsEnd.iso,
1759
+ durationMs: event.durationMs,
1760
+ confidence: "explicit",
1761
+ source,
1762
+ attributes,
1763
+ error
1764
+ };
1765
+ }
1766
+ case "step_started": {
1767
+ const tsStart = toIsoTimestamp(event.startTime);
1768
+ const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
1769
+ const attributes = compactAttributes({
1770
+ legacyEvent: "step_started",
1771
+ stepId: event.stepId,
1772
+ stepType: event.type,
1773
+ metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
1774
+ invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
1775
+ });
1776
+ const out = {
1777
+ schemaVersion: "0.2",
1778
+ eventId,
1779
+ runId: event.runId,
1780
+ kind: mapStepTypeToInspectKind(event.type),
1781
+ name: event.name,
1782
+ status: "running",
1783
+ timestamp: tsMain.iso,
1784
+ startedAt: tsStart.iso,
1785
+ confidence: "explicit",
1786
+ source,
1787
+ attributes
1788
+ };
1789
+ if (event.parentId !== void 0) {
1790
+ out.parentId = event.parentId;
1791
+ }
1792
+ if (tokenUsage !== void 0) {
1793
+ out.tokenUsage = tokenUsage;
1794
+ }
1795
+ return out;
1796
+ }
1797
+ case "step_completed": {
1798
+ const tsEnd = toIsoTimestamp(event.endTime);
1799
+ const { persisted: error, errorStack } = mapErrorInfo(event.error);
1800
+ const attributes = compactAttributes({
1801
+ legacyEvent: "step_completed",
1802
+ stepId: event.stepId,
1803
+ errorStack,
1804
+ invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
1805
+ });
1806
+ return {
1807
+ schemaVersion: "0.2",
1808
+ eventId,
1809
+ runId: event.runId,
1810
+ kind: "LOGIC",
1811
+ name: event.stepId,
1812
+ status: mapRunOrStepStatus(event.status),
1813
+ timestamp: tsMain.iso,
1814
+ endedAt: tsEnd.iso,
1815
+ durationMs: event.durationMs,
1816
+ confidence: "explicit",
1817
+ source,
1818
+ attributes,
1819
+ error
1820
+ };
1821
+ }
1822
+ default: {
1823
+ const _exhaustive = event;
1824
+ throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
1825
+ }
1826
+ }
1827
+ }
1828
+ function traceEventsToPersistedInspectEvents(events, options) {
1829
+ return events.map(
1830
+ (event, index) => traceEventToPersistedInspectEvent(event, { ...options, eventIndex: index })
1831
+ );
1832
+ }
1833
+
1834
+ // packages/core/src/persisted/to-inspect-event.ts
1835
+ function compactAttributes2(entries) {
1836
+ const out = {};
1837
+ for (const [key, value] of Object.entries(entries)) {
1838
+ if (value !== void 0) {
1839
+ out[key] = value;
1840
+ }
1841
+ }
1842
+ return Object.keys(out).length > 0 ? out : void 0;
1843
+ }
1844
+ function parseIsoToMs3(iso) {
1845
+ const parsed = Date.parse(iso);
1846
+ if (!Number.isFinite(parsed)) {
1847
+ return { ms: 0, invalidTimestamp: true };
1848
+ }
1849
+ return { ms: parsed, invalidTimestamp: false };
1850
+ }
1851
+ function mapPersistedSourceToInspect(event) {
1852
+ const attrs = event.attributes ?? {};
1853
+ const sourceName = event.source.name;
1854
+ if (sourceName === "pino") {
1855
+ return {
1856
+ type: "pino",
1857
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
1858
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
1859
+ };
1860
+ }
1861
+ if (sourceName === "winston") {
1862
+ return {
1863
+ type: "winston",
1864
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
1865
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
1866
+ };
1867
+ }
1868
+ const mapType = (t) => {
1869
+ switch (t) {
1870
+ case "manual":
1871
+ return "manual";
1872
+ case "json-log":
1873
+ return "json-log";
1874
+ case "log4js":
1875
+ return "log4js";
1876
+ case "adapter":
1877
+ case "ai-sdk":
1878
+ case "otel":
1879
+ return "adapter";
1880
+ default:
1881
+ return "json-log";
1882
+ }
1883
+ };
1884
+ return {
1885
+ type: mapType(event.source.type),
1886
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
1887
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
1888
+ };
1889
+ }
1890
+ function buildInspectAttributes(event) {
1891
+ const attrs = event.attributes !== void 0 ? { ...event.attributes } : {};
1892
+ if (event.inputSummary !== void 0) {
1893
+ attrs.inputSummary = event.inputSummary;
1894
+ }
1895
+ if (event.outputSummary !== void 0) {
1896
+ attrs.outputSummary = event.outputSummary;
1897
+ }
1898
+ if (event.error) {
1899
+ if (event.error.name !== void 0) {
1900
+ attrs.errorName = event.error.name;
1901
+ }
1902
+ attrs.errorMessage = event.error.message;
1903
+ if (event.error.code !== void 0) {
1904
+ attrs.errorCode = event.error.code;
1905
+ }
1906
+ }
1907
+ if (event.tokenUsage) {
1908
+ attrs.tokens = { ...event.tokenUsage };
1909
+ }
1910
+ if (event.source.type === "ai-sdk" || event.source.type === "otel") {
1911
+ attrs.originalSourceType = event.source.type;
1912
+ }
1913
+ if (event.source.name !== void 0) {
1914
+ attrs.sourceName = event.source.name;
1915
+ }
1916
+ if (event.source.version !== void 0) {
1917
+ attrs.sourceVersion = event.source.version;
1918
+ }
1919
+ return attrs;
1920
+ }
1921
+ function persistedInspectEventToInspectEvent(event) {
1922
+ if (!isPersistedInspectEvent(event)) {
1923
+ throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
1924
+ }
1925
+ const ts = parseIsoToMs3(event.timestamp);
1926
+ const attrs = buildInspectAttributes(event);
1927
+ if (ts.invalidTimestamp) {
1928
+ attrs.invalidTimestamp = true;
1929
+ }
1930
+ let status;
1931
+ if (event.status === "running" || event.status === "ok" || event.status === "error") {
1932
+ status = event.status;
1933
+ } else if (event.status === "unknown") {
1934
+ attrs.persistedStatus = "unknown";
1935
+ }
1936
+ const out = {
1937
+ eventId: event.eventId,
1938
+ runId: event.runId,
1939
+ name: event.name,
1940
+ kind: event.kind,
1941
+ timestamp: ts.ms,
1942
+ confidence: event.confidence,
1943
+ source: mapPersistedSourceToInspect(event),
1944
+ attributes: compactAttributes2(attrs)
1945
+ };
1946
+ if (event.parentId !== void 0) {
1947
+ out.parentId = event.parentId;
1948
+ }
1949
+ if (status !== void 0) {
1950
+ out.status = status;
1951
+ }
1952
+ if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
1953
+ out.durationMs = event.durationMs;
1954
+ }
1955
+ return out;
1956
+ }
1957
+ function persistedInspectEventsToInspectEvents(events, options) {
1958
+ const skipInvalid = options?.skipInvalid === true;
1959
+ const out = [];
1960
+ for (const event of events) {
1961
+ if (!isPersistedInspectEvent(event)) {
1962
+ if (skipInvalid) {
1963
+ continue;
1964
+ }
1965
+ throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
1966
+ }
1967
+ out.push(persistedInspectEventToInspectEvent(event));
1968
+ }
1969
+ return out;
1970
+ }
1971
+
1972
+ // packages/core/src/logs/tree-builder.ts
1973
+ function inc(map, key) {
1974
+ map[key] = (map[key] ?? 0) + 1;
1975
+ }
1976
+ function computeRunStatus(events) {
1977
+ let hasRunning = false;
1978
+ for (const e of events) {
1979
+ if (e.status === "error") return "error";
1980
+ if (e.status === "running") hasRunning = true;
1981
+ }
1982
+ if (hasRunning) return "running";
1983
+ return "ok";
1984
+ }
1985
+ var TreeBuilder = class {
1986
+ constructor(options) {
1987
+ void options?.config;
1988
+ }
1989
+ build(events) {
1990
+ const byRun = /* @__PURE__ */ new Map();
1991
+ for (const e of events) {
1992
+ if (!byRun.has(e.runId)) byRun.set(e.runId, []);
1993
+ byRun.get(e.runId).push(e);
1994
+ }
1995
+ const out = [];
1996
+ for (const [runId, runEvents] of byRun.entries()) {
1997
+ const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
1998
+ const nodes = /* @__PURE__ */ new Map();
1999
+ for (const e of sorted) {
2000
+ nodes.set(e.eventId, { event: e, children: [], depth: 0 });
2001
+ }
2002
+ const roots = [];
2003
+ for (const node of nodes.values()) {
2004
+ const parentId = node.event.parentId;
2005
+ if (parentId && nodes.has(parentId)) {
2006
+ nodes.get(parentId).children.push(node);
2007
+ } else {
2008
+ roots.push(node);
2009
+ }
2010
+ }
2011
+ const assignDepth = (n, depth) => {
2012
+ n.depth = depth;
2013
+ for (const c of n.children) assignDepth(c, depth + 1);
2014
+ };
2015
+ for (const r of roots) assignDepth(r, 0);
2016
+ const confidenceBreakdown = {
2017
+ explicit: 0,
2018
+ correlated: 0,
2019
+ heuristic: 0,
2020
+ unknown: 0
2021
+ };
2022
+ const kinds = {};
2023
+ for (const e of sorted) {
2024
+ inc(confidenceBreakdown, e.confidence);
2025
+ kinds[e.kind] = (kinds[e.kind] ?? 0) + 1;
2026
+ }
2027
+ const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
2028
+ const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
2029
+ const status = computeRunStatus(sorted);
2030
+ const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
2031
+ const name = sorted.find((e) => e.kind === "RUN")?.name;
2032
+ out.push({
2033
+ runId,
2034
+ name,
2035
+ status,
2036
+ startedAt,
2037
+ endedAt: status === "running" ? void 0 : endedAt,
2038
+ durationMs,
2039
+ children: roots,
2040
+ metadata: {
2041
+ totalEvents: sorted.length,
2042
+ confidenceBreakdown,
2043
+ kinds
2044
+ }
2045
+ });
2046
+ }
2047
+ out.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
2048
+ return out;
2049
+ }
2050
+ };
2051
+
2052
+ // packages/core/src/persisted/tree-bridge.ts
2053
+ function persistedInspectEventsToRunTrees(events, options) {
2054
+ const inspectEvents = persistedInspectEventsToInspectEvents(events, {
2055
+ skipInvalid: options?.skipInvalid
2056
+ });
2057
+ return new TreeBuilder().build(inspectEvents);
2058
+ }
2059
+ var DEFAULT_MAX_TRACE_INPUT_BYTES = 10 * 1024 * 1024;
2060
+ var MIN_DETECTION_CONFIDENCE = 0.5;
2061
+ var AMBIGUOUS_CONFIDENCE_DELTA = 0.05;
2062
+ var resolvedInputCache = /* @__PURE__ */ new WeakMap();
2063
+ var OPENINFERENCE_READER_FORMAT = "openinference-json";
2064
+ var OTLP_READER_FORMAT = "otlp-json";
2065
+ var OPENINFERENCE_SPAN_KEYS = /* @__PURE__ */ new Set([
2066
+ "trace_id",
2067
+ "traceId",
2068
+ "span_id",
2069
+ "spanId",
2070
+ "parent_span_id",
2071
+ "parentSpanId",
2072
+ "name",
2073
+ "start_time_unix_nano",
2074
+ "startTimeUnixNano",
2075
+ "end_time_unix_nano",
2076
+ "endTimeUnixNano",
2077
+ "start_time",
2078
+ "startTime",
2079
+ "end_time",
2080
+ "endTime",
2081
+ "attributes",
2082
+ "status",
2083
+ "kind",
2084
+ "span_kind",
2085
+ "spanKind"
2086
+ ]);
2087
+ var OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS = [
2088
+ "input.value",
2089
+ "output.value",
2090
+ "input.mime_type",
2091
+ "output.mime_type",
2092
+ "llm.input_messages",
2093
+ "llm.output_messages",
2094
+ "llm.prompts",
2095
+ "llm.completions",
2096
+ "retrieval.documents",
2097
+ "reranker.input_documents",
2098
+ "reranker.output_documents",
2099
+ "document.content",
2100
+ "gen_ai.prompt",
2101
+ "gen_ai.completion",
2102
+ "gen_ai.input.messages",
2103
+ "gen_ai.output.messages"
2104
+ ];
2105
+ var OTLP_SPAN_KEYS = /* @__PURE__ */ new Set([
2106
+ "traceId",
2107
+ "spanId",
2108
+ "parentSpanId",
2109
+ "name",
2110
+ "kind",
2111
+ "startTimeUnixNano",
2112
+ "endTimeUnixNano",
2113
+ "attributes",
2114
+ "events",
2115
+ "status",
2116
+ "droppedAttributesCount",
2117
+ "droppedEventsCount",
2118
+ "droppedLinksCount",
2119
+ "links",
2120
+ "flags"
2121
+ ]);
2122
+ var TraceReadError = class extends Error {
2123
+ code;
2124
+ warnings;
2125
+ constructor(code, message, warnings = []) {
2126
+ super(message);
2127
+ this.name = "TraceReadError";
2128
+ this.code = code;
2129
+ this.warnings = warnings;
2130
+ }
2131
+ };
2132
+ function normalizeCandidate(reader, candidate) {
2133
+ const confidence = Number.isFinite(candidate.confidence) ? Math.max(0, Math.min(1, candidate.confidence)) : 0;
2134
+ return {
2135
+ ...candidate,
2136
+ format: candidate.format || reader.format,
2137
+ confidence,
2138
+ readerName: candidate.readerName ?? reader.name
2139
+ };
2140
+ }
2141
+ function sortCandidates(candidates) {
2142
+ return [...candidates].sort((a, b) => {
2143
+ if (b.confidence !== a.confidence) return b.confidence - a.confidence;
2144
+ return a.format.localeCompare(b.format);
2145
+ });
2146
+ }
2147
+ function collectWarnings(candidates) {
2148
+ return candidates.flatMap((candidate) => candidate.warnings ?? []);
2149
+ }
2150
+ function dedupeWarnings(warnings) {
2151
+ const seen = /* @__PURE__ */ new Set();
2152
+ const out = [];
2153
+ for (const warning of warnings) {
2154
+ const key = [
2155
+ warning.code,
2156
+ warning.message,
2157
+ warning.severity ?? "",
2158
+ warning.sourceFile ?? "",
2159
+ warning.line ?? "",
2160
+ warning.field ?? ""
2161
+ ].join("\0");
2162
+ if (seen.has(key)) continue;
2163
+ seen.add(key);
2164
+ out.push(warning);
2165
+ }
2166
+ return out;
2167
+ }
2168
+ function attachSingleSourceFile(warnings, resolved) {
2169
+ if (resolved.sourceFiles.length !== 1) return [...warnings];
2170
+ const [sourceFile] = resolved.sourceFiles;
2171
+ return warnings.map((warning) => ({
2172
+ ...warning,
2173
+ sourceFile: warning.sourceFile ?? sourceFile
2174
+ }));
2175
+ }
2176
+ function findReaderByFormat(format, readers) {
2177
+ return readers.find((reader) => reader.format === format);
2178
+ }
2179
+ async function jsonlFilesInDirectory(dirPath) {
2180
+ const entries = await promises.readdir(dirPath, { withFileTypes: true });
2181
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path6__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
2182
+ }
2183
+ async function resolveInput(input) {
2184
+ const cached = resolvedInputCache.get(input);
2185
+ if (cached) return cached;
2186
+ const promise = resolveInputUncached(input);
2187
+ resolvedInputCache.set(input, promise);
2188
+ return promise;
2189
+ }
2190
+ function assertInputWithinBounds(content, sourceFile) {
2191
+ const bytes = Buffer.byteLength(content, "utf8");
2192
+ if (bytes <= DEFAULT_MAX_TRACE_INPUT_BYTES) return;
2193
+ throw new TraceReadError("unsupported_format", "Trace input exceeds the local reader size limit.", [
2194
+ {
2195
+ code: "input_too_large",
2196
+ message: `Trace input is ${bytes} bytes; max is ${DEFAULT_MAX_TRACE_INPUT_BYTES} bytes.`,
2197
+ severity: "error",
2198
+ ...sourceFile !== void 0 ? { sourceFile } : {}
2199
+ }
2200
+ ]);
2201
+ }
2202
+ async function resolveInputUncached(input) {
2203
+ if (input.type === "string") {
2204
+ assertInputWithinBounds(input.content);
2205
+ return { content: input.content, sourceFiles: [] };
2206
+ }
2207
+ if (input.type === "buffer") {
2208
+ const content = input.content.toString("utf-8");
2209
+ assertInputWithinBounds(content);
2210
+ return { content, sourceFiles: [] };
2211
+ }
2212
+ if (input.type === "file") {
2213
+ const content = await promises.readFile(input.path, "utf-8");
2214
+ assertInputWithinBounds(content, input.path);
2215
+ return { content, sourceFiles: [input.path] };
2216
+ }
2217
+ if (input.type === "directory") {
2218
+ const files = await jsonlFilesInDirectory(input.path);
2219
+ const parts = await Promise.all(
2220
+ files.map(async (file) => (await promises.readFile(file, "utf-8")).trimEnd())
2221
+ );
2222
+ const content = parts.filter((part) => part.trim() !== "").join("\n");
2223
+ assertInputWithinBounds(content, input.path);
2224
+ return {
2225
+ content,
2226
+ sourceFiles: files
2227
+ };
2228
+ }
2229
+ return void 0;
2230
+ }
2231
+ function detectJsonlFormat(content) {
2232
+ let saw01 = false;
2233
+ let saw02 = false;
2234
+ let saw10 = false;
2235
+ let validRows = 0;
2236
+ let invalidJsonRows = 0;
2237
+ let unknownSchemaRows = 0;
2238
+ let firstInvalidJsonLine;
2239
+ let firstUnknownSchemaLine;
2240
+ let lineNumber = 0;
2241
+ for (const line of content.split(/\r?\n/)) {
2242
+ lineNumber += 1;
2243
+ const trimmed = line.trim();
2244
+ if (trimmed === "") continue;
2245
+ let parsed;
2246
+ try {
2247
+ parsed = JSON.parse(trimmed);
2248
+ } catch {
2249
+ invalidJsonRows += 1;
2250
+ firstInvalidJsonLine ??= lineNumber;
2251
+ continue;
2252
+ }
2253
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "schemaVersion" in parsed) {
2254
+ const version = parsed.schemaVersion;
2255
+ if (version === "0.1") {
2256
+ saw01 = true;
2257
+ validRows += 1;
2258
+ continue;
2259
+ }
2260
+ if (version === "0.2") {
2261
+ saw02 = true;
2262
+ validRows += 1;
2263
+ continue;
2264
+ }
2265
+ if (version === "1.0") {
2266
+ saw10 = true;
2267
+ validRows += 1;
2268
+ continue;
2269
+ }
2270
+ }
2271
+ unknownSchemaRows += 1;
2272
+ firstUnknownSchemaLine ??= lineNumber;
2273
+ }
2274
+ const warnings = [];
2275
+ if (invalidJsonRows > 0) {
2276
+ warnings.push({
2277
+ code: "invalid_jsonl_rows",
2278
+ message: `Skipped ${invalidJsonRows} invalid JSONL row(s) during format detection.`,
2279
+ severity: "warning",
2280
+ ...firstInvalidJsonLine !== void 0 ? { line: firstInvalidJsonLine } : {}
2281
+ });
2282
+ }
2283
+ if (unknownSchemaRows > 0) {
2284
+ warnings.push({
2285
+ code: "unknown_schema_rows",
2286
+ message: `Skipped ${unknownSchemaRows} row(s) with unknown schemaVersion during format detection.`,
2287
+ severity: "warning",
2288
+ ...firstUnknownSchemaLine !== void 0 ? { line: firstUnknownSchemaLine } : {}
2289
+ });
2290
+ }
2291
+ let format = "empty";
2292
+ const seenFormats = [saw01, saw02, saw10].filter(Boolean).length;
2293
+ if (seenFormats > 1) format = "mixed";
2294
+ else if (saw01) format = "0.1";
2295
+ else if (saw02) format = "0.2";
2296
+ else if (saw10) format = "1.0";
2297
+ return { format, validRows, warnings };
2298
+ }
2299
+ function agentInspectFormatLabel(format) {
2300
+ switch (format) {
2301
+ case "0.1":
2302
+ return "agent-inspect-v0.1-jsonl";
2303
+ case "0.2":
2304
+ return "agent-inspect-v0.2-jsonl";
2305
+ case "1.0":
2306
+ return "agent-inspect-v1.0-jsonl";
2307
+ case "mixed":
2308
+ return "agent-inspect-mixed-jsonl";
2309
+ default:
2310
+ return "agent-inspect-jsonl";
2311
+ }
2312
+ }
2313
+ function persistedEventsForParsedTrace(parsed) {
2314
+ if ((parsed.format === "0.2" || parsed.format === "1.0") && parsed.persisted.length > 0) {
2315
+ return [...parsed.persisted];
2316
+ }
2317
+ if (parsed.format === "mixed" && parsed.rows.length > 0) {
2318
+ return parsed.rows.map((row, index) => {
2319
+ if (row.format === "0.2" || row.format === "1.0") return row.event;
2320
+ return traceEventToPersistedInspectEvent(row.event, {
2321
+ eventIndex: index,
2322
+ sourceName: "agent-inspect-jsonl-reader"
2323
+ });
2324
+ });
2325
+ }
2326
+ return traceEventsToPersistedInspectEvents(parsed.events, {
2327
+ sourceName: "agent-inspect-jsonl-reader"
2328
+ });
2329
+ }
2330
+ function isRecord6(value) {
2331
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2332
+ }
2333
+ function isNonEmptyString4(value) {
2334
+ return typeof value === "string" && value.trim() !== "";
2335
+ }
2336
+ function readStringField(record, keys) {
2337
+ for (const key of keys) {
2338
+ const value = record[key];
2339
+ if (isNonEmptyString4(value)) return value;
2340
+ }
2341
+ return void 0;
2342
+ }
2343
+ function readRecordField(record, key) {
2344
+ const value = record[key];
2345
+ return isRecord6(value) ? value : void 0;
2346
+ }
2347
+ function parseJsonDocument(content) {
2348
+ return JSON.parse(content);
2349
+ }
2350
+ function looksLikeOpenInferenceSpan(value) {
2351
+ if (!isRecord6(value)) return false;
2352
+ const attributes = readRecordField(value, "attributes");
2353
+ 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);
2354
+ }
2355
+ function extractOpenInferenceDocument(root) {
2356
+ const warnings = [];
2357
+ const unsupportedFields = [];
2358
+ if (Array.isArray(root)) {
2359
+ const spans = root.filter(looksLikeOpenInferenceSpan);
2360
+ if (spans.length === 0) return void 0;
2361
+ if (spans.length !== root.length) {
2362
+ warnings.push({
2363
+ code: "openinference_skipped_items",
2364
+ message: "Skipped non-span item(s) in OpenInference span array.",
2365
+ severity: "warning"
2366
+ });
2367
+ }
2368
+ return {
2369
+ spans,
2370
+ confidence: 0.82,
2371
+ description: "OpenInference span array",
2372
+ warnings,
2373
+ unsupportedFields
2374
+ };
2375
+ }
2376
+ if (!isRecord6(root)) return void 0;
2377
+ const rootFormat = root.format;
2378
+ const rootCompatibility = root.compatibility;
2379
+ const version = typeof root.version === "string" && root.version.trim() !== "" ? root.version : void 0;
2380
+ if (Array.isArray(root.spans)) {
2381
+ const spans = root.spans.filter(looksLikeOpenInferenceSpan);
2382
+ if (spans.length === 0 && (rootFormat === "openinference" || rootCompatibility === "openinference-compatible")) {
2383
+ warnings.push({
2384
+ code: "openinference_no_valid_spans",
2385
+ message: "OpenInference document did not contain any valid spans.",
2386
+ severity: "error"
2387
+ });
2388
+ return {
2389
+ spans,
2390
+ confidence: 0.7,
2391
+ description: "Malformed OpenInference document",
2392
+ version,
2393
+ warnings,
2394
+ unsupportedFields
2395
+ };
2396
+ }
2397
+ if (spans.length === 0) return void 0;
2398
+ if (spans.length !== root.spans.length) {
2399
+ warnings.push({
2400
+ code: "openinference_skipped_spans",
2401
+ message: "Skipped invalid OpenInference span item(s).",
2402
+ severity: "warning"
2403
+ });
2404
+ }
2405
+ return {
2406
+ spans,
2407
+ confidence: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? 0.9 : 0.84,
2408
+ description: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? "OpenInference document" : "OpenInference spans document",
2409
+ version,
2410
+ warnings,
2411
+ unsupportedFields
2412
+ };
2413
+ }
2414
+ if (Array.isArray(root.data)) {
2415
+ const spans = root.data.filter(looksLikeOpenInferenceSpan);
2416
+ if (spans.length === 0) return void 0;
2417
+ if (spans.length !== root.data.length) {
2418
+ warnings.push({
2419
+ code: "openinference_skipped_data_items",
2420
+ message: "Skipped non-span item(s) in OpenInference data array.",
2421
+ severity: "warning"
2422
+ });
2423
+ }
2424
+ return {
2425
+ spans,
2426
+ confidence: 0.8,
2427
+ description: "OpenInference data document",
2428
+ version,
2429
+ warnings,
2430
+ unsupportedFields
2431
+ };
2432
+ }
2433
+ if (looksLikeOpenInferenceSpan(root)) {
2434
+ return {
2435
+ spans: [root],
2436
+ confidence: 0.76,
2437
+ description: "OpenInference single span",
2438
+ version,
2439
+ warnings,
2440
+ unsupportedFields
2441
+ };
2442
+ }
2443
+ if (rootFormat === "openinference" || rootCompatibility === "openinference-compatible") {
2444
+ warnings.push({
2445
+ code: "openinference_missing_spans",
2446
+ message: "OpenInference document is missing a spans array.",
2447
+ severity: "error"
2448
+ });
2449
+ return {
2450
+ spans: [],
2451
+ confidence: 0.7,
2452
+ description: "Malformed OpenInference document",
2453
+ version,
2454
+ warnings,
2455
+ unsupportedFields
2456
+ };
2457
+ }
2458
+ return void 0;
2459
+ }
2460
+ function parseUnixNanoToIso(value) {
2461
+ if (typeof value === "bigint" && value >= 0n) {
2462
+ return new Date(Number(value / 1000000n)).toISOString();
2463
+ }
2464
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
2465
+ return new Date(Math.floor(value / 1e6)).toISOString();
2466
+ }
2467
+ if (typeof value === "string" && /^\d+$/.test(value)) {
2468
+ return new Date(Number(BigInt(value) / 1000000n)).toISOString();
2469
+ }
2470
+ return void 0;
2471
+ }
2472
+ function parseIsoTime(value) {
2473
+ if (!isNonEmptyString4(value)) return void 0;
2474
+ const ms = Date.parse(value);
2475
+ if (!Number.isFinite(ms)) return void 0;
2476
+ return new Date(ms).toISOString();
2477
+ }
2478
+ function readOpenInferenceTimestamp(span, nanoKeys, isoKeys) {
2479
+ for (const key of nanoKeys) {
2480
+ const iso = parseUnixNanoToIso(span[key]);
2481
+ if (iso !== void 0) return iso;
2482
+ }
2483
+ for (const key of isoKeys) {
2484
+ const iso = parseIsoTime(span[key]);
2485
+ if (iso !== void 0) return iso;
2486
+ }
2487
+ return void 0;
2488
+ }
2489
+ function durationBetweenIso(startedAt, endedAt) {
2490
+ if (startedAt === void 0 || endedAt === void 0) return void 0;
2491
+ const startMs = Date.parse(startedAt);
2492
+ const endMs = Date.parse(endedAt);
2493
+ if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs < startMs) {
2494
+ return void 0;
2495
+ }
2496
+ return endMs - startMs;
2497
+ }
2498
+ function isSensitiveOpenInferenceAttribute(key) {
2499
+ return OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS.some(
2500
+ (sensitiveKey) => key === sensitiveKey || key.startsWith(`${sensitiveKey}.`) || key.endsWith(".message.content") || key.endsWith(".document.content")
2501
+ );
2502
+ }
2503
+ function summarizeAttributeValue(value) {
2504
+ if (typeof value === "string") {
2505
+ return { type: "string", length: value.length };
2506
+ }
2507
+ if (typeof value === "number") {
2508
+ return { type: "number", finite: Number.isFinite(value) };
2509
+ }
2510
+ if (typeof value === "boolean") {
2511
+ return { type: "boolean" };
2512
+ }
2513
+ if (Array.isArray(value)) {
2514
+ return { type: "array", length: value.length };
2515
+ }
2516
+ if (isRecord6(value)) {
2517
+ return { type: "object", keyCount: Object.keys(value).length };
2518
+ }
2519
+ if (value === null) {
2520
+ return { type: "null" };
2521
+ }
2522
+ return { type: typeof value };
2523
+ }
2524
+ function sanitizeOpenInferenceAttributes(attributes, pathPrefix) {
2525
+ const out = {};
2526
+ const warnings = [];
2527
+ const unsupportedFields = [];
2528
+ const summarizedKeys = [];
2529
+ for (const [key, value] of Object.entries(attributes)) {
2530
+ if (isSensitiveOpenInferenceAttribute(key)) {
2531
+ summarizedKeys.push(key);
2532
+ out[`${key}.summary`] = summarizeAttributeValue(value);
2533
+ unsupportedFields.push(`${pathPrefix}.attributes.${key}`);
2534
+ continue;
2535
+ }
2536
+ out[key] = value;
2537
+ }
2538
+ if (summarizedKeys.length > 0) {
2539
+ out["openinference.summarized_attributes"] = summarizedKeys;
2540
+ warnings.push({
2541
+ code: "openinference_sensitive_attribute_summarized",
2542
+ message: "OpenInference prompt/output/document attribute(s) were summarized instead of copied verbatim.",
2543
+ severity: "warning"
2544
+ });
2545
+ }
2546
+ return { attributes: out, warnings, unsupportedFields };
2547
+ }
2548
+ function mapOpenInferenceKind(span, attributes, pathPrefix) {
2549
+ const warnings = [];
2550
+ const agentInspectKind = attributes["agent_inspect.kind"];
2551
+ if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
2552
+ return { kind: agentInspectKind, warnings };
2553
+ }
2554
+ const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
2555
+ const normalized = rawKind?.toUpperCase();
2556
+ switch (normalized) {
2557
+ case "LLM":
2558
+ return { kind: "LLM", warnings };
2559
+ case "TOOL":
2560
+ return { kind: "TOOL", warnings };
2561
+ case "CHAIN":
2562
+ return { kind: "CHAIN", warnings };
2563
+ case "RETRIEVER":
2564
+ return { kind: "RETRIEVER", warnings };
2565
+ case "AGENT":
2566
+ return { kind: "AGENT", warnings };
2567
+ case "EMBEDDING":
2568
+ warnings.push({
2569
+ code: "openinference_kind_semantic_loss",
2570
+ message: "OpenInference EMBEDDING span kind mapped to AgentInspect LLM.",
2571
+ severity: "warning",
2572
+ field: `${pathPrefix}.attributes.openinference.span.kind`
2573
+ });
2574
+ return { kind: "LLM", warnings };
2575
+ case "RERANKER":
2576
+ warnings.push({
2577
+ code: "openinference_kind_semantic_loss",
2578
+ message: "OpenInference RERANKER span kind mapped to AgentInspect RETRIEVER.",
2579
+ severity: "warning",
2580
+ field: `${pathPrefix}.attributes.openinference.span.kind`
2581
+ });
2582
+ return { kind: "RETRIEVER", warnings };
2583
+ case "UNKNOWN":
2584
+ case void 0:
2585
+ warnings.push({
2586
+ code: "openinference_kind_unknown",
2587
+ message: "OpenInference span kind was missing or unknown; mapped to AgentInspect LOGIC.",
2588
+ severity: "warning",
2589
+ field: `${pathPrefix}.attributes.openinference.span.kind`
2590
+ });
2591
+ return { kind: "LOGIC", warnings };
2592
+ default:
2593
+ warnings.push({
2594
+ code: "openinference_kind_unsupported",
2595
+ message: `Unsupported OpenInference span kind "${rawKind}" mapped to AgentInspect LOGIC.`,
2596
+ severity: "warning",
2597
+ field: `${pathPrefix}.attributes.openinference.span.kind`
2598
+ });
2599
+ return { kind: "LOGIC", warnings };
2600
+ }
2601
+ }
2602
+ function mapOpenInferenceStatus(status) {
2603
+ if (!isRecord6(status)) return void 0;
2604
+ const rawCode = status.code;
2605
+ if (typeof rawCode !== "string") return void 0;
2606
+ switch (rawCode.toUpperCase()) {
2607
+ case "OK":
2608
+ return "ok";
2609
+ case "ERROR":
2610
+ return "error";
2611
+ case "UNSET":
2612
+ return "unknown";
2613
+ default:
2614
+ return "unknown";
2615
+ }
2616
+ }
2617
+ function readOpenInferenceTokenUsage(attributes) {
2618
+ const prompt = attributes["llm.token_count.prompt"];
2619
+ const completion = attributes["llm.token_count.completion"];
2620
+ const total = attributes["llm.token_count.total"];
2621
+ const cached = attributes["llm.token_count.prompt_details.cache_read"];
2622
+ const usage = {};
2623
+ if (typeof prompt === "number" && Number.isFinite(prompt) && prompt >= 0) {
2624
+ usage.input = prompt;
2625
+ }
2626
+ if (typeof completion === "number" && Number.isFinite(completion) && completion >= 0) {
2627
+ usage.output = completion;
2628
+ }
2629
+ if (typeof total === "number" && Number.isFinite(total) && total >= 0) {
2630
+ usage.total = total;
2631
+ }
2632
+ if (typeof cached === "number" && Number.isFinite(cached) && cached >= 0) {
2633
+ usage.cached = cached;
2634
+ }
2635
+ if (usage.total === void 0 && usage.input !== void 0 && usage.output !== void 0) {
2636
+ usage.total = usage.input + usage.output;
2637
+ }
2638
+ return Object.keys(usage).length > 0 ? usage : void 0;
2639
+ }
2640
+ function readOpenInferenceConfidence(attributes) {
2641
+ const confidence = attributes["agent_inspect.confidence"];
2642
+ if (confidence === "explicit" || confidence === "correlated" || confidence === "heuristic" || confidence === "unknown") {
2643
+ return confidence;
2644
+ }
2645
+ return "correlated";
2646
+ }
2647
+ function mapOpenInferenceSpan(span, index, version) {
2648
+ const pathPrefix = `spans[${index}]`;
2649
+ const warnings = [];
2650
+ const unsupportedFields = [];
2651
+ const rawAttributes = readRecordField(span, "attributes") ?? {};
2652
+ const sanitized = sanitizeOpenInferenceAttributes(rawAttributes, pathPrefix);
2653
+ warnings.push(...sanitized.warnings);
2654
+ unsupportedFields.push(...sanitized.unsupportedFields);
2655
+ const attributes = { ...sanitized.attributes };
2656
+ for (const [key, value] of Object.entries(span)) {
2657
+ if (OPENINFERENCE_SPAN_KEYS.has(key)) continue;
2658
+ unsupportedFields.push(`${pathPrefix}.${key}`);
2659
+ if (value === null || typeof value !== "object") {
2660
+ attributes[`openinference.${key}`] = value;
2661
+ } else {
2662
+ attributes[`openinference.${key}.summary`] = summarizeAttributeValue(value);
2663
+ warnings.push({
2664
+ code: "openinference_unsupported_field_summarized",
2665
+ message: `Unsupported OpenInference span field "${key}" was summarized.`,
2666
+ severity: "warning",
2667
+ field: `${pathPrefix}.${key}`
2668
+ });
2669
+ }
2670
+ }
2671
+ const traceId = readStringField(span, ["trace_id", "traceId"]) ?? `trace-${index}`;
2672
+ const spanId = readStringField(span, ["span_id", "spanId"]) ?? `span-${index}`;
2673
+ const parentSpanId = readStringField(span, ["parent_span_id", "parentSpanId"]);
2674
+ const name = readStringField(span, ["name"]) ?? spanId;
2675
+ const startedAt = readOpenInferenceTimestamp(
2676
+ span,
2677
+ ["start_time_unix_nano", "startTimeUnixNano"],
2678
+ ["start_time", "startTime"]
2679
+ );
2680
+ const endedAt = readOpenInferenceTimestamp(
2681
+ span,
2682
+ ["end_time_unix_nano", "endTimeUnixNano"],
2683
+ ["end_time", "endTime"]
2684
+ );
2685
+ const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
2686
+ if (startedAt === void 0) {
2687
+ warnings.push({
2688
+ code: "openinference_missing_start_time",
2689
+ message: "OpenInference span is missing a valid start time; using Unix epoch.",
2690
+ severity: "warning",
2691
+ field: `${pathPrefix}.start_time_unix_nano`
2692
+ });
2693
+ unsupportedFields.push(`${pathPrefix}.start_time_unix_nano`);
2694
+ }
2695
+ const { kind, warnings: kindWarnings } = mapOpenInferenceKind(
2696
+ span,
2697
+ rawAttributes,
2698
+ pathPrefix
2699
+ );
2700
+ warnings.push(...kindWarnings);
2701
+ const status = mapOpenInferenceStatus(span.status);
2702
+ const tokenUsage = readOpenInferenceTokenUsage(rawAttributes);
2703
+ const errorMessage = isRecord6(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
2704
+ const event = {
2705
+ schemaVersion: "0.2",
2706
+ eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
2707
+ runId: typeof rawAttributes["agent_inspect.run_id"] === "string" ? rawAttributes["agent_inspect.run_id"] : traceId,
2708
+ kind,
2709
+ name,
2710
+ timestamp,
2711
+ confidence: readOpenInferenceConfidence(rawAttributes),
2712
+ source: {
2713
+ type: "otel",
2714
+ name: "openinference",
2715
+ ...version !== void 0 ? { version } : {}
2716
+ },
2717
+ attributes,
2718
+ trace: {
2719
+ traceId,
2720
+ spanId,
2721
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
2722
+ }
2723
+ };
2724
+ if (status !== void 0) {
2725
+ event.status = status;
2726
+ }
2727
+ if (startedAt !== void 0) {
2728
+ event.startedAt = startedAt;
2729
+ }
2730
+ if (endedAt !== void 0) {
2731
+ event.endedAt = endedAt;
2732
+ }
2733
+ const durationMs = durationBetweenIso(startedAt, endedAt);
2734
+ if (durationMs !== void 0) {
2735
+ event.durationMs = durationMs;
2736
+ }
2737
+ if (tokenUsage !== void 0) {
2738
+ event.tokenUsage = tokenUsage;
2739
+ }
2740
+ if (status === "error") {
2741
+ event.error = {
2742
+ message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OpenInference span error"
2743
+ };
2744
+ }
2745
+ return {
2746
+ event,
2747
+ warnings,
2748
+ unsupportedFields,
2749
+ spanId,
2750
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
2751
+ };
2752
+ }
2753
+ function mapOpenInferenceEvents(document) {
2754
+ const mapped = document.spans.map(
2755
+ (span, index) => mapOpenInferenceSpan(span, index, document.version)
2756
+ );
2757
+ const spanIdToEventId = new Map(
2758
+ mapped.map((span) => [span.spanId, span.event.eventId])
2759
+ );
2760
+ for (const span of mapped) {
2761
+ if (span.parentSpanId === void 0) continue;
2762
+ span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
2763
+ }
2764
+ return {
2765
+ events: mapped.map((span) => span.event),
2766
+ warnings: mapped.flatMap((span) => span.warnings),
2767
+ unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
2768
+ };
2769
+ }
2770
+ var openInferenceJsonReader = {
2771
+ format: OPENINFERENCE_READER_FORMAT,
2772
+ name: "OpenInference JSON",
2773
+ async detect(input) {
2774
+ const resolved = await resolveInput(input);
2775
+ if (!resolved) return void 0;
2776
+ let parsed;
2777
+ try {
2778
+ parsed = parseJsonDocument(resolved.content);
2779
+ } catch {
2780
+ return void 0;
2781
+ }
2782
+ const document = extractOpenInferenceDocument(parsed);
2783
+ if (!document) return void 0;
2784
+ return {
2785
+ format: OPENINFERENCE_READER_FORMAT,
2786
+ confidence: document.confidence,
2787
+ readerName: "OpenInference JSON",
2788
+ description: document.description,
2789
+ warnings: attachSingleSourceFile(document.warnings, resolved)
2790
+ };
2791
+ },
2792
+ async read(input) {
2793
+ const resolved = await resolveInput(input);
2794
+ if (!resolved) {
2795
+ throw new TraceReadError(
2796
+ "unsupported_format",
2797
+ "OpenInference JSON reader requires file, string, or buffer input."
2798
+ );
2799
+ }
2800
+ let parsed;
2801
+ try {
2802
+ parsed = parseJsonDocument(resolved.content);
2803
+ } catch {
2804
+ throw new TraceReadError("unsupported_format", "OpenInference JSON input is not valid JSON.", [
2805
+ {
2806
+ code: "openinference_invalid_json",
2807
+ message: "OpenInference JSON reader could not parse the input as JSON.",
2808
+ severity: "error"
2809
+ }
2810
+ ]);
2811
+ }
2812
+ const document = extractOpenInferenceDocument(parsed);
2813
+ if (!document || document.spans.length === 0) {
2814
+ throw new TraceReadError(
2815
+ "unsupported_format",
2816
+ "No valid OpenInference spans found.",
2817
+ attachSingleSourceFile(
2818
+ document?.warnings ?? [
2819
+ {
2820
+ code: "openinference_no_valid_spans",
2821
+ message: "OpenInference JSON input did not contain valid spans.",
2822
+ severity: "error"
2823
+ }
2824
+ ],
2825
+ resolved
2826
+ )
2827
+ );
2828
+ }
2829
+ const mapped = mapOpenInferenceEvents(document);
2830
+ const warnings = attachSingleSourceFile(
2831
+ [...document.warnings, ...mapped.warnings],
2832
+ resolved
2833
+ );
2834
+ const unsupportedFields = [
2835
+ ...document.unsupportedFields,
2836
+ ...mapped.unsupportedFields
2837
+ ].sort((a, b) => a.localeCompare(b));
2838
+ return {
2839
+ format: OPENINFERENCE_READER_FORMAT,
2840
+ events: mapped.events,
2841
+ runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
2842
+ warnings,
2843
+ unsupportedFields,
2844
+ sourceFiles: resolved.sourceFiles
2845
+ };
2846
+ }
2847
+ };
2848
+ function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
2849
+ if (!isRecord6(value)) {
2850
+ unsupportedFields.push(field);
2851
+ warnings.push({
2852
+ code: "otlp_attribute_value_invalid",
2853
+ message: "OTLP attribute value was not an AnyValue object.",
2854
+ severity: "warning",
2855
+ field
2856
+ });
2857
+ return void 0;
2858
+ }
2859
+ if (typeof value.stringValue === "string") return value.stringValue;
2860
+ if (typeof value.boolValue === "boolean") return value.boolValue;
2861
+ if (typeof value.intValue === "number" && Number.isFinite(value.intValue)) {
2862
+ return value.intValue;
2863
+ }
2864
+ if (typeof value.intValue === "string" && value.intValue.trim() !== "") {
2865
+ const n = Number(value.intValue);
2866
+ if (Number.isFinite(n)) return n;
2867
+ }
2868
+ if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
2869
+ return value.doubleValue;
2870
+ }
2871
+ if (isRecord6(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
2872
+ return value.arrayValue.values.map(
2873
+ (item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
2874
+ );
2875
+ }
2876
+ if (isRecord6(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
2877
+ const out = {};
2878
+ for (const [index, item] of value.kvlistValue.values.entries()) {
2879
+ if (!isRecord6(item) || typeof item.key !== "string") {
2880
+ unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
2881
+ continue;
2882
+ }
2883
+ out[item.key] = parseOtlpAnyValue(
2884
+ item.value,
2885
+ `${field}.kvlistValue.values[${index}].value`,
2886
+ warnings,
2887
+ unsupportedFields
2888
+ );
2889
+ }
2890
+ return out;
2891
+ }
2892
+ if (typeof value.bytesValue === "string") {
2893
+ unsupportedFields.push(field);
2894
+ warnings.push({
2895
+ code: "otlp_bytes_value_summarized",
2896
+ message: "OTLP bytesValue attribute was summarized instead of decoded.",
2897
+ severity: "warning",
2898
+ field
2899
+ });
2900
+ return { type: "bytes", length: value.bytesValue.length };
2901
+ }
2902
+ unsupportedFields.push(field);
2903
+ warnings.push({
2904
+ code: "otlp_attribute_value_unsupported",
2905
+ message: "OTLP attribute value used an unsupported AnyValue shape.",
2906
+ severity: "warning",
2907
+ field
2908
+ });
2909
+ return void 0;
2910
+ }
2911
+ function parseOtlpAttributes(value, pathPrefix) {
2912
+ const attributes = {};
2913
+ const warnings = [];
2914
+ const unsupportedFields = [];
2915
+ if (value === void 0) {
2916
+ return { attributes, warnings, unsupportedFields };
2917
+ }
2918
+ if (!Array.isArray(value)) {
2919
+ unsupportedFields.push(pathPrefix);
2920
+ warnings.push({
2921
+ code: "otlp_attributes_invalid",
2922
+ message: "OTLP attributes field was not an array.",
2923
+ severity: "warning",
2924
+ field: pathPrefix
2925
+ });
2926
+ return { attributes, warnings, unsupportedFields };
2927
+ }
2928
+ for (const [index, item] of value.entries()) {
2929
+ const field = `${pathPrefix}[${index}]`;
2930
+ if (!isRecord6(item) || typeof item.key !== "string") {
2931
+ unsupportedFields.push(field);
2932
+ warnings.push({
2933
+ code: "otlp_attribute_invalid",
2934
+ message: "Skipped OTLP attribute without a string key.",
2935
+ severity: "warning",
2936
+ field
2937
+ });
2938
+ continue;
2939
+ }
2940
+ const parsed = parseOtlpAnyValue(
2941
+ item.value,
2942
+ `${field}.value`,
2943
+ warnings,
2944
+ unsupportedFields
2945
+ );
2946
+ if (parsed !== void 0) {
2947
+ attributes[item.key] = parsed;
2948
+ }
2949
+ }
2950
+ return { attributes, warnings, unsupportedFields };
2951
+ }
2952
+ function looksLikeOtlpSpan(value) {
2953
+ return isRecord6(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
2954
+ }
2955
+ function extractOtlpDocument(root) {
2956
+ if (!isRecord6(root) || !Array.isArray(root.resourceSpans)) return void 0;
2957
+ const spans = [];
2958
+ const warnings = [];
2959
+ const unsupportedFields = [];
2960
+ for (const [resourceIndex, resourceSpan] of root.resourceSpans.entries()) {
2961
+ const resourcePath = `resourceSpans[${resourceIndex}]`;
2962
+ if (!isRecord6(resourceSpan)) {
2963
+ unsupportedFields.push(resourcePath);
2964
+ continue;
2965
+ }
2966
+ const resource = readRecordField(resourceSpan, "resource");
2967
+ const resourceParsed = parseOtlpAttributes(
2968
+ resource?.attributes,
2969
+ `${resourcePath}.resource.attributes`
2970
+ );
2971
+ warnings.push(...resourceParsed.warnings);
2972
+ unsupportedFields.push(...resourceParsed.unsupportedFields);
2973
+ if (!Array.isArray(resourceSpan.scopeSpans)) {
2974
+ unsupportedFields.push(`${resourcePath}.scopeSpans`);
2975
+ warnings.push({
2976
+ code: "otlp_scope_spans_missing",
2977
+ message: "OTLP resourceSpans entry did not contain a scopeSpans array.",
2978
+ severity: "warning",
2979
+ field: `${resourcePath}.scopeSpans`
2980
+ });
2981
+ continue;
2982
+ }
2983
+ for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
2984
+ const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
2985
+ if (!isRecord6(scopeSpan)) {
2986
+ unsupportedFields.push(scopePath);
2987
+ continue;
2988
+ }
2989
+ const scope = readRecordField(scopeSpan, "scope");
2990
+ const scopeParsed = parseOtlpAttributes(
2991
+ scope?.attributes,
2992
+ `${scopePath}.scope.attributes`
2993
+ );
2994
+ warnings.push(...scopeParsed.warnings);
2995
+ unsupportedFields.push(...scopeParsed.unsupportedFields);
2996
+ if (!Array.isArray(scopeSpan.spans)) {
2997
+ unsupportedFields.push(`${scopePath}.spans`);
2998
+ warnings.push({
2999
+ code: "otlp_spans_missing",
3000
+ message: "OTLP scopeSpans entry did not contain a spans array.",
3001
+ severity: "warning",
3002
+ field: `${scopePath}.spans`
3003
+ });
3004
+ continue;
3005
+ }
3006
+ for (const [spanIndex, span] of scopeSpan.spans.entries()) {
3007
+ const spanPath = `${scopePath}.spans[${spanIndex}]`;
3008
+ if (!looksLikeOtlpSpan(span)) {
3009
+ unsupportedFields.push(spanPath);
3010
+ warnings.push({
3011
+ code: "otlp_invalid_span",
3012
+ message: "Skipped OTLP span without required traceId, spanId, or name.",
3013
+ severity: "warning",
3014
+ field: spanPath
3015
+ });
3016
+ continue;
3017
+ }
3018
+ spans.push({
3019
+ span,
3020
+ resourceAttributes: resourceParsed.attributes,
3021
+ scopeAttributes: scopeParsed.attributes,
3022
+ scopeName: readStringField(scope ?? {}, ["name"]),
3023
+ scopeVersion: readStringField(scope ?? {}, ["version"]),
3024
+ pathPrefix: spanPath
3025
+ });
3026
+ }
3027
+ }
3028
+ }
3029
+ if (spans.length === 0) {
3030
+ warnings.push({
3031
+ code: "otlp_no_valid_spans",
3032
+ message: "OTLP JSON payload did not contain any valid spans.",
3033
+ severity: "error"
3034
+ });
3035
+ return {
3036
+ spans,
3037
+ confidence: 0.7,
3038
+ description: "Malformed OTLP JSON trace payload",
3039
+ warnings,
3040
+ unsupportedFields
3041
+ };
3042
+ }
3043
+ return {
3044
+ spans,
3045
+ confidence: 0.93,
3046
+ description: "OTLP JSON trace payload",
3047
+ warnings,
3048
+ unsupportedFields
3049
+ };
3050
+ }
3051
+ function mapOtlpStatus(status) {
3052
+ if (!isRecord6(status)) return void 0;
3053
+ const rawCode = status.code;
3054
+ if (typeof rawCode !== "string") return void 0;
3055
+ switch (rawCode.toUpperCase()) {
3056
+ case "STATUS_CODE_OK":
3057
+ case "OK":
3058
+ return "ok";
3059
+ case "STATUS_CODE_ERROR":
3060
+ case "ERROR":
3061
+ return "error";
3062
+ case "STATUS_CODE_UNSET":
3063
+ case "UNSET":
3064
+ return "unknown";
3065
+ default:
3066
+ return "unknown";
3067
+ }
3068
+ }
3069
+ function readOtlpKind(attributes, pathPrefix) {
3070
+ const warnings = [];
3071
+ const agentInspectKind = attributes["agent_inspect.kind"];
3072
+ if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
3073
+ return { kind: agentInspectKind, warnings };
3074
+ }
3075
+ const operation = attributes["gen_ai.operation.name"];
3076
+ if (typeof operation === "string") {
3077
+ switch (operation) {
3078
+ case "generate_content":
3079
+ case "chat":
3080
+ return { kind: "LLM", warnings };
3081
+ case "execute_tool":
3082
+ return { kind: "TOOL", warnings };
3083
+ case "invoke_agent":
3084
+ return { kind: "AGENT", warnings };
3085
+ default:
3086
+ warnings.push({
3087
+ code: "otlp_gen_ai_operation_semantic_loss",
3088
+ message: `OTLP GenAI operation "${operation}" mapped to AgentInspect LOGIC.`,
3089
+ severity: "warning",
3090
+ field: `${pathPrefix}.attributes.gen_ai.operation.name`
3091
+ });
3092
+ return { kind: "LOGIC", warnings };
3093
+ }
3094
+ }
3095
+ warnings.push({
3096
+ code: "otlp_kind_unknown",
3097
+ message: "OTLP span had no AgentInspect kind or GenAI operation; mapped to LOGIC.",
3098
+ severity: "warning",
3099
+ field: `${pathPrefix}.attributes`
3100
+ });
3101
+ return { kind: "LOGIC", warnings };
3102
+ }
3103
+ function readOtlpTokenUsage(attributes) {
3104
+ const input = attributes["gen_ai.usage.input_tokens"];
3105
+ const output = attributes["gen_ai.usage.output_tokens"];
3106
+ const usage = {};
3107
+ if (typeof input === "number" && Number.isFinite(input) && input >= 0) {
3108
+ usage.input = input;
3109
+ }
3110
+ if (typeof output === "number" && Number.isFinite(output) && output >= 0) {
3111
+ usage.output = output;
3112
+ }
3113
+ if (usage.input !== void 0 && usage.output !== void 0) {
3114
+ usage.total = usage.input + usage.output;
3115
+ }
3116
+ return Object.keys(usage).length > 0 ? usage : void 0;
3117
+ }
3118
+ function readOtlpConfidence(attributes) {
3119
+ return readOpenInferenceConfidence(attributes);
3120
+ }
3121
+ function sanitizeOtlpAttributes(attributes, pathPrefix) {
3122
+ const ownerPath = pathPrefix.endsWith(".attributes") ? pathPrefix.slice(0, -".attributes".length) : pathPrefix;
3123
+ const sanitized = sanitizeOpenInferenceAttributes(attributes, ownerPath);
3124
+ return {
3125
+ ...sanitized,
3126
+ warnings: sanitized.warnings.map(
3127
+ (warning) => warning.code === "openinference_sensitive_attribute_summarized" ? {
3128
+ ...warning,
3129
+ code: "otlp_sensitive_attribute_summarized",
3130
+ message: "OTLP prompt/output/document attribute(s) were summarized instead of copied verbatim."
3131
+ } : warning
3132
+ )
3133
+ };
3134
+ }
3135
+ function mapOtlpEvents(value, pathPrefix) {
3136
+ const warnings = [];
3137
+ const unsupportedFields = [];
3138
+ if (value === void 0) return { warnings, unsupportedFields };
3139
+ if (!Array.isArray(value)) {
3140
+ unsupportedFields.push(pathPrefix);
3141
+ warnings.push({
3142
+ code: "otlp_events_invalid",
3143
+ message: "OTLP events field was not an array.",
3144
+ severity: "warning",
3145
+ field: pathPrefix
3146
+ });
3147
+ return { warnings, unsupportedFields };
3148
+ }
3149
+ const events = [];
3150
+ for (const [index, event] of value.entries()) {
3151
+ const eventPath = `${pathPrefix}[${index}]`;
3152
+ if (!isRecord6(event)) {
3153
+ unsupportedFields.push(eventPath);
3154
+ continue;
3155
+ }
3156
+ const parsedAttributes = parseOtlpAttributes(
3157
+ event.attributes,
3158
+ `${eventPath}.attributes`
3159
+ );
3160
+ warnings.push(...parsedAttributes.warnings);
3161
+ unsupportedFields.push(...parsedAttributes.unsupportedFields);
3162
+ const sanitized = sanitizeOtlpAttributes(
3163
+ parsedAttributes.attributes,
3164
+ `${eventPath}.attributes`
3165
+ );
3166
+ warnings.push(...sanitized.warnings);
3167
+ unsupportedFields.push(...sanitized.unsupportedFields);
3168
+ const out = {};
3169
+ const name = readStringField(event, ["name"]);
3170
+ if (name !== void 0) {
3171
+ out.name = name;
3172
+ }
3173
+ const timestamp = parseUnixNanoToIso(event.timeUnixNano);
3174
+ if (timestamp !== void 0) {
3175
+ out.timestamp = timestamp;
3176
+ } else if (event.timeUnixNano !== void 0) {
3177
+ unsupportedFields.push(`${eventPath}.timeUnixNano`);
3178
+ warnings.push({
3179
+ code: "otlp_event_timestamp_invalid",
3180
+ message: "OTLP event timeUnixNano could not be parsed.",
3181
+ severity: "warning",
3182
+ field: `${eventPath}.timeUnixNano`
3183
+ });
3184
+ }
3185
+ if (Object.keys(sanitized.attributes).length > 0) {
3186
+ out.attributes = sanitized.attributes;
3187
+ }
3188
+ events.push(out);
3189
+ }
3190
+ return {
3191
+ events: events.length > 0 ? events : void 0,
3192
+ warnings,
3193
+ unsupportedFields
3194
+ };
3195
+ }
3196
+ function mapOtlpSpan(context) {
3197
+ const { span, pathPrefix } = context;
3198
+ const warnings = [];
3199
+ const unsupportedFields = [];
3200
+ const parsedSpanAttributes = parseOtlpAttributes(
3201
+ span.attributes,
3202
+ `${pathPrefix}.attributes`
3203
+ );
3204
+ warnings.push(...parsedSpanAttributes.warnings);
3205
+ unsupportedFields.push(...parsedSpanAttributes.unsupportedFields);
3206
+ const sanitizedSpanAttributes = sanitizeOtlpAttributes(
3207
+ parsedSpanAttributes.attributes,
3208
+ `${pathPrefix}.attributes`
3209
+ );
3210
+ warnings.push(...sanitizedSpanAttributes.warnings);
3211
+ unsupportedFields.push(...sanitizedSpanAttributes.unsupportedFields);
3212
+ const attributes = {
3213
+ ...sanitizedSpanAttributes.attributes
3214
+ };
3215
+ for (const [key, value] of Object.entries(context.resourceAttributes)) {
3216
+ attributes[`resource.${key}`] = value;
3217
+ }
3218
+ for (const [key, value] of Object.entries(context.scopeAttributes)) {
3219
+ attributes[`scope.${key}`] = value;
3220
+ }
3221
+ if (context.scopeName !== void 0) {
3222
+ attributes["scope.name"] = context.scopeName;
3223
+ }
3224
+ if (context.scopeVersion !== void 0) {
3225
+ attributes["scope.version"] = context.scopeVersion;
3226
+ }
3227
+ for (const [key, value] of Object.entries(span)) {
3228
+ if (OTLP_SPAN_KEYS.has(key)) continue;
3229
+ unsupportedFields.push(`${pathPrefix}.${key}`);
3230
+ if (value === null || typeof value !== "object") {
3231
+ attributes[`otlp.${key}`] = value;
3232
+ } else {
3233
+ attributes[`otlp.${key}.summary`] = summarizeAttributeValue(value);
3234
+ warnings.push({
3235
+ code: "otlp_unsupported_field_summarized",
3236
+ message: `Unsupported OTLP span field "${key}" was summarized.`,
3237
+ severity: "warning",
3238
+ field: `${pathPrefix}.${key}`
3239
+ });
3240
+ }
3241
+ }
3242
+ for (const key of [
3243
+ "droppedAttributesCount",
3244
+ "droppedEventsCount",
3245
+ "droppedLinksCount",
3246
+ "links"
3247
+ ]) {
3248
+ if (span[key] !== void 0) {
3249
+ unsupportedFields.push(`${pathPrefix}.${key}`);
3250
+ warnings.push({
3251
+ code: "otlp_span_field_not_mapped",
3252
+ message: `OTLP span field "${key}" is not represented in AgentInspect events.`,
3253
+ severity: "warning",
3254
+ field: `${pathPrefix}.${key}`
3255
+ });
3256
+ }
3257
+ }
3258
+ const events = mapOtlpEvents(span.events, `${pathPrefix}.events`);
3259
+ warnings.push(...events.warnings);
3260
+ unsupportedFields.push(...events.unsupportedFields);
3261
+ if (events.events !== void 0) {
3262
+ attributes["otlp.events"] = events.events;
3263
+ }
3264
+ const traceId = readStringField(span, ["traceId"]) ?? "trace-unknown";
3265
+ const spanId = readStringField(span, ["spanId"]) ?? "span-unknown";
3266
+ const parentSpanId = readStringField(span, ["parentSpanId"]);
3267
+ const startedAt = readOpenInferenceTimestamp(
3268
+ span,
3269
+ ["startTimeUnixNano"],
3270
+ []
3271
+ );
3272
+ const endedAt = readOpenInferenceTimestamp(span, ["endTimeUnixNano"], []);
3273
+ const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
3274
+ if (startedAt === void 0) {
3275
+ unsupportedFields.push(`${pathPrefix}.startTimeUnixNano`);
3276
+ warnings.push({
3277
+ code: "otlp_missing_start_time",
3278
+ message: "OTLP span is missing a valid startTimeUnixNano; using Unix epoch.",
3279
+ severity: "warning",
3280
+ field: `${pathPrefix}.startTimeUnixNano`
3281
+ });
3282
+ }
3283
+ const { kind, warnings: kindWarnings } = readOtlpKind(
3284
+ parsedSpanAttributes.attributes,
3285
+ pathPrefix
3286
+ );
3287
+ warnings.push(...kindWarnings);
3288
+ const status = mapOtlpStatus(span.status);
3289
+ const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
3290
+ const errorMessage = isRecord6(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
3291
+ const event = {
3292
+ schemaVersion: "0.2",
3293
+ eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
3294
+ runId: typeof parsedSpanAttributes.attributes["agent_inspect.run_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.run_id"] : traceId,
3295
+ kind,
3296
+ name: readStringField(span, ["name"]) ?? spanId,
3297
+ timestamp,
3298
+ confidence: readOtlpConfidence(parsedSpanAttributes.attributes),
3299
+ source: {
3300
+ type: "otel",
3301
+ name: context.scopeName ?? (typeof context.resourceAttributes["service.name"] === "string" ? context.resourceAttributes["service.name"] : "otlp-json"),
3302
+ ...context.scopeVersion !== void 0 ? { version: context.scopeVersion } : {}
3303
+ },
3304
+ attributes,
3305
+ trace: {
3306
+ traceId,
3307
+ spanId,
3308
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
3309
+ }
3310
+ };
3311
+ if (status !== void 0) {
3312
+ event.status = status;
3313
+ }
3314
+ if (startedAt !== void 0) {
3315
+ event.startedAt = startedAt;
3316
+ }
3317
+ if (endedAt !== void 0) {
3318
+ event.endedAt = endedAt;
3319
+ }
3320
+ const durationMs = durationBetweenIso(startedAt, endedAt);
3321
+ if (durationMs !== void 0) {
3322
+ event.durationMs = durationMs;
3323
+ }
3324
+ if (tokenUsage !== void 0) {
3325
+ event.tokenUsage = tokenUsage;
3326
+ }
3327
+ if (status === "error") {
3328
+ event.error = {
3329
+ message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OTLP span error"
3330
+ };
3331
+ }
3332
+ return {
3333
+ event,
3334
+ warnings,
3335
+ unsupportedFields,
3336
+ spanId,
3337
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
3338
+ };
3339
+ }
3340
+ function mapOtlpEventsToPersisted(document) {
3341
+ const mapped = document.spans.map((span) => mapOtlpSpan(span));
3342
+ const spanIdToEventId = new Map(
3343
+ mapped.map((span) => [span.spanId, span.event.eventId])
3344
+ );
3345
+ for (const span of mapped) {
3346
+ if (span.parentSpanId === void 0) continue;
3347
+ span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
3348
+ }
3349
+ return {
3350
+ events: mapped.map((span) => span.event),
3351
+ warnings: mapped.flatMap((span) => span.warnings),
3352
+ unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
3353
+ };
3354
+ }
3355
+ var otlpJsonReader = {
3356
+ format: OTLP_READER_FORMAT,
3357
+ name: "OTLP JSON",
3358
+ async detect(input) {
3359
+ const resolved = await resolveInput(input);
3360
+ if (!resolved) return void 0;
3361
+ let parsed;
3362
+ try {
3363
+ parsed = parseJsonDocument(resolved.content);
3364
+ } catch {
3365
+ return void 0;
3366
+ }
3367
+ const document = extractOtlpDocument(parsed);
3368
+ if (!document) return void 0;
3369
+ return {
3370
+ format: OTLP_READER_FORMAT,
3371
+ confidence: document.confidence,
3372
+ readerName: "OTLP JSON",
3373
+ description: document.description,
3374
+ warnings: attachSingleSourceFile(document.warnings, resolved)
3375
+ };
3376
+ },
3377
+ async read(input) {
3378
+ const resolved = await resolveInput(input);
3379
+ if (!resolved) {
3380
+ throw new TraceReadError(
3381
+ "unsupported_format",
3382
+ "OTLP JSON reader requires file, string, or buffer input."
3383
+ );
3384
+ }
3385
+ let parsed;
3386
+ try {
3387
+ parsed = parseJsonDocument(resolved.content);
3388
+ } catch {
3389
+ throw new TraceReadError("unsupported_format", "OTLP JSON input is not valid JSON.", [
3390
+ {
3391
+ code: "otlp_invalid_json",
3392
+ message: "OTLP JSON reader could not parse the input as JSON.",
3393
+ severity: "error"
3394
+ }
3395
+ ]);
3396
+ }
3397
+ const document = extractOtlpDocument(parsed);
3398
+ if (!document || document.spans.length === 0) {
3399
+ throw new TraceReadError(
3400
+ "unsupported_format",
3401
+ "No valid OTLP spans found.",
3402
+ attachSingleSourceFile(
3403
+ document?.warnings ?? [
3404
+ {
3405
+ code: "otlp_no_valid_spans",
3406
+ message: "OTLP JSON input did not contain valid spans.",
3407
+ severity: "error"
3408
+ }
3409
+ ],
3410
+ resolved
3411
+ )
3412
+ );
3413
+ }
3414
+ const mapped = mapOtlpEventsToPersisted(document);
3415
+ const warnings = attachSingleSourceFile(
3416
+ [...document.warnings, ...mapped.warnings],
3417
+ resolved
3418
+ );
3419
+ const unsupportedFields = [
3420
+ ...document.unsupportedFields,
3421
+ ...mapped.unsupportedFields
3422
+ ].sort((a, b) => a.localeCompare(b));
3423
+ return {
3424
+ format: OTLP_READER_FORMAT,
3425
+ events: mapped.events,
3426
+ runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
3427
+ warnings,
3428
+ unsupportedFields,
3429
+ sourceFiles: resolved.sourceFiles
3430
+ };
3431
+ }
3432
+ };
3433
+ var agentInspectJsonlReader = {
3434
+ format: "agent-inspect-jsonl",
3435
+ name: "AgentInspect JSONL",
3436
+ async detect(input) {
3437
+ const resolved = await resolveInput(input);
3438
+ if (!resolved) return void 0;
3439
+ const detected = detectJsonlFormat(resolved.content);
3440
+ if (detected.validRows === 0 || detected.format === "empty") {
3441
+ return void 0;
3442
+ }
3443
+ return {
3444
+ format: "agent-inspect-jsonl",
3445
+ confidence: 0.95,
3446
+ readerName: "AgentInspect JSONL",
3447
+ description: agentInspectFormatLabel(detected.format),
3448
+ warnings: attachSingleSourceFile(detected.warnings, resolved)
3449
+ };
3450
+ },
3451
+ async read(input) {
3452
+ const resolved = await resolveInput(input);
3453
+ if (!resolved) {
3454
+ throw new Error("AgentInspect JSONL reader requires file, directory, string, or buffer input.");
3455
+ }
3456
+ const parsed = parseTraceJsonl(resolved.content, { warnings: false });
3457
+ if (parsed.sourceEventCount === 0) {
3458
+ throw new Error("No valid AgentInspect JSONL events found.");
3459
+ }
3460
+ const events = persistedEventsForParsedTrace(parsed);
3461
+ return {
3462
+ format: agentInspectFormatLabel(parsed.format),
3463
+ events,
3464
+ runs: persistedInspectEventsToRunTrees(events, { skipInvalid: true }),
3465
+ warnings: parsed.format === "mixed" ? attachSingleSourceFile(
3466
+ [
3467
+ {
3468
+ code: "mixed_agent_inspect_jsonl",
3469
+ message: "Trace input mixes schemaVersion 0.1 and 0.2 rows; events were normalized for reading.",
3470
+ severity: "warning"
3471
+ }
3472
+ ],
3473
+ resolved
3474
+ ) : [],
3475
+ unsupportedFields: [],
3476
+ sourceFiles: resolved.sourceFiles
3477
+ };
3478
+ }
3479
+ };
3480
+ var DEFAULT_TRACE_READERS = [
3481
+ agentInspectJsonlReader,
3482
+ openInferenceJsonReader,
3483
+ otlpJsonReader
3484
+ ];
3485
+ async function detectTraceFormat(input, options = {}) {
3486
+ const readers = options.readers ?? DEFAULT_TRACE_READERS;
3487
+ if (options.format !== void 0) {
3488
+ const reader = findReaderByFormat(options.format, readers);
3489
+ if (!reader) {
3490
+ return {
3491
+ status: "unsupported",
3492
+ candidates: [],
3493
+ warnings: [
3494
+ {
3495
+ code: "unsupported_format",
3496
+ message: `No trace reader is registered for format "${options.format}".`,
3497
+ severity: "error"
3498
+ }
3499
+ ]
3500
+ };
3501
+ }
3502
+ return {
3503
+ status: "detected",
3504
+ format: reader.format,
3505
+ candidates: [
3506
+ {
3507
+ format: reader.format,
3508
+ confidence: 1,
3509
+ readerName: reader.name,
3510
+ description: "Explicit format override"
3511
+ }
3512
+ ],
3513
+ warnings: []
3514
+ };
3515
+ }
3516
+ const candidates = [];
3517
+ const warnings = [];
3518
+ for (const reader of readers) {
3519
+ try {
3520
+ const candidate = await reader.detect(input);
3521
+ if (candidate !== void 0) {
3522
+ candidates.push(normalizeCandidate(reader, candidate));
3523
+ }
3524
+ } catch (error) {
3525
+ if (error instanceof TraceReadError) {
3526
+ warnings.push(...error.warnings);
3527
+ continue;
3528
+ }
3529
+ warnings.push({
3530
+ code: "reader_detect_failed",
3531
+ message: error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed during detection.`,
3532
+ severity: "warning"
3533
+ });
3534
+ }
3535
+ }
3536
+ const sorted = sortCandidates(
3537
+ candidates.filter((candidate) => candidate.confidence >= MIN_DETECTION_CONFIDENCE)
3538
+ );
3539
+ const candidateWarnings = collectWarnings(sorted);
3540
+ const lowConfidenceWarnings = candidates.length > sorted.length ? [
3541
+ {
3542
+ code: "low_confidence_candidates",
3543
+ message: `Ignored ${candidates.length - sorted.length} low-confidence format candidate(s).`,
3544
+ severity: "info"
3545
+ }
3546
+ ] : [];
3547
+ const allWarnings = dedupeWarnings([
3548
+ ...warnings,
3549
+ ...candidateWarnings,
3550
+ ...lowConfidenceWarnings
3551
+ ]);
3552
+ if (sorted.length === 0) {
3553
+ return {
3554
+ status: "unsupported",
3555
+ candidates: [],
3556
+ warnings: allWarnings
3557
+ };
3558
+ }
3559
+ const [best, second] = sorted;
3560
+ if (second !== void 0 && best.confidence - second.confidence <= AMBIGUOUS_CONFIDENCE_DELTA) {
3561
+ return {
3562
+ status: "ambiguous",
3563
+ candidates: sorted,
3564
+ warnings: [
3565
+ ...allWarnings,
3566
+ {
3567
+ code: "ambiguous_format_candidates",
3568
+ message: `Top trace format candidates are within ${AMBIGUOUS_CONFIDENCE_DELTA} confidence.`,
3569
+ severity: "warning"
3570
+ }
3571
+ ]
3572
+ };
3573
+ }
3574
+ return {
3575
+ status: "detected",
3576
+ format: best.format,
3577
+ candidates: sorted,
3578
+ warnings: allWarnings
3579
+ };
3580
+ }
3581
+ async function readTrace(input, options = {}) {
3582
+ const readers = options.readers ?? DEFAULT_TRACE_READERS;
3583
+ const detection = await detectTraceFormat(input, options);
3584
+ if (detection.status === "unsupported" || detection.format === void 0) {
3585
+ throw new TraceReadError(
3586
+ "unsupported_format",
3587
+ "No trace reader could detect the input format.",
3588
+ detection.warnings
3589
+ );
3590
+ }
3591
+ if (detection.status === "ambiguous") {
3592
+ throw new TraceReadError(
3593
+ "ambiguous_format",
3594
+ "Multiple trace readers matched the input with equal confidence.",
3595
+ detection.warnings
3596
+ );
3597
+ }
3598
+ const reader = findReaderByFormat(detection.format, readers);
3599
+ if (!reader) {
3600
+ throw new TraceReadError(
3601
+ "unsupported_format",
3602
+ `No trace reader is registered for format "${detection.format}".`,
3603
+ detection.warnings
3604
+ );
3605
+ }
3606
+ try {
3607
+ const result = await reader.read(input, { format: detection.format });
3608
+ return {
3609
+ ...result,
3610
+ format: result.format || detection.format,
3611
+ warnings: [...detection.warnings, ...result.warnings]
3612
+ };
3613
+ } catch (error) {
3614
+ if (error instanceof TraceReadError) {
3615
+ throw new TraceReadError(
3616
+ error.code,
3617
+ error.message,
3618
+ dedupeWarnings([...detection.warnings, ...error.warnings])
3619
+ );
3620
+ }
3621
+ throw new TraceReadError(
3622
+ "reader_failed",
3623
+ error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed.`,
3624
+ detection.warnings
3625
+ );
3626
+ }
3627
+ }
3628
+ function openTrace(input, options = {}) {
3629
+ return readTrace(input, options);
3630
+ }
3631
+
3632
+ // packages/viewer/src/html.ts
3633
+ var viewerIndexHtml = `<!DOCTYPE html>
3634
+ <html lang="en">
3635
+ <head>
3636
+ <meta charset="utf-8" />
3637
+ <title>AgentInspect Viewer</title>
3638
+ <style>
3639
+ body { font-family: system-ui, sans-serif; margin: 1.5rem; line-height: 1.4; }
3640
+ h1 { font-size: 1.25rem; }
3641
+ pre { background: #f4f4f5; padding: 1rem; overflow: auto; max-height: 70vh; }
3642
+ a { color: #0b57d0; }
3643
+ .muted { color: #666; }
3644
+ </style>
3645
+ </head>
3646
+ <body>
3647
+ <h1>AgentInspect local viewer</h1>
3648
+ <p class="muted">Read-only. JSONL on disk remains canonical.</p>
3649
+ <p><a href="/api/health">/api/health</a> \xB7 <a href="/api/traces">/api/traces</a> \xB7 <a href="/api/sessions">/api/sessions</a></p>
3650
+ <pre id="out">Loading traces\u2026</pre>
3651
+ <script>
3652
+ fetch("/api/traces").then((r) => r.json()).then((data) => {
3653
+ document.getElementById("out").textContent = JSON.stringify(data, null, 2);
3654
+ }).catch((err) => {
3655
+ document.getElementById("out").textContent = String(err);
3656
+ });
3657
+ </script>
3658
+ </body>
3659
+ </html>
3660
+ `;
3661
+
3662
+ // packages/viewer/src/server.ts
3663
+ var DEFAULT_HOST = "127.0.0.1";
3664
+ var DEFAULT_PORT = 7337;
3665
+ var DEFAULT_MAX_EVENTS = 500;
3666
+ function sendJson(res, status, body) {
3667
+ const payload = JSON.stringify(body);
3668
+ res.writeHead(status, {
3669
+ "content-type": "application/json; charset=utf-8",
3670
+ "cache-control": "no-store"
3671
+ });
3672
+ res.end(payload);
3673
+ }
3674
+ function notFound(res, message) {
3675
+ sendJson(res, 404, { error: message });
3676
+ }
3677
+ function badRequest(res, message) {
3678
+ sendJson(res, 400, { error: message });
3679
+ }
3680
+ function decodeId(segment) {
3681
+ if (!segment) return "";
3682
+ try {
3683
+ return decodeURIComponent(segment);
3684
+ } catch {
3685
+ return segment;
3686
+ }
3687
+ }
3688
+ function boundedEvents(events, maxEvents) {
3689
+ if (events.length <= maxEvents) return [...events];
3690
+ return events.slice(0, maxEvents);
3691
+ }
3692
+ function createViewerServer(options = {}) {
3693
+ const traceDir = resolveTraceDir({ dir: options.traceDir });
3694
+ const host = options.host ?? DEFAULT_HOST;
3695
+ const port = options.port ?? DEFAULT_PORT;
3696
+ const maxEvents = options.maxEvents ?? DEFAULT_MAX_EVENTS;
3697
+ if (host === "0.0.0.0") {
3698
+ console.warn(
3699
+ "[AgentInspect viewer] Binding to 0.0.0.0 exposes traces on the network. Use 127.0.0.1 unless you accept that risk."
3700
+ );
3701
+ }
3702
+ const server = http.createServer(async (req, res) => {
3703
+ try {
3704
+ if (req.method !== "GET" && req.method !== "HEAD") {
3705
+ return badRequest(res, "Only GET is supported.");
3706
+ }
3707
+ const url = new URL(req.url ?? "/", `http://${host}:${port}`);
3708
+ const pathname = url.pathname;
3709
+ if (pathname === "/" || pathname === "/index.html") {
3710
+ res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
3711
+ res.end(viewerIndexHtml);
3712
+ return;
3713
+ }
3714
+ if (pathname === "/api/health") {
3715
+ return sendJson(res, 200, {
3716
+ ok: true,
3717
+ readOnly: true,
3718
+ traceDir: path6__default.default.resolve(traceDir)
3719
+ });
3720
+ }
3721
+ const td = new TraceDirectory({ dir: traceDir });
3722
+ if (pathname === "/api/traces") {
3723
+ const files = await td.list();
3724
+ const metas = await loadTraceMetadataList(
3725
+ traceDir,
3726
+ files,
3727
+ (fileName) => td.getPath(fileName)
3728
+ );
3729
+ return sendJson(
3730
+ res,
3731
+ 200,
3732
+ metas.map((meta) => ({
3733
+ runId: meta.runId,
3734
+ name: meta.name,
3735
+ status: meta.status,
3736
+ file: path6__default.default.basename(meta.filePath),
3737
+ startedAt: meta.startedAt,
3738
+ durationMs: meta.durationMs
3739
+ }))
3740
+ );
3741
+ }
3742
+ if (pathname === "/api/sessions") {
3743
+ const files = await td.list();
3744
+ const metas = await loadTraceMetadataList(
3745
+ traceDir,
3746
+ files,
3747
+ (fileName) => td.getPath(fileName)
3748
+ );
3749
+ const runs = await loadSessionRunRecords(metas);
3750
+ const index = buildSessionIndex(runs, {
3751
+ correlateByGroupId: url.searchParams.get("correlateGroup") === "true"
3752
+ });
3753
+ return sendJson(res, 200, index);
3754
+ }
3755
+ const sessionMatch = pathname.match(/^\/api\/session\/([^/]+)$/);
3756
+ if (sessionMatch) {
3757
+ const sessionId = decodeId(sessionMatch[1]);
3758
+ const files = await td.list();
3759
+ const metas = await loadTraceMetadataList(
3760
+ traceDir,
3761
+ files,
3762
+ (fileName) => td.getPath(fileName)
3763
+ );
3764
+ const runs = await loadSessionRunRecords(metas);
3765
+ const index = buildSessionIndex(runs, {
3766
+ correlateByGroupId: url.searchParams.get("correlateGroup") === "true"
3767
+ });
3768
+ const session = index.sessions.find((item) => item.sessionId === sessionId);
3769
+ if (!session) return notFound(res, `Session not found: ${sessionId}`);
3770
+ return sendJson(res, 200, session);
3771
+ }
3772
+ const traceMatch = pathname.match(/^\/api\/trace\/([^/]+)$/);
3773
+ if (traceMatch) {
3774
+ const runId = decodeId(traceMatch[1]);
3775
+ const files = await td.list();
3776
+ const metas = await loadTraceMetadataList(
3777
+ traceDir,
3778
+ files,
3779
+ (fileName) => td.getPath(fileName)
3780
+ );
3781
+ const meta = metas.find((item) => item.runId === runId);
3782
+ if (!meta) return notFound(res, `Run not found: ${runId}`);
3783
+ const read = await openTrace({ type: "file", path: meta.filePath });
3784
+ const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
3785
+ return sendJson(res, 200, {
3786
+ runId,
3787
+ format: read.format,
3788
+ run,
3789
+ events: boundedEvents(read.events, maxEvents),
3790
+ warnings: read.warnings,
3791
+ truncated: read.events.length > maxEvents
3792
+ });
3793
+ }
3794
+ const timelineMatch = pathname.match(/^\/api\/trace\/([^/]+)\/timeline$/);
3795
+ if (timelineMatch) {
3796
+ const runId = decodeId(timelineMatch[1]);
3797
+ const files = await td.list();
3798
+ const metas = await loadTraceMetadataList(
3799
+ traceDir,
3800
+ files,
3801
+ (fileName) => td.getPath(fileName)
3802
+ );
3803
+ const meta = metas.find((item) => item.runId === runId);
3804
+ if (!meta) return notFound(res, `Run not found: ${runId}`);
3805
+ const read = await openTrace({ type: "file", path: meta.filePath });
3806
+ const legacyEvents = persistedInspectEventsToTraceEvents(
3807
+ boundedEvents(read.events, maxEvents)
3808
+ );
3809
+ const timeline = buildRunTimeline(legacyEvents, { focus: "all" });
3810
+ return sendJson(res, 200, { runId, timeline });
3811
+ }
3812
+ const checkMatch = pathname.match(/^\/api\/trace\/([^/]+)\/check$/);
3813
+ if (checkMatch) {
3814
+ const runId = decodeId(checkMatch[1]);
3815
+ const files = await td.list();
3816
+ const metas = await loadTraceMetadataList(
3817
+ traceDir,
3818
+ files,
3819
+ (fileName) => td.getPath(fileName)
3820
+ );
3821
+ const meta = metas.find((item) => item.runId === runId);
3822
+ if (!meta) return notFound(res, `Run not found: ${runId}`);
3823
+ const read = await openTrace({ type: "file", path: meta.filePath });
3824
+ const result = runTraceChecks(
3825
+ { read },
3826
+ { rules: [createRunStatusRule()], select: ["run.status"], runId }
3827
+ );
3828
+ return sendJson(res, 200, result);
3829
+ }
3830
+ return notFound(res, `Unknown route: ${pathname}`);
3831
+ } catch (error) {
3832
+ const message = error instanceof Error ? error.message : String(error);
3833
+ sendJson(res, 500, { error: message });
3834
+ }
3835
+ });
3836
+ return server;
3837
+ }
3838
+ function startViewerServer(options = {}) {
3839
+ const host = options.host ?? DEFAULT_HOST;
3840
+ const port = options.port ?? DEFAULT_PORT;
3841
+ const traceDir = resolveTraceDir({ dir: options.traceDir });
3842
+ const server = createViewerServer(options);
3843
+ return new Promise((resolve, reject) => {
3844
+ server.once("error", reject);
3845
+ server.listen(port, host, () => {
3846
+ const address = server.address();
3847
+ const resolvedPort = typeof address === "object" && address ? address.port : port;
3848
+ resolve({
3849
+ host,
3850
+ port: resolvedPort,
3851
+ traceDir: path6__default.default.resolve(traceDir),
3852
+ url: `http://${host}:${resolvedPort}`
3853
+ });
3854
+ });
3855
+ });
3856
+ }
3857
+
3858
+ exports.createViewerServer = createViewerServer;
3859
+ exports.startViewerServer = startViewerServer;
3860
+ exports.viewerIndexHtml = viewerIndexHtml;
3861
+ //# sourceMappingURL=index.cjs.map
3862
+ //# sourceMappingURL=index.cjs.map