opencode-codebase-index 0.19.1 → 0.20.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +7 -5
- package/dist/cli.cjs +765 -124
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +765 -124
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +400 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +400 -55
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +361 -48
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +361 -48
- package/dist/pi-extension.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +4 -3
package/dist/cli.cjs
CHANGED
|
@@ -793,6 +793,7 @@ function getDefaultIndexingConfig() {
|
|
|
793
793
|
autoIndexMaxRetries: 5,
|
|
794
794
|
autoIndexRetryDelayMs: 100,
|
|
795
795
|
watchFiles: true,
|
|
796
|
+
pauseBackgroundIndexingOnBattery: false,
|
|
796
797
|
maxFileSize: 1048576,
|
|
797
798
|
maxChunksPerFile: 100,
|
|
798
799
|
semanticOnly: false,
|
|
@@ -924,6 +925,7 @@ function parseConfig(raw) {
|
|
|
924
925
|
autoIndexMaxRetries: typeof rawIndexing.autoIndexMaxRetries === "number" ? Math.min(10, Math.max(0, Math.floor(rawIndexing.autoIndexMaxRetries))) : defaultIndexing.autoIndexMaxRetries,
|
|
925
926
|
autoIndexRetryDelayMs: typeof rawIndexing.autoIndexRetryDelayMs === "number" ? Math.min(1e4, Math.max(10, Math.floor(rawIndexing.autoIndexRetryDelayMs))) : defaultIndexing.autoIndexRetryDelayMs,
|
|
926
927
|
watchFiles: typeof rawIndexing.watchFiles === "boolean" ? rawIndexing.watchFiles : defaultIndexing.watchFiles,
|
|
928
|
+
pauseBackgroundIndexingOnBattery: typeof rawIndexing.pauseBackgroundIndexingOnBattery === "boolean" ? rawIndexing.pauseBackgroundIndexingOnBattery : defaultIndexing.pauseBackgroundIndexingOnBattery,
|
|
927
929
|
maxFileSize: typeof rawIndexing.maxFileSize === "number" ? rawIndexing.maxFileSize : defaultIndexing.maxFileSize,
|
|
928
930
|
maxChunksPerFile: typeof rawIndexing.maxChunksPerFile === "number" ? Math.max(1, rawIndexing.maxChunksPerFile) : defaultIndexing.maxChunksPerFile,
|
|
929
931
|
semanticOnly: typeof rawIndexing.semanticOnly === "boolean" ? rawIndexing.semanticOnly : defaultIndexing.semanticOnly,
|
|
@@ -1097,6 +1099,18 @@ function metricDelta(current, baseline) {
|
|
|
1097
1099
|
};
|
|
1098
1100
|
}
|
|
1099
1101
|
function compareSummaries(current, baseline, againstPath) {
|
|
1102
|
+
const hasCurrentFingerprint = current.datasetFingerprint !== void 0;
|
|
1103
|
+
const hasBaselineFingerprint = baseline.datasetFingerprint !== void 0;
|
|
1104
|
+
if (hasCurrentFingerprint !== hasBaselineFingerprint) {
|
|
1105
|
+
throw new Error(
|
|
1106
|
+
`Cannot compare evaluation summaries with mismatched dataset fingerprint presence: current=${hasCurrentFingerprint ? "present" : "missing"}, baseline=${hasBaselineFingerprint ? "present" : "missing"} at ${againstPath}`
|
|
1107
|
+
);
|
|
1108
|
+
}
|
|
1109
|
+
if (hasCurrentFingerprint && hasBaselineFingerprint && current.datasetFingerprint !== baseline.datasetFingerprint) {
|
|
1110
|
+
throw new Error(
|
|
1111
|
+
`Cannot compare incompatible evaluation datasets by fingerprint: current=${current.datasetFingerprint}, baseline=${baseline.datasetFingerprint} at ${againstPath}`
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1100
1114
|
if (current.datasetName !== baseline.datasetName || current.datasetVersion !== baseline.datasetVersion || current.queryCount !== baseline.queryCount) {
|
|
1101
1115
|
throw new Error(
|
|
1102
1116
|
`Cannot compare incompatible evaluation datasets: current=${current.datasetName}@${current.datasetVersion} (${current.queryCount} queries), baseline=${baseline.datasetName}@${baseline.datasetVersion} (${baseline.queryCount} queries) at ${againstPath}`
|
|
@@ -1400,6 +1414,7 @@ function buildPerQueryArtifact(perQuery) {
|
|
|
1400
1414
|
}
|
|
1401
1415
|
|
|
1402
1416
|
// src/eval/runner.ts
|
|
1417
|
+
var crypto = __toESM(require("crypto"), 1);
|
|
1403
1418
|
var import_fs17 = require("fs");
|
|
1404
1419
|
var path21 = __toESM(require("path"), 1);
|
|
1405
1420
|
var import_perf_hooks2 = require("perf_hooks");
|
|
@@ -4037,9 +4052,21 @@ function parseFiles(files) {
|
|
|
4037
4052
|
return result.map((f) => ({
|
|
4038
4053
|
path: f.path,
|
|
4039
4054
|
chunks: f.chunks.map(mapChunk),
|
|
4055
|
+
symbols: (f.symbols ?? []).map(mapParsedSymbol),
|
|
4040
4056
|
hash: f.hash
|
|
4041
4057
|
}));
|
|
4042
4058
|
}
|
|
4059
|
+
function mapParsedSymbol(symbol) {
|
|
4060
|
+
return {
|
|
4061
|
+
name: symbol.name,
|
|
4062
|
+
kind: symbol.kind,
|
|
4063
|
+
startLine: symbol.startLine ?? symbol.start_line,
|
|
4064
|
+
startCol: symbol.startCol ?? symbol.start_col,
|
|
4065
|
+
endLine: symbol.endLine ?? symbol.end_line,
|
|
4066
|
+
endCol: symbol.endCol ?? symbol.end_col,
|
|
4067
|
+
language: symbol.language
|
|
4068
|
+
};
|
|
4069
|
+
}
|
|
4043
4070
|
function mapChunk(c) {
|
|
4044
4071
|
return {
|
|
4045
4072
|
content: c.content,
|
|
@@ -6850,6 +6877,7 @@ var INDEX_METADATA_VERSION = "1";
|
|
|
6850
6877
|
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
6851
6878
|
var SWIFT_PARSER_VERSION = "1";
|
|
6852
6879
|
var METAL_PARSER_VERSION = "1";
|
|
6880
|
+
var SYMBOL_EXTRACTOR_VERSION = "1";
|
|
6853
6881
|
var RANKING_TOKEN_CACHE_LIMIT = 4096;
|
|
6854
6882
|
var RANK_HYBRID_CACHE_LIMIT = 256;
|
|
6855
6883
|
function createPendingChunkStorageText(texts) {
|
|
@@ -7151,7 +7179,7 @@ function classifyQueryIntentRaw(query) {
|
|
|
7151
7179
|
return "neutral";
|
|
7152
7180
|
}
|
|
7153
7181
|
function isImplementationChunkType(chunkType) {
|
|
7154
|
-
return [
|
|
7182
|
+
return CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(chunkType) || [
|
|
7155
7183
|
"export_statement",
|
|
7156
7184
|
"function",
|
|
7157
7185
|
"function_declaration",
|
|
@@ -7596,7 +7624,7 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
|
|
|
7596
7624
|
const remainder = combined.filter((candidate) => !promotedIds.has(candidate.id));
|
|
7597
7625
|
return [...promoted, ...remainder];
|
|
7598
7626
|
}
|
|
7599
|
-
function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
7627
|
+
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
7600
7628
|
if (!prioritizeSourcePaths) {
|
|
7601
7629
|
return [];
|
|
7602
7630
|
}
|
|
@@ -7610,14 +7638,14 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7610
7638
|
const primaryHint = extractPrimaryIdentifierQueryHint(query);
|
|
7611
7639
|
const upsertChunkCandidate = (chunk, identifier, normalizedIdentifier, baseScore) => {
|
|
7612
7640
|
if (branchChunkIds && !branchChunkIds.has(chunk.chunkId)) {
|
|
7613
|
-
return;
|
|
7641
|
+
return false;
|
|
7614
7642
|
}
|
|
7615
7643
|
const chunkType = chunk.nodeType ?? "other";
|
|
7616
7644
|
if (!isImplementationChunkType(chunkType)) {
|
|
7617
|
-
return;
|
|
7645
|
+
return false;
|
|
7618
7646
|
}
|
|
7619
7647
|
if (!isLikelyImplementationPath2(chunk.filePath)) {
|
|
7620
|
-
return;
|
|
7648
|
+
return false;
|
|
7621
7649
|
}
|
|
7622
7650
|
const nameLower = (chunk.name ?? "").toLowerCase();
|
|
7623
7651
|
const exactName = nameLower === identifier || nameLower.replace(/_/g, "") === normalizedIdentifier;
|
|
@@ -7639,6 +7667,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7639
7667
|
}
|
|
7640
7668
|
});
|
|
7641
7669
|
}
|
|
7670
|
+
return true;
|
|
7642
7671
|
};
|
|
7643
7672
|
const normalizedHints = identifierHints.flatMap((hint) => [
|
|
7644
7673
|
hint,
|
|
@@ -7660,12 +7689,46 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7660
7689
|
dedupSymbols.set(symbol.id, symbol);
|
|
7661
7690
|
}
|
|
7662
7691
|
for (const symbol of dedupSymbols.values()) {
|
|
7692
|
+
if (branchSymbolIds && !branchSymbolIds.has(symbol.id)) {
|
|
7693
|
+
continue;
|
|
7694
|
+
}
|
|
7695
|
+
if (filePathHint && !pathMatchesHint(symbol.filePath, filePathHint)) {
|
|
7696
|
+
continue;
|
|
7697
|
+
}
|
|
7663
7698
|
const chunks = database.getChunksByFile(symbol.filePath);
|
|
7699
|
+
let foundCoveringChunk = false;
|
|
7664
7700
|
for (const chunk of chunks) {
|
|
7665
7701
|
if (chunk.startLine > symbol.startLine || chunk.endLine < symbol.endLine) {
|
|
7666
7702
|
continue;
|
|
7667
7703
|
}
|
|
7668
|
-
|
|
7704
|
+
const chunkName = (chunk.name ?? "").toLowerCase();
|
|
7705
|
+
const symbolName2 = symbol.name.toLowerCase();
|
|
7706
|
+
if (chunkName !== symbolName2 && chunkName.replace(/_/g, "") !== symbolName2.replace(/_/g, "")) {
|
|
7707
|
+
continue;
|
|
7708
|
+
}
|
|
7709
|
+
foundCoveringChunk = upsertChunkCandidate(chunk, identifier, normalizedIdentifier) || foundCoveringChunk;
|
|
7710
|
+
}
|
|
7711
|
+
if (foundCoveringChunk || !isLikelyImplementationPath2(symbol.filePath)) {
|
|
7712
|
+
continue;
|
|
7713
|
+
}
|
|
7714
|
+
const symbolName = symbol.name.toLowerCase();
|
|
7715
|
+
const exactName = symbolName === identifier || symbolName.replace(/_/g, "") === normalizedIdentifier;
|
|
7716
|
+
const score = exactName ? 0.99 : 0.88;
|
|
7717
|
+
const existing = symbolCandidates.get(symbol.id);
|
|
7718
|
+
if (!existing || score > existing.score) {
|
|
7719
|
+
symbolCandidates.set(symbol.id, {
|
|
7720
|
+
id: symbol.id,
|
|
7721
|
+
score,
|
|
7722
|
+
metadata: {
|
|
7723
|
+
filePath: symbol.filePath,
|
|
7724
|
+
startLine: symbol.startLine,
|
|
7725
|
+
endLine: symbol.endLine,
|
|
7726
|
+
chunkType: symbol.kind,
|
|
7727
|
+
name: symbol.name,
|
|
7728
|
+
language: symbol.language,
|
|
7729
|
+
hash: symbol.id
|
|
7730
|
+
}
|
|
7731
|
+
});
|
|
7669
7732
|
}
|
|
7670
7733
|
}
|
|
7671
7734
|
const dedupChunksByName = /* @__PURE__ */ new Map();
|
|
@@ -7673,6 +7736,9 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7673
7736
|
dedupChunksByName.set(chunk.chunkId, chunk);
|
|
7674
7737
|
}
|
|
7675
7738
|
for (const chunk of dedupChunksByName.values()) {
|
|
7739
|
+
if (filePathHint && !pathMatchesHint(chunk.filePath, filePathHint)) {
|
|
7740
|
+
continue;
|
|
7741
|
+
}
|
|
7676
7742
|
upsertChunkCandidate(chunk, identifier, normalizedIdentifier);
|
|
7677
7743
|
}
|
|
7678
7744
|
}
|
|
@@ -8214,6 +8280,10 @@ var Indexer = class _Indexer {
|
|
|
8214
8280
|
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
8215
8281
|
return `${key}.${projectHash}`;
|
|
8216
8282
|
}
|
|
8283
|
+
getSymbolExtractorVersionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8284
|
+
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
8285
|
+
return `index.symbolExtractorVersion.${hashContent(branchKey).slice(0, 24)}`;
|
|
8286
|
+
}
|
|
8217
8287
|
hasProjectForceReembedPending() {
|
|
8218
8288
|
return this.config.scope === "global" && this.database?.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
8219
8289
|
}
|
|
@@ -9557,7 +9627,8 @@ var Indexer = class _Indexer {
|
|
|
9557
9627
|
}
|
|
9558
9628
|
const branchKey = this.getBranchCatalogKey();
|
|
9559
9629
|
const alreadyIndexed = database.getBranchChunkIds(branchKey).length > 0 && database.getBranchSymbolIds(branchKey).length > 0;
|
|
9560
|
-
|
|
9630
|
+
const symbolsCurrent = database.getMetadata(this.getSymbolExtractorVersionMetadataKey()) === SYMBOL_EXTRACTOR_VERSION;
|
|
9631
|
+
if (alreadyIndexed && symbolsCurrent && this.getStoredBranchCommit(database) === normalizedCommit) {
|
|
9561
9632
|
return { prepared: false };
|
|
9562
9633
|
}
|
|
9563
9634
|
const stats = await this.indexUnlocked(onProgress, [], true);
|
|
@@ -9616,6 +9687,8 @@ var Indexer = class _Indexer {
|
|
|
9616
9687
|
const reparseCachedSwiftFiles = database.getMetadata(swiftParserMetadataKey) !== SWIFT_PARSER_VERSION;
|
|
9617
9688
|
const metalParserMetadataKey = this.getMetalParserVersionMetadataKey();
|
|
9618
9689
|
const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
|
|
9690
|
+
const symbolExtractorMetadataKey = this.getSymbolExtractorVersionMetadataKey();
|
|
9691
|
+
const refreshCachedSymbols = database.getMetadata(symbolExtractorMetadataKey) !== SYMBOL_EXTRACTOR_VERSION;
|
|
9619
9692
|
if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some(
|
|
9620
9693
|
(filePath) => path13.extname(filePath).toLowerCase() === ".swift"
|
|
9621
9694
|
)) {
|
|
@@ -9659,7 +9732,7 @@ var Indexer = class _Indexer {
|
|
|
9659
9732
|
);
|
|
9660
9733
|
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path13.extname(canonicalPath).toLowerCase() === ".swift";
|
|
9661
9734
|
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path13.extname(canonicalPath).toLowerCase() === ".metal";
|
|
9662
|
-
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade) {
|
|
9735
|
+
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
9663
9736
|
unchangedFilePaths.add(canonicalPath);
|
|
9664
9737
|
this.logger.recordCacheHit();
|
|
9665
9738
|
} else {
|
|
@@ -9870,37 +9943,27 @@ var Indexer = class _Indexer {
|
|
|
9870
9943
|
const parsed = parsedFiles[i];
|
|
9871
9944
|
const changedFile = changedFiles[i];
|
|
9872
9945
|
const fileSymbols = [];
|
|
9873
|
-
for (const
|
|
9874
|
-
if (!
|
|
9875
|
-
const existingMetalSymbol = chunk.language === "metal" ? fileSymbols.find(
|
|
9876
|
-
(symbol2) => symbol2.name === chunk.name && symbol2.kind === chunk.chunkType && symbol2.startLine <= chunk.endLine && chunk.startLine <= symbol2.endLine
|
|
9877
|
-
) : void 0;
|
|
9878
|
-
if (existingMetalSymbol) {
|
|
9879
|
-
existingMetalSymbol.startLine = Math.min(existingMetalSymbol.startLine, chunk.startLine);
|
|
9880
|
-
existingMetalSymbol.endLine = Math.max(existingMetalSymbol.endLine, chunk.endLine);
|
|
9881
|
-
existingMetalSymbol.startCol = Math.min(existingMetalSymbol.startCol, chunk.startCol ?? 0);
|
|
9882
|
-
existingMetalSymbol.endCol = Math.max(existingMetalSymbol.endCol, chunk.endCol ?? 0);
|
|
9883
|
-
continue;
|
|
9884
|
-
}
|
|
9946
|
+
for (const parsedSymbol of parsed.symbols) {
|
|
9947
|
+
if (!CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(parsedSymbol.kind)) continue;
|
|
9885
9948
|
const preparedNamespace = this.getPreparedBranchNamespace();
|
|
9886
9949
|
const symbolId = `sym_${hashContent(
|
|
9887
|
-
(preparedNamespace ? `${preparedNamespace}:` : "") + parsed.path + ":" +
|
|
9950
|
+
(preparedNamespace ? `${preparedNamespace}:` : "") + parsed.path + ":" + parsedSymbol.name + ":" + parsedSymbol.kind + ":" + parsedSymbol.startLine + ":" + parsedSymbol.startCol + ":" + changedFile.hash
|
|
9888
9951
|
).slice(0, 16)}`;
|
|
9889
9952
|
const symbol = {
|
|
9890
9953
|
id: symbolId,
|
|
9891
9954
|
filePath: parsed.path,
|
|
9892
|
-
name:
|
|
9893
|
-
kind:
|
|
9894
|
-
startLine:
|
|
9895
|
-
startCol:
|
|
9896
|
-
endLine:
|
|
9897
|
-
endCol:
|
|
9898
|
-
language:
|
|
9955
|
+
name: parsedSymbol.name,
|
|
9956
|
+
kind: parsedSymbol.kind,
|
|
9957
|
+
startLine: parsedSymbol.startLine,
|
|
9958
|
+
startCol: parsedSymbol.startCol,
|
|
9959
|
+
endLine: parsedSymbol.endLine,
|
|
9960
|
+
endCol: parsedSymbol.endCol,
|
|
9961
|
+
language: parsedSymbol.language
|
|
9899
9962
|
};
|
|
9900
9963
|
fileSymbols.push(symbol);
|
|
9901
9964
|
allSymbolIds.add(symbolId);
|
|
9902
9965
|
}
|
|
9903
|
-
const fileLanguage = parsed.chunks[0]?.language;
|
|
9966
|
+
const fileLanguage = parsed.symbols[0]?.language ?? parsed.chunks[0]?.language;
|
|
9904
9967
|
const isCaseInsensitiveLanguage = !!fileLanguage && CASE_INSENSITIVE_LANGUAGES.has(fileLanguage);
|
|
9905
9968
|
const normalizeSymbolKey = (name) => isCaseInsensitiveLanguage ? name.toLowerCase() : name;
|
|
9906
9969
|
const symbolsByName = /* @__PURE__ */ new Map();
|
|
@@ -10016,6 +10079,7 @@ var Indexer = class _Indexer {
|
|
|
10016
10079
|
}
|
|
10017
10080
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
10018
10081
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
10082
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
10019
10083
|
this.saveBranchCommit(database, indexedCommit);
|
|
10020
10084
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
10021
10085
|
this.indexCompatibility = { compatible: true };
|
|
@@ -10052,6 +10116,7 @@ var Indexer = class _Indexer {
|
|
|
10052
10116
|
}
|
|
10053
10117
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
10054
10118
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
10119
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
10055
10120
|
this.saveBranchCommit(database, indexedCommit);
|
|
10056
10121
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
10057
10122
|
this.indexCompatibility = { compatible: true };
|
|
@@ -10331,6 +10396,7 @@ var Indexer = class _Indexer {
|
|
|
10331
10396
|
}
|
|
10332
10397
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
10333
10398
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
10399
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
10334
10400
|
this.saveBranchCommit(database, indexedCommit);
|
|
10335
10401
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
10336
10402
|
this.indexCompatibility = { compatible: true };
|
|
@@ -10469,10 +10535,11 @@ var Indexer = class _Indexer {
|
|
|
10469
10535
|
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
10470
10536
|
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
10471
10537
|
let branchChunkIds = null;
|
|
10538
|
+
let branchSymbolIds = null;
|
|
10472
10539
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
);
|
|
10540
|
+
const branchCatalogKeys = this.getBranchCatalogKeys();
|
|
10541
|
+
branchChunkIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchChunkIds(branchKey)));
|
|
10542
|
+
branchSymbolIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchSymbolIds(branchKey)));
|
|
10476
10543
|
}
|
|
10477
10544
|
const prefilterStartTime = import_perf_hooks.performance.now();
|
|
10478
10545
|
const shouldPrefilterByBranch = branchChunkIds !== null && (this.config.scope === "global" || branchChunkIds.size > 0);
|
|
@@ -10544,6 +10611,7 @@ var Indexer = class _Indexer {
|
|
|
10544
10611
|
query,
|
|
10545
10612
|
database,
|
|
10546
10613
|
branchChunkIds,
|
|
10614
|
+
branchSymbolIds,
|
|
10547
10615
|
maxResults,
|
|
10548
10616
|
union,
|
|
10549
10617
|
sourceIntent
|
|
@@ -10557,7 +10625,7 @@ var Indexer = class _Indexer {
|
|
|
10557
10625
|
(r) => isLikelyImplementationPath2(r.metadata.filePath) && isImplementationChunkType(r.metadata.chunkType)
|
|
10558
10626
|
);
|
|
10559
10627
|
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly : baseFiltered).slice(0, maxResults);
|
|
10560
|
-
const identifierFallback = !options?.definitionIntent && filtered.length === 0 && identifierHints.length > 0 ? buildSymbolDefinitionLane(query, database, branchChunkIds, maxResults, union, true).filter((r) => matchesSearchFilters(r, options, this.config.search.minScore)).slice(0, maxResults) : [];
|
|
10628
|
+
const identifierFallback = !options?.definitionIntent && filtered.length === 0 && identifierHints.length > 0 ? buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, maxResults, union, true).filter((r) => matchesSearchFilters(r, options, this.config.search.minScore)).slice(0, maxResults) : [];
|
|
10561
10629
|
const finalResults = filtered.length > 0 ? filtered : identifierFallback;
|
|
10562
10630
|
const totalSearchMs = import_perf_hooks.performance.now() - searchStartTime;
|
|
10563
10631
|
this.logger.recordSearch(totalSearchMs, {
|
|
@@ -10714,7 +10782,7 @@ var Indexer = class _Indexer {
|
|
|
10714
10782
|
const extension = path13.extname(filePath).toLowerCase();
|
|
10715
10783
|
return extension === ".php" || extension === ".c" || extension === ".cc" || extension === ".cpp" || extension === ".cxx";
|
|
10716
10784
|
});
|
|
10717
|
-
if (hasSwiftFiles && database.getMetadata(this.getSwiftParserVersionMetadataKey()) !== SWIFT_PARSER_VERSION || hasMetalFiles && database.getMetadata(this.getMetalParserVersionMetadataKey()) !== METAL_PARSER_VERSION || hasCallGraphMigrationFiles && database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION) {
|
|
10785
|
+
if (hasSwiftFiles && database.getMetadata(this.getSwiftParserVersionMetadataKey()) !== SWIFT_PARSER_VERSION || hasMetalFiles && database.getMetadata(this.getMetalParserVersionMetadataKey()) !== METAL_PARSER_VERSION || database.getMetadata(this.getSymbolExtractorVersionMetadataKey()) !== SYMBOL_EXTRACTOR_VERSION || hasCallGraphMigrationFiles && database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION) {
|
|
10718
10786
|
return { readable: true, current: false, reason: "migration-required" };
|
|
10719
10787
|
}
|
|
10720
10788
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -11422,7 +11490,10 @@ var Indexer = class _Indexer {
|
|
|
11422
11490
|
const requestedRef = opts.pr !== void 0 ? expectedCommit : headRefName ?? resolvedBranch;
|
|
11423
11491
|
const storedCommit = this.getStoredBranchCommit(database, catalogIdentity);
|
|
11424
11492
|
const catalogIdentityMatches = storedCommit === expectedCommit;
|
|
11425
|
-
|
|
11493
|
+
const symbolsCurrent = database.getMetadata(
|
|
11494
|
+
this.getSymbolExtractorVersionMetadataKey(catalogIdentity)
|
|
11495
|
+
) === SYMBOL_EXTRACTOR_VERSION;
|
|
11496
|
+
if (branchSymbols.length === 0 || !catalogIdentityMatches || !symbolsCurrent) {
|
|
11426
11497
|
if (!resolvedBranch || resolvedBranch === "default") {
|
|
11427
11498
|
throw new Error("Run index_codebase first to build the call graph and symbol index for this project.");
|
|
11428
11499
|
}
|
|
@@ -11738,8 +11809,17 @@ function fitTextToContextBudget(text, tokenBudget) {
|
|
|
11738
11809
|
function normalizedLineRange(result) {
|
|
11739
11810
|
return result.startLine <= result.endLine ? { start: result.startLine, end: result.endLine } : { start: result.endLine, end: result.startLine };
|
|
11740
11811
|
}
|
|
11741
|
-
function rankContextCandidates(results) {
|
|
11742
|
-
return results.map((result, originalIndex) => ({ result, originalIndex })).sort((left, right) =>
|
|
11812
|
+
function rankContextCandidates(results, preferImplementationPaths) {
|
|
11813
|
+
return results.map((result, originalIndex) => ({ result, originalIndex })).sort((left, right) => {
|
|
11814
|
+
if (preferImplementationPaths) {
|
|
11815
|
+
const leftIsImplementation = isLikelyImplementationPath(left.result.filePath);
|
|
11816
|
+
const rightIsImplementation = isLikelyImplementationPath(right.result.filePath);
|
|
11817
|
+
if (leftIsImplementation !== rightIsImplementation) {
|
|
11818
|
+
return leftIsImplementation ? -1 : 1;
|
|
11819
|
+
}
|
|
11820
|
+
}
|
|
11821
|
+
return right.result.score - left.result.score || left.originalIndex - right.originalIndex;
|
|
11822
|
+
});
|
|
11743
11823
|
}
|
|
11744
11824
|
function deduplicateContextCandidates(candidates) {
|
|
11745
11825
|
const acceptedByFile = /* @__PURE__ */ new Map();
|
|
@@ -11828,7 +11908,12 @@ function buildContextPack(results, options = {}) {
|
|
|
11828
11908
|
const maxResults = Math.max(0, Math.floor(options.maxResults ?? results.length));
|
|
11829
11909
|
const includeExactSearchHandoff = options.includeExactSearchHandoff ?? false;
|
|
11830
11910
|
const candidateCount = results.length;
|
|
11831
|
-
const deduplicated = deduplicateContextCandidates(
|
|
11911
|
+
const deduplicated = deduplicateContextCandidates(
|
|
11912
|
+
rankContextCandidates(
|
|
11913
|
+
results,
|
|
11914
|
+
options.preferImplementationPaths ?? false
|
|
11915
|
+
)
|
|
11916
|
+
);
|
|
11832
11917
|
const diversified = diversifyContextCandidates(deduplicated);
|
|
11833
11918
|
const duplicateCount = candidateCount - deduplicated.length;
|
|
11834
11919
|
const selectable = diversified.slice(0, maxResults);
|
|
@@ -12200,7 +12285,7 @@ ${truncateContent(r.content)}
|
|
|
12200
12285
|
}
|
|
12201
12286
|
|
|
12202
12287
|
// src/utils/effectiveness-metrics.ts
|
|
12203
|
-
var EFFECTIVENESS_METRICS_SCHEMA_VERSION =
|
|
12288
|
+
var EFFECTIVENESS_METRICS_SCHEMA_VERSION = 3;
|
|
12204
12289
|
var MAX_EFFECTIVENESS_COUNTER = 1e9;
|
|
12205
12290
|
var EFFECTIVENESS_TOOL_ROUTES = [
|
|
12206
12291
|
"context-conceptual",
|
|
@@ -12246,6 +12331,9 @@ var EFFECTIVENESS_SCOPE_RELAXATION_BUCKETS = ["none", "directory", "file-type",
|
|
|
12246
12331
|
function emptyCounterMap(values) {
|
|
12247
12332
|
return Object.fromEntries(values.map((value) => [value, 0]));
|
|
12248
12333
|
}
|
|
12334
|
+
function emptyRouteCounterMap(routes, values) {
|
|
12335
|
+
return Object.fromEntries(routes.map((route) => [route, emptyCounterMap(values)]));
|
|
12336
|
+
}
|
|
12249
12337
|
function boundedNumber(value) {
|
|
12250
12338
|
if (value === void 0 || !Number.isFinite(value)) return 0;
|
|
12251
12339
|
return Math.max(0, Math.floor(value));
|
|
@@ -12294,6 +12382,11 @@ function allowedValue(value, allowed, fallback) {
|
|
|
12294
12382
|
function cloneCounterMap(counters) {
|
|
12295
12383
|
return { ...counters };
|
|
12296
12384
|
}
|
|
12385
|
+
function cloneRouteCounterMap(counters) {
|
|
12386
|
+
return Object.fromEntries(
|
|
12387
|
+
EFFECTIVENESS_TOOL_ROUTES.map((route) => [route, cloneCounterMap(counters[route])])
|
|
12388
|
+
);
|
|
12389
|
+
}
|
|
12297
12390
|
var EffectivenessMetrics = class {
|
|
12298
12391
|
constructor(counterCap = MAX_EFFECTIVENESS_COUNTER) {
|
|
12299
12392
|
this.counterCap = counterCap;
|
|
@@ -12309,7 +12402,7 @@ var EffectivenessMetrics = class {
|
|
|
12309
12402
|
lifetime: "process",
|
|
12310
12403
|
reset: "index_metrics-reset-or-process-exit",
|
|
12311
12404
|
maxCounterValue: this.counterCap,
|
|
12312
|
-
dimensions: "bounded-
|
|
12405
|
+
dimensions: "bounded-route-and-bucketed-performance-only"
|
|
12313
12406
|
},
|
|
12314
12407
|
totalCalls: 0,
|
|
12315
12408
|
toolRoute: emptyCounterMap(EFFECTIVENESS_TOOL_ROUTES),
|
|
@@ -12321,7 +12414,14 @@ var EffectivenessMetrics = class {
|
|
|
12321
12414
|
tokenBudget: emptyCounterMap(EFFECTIVENESS_TOKEN_BUDGET_BUCKETS),
|
|
12322
12415
|
returnedTokenEstimate: emptyCounterMap(EFFECTIVENESS_RETURNED_TOKEN_BUCKETS),
|
|
12323
12416
|
exactHandoffEmitted: emptyCounterMap(EFFECTIVENESS_BOOLEAN_BUCKETS),
|
|
12324
|
-
scopeRelaxation: emptyCounterMap(EFFECTIVENESS_SCOPE_RELAXATION_BUCKETS)
|
|
12417
|
+
scopeRelaxation: emptyCounterMap(EFFECTIVENESS_SCOPE_RELAXATION_BUCKETS),
|
|
12418
|
+
routeOutcome: emptyRouteCounterMap(EFFECTIVENESS_TOOL_ROUTES, EFFECTIVENESS_OUTCOMES),
|
|
12419
|
+
routeLatency: emptyRouteCounterMap(EFFECTIVENESS_TOOL_ROUTES, EFFECTIVENESS_LATENCY_BUCKETS),
|
|
12420
|
+
routeResultCount: emptyRouteCounterMap(EFFECTIVENESS_TOOL_ROUTES, EFFECTIVENESS_RESULT_COUNT_BUCKETS),
|
|
12421
|
+
routeReturnedTokenEstimate: emptyRouteCounterMap(
|
|
12422
|
+
EFFECTIVENESS_TOOL_ROUTES,
|
|
12423
|
+
EFFECTIVENESS_RETURNED_TOKEN_BUCKETS
|
|
12424
|
+
)
|
|
12325
12425
|
};
|
|
12326
12426
|
}
|
|
12327
12427
|
increment(counters, key) {
|
|
@@ -12343,12 +12443,19 @@ var EffectivenessMetrics = class {
|
|
|
12343
12443
|
this.increment(this.snapshot.hostMode, host);
|
|
12344
12444
|
this.increment(this.snapshot.outcome, outcome);
|
|
12345
12445
|
this.increment(this.snapshot.recoveryUsed, recoveryUsed);
|
|
12346
|
-
|
|
12347
|
-
|
|
12446
|
+
const resultCountBucketValue = resultCountBucket(event.resultCount);
|
|
12447
|
+
const latencyBucketValue = latencyBucket(event.latencyMs);
|
|
12448
|
+
const returnedTokenBucketValue = returnedTokenBucket(event.returnedTokenEstimate);
|
|
12449
|
+
this.increment(this.snapshot.resultCount, resultCountBucketValue);
|
|
12450
|
+
this.increment(this.snapshot.latency, latencyBucketValue);
|
|
12348
12451
|
this.increment(this.snapshot.tokenBudget, tokenBudgetBucket(event.tokenBudget));
|
|
12349
|
-
this.increment(this.snapshot.returnedTokenEstimate,
|
|
12452
|
+
this.increment(this.snapshot.returnedTokenEstimate, returnedTokenBucketValue);
|
|
12350
12453
|
this.increment(this.snapshot.exactHandoffEmitted, exactHandoffEmitted);
|
|
12351
12454
|
this.increment(this.snapshot.scopeRelaxation, scopeRelaxation);
|
|
12455
|
+
this.increment(this.snapshot.routeOutcome[route], outcome);
|
|
12456
|
+
this.increment(this.snapshot.routeLatency[route], latencyBucketValue);
|
|
12457
|
+
this.increment(this.snapshot.routeResultCount[route], resultCountBucketValue);
|
|
12458
|
+
this.increment(this.snapshot.routeReturnedTokenEstimate[route], returnedTokenBucketValue);
|
|
12352
12459
|
}
|
|
12353
12460
|
getSnapshot() {
|
|
12354
12461
|
return {
|
|
@@ -12363,7 +12470,11 @@ var EffectivenessMetrics = class {
|
|
|
12363
12470
|
tokenBudget: cloneCounterMap(this.snapshot.tokenBudget),
|
|
12364
12471
|
returnedTokenEstimate: cloneCounterMap(this.snapshot.returnedTokenEstimate),
|
|
12365
12472
|
exactHandoffEmitted: cloneCounterMap(this.snapshot.exactHandoffEmitted),
|
|
12366
|
-
scopeRelaxation: cloneCounterMap(this.snapshot.scopeRelaxation)
|
|
12473
|
+
scopeRelaxation: cloneCounterMap(this.snapshot.scopeRelaxation),
|
|
12474
|
+
routeOutcome: cloneRouteCounterMap(this.snapshot.routeOutcome),
|
|
12475
|
+
routeLatency: cloneRouteCounterMap(this.snapshot.routeLatency),
|
|
12476
|
+
routeResultCount: cloneRouteCounterMap(this.snapshot.routeResultCount),
|
|
12477
|
+
routeReturnedTokenEstimate: cloneRouteCounterMap(this.snapshot.routeReturnedTokenEstimate)
|
|
12367
12478
|
};
|
|
12368
12479
|
}
|
|
12369
12480
|
reset() {
|
|
@@ -12382,6 +12493,7 @@ function resetProcessEffectivenessMetrics() {
|
|
|
12382
12493
|
}
|
|
12383
12494
|
function formatEffectivenessMetrics(snapshot) {
|
|
12384
12495
|
const formatCounters = (counters) => Object.entries(counters).map(([bucket, count]) => `${bucket}=${count}`).join(", ");
|
|
12496
|
+
const formatRouteCounters = (counters) => EFFECTIVENESS_TOOL_ROUTES.map((route) => `${route} => ${formatCounters(counters[route])}`).join("; ");
|
|
12385
12497
|
const lines = [
|
|
12386
12498
|
`Privacy-safe effectiveness (schema v${snapshot.schemaVersion}):`,
|
|
12387
12499
|
` Retention: ${snapshot.retention.storage}, ${snapshot.retention.lifetime}-lifetime; reset with index_metrics(reset=true) or process exit`,
|
|
@@ -12396,6 +12508,10 @@ function formatEffectivenessMetrics(snapshot) {
|
|
|
12396
12508
|
` Latency bucket: ${formatCounters(snapshot.latency)}`,
|
|
12397
12509
|
` Token-budget bucket: ${formatCounters(snapshot.tokenBudget)}`,
|
|
12398
12510
|
` Returned-token estimate: ${formatCounters(snapshot.returnedTokenEstimate)}`,
|
|
12511
|
+
` Route outcome buckets: ${formatRouteCounters(snapshot.routeOutcome)}`,
|
|
12512
|
+
` Route latency buckets: ${formatRouteCounters(snapshot.routeLatency)}`,
|
|
12513
|
+
` Route result-count buckets: ${formatRouteCounters(snapshot.routeResultCount)}`,
|
|
12514
|
+
` Route returned-token buckets: ${formatRouteCounters(snapshot.routeReturnedTokenEstimate)}`,
|
|
12399
12515
|
` Exact handoff emitted: ${formatCounters(snapshot.exactHandoffEmitted)}`,
|
|
12400
12516
|
` Scope relaxation: ${formatCounters(snapshot.scopeRelaxation)}`,
|
|
12401
12517
|
" Privacy: no queries, response text, source, symbols, paths, repository names, user identity, or stable identifiers are retained."
|
|
@@ -12407,6 +12523,103 @@ function formatEffectivenessMetrics(snapshot) {
|
|
|
12407
12523
|
var import_fs11 = require("fs");
|
|
12408
12524
|
var os6 = __toESM(require("os"), 1);
|
|
12409
12525
|
var path15 = __toESM(require("path"), 1);
|
|
12526
|
+
|
|
12527
|
+
// src/utils/power-source.ts
|
|
12528
|
+
var childProcess = __toESM(require("child_process"), 1);
|
|
12529
|
+
var POWER_SOURCE_RECHECK_DELAY_MS = 6e4;
|
|
12530
|
+
var PMSET_TIMEOUT_MS = 5e3;
|
|
12531
|
+
function getErrorMessage4(error) {
|
|
12532
|
+
return error instanceof Error ? error.message : String(error);
|
|
12533
|
+
}
|
|
12534
|
+
function runCommand(file, args, options) {
|
|
12535
|
+
return new Promise((resolve17, reject) => {
|
|
12536
|
+
childProcess.execFile(
|
|
12537
|
+
file,
|
|
12538
|
+
args,
|
|
12539
|
+
{ encoding: "utf8", timeout: options.timeoutMs },
|
|
12540
|
+
(error, stdout) => {
|
|
12541
|
+
if (error) {
|
|
12542
|
+
reject(error);
|
|
12543
|
+
return;
|
|
12544
|
+
}
|
|
12545
|
+
resolve17(stdout);
|
|
12546
|
+
}
|
|
12547
|
+
);
|
|
12548
|
+
});
|
|
12549
|
+
}
|
|
12550
|
+
function parseMacOsPowerSource(output) {
|
|
12551
|
+
const match = output.match(/Now drawing from '([^']+)'/i);
|
|
12552
|
+
if (!match) {
|
|
12553
|
+
return "unknown";
|
|
12554
|
+
}
|
|
12555
|
+
const source = match[1].toLowerCase();
|
|
12556
|
+
if (source === "battery power") {
|
|
12557
|
+
return "battery";
|
|
12558
|
+
}
|
|
12559
|
+
if (source === "ac power") {
|
|
12560
|
+
return "ac";
|
|
12561
|
+
}
|
|
12562
|
+
return "unknown";
|
|
12563
|
+
}
|
|
12564
|
+
async function readMacOsPowerSource(commandRunner = runCommand) {
|
|
12565
|
+
const output = await commandRunner(
|
|
12566
|
+
"/usr/bin/pmset",
|
|
12567
|
+
["-g", "batt"],
|
|
12568
|
+
{ timeoutMs: PMSET_TIMEOUT_MS }
|
|
12569
|
+
);
|
|
12570
|
+
return parseMacOsPowerSource(output);
|
|
12571
|
+
}
|
|
12572
|
+
var MacOsBackgroundIndexingPolicy = class {
|
|
12573
|
+
constructor(readPowerSource, recheckDelayMs) {
|
|
12574
|
+
this.readPowerSource = readPowerSource;
|
|
12575
|
+
this.recheckDelayMs = recheckDelayMs;
|
|
12576
|
+
}
|
|
12577
|
+
readPowerSource;
|
|
12578
|
+
recheckDelayMs;
|
|
12579
|
+
lastPaused = null;
|
|
12580
|
+
reportedFailure = false;
|
|
12581
|
+
isPaused() {
|
|
12582
|
+
return this.checkPowerSource();
|
|
12583
|
+
}
|
|
12584
|
+
async checkPowerSource() {
|
|
12585
|
+
try {
|
|
12586
|
+
const source = await this.readPowerSource();
|
|
12587
|
+
if (source === "unknown") {
|
|
12588
|
+
throw new Error("pmset returned an unrecognized power source");
|
|
12589
|
+
}
|
|
12590
|
+
this.reportedFailure = false;
|
|
12591
|
+
const paused = source === "battery";
|
|
12592
|
+
if (paused && this.lastPaused !== true) {
|
|
12593
|
+
console.warn("[codebase-index] Background indexing paused while macOS is using battery power.");
|
|
12594
|
+
} else if (!paused && this.lastPaused === true) {
|
|
12595
|
+
console.warn("[codebase-index] AC power detected; resuming pending background indexing.");
|
|
12596
|
+
}
|
|
12597
|
+
this.lastPaused = paused;
|
|
12598
|
+
return paused;
|
|
12599
|
+
} catch (error) {
|
|
12600
|
+
if (!this.reportedFailure) {
|
|
12601
|
+
console.error(
|
|
12602
|
+
`[codebase-index] Failed to determine the macOS power source; background indexing will continue: ${getErrorMessage4(error)}`
|
|
12603
|
+
);
|
|
12604
|
+
this.reportedFailure = true;
|
|
12605
|
+
}
|
|
12606
|
+
this.lastPaused = false;
|
|
12607
|
+
return false;
|
|
12608
|
+
}
|
|
12609
|
+
}
|
|
12610
|
+
};
|
|
12611
|
+
function createBackgroundIndexingPolicy(pauseOnBattery, options = {}) {
|
|
12612
|
+
const platform2 = options.platform ?? process.platform;
|
|
12613
|
+
if (!pauseOnBattery || platform2 !== "darwin") {
|
|
12614
|
+
return null;
|
|
12615
|
+
}
|
|
12616
|
+
return new MacOsBackgroundIndexingPolicy(
|
|
12617
|
+
options.readPowerSource ?? readMacOsPowerSource,
|
|
12618
|
+
options.recheckDelayMs ?? POWER_SOURCE_RECHECK_DELAY_MS
|
|
12619
|
+
);
|
|
12620
|
+
}
|
|
12621
|
+
|
|
12622
|
+
// src/utils/auto-index.ts
|
|
12410
12623
|
var MAX_RETRY_DELAY_MS = 1e4;
|
|
12411
12624
|
var SHUTDOWN_WAIT_MS = 2e3;
|
|
12412
12625
|
var coordinators = /* @__PURE__ */ new Map();
|
|
@@ -12523,7 +12736,13 @@ var AutoIndexCoordinator = class {
|
|
|
12523
12736
|
activation = Promise.resolve();
|
|
12524
12737
|
inFlight = null;
|
|
12525
12738
|
activeRequest = null;
|
|
12739
|
+
batteryCheck = null;
|
|
12740
|
+
batteryIndexJob = null;
|
|
12741
|
+
batteryDeferredRequest = null;
|
|
12742
|
+
batteryRetryTimer = null;
|
|
12743
|
+
resolveBatteryRetry = null;
|
|
12526
12744
|
pendingRequest = null;
|
|
12745
|
+
pendingFollowUp = null;
|
|
12527
12746
|
abortController = null;
|
|
12528
12747
|
stopped = false;
|
|
12529
12748
|
constructor(registration) {
|
|
@@ -12536,6 +12755,7 @@ var AutoIndexCoordinator = class {
|
|
|
12536
12755
|
};
|
|
12537
12756
|
}
|
|
12538
12757
|
update(registration) {
|
|
12758
|
+
const pauseOnBatteryChanged = registration.config.indexing.pauseBackgroundIndexingOnBattery !== this.registration.config.indexing.pauseBackgroundIndexingOnBattery;
|
|
12539
12759
|
this.registration = registration;
|
|
12540
12760
|
this.status.enabled = registration.config.indexing.autoIndex;
|
|
12541
12761
|
this.status.blockedReason = registration.blockedReason;
|
|
@@ -12551,6 +12771,9 @@ var AutoIndexCoordinator = class {
|
|
|
12551
12771
|
this.setState("idle", { source: void 0 });
|
|
12552
12772
|
}
|
|
12553
12773
|
}
|
|
12774
|
+
if (pauseOnBatteryChanged) {
|
|
12775
|
+
this.cancelBatteryRetry();
|
|
12776
|
+
}
|
|
12554
12777
|
}
|
|
12555
12778
|
activateAfter(activation) {
|
|
12556
12779
|
this.activation = activation;
|
|
@@ -12572,7 +12795,26 @@ var AutoIndexCoordinator = class {
|
|
|
12572
12795
|
if (this.stopped) {
|
|
12573
12796
|
return Promise.resolve({ outcome: "stopped" });
|
|
12574
12797
|
}
|
|
12575
|
-
return this.activation.then(() => this.
|
|
12798
|
+
return this.activation.then(() => this.enqueueBatteryAwareRequest(request));
|
|
12799
|
+
}
|
|
12800
|
+
enqueueBatteryAwareRequest(request) {
|
|
12801
|
+
if (!this.shouldDeferForBattery(request)) {
|
|
12802
|
+
return this.enqueueRequest(request);
|
|
12803
|
+
}
|
|
12804
|
+
if (this.batteryCheck && this.batteryIndexJob !== null && this.batteryIndexJob === this.inFlight) {
|
|
12805
|
+
return this.enqueueRequest(request);
|
|
12806
|
+
}
|
|
12807
|
+
this.batteryDeferredRequest = mergeRequests(this.batteryDeferredRequest, request);
|
|
12808
|
+
if (this.batteryCheck) {
|
|
12809
|
+
return this.batteryCheck;
|
|
12810
|
+
}
|
|
12811
|
+
const batteryCheck = this.waitForACPower();
|
|
12812
|
+
this.batteryCheck = batteryCheck;
|
|
12813
|
+
void batteryCheck.then(
|
|
12814
|
+
() => this.finishBatteryCheck(batteryCheck),
|
|
12815
|
+
() => this.finishBatteryCheck(batteryCheck)
|
|
12816
|
+
);
|
|
12817
|
+
return batteryCheck;
|
|
12576
12818
|
}
|
|
12577
12819
|
enqueueRequest(request) {
|
|
12578
12820
|
if (this.stopped || !this.canRun(request)) {
|
|
@@ -12590,6 +12832,11 @@ var AutoIndexCoordinator = class {
|
|
|
12590
12832
|
}
|
|
12591
12833
|
if (request.source === "watcher") {
|
|
12592
12834
|
this.pendingRequest = mergeRequests(this.pendingRequest, request);
|
|
12835
|
+
const active = this.inFlight;
|
|
12836
|
+
return active.then(() => {
|
|
12837
|
+
if (this.stopped) return { outcome: "stopped" };
|
|
12838
|
+
return this.pendingFollowUp ?? { outcome: "stopped" };
|
|
12839
|
+
});
|
|
12593
12840
|
}
|
|
12594
12841
|
return this.inFlight;
|
|
12595
12842
|
}
|
|
@@ -12606,6 +12853,8 @@ var AutoIndexCoordinator = class {
|
|
|
12606
12853
|
}
|
|
12607
12854
|
async stop(waitForCompletion = false) {
|
|
12608
12855
|
this.stopped = true;
|
|
12856
|
+
this.batteryDeferredRequest = null;
|
|
12857
|
+
this.cancelBatteryRetry();
|
|
12609
12858
|
this.pendingRequest = null;
|
|
12610
12859
|
this.abortController?.abort();
|
|
12611
12860
|
this.setState("stopped", {
|
|
@@ -12635,10 +12884,20 @@ var AutoIndexCoordinator = class {
|
|
|
12635
12884
|
this.inFlight = null;
|
|
12636
12885
|
this.activeRequest = null;
|
|
12637
12886
|
this.abortController = null;
|
|
12887
|
+
if (this.batteryIndexJob === job) {
|
|
12888
|
+
this.batteryIndexJob = null;
|
|
12889
|
+
this.batteryCheck = null;
|
|
12890
|
+
}
|
|
12638
12891
|
const pending = this.pendingRequest;
|
|
12639
12892
|
this.pendingRequest = null;
|
|
12640
12893
|
if (pending && !this.stopped) {
|
|
12641
|
-
this.
|
|
12894
|
+
const followUp = this.request(pending);
|
|
12895
|
+
this.pendingFollowUp = followUp;
|
|
12896
|
+
void followUp.then(() => {
|
|
12897
|
+
if (this.pendingFollowUp === followUp) {
|
|
12898
|
+
this.pendingFollowUp = null;
|
|
12899
|
+
}
|
|
12900
|
+
});
|
|
12642
12901
|
}
|
|
12643
12902
|
});
|
|
12644
12903
|
return job;
|
|
@@ -12802,6 +13061,68 @@ var AutoIndexCoordinator = class {
|
|
|
12802
13061
|
}
|
|
12803
13062
|
return this.registration.safeToRun && this.registration.config.indexing.autoIndex;
|
|
12804
13063
|
}
|
|
13064
|
+
shouldDeferForBattery(request) {
|
|
13065
|
+
return this.registration.backgroundIndexingPolicy !== null && (request.source === "startup" || request.source === "watcher");
|
|
13066
|
+
}
|
|
13067
|
+
async waitForACPower() {
|
|
13068
|
+
while (!this.stopped) {
|
|
13069
|
+
const policy = this.registration.backgroundIndexingPolicy;
|
|
13070
|
+
if (!policy || !await this.isBatteryPauseActive(policy)) {
|
|
13071
|
+
const request = this.batteryDeferredRequest;
|
|
13072
|
+
this.batteryDeferredRequest = null;
|
|
13073
|
+
if (!request) return { outcome: "stopped" };
|
|
13074
|
+
const job = this.enqueueRequest(request);
|
|
13075
|
+
if (this.inFlight === job) {
|
|
13076
|
+
this.batteryIndexJob = job;
|
|
13077
|
+
}
|
|
13078
|
+
return job;
|
|
13079
|
+
}
|
|
13080
|
+
await this.waitForBatteryRetry(policy.recheckDelayMs);
|
|
13081
|
+
}
|
|
13082
|
+
return { outcome: "stopped" };
|
|
13083
|
+
}
|
|
13084
|
+
async isBatteryPauseActive(policy) {
|
|
13085
|
+
try {
|
|
13086
|
+
return await policy.isPaused();
|
|
13087
|
+
} catch (error) {
|
|
13088
|
+
console.error(
|
|
13089
|
+
`[codebase-index] Failed to apply the background indexing power policy; background indexing will continue: ${safeFailureMessage(error)}`
|
|
13090
|
+
);
|
|
13091
|
+
return false;
|
|
13092
|
+
}
|
|
13093
|
+
}
|
|
13094
|
+
waitForBatteryRetry(delayMs) {
|
|
13095
|
+
return new Promise((resolve17) => {
|
|
13096
|
+
const timer = setTimeout(() => {
|
|
13097
|
+
if (this.batteryRetryTimer === timer) {
|
|
13098
|
+
this.batteryRetryTimer = null;
|
|
13099
|
+
this.resolveBatteryRetry = null;
|
|
13100
|
+
}
|
|
13101
|
+
resolve17();
|
|
13102
|
+
}, delayMs);
|
|
13103
|
+
timer.unref?.();
|
|
13104
|
+
this.batteryRetryTimer = timer;
|
|
13105
|
+
this.resolveBatteryRetry = resolve17;
|
|
13106
|
+
});
|
|
13107
|
+
}
|
|
13108
|
+
cancelBatteryRetry() {
|
|
13109
|
+
if (this.batteryRetryTimer) {
|
|
13110
|
+
clearTimeout(this.batteryRetryTimer);
|
|
13111
|
+
this.batteryRetryTimer = null;
|
|
13112
|
+
}
|
|
13113
|
+
const resolve17 = this.resolveBatteryRetry;
|
|
13114
|
+
this.resolveBatteryRetry = null;
|
|
13115
|
+
resolve17?.();
|
|
13116
|
+
}
|
|
13117
|
+
finishBatteryCheck(batteryCheck) {
|
|
13118
|
+
if (this.batteryCheck !== batteryCheck) return;
|
|
13119
|
+
this.batteryCheck = null;
|
|
13120
|
+
const deferredRequest = this.batteryDeferredRequest;
|
|
13121
|
+
this.batteryDeferredRequest = null;
|
|
13122
|
+
if (deferredRequest && !this.stopped) {
|
|
13123
|
+
void this.request(deferredRequest);
|
|
13124
|
+
}
|
|
13125
|
+
}
|
|
12805
13126
|
};
|
|
12806
13127
|
function getCoordinator(projectRoot, host) {
|
|
12807
13128
|
const key = coordinatorKeysByProject.get(projectLookupKey(projectRoot, host));
|
|
@@ -12811,6 +13132,9 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
|
|
|
12811
13132
|
const projectKey = projectLookupKey(projectRoot, host);
|
|
12812
13133
|
const safety = getProjectSafety(projectRoot, config);
|
|
12813
13134
|
const registration = {
|
|
13135
|
+
backgroundIndexingPolicy: createBackgroundIndexingPolicy(
|
|
13136
|
+
config.indexing.pauseBackgroundIndexingOnBattery
|
|
13137
|
+
),
|
|
12814
13138
|
config,
|
|
12815
13139
|
getIndexer,
|
|
12816
13140
|
projectRoot,
|
|
@@ -14034,6 +14358,7 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
14034
14358
|
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed);
|
|
14035
14359
|
if (results.length > 0) {
|
|
14036
14360
|
const heading = buildPackHeading("conceptual", decisions);
|
|
14361
|
+
const intent = analyzeQueryIntent(attempt.queryText);
|
|
14037
14362
|
return toResult(
|
|
14038
14363
|
"conceptual",
|
|
14039
14364
|
attempt.queryText,
|
|
@@ -14041,7 +14366,8 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
14041
14366
|
tokenBudget,
|
|
14042
14367
|
maxResults: limit,
|
|
14043
14368
|
heading,
|
|
14044
|
-
includeExactSearchHandoff: true
|
|
14369
|
+
includeExactSearchHandoff: true,
|
|
14370
|
+
preferImplementationPaths: intent.preferSourcePaths || intent.primary !== "docs" && intent.primary !== "test"
|
|
14045
14371
|
})
|
|
14046
14372
|
);
|
|
14047
14373
|
}
|
|
@@ -14342,17 +14668,26 @@ function percentile(values, p) {
|
|
|
14342
14668
|
function normalizePath2(input) {
|
|
14343
14669
|
return normalizePathSeparators(input);
|
|
14344
14670
|
}
|
|
14345
|
-
function
|
|
14671
|
+
function uniqueResultsByEvidence(results) {
|
|
14346
14672
|
const seen = /* @__PURE__ */ new Set();
|
|
14347
14673
|
const unique = [];
|
|
14348
14674
|
for (const result of results) {
|
|
14349
|
-
const
|
|
14350
|
-
if (seen.has(
|
|
14351
|
-
seen.add(
|
|
14675
|
+
const key = `${normalizePath2(result.filePath)}::${result.name ?? ""}`;
|
|
14676
|
+
if (seen.has(key)) continue;
|
|
14677
|
+
seen.add(key);
|
|
14352
14678
|
unique.push(result);
|
|
14353
14679
|
}
|
|
14354
14680
|
return unique;
|
|
14355
14681
|
}
|
|
14682
|
+
function uniqueResultsByPath(results) {
|
|
14683
|
+
const seen = /* @__PURE__ */ new Set();
|
|
14684
|
+
return results.filter((result) => {
|
|
14685
|
+
const key = normalizePath2(result.filePath);
|
|
14686
|
+
if (seen.has(key)) return false;
|
|
14687
|
+
seen.add(key);
|
|
14688
|
+
return true;
|
|
14689
|
+
});
|
|
14690
|
+
}
|
|
14356
14691
|
function distinctTopKRatio(results, k) {
|
|
14357
14692
|
const top = results.slice(0, k);
|
|
14358
14693
|
if (top.length === 0) return 0;
|
|
@@ -14363,60 +14698,169 @@ function pathMatchesExpected(actualPath, expectedPath) {
|
|
|
14363
14698
|
const actual = normalizePath2(actualPath);
|
|
14364
14699
|
const expected = normalizePath2(expectedPath);
|
|
14365
14700
|
if (actual === expected) return true;
|
|
14366
|
-
return actual.endsWith(`/${expected}`)
|
|
14701
|
+
return actual.endsWith(`/${expected}`);
|
|
14702
|
+
}
|
|
14703
|
+
function getRelevantEvidence(query) {
|
|
14704
|
+
const legacyEvidence = [];
|
|
14705
|
+
if (query.expected.filePath !== void 0) {
|
|
14706
|
+
legacyEvidence.push({
|
|
14707
|
+
path: query.expected.filePath,
|
|
14708
|
+
...query.expected.symbol !== void 0 ? { symbol: query.expected.symbol } : {},
|
|
14709
|
+
relevance: 1
|
|
14710
|
+
});
|
|
14711
|
+
}
|
|
14712
|
+
if (query.expected.acceptableFiles) {
|
|
14713
|
+
for (const path29 of query.expected.acceptableFiles) {
|
|
14714
|
+
legacyEvidence.push({
|
|
14715
|
+
path: path29,
|
|
14716
|
+
...query.expected.symbol !== void 0 ? { symbol: query.expected.symbol } : {},
|
|
14717
|
+
relevance: 1
|
|
14718
|
+
});
|
|
14719
|
+
}
|
|
14720
|
+
}
|
|
14721
|
+
const gradedEvidence = query.expected.gradedEvidence ?? [];
|
|
14722
|
+
const allEvidence = [...legacyEvidence, ...gradedEvidence];
|
|
14723
|
+
const dedupeKey = (entry) => {
|
|
14724
|
+
return `${normalizePath2(entry.path)}::${entry.symbol ?? ""}`;
|
|
14725
|
+
};
|
|
14726
|
+
const unique = /* @__PURE__ */ new Map();
|
|
14727
|
+
for (const entry of allEvidence) {
|
|
14728
|
+
unique.set(dedupeKey(entry), entry);
|
|
14729
|
+
}
|
|
14730
|
+
return Array.from(unique.values());
|
|
14731
|
+
}
|
|
14732
|
+
function hasSymbolRequirement(query) {
|
|
14733
|
+
return isSymbolIntended(query);
|
|
14734
|
+
}
|
|
14735
|
+
function isSymbolIntended(query) {
|
|
14736
|
+
return query.expected.symbol !== void 0 || query.args?.symbol !== void 0 || query.expected.gradedEvidence?.some((entry) => entry.symbol !== void 0) === true;
|
|
14737
|
+
}
|
|
14738
|
+
function isExpectedFile(filePath, relevant) {
|
|
14739
|
+
return relevant.some((entry) => pathMatchesExpected(filePath, entry.path));
|
|
14740
|
+
}
|
|
14741
|
+
function resultRelevance(filePath, symbol, relevant, isSymbolIntendedQuery) {
|
|
14742
|
+
let relevance = 0;
|
|
14743
|
+
for (const entry of relevant) {
|
|
14744
|
+
if (!pathMatchesExpected(filePath, entry.path)) {
|
|
14745
|
+
continue;
|
|
14746
|
+
}
|
|
14747
|
+
if (isSymbolIntendedQuery) {
|
|
14748
|
+
if (symbol === void 0 || entry.symbol === void 0 || symbol !== entry.symbol) {
|
|
14749
|
+
continue;
|
|
14750
|
+
}
|
|
14751
|
+
} else if (entry.symbol !== void 0 && symbol !== void 0 && symbol !== entry.symbol) {
|
|
14752
|
+
continue;
|
|
14753
|
+
}
|
|
14754
|
+
if (entry.symbol === void 0) {
|
|
14755
|
+
relevance = Math.max(relevance, entry.relevance);
|
|
14756
|
+
continue;
|
|
14757
|
+
}
|
|
14758
|
+
if (isSymbolIntendedQuery && entry.symbol !== void 0 && symbol === entry.symbol) {
|
|
14759
|
+
relevance = Math.max(relevance, entry.relevance);
|
|
14760
|
+
continue;
|
|
14761
|
+
}
|
|
14762
|
+
if (!isSymbolIntendedQuery && symbol !== void 0 && symbol === entry.symbol) {
|
|
14763
|
+
relevance = Math.max(relevance, entry.relevance);
|
|
14764
|
+
}
|
|
14765
|
+
}
|
|
14766
|
+
return relevance;
|
|
14367
14767
|
}
|
|
14368
|
-
function
|
|
14369
|
-
|
|
14370
|
-
|
|
14371
|
-
|
|
14768
|
+
function isRelevantResult(filePath, symbol, relevant, isSymbolIntendedQuery) {
|
|
14769
|
+
return resultRelevance(filePath, symbol, relevant, isSymbolIntendedQuery) > 0;
|
|
14770
|
+
}
|
|
14771
|
+
function evidenceMatchesResult(entry, filePath, symbol, isSymbolIntendedQuery) {
|
|
14772
|
+
if (!pathMatchesExpected(filePath, entry.path)) {
|
|
14773
|
+
return false;
|
|
14774
|
+
}
|
|
14775
|
+
if (isSymbolIntendedQuery) {
|
|
14776
|
+
return entry.symbol !== void 0 && symbol === entry.symbol;
|
|
14777
|
+
}
|
|
14778
|
+
return entry.symbol === void 0 || symbol === entry.symbol;
|
|
14372
14779
|
}
|
|
14373
|
-
function
|
|
14374
|
-
return
|
|
14780
|
+
function hasGradeBasedEvidence(query) {
|
|
14781
|
+
return (query.expected.gradedEvidence?.length ?? 0) > 0;
|
|
14375
14782
|
}
|
|
14376
|
-
function
|
|
14377
|
-
const
|
|
14783
|
+
function dedupeRelevantEvidence(relevant) {
|
|
14784
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
14785
|
+
for (const entry of relevant) {
|
|
14786
|
+
const key = `${normalizePath2(entry.path)}::${entry.symbol ?? ""}`;
|
|
14787
|
+
if (!deduped.has(key)) {
|
|
14788
|
+
deduped.set(key, entry);
|
|
14789
|
+
}
|
|
14790
|
+
}
|
|
14791
|
+
return [...deduped.values()];
|
|
14792
|
+
}
|
|
14793
|
+
function reciprocalRankAtK(results, relevant, isSymbolIntendedQuery, k) {
|
|
14794
|
+
const top = uniqueResultsByEvidence(results).slice(0, k);
|
|
14378
14795
|
for (let i = 0; i < top.length; i += 1) {
|
|
14379
|
-
if (isRelevantResult(top[i].filePath,
|
|
14796
|
+
if (isRelevantResult(top[i].filePath, top[i].name, relevant, isSymbolIntendedQuery)) {
|
|
14380
14797
|
return 1 / (i + 1);
|
|
14381
14798
|
}
|
|
14382
14799
|
}
|
|
14383
14800
|
return 0;
|
|
14384
14801
|
}
|
|
14385
|
-
function ndcgAtK(results,
|
|
14386
|
-
const top =
|
|
14802
|
+
function ndcgAtK(query, results, relevant, isSymbolIntendedQuery, k) {
|
|
14803
|
+
const top = uniqueResultsByEvidence(results).slice(0, k);
|
|
14804
|
+
const availableEvidence = dedupeRelevantEvidence(relevant).filter(
|
|
14805
|
+
(entry) => !isSymbolIntendedQuery || entry.symbol !== void 0
|
|
14806
|
+
);
|
|
14387
14807
|
const dcg = top.reduce((sum, result, i) => {
|
|
14388
|
-
|
|
14389
|
-
|
|
14808
|
+
let bestEvidenceIndex = -1;
|
|
14809
|
+
let rel = 0;
|
|
14810
|
+
for (let evidenceIndex = 0; evidenceIndex < availableEvidence.length; evidenceIndex += 1) {
|
|
14811
|
+
const entry = availableEvidence[evidenceIndex];
|
|
14812
|
+
if (entry.relevance > rel && evidenceMatchesResult(entry, result.filePath, result.name, isSymbolIntendedQuery)) {
|
|
14813
|
+
bestEvidenceIndex = evidenceIndex;
|
|
14814
|
+
rel = entry.relevance;
|
|
14815
|
+
}
|
|
14816
|
+
}
|
|
14817
|
+
if (bestEvidenceIndex < 0) {
|
|
14818
|
+
return sum;
|
|
14819
|
+
}
|
|
14820
|
+
availableEvidence.splice(bestEvidenceIndex, 1);
|
|
14821
|
+
return sum + (2 ** rel - 1) / Math.log2(i + 2);
|
|
14390
14822
|
}, 0);
|
|
14391
|
-
const
|
|
14392
|
-
|
|
14393
|
-
|
|
14823
|
+
const dedupedRelevant = dedupeRelevantEvidence(relevant).filter(
|
|
14824
|
+
(entry) => !isSymbolIntendedQuery || entry.symbol !== void 0
|
|
14825
|
+
);
|
|
14826
|
+
const idealRelevances = hasGradeBasedEvidence(query) ? dedupedRelevant.map((entry) => entry.relevance).sort((a, b) => b - a).slice(0, k) : relevant.length > 0 ? [1] : [];
|
|
14827
|
+
const idcg = idealRelevances.reduce(
|
|
14828
|
+
(sum, rel, index) => sum + (2 ** rel - 1) / Math.log2(index + 2),
|
|
14394
14829
|
0
|
|
14395
14830
|
);
|
|
14396
|
-
|
|
14831
|
+
if (idcg === 0) {
|
|
14832
|
+
return 0;
|
|
14833
|
+
}
|
|
14834
|
+
const ndcg = dcg / idcg;
|
|
14835
|
+
if (ndcg <= 0) {
|
|
14836
|
+
return 0;
|
|
14837
|
+
}
|
|
14838
|
+
return ndcg > 1 ? 1 : ndcg;
|
|
14397
14839
|
}
|
|
14398
14840
|
function isDocsOrTestsPath(filePath) {
|
|
14399
14841
|
const lowered = normalizePath2(filePath).toLowerCase();
|
|
14400
14842
|
return lowered.includes("/docs/") || lowered.includes("/test/") || lowered.includes("/tests/") || lowered.includes("readme") || lowered.includes("/benchmarks/");
|
|
14401
14843
|
}
|
|
14402
14844
|
function classifyFailureBucket(query, results, k) {
|
|
14403
|
-
const
|
|
14404
|
-
const
|
|
14405
|
-
|
|
14845
|
+
const relevant = getRelevantEvidence(query);
|
|
14846
|
+
const isSymbolIntendedQuery = isSymbolIntended(query);
|
|
14847
|
+
if (query.expected.expectedOutcome === "no-results") return void 0;
|
|
14848
|
+
const top = uniqueResultsByEvidence(results).slice(0, k);
|
|
14849
|
+
const hasRelevantTopK = top.some(
|
|
14850
|
+
(result) => isRelevantResult(result.filePath, result.name, relevant, isSymbolIntendedQuery)
|
|
14851
|
+
);
|
|
14406
14852
|
if (!hasRelevantTopK) {
|
|
14853
|
+
const hasExpectedFileTopK = top.some((result) => isExpectedFile(result.filePath, relevant));
|
|
14854
|
+
if (hasExpectedFileTopK && hasSymbolRequirement(query)) {
|
|
14855
|
+
return "wrong-symbol";
|
|
14856
|
+
}
|
|
14407
14857
|
return "no-relevant-hit-top-k";
|
|
14408
14858
|
}
|
|
14409
|
-
if (query.expected.symbol) {
|
|
14410
|
-
const hasSymbol = top.some(
|
|
14411
|
-
(result) => isRelevantResult(result.filePath, relevantPaths) && result.name === query.expected.symbol
|
|
14412
|
-
);
|
|
14413
|
-
if (!hasSymbol) return "wrong-symbol";
|
|
14414
|
-
}
|
|
14415
14859
|
const top1 = top[0];
|
|
14416
|
-
if (top1 && !
|
|
14860
|
+
if (top1 && !isExpectedFile(top1.filePath, relevant) && isDocsOrTestsPath(top1.filePath)) {
|
|
14417
14861
|
return "docs-tests-outranking-source";
|
|
14418
14862
|
}
|
|
14419
|
-
if (top1 && !
|
|
14863
|
+
if (top1 && !isExpectedFile(top1.filePath, relevant)) {
|
|
14420
14864
|
return "wrong-file";
|
|
14421
14865
|
}
|
|
14422
14866
|
return void 0;
|
|
@@ -14425,9 +14869,12 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
14425
14869
|
resolvedRoute: "search",
|
|
14426
14870
|
routedQuery: query.query
|
|
14427
14871
|
}, context) {
|
|
14428
|
-
const
|
|
14429
|
-
const
|
|
14430
|
-
const
|
|
14872
|
+
const relevant = getRelevantEvidence(query);
|
|
14873
|
+
const isSymbolIntendedQuery = isSymbolIntended(query);
|
|
14874
|
+
const deduped = uniqueResultsByEvidence(results);
|
|
14875
|
+
const hitAt = (cutoff) => deduped.slice(0, cutoff).some(
|
|
14876
|
+
(result) => isRelevantResult(result.filePath, result.name, relevant, isSymbolIntendedQuery)
|
|
14877
|
+
);
|
|
14431
14878
|
const perQuery = {
|
|
14432
14879
|
id: query.id,
|
|
14433
14880
|
query: query.query,
|
|
@@ -14435,13 +14882,19 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
14435
14882
|
retrievalMode: query.retrievalMode ?? "search",
|
|
14436
14883
|
resolvedRoute: route.resolvedRoute,
|
|
14437
14884
|
routedQuery: route.routedQuery,
|
|
14885
|
+
routeMatched: query.expected.expectedRoute ? query.expected.expectedRoute === route.resolvedRoute : void 0,
|
|
14886
|
+
outcomeMatched: query.expected.expectedOutcome === void 0 ? void 0 : query.expected.expectedOutcome === "results" ? deduped.length > 0 : deduped.length === 0,
|
|
14887
|
+
recoveryMatched: query.expected.recoveryExpectation === void 0 ? void 0 : query.expected.recoveryExpectation === "filter-relaxed" ? context?.recoveryRelaxed === true : context?.recoveryUsed !== true,
|
|
14888
|
+
language: query.language,
|
|
14889
|
+
difficulty: query.difficulty,
|
|
14890
|
+
tags: query.tags,
|
|
14438
14891
|
latencyMs,
|
|
14439
14892
|
hitAt1: hitAt(1),
|
|
14440
14893
|
hitAt3: hitAt(3),
|
|
14441
14894
|
hitAt5: hitAt(5),
|
|
14442
14895
|
hitAt10: hitAt(10),
|
|
14443
|
-
reciprocalRankAt10: reciprocalRankAtK(deduped,
|
|
14444
|
-
ndcgAt10: ndcgAtK(deduped,
|
|
14896
|
+
reciprocalRankAt10: reciprocalRankAtK(deduped, relevant, isSymbolIntendedQuery, 10),
|
|
14897
|
+
ndcgAt10: ndcgAtK(query, deduped, relevant, isSymbolIntendedQuery, 10),
|
|
14445
14898
|
failureBucket: classifyFailureBucket(query, results, k),
|
|
14446
14899
|
rawTop3DistinctRatio: distinctTopKRatio(results, 3),
|
|
14447
14900
|
tokenBudget: context?.tokenBudget,
|
|
@@ -14459,6 +14912,11 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
14459
14912
|
function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingTokensUsed, costPer1MTokensUsd) {
|
|
14460
14913
|
const count = perQuery.length;
|
|
14461
14914
|
const safeDiv = (value) => count === 0 ? 0 : value / count;
|
|
14915
|
+
const positiveQueryIds = new Set(
|
|
14916
|
+
queries.filter((query) => query.expected.expectedOutcome !== "no-results").map((query) => query.id)
|
|
14917
|
+
);
|
|
14918
|
+
const positiveCount = perQuery.filter((query) => positiveQueryIds.has(query.id)).length;
|
|
14919
|
+
const safePositiveDiv = (value) => positiveCount === 0 ? 0 : value / positiveCount;
|
|
14462
14920
|
const sum = {
|
|
14463
14921
|
hitAt1: 0,
|
|
14464
14922
|
hitAt3: 0,
|
|
@@ -14480,27 +14938,52 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
14480
14938
|
const contextResponseTokens = contextQueries.map((item) => item.responseTokens);
|
|
14481
14939
|
const totalContextResponseTokens = contextResponseTokens.reduce((sum2, value) => sum2 + value, 0);
|
|
14482
14940
|
const contextTokenUnits = totalContextResponseTokens / 1e3;
|
|
14941
|
+
let routeMatchedCount = 0;
|
|
14942
|
+
let routeExpectedCount = 0;
|
|
14943
|
+
let outcomeMatchedCount = 0;
|
|
14944
|
+
let outcomeExpectedCount = 0;
|
|
14945
|
+
let recoveryMatchedCount = 0;
|
|
14946
|
+
let recoveryExpectedCount = 0;
|
|
14483
14947
|
for (const query of perQuery) {
|
|
14484
|
-
if (query.
|
|
14485
|
-
|
|
14486
|
-
|
|
14487
|
-
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
|
|
14948
|
+
if (positiveQueryIds.has(query.id)) {
|
|
14949
|
+
if (query.hitAt1) sum.hitAt1 += 1;
|
|
14950
|
+
if (query.hitAt3) sum.hitAt3 += 1;
|
|
14951
|
+
if (query.hitAt5) sum.hitAt5 += 1;
|
|
14952
|
+
if (query.hitAt10) sum.hitAt10 += 1;
|
|
14953
|
+
sum.mrrAt10 += query.reciprocalRankAt10;
|
|
14954
|
+
sum.ndcgAt10 += query.ndcgAt10;
|
|
14955
|
+
}
|
|
14956
|
+
sum.distinctTop3Ratio += distinctTopKRatio(uniqueResultsByPath(query.results), 3);
|
|
14491
14957
|
sum.rawDistinctTop3Ratio += query.rawTop3DistinctRatio;
|
|
14492
14958
|
if (query.failureBucket) {
|
|
14493
14959
|
failureBuckets[query.failureBucket] += 1;
|
|
14494
14960
|
}
|
|
14961
|
+
if (query.routeMatched !== void 0) {
|
|
14962
|
+
routeExpectedCount += 1;
|
|
14963
|
+
if (query.routeMatched) {
|
|
14964
|
+
routeMatchedCount += 1;
|
|
14965
|
+
}
|
|
14966
|
+
}
|
|
14967
|
+
if (query.outcomeMatched !== void 0) {
|
|
14968
|
+
outcomeExpectedCount += 1;
|
|
14969
|
+
if (query.outcomeMatched) outcomeMatchedCount += 1;
|
|
14970
|
+
}
|
|
14971
|
+
if (query.recoveryMatched !== void 0) {
|
|
14972
|
+
recoveryExpectedCount += 1;
|
|
14973
|
+
if (query.recoveryMatched) recoveryMatchedCount += 1;
|
|
14974
|
+
}
|
|
14495
14975
|
}
|
|
14496
14976
|
const queryTokens = queries.reduce((acc, q) => acc + estimateTokens(q.query), 0);
|
|
14497
14977
|
return {
|
|
14498
|
-
hitAt1:
|
|
14499
|
-
hitAt3:
|
|
14500
|
-
hitAt5:
|
|
14501
|
-
hitAt10:
|
|
14502
|
-
mrrAt10:
|
|
14503
|
-
ndcgAt10:
|
|
14978
|
+
hitAt1: safePositiveDiv(sum.hitAt1),
|
|
14979
|
+
hitAt3: safePositiveDiv(sum.hitAt3),
|
|
14980
|
+
hitAt5: safePositiveDiv(sum.hitAt5),
|
|
14981
|
+
hitAt10: safePositiveDiv(sum.hitAt10),
|
|
14982
|
+
mrrAt10: safePositiveDiv(sum.mrrAt10),
|
|
14983
|
+
ndcgAt10: safePositiveDiv(sum.ndcgAt10),
|
|
14984
|
+
routeAccuracy: routeExpectedCount === 0 ? 0 : routeMatchedCount / routeExpectedCount,
|
|
14985
|
+
outcomeAccuracy: outcomeExpectedCount === 0 ? 0 : outcomeMatchedCount / outcomeExpectedCount,
|
|
14986
|
+
recoveryAccuracy: recoveryExpectedCount === 0 ? 0 : recoveryMatchedCount / recoveryExpectedCount,
|
|
14504
14987
|
distinctTop3Ratio: safeDiv(sum.distinctTop3Ratio),
|
|
14505
14988
|
rawDistinctTop3Ratio: safeDiv(sum.rawDistinctTop3Ratio),
|
|
14506
14989
|
latencyMs: {
|
|
@@ -14707,6 +15190,9 @@ function isRecord3(value) {
|
|
|
14707
15190
|
function isStringArray4(value) {
|
|
14708
15191
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
14709
15192
|
}
|
|
15193
|
+
function isNonEmptyString(value) {
|
|
15194
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
15195
|
+
}
|
|
14710
15196
|
function asPositiveNumber(value, path29) {
|
|
14711
15197
|
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
14712
15198
|
throw new Error(`${path29} must be a non-negative number`);
|
|
@@ -14721,11 +15207,109 @@ function parseQueryType(value, path29) {
|
|
|
14721
15207
|
`${path29} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
|
|
14722
15208
|
);
|
|
14723
15209
|
}
|
|
15210
|
+
function parseExpectedRoute(value, path29) {
|
|
15211
|
+
if (value === void 0) return void 0;
|
|
15212
|
+
if (value === "search" || value === "definition") return value;
|
|
15213
|
+
throw new Error(`${path29} must be one of: search, definition`);
|
|
15214
|
+
}
|
|
15215
|
+
function parseExpectedOutcome(value, path29) {
|
|
15216
|
+
if (value === void 0) return void 0;
|
|
15217
|
+
if (value === "results" || value === "no-results") {
|
|
15218
|
+
return value;
|
|
15219
|
+
}
|
|
15220
|
+
throw new Error(`${path29} must be one of: results, no-results`);
|
|
15221
|
+
}
|
|
15222
|
+
function parseRecoveryExpectation(value, path29) {
|
|
15223
|
+
if (value === void 0) return void 0;
|
|
15224
|
+
if (value === "none" || value === "filter-relaxed") {
|
|
15225
|
+
return value;
|
|
15226
|
+
}
|
|
15227
|
+
throw new Error(`${path29} must be one of: none, filter-relaxed`);
|
|
15228
|
+
}
|
|
15229
|
+
function parseQueryDifficulty(value, path29) {
|
|
15230
|
+
if (value === void 0) return void 0;
|
|
15231
|
+
if (value === "easy" || value === "medium" || value === "hard") {
|
|
15232
|
+
return value;
|
|
15233
|
+
}
|
|
15234
|
+
throw new Error(`${path29} must be one of: easy, medium, hard`);
|
|
15235
|
+
}
|
|
15236
|
+
function parseQueryTags(value, path29) {
|
|
15237
|
+
if (value === void 0) return void 0;
|
|
15238
|
+
if (!isStringArray4(value) || value.some((tag) => tag.trim().length === 0)) {
|
|
15239
|
+
throw new Error(`${path29} must be an array of non-empty strings`);
|
|
15240
|
+
}
|
|
15241
|
+
if (value.length > 16) {
|
|
15242
|
+
throw new Error(`${path29} must contain at most 16 tags`);
|
|
15243
|
+
}
|
|
15244
|
+
return value;
|
|
15245
|
+
}
|
|
15246
|
+
function parseQueryArgs(value, path29) {
|
|
15247
|
+
if (value === void 0) return void 0;
|
|
15248
|
+
if (!isRecord3(value)) {
|
|
15249
|
+
throw new Error(`${path29} must be an object`);
|
|
15250
|
+
}
|
|
15251
|
+
const symbol = parseStringOrUndefined(value.symbol, `${path29}.symbol`);
|
|
15252
|
+
const fileType = parseStringOrUndefined(value.fileType, `${path29}.fileType`);
|
|
15253
|
+
const directory = parseStringOrUndefined(value.directory, `${path29}.directory`);
|
|
15254
|
+
return {
|
|
15255
|
+
...symbol !== void 0 ? { symbol } : {},
|
|
15256
|
+
...fileType !== void 0 ? { fileType } : {},
|
|
15257
|
+
...directory !== void 0 ? { directory } : {}
|
|
15258
|
+
};
|
|
15259
|
+
}
|
|
15260
|
+
var SEMVER_VERSION_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
|
|
15261
|
+
function parseSemanticVersion(value, path29) {
|
|
15262
|
+
if (!isNonEmptyString(value)) {
|
|
15263
|
+
throw new Error(`${path29} must be a non-empty string`);
|
|
15264
|
+
}
|
|
15265
|
+
if (!SEMVER_VERSION_PATTERN.test(value)) {
|
|
15266
|
+
throw new Error(`${path29} must be a valid semantic version (MAJOR.MINOR.PATCH)`);
|
|
15267
|
+
}
|
|
15268
|
+
return value;
|
|
15269
|
+
}
|
|
14724
15270
|
function parseRetrievalMode(value, path29) {
|
|
14725
15271
|
if (value === void 0 || value === "search") return "search";
|
|
14726
15272
|
if (value === "context") return value;
|
|
14727
15273
|
throw new Error(`${path29} must be one of: search, context`);
|
|
14728
15274
|
}
|
|
15275
|
+
function parseStringOrUndefined(value, path29) {
|
|
15276
|
+
if (value === void 0 || value === null) return void 0;
|
|
15277
|
+
if (!isNonEmptyString(value)) {
|
|
15278
|
+
throw new Error(`${path29} must be a non-empty string`);
|
|
15279
|
+
}
|
|
15280
|
+
return value;
|
|
15281
|
+
}
|
|
15282
|
+
function parseGradedEvidence(value, path29) {
|
|
15283
|
+
if (value === void 0) return [];
|
|
15284
|
+
if (!Array.isArray(value)) {
|
|
15285
|
+
throw new Error(`${path29} must be an array`);
|
|
15286
|
+
}
|
|
15287
|
+
return value.map((entry, index) => {
|
|
15288
|
+
if (!isRecord3(entry)) {
|
|
15289
|
+
throw new Error(`${path29}[${index}] must be an object`);
|
|
15290
|
+
}
|
|
15291
|
+
const evidencePath = parseStringOrUndefined(entry.path, `${path29}[${index}].path`);
|
|
15292
|
+
if (evidencePath === void 0) {
|
|
15293
|
+
throw new Error(`${path29}[${index}].path is required`);
|
|
15294
|
+
}
|
|
15295
|
+
const symbol = parseStringOrUndefined(entry.symbol, `${path29}[${index}].symbol`);
|
|
15296
|
+
const relevance = parseEvidenceRelevance(entry.relevance, `${path29}[${index}].relevance`);
|
|
15297
|
+
return {
|
|
15298
|
+
path: evidencePath,
|
|
15299
|
+
...symbol !== void 0 ? { symbol } : {},
|
|
15300
|
+
relevance
|
|
15301
|
+
};
|
|
15302
|
+
});
|
|
15303
|
+
}
|
|
15304
|
+
function parseEvidenceRelevance(value, path29) {
|
|
15305
|
+
if (value === void 0) {
|
|
15306
|
+
throw new Error(`${path29} is required`);
|
|
15307
|
+
}
|
|
15308
|
+
if (value !== 1 && value !== 2 && value !== 3) {
|
|
15309
|
+
throw new Error(`${path29} must be 1, 2, or 3`);
|
|
15310
|
+
}
|
|
15311
|
+
return value;
|
|
15312
|
+
}
|
|
14729
15313
|
function parseExpected(input, path29) {
|
|
14730
15314
|
if (!isRecord3(input)) {
|
|
14731
15315
|
throw new Error(`${path29} must be an object`);
|
|
@@ -14734,10 +15318,18 @@ function parseExpected(input, path29) {
|
|
|
14734
15318
|
const acceptableFilesRaw = input.acceptableFiles;
|
|
14735
15319
|
const symbolRaw = input.symbol;
|
|
14736
15320
|
const branchRaw = input.branch;
|
|
14737
|
-
const
|
|
15321
|
+
const expectedRouteRaw = input.expectedRoute;
|
|
15322
|
+
const expectedOutcomeRaw = input.expectedOutcome;
|
|
15323
|
+
const recoveryExpectationRaw = input.recoveryExpectation;
|
|
15324
|
+
const gradedEvidenceRaw = input.gradedEvidence;
|
|
15325
|
+
const filePath = parseStringOrUndefined(filePathRaw, `${path29}.filePath`);
|
|
14738
15326
|
const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
14739
|
-
|
|
14740
|
-
|
|
15327
|
+
const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path29}.gradedEvidence`);
|
|
15328
|
+
const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path29}.expectedOutcome`);
|
|
15329
|
+
if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
|
|
15330
|
+
throw new Error(
|
|
15331
|
+
`${path29} must include expected.filePath, expected.acceptableFiles, or expected.gradedEvidence`
|
|
15332
|
+
);
|
|
14741
15333
|
}
|
|
14742
15334
|
if (acceptableFilesRaw !== void 0 && !isStringArray4(acceptableFilesRaw)) {
|
|
14743
15335
|
throw new Error(`${path29}.acceptableFiles must be an array of strings`);
|
|
@@ -14748,13 +15340,25 @@ function parseExpected(input, path29) {
|
|
|
14748
15340
|
if (branchRaw !== void 0 && typeof branchRaw !== "string") {
|
|
14749
15341
|
throw new Error(`${path29}.branch must be a string when provided`);
|
|
14750
15342
|
}
|
|
15343
|
+
const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path29}.expectedRoute`);
|
|
15344
|
+
const recoveryExpectation = parseRecoveryExpectation(
|
|
15345
|
+
recoveryExpectationRaw,
|
|
15346
|
+
`${path29}.recoveryExpectation`
|
|
15347
|
+
);
|
|
14751
15348
|
return {
|
|
14752
15349
|
filePath,
|
|
14753
15350
|
acceptableFiles,
|
|
14754
15351
|
symbol: typeof symbolRaw === "string" ? symbolRaw : void 0,
|
|
14755
|
-
branch: typeof branchRaw === "string" ? branchRaw : void 0
|
|
15352
|
+
branch: typeof branchRaw === "string" ? branchRaw : void 0,
|
|
15353
|
+
expectedRoute,
|
|
15354
|
+
expectedOutcome,
|
|
15355
|
+
recoveryExpectation,
|
|
15356
|
+
...gradedEvidence.length > 0 ? { gradedEvidence } : {}
|
|
14756
15357
|
};
|
|
14757
15358
|
}
|
|
15359
|
+
function parseQueryLanguage(value, path29) {
|
|
15360
|
+
return parseStringOrUndefined(value, path29);
|
|
15361
|
+
}
|
|
14758
15362
|
function parseQuery(input, index) {
|
|
14759
15363
|
const path29 = `queries[${index}]`;
|
|
14760
15364
|
if (!isRecord3(input)) {
|
|
@@ -14765,6 +15369,10 @@ function parseQuery(input, index) {
|
|
|
14765
15369
|
const queryType = input.queryType;
|
|
14766
15370
|
const retrievalMode = input.retrievalMode;
|
|
14767
15371
|
const expected = input.expected;
|
|
15372
|
+
const language = input.language;
|
|
15373
|
+
const difficulty = input.difficulty;
|
|
15374
|
+
const tags = input.tags;
|
|
15375
|
+
const args = input.args;
|
|
14768
15376
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
14769
15377
|
throw new Error(`${path29}.id must be a non-empty string`);
|
|
14770
15378
|
}
|
|
@@ -14776,6 +15384,10 @@ function parseQuery(input, index) {
|
|
|
14776
15384
|
query,
|
|
14777
15385
|
queryType: parseQueryType(queryType, `${path29}.queryType`),
|
|
14778
15386
|
retrievalMode: parseRetrievalMode(retrievalMode, `${path29}.retrievalMode`),
|
|
15387
|
+
language: parseQueryLanguage(language, `${path29}.language`),
|
|
15388
|
+
difficulty: parseQueryDifficulty(difficulty, `${path29}.difficulty`),
|
|
15389
|
+
args: parseQueryArgs(args, `${path29}.args`),
|
|
15390
|
+
tags: parseQueryTags(tags, `${path29}.tags`),
|
|
14779
15391
|
expected: parseExpected(expected, `${path29}.expected`)
|
|
14780
15392
|
};
|
|
14781
15393
|
}
|
|
@@ -14787,9 +15399,7 @@ function parseGoldenDataset(raw, sourceLabel) {
|
|
|
14787
15399
|
const name = raw.name;
|
|
14788
15400
|
const description = raw.description;
|
|
14789
15401
|
const queriesRaw = raw.queries;
|
|
14790
|
-
|
|
14791
|
-
throw new Error(`${sourceLabel}.version must be a non-empty string`);
|
|
14792
|
-
}
|
|
15402
|
+
const validatedVersion = parseSemanticVersion(version, `${sourceLabel}.version`);
|
|
14793
15403
|
if (typeof name !== "string" || name.trim().length === 0) {
|
|
14794
15404
|
throw new Error(`${sourceLabel}.name must be a non-empty string`);
|
|
14795
15405
|
}
|
|
@@ -14811,7 +15421,7 @@ function parseGoldenDataset(raw, sourceLabel) {
|
|
|
14811
15421
|
idSet.add(query.id);
|
|
14812
15422
|
}
|
|
14813
15423
|
return {
|
|
14814
|
-
version,
|
|
15424
|
+
version: validatedVersion,
|
|
14815
15425
|
name,
|
|
14816
15426
|
description: typeof description === "string" ? description : void 0,
|
|
14817
15427
|
queries
|
|
@@ -14871,16 +15481,8 @@ function parseBudget(raw, sourceLabel) {
|
|
|
14871
15481
|
"p95LatencyMaxAbsoluteMs",
|
|
14872
15482
|
sourceLabel
|
|
14873
15483
|
),
|
|
14874
|
-
minHitAt5: parseThresholdValue(
|
|
14875
|
-
|
|
14876
|
-
"minHitAt5",
|
|
14877
|
-
sourceLabel
|
|
14878
|
-
),
|
|
14879
|
-
minMrrAt10: parseThresholdValue(
|
|
14880
|
-
thresholds.minMrrAt10,
|
|
14881
|
-
"minMrrAt10",
|
|
14882
|
-
sourceLabel
|
|
14883
|
-
),
|
|
15484
|
+
minHitAt5: parseThresholdValue(thresholds.minHitAt5, "minHitAt5", sourceLabel),
|
|
15485
|
+
minMrrAt10: parseThresholdValue(thresholds.minMrrAt10, "minMrrAt10", sourceLabel),
|
|
14884
15486
|
minRawDistinctTop3Ratio: parseThresholdValue(
|
|
14885
15487
|
thresholds.minRawDistinctTop3Ratio,
|
|
14886
15488
|
"minRawDistinctTop3Ratio",
|
|
@@ -14930,6 +15532,26 @@ function loadBudget(budgetPath) {
|
|
|
14930
15532
|
}
|
|
14931
15533
|
|
|
14932
15534
|
// src/eval/runner.ts
|
|
15535
|
+
function normalizeForFingerprint(value) {
|
|
15536
|
+
if (Array.isArray(value)) {
|
|
15537
|
+
return value.map((entry) => normalizeForFingerprint(entry));
|
|
15538
|
+
}
|
|
15539
|
+
if (value && typeof value === "object") {
|
|
15540
|
+
const normalized = {};
|
|
15541
|
+
for (const key of Object.keys(value).sort()) {
|
|
15542
|
+
const normalizedValue = normalizeForFingerprint(value[key]);
|
|
15543
|
+
if (normalizedValue !== void 0) {
|
|
15544
|
+
normalized[key] = normalizedValue;
|
|
15545
|
+
}
|
|
15546
|
+
}
|
|
15547
|
+
return normalized;
|
|
15548
|
+
}
|
|
15549
|
+
return value;
|
|
15550
|
+
}
|
|
15551
|
+
function buildDatasetFingerprint(dataset) {
|
|
15552
|
+
const canonical = JSON.stringify(normalizeForFingerprint(dataset));
|
|
15553
|
+
return crypto.createHash("sha256").update(canonical).digest("hex");
|
|
15554
|
+
}
|
|
14933
15555
|
async function runEvaluation(options) {
|
|
14934
15556
|
const datasetPath = toAbsolute(options.projectRoot, options.datasetPath);
|
|
14935
15557
|
const againstPath = options.againstPath ? toAbsolute(options.projectRoot, options.againstPath) : void 0;
|
|
@@ -14954,27 +15576,40 @@ async function runEvaluation(options) {
|
|
|
14954
15576
|
const start = import_perf_hooks2.performance.now();
|
|
14955
15577
|
const contextResult = query.retrievalMode === "context" ? await resolveSearchContext({
|
|
14956
15578
|
query: query.query,
|
|
15579
|
+
symbol: query.args?.symbol,
|
|
15580
|
+
fileType: query.args?.fileType,
|
|
15581
|
+
directory: query.args?.directory,
|
|
14957
15582
|
limit: 10,
|
|
14958
15583
|
tokenBudget: DEFAULT_CONTEXT_PACK_TOKEN_BUDGET
|
|
14959
15584
|
}, {
|
|
14960
|
-
lookup: (symbol, limit,
|
|
15585
|
+
lookup: (symbol, limit, scope) => indexer.search(symbol, limit, {
|
|
14961
15586
|
metadataOnly: true,
|
|
14962
15587
|
filterByBranch: !!query.expected.branch,
|
|
14963
|
-
definitionIntent: true
|
|
15588
|
+
definitionIntent: true,
|
|
15589
|
+
fileType: scope.fileType,
|
|
15590
|
+
directory: scope.directory
|
|
14964
15591
|
}),
|
|
14965
|
-
search: (searchQuery, limit,
|
|
15592
|
+
search: (searchQuery, limit, scope) => indexer.search(searchQuery, limit, {
|
|
14966
15593
|
metadataOnly: true,
|
|
14967
15594
|
filterByBranch: !!query.expected.branch,
|
|
14968
|
-
definitionIntent: false
|
|
15595
|
+
definitionIntent: false,
|
|
15596
|
+
fileType: scope.fileType,
|
|
15597
|
+
directory: scope.directory
|
|
14969
15598
|
})
|
|
14970
15599
|
}) : void 0;
|
|
14971
15600
|
const result = contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
14972
15601
|
metadataOnly: true,
|
|
14973
|
-
filterByBranch: !!query.expected.branch
|
|
15602
|
+
filterByBranch: !!query.expected.branch,
|
|
15603
|
+
fileType: query.args?.fileType,
|
|
15604
|
+
directory: query.args?.directory
|
|
14974
15605
|
});
|
|
14975
15606
|
const elapsed = import_perf_hooks2.performance.now() - start;
|
|
14976
15607
|
const resolvedRoute = contextResult?.details?.route === "definition" ? "definition" : "search";
|
|
14977
15608
|
const routedQuery = contextResult?.details?.routedQuery ?? query.query;
|
|
15609
|
+
const successfulRecoveryAttempt = contextResult?.details?.recovery?.successfulAttemptIndex;
|
|
15610
|
+
const recoveryAttempts = contextResult?.details?.recovery?.attempts ?? [];
|
|
15611
|
+
const recoveryRelaxed = successfulRecoveryAttempt === void 0 ? false : (recoveryAttempts[successfulRecoveryAttempt]?.relaxedFields.length ?? 0) > 0;
|
|
15612
|
+
const recoveryUsed = recoveryAttempts.length > 1 || recoveryAttempts.some((attempt) => attempt.relaxedFields.length > 0);
|
|
14978
15613
|
const materialized = result.map((item) => ({
|
|
14979
15614
|
filePath: item.filePath,
|
|
14980
15615
|
startLine: item.startLine,
|
|
@@ -14991,7 +15626,9 @@ async function runEvaluation(options) {
|
|
|
14991
15626
|
responseTokens: contextResult.details.tokenEstimate,
|
|
14992
15627
|
candidateCount: contextResult.details.candidateCount ?? 0,
|
|
14993
15628
|
deduplicatedCount: contextResult.details.deduplicatedCount ?? 0,
|
|
14994
|
-
omittedCount: contextResult.details.omittedCount ?? 0
|
|
15629
|
+
omittedCount: contextResult.details.omittedCount ?? 0,
|
|
15630
|
+
recoveryUsed,
|
|
15631
|
+
recoveryRelaxed
|
|
14995
15632
|
} : void 0));
|
|
14996
15633
|
}
|
|
14997
15634
|
const logger = indexer.getLogger();
|
|
@@ -15003,6 +15640,7 @@ async function runEvaluation(options) {
|
|
|
15003
15640
|
datasetPath,
|
|
15004
15641
|
datasetName: dataset.name,
|
|
15005
15642
|
datasetVersion: dataset.version,
|
|
15643
|
+
datasetFingerprint: buildDatasetFingerprint(dataset),
|
|
15006
15644
|
queryCount: dataset.queries.length,
|
|
15007
15645
|
topK: 10,
|
|
15008
15646
|
searchConfig: {
|
|
@@ -17920,7 +18558,10 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
17920
18558
|
if (isGitRepo(projectRoot)) {
|
|
17921
18559
|
gitWatcher = new GitHeadWatcher(projectRoot);
|
|
17922
18560
|
gitWatcher.start(async (oldBranch, newBranch) => {
|
|
17923
|
-
|
|
18561
|
+
getIndexer().getLogger().branch("info", "Branch changed", {
|
|
18562
|
+
oldBranch,
|
|
18563
|
+
newBranch
|
|
18564
|
+
});
|
|
17924
18565
|
requestReindex();
|
|
17925
18566
|
});
|
|
17926
18567
|
}
|