@agent-inspect/mcp-server 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,4450 @@
1
+ 'use strict';
2
+
3
+ var readline = require('readline');
4
+ var path = 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
+
13
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
14
+
15
+ var readline__default = /*#__PURE__*/_interopDefault(readline);
16
+ var path__default = /*#__PURE__*/_interopDefault(path);
17
+ var os__default = /*#__PURE__*/_interopDefault(os);
18
+
19
+ // packages/mcp-server/src/index.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
+
492
+ // packages/core/src/utils/duration.ts
493
+ function parseDuration(duration) {
494
+ const raw = typeof duration === "string" ? duration.trim() : "";
495
+ const match = raw.match(/^(\d+)(ms|[smhd])$/);
496
+ if (!match) {
497
+ throw new Error(
498
+ `Invalid duration format: ${duration}. Use a positive integer followed by ms, s, m, h, or d (e.g. 500ms, 30s, 5m, 2h, 7d).`
499
+ );
500
+ }
501
+ const amount = Number.parseInt(match[1], 10);
502
+ const unit = match[2];
503
+ if (!Number.isFinite(amount) || amount <= 0) {
504
+ throw new Error(
505
+ `Invalid duration amount: ${duration}. Amount must be a positive integer.`
506
+ );
507
+ }
508
+ switch (unit) {
509
+ case "ms":
510
+ return amount;
511
+ case "s":
512
+ return amount * 1e3;
513
+ case "m":
514
+ return amount * 60 * 1e3;
515
+ case "h":
516
+ return amount * 60 * 60 * 1e3;
517
+ case "d":
518
+ return amount * 24 * 60 * 60 * 1e3;
519
+ default: {
520
+ throw new Error(`Unknown duration unit: ${unit}`);
521
+ }
522
+ }
523
+ }
524
+
525
+ // packages/core/src/utils.ts
526
+ var DEFAULT_TRACE_DIR_NAME = ".agent-inspect";
527
+ var RUNS_DIR_NAME = "runs";
528
+ var FALLBACK_TRACE_DIR = path__default.default.join(
529
+ os__default.default.tmpdir(),
530
+ "agent-inspect",
531
+ RUNS_DIR_NAME
532
+ );
533
+ function getDefaultTraceDir() {
534
+ const envDir = process.env.AGENT_INSPECT_TRACE_DIR;
535
+ if (typeof envDir === "string" && envDir.trim() !== "") {
536
+ return envDir.trim();
537
+ }
538
+ try {
539
+ const home = os__default.default.homedir();
540
+ if (typeof home !== "string" || home.trim() === "") {
541
+ return FALLBACK_TRACE_DIR;
542
+ }
543
+ return path__default.default.join(home, DEFAULT_TRACE_DIR_NAME, RUNS_DIR_NAME);
544
+ } catch {
545
+ return FALLBACK_TRACE_DIR;
546
+ }
547
+ }
548
+ function formatError(error) {
549
+ if (error instanceof Error) {
550
+ const out = { message: error.message };
551
+ if (typeof error.stack === "string" && error.stack.length > 0) {
552
+ out.stack = error.stack;
553
+ }
554
+ return out;
555
+ }
556
+ if (typeof error === "string") {
557
+ return { message: error };
558
+ }
559
+ if (error === null) {
560
+ return { message: "Unknown error: null" };
561
+ }
562
+ if (error === void 0) {
563
+ return { message: "Unknown error: undefined" };
564
+ }
565
+ if (typeof error === "number" || typeof error === "boolean" || typeof error === "bigint") {
566
+ return { message: String(error) };
567
+ }
568
+ if (typeof error === "object") {
569
+ try {
570
+ return { message: JSON.stringify(error) };
571
+ } catch {
572
+ return { message: "Unknown error" };
573
+ }
574
+ }
575
+ return { message: "Unknown error" };
576
+ }
577
+ function warn(message, error) {
578
+ const base = `[AgentInspect] ${message}`;
579
+ if (error === void 0) {
580
+ console.warn(base);
581
+ return;
582
+ }
583
+ console.warn(`${base}: ${formatError(error).message}`);
584
+ }
585
+
586
+ // packages/core/src/read-trace.ts
587
+ function isRecord3(value) {
588
+ return typeof value === "object" && value !== null && !Array.isArray(value);
589
+ }
590
+ function detectLineFormat(parsed) {
591
+ if (!isRecord3(parsed)) return "unknown";
592
+ if (parsed.schemaVersion === "0.1") return "0.1";
593
+ if (parsed.schemaVersion === "0.2") return "0.2";
594
+ if (parsed.schemaVersion === "1.0") return "1.0";
595
+ return "unknown";
596
+ }
597
+ function parseTraceJsonl(raw, options = {}) {
598
+ const validate = options.validate ?? isTraceEvent;
599
+ const emitWarning = (message) => {
600
+ if (options.warnings !== false) warn(message);
601
+ };
602
+ const persisted = [];
603
+ const traceEvents = [];
604
+ const rows = [];
605
+ let sourceEventCount = 0;
606
+ let saw01 = false;
607
+ let saw02 = false;
608
+ let saw10 = false;
609
+ let lineNumber = 0;
610
+ for (const line of raw.split(/\r?\n/)) {
611
+ lineNumber += 1;
612
+ const trimmed = line.trim();
613
+ if (trimmed === "") continue;
614
+ let parsed;
615
+ try {
616
+ parsed = JSON.parse(trimmed);
617
+ } catch {
618
+ emitWarning("Skipped invalid JSON line in trace file");
619
+ continue;
620
+ }
621
+ const format2 = detectLineFormat(parsed);
622
+ if (format2 === "0.1") {
623
+ saw01 = true;
624
+ if (validate(parsed)) {
625
+ sourceEventCount += 1;
626
+ traceEvents.push(parsed);
627
+ rows.push({ format: "0.1", event: parsed, sourceLine: lineNumber });
628
+ } else {
629
+ emitWarning("Skipped invalid trace event line in trace file");
630
+ }
631
+ continue;
632
+ }
633
+ if (format2 === "0.2" || format2 === "1.0") {
634
+ if (format2 === "0.2") saw02 = true;
635
+ else saw10 = true;
636
+ if (isPersistedInspectEvent(parsed)) {
637
+ sourceEventCount += 1;
638
+ persisted.push(parsed);
639
+ rows.push({ format: format2, event: parsed, sourceLine: lineNumber });
640
+ traceEvents.push(...persistedInspectEventToTraceEvents(parsed));
641
+ } else {
642
+ emitWarning("Skipped invalid persisted inspect event line in trace file");
643
+ }
644
+ continue;
645
+ }
646
+ emitWarning("Skipped trace line with unknown schemaVersion");
647
+ }
648
+ const seenFormats = [saw01, saw02, saw10].filter(Boolean).length;
649
+ if (seenFormats > 1) {
650
+ emitWarning(
651
+ "Trace file mixes AgentInspect schemaVersion rows; normalizing all rows"
652
+ );
653
+ }
654
+ let format = "empty";
655
+ if (seenFormats > 1) format = "mixed";
656
+ else if (saw01) format = "0.1";
657
+ else if (saw02) format = "0.2";
658
+ else if (saw10) format = "1.0";
659
+ return { format, sourceEventCount, events: traceEvents, persisted, rows };
660
+ }
661
+
662
+ // packages/core/src/storage.ts
663
+ function isRecord4(value) {
664
+ return typeof value === "object" && value !== null && !Array.isArray(value);
665
+ }
666
+ function nonEmptyString(value) {
667
+ return typeof value === "string" && value.trim() !== "";
668
+ }
669
+ function finiteNumber(value) {
670
+ return typeof value === "number" && Number.isFinite(value);
671
+ }
672
+ function optionalErrorInfo(value) {
673
+ if (value === void 0) return true;
674
+ if (!isRecord4(value)) return false;
675
+ if (typeof value.message !== "string") return false;
676
+ if ("stack" in value && value.stack !== void 0) {
677
+ if (typeof value.stack !== "string") return false;
678
+ }
679
+ return true;
680
+ }
681
+ function validateEvent(event) {
682
+ if (!isRecord4(event)) return false;
683
+ if (event.schemaVersion !== "0.1") return false;
684
+ if (!finiteNumber(event.timestamp)) return false;
685
+ if (typeof event.event !== "string") return false;
686
+ switch (event.event) {
687
+ case "run_started": {
688
+ if (!nonEmptyString(event.runId) || !nonEmptyString(event.name) || !finiteNumber(event.startTime)) {
689
+ return false;
690
+ }
691
+ if (event.metadata !== void 0 && !isRecord4(event.metadata)) {
692
+ return false;
693
+ }
694
+ return true;
695
+ }
696
+ case "run_completed": {
697
+ return nonEmptyString(event.runId) && (event.status === "success" || event.status === "error") && finiteNumber(event.endTime) && finiteNumber(event.durationMs) && optionalErrorInfo(event.error);
698
+ }
699
+ case "step_started": {
700
+ if (!nonEmptyString(event.runId) || !nonEmptyString(event.stepId) || !nonEmptyString(event.name) || !isStepType(event.type) || !finiteNumber(event.startTime)) {
701
+ return false;
702
+ }
703
+ if (event.parentId !== void 0 && typeof event.parentId !== "string") {
704
+ return false;
705
+ }
706
+ if (event.metadata !== void 0 && !isRecord4(event.metadata)) {
707
+ return false;
708
+ }
709
+ return true;
710
+ }
711
+ case "step_completed": {
712
+ return nonEmptyString(event.runId) && nonEmptyString(event.stepId) && (event.status === "success" || event.status === "error") && finiteNumber(event.endTime) && finiteNumber(event.durationMs) && optionalErrorInfo(event.error);
713
+ }
714
+ default:
715
+ return false;
716
+ }
717
+ }
718
+ async function readTraceEventsFromFile(filePath) {
719
+ try {
720
+ const raw = await promises.readFile(filePath, "utf-8");
721
+ return parseTraceJsonl(raw, { validate: validateEvent }).events;
722
+ } catch (e) {
723
+ if (e && typeof e === "object" && "code" in e && e.code === "ENOENT") {
724
+ return [];
725
+ }
726
+ warn("Failed to read trace events from file", e);
727
+ return [];
728
+ }
729
+ }
730
+
731
+ // packages/core/src/context.ts
732
+ new async_hooks.AsyncLocalStorage();
733
+ function resolveTraceDir(options = {}) {
734
+ if (typeof options.dir === "string" && options.dir.trim() !== "") {
735
+ return options.dir.trim();
736
+ }
737
+ const envDir = process.env.AGENT_INSPECT_TRACE_DIR;
738
+ if (typeof envDir === "string" && envDir.trim() !== "") {
739
+ return envDir.trim();
740
+ }
741
+ return getDefaultTraceDir();
742
+ }
743
+ var TraceDirectory = class {
744
+ #dir;
745
+ constructor(options = {}) {
746
+ this.#dir = resolveTraceDir(options);
747
+ }
748
+ getPath(filename) {
749
+ return filename ? path__default.default.join(this.#dir, filename) : this.#dir;
750
+ }
751
+ async list() {
752
+ try {
753
+ const files = await promises.readdir(this.#dir);
754
+ return files.filter((f) => f.endsWith(".jsonl"));
755
+ } catch (e) {
756
+ if (e && typeof e === "object" && "code" in e && e.code === "ENOENT") {
757
+ return [];
758
+ }
759
+ throw e;
760
+ }
761
+ }
762
+ async getFileStats(filename) {
763
+ return await promises.stat(this.getPath(filename));
764
+ }
765
+ };
766
+ function isFiniteNumber(v) {
767
+ return typeof v === "number" && Number.isFinite(v);
768
+ }
769
+ function parseIsoToMs2(value) {
770
+ if (value === void 0) return void 0;
771
+ const parsed = Date.parse(value);
772
+ return Number.isFinite(parsed) ? parsed : void 0;
773
+ }
774
+ async function extractMetadata(filePath, _quickScan) {
775
+ const stats = await promises.stat(filePath);
776
+ let runIdFromFile = path__default.default.basename(filePath);
777
+ if (runIdFromFile.endsWith(".jsonl")) {
778
+ runIdFromFile = runIdFromFile.slice(0, -".jsonl".length);
779
+ }
780
+ const raw = await promises.readFile(filePath, "utf-8");
781
+ const parsedTrace = parseTraceJsonl(raw, { warnings: false });
782
+ let runId;
783
+ let name;
784
+ let startedAt;
785
+ let endedAt;
786
+ let explicitDurationMs;
787
+ let hasRunStarted = false;
788
+ let hasRunCompleted = false;
789
+ let runCompletedStatus;
790
+ let anyStepError = false;
791
+ const anyKnownEvent = parsedTrace.sourceEventCount > 0;
792
+ let persistedStatus;
793
+ const persistedRun = parsedTrace.persisted.find(
794
+ (event) => event.kind === "RUN"
795
+ );
796
+ if (persistedRun) {
797
+ runId = persistedRun.runId;
798
+ if (persistedRun.name.trim() !== "") {
799
+ name = persistedRun.name;
800
+ }
801
+ startedAt = parseIsoToMs2(persistedRun.startedAt) ?? parseIsoToMs2(persistedRun.timestamp);
802
+ endedAt = parseIsoToMs2(persistedRun.endedAt);
803
+ if (isFiniteNumber(persistedRun.durationMs)) {
804
+ explicitDurationMs = persistedRun.durationMs;
805
+ if (endedAt === void 0 && startedAt !== void 0) {
806
+ endedAt = startedAt + persistedRun.durationMs;
807
+ }
808
+ }
809
+ if (persistedRun.status === "ok") persistedStatus = "success";
810
+ else if (persistedRun.status === "error") persistedStatus = "error";
811
+ else if (persistedRun.status === "running") persistedStatus = "running";
812
+ else if (persistedRun.status === "unknown") persistedStatus = "unknown";
813
+ } else {
814
+ runId = parsedTrace.persisted[0]?.runId;
815
+ }
816
+ for (const e of parsedTrace.events) {
817
+ if (runId === void 0 && typeof e.runId === "string") {
818
+ runId = e.runId;
819
+ }
820
+ if (e.event === "run_started") {
821
+ hasRunStarted = true;
822
+ const rs = e;
823
+ if (typeof rs.name === "string" && rs.name.trim() !== "") {
824
+ name = rs.name;
825
+ }
826
+ if (isFiniteNumber(rs.startTime)) {
827
+ startedAt = rs.startTime;
828
+ } else if (isFiniteNumber(rs.timestamp)) {
829
+ startedAt = rs.timestamp;
830
+ }
831
+ }
832
+ if (e.event === "run_completed") {
833
+ hasRunCompleted = true;
834
+ const rc = e;
835
+ runCompletedStatus = rc.status;
836
+ if (isFiniteNumber(rc.endTime)) endedAt = rc.endTime;
837
+ else if (isFiniteNumber(rc.timestamp)) endedAt = rc.timestamp;
838
+ if (isFiniteNumber(rc.durationMs)) explicitDurationMs = rc.durationMs;
839
+ }
840
+ if (e.event === "step_completed") {
841
+ const sc = e;
842
+ if (sc.status === "error") {
843
+ anyStepError = true;
844
+ }
845
+ }
846
+ }
847
+ const resolvedRunId = runId ?? runIdFromFile;
848
+ let status = "unknown";
849
+ if (hasRunCompleted && (runCompletedStatus === "success" || runCompletedStatus === "error")) {
850
+ status = runCompletedStatus;
851
+ } else if (anyStepError) {
852
+ status = "error";
853
+ } else if (persistedStatus !== void 0) {
854
+ status = persistedStatus;
855
+ } else if (hasRunStarted && !hasRunCompleted) {
856
+ status = "running";
857
+ } else if (anyKnownEvent) {
858
+ status = "unknown";
859
+ } else {
860
+ status = "unknown";
861
+ }
862
+ const durationMs = explicitDurationMs ?? (startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt ? endedAt - startedAt : void 0);
863
+ return {
864
+ runId: resolvedRunId,
865
+ name,
866
+ status,
867
+ startedAt,
868
+ endedAt,
869
+ durationMs,
870
+ eventCount: parsedTrace.sourceEventCount,
871
+ filePath,
872
+ fileSize: stats.size,
873
+ createdAt: stats.birthtime
874
+ };
875
+ }
876
+
877
+ // packages/core/src/trace-filter.ts
878
+ function toLower(s) {
879
+ return typeof s === "string" ? s.toLowerCase() : "";
880
+ }
881
+ function filterTraces(traces, options) {
882
+ const input = [...traces];
883
+ let out = input.filter((t) => {
884
+ if (options.status && t.status !== options.status) return false;
885
+ if (options.name) {
886
+ const q = options.name.toLowerCase();
887
+ const hay = `${toLower(t.name)} ${toLower(t.runId)}`;
888
+ if (!hay.includes(q)) return false;
889
+ }
890
+ if (options.since) {
891
+ const windowMs = parseDuration(options.since);
892
+ const cutoff = Date.now() - windowMs;
893
+ const started = typeof t.startedAt === "number" ? t.startedAt : void 0;
894
+ const basis = started ?? t.createdAt.getTime();
895
+ if (!Number.isFinite(basis) || basis < cutoff) return false;
896
+ }
897
+ return true;
898
+ });
899
+ out.sort((a, b) => {
900
+ const aTime = (typeof a.startedAt === "number" ? a.startedAt : void 0) ?? a.createdAt.getTime();
901
+ const bTime = (typeof b.startedAt === "number" ? b.startedAt : void 0) ?? b.createdAt.getTime();
902
+ return bTime - aTime;
903
+ });
904
+ if (typeof options.limit === "number" && Number.isFinite(options.limit)) {
905
+ const n = Math.max(0, Math.floor(options.limit));
906
+ out = out.slice(0, n);
907
+ }
908
+ return out;
909
+ }
910
+
911
+ // packages/core/src/timeline.ts
912
+ function finite(n) {
913
+ return typeof n === "number" && Number.isFinite(n);
914
+ }
915
+ function pickStreamingMeta(metadata) {
916
+ if (!metadata || typeof metadata !== "object") return void 0;
917
+ const chunkCount = metadata.chunkCount;
918
+ const streamDurationMs = metadata.streamDurationMs;
919
+ const streamedCharCount = metadata.streamedCharCount;
920
+ if (!finite(chunkCount) && !finite(streamDurationMs) && !finite(streamedCharCount)) {
921
+ return void 0;
922
+ }
923
+ return {
924
+ ...finite(chunkCount) ? { chunkCount } : {},
925
+ ...finite(streamDurationMs) ? { streamDurationMs } : {},
926
+ ...finite(streamedCharCount) ? { streamedCharCount } : {}
927
+ };
928
+ }
929
+ function pickCorrelation(metadata) {
930
+ if (!metadata || typeof metadata !== "object") return void 0;
931
+ const out = {};
932
+ for (const key of [
933
+ "correlationId",
934
+ "requestId",
935
+ "decisionId",
936
+ "groupId"
937
+ ]) {
938
+ const v = metadata[key];
939
+ if (typeof v === "string" && v.trim() !== "") {
940
+ out[key] = v;
941
+ }
942
+ }
943
+ return Object.keys(out).length > 0 ? out : void 0;
944
+ }
945
+ function buildRunTimeline(events, options = {}) {
946
+ const started = events.find(
947
+ (e) => e.event === "run_started"
948
+ );
949
+ const completed = events.filter(
950
+ (e) => e.event === "run_completed"
951
+ );
952
+ const lastCompleted = completed[completed.length - 1];
953
+ const runId = started?.runId ?? events.find((e) => typeof e.runId === "string")?.runId ?? "unknown-run";
954
+ const runStart = started && finite(started.startTime) ? started.startTime : started && finite(started.timestamp) ? started.timestamp : void 0;
955
+ const status = lastCompleted ? lastCompleted.status : started ? "running" : "unknown";
956
+ const steps = /* @__PURE__ */ new Map();
957
+ for (const e of events) {
958
+ if (e.event === "step_started") {
959
+ const s = e;
960
+ steps.set(s.stepId, {
961
+ name: s.name,
962
+ type: s.type,
963
+ parentId: s.parentId,
964
+ startedAt: finite(s.startTime) ? s.startTime : s.timestamp,
965
+ status: "running",
966
+ metadata: s.metadata
967
+ });
968
+ }
969
+ }
970
+ for (const e of events) {
971
+ if (e.event !== "step_completed") continue;
972
+ const c = e;
973
+ const node = steps.get(c.stepId);
974
+ if (!node) continue;
975
+ node.status = c.status;
976
+ if (finite(c.durationMs)) node.durationMs = c.durationMs;
977
+ }
978
+ const depthCache = /* @__PURE__ */ new Map();
979
+ const computeDepth = (stepId) => {
980
+ const cached = depthCache.get(stepId);
981
+ if (cached !== void 0) return cached;
982
+ const node = steps.get(stepId);
983
+ if (!node) return 0;
984
+ const parent = node.parentId;
985
+ if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
986
+ depthCache.set(stepId, 0);
987
+ return 0;
988
+ }
989
+ const d = Math.min(1e3, computeDepth(parent) + 1);
990
+ depthCache.set(stepId, d);
991
+ return d;
992
+ };
993
+ const entries = [];
994
+ for (const [stepId, s] of steps.entries()) {
995
+ const offsetMs = runStart !== void 0 && finite(s.startedAt) ? Math.max(0, s.startedAt - runStart) : 0;
996
+ entries.push({
997
+ stepId,
998
+ name: s.name,
999
+ type: s.type,
1000
+ status: s.status,
1001
+ depth: computeDepth(stepId),
1002
+ startedAt: s.startedAt,
1003
+ offsetMs,
1004
+ durationMs: s.durationMs,
1005
+ isError: s.status === "error",
1006
+ streaming: pickStreamingMeta(s.metadata)
1007
+ });
1008
+ }
1009
+ entries.sort((a, b) => a.startedAt - b.startedAt);
1010
+ const slowTopN = options.slowTopN ?? 3;
1011
+ if (options.focus === "slow" && entries.length > 0) {
1012
+ const ranked = [...entries].filter((e) => finite(e.durationMs)).sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0));
1013
+ const slowIds = new Set(
1014
+ ranked.slice(0, slowTopN).map((e) => e.stepId)
1015
+ );
1016
+ for (const e of entries) {
1017
+ if (slowIds.has(e.stepId)) e.slow = true;
1018
+ }
1019
+ }
1020
+ return {
1021
+ runId,
1022
+ name: typeof started?.name === "string" && started.name.trim() !== "" ? started.name : void 0,
1023
+ status,
1024
+ startedAt: runStart,
1025
+ endedAt: lastCompleted && finite(lastCompleted.endTime) ? lastCompleted.endTime : void 0,
1026
+ durationMs: lastCompleted && finite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0,
1027
+ correlation: pickCorrelation(
1028
+ started?.metadata
1029
+ ),
1030
+ entries
1031
+ };
1032
+ }
1033
+
1034
+ // packages/core/src/search.ts
1035
+ function parseDurationFilter(expr) {
1036
+ const raw = expr.trim();
1037
+ const m = raw.match(/^(>=|<=|>|<)\s*(.+)$/);
1038
+ if (!m) {
1039
+ throw new Error(
1040
+ `Invalid --duration "${expr}". Use forms like >5s, >=500ms, <2m.`
1041
+ );
1042
+ }
1043
+ const op = m[1];
1044
+ const ms = parseDuration(m[2].trim());
1045
+ return { op, ms };
1046
+ }
1047
+ function durationMatches(valueMs, filter) {
1048
+ if (valueMs === void 0 || !Number.isFinite(valueMs)) return false;
1049
+ switch (filter.op) {
1050
+ case ">":
1051
+ return valueMs > filter.ms;
1052
+ case ">=":
1053
+ return valueMs >= filter.ms;
1054
+ case "<":
1055
+ return valueMs < filter.ms;
1056
+ case "<=":
1057
+ return valueMs <= filter.ms;
1058
+ default:
1059
+ return false;
1060
+ }
1061
+ }
1062
+ function normalizeStepTypeFilter(kind, type) {
1063
+ const v = (kind ?? type)?.trim().toLowerCase();
1064
+ return v && v !== "" ? v : void 0;
1065
+ }
1066
+ function nameMatches(hay, needle) {
1067
+ return hay.toLowerCase().includes(needle.toLowerCase());
1068
+ }
1069
+ async function searchTraces(metas, options) {
1070
+ let filtered = filterTraces(metas, { since: options.since });
1071
+ const stepTypeFilter = normalizeStepTypeFilter(options.kind, options.type);
1072
+ const nameQuery = options.name?.trim();
1073
+ const toolQuery = options.tool?.trim();
1074
+ let durationFilter;
1075
+ if (options.duration) {
1076
+ durationFilter = parseDurationFilter(options.duration);
1077
+ }
1078
+ const limit = options.limit;
1079
+ const sessionId = options.session?.trim();
1080
+ const hasContentFilter = Boolean(
1081
+ options.status || stepTypeFilter || nameQuery || toolQuery || durationFilter
1082
+ );
1083
+ const results = [];
1084
+ const sessionLabel = sessionId && sessionId !== "" ? sessionId : void 0;
1085
+ if (!hasContentFilter) {
1086
+ for (const m of filtered) {
1087
+ results.push({
1088
+ runId: m.runId,
1089
+ runName: m.name,
1090
+ runStatus: m.status,
1091
+ timestamp: m.startedAt,
1092
+ durationMs: m.durationMs,
1093
+ matchReason: sessionLabel ? `trace in session ${sessionLabel}` : "trace in directory",
1094
+ matchedFields: sessionLabel ? ["run", "session"] : ["run"],
1095
+ filePath: m.filePath,
1096
+ ...sessionLabel ? { sessionId: sessionLabel } : {}
1097
+ });
1098
+ }
1099
+ return results.slice(0, limit);
1100
+ }
1101
+ for (const m of filtered) {
1102
+ if (options.status && m.status !== options.status) continue;
1103
+ let events = [];
1104
+ try {
1105
+ events = await readTraceEventsFromFile(m.filePath);
1106
+ } catch {
1107
+ continue;
1108
+ }
1109
+ if (events.length === 0) continue;
1110
+ const runMatches = matchRunLevel(m, {
1111
+ stepTypeFilter,
1112
+ nameQuery,
1113
+ toolQuery,
1114
+ durationFilter,
1115
+ statusFilter: options.status
1116
+ });
1117
+ results.push(...runMatches);
1118
+ const stepMatches = matchStepLevel(m, events, {
1119
+ stepTypeFilter,
1120
+ nameQuery,
1121
+ toolQuery,
1122
+ durationFilter,
1123
+ statusFilter: options.status
1124
+ });
1125
+ results.push(...stepMatches);
1126
+ }
1127
+ results.sort((a, b) => {
1128
+ const ta = a.timestamp ?? 0;
1129
+ const tb = b.timestamp ?? 0;
1130
+ if (ta !== tb) return ta - tb;
1131
+ const runCmp = a.runId.localeCompare(b.runId);
1132
+ if (runCmp !== 0) return runCmp;
1133
+ return (a.stepName ?? "").localeCompare(b.stepName ?? "");
1134
+ });
1135
+ return results.slice(0, limit);
1136
+ }
1137
+ function matchRunLevel(m, opts) {
1138
+ if (opts.stepTypeFilter || opts.toolQuery) return [];
1139
+ const out = [];
1140
+ const fields = [];
1141
+ if (opts.statusFilter && m.status === opts.statusFilter) {
1142
+ fields.push("run.status");
1143
+ }
1144
+ if (opts.nameQuery && nameMatches(m.name ?? m.runId, opts.nameQuery)) {
1145
+ fields.push("run.name");
1146
+ }
1147
+ if (opts.durationFilter && durationMatches(m.durationMs, opts.durationFilter)) {
1148
+ fields.push("run.durationMs");
1149
+ }
1150
+ if (fields.length === 0) return out;
1151
+ out.push({
1152
+ runId: m.runId,
1153
+ runName: m.name,
1154
+ runStatus: m.status,
1155
+ timestamp: m.startedAt,
1156
+ durationMs: m.durationMs,
1157
+ matchReason: `run match: ${fields.join(", ")}`,
1158
+ matchedFields: fields,
1159
+ filePath: m.filePath
1160
+ });
1161
+ return out;
1162
+ }
1163
+ function matchStepLevel(m, events, opts) {
1164
+ const out = [];
1165
+ const started = /* @__PURE__ */ new Map();
1166
+ for (const e of events) {
1167
+ if (e.event === "step_started") {
1168
+ started.set(e.stepId, e);
1169
+ }
1170
+ }
1171
+ for (const e of events) {
1172
+ if (e.event !== "step_completed") continue;
1173
+ const c = e;
1174
+ const s = started.get(c.stepId);
1175
+ if (!s) continue;
1176
+ const fields = [];
1177
+ const stepType = s.type;
1178
+ if (opts.stepTypeFilter && stepType !== opts.stepTypeFilter) {
1179
+ continue;
1180
+ }
1181
+ const hasStepFilters = opts.stepTypeFilter || opts.nameQuery || opts.toolQuery || opts.durationFilter || opts.statusFilter === "error" || opts.statusFilter === "success";
1182
+ if (!hasStepFilters) continue;
1183
+ if (opts.statusFilter === "error" && c.status === "error") {
1184
+ fields.push("step.status");
1185
+ } else if (opts.statusFilter === "success" && c.status === "success") {
1186
+ fields.push("step.status");
1187
+ } else if (opts.statusFilter === "error" || opts.statusFilter === "success") {
1188
+ continue;
1189
+ }
1190
+ if (opts.nameQuery) {
1191
+ if (!nameMatches(s.name, opts.nameQuery)) continue;
1192
+ fields.push("step.name");
1193
+ }
1194
+ if (opts.toolQuery) {
1195
+ const toolName = typeof s.metadata?.toolName === "string" ? s.metadata.toolName : s.name;
1196
+ if (!nameMatches(toolName, opts.toolQuery)) continue;
1197
+ fields.push("step.tool");
1198
+ }
1199
+ if (opts.durationFilter) {
1200
+ if (!durationMatches(c.durationMs, opts.durationFilter)) continue;
1201
+ fields.push("step.durationMs");
1202
+ }
1203
+ if (opts.stepTypeFilter) {
1204
+ fields.push("step.type");
1205
+ }
1206
+ if (fields.length === 0) continue;
1207
+ out.push({
1208
+ runId: m.runId,
1209
+ runName: m.name,
1210
+ runStatus: m.status,
1211
+ stepId: c.stepId,
1212
+ stepName: s.name,
1213
+ stepType,
1214
+ timestamp: s.startTime ?? s.timestamp,
1215
+ durationMs: c.durationMs,
1216
+ matchReason: `step match: ${fields.join(", ")}`,
1217
+ matchedFields: fields,
1218
+ filePath: m.filePath
1219
+ });
1220
+ }
1221
+ return out;
1222
+ }
1223
+ async function loadTraceMetadataList(_traceDir, fileNames, getPath) {
1224
+ const metas = [];
1225
+ for (const fileName of fileNames) {
1226
+ try {
1227
+ const filePath = getPath(fileName);
1228
+ const meta = await extractMetadata(filePath);
1229
+ metas.push(meta);
1230
+ } catch {
1231
+ }
1232
+ }
1233
+ return metas;
1234
+ }
1235
+
1236
+ // packages/core/src/checks/index.ts
1237
+ var SEVERITY_RANK = {
1238
+ error: 0,
1239
+ warning: 1,
1240
+ info: 2
1241
+ };
1242
+ var STATUS_RANK = {
1243
+ fail: 0,
1244
+ warning: 1,
1245
+ pass: 2
1246
+ };
1247
+ function compareStrings(a, b) {
1248
+ return (a ?? "").localeCompare(b ?? "");
1249
+ }
1250
+ function diagnostic(code, message, ruleId) {
1251
+ return {
1252
+ code,
1253
+ message,
1254
+ severity: "error",
1255
+ ...ruleId ? { ruleId } : {}
1256
+ };
1257
+ }
1258
+ function emptySummary() {
1259
+ return {
1260
+ passed: 0,
1261
+ failed: 0,
1262
+ warnings: 0,
1263
+ errors: 0
1264
+ };
1265
+ }
1266
+ function errorResult(input, diagnostics, selectedRun) {
1267
+ return {
1268
+ ok: false,
1269
+ status: "error",
1270
+ format: input.read.format,
1271
+ ...selectedRun ? { runId: selectedRun.runId } : {},
1272
+ summary: {
1273
+ ...emptySummary(),
1274
+ errors: diagnostics.filter((item) => item.severity === "error").length
1275
+ },
1276
+ findings: [],
1277
+ diagnostics: [...diagnostics]
1278
+ };
1279
+ }
1280
+ function flattenNodes(nodes) {
1281
+ return nodes.flatMap((node) => [node, ...flattenNodes(node.children)]);
1282
+ }
1283
+ function buildFacts(input, selectedRun) {
1284
+ const scopedRuns = selectedRun ? [selectedRun] : input.read.runs;
1285
+ const scopedRunIds = new Set(scopedRuns.map((run) => run.runId));
1286
+ const scopedEvents = selectedRun === void 0 ? input.read.events : input.read.events.filter((event) => scopedRunIds.has(event.runId));
1287
+ const nodes = flattenNodes(scopedRuns.flatMap((run) => run.children));
1288
+ const nodesByEventId = /* @__PURE__ */ new Map();
1289
+ const childrenByParentId = /* @__PURE__ */ new Map();
1290
+ for (const node of nodes) {
1291
+ nodesByEventId.set(node.event.eventId, node);
1292
+ const parentId = node.event.parentId;
1293
+ if (parentId) {
1294
+ const children = childrenByParentId.get(parentId) ?? [];
1295
+ children.push(node);
1296
+ childrenByParentId.set(parentId, children);
1297
+ }
1298
+ }
1299
+ return {
1300
+ format: input.read.format,
1301
+ runs: Object.freeze([...input.read.runs]),
1302
+ events: Object.freeze([...scopedEvents]),
1303
+ readerWarnings: Object.freeze([...input.read.warnings]),
1304
+ unsupportedFields: Object.freeze([...input.read.unsupportedFields]),
1305
+ sourceFiles: Object.freeze([...input.read.sourceFiles]),
1306
+ nodesByEventId,
1307
+ childrenByParentId,
1308
+ rootNodes: Object.freeze(scopedRuns.flatMap((run) => run.children))
1309
+ };
1310
+ }
1311
+ function resolveSelectedRun(input, runId) {
1312
+ if (input.selectedRun) {
1313
+ if (runId && input.selectedRun.runId !== runId) {
1314
+ return {
1315
+ diagnostics: [
1316
+ diagnostic(
1317
+ "AI_CHECK_INVALID_ARGUMENTS",
1318
+ `Selected run ${input.selectedRun.runId} does not match requested run ${runId}.`
1319
+ )
1320
+ ]
1321
+ };
1322
+ }
1323
+ return { run: input.selectedRun, diagnostics: [] };
1324
+ }
1325
+ if (runId) {
1326
+ const run = input.read.runs.find((candidate) => candidate.runId === runId);
1327
+ if (!run) {
1328
+ return {
1329
+ diagnostics: [
1330
+ diagnostic("AI_CHECK_RUN_SELECTION_REQUIRED", `Run not found: ${runId}.`)
1331
+ ]
1332
+ };
1333
+ }
1334
+ return { run, diagnostics: [] };
1335
+ }
1336
+ if (input.read.runs.length === 1) {
1337
+ return { run: input.read.runs[0], diagnostics: [] };
1338
+ }
1339
+ if (input.read.runs.length === 0) {
1340
+ return {
1341
+ diagnostics: [
1342
+ diagnostic("AI_CHECK_RUN_SELECTION_REQUIRED", "No runs are available for checks.")
1343
+ ]
1344
+ };
1345
+ }
1346
+ return {
1347
+ diagnostics: [
1348
+ diagnostic(
1349
+ "AI_CHECK_RUN_SELECTION_REQUIRED",
1350
+ "Multiple runs are available; select a run before executing checks."
1351
+ )
1352
+ ]
1353
+ };
1354
+ }
1355
+ function selectRules(rules, selectedIds) {
1356
+ const diagnostics = [];
1357
+ const byId = /* @__PURE__ */ new Map();
1358
+ for (const rule of rules) {
1359
+ if (byId.has(rule.id)) {
1360
+ diagnostics.push(
1361
+ diagnostic("AI_CHECK_INVALID_CONFIG", `Duplicate trace check rule id: ${rule.id}.`, rule.id)
1362
+ );
1363
+ continue;
1364
+ }
1365
+ byId.set(rule.id, rule);
1366
+ }
1367
+ if (selectedIds && selectedIds.length > 0) {
1368
+ const selected = new Set(selectedIds);
1369
+ for (const id of selected) {
1370
+ if (!byId.has(id)) {
1371
+ diagnostics.push(
1372
+ diagnostic("AI_CHECK_INVALID_CONFIG", `Unknown trace check rule id: ${id}.`, id)
1373
+ );
1374
+ }
1375
+ }
1376
+ return {
1377
+ rules: [...byId.values()].filter((rule) => selected.has(rule.id)).sort(compareRules),
1378
+ diagnostics
1379
+ };
1380
+ }
1381
+ return { rules: [...byId.values()].sort(compareRules), diagnostics };
1382
+ }
1383
+ function compareRules(a, b) {
1384
+ return a.id.localeCompare(b.id);
1385
+ }
1386
+ function eventTimestamp(finding, eventById) {
1387
+ const eventId = finding.evidence[0]?.eventId;
1388
+ return eventId ? eventById.get(eventId)?.timestamp ?? "" : "";
1389
+ }
1390
+ function compareFindings(eventById) {
1391
+ return (a, b) => {
1392
+ if (SEVERITY_RANK[a.severity] !== SEVERITY_RANK[b.severity]) {
1393
+ return SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity];
1394
+ }
1395
+ const byRule = a.ruleId.localeCompare(b.ruleId);
1396
+ if (byRule !== 0) return byRule;
1397
+ if (STATUS_RANK[a.status] !== STATUS_RANK[b.status]) {
1398
+ return STATUS_RANK[a.status] - STATUS_RANK[b.status];
1399
+ }
1400
+ const byRun = compareStrings(a.evidence[0]?.runId, b.evidence[0]?.runId);
1401
+ if (byRun !== 0) return byRun;
1402
+ const byTime = eventTimestamp(a, eventById).localeCompare(eventTimestamp(b, eventById));
1403
+ if (byTime !== 0) return byTime;
1404
+ const byEvent = compareStrings(a.evidence[0]?.eventId, b.evidence[0]?.eventId);
1405
+ if (byEvent !== 0) return byEvent;
1406
+ return compareStrings(a.evidence[0]?.path, b.evidence[0]?.path);
1407
+ };
1408
+ }
1409
+ function normalizeFinding(rule, finding) {
1410
+ return {
1411
+ ruleId: finding.ruleId || rule.id,
1412
+ severity: finding.severity ?? rule.defaultSeverity,
1413
+ status: finding.status,
1414
+ message: finding.message,
1415
+ ...finding.expected !== void 0 ? { expected: finding.expected } : {},
1416
+ ...finding.actual !== void 0 ? { actual: finding.actual } : {},
1417
+ evidence: [...finding.evidence ?? []]
1418
+ };
1419
+ }
1420
+ function summarize(findings, diagnostics) {
1421
+ return {
1422
+ passed: findings.filter((finding) => finding.status === "pass").length,
1423
+ failed: findings.filter(
1424
+ (finding) => finding.status === "fail" && finding.severity === "error"
1425
+ ).length,
1426
+ warnings: findings.filter(
1427
+ (finding) => finding.status === "warning" || finding.severity === "warning"
1428
+ ).length,
1429
+ errors: diagnostics.filter((item) => item.severity === "error").length
1430
+ };
1431
+ }
1432
+ function eventEvidence(event, path7) {
1433
+ return {
1434
+ runId: event.runId,
1435
+ eventId: event.eventId,
1436
+ parentId: event.parentId,
1437
+ traceId: event.trace?.traceId,
1438
+ spanId: event.trace?.spanId,
1439
+ kind: event.kind,
1440
+ name: event.name,
1441
+ status: event.status,
1442
+ ...{}
1443
+ };
1444
+ }
1445
+ function runEvidence(run) {
1446
+ return run ? [{ runId: run.runId, name: run.name, status: run.status }] : [];
1447
+ }
1448
+ function failFinding(ruleId, message, evidence, expected, actual) {
1449
+ return {
1450
+ ruleId,
1451
+ severity: "error",
1452
+ status: "fail",
1453
+ message,
1454
+ ...expected !== void 0 ? { expected } : {},
1455
+ ...actual !== void 0 ? { actual } : {},
1456
+ evidence: [...evidence]
1457
+ };
1458
+ }
1459
+ function createRunStatusRule(options = {}) {
1460
+ const expected = options.expected ?? "ok";
1461
+ const allowIncomplete = options.allowIncomplete === true;
1462
+ return {
1463
+ id: "run.status",
1464
+ category: "run",
1465
+ defaultSeverity: "error",
1466
+ evaluate(context) {
1467
+ const findings = [];
1468
+ const actual = context.selectedRun?.status ?? "unknown";
1469
+ if (actual !== expected) {
1470
+ findings.push(
1471
+ failFinding(
1472
+ "run.status",
1473
+ `Run status ${actual} did not match expected ${expected}.`,
1474
+ runEvidence(context.selectedRun),
1475
+ expected,
1476
+ actual
1477
+ )
1478
+ );
1479
+ }
1480
+ if (!allowIncomplete) {
1481
+ const running = context.events.filter((event) => event.status === "running");
1482
+ if (running.length > 0) {
1483
+ findings.push(
1484
+ failFinding(
1485
+ "run.status",
1486
+ "Run contains incomplete running events.",
1487
+ running.map((event) => eventEvidence(event)),
1488
+ "no running events",
1489
+ running.length
1490
+ )
1491
+ );
1492
+ }
1493
+ }
1494
+ return findings;
1495
+ }
1496
+ };
1497
+ }
1498
+ function runTraceChecks(input, options = {}) {
1499
+ const selected = resolveSelectedRun(input, options.runId);
1500
+ if (selected.diagnostics.length > 0) {
1501
+ return errorResult(input, selected.diagnostics, selected.run);
1502
+ }
1503
+ const rules = selectRules(options.rules ?? [], options.select);
1504
+ if (rules.diagnostics.length > 0) {
1505
+ return errorResult(input, rules.diagnostics, selected.run);
1506
+ }
1507
+ const facts = buildFacts(input, selected.run);
1508
+ const context = {
1509
+ ...facts,
1510
+ ...selected.run ? { selectedRun: selected.run } : {},
1511
+ ...input.sourceLabel ? { sourceLabel: input.sourceLabel } : {}
1512
+ };
1513
+ const diagnostics = [];
1514
+ const findings = [];
1515
+ for (const rule of rules.rules) {
1516
+ try {
1517
+ findings.push(...rule.evaluate(context).map((finding) => normalizeFinding(rule, finding)));
1518
+ } catch (error) {
1519
+ const message = error instanceof Error ? error.message : String(error);
1520
+ diagnostics.push(
1521
+ diagnostic("AI_CHECK_INTERNAL_ERROR", `Rule ${rule.id} failed: ${message}`, rule.id)
1522
+ );
1523
+ }
1524
+ }
1525
+ if (diagnostics.length > 0) {
1526
+ return errorResult(input, diagnostics, selected.run);
1527
+ }
1528
+ const eventById = new Map(input.read.events.map((event) => [event.eventId, event]));
1529
+ const sortedFindings = findings.sort(compareFindings(eventById));
1530
+ const summary = summarize(sortedFindings, diagnostics);
1531
+ const status = summary.failed > 0 ? "fail" : "pass";
1532
+ return {
1533
+ ok: status === "pass",
1534
+ status,
1535
+ format: input.read.format,
1536
+ ...selected.run ? { runId: selected.run.runId } : {},
1537
+ summary,
1538
+ findings: sortedFindings,
1539
+ diagnostics
1540
+ };
1541
+ }
1542
+
1543
+ // packages/core/src/diff/comparable.ts
1544
+ function extractOutputPreview(meta) {
1545
+ if (meta === void 0) return void 0;
1546
+ if ("outputPreview" in meta) return meta.outputPreview;
1547
+ if ("resultPreview" in meta) return meta.resultPreview;
1548
+ return void 0;
1549
+ }
1550
+ function mapStepStatus(s) {
1551
+ if (s === void 0) return "running";
1552
+ return s;
1553
+ }
1554
+ function manualTraceEventsToComparableRun(events) {
1555
+ const started = events.find((e) => e.event === "run_started");
1556
+ if (!started || started.event !== "run_started") {
1557
+ throw new Error("Invalid trace: missing run_started");
1558
+ }
1559
+ const rs = started;
1560
+ const runId = rs.runId;
1561
+ const completedAll = events.filter((e) => e.event === "run_completed");
1562
+ const lastCompleted = completedAll[completedAll.length - 1];
1563
+ let runStatus;
1564
+ if (lastCompleted === void 0) runStatus = "running";
1565
+ else runStatus = lastCompleted.status;
1566
+ const durationMs = lastCompleted !== void 0 && Number.isFinite(lastCompleted.durationMs) ? lastCompleted.durationMs : void 0;
1567
+ const steps = /* @__PURE__ */ new Map();
1568
+ let order = 0;
1569
+ for (const e of events) {
1570
+ if (e.event !== "step_started") continue;
1571
+ const s = e;
1572
+ const meta = s.metadata ? { ...s.metadata } : void 0;
1573
+ steps.set(s.stepId, {
1574
+ id: s.stepId,
1575
+ parentId: s.parentId,
1576
+ name: s.name,
1577
+ type: s.type,
1578
+ order: order++,
1579
+ timestamp: s.timestamp,
1580
+ metadata: meta
1581
+ });
1582
+ }
1583
+ for (const e of events) {
1584
+ if (e.event !== "step_completed") continue;
1585
+ const acc = steps.get(e.stepId);
1586
+ if (!acc) continue;
1587
+ acc.status = e.status;
1588
+ acc.durationMs = e.durationMs;
1589
+ if (e.error?.message) acc.errorMsg = e.error.message;
1590
+ const extra = e;
1591
+ if (extra.metadata !== void 0 && typeof extra.metadata === "object") {
1592
+ acc.metadata = { ...acc.metadata ?? {}, ...extra.metadata };
1593
+ }
1594
+ }
1595
+ const nodes = /* @__PURE__ */ new Map();
1596
+ for (const acc of steps.values()) {
1597
+ let meta = acc.metadata ? { ...acc.metadata } : void 0;
1598
+ if (acc.parentId !== void 0 && !steps.has(acc.parentId)) {
1599
+ meta = { ...meta ?? {}, agent_inspect_diff_parent_missing: true };
1600
+ }
1601
+ const outputPreview = extractOutputPreview(meta);
1602
+ const sc = {
1603
+ id: acc.id,
1604
+ name: acc.name,
1605
+ type: acc.type,
1606
+ status: mapStepStatus(acc.status),
1607
+ durationMs: acc.durationMs,
1608
+ error: acc.errorMsg,
1609
+ metadata: meta && Object.keys(meta).length > 0 ? meta : void 0,
1610
+ outputPreview,
1611
+ children: []
1612
+ };
1613
+ nodes.set(acc.id, sc);
1614
+ }
1615
+ const roots = [];
1616
+ const sortByOrder = (a, b) => {
1617
+ const oa = steps.get(a.id)?.order ?? 0;
1618
+ const ob = steps.get(b.id)?.order ?? 0;
1619
+ return oa - ob;
1620
+ };
1621
+ for (const acc of steps.values()) {
1622
+ const node = nodes.get(acc.id);
1623
+ if (acc.parentId !== void 0 && nodes.has(acc.parentId)) {
1624
+ nodes.get(acc.parentId).children.push(node);
1625
+ } else {
1626
+ roots.push(node);
1627
+ }
1628
+ }
1629
+ roots.sort(sortByOrder);
1630
+ for (const n of nodes.values()) {
1631
+ n.children.sort(sortByOrder);
1632
+ }
1633
+ return {
1634
+ runId,
1635
+ name: rs.name,
1636
+ status: runStatus,
1637
+ durationMs,
1638
+ steps: roots
1639
+ };
1640
+ }
1641
+
1642
+ // packages/core/src/exporters/helpers.ts
1643
+ var REDACT_SUBSTRINGS = [
1644
+ "authorization",
1645
+ "cookie",
1646
+ "token",
1647
+ "apikey",
1648
+ "password",
1649
+ "secret",
1650
+ "email"
1651
+ ];
1652
+ function shouldRedactKey(key) {
1653
+ const k = key.toLowerCase();
1654
+ for (const s of REDACT_SUBSTRINGS) {
1655
+ if (k.includes(s)) return true;
1656
+ }
1657
+ return false;
1658
+ }
1659
+ function safeString(value, maxLength) {
1660
+ if (value === null || value === void 0) return "";
1661
+ let s;
1662
+ if (typeof value === "string") s = value;
1663
+ else if (typeof value === "number" || typeof value === "boolean") s = String(value);
1664
+ else s = stableJson(value, false);
1665
+ if (maxLength !== void 0 && maxLength >= 0 && s.length > maxLength) {
1666
+ return `${s.slice(0, maxLength)}\u2026`;
1667
+ }
1668
+ return s;
1669
+ }
1670
+ function escapeMarkdown(value) {
1671
+ return value.replace(/\|/g, "\\|").replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n/g, " ");
1672
+ }
1673
+ function sortKeysDeep(input) {
1674
+ if (input === null || typeof input !== "object") return input;
1675
+ if (Array.isArray(input)) return input.map(sortKeysDeep);
1676
+ const o = input;
1677
+ const out = {};
1678
+ for (const k of Object.keys(o).sort()) {
1679
+ out[k] = sortKeysDeep(o[k]);
1680
+ }
1681
+ return out;
1682
+ }
1683
+ function stableJson(value, pretty) {
1684
+ const sorted = sortKeysDeep(value);
1685
+ return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
1686
+ }
1687
+ function compactAttributes(attrs, options) {
1688
+ if (attrs === void 0) return {};
1689
+ const maxLen = options?.maxLength ?? 500;
1690
+ const out = {};
1691
+ for (const key of Object.keys(attrs).sort()) {
1692
+ if (shouldRedactKey(key)) {
1693
+ out[key] = "[REDACTED]";
1694
+ continue;
1695
+ }
1696
+ const v = attrs[key];
1697
+ out[key] = compactValue(v, maxLen);
1698
+ }
1699
+ return out;
1700
+ }
1701
+ function compactValue(value, maxLen, redacted) {
1702
+ if (value === null || typeof value !== "object") {
1703
+ return typeof value === "string" ? safeString(value, maxLen) : value;
1704
+ }
1705
+ if (Array.isArray(value)) {
1706
+ const arr = value.slice(0, 20).map((x) => compactValue(x, maxLen));
1707
+ if (value.length > 20) arr.push(`\u2026(+${value.length - 20} more)`);
1708
+ return arr;
1709
+ }
1710
+ const o = value;
1711
+ const inner = {};
1712
+ for (const k of Object.keys(o)) {
1713
+ if (shouldRedactKey(k)) inner[k] = "[REDACTED]";
1714
+ else inner[k] = compactValue(o[k], maxLen);
1715
+ }
1716
+ return inner;
1717
+ }
1718
+ function flattenTree(tree) {
1719
+ const out = [];
1720
+ function walk(nodes) {
1721
+ for (const n of nodes) {
1722
+ out.push(n);
1723
+ if (n.children.length > 0) walk(n.children);
1724
+ }
1725
+ }
1726
+ walk(tree.children);
1727
+ return out;
1728
+ }
1729
+
1730
+ // packages/core/src/diff/engine.ts
1731
+ var DEFAULT_THRESHOLD_MS = 0;
1732
+ function pathSeg(step, index) {
1733
+ return { index, name: step.name, stepId: step.id };
1734
+ }
1735
+ function buildPath(segments) {
1736
+ return { path: [...segments] };
1737
+ }
1738
+ function pairSteps(left, right) {
1739
+ const usedRight = /* @__PURE__ */ new Set();
1740
+ const pairs = [];
1741
+ for (let i = 0; i < left.length; i++) {
1742
+ const L = left[i];
1743
+ let R = right.find((r) => !usedRight.has(r.id) && r.id === L.id);
1744
+ if (R === void 0 && i < right.length && !usedRight.has(right[i].id)) {
1745
+ const cand = right[i];
1746
+ if (cand.name === L.name && (cand.type ?? "") === (L.type ?? "")) {
1747
+ R = cand;
1748
+ }
1749
+ }
1750
+ if (R === void 0) {
1751
+ R = right.find(
1752
+ (r) => !usedRight.has(r.id) && r.name === L.name && (r.type ?? "") === (L.type ?? "")
1753
+ );
1754
+ }
1755
+ if (R !== void 0) {
1756
+ usedRight.add(R.id);
1757
+ pairs.push([L, R]);
1758
+ } else {
1759
+ pairs.push([L, void 0]);
1760
+ }
1761
+ }
1762
+ for (const R of right) {
1763
+ if (!usedRight.has(R.id)) {
1764
+ pairs.push([void 0, R]);
1765
+ }
1766
+ }
1767
+ return pairs;
1768
+ }
1769
+ function compareLeafSteps(L, R, segments, opts, out) {
1770
+ const path7 = buildPath(segments);
1771
+ if (L.name !== R.name) {
1772
+ out.push({
1773
+ kind: "structure",
1774
+ severity: "warning",
1775
+ message: "Step name differs",
1776
+ path: path7,
1777
+ left: L.name,
1778
+ right: R.name
1779
+ });
1780
+ }
1781
+ if ((L.type ?? "") !== (R.type ?? "")) {
1782
+ out.push({
1783
+ kind: "step-type",
1784
+ severity: "warning",
1785
+ message: "Step type differs",
1786
+ path: path7,
1787
+ left: L.type,
1788
+ right: R.type
1789
+ });
1790
+ }
1791
+ if ((L.status ?? "") !== (R.status ?? "")) {
1792
+ out.push({
1793
+ kind: "step-status",
1794
+ severity: "warning",
1795
+ message: "Step status differs",
1796
+ path: path7,
1797
+ left: L.status,
1798
+ right: R.status
1799
+ });
1800
+ }
1801
+ const le = L.error ?? "";
1802
+ const re = R.error ?? "";
1803
+ if (le !== re) {
1804
+ out.push({
1805
+ kind: "error",
1806
+ severity: "error",
1807
+ message: "Step error message differs",
1808
+ path: path7,
1809
+ left: le || void 0,
1810
+ right: re || void 0
1811
+ });
1812
+ }
1813
+ if (!opts.ignoreDuration) {
1814
+ const ld = L.durationMs;
1815
+ const rd = R.durationMs;
1816
+ const th = opts.durationThresholdMs;
1817
+ let differs = false;
1818
+ if (ld === void 0 && rd === void 0) differs = false;
1819
+ else if (ld === void 0 || rd === void 0) differs = true;
1820
+ else differs = Math.abs(ld - rd) > th;
1821
+ if (differs) {
1822
+ out.push({
1823
+ kind: "duration",
1824
+ severity: "info",
1825
+ message: "Step duration differs",
1826
+ path: path7,
1827
+ left: ld,
1828
+ right: rd
1829
+ });
1830
+ }
1831
+ }
1832
+ const lm = stableJson(L.metadata ?? {});
1833
+ const rm = stableJson(R.metadata ?? {});
1834
+ if (lm !== rm) {
1835
+ out.push({
1836
+ kind: "metadata",
1837
+ severity: "info",
1838
+ message: "Step metadata differs",
1839
+ path: path7,
1840
+ left: L.metadata,
1841
+ right: R.metadata
1842
+ });
1843
+ }
1844
+ const lo = stableJson(L.outputPreview ?? null);
1845
+ const ro = stableJson(R.outputPreview ?? null);
1846
+ if (lo !== ro) {
1847
+ out.push({
1848
+ kind: "output",
1849
+ severity: "info",
1850
+ message: "Output preview differs",
1851
+ path: path7,
1852
+ left: L.outputPreview,
1853
+ right: R.outputPreview
1854
+ });
1855
+ }
1856
+ }
1857
+ function compareRecursive(L, R, segments, opts, out) {
1858
+ compareLeafSteps(L, R, segments, opts, out);
1859
+ const pairs = pairSteps(L.children, R.children);
1860
+ let ci = 0;
1861
+ for (const [lch, rch] of pairs) {
1862
+ if (lch !== void 0 && rch !== void 0) {
1863
+ compareRecursive(lch, rch, [...segments, pathSeg(lch, ci)], opts, out);
1864
+ } else if (lch !== void 0) {
1865
+ out.push({
1866
+ kind: "step-removed",
1867
+ severity: "warning",
1868
+ message: `Step only in left run: ${lch.name}`,
1869
+ path: buildPath([...segments, pathSeg(lch, ci)]),
1870
+ left: lch.id,
1871
+ right: void 0
1872
+ });
1873
+ } else if (rch !== void 0) {
1874
+ out.push({
1875
+ kind: "step-added",
1876
+ severity: "warning",
1877
+ message: `Step only in right run: ${rch.name}`,
1878
+ path: buildPath([...segments, pathSeg(rch, ci)]),
1879
+ left: void 0,
1880
+ right: rch.id
1881
+ });
1882
+ }
1883
+ ci += 1;
1884
+ }
1885
+ }
1886
+ function mergeDiffDefaults(options) {
1887
+ return {
1888
+ ignoreDuration: false,
1889
+ durationThresholdMs: DEFAULT_THRESHOLD_MS,
1890
+ focus: "all",
1891
+ check: "all"
1892
+ };
1893
+ }
1894
+ function kindMatchesFilter(kind, merged) {
1895
+ return true;
1896
+ }
1897
+ function diffRuns(left, right, options) {
1898
+ const merged = mergeDiffDefaults();
1899
+ const opts = {
1900
+ ignoreDuration: merged.ignoreDuration,
1901
+ durationThresholdMs: merged.durationThresholdMs
1902
+ };
1903
+ const raw = [];
1904
+ if ((left.status ?? "") !== (right.status ?? "")) {
1905
+ raw.push({
1906
+ kind: "run-status",
1907
+ severity: "warning",
1908
+ message: "Run completion status differs",
1909
+ left: left.status,
1910
+ right: right.status
1911
+ });
1912
+ }
1913
+ {
1914
+ const ld = left.durationMs;
1915
+ const rd = right.durationMs;
1916
+ const th = merged.durationThresholdMs;
1917
+ let differs = false;
1918
+ if (ld === void 0 && rd === void 0) differs = false;
1919
+ else if (ld === void 0 || rd === void 0) differs = true;
1920
+ else differs = Math.abs(ld - rd) > th;
1921
+ if (differs) {
1922
+ raw.push({
1923
+ kind: "duration",
1924
+ severity: "info",
1925
+ message: "Run duration differs",
1926
+ left: ld,
1927
+ right: rd
1928
+ });
1929
+ }
1930
+ }
1931
+ const pairs = pairSteps(left.steps, right.steps);
1932
+ let idx = 0;
1933
+ for (const [ls, rs] of pairs) {
1934
+ if (ls !== void 0 && rs !== void 0) {
1935
+ compareRecursive(ls, rs, [pathSeg(ls, idx)], opts, raw);
1936
+ idx += 1;
1937
+ } else if (ls !== void 0) {
1938
+ raw.push({
1939
+ kind: "step-removed",
1940
+ severity: "warning",
1941
+ message: `Step only in left run: ${ls.name}`,
1942
+ path: buildPath([pathSeg(ls, idx)]),
1943
+ left: ls.id,
1944
+ right: void 0
1945
+ });
1946
+ idx += 1;
1947
+ } else if (rs !== void 0) {
1948
+ raw.push({
1949
+ kind: "step-added",
1950
+ severity: "warning",
1951
+ message: `Step only in right run: ${rs.name}`,
1952
+ path: buildPath([pathSeg(rs, idx)]),
1953
+ left: void 0,
1954
+ right: rs.id
1955
+ });
1956
+ idx += 1;
1957
+ }
1958
+ }
1959
+ const differences = raw.filter((d) => kindMatchesFilter(d.kind));
1960
+ let errors = 0;
1961
+ let warnings = 0;
1962
+ let info = 0;
1963
+ for (const d of differences) {
1964
+ if (d.severity === "error") errors += 1;
1965
+ else if (d.severity === "warning") warnings += 1;
1966
+ else info += 1;
1967
+ }
1968
+ const firstVisible = differences[0];
1969
+ const firstDivergence = firstVisible !== void 0 ? {
1970
+ kind: "first-divergence",
1971
+ severity: firstVisible.severity,
1972
+ message: `First divergence: ${firstVisible.message}`,
1973
+ path: firstVisible.path,
1974
+ left: firstVisible.left,
1975
+ right: firstVisible.right
1976
+ } : void 0;
1977
+ const summary = {
1978
+ leftRunId: left.runId,
1979
+ rightRunId: right.runId,
1980
+ totalDifferences: differences.length,
1981
+ errors,
1982
+ warnings,
1983
+ info,
1984
+ firstDivergence
1985
+ };
1986
+ return { summary, differences };
1987
+ }
1988
+
1989
+ // packages/core/src/exporters/markdown-exporter.ts
1990
+ function renderTreeAscii(nodes, indent = "") {
1991
+ const lines = [];
1992
+ for (let i = 0; i < nodes.length; i++) {
1993
+ const n = nodes[i];
1994
+ const last = i === nodes.length - 1;
1995
+ const branch = last ? "\u2514\u2500 " : "\u251C\u2500 ";
1996
+ const ev = n.event;
1997
+ const status = ev.status ?? "?";
1998
+ const dur = ev.durationMs !== void 0 && Number.isFinite(ev.durationMs) ? `${ev.durationMs}ms` : "-";
1999
+ lines.push(`${indent}${branch}${escapeMarkdown(ev.name)} [${ev.kind}] ${status} (${dur})`);
2000
+ const nextIndent = indent + (last ? " " : "\u2502 ");
2001
+ if (n.children.length > 0) {
2002
+ const childStr = renderTreeAscii(n.children, nextIndent);
2003
+ if (childStr.length > 0) lines.push(childStr);
2004
+ }
2005
+ }
2006
+ return lines.join("\n");
2007
+ }
2008
+ function exportMarkdown(tree, options) {
2009
+ const warnings = [];
2010
+ const includeMetadata = options?.includeMetadata ?? true;
2011
+ const includeAttributes = options?.includeAttributes ?? false;
2012
+ const includeErrors = options?.includeErrors ?? true;
2013
+ const maxLen = options?.maxAttributeLength ?? 500;
2014
+ const titleName = tree.name ?? tree.runId;
2015
+ const lines = [];
2016
+ lines.push(`# AgentInspect Run: ${escapeMarkdown(titleName)}`);
2017
+ lines.push("");
2018
+ lines.push("Generated locally by AgentInspect. Review for sensitive data before sharing.");
2019
+ lines.push("");
2020
+ if (includeMetadata) {
2021
+ lines.push("## Summary");
2022
+ lines.push("");
2023
+ lines.push(`- **runId**: ${escapeMarkdown(tree.runId)}`);
2024
+ if (tree.name !== void 0) lines.push(`- **name**: ${escapeMarkdown(tree.name)}`);
2025
+ lines.push(`- **status**: ${escapeMarkdown(String(tree.status ?? "unknown"))}`);
2026
+ lines.push(
2027
+ `- **durationMs**: ${tree.durationMs !== void 0 ? escapeMarkdown(String(tree.durationMs)) : "-"}`
2028
+ );
2029
+ lines.push(
2030
+ `- **startedAt**: ${tree.startedAt !== void 0 ? escapeMarkdown(String(tree.startedAt)) : "-"}`
2031
+ );
2032
+ lines.push(
2033
+ `- **endedAt**: ${tree.endedAt !== void 0 ? escapeMarkdown(String(tree.endedAt)) : "-"}`
2034
+ );
2035
+ lines.push(`- **totalEvents**: ${tree.metadata.totalEvents}`);
2036
+ lines.push("");
2037
+ lines.push("### Confidence breakdown");
2038
+ lines.push("");
2039
+ lines.push("| bucket | count |");
2040
+ lines.push("| --- | --- |");
2041
+ for (const k of Object.keys(tree.metadata.confidenceBreakdown).sort()) {
2042
+ const key = k;
2043
+ lines.push(
2044
+ `| ${escapeMarkdown(key)} | ${tree.metadata.confidenceBreakdown[key]} |`
2045
+ );
2046
+ }
2047
+ lines.push("");
2048
+ lines.push("### Kind breakdown");
2049
+ lines.push("");
2050
+ lines.push("| kind | count |");
2051
+ lines.push("| --- | --- |");
2052
+ for (const k of Object.keys(tree.metadata.kinds).sort()) {
2053
+ const key = k;
2054
+ const c = tree.metadata.kinds[key];
2055
+ if (c > 0) lines.push(`| ${escapeMarkdown(key)} | ${c} |`);
2056
+ }
2057
+ lines.push("");
2058
+ }
2059
+ lines.push("## Execution tree");
2060
+ lines.push("");
2061
+ lines.push("```text");
2062
+ lines.push(
2063
+ tree.children.length > 0 ? renderTreeAscii(tree.children) : "(no steps)"
2064
+ );
2065
+ lines.push("```");
2066
+ lines.push("");
2067
+ const flat = flattenTree(tree);
2068
+ const errors = flat.filter((n) => n.event.status === "error");
2069
+ if (includeErrors && errors.length > 0) {
2070
+ lines.push("## Errors");
2071
+ lines.push("");
2072
+ for (const n of errors) {
2073
+ const msg = n.event.attributes && typeof n.event.attributes.error === "object" ? safeString(
2074
+ n.event.attributes.error.message,
2075
+ maxLen
2076
+ ) : "";
2077
+ lines.push(
2078
+ `- **${escapeMarkdown(n.event.name)}** (${escapeMarkdown(n.event.eventId)}): ${escapeMarkdown(msg || "error")}`
2079
+ );
2080
+ }
2081
+ lines.push("");
2082
+ }
2083
+ if (includeAttributes) {
2084
+ lines.push("## Attributes (bounded)");
2085
+ lines.push("");
2086
+ for (const n of flat) {
2087
+ if (!n.event.attributes || Object.keys(n.event.attributes).length === 0) continue;
2088
+ const compact = compactAttributes(n.event.attributes, {
2089
+ maxLength: maxLen});
2090
+ lines.push(`### ${escapeMarkdown(n.event.name)}`);
2091
+ lines.push("");
2092
+ lines.push("```json");
2093
+ lines.push(stableJson(compact, true));
2094
+ lines.push("```");
2095
+ lines.push("");
2096
+ }
2097
+ warnings.push(
2098
+ "Attributes may still contain sensitive data; review exports before sharing."
2099
+ );
2100
+ }
2101
+ return {
2102
+ format: "markdown",
2103
+ content: lines.join("\n"),
2104
+ contentType: "text/markdown",
2105
+ fileExtension: ".md",
2106
+ warnings
2107
+ };
2108
+ }
2109
+
2110
+ // packages/core/src/persisted/token-usage.ts
2111
+ function isRecord5(value) {
2112
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2113
+ }
2114
+ function nonNegativeFinite(value) {
2115
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
2116
+ }
2117
+ function normalizeTokenUsage(value) {
2118
+ if (!isRecord5(value)) return void 0;
2119
+ const input = nonNegativeFinite(value.input);
2120
+ const output = nonNegativeFinite(value.output);
2121
+ const suppliedTotal = nonNegativeFinite(value.total);
2122
+ const cached = nonNegativeFinite(value.cached);
2123
+ const derivedTotal = input !== void 0 && output !== void 0 && Number.isFinite(input + output) ? input + output : void 0;
2124
+ const total = suppliedTotal ?? derivedTotal;
2125
+ if (input === void 0 && output === void 0 && total === void 0 && cached === void 0) {
2126
+ return void 0;
2127
+ }
2128
+ return {
2129
+ ...input !== void 0 ? { input } : {},
2130
+ ...output !== void 0 ? { output } : {},
2131
+ ...total !== void 0 ? { total } : {},
2132
+ ...cached !== void 0 ? { cached } : {}
2133
+ };
2134
+ }
2135
+
2136
+ // packages/core/src/persisted/from-trace-event.ts
2137
+ function sanitizeIdPart(value) {
2138
+ return value.replace(/[^a-zA-Z0-9_-]/g, "_");
2139
+ }
2140
+ function nodeIdForEvent(event) {
2141
+ switch (event.event) {
2142
+ case "run_started":
2143
+ case "run_completed":
2144
+ return event.runId;
2145
+ case "step_started":
2146
+ case "step_completed":
2147
+ return event.stepId;
2148
+ default:
2149
+ return "unknown";
2150
+ }
2151
+ }
2152
+ function createPersistedEventId(event, eventIndex) {
2153
+ const runId = sanitizeIdPart(event.runId);
2154
+ const ev = sanitizeIdPart(event.event);
2155
+ const node = sanitizeIdPart(nodeIdForEvent(event));
2156
+ return `manual:${runId}:${ev}:${node}:${eventIndex}`;
2157
+ }
2158
+ function toIsoTimestamp(ms) {
2159
+ if (typeof ms !== "number" || !Number.isFinite(ms)) {
2160
+ return { iso: (/* @__PURE__ */ new Date(0)).toISOString(), invalidTimestamp: true };
2161
+ }
2162
+ return { iso: new Date(ms).toISOString(), invalidTimestamp: false };
2163
+ }
2164
+ function buildSource(options) {
2165
+ return {
2166
+ type: "manual",
2167
+ name: options?.sourceName ?? "trace-event",
2168
+ version: options?.sourceVersion ?? "0.1"
2169
+ };
2170
+ }
2171
+ function mapStepTypeToInspectKind(type) {
2172
+ switch (type) {
2173
+ case "run":
2174
+ return "RUN";
2175
+ case "llm":
2176
+ return "LLM";
2177
+ case "tool":
2178
+ return "TOOL";
2179
+ case "decision":
2180
+ return "DECISION";
2181
+ case "logic":
2182
+ case "state":
2183
+ case "custom":
2184
+ return "LOGIC";
2185
+ default:
2186
+ return "LOGIC";
2187
+ }
2188
+ }
2189
+ function mapRunOrStepStatus(status) {
2190
+ return status === "success" ? "ok" : "error";
2191
+ }
2192
+ function mapErrorInfo(error) {
2193
+ if (!error?.message) {
2194
+ return {};
2195
+ }
2196
+ const out = {
2197
+ persisted: {
2198
+ message: error.message,
2199
+ name: "Error"
2200
+ }
2201
+ };
2202
+ if (typeof error.stack === "string" && error.stack.length > 0) {
2203
+ out.errorStack = error.stack;
2204
+ }
2205
+ return out;
2206
+ }
2207
+ function mapTokenUsageFromMetadata(metadata) {
2208
+ return normalizeTokenUsage(metadata?.tokens);
2209
+ }
2210
+ function compactAttributes2(entries) {
2211
+ const out = {};
2212
+ for (const [key, value] of Object.entries(entries)) {
2213
+ if (value !== void 0) {
2214
+ out[key] = value;
2215
+ }
2216
+ }
2217
+ return Object.keys(out).length > 0 ? out : void 0;
2218
+ }
2219
+ function traceEventToPersistedInspectEvent(event, options) {
2220
+ const eventIndex = options?.eventIndex ?? 0;
2221
+ const eventId = createPersistedEventId(event, eventIndex);
2222
+ const source = buildSource(options);
2223
+ const tsMain = toIsoTimestamp(event.timestamp);
2224
+ switch (event.event) {
2225
+ case "run_started": {
2226
+ const tsStart = toIsoTimestamp(event.startTime);
2227
+ const correlation = extractCorrelationMetadata(event.metadata);
2228
+ const attributes = compactAttributes2({
2229
+ legacyEvent: "run_started",
2230
+ metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
2231
+ correlationId: correlation?.correlationId,
2232
+ requestId: correlation?.requestId,
2233
+ decisionId: correlation?.decisionId,
2234
+ groupId: correlation?.groupId,
2235
+ invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
2236
+ });
2237
+ return {
2238
+ schemaVersion: "0.2",
2239
+ eventId,
2240
+ runId: event.runId,
2241
+ kind: "RUN",
2242
+ name: event.name,
2243
+ status: "running",
2244
+ timestamp: tsMain.iso,
2245
+ startedAt: tsStart.iso,
2246
+ confidence: "explicit",
2247
+ source,
2248
+ attributes
2249
+ };
2250
+ }
2251
+ case "run_completed": {
2252
+ const tsEnd = toIsoTimestamp(event.endTime);
2253
+ const { persisted: error, errorStack } = mapErrorInfo(event.error);
2254
+ const attributes = compactAttributes2({
2255
+ legacyEvent: "run_completed",
2256
+ errorStack,
2257
+ invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
2258
+ });
2259
+ return {
2260
+ schemaVersion: "0.2",
2261
+ eventId,
2262
+ runId: event.runId,
2263
+ kind: "RUN",
2264
+ name: "run",
2265
+ status: mapRunOrStepStatus(event.status),
2266
+ timestamp: tsMain.iso,
2267
+ endedAt: tsEnd.iso,
2268
+ durationMs: event.durationMs,
2269
+ confidence: "explicit",
2270
+ source,
2271
+ attributes,
2272
+ error
2273
+ };
2274
+ }
2275
+ case "step_started": {
2276
+ const tsStart = toIsoTimestamp(event.startTime);
2277
+ const tokenUsage = mapTokenUsageFromMetadata(event.metadata);
2278
+ const attributes = compactAttributes2({
2279
+ legacyEvent: "step_started",
2280
+ stepId: event.stepId,
2281
+ stepType: event.type,
2282
+ metadata: event.metadata !== void 0 ? { ...event.metadata } : void 0,
2283
+ invalidTimestamp: tsMain.invalidTimestamp || tsStart.invalidTimestamp ? true : void 0
2284
+ });
2285
+ const out = {
2286
+ schemaVersion: "0.2",
2287
+ eventId,
2288
+ runId: event.runId,
2289
+ kind: mapStepTypeToInspectKind(event.type),
2290
+ name: event.name,
2291
+ status: "running",
2292
+ timestamp: tsMain.iso,
2293
+ startedAt: tsStart.iso,
2294
+ confidence: "explicit",
2295
+ source,
2296
+ attributes
2297
+ };
2298
+ if (event.parentId !== void 0) {
2299
+ out.parentId = event.parentId;
2300
+ }
2301
+ if (tokenUsage !== void 0) {
2302
+ out.tokenUsage = tokenUsage;
2303
+ }
2304
+ return out;
2305
+ }
2306
+ case "step_completed": {
2307
+ const tsEnd = toIsoTimestamp(event.endTime);
2308
+ const { persisted: error, errorStack } = mapErrorInfo(event.error);
2309
+ const attributes = compactAttributes2({
2310
+ legacyEvent: "step_completed",
2311
+ stepId: event.stepId,
2312
+ errorStack,
2313
+ invalidTimestamp: tsMain.invalidTimestamp || tsEnd.invalidTimestamp ? true : void 0
2314
+ });
2315
+ return {
2316
+ schemaVersion: "0.2",
2317
+ eventId,
2318
+ runId: event.runId,
2319
+ kind: "LOGIC",
2320
+ name: event.stepId,
2321
+ status: mapRunOrStepStatus(event.status),
2322
+ timestamp: tsMain.iso,
2323
+ endedAt: tsEnd.iso,
2324
+ durationMs: event.durationMs,
2325
+ confidence: "explicit",
2326
+ source,
2327
+ attributes,
2328
+ error
2329
+ };
2330
+ }
2331
+ default: {
2332
+ const _exhaustive = event;
2333
+ throw new Error(`Unsupported trace event: ${_exhaustive.event}`);
2334
+ }
2335
+ }
2336
+ }
2337
+ function traceEventsToPersistedInspectEvents(events, options) {
2338
+ return events.map(
2339
+ (event, index) => traceEventToPersistedInspectEvent(event, { ...options, eventIndex: index })
2340
+ );
2341
+ }
2342
+
2343
+ // packages/core/src/persisted/to-inspect-event.ts
2344
+ function compactAttributes3(entries) {
2345
+ const out = {};
2346
+ for (const [key, value] of Object.entries(entries)) {
2347
+ if (value !== void 0) {
2348
+ out[key] = value;
2349
+ }
2350
+ }
2351
+ return Object.keys(out).length > 0 ? out : void 0;
2352
+ }
2353
+ function parseIsoToMs3(iso) {
2354
+ const parsed = Date.parse(iso);
2355
+ if (!Number.isFinite(parsed)) {
2356
+ return { ms: 0, invalidTimestamp: true };
2357
+ }
2358
+ return { ms: parsed, invalidTimestamp: false };
2359
+ }
2360
+ function mapPersistedSourceToInspect(event) {
2361
+ const attrs = event.attributes ?? {};
2362
+ const sourceName = event.source.name;
2363
+ if (sourceName === "pino") {
2364
+ return {
2365
+ type: "pino",
2366
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
2367
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
2368
+ };
2369
+ }
2370
+ if (sourceName === "winston") {
2371
+ return {
2372
+ type: "winston",
2373
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
2374
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
2375
+ };
2376
+ }
2377
+ const mapType = (t) => {
2378
+ switch (t) {
2379
+ case "manual":
2380
+ return "manual";
2381
+ case "json-log":
2382
+ return "json-log";
2383
+ case "log4js":
2384
+ return "log4js";
2385
+ case "adapter":
2386
+ case "ai-sdk":
2387
+ case "otel":
2388
+ return "adapter";
2389
+ default:
2390
+ return "json-log";
2391
+ }
2392
+ };
2393
+ return {
2394
+ type: mapType(event.source.type),
2395
+ file: typeof attrs.sourceFile === "string" ? attrs.sourceFile : void 0,
2396
+ line: typeof attrs.sourceLine === "number" ? attrs.sourceLine : void 0
2397
+ };
2398
+ }
2399
+ function buildInspectAttributes(event) {
2400
+ const attrs = event.attributes !== void 0 ? { ...event.attributes } : {};
2401
+ if (event.inputSummary !== void 0) {
2402
+ attrs.inputSummary = event.inputSummary;
2403
+ }
2404
+ if (event.outputSummary !== void 0) {
2405
+ attrs.outputSummary = event.outputSummary;
2406
+ }
2407
+ if (event.error) {
2408
+ if (event.error.name !== void 0) {
2409
+ attrs.errorName = event.error.name;
2410
+ }
2411
+ attrs.errorMessage = event.error.message;
2412
+ if (event.error.code !== void 0) {
2413
+ attrs.errorCode = event.error.code;
2414
+ }
2415
+ }
2416
+ if (event.tokenUsage) {
2417
+ attrs.tokens = { ...event.tokenUsage };
2418
+ }
2419
+ if (event.source.type === "ai-sdk" || event.source.type === "otel") {
2420
+ attrs.originalSourceType = event.source.type;
2421
+ }
2422
+ if (event.source.name !== void 0) {
2423
+ attrs.sourceName = event.source.name;
2424
+ }
2425
+ if (event.source.version !== void 0) {
2426
+ attrs.sourceVersion = event.source.version;
2427
+ }
2428
+ return attrs;
2429
+ }
2430
+ function persistedInspectEventToInspectEvent(event) {
2431
+ if (!isPersistedInspectEvent(event)) {
2432
+ throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
2433
+ }
2434
+ const ts = parseIsoToMs3(event.timestamp);
2435
+ const attrs = buildInspectAttributes(event);
2436
+ if (ts.invalidTimestamp) {
2437
+ attrs.invalidTimestamp = true;
2438
+ }
2439
+ let status;
2440
+ if (event.status === "running" || event.status === "ok" || event.status === "error") {
2441
+ status = event.status;
2442
+ } else if (event.status === "unknown") {
2443
+ attrs.persistedStatus = "unknown";
2444
+ }
2445
+ const out = {
2446
+ eventId: event.eventId,
2447
+ runId: event.runId,
2448
+ name: event.name,
2449
+ kind: event.kind,
2450
+ timestamp: ts.ms,
2451
+ confidence: event.confidence,
2452
+ source: mapPersistedSourceToInspect(event),
2453
+ attributes: compactAttributes3(attrs)
2454
+ };
2455
+ if (event.parentId !== void 0) {
2456
+ out.parentId = event.parentId;
2457
+ }
2458
+ if (status !== void 0) {
2459
+ out.status = status;
2460
+ }
2461
+ if (event.durationMs !== void 0 && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
2462
+ out.durationMs = event.durationMs;
2463
+ }
2464
+ return out;
2465
+ }
2466
+ function persistedInspectEventsToInspectEvents(events, options) {
2467
+ const skipInvalid = options?.skipInvalid === true;
2468
+ const out = [];
2469
+ for (const event of events) {
2470
+ if (!isPersistedInspectEvent(event)) {
2471
+ if (skipInvalid) {
2472
+ continue;
2473
+ }
2474
+ throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
2475
+ }
2476
+ out.push(persistedInspectEventToInspectEvent(event));
2477
+ }
2478
+ return out;
2479
+ }
2480
+
2481
+ // packages/core/src/logs/tree-builder.ts
2482
+ function inc(map, key) {
2483
+ map[key] = (map[key] ?? 0) + 1;
2484
+ }
2485
+ function computeRunStatus(events) {
2486
+ let hasRunning = false;
2487
+ for (const e of events) {
2488
+ if (e.status === "error") return "error";
2489
+ if (e.status === "running") hasRunning = true;
2490
+ }
2491
+ if (hasRunning) return "running";
2492
+ return "ok";
2493
+ }
2494
+ var TreeBuilder = class {
2495
+ constructor(options) {
2496
+ void options?.config;
2497
+ }
2498
+ build(events) {
2499
+ const byRun = /* @__PURE__ */ new Map();
2500
+ for (const e of events) {
2501
+ if (!byRun.has(e.runId)) byRun.set(e.runId, []);
2502
+ byRun.get(e.runId).push(e);
2503
+ }
2504
+ const out = [];
2505
+ for (const [runId, runEvents] of byRun.entries()) {
2506
+ const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
2507
+ const nodes = /* @__PURE__ */ new Map();
2508
+ for (const e of sorted) {
2509
+ nodes.set(e.eventId, { event: e, children: [], depth: 0 });
2510
+ }
2511
+ const roots = [];
2512
+ for (const node of nodes.values()) {
2513
+ const parentId = node.event.parentId;
2514
+ if (parentId && nodes.has(parentId)) {
2515
+ nodes.get(parentId).children.push(node);
2516
+ } else {
2517
+ roots.push(node);
2518
+ }
2519
+ }
2520
+ const assignDepth = (n, depth) => {
2521
+ n.depth = depth;
2522
+ for (const c of n.children) assignDepth(c, depth + 1);
2523
+ };
2524
+ for (const r of roots) assignDepth(r, 0);
2525
+ const confidenceBreakdown = {
2526
+ explicit: 0,
2527
+ correlated: 0,
2528
+ heuristic: 0,
2529
+ unknown: 0
2530
+ };
2531
+ const kinds = {};
2532
+ for (const e of sorted) {
2533
+ inc(confidenceBreakdown, e.confidence);
2534
+ kinds[e.kind] = (kinds[e.kind] ?? 0) + 1;
2535
+ }
2536
+ const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
2537
+ const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
2538
+ const status = computeRunStatus(sorted);
2539
+ const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
2540
+ const name = sorted.find((e) => e.kind === "RUN")?.name;
2541
+ out.push({
2542
+ runId,
2543
+ name,
2544
+ status,
2545
+ startedAt,
2546
+ endedAt: status === "running" ? void 0 : endedAt,
2547
+ durationMs,
2548
+ children: roots,
2549
+ metadata: {
2550
+ totalEvents: sorted.length,
2551
+ confidenceBreakdown,
2552
+ kinds
2553
+ }
2554
+ });
2555
+ }
2556
+ out.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));
2557
+ return out;
2558
+ }
2559
+ };
2560
+
2561
+ // packages/core/src/persisted/tree-bridge.ts
2562
+ function persistedInspectEventsToRunTrees(events, options) {
2563
+ const inspectEvents = persistedInspectEventsToInspectEvents(events, {
2564
+ skipInvalid: options?.skipInvalid
2565
+ });
2566
+ return new TreeBuilder().build(inspectEvents);
2567
+ }
2568
+ var DEFAULT_MAX_TRACE_INPUT_BYTES = 10 * 1024 * 1024;
2569
+ var MIN_DETECTION_CONFIDENCE = 0.5;
2570
+ var AMBIGUOUS_CONFIDENCE_DELTA = 0.05;
2571
+ var resolvedInputCache = /* @__PURE__ */ new WeakMap();
2572
+ var OPENINFERENCE_READER_FORMAT = "openinference-json";
2573
+ var OTLP_READER_FORMAT = "otlp-json";
2574
+ var OPENINFERENCE_SPAN_KEYS = /* @__PURE__ */ new Set([
2575
+ "trace_id",
2576
+ "traceId",
2577
+ "span_id",
2578
+ "spanId",
2579
+ "parent_span_id",
2580
+ "parentSpanId",
2581
+ "name",
2582
+ "start_time_unix_nano",
2583
+ "startTimeUnixNano",
2584
+ "end_time_unix_nano",
2585
+ "endTimeUnixNano",
2586
+ "start_time",
2587
+ "startTime",
2588
+ "end_time",
2589
+ "endTime",
2590
+ "attributes",
2591
+ "status",
2592
+ "kind",
2593
+ "span_kind",
2594
+ "spanKind"
2595
+ ]);
2596
+ var OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS = [
2597
+ "input.value",
2598
+ "output.value",
2599
+ "input.mime_type",
2600
+ "output.mime_type",
2601
+ "llm.input_messages",
2602
+ "llm.output_messages",
2603
+ "llm.prompts",
2604
+ "llm.completions",
2605
+ "retrieval.documents",
2606
+ "reranker.input_documents",
2607
+ "reranker.output_documents",
2608
+ "document.content",
2609
+ "gen_ai.prompt",
2610
+ "gen_ai.completion",
2611
+ "gen_ai.input.messages",
2612
+ "gen_ai.output.messages"
2613
+ ];
2614
+ var OTLP_SPAN_KEYS = /* @__PURE__ */ new Set([
2615
+ "traceId",
2616
+ "spanId",
2617
+ "parentSpanId",
2618
+ "name",
2619
+ "kind",
2620
+ "startTimeUnixNano",
2621
+ "endTimeUnixNano",
2622
+ "attributes",
2623
+ "events",
2624
+ "status",
2625
+ "droppedAttributesCount",
2626
+ "droppedEventsCount",
2627
+ "droppedLinksCount",
2628
+ "links",
2629
+ "flags"
2630
+ ]);
2631
+ var TraceReadError = class extends Error {
2632
+ code;
2633
+ warnings;
2634
+ constructor(code, message, warnings = []) {
2635
+ super(message);
2636
+ this.name = "TraceReadError";
2637
+ this.code = code;
2638
+ this.warnings = warnings;
2639
+ }
2640
+ };
2641
+ function normalizeCandidate(reader, candidate) {
2642
+ const confidence = Number.isFinite(candidate.confidence) ? Math.max(0, Math.min(1, candidate.confidence)) : 0;
2643
+ return {
2644
+ ...candidate,
2645
+ format: candidate.format || reader.format,
2646
+ confidence,
2647
+ readerName: candidate.readerName ?? reader.name
2648
+ };
2649
+ }
2650
+ function sortCandidates(candidates) {
2651
+ return [...candidates].sort((a, b) => {
2652
+ if (b.confidence !== a.confidence) return b.confidence - a.confidence;
2653
+ return a.format.localeCompare(b.format);
2654
+ });
2655
+ }
2656
+ function collectWarnings(candidates) {
2657
+ return candidates.flatMap((candidate) => candidate.warnings ?? []);
2658
+ }
2659
+ function dedupeWarnings(warnings) {
2660
+ const seen = /* @__PURE__ */ new Set();
2661
+ const out = [];
2662
+ for (const warning of warnings) {
2663
+ const key = [
2664
+ warning.code,
2665
+ warning.message,
2666
+ warning.severity ?? "",
2667
+ warning.sourceFile ?? "",
2668
+ warning.line ?? "",
2669
+ warning.field ?? ""
2670
+ ].join("\0");
2671
+ if (seen.has(key)) continue;
2672
+ seen.add(key);
2673
+ out.push(warning);
2674
+ }
2675
+ return out;
2676
+ }
2677
+ function attachSingleSourceFile(warnings, resolved) {
2678
+ if (resolved.sourceFiles.length !== 1) return [...warnings];
2679
+ const [sourceFile] = resolved.sourceFiles;
2680
+ return warnings.map((warning) => ({
2681
+ ...warning,
2682
+ sourceFile: warning.sourceFile ?? sourceFile
2683
+ }));
2684
+ }
2685
+ function findReaderByFormat(format, readers) {
2686
+ return readers.find((reader) => reader.format === format);
2687
+ }
2688
+ async function jsonlFilesInDirectory(dirPath) {
2689
+ const entries = await promises.readdir(dirPath, { withFileTypes: true });
2690
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => path__default.default.join(dirPath, entry.name)).sort((a, b) => a.localeCompare(b));
2691
+ }
2692
+ async function resolveInput(input) {
2693
+ const cached = resolvedInputCache.get(input);
2694
+ if (cached) return cached;
2695
+ const promise = resolveInputUncached(input);
2696
+ resolvedInputCache.set(input, promise);
2697
+ return promise;
2698
+ }
2699
+ function assertInputWithinBounds(content, sourceFile) {
2700
+ const bytes = Buffer.byteLength(content, "utf8");
2701
+ if (bytes <= DEFAULT_MAX_TRACE_INPUT_BYTES) return;
2702
+ throw new TraceReadError("unsupported_format", "Trace input exceeds the local reader size limit.", [
2703
+ {
2704
+ code: "input_too_large",
2705
+ message: `Trace input is ${bytes} bytes; max is ${DEFAULT_MAX_TRACE_INPUT_BYTES} bytes.`,
2706
+ severity: "error",
2707
+ ...sourceFile !== void 0 ? { sourceFile } : {}
2708
+ }
2709
+ ]);
2710
+ }
2711
+ async function resolveInputUncached(input) {
2712
+ if (input.type === "string") {
2713
+ assertInputWithinBounds(input.content);
2714
+ return { content: input.content, sourceFiles: [] };
2715
+ }
2716
+ if (input.type === "buffer") {
2717
+ const content = input.content.toString("utf-8");
2718
+ assertInputWithinBounds(content);
2719
+ return { content, sourceFiles: [] };
2720
+ }
2721
+ if (input.type === "file") {
2722
+ const content = await promises.readFile(input.path, "utf-8");
2723
+ assertInputWithinBounds(content, input.path);
2724
+ return { content, sourceFiles: [input.path] };
2725
+ }
2726
+ if (input.type === "directory") {
2727
+ const files = await jsonlFilesInDirectory(input.path);
2728
+ const parts = await Promise.all(
2729
+ files.map(async (file) => (await promises.readFile(file, "utf-8")).trimEnd())
2730
+ );
2731
+ const content = parts.filter((part) => part.trim() !== "").join("\n");
2732
+ assertInputWithinBounds(content, input.path);
2733
+ return {
2734
+ content,
2735
+ sourceFiles: files
2736
+ };
2737
+ }
2738
+ return void 0;
2739
+ }
2740
+ function detectJsonlFormat(content) {
2741
+ let saw01 = false;
2742
+ let saw02 = false;
2743
+ let saw10 = false;
2744
+ let validRows = 0;
2745
+ let invalidJsonRows = 0;
2746
+ let unknownSchemaRows = 0;
2747
+ let firstInvalidJsonLine;
2748
+ let firstUnknownSchemaLine;
2749
+ let lineNumber = 0;
2750
+ for (const line of content.split(/\r?\n/)) {
2751
+ lineNumber += 1;
2752
+ const trimmed = line.trim();
2753
+ if (trimmed === "") continue;
2754
+ let parsed;
2755
+ try {
2756
+ parsed = JSON.parse(trimmed);
2757
+ } catch {
2758
+ invalidJsonRows += 1;
2759
+ firstInvalidJsonLine ??= lineNumber;
2760
+ continue;
2761
+ }
2762
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "schemaVersion" in parsed) {
2763
+ const version = parsed.schemaVersion;
2764
+ if (version === "0.1") {
2765
+ saw01 = true;
2766
+ validRows += 1;
2767
+ continue;
2768
+ }
2769
+ if (version === "0.2") {
2770
+ saw02 = true;
2771
+ validRows += 1;
2772
+ continue;
2773
+ }
2774
+ if (version === "1.0") {
2775
+ saw10 = true;
2776
+ validRows += 1;
2777
+ continue;
2778
+ }
2779
+ }
2780
+ unknownSchemaRows += 1;
2781
+ firstUnknownSchemaLine ??= lineNumber;
2782
+ }
2783
+ const warnings = [];
2784
+ if (invalidJsonRows > 0) {
2785
+ warnings.push({
2786
+ code: "invalid_jsonl_rows",
2787
+ message: `Skipped ${invalidJsonRows} invalid JSONL row(s) during format detection.`,
2788
+ severity: "warning",
2789
+ ...firstInvalidJsonLine !== void 0 ? { line: firstInvalidJsonLine } : {}
2790
+ });
2791
+ }
2792
+ if (unknownSchemaRows > 0) {
2793
+ warnings.push({
2794
+ code: "unknown_schema_rows",
2795
+ message: `Skipped ${unknownSchemaRows} row(s) with unknown schemaVersion during format detection.`,
2796
+ severity: "warning",
2797
+ ...firstUnknownSchemaLine !== void 0 ? { line: firstUnknownSchemaLine } : {}
2798
+ });
2799
+ }
2800
+ let format = "empty";
2801
+ const seenFormats = [saw01, saw02, saw10].filter(Boolean).length;
2802
+ if (seenFormats > 1) format = "mixed";
2803
+ else if (saw01) format = "0.1";
2804
+ else if (saw02) format = "0.2";
2805
+ else if (saw10) format = "1.0";
2806
+ return { format, validRows, warnings };
2807
+ }
2808
+ function agentInspectFormatLabel(format) {
2809
+ switch (format) {
2810
+ case "0.1":
2811
+ return "agent-inspect-v0.1-jsonl";
2812
+ case "0.2":
2813
+ return "agent-inspect-v0.2-jsonl";
2814
+ case "1.0":
2815
+ return "agent-inspect-v1.0-jsonl";
2816
+ case "mixed":
2817
+ return "agent-inspect-mixed-jsonl";
2818
+ default:
2819
+ return "agent-inspect-jsonl";
2820
+ }
2821
+ }
2822
+ function persistedEventsForParsedTrace(parsed) {
2823
+ if ((parsed.format === "0.2" || parsed.format === "1.0") && parsed.persisted.length > 0) {
2824
+ return [...parsed.persisted];
2825
+ }
2826
+ if (parsed.format === "mixed" && parsed.rows.length > 0) {
2827
+ return parsed.rows.map((row, index) => {
2828
+ if (row.format === "0.2" || row.format === "1.0") return row.event;
2829
+ return traceEventToPersistedInspectEvent(row.event, {
2830
+ eventIndex: index,
2831
+ sourceName: "agent-inspect-jsonl-reader"
2832
+ });
2833
+ });
2834
+ }
2835
+ return traceEventsToPersistedInspectEvents(parsed.events, {
2836
+ sourceName: "agent-inspect-jsonl-reader"
2837
+ });
2838
+ }
2839
+ function isRecord6(value) {
2840
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2841
+ }
2842
+ function isNonEmptyString3(value) {
2843
+ return typeof value === "string" && value.trim() !== "";
2844
+ }
2845
+ function readStringField(record, keys) {
2846
+ for (const key of keys) {
2847
+ const value = record[key];
2848
+ if (isNonEmptyString3(value)) return value;
2849
+ }
2850
+ return void 0;
2851
+ }
2852
+ function readRecordField(record, key) {
2853
+ const value = record[key];
2854
+ return isRecord6(value) ? value : void 0;
2855
+ }
2856
+ function parseJsonDocument(content) {
2857
+ return JSON.parse(content);
2858
+ }
2859
+ function looksLikeOpenInferenceSpan(value) {
2860
+ if (!isRecord6(value)) return false;
2861
+ const attributes = readRecordField(value, "attributes");
2862
+ 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);
2863
+ }
2864
+ function extractOpenInferenceDocument(root) {
2865
+ const warnings = [];
2866
+ const unsupportedFields = [];
2867
+ if (Array.isArray(root)) {
2868
+ const spans = root.filter(looksLikeOpenInferenceSpan);
2869
+ if (spans.length === 0) return void 0;
2870
+ if (spans.length !== root.length) {
2871
+ warnings.push({
2872
+ code: "openinference_skipped_items",
2873
+ message: "Skipped non-span item(s) in OpenInference span array.",
2874
+ severity: "warning"
2875
+ });
2876
+ }
2877
+ return {
2878
+ spans,
2879
+ confidence: 0.82,
2880
+ description: "OpenInference span array",
2881
+ warnings,
2882
+ unsupportedFields
2883
+ };
2884
+ }
2885
+ if (!isRecord6(root)) return void 0;
2886
+ const rootFormat = root.format;
2887
+ const rootCompatibility = root.compatibility;
2888
+ const version = typeof root.version === "string" && root.version.trim() !== "" ? root.version : void 0;
2889
+ if (Array.isArray(root.spans)) {
2890
+ const spans = root.spans.filter(looksLikeOpenInferenceSpan);
2891
+ if (spans.length === 0 && (rootFormat === "openinference" || rootCompatibility === "openinference-compatible")) {
2892
+ warnings.push({
2893
+ code: "openinference_no_valid_spans",
2894
+ message: "OpenInference document did not contain any valid spans.",
2895
+ severity: "error"
2896
+ });
2897
+ return {
2898
+ spans,
2899
+ confidence: 0.7,
2900
+ description: "Malformed OpenInference document",
2901
+ version,
2902
+ warnings,
2903
+ unsupportedFields
2904
+ };
2905
+ }
2906
+ if (spans.length === 0) return void 0;
2907
+ if (spans.length !== root.spans.length) {
2908
+ warnings.push({
2909
+ code: "openinference_skipped_spans",
2910
+ message: "Skipped invalid OpenInference span item(s).",
2911
+ severity: "warning"
2912
+ });
2913
+ }
2914
+ return {
2915
+ spans,
2916
+ confidence: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? 0.9 : 0.84,
2917
+ description: rootFormat === "openinference" || rootCompatibility === "openinference-compatible" ? "OpenInference document" : "OpenInference spans document",
2918
+ version,
2919
+ warnings,
2920
+ unsupportedFields
2921
+ };
2922
+ }
2923
+ if (Array.isArray(root.data)) {
2924
+ const spans = root.data.filter(looksLikeOpenInferenceSpan);
2925
+ if (spans.length === 0) return void 0;
2926
+ if (spans.length !== root.data.length) {
2927
+ warnings.push({
2928
+ code: "openinference_skipped_data_items",
2929
+ message: "Skipped non-span item(s) in OpenInference data array.",
2930
+ severity: "warning"
2931
+ });
2932
+ }
2933
+ return {
2934
+ spans,
2935
+ confidence: 0.8,
2936
+ description: "OpenInference data document",
2937
+ version,
2938
+ warnings,
2939
+ unsupportedFields
2940
+ };
2941
+ }
2942
+ if (looksLikeOpenInferenceSpan(root)) {
2943
+ return {
2944
+ spans: [root],
2945
+ confidence: 0.76,
2946
+ description: "OpenInference single span",
2947
+ version,
2948
+ warnings,
2949
+ unsupportedFields
2950
+ };
2951
+ }
2952
+ if (rootFormat === "openinference" || rootCompatibility === "openinference-compatible") {
2953
+ warnings.push({
2954
+ code: "openinference_missing_spans",
2955
+ message: "OpenInference document is missing a spans array.",
2956
+ severity: "error"
2957
+ });
2958
+ return {
2959
+ spans: [],
2960
+ confidence: 0.7,
2961
+ description: "Malformed OpenInference document",
2962
+ version,
2963
+ warnings,
2964
+ unsupportedFields
2965
+ };
2966
+ }
2967
+ return void 0;
2968
+ }
2969
+ function parseUnixNanoToIso(value) {
2970
+ if (typeof value === "bigint" && value >= 0n) {
2971
+ return new Date(Number(value / 1000000n)).toISOString();
2972
+ }
2973
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
2974
+ return new Date(Math.floor(value / 1e6)).toISOString();
2975
+ }
2976
+ if (typeof value === "string" && /^\d+$/.test(value)) {
2977
+ return new Date(Number(BigInt(value) / 1000000n)).toISOString();
2978
+ }
2979
+ return void 0;
2980
+ }
2981
+ function parseIsoTime(value) {
2982
+ if (!isNonEmptyString3(value)) return void 0;
2983
+ const ms = Date.parse(value);
2984
+ if (!Number.isFinite(ms)) return void 0;
2985
+ return new Date(ms).toISOString();
2986
+ }
2987
+ function readOpenInferenceTimestamp(span, nanoKeys, isoKeys) {
2988
+ for (const key of nanoKeys) {
2989
+ const iso = parseUnixNanoToIso(span[key]);
2990
+ if (iso !== void 0) return iso;
2991
+ }
2992
+ for (const key of isoKeys) {
2993
+ const iso = parseIsoTime(span[key]);
2994
+ if (iso !== void 0) return iso;
2995
+ }
2996
+ return void 0;
2997
+ }
2998
+ function durationBetweenIso(startedAt, endedAt) {
2999
+ if (startedAt === void 0 || endedAt === void 0) return void 0;
3000
+ const startMs = Date.parse(startedAt);
3001
+ const endMs = Date.parse(endedAt);
3002
+ if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs < startMs) {
3003
+ return void 0;
3004
+ }
3005
+ return endMs - startMs;
3006
+ }
3007
+ function isSensitiveOpenInferenceAttribute(key) {
3008
+ return OPENINFERENCE_SENSITIVE_ATTRIBUTE_KEYS.some(
3009
+ (sensitiveKey) => key === sensitiveKey || key.startsWith(`${sensitiveKey}.`) || key.endsWith(".message.content") || key.endsWith(".document.content")
3010
+ );
3011
+ }
3012
+ function summarizeAttributeValue(value) {
3013
+ if (typeof value === "string") {
3014
+ return { type: "string", length: value.length };
3015
+ }
3016
+ if (typeof value === "number") {
3017
+ return { type: "number", finite: Number.isFinite(value) };
3018
+ }
3019
+ if (typeof value === "boolean") {
3020
+ return { type: "boolean" };
3021
+ }
3022
+ if (Array.isArray(value)) {
3023
+ return { type: "array", length: value.length };
3024
+ }
3025
+ if (isRecord6(value)) {
3026
+ return { type: "object", keyCount: Object.keys(value).length };
3027
+ }
3028
+ if (value === null) {
3029
+ return { type: "null" };
3030
+ }
3031
+ return { type: typeof value };
3032
+ }
3033
+ function sanitizeOpenInferenceAttributes(attributes, pathPrefix) {
3034
+ const out = {};
3035
+ const warnings = [];
3036
+ const unsupportedFields = [];
3037
+ const summarizedKeys = [];
3038
+ for (const [key, value] of Object.entries(attributes)) {
3039
+ if (isSensitiveOpenInferenceAttribute(key)) {
3040
+ summarizedKeys.push(key);
3041
+ out[`${key}.summary`] = summarizeAttributeValue(value);
3042
+ unsupportedFields.push(`${pathPrefix}.attributes.${key}`);
3043
+ continue;
3044
+ }
3045
+ out[key] = value;
3046
+ }
3047
+ if (summarizedKeys.length > 0) {
3048
+ out["openinference.summarized_attributes"] = summarizedKeys;
3049
+ warnings.push({
3050
+ code: "openinference_sensitive_attribute_summarized",
3051
+ message: "OpenInference prompt/output/document attribute(s) were summarized instead of copied verbatim.",
3052
+ severity: "warning"
3053
+ });
3054
+ }
3055
+ return { attributes: out, warnings, unsupportedFields };
3056
+ }
3057
+ function mapOpenInferenceKind(span, attributes, pathPrefix) {
3058
+ const warnings = [];
3059
+ const agentInspectKind = attributes["agent_inspect.kind"];
3060
+ if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
3061
+ return { kind: agentInspectKind, warnings };
3062
+ }
3063
+ const rawKind = readStringField(span, ["kind", "span_kind", "spanKind"]) ?? (typeof attributes["openinference.span.kind"] === "string" ? attributes["openinference.span.kind"] : void 0);
3064
+ const normalized = rawKind?.toUpperCase();
3065
+ switch (normalized) {
3066
+ case "LLM":
3067
+ return { kind: "LLM", warnings };
3068
+ case "TOOL":
3069
+ return { kind: "TOOL", warnings };
3070
+ case "CHAIN":
3071
+ return { kind: "CHAIN", warnings };
3072
+ case "RETRIEVER":
3073
+ return { kind: "RETRIEVER", warnings };
3074
+ case "AGENT":
3075
+ return { kind: "AGENT", warnings };
3076
+ case "EMBEDDING":
3077
+ warnings.push({
3078
+ code: "openinference_kind_semantic_loss",
3079
+ message: "OpenInference EMBEDDING span kind mapped to AgentInspect LLM.",
3080
+ severity: "warning",
3081
+ field: `${pathPrefix}.attributes.openinference.span.kind`
3082
+ });
3083
+ return { kind: "LLM", warnings };
3084
+ case "RERANKER":
3085
+ warnings.push({
3086
+ code: "openinference_kind_semantic_loss",
3087
+ message: "OpenInference RERANKER span kind mapped to AgentInspect RETRIEVER.",
3088
+ severity: "warning",
3089
+ field: `${pathPrefix}.attributes.openinference.span.kind`
3090
+ });
3091
+ return { kind: "RETRIEVER", warnings };
3092
+ case "UNKNOWN":
3093
+ case void 0:
3094
+ warnings.push({
3095
+ code: "openinference_kind_unknown",
3096
+ message: "OpenInference span kind was missing or unknown; mapped to AgentInspect LOGIC.",
3097
+ severity: "warning",
3098
+ field: `${pathPrefix}.attributes.openinference.span.kind`
3099
+ });
3100
+ return { kind: "LOGIC", warnings };
3101
+ default:
3102
+ warnings.push({
3103
+ code: "openinference_kind_unsupported",
3104
+ message: `Unsupported OpenInference span kind "${rawKind}" mapped to AgentInspect LOGIC.`,
3105
+ severity: "warning",
3106
+ field: `${pathPrefix}.attributes.openinference.span.kind`
3107
+ });
3108
+ return { kind: "LOGIC", warnings };
3109
+ }
3110
+ }
3111
+ function mapOpenInferenceStatus(status) {
3112
+ if (!isRecord6(status)) return void 0;
3113
+ const rawCode = status.code;
3114
+ if (typeof rawCode !== "string") return void 0;
3115
+ switch (rawCode.toUpperCase()) {
3116
+ case "OK":
3117
+ return "ok";
3118
+ case "ERROR":
3119
+ return "error";
3120
+ case "UNSET":
3121
+ return "unknown";
3122
+ default:
3123
+ return "unknown";
3124
+ }
3125
+ }
3126
+ function readOpenInferenceTokenUsage(attributes) {
3127
+ const prompt = attributes["llm.token_count.prompt"];
3128
+ const completion = attributes["llm.token_count.completion"];
3129
+ const total = attributes["llm.token_count.total"];
3130
+ const cached = attributes["llm.token_count.prompt_details.cache_read"];
3131
+ const usage = {};
3132
+ if (typeof prompt === "number" && Number.isFinite(prompt) && prompt >= 0) {
3133
+ usage.input = prompt;
3134
+ }
3135
+ if (typeof completion === "number" && Number.isFinite(completion) && completion >= 0) {
3136
+ usage.output = completion;
3137
+ }
3138
+ if (typeof total === "number" && Number.isFinite(total) && total >= 0) {
3139
+ usage.total = total;
3140
+ }
3141
+ if (typeof cached === "number" && Number.isFinite(cached) && cached >= 0) {
3142
+ usage.cached = cached;
3143
+ }
3144
+ if (usage.total === void 0 && usage.input !== void 0 && usage.output !== void 0) {
3145
+ usage.total = usage.input + usage.output;
3146
+ }
3147
+ return Object.keys(usage).length > 0 ? usage : void 0;
3148
+ }
3149
+ function readOpenInferenceConfidence(attributes) {
3150
+ const confidence = attributes["agent_inspect.confidence"];
3151
+ if (confidence === "explicit" || confidence === "correlated" || confidence === "heuristic" || confidence === "unknown") {
3152
+ return confidence;
3153
+ }
3154
+ return "correlated";
3155
+ }
3156
+ function mapOpenInferenceSpan(span, index, version) {
3157
+ const pathPrefix = `spans[${index}]`;
3158
+ const warnings = [];
3159
+ const unsupportedFields = [];
3160
+ const rawAttributes = readRecordField(span, "attributes") ?? {};
3161
+ const sanitized = sanitizeOpenInferenceAttributes(rawAttributes, pathPrefix);
3162
+ warnings.push(...sanitized.warnings);
3163
+ unsupportedFields.push(...sanitized.unsupportedFields);
3164
+ const attributes = { ...sanitized.attributes };
3165
+ for (const [key, value] of Object.entries(span)) {
3166
+ if (OPENINFERENCE_SPAN_KEYS.has(key)) continue;
3167
+ unsupportedFields.push(`${pathPrefix}.${key}`);
3168
+ if (value === null || typeof value !== "object") {
3169
+ attributes[`openinference.${key}`] = value;
3170
+ } else {
3171
+ attributes[`openinference.${key}.summary`] = summarizeAttributeValue(value);
3172
+ warnings.push({
3173
+ code: "openinference_unsupported_field_summarized",
3174
+ message: `Unsupported OpenInference span field "${key}" was summarized.`,
3175
+ severity: "warning",
3176
+ field: `${pathPrefix}.${key}`
3177
+ });
3178
+ }
3179
+ }
3180
+ const traceId = readStringField(span, ["trace_id", "traceId"]) ?? `trace-${index}`;
3181
+ const spanId = readStringField(span, ["span_id", "spanId"]) ?? `span-${index}`;
3182
+ const parentSpanId = readStringField(span, ["parent_span_id", "parentSpanId"]);
3183
+ const name = readStringField(span, ["name"]) ?? spanId;
3184
+ const startedAt = readOpenInferenceTimestamp(
3185
+ span,
3186
+ ["start_time_unix_nano", "startTimeUnixNano"],
3187
+ ["start_time", "startTime"]
3188
+ );
3189
+ const endedAt = readOpenInferenceTimestamp(
3190
+ span,
3191
+ ["end_time_unix_nano", "endTimeUnixNano"],
3192
+ ["end_time", "endTime"]
3193
+ );
3194
+ const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
3195
+ if (startedAt === void 0) {
3196
+ warnings.push({
3197
+ code: "openinference_missing_start_time",
3198
+ message: "OpenInference span is missing a valid start time; using Unix epoch.",
3199
+ severity: "warning",
3200
+ field: `${pathPrefix}.start_time_unix_nano`
3201
+ });
3202
+ unsupportedFields.push(`${pathPrefix}.start_time_unix_nano`);
3203
+ }
3204
+ const { kind, warnings: kindWarnings } = mapOpenInferenceKind(
3205
+ span,
3206
+ rawAttributes,
3207
+ pathPrefix
3208
+ );
3209
+ warnings.push(...kindWarnings);
3210
+ const status = mapOpenInferenceStatus(span.status);
3211
+ const tokenUsage = readOpenInferenceTokenUsage(rawAttributes);
3212
+ const errorMessage = isRecord6(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
3213
+ const event = {
3214
+ schemaVersion: "0.2",
3215
+ eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
3216
+ runId: typeof rawAttributes["agent_inspect.run_id"] === "string" ? rawAttributes["agent_inspect.run_id"] : traceId,
3217
+ kind,
3218
+ name,
3219
+ timestamp,
3220
+ confidence: readOpenInferenceConfidence(rawAttributes),
3221
+ source: {
3222
+ type: "otel",
3223
+ name: "openinference",
3224
+ ...version !== void 0 ? { version } : {}
3225
+ },
3226
+ attributes,
3227
+ trace: {
3228
+ traceId,
3229
+ spanId,
3230
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
3231
+ }
3232
+ };
3233
+ if (status !== void 0) {
3234
+ event.status = status;
3235
+ }
3236
+ if (startedAt !== void 0) {
3237
+ event.startedAt = startedAt;
3238
+ }
3239
+ if (endedAt !== void 0) {
3240
+ event.endedAt = endedAt;
3241
+ }
3242
+ const durationMs = durationBetweenIso(startedAt, endedAt);
3243
+ if (durationMs !== void 0) {
3244
+ event.durationMs = durationMs;
3245
+ }
3246
+ if (tokenUsage !== void 0) {
3247
+ event.tokenUsage = tokenUsage;
3248
+ }
3249
+ if (status === "error") {
3250
+ event.error = {
3251
+ message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OpenInference span error"
3252
+ };
3253
+ }
3254
+ return {
3255
+ event,
3256
+ warnings,
3257
+ unsupportedFields,
3258
+ spanId,
3259
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
3260
+ };
3261
+ }
3262
+ function mapOpenInferenceEvents(document) {
3263
+ const mapped = document.spans.map(
3264
+ (span, index) => mapOpenInferenceSpan(span, index, document.version)
3265
+ );
3266
+ const spanIdToEventId = new Map(
3267
+ mapped.map((span) => [span.spanId, span.event.eventId])
3268
+ );
3269
+ for (const span of mapped) {
3270
+ if (span.parentSpanId === void 0) continue;
3271
+ span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
3272
+ }
3273
+ return {
3274
+ events: mapped.map((span) => span.event),
3275
+ warnings: mapped.flatMap((span) => span.warnings),
3276
+ unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
3277
+ };
3278
+ }
3279
+ var openInferenceJsonReader = {
3280
+ format: OPENINFERENCE_READER_FORMAT,
3281
+ name: "OpenInference JSON",
3282
+ async detect(input) {
3283
+ const resolved = await resolveInput(input);
3284
+ if (!resolved) return void 0;
3285
+ let parsed;
3286
+ try {
3287
+ parsed = parseJsonDocument(resolved.content);
3288
+ } catch {
3289
+ return void 0;
3290
+ }
3291
+ const document = extractOpenInferenceDocument(parsed);
3292
+ if (!document) return void 0;
3293
+ return {
3294
+ format: OPENINFERENCE_READER_FORMAT,
3295
+ confidence: document.confidence,
3296
+ readerName: "OpenInference JSON",
3297
+ description: document.description,
3298
+ warnings: attachSingleSourceFile(document.warnings, resolved)
3299
+ };
3300
+ },
3301
+ async read(input) {
3302
+ const resolved = await resolveInput(input);
3303
+ if (!resolved) {
3304
+ throw new TraceReadError(
3305
+ "unsupported_format",
3306
+ "OpenInference JSON reader requires file, string, or buffer input."
3307
+ );
3308
+ }
3309
+ let parsed;
3310
+ try {
3311
+ parsed = parseJsonDocument(resolved.content);
3312
+ } catch {
3313
+ throw new TraceReadError("unsupported_format", "OpenInference JSON input is not valid JSON.", [
3314
+ {
3315
+ code: "openinference_invalid_json",
3316
+ message: "OpenInference JSON reader could not parse the input as JSON.",
3317
+ severity: "error"
3318
+ }
3319
+ ]);
3320
+ }
3321
+ const document = extractOpenInferenceDocument(parsed);
3322
+ if (!document || document.spans.length === 0) {
3323
+ throw new TraceReadError(
3324
+ "unsupported_format",
3325
+ "No valid OpenInference spans found.",
3326
+ attachSingleSourceFile(
3327
+ document?.warnings ?? [
3328
+ {
3329
+ code: "openinference_no_valid_spans",
3330
+ message: "OpenInference JSON input did not contain valid spans.",
3331
+ severity: "error"
3332
+ }
3333
+ ],
3334
+ resolved
3335
+ )
3336
+ );
3337
+ }
3338
+ const mapped = mapOpenInferenceEvents(document);
3339
+ const warnings = attachSingleSourceFile(
3340
+ [...document.warnings, ...mapped.warnings],
3341
+ resolved
3342
+ );
3343
+ const unsupportedFields = [
3344
+ ...document.unsupportedFields,
3345
+ ...mapped.unsupportedFields
3346
+ ].sort((a, b) => a.localeCompare(b));
3347
+ return {
3348
+ format: OPENINFERENCE_READER_FORMAT,
3349
+ events: mapped.events,
3350
+ runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
3351
+ warnings,
3352
+ unsupportedFields,
3353
+ sourceFiles: resolved.sourceFiles
3354
+ };
3355
+ }
3356
+ };
3357
+ function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
3358
+ if (!isRecord6(value)) {
3359
+ unsupportedFields.push(field);
3360
+ warnings.push({
3361
+ code: "otlp_attribute_value_invalid",
3362
+ message: "OTLP attribute value was not an AnyValue object.",
3363
+ severity: "warning",
3364
+ field
3365
+ });
3366
+ return void 0;
3367
+ }
3368
+ if (typeof value.stringValue === "string") return value.stringValue;
3369
+ if (typeof value.boolValue === "boolean") return value.boolValue;
3370
+ if (typeof value.intValue === "number" && Number.isFinite(value.intValue)) {
3371
+ return value.intValue;
3372
+ }
3373
+ if (typeof value.intValue === "string" && value.intValue.trim() !== "") {
3374
+ const n = Number(value.intValue);
3375
+ if (Number.isFinite(n)) return n;
3376
+ }
3377
+ if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
3378
+ return value.doubleValue;
3379
+ }
3380
+ if (isRecord6(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
3381
+ return value.arrayValue.values.map(
3382
+ (item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
3383
+ );
3384
+ }
3385
+ if (isRecord6(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
3386
+ const out = {};
3387
+ for (const [index, item] of value.kvlistValue.values.entries()) {
3388
+ if (!isRecord6(item) || typeof item.key !== "string") {
3389
+ unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
3390
+ continue;
3391
+ }
3392
+ out[item.key] = parseOtlpAnyValue(
3393
+ item.value,
3394
+ `${field}.kvlistValue.values[${index}].value`,
3395
+ warnings,
3396
+ unsupportedFields
3397
+ );
3398
+ }
3399
+ return out;
3400
+ }
3401
+ if (typeof value.bytesValue === "string") {
3402
+ unsupportedFields.push(field);
3403
+ warnings.push({
3404
+ code: "otlp_bytes_value_summarized",
3405
+ message: "OTLP bytesValue attribute was summarized instead of decoded.",
3406
+ severity: "warning",
3407
+ field
3408
+ });
3409
+ return { type: "bytes", length: value.bytesValue.length };
3410
+ }
3411
+ unsupportedFields.push(field);
3412
+ warnings.push({
3413
+ code: "otlp_attribute_value_unsupported",
3414
+ message: "OTLP attribute value used an unsupported AnyValue shape.",
3415
+ severity: "warning",
3416
+ field
3417
+ });
3418
+ return void 0;
3419
+ }
3420
+ function parseOtlpAttributes(value, pathPrefix) {
3421
+ const attributes = {};
3422
+ const warnings = [];
3423
+ const unsupportedFields = [];
3424
+ if (value === void 0) {
3425
+ return { attributes, warnings, unsupportedFields };
3426
+ }
3427
+ if (!Array.isArray(value)) {
3428
+ unsupportedFields.push(pathPrefix);
3429
+ warnings.push({
3430
+ code: "otlp_attributes_invalid",
3431
+ message: "OTLP attributes field was not an array.",
3432
+ severity: "warning",
3433
+ field: pathPrefix
3434
+ });
3435
+ return { attributes, warnings, unsupportedFields };
3436
+ }
3437
+ for (const [index, item] of value.entries()) {
3438
+ const field = `${pathPrefix}[${index}]`;
3439
+ if (!isRecord6(item) || typeof item.key !== "string") {
3440
+ unsupportedFields.push(field);
3441
+ warnings.push({
3442
+ code: "otlp_attribute_invalid",
3443
+ message: "Skipped OTLP attribute without a string key.",
3444
+ severity: "warning",
3445
+ field
3446
+ });
3447
+ continue;
3448
+ }
3449
+ const parsed = parseOtlpAnyValue(
3450
+ item.value,
3451
+ `${field}.value`,
3452
+ warnings,
3453
+ unsupportedFields
3454
+ );
3455
+ if (parsed !== void 0) {
3456
+ attributes[item.key] = parsed;
3457
+ }
3458
+ }
3459
+ return { attributes, warnings, unsupportedFields };
3460
+ }
3461
+ function looksLikeOtlpSpan(value) {
3462
+ return isRecord6(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
3463
+ }
3464
+ function extractOtlpDocument(root) {
3465
+ if (!isRecord6(root) || !Array.isArray(root.resourceSpans)) return void 0;
3466
+ const spans = [];
3467
+ const warnings = [];
3468
+ const unsupportedFields = [];
3469
+ for (const [resourceIndex, resourceSpan] of root.resourceSpans.entries()) {
3470
+ const resourcePath = `resourceSpans[${resourceIndex}]`;
3471
+ if (!isRecord6(resourceSpan)) {
3472
+ unsupportedFields.push(resourcePath);
3473
+ continue;
3474
+ }
3475
+ const resource = readRecordField(resourceSpan, "resource");
3476
+ const resourceParsed = parseOtlpAttributes(
3477
+ resource?.attributes,
3478
+ `${resourcePath}.resource.attributes`
3479
+ );
3480
+ warnings.push(...resourceParsed.warnings);
3481
+ unsupportedFields.push(...resourceParsed.unsupportedFields);
3482
+ if (!Array.isArray(resourceSpan.scopeSpans)) {
3483
+ unsupportedFields.push(`${resourcePath}.scopeSpans`);
3484
+ warnings.push({
3485
+ code: "otlp_scope_spans_missing",
3486
+ message: "OTLP resourceSpans entry did not contain a scopeSpans array.",
3487
+ severity: "warning",
3488
+ field: `${resourcePath}.scopeSpans`
3489
+ });
3490
+ continue;
3491
+ }
3492
+ for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
3493
+ const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
3494
+ if (!isRecord6(scopeSpan)) {
3495
+ unsupportedFields.push(scopePath);
3496
+ continue;
3497
+ }
3498
+ const scope = readRecordField(scopeSpan, "scope");
3499
+ const scopeParsed = parseOtlpAttributes(
3500
+ scope?.attributes,
3501
+ `${scopePath}.scope.attributes`
3502
+ );
3503
+ warnings.push(...scopeParsed.warnings);
3504
+ unsupportedFields.push(...scopeParsed.unsupportedFields);
3505
+ if (!Array.isArray(scopeSpan.spans)) {
3506
+ unsupportedFields.push(`${scopePath}.spans`);
3507
+ warnings.push({
3508
+ code: "otlp_spans_missing",
3509
+ message: "OTLP scopeSpans entry did not contain a spans array.",
3510
+ severity: "warning",
3511
+ field: `${scopePath}.spans`
3512
+ });
3513
+ continue;
3514
+ }
3515
+ for (const [spanIndex, span] of scopeSpan.spans.entries()) {
3516
+ const spanPath = `${scopePath}.spans[${spanIndex}]`;
3517
+ if (!looksLikeOtlpSpan(span)) {
3518
+ unsupportedFields.push(spanPath);
3519
+ warnings.push({
3520
+ code: "otlp_invalid_span",
3521
+ message: "Skipped OTLP span without required traceId, spanId, or name.",
3522
+ severity: "warning",
3523
+ field: spanPath
3524
+ });
3525
+ continue;
3526
+ }
3527
+ spans.push({
3528
+ span,
3529
+ resourceAttributes: resourceParsed.attributes,
3530
+ scopeAttributes: scopeParsed.attributes,
3531
+ scopeName: readStringField(scope ?? {}, ["name"]),
3532
+ scopeVersion: readStringField(scope ?? {}, ["version"]),
3533
+ pathPrefix: spanPath
3534
+ });
3535
+ }
3536
+ }
3537
+ }
3538
+ if (spans.length === 0) {
3539
+ warnings.push({
3540
+ code: "otlp_no_valid_spans",
3541
+ message: "OTLP JSON payload did not contain any valid spans.",
3542
+ severity: "error"
3543
+ });
3544
+ return {
3545
+ spans,
3546
+ confidence: 0.7,
3547
+ description: "Malformed OTLP JSON trace payload",
3548
+ warnings,
3549
+ unsupportedFields
3550
+ };
3551
+ }
3552
+ return {
3553
+ spans,
3554
+ confidence: 0.93,
3555
+ description: "OTLP JSON trace payload",
3556
+ warnings,
3557
+ unsupportedFields
3558
+ };
3559
+ }
3560
+ function mapOtlpStatus(status) {
3561
+ if (!isRecord6(status)) return void 0;
3562
+ const rawCode = status.code;
3563
+ if (typeof rawCode !== "string") return void 0;
3564
+ switch (rawCode.toUpperCase()) {
3565
+ case "STATUS_CODE_OK":
3566
+ case "OK":
3567
+ return "ok";
3568
+ case "STATUS_CODE_ERROR":
3569
+ case "ERROR":
3570
+ return "error";
3571
+ case "STATUS_CODE_UNSET":
3572
+ case "UNSET":
3573
+ return "unknown";
3574
+ default:
3575
+ return "unknown";
3576
+ }
3577
+ }
3578
+ function readOtlpKind(attributes, pathPrefix) {
3579
+ const warnings = [];
3580
+ const agentInspectKind = attributes["agent_inspect.kind"];
3581
+ if (agentInspectKind === "RUN" || agentInspectKind === "AGENT" || agentInspectKind === "LLM" || agentInspectKind === "TOOL" || agentInspectKind === "CHAIN" || agentInspectKind === "RETRIEVER" || agentInspectKind === "DECISION" || agentInspectKind === "RESULT" || agentInspectKind === "ERROR" || agentInspectKind === "LOGIC" || agentInspectKind === "LOG") {
3582
+ return { kind: agentInspectKind, warnings };
3583
+ }
3584
+ const operation = attributes["gen_ai.operation.name"];
3585
+ if (typeof operation === "string") {
3586
+ switch (operation) {
3587
+ case "generate_content":
3588
+ case "chat":
3589
+ return { kind: "LLM", warnings };
3590
+ case "execute_tool":
3591
+ return { kind: "TOOL", warnings };
3592
+ case "invoke_agent":
3593
+ return { kind: "AGENT", warnings };
3594
+ default:
3595
+ warnings.push({
3596
+ code: "otlp_gen_ai_operation_semantic_loss",
3597
+ message: `OTLP GenAI operation "${operation}" mapped to AgentInspect LOGIC.`,
3598
+ severity: "warning",
3599
+ field: `${pathPrefix}.attributes.gen_ai.operation.name`
3600
+ });
3601
+ return { kind: "LOGIC", warnings };
3602
+ }
3603
+ }
3604
+ warnings.push({
3605
+ code: "otlp_kind_unknown",
3606
+ message: "OTLP span had no AgentInspect kind or GenAI operation; mapped to LOGIC.",
3607
+ severity: "warning",
3608
+ field: `${pathPrefix}.attributes`
3609
+ });
3610
+ return { kind: "LOGIC", warnings };
3611
+ }
3612
+ function readOtlpTokenUsage(attributes) {
3613
+ const input = attributes["gen_ai.usage.input_tokens"];
3614
+ const output = attributes["gen_ai.usage.output_tokens"];
3615
+ const usage = {};
3616
+ if (typeof input === "number" && Number.isFinite(input) && input >= 0) {
3617
+ usage.input = input;
3618
+ }
3619
+ if (typeof output === "number" && Number.isFinite(output) && output >= 0) {
3620
+ usage.output = output;
3621
+ }
3622
+ if (usage.input !== void 0 && usage.output !== void 0) {
3623
+ usage.total = usage.input + usage.output;
3624
+ }
3625
+ return Object.keys(usage).length > 0 ? usage : void 0;
3626
+ }
3627
+ function readOtlpConfidence(attributes) {
3628
+ return readOpenInferenceConfidence(attributes);
3629
+ }
3630
+ function sanitizeOtlpAttributes(attributes, pathPrefix) {
3631
+ const ownerPath = pathPrefix.endsWith(".attributes") ? pathPrefix.slice(0, -".attributes".length) : pathPrefix;
3632
+ const sanitized = sanitizeOpenInferenceAttributes(attributes, ownerPath);
3633
+ return {
3634
+ ...sanitized,
3635
+ warnings: sanitized.warnings.map(
3636
+ (warning) => warning.code === "openinference_sensitive_attribute_summarized" ? {
3637
+ ...warning,
3638
+ code: "otlp_sensitive_attribute_summarized",
3639
+ message: "OTLP prompt/output/document attribute(s) were summarized instead of copied verbatim."
3640
+ } : warning
3641
+ )
3642
+ };
3643
+ }
3644
+ function mapOtlpEvents(value, pathPrefix) {
3645
+ const warnings = [];
3646
+ const unsupportedFields = [];
3647
+ if (value === void 0) return { warnings, unsupportedFields };
3648
+ if (!Array.isArray(value)) {
3649
+ unsupportedFields.push(pathPrefix);
3650
+ warnings.push({
3651
+ code: "otlp_events_invalid",
3652
+ message: "OTLP events field was not an array.",
3653
+ severity: "warning",
3654
+ field: pathPrefix
3655
+ });
3656
+ return { warnings, unsupportedFields };
3657
+ }
3658
+ const events = [];
3659
+ for (const [index, event] of value.entries()) {
3660
+ const eventPath = `${pathPrefix}[${index}]`;
3661
+ if (!isRecord6(event)) {
3662
+ unsupportedFields.push(eventPath);
3663
+ continue;
3664
+ }
3665
+ const parsedAttributes = parseOtlpAttributes(
3666
+ event.attributes,
3667
+ `${eventPath}.attributes`
3668
+ );
3669
+ warnings.push(...parsedAttributes.warnings);
3670
+ unsupportedFields.push(...parsedAttributes.unsupportedFields);
3671
+ const sanitized = sanitizeOtlpAttributes(
3672
+ parsedAttributes.attributes,
3673
+ `${eventPath}.attributes`
3674
+ );
3675
+ warnings.push(...sanitized.warnings);
3676
+ unsupportedFields.push(...sanitized.unsupportedFields);
3677
+ const out = {};
3678
+ const name = readStringField(event, ["name"]);
3679
+ if (name !== void 0) {
3680
+ out.name = name;
3681
+ }
3682
+ const timestamp = parseUnixNanoToIso(event.timeUnixNano);
3683
+ if (timestamp !== void 0) {
3684
+ out.timestamp = timestamp;
3685
+ } else if (event.timeUnixNano !== void 0) {
3686
+ unsupportedFields.push(`${eventPath}.timeUnixNano`);
3687
+ warnings.push({
3688
+ code: "otlp_event_timestamp_invalid",
3689
+ message: "OTLP event timeUnixNano could not be parsed.",
3690
+ severity: "warning",
3691
+ field: `${eventPath}.timeUnixNano`
3692
+ });
3693
+ }
3694
+ if (Object.keys(sanitized.attributes).length > 0) {
3695
+ out.attributes = sanitized.attributes;
3696
+ }
3697
+ events.push(out);
3698
+ }
3699
+ return {
3700
+ events: events.length > 0 ? events : void 0,
3701
+ warnings,
3702
+ unsupportedFields
3703
+ };
3704
+ }
3705
+ function mapOtlpSpan(context) {
3706
+ const { span, pathPrefix } = context;
3707
+ const warnings = [];
3708
+ const unsupportedFields = [];
3709
+ const parsedSpanAttributes = parseOtlpAttributes(
3710
+ span.attributes,
3711
+ `${pathPrefix}.attributes`
3712
+ );
3713
+ warnings.push(...parsedSpanAttributes.warnings);
3714
+ unsupportedFields.push(...parsedSpanAttributes.unsupportedFields);
3715
+ const sanitizedSpanAttributes = sanitizeOtlpAttributes(
3716
+ parsedSpanAttributes.attributes,
3717
+ `${pathPrefix}.attributes`
3718
+ );
3719
+ warnings.push(...sanitizedSpanAttributes.warnings);
3720
+ unsupportedFields.push(...sanitizedSpanAttributes.unsupportedFields);
3721
+ const attributes = {
3722
+ ...sanitizedSpanAttributes.attributes
3723
+ };
3724
+ for (const [key, value] of Object.entries(context.resourceAttributes)) {
3725
+ attributes[`resource.${key}`] = value;
3726
+ }
3727
+ for (const [key, value] of Object.entries(context.scopeAttributes)) {
3728
+ attributes[`scope.${key}`] = value;
3729
+ }
3730
+ if (context.scopeName !== void 0) {
3731
+ attributes["scope.name"] = context.scopeName;
3732
+ }
3733
+ if (context.scopeVersion !== void 0) {
3734
+ attributes["scope.version"] = context.scopeVersion;
3735
+ }
3736
+ for (const [key, value] of Object.entries(span)) {
3737
+ if (OTLP_SPAN_KEYS.has(key)) continue;
3738
+ unsupportedFields.push(`${pathPrefix}.${key}`);
3739
+ if (value === null || typeof value !== "object") {
3740
+ attributes[`otlp.${key}`] = value;
3741
+ } else {
3742
+ attributes[`otlp.${key}.summary`] = summarizeAttributeValue(value);
3743
+ warnings.push({
3744
+ code: "otlp_unsupported_field_summarized",
3745
+ message: `Unsupported OTLP span field "${key}" was summarized.`,
3746
+ severity: "warning",
3747
+ field: `${pathPrefix}.${key}`
3748
+ });
3749
+ }
3750
+ }
3751
+ for (const key of [
3752
+ "droppedAttributesCount",
3753
+ "droppedEventsCount",
3754
+ "droppedLinksCount",
3755
+ "links"
3756
+ ]) {
3757
+ if (span[key] !== void 0) {
3758
+ unsupportedFields.push(`${pathPrefix}.${key}`);
3759
+ warnings.push({
3760
+ code: "otlp_span_field_not_mapped",
3761
+ message: `OTLP span field "${key}" is not represented in AgentInspect events.`,
3762
+ severity: "warning",
3763
+ field: `${pathPrefix}.${key}`
3764
+ });
3765
+ }
3766
+ }
3767
+ const events = mapOtlpEvents(span.events, `${pathPrefix}.events`);
3768
+ warnings.push(...events.warnings);
3769
+ unsupportedFields.push(...events.unsupportedFields);
3770
+ if (events.events !== void 0) {
3771
+ attributes["otlp.events"] = events.events;
3772
+ }
3773
+ const traceId = readStringField(span, ["traceId"]) ?? "trace-unknown";
3774
+ const spanId = readStringField(span, ["spanId"]) ?? "span-unknown";
3775
+ const parentSpanId = readStringField(span, ["parentSpanId"]);
3776
+ const startedAt = readOpenInferenceTimestamp(
3777
+ span,
3778
+ ["startTimeUnixNano"],
3779
+ []
3780
+ );
3781
+ const endedAt = readOpenInferenceTimestamp(span, ["endTimeUnixNano"], []);
3782
+ const timestamp = startedAt ?? "1970-01-01T00:00:00.000Z";
3783
+ if (startedAt === void 0) {
3784
+ unsupportedFields.push(`${pathPrefix}.startTimeUnixNano`);
3785
+ warnings.push({
3786
+ code: "otlp_missing_start_time",
3787
+ message: "OTLP span is missing a valid startTimeUnixNano; using Unix epoch.",
3788
+ severity: "warning",
3789
+ field: `${pathPrefix}.startTimeUnixNano`
3790
+ });
3791
+ }
3792
+ const { kind, warnings: kindWarnings } = readOtlpKind(
3793
+ parsedSpanAttributes.attributes,
3794
+ pathPrefix
3795
+ );
3796
+ warnings.push(...kindWarnings);
3797
+ const status = mapOtlpStatus(span.status);
3798
+ const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
3799
+ const errorMessage = isRecord6(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
3800
+ const event = {
3801
+ schemaVersion: "0.2",
3802
+ eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
3803
+ runId: typeof parsedSpanAttributes.attributes["agent_inspect.run_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.run_id"] : traceId,
3804
+ kind,
3805
+ name: readStringField(span, ["name"]) ?? spanId,
3806
+ timestamp,
3807
+ confidence: readOtlpConfidence(parsedSpanAttributes.attributes),
3808
+ source: {
3809
+ type: "otel",
3810
+ name: context.scopeName ?? (typeof context.resourceAttributes["service.name"] === "string" ? context.resourceAttributes["service.name"] : "otlp-json"),
3811
+ ...context.scopeVersion !== void 0 ? { version: context.scopeVersion } : {}
3812
+ },
3813
+ attributes,
3814
+ trace: {
3815
+ traceId,
3816
+ spanId,
3817
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
3818
+ }
3819
+ };
3820
+ if (status !== void 0) {
3821
+ event.status = status;
3822
+ }
3823
+ if (startedAt !== void 0) {
3824
+ event.startedAt = startedAt;
3825
+ }
3826
+ if (endedAt !== void 0) {
3827
+ event.endedAt = endedAt;
3828
+ }
3829
+ const durationMs = durationBetweenIso(startedAt, endedAt);
3830
+ if (durationMs !== void 0) {
3831
+ event.durationMs = durationMs;
3832
+ }
3833
+ if (tokenUsage !== void 0) {
3834
+ event.tokenUsage = tokenUsage;
3835
+ }
3836
+ if (status === "error") {
3837
+ event.error = {
3838
+ message: errorMessage !== void 0 && errorMessage.trim() !== "" ? errorMessage : "OTLP span error"
3839
+ };
3840
+ }
3841
+ return {
3842
+ event,
3843
+ warnings,
3844
+ unsupportedFields,
3845
+ spanId,
3846
+ ...parentSpanId !== void 0 ? { parentSpanId } : {}
3847
+ };
3848
+ }
3849
+ function mapOtlpEventsToPersisted(document) {
3850
+ const mapped = document.spans.map((span) => mapOtlpSpan(span));
3851
+ const spanIdToEventId = new Map(
3852
+ mapped.map((span) => [span.spanId, span.event.eventId])
3853
+ );
3854
+ for (const span of mapped) {
3855
+ if (span.parentSpanId === void 0) continue;
3856
+ span.event.parentId = spanIdToEventId.get(span.parentSpanId) ?? span.parentSpanId;
3857
+ }
3858
+ return {
3859
+ events: mapped.map((span) => span.event),
3860
+ warnings: mapped.flatMap((span) => span.warnings),
3861
+ unsupportedFields: mapped.flatMap((span) => span.unsupportedFields)
3862
+ };
3863
+ }
3864
+ var otlpJsonReader = {
3865
+ format: OTLP_READER_FORMAT,
3866
+ name: "OTLP JSON",
3867
+ async detect(input) {
3868
+ const resolved = await resolveInput(input);
3869
+ if (!resolved) return void 0;
3870
+ let parsed;
3871
+ try {
3872
+ parsed = parseJsonDocument(resolved.content);
3873
+ } catch {
3874
+ return void 0;
3875
+ }
3876
+ const document = extractOtlpDocument(parsed);
3877
+ if (!document) return void 0;
3878
+ return {
3879
+ format: OTLP_READER_FORMAT,
3880
+ confidence: document.confidence,
3881
+ readerName: "OTLP JSON",
3882
+ description: document.description,
3883
+ warnings: attachSingleSourceFile(document.warnings, resolved)
3884
+ };
3885
+ },
3886
+ async read(input) {
3887
+ const resolved = await resolveInput(input);
3888
+ if (!resolved) {
3889
+ throw new TraceReadError(
3890
+ "unsupported_format",
3891
+ "OTLP JSON reader requires file, string, or buffer input."
3892
+ );
3893
+ }
3894
+ let parsed;
3895
+ try {
3896
+ parsed = parseJsonDocument(resolved.content);
3897
+ } catch {
3898
+ throw new TraceReadError("unsupported_format", "OTLP JSON input is not valid JSON.", [
3899
+ {
3900
+ code: "otlp_invalid_json",
3901
+ message: "OTLP JSON reader could not parse the input as JSON.",
3902
+ severity: "error"
3903
+ }
3904
+ ]);
3905
+ }
3906
+ const document = extractOtlpDocument(parsed);
3907
+ if (!document || document.spans.length === 0) {
3908
+ throw new TraceReadError(
3909
+ "unsupported_format",
3910
+ "No valid OTLP spans found.",
3911
+ attachSingleSourceFile(
3912
+ document?.warnings ?? [
3913
+ {
3914
+ code: "otlp_no_valid_spans",
3915
+ message: "OTLP JSON input did not contain valid spans.",
3916
+ severity: "error"
3917
+ }
3918
+ ],
3919
+ resolved
3920
+ )
3921
+ );
3922
+ }
3923
+ const mapped = mapOtlpEventsToPersisted(document);
3924
+ const warnings = attachSingleSourceFile(
3925
+ [...document.warnings, ...mapped.warnings],
3926
+ resolved
3927
+ );
3928
+ const unsupportedFields = [
3929
+ ...document.unsupportedFields,
3930
+ ...mapped.unsupportedFields
3931
+ ].sort((a, b) => a.localeCompare(b));
3932
+ return {
3933
+ format: OTLP_READER_FORMAT,
3934
+ events: mapped.events,
3935
+ runs: persistedInspectEventsToRunTrees(mapped.events, { skipInvalid: true }),
3936
+ warnings,
3937
+ unsupportedFields,
3938
+ sourceFiles: resolved.sourceFiles
3939
+ };
3940
+ }
3941
+ };
3942
+ var agentInspectJsonlReader = {
3943
+ format: "agent-inspect-jsonl",
3944
+ name: "AgentInspect JSONL",
3945
+ async detect(input) {
3946
+ const resolved = await resolveInput(input);
3947
+ if (!resolved) return void 0;
3948
+ const detected = detectJsonlFormat(resolved.content);
3949
+ if (detected.validRows === 0 || detected.format === "empty") {
3950
+ return void 0;
3951
+ }
3952
+ return {
3953
+ format: "agent-inspect-jsonl",
3954
+ confidence: 0.95,
3955
+ readerName: "AgentInspect JSONL",
3956
+ description: agentInspectFormatLabel(detected.format),
3957
+ warnings: attachSingleSourceFile(detected.warnings, resolved)
3958
+ };
3959
+ },
3960
+ async read(input) {
3961
+ const resolved = await resolveInput(input);
3962
+ if (!resolved) {
3963
+ throw new Error("AgentInspect JSONL reader requires file, directory, string, or buffer input.");
3964
+ }
3965
+ const parsed = parseTraceJsonl(resolved.content, { warnings: false });
3966
+ if (parsed.sourceEventCount === 0) {
3967
+ throw new Error("No valid AgentInspect JSONL events found.");
3968
+ }
3969
+ const events = persistedEventsForParsedTrace(parsed);
3970
+ return {
3971
+ format: agentInspectFormatLabel(parsed.format),
3972
+ events,
3973
+ runs: persistedInspectEventsToRunTrees(events, { skipInvalid: true }),
3974
+ warnings: parsed.format === "mixed" ? attachSingleSourceFile(
3975
+ [
3976
+ {
3977
+ code: "mixed_agent_inspect_jsonl",
3978
+ message: "Trace input mixes schemaVersion 0.1 and 0.2 rows; events were normalized for reading.",
3979
+ severity: "warning"
3980
+ }
3981
+ ],
3982
+ resolved
3983
+ ) : [],
3984
+ unsupportedFields: [],
3985
+ sourceFiles: resolved.sourceFiles
3986
+ };
3987
+ }
3988
+ };
3989
+ var DEFAULT_TRACE_READERS = [
3990
+ agentInspectJsonlReader,
3991
+ openInferenceJsonReader,
3992
+ otlpJsonReader
3993
+ ];
3994
+ async function detectTraceFormat(input, options = {}) {
3995
+ const readers = options.readers ?? DEFAULT_TRACE_READERS;
3996
+ if (options.format !== void 0) {
3997
+ const reader = findReaderByFormat(options.format, readers);
3998
+ if (!reader) {
3999
+ return {
4000
+ status: "unsupported",
4001
+ candidates: [],
4002
+ warnings: [
4003
+ {
4004
+ code: "unsupported_format",
4005
+ message: `No trace reader is registered for format "${options.format}".`,
4006
+ severity: "error"
4007
+ }
4008
+ ]
4009
+ };
4010
+ }
4011
+ return {
4012
+ status: "detected",
4013
+ format: reader.format,
4014
+ candidates: [
4015
+ {
4016
+ format: reader.format,
4017
+ confidence: 1,
4018
+ readerName: reader.name,
4019
+ description: "Explicit format override"
4020
+ }
4021
+ ],
4022
+ warnings: []
4023
+ };
4024
+ }
4025
+ const candidates = [];
4026
+ const warnings = [];
4027
+ for (const reader of readers) {
4028
+ try {
4029
+ const candidate = await reader.detect(input);
4030
+ if (candidate !== void 0) {
4031
+ candidates.push(normalizeCandidate(reader, candidate));
4032
+ }
4033
+ } catch (error) {
4034
+ if (error instanceof TraceReadError) {
4035
+ warnings.push(...error.warnings);
4036
+ continue;
4037
+ }
4038
+ warnings.push({
4039
+ code: "reader_detect_failed",
4040
+ message: error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed during detection.`,
4041
+ severity: "warning"
4042
+ });
4043
+ }
4044
+ }
4045
+ const sorted = sortCandidates(
4046
+ candidates.filter((candidate) => candidate.confidence >= MIN_DETECTION_CONFIDENCE)
4047
+ );
4048
+ const candidateWarnings = collectWarnings(sorted);
4049
+ const lowConfidenceWarnings = candidates.length > sorted.length ? [
4050
+ {
4051
+ code: "low_confidence_candidates",
4052
+ message: `Ignored ${candidates.length - sorted.length} low-confidence format candidate(s).`,
4053
+ severity: "info"
4054
+ }
4055
+ ] : [];
4056
+ const allWarnings = dedupeWarnings([
4057
+ ...warnings,
4058
+ ...candidateWarnings,
4059
+ ...lowConfidenceWarnings
4060
+ ]);
4061
+ if (sorted.length === 0) {
4062
+ return {
4063
+ status: "unsupported",
4064
+ candidates: [],
4065
+ warnings: allWarnings
4066
+ };
4067
+ }
4068
+ const [best, second] = sorted;
4069
+ if (second !== void 0 && best.confidence - second.confidence <= AMBIGUOUS_CONFIDENCE_DELTA) {
4070
+ return {
4071
+ status: "ambiguous",
4072
+ candidates: sorted,
4073
+ warnings: [
4074
+ ...allWarnings,
4075
+ {
4076
+ code: "ambiguous_format_candidates",
4077
+ message: `Top trace format candidates are within ${AMBIGUOUS_CONFIDENCE_DELTA} confidence.`,
4078
+ severity: "warning"
4079
+ }
4080
+ ]
4081
+ };
4082
+ }
4083
+ return {
4084
+ status: "detected",
4085
+ format: best.format,
4086
+ candidates: sorted,
4087
+ warnings: allWarnings
4088
+ };
4089
+ }
4090
+ async function readTrace(input, options = {}) {
4091
+ const readers = options.readers ?? DEFAULT_TRACE_READERS;
4092
+ const detection = await detectTraceFormat(input, options);
4093
+ if (detection.status === "unsupported" || detection.format === void 0) {
4094
+ throw new TraceReadError(
4095
+ "unsupported_format",
4096
+ "No trace reader could detect the input format.",
4097
+ detection.warnings
4098
+ );
4099
+ }
4100
+ if (detection.status === "ambiguous") {
4101
+ throw new TraceReadError(
4102
+ "ambiguous_format",
4103
+ "Multiple trace readers matched the input with equal confidence.",
4104
+ detection.warnings
4105
+ );
4106
+ }
4107
+ const reader = findReaderByFormat(detection.format, readers);
4108
+ if (!reader) {
4109
+ throw new TraceReadError(
4110
+ "unsupported_format",
4111
+ `No trace reader is registered for format "${detection.format}".`,
4112
+ detection.warnings
4113
+ );
4114
+ }
4115
+ try {
4116
+ const result = await reader.read(input, { format: detection.format });
4117
+ return {
4118
+ ...result,
4119
+ format: result.format || detection.format,
4120
+ warnings: [...detection.warnings, ...result.warnings]
4121
+ };
4122
+ } catch (error) {
4123
+ if (error instanceof TraceReadError) {
4124
+ throw new TraceReadError(
4125
+ error.code,
4126
+ error.message,
4127
+ dedupeWarnings([...detection.warnings, ...error.warnings])
4128
+ );
4129
+ }
4130
+ throw new TraceReadError(
4131
+ "reader_failed",
4132
+ error instanceof Error && error.message.trim() !== "" ? error.message : `Trace reader "${reader.format}" failed.`,
4133
+ detection.warnings
4134
+ );
4135
+ }
4136
+ }
4137
+ function openTrace(input, options = {}) {
4138
+ return readTrace(input, options);
4139
+ }
4140
+
4141
+ // packages/mcp-server/src/tools.ts
4142
+ var READ_ONLY_TOOLS = [
4143
+ {
4144
+ name: "list_traces",
4145
+ description: "List local trace runs in the configured trace directory.",
4146
+ inputSchema: { type: "object", properties: {} }
4147
+ },
4148
+ {
4149
+ name: "read_trace",
4150
+ description: "Read a bounded trace projection for one run id.",
4151
+ inputSchema: {
4152
+ type: "object",
4153
+ properties: { runId: { type: "string" } },
4154
+ required: ["runId"]
4155
+ }
4156
+ },
4157
+ {
4158
+ name: "search_traces",
4159
+ description: "Search traces deterministically by query string.",
4160
+ inputSchema: {
4161
+ type: "object",
4162
+ properties: { query: { type: "string" } },
4163
+ required: ["query"]
4164
+ }
4165
+ },
4166
+ {
4167
+ name: "find_first_error",
4168
+ description: "Find the first error step in one run timeline.",
4169
+ inputSchema: {
4170
+ type: "object",
4171
+ properties: { runId: { type: "string" } },
4172
+ required: ["runId"]
4173
+ }
4174
+ },
4175
+ {
4176
+ name: "find_slowest_path",
4177
+ description: "Summarize the slowest steps in one run.",
4178
+ inputSchema: {
4179
+ type: "object",
4180
+ properties: { runId: { type: "string" } },
4181
+ required: ["runId"]
4182
+ }
4183
+ },
4184
+ {
4185
+ name: "compare_runs",
4186
+ description: "Compare two runs and return a bounded structural diff summary.",
4187
+ inputSchema: {
4188
+ type: "object",
4189
+ properties: {
4190
+ leftRunId: { type: "string" },
4191
+ rightRunId: { type: "string" }
4192
+ },
4193
+ required: ["leftRunId", "rightRunId"]
4194
+ }
4195
+ },
4196
+ {
4197
+ name: "run_checks",
4198
+ description: "Run deterministic run.status check for one run.",
4199
+ inputSchema: {
4200
+ type: "object",
4201
+ properties: { runId: { type: "string" } },
4202
+ required: ["runId"]
4203
+ }
4204
+ },
4205
+ {
4206
+ name: "create_share_safe_report",
4207
+ description: "Create a share-profile markdown report for one run.",
4208
+ inputSchema: {
4209
+ type: "object",
4210
+ properties: { runId: { type: "string" } },
4211
+ required: ["runId"]
4212
+ }
4213
+ }
4214
+ ];
4215
+ function textResult(payload) {
4216
+ return {
4217
+ content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
4218
+ isError: false
4219
+ };
4220
+ }
4221
+ function errorResult2(message) {
4222
+ return {
4223
+ content: [{ type: "text", text: message }],
4224
+ isError: true
4225
+ };
4226
+ }
4227
+ async function resolveMeta(context, runId) {
4228
+ const td = new TraceDirectory({ dir: context.traceDir });
4229
+ const files = await td.list();
4230
+ const metas = await loadTraceMetadataList(
4231
+ context.traceDir,
4232
+ files,
4233
+ (fileName) => td.getPath(fileName)
4234
+ );
4235
+ const meta = metas.find((item) => item.runId === runId);
4236
+ if (!meta) throw new Error(`Run not found: ${runId}`);
4237
+ return meta;
4238
+ }
4239
+ async function openRunTrace(context, runId) {
4240
+ const meta = await resolveMeta(context, runId);
4241
+ const read = await openTrace({ type: "file", path: meta.filePath });
4242
+ return { meta, read };
4243
+ }
4244
+ function legacyTraceEvents(events) {
4245
+ return persistedInspectEventsToTraceEvents(events);
4246
+ }
4247
+ async function callReadOnlyTool(context, name, args = {}) {
4248
+ switch (name) {
4249
+ case "list_traces": {
4250
+ const td = new TraceDirectory({ dir: context.traceDir });
4251
+ const files = await td.list();
4252
+ const metas = await loadTraceMetadataList(
4253
+ context.traceDir,
4254
+ files,
4255
+ (fileName) => td.getPath(fileName)
4256
+ );
4257
+ return textResult(
4258
+ metas.map((meta) => ({
4259
+ runId: meta.runId,
4260
+ name: meta.name,
4261
+ status: meta.status,
4262
+ file: path__default.default.basename(meta.filePath)
4263
+ }))
4264
+ );
4265
+ }
4266
+ case "read_trace": {
4267
+ const runId = String(args.runId ?? "");
4268
+ const { read } = await openRunTrace(context, runId);
4269
+ const events = read.events.length > context.maxEvents ? read.events.slice(0, context.maxEvents) : read.events;
4270
+ return textResult({
4271
+ runId,
4272
+ format: read.format,
4273
+ truncated: read.events.length > events.length,
4274
+ events
4275
+ });
4276
+ }
4277
+ case "search_traces": {
4278
+ const query = String(args.query ?? "").trim();
4279
+ if (!query) return errorResult2("query is required");
4280
+ const td = new TraceDirectory({ dir: context.traceDir });
4281
+ const files = await td.list();
4282
+ const metas = await loadTraceMetadataList(
4283
+ context.traceDir,
4284
+ files,
4285
+ (fileName) => td.getPath(fileName)
4286
+ );
4287
+ const results = await searchTraces(metas, {
4288
+ traceDir: context.traceDir,
4289
+ name: query,
4290
+ limit: 25
4291
+ });
4292
+ return textResult(results);
4293
+ }
4294
+ case "find_first_error": {
4295
+ const runId = String(args.runId ?? "");
4296
+ const { read } = await openRunTrace(context, runId);
4297
+ const timeline = buildRunTimeline(legacyTraceEvents(read.events));
4298
+ const firstError = timeline.entries.find((entry) => entry.isError);
4299
+ return textResult({
4300
+ runId,
4301
+ firstError: firstError ?? null
4302
+ });
4303
+ }
4304
+ case "find_slowest_path": {
4305
+ const runId = String(args.runId ?? "");
4306
+ const { read } = await openRunTrace(context, runId);
4307
+ const timeline = buildRunTimeline(legacyTraceEvents(read.events), {
4308
+ focus: "slow",
4309
+ slowTopN: 5
4310
+ });
4311
+ const ranked = [...timeline.entries].filter((entry) => entry.durationMs !== void 0 && Number.isFinite(entry.durationMs)).sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0)).slice(0, 5);
4312
+ return textResult({
4313
+ runId,
4314
+ slowest: ranked[0] ?? null,
4315
+ top: ranked
4316
+ });
4317
+ }
4318
+ case "compare_runs": {
4319
+ const leftRunId = String(args.leftRunId ?? "");
4320
+ const rightRunId = String(args.rightRunId ?? "");
4321
+ const left = await openRunTrace(context, leftRunId);
4322
+ const right = await openRunTrace(context, rightRunId);
4323
+ const diff = diffRuns(
4324
+ manualTraceEventsToComparableRun(legacyTraceEvents(left.read.events)),
4325
+ manualTraceEventsToComparableRun(legacyTraceEvents(right.read.events))
4326
+ );
4327
+ return textResult({
4328
+ summary: diff.summary,
4329
+ differences: diff.differences.slice(0, 50),
4330
+ truncated: diff.differences.length > 50
4331
+ });
4332
+ }
4333
+ case "run_checks": {
4334
+ const runId = String(args.runId ?? "");
4335
+ const { read } = await openRunTrace(context, runId);
4336
+ const result = runTraceChecks(
4337
+ { read },
4338
+ { rules: [createRunStatusRule()], select: ["run.status"], runId }
4339
+ );
4340
+ return textResult(result);
4341
+ }
4342
+ case "create_share_safe_report": {
4343
+ const runId = String(args.runId ?? "");
4344
+ const { read } = await openRunTrace(context, runId);
4345
+ const run = read.runs.find((item) => item.runId === runId) ?? read.runs[0];
4346
+ if (!run) return errorResult2(`Run tree not found: ${runId}`);
4347
+ const markdown = exportMarkdown(run, {
4348
+ redactionProfile: context.redactionProfile === "local" ? "share" : context.redactionProfile
4349
+ });
4350
+ return textResult({ runId, profile: context.redactionProfile, markdown: markdown.content });
4351
+ }
4352
+ default:
4353
+ return errorResult2(`Unknown tool: ${name}`);
4354
+ }
4355
+ }
4356
+ function createMcpServerContext(options = {}) {
4357
+ return {
4358
+ traceDir: resolveTraceDir({ dir: options.traceDir }),
4359
+ maxEvents: options.maxEvents ?? 500,
4360
+ redactionProfile: options.redactionProfile ?? "share"
4361
+ };
4362
+ }
4363
+
4364
+ // packages/mcp-server/src/index.ts
4365
+ async function runReadOnlyMcpServer(options = {}) {
4366
+ const context = createMcpServerContext(options);
4367
+ const input = options.input ?? process.stdin;
4368
+ const output = options.output ?? process.stdout;
4369
+ const rl = readline__default.default.createInterface({ input, crlfDelay: Infinity });
4370
+ const write = (line) => {
4371
+ output.write(`${line}
4372
+ `);
4373
+ };
4374
+ const replyError = (id, code, message) => {
4375
+ write(
4376
+ JSON.stringify({
4377
+ jsonrpc: "2.0",
4378
+ id: id ?? null,
4379
+ error: { code, message }
4380
+ })
4381
+ );
4382
+ };
4383
+ for await (const line of rl) {
4384
+ if (!line.trim()) continue;
4385
+ let request;
4386
+ try {
4387
+ request = JSON.parse(line);
4388
+ } catch {
4389
+ write(
4390
+ JSON.stringify({
4391
+ jsonrpc: "2.0",
4392
+ id: null,
4393
+ error: { code: -32700, message: "Parse error" }
4394
+ })
4395
+ );
4396
+ continue;
4397
+ }
4398
+ const { id, method, params } = request;
4399
+ try {
4400
+ if (method === "initialize") {
4401
+ write(
4402
+ JSON.stringify({
4403
+ jsonrpc: "2.0",
4404
+ id,
4405
+ result: {
4406
+ protocolVersion: "2024-11-05",
4407
+ serverInfo: { name: "@agent-inspect/mcp-server", version: "2.5.0" },
4408
+ capabilities: { tools: {} }
4409
+ }
4410
+ })
4411
+ );
4412
+ continue;
4413
+ }
4414
+ if (method === "notifications/initialized") {
4415
+ continue;
4416
+ }
4417
+ if (method === "tools/list") {
4418
+ write(
4419
+ JSON.stringify({
4420
+ jsonrpc: "2.0",
4421
+ id,
4422
+ result: { tools: READ_ONLY_TOOLS }
4423
+ })
4424
+ );
4425
+ continue;
4426
+ }
4427
+ if (method === "tools/call") {
4428
+ const toolParams = params ?? {};
4429
+ const result = await callReadOnlyTool(
4430
+ context,
4431
+ String(toolParams.name ?? ""),
4432
+ toolParams.arguments ?? {}
4433
+ );
4434
+ write(JSON.stringify({ jsonrpc: "2.0", id, result }));
4435
+ continue;
4436
+ }
4437
+ replyError(id, -32601, `Method not found: ${method}`);
4438
+ } catch (error) {
4439
+ const message = error instanceof Error ? error.message : String(error);
4440
+ replyError(id, -32e3, message);
4441
+ }
4442
+ }
4443
+ }
4444
+
4445
+ exports.READ_ONLY_TOOLS = READ_ONLY_TOOLS;
4446
+ exports.callReadOnlyTool = callReadOnlyTool;
4447
+ exports.createMcpServerContext = createMcpServerContext;
4448
+ exports.runReadOnlyMcpServer = runReadOnlyMcpServer;
4449
+ //# sourceMappingURL=index.cjs.map
4450
+ //# sourceMappingURL=index.cjs.map