@0xsarwagya/ontoly-core 0.1.0-alpha.13 → 0.1.0-alpha.14

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 CHANGED
@@ -12,7 +12,7 @@ pnpm add @0xsarwagya/ontoly-core
12
12
 
13
13
  ## Status
14
14
 
15
- Alpha package for Ontoly v0.1.0-alpha.13. The public API is versioned with the Software Graph and RFC process.
15
+ Alpha package for Ontoly v0.1.0-alpha.14. The public API is versioned with the Software Graph and RFC process.
16
16
 
17
17
  ## Links
18
18
 
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/semantic-index.ts
2
- var SEMANTIC_INDEX_VERSION = "1.0.0";
2
+ var SEMANTIC_INDEX_VERSION = "1.0.1";
3
3
  var STOP_WORDS = /* @__PURE__ */ new Set([
4
4
  "a",
5
5
  "an",
@@ -63,7 +63,7 @@ var FRAMEWORK_SUFFIXES = /* @__PURE__ */ new Set([
63
63
  var CATEGORY_TYPES = {
64
64
  concept: [],
65
65
  symbol: ["Function", "Method", "Class", "Interface", "TypeAlias", "Enum", "Field", "Service", "Provider", "Controller", "Repository", "Resource", "Model", "Route"],
66
- feature: ["Route", "Controller", "Service", "Provider", "Module", "Package", "Repository", "Model", "Resource", "Class", "Interface", "TypeAlias", "Function", "Method", "Operation"],
66
+ feature: ["Route", "Controller", "Service", "Provider", "Module", "Package", "Repository", "Model", "Resource", "Class", "Interface", "TypeAlias", "Function", "Method", "Operation", "Configuration", "EnvironmentVariable", "Guard", "Permission", "Middleware"],
67
67
  configuration: ["Configuration", "EnvironmentVariable", "BuildTarget", "Script", "Task"],
68
68
  environment: ["EnvironmentVariable"],
69
69
  route: ["Route", "Operation", "Controller"],
@@ -133,8 +133,67 @@ var SEMANTIC_EXPANSIONS = {
133
133
  var SEMANTIC_METADATA_DEPTH_LIMIT = 3;
134
134
  var SEMANTIC_METADATA_ARRAY_LIMIT = 12;
135
135
  var SEMANTIC_METADATA_ENTRY_LIMIT = 24;
136
- var SEMANTIC_METADATA_VALUES_LIMIT = 80;
136
+ var SEMANTIC_METADATA_VALUES_LIMIT = 48;
137
137
  var SEMANTIC_TEXT_LIMIT = 240;
138
+ var SEMANTIC_ALIAS_SOURCE_LIMIT = 16;
139
+ var SEMANTIC_ALIAS_LIMIT = 20;
140
+ var SEMANTIC_KEYWORD_LIMIT = 24;
141
+ var SEMANTIC_INVERTED_IDS_LIMIT = 160;
142
+ var SEMANTIC_VOCABULARY_IDS_LIMIT = 50;
143
+ var SEMANTIC_NOISE_TERMS = /* @__PURE__ */ new Set([
144
+ "0",
145
+ "0s",
146
+ "1",
147
+ "1s",
148
+ "2",
149
+ "2s",
150
+ "3",
151
+ "3s",
152
+ "4",
153
+ "4s",
154
+ "abstract",
155
+ "abstracts",
156
+ "active",
157
+ "actives",
158
+ "anies",
159
+ "any",
160
+ "async",
161
+ "asyncs",
162
+ "boolean",
163
+ "booleans",
164
+ "false",
165
+ "falses",
166
+ "number",
167
+ "numbers",
168
+ "parameter",
169
+ "parameters",
170
+ "static",
171
+ "statics",
172
+ "string",
173
+ "strings",
174
+ "true",
175
+ "trues",
176
+ "typeparameter",
177
+ "typeparameters"
178
+ ]);
179
+ var REPOSITORY_LOCAL_KIND_BOOSTS = {
180
+ Service: 112,
181
+ Controller: 92,
182
+ Repository: 72,
183
+ Route: 68,
184
+ Operation: 64,
185
+ Method: 62,
186
+ Module: 58,
187
+ Configuration: 58,
188
+ Model: 56,
189
+ Interface: 54,
190
+ TypeAlias: 54,
191
+ Class: 50,
192
+ Function: 48,
193
+ EnvironmentVariable: 44,
194
+ Provider: 42
195
+ };
196
+ var FRAMEWORK_PACKAGE_PATTERN = /@nestjs\/|@medplum\/|next\/dist|react\/|typescript\/lib|@types\/|@babel\/|@typescript-eslint\/|tslib|rxjs|zone\.js/i;
138
197
  function createSemanticIndex(graph) {
139
198
  const nodeById = new Map(graph.nodes.map((node) => [node.id, node]));
140
199
  const incoming = groupEdges(graph.edges, "to");
@@ -352,12 +411,12 @@ function aliasSourceValues(graph, node, neighbors, parentChain) {
352
411
  node.file ?? "",
353
412
  node.package ?? "",
354
413
  ...parentChain,
355
- ...metadataValues,
356
414
  ...routeValues,
357
415
  ...envValues,
358
- ...neighbors.map((neighbor) => neighbor.name),
359
- ...neighbors.map((neighbor) => neighbor.id)
360
- ].filter(Boolean));
416
+ ...neighbors.slice(0, 16).map((neighbor) => neighbor.name),
417
+ ...neighbors.slice(0, 16).map((neighbor) => neighbor.id),
418
+ ...metadataValues
419
+ ].filter(Boolean)).slice(0, SEMANTIC_ALIAS_SOURCE_LIMIT);
361
420
  }
362
421
  function generateAliases(values, node) {
363
422
  const aliases = /* @__PURE__ */ new Set();
@@ -379,6 +438,9 @@ function generateAliases(values, node) {
379
438
  aliases.add(withoutSuffix.join(""));
380
439
  }
381
440
  for (const token of tokens) {
441
+ if (isSemanticNoiseTerm(token)) {
442
+ continue;
443
+ }
382
444
  aliases.add(token);
383
445
  aliases.add(singularize(token));
384
446
  aliases.add(pluralize(token));
@@ -408,12 +470,15 @@ function generateAliases(values, node) {
408
470
  aliases.add("feature");
409
471
  aliases.add("feature module");
410
472
  }
411
- return uniqueStrings([...aliases].filter((alias) => alias.length > 1));
473
+ return uniqueStrings([...aliases].filter((alias) => alias.length > 1).filter((alias) => !isNoisySemanticAlias(alias))).slice(0, SEMANTIC_ALIAS_LIMIT);
412
474
  }
413
475
  function generateKeywords(values, node) {
414
476
  const keywords = /* @__PURE__ */ new Set();
415
477
  for (const value of values) {
416
478
  for (const token of tokenize(value)) {
479
+ if (isSemanticNoiseTerm(token)) {
480
+ continue;
481
+ }
417
482
  keywords.add(token);
418
483
  keywords.add(singularize(token));
419
484
  }
@@ -434,7 +499,7 @@ function generateKeywords(values, node) {
434
499
  if (isFeatureModuleNode(node)) {
435
500
  keywords.add("feature");
436
501
  }
437
- return uniqueStrings([...keywords].filter((keyword) => keyword.length > 1));
502
+ return uniqueStrings([...keywords].filter((keyword) => keyword.length > 1).filter((keyword) => !isSemanticNoiseTerm(keyword))).slice(0, SEMANTIC_KEYWORD_LIMIT);
438
503
  }
439
504
  function buildInvertedIndex(entries) {
440
505
  const inverted = /* @__PURE__ */ new Map();
@@ -457,9 +522,14 @@ function buildVocabulary(entries) {
457
522
  if (term.length < 2 || STOP_WORDS.has(term)) {
458
523
  continue;
459
524
  }
525
+ if (isSemanticNoiseTerm(term)) {
526
+ continue;
527
+ }
460
528
  const current = terms.get(term) ?? { count: 0, nodeIds: /* @__PURE__ */ new Set(), kinds: /* @__PURE__ */ new Set() };
461
529
  current.count += 1;
462
- current.nodeIds.add(entry.stableId);
530
+ if (current.nodeIds.size < SEMANTIC_VOCABULARY_IDS_LIMIT) {
531
+ current.nodeIds.add(entry.stableId);
532
+ }
463
533
  current.kinds.add(entry.kind);
464
534
  terms.set(term, current);
465
535
  }
@@ -476,7 +546,7 @@ function rankCandidates(index, intent, options) {
476
546
  const entryById = new Map(index.entries.map((entry) => [entry.stableId, entry]));
477
547
  const kinds = options.kinds?.length ? new Set(options.kinds) : categoryTypeSet(options.category);
478
548
  const candidates = candidateIds.map((id) => entryById.get(id)).filter(isEntry).filter((entry) => includeEntryForCategory(entry, intent, options.category)).filter((entry) => !kinds || kinds.has(entry.kind)).filter((entry) => !options.packageScope || entry.package === options.packageScope || entry.filePath?.startsWith(options.packageScope)).map((entry) => scoreEntry(entry, intent, options.category)).filter((candidate) => candidate.score > 0).sort(compareCandidates);
479
- return candidates.slice(0, options.limit ?? 20);
549
+ return calibrateCandidateConfidences(candidates, intent, options.category).slice(0, options.limit ?? 20);
480
550
  }
481
551
  function candidateIdsFor(index, intent) {
482
552
  const ids = /* @__PURE__ */ new Set();
@@ -486,14 +556,59 @@ function candidateIdsFor(index, intent) {
486
556
  ids.add(id);
487
557
  }
488
558
  }
559
+ for (const id of repositoryVocabularyCandidateIds(index, terms)) {
560
+ ids.add(id);
561
+ }
489
562
  const fuzzyIds = index.entries.filter((entry) => fuzzyEntryMatch(entry, intent)).map((entry) => entry.stableId).sort();
490
563
  if (ids.size > 0) {
491
564
  for (const id of fuzzyIds.slice(0, 250)) {
492
565
  ids.add(id);
493
566
  }
494
- return [...ids].sort();
567
+ return [...expandCandidateIds(index, intent, [...ids])].sort();
495
568
  }
496
- return fuzzyIds;
569
+ return [...expandCandidateIds(index, intent, fuzzyIds)].sort();
570
+ }
571
+ function repositoryVocabularyCandidateIds(index, terms) {
572
+ const termSet = new Set(terms);
573
+ const ids = /* @__PURE__ */ new Set();
574
+ for (const vocabularyTerm of index.vocabulary) {
575
+ if (termSet.has(vocabularyTerm.term)) {
576
+ for (const id of vocabularyTerm.nodeIds) {
577
+ ids.add(id);
578
+ }
579
+ }
580
+ }
581
+ for (const vocabularyTerm of index.vocabulary) {
582
+ if (ids.size >= 300) {
583
+ break;
584
+ }
585
+ if (!termSet.has(vocabularyTerm.term) && vocabularyTerm.term.length > 3 && terms.some((term) => term.length > 3 && (term.includes(vocabularyTerm.term) || vocabularyTerm.term.includes(term)))) {
586
+ for (const id of vocabularyTerm.nodeIds.slice(0, 20)) {
587
+ ids.add(id);
588
+ }
589
+ }
590
+ }
591
+ return [...ids].sort();
592
+ }
593
+ function expandCandidateIds(index, intent, seedIds) {
594
+ const ids = new Set(seedIds);
595
+ const entryById = new Map(index.entries.map((entry) => [entry.stableId, entry]));
596
+ for (const id of seedIds.slice(0, 300)) {
597
+ const entry = entryById.get(id);
598
+ if (!entry || semanticFeatureAgreement(entry, intent) < 0.35) {
599
+ continue;
600
+ }
601
+ for (const neighborId of entry.relationships.neighborIds) {
602
+ const neighbor = entryById.get(neighborId);
603
+ if (!neighbor) {
604
+ continue;
605
+ }
606
+ if (semanticFeatureAgreement(neighbor, intent) >= 0.25 || architectureAgreementConfidence(neighbor, "feature") >= 0.75) {
607
+ ids.add(neighborId);
608
+ }
609
+ }
610
+ }
611
+ return [...ids];
497
612
  }
498
613
  function scoreEntry(entry, intent, category) {
499
614
  const reasons = [];
@@ -540,94 +655,347 @@ function scoreEntry(entry, intent, category) {
540
655
  addScore(reasons, "intent-expansion", expandedMatches.length * 32, expandedMatches.join(", "));
541
656
  const relationshipMatches = intent.expandedTerms.filter((term) => entry.relationships.neighborNames.some((name) => tokenize(name).includes(term)));
542
657
  addScore(reasons, "relationship-context", Math.min(120, relationshipMatches.length * 18), relationshipMatches.join(", "));
658
+ const semanticSimilarity = semanticSimilarityConfidence(entry, intent);
659
+ addScore(reasons, "semantic-similarity", semanticSimilarity * 140, `${Math.round(semanticSimilarity * 100)}% lexical/semantic coverage`);
660
+ const featureAgreement = semanticFeatureAgreement(entry, intent);
661
+ addScore(reasons, "feature-agreement", featureAgreement * 120, `${Math.round(featureAgreement * 100)}% repository feature agreement`);
662
+ const queryCoverage = originalQueryCoverage(entry, intent);
663
+ addScore(reasons, "query-token-coverage", queryCoverage.ratio * 110, `${Math.round(queryCoverage.ratio * 100)}% original query token coverage`);
664
+ const architectureAgreement = architectureAgreementConfidence(entry, category);
665
+ addScore(reasons, "architecture-agreement", architectureAgreement * 90, `${entry.kind} agreement for ${category}`);
543
666
  const categoryBoost = categoryBoostFor(entry, category);
544
667
  addScore(reasons, "architecture-layer", categoryBoost, `${entry.kind} in ${entry.architectureLayer}`);
668
+ addScore(reasons, "feature-owner", featureOwnerBoostFor(entry, category), `${entry.displayName} owns repository feature behavior`);
545
669
  addScore(reasons, "graph-importance", Math.min(90, entry.importance * 18), `degree ${entry.relationships.degree}`);
546
670
  addScore(reasons, "usage-frequency", Math.min(60, entry.usageFrequency * 8), `${entry.usageFrequency} incoming edges`);
547
671
  addScore(reasons, "repository-locality", repositoryLocalityBoost(entry), repositoryLocalityEvidence(entry));
548
- const penalty = repositoryRankingPenalty(entry);
672
+ const penalty = repositoryRankingPenalty(entry) + queryCoverage.missing.length * 120 + semanticFeaturePenalty(entry, intent);
549
673
  const score = Math.max(0, reasons.reduce((total, reason) => total + reason.score, 0) - penalty);
550
- const returnedReasons = penalty > 0 ? [...reasons, { factor: "repository-noise-demotion", score: -penalty, evidence: repositoryNoiseEvidence(entry) }] : reasons;
674
+ const returnedReasons = penalty > 0 ? [...reasons, { factor: "repository-noise-demotion", score: -penalty, evidence: repositoryNoiseEvidence(entry, queryCoverage.missing) }] : reasons;
551
675
  return {
552
676
  nodeId: entry.stableId,
553
677
  stableId: entry.stableId,
554
678
  displayName: entry.displayName,
555
679
  kind: entry.kind,
556
680
  score: round(score, 3),
557
- confidence: round(Math.min(1, score / 650), 3),
681
+ confidence: round(confidenceFromCandidateSignals(entry, intent, category, score), 3),
558
682
  matchedTerms: uniqueStrings([...matchedTerms]),
559
683
  reasons: returnedReasons.filter((reason) => reason.score !== 0).sort(compareReasons),
560
684
  entry
561
685
  };
562
686
  }
563
- function repositoryLocalityBoost(entry) {
564
- let score = 0;
565
- if (isRepositoryLocalEntry(entry)) {
566
- score += 32;
687
+ function calibrateCandidateConfidences(candidates, intent, category) {
688
+ if (candidates.length === 0) {
689
+ return candidates;
567
690
  }
568
- if (["Service", "Controller", "Repository", "Provider"].includes(entry.kind)) {
569
- score += 34;
691
+ const topScore = candidates[0]?.score ?? 0;
692
+ const secondScore = candidates[1]?.score ?? 0;
693
+ const closeAlternatives = candidates.filter(
694
+ (candidate, index) => index > 0 && isPlausibleAlternative(candidate, topScore, secondScore)
695
+ );
696
+ return candidates.map((candidate, index) => {
697
+ const baseConfidence = confidenceFromCandidateSignals(candidate.entry, intent, category, candidate.score);
698
+ const ratio = topScore > 0 ? Math.min(1, candidate.score / topScore) : 0;
699
+ const confidence = round(calibratedConfidenceCap(candidate, baseConfidence * (index === 0 ? 1 : ratio), {
700
+ index,
701
+ topScore,
702
+ secondScore,
703
+ closeAlternatives,
704
+ intent,
705
+ category
706
+ }), 3);
707
+ const reasons = [
708
+ ...candidate.reasons,
709
+ {
710
+ factor: "confidence-calibration",
711
+ score: round(confidence * 100),
712
+ evidence: confidenceCalibrationEvidence(candidate.entry, confidence, closeAlternatives.length)
713
+ }
714
+ ];
715
+ if (index === 0 && closeAlternatives.length > 0) {
716
+ reasons.push({
717
+ factor: "ambiguity-calibration",
718
+ score: -Math.min(30, closeAlternatives.length * 8),
719
+ evidence: `${closeAlternatives.length} plausible alternative seed(s) were retained.`
720
+ });
721
+ }
722
+ return {
723
+ ...candidate,
724
+ confidence,
725
+ reasons: reasons.filter((reason) => reason.score !== 0).sort(compareReasons)
726
+ };
727
+ });
728
+ }
729
+ function calibratedConfidenceCap(candidate, confidence, context) {
730
+ let capped = Math.max(0, Math.min(1, confidence));
731
+ const isExact = candidate.reasons.some((reason) => reason.factor === "exact-symbol" || reason.factor === "exact-normalized-name");
732
+ const scoreGap = context.topScore - context.secondScore;
733
+ if (isExternalOrFrameworkEntry(candidate.entry)) {
734
+ capped = Math.min(capped, hasRepositoryLocalAlternative(context.closeAlternatives) ? 0.52 : 0.58);
570
735
  }
571
- if (["Module", "Route", "Operation"].includes(entry.kind)) {
572
- score += 26;
736
+ if (isGenericUtilityEntry(candidate.entry) && !isExact) {
737
+ capped = Math.min(capped, 0.72);
573
738
  }
574
- if (isRepositorySymbolEntry(entry)) {
575
- score += 16;
739
+ if (context.closeAlternatives.length > 0 && !isExact) {
740
+ capped = Math.min(capped, Math.max(0.72, 0.9 - context.closeAlternatives.length * 0.06));
576
741
  }
577
- if (isDtoLikeEntry(entry)) {
578
- score += 28;
742
+ if (context.index === 0 && scoreGap > 0 && scoreGap < 45 && !isExact) {
743
+ capped = Math.min(capped, 0.78);
744
+ } else if (context.index === 0 && scoreGap > 0 && scoreGap < 90 && !isExact) {
745
+ capped = Math.min(capped, 0.86);
579
746
  }
580
- if (isFeatureModuleEntry(entry)) {
581
- score += 8;
747
+ if (context.intent.tokens.length >= 2 && originalTokenCoverage(candidate.entry, context.intent) < 0.5 && !isExact) {
748
+ capped = Math.min(capped, 0.62);
749
+ }
750
+ if (context.index > 0) {
751
+ capped = Math.min(capped, 0.84);
752
+ }
753
+ if (context.category === "feature" && architectureAgreementConfidence(candidate.entry, context.category) < 0.4 && !isExact) {
754
+ capped = Math.min(capped, 0.6);
755
+ }
756
+ return capped;
757
+ }
758
+ function isPlausibleAlternative(candidate, topScore, secondScore) {
759
+ return candidate.score >= Math.max(secondScore, topScore - 90) || candidate.score >= topScore * 0.84;
760
+ }
761
+ function hasRepositoryLocalAlternative(candidates) {
762
+ return candidates.some((candidate) => isRepositoryLocalEntry(candidate.entry));
763
+ }
764
+ function confidenceCalibrationEvidence(entry, confidence, alternativeCount) {
765
+ const locality = isRepositoryLocalEntry(entry) ? "repository-local" : "external/framework";
766
+ const ambiguity = alternativeCount > 0 ? ` with ${alternativeCount} plausible alternative(s)` : "";
767
+ return `${locality} ${entry.kind} calibrated to ${confidence.toFixed(3)}${ambiguity}`;
768
+ }
769
+ function confidenceFromCandidateSignals(entry, intent, category, score) {
770
+ const semantic = semanticSimilarityConfidence(entry, intent);
771
+ const locality = repositoryLocalityConfidence(entry);
772
+ const architecture = architectureAgreementConfidence(entry, category);
773
+ const feature = semanticFeatureAgreement(entry, intent);
774
+ const connectivity = graphConnectivityConfidence(entry);
775
+ const scoreConfidence = Math.min(1, score / 760);
776
+ const confidence = semantic * 0.32 + locality * 0.22 + architecture * 0.16 + feature * 0.16 + connectivity * 0.08 + scoreConfidence * 0.06;
777
+ const exact = entry.normalizedName === intent.normalized || normalizePhrase(entry.stableId) === intent.normalized || entry.aliases.includes(intent.normalized);
778
+ return exact ? Math.max(confidence, isRepositoryLocalEntry(entry) ? 0.78 : 0.55) : confidence;
779
+ }
780
+ function semanticSimilarityConfidence(entry, intent) {
781
+ const entryTerms = /* @__PURE__ */ new Set([entry.normalizedName, ...entry.aliases, ...entry.keywords]);
782
+ if (!intent.normalized) {
783
+ return 0;
582
784
  }
583
- if (["Configuration", "EnvironmentVariable"].includes(entry.kind)) {
584
- score += 18;
785
+ if (entry.normalizedName === intent.normalized || entry.aliases.includes(intent.normalized) || normalizePhrase(entry.stableId) === intent.normalized) {
786
+ return 1;
787
+ }
788
+ const originalCoverage = originalTokenCoverage(entry, intent);
789
+ const expandedTerms = intent.expandedTerms.filter((term) => !intent.tokens.includes(term));
790
+ const expandedCoverage = expandedTerms.length === 0 ? 0 : expandedTerms.filter((term) => entryTerms.has(term)).length / expandedTerms.length;
791
+ const phraseCoverage = intent.phrases.some((phrase) => entryTerms.has(phrase)) ? 0.9 : intent.phrases.some((phrase) => [...entryTerms].some((term) => term.includes(phrase) || phrase.includes(term))) ? 0.55 : 0;
792
+ return Math.min(1, Math.max(phraseCoverage, originalCoverage * 0.78 + expandedCoverage * 0.22));
793
+ }
794
+ function originalTokenCoverage(entry, intent) {
795
+ if (intent.tokens.length === 0) {
796
+ return 0;
797
+ }
798
+ const entryTerms = /* @__PURE__ */ new Set([entry.normalizedName, ...entry.aliases, ...entry.keywords]);
799
+ const searchableText = [
800
+ entry.stableId,
801
+ entry.displayName,
802
+ entry.filePath ?? "",
803
+ entry.folderPath ?? "",
804
+ entry.parentChain.join(" "),
805
+ entry.relationships.neighborNames.join(" "),
806
+ entry.documentation ?? "",
807
+ entry.comments ?? ""
808
+ ].join(" ").toLowerCase();
809
+ const matches = intent.tokens.filter(
810
+ (token) => entryTerms.has(token) || entryTerms.has(singularize(token)) || searchableText.includes(token)
811
+ );
812
+ return matches.length / intent.tokens.length;
813
+ }
814
+ function originalQueryCoverage(entry, intent) {
815
+ const required = uniqueStrings(intent.tokens.map((token) => singularize(token)).filter((token) => token.length > 2));
816
+ if (required.length === 0) {
817
+ return { matched: [], missing: [], ratio: 1 };
818
+ }
819
+ const entryTerms = new Set([entry.normalizedName, ...entry.aliases, ...entry.keywords].flatMap((term) => tokenize(term).map(singularize)));
820
+ const searchableTerms = new Set(tokenize([
821
+ entry.stableId,
822
+ entry.displayName,
823
+ entry.filePath ?? "",
824
+ entry.folderPath ?? "",
825
+ entry.parentChain.join(" "),
826
+ entry.relationships.neighborNames.join(" "),
827
+ entry.documentation ?? "",
828
+ entry.comments ?? ""
829
+ ].join(" ")).map(singularize));
830
+ const matched = required.filter((term) => entryTerms.has(term) || searchableTerms.has(term));
831
+ const missing = required.filter((term) => !matched.includes(term));
832
+ return { matched, missing, ratio: matched.length / required.length };
833
+ }
834
+ function semanticFeatureAgreement(entry, intent) {
835
+ if (intent.tokens.length === 0) {
836
+ return 0;
585
837
  }
586
- if (/(^|[/.])(test|tests|spec|__tests__)([/.]|$)|\.(test|spec)\./i.test(entry.filePath ?? "")) {
838
+ const featureText = tokenize([
839
+ entry.stableId,
840
+ entry.displayName,
841
+ entry.filePath ?? "",
842
+ entry.folderPath ?? "",
843
+ entry.parentChain.join(" "),
844
+ entry.relationships.neighborNames.join(" "),
845
+ entry.documentation ?? "",
846
+ entry.comments ?? ""
847
+ ].join(" "));
848
+ const featureTerms = new Set(featureText);
849
+ const original = intent.tokens.filter((token) => featureTerms.has(token) || featureTerms.has(singularize(token))).length / intent.tokens.length;
850
+ const expanded = intent.expandedTerms.length === 0 ? 0 : intent.expandedTerms.filter((term) => featureTerms.has(term)).length / intent.expandedTerms.length;
851
+ return Math.min(1, original * 0.82 + expanded * 0.18);
852
+ }
853
+ function semanticFeaturePenalty(entry, intent) {
854
+ const required = new Set(intent.tokens.map(singularize));
855
+ const terms = new Set(tokenize([
856
+ entry.stableId,
857
+ entry.displayName,
858
+ entry.filePath ?? "",
859
+ entry.folderPath ?? "",
860
+ entry.parentChain.join(" "),
861
+ entry.relationships.neighborNames.join(" "),
862
+ entry.documentation ?? "",
863
+ entry.comments ?? ""
864
+ ].join(" ")).map(singularize));
865
+ let penalty = 0;
866
+ if (required.has("threshold") && !terms.has("threshold")) {
867
+ penalty += 130;
868
+ }
869
+ if (required.has("threshold") && (terms.has("statistic") || terms.has("observation")) && !terms.has("threshold")) {
870
+ penalty += 220;
871
+ }
872
+ if (required.has("duration") && required.has("sleep") && !terms.has("duration")) {
873
+ penalty += 80;
874
+ }
875
+ return penalty;
876
+ }
877
+ function repositoryLocalityConfidence(entry) {
878
+ if (!isRepositoryLocalEntry(entry)) {
879
+ return 0.08;
880
+ }
881
+ if (isGenericUtilityEntry(entry)) {
882
+ return 0.72;
883
+ }
884
+ if (!entry.filePath && (entry.kind === "Dependency" || entry.kind === "Framework")) {
885
+ return 0.2;
886
+ }
887
+ return 1;
888
+ }
889
+ function architectureAgreementConfidence(entry, category) {
890
+ if (isExternalOrFrameworkEntry(entry)) {
891
+ return 0.12;
892
+ }
893
+ if (isPreferredRepositorySeedEntry(entry)) {
894
+ return 1;
895
+ }
896
+ if (categoryTypeSet(category)?.has(entry.kind)) {
897
+ return 0.82;
898
+ }
899
+ if (entry.architectureLayer === "architecture" && category === "feature") {
900
+ return 0.4;
901
+ }
902
+ return entry.architectureLayer === "language" ? 0.55 : 0.65;
903
+ }
904
+ function graphConnectivityConfidence(entry) {
905
+ return Math.min(1, 0.25 + Math.log2(entry.relationships.degree + 1) / 4);
906
+ }
907
+ function repositoryLocalityBoost(entry) {
908
+ let score = 0;
909
+ if (isRepositoryLocalEntry(entry)) {
910
+ score += 56;
911
+ score += REPOSITORY_LOCAL_KIND_BOOSTS[entry.kind] ?? 0;
912
+ }
913
+ if (isDtoLikeEntry(entry)) {
914
+ score += 46;
915
+ }
916
+ if (isMapperEntry(entry)) {
917
+ score += 34;
918
+ }
919
+ if (isFeatureModuleEntry(entry)) {
587
920
  score += 22;
588
921
  }
922
+ if (isTestEntry(entry)) {
923
+ score += 28;
924
+ }
589
925
  return score;
590
926
  }
591
927
  function repositoryRankingPenalty(entry) {
592
928
  let penalty = 0;
593
929
  const text = `${entry.stableId} ${entry.filePath ?? ""} ${entry.package ?? ""} ${entry.displayName}`.toLowerCase();
594
930
  if (text.includes("node_modules") || entry.stableId.startsWith("dep:")) {
595
- penalty += 120;
931
+ penalty += 260;
596
932
  }
597
933
  if (/(\bdto\b|dto$|schema$|type$|types$|generated|__generated__)/i.test(entry.displayName) && !isRepositoryLocalEntry(entry)) {
598
- penalty += 35;
934
+ penalty += 90;
599
935
  }
600
- if (/node_modules|@nestjs\/|next\/dist|react\/|typescript\/lib/i.test(text)) {
601
- penalty += 80;
936
+ if (/node_modules|@nestjs\/|@medplum\/|next\/dist|react\/|typescript\/lib|@types\//i.test(text)) {
937
+ penalty += 180;
938
+ }
939
+ if (isExternalOrFrameworkEntry(entry)) {
940
+ penalty += 120;
602
941
  }
603
942
  if (/^framework:|framework|adapter|platform|runtime/.test(entry.stableId.toLowerCase()) && !isRepositoryLocalEntry(entry)) {
604
- penalty += 55;
943
+ penalty += 90;
605
944
  }
606
945
  if (isGenericUtilityEntry(entry)) {
607
- penalty += 35;
946
+ penalty += 55;
608
947
  }
609
948
  return penalty;
610
949
  }
611
950
  function repositoryLocalityEvidence(entry) {
612
951
  return isRepositoryLocalEntry(entry) ? `${entry.kind} from repository-local graph evidence` : `${entry.kind} from external or generated boundary`;
613
952
  }
614
- function repositoryNoiseEvidence(entry) {
615
- return `${entry.displayName} is external, generated, or generic framework-adjacent evidence`;
953
+ function repositoryNoiseEvidence(entry, missingTerms = []) {
954
+ const missing = missingTerms.length > 0 ? `; missing query term(s): ${missingTerms.join(", ")}` : "";
955
+ return `${entry.displayName} is external, generated, generic framework-adjacent, or lower-coverage evidence${missing}`;
616
956
  }
617
957
  function isRepositoryLocalEntry(entry) {
618
958
  const text = `${entry.stableId} ${entry.filePath ?? ""} ${entry.package ?? ""}`.toLowerCase();
619
- return !text.includes("node_modules") && !entry.stableId.startsWith("dep:") && !entry.stableId.startsWith("framework:");
620
- }
621
- function isRepositorySymbolEntry(entry) {
622
- return ["Function", "Method", "Class", "Interface", "TypeAlias", "Enum", "Field", "Model", "Resource"].includes(entry.kind);
959
+ return !text.includes("node_modules") && !entry.stableId.startsWith("dep:") && !entry.stableId.startsWith("framework:") && !isKnownExternalPackageEntry(entry);
623
960
  }
624
961
  function isDtoLikeEntry(entry) {
625
962
  const text = `${entry.displayName} ${entry.filePath ?? ""} ${entry.keywords.join(" ")}`;
626
963
  return ["Model", "Interface", "TypeAlias", "Class"].includes(entry.kind) && /\bdto\b|dto$|data transfer|payload|request|response/i.test(text);
627
964
  }
965
+ function featureOwnerBoostFor(entry, category) {
966
+ if (category !== "feature" && category !== "concept" || !isRepositoryLocalEntry(entry)) {
967
+ return 0;
968
+ }
969
+ if (entry.kind === "Service" || entry.kind === "Provider") {
970
+ return 170;
971
+ }
972
+ if (entry.kind === "Class" && /service$/i.test(entry.displayName)) {
973
+ return 170;
974
+ }
975
+ if (entry.kind === "Controller") {
976
+ return 90;
977
+ }
978
+ return 0;
979
+ }
628
980
  function isFeatureModuleEntry(entry) {
629
981
  return entry.kind === "Module" && isRepositoryLocalEntry(entry) && !isGenericUtilityEntry(entry);
630
982
  }
983
+ function isPreferredRepositorySeedEntry(entry) {
984
+ return isRepositoryLocalEntry(entry) && (["Service", "Controller", "Repository", "Method", "Route", "Operation", "Configuration", "EnvironmentVariable", "Guard", "Permission", "Middleware"].includes(entry.kind) || isDtoLikeEntry(entry) || isFeatureModuleEntry(entry) || isTestEntry(entry) || isMapperEntry(entry));
985
+ }
986
+ function isMapperEntry(entry) {
987
+ return isRepositoryLocalEntry(entry) && /\bmapper\b|mapper$/i.test(`${entry.displayName} ${entry.filePath ?? ""}`);
988
+ }
989
+ function isTestEntry(entry) {
990
+ return /(^|[/.])(test|tests|spec|__tests__)([/.]|$)|\.(test|spec)\./i.test(`${entry.filePath ?? ""} ${entry.displayName}`);
991
+ }
992
+ function isExternalOrFrameworkEntry(entry) {
993
+ return !isRepositoryLocalEntry(entry) || entry.kind === "Dependency" || entry.kind === "Framework" || isKnownExternalPackageEntry(entry);
994
+ }
995
+ function isKnownExternalPackageEntry(entry) {
996
+ const text = `${entry.stableId} ${entry.displayName} ${entry.package ?? ""} ${entry.filePath ?? ""}`.toLowerCase();
997
+ return FRAMEWORK_PACKAGE_PATTERN.test(text) || /(^|[/:])(@nestjs|@medplum|@types|@angular|next|react|typescript|express|fastify)([/:-]|$)/.test(text) || /(^|[/:])(node|npm):/.test(text);
998
+ }
631
999
  function isGenericUtilityEntry(entry) {
632
1000
  const text = `${entry.stableId} ${entry.displayName} ${entry.filePath ?? ""}`.toLowerCase();
633
1001
  return /(^|[/.:-])(common|shared|utils?|helpers?|internal|misc|core|index)([/.:-]|$)/.test(text) || /\b(base|abstract|generic|utility|utils?|helpers?)\b/.test(entry.displayName);
@@ -900,7 +1268,20 @@ function collectMetadataText(value, values, depth, seen) {
900
1268
  }
901
1269
  }
902
1270
  function isNoisyMetadataKey(key) {
903
- return ["language", "parser", "parserVersion", "passId", "provenance", "source"].includes(key);
1271
+ return [
1272
+ "abstract",
1273
+ "async",
1274
+ "declarationKind",
1275
+ "language",
1276
+ "parameters",
1277
+ "parser",
1278
+ "parserVersion",
1279
+ "passId",
1280
+ "provenance",
1281
+ "source",
1282
+ "static",
1283
+ "typeParameters"
1284
+ ].includes(key);
904
1285
  }
905
1286
  function routeTextValues(node) {
906
1287
  if (node.type !== "Route" && node.type !== "Operation") {
@@ -962,10 +1343,13 @@ function groupEdges(edges, side) {
962
1343
  return new Map([...grouped.entries()].map(([key, values]) => [key, values.sort(compareEdges)]));
963
1344
  }
964
1345
  function addInverted(map, term, id) {
965
- if (!term || STOP_WORDS.has(term)) {
1346
+ if (!term || STOP_WORDS.has(term) || isSemanticNoiseTerm(term) || isNoisySemanticAlias(term)) {
966
1347
  return;
967
1348
  }
968
1349
  const current = map.get(term) ?? /* @__PURE__ */ new Set();
1350
+ if (current.size >= SEMANTIC_INVERTED_IDS_LIMIT) {
1351
+ return;
1352
+ }
969
1353
  current.add(id);
970
1354
  map.set(term, current);
971
1355
  }
@@ -990,6 +1374,19 @@ function compareEdges(left, right) {
990
1374
  function uniqueStrings(values) {
991
1375
  return [...new Set(values.map((value) => value.trim()).filter(Boolean))].sort((left, right) => left.localeCompare(right));
992
1376
  }
1377
+ function isSemanticNoiseTerm(term) {
1378
+ return SEMANTIC_NOISE_TERMS.has(term) || /^\d+[a-z]*$/i.test(term);
1379
+ }
1380
+ function isNoisySemanticAlias(alias) {
1381
+ const tokens = tokenize(alias);
1382
+ if (tokens.length === 0) {
1383
+ return true;
1384
+ }
1385
+ if (tokens.length > 14) {
1386
+ return true;
1387
+ }
1388
+ return tokens.every(isSemanticNoiseTerm);
1389
+ }
993
1390
  function sum(values) {
994
1391
  return values.reduce((total, value) => total + value, 0);
995
1392
  }