@agent-inspect/viewer 6.31.20 → 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.cjs +247 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +247 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -2755,22 +2755,178 @@ 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 SYNTHETIC_DELIVERY_ID = /^(?:manual|inspect):[A-Za-z0-9_-]+:[A-Za-z0-9_-]+:[A-Za-z0-9_-]+:\d+$/;
|
|
2817
|
+
function stripReaderDeliveryDiscriminator(eventId) {
|
|
2818
|
+
if (!SYNTHETIC_DELIVERY_ID.test(eventId)) return eventId;
|
|
2819
|
+
return eventId.replace(/:\d+$/, "");
|
|
2820
|
+
}
|
|
2821
|
+
function canonicalDigest(value) {
|
|
2822
|
+
if (value === void 0) return null;
|
|
2823
|
+
const encoded = encodeCanonicalStructured(value);
|
|
2824
|
+
return encoded.ok ? encoded.value : `unencodable:${encoded.code}`;
|
|
2825
|
+
}
|
|
2826
|
+
function capturedContentDigest(attributes) {
|
|
2827
|
+
if (!attributes || typeof attributes !== "object") {
|
|
2828
|
+
return { args: null, result: null };
|
|
2829
|
+
}
|
|
2830
|
+
const nested = attributes.metadata && typeof attributes.metadata === "object" ? attributes.metadata : void 0;
|
|
2831
|
+
const args = attributes.arguments ?? attributes.args ?? attributes.input ?? nested?.arguments ?? nested?.args ?? nested?.input;
|
|
2832
|
+
const result = attributes.result ?? attributes.output ?? attributes.response ?? nested?.result ?? nested?.output ?? nested?.response;
|
|
2833
|
+
return {
|
|
2834
|
+
args: args === void 0 ? null : canonicalDigest(args),
|
|
2835
|
+
result: result === void 0 ? null : canonicalDigest(result)
|
|
2836
|
+
};
|
|
2837
|
+
}
|
|
2838
|
+
function timeKey(fields) {
|
|
2839
|
+
if (typeof fields.startedAt === "string" && fields.startedAt.trim() !== "") {
|
|
2840
|
+
return fields.startedAt;
|
|
2841
|
+
}
|
|
2842
|
+
if (typeof fields.timestamp === "string" && fields.timestamp.trim() !== "") {
|
|
2843
|
+
return fields.timestamp;
|
|
2844
|
+
}
|
|
2845
|
+
if (typeof fields.timestamp === "number" && Number.isFinite(fields.timestamp)) {
|
|
2846
|
+
return String(fields.timestamp);
|
|
2847
|
+
}
|
|
2848
|
+
return null;
|
|
2849
|
+
}
|
|
2850
|
+
function deliverySemanticFingerprint(fields) {
|
|
2851
|
+
const content = capturedContentDigest(fields.attributes);
|
|
2852
|
+
return JSON.stringify({
|
|
2853
|
+
stableEventId: stripReaderDeliveryDiscriminator(fields.eventId),
|
|
2854
|
+
runId: fields.runId,
|
|
2855
|
+
kind: fields.kind,
|
|
2856
|
+
name: fields.name,
|
|
2857
|
+
status: fields.status ?? null,
|
|
2858
|
+
parentId: fields.parentId ?? null,
|
|
2859
|
+
sourceType: fields.sourceType ?? null,
|
|
2860
|
+
time: timeKey(fields),
|
|
2861
|
+
operationId: fields.operationId ?? null,
|
|
2862
|
+
attemptId: fields.attemptId ?? null,
|
|
2863
|
+
attemptNumber: fields.attemptNumber ?? null,
|
|
2864
|
+
retryOf: fields.retryOf ?? null,
|
|
2865
|
+
argsDigest: content.args,
|
|
2866
|
+
resultDigest: content.result
|
|
2867
|
+
});
|
|
2868
|
+
}
|
|
2869
|
+
function identityFieldsFromAttributes(attributes) {
|
|
2870
|
+
if (!attributes || typeof attributes !== "object") {
|
|
2871
|
+
return {
|
|
2872
|
+
operationId: null,
|
|
2873
|
+
attemptId: null,
|
|
2874
|
+
attemptNumber: null,
|
|
2875
|
+
retryOf: null
|
|
2876
|
+
};
|
|
2877
|
+
}
|
|
2878
|
+
const nested = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? attributes.metadata : void 0;
|
|
2879
|
+
const pickString2 = (key) => {
|
|
2880
|
+
const direct = attributes[key];
|
|
2881
|
+
if (typeof direct === "string" && direct.trim() !== "") return direct.trim();
|
|
2882
|
+
const nestedValue = nested?.[key];
|
|
2883
|
+
if (typeof nestedValue === "string" && nestedValue.trim() !== "") {
|
|
2884
|
+
return nestedValue.trim();
|
|
2885
|
+
}
|
|
2886
|
+
return null;
|
|
2887
|
+
};
|
|
2888
|
+
const pickNumber = () => {
|
|
2889
|
+
const direct = attributes.attemptNumber ?? attributes.attempt;
|
|
2890
|
+
if (typeof direct === "number" && Number.isFinite(direct)) return direct;
|
|
2891
|
+
const nestedValue = nested?.attemptNumber ?? nested?.attempt;
|
|
2892
|
+
if (typeof nestedValue === "number" && Number.isFinite(nestedValue)) {
|
|
2893
|
+
return nestedValue;
|
|
2894
|
+
}
|
|
2895
|
+
return null;
|
|
2896
|
+
};
|
|
2897
|
+
return {
|
|
2898
|
+
operationId: pickString2("operationId"),
|
|
2899
|
+
attemptId: pickString2("attemptId"),
|
|
2900
|
+
retryOf: pickString2("retryOf"),
|
|
2901
|
+
attemptNumber: pickNumber()
|
|
2902
|
+
};
|
|
2903
|
+
}
|
|
2904
|
+
function stampDeliveryConflict(event) {
|
|
2905
|
+
return {
|
|
2906
|
+
...event,
|
|
2907
|
+
attributes: {
|
|
2908
|
+
...event.attributes ?? {},
|
|
2909
|
+
[DELIVERY_CONFLICT_ATTR]: true
|
|
2910
|
+
}
|
|
2911
|
+
};
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2758
2914
|
// packages/core/src/checks/contract.ts
|
|
2759
2915
|
new Set(OBSERVED_OUTCOME_METHODS);
|
|
2760
2916
|
|
|
2761
2917
|
// packages/core/src/exporters/helpers.ts
|
|
2762
|
-
function
|
|
2918
|
+
function sortKeysDeep2(input) {
|
|
2763
2919
|
if (input === null || typeof input !== "object") return input;
|
|
2764
|
-
if (Array.isArray(input)) return input.map(
|
|
2920
|
+
if (Array.isArray(input)) return input.map(sortKeysDeep2);
|
|
2765
2921
|
const o = input;
|
|
2766
2922
|
const out = {};
|
|
2767
2923
|
for (const k of Object.keys(o).sort()) {
|
|
2768
|
-
out[k] =
|
|
2924
|
+
out[k] = sortKeysDeep2(o[k]);
|
|
2769
2925
|
}
|
|
2770
2926
|
return out;
|
|
2771
2927
|
}
|
|
2772
2928
|
function stableJson(value, pretty) {
|
|
2773
|
-
const sorted =
|
|
2929
|
+
const sorted = sortKeysDeep2(value);
|
|
2774
2930
|
return JSON.stringify(sorted);
|
|
2775
2931
|
}
|
|
2776
2932
|
|
|
@@ -3936,7 +4092,47 @@ var TreeBuilder = class {
|
|
|
3936
4092
|
const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
|
|
3937
4093
|
const nodes = /* @__PURE__ */ new Map();
|
|
3938
4094
|
for (const e of sorted) {
|
|
3939
|
-
nodes.
|
|
4095
|
+
const existing = nodes.get(e.eventId);
|
|
4096
|
+
if (!existing) {
|
|
4097
|
+
nodes.set(e.eventId, { event: e, children: [], depth: 0 });
|
|
4098
|
+
continue;
|
|
4099
|
+
}
|
|
4100
|
+
const existingAttrs = existing.event.attributes && typeof existing.event.attributes === "object" ? existing.event.attributes : void 0;
|
|
4101
|
+
const nextAttrs = e.attributes && typeof e.attributes === "object" ? e.attributes : void 0;
|
|
4102
|
+
const existingIdentity = identityFieldsFromAttributes(existingAttrs);
|
|
4103
|
+
const nextIdentity = identityFieldsFromAttributes(nextAttrs);
|
|
4104
|
+
const existingFp = deliverySemanticFingerprint({
|
|
4105
|
+
eventId: existing.event.eventId,
|
|
4106
|
+
runId: existing.event.runId,
|
|
4107
|
+
kind: existing.event.kind,
|
|
4108
|
+
name: existing.event.name,
|
|
4109
|
+
status: existing.event.status,
|
|
4110
|
+
parentId: existing.event.parentId,
|
|
4111
|
+
timestamp: existing.event.timestamp,
|
|
4112
|
+
sourceType: existing.event.source?.type,
|
|
4113
|
+
attributes: existingAttrs,
|
|
4114
|
+
...existingIdentity
|
|
4115
|
+
});
|
|
4116
|
+
const nextFp = deliverySemanticFingerprint({
|
|
4117
|
+
eventId: e.eventId,
|
|
4118
|
+
runId: e.runId,
|
|
4119
|
+
kind: e.kind,
|
|
4120
|
+
name: e.name,
|
|
4121
|
+
status: e.status,
|
|
4122
|
+
parentId: e.parentId,
|
|
4123
|
+
timestamp: e.timestamp,
|
|
4124
|
+
sourceType: e.source?.type,
|
|
4125
|
+
attributes: nextAttrs,
|
|
4126
|
+
...nextIdentity
|
|
4127
|
+
});
|
|
4128
|
+
if (existingFp === nextFp) {
|
|
4129
|
+
continue;
|
|
4130
|
+
}
|
|
4131
|
+
nodes.set(e.eventId, {
|
|
4132
|
+
event: stampDeliveryConflict(e),
|
|
4133
|
+
children: [],
|
|
4134
|
+
depth: 0
|
|
4135
|
+
});
|
|
3940
4136
|
}
|
|
3941
4137
|
const { roots, summary } = linkNodesVisibilityFirst(nodes);
|
|
3942
4138
|
const confidenceBreakdown = {
|
|
@@ -5401,6 +5597,12 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
|
|
|
5401
5597
|
["agent_inspect.retry_of", "retryOf", "string"],
|
|
5402
5598
|
["agent_inspect.attempt_number", "attemptNumber", "int"]
|
|
5403
5599
|
];
|
|
5600
|
+
const conflictMapping = [
|
|
5601
|
+
["agent_inspect.identity_conflict.operation_id", "operationId", "string"],
|
|
5602
|
+
["agent_inspect.identity_conflict.attempt_id", "attemptId", "string"],
|
|
5603
|
+
["agent_inspect.identity_conflict.retry_of", "retryOf", "string"],
|
|
5604
|
+
["agent_inspect.identity_conflict.attempt_number", "attemptNumber", "int"]
|
|
5605
|
+
];
|
|
5404
5606
|
const metadata = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? { ...attributes.metadata } : {};
|
|
5405
5607
|
for (const [dotted, camel, kind] of mapping) {
|
|
5406
5608
|
const raw = attributes[dotted];
|
|
@@ -5452,6 +5654,46 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
|
|
|
5452
5654
|
if (direct === void 0) attributes[camel] = promoted;
|
|
5453
5655
|
if (nested === void 0) metadata[camel] = promoted;
|
|
5454
5656
|
}
|
|
5657
|
+
for (const [dotted, camel, kind] of conflictMapping) {
|
|
5658
|
+
const raw = attributes[dotted];
|
|
5659
|
+
if (raw === void 0 || raw === null) continue;
|
|
5660
|
+
let alternate;
|
|
5661
|
+
if (kind === "int") {
|
|
5662
|
+
const n = typeof raw === "number" ? raw : typeof raw === "string" ? Number(raw) : NaN;
|
|
5663
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
5664
|
+
warnings.push({
|
|
5665
|
+
code: "otlp_identity_invalid",
|
|
5666
|
+
message: `Ignoring invalid ${dotted} identity conflict value.`,
|
|
5667
|
+
severity: "warning",
|
|
5668
|
+
field: `${pathPrefix}.${dotted}`
|
|
5669
|
+
});
|
|
5670
|
+
continue;
|
|
5671
|
+
}
|
|
5672
|
+
alternate = Math.trunc(n);
|
|
5673
|
+
} else if (typeof raw === "string" && raw.trim() !== "") {
|
|
5674
|
+
alternate = raw.trim();
|
|
5675
|
+
} else {
|
|
5676
|
+
warnings.push({
|
|
5677
|
+
code: "otlp_identity_invalid",
|
|
5678
|
+
message: `Ignoring non-string ${dotted} identity conflict value.`,
|
|
5679
|
+
severity: "warning",
|
|
5680
|
+
field: `${pathPrefix}.${dotted}`
|
|
5681
|
+
});
|
|
5682
|
+
continue;
|
|
5683
|
+
}
|
|
5684
|
+
const preferred = attributes[camel];
|
|
5685
|
+
if (preferred !== void 0 && preferred !== alternate) {
|
|
5686
|
+
metadata[camel] = alternate;
|
|
5687
|
+
warnings.push({
|
|
5688
|
+
code: "otlp_identity_conflict",
|
|
5689
|
+
message: `Conflicting ${camel}: attributes.${camel} vs nested alternate from ${dotted}; retaining both.`,
|
|
5690
|
+
severity: "warning",
|
|
5691
|
+
field: `${pathPrefix}.${dotted}`
|
|
5692
|
+
});
|
|
5693
|
+
} else if (preferred === void 0) {
|
|
5694
|
+
metadata[camel] = alternate;
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5455
5697
|
if (Object.keys(metadata).length > 0) {
|
|
5456
5698
|
attributes.metadata = metadata;
|
|
5457
5699
|
}
|