sf-intelligence 0.1.1 → 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.
- package/bin/sfi.js +0 -0
- package/dist/index.js +2403 -401
- package/package.json +16 -16
- package/LICENSE +0 -201
package/dist/index.js
CHANGED
|
@@ -944,7 +944,7 @@ var init_resolve_index = __esm({
|
|
|
944
944
|
});
|
|
945
945
|
|
|
946
946
|
// ../graph/dist/src/resolve.js
|
|
947
|
-
var DEFAULT_LIMIT, MAX_LIMIT, MIN_BASE, MATCHED_FLOOR, NONE_THRESHOLD, EXACT_THRESHOLD, EXACT_COVERAGE, CONTENDER_RATIO, LENGTH_RATIO_FLOOR, SYNONYM_SCORE, POP_K, COVERAGE_EXP, STRONG_ANCHOR,
|
|
947
|
+
var DEFAULT_LIMIT, MAX_LIMIT, MIN_BASE, MATCHED_FLOOR, NONE_THRESHOLD, EXACT_THRESHOLD, EXACT_COVERAGE, CONTENDER_RATIO, LENGTH_RATIO_FLOOR, SYNONYM_SCORE, POP_K, COVERAGE_EXP, STRONG_ANCHOR, TYPE_WEIGHT, typeWeight, scoreToken, rollupKind, buildEvidence, resolveComponents;
|
|
948
948
|
var init_resolve = __esm({
|
|
949
949
|
"../graph/dist/src/resolve.js"() {
|
|
950
950
|
"use strict";
|
|
@@ -964,7 +964,6 @@ var init_resolve = __esm({
|
|
|
964
964
|
POP_K = 0.08;
|
|
965
965
|
COVERAGE_EXP = 0.5;
|
|
966
966
|
STRONG_ANCHOR = 0.9;
|
|
967
|
-
PARENT_MATCH_BONUS = 1.1;
|
|
968
967
|
TYPE_WEIGHT = {
|
|
969
968
|
CustomObject: 1,
|
|
970
969
|
CustomField: 0.95,
|
|
@@ -1028,6 +1027,7 @@ var init_resolve = __esm({
|
|
|
1028
1027
|
const minBase = options?.minScore ?? MIN_BASE;
|
|
1029
1028
|
const queryTokens = tokenizeText(query);
|
|
1030
1029
|
const normQuery = normalizeName(query);
|
|
1030
|
+
const querySpaceWords = new Set(query.trim().split(/\s+/).map(normalizeName).filter((w) => w.length > 0));
|
|
1031
1031
|
let index;
|
|
1032
1032
|
try {
|
|
1033
1033
|
index = await getResolveIndex(store);
|
|
@@ -1038,6 +1038,12 @@ var init_resolve = __esm({
|
|
|
1038
1038
|
});
|
|
1039
1039
|
}
|
|
1040
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)));
|
|
1041
1047
|
const typeFilter = options?.types !== void 0 && options.types.length > 0 ? new Set(options.types) : null;
|
|
1042
1048
|
const parentFilter = options?.parentId;
|
|
1043
1049
|
const pass1 = [];
|
|
@@ -1080,7 +1086,8 @@ var init_resolve = __esm({
|
|
|
1080
1086
|
if (perToken[i].score > globalBest[i])
|
|
1081
1087
|
globalBest[i] = perToken[i].score;
|
|
1082
1088
|
}
|
|
1083
|
-
const
|
|
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;
|
|
1084
1091
|
pass1.push({ node, perToken, wholeExact, parentMatched });
|
|
1085
1092
|
}
|
|
1086
1093
|
const anchorIdx = [];
|
|
@@ -1092,6 +1099,7 @@ var init_resolve = __esm({
|
|
|
1092
1099
|
const scored = [];
|
|
1093
1100
|
const coverageById = /* @__PURE__ */ new Map();
|
|
1094
1101
|
const wholeExactIds = /* @__PURE__ */ new Set();
|
|
1102
|
+
const parentMatchedIds = /* @__PURE__ */ new Set();
|
|
1095
1103
|
for (const c of pass1) {
|
|
1096
1104
|
const matched = c.perToken.filter((t) => t.score >= MATCHED_FLOOR);
|
|
1097
1105
|
if (matched.length === 0 && !c.wholeExact)
|
|
@@ -1106,13 +1114,15 @@ var init_resolve = __esm({
|
|
|
1106
1114
|
continue;
|
|
1107
1115
|
const type = c.node.type;
|
|
1108
1116
|
const refs = c.node.inbound;
|
|
1109
|
-
const score = base * typeWeight(type) * (1 + POP_K * Math.log10(1 + refs))
|
|
1117
|
+
const score = base * typeWeight(type) * (1 + POP_K * Math.log10(1 + refs));
|
|
1110
1118
|
const kind = c.wholeExact ? "exact" : rollupKind(matched);
|
|
1111
1119
|
const nodeTokenSet = new Set(c.node.tokens);
|
|
1112
1120
|
const matchedNodeTokens = new Set(matched.map((m) => m.matchedToken).filter((t) => nodeTokenSet.has(t)));
|
|
1113
1121
|
coverageById.set(c.node.id, c.wholeExact ? 1 : matchedNodeTokens.size / c.node.tokens.length);
|
|
1114
1122
|
if (c.wholeExact)
|
|
1115
1123
|
wholeExactIds.add(c.node.id);
|
|
1124
|
+
if (c.parentMatched)
|
|
1125
|
+
parentMatchedIds.add(c.node.id);
|
|
1116
1126
|
scored.push({
|
|
1117
1127
|
id: c.node.id,
|
|
1118
1128
|
type,
|
|
@@ -1134,6 +1144,10 @@ var init_resolve = __esm({
|
|
|
1134
1144
|
const tierB = b.base >= EXACT_THRESHOLD ? 0 : 1;
|
|
1135
1145
|
if (tierA !== tierB)
|
|
1136
1146
|
return tierA - tierB;
|
|
1147
|
+
const pmA = parentMatchedIds.has(a.id) ? 0 : 1;
|
|
1148
|
+
const pmB = parentMatchedIds.has(b.id) ? 0 : 1;
|
|
1149
|
+
if (pmA !== pmB)
|
|
1150
|
+
return pmA - pmB;
|
|
1137
1151
|
if (a.score !== b.score)
|
|
1138
1152
|
return b.score - a.score;
|
|
1139
1153
|
return a.id < b.id ? -1 : 1;
|
|
@@ -1145,7 +1159,7 @@ var init_resolve = __esm({
|
|
|
1145
1159
|
if (top === void 0 || bestBase < NONE_THRESHOLD) {
|
|
1146
1160
|
disposition = "none";
|
|
1147
1161
|
} else {
|
|
1148
|
-
const contenders = candidates.filter((c) => c.score >= top.score * CONTENDER_RATIO);
|
|
1162
|
+
const contenders = candidates.filter((c) => c.score >= top.score * CONTENDER_RATIO || parentMatchedIds.has(c.id));
|
|
1149
1163
|
const topCoverage = coverageById.get(top.id) ?? 0;
|
|
1150
1164
|
disposition = top.base >= EXACT_THRESHOLD && contenders.length === 1 && topCoverage >= EXACT_COVERAGE ? "exact" : "ambiguous";
|
|
1151
1165
|
}
|
|
@@ -2482,7 +2496,7 @@ var init_finding_suppression = __esm({
|
|
|
2482
2496
|
|
|
2483
2497
|
// ../mcp/dist/src/tools/crud-fls-audit.js
|
|
2484
2498
|
import { z as z2 } from "zod";
|
|
2485
|
-
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;
|
|
2486
2500
|
var init_crud_fls_audit = __esm({
|
|
2487
2501
|
"../mcp/dist/src/tools/crud-fls-audit.js"() {
|
|
2488
2502
|
"use strict";
|
|
@@ -2512,8 +2526,39 @@ var init_crud_fls_audit = __esm({
|
|
|
2512
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.";
|
|
2513
2527
|
DYNAMIC_SOQL_DISCLOSURE = "dynamic SOQL (Database.query) strings are stripped before pattern passes; the embedded SQL is invisible to the FLS recognizer.";
|
|
2514
2528
|
crudFlsAuditInputSchema = z2.object({
|
|
2515
|
-
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()
|
|
2516
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
|
+
};
|
|
2517
2562
|
coerceIssue = (raw) => {
|
|
2518
2563
|
if (raw === null || typeof raw !== "object")
|
|
2519
2564
|
return null;
|
|
@@ -2586,8 +2631,11 @@ var init_crud_fls_audit = __esm({
|
|
|
2586
2631
|
}
|
|
2587
2632
|
}
|
|
2588
2633
|
const classes = [...perClass.values()].sort(compareClassById);
|
|
2589
|
-
const
|
|
2590
|
-
const
|
|
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;
|
|
2591
2639
|
const boundaries = classes.length === 0 ? [] : [
|
|
2592
2640
|
Q80_FALSE_POSITIVE_DISCLOSURE,
|
|
2593
2641
|
CROSS_METHOD_DATAFLOW_DISCLOSURE,
|
|
@@ -2595,13 +2643,19 @@ var init_crud_fls_audit = __esm({
|
|
|
2595
2643
|
];
|
|
2596
2644
|
return ok({
|
|
2597
2645
|
data: {
|
|
2598
|
-
classes:
|
|
2646
|
+
classes: kept,
|
|
2599
2647
|
totalClassCount: classes.length,
|
|
2600
2648
|
totalFindingCount,
|
|
2601
2649
|
suppressedFindingCount,
|
|
2602
2650
|
byRule,
|
|
2603
2651
|
boundaries,
|
|
2604
|
-
|
|
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
|
+
} : {}
|
|
2605
2659
|
},
|
|
2606
2660
|
vaultState: {
|
|
2607
2661
|
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
@@ -7193,7 +7247,10 @@ var init_event_subscribers = __esm({
|
|
|
7193
7247
|
"Flow"
|
|
7194
7248
|
]);
|
|
7195
7249
|
eventSubscribersInputSchema = z29.object({
|
|
7196
|
-
|
|
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(),
|
|
7197
7254
|
limit: z29.number().int().min(1).max(EVENT_SUBSCRIBERS_MAX_LIMIT).optional()
|
|
7198
7255
|
});
|
|
7199
7256
|
validateEventId = (eventId) => {
|
|
@@ -7228,6 +7285,46 @@ var init_event_subscribers = __esm({
|
|
|
7228
7285
|
};
|
|
7229
7286
|
compareSubscribers2 = (a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
7230
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
|
+
}
|
|
7231
7328
|
const apiName = validateEventId(input2.eventId);
|
|
7232
7329
|
if (apiName === null) {
|
|
7233
7330
|
return err({
|
|
@@ -7236,7 +7333,6 @@ var init_event_subscribers = __esm({
|
|
|
7236
7333
|
path: "eventId"
|
|
7237
7334
|
});
|
|
7238
7335
|
}
|
|
7239
|
-
const limit = input2.limit ?? EVENT_SUBSCRIBERS_DEFAULT_LIMIT;
|
|
7240
7336
|
const edgesResult = await listEdges(ctx.graph, input2.eventId, {
|
|
7241
7337
|
direction: "in",
|
|
7242
7338
|
edgeType: "listensTo"
|
|
@@ -7460,6 +7556,23 @@ var init_explain_apex_method = __esm({
|
|
|
7460
7556
|
}
|
|
7461
7557
|
});
|
|
7462
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
|
+
|
|
7463
7576
|
// ../mcp/dist/src/tools/explain-field.js
|
|
7464
7577
|
import { z as z31 } from "zod";
|
|
7465
7578
|
var CUSTOM_FIELD_PREFIX, CUSTOM_METADATA_DEFINITION_SUFFIX, explainFieldInputSchema, readFieldType2, readFieldFormula, readFieldReferenceTo, readFieldDescription, readFieldInlineHelpText, readFieldRequired, readFieldLabel2, parentTypeApiName2, shouldIncludeRecordValues, readFieldApiName, findValueForField, compareRecordValues, collectRecordValues, explainFieldHandler;
|
|
@@ -7468,6 +7581,7 @@ var init_explain_field = __esm({
|
|
|
7468
7581
|
"use strict";
|
|
7469
7582
|
init_dist();
|
|
7470
7583
|
init_src();
|
|
7584
|
+
init_phantom_node();
|
|
7471
7585
|
CUSTOM_FIELD_PREFIX = "CustomField:";
|
|
7472
7586
|
CUSTOM_METADATA_DEFINITION_SUFFIX = "__mdt";
|
|
7473
7587
|
explainFieldInputSchema = z31.object({
|
|
@@ -7581,7 +7695,7 @@ var init_explain_field = __esm({
|
|
|
7581
7695
|
if (node === null) {
|
|
7582
7696
|
return err({
|
|
7583
7697
|
kind: "component-not-found",
|
|
7584
|
-
message:
|
|
7698
|
+
message: await phantomAwareNotFoundMessage(ctx, input2.fieldId, "CustomField"),
|
|
7585
7699
|
path: input2.fieldId
|
|
7586
7700
|
});
|
|
7587
7701
|
}
|
|
@@ -9900,7 +10014,7 @@ var init_code_quality_patterns = __esm({
|
|
|
9900
10014
|
return [];
|
|
9901
10015
|
const issues = [];
|
|
9902
10016
|
const seen = /* @__PURE__ */ new Set();
|
|
9903
|
-
const
|
|
10017
|
+
const collect2 = (re, op, sobject) => {
|
|
9904
10018
|
const r = new RegExp(re.source, "g");
|
|
9905
10019
|
let m;
|
|
9906
10020
|
while ((m = r.exec(stripped)) !== null) {
|
|
@@ -9920,8 +10034,8 @@ var init_code_quality_patterns = __esm({
|
|
|
9920
10034
|
});
|
|
9921
10035
|
}
|
|
9922
10036
|
};
|
|
9923
|
-
|
|
9924
|
-
|
|
10037
|
+
collect2(DML_STATEMENT_PATTERN, "dml", null);
|
|
10038
|
+
collect2(DML_DATABASE_CALL_PATTERN, "dml", null);
|
|
9925
10039
|
return issues;
|
|
9926
10040
|
};
|
|
9927
10041
|
INLINE_SOQL_PATTERN = /\[\s*SELECT\b([\s\S]*?)\]/gi;
|
|
@@ -10384,14 +10498,32 @@ var init_src4 = __esm({
|
|
|
10384
10498
|
|
|
10385
10499
|
// ../mcp/dist/src/tools/field-access-audit.js
|
|
10386
10500
|
import { z as z35 } from "zod";
|
|
10387
|
-
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;
|
|
10388
10502
|
var init_field_access_audit = __esm({
|
|
10389
10503
|
"../mcp/dist/src/tools/field-access-audit.js"() {
|
|
10390
10504
|
"use strict";
|
|
10391
10505
|
init_dist();
|
|
10392
10506
|
init_src();
|
|
10393
10507
|
init_src4();
|
|
10508
|
+
init_phantom_node();
|
|
10394
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
|
+
};
|
|
10395
10527
|
PERMISSION_TYPE_VALUES = ["read", "edit", "all"];
|
|
10396
10528
|
GRANTOR_TYPES = /* @__PURE__ */ new Set([
|
|
10397
10529
|
"Profile",
|
|
@@ -10445,14 +10577,6 @@ var init_field_access_audit = __esm({
|
|
|
10445
10577
|
});
|
|
10446
10578
|
}
|
|
10447
10579
|
const fieldNode = fieldResult.value;
|
|
10448
|
-
if (fieldNode === null) {
|
|
10449
|
-
return err({
|
|
10450
|
-
kind: "component-not-found",
|
|
10451
|
-
message: `no field with id ${fieldId}`,
|
|
10452
|
-
path: fieldId
|
|
10453
|
-
});
|
|
10454
|
-
}
|
|
10455
|
-
const detection = detectPiiClassificationWithReason(fieldNode);
|
|
10456
10580
|
const grantedByResult = await listEdges(ctx.graph, fieldId, {
|
|
10457
10581
|
direction: "in",
|
|
10458
10582
|
edgeType: "grantedBy"
|
|
@@ -10472,6 +10596,16 @@ var init_field_access_audit = __esm({
|
|
|
10472
10596
|
message: `graph query failed: ${allIncomingResult.error.message}`
|
|
10473
10597
|
});
|
|
10474
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);
|
|
10475
10609
|
let profilesWithRead = 0;
|
|
10476
10610
|
let profilesWithEdit = 0;
|
|
10477
10611
|
let profilesWithUnknown = 0;
|
|
@@ -10553,7 +10687,11 @@ var init_field_access_audit = __esm({
|
|
|
10553
10687
|
return ok({
|
|
10554
10688
|
data: {
|
|
10555
10689
|
fieldId,
|
|
10556
|
-
fieldLabel:
|
|
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
|
+
} : {},
|
|
10557
10695
|
piiClassification: detection.piiClassification,
|
|
10558
10696
|
piiCategory: detection.piiCategory,
|
|
10559
10697
|
grants: [...grants].sort(compareGrants),
|
|
@@ -10585,6 +10723,7 @@ var init_safe_to_delete_field = __esm({
|
|
|
10585
10723
|
init_dist();
|
|
10586
10724
|
init_src();
|
|
10587
10725
|
init_src2();
|
|
10726
|
+
init_phantom_node();
|
|
10588
10727
|
CUSTOM_FIELD_PREFIX4 = "CustomField:";
|
|
10589
10728
|
EXAMPLES_PER_CATEGORY_LIMIT = 5;
|
|
10590
10729
|
CATEGORY_ORDER = [
|
|
@@ -10793,10 +10932,49 @@ var init_safe_to_delete_field = __esm({
|
|
|
10793
10932
|
});
|
|
10794
10933
|
}
|
|
10795
10934
|
if (nodeResult.value === null) {
|
|
10796
|
-
|
|
10797
|
-
|
|
10798
|
-
|
|
10799
|
-
|
|
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
|
+
}
|
|
10800
10978
|
});
|
|
10801
10979
|
}
|
|
10802
10980
|
if (nodeResult.value.properties["system"] === true) {
|
|
@@ -13287,6 +13465,11 @@ var init_find_dead_code = __esm({
|
|
|
13287
13465
|
FALSE) AS is_own_entry_point
|
|
13288
13466
|
FROM nodes
|
|
13289
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 '\\')
|
|
13290
13473
|
),
|
|
13291
13474
|
incoming AS (
|
|
13292
13475
|
SELECT e.to_id AS cid,
|
|
@@ -16144,7 +16327,7 @@ var init_generate_architecture_overview = __esm({
|
|
|
16144
16327
|
|
|
16145
16328
|
// ../mcp/dist/src/tools/pii-inventory.js
|
|
16146
16329
|
import { z as z60 } from "zod";
|
|
16147
|
-
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;
|
|
16148
16331
|
var init_pii_inventory = __esm({
|
|
16149
16332
|
"../mcp/dist/src/tools/pii-inventory.js"() {
|
|
16150
16333
|
"use strict";
|
|
@@ -16165,8 +16348,23 @@ var init_pii_inventory = __esm({
|
|
|
16165
16348
|
piiInventoryInputSchema = z60.object({
|
|
16166
16349
|
classification: z60.enum(CLASSIFICATION_FILTER_VALUES).optional(),
|
|
16167
16350
|
category: z60.enum(CATEGORY_FILTER_VALUES).optional(),
|
|
16168
|
-
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()
|
|
16169
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
|
+
};
|
|
16170
16368
|
emptyClassificationCounts = () => ({
|
|
16171
16369
|
pii: 0,
|
|
16172
16370
|
sensitive: 0,
|
|
@@ -16253,17 +16451,26 @@ var init_pii_inventory = __esm({
|
|
|
16253
16451
|
});
|
|
16254
16452
|
}
|
|
16255
16453
|
const sorted = [...matched].sort(compareFields);
|
|
16256
|
-
const
|
|
16257
|
-
const
|
|
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;
|
|
16258
16459
|
return ok({
|
|
16259
16460
|
data: {
|
|
16260
|
-
fields,
|
|
16461
|
+
fields: kept,
|
|
16261
16462
|
summary: {
|
|
16262
16463
|
total: matched.length,
|
|
16263
16464
|
byClassification,
|
|
16264
16465
|
byCategory
|
|
16265
16466
|
},
|
|
16266
|
-
|
|
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
|
+
} : {}
|
|
16267
16474
|
},
|
|
16268
16475
|
vaultState: {
|
|
16269
16476
|
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
@@ -16977,6 +17184,7 @@ var init_get_component = __esm({
|
|
|
16977
17184
|
init_dist();
|
|
16978
17185
|
init_src();
|
|
16979
17186
|
init_src2();
|
|
17187
|
+
init_phantom_node();
|
|
16980
17188
|
DEFAULT_COMPONENT_BODY_MAX_BYTES = 3e4;
|
|
16981
17189
|
getComponentInputSchema = z64.object({
|
|
16982
17190
|
id: z64.string().min(1),
|
|
@@ -16991,9 +17199,10 @@ var init_get_component = __esm({
|
|
|
16991
17199
|
});
|
|
16992
17200
|
}
|
|
16993
17201
|
if (nodeResult.value === null) {
|
|
17202
|
+
const kindLabel = input2.id.includes(":") ? input2.id.slice(0, input2.id.indexOf(":")) : "component";
|
|
16994
17203
|
return err({
|
|
16995
17204
|
kind: "component-not-found",
|
|
16996
|
-
message:
|
|
17205
|
+
message: await phantomAwareNotFoundMessage(ctx, input2.id, kindLabel),
|
|
16997
17206
|
path: input2.id
|
|
16998
17207
|
});
|
|
16999
17208
|
}
|
|
@@ -17448,11 +17657,201 @@ var init_get_subgraph = __esm({
|
|
|
17448
17657
|
}
|
|
17449
17658
|
});
|
|
17450
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
|
+
|
|
17451
17850
|
// ../mcp/dist/src/tools/health-check.js
|
|
17452
17851
|
import { existsSync as existsSync3 } from "node:fs";
|
|
17453
17852
|
import { stat as stat4 } from "node:fs/promises";
|
|
17454
17853
|
import { join as join7 } from "node:path";
|
|
17455
|
-
import { z as
|
|
17854
|
+
import { z as z69 } from "zod";
|
|
17456
17855
|
var SKIPPED_FILES_DEGRADED_THRESHOLD, healthCheckInputSchema, probeGraph, probeSourceHash, probeRenderComplete, healthCheckHandler;
|
|
17457
17856
|
var init_health_check = __esm({
|
|
17458
17857
|
"../mcp/dist/src/tools/health-check.js"() {
|
|
@@ -17461,7 +17860,7 @@ var init_health_check = __esm({
|
|
|
17461
17860
|
init_src();
|
|
17462
17861
|
init_src2();
|
|
17463
17862
|
SKIPPED_FILES_DEGRADED_THRESHOLD = 100;
|
|
17464
|
-
healthCheckInputSchema =
|
|
17863
|
+
healthCheckInputSchema = z69.object({});
|
|
17465
17864
|
probeGraph = async (ctx) => {
|
|
17466
17865
|
try {
|
|
17467
17866
|
const result = await listNodesByType(ctx.graph, "CustomObject", { limit: 1 });
|
|
@@ -17581,7 +17980,7 @@ var init_health_check = __esm({
|
|
|
17581
17980
|
// ../mcp/dist/src/tools/integration-procedure-chain.js
|
|
17582
17981
|
import { readFile as readFile10 } from "node:fs/promises";
|
|
17583
17982
|
import { XMLParser as XMLParser3, XMLValidator as XMLValidator3 } from "fast-xml-parser";
|
|
17584
|
-
import { z as
|
|
17983
|
+
import { z as z70 } from "zod";
|
|
17585
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;
|
|
17586
17985
|
var init_integration_procedure_chain = __esm({
|
|
17587
17986
|
"../mcp/dist/src/tools/integration-procedure-chain.js"() {
|
|
@@ -17605,9 +18004,9 @@ var init_integration_procedure_chain = __esm({
|
|
|
17605
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.";
|
|
17606
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.";
|
|
17607
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.";
|
|
17608
|
-
integrationProcedureChainInputSchema =
|
|
17609
|
-
integrationProcedureId:
|
|
17610
|
-
includeChildPropertySetConfig:
|
|
18007
|
+
integrationProcedureChainInputSchema = z70.object({
|
|
18008
|
+
integrationProcedureId: z70.string().min(1),
|
|
18009
|
+
includeChildPropertySetConfig: z70.boolean().optional()
|
|
17611
18010
|
});
|
|
17612
18011
|
unwrapSingle3 = (value) => Array.isArray(value) ? value[0] : value;
|
|
17613
18012
|
toArray3 = (value) => {
|
|
@@ -17909,7 +18308,7 @@ var init_integration_procedure_chain = __esm({
|
|
|
17909
18308
|
});
|
|
17910
18309
|
|
|
17911
18310
|
// ../mcp/dist/src/tools/last-modified.js
|
|
17912
|
-
import { z as
|
|
18311
|
+
import { z as z71 } from "zod";
|
|
17913
18312
|
var LAST_MODIFIED_UNENRICHED_DISCLOSURE, LAST_MODIFIED_ENRICHED_DISCLOSURE, lastModifiedInputSchema, extractLastModifiedDate2, extractLastModifiedBy2, extractApiVersion, lastModifiedHandler;
|
|
17914
18313
|
var init_last_modified = __esm({
|
|
17915
18314
|
"../mcp/dist/src/tools/last-modified.js"() {
|
|
@@ -17918,8 +18317,8 @@ var init_last_modified = __esm({
|
|
|
17918
18317
|
init_src();
|
|
17919
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.";
|
|
17920
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).";
|
|
17921
|
-
lastModifiedInputSchema =
|
|
17922
|
-
componentId:
|
|
18320
|
+
lastModifiedInputSchema = z71.object({
|
|
18321
|
+
componentId: z71.string().min(1)
|
|
17923
18322
|
});
|
|
17924
18323
|
extractLastModifiedDate2 = (legacy, properties) => {
|
|
17925
18324
|
const propsValue = properties["lastModifiedDate"];
|
|
@@ -17995,17 +18394,17 @@ var init_last_modified = __esm({
|
|
|
17995
18394
|
});
|
|
17996
18395
|
|
|
17997
18396
|
// ../mcp/dist/src/tools/layout-for-user.js
|
|
17998
|
-
import { z as
|
|
18397
|
+
import { z as z72 } from "zod";
|
|
17999
18398
|
var layoutForUserInputSchema, step, evaluateProfileLookup, readLayoutAssignments, layoutTargetsObject, canonicaliseLayoutId, findLayoutAssignment, pickFlexiPageForObject, evaluateLightningPageLookup, layoutForUserHandler;
|
|
18000
18399
|
var init_layout_for_user = __esm({
|
|
18001
18400
|
"../mcp/dist/src/tools/layout-for-user.js"() {
|
|
18002
18401
|
"use strict";
|
|
18003
18402
|
init_dist();
|
|
18004
18403
|
init_src();
|
|
18005
|
-
layoutForUserInputSchema =
|
|
18006
|
-
objectApiName:
|
|
18007
|
-
recordTypeId:
|
|
18008
|
-
profileId:
|
|
18404
|
+
layoutForUserInputSchema = z72.object({
|
|
18405
|
+
objectApiName: z72.string().min(1),
|
|
18406
|
+
recordTypeId: z72.string().min(1).optional(),
|
|
18407
|
+
profileId: z72.string().min(1)
|
|
18009
18408
|
});
|
|
18010
18409
|
step = (stage, verdict, reason, value) => value === void 0 ? { stage, verdict, reason } : { stage, verdict, reason, value };
|
|
18011
18410
|
evaluateProfileLookup = async (ctx, profileId) => {
|
|
@@ -18223,7 +18622,7 @@ var init_layout_for_user = __esm({
|
|
|
18223
18622
|
});
|
|
18224
18623
|
|
|
18225
18624
|
// ../mcp/dist/src/tools/list-components.js
|
|
18226
|
-
import { z as
|
|
18625
|
+
import { z as z73 } from "zod";
|
|
18227
18626
|
var COMPONENT_TYPES2, LIST_MAX_LIMIT2, LIST_DEFAULT_LIMIT2, LIST_PAYLOAD_BUDGET_BYTES, fitNodesToBudget, listComponentsInputSchema, listComponentsHandler;
|
|
18228
18627
|
var init_list_components = __esm({
|
|
18229
18628
|
"../mcp/dist/src/tools/list-components.js"() {
|
|
@@ -18332,11 +18731,11 @@ var init_list_components = __esm({
|
|
|
18332
18731
|
}
|
|
18333
18732
|
return { kept, trimmed: false };
|
|
18334
18733
|
};
|
|
18335
|
-
listComponentsInputSchema =
|
|
18336
|
-
type:
|
|
18337
|
-
parentId:
|
|
18338
|
-
limit:
|
|
18339
|
-
offset:
|
|
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()
|
|
18340
18739
|
});
|
|
18341
18740
|
listComponentsHandler = async (ctx, input2) => {
|
|
18342
18741
|
if (input2.type === void 0) {
|
|
@@ -18963,7 +19362,7 @@ var init_live_consent = __esm({
|
|
|
18963
19362
|
// ../mcp/dist/src/tools/live-plane.js
|
|
18964
19363
|
import { execFile as execFile2 } from "node:child_process";
|
|
18965
19364
|
import { promisify as promisify2 } from "node:util";
|
|
18966
|
-
import { z as
|
|
19365
|
+
import { z as z74 } from "zod";
|
|
18967
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;
|
|
18968
19367
|
var init_live_plane = __esm({
|
|
18969
19368
|
"../mcp/dist/src/tools/live-plane.js"() {
|
|
@@ -18976,8 +19375,8 @@ var init_live_plane = __esm({
|
|
|
18976
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]");
|
|
18977
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.";
|
|
18978
19377
|
MAX_SAMPLE_ROWS = 200;
|
|
18979
|
-
liveEnabledSchema =
|
|
18980
|
-
liveEnabled:
|
|
19378
|
+
liveEnabledSchema = z74.object({
|
|
19379
|
+
liveEnabled: z74.boolean().optional()
|
|
18981
19380
|
});
|
|
18982
19381
|
isLivePlaneEnabled = (input2) => {
|
|
18983
19382
|
if (input2 === true)
|
|
@@ -19055,8 +19454,8 @@ var init_live_plane = __esm({
|
|
|
19055
19454
|
}
|
|
19056
19455
|
};
|
|
19057
19456
|
liveDescribeInputSchema = liveEnabledSchema.extend({
|
|
19058
|
-
objectApiName:
|
|
19059
|
-
orgAlias:
|
|
19457
|
+
objectApiName: z74.string().min(1),
|
|
19458
|
+
orgAlias: z74.string().min(1).optional()
|
|
19060
19459
|
});
|
|
19061
19460
|
liveDescribeHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
19062
19461
|
const gate = await gateLive(ctx, input2);
|
|
@@ -19084,9 +19483,9 @@ var init_live_plane = __esm({
|
|
|
19084
19483
|
// Either `soql` (a SELECT COUNT() query) OR `objectApiName` (count every row
|
|
19085
19484
|
// of that object). Both optional at the schema level; the handler requires
|
|
19086
19485
|
// exactly one and turns objectApiName into `SELECT COUNT() FROM <object>`.
|
|
19087
|
-
soql:
|
|
19088
|
-
objectApiName:
|
|
19089
|
-
orgAlias:
|
|
19486
|
+
soql: z74.string().min(1).optional(),
|
|
19487
|
+
objectApiName: z74.string().min(1).optional(),
|
|
19488
|
+
orgAlias: z74.string().min(1).optional()
|
|
19090
19489
|
});
|
|
19091
19490
|
OBJECT_API_NAME_RE = /^[A-Za-z][A-Za-z0-9_]*$/;
|
|
19092
19491
|
resolveCountSoql = (input2) => {
|
|
@@ -19150,9 +19549,9 @@ var init_live_plane = __esm({
|
|
|
19150
19549
|
});
|
|
19151
19550
|
};
|
|
19152
19551
|
liveSampleInputSchema = liveEnabledSchema.extend({
|
|
19153
|
-
soql:
|
|
19154
|
-
limit:
|
|
19155
|
-
orgAlias:
|
|
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()
|
|
19156
19555
|
});
|
|
19157
19556
|
capSampleSoql = (soql, limit) => {
|
|
19158
19557
|
const trimmed = soql.trim().replace(/;\s*$/, "");
|
|
@@ -19189,9 +19588,9 @@ var init_live_plane = __esm({
|
|
|
19189
19588
|
});
|
|
19190
19589
|
};
|
|
19191
19590
|
liveFieldPopulationInputSchema = liveEnabledSchema.extend({
|
|
19192
|
-
objectApiName:
|
|
19193
|
-
fieldApiName:
|
|
19194
|
-
orgAlias:
|
|
19591
|
+
objectApiName: z74.string().min(1),
|
|
19592
|
+
fieldApiName: z74.string().min(1),
|
|
19593
|
+
orgAlias: z74.string().min(1).optional()
|
|
19195
19594
|
});
|
|
19196
19595
|
liveFieldPopulationHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
19197
19596
|
const gate = await gateLive(ctx, input2);
|
|
@@ -19237,7 +19636,7 @@ var init_live_plane = __esm({
|
|
|
19237
19636
|
});
|
|
19238
19637
|
};
|
|
19239
19638
|
liveOrgLimitsInputSchema = liveEnabledSchema.extend({
|
|
19240
|
-
orgAlias:
|
|
19639
|
+
orgAlias: z74.string().min(1).optional()
|
|
19241
19640
|
});
|
|
19242
19641
|
liveOrgLimitsHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
19243
19642
|
const gate = await gateLive(ctx, input2);
|
|
@@ -19268,14 +19667,14 @@ var init_live_plane = __esm({
|
|
|
19268
19667
|
liveInactiveUsersInputSchema = liveEnabledSchema.extend({
|
|
19269
19668
|
/** Inactivity threshold in days (default 30). A user is "inactive" if their
|
|
19270
19669
|
* last login is older than this — or they have never logged in. */
|
|
19271
|
-
days:
|
|
19670
|
+
days: z74.number().int().min(1).max(3650).optional(),
|
|
19272
19671
|
/** Include non-Standard user types (integration/system/etc.). Default false
|
|
19273
19672
|
* → only human (Standard) users, the usual intent of "who hasn't logged in". */
|
|
19274
|
-
includeAllUserTypes:
|
|
19673
|
+
includeAllUserTypes: z74.boolean().optional(),
|
|
19275
19674
|
/** Max detail rows returned (default + hard cap 500). The TOTAL count is
|
|
19276
19675
|
* always reported separately, so a capped list never understates the count. */
|
|
19277
|
-
limit:
|
|
19278
|
-
orgAlias:
|
|
19676
|
+
limit: z74.number().int().min(1).max(MAX_INACTIVE_USER_ROWS).optional(),
|
|
19677
|
+
orgAlias: z74.string().min(1).optional()
|
|
19279
19678
|
});
|
|
19280
19679
|
soqlDateTime = (d) => d.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
19281
19680
|
liveInactiveUsersHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
@@ -19341,10 +19740,10 @@ var init_live_plane = __esm({
|
|
|
19341
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.';
|
|
19342
19741
|
liveLicenseUsageInputSchema = liveEnabledSchema.extend({
|
|
19343
19742
|
/** Dormancy window for reclaimable seats, in days (default 90). */
|
|
19344
|
-
inactiveDays:
|
|
19743
|
+
inactiveDays: z74.number().int().min(1).max(3650).optional(),
|
|
19345
19744
|
/** Max reclaimable-seat groups returned (default + hard cap 200). */
|
|
19346
|
-
limit:
|
|
19347
|
-
orgAlias:
|
|
19745
|
+
limit: z74.number().int().min(1).max(MAX_RECLAIM_ROWS).optional(),
|
|
19746
|
+
orgAlias: z74.string().min(1).optional()
|
|
19348
19747
|
});
|
|
19349
19748
|
toUtilization = (rows, nameKey) => rows.map((r) => {
|
|
19350
19749
|
const total = Number(r.TotalLicenses ?? 0);
|
|
@@ -19432,13 +19831,13 @@ var init_live_plane = __esm({
|
|
|
19432
19831
|
}
|
|
19433
19832
|
});
|
|
19434
19833
|
};
|
|
19435
|
-
liveConsentInputSchema =
|
|
19834
|
+
liveConsentInputSchema = z74.object({
|
|
19436
19835
|
/** Org alias/username; defaults to the vault's source org. */
|
|
19437
|
-
orgAlias:
|
|
19836
|
+
orgAlias: z74.string().min(1).optional(),
|
|
19438
19837
|
/** Grant standing consent for the org (persists across sessions). */
|
|
19439
|
-
grant:
|
|
19838
|
+
grant: z74.boolean().optional(),
|
|
19440
19839
|
/** Revoke standing consent for the org. */
|
|
19441
|
-
revoke:
|
|
19840
|
+
revoke: z74.boolean().optional()
|
|
19442
19841
|
});
|
|
19443
19842
|
consentTrust = () => ({
|
|
19444
19843
|
provenance: "offline_snapshot",
|
|
@@ -19525,12 +19924,12 @@ var init_live_plane = __esm({
|
|
|
19525
19924
|
DEFAULT_STALE_DAYS = 90;
|
|
19526
19925
|
DEFAULT_RECENT_DAYS = 7;
|
|
19527
19926
|
liveGroupCountInputSchema = liveEnabledSchema.extend({
|
|
19528
|
-
objectApiName:
|
|
19529
|
-
groupByField:
|
|
19530
|
-
limit:
|
|
19531
|
-
filterField:
|
|
19532
|
-
filterValue:
|
|
19533
|
-
orgAlias:
|
|
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()
|
|
19534
19933
|
});
|
|
19535
19934
|
liveGroupCountHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
19536
19935
|
const gate = await gateLive(ctx, input2);
|
|
@@ -19598,12 +19997,12 @@ ${renderTrustFooter(trust)}`;
|
|
|
19598
19997
|
});
|
|
19599
19998
|
};
|
|
19600
19999
|
liveStaleRecordsInputSchema = liveEnabledSchema.extend({
|
|
19601
|
-
objectApiName:
|
|
19602
|
-
staleDays:
|
|
19603
|
-
dateField:
|
|
19604
|
-
includeNeverSet:
|
|
19605
|
-
limit:
|
|
19606
|
-
orgAlias:
|
|
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()
|
|
19607
20006
|
});
|
|
19608
20007
|
liveStaleRecordsHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
19609
20008
|
const gate = await gateLive(ctx, input2);
|
|
@@ -19665,11 +20064,11 @@ ${renderTrustFooter(trust)}`;
|
|
|
19665
20064
|
});
|
|
19666
20065
|
};
|
|
19667
20066
|
liveRecentActivityInputSchema = liveEnabledSchema.extend({
|
|
19668
|
-
objectApiName:
|
|
19669
|
-
days:
|
|
19670
|
-
activity:
|
|
19671
|
-
limit:
|
|
19672
|
-
orgAlias:
|
|
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()
|
|
19673
20072
|
});
|
|
19674
20073
|
liveRecentActivityHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
19675
20074
|
const gate = await gateLive(ctx, input2);
|
|
@@ -19747,11 +20146,11 @@ ${renderTrustFooter(trust)}`;
|
|
|
19747
20146
|
return 0;
|
|
19748
20147
|
};
|
|
19749
20148
|
liveAggregateInputSchema = liveEnabledSchema.extend({
|
|
19750
|
-
objectApiName:
|
|
19751
|
-
fieldApiName:
|
|
19752
|
-
filterField:
|
|
19753
|
-
filterValue:
|
|
19754
|
-
orgAlias:
|
|
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()
|
|
19755
20154
|
});
|
|
19756
20155
|
liveAggregateHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
19757
20156
|
const gate = await gateLive(ctx, input2);
|
|
@@ -19811,12 +20210,12 @@ ${renderTrustFooter(trust)}`;
|
|
|
19811
20210
|
};
|
|
19812
20211
|
MAX_DUPLICATE_GROUPS = 100;
|
|
19813
20212
|
liveDuplicateCheckInputSchema = liveEnabledSchema.extend({
|
|
19814
|
-
objectApiName:
|
|
19815
|
-
fieldApiName:
|
|
19816
|
-
limit:
|
|
19817
|
-
filterField:
|
|
19818
|
-
filterValue:
|
|
19819
|
-
orgAlias:
|
|
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()
|
|
19820
20219
|
});
|
|
19821
20220
|
liveDuplicateCheckHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
19822
20221
|
const gate = await gateLive(ctx, input2);
|
|
@@ -19875,11 +20274,11 @@ ${renderTrustFooter(trust)}`;
|
|
|
19875
20274
|
};
|
|
19876
20275
|
MAX_OWNER_BUCKETS = 100;
|
|
19877
20276
|
liveOwnerBreakdownInputSchema = liveEnabledSchema.extend({
|
|
19878
|
-
objectApiName:
|
|
19879
|
-
limit:
|
|
19880
|
-
filterField:
|
|
19881
|
-
filterValue:
|
|
19882
|
-
orgAlias:
|
|
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()
|
|
19883
20282
|
});
|
|
19884
20283
|
liveOwnerBreakdownHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
19885
20284
|
const gate = await gateLive(ctx, input2);
|
|
@@ -19956,9 +20355,9 @@ ${renderTrustFooter(trust)}`;
|
|
|
19956
20355
|
};
|
|
19957
20356
|
DEFAULT_REPORT_STALE_DAYS = 90;
|
|
19958
20357
|
liveReportUsageInputSchema = liveEnabledSchema.extend({
|
|
19959
|
-
staleDays:
|
|
19960
|
-
limit:
|
|
19961
|
-
orgAlias:
|
|
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()
|
|
19962
20361
|
});
|
|
19963
20362
|
liveReportUsageHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
19964
20363
|
const gate = await gateLive(ctx, input2);
|
|
@@ -20010,9 +20409,9 @@ ${renderTrustFooter(trust)}`;
|
|
|
20010
20409
|
});
|
|
20011
20410
|
};
|
|
20012
20411
|
liveFolderAccessInputSchema = liveEnabledSchema.extend({
|
|
20013
|
-
folderType:
|
|
20014
|
-
limit:
|
|
20015
|
-
orgAlias:
|
|
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()
|
|
20016
20415
|
});
|
|
20017
20416
|
liveFolderAccessHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
20018
20417
|
const gate = await gateLive(ctx, input2);
|
|
@@ -20067,9 +20466,9 @@ ${renderTrustFooter(trust)}`;
|
|
|
20067
20466
|
DEFAULT_TEMPLATE_STALE_DAYS = 180;
|
|
20068
20467
|
CLASSIC_TEMPLATE_TYPES = /* @__PURE__ */ new Set(["text", "html", "custom", "visualforce"]);
|
|
20069
20468
|
liveEmailTemplateUsageInputSchema = liveEnabledSchema.extend({
|
|
20070
|
-
staleDays:
|
|
20071
|
-
limit:
|
|
20072
|
-
orgAlias:
|
|
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()
|
|
20073
20472
|
});
|
|
20074
20473
|
liveEmailTemplateUsageHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
20075
20474
|
const gate = await gateLive(ctx, input2);
|
|
@@ -20134,8 +20533,8 @@ ${renderTrustFooter(trust)}`;
|
|
|
20134
20533
|
DEFAULT_HEALTH_DAYS = 7;
|
|
20135
20534
|
LIMIT_RISK_THRESHOLD = 0.8;
|
|
20136
20535
|
liveOrgHealthInputSchema = liveEnabledSchema.extend({
|
|
20137
|
-
days:
|
|
20138
|
-
orgAlias:
|
|
20536
|
+
days: z74.number().int().min(1).max(90).optional(),
|
|
20537
|
+
orgAlias: z74.string().min(1).optional()
|
|
20139
20538
|
});
|
|
20140
20539
|
liveOrgHealthHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
20141
20540
|
const gate = await gateLive(ctx, input2);
|
|
@@ -20202,9 +20601,9 @@ ${renderTrustFooter(trust)}`;
|
|
|
20202
20601
|
});
|
|
20203
20602
|
};
|
|
20204
20603
|
liveStorageByObjectInputSchema = liveEnabledSchema.extend({
|
|
20205
|
-
limit:
|
|
20206
|
-
objectApiNames:
|
|
20207
|
-
orgAlias:
|
|
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()
|
|
20208
20607
|
});
|
|
20209
20608
|
liveStorageByObjectHandler = async (ctx, input2, exec4 = nodeExecFile2) => {
|
|
20210
20609
|
const gate = await gateLive(ctx, input2);
|
|
@@ -20247,25 +20646,25 @@ ${renderTrustFooter(trust)}`;
|
|
|
20247
20646
|
});
|
|
20248
20647
|
};
|
|
20249
20648
|
liveDataSkewInputSchema = liveEnabledSchema.extend({
|
|
20250
|
-
objectApiName:
|
|
20251
|
-
ownerField:
|
|
20252
|
-
threshold:
|
|
20253
|
-
limit:
|
|
20254
|
-
orgAlias:
|
|
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()
|
|
20255
20654
|
});
|
|
20256
20655
|
liveSetupAuditTrailInputSchema = liveEnabledSchema.extend({
|
|
20257
|
-
days:
|
|
20258
|
-
limit:
|
|
20259
|
-
orgAlias:
|
|
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()
|
|
20260
20659
|
});
|
|
20261
20660
|
liveSecurityExposureInputSchema = liveEnabledSchema.extend({
|
|
20262
|
-
orgAlias:
|
|
20661
|
+
orgAlias: z74.string().min(1).optional()
|
|
20263
20662
|
});
|
|
20264
20663
|
}
|
|
20265
20664
|
});
|
|
20266
20665
|
|
|
20267
20666
|
// ../mcp/dist/src/tools/live-drift-check.js
|
|
20268
|
-
import { z as
|
|
20667
|
+
import { z as z75 } from "zod";
|
|
20269
20668
|
var FIELD_PAGE_SIZE2, liveDriftCheckInputSchema, BOUNDARIES8, isCustomField2, diffFields, liveFieldNames, liveDriftCheckHandler;
|
|
20270
20669
|
var init_live_drift_check = __esm({
|
|
20271
20670
|
"../mcp/dist/src/tools/live-drift-check.js"() {
|
|
@@ -20274,11 +20673,11 @@ var init_live_drift_check = __esm({
|
|
|
20274
20673
|
init_src();
|
|
20275
20674
|
init_live_plane();
|
|
20276
20675
|
FIELD_PAGE_SIZE2 = 500;
|
|
20277
|
-
liveDriftCheckInputSchema =
|
|
20278
|
-
objectApiName:
|
|
20279
|
-
orgAlias:
|
|
20676
|
+
liveDriftCheckInputSchema = z75.object({
|
|
20677
|
+
objectApiName: z75.string().min(1),
|
|
20678
|
+
orgAlias: z75.string().min(1).optional(),
|
|
20280
20679
|
/** Opt-in to the live plane (mirrors the other live_* tools). */
|
|
20281
|
-
liveEnabled:
|
|
20680
|
+
liveEnabled: z75.boolean().optional()
|
|
20282
20681
|
});
|
|
20283
20682
|
BOUNDARIES8 = Object.freeze([
|
|
20284
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.",
|
|
@@ -20346,7 +20745,7 @@ var init_live_drift_check = __esm({
|
|
|
20346
20745
|
});
|
|
20347
20746
|
|
|
20348
20747
|
// ../mcp/dist/src/tools/lookup-record.js
|
|
20349
|
-
import { z as
|
|
20748
|
+
import { z as z76 } from "zod";
|
|
20350
20749
|
var RECORD_NODE_TYPES, CUSTOM_METADATA_RECORD_PREFIX, CUSTOM_SETTING_RECORD_PREFIX, lookupRecordInputSchema, classifyRecordId, normalizeValueEntry, readRecordValues, readTypeApiName, lookupRecordHandler;
|
|
20351
20750
|
var init_lookup_record = __esm({
|
|
20352
20751
|
"../mcp/dist/src/tools/lookup-record.js"() {
|
|
@@ -20359,8 +20758,8 @@ var init_lookup_record = __esm({
|
|
|
20359
20758
|
]);
|
|
20360
20759
|
CUSTOM_METADATA_RECORD_PREFIX = "CustomMetadataRecord:";
|
|
20361
20760
|
CUSTOM_SETTING_RECORD_PREFIX = "CustomSettingRecord:";
|
|
20362
|
-
lookupRecordInputSchema =
|
|
20363
|
-
recordId:
|
|
20761
|
+
lookupRecordInputSchema = z76.object({
|
|
20762
|
+
recordId: z76.string().min(1)
|
|
20364
20763
|
});
|
|
20365
20764
|
classifyRecordId = (recordId) => {
|
|
20366
20765
|
if (recordId.startsWith(CUSTOM_METADATA_RECORD_PREFIX)) {
|
|
@@ -20453,14 +20852,14 @@ var init_lookup_record = __esm({
|
|
|
20453
20852
|
});
|
|
20454
20853
|
|
|
20455
20854
|
// ../mcp/dist/src/tools/manifest.js
|
|
20456
|
-
import { z as
|
|
20855
|
+
import { z as z77 } from "zod";
|
|
20457
20856
|
var getManifestInputSchema, getManifestHandler;
|
|
20458
20857
|
var init_manifest2 = __esm({
|
|
20459
20858
|
"../mcp/dist/src/tools/manifest.js"() {
|
|
20460
20859
|
"use strict";
|
|
20461
20860
|
init_dist();
|
|
20462
20861
|
init_src2();
|
|
20463
|
-
getManifestInputSchema =
|
|
20862
|
+
getManifestInputSchema = z77.object({});
|
|
20464
20863
|
getManifestHandler = async (ctx, _input) => {
|
|
20465
20864
|
const data = {
|
|
20466
20865
|
...ctx.manifest,
|
|
@@ -20478,7 +20877,7 @@ var init_manifest2 = __esm({
|
|
|
20478
20877
|
});
|
|
20479
20878
|
|
|
20480
20879
|
// ../mcp/dist/src/tools/meaningful-test-audit.js
|
|
20481
|
-
import { z as
|
|
20880
|
+
import { z as z78 } from "zod";
|
|
20482
20881
|
var LIST_PAGE_SIZE8, APEX_CLASS_PREFIX5, MEANINGFUL_TEST_DISCLOSURE, meaningfulTestAuditInputSchema, isTestClass3, readNonNegativeInt, collectFakeAssertions, buildEntry, compareEntries2, meaningfulTestAuditHandler;
|
|
20483
20882
|
var init_meaningful_test_audit = __esm({
|
|
20484
20883
|
"../mcp/dist/src/tools/meaningful-test-audit.js"() {
|
|
@@ -20488,8 +20887,8 @@ var init_meaningful_test_audit = __esm({
|
|
|
20488
20887
|
LIST_PAGE_SIZE8 = 500;
|
|
20489
20888
|
APEX_CLASS_PREFIX5 = "ApexClass:";
|
|
20490
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.";
|
|
20491
|
-
meaningfulTestAuditInputSchema =
|
|
20492
|
-
classFilter:
|
|
20890
|
+
meaningfulTestAuditInputSchema = z78.object({
|
|
20891
|
+
classFilter: z78.array(z78.string().min(1)).max(LIST_PAGE_SIZE8).optional()
|
|
20493
20892
|
});
|
|
20494
20893
|
isTestClass3 = (node) => node.properties["isTest"] === true;
|
|
20495
20894
|
readNonNegativeInt = (node, key) => {
|
|
@@ -20594,7 +20993,7 @@ var init_meaningful_test_audit = __esm({
|
|
|
20594
20993
|
});
|
|
20595
20994
|
|
|
20596
20995
|
// ../mcp/dist/src/tools/method-reachability.js
|
|
20597
|
-
import { z as
|
|
20996
|
+
import { z as z79 } from "zod";
|
|
20598
20997
|
var REACHABILITY_BFS_DEPTH, APEX_CLASS_PREFIX6, APEX_TRIGGER_PREFIX4, REACHABILITY_DISCLOSURE, methodReachabilityInputSchema, isApexCallable3, isTestClass4, entryKindsFor, upstreamWalk, compareEntryHits, compareReachingTests, methodReachabilityHandler;
|
|
20599
20998
|
var init_method_reachability = __esm({
|
|
20600
20999
|
"../mcp/dist/src/tools/method-reachability.js"() {
|
|
@@ -20606,8 +21005,8 @@ var init_method_reachability = __esm({
|
|
|
20606
21005
|
APEX_CLASS_PREFIX6 = "ApexClass:";
|
|
20607
21006
|
APEX_TRIGGER_PREFIX4 = "ApexTrigger:";
|
|
20608
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.";
|
|
20609
|
-
methodReachabilityInputSchema =
|
|
20610
|
-
classApiName:
|
|
21008
|
+
methodReachabilityInputSchema = z79.object({
|
|
21009
|
+
classApiName: z79.string().min(1)
|
|
20611
21010
|
});
|
|
20612
21011
|
isApexCallable3 = (id) => id.startsWith(APEX_CLASS_PREFIX6) || id.startsWith(APEX_TRIGGER_PREFIX4);
|
|
20613
21012
|
isTestClass4 = (node) => node.properties["isTest"] === true;
|
|
@@ -20747,15 +21146,15 @@ var init_method_reachability = __esm({
|
|
|
20747
21146
|
});
|
|
20748
21147
|
|
|
20749
21148
|
// ../mcp/dist/src/tools/naming-convention-report.js
|
|
20750
|
-
import { z as
|
|
21149
|
+
import { z as z80 } from "zod";
|
|
20751
21150
|
var namingConventionReportInputSchema, namingConventionReportHandler;
|
|
20752
21151
|
var init_naming_convention_report = __esm({
|
|
20753
21152
|
"../mcp/dist/src/tools/naming-convention-report.js"() {
|
|
20754
21153
|
"use strict";
|
|
20755
21154
|
init_dist();
|
|
20756
21155
|
init_src4();
|
|
20757
|
-
namingConventionReportInputSchema =
|
|
20758
|
-
scope:
|
|
21156
|
+
namingConventionReportInputSchema = z80.object({
|
|
21157
|
+
scope: z80.string().min(1).optional()
|
|
20759
21158
|
});
|
|
20760
21159
|
namingConventionReportHandler = async (ctx, input2) => {
|
|
20761
21160
|
const result = await recognizeNamingConventions(ctx.graph, input2.scope !== void 0 ? { scope: input2.scope } : {});
|
|
@@ -20785,7 +21184,7 @@ var init_naming_convention_report = __esm({
|
|
|
20785
21184
|
// ../mcp/dist/src/tools/omniscript-flow.js
|
|
20786
21185
|
import { readFile as readFile12 } from "node:fs/promises";
|
|
20787
21186
|
import { XMLParser as XMLParser4 } from "fast-xml-parser";
|
|
20788
|
-
import { z as
|
|
21187
|
+
import { z as z81 } from "zod";
|
|
20789
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;
|
|
20790
21189
|
var init_omniscript_flow = __esm({
|
|
20791
21190
|
"../mcp/dist/src/tools/omniscript-flow.js"() {
|
|
@@ -20798,9 +21197,9 @@ var init_omniscript_flow = __esm({
|
|
|
20798
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.";
|
|
20799
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.";
|
|
20800
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.";
|
|
20801
|
-
omniscriptFlowInputSchema =
|
|
20802
|
-
omniScriptId:
|
|
20803
|
-
includeChildPropertySetConfig:
|
|
21200
|
+
omniscriptFlowInputSchema = z81.object({
|
|
21201
|
+
omniScriptId: z81.string().min(1),
|
|
21202
|
+
includeChildPropertySetConfig: z81.boolean().optional()
|
|
20804
21203
|
});
|
|
20805
21204
|
isOmniScriptId = (id) => id.startsWith(OMNISCRIPT_PREFIX);
|
|
20806
21205
|
unwrapSingle4 = (value) => Array.isArray(value) ? value[0] : value;
|
|
@@ -21047,7 +21446,7 @@ var init_omniscript_flow = __esm({
|
|
|
21047
21446
|
// ../mcp/dist/src/tools/omniuicard-widget-breakdown.js
|
|
21048
21447
|
import { readFile as readFile13 } from "node:fs/promises";
|
|
21049
21448
|
import { XMLParser as XMLParser5, XMLValidator as XMLValidator4 } from "fast-xml-parser";
|
|
21050
|
-
import { z as
|
|
21449
|
+
import { z as z82 } from "zod";
|
|
21051
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;
|
|
21052
21451
|
var init_omniuicard_widget_breakdown = __esm({
|
|
21053
21452
|
"../mcp/dist/src/tools/omniuicard-widget-breakdown.js"() {
|
|
@@ -21059,8 +21458,8 @@ var init_omniuicard_widget_breakdown = __esm({
|
|
|
21059
21458
|
ROOT_ELEMENT4 = "OmniUiCard";
|
|
21060
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.";
|
|
21061
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.";
|
|
21062
|
-
omniuicardWidgetBreakdownInputSchema =
|
|
21063
|
-
omniUiCardId:
|
|
21461
|
+
omniuicardWidgetBreakdownInputSchema = z82.object({
|
|
21462
|
+
omniUiCardId: z82.string().min(1)
|
|
21064
21463
|
});
|
|
21065
21464
|
unwrapSingle5 = (value) => Array.isArray(value) ? value[0] : value;
|
|
21066
21465
|
parseJsonBlob = (raw) => {
|
|
@@ -21353,10 +21752,11 @@ var init_soe_payload_bounds = __esm({
|
|
|
21353
21752
|
SOE_MAX_PAYLOAD_BYTES = 4e4;
|
|
21354
21753
|
KEEP_ALL_AT_OR_BELOW = 4;
|
|
21355
21754
|
sizeOf = (payload) => Buffer.byteLength(JSON.stringify(payload), "utf8");
|
|
21356
|
-
enforceSoeByteBudget = (payload,
|
|
21755
|
+
enforceSoeByteBudget = (payload, containers) => {
|
|
21357
21756
|
if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES) {
|
|
21358
|
-
return { truncated: false, actionsOmitted: 0 };
|
|
21757
|
+
return { truncated: false, actionsOmitted: 0, conditionalsTrimmed: 0, stepsOmitted: 0 };
|
|
21359
21758
|
}
|
|
21759
|
+
const steps = containers.flat();
|
|
21360
21760
|
let totalOmitted = 0;
|
|
21361
21761
|
const trimTo = (step3, keep) => {
|
|
21362
21762
|
if (keep >= step3.actions.length)
|
|
@@ -21395,15 +21795,82 @@ var init_soe_payload_bounds = __esm({
|
|
|
21395
21795
|
break;
|
|
21396
21796
|
trimTo(target, Math.floor(target.actions.length / 2));
|
|
21397
21797
|
}
|
|
21398
|
-
|
|
21798
|
+
let conditionalsTrimmed = 0;
|
|
21799
|
+
for (let guard = 0; guard < 1e5; guard += 1) {
|
|
21800
|
+
if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES)
|
|
21801
|
+
break;
|
|
21802
|
+
let target;
|
|
21803
|
+
let targetBytes = 0;
|
|
21804
|
+
for (const s of steps) {
|
|
21805
|
+
const cond = s.conditional;
|
|
21806
|
+
if (cond === void 0 || s.conditionalTruncated)
|
|
21807
|
+
continue;
|
|
21808
|
+
if (cond.expression === "" && cond.fieldRefs.length === 0)
|
|
21809
|
+
continue;
|
|
21810
|
+
const b = Buffer.byteLength(JSON.stringify(cond), "utf8");
|
|
21811
|
+
if (target === void 0 || b > targetBytes) {
|
|
21812
|
+
target = s;
|
|
21813
|
+
targetBytes = b;
|
|
21814
|
+
}
|
|
21815
|
+
}
|
|
21816
|
+
if (target === void 0)
|
|
21817
|
+
break;
|
|
21818
|
+
target.conditional = {
|
|
21819
|
+
conditionContextId: target.conditional.conditionContextId,
|
|
21820
|
+
expression: "",
|
|
21821
|
+
fieldRefs: []
|
|
21822
|
+
};
|
|
21823
|
+
target.conditionalTruncated = true;
|
|
21824
|
+
conditionalsTrimmed += 1;
|
|
21825
|
+
}
|
|
21826
|
+
let stepsOmitted = 0;
|
|
21827
|
+
for (let guard = 0; guard < 1e6; guard += 1) {
|
|
21828
|
+
if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES)
|
|
21829
|
+
break;
|
|
21830
|
+
let target;
|
|
21831
|
+
let targetBytes = 0;
|
|
21832
|
+
for (const c of containers) {
|
|
21833
|
+
if (c.length <= 1)
|
|
21834
|
+
continue;
|
|
21835
|
+
const b = Buffer.byteLength(JSON.stringify(c), "utf8");
|
|
21836
|
+
if (target === void 0 || b > targetBytes) {
|
|
21837
|
+
target = c;
|
|
21838
|
+
targetBytes = b;
|
|
21839
|
+
}
|
|
21840
|
+
}
|
|
21841
|
+
if (target === void 0)
|
|
21842
|
+
break;
|
|
21843
|
+
target.pop();
|
|
21844
|
+
stepsOmitted += 1;
|
|
21845
|
+
}
|
|
21846
|
+
return {
|
|
21847
|
+
truncated: totalOmitted > 0 || conditionalsTrimmed > 0 || stepsOmitted > 0,
|
|
21848
|
+
actionsOmitted: totalOmitted,
|
|
21849
|
+
conditionalsTrimmed,
|
|
21850
|
+
stepsOmitted
|
|
21851
|
+
};
|
|
21852
|
+
};
|
|
21853
|
+
soeTruncationNote = (result) => {
|
|
21854
|
+
const budgetKb = Math.round(SOE_MAX_PAYLOAD_BYTES / 1e3);
|
|
21855
|
+
const parts = [];
|
|
21856
|
+
if (result.actionsOmitted > 0) {
|
|
21857
|
+
parts.push(`${result.actionsOmitted} per-step action edge(s) across the heaviest steps were omitted (see each step's \`actionsOmitted\`)`);
|
|
21858
|
+
}
|
|
21859
|
+
if (result.conditionalsTrimmed > 0) {
|
|
21860
|
+
parts.push(`${result.conditionalsTrimmed} step condition(s) had their expression/fieldRefs dropped \u2014 the \`conditionContextId\` remains, fetch it with \`get_component\` for the full condition (see each step's \`conditionalTruncated\`)`);
|
|
21861
|
+
}
|
|
21862
|
+
if (result.stepsOmitted > 0) {
|
|
21863
|
+
parts.push(`${result.stepsOmitted} trailing step(s) were dropped to fit (the tail-most async/post-save steps; \`summary.totalSteps\` still reports the true total) \u2014 query a single event with \`what_happens_on_save\` to see them all`);
|
|
21864
|
+
}
|
|
21865
|
+
const lead = result.stepsOmitted > 0 ? `Response trimmed to fit the ~${budgetKb} KB MCP response budget` : `Response trimmed to fit the ~${budgetKb} KB MCP response budget: every save-order STEP is present and in order, but`;
|
|
21866
|
+
return `${lead} ${parts.join("; ")}. Query a single object/event for full detail.`;
|
|
21399
21867
|
};
|
|
21400
|
-
soeTruncationNote = (n) => `Response trimmed to fit the ~${Math.round(SOE_MAX_PAYLOAD_BYTES / 1e3)} KB MCP response budget: every save-order STEP is present and in order, but ${n} per-step action edge(s) across the heaviest steps were omitted (see each step's \`actionsOmitted\`). Query a single object/event or use \`get_edges\` on a specific step's component for its full action list.`;
|
|
21401
21868
|
}
|
|
21402
21869
|
});
|
|
21403
21870
|
|
|
21404
21871
|
// ../mcp/dist/src/tools/order-of-execution.js
|
|
21405
|
-
import { z as
|
|
21406
|
-
var
|
|
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;
|
|
21407
21874
|
var init_order_of_execution = __esm({
|
|
21408
21875
|
"../mcp/dist/src/tools/order-of-execution.js"() {
|
|
21409
21876
|
"use strict";
|
|
@@ -21411,10 +21878,10 @@ var init_order_of_execution = __esm({
|
|
|
21411
21878
|
init_src();
|
|
21412
21879
|
init_soe_admission();
|
|
21413
21880
|
init_soe_payload_bounds();
|
|
21414
|
-
|
|
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.";
|
|
21415
21882
|
SOE_EVENTS = ["insert", "update", "delete", "undelete"];
|
|
21416
|
-
orderOfExecutionInputSchema =
|
|
21417
|
-
objectApiName:
|
|
21883
|
+
orderOfExecutionInputSchema = z83.object({
|
|
21884
|
+
objectApiName: z83.string().min(1)
|
|
21418
21885
|
});
|
|
21419
21886
|
workflowMatchesEvent = (triggerType, event) => {
|
|
21420
21887
|
if (typeof triggerType !== "string")
|
|
@@ -21762,13 +22229,13 @@ var init_order_of_execution = __esm({
|
|
|
21762
22229
|
objectApiName: input2.objectApiName,
|
|
21763
22230
|
objectModeled,
|
|
21764
22231
|
byEvent,
|
|
21765
|
-
disclosure: composeSoeDisclosure(
|
|
22232
|
+
disclosure: composeSoeDisclosure(DISCLOSURE7, objectModeled)
|
|
21766
22233
|
};
|
|
21767
|
-
const
|
|
21768
|
-
const budget = enforceSoeByteBudget(data,
|
|
22234
|
+
const containers = SOE_EVENTS.map((event) => byEvent[event].soe);
|
|
22235
|
+
const budget = enforceSoeByteBudget(data, containers);
|
|
21769
22236
|
if (budget.truncated) {
|
|
21770
22237
|
data.truncated = true;
|
|
21771
|
-
data.disclosure = `${data.disclosure} ${soeTruncationNote(budget
|
|
22238
|
+
data.disclosure = `${data.disclosure} ${soeTruncationNote(budget)}`;
|
|
21772
22239
|
}
|
|
21773
22240
|
return ok({
|
|
21774
22241
|
data,
|
|
@@ -21782,7 +22249,7 @@ var init_order_of_execution = __esm({
|
|
|
21782
22249
|
});
|
|
21783
22250
|
|
|
21784
22251
|
// ../mcp/dist/src/tools/org-history.js
|
|
21785
|
-
import { z as
|
|
22252
|
+
import { z as z84 } from "zod";
|
|
21786
22253
|
var DEFAULT_LIMIT8, MAX_LIMIT9, orgHistoryInputSchema, BOUNDARIES9, orgHistoryHandler;
|
|
21787
22254
|
var init_org_history = __esm({
|
|
21788
22255
|
"../mcp/dist/src/tools/org-history.js"() {
|
|
@@ -21791,8 +22258,8 @@ var init_org_history = __esm({
|
|
|
21791
22258
|
init_history_store();
|
|
21792
22259
|
DEFAULT_LIMIT8 = 50;
|
|
21793
22260
|
MAX_LIMIT9 = 500;
|
|
21794
|
-
orgHistoryInputSchema =
|
|
21795
|
-
limit:
|
|
22261
|
+
orgHistoryInputSchema = z84.object({
|
|
22262
|
+
limit: z84.number().int().min(1).max(MAX_LIMIT9).optional()
|
|
21796
22263
|
});
|
|
21797
22264
|
BOUNDARIES9 = Object.freeze([
|
|
21798
22265
|
"History only covers refreshes performed since the continuous-learning store shipped; older or single-refresh vaults yield a short/empty timeline.",
|
|
@@ -21823,7 +22290,7 @@ var init_org_history = __esm({
|
|
|
21823
22290
|
});
|
|
21824
22291
|
|
|
21825
22292
|
// ../mcp/dist/src/tools/org-pulse.js
|
|
21826
|
-
import { z as
|
|
22293
|
+
import { z as z85 } from "zod";
|
|
21827
22294
|
var DEFAULT_LIMIT9, MAX_LIMIT10, orgPulseInputSchema, ORG_PULSE_DISCLOSURE, orgPulseHandler;
|
|
21828
22295
|
var init_org_pulse = __esm({
|
|
21829
22296
|
"../mcp/dist/src/tools/org-pulse.js"() {
|
|
@@ -21832,8 +22299,8 @@ var init_org_pulse = __esm({
|
|
|
21832
22299
|
init_src();
|
|
21833
22300
|
DEFAULT_LIMIT9 = 10;
|
|
21834
22301
|
MAX_LIMIT10 = 50;
|
|
21835
|
-
orgPulseInputSchema =
|
|
21836
|
-
limit:
|
|
22302
|
+
orgPulseInputSchema = z85.object({
|
|
22303
|
+
limit: z85.number().int().min(1).max(MAX_LIMIT10).optional()
|
|
21837
22304
|
});
|
|
21838
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.";
|
|
21839
22306
|
orgPulseHandler = async (ctx, input2) => {
|
|
@@ -21868,7 +22335,7 @@ var init_org_pulse = __esm({
|
|
|
21868
22335
|
});
|
|
21869
22336
|
|
|
21870
22337
|
// ../mcp/dist/src/tools/outbound-message-catalog.js
|
|
21871
|
-
import { z as
|
|
22338
|
+
import { z as z86 } from "zod";
|
|
21872
22339
|
var OUTBOUND_MESSAGE_CATALOG_MAX_ENTRIES, outboundMessageCatalogInputSchema, OUTBOUND_MESSAGE_DISCLOSURE, readOptionalString2, readBoolean, readFields, readName, apiNameToObjectKey, collectInvokers, compareEntries3, compareInvokers, outboundMessageCatalogHandler;
|
|
21873
22340
|
var init_outbound_message_catalog = __esm({
|
|
21874
22341
|
"../mcp/dist/src/tools/outbound-message-catalog.js"() {
|
|
@@ -21876,8 +22343,8 @@ var init_outbound_message_catalog = __esm({
|
|
|
21876
22343
|
init_dist();
|
|
21877
22344
|
init_src();
|
|
21878
22345
|
OUTBOUND_MESSAGE_CATALOG_MAX_ENTRIES = 500;
|
|
21879
|
-
outboundMessageCatalogInputSchema =
|
|
21880
|
-
objectFilter:
|
|
22346
|
+
outboundMessageCatalogInputSchema = z86.object({
|
|
22347
|
+
objectFilter: z86.string().min(1).optional()
|
|
21881
22348
|
});
|
|
21882
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.";
|
|
21883
22350
|
readOptionalString2 = (properties, key) => {
|
|
@@ -21994,7 +22461,7 @@ var init_outbound_message_catalog = __esm({
|
|
|
21994
22461
|
});
|
|
21995
22462
|
|
|
21996
22463
|
// ../mcp/dist/src/tools/package-impact.js
|
|
21997
|
-
import { z as
|
|
22464
|
+
import { z as z87 } from "zod";
|
|
21998
22465
|
var DEFAULT_LIMIT10, MAX_LIMIT11, INVENTORY_SAMPLE, EDGE_SCAN_CAP, PACKAGE_IMPACT_DISCLOSURE, packageImpactInputSchema, namespaceOf, buildInventory, buildImpact, packageImpactHandler;
|
|
21999
22466
|
var init_package_impact = __esm({
|
|
22000
22467
|
"../mcp/dist/src/tools/package-impact.js"() {
|
|
@@ -22006,9 +22473,9 @@ var init_package_impact = __esm({
|
|
|
22006
22473
|
INVENTORY_SAMPLE = 5;
|
|
22007
22474
|
EDGE_SCAN_CAP = 2e3;
|
|
22008
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.';
|
|
22009
|
-
packageImpactInputSchema =
|
|
22010
|
-
namespace:
|
|
22011
|
-
limit:
|
|
22476
|
+
packageImpactInputSchema = z87.object({
|
|
22477
|
+
namespace: z87.string().min(1).optional(),
|
|
22478
|
+
limit: z87.number().int().min(1).max(MAX_LIMIT11).optional()
|
|
22012
22479
|
});
|
|
22013
22480
|
namespaceOf = (idOrName) => {
|
|
22014
22481
|
const colon = idOrName.indexOf(":");
|
|
@@ -22147,7 +22614,7 @@ var init_package_impact = __esm({
|
|
|
22147
22614
|
});
|
|
22148
22615
|
|
|
22149
22616
|
// ../mcp/dist/src/tools/process-builder-migration-candidates.js
|
|
22150
|
-
import { z as
|
|
22617
|
+
import { z as z88 } from "zod";
|
|
22151
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;
|
|
22152
22619
|
var init_process_builder_migration_candidates = __esm({
|
|
22153
22620
|
"../mcp/dist/src/tools/process-builder-migration-candidates.js"() {
|
|
@@ -22163,12 +22630,12 @@ var init_process_builder_migration_candidates = __esm({
|
|
|
22163
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.",
|
|
22164
22631
|
"the migration tool itself (Setup \u2192 Migrate to Flow) does not run here \u2014 this tool produces the inventory and per-rule guidance."
|
|
22165
22632
|
]);
|
|
22166
|
-
processBuilderMigrationCandidatesInputSchema =
|
|
22167
|
-
includeWorkflowRules:
|
|
22168
|
-
includeApprovalProcesses:
|
|
22169
|
-
activeOnly:
|
|
22170
|
-
sortBy:
|
|
22171
|
-
limit:
|
|
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()
|
|
22172
22639
|
});
|
|
22173
22640
|
propertyNumber = (node, key) => {
|
|
22174
22641
|
const v = node.properties[key];
|
|
@@ -22525,7 +22992,7 @@ var init_clarify = __esm({
|
|
|
22525
22992
|
});
|
|
22526
22993
|
|
|
22527
22994
|
// ../mcp/dist/src/tools/resolve.js
|
|
22528
|
-
import { z as
|
|
22995
|
+
import { z as z89 } from "zod";
|
|
22529
22996
|
var RESOLVE_MAX_LIMIT, RESOLVE_DISCLOSURE, resolveInputSchema, resolveHandler;
|
|
22530
22997
|
var init_resolve2 = __esm({
|
|
22531
22998
|
"../mcp/dist/src/tools/resolve.js"() {
|
|
@@ -22536,11 +23003,11 @@ var init_resolve2 = __esm({
|
|
|
22536
23003
|
init_clarify();
|
|
22537
23004
|
RESOLVE_MAX_LIMIT = 50;
|
|
22538
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.";
|
|
22539
|
-
resolveInputSchema =
|
|
22540
|
-
query:
|
|
22541
|
-
types:
|
|
22542
|
-
parentId:
|
|
22543
|
-
limit:
|
|
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()
|
|
22544
23011
|
});
|
|
22545
23012
|
resolveHandler = async (ctx, input2) => {
|
|
22546
23013
|
const result = await resolveComponents(ctx.graph, input2.query, {
|
|
@@ -22597,11 +23064,41 @@ var init_resolve2 = __esm({
|
|
|
22597
23064
|
import { appendFile, mkdir as mkdir6 } from "node:fs/promises";
|
|
22598
23065
|
import { homedir as homedir2 } from "node:os";
|
|
22599
23066
|
import { dirname as dirname5, join as join9 } from "node:path";
|
|
22600
|
-
var normalize, routeText, RULES, classifyQuestion, gapLogPath, logGapIfAny;
|
|
23067
|
+
var normalize, deriveSaveEvent, deriveKnowledgeTopic, routeText, RULES, classifyQuestion, gapLogPath, logGapIfAny;
|
|
22601
23068
|
var init_intent_router = __esm({
|
|
22602
23069
|
"../mcp/dist/src/intent-router.js"() {
|
|
22603
23070
|
"use strict";
|
|
22604
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
|
+
};
|
|
22605
23102
|
routeText = (question) => {
|
|
22606
23103
|
const normalized = normalize(question).replace(/\s*\[[^\]]+\]\s*$/, "");
|
|
22607
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*/, "");
|
|
@@ -22663,7 +23160,10 @@ var init_intent_router = __esm({
|
|
|
22663
23160
|
needsResolve: false,
|
|
22664
23161
|
reason: "Storage, API usage, and governor headroom are live org telemetry.",
|
|
22665
23162
|
patterns: [
|
|
22666
|
-
|
|
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)/,
|
|
22667
23167
|
/\b(api|daily)\s+(usage|calls?|limit)\b/,
|
|
22668
23168
|
/\b(data|file)\s+storage\b/,
|
|
22669
23169
|
/\bhow\s+much\s+(storage|api|data)\b/,
|
|
@@ -22697,6 +23197,25 @@ var init_intent_router = __esm({
|
|
|
22697
23197
|
/\brecords?\s+by\s+owner\b/
|
|
22698
23198
|
]
|
|
22699
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
|
+
},
|
|
22700
23219
|
{
|
|
22701
23220
|
intent: "group-count",
|
|
22702
23221
|
plane: "live",
|
|
@@ -22884,7 +23403,27 @@ var init_intent_router = __esm({
|
|
|
22884
23403
|
patterns: [
|
|
22885
23404
|
/\b(page\s+)?layouts?\b.*\b(who|access|assigned|profile|sees?|user)\b/,
|
|
22886
23405
|
/\bwho\s+(sees|has|can)\b.*\blayouts?\b/,
|
|
22887
|
-
/\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/
|
|
22888
23427
|
]
|
|
22889
23428
|
},
|
|
22890
23429
|
{
|
|
@@ -22925,9 +23464,13 @@ var init_intent_router = __esm({
|
|
|
22925
23464
|
needsResolve: false,
|
|
22926
23465
|
reason: "God-mode / over-permission detection is a vault permission synthesis.",
|
|
22927
23466
|
patterns: [
|
|
22928
|
-
/\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/,
|
|
22929
23468
|
/\bwho\s+is\s+(an?\s+)?admin\b/,
|
|
22930
|
-
/\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/
|
|
22931
23474
|
]
|
|
22932
23475
|
},
|
|
22933
23476
|
{
|
|
@@ -23027,14 +23570,21 @@ var init_intent_router = __esm({
|
|
|
23027
23570
|
liveRequired: false,
|
|
23028
23571
|
needsResolve: true,
|
|
23029
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) }),
|
|
23030
23574
|
patterns: [
|
|
23031
23575
|
/\b(trigger\s+order|order\s+of\s+execution)\b/,
|
|
23032
|
-
/\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/,
|
|
23033
23577
|
// "which/what flows|triggers|VRs|workflows run|fire when ..." — the
|
|
23034
23578
|
// "which" phrasing was a router gap (e.g. "which flows run when a Case is
|
|
23035
23579
|
// created"), so the question fell through to unrouted.
|
|
23036
23580
|
/\b(what|which)\s+(triggers?|automation|flows?|validation\s+rules?|workflows?)\b.*\b(fire|run|execute|happen)\b/,
|
|
23037
|
-
/\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/
|
|
23038
23588
|
]
|
|
23039
23589
|
},
|
|
23040
23590
|
{
|
|
@@ -23050,6 +23600,25 @@ var init_intent_router = __esm({
|
|
|
23050
23600
|
/\bwhat\s+changed\b.*\bfield\b.*\bon\s+save\b/
|
|
23051
23601
|
]
|
|
23052
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
|
+
},
|
|
23053
23622
|
{
|
|
23054
23623
|
intent: "automation-on-object",
|
|
23055
23624
|
plane: "vault",
|
|
@@ -23256,6 +23825,23 @@ var init_intent_router = __esm({
|
|
|
23256
23825
|
/\borg\s+(risk|health)\b/
|
|
23257
23826
|
]
|
|
23258
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
|
+
},
|
|
23259
23845
|
{
|
|
23260
23846
|
intent: "apex-search",
|
|
23261
23847
|
plane: "vault",
|
|
@@ -23283,19 +23869,53 @@ var init_intent_router = __esm({
|
|
|
23283
23869
|
/\bapi\b.*\b(connections?|surfaces?)\b/
|
|
23284
23870
|
]
|
|
23285
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
|
+
},
|
|
23286
23889
|
{
|
|
23287
23890
|
intent: "event-subscribers",
|
|
23288
23891
|
plane: "vault",
|
|
23289
23892
|
tools: ["sfi.resolve", "sfi.event_subscribers", "sfi.cdc_subscribers"],
|
|
23290
23893
|
liveRequired: false,
|
|
23291
23894
|
needsResolve: true,
|
|
23292
|
-
reason: "Who subscribes to a platform event / change data capture channel.",
|
|
23895
|
+
reason: "Who subscribes to a specific platform event / change data capture channel.",
|
|
23293
23896
|
patterns: [
|
|
23294
23897
|
/\b(platform\s+events?|change\s+data\s+capture|cdc)\b.*\b(subscrib|listen|consum)\b/,
|
|
23295
23898
|
/\bwho\s+(subscribes?|listens?)\b/,
|
|
23296
23899
|
/\b(subscribers?|subscriptions?)\b.*\bevents?\b/
|
|
23297
23900
|
]
|
|
23298
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
|
+
},
|
|
23299
23919
|
{
|
|
23300
23920
|
intent: "endpoints",
|
|
23301
23921
|
plane: "vault",
|
|
@@ -23360,6 +23980,24 @@ var init_intent_router = __esm({
|
|
|
23360
23980
|
/\b(safe\s+to\s+delete|can\s+i\s+delete|ok\s+to\s+(delete|remove))\b/
|
|
23361
23981
|
]
|
|
23362
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
|
+
},
|
|
23363
24001
|
{
|
|
23364
24002
|
intent: "impact-analysis",
|
|
23365
24003
|
plane: "vault",
|
|
@@ -23572,6 +24210,37 @@ var init_intent_router = __esm({
|
|
|
23572
24210
|
]
|
|
23573
24211
|
},
|
|
23574
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
|
+
},
|
|
23575
24244
|
{
|
|
23576
24245
|
intent: "resolve-lookup",
|
|
23577
24246
|
plane: "vault",
|
|
@@ -23599,6 +24268,92 @@ var init_intent_router = __esm({
|
|
|
23599
24268
|
/\b(suffix|prefix)\b.*\b(convention|standard)\b/
|
|
23600
24269
|
]
|
|
23601
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
|
+
},
|
|
23602
24357
|
{
|
|
23603
24358
|
intent: "schema",
|
|
23604
24359
|
plane: "vault",
|
|
@@ -23633,6 +24388,7 @@ var init_intent_router = __esm({
|
|
|
23633
24388
|
}
|
|
23634
24389
|
for (const rule of RULES) {
|
|
23635
24390
|
if (rule.patterns.some((p) => p.test(q))) {
|
|
24391
|
+
const suggestedArgs = rule.suggestArgs?.(q);
|
|
23636
24392
|
return {
|
|
23637
24393
|
question,
|
|
23638
24394
|
plane: rule.plane,
|
|
@@ -23641,7 +24397,8 @@ var init_intent_router = __esm({
|
|
|
23641
24397
|
liveRequired: rule.liveRequired,
|
|
23642
24398
|
needsResolve: rule.needsResolve,
|
|
23643
24399
|
reason: rule.reason,
|
|
23644
|
-
gap: rule.gap ?? null
|
|
24400
|
+
gap: rule.gap ?? null,
|
|
24401
|
+
...suggestedArgs !== void 0 ? { suggestedArgs } : {}
|
|
23645
24402
|
};
|
|
23646
24403
|
}
|
|
23647
24404
|
}
|
|
@@ -23689,7 +24446,7 @@ var init_intent_router = __esm({
|
|
|
23689
24446
|
});
|
|
23690
24447
|
|
|
23691
24448
|
// ../mcp/dist/src/tools/route-question.js
|
|
23692
|
-
import { z as
|
|
24449
|
+
import { z as z90 } from "zod";
|
|
23693
24450
|
var routeQuestionInputSchema, routeTrust, routeQuestionHandler;
|
|
23694
24451
|
var init_route_question = __esm({
|
|
23695
24452
|
"../mcp/dist/src/tools/route-question.js"() {
|
|
@@ -23697,11 +24454,11 @@ var init_route_question = __esm({
|
|
|
23697
24454
|
init_dist();
|
|
23698
24455
|
init_answer_render();
|
|
23699
24456
|
init_intent_router();
|
|
23700
|
-
routeQuestionInputSchema =
|
|
24457
|
+
routeQuestionInputSchema = z90.object({
|
|
23701
24458
|
/** The user's plain-language question. */
|
|
23702
|
-
question:
|
|
24459
|
+
question: z90.string().min(1),
|
|
23703
24460
|
/** Append a gap entry to the local backlog when the route has one (default true). */
|
|
23704
|
-
logGap:
|
|
24461
|
+
logGap: z90.boolean().optional()
|
|
23705
24462
|
});
|
|
23706
24463
|
routeTrust = () => ({
|
|
23707
24464
|
provenance: "offline_snapshot",
|
|
@@ -23732,7 +24489,7 @@ var init_route_question = __esm({
|
|
|
23732
24489
|
});
|
|
23733
24490
|
|
|
23734
24491
|
// ../mcp/dist/src/tools/scheduled-job-catalog.js
|
|
23735
|
-
import { z as
|
|
24492
|
+
import { z as z91 } from "zod";
|
|
23736
24493
|
var SCHEDULED_JOB_CATALOG_MAX_CLASSES, scheduledJobCatalogInputSchema, SCHEDULED_JOB_CATALOG_DISCLOSURE, readCronExpressions, readEdgeCronExpression, readIsSchedulable, collectScheduledCalls, compareEntries4, compareCalls, scheduledJobCatalogHandler;
|
|
23737
24494
|
var init_scheduled_job_catalog = __esm({
|
|
23738
24495
|
"../mcp/dist/src/tools/scheduled-job-catalog.js"() {
|
|
@@ -23740,7 +24497,7 @@ var init_scheduled_job_catalog = __esm({
|
|
|
23740
24497
|
init_dist();
|
|
23741
24498
|
init_src();
|
|
23742
24499
|
SCHEDULED_JOB_CATALOG_MAX_CLASSES = 500;
|
|
23743
|
-
scheduledJobCatalogInputSchema =
|
|
24500
|
+
scheduledJobCatalogInputSchema = z91.object({});
|
|
23744
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.";
|
|
23745
24502
|
readCronExpressions = (properties) => {
|
|
23746
24503
|
const raw = properties["cronExpressions"];
|
|
@@ -23829,7 +24586,7 @@ var init_scheduled_job_catalog = __esm({
|
|
|
23829
24586
|
|
|
23830
24587
|
// ../mcp/dist/src/tools/search-apex-source.js
|
|
23831
24588
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
23832
|
-
import { z as
|
|
24589
|
+
import { z as z92 } from "zod";
|
|
23833
24590
|
var SEARCH_APEX_SOURCE_MAX_LIMIT, SEARCH_APEX_SOURCE_DEFAULT_LIMIT, APEX_SUFFIXES, searchApexSourceInputSchema, searchApexSourceHandler, buildMatcher, searchFile;
|
|
23834
24591
|
var init_search_apex_source = __esm({
|
|
23835
24592
|
"../mcp/dist/src/tools/search-apex-source.js"() {
|
|
@@ -23839,10 +24596,10 @@ var init_search_apex_source = __esm({
|
|
|
23839
24596
|
SEARCH_APEX_SOURCE_MAX_LIMIT = 200;
|
|
23840
24597
|
SEARCH_APEX_SOURCE_DEFAULT_LIMIT = 50;
|
|
23841
24598
|
APEX_SUFFIXES = [".cls", ".trigger"];
|
|
23842
|
-
searchApexSourceInputSchema =
|
|
23843
|
-
query:
|
|
23844
|
-
regex:
|
|
23845
|
-
limit:
|
|
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()
|
|
23846
24603
|
});
|
|
23847
24604
|
searchApexSourceHandler = async (ctx, input2) => {
|
|
23848
24605
|
const limit = input2.limit ?? SEARCH_APEX_SOURCE_DEFAULT_LIMIT;
|
|
@@ -23919,7 +24676,7 @@ var init_search_apex_source = __esm({
|
|
|
23919
24676
|
});
|
|
23920
24677
|
|
|
23921
24678
|
// ../mcp/dist/src/tools/search-components.js
|
|
23922
|
-
import { z as
|
|
24679
|
+
import { z as z93 } from "zod";
|
|
23923
24680
|
var SEARCH_MAX_LIMIT2, searchComponentsInputSchema, SUGGESTIONS_NOTE, searchComponentsHandler;
|
|
23924
24681
|
var init_search_components = __esm({
|
|
23925
24682
|
"../mcp/dist/src/tools/search-components.js"() {
|
|
@@ -23927,10 +24684,10 @@ var init_search_components = __esm({
|
|
|
23927
24684
|
init_dist();
|
|
23928
24685
|
init_src();
|
|
23929
24686
|
SEARCH_MAX_LIMIT2 = 100;
|
|
23930
|
-
searchComponentsInputSchema =
|
|
23931
|
-
query:
|
|
23932
|
-
limit:
|
|
23933
|
-
types:
|
|
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()
|
|
23934
24691
|
});
|
|
23935
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.";
|
|
23936
24693
|
searchComponentsHandler = async (ctx, input2) => {
|
|
@@ -23981,7 +24738,7 @@ var init_search_components = __esm({
|
|
|
23981
24738
|
|
|
23982
24739
|
// ../mcp/dist/src/tools/search-flow-metadata.js
|
|
23983
24740
|
import { readFile as readFile15 } from "node:fs/promises";
|
|
23984
|
-
import { z as
|
|
24741
|
+
import { z as z94 } from "zod";
|
|
23985
24742
|
var SEARCH_FLOW_METADATA_MAX_LIMIT, SEARCH_FLOW_METADATA_DEFAULT_LIMIT, FLOW_FILE_SUFFIX, searchFlowMetadataInputSchema, searchFlowMetadataHandler, buildMatcher2, searchFile2;
|
|
23986
24743
|
var init_search_flow_metadata = __esm({
|
|
23987
24744
|
"../mcp/dist/src/tools/search-flow-metadata.js"() {
|
|
@@ -23991,10 +24748,10 @@ var init_search_flow_metadata = __esm({
|
|
|
23991
24748
|
SEARCH_FLOW_METADATA_MAX_LIMIT = 200;
|
|
23992
24749
|
SEARCH_FLOW_METADATA_DEFAULT_LIMIT = 50;
|
|
23993
24750
|
FLOW_FILE_SUFFIX = ".flow-meta.xml";
|
|
23994
|
-
searchFlowMetadataInputSchema =
|
|
23995
|
-
query:
|
|
23996
|
-
regex:
|
|
23997
|
-
limit:
|
|
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()
|
|
23998
24755
|
});
|
|
23999
24756
|
searchFlowMetadataHandler = async (ctx, input2) => {
|
|
24000
24757
|
const limit = input2.limit ?? SEARCH_FLOW_METADATA_DEFAULT_LIMIT;
|
|
@@ -24071,14 +24828,14 @@ var init_search_flow_metadata = __esm({
|
|
|
24071
24828
|
});
|
|
24072
24829
|
|
|
24073
24830
|
// ../mcp/dist/src/tools/snapshot-trend.js
|
|
24074
|
-
import { z as
|
|
24831
|
+
import { z as z95 } from "zod";
|
|
24075
24832
|
var trendInputSchema, TREND_DISCLOSURE, trendHandler, churnInputSchema, CHURN_DISCLOSURE, diffNodeSets, churnHandler;
|
|
24076
24833
|
var init_snapshot_trend = __esm({
|
|
24077
24834
|
"../mcp/dist/src/tools/snapshot-trend.js"() {
|
|
24078
24835
|
"use strict";
|
|
24079
24836
|
init_dist();
|
|
24080
24837
|
init_src2();
|
|
24081
|
-
trendInputSchema =
|
|
24838
|
+
trendInputSchema = z95.object({});
|
|
24082
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`.";
|
|
24083
24840
|
trendHandler = async (ctx, _input) => {
|
|
24084
24841
|
const listed = await listSnapshots(ctx.vaultRoot);
|
|
@@ -24104,9 +24861,9 @@ var init_snapshot_trend = __esm({
|
|
|
24104
24861
|
}
|
|
24105
24862
|
});
|
|
24106
24863
|
};
|
|
24107
|
-
churnInputSchema =
|
|
24108
|
-
fromLabel:
|
|
24109
|
-
toLabel:
|
|
24864
|
+
churnInputSchema = z95.object({
|
|
24865
|
+
fromLabel: z95.string().min(1).optional(),
|
|
24866
|
+
toLabel: z95.string().min(1).optional()
|
|
24110
24867
|
});
|
|
24111
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.';
|
|
24112
24869
|
diffNodeSets = (fromNodes, toNodes) => {
|
|
@@ -24244,7 +25001,7 @@ var init_coverage_trust = __esm({
|
|
|
24244
25001
|
});
|
|
24245
25002
|
|
|
24246
25003
|
// ../mcp/dist/src/tools/unassigned-permission-sets.js
|
|
24247
|
-
import { z as
|
|
25004
|
+
import { z as z96 } from "zod";
|
|
24248
25005
|
var UNASSIGNED_MAX_LIMIT, UNASSIGNED_DEFAULT_LIMIT, LIST_PAGE_SIZE10, BOUNDARIES11, unassignedPermissionSetsInputSchema, namespacePrefixOf2, propertyString3, propertyBoolean2, propertyNumberOrNull, detectEnrichmentStatus, compareById2, unassignedPermissionSetsHandler;
|
|
24249
25006
|
var init_unassigned_permission_sets = __esm({
|
|
24250
25007
|
"../mcp/dist/src/tools/unassigned-permission-sets.js"() {
|
|
@@ -24258,10 +25015,10 @@ var init_unassigned_permission_sets = __esm({
|
|
|
24258
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`.",
|
|
24259
25016
|
"unknownAssignmentCount values require v1.7 Tooling API enrichment to resolve \u2014 they are NOT counted toward unassignedCount."
|
|
24260
25017
|
]);
|
|
24261
|
-
unassignedPermissionSetsInputSchema =
|
|
24262
|
-
includeManagedPackage:
|
|
24263
|
-
includeMutingPermissionSets:
|
|
24264
|
-
limit:
|
|
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()
|
|
24265
25022
|
});
|
|
24266
25023
|
namespacePrefixOf2 = (apiName) => {
|
|
24267
25024
|
const idx = apiName.indexOf("__");
|
|
@@ -24402,7 +25159,7 @@ var init_unassigned_permission_sets = __esm({
|
|
|
24402
25159
|
});
|
|
24403
25160
|
|
|
24404
25161
|
// ../mcp/dist/src/tools/unused-components.js
|
|
24405
|
-
import { z as
|
|
25162
|
+
import { z as z97 } from "zod";
|
|
24406
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;
|
|
24407
25164
|
var init_unused_components = __esm({
|
|
24408
25165
|
"../mcp/dist/src/tools/unused-components.js"() {
|
|
@@ -24477,9 +25234,9 @@ var init_unused_components = __esm({
|
|
|
24477
25234
|
"CustomMetadataRecord",
|
|
24478
25235
|
"CustomSettingRecord"
|
|
24479
25236
|
];
|
|
24480
|
-
unusedComponentsInputSchema =
|
|
24481
|
-
types:
|
|
24482
|
-
limit:
|
|
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()
|
|
24483
25240
|
});
|
|
24484
25241
|
INVISIBLE_REFERENCES_NOTES = Object.freeze({
|
|
24485
25242
|
ApexClass: "Dynamic Apex (Type.forName), reflective dispatch, and Tooling API references are invisible to the v1.x scanner; spot-check before deleting.",
|
|
@@ -24623,7 +25380,7 @@ var init_unused_components = __esm({
|
|
|
24623
25380
|
});
|
|
24624
25381
|
|
|
24625
25382
|
// ../mcp/dist/src/tools/unused-fields-deep.js
|
|
24626
|
-
import { z as
|
|
25383
|
+
import { z as z98 } from "zod";
|
|
24627
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;
|
|
24628
25385
|
var init_unused_fields_deep = __esm({
|
|
24629
25386
|
"../mcp/dist/src/tools/unused-fields-deep.js"() {
|
|
@@ -24674,11 +25431,11 @@ var init_unused_fields_deep = __esm({
|
|
|
24674
25431
|
BOUNDARIES12 = Object.freeze([
|
|
24675
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.'"
|
|
24676
25433
|
]);
|
|
24677
|
-
unusedFieldsDeepInputSchema =
|
|
24678
|
-
parentObjectFilter:
|
|
24679
|
-
excludeManagedPackage:
|
|
24680
|
-
excludeStandardFields:
|
|
24681
|
-
limit:
|
|
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()
|
|
24682
25439
|
});
|
|
24683
25440
|
containsApiName = (text, apiName) => text.toLowerCase().includes(apiName.toLowerCase());
|
|
24684
25441
|
propertyString4 = (node, key) => {
|
|
@@ -25056,7 +25813,7 @@ var init_unused_fields_deep = __esm({
|
|
|
25056
25813
|
});
|
|
25057
25814
|
|
|
25058
25815
|
// ../mcp/dist/src/tools/tech-debt-score.js
|
|
25059
|
-
import { z as
|
|
25816
|
+
import { z as z99 } from "zod";
|
|
25060
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;
|
|
25061
25818
|
var init_tech_debt_score = __esm({
|
|
25062
25819
|
"../mcp/dist/src/tools/tech-debt-score.js"() {
|
|
@@ -25102,8 +25859,8 @@ var init_tech_debt_score = __esm({
|
|
|
25102
25859
|
"apiVersions",
|
|
25103
25860
|
"unassignedGrants"
|
|
25104
25861
|
];
|
|
25105
|
-
techDebtScoreInputSchema =
|
|
25106
|
-
excludeCategories:
|
|
25862
|
+
techDebtScoreInputSchema = z99.object({
|
|
25863
|
+
excludeCategories: z99.array(z99.enum([
|
|
25107
25864
|
"deadWeight",
|
|
25108
25865
|
"legacyAutomation",
|
|
25109
25866
|
"codeQuality",
|
|
@@ -25114,13 +25871,13 @@ var init_tech_debt_score = __esm({
|
|
|
25114
25871
|
// `.passthrough()` keeps unknown weight keys in the parsed input so
|
|
25115
25872
|
// the handler can surface them in a structured `invalid-query`
|
|
25116
25873
|
// refusal rather than silently dropping them at the Zod boundary.
|
|
25117
|
-
weights:
|
|
25118
|
-
deadWeight:
|
|
25119
|
-
legacyAutomation:
|
|
25120
|
-
codeQuality:
|
|
25121
|
-
freshness:
|
|
25122
|
-
apiVersions:
|
|
25123
|
-
unassignedGrants:
|
|
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()
|
|
25124
25881
|
}).passthrough().optional()
|
|
25125
25882
|
});
|
|
25126
25883
|
bandFor = (score) => {
|
|
@@ -25480,12 +26237,13 @@ var init_tech_debt_score = __esm({
|
|
|
25480
26237
|
});
|
|
25481
26238
|
|
|
25482
26239
|
// ../mcp/dist/src/tools/synthesis-reports.js
|
|
25483
|
-
import { z as
|
|
25484
|
-
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;
|
|
25485
26242
|
var init_synthesis_reports = __esm({
|
|
25486
26243
|
"../mcp/dist/src/tools/synthesis-reports.js"() {
|
|
25487
26244
|
"use strict";
|
|
25488
26245
|
init_dist();
|
|
26246
|
+
init_src();
|
|
25489
26247
|
init_src2();
|
|
25490
26248
|
init_coverage_trust();
|
|
25491
26249
|
init_crud_fls_audit();
|
|
@@ -25495,8 +26253,8 @@ var init_synthesis_reports = __esm({
|
|
|
25495
26253
|
init_tech_debt_score();
|
|
25496
26254
|
init_unassigned_permission_sets();
|
|
25497
26255
|
init_unused_fields_deep();
|
|
25498
|
-
synthesisInputSchema =
|
|
25499
|
-
limit:
|
|
26256
|
+
synthesisInputSchema = z100.object({
|
|
26257
|
+
limit: z100.number().int().min(1).max(500).optional()
|
|
25500
26258
|
});
|
|
25501
26259
|
orgRiskReportInputSchema = synthesisInputSchema;
|
|
25502
26260
|
fieldCleanupCandidatesInputSchema = synthesisInputSchema;
|
|
@@ -25650,9 +26408,209 @@ var init_synthesis_reports = __esm({
|
|
|
25650
26408
|
}
|
|
25651
26409
|
});
|
|
25652
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
|
+
};
|
|
25653
26609
|
permissionRiskReportHandler = async (ctx, input2) => {
|
|
25654
26610
|
const limit = input2.limit ?? 50;
|
|
25655
26611
|
const findings = [];
|
|
26612
|
+
const overPriv = await analyzeOverPrivilege(ctx, limit);
|
|
26613
|
+
findings.push(...overPriv.findings);
|
|
25656
26614
|
const unassigned = await unassignedPermissionSetsHandler(ctx, { limit });
|
|
25657
26615
|
if (unassigned.ok) {
|
|
25658
26616
|
for (const row of unassigned.value.data.unassigned.slice(0, limit)) {
|
|
@@ -25688,6 +26646,7 @@ var init_synthesis_reports = __esm({
|
|
|
25688
26646
|
data: {
|
|
25689
26647
|
findings: sortFindings(findings),
|
|
25690
26648
|
auditTotals,
|
|
26649
|
+
privilege: overPriv.privilege,
|
|
25691
26650
|
trust: coverageTrust(ctx),
|
|
25692
26651
|
disclosure: SYNTHESIS_DISCLOSURE
|
|
25693
26652
|
},
|
|
@@ -25729,8 +26688,109 @@ var init_synthesis_reports = __esm({
|
|
|
25729
26688
|
}
|
|
25730
26689
|
});
|
|
25731
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
|
+
|
|
25732
26792
|
// ../mcp/dist/src/tools/test-coverage-for-method.js
|
|
25733
|
-
import { z as
|
|
26793
|
+
import { z as z102 } from "zod";
|
|
25734
26794
|
var COVERAGE_BFS_DEPTH, COVERAGE_EDGE_TYPES, APEX_CLASS_PREFIX7, APEX_TRIGGER_PREFIX5, COVERAGE_DISCLOSURE2, testCoverageForMethodInputSchema, isApexCallable4, isTestClass5, upstreamWalk2, testCoverageForMethodHandler;
|
|
25735
26795
|
var init_test_coverage_for_method = __esm({
|
|
25736
26796
|
"../mcp/dist/src/tools/test-coverage-for-method.js"() {
|
|
@@ -25746,9 +26806,9 @@ var init_test_coverage_for_method = __esm({
|
|
|
25746
26806
|
APEX_CLASS_PREFIX7 = "ApexClass:";
|
|
25747
26807
|
APEX_TRIGGER_PREFIX5 = "ApexTrigger:";
|
|
25748
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.";
|
|
25749
|
-
testCoverageForMethodInputSchema =
|
|
25750
|
-
classApiName:
|
|
25751
|
-
methodName:
|
|
26809
|
+
testCoverageForMethodInputSchema = z102.object({
|
|
26810
|
+
classApiName: z102.string().min(1),
|
|
26811
|
+
methodName: z102.string().min(1).optional()
|
|
25752
26812
|
});
|
|
25753
26813
|
isApexCallable4 = (id) => id.startsWith(APEX_CLASS_PREFIX7) || id.startsWith(APEX_TRIGGER_PREFIX5);
|
|
25754
26814
|
isTestClass5 = (node) => node.properties["isTest"] === true;
|
|
@@ -25848,8 +26908,8 @@ var init_test_coverage_for_method = __esm({
|
|
|
25848
26908
|
});
|
|
25849
26909
|
|
|
25850
26910
|
// ../mcp/dist/src/tools/test-coverage-gaps.js
|
|
25851
|
-
import { z as
|
|
25852
|
-
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;
|
|
25853
26913
|
var init_test_coverage_gaps = __esm({
|
|
25854
26914
|
"../mcp/dist/src/tools/test-coverage-gaps.js"() {
|
|
25855
26915
|
"use strict";
|
|
@@ -25857,13 +26917,31 @@ var init_test_coverage_gaps = __esm({
|
|
|
25857
26917
|
init_src();
|
|
25858
26918
|
LIST_PAGE_SIZE14 = 500;
|
|
25859
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;
|
|
25860
26923
|
MAX_COVERAGE_DEPTH = 3;
|
|
25861
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.";
|
|
25862
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.";
|
|
25863
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.`;
|
|
25864
|
-
testCoverageGapsInputSchema =
|
|
25865
|
-
classFilter:
|
|
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()
|
|
25866
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
|
+
};
|
|
25867
26945
|
collectFakeAssertions2 = (node) => {
|
|
25868
26946
|
const raw = node.properties["qualityIssues"];
|
|
25869
26947
|
if (!Array.isArray(raw))
|
|
@@ -26022,6 +27100,12 @@ var init_test_coverage_gaps = __esm({
|
|
|
26022
27100
|
for (const g of sorted) {
|
|
26023
27101
|
byStatus[g.coverageStatus] += 1;
|
|
26024
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;
|
|
26025
27109
|
const boundaries = sorted.length === 0 ? [] : [
|
|
26026
27110
|
MEANINGFUL_ASSERTION_DISCLOSURE,
|
|
26027
27111
|
DYNAMIC_DISPATCH_DISCLOSURE,
|
|
@@ -26029,10 +27113,17 @@ var init_test_coverage_gaps = __esm({
|
|
|
26029
27113
|
];
|
|
26030
27114
|
return ok({
|
|
26031
27115
|
data: {
|
|
26032
|
-
gaps:
|
|
27116
|
+
gaps: kept,
|
|
26033
27117
|
totalGapsCount: sorted.length,
|
|
26034
27118
|
byStatus,
|
|
26035
|
-
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
|
+
} : {}
|
|
26036
27127
|
},
|
|
26037
27128
|
vaultState: {
|
|
26038
27129
|
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
@@ -26044,7 +27135,7 @@ var init_test_coverage_gaps = __esm({
|
|
|
26044
27135
|
});
|
|
26045
27136
|
|
|
26046
27137
|
// ../mcp/dist/src/tools/tests-for-change.js
|
|
26047
|
-
import { z as
|
|
27138
|
+
import { z as z104 } from "zod";
|
|
26048
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;
|
|
26049
27140
|
var init_tests_for_change = __esm({
|
|
26050
27141
|
"../mcp/dist/src/tools/tests-for-change.js"() {
|
|
@@ -26058,8 +27149,8 @@ var init_tests_for_change = __esm({
|
|
|
26058
27149
|
APEX_TRIGGER_PREFIX6 = "ApexTrigger:";
|
|
26059
27150
|
MAX_CHANGED_ITEMS = 500;
|
|
26060
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.";
|
|
26061
|
-
testsForChangeInputSchema =
|
|
26062
|
-
changedComponents:
|
|
27152
|
+
testsForChangeInputSchema = z104.object({
|
|
27153
|
+
changedComponents: z104.array(z104.string().min(1)).min(1).max(MAX_CHANGED_ITEMS)
|
|
26063
27154
|
});
|
|
26064
27155
|
isApexCallable5 = (id) => id.startsWith(APEX_CLASS_PREFIX8) || id.startsWith(APEX_TRIGGER_PREFIX6);
|
|
26065
27156
|
isTestClass7 = (node) => node.properties["isTest"] === true;
|
|
@@ -26219,9 +27310,711 @@ var init_tests_for_change = __esm({
|
|
|
26219
27310
|
}
|
|
26220
27311
|
});
|
|
26221
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
|
+
|
|
26222
28015
|
// ../mcp/dist/src/tools/what-happens-on-save.js
|
|
26223
|
-
import { z as
|
|
26224
|
-
var
|
|
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;
|
|
26225
28018
|
var init_what_happens_on_save = __esm({
|
|
26226
28019
|
"../mcp/dist/src/tools/what-happens-on-save.js"() {
|
|
26227
28020
|
"use strict";
|
|
@@ -26229,7 +28022,7 @@ var init_what_happens_on_save = __esm({
|
|
|
26229
28022
|
init_src();
|
|
26230
28023
|
init_soe_admission();
|
|
26231
28024
|
init_soe_payload_bounds();
|
|
26232
|
-
|
|
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.";
|
|
26233
28026
|
ALLOWED_EVENTS = [
|
|
26234
28027
|
"insert",
|
|
26235
28028
|
"update",
|
|
@@ -26237,13 +28030,13 @@ var init_what_happens_on_save = __esm({
|
|
|
26237
28030
|
"delete",
|
|
26238
28031
|
"undelete"
|
|
26239
28032
|
];
|
|
26240
|
-
whatHappensOnSaveInputSchema =
|
|
26241
|
-
objectApiName:
|
|
28033
|
+
whatHappensOnSaveInputSchema = z106.object({
|
|
28034
|
+
objectApiName: z106.string().min(1),
|
|
26242
28035
|
// Accept "after update" / "Before Insert" etc.: lower-case and drop the
|
|
26243
28036
|
// before/after timing prefix so the bare DML event matches the enum. The
|
|
26244
28037
|
// SOE walker models both timings internally; the event arg selects the row.
|
|
26245
|
-
event:
|
|
26246
|
-
recordTypeId:
|
|
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()
|
|
26247
28040
|
});
|
|
26248
28041
|
workflowMatchesEvent2 = (triggerType, event) => {
|
|
26249
28042
|
if (typeof triggerType !== "string")
|
|
@@ -26609,12 +28402,12 @@ var init_what_happens_on_save = __esm({
|
|
|
26609
28402
|
conditionalSteps: conditionalCount,
|
|
26610
28403
|
asyncFanOut
|
|
26611
28404
|
},
|
|
26612
|
-
disclosure: composeSoeDisclosure(
|
|
28405
|
+
disclosure: composeSoeDisclosure(DISCLOSURE10, objectModeled)
|
|
26613
28406
|
};
|
|
26614
|
-
const budget = enforceSoeByteBudget(data, soe);
|
|
28407
|
+
const budget = enforceSoeByteBudget(data, [soe]);
|
|
26615
28408
|
if (budget.truncated) {
|
|
26616
28409
|
data.truncated = true;
|
|
26617
|
-
data.disclosure = `${data.disclosure} ${soeTruncationNote(budget
|
|
28410
|
+
data.disclosure = `${data.disclosure} ${soeTruncationNote(budget)}`;
|
|
26618
28411
|
}
|
|
26619
28412
|
return ok({
|
|
26620
28413
|
data,
|
|
@@ -26627,9 +28420,111 @@ var init_what_happens_on_save = __esm({
|
|
|
26627
28420
|
}
|
|
26628
28421
|
});
|
|
26629
28422
|
|
|
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"() {
|
|
28428
|
+
"use strict";
|
|
28429
|
+
init_dist();
|
|
28430
|
+
init_src();
|
|
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
|
+
});
|
|
28468
|
+
}
|
|
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
|
+
});
|
|
28521
|
+
};
|
|
28522
|
+
}
|
|
28523
|
+
});
|
|
28524
|
+
|
|
26630
28525
|
// ../mcp/dist/src/tools/what-if-change-method-signature.js
|
|
26631
|
-
import { z as
|
|
26632
|
-
var APEX_CLASS_PREFIX9,
|
|
28526
|
+
import { z as z108 } from "zod";
|
|
28527
|
+
var APEX_CLASS_PREFIX9, DISCLOSURE12, whatIfChangeMethodSignatureInputSchema, readMethodName, isTestClass8, buildExplanation2, classifyCaller, aggregateVerdict4, compareImpacts, compareIds, collectCallers, collectCoveringTests, whatIfChangeMethodSignatureHandler;
|
|
26633
28528
|
var init_what_if_change_method_signature = __esm({
|
|
26634
28529
|
"../mcp/dist/src/tools/what-if-change-method-signature.js"() {
|
|
26635
28530
|
"use strict";
|
|
@@ -26637,12 +28532,13 @@ var init_what_if_change_method_signature = __esm({
|
|
|
26637
28532
|
init_src();
|
|
26638
28533
|
init_coerce_id();
|
|
26639
28534
|
init_coverage_trust();
|
|
28535
|
+
init_phantom_node();
|
|
26640
28536
|
APEX_CLASS_PREFIX9 = "ApexClass:";
|
|
26641
|
-
|
|
26642
|
-
whatIfChangeMethodSignatureInputSchema =
|
|
26643
|
-
classApiName:
|
|
26644
|
-
methodName:
|
|
26645
|
-
newSignature:
|
|
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()
|
|
26646
28542
|
});
|
|
26647
28543
|
readMethodName = (edge) => {
|
|
26648
28544
|
const raw = edge.properties["methodName"];
|
|
@@ -26776,7 +28672,7 @@ var init_what_if_change_method_signature = __esm({
|
|
|
26776
28672
|
if (nodeResult.value === null) {
|
|
26777
28673
|
return err({
|
|
26778
28674
|
kind: "component-not-found",
|
|
26779
|
-
message:
|
|
28675
|
+
message: await phantomAwareNotFoundMessage(ctx, classId, "ApexClass"),
|
|
26780
28676
|
path: classId
|
|
26781
28677
|
});
|
|
26782
28678
|
}
|
|
@@ -26823,7 +28719,7 @@ var init_what_if_change_method_signature = __esm({
|
|
|
26823
28719
|
verdict: coverage.verdict,
|
|
26824
28720
|
...coverage.coverageCaveat !== void 0 ? { coverageCaveat: coverage.coverageCaveat } : {},
|
|
26825
28721
|
trust: coverage.trust,
|
|
26826
|
-
disclosure:
|
|
28722
|
+
disclosure: DISCLOSURE12
|
|
26827
28723
|
},
|
|
26828
28724
|
vaultState: {
|
|
26829
28725
|
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
@@ -26835,8 +28731,8 @@ var init_what_if_change_method_signature = __esm({
|
|
|
26835
28731
|
});
|
|
26836
28732
|
|
|
26837
28733
|
// ../mcp/dist/src/tools/what-if-deactivate-flow.js
|
|
26838
|
-
import { z as
|
|
26839
|
-
var FLOW_PREFIX3,
|
|
28734
|
+
import { z as z109 } from "zod";
|
|
28735
|
+
var FLOW_PREFIX3, DISCLOSURE13, whatIfDeactivateFlowInputSchema, stripPrefix2, readFlowStatus2, classifyOutgoingEdge, buildExplanation3, aggregateVerdict5, compareImpacts2, collectFiringConditions, whatIfDeactivateFlowHandler;
|
|
26840
28736
|
var init_what_if_deactivate_flow = __esm({
|
|
26841
28737
|
"../mcp/dist/src/tools/what-if-deactivate-flow.js"() {
|
|
26842
28738
|
"use strict";
|
|
@@ -26845,9 +28741,9 @@ var init_what_if_deactivate_flow = __esm({
|
|
|
26845
28741
|
init_coerce_id();
|
|
26846
28742
|
init_coverage_trust();
|
|
26847
28743
|
FLOW_PREFIX3 = "Flow:";
|
|
26848
|
-
|
|
26849
|
-
whatIfDeactivateFlowInputSchema =
|
|
26850
|
-
flowId:
|
|
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)
|
|
26851
28747
|
});
|
|
26852
28748
|
stripPrefix2 = (id) => {
|
|
26853
28749
|
const colonIdx = id.indexOf(":");
|
|
@@ -27005,7 +28901,7 @@ var init_what_if_deactivate_flow = __esm({
|
|
|
27005
28901
|
verdict: coverage.verdict,
|
|
27006
28902
|
...coverage.coverageCaveat !== void 0 ? { coverageCaveat: coverage.coverageCaveat } : {},
|
|
27007
28903
|
trust: coverage.trust,
|
|
27008
|
-
disclosure:
|
|
28904
|
+
disclosure: DISCLOSURE13
|
|
27009
28905
|
},
|
|
27010
28906
|
vaultState: {
|
|
27011
28907
|
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
@@ -27017,8 +28913,8 @@ var init_what_if_deactivate_flow = __esm({
|
|
|
27017
28913
|
});
|
|
27018
28914
|
|
|
27019
28915
|
// ../mcp/dist/src/tools/what-if-disable-trigger.js
|
|
27020
|
-
import { z as
|
|
27021
|
-
var APEX_TRIGGER_PREFIX7,
|
|
28916
|
+
import { z as z110 } from "zod";
|
|
28917
|
+
var APEX_TRIGGER_PREFIX7, DISCLOSURE14, whatIfDisableTriggerInputSchema, readTriggerStatus, readTriggerEvents, classifyOutgoingEdge2, buildExplanation4, aggregateVerdict6, compareImpacts3, findParentObject, whatIfDisableTriggerHandler;
|
|
27022
28918
|
var init_what_if_disable_trigger = __esm({
|
|
27023
28919
|
"../mcp/dist/src/tools/what-if-disable-trigger.js"() {
|
|
27024
28920
|
"use strict";
|
|
@@ -27026,9 +28922,9 @@ var init_what_if_disable_trigger = __esm({
|
|
|
27026
28922
|
init_src();
|
|
27027
28923
|
init_coverage_trust();
|
|
27028
28924
|
APEX_TRIGGER_PREFIX7 = "ApexTrigger:";
|
|
27029
|
-
|
|
27030
|
-
whatIfDisableTriggerInputSchema =
|
|
27031
|
-
triggerId:
|
|
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)
|
|
27032
28928
|
});
|
|
27033
28929
|
readTriggerStatus = (node) => {
|
|
27034
28930
|
const raw = node.properties["status"];
|
|
@@ -27179,7 +29075,7 @@ var init_what_if_disable_trigger = __esm({
|
|
|
27179
29075
|
verdict: coverage.verdict,
|
|
27180
29076
|
...coverage.coverageCaveat !== void 0 ? { coverageCaveat: coverage.coverageCaveat } : {},
|
|
27181
29077
|
trust: coverage.trust,
|
|
27182
|
-
disclosure:
|
|
29078
|
+
disclosure: DISCLOSURE14
|
|
27183
29079
|
},
|
|
27184
29080
|
vaultState: {
|
|
27185
29081
|
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
@@ -27191,22 +29087,22 @@ var init_what_if_disable_trigger = __esm({
|
|
|
27191
29087
|
});
|
|
27192
29088
|
|
|
27193
29089
|
// ../mcp/dist/src/tools/what-if-merge-profiles.js
|
|
27194
|
-
import { z as
|
|
27195
|
-
var PROFILE_PREFIX,
|
|
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;
|
|
27196
29092
|
var init_what_if_merge_profiles = __esm({
|
|
27197
29093
|
"../mcp/dist/src/tools/what-if-merge-profiles.js"() {
|
|
27198
29094
|
"use strict";
|
|
27199
29095
|
init_dist();
|
|
27200
29096
|
init_src();
|
|
27201
29097
|
PROFILE_PREFIX = "Profile:";
|
|
27202
|
-
|
|
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.";
|
|
27203
29099
|
MERGE_DEFAULT_LIMIT = 120;
|
|
27204
29100
|
MERGE_MAX_LIMIT = 2e3;
|
|
27205
|
-
whatIfMergeProfilesInputSchema =
|
|
27206
|
-
profileIdA:
|
|
27207
|
-
profileIdB:
|
|
27208
|
-
limit:
|
|
27209
|
-
offset:
|
|
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()
|
|
27210
29106
|
});
|
|
27211
29107
|
readUserPermissions = (profile) => {
|
|
27212
29108
|
const raw = profile.properties["userPermissions"];
|
|
@@ -27591,7 +29487,7 @@ var init_what_if_merge_profiles = __esm({
|
|
|
27591
29487
|
const page = conflicts.slice(offset, offset + limit);
|
|
27592
29488
|
const hasMore = offset + page.length < conflicts.length;
|
|
27593
29489
|
const truncated = hasMore || offset > 0;
|
|
27594
|
-
const disclosure = truncated ? `${
|
|
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;
|
|
27595
29491
|
return ok({
|
|
27596
29492
|
data: {
|
|
27597
29493
|
profileIdA,
|
|
@@ -27620,8 +29516,8 @@ var init_what_if_merge_profiles = __esm({
|
|
|
27620
29516
|
});
|
|
27621
29517
|
|
|
27622
29518
|
// ../mcp/dist/src/tools/what-if-remove-picklist-value.js
|
|
27623
|
-
import { z as
|
|
27624
|
-
var
|
|
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;
|
|
27625
29521
|
var init_what_if_remove_picklist_value = __esm({
|
|
27626
29522
|
"../mcp/dist/src/tools/what-if-remove-picklist-value.js"() {
|
|
27627
29523
|
"use strict";
|
|
@@ -27629,7 +29525,7 @@ var init_what_if_remove_picklist_value = __esm({
|
|
|
27629
29525
|
init_src();
|
|
27630
29526
|
init_src2();
|
|
27631
29527
|
init_field_properties();
|
|
27632
|
-
|
|
29528
|
+
CUSTOM_FIELD_PREFIX13 = "CustomField:";
|
|
27633
29529
|
PICKLIST_TYPES2 = /* @__PURE__ */ new Set([
|
|
27634
29530
|
"Picklist",
|
|
27635
29531
|
"MultiselectPicklist"
|
|
@@ -27647,8 +29543,8 @@ var init_what_if_remove_picklist_value = __esm({
|
|
|
27647
29543
|
"ListView",
|
|
27648
29544
|
"FlexiPage"
|
|
27649
29545
|
];
|
|
27650
|
-
|
|
27651
|
-
|
|
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) => {
|
|
27652
29548
|
const coverage = summarizeCoverage(ctx.manifest, PICKLIST_VALUE_COVERAGE);
|
|
27653
29549
|
if (coverage.status === "complete")
|
|
27654
29550
|
return void 0;
|
|
@@ -27659,9 +29555,9 @@ var init_what_if_remove_picklist_value = __esm({
|
|
|
27659
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".`
|
|
27660
29556
|
};
|
|
27661
29557
|
};
|
|
27662
|
-
whatIfRemovePicklistValueInputSchema =
|
|
27663
|
-
fieldId:
|
|
27664
|
-
value:
|
|
29558
|
+
whatIfRemovePicklistValueInputSchema = z112.object({
|
|
29559
|
+
fieldId: z112.string().min(1),
|
|
29560
|
+
value: z112.string().min(1)
|
|
27665
29561
|
});
|
|
27666
29562
|
buildValueNeedles = (value) => [
|
|
27667
29563
|
`'${value}'`,
|
|
@@ -27760,10 +29656,10 @@ var init_what_if_remove_picklist_value = __esm({
|
|
|
27760
29656
|
return "risky";
|
|
27761
29657
|
};
|
|
27762
29658
|
whatIfRemovePicklistValueHandler = async (ctx, input2) => {
|
|
27763
|
-
if (!input2.fieldId.startsWith(
|
|
29659
|
+
if (!input2.fieldId.startsWith(CUSTOM_FIELD_PREFIX13)) {
|
|
27764
29660
|
return err({
|
|
27765
29661
|
kind: "invalid-query",
|
|
27766
|
-
message: `fieldId must start with '${
|
|
29662
|
+
message: `fieldId must start with '${CUSTOM_FIELD_PREFIX13}'; got '${input2.fieldId}'`,
|
|
27767
29663
|
path: "fieldId"
|
|
27768
29664
|
});
|
|
27769
29665
|
}
|
|
@@ -27842,7 +29738,7 @@ var init_what_if_remove_picklist_value = __esm({
|
|
|
27842
29738
|
}
|
|
27843
29739
|
const sortedImpacts = [...impactsById.values()].sort((a, b) => a.componentId < b.componentId ? -1 : a.componentId > b.componentId ? 1 : 0);
|
|
27844
29740
|
const compatibility = sortedImpacts.length === 0 ? "review" : "breaking";
|
|
27845
|
-
const coverageCaveat =
|
|
29741
|
+
const coverageCaveat = coverageCaveatFor5(ctx);
|
|
27846
29742
|
const rawVerdict = aggregateVerdict7(sortedImpacts);
|
|
27847
29743
|
const verdict = rawVerdict === "safe" && coverageCaveat !== void 0 ? "review" : rawVerdict;
|
|
27848
29744
|
return ok({
|
|
@@ -27863,11 +29759,11 @@ var init_what_if_remove_picklist_value = __esm({
|
|
|
27863
29759
|
...coverageCaveat !== void 0 ? { missingCoverage: coverageCaveat.missingCoverage } : {}
|
|
27864
29760
|
},
|
|
27865
29761
|
limitations: [
|
|
27866
|
-
|
|
29762
|
+
DISCLOSURE16,
|
|
27867
29763
|
...coverageCaveat !== void 0 ? [coverageCaveat.message] : []
|
|
27868
29764
|
]
|
|
27869
29765
|
},
|
|
27870
|
-
disclosure:
|
|
29766
|
+
disclosure: DISCLOSURE16
|
|
27871
29767
|
},
|
|
27872
29768
|
vaultState: {
|
|
27873
29769
|
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
@@ -27879,8 +29775,8 @@ var init_what_if_remove_picklist_value = __esm({
|
|
|
27879
29775
|
});
|
|
27880
29776
|
|
|
27881
29777
|
// ../mcp/dist/src/tools/what-if-split-profile.js
|
|
27882
|
-
import { z as
|
|
27883
|
-
var PROFILE_PREFIX2, PERMISSION_SET_PREFIX, SPLIT_DEFAULT_LIMIT, SPLIT_MAX_LIMIT,
|
|
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;
|
|
27884
29780
|
var init_what_if_split_profile = __esm({
|
|
27885
29781
|
"../mcp/dist/src/tools/what-if-split-profile.js"() {
|
|
27886
29782
|
"use strict";
|
|
@@ -27890,12 +29786,12 @@ var init_what_if_split_profile = __esm({
|
|
|
27890
29786
|
PERMISSION_SET_PREFIX = "PermissionSet:";
|
|
27891
29787
|
SPLIT_DEFAULT_LIMIT = 120;
|
|
27892
29788
|
SPLIT_MAX_LIMIT = 2e3;
|
|
27893
|
-
|
|
27894
|
-
whatIfSplitProfileInputSchema =
|
|
27895
|
-
profileId:
|
|
27896
|
-
targetPermSets:
|
|
27897
|
-
limit:
|
|
27898
|
-
offset:
|
|
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()
|
|
27899
29795
|
});
|
|
27900
29796
|
tokenize4 = (raw) => {
|
|
27901
29797
|
const spaced = raw.replace(/[._\-/]/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/([A-Z])([A-Z][a-z])/g, "$1 $2");
|
|
@@ -28156,7 +30052,7 @@ var init_what_if_split_profile = __esm({
|
|
|
28156
30052
|
const page = assignments.slice(offset, offset + limit);
|
|
28157
30053
|
const hasMore = offset + page.length < assignments.length;
|
|
28158
30054
|
const truncated = hasMore || offset > 0;
|
|
28159
|
-
const disclosure = truncated ? `${
|
|
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;
|
|
28160
30056
|
return ok({
|
|
28161
30057
|
data: {
|
|
28162
30058
|
profileId,
|
|
@@ -28184,7 +30080,7 @@ var init_what_if_split_profile = __esm({
|
|
|
28184
30080
|
});
|
|
28185
30081
|
|
|
28186
30082
|
// ../mcp/dist/src/tools/why-cant-user-see-record.js
|
|
28187
|
-
import { z as
|
|
30083
|
+
import { z as z114 } from "zod";
|
|
28188
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;
|
|
28189
30085
|
var init_why_cant_user_see_record = __esm({
|
|
28190
30086
|
"../mcp/dist/src/tools/why-cant-user-see-record.js"() {
|
|
@@ -28205,13 +30101,13 @@ var init_why_cant_user_see_record = __esm({
|
|
|
28205
30101
|
]);
|
|
28206
30102
|
ALL_INTERNAL_USERS_GROUP_ID = "Group:AllInternalUsers";
|
|
28207
30103
|
ROLE_HIERARCHY_MAX_DEPTH = 100;
|
|
28208
|
-
whyCantUserSeeRecordInputSchema =
|
|
28209
|
-
componentId:
|
|
28210
|
-
userContext:
|
|
28211
|
-
profileId:
|
|
28212
|
-
permissionSetIds:
|
|
28213
|
-
roleId:
|
|
28214
|
-
groupIds:
|
|
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()
|
|
28215
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, {
|
|
28216
30112
|
message: "userContext must supply at least one of: profileId, permissionSetIds, roleId, groupIds"
|
|
28217
30113
|
})
|
|
@@ -28581,17 +30477,17 @@ var init_why_cant_user_see_record = __esm({
|
|
|
28581
30477
|
});
|
|
28582
30478
|
|
|
28583
30479
|
// ../mcp/dist/src/tools/why-field-changed.js
|
|
28584
|
-
import { z as
|
|
28585
|
-
var
|
|
30480
|
+
import { z as z115 } from "zod";
|
|
30481
|
+
var CUSTOM_FIELD_PREFIX14, DISCLOSURE18, whyFieldChangedInputSchema, surfaceFirstCondition3, surfaceTriggerEvent, buildWriter, whyFieldChangedHandler;
|
|
28586
30482
|
var init_why_field_changed = __esm({
|
|
28587
30483
|
"../mcp/dist/src/tools/why-field-changed.js"() {
|
|
28588
30484
|
"use strict";
|
|
28589
30485
|
init_dist();
|
|
28590
30486
|
init_src();
|
|
28591
|
-
|
|
28592
|
-
|
|
28593
|
-
whyFieldChangedInputSchema =
|
|
28594
|
-
fieldId:
|
|
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)
|
|
28595
30491
|
});
|
|
28596
30492
|
surfaceFirstCondition3 = async (ctx, writerId) => {
|
|
28597
30493
|
const edgesResult = await listEdges(ctx.graph, writerId, {
|
|
@@ -28641,10 +30537,10 @@ var init_why_field_changed = __esm({
|
|
|
28641
30537
|
return ok(triggerEvent === void 0 ? withCondition : { ...withCondition, triggerEvent });
|
|
28642
30538
|
};
|
|
28643
30539
|
whyFieldChangedHandler = async (ctx, input2) => {
|
|
28644
|
-
if (!input2.fieldId.startsWith(
|
|
30540
|
+
if (!input2.fieldId.startsWith(CUSTOM_FIELD_PREFIX14)) {
|
|
28645
30541
|
return err({
|
|
28646
30542
|
kind: "invalid-query",
|
|
28647
|
-
message: `fieldId must start with '${
|
|
30543
|
+
message: `fieldId must start with '${CUSTOM_FIELD_PREFIX14}'; got '${input2.fieldId}'`,
|
|
28648
30544
|
path: "fieldId"
|
|
28649
30545
|
});
|
|
28650
30546
|
}
|
|
@@ -28704,7 +30600,7 @@ var init_why_field_changed = __esm({
|
|
|
28704
30600
|
fieldId,
|
|
28705
30601
|
writers: sortedWriters,
|
|
28706
30602
|
summary: { declaredCount, heuristicCount },
|
|
28707
|
-
disclosure:
|
|
30603
|
+
disclosure: DISCLOSURE18
|
|
28708
30604
|
},
|
|
28709
30605
|
vaultState: {
|
|
28710
30606
|
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
@@ -28725,7 +30621,7 @@ __export(tools_exports, {
|
|
|
28725
30621
|
registerTools: () => registerTools
|
|
28726
30622
|
});
|
|
28727
30623
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
28728
|
-
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;
|
|
28729
30625
|
var init_tools = __esm({
|
|
28730
30626
|
"../mcp/dist/src/tools/index.js"() {
|
|
28731
30627
|
"use strict";
|
|
@@ -28792,6 +30688,7 @@ var init_tools = __esm({
|
|
|
28792
30688
|
init_get_impact();
|
|
28793
30689
|
init_get_subgraph();
|
|
28794
30690
|
init_governor_limit_risks();
|
|
30691
|
+
init_guidance();
|
|
28795
30692
|
init_health_check();
|
|
28796
30693
|
init_integration_map();
|
|
28797
30694
|
init_integration_procedure_chain();
|
|
@@ -28824,6 +30721,7 @@ var init_tools = __esm({
|
|
|
28824
30721
|
init_search_flow_metadata();
|
|
28825
30722
|
init_snapshot_trend();
|
|
28826
30723
|
init_synthesis_reports();
|
|
30724
|
+
init_synthesize_answer();
|
|
28827
30725
|
init_tech_debt_score();
|
|
28828
30726
|
init_test_coverage_for_method();
|
|
28829
30727
|
init_test_coverage_gaps();
|
|
@@ -28831,8 +30729,10 @@ var init_tools = __esm({
|
|
|
28831
30729
|
init_unassigned_permission_sets();
|
|
28832
30730
|
init_unused_components();
|
|
28833
30731
|
init_unused_fields_deep();
|
|
30732
|
+
init_value_change_audit();
|
|
28834
30733
|
init_what_happens_on_save();
|
|
28835
30734
|
init_what_if_change_field_type();
|
|
30735
|
+
init_what_if_change_field_value();
|
|
28836
30736
|
init_what_if_change_method_signature();
|
|
28837
30737
|
init_what_if_deactivate_flow();
|
|
28838
30738
|
init_what_if_disable_trigger();
|
|
@@ -28855,6 +30755,14 @@ var init_tools = __esm({
|
|
|
28855
30755
|
type: "object",
|
|
28856
30756
|
properties: {}
|
|
28857
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
|
+
});
|
|
28858
30766
|
ORG_PULSE_INPUT_SCHEMA = Object.freeze({
|
|
28859
30767
|
type: "object",
|
|
28860
30768
|
properties: {
|
|
@@ -29375,8 +31283,7 @@ var init_tools = __esm({
|
|
|
29375
31283
|
properties: {
|
|
29376
31284
|
eventId: { type: "string", minLength: 1 },
|
|
29377
31285
|
limit: { type: "integer", minimum: 1, maximum: 500 }
|
|
29378
|
-
}
|
|
29379
|
-
required: ["eventId"]
|
|
31286
|
+
}
|
|
29380
31287
|
});
|
|
29381
31288
|
LOOKUP_RECORD_INPUT_SCHEMA = Object.freeze({
|
|
29382
31289
|
type: "object",
|
|
@@ -29385,6 +31292,12 @@ var init_tools = __esm({
|
|
|
29385
31292
|
},
|
|
29386
31293
|
required: ["recordId"]
|
|
29387
31294
|
});
|
|
31295
|
+
GUIDANCE_INPUT_SCHEMA = Object.freeze({
|
|
31296
|
+
type: "object",
|
|
31297
|
+
properties: {
|
|
31298
|
+
topic: { type: "string", minLength: 1 }
|
|
31299
|
+
}
|
|
31300
|
+
});
|
|
29388
31301
|
EXPLAIN_FIELD_INPUT_SCHEMA = Object.freeze({
|
|
29389
31302
|
type: "object",
|
|
29390
31303
|
properties: {
|
|
@@ -29492,7 +31405,8 @@ var init_tools = __esm({
|
|
|
29492
31405
|
type: "string",
|
|
29493
31406
|
enum: ["identifier", "contact", "financial", "health", "all"]
|
|
29494
31407
|
},
|
|
29495
|
-
limit: { type: "integer", minimum: 1, maximum: 500 }
|
|
31408
|
+
limit: { type: "integer", minimum: 1, maximum: 500 },
|
|
31409
|
+
offset: { type: "integer", minimum: 0 }
|
|
29496
31410
|
}
|
|
29497
31411
|
});
|
|
29498
31412
|
FIELD_ACCESS_AUDIT_INPUT_SCHEMA = Object.freeze({
|
|
@@ -29782,7 +31696,8 @@ var init_tools = __esm({
|
|
|
29782
31696
|
CRUD_FLS_AUDIT_INPUT_SCHEMA = Object.freeze({
|
|
29783
31697
|
type: "object",
|
|
29784
31698
|
properties: {
|
|
29785
|
-
limit: { type: "integer", minimum: 1, maximum: 500 }
|
|
31699
|
+
limit: { type: "integer", minimum: 1, maximum: 500 },
|
|
31700
|
+
offset: { type: "integer", minimum: 0 }
|
|
29786
31701
|
}
|
|
29787
31702
|
});
|
|
29788
31703
|
TEST_COVERAGE_GAPS_INPUT_SCHEMA = Object.freeze({
|
|
@@ -29792,9 +31707,30 @@ var init_tools = __esm({
|
|
|
29792
31707
|
type: "array",
|
|
29793
31708
|
items: { type: "string", minLength: 1 },
|
|
29794
31709
|
maxItems: 500
|
|
29795
|
-
}
|
|
31710
|
+
},
|
|
31711
|
+
limit: { type: "integer", minimum: 1, maximum: 500 },
|
|
31712
|
+
offset: { type: "integer", minimum: 0 }
|
|
29796
31713
|
}
|
|
29797
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
|
+
});
|
|
29798
31734
|
WHAT_IF_CHANGE_FIELD_TYPE_INPUT_SCHEMA = Object.freeze({
|
|
29799
31735
|
type: "object",
|
|
29800
31736
|
properties: {
|
|
@@ -30277,9 +32213,14 @@ var init_tools = __esm({
|
|
|
30277
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.',
|
|
30278
32214
|
inputSchema: CAPABILITIES_INPUT_SCHEMA
|
|
30279
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
|
+
},
|
|
30280
32221
|
{
|
|
30281
32222
|
name: "sfi.route_question",
|
|
30282
|
-
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.",
|
|
30283
32224
|
inputSchema: ROUTE_QUESTION_INPUT_SCHEMA
|
|
30284
32225
|
},
|
|
30285
32226
|
{
|
|
@@ -30464,7 +32405,7 @@ var init_tools = __esm({
|
|
|
30464
32405
|
},
|
|
30465
32406
|
{
|
|
30466
32407
|
name: "sfi.permission_risk_report",
|
|
30467
|
-
description: "Ranked permission
|
|
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.",
|
|
30468
32409
|
inputSchema: SYNTHESIS_INPUT_SCHEMA
|
|
30469
32410
|
},
|
|
30470
32411
|
{
|
|
@@ -30504,9 +32445,14 @@ var init_tools = __esm({
|
|
|
30504
32445
|
},
|
|
30505
32446
|
{
|
|
30506
32447
|
name: "sfi.event_subscribers",
|
|
30507
|
-
description:
|
|
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.',
|
|
30508
32449
|
inputSchema: EVENT_SUBSCRIBERS_INPUT_SCHEMA
|
|
30509
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
|
+
},
|
|
30510
32456
|
{
|
|
30511
32457
|
name: "sfi.find_code_usages",
|
|
30512
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`.",
|
|
@@ -30524,7 +32470,7 @@ var init_tools = __esm({
|
|
|
30524
32470
|
},
|
|
30525
32471
|
{
|
|
30526
32472
|
name: "sfi.safe_to_delete_field",
|
|
30527
|
-
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),
|
|
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).",
|
|
30528
32474
|
inputSchema: SAFE_TO_DELETE_FIELD_INPUT_SCHEMA
|
|
30529
32475
|
},
|
|
30530
32476
|
{
|
|
@@ -30557,6 +32503,16 @@ var init_tools = __esm({
|
|
|
30557
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.",
|
|
30558
32504
|
inputSchema: FIELD_CHANGE_ADVISOR_INPUT_SCHEMA
|
|
30559
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
|
+
},
|
|
30560
32516
|
{
|
|
30561
32517
|
name: "sfi.live_drift_check",
|
|
30562
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.",
|
|
@@ -30589,12 +32545,12 @@ var init_tools = __esm({
|
|
|
30589
32545
|
},
|
|
30590
32546
|
{
|
|
30591
32547
|
name: "sfi.pii_inventory",
|
|
30592
|
-
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
|
|
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.",
|
|
30593
32549
|
inputSchema: PII_INVENTORY_INPUT_SCHEMA
|
|
30594
32550
|
},
|
|
30595
32551
|
{
|
|
30596
32552
|
name: "sfi.field_access_audit",
|
|
30597
|
-
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
|
|
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`.",
|
|
30598
32554
|
inputSchema: FIELD_ACCESS_AUDIT_INPUT_SCHEMA
|
|
30599
32555
|
},
|
|
30600
32556
|
{
|
|
@@ -30689,12 +32645,12 @@ var init_tools = __esm({
|
|
|
30689
32645
|
},
|
|
30690
32646
|
{
|
|
30691
32647
|
name: "sfi.crud_fls_audit",
|
|
30692
|
-
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
|
|
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.",
|
|
30693
32649
|
inputSchema: CRUD_FLS_AUDIT_INPUT_SCHEMA
|
|
30694
32650
|
},
|
|
30695
32651
|
{
|
|
30696
32652
|
name: "sfi.test_coverage_gaps",
|
|
30697
|
-
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.",
|
|
30698
32654
|
inputSchema: TEST_COVERAGE_GAPS_INPUT_SCHEMA
|
|
30699
32655
|
},
|
|
30700
32656
|
{
|
|
@@ -31024,6 +32980,10 @@ var init_tools = __esm({
|
|
|
31024
32980
|
return runTool(ctx, args, resolveInputSchema, resolveHandler);
|
|
31025
32981
|
case "sfi.capabilities":
|
|
31026
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);
|
|
31027
32987
|
case "sfi.org_pulse":
|
|
31028
32988
|
return runTool(ctx, args, orgPulseInputSchema, orgPulseHandler);
|
|
31029
32989
|
case "sfi.fleet_find":
|
|
@@ -31194,6 +33154,10 @@ var init_tools = __esm({
|
|
|
31194
33154
|
return runTool(ctx, args, testCoverageGapsInputSchema, testCoverageGapsHandler);
|
|
31195
33155
|
case "sfi.what_if_change_field_type":
|
|
31196
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);
|
|
31197
33161
|
case "sfi.what_if_remove_picklist_value":
|
|
31198
33162
|
return runTool(ctx, args, whatIfRemovePicklistValueInputSchema, whatIfRemovePicklistValueHandler);
|
|
31199
33163
|
case "sfi.what_if_make_field_required":
|
|
@@ -37255,11 +39219,30 @@ var extractEnterpriseMetadata = async (path, config) => {
|
|
|
37255
39219
|
apiName = deriveComponentApiName(path, config.suffix);
|
|
37256
39220
|
}
|
|
37257
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
|
+
}
|
|
37258
39240
|
const node = makeNode(config.type, apiName, path, parentObjectApiName === null ? null : `CustomObject:${parentObjectApiName}`, {
|
|
37259
39241
|
fieldRefs,
|
|
37260
|
-
rawReferenceCount: fieldRefs.length
|
|
39242
|
+
rawReferenceCount: fieldRefs.length,
|
|
39243
|
+
...childRefSummary
|
|
37261
39244
|
});
|
|
37262
|
-
const
|
|
39245
|
+
const fieldRefEdges = fieldRefs.map((fieldId) => ({
|
|
37263
39246
|
fromId: node.id,
|
|
37264
39247
|
toId: fieldId,
|
|
37265
39248
|
edgeType: "references",
|
|
@@ -37267,7 +39250,7 @@ var extractEnterpriseMetadata = async (path, config) => {
|
|
|
37267
39250
|
source: EXTRACTOR_SOURCE10,
|
|
37268
39251
|
properties: { referenceKind: "fieldRef" }
|
|
37269
39252
|
}));
|
|
37270
|
-
return ok({ nodes: [node], edges });
|
|
39253
|
+
return ok({ nodes: [node], edges: [...fieldRefEdges, ...childRefEdges] });
|
|
37271
39254
|
};
|
|
37272
39255
|
var extractReport = (path) => extractEnterpriseMetadata(path, { type: "Report", suffix: ".report-meta.xml" });
|
|
37273
39256
|
var extractDashboard = (path) => extractEnterpriseMetadata(path, { type: "Dashboard", suffix: ".dashboard-meta.xml" });
|
|
@@ -37280,7 +39263,22 @@ var extractReportType = (path) => extractEnterpriseMetadata(path, { type: "Repor
|
|
|
37280
39263
|
var extractFlexiPage = (path) => extractEnterpriseMetadata(path, { type: "FlexiPage", suffix: ".flexipage-meta.xml" });
|
|
37281
39264
|
var extractPermissionSetGroup = (path) => extractEnterpriseMetadata(path, {
|
|
37282
39265
|
type: "PermissionSetGroup",
|
|
37283
|
-
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
|
+
]
|
|
37284
39282
|
});
|
|
37285
39283
|
var extractMutingPermissionSet = (path) => extractEnterpriseMetadata(path, {
|
|
37286
39284
|
type: "MutingPermissionSet",
|
|
@@ -43165,10 +45163,14 @@ var extractRole = async (path) => {
|
|
|
43165
45163
|
return ok({ nodes: [node], edges });
|
|
43166
45164
|
};
|
|
43167
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
|
+
|
|
43168
45170
|
// ../extractors/dist/src/sharing-rules.js
|
|
43169
45171
|
init_dist();
|
|
43170
45172
|
import { readFile as readFile66 } from "node:fs/promises";
|
|
43171
|
-
import { XMLParser as
|
|
45173
|
+
import { XMLParser as XMLParser54, XMLValidator as XMLValidator53 } from "fast-xml-parser";
|
|
43172
45174
|
var SHARING_RULES_FILE_SUFFIX = ".sharingRules-meta.xml";
|
|
43173
45175
|
var ROOT_ELEMENT52 = "SharingRules";
|
|
43174
45176
|
var EXTRACTOR_SOURCE28 = "sharing-rule-extractor";
|
|
@@ -43229,7 +45231,7 @@ var readAndValidateXml48 = async (path) => {
|
|
|
43229
45231
|
cause
|
|
43230
45232
|
});
|
|
43231
45233
|
}
|
|
43232
|
-
const validation =
|
|
45234
|
+
const validation = XMLValidator53.validate(xmlText);
|
|
43233
45235
|
if (validation !== true) {
|
|
43234
45236
|
return err({ kind: "parse-error", path, message: validation.err.msg });
|
|
43235
45237
|
}
|
|
@@ -43419,7 +45421,7 @@ var extractSharingRules = async (path) => {
|
|
|
43419
45421
|
const xmlResult = await readAndValidateXml48(path);
|
|
43420
45422
|
if (!xmlResult.ok)
|
|
43421
45423
|
return xmlResult;
|
|
43422
|
-
const parser = new
|
|
45424
|
+
const parser = new XMLParser54({
|
|
43423
45425
|
ignoreAttributes: true,
|
|
43424
45426
|
parseTagValue: false,
|
|
43425
45427
|
trimValues: true,
|
|
@@ -43466,7 +45468,7 @@ var extractSharingRules = async (path) => {
|
|
|
43466
45468
|
// ../extractors/dist/src/static-resource.js
|
|
43467
45469
|
init_dist();
|
|
43468
45470
|
import { readFile as readFile67 } from "node:fs/promises";
|
|
43469
|
-
import { XMLParser as
|
|
45471
|
+
import { XMLParser as XMLParser55, XMLValidator as XMLValidator54 } from "fast-xml-parser";
|
|
43470
45472
|
var STATIC_RESOURCE_FILE_SUFFIX = ".resource-meta.xml";
|
|
43471
45473
|
var ROOT_ELEMENT53 = "StaticResource";
|
|
43472
45474
|
var REQUIRED_ELEMENTS24 = ["cacheControl"];
|
|
@@ -43491,7 +45493,7 @@ var readAndValidateXml49 = async (path) => {
|
|
|
43491
45493
|
cause
|
|
43492
45494
|
});
|
|
43493
45495
|
}
|
|
43494
|
-
const validation =
|
|
45496
|
+
const validation = XMLValidator54.validate(xmlText);
|
|
43495
45497
|
if (validation !== true) {
|
|
43496
45498
|
return err({ kind: "parse-error", path, message: validation.err.msg });
|
|
43497
45499
|
}
|
|
@@ -43522,7 +45524,7 @@ var extractStaticResource = async (path) => {
|
|
|
43522
45524
|
const xmlResult = await readAndValidateXml49(path);
|
|
43523
45525
|
if (!xmlResult.ok)
|
|
43524
45526
|
return xmlResult;
|
|
43525
|
-
const parser = new
|
|
45527
|
+
const parser = new XMLParser55({
|
|
43526
45528
|
ignoreAttributes: true,
|
|
43527
45529
|
parseTagValue: false,
|
|
43528
45530
|
trimValues: true,
|
|
@@ -43576,7 +45578,7 @@ var extractStaticResource = async (path) => {
|
|
|
43576
45578
|
init_dist();
|
|
43577
45579
|
import { readFile as readFile68 } from "node:fs/promises";
|
|
43578
45580
|
import { basename as basename10, dirname as dirname13 } from "node:path";
|
|
43579
|
-
import { XMLParser as
|
|
45581
|
+
import { XMLParser as XMLParser56, XMLValidator as XMLValidator55 } from "fast-xml-parser";
|
|
43580
45582
|
var VALIDATION_RULE_FILE_SUFFIX = ".validationRule-meta.xml";
|
|
43581
45583
|
var ROOT_ELEMENT54 = "ValidationRule";
|
|
43582
45584
|
var VALIDATION_RULES_DIR_NAME = "validationRules";
|
|
@@ -43611,7 +45613,7 @@ var readAndValidateXml50 = async (path) => {
|
|
|
43611
45613
|
cause
|
|
43612
45614
|
});
|
|
43613
45615
|
}
|
|
43614
|
-
const validation =
|
|
45616
|
+
const validation = XMLValidator55.validate(xmlText);
|
|
43615
45617
|
if (validation !== true) {
|
|
43616
45618
|
return err({ kind: "parse-error", path, message: validation.err.msg });
|
|
43617
45619
|
}
|
|
@@ -43659,7 +45661,7 @@ var extractValidationRule = async (path) => {
|
|
|
43659
45661
|
const xmlResult = await readAndValidateXml50(path);
|
|
43660
45662
|
if (!xmlResult.ok)
|
|
43661
45663
|
return xmlResult;
|
|
43662
|
-
const parser = new
|
|
45664
|
+
const parser = new XMLParser56({
|
|
43663
45665
|
ignoreAttributes: true,
|
|
43664
45666
|
parseTagValue: false,
|
|
43665
45667
|
trimValues: true,
|
|
@@ -43730,7 +45732,7 @@ var extractValidationRule = async (path) => {
|
|
|
43730
45732
|
init_dist();
|
|
43731
45733
|
init_src3();
|
|
43732
45734
|
import { readFile as readFile69 } from "node:fs/promises";
|
|
43733
|
-
import { XMLParser as
|
|
45735
|
+
import { XMLParser as XMLParser57, XMLValidator as XMLValidator56 } from "fast-xml-parser";
|
|
43734
45736
|
var COMPONENT_FILE_SUFFIX = ".component";
|
|
43735
45737
|
var META_FILE_EXT3 = "-meta.xml";
|
|
43736
45738
|
var ROOT_ELEMENT55 = "ApexComponent";
|
|
@@ -43756,14 +45758,14 @@ var readAndValidateXml51 = async (path, missingMessage) => {
|
|
|
43756
45758
|
cause
|
|
43757
45759
|
});
|
|
43758
45760
|
}
|
|
43759
|
-
const validation =
|
|
45761
|
+
const validation = XMLValidator56.validate(xmlText);
|
|
43760
45762
|
if (validation !== true) {
|
|
43761
45763
|
return err({ kind: "parse-error", path, message: validation.err.msg });
|
|
43762
45764
|
}
|
|
43763
45765
|
return ok(xmlText);
|
|
43764
45766
|
};
|
|
43765
45767
|
var parseMetaXml5 = (xmlText, path) => {
|
|
43766
|
-
const parser = new
|
|
45768
|
+
const parser = new XMLParser57({
|
|
43767
45769
|
ignoreAttributes: true,
|
|
43768
45770
|
parseTagValue: false,
|
|
43769
45771
|
trimValues: true,
|
|
@@ -43971,7 +45973,7 @@ var extractVisualforceComponent = async (path) => {
|
|
|
43971
45973
|
init_dist();
|
|
43972
45974
|
init_src3();
|
|
43973
45975
|
import { readFile as readFile70 } from "node:fs/promises";
|
|
43974
|
-
import { XMLParser as
|
|
45976
|
+
import { XMLParser as XMLParser58, XMLValidator as XMLValidator57 } from "fast-xml-parser";
|
|
43975
45977
|
var PAGE_FILE_SUFFIX = ".page";
|
|
43976
45978
|
var META_FILE_EXT4 = "-meta.xml";
|
|
43977
45979
|
var ROOT_ELEMENT56 = "ApexPage";
|
|
@@ -43998,14 +46000,14 @@ var readAndValidateXml52 = async (path, missingMessage) => {
|
|
|
43998
46000
|
cause
|
|
43999
46001
|
});
|
|
44000
46002
|
}
|
|
44001
|
-
const validation =
|
|
46003
|
+
const validation = XMLValidator57.validate(xmlText);
|
|
44002
46004
|
if (validation !== true) {
|
|
44003
46005
|
return err({ kind: "parse-error", path, message: validation.err.msg });
|
|
44004
46006
|
}
|
|
44005
46007
|
return ok(xmlText);
|
|
44006
46008
|
};
|
|
44007
46009
|
var parseMetaXml6 = (xmlText, path) => {
|
|
44008
|
-
const parser = new
|
|
46010
|
+
const parser = new XMLParser58({
|
|
44009
46011
|
ignoreAttributes: true,
|
|
44010
46012
|
parseTagValue: false,
|
|
44011
46013
|
trimValues: true,
|
|
@@ -44219,7 +46221,7 @@ var extractVisualforcePage = async (path) => {
|
|
|
44219
46221
|
init_dist();
|
|
44220
46222
|
import { readFile as readFile71 } from "node:fs/promises";
|
|
44221
46223
|
import { basename as basename11, dirname as dirname14 } from "node:path";
|
|
44222
|
-
import { XMLParser as
|
|
46224
|
+
import { XMLParser as XMLParser59, XMLValidator as XMLValidator58 } from "fast-xml-parser";
|
|
44223
46225
|
var FILE_SUFFIX5 = ".webLink-meta.xml";
|
|
44224
46226
|
var ROOT_ELEMENT57 = "WebLink";
|
|
44225
46227
|
var DIR_NAME4 = "webLinks";
|
|
@@ -44257,7 +46259,7 @@ var readAndValidateXml53 = async (path) => {
|
|
|
44257
46259
|
cause
|
|
44258
46260
|
});
|
|
44259
46261
|
}
|
|
44260
|
-
const validation =
|
|
46262
|
+
const validation = XMLValidator58.validate(xmlText);
|
|
44261
46263
|
if (validation !== true) {
|
|
44262
46264
|
return err({ kind: "parse-error", path, message: validation.err.msg });
|
|
44263
46265
|
}
|
|
@@ -44284,7 +46286,7 @@ var extractWebLink = async (path) => {
|
|
|
44284
46286
|
const xmlResult = await readAndValidateXml53(path);
|
|
44285
46287
|
if (!xmlResult.ok)
|
|
44286
46288
|
return xmlResult;
|
|
44287
|
-
const parser = new
|
|
46289
|
+
const parser = new XMLParser59({
|
|
44288
46290
|
ignoreAttributes: true,
|
|
44289
46291
|
parseTagValue: false,
|
|
44290
46292
|
trimValues: true,
|
|
@@ -44359,7 +46361,7 @@ var extractWebLink = async (path) => {
|
|
|
44359
46361
|
// ../extractors/dist/src/workflow-rule.js
|
|
44360
46362
|
init_dist();
|
|
44361
46363
|
import { readFile as readFile72 } from "node:fs/promises";
|
|
44362
|
-
import { XMLParser as
|
|
46364
|
+
import { XMLParser as XMLParser60, XMLValidator as XMLValidator59 } from "fast-xml-parser";
|
|
44363
46365
|
var WORKFLOW_FILE_SUFFIX = ".workflow-meta.xml";
|
|
44364
46366
|
var ROOT_ELEMENT58 = "Workflow";
|
|
44365
46367
|
var EXTRACTOR_SOURCE29 = "workflow-rule-extractor";
|
|
@@ -44435,7 +46437,7 @@ var readAndValidateXml54 = async (path) => {
|
|
|
44435
46437
|
cause
|
|
44436
46438
|
});
|
|
44437
46439
|
}
|
|
44438
|
-
const validation =
|
|
46440
|
+
const validation = XMLValidator59.validate(xmlText);
|
|
44439
46441
|
if (validation !== true) {
|
|
44440
46442
|
return err({ kind: "parse-error", path, message: validation.err.msg });
|
|
44441
46443
|
}
|
|
@@ -44749,7 +46751,7 @@ var extractWorkflowRule = async (path) => {
|
|
|
44749
46751
|
const xmlResult = await readAndValidateXml54(path);
|
|
44750
46752
|
if (!xmlResult.ok)
|
|
44751
46753
|
return xmlResult;
|
|
44752
|
-
const parser = new
|
|
46754
|
+
const parser = new XMLParser60({
|
|
44753
46755
|
ignoreAttributes: true,
|
|
44754
46756
|
parseTagValue: false,
|
|
44755
46757
|
trimValues: true,
|
|
@@ -46748,7 +48750,7 @@ var registerStatusCommand = (program) => {
|
|
|
46748
48750
|
// dist/src/program.js
|
|
46749
48751
|
var readVersion = () => {
|
|
46750
48752
|
if (true)
|
|
46751
|
-
return "0.1.
|
|
48753
|
+
return "0.1.5";
|
|
46752
48754
|
const pkgUrl = new URL("../../package.json", import.meta.url);
|
|
46753
48755
|
const raw = readFileSync2(fileURLToPath(pkgUrl), "utf8");
|
|
46754
48756
|
const parsed = JSON.parse(raw);
|