agent-inspect 6.31.6 → 6.31.8

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.
Files changed (46) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +1 -1
  3. package/docs/AI-SDK-ADOPTION.md +62 -34
  4. package/package.json +1 -1
  5. package/packages/cli/dist/{chunk-YXDNWHNJ.mjs → chunk-6FE47XSF.mjs} +119 -34
  6. package/packages/cli/dist/chunk-6FE47XSF.mjs.map +1 -0
  7. package/packages/cli/dist/index.cjs +569 -102
  8. package/packages/cli/dist/index.cjs.map +1 -1
  9. package/packages/cli/dist/index.mjs +454 -74
  10. package/packages/cli/dist/index.mjs.map +1 -1
  11. package/packages/cli/dist/{src-H22KNWAZ.mjs → src-NXAYHG34.mjs} +3 -3
  12. package/packages/cli/dist/{src-H22KNWAZ.mjs.map → src-NXAYHG34.mjs.map} +1 -1
  13. package/packages/core/dist/advanced.cjs +116 -31
  14. package/packages/core/dist/advanced.cjs.map +1 -1
  15. package/packages/core/dist/advanced.mjs +7 -7
  16. package/packages/core/dist/checks.mjs +2 -2
  17. package/packages/core/dist/{chunk-LXYPM2ZO.mjs → chunk-2CCRUI42.mjs} +19 -318
  18. package/packages/core/dist/chunk-2CCRUI42.mjs.map +1 -0
  19. package/packages/core/dist/{chunk-Q5SLKV2Y.mjs → chunk-2IRRUNIN.mjs} +28 -5
  20. package/packages/core/dist/chunk-2IRRUNIN.mjs.map +1 -0
  21. package/packages/core/dist/{chunk-EXGQLMVB.mjs → chunk-7YYS742B.mjs} +49 -12
  22. package/packages/core/dist/chunk-7YYS742B.mjs.map +1 -0
  23. package/packages/core/dist/{chunk-OSGNGRHF.mjs → chunk-IJJSPJXV.mjs} +51 -25
  24. package/packages/core/dist/chunk-IJJSPJXV.mjs.map +1 -0
  25. package/packages/core/dist/chunk-L5WLVBW4.mjs +355 -0
  26. package/packages/core/dist/chunk-L5WLVBW4.mjs.map +1 -0
  27. package/packages/core/dist/exporters.cjs +327 -58
  28. package/packages/core/dist/exporters.cjs.map +1 -1
  29. package/packages/core/dist/exporters.mjs +281 -49
  30. package/packages/core/dist/exporters.mjs.map +1 -1
  31. package/packages/core/dist/persisted.cjs +361 -9
  32. package/packages/core/dist/persisted.cjs.map +1 -1
  33. package/packages/core/dist/persisted.d.cts +8 -1
  34. package/packages/core/dist/persisted.d.ts +8 -1
  35. package/packages/core/dist/persisted.mjs +3 -2
  36. package/packages/core/dist/persisted.mjs.map +1 -1
  37. package/packages/core/dist/readers.cjs +425 -100
  38. package/packages/core/dist/readers.cjs.map +1 -1
  39. package/packages/core/dist/readers.mjs +3 -3
  40. package/packages/cli/dist/chunk-YXDNWHNJ.mjs.map +0 -1
  41. package/packages/core/dist/chunk-EXGQLMVB.mjs.map +0 -1
  42. package/packages/core/dist/chunk-FWK3Q3OJ.mjs +0 -56
  43. package/packages/core/dist/chunk-FWK3Q3OJ.mjs.map +0 -1
  44. package/packages/core/dist/chunk-LXYPM2ZO.mjs.map +0 -1
  45. package/packages/core/dist/chunk-OSGNGRHF.mjs.map +0 -1
  46. package/packages/core/dist/chunk-Q5SLKV2Y.mjs.map +0 -1
@@ -1146,11 +1146,13 @@ function exportOpenInference(tree, options) {
1146
1146
  endNs = startNs + unixNano(ev.durationMs);
1147
1147
  }
1148
1148
  const { openInferenceKind } = mapInspectKindToOI(ev.kind, warnings);
1149
+ const meta = ev.attributes;
1150
+ const originalSource = typeof meta?.originalSourceType === "string" && meta.originalSourceType.trim() !== "" ? meta.originalSourceType.trim() : ev.source.type;
1149
1151
  const attrs = {
1150
1152
  "openinference.span.kind": openInferenceKind,
1151
1153
  "agent_inspect.kind": ev.kind,
1152
1154
  "agent_inspect.confidence": ev.confidence,
1153
- "agent_inspect.source.type": ev.source.type,
1155
+ "agent_inspect.source.type": originalSource,
1154
1156
  "agent_inspect.run_id": tree.runId,
1155
1157
  "agent_inspect.event_id": ev.eventId,
1156
1158
  "agent_inspect.status": ev.status ?? "unset"
@@ -1158,9 +1160,9 @@ function exportOpenInference(tree, options) {
1158
1160
  if (ev.durationMs !== void 0) {
1159
1161
  attrs["agent_inspect.duration_ms"] = ev.durationMs;
1160
1162
  }
1161
- const meta = ev.attributes;
1162
- if (meta?.model !== void 0 && typeof meta.model === "string") {
1163
- attrs["llm.model_name"] = meta.model;
1163
+ const requestModel = (typeof meta?.model === "string" && meta.model.trim() !== "" ? meta.model : void 0) ?? (typeof meta?.modelId === "string" && meta.modelId.trim() !== "" ? meta.modelId : void 0);
1164
+ if (requestModel !== void 0) {
1165
+ attrs["llm.model_name"] = requestModel;
1164
1166
  }
1165
1167
  const tokens = meta?.tokens;
1166
1168
  if (tokens && typeof tokens === "object" && tokens !== null) {
@@ -1170,8 +1172,9 @@ function exportOpenInference(tree, options) {
1170
1172
  if (typeof outp === "number") attrs["llm.token_count.completion"] = outp;
1171
1173
  }
1172
1174
  if (includeAttributes && meta && typeof meta === "object") {
1175
+ const skipKeys = /* @__PURE__ */ new Set(["tokens", "model", "modelId", "originalSourceType"]);
1173
1176
  for (const [k, v] of Object.entries(meta)) {
1174
- if (k === "tokens" || k === "model") continue;
1177
+ if (skipKeys.has(k)) continue;
1175
1178
  if (v !== void 0 && v !== null && typeof v !== "object") {
1176
1179
  attrs[`agent_inspect.preview.${k}`] = typeof v === "string" ? v.slice(0, maxLen) : v;
1177
1180
  }
@@ -1214,6 +1217,10 @@ function exportOpenInference(tree, options) {
1214
1217
  warnings
1215
1218
  };
1216
1219
  }
1220
+ var OTLP_STATUS_CODE_UNSET = 0;
1221
+ var OTLP_STATUS_CODE_OK = 1;
1222
+ var OTLP_STATUS_CODE_ERROR = 2;
1223
+ var OTLP_SPAN_KIND_INTERNAL = 1;
1217
1224
  function hexFrom2(seed, byteLen) {
1218
1225
  return crypto__default.default.createHash("sha256").update(seed, "utf8").digest("hex").slice(0, byteLen * 2);
1219
1226
  }
@@ -1226,7 +1233,19 @@ function stringAttr(key, value) {
1226
1233
  return { key, value: { stringValue: value } };
1227
1234
  }
1228
1235
  function intAttr(key, value) {
1229
- return { key, value: { intValue: String(value) } };
1236
+ return { key, value: { intValue: String(Math.trunc(value)) } };
1237
+ }
1238
+ function doubleAttr(key, value) {
1239
+ return { key, value: { doubleValue: value } };
1240
+ }
1241
+ function boolAttr(key, value) {
1242
+ return { key, value: { boolValue: value } };
1243
+ }
1244
+ function numberAttr(key, value) {
1245
+ if (Number.isFinite(value) && !Number.isInteger(value)) {
1246
+ return doubleAttr(key, value);
1247
+ }
1248
+ return intAttr(key, value);
1230
1249
  }
1231
1250
  function genAiOperationName(kind) {
1232
1251
  switch (kind) {
@@ -1240,10 +1259,27 @@ function genAiOperationName(kind) {
1240
1259
  return void 0;
1241
1260
  }
1242
1261
  }
1262
+ function resolveSpanTimes(ev) {
1263
+ const startedAtMs = typeof ev.attributes?.startedAtMs === "number" && Number.isFinite(ev.attributes.startedAtMs) ? ev.attributes.startedAtMs : void 0;
1264
+ const endedAtMs = typeof ev.attributes?.endedAtMs === "number" && Number.isFinite(ev.attributes.endedAtMs) ? ev.attributes.endedAtMs : void 0;
1265
+ const startMs = startedAtMs ?? ev.timestamp;
1266
+ const startNs = String(unixNano2(startMs));
1267
+ if (endedAtMs !== void 0) {
1268
+ return { startNs, endNs: String(unixNano2(endedAtMs)) };
1269
+ }
1270
+ if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
1271
+ return {
1272
+ startNs,
1273
+ endNs: String(unixNano2(startMs) + unixNano2(ev.durationMs))
1274
+ };
1275
+ }
1276
+ return { startNs };
1277
+ }
1243
1278
  function exportOtlpJson(tree, options) {
1244
1279
  const warnings = [
1245
1280
  "OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
1246
- "Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload."
1281
+ "Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload.",
1282
+ "Status and span kind use OTLP numeric enums (StatusCode 0/1/2, SpanKind INTERNAL=1)."
1247
1283
  ];
1248
1284
  const traceId = hexFrom2(`trace:${tree.runId}`, 16);
1249
1285
  const includeAttributes = options?.includeAttributes ?? false;
@@ -1255,29 +1291,34 @@ function exportOtlpJson(tree, options) {
1255
1291
  const ev = n.event;
1256
1292
  const spanId = hexFrom2(`${tree.runId}:${ev.eventId}`, 8);
1257
1293
  const parentSpanId = ev.parentId ? hexFrom2(`${tree.runId}:${ev.parentId}`, 8) : void 0;
1258
- const startNs = String(unixNano2(ev.timestamp));
1259
- let endNs;
1260
- if (ev.durationMs !== void 0 && Number.isFinite(ev.durationMs)) {
1261
- endNs = String(unixNano2(ev.timestamp) + unixNano2(ev.durationMs));
1262
- }
1294
+ const { startNs, endNs } = resolveSpanTimes(ev);
1295
+ const meta = ev.attributes;
1296
+ const originalSource = typeof meta?.originalSourceType === "string" && meta.originalSourceType.trim() !== "" ? meta.originalSourceType.trim() : ev.source.type;
1263
1297
  const attrs = [
1264
1298
  stringAttr("agent_inspect.kind", ev.kind),
1265
1299
  stringAttr("agent_inspect.confidence", ev.confidence),
1266
- stringAttr("agent_inspect.source.type", ev.source.type),
1300
+ stringAttr("agent_inspect.source.type", originalSource),
1267
1301
  stringAttr("agent_inspect.run_id", tree.runId),
1268
1302
  stringAttr("agent_inspect.event_id", ev.eventId),
1269
1303
  stringAttr("agent_inspect.status", ev.status ?? "unset")
1270
1304
  ];
1271
1305
  if (ev.durationMs !== void 0) {
1272
- attrs.push(intAttr("agent_inspect.duration_ms", ev.durationMs));
1306
+ attrs.push(numberAttr("agent_inspect.duration_ms", ev.durationMs));
1273
1307
  }
1274
1308
  const op = genAiOperationName(ev.kind);
1275
1309
  if (op !== void 0) {
1276
1310
  attrs.push(stringAttr("gen_ai.operation.name", op));
1277
1311
  }
1278
- const meta = ev.attributes;
1279
- if (meta?.model !== void 0 && typeof meta.model === "string") {
1280
- attrs.push(stringAttr("gen_ai.request.model", meta.model.slice(0, maxLen)));
1312
+ const requestModel = (typeof meta?.model === "string" && meta.model.trim() !== "" ? meta.model : void 0) ?? (typeof meta?.modelId === "string" && meta.modelId.trim() !== "" ? meta.modelId : void 0);
1313
+ if (requestModel !== void 0) {
1314
+ attrs.push(stringAttr("gen_ai.request.model", requestModel.slice(0, maxLen)));
1315
+ }
1316
+ const responseModel = typeof meta?.responseModelId === "string" && meta.responseModelId.trim() !== "" ? meta.responseModelId : typeof meta?.responseModel === "string" && meta.responseModel.trim() !== "" ? meta.responseModel : void 0;
1317
+ if (responseModel !== void 0) {
1318
+ attrs.push(stringAttr("gen_ai.response.model", responseModel.slice(0, maxLen)));
1319
+ }
1320
+ if (typeof meta?.provider === "string" && meta.provider.trim() !== "") {
1321
+ attrs.push(stringAttr("gen_ai.provider.name", meta.provider.slice(0, maxLen)));
1281
1322
  }
1282
1323
  const tokens = meta?.tokens;
1283
1324
  if (tokens && typeof tokens === "object" && tokens !== null) {
@@ -1287,31 +1328,39 @@ function exportOtlpJson(tree, options) {
1287
1328
  if (typeof outp === "number") attrs.push(intAttr("gen_ai.usage.output_tokens", outp));
1288
1329
  }
1289
1330
  if (includeAttributes && meta && typeof meta === "object") {
1331
+ const skipKeys = /* @__PURE__ */ new Set([
1332
+ "tokens",
1333
+ "model",
1334
+ "modelId",
1335
+ "responseModel",
1336
+ "responseModelId",
1337
+ "provider",
1338
+ "originalSourceType"
1339
+ ]);
1290
1340
  for (const [k, v] of Object.entries(meta)) {
1291
- if (k === "tokens" || k === "model") continue;
1292
- if (typeof v === "string" || typeof v === "number" || typeof v === "boolean") {
1293
- attrs.push(
1294
- stringAttr(
1295
- `agent_inspect.preview.${k}`,
1296
- typeof v === "string" ? v.slice(0, maxLen) : String(v)
1297
- )
1298
- );
1341
+ if (skipKeys.has(k)) continue;
1342
+ if (typeof v === "string") {
1343
+ attrs.push(stringAttr(`agent_inspect.preview.${k}`, v.slice(0, maxLen)));
1344
+ } else if (typeof v === "number" && Number.isFinite(v)) {
1345
+ attrs.push(numberAttr(`agent_inspect.preview.${k}`, v));
1346
+ } else if (typeof v === "boolean") {
1347
+ attrs.push(boolAttr(`agent_inspect.preview.${k}`, v));
1299
1348
  }
1300
1349
  }
1301
1350
  }
1302
- let statusCode = "STATUS_CODE_UNSET";
1351
+ let statusCode = OTLP_STATUS_CODE_UNSET;
1303
1352
  let statusMessage;
1304
1353
  if (ev.status === "error") {
1305
- statusCode = "STATUS_CODE_ERROR";
1354
+ statusCode = OTLP_STATUS_CODE_ERROR;
1306
1355
  statusMessage = meta && typeof meta.error === "object" && meta.error !== null ? String(meta.error.message ?? "error").slice(0, maxLen) : "error";
1307
1356
  } else if (ev.status === "ok") {
1308
- statusCode = "STATUS_CODE_OK";
1357
+ statusCode = OTLP_STATUS_CODE_OK;
1309
1358
  }
1310
1359
  const spanJson = {
1311
1360
  traceId,
1312
1361
  spanId,
1313
1362
  name: ev.name,
1314
- kind: "SPAN_KIND_INTERNAL",
1363
+ kind: OTLP_SPAN_KIND_INTERNAL,
1315
1364
  startTimeUnixNano: startNs,
1316
1365
  attributes: attrs,
1317
1366
  status: {
@@ -1353,6 +1402,207 @@ function exportOtlpJson(tree, options) {
1353
1402
 
1354
1403
  // packages/core/src/exporters/validation.ts
1355
1404
  var EXPERIMENTAL = "Experimental compatibility export \u2014 verify against your target tooling before relying on it.";
1405
+ function pushPath(errors, path4, message) {
1406
+ errors.push(`${path4}: ${message}`);
1407
+ }
1408
+ function isRecord3(value) {
1409
+ return value !== null && typeof value === "object" && !Array.isArray(value);
1410
+ }
1411
+ function isHexId(value, byteLen) {
1412
+ return typeof value === "string" && new RegExp(`^[0-9a-fA-F]{${byteLen * 2}}$`).test(value);
1413
+ }
1414
+ function validateAnyValue(errors, path4, value) {
1415
+ if (!isRecord3(value)) {
1416
+ pushPath(errors, path4, "must be an AnyValue object");
1417
+ return;
1418
+ }
1419
+ const variants = [
1420
+ "stringValue",
1421
+ "boolValue",
1422
+ "intValue",
1423
+ "doubleValue",
1424
+ "bytesValue",
1425
+ "arrayValue",
1426
+ "kvlistValue"
1427
+ ].filter((key) => value[key] !== void 0);
1428
+ if (variants.length !== 1) {
1429
+ pushPath(
1430
+ errors,
1431
+ path4,
1432
+ `AnyValue must set exactly one variant (found ${variants.length}: ${variants.join(",")})`
1433
+ );
1434
+ }
1435
+ if (value.intValue !== void 0) {
1436
+ if (typeof value.intValue !== "string" || !/^-?\d+$/.test(value.intValue)) {
1437
+ pushPath(errors, `${path4}.intValue`, "must be a decimal integer string");
1438
+ }
1439
+ }
1440
+ }
1441
+ function validateOtlpJson(content) {
1442
+ const errors = [];
1443
+ const warnings = [
1444
+ EXPERIMENTAL,
1445
+ "OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation.",
1446
+ "producer-profile checks IDs, timing, and AnyValue shape; historical string enums are reader-compat warnings only."
1447
+ ];
1448
+ let parsed;
1449
+ try {
1450
+ parsed = JSON.parse(content);
1451
+ } catch {
1452
+ pushPath(errors, "$", "OTLP JSON export is not valid JSON");
1453
+ return { ok: false, format: "otlp-json", errors, warnings };
1454
+ }
1455
+ if (!isRecord3(parsed)) {
1456
+ pushPath(errors, "$", "OTLP JSON export must be an object");
1457
+ return { ok: false, format: "otlp-json", errors, warnings };
1458
+ }
1459
+ if (!Array.isArray(parsed.resourceSpans)) {
1460
+ pushPath(errors, "$.resourceSpans", "must be an array");
1461
+ return { ok: false, format: "otlp-json", errors, warnings };
1462
+ }
1463
+ parsed.resourceSpans.forEach((resourceSpan, rsi) => {
1464
+ const rsPath = `$.resourceSpans[${rsi}]`;
1465
+ if (!isRecord3(resourceSpan)) {
1466
+ pushPath(errors, rsPath, "must be an object");
1467
+ return;
1468
+ }
1469
+ if (!Array.isArray(resourceSpan.scopeSpans)) {
1470
+ pushPath(errors, `${rsPath}.scopeSpans`, "must be an array");
1471
+ return;
1472
+ }
1473
+ resourceSpan.scopeSpans.forEach((scopeSpan, ssi) => {
1474
+ const ssPath = `${rsPath}.scopeSpans[${ssi}]`;
1475
+ if (!isRecord3(scopeSpan)) {
1476
+ pushPath(errors, ssPath, "must be an object");
1477
+ return;
1478
+ }
1479
+ if (!Array.isArray(scopeSpan.spans)) {
1480
+ pushPath(errors, `${ssPath}.spans`, "must be an array");
1481
+ return;
1482
+ }
1483
+ scopeSpan.spans.forEach((span, spi) => {
1484
+ const spanPath = `${ssPath}.spans[${spi}]`;
1485
+ if (!isRecord3(span)) {
1486
+ pushPath(errors, spanPath, "must be an object");
1487
+ return;
1488
+ }
1489
+ if (!isHexId(span.traceId, 16)) {
1490
+ pushPath(
1491
+ errors,
1492
+ `${spanPath}.traceId`,
1493
+ "must be a 32-char hex trace id (16 bytes)"
1494
+ );
1495
+ }
1496
+ if (!isHexId(span.spanId, 8)) {
1497
+ pushPath(
1498
+ errors,
1499
+ `${spanPath}.spanId`,
1500
+ "must be a 16-char hex span id (8 bytes)"
1501
+ );
1502
+ }
1503
+ if (span.parentSpanId !== void 0 && !isHexId(span.parentSpanId, 8)) {
1504
+ pushPath(
1505
+ errors,
1506
+ `${spanPath}.parentSpanId`,
1507
+ "must be a 16-char hex span id when present"
1508
+ );
1509
+ }
1510
+ if (typeof span.name !== "string") {
1511
+ pushPath(errors, `${spanPath}.name`, "must be a string");
1512
+ }
1513
+ if (typeof span.startTimeUnixNano !== "string" || !/^\d+$/.test(span.startTimeUnixNano)) {
1514
+ pushPath(
1515
+ errors,
1516
+ `${spanPath}.startTimeUnixNano`,
1517
+ "must be a decimal-string 64-bit integer"
1518
+ );
1519
+ }
1520
+ if (span.endTimeUnixNano !== void 0 && (typeof span.endTimeUnixNano !== "string" || !/^\d+$/.test(span.endTimeUnixNano))) {
1521
+ pushPath(
1522
+ errors,
1523
+ `${spanPath}.endTimeUnixNano`,
1524
+ "must be a decimal-string 64-bit integer when present"
1525
+ );
1526
+ }
1527
+ if (typeof span.startTimeUnixNano === "string" && /^\d+$/.test(span.startTimeUnixNano) && typeof span.endTimeUnixNano === "string" && /^\d+$/.test(span.endTimeUnixNano)) {
1528
+ try {
1529
+ if (BigInt(span.endTimeUnixNano) < BigInt(span.startTimeUnixNano)) {
1530
+ pushPath(
1531
+ errors,
1532
+ `${spanPath}.endTimeUnixNano`,
1533
+ "must be >= startTimeUnixNano"
1534
+ );
1535
+ }
1536
+ } catch {
1537
+ pushPath(errors, `${spanPath}.endTimeUnixNano`, "out of integer range");
1538
+ }
1539
+ }
1540
+ if (Array.isArray(span.attributes)) {
1541
+ span.attributes.forEach((attr, ai) => {
1542
+ const attrPath = `${spanPath}.attributes[${ai}]`;
1543
+ if (!isRecord3(attr)) {
1544
+ pushPath(errors, attrPath, "must be an object");
1545
+ return;
1546
+ }
1547
+ if (typeof attr.key !== "string") {
1548
+ pushPath(errors, `${attrPath}.key`, "must be a string");
1549
+ }
1550
+ validateAnyValue(errors, `${attrPath}.value`, attr.value);
1551
+ });
1552
+ }
1553
+ if (typeof span.kind === "number") {
1554
+ if (!Number.isInteger(span.kind)) {
1555
+ pushPath(errors, `${spanPath}.kind`, "must be an integer SpanKind enum");
1556
+ }
1557
+ } else if (typeof span.kind === "string") {
1558
+ warnings.push(
1559
+ `${spanPath}.kind: string SpanKind is historical reader-compat only; producer wire format uses numeric enums`
1560
+ );
1561
+ } else if (span.kind !== void 0) {
1562
+ pushPath(
1563
+ errors,
1564
+ `${spanPath}.kind`,
1565
+ "must be numeric SpanKind or historical string enum"
1566
+ );
1567
+ }
1568
+ if (!isRecord3(span.status)) {
1569
+ pushPath(errors, `${spanPath}.status`, "must be an object");
1570
+ } else {
1571
+ const code = span.status.code;
1572
+ if (typeof code === "number") {
1573
+ if (![0, 1, 2].includes(code)) {
1574
+ pushPath(
1575
+ errors,
1576
+ `${spanPath}.status.code`,
1577
+ "must be numeric StatusCode 0 (UNSET), 1 (OK), or 2 (ERROR)"
1578
+ );
1579
+ }
1580
+ } else if (typeof code === "string") {
1581
+ const upper = code.toUpperCase();
1582
+ if (upper !== "STATUS_CODE_UNSET" && upper !== "STATUS_CODE_OK" && upper !== "STATUS_CODE_ERROR" && upper !== "UNSET" && upper !== "OK" && upper !== "ERROR" && !/^[012]$/.test(code.trim())) {
1583
+ pushPath(
1584
+ errors,
1585
+ `${spanPath}.status.code`,
1586
+ "unrecognized StatusCode string"
1587
+ );
1588
+ } else {
1589
+ warnings.push(
1590
+ `${spanPath}.status.code: string StatusCode is historical reader-compat only; producer wire format uses numeric 0/1/2`
1591
+ );
1592
+ }
1593
+ } else {
1594
+ pushPath(
1595
+ errors,
1596
+ `${spanPath}.status.code`,
1597
+ "must be numeric StatusCode 0/1/2 or historical string enum"
1598
+ );
1599
+ }
1600
+ }
1601
+ });
1602
+ });
1603
+ });
1604
+ return { ok: errors.length === 0, format: "otlp-json", errors, warnings };
1605
+ }
1356
1606
  function validateExportContent(format, content) {
1357
1607
  const errors = [];
1358
1608
  const warnings = [EXPERIMENTAL];
@@ -1398,25 +1648,7 @@ function validateExportContent(format, content) {
1398
1648
  return { ok: errors.length === 0, format, errors, warnings };
1399
1649
  }
1400
1650
  if (format === "otlp-json") {
1401
- let parsed;
1402
- try {
1403
- parsed = JSON.parse(content);
1404
- } catch {
1405
- errors.push("OTLP JSON export is not valid JSON");
1406
- return { ok: false, format, errors, warnings };
1407
- }
1408
- if (!parsed || typeof parsed !== "object") {
1409
- errors.push("OTLP JSON export must be an object");
1410
- return { ok: false, format, errors, warnings };
1411
- }
1412
- const o = parsed;
1413
- if (!Array.isArray(o.resourceSpans)) {
1414
- errors.push("OTLP JSON export must include resourceSpans array");
1415
- }
1416
- warnings.push(
1417
- "OTLP JSON mapping uses OTel GenAI-aligned attributes where applicable; collectors may require transformation."
1418
- );
1419
- return { ok: errors.length === 0, format, errors, warnings };
1651
+ return validateOtlpJson(content);
1420
1652
  }
1421
1653
  errors.push(`Unsupported export format`);
1422
1654
  return { ok: false, format, errors, warnings };
@@ -1917,6 +2149,8 @@ function formatStepLabel(stepType, stepName) {
1917
2149
  }
1918
2150
 
1919
2151
  // packages/core/src/timeline.ts
2152
+ var ROOT_TIMELINE_DEPTH = 0;
2153
+ var MAX_TIMELINE_DEPTH = 1e3;
1920
2154
  function finite(n) {
1921
2155
  return typeof n === "number" && Number.isFinite(n);
1922
2156
  }
@@ -1987,16 +2221,51 @@ function buildRunTimeline(events, options = {}) {
1987
2221
  const computeDepth = (stepId) => {
1988
2222
  const cached = depthCache.get(stepId);
1989
2223
  if (cached !== void 0) return cached;
1990
- const node = steps.get(stepId);
1991
- if (!node) return 0;
1992
- const parent = node.parentId;
1993
- if (typeof parent !== "string" || parent.trim() === "" || !steps.has(parent)) {
1994
- depthCache.set(stepId, 0);
1995
- return 0;
1996
- }
1997
- const d = Math.min(1e3, computeDepth(parent) + 1);
1998
- depthCache.set(stepId, d);
1999
- return d;
2224
+ const ancestry = [];
2225
+ const ancestryIndexes = /* @__PURE__ */ new Map();
2226
+ let currentStepId = stepId;
2227
+ let resolvedDepth;
2228
+ while (resolvedDepth === void 0) {
2229
+ const cachedDepth = depthCache.get(currentStepId);
2230
+ if (cachedDepth !== void 0) {
2231
+ resolvedDepth = cachedDepth;
2232
+ continue;
2233
+ }
2234
+ const cycleStart = ancestryIndexes.get(currentStepId);
2235
+ if (cycleStart !== void 0) {
2236
+ const cycleStepIds = ancestry.splice(cycleStart);
2237
+ for (const cycleStepId of cycleStepIds) {
2238
+ depthCache.set(cycleStepId, ROOT_TIMELINE_DEPTH);
2239
+ }
2240
+ resolvedDepth = ROOT_TIMELINE_DEPTH;
2241
+ continue;
2242
+ }
2243
+ const currentStep = steps.get(currentStepId);
2244
+ if (!currentStep) {
2245
+ resolvedDepth = ROOT_TIMELINE_DEPTH;
2246
+ continue;
2247
+ }
2248
+ ancestryIndexes.set(currentStepId, ancestry.length);
2249
+ ancestry.push(currentStepId);
2250
+ const parentId = currentStep.parentId;
2251
+ if (typeof parentId !== "string" || parentId.trim() === "" || !steps.has(parentId)) {
2252
+ ancestry.pop();
2253
+ depthCache.set(currentStepId, ROOT_TIMELINE_DEPTH);
2254
+ resolvedDepth = ROOT_TIMELINE_DEPTH;
2255
+ continue;
2256
+ }
2257
+ currentStepId = parentId;
2258
+ }
2259
+ ancestry.reverse();
2260
+ for (const ancestorStepId of ancestry) {
2261
+ resolvedDepth = Math.min(MAX_TIMELINE_DEPTH, resolvedDepth + 1);
2262
+ depthCache.set(ancestorStepId, resolvedDepth);
2263
+ }
2264
+ const resolvedStepDepth = depthCache.get(stepId);
2265
+ if (resolvedStepDepth === void 0) {
2266
+ throw new Error(`Failed to resolve timeline depth for step "${stepId}"`);
2267
+ }
2268
+ return resolvedStepDepth;
2000
2269
  };
2001
2270
  const entries = [];
2002
2271
  for (const [stepId, s] of steps.entries()) {