agent-inspect 6.31.11 → 6.31.13

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 (36) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/packages/cli/dist/{chunk-MQYHJL3W.mjs → chunk-ZAYVAHIH.mjs} +206 -34
  5. package/packages/cli/dist/chunk-ZAYVAHIH.mjs.map +1 -0
  6. package/packages/cli/dist/index.cjs +413 -67
  7. package/packages/cli/dist/index.cjs.map +1 -1
  8. package/packages/cli/dist/index.mjs +209 -36
  9. package/packages/cli/dist/index.mjs.map +1 -1
  10. package/packages/cli/dist/{src-76R6E2LQ.mjs → src-KP4UIFK5.mjs} +3 -3
  11. package/packages/cli/dist/{src-76R6E2LQ.mjs.map → src-KP4UIFK5.mjs.map} +1 -1
  12. package/packages/core/dist/advanced.cjs +204 -32
  13. package/packages/core/dist/advanced.cjs.map +1 -1
  14. package/packages/core/dist/advanced.mjs +151 -28
  15. package/packages/core/dist/advanced.mjs.map +1 -1
  16. package/packages/core/dist/{chunk-OAUNS5CS.mjs → chunk-JE3TUFAQ.mjs} +3 -3
  17. package/packages/core/dist/{chunk-OAUNS5CS.mjs.map → chunk-JE3TUFAQ.mjs.map} +1 -1
  18. package/packages/core/dist/{chunk-QOC3PJO3.mjs → chunk-NJUSSJX3.mjs} +3 -3
  19. package/packages/core/dist/{chunk-QOC3PJO3.mjs.map → chunk-NJUSSJX3.mjs.map} +1 -1
  20. package/packages/core/dist/{chunk-AK5EN4U2.mjs → chunk-YU6XT5A2.mjs} +58 -9
  21. package/packages/core/dist/chunk-YU6XT5A2.mjs.map +1 -0
  22. package/packages/core/dist/exporters.cjs +205 -32
  23. package/packages/core/dist/exporters.cjs.map +1 -1
  24. package/packages/core/dist/exporters.mjs +205 -32
  25. package/packages/core/dist/exporters.mjs.map +1 -1
  26. package/packages/core/dist/logs.cjs +56 -7
  27. package/packages/core/dist/logs.cjs.map +1 -1
  28. package/packages/core/dist/logs.mjs +2 -2
  29. package/packages/core/dist/persisted.cjs +56 -7
  30. package/packages/core/dist/persisted.cjs.map +1 -1
  31. package/packages/core/dist/persisted.mjs +3 -3
  32. package/packages/core/dist/readers.cjs +56 -7
  33. package/packages/core/dist/readers.cjs.map +1 -1
  34. package/packages/core/dist/readers.mjs +3 -3
  35. package/packages/cli/dist/chunk-MQYHJL3W.mjs.map +0 -1
  36. package/packages/core/dist/chunk-AK5EN4U2.mjs.map +0 -1
@@ -143,18 +143,16 @@ var TreeBuilder = class {
143
143
  inc(confidenceBreakdown, e.confidence);
144
144
  kinds[e.kind] = (kinds[e.kind] ?? 0) + 1;
145
145
  }
146
- const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
147
- const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
148
- const status = computeRunStatus(sorted);
149
- const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
150
146
  const name = sorted.find((e) => e.kind === "RUN")?.name;
147
+ const status = computeRunStatus(sorted);
148
+ const timing = resolveRunTiming(sorted, status);
151
149
  out.push({
152
150
  runId,
153
151
  name,
154
152
  status,
155
- startedAt,
156
- endedAt: status === "running" ? void 0 : endedAt,
157
- durationMs,
153
+ startedAt: timing.startedAt,
154
+ endedAt: timing.endedAt,
155
+ durationMs: timing.durationMs,
158
156
  children: roots,
159
157
  metadata: {
160
158
  totalEvents: sorted.length,
@@ -168,7 +166,58 @@ var TreeBuilder = class {
168
166
  return out;
169
167
  }
170
168
  };
169
+ function resolveRunTiming(sorted, status) {
170
+ let startedAtMs;
171
+ let endedAtMs;
172
+ let explicitDurationMs;
173
+ for (const event of sorted) {
174
+ if (event.kind !== "RUN") continue;
175
+ const attrs = event.attributes ?? {};
176
+ if (typeof attrs.startedAtMs === "number" && Number.isFinite(attrs.startedAtMs)) {
177
+ startedAtMs = startedAtMs === void 0 ? attrs.startedAtMs : Math.min(startedAtMs, attrs.startedAtMs);
178
+ }
179
+ if (typeof attrs.endedAtMs === "number" && Number.isFinite(attrs.endedAtMs)) {
180
+ endedAtMs = endedAtMs === void 0 ? attrs.endedAtMs : Math.max(endedAtMs, attrs.endedAtMs);
181
+ }
182
+ if ((event.status === "ok" || event.status === "error") && typeof event.durationMs === "number" && Number.isFinite(event.durationMs) && event.durationMs >= 0) {
183
+ explicitDurationMs = event.durationMs;
184
+ if (endedAtMs === void 0 && startedAtMs !== void 0) {
185
+ endedAtMs = startedAtMs + event.durationMs;
186
+ } else if (endedAtMs === void 0 && Number.isFinite(event.timestamp)) {
187
+ endedAtMs = event.timestamp + event.durationMs;
188
+ if (startedAtMs === void 0) {
189
+ startedAtMs = event.timestamp;
190
+ }
191
+ }
192
+ }
193
+ if (startedAtMs === void 0 && event.status === "running" && Number.isFinite(event.timestamp)) {
194
+ startedAtMs = event.timestamp;
195
+ }
196
+ }
197
+ if (startedAtMs !== void 0 && endedAtMs !== void 0 && Number.isFinite(startedAtMs) && Number.isFinite(endedAtMs) && endedAtMs >= startedAtMs && status !== "running") {
198
+ return {
199
+ startedAt: startedAtMs,
200
+ endedAt: endedAtMs,
201
+ durationMs: endedAtMs - startedAtMs
202
+ };
203
+ }
204
+ if (startedAtMs !== void 0 && explicitDurationMs !== void 0 && status !== "running") {
205
+ return {
206
+ startedAt: startedAtMs,
207
+ endedAt: startedAtMs + explicitDurationMs,
208
+ durationMs: explicitDurationMs
209
+ };
210
+ }
211
+ const startedAt = sorted.length > 0 ? sorted[0].timestamp : void 0;
212
+ const endedAt = sorted.length > 0 ? sorted[sorted.length - 1].timestamp : void 0;
213
+ const durationMs = startedAt !== void 0 && endedAt !== void 0 && Number.isFinite(startedAt) && Number.isFinite(endedAt) && endedAt >= startedAt && status !== "running" ? endedAt - startedAt : void 0;
214
+ return {
215
+ startedAt,
216
+ endedAt: status === "running" ? void 0 : endedAt,
217
+ durationMs
218
+ };
219
+ }
171
220
 
172
221
  export { TreeBuilder };
173
- //# sourceMappingURL=chunk-AK5EN4U2.mjs.map
174
- //# sourceMappingURL=chunk-AK5EN4U2.mjs.map
222
+ //# sourceMappingURL=chunk-YU6XT5A2.mjs.map
223
+ //# sourceMappingURL=chunk-YU6XT5A2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/logs/tree-builder.ts"],"names":[],"mappings":"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","file":"chunk-YU6XT5A2.mjs","sourcesContent":["import type {\n InspectEvent,\n InspectNode,\n InspectRunTree,\n RelationshipSummary,\n} from \"../types/inspect-event.js\";\nimport type { LogIngestConfig } from \"../types/log-config.js\";\n\nexport interface TreeBuilderOptions {\n config?: LogIngestConfig;\n}\n\nfunction inc<T extends string>(map: Record<T, number>, key: T): void {\n map[key] = (map[key] ?? 0) + 1;\n}\n\nfunction confidenceRank(confidence: InspectEvent[\"confidence\"]): number {\n switch (confidence) {\n case \"unknown\":\n return 0;\n case \"heuristic\":\n return 1;\n case \"correlated\":\n return 2;\n case \"explicit\":\n return 3;\n default:\n return 0;\n }\n}\n\nfunction computeRunStatus(events: InspectEvent[]): InspectRunTree[\"status\"] {\n // Derive the run status from the RUN node lifecycle, matching\n // manualTraceEventsToRunTree and extractMetadata: a run is \"running\" only\n // until its RUN completion is recorded. The v0.1 bridge writes each node as a\n // \"started\" event (status \"running\") followed by a \"completed\" event, so a\n // finished run still carries \"running\" started rows; those must not force the\n // whole run back to \"running\". Events arrive timestamp-sorted, so the last\n // terminal RUN status wins.\n let runTerminal: \"ok\" | \"error\" | undefined;\n let sawRunEvent = false;\n for (const e of events) {\n if (e.kind !== \"RUN\") continue;\n sawRunEvent = true;\n if (e.status === \"ok\" || e.status === \"error\") {\n runTerminal = e.status;\n }\n }\n if (sawRunEvent) {\n return runTerminal ?? \"running\";\n }\n\n // Fallback for traces with no RUN node (e.g. log-only ingestion).\n let hasRunning = false;\n for (const e of events) {\n if (e.status === \"error\") return \"error\";\n if (e.status === \"running\") hasRunning = true;\n }\n return hasRunning ? \"running\" : \"ok\";\n}\n\ninterface PendingEdge {\n childId: string;\n parentId: string;\n childTimestamp: number;\n childConfidence: InspectEvent[\"confidence\"];\n}\n\n/**\n * Link children to parents without self-edges or cycles. Broken edges become roots.\n */\nexport function linkNodesVisibilityFirst(\n nodes: Map<string, InspectNode>,\n): { roots: InspectNode[]; summary: RelationshipSummary } {\n let selfParentCount = 0;\n let unresolvedParentCount = 0;\n let normalizedEdgeCount = 0;\n let cycleCount = 0;\n\n const pending: PendingEdge[] = [];\n for (const node of nodes.values()) {\n const parentId = node.event.parentId;\n if (!parentId) continue;\n if (parentId === node.event.eventId) {\n selfParentCount += 1;\n normalizedEdgeCount += 1;\n continue;\n }\n if (!nodes.has(parentId)) {\n unresolvedParentCount += 1;\n continue;\n }\n pending.push({\n childId: node.event.eventId,\n parentId,\n childTimestamp: node.event.timestamp,\n childConfidence: node.event.confidence,\n });\n }\n\n // Prefer keeping higher-confidence / earlier edges; break the weakest last edge in a cycle.\n pending.sort((a, b) => {\n const conf = confidenceRank(b.childConfidence) - confidenceRank(a.childConfidence);\n if (conf !== 0) return conf;\n return a.childTimestamp - b.childTimestamp;\n });\n\n const parentOf = new Map<string, string>();\n for (const edge of pending) {\n parentOf.set(edge.childId, edge.parentId);\n // Detect if accepting this edge creates a cycle (walk from parent toward roots).\n let cursor: string | undefined = edge.parentId;\n const seen = new Set<string>([edge.childId]);\n let cyclic = false;\n while (cursor) {\n if (seen.has(cursor)) {\n cyclic = true;\n break;\n }\n seen.add(cursor);\n cursor = parentOf.get(cursor);\n }\n if (cyclic) {\n parentOf.delete(edge.childId);\n cycleCount += 1;\n normalizedEdgeCount += 1;\n }\n }\n\n for (const [childId, parentId] of parentOf) {\n nodes.get(parentId)!.children.push(nodes.get(childId)!);\n }\n\n const roots: InspectNode[] = [];\n for (const node of nodes.values()) {\n if (!parentOf.has(node.event.eventId)) {\n roots.push(node);\n }\n }\n\n const assignDepth = (n: InspectNode, depth: number, stack: Set<string>) => {\n if (stack.has(n.event.eventId)) return;\n n.depth = depth;\n stack.add(n.event.eventId);\n for (const c of n.children) assignDepth(c, depth + 1, stack);\n stack.delete(n.event.eventId);\n };\n for (const r of roots) assignDepth(r, 0, new Set());\n\n return {\n roots,\n summary: {\n rootCount: roots.length,\n selfParentCount,\n cycleCount,\n unresolvedParentCount,\n normalizedEdgeCount,\n },\n };\n}\n\nexport class TreeBuilder {\n constructor(options?: TreeBuilderOptions) {\n void options?.config;\n }\n\n build(events: InspectEvent[]): InspectRunTree[] {\n const byRun = new Map<string, InspectEvent[]>();\n for (const e of events) {\n if (!byRun.has(e.runId)) byRun.set(e.runId, []);\n byRun.get(e.runId)!.push(e);\n }\n\n const out: InspectRunTree[] = [];\n for (const [runId, runEvents] of byRun.entries()) {\n const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);\n\n const nodes = new Map<string, InspectNode>();\n for (const e of sorted) {\n nodes.set(e.eventId, { event: e, children: [], depth: 0 });\n }\n\n const { roots, summary } = linkNodesVisibilityFirst(nodes);\n\n // metadata\n const confidenceBreakdown = {\n explicit: 0,\n correlated: 0,\n heuristic: 0,\n unknown: 0,\n } as Record<InspectEvent[\"confidence\"], number>;\n const kinds = {} as Record<InspectEvent[\"kind\"], number>;\n for (const e of sorted) {\n inc(confidenceBreakdown, e.confidence);\n (kinds as any)[e.kind] = ((kinds as any)[e.kind] ?? 0) + 1;\n }\n\n const name = sorted.find((e) => e.kind === \"RUN\")?.name;\n const status = computeRunStatus(sorted);\n const timing = resolveRunTiming(sorted, status);\n\n out.push({\n runId,\n name,\n status,\n startedAt: timing.startedAt,\n endedAt: timing.endedAt,\n durationMs: timing.durationMs,\n children: roots,\n metadata: {\n totalEvents: sorted.length,\n confidenceBreakdown,\n kinds,\n relationshipSummary: summary,\n },\n });\n }\n\n // newest first for stable display\n out.sort((a, b) => (b.startedAt ?? 0) - (a.startedAt ?? 0));\n return out;\n }\n}\n\n/**\n * Prefer explicit RUN start/end/duration facts when consistent; otherwise use a\n * conservative observed interval from event timestamps. Never invent wall-clock ends.\n */\nexport function resolveRunTiming(\n sorted: readonly InspectEvent[],\n status: InspectRunTree[\"status\"],\n): { startedAt?: number; endedAt?: number; durationMs?: number } {\n let startedAtMs: number | undefined;\n let endedAtMs: number | undefined;\n let explicitDurationMs: number | undefined;\n\n for (const event of sorted) {\n if (event.kind !== \"RUN\") continue;\n const attrs = event.attributes ?? {};\n if (\n typeof attrs.startedAtMs === \"number\" &&\n Number.isFinite(attrs.startedAtMs)\n ) {\n startedAtMs =\n startedAtMs === undefined\n ? attrs.startedAtMs\n : Math.min(startedAtMs, attrs.startedAtMs);\n }\n if (\n typeof attrs.endedAtMs === \"number\" &&\n Number.isFinite(attrs.endedAtMs)\n ) {\n endedAtMs =\n endedAtMs === undefined\n ? attrs.endedAtMs\n : Math.max(endedAtMs, attrs.endedAtMs);\n }\n if (\n (event.status === \"ok\" || event.status === \"error\") &&\n typeof event.durationMs === \"number\" &&\n Number.isFinite(event.durationMs) &&\n event.durationMs >= 0\n ) {\n explicitDurationMs = event.durationMs;\n // Completion rows often keep the start timestamp; recover end from duration.\n if (endedAtMs === undefined && startedAtMs !== undefined) {\n endedAtMs = startedAtMs + event.durationMs;\n } else if (\n endedAtMs === undefined &&\n Number.isFinite(event.timestamp)\n ) {\n endedAtMs = event.timestamp + event.durationMs;\n if (startedAtMs === undefined) {\n startedAtMs = event.timestamp;\n }\n }\n }\n if (\n startedAtMs === undefined &&\n event.status === \"running\" &&\n Number.isFinite(event.timestamp)\n ) {\n startedAtMs = event.timestamp;\n }\n }\n\n if (\n startedAtMs !== undefined &&\n endedAtMs !== undefined &&\n Number.isFinite(startedAtMs) &&\n Number.isFinite(endedAtMs) &&\n endedAtMs >= startedAtMs &&\n status !== \"running\"\n ) {\n return {\n startedAt: startedAtMs,\n endedAt: endedAtMs,\n durationMs: endedAtMs - startedAtMs,\n };\n }\n\n if (\n startedAtMs !== undefined &&\n explicitDurationMs !== undefined &&\n status !== \"running\"\n ) {\n return {\n startedAt: startedAtMs,\n endedAt: startedAtMs + explicitDurationMs,\n durationMs: explicitDurationMs,\n };\n }\n\n // Conservative observed interval from event timestamps (no wall-clock invention).\n const startedAt = sorted.length > 0 ? sorted[0]!.timestamp : undefined;\n const endedAt = sorted.length > 0 ? sorted[sorted.length - 1]!.timestamp : undefined;\n const durationMs =\n startedAt !== undefined &&\n endedAt !== undefined &&\n Number.isFinite(startedAt) &&\n Number.isFinite(endedAt) &&\n endedAt >= startedAt &&\n status !== \"running\"\n ? endedAt - startedAt\n : undefined;\n\n return {\n startedAt,\n endedAt: status === \"running\" ? undefined : endedAt,\n durationMs,\n };\n}\n"]}
@@ -1402,7 +1402,14 @@ function exportOtlpJson(tree, options) {
1402
1402
 
1403
1403
  // packages/core/src/exporters/validation.ts
1404
1404
  var EXPERIMENTAL = "Experimental compatibility export \u2014 verify against your target tooling before relying on it.";
1405
+ var UINT64_MAX = 18446744073709551615n;
1406
+ var INT64_MIN = -9223372036854775808n;
1407
+ var INT64_MAX = 9223372036854775807n;
1408
+ var MAX_ANYVALUE_DEPTH = 8;
1409
+ var MAX_ANYVALUE_ELEMENTS = 256;
1410
+ var MAX_DIAGNOSTIC_ERRORS = 64;
1405
1411
  function pushPath(errors, path4, message) {
1412
+ if (errors.length >= MAX_DIAGNOSTIC_ERRORS) return;
1406
1413
  errors.push(`${path4}: ${message}`);
1407
1414
  }
1408
1415
  function isRecord3(value) {
@@ -1411,7 +1418,59 @@ function isRecord3(value) {
1411
1418
  function isHexId(value, byteLen) {
1412
1419
  return typeof value === "string" && new RegExp(`^[0-9a-fA-F]{${byteLen * 2}}$`).test(value);
1413
1420
  }
1414
- function validateAnyValue(errors, path4, value) {
1421
+ function parseDecimalIntegerString(value) {
1422
+ if (typeof value !== "string" || !/^-?\d+$/.test(value)) {
1423
+ return { ok: false, reason: "must be a decimal integer string" };
1424
+ }
1425
+ try {
1426
+ return { ok: true, n: BigInt(value) };
1427
+ } catch {
1428
+ return { ok: false, reason: "out of integer range" };
1429
+ }
1430
+ }
1431
+ function validateUint64String(errors, path4, value, label) {
1432
+ const parsed = parseDecimalIntegerString(value);
1433
+ if (!parsed.ok) {
1434
+ pushPath(errors, path4, `${label} ${parsed.reason}`);
1435
+ return;
1436
+ }
1437
+ if (parsed.n < 0n || parsed.n > UINT64_MAX) {
1438
+ pushPath(
1439
+ errors,
1440
+ path4,
1441
+ `${label} must be an unsigned 64-bit integer (0..${UINT64_MAX.toString()})`
1442
+ );
1443
+ }
1444
+ }
1445
+ function validateInt64String(errors, path4, value) {
1446
+ const parsed = parseDecimalIntegerString(value);
1447
+ if (!parsed.ok) {
1448
+ pushPath(errors, path4, parsed.reason);
1449
+ return;
1450
+ }
1451
+ if (parsed.n < INT64_MIN || parsed.n > INT64_MAX) {
1452
+ pushPath(
1453
+ errors,
1454
+ path4,
1455
+ `must be a signed 64-bit integer (${INT64_MIN.toString()}..${INT64_MAX.toString()})`
1456
+ );
1457
+ }
1458
+ }
1459
+ function validateAnyValue(errors, path4, value, depth = 0, counters = { elements: 0 }) {
1460
+ if (errors.length >= MAX_DIAGNOSTIC_ERRORS) return;
1461
+ if (depth > MAX_ANYVALUE_DEPTH) {
1462
+ pushPath(errors, path4, `AnyValue nesting exceeds depth ${MAX_ANYVALUE_DEPTH}`);
1463
+ return;
1464
+ }
1465
+ counters.elements += 1;
1466
+ if (counters.elements > MAX_ANYVALUE_ELEMENTS) {
1467
+ pushPath(
1468
+ errors,
1469
+ path4,
1470
+ `AnyValue element count exceeds ${MAX_ANYVALUE_ELEMENTS}`
1471
+ );
1472
+ return;
1473
+ }
1415
1474
  if (!isRecord3(value)) {
1416
1475
  pushPath(errors, path4, "must be an AnyValue object");
1417
1476
  return;
@@ -1431,12 +1490,98 @@ function validateAnyValue(errors, path4, value) {
1431
1490
  path4,
1432
1491
  `AnyValue must set exactly one variant (found ${variants.length}: ${variants.join(",")})`
1433
1492
  );
1493
+ return;
1434
1494
  }
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");
1495
+ const arm = variants[0];
1496
+ switch (arm) {
1497
+ case "stringValue":
1498
+ if (typeof value.stringValue !== "string") {
1499
+ pushPath(errors, `${path4}.stringValue`, "must be a string");
1500
+ }
1501
+ break;
1502
+ case "boolValue":
1503
+ if (typeof value.boolValue !== "boolean") {
1504
+ pushPath(errors, `${path4}.boolValue`, "must be a boolean");
1505
+ }
1506
+ break;
1507
+ case "intValue":
1508
+ validateInt64String(errors, `${path4}.intValue`, value.intValue);
1509
+ break;
1510
+ case "doubleValue":
1511
+ if (typeof value.doubleValue !== "number" || !Number.isFinite(value.doubleValue)) {
1512
+ pushPath(errors, `${path4}.doubleValue`, "must be a finite number");
1513
+ }
1514
+ break;
1515
+ case "bytesValue":
1516
+ if (typeof value.bytesValue !== "string") {
1517
+ pushPath(errors, `${path4}.bytesValue`, "must be a base64 string");
1518
+ }
1519
+ break;
1520
+ case "arrayValue": {
1521
+ if (!isRecord3(value.arrayValue)) {
1522
+ pushPath(errors, `${path4}.arrayValue`, "must be an object");
1523
+ break;
1524
+ }
1525
+ const values = value.arrayValue.values;
1526
+ if (values === void 0) break;
1527
+ if (!Array.isArray(values)) {
1528
+ pushPath(errors, `${path4}.arrayValue.values`, "must be an array");
1529
+ break;
1530
+ }
1531
+ values.forEach((item, i) => {
1532
+ validateAnyValue(
1533
+ errors,
1534
+ `${path4}.arrayValue.values[${i}]`,
1535
+ item,
1536
+ depth + 1,
1537
+ counters
1538
+ );
1539
+ });
1540
+ break;
1438
1541
  }
1542
+ case "kvlistValue": {
1543
+ if (!isRecord3(value.kvlistValue)) {
1544
+ pushPath(errors, `${path4}.kvlistValue`, "must be an object");
1545
+ break;
1546
+ }
1547
+ const values = value.kvlistValue.values;
1548
+ if (values === void 0) break;
1549
+ if (!Array.isArray(values)) {
1550
+ pushPath(errors, `${path4}.kvlistValue.values`, "must be an array");
1551
+ break;
1552
+ }
1553
+ values.forEach((item, i) => {
1554
+ const itemPath = `${path4}.kvlistValue.values[${i}]`;
1555
+ if (!isRecord3(item)) {
1556
+ pushPath(errors, itemPath, "must be a KeyValue object");
1557
+ return;
1558
+ }
1559
+ if (typeof item.key !== "string") {
1560
+ pushPath(errors, `${itemPath}.key`, "must be a string");
1561
+ }
1562
+ validateAnyValue(errors, `${itemPath}.value`, item.value, depth + 1, counters);
1563
+ });
1564
+ break;
1565
+ }
1566
+ }
1567
+ }
1568
+ function validateKeyValueList(errors, path4, attrs) {
1569
+ if (attrs === void 0) return;
1570
+ if (!Array.isArray(attrs)) {
1571
+ pushPath(errors, path4, "must be an array");
1572
+ return;
1439
1573
  }
1574
+ attrs.forEach((attr, ai) => {
1575
+ const attrPath = `${path4}[${ai}]`;
1576
+ if (!isRecord3(attr)) {
1577
+ pushPath(errors, attrPath, "must be an object");
1578
+ return;
1579
+ }
1580
+ if (typeof attr.key !== "string") {
1581
+ pushPath(errors, `${attrPath}.key`, "must be a string");
1582
+ }
1583
+ validateAnyValue(errors, `${attrPath}.value`, attr.value);
1584
+ });
1440
1585
  }
1441
1586
  function validateOtlpJson(content) {
1442
1587
  const errors = [];
@@ -1466,6 +1611,13 @@ function validateOtlpJson(content) {
1466
1611
  pushPath(errors, rsPath, "must be an object");
1467
1612
  return;
1468
1613
  }
1614
+ if (isRecord3(resourceSpan.resource)) {
1615
+ validateKeyValueList(
1616
+ errors,
1617
+ `${rsPath}.resource.attributes`,
1618
+ resourceSpan.resource.attributes
1619
+ );
1620
+ }
1469
1621
  if (!Array.isArray(resourceSpan.scopeSpans)) {
1470
1622
  pushPath(errors, `${rsPath}.scopeSpans`, "must be an array");
1471
1623
  return;
@@ -1476,6 +1628,13 @@ function validateOtlpJson(content) {
1476
1628
  pushPath(errors, ssPath, "must be an object");
1477
1629
  return;
1478
1630
  }
1631
+ if (isRecord3(scopeSpan.scope)) {
1632
+ validateKeyValueList(
1633
+ errors,
1634
+ `${ssPath}.scope.attributes`,
1635
+ scopeSpan.scope.attributes
1636
+ );
1637
+ }
1479
1638
  if (!Array.isArray(scopeSpan.spans)) {
1480
1639
  pushPath(errors, `${ssPath}.spans`, "must be an array");
1481
1640
  return;
@@ -1510,44 +1669,58 @@ function validateOtlpJson(content) {
1510
1669
  if (typeof span.name !== "string") {
1511
1670
  pushPath(errors, `${spanPath}.name`, "must be a string");
1512
1671
  }
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(
1672
+ validateUint64String(
1673
+ errors,
1674
+ `${spanPath}.startTimeUnixNano`,
1675
+ span.startTimeUnixNano,
1676
+ "startTimeUnixNano"
1677
+ );
1678
+ if (span.endTimeUnixNano !== void 0) {
1679
+ validateUint64String(
1522
1680
  errors,
1523
1681
  `${spanPath}.endTimeUnixNano`,
1524
- "must be a decimal-string 64-bit integer when present"
1682
+ span.endTimeUnixNano,
1683
+ "endTimeUnixNano"
1525
1684
  );
1526
1685
  }
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(
1686
+ if (typeof span.startTimeUnixNano === "string" && typeof span.endTimeUnixNano === "string") {
1687
+ const start = parseDecimalIntegerString(span.startTimeUnixNano);
1688
+ const end = parseDecimalIntegerString(span.endTimeUnixNano);
1689
+ if (start.ok && end.ok && end.n < start.n) {
1690
+ pushPath(
1691
+ errors,
1692
+ `${spanPath}.endTimeUnixNano`,
1693
+ "must be >= startTimeUnixNano"
1694
+ );
1695
+ }
1696
+ }
1697
+ validateKeyValueList(errors, `${spanPath}.attributes`, span.attributes);
1698
+ if (Array.isArray(span.events)) {
1699
+ span.events.forEach((event, ei) => {
1700
+ const eventPath = `${spanPath}.events[${ei}]`;
1701
+ if (!isRecord3(event)) {
1702
+ pushPath(errors, eventPath, "must be an object");
1703
+ return;
1704
+ }
1705
+ if (event.timeUnixNano !== void 0) {
1706
+ validateUint64String(
1531
1707
  errors,
1532
- `${spanPath}.endTimeUnixNano`,
1533
- "must be >= startTimeUnixNano"
1708
+ `${eventPath}.timeUnixNano`,
1709
+ event.timeUnixNano,
1710
+ "timeUnixNano"
1534
1711
  );
1535
1712
  }
1536
- } catch {
1537
- pushPath(errors, `${spanPath}.endTimeUnixNano`, "out of integer range");
1538
- }
1713
+ validateKeyValueList(errors, `${eventPath}.attributes`, event.attributes);
1714
+ });
1539
1715
  }
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");
1716
+ if (Array.isArray(span.links)) {
1717
+ span.links.forEach((link, li) => {
1718
+ const linkPath = `${spanPath}.links[${li}]`;
1719
+ if (!isRecord3(link)) {
1720
+ pushPath(errors, linkPath, "must be an object");
1545
1721
  return;
1546
1722
  }
1547
- if (typeof attr.key !== "string") {
1548
- pushPath(errors, `${attrPath}.key`, "must be a string");
1549
- }
1550
- validateAnyValue(errors, `${attrPath}.value`, attr.value);
1723
+ validateKeyValueList(errors, `${linkPath}.attributes`, link.attributes);
1551
1724
  });
1552
1725
  }
1553
1726
  if (typeof span.kind === "number") {