@agent-inspect/viewer 6.31.21 → 6.31.22

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,178 @@ 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 SYNTHETIC_DELIVERY_ID = /^(?:manual|inspect):[A-Za-z0-9_-]+:[A-Za-z0-9_-]+:[A-Za-z0-9_-]+:\d+$/;
2810
+ function stripReaderDeliveryDiscriminator(eventId) {
2811
+ if (!SYNTHETIC_DELIVERY_ID.test(eventId)) return eventId;
2812
+ return eventId.replace(/:\d+$/, "");
2813
+ }
2814
+ function canonicalDigest(value) {
2815
+ if (value === void 0) return null;
2816
+ const encoded = encodeCanonicalStructured(value);
2817
+ return encoded.ok ? encoded.value : `unencodable:${encoded.code}`;
2818
+ }
2819
+ function capturedContentDigest(attributes) {
2820
+ if (!attributes || typeof attributes !== "object") {
2821
+ return { args: null, result: null };
2822
+ }
2823
+ const nested = attributes.metadata && typeof attributes.metadata === "object" ? attributes.metadata : void 0;
2824
+ const args = attributes.arguments ?? attributes.args ?? attributes.input ?? nested?.arguments ?? nested?.args ?? nested?.input;
2825
+ const result = attributes.result ?? attributes.output ?? attributes.response ?? nested?.result ?? nested?.output ?? nested?.response;
2826
+ return {
2827
+ args: args === void 0 ? null : canonicalDigest(args),
2828
+ result: result === void 0 ? null : canonicalDigest(result)
2829
+ };
2830
+ }
2831
+ function timeKey(fields) {
2832
+ if (typeof fields.startedAt === "string" && fields.startedAt.trim() !== "") {
2833
+ return fields.startedAt;
2834
+ }
2835
+ if (typeof fields.timestamp === "string" && fields.timestamp.trim() !== "") {
2836
+ return fields.timestamp;
2837
+ }
2838
+ if (typeof fields.timestamp === "number" && Number.isFinite(fields.timestamp)) {
2839
+ return String(fields.timestamp);
2840
+ }
2841
+ return null;
2842
+ }
2843
+ function deliverySemanticFingerprint(fields) {
2844
+ const content = capturedContentDigest(fields.attributes);
2845
+ return JSON.stringify({
2846
+ stableEventId: stripReaderDeliveryDiscriminator(fields.eventId),
2847
+ runId: fields.runId,
2848
+ kind: fields.kind,
2849
+ name: fields.name,
2850
+ status: fields.status ?? null,
2851
+ parentId: fields.parentId ?? null,
2852
+ sourceType: fields.sourceType ?? null,
2853
+ time: timeKey(fields),
2854
+ operationId: fields.operationId ?? null,
2855
+ attemptId: fields.attemptId ?? null,
2856
+ attemptNumber: fields.attemptNumber ?? null,
2857
+ retryOf: fields.retryOf ?? null,
2858
+ argsDigest: content.args,
2859
+ resultDigest: content.result
2860
+ });
2861
+ }
2862
+ function identityFieldsFromAttributes(attributes) {
2863
+ if (!attributes || typeof attributes !== "object") {
2864
+ return {
2865
+ operationId: null,
2866
+ attemptId: null,
2867
+ attemptNumber: null,
2868
+ retryOf: null
2869
+ };
2870
+ }
2871
+ const nested = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? attributes.metadata : void 0;
2872
+ const pickString2 = (key) => {
2873
+ const direct = attributes[key];
2874
+ if (typeof direct === "string" && direct.trim() !== "") return direct.trim();
2875
+ const nestedValue = nested?.[key];
2876
+ if (typeof nestedValue === "string" && nestedValue.trim() !== "") {
2877
+ return nestedValue.trim();
2878
+ }
2879
+ return null;
2880
+ };
2881
+ const pickNumber = () => {
2882
+ const direct = attributes.attemptNumber ?? attributes.attempt;
2883
+ if (typeof direct === "number" && Number.isFinite(direct)) return direct;
2884
+ const nestedValue = nested?.attemptNumber ?? nested?.attempt;
2885
+ if (typeof nestedValue === "number" && Number.isFinite(nestedValue)) {
2886
+ return nestedValue;
2887
+ }
2888
+ return null;
2889
+ };
2890
+ return {
2891
+ operationId: pickString2("operationId"),
2892
+ attemptId: pickString2("attemptId"),
2893
+ retryOf: pickString2("retryOf"),
2894
+ attemptNumber: pickNumber()
2895
+ };
2896
+ }
2897
+ function stampDeliveryConflict(event) {
2898
+ return {
2899
+ ...event,
2900
+ attributes: {
2901
+ ...event.attributes ?? {},
2902
+ [DELIVERY_CONFLICT_ATTR]: true
2903
+ }
2904
+ };
2905
+ }
2906
+
2751
2907
  // packages/core/src/checks/contract.ts
2752
2908
  new Set(OBSERVED_OUTCOME_METHODS);
2753
2909
 
2754
2910
  // packages/core/src/exporters/helpers.ts
2755
- function sortKeysDeep(input) {
2911
+ function sortKeysDeep2(input) {
2756
2912
  if (input === null || typeof input !== "object") return input;
2757
- if (Array.isArray(input)) return input.map(sortKeysDeep);
2913
+ if (Array.isArray(input)) return input.map(sortKeysDeep2);
2758
2914
  const o = input;
2759
2915
  const out = {};
2760
2916
  for (const k of Object.keys(o).sort()) {
2761
- out[k] = sortKeysDeep(o[k]);
2917
+ out[k] = sortKeysDeep2(o[k]);
2762
2918
  }
2763
2919
  return out;
2764
2920
  }
2765
2921
  function stableJson(value, pretty) {
2766
- const sorted = sortKeysDeep(value);
2922
+ const sorted = sortKeysDeep2(value);
2767
2923
  return JSON.stringify(sorted);
2768
2924
  }
2769
2925
 
@@ -3929,7 +4085,47 @@ var TreeBuilder = class {
3929
4085
  const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
3930
4086
  const nodes = /* @__PURE__ */ new Map();
3931
4087
  for (const e of sorted) {
3932
- nodes.set(e.eventId, { event: e, children: [], depth: 0 });
4088
+ const existing = nodes.get(e.eventId);
4089
+ if (!existing) {
4090
+ nodes.set(e.eventId, { event: e, children: [], depth: 0 });
4091
+ continue;
4092
+ }
4093
+ const existingAttrs = existing.event.attributes && typeof existing.event.attributes === "object" ? existing.event.attributes : void 0;
4094
+ const nextAttrs = e.attributes && typeof e.attributes === "object" ? e.attributes : void 0;
4095
+ const existingIdentity = identityFieldsFromAttributes(existingAttrs);
4096
+ const nextIdentity = identityFieldsFromAttributes(nextAttrs);
4097
+ const existingFp = deliverySemanticFingerprint({
4098
+ eventId: existing.event.eventId,
4099
+ runId: existing.event.runId,
4100
+ kind: existing.event.kind,
4101
+ name: existing.event.name,
4102
+ status: existing.event.status,
4103
+ parentId: existing.event.parentId,
4104
+ timestamp: existing.event.timestamp,
4105
+ sourceType: existing.event.source?.type,
4106
+ attributes: existingAttrs,
4107
+ ...existingIdentity
4108
+ });
4109
+ const nextFp = deliverySemanticFingerprint({
4110
+ eventId: e.eventId,
4111
+ runId: e.runId,
4112
+ kind: e.kind,
4113
+ name: e.name,
4114
+ status: e.status,
4115
+ parentId: e.parentId,
4116
+ timestamp: e.timestamp,
4117
+ sourceType: e.source?.type,
4118
+ attributes: nextAttrs,
4119
+ ...nextIdentity
4120
+ });
4121
+ if (existingFp === nextFp) {
4122
+ continue;
4123
+ }
4124
+ nodes.set(e.eventId, {
4125
+ event: stampDeliveryConflict(e),
4126
+ children: [],
4127
+ depth: 0
4128
+ });
3933
4129
  }
3934
4130
  const { roots, summary } = linkNodesVisibilityFirst(nodes);
3935
4131
  const confidenceBreakdown = {
@@ -5394,6 +5590,12 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
5394
5590
  ["agent_inspect.retry_of", "retryOf", "string"],
5395
5591
  ["agent_inspect.attempt_number", "attemptNumber", "int"]
5396
5592
  ];
5593
+ const conflictMapping = [
5594
+ ["agent_inspect.identity_conflict.operation_id", "operationId", "string"],
5595
+ ["agent_inspect.identity_conflict.attempt_id", "attemptId", "string"],
5596
+ ["agent_inspect.identity_conflict.retry_of", "retryOf", "string"],
5597
+ ["agent_inspect.identity_conflict.attempt_number", "attemptNumber", "int"]
5598
+ ];
5397
5599
  const metadata = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? { ...attributes.metadata } : {};
5398
5600
  for (const [dotted, camel, kind] of mapping) {
5399
5601
  const raw = attributes[dotted];
@@ -5445,6 +5647,46 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
5445
5647
  if (direct === void 0) attributes[camel] = promoted;
5446
5648
  if (nested === void 0) metadata[camel] = promoted;
5447
5649
  }
5650
+ for (const [dotted, camel, kind] of conflictMapping) {
5651
+ const raw = attributes[dotted];
5652
+ if (raw === void 0 || raw === null) continue;
5653
+ let alternate;
5654
+ if (kind === "int") {
5655
+ const n = typeof raw === "number" ? raw : typeof raw === "string" ? Number(raw) : NaN;
5656
+ if (!Number.isFinite(n) || n < 1) {
5657
+ warnings.push({
5658
+ code: "otlp_identity_invalid",
5659
+ message: `Ignoring invalid ${dotted} identity conflict value.`,
5660
+ severity: "warning",
5661
+ field: `${pathPrefix}.${dotted}`
5662
+ });
5663
+ continue;
5664
+ }
5665
+ alternate = Math.trunc(n);
5666
+ } else if (typeof raw === "string" && raw.trim() !== "") {
5667
+ alternate = raw.trim();
5668
+ } else {
5669
+ warnings.push({
5670
+ code: "otlp_identity_invalid",
5671
+ message: `Ignoring non-string ${dotted} identity conflict value.`,
5672
+ severity: "warning",
5673
+ field: `${pathPrefix}.${dotted}`
5674
+ });
5675
+ continue;
5676
+ }
5677
+ const preferred = attributes[camel];
5678
+ if (preferred !== void 0 && preferred !== alternate) {
5679
+ metadata[camel] = alternate;
5680
+ warnings.push({
5681
+ code: "otlp_identity_conflict",
5682
+ message: `Conflicting ${camel}: attributes.${camel} vs nested alternate from ${dotted}; retaining both.`,
5683
+ severity: "warning",
5684
+ field: `${pathPrefix}.${dotted}`
5685
+ });
5686
+ } else if (preferred === void 0) {
5687
+ metadata[camel] = alternate;
5688
+ }
5689
+ }
5448
5690
  if (Object.keys(metadata).length > 0) {
5449
5691
  attributes.metadata = metadata;
5450
5692
  }