@agent-inspect/mcp-server 6.14.1 → 6.15.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.
@@ -2568,6 +2568,126 @@ function diffRuns(left, right, options) {
2568
2568
  return table;
2569
2569
  })();
2570
2570
 
2571
+ // packages/core/src/diagnostics/programmatic.ts
2572
+ var PROGRAMMATIC_DIAGNOSTIC_SPECS = Object.freeze({
2573
+ AI_TRACE_INPUT_INVALID: {
2574
+ code: "AI_TRACE_INPUT_INVALID",
2575
+ summary: 'Expected { type: "file", path }, { type: "directory", path }, { type: "string", content }, { type: "buffer", content }, or { type: "stdin" }.',
2576
+ remediation: "For a file path, use openTraceFile(path).",
2577
+ relatedCodes: ["invalid_input"]
2578
+ },
2579
+ AI_TRACE_FORMAT_UNSUPPORTED: {
2580
+ code: "AI_TRACE_FORMAT_UNSUPPORTED",
2581
+ summary: "No trace reader could detect the input format.",
2582
+ remediation: "Pass an AgentInspect JSONL file via openTraceFile, or set options.format to a registered reader.",
2583
+ relatedCodes: ["unsupported_format"]
2584
+ },
2585
+ AI_TRACE_FORMAT_AMBIGUOUS: {
2586
+ code: "AI_TRACE_FORMAT_AMBIGUOUS",
2587
+ summary: "Multiple trace readers matched the input with equal confidence.",
2588
+ remediation: "Set options.format explicitly to disambiguate the reader.",
2589
+ relatedCodes: ["ambiguous_format"]
2590
+ },
2591
+ AI_TRACE_FACTS_INPUT_NOT_NORMALIZED: {
2592
+ code: "AI_TRACE_FACTS_INPUT_NOT_NORMALIZED",
2593
+ summary: "TraceFacts requires TraceReadResult or PersistedInspectEvent[].",
2594
+ remediation: "Use openTraceFile() to normalize a JSONL trace first."
2595
+ },
2596
+ AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED: {
2597
+ code: "AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED",
2598
+ summary: "Multiple runs are available; select a run before executing checks.",
2599
+ remediation: "Pass options.runId or TraceCheckInput.selectedRun.",
2600
+ relatedCodes: ["AI_CHECK_RUN_SELECTION_REQUIRED"]
2601
+ },
2602
+ AI_TRACE_RELATIONSHIP_SELF_PARENT: {
2603
+ code: "AI_TRACE_RELATIONSHIP_SELF_PARENT",
2604
+ summary: "A parentId equals its own event/step id.",
2605
+ remediation: "Reject at capture (AI_LANGGRAPH_SELF_PARENT_REJECTED) or drop via logical projection; do not invent replacement parents.",
2606
+ relatedCodes: [
2607
+ "AI_LANGGRAPH_SELF_PARENT_REJECTED",
2608
+ "AI_LOGICAL_SELF_PARENT_REMOVED"
2609
+ ]
2610
+ },
2611
+ AI_TRACE_RELATIONSHIP_CYCLE: {
2612
+ code: "AI_TRACE_RELATIONSHIP_CYCLE",
2613
+ summary: "Trace contains a parentId cycle.",
2614
+ remediation: "Use visibility-first tree linking for legacy fixtures; prefer acyclic capture for new adapter output.",
2615
+ relatedCodes: ["structure.cycle"]
2616
+ }
2617
+ });
2618
+ function formatProgrammaticDiagnostic(code, detail) {
2619
+ const spec = PROGRAMMATIC_DIAGNOSTIC_SPECS[code];
2620
+ const summary = detail?.trim() ? detail.trim() : spec.summary;
2621
+ return `${code}: ${summary} Remediation: ${spec.remediation}`;
2622
+ }
2623
+
2624
+ // packages/core/src/safety/sensitive-key.ts
2625
+ function normalizeSensitiveKey(value) {
2626
+ return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
2627
+ }
2628
+ var NON_CREDENTIAL_TOKEN_CONFIG_KEYS = new Set(
2629
+ [
2630
+ "tokens",
2631
+ "max_tokens",
2632
+ "min_tokens",
2633
+ "ls_max_tokens",
2634
+ "token_count",
2635
+ "token_limit",
2636
+ "token_budget",
2637
+ "input_tokens",
2638
+ "output_tokens",
2639
+ "total_tokens",
2640
+ "cached_tokens",
2641
+ "prompt_tokens",
2642
+ "completion_tokens"
2643
+ ].map(normalizeSensitiveKey)
2644
+ );
2645
+ var DEFAULT_CREDENTIAL_SENSITIVE_KEYS = [
2646
+ "authorization",
2647
+ "cookie",
2648
+ "token",
2649
+ "access_token",
2650
+ "accesstoken",
2651
+ "auth_token",
2652
+ "authtoken",
2653
+ "refresh_token",
2654
+ "refreshtoken",
2655
+ "id_token",
2656
+ "idtoken",
2657
+ "bearer_token",
2658
+ "bearertoken",
2659
+ "api_token",
2660
+ "apitoken",
2661
+ "apikey",
2662
+ "api_key",
2663
+ "password",
2664
+ "secret",
2665
+ "email"
2666
+ ];
2667
+ function isTokenCredentialKey(normalized) {
2668
+ if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
2669
+ if (normalized === "token") return true;
2670
+ if (normalized.endsWith("tokens")) return false;
2671
+ return normalized.endsWith("token");
2672
+ }
2673
+ function isCredentialSensitiveKey(key, sensitiveKeys = DEFAULT_CREDENTIAL_SENSITIVE_KEYS) {
2674
+ if (!key) return false;
2675
+ const normalized = normalizeSensitiveKey(key);
2676
+ if (!normalized) return false;
2677
+ if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS.has(normalized)) return false;
2678
+ for (const sensitive of sensitiveKeys) {
2679
+ const s = normalizeSensitiveKey(sensitive);
2680
+ if (!s) continue;
2681
+ if (s === "token") {
2682
+ if (isTokenCredentialKey(normalized)) return true;
2683
+ continue;
2684
+ }
2685
+ if (normalized === s) return true;
2686
+ if (normalized.endsWith(`_${s}`) || normalized.startsWith(`${s}_`)) return true;
2687
+ }
2688
+ return false;
2689
+ }
2690
+
2571
2691
  // packages/core/src/checks/logical-events.ts
2572
2692
  function isRecord6(value) {
2573
2693
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -2763,6 +2883,26 @@ function projectLogicalEvents(events) {
2763
2883
  }
2764
2884
  }
2765
2885
  if (!remapped) {
2886
+ if (originalParentId === event.eventId) {
2887
+ diagnostics.push({
2888
+ code: "AI_LOGICAL_SELF_PARENT_REMOVED",
2889
+ message: formatProgrammaticDiagnostic(
2890
+ "AI_TRACE_RELATIONSHIP_SELF_PARENT",
2891
+ `Removed self-parent edge on ${event.eventId}.`
2892
+ ),
2893
+ eventIds: [event.eventId]
2894
+ });
2895
+ const { parentId: _drop, ...rest } = event;
2896
+ normalized.push({
2897
+ ...rest,
2898
+ projection: {
2899
+ ...event.projection,
2900
+ parentNormalized: true,
2901
+ originalParentId
2902
+ }
2903
+ });
2904
+ continue;
2905
+ }
2766
2906
  if (!logicalById.has(originalParentId) && !logicalByRawId.has(originalParentId)) {
2767
2907
  const mapping = event.attributes?.parentMapping;
2768
2908
  const unresolved = mapping === "unresolved" || event.attributes?.parentUnresolved === true || event.attributes?.unresolvedParent === true || // LangGraph/framework scaffolding sentinel labels are not event ids.
@@ -2778,6 +2918,26 @@ function projectLogicalEvents(events) {
2778
2918
  normalized.push(event);
2779
2919
  continue;
2780
2920
  }
2921
+ if (nextParent === event.eventId) {
2922
+ diagnostics.push({
2923
+ code: "AI_LOGICAL_SELF_PARENT_REMOVED",
2924
+ message: formatProgrammaticDiagnostic(
2925
+ "AI_TRACE_RELATIONSHIP_SELF_PARENT",
2926
+ `Removed self-parent edge on ${event.eventId} (was ${originalParentId}).`
2927
+ ),
2928
+ eventIds: [event.eventId]
2929
+ });
2930
+ const { parentId: _drop, ...rest } = event;
2931
+ normalized.push({
2932
+ ...rest,
2933
+ projection: {
2934
+ ...event.projection,
2935
+ parentNormalized: true,
2936
+ originalParentId
2937
+ }
2938
+ });
2939
+ continue;
2940
+ }
2781
2941
  diagnostics.push({
2782
2942
  code: "AI_LOGICAL_PARENT_REMAPPED",
2783
2943
  message: `Remapped parent ${originalParentId} \u2192 ${nextParent} for ${event.eventId}.`,
@@ -2850,7 +3010,45 @@ function summarizeSemanticParity(events) {
2850
3010
  diagnostics: projection.diagnostics
2851
3011
  };
2852
3012
  }
2853
- function buildTraceFacts(events) {
3013
+ var TRACE_FACTS_INPUT_NOT_NORMALIZED = formatProgrammaticDiagnostic(
3014
+ "AI_TRACE_FACTS_INPUT_NOT_NORMALIZED"
3015
+ );
3016
+ function isTraceReadResult(input) {
3017
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
3018
+ return false;
3019
+ }
3020
+ const record = input;
3021
+ return Array.isArray(record.events) && Array.isArray(record.runs) && typeof record.format === "string" && Array.isArray(record.warnings);
3022
+ }
3023
+ function looksLikeRawV01TraceEvents(input) {
3024
+ if (!Array.isArray(input) || input.length === 0) return false;
3025
+ const first = input[0];
3026
+ if (typeof first !== "object" || first === null) return false;
3027
+ const row = first;
3028
+ return typeof row.event === "string" && (row.schemaVersion === "0.1" || row.eventId === void 0);
3029
+ }
3030
+ function isPersistedInspectEventArray(input) {
3031
+ if (!Array.isArray(input)) return false;
3032
+ if (input.length === 0) return true;
3033
+ const first = input[0];
3034
+ if (typeof first !== "object" || first === null) return false;
3035
+ const row = first;
3036
+ return typeof row.eventId === "string" && (row.schemaVersion === "0.2" || row.schemaVersion === "1.0" || row.schemaVersion === "0.1") && typeof row.event !== "string";
3037
+ }
3038
+ function resolveTraceFactsEvents(input) {
3039
+ if (isTraceReadResult(input)) {
3040
+ return input.events;
3041
+ }
3042
+ if (looksLikeRawV01TraceEvents(input)) {
3043
+ throw new TypeError(TRACE_FACTS_INPUT_NOT_NORMALIZED);
3044
+ }
3045
+ if (isPersistedInspectEventArray(input)) {
3046
+ return input;
3047
+ }
3048
+ throw new TypeError(TRACE_FACTS_INPUT_NOT_NORMALIZED);
3049
+ }
3050
+ function buildTraceFacts(input) {
3051
+ const events = resolveTraceFactsEvents(input);
2854
3052
  const projection = projectLogicalEvents(events);
2855
3053
  const toolsByName = /* @__PURE__ */ new Map();
2856
3054
  const llmEvents = [];
@@ -2894,16 +3092,7 @@ var STATUS_RANK = {
2894
3092
  warning: 1,
2895
3093
  pass: 2
2896
3094
  };
2897
- var DEFAULT_SENSITIVE_KEYS = [
2898
- "authorization",
2899
- "cookie",
2900
- "token",
2901
- "apikey",
2902
- "api_key",
2903
- "password",
2904
- "secret",
2905
- "email"
2906
- ];
3095
+ var DEFAULT_SENSITIVE_KEYS = DEFAULT_CREDENTIAL_SENSITIVE_KEYS;
2907
3096
  var DEFAULT_RAW_CONTENT_KEYS = [
2908
3097
  "body",
2909
3098
  "headers",
@@ -3061,7 +3250,13 @@ function resolveSelectedRun(input, runId) {
3061
3250
  if (input.read.runs.length === 0) {
3062
3251
  return {
3063
3252
  diagnostics: [
3064
- diagnostic("AI_CHECK_RUN_SELECTION_REQUIRED", "No runs are available for checks.")
3253
+ diagnostic(
3254
+ "AI_CHECK_RUN_SELECTION_REQUIRED",
3255
+ formatProgrammaticDiagnostic(
3256
+ "AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED",
3257
+ "No runs are available for checks."
3258
+ )
3259
+ )
3065
3260
  ]
3066
3261
  };
3067
3262
  }
@@ -3069,7 +3264,7 @@ function resolveSelectedRun(input, runId) {
3069
3264
  diagnostics: [
3070
3265
  diagnostic(
3071
3266
  "AI_CHECK_RUN_SELECTION_REQUIRED",
3072
- "Multiple runs are available; select a run before executing checks."
3267
+ formatProgrammaticDiagnostic("AI_TRACE_CONTRACT_RUN_SELECTION_REQUIRED")
3073
3268
  )
3074
3269
  ]
3075
3270
  };
@@ -3258,9 +3453,7 @@ function hasRedactionMarker(value, markers) {
3258
3453
  return markers.some((marker) => value.includes(marker)) || /^\[HASH:[A-Za-z0-9_-]+\]$/.test(value);
3259
3454
  }
3260
3455
  function isSensitiveKey(key, sensitiveKeys) {
3261
- if (!key) return false;
3262
- const normalized = normalizedKey(key);
3263
- return sensitiveKeys.some((sensitive) => normalized.includes(normalizedKey(sensitive)));
3456
+ return isCredentialSensitiveKey(key, sensitiveKeys);
3264
3457
  }
3265
3458
  function isRawContentKey(key, forbiddenKeys) {
3266
3459
  if (!key) return false;
@@ -3830,6 +4023,20 @@ function traceEventsToPersistedInspectEvents(events, options) {
3830
4023
  function inc(map, key) {
3831
4024
  map[key] = (map[key] ?? 0) + 1;
3832
4025
  }
4026
+ function confidenceRank(confidence) {
4027
+ switch (confidence) {
4028
+ case "unknown":
4029
+ return 0;
4030
+ case "heuristic":
4031
+ return 1;
4032
+ case "correlated":
4033
+ return 2;
4034
+ case "explicit":
4035
+ return 3;
4036
+ default:
4037
+ return 0;
4038
+ }
4039
+ }
3833
4040
  function computeRunStatus(events) {
3834
4041
  let runTerminal;
3835
4042
  let sawRunEvent = false;
@@ -3850,6 +4057,84 @@ function computeRunStatus(events) {
3850
4057
  }
3851
4058
  return hasRunning ? "running" : "ok";
3852
4059
  }
4060
+ function linkNodesVisibilityFirst(nodes) {
4061
+ let selfParentCount = 0;
4062
+ let unresolvedParentCount = 0;
4063
+ let normalizedEdgeCount = 0;
4064
+ let cycleCount = 0;
4065
+ const pending = [];
4066
+ for (const node of nodes.values()) {
4067
+ const parentId = node.event.parentId;
4068
+ if (!parentId) continue;
4069
+ if (parentId === node.event.eventId) {
4070
+ selfParentCount += 1;
4071
+ normalizedEdgeCount += 1;
4072
+ continue;
4073
+ }
4074
+ if (!nodes.has(parentId)) {
4075
+ unresolvedParentCount += 1;
4076
+ continue;
4077
+ }
4078
+ pending.push({
4079
+ childId: node.event.eventId,
4080
+ parentId,
4081
+ childTimestamp: node.event.timestamp,
4082
+ childConfidence: node.event.confidence
4083
+ });
4084
+ }
4085
+ pending.sort((a, b) => {
4086
+ const conf = confidenceRank(b.childConfidence) - confidenceRank(a.childConfidence);
4087
+ if (conf !== 0) return conf;
4088
+ return a.childTimestamp - b.childTimestamp;
4089
+ });
4090
+ const parentOf = /* @__PURE__ */ new Map();
4091
+ for (const edge of pending) {
4092
+ parentOf.set(edge.childId, edge.parentId);
4093
+ let cursor = edge.parentId;
4094
+ const seen = /* @__PURE__ */ new Set([edge.childId]);
4095
+ let cyclic = false;
4096
+ while (cursor) {
4097
+ if (seen.has(cursor)) {
4098
+ cyclic = true;
4099
+ break;
4100
+ }
4101
+ seen.add(cursor);
4102
+ cursor = parentOf.get(cursor);
4103
+ }
4104
+ if (cyclic) {
4105
+ parentOf.delete(edge.childId);
4106
+ cycleCount += 1;
4107
+ normalizedEdgeCount += 1;
4108
+ }
4109
+ }
4110
+ for (const [childId, parentId] of parentOf) {
4111
+ nodes.get(parentId).children.push(nodes.get(childId));
4112
+ }
4113
+ const roots = [];
4114
+ for (const node of nodes.values()) {
4115
+ if (!parentOf.has(node.event.eventId)) {
4116
+ roots.push(node);
4117
+ }
4118
+ }
4119
+ const assignDepth = (n, depth, stack) => {
4120
+ if (stack.has(n.event.eventId)) return;
4121
+ n.depth = depth;
4122
+ stack.add(n.event.eventId);
4123
+ for (const c of n.children) assignDepth(c, depth + 1, stack);
4124
+ stack.delete(n.event.eventId);
4125
+ };
4126
+ for (const r of roots) assignDepth(r, 0, /* @__PURE__ */ new Set());
4127
+ return {
4128
+ roots,
4129
+ summary: {
4130
+ rootCount: roots.length,
4131
+ selfParentCount,
4132
+ cycleCount,
4133
+ unresolvedParentCount,
4134
+ normalizedEdgeCount
4135
+ }
4136
+ };
4137
+ }
3853
4138
  var TreeBuilder = class {
3854
4139
  constructor(options) {
3855
4140
  void options?.config;
@@ -3867,20 +4152,7 @@ var TreeBuilder = class {
3867
4152
  for (const e of sorted) {
3868
4153
  nodes.set(e.eventId, { event: e, children: [], depth: 0 });
3869
4154
  }
3870
- const roots = [];
3871
- for (const node of nodes.values()) {
3872
- const parentId = node.event.parentId;
3873
- if (parentId && nodes.has(parentId)) {
3874
- nodes.get(parentId).children.push(node);
3875
- } else {
3876
- roots.push(node);
3877
- }
3878
- }
3879
- const assignDepth = (n, depth) => {
3880
- n.depth = depth;
3881
- for (const c of n.children) assignDepth(c, depth + 1);
3882
- };
3883
- for (const r of roots) assignDepth(r, 0);
4155
+ const { roots, summary } = linkNodesVisibilityFirst(nodes);
3884
4156
  const confidenceBreakdown = {
3885
4157
  explicit: 0,
3886
4158
  correlated: 0,
@@ -3908,7 +4180,8 @@ var TreeBuilder = class {
3908
4180
  metadata: {
3909
4181
  totalEvents: sorted.length,
3910
4182
  confidenceBreakdown,
3911
- kinds
4183
+ kinds,
4184
+ relationshipSummary: summary
3912
4185
  }
3913
4186
  });
3914
4187
  }
@@ -4137,6 +4410,38 @@ var TraceReadError = class extends Error {
4137
4410
  this.warnings = warnings;
4138
4411
  }
4139
4412
  };
4413
+ var TRACE_INPUT_INVALID_MESSAGE = formatProgrammaticDiagnostic(
4414
+ "AI_TRACE_INPUT_INVALID"
4415
+ );
4416
+ function isTraceInput(input) {
4417
+ if (typeof input !== "object" || input === null || Array.isArray(input)) {
4418
+ return false;
4419
+ }
4420
+ const record = input;
4421
+ switch (record.type) {
4422
+ case "file":
4423
+ case "directory":
4424
+ return typeof record.path === "string";
4425
+ case "string":
4426
+ return typeof record.content === "string";
4427
+ case "buffer":
4428
+ return Buffer.isBuffer(record.content);
4429
+ case "stdin":
4430
+ return true;
4431
+ default:
4432
+ return false;
4433
+ }
4434
+ }
4435
+ function assertTraceInput(input) {
4436
+ if (isTraceInput(input)) return;
4437
+ throw new TraceReadError("invalid_input", TRACE_INPUT_INVALID_MESSAGE, [
4438
+ {
4439
+ code: "AI_TRACE_INPUT_INVALID",
4440
+ message: TRACE_INPUT_INVALID_MESSAGE,
4441
+ severity: "error"
4442
+ }
4443
+ ]);
4444
+ }
4140
4445
  function normalizeCandidate(reader, candidate) {
4141
4446
  const confidence = Number.isFinite(candidate.confidence) ? Math.max(0, Math.min(1, candidate.confidence)) : 0;
4142
4447
  return {
@@ -5491,6 +5796,7 @@ var DEFAULT_TRACE_READERS = [
5491
5796
  otlpJsonReader
5492
5797
  ];
5493
5798
  async function detectTraceFormat(input, options = {}) {
5799
+ assertTraceInput(input);
5494
5800
  const readers = options.readers ?? DEFAULT_TRACE_READERS;
5495
5801
  if (options.format !== void 0) {
5496
5802
  const reader = findReaderByFormat(options.format, readers);
@@ -5587,19 +5893,20 @@ async function detectTraceFormat(input, options = {}) {
5587
5893
  };
5588
5894
  }
5589
5895
  async function readTrace(input, options = {}) {
5896
+ assertTraceInput(input);
5590
5897
  const readers = options.readers ?? DEFAULT_TRACE_READERS;
5591
5898
  const detection = await detectTraceFormat(input, options);
5592
5899
  if (detection.status === "unsupported" || detection.format === void 0) {
5593
5900
  throw new TraceReadError(
5594
5901
  "unsupported_format",
5595
- "No trace reader could detect the input format.",
5902
+ formatProgrammaticDiagnostic("AI_TRACE_FORMAT_UNSUPPORTED"),
5596
5903
  detection.warnings
5597
5904
  );
5598
5905
  }
5599
5906
  if (detection.status === "ambiguous") {
5600
5907
  throw new TraceReadError(
5601
5908
  "ambiguous_format",
5602
- "Multiple trace readers matched the input with equal confidence.",
5909
+ formatProgrammaticDiagnostic("AI_TRACE_FORMAT_AMBIGUOUS"),
5603
5910
  detection.warnings
5604
5911
  );
5605
5912
  }
@@ -5607,7 +5914,10 @@ async function readTrace(input, options = {}) {
5607
5914
  if (!reader) {
5608
5915
  throw new TraceReadError(
5609
5916
  "unsupported_format",
5610
- `No trace reader is registered for format "${detection.format}".`,
5917
+ formatProgrammaticDiagnostic(
5918
+ "AI_TRACE_FORMAT_UNSUPPORTED",
5919
+ `No trace reader is registered for format "${detection.format}".`
5920
+ ),
5611
5921
  detection.warnings
5612
5922
  );
5613
5923
  }
@@ -6322,6 +6632,53 @@ function exportRunTree(tree, options) {
6322
6632
  }
6323
6633
  }
6324
6634
  }
6635
+
6636
+ // packages/redact/src/sensitive-key.ts
6637
+ function normalizeSensitiveKey2(value) {
6638
+ return value.toLowerCase().replace(/[^a-z0-9_]/g, "");
6639
+ }
6640
+ var NON_CREDENTIAL_TOKEN_CONFIG_KEYS2 = new Set(
6641
+ [
6642
+ "tokens",
6643
+ "max_tokens",
6644
+ "min_tokens",
6645
+ "ls_max_tokens",
6646
+ "token_count",
6647
+ "token_limit",
6648
+ "token_budget",
6649
+ "input_tokens",
6650
+ "output_tokens",
6651
+ "total_tokens",
6652
+ "cached_tokens",
6653
+ "prompt_tokens",
6654
+ "completion_tokens"
6655
+ ].map(normalizeSensitiveKey2)
6656
+ );
6657
+ function isTokenCredentialKey2(normalized) {
6658
+ if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS2.has(normalized)) return false;
6659
+ if (normalized === "token") return true;
6660
+ if (normalized.endsWith("tokens")) return false;
6661
+ return normalized.endsWith("token");
6662
+ }
6663
+ function isCredentialSensitiveKey2(key, sensitiveKeys) {
6664
+ if (!key) return false;
6665
+ const normalized = normalizeSensitiveKey2(key);
6666
+ if (!normalized) return false;
6667
+ if (NON_CREDENTIAL_TOKEN_CONFIG_KEYS2.has(normalized)) return false;
6668
+ for (const sensitive of sensitiveKeys) {
6669
+ const s = normalizeSensitiveKey2(sensitive);
6670
+ if (!s) continue;
6671
+ if (s === "token") {
6672
+ if (isTokenCredentialKey2(normalized)) return true;
6673
+ continue;
6674
+ }
6675
+ if (normalized === s) return true;
6676
+ if (normalized.endsWith(`_${s}`) || normalized.startsWith(`${s}_`)) return true;
6677
+ }
6678
+ return false;
6679
+ }
6680
+
6681
+ // packages/redact/src/index.ts
6325
6682
  var DEFAULT_REDACT_KEYS2 = [
6326
6683
  "authorization",
6327
6684
  "cookie",
@@ -6383,6 +6740,12 @@ function isRecord11(value) {
6383
6740
  function toKey2(key) {
6384
6741
  return key.toLowerCase();
6385
6742
  }
6743
+ function findCompiledKeyRule(key, rules) {
6744
+ const exact = toKey2(key);
6745
+ const direct = rules.find((candidate) => candidate.key === exact);
6746
+ if (direct) return direct;
6747
+ return rules.find((candidate) => isCredentialSensitiveKey2(key, [candidate.key]));
6748
+ }
6386
6749
  function stableHash2(value) {
6387
6750
  const hash = crypto.createHash("sha256").update(value, "utf8").digest("hex");
6388
6751
  return hash.slice(0, 8);
@@ -6704,7 +7067,7 @@ var Redactor2 = class {
6704
7067
  return "[Truncated]";
6705
7068
  }
6706
7069
  if (key !== void 0) {
6707
- const rule = this.#rules.find((candidate) => candidate.key === toKey2(key));
7070
+ const rule = findCompiledKeyRule(key, this.#rules);
6708
7071
  if (rule) {
6709
7072
  this.#recordFinding(
6710
7073
  state,
@@ -7586,5 +7949,5 @@ async function runReadOnlyMcpServer(options = {}) {
7586
7949
  }
7587
7950
 
7588
7951
  export { MCP_MAX_REQUEST_BYTES, MCP_PROTOCOL_VERSION, READ_ONLY_TOOLS, callReadOnlyTool, createMcpServerContext, handleMcpProtocolLine, runReadOnlyMcpServer };
7589
- //# sourceMappingURL=chunk-C6EBJS57.mjs.map
7590
- //# sourceMappingURL=chunk-C6EBJS57.mjs.map
7952
+ //# sourceMappingURL=chunk-JASWIBAT.mjs.map
7953
+ //# sourceMappingURL=chunk-JASWIBAT.mjs.map