@agent-inspect/viewer 6.31.21 → 6.31.23

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.mjs CHANGED
@@ -2748,22 +2748,192 @@ function runTraceChecks(input, options = {}) {
2748
2748
  };
2749
2749
  }
2750
2750
 
2751
+ // packages/core/src/checks/canonical-equality.ts
2752
+ var DEFAULT_MAX_DEPTH = 8;
2753
+ var DEFAULT_MAX_BYTES = 16384;
2754
+ function sortKeysDeep(input, depth, maxDepth) {
2755
+ if (depth > maxDepth) {
2756
+ throw new Error("AI_CHECK_CANONICAL_DEPTH_EXCEEDED");
2757
+ }
2758
+ if (input === null || typeof input !== "object") return input;
2759
+ if (Array.isArray(input)) {
2760
+ return input.map((item) => sortKeysDeep(item, depth + 1, maxDepth));
2761
+ }
2762
+ const record = input;
2763
+ const out = {};
2764
+ for (const key of Object.keys(record).sort()) {
2765
+ out[key] = sortKeysDeep(record[key], depth + 1, maxDepth);
2766
+ }
2767
+ return out;
2768
+ }
2769
+ function encodeCanonicalStructured(value, limits) {
2770
+ const maxDepth = DEFAULT_MAX_DEPTH;
2771
+ const maxBytes = DEFAULT_MAX_BYTES;
2772
+ try {
2773
+ const sorted = sortKeysDeep(value, 0, maxDepth);
2774
+ const encoded = JSON.stringify(sorted);
2775
+ if (encoded === void 0) {
2776
+ return {
2777
+ ok: false,
2778
+ reason: "Value is not JSON-serializable for canonical comparison.",
2779
+ code: "AI_CHECK_CANONICAL_UNSERIALIZABLE"
2780
+ };
2781
+ }
2782
+ if (Buffer.byteLength(encoded, "utf8") > maxBytes) {
2783
+ return {
2784
+ ok: false,
2785
+ reason: "Canonical structured value exceeds bounded size limit.",
2786
+ code: "AI_CHECK_CANONICAL_SIZE_EXCEEDED"
2787
+ };
2788
+ }
2789
+ return { ok: true, value: encoded };
2790
+ } catch (error) {
2791
+ const message = error instanceof Error ? error.message : String(error);
2792
+ if (message === "AI_CHECK_CANONICAL_DEPTH_EXCEEDED") {
2793
+ return {
2794
+ ok: false,
2795
+ reason: "Canonical structured value exceeds bounded depth limit.",
2796
+ code: "AI_CHECK_CANONICAL_DEPTH_EXCEEDED"
2797
+ };
2798
+ }
2799
+ return {
2800
+ ok: false,
2801
+ reason: "Canonical structured encoding failed.",
2802
+ code: "AI_CHECK_CANONICAL_UNSERIALIZABLE"
2803
+ };
2804
+ }
2805
+ }
2806
+
2807
+ // packages/core/src/persisted/delivery-identity.ts
2808
+ var DELIVERY_CONFLICT_ATTR = "agent_inspect.delivery_conflict";
2809
+ var DELIVERY_EVENT_BASE_ATTR = "agent_inspect.delivery_event_base";
2810
+ var MAX_DELIVERY_BASE_LEN = 500;
2811
+ function stripReaderDeliveryDiscriminator(eventId, attributes) {
2812
+ if (!eventId) return eventId;
2813
+ const rawBase = attributes?.[DELIVERY_EVENT_BASE_ATTR];
2814
+ if (typeof rawBase !== "string" || rawBase.trim() === "") return eventId;
2815
+ const base = rawBase.trim().slice(0, MAX_DELIVERY_BASE_LEN);
2816
+ if (eventId === base) return base;
2817
+ const prefix = `${base}:`;
2818
+ if (!eventId.startsWith(prefix)) return eventId;
2819
+ const suffix = eventId.slice(prefix.length);
2820
+ if (/^\d+$/.test(suffix)) return base;
2821
+ return eventId;
2822
+ }
2823
+ function deliveryEventBaseFromIndexedId(eventId) {
2824
+ if (!/:\d+$/.test(eventId)) return void 0;
2825
+ const base = eventId.replace(/:\d+$/, "");
2826
+ return base.length > 0 ? base.slice(0, MAX_DELIVERY_BASE_LEN) : void 0;
2827
+ }
2828
+ function canonicalDigest(value) {
2829
+ if (value === void 0) return null;
2830
+ const encoded = encodeCanonicalStructured(value);
2831
+ return encoded.ok ? encoded.value : `unencodable:${encoded.code}`;
2832
+ }
2833
+ function capturedContentDigest(attributes) {
2834
+ if (!attributes || typeof attributes !== "object") {
2835
+ return { args: null, result: null };
2836
+ }
2837
+ const nested = attributes.metadata && typeof attributes.metadata === "object" ? attributes.metadata : void 0;
2838
+ const args = attributes.arguments ?? attributes.args ?? attributes.input ?? nested?.arguments ?? nested?.args ?? nested?.input;
2839
+ const result = attributes.result ?? attributes.output ?? attributes.response ?? nested?.result ?? nested?.output ?? nested?.response;
2840
+ return {
2841
+ args: args === void 0 ? null : canonicalDigest(args),
2842
+ result: result === void 0 ? null : canonicalDigest(result)
2843
+ };
2844
+ }
2845
+ function timeKey(fields) {
2846
+ if (typeof fields.startedAt === "string" && fields.startedAt.trim() !== "") {
2847
+ return fields.startedAt;
2848
+ }
2849
+ if (typeof fields.timestamp === "string" && fields.timestamp.trim() !== "") {
2850
+ return fields.timestamp;
2851
+ }
2852
+ if (typeof fields.timestamp === "number" && Number.isFinite(fields.timestamp)) {
2853
+ return String(fields.timestamp);
2854
+ }
2855
+ return null;
2856
+ }
2857
+ function deliverySemanticFingerprint(fields) {
2858
+ const content = capturedContentDigest(fields.attributes);
2859
+ return JSON.stringify({
2860
+ stableEventId: stripReaderDeliveryDiscriminator(fields.eventId, fields.attributes),
2861
+ runId: fields.runId,
2862
+ kind: fields.kind,
2863
+ name: fields.name,
2864
+ status: fields.status ?? null,
2865
+ parentId: fields.parentId ?? null,
2866
+ sourceType: fields.sourceType ?? null,
2867
+ time: timeKey(fields),
2868
+ operationId: fields.operationId ?? null,
2869
+ attemptId: fields.attemptId ?? null,
2870
+ attemptNumber: fields.attemptNumber ?? null,
2871
+ retryOf: fields.retryOf ?? null,
2872
+ argsDigest: content.args,
2873
+ resultDigest: content.result
2874
+ });
2875
+ }
2876
+ function identityFieldsFromAttributes(attributes) {
2877
+ if (!attributes || typeof attributes !== "object") {
2878
+ return {
2879
+ operationId: null,
2880
+ attemptId: null,
2881
+ attemptNumber: null,
2882
+ retryOf: null
2883
+ };
2884
+ }
2885
+ const nested = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? attributes.metadata : void 0;
2886
+ const pickString2 = (key) => {
2887
+ const direct = attributes[key];
2888
+ if (typeof direct === "string" && direct.trim() !== "") return direct.trim();
2889
+ const nestedValue = nested?.[key];
2890
+ if (typeof nestedValue === "string" && nestedValue.trim() !== "") {
2891
+ return nestedValue.trim();
2892
+ }
2893
+ return null;
2894
+ };
2895
+ const pickNumber = () => {
2896
+ const direct = attributes.attemptNumber ?? attributes.attempt;
2897
+ if (typeof direct === "number" && Number.isFinite(direct)) return direct;
2898
+ const nestedValue = nested?.attemptNumber ?? nested?.attempt;
2899
+ if (typeof nestedValue === "number" && Number.isFinite(nestedValue)) {
2900
+ return nestedValue;
2901
+ }
2902
+ return null;
2903
+ };
2904
+ return {
2905
+ operationId: pickString2("operationId"),
2906
+ attemptId: pickString2("attemptId"),
2907
+ retryOf: pickString2("retryOf"),
2908
+ attemptNumber: pickNumber()
2909
+ };
2910
+ }
2911
+ function stampDeliveryConflict(event) {
2912
+ return {
2913
+ ...event,
2914
+ attributes: {
2915
+ ...event.attributes ?? {},
2916
+ [DELIVERY_CONFLICT_ATTR]: true
2917
+ }
2918
+ };
2919
+ }
2920
+
2751
2921
  // packages/core/src/checks/contract.ts
2752
2922
  new Set(OBSERVED_OUTCOME_METHODS);
2753
2923
 
2754
2924
  // packages/core/src/exporters/helpers.ts
2755
- function sortKeysDeep(input) {
2925
+ function sortKeysDeep2(input) {
2756
2926
  if (input === null || typeof input !== "object") return input;
2757
- if (Array.isArray(input)) return input.map(sortKeysDeep);
2927
+ if (Array.isArray(input)) return input.map(sortKeysDeep2);
2758
2928
  const o = input;
2759
2929
  const out = {};
2760
2930
  for (const k of Object.keys(o).sort()) {
2761
- out[k] = sortKeysDeep(o[k]);
2931
+ out[k] = sortKeysDeep2(o[k]);
2762
2932
  }
2763
2933
  return out;
2764
2934
  }
2765
2935
  function stableJson(value, pretty) {
2766
- const sorted = sortKeysDeep(value);
2936
+ const sorted = sortKeysDeep2(value);
2767
2937
  return JSON.stringify(sorted);
2768
2938
  }
2769
2939
 
@@ -3641,6 +3811,11 @@ function compactAttributes(entries) {
3641
3811
  }
3642
3812
  return Object.keys(out).length > 0 ? out : void 0;
3643
3813
  }
3814
+ function stampDeliveryEventBase(eventId, attributes) {
3815
+ const base = deliveryEventBaseFromIndexedId(eventId);
3816
+ if (!base) return attributes;
3817
+ return { ...attributes ?? {}, [DELIVERY_EVENT_BASE_ATTR]: base };
3818
+ }
3644
3819
  function traceEventToPersistedInspectEvent(event, options) {
3645
3820
  const eventIndex = options?.eventIndex ?? 0;
3646
3821
  const eventId = createPersistedEventId(event, eventIndex);
@@ -3670,7 +3845,7 @@ function traceEventToPersistedInspectEvent(event, options) {
3670
3845
  startedAt: tsStart.iso,
3671
3846
  confidence: "explicit",
3672
3847
  source,
3673
- attributes
3848
+ attributes: stampDeliveryEventBase(eventId, attributes)
3674
3849
  };
3675
3850
  }
3676
3851
  case "run_completed": {
@@ -3693,7 +3868,7 @@ function traceEventToPersistedInspectEvent(event, options) {
3693
3868
  durationMs: event.durationMs,
3694
3869
  confidence: "explicit",
3695
3870
  source,
3696
- attributes,
3871
+ attributes: stampDeliveryEventBase(eventId, attributes),
3697
3872
  error
3698
3873
  };
3699
3874
  }
@@ -3718,7 +3893,7 @@ function traceEventToPersistedInspectEvent(event, options) {
3718
3893
  startedAt: tsStart.iso,
3719
3894
  confidence: "explicit",
3720
3895
  source,
3721
- attributes
3896
+ attributes: stampDeliveryEventBase(eventId, attributes)
3722
3897
  };
3723
3898
  if (event.parentId !== void 0) {
3724
3899
  out.parentId = event.parentId;
@@ -3749,7 +3924,7 @@ function traceEventToPersistedInspectEvent(event, options) {
3749
3924
  durationMs: event.durationMs,
3750
3925
  confidence: "explicit",
3751
3926
  source,
3752
- attributes,
3927
+ attributes: stampDeliveryEventBase(eventId, attributes),
3753
3928
  error
3754
3929
  };
3755
3930
  }
@@ -3776,7 +3951,7 @@ function traceEventToPersistedInspectEvent(event, options) {
3776
3951
  timestamp: tsMain.iso,
3777
3952
  confidence: "explicit",
3778
3953
  source,
3779
- attributes
3954
+ attributes: stampDeliveryEventBase(eventId, attributes)
3780
3955
  };
3781
3956
  if (event.parentId !== void 0) {
3782
3957
  out.parentId = event.parentId;
@@ -3929,7 +4104,47 @@ var TreeBuilder = class {
3929
4104
  const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
3930
4105
  const nodes = /* @__PURE__ */ new Map();
3931
4106
  for (const e of sorted) {
3932
- nodes.set(e.eventId, { event: e, children: [], depth: 0 });
4107
+ const existing = nodes.get(e.eventId);
4108
+ if (!existing) {
4109
+ nodes.set(e.eventId, { event: e, children: [], depth: 0 });
4110
+ continue;
4111
+ }
4112
+ const existingAttrs = existing.event.attributes && typeof existing.event.attributes === "object" ? existing.event.attributes : void 0;
4113
+ const nextAttrs = e.attributes && typeof e.attributes === "object" ? e.attributes : void 0;
4114
+ const existingIdentity = identityFieldsFromAttributes(existingAttrs);
4115
+ const nextIdentity = identityFieldsFromAttributes(nextAttrs);
4116
+ const existingFp = deliverySemanticFingerprint({
4117
+ eventId: existing.event.eventId,
4118
+ runId: existing.event.runId,
4119
+ kind: existing.event.kind,
4120
+ name: existing.event.name,
4121
+ status: existing.event.status,
4122
+ parentId: existing.event.parentId,
4123
+ timestamp: existing.event.timestamp,
4124
+ sourceType: existing.event.source?.type,
4125
+ attributes: existingAttrs,
4126
+ ...existingIdentity
4127
+ });
4128
+ const nextFp = deliverySemanticFingerprint({
4129
+ eventId: e.eventId,
4130
+ runId: e.runId,
4131
+ kind: e.kind,
4132
+ name: e.name,
4133
+ status: e.status,
4134
+ parentId: e.parentId,
4135
+ timestamp: e.timestamp,
4136
+ sourceType: e.source?.type,
4137
+ attributes: nextAttrs,
4138
+ ...nextIdentity
4139
+ });
4140
+ if (existingFp === nextFp) {
4141
+ continue;
4142
+ }
4143
+ nodes.set(e.eventId, {
4144
+ event: stampDeliveryConflict(e),
4145
+ children: [],
4146
+ depth: 0
4147
+ });
3933
4148
  }
3934
4149
  const { roots, summary } = linkNodesVisibilityFirst(nodes);
3935
4150
  const confidenceBreakdown = {
@@ -5394,6 +5609,12 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
5394
5609
  ["agent_inspect.retry_of", "retryOf", "string"],
5395
5610
  ["agent_inspect.attempt_number", "attemptNumber", "int"]
5396
5611
  ];
5612
+ const conflictMapping = [
5613
+ ["agent_inspect.identity_conflict.operation_id", "operationId", "string"],
5614
+ ["agent_inspect.identity_conflict.attempt_id", "attemptId", "string"],
5615
+ ["agent_inspect.identity_conflict.retry_of", "retryOf", "string"],
5616
+ ["agent_inspect.identity_conflict.attempt_number", "attemptNumber", "int"]
5617
+ ];
5397
5618
  const metadata = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? { ...attributes.metadata } : {};
5398
5619
  for (const [dotted, camel, kind] of mapping) {
5399
5620
  const raw = attributes[dotted];
@@ -5445,6 +5666,55 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
5445
5666
  if (direct === void 0) attributes[camel] = promoted;
5446
5667
  if (nested === void 0) metadata[camel] = promoted;
5447
5668
  }
5669
+ for (const [dotted, camel, kind] of conflictMapping) {
5670
+ const raw = attributes[dotted];
5671
+ if (raw === void 0 || raw === null) continue;
5672
+ let alternate;
5673
+ if (kind === "int") {
5674
+ const n = typeof raw === "number" ? raw : typeof raw === "string" ? Number(raw) : NaN;
5675
+ if (!Number.isFinite(n) || n < 1) {
5676
+ warnings.push({
5677
+ code: "otlp_identity_invalid",
5678
+ message: `Ignoring invalid ${dotted} identity conflict value.`,
5679
+ severity: "warning",
5680
+ field: `${pathPrefix}.${dotted}`
5681
+ });
5682
+ continue;
5683
+ }
5684
+ alternate = Math.trunc(n);
5685
+ } else if (typeof raw === "string" && raw.trim() !== "") {
5686
+ alternate = raw.trim();
5687
+ } else {
5688
+ warnings.push({
5689
+ code: "otlp_identity_invalid",
5690
+ message: `Ignoring non-string ${dotted} identity conflict value.`,
5691
+ severity: "warning",
5692
+ field: `${pathPrefix}.${dotted}`
5693
+ });
5694
+ continue;
5695
+ }
5696
+ const preferred = attributes[camel];
5697
+ if (preferred !== void 0 && preferred !== alternate) {
5698
+ metadata[camel] = alternate;
5699
+ warnings.push({
5700
+ code: "otlp_identity_conflict",
5701
+ message: `Conflicting ${camel}: attributes.${camel} vs nested alternate from ${dotted}; retaining both.`,
5702
+ severity: "warning",
5703
+ field: `${pathPrefix}.${dotted}`
5704
+ });
5705
+ } else if (preferred === void 0) {
5706
+ metadata[camel] = alternate;
5707
+ }
5708
+ }
5709
+ const conflictFields = attributes["agent_inspect.identity_conflict.fields"];
5710
+ if (typeof conflictFields === "string" && conflictFields.trim() !== "") {
5711
+ warnings.push({
5712
+ code: "otlp_identity_conflict",
5713
+ message: `Identity representation conflict fields retained after OTLP bounding: ${conflictFields.trim().slice(0, 200)}.`,
5714
+ severity: "warning",
5715
+ field: `${pathPrefix}.agent_inspect.identity_conflict.fields`
5716
+ });
5717
+ }
5448
5718
  if (Object.keys(metadata).length > 0) {
5449
5719
  attributes.metadata = metadata;
5450
5720
  }