sf-intelligence 0.1.4 → 0.1.5

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 (4) hide show
  1. package/bin/sfi.js +0 -0
  2. package/dist/index.js +2307 -397
  3. package/package.json +16 -16
  4. package/LICENSE +0 -201
package/dist/index.js CHANGED
@@ -1027,6 +1027,7 @@ var init_resolve = __esm({
1027
1027
  const minBase = options?.minScore ?? MIN_BASE;
1028
1028
  const queryTokens = tokenizeText(query);
1029
1029
  const normQuery = normalizeName(query);
1030
+ const querySpaceWords = new Set(query.trim().split(/\s+/).map(normalizeName).filter((w) => w.length > 0));
1030
1031
  let index;
1031
1032
  try {
1032
1033
  index = await getResolveIndex(store);
@@ -1037,6 +1038,12 @@ var init_resolve = __esm({
1037
1038
  });
1038
1039
  }
1039
1040
  const candidateIdx = gatherCandidates(index, queryTokens, normQuery);
1041
+ const objectNormNames = /* @__PURE__ */ new Set();
1042
+ for (const n of index.nodes) {
1043
+ if (n.type === "CustomObject")
1044
+ objectNormNames.add(n.normName);
1045
+ }
1046
+ const namedObjectWords = new Set([...querySpaceWords].filter((w) => objectNormNames.has(w)));
1040
1047
  const typeFilter = options?.types !== void 0 && options.types.length > 0 ? new Set(options.types) : null;
1041
1048
  const parentFilter = options?.parentId;
1042
1049
  const pass1 = [];
@@ -1079,7 +1086,8 @@ var init_resolve = __esm({
1079
1086
  if (perToken[i].score > globalBest[i])
1080
1087
  globalBest[i] = perToken[i].score;
1081
1088
  }
1082
- const wholeExact = normQuery.length >= 2 && node.normName === normQuery && query.includes(".") === node.apiName.includes(".");
1089
+ const crossObjectFieldDecoy = node.type === "CustomField" && namedObjectWords.size > 0 && (node.parentApiName === null || !namedObjectWords.has(normalizeName(node.parentApiName)));
1090
+ const wholeExact = normQuery.length >= 2 && node.normName === normQuery && query.includes(".") === node.apiName.includes(".") && !crossObjectFieldDecoy;
1083
1091
  pass1.push({ node, perToken, wholeExact, parentMatched });
1084
1092
  }
1085
1093
  const anchorIdx = [];
@@ -2488,7 +2496,7 @@ var init_finding_suppression = __esm({
2488
2496
 
2489
2497
  // ../mcp/dist/src/tools/crud-fls-audit.js
2490
2498
  import { z as z2 } from "zod";
2491
- var CRUD_FLS_TOOL, CRUD_FLS_MAX_LIMIT, CRUD_FLS_DEFAULT_LIMIT, LIST_PAGE_SIZE, CRUD_FLS_RULES, SCANNED_TYPES, SEVERITY_SET, Q80_FALSE_POSITIVE_DISCLOSURE, CROSS_METHOD_DATAFLOW_DISCLOSURE, DYNAMIC_SOQL_DISCLOSURE, crudFlsAuditInputSchema, coerceIssue, compareClassById, crudFlsAuditHandler;
2499
+ var CRUD_FLS_TOOL, CRUD_FLS_MAX_LIMIT, CRUD_FLS_DEFAULT_LIMIT, LIST_PAGE_SIZE, CRUD_FLS_RULES, SCANNED_TYPES, SEVERITY_SET, Q80_FALSE_POSITIVE_DISCLOSURE, CROSS_METHOD_DATAFLOW_DISCLOSURE, DYNAMIC_SOQL_DISCLOSURE, crudFlsAuditInputSchema, CRUD_FLS_PAYLOAD_BUDGET_BYTES, trimEntryFindings, fitClassesToBudget, coerceIssue, compareClassById, crudFlsAuditHandler;
2492
2500
  var init_crud_fls_audit = __esm({
2493
2501
  "../mcp/dist/src/tools/crud-fls-audit.js"() {
2494
2502
  "use strict";
@@ -2518,8 +2526,39 @@ var init_crud_fls_audit = __esm({
2518
2526
  CROSS_METHOD_DATAFLOW_DISCLOSURE = "cross-method dataflow is invisible \u2014 a method that delegates the dangerous operation to a helper is analyzed in isolation; the helper behavior is invisible. Spot-check the call chain before treating a finding as a bug.";
2519
2527
  DYNAMIC_SOQL_DISCLOSURE = "dynamic SOQL (Database.query) strings are stripped before pattern passes; the embedded SQL is invisible to the FLS recognizer.";
2520
2528
  crudFlsAuditInputSchema = z2.object({
2521
- limit: z2.number().int().min(1).max(CRUD_FLS_MAX_LIMIT).optional()
2529
+ limit: z2.number().int().min(1).max(CRUD_FLS_MAX_LIMIT).optional(),
2530
+ offset: z2.number().int().min(0).optional()
2522
2531
  });
2532
+ CRUD_FLS_PAYLOAD_BUDGET_BYTES = 36e3;
2533
+ trimEntryFindings = (entry, budgetBytes) => {
2534
+ const kept = [];
2535
+ let used = Buffer.byteLength(JSON.stringify({ ...entry, findings: [] }), "utf8");
2536
+ for (const finding of entry.findings) {
2537
+ const size = Buffer.byteLength(JSON.stringify(finding), "utf8") + 1;
2538
+ if (kept.length > 0 && used + size > budgetBytes)
2539
+ break;
2540
+ kept.push(finding);
2541
+ used += size;
2542
+ }
2543
+ return { ...entry, findings: kept, findingsTruncated: true };
2544
+ };
2545
+ fitClassesToBudget = (classes, budgetBytes) => {
2546
+ const kept = [];
2547
+ let used = 0;
2548
+ for (const entry of classes) {
2549
+ const size = Buffer.byteLength(JSON.stringify(entry), "utf8") + 1;
2550
+ if (kept.length === 0 && size > budgetBytes) {
2551
+ kept.push(trimEntryFindings(entry, budgetBytes));
2552
+ return { kept, trimmed: true };
2553
+ }
2554
+ if (kept.length > 0 && used + size > budgetBytes) {
2555
+ return { kept, trimmed: true };
2556
+ }
2557
+ kept.push(entry);
2558
+ used += size;
2559
+ }
2560
+ return { kept, trimmed: false };
2561
+ };
2523
2562
  coerceIssue = (raw) => {
2524
2563
  if (raw === null || typeof raw !== "object")
2525
2564
  return null;
@@ -2592,8 +2631,11 @@ var init_crud_fls_audit = __esm({
2592
2631
  }
2593
2632
  }
2594
2633
  const classes = [...perClass.values()].sort(compareClassById);
2595
- const truncated = classes.length > limit;
2596
- const slice = classes.slice(0, limit);
2634
+ const offset = input2.offset ?? 0;
2635
+ const page = classes.slice(offset, offset + limit);
2636
+ const { kept, trimmed } = fitClassesToBudget(page, CRUD_FLS_PAYLOAD_BUDGET_BYTES);
2637
+ const returnedEnd = offset + kept.length;
2638
+ const truncated = returnedEnd < classes.length;
2597
2639
  const boundaries = classes.length === 0 ? [] : [
2598
2640
  Q80_FALSE_POSITIVE_DISCLOSURE,
2599
2641
  CROSS_METHOD_DATAFLOW_DISCLOSURE,
@@ -2601,13 +2643,19 @@ var init_crud_fls_audit = __esm({
2601
2643
  ];
2602
2644
  return ok({
2603
2645
  data: {
2604
- classes: slice,
2646
+ classes: kept,
2605
2647
  totalClassCount: classes.length,
2606
2648
  totalFindingCount,
2607
2649
  suppressedFindingCount,
2608
2650
  byRule,
2609
2651
  boundaries,
2610
- truncated
2652
+ limit,
2653
+ offset,
2654
+ truncated,
2655
+ ...truncated ? { nextOffset: returnedEnd } : {},
2656
+ ...trimmed ? {
2657
+ note: `Response trimmed to ${kept.length} of ${page.length} classes (${classes.length} total) to stay under the ~45 KB MCP response limit. Advance with offset += ${kept.length} for the rest.`
2658
+ } : {}
2611
2659
  },
2612
2660
  vaultState: {
2613
2661
  sourceTreeHash: ctx.manifest.sourceTreeHash,
@@ -7199,7 +7247,10 @@ var init_event_subscribers = __esm({
7199
7247
  "Flow"
7200
7248
  ]);
7201
7249
  eventSubscribersInputSchema = z29.object({
7202
- eventId: z29.string().min(1),
7250
+ // Optional (R0791 fix): OMIT to get the catalog of ALL Platform Events with
7251
+ // their subscriber counts — answers "what platform events does this org
7252
+ // publish?". Supply it for the subscriber list of one specific event.
7253
+ eventId: z29.string().min(1).optional(),
7203
7254
  limit: z29.number().int().min(1).max(EVENT_SUBSCRIBERS_MAX_LIMIT).optional()
7204
7255
  });
7205
7256
  validateEventId = (eventId) => {
@@ -7234,6 +7285,46 @@ var init_event_subscribers = __esm({
7234
7285
  };
7235
7286
  compareSubscribers2 = (a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
7236
7287
  eventSubscribersHandler = async (ctx, input2) => {
7288
+ const limit = input2.limit ?? EVENT_SUBSCRIBERS_DEFAULT_LIMIT;
7289
+ if (input2.eventId === void 0) {
7290
+ const nodesResult = await listNodesByType(ctx.graph, "CustomObject", {
7291
+ limit: EVENT_SUBSCRIBERS_MAX_LIMIT
7292
+ });
7293
+ if (!nodesResult.ok) {
7294
+ return err({ kind: "internal", message: `graph query failed: ${nodesResult.error.message}` });
7295
+ }
7296
+ const events = [];
7297
+ for (const node of nodesResult.value) {
7298
+ if (!node.apiName.endsWith(EVENT_API_NAME_SUFFIX))
7299
+ continue;
7300
+ const inEdges = await listEdges(ctx.graph, node.id, {
7301
+ direction: "in",
7302
+ edgeType: "listensTo"
7303
+ });
7304
+ if (!inEdges.ok) {
7305
+ return err({ kind: "internal", message: `graph query failed: ${inEdges.error.message}` });
7306
+ }
7307
+ let subscriberCount = 0;
7308
+ for (const edge of inEdges.value) {
7309
+ const sub = await getNodeById(ctx.graph, edge.fromId);
7310
+ if (!sub.ok) {
7311
+ return err({ kind: "internal", message: `graph query failed: ${sub.error.message}` });
7312
+ }
7313
+ if (sub.value !== null && SUBSCRIBER_NODE_TYPES2.has(sub.value.type)) {
7314
+ subscriberCount += 1;
7315
+ }
7316
+ }
7317
+ events.push({ eventId: node.id, eventApiName: node.apiName, subscriberCount });
7318
+ }
7319
+ events.sort((a, b) => a.eventId < b.eventId ? -1 : a.eventId > b.eventId ? 1 : 0);
7320
+ return ok({
7321
+ data: { subscribers: [], eventApiName: null, events: events.slice(0, limit) },
7322
+ vaultState: {
7323
+ sourceTreeHash: ctx.manifest.sourceTreeHash,
7324
+ refreshedAt: ctx.manifest.refreshedAt
7325
+ }
7326
+ });
7327
+ }
7237
7328
  const apiName = validateEventId(input2.eventId);
7238
7329
  if (apiName === null) {
7239
7330
  return err({
@@ -7242,7 +7333,6 @@ var init_event_subscribers = __esm({
7242
7333
  path: "eventId"
7243
7334
  });
7244
7335
  }
7245
- const limit = input2.limit ?? EVENT_SUBSCRIBERS_DEFAULT_LIMIT;
7246
7336
  const edgesResult = await listEdges(ctx.graph, input2.eventId, {
7247
7337
  direction: "in",
7248
7338
  edgeType: "listensTo"
@@ -7466,6 +7556,23 @@ var init_explain_apex_method = __esm({
7466
7556
  }
7467
7557
  });
7468
7558
 
7559
+ // ../mcp/dist/src/tools/phantom-node.js
7560
+ var phantomAwareNotFoundMessage;
7561
+ var init_phantom_node = __esm({
7562
+ "../mcp/dist/src/tools/phantom-node.js"() {
7563
+ "use strict";
7564
+ init_src();
7565
+ phantomAwareNotFoundMessage = async (ctx, id, kindLabel) => {
7566
+ const inbound = await listEdges(ctx.graph, id, { direction: "in" });
7567
+ const refs = inbound.ok ? inbound.value.length : 0;
7568
+ if (refs === 0) {
7569
+ return `no ${kindLabel} with id ${id}`;
7570
+ }
7571
+ return `\`${id}\` is referenced by ${refs} other component(s) in this org (e.g. code, tests, or permission grants) but its own ${kindLabel} definition was never retrieved into the vault \u2014 typically a managed-package component or one outside the retrieve scope. Run \`sfi refresh\` if it should be retrievable; otherwise treat it as external.`;
7572
+ };
7573
+ }
7574
+ });
7575
+
7469
7576
  // ../mcp/dist/src/tools/explain-field.js
7470
7577
  import { z as z31 } from "zod";
7471
7578
  var CUSTOM_FIELD_PREFIX, CUSTOM_METADATA_DEFINITION_SUFFIX, explainFieldInputSchema, readFieldType2, readFieldFormula, readFieldReferenceTo, readFieldDescription, readFieldInlineHelpText, readFieldRequired, readFieldLabel2, parentTypeApiName2, shouldIncludeRecordValues, readFieldApiName, findValueForField, compareRecordValues, collectRecordValues, explainFieldHandler;
@@ -7474,6 +7581,7 @@ var init_explain_field = __esm({
7474
7581
  "use strict";
7475
7582
  init_dist();
7476
7583
  init_src();
7584
+ init_phantom_node();
7477
7585
  CUSTOM_FIELD_PREFIX = "CustomField:";
7478
7586
  CUSTOM_METADATA_DEFINITION_SUFFIX = "__mdt";
7479
7587
  explainFieldInputSchema = z31.object({
@@ -7587,7 +7695,7 @@ var init_explain_field = __esm({
7587
7695
  if (node === null) {
7588
7696
  return err({
7589
7697
  kind: "component-not-found",
7590
- message: `no field with id ${input2.fieldId}`,
7698
+ message: await phantomAwareNotFoundMessage(ctx, input2.fieldId, "CustomField"),
7591
7699
  path: input2.fieldId
7592
7700
  });
7593
7701
  }
@@ -9906,7 +10014,7 @@ var init_code_quality_patterns = __esm({
9906
10014
  return [];
9907
10015
  const issues = [];
9908
10016
  const seen = /* @__PURE__ */ new Set();
9909
- const collect = (re, op, sobject) => {
10017
+ const collect2 = (re, op, sobject) => {
9910
10018
  const r = new RegExp(re.source, "g");
9911
10019
  let m;
9912
10020
  while ((m = r.exec(stripped)) !== null) {
@@ -9926,8 +10034,8 @@ var init_code_quality_patterns = __esm({
9926
10034
  });
9927
10035
  }
9928
10036
  };
9929
- collect(DML_STATEMENT_PATTERN, "dml", null);
9930
- collect(DML_DATABASE_CALL_PATTERN, "dml", null);
10037
+ collect2(DML_STATEMENT_PATTERN, "dml", null);
10038
+ collect2(DML_DATABASE_CALL_PATTERN, "dml", null);
9931
10039
  return issues;
9932
10040
  };
9933
10041
  INLINE_SOQL_PATTERN = /\[\s*SELECT\b([\s\S]*?)\]/gi;
@@ -10390,14 +10498,32 @@ var init_src4 = __esm({
10390
10498
 
10391
10499
  // ../mcp/dist/src/tools/field-access-audit.js
10392
10500
  import { z as z35 } from "zod";
10393
- var CUSTOM_FIELD_PREFIX3, PERMISSION_TYPE_VALUES, GRANTOR_TYPES, APEX_NODE_TYPES, fieldAccessAuditInputSchema, resolvePermissionLevel, permissionMatches, compareGrants, compareApexAccess, fieldAccessAuditHandler;
10501
+ var CUSTOM_FIELD_PREFIX3, synthesizeFieldNode, PERMISSION_TYPE_VALUES, GRANTOR_TYPES, APEX_NODE_TYPES, fieldAccessAuditInputSchema, resolvePermissionLevel, permissionMatches, compareGrants, compareApexAccess, fieldAccessAuditHandler;
10394
10502
  var init_field_access_audit = __esm({
10395
10503
  "../mcp/dist/src/tools/field-access-audit.js"() {
10396
10504
  "use strict";
10397
10505
  init_dist();
10398
10506
  init_src();
10399
10507
  init_src4();
10508
+ init_phantom_node();
10400
10509
  CUSTOM_FIELD_PREFIX3 = "CustomField:";
10510
+ synthesizeFieldNode = (fieldId) => {
10511
+ const afterPrefix = fieldId.slice(CUSTOM_FIELD_PREFIX3.length);
10512
+ const dot = afterPrefix.lastIndexOf(".");
10513
+ const apiName = dot >= 0 ? afterPrefix.slice(dot + 1) : afterPrefix;
10514
+ return {
10515
+ id: fieldId,
10516
+ type: "CustomField",
10517
+ apiName,
10518
+ label: null,
10519
+ parentId: dot >= 0 ? `CustomObject:${afterPrefix.slice(0, dot)}` : null,
10520
+ sourcePath: "",
10521
+ lastModifiedDate: null,
10522
+ lastModifiedBy: null,
10523
+ apiVersion: null,
10524
+ properties: {}
10525
+ };
10526
+ };
10401
10527
  PERMISSION_TYPE_VALUES = ["read", "edit", "all"];
10402
10528
  GRANTOR_TYPES = /* @__PURE__ */ new Set([
10403
10529
  "Profile",
@@ -10451,14 +10577,6 @@ var init_field_access_audit = __esm({
10451
10577
  });
10452
10578
  }
10453
10579
  const fieldNode = fieldResult.value;
10454
- if (fieldNode === null) {
10455
- return err({
10456
- kind: "component-not-found",
10457
- message: `no field with id ${fieldId}`,
10458
- path: fieldId
10459
- });
10460
- }
10461
- const detection = detectPiiClassificationWithReason(fieldNode);
10462
10580
  const grantedByResult = await listEdges(ctx.graph, fieldId, {
10463
10581
  direction: "in",
10464
10582
  edgeType: "grantedBy"
@@ -10478,6 +10596,16 @@ var init_field_access_audit = __esm({
10478
10596
  message: `graph query failed: ${allIncomingResult.error.message}`
10479
10597
  });
10480
10598
  }
10599
+ if (fieldNode === null && allIncomingResult.value.length === 0) {
10600
+ return err({
10601
+ kind: "component-not-found",
10602
+ message: await phantomAwareNotFoundMessage(ctx, fieldId, "CustomField"),
10603
+ path: fieldId
10604
+ });
10605
+ }
10606
+ const notModeled = fieldNode === null;
10607
+ const effectiveField = fieldNode ?? synthesizeFieldNode(fieldId);
10608
+ const detection = detectPiiClassificationWithReason(effectiveField);
10481
10609
  let profilesWithRead = 0;
10482
10610
  let profilesWithEdit = 0;
10483
10611
  let profilesWithUnknown = 0;
@@ -10559,7 +10687,11 @@ var init_field_access_audit = __esm({
10559
10687
  return ok({
10560
10688
  data: {
10561
10689
  fieldId,
10562
- fieldLabel: fieldNode.label ?? fieldNode.apiName,
10690
+ fieldLabel: effectiveField.label ?? effectiveField.apiName,
10691
+ notModeled,
10692
+ ...notModeled ? {
10693
+ notModeledNote: `\`${fieldId}\`'s own field definition was not retrieved into the vault \u2014 standard fields and managed-package fields are not modeled. The grants and Apex access below are read from the permission/usage edges (accurate); the field's data type, formula, and description are unavailable, and the PII classification is inferred from the field name alone.`
10694
+ } : {},
10563
10695
  piiClassification: detection.piiClassification,
10564
10696
  piiCategory: detection.piiCategory,
10565
10697
  grants: [...grants].sort(compareGrants),
@@ -10591,6 +10723,7 @@ var init_safe_to_delete_field = __esm({
10591
10723
  init_dist();
10592
10724
  init_src();
10593
10725
  init_src2();
10726
+ init_phantom_node();
10594
10727
  CUSTOM_FIELD_PREFIX4 = "CustomField:";
10595
10728
  EXAMPLES_PER_CATEGORY_LIMIT = 5;
10596
10729
  CATEGORY_ORDER = [
@@ -10799,10 +10932,49 @@ var init_safe_to_delete_field = __esm({
10799
10932
  });
10800
10933
  }
10801
10934
  if (nodeResult.value === null) {
10802
- return err({
10803
- kind: "component-not-found",
10804
- message: `no field with id ${fieldId}`,
10805
- path: fieldId
10935
+ const inboundResult = await listEdges(ctx.graph, fieldId, {
10936
+ direction: "in"
10937
+ });
10938
+ if (!inboundResult.ok) {
10939
+ return err({
10940
+ kind: "internal",
10941
+ message: `graph query failed: ${inboundResult.error.message}`
10942
+ });
10943
+ }
10944
+ if (inboundResult.value.length === 0) {
10945
+ return err({
10946
+ kind: "component-not-found",
10947
+ message: await phantomAwareNotFoundMessage(ctx, fieldId, "CustomField"),
10948
+ path: fieldId
10949
+ });
10950
+ }
10951
+ return ok({
10952
+ data: {
10953
+ fieldId,
10954
+ verdict: "review",
10955
+ reasoning: [
10956
+ {
10957
+ category: "unknown",
10958
+ verdict: "review",
10959
+ count: inboundResult.value.length,
10960
+ examples: [],
10961
+ note: `This field's own definition was not retrieved into the vault \u2014 standard fields and managed-package fields are not modeled. It is referenced by ${inboundResult.value.length} component(s)/grant(s). NOT proven safe to delete: a standard field cannot be deleted via metadata, and a not-modeled field cannot be fully assessed. Run \`sfi refresh\` if it should be retrievable, or treat it as external.`
10962
+ }
10963
+ ],
10964
+ trust: {
10965
+ provenance: "offline_snapshot",
10966
+ confidence: "declared",
10967
+ freshness: { snapshotRefreshedAt: ctx.manifest.refreshedAt },
10968
+ completeness: { status: "partial" },
10969
+ limitations: [
10970
+ "The field's own definition is not in the vault (standard or managed-package field); this verdict is based on inbound references only."
10971
+ ]
10972
+ }
10973
+ },
10974
+ vaultState: {
10975
+ sourceTreeHash: ctx.manifest.sourceTreeHash,
10976
+ refreshedAt: ctx.manifest.refreshedAt
10977
+ }
10806
10978
  });
10807
10979
  }
10808
10980
  if (nodeResult.value.properties["system"] === true) {
@@ -13293,6 +13465,11 @@ var init_find_dead_code = __esm({
13293
13465
  FALSE) AS is_own_entry_point
13294
13466
  FROM nodes
13295
13467
  WHERE type IN (${placeholders})
13468
+ -- Standard fields (api name has no __c suffix) are platform fields:
13469
+ -- not deletable and not "dead code". Only custom / managed-package
13470
+ -- fields (which end in __c) are valid CustomField dead-code candidates
13471
+ -- (NI-6: stops IsPartner/IsCustomerPortal/etc. being flagged dead).
13472
+ AND NOT (type = 'CustomField' AND api_name NOT LIKE '%\\_\\_c' ESCAPE '\\')
13296
13473
  ),
13297
13474
  incoming AS (
13298
13475
  SELECT e.to_id AS cid,
@@ -16150,7 +16327,7 @@ var init_generate_architecture_overview = __esm({
16150
16327
 
16151
16328
  // ../mcp/dist/src/tools/pii-inventory.js
16152
16329
  import { z as z60 } from "zod";
16153
- var PII_INVENTORY_MAX_LIMIT, PII_INVENTORY_DEFAULT_LIMIT, SCAN_PAGE_SIZE, CLASSIFICATION_FILTER_VALUES, CATEGORY_FILTER_VALUES, piiInventoryInputSchema, emptyClassificationCounts, emptyCategoryCounts, classificationMatches, categoryMatches, readDataType, readDescription, compareFields, fetchAllCustomFields2, piiInventoryHandler;
16330
+ var PII_INVENTORY_MAX_LIMIT, PII_INVENTORY_DEFAULT_LIMIT, SCAN_PAGE_SIZE, CLASSIFICATION_FILTER_VALUES, CATEGORY_FILTER_VALUES, piiInventoryInputSchema, PII_PAYLOAD_BUDGET_BYTES, fitFieldsToBudget, emptyClassificationCounts, emptyCategoryCounts, classificationMatches, categoryMatches, readDataType, readDescription, compareFields, fetchAllCustomFields2, piiInventoryHandler;
16154
16331
  var init_pii_inventory = __esm({
16155
16332
  "../mcp/dist/src/tools/pii-inventory.js"() {
16156
16333
  "use strict";
@@ -16171,8 +16348,23 @@ var init_pii_inventory = __esm({
16171
16348
  piiInventoryInputSchema = z60.object({
16172
16349
  classification: z60.enum(CLASSIFICATION_FILTER_VALUES).optional(),
16173
16350
  category: z60.enum(CATEGORY_FILTER_VALUES).optional(),
16174
- limit: z60.number().int().min(1).max(PII_INVENTORY_MAX_LIMIT).optional()
16351
+ limit: z60.number().int().min(1).max(PII_INVENTORY_MAX_LIMIT).optional(),
16352
+ offset: z60.number().int().min(0).optional()
16175
16353
  });
16354
+ PII_PAYLOAD_BUDGET_BYTES = 38e3;
16355
+ fitFieldsToBudget = (fields, budgetBytes) => {
16356
+ const kept = [];
16357
+ let used = 0;
16358
+ for (const field of fields) {
16359
+ const size = Buffer.byteLength(JSON.stringify(field), "utf8") + 1;
16360
+ if (kept.length > 0 && used + size > budgetBytes) {
16361
+ return { kept, trimmed: true };
16362
+ }
16363
+ kept.push(field);
16364
+ used += size;
16365
+ }
16366
+ return { kept, trimmed: false };
16367
+ };
16176
16368
  emptyClassificationCounts = () => ({
16177
16369
  pii: 0,
16178
16370
  sensitive: 0,
@@ -16259,17 +16451,26 @@ var init_pii_inventory = __esm({
16259
16451
  });
16260
16452
  }
16261
16453
  const sorted = [...matched].sort(compareFields);
16262
- const truncated = sorted.length > limit;
16263
- const fields = sorted.slice(0, limit);
16454
+ const offset = input2.offset ?? 0;
16455
+ const page = sorted.slice(offset, offset + limit);
16456
+ const { kept, trimmed } = fitFieldsToBudget(page, PII_PAYLOAD_BUDGET_BYTES);
16457
+ const returnedEnd = offset + kept.length;
16458
+ const truncated = returnedEnd < sorted.length;
16264
16459
  return ok({
16265
16460
  data: {
16266
- fields,
16461
+ fields: kept,
16267
16462
  summary: {
16268
16463
  total: matched.length,
16269
16464
  byClassification,
16270
16465
  byCategory
16271
16466
  },
16272
- truncated
16467
+ limit,
16468
+ offset,
16469
+ truncated,
16470
+ ...truncated ? { nextOffset: returnedEnd } : {},
16471
+ ...trimmed ? {
16472
+ note: `Response trimmed to ${kept.length} of ${page.length} matched fields to stay under the ~45 KB MCP response limit. Advance with offset += ${kept.length} for the rest.`
16473
+ } : {}
16273
16474
  },
16274
16475
  vaultState: {
16275
16476
  sourceTreeHash: ctx.manifest.sourceTreeHash,
@@ -16983,6 +17184,7 @@ var init_get_component = __esm({
16983
17184
  init_dist();
16984
17185
  init_src();
16985
17186
  init_src2();
17187
+ init_phantom_node();
16986
17188
  DEFAULT_COMPONENT_BODY_MAX_BYTES = 3e4;
16987
17189
  getComponentInputSchema = z64.object({
16988
17190
  id: z64.string().min(1),
@@ -16997,9 +17199,10 @@ var init_get_component = __esm({
16997
17199
  });
16998
17200
  }
16999
17201
  if (nodeResult.value === null) {
17202
+ const kindLabel = input2.id.includes(":") ? input2.id.slice(0, input2.id.indexOf(":")) : "component";
17000
17203
  return err({
17001
17204
  kind: "component-not-found",
17002
- message: `no node with id ${input2.id}`,
17205
+ message: await phantomAwareNotFoundMessage(ctx, input2.id, kindLabel),
17003
17206
  path: input2.id
17004
17207
  });
17005
17208
  }
@@ -17454,11 +17657,201 @@ var init_get_subgraph = __esm({
17454
17657
  }
17455
17658
  });
17456
17659
 
17660
+ // ../mcp/dist/src/knowledge-topics.js
17661
+ var TRAILHEAD, HELP, KNOWLEDGE_TOPICS, resolveTopicKey;
17662
+ var init_knowledge_topics = __esm({
17663
+ "../mcp/dist/src/knowledge-topics.js"() {
17664
+ "use strict";
17665
+ TRAILHEAD = { label: "Trailhead", url: "https://trailhead.salesforce.com" };
17666
+ HELP = { label: "Salesforce Help", url: "https://help.salesforce.com" };
17667
+ KNOWLEDGE_TOPICS = Object.freeze({
17668
+ "flow-vs-apex": {
17669
+ title: "Flow vs Apex \u2014 when to use which",
17670
+ summary: "Prefer declarative record-triggered Flow for most automation; reach for an Apex trigger only when you need logic Flow cannot do well: complex bulk processing, callouts with fine control, recursion management, custom error handling, or operations across many records/objects in one transaction. Keep one record-triggered Flow (or one trigger) per object/timing and delegate to a handler. Avoid mixing Workflow Rules / Process Builder with Flow on the same object.",
17671
+ docs: [
17672
+ { label: "Record-Triggered Automation (Architects)", url: "https://architect.salesforce.com/decision-guides/trigger-automation" },
17673
+ TRAILHEAD
17674
+ ]
17675
+ },
17676
+ "order-of-execution": {
17677
+ title: "Apex order of execution on save",
17678
+ summary: `On save Salesforce runs, in order: system validation, before-save record-triggered flows, before triggers, system + custom validation rules, duplicate rules, after triggers, assignment/auto-response/workflow rules, processes & after-save flows, escalation rules, roll-up summary recalculation, sharing recalculation, then commit and post-commit (async, email, @future). Knowing the order explains why a field looks stale or an automation "didn't fire".`,
17679
+ docs: [
17680
+ { label: "Apex Developer Guide \u2014 Triggers and Order of Execution", url: "https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_order_of_execution.htm" }
17681
+ ]
17682
+ },
17683
+ "governor-limits": {
17684
+ title: "Apex governor limits to design around",
17685
+ summary: "Per-transaction limits force bulk-safe design: 100 SOQL queries (200 async), 150 DML statements, 50,000 rows retrieved, 10s sync / 60s async CPU time, 6 MB sync / 12 MB async heap, 100 callouts, 10 emails. Never put SOQL/DML inside loops; bulkify; query selectively; move heavy work to Batch/Queueable.",
17686
+ docs: [
17687
+ { label: "Apex Developer Guide \u2014 Execution Governors and Limits", url: "https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_gov_limits.htm" }
17688
+ ]
17689
+ },
17690
+ "async-apex": {
17691
+ title: "Asynchronous Apex options",
17692
+ summary: "Future (@future): simplest fire-and-forget, primitives only, for callouts/decoupling \u2014 no chaining, no monitoring. Queueable: like future but accepts objects, can chain, returns a job id. Batch (Database.Batchable): process millions of records in chunks (start/execute/finish), higher limits. Schedulable: run Apex on a cron schedule (often to enqueue a Batch). Choose by data volume, need to chain/monitor, and scheduling.",
17693
+ docs: [
17694
+ { label: "Apex Developer Guide \u2014 Asynchronous Apex", url: "https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_async_overview.htm" },
17695
+ TRAILHEAD
17696
+ ]
17697
+ },
17698
+ "trigger-framework": {
17699
+ title: "Apex trigger framework pattern",
17700
+ summary: "Adopt one-trigger-per-object that immediately delegates to a handler class; keep zero business logic in the trigger body. A framework gives you context routing (before/after, insert/update/...), recursion control (static guards), and bulk-safe handler methods. Popular patterns: a simple hand-rolled handler base class, or community frameworks. Consistency matters more than which framework.",
17701
+ docs: [
17702
+ { label: "Apex Developer Guide \u2014 Triggers", url: "https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers.htm" },
17703
+ TRAILHEAD
17704
+ ]
17705
+ },
17706
+ "bulkification": {
17707
+ title: "Bulkified Apex best practices",
17708
+ summary: "Assume every trigger/method processes up to 200 records. Query once with maps keyed by id, never inside loops; collect DML into lists and do one insert/update outside loops; use collections and Maps for lookups; pass record collections, not single records. Test with \u2265200-record bulk data, not one record.",
17709
+ docs: [
17710
+ { label: "Apex Developer Guide \u2014 Bulk Trigger Idioms", url: "https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_triggers_bulk_idioms.htm" }
17711
+ ]
17712
+ },
17713
+ "apex-testing": {
17714
+ title: "Apex testing, coverage, and test data",
17715
+ summary: "Production deploys require \u226575% org-wide Apex coverage and all tests passing (each trigger must have some coverage). Coverage is a floor, not the goal: assert real behavior with System.assertEquals on distinct expected/actual values. Build test data in @isTest test-data-factory classes; use Test.startTest/stopTest for limits and async; avoid SeeAllData=true. Structure: one test class per class, descriptive methods, positive + negative + bulk cases.",
17716
+ docs: [
17717
+ { label: "Apex Developer Guide \u2014 Testing Apex", url: "https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing.htm" }
17718
+ ]
17719
+ },
17720
+ "apex-callouts": {
17721
+ title: "Making external callouts from Apex",
17722
+ summary: "Use Named Credentials for endpoints + auth (never hardcode URLs/secrets); call via HttpRequest/HttpResponse or external services. Callouts cannot follow uncommitted DML in the same transaction \u2014 do them before DML or from async (@future(callout=true)/Queueable). Set timeouts, handle non-200s, and write tests with HttpCalloutMock.",
17723
+ docs: [
17724
+ { label: "Apex Developer Guide \u2014 Invoking Callouts Using Apex", url: "https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts.htm" }
17725
+ ]
17726
+ },
17727
+ "sfdx-source-driven-dev": {
17728
+ title: "Source-driven development with SFDX",
17729
+ summary: "Treat metadata as source in version control. Use a Salesforce DX project (sfdx-project.json), scratch orgs for feature development, and the `sf` CLI to retrieve/deploy/convert source. Pair with a CI/CD pipeline (validate + run tests on PR, deploy on merge) and unlocked packages for modular delivery.",
17730
+ docs: [
17731
+ { label: "Salesforce DX Developer Guide", url: "https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_setup_intro.htm" },
17732
+ TRAILHEAD
17733
+ ]
17734
+ },
17735
+ "package-strategy": {
17736
+ title: "Unlocked packages & modular structure",
17737
+ summary: "Decompose the org into unlocked packages aligned to functional domains, each with a clear dependency direction (no cycles). Packages give versioning, clean install/upgrade, and ownership boundaries. Start with a small number of coarse packages; split as ownership and dependencies clarify. Keep a base/common package for shared components.",
17738
+ docs: [
17739
+ { label: "SFDX \u2014 Unlocked Packages", url: "https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_unlocked_pkg_intro.htm" },
17740
+ TRAILHEAD
17741
+ ]
17742
+ },
17743
+ "profiles-vs-permission-sets": {
17744
+ title: "Profiles vs Permission Sets",
17745
+ summary: 'Modern best practice: keep profiles minimal (login hours, default record types, page layouts as needed) and grant capabilities via Permission Sets and Permission Set Groups. This avoids "profile proliferation", makes access additive and auditable, and lets you assign least-privilege bundles per role. Salesforce is moving permissions toward permission sets.',
17746
+ docs: [HELP, TRAILHEAD]
17747
+ },
17748
+ "owd-sharing-model": {
17749
+ title: "OWD & the sharing model (greenfield)",
17750
+ summary: "Set Org-Wide Defaults to the most restrictive level each object needs (Private / Public Read Only / Public Read-Write), then OPEN UP access with the role hierarchy, sharing rules (ownership- or criteria-based), permission sets (View All/Modify All), teams, and manual/Apex sharing. Design least-privilege: start Private and grant deliberately. Plan sharing recalculation cost for large data volumes.",
17751
+ docs: [HELP, TRAILHEAD]
17752
+ },
17753
+ "standard-vs-custom-objects": {
17754
+ title: "Standard vs custom objects",
17755
+ summary: "Use standard objects (Account, Contact, Opportunity, Case, Lead, ...) whenever they fit \u2014 they come with built-in features, reports, and integrations. Create custom objects only for concepts standard objects do not represent. Prefer extending a standard object with custom fields over cloning it. Consider the data model, relationships, and license implications before adding custom objects.",
17756
+ docs: [HELP, TRAILHEAD]
17757
+ },
17758
+ "naming-conventions": {
17759
+ title: "Naming & documentation conventions",
17760
+ summary: "Establish conventions from day one: consistent API names (PascalCase objects, clear field suffixes), prefixes for app/domain, description + help-text on every field, and a documented automation/trigger naming scheme. Conventions make the org self-describing and reduce technical debt. Record them in a living standards doc.",
17761
+ docs: [TRAILHEAD, HELP]
17762
+ },
17763
+ "sandbox-environment-strategy": {
17764
+ title: "Sandbox & environment strategy",
17765
+ summary: "Use a tiered environment path: developer/scratch orgs for build, an integration/partial sandbox for merged work, a full or partial sandbox for UAT/staging, then production. Match sandbox type to need (Developer/Developer Pro for config/code, Partial/Full for data-dependent testing). Automate refresh + seeding and gate promotion with CI tests.",
17766
+ docs: [HELP, TRAILHEAD]
17767
+ },
17768
+ "single-vs-multi-org": {
17769
+ title: "Single-org vs multi-org strategy",
17770
+ summary: "Prefer a single org when business units share processes, data, and customers \u2014 it maximizes the 360\xB0 view and minimizes integration/duplication cost. Choose multi-org when regulatory isolation, radically different processes, divestiture risk, or per-region governance demand hard boundaries. Weigh org limits, governance maturity, and the cost of cross-org integration before splitting.",
17771
+ docs: [{ label: "Architect decision guides", url: "https://architect.salesforce.com/decision-guides" }, TRAILHEAD]
17772
+ },
17773
+ "data-retention-archiving": {
17774
+ title: "Data retention & archiving strategy",
17775
+ summary: "Plan retention up front: classify data by regulatory/operational need, define how long each object is kept hot, and archive or purge the rest. Options include Big Objects for low-cost archival, scheduled batch purges, external/offline archives, and field-history/event-log retention settings. Designing this early prevents storage-limit and LDV pain later.",
17776
+ docs: [HELP, TRAILHEAD]
17777
+ },
17778
+ "large-data-volumes": {
17779
+ title: "Large data volumes (LDV) planning",
17780
+ summary: "At millions of records, design for selectivity: indexed/selective filters, skinny tables (via Support) for hot reports, careful sharing (ownership/data skew avoidance), Big Objects for archival, and bulk/async data loads. Watch for non-selective SOQL, account/ownership skew, and sharing-recalculation cost. Plan partitioning and reporting strategy before volume arrives.",
17781
+ docs: [{ label: "LDV Best Practices (Architects)", url: "https://architect.salesforce.com/well-architected/adaptable/resilient" }, TRAILHEAD]
17782
+ },
17783
+ "release-management": {
17784
+ title: "Release management & deployment pipeline",
17785
+ summary: "Establish a source-of-truth VCS, environment promotion path (dev \u2192 integration \u2192 UAT \u2192 prod), and a CI/CD pipeline that validates + runs Apex tests on every change and deploys on merge. Use unlocked packages or change sets/metadata API, automate with the `sf` CLI or a DevOps tool, and keep deployments small and frequent. Gate prod with required coverage + review.",
17786
+ docs: [TRAILHEAD, { label: "Salesforce DX Developer Guide", url: "https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_setup_intro.htm" }]
17787
+ },
17788
+ "well-architected": {
17789
+ title: "Salesforce Well-Architected principles",
17790
+ summary: "Salesforce Well-Architected frames a healthy org as Trusted (secure, compliant, reliable), Easy (intentional, automated, resilient \u2014 minimal complexity/debt), and Adaptable (composable, scalable). Use it as a checklist when designing: least-privilege security, consolidated automation, documented conventions, modular packaging, and scalable data design.",
17791
+ docs: [{ label: "Salesforce Well-Architected", url: "https://architect.salesforce.com/well-architected/overview" }, TRAILHEAD]
17792
+ },
17793
+ "license-types": {
17794
+ title: "License types & planning",
17795
+ summary: "Match license types to user needs: full Salesforce/CRM licenses for internal power users, Platform licenses for users who only need custom apps + a few standard objects, and Experience Cloud licenses for external community users. Permission Set Licenses add capabilities on top. Count licenses by persona early \u2014 they drive cost and constrain object/feature access.",
17796
+ docs: [HELP, TRAILHEAD]
17797
+ },
17798
+ "integration-patterns": {
17799
+ title: "Integration patterns",
17800
+ summary: "Pick the pattern by need: Request-Reply (synchronous UI callouts), Fire-and-Forget (Platform Events / outbound), Batch Data Sync (scheduled bulk), Remote Call-In (inbound REST/SOAP API), and UI Update from external (streaming/CDC). Prefer middleware/event-driven over point-to-point at scale; use Named Credentials for auth and design idempotent retries + error handling.",
17801
+ docs: [{ label: "Integration Patterns (Architects)", url: "https://architect.salesforce.com/decision-guides/integrate-salesforce" }, TRAILHEAD]
17802
+ }
17803
+ });
17804
+ resolveTopicKey = (topic) => {
17805
+ const t = topic.trim().toLowerCase();
17806
+ if (t.length === 0)
17807
+ return null;
17808
+ const keys = Object.keys(KNOWLEDGE_TOPICS);
17809
+ if (keys.includes(t))
17810
+ return t;
17811
+ const hit = keys.find((k) => k.includes(t) || t.includes(k) || KNOWLEDGE_TOPICS[k].title.toLowerCase().includes(t));
17812
+ return hit ?? null;
17813
+ };
17814
+ }
17815
+ });
17816
+
17817
+ // ../mcp/dist/src/tools/guidance.js
17818
+ import { z as z68 } from "zod";
17819
+ var guidanceInputSchema, DISCLOSURE6, guidanceHandler;
17820
+ var init_guidance = __esm({
17821
+ "../mcp/dist/src/tools/guidance.js"() {
17822
+ "use strict";
17823
+ init_dist();
17824
+ init_knowledge_topics();
17825
+ guidanceInputSchema = z68.object({
17826
+ topic: z68.string().min(1).optional()
17827
+ });
17828
+ DISCLOSURE6 = "General Salesforce best-practice guidance with links to official docs \u2014 NOT specific to this org or its metadata. For org-specific answers, use the vault tools (schema, automation, permissions, impact) or the opt-in live plane.";
17829
+ guidanceHandler = async (ctx, input2) => {
17830
+ const availableTopics = Object.entries(KNOWLEDGE_TOPICS).map(([key2, t]) => ({ key: key2, title: t.title }));
17831
+ const key = input2.topic !== void 0 ? resolveTopicKey(input2.topic) : null;
17832
+ const guidance = key !== null ? KNOWLEDGE_TOPICS[key] ?? null : null;
17833
+ return ok({
17834
+ data: {
17835
+ topic: key,
17836
+ guidance,
17837
+ matched: guidance !== null,
17838
+ availableTopics,
17839
+ disclosure: DISCLOSURE6
17840
+ },
17841
+ vaultState: {
17842
+ sourceTreeHash: ctx.manifest.sourceTreeHash,
17843
+ refreshedAt: ctx.manifest.refreshedAt
17844
+ }
17845
+ });
17846
+ };
17847
+ }
17848
+ });
17849
+
17457
17850
  // ../mcp/dist/src/tools/health-check.js
17458
17851
  import { existsSync as existsSync3 } from "node:fs";
17459
17852
  import { stat as stat4 } from "node:fs/promises";
17460
17853
  import { join as join7 } from "node:path";
17461
- import { z as z68 } from "zod";
17854
+ import { z as z69 } from "zod";
17462
17855
  var SKIPPED_FILES_DEGRADED_THRESHOLD, healthCheckInputSchema, probeGraph, probeSourceHash, probeRenderComplete, healthCheckHandler;
17463
17856
  var init_health_check = __esm({
17464
17857
  "../mcp/dist/src/tools/health-check.js"() {
@@ -17467,7 +17860,7 @@ var init_health_check = __esm({
17467
17860
  init_src();
17468
17861
  init_src2();
17469
17862
  SKIPPED_FILES_DEGRADED_THRESHOLD = 100;
17470
- healthCheckInputSchema = z68.object({});
17863
+ healthCheckInputSchema = z69.object({});
17471
17864
  probeGraph = async (ctx) => {
17472
17865
  try {
17473
17866
  const result = await listNodesByType(ctx.graph, "CustomObject", { limit: 1 });
@@ -17587,7 +17980,7 @@ var init_health_check = __esm({
17587
17980
  // ../mcp/dist/src/tools/integration-procedure-chain.js
17588
17981
  import { readFile as readFile10 } from "node:fs/promises";
17589
17982
  import { XMLParser as XMLParser3, XMLValidator as XMLValidator3 } from "fast-xml-parser";
17590
- import { z as z69 } from "zod";
17983
+ import { z as z70 } from "zod";
17591
17984
  var IP_PREFIX, DATA_TRANSFORM_PREFIX, ROOT_ELEMENT3, DATARAPTOR_ACTION_TYPES, REST_ACTION_TYPE, NESTED_IP_ACTION_TYPE, REMOTE_ACTION_TYPE, RESPONSE_ACTION_TYPE, NATIVE_VS_VLOCITY_DISCLOSURE2, APEX_COUPLING_DEFERRAL_DISCLOSURE, RECORD_LEVEL_BOUNDARY_DISCLOSURE, REST_REACHABILITY_DISCLOSURE, integrationProcedureChainInputSchema, unwrapSingle3, toArray3, coerceBoolean2, coerceNumber, nonEmptyString, parsePropertySetConfig, BOUNDARIES_VERBATIM, integrationProcedureChainHandler, walkActions, resolveExternalEndpoints, readNodeStringProperty, readNodeNumberProperty, readNodeBooleanProperty;
17592
17985
  var init_integration_procedure_chain = __esm({
17593
17986
  "../mcp/dist/src/tools/integration-procedure-chain.js"() {
@@ -17611,9 +18004,9 @@ var init_integration_procedure_chain = __esm({
17611
18004
  APEX_COUPLING_DEFERRAL_DISCLOSURE = "v3.2 captures OmniStudio components and intra-OmniStudio call chains (`dispatchesOmniAction`). The Apex-to-OmniProcess coupling (`implements omnistudio.VlocityOpenInterface` etc.) is a v3.3 follow-up \u2014 those edges are NOT yet in the graph.";
17612
18005
  RECORD_LEVEL_BOUNDARY_DISCLOSURE = "v3.2 walks the OmniScript / IP / Card metadata XML. The actual user-entered data and runtime state lives in OmniProcessElement and related SObject records; that is record-level data, out of scope for v0.1's read-the-metadata posture.";
17613
18006
  REST_REACHABILITY_DISCLOSURE = "REST endpoint URLs are surfaced with `parsed` confidence (from the propertySetConfig JSON blob); v3.2 does NOT probe the URL, verify the endpoint is reachable, or resolve the Named Credential against live state.";
17614
- integrationProcedureChainInputSchema = z69.object({
17615
- integrationProcedureId: z69.string().min(1),
17616
- includeChildPropertySetConfig: z69.boolean().optional()
18007
+ integrationProcedureChainInputSchema = z70.object({
18008
+ integrationProcedureId: z70.string().min(1),
18009
+ includeChildPropertySetConfig: z70.boolean().optional()
17617
18010
  });
17618
18011
  unwrapSingle3 = (value) => Array.isArray(value) ? value[0] : value;
17619
18012
  toArray3 = (value) => {
@@ -17915,7 +18308,7 @@ var init_integration_procedure_chain = __esm({
17915
18308
  });
17916
18309
 
17917
18310
  // ../mcp/dist/src/tools/last-modified.js
17918
- import { z as z70 } from "zod";
18311
+ import { z as z71 } from "zod";
17919
18312
  var LAST_MODIFIED_UNENRICHED_DISCLOSURE, LAST_MODIFIED_ENRICHED_DISCLOSURE, lastModifiedInputSchema, extractLastModifiedDate2, extractLastModifiedBy2, extractApiVersion, lastModifiedHandler;
17920
18313
  var init_last_modified = __esm({
17921
18314
  "../mcp/dist/src/tools/last-modified.js"() {
@@ -17924,8 +18317,8 @@ var init_last_modified = __esm({
17924
18317
  init_src();
17925
18318
  LAST_MODIFIED_UNENRICHED_DISCLOSURE = "v1.7 Tooling API enrichment has not run for this vault. Run `sfi refresh --with-tooling-api --target-org <alias>` to populate lastModifiedDate / lastModifiedBy / apiVersion for the enriched types.";
17926
18319
  LAST_MODIFIED_ENRICHED_DISCLOSURE = "Freshness fields populated. lastModifiedDate / lastModifiedBy reflect the Tooling API at enrichment time (or the DX-source extractor when the type carries a `<lastModifiedDate>` element).";
17927
- lastModifiedInputSchema = z70.object({
17928
- componentId: z70.string().min(1)
18320
+ lastModifiedInputSchema = z71.object({
18321
+ componentId: z71.string().min(1)
17929
18322
  });
17930
18323
  extractLastModifiedDate2 = (legacy, properties) => {
17931
18324
  const propsValue = properties["lastModifiedDate"];
@@ -18001,17 +18394,17 @@ var init_last_modified = __esm({
18001
18394
  });
18002
18395
 
18003
18396
  // ../mcp/dist/src/tools/layout-for-user.js
18004
- import { z as z71 } from "zod";
18397
+ import { z as z72 } from "zod";
18005
18398
  var layoutForUserInputSchema, step, evaluateProfileLookup, readLayoutAssignments, layoutTargetsObject, canonicaliseLayoutId, findLayoutAssignment, pickFlexiPageForObject, evaluateLightningPageLookup, layoutForUserHandler;
18006
18399
  var init_layout_for_user = __esm({
18007
18400
  "../mcp/dist/src/tools/layout-for-user.js"() {
18008
18401
  "use strict";
18009
18402
  init_dist();
18010
18403
  init_src();
18011
- layoutForUserInputSchema = z71.object({
18012
- objectApiName: z71.string().min(1),
18013
- recordTypeId: z71.string().min(1).optional(),
18014
- profileId: z71.string().min(1)
18404
+ layoutForUserInputSchema = z72.object({
18405
+ objectApiName: z72.string().min(1),
18406
+ recordTypeId: z72.string().min(1).optional(),
18407
+ profileId: z72.string().min(1)
18015
18408
  });
18016
18409
  step = (stage, verdict, reason, value) => value === void 0 ? { stage, verdict, reason } : { stage, verdict, reason, value };
18017
18410
  evaluateProfileLookup = async (ctx, profileId) => {
@@ -18229,7 +18622,7 @@ var init_layout_for_user = __esm({
18229
18622
  });
18230
18623
 
18231
18624
  // ../mcp/dist/src/tools/list-components.js
18232
- import { z as z72 } from "zod";
18625
+ import { z as z73 } from "zod";
18233
18626
  var COMPONENT_TYPES2, LIST_MAX_LIMIT2, LIST_DEFAULT_LIMIT2, LIST_PAYLOAD_BUDGET_BYTES, fitNodesToBudget, listComponentsInputSchema, listComponentsHandler;
18234
18627
  var init_list_components = __esm({
18235
18628
  "../mcp/dist/src/tools/list-components.js"() {
@@ -18338,11 +18731,11 @@ var init_list_components = __esm({
18338
18731
  }
18339
18732
  return { kept, trimmed: false };
18340
18733
  };
18341
- listComponentsInputSchema = z72.object({
18342
- type: z72.enum(COMPONENT_TYPES2).optional(),
18343
- parentId: z72.string().min(1).optional(),
18344
- limit: z72.number().int().min(1).max(LIST_MAX_LIMIT2).optional(),
18345
- offset: z72.number().int().min(0).optional()
18734
+ listComponentsInputSchema = z73.object({
18735
+ type: z73.enum(COMPONENT_TYPES2).optional(),
18736
+ parentId: z73.string().min(1).optional(),
18737
+ limit: z73.number().int().min(1).max(LIST_MAX_LIMIT2).optional(),
18738
+ offset: z73.number().int().min(0).optional()
18346
18739
  });
18347
18740
  listComponentsHandler = async (ctx, input2) => {
18348
18741
  if (input2.type === void 0) {
@@ -18969,7 +19362,7 @@ var init_live_consent = __esm({
18969
19362
  // ../mcp/dist/src/tools/live-plane.js
18970
19363
  import { execFile as execFile2 } from "node:child_process";
18971
19364
  import { promisify as promisify2 } from "node:util";
18972
- import { z as z73 } from "zod";
19365
+ import { z as z74 } from "zod";
18973
19366
  var nodeExecFile2, redactSecrets, LIVE_PLANE_DISCLOSURE, MAX_SAMPLE_ROWS, liveEnabledSchema, isLivePlaneEnabled, liveTrust, resolveOrg, resolveLiveAccess, liveConsentRequiredError, gateLive, getLiveAuth, runSfJson, apiPath, restGet, liveDescribeInputSchema, liveDescribeHandler, liveCountInputSchema, OBJECT_API_NAME_RE, resolveCountSoql, assertCountSoql, liveCountHandler, liveSampleInputSchema, capSampleSoql, liveSampleHandler, liveFieldPopulationInputSchema, liveFieldPopulationHandler, liveOrgLimitsInputSchema, liveOrgLimitsHandler, MAX_INACTIVE_USER_ROWS, DEFAULT_INACTIVE_DAYS, MS_PER_DAY, liveInactiveUsersInputSchema, soqlDateTime, liveInactiveUsersHandler, DEFAULT_LICENSE_INACTIVE_DAYS, MAX_RECLAIM_ROWS, LICENSE_USAGE_DISCLOSURE, liveLicenseUsageInputSchema, toUtilization, renderLicenseUsageMarkdown, liveLicenseUsageHandler, liveConsentInputSchema, consentTrust, liveConsentHandler, liveQuery, MAX_DETAIL_ROWS, daysAgoSoql, daysSince, livePlaneVaultState, UNAVAILABLE_ERROR, assertSoqlIdentifier, soqlLiteral, MAX_GROUP_BUCKETS, DEFAULT_STALE_DAYS, DEFAULT_RECENT_DAYS, liveGroupCountInputSchema, liveGroupCountHandler, liveStaleRecordsInputSchema, liveStaleRecordsHandler, liveRecentActivityInputSchema, liveRecentActivityHandler, buildEqualityWhere, aggregateCountFromRow, liveAggregateInputSchema, liveAggregateHandler, MAX_DUPLICATE_GROUPS, liveDuplicateCheckInputSchema, liveDuplicateCheckHandler, MAX_OWNER_BUCKETS, liveOwnerBreakdownInputSchema, liveOwnerBreakdownHandler, DEFAULT_REPORT_STALE_DAYS, liveReportUsageInputSchema, liveReportUsageHandler, liveFolderAccessInputSchema, liveFolderAccessHandler, DEFAULT_TEMPLATE_STALE_DAYS, CLASSIC_TEMPLATE_TYPES, liveEmailTemplateUsageInputSchema, liveEmailTemplateUsageHandler, DEFAULT_HEALTH_DAYS, LIMIT_RISK_THRESHOLD, liveOrgHealthInputSchema, liveOrgHealthHandler, liveStorageByObjectInputSchema, liveStorageByObjectHandler, liveDataSkewInputSchema, liveSetupAuditTrailInputSchema, liveSecurityExposureInputSchema;
18974
19367
  var init_live_plane = __esm({
18975
19368
  "../mcp/dist/src/tools/live-plane.js"() {
@@ -18982,8 +19375,8 @@ var init_live_plane = __esm({
18982
19375
  redactSecrets = (message) => message.replace(/Bearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [REDACTED]").replace(/\b00D[A-Za-z0-9]{12,}![A-Za-z0-9._~+/=-]{20,}\b/g, "[REDACTED_TOKEN]");
18983
19376
  LIVE_PLANE_DISCLOSURE = "Live org data is read-only, queried at call time via the Salesforce CLI. It does not update the vault. Enable with SFI_LIVE_PLANE_ENABLED=1 or pass liveEnabled: true.";
18984
19377
  MAX_SAMPLE_ROWS = 200;
18985
- liveEnabledSchema = z73.object({
18986
- liveEnabled: z73.boolean().optional()
19378
+ liveEnabledSchema = z74.object({
19379
+ liveEnabled: z74.boolean().optional()
18987
19380
  });
18988
19381
  isLivePlaneEnabled = (input2) => {
18989
19382
  if (input2 === true)
@@ -19061,8 +19454,8 @@ var init_live_plane = __esm({
19061
19454
  }
19062
19455
  };
19063
19456
  liveDescribeInputSchema = liveEnabledSchema.extend({
19064
- objectApiName: z73.string().min(1),
19065
- orgAlias: z73.string().min(1).optional()
19457
+ objectApiName: z74.string().min(1),
19458
+ orgAlias: z74.string().min(1).optional()
19066
19459
  });
19067
19460
  liveDescribeHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
19068
19461
  const gate = await gateLive(ctx, input2);
@@ -19090,9 +19483,9 @@ var init_live_plane = __esm({
19090
19483
  // Either `soql` (a SELECT COUNT() query) OR `objectApiName` (count every row
19091
19484
  // of that object). Both optional at the schema level; the handler requires
19092
19485
  // exactly one and turns objectApiName into `SELECT COUNT() FROM <object>`.
19093
- soql: z73.string().min(1).optional(),
19094
- objectApiName: z73.string().min(1).optional(),
19095
- orgAlias: z73.string().min(1).optional()
19486
+ soql: z74.string().min(1).optional(),
19487
+ objectApiName: z74.string().min(1).optional(),
19488
+ orgAlias: z74.string().min(1).optional()
19096
19489
  });
19097
19490
  OBJECT_API_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
19098
19491
  resolveCountSoql = (input2) => {
@@ -19156,9 +19549,9 @@ var init_live_plane = __esm({
19156
19549
  });
19157
19550
  };
19158
19551
  liveSampleInputSchema = liveEnabledSchema.extend({
19159
- soql: z73.string().min(1),
19160
- limit: z73.number().int().min(1).max(MAX_SAMPLE_ROWS).optional(),
19161
- orgAlias: z73.string().min(1).optional()
19552
+ soql: z74.string().min(1),
19553
+ limit: z74.number().int().min(1).max(MAX_SAMPLE_ROWS).optional(),
19554
+ orgAlias: z74.string().min(1).optional()
19162
19555
  });
19163
19556
  capSampleSoql = (soql, limit) => {
19164
19557
  const trimmed = soql.trim().replace(/;\s*$/, "");
@@ -19195,9 +19588,9 @@ var init_live_plane = __esm({
19195
19588
  });
19196
19589
  };
19197
19590
  liveFieldPopulationInputSchema = liveEnabledSchema.extend({
19198
- objectApiName: z73.string().min(1),
19199
- fieldApiName: z73.string().min(1),
19200
- orgAlias: z73.string().min(1).optional()
19591
+ objectApiName: z74.string().min(1),
19592
+ fieldApiName: z74.string().min(1),
19593
+ orgAlias: z74.string().min(1).optional()
19201
19594
  });
19202
19595
  liveFieldPopulationHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
19203
19596
  const gate = await gateLive(ctx, input2);
@@ -19243,7 +19636,7 @@ var init_live_plane = __esm({
19243
19636
  });
19244
19637
  };
19245
19638
  liveOrgLimitsInputSchema = liveEnabledSchema.extend({
19246
- orgAlias: z73.string().min(1).optional()
19639
+ orgAlias: z74.string().min(1).optional()
19247
19640
  });
19248
19641
  liveOrgLimitsHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
19249
19642
  const gate = await gateLive(ctx, input2);
@@ -19274,14 +19667,14 @@ var init_live_plane = __esm({
19274
19667
  liveInactiveUsersInputSchema = liveEnabledSchema.extend({
19275
19668
  /** Inactivity threshold in days (default 30). A user is "inactive" if their
19276
19669
  * last login is older than this — or they have never logged in. */
19277
- days: z73.number().int().min(1).max(3650).optional(),
19670
+ days: z74.number().int().min(1).max(3650).optional(),
19278
19671
  /** Include non-Standard user types (integration/system/etc.). Default false
19279
19672
  * → only human (Standard) users, the usual intent of "who hasn't logged in". */
19280
- includeAllUserTypes: z73.boolean().optional(),
19673
+ includeAllUserTypes: z74.boolean().optional(),
19281
19674
  /** Max detail rows returned (default + hard cap 500). The TOTAL count is
19282
19675
  * always reported separately, so a capped list never understates the count. */
19283
- limit: z73.number().int().min(1).max(MAX_INACTIVE_USER_ROWS).optional(),
19284
- orgAlias: z73.string().min(1).optional()
19676
+ limit: z74.number().int().min(1).max(MAX_INACTIVE_USER_ROWS).optional(),
19677
+ orgAlias: z74.string().min(1).optional()
19285
19678
  });
19286
19679
  soqlDateTime = (d) => d.toISOString().replace(/\.\d{3}Z$/, "Z");
19287
19680
  liveInactiveUsersHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
@@ -19347,10 +19740,10 @@ var init_live_plane = __esm({
19347
19740
  LICENSE_USAGE_DISCLOSURE = 'License counts are live UserLicense / PermissionSetLicense state. "Reclaimable seats" is a PROXY \u2014 it groups active users who have not logged in within the window by their license; it does NOT measure actual feature usage, and some dormant seats are held intentionally (seasonal staff, service/integration accounts mis-typed as Standard, compliance holds). Per-feature-license usage (Marketing User, Knowledge User, etc.) is NOT covered. This tool is READ-ONLY: it never deprovisions or reassigns a license \u2014 verify each seat before reclaiming it.';
19348
19741
  liveLicenseUsageInputSchema = liveEnabledSchema.extend({
19349
19742
  /** Dormancy window for reclaimable seats, in days (default 90). */
19350
- inactiveDays: z73.number().int().min(1).max(3650).optional(),
19743
+ inactiveDays: z74.number().int().min(1).max(3650).optional(),
19351
19744
  /** Max reclaimable-seat groups returned (default + hard cap 200). */
19352
- limit: z73.number().int().min(1).max(MAX_RECLAIM_ROWS).optional(),
19353
- orgAlias: z73.string().min(1).optional()
19745
+ limit: z74.number().int().min(1).max(MAX_RECLAIM_ROWS).optional(),
19746
+ orgAlias: z74.string().min(1).optional()
19354
19747
  });
19355
19748
  toUtilization = (rows, nameKey) => rows.map((r) => {
19356
19749
  const total = Number(r.TotalLicenses ?? 0);
@@ -19438,13 +19831,13 @@ var init_live_plane = __esm({
19438
19831
  }
19439
19832
  });
19440
19833
  };
19441
- liveConsentInputSchema = z73.object({
19834
+ liveConsentInputSchema = z74.object({
19442
19835
  /** Org alias/username; defaults to the vault's source org. */
19443
- orgAlias: z73.string().min(1).optional(),
19836
+ orgAlias: z74.string().min(1).optional(),
19444
19837
  /** Grant standing consent for the org (persists across sessions). */
19445
- grant: z73.boolean().optional(),
19838
+ grant: z74.boolean().optional(),
19446
19839
  /** Revoke standing consent for the org. */
19447
- revoke: z73.boolean().optional()
19840
+ revoke: z74.boolean().optional()
19448
19841
  });
19449
19842
  consentTrust = () => ({
19450
19843
  provenance: "offline_snapshot",
@@ -19531,12 +19924,12 @@ var init_live_plane = __esm({
19531
19924
  DEFAULT_STALE_DAYS = 90;
19532
19925
  DEFAULT_RECENT_DAYS = 7;
19533
19926
  liveGroupCountInputSchema = liveEnabledSchema.extend({
19534
- objectApiName: z73.string().min(1),
19535
- groupByField: z73.string().min(1),
19536
- limit: z73.number().int().min(1).max(MAX_GROUP_BUCKETS).optional(),
19537
- filterField: z73.string().min(1).optional(),
19538
- filterValue: z73.union([z73.string(), z73.number(), z73.boolean()]).optional(),
19539
- orgAlias: z73.string().min(1).optional()
19927
+ objectApiName: z74.string().min(1),
19928
+ groupByField: z74.string().min(1),
19929
+ limit: z74.number().int().min(1).max(MAX_GROUP_BUCKETS).optional(),
19930
+ filterField: z74.string().min(1).optional(),
19931
+ filterValue: z74.union([z74.string(), z74.number(), z74.boolean()]).optional(),
19932
+ orgAlias: z74.string().min(1).optional()
19540
19933
  });
19541
19934
  liveGroupCountHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
19542
19935
  const gate = await gateLive(ctx, input2);
@@ -19604,12 +19997,12 @@ ${renderTrustFooter(trust)}`;
19604
19997
  });
19605
19998
  };
19606
19999
  liveStaleRecordsInputSchema = liveEnabledSchema.extend({
19607
- objectApiName: z73.string().min(1),
19608
- staleDays: z73.number().int().min(1).max(3650).optional(),
19609
- dateField: z73.string().min(1).optional(),
19610
- includeNeverSet: z73.boolean().optional(),
19611
- limit: z73.number().int().min(1).max(MAX_SAMPLE_ROWS).optional(),
19612
- orgAlias: z73.string().min(1).optional()
20000
+ objectApiName: z74.string().min(1),
20001
+ staleDays: z74.number().int().min(1).max(3650).optional(),
20002
+ dateField: z74.string().min(1).optional(),
20003
+ includeNeverSet: z74.boolean().optional(),
20004
+ limit: z74.number().int().min(1).max(MAX_SAMPLE_ROWS).optional(),
20005
+ orgAlias: z74.string().min(1).optional()
19613
20006
  });
19614
20007
  liveStaleRecordsHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
19615
20008
  const gate = await gateLive(ctx, input2);
@@ -19671,11 +20064,11 @@ ${renderTrustFooter(trust)}`;
19671
20064
  });
19672
20065
  };
19673
20066
  liveRecentActivityInputSchema = liveEnabledSchema.extend({
19674
- objectApiName: z73.string().min(1),
19675
- days: z73.number().int().min(1).max(365).optional(),
19676
- activity: z73.enum(["created", "modified", "both"]).optional(),
19677
- limit: z73.number().int().min(1).max(MAX_SAMPLE_ROWS).optional(),
19678
- orgAlias: z73.string().min(1).optional()
20067
+ objectApiName: z74.string().min(1),
20068
+ days: z74.number().int().min(1).max(365).optional(),
20069
+ activity: z74.enum(["created", "modified", "both"]).optional(),
20070
+ limit: z74.number().int().min(1).max(MAX_SAMPLE_ROWS).optional(),
20071
+ orgAlias: z74.string().min(1).optional()
19679
20072
  });
19680
20073
  liveRecentActivityHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
19681
20074
  const gate = await gateLive(ctx, input2);
@@ -19753,11 +20146,11 @@ ${renderTrustFooter(trust)}`;
19753
20146
  return 0;
19754
20147
  };
19755
20148
  liveAggregateInputSchema = liveEnabledSchema.extend({
19756
- objectApiName: z73.string().min(1),
19757
- fieldApiName: z73.string().min(1),
19758
- filterField: z73.string().min(1).optional(),
19759
- filterValue: z73.union([z73.string(), z73.number(), z73.boolean()]).optional(),
19760
- orgAlias: z73.string().min(1).optional()
20149
+ objectApiName: z74.string().min(1),
20150
+ fieldApiName: z74.string().min(1),
20151
+ filterField: z74.string().min(1).optional(),
20152
+ filterValue: z74.union([z74.string(), z74.number(), z74.boolean()]).optional(),
20153
+ orgAlias: z74.string().min(1).optional()
19761
20154
  });
19762
20155
  liveAggregateHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
19763
20156
  const gate = await gateLive(ctx, input2);
@@ -19817,12 +20210,12 @@ ${renderTrustFooter(trust)}`;
19817
20210
  };
19818
20211
  MAX_DUPLICATE_GROUPS = 100;
19819
20212
  liveDuplicateCheckInputSchema = liveEnabledSchema.extend({
19820
- objectApiName: z73.string().min(1),
19821
- fieldApiName: z73.string().min(1),
19822
- limit: z73.number().int().min(1).max(MAX_DUPLICATE_GROUPS).optional(),
19823
- filterField: z73.string().min(1).optional(),
19824
- filterValue: z73.union([z73.string(), z73.number(), z73.boolean()]).optional(),
19825
- orgAlias: z73.string().min(1).optional()
20213
+ objectApiName: z74.string().min(1),
20214
+ fieldApiName: z74.string().min(1),
20215
+ limit: z74.number().int().min(1).max(MAX_DUPLICATE_GROUPS).optional(),
20216
+ filterField: z74.string().min(1).optional(),
20217
+ filterValue: z74.union([z74.string(), z74.number(), z74.boolean()]).optional(),
20218
+ orgAlias: z74.string().min(1).optional()
19826
20219
  });
19827
20220
  liveDuplicateCheckHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
19828
20221
  const gate = await gateLive(ctx, input2);
@@ -19881,11 +20274,11 @@ ${renderTrustFooter(trust)}`;
19881
20274
  };
19882
20275
  MAX_OWNER_BUCKETS = 100;
19883
20276
  liveOwnerBreakdownInputSchema = liveEnabledSchema.extend({
19884
- objectApiName: z73.string().min(1),
19885
- limit: z73.number().int().min(1).max(MAX_OWNER_BUCKETS).optional(),
19886
- filterField: z73.string().min(1).optional(),
19887
- filterValue: z73.union([z73.string(), z73.number(), z73.boolean()]).optional(),
19888
- orgAlias: z73.string().min(1).optional()
20277
+ objectApiName: z74.string().min(1),
20278
+ limit: z74.number().int().min(1).max(MAX_OWNER_BUCKETS).optional(),
20279
+ filterField: z74.string().min(1).optional(),
20280
+ filterValue: z74.union([z74.string(), z74.number(), z74.boolean()]).optional(),
20281
+ orgAlias: z74.string().min(1).optional()
19889
20282
  });
19890
20283
  liveOwnerBreakdownHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
19891
20284
  const gate = await gateLive(ctx, input2);
@@ -19962,9 +20355,9 @@ ${renderTrustFooter(trust)}`;
19962
20355
  };
19963
20356
  DEFAULT_REPORT_STALE_DAYS = 90;
19964
20357
  liveReportUsageInputSchema = liveEnabledSchema.extend({
19965
- staleDays: z73.number().int().min(1).max(3650).optional(),
19966
- limit: z73.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
19967
- orgAlias: z73.string().min(1).optional()
20358
+ staleDays: z74.number().int().min(1).max(3650).optional(),
20359
+ limit: z74.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20360
+ orgAlias: z74.string().min(1).optional()
19968
20361
  });
19969
20362
  liveReportUsageHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
19970
20363
  const gate = await gateLive(ctx, input2);
@@ -20016,9 +20409,9 @@ ${renderTrustFooter(trust)}`;
20016
20409
  });
20017
20410
  };
20018
20411
  liveFolderAccessInputSchema = liveEnabledSchema.extend({
20019
- folderType: z73.enum(["Report", "Dashboard", "Email", "Document", "all"]).optional(),
20020
- limit: z73.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20021
- orgAlias: z73.string().min(1).optional()
20412
+ folderType: z74.enum(["Report", "Dashboard", "Email", "Document", "all"]).optional(),
20413
+ limit: z74.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20414
+ orgAlias: z74.string().min(1).optional()
20022
20415
  });
20023
20416
  liveFolderAccessHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
20024
20417
  const gate = await gateLive(ctx, input2);
@@ -20073,9 +20466,9 @@ ${renderTrustFooter(trust)}`;
20073
20466
  DEFAULT_TEMPLATE_STALE_DAYS = 180;
20074
20467
  CLASSIC_TEMPLATE_TYPES = /* @__PURE__ */ new Set(["text", "html", "custom", "visualforce"]);
20075
20468
  liveEmailTemplateUsageInputSchema = liveEnabledSchema.extend({
20076
- staleDays: z73.number().int().min(1).max(3650).optional(),
20077
- limit: z73.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20078
- orgAlias: z73.string().min(1).optional()
20469
+ staleDays: z74.number().int().min(1).max(3650).optional(),
20470
+ limit: z74.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20471
+ orgAlias: z74.string().min(1).optional()
20079
20472
  });
20080
20473
  liveEmailTemplateUsageHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
20081
20474
  const gate = await gateLive(ctx, input2);
@@ -20140,8 +20533,8 @@ ${renderTrustFooter(trust)}`;
20140
20533
  DEFAULT_HEALTH_DAYS = 7;
20141
20534
  LIMIT_RISK_THRESHOLD = 0.8;
20142
20535
  liveOrgHealthInputSchema = liveEnabledSchema.extend({
20143
- days: z73.number().int().min(1).max(90).optional(),
20144
- orgAlias: z73.string().min(1).optional()
20536
+ days: z74.number().int().min(1).max(90).optional(),
20537
+ orgAlias: z74.string().min(1).optional()
20145
20538
  });
20146
20539
  liveOrgHealthHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
20147
20540
  const gate = await gateLive(ctx, input2);
@@ -20208,9 +20601,9 @@ ${renderTrustFooter(trust)}`;
20208
20601
  });
20209
20602
  };
20210
20603
  liveStorageByObjectInputSchema = liveEnabledSchema.extend({
20211
- limit: z73.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20212
- objectApiNames: z73.array(z73.string().min(1)).max(80).optional(),
20213
- orgAlias: z73.string().min(1).optional()
20604
+ limit: z74.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20605
+ objectApiNames: z74.array(z74.string().min(1)).max(80).optional(),
20606
+ orgAlias: z74.string().min(1).optional()
20214
20607
  });
20215
20608
  liveStorageByObjectHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
20216
20609
  const gate = await gateLive(ctx, input2);
@@ -20253,25 +20646,25 @@ ${renderTrustFooter(trust)}`;
20253
20646
  });
20254
20647
  };
20255
20648
  liveDataSkewInputSchema = liveEnabledSchema.extend({
20256
- objectApiName: z73.string().min(1),
20257
- ownerField: z73.string().min(1).optional(),
20258
- threshold: z73.number().int().min(1).optional(),
20259
- limit: z73.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20260
- orgAlias: z73.string().min(1).optional()
20649
+ objectApiName: z74.string().min(1),
20650
+ ownerField: z74.string().min(1).optional(),
20651
+ threshold: z74.number().int().min(1).optional(),
20652
+ limit: z74.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20653
+ orgAlias: z74.string().min(1).optional()
20261
20654
  });
20262
20655
  liveSetupAuditTrailInputSchema = liveEnabledSchema.extend({
20263
- days: z73.number().int().min(1).max(180).optional(),
20264
- limit: z73.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20265
- orgAlias: z73.string().min(1).optional()
20656
+ days: z74.number().int().min(1).max(180).optional(),
20657
+ limit: z74.number().int().min(1).max(MAX_DETAIL_ROWS).optional(),
20658
+ orgAlias: z74.string().min(1).optional()
20266
20659
  });
20267
20660
  liveSecurityExposureInputSchema = liveEnabledSchema.extend({
20268
- orgAlias: z73.string().min(1).optional()
20661
+ orgAlias: z74.string().min(1).optional()
20269
20662
  });
20270
20663
  }
20271
20664
  });
20272
20665
 
20273
20666
  // ../mcp/dist/src/tools/live-drift-check.js
20274
- import { z as z74 } from "zod";
20667
+ import { z as z75 } from "zod";
20275
20668
  var FIELD_PAGE_SIZE2, liveDriftCheckInputSchema, BOUNDARIES8, isCustomField2, diffFields, liveFieldNames, liveDriftCheckHandler;
20276
20669
  var init_live_drift_check = __esm({
20277
20670
  "../mcp/dist/src/tools/live-drift-check.js"() {
@@ -20280,11 +20673,11 @@ var init_live_drift_check = __esm({
20280
20673
  init_src();
20281
20674
  init_live_plane();
20282
20675
  FIELD_PAGE_SIZE2 = 500;
20283
- liveDriftCheckInputSchema = z74.object({
20284
- objectApiName: z74.string().min(1),
20285
- orgAlias: z74.string().min(1).optional(),
20676
+ liveDriftCheckInputSchema = z75.object({
20677
+ objectApiName: z75.string().min(1),
20678
+ orgAlias: z75.string().min(1).optional(),
20286
20679
  /** Opt-in to the live plane (mirrors the other live_* tools). */
20287
- liveEnabled: z74.boolean().optional()
20680
+ liveEnabled: z75.boolean().optional()
20288
20681
  });
20289
20682
  BOUNDARIES8 = Object.freeze([
20290
20683
  "Compares the offline snapshot to a LIVE read-only describe; requires the live plane (SFI_LIVE_PLANE_ENABLED or liveEnabled:true). Does not mutate the org.",
@@ -20352,7 +20745,7 @@ var init_live_drift_check = __esm({
20352
20745
  });
20353
20746
 
20354
20747
  // ../mcp/dist/src/tools/lookup-record.js
20355
- import { z as z75 } from "zod";
20748
+ import { z as z76 } from "zod";
20356
20749
  var RECORD_NODE_TYPES, CUSTOM_METADATA_RECORD_PREFIX, CUSTOM_SETTING_RECORD_PREFIX, lookupRecordInputSchema, classifyRecordId, normalizeValueEntry, readRecordValues, readTypeApiName, lookupRecordHandler;
20357
20750
  var init_lookup_record = __esm({
20358
20751
  "../mcp/dist/src/tools/lookup-record.js"() {
@@ -20365,8 +20758,8 @@ var init_lookup_record = __esm({
20365
20758
  ]);
20366
20759
  CUSTOM_METADATA_RECORD_PREFIX = "CustomMetadataRecord:";
20367
20760
  CUSTOM_SETTING_RECORD_PREFIX = "CustomSettingRecord:";
20368
- lookupRecordInputSchema = z75.object({
20369
- recordId: z75.string().min(1)
20761
+ lookupRecordInputSchema = z76.object({
20762
+ recordId: z76.string().min(1)
20370
20763
  });
20371
20764
  classifyRecordId = (recordId) => {
20372
20765
  if (recordId.startsWith(CUSTOM_METADATA_RECORD_PREFIX)) {
@@ -20459,14 +20852,14 @@ var init_lookup_record = __esm({
20459
20852
  });
20460
20853
 
20461
20854
  // ../mcp/dist/src/tools/manifest.js
20462
- import { z as z76 } from "zod";
20855
+ import { z as z77 } from "zod";
20463
20856
  var getManifestInputSchema, getManifestHandler;
20464
20857
  var init_manifest2 = __esm({
20465
20858
  "../mcp/dist/src/tools/manifest.js"() {
20466
20859
  "use strict";
20467
20860
  init_dist();
20468
20861
  init_src2();
20469
- getManifestInputSchema = z76.object({});
20862
+ getManifestInputSchema = z77.object({});
20470
20863
  getManifestHandler = async (ctx, _input) => {
20471
20864
  const data = {
20472
20865
  ...ctx.manifest,
@@ -20484,7 +20877,7 @@ var init_manifest2 = __esm({
20484
20877
  });
20485
20878
 
20486
20879
  // ../mcp/dist/src/tools/meaningful-test-audit.js
20487
- import { z as z77 } from "zod";
20880
+ import { z as z78 } from "zod";
20488
20881
  var LIST_PAGE_SIZE8, APEX_CLASS_PREFIX5, MEANINGFUL_TEST_DISCLOSURE, meaningfulTestAuditInputSchema, isTestClass3, readNonNegativeInt, collectFakeAssertions, buildEntry, compareEntries2, meaningfulTestAuditHandler;
20489
20882
  var init_meaningful_test_audit = __esm({
20490
20883
  "../mcp/dist/src/tools/meaningful-test-audit.js"() {
@@ -20494,8 +20887,8 @@ var init_meaningful_test_audit = __esm({
20494
20887
  LIST_PAGE_SIZE8 = 500;
20495
20888
  APEX_CLASS_PREFIX5 = "ApexClass:";
20496
20889
  MEANINGFUL_TEST_DISCLOSURE = "v2.7 meaningful_test_audit ranks test classes by the v2.1 R2 fake-assertion recognizer output and an assertions-per-KB density heuristic. The recognizer matches System.assert* invocations against literal tokens; assertions via helper methods (MyTestHelper.assertField) and framework wrappers are invisible. A test class with a high fakeAssertionCount may have meaningful tests via a custom assertion helper the recognizer cannot see. When qualityIssues is absent the v2.1 R2 pass has not run for this vault; entries surface with fakeAssertionCount: 0 and the rank is driven by density alone.";
20497
- meaningfulTestAuditInputSchema = z77.object({
20498
- classFilter: z77.array(z77.string().min(1)).max(LIST_PAGE_SIZE8).optional()
20890
+ meaningfulTestAuditInputSchema = z78.object({
20891
+ classFilter: z78.array(z78.string().min(1)).max(LIST_PAGE_SIZE8).optional()
20499
20892
  });
20500
20893
  isTestClass3 = (node) => node.properties["isTest"] === true;
20501
20894
  readNonNegativeInt = (node, key) => {
@@ -20600,7 +20993,7 @@ var init_meaningful_test_audit = __esm({
20600
20993
  });
20601
20994
 
20602
20995
  // ../mcp/dist/src/tools/method-reachability.js
20603
- import { z as z78 } from "zod";
20996
+ import { z as z79 } from "zod";
20604
20997
  var REACHABILITY_BFS_DEPTH, APEX_CLASS_PREFIX6, APEX_TRIGGER_PREFIX4, REACHABILITY_DISCLOSURE, methodReachabilityInputSchema, isApexCallable3, isTestClass4, entryKindsFor, upstreamWalk, compareEntryHits, compareReachingTests, methodReachabilityHandler;
20605
20998
  var init_method_reachability = __esm({
20606
20999
  "../mcp/dist/src/tools/method-reachability.js"() {
@@ -20612,8 +21005,8 @@ var init_method_reachability = __esm({
20612
21005
  APEX_CLASS_PREFIX6 = "ApexClass:";
20613
21006
  APEX_TRIGGER_PREFIX4 = "ApexTrigger:";
20614
21007
  REACHABILITY_DISCLOSURE = "v2.7 method_reachability ships CLASS granularity (method-level promised in v2.7.1). Dynamic dispatch (Type.forName) and reflective invocation are invisible \u2014 a class genuinely invoked at runtime via reflection or framework wiring will surface as likely-dead-code. Trigger framework base classes (TriggerHandler, fflib) may be partially invisible. BFS is capped at depth 3.";
20615
- methodReachabilityInputSchema = z78.object({
20616
- classApiName: z78.string().min(1)
21008
+ methodReachabilityInputSchema = z79.object({
21009
+ classApiName: z79.string().min(1)
20617
21010
  });
20618
21011
  isApexCallable3 = (id) => id.startsWith(APEX_CLASS_PREFIX6) || id.startsWith(APEX_TRIGGER_PREFIX4);
20619
21012
  isTestClass4 = (node) => node.properties["isTest"] === true;
@@ -20753,15 +21146,15 @@ var init_method_reachability = __esm({
20753
21146
  });
20754
21147
 
20755
21148
  // ../mcp/dist/src/tools/naming-convention-report.js
20756
- import { z as z79 } from "zod";
21149
+ import { z as z80 } from "zod";
20757
21150
  var namingConventionReportInputSchema, namingConventionReportHandler;
20758
21151
  var init_naming_convention_report = __esm({
20759
21152
  "../mcp/dist/src/tools/naming-convention-report.js"() {
20760
21153
  "use strict";
20761
21154
  init_dist();
20762
21155
  init_src4();
20763
- namingConventionReportInputSchema = z79.object({
20764
- scope: z79.string().min(1).optional()
21156
+ namingConventionReportInputSchema = z80.object({
21157
+ scope: z80.string().min(1).optional()
20765
21158
  });
20766
21159
  namingConventionReportHandler = async (ctx, input2) => {
20767
21160
  const result = await recognizeNamingConventions(ctx.graph, input2.scope !== void 0 ? { scope: input2.scope } : {});
@@ -20791,7 +21184,7 @@ var init_naming_convention_report = __esm({
20791
21184
  // ../mcp/dist/src/tools/omniscript-flow.js
20792
21185
  import { readFile as readFile12 } from "node:fs/promises";
20793
21186
  import { XMLParser as XMLParser4 } from "fast-xml-parser";
20794
- import { z as z80 } from "zod";
21187
+ import { z as z81 } from "zod";
20795
21188
  var OMNISCRIPT_PREFIX, DISPATCH_EDGE_TYPE, NATIVE_VS_VLOCITY_DISCLOSURE3, RECORD_LEVEL_DISCLOSURE, APEX_COUPLING_DEFERRAL_DISCLOSURE2, omniscriptFlowInputSchema, isOmniScriptId, unwrapSingle4, toArray4, toNullableString, coerceBoolean3, toNullableNumber, parsePropertySetConfig2, walkElements, readSteps, readStringProp, readNumberProp, readBoolProp, compareDispatched, omniscriptFlowHandler;
20796
21189
  var init_omniscript_flow = __esm({
20797
21190
  "../mcp/dist/src/tools/omniscript-flow.js"() {
@@ -20804,9 +21197,9 @@ var init_omniscript_flow = __esm({
20804
21197
  NATIVE_VS_VLOCITY_DISCLOSURE3 = "v3.2 recognizes Industries Native XML shapes (file extensions `.os-meta.xml`, `.oip-meta.xml`, `.rpt-meta.xml`, `.ouc-meta.xml`, `.decisionTable-meta.xml`). Legacy Vlocity-managed-package components (namespace `vlocity_cmt__`) are NOT extracted by v3.2. Mid-migration orgs may show partial coverage.";
20805
21198
  RECORD_LEVEL_DISCLOSURE = "v3.2 walks the OmniScript / IP / Card metadata XML. The actual user-entered data and runtime state lives in OmniProcessElement and related SObject records; that is record-level data, out of scope for v0.1's read-the-metadata posture.";
20806
21199
  APEX_COUPLING_DEFERRAL_DISCLOSURE2 = "v3.2 captures OmniStudio components and intra-OmniStudio call chains (`dispatchesOmniAction`). The Apex-to-OmniProcess coupling (`implements omnistudio.VlocityOpenInterface` etc.) is a v3.3 follow-up \u2014 those edges are NOT yet in the graph.";
20807
- omniscriptFlowInputSchema = z80.object({
20808
- omniScriptId: z80.string().min(1),
20809
- includeChildPropertySetConfig: z80.boolean().optional()
21200
+ omniscriptFlowInputSchema = z81.object({
21201
+ omniScriptId: z81.string().min(1),
21202
+ includeChildPropertySetConfig: z81.boolean().optional()
20810
21203
  });
20811
21204
  isOmniScriptId = (id) => id.startsWith(OMNISCRIPT_PREFIX);
20812
21205
  unwrapSingle4 = (value) => Array.isArray(value) ? value[0] : value;
@@ -21053,7 +21446,7 @@ var init_omniscript_flow = __esm({
21053
21446
  // ../mcp/dist/src/tools/omniuicard-widget-breakdown.js
21054
21447
  import { readFile as readFile13 } from "node:fs/promises";
21055
21448
  import { XMLParser as XMLParser5, XMLValidator as XMLValidator4 } from "fast-xml-parser";
21056
- import { z as z81 } from "zod";
21449
+ import { z as z82 } from "zod";
21057
21450
  var OMNI_UI_CARD_PREFIX, ROOT_ELEMENT4, PROPERTY_SET_CONFIG_PARSING_DISCLOSURE, NATIVE_VS_VLOCITY_DISCLOSURE4, omniuicardWidgetBreakdownInputSchema, unwrapSingle5, parseJsonBlob, walkWidgets, countWidgets, buildStates, readStringArrayProperty, readNullableStringProperty, readNullableNumberProperty, readBooleanProperty2, projectDispatchedAction, sortDispatchedActions, buildStatesFromSourceXml, omniuicardWidgetBreakdownHandler;
21058
21451
  var init_omniuicard_widget_breakdown = __esm({
21059
21452
  "../mcp/dist/src/tools/omniuicard-widget-breakdown.js"() {
@@ -21065,8 +21458,8 @@ var init_omniuicard_widget_breakdown = __esm({
21065
21458
  ROOT_ELEMENT4 = "OmniUiCard";
21066
21459
  PROPERTY_SET_CONFIG_PARSING_DISCLOSURE = "widget breakdown parses the propertySetConfig JSON blob. FlexCard authors can edit the raw blob in the OmniStudio designer; widget order in the breakdown follows the JSON's declared order, not the visual designer's drag-drop order.";
21067
21460
  NATIVE_VS_VLOCITY_DISCLOSURE4 = "v3.2 recognizes Industries Native XML shapes (file extensions `.os-meta.xml`, `.oip-meta.xml`, `.rpt-meta.xml`, `.ouc-meta.xml`, `.decisionTable-meta.xml`). Legacy Vlocity-managed-package components (namespace `vlocity_cmt__`) are NOT extracted by v3.2. Mid-migration orgs may show partial coverage.";
21068
- omniuicardWidgetBreakdownInputSchema = z81.object({
21069
- omniUiCardId: z81.string().min(1)
21461
+ omniuicardWidgetBreakdownInputSchema = z82.object({
21462
+ omniUiCardId: z82.string().min(1)
21070
21463
  });
21071
21464
  unwrapSingle5 = (value) => Array.isArray(value) ? value[0] : value;
21072
21465
  parseJsonBlob = (raw) => {
@@ -21476,8 +21869,8 @@ var init_soe_payload_bounds = __esm({
21476
21869
  });
21477
21870
 
21478
21871
  // ../mcp/dist/src/tools/order-of-execution.js
21479
- import { z as z82 } from "zod";
21480
- var DISCLOSURE6, SOE_EVENTS, orderOfExecutionInputSchema, workflowMatchesEvent, flowMatchesEvent, triggerMatchesEvent, surfaceFirstCondition, buildActions, buildStep, fetchParentedFirers, fetchTriggersOnFirers, buildAsyncSteps, ASSIGNMENT_TYPES, APPROVAL_TYPES, VALIDATION_TYPES, FLOW_TYPES, TRIGGER_TYPES, WORKFLOW_TYPES, composeForEvent, orderOfExecutionHandler;
21872
+ import { z as z83 } from "zod";
21873
+ var DISCLOSURE7, SOE_EVENTS, orderOfExecutionInputSchema, workflowMatchesEvent, flowMatchesEvent, triggerMatchesEvent, surfaceFirstCondition, buildActions, buildStep, fetchParentedFirers, fetchTriggersOnFirers, buildAsyncSteps, ASSIGNMENT_TYPES, APPROVAL_TYPES, VALIDATION_TYPES, FLOW_TYPES, TRIGGER_TYPES, WORKFLOW_TYPES, composeForEvent, orderOfExecutionHandler;
21481
21874
  var init_order_of_execution = __esm({
21482
21875
  "../mcp/dist/src/tools/order-of-execution.js"() {
21483
21876
  "use strict";
@@ -21485,10 +21878,10 @@ var init_order_of_execution = __esm({
21485
21878
  init_src();
21486
21879
  init_soe_admission();
21487
21880
  init_soe_payload_bounds();
21488
- DISCLOSURE6 = "v2.0e composes the documented Salesforce order-of-execution instantiated against THIS org's extracted automation. Conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether this particular record satisfies them at runtime. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.";
21881
+ DISCLOSURE7 = "v2.0e composes the documented Salesforce order-of-execution instantiated against THIS org's extracted automation. Conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether this particular record satisfies them at runtime. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.";
21489
21882
  SOE_EVENTS = ["insert", "update", "delete", "undelete"];
21490
- orderOfExecutionInputSchema = z82.object({
21491
- objectApiName: z82.string().min(1)
21883
+ orderOfExecutionInputSchema = z83.object({
21884
+ objectApiName: z83.string().min(1)
21492
21885
  });
21493
21886
  workflowMatchesEvent = (triggerType, event) => {
21494
21887
  if (typeof triggerType !== "string")
@@ -21836,7 +22229,7 @@ var init_order_of_execution = __esm({
21836
22229
  objectApiName: input2.objectApiName,
21837
22230
  objectModeled,
21838
22231
  byEvent,
21839
- disclosure: composeSoeDisclosure(DISCLOSURE6, objectModeled)
22232
+ disclosure: composeSoeDisclosure(DISCLOSURE7, objectModeled)
21840
22233
  };
21841
22234
  const containers = SOE_EVENTS.map((event) => byEvent[event].soe);
21842
22235
  const budget = enforceSoeByteBudget(data, containers);
@@ -21856,7 +22249,7 @@ var init_order_of_execution = __esm({
21856
22249
  });
21857
22250
 
21858
22251
  // ../mcp/dist/src/tools/org-history.js
21859
- import { z as z83 } from "zod";
22252
+ import { z as z84 } from "zod";
21860
22253
  var DEFAULT_LIMIT8, MAX_LIMIT9, orgHistoryInputSchema, BOUNDARIES9, orgHistoryHandler;
21861
22254
  var init_org_history = __esm({
21862
22255
  "../mcp/dist/src/tools/org-history.js"() {
@@ -21865,8 +22258,8 @@ var init_org_history = __esm({
21865
22258
  init_history_store();
21866
22259
  DEFAULT_LIMIT8 = 50;
21867
22260
  MAX_LIMIT9 = 500;
21868
- orgHistoryInputSchema = z83.object({
21869
- limit: z83.number().int().min(1).max(MAX_LIMIT9).optional()
22261
+ orgHistoryInputSchema = z84.object({
22262
+ limit: z84.number().int().min(1).max(MAX_LIMIT9).optional()
21870
22263
  });
21871
22264
  BOUNDARIES9 = Object.freeze([
21872
22265
  "History only covers refreshes performed since the continuous-learning store shipped; older or single-refresh vaults yield a short/empty timeline.",
@@ -21897,7 +22290,7 @@ var init_org_history = __esm({
21897
22290
  });
21898
22291
 
21899
22292
  // ../mcp/dist/src/tools/org-pulse.js
21900
- import { z as z84 } from "zod";
22293
+ import { z as z85 } from "zod";
21901
22294
  var DEFAULT_LIMIT9, MAX_LIMIT10, orgPulseInputSchema, ORG_PULSE_DISCLOSURE, orgPulseHandler;
21902
22295
  var init_org_pulse = __esm({
21903
22296
  "../mcp/dist/src/tools/org-pulse.js"() {
@@ -21906,8 +22299,8 @@ var init_org_pulse = __esm({
21906
22299
  init_src();
21907
22300
  DEFAULT_LIMIT9 = 10;
21908
22301
  MAX_LIMIT10 = 50;
21909
- orgPulseInputSchema = z84.object({
21910
- limit: z84.number().int().min(1).max(MAX_LIMIT10).optional()
22302
+ orgPulseInputSchema = z85.object({
22303
+ limit: z85.number().int().min(1).max(MAX_LIMIT10).optional()
21911
22304
  });
21912
22305
  ORG_PULSE_DISCLOSURE = "Freshness and contributor signals come from each component's lastModifiedDate / lastModifiedBy. A plain `sf project retrieve` does NOT populate those \u2014 they need a refresh enriched via the Tooling API. If coverage is ~0% and the contributor list is empty, that means the data was not captured at refresh time, NOT that the org has no history. Run a tooling-API-enabled refresh to populate it.";
21913
22306
  orgPulseHandler = async (ctx, input2) => {
@@ -21942,7 +22335,7 @@ var init_org_pulse = __esm({
21942
22335
  });
21943
22336
 
21944
22337
  // ../mcp/dist/src/tools/outbound-message-catalog.js
21945
- import { z as z85 } from "zod";
22338
+ import { z as z86 } from "zod";
21946
22339
  var OUTBOUND_MESSAGE_CATALOG_MAX_ENTRIES, outboundMessageCatalogInputSchema, OUTBOUND_MESSAGE_DISCLOSURE, readOptionalString2, readBoolean, readFields, readName, apiNameToObjectKey, collectInvokers, compareEntries3, compareInvokers, outboundMessageCatalogHandler;
21947
22340
  var init_outbound_message_catalog = __esm({
21948
22341
  "../mcp/dist/src/tools/outbound-message-catalog.js"() {
@@ -21950,8 +22343,8 @@ var init_outbound_message_catalog = __esm({
21950
22343
  init_dist();
21951
22344
  init_src();
21952
22345
  OUTBOUND_MESSAGE_CATALOG_MAX_ENTRIES = 500;
21953
- outboundMessageCatalogInputSchema = z85.object({
21954
- objectFilter: z85.string().min(1).optional()
22346
+ outboundMessageCatalogInputSchema = z86.object({
22347
+ objectFilter: z86.string().min(1).optional()
21955
22348
  });
21956
22349
  OUTBOUND_MESSAGE_DISCLOSURE = "Endpoint URLs are captured verbatim from the `<outboundMessages><endpointUrl>` element and NOT VALIDATED \u2014 v2.8 does not probe the URL, does not confirm the destination exists, and does not confirm the message is invoked at runtime. Runtime registration via a custom Apex caller or a programmatically-modified workflow rule is invisible to the offline extractor.";
21957
22350
  readOptionalString2 = (properties, key) => {
@@ -22068,7 +22461,7 @@ var init_outbound_message_catalog = __esm({
22068
22461
  });
22069
22462
 
22070
22463
  // ../mcp/dist/src/tools/package-impact.js
22071
- import { z as z86 } from "zod";
22464
+ import { z as z87 } from "zod";
22072
22465
  var DEFAULT_LIMIT10, MAX_LIMIT11, INVENTORY_SAMPLE, EDGE_SCAN_CAP, PACKAGE_IMPACT_DISCLOSURE, packageImpactInputSchema, namespaceOf, buildInventory, buildImpact, packageImpactHandler;
22073
22466
  var init_package_impact = __esm({
22074
22467
  "../mcp/dist/src/tools/package-impact.js"() {
@@ -22080,9 +22473,9 @@ var init_package_impact = __esm({
22080
22473
  INVENTORY_SAMPLE = 5;
22081
22474
  EDGE_SCAN_CAP = 2e3;
22082
22475
  PACKAGE_IMPACT_DISCLOSURE = 'package_impact detects managed/namespaced components by API-name prefix: a name is namespaced iff its leaf splits into >= 3 "__"-delimited segments (NS__Object__c). This reliably catches namespaced objects, fields, and custom metadata \u2014 the bulk of what a package adds \u2014 but MISSES managed Apex referenced via dot-notation (NS.ClassName) and namespaced components without a standard suffix. The vault holds only what `sf project retrieve` pulled: a package\u2019s INTERNAL components are usually NOT retrieved, so packageComponentCount reflects what you can SEE, not the package\u2019s full footprint. "no-detected-dependencies" means NO STATIC evidence in retrieved metadata that your components reference this namespace \u2014 it does NOT prove the package is safe to uninstall (dynamic SOQL, Type.forName("NS.X"), merge-field/formula references, and unretrieved metadata are invisible). Always validate an uninstall in a sandbox first.';
22083
- packageImpactInputSchema = z86.object({
22084
- namespace: z86.string().min(1).optional(),
22085
- limit: z86.number().int().min(1).max(MAX_LIMIT11).optional()
22476
+ packageImpactInputSchema = z87.object({
22477
+ namespace: z87.string().min(1).optional(),
22478
+ limit: z87.number().int().min(1).max(MAX_LIMIT11).optional()
22086
22479
  });
22087
22480
  namespaceOf = (idOrName) => {
22088
22481
  const colon = idOrName.indexOf(":");
@@ -22221,7 +22614,7 @@ var init_package_impact = __esm({
22221
22614
  });
22222
22615
 
22223
22616
  // ../mcp/dist/src/tools/process-builder-migration-candidates.js
22224
- import { z as z87 } from "zod";
22617
+ import { z as z88 } from "zod";
22225
22618
  var PROCESS_BUILDER_MAX_LIMIT, PROCESS_BUILDER_DEFAULT_LIMIT, LIST_PAGE_SIZE9, PROCESS_BUILDER_RETIREMENT_NOTE, WORKFLOW_RULE_RETIREMENT_NOTE, BOUNDARIES10, processBuilderMigrationCandidatesInputSchema, propertyNumber, propertyBoolean, propertyString2, countOutgoingEdges, classifyProcessBuilder, classifyWorkflowRule, classifyApprovalProcess, migrationNoteForProcessBuilder, migrationNoteForWorkflowRule, migrationNoteForApprovalProcess, complexityRank, compareByComplexity, compareByApiName2, compareByParent, sortFor, processBuilderMigrationCandidatesHandler;
22226
22619
  var init_process_builder_migration_candidates = __esm({
22227
22620
  "../mcp/dist/src/tools/process-builder-migration-candidates.js"() {
@@ -22237,12 +22630,12 @@ var init_process_builder_migration_candidates = __esm({
22237
22630
  "the complexity classification is heuristic based on edge counts and time-trigger presence; complex business logic in a single-decision Process Builder may rank as 'simple' but require manual review for migration.",
22238
22631
  "the migration tool itself (Setup \u2192 Migrate to Flow) does not run here \u2014 this tool produces the inventory and per-rule guidance."
22239
22632
  ]);
22240
- processBuilderMigrationCandidatesInputSchema = z87.object({
22241
- includeWorkflowRules: z87.boolean().optional(),
22242
- includeApprovalProcesses: z87.boolean().optional(),
22243
- activeOnly: z87.boolean().optional(),
22244
- sortBy: z87.enum(["complexity", "object", "name"]).optional(),
22245
- limit: z87.number().int().min(1).max(PROCESS_BUILDER_MAX_LIMIT).optional()
22633
+ processBuilderMigrationCandidatesInputSchema = z88.object({
22634
+ includeWorkflowRules: z88.boolean().optional(),
22635
+ includeApprovalProcesses: z88.boolean().optional(),
22636
+ activeOnly: z88.boolean().optional(),
22637
+ sortBy: z88.enum(["complexity", "object", "name"]).optional(),
22638
+ limit: z88.number().int().min(1).max(PROCESS_BUILDER_MAX_LIMIT).optional()
22246
22639
  });
22247
22640
  propertyNumber = (node, key) => {
22248
22641
  const v = node.properties[key];
@@ -22599,7 +22992,7 @@ var init_clarify = __esm({
22599
22992
  });
22600
22993
 
22601
22994
  // ../mcp/dist/src/tools/resolve.js
22602
- import { z as z88 } from "zod";
22995
+ import { z as z89 } from "zod";
22603
22996
  var RESOLVE_MAX_LIMIT, RESOLVE_DISCLOSURE, resolveInputSchema, resolveHandler;
22604
22997
  var init_resolve2 = __esm({
22605
22998
  "../mcp/dist/src/tools/resolve.js"() {
@@ -22610,11 +23003,11 @@ var init_resolve2 = __esm({
22610
23003
  init_clarify();
22611
23004
  RESOLVE_MAX_LIMIT = 50;
22612
23005
  RESOLVE_DISCLOSURE = "These are typo-tolerant, fuzzy-ranked guesses at which component you meant \u2014 heuristic, not declared. disposition 'exact' = one confident match; 'ambiguous' = several plausible candidates, confirm the right one before acting; 'none' = nothing matched confidently (any listed items are weak near-misses). A high score is string similarity, not proof \u2014 verify the candidate's canonical id and label.";
22613
- resolveInputSchema = z88.object({
22614
- query: z88.string().min(1),
22615
- types: z88.array(z88.string()).optional(),
22616
- parentId: z88.string().min(1).optional(),
22617
- limit: z88.number().int().min(1).max(RESOLVE_MAX_LIMIT).optional()
23006
+ resolveInputSchema = z89.object({
23007
+ query: z89.string().min(1),
23008
+ types: z89.array(z89.string()).optional(),
23009
+ parentId: z89.string().min(1).optional(),
23010
+ limit: z89.number().int().min(1).max(RESOLVE_MAX_LIMIT).optional()
22618
23011
  });
22619
23012
  resolveHandler = async (ctx, input2) => {
22620
23013
  const result = await resolveComponents(ctx.graph, input2.query, {
@@ -22671,11 +23064,41 @@ var init_resolve2 = __esm({
22671
23064
  import { appendFile, mkdir as mkdir6 } from "node:fs/promises";
22672
23065
  import { homedir as homedir2 } from "node:os";
22673
23066
  import { dirname as dirname5, join as join9 } from "node:path";
22674
- var normalize, routeText, RULES, classifyQuestion, gapLogPath, logGapIfAny;
23067
+ var normalize, deriveSaveEvent, deriveKnowledgeTopic, routeText, RULES, classifyQuestion, gapLogPath, logGapIfAny;
22675
23068
  var init_intent_router = __esm({
22676
23069
  "../mcp/dist/src/intent-router.js"() {
22677
23070
  "use strict";
22678
23071
  normalize = (q) => q.trim().toLowerCase().replace(/\s+/g, " ");
23072
+ deriveSaveEvent = (q) => {
23073
+ if (/\b(undelet|restor)/.test(q))
23074
+ return "undelete";
23075
+ if (/\b(creat|insert)/.test(q))
23076
+ return "insert";
23077
+ if (/\b(delet|remov)/.test(q))
23078
+ return "delete";
23079
+ return "update";
23080
+ };
23081
+ deriveKnowledgeTopic = (q) => {
23082
+ if (/\b(flow\s+(vs\.?|versus|or)\s+apex|apex\s+(vs\.?|versus|or)\s+flow)\b/.test(q))
23083
+ return "flow-vs-apex";
23084
+ if (/\bwhen\s+(should\s+i|to)\s+use\b.*\bflow\b/.test(q))
23085
+ return "flow-vs-apex";
23086
+ if (/\b(apex\s+)?trigger\s+framework\b/.test(q))
23087
+ return "trigger-framework";
23088
+ if (/\basync(hronous)?\s+apex\b/.test(q) || /\b(future|queueable|batch|schedulable)\b.*\b(vs\.?|versus|when\s+to|which\s+to|each\s+appropriate|options?)\b/.test(q))
23089
+ return "async-apex";
23090
+ if (/\b(sfdx|source[-\s]driven\s+development|scratch\s+orgs?)\b/.test(q))
23091
+ return "sfdx-source-driven-dev";
23092
+ if (/\bunlocked\s+packages?\b/.test(q))
23093
+ return "package-strategy";
23094
+ if (/\bsingle[-\s]org\b.*\bmulti[-\s]org\b/.test(q))
23095
+ return "single-vs-multi-org";
23096
+ if (/\bdata\s+(retention|archiv)/.test(q))
23097
+ return "data-retention-archiving";
23098
+ if (/\b(large\s+data\s+volumes?|\bldv\b)/.test(q))
23099
+ return "large-data-volumes";
23100
+ return void 0;
23101
+ };
22679
23102
  routeText = (question) => {
22680
23103
  const normalized = normalize(question).replace(/\s*\[[^\]]+\]\s*$/, "");
22681
23104
  const stripped = normalized.replace(/^.*?\b(answer this|route this|do not guess|tell me|include|show what|fail closed|flag anything|use the safest|state what)\b[^:]*:\s*/, "");
@@ -22737,7 +23160,10 @@ var init_intent_router = __esm({
22737
23160
  needsResolve: false,
22738
23161
  reason: "Storage, API usage, and governor headroom are live org telemetry.",
22739
23162
  patterns: [
22740
- /\b(org|governor)\s+limits?\b/,
23163
+ // "governor limits" → live runtime headroom, BUT not when the question is
23164
+ // about static Apex risk ("governor limit risks in our Apex", "SOQL in
23165
+ // loops") — that's the vault governor-risks scan (NI-7 misroute fix).
23166
+ /\b(org|governor)\s+limits?\b(?!.*\b(risk|apex|loop|soql|dml|static|trigger|class)\b)/,
22741
23167
  /\b(api|daily)\s+(usage|calls?|limit)\b/,
22742
23168
  /\b(data|file)\s+storage\b/,
22743
23169
  /\bhow\s+much\s+(storage|api|data)\b/,
@@ -22771,6 +23197,25 @@ var init_intent_router = __esm({
22771
23197
  /\brecords?\s+by\s+owner\b/
22772
23198
  ]
22773
23199
  },
23200
+ {
23201
+ // METADATA counts are vault, not live. "How many layouts / fields / objects
23202
+ // / profiles / validation rules ... [per X]" was stolen by the live
23203
+ // group-count/record-count rules and misrouted to live GROUP BY (B30.1).
23204
+ // Record counts (accounts, contacts, rows) fall through to the live rules
23205
+ // below. Placed AFTER field-population so "how many fields are populated"
23206
+ // stays hybrid/live.
23207
+ intent: "metadata-count",
23208
+ plane: "vault",
23209
+ tools: ["sfi.list_components", "sfi.get_component"],
23210
+ liveRequired: false,
23211
+ needsResolve: false,
23212
+ reason: "Counting metadata components (layouts, fields, objects, profiles, validation rules, flows, classes, record types, list views) is a vault list_components count \u2014 not live record data.",
23213
+ patterns: [
23214
+ /\bhow\s+many\b.*\b(page\s+layouts?|layouts?|custom\s+objects?|profiles?|permission\s+sets?|validation\s+rules?|flows?|(apex\s+)?classes?|triggers?|record\s+types?|list\s+views?|report\s+types?|record\s+pages?|flexipages?|approval\s+process(es)?|custom\s+settings?|quick\s+actions?|sharing\s+rules?|named\s+credentials?|picklists?)\b/,
23215
+ // fields, but NOT field usage/population (those are unused-fields / field-population)
23216
+ /\bhow\s+many\b.*\b(custom\s+)?fields?\b(?!.*\b(used|populated|filled|actually|unused|empty|blank|set|values?)\b)/
23217
+ ]
23218
+ },
22774
23219
  {
22775
23220
  intent: "group-count",
22776
23221
  plane: "live",
@@ -22958,7 +23403,27 @@ var init_intent_router = __esm({
22958
23403
  patterns: [
22959
23404
  /\b(page\s+)?layouts?\b.*\b(who|access|assigned|profile|sees?|user)\b/,
22960
23405
  /\bwho\s+(sees|has|can)\b.*\blayouts?\b/,
22961
- /\bwhich\s+layout\b/
23406
+ /\bwhich\s+layout\b/,
23407
+ // "what/which (page) layouts show|contain|have a FIELD" — a field->layout
23408
+ // question (vs the who-sees-layout above) used to fall through (B21).
23409
+ /\b(what|which)\s+(page\s+)?layouts?\b.*\b(show|contain|display|include|have|with|for)\b.*\bfield\b/
23410
+ ]
23411
+ },
23412
+ {
23413
+ // Layout INVENTORY + CONTENTS (vs layout-access's who-sees-which). "How many
23414
+ // layouts exist for X", "what fields / related lists / quick actions are on
23415
+ // the Y layout" used to fall through to generic schema or unrouted (B21.1).
23416
+ intent: "layout-inventory",
23417
+ plane: "vault",
23418
+ tools: ["sfi.resolve", "sfi.list_components", "sfi.get_component"],
23419
+ liveRequired: false,
23420
+ needsResolve: false,
23421
+ reason: "Page-layout inventory (how many / which layouts on an object) and contents (fields, related lists, quick actions on a named layout) are modeled in the vault \u2014 Layout is a covered type.",
23422
+ patterns: [
23423
+ /\bhow\s+many\b.*\blayouts?\b/,
23424
+ /\b(what|which|list)\b.*\b(page\s+)?layouts?\b.*\b(exist|are\s+there|for\s+the|on\s+the|does|available)\b/,
23425
+ /\bwhat\b.*\b(fields?|related\s+lists?|quick\s+actions?|sections?|buttons?)\b.*\bon\b.*\blayout\b/,
23426
+ /\b(related\s+lists?|quick\s+actions?)\b.*\b(on|appear|for)\b.*\blayout\b/
22962
23427
  ]
22963
23428
  },
22964
23429
  {
@@ -22999,9 +23464,13 @@ var init_intent_router = __esm({
22999
23464
  needsResolve: false,
23000
23465
  reason: "God-mode / over-permission detection is a vault permission synthesis.",
23001
23466
  patterns: [
23002
- /\b(over[-\s]?permission|god[-\s]?mode|too\s+much\s+access|modify\s+all|view\s+all)\b/,
23467
+ /\b(over[-\s]?permission(ed|s)?|god[-\s]?mode|too\s+much\s+access|modify\s+all|view\s+all)\b/,
23003
23468
  /\bwho\s+is\s+(an?\s+)?admin\b/,
23004
- /\b(permission|access)\s+(risk|sprawl|hygiene)\b/
23469
+ /\b(permission|access)\s+(risk|sprawl|hygiene)\b/,
23470
+ // Specific high-risk system permissions — "who can author apex / customize
23471
+ // the application / manage users / modify metadata / view setup" — all
23472
+ // answered by the permission_risk_report over-privilege pass.
23473
+ /\bwho\s+(can|has)\b.*\b(author\s+apex|customi[sz]e\s+(the\s+)?application|manage\s+(users|sharing|roles|profiles)|modify\s+metadata|view\s+setup)\b/
23005
23474
  ]
23006
23475
  },
23007
23476
  {
@@ -23101,14 +23570,21 @@ var init_intent_router = __esm({
23101
23570
  liveRequired: false,
23102
23571
  needsResolve: true,
23103
23572
  reason: "Order of execution / what runs on save is reconstructed from the vault graph. what_happens_on_save needs an explicit DML event \u2014 default to 'update' (or insert/delete to match the question) when none is stated.",
23573
+ suggestArgs: (q) => ({ event: deriveSaveEvent(q) }),
23104
23574
  patterns: [
23105
23575
  /\b(trigger\s+order|order\s+of\s+execution)\b/,
23106
- /\bwhat\s+(happens|runs|fires)\b.*\b(on\s+save|when\b.*\b(created|saved|updated|inserted|deleted))\b/,
23576
+ /\bwhat\s+(happens|runs|fires)\b.*\b(on\s+save|when\b.*\b(created|saved|updated|inserted|deleted|undeleted|restored))\b/,
23107
23577
  // "which/what flows|triggers|VRs|workflows run|fire when ..." — the
23108
23578
  // "which" phrasing was a router gap (e.g. "which flows run when a Case is
23109
23579
  // created"), so the question fell through to unrouted.
23110
23580
  /\b(what|which)\s+(triggers?|automation|flows?|validation\s+rules?|workflows?)\b.*\b(fire|run|execute|happen)\b/,
23111
- /\b(flows?|triggers?|automation)\b.*\bwhen\b.*\b\w+\s+is\b.*\b(created|updated|inserted|deleted|saved)\b/
23581
+ /\b(flows?|triggers?|automation)\b.*\bwhen\b.*\b\w+\s+is\b.*\b(created|updated|inserted|deleted|saved)\b/,
23582
+ // "what APEX/code/class runs when ..." — a noun between "what" and the
23583
+ // verb (vs the adjacent "what runs") used to fall through (B21).
23584
+ /\bwhat\s+(apex|code|class(es)?)\b.*\bruns?\b.*\bwhen\b/,
23585
+ // "what happens when a Case STATUS CHANGES" — a status/stage change is a
23586
+ // save-order event the "(created|updated|...)" verb list missed (B21).
23587
+ /\bwhat\s+(happens|runs|fires)\b.*\b(status|stage)\b.*\bchang/
23112
23588
  ]
23113
23589
  },
23114
23590
  {
@@ -23124,6 +23600,25 @@ var init_intent_router = __esm({
23124
23600
  /\bwhat\s+changed\b.*\bfield\b.*\bon\s+save\b/
23125
23601
  ]
23126
23602
  },
23603
+ {
23604
+ // What a validation rule enforces / its error — get_component on the rule.
23605
+ // MUST precede automation-on-object, which else steals "what does the X
23606
+ // validation rule on <object> enforce" (it has "validation rule … on
23607
+ // account") and sends it to automation_build_advisor instead of the rule's
23608
+ // formula+error (NI-11). "What validation rules exist/run" stays schema /
23609
+ // trigger-order (those lack the enforce/does/error verbs below).
23610
+ intent: "explain-validation-rule",
23611
+ plane: "vault",
23612
+ tools: ["sfi.resolve", "sfi.get_component"],
23613
+ liveRequired: false,
23614
+ needsResolve: true,
23615
+ reason: "A validation rule's condition and error message are in the vault \u2014 resolve the rule, then get_component for its formula and error text.",
23616
+ patterns: [
23617
+ /\bvalidation\s+rules?\b.*\b(enforce|does|do\b|check|mean|prevent|block|error|message|stop)\b/,
23618
+ /\bwhat\s+(does|error|message)\b.*\bvalidation\s+rule\b/,
23619
+ /\b(error|message)\b.*\bvalidation\s+rule\b.*\b(show|display|return)\b/
23620
+ ]
23621
+ },
23127
23622
  {
23128
23623
  intent: "automation-on-object",
23129
23624
  plane: "vault",
@@ -23330,6 +23825,23 @@ var init_intent_router = __esm({
23330
23825
  /\borg\s+(risk|health)\b/
23331
23826
  ]
23332
23827
  },
23828
+ {
23829
+ // "Which classes implement <interface>" (Batchable, Schedulable, Queueable,
23830
+ // RestResource, ...) — grep Apex source for the implements clause. The
23831
+ // apex-search verbs (uses/references) didn't cover "implement" (B21.16/17,
23832
+ // E.4-6 / BL-13 interface filters).
23833
+ intent: "interface-implementers",
23834
+ plane: "vault",
23835
+ tools: ["sfi.search_apex_source", "sfi.find_apex_usages"],
23836
+ liveRequired: false,
23837
+ needsResolve: false,
23838
+ reason: "Classes implementing an interface (Batchable / Schedulable / Queueable / RestResource) are found by grepping Apex source for the implements clause.",
23839
+ patterns: [
23840
+ /\b(which|what)\s+(classes?|apex)\b.*\bimplements?\b/,
23841
+ /\bimplements?\b.*\b(batchable|schedulable|queueable|database\.\w+|interface)\b/,
23842
+ /\b(batchable|schedulable|queueable)\b.*\b(classes?|implement)\b/
23843
+ ]
23844
+ },
23333
23845
  {
23334
23846
  intent: "apex-search",
23335
23847
  plane: "vault",
@@ -23357,19 +23869,53 @@ var init_intent_router = __esm({
23357
23869
  /\bapi\b.*\b(connections?|surfaces?)\b/
23358
23870
  ]
23359
23871
  },
23872
+ {
23873
+ // "What platform events does this org publish / exist?" → CATALOG mode
23874
+ // (event_subscribers with NO eventId). No named component to resolve —
23875
+ // distinct from who-subscribes-to-EVENT-X below. NI-1: follow-up to the D6
23876
+ // catalog fix so the route stops telling the agent to resolve a component
23877
+ // that was never named.
23878
+ intent: "event-catalog",
23879
+ plane: "vault",
23880
+ tools: ["sfi.event_subscribers"],
23881
+ liveRequired: false,
23882
+ needsResolve: false,
23883
+ reason: "Lists every Platform Event the org publishes with its subscriber count (event_subscribers catalog mode \u2014 call with no eventId).",
23884
+ patterns: [
23885
+ /\bwhat\s+(platform\s+events?|cdc\s+channels?)\b/,
23886
+ /\b(platform\s+events?)\b.*\b(publish|emit|defined|exist|list|are\s+there)\b/
23887
+ ]
23888
+ },
23360
23889
  {
23361
23890
  intent: "event-subscribers",
23362
23891
  plane: "vault",
23363
23892
  tools: ["sfi.resolve", "sfi.event_subscribers", "sfi.cdc_subscribers"],
23364
23893
  liveRequired: false,
23365
23894
  needsResolve: true,
23366
- reason: "Who subscribes to a platform event / change data capture channel.",
23895
+ reason: "Who subscribes to a specific platform event / change data capture channel.",
23367
23896
  patterns: [
23368
23897
  /\b(platform\s+events?|change\s+data\s+capture|cdc)\b.*\b(subscrib|listen|consum)\b/,
23369
23898
  /\bwho\s+(subscribes?|listens?)\b/,
23370
23899
  /\b(subscribers?|subscriptions?)\b.*\bevents?\b/
23371
23900
  ]
23372
23901
  },
23902
+ {
23903
+ // Inbound Apex REST (@RestResource) — "what REST endpoints are exposed".
23904
+ // MUST precede the outbound `endpoints` rule, which would otherwise grab
23905
+ // "endpoints" and misroute to the outbound catalog (B21.15).
23906
+ intent: "rest-endpoints",
23907
+ plane: "vault",
23908
+ tools: ["sfi.search_apex_source", "sfi.find_apex_usages"],
23909
+ liveRequired: false,
23910
+ needsResolve: false,
23911
+ reason: "Inbound Apex REST endpoints are @RestResource/@Http* classes \u2014 grep Apex source for the annotations.",
23912
+ patterns: [
23913
+ /\b(apex\s+)?rest\s+(endpoints?|resources?|services?|api)\b/,
23914
+ /\b@?restresource\b/,
23915
+ /\b(inbound|exposed|expose)\b.*\brest\b/,
23916
+ /\brest\b.*\b(endpoints?|resources?)\b.*\b(expose|exposed|apex|class)\b/
23917
+ ]
23918
+ },
23373
23919
  {
23374
23920
  intent: "endpoints",
23375
23921
  plane: "vault",
@@ -23434,6 +23980,24 @@ var init_intent_router = __esm({
23434
23980
  /\b(safe\s+to\s+delete|can\s+i\s+delete|ok\s+to\s+(delete|remove))\b/
23435
23981
  ]
23436
23982
  },
23983
+ {
23984
+ // MUST precede impact-analysis + what-if-field: a question about changing a
23985
+ // field's stored VALUE (not its type/required/deletion) routes to the
23986
+ // value-change tier. The discriminator is the word "value(s)" near a
23987
+ // change/impact verb; schema what-ifs say "field type" / "delete" / "required".
23988
+ intent: "value-change",
23989
+ plane: "vault",
23990
+ tools: ["sfi.resolve", "sfi.value_change_audit", "sfi.what_if_change_field_value"],
23991
+ liveRequired: false,
23992
+ needsResolve: true,
23993
+ reason: "Changing a field's stored VALUE (not its schema) has a distinct blast radius \u2014 identity / integration-key / uniqueness / automation / cross-object \u2014 surfaced by value_change_audit (a set of fields on an object) or what_if_change_field_value (one field). Distinct from the type/required/delete what-ifs.",
23994
+ patterns: [
23995
+ /\bvalue[-\s]changes?\b/,
23996
+ /\b(chang|updat|edit|modif|bulk[-\s]?updat)\w*\b[^.?!]{0,40}\bvalues?\b/,
23997
+ /\bvalues?\b[^.?!]{0,40}\b(impact|affect|break|desync|safe|risk)\w*/,
23998
+ /\b(impact|affect|safe|risky?)\b[^.?!]{0,70}\b(chang|updat|edit|modif)\w*\b[^.?!]{0,40}\bvalues?\b/
23999
+ ]
24000
+ },
23437
24001
  {
23438
24002
  intent: "impact-analysis",
23439
24003
  plane: "vault",
@@ -23646,6 +24210,37 @@ var init_intent_router = __esm({
23646
24210
  ]
23647
24211
  },
23648
24212
  // === Schema / naming (general — near the end) =============================
24213
+ {
24214
+ // KNOWLEDGE plane (B1) — greenfield/best-practice asks with NO org-specific
24215
+ // answer. Patterns are deliberately narrow (only asks no vault rule already
24216
+ // catches: flow-vs-apex, trigger framework, async options, SFDX, unlocked
24217
+ // packages) so org-specific questions still route to vault/live. Other
24218
+ // greenfield topics (governor limits, callouts, coverage, naming) are caught
24219
+ // by their vault rules earlier and stay org-specific; their curated topics
24220
+ // remain reachable via an explicit sfi.guidance { topic } call.
24221
+ intent: "guidance",
24222
+ plane: "knowledge",
24223
+ tools: ["sfi.guidance"],
24224
+ liveRequired: false,
24225
+ needsResolve: false,
24226
+ reason: "General Salesforce best-practice question with no org-specific answer \u2014 sfi.guidance returns a curated summary + official doc links (not org data).",
24227
+ suggestArgs: (q) => {
24228
+ const topic = deriveKnowledgeTopic(q);
24229
+ return topic !== void 0 ? { topic } : void 0;
24230
+ },
24231
+ patterns: [
24232
+ /\b(flow\s+(vs\.?|versus|or)\s+apex|apex\s+(vs\.?|versus|or)\s+flow)\b/,
24233
+ /\bwhen\s+(should\s+i|to)\s+use\b.*\bflow\b/,
24234
+ /\b(apex\s+)?trigger\s+framework\b/,
24235
+ /\basync(hronous)?\s+apex\b/,
24236
+ /\b(future|queueable|batch|schedulable)\b.*\b(vs\.?|versus|when\s+to|which\s+to|each\s+appropriate|options?)\b/,
24237
+ /\b(sfdx|source[-\s]driven\s+development|scratch\s+orgs?)\b/,
24238
+ /\bunlocked\s+packages?\b/,
24239
+ /\bsingle[-\s]org\b.*\bmulti[-\s]org\b/,
24240
+ /\bdata\s+(retention|archiv)/,
24241
+ /\b(large\s+data\s+volumes?|\bldv\b)/
24242
+ ]
24243
+ },
23649
24244
  {
23650
24245
  intent: "resolve-lookup",
23651
24246
  plane: "vault",
@@ -23673,6 +24268,92 @@ var init_intent_router = __esm({
23673
24268
  /\b(suffix|prefix)\b.*\b(convention|standard)\b/
23674
24269
  ]
23675
24270
  },
24271
+ {
24272
+ // Lightning record pages / FlexiPages — assignment + component breakdown.
24273
+ // "Which Lightning record page is assigned to X", "what components are on
24274
+ // the Y record page" fell through to schema/unrouted (B21.2).
24275
+ intent: "flexipage",
24276
+ plane: "vault",
24277
+ tools: ["sfi.resolve", "sfi.list_components", "sfi.get_component"],
24278
+ liveRequired: false,
24279
+ needsResolve: false,
24280
+ reason: "Lightning record pages (FlexiPage) \u2014 assignment and component breakdown \u2014 are modeled in the vault.",
24281
+ patterns: [
24282
+ /\b(lightning\s+(record\s+)?page|flexipage)s?\b/,
24283
+ /\b(component|assign|which|what)\b.*\brecord\s+pages?\b/,
24284
+ /\brecord\s+pages?\b.*\b(assign|component|layout|for\s+the)\b/
24285
+ ]
24286
+ },
24287
+ {
24288
+ // Picklist-value differences across record types — get_component on each
24289
+ // RecordType shows its picklist value sets. Fell through (B21.4).
24290
+ intent: "record-type-picklist",
24291
+ plane: "vault",
24292
+ tools: ["sfi.resolve", "sfi.list_components", "sfi.get_component"],
24293
+ liveRequired: false,
24294
+ needsResolve: false,
24295
+ reason: "Record-type picklist-value assignments are in the vault \u2014 list the record types, then get_component each to compare picklist values.",
24296
+ patterns: [
24297
+ /\brecord\s+types?\b.*\b(picklist|differ|difference|values?)\b/,
24298
+ /\bpicklist\s+values?\b.*\b(record\s+types?|differ|between|across)\b/,
24299
+ /\bwhich\s+picklist\s+values?\b.*\brecord\s+type/
24300
+ ]
24301
+ },
24302
+ {
24303
+ // Approval processes — list + steps. Fell through to schema/unrouted (B21.6).
24304
+ intent: "approval-process",
24305
+ plane: "vault",
24306
+ tools: ["sfi.resolve", "sfi.list_components", "sfi.get_component"],
24307
+ liveRequired: false,
24308
+ needsResolve: false,
24309
+ reason: "Approval processes and their steps are modeled in the vault (ApprovalProcess).",
24310
+ patterns: [
24311
+ /\bapproval\s+process(es)?\b/,
24312
+ /\bapproval\s+steps?\b/,
24313
+ /\bapproval\b.*\b(steps?|process|who\s+approves?|approvers?|stages?)\b/
24314
+ ]
24315
+ },
24316
+ {
24317
+ // List views inventory (B21.8). Note: ListView is retrieved but not
24318
+ // graph-modeled (notModeled), so relationships are limited — list only.
24319
+ intent: "list-views",
24320
+ plane: "vault",
24321
+ tools: ["sfi.resolve", "sfi.list_components", "sfi.get_component"],
24322
+ liveRequired: false,
24323
+ needsResolve: false,
24324
+ reason: "List views are catalogued in the vault via list_components (ListView). Coverage note: ListView is retrieved but not graph-modeled.",
24325
+ patterns: [/\blist\s+views?\b/]
24326
+ },
24327
+ {
24328
+ // Custom Settings vs Custom Metadata Types — both retrieved as CustomObject.
24329
+ // Disambiguation gap (B21.9).
24330
+ intent: "custom-settings-cmdt",
24331
+ plane: "vault",
24332
+ tools: ["sfi.resolve", "sfi.list_components", "sfi.get_component"],
24333
+ liveRequired: false,
24334
+ needsResolve: false,
24335
+ reason: "Custom Settings and Custom Metadata Types are modeled as CustomObject in the vault \u2014 list + get_component to see what they store.",
24336
+ patterns: [
24337
+ /\bcustom\s+settings?\b/,
24338
+ /\bcustom\s+metadata\s+types?\b/,
24339
+ /\b(cmdt|__mdt)\b/,
24340
+ /\bhierarch(y|ical|ic)\b.*\bsettings?\b/
24341
+ ]
24342
+ },
24343
+ {
24344
+ // Difference between two profiles / permission sets (B21.10).
24345
+ intent: "compare-profiles",
24346
+ plane: "vault",
24347
+ tools: ["sfi.resolve", "sfi.compare_components"],
24348
+ liveRequired: false,
24349
+ needsResolve: true,
24350
+ reason: "Comparing two profiles or permission sets is a vault diff \u2014 resolve both, then compare_components.",
24351
+ patterns: [
24352
+ /\b(difference|differ|compare|versus|vs\.?)\b.*\bprofiles?\b/,
24353
+ /\bprofiles?\b.*\b(difference|differ|compare|versus|vs\b)\b/,
24354
+ /\bcompare\b.*\b(permission\s+sets?|profiles?)\b/
24355
+ ]
24356
+ },
23676
24357
  {
23677
24358
  intent: "schema",
23678
24359
  plane: "vault",
@@ -23707,6 +24388,7 @@ var init_intent_router = __esm({
23707
24388
  }
23708
24389
  for (const rule of RULES) {
23709
24390
  if (rule.patterns.some((p) => p.test(q))) {
24391
+ const suggestedArgs = rule.suggestArgs?.(q);
23710
24392
  return {
23711
24393
  question,
23712
24394
  plane: rule.plane,
@@ -23715,7 +24397,8 @@ var init_intent_router = __esm({
23715
24397
  liveRequired: rule.liveRequired,
23716
24398
  needsResolve: rule.needsResolve,
23717
24399
  reason: rule.reason,
23718
- gap: rule.gap ?? null
24400
+ gap: rule.gap ?? null,
24401
+ ...suggestedArgs !== void 0 ? { suggestedArgs } : {}
23719
24402
  };
23720
24403
  }
23721
24404
  }
@@ -23763,7 +24446,7 @@ var init_intent_router = __esm({
23763
24446
  });
23764
24447
 
23765
24448
  // ../mcp/dist/src/tools/route-question.js
23766
- import { z as z89 } from "zod";
24449
+ import { z as z90 } from "zod";
23767
24450
  var routeQuestionInputSchema, routeTrust, routeQuestionHandler;
23768
24451
  var init_route_question = __esm({
23769
24452
  "../mcp/dist/src/tools/route-question.js"() {
@@ -23771,11 +24454,11 @@ var init_route_question = __esm({
23771
24454
  init_dist();
23772
24455
  init_answer_render();
23773
24456
  init_intent_router();
23774
- routeQuestionInputSchema = z89.object({
24457
+ routeQuestionInputSchema = z90.object({
23775
24458
  /** The user's plain-language question. */
23776
- question: z89.string().min(1),
24459
+ question: z90.string().min(1),
23777
24460
  /** Append a gap entry to the local backlog when the route has one (default true). */
23778
- logGap: z89.boolean().optional()
24461
+ logGap: z90.boolean().optional()
23779
24462
  });
23780
24463
  routeTrust = () => ({
23781
24464
  provenance: "offline_snapshot",
@@ -23806,7 +24489,7 @@ var init_route_question = __esm({
23806
24489
  });
23807
24490
 
23808
24491
  // ../mcp/dist/src/tools/scheduled-job-catalog.js
23809
- import { z as z90 } from "zod";
24492
+ import { z as z91 } from "zod";
23810
24493
  var SCHEDULED_JOB_CATALOG_MAX_CLASSES, scheduledJobCatalogInputSchema, SCHEDULED_JOB_CATALOG_DISCLOSURE, readCronExpressions, readEdgeCronExpression, readIsSchedulable, collectScheduledCalls, compareEntries4, compareCalls, scheduledJobCatalogHandler;
23811
24494
  var init_scheduled_job_catalog = __esm({
23812
24495
  "../mcp/dist/src/tools/scheduled-job-catalog.js"() {
@@ -23814,7 +24497,7 @@ var init_scheduled_job_catalog = __esm({
23814
24497
  init_dist();
23815
24498
  init_src();
23816
24499
  SCHEDULED_JOB_CATALOG_MAX_CLASSES = 500;
23817
- scheduledJobCatalogInputSchema = z90.object({});
24500
+ scheduledJobCatalogInputSchema = z91.object({});
23818
24501
  SCHEDULED_JOB_CATALOG_DISCLOSURE = "Scanning for System.schedule() invocations is heuristic \u2014 the v0.3 Apex scanner detects literal call sites only, NOT runtime registration via Tooling API. Runtime schedules require Tooling API access (CronTrigger / AsyncApexJob). A class flagged `isSchedulable: true` may not currently be scheduled; conversely, a class scheduled via a helper-wrapper or dynamic class load is invisible to the scanner.";
23819
24502
  readCronExpressions = (properties) => {
23820
24503
  const raw = properties["cronExpressions"];
@@ -23903,7 +24586,7 @@ var init_scheduled_job_catalog = __esm({
23903
24586
 
23904
24587
  // ../mcp/dist/src/tools/search-apex-source.js
23905
24588
  import { readFile as readFile14 } from "node:fs/promises";
23906
- import { z as z91 } from "zod";
24589
+ import { z as z92 } from "zod";
23907
24590
  var SEARCH_APEX_SOURCE_MAX_LIMIT, SEARCH_APEX_SOURCE_DEFAULT_LIMIT, APEX_SUFFIXES, searchApexSourceInputSchema, searchApexSourceHandler, buildMatcher, searchFile;
23908
24591
  var init_search_apex_source = __esm({
23909
24592
  "../mcp/dist/src/tools/search-apex-source.js"() {
@@ -23913,10 +24596,10 @@ var init_search_apex_source = __esm({
23913
24596
  SEARCH_APEX_SOURCE_MAX_LIMIT = 200;
23914
24597
  SEARCH_APEX_SOURCE_DEFAULT_LIMIT = 50;
23915
24598
  APEX_SUFFIXES = [".cls", ".trigger"];
23916
- searchApexSourceInputSchema = z91.object({
23917
- query: z91.string().min(1),
23918
- regex: z91.boolean().optional(),
23919
- limit: z91.number().int().min(1).max(SEARCH_APEX_SOURCE_MAX_LIMIT).optional()
24599
+ searchApexSourceInputSchema = z92.object({
24600
+ query: z92.string().min(1),
24601
+ regex: z92.boolean().optional(),
24602
+ limit: z92.number().int().min(1).max(SEARCH_APEX_SOURCE_MAX_LIMIT).optional()
23920
24603
  });
23921
24604
  searchApexSourceHandler = async (ctx, input2) => {
23922
24605
  const limit = input2.limit ?? SEARCH_APEX_SOURCE_DEFAULT_LIMIT;
@@ -23993,7 +24676,7 @@ var init_search_apex_source = __esm({
23993
24676
  });
23994
24677
 
23995
24678
  // ../mcp/dist/src/tools/search-components.js
23996
- import { z as z92 } from "zod";
24679
+ import { z as z93 } from "zod";
23997
24680
  var SEARCH_MAX_LIMIT2, searchComponentsInputSchema, SUGGESTIONS_NOTE, searchComponentsHandler;
23998
24681
  var init_search_components = __esm({
23999
24682
  "../mcp/dist/src/tools/search-components.js"() {
@@ -24001,10 +24684,10 @@ var init_search_components = __esm({
24001
24684
  init_dist();
24002
24685
  init_src();
24003
24686
  SEARCH_MAX_LIMIT2 = 100;
24004
- searchComponentsInputSchema = z92.object({
24005
- query: z92.string().min(1),
24006
- limit: z92.number().int().min(1).max(SEARCH_MAX_LIMIT2).optional(),
24007
- types: z92.array(z92.string()).optional()
24687
+ searchComponentsInputSchema = z93.object({
24688
+ query: z93.string().min(1),
24689
+ limit: z93.number().int().min(1).max(SEARCH_MAX_LIMIT2).optional(),
24690
+ types: z93.array(z93.string()).optional()
24008
24691
  });
24009
24692
  SUGGESTIONS_NOTE = "No exact substring matches. These are typo-tolerant, fuzzy-ranked guesses (heuristic) at what you may have meant \u2014 verify the canonical id before acting.";
24010
24693
  searchComponentsHandler = async (ctx, input2) => {
@@ -24055,7 +24738,7 @@ var init_search_components = __esm({
24055
24738
 
24056
24739
  // ../mcp/dist/src/tools/search-flow-metadata.js
24057
24740
  import { readFile as readFile15 } from "node:fs/promises";
24058
- import { z as z93 } from "zod";
24741
+ import { z as z94 } from "zod";
24059
24742
  var SEARCH_FLOW_METADATA_MAX_LIMIT, SEARCH_FLOW_METADATA_DEFAULT_LIMIT, FLOW_FILE_SUFFIX, searchFlowMetadataInputSchema, searchFlowMetadataHandler, buildMatcher2, searchFile2;
24060
24743
  var init_search_flow_metadata = __esm({
24061
24744
  "../mcp/dist/src/tools/search-flow-metadata.js"() {
@@ -24065,10 +24748,10 @@ var init_search_flow_metadata = __esm({
24065
24748
  SEARCH_FLOW_METADATA_MAX_LIMIT = 200;
24066
24749
  SEARCH_FLOW_METADATA_DEFAULT_LIMIT = 50;
24067
24750
  FLOW_FILE_SUFFIX = ".flow-meta.xml";
24068
- searchFlowMetadataInputSchema = z93.object({
24069
- query: z93.string().min(1),
24070
- regex: z93.boolean().optional(),
24071
- limit: z93.number().int().min(1).max(SEARCH_FLOW_METADATA_MAX_LIMIT).optional()
24751
+ searchFlowMetadataInputSchema = z94.object({
24752
+ query: z94.string().min(1),
24753
+ regex: z94.boolean().optional(),
24754
+ limit: z94.number().int().min(1).max(SEARCH_FLOW_METADATA_MAX_LIMIT).optional()
24072
24755
  });
24073
24756
  searchFlowMetadataHandler = async (ctx, input2) => {
24074
24757
  const limit = input2.limit ?? SEARCH_FLOW_METADATA_DEFAULT_LIMIT;
@@ -24145,14 +24828,14 @@ var init_search_flow_metadata = __esm({
24145
24828
  });
24146
24829
 
24147
24830
  // ../mcp/dist/src/tools/snapshot-trend.js
24148
- import { z as z94 } from "zod";
24831
+ import { z as z95 } from "zod";
24149
24832
  var trendInputSchema, TREND_DISCLOSURE, trendHandler, churnInputSchema, CHURN_DISCLOSURE, diffNodeSets, churnHandler;
24150
24833
  var init_snapshot_trend = __esm({
24151
24834
  "../mcp/dist/src/tools/snapshot-trend.js"() {
24152
24835
  "use strict";
24153
24836
  init_dist();
24154
24837
  init_src2();
24155
- trendInputSchema = z94.object({});
24838
+ trendInputSchema = z95.object({});
24156
24839
  TREND_DISCLOSURE = "Trend points come from persisted `sfi snapshot create` captures. Successful `sfi refresh` auto-captures a snapshot unless `meta/config.json` sets `snapshotOnRefresh: false`.";
24157
24840
  trendHandler = async (ctx, _input) => {
24158
24841
  const listed = await listSnapshots(ctx.vaultRoot);
@@ -24178,9 +24861,9 @@ var init_snapshot_trend = __esm({
24178
24861
  }
24179
24862
  });
24180
24863
  };
24181
- churnInputSchema = z94.object({
24182
- fromLabel: z94.string().min(1).optional(),
24183
- toLabel: z94.string().min(1).optional()
24864
+ churnInputSchema = z95.object({
24865
+ fromLabel: z95.string().min(1).optional(),
24866
+ toLabel: z95.string().min(1).optional()
24184
24867
  });
24185
24868
  CHURN_DISCLOSURE = 'Churn compares two persisted snapshot labels by structural id/hash only \u2014 not semantic "risk". Use `sfi.diff_snapshots` for the full slice.';
24186
24869
  diffNodeSets = (fromNodes, toNodes) => {
@@ -24318,7 +25001,7 @@ var init_coverage_trust = __esm({
24318
25001
  });
24319
25002
 
24320
25003
  // ../mcp/dist/src/tools/unassigned-permission-sets.js
24321
- import { z as z95 } from "zod";
25004
+ import { z as z96 } from "zod";
24322
25005
  var UNASSIGNED_MAX_LIMIT, UNASSIGNED_DEFAULT_LIMIT, LIST_PAGE_SIZE10, BOUNDARIES11, unassignedPermissionSetsInputSchema, namespacePrefixOf2, propertyString3, propertyBoolean2, propertyNumberOrNull, detectEnrichmentStatus, compareById2, unassignedPermissionSetsHandler;
24323
25006
  var init_unassigned_permission_sets = __esm({
24324
25007
  "../mcp/dist/src/tools/unassigned-permission-sets.js"() {
@@ -24332,10 +25015,10 @@ var init_unassigned_permission_sets = __esm({
24332
25015
  "tooling-api freshness reflects when v1.7 R2 last ran; live org assignment changes since then are not reflected. To refresh, run `sfi refresh --classify-permissions`.",
24333
25016
  "unknownAssignmentCount values require v1.7 Tooling API enrichment to resolve \u2014 they are NOT counted toward unassignedCount."
24334
25017
  ]);
24335
- unassignedPermissionSetsInputSchema = z95.object({
24336
- includeManagedPackage: z95.boolean().optional(),
24337
- includeMutingPermissionSets: z95.boolean().optional(),
24338
- limit: z95.number().int().min(1).max(UNASSIGNED_MAX_LIMIT).optional()
25018
+ unassignedPermissionSetsInputSchema = z96.object({
25019
+ includeManagedPackage: z96.boolean().optional(),
25020
+ includeMutingPermissionSets: z96.boolean().optional(),
25021
+ limit: z96.number().int().min(1).max(UNASSIGNED_MAX_LIMIT).optional()
24339
25022
  });
24340
25023
  namespacePrefixOf2 = (apiName) => {
24341
25024
  const idx = apiName.indexOf("__");
@@ -24476,7 +25159,7 @@ var init_unassigned_permission_sets = __esm({
24476
25159
  });
24477
25160
 
24478
25161
  // ../mcp/dist/src/tools/unused-components.js
24479
- import { z as z96 } from "zod";
25162
+ import { z as z97 } from "zod";
24480
25163
  var UNUSED_MAX_LIMIT, UNUSED_DEFAULT_LIMIT, LIST_PAGE_SIZE11, DEFAULT_UNUSED_TYPES, COMPONENT_TYPES3, unusedComponentsInputSchema, INVISIBLE_REFERENCES_NOTES, DEFAULT_INVISIBLE_REFERENCES_NOTE, noteForType, ENTRY_POINT_TYPES, isInactiveEntryPoint, isUnused, isTestApexClass, compareById3, compareGlobally, scanType, unusedComponentsHandler;
24481
25164
  var init_unused_components = __esm({
24482
25165
  "../mcp/dist/src/tools/unused-components.js"() {
@@ -24551,9 +25234,9 @@ var init_unused_components = __esm({
24551
25234
  "CustomMetadataRecord",
24552
25235
  "CustomSettingRecord"
24553
25236
  ];
24554
- unusedComponentsInputSchema = z96.object({
24555
- types: z96.array(z96.enum(COMPONENT_TYPES3)).optional(),
24556
- limit: z96.number().int().min(1).max(UNUSED_MAX_LIMIT).optional()
25237
+ unusedComponentsInputSchema = z97.object({
25238
+ types: z97.array(z97.enum(COMPONENT_TYPES3)).optional(),
25239
+ limit: z97.number().int().min(1).max(UNUSED_MAX_LIMIT).optional()
24557
25240
  });
24558
25241
  INVISIBLE_REFERENCES_NOTES = Object.freeze({
24559
25242
  ApexClass: "Dynamic Apex (Type.forName), reflective dispatch, and Tooling API references are invisible to the v1.x scanner; spot-check before deleting.",
@@ -24697,7 +25380,7 @@ var init_unused_components = __esm({
24697
25380
  });
24698
25381
 
24699
25382
  // ../mcp/dist/src/tools/unused-fields-deep.js
24700
- import { z as z97 } from "zod";
25383
+ import { z as z98 } from "zod";
24701
25384
  var UNUSED_FIELDS_DEEP_REQUIRED_COVERAGE, completenessForUnusedFieldsDeep, UNUSED_FIELDS_DEEP_MAX_LIMIT, UNUSED_FIELDS_DEEP_DEFAULT_LIMIT, LIST_PAGE_SIZE12, FRONTEND_REFERENCE_TYPES, INVISIBILITY_WARNINGS, BOUNDARIES12, unusedFieldsDeepInputSchema, containsApiName, propertyString4, propertyStringArray2, criteriaItemsText, layoutSectionFields, relatedListFields, buildCorpora, checkNoIncomingEdges, checkNoFormulaTextReferences, checkNoLayoutReferences, checkNoSoqlStringReferences, checkNoUnresolvedApexReferences, checkNoLwcAuraVfReferences, checkNoConditionalContextReferences, checkNoIntegrationExposure, parseParentApiName2, fieldTypeOf2, isCustomField3, namespacePrefixOf3, computeConfidence, recommendedActionFor, compareById4, unusedFieldsDeepHandler;
24702
25385
  var init_unused_fields_deep = __esm({
24703
25386
  "../mcp/dist/src/tools/unused-fields-deep.js"() {
@@ -24748,11 +25431,11 @@ var init_unused_fields_deep = __esm({
24748
25431
  BOUNDARIES12 = Object.freeze([
24749
25432
  "even after checking formula expressions, layout placements, SOQL strings, conditional contexts, LWC / Aura / VF references, and integration exposure, the scanner cannot see dynamic SOQL, LWC dynamic field access (record[fieldName]), Apex reflective access (obj.get(...)), or runtime metadata references. Treat a 'high-confidence unused' flag as 'no static evidence of use' rather than 'definitely unused.'"
24750
25433
  ]);
24751
- unusedFieldsDeepInputSchema = z97.object({
24752
- parentObjectFilter: z97.string().min(1).optional(),
24753
- excludeManagedPackage: z97.boolean().optional(),
24754
- excludeStandardFields: z97.boolean().optional(),
24755
- limit: z97.number().int().min(1).max(UNUSED_FIELDS_DEEP_MAX_LIMIT).optional()
25434
+ unusedFieldsDeepInputSchema = z98.object({
25435
+ parentObjectFilter: z98.string().min(1).optional(),
25436
+ excludeManagedPackage: z98.boolean().optional(),
25437
+ excludeStandardFields: z98.boolean().optional(),
25438
+ limit: z98.number().int().min(1).max(UNUSED_FIELDS_DEEP_MAX_LIMIT).optional()
24756
25439
  });
24757
25440
  containsApiName = (text, apiName) => text.toLowerCase().includes(apiName.toLowerCase());
24758
25441
  propertyString4 = (node, key) => {
@@ -25130,7 +25813,7 @@ var init_unused_fields_deep = __esm({
25130
25813
  });
25131
25814
 
25132
25815
  // ../mcp/dist/src/tools/tech-debt-score.js
25133
- import { z as z98 } from "zod";
25816
+ import { z as z99 } from "zod";
25134
25817
  var LIST_PAGE_SIZE13, DEFAULT_WEIGHTS, Q115_DISCLOSURE, SCORE_DIRECTION_DISCLOSURE, WEIGHT_SCHEME_DISCLOSURE, SCALE_FACTORS, ALLOWED_WEIGHT_KEYS, techDebtScoreInputSchema, bandFor, contribFor, recommendationFor, weightedScore, computeApiVersionDistribution, computeCodeQualityCounts, computeFreshnessCounts, techDebtScoreHandler;
25135
25818
  var init_tech_debt_score = __esm({
25136
25819
  "../mcp/dist/src/tools/tech-debt-score.js"() {
@@ -25176,8 +25859,8 @@ var init_tech_debt_score = __esm({
25176
25859
  "apiVersions",
25177
25860
  "unassignedGrants"
25178
25861
  ];
25179
- techDebtScoreInputSchema = z98.object({
25180
- excludeCategories: z98.array(z98.enum([
25862
+ techDebtScoreInputSchema = z99.object({
25863
+ excludeCategories: z99.array(z99.enum([
25181
25864
  "deadWeight",
25182
25865
  "legacyAutomation",
25183
25866
  "codeQuality",
@@ -25188,13 +25871,13 @@ var init_tech_debt_score = __esm({
25188
25871
  // `.passthrough()` keeps unknown weight keys in the parsed input so
25189
25872
  // the handler can surface them in a structured `invalid-query`
25190
25873
  // refusal rather than silently dropping them at the Zod boundary.
25191
- weights: z98.object({
25192
- deadWeight: z98.number().min(0).max(1).optional(),
25193
- legacyAutomation: z98.number().min(0).max(1).optional(),
25194
- codeQuality: z98.number().min(0).max(1).optional(),
25195
- freshness: z98.number().min(0).max(1).optional(),
25196
- apiVersions: z98.number().min(0).max(1).optional(),
25197
- unassignedGrants: z98.number().min(0).max(1).optional()
25874
+ weights: z99.object({
25875
+ deadWeight: z99.number().min(0).max(1).optional(),
25876
+ legacyAutomation: z99.number().min(0).max(1).optional(),
25877
+ codeQuality: z99.number().min(0).max(1).optional(),
25878
+ freshness: z99.number().min(0).max(1).optional(),
25879
+ apiVersions: z99.number().min(0).max(1).optional(),
25880
+ unassignedGrants: z99.number().min(0).max(1).optional()
25198
25881
  }).passthrough().optional()
25199
25882
  });
25200
25883
  bandFor = (score) => {
@@ -25554,12 +26237,13 @@ var init_tech_debt_score = __esm({
25554
26237
  });
25555
26238
 
25556
26239
  // ../mcp/dist/src/tools/synthesis-reports.js
25557
- import { z as z99 } from "zod";
25558
- var synthesisInputSchema, orgRiskReportInputSchema, fieldCleanupCandidatesInputSchema, automationRiskReportInputSchema, permissionRiskReportInputSchema, releaseReadinessReportInputSchema, SYNTHESIS_DISCLOSURE, rankSeverity, sortFindings, coverageTrust, orgRiskReportHandler, fieldCleanupCandidatesHandler, automationRiskReportHandler, permissionRiskReportHandler, releaseReadinessReportHandler;
26240
+ import { z as z100 } from "zod";
26241
+ var synthesisInputSchema, orgRiskReportInputSchema, fieldCleanupCandidatesInputSchema, automationRiskReportInputSchema, permissionRiskReportInputSchema, releaseReadinessReportInputSchema, SYNTHESIS_DISCLOSURE, rankSeverity, sortFindings, coverageTrust, orgRiskReportHandler, fieldCleanupCandidatesHandler, automationRiskReportHandler, SYSTEM_PERMISSION_SEVERITY, PRIVILEGE_SCAN_CAP, ESCALATION_EXAMPLE_CAP, ROSTER_CAP, grantorFinding, analyzeOverPrivilege, permissionRiskReportHandler, releaseReadinessReportHandler;
25559
26242
  var init_synthesis_reports = __esm({
25560
26243
  "../mcp/dist/src/tools/synthesis-reports.js"() {
25561
26244
  "use strict";
25562
26245
  init_dist();
26246
+ init_src();
25563
26247
  init_src2();
25564
26248
  init_coverage_trust();
25565
26249
  init_crud_fls_audit();
@@ -25569,8 +26253,8 @@ var init_synthesis_reports = __esm({
25569
26253
  init_tech_debt_score();
25570
26254
  init_unassigned_permission_sets();
25571
26255
  init_unused_fields_deep();
25572
- synthesisInputSchema = z99.object({
25573
- limit: z99.number().int().min(1).max(500).optional()
26256
+ synthesisInputSchema = z100.object({
26257
+ limit: z100.number().int().min(1).max(500).optional()
25574
26258
  });
25575
26259
  orgRiskReportInputSchema = synthesisInputSchema;
25576
26260
  fieldCleanupCandidatesInputSchema = synthesisInputSchema;
@@ -25724,9 +26408,209 @@ var init_synthesis_reports = __esm({
25724
26408
  }
25725
26409
  });
25726
26410
  };
26411
+ SYSTEM_PERMISSION_SEVERITY = {
26412
+ ModifyAllData: "critical",
26413
+ ViewAllData: "critical",
26414
+ AuthorApex: "high",
26415
+ CustomizeApplication: "high",
26416
+ ManageUsers: "high",
26417
+ ManageInternalUsers: "high",
26418
+ ManageProfilesPermissionsets: "high",
26419
+ ModifyMetadata: "high",
26420
+ ManageSharing: "high",
26421
+ ManageRoles: "high",
26422
+ ManagePasswordPolicies: "high",
26423
+ ManageLoginAccessPolicies: "high",
26424
+ ViewAllUsers: "medium",
26425
+ ViewSetup: "medium",
26426
+ PasswordNeverExpires: "medium"
26427
+ };
26428
+ PRIVILEGE_SCAN_CAP = 500;
26429
+ ESCALATION_EXAMPLE_CAP = 5;
26430
+ ROSTER_CAP = 100;
26431
+ grantorFinding = (node, type, riskyPerms, modifyAllObjects, viewAllObjects, worst, examples) => {
26432
+ if (riskyPerms.length === 0 && modifyAllObjects === 0 && viewAllObjects === 0) {
26433
+ return null;
26434
+ }
26435
+ const parts = [];
26436
+ if (riskyPerms.length > 0) {
26437
+ parts.push(`system perms: ${[...riskyPerms].sort().join(", ")}`);
26438
+ }
26439
+ if (modifyAllObjects > 0) {
26440
+ parts.push(`Modify All on ${modifyAllObjects} object(s)`);
26441
+ }
26442
+ if (viewAllObjects > 0) {
26443
+ parts.push(`View All on ${viewAllObjects} object(s)`);
26444
+ }
26445
+ return {
26446
+ rank: 0,
26447
+ severity: worst ?? "medium",
26448
+ category: "over-privilege",
26449
+ summary: `${type} ${node.apiName} grants ${parts.join("; ")}`,
26450
+ evidence: [node.id, ...examples],
26451
+ confidence: "declared"
26452
+ };
26453
+ };
26454
+ analyzeOverPrivilege = async (ctx, limit) => {
26455
+ const findings = [];
26456
+ const modifyAllDataGrantors = [];
26457
+ const viewAllDataGrantors = [];
26458
+ const scanned = { profiles: 0, permissionSets: 0, permissionSetGroups: 0 };
26459
+ const permsetRisk = /* @__PURE__ */ new Map();
26460
+ for (const type of ["Profile", "PermissionSet"]) {
26461
+ const nodesResult = await listNodesByType(ctx.graph, type, {
26462
+ limit: PRIVILEGE_SCAN_CAP
26463
+ });
26464
+ if (!nodesResult.ok)
26465
+ continue;
26466
+ for (const node of nodesResult.value) {
26467
+ if (type === "Profile")
26468
+ scanned.profiles += 1;
26469
+ else
26470
+ scanned.permissionSets += 1;
26471
+ const riskyPerms = [];
26472
+ let worst = null;
26473
+ const ups = node.properties["userPermissions"];
26474
+ if (Array.isArray(ups)) {
26475
+ for (const perm of ups) {
26476
+ if (typeof perm !== "string")
26477
+ continue;
26478
+ const sev = SYSTEM_PERMISSION_SEVERITY[perm];
26479
+ if (sev === void 0)
26480
+ continue;
26481
+ riskyPerms.push(perm);
26482
+ if (worst === null || rankSeverity(sev) > rankSeverity(worst)) {
26483
+ worst = sev;
26484
+ }
26485
+ if (perm === "ModifyAllData" && modifyAllDataGrantors.length < ROSTER_CAP) {
26486
+ modifyAllDataGrantors.push(node.id);
26487
+ }
26488
+ if (perm === "ViewAllData" && viewAllDataGrantors.length < ROSTER_CAP) {
26489
+ viewAllDataGrantors.push(node.id);
26490
+ }
26491
+ }
26492
+ }
26493
+ let modifyAllObjects = 0;
26494
+ let viewAllObjects = 0;
26495
+ const examples = [];
26496
+ const outResult = await listEdges(ctx.graph, node.id, {
26497
+ direction: "out",
26498
+ edgeType: "grantedBy"
26499
+ });
26500
+ if (outResult.ok) {
26501
+ for (const edge of outResult.value) {
26502
+ if (!edge.toId.startsWith("CustomObject:"))
26503
+ continue;
26504
+ if (edge.properties["modifyAllRecords"] === true) {
26505
+ modifyAllObjects += 1;
26506
+ if (examples.length < ESCALATION_EXAMPLE_CAP)
26507
+ examples.push(edge.toId);
26508
+ } else if (edge.properties["viewAllRecords"] === true) {
26509
+ viewAllObjects += 1;
26510
+ if (examples.length < ESCALATION_EXAMPLE_CAP)
26511
+ examples.push(edge.toId);
26512
+ }
26513
+ }
26514
+ }
26515
+ if (modifyAllObjects > 0 && (worst === null || rankSeverity("high") > rankSeverity(worst))) {
26516
+ worst = "high";
26517
+ } else if (viewAllObjects > 0 && worst === null) {
26518
+ worst = "medium";
26519
+ }
26520
+ if (type === "PermissionSet") {
26521
+ permsetRisk.set(node.id, {
26522
+ perms: riskyPerms,
26523
+ modAll: modifyAllObjects,
26524
+ viewAll: viewAllObjects
26525
+ });
26526
+ }
26527
+ const finding = grantorFinding(node, type, riskyPerms, modifyAllObjects, viewAllObjects, worst, examples);
26528
+ if (finding !== null)
26529
+ findings.push(finding);
26530
+ }
26531
+ }
26532
+ const psgResult = await listNodesByType(ctx.graph, "PermissionSetGroup", {
26533
+ limit: PRIVILEGE_SCAN_CAP
26534
+ });
26535
+ if (psgResult.ok) {
26536
+ for (const psg of psgResult.value) {
26537
+ scanned.permissionSetGroups += 1;
26538
+ const members = psg.properties["permissionSets"];
26539
+ if (!Array.isArray(members))
26540
+ continue;
26541
+ const conferred = /* @__PURE__ */ new Set();
26542
+ let aggModAll = 0;
26543
+ let aggViewAll = 0;
26544
+ const riskyMembers = [];
26545
+ for (const member of members) {
26546
+ if (typeof member !== "string")
26547
+ continue;
26548
+ const risk = permsetRisk.get(`PermissionSet:${member}`);
26549
+ if (risk === void 0)
26550
+ continue;
26551
+ if (risk.perms.length === 0 && risk.modAll === 0 && risk.viewAll === 0) {
26552
+ continue;
26553
+ }
26554
+ for (const perm of risk.perms)
26555
+ conferred.add(perm);
26556
+ aggModAll += risk.modAll;
26557
+ aggViewAll += risk.viewAll;
26558
+ if (riskyMembers.length < ESCALATION_EXAMPLE_CAP) {
26559
+ riskyMembers.push(`PermissionSet:${member}`);
26560
+ }
26561
+ }
26562
+ if (conferred.size === 0 && aggModAll === 0 && aggViewAll === 0)
26563
+ continue;
26564
+ let worst = "medium";
26565
+ if (conferred.has("ModifyAllData") || conferred.has("ViewAllData")) {
26566
+ worst = "critical";
26567
+ } else if (aggModAll > 0 || [...conferred].some((p) => SYSTEM_PERMISSION_SEVERITY[p] === "high")) {
26568
+ worst = "high";
26569
+ }
26570
+ if (conferred.has("ModifyAllData") && modifyAllDataGrantors.length < ROSTER_CAP) {
26571
+ modifyAllDataGrantors.push(psg.id);
26572
+ }
26573
+ if (conferred.has("ViewAllData") && viewAllDataGrantors.length < ROSTER_CAP) {
26574
+ viewAllDataGrantors.push(psg.id);
26575
+ }
26576
+ const muting = psg.properties["mutingPermissionSets"];
26577
+ const hasMuting = Array.isArray(muting) && muting.length > 0;
26578
+ const parts = [];
26579
+ if (conferred.size > 0) {
26580
+ parts.push(`system perms: ${[...conferred].sort().join(", ")}`);
26581
+ }
26582
+ if (aggModAll > 0) {
26583
+ parts.push(`Modify All on ${aggModAll} object(s) (aggregate)`);
26584
+ }
26585
+ if (aggViewAll > 0) {
26586
+ parts.push(`View All on ${aggViewAll} object(s) (aggregate)`);
26587
+ }
26588
+ findings.push({
26589
+ rank: 0,
26590
+ severity: worst,
26591
+ category: "over-privilege",
26592
+ summary: `PermissionSetGroup ${psg.apiName} confers via member permission set(s) ${parts.join("; ")}` + (hasMuting ? " (has a muting permission set \u2014 effective perms may be lower)" : ""),
26593
+ evidence: [psg.id, ...riskyMembers],
26594
+ confidence: "declared"
26595
+ });
26596
+ }
26597
+ }
26598
+ const ranked = [...findings].sort((a, b) => rankSeverity(b.severity) - rankSeverity(a.severity));
26599
+ return {
26600
+ findings: ranked.slice(0, limit),
26601
+ privilege: {
26602
+ modifyAllDataGrantors: [...modifyAllDataGrantors].sort(),
26603
+ viewAllDataGrantors: [...viewAllDataGrantors].sort(),
26604
+ overPrivilegedGrantorCount: findings.length,
26605
+ scanned
26606
+ }
26607
+ };
26608
+ };
25727
26609
  permissionRiskReportHandler = async (ctx, input2) => {
25728
26610
  const limit = input2.limit ?? 50;
25729
26611
  const findings = [];
26612
+ const overPriv = await analyzeOverPrivilege(ctx, limit);
26613
+ findings.push(...overPriv.findings);
25730
26614
  const unassigned = await unassignedPermissionSetsHandler(ctx, { limit });
25731
26615
  if (unassigned.ok) {
25732
26616
  for (const row of unassigned.value.data.unassigned.slice(0, limit)) {
@@ -25762,6 +26646,7 @@ var init_synthesis_reports = __esm({
25762
26646
  data: {
25763
26647
  findings: sortFindings(findings),
25764
26648
  auditTotals,
26649
+ privilege: overPriv.privilege,
25765
26650
  trust: coverageTrust(ctx),
25766
26651
  disclosure: SYNTHESIS_DISCLOSURE
25767
26652
  },
@@ -25803,8 +26688,109 @@ var init_synthesis_reports = __esm({
25803
26688
  }
25804
26689
  });
25805
26690
 
26691
+ // ../mcp/dist/src/tools/synthesize-answer.js
26692
+ import { z as z101 } from "zod";
26693
+ var CANONICAL_ID_WHOLE, CANONICAL_ID_INLINE, CAVEAT_KEY, FACT_KEY, COUNT_ARRAY_KEY, MAX_CITATIONS, MAX_BULLETS, MAX_CAVEATS, DISCLOSURE8, synthesizeAnswerInputSchema, pushCapped, collect, parseCitation, synthesizeAnswerHandler;
26694
+ var init_synthesize_answer = __esm({
26695
+ "../mcp/dist/src/tools/synthesize-answer.js"() {
26696
+ "use strict";
26697
+ init_dist();
26698
+ CANONICAL_ID_WHOLE = /^[A-Z][A-Za-z0-9]+:[^\s]+$/;
26699
+ CANONICAL_ID_INLINE = /\b[A-Z][A-Za-z0-9]+:[A-Za-z0-9_./-]*[A-Za-z0-9_]/g;
26700
+ CAVEAT_KEY = /^(disclosure|caveat|caveats|note|notes|boundary|boundaries|limitation|limitations|notModeledNote|honesty|warning|warnings)$/i;
26701
+ FACT_KEY = /^(count|total|totalCount|totalClassCount|totalFindingCount|totalGapsCount|verdict|disposition|status|coverageStatus|riskLevel|truncated|notModeled|plane|intent|confidence|matchKind|fieldLabel|fieldId|piiClassification|piiCategory)$/;
26702
+ COUNT_ARRAY_KEY = /^(grants|findings|gaps|components|candidates|edges|nodes|reasoning|viaApexAccess|fields|matches|classes|tools)$/;
26703
+ MAX_CITATIONS = 200;
26704
+ MAX_BULLETS = 60;
26705
+ MAX_CAVEATS = 40;
26706
+ DISCLOSURE8 = "This answer skeleton is composed ONLY from the JSON supplied in `input`: every citation is an id that appears verbatim in that input, every caveat is carried from it, and no components, counts, or facts were invented. When a `draft` is supplied, `hallucinatedIds` lists canonical ids in the draft that are NOT in the source \u2014 remove or re-ground them before answering. Prose wording is the caller\u2019s job; this tool guarantees grounding, not sentences.";
26707
+ synthesizeAnswerInputSchema = z101.object({
26708
+ input: z101.unknown(),
26709
+ question: z101.string().optional(),
26710
+ draft: z101.string().optional()
26711
+ });
26712
+ pushCapped = (arr, s, cap) => {
26713
+ if (arr.length < cap && !arr.includes(s))
26714
+ arr.push(s);
26715
+ };
26716
+ collect = (value, key, out) => {
26717
+ if (typeof value === "string") {
26718
+ if (out.ids.size < MAX_CITATIONS && CANONICAL_ID_WHOLE.test(value)) {
26719
+ out.ids.add(value);
26720
+ }
26721
+ if (key !== null && CAVEAT_KEY.test(key) && value.trim().length > 0) {
26722
+ pushCapped(out.caveats, value, MAX_CAVEATS);
26723
+ } else if (key !== null && FACT_KEY.test(key)) {
26724
+ pushCapped(out.bullets, `${key}: ${value}`, MAX_BULLETS);
26725
+ }
26726
+ return;
26727
+ }
26728
+ if (typeof value === "number" || typeof value === "boolean") {
26729
+ if (key !== null && FACT_KEY.test(key)) {
26730
+ pushCapped(out.bullets, `${key}: ${String(value)}`, MAX_BULLETS);
26731
+ }
26732
+ return;
26733
+ }
26734
+ if (Array.isArray(value)) {
26735
+ if (key !== null && CAVEAT_KEY.test(key)) {
26736
+ for (const item of value) {
26737
+ if (typeof item === "string" && item.trim().length > 0) {
26738
+ pushCapped(out.caveats, item, MAX_CAVEATS);
26739
+ }
26740
+ }
26741
+ } else if (key !== null && COUNT_ARRAY_KEY.test(key)) {
26742
+ pushCapped(out.bullets, `${key}: ${value.length} item(s)`, MAX_BULLETS);
26743
+ }
26744
+ for (const item of value)
26745
+ collect(item, null, out);
26746
+ return;
26747
+ }
26748
+ if (value !== null && typeof value === "object") {
26749
+ for (const [k, v] of Object.entries(value))
26750
+ collect(v, k, out);
26751
+ }
26752
+ };
26753
+ parseCitation = (id) => {
26754
+ const colon = id.indexOf(":");
26755
+ return {
26756
+ id,
26757
+ type: id.slice(0, colon),
26758
+ apiName: id.slice(colon + 1)
26759
+ };
26760
+ };
26761
+ synthesizeAnswerHandler = async (ctx, input2) => {
26762
+ const out = { ids: /* @__PURE__ */ new Set(), caveats: [], bullets: [] };
26763
+ collect(input2.input, null, out);
26764
+ const citations = [...out.ids].sort().map(parseCitation);
26765
+ let hallucinatedIds;
26766
+ let groundedIds;
26767
+ if (input2.draft !== void 0) {
26768
+ const draftIds = [...new Set(input2.draft.match(CANONICAL_ID_INLINE) ?? [])];
26769
+ groundedIds = draftIds.filter((id) => out.ids.has(id)).sort();
26770
+ hallucinatedIds = draftIds.filter((id) => !out.ids.has(id)).sort();
26771
+ }
26772
+ const qPrefix = input2.question !== void 0 && input2.question.trim().length > 0 ? `Q: ${input2.question.trim()} \u2014 ` : "";
26773
+ const summary = `${qPrefix}Grounded in the supplied tool output: ${citations.length} component(s) cited, ${out.bullets.length} key fact(s), ${out.caveats.length} caveat(s)` + (hallucinatedIds !== void 0 ? `, ${hallucinatedIds.length} ungrounded id(s) in the draft` : "") + ".";
26774
+ return ok({
26775
+ data: {
26776
+ summary,
26777
+ bullets: out.bullets,
26778
+ citations,
26779
+ caveats: out.caveats,
26780
+ ...input2.draft !== void 0 ? { hallucinatedIds: hallucinatedIds ?? [], groundedIds: groundedIds ?? [] } : {},
26781
+ disclosure: DISCLOSURE8
26782
+ },
26783
+ vaultState: {
26784
+ sourceTreeHash: ctx.manifest.sourceTreeHash,
26785
+ refreshedAt: ctx.manifest.refreshedAt
26786
+ }
26787
+ });
26788
+ };
26789
+ }
26790
+ });
26791
+
25806
26792
  // ../mcp/dist/src/tools/test-coverage-for-method.js
25807
- import { z as z100 } from "zod";
26793
+ import { z as z102 } from "zod";
25808
26794
  var COVERAGE_BFS_DEPTH, COVERAGE_EDGE_TYPES, APEX_CLASS_PREFIX7, APEX_TRIGGER_PREFIX5, COVERAGE_DISCLOSURE2, testCoverageForMethodInputSchema, isApexCallable4, isTestClass5, upstreamWalk2, testCoverageForMethodHandler;
25809
26795
  var init_test_coverage_for_method = __esm({
25810
26796
  "../mcp/dist/src/tools/test-coverage-for-method.js"() {
@@ -25820,9 +26806,9 @@ var init_test_coverage_for_method = __esm({
25820
26806
  APEX_CLASS_PREFIX7 = "ApexClass:";
25821
26807
  APEX_TRIGGER_PREFIX5 = "ApexTrigger:";
25822
26808
  COVERAGE_DISCLOSURE2 = "v2.7 test_coverage_for_method ships CLASS granularity (method-level promised in v2.7.1). The methodName input is echoed verbatim into the response but does NOT subset the upstream walk. The upstream walk follows both callsApex and dispatchesAsync incoming edges, so coverage exercised via async dispatch (Database.executeBatch, System.enqueueJob, System.schedule) is included. Dynamic dispatch (Type.forName) and reflective invocation are still invisible. BFS is capped at depth 3; coverage chains longer than 3 hops surface as uncovered even when they exist.";
25823
- testCoverageForMethodInputSchema = z100.object({
25824
- classApiName: z100.string().min(1),
25825
- methodName: z100.string().min(1).optional()
26809
+ testCoverageForMethodInputSchema = z102.object({
26810
+ classApiName: z102.string().min(1),
26811
+ methodName: z102.string().min(1).optional()
25826
26812
  });
25827
26813
  isApexCallable4 = (id) => id.startsWith(APEX_CLASS_PREFIX7) || id.startsWith(APEX_TRIGGER_PREFIX5);
25828
26814
  isTestClass5 = (node) => node.properties["isTest"] === true;
@@ -25922,8 +26908,8 @@ var init_test_coverage_for_method = __esm({
25922
26908
  });
25923
26909
 
25924
26910
  // ../mcp/dist/src/tools/test-coverage-gaps.js
25925
- import { z as z101 } from "zod";
25926
- var LIST_PAGE_SIZE14, CLASS_FILTER_MAX_SIZE, MAX_COVERAGE_DEPTH, MEANINGFUL_ASSERTION_DISCLOSURE, DYNAMIC_DISPATCH_DISCLOSURE, DEPTH_CAP_DISCLOSURE, testCoverageGapsInputSchema, collectFakeAssertions2, isTestClass6, collectCoveringTestClasses, recommendationFor2, compareGapById, emptyByStatus, testCoverageGapsHandler;
26911
+ import { z as z103 } from "zod";
26912
+ var LIST_PAGE_SIZE14, CLASS_FILTER_MAX_SIZE, TEST_COVERAGE_GAPS_MAX_LIMIT, TEST_COVERAGE_GAPS_DEFAULT_LIMIT, TEST_COVERAGE_GAPS_PAYLOAD_BUDGET_BYTES, MAX_COVERAGE_DEPTH, MEANINGFUL_ASSERTION_DISCLOSURE, DYNAMIC_DISPATCH_DISCLOSURE, DEPTH_CAP_DISCLOSURE, testCoverageGapsInputSchema, fitGapsToBudget, collectFakeAssertions2, isTestClass6, collectCoveringTestClasses, recommendationFor2, compareGapById, emptyByStatus, testCoverageGapsHandler;
25927
26913
  var init_test_coverage_gaps = __esm({
25928
26914
  "../mcp/dist/src/tools/test-coverage-gaps.js"() {
25929
26915
  "use strict";
@@ -25931,13 +26917,31 @@ var init_test_coverage_gaps = __esm({
25931
26917
  init_src();
25932
26918
  LIST_PAGE_SIZE14 = 500;
25933
26919
  CLASS_FILTER_MAX_SIZE = 500;
26920
+ TEST_COVERAGE_GAPS_MAX_LIMIT = 500;
26921
+ TEST_COVERAGE_GAPS_DEFAULT_LIMIT = 200;
26922
+ TEST_COVERAGE_GAPS_PAYLOAD_BUDGET_BYTES = 38e3;
25934
26923
  MAX_COVERAGE_DEPTH = 3;
25935
26924
  MEANINGFUL_ASSERTION_DISCLOSURE = "the meaningful-assertion heuristic recognizes System.assertEquals(expected, actual) patterns with distinct expected/actual tokens, plus System.assert(condition) with a non-literal condition. Assertions via helper methods or framework wrappers are invisible. A class flagged fake-coverage may actually have meaningful tests via a custom assertion helper.";
25936
26925
  DYNAMIC_DISPATCH_DISCLOSURE = "reachability via callsApex does NOT cover dynamic dispatch (Type.forName) or reflective invocation. A class genuinely tested via dynamic dispatch will surface as uncovered by this heuristic.";
25937
26926
  DEPTH_CAP_DISCLOSURE = `the coverage BFS is capped at depth ${MAX_COVERAGE_DEPTH}; coverage chains longer than ${MAX_COVERAGE_DEPTH} hops surface as uncovered even when they exist.`;
25938
- testCoverageGapsInputSchema = z101.object({
25939
- classFilter: z101.array(z101.string().min(1)).max(CLASS_FILTER_MAX_SIZE).optional()
26927
+ testCoverageGapsInputSchema = z103.object({
26928
+ classFilter: z103.array(z103.string().min(1)).max(CLASS_FILTER_MAX_SIZE).optional(),
26929
+ limit: z103.number().int().min(1).max(TEST_COVERAGE_GAPS_MAX_LIMIT).optional(),
26930
+ offset: z103.number().int().min(0).optional()
25940
26931
  });
26932
+ fitGapsToBudget = (gaps, budgetBytes) => {
26933
+ const kept = [];
26934
+ let used = 0;
26935
+ for (const gap of gaps) {
26936
+ const size = Buffer.byteLength(JSON.stringify(gap), "utf8") + 1;
26937
+ if (kept.length > 0 && used + size > budgetBytes) {
26938
+ return { kept, trimmed: true };
26939
+ }
26940
+ kept.push(gap);
26941
+ used += size;
26942
+ }
26943
+ return { kept, trimmed: false };
26944
+ };
25941
26945
  collectFakeAssertions2 = (node) => {
25942
26946
  const raw = node.properties["qualityIssues"];
25943
26947
  if (!Array.isArray(raw))
@@ -26096,6 +27100,12 @@ var init_test_coverage_gaps = __esm({
26096
27100
  for (const g of sorted) {
26097
27101
  byStatus[g.coverageStatus] += 1;
26098
27102
  }
27103
+ const limit = input2.limit ?? TEST_COVERAGE_GAPS_DEFAULT_LIMIT;
27104
+ const offset = input2.offset ?? 0;
27105
+ const page = sorted.slice(offset, offset + limit);
27106
+ const { kept, trimmed } = fitGapsToBudget(page, TEST_COVERAGE_GAPS_PAYLOAD_BUDGET_BYTES);
27107
+ const returnedEnd = offset + kept.length;
27108
+ const truncated = returnedEnd < sorted.length;
26099
27109
  const boundaries = sorted.length === 0 ? [] : [
26100
27110
  MEANINGFUL_ASSERTION_DISCLOSURE,
26101
27111
  DYNAMIC_DISPATCH_DISCLOSURE,
@@ -26103,10 +27113,17 @@ var init_test_coverage_gaps = __esm({
26103
27113
  ];
26104
27114
  return ok({
26105
27115
  data: {
26106
- gaps: sorted,
27116
+ gaps: kept,
26107
27117
  totalGapsCount: sorted.length,
26108
27118
  byStatus,
26109
- boundaries
27119
+ boundaries,
27120
+ limit,
27121
+ offset,
27122
+ truncated,
27123
+ ...truncated ? { nextOffset: returnedEnd } : {},
27124
+ ...trimmed ? {
27125
+ note: `Response trimmed to ${kept.length} of ${page.length} gaps (${sorted.length} total) to stay under the ~45 KB MCP response limit. Advance with offset += ${kept.length} for the rest.`
27126
+ } : {}
26110
27127
  },
26111
27128
  vaultState: {
26112
27129
  sourceTreeHash: ctx.manifest.sourceTreeHash,
@@ -26118,7 +27135,7 @@ var init_test_coverage_gaps = __esm({
26118
27135
  });
26119
27136
 
26120
27137
  // ../mcp/dist/src/tools/tests-for-change.js
26121
- import { z as z102 } from "zod";
27138
+ import { z as z104 } from "zod";
26122
27139
  var TESTS_FOR_CHANGE_BFS_DEPTH, COVERAGE_EDGE_TYPES2, APEX_CLASS_PREFIX8, APEX_TRIGGER_PREFIX6, MAX_CHANGED_ITEMS, TESTS_FOR_CHANGE_DISCLOSURE, testsForChangeInputSchema, isApexCallable5, isTestClass7, upstreamWalk3, sortIds, testsForChangeHandler;
26123
27140
  var init_tests_for_change = __esm({
26124
27141
  "../mcp/dist/src/tools/tests-for-change.js"() {
@@ -26132,8 +27149,8 @@ var init_tests_for_change = __esm({
26132
27149
  APEX_TRIGGER_PREFIX6 = "ApexTrigger:";
26133
27150
  MAX_CHANGED_ITEMS = 500;
26134
27151
  TESTS_FOR_CHANGE_DISCLOSURE = "tests_for_change selects at CLASS granularity (a changed method on a covered class still selects that class\u2019s tests; method-level resolution promised in v2.7.1). The upstream walk follows both callsApex and dispatchesAsync incoming edges, so coverage via async dispatch (Database.executeBatch, System.enqueueJob, System.schedule) is included. Dynamic dispatch (Type.forName) and reflective invocation are invisible \u2014 a test reaching the change only via reflection is missed. Managed-package test classes are invisible. BFS is capped at depth 3; coverage chains longer than 3 hops surface as uncovered even when they exist. A changed component in uncoveredChanges is UNGUARDED \u2014 running the selected set will NOT exercise it; run the full suite when any change is uncovered or you suspect a deep chain.";
26135
- testsForChangeInputSchema = z102.object({
26136
- changedComponents: z102.array(z102.string().min(1)).min(1).max(MAX_CHANGED_ITEMS)
27152
+ testsForChangeInputSchema = z104.object({
27153
+ changedComponents: z104.array(z104.string().min(1)).min(1).max(MAX_CHANGED_ITEMS)
26137
27154
  });
26138
27155
  isApexCallable5 = (id) => id.startsWith(APEX_CLASS_PREFIX8) || id.startsWith(APEX_TRIGGER_PREFIX6);
26139
27156
  isTestClass7 = (node) => node.properties["isTest"] === true;
@@ -26293,9 +27310,711 @@ var init_tests_for_change = __esm({
26293
27310
  }
26294
27311
  });
26295
27312
 
27313
+ // ../mcp/dist/src/tools/value-change-classification.js
27314
+ var CUSTOM_FIELD_PREFIX11, SEV_RANK, parseFieldId, leafName, DERIVED_DATA_TYPES, SYSTEM_AUDIT_FIELDS, classifyMutability, STANDARD_IDLOOKUP, classifyUpsertKey, IDENTITY_CATALOG, lookupIdentityCatalog, severityRank, maxSeverity, NAME_LEXICON, CONF_RANK, classifyRole, classifyField;
27315
+ var init_value_change_classification = __esm({
27316
+ "../mcp/dist/src/tools/value-change-classification.js"() {
27317
+ "use strict";
27318
+ init_field_properties();
27319
+ CUSTOM_FIELD_PREFIX11 = "CustomField:";
27320
+ SEV_RANK = {
27321
+ info: 0,
27322
+ low: 1,
27323
+ medium: 2,
27324
+ high: 3,
27325
+ critical: 4
27326
+ };
27327
+ parseFieldId = (id) => {
27328
+ if (!id.startsWith(CUSTOM_FIELD_PREFIX11))
27329
+ return null;
27330
+ const rest = id.slice(CUSTOM_FIELD_PREFIX11.length);
27331
+ const dot = rest.indexOf(".");
27332
+ if (dot < 0)
27333
+ return null;
27334
+ return { object: rest.slice(0, dot), field: rest.slice(dot + 1) };
27335
+ };
27336
+ leafName = (apiName) => {
27337
+ const i = apiName.lastIndexOf(".");
27338
+ return i >= 0 ? apiName.slice(i + 1) : apiName;
27339
+ };
27340
+ DERIVED_DATA_TYPES = /* @__PURE__ */ new Set(["Summary", "AutoNumber"]);
27341
+ SYSTEM_AUDIT_FIELDS = /* @__PURE__ */ new Set([
27342
+ "Id",
27343
+ "IsDeleted",
27344
+ "CreatedDate",
27345
+ "CreatedById",
27346
+ "LastModifiedDate",
27347
+ "LastModifiedById",
27348
+ "SystemModstamp",
27349
+ "LastActivityDate",
27350
+ "LastViewedDate",
27351
+ "LastReferencedDate"
27352
+ ]);
27353
+ classifyMutability = (node, fieldName) => {
27354
+ const formula = node.properties["formula"];
27355
+ if (typeof formula === "string" && formula.trim() !== "") {
27356
+ const expr = formula.trim();
27357
+ return {
27358
+ mutability: "derived",
27359
+ reason: `Formula field \u2014 value is computed from \`${expr}\`. Change the source field(s), not this one.`,
27360
+ sourceFormula: expr
27361
+ };
27362
+ }
27363
+ const dataType = readFieldDataType(node);
27364
+ if (DERIVED_DATA_TYPES.has(dataType)) {
27365
+ return {
27366
+ mutability: "derived",
27367
+ reason: `${dataType} field \u2014 value is system-computed, not directly editable.`
27368
+ };
27369
+ }
27370
+ const name = fieldName ?? leafName(node.apiName);
27371
+ if (SYSTEM_AUDIT_FIELDS.has(name)) {
27372
+ return {
27373
+ mutability: "derived",
27374
+ reason: `${name} is a system audit field \u2014 not user-writable.`
27375
+ };
27376
+ }
27377
+ return { mutability: "writable", reason: "Directly editable field." };
27378
+ };
27379
+ STANDARD_IDLOOKUP = {
27380
+ "*": /* @__PURE__ */ new Set(["Id"]),
27381
+ User: /* @__PURE__ */ new Set(["Username", "FederationIdentifier"])
27382
+ };
27383
+ classifyUpsertKey = (node, object, fieldName) => {
27384
+ const signals = [];
27385
+ if (node.properties["externalId"] === true)
27386
+ signals.push("externalId");
27387
+ if (node.properties["unique"] === true)
27388
+ signals.push("unique");
27389
+ const idLookup = (STANDARD_IDLOOKUP[object]?.has(fieldName) ?? false) || (STANDARD_IDLOOKUP["*"]?.has(fieldName) ?? false);
27390
+ if (idLookup)
27391
+ signals.push("idLookup");
27392
+ return { isUpsertKey: signals.length > 0, signals };
27393
+ };
27394
+ IDENTITY_CATALOG = {
27395
+ "User.Username": { role: "Login identity (global-unique)", severity: "critical" },
27396
+ "User.FederationIdentifier": { role: "SSO / SAML federation subject", severity: "high" },
27397
+ "User.EmployeeNumber": { role: "Employee number \u2014 common HRIS integration key", severity: "high" },
27398
+ "User.CommunityNickname": { role: "Experience Cloud display (org-unique)", severity: "medium" },
27399
+ "User.Alias": { role: "Short display name", severity: "low" },
27400
+ "User.Email": { role: "User email \u2014 login & notifications", severity: "high" },
27401
+ "Contact.Email": { role: "Contact email / common match key", severity: "medium" }
27402
+ };
27403
+ lookupIdentityCatalog = (object, field) => IDENTITY_CATALOG[`${object}.${field}`] ?? null;
27404
+ severityRank = (s) => SEV_RANK[s];
27405
+ maxSeverity = (a, b) => SEV_RANK[a] >= SEV_RANK[b] ? a : b;
27406
+ NAME_LEXICON = [
27407
+ { test: /(_SIS_ID|SIS_?Key|Comment_SIS_ID)__c$/i, role: "SIS integration key (name pattern)", severity: "high" },
27408
+ { test: /(Marketo|Sparkroom|Pardot)/i, role: "Marketing-platform key (name pattern)", severity: "medium" },
27409
+ { test: /(_uuid|_guid)__c$/i, role: "External UUID/GUID key (name pattern)", severity: "medium" },
27410
+ { test: /Federation/i, role: "Federation / SSO identity (name pattern)", severity: "high" }
27411
+ ];
27412
+ CONF_RANK = { potential: 0, likely: 1, confirmed: 2 };
27413
+ classifyRole = (upsert, mutability, object, fieldName) => {
27414
+ if (mutability.mutability === "derived") {
27415
+ return {
27416
+ role: "Derived / computed \u2014 value not directly changeable",
27417
+ severity: "low",
27418
+ confidence: "confirmed",
27419
+ signals: [mutability.reason]
27420
+ };
27421
+ }
27422
+ const candidates = [];
27423
+ const catalog = IDENTITY_CATALOG[`${object}.${fieldName}`];
27424
+ if (catalog !== void 0) {
27425
+ candidates.push({
27426
+ role: catalog.role,
27427
+ severity: catalog.severity,
27428
+ // Corroborated by an upsert flag → confirmed; otherwise a known but
27429
+ // org-config-dependent default → likely.
27430
+ confidence: upsert.isUpsertKey ? "confirmed" : "likely",
27431
+ signal: "standard-identity-field catalog"
27432
+ });
27433
+ }
27434
+ if (upsert.isUpsertKey) {
27435
+ candidates.push({
27436
+ role: "Integration / upsert key",
27437
+ severity: "high",
27438
+ confidence: "confirmed",
27439
+ signal: `upsert key (${upsert.signals.join(" + ")})`
27440
+ });
27441
+ }
27442
+ const lex = NAME_LEXICON.find((r) => r.test.test(fieldName));
27443
+ if (lex !== void 0) {
27444
+ candidates.push({
27445
+ role: lex.role,
27446
+ severity: lex.severity,
27447
+ confidence: "potential",
27448
+ signal: "name pattern"
27449
+ });
27450
+ }
27451
+ if (candidates.length === 0) {
27452
+ return {
27453
+ role: "Standard editable field",
27454
+ severity: "low",
27455
+ confidence: "confirmed",
27456
+ signals: []
27457
+ };
27458
+ }
27459
+ const top = candidates.reduce((best, c) => {
27460
+ if (SEV_RANK[c.severity] !== SEV_RANK[best.severity]) {
27461
+ return SEV_RANK[c.severity] > SEV_RANK[best.severity] ? c : best;
27462
+ }
27463
+ return CONF_RANK[c.confidence] > CONF_RANK[best.confidence] ? c : best;
27464
+ });
27465
+ return {
27466
+ role: top.role,
27467
+ severity: top.severity,
27468
+ confidence: top.confidence,
27469
+ signals: candidates.map((c) => c.signal)
27470
+ };
27471
+ };
27472
+ classifyField = (node) => {
27473
+ const parsed = parseFieldId(node.id);
27474
+ const object = parsed?.object ?? "";
27475
+ const field = parsed?.field ?? leafName(node.apiName);
27476
+ const mutability = classifyMutability(node, field);
27477
+ const upsertKey = classifyUpsertKey(node, object, field);
27478
+ const role = classifyRole(upsertKey, mutability, object, field);
27479
+ return { fieldId: node.id, object, field, mutability, upsertKey, role };
27480
+ };
27481
+ }
27482
+ });
27483
+
27484
+ // ../mcp/dist/src/tools/value-change-risk.js
27485
+ var SOURCE_CATEGORY, EMPTY_EDGE_SUMMARY, buildBuckets, buildDisclosures, recommendedChecksFor, overallSeverityOf, summarizeIncomingEdges, gateFederationIdentifier, detectShadowJoin, extractQuotedLiterals, normalizeExpr, findValueCouplings, assessValueChange;
27486
+ var init_value_change_risk = __esm({
27487
+ "../mcp/dist/src/tools/value-change-risk.js"() {
27488
+ "use strict";
27489
+ init_dist();
27490
+ init_src();
27491
+ init_value_change_classification();
27492
+ SOURCE_CATEGORY = {
27493
+ ValidationRule: "automation",
27494
+ WorkflowRule: "automation",
27495
+ Flow: "automation",
27496
+ ApprovalProcess: "automation",
27497
+ AssignmentRule: "automation",
27498
+ EscalationRule: "automation",
27499
+ AutoResponseRule: "automation",
27500
+ DuplicateRule: "automation",
27501
+ CustomField: "automation",
27502
+ // a formula field referencing this one
27503
+ ApexClass: "code",
27504
+ ApexTrigger: "code",
27505
+ LightningComponentBundle: "code",
27506
+ AuraDefinitionBundle: "code",
27507
+ VisualforcePage: "code",
27508
+ VisualforceComponent: "code",
27509
+ ExternalService: "integration",
27510
+ ExternalDataSource: "integration",
27511
+ NamedCredential: "integration",
27512
+ Layout: "display",
27513
+ QuickAction: "display",
27514
+ FlexiPage: "display",
27515
+ ListView: "display"
27516
+ };
27517
+ EMPTY_EDGE_SUMMARY = {
27518
+ automation: [],
27519
+ code: [],
27520
+ integration: [],
27521
+ display: []
27522
+ };
27523
+ buildBuckets = (classification, edges) => {
27524
+ const { object, field, mutability, upsertKey } = classification;
27525
+ if (mutability.mutability === "derived") {
27526
+ return [
27527
+ {
27528
+ bucket: "save-pipeline",
27529
+ severity: "info",
27530
+ confidence: "confirmed",
27531
+ summary: `Not directly changeable \u2014 ${mutability.reason}`,
27532
+ evidence: []
27533
+ }
27534
+ ];
27535
+ }
27536
+ const buckets = [];
27537
+ const identity = lookupIdentityCatalog(object, field);
27538
+ if (identity !== null) {
27539
+ buckets.push({
27540
+ bucket: "identity",
27541
+ severity: identity.severity,
27542
+ confidence: upsertKey.isUpsertKey ? "confirmed" : "likely",
27543
+ summary: `${identity.role} \u2014 changing the value alters how this record is identified/authenticated.`,
27544
+ evidence: ["standard-identity-field catalog"]
27545
+ });
27546
+ }
27547
+ const keySignals = upsertKey.signals.filter((s) => s === "externalId" || s === "idLookup");
27548
+ if (keySignals.length > 0) {
27549
+ const designedKey = keySignals.includes("externalId");
27550
+ buckets.push({
27551
+ bucket: "integration-key",
27552
+ severity: designedKey ? "high" : "medium",
27553
+ confidence: "confirmed",
27554
+ summary: designedKey ? "Upsert / integration match key (External ID) \u2014 changing the value breaks inbound matching (next sync no-matches \u2192 duplicate or error) and desyncs external systems keyed on the old value." : "Upsert-capable key (idLookup) \u2014 an integration may upsert records by this field; changing the value could break that matching.",
27555
+ evidence: [...keySignals.map((s) => `${s}=true`), ...edges.integration]
27556
+ });
27557
+ }
27558
+ if (upsertKey.signals.includes("unique")) {
27559
+ buckets.push({
27560
+ bucket: "uniqueness",
27561
+ severity: "medium",
27562
+ confidence: "confirmed",
27563
+ summary: "Unique field \u2014 a new value that collides with an existing record fails to save (DML error).",
27564
+ evidence: ["unique=true"]
27565
+ });
27566
+ }
27567
+ const automationRefs = [...edges.automation, ...edges.code];
27568
+ if (automationRefs.length > 0) {
27569
+ buckets.push({
27570
+ bucket: "automation",
27571
+ severity: edges.automation.length > 0 ? "medium" : "low",
27572
+ confidence: "likely",
27573
+ summary: `${automationRefs.length} automation/code component(s) reference this field; a value change may alter routing, validation, or sharing. Declarative value-couplings (where a rule compares this field to a literal) are surfaced when found; Apex literal comparisons remain invisible.`,
27574
+ evidence: automationRefs.slice(0, 12)
27575
+ });
27576
+ }
27577
+ const isDisplayIdentity = identity !== null && /display|nickname|alias/i.test(identity.role);
27578
+ if (edges.display.length > 0 || isDisplayIdentity) {
27579
+ buckets.push({
27580
+ bucket: "display",
27581
+ severity: "low",
27582
+ confidence: "likely",
27583
+ summary: "Appears on display surfaces (layouts / list views / UI); a value change is user-visible.",
27584
+ evidence: edges.display.slice(0, 8)
27585
+ });
27586
+ }
27587
+ buckets.push({
27588
+ bucket: "save-pipeline",
27589
+ severity: "info",
27590
+ confidence: "likely",
27591
+ summary: `Updating this field fires the save pipeline on ${object} (triggers / flows / rules) \u2014 relevant for bulk value changes.`,
27592
+ evidence: []
27593
+ });
27594
+ return buckets;
27595
+ };
27596
+ buildDisclosures = (buckets) => {
27597
+ const out = [];
27598
+ const has = (b) => buckets.some((x) => x.bucket === b);
27599
+ if (has("integration-key")) {
27600
+ out.push("The metadata declares this an upsert key, but NOT which external system upserts on it \u2014 confirm the key in your middleware / ETL (Data Loader, MuleSoft, etc.) before bulk-changing values.");
27601
+ }
27602
+ if (has("identity")) {
27603
+ out.push("If SSO maps users by this field, changing its value breaks login until the IdP is updated \u2014 verify with your IdP / My Domain SSO.");
27604
+ }
27605
+ if (has("automation")) {
27606
+ out.push("Dynamic SOQL, reflective Apex field access, and managed-package code that compare this field to a value are invisible to the scanner \u2014 the listed automations are the visible subset.");
27607
+ }
27608
+ if (has("cross-object")) {
27609
+ out.push("This key is replicated by name on other objects with no formal relationship \u2014 update the copies together, or they will silently desync.");
27610
+ }
27611
+ out.push("Reports, list-view filters, and manual processes may also key on this value.");
27612
+ return out;
27613
+ };
27614
+ recommendedChecksFor = (buckets) => {
27615
+ const checks = [];
27616
+ const has = (b) => buckets.some((x) => x.bucket === b);
27617
+ if (has("integration-key"))
27618
+ checks.push("Confirm the external upsert key + whether the external system will re-match or duplicate on the new value.");
27619
+ if (has("uniqueness"))
27620
+ checks.push("Confirm the new value does not collide with an existing record (unique constraint).");
27621
+ if (has("identity"))
27622
+ checks.push("Confirm SSO/login impact with your IdP before changing identity values.");
27623
+ if (has("automation"))
27624
+ checks.push("Review the referencing automations for value-literal comparisons that the change would flip.");
27625
+ if (has("cross-object"))
27626
+ checks.push("Update the same-named copies on the other objects together \u2014 they are value-joined with no formal relationship.");
27627
+ checks.push("For bulk changes, stage in a sandbox and watch the save pipeline (row locks, governor limits).");
27628
+ return checks;
27629
+ };
27630
+ overallSeverityOf = (buckets) => buckets.reduce((acc, b) => maxSeverity(acc, b.severity), "info");
27631
+ summarizeIncomingEdges = async (ctx, fieldId) => {
27632
+ const edgesResult = await listEdges(ctx.graph, fieldId, { direction: "in" });
27633
+ if (!edgesResult.ok) {
27634
+ return err({ kind: "internal", message: `graph query failed: ${edgesResult.error.message}` });
27635
+ }
27636
+ const automation = [];
27637
+ const code = [];
27638
+ const integration = [];
27639
+ const display = [];
27640
+ for (const edge of edgesResult.value) {
27641
+ if (edge.edgeType === "parentOf")
27642
+ continue;
27643
+ const fromResult = await getNodeById(ctx.graph, edge.fromId);
27644
+ if (!fromResult.ok) {
27645
+ return err({ kind: "internal", message: `graph query failed: ${fromResult.error.message}` });
27646
+ }
27647
+ const fromNode = fromResult.value;
27648
+ if (fromNode === null)
27649
+ continue;
27650
+ const category = SOURCE_CATEGORY[fromNode.type];
27651
+ if (category === "automation")
27652
+ automation.push(fromNode.id);
27653
+ else if (category === "code")
27654
+ code.push(fromNode.id);
27655
+ else if (category === "integration")
27656
+ integration.push(fromNode.id);
27657
+ else if (category === "display")
27658
+ display.push(fromNode.id);
27659
+ }
27660
+ const dedupe = (xs) => [...new Set(xs)].sort();
27661
+ return ok({
27662
+ automation: dedupe(automation),
27663
+ code: dedupe(code),
27664
+ integration: dedupe(integration),
27665
+ display: dedupe(display)
27666
+ });
27667
+ };
27668
+ gateFederationIdentifier = async (ctx, buckets, codeRefs) => {
27669
+ const samlRes = await listNodesByType(ctx.graph, "SamlSsoConfig", { limit: 200 });
27670
+ if (!samlRes.ok)
27671
+ return err({ kind: "internal", message: `graph query failed: ${samlRes.error.message}` });
27672
+ const apRes = await listNodesByType(ctx.graph, "AuthProvider", { limit: 200 });
27673
+ if (!apRes.ok)
27674
+ return err({ kind: "internal", message: `graph query failed: ${apRes.error.message}` });
27675
+ const configs = samlRes.value;
27676
+ const mappings = configs.map((n) => String(n.properties["identityMapping"] ?? "Username"));
27677
+ const fedConfigs = configs.filter((n) => n.properties["identityMapping"] === "FederationId");
27678
+ const oidcLogin = apRes.value.filter((ap) => {
27679
+ const rh = ap.properties["registrationHandler"];
27680
+ return typeof rh === "string" && rh.length > 0 || ap.properties["providerType"] === "OpenIdConnect";
27681
+ });
27682
+ const others = buckets.filter((b) => b.bucket !== "identity");
27683
+ let identity;
27684
+ let disclosure;
27685
+ if (fedConfigs.length > 0) {
27686
+ identity = {
27687
+ bucket: "identity",
27688
+ severity: "critical",
27689
+ confidence: "confirmed",
27690
+ summary: `SAML SSO maps users by Federation ID (${fedConfigs.length} SamlSsoConfig) \u2014 changing this value breaks SSO login until the IdP is updated.`,
27691
+ evidence: fedConfigs.map((c) => c.id)
27692
+ };
27693
+ } else if (oidcLogin.length > 0) {
27694
+ const handlers = [...new Set(oidcLogin.map((a) => a.properties["registrationHandler"]).filter((s) => typeof s === "string" && s.length > 0))];
27695
+ const handlersRefFed = handlers.filter((h) => codeRefs.includes(`ApexClass:${h}`));
27696
+ if (handlersRefFed.length > 0) {
27697
+ identity = {
27698
+ bucket: "identity",
27699
+ severity: "medium",
27700
+ confidence: "likely",
27701
+ summary: `OIDC / social SSO registration handler(s) ${handlersRefFed.join(", ")} reference FederationIdentifier (heuristic Apex scan) \u2014 it is part of the OIDC identity mapping; changing the value may break login.`,
27702
+ evidence: [...oidcLogin.map((a) => a.id), ...handlersRefFed.map((h) => `ApexClass:${h}`)]
27703
+ };
27704
+ disclosure = "The handler-to-FederationIdentifier link is from the heuristic Apex scanner (token-level); cross-method dataflow, dynamic SOQL, and reflective field access are invisible \u2014 confirm in the handler source + ThirdPartyAccountLink.";
27705
+ } else {
27706
+ identity = {
27707
+ bucket: "identity",
27708
+ severity: "medium",
27709
+ confidence: "likely",
27710
+ summary: `OIDC / social SSO login provider(s) present (${oidcLogin.map((a) => a.apiName).slice(0, 3).join(", ")}); the mapping is in an Apex registration handler \u2014 the heuristic scan found no FederationIdentifier reference there, but it may map it dynamically.`,
27711
+ evidence: oidcLogin.map((a) => a.id)
27712
+ };
27713
+ disclosure = `OIDC/social SSO maps identity in an Apex registration handler${handlers.length > 0 ? ` (${handlers.slice(0, 2).join(", ")})` : ""}; the scan saw no FederationIdentifier reference, but dynamic / reflective field access is invisible \u2014 verify the handler + ThirdPartyAccountLink before changing values.`;
27714
+ }
27715
+ } else if (configs.length > 0) {
27716
+ identity = {
27717
+ bucket: "identity",
27718
+ severity: "low",
27719
+ confidence: "confirmed",
27720
+ summary: `SAML SSO maps by ${[...new Set(mappings)].join("/")}, not Federation ID, and no OIDC login provider is present \u2014 this field is not the SSO subject here.`,
27721
+ evidence: configs.map((c) => c.id)
27722
+ };
27723
+ } else {
27724
+ identity = {
27725
+ bucket: "identity",
27726
+ severity: "low",
27727
+ confidence: "likely",
27728
+ summary: "No SAML SSO config and no OIDC/social login provider in the vault \u2014 Federation ID is not in use for SSO here.",
27729
+ evidence: []
27730
+ };
27731
+ disclosure = "No SamlSsoConfig or login-capable AuthProvider in the vault: if SSO is configured outside retrievable metadata, changing FederationIdentifier could still affect login \u2014 verify with your IdP.";
27732
+ }
27733
+ return ok({ buckets: [identity, ...others], ...disclosure !== void 0 ? { disclosure } : {} });
27734
+ };
27735
+ detectShadowJoin = async (ctx, classification) => {
27736
+ const { object, field, upsertKey } = classification;
27737
+ const keyish = upsertKey.isUpsertKey || /(_ID|_SIS_ID|Key|_uuid|_guid|Code)__c$/i.test(field);
27738
+ if (!keyish)
27739
+ return ok(null);
27740
+ const all = [];
27741
+ let offset = 0;
27742
+ for (; ; ) {
27743
+ const res = await listNodesByType(ctx.graph, "CustomField", { limit: 500, offset });
27744
+ if (!res.ok)
27745
+ return err({ kind: "internal", message: `graph query failed: ${res.error.message}` });
27746
+ all.push(...res.value);
27747
+ if (res.value.length < 500)
27748
+ break;
27749
+ offset += 500;
27750
+ }
27751
+ const objOf = (n) => parseFieldId(n.id)?.object ?? "?";
27752
+ const sameName = all.filter((n) => parseFieldId(n.id)?.field === field);
27753
+ if (sameName.length <= 1)
27754
+ return ok(null);
27755
+ const masters = sameName.filter((n) => n.properties["externalId"] === true);
27756
+ if (masters.length === 0)
27757
+ return ok(null);
27758
+ const otherObjects = sameName.filter((n) => objOf(n) !== object);
27759
+ if (otherObjects.length === 0)
27760
+ return ok(null);
27761
+ const masterObjs = [...new Set(masters.map(objOf))].sort();
27762
+ const copyObjs = [...new Set(sameName.filter((n) => n.properties["externalId"] !== true).map(objOf))].sort();
27763
+ const thisIsMaster = upsertKey.signals.includes("externalId");
27764
+ return ok({
27765
+ bucket: "cross-object",
27766
+ severity: thisIsMaster ? "high" : "medium",
27767
+ confidence: "likely",
27768
+ summary: `'${field}' is replicated on ${sameName.length} objects \u2014 External-ID key on [${masterObjs.join(", ")}]${copyObjs.length > 0 ? `, plain copy on [${copyObjs.join(", ")}]` : ""}. These are value-joined with no formal relationship; changing the value here can silently desync the copies.`,
27769
+ evidence: sameName.map((n) => n.id).sort()
27770
+ });
27771
+ };
27772
+ extractQuotedLiterals = (expr) => {
27773
+ const out = [];
27774
+ const re = /["']([^"']{1,40})["']/g;
27775
+ let m;
27776
+ while ((m = re.exec(expr)) !== null)
27777
+ out.push(m[1]);
27778
+ return out;
27779
+ };
27780
+ normalizeExpr = (expr) => typeof expr === "string" ? expr.replace(/\s+/g, " ").trim().slice(0, 120) : "";
27781
+ findValueCouplings = async (ctx, fieldId) => {
27782
+ const leaf = parseFieldId(fieldId)?.field ?? fieldId;
27783
+ const re = new RegExp(`\\b${leaf}\\b`);
27784
+ const matched = [];
27785
+ let offset = 0;
27786
+ for (; ; ) {
27787
+ const res = await listNodesByType(ctx.graph, "ConditionalContext", { limit: 500, offset });
27788
+ if (!res.ok)
27789
+ return err({ kind: "internal", message: `graph query failed: ${res.error.message}` });
27790
+ for (const n of res.value) {
27791
+ const refs = Array.isArray(n.properties["fieldRefs"]) ? n.properties["fieldRefs"] : [];
27792
+ const expr = n.properties["expression"];
27793
+ if (refs.includes(fieldId) || typeof expr === "string" && re.test(expr))
27794
+ matched.push(n);
27795
+ }
27796
+ if (res.value.length < 500)
27797
+ break;
27798
+ offset += 500;
27799
+ }
27800
+ const expressions = [...new Set(matched.map((n) => normalizeExpr(n.properties["expression"])).filter((s) => s.length > 0))].slice(0, 8);
27801
+ const literals = [...new Set(matched.flatMap((n) => extractQuotedLiterals(String(n.properties["expression"] ?? ""))))].slice(0, 12);
27802
+ return ok({ expressions, literals });
27803
+ };
27804
+ assessValueChange = async (ctx, node) => {
27805
+ const classification = classifyField(node);
27806
+ const mutable = classification.mutability.mutability === "writable";
27807
+ const edgesResult = mutable ? await summarizeIncomingEdges(ctx, node.id) : ok(EMPTY_EDGE_SUMMARY);
27808
+ if (!edgesResult.ok)
27809
+ return err(edgesResult.error);
27810
+ let buckets = buildBuckets(classification, edgesResult.value);
27811
+ const extraDisclosures = [];
27812
+ if (mutable && classification.field === "FederationIdentifier") {
27813
+ const gated = await gateFederationIdentifier(ctx, buckets, edgesResult.value.code);
27814
+ if (!gated.ok)
27815
+ return err(gated.error);
27816
+ buckets = gated.value.buckets;
27817
+ if (gated.value.disclosure !== void 0)
27818
+ extraDisclosures.push(gated.value.disclosure);
27819
+ }
27820
+ if (mutable) {
27821
+ const shadow = await detectShadowJoin(ctx, classification);
27822
+ if (!shadow.ok)
27823
+ return err(shadow.error);
27824
+ if (shadow.value !== null)
27825
+ buckets = [...buckets, shadow.value];
27826
+ }
27827
+ if (mutable) {
27828
+ const cp = await findValueCouplings(ctx, node.id);
27829
+ if (!cp.ok)
27830
+ return err(cp.error);
27831
+ if (cp.value.expressions.length > 0) {
27832
+ const existing = buckets.find((b) => b.bucket === "automation");
27833
+ const enriched = {
27834
+ bucket: "automation",
27835
+ severity: existing?.severity ?? "medium",
27836
+ confidence: "confirmed",
27837
+ summary: `Value-coupled in ${cp.value.expressions.length} declarative condition(s)${cp.value.literals.length > 0 ? ` (literals: ${cp.value.literals.slice(0, 6).join(", ")})` : ""} \u2014 changing the value may flip them. e.g. ${cp.value.expressions.slice(0, 3).join(" | ")}`,
27838
+ evidence: [...existing?.evidence ?? [], ...cp.value.expressions.slice(0, 6)]
27839
+ };
27840
+ buckets = [...buckets.filter((b) => b.bucket !== "automation"), enriched];
27841
+ }
27842
+ }
27843
+ return ok({
27844
+ fieldId: node.id,
27845
+ object: classification.object,
27846
+ field: classification.field,
27847
+ mutable,
27848
+ overallSeverity: overallSeverityOf(buckets),
27849
+ buckets,
27850
+ disclosures: mutable ? [...buildDisclosures(buckets), ...extraDisclosures] : [],
27851
+ recommendedChecks: mutable ? recommendedChecksFor(buckets) : ["Change the source field(s) instead \u2014 this value is derived."]
27852
+ });
27853
+ };
27854
+ }
27855
+ });
27856
+
27857
+ // ../mcp/dist/src/tools/value-change-audit.js
27858
+ import { z as z105 } from "zod";
27859
+ var VALUE_CHANGE_REQUIRED_COVERAGE, MAX_ROWS, PAGE, DISCLOSURE9, GLOBAL_DISCLOSURES, valueChangeAuditInputSchema, coverageCaveatFor3, listObjectFields, isCandidate, buildRow2, valueChangeAuditHandler;
27860
+ var init_value_change_audit = __esm({
27861
+ "../mcp/dist/src/tools/value-change-audit.js"() {
27862
+ "use strict";
27863
+ init_dist();
27864
+ init_src();
27865
+ init_src2();
27866
+ init_value_change_classification();
27867
+ init_value_change_risk();
27868
+ VALUE_CHANGE_REQUIRED_COVERAGE = [
27869
+ "CustomField",
27870
+ "ValidationRule",
27871
+ "Flow",
27872
+ "ApexClass",
27873
+ "ApexTrigger",
27874
+ "WorkflowRule",
27875
+ "Layout",
27876
+ "SharingRule",
27877
+ "DuplicateRule"
27878
+ ];
27879
+ MAX_ROWS = 200;
27880
+ PAGE = 500;
27881
+ DISCLOSURE9 = "value_change_audit ranks fields by the impact of changing their stored VALUE (not schema). Auto-detect surfaces upsert keys (externalId/unique/idLookup), identity-catalog fields, and name-lexicon matches \u2014 it can miss a value-sensitive field that carries none of those signals, and the per-row blast radius inherits what_if_change_field_value\u2019s boundaries (external upsert systems, the IdP side of SSO, and dynamic/managed-package code are invisible).";
27882
+ GLOBAL_DISCLOSURES = [
27883
+ "External systems that upsert on these keys live outside org metadata \u2014 confirm them in your middleware / ETL.",
27884
+ "SSO identity mapping (which field the IdP asserts) is read from SamlSsoConfig when present; verify your IdP.",
27885
+ "Reports, list-view filters, and manual processes may key on these values."
27886
+ ];
27887
+ valueChangeAuditInputSchema = z105.object({
27888
+ object: z105.string().min(1),
27889
+ fields: z105.array(z105.string()).optional(),
27890
+ verbosity: z105.enum(["summary", "detail"]).optional()
27891
+ });
27892
+ coverageCaveatFor3 = (ctx) => {
27893
+ const coverage = summarizeCoverage(ctx.manifest, VALUE_CHANGE_REQUIRED_COVERAGE);
27894
+ if (coverage.status === "complete")
27895
+ return void 0;
27896
+ const missingCoverage = coverage.missingCoverage.length > 0 ? coverage.missingCoverage : [...VALUE_CHANGE_REQUIRED_COVERAGE];
27897
+ return {
27898
+ status: coverage.status === "partial" ? "partial" : "unknown",
27899
+ missingCoverage,
27900
+ message: `Value-change audit is incomplete because the vault lacks coverage for: ${missingCoverage.join(", ")}.`
27901
+ };
27902
+ };
27903
+ listObjectFields = async (ctx, objectId) => {
27904
+ const all = [];
27905
+ let offset = 0;
27906
+ for (; ; ) {
27907
+ const res = await listNodesByType(ctx.graph, "CustomField", { parentId: objectId, limit: PAGE, offset });
27908
+ if (!res.ok)
27909
+ return err({ kind: "internal", message: `graph query failed: ${res.error.message}` });
27910
+ all.push(...res.value);
27911
+ if (res.value.length < PAGE)
27912
+ break;
27913
+ offset += PAGE;
27914
+ }
27915
+ return ok(all);
27916
+ };
27917
+ isCandidate = (node) => {
27918
+ const c = classifyField(node);
27919
+ if (c.mutability.mutability === "derived")
27920
+ return false;
27921
+ return c.upsertKey.isUpsertKey || lookupIdentityCatalog(c.object, c.field) !== null || severityRank(c.role.severity) >= severityRank("medium");
27922
+ };
27923
+ buildRow2 = (assessment, node, verbosity) => {
27924
+ const classification = classifyField(node);
27925
+ const topReasons = [...assessment.buckets].filter((b) => b.severity !== "info").sort((a, b) => severityRank(b.severity) - severityRank(a.severity)).slice(0, 3).map((b) => `${b.bucket} (${b.severity})`);
27926
+ return {
27927
+ field: assessment.field,
27928
+ fieldId: assessment.fieldId,
27929
+ role: classification.role.role,
27930
+ overallSeverity: assessment.overallSeverity,
27931
+ mutable: assessment.mutable,
27932
+ topReasons,
27933
+ confidence: classification.role.confidence,
27934
+ disclosureCount: assessment.disclosures.length,
27935
+ ...verbosity === "detail" ? { buckets: assessment.buckets } : {}
27936
+ };
27937
+ };
27938
+ valueChangeAuditHandler = async (ctx, input2) => {
27939
+ const object = input2.object;
27940
+ const objectId = `CustomObject:${object}`;
27941
+ const verbosity = input2.verbosity ?? "summary";
27942
+ let candidates = [];
27943
+ let autoDetected;
27944
+ let scannedFieldCount;
27945
+ const notFound = [];
27946
+ if (input2.fields !== void 0 && input2.fields.length > 0) {
27947
+ autoDetected = false;
27948
+ scannedFieldCount = input2.fields.length;
27949
+ for (const f of input2.fields) {
27950
+ const id = f.startsWith("CustomField:") ? f : `CustomField:${object}.${f}`;
27951
+ const nodeResult = await getNodeById(ctx.graph, id);
27952
+ if (!nodeResult.ok)
27953
+ return err({ kind: "internal", message: `graph query failed: ${nodeResult.error.message}` });
27954
+ if (nodeResult.value === null) {
27955
+ notFound.push(f);
27956
+ continue;
27957
+ }
27958
+ candidates.push(nodeResult.value);
27959
+ }
27960
+ } else {
27961
+ autoDetected = true;
27962
+ const fieldsResult = await listObjectFields(ctx, objectId);
27963
+ if (!fieldsResult.ok)
27964
+ return err(fieldsResult.error);
27965
+ scannedFieldCount = fieldsResult.value.length;
27966
+ candidates = fieldsResult.value.filter(isCandidate);
27967
+ }
27968
+ const rows = [];
27969
+ for (const node of candidates) {
27970
+ const assessmentResult = await assessValueChange(ctx, node);
27971
+ if (!assessmentResult.ok)
27972
+ return err(assessmentResult.error);
27973
+ rows.push(buildRow2(assessmentResult.value, node, verbosity));
27974
+ }
27975
+ rows.sort((a, b) => severityRank(b.overallSeverity) - severityRank(a.overallSeverity) || (a.field < b.field ? -1 : a.field > b.field ? 1 : 0));
27976
+ const truncated = rows.length > MAX_ROWS;
27977
+ const shownRows = truncated ? rows.slice(0, MAX_ROWS) : rows;
27978
+ const summary = { critical: 0, high: 0, medium: 0, low: 0, info: 0 };
27979
+ for (const r of rows)
27980
+ summary[r.overallSeverity] += 1;
27981
+ const coverageCaveat = coverageCaveatFor3(ctx);
27982
+ const confidence = rows.some((r) => r.topReasons.some((t) => t.startsWith("automation"))) ? "heuristic" : "declared";
27983
+ return ok({
27984
+ data: {
27985
+ object,
27986
+ autoDetected,
27987
+ scannedFieldCount,
27988
+ rows: shownRows,
27989
+ truncated,
27990
+ summary,
27991
+ globalDisclosures: GLOBAL_DISCLOSURES,
27992
+ ...notFound.length > 0 ? { notFound } : {},
27993
+ ...coverageCaveat !== void 0 ? { coverageCaveat } : {},
27994
+ trust: {
27995
+ provenance: "offline_snapshot",
27996
+ confidence,
27997
+ freshness: { snapshotRefreshedAt: ctx.manifest.refreshedAt },
27998
+ completeness: {
27999
+ status: coverageCaveat === void 0 ? "complete" : coverageCaveat.status,
28000
+ ...coverageCaveat !== void 0 ? { missingCoverage: coverageCaveat.missingCoverage } : {}
28001
+ },
28002
+ limitations: [DISCLOSURE9, ...coverageCaveat !== void 0 ? [coverageCaveat.message] : []]
28003
+ },
28004
+ disclosure: DISCLOSURE9
28005
+ },
28006
+ vaultState: {
28007
+ sourceTreeHash: ctx.manifest.sourceTreeHash,
28008
+ refreshedAt: ctx.manifest.refreshedAt
28009
+ }
28010
+ });
28011
+ };
28012
+ }
28013
+ });
28014
+
26296
28015
  // ../mcp/dist/src/tools/what-happens-on-save.js
26297
- import { z as z103 } from "zod";
26298
- var DISCLOSURE7, ALLOWED_EVENTS, whatHappensOnSaveInputSchema, workflowMatchesEvent2, flowMatchesEvent2, triggerMatchesEvent2, surfaceFirstCondition2, buildActions2, buildStep2, fetchParentedFirers2, fetchTriggersOnFirers2, buildAsyncSteps2, ASSIGNMENT_TYPES2, APPROVAL_TYPES2, VALIDATION_TYPES2, FLOW_TYPES2, TRIGGER_TYPES2, WORKFLOW_TYPES2, whatHappensOnSaveHandler;
28016
+ import { z as z106 } from "zod";
28017
+ var DISCLOSURE10, ALLOWED_EVENTS, whatHappensOnSaveInputSchema, workflowMatchesEvent2, flowMatchesEvent2, triggerMatchesEvent2, surfaceFirstCondition2, buildActions2, buildStep2, fetchParentedFirers2, fetchTriggersOnFirers2, buildAsyncSteps2, ASSIGNMENT_TYPES2, APPROVAL_TYPES2, VALIDATION_TYPES2, FLOW_TYPES2, TRIGGER_TYPES2, WORKFLOW_TYPES2, whatHappensOnSaveHandler;
26299
28018
  var init_what_happens_on_save = __esm({
26300
28019
  "../mcp/dist/src/tools/what-happens-on-save.js"() {
26301
28020
  "use strict";
@@ -26303,7 +28022,7 @@ var init_what_happens_on_save = __esm({
26303
28022
  init_src();
26304
28023
  init_soe_admission();
26305
28024
  init_soe_payload_bounds();
26306
- DISCLOSURE7 = "v2.0e composes the documented Salesforce order-of-execution instantiated against THIS org's extracted automation. Conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether this particular record satisfies them at runtime. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.";
28025
+ DISCLOSURE10 = "v2.0e composes the documented Salesforce order-of-execution instantiated against THIS org's extracted automation. Conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether this particular record satisfies them at runtime. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.";
26307
28026
  ALLOWED_EVENTS = [
26308
28027
  "insert",
26309
28028
  "update",
@@ -26311,13 +28030,13 @@ var init_what_happens_on_save = __esm({
26311
28030
  "delete",
26312
28031
  "undelete"
26313
28032
  ];
26314
- whatHappensOnSaveInputSchema = z103.object({
26315
- objectApiName: z103.string().min(1),
28033
+ whatHappensOnSaveInputSchema = z106.object({
28034
+ objectApiName: z106.string().min(1),
26316
28035
  // Accept "after update" / "Before Insert" etc.: lower-case and drop the
26317
28036
  // before/after timing prefix so the bare DML event matches the enum. The
26318
28037
  // SOE walker models both timings internally; the event arg selects the row.
26319
- event: z103.preprocess((v) => typeof v === "string" ? v.trim().toLowerCase().replace(/^(?:before|after)\s+/, "") : v, z103.enum(ALLOWED_EVENTS)),
26320
- recordTypeId: z103.string().min(1).optional()
28038
+ event: z106.preprocess((v) => typeof v === "string" ? v.trim().toLowerCase().replace(/^(?:before|after)\s+/, "") : v, z106.enum(ALLOWED_EVENTS)),
28039
+ recordTypeId: z106.string().min(1).optional()
26321
28040
  });
26322
28041
  workflowMatchesEvent2 = (triggerType, event) => {
26323
28042
  if (typeof triggerType !== "string")
@@ -26683,7 +28402,7 @@ var init_what_happens_on_save = __esm({
26683
28402
  conditionalSteps: conditionalCount,
26684
28403
  asyncFanOut
26685
28404
  },
26686
- disclosure: composeSoeDisclosure(DISCLOSURE7, objectModeled)
28405
+ disclosure: composeSoeDisclosure(DISCLOSURE10, objectModeled)
26687
28406
  };
26688
28407
  const budget = enforceSoeByteBudget(data, [soe]);
26689
28408
  if (budget.truncated) {
@@ -26701,26 +28420,111 @@ var init_what_happens_on_save = __esm({
26701
28420
  }
26702
28421
  });
26703
28422
 
26704
- // ../mcp/dist/src/tools/phantom-node.js
26705
- var phantomAwareNotFoundMessage;
26706
- var init_phantom_node = __esm({
26707
- "../mcp/dist/src/tools/phantom-node.js"() {
28423
+ // ../mcp/dist/src/tools/what-if-change-field-value.js
28424
+ import { z as z107 } from "zod";
28425
+ var CUSTOM_FIELD_PREFIX12, VALUE_CHANGE_REQUIRED_COVERAGE2, DISCLOSURE11, whatIfChangeFieldValueInputSchema, coverageCaveatFor4, whatIfChangeFieldValueHandler;
28426
+ var init_what_if_change_field_value = __esm({
28427
+ "../mcp/dist/src/tools/what-if-change-field-value.js"() {
26708
28428
  "use strict";
28429
+ init_dist();
26709
28430
  init_src();
26710
- phantomAwareNotFoundMessage = async (ctx, id, kindLabel) => {
26711
- const inbound = await listEdges(ctx.graph, id, { direction: "in" });
26712
- const refs = inbound.ok ? inbound.value.length : 0;
26713
- if (refs === 0) {
26714
- return `no ${kindLabel} with id ${id}`;
28431
+ init_src2();
28432
+ init_value_change_risk();
28433
+ CUSTOM_FIELD_PREFIX12 = "CustomField:";
28434
+ VALUE_CHANGE_REQUIRED_COVERAGE2 = [
28435
+ "CustomField",
28436
+ "ValidationRule",
28437
+ "Flow",
28438
+ "ApexClass",
28439
+ "ApexTrigger",
28440
+ "WorkflowRule",
28441
+ "Layout",
28442
+ "SharingRule",
28443
+ "DuplicateRule"
28444
+ ];
28445
+ DISCLOSURE11 = "Value-change impact analyzes what breaks if this field\u2019s stored VALUE changes (not its schema) \u2014 distinct from what_if_change_field_type, which walks references. Identity / integration-key / uniqueness verdicts come from the field\u2019s own metadata (externalId / unique / idLookup, identity catalog); automation buckets surface the declarative value-literal coupling (the value a rule compares this field to); Apex literal comparisons remain invisible. The vault cannot see external upsert systems, the IdP side of SSO, or dynamic / managed-package code \u2014 see disclosures.";
28446
+ whatIfChangeFieldValueInputSchema = z107.object({
28447
+ fieldId: z107.string().min(1),
28448
+ newValue: z107.string().optional()
28449
+ });
28450
+ coverageCaveatFor4 = (ctx) => {
28451
+ const coverage = summarizeCoverage(ctx.manifest, VALUE_CHANGE_REQUIRED_COVERAGE2);
28452
+ if (coverage.status === "complete")
28453
+ return void 0;
28454
+ const missingCoverage = coverage.missingCoverage.length > 0 ? coverage.missingCoverage : [...VALUE_CHANGE_REQUIRED_COVERAGE2];
28455
+ return {
28456
+ status: coverage.status === "partial" ? "partial" : "unknown",
28457
+ missingCoverage,
28458
+ message: `Value-change impact is incomplete because the vault lacks coverage for: ${missingCoverage.join(", ")}. Absence of impacts in those families means "not checked", not "safe".`
28459
+ };
28460
+ };
28461
+ whatIfChangeFieldValueHandler = async (ctx, input2) => {
28462
+ if (!input2.fieldId.startsWith(CUSTOM_FIELD_PREFIX12)) {
28463
+ return err({
28464
+ kind: "invalid-query",
28465
+ message: `fieldId must start with '${CUSTOM_FIELD_PREFIX12}'; got '${input2.fieldId}'`,
28466
+ path: "fieldId"
28467
+ });
26715
28468
  }
26716
- return `\`${id}\` is referenced by ${refs} other component(s) in this org (e.g. code, tests, or permission grants) but its own ${kindLabel} definition was never retrieved into the vault \u2014 typically a managed-package component or one outside the retrieve scope. Run \`sfi refresh\` if it should be retrievable; otherwise treat it as external.`;
28469
+ const fieldId = input2.fieldId;
28470
+ const nodeResult = await getNodeById(ctx.graph, fieldId);
28471
+ if (!nodeResult.ok) {
28472
+ return err({ kind: "internal", message: `graph query failed: ${nodeResult.error.message}` });
28473
+ }
28474
+ if (nodeResult.value === null) {
28475
+ return err({ kind: "component-not-found", message: `no field with id ${fieldId}`, path: fieldId });
28476
+ }
28477
+ const assessmentResult = await assessValueChange(ctx, nodeResult.value);
28478
+ if (!assessmentResult.ok)
28479
+ return err(assessmentResult.error);
28480
+ const a = assessmentResult.value;
28481
+ const recommendedChecks = input2.newValue !== void 0 ? [
28482
+ `Verify the proposed value "${input2.newValue}" does not collide on a unique/external-ID field and is accepted by referencing automation.`,
28483
+ ...a.recommendedChecks
28484
+ ] : a.recommendedChecks;
28485
+ const coverageCaveat = coverageCaveatFor4(ctx);
28486
+ const hasAutomation = a.buckets.some((b) => b.bucket === "automation");
28487
+ const confidence = hasAutomation ? "heuristic" : "declared";
28488
+ return ok({
28489
+ data: {
28490
+ fieldId,
28491
+ object: a.object,
28492
+ field: a.field,
28493
+ mutable: a.mutable,
28494
+ overallSeverity: a.overallSeverity,
28495
+ buckets: a.buckets,
28496
+ disclosures: a.disclosures,
28497
+ recommendedChecks,
28498
+ ...input2.newValue !== void 0 ? { newValue: input2.newValue } : {},
28499
+ ...coverageCaveat !== void 0 ? { coverageCaveat } : {},
28500
+ trust: {
28501
+ provenance: "offline_snapshot",
28502
+ confidence,
28503
+ freshness: { snapshotRefreshedAt: ctx.manifest.refreshedAt },
28504
+ completeness: {
28505
+ status: coverageCaveat === void 0 ? "complete" : coverageCaveat.status,
28506
+ ...coverageCaveat !== void 0 ? { missingCoverage: coverageCaveat.missingCoverage } : {}
28507
+ },
28508
+ limitations: [
28509
+ DISCLOSURE11,
28510
+ ...a.disclosures,
28511
+ ...coverageCaveat !== void 0 ? [coverageCaveat.message] : []
28512
+ ]
28513
+ },
28514
+ disclosure: DISCLOSURE11
28515
+ },
28516
+ vaultState: {
28517
+ sourceTreeHash: ctx.manifest.sourceTreeHash,
28518
+ refreshedAt: ctx.manifest.refreshedAt
28519
+ }
28520
+ });
26717
28521
  };
26718
28522
  }
26719
28523
  });
26720
28524
 
26721
28525
  // ../mcp/dist/src/tools/what-if-change-method-signature.js
26722
- import { z as z104 } from "zod";
26723
- var APEX_CLASS_PREFIX9, DISCLOSURE8, whatIfChangeMethodSignatureInputSchema, readMethodName, isTestClass8, buildExplanation2, classifyCaller, aggregateVerdict4, compareImpacts, compareIds, collectCallers, collectCoveringTests, whatIfChangeMethodSignatureHandler;
28526
+ import { z as z108 } from "zod";
28527
+ var APEX_CLASS_PREFIX9, DISCLOSURE12, whatIfChangeMethodSignatureInputSchema, readMethodName, isTestClass8, buildExplanation2, classifyCaller, aggregateVerdict4, compareImpacts, compareIds, collectCallers, collectCoveringTests, whatIfChangeMethodSignatureHandler;
26724
28528
  var init_what_if_change_method_signature = __esm({
26725
28529
  "../mcp/dist/src/tools/what-if-change-method-signature.js"() {
26726
28530
  "use strict";
@@ -26730,11 +28534,11 @@ var init_what_if_change_method_signature = __esm({
26730
28534
  init_coverage_trust();
26731
28535
  init_phantom_node();
26732
28536
  APEX_CLASS_PREFIX9 = "ApexClass:";
26733
- DISCLOSURE8 = "callers identified via the v1.4 apex-scanner are at heuristic confidence; dynamic dispatch via Type.forName + invoke is invisible. Test classes are identified by @isTest + naming convention (className + 'Test' suffix) and by coversTest edges; a test class that doesn't follow the naming convention and doesn't carry a @TestVisible-tagged covering reference may be missed.";
26734
- whatIfChangeMethodSignatureInputSchema = z104.object({
26735
- classApiName: z104.string().min(1),
26736
- methodName: z104.string().min(1),
26737
- newSignature: z104.string().optional()
28537
+ DISCLOSURE12 = "callers identified via the v1.4 apex-scanner are at heuristic confidence; dynamic dispatch via Type.forName + invoke is invisible. Test classes are identified by @isTest + naming convention (className + 'Test' suffix) and by coversTest edges; a test class that doesn't follow the naming convention and doesn't carry a @TestVisible-tagged covering reference may be missed.";
28538
+ whatIfChangeMethodSignatureInputSchema = z108.object({
28539
+ classApiName: z108.string().min(1),
28540
+ methodName: z108.string().min(1),
28541
+ newSignature: z108.string().optional()
26738
28542
  });
26739
28543
  readMethodName = (edge) => {
26740
28544
  const raw = edge.properties["methodName"];
@@ -26915,7 +28719,7 @@ var init_what_if_change_method_signature = __esm({
26915
28719
  verdict: coverage.verdict,
26916
28720
  ...coverage.coverageCaveat !== void 0 ? { coverageCaveat: coverage.coverageCaveat } : {},
26917
28721
  trust: coverage.trust,
26918
- disclosure: DISCLOSURE8
28722
+ disclosure: DISCLOSURE12
26919
28723
  },
26920
28724
  vaultState: {
26921
28725
  sourceTreeHash: ctx.manifest.sourceTreeHash,
@@ -26927,8 +28731,8 @@ var init_what_if_change_method_signature = __esm({
26927
28731
  });
26928
28732
 
26929
28733
  // ../mcp/dist/src/tools/what-if-deactivate-flow.js
26930
- import { z as z105 } from "zod";
26931
- var FLOW_PREFIX3, DISCLOSURE9, whatIfDeactivateFlowInputSchema, stripPrefix2, readFlowStatus2, classifyOutgoingEdge, buildExplanation3, aggregateVerdict5, compareImpacts2, collectFiringConditions, whatIfDeactivateFlowHandler;
28734
+ import { z as z109 } from "zod";
28735
+ var FLOW_PREFIX3, DISCLOSURE13, whatIfDeactivateFlowInputSchema, stripPrefix2, readFlowStatus2, classifyOutgoingEdge, buildExplanation3, aggregateVerdict5, compareImpacts2, collectFiringConditions, whatIfDeactivateFlowHandler;
26932
28736
  var init_what_if_deactivate_flow = __esm({
26933
28737
  "../mcp/dist/src/tools/what-if-deactivate-flow.js"() {
26934
28738
  "use strict";
@@ -26937,9 +28741,9 @@ var init_what_if_deactivate_flow = __esm({
26937
28741
  init_coerce_id();
26938
28742
  init_coverage_trust();
26939
28743
  FLOW_PREFIX3 = "Flow:";
26940
- DISCLOSURE9 = "v2.3 what-if analysis is composition over the v2.2 vault state. Deactivating a Flow stops every action listed in impacts; the Flow's definition remains in the org and a later reactivation restores the effects. Apex code that conditionally invokes the Flow via Flow.Interview or @InvocableMethod chains is invisible to the heuristic walker; review callers via sfi.find_code_usages targeting the Flow id before relying on this finding.";
26941
- whatIfDeactivateFlowInputSchema = z105.object({
26942
- flowId: z105.string().min(1)
28744
+ DISCLOSURE13 = "v2.3 what-if analysis is composition over the v2.2 vault state. Deactivating a Flow stops every action listed in impacts; the Flow's definition remains in the org and a later reactivation restores the effects. Apex code that conditionally invokes the Flow via Flow.Interview or @InvocableMethod chains is invisible to the heuristic walker; review callers via sfi.find_code_usages targeting the Flow id before relying on this finding.";
28745
+ whatIfDeactivateFlowInputSchema = z109.object({
28746
+ flowId: z109.string().min(1)
26943
28747
  });
26944
28748
  stripPrefix2 = (id) => {
26945
28749
  const colonIdx = id.indexOf(":");
@@ -27097,7 +28901,7 @@ var init_what_if_deactivate_flow = __esm({
27097
28901
  verdict: coverage.verdict,
27098
28902
  ...coverage.coverageCaveat !== void 0 ? { coverageCaveat: coverage.coverageCaveat } : {},
27099
28903
  trust: coverage.trust,
27100
- disclosure: DISCLOSURE9
28904
+ disclosure: DISCLOSURE13
27101
28905
  },
27102
28906
  vaultState: {
27103
28907
  sourceTreeHash: ctx.manifest.sourceTreeHash,
@@ -27109,8 +28913,8 @@ var init_what_if_deactivate_flow = __esm({
27109
28913
  });
27110
28914
 
27111
28915
  // ../mcp/dist/src/tools/what-if-disable-trigger.js
27112
- import { z as z106 } from "zod";
27113
- var APEX_TRIGGER_PREFIX7, DISCLOSURE10, whatIfDisableTriggerInputSchema, readTriggerStatus, readTriggerEvents, classifyOutgoingEdge2, buildExplanation4, aggregateVerdict6, compareImpacts3, findParentObject, whatIfDisableTriggerHandler;
28916
+ import { z as z110 } from "zod";
28917
+ var APEX_TRIGGER_PREFIX7, DISCLOSURE14, whatIfDisableTriggerInputSchema, readTriggerStatus, readTriggerEvents, classifyOutgoingEdge2, buildExplanation4, aggregateVerdict6, compareImpacts3, findParentObject, whatIfDisableTriggerHandler;
27114
28918
  var init_what_if_disable_trigger = __esm({
27115
28919
  "../mcp/dist/src/tools/what-if-disable-trigger.js"() {
27116
28920
  "use strict";
@@ -27118,9 +28922,9 @@ var init_what_if_disable_trigger = __esm({
27118
28922
  init_src();
27119
28923
  init_coverage_trust();
27120
28924
  APEX_TRIGGER_PREFIX7 = "ApexTrigger:";
27121
- DISCLOSURE10 = "v2.3 what-if analysis is composition over the v2.2 vault state. Disabling a trigger stops every action listed in impacts; the trigger definition remains in the org and a later re-enable restores the effects. The v0.3 apex-scanner's edge confidence is heuristic for most outgoing edges; spot-check the trigger body when a finding's confidence is heuristic. Indirect dispatch via trigger framework base classes (TriggerHandler, fflib) may be partially invisible to the recognizer.";
27122
- whatIfDisableTriggerInputSchema = z106.object({
27123
- triggerId: z106.string().min(1)
28925
+ DISCLOSURE14 = "v2.3 what-if analysis is composition over the v2.2 vault state. Disabling a trigger stops every action listed in impacts; the trigger definition remains in the org and a later re-enable restores the effects. The v0.3 apex-scanner's edge confidence is heuristic for most outgoing edges; spot-check the trigger body when a finding's confidence is heuristic. Indirect dispatch via trigger framework base classes (TriggerHandler, fflib) may be partially invisible to the recognizer.";
28926
+ whatIfDisableTriggerInputSchema = z110.object({
28927
+ triggerId: z110.string().min(1)
27124
28928
  });
27125
28929
  readTriggerStatus = (node) => {
27126
28930
  const raw = node.properties["status"];
@@ -27271,7 +29075,7 @@ var init_what_if_disable_trigger = __esm({
27271
29075
  verdict: coverage.verdict,
27272
29076
  ...coverage.coverageCaveat !== void 0 ? { coverageCaveat: coverage.coverageCaveat } : {},
27273
29077
  trust: coverage.trust,
27274
- disclosure: DISCLOSURE10
29078
+ disclosure: DISCLOSURE14
27275
29079
  },
27276
29080
  vaultState: {
27277
29081
  sourceTreeHash: ctx.manifest.sourceTreeHash,
@@ -27283,22 +29087,22 @@ var init_what_if_disable_trigger = __esm({
27283
29087
  });
27284
29088
 
27285
29089
  // ../mcp/dist/src/tools/what-if-merge-profiles.js
27286
- import { z as z107 } from "zod";
27287
- var PROFILE_PREFIX, DISCLOSURE11, MERGE_DEFAULT_LIMIT, MERGE_MAX_LIMIT, whatIfMergeProfilesInputSchema, readUserPermissions, readTabVisibilities, readLayoutAssignments2, readRecordTypeVisibilities, classifyGrantTarget, stripPrefix3, fieldGrantToLevel, gatherGrants, gatherProfileSettings, objectFlagsEqual, recordTypeVisibilityEqual, compareScalarMaps, compareObjectPermissions, compareFieldPermissions, compareTabVisibilities, compareLayoutAssignments, compareRecordTypeVisibilities, fetchProfile, sortConflicts, whatIfMergeProfilesHandler;
29090
+ import { z as z111 } from "zod";
29091
+ var PROFILE_PREFIX, DISCLOSURE15, MERGE_DEFAULT_LIMIT, MERGE_MAX_LIMIT, whatIfMergeProfilesInputSchema, readUserPermissions, readTabVisibilities, readLayoutAssignments2, readRecordTypeVisibilities, classifyGrantTarget, stripPrefix3, fieldGrantToLevel, gatherGrants, gatherProfileSettings, objectFlagsEqual, recordTypeVisibilityEqual, compareScalarMaps, compareObjectPermissions, compareFieldPermissions, compareTabVisibilities, compareLayoutAssignments, compareRecordTypeVisibilities, fetchProfile, sortConflicts, whatIfMergeProfilesHandler;
27288
29092
  var init_what_if_merge_profiles = __esm({
27289
29093
  "../mcp/dist/src/tools/what-if-merge-profiles.js"() {
27290
29094
  "use strict";
27291
29095
  init_dist();
27292
29096
  init_src();
27293
29097
  PROFILE_PREFIX = "Profile:";
27294
- DISCLOSURE11 = "v2.3 surfaces conflicts but does NOT auto-resolve. Recommended policies are heuristic; manually verify each conflict before applying. Profile-edition rollup (e.g., admin-level overrides) is not modeled.";
29098
+ DISCLOSURE15 = "v2.3 surfaces conflicts but does NOT auto-resolve. Recommended policies are heuristic; manually verify each conflict before applying. Profile-edition rollup (e.g., admin-level overrides) is not modeled.";
27295
29099
  MERGE_DEFAULT_LIMIT = 120;
27296
29100
  MERGE_MAX_LIMIT = 2e3;
27297
- whatIfMergeProfilesInputSchema = z107.object({
27298
- profileIdA: z107.string().min(1),
27299
- profileIdB: z107.string().min(1),
27300
- limit: z107.number().int().min(1).max(MERGE_MAX_LIMIT).optional(),
27301
- offset: z107.number().int().min(0).optional()
29101
+ whatIfMergeProfilesInputSchema = z111.object({
29102
+ profileIdA: z111.string().min(1),
29103
+ profileIdB: z111.string().min(1),
29104
+ limit: z111.number().int().min(1).max(MERGE_MAX_LIMIT).optional(),
29105
+ offset: z111.number().int().min(0).optional()
27302
29106
  });
27303
29107
  readUserPermissions = (profile) => {
27304
29108
  const raw = profile.properties["userPermissions"];
@@ -27683,7 +29487,7 @@ var init_what_if_merge_profiles = __esm({
27683
29487
  const page = conflicts.slice(offset, offset + limit);
27684
29488
  const hasMore = offset + page.length < conflicts.length;
27685
29489
  const truncated = hasMore || offset > 0;
27686
- const disclosure = truncated ? `${DISCLOSURE11} Returning conflicts ${offset}\u2013${offset + page.length} of ${conflicts.length} (page size ${limit}); summary.byCategory / byPolicy hold the COMPLETE counts. Page through the rest with offset/limit.` : DISCLOSURE11;
29490
+ const disclosure = truncated ? `${DISCLOSURE15} Returning conflicts ${offset}\u2013${offset + page.length} of ${conflicts.length} (page size ${limit}); summary.byCategory / byPolicy hold the COMPLETE counts. Page through the rest with offset/limit.` : DISCLOSURE15;
27687
29491
  return ok({
27688
29492
  data: {
27689
29493
  profileIdA,
@@ -27712,8 +29516,8 @@ var init_what_if_merge_profiles = __esm({
27712
29516
  });
27713
29517
 
27714
29518
  // ../mcp/dist/src/tools/what-if-remove-picklist-value.js
27715
- import { z as z108 } from "zod";
27716
- var CUSTOM_FIELD_PREFIX11, PICKLIST_TYPES2, PICKLIST_VALUE_COVERAGE, DISCLOSURE12, coverageCaveatFor3, whatIfRemovePicklistValueInputSchema, buildValueNeedles, containsAnyNeedle, extractHaystackTexts, classifyCategory2, buildExplanation5, findValueInConditionalContexts, aggregateVerdict7, whatIfRemovePicklistValueHandler;
29519
+ import { z as z112 } from "zod";
29520
+ var CUSTOM_FIELD_PREFIX13, PICKLIST_TYPES2, PICKLIST_VALUE_COVERAGE, DISCLOSURE16, coverageCaveatFor5, whatIfRemovePicklistValueInputSchema, buildValueNeedles, containsAnyNeedle, extractHaystackTexts, classifyCategory2, buildExplanation5, findValueInConditionalContexts, aggregateVerdict7, whatIfRemovePicklistValueHandler;
27717
29521
  var init_what_if_remove_picklist_value = __esm({
27718
29522
  "../mcp/dist/src/tools/what-if-remove-picklist-value.js"() {
27719
29523
  "use strict";
@@ -27721,7 +29525,7 @@ var init_what_if_remove_picklist_value = __esm({
27721
29525
  init_src();
27722
29526
  init_src2();
27723
29527
  init_field_properties();
27724
- CUSTOM_FIELD_PREFIX11 = "CustomField:";
29528
+ CUSTOM_FIELD_PREFIX13 = "CustomField:";
27725
29529
  PICKLIST_TYPES2 = /* @__PURE__ */ new Set([
27726
29530
  "Picklist",
27727
29531
  "MultiselectPicklist"
@@ -27739,8 +29543,8 @@ var init_what_if_remove_picklist_value = __esm({
27739
29543
  "ListView",
27740
29544
  "FlexiPage"
27741
29545
  ];
27742
- DISCLOSURE12 = "Apex code referencing the picklist value as a string literal is recognized only for static literals. Variable-based picklist comparisons (`if (account.Industry__c == myVar)`), dynamic SOQL strings, and reflective field access via `obj.get('FieldName')` are invisible to the recognizer; review dynamic comparisons separately before removing the value.";
27743
- coverageCaveatFor3 = (ctx) => {
29546
+ DISCLOSURE16 = "Apex code referencing the picklist value as a string literal is recognized only for static literals. Variable-based picklist comparisons (`if (account.Industry__c == myVar)`), dynamic SOQL strings, and reflective field access via `obj.get('FieldName')` are invisible to the recognizer; review dynamic comparisons separately before removing the value.";
29547
+ coverageCaveatFor5 = (ctx) => {
27744
29548
  const coverage = summarizeCoverage(ctx.manifest, PICKLIST_VALUE_COVERAGE);
27745
29549
  if (coverage.status === "complete")
27746
29550
  return void 0;
@@ -27751,9 +29555,9 @@ var init_what_if_remove_picklist_value = __esm({
27751
29555
  message: `Picklist-value removal impact is incomplete because the vault lacks coverage for: ${missingCoverage.join(", ")}. Absence of references in those families means "not checked", not "safe".`
27752
29556
  };
27753
29557
  };
27754
- whatIfRemovePicklistValueInputSchema = z108.object({
27755
- fieldId: z108.string().min(1),
27756
- value: z108.string().min(1)
29558
+ whatIfRemovePicklistValueInputSchema = z112.object({
29559
+ fieldId: z112.string().min(1),
29560
+ value: z112.string().min(1)
27757
29561
  });
27758
29562
  buildValueNeedles = (value) => [
27759
29563
  `'${value}'`,
@@ -27852,10 +29656,10 @@ var init_what_if_remove_picklist_value = __esm({
27852
29656
  return "risky";
27853
29657
  };
27854
29658
  whatIfRemovePicklistValueHandler = async (ctx, input2) => {
27855
- if (!input2.fieldId.startsWith(CUSTOM_FIELD_PREFIX11)) {
29659
+ if (!input2.fieldId.startsWith(CUSTOM_FIELD_PREFIX13)) {
27856
29660
  return err({
27857
29661
  kind: "invalid-query",
27858
- message: `fieldId must start with '${CUSTOM_FIELD_PREFIX11}'; got '${input2.fieldId}'`,
29662
+ message: `fieldId must start with '${CUSTOM_FIELD_PREFIX13}'; got '${input2.fieldId}'`,
27859
29663
  path: "fieldId"
27860
29664
  });
27861
29665
  }
@@ -27934,7 +29738,7 @@ var init_what_if_remove_picklist_value = __esm({
27934
29738
  }
27935
29739
  const sortedImpacts = [...impactsById.values()].sort((a, b) => a.componentId < b.componentId ? -1 : a.componentId > b.componentId ? 1 : 0);
27936
29740
  const compatibility = sortedImpacts.length === 0 ? "review" : "breaking";
27937
- const coverageCaveat = coverageCaveatFor3(ctx);
29741
+ const coverageCaveat = coverageCaveatFor5(ctx);
27938
29742
  const rawVerdict = aggregateVerdict7(sortedImpacts);
27939
29743
  const verdict = rawVerdict === "safe" && coverageCaveat !== void 0 ? "review" : rawVerdict;
27940
29744
  return ok({
@@ -27955,11 +29759,11 @@ var init_what_if_remove_picklist_value = __esm({
27955
29759
  ...coverageCaveat !== void 0 ? { missingCoverage: coverageCaveat.missingCoverage } : {}
27956
29760
  },
27957
29761
  limitations: [
27958
- DISCLOSURE12,
29762
+ DISCLOSURE16,
27959
29763
  ...coverageCaveat !== void 0 ? [coverageCaveat.message] : []
27960
29764
  ]
27961
29765
  },
27962
- disclosure: DISCLOSURE12
29766
+ disclosure: DISCLOSURE16
27963
29767
  },
27964
29768
  vaultState: {
27965
29769
  sourceTreeHash: ctx.manifest.sourceTreeHash,
@@ -27971,8 +29775,8 @@ var init_what_if_remove_picklist_value = __esm({
27971
29775
  });
27972
29776
 
27973
29777
  // ../mcp/dist/src/tools/what-if-split-profile.js
27974
- import { z as z109 } from "zod";
27975
- var PROFILE_PREFIX2, PERMISSION_SET_PREFIX, SPLIT_DEFAULT_LIMIT, SPLIT_MAX_LIMIT, DISCLOSURE13, whatIfSplitProfileInputSchema, tokenize4, makeCandidate, resolveTargets, overlapCount, keywordMatch, domainClusterMatch, assignGrant, splitGrants, fetchProfile2, sortAssignments, sortUnassigned, whatIfSplitProfileHandler;
29778
+ import { z as z113 } from "zod";
29779
+ var PROFILE_PREFIX2, PERMISSION_SET_PREFIX, SPLIT_DEFAULT_LIMIT, SPLIT_MAX_LIMIT, DISCLOSURE17, whatIfSplitProfileInputSchema, tokenize4, makeCandidate, resolveTargets, overlapCount, keywordMatch, domainClusterMatch, assignGrant, splitGrants, fetchProfile2, sortAssignments, sortUnassigned, whatIfSplitProfileHandler;
27976
29780
  var init_what_if_split_profile = __esm({
27977
29781
  "../mcp/dist/src/tools/what-if-split-profile.js"() {
27978
29782
  "use strict";
@@ -27982,12 +29786,12 @@ var init_what_if_split_profile = __esm({
27982
29786
  PERMISSION_SET_PREFIX = "PermissionSet:";
27983
29787
  SPLIT_DEFAULT_LIMIT = 120;
27984
29788
  SPLIT_MAX_LIMIT = 2e3;
27985
- DISCLOSURE13 = "v2.3 split clustering is approximate; the greedy keyword-match heuristic is fail-conservative. Review every assignment before applying \u2014 grants the heuristic could not place are surfaced in unassignedSettings rather than forced into an inappropriate target. Layout assignments, tab visibilities, and record-type visibilities are not split (Profile-only settings in the Salesforce metadata model).";
27986
- whatIfSplitProfileInputSchema = z109.object({
27987
- profileId: z109.string().min(1),
27988
- targetPermSets: z109.array(z109.string().min(1)).min(1),
27989
- limit: z109.number().int().min(1).max(SPLIT_MAX_LIMIT).optional(),
27990
- offset: z109.number().int().min(0).optional()
29789
+ DISCLOSURE17 = "v2.3 split clustering is approximate; the greedy keyword-match heuristic is fail-conservative. Review every assignment before applying \u2014 grants the heuristic could not place are surfaced in unassignedSettings rather than forced into an inappropriate target. Layout assignments, tab visibilities, and record-type visibilities are not split (Profile-only settings in the Salesforce metadata model).";
29790
+ whatIfSplitProfileInputSchema = z113.object({
29791
+ profileId: z113.string().min(1),
29792
+ targetPermSets: z113.array(z113.string().min(1)).min(1),
29793
+ limit: z113.number().int().min(1).max(SPLIT_MAX_LIMIT).optional(),
29794
+ offset: z113.number().int().min(0).optional()
27991
29795
  });
27992
29796
  tokenize4 = (raw) => {
27993
29797
  const spaced = raw.replace(/[._\-/]/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2");
@@ -28248,7 +30052,7 @@ var init_what_if_split_profile = __esm({
28248
30052
  const page = assignments.slice(offset, offset + limit);
28249
30053
  const hasMore = offset + page.length < assignments.length;
28250
30054
  const truncated = hasMore || offset > 0;
28251
- const disclosure = truncated ? `${DISCLOSURE13} Returning assignments ${offset}\u2013${offset + page.length} of ${assignments.length} (page size ${limit}); summary.byTarget holds the COMPLETE per-target counts. Page through the remaining grants with offset/limit.` : DISCLOSURE13;
30055
+ const disclosure = truncated ? `${DISCLOSURE17} Returning assignments ${offset}\u2013${offset + page.length} of ${assignments.length} (page size ${limit}); summary.byTarget holds the COMPLETE per-target counts. Page through the remaining grants with offset/limit.` : DISCLOSURE17;
28252
30056
  return ok({
28253
30057
  data: {
28254
30058
  profileId,
@@ -28276,7 +30080,7 @@ var init_what_if_split_profile = __esm({
28276
30080
  });
28277
30081
 
28278
30082
  // ../mcp/dist/src/tools/why-cant-user-see-record.js
28279
- import { z as z110 } from "zod";
30083
+ import { z as z114 } from "zod";
28280
30084
  var OWD_VISIBLE, OWD_RESTRICTED, ALL_INTERNAL_USERS_GROUP_ID, ROLE_HIERARCHY_MAX_DEPTH, whyCantUserSeeRecordInputSchema, step2, evaluateOWD, grantsReadOrBetter, evaluatePermissionGrants, walkRoleHierarchy, evaluateRoleHierarchy, buildUserMembership, fetchSharingRules, ownerRuleMatches, evaluateOwnerSharingRules, evaluateCriteriaSharingRules, listObjectChildRules, evaluateRestrictionRules, evaluateScopingRules, evaluatePermissionSetGroups, UNKNOWN_TAIL, aggregateVerdict8, PROFILE_PREFIX3, PERMISSION_SET_PREFIX2, ROLE_PREFIX, GROUP_PREFIX, QUEUE_PREFIX, coerceGroupId, coerceUserContext, whyCantUserSeeRecordHandler;
28281
30085
  var init_why_cant_user_see_record = __esm({
28282
30086
  "../mcp/dist/src/tools/why-cant-user-see-record.js"() {
@@ -28297,13 +30101,13 @@ var init_why_cant_user_see_record = __esm({
28297
30101
  ]);
28298
30102
  ALL_INTERNAL_USERS_GROUP_ID = "Group:AllInternalUsers";
28299
30103
  ROLE_HIERARCHY_MAX_DEPTH = 100;
28300
- whyCantUserSeeRecordInputSchema = z110.object({
28301
- componentId: z110.string().min(1),
28302
- userContext: z110.object({
28303
- profileId: z110.string().min(1).optional(),
28304
- permissionSetIds: z110.array(z110.string().min(1)).optional(),
28305
- roleId: z110.string().min(1).optional(),
28306
- groupIds: z110.array(z110.string().min(1)).optional()
30104
+ whyCantUserSeeRecordInputSchema = z114.object({
30105
+ componentId: z114.string().min(1),
30106
+ userContext: z114.object({
30107
+ profileId: z114.string().min(1).optional(),
30108
+ permissionSetIds: z114.array(z114.string().min(1)).optional(),
30109
+ roleId: z114.string().min(1).optional(),
30110
+ groupIds: z114.array(z114.string().min(1)).optional()
28307
30111
  }).refine((uc) => uc.profileId !== void 0 || uc.permissionSetIds !== void 0 && uc.permissionSetIds.length > 0 || uc.roleId !== void 0 || uc.groupIds !== void 0 && uc.groupIds.length > 0, {
28308
30112
  message: "userContext must supply at least one of: profileId, permissionSetIds, roleId, groupIds"
28309
30113
  })
@@ -28673,17 +30477,17 @@ var init_why_cant_user_see_record = __esm({
28673
30477
  });
28674
30478
 
28675
30479
  // ../mcp/dist/src/tools/why-field-changed.js
28676
- import { z as z111 } from "zod";
28677
- var CUSTOM_FIELD_PREFIX12, DISCLOSURE14, whyFieldChangedInputSchema, surfaceFirstCondition3, surfaceTriggerEvent, buildWriter, whyFieldChangedHandler;
30480
+ import { z as z115 } from "zod";
30481
+ var CUSTOM_FIELD_PREFIX14, DISCLOSURE18, whyFieldChangedInputSchema, surfaceFirstCondition3, surfaceTriggerEvent, buildWriter, whyFieldChangedHandler;
28678
30482
  var init_why_field_changed = __esm({
28679
30483
  "../mcp/dist/src/tools/why-field-changed.js"() {
28680
30484
  "use strict";
28681
30485
  init_dist();
28682
30486
  init_src();
28683
- CUSTOM_FIELD_PREFIX12 = "CustomField:";
28684
- DISCLOSURE14 = "v2.0e composes the documented Salesforce order-of-execution instantiated against THIS org's extracted automation. Conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether this particular record satisfies them at runtime. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.";
28685
- whyFieldChangedInputSchema = z111.object({
28686
- fieldId: z111.string().min(1)
30487
+ CUSTOM_FIELD_PREFIX14 = "CustomField:";
30488
+ DISCLOSURE18 = "v2.0e composes the documented Salesforce order-of-execution instantiated against THIS org's extracted automation. Conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether this particular record satisfies them at runtime. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.";
30489
+ whyFieldChangedInputSchema = z115.object({
30490
+ fieldId: z115.string().min(1)
28687
30491
  });
28688
30492
  surfaceFirstCondition3 = async (ctx, writerId) => {
28689
30493
  const edgesResult = await listEdges(ctx.graph, writerId, {
@@ -28733,10 +30537,10 @@ var init_why_field_changed = __esm({
28733
30537
  return ok(triggerEvent === void 0 ? withCondition : { ...withCondition, triggerEvent });
28734
30538
  };
28735
30539
  whyFieldChangedHandler = async (ctx, input2) => {
28736
- if (!input2.fieldId.startsWith(CUSTOM_FIELD_PREFIX12)) {
30540
+ if (!input2.fieldId.startsWith(CUSTOM_FIELD_PREFIX14)) {
28737
30541
  return err({
28738
30542
  kind: "invalid-query",
28739
- message: `fieldId must start with '${CUSTOM_FIELD_PREFIX12}'; got '${input2.fieldId}'`,
30543
+ message: `fieldId must start with '${CUSTOM_FIELD_PREFIX14}'; got '${input2.fieldId}'`,
28740
30544
  path: "fieldId"
28741
30545
  });
28742
30546
  }
@@ -28796,7 +30600,7 @@ var init_why_field_changed = __esm({
28796
30600
  fieldId,
28797
30601
  writers: sortedWriters,
28798
30602
  summary: { declaredCount, heuristicCount },
28799
- disclosure: DISCLOSURE14
30603
+ disclosure: DISCLOSURE18
28800
30604
  },
28801
30605
  vaultState: {
28802
30606
  sourceTreeHash: ctx.manifest.sourceTreeHash,
@@ -28817,7 +30621,7 @@ __export(tools_exports, {
28817
30621
  registerTools: () => registerTools
28818
30622
  });
28819
30623
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
28820
- var SEARCH_COMPONENTS_INPUT_SCHEMA, CAPABILITIES_INPUT_SCHEMA, ORG_PULSE_INPUT_SCHEMA, FLEET_FIND_INPUT_SCHEMA, RESOLVE_INPUT_SCHEMA, GET_COMPONENT_INPUT_SCHEMA, GET_EDGES_INPUT_SCHEMA, LIST_COMPONENTS_INPUT_SCHEMA, GET_SUBGRAPH_INPUT_SCHEMA, SEARCH_APEX_SOURCE_INPUT_SCHEMA, SEARCH_FLOW_METADATA_INPUT_SCHEMA, NAMING_CONVENTION_REPORT_INPUT_SCHEMA, GET_MANIFEST_INPUT_SCHEMA, COVERAGE_REPORT_INPUT_SCHEMA, BASELINE_ACKNOWLEDGE_INPUT_SCHEMA, BASELINE_STATUS_INPUT_SCHEMA, TREND_INPUT_SCHEMA, CHURN_INPUT_SCHEMA, LIVE_ENABLED_PROPERTY, LIVE_DESCRIBE_INPUT_SCHEMA, LIVE_COUNT_INPUT_SCHEMA, LIVE_SAMPLE_INPUT_SCHEMA, LIVE_FIELD_POPULATION_INPUT_SCHEMA, LIVE_ORG_LIMITS_INPUT_SCHEMA, LIVE_INACTIVE_USERS_INPUT_SCHEMA, LIVE_LICENSE_USAGE_INPUT_SCHEMA, LIVE_GROUP_COUNT_INPUT_SCHEMA, LIVE_STALE_RECORDS_INPUT_SCHEMA, LIVE_RECENT_ACTIVITY_INPUT_SCHEMA, LIVE_AGGREGATE_INPUT_SCHEMA, LIVE_DUPLICATE_CHECK_INPUT_SCHEMA, LIVE_OWNER_BREAKDOWN_INPUT_SCHEMA, LIVE_STORAGE_BY_OBJECT_INPUT_SCHEMA, LIVE_REPORT_USAGE_INPUT_SCHEMA, LIVE_FOLDER_ACCESS_INPUT_SCHEMA, LIVE_EMAIL_TEMPLATE_USAGE_INPUT_SCHEMA, LIVE_ORG_HEALTH_INPUT_SCHEMA, LIVE_CONSENT_INPUT_SCHEMA, ROUTE_QUESTION_INPUT_SCHEMA, SYNTHESIS_INPUT_SCHEMA, HEALTH_CHECK_INPUT_SCHEMA, GET_IMPACT_INPUT_SCHEMA, FIND_FORMULA_REFERENCES_INPUT_SCHEMA, FIND_APEX_USAGES_INPUT_SCHEMA, FIND_CODE_USAGES_INPUT_SCHEMA, WHY_CANT_USER_SEE_RECORD_INPUT_SCHEMA, LAYOUT_FOR_USER_INPUT_SCHEMA, INTEGRATION_MAP_INPUT_SCHEMA, EVENT_SUBSCRIBERS_INPUT_SCHEMA, LOOKUP_RECORD_INPUT_SCHEMA, EXPLAIN_FIELD_INPUT_SCHEMA, SAFE_TO_DELETE_FIELD_INPUT_SCHEMA, UNUSED_COMPONENTS_INPUT_SCHEMA, DIFF_SNAPSHOTS_INPUT_SCHEMA, COMPARE_COMPONENTS_INPUT_SCHEMA, PII_INVENTORY_INPUT_SCHEMA, FIELD_ACCESS_AUDIT_INPUT_SCHEMA, FIELD_360_INPUT_SCHEMA, FIELD_LINEAGE_INPUT_SCHEMA, ORG_OVERVIEW_INPUT_SCHEMA, DOMAIN_CLUSTERS_INPUT_SCHEMA, CHANGED_SINCE_INPUT_SCHEMA, LAST_MODIFIED_INPUT_SCHEMA, WHAT_HAPPENS_ON_SAVE_INPUT_SCHEMA, WHY_FIELD_CHANGED_INPUT_SCHEMA, ORDER_OF_EXECUTION_INPUT_SCHEMA, EXPLAIN_FLOW_INPUT_SCHEMA, EXPLAIN_APEX_METHOD_INPUT_SCHEMA, EXPLAIN_FORMULA_INPUT_SCHEMA, UNUSED_FIELDS_DEEP_INPUT_SCHEMA, PROCESS_BUILDER_MIGRATION_CANDIDATES_INPUT_SCHEMA, UNASSIGNED_PERMISSION_SETS_INPUT_SCHEMA, EMPTY_QUEUES_AND_GROUPS_INPUT_SCHEMA, TECH_DEBT_SCORE_INPUT_SCHEMA, CODE_QUALITY_AUDIT_INPUT_SCHEMA, GOVERNOR_LIMIT_RISKS_INPUT_SCHEMA, FIND_HARDCODED_VALUES_INPUT_SCHEMA, CRUD_FLS_AUDIT_INPUT_SCHEMA, TEST_COVERAGE_GAPS_INPUT_SCHEMA, WHAT_IF_CHANGE_FIELD_TYPE_INPUT_SCHEMA, WHAT_IF_REMOVE_PICKLIST_VALUE_INPUT_SCHEMA, WHAT_IF_MAKE_FIELD_REQUIRED_INPUT_SCHEMA, WHAT_IF_DEACTIVATE_FLOW_INPUT_SCHEMA, WHAT_IF_DISABLE_TRIGGER_INPUT_SCHEMA, WHAT_IF_CHANGE_METHOD_SIGNATURE_INPUT_SCHEMA, WHAT_IF_MERGE_PROFILES_INPUT_SCHEMA, WHAT_IF_SPLIT_PROFILE_INPUT_SCHEMA, GENERATE_DATA_DICTIONARY_INPUT_SCHEMA, GENERATE_ADMIN_HANDBOOK_INPUT_SCHEMA, GENERATE_ARCHITECTURE_OVERVIEW_INPUT_SCHEMA, GENERATE_SHARING_SUMMARY_INPUT_SCHEMA, GENERATE_COMPLIANCE_REPORT_INPUT_SCHEMA, GENERATE_ONBOARDING_DOC_INPUT_SCHEMA, CALL_GRAPH_INPUT_SCHEMA, DOWNSTREAM_EFFECTS_INPUT_SCHEMA, TEST_COVERAGE_FOR_METHOD_INPUT_SCHEMA, MEANINGFUL_TEST_AUDIT_INPUT_SCHEMA, METHOD_REACHABILITY_INPUT_SCHEMA, TESTS_FOR_CHANGE_INPUT_SCHEMA, PACKAGE_IMPACT_INPUT_SCHEMA, CDC_SUBSCRIBERS_INPUT_SCHEMA, ASYNC_CHAIN_DEPTH_INPUT_SCHEMA, SCHEDULED_JOB_CATALOG_INPUT_SCHEMA, OUTBOUND_MESSAGE_CATALOG_INPUT_SCHEMA, ENDPOINT_CATALOG_INPUT_SCHEMA, FIELD_MEANING_INPUT_SCHEMA, DISAMBIGUATE_CONCEPTS_INPUT_SCHEMA, FIELD_PROVENANCE_INPUT_SCHEMA, FIND_FIELD_ANYWHERE_INPUT_SCHEMA, FIND_SEMANTIC_FIELD_INPUT_SCHEMA, FIND_HARDCODED_VALUES_ANYWHERE_INPUT_SCHEMA, FIND_CLONE_PATTERNS_INPUT_SCHEMA, FIND_DEAD_CODE_INPUT_SCHEMA, CPQ_RULE_CHAIN_INPUT_SCHEMA, CPQ_QUOTE_TEMPLATE_BREAKDOWN_INPUT_SCHEMA, CPQ_DEPENDENCY_MAP_INPUT_SCHEMA, COMPARE_VAULTS_INPUT_SCHEMA, COMPARE_OBJECT_ACROSS_VAULTS_INPUT_SCHEMA, COMPARE_PROFILE_ACROSS_VAULTS_INPUT_SCHEMA, FIELD_MAPPING_BETWEEN_OBJECTS_INPUT_SCHEMA, INTEGRATION_PROCEDURE_CHAIN_INPUT_SCHEMA, OMNISCRIPT_FLOW_INPUT_SCHEMA, OMNIUICARD_WIDGET_BREAKDOWN_INPUT_SCHEMA, DATATRANSFORM_FIELD_MAP_INPUT_SCHEMA, DECISION_TABLE_BROWSE_INPUT_SCHEMA, FIND_DEPENDENCY_CYCLES_INPUT_SCHEMA, APEX_TEST_COVERAGE_INPUT_SCHEMA, AUTOMATION_BUILD_ADVISOR_INPUT_SCHEMA, APEX_BUILD_ADVISOR_INPUT_SCHEMA, FIELD_CHANGE_ADVISOR_INPUT_SCHEMA, LIVE_DRIFT_CHECK_INPUT_SCHEMA, ORG_HISTORY_INPUT_SCHEMA, V01_TOOLS, KNOWN_TOOL_NAMES, dispatchTool, runTool, registerTools, MAX_RESPONSE_BYTES, jsonResult;
30624
+ var SEARCH_COMPONENTS_INPUT_SCHEMA, CAPABILITIES_INPUT_SCHEMA, SYNTHESIZE_ANSWER_INPUT_SCHEMA, ORG_PULSE_INPUT_SCHEMA, FLEET_FIND_INPUT_SCHEMA, RESOLVE_INPUT_SCHEMA, GET_COMPONENT_INPUT_SCHEMA, GET_EDGES_INPUT_SCHEMA, LIST_COMPONENTS_INPUT_SCHEMA, GET_SUBGRAPH_INPUT_SCHEMA, SEARCH_APEX_SOURCE_INPUT_SCHEMA, SEARCH_FLOW_METADATA_INPUT_SCHEMA, NAMING_CONVENTION_REPORT_INPUT_SCHEMA, GET_MANIFEST_INPUT_SCHEMA, COVERAGE_REPORT_INPUT_SCHEMA, BASELINE_ACKNOWLEDGE_INPUT_SCHEMA, BASELINE_STATUS_INPUT_SCHEMA, TREND_INPUT_SCHEMA, CHURN_INPUT_SCHEMA, LIVE_ENABLED_PROPERTY, LIVE_DESCRIBE_INPUT_SCHEMA, LIVE_COUNT_INPUT_SCHEMA, LIVE_SAMPLE_INPUT_SCHEMA, LIVE_FIELD_POPULATION_INPUT_SCHEMA, LIVE_ORG_LIMITS_INPUT_SCHEMA, LIVE_INACTIVE_USERS_INPUT_SCHEMA, LIVE_LICENSE_USAGE_INPUT_SCHEMA, LIVE_GROUP_COUNT_INPUT_SCHEMA, LIVE_STALE_RECORDS_INPUT_SCHEMA, LIVE_RECENT_ACTIVITY_INPUT_SCHEMA, LIVE_AGGREGATE_INPUT_SCHEMA, LIVE_DUPLICATE_CHECK_INPUT_SCHEMA, LIVE_OWNER_BREAKDOWN_INPUT_SCHEMA, LIVE_STORAGE_BY_OBJECT_INPUT_SCHEMA, LIVE_REPORT_USAGE_INPUT_SCHEMA, LIVE_FOLDER_ACCESS_INPUT_SCHEMA, LIVE_EMAIL_TEMPLATE_USAGE_INPUT_SCHEMA, LIVE_ORG_HEALTH_INPUT_SCHEMA, LIVE_CONSENT_INPUT_SCHEMA, ROUTE_QUESTION_INPUT_SCHEMA, SYNTHESIS_INPUT_SCHEMA, HEALTH_CHECK_INPUT_SCHEMA, GET_IMPACT_INPUT_SCHEMA, FIND_FORMULA_REFERENCES_INPUT_SCHEMA, FIND_APEX_USAGES_INPUT_SCHEMA, FIND_CODE_USAGES_INPUT_SCHEMA, WHY_CANT_USER_SEE_RECORD_INPUT_SCHEMA, LAYOUT_FOR_USER_INPUT_SCHEMA, INTEGRATION_MAP_INPUT_SCHEMA, EVENT_SUBSCRIBERS_INPUT_SCHEMA, LOOKUP_RECORD_INPUT_SCHEMA, GUIDANCE_INPUT_SCHEMA, EXPLAIN_FIELD_INPUT_SCHEMA, SAFE_TO_DELETE_FIELD_INPUT_SCHEMA, UNUSED_COMPONENTS_INPUT_SCHEMA, DIFF_SNAPSHOTS_INPUT_SCHEMA, COMPARE_COMPONENTS_INPUT_SCHEMA, PII_INVENTORY_INPUT_SCHEMA, FIELD_ACCESS_AUDIT_INPUT_SCHEMA, FIELD_360_INPUT_SCHEMA, FIELD_LINEAGE_INPUT_SCHEMA, ORG_OVERVIEW_INPUT_SCHEMA, DOMAIN_CLUSTERS_INPUT_SCHEMA, CHANGED_SINCE_INPUT_SCHEMA, LAST_MODIFIED_INPUT_SCHEMA, WHAT_HAPPENS_ON_SAVE_INPUT_SCHEMA, WHY_FIELD_CHANGED_INPUT_SCHEMA, ORDER_OF_EXECUTION_INPUT_SCHEMA, EXPLAIN_FLOW_INPUT_SCHEMA, EXPLAIN_APEX_METHOD_INPUT_SCHEMA, EXPLAIN_FORMULA_INPUT_SCHEMA, UNUSED_FIELDS_DEEP_INPUT_SCHEMA, PROCESS_BUILDER_MIGRATION_CANDIDATES_INPUT_SCHEMA, UNASSIGNED_PERMISSION_SETS_INPUT_SCHEMA, EMPTY_QUEUES_AND_GROUPS_INPUT_SCHEMA, TECH_DEBT_SCORE_INPUT_SCHEMA, CODE_QUALITY_AUDIT_INPUT_SCHEMA, GOVERNOR_LIMIT_RISKS_INPUT_SCHEMA, FIND_HARDCODED_VALUES_INPUT_SCHEMA, CRUD_FLS_AUDIT_INPUT_SCHEMA, TEST_COVERAGE_GAPS_INPUT_SCHEMA, WHAT_IF_CHANGE_FIELD_VALUE_INPUT_SCHEMA, VALUE_CHANGE_AUDIT_INPUT_SCHEMA, WHAT_IF_CHANGE_FIELD_TYPE_INPUT_SCHEMA, WHAT_IF_REMOVE_PICKLIST_VALUE_INPUT_SCHEMA, WHAT_IF_MAKE_FIELD_REQUIRED_INPUT_SCHEMA, WHAT_IF_DEACTIVATE_FLOW_INPUT_SCHEMA, WHAT_IF_DISABLE_TRIGGER_INPUT_SCHEMA, WHAT_IF_CHANGE_METHOD_SIGNATURE_INPUT_SCHEMA, WHAT_IF_MERGE_PROFILES_INPUT_SCHEMA, WHAT_IF_SPLIT_PROFILE_INPUT_SCHEMA, GENERATE_DATA_DICTIONARY_INPUT_SCHEMA, GENERATE_ADMIN_HANDBOOK_INPUT_SCHEMA, GENERATE_ARCHITECTURE_OVERVIEW_INPUT_SCHEMA, GENERATE_SHARING_SUMMARY_INPUT_SCHEMA, GENERATE_COMPLIANCE_REPORT_INPUT_SCHEMA, GENERATE_ONBOARDING_DOC_INPUT_SCHEMA, CALL_GRAPH_INPUT_SCHEMA, DOWNSTREAM_EFFECTS_INPUT_SCHEMA, TEST_COVERAGE_FOR_METHOD_INPUT_SCHEMA, MEANINGFUL_TEST_AUDIT_INPUT_SCHEMA, METHOD_REACHABILITY_INPUT_SCHEMA, TESTS_FOR_CHANGE_INPUT_SCHEMA, PACKAGE_IMPACT_INPUT_SCHEMA, CDC_SUBSCRIBERS_INPUT_SCHEMA, ASYNC_CHAIN_DEPTH_INPUT_SCHEMA, SCHEDULED_JOB_CATALOG_INPUT_SCHEMA, OUTBOUND_MESSAGE_CATALOG_INPUT_SCHEMA, ENDPOINT_CATALOG_INPUT_SCHEMA, FIELD_MEANING_INPUT_SCHEMA, DISAMBIGUATE_CONCEPTS_INPUT_SCHEMA, FIELD_PROVENANCE_INPUT_SCHEMA, FIND_FIELD_ANYWHERE_INPUT_SCHEMA, FIND_SEMANTIC_FIELD_INPUT_SCHEMA, FIND_HARDCODED_VALUES_ANYWHERE_INPUT_SCHEMA, FIND_CLONE_PATTERNS_INPUT_SCHEMA, FIND_DEAD_CODE_INPUT_SCHEMA, CPQ_RULE_CHAIN_INPUT_SCHEMA, CPQ_QUOTE_TEMPLATE_BREAKDOWN_INPUT_SCHEMA, CPQ_DEPENDENCY_MAP_INPUT_SCHEMA, COMPARE_VAULTS_INPUT_SCHEMA, COMPARE_OBJECT_ACROSS_VAULTS_INPUT_SCHEMA, COMPARE_PROFILE_ACROSS_VAULTS_INPUT_SCHEMA, FIELD_MAPPING_BETWEEN_OBJECTS_INPUT_SCHEMA, INTEGRATION_PROCEDURE_CHAIN_INPUT_SCHEMA, OMNISCRIPT_FLOW_INPUT_SCHEMA, OMNIUICARD_WIDGET_BREAKDOWN_INPUT_SCHEMA, DATATRANSFORM_FIELD_MAP_INPUT_SCHEMA, DECISION_TABLE_BROWSE_INPUT_SCHEMA, FIND_DEPENDENCY_CYCLES_INPUT_SCHEMA, APEX_TEST_COVERAGE_INPUT_SCHEMA, AUTOMATION_BUILD_ADVISOR_INPUT_SCHEMA, APEX_BUILD_ADVISOR_INPUT_SCHEMA, FIELD_CHANGE_ADVISOR_INPUT_SCHEMA, LIVE_DRIFT_CHECK_INPUT_SCHEMA, ORG_HISTORY_INPUT_SCHEMA, V01_TOOLS, KNOWN_TOOL_NAMES, dispatchTool, runTool, registerTools, MAX_RESPONSE_BYTES, jsonResult;
28821
30625
  var init_tools = __esm({
28822
30626
  "../mcp/dist/src/tools/index.js"() {
28823
30627
  "use strict";
@@ -28884,6 +30688,7 @@ var init_tools = __esm({
28884
30688
  init_get_impact();
28885
30689
  init_get_subgraph();
28886
30690
  init_governor_limit_risks();
30691
+ init_guidance();
28887
30692
  init_health_check();
28888
30693
  init_integration_map();
28889
30694
  init_integration_procedure_chain();
@@ -28916,6 +30721,7 @@ var init_tools = __esm({
28916
30721
  init_search_flow_metadata();
28917
30722
  init_snapshot_trend();
28918
30723
  init_synthesis_reports();
30724
+ init_synthesize_answer();
28919
30725
  init_tech_debt_score();
28920
30726
  init_test_coverage_for_method();
28921
30727
  init_test_coverage_gaps();
@@ -28923,8 +30729,10 @@ var init_tools = __esm({
28923
30729
  init_unassigned_permission_sets();
28924
30730
  init_unused_components();
28925
30731
  init_unused_fields_deep();
30732
+ init_value_change_audit();
28926
30733
  init_what_happens_on_save();
28927
30734
  init_what_if_change_field_type();
30735
+ init_what_if_change_field_value();
28928
30736
  init_what_if_change_method_signature();
28929
30737
  init_what_if_deactivate_flow();
28930
30738
  init_what_if_disable_trigger();
@@ -28947,6 +30755,14 @@ var init_tools = __esm({
28947
30755
  type: "object",
28948
30756
  properties: {}
28949
30757
  });
30758
+ SYNTHESIZE_ANSWER_INPUT_SCHEMA = Object.freeze({
30759
+ type: "object",
30760
+ properties: {
30761
+ input: {},
30762
+ question: { type: "string" },
30763
+ draft: { type: "string" }
30764
+ }
30765
+ });
28950
30766
  ORG_PULSE_INPUT_SCHEMA = Object.freeze({
28951
30767
  type: "object",
28952
30768
  properties: {
@@ -29467,8 +31283,7 @@ var init_tools = __esm({
29467
31283
  properties: {
29468
31284
  eventId: { type: "string", minLength: 1 },
29469
31285
  limit: { type: "integer", minimum: 1, maximum: 500 }
29470
- },
29471
- required: ["eventId"]
31286
+ }
29472
31287
  });
29473
31288
  LOOKUP_RECORD_INPUT_SCHEMA = Object.freeze({
29474
31289
  type: "object",
@@ -29477,6 +31292,12 @@ var init_tools = __esm({
29477
31292
  },
29478
31293
  required: ["recordId"]
29479
31294
  });
31295
+ GUIDANCE_INPUT_SCHEMA = Object.freeze({
31296
+ type: "object",
31297
+ properties: {
31298
+ topic: { type: "string", minLength: 1 }
31299
+ }
31300
+ });
29480
31301
  EXPLAIN_FIELD_INPUT_SCHEMA = Object.freeze({
29481
31302
  type: "object",
29482
31303
  properties: {
@@ -29584,7 +31405,8 @@ var init_tools = __esm({
29584
31405
  type: "string",
29585
31406
  enum: ["identifier", "contact", "financial", "health", "all"]
29586
31407
  },
29587
- limit: { type: "integer", minimum: 1, maximum: 500 }
31408
+ limit: { type: "integer", minimum: 1, maximum: 500 },
31409
+ offset: { type: "integer", minimum: 0 }
29588
31410
  }
29589
31411
  });
29590
31412
  FIELD_ACCESS_AUDIT_INPUT_SCHEMA = Object.freeze({
@@ -29874,7 +31696,8 @@ var init_tools = __esm({
29874
31696
  CRUD_FLS_AUDIT_INPUT_SCHEMA = Object.freeze({
29875
31697
  type: "object",
29876
31698
  properties: {
29877
- limit: { type: "integer", minimum: 1, maximum: 500 }
31699
+ limit: { type: "integer", minimum: 1, maximum: 500 },
31700
+ offset: { type: "integer", minimum: 0 }
29878
31701
  }
29879
31702
  });
29880
31703
  TEST_COVERAGE_GAPS_INPUT_SCHEMA = Object.freeze({
@@ -29884,9 +31707,30 @@ var init_tools = __esm({
29884
31707
  type: "array",
29885
31708
  items: { type: "string", minLength: 1 },
29886
31709
  maxItems: 500
29887
- }
31710
+ },
31711
+ limit: { type: "integer", minimum: 1, maximum: 500 },
31712
+ offset: { type: "integer", minimum: 0 }
29888
31713
  }
29889
31714
  });
31715
+ WHAT_IF_CHANGE_FIELD_VALUE_INPUT_SCHEMA = Object.freeze({
31716
+ type: "object",
31717
+ properties: {
31718
+ fieldId: { type: "string", minLength: 1 },
31719
+ newValue: { type: "string" }
31720
+ },
31721
+ required: ["fieldId"],
31722
+ additionalProperties: false
31723
+ });
31724
+ VALUE_CHANGE_AUDIT_INPUT_SCHEMA = Object.freeze({
31725
+ type: "object",
31726
+ properties: {
31727
+ object: { type: "string", minLength: 1 },
31728
+ fields: { type: "array", items: { type: "string" } },
31729
+ verbosity: { type: "string", enum: ["summary", "detail"] }
31730
+ },
31731
+ required: ["object"],
31732
+ additionalProperties: false
31733
+ });
29890
31734
  WHAT_IF_CHANGE_FIELD_TYPE_INPUT_SCHEMA = Object.freeze({
29891
31735
  type: "object",
29892
31736
  properties: {
@@ -30369,9 +32213,14 @@ var init_tools = __esm({
30369
32213
  description: 'Product self-description: what this knowledge base can answer. Returns a categorized capability map (with example natural-language questions per area), the live registered-tool count, the recommended conversational pattern (call sfi.resolve first; ask a clarifying question on ambiguous; offer /sfi-refresh or stop on none), the three slash commands, and the v0.1 read-only/offline boundary. Takes no arguments. Call when the user asks "what can you do / what can I ask?" or to orient a fresh session.',
30370
32214
  inputSchema: CAPABILITIES_INPUT_SCHEMA
30371
32215
  },
32216
+ {
32217
+ name: "sfi.synthesize_answer",
32218
+ description: "Answer-layer grounding pass: turns the JSON returned by prior sfi.* tool call(s) into a structured, citation-grounded answer skeleton \u2014 `summary`, `bullets` (headline facts extracted from the input), `citations` (ONLY canonical ids present in the input, parsed to type + apiName), and `caveats` (honesty/limitation strings carried verbatim). Pass the source tool output as `input` (any JSON), optionally the user `question` (echoed into the summary), and optionally a `draft` narrative \u2014 when given, `hallucinatedIds` lists canonical ids in the draft that do NOT appear in the source so they can be removed before answering. Pure transform: reads ONLY `input`, never the graph or live org, so it can never add a fact it was not handed. Prose wording stays with the caller; this guarantees grounding, not sentences.",
32219
+ inputSchema: SYNTHESIZE_ANSWER_INPUT_SCHEMA
32220
+ },
30372
32221
  {
30373
32222
  name: "sfi.route_question",
30374
- description: "Front-door router: map a plain-language question to the plane that answers it (vault | live | hybrid | unknown) and the ordered sfi.* tools to call \u2014 so the user never types a tool name. Read-only; it suggests a route, it does not answer. Tells you when to sfi.resolve a named component first, whether the opt-in live plane is required, and \u2014 when the question hits a capability we lack \u2014 returns an honest 'unknown'/gap and logs it for the backlog instead of fabricating. Call this first on a vague/broad question to decide which tool(s) to run.",
32223
+ description: "Front-door router: map a plain-language question to the plane that answers it (vault | live | hybrid | unknown) and the ordered sfi.* tools to call \u2014 so the user never types a tool name. Read-only; it suggests a route, it does not answer. Tells you when to sfi.resolve a named component first, whether the opt-in live plane is required, surfaces `suggestedArgs` (heuristic per-intent hints \u2014 e.g. `event: 'update'` for a save-order question so you can call `what_happens_on_save` without guessing the DML event), and \u2014 when the question hits a capability we lack \u2014 returns an honest 'unknown'/gap and logs it for the backlog instead of fabricating. Call this first on a vague/broad question to decide which tool(s) to run.",
30375
32224
  inputSchema: ROUTE_QUESTION_INPUT_SCHEMA
30376
32225
  },
30377
32226
  {
@@ -30556,7 +32405,7 @@ var init_tools = __esm({
30556
32405
  },
30557
32406
  {
30558
32407
  name: "sfi.permission_risk_report",
30559
- description: "Ranked permission risks: unassigned permission sets and CRUD/FLS audit totals.",
32408
+ description: "Ranked permission-risk report, leading with OVER-PRIVILEGE read straight from the extracted profile / permission-set metadata: every Profile or PermissionSet that grants a god-mode or administrative system permission (Modify All Data / View All Data = critical; Author Apex, Customize Application, Manage Users, Manage Profiles/PermSets, Modify Metadata, Manage Sharing, Manage Roles, password/login policies = high) OR object-level View All / Modify All, surfaced as ONE aggregated finding per grantor (severity = the worst signal; system perms + a per-grantor count of objects escalated). PermissionSetGroups are analysed too: a PSG's effective god-mode is aggregated from its MEMBER permission sets (so a user who gets Modify All Data via a group is caught), with the muting permission set noted but not subtracted (v1 honesty boundary). A `privilege` block rosters the `modifyAllDataGrantors` / `viewAllDataGrantors` (profiles, permission sets, AND groups) and the `overPrivilegedGrantorCount`. Also rolls in unassigned permission sets and CRUD/FLS audit totals. Answers 'who has god mode / Modify All / View All / who is an admin / who is over-permissioned'. Read-only, declared confidence (literal metadata flags, not heuristics); `limit` (default 50) caps the findings.",
30560
32409
  inputSchema: SYNTHESIS_INPUT_SCHEMA
30561
32410
  },
30562
32411
  {
@@ -30596,9 +32445,14 @@ var init_tools = __esm({
30596
32445
  },
30597
32446
  {
30598
32447
  name: "sfi.event_subscribers",
30599
- description: "Given a Platform Event id (`CustomObject:{ApiName}__e`), list every subscriber (ApexTrigger, ApexClass, Flow) that emits an incoming `listensTo` edge into the event. Returns each subscriber's identity, the extractor that emitted the edge, and the edge-level subscription metadata. Honest empty list when no subscribers exist; `invalid-query` when the id is not a Platform Event canonical form.",
32448
+ description: 'Given a Platform Event id (`CustomObject:{ApiName}__e`), list every subscriber (ApexTrigger, ApexClass, Flow) that emits an incoming `listensTo` edge into the event. OMIT `eventId` for CATALOG mode: every Platform Event in the org with its subscriber count (`events[]`) \u2014 answers "what platform events does this org publish?" (then `subscribers` is `[]` and `eventApiName` is `null`). Single-event mode returns each subscriber\'s identity, the emitting extractor, and edge-level subscription metadata. Honest empty list when no subscribers exist; `invalid-query` when a supplied id is not a Platform Event canonical form.',
30600
32449
  inputSchema: EVENT_SUBSCRIBERS_INPUT_SCHEMA
30601
32450
  },
32451
+ {
32452
+ name: "sfi.guidance",
32453
+ description: "General Salesforce best-practice guidance \u2014 the `knowledge` plane for greenfield / New-Org questions that have NO org-specific answer (Flow vs Apex, order of execution, governor limits, async Apex, trigger frameworks, bulkification, Apex testing, callouts, SFDX, unlocked packages, profiles vs permission sets, OWD/sharing, standard vs custom objects, naming, sandboxes). With `topic` (a key like `flow-vs-apex`, or a phrase that loose-matches) it returns a curated summary plus links to official Salesforce docs; without `topic` it lists available topics. Explicitly NOT specific to this org (see `disclosure`) \u2014 it points to authoritative docs and never fabricates vault data.",
32454
+ inputSchema: GUIDANCE_INPUT_SCHEMA
32455
+ },
30602
32456
  {
30603
32457
  name: "sfi.find_code_usages",
30604
32458
  description: "List the code source files (ApexClass, ApexTrigger, LightningComponentBundle, AuraDefinitionBundle, VisualforcePage, VisualforceComponent) that read, write, call, or reference a component. Strict superset of `sfi.find_apex_usages`: same Apex-source coverage plus the v1.4 frontend tier. Filters incoming `readsFrom`/`writesTo`/`callsApex`/`references` edges to those originating from one of the six code node types; optional `nodeTypes` narrows to a single producer (e.g., `['LightningComponentBundle']` for LWC-only). LWC apex-import callsApex edges are `declared`; LWC field reads, Aura field accesses, and VF field touches are `heuristic`; VF controller/extension references are `declared`.",
@@ -30616,7 +32470,7 @@ var init_tools = __esm({
30616
32470
  },
30617
32471
  {
30618
32472
  name: "sfi.safe_to_delete_field",
30619
- description: "Given a CustomField canonical id (`CustomField:{Object}.{Field}`), composes every incoming dependency edge into a confidence-weighted deletion verdict: `safe` (no incoming edges), `risky` (heuristic-confidence Apex/LWC references that need spot-checking), `blocking` (declared Flow/ValidationRule/Layout/formula dependencies the platform will refuse to drop), or `unknown` (only unrecognised edges). Each category in the `reasoning` array carries its referrer count, up to 5 example referrers (full list via `sfi.get_impact`), and a per-category note explaining the honesty boundary. Does NOT consult the Tooling API for runtime dependency confirmation (deferred to v1.7+ `dependsOnFromApi` enrichment).",
32473
+ description: "Given a CustomField canonical id (`CustomField:{Object}.{Field}`), composes every incoming dependency edge into a confidence-weighted deletion verdict: `safe` (no incoming edges), `risky` (heuristic-confidence Apex/LWC references that need spot-checking), `blocking` (declared Flow/ValidationRule/Layout/formula dependencies the platform will refuse to drop), `unknown` (only unrecognised edges), or `review` (NOT proven safe \u2014 incomplete coverage, OR a standard / managed-package field with no node of its own but referenced by edges: it is reviewed from those edges with a not-modeled caveat instead of returning component-not-found; B12). Each category in the `reasoning` array carries its referrer count, up to 5 example referrers (full list via `sfi.get_impact`), and a per-category note explaining the honesty boundary. Does NOT consult the Tooling API for runtime dependency confirmation (deferred to v1.7+ `dependsOnFromApi` enrichment).",
30620
32474
  inputSchema: SAFE_TO_DELETE_FIELD_INPUT_SCHEMA
30621
32475
  },
30622
32476
  {
@@ -30649,6 +32503,16 @@ var init_tools = __esm({
30649
32503
  description: "Decision-support tool: before changing a field, see the whole blast radius in one briefing. Given `fieldId`, synthesises `makeRequired` (verdict + create-path impact count from what_if_make_field_required), `deletion` (verdict + blocking/risky dependency counts from safe_to_delete_field), and \u2014 when `newType` is given \u2014 `changeType` (compatibility + verdict + reference count from what_if_change_field_type), plus combined `recommendations`. Does NOT change anything (backend knowledge layer). Honesty axis: inherits the composed tools' boundaries \u2014 dataflow into Apex insert/update and dynamic/reflective field access are invisible, so verdicts mean 'investigate', not guarantees. `component-not-found` when the fieldId is not a CustomField in the vault.",
30650
32504
  inputSchema: FIELD_CHANGE_ADVISOR_INPUT_SCHEMA
30651
32505
  },
32506
+ {
32507
+ name: "sfi.what_if_change_field_value",
32508
+ description: "Value-change impact (Data Steward / Identity & Integration lens): given a CustomField `fieldId`, what breaks if its stored VALUE changes \u2014 NOT its schema (use what_if_change_field_type for type/required/delete). Returns impact buckets (identity / integration-key / uniqueness / automation / save-pipeline / display), an overall severity, honesty-surface disclosures, and recommended pre-change checks. Identity / key / uniqueness verdicts come from the field's own metadata (externalId / unique / idLookup, identity catalog) \u2014 so a value change is flagged even on a field with ZERO references (e.g. a SAML federation key). Derived fields (formula / roll-up / auto-number) return mutable:false and re-route to their source. Honesty axis: the vault cannot see external upsert systems, the IdP side of SSO, or dynamic / managed-package code; automation buckets surface declarative value-literal couplings (the value a rule compares this field to); Apex literal comparisons remain invisible. Optional `newValue` adds a targeted collision/acceptance check. `component-not-found` when the fieldId is not a CustomField in the vault.",
32509
+ inputSchema: WHAT_IF_CHANGE_FIELD_VALUE_INPUT_SCHEMA
32510
+ },
32511
+ {
32512
+ name: "sfi.value_change_audit",
32513
+ description: "Batch value-change audit (Data Steward lens): given an `object` and optionally a list of `fields`, risk-ranks the impact of changing each field's stored VALUE \u2014 the portfolio version of what_if_change_field_value. WITHOUT `fields`, auto-detects the value-sensitive fields on the object (upsert keys via externalId/unique/idLookup, identity-catalog fields, name-lexicon matches). Each row carries an overall severity, role, top impact reasons, confidence, and disclosure count; `verbosity:'detail'` inlines full buckets. Returns a severity summary + global disclosures; unknown explicit fields come back in `notFound`. This answers 'tell me if changing any of these has an impact on {object}'. Honesty axis: auto-detect can miss a value-sensitive field carrying none of those signals; per-row blast radius inherits what_if_change_field_value's boundaries (external upsert systems, IdP side of SSO, dynamic/managed-package code invisible).",
32514
+ inputSchema: VALUE_CHANGE_AUDIT_INPUT_SCHEMA
32515
+ },
30652
32516
  {
30653
32517
  name: "sfi.live_drift_check",
30654
32518
  description: "Offline\u2194live contradiction detection (requires the opt-in live plane). For `objectApiName`, compares the fields the vault recorded at the last refresh against a LIVE read-only describe and reports `onlyInVault` (fields in the snapshot the live org no longer returns \u2014 deleted/renamed/permission-hidden since refresh; the high-signal STALE indicator), `onlyInLiveCustom` (custom fields added live since refresh, filtered to `__`-suffixed to avoid standard-field noise), `inSync`, and a plain-language `interpretation`. The only check that uses BOTH planes at once; never mutates the org. Honesty axis: the vault models extracted custom fields + standard object definitions (not standard fields), so onlyInVault is the trustworthy drift signal. Pass `liveEnabled:true` or set SFI_LIVE_PLANE_ENABLED.",
@@ -30681,12 +32545,12 @@ var init_tools = __esm({
30681
32545
  },
30682
32546
  {
30683
32547
  name: "sfi.pii_inventory",
30684
- description: "Enumerate every CustomField in the vault, classify each with the v2.0d `pii-detection` recognizer (which inspects API name, declared data type, and description text), then emit a structured inventory. Filter by `classification` (`'pii' | 'sensitive' | 'all'`) and/or `category` (`'identifier' | 'contact' | 'financial' | 'health' | 'all'`); both default to `'all'`. Each emitted field carries its classification, category, data type, description, and a plain-English `reason` naming the rule that fired. `summary` reports the full per-classification and per-category counts across the matched set; `truncated` flips true when the response slice was trimmed to `limit` (default 200, max 500). The recognizer is heuristic \u2014 a field with no name-token match and no description signal classifies as `public` even if it stores PII at runtime; `EncryptedText`-typed fields ALWAYS classify as `sensitive` because the encryption type IS the declaration.",
32548
+ description: "Enumerate every CustomField in the vault, classify each with the v2.0d `pii-detection` recognizer (which inspects API name, declared data type, and description text), then emit a structured inventory. Filter by `classification` (`'pii' | 'sensitive' | 'all'`) and/or `category` (`'identifier' | 'contact' | 'financial' | 'health' | 'all'`); both default to `'all'`. Each emitted field carries its classification, category, data type, description, and a plain-English `reason` naming the rule that fired. `summary` reports the full per-classification and per-category counts across the matched set. The response is paginated: `limit` (default 200, max 500) and `offset` (default 0) page through the inventory, and a per-response ~38 KB byte budget trims the `fields` slice further when a page would exceed it, so the result never trips the global ~45 KB MCP response limit; `truncated` flips true when more matching fields remain, with `nextOffset` carrying the cursor to advance (plus a `note` when a page was byte-trimmed). The recognizer is heuristic \u2014 a field with no name-token match and no description signal classifies as `public` even if it stores PII at runtime; `EncryptedText`-typed fields ALWAYS classify as `sensitive` because the encryption type IS the declaration.",
30685
32549
  inputSchema: PII_INVENTORY_INPUT_SCHEMA
30686
32550
  },
30687
32551
  {
30688
32552
  name: "sfi.field_access_audit",
30689
- description: "Given a CustomField canonical id (`CustomField:{Object}.{Field}`), cross-walk every Profile and PermissionSet that grants access to the field via incoming `grantedBy` edges. `grants` carries one entry per (Profile or PermissionSet, permission level) pair where permission is `'read'` / `'edit'` / `'unknown'` (the last meaning the older extractor did not populate the per-flag axis). `summary` reports the unfiltered counts split four ways (profilesWithRead, profilesWithEdit, permSetsWithRead, permSetsWithEdit). `viaApexAccess` enumerates ApexClass / ApexTrigger nodes with incoming `readsFrom` / `writesTo` edges to the field \u2014 a user with execute permission on one of those classes may access the field through that code path even when the metadata-grant audit reports no direct grant. Optional `permissionType` (`'read' | 'edit' | 'all'`, default `'all'`) narrows the emitted `grants` array. Honesty axis: this is the v2.0d.0 permission-grant-level audit; criteria-based and account-team sharing rules are deferred to v2.0d.1. Invalid prefix surfaces as `invalid-query`; unknown ids surface as `component-not-found`.",
32553
+ description: "Given a CustomField canonical id (`CustomField:{Object}.{Field}`), cross-walk every Profile and PermissionSet that grants access to the field via incoming `grantedBy` edges. `grants` carries one entry per (Profile or PermissionSet, permission level) pair where permission is `'read'` / `'edit'` / `'unknown'` (the last meaning the older extractor did not populate the per-flag axis). `summary` reports the unfiltered counts split four ways (profilesWithRead, profilesWithEdit, permSetsWithRead, permSetsWithEdit). `viaApexAccess` enumerates ApexClass / ApexTrigger nodes with incoming `readsFrom` / `writesTo` edges to the field \u2014 a user with execute permission on one of those classes may access the field through that code path even when the metadata-grant audit reports no direct grant. Optional `permissionType` (`'read' | 'edit' | 'all'`, default `'all'`) narrows the emitted `grants` array. A standard or managed-package field with no node of its own but referenced by fieldPermissions / Apex edges is still audited from those edges with `notModeled: true` + a `notModeledNote` (grants are accurate; data type / formula are unavailable and PII is inferred from the field name) \u2014 only an id with no node AND no inbound references is `component-not-found` (B12). Honesty axis: this is the v2.0d.0 permission-grant-level audit; criteria-based and account-team sharing rules are deferred to v2.0d.1. Invalid prefix surfaces as `invalid-query`.",
30690
32554
  inputSchema: FIELD_ACCESS_AUDIT_INPUT_SCHEMA
30691
32555
  },
30692
32556
  {
@@ -30781,12 +32645,12 @@ var init_tools = __esm({
30781
32645
  },
30782
32646
  {
30783
32647
  name: "sfi.crud_fls_audit",
30784
- description: "v2.1 R3 CRUD/FLS enforcement audit. Walks every ApexClass / ApexTrigger node's `properties.qualityIssues[]`, narrows to the two CRUD/FLS rules (`missing-crud-check`, `missing-fls-check`), groups findings by class, and surfaces the verbatim Q80 disclosure naming the HIGH false-positive rate inherited from ApexQualitySemantics.md \xA7\xA7 6-7. Each class entry carries its identity and a per-finding list (rule / severity / location / explanation). `totalFindingCount` / `byRule` report the FULL pre-slice counts. `limit` defaults to 100 (max 500); the slice is over CLASSES. Honesty axis (verbatim, surfaced in `boundaries[]` when at least one finding qualifies): the Q80 false-positive disclosure \u2014 'custom security utility methods are invisible to the recognizer; this finding may be a false positive if your org uses a helper like SecurityUtils.canCreate(account)' \u2014 is the load-bearing honesty surface for this tool. Also surfaced: cross-method dataflow is invisible; dynamic SOQL strings (Database.query) are stripped before pattern passes.",
32648
+ description: "v2.1 R3 CRUD/FLS enforcement audit. Walks every ApexClass / ApexTrigger node's `properties.qualityIssues[]`, narrows to the two CRUD/FLS rules (`missing-crud-check`, `missing-fls-check`), groups findings by class, and surfaces the verbatim Q80 disclosure naming the HIGH false-positive rate inherited from ApexQualitySemantics.md \xA7\xA7 6-7. Each class entry carries its identity and a per-finding list (rule / severity / location / explanation). `totalFindingCount` / `byRule` report the FULL pre-slice counts. The class list is paginated: `limit` defaults to 100 (max 500) and `offset` (default 0) page over CLASSES, and a per-response ~36 KB byte budget trims the page further when a page would exceed it, so the result never trips the global ~45 KB MCP response limit; `truncated` flips true when more classes remain, with `nextOffset` to advance (plus a `note` when byte-trimmed, and a per-class `findingsTruncated` flag in the rare case one class's findings alone overflow). Honesty axis (verbatim, surfaced in `boundaries[]` when at least one finding qualifies): the Q80 false-positive disclosure \u2014 'custom security utility methods are invisible to the recognizer; this finding may be a false positive if your org uses a helper like SecurityUtils.canCreate(account)' \u2014 is the load-bearing honesty surface for this tool. Also surfaced: cross-method dataflow is invisible; dynamic SOQL strings (Database.query) are stripped before pattern passes.",
30785
32649
  inputSchema: CRUD_FLS_AUDIT_INPUT_SCHEMA
30786
32650
  },
30787
32651
  {
30788
32652
  name: "sfi.test_coverage_gaps",
30789
- description: "v2.1 R3 test-coverage-gap surface. Combines three signals: (1) `properties.isTest === true` identifies test classes (excluded from the scan), (2) BFS over incoming `callsApex` edges (capped at depth 3) collects the test classes reaching each non-test class, (3) `qualityIssues[]` `fake-assertion` findings on those test classes mark meaninglessly-covered classes. Classifies each non-test ApexClass into one of three coverage statuses \u2014 `uncovered` (no test reaches it within depth 3), `fake-coverage` (covered, but EVERY covering test has fake-assertion findings), `low-quality-coverage` (covered, but SOME covering test has fake-assertion findings). Each gap entry carries `componentId` / `apiName` / `coverageStatus` / `coveringTestClassIds[]` / `fakeAssertions[]` / `recommendedAction`. `byStatus` reports the per-status counts. Optional `classFilter[]` narrows the scan to specific ApexClass ids (capped at 500). Honesty axis (verbatim, surfaced in `boundaries[]` when at least one gap qualifies): the meaningful-assertion heuristic recognizes `System.assertEquals(expected, actual)` with distinct tokens; assertions via helper methods or framework wrappers are invisible. Reachability via `callsApex` does NOT cover dynamic dispatch. BFS is capped at depth 3.",
32653
+ description: "v2.1 R3 test-coverage-gap surface. Combines three signals: (1) `properties.isTest === true` identifies test classes (excluded from the scan), (2) BFS over incoming `callsApex` edges (capped at depth 3) collects the test classes reaching each non-test class, (3) `qualityIssues[]` `fake-assertion` findings on those test classes mark meaninglessly-covered classes. Classifies each non-test ApexClass into one of three coverage statuses \u2014 `uncovered` (no test reaches it within depth 3), `fake-coverage` (covered, but EVERY covering test has fake-assertion findings), `low-quality-coverage` (covered, but SOME covering test has fake-assertion findings). Each gap entry carries `componentId` / `apiName` / `coverageStatus` / `coveringTestClassIds[]` / `fakeAssertions[]` / `recommendedAction`. `byStatus` reports the per-status counts. The gap list is paginated: `limit` (default 200, max 500) and `offset` (default 0) page over gap entries, and a per-response ~38 KB byte budget trims the page further when a page would exceed it, so the result never trips the global ~45 KB MCP response limit; `truncated` flips true when more gaps remain, with `nextOffset` to advance (plus a `note` when byte-trimmed). Optional `classFilter[]` narrows the scan to specific ApexClass ids (capped at 500). Honesty axis (verbatim, surfaced in `boundaries[]` when at least one gap qualifies): the meaningful-assertion heuristic recognizes `System.assertEquals(expected, actual)` with distinct tokens; assertions via helper methods or framework wrappers are invisible. Reachability via `callsApex` does NOT cover dynamic dispatch. BFS is capped at depth 3.",
30790
32654
  inputSchema: TEST_COVERAGE_GAPS_INPUT_SCHEMA
30791
32655
  },
30792
32656
  {
@@ -31116,6 +32980,10 @@ var init_tools = __esm({
31116
32980
  return runTool(ctx, args, resolveInputSchema, resolveHandler);
31117
32981
  case "sfi.capabilities":
31118
32982
  return runTool(ctx, args, capabilitiesInputSchema, capabilitiesHandler);
32983
+ case "sfi.guidance":
32984
+ return runTool(ctx, args, guidanceInputSchema, guidanceHandler);
32985
+ case "sfi.synthesize_answer":
32986
+ return runTool(ctx, args, synthesizeAnswerInputSchema, synthesizeAnswerHandler);
31119
32987
  case "sfi.org_pulse":
31120
32988
  return runTool(ctx, args, orgPulseInputSchema, orgPulseHandler);
31121
32989
  case "sfi.fleet_find":
@@ -31286,6 +33154,10 @@ var init_tools = __esm({
31286
33154
  return runTool(ctx, args, testCoverageGapsInputSchema, testCoverageGapsHandler);
31287
33155
  case "sfi.what_if_change_field_type":
31288
33156
  return runTool(ctx, args, whatIfChangeFieldTypeInputSchema, whatIfChangeFieldTypeHandler);
33157
+ case "sfi.what_if_change_field_value":
33158
+ return runTool(ctx, args, whatIfChangeFieldValueInputSchema, whatIfChangeFieldValueHandler);
33159
+ case "sfi.value_change_audit":
33160
+ return runTool(ctx, args, valueChangeAuditInputSchema, valueChangeAuditHandler);
31289
33161
  case "sfi.what_if_remove_picklist_value":
31290
33162
  return runTool(ctx, args, whatIfRemovePicklistValueInputSchema, whatIfRemovePicklistValueHandler);
31291
33163
  case "sfi.what_if_make_field_required":
@@ -37347,11 +39219,30 @@ var extractEnterpriseMetadata = async (path, config) => {
37347
39219
  apiName = deriveComponentApiName(path, config.suffix);
37348
39220
  }
37349
39221
  const fieldRefs = extractFieldRefs(text.value, parentObjectApiName);
39222
+ const nodeId = `${config.type}:${apiName}`;
39223
+ const childRefEdges = [];
39224
+ const childRefSummary = {};
39225
+ for (const spec of config.childRefs ?? []) {
39226
+ const values = [...new Set(extractXmlValues(text.value, spec.element))].sort();
39227
+ if (values.length > 0)
39228
+ childRefSummary[spec.element] = values;
39229
+ for (const value of values) {
39230
+ childRefEdges.push({
39231
+ fromId: nodeId,
39232
+ toId: `${spec.toType}:${value}`,
39233
+ edgeType: "references",
39234
+ confidence: "declared",
39235
+ source: EXTRACTOR_SOURCE10,
39236
+ properties: { referenceKind: spec.referenceKind }
39237
+ });
39238
+ }
39239
+ }
37350
39240
  const node = makeNode(config.type, apiName, path, parentObjectApiName === null ? null : `CustomObject:${parentObjectApiName}`, {
37351
39241
  fieldRefs,
37352
- rawReferenceCount: fieldRefs.length
39242
+ rawReferenceCount: fieldRefs.length,
39243
+ ...childRefSummary
37353
39244
  });
37354
- const edges = fieldRefs.map((fieldId) => ({
39245
+ const fieldRefEdges = fieldRefs.map((fieldId) => ({
37355
39246
  fromId: node.id,
37356
39247
  toId: fieldId,
37357
39248
  edgeType: "references",
@@ -37359,7 +39250,7 @@ var extractEnterpriseMetadata = async (path, config) => {
37359
39250
  source: EXTRACTOR_SOURCE10,
37360
39251
  properties: { referenceKind: "fieldRef" }
37361
39252
  }));
37362
- return ok({ nodes: [node], edges });
39253
+ return ok({ nodes: [node], edges: [...fieldRefEdges, ...childRefEdges] });
37363
39254
  };
37364
39255
  var extractReport = (path) => extractEnterpriseMetadata(path, { type: "Report", suffix: ".report-meta.xml" });
37365
39256
  var extractDashboard = (path) => extractEnterpriseMetadata(path, { type: "Dashboard", suffix: ".dashboard-meta.xml" });
@@ -37372,7 +39263,22 @@ var extractReportType = (path) => extractEnterpriseMetadata(path, { type: "Repor
37372
39263
  var extractFlexiPage = (path) => extractEnterpriseMetadata(path, { type: "FlexiPage", suffix: ".flexipage-meta.xml" });
37373
39264
  var extractPermissionSetGroup = (path) => extractEnterpriseMetadata(path, {
37374
39265
  type: "PermissionSetGroup",
37375
- suffix: ".permissionsetgroup-meta.xml"
39266
+ suffix: ".permissionsetgroup-meta.xml",
39267
+ // A PSG's effective permissions are the UNION of its member permission
39268
+ // sets' grants, minus the muting permission set's. Capture both so the
39269
+ // permission analysis can flow god-mode / object grants through the group.
39270
+ childRefs: [
39271
+ {
39272
+ element: "permissionSets",
39273
+ toType: "PermissionSet",
39274
+ referenceKind: "permissionSetGroupMember"
39275
+ },
39276
+ {
39277
+ element: "mutingPermissionSets",
39278
+ toType: "MutingPermissionSet",
39279
+ referenceKind: "mutingPermissionSet"
39280
+ }
39281
+ ]
37376
39282
  });
37377
39283
  var extractMutingPermissionSet = (path) => extractEnterpriseMetadata(path, {
37378
39284
  type: "MutingPermissionSet",
@@ -43257,10 +45163,14 @@ var extractRole = async (path) => {
43257
45163
  return ok({ nodes: [node], edges });
43258
45164
  };
43259
45165
 
45166
+ // ../extractors/dist/src/saml-sso-config.js
45167
+ init_dist();
45168
+ import { XMLParser as XMLParser53, XMLValidator as XMLValidator52 } from "fast-xml-parser";
45169
+
43260
45170
  // ../extractors/dist/src/sharing-rules.js
43261
45171
  init_dist();
43262
45172
  import { readFile as readFile66 } from "node:fs/promises";
43263
- import { XMLParser as XMLParser53, XMLValidator as XMLValidator52 } from "fast-xml-parser";
45173
+ import { XMLParser as XMLParser54, XMLValidator as XMLValidator53 } from "fast-xml-parser";
43264
45174
  var SHARING_RULES_FILE_SUFFIX = ".sharingRules-meta.xml";
43265
45175
  var ROOT_ELEMENT52 = "SharingRules";
43266
45176
  var EXTRACTOR_SOURCE28 = "sharing-rule-extractor";
@@ -43321,7 +45231,7 @@ var readAndValidateXml48 = async (path) => {
43321
45231
  cause
43322
45232
  });
43323
45233
  }
43324
- const validation = XMLValidator52.validate(xmlText);
45234
+ const validation = XMLValidator53.validate(xmlText);
43325
45235
  if (validation !== true) {
43326
45236
  return err({ kind: "parse-error", path, message: validation.err.msg });
43327
45237
  }
@@ -43511,7 +45421,7 @@ var extractSharingRules = async (path) => {
43511
45421
  const xmlResult = await readAndValidateXml48(path);
43512
45422
  if (!xmlResult.ok)
43513
45423
  return xmlResult;
43514
- const parser = new XMLParser53({
45424
+ const parser = new XMLParser54({
43515
45425
  ignoreAttributes: true,
43516
45426
  parseTagValue: false,
43517
45427
  trimValues: true,
@@ -43558,7 +45468,7 @@ var extractSharingRules = async (path) => {
43558
45468
  // ../extractors/dist/src/static-resource.js
43559
45469
  init_dist();
43560
45470
  import { readFile as readFile67 } from "node:fs/promises";
43561
- import { XMLParser as XMLParser54, XMLValidator as XMLValidator53 } from "fast-xml-parser";
45471
+ import { XMLParser as XMLParser55, XMLValidator as XMLValidator54 } from "fast-xml-parser";
43562
45472
  var STATIC_RESOURCE_FILE_SUFFIX = ".resource-meta.xml";
43563
45473
  var ROOT_ELEMENT53 = "StaticResource";
43564
45474
  var REQUIRED_ELEMENTS24 = ["cacheControl"];
@@ -43583,7 +45493,7 @@ var readAndValidateXml49 = async (path) => {
43583
45493
  cause
43584
45494
  });
43585
45495
  }
43586
- const validation = XMLValidator53.validate(xmlText);
45496
+ const validation = XMLValidator54.validate(xmlText);
43587
45497
  if (validation !== true) {
43588
45498
  return err({ kind: "parse-error", path, message: validation.err.msg });
43589
45499
  }
@@ -43614,7 +45524,7 @@ var extractStaticResource = async (path) => {
43614
45524
  const xmlResult = await readAndValidateXml49(path);
43615
45525
  if (!xmlResult.ok)
43616
45526
  return xmlResult;
43617
- const parser = new XMLParser54({
45527
+ const parser = new XMLParser55({
43618
45528
  ignoreAttributes: true,
43619
45529
  parseTagValue: false,
43620
45530
  trimValues: true,
@@ -43668,7 +45578,7 @@ var extractStaticResource = async (path) => {
43668
45578
  init_dist();
43669
45579
  import { readFile as readFile68 } from "node:fs/promises";
43670
45580
  import { basename as basename10, dirname as dirname13 } from "node:path";
43671
- import { XMLParser as XMLParser55, XMLValidator as XMLValidator54 } from "fast-xml-parser";
45581
+ import { XMLParser as XMLParser56, XMLValidator as XMLValidator55 } from "fast-xml-parser";
43672
45582
  var VALIDATION_RULE_FILE_SUFFIX = ".validationRule-meta.xml";
43673
45583
  var ROOT_ELEMENT54 = "ValidationRule";
43674
45584
  var VALIDATION_RULES_DIR_NAME = "validationRules";
@@ -43703,7 +45613,7 @@ var readAndValidateXml50 = async (path) => {
43703
45613
  cause
43704
45614
  });
43705
45615
  }
43706
- const validation = XMLValidator54.validate(xmlText);
45616
+ const validation = XMLValidator55.validate(xmlText);
43707
45617
  if (validation !== true) {
43708
45618
  return err({ kind: "parse-error", path, message: validation.err.msg });
43709
45619
  }
@@ -43751,7 +45661,7 @@ var extractValidationRule = async (path) => {
43751
45661
  const xmlResult = await readAndValidateXml50(path);
43752
45662
  if (!xmlResult.ok)
43753
45663
  return xmlResult;
43754
- const parser = new XMLParser55({
45664
+ const parser = new XMLParser56({
43755
45665
  ignoreAttributes: true,
43756
45666
  parseTagValue: false,
43757
45667
  trimValues: true,
@@ -43822,7 +45732,7 @@ var extractValidationRule = async (path) => {
43822
45732
  init_dist();
43823
45733
  init_src3();
43824
45734
  import { readFile as readFile69 } from "node:fs/promises";
43825
- import { XMLParser as XMLParser56, XMLValidator as XMLValidator55 } from "fast-xml-parser";
45735
+ import { XMLParser as XMLParser57, XMLValidator as XMLValidator56 } from "fast-xml-parser";
43826
45736
  var COMPONENT_FILE_SUFFIX = ".component";
43827
45737
  var META_FILE_EXT3 = "-meta.xml";
43828
45738
  var ROOT_ELEMENT55 = "ApexComponent";
@@ -43848,14 +45758,14 @@ var readAndValidateXml51 = async (path, missingMessage) => {
43848
45758
  cause
43849
45759
  });
43850
45760
  }
43851
- const validation = XMLValidator55.validate(xmlText);
45761
+ const validation = XMLValidator56.validate(xmlText);
43852
45762
  if (validation !== true) {
43853
45763
  return err({ kind: "parse-error", path, message: validation.err.msg });
43854
45764
  }
43855
45765
  return ok(xmlText);
43856
45766
  };
43857
45767
  var parseMetaXml5 = (xmlText, path) => {
43858
- const parser = new XMLParser56({
45768
+ const parser = new XMLParser57({
43859
45769
  ignoreAttributes: true,
43860
45770
  parseTagValue: false,
43861
45771
  trimValues: true,
@@ -44063,7 +45973,7 @@ var extractVisualforceComponent = async (path) => {
44063
45973
  init_dist();
44064
45974
  init_src3();
44065
45975
  import { readFile as readFile70 } from "node:fs/promises";
44066
- import { XMLParser as XMLParser57, XMLValidator as XMLValidator56 } from "fast-xml-parser";
45976
+ import { XMLParser as XMLParser58, XMLValidator as XMLValidator57 } from "fast-xml-parser";
44067
45977
  var PAGE_FILE_SUFFIX = ".page";
44068
45978
  var META_FILE_EXT4 = "-meta.xml";
44069
45979
  var ROOT_ELEMENT56 = "ApexPage";
@@ -44090,14 +46000,14 @@ var readAndValidateXml52 = async (path, missingMessage) => {
44090
46000
  cause
44091
46001
  });
44092
46002
  }
44093
- const validation = XMLValidator56.validate(xmlText);
46003
+ const validation = XMLValidator57.validate(xmlText);
44094
46004
  if (validation !== true) {
44095
46005
  return err({ kind: "parse-error", path, message: validation.err.msg });
44096
46006
  }
44097
46007
  return ok(xmlText);
44098
46008
  };
44099
46009
  var parseMetaXml6 = (xmlText, path) => {
44100
- const parser = new XMLParser57({
46010
+ const parser = new XMLParser58({
44101
46011
  ignoreAttributes: true,
44102
46012
  parseTagValue: false,
44103
46013
  trimValues: true,
@@ -44311,7 +46221,7 @@ var extractVisualforcePage = async (path) => {
44311
46221
  init_dist();
44312
46222
  import { readFile as readFile71 } from "node:fs/promises";
44313
46223
  import { basename as basename11, dirname as dirname14 } from "node:path";
44314
- import { XMLParser as XMLParser58, XMLValidator as XMLValidator57 } from "fast-xml-parser";
46224
+ import { XMLParser as XMLParser59, XMLValidator as XMLValidator58 } from "fast-xml-parser";
44315
46225
  var FILE_SUFFIX5 = ".webLink-meta.xml";
44316
46226
  var ROOT_ELEMENT57 = "WebLink";
44317
46227
  var DIR_NAME4 = "webLinks";
@@ -44349,7 +46259,7 @@ var readAndValidateXml53 = async (path) => {
44349
46259
  cause
44350
46260
  });
44351
46261
  }
44352
- const validation = XMLValidator57.validate(xmlText);
46262
+ const validation = XMLValidator58.validate(xmlText);
44353
46263
  if (validation !== true) {
44354
46264
  return err({ kind: "parse-error", path, message: validation.err.msg });
44355
46265
  }
@@ -44376,7 +46286,7 @@ var extractWebLink = async (path) => {
44376
46286
  const xmlResult = await readAndValidateXml53(path);
44377
46287
  if (!xmlResult.ok)
44378
46288
  return xmlResult;
44379
- const parser = new XMLParser58({
46289
+ const parser = new XMLParser59({
44380
46290
  ignoreAttributes: true,
44381
46291
  parseTagValue: false,
44382
46292
  trimValues: true,
@@ -44451,7 +46361,7 @@ var extractWebLink = async (path) => {
44451
46361
  // ../extractors/dist/src/workflow-rule.js
44452
46362
  init_dist();
44453
46363
  import { readFile as readFile72 } from "node:fs/promises";
44454
- import { XMLParser as XMLParser59, XMLValidator as XMLValidator58 } from "fast-xml-parser";
46364
+ import { XMLParser as XMLParser60, XMLValidator as XMLValidator59 } from "fast-xml-parser";
44455
46365
  var WORKFLOW_FILE_SUFFIX = ".workflow-meta.xml";
44456
46366
  var ROOT_ELEMENT58 = "Workflow";
44457
46367
  var EXTRACTOR_SOURCE29 = "workflow-rule-extractor";
@@ -44527,7 +46437,7 @@ var readAndValidateXml54 = async (path) => {
44527
46437
  cause
44528
46438
  });
44529
46439
  }
44530
- const validation = XMLValidator58.validate(xmlText);
46440
+ const validation = XMLValidator59.validate(xmlText);
44531
46441
  if (validation !== true) {
44532
46442
  return err({ kind: "parse-error", path, message: validation.err.msg });
44533
46443
  }
@@ -44841,7 +46751,7 @@ var extractWorkflowRule = async (path) => {
44841
46751
  const xmlResult = await readAndValidateXml54(path);
44842
46752
  if (!xmlResult.ok)
44843
46753
  return xmlResult;
44844
- const parser = new XMLParser59({
46754
+ const parser = new XMLParser60({
44845
46755
  ignoreAttributes: true,
44846
46756
  parseTagValue: false,
44847
46757
  trimValues: true,
@@ -46840,7 +48750,7 @@ var registerStatusCommand = (program) => {
46840
48750
  // dist/src/program.js
46841
48751
  var readVersion = () => {
46842
48752
  if (true)
46843
- return "0.1.4";
48753
+ return "0.1.5";
46844
48754
  const pkgUrl = new URL("../../package.json", import.meta.url);
46845
48755
  const raw = readFileSync2(fileURLToPath(pkgUrl), "utf8");
46846
48756
  const parsed = JSON.parse(raw);