@agent-inspect/mcp-server 6.12.1 → 6.13.0

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
@@ -679,12 +679,12 @@ function persistedInspectEventToTraceEvents(event) {
679
679
  if (!isPersistedInspectEvent(event)) {
680
680
  throw new Error("Invalid PersistedInspectEvent: failed isPersistedInspectEvent");
681
681
  }
682
- const legacyEvent = event.attributes?.legacyEvent;
683
- if (legacyEvent === "run_started") return [fromLegacyRunStarted(event)];
684
- if (legacyEvent === "run_completed") return [fromLegacyRunCompleted(event)];
685
- if (legacyEvent === "step_started") return [fromLegacyStepStarted(event)];
686
- if (legacyEvent === "step_completed") return [fromLegacyStepCompleted(event)];
687
- if (legacyEvent === "outcome_observed") return [fromLegacyOutcomeObserved(event)];
682
+ const legacyEvent2 = event.attributes?.legacyEvent;
683
+ if (legacyEvent2 === "run_started") return [fromLegacyRunStarted(event)];
684
+ if (legacyEvent2 === "run_completed") return [fromLegacyRunCompleted(event)];
685
+ if (legacyEvent2 === "step_started") return [fromLegacyStepStarted(event)];
686
+ if (legacyEvent2 === "step_completed") return [fromLegacyStepCompleted(event)];
687
+ if (legacyEvent2 === "outcome_observed") return [fromLegacyOutcomeObserved(event)];
688
688
  if (event.kind === "RUN") {
689
689
  return fromNativeRun(event);
690
690
  }
@@ -2577,6 +2577,289 @@ function diffRuns(left, right, options) {
2577
2577
  return table;
2578
2578
  })();
2579
2579
 
2580
+ // packages/core/src/checks/logical-events.ts
2581
+ function isRecord6(value) {
2582
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2583
+ }
2584
+ function legacyEvent(event) {
2585
+ const value = event.attributes?.legacyEvent;
2586
+ return typeof value === "string" ? value : void 0;
2587
+ }
2588
+ function stepIdOf(event) {
2589
+ const value = event.attributes?.stepId;
2590
+ if (typeof value === "string" && value.trim() !== "") return value;
2591
+ return void 0;
2592
+ }
2593
+ function cloneEvent(event) {
2594
+ return {
2595
+ ...event,
2596
+ ...event.attributes !== void 0 ? { attributes: { ...event.attributes } } : {},
2597
+ ...event.error !== void 0 ? { error: { ...event.error } } : {},
2598
+ ...event.tokenUsage !== void 0 ? { tokenUsage: { ...event.tokenUsage } } : {},
2599
+ ...event.source !== void 0 ? { source: { ...event.source } } : {}
2600
+ };
2601
+ }
2602
+ function mergeAttributes(start, complete) {
2603
+ const merged = {
2604
+ ...isRecord6(complete.attributes) ? complete.attributes : {},
2605
+ ...isRecord6(start.attributes) ? start.attributes : {}
2606
+ };
2607
+ merged.legacyEvent = start.attributes?.legacyEvent ?? complete.attributes?.legacyEvent;
2608
+ merged.legacyCompleteEvent = complete.attributes?.legacyEvent;
2609
+ if (complete.attributes?.errorStack !== void 0) {
2610
+ merged.errorStack = complete.attributes.errorStack;
2611
+ }
2612
+ return Object.keys(merged).length > 0 ? merged : void 0;
2613
+ }
2614
+ function pairStartComplete(start, complete) {
2615
+ const attributes = mergeAttributes(start, complete);
2616
+ const paired = {
2617
+ ...cloneEvent(start),
2618
+ status: complete.status,
2619
+ timestamp: complete.timestamp ?? start.timestamp,
2620
+ ...complete.endedAt !== void 0 ? { endedAt: complete.endedAt } : {},
2621
+ ...complete.durationMs !== void 0 ? { durationMs: complete.durationMs } : {},
2622
+ ...complete.error !== void 0 ? { error: { ...complete.error } } : {},
2623
+ ...complete.tokenUsage !== void 0 && start.tokenUsage === void 0 ? { tokenUsage: { ...complete.tokenUsage } } : {},
2624
+ ...attributes !== void 0 ? { attributes } : {}
2625
+ };
2626
+ return {
2627
+ ...paired,
2628
+ sourceEventIds: Object.freeze([start.eventId, complete.eventId]),
2629
+ projection: {
2630
+ paired: true,
2631
+ absorbedEventIds: Object.freeze([complete.eventId]),
2632
+ parentNormalized: false,
2633
+ ...start.parentId !== void 0 ? { originalParentId: start.parentId } : {}
2634
+ }
2635
+ };
2636
+ }
2637
+ function asLogical(event, extras) {
2638
+ return {
2639
+ ...cloneEvent(event),
2640
+ sourceEventIds: Object.freeze([event.eventId]),
2641
+ projection: {
2642
+ paired: false,
2643
+ absorbedEventIds: Object.freeze([]),
2644
+ parentNormalized: extras?.parentNormalized === true,
2645
+ ...{}
2646
+ }
2647
+ };
2648
+ }
2649
+ function projectLogicalEvents(events) {
2650
+ const diagnostics = [];
2651
+ const byRun = /* @__PURE__ */ new Map();
2652
+ for (const event of events) {
2653
+ const list = byRun.get(event.runId) ?? [];
2654
+ list.push(event);
2655
+ byRun.set(event.runId, list);
2656
+ }
2657
+ const absorbedIds = /* @__PURE__ */ new Set();
2658
+ const logicalByRawId = /* @__PURE__ */ new Map();
2659
+ const stepIdToLogicalId = /* @__PURE__ */ new Map();
2660
+ const logical = [];
2661
+ const orderedRuns = [...byRun.keys()].sort((a, b) => a.localeCompare(b));
2662
+ for (const runId of orderedRuns) {
2663
+ const runEvents = byRun.get(runId) ?? [];
2664
+ const starts = [];
2665
+ const completes = [];
2666
+ const others = [];
2667
+ for (const event of runEvents) {
2668
+ const legacy = legacyEvent(event);
2669
+ if (legacy === "step_started" || legacy === "run_started" && event.status === "running") {
2670
+ starts.push(event);
2671
+ } else if (legacy === "step_completed" || legacy === "run_completed") {
2672
+ completes.push(event);
2673
+ } else {
2674
+ others.push(event);
2675
+ }
2676
+ }
2677
+ const usedCompletes = /* @__PURE__ */ new Set();
2678
+ for (const start of starts) {
2679
+ const stepId = stepIdOf(start);
2680
+ let match;
2681
+ if (legacyEvent(start) === "run_started") {
2682
+ const candidates = completes.filter(
2683
+ (c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "run_completed"
2684
+ );
2685
+ if (candidates.length > 1) {
2686
+ diagnostics.push({
2687
+ code: "AI_LOGICAL_PAIR_AMBIGUOUS",
2688
+ message: `Multiple run_completed rows for run ${runId}; using first by eventId.`,
2689
+ eventIds: candidates.map((c) => c.eventId)
2690
+ });
2691
+ candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
2692
+ }
2693
+ match = candidates[0];
2694
+ } else if (stepId) {
2695
+ const candidates = completes.filter(
2696
+ (c) => !usedCompletes.has(c.eventId) && legacyEvent(c) === "step_completed" && stepIdOf(c) === stepId
2697
+ );
2698
+ if (candidates.length > 1) {
2699
+ diagnostics.push({
2700
+ code: "AI_LOGICAL_PAIR_AMBIGUOUS",
2701
+ message: `Multiple step_completed rows for stepId ${stepId}; using first by eventId.`,
2702
+ eventIds: candidates.map((c) => c.eventId)
2703
+ });
2704
+ candidates.sort((a, b) => a.eventId.localeCompare(b.eventId));
2705
+ }
2706
+ match = candidates[0];
2707
+ }
2708
+ if (match) {
2709
+ usedCompletes.add(match.eventId);
2710
+ absorbedIds.add(match.eventId);
2711
+ const paired = pairStartComplete(start, match);
2712
+ logical.push(paired);
2713
+ logicalByRawId.set(start.eventId, paired);
2714
+ logicalByRawId.set(match.eventId, paired);
2715
+ if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, paired.eventId);
2716
+ } else {
2717
+ diagnostics.push({
2718
+ code: "AI_LOGICAL_PAIR_UNMATCHED_START",
2719
+ message: `No matching complete for start ${start.eventId}.`,
2720
+ eventIds: [start.eventId]
2721
+ });
2722
+ const alone = asLogical(start);
2723
+ logical.push(alone);
2724
+ logicalByRawId.set(start.eventId, alone);
2725
+ if (stepId) stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
2726
+ }
2727
+ }
2728
+ for (const complete of completes) {
2729
+ if (usedCompletes.has(complete.eventId)) continue;
2730
+ diagnostics.push({
2731
+ code: "AI_LOGICAL_PAIR_UNMATCHED_COMPLETE",
2732
+ message: `No matching start for complete ${complete.eventId}.`,
2733
+ eventIds: [complete.eventId]
2734
+ });
2735
+ const alone = asLogical(complete);
2736
+ logical.push(alone);
2737
+ logicalByRawId.set(complete.eventId, alone);
2738
+ const stepId = stepIdOf(complete);
2739
+ if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
2740
+ stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
2741
+ }
2742
+ }
2743
+ for (const event of others) {
2744
+ const alone = asLogical(event);
2745
+ logical.push(alone);
2746
+ logicalByRawId.set(event.eventId, alone);
2747
+ const stepId = stepIdOf(event);
2748
+ if (stepId && !stepIdToLogicalId.has(`${runId}:${stepId}`)) {
2749
+ stepIdToLogicalId.set(`${runId}:${stepId}`, alone.eventId);
2750
+ }
2751
+ }
2752
+ }
2753
+ const logicalById = new Map(logical.map((e) => [e.eventId, e]));
2754
+ const normalized = [];
2755
+ for (const event of logical) {
2756
+ const originalParentId = event.parentId;
2757
+ if (!originalParentId) {
2758
+ normalized.push(event);
2759
+ continue;
2760
+ }
2761
+ let nextParent = originalParentId;
2762
+ let remapped = false;
2763
+ const viaAbsorbed = logicalByRawId.get(originalParentId);
2764
+ if (viaAbsorbed && viaAbsorbed.eventId !== originalParentId) {
2765
+ nextParent = viaAbsorbed.eventId;
2766
+ remapped = true;
2767
+ } else if (!logicalById.has(originalParentId)) {
2768
+ const viaStep = stepIdToLogicalId.get(`${event.runId}:${originalParentId}`);
2769
+ if (viaStep) {
2770
+ nextParent = viaStep;
2771
+ remapped = true;
2772
+ }
2773
+ }
2774
+ if (!remapped) {
2775
+ if (!logicalById.has(originalParentId) && !logicalByRawId.has(originalParentId)) {
2776
+ const mapping = event.attributes?.parentMapping;
2777
+ const unresolved = mapping === "unresolved" || event.attributes?.parentUnresolved === true || event.attributes?.unresolvedParent === true || // LangGraph/framework scaffolding sentinel labels are not event ids.
2778
+ /^LangGraph$/i.test(originalParentId) || originalParentId.startsWith("unresolved:");
2779
+ if (!unresolved) {
2780
+ diagnostics.push({
2781
+ code: "AI_LOGICAL_PARENT_UNRESOLVED",
2782
+ message: `Parent ${originalParentId} unresolved for ${event.eventId}.`,
2783
+ eventIds: [event.eventId]
2784
+ });
2785
+ }
2786
+ }
2787
+ normalized.push(event);
2788
+ continue;
2789
+ }
2790
+ diagnostics.push({
2791
+ code: "AI_LOGICAL_PARENT_REMAPPED",
2792
+ message: `Remapped parent ${originalParentId} \u2192 ${nextParent} for ${event.eventId}.`,
2793
+ eventIds: [event.eventId]
2794
+ });
2795
+ normalized.push({
2796
+ ...event,
2797
+ parentId: nextParent,
2798
+ projection: {
2799
+ ...event.projection,
2800
+ parentNormalized: true,
2801
+ originalParentId
2802
+ }
2803
+ });
2804
+ }
2805
+ const rawIndex = new Map(events.map((e, i) => [e.eventId, i]));
2806
+ normalized.sort((a, b) => {
2807
+ const ai = rawIndex.get(a.sourceEventIds[0]) ?? 0;
2808
+ const bi = rawIndex.get(b.sourceEventIds[0]) ?? 0;
2809
+ return ai - bi || a.eventId.localeCompare(b.eventId);
2810
+ });
2811
+ return {
2812
+ logicalEvents: Object.freeze(normalized),
2813
+ diagnostics: Object.freeze(diagnostics)
2814
+ };
2815
+ }
2816
+ function resolveCanonicalToolName(event) {
2817
+ const attrs = event.attributes;
2818
+ const direct = pickString(attrs, ["toolName", "tool"]);
2819
+ if (direct) return direct;
2820
+ const metadata = attrs?.metadata;
2821
+ if (isRecord6(metadata)) {
2822
+ const nested = pickString(metadata, ["toolName", "tool"]);
2823
+ if (nested) return nested;
2824
+ }
2825
+ for (const prefix of ["tool:", "function:", "mcp-tools:"]) {
2826
+ if (event.name.startsWith(prefix)) return event.name.slice(prefix.length);
2827
+ }
2828
+ return event.name;
2829
+ }
2830
+ function pickString(record, keys) {
2831
+ if (!record) return void 0;
2832
+ for (const key of keys) {
2833
+ const value = record[key];
2834
+ if (typeof value === "string" && value.trim() !== "") return value.trim();
2835
+ }
2836
+ return void 0;
2837
+ }
2838
+
2839
+ // packages/core/src/checks/trace-facts.ts
2840
+ function summarizeSemanticParity(events) {
2841
+ const projection = projectLogicalEvents(events);
2842
+ const logical = projection.logicalEvents;
2843
+ const finishedTools = logical.filter(
2844
+ (event) => event.kind === "TOOL" && event.status !== "running"
2845
+ );
2846
+ const finishedToolNames = Object.freeze(
2847
+ finishedTools.map((event) => resolveCanonicalToolName(event)).sort((a, b) => a.localeCompare(b))
2848
+ );
2849
+ return {
2850
+ rawEventCount: events.length,
2851
+ logicalEventCount: logical.length,
2852
+ runningLogicalCount: logical.filter((event) => event.status === "running").length,
2853
+ finishedToolNames,
2854
+ finishedToolCount: finishedTools.length,
2855
+ pairedCount: logical.filter((event) => event.projection.paired).length,
2856
+ parentRemapCount: projection.diagnostics.filter(
2857
+ (item) => item.code === "AI_LOGICAL_PARENT_REMAPPED"
2858
+ ).length,
2859
+ diagnostics: projection.diagnostics
2860
+ };
2861
+ }
2862
+
2580
2863
  // packages/core/src/checks/index.ts
2581
2864
  var SEVERITY_RANK = {
2582
2865
  error: 0,
@@ -2629,7 +2912,11 @@ var DEFAULT_RAW_CONTENT_KEYS = [
2629
2912
  "conversationtext",
2630
2913
  "conversation_text"
2631
2914
  ];
2632
- var DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES = ["tokenUsage", "usage"];
2915
+ var DEFAULT_SAFE_RAW_CONTENT_PATH_PREFIXES = [
2916
+ "tokenUsage",
2917
+ "usage",
2918
+ "tokens"
2919
+ ];
2633
2920
  var SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
2634
2921
  "input",
2635
2922
  "output",
@@ -2705,10 +2992,13 @@ function buildFacts(input, selectedRun) {
2705
2992
  childrenByParentId.set(parentId, children);
2706
2993
  }
2707
2994
  }
2995
+ const projection = projectLogicalEvents(scopedEvents);
2708
2996
  return {
2709
2997
  format: input.read.format,
2710
2998
  runs: Object.freeze([...input.read.runs]),
2711
2999
  events: Object.freeze([...scopedEvents]),
3000
+ logicalEvents: projection.logicalEvents,
3001
+ logicalProjectionDiagnostics: projection.diagnostics,
2712
3002
  readerWarnings: Object.freeze([...input.read.warnings]),
2713
3003
  unsupportedFields: Object.freeze([...input.read.unsupportedFields]),
2714
3004
  sourceFiles: Object.freeze([...input.read.sourceFiles]),
@@ -2873,7 +3163,10 @@ function failFinding(ruleId, message, evidence, expected, actual, meta) {
2873
3163
  ...meta?.action !== void 0 ? { action: meta.action } : {}
2874
3164
  };
2875
3165
  }
2876
- function isRecord6(value) {
3166
+ function semanticEvents(context) {
3167
+ return context.logicalEvents ?? context.events;
3168
+ }
3169
+ function isRecord7(value) {
2877
3170
  return typeof value === "object" && value !== null && !Array.isArray(value);
2878
3171
  }
2879
3172
  function normalizedKey(value) {
@@ -2904,7 +3197,7 @@ function pushValueEntries(entries, event, value, path17, key, depth = 0) {
2904
3197
  }
2905
3198
  return;
2906
3199
  }
2907
- if (!isRecord6(value)) return;
3200
+ if (!isRecord7(value)) return;
2908
3201
  for (const nestedKey of Object.keys(value).sort((a, b) => a.localeCompare(b))) {
2909
3202
  pushValueEntries(
2910
3203
  entries,
@@ -2986,7 +3279,7 @@ function createRunStatusRule(options = {}) {
2986
3279
  );
2987
3280
  }
2988
3281
  if (!allowIncomplete) {
2989
- const running = context.events.filter((event) => event.status === "running");
3282
+ const running = semanticEvents(context).filter((event) => event.status === "running");
2990
3283
  if (running.length > 0) {
2991
3284
  findings.push(
2992
3285
  failFinding(
@@ -3154,7 +3447,7 @@ function createSafetyOversizedAttributeRule(options) {
3154
3447
  )
3155
3448
  );
3156
3449
  }
3157
- if (isRecord6(entry.value) && options.maxObjectKeys !== void 0 && Object.keys(entry.value).length > options.maxObjectKeys) {
3450
+ if (isRecord7(entry.value) && options.maxObjectKeys !== void 0 && Object.keys(entry.value).length > options.maxObjectKeys) {
3158
3451
  findings.push(
3159
3452
  failFinding(
3160
3453
  "safety.oversizedAttribute",
@@ -3243,14 +3536,14 @@ function runTraceChecks(input, options = {}) {
3243
3536
  }
3244
3537
 
3245
3538
  // packages/core/src/persisted/token-usage.ts
3246
- function isRecord7(value) {
3539
+ function isRecord8(value) {
3247
3540
  return typeof value === "object" && value !== null && !Array.isArray(value);
3248
3541
  }
3249
3542
  function nonNegativeFinite(value) {
3250
3543
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
3251
3544
  }
3252
3545
  function normalizeTokenUsage(value) {
3253
- if (!isRecord7(value)) return void 0;
3546
+ if (!isRecord8(value)) return void 0;
3254
3547
  const input = nonNegativeFinite(value.input);
3255
3548
  const output = nonNegativeFinite(value.output);
3256
3549
  const suppliedTotal = nonNegativeFinite(value.total);
@@ -4019,7 +4312,7 @@ function persistedEventsForParsedTrace(parsed) {
4019
4312
  sourceName: "agent-inspect-jsonl-reader"
4020
4313
  });
4021
4314
  }
4022
- function isRecord8(value) {
4315
+ function isRecord9(value) {
4023
4316
  return typeof value === "object" && value !== null && !Array.isArray(value);
4024
4317
  }
4025
4318
  function isNonEmptyString3(value) {
@@ -4034,13 +4327,13 @@ function readStringField(record, keys) {
4034
4327
  }
4035
4328
  function readRecordField(record, key) {
4036
4329
  const value = record[key];
4037
- return isRecord8(value) ? value : void 0;
4330
+ return isRecord9(value) ? value : void 0;
4038
4331
  }
4039
4332
  function parseJsonDocument(content) {
4040
4333
  return JSON.parse(content);
4041
4334
  }
4042
4335
  function looksLikeOpenInferenceSpan(value) {
4043
- if (!isRecord8(value)) return false;
4336
+ if (!isRecord9(value)) return false;
4044
4337
  const attributes = readRecordField(value, "attributes");
4045
4338
  return readStringField(value, ["trace_id", "traceId"]) !== void 0 && readStringField(value, ["span_id", "spanId"]) !== void 0 && (readStringField(value, ["name"]) !== void 0 || attributes?.["openinference.span.kind"] !== void 0);
4046
4339
  }
@@ -4065,7 +4358,7 @@ function extractOpenInferenceDocument(root) {
4065
4358
  unsupportedFields
4066
4359
  };
4067
4360
  }
4068
- if (!isRecord8(root)) return void 0;
4361
+ if (!isRecord9(root)) return void 0;
4069
4362
  const rootFormat = root.format;
4070
4363
  const rootCompatibility = root.compatibility;
4071
4364
  const version = typeof root.version === "string" && root.version.trim() !== "" ? root.version : void 0;
@@ -4205,7 +4498,7 @@ function summarizeAttributeValue(value) {
4205
4498
  if (Array.isArray(value)) {
4206
4499
  return { type: "array", length: value.length };
4207
4500
  }
4208
- if (isRecord8(value)) {
4501
+ if (isRecord9(value)) {
4209
4502
  return { type: "object", keyCount: Object.keys(value).length };
4210
4503
  }
4211
4504
  if (value === null) {
@@ -4292,7 +4585,7 @@ function mapOpenInferenceKind(span, attributes, pathPrefix) {
4292
4585
  }
4293
4586
  }
4294
4587
  function mapOpenInferenceStatus(status) {
4295
- if (!isRecord8(status)) return void 0;
4588
+ if (!isRecord9(status)) return void 0;
4296
4589
  const rawCode = status.code;
4297
4590
  if (typeof rawCode !== "string") return void 0;
4298
4591
  switch (rawCode.toUpperCase()) {
@@ -4392,7 +4685,7 @@ function mapOpenInferenceSpan(span, index, version) {
4392
4685
  warnings.push(...kindWarnings);
4393
4686
  const status = mapOpenInferenceStatus(span.status);
4394
4687
  const tokenUsage = readOpenInferenceTokenUsage(rawAttributes);
4395
- const errorMessage = isRecord8(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
4688
+ const errorMessage = isRecord9(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
4396
4689
  const event = {
4397
4690
  schemaVersion: "0.2",
4398
4691
  eventId: typeof rawAttributes["agent_inspect.event_id"] === "string" ? rawAttributes["agent_inspect.event_id"] : spanId,
@@ -4538,7 +4831,7 @@ var openInferenceJsonReader = {
4538
4831
  }
4539
4832
  };
4540
4833
  function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
4541
- if (!isRecord8(value)) {
4834
+ if (!isRecord9(value)) {
4542
4835
  unsupportedFields.push(field);
4543
4836
  warnings.push({
4544
4837
  code: "otlp_attribute_value_invalid",
@@ -4560,15 +4853,15 @@ function parseOtlpAnyValue(value, field, warnings, unsupportedFields) {
4560
4853
  if (typeof value.doubleValue === "number" && Number.isFinite(value.doubleValue)) {
4561
4854
  return value.doubleValue;
4562
4855
  }
4563
- if (isRecord8(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
4856
+ if (isRecord9(value.arrayValue) && Array.isArray(value.arrayValue.values)) {
4564
4857
  return value.arrayValue.values.map(
4565
4858
  (item, index) => parseOtlpAnyValue(item, `${field}.arrayValue.values[${index}]`, warnings, unsupportedFields)
4566
4859
  );
4567
4860
  }
4568
- if (isRecord8(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
4861
+ if (isRecord9(value.kvlistValue) && Array.isArray(value.kvlistValue.values)) {
4569
4862
  const out = {};
4570
4863
  for (const [index, item] of value.kvlistValue.values.entries()) {
4571
- if (!isRecord8(item) || typeof item.key !== "string") {
4864
+ if (!isRecord9(item) || typeof item.key !== "string") {
4572
4865
  unsupportedFields.push(`${field}.kvlistValue.values[${index}]`);
4573
4866
  continue;
4574
4867
  }
@@ -4619,7 +4912,7 @@ function parseOtlpAttributes(value, pathPrefix) {
4619
4912
  }
4620
4913
  for (const [index, item] of value.entries()) {
4621
4914
  const field = `${pathPrefix}[${index}]`;
4622
- if (!isRecord8(item) || typeof item.key !== "string") {
4915
+ if (!isRecord9(item) || typeof item.key !== "string") {
4623
4916
  unsupportedFields.push(field);
4624
4917
  warnings.push({
4625
4918
  code: "otlp_attribute_invalid",
@@ -4642,16 +4935,16 @@ function parseOtlpAttributes(value, pathPrefix) {
4642
4935
  return { attributes, warnings, unsupportedFields };
4643
4936
  }
4644
4937
  function looksLikeOtlpSpan(value) {
4645
- return isRecord8(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
4938
+ return isRecord9(value) && readStringField(value, ["traceId"]) !== void 0 && readStringField(value, ["spanId"]) !== void 0 && readStringField(value, ["name"]) !== void 0;
4646
4939
  }
4647
4940
  function extractOtlpDocument(root) {
4648
- if (!isRecord8(root) || !Array.isArray(root.resourceSpans)) return void 0;
4941
+ if (!isRecord9(root) || !Array.isArray(root.resourceSpans)) return void 0;
4649
4942
  const spans = [];
4650
4943
  const warnings = [];
4651
4944
  const unsupportedFields = [];
4652
4945
  for (const [resourceIndex, resourceSpan] of root.resourceSpans.entries()) {
4653
4946
  const resourcePath = `resourceSpans[${resourceIndex}]`;
4654
- if (!isRecord8(resourceSpan)) {
4947
+ if (!isRecord9(resourceSpan)) {
4655
4948
  unsupportedFields.push(resourcePath);
4656
4949
  continue;
4657
4950
  }
@@ -4674,7 +4967,7 @@ function extractOtlpDocument(root) {
4674
4967
  }
4675
4968
  for (const [scopeIndex, scopeSpan] of resourceSpan.scopeSpans.entries()) {
4676
4969
  const scopePath = `${resourcePath}.scopeSpans[${scopeIndex}]`;
4677
- if (!isRecord8(scopeSpan)) {
4970
+ if (!isRecord9(scopeSpan)) {
4678
4971
  unsupportedFields.push(scopePath);
4679
4972
  continue;
4680
4973
  }
@@ -4741,7 +5034,7 @@ function extractOtlpDocument(root) {
4741
5034
  };
4742
5035
  }
4743
5036
  function mapOtlpStatus(status) {
4744
- if (!isRecord8(status)) return void 0;
5037
+ if (!isRecord9(status)) return void 0;
4745
5038
  const rawCode = status.code;
4746
5039
  if (typeof rawCode !== "string") return void 0;
4747
5040
  switch (rawCode.toUpperCase()) {
@@ -4841,7 +5134,7 @@ function mapOtlpEvents(value, pathPrefix) {
4841
5134
  const events = [];
4842
5135
  for (const [index, event] of value.entries()) {
4843
5136
  const eventPath = `${pathPrefix}[${index}]`;
4844
- if (!isRecord8(event)) {
5137
+ if (!isRecord9(event)) {
4845
5138
  unsupportedFields.push(eventPath);
4846
5139
  continue;
4847
5140
  }
@@ -4979,7 +5272,7 @@ function mapOtlpSpan(context) {
4979
5272
  warnings.push(...kindWarnings);
4980
5273
  const status = mapOtlpStatus(span.status);
4981
5274
  const tokenUsage = readOtlpTokenUsage(parsedSpanAttributes.attributes);
4982
- const errorMessage = isRecord8(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
5275
+ const errorMessage = isRecord9(span.status) && typeof span.status.message === "string" ? span.status.message : void 0;
4983
5276
  const event = {
4984
5277
  schemaVersion: "0.2",
4985
5278
  eventId: typeof parsedSpanAttributes.attributes["agent_inspect.event_id"] === "string" ? parsedSpanAttributes.attributes["agent_inspect.event_id"] : spanId,
@@ -5325,7 +5618,7 @@ function openTrace(input, options = {}) {
5325
5618
  var EXPORT_PAYLOAD_VERSION = "0.1.2";
5326
5619
 
5327
5620
  // packages/core/src/exporters/redact-export.ts
5328
- function isRecord9(value) {
5621
+ function isRecord10(value) {
5329
5622
  return typeof value === "object" && value !== null && !Array.isArray(value);
5330
5623
  }
5331
5624
  function deepClone(value) {
@@ -5399,7 +5692,7 @@ function redactEventAttributes(attrs, redactor, maxMetadataValueLength, maxPrevi
5399
5692
  0
5400
5693
  );
5401
5694
  const err = bounded.error;
5402
- if (isRecord9(err) && typeof err.message === "string") {
5695
+ if (isRecord10(err) && typeof err.message === "string") {
5403
5696
  bounded.error = {
5404
5697
  ...err,
5405
5698
  message: truncateStringForProfile(
@@ -6061,7 +6354,7 @@ var STRICT_PROFILE_EXTRA_KEYS2 = [
6061
6354
  "retrieval",
6062
6355
  "query"
6063
6356
  ];
6064
- function isRecord10(value) {
6357
+ function isRecord11(value) {
6065
6358
  return typeof value === "object" && value !== null && !Array.isArray(value);
6066
6359
  }
6067
6360
  function toKey2(key) {
@@ -6426,7 +6719,7 @@ var Redactor2 = class {
6426
6719
  });
6427
6720
  return out;
6428
6721
  }
6429
- if (isRecord10(value)) {
6722
+ if (isRecord11(value)) {
6430
6723
  if (state.seen.has(value)) return state.seen.get(value);
6431
6724
  const out = {};
6432
6725
  state.seen.set(value, out);
@@ -6804,7 +7097,8 @@ async function callReadOnlyTool(context, name, args = {}) {
6804
7097
  sourceFile: path14__default.default.basename(meta.filePath),
6805
7098
  warnings: read.warnings.slice(0, 20),
6806
7099
  unsupportedFields: read.unsupportedFields.slice(0, 20),
6807
- note: "Bounded local diagnostics only; not a network health check."
7100
+ semanticParity: summarizeSemanticParity(read.events),
7101
+ note: "Bounded local diagnostics only; not a network health check. semanticParity uses logicalEvents projection."
6808
7102
  },
6809
7103
  context
6810
7104
  );