@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.cjs CHANGED
@@ -2755,22 +2755,192 @@ function runTraceChecks(input, options = {}) {
2755
2755
  };
2756
2756
  }
2757
2757
 
2758
+ // packages/core/src/checks/canonical-equality.ts
2759
+ var DEFAULT_MAX_DEPTH = 8;
2760
+ var DEFAULT_MAX_BYTES = 16384;
2761
+ function sortKeysDeep(input, depth, maxDepth) {
2762
+ if (depth > maxDepth) {
2763
+ throw new Error("AI_CHECK_CANONICAL_DEPTH_EXCEEDED");
2764
+ }
2765
+ if (input === null || typeof input !== "object") return input;
2766
+ if (Array.isArray(input)) {
2767
+ return input.map((item) => sortKeysDeep(item, depth + 1, maxDepth));
2768
+ }
2769
+ const record = input;
2770
+ const out = {};
2771
+ for (const key of Object.keys(record).sort()) {
2772
+ out[key] = sortKeysDeep(record[key], depth + 1, maxDepth);
2773
+ }
2774
+ return out;
2775
+ }
2776
+ function encodeCanonicalStructured(value, limits) {
2777
+ const maxDepth = DEFAULT_MAX_DEPTH;
2778
+ const maxBytes = DEFAULT_MAX_BYTES;
2779
+ try {
2780
+ const sorted = sortKeysDeep(value, 0, maxDepth);
2781
+ const encoded = JSON.stringify(sorted);
2782
+ if (encoded === void 0) {
2783
+ return {
2784
+ ok: false,
2785
+ reason: "Value is not JSON-serializable for canonical comparison.",
2786
+ code: "AI_CHECK_CANONICAL_UNSERIALIZABLE"
2787
+ };
2788
+ }
2789
+ if (Buffer.byteLength(encoded, "utf8") > maxBytes) {
2790
+ return {
2791
+ ok: false,
2792
+ reason: "Canonical structured value exceeds bounded size limit.",
2793
+ code: "AI_CHECK_CANONICAL_SIZE_EXCEEDED"
2794
+ };
2795
+ }
2796
+ return { ok: true, value: encoded };
2797
+ } catch (error) {
2798
+ const message = error instanceof Error ? error.message : String(error);
2799
+ if (message === "AI_CHECK_CANONICAL_DEPTH_EXCEEDED") {
2800
+ return {
2801
+ ok: false,
2802
+ reason: "Canonical structured value exceeds bounded depth limit.",
2803
+ code: "AI_CHECK_CANONICAL_DEPTH_EXCEEDED"
2804
+ };
2805
+ }
2806
+ return {
2807
+ ok: false,
2808
+ reason: "Canonical structured encoding failed.",
2809
+ code: "AI_CHECK_CANONICAL_UNSERIALIZABLE"
2810
+ };
2811
+ }
2812
+ }
2813
+
2814
+ // packages/core/src/persisted/delivery-identity.ts
2815
+ var DELIVERY_CONFLICT_ATTR = "agent_inspect.delivery_conflict";
2816
+ var DELIVERY_EVENT_BASE_ATTR = "agent_inspect.delivery_event_base";
2817
+ var MAX_DELIVERY_BASE_LEN = 500;
2818
+ function stripReaderDeliveryDiscriminator(eventId, attributes) {
2819
+ if (!eventId) return eventId;
2820
+ const rawBase = attributes?.[DELIVERY_EVENT_BASE_ATTR];
2821
+ if (typeof rawBase !== "string" || rawBase.trim() === "") return eventId;
2822
+ const base = rawBase.trim().slice(0, MAX_DELIVERY_BASE_LEN);
2823
+ if (eventId === base) return base;
2824
+ const prefix = `${base}:`;
2825
+ if (!eventId.startsWith(prefix)) return eventId;
2826
+ const suffix = eventId.slice(prefix.length);
2827
+ if (/^\d+$/.test(suffix)) return base;
2828
+ return eventId;
2829
+ }
2830
+ function deliveryEventBaseFromIndexedId(eventId) {
2831
+ if (!/:\d+$/.test(eventId)) return void 0;
2832
+ const base = eventId.replace(/:\d+$/, "");
2833
+ return base.length > 0 ? base.slice(0, MAX_DELIVERY_BASE_LEN) : void 0;
2834
+ }
2835
+ function canonicalDigest(value) {
2836
+ if (value === void 0) return null;
2837
+ const encoded = encodeCanonicalStructured(value);
2838
+ return encoded.ok ? encoded.value : `unencodable:${encoded.code}`;
2839
+ }
2840
+ function capturedContentDigest(attributes) {
2841
+ if (!attributes || typeof attributes !== "object") {
2842
+ return { args: null, result: null };
2843
+ }
2844
+ const nested = attributes.metadata && typeof attributes.metadata === "object" ? attributes.metadata : void 0;
2845
+ const args = attributes.arguments ?? attributes.args ?? attributes.input ?? nested?.arguments ?? nested?.args ?? nested?.input;
2846
+ const result = attributes.result ?? attributes.output ?? attributes.response ?? nested?.result ?? nested?.output ?? nested?.response;
2847
+ return {
2848
+ args: args === void 0 ? null : canonicalDigest(args),
2849
+ result: result === void 0 ? null : canonicalDigest(result)
2850
+ };
2851
+ }
2852
+ function timeKey(fields) {
2853
+ if (typeof fields.startedAt === "string" && fields.startedAt.trim() !== "") {
2854
+ return fields.startedAt;
2855
+ }
2856
+ if (typeof fields.timestamp === "string" && fields.timestamp.trim() !== "") {
2857
+ return fields.timestamp;
2858
+ }
2859
+ if (typeof fields.timestamp === "number" && Number.isFinite(fields.timestamp)) {
2860
+ return String(fields.timestamp);
2861
+ }
2862
+ return null;
2863
+ }
2864
+ function deliverySemanticFingerprint(fields) {
2865
+ const content = capturedContentDigest(fields.attributes);
2866
+ return JSON.stringify({
2867
+ stableEventId: stripReaderDeliveryDiscriminator(fields.eventId, fields.attributes),
2868
+ runId: fields.runId,
2869
+ kind: fields.kind,
2870
+ name: fields.name,
2871
+ status: fields.status ?? null,
2872
+ parentId: fields.parentId ?? null,
2873
+ sourceType: fields.sourceType ?? null,
2874
+ time: timeKey(fields),
2875
+ operationId: fields.operationId ?? null,
2876
+ attemptId: fields.attemptId ?? null,
2877
+ attemptNumber: fields.attemptNumber ?? null,
2878
+ retryOf: fields.retryOf ?? null,
2879
+ argsDigest: content.args,
2880
+ resultDigest: content.result
2881
+ });
2882
+ }
2883
+ function identityFieldsFromAttributes(attributes) {
2884
+ if (!attributes || typeof attributes !== "object") {
2885
+ return {
2886
+ operationId: null,
2887
+ attemptId: null,
2888
+ attemptNumber: null,
2889
+ retryOf: null
2890
+ };
2891
+ }
2892
+ const nested = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? attributes.metadata : void 0;
2893
+ const pickString2 = (key) => {
2894
+ const direct = attributes[key];
2895
+ if (typeof direct === "string" && direct.trim() !== "") return direct.trim();
2896
+ const nestedValue = nested?.[key];
2897
+ if (typeof nestedValue === "string" && nestedValue.trim() !== "") {
2898
+ return nestedValue.trim();
2899
+ }
2900
+ return null;
2901
+ };
2902
+ const pickNumber = () => {
2903
+ const direct = attributes.attemptNumber ?? attributes.attempt;
2904
+ if (typeof direct === "number" && Number.isFinite(direct)) return direct;
2905
+ const nestedValue = nested?.attemptNumber ?? nested?.attempt;
2906
+ if (typeof nestedValue === "number" && Number.isFinite(nestedValue)) {
2907
+ return nestedValue;
2908
+ }
2909
+ return null;
2910
+ };
2911
+ return {
2912
+ operationId: pickString2("operationId"),
2913
+ attemptId: pickString2("attemptId"),
2914
+ retryOf: pickString2("retryOf"),
2915
+ attemptNumber: pickNumber()
2916
+ };
2917
+ }
2918
+ function stampDeliveryConflict(event) {
2919
+ return {
2920
+ ...event,
2921
+ attributes: {
2922
+ ...event.attributes ?? {},
2923
+ [DELIVERY_CONFLICT_ATTR]: true
2924
+ }
2925
+ };
2926
+ }
2927
+
2758
2928
  // packages/core/src/checks/contract.ts
2759
2929
  new Set(OBSERVED_OUTCOME_METHODS);
2760
2930
 
2761
2931
  // packages/core/src/exporters/helpers.ts
2762
- function sortKeysDeep(input) {
2932
+ function sortKeysDeep2(input) {
2763
2933
  if (input === null || typeof input !== "object") return input;
2764
- if (Array.isArray(input)) return input.map(sortKeysDeep);
2934
+ if (Array.isArray(input)) return input.map(sortKeysDeep2);
2765
2935
  const o = input;
2766
2936
  const out = {};
2767
2937
  for (const k of Object.keys(o).sort()) {
2768
- out[k] = sortKeysDeep(o[k]);
2938
+ out[k] = sortKeysDeep2(o[k]);
2769
2939
  }
2770
2940
  return out;
2771
2941
  }
2772
2942
  function stableJson(value, pretty) {
2773
- const sorted = sortKeysDeep(value);
2943
+ const sorted = sortKeysDeep2(value);
2774
2944
  return JSON.stringify(sorted);
2775
2945
  }
2776
2946
 
@@ -3648,6 +3818,11 @@ function compactAttributes(entries) {
3648
3818
  }
3649
3819
  return Object.keys(out).length > 0 ? out : void 0;
3650
3820
  }
3821
+ function stampDeliveryEventBase(eventId, attributes) {
3822
+ const base = deliveryEventBaseFromIndexedId(eventId);
3823
+ if (!base) return attributes;
3824
+ return { ...attributes ?? {}, [DELIVERY_EVENT_BASE_ATTR]: base };
3825
+ }
3651
3826
  function traceEventToPersistedInspectEvent(event, options) {
3652
3827
  const eventIndex = options?.eventIndex ?? 0;
3653
3828
  const eventId = createPersistedEventId(event, eventIndex);
@@ -3677,7 +3852,7 @@ function traceEventToPersistedInspectEvent(event, options) {
3677
3852
  startedAt: tsStart.iso,
3678
3853
  confidence: "explicit",
3679
3854
  source,
3680
- attributes
3855
+ attributes: stampDeliveryEventBase(eventId, attributes)
3681
3856
  };
3682
3857
  }
3683
3858
  case "run_completed": {
@@ -3700,7 +3875,7 @@ function traceEventToPersistedInspectEvent(event, options) {
3700
3875
  durationMs: event.durationMs,
3701
3876
  confidence: "explicit",
3702
3877
  source,
3703
- attributes,
3878
+ attributes: stampDeliveryEventBase(eventId, attributes),
3704
3879
  error
3705
3880
  };
3706
3881
  }
@@ -3725,7 +3900,7 @@ function traceEventToPersistedInspectEvent(event, options) {
3725
3900
  startedAt: tsStart.iso,
3726
3901
  confidence: "explicit",
3727
3902
  source,
3728
- attributes
3903
+ attributes: stampDeliveryEventBase(eventId, attributes)
3729
3904
  };
3730
3905
  if (event.parentId !== void 0) {
3731
3906
  out.parentId = event.parentId;
@@ -3756,7 +3931,7 @@ function traceEventToPersistedInspectEvent(event, options) {
3756
3931
  durationMs: event.durationMs,
3757
3932
  confidence: "explicit",
3758
3933
  source,
3759
- attributes,
3934
+ attributes: stampDeliveryEventBase(eventId, attributes),
3760
3935
  error
3761
3936
  };
3762
3937
  }
@@ -3783,7 +3958,7 @@ function traceEventToPersistedInspectEvent(event, options) {
3783
3958
  timestamp: tsMain.iso,
3784
3959
  confidence: "explicit",
3785
3960
  source,
3786
- attributes
3961
+ attributes: stampDeliveryEventBase(eventId, attributes)
3787
3962
  };
3788
3963
  if (event.parentId !== void 0) {
3789
3964
  out.parentId = event.parentId;
@@ -3936,7 +4111,47 @@ var TreeBuilder = class {
3936
4111
  const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
3937
4112
  const nodes = /* @__PURE__ */ new Map();
3938
4113
  for (const e of sorted) {
3939
- nodes.set(e.eventId, { event: e, children: [], depth: 0 });
4114
+ const existing = nodes.get(e.eventId);
4115
+ if (!existing) {
4116
+ nodes.set(e.eventId, { event: e, children: [], depth: 0 });
4117
+ continue;
4118
+ }
4119
+ const existingAttrs = existing.event.attributes && typeof existing.event.attributes === "object" ? existing.event.attributes : void 0;
4120
+ const nextAttrs = e.attributes && typeof e.attributes === "object" ? e.attributes : void 0;
4121
+ const existingIdentity = identityFieldsFromAttributes(existingAttrs);
4122
+ const nextIdentity = identityFieldsFromAttributes(nextAttrs);
4123
+ const existingFp = deliverySemanticFingerprint({
4124
+ eventId: existing.event.eventId,
4125
+ runId: existing.event.runId,
4126
+ kind: existing.event.kind,
4127
+ name: existing.event.name,
4128
+ status: existing.event.status,
4129
+ parentId: existing.event.parentId,
4130
+ timestamp: existing.event.timestamp,
4131
+ sourceType: existing.event.source?.type,
4132
+ attributes: existingAttrs,
4133
+ ...existingIdentity
4134
+ });
4135
+ const nextFp = deliverySemanticFingerprint({
4136
+ eventId: e.eventId,
4137
+ runId: e.runId,
4138
+ kind: e.kind,
4139
+ name: e.name,
4140
+ status: e.status,
4141
+ parentId: e.parentId,
4142
+ timestamp: e.timestamp,
4143
+ sourceType: e.source?.type,
4144
+ attributes: nextAttrs,
4145
+ ...nextIdentity
4146
+ });
4147
+ if (existingFp === nextFp) {
4148
+ continue;
4149
+ }
4150
+ nodes.set(e.eventId, {
4151
+ event: stampDeliveryConflict(e),
4152
+ children: [],
4153
+ depth: 0
4154
+ });
3940
4155
  }
3941
4156
  const { roots, summary } = linkNodesVisibilityFirst(nodes);
3942
4157
  const confidenceBreakdown = {
@@ -5401,6 +5616,12 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
5401
5616
  ["agent_inspect.retry_of", "retryOf", "string"],
5402
5617
  ["agent_inspect.attempt_number", "attemptNumber", "int"]
5403
5618
  ];
5619
+ const conflictMapping = [
5620
+ ["agent_inspect.identity_conflict.operation_id", "operationId", "string"],
5621
+ ["agent_inspect.identity_conflict.attempt_id", "attemptId", "string"],
5622
+ ["agent_inspect.identity_conflict.retry_of", "retryOf", "string"],
5623
+ ["agent_inspect.identity_conflict.attempt_number", "attemptNumber", "int"]
5624
+ ];
5404
5625
  const metadata = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? { ...attributes.metadata } : {};
5405
5626
  for (const [dotted, camel, kind] of mapping) {
5406
5627
  const raw = attributes[dotted];
@@ -5452,6 +5673,55 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
5452
5673
  if (direct === void 0) attributes[camel] = promoted;
5453
5674
  if (nested === void 0) metadata[camel] = promoted;
5454
5675
  }
5676
+ for (const [dotted, camel, kind] of conflictMapping) {
5677
+ const raw = attributes[dotted];
5678
+ if (raw === void 0 || raw === null) continue;
5679
+ let alternate;
5680
+ if (kind === "int") {
5681
+ const n = typeof raw === "number" ? raw : typeof raw === "string" ? Number(raw) : NaN;
5682
+ if (!Number.isFinite(n) || n < 1) {
5683
+ warnings.push({
5684
+ code: "otlp_identity_invalid",
5685
+ message: `Ignoring invalid ${dotted} identity conflict value.`,
5686
+ severity: "warning",
5687
+ field: `${pathPrefix}.${dotted}`
5688
+ });
5689
+ continue;
5690
+ }
5691
+ alternate = Math.trunc(n);
5692
+ } else if (typeof raw === "string" && raw.trim() !== "") {
5693
+ alternate = raw.trim();
5694
+ } else {
5695
+ warnings.push({
5696
+ code: "otlp_identity_invalid",
5697
+ message: `Ignoring non-string ${dotted} identity conflict value.`,
5698
+ severity: "warning",
5699
+ field: `${pathPrefix}.${dotted}`
5700
+ });
5701
+ continue;
5702
+ }
5703
+ const preferred = attributes[camel];
5704
+ if (preferred !== void 0 && preferred !== alternate) {
5705
+ metadata[camel] = alternate;
5706
+ warnings.push({
5707
+ code: "otlp_identity_conflict",
5708
+ message: `Conflicting ${camel}: attributes.${camel} vs nested alternate from ${dotted}; retaining both.`,
5709
+ severity: "warning",
5710
+ field: `${pathPrefix}.${dotted}`
5711
+ });
5712
+ } else if (preferred === void 0) {
5713
+ metadata[camel] = alternate;
5714
+ }
5715
+ }
5716
+ const conflictFields = attributes["agent_inspect.identity_conflict.fields"];
5717
+ if (typeof conflictFields === "string" && conflictFields.trim() !== "") {
5718
+ warnings.push({
5719
+ code: "otlp_identity_conflict",
5720
+ message: `Identity representation conflict fields retained after OTLP bounding: ${conflictFields.trim().slice(0, 200)}.`,
5721
+ severity: "warning",
5722
+ field: `${pathPrefix}.agent_inspect.identity_conflict.fields`
5723
+ });
5724
+ }
5455
5725
  if (Object.keys(metadata).length > 0) {
5456
5726
  attributes.metadata = metadata;
5457
5727
  }