opencode-codebase-index 0.19.0 → 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 +766 -127
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +766 -127
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +408 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +408 -59
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +362 -51
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +362 -51
- 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");
|
|
@@ -3342,9 +3357,7 @@ var PROJECT_MARKERS = [
|
|
|
3342
3357
|
"pom.xml",
|
|
3343
3358
|
"build.gradle",
|
|
3344
3359
|
"CMakeLists.txt",
|
|
3345
|
-
"Makefile"
|
|
3346
|
-
".opencode",
|
|
3347
|
-
".codebase-index"
|
|
3360
|
+
"Makefile"
|
|
3348
3361
|
];
|
|
3349
3362
|
function hasProjectMarker(projectRoot) {
|
|
3350
3363
|
for (const marker of PROJECT_MARKERS) {
|
|
@@ -4039,9 +4052,21 @@ function parseFiles(files) {
|
|
|
4039
4052
|
return result.map((f) => ({
|
|
4040
4053
|
path: f.path,
|
|
4041
4054
|
chunks: f.chunks.map(mapChunk),
|
|
4055
|
+
symbols: (f.symbols ?? []).map(mapParsedSymbol),
|
|
4042
4056
|
hash: f.hash
|
|
4043
4057
|
}));
|
|
4044
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
|
+
}
|
|
4045
4070
|
function mapChunk(c) {
|
|
4046
4071
|
return {
|
|
4047
4072
|
content: c.content,
|
|
@@ -6852,6 +6877,7 @@ var INDEX_METADATA_VERSION = "1";
|
|
|
6852
6877
|
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
6853
6878
|
var SWIFT_PARSER_VERSION = "1";
|
|
6854
6879
|
var METAL_PARSER_VERSION = "1";
|
|
6880
|
+
var SYMBOL_EXTRACTOR_VERSION = "1";
|
|
6855
6881
|
var RANKING_TOKEN_CACHE_LIMIT = 4096;
|
|
6856
6882
|
var RANK_HYBRID_CACHE_LIMIT = 256;
|
|
6857
6883
|
function createPendingChunkStorageText(texts) {
|
|
@@ -7153,7 +7179,7 @@ function classifyQueryIntentRaw(query) {
|
|
|
7153
7179
|
return "neutral";
|
|
7154
7180
|
}
|
|
7155
7181
|
function isImplementationChunkType(chunkType) {
|
|
7156
|
-
return [
|
|
7182
|
+
return CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(chunkType) || [
|
|
7157
7183
|
"export_statement",
|
|
7158
7184
|
"function",
|
|
7159
7185
|
"function_declaration",
|
|
@@ -7598,7 +7624,7 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
|
|
|
7598
7624
|
const remainder = combined.filter((candidate) => !promotedIds.has(candidate.id));
|
|
7599
7625
|
return [...promoted, ...remainder];
|
|
7600
7626
|
}
|
|
7601
|
-
function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
7627
|
+
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
7602
7628
|
if (!prioritizeSourcePaths) {
|
|
7603
7629
|
return [];
|
|
7604
7630
|
}
|
|
@@ -7612,14 +7638,14 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7612
7638
|
const primaryHint = extractPrimaryIdentifierQueryHint(query);
|
|
7613
7639
|
const upsertChunkCandidate = (chunk, identifier, normalizedIdentifier, baseScore) => {
|
|
7614
7640
|
if (branchChunkIds && !branchChunkIds.has(chunk.chunkId)) {
|
|
7615
|
-
return;
|
|
7641
|
+
return false;
|
|
7616
7642
|
}
|
|
7617
7643
|
const chunkType = chunk.nodeType ?? "other";
|
|
7618
7644
|
if (!isImplementationChunkType(chunkType)) {
|
|
7619
|
-
return;
|
|
7645
|
+
return false;
|
|
7620
7646
|
}
|
|
7621
7647
|
if (!isLikelyImplementationPath2(chunk.filePath)) {
|
|
7622
|
-
return;
|
|
7648
|
+
return false;
|
|
7623
7649
|
}
|
|
7624
7650
|
const nameLower = (chunk.name ?? "").toLowerCase();
|
|
7625
7651
|
const exactName = nameLower === identifier || nameLower.replace(/_/g, "") === normalizedIdentifier;
|
|
@@ -7641,6 +7667,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7641
7667
|
}
|
|
7642
7668
|
});
|
|
7643
7669
|
}
|
|
7670
|
+
return true;
|
|
7644
7671
|
};
|
|
7645
7672
|
const normalizedHints = identifierHints.flatMap((hint) => [
|
|
7646
7673
|
hint,
|
|
@@ -7662,12 +7689,46 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7662
7689
|
dedupSymbols.set(symbol.id, symbol);
|
|
7663
7690
|
}
|
|
7664
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
|
+
}
|
|
7665
7698
|
const chunks = database.getChunksByFile(symbol.filePath);
|
|
7699
|
+
let foundCoveringChunk = false;
|
|
7666
7700
|
for (const chunk of chunks) {
|
|
7667
7701
|
if (chunk.startLine > symbol.startLine || chunk.endLine < symbol.endLine) {
|
|
7668
7702
|
continue;
|
|
7669
7703
|
}
|
|
7670
|
-
|
|
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
|
+
});
|
|
7671
7732
|
}
|
|
7672
7733
|
}
|
|
7673
7734
|
const dedupChunksByName = /* @__PURE__ */ new Map();
|
|
@@ -7675,6 +7736,9 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7675
7736
|
dedupChunksByName.set(chunk.chunkId, chunk);
|
|
7676
7737
|
}
|
|
7677
7738
|
for (const chunk of dedupChunksByName.values()) {
|
|
7739
|
+
if (filePathHint && !pathMatchesHint(chunk.filePath, filePathHint)) {
|
|
7740
|
+
continue;
|
|
7741
|
+
}
|
|
7678
7742
|
upsertChunkCandidate(chunk, identifier, normalizedIdentifier);
|
|
7679
7743
|
}
|
|
7680
7744
|
}
|
|
@@ -8216,6 +8280,10 @@ var Indexer = class _Indexer {
|
|
|
8216
8280
|
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
8217
8281
|
return `${key}.${projectHash}`;
|
|
8218
8282
|
}
|
|
8283
|
+
getSymbolExtractorVersionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8284
|
+
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
8285
|
+
return `index.symbolExtractorVersion.${hashContent(branchKey).slice(0, 24)}`;
|
|
8286
|
+
}
|
|
8219
8287
|
hasProjectForceReembedPending() {
|
|
8220
8288
|
return this.config.scope === "global" && this.database?.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
8221
8289
|
}
|
|
@@ -9559,7 +9627,8 @@ var Indexer = class _Indexer {
|
|
|
9559
9627
|
}
|
|
9560
9628
|
const branchKey = this.getBranchCatalogKey();
|
|
9561
9629
|
const alreadyIndexed = database.getBranchChunkIds(branchKey).length > 0 && database.getBranchSymbolIds(branchKey).length > 0;
|
|
9562
|
-
|
|
9630
|
+
const symbolsCurrent = database.getMetadata(this.getSymbolExtractorVersionMetadataKey()) === SYMBOL_EXTRACTOR_VERSION;
|
|
9631
|
+
if (alreadyIndexed && symbolsCurrent && this.getStoredBranchCommit(database) === normalizedCommit) {
|
|
9563
9632
|
return { prepared: false };
|
|
9564
9633
|
}
|
|
9565
9634
|
const stats = await this.indexUnlocked(onProgress, [], true);
|
|
@@ -9618,6 +9687,8 @@ var Indexer = class _Indexer {
|
|
|
9618
9687
|
const reparseCachedSwiftFiles = database.getMetadata(swiftParserMetadataKey) !== SWIFT_PARSER_VERSION;
|
|
9619
9688
|
const metalParserMetadataKey = this.getMetalParserVersionMetadataKey();
|
|
9620
9689
|
const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
|
|
9690
|
+
const symbolExtractorMetadataKey = this.getSymbolExtractorVersionMetadataKey();
|
|
9691
|
+
const refreshCachedSymbols = database.getMetadata(symbolExtractorMetadataKey) !== SYMBOL_EXTRACTOR_VERSION;
|
|
9621
9692
|
if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some(
|
|
9622
9693
|
(filePath) => path13.extname(filePath).toLowerCase() === ".swift"
|
|
9623
9694
|
)) {
|
|
@@ -9661,7 +9732,7 @@ var Indexer = class _Indexer {
|
|
|
9661
9732
|
);
|
|
9662
9733
|
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path13.extname(canonicalPath).toLowerCase() === ".swift";
|
|
9663
9734
|
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path13.extname(canonicalPath).toLowerCase() === ".metal";
|
|
9664
|
-
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade) {
|
|
9735
|
+
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
9665
9736
|
unchangedFilePaths.add(canonicalPath);
|
|
9666
9737
|
this.logger.recordCacheHit();
|
|
9667
9738
|
} else {
|
|
@@ -9872,37 +9943,27 @@ var Indexer = class _Indexer {
|
|
|
9872
9943
|
const parsed = parsedFiles[i];
|
|
9873
9944
|
const changedFile = changedFiles[i];
|
|
9874
9945
|
const fileSymbols = [];
|
|
9875
|
-
for (const
|
|
9876
|
-
if (!
|
|
9877
|
-
const existingMetalSymbol = chunk.language === "metal" ? fileSymbols.find(
|
|
9878
|
-
(symbol2) => symbol2.name === chunk.name && symbol2.kind === chunk.chunkType && symbol2.startLine <= chunk.endLine && chunk.startLine <= symbol2.endLine
|
|
9879
|
-
) : void 0;
|
|
9880
|
-
if (existingMetalSymbol) {
|
|
9881
|
-
existingMetalSymbol.startLine = Math.min(existingMetalSymbol.startLine, chunk.startLine);
|
|
9882
|
-
existingMetalSymbol.endLine = Math.max(existingMetalSymbol.endLine, chunk.endLine);
|
|
9883
|
-
existingMetalSymbol.startCol = Math.min(existingMetalSymbol.startCol, chunk.startCol ?? 0);
|
|
9884
|
-
existingMetalSymbol.endCol = Math.max(existingMetalSymbol.endCol, chunk.endCol ?? 0);
|
|
9885
|
-
continue;
|
|
9886
|
-
}
|
|
9946
|
+
for (const parsedSymbol of parsed.symbols) {
|
|
9947
|
+
if (!CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(parsedSymbol.kind)) continue;
|
|
9887
9948
|
const preparedNamespace = this.getPreparedBranchNamespace();
|
|
9888
9949
|
const symbolId = `sym_${hashContent(
|
|
9889
|
-
(preparedNamespace ? `${preparedNamespace}:` : "") + parsed.path + ":" +
|
|
9950
|
+
(preparedNamespace ? `${preparedNamespace}:` : "") + parsed.path + ":" + parsedSymbol.name + ":" + parsedSymbol.kind + ":" + parsedSymbol.startLine + ":" + parsedSymbol.startCol + ":" + changedFile.hash
|
|
9890
9951
|
).slice(0, 16)}`;
|
|
9891
9952
|
const symbol = {
|
|
9892
9953
|
id: symbolId,
|
|
9893
9954
|
filePath: parsed.path,
|
|
9894
|
-
name:
|
|
9895
|
-
kind:
|
|
9896
|
-
startLine:
|
|
9897
|
-
startCol:
|
|
9898
|
-
endLine:
|
|
9899
|
-
endCol:
|
|
9900
|
-
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
|
|
9901
9962
|
};
|
|
9902
9963
|
fileSymbols.push(symbol);
|
|
9903
9964
|
allSymbolIds.add(symbolId);
|
|
9904
9965
|
}
|
|
9905
|
-
const fileLanguage = parsed.chunks[0]?.language;
|
|
9966
|
+
const fileLanguage = parsed.symbols[0]?.language ?? parsed.chunks[0]?.language;
|
|
9906
9967
|
const isCaseInsensitiveLanguage = !!fileLanguage && CASE_INSENSITIVE_LANGUAGES.has(fileLanguage);
|
|
9907
9968
|
const normalizeSymbolKey = (name) => isCaseInsensitiveLanguage ? name.toLowerCase() : name;
|
|
9908
9969
|
const symbolsByName = /* @__PURE__ */ new Map();
|
|
@@ -10018,6 +10079,7 @@ var Indexer = class _Indexer {
|
|
|
10018
10079
|
}
|
|
10019
10080
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
10020
10081
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
10082
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
10021
10083
|
this.saveBranchCommit(database, indexedCommit);
|
|
10022
10084
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
10023
10085
|
this.indexCompatibility = { compatible: true };
|
|
@@ -10054,6 +10116,7 @@ var Indexer = class _Indexer {
|
|
|
10054
10116
|
}
|
|
10055
10117
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
10056
10118
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
10119
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
10057
10120
|
this.saveBranchCommit(database, indexedCommit);
|
|
10058
10121
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
10059
10122
|
this.indexCompatibility = { compatible: true };
|
|
@@ -10333,6 +10396,7 @@ var Indexer = class _Indexer {
|
|
|
10333
10396
|
}
|
|
10334
10397
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
10335
10398
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
10399
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
10336
10400
|
this.saveBranchCommit(database, indexedCommit);
|
|
10337
10401
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
10338
10402
|
this.indexCompatibility = { compatible: true };
|
|
@@ -10471,10 +10535,11 @@ var Indexer = class _Indexer {
|
|
|
10471
10535
|
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
10472
10536
|
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
10473
10537
|
let branchChunkIds = null;
|
|
10538
|
+
let branchSymbolIds = null;
|
|
10474
10539
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
);
|
|
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)));
|
|
10478
10543
|
}
|
|
10479
10544
|
const prefilterStartTime = import_perf_hooks.performance.now();
|
|
10480
10545
|
const shouldPrefilterByBranch = branchChunkIds !== null && (this.config.scope === "global" || branchChunkIds.size > 0);
|
|
@@ -10546,6 +10611,7 @@ var Indexer = class _Indexer {
|
|
|
10546
10611
|
query,
|
|
10547
10612
|
database,
|
|
10548
10613
|
branchChunkIds,
|
|
10614
|
+
branchSymbolIds,
|
|
10549
10615
|
maxResults,
|
|
10550
10616
|
union,
|
|
10551
10617
|
sourceIntent
|
|
@@ -10559,7 +10625,7 @@ var Indexer = class _Indexer {
|
|
|
10559
10625
|
(r) => isLikelyImplementationPath2(r.metadata.filePath) && isImplementationChunkType(r.metadata.chunkType)
|
|
10560
10626
|
);
|
|
10561
10627
|
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly : baseFiltered).slice(0, maxResults);
|
|
10562
|
-
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) : [];
|
|
10563
10629
|
const finalResults = filtered.length > 0 ? filtered : identifierFallback;
|
|
10564
10630
|
const totalSearchMs = import_perf_hooks.performance.now() - searchStartTime;
|
|
10565
10631
|
this.logger.recordSearch(totalSearchMs, {
|
|
@@ -10716,7 +10782,7 @@ var Indexer = class _Indexer {
|
|
|
10716
10782
|
const extension = path13.extname(filePath).toLowerCase();
|
|
10717
10783
|
return extension === ".php" || extension === ".c" || extension === ".cc" || extension === ".cpp" || extension === ".cxx";
|
|
10718
10784
|
});
|
|
10719
|
-
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) {
|
|
10720
10786
|
return { readable: true, current: false, reason: "migration-required" };
|
|
10721
10787
|
}
|
|
10722
10788
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -11424,7 +11490,10 @@ var Indexer = class _Indexer {
|
|
|
11424
11490
|
const requestedRef = opts.pr !== void 0 ? expectedCommit : headRefName ?? resolvedBranch;
|
|
11425
11491
|
const storedCommit = this.getStoredBranchCommit(database, catalogIdentity);
|
|
11426
11492
|
const catalogIdentityMatches = storedCommit === expectedCommit;
|
|
11427
|
-
|
|
11493
|
+
const symbolsCurrent = database.getMetadata(
|
|
11494
|
+
this.getSymbolExtractorVersionMetadataKey(catalogIdentity)
|
|
11495
|
+
) === SYMBOL_EXTRACTOR_VERSION;
|
|
11496
|
+
if (branchSymbols.length === 0 || !catalogIdentityMatches || !symbolsCurrent) {
|
|
11428
11497
|
if (!resolvedBranch || resolvedBranch === "default") {
|
|
11429
11498
|
throw new Error("Run index_codebase first to build the call graph and symbol index for this project.");
|
|
11430
11499
|
}
|
|
@@ -11740,8 +11809,17 @@ function fitTextToContextBudget(text, tokenBudget) {
|
|
|
11740
11809
|
function normalizedLineRange(result) {
|
|
11741
11810
|
return result.startLine <= result.endLine ? { start: result.startLine, end: result.endLine } : { start: result.endLine, end: result.startLine };
|
|
11742
11811
|
}
|
|
11743
|
-
function rankContextCandidates(results) {
|
|
11744
|
-
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
|
+
});
|
|
11745
11823
|
}
|
|
11746
11824
|
function deduplicateContextCandidates(candidates) {
|
|
11747
11825
|
const acceptedByFile = /* @__PURE__ */ new Map();
|
|
@@ -11830,7 +11908,12 @@ function buildContextPack(results, options = {}) {
|
|
|
11830
11908
|
const maxResults = Math.max(0, Math.floor(options.maxResults ?? results.length));
|
|
11831
11909
|
const includeExactSearchHandoff = options.includeExactSearchHandoff ?? false;
|
|
11832
11910
|
const candidateCount = results.length;
|
|
11833
|
-
const deduplicated = deduplicateContextCandidates(
|
|
11911
|
+
const deduplicated = deduplicateContextCandidates(
|
|
11912
|
+
rankContextCandidates(
|
|
11913
|
+
results,
|
|
11914
|
+
options.preferImplementationPaths ?? false
|
|
11915
|
+
)
|
|
11916
|
+
);
|
|
11834
11917
|
const diversified = diversifyContextCandidates(deduplicated);
|
|
11835
11918
|
const duplicateCount = candidateCount - deduplicated.length;
|
|
11836
11919
|
const selectable = diversified.slice(0, maxResults);
|
|
@@ -12202,7 +12285,7 @@ ${truncateContent(r.content)}
|
|
|
12202
12285
|
}
|
|
12203
12286
|
|
|
12204
12287
|
// src/utils/effectiveness-metrics.ts
|
|
12205
|
-
var EFFECTIVENESS_METRICS_SCHEMA_VERSION =
|
|
12288
|
+
var EFFECTIVENESS_METRICS_SCHEMA_VERSION = 3;
|
|
12206
12289
|
var MAX_EFFECTIVENESS_COUNTER = 1e9;
|
|
12207
12290
|
var EFFECTIVENESS_TOOL_ROUTES = [
|
|
12208
12291
|
"context-conceptual",
|
|
@@ -12248,6 +12331,9 @@ var EFFECTIVENESS_SCOPE_RELAXATION_BUCKETS = ["none", "directory", "file-type",
|
|
|
12248
12331
|
function emptyCounterMap(values) {
|
|
12249
12332
|
return Object.fromEntries(values.map((value) => [value, 0]));
|
|
12250
12333
|
}
|
|
12334
|
+
function emptyRouteCounterMap(routes, values) {
|
|
12335
|
+
return Object.fromEntries(routes.map((route) => [route, emptyCounterMap(values)]));
|
|
12336
|
+
}
|
|
12251
12337
|
function boundedNumber(value) {
|
|
12252
12338
|
if (value === void 0 || !Number.isFinite(value)) return 0;
|
|
12253
12339
|
return Math.max(0, Math.floor(value));
|
|
@@ -12296,6 +12382,11 @@ function allowedValue(value, allowed, fallback) {
|
|
|
12296
12382
|
function cloneCounterMap(counters) {
|
|
12297
12383
|
return { ...counters };
|
|
12298
12384
|
}
|
|
12385
|
+
function cloneRouteCounterMap(counters) {
|
|
12386
|
+
return Object.fromEntries(
|
|
12387
|
+
EFFECTIVENESS_TOOL_ROUTES.map((route) => [route, cloneCounterMap(counters[route])])
|
|
12388
|
+
);
|
|
12389
|
+
}
|
|
12299
12390
|
var EffectivenessMetrics = class {
|
|
12300
12391
|
constructor(counterCap = MAX_EFFECTIVENESS_COUNTER) {
|
|
12301
12392
|
this.counterCap = counterCap;
|
|
@@ -12311,7 +12402,7 @@ var EffectivenessMetrics = class {
|
|
|
12311
12402
|
lifetime: "process",
|
|
12312
12403
|
reset: "index_metrics-reset-or-process-exit",
|
|
12313
12404
|
maxCounterValue: this.counterCap,
|
|
12314
|
-
dimensions: "bounded-
|
|
12405
|
+
dimensions: "bounded-route-and-bucketed-performance-only"
|
|
12315
12406
|
},
|
|
12316
12407
|
totalCalls: 0,
|
|
12317
12408
|
toolRoute: emptyCounterMap(EFFECTIVENESS_TOOL_ROUTES),
|
|
@@ -12323,7 +12414,14 @@ var EffectivenessMetrics = class {
|
|
|
12323
12414
|
tokenBudget: emptyCounterMap(EFFECTIVENESS_TOKEN_BUDGET_BUCKETS),
|
|
12324
12415
|
returnedTokenEstimate: emptyCounterMap(EFFECTIVENESS_RETURNED_TOKEN_BUCKETS),
|
|
12325
12416
|
exactHandoffEmitted: emptyCounterMap(EFFECTIVENESS_BOOLEAN_BUCKETS),
|
|
12326
|
-
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
|
+
)
|
|
12327
12425
|
};
|
|
12328
12426
|
}
|
|
12329
12427
|
increment(counters, key) {
|
|
@@ -12345,12 +12443,19 @@ var EffectivenessMetrics = class {
|
|
|
12345
12443
|
this.increment(this.snapshot.hostMode, host);
|
|
12346
12444
|
this.increment(this.snapshot.outcome, outcome);
|
|
12347
12445
|
this.increment(this.snapshot.recoveryUsed, recoveryUsed);
|
|
12348
|
-
|
|
12349
|
-
|
|
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);
|
|
12350
12451
|
this.increment(this.snapshot.tokenBudget, tokenBudgetBucket(event.tokenBudget));
|
|
12351
|
-
this.increment(this.snapshot.returnedTokenEstimate,
|
|
12452
|
+
this.increment(this.snapshot.returnedTokenEstimate, returnedTokenBucketValue);
|
|
12352
12453
|
this.increment(this.snapshot.exactHandoffEmitted, exactHandoffEmitted);
|
|
12353
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);
|
|
12354
12459
|
}
|
|
12355
12460
|
getSnapshot() {
|
|
12356
12461
|
return {
|
|
@@ -12365,7 +12470,11 @@ var EffectivenessMetrics = class {
|
|
|
12365
12470
|
tokenBudget: cloneCounterMap(this.snapshot.tokenBudget),
|
|
12366
12471
|
returnedTokenEstimate: cloneCounterMap(this.snapshot.returnedTokenEstimate),
|
|
12367
12472
|
exactHandoffEmitted: cloneCounterMap(this.snapshot.exactHandoffEmitted),
|
|
12368
|
-
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)
|
|
12369
12478
|
};
|
|
12370
12479
|
}
|
|
12371
12480
|
reset() {
|
|
@@ -12384,6 +12493,7 @@ function resetProcessEffectivenessMetrics() {
|
|
|
12384
12493
|
}
|
|
12385
12494
|
function formatEffectivenessMetrics(snapshot) {
|
|
12386
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("; ");
|
|
12387
12497
|
const lines = [
|
|
12388
12498
|
`Privacy-safe effectiveness (schema v${snapshot.schemaVersion}):`,
|
|
12389
12499
|
` Retention: ${snapshot.retention.storage}, ${snapshot.retention.lifetime}-lifetime; reset with index_metrics(reset=true) or process exit`,
|
|
@@ -12398,6 +12508,10 @@ function formatEffectivenessMetrics(snapshot) {
|
|
|
12398
12508
|
` Latency bucket: ${formatCounters(snapshot.latency)}`,
|
|
12399
12509
|
` Token-budget bucket: ${formatCounters(snapshot.tokenBudget)}`,
|
|
12400
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)}`,
|
|
12401
12515
|
` Exact handoff emitted: ${formatCounters(snapshot.exactHandoffEmitted)}`,
|
|
12402
12516
|
` Scope relaxation: ${formatCounters(snapshot.scopeRelaxation)}`,
|
|
12403
12517
|
" Privacy: no queries, response text, source, symbols, paths, repository names, user identity, or stable identifiers are retained."
|
|
@@ -12409,6 +12523,103 @@ function formatEffectivenessMetrics(snapshot) {
|
|
|
12409
12523
|
var import_fs11 = require("fs");
|
|
12410
12524
|
var os6 = __toESM(require("os"), 1);
|
|
12411
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
|
|
12412
12623
|
var MAX_RETRY_DELAY_MS = 1e4;
|
|
12413
12624
|
var SHUTDOWN_WAIT_MS = 2e3;
|
|
12414
12625
|
var coordinators = /* @__PURE__ */ new Map();
|
|
@@ -12525,7 +12736,13 @@ var AutoIndexCoordinator = class {
|
|
|
12525
12736
|
activation = Promise.resolve();
|
|
12526
12737
|
inFlight = null;
|
|
12527
12738
|
activeRequest = null;
|
|
12739
|
+
batteryCheck = null;
|
|
12740
|
+
batteryIndexJob = null;
|
|
12741
|
+
batteryDeferredRequest = null;
|
|
12742
|
+
batteryRetryTimer = null;
|
|
12743
|
+
resolveBatteryRetry = null;
|
|
12528
12744
|
pendingRequest = null;
|
|
12745
|
+
pendingFollowUp = null;
|
|
12529
12746
|
abortController = null;
|
|
12530
12747
|
stopped = false;
|
|
12531
12748
|
constructor(registration) {
|
|
@@ -12538,6 +12755,7 @@ var AutoIndexCoordinator = class {
|
|
|
12538
12755
|
};
|
|
12539
12756
|
}
|
|
12540
12757
|
update(registration) {
|
|
12758
|
+
const pauseOnBatteryChanged = registration.config.indexing.pauseBackgroundIndexingOnBattery !== this.registration.config.indexing.pauseBackgroundIndexingOnBattery;
|
|
12541
12759
|
this.registration = registration;
|
|
12542
12760
|
this.status.enabled = registration.config.indexing.autoIndex;
|
|
12543
12761
|
this.status.blockedReason = registration.blockedReason;
|
|
@@ -12553,6 +12771,9 @@ var AutoIndexCoordinator = class {
|
|
|
12553
12771
|
this.setState("idle", { source: void 0 });
|
|
12554
12772
|
}
|
|
12555
12773
|
}
|
|
12774
|
+
if (pauseOnBatteryChanged) {
|
|
12775
|
+
this.cancelBatteryRetry();
|
|
12776
|
+
}
|
|
12556
12777
|
}
|
|
12557
12778
|
activateAfter(activation) {
|
|
12558
12779
|
this.activation = activation;
|
|
@@ -12574,7 +12795,26 @@ var AutoIndexCoordinator = class {
|
|
|
12574
12795
|
if (this.stopped) {
|
|
12575
12796
|
return Promise.resolve({ outcome: "stopped" });
|
|
12576
12797
|
}
|
|
12577
|
-
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;
|
|
12578
12818
|
}
|
|
12579
12819
|
enqueueRequest(request) {
|
|
12580
12820
|
if (this.stopped || !this.canRun(request)) {
|
|
@@ -12592,6 +12832,11 @@ var AutoIndexCoordinator = class {
|
|
|
12592
12832
|
}
|
|
12593
12833
|
if (request.source === "watcher") {
|
|
12594
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
|
+
});
|
|
12595
12840
|
}
|
|
12596
12841
|
return this.inFlight;
|
|
12597
12842
|
}
|
|
@@ -12608,6 +12853,8 @@ var AutoIndexCoordinator = class {
|
|
|
12608
12853
|
}
|
|
12609
12854
|
async stop(waitForCompletion = false) {
|
|
12610
12855
|
this.stopped = true;
|
|
12856
|
+
this.batteryDeferredRequest = null;
|
|
12857
|
+
this.cancelBatteryRetry();
|
|
12611
12858
|
this.pendingRequest = null;
|
|
12612
12859
|
this.abortController?.abort();
|
|
12613
12860
|
this.setState("stopped", {
|
|
@@ -12637,10 +12884,20 @@ var AutoIndexCoordinator = class {
|
|
|
12637
12884
|
this.inFlight = null;
|
|
12638
12885
|
this.activeRequest = null;
|
|
12639
12886
|
this.abortController = null;
|
|
12887
|
+
if (this.batteryIndexJob === job) {
|
|
12888
|
+
this.batteryIndexJob = null;
|
|
12889
|
+
this.batteryCheck = null;
|
|
12890
|
+
}
|
|
12640
12891
|
const pending = this.pendingRequest;
|
|
12641
12892
|
this.pendingRequest = null;
|
|
12642
12893
|
if (pending && !this.stopped) {
|
|
12643
|
-
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
|
+
});
|
|
12644
12901
|
}
|
|
12645
12902
|
});
|
|
12646
12903
|
return job;
|
|
@@ -12804,6 +13061,68 @@ var AutoIndexCoordinator = class {
|
|
|
12804
13061
|
}
|
|
12805
13062
|
return this.registration.safeToRun && this.registration.config.indexing.autoIndex;
|
|
12806
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
|
+
}
|
|
12807
13126
|
};
|
|
12808
13127
|
function getCoordinator(projectRoot, host) {
|
|
12809
13128
|
const key = coordinatorKeysByProject.get(projectLookupKey(projectRoot, host));
|
|
@@ -12813,6 +13132,9 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
|
|
|
12813
13132
|
const projectKey = projectLookupKey(projectRoot, host);
|
|
12814
13133
|
const safety = getProjectSafety(projectRoot, config);
|
|
12815
13134
|
const registration = {
|
|
13135
|
+
backgroundIndexingPolicy: createBackgroundIndexingPolicy(
|
|
13136
|
+
config.indexing.pauseBackgroundIndexingOnBattery
|
|
13137
|
+
),
|
|
12816
13138
|
config,
|
|
12817
13139
|
getIndexer,
|
|
12818
13140
|
projectRoot,
|
|
@@ -14036,6 +14358,7 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
14036
14358
|
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed);
|
|
14037
14359
|
if (results.length > 0) {
|
|
14038
14360
|
const heading = buildPackHeading("conceptual", decisions);
|
|
14361
|
+
const intent = analyzeQueryIntent(attempt.queryText);
|
|
14039
14362
|
return toResult(
|
|
14040
14363
|
"conceptual",
|
|
14041
14364
|
attempt.queryText,
|
|
@@ -14043,7 +14366,8 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
14043
14366
|
tokenBudget,
|
|
14044
14367
|
maxResults: limit,
|
|
14045
14368
|
heading,
|
|
14046
|
-
includeExactSearchHandoff: true
|
|
14369
|
+
includeExactSearchHandoff: true,
|
|
14370
|
+
preferImplementationPaths: intent.preferSourcePaths || intent.primary !== "docs" && intent.primary !== "test"
|
|
14047
14371
|
})
|
|
14048
14372
|
);
|
|
14049
14373
|
}
|
|
@@ -14344,17 +14668,26 @@ function percentile(values, p) {
|
|
|
14344
14668
|
function normalizePath2(input) {
|
|
14345
14669
|
return normalizePathSeparators(input);
|
|
14346
14670
|
}
|
|
14347
|
-
function
|
|
14671
|
+
function uniqueResultsByEvidence(results) {
|
|
14348
14672
|
const seen = /* @__PURE__ */ new Set();
|
|
14349
14673
|
const unique = [];
|
|
14350
14674
|
for (const result of results) {
|
|
14351
|
-
const
|
|
14352
|
-
if (seen.has(
|
|
14353
|
-
seen.add(
|
|
14675
|
+
const key = `${normalizePath2(result.filePath)}::${result.name ?? ""}`;
|
|
14676
|
+
if (seen.has(key)) continue;
|
|
14677
|
+
seen.add(key);
|
|
14354
14678
|
unique.push(result);
|
|
14355
14679
|
}
|
|
14356
14680
|
return unique;
|
|
14357
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
|
+
}
|
|
14358
14691
|
function distinctTopKRatio(results, k) {
|
|
14359
14692
|
const top = results.slice(0, k);
|
|
14360
14693
|
if (top.length === 0) return 0;
|
|
@@ -14365,60 +14698,169 @@ function pathMatchesExpected(actualPath, expectedPath) {
|
|
|
14365
14698
|
const actual = normalizePath2(actualPath);
|
|
14366
14699
|
const expected = normalizePath2(expectedPath);
|
|
14367
14700
|
if (actual === expected) return true;
|
|
14368
|
-
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;
|
|
14369
14767
|
}
|
|
14370
|
-
function
|
|
14371
|
-
|
|
14372
|
-
|
|
14373
|
-
|
|
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;
|
|
14374
14779
|
}
|
|
14375
|
-
function
|
|
14376
|
-
return
|
|
14780
|
+
function hasGradeBasedEvidence(query) {
|
|
14781
|
+
return (query.expected.gradedEvidence?.length ?? 0) > 0;
|
|
14377
14782
|
}
|
|
14378
|
-
function
|
|
14379
|
-
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);
|
|
14380
14795
|
for (let i = 0; i < top.length; i += 1) {
|
|
14381
|
-
if (isRelevantResult(top[i].filePath,
|
|
14796
|
+
if (isRelevantResult(top[i].filePath, top[i].name, relevant, isSymbolIntendedQuery)) {
|
|
14382
14797
|
return 1 / (i + 1);
|
|
14383
14798
|
}
|
|
14384
14799
|
}
|
|
14385
14800
|
return 0;
|
|
14386
14801
|
}
|
|
14387
|
-
function ndcgAtK(results,
|
|
14388
|
-
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
|
+
);
|
|
14389
14807
|
const dcg = top.reduce((sum, result, i) => {
|
|
14390
|
-
|
|
14391
|
-
|
|
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);
|
|
14392
14822
|
}, 0);
|
|
14393
|
-
const
|
|
14394
|
-
|
|
14395
|
-
|
|
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),
|
|
14396
14829
|
0
|
|
14397
14830
|
);
|
|
14398
|
-
|
|
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;
|
|
14399
14839
|
}
|
|
14400
14840
|
function isDocsOrTestsPath(filePath) {
|
|
14401
14841
|
const lowered = normalizePath2(filePath).toLowerCase();
|
|
14402
14842
|
return lowered.includes("/docs/") || lowered.includes("/test/") || lowered.includes("/tests/") || lowered.includes("readme") || lowered.includes("/benchmarks/");
|
|
14403
14843
|
}
|
|
14404
14844
|
function classifyFailureBucket(query, results, k) {
|
|
14405
|
-
const
|
|
14406
|
-
const
|
|
14407
|
-
|
|
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
|
+
);
|
|
14408
14852
|
if (!hasRelevantTopK) {
|
|
14853
|
+
const hasExpectedFileTopK = top.some((result) => isExpectedFile(result.filePath, relevant));
|
|
14854
|
+
if (hasExpectedFileTopK && hasSymbolRequirement(query)) {
|
|
14855
|
+
return "wrong-symbol";
|
|
14856
|
+
}
|
|
14409
14857
|
return "no-relevant-hit-top-k";
|
|
14410
14858
|
}
|
|
14411
|
-
if (query.expected.symbol) {
|
|
14412
|
-
const hasSymbol = top.some(
|
|
14413
|
-
(result) => isRelevantResult(result.filePath, relevantPaths) && result.name === query.expected.symbol
|
|
14414
|
-
);
|
|
14415
|
-
if (!hasSymbol) return "wrong-symbol";
|
|
14416
|
-
}
|
|
14417
14859
|
const top1 = top[0];
|
|
14418
|
-
if (top1 && !
|
|
14860
|
+
if (top1 && !isExpectedFile(top1.filePath, relevant) && isDocsOrTestsPath(top1.filePath)) {
|
|
14419
14861
|
return "docs-tests-outranking-source";
|
|
14420
14862
|
}
|
|
14421
|
-
if (top1 && !
|
|
14863
|
+
if (top1 && !isExpectedFile(top1.filePath, relevant)) {
|
|
14422
14864
|
return "wrong-file";
|
|
14423
14865
|
}
|
|
14424
14866
|
return void 0;
|
|
@@ -14427,9 +14869,12 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
14427
14869
|
resolvedRoute: "search",
|
|
14428
14870
|
routedQuery: query.query
|
|
14429
14871
|
}, context) {
|
|
14430
|
-
const
|
|
14431
|
-
const
|
|
14432
|
-
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
|
+
);
|
|
14433
14878
|
const perQuery = {
|
|
14434
14879
|
id: query.id,
|
|
14435
14880
|
query: query.query,
|
|
@@ -14437,13 +14882,19 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
14437
14882
|
retrievalMode: query.retrievalMode ?? "search",
|
|
14438
14883
|
resolvedRoute: route.resolvedRoute,
|
|
14439
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,
|
|
14440
14891
|
latencyMs,
|
|
14441
14892
|
hitAt1: hitAt(1),
|
|
14442
14893
|
hitAt3: hitAt(3),
|
|
14443
14894
|
hitAt5: hitAt(5),
|
|
14444
14895
|
hitAt10: hitAt(10),
|
|
14445
|
-
reciprocalRankAt10: reciprocalRankAtK(deduped,
|
|
14446
|
-
ndcgAt10: ndcgAtK(deduped,
|
|
14896
|
+
reciprocalRankAt10: reciprocalRankAtK(deduped, relevant, isSymbolIntendedQuery, 10),
|
|
14897
|
+
ndcgAt10: ndcgAtK(query, deduped, relevant, isSymbolIntendedQuery, 10),
|
|
14447
14898
|
failureBucket: classifyFailureBucket(query, results, k),
|
|
14448
14899
|
rawTop3DistinctRatio: distinctTopKRatio(results, 3),
|
|
14449
14900
|
tokenBudget: context?.tokenBudget,
|
|
@@ -14461,6 +14912,11 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
14461
14912
|
function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingTokensUsed, costPer1MTokensUsd) {
|
|
14462
14913
|
const count = perQuery.length;
|
|
14463
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;
|
|
14464
14920
|
const sum = {
|
|
14465
14921
|
hitAt1: 0,
|
|
14466
14922
|
hitAt3: 0,
|
|
@@ -14482,27 +14938,52 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
14482
14938
|
const contextResponseTokens = contextQueries.map((item) => item.responseTokens);
|
|
14483
14939
|
const totalContextResponseTokens = contextResponseTokens.reduce((sum2, value) => sum2 + value, 0);
|
|
14484
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;
|
|
14485
14947
|
for (const query of perQuery) {
|
|
14486
|
-
if (query.
|
|
14487
|
-
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
|
|
14491
|
-
|
|
14492
|
-
|
|
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);
|
|
14493
14957
|
sum.rawDistinctTop3Ratio += query.rawTop3DistinctRatio;
|
|
14494
14958
|
if (query.failureBucket) {
|
|
14495
14959
|
failureBuckets[query.failureBucket] += 1;
|
|
14496
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
|
+
}
|
|
14497
14975
|
}
|
|
14498
14976
|
const queryTokens = queries.reduce((acc, q) => acc + estimateTokens(q.query), 0);
|
|
14499
14977
|
return {
|
|
14500
|
-
hitAt1:
|
|
14501
|
-
hitAt3:
|
|
14502
|
-
hitAt5:
|
|
14503
|
-
hitAt10:
|
|
14504
|
-
mrrAt10:
|
|
14505
|
-
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,
|
|
14506
14987
|
distinctTop3Ratio: safeDiv(sum.distinctTop3Ratio),
|
|
14507
14988
|
rawDistinctTop3Ratio: safeDiv(sum.rawDistinctTop3Ratio),
|
|
14508
14989
|
latencyMs: {
|
|
@@ -14709,6 +15190,9 @@ function isRecord3(value) {
|
|
|
14709
15190
|
function isStringArray4(value) {
|
|
14710
15191
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
14711
15192
|
}
|
|
15193
|
+
function isNonEmptyString(value) {
|
|
15194
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
15195
|
+
}
|
|
14712
15196
|
function asPositiveNumber(value, path29) {
|
|
14713
15197
|
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
14714
15198
|
throw new Error(`${path29} must be a non-negative number`);
|
|
@@ -14723,11 +15207,109 @@ function parseQueryType(value, path29) {
|
|
|
14723
15207
|
`${path29} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
|
|
14724
15208
|
);
|
|
14725
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
|
+
}
|
|
14726
15270
|
function parseRetrievalMode(value, path29) {
|
|
14727
15271
|
if (value === void 0 || value === "search") return "search";
|
|
14728
15272
|
if (value === "context") return value;
|
|
14729
15273
|
throw new Error(`${path29} must be one of: search, context`);
|
|
14730
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
|
+
}
|
|
14731
15313
|
function parseExpected(input, path29) {
|
|
14732
15314
|
if (!isRecord3(input)) {
|
|
14733
15315
|
throw new Error(`${path29} must be an object`);
|
|
@@ -14736,10 +15318,18 @@ function parseExpected(input, path29) {
|
|
|
14736
15318
|
const acceptableFilesRaw = input.acceptableFiles;
|
|
14737
15319
|
const symbolRaw = input.symbol;
|
|
14738
15320
|
const branchRaw = input.branch;
|
|
14739
|
-
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`);
|
|
14740
15326
|
const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
14741
|
-
|
|
14742
|
-
|
|
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
|
+
);
|
|
14743
15333
|
}
|
|
14744
15334
|
if (acceptableFilesRaw !== void 0 && !isStringArray4(acceptableFilesRaw)) {
|
|
14745
15335
|
throw new Error(`${path29}.acceptableFiles must be an array of strings`);
|
|
@@ -14750,13 +15340,25 @@ function parseExpected(input, path29) {
|
|
|
14750
15340
|
if (branchRaw !== void 0 && typeof branchRaw !== "string") {
|
|
14751
15341
|
throw new Error(`${path29}.branch must be a string when provided`);
|
|
14752
15342
|
}
|
|
15343
|
+
const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path29}.expectedRoute`);
|
|
15344
|
+
const recoveryExpectation = parseRecoveryExpectation(
|
|
15345
|
+
recoveryExpectationRaw,
|
|
15346
|
+
`${path29}.recoveryExpectation`
|
|
15347
|
+
);
|
|
14753
15348
|
return {
|
|
14754
15349
|
filePath,
|
|
14755
15350
|
acceptableFiles,
|
|
14756
15351
|
symbol: typeof symbolRaw === "string" ? symbolRaw : void 0,
|
|
14757
|
-
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 } : {}
|
|
14758
15357
|
};
|
|
14759
15358
|
}
|
|
15359
|
+
function parseQueryLanguage(value, path29) {
|
|
15360
|
+
return parseStringOrUndefined(value, path29);
|
|
15361
|
+
}
|
|
14760
15362
|
function parseQuery(input, index) {
|
|
14761
15363
|
const path29 = `queries[${index}]`;
|
|
14762
15364
|
if (!isRecord3(input)) {
|
|
@@ -14767,6 +15369,10 @@ function parseQuery(input, index) {
|
|
|
14767
15369
|
const queryType = input.queryType;
|
|
14768
15370
|
const retrievalMode = input.retrievalMode;
|
|
14769
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;
|
|
14770
15376
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
14771
15377
|
throw new Error(`${path29}.id must be a non-empty string`);
|
|
14772
15378
|
}
|
|
@@ -14778,6 +15384,10 @@ function parseQuery(input, index) {
|
|
|
14778
15384
|
query,
|
|
14779
15385
|
queryType: parseQueryType(queryType, `${path29}.queryType`),
|
|
14780
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`),
|
|
14781
15391
|
expected: parseExpected(expected, `${path29}.expected`)
|
|
14782
15392
|
};
|
|
14783
15393
|
}
|
|
@@ -14789,9 +15399,7 @@ function parseGoldenDataset(raw, sourceLabel) {
|
|
|
14789
15399
|
const name = raw.name;
|
|
14790
15400
|
const description = raw.description;
|
|
14791
15401
|
const queriesRaw = raw.queries;
|
|
14792
|
-
|
|
14793
|
-
throw new Error(`${sourceLabel}.version must be a non-empty string`);
|
|
14794
|
-
}
|
|
15402
|
+
const validatedVersion = parseSemanticVersion(version, `${sourceLabel}.version`);
|
|
14795
15403
|
if (typeof name !== "string" || name.trim().length === 0) {
|
|
14796
15404
|
throw new Error(`${sourceLabel}.name must be a non-empty string`);
|
|
14797
15405
|
}
|
|
@@ -14813,7 +15421,7 @@ function parseGoldenDataset(raw, sourceLabel) {
|
|
|
14813
15421
|
idSet.add(query.id);
|
|
14814
15422
|
}
|
|
14815
15423
|
return {
|
|
14816
|
-
version,
|
|
15424
|
+
version: validatedVersion,
|
|
14817
15425
|
name,
|
|
14818
15426
|
description: typeof description === "string" ? description : void 0,
|
|
14819
15427
|
queries
|
|
@@ -14873,16 +15481,8 @@ function parseBudget(raw, sourceLabel) {
|
|
|
14873
15481
|
"p95LatencyMaxAbsoluteMs",
|
|
14874
15482
|
sourceLabel
|
|
14875
15483
|
),
|
|
14876
|
-
minHitAt5: parseThresholdValue(
|
|
14877
|
-
|
|
14878
|
-
"minHitAt5",
|
|
14879
|
-
sourceLabel
|
|
14880
|
-
),
|
|
14881
|
-
minMrrAt10: parseThresholdValue(
|
|
14882
|
-
thresholds.minMrrAt10,
|
|
14883
|
-
"minMrrAt10",
|
|
14884
|
-
sourceLabel
|
|
14885
|
-
),
|
|
15484
|
+
minHitAt5: parseThresholdValue(thresholds.minHitAt5, "minHitAt5", sourceLabel),
|
|
15485
|
+
minMrrAt10: parseThresholdValue(thresholds.minMrrAt10, "minMrrAt10", sourceLabel),
|
|
14886
15486
|
minRawDistinctTop3Ratio: parseThresholdValue(
|
|
14887
15487
|
thresholds.minRawDistinctTop3Ratio,
|
|
14888
15488
|
"minRawDistinctTop3Ratio",
|
|
@@ -14932,6 +15532,26 @@ function loadBudget(budgetPath) {
|
|
|
14932
15532
|
}
|
|
14933
15533
|
|
|
14934
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
|
+
}
|
|
14935
15555
|
async function runEvaluation(options) {
|
|
14936
15556
|
const datasetPath = toAbsolute(options.projectRoot, options.datasetPath);
|
|
14937
15557
|
const againstPath = options.againstPath ? toAbsolute(options.projectRoot, options.againstPath) : void 0;
|
|
@@ -14956,27 +15576,40 @@ async function runEvaluation(options) {
|
|
|
14956
15576
|
const start = import_perf_hooks2.performance.now();
|
|
14957
15577
|
const contextResult = query.retrievalMode === "context" ? await resolveSearchContext({
|
|
14958
15578
|
query: query.query,
|
|
15579
|
+
symbol: query.args?.symbol,
|
|
15580
|
+
fileType: query.args?.fileType,
|
|
15581
|
+
directory: query.args?.directory,
|
|
14959
15582
|
limit: 10,
|
|
14960
15583
|
tokenBudget: DEFAULT_CONTEXT_PACK_TOKEN_BUDGET
|
|
14961
15584
|
}, {
|
|
14962
|
-
lookup: (symbol, limit,
|
|
15585
|
+
lookup: (symbol, limit, scope) => indexer.search(symbol, limit, {
|
|
14963
15586
|
metadataOnly: true,
|
|
14964
15587
|
filterByBranch: !!query.expected.branch,
|
|
14965
|
-
definitionIntent: true
|
|
15588
|
+
definitionIntent: true,
|
|
15589
|
+
fileType: scope.fileType,
|
|
15590
|
+
directory: scope.directory
|
|
14966
15591
|
}),
|
|
14967
|
-
search: (searchQuery, limit,
|
|
15592
|
+
search: (searchQuery, limit, scope) => indexer.search(searchQuery, limit, {
|
|
14968
15593
|
metadataOnly: true,
|
|
14969
15594
|
filterByBranch: !!query.expected.branch,
|
|
14970
|
-
definitionIntent: false
|
|
15595
|
+
definitionIntent: false,
|
|
15596
|
+
fileType: scope.fileType,
|
|
15597
|
+
directory: scope.directory
|
|
14971
15598
|
})
|
|
14972
15599
|
}) : void 0;
|
|
14973
15600
|
const result = contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
14974
15601
|
metadataOnly: true,
|
|
14975
|
-
filterByBranch: !!query.expected.branch
|
|
15602
|
+
filterByBranch: !!query.expected.branch,
|
|
15603
|
+
fileType: query.args?.fileType,
|
|
15604
|
+
directory: query.args?.directory
|
|
14976
15605
|
});
|
|
14977
15606
|
const elapsed = import_perf_hooks2.performance.now() - start;
|
|
14978
15607
|
const resolvedRoute = contextResult?.details?.route === "definition" ? "definition" : "search";
|
|
14979
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);
|
|
14980
15613
|
const materialized = result.map((item) => ({
|
|
14981
15614
|
filePath: item.filePath,
|
|
14982
15615
|
startLine: item.startLine,
|
|
@@ -14993,7 +15626,9 @@ async function runEvaluation(options) {
|
|
|
14993
15626
|
responseTokens: contextResult.details.tokenEstimate,
|
|
14994
15627
|
candidateCount: contextResult.details.candidateCount ?? 0,
|
|
14995
15628
|
deduplicatedCount: contextResult.details.deduplicatedCount ?? 0,
|
|
14996
|
-
omittedCount: contextResult.details.omittedCount ?? 0
|
|
15629
|
+
omittedCount: contextResult.details.omittedCount ?? 0,
|
|
15630
|
+
recoveryUsed,
|
|
15631
|
+
recoveryRelaxed
|
|
14997
15632
|
} : void 0));
|
|
14998
15633
|
}
|
|
14999
15634
|
const logger = indexer.getLogger();
|
|
@@ -15005,6 +15640,7 @@ async function runEvaluation(options) {
|
|
|
15005
15640
|
datasetPath,
|
|
15006
15641
|
datasetName: dataset.name,
|
|
15007
15642
|
datasetVersion: dataset.version,
|
|
15643
|
+
datasetFingerprint: buildDatasetFingerprint(dataset),
|
|
15008
15644
|
queryCount: dataset.queries.length,
|
|
15009
15645
|
topK: 10,
|
|
15010
15646
|
searchConfig: {
|
|
@@ -17922,7 +18558,10 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
17922
18558
|
if (isGitRepo(projectRoot)) {
|
|
17923
18559
|
gitWatcher = new GitHeadWatcher(projectRoot);
|
|
17924
18560
|
gitWatcher.start(async (oldBranch, newBranch) => {
|
|
17925
|
-
|
|
18561
|
+
getIndexer().getLogger().branch("info", "Branch changed", {
|
|
18562
|
+
oldBranch,
|
|
18563
|
+
newBranch
|
|
18564
|
+
});
|
|
17926
18565
|
requestReindex();
|
|
17927
18566
|
});
|
|
17928
18567
|
}
|