sf-intelligence 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -7
- package/dist/index.js +3421 -86
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2315,7 +2315,8 @@ var init_resolve = __esm({
|
|
|
2315
2315
|
const kind = c2.wholeExact ? "exact" : rollupKind(matched);
|
|
2316
2316
|
const nodeTokenSet = new Set(c2.node.tokens);
|
|
2317
2317
|
const matchedNodeTokens = new Set(matched.map((m2) => m2.matchedToken).filter((t2) => nodeTokenSet.has(t2)));
|
|
2318
|
-
|
|
2318
|
+
const nameCoverage = c2.wholeExact ? 1 : matchedNodeTokens.size / c2.node.tokens.length;
|
|
2319
|
+
coverageById.set(c2.node.id, nameCoverage);
|
|
2319
2320
|
if (c2.wholeExact)
|
|
2320
2321
|
wholeExactIds.add(c2.node.id);
|
|
2321
2322
|
if (c2.parentMatched)
|
|
@@ -2328,6 +2329,7 @@ var init_resolve = __esm({
|
|
|
2328
2329
|
parentApiName: c2.node.parentApiName,
|
|
2329
2330
|
score: Number(score.toFixed(6)),
|
|
2330
2331
|
base: Number(base.toFixed(6)),
|
|
2332
|
+
nameCoverage: Number(nameCoverage.toFixed(6)),
|
|
2331
2333
|
matchKind: kind,
|
|
2332
2334
|
evidence: c2.wholeExact ? `exact name match on "${query.trim()}"` : buildEvidence(kind, matched)
|
|
2333
2335
|
});
|
|
@@ -21345,7 +21347,7 @@ var init_live_consent = __esm({
|
|
|
21345
21347
|
});
|
|
21346
21348
|
|
|
21347
21349
|
// ../mcp/dist/src/answer-render.js
|
|
21348
|
-
var cell, renderTrustFooter, mdTable, renderResolveMarkdown, formatDeltas, renderOrgOverviewMarkdown, renderLiveCountMarkdown, renderFieldPopulationMarkdown, renderInactiveUsersMarkdown, renderRouteMarkdown;
|
|
21350
|
+
var cell, renderTrustFooter, mdTable, renderResolveMarkdown, formatDeltas, renderOrgOverviewMarkdown, renderLiveCountMarkdown, renderFieldPopulationMarkdown, renderInactiveUsersMarkdown, renderContextApplied, renderRouteMarkdown;
|
|
21349
21351
|
var init_answer_render = __esm({
|
|
21350
21352
|
"../mcp/dist/src/answer-render.js"() {
|
|
21351
21353
|
"use strict";
|
|
@@ -21459,9 +21461,16 @@ ${table}${capped}
|
|
|
21459
21461
|
|
|
21460
21462
|
${renderTrustFooter(data.trust)}`;
|
|
21461
21463
|
};
|
|
21464
|
+
renderContextApplied = (contextApplied) => {
|
|
21465
|
+
const substituted = contextApplied.substitutedComponentId ?? contextApplied.selection;
|
|
21466
|
+
return `Context applied: '${contextApplied.anaphor}'` + (substituted !== void 0 ? ` \u2192 ${substituted}` : "") + " (from previous turn)" + (contextApplied.inheritedTool !== void 0 ? `; tool inherited: \`${contextApplied.inheritedTool}\`.` : ".");
|
|
21467
|
+
};
|
|
21462
21468
|
renderRouteMarkdown = (route) => {
|
|
21469
|
+
const contextLine = route.contextApplied !== void 0 ? `
|
|
21470
|
+
|
|
21471
|
+
${renderContextApplied(route.contextApplied)}` : "";
|
|
21463
21472
|
if (route.plane === "unknown") {
|
|
21464
|
-
return `I don't have a tool for **"${route.question}"** yet \u2014 ${route.reason} ${route.gap ? `(logged as \`${route.gap.category}\`).` : ""}
|
|
21473
|
+
return `I don't have a tool for **"${route.question}"** yet \u2014 ${route.reason} ${route.gap ? `(logged as \`${route.gap.category}\`).` : ""}` + contextLine;
|
|
21465
21474
|
}
|
|
21466
21475
|
const steps = [];
|
|
21467
21476
|
let plannedTools = route.tools;
|
|
@@ -21491,7 +21500,7 @@ ${route.plan.map((step3, i2) => {
|
|
|
21491
21500
|
|
|
21492
21501
|
Plan: ${steps.join(" \u2192 ")}` + enterprise + clarification + compoundPlan + (route.gap ? `
|
|
21493
21502
|
|
|
21494
|
-
_Partial: ${route.gap.note}_` : "");
|
|
21503
|
+
_Partial: ${route.gap.note}_` : "") + contextLine;
|
|
21495
21504
|
};
|
|
21496
21505
|
}
|
|
21497
21506
|
});
|
|
@@ -24182,7 +24191,7 @@ ${renderTrustFooter(trust)}`;
|
|
|
24182
24191
|
import { appendFile as appendFile3, mkdir as mkdir8 } from "node:fs/promises";
|
|
24183
24192
|
import { homedir as homedir3 } from "node:os";
|
|
24184
24193
|
import { dirname as dirname18, join as join13 } from "node:path";
|
|
24185
|
-
var riskForIntent, routeFromRule, routeForSelectedIntent, normalize, NAMED_COMPONENT_ID, NAMED_FIELD_ID, deriveSaveEvent, deriveKnowledgeTopic, deriveListType, FIELD_PARENT_OBJECTS, deriveFieldListParent, deriveMetadataParentId, NON_OBJECT_CAPTURES, deriveObjectApiFromQuestion, deriveImpactHops, FIELD_MAP_OBJECT, deriveFieldMappingArgs, deriveLayoutForUserArgs, derivePiiInventoryArgs, deriveMetadataCountArgs, deriveOmniType, routeText, RULES, alternativeFromIntent, semanticAlternatives, intentLabel, classifyQuestion, gapLogPath, logGapIfAny;
|
|
24194
|
+
var riskForIntent, routeFromRule, routeForSelectedIntent, normalize, NAMED_COMPONENT_ID, NAMED_FIELD_ID, deriveSaveEvent, deriveKnowledgeTopic, deriveListType, FIELD_PARENT_OBJECTS, deriveFieldListParent, deriveMetadataParentId, NON_OBJECT_CAPTURES, deriveObjectApiFromQuestion, deriveImpactHops, FIELD_MAP_OBJECT, deriveFieldMappingArgs, deriveLayoutForUserArgs, derivePiiInventoryArgs, deriveMetadataCountArgs, deriveOmniType, routeText, RULES, alternativeFromIntent, semanticAlternatives, intentLabel, COMPLEMENTARY_ALTERNATIVE_PAIRS, NAMES_COMPONENT_FOR_LAST_MODIFIED, classifyQuestion, gapLogPath, logGapIfAny;
|
|
24186
24195
|
var init_intent_router = __esm({
|
|
24187
24196
|
"../mcp/dist/src/intent-router.js"() {
|
|
24188
24197
|
"use strict";
|
|
@@ -24797,6 +24806,73 @@ var init_intent_router = __esm({
|
|
|
24797
24806
|
/\bwhat_if_remove_picklist_value\b/
|
|
24798
24807
|
]
|
|
24799
24808
|
},
|
|
24809
|
+
// === R3 §5b — what-if CHANGE-TYPE WHITELIST honest routes ================
|
|
24810
|
+
// The what_if_* simulators cover a fixed change-type list (field type /
|
|
24811
|
+
// required / picklist value, flow deactivation, trigger disable, method
|
|
24812
|
+
// signature, profile merge/split). A what-if ask about a change type with
|
|
24813
|
+
// NO simulator (remove a record type, change a layout assignment, change a
|
|
24814
|
+
// flow variable's type) must not be absorbed by the nearest-neighbor
|
|
24815
|
+
// what_if_* — these rules catch those shapes FIRST and route the honest
|
|
24816
|
+
// dependency read (get_impact & friends) with an explicit no-simulator
|
|
24817
|
+
// disclosure, mirroring `permission-set-deactivation-impact`.
|
|
24818
|
+
{
|
|
24819
|
+
intent: "record-type-removal-impact",
|
|
24820
|
+
plane: "vault",
|
|
24821
|
+
tools: ["sfi.resolve", "sfi.get_impact", "sfi.recordtype_availability"],
|
|
24822
|
+
liveRequired: false,
|
|
24823
|
+
needsResolve: true,
|
|
24824
|
+
reason: "No what_if simulator exists for removing a record type, so this routes honestly to the dependency read: get_impact surfaces the flows, layouts, validation rules, and profiles referencing the record type, and recordtype_availability shows who can see it today \u2014 the truthful stand-in for a removal blast radius, not a fabricated simulation.",
|
|
24825
|
+
patterns: [
|
|
24826
|
+
/\b(?:remov\w+|delet\w+|dropp?\w*|consolidat\w+|retir\w+)\b[^.?!]{0,50}\brecord\s+types?\b/,
|
|
24827
|
+
/\brecord\s+types?\b[^.?!]{0,50}\b(?:is|are|was|were|gets?|being)\s+(?:removed|deleted|dropped|consolidated|retired)\b/
|
|
24828
|
+
]
|
|
24829
|
+
},
|
|
24830
|
+
{
|
|
24831
|
+
intent: "layout-assignment-change-impact",
|
|
24832
|
+
plane: "vault",
|
|
24833
|
+
tools: ["sfi.resolve", "sfi.layout_assignments", "sfi.get_impact"],
|
|
24834
|
+
liveRequired: false,
|
|
24835
|
+
needsResolve: true,
|
|
24836
|
+
reason: "No what_if simulator exists for changing a page-layout assignment, so this routes honestly to the current state: layout_assignments shows the Profile \xD7 RecordType assignments the change would rewire, plus get_impact for the dependency surface \u2014 a lookup plus impact read, not a fabricated simulation.",
|
|
24837
|
+
patterns: [
|
|
24838
|
+
/\b(?:chang\w+|swapp?\w*|reassign\w*|switch\w*)\b[^.?!]{0,60}\blayout\s+assignments?\b/,
|
|
24839
|
+
/\bwhat\s+(?:if|happens|breaks)\b[^.?!]{0,80}\blayout\s+assignments?\b/
|
|
24840
|
+
]
|
|
24841
|
+
},
|
|
24842
|
+
{
|
|
24843
|
+
intent: "flow-variable-type-change-impact",
|
|
24844
|
+
plane: "vault",
|
|
24845
|
+
tools: ["sfi.resolve", "sfi.get_impact", "sfi.explain_flow"],
|
|
24846
|
+
liveRequired: false,
|
|
24847
|
+
needsResolve: true,
|
|
24848
|
+
reason: "No what_if simulator models changing a flow variable's type (e.g. single record to collection), so this routes honestly to the callers: get_impact surfaces the flows/processes invoking it (whose passed values a type change breaks), and explain_flow the variable's uses inside \u2014 an interface-impact read, not a fabricated simulation.",
|
|
24849
|
+
patterns: [
|
|
24850
|
+
/\b(?:input|output)\s+variable\b[^.?!]{0,60}\btype\b/,
|
|
24851
|
+
/\bvariable\s+type\b[^.?!]{0,60}\bflow\b/,
|
|
24852
|
+
/\bflow\b[^.?!]{0,60}\bvariable\s+type\b/
|
|
24853
|
+
]
|
|
24854
|
+
},
|
|
24855
|
+
{
|
|
24856
|
+
// R3 §5b — ENUMERATION-vs-ID gate: `cpq_rule_chain` and
|
|
24857
|
+
// `cpq_quote_template_breakdown` REQUIRE a specific canonical id, so an
|
|
24858
|
+
// org-wide ask ("map the whole CPQ rule chain", "what quote template does
|
|
24859
|
+
// the org use?") must route the ENUMERATION path — the id-free dependency
|
|
24860
|
+
// map plus list_components — never commit an id-required tool without an
|
|
24861
|
+
// id. Hoisted ABOVE the call-graph/"map the chain" family and the generic
|
|
24862
|
+
// `cpq` rule (first-match): only org-wide CPQ phrasings land here; a
|
|
24863
|
+
// named rule/template still gets the per-id chain tools.
|
|
24864
|
+
intent: "cpq-enumeration",
|
|
24865
|
+
plane: "vault",
|
|
24866
|
+
tools: ["sfi.cpq_dependency_map", "sfi.list_components"],
|
|
24867
|
+
liveRequired: false,
|
|
24868
|
+
needsResolve: false,
|
|
24869
|
+
reason: "Org-wide CPQ enumeration: cpq_dependency_map maps the whole rule/template surface with no id needed, and list_components enumerates the CPQ rules/templates themselves. The per-id tools (cpq_rule_chain, cpq_quote_template_breakdown) need a specific named rule or template \u2014 pick one from the enumeration first.",
|
|
24870
|
+
patterns: [
|
|
24871
|
+
/\bmap\s+the\b[^.?!]{0,40}\bcpq\b|\bcpq\b[^.?!]{0,40}\brule\s+chain\b[^.?!]{0,60}\b(?:evaluation\s+order|all|every|org-?wide|whole)\b/,
|
|
24872
|
+
/\b(?:what|which)\s+(?:cpq\s+)?quote\s+templates?\b[^.?!]{0,50}\b(?:do(?:es)?\s+(?:we|the\s+org)|org)\s*use\b/,
|
|
24873
|
+
/\b(?:all|every|list)\b[^.?!]{0,30}\b(?:price|product|discount)\s+rules?\b[^.?!]{0,50}\b(?:evaluation\s+order|and\s+their)\b/
|
|
24874
|
+
]
|
|
24875
|
+
},
|
|
24800
24876
|
{
|
|
24801
24877
|
intent: "async-chain-depth",
|
|
24802
24878
|
plane: "vault",
|
|
@@ -25159,7 +25235,9 @@ var init_intent_router = __esm({
|
|
|
25159
25235
|
needsResolve: false,
|
|
25160
25236
|
reason: "Login activity (LastLoginDate) is runtime state that lives only in the org.",
|
|
25161
25237
|
patterns: [
|
|
25162
|
-
|
|
25238
|
+
// `in(to)?` — "which users logged INTO the org last week" is the same
|
|
25239
|
+
// LastLoginDate read; the bare `in\b` missed the fused "into" (P4).
|
|
25240
|
+
/\b(who|users?|people)\b.*\b(log(ged)?\s?in(to)?|login|active|inactive|dormant)\b/,
|
|
25163
25241
|
/\b(inactive|dormant|stale|unused)\b.*\busers?\b/,
|
|
25164
25242
|
/\bhaven'?t\b.*\blog(ged)?\s?in\b/,
|
|
25165
25243
|
/\blast\s+login\b/,
|
|
@@ -25184,15 +25262,24 @@ var init_intent_router = __esm({
|
|
|
25184
25262
|
// near a field/value (so "empty queues" doesn't get swallowed here).
|
|
25185
25263
|
/\b(populated|filled)\b/,
|
|
25186
25264
|
// "field population for X" / "population rate" — the noun "population"
|
|
25187
|
-
// (vs the adjective "populated"). Battery gap.
|
|
25188
|
-
|
|
25265
|
+
// (vs the adjective "populated"). Battery gap. Guarded (P4): "the FLOW
|
|
25266
|
+
// THAT fires the 'General Population RR Group' step" uses "population"
|
|
25267
|
+
// inside a quoted flow-step name — a flow-search ask, not field fill.
|
|
25268
|
+
/^(?!.*\bflows?\s+that\b).*\b(field\s+)?population\b/,
|
|
25189
25269
|
// `fields?`/`values?` — `\b(field|value)\b` missed the PLURALS, so
|
|
25190
25270
|
// "which Account fields are empty" fell through to metadata-count
|
|
25191
25271
|
// (vault plane) instead of this hybrid live-data intent.
|
|
25192
25272
|
/\b(empty|blank|null)\b.*\b(fields?|values?)\b/,
|
|
25193
25273
|
/\b(fields?|values?)\b.*\b(empty|blank|null|populated|filled)\b/,
|
|
25194
25274
|
/\bhow\s+many\b.*\b(have|with|without)\b.*\b(field|value|filled|set)\b/,
|
|
25195
|
-
/\b(actually|really)\s+(populated|filled)\b
|
|
25275
|
+
/\b(actually|really)\s+(populated|filled)\b/,
|
|
25276
|
+
// Router-v2 P4 needs-live reachability: "fill rate", "completeness of
|
|
25277
|
+
// key fields", and "how many X have a blank Y" are all the same live
|
|
25278
|
+
// per-field population read. Queue/group emptiness and missing
|
|
25279
|
+
// DESCRIPTIONS stay vault (excluded).
|
|
25280
|
+
/\bfill\s+rates?\b/,
|
|
25281
|
+
/\bcompleteness\b.*\b(fields?|data|records?)\b|\b(fields?|data)\b.*\bcompleteness\b/,
|
|
25282
|
+
/\bhow\s+many\b(?!.*\b(queues?|groups?|descriptions?|help\s+text)\b)[^.?!]*\b(blank|null|missing)\b/
|
|
25196
25283
|
]
|
|
25197
25284
|
},
|
|
25198
25285
|
{
|
|
@@ -25228,7 +25315,11 @@ var init_intent_router = __esm({
|
|
|
25228
25315
|
/^(?!.*(__mdt\b|\bcustom\s+metadata\b|\bcustom\s+settings?\b)).*\b(show|give|sample|example)s?\b.*\b(records?|rows?)\b/,
|
|
25229
25316
|
/\b(show|give)\s+me\s+\d+\b/,
|
|
25230
25317
|
/\bsample\s+\d+\b/,
|
|
25231
|
-
/\b\d+\s+(sample|example)\s+\w+\b
|
|
25318
|
+
/\b\d+\s+(sample|example)\s+\w+\b/,
|
|
25319
|
+
// Router-v2 P4: a literal Salesforce record ID (15/18 chars, leading 0 +
|
|
25320
|
+
// keyprefix — "did lead 00Q5x000004abcd convert?") is a live row lookup;
|
|
25321
|
+
// the vault never holds record data.
|
|
25322
|
+
/\b0[0-9a-z]{2}[0-9a-z]{12}(?:[0-9a-z]{3})?\b/
|
|
25232
25323
|
]
|
|
25233
25324
|
},
|
|
25234
25325
|
{
|
|
@@ -25290,7 +25381,10 @@ var init_intent_router = __esm({
|
|
|
25290
25381
|
// components (P0a). Keyed on the word "users" alone — metadata-count
|
|
25291
25382
|
// questions ("how many profiles / layouts / … , and which is assigned to
|
|
25292
25383
|
// each profile") never say "users", so they still fire here.
|
|
25293
|
-
|
|
25384
|
+
// The second lookahead (P4): "how many RECORDS per record type" is a
|
|
25385
|
+
// live GROUP BY over record data — the metadata noun ("record type") is
|
|
25386
|
+
// the grouping key, not the thing being counted.
|
|
25387
|
+
/\bhow\s+many\b(?!.*\busers?\b)(?!.*\brecords?\s+(?:per|by|for\s+each)\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/,
|
|
25294
25388
|
/\bhow\s+many\b.*\blayouts?\b.*\b(per|for\s+each|by)\b.*\bprofiles?\b/,
|
|
25295
25389
|
/\blayouts?\b.*\b(per|for\s+each|by)\b.*\bprofiles?\b/,
|
|
25296
25390
|
// fields, but NOT field usage/population (those are unused-fields / field-population)
|
|
@@ -25312,7 +25406,16 @@ var init_intent_router = __esm({
|
|
|
25312
25406
|
/\b(by|per)\s+(status|stage|type|industry|record\s+type)\b/,
|
|
25313
25407
|
/\bhow\s+many\b.*\b(in|with)\s+(each|every)\b/,
|
|
25314
25408
|
// "how many Applications with status Submitted" — filtered live COUNT, not vault metadata-count (B21).
|
|
25315
|
-
/\bhow\s+many\b.*\bwith\s+(status|stage|type|record\s+type)\b
|
|
25409
|
+
/\bhow\s+many\b.*\bwith\s+(status|stage|type|record\s+type)\b/,
|
|
25410
|
+
// Router-v2 P4: imperative "count X grouped by Y" / "count of X by Y" /
|
|
25411
|
+
// "count X records by Y" — the same live GROUP BY, phrased without
|
|
25412
|
+
// "how many".
|
|
25413
|
+
/\bcount\b[^.?!]{0,60}\bgrouped?\s+by\b/,
|
|
25414
|
+
/\bcount\s+of\b[^.?!]{0,60}\bby\b/,
|
|
25415
|
+
/\bcount\b[^.?!]{0,60}\brecords?\s+by\b/,
|
|
25416
|
+
// "who's in the ADA Team Queue" — queue/public-group membership is a
|
|
25417
|
+
// live GroupMember read; the vault holds the queue definition only.
|
|
25418
|
+
/\bwho(?:'?s|\s+is|\s+are)?\b[^.?!]{0,40}\bin\s+the\b[^.?!]{0,50}\b(queues?|public\s+groups?)\b/
|
|
25316
25419
|
]
|
|
25317
25420
|
},
|
|
25318
25421
|
{
|
|
@@ -25325,7 +25428,10 @@ var init_intent_router = __esm({
|
|
|
25325
25428
|
patterns: [
|
|
25326
25429
|
/\b(average|avg|mean|minimum|min|maximum|max|sum|total)\b.*\b(field|value|amount|revenue|score)\b/,
|
|
25327
25430
|
/\bwhat\s+is\s+the\b.*\b(average|avg|min|max|sum)\b/,
|
|
25328
|
-
/\bhow\s+(big|large|small)\b.*\b(on\s+average|average)\b
|
|
25431
|
+
/\bhow\s+(big|large|small)\b.*\b(on\s+average|average)\b/,
|
|
25432
|
+
// Router-v2 P4: "average number of X per Y" — a live AVG/GROUP BY ask
|
|
25433
|
+
// that named no field noun.
|
|
25434
|
+
/\baverage\s+number\s+of\b/
|
|
25329
25435
|
]
|
|
25330
25436
|
},
|
|
25331
25437
|
{
|
|
@@ -25336,8 +25442,12 @@ var init_intent_router = __esm({
|
|
|
25336
25442
|
needsResolve: false,
|
|
25337
25443
|
reason: "Duplicate detection needs live GROUP BY + HAVING on field values.",
|
|
25338
25444
|
patterns: [
|
|
25339
|
-
|
|
25340
|
-
|
|
25445
|
+
// `duplicat\w*` — "most DUPLICATED email domain" is the same live GROUP
|
|
25446
|
+
// BY + HAVING read; the bare `duplicate` missed the participle (P4).
|
|
25447
|
+
/\bduplicat\w*\b.*\b(records?|values?|emails?|domains?|contacts?|accounts?|fields?|rows?)\b/,
|
|
25448
|
+
// Guarded: "which FIELDS are duplicated" is a SCHEMA-redundancy ask
|
|
25449
|
+
// (vault), not live record duplicates — exclude the fields subject.
|
|
25450
|
+
/^(?!.*\bfields?\b[^.?!]{0,40}\bduplicat)\b.*\b(records?|values?|emails?|domains?|contacts?|accounts?)\b.*\bduplicat\w*\b/,
|
|
25341
25451
|
/\bsame\b.*\b(email|phone|name|value)\b/
|
|
25342
25452
|
]
|
|
25343
25453
|
},
|
|
@@ -25360,7 +25470,14 @@ var init_intent_router = __esm({
|
|
|
25360
25470
|
// vault cannot answer it at all (eval family D).
|
|
25361
25471
|
/\b(which|what)\b[^.?!]{0,40}\bobjects?\b[^.?!]{0,50}\bempty\b/,
|
|
25362
25472
|
/\bempty\b[^.?!]{0,20}\b(custom\s+)?objects?\b/,
|
|
25363
|
-
/\bobjects?\b[^.?!]{0,40}\b(no|zero|barely\s+any|hardly\s+any)\s+(records?|rows?|data)\b
|
|
25473
|
+
/\bobjects?\b[^.?!]{0,40}\b(no|zero|barely\s+any|hardly\s+any)\s+(records?|rows?|data)\b/,
|
|
25474
|
+
// Router-v2 P4: growth and per-object size asks are the same live
|
|
25475
|
+
// per-object COUNT read ("which objects have grown the fastest",
|
|
25476
|
+
// "inventory of every custom object and roughly how big each is").
|
|
25477
|
+
/\bobjects?\b[^.?!]{0,60}\b(grown|growth|growing)\b/,
|
|
25478
|
+
/\b(grown|growth|growing)\b[^.?!]{0,60}\bobjects?\b/,
|
|
25479
|
+
/\bobjects?\b[^.?!]{0,60}\bhow\s+big\b/,
|
|
25480
|
+
/\bhow\s+big\b[^.?!]{0,60}\bobjects?\b/
|
|
25364
25481
|
]
|
|
25365
25482
|
},
|
|
25366
25483
|
{
|
|
@@ -25371,8 +25488,10 @@ var init_intent_router = __esm({
|
|
|
25371
25488
|
needsResolve: false,
|
|
25372
25489
|
reason: "A record COUNT is live data; the offline vault holds metadata only.",
|
|
25373
25490
|
patterns: [
|
|
25374
|
-
/\bhow\s+many\b.*\b(records?|rows?|accounts?|contacts?|opportunit|leads?|cases?)\b/,
|
|
25491
|
+
/\bhow\s+many\b.*\b(records?|rows?|accounts?|contacts?|opportunit(?:y|ies)?|leads?|cases?)\b/,
|
|
25375
25492
|
/\b(count|number)\s+of\b.*\b(records?|rows?)\b/,
|
|
25493
|
+
// Router-v2 P4: imperative "count <X> records" (no "of", no "how many").
|
|
25494
|
+
/\bcount\b[^.?!]{0,60}\brecords?\b/,
|
|
25376
25495
|
/\bhow\s+many\b.*\bin\s+(the\s+)?(org|production|prod)\b/,
|
|
25377
25496
|
/\blive\s+count\b/,
|
|
25378
25497
|
// A TEMPORAL qualifier cues live data regardless of the noun — "how
|
|
@@ -25382,7 +25501,7 @@ var init_intent_router = __esm({
|
|
|
25382
25501
|
// win: the metadata-count rule sits earlier. fired/ran/logged forms
|
|
25383
25502
|
// are excluded so automation/login activity asks don't collapse into
|
|
25384
25503
|
// a bare record count.
|
|
25385
|
-
/\bhow\s+many\b(?!.*\b(fired|ran|executed|triggered|logged)\b).*\b(right\s+now|currently|at\s+the\s+moment|as\s+of\s+(now|today)|today)\b/,
|
|
25504
|
+
/\bhow\s+many\b(?!.*\b(fired|ran|executed|triggered|logged)\b).*\b(right\s+now|currently|at\s+the\s+moment|as\s+of\s+(now|today)|today|this\s+(term|semester|quarter))\b/,
|
|
25386
25505
|
/\b(count|number)\s+of\b.*\b(right\s+now|currently|at\s+the\s+moment|today)\b/,
|
|
25387
25506
|
// Named sObject totals without the word "records" — "how many Opportunities
|
|
25388
25507
|
// are there" (TEST-SANDBOX-ROUTER-first-user). Exclude non-SF platforms
|
|
@@ -25415,7 +25534,53 @@ var init_intent_router = __esm({
|
|
|
25415
25534
|
patterns: [
|
|
25416
25535
|
/\b(recent(ly)?|last\s+\d+\s+days?|this\s+week|past\s+week)\b.*\b(created|modified|updated|changed|added)\b/,
|
|
25417
25536
|
/\b(created|modified|updated|new)\b.*\b(recent(ly)?|last\s+\d+\s+days?|this\s+week)\b/,
|
|
25418
|
-
/\bwhat\s+(was|were)\b.*\b(created|modified|updated|changed)\b.*\b(recent|last)\b
|
|
25537
|
+
/\bwhat\s+(was|were)\b.*\b(created|modified|updated|changed)\b.*\b(recent|last)\b/,
|
|
25538
|
+
// Router-v2 P4: "who's been making the most changes lately" / "busiest
|
|
25539
|
+
// user by record edits" — the same recent-modified read, cut by editor.
|
|
25540
|
+
/\bwho\b[^.?!]{0,40}\b(making|made)\b[^.?!]{0,30}\bchanges\b/,
|
|
25541
|
+
/\bbusiest\s+users?\b/
|
|
25542
|
+
]
|
|
25543
|
+
},
|
|
25544
|
+
{
|
|
25545
|
+
// Live picklist VALUE USAGE — "which Case.Status values are never used",
|
|
25546
|
+
// "distribution of Status values", "most common Resolution_Code__c".
|
|
25547
|
+
// Distinct from picklist-values (vault, DECLARED value set): usage /
|
|
25548
|
+
// distribution / most-common language means live GROUP BY counts per
|
|
25549
|
+
// value (router-v2 P4 needs-live reachability). Placed in the live block
|
|
25550
|
+
// so it wins over the vault picklist-values rule further down.
|
|
25551
|
+
intent: "picklist-usage",
|
|
25552
|
+
plane: "live",
|
|
25553
|
+
tools: ["sfi.live_picklist_usage"],
|
|
25554
|
+
liveRequired: true,
|
|
25555
|
+
needsResolve: false,
|
|
25556
|
+
reason: "How picklist/field VALUES are actually used across records (counts per value, never-used values) is live GROUP BY data \u2014 the vault only declares the value set.",
|
|
25557
|
+
// NOTE the distance windows allow an interior dot (`Case.Status`) — a
|
|
25558
|
+
// dotted field reference must not break the sentence-bounded window.
|
|
25559
|
+
patterns: [
|
|
25560
|
+
/\bpicklist\b(?:[^.?!]|\.(?=\w)){0,80}\b(never\s+used|actually\s+used|usage|distribut\w+|frequen\w+)\b/,
|
|
25561
|
+
/\bvalues?\b(?:[^.?!]|\.(?=\w)){0,60}\b(never|rarely)\s+used\b/,
|
|
25562
|
+
/\b(distribution|breakdown)\s+of\b(?:[^.?!]|\.(?=\w)){0,60}\bvalues?\b/,
|
|
25563
|
+
/\bhow\s+are\b(?:[^.?!]|\.(?=\w)){0,40}\bvalues?\b(?:[^.?!]|\.(?=\w)){0,40}\bdistributed\b/,
|
|
25564
|
+
/\b(most|least)\s+common\b(?:[^.?!]|\.(?=\w)){0,50}\b(value|status|code|__c)\b/,
|
|
25565
|
+
/\b(?:whats?|what\s+is)\s+the\s+most\s+common\b/
|
|
25566
|
+
]
|
|
25567
|
+
},
|
|
25568
|
+
{
|
|
25569
|
+
// Live automation execution — "did the flow FIRE yesterday?" is a runtime
|
|
25570
|
+
// question about actual executions (FlowInterview / job traces), not the
|
|
25571
|
+
// automation catalog (router-v2 P4). Temporal anchor required so the
|
|
25572
|
+
// metadata asks ("what fires on save") never land here.
|
|
25573
|
+
intent: "automation-fired",
|
|
25574
|
+
plane: "live",
|
|
25575
|
+
tools: ["sfi.live_automation_fired"],
|
|
25576
|
+
liveRequired: true,
|
|
25577
|
+
needsResolve: false,
|
|
25578
|
+
reason: "Whether an automation actually RAN (and when) is runtime execution state \u2014 live_automation_fired reads it; the vault only holds the automation definitions.",
|
|
25579
|
+
patterns: [
|
|
25580
|
+
/\b(flow|trigger|automation|process|it)\b[^.?!]{0,60}\bfired\b[^.?!]{0,50}\b(yesterday|today|last\s+\w+|this\s+\w+|recently)\b/,
|
|
25581
|
+
/\bfired\b[^.?!]{0,30}\b(yesterday|today|last\s+night|recently)\b/,
|
|
25582
|
+
/\bdid\b[^.?!]{0,60}\b(flow|trigger|automation)\b[^.?!]{0,50}\b(fire|run|execute|actually\s+fire)\b/,
|
|
25583
|
+
/\b(flow|trigger|automation)\b[^.?!]{0,40}\b(actually\s+(ran|fired|executed))\b/
|
|
25419
25584
|
]
|
|
25420
25585
|
},
|
|
25421
25586
|
{
|
|
@@ -25429,7 +25594,11 @@ var init_intent_router = __esm({
|
|
|
25429
25594
|
/\b(org|org'?s?)\b.*\b(on\s+fire|unhealthy|health|healthy)\b/,
|
|
25430
25595
|
/\b(failed|faulted|stuck)\b.*\b(jobs?|batch|async|apex\s+job)\b/,
|
|
25431
25596
|
/\bpaused\b.*\bflows?\b/i,
|
|
25432
|
-
/\bis\s+my\s+org\b.*\b(ok|fine|broken|failing)\b
|
|
25597
|
+
/\bis\s+my\s+org\b.*\b(ok|fine|broken|failing)\b/,
|
|
25598
|
+
// Router-v2 P4: "give me a health snapshot of the org" — the noun order
|
|
25599
|
+
// put "health" before "org", which the first pattern missed, and the
|
|
25600
|
+
// word "snapshot" was stolen by the vault snapshot-diff rule.
|
|
25601
|
+
/\bhealth\s+snapshot\b/
|
|
25433
25602
|
]
|
|
25434
25603
|
},
|
|
25435
25604
|
{
|
|
@@ -25525,7 +25694,13 @@ var init_intent_router = __esm({
|
|
|
25525
25694
|
/\b(useless|unused|stale|dead)\b.*\breports?\b/,
|
|
25526
25695
|
/\b(dashboards?)\b.*\b(unused|stale|broken|refresh)\b/,
|
|
25527
25696
|
/\breports?\b.*\b(not\s+run|haven'?t\s+been\s+run)\b/,
|
|
25528
|
-
/\breports?\b.*\b(last\s+year|in\s+the\s+last)\b
|
|
25697
|
+
/\breports?\b.*\b(last\s+year|in\s+the\s+last)\b/,
|
|
25698
|
+
// "which reports are ACTUALLY USED" — run-history is live LastRunDate
|
|
25699
|
+
// (router-v2 P4; previously a funnel plane near-tie that BLOCKED).
|
|
25700
|
+
/\breports?\b[^.?!]{0,40}\bactually\s+used\b/,
|
|
25701
|
+
// Router-v2 P4: "reports/dashboards nobody looks at / no one uses" —
|
|
25702
|
+
// the colloquial unused-ask without the word "unused".
|
|
25703
|
+
/\b(reports?|dashboards?)\b[^.?!]{0,60}\b(nobody|no\s+one)\b[^.?!]{0,40}\b(looks?|uses?|runs?|view\w*|open\w*)\b/
|
|
25529
25704
|
]
|
|
25530
25705
|
},
|
|
25531
25706
|
{
|
|
@@ -25544,7 +25719,7 @@ var init_intent_router = __esm({
|
|
|
25544
25719
|
/\b(who\s+can|access\s+to)\b.*\bfolders?\b/,
|
|
25545
25720
|
// Folder-gated artifacts asked about by ACCESS without the word "folder":
|
|
25546
25721
|
// "who can see/access/view this report/dashboard/document".
|
|
25547
|
-
/\bwho\s+can\s+(access|see|view|open)\b.*\b(reports?|dashboards?|documents?)\b/,
|
|
25722
|
+
/\bwho\s+can\s+(access|see|view|open|run)\b.*\b(reports?|dashboards?|documents?)\b/,
|
|
25548
25723
|
/\b(access|visib(le|ility))\b.*\b(reports?|dashboards?|documents?)\s+folders?\b/,
|
|
25549
25724
|
/\b(dashboards?|reports?)\b.*\bshared\b.*\b(with|to)\b/
|
|
25550
25725
|
]
|
|
@@ -26003,6 +26178,35 @@ var init_intent_router = __esm({
|
|
|
26003
26178
|
/\bhighlights?\b.*\b(vs|versus|full)\b.*\blayout\b/
|
|
26004
26179
|
]
|
|
26005
26180
|
},
|
|
26181
|
+
{
|
|
26182
|
+
// COMPONENT-TYPE CONFUSION (router-v2 R2, type-confusion trap family):
|
|
26183
|
+
// "is <Name> a flow or a trigger?", "is that an Apex class or a flow?",
|
|
26184
|
+
// "the name has 'Trigger' in it but is it actually a test class?", "what
|
|
26185
|
+
// type is that?". The user is unsure WHAT KIND of component a name is —
|
|
26186
|
+
// resolve answers the type question, and the two explainers cover
|
|
26187
|
+
// whichever family it lands on (the stage-5 type guard swaps the
|
|
26188
|
+
// incompatible one once the entity resolves). MUST precede
|
|
26189
|
+
// automation-on-object, whose "triggers … on <object>" pattern otherwise
|
|
26190
|
+
// steals "…an actual trigger on Contact?".
|
|
26191
|
+
intent: "component-type",
|
|
26192
|
+
plane: "vault",
|
|
26193
|
+
tools: ["sfi.resolve", "sfi.explain_flow", "sfi.explain_apex_method"],
|
|
26194
|
+
liveRequired: false,
|
|
26195
|
+
needsResolve: true,
|
|
26196
|
+
reason: "The question asks WHICH KIND of component a name is (flow vs trigger vs Apex class vs test class). Resolve the name to its actual type first; then explain it with the family-appropriate tool \u2014 the resolver output, not the name, decides which.",
|
|
26197
|
+
patterns: [
|
|
26198
|
+
// Two component-family nouns joined by "or" inside one clause:
|
|
26199
|
+
// "is <name>/that/it (actually) a flow … or (an actual) trigger".
|
|
26200
|
+
/\bis\s+(?:that|this|it|[a-z0-9_]+)\s+(?:actually\s+)?(?:an?\s+)?(?:apex(?:\s+(?:class|test\s+class|logging))?|flow|trigger|test\s+class)\b[^.?!]{0,40}\bor\s+(?:an?\s+)?(?:actual(?:ly)?\s+a?\s*)?(?:apex(?:\s+class)?|flow|trigger|test\s+class|something\s+else)/,
|
|
26201
|
+
// Name-vs-nature: "…but is it actually a test class?"
|
|
26202
|
+
/\bbut\s+is\s+(?:it|that)\s+(?:actually\s+)?an?\s+(?:test\s+class|trigger|flow|apex\s+class)\b/,
|
|
26203
|
+
// "what type is that / what type of component is <name>" — `that/this`
|
|
26204
|
+
// only (not `it`), and never when the question names a __c FIELD:
|
|
26205
|
+
// "what type is it?" about CON_...__c asks the field's DATA type, not
|
|
26206
|
+
// the component family (the cov3-029 negative).
|
|
26207
|
+
/^(?!.*__c\b)(?=[\s\S]*\bwhat\s+type\s+(?:is\s+(?:that|this)|of\s+component\s+is)\b)/
|
|
26208
|
+
]
|
|
26209
|
+
},
|
|
26006
26210
|
{
|
|
26007
26211
|
intent: "field-access",
|
|
26008
26212
|
plane: "vault",
|
|
@@ -26116,6 +26320,8 @@ var init_intent_router = __esm({
|
|
|
26116
26320
|
// Admin-level / least-privilege / security-gap phrasings (baseline-300).
|
|
26117
26321
|
/\b(which|what)\s+users?\b.*\badmin\b/,
|
|
26118
26322
|
/\busers?\b.*\badmin[-\s]level\b/,
|
|
26323
|
+
// "do we have too many admins" (router-v2 P4).
|
|
26324
|
+
/\btoo\s+many\s+admins?\b/,
|
|
26119
26325
|
/\bleast\s+privilege\b/,
|
|
26120
26326
|
/\bsecurity\s+gaps?\b.*\b(profile|permission)/,
|
|
26121
26327
|
/\bpermission\s+sets?\b.*\binstead\s+of\b.*\bprofiles?\b/
|
|
@@ -26251,6 +26457,35 @@ var init_intent_router = __esm({
|
|
|
26251
26457
|
/\busers?\b[^.?!]{0,30}\bassigned\b[^.?!]{0,30}\bpermission\s+sets?\b/
|
|
26252
26458
|
]
|
|
26253
26459
|
},
|
|
26460
|
+
{
|
|
26461
|
+
// HONEST GAP (round-2, q1948/q1559): "which PSG grants the X custom
|
|
26462
|
+
// permission / the Y role" is a PermissionSetGroup COMPOSITION lookup —
|
|
26463
|
+
// PSG membership/composition is not modeled (same boundary as the two PSG
|
|
26464
|
+
// gaps above), and roles are not granted by PSGs at all, so substituting
|
|
26465
|
+
// object_access_audit / effective_permissions would be confidently wrong.
|
|
26466
|
+
// Requires the GROUP noun: "which permission SETS grant edit on X" (no
|
|
26467
|
+
// `group`) stays on the real field/effective-permissions routes.
|
|
26468
|
+
intent: "permset-group-grants",
|
|
26469
|
+
plane: "vault",
|
|
26470
|
+
tools: [],
|
|
26471
|
+
liveRequired: false,
|
|
26472
|
+
needsResolve: false,
|
|
26473
|
+
reason: "Which permission set group grants a permission (PermissionSetGroup composition) is not modeled \u2014 capability gap.",
|
|
26474
|
+
gap: {
|
|
26475
|
+
category: "permset-group-grants",
|
|
26476
|
+
note: 'PermissionSetGroup composition/membership modeling is not built yet, so "which PSG grants X" cannot be answered. Do not substitute effective_permissions or object_access_audit \u2014 they cover permission sets and profiles, not PSG composition. (If X is a custom permission, sfi.resolve + the permission-set reverse lookup covers PERMISSION SETS granting it.)'
|
|
26477
|
+
},
|
|
26478
|
+
patterns: [
|
|
26479
|
+
// Tempered verb→grants gap: "which permission set groups REFERENCE a
|
|
26480
|
+
// permission set that grants X" is a graph-edge read (answerable) — a
|
|
26481
|
+
// reference/contain/include verb between the PSG noun and `grants`
|
|
26482
|
+
// breaks the match, so only the PSG-as-grantor ask gaps. `not` breaks it
|
|
26483
|
+
// too: "why would a PSG NOT grant expected access" is a troubleshooting
|
|
26484
|
+
// ask that stays on its pre-round-2 route (sweep-parity pin).
|
|
26485
|
+
/\b(?:which|what)\s+(?:permission\s+set\s+groups?|psgs?)\b(?:(?!\b(?:references?|referencing|contains?|containing|includes?|including|not)\b)[^.?!]){0,60}\bgrants?\b/,
|
|
26486
|
+
/\b(?:permission\s+set\s+groups?|psgs?)\b(?:(?!\b(?:references?|referencing|contains?|containing|includes?|including|not)\b)[^.?!]){0,40}\bgrants?\b[^.?!]{0,60}\b(?:custom\s+permission|permission|role|access)\b/
|
|
26487
|
+
]
|
|
26488
|
+
},
|
|
26254
26489
|
{
|
|
26255
26490
|
intent: "empty-queues-groups",
|
|
26256
26491
|
plane: "vault",
|
|
@@ -26849,6 +27084,9 @@ var init_intent_router = __esm({
|
|
|
26849
27084
|
patterns: [
|
|
26850
27085
|
/\b(which|what)\s+flows?\b.*\b(references?|uses?|mentions?|touch(es)?|calls?)\b/,
|
|
26851
27086
|
/\bflows?\b.*\b(that|which)\b.*\b(references?|uses?)\b/,
|
|
27087
|
+
// Router-v2 P4: "pull the flow that FIRES/SENDS/CREATES the '<step
|
|
27088
|
+
// name>' step" — find-a-flow-by-what-it-does is a Flow-XML grep.
|
|
27089
|
+
/\bflows?\s+that\s+(fires?|sends?|creates?|assigns?|updates?|contains?|does)\b/,
|
|
26852
27090
|
/\b(sync|stamping|affiliate|marketo|applicant|partner|budget)\b.*\bflows?\b/,
|
|
26853
27091
|
/\bflows?\b.*\b(sync|stamping|affiliate|marketo|applicant|partner|budget)\b/,
|
|
26854
27092
|
/\bflows?\b.*\b(send|sends|email)\b/,
|
|
@@ -26970,7 +27208,9 @@ var init_intent_router = __esm({
|
|
|
26970
27208
|
// runs bulk") or test coverage ("which test class covers Y … the bulk
|
|
26971
27209
|
// case"), those earlier rules win by order — this guard keeps the bare
|
|
26972
27210
|
// word from firing even when their patterns miss a phrasing.
|
|
26973
|
-
|
|
27211
|
+
// …and "WHICH CLASS HANDLES the Boomi integration for BULK loads" is a
|
|
27212
|
+
// find-the-class source grep (apex-search), not a limit report (P4).
|
|
27213
|
+
/^(?!.*\b(?:what\s+(?:fires|runs|happens)|test\s+class|(?:which|what)\s+(?:apex\s+)?class(?:es)?\s+(?:handles?|processes?|implements?))\b).*\b(bulk|bulkif|unbounded)\b/,
|
|
26974
27214
|
/\bcpu\s+time\b/,
|
|
26975
27215
|
/\bheap\s+size\b/,
|
|
26976
27216
|
/\bmost\s+dml\b/,
|
|
@@ -27301,10 +27541,26 @@ var init_intent_router = __esm({
|
|
|
27301
27541
|
liveRequired: false,
|
|
27302
27542
|
needsResolve: false,
|
|
27303
27543
|
reason: "Text grep over Apex source + usage edges.",
|
|
27544
|
+
// A dotted CODE LITERAL in the question ("System.debug") is the grep text —
|
|
27545
|
+
// bind it so the routed search_apex_source call is executable as-is.
|
|
27546
|
+
suggestArgs: (q2) => {
|
|
27547
|
+
const literal = q2.match(/\b[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*\b/)?.[0];
|
|
27548
|
+
return literal !== void 0 ? { query: literal } : void 0;
|
|
27549
|
+
},
|
|
27304
27550
|
patterns: [
|
|
27305
27551
|
/\bfind\b.*\b(class|apex|code)\b.*\b(mentions?|references?|uses?|calls?|reads?|writes?|with)\b/,
|
|
27306
27552
|
/\b(which|what)\s+(classes?|apex)\b.*\b(mentions?|references?|uses?|touch(es)?|reads?|writes?|calls?|invokes?)\b/,
|
|
27307
27553
|
/\bsearch\b.*\b(apex|code)\b/,
|
|
27554
|
+
// Router-v2 P4 (q522 family): a CODE-LITERAL search — "does anything
|
|
27555
|
+
// call System.debug", "leftover System.debug statements" — is a source
|
|
27556
|
+
// grep, never the runtime-audit-trail fallback ("debug logs" in the
|
|
27557
|
+
// prose must not swallow it; this rule sits earlier, so the literal
|
|
27558
|
+
// wins).
|
|
27559
|
+
/\bsystem\.debug\b/,
|
|
27560
|
+
/\b(?:calls?|calling|invokes?|references?)\b[^.?!]{0,40}\b[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*\s*\(/,
|
|
27561
|
+
// "which apex class HANDLES the Boomi integration" — find-the-class-by-
|
|
27562
|
+
// what-it-does is a source/usage grep, not a code-quality report.
|
|
27563
|
+
/\b(?:which|what)\s+(?:apex\s+)?class(?:es)?\b[^.?!]{0,60}\b(?:handles?|processes?|implements?|integrates?|owns?)\b/,
|
|
27308
27564
|
// "which classes make HTTP callouts and what endpoints do they hit" —
|
|
27309
27565
|
// HTTP callouts live in Apex SOURCE, so the answer is a source grep,
|
|
27310
27566
|
// not the outbound endpoint catalog and never field lineage (eval
|
|
@@ -28035,7 +28291,7 @@ var init_intent_router = __esm({
|
|
|
28035
28291
|
patterns: [
|
|
28036
28292
|
/\bfield\s*360\b/,
|
|
28037
28293
|
/\b360\b[^.?!]{0,20}\b(on|of|for)\b[^.?!]{0,20}(?:[a-z][a-z0-9_]*\.[a-z][a-z0-9_]*|[a-z][a-z0-9_]*__c)\b/,
|
|
28038
|
-
new RegExp(`\\b(
|
|
28294
|
+
new RegExp(`\\b(full\\s+(?:picture|profile)|everything\\s+that\\s+(?:touches|uses))\\b[^.?!]{0,30}\\b${NAMED_FIELD_ID}\\b`)
|
|
28039
28295
|
]
|
|
28040
28296
|
},
|
|
28041
28297
|
{
|
|
@@ -28604,6 +28860,12 @@ var init_intent_router = __esm({
|
|
|
28604
28860
|
};
|
|
28605
28861
|
return labels[intent] ?? `the ${intent.replaceAll("-", " ")} analysis`;
|
|
28606
28862
|
};
|
|
28863
|
+
COMPLEMENTARY_ALTERNATIVE_PAIRS = /* @__PURE__ */ new Set([
|
|
28864
|
+
"impact-analysis|safe-to-delete",
|
|
28865
|
+
"impact-analysis|what-if-field",
|
|
28866
|
+
"who-can-access-object|object-access"
|
|
28867
|
+
]);
|
|
28868
|
+
NAMES_COMPONENT_FOR_LAST_MODIFIED = /[a-z0-9]_[a-z0-9]|__(?:c|mdt|e|x|b|kav)\b|\w+\.\w+|\b(?:flow|flows|class|classes|trigger|triggers|validation\s+rule|permission\s+set|profile|layout|object|field|component|page|dashboard|report)\b/;
|
|
28607
28869
|
classifyQuestion = (question) => {
|
|
28608
28870
|
const q2 = routeText(question);
|
|
28609
28871
|
if (q2.length === 0) {
|
|
@@ -28626,12 +28888,32 @@ var init_intent_router = __esm({
|
|
|
28626
28888
|
const matches = RULES.filter((rule) => rule.patterns.some((p2) => p2.test(q2)));
|
|
28627
28889
|
const first = matches[0];
|
|
28628
28890
|
if (first !== void 0) {
|
|
28629
|
-
|
|
28630
|
-
|
|
28631
|
-
|
|
28891
|
+
let primary = routeFromRule(question, q2, first);
|
|
28892
|
+
let alternatives = [...semanticAlternatives(q2, primary.intent)].filter((alternative, i2, all) => alternative.intent !== primary.intent && all.findIndex((candidate) => candidate.intent === alternative.intent) === i2).slice(0, 3);
|
|
28893
|
+
if (primary.intent === "runtime-audit-trail" && alternatives.some((alternative) => alternative.intent === "last-modified") && NAMES_COMPONENT_FOR_LAST_MODIFIED.test(q2)) {
|
|
28894
|
+
const lastModifiedRule = RULES.find((rule) => rule.intent === "last-modified");
|
|
28895
|
+
if (lastModifiedRule !== void 0) {
|
|
28896
|
+
primary = routeFromRule(question, q2, lastModifiedRule);
|
|
28897
|
+
alternatives = [];
|
|
28898
|
+
}
|
|
28899
|
+
}
|
|
28900
|
+
const complementary = alternatives.filter((alternative) => COMPLEMENTARY_ALTERNATIVE_PAIRS.has(`${primary.intent}|${alternative.intent}`));
|
|
28901
|
+
const blocking = alternatives.filter((alternative) => !complementary.includes(alternative));
|
|
28902
|
+
const stackedTools = complementary.length > 0 ? [
|
|
28903
|
+
...primary.tools,
|
|
28904
|
+
...complementary.flatMap((alternative) => alternative.tools).filter((tool) => !primary.tools.includes(tool))
|
|
28905
|
+
] : primary.tools;
|
|
28906
|
+
if (complementary.length > 0) {
|
|
28907
|
+
primary = {
|
|
28908
|
+
...primary,
|
|
28909
|
+
tools: stackedTools,
|
|
28910
|
+
reason: `${primary.reason} The question also admits a complementary reading (${complementary.map((a2) => intentLabel(a2.intent)).join("; ")}) \u2014 its tools are stacked after the primary so either or both can run; no clarification needed.`
|
|
28911
|
+
};
|
|
28912
|
+
}
|
|
28913
|
+
const clarification = blocking.length > 0 ? {
|
|
28632
28914
|
required: true,
|
|
28633
|
-
question: `Which result do you want first: ${intentLabel(primary.intent)}, or ${
|
|
28634
|
-
options: [primary.intent, ...
|
|
28915
|
+
question: `Which result do you want first: ${intentLabel(primary.intent)}, or ${blocking.map((a2) => intentLabel(a2.intent)).join(", ")}?`,
|
|
28916
|
+
options: [primary.intent, ...blocking.map((a2) => a2.intent)],
|
|
28635
28917
|
fallback: {
|
|
28636
28918
|
intent: primary.intent,
|
|
28637
28919
|
warning: `Clarification was unavailable. Provisional route: ${primary.intent}. Show the user the alternatives before executing it.`
|
|
@@ -28639,7 +28921,7 @@ var init_intent_router = __esm({
|
|
|
28639
28921
|
} : null;
|
|
28640
28922
|
return {
|
|
28641
28923
|
...primary,
|
|
28642
|
-
confidence:
|
|
28924
|
+
confidence: blocking.length > 0 ? "low" : complementary.length > 0 ? "medium" : "high",
|
|
28643
28925
|
alternatives,
|
|
28644
28926
|
clarification,
|
|
28645
28927
|
plan: [{
|
|
@@ -54928,6 +55210,7 @@ var init_resolve2 = __esm({
|
|
|
54928
55210
|
parentApiName: a2.parentApiName,
|
|
54929
55211
|
score: a2.score,
|
|
54930
55212
|
base: a2.base,
|
|
55213
|
+
nameCoverage: 1,
|
|
54931
55214
|
matchKind: "exact",
|
|
54932
55215
|
evidence: a2.evidence
|
|
54933
55216
|
})),
|
|
@@ -55102,11 +55385,2169 @@ var init_retrieve_blindspot_report = __esm({
|
|
|
55102
55385
|
}
|
|
55103
55386
|
});
|
|
55104
55387
|
|
|
55388
|
+
// ../mcp/dist/src/context-resolution.js
|
|
55389
|
+
var PRONOUN_ANCHORS, REPARAM_ANCHORS, detectPronounAnchor, detectReparamAnchor, ANAPHOR_ONLY, isAnaphorOnly, ORDINAL_INDEX, SELECTION_SHAPE, NON_DESCRIPTOR_TOKENS, detectClarificationSelection, GAP_FOLLOW_UP_SHAPES, detectGapShapedFollowUp, COMPONENT_ID_SHAPE, validatePreviousContext, CONTINUATION_TOOL_TYPES, continuationToolCompatible, REPARAM_FILLER, extractReparamTarget;
|
|
55390
|
+
var init_context_resolution = __esm({
|
|
55391
|
+
"../mcp/dist/src/context-resolution.js"() {
|
|
55392
|
+
"use strict";
|
|
55393
|
+
PRONOUN_ANCHORS = [
|
|
55394
|
+
/\bthat\s+one\b/i,
|
|
55395
|
+
/\bthis\s+one\b/i,
|
|
55396
|
+
/\bthe\s+same\b/i,
|
|
55397
|
+
/\bthe\s+other(?:\s+one)?\b/i,
|
|
55398
|
+
/\beach\s+of\s+them\b/i,
|
|
55399
|
+
/\bany\s+of\s+them\b/i,
|
|
55400
|
+
/\beither(?:\s+one)?\b/i,
|
|
55401
|
+
/\bboth\b/i,
|
|
55402
|
+
/\bit\b/i,
|
|
55403
|
+
/\bits\b/i,
|
|
55404
|
+
/\bthey\b/i,
|
|
55405
|
+
/\bthem\b/i,
|
|
55406
|
+
/\bthose\b/i,
|
|
55407
|
+
/\bthese\b/i
|
|
55408
|
+
];
|
|
55409
|
+
REPARAM_ANCHORS = [
|
|
55410
|
+
/\bwhat\s+about\b/i,
|
|
55411
|
+
/\bhow\s+about\b/i,
|
|
55412
|
+
/\band\s+(?:on|for|the)\b/i,
|
|
55413
|
+
/\bsame\s+(?:for|on|thing|question)\b/i,
|
|
55414
|
+
/\bwhat\s+does\s+.*\s+look\s+like\s+on\b/i,
|
|
55415
|
+
/\bthe\s+(?:first|second|third|last)\s+one\b/i,
|
|
55416
|
+
/\bthe\s+[A-Za-z0-9_]+\s+one\b/i
|
|
55417
|
+
];
|
|
55418
|
+
detectPronounAnchor = (question) => {
|
|
55419
|
+
for (const anchor of PRONOUN_ANCHORS) {
|
|
55420
|
+
const match = question.match(anchor);
|
|
55421
|
+
if (match !== null)
|
|
55422
|
+
return match[0];
|
|
55423
|
+
}
|
|
55424
|
+
return null;
|
|
55425
|
+
};
|
|
55426
|
+
detectReparamAnchor = (question) => {
|
|
55427
|
+
for (const anchor of REPARAM_ANCHORS) {
|
|
55428
|
+
const match = question.match(anchor);
|
|
55429
|
+
if (match !== null)
|
|
55430
|
+
return match[0];
|
|
55431
|
+
}
|
|
55432
|
+
return null;
|
|
55433
|
+
};
|
|
55434
|
+
ANAPHOR_ONLY = /* @__PURE__ */ new Set([
|
|
55435
|
+
"it",
|
|
55436
|
+
"its",
|
|
55437
|
+
"they",
|
|
55438
|
+
"them",
|
|
55439
|
+
"those",
|
|
55440
|
+
"these",
|
|
55441
|
+
"both",
|
|
55442
|
+
"either",
|
|
55443
|
+
"that one",
|
|
55444
|
+
"this one",
|
|
55445
|
+
"the same",
|
|
55446
|
+
"the other",
|
|
55447
|
+
"the other one",
|
|
55448
|
+
"same",
|
|
55449
|
+
"other",
|
|
55450
|
+
"either one"
|
|
55451
|
+
]);
|
|
55452
|
+
isAnaphorOnly = (phrase) => ANAPHOR_ONLY.has(phrase.trim().toLowerCase());
|
|
55453
|
+
ORDINAL_INDEX = {
|
|
55454
|
+
first: 0,
|
|
55455
|
+
second: 1,
|
|
55456
|
+
third: 2
|
|
55457
|
+
};
|
|
55458
|
+
SELECTION_SHAPE = /^\s*(?:(?:show\s+me|pick|use|go\s+with|i\s+meant|let'?s\s+go\s+with|the\s+answer\s+is)\s+)?the\s+([A-Za-z0-9_]+)\s+one\s*[?.!]*\s*$/i;
|
|
55459
|
+
NON_DESCRIPTOR_TOKENS = /* @__PURE__ */ new Set(["other", "same", "right", "wrong"]);
|
|
55460
|
+
detectClarificationSelection = (question, options) => {
|
|
55461
|
+
const match = question.match(SELECTION_SHAPE);
|
|
55462
|
+
if (match === null)
|
|
55463
|
+
return null;
|
|
55464
|
+
const token = match[1].toLowerCase();
|
|
55465
|
+
if (NON_DESCRIPTOR_TOKENS.has(token))
|
|
55466
|
+
return null;
|
|
55467
|
+
const anaphor = `the ${match[1]} one`;
|
|
55468
|
+
if (token === "last") {
|
|
55469
|
+
const selection = options.at(-1);
|
|
55470
|
+
return selection === void 0 ? { kind: "re-ask", anaphor } : { kind: "selected", anaphor, selection };
|
|
55471
|
+
}
|
|
55472
|
+
const ordinal = ORDINAL_INDEX[token];
|
|
55473
|
+
if (ordinal !== void 0) {
|
|
55474
|
+
const selection = options[ordinal];
|
|
55475
|
+
return selection === void 0 ? { kind: "re-ask", anaphor } : { kind: "selected", anaphor, selection };
|
|
55476
|
+
}
|
|
55477
|
+
const containing = options.filter((option) => option.toLowerCase().includes(token));
|
|
55478
|
+
return containing.length === 1 ? { kind: "selected", anaphor, selection: containing[0] } : { kind: "re-ask", anaphor };
|
|
55479
|
+
};
|
|
55480
|
+
GAP_FOLLOW_UP_SHAPES = [
|
|
55481
|
+
// Normative/judgment asks — the product reads metadata, it has no opinion
|
|
55482
|
+
// (q891/q30 "should they be able to?", q95 "is it normal?", q1075 "was any
|
|
55483
|
+
// of it risky?", q1054 "are those considered sensitive?", q1065 "how hard
|
|
55484
|
+
// is it to close?", q1060 "does that mean something's broken?", q1012 "is
|
|
55485
|
+
// it doing its job?").
|
|
55486
|
+
[/\bshould\s+(?:they|he|she|it)\s+(?:be\s+able|have|see)\b/i, "judgment"],
|
|
55487
|
+
[/\bis\s+(?:it|that|this)\s+normal\b|\bseems?\s+like\s+a\s+lot\b/i, "judgment"],
|
|
55488
|
+
[/\bwas\s+(?:any\s+of\s+)?(?:it|that|this)\s+risky\b/i, "judgment"],
|
|
55489
|
+
[/\bare\s+(?:those|these|they)\s+considered\s+\w+/i, "judgment"],
|
|
55490
|
+
[/\bhow\s+hard\s+(?:is|would)\s+it\b/i, "judgment"],
|
|
55491
|
+
[/\bdoes\s+that\s+mean\s+something(?:'s|\s+is)\s+broken\b/i, "judgment"],
|
|
55492
|
+
[/\bis\s+it\s+doing\s+its\s+job\b/i, "judgment"],
|
|
55493
|
+
// Delivery/export asks (q914 "as a file rather than on screen", q882 "can
|
|
55494
|
+
// it be exported…", q1939 "flag those as an audit finding").
|
|
55495
|
+
[/\bas\s+a\s+file\b|\bcan\s+(?:it|this|that)\s+be\s+exported\b/i, "delivery"],
|
|
55496
|
+
[/^\s*flag\s+(?:those|these|them|it)\b/i, "delivery"],
|
|
55497
|
+
// Self-capability probes about the TOOL, not the org (q849 "does the tool
|
|
55498
|
+
// trace transitive access like that, or is that beyond it?").
|
|
55499
|
+
[/\b(?:does|can)\s+(?:the|this)\s+tool\b/i, "tool-self-capability"],
|
|
55500
|
+
// Deployment-status telemetry (q1402 "was the change deployed or is it
|
|
55501
|
+
// still pending?").
|
|
55502
|
+
[/\bstill\s+pending\b|\bwas\s+(?:it|that|the\s+change)\s+deployed\b/i, "deployment-status"]
|
|
55503
|
+
];
|
|
55504
|
+
detectGapShapedFollowUp = (question) => {
|
|
55505
|
+
for (const [shape, family] of GAP_FOLLOW_UP_SHAPES) {
|
|
55506
|
+
if (shape.test(question))
|
|
55507
|
+
return family;
|
|
55508
|
+
}
|
|
55509
|
+
return null;
|
|
55510
|
+
};
|
|
55511
|
+
COMPONENT_ID_SHAPE = /^[A-Za-z]+:.+$/;
|
|
55512
|
+
validatePreviousContext = (previous, isRegisteredTool) => {
|
|
55513
|
+
const ignored = [];
|
|
55514
|
+
let cleaned = previous;
|
|
55515
|
+
if (previous.tool !== void 0 && !isRegisteredTool(previous.tool)) {
|
|
55516
|
+
ignored.push(`previous.tool '${previous.tool}' is not a registered tool; ignored`);
|
|
55517
|
+
cleaned = { ...cleaned, tool: void 0 };
|
|
55518
|
+
}
|
|
55519
|
+
if (previous.componentId !== void 0 && !COMPONENT_ID_SHAPE.test(previous.componentId)) {
|
|
55520
|
+
ignored.push(`previous.componentId '${previous.componentId}' is not a canonical Type:Name id; ignored`);
|
|
55521
|
+
cleaned = { ...cleaned, componentId: void 0 };
|
|
55522
|
+
}
|
|
55523
|
+
return { previous: cleaned, ignored };
|
|
55524
|
+
};
|
|
55525
|
+
CONTINUATION_TOOL_TYPES = /* @__PURE__ */ new Map([
|
|
55526
|
+
["sfi.call_graph", /* @__PURE__ */ new Set(["ApexClass", "ApexTrigger"])],
|
|
55527
|
+
["sfi.method_reachability", /* @__PURE__ */ new Set(["ApexClass", "ApexTrigger"])],
|
|
55528
|
+
["sfi.explain_apex_method", /* @__PURE__ */ new Set(["ApexClass", "ApexTrigger"])],
|
|
55529
|
+
["sfi.who_can_access_object", /* @__PURE__ */ new Set(["CustomObject"])],
|
|
55530
|
+
["sfi.object_access_audit", /* @__PURE__ */ new Set(["CustomObject"])],
|
|
55531
|
+
["sfi.field_access_audit", /* @__PURE__ */ new Set(["CustomField"])],
|
|
55532
|
+
["sfi.explain_flow", /* @__PURE__ */ new Set(["Flow"])],
|
|
55533
|
+
["sfi.who_can_run", /* @__PURE__ */ new Set(["Flow"])],
|
|
55534
|
+
["sfi.what_if_deactivate_flow", /* @__PURE__ */ new Set(["Flow"])],
|
|
55535
|
+
["sfi.explain_field", /* @__PURE__ */ new Set(["CustomField"])],
|
|
55536
|
+
["sfi.field_360", /* @__PURE__ */ new Set(["CustomField"])],
|
|
55537
|
+
["sfi.field_lineage", /* @__PURE__ */ new Set(["CustomField"])],
|
|
55538
|
+
["sfi.safe_to_delete_field", /* @__PURE__ */ new Set(["CustomField"])],
|
|
55539
|
+
["sfi.explain_formula", /* @__PURE__ */ new Set(["CustomField"])],
|
|
55540
|
+
["sfi.what_happens_on_save", /* @__PURE__ */ new Set(["CustomObject"])],
|
|
55541
|
+
["sfi.order_of_execution", /* @__PURE__ */ new Set(["CustomObject"])],
|
|
55542
|
+
["sfi.what_if_disable_trigger", /* @__PURE__ */ new Set(["ApexTrigger"])]
|
|
55543
|
+
]);
|
|
55544
|
+
continuationToolCompatible = (tool, resolvedType) => {
|
|
55545
|
+
if (resolvedType === null)
|
|
55546
|
+
return true;
|
|
55547
|
+
const compatible = CONTINUATION_TOOL_TYPES.get(tool);
|
|
55548
|
+
return compatible === void 0 || compatible.has(resolvedType);
|
|
55549
|
+
};
|
|
55550
|
+
REPARAM_FILLER = /* @__PURE__ */ new Set([
|
|
55551
|
+
"what",
|
|
55552
|
+
"whats",
|
|
55553
|
+
"how",
|
|
55554
|
+
"about",
|
|
55555
|
+
"and",
|
|
55556
|
+
"same",
|
|
55557
|
+
"for",
|
|
55558
|
+
"on",
|
|
55559
|
+
"the",
|
|
55560
|
+
"a",
|
|
55561
|
+
"an",
|
|
55562
|
+
"of",
|
|
55563
|
+
"in",
|
|
55564
|
+
"to",
|
|
55565
|
+
"does",
|
|
55566
|
+
"do",
|
|
55567
|
+
"is",
|
|
55568
|
+
"are",
|
|
55569
|
+
"look",
|
|
55570
|
+
"like",
|
|
55571
|
+
"way",
|
|
55572
|
+
"thing",
|
|
55573
|
+
"question",
|
|
55574
|
+
"too",
|
|
55575
|
+
"also",
|
|
55576
|
+
"instead",
|
|
55577
|
+
"then",
|
|
55578
|
+
"now",
|
|
55579
|
+
"with",
|
|
55580
|
+
"combined",
|
|
55581
|
+
"anything",
|
|
55582
|
+
"everything",
|
|
55583
|
+
"else",
|
|
55584
|
+
"one"
|
|
55585
|
+
]);
|
|
55586
|
+
extractReparamTarget = (question) => {
|
|
55587
|
+
const tokens = question.replace(/[?!.,;:]+/g, " ").split(/\s+/).filter((token) => token.length > 0).filter((token) => !REPARAM_FILLER.has(token.toLowerCase()));
|
|
55588
|
+
if (tokens.length === 0 || tokens.length > 4)
|
|
55589
|
+
return null;
|
|
55590
|
+
return tokens.join(" ");
|
|
55591
|
+
};
|
|
55592
|
+
}
|
|
55593
|
+
});
|
|
55594
|
+
|
|
55595
|
+
// ../mcp/dist/src/refusal-gates.js
|
|
55596
|
+
var INJECTION_PATTERNS, EXFIL_VERB, EXFIL_QUANTIFIER, EXFIL_SENSITIVE, EXFIL_VALUES, EXFIL_SINGLE_VALUE, INJECTION_DISCLOSURE, WRITE_VERB, WRITE_VERB_FILLER, WRITE_EXCLUDER, SIMULATION_TAIL, WRITE_SENTENCE_INITIAL, WRITE_LEAD_IN, WRITE_TRAILING_FRAME, WRITE_MAKE_SCHEMA, WRITE_GIVE_GRANT, WRITE_GIVE_INITIAL, WRITE_CHAINED_DUPE_DELETE, RUN_VERB, RUN_TARGET, RUN_GAP, RUN_IMPERATIVE_INITIAL, RUN_IMPERATIVE_LEAD_IN, RUN_ANAPHOR, runReadOnlyAlternativeFor, readOnlyAlternativeFor, IDENTITY_IDIOM, IDENTITY_ALLOWED_MUTATE, IDENTITY_LOOK_EXCLUDER, IDENTITY_DISCLOSURE, RUNTIME_TRIGGERS, RUNTIME_WINDOW, RUNTIME_INCIDENT, runtimeDisclosure, EXTERNAL_SYSTEM, POLICY_ASK, RETENTION_POLICY_ASK, CONSENT_PROCESS_ASK, OPINION_ASK, SHOULD_WE, METADATA_NOUN, DELIVERY_ASK, WRITE_CODE_ASK, DRAFT_DOCUMENT_ASK, CAREER_ASK, outOfScopeDisclosure, detectRefusalShape;
|
|
55597
|
+
var init_refusal_gates = __esm({
|
|
55598
|
+
"../mcp/dist/src/refusal-gates.js"() {
|
|
55599
|
+
"use strict";
|
|
55600
|
+
INJECTION_PATTERNS = [
|
|
55601
|
+
/\bignore\s+(?:your|all|the)\s+(?:previous|prior|read-?only)\s+(?:instructions?|rules?|restrictions?)\b/i,
|
|
55602
|
+
/\bpretend\s+the\s+(?:previous|prior)\s+rules\b/i,
|
|
55603
|
+
/\byou\s+are\s+now\s+(?:in\s+)?(?:admin|deploy|god)\s*[- ]?mode\b/i,
|
|
55604
|
+
/\byou'?re\s+actually\s+an?\b[^.?!]*\bbot\b/i,
|
|
55605
|
+
/\b(?:print|reveal|show)\s+(?:me\s+)?your\s+(?:system\s+)?prompt\b/i,
|
|
55606
|
+
// Privilege-escalation asks (q1548 "sudo give me full access"): `sudo` as a
|
|
55607
|
+
// command prefix, or a grant-to-SELF ("give me full/admin access"). The
|
|
55608
|
+
// READ delivery ask "give me the FLS grant list" carries no privilege noun
|
|
55609
|
+
// and stays routed — the escalation arm requires full/admin/elevated +
|
|
55610
|
+
// access/permission.
|
|
55611
|
+
/(?:^|[.!?;]\s+)sudo\b/i,
|
|
55612
|
+
/\bgive\s+me\s+(?:full|admin|superuser|elevated|god[-\s]?mode)\s+(?:access|permissions?|rights?|privileges?)\b/i
|
|
55613
|
+
];
|
|
55614
|
+
EXFIL_VERB = /\b(?:dump|export|give\s+me|tell\s+me|list|retrieve)\b/i;
|
|
55615
|
+
EXFIL_QUANTIFIER = /\b(?:every|all|actual|the\s+real|random)\b/i;
|
|
55616
|
+
EXFIL_SENSITIVE = /\b(?:ssn|social\s+security|pii|ferpa|hipaa)\b/i;
|
|
55617
|
+
EXFIL_VALUES = /\bvalues?\b|\bstored\b/i;
|
|
55618
|
+
EXFIL_SINGLE_VALUE = /\b(?:what(?:'s|s|\s+is)|show\s+me|give\s+me|pull)\s+the\s+(?:ssn|social\s+security(?:\s+number)?)\b(?!\s+fields?\b)[^.?!]{0,60}\b(?:lead|contact|record|student|person|user|account|applicant)s?\b/i;
|
|
55619
|
+
INJECTION_DISCLOSURE = "REFUSED: this request asks me to override instructions or exfiltrate record values. sf-intelligence reads metadata only; it never returns stored record values and its instructions are not overridable.";
|
|
55620
|
+
WRITE_VERB = "(?:create|add|delete|remove|update|change|(?<!permission\\s)set|deploy|assign|reassign|revoke|deactivate|activate|disable|enable|merge|reset|rename|fix|grant|provision|turn\\s+(?:on|off)|clean\\s+up|migrate|convert|insert|upsert|push|publish|install|uninstall|schedule|upgrade|downgrade|throttle|purge)";
|
|
55621
|
+
WRITE_VERB_FILLER = "(?:(?:bulk|mass|batch|just|please|go\\s+ahead\\s+and)\\s+)*";
|
|
55622
|
+
WRITE_EXCLUDER = /\b(?:am\s+i|can\s+i|could\s+i|do\s+i|who\s+can|who\s+is\s+able|allowed\s+to|able\s+to|what\s+if|what\s+would|would\s+happen|is\s+it\s+safe|safe\s+to|before\s+i|if\s+(?:i|we)|when\s+(?:i|we)|suppose|should\s+i|how\s+do\s+i|how\s+would\s+i|what\s+happens\s+when)\b|\bsafely\s*\?|\bwhat\s+(?:depends\s+on|breaks|would\s+break|will\s+break|stops\s+working)\b/i;
|
|
55623
|
+
SIMULATION_TAIL = /\b(?:and\s+)?(?:tell|show|give)\s+me\s+(?:the\s+)?(?:impact|blast\s+radius|consequences?|every\s+conflict|conflicts|what\s+(?:breaks|happens|stops|changes))\b|[—–-]\s*(?:impact|consequences?|blast\s+radius)\s*\??\s*$|\b(?:impact|consequences)\s*\?\s*$|\bwhat(?:'s|\s+is)\s+the\s+(?:impact|blast\s+radius|fallout)\b|\bwhat\s+(?:happens|stops|breaks)\s+(?:to|if|when|after|downstream)\b/i;
|
|
55624
|
+
WRITE_SENTENCE_INITIAL = new RegExp(`(?:^|[.!?;]\\s+)(?:please\\s+|just\\s+)?${WRITE_VERB_FILLER}(${WRITE_VERB}\\b[^.?!;]{0,80})`, "i");
|
|
55625
|
+
WRITE_LEAD_IN = new RegExp(`\\b(?:please|(?<!\\b(?:or|vs|than)\\s)just|go\\s+ahead\\s+and|can\\s+(?:you|u)|could\\s+(?:you|u)|would\\s+(?:you|u)|you\\s+should|i\\s+(?:need|want)\\s+you\\s+to)\\s+(?:please\\s+|just\\s+)?${WRITE_VERB_FILLER}(${WRITE_VERB}\\b[^.?!;]{0,80})`, "i");
|
|
55626
|
+
WRITE_TRAILING_FRAME = new RegExp(`\\b(${WRITE_VERB}\\b[^.?!;]{0,80}?\\b(?:for\\s+me|right\\s+now|and\\s+confirm)\\b[^.?!;]{0,20})`, "i");
|
|
55627
|
+
WRITE_MAKE_SCHEMA = new RegExp(`\\b(?:can|could|would)\\s+(?:you|u)\\b[^.?!;]{0,10}\\s(make\\b[^.?!;]{0,60}\\b(?:required|mandatory|optional|unique|read[-\\s]?only|editable|visible)\\b)`, "i");
|
|
55628
|
+
WRITE_GIVE_GRANT = new RegExp(`\\b(?:can|could|would)\\s+(?:you|u)\\b[^.?!;]{0,30}\\b(give\\s+(?!me\\b)[^.?!;]{0,60}\\b(?:profile|permission|perm\\s+set|access|user)\\b[^.?!;]{0,40})`, "i");
|
|
55629
|
+
WRITE_GIVE_INITIAL = /(?:^|[.!?;]\s+)(give\s+(?!me\b)[^.?!;]{0,60}\b(?:admin|access|permission|profile)\b[^.?!;]{0,30})/i;
|
|
55630
|
+
WRITE_CHAINED_DUPE_DELETE = /\b(?:and|then)\s+(?:just\s+)?((?:delete|remove|purge|merge)\s+(?:the\s+)?dup(?:e|licate)s?\b[^.?!;]{0,30})/i;
|
|
55631
|
+
RUN_VERB = "(?:run|re-?run|execute|invoke|kick\\s+off|trigger|launch|fire\\s+off)";
|
|
55632
|
+
RUN_TARGET = "(?:flows?|triggers?|batch(?:\\s+(?:jobs?|class(?:es)?|apex))?|jobs?|apex\\s+class(?:es)?|automations?|scripts?)";
|
|
55633
|
+
RUN_GAP = "(?:(?!\\b(?:on|of|for|across|against|in|over|about)\\b)[^.?!;]){0,60}?";
|
|
55634
|
+
RUN_IMPERATIVE_INITIAL = new RegExp(`(?:^|[.!?;]\\s+)(?:please\\s+|just\\s+)?(${RUN_VERB}\\b${RUN_GAP}\\b${RUN_TARGET}\\b[^.?!;]{0,60})`, "i");
|
|
55635
|
+
RUN_IMPERATIVE_LEAD_IN = new RegExp(`\\b(?:please|just|go\\s+ahead\\s+and|can\\s+(?:you|u)|could\\s+(?:you|u)|would\\s+(?:you|u)|you\\s+should|i\\s+need\\s+you\\s+to)\\s+(?:please\\s+|just\\s+)?(${RUN_VERB}\\b${RUN_GAP}\\b${RUN_TARGET}\\b[^.?!;]{0,60})`, "i");
|
|
55636
|
+
RUN_ANAPHOR = new RegExp(`(?:(?:^|[.!?;]\\s+)(?:please\\s+|just\\s+)?|\\b(?:please|just|go\\s+ahead\\s+and|can\\s+(?:you|u)|could\\s+(?:you|u)|would\\s+(?:you|u)|you\\s+should|i\\s+need\\s+you\\s+to)\\s+(?:please\\s+|just\\s+)?)(${RUN_VERB}\\s+(?:it|that|this)\\b(?!\\s+(?:by|past)\\b)[^.?!;]{0,40}|(?:kick|fire|set)\\s+(?:it|that|this)\\s+off\\b[^.?!;]{0,30})`, "i");
|
|
55637
|
+
runReadOnlyAlternativeFor = (verbPhrase) => {
|
|
55638
|
+
const v2 = verbPhrase.toLowerCase();
|
|
55639
|
+
if (/\bflows?\b/.test(v2))
|
|
55640
|
+
return "sfi.explain_flow";
|
|
55641
|
+
if (/\b(?:batch|jobs?)\b/.test(v2))
|
|
55642
|
+
return "sfi.scheduled_job_catalog";
|
|
55643
|
+
if (/\btriggers?\b/.test(v2))
|
|
55644
|
+
return "sfi.what_happens_on_save";
|
|
55645
|
+
if (/\bapex|class(?:es)?\b/.test(v2))
|
|
55646
|
+
return "sfi.explain_apex_method";
|
|
55647
|
+
return "sfi.get_impact";
|
|
55648
|
+
};
|
|
55649
|
+
readOnlyAlternativeFor = (verbPhrase, question) => {
|
|
55650
|
+
const v2 = verbPhrase.toLowerCase();
|
|
55651
|
+
const q2 = question.toLowerCase();
|
|
55652
|
+
if (/^(?:delete|remove)\b/.test(v2) && /\bfield\b|__c\b/.test(q2)) {
|
|
55653
|
+
return "sfi.safe_to_delete_field";
|
|
55654
|
+
}
|
|
55655
|
+
if (/^(?:deactivate|disable|turn)\b/.test(v2) && /\bflow\b/.test(q2)) {
|
|
55656
|
+
return "sfi.what_if_deactivate_flow";
|
|
55657
|
+
}
|
|
55658
|
+
if (/^disable\b/.test(v2) && /\btrigger\b/.test(q2)) {
|
|
55659
|
+
return "sfi.what_if_disable_trigger";
|
|
55660
|
+
}
|
|
55661
|
+
if (/^(?:change|convert)\b/.test(v2) && /\bfield\b/.test(q2)) {
|
|
55662
|
+
return "sfi.what_if_change_field_type";
|
|
55663
|
+
}
|
|
55664
|
+
if (/^merge\b/.test(v2) && /\bprofile/.test(q2)) {
|
|
55665
|
+
return "sfi.what_if_merge_profiles";
|
|
55666
|
+
}
|
|
55667
|
+
if (/^make\b/.test(v2) && /\brequired|mandatory\b/.test(v2)) {
|
|
55668
|
+
return "sfi.what_if_make_field_required";
|
|
55669
|
+
}
|
|
55670
|
+
if (/^give\b/.test(v2) || /^grant\b/.test(v2) && /\b(?:profile|permission)\b/.test(q2)) {
|
|
55671
|
+
return "sfi.permission_risk_report";
|
|
55672
|
+
}
|
|
55673
|
+
if (/^(?:upgrade|downgrade|install|uninstall)\b/.test(v2) && /\bpackage\b/.test(q2)) {
|
|
55674
|
+
return "sfi.package_impact";
|
|
55675
|
+
}
|
|
55676
|
+
if (/^throttle\b/.test(v2) || /\basync\b/.test(q2)) {
|
|
55677
|
+
return "sfi.async_chain_depth";
|
|
55678
|
+
}
|
|
55679
|
+
if (/dup(?:e|licate)s?\b/.test(v2)) {
|
|
55680
|
+
return "sfi.live_duplicate_check";
|
|
55681
|
+
}
|
|
55682
|
+
return "sfi.get_impact";
|
|
55683
|
+
};
|
|
55684
|
+
IDENTITY_IDIOM = /\bcan\s+i\s+be\s+trusted\s+to\b|\bwithin\s+my\s+power\b|\babove\s+my\s+(?:permission\s+level|pay\s+grade)\b|\bwhat\s+(?:am\s+i|i'?m)\s+capable\s+of\b|\bwondering\s+what\s+i'?m\s+capable\s+of\b/i;
|
|
55685
|
+
IDENTITY_ALLOWED_MUTATE = /\bam\s+i\s+(?:allowed|permitted|able)\s+to\s+(?:change|delete|remove|merge|deactivate|disable|create|rename|reset|convert|reassign|modify|update)\b|\bis\s+it\s+(?:okay?|ok)\s+for\s+me\s+to\s+(?:change|delete|remove|merge|deactivate|disable|create|rename|reset|convert|reassign|modify|update)\b/i;
|
|
55686
|
+
IDENTITY_LOOK_EXCLUDER = /\bam\s+i\s+(?:allowed|permitted|able)\s+to\s+(?:look|see|view|read|ask|know|check|understand|open)\b|\bis\s+it\s+(?:okay?|ok)\s+for\s+me\s+to\s+(?:look|see|view|read|ask|know|check)\b/i;
|
|
55687
|
+
IDENTITY_DISCLOSURE = "HONEST GAP (identity): sf-intelligence has no session-user identity \u2014 it cannot know what YOU specifically are allowed to do. Name the user, profile, or permission set to check and I can answer from the permission metadata (sfi.effective_permissions / sfi.user_ability). Which user or profile should I check?";
|
|
55688
|
+
RUNTIME_TRIGGERS = [
|
|
55689
|
+
[/\blogin\s+history\b|\baudit\s+trail\s+of\s+logins?\b/i, "login history"],
|
|
55690
|
+
// Per-user LOGIN EVENTS (hon-031/hon-036/hon-060): who logged in when,
|
|
55691
|
+
// exact login timestamps, who is logged in right now, or a full per-user
|
|
55692
|
+
// last-login roster. All are LoginHistory/session event data no tool reads.
|
|
55693
|
+
// Precision guards, each a live_inactive_users question that must NOT gate:
|
|
55694
|
+
// "which users are inactive", "who HASN'T logged in for 90 days" (the
|
|
55695
|
+
// negation breaks the strict token sequence), "login IP ranges on a
|
|
55696
|
+
// profile" (profile_security metadata — no logged-in/timestamp token).
|
|
55697
|
+
[
|
|
55698
|
+
/\b(?:which|what)\s+(?:specific\s+)?users?\s+(?:have\s+|are\s+)?logged\s+in(?:to)?\b|\bwho\s+logged\s+in(?:to)?\b/i,
|
|
55699
|
+
"per-user login events (who logged in when)"
|
|
55700
|
+
],
|
|
55701
|
+
[
|
|
55702
|
+
/\blogin\s+timestamps?\b|\bcurrently\s+logged\s+in(?:to)?\b|\blogged\s+in(?:to)?\b[^.?!]{0,30}\bright\s+now\b/i,
|
|
55703
|
+
"login sessions/timestamps"
|
|
55704
|
+
],
|
|
55705
|
+
[
|
|
55706
|
+
/\b(?:every|each|all)\s+(?:salesforce\s+)?users?'?s?\b[^.?!]{0,30}\blast\s+login\b/i,
|
|
55707
|
+
"a full per-user last-login roster (sfi.live_inactive_users covers dormancy thresholds, not a full roster)"
|
|
55708
|
+
],
|
|
55709
|
+
[/\badoption\b|\bhow\s+often\s+do\s+users\s+actually\s+use\b/i, "adoption/usage telemetry"],
|
|
55710
|
+
[
|
|
55711
|
+
/\bping\s+(?:the\s+)?\S+|\bendpoints?\b[^.?!]*\bup\b|\b(?:returned|returning|throwing|threw)\s+errors?\b/i,
|
|
55712
|
+
"endpoint health"
|
|
55713
|
+
],
|
|
55714
|
+
[/\brunning[-\s]user\s+context\b/i, "the runtime running-user context"],
|
|
55715
|
+
// Automation EXECUTION forensics (hon-032/hon-039/hon-037): traces of what
|
|
55716
|
+
// a run touched, aggregate run counts, the error message from the last
|
|
55717
|
+
// failure. Static reads stay routed: "how many times is this field
|
|
55718
|
+
// REFERENCED" (no run verb), "what runs when an Application is submitted"
|
|
55719
|
+
// (no how-many-times/trace), "when was it last MODIFIED" (no failed/ran).
|
|
55720
|
+
[
|
|
55721
|
+
/\bexecution\s+trace\b|\bwhat\s+records\s+did\s+(?:it|the\s+\S+(?:\s+\S+)?)\s+touch\b/i,
|
|
55722
|
+
"flow/automation execution traces"
|
|
55723
|
+
],
|
|
55724
|
+
[
|
|
55725
|
+
/\bhow\s+many\s+times\s+(?:has|have|did|was|were)\b[^.?!]{0,80}\b(?:executed|run|ran|fired|triggered)\b/i,
|
|
55726
|
+
"aggregate automation run counts (sfi.live_automation_fired infers per-record only)"
|
|
55727
|
+
],
|
|
55728
|
+
[
|
|
55729
|
+
/\b(?:the\s+)?last\s+time\b[^.?!]{0,60}\b(?:failed|errored|crashed|ran)\b/i,
|
|
55730
|
+
"run/failure forensics from past executions"
|
|
55731
|
+
],
|
|
55732
|
+
// Apex/query RUNTIME PROFILING + debug logs (hon-040/hon-044/hon-053):
|
|
55733
|
+
// CPU/heap profiles of a past run, debug logs, SOQL execution plans.
|
|
55734
|
+
// Static reads stay routed: "which classes RISK hitting governor limits"
|
|
55735
|
+
// (governor_limit_risks — no profile/log/plan noun), "which class contains
|
|
55736
|
+
// System.debug" (code-literal search — no `debug log` bigram).
|
|
55737
|
+
[
|
|
55738
|
+
/\b(?:cpu\s+time|heap\s+(?:usage|size)|memory\s+(?:usage|utilization|consumption))\b[^.?!]{0,50}\bprofile\b|\bprofile\b[^.?!]{0,50}\b(?:cpu\s+time|heap\s+usage)\b/i,
|
|
55739
|
+
"runtime CPU/heap profiling"
|
|
55740
|
+
],
|
|
55741
|
+
[/\bmemory\s+(?:utilization|consumption)\b|\bapplication\s+servers?\b/i, "infrastructure telemetry"],
|
|
55742
|
+
// Debug-LOG retrieval needs the retrieval-verb frame: "SHOW ME the debug
|
|
55743
|
+
// log from yesterday's batch run" is runtime; "leftover debug logs in apex"
|
|
55744
|
+
// (q522 — System.debug code artifacts, search_apex_source) and "is it in a
|
|
55745
|
+
// debug log anywhere" carry no retrieval verb and stay routed. Shield
|
|
55746
|
+
// EVENT-MONITORING logs (router-v2 R2, hon-057) are the same retrieval
|
|
55747
|
+
// shape — runtime telemetry no vault holds; "which flows subscribe to
|
|
55748
|
+
// platform events" carries no log noun and stays routed.
|
|
55749
|
+
[
|
|
55750
|
+
/\b(?:show|see|view|pull|get|give|retrieve|read|check|download|fetch)\b[^.?!]{0,45}\b(?:debug|event[\s-]monitoring)\s+logs?\b/i,
|
|
55751
|
+
"debug / event-monitoring logs"
|
|
55752
|
+
],
|
|
55753
|
+
[
|
|
55754
|
+
/\b(?:query|execution|soql)\s+plans?\b|\bindexes?\s+(?:are\s+)?being\s+used\b/i,
|
|
55755
|
+
"SOQL execution plans / index usage"
|
|
55756
|
+
],
|
|
55757
|
+
// Message DELIVERY telemetry (hon-056/hon-041): delivered/bounce counts and
|
|
55758
|
+
// the ACTUAL content of sent messages. Metadata reads stay routed: "which
|
|
55759
|
+
// email templates are unused" (live_email_template_usage), "what emails go
|
|
55760
|
+
// out for this approval" (sendsEmail edges — no delivered/sent-count ask).
|
|
55761
|
+
[
|
|
55762
|
+
/\b(?:emails?|sms|messages?|texts?)\b[^.?!]{0,50}\b(?:delivered|deliverability|bounced?|bounce\s+rate)\b/i,
|
|
55763
|
+
"message delivery telemetry"
|
|
55764
|
+
],
|
|
55765
|
+
[
|
|
55766
|
+
/\bhow\s+many\s+(?:emails?|sms|messages?|texts?)\b[^.?!]{0,60}\b(?:sent|delivered|went\s+out)\b/i,
|
|
55767
|
+
"sent-message counts"
|
|
55768
|
+
],
|
|
55769
|
+
[
|
|
55770
|
+
/\bactual\s+content\s+of\s+the\s+(?:emails?|messages?|texts?)\b/i,
|
|
55771
|
+
"the content of individually sent messages (the TEMPLATE body is metadata \u2014 sfi.get_component reads it)"
|
|
55772
|
+
],
|
|
55773
|
+
// Site/community WEB ANALYTICS (hon-050): click paths, page views.
|
|
55774
|
+
[
|
|
55775
|
+
/\b(?:click|page|site|web)\s*[- ]?(?:analytics|traffic)\b|\bpage\s+views?\b|\bwhich\s+pages\b[^.?!]{0,60}\bvisit/i,
|
|
55776
|
+
"site/community click analytics"
|
|
55777
|
+
],
|
|
55778
|
+
// RECORD-LEVEL field history (hon-054): before/after values of changed
|
|
55779
|
+
// records. "Which fields HAVE history tracking enabled" is object metadata
|
|
55780
|
+
// and stays routed (no before/after-values bigram).
|
|
55781
|
+
[
|
|
55782
|
+
/\bbefore(?:\s*\/\s*|\s*-\s*|\s+and\s+)after\b[^.?!]{0,40}\b(?:values?|data)\b/i,
|
|
55783
|
+
"record-level field history (before/after values)"
|
|
55784
|
+
],
|
|
55785
|
+
// RECORD-ACCESS audit (q960/q1117): who ACCESSED/viewed a record or field.
|
|
55786
|
+
// "Who CAN see/access X" is a permissions read (who_can_access_object /
|
|
55787
|
+
// field_access_audit) and never matches — the arm requires the past-tense
|
|
55788
|
+
// accessed/viewed/opened event verb.
|
|
55789
|
+
[
|
|
55790
|
+
/\bwho\b[^.?!]{0,30}\b(?:accessed|viewed|opened|looked\s+at)\b[^.?!]{0,60}\b(?:records?|fields?|data)\b/i,
|
|
55791
|
+
"record-access audit events (who accessed what)"
|
|
55792
|
+
],
|
|
55793
|
+
// R3 §5b AUDIT-TRAIL-PLANE arms (8-question over-route cluster): Setup
|
|
55794
|
+
// Audit Trail asks — who changed a SETTING (FLS, sharing, OWD, session/
|
|
55795
|
+
// password policy) and when. Component lastModifiedBy IS metadata
|
|
55796
|
+
// (sfi.last_modified stays routed — those asks say "who changed <component>",
|
|
55797
|
+
// no settings noun); FLS/OWD/policy flips are only in the Setup Audit
|
|
55798
|
+
// Trail, which neither the vault nor the live plane reads.
|
|
55799
|
+
[/\bsetup\s+audit\s+trail\b/i, "Setup Audit Trail entries"],
|
|
55800
|
+
[
|
|
55801
|
+
/\bwho\s+(?:modified|changed|flipped|updated|edited)\b[^.?!]{0,50}\b(?:field-?level\s+security|fls|sharing\s+settings?|org-?wide\s+defaults?|owd|session\s+settings?|password\s+polic\w*|mfa)\b/i,
|
|
55802
|
+
"who changed org security settings and when (Setup Audit Trail data)"
|
|
55803
|
+
],
|
|
55804
|
+
// R3 §5b: report/dashboard DEFINITIONS (source object, columns, broken
|
|
55805
|
+
// references) are not retrieved into the vault — "which reports are
|
|
55806
|
+
// stale/unused" (live_report_usage) and "who can see the dashboard"
|
|
55807
|
+
// (folder access) carry none of these definition verbs and stay routed.
|
|
55808
|
+
[
|
|
55809
|
+
/\b(?:reports?|dashboards?)\b[^.?!]{0,50}\b(?:built|based)\s+on\b|\breports?\b[^.?!]{0,50}\breference\b[^.?!]{0,50}\b(?:deleted|renamed|broken|missing)\b|\bdashboards?\b[^.?!]{0,40}\b(?:broken|point\s+at)\b/i,
|
|
55810
|
+
"report/dashboard definitions (source object, columns, referenced fields) \u2014 not retrieved into the vault"
|
|
55811
|
+
]
|
|
55812
|
+
];
|
|
55813
|
+
RUNTIME_WINDOW = /\b(?:this\s+week|yesterday|last\s+night|in\s+the\s+\w+\s+incident)\b/i;
|
|
55814
|
+
RUNTIME_INCIDENT = /\b(?:errors?|failed|failures?|incident|outage|gated)\b/i;
|
|
55815
|
+
runtimeDisclosure = (topic) => `HONEST GAP: ${topic} is runtime telemetry sf-intelligence does not model \u2014 the vault holds metadata; the live plane covers counts/samples/limits, not event logs.`;
|
|
55816
|
+
EXTERNAL_SYSTEM = /\b(sharepoint|jira|confluence|google\s+drive|onedrive|slack\s+workspace|s3\s+bucket)\b/i;
|
|
55817
|
+
POLICY_ASK = /\bwhat(?:'s|s|\s+is)\s+our\b(?![^.?!]{0,60}\b(?:password|session|sharing|lockout|ip|login)\b)[^.?!]{0,60}\bpolic(?:y|ies)\b/i;
|
|
55818
|
+
RETENTION_POLICY_ASK = /\bretention\s+(?:polic(?:y|ies)|schedule|rules?)\b/i;
|
|
55819
|
+
CONSENT_PROCESS_ASK = /\bhow\s+do(?:es)?\s+(?:students?|users?|customers?|people|contacts?)\s+consent\b/i;
|
|
55820
|
+
OPINION_ASK = /\bwhat\s+do\s+you\s+think\b/i;
|
|
55821
|
+
SHOULD_WE = /\bshould\s+we\b/i;
|
|
55822
|
+
METADATA_NOUN = /\b(?:field|object|flow|trigger|class|profile|permission|layout|validation|record\s+type|apex|report|dashboard|picklist|component|rule)\b|__(?:c|mdt|e|x|b|kav)\b/i;
|
|
55823
|
+
DELIVERY_ASK = /\bemail\s+me\b|\bsend\s+(?:this|it)\s+to\b|\bpost\s+(?:this\s+|it\s+)?to\s+slack\b/i;
|
|
55824
|
+
WRITE_CODE_ASK = /\bwrite\s+(?:me\s+)?(?:an?\s+)?(?:apex|lwc|trigger|class|component)\b[^.?!]*\b(?:that|to)\b/i;
|
|
55825
|
+
DRAFT_DOCUMENT_ASK = /\bdraft\s+(?:me\s+)?(?:a|an)\b[^.?!]{0,60}\b(?:agreement|contract|clause|policy|memo|press\s+release)\b/i;
|
|
55826
|
+
CAREER_ASK = /\bhow\s+do\s+i\s+become\b/i;
|
|
55827
|
+
outOfScopeDisclosure = (topic) => `OUT OF SCOPE: sf-intelligence answers questions about this org's Salesforce metadata. ${topic} is outside that boundary (no external systems, no policy authorship, no code generation, no message delivery).`;
|
|
55828
|
+
detectRefusalShape = (question) => {
|
|
55829
|
+
const q2 = question.replace(/[‘’ʼ]/g, "'").replace(/\s+/g, " ").trim();
|
|
55830
|
+
if (q2.length === 0)
|
|
55831
|
+
return null;
|
|
55832
|
+
const injected = INJECTION_PATTERNS.some((p2) => p2.test(q2)) || EXFIL_VERB.test(q2) && EXFIL_QUANTIFIER.test(q2) && EXFIL_SENSITIVE.test(q2) && EXFIL_VALUES.test(q2) || EXFIL_SINGLE_VALUE.test(q2);
|
|
55833
|
+
if (injected) {
|
|
55834
|
+
return { kind: "injection-exfiltration", disclosure: INJECTION_DISCLOSURE };
|
|
55835
|
+
}
|
|
55836
|
+
if (!WRITE_EXCLUDER.test(q2)) {
|
|
55837
|
+
const simulationFrame = SIMULATION_TAIL.test(q2);
|
|
55838
|
+
const verbPhrase = simulationFrame ? void 0 : (WRITE_SENTENCE_INITIAL.exec(q2) ?? WRITE_LEAD_IN.exec(q2) ?? WRITE_TRAILING_FRAME.exec(q2) ?? WRITE_MAKE_SCHEMA.exec(q2) ?? WRITE_GIVE_GRANT.exec(q2) ?? WRITE_GIVE_INITIAL.exec(q2) ?? WRITE_CHAINED_DUPE_DELETE.exec(q2))?.[1]?.trim().replace(/[,;:]$/, "");
|
|
55839
|
+
if (verbPhrase !== void 0 && verbPhrase.length > 0) {
|
|
55840
|
+
const alternative = readOnlyAlternativeFor(verbPhrase, q2);
|
|
55841
|
+
return {
|
|
55842
|
+
kind: "write-imperative",
|
|
55843
|
+
disclosure: `REFUSED (read-only boundary): sf-intelligence never mutates the org \u2014 it cannot ${verbPhrase}. I can show you the read-side analysis instead: ${alternative}.`,
|
|
55844
|
+
readOnlyAlternative: alternative
|
|
55845
|
+
};
|
|
55846
|
+
}
|
|
55847
|
+
const runPhrase = (RUN_IMPERATIVE_INITIAL.exec(q2) ?? RUN_IMPERATIVE_LEAD_IN.exec(q2) ?? RUN_ANAPHOR.exec(q2))?.[1]?.trim().replace(/[,;:]$/, "");
|
|
55848
|
+
if (runPhrase !== void 0 && runPhrase.length > 0) {
|
|
55849
|
+
const alternative = runReadOnlyAlternativeFor(runPhrase);
|
|
55850
|
+
return {
|
|
55851
|
+
kind: "write-imperative",
|
|
55852
|
+
disclosure: `REFUSED (read-only boundary): sf-intelligence never executes org automation \u2014 it cannot ${runPhrase}. I can show you what it WOULD do instead: ${alternative}.`,
|
|
55853
|
+
readOnlyAlternative: alternative
|
|
55854
|
+
};
|
|
55855
|
+
}
|
|
55856
|
+
}
|
|
55857
|
+
if ((IDENTITY_IDIOM.test(q2) || IDENTITY_ALLOWED_MUTATE.test(q2)) && !IDENTITY_LOOK_EXCLUDER.test(q2)) {
|
|
55858
|
+
return { kind: "identity-gap", disclosure: IDENTITY_DISCLOSURE };
|
|
55859
|
+
}
|
|
55860
|
+
const runtimeTopic = RUNTIME_TRIGGERS.find(([pattern]) => pattern.test(q2))?.[1] ?? (RUNTIME_WINDOW.test(q2) && RUNTIME_INCIDENT.test(q2) ? "runtime incident forensics" : void 0);
|
|
55861
|
+
if (runtimeTopic !== void 0) {
|
|
55862
|
+
return { kind: "runtime-analytics", disclosure: runtimeDisclosure(runtimeTopic) };
|
|
55863
|
+
}
|
|
55864
|
+
const externalSystem = EXTERNAL_SYSTEM.exec(q2)?.[1];
|
|
55865
|
+
const outOfScopeTopic = externalSystem !== void 0 ? `"${externalSystem}"` : RETENTION_POLICY_ASK.test(q2) ? "A data-retention policy (organizational governance, not org metadata)" : CONSENT_PROCESS_ASK.test(q2) ? "A consent process (organizational governance, not org metadata)" : POLICY_ASK.test(q2) ? "Org policy authorship" : OPINION_ASK.test(q2) ? "An opinion" : DELIVERY_ASK.test(q2) ? "Message delivery" : WRITE_CODE_ASK.test(q2) ? "Code generation" : DRAFT_DOCUMENT_ASK.test(q2) ? "Document authorship (agreements, policies, memos)" : CAREER_ASK.test(q2) ? "Career/self-improvement guidance" : SHOULD_WE.test(q2) && !METADATA_NOUN.test(q2) ? "An org-decision recommendation" : void 0;
|
|
55866
|
+
if (outOfScopeTopic !== void 0) {
|
|
55867
|
+
return { kind: "out-of-scope", disclosure: outOfScopeDisclosure(outOfScopeTopic) };
|
|
55868
|
+
}
|
|
55869
|
+
return null;
|
|
55870
|
+
};
|
|
55871
|
+
}
|
|
55872
|
+
});
|
|
55873
|
+
|
|
55874
|
+
// ../mcp/dist/src/funnel-utterances.js
|
|
55875
|
+
var FUNNEL_UTTERANCES;
|
|
55876
|
+
var init_funnel_utterances = __esm({
|
|
55877
|
+
"../mcp/dist/src/funnel-utterances.js"() {
|
|
55878
|
+
"use strict";
|
|
55879
|
+
FUNNEL_UTTERANCES = {
|
|
55880
|
+
"sfi.search_components": [
|
|
55881
|
+
"is there a validation rule that stops me from saving a Contact without a Status__c?",
|
|
55882
|
+
"find me a validation rule touching Priority__c",
|
|
55883
|
+
"does a rule exist blocking Case creation if Subject is blank?",
|
|
55884
|
+
"is there a rule that fires when Opportunity.Stage = Closed Lost",
|
|
55885
|
+
"search for anything referencing <PermissionName>",
|
|
55886
|
+
"find a validation rule whose formula checks $Permission.<X>",
|
|
55887
|
+
"what custom permission does <PermSet> do \u2014 sounds like it skips a validation",
|
|
55888
|
+
"I heard we have a flow that assigns Cases to the Support queue \u2014 which one and what's the criteria?",
|
|
55889
|
+
"is there a workflow rule that emails someone when a Lead is converted?",
|
|
55890
|
+
"find anything that references the External_Id__c field on Account",
|
|
55891
|
+
"do we have a duplicate rule on Contact?",
|
|
55892
|
+
"search for all components that touch the Amount__c field",
|
|
55893
|
+
"what validation rules exist on the Order object?",
|
|
55894
|
+
"show me every component that references the <PermSet> permission set",
|
|
55895
|
+
"is there an approval process on Opportunity?",
|
|
55896
|
+
"find me anything that blocks record creation when a required field is empty",
|
|
55897
|
+
// biz-user register (router-v2 R2): existence-check with a look-inside.
|
|
55898
|
+
"does a <Object> object exist in this org? I want to see its fields",
|
|
55899
|
+
// R3 custom-permission band (DIAGNOSIS-R3 §2 rank 2 — the #1 corpus
|
|
55900
|
+
// family): describe-what-it-gates and bypass/guard phrasings. The tools
|
|
55901
|
+
// work (0.1.19 CustomPermission exposure); the vocabulary was missing.
|
|
55902
|
+
"what does the <CustomPermission> custom permission bypass exactly?",
|
|
55903
|
+
"whos allowed to bypass the validation and what is that custom permission guarding?",
|
|
55904
|
+
"which custom permission gates the <FlowName> screen flow?",
|
|
55905
|
+
"is there a custom permission that could let someone bypass a control?",
|
|
55906
|
+
"which custom permissions have descriptions that mention bypassing something?",
|
|
55907
|
+
"tell me every custom permission checked by a validation rule",
|
|
55908
|
+
// R3 cross-object field-existence band (same §2 row).
|
|
55909
|
+
"does Amount__c exist on any object, and which ones?",
|
|
55910
|
+
"do we track a completion date anywhere I could report on?",
|
|
55911
|
+
"is the <RecordType> record type only on Case or is it on other objects too?"
|
|
55912
|
+
],
|
|
55913
|
+
"sfi.resolve": [
|
|
55914
|
+
"pull the <vague-field-name> field \u2014 <PermSet> implies we have one",
|
|
55915
|
+
"i keep seeing <prefix>__ everywhere \u2014 whats that prefix",
|
|
55916
|
+
"do we have a field for tracking renewal dates?",
|
|
55917
|
+
"find me the field that stores the customer tier",
|
|
55918
|
+
"I think there's a flow called something like case assignment \u2014 look it up",
|
|
55919
|
+
"look up the thing that sounds like external identifier on Account",
|
|
55920
|
+
"we have something called Priority \u2014 is that a field or a picklist value?",
|
|
55921
|
+
"find the object that handles billing addresses",
|
|
55922
|
+
"is there a permission set that sounds like it grants read-only access to financials?",
|
|
55923
|
+
"I keep hearing about a <fuzzy-name> flow \u2014 can you find it?",
|
|
55924
|
+
"what's the canonical name for the field that stores the SLA deadline?",
|
|
55925
|
+
"look up Status__c \u2014 which object is it on?",
|
|
55926
|
+
"do we have anything for tracking escalation dates on Case?",
|
|
55927
|
+
// biz-user register (router-v2 R2): nickname-existence and
|
|
55928
|
+
// component-type-confusion phrasings ("is that a flow or a trigger").
|
|
55929
|
+
"I keep calling it the '<nickname>' object \u2014 does this org have something like that?",
|
|
55930
|
+
"is <ComponentName> a flow or a trigger? I can't tell from the name",
|
|
55931
|
+
"what type of component is <ComponentName> \u2014 an Apex class or a flow?",
|
|
55932
|
+
// R3 existence/verification/typo band (DIAGNOSIS-R3 §2 rank 1 +
|
|
55933
|
+
// trap-122-a): explicit "does X exist" asks, prove-it challenges, and
|
|
55934
|
+
// is-that-the-right-name verification.
|
|
55935
|
+
"does <Field__c> exist on Contact or only on Lead?",
|
|
55936
|
+
"is <ComponentName> even the right name? prove it actually exists",
|
|
55937
|
+
"wait, is <Name__c> a field or an object?",
|
|
55938
|
+
"pull up <ComponentName> \u2014 I might have the name spelled wrong",
|
|
55939
|
+
"does the <ComponentName> component actually exist in this org, yes or no?"
|
|
55940
|
+
],
|
|
55941
|
+
"sfi.capabilities": [
|
|
55942
|
+
"what can you actually do?",
|
|
55943
|
+
"what tools do you have available?",
|
|
55944
|
+
"give me a rundown of everything I can ask you about",
|
|
55945
|
+
"what kinds of questions can you answer about this org?",
|
|
55946
|
+
"what's your capability list?",
|
|
55947
|
+
"I'm new here \u2014 what does this thing do?",
|
|
55948
|
+
"show me the full menu of what you support"
|
|
55949
|
+
],
|
|
55950
|
+
"sfi.list_analyses": [
|
|
55951
|
+
"what analyses have been run on this org?",
|
|
55952
|
+
"show me all saved analyses",
|
|
55953
|
+
"which analysis reports are available?",
|
|
55954
|
+
"list the pre-built analyses I can run",
|
|
55955
|
+
"what analysis types does this support?",
|
|
55956
|
+
"what named analyses can I run against this org?"
|
|
55957
|
+
],
|
|
55958
|
+
"sfi.describe_analysis": [
|
|
55959
|
+
"what does the tech debt analysis actually check?",
|
|
55960
|
+
"explain what the permission risk analysis looks at",
|
|
55961
|
+
"describe what the coverage report covers",
|
|
55962
|
+
"what does the org risk report measure exactly?",
|
|
55963
|
+
"what inputs does the automation risk analysis need?",
|
|
55964
|
+
"how does the code quality audit work under the hood?"
|
|
55965
|
+
],
|
|
55966
|
+
"sfi.run_analysis": [
|
|
55967
|
+
"run the tech debt analysis now",
|
|
55968
|
+
"kick off the permission risk report",
|
|
55969
|
+
"execute the automation risk analysis",
|
|
55970
|
+
"can you run the coverage report for me?",
|
|
55971
|
+
"start the org risk report analysis",
|
|
55972
|
+
"go ahead and run the coverage report analysis"
|
|
55973
|
+
],
|
|
55974
|
+
"sfi.synthesize_answer": [
|
|
55975
|
+
"can you pull together everything you know about how Case assignment works?",
|
|
55976
|
+
"synthesize an answer about our sharing model for Opportunity",
|
|
55977
|
+
"give me a consolidated view of everything touching the Lead conversion process",
|
|
55978
|
+
"piece together what you know about our CPQ setup",
|
|
55979
|
+
"summarize all the relevant metadata about our Account hierarchy",
|
|
55980
|
+
"combine everything you found and give me a clean answer"
|
|
55981
|
+
],
|
|
55982
|
+
"sfi.route_question": [
|
|
55983
|
+
"I'm not sure which tool to use \u2014 who can edit the Amount__c field?",
|
|
55984
|
+
"figure out the best way to answer: why did this record change?",
|
|
55985
|
+
"where do I even start to understand why a flow failed?",
|
|
55986
|
+
"I have a broad question about our data model \u2014 route me to the right thing",
|
|
55987
|
+
"what's the best tool to find out if a field is used anywhere?",
|
|
55988
|
+
"I'm not sure which tool answers 'what runs on Contact save' \u2014 which one?"
|
|
55989
|
+
],
|
|
55990
|
+
"sfi.org_pulse": [
|
|
55991
|
+
"give me a quick health snapshot of the org",
|
|
55992
|
+
"what's the current state of this Salesforce instance?",
|
|
55993
|
+
"pulse check \u2014 is this org healthy?",
|
|
55994
|
+
"quick status: how is the org doing right now?",
|
|
55995
|
+
"is there anything alarming about this org at a glance?",
|
|
55996
|
+
"one-liner on org health",
|
|
55997
|
+
// biz-user register (router-v2 R2): "who is changing things" asked as a
|
|
55998
|
+
// people question, not a metadata question.
|
|
55999
|
+
"who has been modifying things in this org the most?",
|
|
56000
|
+
"who are the top contributors to changes in this org lately?",
|
|
56001
|
+
"who's been making the most changes to this org lately?"
|
|
56002
|
+
],
|
|
56003
|
+
"sfi.org_card": [
|
|
56004
|
+
"show me the org's basic info \u2014 edition, limits, creation date",
|
|
56005
|
+
"what edition is this org?",
|
|
56006
|
+
"give me the org ID and basic metadata",
|
|
56007
|
+
"what sandbox type is this?",
|
|
56008
|
+
"who's the org admin and what's the org name?",
|
|
56009
|
+
"pull the orientation snapshot for this org"
|
|
56010
|
+
],
|
|
56011
|
+
"sfi.fleet_find": [
|
|
56012
|
+
"find all orgs in the fleet that have the <Package> package installed",
|
|
56013
|
+
"which orgs in our fleet have Flows on the Case object?",
|
|
56014
|
+
"search across all our orgs for a specific component",
|
|
56015
|
+
"show me which orgs have this permission set configured",
|
|
56016
|
+
"which fleet orgs still have Process Builder active?",
|
|
56017
|
+
"search all registered orgs for a component called <ComponentName>"
|
|
56018
|
+
],
|
|
56019
|
+
"sfi.fleet_drift_ranking": [
|
|
56020
|
+
"which orgs in our fleet have drifted most from the baseline?",
|
|
56021
|
+
"rank our orgs by how much they've diverged from each other",
|
|
56022
|
+
"show me which fleet orgs are most out of sync",
|
|
56023
|
+
"drift ranking across our sandbox fleet",
|
|
56024
|
+
"which org has changed the most since last baseline?",
|
|
56025
|
+
"which of our orgs is most out of date and needs a refresh first?"
|
|
56026
|
+
],
|
|
56027
|
+
"sfi.get_component": [
|
|
56028
|
+
"show me the full metadata for the <FlowName> flow",
|
|
56029
|
+
"pull up the <ApexClass> class definition",
|
|
56030
|
+
"get me the raw component data for the Contact validation rule",
|
|
56031
|
+
"fetch the permission set definition for <PermSet>",
|
|
56032
|
+
"show me the layout metadata for Case",
|
|
56033
|
+
"pull the full metadata for <FlowName>",
|
|
56034
|
+
// R3 show-me-the-thing band (DIAGNOSIS-R3 §2 rank 4).
|
|
56035
|
+
"whats in the <PermSet> permission set?",
|
|
56036
|
+
"what is the <PermSet> permission set for? the name confuses me",
|
|
56037
|
+
"is the <FlowName> flow active or dormant?",
|
|
56038
|
+
"can you give me the raw metadata XML for the <CustomPermission> custom permission?",
|
|
56039
|
+
"pull up <ComponentName> so I can look at it"
|
|
56040
|
+
],
|
|
56041
|
+
"sfi.list_components": [
|
|
56042
|
+
"whats on the Account object \u2014 give me the whole field list",
|
|
56043
|
+
"list all custom fields on Contact",
|
|
56044
|
+
"show me every flow on this org",
|
|
56045
|
+
"what record types exist on Case?",
|
|
56046
|
+
"enumerate all permission sets",
|
|
56047
|
+
"show me all active validation rules on Contact",
|
|
56048
|
+
"Which duplicate matching rules exist but are inactive \u2014 deployed and forgotten?",
|
|
56049
|
+
"list every Apex class in this org",
|
|
56050
|
+
"what custom objects do we have?",
|
|
56051
|
+
"show me all the triggers on Account",
|
|
56052
|
+
"what approval processes are deployed?",
|
|
56053
|
+
"list all custom labels",
|
|
56054
|
+
"give me every email template in the org",
|
|
56055
|
+
// R3 custom-permission enumeration band (DIAGNOSIS-R3 §2 rank 6) + the
|
|
56056
|
+
// "can i see the validation rules" type-noun phrasings.
|
|
56057
|
+
"which custom permissions exist and what do they gate?",
|
|
56058
|
+
"what custom permissions are defined in this org?",
|
|
56059
|
+
"pull the full custom permission inventory for the audit workpaper",
|
|
56060
|
+
"can i see the validation rules on the <Object> object",
|
|
56061
|
+
"is there validation on the <Object> object?",
|
|
56062
|
+
"show me all the invocable apex methods"
|
|
56063
|
+
],
|
|
56064
|
+
"sfi.get_edges": [
|
|
56065
|
+
"what are the edges between <ApexClass> and the objects it touches?",
|
|
56066
|
+
"show me all dependency edges for the <FlowName> flow",
|
|
56067
|
+
"what connects to <ComponentName> in the metadata graph?",
|
|
56068
|
+
"enumerate the graph edges for this permission set",
|
|
56069
|
+
"which components does <ApexClass> directly reference?",
|
|
56070
|
+
"list all the outgoing dependencies from Account"
|
|
56071
|
+
],
|
|
56072
|
+
"sfi.get_subgraph": [
|
|
56073
|
+
"give me the dependency subgraph around the Case object",
|
|
56074
|
+
"show me everything in the graph connected to <ApexClass>",
|
|
56075
|
+
"pull the subgraph for flows touching Opportunity",
|
|
56076
|
+
"what's the local neighborhood of <ComponentName> in the dependency graph?",
|
|
56077
|
+
"give me a slice of the graph centered on <FlowName>",
|
|
56078
|
+
"show me a 2-hop neighborhood around the Contact object"
|
|
56079
|
+
],
|
|
56080
|
+
"sfi.search_apex_source": [
|
|
56081
|
+
"search the Apex code for any reference to External_Id__c",
|
|
56082
|
+
"find all Apex that calls Database.insert on Contact",
|
|
56083
|
+
"grep the codebase for 'SOQL injection'",
|
|
56084
|
+
"which Apex classes reference the <PermSet> permission?",
|
|
56085
|
+
"find Apex source that contains a hardcoded email address",
|
|
56086
|
+
"search Apex for any class that uses future methods",
|
|
56087
|
+
"look for Apex code that queries the Order object"
|
|
56088
|
+
],
|
|
56089
|
+
"sfi.search_flow_metadata": [
|
|
56090
|
+
"I heard we have a flow that assigns Cases to the Support queue \u2014 which one and what's the criteria?",
|
|
56091
|
+
"find a flow that references Priority__c",
|
|
56092
|
+
"search flow XML for a specific field value",
|
|
56093
|
+
"which flows contain a Decision element?",
|
|
56094
|
+
"find flows that call the <ApexClass> Apex class",
|
|
56095
|
+
"which flows have a Get Records step on the Account object?",
|
|
56096
|
+
"search for flows that send emails",
|
|
56097
|
+
"find every flow that creates an Opportunity",
|
|
56098
|
+
// R3 q738-class boundary recovery: custom-permission checks inside flow
|
|
56099
|
+
// XML, subflow-by-name, and field-stamping searches.
|
|
56100
|
+
"does any flow reference the <CustomPermission> custom permission to skip a validation rule?",
|
|
56101
|
+
"which custom permissions are checked inside flows?",
|
|
56102
|
+
"find the flow that fires the <SubflowName> subflow",
|
|
56103
|
+
"is there a flow that stamps <Field__c> when the record closes?"
|
|
56104
|
+
],
|
|
56105
|
+
"sfi.get_naming_convention_report": [
|
|
56106
|
+
"do our field names follow any consistent pattern?",
|
|
56107
|
+
"is there a naming convention for our custom objects?",
|
|
56108
|
+
"are our Apex class names consistent?",
|
|
56109
|
+
"what naming patterns do we use for permission sets?",
|
|
56110
|
+
"check if our flow names follow a standard",
|
|
56111
|
+
"are there naming convention violations in our org?"
|
|
56112
|
+
],
|
|
56113
|
+
"sfi.get_manifest": [
|
|
56114
|
+
"export a package.xml for all our custom components",
|
|
56115
|
+
"give me the manifest of everything in this org",
|
|
56116
|
+
"what's in the deployment manifest?",
|
|
56117
|
+
"show me the package manifest for our custom metadata",
|
|
56118
|
+
"generate a manifest I can use for a deployment",
|
|
56119
|
+
"what's in the current vault manifest?"
|
|
56120
|
+
],
|
|
56121
|
+
"sfi.coverage_report": [
|
|
56122
|
+
"what's our overall Apex test coverage?",
|
|
56123
|
+
"give me a coverage summary for all Apex classes",
|
|
56124
|
+
"which classes have the lowest test coverage?",
|
|
56125
|
+
"are we above the 75% coverage threshold?",
|
|
56126
|
+
"show me the coverage report",
|
|
56127
|
+
"which triggers have zero test coverage?",
|
|
56128
|
+
"overall code coverage percentage for this org"
|
|
56129
|
+
],
|
|
56130
|
+
"sfi.retrieve_blindspot_report": [
|
|
56131
|
+
"what metadata types are we NOT tracking in our deployments?",
|
|
56132
|
+
"show me the retrieval blindspots \u2014 what we're missing",
|
|
56133
|
+
"which metadata is deployed but not in our source control?",
|
|
56134
|
+
"what's falling through the cracks in our CI/CD pipeline?",
|
|
56135
|
+
"are there any metadata types we can't retrieve?",
|
|
56136
|
+
"what metadata is referenced but not captured in this vault?"
|
|
56137
|
+
],
|
|
56138
|
+
"sfi.health_check": [
|
|
56139
|
+
"is this org healthy?",
|
|
56140
|
+
"run a health check on the org",
|
|
56141
|
+
"what's wrong with this org?",
|
|
56142
|
+
"give me a health assessment",
|
|
56143
|
+
"check for obvious issues with this org",
|
|
56144
|
+
"is this org in good shape?"
|
|
56145
|
+
],
|
|
56146
|
+
"sfi.baseline_acknowledge": [
|
|
56147
|
+
"acknowledge the current baseline",
|
|
56148
|
+
"mark this as the new baseline",
|
|
56149
|
+
"accept the current state as baseline",
|
|
56150
|
+
"set the baseline to today's snapshot",
|
|
56151
|
+
"confirm I've reviewed the baseline findings",
|
|
56152
|
+
"suppress this heuristic finding so it stops showing up"
|
|
56153
|
+
],
|
|
56154
|
+
"sfi.baseline_status": [
|
|
56155
|
+
"what was the baseline state of the org?",
|
|
56156
|
+
"show me what changed since the baseline",
|
|
56157
|
+
"when was the last baseline taken?",
|
|
56158
|
+
"is the org still at baseline or has it drifted?",
|
|
56159
|
+
"compare current state to baseline",
|
|
56160
|
+
"which findings have been suppressed in the SAST baseline?"
|
|
56161
|
+
],
|
|
56162
|
+
"sfi.live_describe": [
|
|
56163
|
+
"describe the Case object \u2014 fields, picklists, everything",
|
|
56164
|
+
"what fields does the Contact object have in the live org?",
|
|
56165
|
+
"pull the live object description for Opportunity",
|
|
56166
|
+
"show me the actual picklist values for Status on Case right now",
|
|
56167
|
+
"describe Order to me \u2014 what fields exist in production?",
|
|
56168
|
+
"describe the Account sObject from the live org"
|
|
56169
|
+
],
|
|
56170
|
+
"sfi.live_stale_check": [
|
|
56171
|
+
"is the vault up to date or stale?",
|
|
56172
|
+
"when was the last refresh?",
|
|
56173
|
+
"how old is the cached metadata?",
|
|
56174
|
+
"is this data current or should I refresh?",
|
|
56175
|
+
"check if the vault needs refreshing",
|
|
56176
|
+
"has the live org changed since the last vault refresh?"
|
|
56177
|
+
],
|
|
56178
|
+
"sfi.live_count": [
|
|
56179
|
+
"how many Contacts are in the org?",
|
|
56180
|
+
"count all open Cases",
|
|
56181
|
+
"how many Opportunities are in Closed Won stage?",
|
|
56182
|
+
"how many Accounts exist in production?",
|
|
56183
|
+
"total number of active leads",
|
|
56184
|
+
"how many open Cases are there right now?"
|
|
56185
|
+
],
|
|
56186
|
+
"sfi.live_sample": [
|
|
56187
|
+
"show me a few sample Contact records",
|
|
56188
|
+
"give me some example Cases to look at",
|
|
56189
|
+
"pull a random sample of Opportunity records",
|
|
56190
|
+
"show me what Account data actually looks like",
|
|
56191
|
+
"grab a handful of Order records for me to inspect",
|
|
56192
|
+
"give me a sample of 10 Opportunities with Stage = Prospecting",
|
|
56193
|
+
// biz-user register (router-v2 R2): single-record lookup by an id the
|
|
56194
|
+
// user has in hand, asking for every field value.
|
|
56195
|
+
"can you pull up the specific record for id <RecordId>? I need to see all their field values",
|
|
56196
|
+
"show me one actual record with every field value filled in"
|
|
56197
|
+
],
|
|
56198
|
+
"sfi.live_field_population": [
|
|
56199
|
+
"what percentage of Contacts have the Email field filled in?",
|
|
56200
|
+
"how populated is Priority__c on Case?",
|
|
56201
|
+
"which fields on Account are mostly empty?",
|
|
56202
|
+
"what's the fill rate for Amount__c on Opportunity?",
|
|
56203
|
+
"are users actually filling in the Description field on Case?",
|
|
56204
|
+
"what percentage of Contacts have the Mobile field filled in?"
|
|
56205
|
+
],
|
|
56206
|
+
"sfi.live_group_count": [
|
|
56207
|
+
"how many users are in the Support group?",
|
|
56208
|
+
"count the members of each public group",
|
|
56209
|
+
"how many people are in the queue right now?",
|
|
56210
|
+
"show me group sizes across all public groups",
|
|
56211
|
+
"how many members does the Sales territory group have?",
|
|
56212
|
+
"how are Cases distributed by Status right now?",
|
|
56213
|
+
// biz-user register (router-v2 R2): distribution-by-field phrasing.
|
|
56214
|
+
"how are <Object> records distributed by their Type field?",
|
|
56215
|
+
"breakdown of records by picklist value \u2014 how many land in each bucket?"
|
|
56216
|
+
],
|
|
56217
|
+
"sfi.live_stale_records": [
|
|
56218
|
+
"which Cases haven't been updated in over 90 days?",
|
|
56219
|
+
"show me Opportunities that have been sitting in the same stage for months",
|
|
56220
|
+
"find Leads that are more than 6 months old and still open",
|
|
56221
|
+
"which Accounts haven't had any activity in a year?",
|
|
56222
|
+
"stale Contacts \u2014 people we haven't touched in forever",
|
|
56223
|
+
"which Accounts haven't been updated in over 180 days?"
|
|
56224
|
+
],
|
|
56225
|
+
"sfi.live_recent_activity": [
|
|
56226
|
+
"what's changed in the org in the last week?",
|
|
56227
|
+
"show me recently modified records",
|
|
56228
|
+
"what was last touched on the Contact object?",
|
|
56229
|
+
"recent activity on Opportunity \u2014 who's been updating records?",
|
|
56230
|
+
"what records have been created in the last 24 hours?",
|
|
56231
|
+
"what Leads were created in the last 7 days?"
|
|
56232
|
+
],
|
|
56233
|
+
"sfi.live_aggregate": [
|
|
56234
|
+
"what's the total pipeline value by stage?",
|
|
56235
|
+
"average Case resolution time",
|
|
56236
|
+
"sum of Amount__c on open Opportunities",
|
|
56237
|
+
"count and sum of Orders by status",
|
|
56238
|
+
"aggregate the revenue by Account region",
|
|
56239
|
+
"what's the average Opportunity Amount across open deals?"
|
|
56240
|
+
],
|
|
56241
|
+
"sfi.live_duplicate_check": [
|
|
56242
|
+
"do we have duplicate Contacts in the org?",
|
|
56243
|
+
"find duplicate Accounts by name and email",
|
|
56244
|
+
"how many duplicate Lead records do we have?",
|
|
56245
|
+
"show me potential duplicate Opportunities",
|
|
56246
|
+
"run a duplicate check on Contact",
|
|
56247
|
+
"are there any duplicate email addresses on Contact?"
|
|
56248
|
+
],
|
|
56249
|
+
"sfi.live_owner_breakdown": [
|
|
56250
|
+
"who owns the most Opportunities?",
|
|
56251
|
+
"show me Case distribution by owner",
|
|
56252
|
+
"which reps have the most open Leads?",
|
|
56253
|
+
"breakdown of Accounts by owner",
|
|
56254
|
+
"how are Contacts distributed across sales reps?",
|
|
56255
|
+
"how many Cases does each queue own right now?"
|
|
56256
|
+
],
|
|
56257
|
+
"sfi.live_storage_by_object": [
|
|
56258
|
+
"which objects are taking up the most storage?",
|
|
56259
|
+
"how much data storage is the Case object using?",
|
|
56260
|
+
"show me storage consumption broken down by object",
|
|
56261
|
+
"which objects have the most records?",
|
|
56262
|
+
"storage breakdown \u2014 where is the data?",
|
|
56263
|
+
"which objects are taking up the most storage?"
|
|
56264
|
+
],
|
|
56265
|
+
"sfi.live_org_limits": [
|
|
56266
|
+
"are we close to any governor limits?",
|
|
56267
|
+
"how much API call budget do we have left today?",
|
|
56268
|
+
"show me current org limits and usage",
|
|
56269
|
+
"are we near our data storage limit?",
|
|
56270
|
+
"what limits is this org approaching?",
|
|
56271
|
+
"how close are we to hitting any governor limits?"
|
|
56272
|
+
],
|
|
56273
|
+
"sfi.live_inactive_users": [
|
|
56274
|
+
"which users haven't logged in recently?",
|
|
56275
|
+
"show me inactive Salesforce users",
|
|
56276
|
+
"who hasn't logged in in over 90 days?",
|
|
56277
|
+
"list users that are burning licenses but not active",
|
|
56278
|
+
"find users we should probably deactivate",
|
|
56279
|
+
"who hasn't logged in for more than 60 days?"
|
|
56280
|
+
],
|
|
56281
|
+
"sfi.live_license_usage": [
|
|
56282
|
+
"how many Salesforce licenses are we using?",
|
|
56283
|
+
"show me license utilization",
|
|
56284
|
+
"how many licenses are available vs consumed?",
|
|
56285
|
+
"are we over or under our license count?",
|
|
56286
|
+
"which license types do we have and how many are used?",
|
|
56287
|
+
"how many Salesforce licenses are actually being used vs assigned?"
|
|
56288
|
+
],
|
|
56289
|
+
"sfi.live_consent": [
|
|
56290
|
+
"which Contacts have opted out of email?",
|
|
56291
|
+
"show me consent status for marketing contacts",
|
|
56292
|
+
"how many people have unsubscribed?",
|
|
56293
|
+
"what's the email opt-out rate?",
|
|
56294
|
+
"show me GDPR consent flags on Contact",
|
|
56295
|
+
"do I have live org access enabled for this session?"
|
|
56296
|
+
],
|
|
56297
|
+
"sfi.live_report_usage": [
|
|
56298
|
+
"which reports are actually being run?",
|
|
56299
|
+
"show me report usage stats",
|
|
56300
|
+
"what are the most popular reports?",
|
|
56301
|
+
"which reports have never been run?",
|
|
56302
|
+
"find reports that nobody has opened in 6 months",
|
|
56303
|
+
"which reports haven't been run in over 6 months?"
|
|
56304
|
+
],
|
|
56305
|
+
"sfi.live_folder_access": [
|
|
56306
|
+
"who has access to the Sales reports folder?",
|
|
56307
|
+
"show me report folder permissions",
|
|
56308
|
+
"which users can see the Executive dashboards folder?",
|
|
56309
|
+
"who can access the shared document library?",
|
|
56310
|
+
"folder-level access for the Finance reports",
|
|
56311
|
+
"are any report or dashboard folders publicly accessible?",
|
|
56312
|
+
// biz-user register (router-v2 R2): single-report ownership question.
|
|
56313
|
+
"the <ReportName> report \u2014 who owns it and who can run it?"
|
|
56314
|
+
],
|
|
56315
|
+
"sfi.live_email_template_usage": [
|
|
56316
|
+
"which email templates are actually being used?",
|
|
56317
|
+
"show me email template utilization stats",
|
|
56318
|
+
"are there email templates nobody is using?",
|
|
56319
|
+
"what templates are being sent most frequently?",
|
|
56320
|
+
"which email templates are dead \u2014 never triggered?",
|
|
56321
|
+
"which email templates are using Classic format instead of Lightning?"
|
|
56322
|
+
],
|
|
56323
|
+
"sfi.live_org_health": [
|
|
56324
|
+
"how healthy is this org right now \u2014 live check",
|
|
56325
|
+
"live health status of the org",
|
|
56326
|
+
"run a live health assessment",
|
|
56327
|
+
"anything actively broken in the org right now?",
|
|
56328
|
+
"live org health snapshot",
|
|
56329
|
+
"are there any failed async jobs or paused flow interviews right now?"
|
|
56330
|
+
],
|
|
56331
|
+
"sfi.live_automation_fired": [
|
|
56332
|
+
"which flows have actually fired recently?",
|
|
56333
|
+
"show me automation execution history",
|
|
56334
|
+
"has the <FlowName> flow run in the last week?",
|
|
56335
|
+
"which triggers fired yesterday?",
|
|
56336
|
+
"what automation has been active recently?",
|
|
56337
|
+
"does the <FlowName> flow actually run in production or is it dead?"
|
|
56338
|
+
],
|
|
56339
|
+
"sfi.live_picklist_usage": [
|
|
56340
|
+
"which picklist values on Status are actually being used?",
|
|
56341
|
+
"show me picklist value usage for Case.Priority",
|
|
56342
|
+
"which Stage values on Opportunity are most common?",
|
|
56343
|
+
"are there any picklist values that nobody selects?",
|
|
56344
|
+
"picklist value distribution on Contact.LeadSource",
|
|
56345
|
+
"which Stage values on Opportunity are never actually used in production?"
|
|
56346
|
+
],
|
|
56347
|
+
"sfi.live_budget": [
|
|
56348
|
+
"what's our API call usage vs budget this month?",
|
|
56349
|
+
"show me current spending against limits",
|
|
56350
|
+
"how much of our governor budget are we consuming?",
|
|
56351
|
+
"are we on track to hit any limits this month?",
|
|
56352
|
+
"live budget check \u2014 API, storage, everything",
|
|
56353
|
+
"how many live queries have I used this session?"
|
|
56354
|
+
],
|
|
56355
|
+
"sfi.org_risk_report": [
|
|
56356
|
+
"Give me the bottom line: three sentences on whether I should be confident or worried about this org going into production.",
|
|
56357
|
+
"I have a leadership offsite next week and I want to tell a clean story about the state of our Salesforce platform \u2014 the good, the bad",
|
|
56358
|
+
"what's the health of this org?",
|
|
56359
|
+
"is this org production-ready?",
|
|
56360
|
+
"what are the biggest risks in this org?",
|
|
56361
|
+
"give me a risk summary for this org",
|
|
56362
|
+
"executive summary of org risk",
|
|
56363
|
+
"what should I be worried about before we go live?",
|
|
56364
|
+
"pull together the top risks I need to present to leadership",
|
|
56365
|
+
"is there anything that would block a production release right now?",
|
|
56366
|
+
// biz-user register (router-v2 R2): quality asked as a grade.
|
|
56367
|
+
"is our Salesforce org well-architected? would you give it a passing grade?",
|
|
56368
|
+
"if an outside expert graded this org's setup, what would they flag?"
|
|
56369
|
+
],
|
|
56370
|
+
"sfi.field_cleanup_candidates": [
|
|
56371
|
+
"which fields can we safely delete?",
|
|
56372
|
+
"show me fields that are never populated",
|
|
56373
|
+
"what custom fields are candidates for cleanup?",
|
|
56374
|
+
"find fields that nobody uses and we could remove",
|
|
56375
|
+
"which deprecated fields should we clean up?",
|
|
56376
|
+
"list empty fields on Contact that are safe to remove"
|
|
56377
|
+
],
|
|
56378
|
+
"sfi.automation_risk_report": [
|
|
56379
|
+
"which of our flows and triggers are high risk?",
|
|
56380
|
+
"show me automation that could cause problems",
|
|
56381
|
+
"what's the riskiest automation in this org?",
|
|
56382
|
+
"find automations that could hit governor limits",
|
|
56383
|
+
"which flows run synchronously and could slow things down?",
|
|
56384
|
+
"automation risk overview \u2014 what should we fix first?"
|
|
56385
|
+
],
|
|
56386
|
+
"sfi.permission_risk_report": [
|
|
56387
|
+
"who has too much access in this org?",
|
|
56388
|
+
"which users have Modify All permissions they shouldn't?",
|
|
56389
|
+
"show me permission risks \u2014 overprivileged users",
|
|
56390
|
+
"find profiles that have more access than they need",
|
|
56391
|
+
"which permission sets grant dangerous access?",
|
|
56392
|
+
"security risks from permissions \u2014 give me the highlights"
|
|
56393
|
+
],
|
|
56394
|
+
"sfi.release_readiness_report": [
|
|
56395
|
+
"is this org ready for the next release?",
|
|
56396
|
+
"give me a release readiness assessment",
|
|
56397
|
+
"what blockers exist before we can deploy?",
|
|
56398
|
+
"are we ready to promote to production?",
|
|
56399
|
+
"show me the release readiness checklist",
|
|
56400
|
+
"what do we need to fix before the next release?"
|
|
56401
|
+
],
|
|
56402
|
+
"sfi.get_impact": [
|
|
56403
|
+
"what's the impact of changing <ComponentName>?",
|
|
56404
|
+
"show me what gets affected if I delete <Field__c>",
|
|
56405
|
+
"impact analysis for modifying the <FlowName> flow",
|
|
56406
|
+
"what would break if I change <ApexClass>?",
|
|
56407
|
+
"downstream impact of removing <ComponentName>",
|
|
56408
|
+
"what breaks if I delete the <Field__c> field on Account?"
|
|
56409
|
+
],
|
|
56410
|
+
"sfi.blast_radius_live": [
|
|
56411
|
+
"blast radius of changing <Object>.<Field> from currency to text",
|
|
56412
|
+
"live blast radius if I deactivate <FlowName>",
|
|
56413
|
+
"how many records would be affected if I change this field?",
|
|
56414
|
+
"what's the blast radius of making Priority__c required on Case?",
|
|
56415
|
+
"if I change this picklist value, how many records does that hit?",
|
|
56416
|
+
"how many records would be affected if I remove the External_Id__c field?"
|
|
56417
|
+
],
|
|
56418
|
+
"sfi.find_formula_references": [
|
|
56419
|
+
"which formula fields reference Priority__c?",
|
|
56420
|
+
"find all formulas that use the Amount__c field",
|
|
56421
|
+
"what formulas reference the Status field on Case?",
|
|
56422
|
+
"are there any formulas that read from External_Id__c?",
|
|
56423
|
+
"which roll-up summary fields reference Order.Amount__c?",
|
|
56424
|
+
"which formula fields reference the Amount__c field on Opportunity?"
|
|
56425
|
+
],
|
|
56426
|
+
"sfi.find_apex_usages": [
|
|
56427
|
+
"where is <ApexClass> used in other Apex code?",
|
|
56428
|
+
"find all callers of the <Method> method",
|
|
56429
|
+
"which Apex classes instantiate <ApexClass>?",
|
|
56430
|
+
"what code calls into the <ApexClass> service class?",
|
|
56431
|
+
"who uses the <UtilityClass> utility class?",
|
|
56432
|
+
"which Apex classes read or write to the Status__c field on Case?"
|
|
56433
|
+
],
|
|
56434
|
+
"sfi.effective_permissions": [
|
|
56435
|
+
"does the <Profile> profile have access to the Account object?",
|
|
56436
|
+
"Lead convert failed with 'missing Edit permission' \u2014 whose perm is that?",
|
|
56437
|
+
"what can <Profile> actually do \u2014 union of profile plus all their perm sets?",
|
|
56438
|
+
"combine <Profile> and <PermSet> and show me the effective access",
|
|
56439
|
+
"does assigning <PermSet> override the profile restriction on Case?",
|
|
56440
|
+
"what's the max-wins access for this user?",
|
|
56441
|
+
"can the Sales profile create Opportunity records?",
|
|
56442
|
+
"show me the effective field-level security for <Profile> on Contact",
|
|
56443
|
+
"what's the net permission for a user with <Profile> and <PermSet>?",
|
|
56444
|
+
"does this user actually have delete access on Account?",
|
|
56445
|
+
"what objects can the <Profile> profile read?",
|
|
56446
|
+
"effective read/write/delete access for <Profile> on Order",
|
|
56447
|
+
// R3 PSG-expansion band (DIAGNOSIS-R3 permissions family): what a member
|
|
56448
|
+
// of a permission set GROUP ends up with — effective_permissions expands
|
|
56449
|
+
// PSGs (verified). The genuinely-unanswerable directions stay gapped:
|
|
56450
|
+
// "which USERS are in the group" (permset-user-roster) and "which PSG
|
|
56451
|
+
// grants X" (permset-group-grants) are capability-gap intents, untouched.
|
|
56452
|
+
"what does a user get through the <PermSetGroup> permission set group?",
|
|
56453
|
+
"expand the <PermSetGroup> permission set group \u2014 every permission a member ends up with",
|
|
56454
|
+
"if someone is assigned the <PermSetGroup> permission set group, do they automatically get <CustomPermission>?",
|
|
56455
|
+
"what custom permissions does a new hire end up with after all their permission set group assignments?",
|
|
56456
|
+
"can a user in the <PermSetGroup> permission set group edit these records?",
|
|
56457
|
+
"walk me through how permission set group inheritance works here, with a real group as the example"
|
|
56458
|
+
],
|
|
56459
|
+
"sfi.who_can_run": [
|
|
56460
|
+
"who can run the <Report> report?",
|
|
56461
|
+
"which users are allowed to execute <FlowName>?",
|
|
56462
|
+
"who has permission to run this Apex class?",
|
|
56463
|
+
"which profiles can see the Sales dashboard?",
|
|
56464
|
+
"who can actually execute this scheduled job?",
|
|
56465
|
+
"which profiles have permission to run the <FlowName> flow?"
|
|
56466
|
+
],
|
|
56467
|
+
"sfi.who_can_access_object": [
|
|
56468
|
+
"who can access the Order object?",
|
|
56469
|
+
"which profiles and perm sets give read access to Case?",
|
|
56470
|
+
"who can see custom object <ObjectName>?",
|
|
56471
|
+
"which users have access to the financial data object?",
|
|
56472
|
+
"show me everyone who can view Account records",
|
|
56473
|
+
"who can see records on the Order object?"
|
|
56474
|
+
],
|
|
56475
|
+
"sfi.why_cant_user_see_record": [
|
|
56476
|
+
"user can't see the Case record \u2014 why?",
|
|
56477
|
+
"John's not seeing the Account \u2014 what's blocking them?",
|
|
56478
|
+
"a user is getting access denied on Opportunity \u2014 what's wrong?",
|
|
56479
|
+
"why is this record invisible to a specific user?",
|
|
56480
|
+
"I need to figure out why a user can't open this record",
|
|
56481
|
+
"user says they get a 'No access' error on a Contact record \u2014 what gives?",
|
|
56482
|
+
// R3 access-diagnostics band (DIAGNOSIS-R3 permissions family): symptom
|
|
56483
|
+
// phrasings — greyed-out fields, blocked edits, blocked reassignment.
|
|
56484
|
+
"a user cant edit the <Field__c> field on a record, they say its greyed out \u2014 why?",
|
|
56485
|
+
"why cant a user with the <Profile> profile reassign the Case owner?",
|
|
56486
|
+
"a user reports the record is read-only for them \u2014 which layer blocks the edit?"
|
|
56487
|
+
],
|
|
56488
|
+
"sfi.layout_for_user": [
|
|
56489
|
+
"what page layout does <Profile> see on Case?",
|
|
56490
|
+
"which layout gets shown to the Sales rep on Opportunity?",
|
|
56491
|
+
"show me the layout assignment for the <Profile> profile on Contact",
|
|
56492
|
+
"which record type and layout does this user get on Account?",
|
|
56493
|
+
"what layout will a support agent see on a Case?",
|
|
56494
|
+
"which page layout does the <Profile> profile see on Contact?"
|
|
56495
|
+
],
|
|
56496
|
+
"sfi.user_ability": [
|
|
56497
|
+
"can <Username> edit Cases?",
|
|
56498
|
+
"does this user have permission to delete Contacts?",
|
|
56499
|
+
"what can a specific user actually do in the org?",
|
|
56500
|
+
"check if <Username> can create Opportunities",
|
|
56501
|
+
"what record types can this user access on Case?",
|
|
56502
|
+
"what flows can users with the <Profile> profile actually run?",
|
|
56503
|
+
// R3 access-diagnostics band: missing-button / failing-action symptoms.
|
|
56504
|
+
"a user says the Convert button is missing on a Lead \u2014 why?",
|
|
56505
|
+
"why does the send action fail for one user \u2014 do they need the <PermSet> permission set?",
|
|
56506
|
+
"which permission is this user missing to perform the action?"
|
|
56507
|
+
],
|
|
56508
|
+
"sfi.profile_security": [
|
|
56509
|
+
"how locked down is the <Profile> profile?",
|
|
56510
|
+
"show me the security settings for the Sales profile",
|
|
56511
|
+
"does <Profile> have any dangerous permissions like Modify All?",
|
|
56512
|
+
"what system permissions does the <Profile> profile have?",
|
|
56513
|
+
"security review for the support agent profile",
|
|
56514
|
+
"which profiles have Apex execute permissions?"
|
|
56515
|
+
],
|
|
56516
|
+
"sfi.lightning_pages": [
|
|
56517
|
+
"what Lightning pages exist for the Case object?",
|
|
56518
|
+
"show me all App Builder pages",
|
|
56519
|
+
"which Lightning pages use the <Component> component?",
|
|
56520
|
+
"what's on the Account record page?",
|
|
56521
|
+
"list all Lightning Experience record pages",
|
|
56522
|
+
"which Lightning record pages are assigned to the Case object?"
|
|
56523
|
+
],
|
|
56524
|
+
"sfi.list_view_sharing": [
|
|
56525
|
+
"who can see the 'My Open Cases' list view?",
|
|
56526
|
+
"which list views on Contact are visible to everyone?",
|
|
56527
|
+
"show me sharing settings for list views on Opportunity",
|
|
56528
|
+
"who has access to the Sales team list view?",
|
|
56529
|
+
"list views on Account \u2014 who can see which ones?",
|
|
56530
|
+
"who can see the 'My Open Cases' list view on Case?"
|
|
56531
|
+
],
|
|
56532
|
+
"sfi.app_access": [
|
|
56533
|
+
"which users can see the Sales Cloud app?",
|
|
56534
|
+
"who has access to the Service Console?",
|
|
56535
|
+
"what apps is the <Profile> profile allowed to use?",
|
|
56536
|
+
"which Lightning apps are available to the support team?",
|
|
56537
|
+
"show me app access by profile",
|
|
56538
|
+
"which profiles can open the Service app?"
|
|
56539
|
+
],
|
|
56540
|
+
"sfi.tab_availability": [
|
|
56541
|
+
"which tabs can the <Profile> profile see?",
|
|
56542
|
+
"is the Order tab visible to sales reps?",
|
|
56543
|
+
"which objects have tabs hidden from the support profile?",
|
|
56544
|
+
"show me tab settings for the <Profile> profile",
|
|
56545
|
+
"which tabs are set to 'Default Off' for a specific profile?",
|
|
56546
|
+
"which tabs are visible to users with the <Profile> profile?",
|
|
56547
|
+
// biz-user register (router-v2 R2): imperative "list its tabs" shape.
|
|
56548
|
+
"the <Profile> profile \u2014 what does it have access to? list its tabs"
|
|
56549
|
+
],
|
|
56550
|
+
"sfi.lifecycle_process": [
|
|
56551
|
+
"what's the Case lifecycle \u2014 statuses and transitions?",
|
|
56552
|
+
"show me the Opportunity stage progression",
|
|
56553
|
+
"what are the valid Lead status transitions?",
|
|
56554
|
+
"describe the record lifecycle for Order",
|
|
56555
|
+
"what's the path an Account goes through from creation to active?",
|
|
56556
|
+
"what happens when an Opportunity moves to Closed Won?",
|
|
56557
|
+
// biz-user register (router-v2 R2): status-transition asked as "what
|
|
56558
|
+
// kicks in when the value flips".
|
|
56559
|
+
"when the Status field flips to Resolved, what processes kick in for that value transition?",
|
|
56560
|
+
"what happens behind the scenes when a record moves to a new stage?"
|
|
56561
|
+
],
|
|
56562
|
+
"sfi.layout_assignments": [
|
|
56563
|
+
"which layouts are assigned to which profiles on Case?",
|
|
56564
|
+
"show me all layout assignments for Contact",
|
|
56565
|
+
"what layout does the admin profile use on Account?",
|
|
56566
|
+
"layout to profile mapping on Opportunity",
|
|
56567
|
+
"which record type gets which layout on Case?",
|
|
56568
|
+
"which profiles are assigned to the Account Layout page layout?"
|
|
56569
|
+
],
|
|
56570
|
+
"sfi.integration_map": [
|
|
56571
|
+
"what external systems does this org integrate with?",
|
|
56572
|
+
"show me all the integrations in this org",
|
|
56573
|
+
"what APIs does this Salesforce instance call out to?",
|
|
56574
|
+
"which third-party systems are connected?",
|
|
56575
|
+
"integration topology \u2014 what talks to what?",
|
|
56576
|
+
"what external systems is this org connected to?"
|
|
56577
|
+
],
|
|
56578
|
+
"sfi.event_subscribers": [
|
|
56579
|
+
"what's subscribed to the Case platform event?",
|
|
56580
|
+
"who listens to <PlatformEvent__e>?",
|
|
56581
|
+
"show me all event subscribers in this org",
|
|
56582
|
+
"which flows or Apex classes consume platform events?",
|
|
56583
|
+
"what processes trigger off <EventName__e>?",
|
|
56584
|
+
"who listens to the <Event__e> platform event?"
|
|
56585
|
+
],
|
|
56586
|
+
"sfi.guidance": [
|
|
56587
|
+
"how should I think about cleaning up this org?",
|
|
56588
|
+
"what's the recommended approach for migrating Process Builder flows?",
|
|
56589
|
+
"guide me through reducing technical debt in our Apex",
|
|
56590
|
+
"what's best practice for permission set management?",
|
|
56591
|
+
"how do I approach a security review in Salesforce?",
|
|
56592
|
+
"what's the recommended approach for building automation on Salesforce \u2014 Flow vs Apex?"
|
|
56593
|
+
],
|
|
56594
|
+
"sfi.find_code_usages": [
|
|
56595
|
+
"where is <ClassName> referenced in the codebase?",
|
|
56596
|
+
"find all usages of <MethodName>",
|
|
56597
|
+
"which components use <CustomLabel>?",
|
|
56598
|
+
"where is the <ApexClass> class called from?",
|
|
56599
|
+
"find everything that references this utility class",
|
|
56600
|
+
"which LWC components reference the Account.AnnualRevenue field?"
|
|
56601
|
+
],
|
|
56602
|
+
"sfi.lookup_record": [
|
|
56603
|
+
"look up the Case record with ID 5001234",
|
|
56604
|
+
"find the Account named Acme Corp",
|
|
56605
|
+
"pull up the Contact with this email address",
|
|
56606
|
+
"look up Order number 1234",
|
|
56607
|
+
"fetch the record for this Opportunity",
|
|
56608
|
+
"what are the values in the <MetadataRecord__mdt> custom metadata record?"
|
|
56609
|
+
],
|
|
56610
|
+
"sfi.explain_field": [
|
|
56611
|
+
"what does the External_Id__c field on Contact mean?",
|
|
56612
|
+
"explain what Priority__c is used for on Case",
|
|
56613
|
+
"what's the purpose of Amount__c on Opportunity?",
|
|
56614
|
+
"what does this custom field do and where does it come from?",
|
|
56615
|
+
"explain the business meaning of Status__c on Order",
|
|
56616
|
+
"what are the picklist values on Opportunity.Stage?"
|
|
56617
|
+
],
|
|
56618
|
+
"sfi.safe_to_delete_field": [
|
|
56619
|
+
"is it safe to delete Priority__c?",
|
|
56620
|
+
"can I remove this field without breaking anything?",
|
|
56621
|
+
"what would break if I deleted External_Id__c?",
|
|
56622
|
+
"is <Field__c> safe to delete?",
|
|
56623
|
+
"which fields are safe to remove from Contact?",
|
|
56624
|
+
"I want to clean up unused fields \u2014 is this one safe to drop?"
|
|
56625
|
+
],
|
|
56626
|
+
"sfi.unused_components": [
|
|
56627
|
+
"which components in this org are not used anywhere?",
|
|
56628
|
+
"find all dead code \u2014 Apex classes that are never called",
|
|
56629
|
+
"what custom objects have no records and aren't referenced?",
|
|
56630
|
+
"show me unused permission sets",
|
|
56631
|
+
"which flows are deployed but never run?",
|
|
56632
|
+
"dead components \u2014 Apex, flows, whatever \u2014 give me the list"
|
|
56633
|
+
],
|
|
56634
|
+
"sfi.find_dependency_cycles": [
|
|
56635
|
+
"whats the dependency cycle situation between the Course objects \u2014 triggers on <Object1>, <Object2>, <Object3>",
|
|
56636
|
+
"we have <Object1> and <Object2> \u2014 are they in a dependency loop with each other through triggers or flows?",
|
|
56637
|
+
"which Apex classes call each other in a circle?",
|
|
56638
|
+
"is there a circular dependency in our trigger code?",
|
|
56639
|
+
"do any triggers chain back into themselves?",
|
|
56640
|
+
"find recursive call chains in Apex",
|
|
56641
|
+
"do we have any circular flow invocations?",
|
|
56642
|
+
"which flows call each other in a loop?",
|
|
56643
|
+
"check for circular dependencies in the automation layer",
|
|
56644
|
+
"are there any infinite loops possible in our trigger/flow setup?"
|
|
56645
|
+
],
|
|
56646
|
+
"sfi.apex_test_coverage": [
|
|
56647
|
+
"what's the test coverage for <ApexClass>?",
|
|
56648
|
+
"show me line-level coverage for the <TriggerName> trigger",
|
|
56649
|
+
"which lines in <ApexClass> are not covered by tests?",
|
|
56650
|
+
"does <ApexClass> meet the 75% coverage threshold?",
|
|
56651
|
+
"what's the coverage breakdown for the order management class?",
|
|
56652
|
+
"which Apex classes have no test coverage at all?"
|
|
56653
|
+
],
|
|
56654
|
+
"sfi.automation_build_advisor": [
|
|
56655
|
+
"should I build this as a flow or a trigger?",
|
|
56656
|
+
"what's the recommended automation approach for updating Case on Contact change?",
|
|
56657
|
+
"I need to auto-populate a field on save \u2014 flow or Apex?",
|
|
56658
|
+
"advise me on the best way to automate account territory assignment",
|
|
56659
|
+
"how should I implement this business rule \u2014 flow, trigger, or process builder?",
|
|
56660
|
+
"before I add a new flow on Account, what already runs there?"
|
|
56661
|
+
],
|
|
56662
|
+
"sfi.apex_build_advisor": [
|
|
56663
|
+
"how should I structure this Apex class?",
|
|
56664
|
+
"best practice for writing a trigger on Case?",
|
|
56665
|
+
"I'm building a batch job \u2014 any guidance?",
|
|
56666
|
+
"recommend an Apex pattern for async processing of Orders",
|
|
56667
|
+
"how do I avoid governor limits in this Apex scenario?",
|
|
56668
|
+
"before I write Apex for Opportunity, what patterns does existing code use?"
|
|
56669
|
+
],
|
|
56670
|
+
"sfi.field_change_advisor": [
|
|
56671
|
+
"I want to add a new required field to Case \u2014 what should I consider?",
|
|
56672
|
+
"advise me on changing the type of Amount__c",
|
|
56673
|
+
"what do I need to think about before adding a lookup field to Contact?",
|
|
56674
|
+
"guidance on making External_Id__c a unique field",
|
|
56675
|
+
"field change advisory for renaming Status__c",
|
|
56676
|
+
"give me the full risk briefing before I modify the Priority__c field"
|
|
56677
|
+
],
|
|
56678
|
+
"sfi.what_if_change_field_value": [
|
|
56679
|
+
"what happens if I change the Stage to Closed Won on these records?",
|
|
56680
|
+
"if I bulk-update Priority__c on all open Cases to High, what automation fires?",
|
|
56681
|
+
"what changes downstream when I set Status to Active on an Account?",
|
|
56682
|
+
"what would happen if someone changes the Amount__c to zero on an Opportunity?",
|
|
56683
|
+
"simulate changing Contact.LeadSource from Web to Partner",
|
|
56684
|
+
"if someone changes the External_Id__c value on an Account record, what integration breaks?"
|
|
56685
|
+
],
|
|
56686
|
+
"sfi.value_change_audit": [
|
|
56687
|
+
"who changed the Status__c field on this Case and when?",
|
|
56688
|
+
"show me the history of changes to Amount__c on Opportunity",
|
|
56689
|
+
"audit trail for Priority__c changes on Case",
|
|
56690
|
+
"which users are changing Stage on Opportunity most frequently?",
|
|
56691
|
+
"show me all the value changes to External_Id__c in the last month",
|
|
56692
|
+
"which fields on Contact are risky to change values on?"
|
|
56693
|
+
],
|
|
56694
|
+
"sfi.live_drift_check": [
|
|
56695
|
+
"has anything changed in the live org since the last vault refresh?",
|
|
56696
|
+
"check if the live org has drifted from the cached metadata",
|
|
56697
|
+
"are there new fields in production that aren't in our vault?",
|
|
56698
|
+
"live drift check \u2014 anything deployed since the last snapshot?",
|
|
56699
|
+
"compare live org to our stored metadata",
|
|
56700
|
+
"has the live org added or removed fields on Account since the last refresh?"
|
|
56701
|
+
],
|
|
56702
|
+
"sfi.what_changed_since_refresh": [
|
|
56703
|
+
"what's been deployed since the last refresh?",
|
|
56704
|
+
"show me changes since we last pulled the org metadata",
|
|
56705
|
+
"what metadata has changed since the vault was updated?",
|
|
56706
|
+
"anything new or modified since the last snapshot?",
|
|
56707
|
+
"delta since last refresh \u2014 what changed?",
|
|
56708
|
+
"what types of components changed in the last refresh?"
|
|
56709
|
+
],
|
|
56710
|
+
"sfi.org_history": [
|
|
56711
|
+
"show me the change history for this org",
|
|
56712
|
+
"what major changes have happened to this org over time?",
|
|
56713
|
+
"org changelog \u2014 what's been modified recently?",
|
|
56714
|
+
"historical view of org changes",
|
|
56715
|
+
"what was deployed to this org in the last quarter?",
|
|
56716
|
+
"show me the refresh history for this org"
|
|
56717
|
+
],
|
|
56718
|
+
"sfi.diff_snapshots": [
|
|
56719
|
+
"compare the current org snapshot to last month's",
|
|
56720
|
+
"diff between the two most recent vault snapshots",
|
|
56721
|
+
"what changed between snapshot A and snapshot B?",
|
|
56722
|
+
"show me the diff between our production and sandbox snapshots",
|
|
56723
|
+
"compare two org states and tell me what's different",
|
|
56724
|
+
"compare the pre-release snapshot to the post-release snapshot"
|
|
56725
|
+
],
|
|
56726
|
+
"sfi.churn": [
|
|
56727
|
+
"which components change most frequently?",
|
|
56728
|
+
"show me high-churn metadata \u2014 things we keep modifying",
|
|
56729
|
+
"what parts of the org are in constant flux?",
|
|
56730
|
+
"which Apex classes get touched every sprint?",
|
|
56731
|
+
"high-churn fields on Case \u2014 what keeps changing?",
|
|
56732
|
+
"which components had the most changes between these two snapshots?"
|
|
56733
|
+
],
|
|
56734
|
+
"sfi.trend": [
|
|
56735
|
+
"how has the number of Cases grown over time?",
|
|
56736
|
+
"show me the trend in Opportunity creation",
|
|
56737
|
+
"is our lead volume increasing or decreasing?",
|
|
56738
|
+
"trend analysis for Account record creation",
|
|
56739
|
+
"how has our automation complexity changed over the last year?",
|
|
56740
|
+
"show me component count growth over time across all snapshots"
|
|
56741
|
+
],
|
|
56742
|
+
"sfi.compare_components": [
|
|
56743
|
+
"compare the Contact layout between sandbox and production",
|
|
56744
|
+
"show me the diff between <FlowName> v1 and v2",
|
|
56745
|
+
"compare the Case validation rules in two orgs",
|
|
56746
|
+
"what's different between the <Profile> profile in sandbox vs prod?",
|
|
56747
|
+
"compare the <ApexClass> class across environments",
|
|
56748
|
+
"are these two record types actually different or just named differently?",
|
|
56749
|
+
// biz-user register (router-v2 R2): duplicate-automation confusion.
|
|
56750
|
+
"why do we have two triggers on the same object \u2014 what does each one do differently?"
|
|
56751
|
+
],
|
|
56752
|
+
"sfi.export_manifest": [
|
|
56753
|
+
"export the full org manifest",
|
|
56754
|
+
"create a package.xml with all custom components",
|
|
56755
|
+
"export a deployment manifest for the custom objects",
|
|
56756
|
+
"give me a manifest I can use to retrieve this org's metadata",
|
|
56757
|
+
"export all custom Apex and flows to a manifest",
|
|
56758
|
+
"generate a package.xml for these components so I can deploy them"
|
|
56759
|
+
],
|
|
56760
|
+
"sfi.pii_inventory": [
|
|
56761
|
+
"which fields in this org store personally identifiable information?",
|
|
56762
|
+
"show me all PII fields \u2014 SSN, email, phone, etc.",
|
|
56763
|
+
"GDPR compliance \u2014 which fields hold personal data?",
|
|
56764
|
+
"inventory of fields that could contain sensitive customer data",
|
|
56765
|
+
"which objects store PII that we need to mask in sandboxes?",
|
|
56766
|
+
"which fields in this org store personally identifiable information?"
|
|
56767
|
+
],
|
|
56768
|
+
"sfi.field_access_audit": [
|
|
56769
|
+
"who can read and write the Amount__c field?",
|
|
56770
|
+
"which profiles can edit SSN or sensitive financial fields?",
|
|
56771
|
+
"show me field-level security for Priority__c across all profiles",
|
|
56772
|
+
"who has write access to External_Id__c?",
|
|
56773
|
+
"field access audit for Status__c on Case",
|
|
56774
|
+
"who has read access to the SSN field on Contact?"
|
|
56775
|
+
],
|
|
56776
|
+
"sfi.object_access_audit": [
|
|
56777
|
+
"which profiles can create, read, edit, delete Order records?",
|
|
56778
|
+
"show me CRUD permissions on Case by profile",
|
|
56779
|
+
"who has delete access to Account?",
|
|
56780
|
+
"object-level permission audit for Contact",
|
|
56781
|
+
"show me which profiles have full CRUD on custom object <ObjectName>",
|
|
56782
|
+
"which profiles have Create access on the Case object?",
|
|
56783
|
+
// biz-user register (router-v2 R2): group-membership create-access ask.
|
|
56784
|
+
"can members of <GroupName> create <Object> records, and where does that access come from?"
|
|
56785
|
+
],
|
|
56786
|
+
"sfi.recordtype_availability": [
|
|
56787
|
+
"which record types on Case are available to the Support profile?",
|
|
56788
|
+
"which record types does the <Profile> profile have access to on Opportunity?",
|
|
56789
|
+
"show me record type availability on Account by profile",
|
|
56790
|
+
"can the sales rep create a Partner Account record type?",
|
|
56791
|
+
"which record types on Contact are visible to the marketing team?",
|
|
56792
|
+
"which record types can the <Profile> profile create on Case?"
|
|
56793
|
+
],
|
|
56794
|
+
"sfi.org_overview": [
|
|
56795
|
+
"break down the codebase footprint for me \u2014 LWC vs Aura vs Visualforce, and how much legacy VF debt is there",
|
|
56796
|
+
"how many custom objects does this org have?",
|
|
56797
|
+
"what are the top objects in this org?",
|
|
56798
|
+
"give me a tour of this Salesforce org",
|
|
56799
|
+
"what's in this org?",
|
|
56800
|
+
"quick inventory of the codebase",
|
|
56801
|
+
"org summary \u2014 what are we working with?",
|
|
56802
|
+
"high-level overview of this Salesforce instance",
|
|
56803
|
+
"describe this org to me like I just joined the team",
|
|
56804
|
+
"how big is this org \u2014 fields, flows, classes, the works?",
|
|
56805
|
+
"what's the overall complexity of this org?",
|
|
56806
|
+
"give me a lay of the land for this Salesforce environment",
|
|
56807
|
+
// biz-user register (router-v2 R2): the new-joiner altitude idioms.
|
|
56808
|
+
"I just joined this team \u2014 what's the 10,000-foot view of what's in this Salesforce org?",
|
|
56809
|
+
"give me the bird's-eye view of this org for a newcomer"
|
|
56810
|
+
],
|
|
56811
|
+
"sfi.domain_clusters": [
|
|
56812
|
+
"what functional areas does this org cover?",
|
|
56813
|
+
"show me the business domain clusters in this Salesforce org",
|
|
56814
|
+
"how is the metadata organized by business function?",
|
|
56815
|
+
"what logical groups of objects exist in this org?",
|
|
56816
|
+
"cluster the org's objects by business domain",
|
|
56817
|
+
"can you group the objects in this org into logical domain areas?"
|
|
56818
|
+
],
|
|
56819
|
+
"sfi.changed_since": [
|
|
56820
|
+
"what changed in the org since last Tuesday?",
|
|
56821
|
+
"show me everything modified after January 1st",
|
|
56822
|
+
"what metadata was updated this week?",
|
|
56823
|
+
"changes since the last release",
|
|
56824
|
+
"what's been modified since the last sprint?",
|
|
56825
|
+
"what components were modified after 2024-01-01?"
|
|
56826
|
+
],
|
|
56827
|
+
"sfi.last_modified": [
|
|
56828
|
+
"who last modified the <FlowName> flow?",
|
|
56829
|
+
"when was the Case layout last changed?",
|
|
56830
|
+
"who touched the <ApexClass> class most recently?",
|
|
56831
|
+
"when was Priority__c last updated?",
|
|
56832
|
+
"last modified date for all custom fields on Contact",
|
|
56833
|
+
"when was the Account trigger last changed and by whom?"
|
|
56834
|
+
],
|
|
56835
|
+
"sfi.what_happens_on_save": [
|
|
56836
|
+
"I need everything that touches Case on save \u2014 flows, triggers, PB, validation. Full save cascade please.",
|
|
56837
|
+
"Ugh, we keep hitting record-lock errors \u2014 which of our automations run concurrently on Contact and could be colliding?",
|
|
56838
|
+
"give me a rundown of every automation that involves a Case",
|
|
56839
|
+
"what runs when I create a new Opportunity record?",
|
|
56840
|
+
"what automation fires on Account update?",
|
|
56841
|
+
"what triggers exist on Contact?",
|
|
56842
|
+
"if I save an Opportunity, what order does everything fire in?",
|
|
56843
|
+
"is there any automation on Order I should know about before adding more?",
|
|
56844
|
+
"walk me through everything Salesforce does when I hit Save on a Case",
|
|
56845
|
+
"before I add a new flow to Contact, what else is already running on save?",
|
|
56846
|
+
"which flows fire synchronously on Lead save?",
|
|
56847
|
+
"complete save-event automation inventory for the Account object",
|
|
56848
|
+
"what happens behind the scenes when I create a new Contact?",
|
|
56849
|
+
// biz-user register (router-v2 R2): terse "show me its automation" shape.
|
|
56850
|
+
"the <Object> object \u2014 can you show me its automation?",
|
|
56851
|
+
// R3 save-cascade band (DIAGNOSIS-R3 §2 rank 7): terse "whats on save",
|
|
56852
|
+
// event-split phrasings, apex-on-insert, and the automation footprint.
|
|
56853
|
+
"whats on save for the <Object> object?",
|
|
56854
|
+
"how many automations fire when a record gets saved?",
|
|
56855
|
+
"what apex runs on Contact insert?",
|
|
56856
|
+
"list the flows that run on Contact insert",
|
|
56857
|
+
"which flows are triggered by Case creation vs Case update?",
|
|
56858
|
+
"whats the automation footprint on the <Object> object \u2014 flows, triggers, everything together?",
|
|
56859
|
+
"why did the flow not fire on my lead update \u2014 what is even wired to Lead saves?",
|
|
56860
|
+
"does anything on <Object> save update a date field automatically?"
|
|
56861
|
+
],
|
|
56862
|
+
"sfi.why_field_changed": [
|
|
56863
|
+
"which flows write to Contact.Status__c?",
|
|
56864
|
+
"what sets the value of Opportunity.Amount__c?",
|
|
56865
|
+
"who or what writes to Priority__c on Case?",
|
|
56866
|
+
"trace every writer to External_Id__c",
|
|
56867
|
+
"what automation populates the Status field on Order?",
|
|
56868
|
+
"does any trigger or flow update Amount__c?",
|
|
56869
|
+
"We loaded leads from an external system and Converted_from_Lead__c is populated \u2014 what writes it?",
|
|
56870
|
+
"how is Priority__c getting set \u2014 is it a user, a flow, or a trigger?",
|
|
56871
|
+
"the Status field keeps changing on its own \u2014 what's causing it?",
|
|
56872
|
+
"trace all the things that could write to Case.Status__c",
|
|
56873
|
+
"which automation is responsible for populating Amount__c?"
|
|
56874
|
+
],
|
|
56875
|
+
"sfi.order_of_execution": [
|
|
56876
|
+
"in what order do triggers, flows, and validation rules fire on Case save?",
|
|
56877
|
+
"walk me through the Salesforce order of execution for Opportunity",
|
|
56878
|
+
"when does before-trigger run vs after-trigger vs flow?",
|
|
56879
|
+
"is validation run before or after triggers?",
|
|
56880
|
+
"what's the order of operations when I save a Contact record?",
|
|
56881
|
+
"does the flow run before or after the Apex trigger?"
|
|
56882
|
+
],
|
|
56883
|
+
"sfi.explain_flow": [
|
|
56884
|
+
"walk me through <FlowName>'s screens and decisions",
|
|
56885
|
+
"walk me thru the <FlowName> fault handling \u2014 if a step fails, does it retry, skip, or halt?",
|
|
56886
|
+
"Does <FlowName> invoke any Apex, and if so which class/method?",
|
|
56887
|
+
"explain the <FlowName> flow end to end \u2014 it's a screen flow, what's the user actually doing in it?",
|
|
56888
|
+
"what is <FlowName> for?",
|
|
56889
|
+
"when does <FlowName> fire and what does it change?",
|
|
56890
|
+
"break down <FlowName> logic",
|
|
56891
|
+
"what records does <FlowName> create or update?",
|
|
56892
|
+
"I inherited this flow and have no idea what it does \u2014 explain it to me",
|
|
56893
|
+
"summarize <FlowName> for a non-technical stakeholder",
|
|
56894
|
+
"what's the happy path through <FlowName>?",
|
|
56895
|
+
"does <FlowName> have any error handling?",
|
|
56896
|
+
"what variables does <FlowName> use?",
|
|
56897
|
+
"which object does <FlowName> primarily operate on?",
|
|
56898
|
+
// biz-user register (router-v2 R2): non-technical phrasings from the
|
|
56899
|
+
// business-user persona misses — purpose-of-flow asked as a business
|
|
56900
|
+
// question, and the "is that a flow or a trigger" type-confusion shape.
|
|
56901
|
+
"there's a flow called <FlowName> \u2014 what is it for? what business process does it support?",
|
|
56902
|
+
"what does the <FlowName> flow do for the business?",
|
|
56903
|
+
"<FlowName> \u2014 is that a flow or a trigger? what does it invoke?",
|
|
56904
|
+
"is <ComponentName> an Apex class or a flow? I want to see what it does",
|
|
56905
|
+
// R3 flow-explain band (DIAGNOSIS-R3 §2 rank 3): indirect explanation
|
|
56906
|
+
// phrasings — orchestrate/breakdown/what-triggers-it/fires-on-update.
|
|
56907
|
+
"before i touch the <FlowName> flow, what does it even do?",
|
|
56908
|
+
"what does the <FlowName> flow orchestrate?",
|
|
56909
|
+
"give me a breakdown of the <FlowName> flow",
|
|
56910
|
+
"what triggers the <FlowName> flow \u2014 a record change, a schedule, or a button?",
|
|
56911
|
+
"does <FlowName> fire on update too or just on insert?",
|
|
56912
|
+
"which record type does the <FlowName> flow assign to the Case?",
|
|
56913
|
+
"the <FlowName> screen flow \u2014 walk me through its screens and decisions",
|
|
56914
|
+
// R3 flow-diagnostics band ("why did it NOT fire / not stamp"):
|
|
56915
|
+
"why did the <FlowName> flow NOT fire when the record was updated?",
|
|
56916
|
+
"why is the <FlowName> flow not sending the confirmation email \u2014 walk the decision branches",
|
|
56917
|
+
"the flow didnt stamp the date when the stage moved \u2014 why not?",
|
|
56918
|
+
"a reviewer says the <FlowName> flow is skipping records \u2014 what does it actually gate?"
|
|
56919
|
+
],
|
|
56920
|
+
"sfi.explain_apex_method": [
|
|
56921
|
+
"what does the <MethodName> method in <ApexClass> do?",
|
|
56922
|
+
"explain the <MethodName> method \u2014 inputs, outputs, side effects",
|
|
56923
|
+
"walk me through the logic of <ApexClass>.<MethodName>",
|
|
56924
|
+
"what does this method return and what does it modify?",
|
|
56925
|
+
"explain the business logic in the <MethodName> method",
|
|
56926
|
+
"what does the AccountController class do?",
|
|
56927
|
+
// biz-user register (router-v2 R2): whole-class purpose questions and the
|
|
56928
|
+
// "Trigger in the name but is it a test class" type-confusion shape.
|
|
56929
|
+
"we have an Apex class called <ClassName> \u2014 what does it do and what objects does it touch?",
|
|
56930
|
+
"is <ClassName> an Apex test class or an actual trigger?",
|
|
56931
|
+
"that class has Trigger in its name \u2014 is it actually a test class?"
|
|
56932
|
+
],
|
|
56933
|
+
"sfi.explain_formula": [
|
|
56934
|
+
"what does this formula field actually calculate?",
|
|
56935
|
+
"explain the formula on <Field__c> in plain English",
|
|
56936
|
+
"break down this validation rule formula for me",
|
|
56937
|
+
"what's this formula doing \u2014 it's hard to read",
|
|
56938
|
+
"explain the formula logic on Amount__c",
|
|
56939
|
+
"what does this formula evaluate to and when?"
|
|
56940
|
+
],
|
|
56941
|
+
"sfi.unused_fields_deep": [
|
|
56942
|
+
"which fields on Case have zero data and aren't referenced by anything?",
|
|
56943
|
+
"deep analysis of unused fields \u2014 both empty and unreferenced",
|
|
56944
|
+
"show me fields that are populated but never read by any automation",
|
|
56945
|
+
"find fields that appear in no layouts, no flows, no Apex",
|
|
56946
|
+
"which custom fields on Account are completely orphaned?",
|
|
56947
|
+
"which fields on Account have zero usage anywhere in the org?"
|
|
56948
|
+
],
|
|
56949
|
+
"sfi.process_builder_migration_candidates": [
|
|
56950
|
+
"which Process Builder processes should we migrate to Flow?",
|
|
56951
|
+
"show me all Process Builder automations still active",
|
|
56952
|
+
"which processes are candidates for Flow migration?",
|
|
56953
|
+
"what Process Builder is left that we need to retire?",
|
|
56954
|
+
"identify Process Builder processes that would be straightforward to convert",
|
|
56955
|
+
"which Process Builders should we migrate to Flow first?",
|
|
56956
|
+
// biz-user register (router-v2 R2): "sunset/retire" business phrasing.
|
|
56957
|
+
"which automation is Salesforce going to sunset that we need to replace?",
|
|
56958
|
+
"what legacy automation is being retired that we still depend on?"
|
|
56959
|
+
],
|
|
56960
|
+
"sfi.unassigned_permission_sets": [
|
|
56961
|
+
"are there permission sets that aren't assigned to anyone?",
|
|
56962
|
+
"which permission sets are deployed but have zero users?",
|
|
56963
|
+
"find permission sets that are effectively dead \u2014 no assignments",
|
|
56964
|
+
"show me unassigned permission sets we could clean up",
|
|
56965
|
+
"permission sets nobody has \u2014 are there any?",
|
|
56966
|
+
"which permission sets aren't assigned to any user?",
|
|
56967
|
+
// biz-user register (router-v2 R2): single-perm-set assignment questions
|
|
56968
|
+
// ("is X assigned to anyone") — the premise-trap family phrasing.
|
|
56969
|
+
"is the <PermSet> permission set assigned to anyone?",
|
|
56970
|
+
"is <PermSet> assigned to any active users right now?",
|
|
56971
|
+
"who has the <PermSet> permission set assigned and what does it let them do?"
|
|
56972
|
+
],
|
|
56973
|
+
"sfi.empty_queues_and_groups": [
|
|
56974
|
+
"are there any queues with no members?",
|
|
56975
|
+
"show me empty public groups",
|
|
56976
|
+
"which queues have nobody in them?",
|
|
56977
|
+
"find groups and queues that are effectively abandoned",
|
|
56978
|
+
"empty queue check \u2014 anything routing to nowhere?",
|
|
56979
|
+
"which queues have no members assigned to them?"
|
|
56980
|
+
],
|
|
56981
|
+
"sfi.tech_debt_score": [
|
|
56982
|
+
"what's our overall tech debt score?",
|
|
56983
|
+
"how much technical debt does this org carry?",
|
|
56984
|
+
"give me a technical debt rating for this Salesforce org",
|
|
56985
|
+
"debt score breakdown \u2014 what's contributing most?",
|
|
56986
|
+
"quantify the technical debt in this org",
|
|
56987
|
+
"what's the overall technical debt score for this org?",
|
|
56988
|
+
// biz-user register (router-v2 R2): quality asked as a grade.
|
|
56989
|
+
"overall, is this org in good shape or carrying a lot of debt \u2014 passing grade or not?"
|
|
56990
|
+
],
|
|
56991
|
+
"sfi.code_quality_audit": [
|
|
56992
|
+
"how good is our Apex code quality?",
|
|
56993
|
+
"find code quality issues in our Apex classes",
|
|
56994
|
+
"audit the Apex code for common anti-patterns",
|
|
56995
|
+
"show me code quality violations",
|
|
56996
|
+
"which Apex classes have the most quality issues?",
|
|
56997
|
+
"are there any SOQL queries inside loops in our Apex code?"
|
|
56998
|
+
],
|
|
56999
|
+
"sfi.governor_limit_risks": [
|
|
57000
|
+
"which parts of our Apex are at risk of hitting governor limits?",
|
|
57001
|
+
"find SOQL queries inside loops",
|
|
57002
|
+
"which triggers could hit DML limits?",
|
|
57003
|
+
"governor limit risk assessment for our Apex",
|
|
57004
|
+
"which batch jobs are close to the 50M record limit?",
|
|
57005
|
+
"find places where we're at risk of CPU timeout"
|
|
57006
|
+
],
|
|
57007
|
+
"sfi.find_hardcoded_values": [
|
|
57008
|
+
"find hardcoded IDs in the Apex code",
|
|
57009
|
+
"which Apex classes have hardcoded email addresses?",
|
|
57010
|
+
"find hardcoded profile names in our triggers",
|
|
57011
|
+
"are there any hardcoded record type IDs in the code?",
|
|
57012
|
+
"show me hardcoded values in Apex that should be in Custom Settings",
|
|
57013
|
+
"are there hardcoded IDs or email addresses in our Apex code?"
|
|
57014
|
+
],
|
|
57015
|
+
"sfi.crud_fls_audit": [
|
|
57016
|
+
"are we enforcing FLS in our Apex?",
|
|
57017
|
+
"which Apex classes skip CRUD/FLS checks?",
|
|
57018
|
+
"find Apex that queries without checking field-level security",
|
|
57019
|
+
"CRUD and FLS audit for our triggers",
|
|
57020
|
+
"which classes are vulnerable to privilege escalation via missing FLS?",
|
|
57021
|
+
"which Apex classes skip CRUD or FLS checks?"
|
|
57022
|
+
],
|
|
57023
|
+
"sfi.test_coverage_gaps": [
|
|
57024
|
+
"where are the gaps in our test coverage?",
|
|
57025
|
+
"which Apex classes have coverage below 75%?",
|
|
57026
|
+
"find untested code paths in our triggers",
|
|
57027
|
+
"coverage gaps across all Apex",
|
|
57028
|
+
"which classes would block a deployment due to low coverage?",
|
|
57029
|
+
"which Apex classes have fake or missing test coverage?"
|
|
57030
|
+
],
|
|
57031
|
+
"sfi.what_if_change_field_type": [
|
|
57032
|
+
"what happens if I change the type of Case.Priority__c from picklist to text?",
|
|
57033
|
+
"blast radius of changing Opportunity.Amount__c from currency to number",
|
|
57034
|
+
"if I convert Contact.External_Id__c from Text to EncryptedText, what breaks?",
|
|
57035
|
+
"can I safely change <Field> type from Number to Text?",
|
|
57036
|
+
"what downstream impact if I make Status__c a lookup instead of text?",
|
|
57037
|
+
"impact of changing field type on Account.<Field__c>",
|
|
57038
|
+
"I want to change a Text field to a Long Text Area \u2014 what's the risk?",
|
|
57039
|
+
"what breaks if I change the data type of this field?",
|
|
57040
|
+
"if I convert this currency field to a formula field, what automation breaks?",
|
|
57041
|
+
"blast radius of making Amount__c a formula field instead of editable"
|
|
57042
|
+
],
|
|
57043
|
+
"sfi.what_if_remove_picklist_value": [
|
|
57044
|
+
"what happens if I delete the 'Pending' value from Case.Status?",
|
|
57045
|
+
"if I remove the 'Cold' picklist value from Lead.Rating, what records are affected?",
|
|
57046
|
+
"can I safely remove the 'Legacy' stage from Opportunity.StageName?",
|
|
57047
|
+
"what breaks if I remove a picklist value that records already use?",
|
|
57048
|
+
"impact of removing 'On Hold' from Case.Status picklist",
|
|
57049
|
+
"what breaks if I remove 'Closed - Duplicate' from Case.Status?"
|
|
57050
|
+
],
|
|
57051
|
+
"sfi.what_if_make_field_required": [
|
|
57052
|
+
"what happens if I make Priority__c required on Case?",
|
|
57053
|
+
"if I make Amount__c required on Opportunity, what existing records would violate that?",
|
|
57054
|
+
"impact of making Contact.External_Id__c required",
|
|
57055
|
+
"can I safely make this field required without breaking flows?",
|
|
57056
|
+
"what automation would break if I make this field mandatory?",
|
|
57057
|
+
"what would break if I made Account.Website required?",
|
|
57058
|
+
// biz-user register (router-v2 R2): "I want to set X to required" ask.
|
|
57059
|
+
"I want to set <Field__c> to required \u2014 can you check what flows and Apex would break?"
|
|
57060
|
+
],
|
|
57061
|
+
"sfi.what_if_deactivate_flow": [
|
|
57062
|
+
"what breaks if I deactivate <FlowName>?",
|
|
57063
|
+
"if I turn off this flow, what processes depend on it?",
|
|
57064
|
+
"impact of deactivating the Case assignment flow",
|
|
57065
|
+
"can I safely deactivate <FlowName> without breaking anything?",
|
|
57066
|
+
"what would stop working if I switch off <FlowName>?",
|
|
57067
|
+
"what happens if I deactivate the <FlowName> flow?",
|
|
57068
|
+
// biz-user register (router-v2 R2): "can you deactivate it safely" and
|
|
57069
|
+
// "impact if we turn it off" phrasings (premise-trap family).
|
|
57070
|
+
"can you deactivate <FlowName> safely? I need to know what depends on it",
|
|
57071
|
+
"we have a <ProcessName> process that runs on these records \u2014 what's the impact if we turn it off?",
|
|
57072
|
+
// R3 what-if band (DIAGNOSIS-R3 flows family + §5c simulation frames):
|
|
57073
|
+
// imperative-plus-impact phrasings the write gate now carves out.
|
|
57074
|
+
"deactivate <FlowName> and tell me the impact",
|
|
57075
|
+
"deactivating <FlowName> \u2014 consequences?",
|
|
57076
|
+
"what happens if the <FlowName> flow is turned off during a busy period?",
|
|
57077
|
+
"deactivate the <FlowName> flow \u2014 what happens to new records?",
|
|
57078
|
+
"deactivate <FlowName> and tell me what stops recalculating",
|
|
57079
|
+
"does deactivating <FlowName> stop the confirmation emails?",
|
|
57080
|
+
"when I deactivate <FlowName>, does the review process just stall?",
|
|
57081
|
+
"consequences of deactivating the <FlowName> flow?",
|
|
57082
|
+
"what would happen if the <FlowName> flow were deactivated?"
|
|
57083
|
+
],
|
|
57084
|
+
"sfi.what_if_disable_trigger": [
|
|
57085
|
+
"what breaks if I disable the Contact trigger?",
|
|
57086
|
+
"impact of disabling <TriggerName>",
|
|
57087
|
+
"if I turn off this trigger, what automation stops running?",
|
|
57088
|
+
"can I safely disable the Account trigger?",
|
|
57089
|
+
"what would I lose if the Case trigger was off?",
|
|
57090
|
+
"if I disable the Account trigger, what stops working?"
|
|
57091
|
+
],
|
|
57092
|
+
"sfi.what_if_change_method_signature": [
|
|
57093
|
+
"what breaks if I change the signature of <MethodName>?",
|
|
57094
|
+
"impact of renaming the <MethodName> method in <ApexClass>",
|
|
57095
|
+
"if I add a parameter to <MethodName>, what callers need updating?",
|
|
57096
|
+
"which classes call <MethodName> and would be affected by signature change?",
|
|
57097
|
+
"can I remove a parameter from <MethodName> without breaking anything?",
|
|
57098
|
+
"what breaks if I rename the calculateTotal() method signature?"
|
|
57099
|
+
],
|
|
57100
|
+
"sfi.what_if_merge_profiles": [
|
|
57101
|
+
"I want to consolidate the permission set group \u2014 what would break if I merge <PermSetA> and <PermSetB>?",
|
|
57102
|
+
"can I combine the Sales and Sales Manager profiles?",
|
|
57103
|
+
"what conflicts exist between <ProfileA> and <ProfileB>?",
|
|
57104
|
+
"merge <Profile1> into <Profile2> \u2014 what are the collisions?",
|
|
57105
|
+
"what permission conflicts come up if I merge these two profiles?",
|
|
57106
|
+
"impact of combining two permission sets into one",
|
|
57107
|
+
"if I consolidate our three sales profiles into one, what conflicts arise?",
|
|
57108
|
+
"what would I lose or gain by merging <PermSet1> and <PermSet2>?"
|
|
57109
|
+
],
|
|
57110
|
+
"sfi.what_if_split_profile": [
|
|
57111
|
+
"should I split the Admin profile into two?",
|
|
57112
|
+
"impact of splitting the <Profile> profile by region",
|
|
57113
|
+
"how would I split the Sales profile into read-only and full-access variants?",
|
|
57114
|
+
"what's involved in splitting a monolithic profile into permission sets?",
|
|
57115
|
+
"if I break this profile into two, what do I need to account for?",
|
|
57116
|
+
"how would I split the <Profile> profile into multiple permission sets?"
|
|
57117
|
+
],
|
|
57118
|
+
"sfi.generate_data_dictionary": [
|
|
57119
|
+
"generate a data dictionary for this org",
|
|
57120
|
+
"create documentation of all our fields and objects",
|
|
57121
|
+
"build a data dictionary I can share with the data team",
|
|
57122
|
+
"export field descriptions and types for all custom objects",
|
|
57123
|
+
"generate a schema reference document",
|
|
57124
|
+
"generate a data dictionary for the Contact object",
|
|
57125
|
+
// biz-user register (router-v2 R2): "reference sheet" business phrasing.
|
|
57126
|
+
"our business team needs a reference sheet for the <Object> object \u2014 field names, types, required vs optional",
|
|
57127
|
+
"make a field reference sheet the business team can read"
|
|
57128
|
+
],
|
|
57129
|
+
"sfi.generate_admin_handbook": [
|
|
57130
|
+
"write an admin handbook for this org",
|
|
57131
|
+
"generate a guide for onboarding a new Salesforce admin",
|
|
57132
|
+
"create an administrative reference document",
|
|
57133
|
+
"build an ops runbook for our Salesforce configuration",
|
|
57134
|
+
"document the admin processes in this org",
|
|
57135
|
+
"create an admin handbook for this org"
|
|
57136
|
+
],
|
|
57137
|
+
"sfi.generate_architecture_overview": [
|
|
57138
|
+
"generate an architecture overview for this org",
|
|
57139
|
+
"write a technical architecture document",
|
|
57140
|
+
"create a high-level architecture summary for stakeholders",
|
|
57141
|
+
"document the overall structure of this Salesforce implementation",
|
|
57142
|
+
"generate an architecture diagram description for this org",
|
|
57143
|
+
"write an architecture overview document for this Salesforce org",
|
|
57144
|
+
// biz-user register (router-v2 R2): altitude idiom for leadership.
|
|
57145
|
+
"the leadership team wants the 10,000-foot architecture picture of our Salesforce org"
|
|
57146
|
+
],
|
|
57147
|
+
"sfi.generate_sharing_summary": [
|
|
57148
|
+
"generate a sharing model summary for this org",
|
|
57149
|
+
"document how record sharing works in this org",
|
|
57150
|
+
"create a sharing and visibility overview",
|
|
57151
|
+
"write up the sharing architecture \u2014 OWD, roles, sharing rules",
|
|
57152
|
+
"generate a sharing model document I can review with security",
|
|
57153
|
+
"generate a sharing model summary for this org"
|
|
57154
|
+
],
|
|
57155
|
+
"sfi.generate_compliance_report": [
|
|
57156
|
+
"generate a compliance report for this org",
|
|
57157
|
+
"create a GDPR compliance overview",
|
|
57158
|
+
"document our data handling practices from a compliance perspective",
|
|
57159
|
+
"produce a security and compliance report",
|
|
57160
|
+
"generate a report for an auditor about our data controls",
|
|
57161
|
+
"produce a compliance report for data privacy fields in this org"
|
|
57162
|
+
],
|
|
57163
|
+
"sfi.generate_onboarding_doc": [
|
|
57164
|
+
"generate an onboarding guide for new users",
|
|
57165
|
+
"create a getting started document for sales reps",
|
|
57166
|
+
"write onboarding documentation for the support team",
|
|
57167
|
+
"generate a new hire guide to our Salesforce org",
|
|
57168
|
+
"create documentation for someone who has never used our Salesforce before",
|
|
57169
|
+
"generate an onboarding document for a new admin joining this org"
|
|
57170
|
+
],
|
|
57171
|
+
"sfi.call_graph": [
|
|
57172
|
+
"Are there any flows nested inside other flows that make things hard to follow?",
|
|
57173
|
+
"what does <ApexClass> call downstream?",
|
|
57174
|
+
"trace the full call chain from <TriggerName>",
|
|
57175
|
+
"what classes does <ApexClass> depend on transitively?",
|
|
57176
|
+
"upstream callers of <ApexClass>",
|
|
57177
|
+
"what's the call depth on <MethodName>?",
|
|
57178
|
+
"show me the full dependency chain for <ApexClass>",
|
|
57179
|
+
"which flows invoke other flows?",
|
|
57180
|
+
"what's the transitive call graph from <ApexClass>?",
|
|
57181
|
+
"give me all callers and callees for <ApexClass>",
|
|
57182
|
+
"how deep is the call stack for <TriggerName>?",
|
|
57183
|
+
"subflow map \u2014 which flows call other flows?"
|
|
57184
|
+
],
|
|
57185
|
+
"sfi.downstream_effects": [
|
|
57186
|
+
"what does changing <Field__c> ripple out to downstream?",
|
|
57187
|
+
"show me the downstream effects of modifying <ApexClass>",
|
|
57188
|
+
"if I change <FlowName>, what else is affected downstream?",
|
|
57189
|
+
"downstream impact of updating the Case status picklist",
|
|
57190
|
+
"what does it affect if <ComponentName> changes?",
|
|
57191
|
+
"what side effects does the AccountTrigger handler produce downstream?",
|
|
57192
|
+
// self-recall reinforcement (router-v2 R2): the two paraphrase-only
|
|
57193
|
+
// utterances above ("what else is affected", "what does it affect")
|
|
57194
|
+
// shared no vocabulary with the tool's own name and lost the tie to
|
|
57195
|
+
// get_impact — anchor the downstream/effects terms.
|
|
57196
|
+
"what are the downstream effects if I change <FlowName>?",
|
|
57197
|
+
"show downstream effects of a change to <ComponentName>"
|
|
57198
|
+
],
|
|
57199
|
+
"sfi.test_coverage_for_method": [
|
|
57200
|
+
"what tests cover the <MethodName> method?",
|
|
57201
|
+
"which test classes exercise <ApexClass>.<MethodName>?",
|
|
57202
|
+
"is there a test for the <MethodName> method?",
|
|
57203
|
+
"show me the test coverage for this specific method",
|
|
57204
|
+
"what unit tests hit <MethodName>?",
|
|
57205
|
+
"which test classes exercise the processPayment() method?"
|
|
57206
|
+
],
|
|
57207
|
+
"sfi.meaningful_test_audit": [
|
|
57208
|
+
"are our tests actually meaningful or just padding coverage?",
|
|
57209
|
+
"which tests have no real assertions?",
|
|
57210
|
+
"find tests that just inflate coverage without testing anything",
|
|
57211
|
+
"audit our test suite for meaningful assertions",
|
|
57212
|
+
"are these tests actually checking anything useful?",
|
|
57213
|
+
"which of our test classes have no real assertions \u2014 just System.assert(true)?"
|
|
57214
|
+
],
|
|
57215
|
+
"sfi.method_reachability": [
|
|
57216
|
+
"is <MethodName> reachable from any active flow or trigger?",
|
|
57217
|
+
"can this method actually be called in production?",
|
|
57218
|
+
"which entry points reach <MethodName>?",
|
|
57219
|
+
"is <ApexClass>.<MethodName> dead code?",
|
|
57220
|
+
"what paths lead to <MethodName> being invoked?",
|
|
57221
|
+
"is the AccountService class reachable from any trigger or REST endpoint?"
|
|
57222
|
+
],
|
|
57223
|
+
"sfi.tests_for_change": [
|
|
57224
|
+
"which tests do I need to run after changing <ApexClass>?",
|
|
57225
|
+
"what test classes cover the code I'm about to modify?",
|
|
57226
|
+
"I changed <FlowName> \u2014 which tests should I run?",
|
|
57227
|
+
"which tests are relevant to my changes in <ApexClass>?",
|
|
57228
|
+
"give me the test suite to run before deploying <ComponentName>",
|
|
57229
|
+
"if I change the AccountService and CaseHandler classes, which tests should I run?"
|
|
57230
|
+
],
|
|
57231
|
+
"sfi.cdc_subscribers": [
|
|
57232
|
+
"what's subscribed to Change Data Capture events on Contact?",
|
|
57233
|
+
"who listens to CDC events on Account?",
|
|
57234
|
+
"show me all Change Data Capture subscribers",
|
|
57235
|
+
"which flows or external systems consume CDC events?",
|
|
57236
|
+
"what processes react to Account change events?",
|
|
57237
|
+
"which flows or triggers listen to Account CDC change events?"
|
|
57238
|
+
],
|
|
57239
|
+
"sfi.async_chain_depth": [
|
|
57240
|
+
"how deep is the async chain for <ApexClass>?",
|
|
57241
|
+
"are there chained queueables that go too deep?",
|
|
57242
|
+
"show me the async job chain depth",
|
|
57243
|
+
"how many levels of chained async jobs do we have?",
|
|
57244
|
+
"which async jobs kick off other async jobs?",
|
|
57245
|
+
"how deep is the async call chain from the BatchProcessor class?"
|
|
57246
|
+
],
|
|
57247
|
+
"sfi.scheduled_job_catalog": [
|
|
57248
|
+
"what scheduled jobs are running in this org?",
|
|
57249
|
+
"show me all the scheduled Apex jobs",
|
|
57250
|
+
"which batch jobs are scheduled?",
|
|
57251
|
+
"list all cron-based jobs in the org",
|
|
57252
|
+
"what's running on a schedule in this org?",
|
|
57253
|
+
"which Apex classes are scheduled to run on a cron schedule?"
|
|
57254
|
+
],
|
|
57255
|
+
"sfi.outbound_message_catalog": [
|
|
57256
|
+
"what outbound messages are configured?",
|
|
57257
|
+
"show me all outbound SOAP messages",
|
|
57258
|
+
"which workflow rules send outbound messages?",
|
|
57259
|
+
"list all outbound message endpoints",
|
|
57260
|
+
"what external systems do we push data to via outbound messages?",
|
|
57261
|
+
"what outbound messages does this org send and to which endpoints?"
|
|
57262
|
+
],
|
|
57263
|
+
"sfi.endpoint_catalog": [
|
|
57264
|
+
"what external endpoints does this org call out to?",
|
|
57265
|
+
"show me all named credentials and callout URLs",
|
|
57266
|
+
"which Apex classes make HTTP callouts?",
|
|
57267
|
+
"list all integration endpoints",
|
|
57268
|
+
"what APIs does our Salesforce code talk to?",
|
|
57269
|
+
"list all inbound and outbound API endpoints in this org"
|
|
57270
|
+
],
|
|
57271
|
+
"sfi.field_meaning": [
|
|
57272
|
+
"what does Priority__c mean in our business context?",
|
|
57273
|
+
"explain the business purpose of External_Id__c",
|
|
57274
|
+
"what's the semantic meaning of Status__c on Order?",
|
|
57275
|
+
"what does Amount__c represent \u2014 is it revenue, cost, or something else?",
|
|
57276
|
+
"in plain English, what does this field track?",
|
|
57277
|
+
"what does the External_Id__c field on Account actually mean in this org?",
|
|
57278
|
+
// biz-user register (router-v2 R2): label-first "what is it really for /
|
|
57279
|
+
// who sets it" phrasings from the business-user misses.
|
|
57280
|
+
"what is the <Field__c> field really for in this org?",
|
|
57281
|
+
"the bad email field \u2014 what does it represent and who sets it?",
|
|
57282
|
+
"what does that field mean in context \u2014 is it set by automation or by users, and what object is it on?"
|
|
57283
|
+
],
|
|
57284
|
+
"sfi.disambiguate_concepts": [
|
|
57285
|
+
"we have both Priority__c and Case.Priority \u2014 which one should I use?",
|
|
57286
|
+
"there are three things called Status in this org \u2014 which one matters?",
|
|
57287
|
+
"Amount__c vs Amount \u2014 what's the difference?",
|
|
57288
|
+
"we have External_Id__c on both Contact and Account \u2014 are they related?",
|
|
57289
|
+
"clarify the difference between <ConceptA> and <ConceptB> in this org",
|
|
57290
|
+
"is 'Status' the same concept as 'Stage' in this org or are they different?"
|
|
57291
|
+
],
|
|
57292
|
+
"sfi.field_provenance": [
|
|
57293
|
+
"<Field__c> is populated but I don't know how \u2014 is it manually entered or automated?",
|
|
57294
|
+
"is Priority__c a formula or is it written by code?",
|
|
57295
|
+
"what's the source of truth for Contact.External_Id__c?",
|
|
57296
|
+
"how does Status__c get its value on Order?",
|
|
57297
|
+
"is Amount__c auto-populated or user-entered?",
|
|
57298
|
+
"where does the data in this field come from?",
|
|
57299
|
+
"is this field set by a trigger, a flow, or does a user type it in?",
|
|
57300
|
+
"trace the origin of values in <Field__c>",
|
|
57301
|
+
// biz-user register (router-v2 R2): "how does it get its value" shape.
|
|
57302
|
+
"how does the <Field__c> field get its value \u2014 is it a formula or is something writing to it?"
|
|
57303
|
+
],
|
|
57304
|
+
"sfi.find_field_anywhere": [
|
|
57305
|
+
"find all objects that have a field called External_Id__c",
|
|
57306
|
+
"is there a Priority field on any object in this org?",
|
|
57307
|
+
"search for any field named Status across all objects",
|
|
57308
|
+
"find fields named Amount__ on any object",
|
|
57309
|
+
"which objects have a field called Description?",
|
|
57310
|
+
"where is the Priority__c field used across flows, Apex, and layouts?"
|
|
57311
|
+
],
|
|
57312
|
+
"sfi.find_semantic_field": [
|
|
57313
|
+
"find me a field that tracks customer churn risk",
|
|
57314
|
+
"is there a field that stores the contract end date?",
|
|
57315
|
+
"find a field related to billing frequency",
|
|
57316
|
+
"what field would hold escalation priority?",
|
|
57317
|
+
"find a field that represents the customer's account tier",
|
|
57318
|
+
"do we have a field that stores the customer's loyalty tier?"
|
|
57319
|
+
],
|
|
57320
|
+
"sfi.find_hardcoded_values_anywhere": [
|
|
57321
|
+
"find hardcoded values anywhere \u2014 Apex, flows, validation rules, everywhere",
|
|
57322
|
+
"search all metadata types for hardcoded email addresses",
|
|
57323
|
+
"hardcoded IDs anywhere in the org \u2014 not just Apex",
|
|
57324
|
+
"find hardcoded profile names in flows too, not just triggers",
|
|
57325
|
+
"check flows and validation rules for hardcoded values, not just code",
|
|
57326
|
+
"are there hardcoded IDs in our validation rule formulas?"
|
|
57327
|
+
],
|
|
57328
|
+
"sfi.find_clone_patterns": [
|
|
57329
|
+
"are there Apex classes that look like copy-paste of each other?",
|
|
57330
|
+
"find duplicate code patterns in our Apex",
|
|
57331
|
+
"which classes have very similar logic that should be refactored?",
|
|
57332
|
+
"show me clone candidates in our codebase",
|
|
57333
|
+
"find near-duplicate implementations across Apex classes",
|
|
57334
|
+
"are there Apex classes that look structurally identical to AccountHandler?"
|
|
57335
|
+
],
|
|
57336
|
+
"sfi.find_dead_code": [
|
|
57337
|
+
"find Apex methods that are never called",
|
|
57338
|
+
"which Apex classes are completely unreferenced?",
|
|
57339
|
+
"show me dead code \u2014 methods, classes, that nothing calls",
|
|
57340
|
+
"find unreachable code paths in our Apex",
|
|
57341
|
+
"which classes can I safely delete because nothing uses them?",
|
|
57342
|
+
"which Apex classes have no callers and look completely dead?"
|
|
57343
|
+
],
|
|
57344
|
+
"sfi.package_impact": [
|
|
57345
|
+
"I keep hearing '<PackageName>' thrown around. What is it, how deep is it in our org, and are we locked into it?",
|
|
57346
|
+
"how entangled is the <Package> package in our customizations?",
|
|
57347
|
+
"if we uninstall <Package> what breaks?",
|
|
57348
|
+
"what does our org depend on from <Package>?",
|
|
57349
|
+
"impact of removing the <PackageName> managed package",
|
|
57350
|
+
"show me everything we've built on top of <Package>",
|
|
57351
|
+
"how many of our custom components reference <Package> components?"
|
|
57352
|
+
],
|
|
57353
|
+
"sfi.cpq_rule_chain": [
|
|
57354
|
+
"show me the CPQ pricing rule chain for this product",
|
|
57355
|
+
"what CPQ rules fire when configuring a quote?",
|
|
57356
|
+
"trace the CPQ rule execution for this product bundle",
|
|
57357
|
+
"which CPQ validation and pricing rules apply to Order products?",
|
|
57358
|
+
"show me the CPQ rule dependencies",
|
|
57359
|
+
"what's the order of CPQ product rules that apply to the Bundle product?"
|
|
57360
|
+
],
|
|
57361
|
+
"sfi.cpq_quote_template_breakdown": [
|
|
57362
|
+
"break down the CPQ quote template structure",
|
|
57363
|
+
"what sections and line items does this quote template include?",
|
|
57364
|
+
"explain the CPQ quote template layout",
|
|
57365
|
+
"show me the components of our primary quote template",
|
|
57366
|
+
"what's in the standard CPQ quote template?",
|
|
57367
|
+
"what sections are in the Standard Quote Template?"
|
|
57368
|
+
],
|
|
57369
|
+
"sfi.cpq_dependency_map": [
|
|
57370
|
+
"show me the CPQ dependency map",
|
|
57371
|
+
"how do CPQ objects relate to each other?",
|
|
57372
|
+
"map out the CPQ data model in this org",
|
|
57373
|
+
"what CPQ objects and relationships do we have configured?",
|
|
57374
|
+
"dependency graph for our CPQ implementation",
|
|
57375
|
+
"what SBQQ__ field references exist in this org's CPQ configuration?"
|
|
57376
|
+
],
|
|
57377
|
+
"sfi.field_360": [
|
|
57378
|
+
"give me a 360 view of the Priority__c field",
|
|
57379
|
+
"full profile of External_Id__c \u2014 who uses it, what sets it, where does it appear",
|
|
57380
|
+
"complete field analysis for Amount__c on Opportunity",
|
|
57381
|
+
"everything about Status__c on Case \u2014 definition, usage, writers, layouts",
|
|
57382
|
+
"field 360 for Contact.LeadSource \u2014 the full picture",
|
|
57383
|
+
"give me the complete picture of the Amount__c field on Opportunity \u2014 everything touching it"
|
|
57384
|
+
],
|
|
57385
|
+
"sfi.field_lineage": [
|
|
57386
|
+
"show me the lineage of Priority__c \u2014 where does it come from and where does it go?",
|
|
57387
|
+
"trace the data lineage for Amount__c from source to destination",
|
|
57388
|
+
"where does External_Id__c originate and how does it flow through the org?",
|
|
57389
|
+
"field lineage for Status__c \u2014 what populates it and what reads it?",
|
|
57390
|
+
"data lineage for a key field on Account",
|
|
57391
|
+
"trace where the Opportunity.Amount__c field gets its value from and what it feeds"
|
|
57392
|
+
],
|
|
57393
|
+
"sfi.compare_vaults": [
|
|
57394
|
+
"compare our production org to our sandbox",
|
|
57395
|
+
"show me differences between the two vault snapshots",
|
|
57396
|
+
"what's different between our dev sandbox and production?",
|
|
57397
|
+
"compare the two org configurations",
|
|
57398
|
+
"vault diff between production and staging",
|
|
57399
|
+
"how does our sandbox differ from production in terms of metadata?"
|
|
57400
|
+
],
|
|
57401
|
+
"sfi.promotion_readiness": [
|
|
57402
|
+
"is the sandbox ready to promote to production?",
|
|
57403
|
+
"what's blocking promotion from dev to production?",
|
|
57404
|
+
"promotion readiness check for this sandbox",
|
|
57405
|
+
"are there any conflicts that would prevent a promotion?",
|
|
57406
|
+
"show me what needs to be resolved before we can promote",
|
|
57407
|
+
"what sandbox-only components need to be promoted to production?"
|
|
57408
|
+
],
|
|
57409
|
+
"sfi.compare_object_across_vaults": [
|
|
57410
|
+
"compare the Case object between sandbox and production",
|
|
57411
|
+
"how does the Contact object differ across our orgs?",
|
|
57412
|
+
"show me differences in the Account schema between two vaults",
|
|
57413
|
+
"compare custom object <ObjectName> across environments",
|
|
57414
|
+
"Case object schema diff between dev and prod",
|
|
57415
|
+
"are the Contact fields the same in sandbox vs production?"
|
|
57416
|
+
],
|
|
57417
|
+
"sfi.compare_profile_across_vaults": [
|
|
57418
|
+
"compare the Sales profile between sandbox and production",
|
|
57419
|
+
"how does <Profile> differ across our orgs?",
|
|
57420
|
+
"show me profile differences between environments",
|
|
57421
|
+
"compare admin profile between dev and production",
|
|
57422
|
+
"which permissions exist in production that are missing from sandbox?",
|
|
57423
|
+
"does the <Profile> profile have the same grants in sandbox vs prod?"
|
|
57424
|
+
],
|
|
57425
|
+
"sfi.field_mapping_between_objects": [
|
|
57426
|
+
"how does the Contact object map to Account?",
|
|
57427
|
+
"show me field mappings between Lead and Contact/Account on conversion",
|
|
57428
|
+
"what fields get mapped during Case to Opportunity conversion?",
|
|
57429
|
+
"field mapping between Order and Quote objects",
|
|
57430
|
+
"how do fields on <Object1> map to fields on <Object2>?",
|
|
57431
|
+
"map the Lead fields to Contact fields for conversion \u2014 what would lose data?"
|
|
57432
|
+
],
|
|
57433
|
+
"sfi.datatransform_field_map": [
|
|
57434
|
+
"show me the DataTransform field mappings",
|
|
57435
|
+
"what field mappings are defined in this DataTransform?",
|
|
57436
|
+
"explain the field mapping configuration for this DataTransform",
|
|
57437
|
+
"what source and target fields does this DataTransform map?",
|
|
57438
|
+
"DataTransform mapping details for the Contact integration",
|
|
57439
|
+
"what field mappings does the <DataRaptorName> DataRaptor define?"
|
|
57440
|
+
],
|
|
57441
|
+
"sfi.decision_table_browse": [
|
|
57442
|
+
"show me the decision tables in this org",
|
|
57443
|
+
"browse the Decision Table for pricing rules",
|
|
57444
|
+
"what decision tables exist on the Case object?",
|
|
57445
|
+
"list all configured decision tables",
|
|
57446
|
+
"show me the entries in the routing decision table",
|
|
57447
|
+
"what are the input and output parameters of the <DecisionTable> decision table?"
|
|
57448
|
+
],
|
|
57449
|
+
"sfi.integration_procedure_chain": [
|
|
57450
|
+
"show me the OmniStudio Integration Procedure chain for <IntegrationProcedure>",
|
|
57451
|
+
"what does this Integration Procedure call?",
|
|
57452
|
+
"trace the IP call chain from <IntegrationProcedure>",
|
|
57453
|
+
"which Integration Procedures call each other?",
|
|
57454
|
+
"explain the Integration Procedure flow for this use case",
|
|
57455
|
+
"walk me through the steps in the <IPName> Integration Procedure"
|
|
57456
|
+
],
|
|
57457
|
+
"sfi.omniscript_flow": [
|
|
57458
|
+
"walk me through the OmniScript flow for <OmniScriptName>",
|
|
57459
|
+
"explain the screens and steps in this OmniScript",
|
|
57460
|
+
"what elements does this OmniScript contain?",
|
|
57461
|
+
"describe the OmniScript user journey for <OmniScriptName>",
|
|
57462
|
+
"what Integration Procedures does this OmniScript call?",
|
|
57463
|
+
"what screens and steps does the <OmniScriptName> OmniScript walk the user through?"
|
|
57464
|
+
],
|
|
57465
|
+
"sfi.omniuicard_widget_breakdown": [
|
|
57466
|
+
"break down the widgets in this OmniUI Card",
|
|
57467
|
+
"what components make up this FlexCard?",
|
|
57468
|
+
"show me the widget structure of <OmniUiCardName>",
|
|
57469
|
+
"explain the layout of this OmniUI Card",
|
|
57470
|
+
"what data sources does this FlexCard use?",
|
|
57471
|
+
"what widgets are in the <FlexCardName> FlexCard?"
|
|
57472
|
+
],
|
|
57473
|
+
"sfi.find_component_usages": [
|
|
57474
|
+
"why cant i see the <ComponentName> thing, is it a permission",
|
|
57475
|
+
"who has the <PermSet> perm set",
|
|
57476
|
+
"who's allowed to view the <ComponentName> component?",
|
|
57477
|
+
"what uses the <ComponentName> \u2014 everywhere it's referenced or invoked",
|
|
57478
|
+
"where is this flow referenced?",
|
|
57479
|
+
"what calls into <ComponentName>?",
|
|
57480
|
+
"is <ComponentName> used anywhere?",
|
|
57481
|
+
"does anything still use <ComponentName>?",
|
|
57482
|
+
"find every place <ComponentName> is referenced",
|
|
57483
|
+
"which layouts include the <ComponentName> component?",
|
|
57484
|
+
"which flows invoke <ComponentName>?",
|
|
57485
|
+
"where is this custom label used?",
|
|
57486
|
+
"what uses the <PermSet> permission set \u2014 show me all assignments and references",
|
|
57487
|
+
// R3 reverse custom-permission grant band (DIAGNOSIS-R3 §2 rank 5): who
|
|
57488
|
+
// holds / which perm sets turn on / which profiles enable a custom
|
|
57489
|
+
// permission — the reverse lookup shipped in 0.1.19; the vocabulary here.
|
|
57490
|
+
"who holds the <CustomPermission> custom permission and which perm sets grant it?",
|
|
57491
|
+
"which permission sets or permission set groups turn on <CustomPermission>?",
|
|
57492
|
+
"which profiles enable the <CustomPermission> custom permission directly \u2014 not perm sets?",
|
|
57493
|
+
"is <CustomPermission> granted anywhere besides the <PermSet> permission set?",
|
|
57494
|
+
"i need the granting perm set for the <CustomPermission> custom permission",
|
|
57495
|
+
"how could a user have gotten the <CustomPermission> custom permission?",
|
|
57496
|
+
"what perm set gives access to the <ComponentName> component?",
|
|
57497
|
+
"who is allowed to bypass the validation \u2014 which custom permission guards it and who holds it?"
|
|
57498
|
+
],
|
|
57499
|
+
"sfi.installed_package_catalog": [
|
|
57500
|
+
"what managed packages are installed in this org?",
|
|
57501
|
+
"which packages do we rely on?",
|
|
57502
|
+
"i keep seeing <prefix>__ everywhere \u2014 whats that prefix and what owns it?",
|
|
57503
|
+
"show me all installed AppExchange packages",
|
|
57504
|
+
"give me an inventory of all managed packages",
|
|
57505
|
+
"which packages are installed and what version are they on?",
|
|
57506
|
+
"what third-party packages are we running?",
|
|
57507
|
+
"list all packages \u2014 namespace, publisher, version"
|
|
57508
|
+
],
|
|
57509
|
+
"sfi.annotations": [
|
|
57510
|
+
"show me all annotations for <ComponentName>",
|
|
57511
|
+
"what notes have been added to this component?",
|
|
57512
|
+
"are there any annotations on the <FlowName> flow?",
|
|
57513
|
+
"list component annotations in this org",
|
|
57514
|
+
"show me existing metadata annotations",
|
|
57515
|
+
"what ownership or lifecycle annotations have been added to components?"
|
|
57516
|
+
],
|
|
57517
|
+
"sfi.propose_annotation": [
|
|
57518
|
+
"suggest an annotation for <ComponentName>",
|
|
57519
|
+
"what annotation would you add to this Apex class?",
|
|
57520
|
+
"propose a description for the <FlowName> flow",
|
|
57521
|
+
"generate an annotation for this component based on what it does",
|
|
57522
|
+
"write a description I can add to <ComponentName>",
|
|
57523
|
+
"flag the Amount__c field as PII-sensitive so this is recorded"
|
|
57524
|
+
],
|
|
57525
|
+
"sfi.component_history": [
|
|
57526
|
+
"show me the change history for <ComponentName>",
|
|
57527
|
+
"who has modified the <FlowName> flow over time?",
|
|
57528
|
+
"what changes have been made to <ApexClass> historically?",
|
|
57529
|
+
"component changelog for <ComponentName>",
|
|
57530
|
+
"has <ComponentName> changed recently?",
|
|
57531
|
+
"when was the AccountTrigger last changed and what did it look like before?"
|
|
57532
|
+
],
|
|
57533
|
+
"sfi.component_as_of": [
|
|
57534
|
+
"show me what <ComponentName> looked like last month",
|
|
57535
|
+
"what did the <FlowName> flow look like before the last change?",
|
|
57536
|
+
"show me <ApexClass> as of the January snapshot",
|
|
57537
|
+
"what was the state of <ComponentName> at the time of the release?",
|
|
57538
|
+
"restore view of <ComponentName> from a specific date",
|
|
57539
|
+
"show me what the Account trigger looked like at the v1.0 tag"
|
|
57540
|
+
]
|
|
57541
|
+
};
|
|
57542
|
+
}
|
|
57543
|
+
});
|
|
57544
|
+
|
|
55105
57545
|
// ../mcp/dist/src/semantic-funnel.js
|
|
55106
|
-
var STOPWORDS, SYNONYMS, PHRASE_SYNONYMS3, applyPhraseSynonyms3, tokenize6, TOOL_KEYWORDS, PLANE_OVERRIDES, planeForTool, EXCLUDED_FROM_CANDIDATES, liveRequiredForPlane, buildPlaneByTool, cachedPlanes, getPlaneByTool, resolveCandidatePlane,
|
|
57546
|
+
var STOPWORDS, SYNONYMS, PHRASE_SYNONYMS3, applyPhraseSynonyms3, tokenize6, TOOL_KEYWORDS, PLANE_OVERRIDES, planeForTool, EXCLUDED_FROM_CANDIDATES, liveRequiredForPlane, buildPlaneByTool, cachedPlanes, getPlaneByTool, resolveCandidatePlane, EXPANSION_WEIGHT, expandWeighted, buildToolDocs, cached, buildIndex, getFunnelIndex, CONF_HIGH_TOP1, CONF_HIGH_MARGIN, CONF_HIGH_COVERAGE, CONF_FLOOR_TOP1, CONF_MIN_COVERAGE, CONF_LOW_MARGIN, CONF_RESCUE_TOP1, CONF_RESCUE_COVERAGE, CONF_LOW_COVERAGE, funnelConfidence, semanticCandidates;
|
|
55107
57547
|
var init_semantic_funnel = __esm({
|
|
55108
57548
|
"../mcp/dist/src/semantic-funnel.js"() {
|
|
55109
57549
|
"use strict";
|
|
57550
|
+
init_funnel_utterances();
|
|
55110
57551
|
init_capabilities();
|
|
55111
57552
|
init_tools();
|
|
55112
57553
|
STOPWORDS = /* @__PURE__ */ new Set([
|
|
@@ -55236,14 +57677,187 @@ var init_semantic_funnel = __esm({
|
|
|
55236
57677
|
blow: ["governor", "limit", "risk", "exceed"],
|
|
55237
57678
|
populated: ["population", "filled", "fill", "usage"],
|
|
55238
57679
|
required: ["mandatory", "needed", "blank", "null"],
|
|
55239
|
-
walk: ["explain", "trace", "describe", "order", "execution"]
|
|
57680
|
+
walk: ["explain", "trace", "describe", "order", "execution"],
|
|
57681
|
+
// ---- router-v2 P3 vocabulary bridges (informal → technical). Grown from the
|
|
57682
|
+
// labeled funnel-miss diagnosis; every entry is GENERIC Salesforce vocabulary
|
|
57683
|
+
// (never org-specific) and safe at EXPANSION_WEIGHT (originals always win).
|
|
57684
|
+
// "who has / who's assigned" family → grants & assignment vocabulary
|
|
57685
|
+
assigned: ["permission", "permissionset", "granted", "assignment", "member", "usages"],
|
|
57686
|
+
perm: ["permission", "permissions", "permissionset"],
|
|
57687
|
+
granted: ["permission", "access", "assigned", "effective"],
|
|
57688
|
+
holds: ["assigned", "granted", "permission"],
|
|
57689
|
+
allowed: ["permission", "access", "granted", "effective"],
|
|
57690
|
+
actually: ["effective", "permission", "access"],
|
|
57691
|
+
union: ["effective", "permission", "combined"],
|
|
57692
|
+
combine: ["merge", "effective", "union"],
|
|
57693
|
+
consolidate: ["merge", "combine", "profiles", "permission"],
|
|
57694
|
+
conflicts: ["merge", "conflict", "overlap", "duplicate"],
|
|
57695
|
+
cant: ["access", "permission", "visibility", "see", "blocked"],
|
|
57696
|
+
cannot: ["access", "permission", "visibility", "see", "blocked"],
|
|
57697
|
+
unable: ["access", "permission", "visibility", "see"],
|
|
57698
|
+
// "what writes / what sets" family → writer / provenance vocabulary
|
|
57699
|
+
writes: ["write", "writer", "provenance", "populates", "updates", "changed", "field"],
|
|
57700
|
+
wrote: ["writes", "writer", "provenance", "changed"],
|
|
57701
|
+
writer: ["writes", "provenance", "changed", "flow", "trigger"],
|
|
57702
|
+
sets: ["writes", "populates", "value", "changed"],
|
|
57703
|
+
populates: ["writes", "sets", "provenance", "value", "filled"],
|
|
57704
|
+
updates: ["update", "writes", "changed", "modified"],
|
|
57705
|
+
trace: ["lineage", "provenance", "history", "graph", "walk"],
|
|
57706
|
+
// "do we have / is there / find me" family → search & resolve vocabulary
|
|
57707
|
+
find: ["search", "lookup", "locate", "resolve", "matching", "usages"],
|
|
57708
|
+
lookup: ["find", "resolve", "search", "locate"],
|
|
57709
|
+
locate: ["find", "search", "lookup", "resolve"],
|
|
57710
|
+
called: ["named", "name", "resolve", "find"],
|
|
57711
|
+
named: ["called", "name", "resolve", "find"],
|
|
57712
|
+
anywhere: ["everywhere", "usages", "search", "find", "components"],
|
|
57713
|
+
everywhere: ["anywhere", "usages", "search", "find"],
|
|
57714
|
+
contains: ["references", "includes", "search", "element"],
|
|
57715
|
+
referencing: ["usage", "usages", "references", "used"],
|
|
57716
|
+
points: ["references", "usages", "depends"],
|
|
57717
|
+
// "what is this prefix" family → namespace / package vocabulary
|
|
57718
|
+
prefix: ["namespace", "package", "installed", "managed"],
|
|
57719
|
+
namespace: ["package", "prefix", "installed", "managed"],
|
|
57720
|
+
package: ["namespace", "installed", "managed", "prefix", "dependency"],
|
|
57721
|
+
installed: ["package", "packages", "namespace", "catalog"],
|
|
57722
|
+
managed: ["package", "namespace", "installed"],
|
|
57723
|
+
uninstall: ["package", "impact", "remove", "dependency"],
|
|
57724
|
+
entangled: ["package", "dependency", "impact", "uninstall"],
|
|
57725
|
+
// "what fires / what runs on save" family → save-cascade vocabulary
|
|
57726
|
+
fires: ["fire", "run", "runs", "trigger", "execution", "automation", "save"],
|
|
57727
|
+
fired: ["fire", "run", "trigger", "execution", "automation"],
|
|
57728
|
+
happens: ["fires", "runs", "execution", "automation", "save"],
|
|
57729
|
+
saving: ["save", "automation", "validation", "execution", "trigger"],
|
|
57730
|
+
saved: ["save", "automation", "execution", "trigger"],
|
|
57731
|
+
cascade: ["order", "execution", "save", "automation"],
|
|
57732
|
+
blocks: ["validation", "rule", "save", "required", "error"],
|
|
57733
|
+
blocking: ["validation", "rule", "save", "error"],
|
|
57734
|
+
stops: ["validation", "blocks", "save", "rule", "error"],
|
|
57735
|
+
prevents: ["validation", "blocks", "save", "rule"],
|
|
57736
|
+
automations: ["automation", "flow", "trigger", "workflow", "process", "rule"],
|
|
57737
|
+
lock: ["record", "save", "automation", "contention", "error"],
|
|
57738
|
+
// informal call-topology family → usages / call-graph vocabulary
|
|
57739
|
+
calls: ["call", "invoke", "invokes", "usage", "usages", "graph", "references"],
|
|
57740
|
+
invoke: ["call", "calls", "usage", "references", "apex"],
|
|
57741
|
+
invokes: ["call", "calls", "usage", "references", "apex"],
|
|
57742
|
+
invoked: ["call", "calls", "usage", "references"],
|
|
57743
|
+
nested: ["subflow", "call", "graph", "depth", "invoke"],
|
|
57744
|
+
subflow: ["flow", "call", "graph", "nested", "invoke"],
|
|
57745
|
+
subflows: ["flow", "flows", "call", "graph", "nested"],
|
|
57746
|
+
chain: ["call", "graph", "depth", "dependency", "order"],
|
|
57747
|
+
// dependency loops → cycle vocabulary
|
|
57748
|
+
loop: ["cycle", "cycles", "dependency", "recursion", "circular"],
|
|
57749
|
+
loops: ["cycle", "cycles", "dependency", "recursion", "circular"],
|
|
57750
|
+
circular: ["cycle", "cycles", "dependency", "loop"],
|
|
57751
|
+
recursive: ["cycle", "recursion", "loop", "dependency"],
|
|
57752
|
+
// blast-radius / pre-change safety family → impact vocabulary
|
|
57753
|
+
blast: ["impact", "radius", "breaks", "affected", "change"],
|
|
57754
|
+
radius: ["impact", "blast", "affected"],
|
|
57755
|
+
breaks: ["impact", "affected", "depend", "change", "break"],
|
|
57756
|
+
affected: ["impact", "downstream", "breaks"],
|
|
57757
|
+
downstream: ["impact", "effects", "affected", "dependency"],
|
|
57758
|
+
safe: ["impact", "risk", "change", "delete", "breaks"],
|
|
57759
|
+
safely: ["impact", "risk", "change", "breaks"],
|
|
57760
|
+
// error-driven diagnosis family
|
|
57761
|
+
error: ["failed", "failure", "exception", "validation", "blocked"],
|
|
57762
|
+
failed: ["error", "failure", "exception", "permission"],
|
|
57763
|
+
fault: ["error", "exception", "handling", "flow"],
|
|
57764
|
+
handling: ["fault", "error", "exception", "flow"],
|
|
57765
|
+
// exec-summary / org-tour family → overview & risk vocabulary
|
|
57766
|
+
tour: ["overview", "org", "summary", "inventory"],
|
|
57767
|
+
describe: ["overview", "explain", "summary"],
|
|
57768
|
+
breakdown: ["overview", "explain", "walk", "components", "summary"],
|
|
57769
|
+
big: ["size", "overview", "count", "many", "inventory"],
|
|
57770
|
+
size: ["count", "overview", "many", "storage"],
|
|
57771
|
+
leadership: ["risk", "summary", "overview", "report"],
|
|
57772
|
+
executive: ["risk", "summary", "overview", "report"],
|
|
57773
|
+
worried: ["risk", "report", "health"],
|
|
57774
|
+
confident: ["risk", "report", "health"],
|
|
57775
|
+
shape: ["health", "risk", "overview"],
|
|
57776
|
+
// cleanup / dead-code family
|
|
57777
|
+
unused: ["dead", "usages", "components", "cleanup"],
|
|
57778
|
+
dead: ["unused", "code", "usages"],
|
|
57779
|
+
cleanup: ["unused", "dead", "delete", "candidates"],
|
|
57780
|
+
// freshness / coverage stragglers
|
|
57781
|
+
outdated: ["stale", "fresh", "current"],
|
|
57782
|
+
refresh: ["vault", "stale", "fresh", "current", "health"],
|
|
57783
|
+
tested: ["test", "tests", "coverage"],
|
|
57784
|
+
untested: ["coverage", "gaps", "test", "tests"],
|
|
57785
|
+
// queue / assignment routing family
|
|
57786
|
+
queue: ["assignment", "routing", "owner", "group"],
|
|
57787
|
+
routed: ["assignment", "queue", "rule", "routing"],
|
|
57788
|
+
// ---- router-v2 R2 biz-user vocabulary bridges. Grown from the
|
|
57789
|
+
// business-user persona misses on the additions-tuning set; every entry is
|
|
57790
|
+
// GENERIC business English mapped to Salesforce vocabulary — no org terms.
|
|
57791
|
+
// "which automation gets sunset / retired" → legacy-migration vocabulary
|
|
57792
|
+
sunset: ["retire", "retired", "deprecated", "migrate", "legacy", "replace"],
|
|
57793
|
+
retiring: ["sunset", "deprecated", "migrate", "legacy"],
|
|
57794
|
+
// "when the status flips to X" → transition vocabulary
|
|
57795
|
+
flips: ["changes", "transition", "moves", "value", "status"],
|
|
57796
|
+
// "who are the top contributors" → change-activity vocabulary
|
|
57797
|
+
contributors: ["changes", "modified", "activity", "churn"],
|
|
57798
|
+
// "how are records distributed by X" → grouped-count vocabulary
|
|
57799
|
+
distributed: ["count", "group", "grouped", "breakdown"],
|
|
57800
|
+
// "would you give it a passing grade" → health/risk vocabulary
|
|
57801
|
+
grade: ["risk", "health", "score", "debt"],
|
|
57802
|
+
graded: ["risk", "health", "score", "debt"],
|
|
57803
|
+
architected: ["architecture", "risk", "health", "design"],
|
|
57804
|
+
// "the report my boss uses / the thing she made" → ownership vocabulary
|
|
57805
|
+
boss: ["owner", "user"],
|
|
57806
|
+
made: ["created", "built"],
|
|
57807
|
+
// ---- router-v2 R3 vocabulary bridges. Grown from the v0.1.22 labeled-miss
|
|
57808
|
+
// permissions/flows families (DIAGNOSIS-R3 §2); every entry is GENERIC
|
|
57809
|
+
// Salesforce/business vocabulary — no org terms — and safe at
|
|
57810
|
+
// EXPANSION_WEIGHT (originals always win).
|
|
57811
|
+
// custom-permission family: bypass / gate / guard / unlock verbs → the
|
|
57812
|
+
// permission vocabulary the tool docs actually use.
|
|
57813
|
+
bypass: ["permission", "custom", "validation", "skip"],
|
|
57814
|
+
bypasses: ["bypass", "permission", "custom", "validation"],
|
|
57815
|
+
bypassing: ["bypass", "permission", "custom", "validation"],
|
|
57816
|
+
gate: ["permission", "custom", "guard", "control"],
|
|
57817
|
+
gates: ["gate", "permission", "custom", "guard"],
|
|
57818
|
+
gated: ["gate", "permission", "custom"],
|
|
57819
|
+
guarding: ["gate", "permission", "custom", "validation"],
|
|
57820
|
+
unlock: ["grant", "permission", "access", "enable"],
|
|
57821
|
+
unlocks: ["grant", "permission", "access", "enable"],
|
|
57822
|
+
// PSG-expansion family: expand/inheritance → effective-permission union.
|
|
57823
|
+
expand: ["effective", "permission", "group", "union"],
|
|
57824
|
+
inheritance: ["group", "permission", "effective", "union"],
|
|
57825
|
+
inherits: ["group", "permission", "effective", "union"],
|
|
57826
|
+
// access-diagnostics family: greyed-out / read-only symptom words.
|
|
57827
|
+
greyed: ["edit", "editable", "access", "security", "field"],
|
|
57828
|
+
grayed: ["edit", "editable", "access", "security", "field"],
|
|
57829
|
+
// flow-diagnostics family: "didn't stamp the date", "what does it
|
|
57830
|
+
// orchestrate", "the flow is skipping records".
|
|
57831
|
+
stamp: ["writes", "sets", "populates", "updates", "flow"],
|
|
57832
|
+
stamps: ["writes", "sets", "populates", "updates", "flow"],
|
|
57833
|
+
orchestrate: ["flow", "subflow", "invoke", "explain"],
|
|
57834
|
+
orchestrates: ["flow", "subflow", "invoke", "explain"],
|
|
57835
|
+
skipping: ["skip", "bypass", "decision", "flow"],
|
|
57836
|
+
// automation-footprint family (what_happens_on_save / risk report).
|
|
57837
|
+
footprint: ["automation", "inventory", "overview", "save"]
|
|
55240
57838
|
};
|
|
55241
57839
|
PHRASE_SYNONYMS3 = [
|
|
55242
57840
|
["social security number", "ssn"],
|
|
55243
57841
|
["social security", "ssn"],
|
|
55244
57842
|
["date of birth", "dob"],
|
|
55245
57843
|
["postal code", "zip"],
|
|
55246
|
-
["zip code", "zip"]
|
|
57844
|
+
["zip code", "zip"],
|
|
57845
|
+
// router-v2 P3: "break down X for me" is an EXPLAIN ask, but token-split it
|
|
57846
|
+
// becomes 'break' (→ impact/dependency synonyms) + 'down' — the impact
|
|
57847
|
+
// expansion hijacks the ranking. Collapse the phrase to the single token
|
|
57848
|
+
// 'breakdown' (whose SYNONYMS entry bridges to explain/overview/walk).
|
|
57849
|
+
["break down", "breakdown"],
|
|
57850
|
+
// router-v2 R2 biz-user idioms: altitude metaphors are OVERVIEW asks
|
|
57851
|
+
// ("10,000-foot view" survives tokenization as "...-foot view"), and
|
|
57852
|
+
// "kicks in" is the business phrasing of "fires".
|
|
57853
|
+
["foot view", "overview"],
|
|
57854
|
+
["birds eye view", "overview"],
|
|
57855
|
+
["bird's eye view", "overview"],
|
|
57856
|
+
["big picture", "overview"],
|
|
57857
|
+
["lay of the land", "overview"],
|
|
57858
|
+
["kicks in", "fires"],
|
|
57859
|
+
["kick in", "fire"],
|
|
57860
|
+
["reference sheet", "dictionary"]
|
|
55247
57861
|
].sort((a2, b2) => b2[0].length - a2[0].length);
|
|
55248
57862
|
applyPhraseSynonyms3 = (lower) => {
|
|
55249
57863
|
let out = lower;
|
|
@@ -55267,7 +57881,7 @@ var init_semantic_funnel = __esm({
|
|
|
55267
57881
|
return out;
|
|
55268
57882
|
};
|
|
55269
57883
|
TOOL_KEYWORDS = {
|
|
55270
|
-
"sfi.list_components": "list inventory enumerate catalog all what do we have how many exist objects fields custom fields how many fields on object contact account lead opportunity case metadata count layouts list views triggers validation rules record types web links flows classes triggers profiles permission sets layouts record types validation rules approval processes reports dashboards omniscripts custom standard relationship child parent inactive active picklist values which flows are inactive active draft obsolete flows triggers rules by status enumerate components",
|
|
57884
|
+
"sfi.list_components": "list inventory enumerate catalog all what do we have how many exist objects fields custom fields how many fields on object contact account lead opportunity case metadata count layouts list views triggers validation rules record types web links flows classes triggers profiles permission sets layouts record types validation rules approval processes reports dashboards omniscripts custom standard relationship child parent inactive active picklist values which flows are inactive active draft obsolete flows triggers rules by status enumerate components custom permissions defined exist inventory what do they gate",
|
|
55271
57885
|
"sfi.capabilities": "what can you do help capabilities what can i ask how do i use",
|
|
55272
57886
|
"sfi.automation_risk_report": "objects more than one multiple triggers per object trigger quality automation risk",
|
|
55273
57887
|
"sfi.live_group_count": "how many users assigned to profile membership group count",
|
|
@@ -55281,7 +57895,12 @@ var init_semantic_funnel = __esm({
|
|
|
55281
57895
|
"sfi.layout_for_user": "which layout does the profile user see page on object",
|
|
55282
57896
|
"sfi.integration_map": "api limits at risk integration volume callout capacity external",
|
|
55283
57897
|
"sfi.find_apex_usages": "which flows invoke call use apex classes methods from",
|
|
55284
|
-
"sfi.live_folder_access": "who can access report dashboard folder pipeline see view shared"
|
|
57898
|
+
"sfi.live_folder_access": "who can access report dashboard folder pipeline see view shared",
|
|
57899
|
+
// router-v2 P3: the two labeled miss families for this tool are "who has this
|
|
57900
|
+
// permission set" (assignment/grant phrasing) and "what references this
|
|
57901
|
+
// component" — its (long) description dilutes those terms, so the overlay
|
|
57902
|
+
// re-weights them. Referee: funnel-recall blind-spot cases.
|
|
57903
|
+
"sfi.find_component_usages": "who has this permission set assigned granted holds it references referenced still used anywhere"
|
|
55285
57904
|
};
|
|
55286
57905
|
PLANE_OVERRIDES = {
|
|
55287
57906
|
"sfi.blast_radius_live": "live",
|
|
@@ -55319,21 +57938,28 @@ var init_semantic_funnel = __esm({
|
|
|
55319
57938
|
const plane = planeForTool(toolName);
|
|
55320
57939
|
return { plane, liveRequired: liveRequiredForPlane(plane) };
|
|
55321
57940
|
};
|
|
55322
|
-
|
|
55323
|
-
|
|
57941
|
+
EXPANSION_WEIGHT = 0.5;
|
|
57942
|
+
expandWeighted = (tokens) => {
|
|
57943
|
+
const w2 = /* @__PURE__ */ new Map();
|
|
57944
|
+
for (const t2 of tokens)
|
|
57945
|
+
w2.set(t2, (w2.get(t2) ?? 0) + 1);
|
|
55324
57946
|
for (const t2 of tokens) {
|
|
55325
57947
|
const syn = SYNONYMS[t2];
|
|
55326
|
-
if (syn !== void 0)
|
|
55327
|
-
|
|
57948
|
+
if (syn !== void 0) {
|
|
57949
|
+
for (const s2 of syn)
|
|
57950
|
+
if (!w2.has(s2))
|
|
57951
|
+
w2.set(s2, EXPANSION_WEIGHT);
|
|
57952
|
+
}
|
|
55328
57953
|
}
|
|
55329
|
-
return
|
|
57954
|
+
return w2;
|
|
55330
57955
|
};
|
|
55331
57956
|
buildToolDocs = () => {
|
|
55332
57957
|
const docs = /* @__PURE__ */ new Map();
|
|
55333
57958
|
for (const tool of V01_TOOLS) {
|
|
55334
57959
|
const nameWords = tool.name.replace(/^sfi\./, "").replace(/_/g, " ");
|
|
55335
57960
|
const keywords = TOOL_KEYWORDS[tool.name] ?? "";
|
|
55336
|
-
|
|
57961
|
+
const utterances = (FUNNEL_UTTERANCES[tool.name] ?? []).join(" ");
|
|
57962
|
+
docs.set(tool.name, `${nameWords} ${nameWords} ${tool.description} ${keywords} ${utterances}`);
|
|
55337
57963
|
}
|
|
55338
57964
|
for (const cat of CATEGORIES) {
|
|
55339
57965
|
const catText = ` ${cat.title} ${cat.description} ${cat.exampleQuestions.join(" ")}`;
|
|
@@ -55415,8 +58041,8 @@ var init_semantic_funnel = __esm({
|
|
|
55415
58041
|
const idx = getFunnelIndex();
|
|
55416
58042
|
const planes = getPlaneByTool();
|
|
55417
58043
|
const rawQueryTokens = new Set(tokenize6(question, true));
|
|
55418
|
-
const
|
|
55419
|
-
if (
|
|
58044
|
+
const qWeights = expandWeighted(tokenize6(question, true));
|
|
58045
|
+
if (qWeights.size === 0)
|
|
55420
58046
|
return [];
|
|
55421
58047
|
let coverage = 0;
|
|
55422
58048
|
if (rawQueryTokens.size > 0) {
|
|
@@ -55426,13 +58052,10 @@ var init_semantic_funnel = __esm({
|
|
|
55426
58052
|
hits += 1;
|
|
55427
58053
|
coverage = hits / rawQueryTokens.size;
|
|
55428
58054
|
}
|
|
55429
|
-
const qtf = /* @__PURE__ */ new Map();
|
|
55430
|
-
for (const t2 of qTokens)
|
|
55431
|
-
qtf.set(t2, (qtf.get(t2) ?? 0) + 1);
|
|
55432
58055
|
const qvec = /* @__PURE__ */ new Map();
|
|
55433
58056
|
let qnorm = 0;
|
|
55434
|
-
for (const [term,
|
|
55435
|
-
const w2 =
|
|
58057
|
+
for (const [term, weight] of qWeights) {
|
|
58058
|
+
const w2 = weight * (idx.idf.get(term) ?? 0);
|
|
55436
58059
|
if (w2 > 0) {
|
|
55437
58060
|
qvec.set(term, w2);
|
|
55438
58061
|
qnorm += w2 * w2;
|
|
@@ -55453,10 +58076,14 @@ var init_semantic_funnel = __esm({
|
|
|
55453
58076
|
if (dot <= 0)
|
|
55454
58077
|
continue;
|
|
55455
58078
|
const score = dot / qnorm;
|
|
58079
|
+
const rounded = Math.round(score * 1e3) / 1e3;
|
|
55456
58080
|
const planeEntry = planes.get(tool) ?? { plane: "vault", liveRequired: false };
|
|
55457
58081
|
scored.push({
|
|
55458
58082
|
tool,
|
|
55459
|
-
score:
|
|
58083
|
+
score: rounded,
|
|
58084
|
+
// Pre-fusion semantic evidence — identical to `score` here; preserved
|
|
58085
|
+
// verbatim through route_question's regex-bonus fusion (P2 §4).
|
|
58086
|
+
cosine: rounded,
|
|
55460
58087
|
category: idx.toolCategory.get(tool) ?? null,
|
|
55461
58088
|
plane: planeEntry.plane,
|
|
55462
58089
|
liveRequired: planeEntry.liveRequired
|
|
@@ -55506,7 +58133,7 @@ import { createHash as createHash7 } from "node:crypto";
|
|
|
55506
58133
|
import { readFile as readFile82 } from "node:fs/promises";
|
|
55507
58134
|
import { join as join23, resolve as resolve3 } from "node:path";
|
|
55508
58135
|
import { z as z115 } from "zod";
|
|
55509
|
-
var RESOLVE_FALLBACK_MAX_TOKENS, SAVE_ORDER_INTENTS, SINGLE_ENTITY_INTENTS, COMPARISON_ASIDE, stripComparisonAside, tryResolveFallback, routeQuestionInputSchema, routeTrust, clarificationIdFor, selectedEntityArgsForRoute, extractEntityQuery, inferEntityTypes, refineEntityResolution, applyGlossaryAliases, entityAmbiguityRequiresClarification, splitCompoundQuestion, mixedInventoryAndStoragePlan, PLAN_FAMILY, ASSESSMENT_FAMILY, ROUTE_PREAMBLE_TOOLS, TOOL_COMPATIBLE_TYPES, ACCESS_FLAVORED_TOOLS, resolvedTypeForGuard, applyComponentTypeGuard, REGEX_BONUS, resolveActiveVaultAlias, buildRouteToolArgsMap, mergeRouteHintsIntoCandidates, invokeFromArgsMap, rerankForMode, MARGIN, DESTRUCTIVE_TOOL, INFORMATIONAL_IMPACT_TOOL, planesDiverge, risksDiverge, marginClarification, LIVE_DISCLOSURE_LOOKAHEAD, liveConsentDisclosure, guidanceForMode, buildFunnelCandidates, routerMode, routeQuestionHandler;
|
|
58136
|
+
var RESOLVE_FALLBACK_MAX_TOKENS, SAVE_ORDER_INTENTS, SINGLE_ENTITY_INTENTS, COMPARISON_ASIDE, stripComparisonAside, tryResolveFallback, routeQuestionInputSchema, routeTrust, clarificationIdFor, selectedEntityArgsForRoute, isToolNameMention, extractEntityQuery, NAME_IGNORABLE_WORD, looksLikeComponentName, extractExistenceProbeToken, inferEntityTypes, normLiteral, refineEntityResolution, resolveParentQualifier, applyGlossaryAliases, questionAssertsNameMultiplicity, entityAmbiguityRequiresClarification, hygienicClarificationOptions, splitCompoundQuestion, mixedInventoryAndStoragePlan, PLAN_FAMILY, ASSESSMENT_FAMILY, ROUTE_PREAMBLE_TOOLS, TOOL_COMPATIBLE_TYPES, ACCESS_FLAVORED_TOOLS, resolvedTypeForGuard, applyComponentTypeGuard, REGEX_BONUS, resolveActiveVaultAlias, buildRouteToolArgsMap, mergeRouteHintsIntoCandidates, invokeFromArgsMap, rerankForMode, MARGIN, FUNNEL_PRIMARY_MIN_SCORE, DESTRUCTIVE_TOOL, INFORMATIONAL_IMPACT_TOOL, planesDiverge, LIVE_DATA_SIGNAL, resolvePlaneTie, isCompoundPlaneAsk, risksDiverge, marginClarification, LIVE_DISCLOSURE_LOOKAHEAD, liveConsentDisclosure, guidanceForMode, buildFunnelCandidates, routerMode, REFUSAL_INTENT, REFUSAL_GAP, refusalResponse, CONTEXT_SAVE_ORDER_TOOLS, CONTEXT_TOOL_ARG_KEYS, GENERIC_SCHEMA_INTENTS, contextArgsFor, buildContextContinuation, routeQuestionHandler;
|
|
55510
58137
|
var init_route_question = __esm({
|
|
55511
58138
|
"../mcp/dist/src/tools/route-question.js"() {
|
|
55512
58139
|
"use strict";
|
|
@@ -55514,7 +58141,9 @@ var init_route_question = __esm({
|
|
|
55514
58141
|
init_src2();
|
|
55515
58142
|
init_src3();
|
|
55516
58143
|
init_answer_render();
|
|
58144
|
+
init_context_resolution();
|
|
55517
58145
|
init_intent_router();
|
|
58146
|
+
init_refusal_gates();
|
|
55518
58147
|
init_semantic_funnel();
|
|
55519
58148
|
init_resolve2();
|
|
55520
58149
|
init_tool_profile();
|
|
@@ -55549,6 +58178,8 @@ var init_route_question = __esm({
|
|
|
55549
58178
|
if (disposition !== "exact" && disposition !== "ambiguous" || candidates.length === 0) {
|
|
55550
58179
|
return null;
|
|
55551
58180
|
}
|
|
58181
|
+
const hygienicOptions = disposition === "ambiguous" ? hygienicClarificationOptions(candidates.slice(0, 5)) : [];
|
|
58182
|
+
const effectivelyExact = disposition === "exact" || hygienicOptions.length === 1;
|
|
55552
58183
|
return {
|
|
55553
58184
|
question,
|
|
55554
58185
|
plane: "vault",
|
|
@@ -55558,13 +58189,13 @@ var init_route_question = __esm({
|
|
|
55558
58189
|
needsResolve: true,
|
|
55559
58190
|
reason: `Names a component (resolve disposition: ${disposition}) but asks nothing specific yet. Resolve it, then pick the analysis you want.`,
|
|
55560
58191
|
gap: null,
|
|
55561
|
-
confidence:
|
|
58192
|
+
confidence: effectivelyExact ? "high" : "low",
|
|
55562
58193
|
risk: "informational",
|
|
55563
58194
|
alternatives: [],
|
|
55564
|
-
clarification: disposition === "ambiguous" ? {
|
|
58195
|
+
clarification: disposition === "ambiguous" && !effectivelyExact ? {
|
|
55565
58196
|
required: true,
|
|
55566
58197
|
question: "Several components match. Which component did you mean?",
|
|
55567
|
-
options:
|
|
58198
|
+
options: hygienicOptions.map((candidate) => candidate.id)
|
|
55568
58199
|
} : null,
|
|
55569
58200
|
plan: [{
|
|
55570
58201
|
stepId: "step-1",
|
|
@@ -55602,7 +58233,39 @@ var init_route_question = __esm({
|
|
|
55602
58233
|
* coverage tools). When set, toolCandidates + a mode-specific guidance line are
|
|
55603
58234
|
* always attached, regardless of the deterministic route's confidence.
|
|
55604
58235
|
*/
|
|
55605
|
-
mode: z115.enum(["ask", "plan", "assessment"]).optional()
|
|
58236
|
+
mode: z115.enum(["ask", "plan", "assessment"]).optional(),
|
|
58237
|
+
/**
|
|
58238
|
+
* Router-v2 P5: HOST-PASSED conversation context. The product is STATELESS —
|
|
58239
|
+
* conversation memory belongs to the host, which passes what the PREVIOUS
|
|
58240
|
+
* turn was about so a terse follow-up ("does it fire on delete too?") can be
|
|
58241
|
+
* resolved; nothing is stored server-side. `context` absent ⇒ every code
|
|
58242
|
+
* path is byte-identical to a call without the feature. Value validation is
|
|
58243
|
+
* FAIL-OPEN (an unregistered `tool` / malformed `componentId` is ignored and
|
|
58244
|
+
* noted in `contextApplied.ignored`); shape errors reject normally. Context
|
|
58245
|
+
* strings are NEVER treated as question text — `previous.question` is only
|
|
58246
|
+
* ever re-dispatched through the full handler (refusal gates included) for
|
|
58247
|
+
* clarification continuation. A self-contained question IGNORES context.
|
|
58248
|
+
*/
|
|
58249
|
+
context: z115.object({
|
|
58250
|
+
previous: z115.object({
|
|
58251
|
+
/** Canonical id the prior answer was about, e.g. "Flow:Order_Sync". */
|
|
58252
|
+
componentId: z115.string().min(1).max(256).optional(),
|
|
58253
|
+
/** Object api name in focus last turn, e.g. "Contact". */
|
|
58254
|
+
objectApiName: z115.string().min(1).max(256).optional(),
|
|
58255
|
+
/** sfi.* tool that produced the prior answer. */
|
|
58256
|
+
tool: z115.string().regex(/^sfi\.[a-z0-9_]+$/).optional(),
|
|
58257
|
+
/** route.intent of the prior turn (advisory only). */
|
|
58258
|
+
intent: z115.string().min(1).max(64).optional(),
|
|
58259
|
+
plane: z115.enum(["vault", "live", "hybrid"]).optional(),
|
|
58260
|
+
/** Prior turn's question text — required ONLY for clarification continuation. */
|
|
58261
|
+
question: z115.string().min(1).max(2e3).optional(),
|
|
58262
|
+
/** Open disambiguation set from the prior turn's blocking clarification. */
|
|
58263
|
+
clarification: z115.object({
|
|
58264
|
+
clarificationId: z115.string().min(1).max(64),
|
|
58265
|
+
options: z115.array(z115.string().min(1).max(256)).min(1).max(10)
|
|
58266
|
+
}).strict().optional()
|
|
58267
|
+
}).strict()
|
|
58268
|
+
}).strict().optional()
|
|
55606
58269
|
});
|
|
55607
58270
|
routeTrust = () => ({
|
|
55608
58271
|
provenance: "offline_snapshot",
|
|
@@ -55644,8 +58307,12 @@ var init_route_question = __esm({
|
|
|
55644
58307
|
}
|
|
55645
58308
|
return null;
|
|
55646
58309
|
};
|
|
58310
|
+
isToolNameMention = (token) => {
|
|
58311
|
+
const bare = token.toLowerCase().replace(/^sfi[._]/, "");
|
|
58312
|
+
return getPlaneByTool().has(`sfi.${bare}`);
|
|
58313
|
+
};
|
|
55647
58314
|
extractEntityQuery = (question, intent) => {
|
|
55648
|
-
const apiReference = question.
|
|
58315
|
+
const apiReference = [...question.matchAll(/\b[A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z][A-Za-z0-9_]*)\b|\b[A-Za-z][A-Za-z0-9_]*__(?:c|mdt|e|x|b|kav)\b|\b[A-Za-z][A-Za-z0-9]*(?:_+[A-Za-z0-9]+)+\b/g)].map((match) => match[0]).find((token) => !isToolNameMention(token));
|
|
55649
58316
|
if (apiReference !== void 0) {
|
|
55650
58317
|
if (intent === "what-if-method-signature" && apiReference.includes(".")) {
|
|
55651
58318
|
return `${apiReference.slice(0, apiReference.indexOf("."))} class`;
|
|
@@ -55657,6 +58324,9 @@ var init_route_question = __esm({
|
|
|
55657
58324
|
}
|
|
55658
58325
|
return apiReference;
|
|
55659
58326
|
}
|
|
58327
|
+
const quotedTyped = question.match(/['"‘“]([A-Za-z][A-Za-z0-9_ ]{1,40}?)['"’”]\s+(?:field|object|profile|permission\s+set|flow|record\s+type)\b/i)?.[1];
|
|
58328
|
+
if (quotedTyped !== void 0)
|
|
58329
|
+
return quotedTyped.trim();
|
|
55660
58330
|
const prefixedTypePhrase = question.match(/\b((?:validation\s+rule|permission\s+set|record\s+type|page\s+layout|object|field|flow|class|trigger|layout|profile|report|dashboard)\s+(?:named\s+)?[A-Z][A-Za-z0-9_]*(?:\s+[A-Z][A-Za-z0-9_]*){0,5})\b/)?.[1];
|
|
55661
58331
|
if (prefixedTypePhrase !== void 0)
|
|
55662
58332
|
return prefixedTypePhrase.trim();
|
|
@@ -55664,7 +58334,7 @@ var init_route_question = __esm({
|
|
|
55664
58334
|
const typedPhrase = typedMatch?.[1];
|
|
55665
58335
|
if (typedPhrase === void 0)
|
|
55666
58336
|
return null;
|
|
55667
|
-
const cleaned = typedPhrase.replace(/^(?:(?:what|whatever|which|who|where|when|why|how|is|are|can|does|do|did|show|find|explain|locate|list|calls?|invokes?|references?|owns?|edit|read|view|access|change|delete|remove|possible|values?|apex|api|version|data|type|for|of|the|this|that|in|on|to|used|assigned|set|every|all|each|any|me|a|an)\s+)+/i, "").trim();
|
|
58337
|
+
const cleaned = typedPhrase.replace(/^(?:(?:what|whats|whatever|which|who|whos|where|wheres|when|why|how|hows|is|are|can|does|do|did|show|find|explain|locate|list|walk|through|pull|give|gimme|tell|about|break|down|compare|effective|calls?|invokes?|references?|owns?|edit|read|view|access|change|delete|remove|possible|values?|apex|api|version|data|type|for|of|the|this|that|in|on|to|used|assigned|set|every|all|each|any|me|my|us|a|an)\s+)+/i, "").trim();
|
|
55668
58338
|
if (/\b(?:and|when|should|use|there|any|that|no|available|tools?|difference|between|required\s+fields?|record\s+types?)\b/i.test(cleaned))
|
|
55669
58339
|
return null;
|
|
55670
58340
|
const distinctive = cleaned.replace(/\b(?:object|field|flow|class|trigger|page|layout|profile|permission|set|record|type|validation|rule|report|dashboard|data|logic)\b/gi, "").replace(/[^A-Za-z0-9_]+/g, "");
|
|
@@ -55674,11 +58344,18 @@ var init_route_question = __esm({
|
|
|
55674
58344
|
const parent = typedMatch?.[2];
|
|
55675
58345
|
return parent === void 0 ? phrase : `${phrase} on ${parent}`;
|
|
55676
58346
|
};
|
|
58347
|
+
NAME_IGNORABLE_WORD = /^(?:object|field|flow|class|trigger|page|layout|profile|permission|set|record|type|validation|rule|report|dashboard|logic|named|of|and|or|the|a|an|for)$/i;
|
|
58348
|
+
looksLikeComponentName = (query) => {
|
|
58349
|
+
const words = query.trim().split(/\s+/);
|
|
58350
|
+
if (words.length <= 1)
|
|
58351
|
+
return true;
|
|
58352
|
+
const nameWords = words.filter((word) => !NAME_IGNORABLE_WORD.test(word));
|
|
58353
|
+
return nameWords.length > 0 && nameWords.every((word) => /^["'‘“(]?[A-Z0-9]/.test(word));
|
|
58354
|
+
};
|
|
58355
|
+
extractExistenceProbeToken = (question) => [...question.matchAll(/\b[A-Za-z][A-Za-z0-9_]*__(?:c|mdt|e|x|b|kav)\b|\b[A-Za-z][A-Za-z0-9]*(?:_+[A-Za-z0-9]+)+\b/g)].map((match) => match[0]).find((token) => !isToolNameMention(token) && !/^\d/.test(token)) ?? null;
|
|
55677
58356
|
inferEntityTypes = (query, intent, question) => {
|
|
55678
58357
|
if (intent === "what-if-method-signature")
|
|
55679
58358
|
return ["ApexClass"];
|
|
55680
|
-
if (/\bfield\b/i.test(question))
|
|
55681
|
-
return ["CustomField"];
|
|
55682
58359
|
if (query.includes(".") && !/\sclass$/i.test(query))
|
|
55683
58360
|
return ["CustomField"];
|
|
55684
58361
|
if (/__(?:mdt|e|x|b|kav)$/i.test(query) || /(?:^object\s|\sobject(?:\s+on\s+\w+)?$)/i.test(query))
|
|
@@ -55705,18 +58382,40 @@ var init_route_question = __esm({
|
|
|
55705
58382
|
return ["Report"];
|
|
55706
58383
|
if (/(?:^dashboard\s|\sdashboard$)/i.test(query))
|
|
55707
58384
|
return ["Dashboard"];
|
|
58385
|
+
const escaped = query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
58386
|
+
const qualifier = question.match(new RegExp(`\\b${escaped}\\s+(object|flow|field|trigger|profile|permission\\s+set)\\b`, "i"))?.[1]?.toLowerCase().replace(/\s+/g, " ");
|
|
58387
|
+
if (qualifier === "object")
|
|
58388
|
+
return ["CustomObject"];
|
|
58389
|
+
if (qualifier === "flow")
|
|
58390
|
+
return ["Flow"];
|
|
58391
|
+
if (qualifier === "field")
|
|
58392
|
+
return ["CustomField"];
|
|
58393
|
+
if (qualifier === "trigger")
|
|
58394
|
+
return ["ApexTrigger"];
|
|
58395
|
+
if (qualifier === "profile")
|
|
58396
|
+
return ["Profile"];
|
|
58397
|
+
if (qualifier === "permission set")
|
|
58398
|
+
return ["PermissionSet"];
|
|
58399
|
+
if (/\bfield\b/i.test(question))
|
|
58400
|
+
return ["CustomField"];
|
|
55708
58401
|
return [];
|
|
55709
58402
|
};
|
|
58403
|
+
normLiteral = (value) => value.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
55710
58404
|
refineEntityResolution = (query, resolution) => {
|
|
55711
58405
|
if (resolution.disposition !== "ambiguous")
|
|
55712
58406
|
return resolution;
|
|
55713
58407
|
const normalized = query.toLowerCase();
|
|
55714
|
-
const
|
|
58408
|
+
const looseQuery = normLiteral(query);
|
|
58409
|
+
const queryHasSpace = /\s/.test(query.trim());
|
|
58410
|
+
const exactApi = resolution.candidates.filter((candidate) => candidate.apiName.toLowerCase() === normalized || candidate.id.toLowerCase().endsWith(`:${normalized}`) || normLiteral(candidate.apiName) === looseQuery && looseQuery.length >= 4 && !(candidate.type === "CustomField" && queryHasSpace));
|
|
55715
58411
|
const parentQualified = query.match(/^(.+?)\s+field\s+on\s+([A-Za-z][A-Za-z0-9_]*)$/i);
|
|
55716
58412
|
const normalizedName = parentQualified?.[1]?.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
55717
58413
|
const normalizedParent = parentQualified?.[2]?.replace(/[^a-z0-9]/gi, "").toLowerCase();
|
|
55718
58414
|
const exactParentField = normalizedName === void 0 || normalizedParent === void 0 ? [] : resolution.candidates.filter((candidate) => candidate.type === "CustomField" && candidate.apiName.replace(/__[a-z]+$/i, "").replace(/[^a-z0-9]/gi, "").toLowerCase() === normalizedName && candidate.parentApiName?.replace(/[^a-z0-9]/gi, "").toLowerCase() === normalizedParent);
|
|
55719
|
-
const
|
|
58415
|
+
const parentPlusName = queryHasSpace && looseQuery.length >= 4 ? resolution.candidates.filter((candidate) => candidate.type === "CustomField" && candidate.parentApiName !== null && normLiteral(candidate.parentApiName) + normLiteral(candidate.apiName.replace(/__[a-z]+$/i, "")) === looseQuery) : [];
|
|
58416
|
+
const objectLiteralNames = new Set(exactApi.filter((candidate) => candidate.type === "CustomObject").map((candidate) => normLiteral(candidate.apiName)));
|
|
58417
|
+
const exactApiNoTabTwins = exactApi.filter((candidate) => candidate.type !== "CustomTab" || !objectLiteralNames.has(normLiteral(candidate.apiName)));
|
|
58418
|
+
const exact = exactParentField.length === 1 ? exactParentField : parentPlusName.length === 1 ? parentPlusName : exactApiNoTabTwins.length === 1 ? exactApiNoTabTwins : exactApi;
|
|
55720
58419
|
if (exact.length !== 1)
|
|
55721
58420
|
return resolution;
|
|
55722
58421
|
const winner = exact[0];
|
|
@@ -55726,6 +58425,31 @@ var init_route_question = __esm({
|
|
|
55726
58425
|
candidates: [winner, ...resolution.candidates.filter((candidate) => candidate.id !== winner.id)]
|
|
55727
58426
|
};
|
|
55728
58427
|
};
|
|
58428
|
+
resolveParentQualifier = (question, resolution) => {
|
|
58429
|
+
if (resolution.disposition !== "ambiguous")
|
|
58430
|
+
return resolution;
|
|
58431
|
+
const top = resolution.candidates[0];
|
|
58432
|
+
if (top === void 0)
|
|
58433
|
+
return resolution;
|
|
58434
|
+
const family = resolution.candidates.filter((candidate) => candidate.apiName.toLowerCase() === top.apiName.toLowerCase() && candidate.parentApiName !== null);
|
|
58435
|
+
if (family.length < 2)
|
|
58436
|
+
return resolution;
|
|
58437
|
+
const questionWords = new Set(question.toLowerCase().split(/[^a-z0-9_]+/).filter((word) => word.length > 0));
|
|
58438
|
+
const namesParent = (parentApiName) => {
|
|
58439
|
+
const full = parentApiName.toLowerCase();
|
|
58440
|
+
const bare = full.replace(/__c$/, "").replace(/^[a-z0-9]+__/, "");
|
|
58441
|
+
return questionWords.has(full) || questionWords.has(bare) || questionWords.has(`${bare}s`);
|
|
58442
|
+
};
|
|
58443
|
+
const parentNamed = family.filter((candidate) => namesParent(candidate.parentApiName));
|
|
58444
|
+
if (parentNamed.length !== 1)
|
|
58445
|
+
return resolution;
|
|
58446
|
+
const winner = parentNamed[0];
|
|
58447
|
+
return {
|
|
58448
|
+
...resolution,
|
|
58449
|
+
disposition: "exact",
|
|
58450
|
+
candidates: [winner, ...resolution.candidates.filter((candidate) => candidate.id !== winner.id)]
|
|
58451
|
+
};
|
|
58452
|
+
};
|
|
55729
58453
|
applyGlossaryAliases = async (ctx, query, types, resolution) => {
|
|
55730
58454
|
if (resolution.disposition === "exact")
|
|
55731
58455
|
return resolution;
|
|
@@ -55745,6 +58469,7 @@ var init_route_question = __esm({
|
|
|
55745
58469
|
parentApiName: candidate.parentApiName,
|
|
55746
58470
|
score: candidate.score,
|
|
55747
58471
|
base: candidate.base,
|
|
58472
|
+
nameCoverage: 1,
|
|
55748
58473
|
matchKind: "exact",
|
|
55749
58474
|
evidence: candidate.evidence
|
|
55750
58475
|
})),
|
|
@@ -55752,9 +58477,24 @@ var init_route_question = __esm({
|
|
|
55752
58477
|
]
|
|
55753
58478
|
};
|
|
55754
58479
|
};
|
|
55755
|
-
|
|
58480
|
+
questionAssertsNameMultiplicity = (question) => /\b(?:a\s+few|several|a\s+couple)\s+of\s+(?:these|those|them)\b|\b(?:two|three|four|five|\d+)\s+different\s+(?:things?|ones?|fields?|objects?|components?|versions?)\b|\bmore\s+than\s+one\s+(?:of\s+(?:these|those|them)|with\s+(?:that|this)\s+name)\b|\bwhich\s+one\s+(?:do|should|did)\s+i\b/i.test(question);
|
|
58481
|
+
entityAmbiguityRequiresClarification = (query, resolution, question) => {
|
|
58482
|
+
if (resolution.disposition === "exact" && resolution.candidates.length >= 2 && !/__|\./.test(query)) {
|
|
58483
|
+
const [top2, second2] = resolution.candidates;
|
|
58484
|
+
const sameName = top2 !== void 0 && second2 !== void 0 && top2.matchKind === "exact" && second2.matchKind === "exact" && top2.base === second2.base && top2.apiName.toLowerCase() === second2.apiName.toLowerCase() && top2.parentApiName !== second2.parentApiName;
|
|
58485
|
+
if (sameName) {
|
|
58486
|
+
const promoted = resolveParentQualifier(question ?? "", {
|
|
58487
|
+
...resolution,
|
|
58488
|
+
disposition: "ambiguous"
|
|
58489
|
+
});
|
|
58490
|
+
if (promoted.disposition === "ambiguous")
|
|
58491
|
+
return true;
|
|
58492
|
+
}
|
|
58493
|
+
}
|
|
55756
58494
|
if (resolution.disposition !== "ambiguous" || resolution.candidates.length < 2)
|
|
55757
58495
|
return false;
|
|
58496
|
+
if (question !== void 0 && questionAssertsNameMultiplicity(question))
|
|
58497
|
+
return true;
|
|
55758
58498
|
const normalized = query.toLowerCase();
|
|
55759
58499
|
const literalMatches = resolution.candidates.filter((candidate) => candidate.apiName.toLowerCase() === normalized || candidate.id.toLowerCase().endsWith(`:${normalized}`));
|
|
55760
58500
|
if (literalMatches.length > 1)
|
|
@@ -55762,7 +58502,15 @@ var init_route_question = __esm({
|
|
|
55762
58502
|
if (/[A-Za-z0-9_]__(?:c|mdt|e|x|b|kav)\b|\w+\.\w+/.test(query))
|
|
55763
58503
|
return false;
|
|
55764
58504
|
const [top, second] = resolution.candidates;
|
|
55765
|
-
|
|
58505
|
+
const looksLikeNamedComponent = (candidate) => candidate.matchKind !== "fuzzy" && (candidate.nameCoverage ?? 1) >= 0.5;
|
|
58506
|
+
return top.base >= 0.92 && second.base >= 0.92 && second.score >= top.score * 0.97 && looksLikeNamedComponent(top) && looksLikeNamedComponent(second);
|
|
58507
|
+
};
|
|
58508
|
+
hygienicClarificationOptions = (candidates) => {
|
|
58509
|
+
const top = candidates[0];
|
|
58510
|
+
if (top === void 0)
|
|
58511
|
+
return [];
|
|
58512
|
+
const kept = candidates.filter((candidate) => candidate.matchKind !== "fuzzy" && candidate.base >= top.base * 0.9);
|
|
58513
|
+
return kept.length > 0 && kept[0] === top ? kept : [top, ...kept.filter((c2) => c2 !== top)];
|
|
55766
58514
|
};
|
|
55767
58515
|
splitCompoundQuestion = (question) => {
|
|
55768
58516
|
const parts = question.split(/(\?|;|\band\s+then\b|\bthen\b|\band\s+(?=who|what|which|how|where|when|why|is|are|can|should|does|do)\b)/i);
|
|
@@ -55940,6 +58688,7 @@ var init_route_question = __esm({
|
|
|
55940
58688
|
byTool.set(tool, {
|
|
55941
58689
|
tool,
|
|
55942
58690
|
score: fuse(0),
|
|
58691
|
+
cosine: 0,
|
|
55943
58692
|
category: null,
|
|
55944
58693
|
plane,
|
|
55945
58694
|
liveRequired,
|
|
@@ -55964,12 +58713,36 @@ var init_route_question = __esm({
|
|
|
55964
58713
|
return [...lead, ...rest].slice(0, 8);
|
|
55965
58714
|
};
|
|
55966
58715
|
MARGIN = 0.05;
|
|
58716
|
+
FUNNEL_PRIMARY_MIN_SCORE = 0.26;
|
|
55967
58717
|
DESTRUCTIVE_TOOL = /^sfi\.(safe_to_delete_field|what_if_)/;
|
|
55968
58718
|
INFORMATIONAL_IMPACT_TOOL = /^sfi\.(get_impact|get_edges|get_subgraph|downstream_effects|find_[a-z_]*usages|find_formula_references|field_lineage)$/;
|
|
55969
58719
|
planesDiverge = (a2, b2) => {
|
|
55970
58720
|
const planes = /* @__PURE__ */ new Set([a2.plane, b2.plane]);
|
|
55971
58721
|
return planes.has("vault") && (planes.has("live") || planes.has("hybrid"));
|
|
55972
58722
|
};
|
|
58723
|
+
LIVE_DATA_SIGNAL = /\bhow\s+many\b|\bcounts?\b|\bfill\s+rate\b|\bpopulat(?:ed|ion)\b|\bstorage\b|\bdata\s+volume\b|\blog(?:ged)?\s?in(?:to)?\b|\blast\s+login\b|\bright\s+now\b|\bcurrently\b|\bas\s+of\s+(?:now|today)\b|\bin\s+prod(?:uction)?\b|\bsample\s+records?\b|\brows?\b|\brecord\s+data\b|\bactual(?:ly)?\s+(?:records?|values?|data|used?|runs?)\b|\bapi\s+usage\b|\blimits?\s+headroom\b/i;
|
|
58724
|
+
resolvePlaneTie = (cands, question) => {
|
|
58725
|
+
const [top, second] = cands;
|
|
58726
|
+
if (top === void 0 || second === void 0)
|
|
58727
|
+
return [...cands];
|
|
58728
|
+
if (top.score - second.score > MARGIN)
|
|
58729
|
+
return [...cands];
|
|
58730
|
+
if (!planesDiverge(top, second))
|
|
58731
|
+
return [...cands];
|
|
58732
|
+
if (top.fromRoute === true && second.fromRoute === true)
|
|
58733
|
+
return [...cands];
|
|
58734
|
+
const wantsLive = LIVE_DATA_SIGNAL.test(question);
|
|
58735
|
+
const topReachesOrg = top.plane === "live" || top.plane === "hybrid";
|
|
58736
|
+
if (wantsLive === topReachesOrg)
|
|
58737
|
+
return [...cands];
|
|
58738
|
+
return [second, top, ...cands.slice(2)];
|
|
58739
|
+
};
|
|
58740
|
+
isCompoundPlaneAsk = (question) => {
|
|
58741
|
+
const liveHalf = /\b(?:actually|really)\s+(?:running|firing|executing|live)\b|\bright\s+now\b|\bin\s+production\b/i;
|
|
58742
|
+
const vaultHalf = /\bwhat\s+(?:breaks?|depends|would\s+break|will\s+break)\b|\bwhat\s+does\b[^.?!]{0,60}\bdo\b|\bschema\b|\bmetadata\b|\bsafe\s+to\s+(?:disable|deactivate|delete|remove)\b|\btell\s+me\s+about\b/i;
|
|
58743
|
+
const conjoined = /\b(?:and|also|plus)\b/i;
|
|
58744
|
+
return liveHalf.test(question) && vaultHalf.test(question) && conjoined.test(question);
|
|
58745
|
+
};
|
|
55973
58746
|
risksDiverge = (a2, b2) => DESTRUCTIVE_TOOL.test(a2.tool) && INFORMATIONAL_IMPACT_TOOL.test(b2.tool) || DESTRUCTIVE_TOOL.test(b2.tool) && INFORMATIONAL_IMPACT_TOOL.test(a2.tool);
|
|
55974
58747
|
marginClarification = (cands, existingClarification) => {
|
|
55975
58748
|
if (existingClarification !== null)
|
|
@@ -55981,10 +58754,9 @@ var init_route_question = __esm({
|
|
|
55981
58754
|
return null;
|
|
55982
58755
|
if (top.fromRoute === true && second.fromRoute === true)
|
|
55983
58756
|
return null;
|
|
55984
|
-
|
|
55985
|
-
if (axis === null)
|
|
58757
|
+
if (!risksDiverge(top, second))
|
|
55986
58758
|
return null;
|
|
55987
|
-
const question =
|
|
58759
|
+
const question = `These two tools are equally likely but one is DESTRUCTIVE and one is read-only \u2014 \`${top.tool}\` vs \`${second.tool}\`. Do you want the read-only impact/usage readout, or the change/delete simulation? Which did you mean?`;
|
|
55988
58760
|
return {
|
|
55989
58761
|
required: true,
|
|
55990
58762
|
question,
|
|
@@ -56014,10 +58786,202 @@ var init_route_question = __esm({
|
|
|
56014
58786
|
};
|
|
56015
58787
|
buildFunnelCandidates = (route, question, routeToolArgs, mode) => {
|
|
56016
58788
|
const funnelLimit = mode !== void 0 ? 12 : 8;
|
|
56017
|
-
return rerankForMode(mergeRouteHintsIntoCandidates(route, semanticCandidates(question, funnelLimit), routeToolArgs, funnelLimit), mode);
|
|
58789
|
+
return resolvePlaneTie(rerankForMode(mergeRouteHintsIntoCandidates(route, semanticCandidates(question, funnelLimit), routeToolArgs, funnelLimit), mode), question);
|
|
56018
58790
|
};
|
|
56019
58791
|
routerMode = () => (process.env.SFI_ROUTER_MODE ?? "").trim().toLowerCase() === "offline" ? "offline" : "hybrid";
|
|
58792
|
+
REFUSAL_INTENT = {
|
|
58793
|
+
"write-imperative": "refused-write",
|
|
58794
|
+
"injection-exfiltration": "refused-injection",
|
|
58795
|
+
"identity-gap": "honest-gap-identity",
|
|
58796
|
+
"runtime-analytics": "honest-gap-runtime",
|
|
58797
|
+
"out-of-scope": "out-of-scope"
|
|
58798
|
+
};
|
|
58799
|
+
REFUSAL_GAP = {
|
|
58800
|
+
"write-imperative": {
|
|
58801
|
+
category: "write-request",
|
|
58802
|
+
note: "org mutation requested; refused (read-only product)"
|
|
58803
|
+
},
|
|
58804
|
+
"injection-exfiltration": {
|
|
58805
|
+
category: "injection",
|
|
58806
|
+
note: "prompt-injection/exfiltration shape refused"
|
|
58807
|
+
},
|
|
58808
|
+
"identity-gap": {
|
|
58809
|
+
category: "identity-gap",
|
|
58810
|
+
note: "first-person capability ask; no session-user identity \u2014 declined with a which-user clarify pointer"
|
|
58811
|
+
},
|
|
58812
|
+
"runtime-analytics": {
|
|
58813
|
+
category: "runtime-analytics",
|
|
58814
|
+
note: "runtime/ops telemetry the product does not model; honest gap disclosed"
|
|
58815
|
+
},
|
|
58816
|
+
"out-of-scope": {
|
|
58817
|
+
category: "out-of-scope",
|
|
58818
|
+
note: "outside the Salesforce-metadata boundary; refused"
|
|
58819
|
+
}
|
|
58820
|
+
};
|
|
58821
|
+
refusalResponse = async (ctx, input2, shape) => {
|
|
58822
|
+
const wantCands = routerMode() === "hybrid" && shape.kind !== "injection-exfiltration";
|
|
58823
|
+
const cands = wantCands ? rerankForMode(semanticCandidates(input2.question, input2.mode !== void 0 ? 12 : 8), input2.mode) : [];
|
|
58824
|
+
const disclosure = shape.kind === "runtime-analytics" && cands.length > 0 ? `${shape.disclosure} Nearest reads: ${cands.slice(0, 3).map((c2) => c2.tool).join(", ")}.` : shape.disclosure;
|
|
58825
|
+
const refusal = { ...shape, disclosure };
|
|
58826
|
+
const route = {
|
|
58827
|
+
question: input2.question,
|
|
58828
|
+
plane: "unknown",
|
|
58829
|
+
intent: REFUSAL_INTENT[shape.kind],
|
|
58830
|
+
tools: [],
|
|
58831
|
+
liveRequired: false,
|
|
58832
|
+
needsResolve: false,
|
|
58833
|
+
reason: disclosure,
|
|
58834
|
+
gap: REFUSAL_GAP[shape.kind],
|
|
58835
|
+
confidence: "high",
|
|
58836
|
+
// confident IN the refusal, not in any tool
|
|
58837
|
+
risk: "informational",
|
|
58838
|
+
alternatives: [],
|
|
58839
|
+
clarification: null,
|
|
58840
|
+
plan: [],
|
|
58841
|
+
refusal
|
|
58842
|
+
};
|
|
58843
|
+
const logged = input2.logGap === true ? await logGapIfAny(route, void 0, ctx.vaultRoot) : null;
|
|
58844
|
+
const guidance = wantCands ? `${disclosure} Do not execute any tool to satisfy the refused action.` : void 0;
|
|
58845
|
+
return ok({
|
|
58846
|
+
data: {
|
|
58847
|
+
route,
|
|
58848
|
+
executionBlocked: false,
|
|
58849
|
+
gapLogged: logged !== null,
|
|
58850
|
+
rendered: cands.length > 0 ? `${renderRouteMarkdown(route)}
|
|
58851
|
+
|
|
58852
|
+
**Candidate tools (transparency only \u2014 the request itself is refused/gapped; do not execute them to satisfy it):** ${cands.map((c2) => c2.tool).join(", ")}` : renderRouteMarkdown(route),
|
|
58853
|
+
trust: routeTrust(),
|
|
58854
|
+
...cands.length > 0 ? { toolCandidates: cands } : {},
|
|
58855
|
+
...guidance !== void 0 ? { guidance } : {}
|
|
58856
|
+
},
|
|
58857
|
+
vaultState: {
|
|
58858
|
+
sourceTreeHash: ctx.manifest.sourceTreeHash,
|
|
58859
|
+
refreshedAt: ctx.manifest.refreshedAt
|
|
58860
|
+
}
|
|
58861
|
+
});
|
|
58862
|
+
};
|
|
58863
|
+
CONTEXT_SAVE_ORDER_TOOLS = /* @__PURE__ */ new Set([
|
|
58864
|
+
"sfi.what_happens_on_save",
|
|
58865
|
+
"sfi.order_of_execution"
|
|
58866
|
+
]);
|
|
58867
|
+
CONTEXT_TOOL_ARG_KEYS = /* @__PURE__ */ new Map([
|
|
58868
|
+
["sfi.field_access_audit", "fieldId"],
|
|
58869
|
+
["sfi.safe_to_delete_field", "fieldId"],
|
|
58870
|
+
["sfi.field_360", "fieldId"],
|
|
58871
|
+
["sfi.field_lineage", "fieldId"],
|
|
58872
|
+
["sfi.explain_field", "fieldId"],
|
|
58873
|
+
["sfi.explain_flow", "flowId"]
|
|
58874
|
+
]);
|
|
58875
|
+
GENERIC_SCHEMA_INTENTS = /* @__PURE__ */ new Set([
|
|
58876
|
+
"schema",
|
|
58877
|
+
"metadata-count",
|
|
58878
|
+
"component-lookup"
|
|
58879
|
+
]);
|
|
58880
|
+
contextArgsFor = (route, winner) => {
|
|
58881
|
+
const primary = route.tools.find((tool) => !ROUTE_PREAMBLE_TOOLS.has(tool));
|
|
58882
|
+
if (primary === void 0)
|
|
58883
|
+
return null;
|
|
58884
|
+
if (SAVE_ORDER_INTENTS.has(route.intent) || CONTEXT_SAVE_ORDER_TOOLS.has(primary)) {
|
|
58885
|
+
const objectApiName = winner.type === "CustomObject" ? winner.apiName : winner.parentApiName;
|
|
58886
|
+
if (objectApiName === null || objectApiName === void 0)
|
|
58887
|
+
return null;
|
|
58888
|
+
return { ...route.suggestedArgs ?? {}, objectApiName };
|
|
58889
|
+
}
|
|
58890
|
+
const key = CONTEXT_TOOL_ARG_KEYS.get(primary) ?? "componentId";
|
|
58891
|
+
return { ...route.suggestedArgs ?? {}, [key]: winner.id };
|
|
58892
|
+
};
|
|
58893
|
+
buildContextContinuation = (question, inheritedTool, winner, anaphor, prependResolve) => {
|
|
58894
|
+
const entry = resolveCandidatePlane(inheritedTool);
|
|
58895
|
+
const tools = prependResolve ? ["sfi.resolve", inheritedTool] : [inheritedTool];
|
|
58896
|
+
const base = {
|
|
58897
|
+
question,
|
|
58898
|
+
plane: entry.plane,
|
|
58899
|
+
intent: "context-continuation",
|
|
58900
|
+
tools,
|
|
58901
|
+
liveRequired: entry.liveRequired,
|
|
58902
|
+
needsResolve: prependResolve,
|
|
58903
|
+
reason: `CONTEXT-DERIVED (advisory): no deterministic intent matched this follow-up on its own; '${anaphor}' was resolved from host-passed conversation context` + (winner !== null ? ` to ${winner.id}` : "") + `, and the previous turn's tool ${inheritedTool} was inherited. Verify the pick, resolve any named component first, and ground with sfi.synthesize_answer.`,
|
|
58904
|
+
gap: null,
|
|
58905
|
+
confidence: "low",
|
|
58906
|
+
risk: DESTRUCTIVE_TOOL.test(inheritedTool) ? "destructive" : "informational",
|
|
58907
|
+
alternatives: [],
|
|
58908
|
+
clarification: null,
|
|
58909
|
+
plan: [{
|
|
58910
|
+
stepId: "step-1",
|
|
58911
|
+
dependsOn: [],
|
|
58912
|
+
question,
|
|
58913
|
+
intent: "context-continuation",
|
|
58914
|
+
plane: entry.plane,
|
|
58915
|
+
tools
|
|
58916
|
+
}]
|
|
58917
|
+
};
|
|
58918
|
+
const boundArgs = winner !== null ? contextArgsFor(base, winner) : null;
|
|
58919
|
+
const withArgs = boundArgs !== null ? { ...base, suggestedArgs: boundArgs } : base;
|
|
58920
|
+
const guarded = applyComponentTypeGuard(withArgs, winner?.type ?? null);
|
|
58921
|
+
const primary = guarded.tools.find((tool) => !ROUTE_PREAMBLE_TOOLS.has(tool));
|
|
58922
|
+
if (primary === void 0)
|
|
58923
|
+
return null;
|
|
58924
|
+
if (winner !== null && !continuationToolCompatible(primary, winner.type)) {
|
|
58925
|
+
return null;
|
|
58926
|
+
}
|
|
58927
|
+
const passedGuardUnchanged = primary === inheritedTool;
|
|
58928
|
+
const finalEntry = resolveCandidatePlane(primary);
|
|
58929
|
+
return {
|
|
58930
|
+
...guarded,
|
|
58931
|
+
plane: finalEntry.plane,
|
|
58932
|
+
liveRequired: finalEntry.liveRequired,
|
|
58933
|
+
confidence: winner !== null && passedGuardUnchanged ? "medium" : "low",
|
|
58934
|
+
risk: DESTRUCTIVE_TOOL.test(primary) ? "destructive" : "informational",
|
|
58935
|
+
plan: guarded.plan.map((step3) => ({ ...step3, plane: finalEntry.plane }))
|
|
58936
|
+
};
|
|
58937
|
+
};
|
|
56020
58938
|
routeQuestionHandler = async (ctx, input2) => {
|
|
58939
|
+
const refusalShape = detectRefusalShape(input2.question);
|
|
58940
|
+
if (refusalShape !== null) {
|
|
58941
|
+
if (input2.clarificationResponse !== void 0) {
|
|
58942
|
+
return err({
|
|
58943
|
+
kind: "invalid-query",
|
|
58944
|
+
message: "clarificationResponse was supplied, but this question has no active clarification challenge. Route the question again without a response.",
|
|
58945
|
+
path: "clarificationResponse"
|
|
58946
|
+
});
|
|
58947
|
+
}
|
|
58948
|
+
return refusalResponse(ctx, input2, refusalShape);
|
|
58949
|
+
}
|
|
58950
|
+
const contextInput = input2.context;
|
|
58951
|
+
const validatedContext = contextInput !== void 0 ? validatePreviousContext(contextInput.previous, (tool) => getPlaneByTool().has(tool)) : null;
|
|
58952
|
+
if (validatedContext !== null && validatedContext.previous.clarification !== void 0 && validatedContext.previous.question !== void 0 && input2.clarificationResponse === void 0) {
|
|
58953
|
+
const selected = detectClarificationSelection(input2.question, validatedContext.previous.clarification.options);
|
|
58954
|
+
if (selected !== null) {
|
|
58955
|
+
const inner = selected.kind === "selected" ? await routeQuestionHandler(ctx, {
|
|
58956
|
+
question: validatedContext.previous.question,
|
|
58957
|
+
clarificationResponse: {
|
|
58958
|
+
clarificationId: validatedContext.previous.clarification.clarificationId,
|
|
58959
|
+
selection: selected.selection
|
|
58960
|
+
}
|
|
58961
|
+
}) : await routeQuestionHandler(ctx, {
|
|
58962
|
+
question: validatedContext.previous.question
|
|
58963
|
+
});
|
|
58964
|
+
if (!inner.ok)
|
|
58965
|
+
return inner;
|
|
58966
|
+
const contextApplied2 = {
|
|
58967
|
+
kind: "clarification-selection",
|
|
58968
|
+
anaphor: selected.anaphor,
|
|
58969
|
+
from: "previous.clarification",
|
|
58970
|
+
...selected.kind === "selected" ? { selection: selected.selection } : {},
|
|
58971
|
+
...validatedContext.ignored.length > 0 ? { ignored: validatedContext.ignored } : {}
|
|
58972
|
+
};
|
|
58973
|
+
return ok({
|
|
58974
|
+
...inner.value,
|
|
58975
|
+
data: {
|
|
58976
|
+
...inner.value.data,
|
|
58977
|
+
route: { ...inner.value.data.route, contextApplied: contextApplied2 },
|
|
58978
|
+
rendered: `${inner.value.data.rendered}
|
|
58979
|
+
|
|
58980
|
+
${renderContextApplied(contextApplied2)}`
|
|
58981
|
+
}
|
|
58982
|
+
});
|
|
58983
|
+
}
|
|
58984
|
+
}
|
|
56021
58985
|
let route = classifyQuestion(input2.question);
|
|
56022
58986
|
if (route.plane === "unknown" && route.intent === "unrouted") {
|
|
56023
58987
|
const upgraded = await tryResolveFallback(ctx, input2.question);
|
|
@@ -56073,15 +59037,56 @@ var init_route_question = __esm({
|
|
|
56073
59037
|
const saveOrderIntent = SAVE_ORDER_INTENTS.has(route.intent);
|
|
56074
59038
|
const suggestedObject = route.suggestedArgs?.["objectApiName"];
|
|
56075
59039
|
const entityExtractionSource = stripComparisonAside(input2.question, route.intent);
|
|
56076
|
-
|
|
56077
|
-
|
|
56078
|
-
const
|
|
59040
|
+
let entityQuery = !route.needsResolve ? null : saveOrderIntent ? typeof suggestedObject === "string" ? suggestedObject : null : extractEntityQuery(entityExtractionSource, route.intent);
|
|
59041
|
+
let entityTypes = entityQuery === null ? [] : saveOrderIntent ? ["CustomObject"] : inferEntityTypes(entityQuery, route.intent, input2.question);
|
|
59042
|
+
const pronounAnchor = validatedContext !== null ? detectPronounAnchor(input2.question) : null;
|
|
59043
|
+
let contextExactResolution = null;
|
|
59044
|
+
let contextSubstitution = null;
|
|
59045
|
+
let contextGhostComponentId = null;
|
|
59046
|
+
if (pronounAnchor !== null && validatedContext !== null && (entityQuery === null || isAnaphorOnly(entityQuery))) {
|
|
59047
|
+
const previous = validatedContext.previous;
|
|
59048
|
+
if (saveOrderIntent && previous.objectApiName !== void 0) {
|
|
59049
|
+
entityQuery = previous.objectApiName;
|
|
59050
|
+
entityTypes = ["CustomObject"];
|
|
59051
|
+
contextSubstitution = { anaphor: pronounAnchor, from: "previous.objectApiName" };
|
|
59052
|
+
} else if (!saveOrderIntent && previous.componentId !== void 0) {
|
|
59053
|
+
const carried = await getNodeById(ctx.graph, previous.componentId);
|
|
59054
|
+
if (carried.ok && carried.value !== null) {
|
|
59055
|
+
const node = carried.value;
|
|
59056
|
+
contextExactResolution = {
|
|
59057
|
+
disposition: "exact",
|
|
59058
|
+
candidates: [{
|
|
59059
|
+
id: node.id,
|
|
59060
|
+
type: node.type,
|
|
59061
|
+
apiName: node.apiName,
|
|
59062
|
+
label: node.label,
|
|
59063
|
+
parentApiName: node.parentId === null ? null : node.parentId.slice(node.parentId.indexOf(":") + 1),
|
|
59064
|
+
score: 1,
|
|
59065
|
+
base: 1,
|
|
59066
|
+
matchKind: "exact",
|
|
59067
|
+
nameCoverage: 1,
|
|
59068
|
+
evidence: `context: exact id carried from the previous turn (${node.id})`
|
|
59069
|
+
}],
|
|
59070
|
+
queryTokens: []
|
|
59071
|
+
};
|
|
59072
|
+
entityQuery = node.apiName;
|
|
59073
|
+
entityTypes = [node.type];
|
|
59074
|
+
contextSubstitution = { anaphor: pronounAnchor, from: "previous.componentId" };
|
|
59075
|
+
} else if (carried.ok) {
|
|
59076
|
+
contextGhostComponentId = previous.componentId;
|
|
59077
|
+
}
|
|
59078
|
+
}
|
|
59079
|
+
}
|
|
59080
|
+
const entityResolution = entityQuery !== null && contextExactResolution === null ? await resolveComponents(ctx.graph, entityQuery, {
|
|
56079
59081
|
limit: 5,
|
|
56080
59082
|
...entityTypes.length > 0 ? { types: entityTypes } : {}
|
|
56081
59083
|
}) : null;
|
|
56082
59084
|
const glossaryAwareEntityResolution = entityQuery !== null && entityResolution?.ok === true ? await applyGlossaryAliases(ctx, entityQuery, entityTypes, entityResolution.value) : null;
|
|
56083
|
-
|
|
56084
|
-
const entityClarificationRequired = entityQuery !== null && refinedEntityResolution !== null &&
|
|
59085
|
+
let refinedEntityResolution = contextExactResolution ?? (entityQuery !== null && glossaryAwareEntityResolution !== null ? resolveParentQualifier(input2.question, refineEntityResolution(entityQuery, glossaryAwareEntityResolution)) : null);
|
|
59086
|
+
const entityClarificationRequired = entityQuery !== null && refinedEntityResolution !== null && // component-type asks ("is X a flow or a trigger?") are EXEMPT: a
|
|
59087
|
+
// same-name cross-type collision is the ANSWER to that question, not an
|
|
59088
|
+
// obstacle — sfi.resolve enumerates the types and the host explains.
|
|
59089
|
+
route.intent !== "component-type" && entityAmbiguityRequiresClarification(entityQuery, refinedEntityResolution, input2.question);
|
|
56085
59090
|
let entityEvidence = refinedEntityResolution !== null && refinedEntityResolution.disposition !== "none" ? {
|
|
56086
59091
|
query: entityQuery,
|
|
56087
59092
|
typeHints: entityTypes,
|
|
@@ -56107,13 +59112,37 @@ var init_route_question = __esm({
|
|
|
56107
59112
|
clarification: {
|
|
56108
59113
|
required: true,
|
|
56109
59114
|
question: "Several components match the named entity. Which component did you mean?",
|
|
56110
|
-
|
|
59115
|
+
// P4 option hygiene: fuzzy grazes and far-below-top rivals are junk,
|
|
59116
|
+
// not options — offer only the plausible candidates.
|
|
59117
|
+
options: hygienicClarificationOptions(entityEvidence?.candidates ?? []).map((candidate) => candidate.componentId)
|
|
56111
59118
|
}
|
|
56112
59119
|
};
|
|
56113
59120
|
} else if (entityEvidence?.disposition === "ambiguous" && route.clarification === null) {
|
|
56114
59121
|
route = { ...route, confidence: "medium" };
|
|
59122
|
+
} else if (route.intent === "component-lookup" && route.clarification !== null && refinedEntityResolution?.disposition === "exact") {
|
|
59123
|
+
const winner = refinedEntityResolution.candidates[0];
|
|
59124
|
+
route = {
|
|
59125
|
+
...route,
|
|
59126
|
+
confidence: "high",
|
|
59127
|
+
clarification: null,
|
|
59128
|
+
suggestedArgs: { ...route.suggestedArgs ?? {}, componentId: winner.id }
|
|
59129
|
+
};
|
|
56115
59130
|
}
|
|
56116
59131
|
route = applyComponentTypeGuard(route, resolvedTypeForGuard(route, refinedEntityResolution));
|
|
59132
|
+
let contextEntityApplied = null;
|
|
59133
|
+
if (contextSubstitution !== null && route.intent !== "unrouted" && refinedEntityResolution?.disposition === "exact") {
|
|
59134
|
+
const winner = refinedEntityResolution.candidates[0];
|
|
59135
|
+
const bound = contextArgsFor(route, winner);
|
|
59136
|
+
if (bound !== null) {
|
|
59137
|
+
route = { ...route, suggestedArgs: bound };
|
|
59138
|
+
contextEntityApplied = {
|
|
59139
|
+
kind: "entity-substitution",
|
|
59140
|
+
anaphor: contextSubstitution.anaphor,
|
|
59141
|
+
substitutedComponentId: winner.id,
|
|
59142
|
+
from: contextSubstitution.from
|
|
59143
|
+
};
|
|
59144
|
+
}
|
|
59145
|
+
}
|
|
56117
59146
|
const marginRouteToolArgs = await buildRouteToolArgsMap(route, ctx);
|
|
56118
59147
|
if (routerMode() === "hybrid" && route.confidence !== "high") {
|
|
56119
59148
|
const gateCandidates = buildFunnelCandidates(route, input2.question, marginRouteToolArgs, input2.mode);
|
|
@@ -56122,6 +59151,23 @@ var init_route_question = __esm({
|
|
|
56122
59151
|
route = { ...route, confidence: "low", clarification: marginClar };
|
|
56123
59152
|
}
|
|
56124
59153
|
}
|
|
59154
|
+
if (routerMode() === "hybrid" && route.clarification === null && isCompoundPlaneAsk(input2.question)) {
|
|
59155
|
+
const compoundCands = buildFunnelCandidates(route, input2.question, marginRouteToolArgs, input2.mode);
|
|
59156
|
+
const vaultTop = compoundCands.find((candidate) => candidate.plane === "vault");
|
|
59157
|
+
const liveTop = compoundCands.find((candidate) => candidate.plane === "live" || candidate.plane === "hybrid") ?? (/\b(?:running|firing|executing|fired?|ran)\b/i.test(input2.question) ? { tool: "sfi.live_automation_fired" } : void 0);
|
|
59158
|
+
if (vaultTop !== void 0 && liveTop !== void 0) {
|
|
59159
|
+
route = {
|
|
59160
|
+
...route,
|
|
59161
|
+
confidence: "low",
|
|
59162
|
+
clarification: {
|
|
59163
|
+
required: true,
|
|
59164
|
+
question: `This asks for BOTH a vault-metadata answer AND a live-org verification \u2014 \`${vaultTop.tool}\` answers from the offline vault; \`${liveTop.tool}\` queries the org right now and needs the opt-in live plane. Which should lead? (Pick one; the other can run as a follow-up.)`,
|
|
59165
|
+
options: [vaultTop.tool, liveTop.tool]
|
|
59166
|
+
}
|
|
59167
|
+
};
|
|
59168
|
+
}
|
|
59169
|
+
}
|
|
59170
|
+
let premiseFlagged = false;
|
|
56125
59171
|
if (entityQuery !== null && refinedEntityResolution !== null && refinedEntityResolution.disposition === "none") {
|
|
56126
59172
|
const bareQuery = entityQuery.replace(/^(?:the|a|an)\s+/i, "").replace(/\s+(?:class(?:es)?|trigger(?:s)?|field(?:s)?|object(?:s)?|flow(?:s)?|component(?:s)?|layout(?:s)?|profile(?:s)?|report(?:s)?|dashboard(?:s)?|rule(?:s)?)$/i, "");
|
|
56127
59173
|
const bareRetry = bareQuery !== entityQuery && bareQuery.length > 0 ? await resolveComponents(ctx.graph, bareQuery, {
|
|
@@ -56129,7 +59175,43 @@ var init_route_question = __esm({
|
|
|
56129
59175
|
...entityTypes.length > 0 ? { types: entityTypes } : {}
|
|
56130
59176
|
}) : null;
|
|
56131
59177
|
const bareResolution = bareRetry?.ok === true && bareRetry.value.disposition !== "none" ? bareRetry.value : null;
|
|
56132
|
-
|
|
59178
|
+
let crossTypeResolution = null;
|
|
59179
|
+
if (bareResolution === null && entityTypes.length > 0) {
|
|
59180
|
+
const unscopedQuery = bareQuery.length > 0 ? bareQuery : entityQuery;
|
|
59181
|
+
const unscopedRetry = await resolveComponents(ctx.graph, unscopedQuery, { limit: 5 });
|
|
59182
|
+
if (unscopedRetry.ok && unscopedRetry.value.disposition !== "none" && (unscopedRetry.value.candidates[0]?.base ?? 0) >= 0.9) {
|
|
59183
|
+
crossTypeResolution = unscopedRetry.value;
|
|
59184
|
+
}
|
|
59185
|
+
}
|
|
59186
|
+
if (crossTypeResolution !== null) {
|
|
59187
|
+
const real = crossTypeResolution.candidates[0];
|
|
59188
|
+
const statedTypes = entityTypes.join("/");
|
|
59189
|
+
const typeWarning = `TYPE CHECK: no ${statedTypes} named '${entityQuery}' exists in the vault, but the name matches ${real.id} (a ${real.type}). The question's stated component type appears to be wrong \u2014 answer about the component that actually exists, and say so.`;
|
|
59190
|
+
route = {
|
|
59191
|
+
...route,
|
|
59192
|
+
confidence: "low",
|
|
59193
|
+
reason: `${route.reason} ${typeWarning}`
|
|
59194
|
+
};
|
|
59195
|
+
refinedEntityResolution = crossTypeResolution;
|
|
59196
|
+
entityEvidence = {
|
|
59197
|
+
query: entityQuery,
|
|
59198
|
+
typeHints: entityTypes,
|
|
59199
|
+
disposition: crossTypeResolution.disposition,
|
|
59200
|
+
clarificationRequired: false,
|
|
59201
|
+
warning: typeWarning,
|
|
59202
|
+
candidates: crossTypeResolution.candidates.slice(0, 5).map((candidate) => ({
|
|
59203
|
+
componentId: candidate.id,
|
|
59204
|
+
type: candidate.type,
|
|
59205
|
+
apiName: candidate.apiName,
|
|
59206
|
+
label: candidate.label,
|
|
59207
|
+
parentApiName: candidate.parentApiName,
|
|
59208
|
+
score: candidate.score,
|
|
59209
|
+
base: candidate.base,
|
|
59210
|
+
matchKind: candidate.matchKind,
|
|
59211
|
+
evidence: candidate.evidence
|
|
59212
|
+
}))
|
|
59213
|
+
};
|
|
59214
|
+
} else if (bareResolution !== null) {
|
|
56133
59215
|
entityEvidence = {
|
|
56134
59216
|
query: bareQuery,
|
|
56135
59217
|
typeHints: entityTypes,
|
|
@@ -56148,7 +59230,8 @@ var init_route_question = __esm({
|
|
|
56148
59230
|
evidence: candidate.evidence
|
|
56149
59231
|
}))
|
|
56150
59232
|
};
|
|
56151
|
-
} else {
|
|
59233
|
+
} else if (looksLikeComponentName(entityQuery)) {
|
|
59234
|
+
premiseFlagged = true;
|
|
56152
59235
|
const premiseWarning = `PREMISE CHECK: no component matching '${entityQuery}' exists in the vault \u2014 verify the name (a typo, or metadata newer than the last refresh; /sfi-refresh may help). The routed tool fails closed on an unknown component; do not present its error as an answer about a real component.`;
|
|
56153
59236
|
route = {
|
|
56154
59237
|
...route,
|
|
@@ -56163,8 +59246,18 @@ var init_route_question = __esm({
|
|
|
56163
59246
|
warning: premiseWarning,
|
|
56164
59247
|
candidates: []
|
|
56165
59248
|
};
|
|
59249
|
+
} else {
|
|
59250
|
+
entityEvidence = {
|
|
59251
|
+
query: entityQuery,
|
|
59252
|
+
typeHints: entityTypes,
|
|
59253
|
+
disposition: "none",
|
|
59254
|
+
clarificationRequired: false,
|
|
59255
|
+
warning: null,
|
|
59256
|
+
candidates: []
|
|
59257
|
+
};
|
|
56166
59258
|
}
|
|
56167
59259
|
} else if (entityQuery !== null && refinedEntityResolution !== null && refinedEntityResolution.disposition === "ambiguous" && (/__(?:c|mdt|e|x|b|kav)\b/i.test(entityQuery) || entityQuery.includes(".")) && !refinedEntityResolution.candidates.some((candidate) => candidate.apiName.toLowerCase() === entityQuery.toLowerCase() || candidate.id.toLowerCase().endsWith(`:${entityQuery.toLowerCase()}`))) {
|
|
59260
|
+
premiseFlagged = true;
|
|
56168
59261
|
const premiseWarning = `PREMISE CHECK: no component named '${entityQuery}' exists in the vault \u2014 the listed candidates are fuzzy lookalikes, not the component you named. Verify the name (a typo, or metadata newer than the last refresh; /sfi-refresh may help). The routed tool fails closed on an unknown component; do not present its error as an answer about a real component.`;
|
|
56169
59262
|
route = {
|
|
56170
59263
|
...route,
|
|
@@ -56175,6 +59268,214 @@ var init_route_question = __esm({
|
|
|
56175
59268
|
entityEvidence = { ...entityEvidence, warning: premiseWarning };
|
|
56176
59269
|
}
|
|
56177
59270
|
}
|
|
59271
|
+
if (contextGhostComponentId !== null) {
|
|
59272
|
+
premiseFlagged = true;
|
|
59273
|
+
const premiseWarning = `PREMISE CHECK: the component carried from the previous turn (${contextGhostComponentId}) no longer exists in the vault (deleted, renamed, or newer than the last refresh; /sfi-refresh may help). Do not answer as if it exists \u2014 re-ask with the component named explicitly.`;
|
|
59274
|
+
route = {
|
|
59275
|
+
...route,
|
|
59276
|
+
confidence: "low",
|
|
59277
|
+
reason: `${route.reason} ${premiseWarning}`
|
|
59278
|
+
};
|
|
59279
|
+
entityEvidence = {
|
|
59280
|
+
query: contextGhostComponentId,
|
|
59281
|
+
typeHints: [],
|
|
59282
|
+
disposition: "none",
|
|
59283
|
+
clarificationRequired: false,
|
|
59284
|
+
warning: premiseWarning,
|
|
59285
|
+
candidates: []
|
|
59286
|
+
};
|
|
59287
|
+
}
|
|
59288
|
+
if (!premiseFlagged && entityQuery === null && route.clarification === null && route.intent !== "empty" && route.intent !== "component-lookup") {
|
|
59289
|
+
const probeToken = extractExistenceProbeToken(input2.question);
|
|
59290
|
+
if (probeToken !== null) {
|
|
59291
|
+
const probe = await resolveComponents(ctx.graph, probeToken, { limit: 5 });
|
|
59292
|
+
if (probe.ok) {
|
|
59293
|
+
const lowered = probeToken.toLowerCase();
|
|
59294
|
+
const loose = normLiteral(probeToken);
|
|
59295
|
+
const literalHit = probe.value.candidates.some((candidate) => candidate.apiName.toLowerCase() === lowered || candidate.id.toLowerCase().endsWith(`:${lowered}`) || normLiteral(candidate.apiName) === loose);
|
|
59296
|
+
if (probe.value.disposition === "none" || !literalHit) {
|
|
59297
|
+
premiseFlagged = true;
|
|
59298
|
+
const premiseWarning = `PREMISE CHECK: no component named '${probeToken}' exists in the vault \u2014 any listed candidates are fuzzy lookalikes, not the component you named. Verify the name (a typo, or metadata newer than the last refresh; /sfi-refresh may help). The routed tool fails closed on an unknown component; do not present its error as an answer about a real component.`;
|
|
59299
|
+
route = {
|
|
59300
|
+
...route,
|
|
59301
|
+
confidence: "low",
|
|
59302
|
+
reason: `${route.reason} ${premiseWarning}`
|
|
59303
|
+
};
|
|
59304
|
+
entityEvidence = {
|
|
59305
|
+
query: probeToken,
|
|
59306
|
+
typeHints: [],
|
|
59307
|
+
disposition: "none",
|
|
59308
|
+
clarificationRequired: false,
|
|
59309
|
+
warning: premiseWarning,
|
|
59310
|
+
candidates: probe.value.candidates.slice(0, 5).map((candidate) => ({
|
|
59311
|
+
componentId: candidate.id,
|
|
59312
|
+
type: candidate.type,
|
|
59313
|
+
apiName: candidate.apiName,
|
|
59314
|
+
label: candidate.label,
|
|
59315
|
+
parentApiName: candidate.parentApiName,
|
|
59316
|
+
score: candidate.score,
|
|
59317
|
+
base: candidate.base,
|
|
59318
|
+
matchKind: candidate.matchKind,
|
|
59319
|
+
evidence: candidate.evidence
|
|
59320
|
+
}))
|
|
59321
|
+
};
|
|
59322
|
+
}
|
|
59323
|
+
}
|
|
59324
|
+
}
|
|
59325
|
+
}
|
|
59326
|
+
let contextRouteApplied = null;
|
|
59327
|
+
if (validatedContext !== null && route.clarification === null && !premiseFlagged) {
|
|
59328
|
+
const previous = validatedContext.previous;
|
|
59329
|
+
const stillUnrouted = route.intent === "unrouted" && route.plane === "unknown";
|
|
59330
|
+
const gapFollowUpFamily = stillUnrouted ? detectGapShapedFollowUp(input2.question) : null;
|
|
59331
|
+
if (gapFollowUpFamily !== null) {
|
|
59332
|
+
const gapDescription = {
|
|
59333
|
+
judgment: "a normative judgment (should/normal/risky) \u2014 sf-intelligence reads org metadata and has no opinion to offer",
|
|
59334
|
+
delivery: "file export or message delivery, which the product does not perform",
|
|
59335
|
+
"tool-self-capability": "the product's own capability boundary \u2014 sfi.capabilities answers what is and is not built",
|
|
59336
|
+
"deployment-status": "deployment/pending-change status, runtime telemetry the vault does not model"
|
|
59337
|
+
};
|
|
59338
|
+
const carried = previous.componentId !== void 0 ? ` The component carried from the previous turn (${previous.componentId}) was noted but the previous tool was NOT inherited \u2014 inheriting it would execute a read that cannot answer this ask.` : "";
|
|
59339
|
+
route = {
|
|
59340
|
+
question: input2.question,
|
|
59341
|
+
plane: "unknown",
|
|
59342
|
+
intent: "context-gap-followup",
|
|
59343
|
+
tools: [],
|
|
59344
|
+
liveRequired: false,
|
|
59345
|
+
needsResolve: false,
|
|
59346
|
+
reason: `HONEST GAP (context follow-up): this follow-up asks for ${gapDescription[gapFollowUpFamily] ?? gapFollowUpFamily}.${carried}`,
|
|
59347
|
+
gap: {
|
|
59348
|
+
category: "context-gap-followup",
|
|
59349
|
+
note: `gap-shaped follow-up (${gapFollowUpFamily}); context continuation suppressed rather than inheriting the previous tool`
|
|
59350
|
+
},
|
|
59351
|
+
confidence: "high",
|
|
59352
|
+
risk: "informational",
|
|
59353
|
+
alternatives: [],
|
|
59354
|
+
clarification: null,
|
|
59355
|
+
plan: []
|
|
59356
|
+
};
|
|
59357
|
+
}
|
|
59358
|
+
const reparamAnchor = gapFollowUpFamily !== null ? null : detectReparamAnchor(input2.question);
|
|
59359
|
+
const ownRouteWeak = stillUnrouted || GENERIC_SCHEMA_INTENTS.has(route.intent) && route.confidence !== "high";
|
|
59360
|
+
if (reparamAnchor !== null && ownRouteWeak && previous.tool !== void 0) {
|
|
59361
|
+
let target = contextSubstitution === null && refinedEntityResolution?.disposition === "exact" ? refinedEntityResolution.candidates[0] : null;
|
|
59362
|
+
let newEntityBlocked = false;
|
|
59363
|
+
if (target === null) {
|
|
59364
|
+
const phrase = extractReparamTarget(input2.question);
|
|
59365
|
+
if (phrase !== null) {
|
|
59366
|
+
const resolvedTarget = await resolveComponents(ctx.graph, phrase, { limit: 5 });
|
|
59367
|
+
if (resolvedTarget.ok) {
|
|
59368
|
+
const refinedTarget = refineEntityResolution(phrase, resolvedTarget.value);
|
|
59369
|
+
if (refinedTarget.disposition === "exact") {
|
|
59370
|
+
target = refinedTarget.candidates[0];
|
|
59371
|
+
} else if (refinedTarget.disposition === "ambiguous" && entityAmbiguityRequiresClarification(phrase, refinedTarget)) {
|
|
59372
|
+
route = {
|
|
59373
|
+
...route,
|
|
59374
|
+
confidence: "low",
|
|
59375
|
+
clarification: {
|
|
59376
|
+
required: true,
|
|
59377
|
+
question: "Several components match the named entity. Which component did you mean?",
|
|
59378
|
+
options: hygienicClarificationOptions(refinedTarget.candidates.slice(0, 5)).map((candidate) => candidate.id)
|
|
59379
|
+
}
|
|
59380
|
+
};
|
|
59381
|
+
newEntityBlocked = true;
|
|
59382
|
+
}
|
|
59383
|
+
}
|
|
59384
|
+
}
|
|
59385
|
+
}
|
|
59386
|
+
if (!newEntityBlocked && target !== null) {
|
|
59387
|
+
const continuation = buildContextContinuation(input2.question, previous.tool, target, reparamAnchor, false);
|
|
59388
|
+
if (continuation !== null) {
|
|
59389
|
+
route = continuation;
|
|
59390
|
+
contextRouteApplied = {
|
|
59391
|
+
kind: "reparameterization",
|
|
59392
|
+
anaphor: reparamAnchor,
|
|
59393
|
+
substitutedComponentId: target.id,
|
|
59394
|
+
inheritedTool: previous.tool
|
|
59395
|
+
};
|
|
59396
|
+
}
|
|
59397
|
+
}
|
|
59398
|
+
}
|
|
59399
|
+
if (contextRouteApplied === null && route.clarification === null && stillUnrouted && route.intent === "unrouted" && previous.tool !== void 0) {
|
|
59400
|
+
const anchor = pronounAnchor ?? reparamAnchor;
|
|
59401
|
+
const substitutedExact = contextSubstitution !== null && refinedEntityResolution?.disposition === "exact";
|
|
59402
|
+
if (anchor !== null && (substitutedExact || previous.componentId === void 0)) {
|
|
59403
|
+
const winner = substitutedExact ? refinedEntityResolution.candidates[0] : null;
|
|
59404
|
+
const newEntityAppeared = contextSubstitution === null && entityQuery !== null && !isAnaphorOnly(entityQuery);
|
|
59405
|
+
const continuation = buildContextContinuation(input2.question, previous.tool, winner, anchor, newEntityAppeared);
|
|
59406
|
+
if (continuation !== null) {
|
|
59407
|
+
route = continuation;
|
|
59408
|
+
contextRouteApplied = {
|
|
59409
|
+
kind: "continuation",
|
|
59410
|
+
anaphor: anchor,
|
|
59411
|
+
...winner !== null ? {
|
|
59412
|
+
substitutedComponentId: winner.id,
|
|
59413
|
+
from: contextSubstitution.from
|
|
59414
|
+
} : {},
|
|
59415
|
+
inheritedTool: previous.tool
|
|
59416
|
+
};
|
|
59417
|
+
}
|
|
59418
|
+
}
|
|
59419
|
+
}
|
|
59420
|
+
}
|
|
59421
|
+
let advisoryCandidates = null;
|
|
59422
|
+
const questionHead = input2.question.trim().split(/\s+/).slice(0, 5).join(" ");
|
|
59423
|
+
const anaphorOnlyFragment = validatedContext === null && detectPronounAnchor(questionHead) !== null && (entityQuery === null || isAnaphorOnly(entityQuery));
|
|
59424
|
+
if (route.intent === "unrouted" && route.plane === "unknown" && route.clarification === null && !premiseFlagged && !anaphorOnlyFragment) {
|
|
59425
|
+
const cands = buildFunnelCandidates(route, input2.question, marginRouteToolArgs, input2.mode);
|
|
59426
|
+
advisoryCandidates = cands;
|
|
59427
|
+
const resolvedExact = refinedEntityResolution?.disposition === "exact" ? refinedEntityResolution.candidates[0] : void 0;
|
|
59428
|
+
const usable = resolvedExact === void 0 ? cands : cands.filter((candidate) => {
|
|
59429
|
+
const compatible = TOOL_COMPATIBLE_TYPES.get(candidate.tool);
|
|
59430
|
+
return compatible === void 0 || compatible.has(resolvedExact.type);
|
|
59431
|
+
});
|
|
59432
|
+
const top = usable[0];
|
|
59433
|
+
if (top !== void 0 && top.score >= FUNNEL_PRIMARY_MIN_SCORE && top.fromRoute !== true && top.score > 0.25) {
|
|
59434
|
+
const tools = usable.slice(0, 3).map((candidate) => candidate.tool);
|
|
59435
|
+
let advisoryArgs = null;
|
|
59436
|
+
if (resolvedExact !== void 0) {
|
|
59437
|
+
const compatible = TOOL_COMPATIBLE_TYPES.get(top.tool);
|
|
59438
|
+
if (compatible !== void 0 && compatible.has(resolvedExact.type)) {
|
|
59439
|
+
advisoryArgs = top.tool === "sfi.field_access_audit" ? { fieldId: resolvedExact.id } : { componentId: resolvedExact.id };
|
|
59440
|
+
} else if (compatible === void 0 && /get_impact|get_edges|component/.test(top.tool)) {
|
|
59441
|
+
advisoryArgs = { componentId: resolvedExact.id };
|
|
59442
|
+
}
|
|
59443
|
+
}
|
|
59444
|
+
route = {
|
|
59445
|
+
question: input2.question,
|
|
59446
|
+
plane: top.plane,
|
|
59447
|
+
intent: "funnel-advisory",
|
|
59448
|
+
tools,
|
|
59449
|
+
liveRequired: top.liveRequired,
|
|
59450
|
+
needsResolve: entityQuery !== null,
|
|
59451
|
+
reason: "No deterministic intent matched. This route is FUNNEL-DERIVED (advisory): the semantic funnel ranked these tools by meaning alone. Confidence is low by construction \u2014 verify the pick, resolve any named component first, and ground with sfi.synthesize_answer.",
|
|
59452
|
+
gap: null,
|
|
59453
|
+
confidence: "low",
|
|
59454
|
+
risk: DESTRUCTIVE_TOOL.test(top.tool) ? "destructive" : "informational",
|
|
59455
|
+
alternatives: [],
|
|
59456
|
+
clarification: null,
|
|
59457
|
+
plan: [{
|
|
59458
|
+
stepId: "step-1",
|
|
59459
|
+
dependsOn: [],
|
|
59460
|
+
question: input2.question,
|
|
59461
|
+
intent: "funnel-advisory",
|
|
59462
|
+
plane: top.plane,
|
|
59463
|
+
tools
|
|
59464
|
+
}],
|
|
59465
|
+
...advisoryArgs !== null ? { suggestedArgs: advisoryArgs } : {}
|
|
59466
|
+
};
|
|
59467
|
+
}
|
|
59468
|
+
}
|
|
59469
|
+
const contextApplied = contextRouteApplied ?? contextEntityApplied;
|
|
59470
|
+
if (contextApplied !== null) {
|
|
59471
|
+
route = {
|
|
59472
|
+
...route,
|
|
59473
|
+
contextApplied: {
|
|
59474
|
+
...contextApplied,
|
|
59475
|
+
...validatedContext !== null && validatedContext.ignored.length > 0 ? { ignored: validatedContext.ignored } : {}
|
|
59476
|
+
}
|
|
59477
|
+
};
|
|
59478
|
+
}
|
|
56178
59479
|
const clarificationId = clarificationIdFor(ctx.manifest.sourceTreeHash, route);
|
|
56179
59480
|
if (clarificationId !== null && route.clarification !== null) {
|
|
56180
59481
|
route = {
|
|
@@ -56281,7 +59582,7 @@ var init_route_question = __esm({
|
|
|
56281
59582
|
const routeToolArgs = await buildRouteToolArgsMap(route, ctx);
|
|
56282
59583
|
const invoke = toolProfile() === "core" && !executionBlocked ? invokeFromArgsMap(route, routeToolArgs) : void 0;
|
|
56283
59584
|
const wantCandidates = routerMode() === "hybrid";
|
|
56284
|
-
const toolCandidates = wantCandidates ? buildFunnelCandidates(route, input2.question, routeToolArgs, input2.mode) : [];
|
|
59585
|
+
const toolCandidates = wantCandidates ? advisoryCandidates ?? buildFunnelCandidates(route, input2.question, routeToolArgs, input2.mode) : [];
|
|
56285
59586
|
const guidance = toolCandidates.length > 0 ? guidanceForMode(input2.mode, toolCandidates) : void 0;
|
|
56286
59587
|
return ok({
|
|
56287
59588
|
data: {
|
|
@@ -65497,6 +68798,40 @@ var init_tools = __esm({
|
|
|
65497
68798
|
clarificationId: { type: "string", minLength: 1 },
|
|
65498
68799
|
selection: { type: "string", minLength: 1 }
|
|
65499
68800
|
}
|
|
68801
|
+
},
|
|
68802
|
+
mode: { type: "string", enum: ["ask", "plan", "assessment"] },
|
|
68803
|
+
context: {
|
|
68804
|
+
type: "object",
|
|
68805
|
+
required: ["previous"],
|
|
68806
|
+
additionalProperties: false,
|
|
68807
|
+
properties: {
|
|
68808
|
+
previous: {
|
|
68809
|
+
type: "object",
|
|
68810
|
+
additionalProperties: false,
|
|
68811
|
+
properties: {
|
|
68812
|
+
componentId: { type: "string", minLength: 1, maxLength: 256 },
|
|
68813
|
+
objectApiName: { type: "string", minLength: 1, maxLength: 256 },
|
|
68814
|
+
tool: { type: "string", pattern: "^sfi\\.[a-z0-9_]+$" },
|
|
68815
|
+
intent: { type: "string", minLength: 1, maxLength: 64 },
|
|
68816
|
+
plane: { type: "string", enum: ["vault", "live", "hybrid"] },
|
|
68817
|
+
question: { type: "string", minLength: 1, maxLength: 2e3 },
|
|
68818
|
+
clarification: {
|
|
68819
|
+
type: "object",
|
|
68820
|
+
required: ["clarificationId", "options"],
|
|
68821
|
+
additionalProperties: false,
|
|
68822
|
+
properties: {
|
|
68823
|
+
clarificationId: { type: "string", minLength: 1, maxLength: 64 },
|
|
68824
|
+
options: {
|
|
68825
|
+
type: "array",
|
|
68826
|
+
items: { type: "string", minLength: 1, maxLength: 256 },
|
|
68827
|
+
minItems: 1,
|
|
68828
|
+
maxItems: 10
|
|
68829
|
+
}
|
|
68830
|
+
}
|
|
68831
|
+
}
|
|
68832
|
+
}
|
|
68833
|
+
}
|
|
68834
|
+
}
|
|
65500
68835
|
}
|
|
65501
68836
|
}
|
|
65502
68837
|
});
|
|
@@ -66886,7 +70221,7 @@ var init_tools = __esm({
|
|
|
66886
70221
|
},
|
|
66887
70222
|
{
|
|
66888
70223
|
name: "sfi.resolve",
|
|
66889
|
-
description: "Typo-tolerant resolver: messy/misspelled text -> ranked candidate components with a disposition (exact|ambiguous|none) + per-candidate evidence. Call FIRST when the user names a component informally; tolerates typos, filler, and the org's own misspellings that search_components cannot. Leading/trailing schema nouns are TYPE hints, not name content: 'SSN field' scores exactly like bare 'SSN' (the noun is stripped from fuzzy matching and instead prefers candidates of the hinted type among equally-confident matches \u2014 it never floats a weak fuzzy match of that type over an exact-name match of another). A dotted 'Object.Field' query is a definitive parent-scoped hit. CONFIRMED glossary annotations act as curated synonyms (candidates marked `glossary-alias`) \u2014 an alias never shadows an exact api-name match, and a synonym shared by two components yields `ambiguous` + clarification. Heuristic; never silently picks.",
|
|
70224
|
+
description: "Typo-tolerant resolver: messy/misspelled text -> ranked candidate components with a disposition (exact|ambiguous|none) + per-candidate evidence. Call FIRST when the user names a component informally; tolerates typos, filler, and the org's own misspellings that search_components cannot. Also the answer to informal lookup asks \u2014 'find / look up / locate the X', 'do we have a field for tracking Y?', 'I think there's a flow called something like Z' \u2014 whenever the user only half-remembers a name or describes the thing instead of naming it. Leading/trailing schema nouns are TYPE hints, not name content: 'SSN field' scores exactly like bare 'SSN' (the noun is stripped from fuzzy matching and instead prefers candidates of the hinted type among equally-confident matches \u2014 it never floats a weak fuzzy match of that type over an exact-name match of another). A dotted 'Object.Field' query is a definitive parent-scoped hit. CONFIRMED glossary annotations act as curated synonyms (candidates marked `glossary-alias`) \u2014 an alias never shadows an exact api-name match, and a synonym shared by two components yields `ambiguous` + clarification. Heuristic; never silently picks.",
|
|
66890
70225
|
inputSchema: RESOLVE_INPUT_SCHEMA
|
|
66891
70226
|
},
|
|
66892
70227
|
{
|
|
@@ -66916,7 +70251,7 @@ var init_tools = __esm({
|
|
|
66916
70251
|
},
|
|
66917
70252
|
{
|
|
66918
70253
|
name: "sfi.route_question",
|
|
66919
|
-
description: "Front-door router: for a plain-language question, surface a meaning-ranked shortlist of the sfi.* tools that can answer it \u2014 your host LLM picks which to run \u2014 plus the plane it belongs to (vault | live | hybrid | unknown), so the user never types a tool name. Read-only; it advises, it does not answer. Compound questions carry step ids and `dependsOn` edges: independent steps may run in parallel; a `then`-linked step waits for its prerequisite. On GENUINE ambiguity it fails closed with `executionBlocked`, a clarification id, and offered options; resume deterministically by calling again with the exact same question plus `clarificationResponse: { clarificationId, selection }`. Stale ids and invented selections are rejected. Bare schema nouns in the question (trigger/flow/field/object/profile/permission set/record type) are INTENT vocabulary, never named-entity lookups \u2014 'the Contact trigger' scopes a save-order question to the Contact OBJECT, it does not shop 'trigger' to the resolver; an object-qualified field ('Status__c on Case') is resolved parent-scoped; and an entity the resolver reports `exact` never raises the components-match menu \u2014 that menu is reserved for two genuinely competing components. The resolved TYPE also gates the routed tools: when the named entity resolves to a FLOW, tools that hard-error on a Flow id (call_graph / method_reachability / explain_apex_method, and the object/field access audits) are SWAPPED for the Flow-appropriate ones (who_can_run for access asks, explain_flow \u2014 which narrates the Apex the Flow invokes \u2014 plus get_impact) instead of routing into a guaranteed error. Qualifier words never outrank the HEAD question: 'bulk'/'load' on a save-order or test-coverage ask, 'seats'/'license' on a permission-set-assignment ask, and 'integration'/vendor words on a field-write / profile-security / what-if ask all stay on the head intent (never forced onto governor_limit_risks / live_license_usage / integration_map); field_lineage is reserved for explicit lineage/provenance questions ('where does this data come from'), never a field-adjacent default. FALSE PREMISE: when the question names a component the resolver cannot find \u2014 disposition `none`, or a literal API reference (dotted / __c-suffixed) that none of the fuzzy candidates actually match \u2014 the route is STILL returned (the routed tool fails closed on the unknown id) but its confidence is downgraded and `entityEvidence.warning` carries a premise disclosure (no component matching '<name>' exists in the vault \u2014 verify the name), so a nonexistent component is never answered as if it were real. Honesty routes: asks the surface genuinely cannot answer route to an explicit gap, never a lookalike tool \u2014 e.g. 'which USERS hold permission set X' (PermissionSetAssignment is not modeled; effective_permissions describes GRANTS, not holders); profile user-roster asks route live (live_group_count over User by ProfileId) with a partial-answer disclosure; and cross-vault compare routes carry a disclosure that a SECOND registered vault is required, so a single-vault install is never routed confident-clean into vault-not-found. 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 instead of fabricating (set `logGap: true` to also append the gap to the local backlog; off by default, privacy-first per CR-16); under `SFI_TOOL_PROFILE=core` the response also carries `invoke`: the routed tools as EXECUTABLE calls (core tools direct, everything else as the byte-identical `sfi.run_analysis` gateway envelope, suggestedArgs threaded) (a short phrase that merely NAMES a real vault component, with no question, is instead routed to sfi.resolve rather than 'unknown'). In the default hybrid mode the meaning-ranked `toolCandidates` are PRIMARY: every routable question carries the shortlist (offline TF-IDF over the capability map, no neural model, no network) plus a `guidance` line stating the loop YOU own \u2014 read the candidates \u2192 resolve any named component \u2192 pick/sequence the tool(s) \u2192 run them \u2192 ground via sfi.synthesize_answer. YOU decide which to run; the deterministic `route` rides along only as a non-authoritative HINT (suggested tool order + any resolved entity / suggestedArgs). Set `SFI_ROUTER_MODE=offline` for a deterministic, no-LLM route (Design A) where the route is authoritative and candidates are omitted \u2014 for CI / air-gapped hosts. An optional `mode` ('ask' | 'plan' | 'assessment') tailors the guidance and reranks the candidates toward that mode's family \u2014 'plan' favors the what_if_* / impact tools (an ordered change plan), 'assessment' favors the *_risk_report / readiness / coverage tools (a full evaluation), 'ask' is a quick grounded answer. Call this first on a vague/broad question to decide which tool(s) to run.",
|
|
70254
|
+
description: "Front-door router: for a plain-language question, surface a meaning-ranked shortlist of the sfi.* tools that can answer it \u2014 your host LLM picks which to run \u2014 plus the plane it belongs to (vault | live | hybrid | unknown), so the user never types a tool name. Read-only; it advises, it does not answer. Compound questions carry step ids and `dependsOn` edges: independent steps may run in parallel; a `then`-linked step waits for its prerequisite. On GENUINE ambiguity it fails closed with `executionBlocked`, a clarification id, and offered options; resume deterministically by calling again with the exact same question plus `clarificationResponse: { clarificationId, selection }`. Stale ids and invented selections are rejected. CLARIFICATIONS ARE A LAST RESORT (router-v2 P4): a qualifier already in the question pre-answers the menu \u2014 an object word next to a same-named field ('\u2026saving a CASE without a Resolution_Code__c'), a type word after the name ('the X OBJECT/FLOW'), an underscored/dotted literal api name, or a component mention on a who-last-modified ask all AUTO-RESOLVE instead of blocking; a CustomTab twin of a CustomObject never counts as a rival; complementary readings (who-can-access grantors vs CRUD matrix, impact readout vs change/delete simulation on the same ask) STACK their tools in one route instead of asking which-first; a vault-vs-live candidate near-tie is decided by the question's own runtime-data language (live leads WITH the consent disclosure, vault otherwise) rather than blocking \u2014 only the DESTRUCTIVE-vs-read-only tool tie still stops execution; and offered options are HYGIENIC \u2014 fuzzy acronym-graze rivals (SSN vs {A,B,M}SN_*) and far-below-top junk never appear as options. Bare schema nouns in the question (trigger/flow/field/object/profile/permission set/record type) are INTENT vocabulary, never named-entity lookups \u2014 'the Contact trigger' scopes a save-order question to the Contact OBJECT, it does not shop 'trigger' to the resolver; an object-qualified field ('Status__c on Case') is resolved parent-scoped; and an entity the resolver reports `exact` never raises the components-match menu \u2014 that menu is reserved for two genuinely competing components. The resolved TYPE also gates the routed tools: when the named entity resolves to a FLOW, tools that hard-error on a Flow id (call_graph / method_reachability / explain_apex_method, and the object/field access audits) are SWAPPED for the Flow-appropriate ones (who_can_run for access asks, explain_flow \u2014 which narrates the Apex the Flow invokes \u2014 plus get_impact) instead of routing into a guaranteed error. Qualifier words never outrank the HEAD question: 'bulk'/'load' on a save-order or test-coverage ask, 'seats'/'license' on a permission-set-assignment ask, and 'integration'/vendor words on a field-write / profile-security / what-if ask all stay on the head intent (never forced onto governor_limit_risks / live_license_usage / integration_map); field_lineage is reserved for explicit lineage/provenance questions ('where does this data come from'), never a field-adjacent default. FALSE PREMISE: when the question names a component the resolver cannot find \u2014 disposition `none`, or a literal API reference (dotted / __c-suffixed) that none of the fuzzy candidates actually match \u2014 the route is STILL returned (the routed tool fails closed on the unknown id) but its confidence is downgraded and `entityEvidence.warning` carries a premise disclosure (no component matching '<name>' exists in the vault \u2014 verify the name), so a nonexistent component is never answered as if it were real. Honesty routes: asks the surface genuinely cannot answer route to an explicit gap, never a lookalike tool \u2014 e.g. 'which USERS hold permission set X' (PermissionSetAssignment is not modeled; effective_permissions describes GRANTS, not holders); profile user-roster asks route live (live_group_count over User by ProfileId) with a partial-answer disclosure; and cross-vault compare routes carry a disclosure that a SECOND registered vault is required, so a single-vault install is never routed confident-clean into vault-not-found. 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 instead of fabricating (set `logGap: true` to also append the gap to the local backlog; off by default, privacy-first per CR-16); under `SFI_TOOL_PROFILE=core` the response also carries `invoke`: the routed tools as EXECUTABLE calls (core tools direct, everything else as the byte-identical `sfi.run_analysis` gateway envelope, suggestedArgs threaded) (a short phrase that merely NAMES a real vault component, with no question, is instead routed to sfi.resolve rather than 'unknown'). In the default hybrid mode the meaning-ranked `toolCandidates` are PRIMARY: every routable question carries the shortlist (offline TF-IDF over the capability map, no neural model, no network) plus a `guidance` line stating the loop YOU own \u2014 read the candidates \u2192 resolve any named component \u2192 pick/sequence the tool(s) \u2192 run them \u2192 ground via sfi.synthesize_answer. YOU decide which to run; the deterministic `route` rides along only as a non-authoritative HINT (suggested tool order + any resolved entity / suggestedArgs). Set `SFI_ROUTER_MODE=offline` for a deterministic, no-LLM route (Design A) where the route is authoritative and candidates are omitted \u2014 for CI / air-gapped hosts. An optional `mode` ('ask' | 'plan' | 'assessment') tailors the guidance and reranks the candidates toward that mode's family \u2014 'plan' favors the what_if_* / impact tools (an ordered change plan), 'assessment' favors the *_risk_report / readiness / coverage tools (a full evaluation), 'ask' is a quick grounded answer. REFUSAL SHAPES (score-independent, evaluated BEFORE any intent, in both router modes): a write/mutation imperative aimed at the agent ('delete the X field for me', 'go ahead and merge\u2026') returns intent `refused-write` with EMPTY tools, a 'REFUSED (read-only boundary)' disclosure, and a read-side alternative to offer instead (safe_to_delete_field / what_if_deactivate_flow / what_if_disable_trigger / what_if_change_field_type / what_if_merge_profiles / get_impact by verb family); prompt-injection or record-value exfiltration ('ignore your previous instructions\u2026', 'dump all SSN values') returns `refused-injection` with toolCandidates AND guidance suppressed entirely; runtime/ops telemetry no tool models (login history, adoption metrics, endpoint errors 'this week', running-user context) returns `honest-gap-runtime` with an 'HONEST GAP' disclosure naming the nearest reads; and non-Salesforce asks (SharePoint/Jira subjects, org-policy authorship, 'email me\u2026' delivery, write-me-code) return `out-of-scope`. Every refusal is NON-EXECUTABLE by shape \u2014 `tools: []`, the structured `route.refusal` field carries { kind, disclosure, readOnlyAlternative? }, `executionBlocked` stays false \u2014 and legitimate permission/hypothetical READS ('am I allowed to edit\u2026', 'who can delete\u2026', 'is it safe to\u2026', 'what would happen if\u2026') are explicit excluders that route normally. FUNNEL-PRIMARY (advisory): when NO deterministic intent matches and nothing else stopped the route (no clarification, and a clean premise \u2014 a question naming a component the resolver cannot find gets the premise disclosure, never an advisory route), a pure-semantic top candidate scoring \u2265 0.26 upgrades the dead 'unrouted' to intent `funnel-advisory`: the top-3 funnel tools, confidence LOW by construction, reason flagged FUNNEL-DERIVED \u2014 treat it as the funnel's advisory pick to verify (resolve the named component, then ground), never a command. Candidate rows also carry `cosine`, the raw pre-fusion semantic score (0 for rows inserted purely from the regex route hint), so a host can tell real semantic support from regex assertion. CONVERSATION CONTEXT (stateless, host-passed \u2014 router-v2 P5): the product stores NO conversation memory; the HOST may pass optional `context.previous` per call ({ componentId, objectApiName, tool, intent, plane, question, clarification }) describing what the PRIOR turn was about, and nothing is stored server-side. With it, terse follow-ups resolve: (1) a pronoun/ellipsis follow-up whose own extraction finds no entity ('does it fire on delete too?') substitutes `previous.componentId` as the entity \u2014 an EXACT-id lookup, never fuzzy \u2014 and, when still unrouted, inherits `previous.tool` as an advisory `context-continuation` route (confidence capped at `medium`, never high; plane/liveRequired always from the live tool registry, never `previous.plane`; the resolved TYPE still gates the inherited tool \u2014 a type-incompatible inheritance is swapped or dropped to funnel-primary, never an executable tool bound to an id that guarantees a hard error); (2) a re-parameterization follow-up ('what about on Contact?') re-runs `previous.tool` against the NEW target from the question itself \u2014 an ambiguous new entity still blocks with a clarification; (3) an ordinal/descriptor pick ('the second one', 'the Contact one') against `previous.clarification` + `previous.question` re-dispatches through the full clarification-continuation contract (stale ids rejected exactly as usual; 0 or \u22652 descriptor matches, or an out-of-range ordinal, re-ask instead of guessing). HONESTY UNDER CONTEXT: refusal gates run on the RAW question BEFORE any context logic \u2014 context never bypasses them and adds no executable path to a refused turn; a carried componentId that no longer resolves gets a context-specific PREMISE disclosure and never advisory-routes; value validation is FAIL-OPEN (an unregistered `tool` / malformed `componentId` is skipped and noted in `contextApplied.ignored`, never a hard error \u2014 shape errors still reject). A SELF-CONTAINED question IGNORES context: no anaphor, or a confident own route, returns a response identical to the no-context call, and omitting `context` keeps behavior byte-identical. When (and only when) context changes the route, the response disclosure `route.contextApplied` carries { kind: 'entity-substitution' | 'continuation' | 'reparameterization' | 'clarification-selection', anaphor, substitutedComponentId?, from?, inheritedTool?, selection?, ignored? } and the rendered text appends a 'Context applied: \u2026' line \u2014 mere presence of the param never emits it. Call this first on a vague/broad question to decide which tool(s) to run.",
|
|
66920
70255
|
inputSchema: ROUTE_QUESTION_INPUT_SCHEMA
|
|
66921
70256
|
},
|
|
66922
70257
|
{
|
|
@@ -67386,7 +70721,7 @@ var init_tools = __esm({
|
|
|
67386
70721
|
},
|
|
67387
70722
|
{
|
|
67388
70723
|
name: "sfi.what_happens_on_save",
|
|
67389
|
-
description: "What ALREADY runs when a record on this object is created or updated \u2014 every existing automation (flows, triggers, validation rules, workflow, approvals, assignment rules) that fires on save, in the order it fires. Use it before building a new automation to check what's already there and whether your new process would duplicate or collide with an existing one. Produce the documented Salesforce order-of-execution (SOE) instantiated for THIS org and the given DML event on the target object. Walks the canonical SOE phases in order \u2014 before-save-flows (before-save record-triggered Flows \u2014 `triggersOn` edge `triggerType` RecordBeforeSave \u2014 which run FIRST, ahead of before-triggers; insert/update only), pre-save-validation (ValidationRules), pre-save-triggers + after-triggers (ApexTriggers whose `events` includes a matching `before <event>` / `after <event>` lifecycle entry), save (a documented placeholder for system validation + DB write), post-save-flows (record-triggered AFTER-save Flows whose `recordTriggerType` matches the event), post-save-workflows (WorkflowRules whose `triggerType` matches), post-save-assignment (Lead/Case AssignmentRules + AutoResponseRules + EscalationRules parented to the object), post-save-approval (ApprovalProcesses parented to the object), and post-save-async (ApexClasses dispatched via `dispatchesAsync` from any trigger above). Only ACTIVE automation is listed as execution steps \u2014 Draft/Obsolete Flows and active:false rules/processes are omitted from `soe` and surfaced in `inactiveConfigured` when present. Each step carries the firer's id/type/apiName, the gating `firesWhen` ConditionalContext when one exists, and an actions array enumerating the firer's outgoing edges (excluding structural parentOf/triggersOn/firesWhen). Honesty axis (verbatim): conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether the runtime record satisfies them. Workflow field updates can re-fire before/after-update triggers (a second pass); the composition lists each automation once. A workflow rule's time-dependent actions (workflowTimeTriggers) are SCHEDULED for a record-level offset the offline vault cannot evaluate; the rule is listed once in the synchronous post-save-workflows phase and its time-delayed actions are NOT claimed to fire at save. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.",
|
|
70724
|
+
description: "What ALREADY runs when a record on this object is created or updated \u2014 every existing automation (flows, triggers, validation rules, workflow, approvals, assignment rules) that fires on save, in the order it fires. Use it before building a new automation to check what's already there and whether your new process would duplicate or collide with an existing one. Also the answer to informal asks like 'what fires / runs when I save an Account', 'everything that touches X on save', 'we keep hitting record-lock errors \u2014 what's colliding on this object', 'any automation on X I should know about before adding more' (for WHICH automation is risky, use automation_risk_report instead). Produce the documented Salesforce order-of-execution (SOE) instantiated for THIS org and the given DML event on the target object. Walks the canonical SOE phases in order \u2014 before-save-flows (before-save record-triggered Flows \u2014 `triggersOn` edge `triggerType` RecordBeforeSave \u2014 which run FIRST, ahead of before-triggers; insert/update only), pre-save-validation (ValidationRules), pre-save-triggers + after-triggers (ApexTriggers whose `events` includes a matching `before <event>` / `after <event>` lifecycle entry), save (a documented placeholder for system validation + DB write), post-save-flows (record-triggered AFTER-save Flows whose `recordTriggerType` matches the event), post-save-workflows (WorkflowRules whose `triggerType` matches), post-save-assignment (Lead/Case AssignmentRules + AutoResponseRules + EscalationRules parented to the object), post-save-approval (ApprovalProcesses parented to the object), and post-save-async (ApexClasses dispatched via `dispatchesAsync` from any trigger above). Only ACTIVE automation is listed as execution steps \u2014 Draft/Obsolete Flows and active:false rules/processes are omitted from `soe` and surfaced in `inactiveConfigured` when present. Each step carries the firer's id/type/apiName, the gating `firesWhen` ConditionalContext when one exists, and an actions array enumerating the firer's outgoing edges (excluding structural parentOf/triggersOn/firesWhen). Honesty axis (verbatim): conditions ARE listed but NOT EVALUATED \u2014 the tool does not know whether the runtime record satisfies them. Workflow field updates can re-fire before/after-update triggers (a second pass); the composition lists each automation once. A workflow rule's time-dependent actions (workflowTimeTriggers) are SCHEDULED for a record-level offset the offline vault cannot evaluate; the rule is listed once in the synchronous post-save-workflows phase and its time-delayed actions are NOT claimed to fire at save. Manual sharing, sharing sets, account teams, and Apex callouts after save are out of scope.",
|
|
67390
70725
|
inputSchema: WHAT_HAPPENS_ON_SAVE_INPUT_SCHEMA
|
|
67391
70726
|
},
|
|
67392
70727
|
{
|
|
@@ -67775,7 +71110,7 @@ var init_tools = __esm({
|
|
|
67775
71110
|
},
|
|
67776
71111
|
{
|
|
67777
71112
|
name: "sfi.find_component_usages",
|
|
67778
|
-
description: 'The universal "where is this component used?" answer for ANY canonical component type (`componentId`) \u2014 one entry point instead of fanning out across find_field_anywhere / find_code_usages / get_impact / grep. Composes two evidence tiers: (1) GRAPH \u2014 incoming dependency edges to the target, grouped by referrer type, each carrying edge `confidence`, EXCLUDING access grants (`grantedBy`) and structural `parentOf` (access is not usage); (2) GREP supplement (`text-match` tier, `includeGrep` default true) \u2014 a literal search of Apex AND frontend bundle source (LWC/Aura/Visualforce \u2014 `$Label`/`$Resource`/`@salesforce` module references) for the api name, catching references the graph does not model (dynamic SOQL, reflective access, CustomMetadataType / CustomLabel / StaticResource refs). `graphReferrers[]` (type + count + sample), `grepSupplement` (matches with path/line/snippet), `summary` (counts + `hasStaticEvidence`), `boundaries[]`, `truncated`. ACCESS-GRANT section: grants stay OUT of `graphReferrers`, but a separate `grantedBy` section (`{count, granters[{id,type}]}`) surfaces the granting containers when the target is a `CustomPermission` (its only incoming edges ARE grants \u2014 this answers "which Profiles / PermissionSets grant it?"; always present for that type, count 0 = no container grants it) or when the target has zero usage edges but incoming `grantedBy` edges; absent otherwise. HONESTY: empty graph + empty grep = "no static evidence in the vault" (in `boundaries`), NEVER "nothing uses this" \u2014 dynamic constructs, un-modeled families (reports/dashboards/list-views), and managed packages are invisible; grants are NOT usage, so `grantedBy` never counts toward `hasStaticEvidence`. Phantom-aware (a referenced-but-not-retrieved target still answers from its edges, including a granted-but-undefined managed-package CustomPermission). Specialized tools (find_field_anywhere, layout_assignments, \u2026) stay for a deeper single-family answer; this unifies the common case. Non-canonical id \u2192 `invalid-query`; an id with no node, no referrers AND no grants \u2192 `component-not-found`.',
|
|
71113
|
+
description: 'The universal "where is this component used?" answer for ANY canonical component type (`componentId`) \u2014 one entry point instead of fanning out across find_field_anywhere / find_code_usages / get_impact / grep. Also the answer to informal asks like \'who has this permission set / which profiles grant it\', \'what references / calls into this component\', \'is this still used anywhere\' \u2014 it answers what points TO a named component (use search_components to find components MATCHING a description). Composes two evidence tiers: (1) GRAPH \u2014 incoming dependency edges to the target, grouped by referrer type, each carrying edge `confidence`, EXCLUDING access grants (`grantedBy`) and structural `parentOf` (access is not usage); (2) GREP supplement (`text-match` tier, `includeGrep` default true) \u2014 a literal search of Apex AND frontend bundle source (LWC/Aura/Visualforce \u2014 `$Label`/`$Resource`/`@salesforce` module references) for the api name, catching references the graph does not model (dynamic SOQL, reflective access, CustomMetadataType / CustomLabel / StaticResource refs). `graphReferrers[]` (type + count + sample), `grepSupplement` (matches with path/line/snippet), `summary` (counts + `hasStaticEvidence`), `boundaries[]`, `truncated`. ACCESS-GRANT section: grants stay OUT of `graphReferrers`, but a separate `grantedBy` section (`{count, granters[{id,type}]}`) surfaces the granting containers when the target is a `CustomPermission` (its only incoming edges ARE grants \u2014 this answers "which Profiles / PermissionSets grant it?"; always present for that type, count 0 = no container grants it) or when the target has zero usage edges but incoming `grantedBy` edges; absent otherwise. HONESTY: empty graph + empty grep = "no static evidence in the vault" (in `boundaries`), NEVER "nothing uses this" \u2014 dynamic constructs, un-modeled families (reports/dashboards/list-views), and managed packages are invisible; grants are NOT usage, so `grantedBy` never counts toward `hasStaticEvidence`. Phantom-aware (a referenced-but-not-retrieved target still answers from its edges, including a granted-but-undefined managed-package CustomPermission). Specialized tools (find_field_anywhere, layout_assignments, \u2026) stay for a deeper single-family answer; this unifies the common case. Non-canonical id \u2192 `invalid-query`; an id with no node, no referrers AND no grants \u2192 `component-not-found`.',
|
|
67779
71114
|
inputSchema: FIND_COMPONENT_USAGES_INPUT_SCHEMA
|
|
67780
71115
|
},
|
|
67781
71116
|
{
|
|
@@ -69855,7 +73190,7 @@ var init_package_version = __esm({
|
|
|
69855
73190
|
"use strict";
|
|
69856
73191
|
readCliPackageVersion = () => {
|
|
69857
73192
|
if (true)
|
|
69858
|
-
return "0.1.
|
|
73193
|
+
return "0.1.23";
|
|
69859
73194
|
for (const rel of ["../package.json", "../../package.json"]) {
|
|
69860
73195
|
try {
|
|
69861
73196
|
const raw = readFileSync3(fileURLToPath(new URL(rel, import.meta.url)), "utf8");
|
|
@@ -136983,10 +140318,10 @@ var init_refresh = __esm({
|
|
|
136983
140318
|
progress(`Incremental: reused ${walked.reusedCount} unchanged file(s) from cache.`);
|
|
136984
140319
|
}
|
|
136985
140320
|
if (!opts.noPull && (requestedTypes === null || requestedTypes.has("CustomObject"))) {
|
|
136986
|
-
const
|
|
136987
|
-
if (
|
|
136988
|
-
progress(`Auto-expanding retrieve: ${
|
|
136989
|
-
const pulled2 = await runSfRetrieveObjects(targetOrg, paths.source,
|
|
140321
|
+
const expand = objectsToExpandManifest(walked.results);
|
|
140322
|
+
if (expand.length > 0) {
|
|
140323
|
+
progress(`Auto-expanding retrieve: ${expand.length} object(s) your automation references but the wildcard excluded...`);
|
|
140324
|
+
const pulled2 = await runSfRetrieveObjects(targetOrg, paths.source, expand);
|
|
136990
140325
|
if (pulled2.ok) {
|
|
136991
140326
|
walked = await walkAndExtract(paths.source, requestedTypes, prevCache);
|
|
136992
140327
|
} else {
|
|
@@ -139779,7 +143114,7 @@ var makeShutdownOnce = (ctx) => {
|
|
|
139779
143114
|
|
|
139780
143115
|
// dist/src/commands/demo.js
|
|
139781
143116
|
init_refresh();
|
|
139782
|
-
var buildVersion = () => true ? "0.1.
|
|
143117
|
+
var buildVersion = () => true ? "0.1.23" : "dev";
|
|
139783
143118
|
var SHUTDOWN_SIGNALS2 = ["SIGINT", "SIGTERM"];
|
|
139784
143119
|
var resolveDemoSource = () => {
|
|
139785
143120
|
let dir = dirname21(fileURLToPath2(import.meta.url));
|
|
@@ -140764,7 +144099,7 @@ init_vault_git();
|
|
|
140764
144099
|
init_watch();
|
|
140765
144100
|
var readVersion = () => {
|
|
140766
144101
|
if (true)
|
|
140767
|
-
return "0.1.
|
|
144102
|
+
return "0.1.23";
|
|
140768
144103
|
const pkgUrl = new URL("../../package.json", import.meta.url);
|
|
140769
144104
|
const raw = readFileSync6(fileURLToPath3(pkgUrl), "utf8");
|
|
140770
144105
|
const parsed = JSON.parse(raw);
|