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