@agent-inspect/mcp-server 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/cli.cjs CHANGED
@@ -4092,6 +4092,184 @@ function runTraceChecks(input, options = {}) {
4092
4092
  };
4093
4093
  }
4094
4094
 
4095
+ // packages/core/src/checks/canonical-equality.ts
4096
+ var DEFAULT_MAX_DEPTH = 8;
4097
+ var DEFAULT_MAX_BYTES = 16384;
4098
+ function sortKeysDeep(input, depth, maxDepth) {
4099
+ if (depth > maxDepth) {
4100
+ throw new Error("AI_CHECK_CANONICAL_DEPTH_EXCEEDED");
4101
+ }
4102
+ if (input === null || typeof input !== "object") return input;
4103
+ if (Array.isArray(input)) {
4104
+ return input.map((item) => sortKeysDeep(item, depth + 1, maxDepth));
4105
+ }
4106
+ const record = input;
4107
+ const out = {};
4108
+ for (const key of Object.keys(record).sort()) {
4109
+ out[key] = sortKeysDeep(record[key], depth + 1, maxDepth);
4110
+ }
4111
+ return out;
4112
+ }
4113
+ function encodeCanonicalStructured(value, limits) {
4114
+ const maxDepth = DEFAULT_MAX_DEPTH;
4115
+ const maxBytes = DEFAULT_MAX_BYTES;
4116
+ try {
4117
+ const sorted = sortKeysDeep(value, 0, maxDepth);
4118
+ const encoded = JSON.stringify(sorted);
4119
+ if (encoded === void 0) {
4120
+ return {
4121
+ ok: false,
4122
+ reason: "Value is not JSON-serializable for canonical comparison.",
4123
+ code: "AI_CHECK_CANONICAL_UNSERIALIZABLE"
4124
+ };
4125
+ }
4126
+ if (Buffer.byteLength(encoded, "utf8") > maxBytes) {
4127
+ return {
4128
+ ok: false,
4129
+ reason: "Canonical structured value exceeds bounded size limit.",
4130
+ code: "AI_CHECK_CANONICAL_SIZE_EXCEEDED"
4131
+ };
4132
+ }
4133
+ return { ok: true, value: encoded };
4134
+ } catch (error) {
4135
+ const message = error instanceof Error ? error.message : String(error);
4136
+ if (message === "AI_CHECK_CANONICAL_DEPTH_EXCEEDED") {
4137
+ return {
4138
+ ok: false,
4139
+ reason: "Canonical structured value exceeds bounded depth limit.",
4140
+ code: "AI_CHECK_CANONICAL_DEPTH_EXCEEDED"
4141
+ };
4142
+ }
4143
+ return {
4144
+ ok: false,
4145
+ reason: "Canonical structured encoding failed.",
4146
+ code: "AI_CHECK_CANONICAL_UNSERIALIZABLE"
4147
+ };
4148
+ }
4149
+ }
4150
+
4151
+ // packages/core/src/persisted/delivery-identity.ts
4152
+ var DELIVERY_CONFLICT_ATTR = "agent_inspect.delivery_conflict";
4153
+ var DELIVERY_EVENT_BASE_ATTR = "agent_inspect.delivery_event_base";
4154
+ var IDENTITY_CONFLICT_ATTR = {
4155
+ operationId: "agent_inspect.identity_conflict.operation_id",
4156
+ attemptId: "agent_inspect.identity_conflict.attempt_id",
4157
+ retryOf: "agent_inspect.identity_conflict.retry_of",
4158
+ attemptNumber: "agent_inspect.identity_conflict.attempt_number",
4159
+ /** Comma-separated field names when a conflict survives lossy bounding. */
4160
+ fields: "agent_inspect.identity_conflict.fields"
4161
+ };
4162
+ var MAX_DELIVERY_BASE_LEN = 500;
4163
+ function stripReaderDeliveryDiscriminator(eventId, attributes) {
4164
+ if (!eventId) return eventId;
4165
+ const rawBase = attributes?.[DELIVERY_EVENT_BASE_ATTR];
4166
+ if (typeof rawBase !== "string" || rawBase.trim() === "") return eventId;
4167
+ const base = rawBase.trim().slice(0, MAX_DELIVERY_BASE_LEN);
4168
+ if (eventId === base) return base;
4169
+ const prefix = `${base}:`;
4170
+ if (!eventId.startsWith(prefix)) return eventId;
4171
+ const suffix = eventId.slice(prefix.length);
4172
+ if (/^\d+$/.test(suffix)) return base;
4173
+ return eventId;
4174
+ }
4175
+ function deliveryEventBaseFromIndexedId(eventId) {
4176
+ if (!/:\d+$/.test(eventId)) return void 0;
4177
+ const base = eventId.replace(/:\d+$/, "");
4178
+ return base.length > 0 ? base.slice(0, MAX_DELIVERY_BASE_LEN) : void 0;
4179
+ }
4180
+ function canonicalDigest(value) {
4181
+ if (value === void 0) return null;
4182
+ const encoded = encodeCanonicalStructured(value);
4183
+ return encoded.ok ? encoded.value : `unencodable:${encoded.code}`;
4184
+ }
4185
+ function capturedContentDigest(attributes) {
4186
+ if (!attributes || typeof attributes !== "object") {
4187
+ return { args: null, result: null };
4188
+ }
4189
+ const nested = attributes.metadata && typeof attributes.metadata === "object" ? attributes.metadata : void 0;
4190
+ const args = attributes.arguments ?? attributes.args ?? attributes.input ?? nested?.arguments ?? nested?.args ?? nested?.input;
4191
+ const result = attributes.result ?? attributes.output ?? attributes.response ?? nested?.result ?? nested?.output ?? nested?.response;
4192
+ return {
4193
+ args: args === void 0 ? null : canonicalDigest(args),
4194
+ result: result === void 0 ? null : canonicalDigest(result)
4195
+ };
4196
+ }
4197
+ function timeKey(fields) {
4198
+ if (typeof fields.startedAt === "string" && fields.startedAt.trim() !== "") {
4199
+ return fields.startedAt;
4200
+ }
4201
+ if (typeof fields.timestamp === "string" && fields.timestamp.trim() !== "") {
4202
+ return fields.timestamp;
4203
+ }
4204
+ if (typeof fields.timestamp === "number" && Number.isFinite(fields.timestamp)) {
4205
+ return String(fields.timestamp);
4206
+ }
4207
+ return null;
4208
+ }
4209
+ function deliverySemanticFingerprint(fields) {
4210
+ const content = capturedContentDigest(fields.attributes);
4211
+ return JSON.stringify({
4212
+ stableEventId: stripReaderDeliveryDiscriminator(fields.eventId, fields.attributes),
4213
+ runId: fields.runId,
4214
+ kind: fields.kind,
4215
+ name: fields.name,
4216
+ status: fields.status ?? null,
4217
+ parentId: fields.parentId ?? null,
4218
+ sourceType: fields.sourceType ?? null,
4219
+ time: timeKey(fields),
4220
+ operationId: fields.operationId ?? null,
4221
+ attemptId: fields.attemptId ?? null,
4222
+ attemptNumber: fields.attemptNumber ?? null,
4223
+ retryOf: fields.retryOf ?? null,
4224
+ argsDigest: content.args,
4225
+ resultDigest: content.result
4226
+ });
4227
+ }
4228
+ function identityFieldsFromAttributes(attributes) {
4229
+ if (!attributes || typeof attributes !== "object") {
4230
+ return {
4231
+ operationId: null,
4232
+ attemptId: null,
4233
+ attemptNumber: null,
4234
+ retryOf: null
4235
+ };
4236
+ }
4237
+ const nested = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? attributes.metadata : void 0;
4238
+ const pickString3 = (key) => {
4239
+ const direct = attributes[key];
4240
+ if (typeof direct === "string" && direct.trim() !== "") return direct.trim();
4241
+ const nestedValue = nested?.[key];
4242
+ if (typeof nestedValue === "string" && nestedValue.trim() !== "") {
4243
+ return nestedValue.trim();
4244
+ }
4245
+ return null;
4246
+ };
4247
+ const pickNumber2 = () => {
4248
+ const direct = attributes.attemptNumber ?? attributes.attempt;
4249
+ if (typeof direct === "number" && Number.isFinite(direct)) return direct;
4250
+ const nestedValue = nested?.attemptNumber ?? nested?.attempt;
4251
+ if (typeof nestedValue === "number" && Number.isFinite(nestedValue)) {
4252
+ return nestedValue;
4253
+ }
4254
+ return null;
4255
+ };
4256
+ return {
4257
+ operationId: pickString3("operationId"),
4258
+ attemptId: pickString3("attemptId"),
4259
+ retryOf: pickString3("retryOf"),
4260
+ attemptNumber: pickNumber2()
4261
+ };
4262
+ }
4263
+ function stampDeliveryConflict(event) {
4264
+ return {
4265
+ ...event,
4266
+ attributes: {
4267
+ ...event.attributes ?? {},
4268
+ [DELIVERY_CONFLICT_ATTR]: true
4269
+ }
4270
+ };
4271
+ }
4272
+
4095
4273
  // packages/core/src/checks/contract.ts
4096
4274
  new Set(OBSERVED_OUTCOME_METHODS);
4097
4275
 
@@ -4129,18 +4307,18 @@ function escapeMarkdown(value) {
4129
4307
  function escapeHtml(value) {
4130
4308
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
4131
4309
  }
4132
- function sortKeysDeep(input) {
4310
+ function sortKeysDeep2(input) {
4133
4311
  if (input === null || typeof input !== "object") return input;
4134
- if (Array.isArray(input)) return input.map(sortKeysDeep);
4312
+ if (Array.isArray(input)) return input.map(sortKeysDeep2);
4135
4313
  const o = input;
4136
4314
  const out = {};
4137
4315
  for (const k of Object.keys(o).sort()) {
4138
- out[k] = sortKeysDeep(o[k]);
4316
+ out[k] = sortKeysDeep2(o[k]);
4139
4317
  }
4140
4318
  return out;
4141
4319
  }
4142
4320
  function stableJson(value, pretty) {
4143
- const sorted = sortKeysDeep(value);
4321
+ const sorted = sortKeysDeep2(value);
4144
4322
  return pretty === true ? JSON.stringify(sorted, null, 2) : JSON.stringify(sorted);
4145
4323
  }
4146
4324
  function compactAttributes(attrs2, options) {
@@ -4677,6 +4855,11 @@ function compactAttributes2(entries) {
4677
4855
  }
4678
4856
  return Object.keys(out).length > 0 ? out : void 0;
4679
4857
  }
4858
+ function stampDeliveryEventBase(eventId, attributes) {
4859
+ const base = deliveryEventBaseFromIndexedId(eventId);
4860
+ if (!base) return attributes;
4861
+ return { ...attributes ?? {}, [DELIVERY_EVENT_BASE_ATTR]: base };
4862
+ }
4680
4863
  function traceEventToPersistedInspectEvent(event, options) {
4681
4864
  const eventIndex = options?.eventIndex ?? 0;
4682
4865
  const eventId = createPersistedEventId(event, eventIndex);
@@ -4706,7 +4889,7 @@ function traceEventToPersistedInspectEvent(event, options) {
4706
4889
  startedAt: tsStart.iso,
4707
4890
  confidence: "explicit",
4708
4891
  source,
4709
- attributes
4892
+ attributes: stampDeliveryEventBase(eventId, attributes)
4710
4893
  };
4711
4894
  }
4712
4895
  case "run_completed": {
@@ -4729,7 +4912,7 @@ function traceEventToPersistedInspectEvent(event, options) {
4729
4912
  durationMs: event.durationMs,
4730
4913
  confidence: "explicit",
4731
4914
  source,
4732
- attributes,
4915
+ attributes: stampDeliveryEventBase(eventId, attributes),
4733
4916
  error
4734
4917
  };
4735
4918
  }
@@ -4754,7 +4937,7 @@ function traceEventToPersistedInspectEvent(event, options) {
4754
4937
  startedAt: tsStart.iso,
4755
4938
  confidence: "explicit",
4756
4939
  source,
4757
- attributes
4940
+ attributes: stampDeliveryEventBase(eventId, attributes)
4758
4941
  };
4759
4942
  if (event.parentId !== void 0) {
4760
4943
  out.parentId = event.parentId;
@@ -4785,7 +4968,7 @@ function traceEventToPersistedInspectEvent(event, options) {
4785
4968
  durationMs: event.durationMs,
4786
4969
  confidence: "explicit",
4787
4970
  source,
4788
- attributes,
4971
+ attributes: stampDeliveryEventBase(eventId, attributes),
4789
4972
  error
4790
4973
  };
4791
4974
  }
@@ -4812,7 +4995,7 @@ function traceEventToPersistedInspectEvent(event, options) {
4812
4995
  timestamp: tsMain.iso,
4813
4996
  confidence: "explicit",
4814
4997
  source,
4815
- attributes
4998
+ attributes: stampDeliveryEventBase(eventId, attributes)
4816
4999
  };
4817
5000
  if (event.parentId !== void 0) {
4818
5001
  out.parentId = event.parentId;
@@ -4965,7 +5148,47 @@ var TreeBuilder = class {
4965
5148
  const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
4966
5149
  const nodes = /* @__PURE__ */ new Map();
4967
5150
  for (const e of sorted) {
4968
- nodes.set(e.eventId, { event: e, children: [], depth: 0 });
5151
+ const existing = nodes.get(e.eventId);
5152
+ if (!existing) {
5153
+ nodes.set(e.eventId, { event: e, children: [], depth: 0 });
5154
+ continue;
5155
+ }
5156
+ const existingAttrs = existing.event.attributes && typeof existing.event.attributes === "object" ? existing.event.attributes : void 0;
5157
+ const nextAttrs = e.attributes && typeof e.attributes === "object" ? e.attributes : void 0;
5158
+ const existingIdentity = identityFieldsFromAttributes(existingAttrs);
5159
+ const nextIdentity = identityFieldsFromAttributes(nextAttrs);
5160
+ const existingFp = deliverySemanticFingerprint({
5161
+ eventId: existing.event.eventId,
5162
+ runId: existing.event.runId,
5163
+ kind: existing.event.kind,
5164
+ name: existing.event.name,
5165
+ status: existing.event.status,
5166
+ parentId: existing.event.parentId,
5167
+ timestamp: existing.event.timestamp,
5168
+ sourceType: existing.event.source?.type,
5169
+ attributes: existingAttrs,
5170
+ ...existingIdentity
5171
+ });
5172
+ const nextFp = deliverySemanticFingerprint({
5173
+ eventId: e.eventId,
5174
+ runId: e.runId,
5175
+ kind: e.kind,
5176
+ name: e.name,
5177
+ status: e.status,
5178
+ parentId: e.parentId,
5179
+ timestamp: e.timestamp,
5180
+ sourceType: e.source?.type,
5181
+ attributes: nextAttrs,
5182
+ ...nextIdentity
5183
+ });
5184
+ if (existingFp === nextFp) {
5185
+ continue;
5186
+ }
5187
+ nodes.set(e.eventId, {
5188
+ event: stampDeliveryConflict(e),
5189
+ children: [],
5190
+ depth: 0
5191
+ });
4969
5192
  }
4970
5193
  const { roots, summary } = linkNodesVisibilityFirst(nodes);
4971
5194
  const confidenceBreakdown = {
@@ -6430,6 +6653,12 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
6430
6653
  ["agent_inspect.retry_of", "retryOf", "string"],
6431
6654
  ["agent_inspect.attempt_number", "attemptNumber", "int"]
6432
6655
  ];
6656
+ const conflictMapping = [
6657
+ ["agent_inspect.identity_conflict.operation_id", "operationId", "string"],
6658
+ ["agent_inspect.identity_conflict.attempt_id", "attemptId", "string"],
6659
+ ["agent_inspect.identity_conflict.retry_of", "retryOf", "string"],
6660
+ ["agent_inspect.identity_conflict.attempt_number", "attemptNumber", "int"]
6661
+ ];
6433
6662
  const metadata = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? { ...attributes.metadata } : {};
6434
6663
  for (const [dotted, camel, kind] of mapping) {
6435
6664
  const raw = attributes[dotted];
@@ -6481,6 +6710,55 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
6481
6710
  if (direct === void 0) attributes[camel] = promoted;
6482
6711
  if (nested === void 0) metadata[camel] = promoted;
6483
6712
  }
6713
+ for (const [dotted, camel, kind] of conflictMapping) {
6714
+ const raw = attributes[dotted];
6715
+ if (raw === void 0 || raw === null) continue;
6716
+ let alternate;
6717
+ if (kind === "int") {
6718
+ const n = typeof raw === "number" ? raw : typeof raw === "string" ? Number(raw) : NaN;
6719
+ if (!Number.isFinite(n) || n < 1) {
6720
+ warnings.push({
6721
+ code: "otlp_identity_invalid",
6722
+ message: `Ignoring invalid ${dotted} identity conflict value.`,
6723
+ severity: "warning",
6724
+ field: `${pathPrefix}.${dotted}`
6725
+ });
6726
+ continue;
6727
+ }
6728
+ alternate = Math.trunc(n);
6729
+ } else if (typeof raw === "string" && raw.trim() !== "") {
6730
+ alternate = raw.trim();
6731
+ } else {
6732
+ warnings.push({
6733
+ code: "otlp_identity_invalid",
6734
+ message: `Ignoring non-string ${dotted} identity conflict value.`,
6735
+ severity: "warning",
6736
+ field: `${pathPrefix}.${dotted}`
6737
+ });
6738
+ continue;
6739
+ }
6740
+ const preferred = attributes[camel];
6741
+ if (preferred !== void 0 && preferred !== alternate) {
6742
+ metadata[camel] = alternate;
6743
+ warnings.push({
6744
+ code: "otlp_identity_conflict",
6745
+ message: `Conflicting ${camel}: attributes.${camel} vs nested alternate from ${dotted}; retaining both.`,
6746
+ severity: "warning",
6747
+ field: `${pathPrefix}.${dotted}`
6748
+ });
6749
+ } else if (preferred === void 0) {
6750
+ metadata[camel] = alternate;
6751
+ }
6752
+ }
6753
+ const conflictFields = attributes["agent_inspect.identity_conflict.fields"];
6754
+ if (typeof conflictFields === "string" && conflictFields.trim() !== "") {
6755
+ warnings.push({
6756
+ code: "otlp_identity_conflict",
6757
+ message: `Identity representation conflict fields retained after OTLP bounding: ${conflictFields.trim().slice(0, 200)}.`,
6758
+ severity: "warning",
6759
+ field: `${pathPrefix}.agent_inspect.identity_conflict.fields`
6760
+ });
6761
+ }
6484
6762
  if (Object.keys(metadata).length > 0) {
6485
6763
  attributes.metadata = metadata;
6486
6764
  }
@@ -7667,43 +7945,85 @@ var STRUCTURAL_IDENTITY_KEYS = [
7667
7945
  "attemptNumber",
7668
7946
  "attempt"
7669
7947
  ];
7670
- function pickBoundedIdentityString(bag, key, maxLen) {
7948
+ function nestedBag(bag) {
7671
7949
  if (!bag || typeof bag !== "object") return void 0;
7672
- const direct = bag[key];
7673
- if (typeof direct === "string" && direct.trim() !== "") {
7674
- return direct.trim().slice(0, maxLen);
7675
- }
7676
- const nested = bag.metadata && typeof bag.metadata === "object" ? bag.metadata[key] : void 0;
7677
- if (typeof nested === "string" && nested.trim() !== "") {
7678
- return nested.trim().slice(0, maxLen);
7950
+ const nested = bag.metadata;
7951
+ if (!nested || typeof nested !== "object" || Array.isArray(nested)) return void 0;
7952
+ return nested;
7953
+ }
7954
+ function pickBoundedIdentityString(bag, key, maxLen) {
7955
+ if (!bag || typeof bag !== "object") return {};
7956
+ const directRaw = bag[key];
7957
+ const nestedRaw = nestedBag(bag)?.[key];
7958
+ const directFull = typeof directRaw === "string" && directRaw.trim() !== "" ? directRaw.trim() : void 0;
7959
+ const nestedFull = typeof nestedRaw === "string" && nestedRaw.trim() !== "" ? nestedRaw.trim() : void 0;
7960
+ const direct = directFull !== void 0 ? directFull.slice(0, maxLen) : void 0;
7961
+ const nested = nestedFull !== void 0 ? nestedFull.slice(0, maxLen) : void 0;
7962
+ if (directFull !== void 0 && nestedFull !== void 0 && directFull !== nestedFull) {
7963
+ return {
7964
+ value: direct,
7965
+ conflictAlternate: nested,
7966
+ truncatedCollision: direct === nested
7967
+ };
7679
7968
  }
7680
- return void 0;
7969
+ if (direct !== void 0) return { value: direct };
7970
+ if (nested !== void 0) return { value: nested };
7971
+ return {};
7681
7972
  }
7682
7973
  function pickBoundedIdentityNumber(bag, key) {
7683
- if (!bag || typeof bag !== "object") return void 0;
7684
- const direct = bag[key];
7685
- if (typeof direct === "number" && Number.isFinite(direct)) return direct;
7686
- const nested = bag.metadata && typeof bag.metadata === "object" ? bag.metadata[key] : void 0;
7687
- if (typeof nested === "number" && Number.isFinite(nested)) return nested;
7688
- return void 0;
7974
+ if (!bag || typeof bag !== "object") return {};
7975
+ const directRaw = bag[key];
7976
+ const nestedRaw = nestedBag(bag)?.[key];
7977
+ const direct = typeof directRaw === "number" && Number.isFinite(directRaw) ? directRaw : void 0;
7978
+ const nested = typeof nestedRaw === "number" && Number.isFinite(nestedRaw) ? nestedRaw : void 0;
7979
+ if (direct !== void 0 && nested !== void 0 && direct !== nested) {
7980
+ return { value: direct, conflictAlternate: nested };
7981
+ }
7982
+ if (direct !== void 0) return { value: direct };
7983
+ if (nested !== void 0) return { value: nested };
7984
+ return {};
7689
7985
  }
7690
7986
  function appendStructuralIdentityAttrs(attrs2, meta, maxLen) {
7691
- const operationId = pickBoundedIdentityString(meta, "operationId", maxLen);
7692
- if (operationId !== void 0) {
7693
- attrs2.push(stringAttr2("agent_inspect.operation_id", operationId));
7987
+ const conflicts = [];
7988
+ const emitString = (key, otlpKey, conflictKey) => {
7989
+ const picked = pickBoundedIdentityString(meta, key, maxLen);
7990
+ if (picked.value !== void 0) {
7991
+ attrs2.push(stringAttr2(otlpKey, picked.value));
7992
+ }
7993
+ if (picked.conflictAlternate !== void 0 || picked.truncatedCollision) {
7994
+ if (picked.conflictAlternate !== void 0) {
7995
+ attrs2.push(stringAttr2(conflictKey, picked.conflictAlternate));
7996
+ }
7997
+ conflicts.push(key);
7998
+ }
7999
+ };
8000
+ emitString("operationId", "agent_inspect.operation_id", IDENTITY_CONFLICT_ATTR.operationId);
8001
+ emitString("attemptId", "agent_inspect.attempt_id", IDENTITY_CONFLICT_ATTR.attemptId);
8002
+ emitString("retryOf", "agent_inspect.retry_of", IDENTITY_CONFLICT_ATTR.retryOf);
8003
+ const attemptNumberPrimary = pickBoundedIdentityNumber(meta, "attemptNumber");
8004
+ const attemptNumber = attemptNumberPrimary.value !== void 0 || attemptNumberPrimary.conflictAlternate !== void 0 ? attemptNumberPrimary : pickBoundedIdentityNumber(meta, "attempt");
8005
+ if (attemptNumber.value !== void 0) {
8006
+ attrs2.push(intAttr("agent_inspect.attempt_number", attemptNumber.value));
8007
+ }
8008
+ if (attemptNumber.conflictAlternate !== void 0) {
8009
+ attrs2.push(
8010
+ intAttr(IDENTITY_CONFLICT_ATTR.attemptNumber, attemptNumber.conflictAlternate)
8011
+ );
8012
+ conflicts.push("attemptNumber");
7694
8013
  }
7695
- const attemptId = pickBoundedIdentityString(meta, "attemptId", maxLen);
7696
- if (attemptId !== void 0) {
7697
- attrs2.push(stringAttr2("agent_inspect.attempt_id", attemptId));
8014
+ if (conflicts.length > 0) {
8015
+ attrs2.push(
8016
+ stringAttr2(IDENTITY_CONFLICT_ATTR.fields, conflicts.join(",").slice(0, maxLen))
8017
+ );
7698
8018
  }
7699
- const retryOf = pickBoundedIdentityString(meta, "retryOf", maxLen);
7700
- if (retryOf !== void 0) {
7701
- attrs2.push(stringAttr2("agent_inspect.retry_of", retryOf));
8019
+ if (meta?.[DELIVERY_CONFLICT_ATTR] === true) {
8020
+ attrs2.push(boolAttr(DELIVERY_CONFLICT_ATTR, true));
7702
8021
  }
7703
- const attemptNumber = pickBoundedIdentityNumber(meta, "attemptNumber") ?? pickBoundedIdentityNumber(meta, "attempt");
7704
- if (attemptNumber !== void 0) {
7705
- attrs2.push(intAttr("agent_inspect.attempt_number", attemptNumber));
8022
+ const deliveryBase = meta?.[DELIVERY_EVENT_BASE_ATTR];
8023
+ if (typeof deliveryBase === "string" && deliveryBase.trim() !== "") {
8024
+ attrs2.push(stringAttr2(DELIVERY_EVENT_BASE_ATTR, deliveryBase.trim().slice(0, maxLen)));
7706
8025
  }
8026
+ return conflicts;
7707
8027
  }
7708
8028
  function resolveSpanTimes(ev) {
7709
8029
  const startedAtMs = typeof ev.attributes?.startedAtMs === "number" && Number.isFinite(ev.attributes.startedAtMs) ? ev.attributes.startedAtMs : void 0;
@@ -7726,7 +8046,8 @@ function exportOtlpJson(tree, options) {
7726
8046
  "OTLP JSON export uses OTel GenAI-aligned attributes where applicable; experimental until verified against specific collectors.",
7727
8047
  "Not OTLP gRPC/protobuf \u2014 JSON mapping only. Generated locally; no network upload.",
7728
8048
  "Status and span kind use OTLP numeric enums (StatusCode 0/1/2, SpanKind INTERNAL=1).",
7729
- "When present on source events, operationId/attemptId/retryOf/attemptNumber are emitted as bounded agent_inspect.* identity attributes (not a broad attribute dump)."
8049
+ "When present on source events, operationId/attemptId/retryOf/attemptNumber are emitted as bounded agent_inspect.* identity attributes (not a broad attribute dump).",
8050
+ "When direct and nested identity disagree, export prefers direct for non-strict consumers and emits agent_inspect.identity_conflict.* for the nested alternate; this does not certify conflicting identity."
7730
8051
  ];
7731
8052
  const traceId = hexFrom2(`trace:${tree.runId}`, 16);
7732
8053
  const includeAttributes = options?.includeAttributes ?? false;
@@ -7774,11 +8095,16 @@ function exportOtlpJson(tree, options) {
7774
8095
  if (typeof inp === "number") attrs2.push(intAttr("gen_ai.usage.input_tokens", inp));
7775
8096
  if (typeof outp === "number") attrs2.push(intAttr("gen_ai.usage.output_tokens", outp));
7776
8097
  }
7777
- appendStructuralIdentityAttrs(
8098
+ const identityConflicts = appendStructuralIdentityAttrs(
7778
8099
  attrs2,
7779
8100
  meta && typeof meta === "object" ? meta : void 0,
7780
8101
  maxLen
7781
8102
  );
8103
+ if (identityConflicts.length > 0) {
8104
+ warnings.push(
8105
+ `Identity representation conflict on event ${ev.eventId}: ${identityConflicts.join(", ")} (direct preferred; nested retained on agent_inspect.identity_conflict.*).`
8106
+ );
8107
+ }
7782
8108
  if (includeAttributes && meta && typeof meta === "object") {
7783
8109
  const skipKeys = /* @__PURE__ */ new Set([
7784
8110
  "tokens",