sf-intelligence 0.1.1 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +107 -15
  2. package/package.json +6 -6
package/dist/index.js CHANGED
@@ -944,7 +944,7 @@ var init_resolve_index = __esm({
944
944
  });
945
945
 
946
946
  // ../graph/dist/src/resolve.js
947
- var DEFAULT_LIMIT, MAX_LIMIT, MIN_BASE, MATCHED_FLOOR, NONE_THRESHOLD, EXACT_THRESHOLD, EXACT_COVERAGE, CONTENDER_RATIO, LENGTH_RATIO_FLOOR, SYNONYM_SCORE, POP_K, COVERAGE_EXP, STRONG_ANCHOR, PARENT_MATCH_BONUS, TYPE_WEIGHT, typeWeight, scoreToken, rollupKind, buildEvidence, resolveComponents;
947
+ var DEFAULT_LIMIT, MAX_LIMIT, MIN_BASE, MATCHED_FLOOR, NONE_THRESHOLD, EXACT_THRESHOLD, EXACT_COVERAGE, CONTENDER_RATIO, LENGTH_RATIO_FLOOR, SYNONYM_SCORE, POP_K, COVERAGE_EXP, STRONG_ANCHOR, TYPE_WEIGHT, typeWeight, scoreToken, rollupKind, buildEvidence, resolveComponents;
948
948
  var init_resolve = __esm({
949
949
  "../graph/dist/src/resolve.js"() {
950
950
  "use strict";
@@ -964,7 +964,6 @@ var init_resolve = __esm({
964
964
  POP_K = 0.08;
965
965
  COVERAGE_EXP = 0.5;
966
966
  STRONG_ANCHOR = 0.9;
967
- PARENT_MATCH_BONUS = 1.1;
968
967
  TYPE_WEIGHT = {
969
968
  CustomObject: 1,
970
969
  CustomField: 0.95,
@@ -1092,6 +1091,7 @@ var init_resolve = __esm({
1092
1091
  const scored = [];
1093
1092
  const coverageById = /* @__PURE__ */ new Map();
1094
1093
  const wholeExactIds = /* @__PURE__ */ new Set();
1094
+ const parentMatchedIds = /* @__PURE__ */ new Set();
1095
1095
  for (const c of pass1) {
1096
1096
  const matched = c.perToken.filter((t) => t.score >= MATCHED_FLOOR);
1097
1097
  if (matched.length === 0 && !c.wholeExact)
@@ -1106,13 +1106,15 @@ var init_resolve = __esm({
1106
1106
  continue;
1107
1107
  const type = c.node.type;
1108
1108
  const refs = c.node.inbound;
1109
- const score = base * typeWeight(type) * (1 + POP_K * Math.log10(1 + refs)) * (c.parentMatched ? PARENT_MATCH_BONUS : 1);
1109
+ const score = base * typeWeight(type) * (1 + POP_K * Math.log10(1 + refs));
1110
1110
  const kind = c.wholeExact ? "exact" : rollupKind(matched);
1111
1111
  const nodeTokenSet = new Set(c.node.tokens);
1112
1112
  const matchedNodeTokens = new Set(matched.map((m) => m.matchedToken).filter((t) => nodeTokenSet.has(t)));
1113
1113
  coverageById.set(c.node.id, c.wholeExact ? 1 : matchedNodeTokens.size / c.node.tokens.length);
1114
1114
  if (c.wholeExact)
1115
1115
  wholeExactIds.add(c.node.id);
1116
+ if (c.parentMatched)
1117
+ parentMatchedIds.add(c.node.id);
1116
1118
  scored.push({
1117
1119
  id: c.node.id,
1118
1120
  type,
@@ -1134,6 +1136,10 @@ var init_resolve = __esm({
1134
1136
  const tierB = b.base >= EXACT_THRESHOLD ? 0 : 1;
1135
1137
  if (tierA !== tierB)
1136
1138
  return tierA - tierB;
1139
+ const pmA = parentMatchedIds.has(a.id) ? 0 : 1;
1140
+ const pmB = parentMatchedIds.has(b.id) ? 0 : 1;
1141
+ if (pmA !== pmB)
1142
+ return pmA - pmB;
1137
1143
  if (a.score !== b.score)
1138
1144
  return b.score - a.score;
1139
1145
  return a.id < b.id ? -1 : 1;
@@ -1145,7 +1151,7 @@ var init_resolve = __esm({
1145
1151
  if (top === void 0 || bestBase < NONE_THRESHOLD) {
1146
1152
  disposition = "none";
1147
1153
  } else {
1148
- const contenders = candidates.filter((c) => c.score >= top.score * CONTENDER_RATIO);
1154
+ const contenders = candidates.filter((c) => c.score >= top.score * CONTENDER_RATIO || parentMatchedIds.has(c.id));
1149
1155
  const topCoverage = coverageById.get(top.id) ?? 0;
1150
1156
  disposition = top.base >= EXACT_THRESHOLD && contenders.length === 1 && topCoverage >= EXACT_COVERAGE ? "exact" : "ambiguous";
1151
1157
  }
@@ -21353,10 +21359,11 @@ var init_soe_payload_bounds = __esm({
21353
21359
  SOE_MAX_PAYLOAD_BYTES = 4e4;
21354
21360
  KEEP_ALL_AT_OR_BELOW = 4;
21355
21361
  sizeOf = (payload) => Buffer.byteLength(JSON.stringify(payload), "utf8");
21356
- enforceSoeByteBudget = (payload, steps) => {
21362
+ enforceSoeByteBudget = (payload, containers) => {
21357
21363
  if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES) {
21358
- return { truncated: false, actionsOmitted: 0 };
21364
+ return { truncated: false, actionsOmitted: 0, conditionalsTrimmed: 0, stepsOmitted: 0 };
21359
21365
  }
21366
+ const steps = containers.flat();
21360
21367
  let totalOmitted = 0;
21361
21368
  const trimTo = (step3, keep) => {
21362
21369
  if (keep >= step3.actions.length)
@@ -21395,9 +21402,76 @@ var init_soe_payload_bounds = __esm({
21395
21402
  break;
21396
21403
  trimTo(target, Math.floor(target.actions.length / 2));
21397
21404
  }
21398
- return { truncated: totalOmitted > 0, actionsOmitted: totalOmitted };
21405
+ let conditionalsTrimmed = 0;
21406
+ for (let guard = 0; guard < 1e5; guard += 1) {
21407
+ if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES)
21408
+ break;
21409
+ let target;
21410
+ let targetBytes = 0;
21411
+ for (const s of steps) {
21412
+ const cond = s.conditional;
21413
+ if (cond === void 0 || s.conditionalTruncated)
21414
+ continue;
21415
+ if (cond.expression === "" && cond.fieldRefs.length === 0)
21416
+ continue;
21417
+ const b = Buffer.byteLength(JSON.stringify(cond), "utf8");
21418
+ if (target === void 0 || b > targetBytes) {
21419
+ target = s;
21420
+ targetBytes = b;
21421
+ }
21422
+ }
21423
+ if (target === void 0)
21424
+ break;
21425
+ target.conditional = {
21426
+ conditionContextId: target.conditional.conditionContextId,
21427
+ expression: "",
21428
+ fieldRefs: []
21429
+ };
21430
+ target.conditionalTruncated = true;
21431
+ conditionalsTrimmed += 1;
21432
+ }
21433
+ let stepsOmitted = 0;
21434
+ for (let guard = 0; guard < 1e6; guard += 1) {
21435
+ if (sizeOf(payload) <= SOE_MAX_PAYLOAD_BYTES)
21436
+ break;
21437
+ let target;
21438
+ let targetBytes = 0;
21439
+ for (const c of containers) {
21440
+ if (c.length <= 1)
21441
+ continue;
21442
+ const b = Buffer.byteLength(JSON.stringify(c), "utf8");
21443
+ if (target === void 0 || b > targetBytes) {
21444
+ target = c;
21445
+ targetBytes = b;
21446
+ }
21447
+ }
21448
+ if (target === void 0)
21449
+ break;
21450
+ target.pop();
21451
+ stepsOmitted += 1;
21452
+ }
21453
+ return {
21454
+ truncated: totalOmitted > 0 || conditionalsTrimmed > 0 || stepsOmitted > 0,
21455
+ actionsOmitted: totalOmitted,
21456
+ conditionalsTrimmed,
21457
+ stepsOmitted
21458
+ };
21459
+ };
21460
+ soeTruncationNote = (result) => {
21461
+ const budgetKb = Math.round(SOE_MAX_PAYLOAD_BYTES / 1e3);
21462
+ const parts = [];
21463
+ if (result.actionsOmitted > 0) {
21464
+ parts.push(`${result.actionsOmitted} per-step action edge(s) across the heaviest steps were omitted (see each step's \`actionsOmitted\`)`);
21465
+ }
21466
+ if (result.conditionalsTrimmed > 0) {
21467
+ parts.push(`${result.conditionalsTrimmed} step condition(s) had their expression/fieldRefs dropped \u2014 the \`conditionContextId\` remains, fetch it with \`get_component\` for the full condition (see each step's \`conditionalTruncated\`)`);
21468
+ }
21469
+ if (result.stepsOmitted > 0) {
21470
+ parts.push(`${result.stepsOmitted} trailing step(s) were dropped to fit (the tail-most async/post-save steps; \`summary.totalSteps\` still reports the true total) \u2014 query a single event with \`what_happens_on_save\` to see them all`);
21471
+ }
21472
+ const lead = result.stepsOmitted > 0 ? `Response trimmed to fit the ~${budgetKb} KB MCP response budget` : `Response trimmed to fit the ~${budgetKb} KB MCP response budget: every save-order STEP is present and in order, but`;
21473
+ return `${lead} ${parts.join("; ")}. Query a single object/event for full detail.`;
21399
21474
  };
21400
- soeTruncationNote = (n) => `Response trimmed to fit the ~${Math.round(SOE_MAX_PAYLOAD_BYTES / 1e3)} KB MCP response budget: every save-order STEP is present and in order, but ${n} per-step action edge(s) across the heaviest steps were omitted (see each step's \`actionsOmitted\`). Query a single object/event or use \`get_edges\` on a specific step's component for its full action list.`;
21401
21475
  }
21402
21476
  });
21403
21477
 
@@ -21764,11 +21838,11 @@ var init_order_of_execution = __esm({
21764
21838
  byEvent,
21765
21839
  disclosure: composeSoeDisclosure(DISCLOSURE6, objectModeled)
21766
21840
  };
21767
- const allSteps = SOE_EVENTS.flatMap((event) => byEvent[event].soe);
21768
- const budget = enforceSoeByteBudget(data, allSteps);
21841
+ const containers = SOE_EVENTS.map((event) => byEvent[event].soe);
21842
+ const budget = enforceSoeByteBudget(data, containers);
21769
21843
  if (budget.truncated) {
21770
21844
  data.truncated = true;
21771
- data.disclosure = `${data.disclosure} ${soeTruncationNote(budget.actionsOmitted)}`;
21845
+ data.disclosure = `${data.disclosure} ${soeTruncationNote(budget)}`;
21772
21846
  }
21773
21847
  return ok({
21774
21848
  data,
@@ -26611,10 +26685,10 @@ var init_what_happens_on_save = __esm({
26611
26685
  },
26612
26686
  disclosure: composeSoeDisclosure(DISCLOSURE7, objectModeled)
26613
26687
  };
26614
- const budget = enforceSoeByteBudget(data, soe);
26688
+ const budget = enforceSoeByteBudget(data, [soe]);
26615
26689
  if (budget.truncated) {
26616
26690
  data.truncated = true;
26617
- data.disclosure = `${data.disclosure} ${soeTruncationNote(budget.actionsOmitted)}`;
26691
+ data.disclosure = `${data.disclosure} ${soeTruncationNote(budget)}`;
26618
26692
  }
26619
26693
  return ok({
26620
26694
  data,
@@ -26627,6 +26701,23 @@ var init_what_happens_on_save = __esm({
26627
26701
  }
26628
26702
  });
26629
26703
 
26704
+ // ../mcp/dist/src/tools/phantom-node.js
26705
+ var phantomAwareNotFoundMessage;
26706
+ var init_phantom_node = __esm({
26707
+ "../mcp/dist/src/tools/phantom-node.js"() {
26708
+ "use strict";
26709
+ init_src();
26710
+ phantomAwareNotFoundMessage = async (ctx, id, kindLabel) => {
26711
+ const inbound = await listEdges(ctx.graph, id, { direction: "in" });
26712
+ const refs = inbound.ok ? inbound.value.length : 0;
26713
+ if (refs === 0) {
26714
+ return `no ${kindLabel} with id ${id}`;
26715
+ }
26716
+ return `\`${id}\` is referenced by ${refs} other component(s) in this org (e.g. code, tests, or permission grants) but its own ${kindLabel} definition was never retrieved into the vault \u2014 typically a managed-package component or one outside the retrieve scope. Run \`sfi refresh\` if it should be retrievable; otherwise treat it as external.`;
26717
+ };
26718
+ }
26719
+ });
26720
+
26630
26721
  // ../mcp/dist/src/tools/what-if-change-method-signature.js
26631
26722
  import { z as z104 } from "zod";
26632
26723
  var APEX_CLASS_PREFIX9, DISCLOSURE8, whatIfChangeMethodSignatureInputSchema, readMethodName, isTestClass8, buildExplanation2, classifyCaller, aggregateVerdict4, compareImpacts, compareIds, collectCallers, collectCoveringTests, whatIfChangeMethodSignatureHandler;
@@ -26637,6 +26728,7 @@ var init_what_if_change_method_signature = __esm({
26637
26728
  init_src();
26638
26729
  init_coerce_id();
26639
26730
  init_coverage_trust();
26731
+ init_phantom_node();
26640
26732
  APEX_CLASS_PREFIX9 = "ApexClass:";
26641
26733
  DISCLOSURE8 = "callers identified via the v1.4 apex-scanner are at heuristic confidence; dynamic dispatch via Type.forName + invoke is invisible. Test classes are identified by @isTest + naming convention (className + 'Test' suffix) and by coversTest edges; a test class that doesn't follow the naming convention and doesn't carry a @TestVisible-tagged covering reference may be missed.";
26642
26734
  whatIfChangeMethodSignatureInputSchema = z104.object({
@@ -26776,7 +26868,7 @@ var init_what_if_change_method_signature = __esm({
26776
26868
  if (nodeResult.value === null) {
26777
26869
  return err({
26778
26870
  kind: "component-not-found",
26779
- message: `no ApexClass with id ${classId}`,
26871
+ message: await phantomAwareNotFoundMessage(ctx, classId, "ApexClass"),
26780
26872
  path: classId
26781
26873
  });
26782
26874
  }
@@ -46748,7 +46840,7 @@ var registerStatusCommand = (program) => {
46748
46840
  // dist/src/program.js
46749
46841
  var readVersion = () => {
46750
46842
  if (true)
46751
- return "0.1.1";
46843
+ return "0.1.4";
46752
46844
  const pkgUrl = new URL("../../package.json", import.meta.url);
46753
46845
  const raw = readFileSync2(fileURLToPath(pkgUrl), "utf8");
46754
46846
  const parsed = JSON.parse(raw);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sf-intelligence",
3
- "version": "0.1.1",
3
+ "version": "0.1.4",
4
4
  "description": "Offline-first, MCP-first knowledge base for a Salesforce org. Ask about your org's metadata, dependencies, permissions, and automation — grounded in real retrieved metadata. Ships the sfi CLI and an MCP server.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/PranavNagrecha/Salesforce-Intelligence",
@@ -52,14 +52,14 @@
52
52
  "devDependencies": {
53
53
  "esbuild": "^0.28.0",
54
54
  "vitest": "^1.6.0",
55
- "@sf-intelligence/mcp": "0.1.0",
56
- "@sf-intelligence/core": "0.1.0",
57
- "@sf-intelligence/contracts": "0.1.0",
58
- "@sf-intelligence/patterns": "0.1.0",
59
55
  "@sf-intelligence/extractors": "0.1.0",
60
- "@sf-intelligence/renderers": "0.1.0",
61
56
  "@sf-intelligence/graph": "0.1.0",
57
+ "@sf-intelligence/patterns": "0.1.0",
58
+ "@sf-intelligence/core": "0.1.0",
62
59
  "@sf-intelligence/vault": "0.1.0",
60
+ "@sf-intelligence/contracts": "0.1.0",
61
+ "@sf-intelligence/mcp": "0.1.0",
62
+ "@sf-intelligence/renderers": "0.1.0",
63
63
  "@sf-intelligence/tooling-api": "0.1.0"
64
64
  },
65
65
  "scripts": {