agent-inspect 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.
Files changed (48) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/package.json +1 -1
  4. package/packages/cli/dist/{chunk-N5JN26MW.mjs → chunk-P27XTK5U.mjs} +347 -36
  5. package/packages/cli/dist/chunk-P27XTK5U.mjs.map +1 -0
  6. package/packages/cli/dist/index.cjs +434 -63
  7. package/packages/cli/dist/index.cjs.map +1 -1
  8. package/packages/cli/dist/index.mjs +81 -33
  9. package/packages/cli/dist/index.mjs.map +1 -1
  10. package/packages/cli/dist/{src-VMGRSTG7.mjs → src-EWZYLDST.mjs} +3 -3
  11. package/packages/cli/dist/{src-VMGRSTG7.mjs.map → src-EWZYLDST.mjs.map} +1 -1
  12. package/packages/core/dist/advanced.cjs +280 -10
  13. package/packages/core/dist/advanced.cjs.map +1 -1
  14. package/packages/core/dist/advanced.mjs +6 -5
  15. package/packages/core/dist/advanced.mjs.map +1 -1
  16. package/packages/core/dist/checks.cjs +185 -27
  17. package/packages/core/dist/checks.cjs.map +1 -1
  18. package/packages/core/dist/checks.mjs +2 -1
  19. package/packages/core/dist/{chunk-JE3TUFAQ.mjs → chunk-77PUPLPG.mjs} +14 -8
  20. package/packages/core/dist/chunk-77PUPLPG.mjs.map +1 -0
  21. package/packages/core/dist/chunk-AN5MOADW.mjs +204 -0
  22. package/packages/core/dist/chunk-AN5MOADW.mjs.map +1 -0
  23. package/packages/core/dist/{chunk-4CDX5GDS.mjs → chunk-IMCM42W6.mjs} +110 -99
  24. package/packages/core/dist/chunk-IMCM42W6.mjs.map +1 -0
  25. package/packages/core/dist/{chunk-YU6XT5A2.mjs → chunk-J2ST4VCX.mjs} +45 -3
  26. package/packages/core/dist/chunk-J2ST4VCX.mjs.map +1 -0
  27. package/packages/core/dist/{chunk-EIP5CNEL.mjs → chunk-JJSL346L.mjs} +58 -3
  28. package/packages/core/dist/chunk-JJSL346L.mjs.map +1 -0
  29. package/packages/core/dist/exporters.cjs +91 -29
  30. package/packages/core/dist/exporters.cjs.map +1 -1
  31. package/packages/core/dist/exporters.mjs +78 -29
  32. package/packages/core/dist/exporters.mjs.map +1 -1
  33. package/packages/core/dist/logs.cjs +206 -1
  34. package/packages/core/dist/logs.cjs.map +1 -1
  35. package/packages/core/dist/logs.mjs +3 -2
  36. package/packages/core/dist/logs.mjs.map +1 -1
  37. package/packages/core/dist/persisted.cjs +226 -8
  38. package/packages/core/dist/persisted.cjs.map +1 -1
  39. package/packages/core/dist/persisted.mjs +9 -5
  40. package/packages/core/dist/persisted.mjs.map +1 -1
  41. package/packages/core/dist/readers.cjs +276 -6
  42. package/packages/core/dist/readers.cjs.map +1 -1
  43. package/packages/core/dist/readers.mjs +4 -3
  44. package/packages/cli/dist/chunk-N5JN26MW.mjs.map +0 -1
  45. package/packages/core/dist/chunk-4CDX5GDS.mjs.map +0 -1
  46. package/packages/core/dist/chunk-EIP5CNEL.mjs.map +0 -1
  47. package/packages/core/dist/chunk-JE3TUFAQ.mjs.map +0 -1
  48. package/packages/core/dist/chunk-YU6XT5A2.mjs.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.31.23
4
+
5
+ ### Patch Changes
6
+
7
+ - 297b1f1: Compare full retry identity before OTLP length bounding and stamp converter delivery_event_base provenance so long collisions and native manual:/inspect:-shaped IDs cannot silently pass strict retry gates (Prompt A residuals / #484).
8
+
9
+ ## 6.31.22
10
+
11
+ ### Patch Changes
12
+
13
+ - 3f02b61: Treat native eventIds as opaque for retry redelivery fingerprints, retain conflicting same-ID evidence through tree/export, and surface direct vs nested identity disagreements on OTLP round-trip so source and OTLP strict retry gates agree (Prompt A / #484).
14
+
3
15
  ## 6.31.21
4
16
 
5
17
  ### Patch Changes
package/README.md CHANGED
@@ -213,7 +213,7 @@ The root package is enough for custom capture, the CLI, checks, and Evidence wor
213
213
 
214
214
  ## Status and documentation
215
215
 
216
- **Current published baseline:** **6.31.21** · persisted schema `1.0` · Node.js `>=20` · MIT.
216
+ **Current published baseline:** **6.31.23** · persisted schema `1.0` · Node.js `>=20` · MIT.
217
217
 
218
218
  Legacy v0.1 and v0.2 traces remain readable. Check the npm badge and [changelog](CHANGELOG.md) for the current published version.
219
219
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-inspect",
3
- "version": "6.31.21",
3
+ "version": "6.31.23",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Local evidence debugger and trajectory-test toolkit for TypeScript AI agents — execution trees, TraceContract checks, Evidence v2, and read-only MCP",
@@ -5685,6 +5685,137 @@ function evaluateToolArgumentValue(value, check, evidencePresent) {
5685
5685
  };
5686
5686
  }
5687
5687
 
5688
+ // packages/core/src/persisted/delivery-identity.ts
5689
+ var DELIVERY_CONFLICT_ATTR = "agent_inspect.delivery_conflict";
5690
+ var DELIVERY_EVENT_BASE_ATTR = "agent_inspect.delivery_event_base";
5691
+ var IDENTITY_CONFLICT_ATTR = {
5692
+ operationId: "agent_inspect.identity_conflict.operation_id",
5693
+ attemptId: "agent_inspect.identity_conflict.attempt_id",
5694
+ retryOf: "agent_inspect.identity_conflict.retry_of",
5695
+ attemptNumber: "agent_inspect.identity_conflict.attempt_number",
5696
+ /** Comma-separated field names when a conflict survives lossy bounding. */
5697
+ fields: "agent_inspect.identity_conflict.fields"
5698
+ };
5699
+ var MAX_CONFLICT_FIELDS = 8;
5700
+ var MAX_DELIVERY_BASE_LEN = 500;
5701
+ function stripReaderDeliveryDiscriminator(eventId, attributes) {
5702
+ if (!eventId) return eventId;
5703
+ const rawBase = attributes?.[DELIVERY_EVENT_BASE_ATTR];
5704
+ if (typeof rawBase !== "string" || rawBase.trim() === "") return eventId;
5705
+ const base = rawBase.trim().slice(0, MAX_DELIVERY_BASE_LEN);
5706
+ if (eventId === base) return base;
5707
+ const prefix = `${base}:`;
5708
+ if (!eventId.startsWith(prefix)) return eventId;
5709
+ const suffix = eventId.slice(prefix.length);
5710
+ if (/^\d+$/.test(suffix)) return base;
5711
+ return eventId;
5712
+ }
5713
+ function deliveryEventBaseFromIndexedId(eventId) {
5714
+ if (!/:\d+$/.test(eventId)) return void 0;
5715
+ const base = eventId.replace(/:\d+$/, "");
5716
+ return base.length > 0 ? base.slice(0, MAX_DELIVERY_BASE_LEN) : void 0;
5717
+ }
5718
+ function canonicalDigest(value) {
5719
+ if (value === void 0) return null;
5720
+ const encoded = encodeCanonicalStructured(value);
5721
+ return encoded.ok ? encoded.value : `unencodable:${encoded.code}`;
5722
+ }
5723
+ function capturedContentDigest(attributes) {
5724
+ if (!attributes || typeof attributes !== "object") {
5725
+ return { args: null, result: null };
5726
+ }
5727
+ const nested = attributes.metadata && typeof attributes.metadata === "object" ? attributes.metadata : void 0;
5728
+ const args = attributes.arguments ?? attributes.args ?? attributes.input ?? nested?.arguments ?? nested?.args ?? nested?.input;
5729
+ const result = attributes.result ?? attributes.output ?? attributes.response ?? nested?.result ?? nested?.output ?? nested?.response;
5730
+ return {
5731
+ args: args === void 0 ? null : canonicalDigest(args),
5732
+ result: result === void 0 ? null : canonicalDigest(result)
5733
+ };
5734
+ }
5735
+ function timeKey(fields) {
5736
+ if (typeof fields.startedAt === "string" && fields.startedAt.trim() !== "") {
5737
+ return fields.startedAt;
5738
+ }
5739
+ if (typeof fields.timestamp === "string" && fields.timestamp.trim() !== "") {
5740
+ return fields.timestamp;
5741
+ }
5742
+ if (typeof fields.timestamp === "number" && Number.isFinite(fields.timestamp)) {
5743
+ return String(fields.timestamp);
5744
+ }
5745
+ return null;
5746
+ }
5747
+ function deliverySemanticFingerprint(fields) {
5748
+ const content = capturedContentDigest(fields.attributes);
5749
+ return JSON.stringify({
5750
+ stableEventId: stripReaderDeliveryDiscriminator(fields.eventId, fields.attributes),
5751
+ runId: fields.runId,
5752
+ kind: fields.kind,
5753
+ name: fields.name,
5754
+ status: fields.status ?? null,
5755
+ parentId: fields.parentId ?? null,
5756
+ sourceType: fields.sourceType ?? null,
5757
+ time: timeKey(fields),
5758
+ operationId: fields.operationId ?? null,
5759
+ attemptId: fields.attemptId ?? null,
5760
+ attemptNumber: fields.attemptNumber ?? null,
5761
+ retryOf: fields.retryOf ?? null,
5762
+ argsDigest: content.args,
5763
+ resultDigest: content.result
5764
+ });
5765
+ }
5766
+ function identityFieldsFromAttributes(attributes) {
5767
+ if (!attributes || typeof attributes !== "object") {
5768
+ return {
5769
+ operationId: null,
5770
+ attemptId: null,
5771
+ attemptNumber: null,
5772
+ retryOf: null
5773
+ };
5774
+ }
5775
+ const nested = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? attributes.metadata : void 0;
5776
+ const pickString3 = (key) => {
5777
+ const direct = attributes[key];
5778
+ if (typeof direct === "string" && direct.trim() !== "") return direct.trim();
5779
+ const nestedValue = nested?.[key];
5780
+ if (typeof nestedValue === "string" && nestedValue.trim() !== "") {
5781
+ return nestedValue.trim();
5782
+ }
5783
+ return null;
5784
+ };
5785
+ const pickNumber2 = () => {
5786
+ const direct = attributes.attemptNumber ?? attributes.attempt;
5787
+ if (typeof direct === "number" && Number.isFinite(direct)) return direct;
5788
+ const nestedValue = nested?.attemptNumber ?? nested?.attempt;
5789
+ if (typeof nestedValue === "number" && Number.isFinite(nestedValue)) {
5790
+ return nestedValue;
5791
+ }
5792
+ return null;
5793
+ };
5794
+ return {
5795
+ operationId: pickString3("operationId"),
5796
+ attemptId: pickString3("attemptId"),
5797
+ retryOf: pickString3("retryOf"),
5798
+ attemptNumber: pickNumber2()
5799
+ };
5800
+ }
5801
+ function identityConflictFieldsFromAttributes(attributes) {
5802
+ const raw = attributes?.[IDENTITY_CONFLICT_ATTR.fields];
5803
+ if (typeof raw !== "string" || raw.trim() === "") return [];
5804
+ return raw.split(",").map((part) => part.trim()).filter((part) => part.length > 0).slice(0, MAX_CONFLICT_FIELDS);
5805
+ }
5806
+ function hasDeliveryConflictMarker(attributes) {
5807
+ return attributes?.[DELIVERY_CONFLICT_ATTR] === true;
5808
+ }
5809
+ function stampDeliveryConflict(event) {
5810
+ return {
5811
+ ...event,
5812
+ attributes: {
5813
+ ...event.attributes ?? {},
5814
+ [DELIVERY_CONFLICT_ATTR]: true
5815
+ }
5816
+ };
5817
+ }
5818
+
5688
5819
  // packages/core/src/checks/retry-safety.ts
5689
5820
  function fail2(ruleId, message, evidence, expected, actual) {
5690
5821
  return {
@@ -5740,7 +5871,7 @@ function attemptIdOf(event) {
5740
5871
  }
5741
5872
  function countOperationAttempts(members) {
5742
5873
  const identified = /* @__PURE__ */ new Set();
5743
- let unidentified = 0;
5874
+ const unidentifiedFingerprints = /* @__PURE__ */ new Set();
5744
5875
  for (const event of members) {
5745
5876
  if (event.kind !== "TOOL" && event.kind !== "LLM") continue;
5746
5877
  const id = attemptIdOf(event);
@@ -5748,13 +5879,13 @@ function countOperationAttempts(members) {
5748
5879
  identified.add(id);
5749
5880
  continue;
5750
5881
  }
5751
- unidentified += 1;
5882
+ unidentifiedFingerprints.add(redeliveryFingerprint(event));
5752
5883
  }
5753
5884
  if (identified.size > 0) {
5754
- return identified.size + unidentified;
5885
+ return identified.size + unidentifiedFingerprints.size;
5755
5886
  }
5756
5887
  const numbered = /* @__PURE__ */ new Set();
5757
- let unnumbered = 0;
5888
+ const unnumberedFingerprints = /* @__PURE__ */ new Set();
5758
5889
  for (const event of members) {
5759
5890
  if (event.kind !== "TOOL" && event.kind !== "LLM") continue;
5760
5891
  const num = attemptNumberOf(event);
@@ -5762,12 +5893,12 @@ function countOperationAttempts(members) {
5762
5893
  numbered.add(num);
5763
5894
  continue;
5764
5895
  }
5765
- unnumbered += 1;
5896
+ unnumberedFingerprints.add(redeliveryFingerprint(event));
5766
5897
  }
5767
5898
  if (numbered.size > 0) {
5768
- return numbered.size + unnumbered;
5899
+ return numbered.size + unnumberedFingerprints.size;
5769
5900
  }
5770
- return unnumbered;
5901
+ return unnumberedFingerprints.size;
5771
5902
  }
5772
5903
  function classifyRetryAttempt(event, priorOrdered, allMembers) {
5773
5904
  const workflow = workflowFor(event);
@@ -5819,19 +5950,55 @@ function classifyRetryAttempt(event, priorOrdered, allMembers) {
5819
5950
  }
5820
5951
  function redeliveryFingerprint(event) {
5821
5952
  const workflow = workflowFor(event);
5822
- const stableEventId = (event.eventId ?? "").replace(/:\d+$/, "");
5823
- return JSON.stringify({
5824
- stableEventId,
5953
+ return deliverySemanticFingerprint({
5954
+ eventId: event.eventId ?? "",
5825
5955
  runId: event.runId,
5826
5956
  kind: event.kind,
5827
5957
  name: event.name,
5828
5958
  status: event.status,
5959
+ parentId: event.parentId,
5960
+ timestamp: event.timestamp,
5961
+ startedAt: event.startedAt,
5962
+ sourceType: event.source?.type,
5963
+ attributes: event.attributes && typeof event.attributes === "object" ? event.attributes : void 0,
5829
5964
  operationId: workflow.operationId ?? null,
5830
5965
  attemptId: workflow.attemptId ?? null,
5831
5966
  attemptNumber: workflow.attemptNumber ?? null,
5832
5967
  retryOf: workflow.retryOf ?? null
5833
5968
  });
5834
5969
  }
5970
+ var IDENTITY_KEYS = ["operationId", "attemptId", "retryOf", "attemptNumber"];
5971
+ function identityValuesEqual(a, b) {
5972
+ if (typeof a === "number" && typeof b === "number") {
5973
+ return Number.isFinite(a) && Number.isFinite(b) && a === b;
5974
+ }
5975
+ if (typeof a === "string" && typeof b === "string") {
5976
+ return a.trim() === b.trim();
5977
+ }
5978
+ return a === b;
5979
+ }
5980
+ function directNestedIdentityConflicts(event) {
5981
+ const attrs = event.attributes;
5982
+ if (!attrs || typeof attrs !== "object") return [];
5983
+ const nested = attrs.metadata && typeof attrs.metadata === "object" && !Array.isArray(attrs.metadata) ? attrs.metadata : void 0;
5984
+ const conflicts = [];
5985
+ if (nested) {
5986
+ for (const key of IDENTITY_KEYS) {
5987
+ const direct = attrs[key];
5988
+ const nestedValue = nested[key];
5989
+ if (direct === void 0 || nestedValue === void 0) continue;
5990
+ if (!identityValuesEqual(direct, nestedValue)) {
5991
+ conflicts.push(key);
5992
+ }
5993
+ }
5994
+ }
5995
+ for (const key of identityConflictFieldsFromAttributes(attrs)) {
5996
+ if (IDENTITY_KEYS.includes(key) && !conflicts.includes(key)) {
5997
+ conflicts.push(key);
5998
+ }
5999
+ }
6000
+ return conflicts;
6001
+ }
5835
6002
  function hasConflictingAttemptIdReuse(members) {
5836
6003
  const byAttempt = /* @__PURE__ */ new Map();
5837
6004
  for (const event of members) {
@@ -5847,6 +6014,31 @@ function hasConflictingAttemptIdReuse(members) {
5847
6014
  }
5848
6015
  return false;
5849
6016
  }
6017
+ function hasConflictingEventIdReuse(members) {
6018
+ const byEventId = /* @__PURE__ */ new Map();
6019
+ for (const event of members) {
6020
+ if (event.kind !== "TOOL" && event.kind !== "LLM") continue;
6021
+ const eventId = event.eventId;
6022
+ if (!eventId) continue;
6023
+ if (hasDeliveryConflictMarker(event.attributes)) {
6024
+ return true;
6025
+ }
6026
+ const fingerprints = byEventId.get(eventId) ?? /* @__PURE__ */ new Set();
6027
+ fingerprints.add(redeliveryFingerprint(event));
6028
+ byEventId.set(eventId, fingerprints);
6029
+ }
6030
+ for (const fingerprints of byEventId.values()) {
6031
+ if (fingerprints.size > 1) return true;
6032
+ }
6033
+ return false;
6034
+ }
6035
+ function hasIdentityRepresentationConflict(members) {
6036
+ for (const event of members) {
6037
+ if (event.kind !== "TOOL" && event.kind !== "LLM") continue;
6038
+ if (directNestedIdentityConflicts(event).length > 0) return true;
6039
+ }
6040
+ return false;
6041
+ }
5850
6042
  function evaluateRetrySafetyRules(events, rules, _runEvidence) {
5851
6043
  const findings = [];
5852
6044
  const byOperation = /* @__PURE__ */ new Map();
@@ -5854,13 +6046,14 @@ function evaluateRetrySafetyRules(events, rules, _runEvidence) {
5854
6046
  const workflow = workflowFor(event);
5855
6047
  const operationId = workflow.operationId;
5856
6048
  if (!operationId) continue;
5857
- const list = byOperation.get(operationId) ?? [];
5858
- list.push(event);
5859
- byOperation.set(operationId, list);
6049
+ const key = `${event.runId}\0${operationId}`;
6050
+ const group = byOperation.get(key) ?? { operationId, members: [] };
6051
+ group.members.push(event);
6052
+ byOperation.set(key, group);
5860
6053
  }
5861
6054
  if (rules.maxAttempts !== void 0) {
5862
- for (const [operationId, members] of byOperation) {
5863
- if (hasConflictingAttemptIdReuse(members)) {
6055
+ for (const { operationId, members } of byOperation.values()) {
6056
+ if (hasConflictingAttemptIdReuse(members) || hasConflictingEventIdReuse(members)) {
5864
6057
  findings.push(
5865
6058
  fail2(
5866
6059
  "contract.retry.duplicate-attempt-id",
@@ -5871,6 +6064,31 @@ function evaluateRetrySafetyRules(events, rules, _runEvidence) {
5871
6064
  )
5872
6065
  );
5873
6066
  }
6067
+ if (hasIdentityRepresentationConflict(members)) {
6068
+ findings.push(
6069
+ fail2(
6070
+ "contract.retry.ambiguous-identity",
6071
+ `Operation ${operationId} has conflicting direct vs nested identity fields; strict identity cannot pass.`,
6072
+ members.slice(0, 4).map(eventEvidence),
6073
+ "agreeing identity representation",
6074
+ "direct/nested conflict"
6075
+ )
6076
+ );
6077
+ }
6078
+ const toolish = members.filter((event) => event.kind === "TOOL" || event.kind === "LLM");
6079
+ const hasAttemptId = toolish.some((event) => attemptIdOf(event) !== void 0);
6080
+ const missingAttemptId = toolish.some((event) => attemptIdOf(event) === void 0);
6081
+ if (hasAttemptId && missingAttemptId) {
6082
+ findings.push(
6083
+ fail2(
6084
+ "contract.retry.ambiguous-identity",
6085
+ `Operation ${operationId} has partial attempt identity; strict identity conformance cannot pass.`,
6086
+ members.slice(0, 4).map(eventEvidence),
6087
+ "complete attempt identity",
6088
+ "partial attemptId coverage"
6089
+ )
6090
+ );
6091
+ }
5874
6092
  const count = countOperationAttempts(members);
5875
6093
  if (count > rules.maxAttempts) {
5876
6094
  findings.push(
@@ -5886,7 +6104,7 @@ function evaluateRetrySafetyRules(events, rules, _runEvidence) {
5886
6104
  }
5887
6105
  }
5888
6106
  if (rules.requireTerminalResult) {
5889
- for (const [operationId, members] of byOperation) {
6107
+ for (const { operationId, members } of byOperation.values()) {
5890
6108
  const hasTerminal = members.some((event) => event.status === "ok" || event.status === "error");
5891
6109
  if (!hasTerminal) {
5892
6110
  findings.push(
@@ -5906,7 +6124,7 @@ function evaluateRetrySafetyRules(events, rules, _runEvidence) {
5906
6124
  const workflow = workflowFor(event);
5907
6125
  const fallbackOf = workflow.fallbackOf;
5908
6126
  if (!fallbackOf) continue;
5909
- const prior = byOperation.get(fallbackOf) ?? [];
6127
+ const prior = byOperation.get(`${event.runId}\0${fallbackOf}`)?.members ?? [];
5910
6128
  const fallbackStart = eventTime(event);
5911
6129
  const earlierFailure = prior.some(
5912
6130
  (candidate) => candidate.status === "error" && eventTime(candidate) !== "" && fallbackStart !== "" && eventTime(candidate) < fallbackStart
@@ -5933,7 +6151,7 @@ function evaluateRetrySafetyRules(events, rules, _runEvidence) {
5933
6151
  for (const event of toolEvents) {
5934
6152
  const name = resolveCanonicalToolName(event);
5935
6153
  const workflow = workflowFor(event);
5936
- const key = `${workflow.operationId ?? name}::${name}`;
6154
+ const key = `${event.runId}::${workflow.operationId ?? name}::${name}`;
5937
6155
  const list = byToolOp.get(key) ?? [];
5938
6156
  list.push(event);
5939
6157
  byToolOp.set(key, list);
@@ -5991,7 +6209,7 @@ function evaluateRetrySafetyRules(events, rules, _runEvidence) {
5991
6209
  }
5992
6210
  }
5993
6211
  if (rules.requireRecoveredFailureVisible) {
5994
- for (const [operationId, members] of byOperation) {
6212
+ for (const { operationId, members } of byOperation.values()) {
5995
6213
  const ordered = sortByTime(members);
5996
6214
  const toolish = ordered.filter(
5997
6215
  (event) => (event.kind === "TOOL" || event.kind === "LLM") && (event.status === "ok" || event.status === "error")
@@ -6250,14 +6468,7 @@ function attemptIdOf2(event) {
6250
6468
  return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
6251
6469
  }
6252
6470
  function hasDuplicateAttemptIds(members) {
6253
- const seen = /* @__PURE__ */ new Set();
6254
- for (const event of members) {
6255
- const id = attemptIdOf2(event);
6256
- if (!id) continue;
6257
- if (seen.has(id)) return true;
6258
- seen.add(id);
6259
- }
6260
- return false;
6471
+ return hasConflictingAttemptIdReuse(members) || hasConflictingEventIdReuse(members);
6261
6472
  }
6262
6473
  function findRetryTarget(members, retryOf) {
6263
6474
  return members.find(
@@ -11912,6 +12123,11 @@ function compactAttributes2(entries) {
11912
12123
  }
11913
12124
  return Object.keys(out).length > 0 ? out : void 0;
11914
12125
  }
12126
+ function stampDeliveryEventBase(eventId, attributes) {
12127
+ const base = deliveryEventBaseFromIndexedId(eventId);
12128
+ if (!base) return attributes;
12129
+ return { ...attributes ?? {}, [DELIVERY_EVENT_BASE_ATTR]: base };
12130
+ }
11915
12131
  function traceEventToPersistedInspectEvent(event, options) {
11916
12132
  const eventIndex = options?.eventIndex ?? 0;
11917
12133
  const eventId = createPersistedEventId(event, eventIndex);
@@ -11941,7 +12157,7 @@ function traceEventToPersistedInspectEvent(event, options) {
11941
12157
  startedAt: tsStart.iso,
11942
12158
  confidence: "explicit",
11943
12159
  source,
11944
- attributes
12160
+ attributes: stampDeliveryEventBase(eventId, attributes)
11945
12161
  };
11946
12162
  }
11947
12163
  case "run_completed": {
@@ -11964,7 +12180,7 @@ function traceEventToPersistedInspectEvent(event, options) {
11964
12180
  durationMs: event.durationMs,
11965
12181
  confidence: "explicit",
11966
12182
  source,
11967
- attributes,
12183
+ attributes: stampDeliveryEventBase(eventId, attributes),
11968
12184
  error
11969
12185
  };
11970
12186
  }
@@ -11989,7 +12205,7 @@ function traceEventToPersistedInspectEvent(event, options) {
11989
12205
  startedAt: tsStart.iso,
11990
12206
  confidence: "explicit",
11991
12207
  source,
11992
- attributes
12208
+ attributes: stampDeliveryEventBase(eventId, attributes)
11993
12209
  };
11994
12210
  if (event.parentId !== void 0) {
11995
12211
  out.parentId = event.parentId;
@@ -12020,7 +12236,7 @@ function traceEventToPersistedInspectEvent(event, options) {
12020
12236
  durationMs: event.durationMs,
12021
12237
  confidence: "explicit",
12022
12238
  source,
12023
- attributes,
12239
+ attributes: stampDeliveryEventBase(eventId, attributes),
12024
12240
  error
12025
12241
  };
12026
12242
  }
@@ -12047,7 +12263,7 @@ function traceEventToPersistedInspectEvent(event, options) {
12047
12263
  timestamp: tsMain.iso,
12048
12264
  confidence: "explicit",
12049
12265
  source,
12050
- attributes
12266
+ attributes: stampDeliveryEventBase(eventId, attributes)
12051
12267
  };
12052
12268
  if (event.parentId !== void 0) {
12053
12269
  out.parentId = event.parentId;
@@ -12200,7 +12416,47 @@ var TreeBuilder = class {
12200
12416
  const sorted = [...runEvents].sort((a, b) => a.timestamp - b.timestamp);
12201
12417
  const nodes = /* @__PURE__ */ new Map();
12202
12418
  for (const e of sorted) {
12203
- nodes.set(e.eventId, { event: e, children: [], depth: 0 });
12419
+ const existing = nodes.get(e.eventId);
12420
+ if (!existing) {
12421
+ nodes.set(e.eventId, { event: e, children: [], depth: 0 });
12422
+ continue;
12423
+ }
12424
+ const existingAttrs = existing.event.attributes && typeof existing.event.attributes === "object" ? existing.event.attributes : void 0;
12425
+ const nextAttrs = e.attributes && typeof e.attributes === "object" ? e.attributes : void 0;
12426
+ const existingIdentity = identityFieldsFromAttributes(existingAttrs);
12427
+ const nextIdentity = identityFieldsFromAttributes(nextAttrs);
12428
+ const existingFp = deliverySemanticFingerprint({
12429
+ eventId: existing.event.eventId,
12430
+ runId: existing.event.runId,
12431
+ kind: existing.event.kind,
12432
+ name: existing.event.name,
12433
+ status: existing.event.status,
12434
+ parentId: existing.event.parentId,
12435
+ timestamp: existing.event.timestamp,
12436
+ sourceType: existing.event.source?.type,
12437
+ attributes: existingAttrs,
12438
+ ...existingIdentity
12439
+ });
12440
+ const nextFp = deliverySemanticFingerprint({
12441
+ eventId: e.eventId,
12442
+ runId: e.runId,
12443
+ kind: e.kind,
12444
+ name: e.name,
12445
+ status: e.status,
12446
+ parentId: e.parentId,
12447
+ timestamp: e.timestamp,
12448
+ sourceType: e.source?.type,
12449
+ attributes: nextAttrs,
12450
+ ...nextIdentity
12451
+ });
12452
+ if (existingFp === nextFp) {
12453
+ continue;
12454
+ }
12455
+ nodes.set(e.eventId, {
12456
+ event: stampDeliveryConflict(e),
12457
+ children: [],
12458
+ depth: 0
12459
+ });
12204
12460
  }
12205
12461
  const { roots, summary } = linkNodesVisibilityFirst(nodes);
12206
12462
  const confidenceBreakdown = {
@@ -13665,6 +13921,12 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
13665
13921
  ["agent_inspect.retry_of", "retryOf", "string"],
13666
13922
  ["agent_inspect.attempt_number", "attemptNumber", "int"]
13667
13923
  ];
13924
+ const conflictMapping = [
13925
+ ["agent_inspect.identity_conflict.operation_id", "operationId", "string"],
13926
+ ["agent_inspect.identity_conflict.attempt_id", "attemptId", "string"],
13927
+ ["agent_inspect.identity_conflict.retry_of", "retryOf", "string"],
13928
+ ["agent_inspect.identity_conflict.attempt_number", "attemptNumber", "int"]
13929
+ ];
13668
13930
  const metadata = attributes.metadata && typeof attributes.metadata === "object" && !Array.isArray(attributes.metadata) ? { ...attributes.metadata } : {};
13669
13931
  for (const [dotted, camel, kind] of mapping) {
13670
13932
  const raw = attributes[dotted];
@@ -13716,6 +13978,55 @@ function promoteAgentInspectWorkflowIdentity(attributes, pathPrefix) {
13716
13978
  if (direct === void 0) attributes[camel] = promoted;
13717
13979
  if (nested === void 0) metadata[camel] = promoted;
13718
13980
  }
13981
+ for (const [dotted, camel, kind] of conflictMapping) {
13982
+ const raw = attributes[dotted];
13983
+ if (raw === void 0 || raw === null) continue;
13984
+ let alternate;
13985
+ if (kind === "int") {
13986
+ const n = typeof raw === "number" ? raw : typeof raw === "string" ? Number(raw) : NaN;
13987
+ if (!Number.isFinite(n) || n < 1) {
13988
+ warnings.push({
13989
+ code: "otlp_identity_invalid",
13990
+ message: `Ignoring invalid ${dotted} identity conflict value.`,
13991
+ severity: "warning",
13992
+ field: `${pathPrefix}.${dotted}`
13993
+ });
13994
+ continue;
13995
+ }
13996
+ alternate = Math.trunc(n);
13997
+ } else if (typeof raw === "string" && raw.trim() !== "") {
13998
+ alternate = raw.trim();
13999
+ } else {
14000
+ warnings.push({
14001
+ code: "otlp_identity_invalid",
14002
+ message: `Ignoring non-string ${dotted} identity conflict value.`,
14003
+ severity: "warning",
14004
+ field: `${pathPrefix}.${dotted}`
14005
+ });
14006
+ continue;
14007
+ }
14008
+ const preferred = attributes[camel];
14009
+ if (preferred !== void 0 && preferred !== alternate) {
14010
+ metadata[camel] = alternate;
14011
+ warnings.push({
14012
+ code: "otlp_identity_conflict",
14013
+ message: `Conflicting ${camel}: attributes.${camel} vs nested alternate from ${dotted}; retaining both.`,
14014
+ severity: "warning",
14015
+ field: `${pathPrefix}.${dotted}`
14016
+ });
14017
+ } else if (preferred === void 0) {
14018
+ metadata[camel] = alternate;
14019
+ }
14020
+ }
14021
+ const conflictFields = attributes["agent_inspect.identity_conflict.fields"];
14022
+ if (typeof conflictFields === "string" && conflictFields.trim() !== "") {
14023
+ warnings.push({
14024
+ code: "otlp_identity_conflict",
14025
+ message: `Identity representation conflict fields retained after OTLP bounding: ${conflictFields.trim().slice(0, 200)}.`,
14026
+ severity: "warning",
14027
+ field: `${pathPrefix}.agent_inspect.identity_conflict.fields`
14028
+ });
14029
+ }
13719
14030
  if (Object.keys(metadata).length > 0) {
13720
14031
  attributes.metadata = metadata;
13721
14032
  }
@@ -15696,6 +16007,6 @@ function renderGateReport(result, options = {}) {
15696
16007
  return renderGateSummaryMarkdown(result);
15697
16008
  }
15698
16009
 
15699
- export { ATTRIBUTION_CONFIDENCES, COHORT_METRIC_IDS, DEFAULT_REDACT_KEYS, DEFAULT_SUITE_ARTIFACTS_DIR, EVIDENCE_FORMAT_VERSION, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, Redactor, TraceDirectory, TraceReadError, TreeBuilder, aggregateBundleSafeStatus, aggregateSessionCheckResults, analyzeCohort, applyProfileMetadataCaps, assertBundlePathContained, assertEvidenceRelativePath, buildActivitySummary, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceCausalFailureViewHtml, buildEvidenceCiPackage, buildEvidenceCircuitViewHtml, buildEvidenceContractsViewHtml, buildEvidenceDiffViewHtml, buildEvidenceHtmlShell, buildEvidenceManifest, buildEvidenceOutcomesViewHtml, buildEvidenceProvenanceViewHtml, buildEvidenceSafetyViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceTreeViewHtml, buildLocalExplanation, buildPlaceholderArtifact, buildRunSummary, buildRunTimeline, buildRunWhatSummary, buildSessionIndex, buildTraceStats, buildZipArchive, bundleFailsOnSafety, bundleRunAssetRelativePath, collectTraceSchemaVersions, compactAttributes, createBaselineRegressionRule, createLlmUsageRule, createMaxStepDurationRule, createObservedOutcomeRule, createRequireCompletedRule, createRunDepthRule, createRunDurationRule, createRunStatusRule, createSafetyOversizedAttributeRule, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createStallDetectionRule, createStructureCycleRule, createStructureOrphanRule, createStructureParallelWidthRule, createStructureRelationshipRule, createToolUsageRule, defaultBundleOutputPath, defaultSuiteConfigTemplate, defineTraceContract, diffRuns, diffTraceEvents, enrichSessionRunRecord, escapeHtml, escapeMarkdown, evaluateTraceContract, extractMetadata, extractOutcomesFromTraceEvents, filterMetasBySessionScope, filterTraces, flattenTree, formatDuration2 as formatDuration, formatStepLabel, formatTimestamp, gateHasThresholds, getIndent, getTraceFilePath, inferEvidenceFileRole, isAgentInspectTrace, isAttributionConfidence, isCredentialSensitiveKey, isPersistedInspectEvent, loadSessionRunRecords, loadSuiteConfig, loadTraceMetadataList, manualTraceEventsToComparableRun, nanoid, normalizeBundleOutputPath, openTrace, parseCohortMetricList, parseDuration, parseDurationFilter, parseGateList, parseTraceJsonl, persistedInspectEventsToRunTrees, persistedInspectEventsToTraceEvents, projectLogicalEvents, renderActivitySummaryHuman, renderCohortReport, renderErrorLine, renderGateReport, renderObservedOutcomesHtml, renderObservedOutcomesMarkdown, renderRunDiff, renderRunWhat, renderStepLine, renderSuiteReport, renderTimeline, renderTraceStats, resolveBundleRunIds, resolveRedactionProfile, resolveSuiteTemplate, resolveTraceDir, runGate, runSuite, runTraceChecks, safeString, sanitizeBundleRunId, searchTraces, serializeEvidenceManifest, sha256Hex, stableJson, stringContainsHighConfidenceCredential, summarizeObservedOutcomes, summarizeSemanticParity, traceEventToPersistedInspectEvent, truncateName, truncateStringForProfile, validateEvent, validateSuiteConfig, verifyEvidenceDirectory, zeroKinds };
15700
- //# sourceMappingURL=chunk-N5JN26MW.mjs.map
15701
- //# sourceMappingURL=chunk-N5JN26MW.mjs.map
16010
+ export { ATTRIBUTION_CONFIDENCES, COHORT_METRIC_IDS, DEFAULT_REDACT_KEYS, DEFAULT_SUITE_ARTIFACTS_DIR, DELIVERY_CONFLICT_ATTR, DELIVERY_EVENT_BASE_ATTR, EVIDENCE_FORMAT_VERSION, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, IDENTITY_CONFLICT_ATTR, Redactor, TraceDirectory, TraceReadError, TreeBuilder, aggregateBundleSafeStatus, aggregateSessionCheckResults, analyzeCohort, applyProfileMetadataCaps, assertBundlePathContained, assertEvidenceRelativePath, buildActivitySummary, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceCausalFailureViewHtml, buildEvidenceCiPackage, buildEvidenceCircuitViewHtml, buildEvidenceContractsViewHtml, buildEvidenceDiffViewHtml, buildEvidenceHtmlShell, buildEvidenceManifest, buildEvidenceOutcomesViewHtml, buildEvidenceProvenanceViewHtml, buildEvidenceSafetyViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceTreeViewHtml, buildLocalExplanation, buildPlaceholderArtifact, buildRunSummary, buildRunTimeline, buildRunWhatSummary, buildSessionIndex, buildTraceStats, buildZipArchive, bundleFailsOnSafety, bundleRunAssetRelativePath, collectTraceSchemaVersions, compactAttributes, createBaselineRegressionRule, createLlmUsageRule, createMaxStepDurationRule, createObservedOutcomeRule, createRequireCompletedRule, createRunDepthRule, createRunDurationRule, createRunStatusRule, createSafetyOversizedAttributeRule, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createStallDetectionRule, createStructureCycleRule, createStructureOrphanRule, createStructureParallelWidthRule, createStructureRelationshipRule, createToolUsageRule, defaultBundleOutputPath, defaultSuiteConfigTemplate, defineTraceContract, diffRuns, diffTraceEvents, enrichSessionRunRecord, escapeHtml, escapeMarkdown, evaluateTraceContract, extractMetadata, extractOutcomesFromTraceEvents, filterMetasBySessionScope, filterTraces, flattenTree, formatDuration2 as formatDuration, formatStepLabel, formatTimestamp, gateHasThresholds, getIndent, getTraceFilePath, inferEvidenceFileRole, isAgentInspectTrace, isAttributionConfidence, isCredentialSensitiveKey, isPersistedInspectEvent, loadSessionRunRecords, loadSuiteConfig, loadTraceMetadataList, manualTraceEventsToComparableRun, nanoid, normalizeBundleOutputPath, openTrace, parseCohortMetricList, parseDuration, parseDurationFilter, parseGateList, parseTraceJsonl, persistedInspectEventsToRunTrees, persistedInspectEventsToTraceEvents, projectLogicalEvents, renderActivitySummaryHuman, renderCohortReport, renderErrorLine, renderGateReport, renderObservedOutcomesHtml, renderObservedOutcomesMarkdown, renderRunDiff, renderRunWhat, renderStepLine, renderSuiteReport, renderTimeline, renderTraceStats, resolveBundleRunIds, resolveRedactionProfile, resolveSuiteTemplate, resolveTraceDir, runGate, runSuite, runTraceChecks, safeString, sanitizeBundleRunId, searchTraces, serializeEvidenceManifest, sha256Hex, stableJson, stringContainsHighConfidenceCredential, summarizeObservedOutcomes, summarizeSemanticParity, traceEventToPersistedInspectEvent, truncateName, truncateStringForProfile, validateEvent, validateSuiteConfig, verifyEvidenceDirectory, zeroKinds };
16011
+ //# sourceMappingURL=chunk-P27XTK5U.mjs.map
16012
+ //# sourceMappingURL=chunk-P27XTK5U.mjs.map