opencode-codebase-index 0.19.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +7 -5
- package/dist/cli.cjs +765 -124
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +765 -124
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +400 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +400 -55
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +361 -48
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +361 -48
- package/dist/pi-extension.js.map +1 -1
- package/native/codebase-index-native.darwin-arm64.node +0 -0
- package/native/codebase-index-native.darwin-x64.node +0 -0
- package/native/codebase-index-native.linux-arm64-gnu.node +0 -0
- package/native/codebase-index-native.linux-x64-gnu.node +0 -0
- package/native/codebase-index-native.win32-x64-msvc.node +0 -0
- package/package.json +4 -3
package/dist/cli.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";
|
|
@@ -4024,9 +4039,21 @@ function parseFiles(files) {
|
|
|
4024
4039
|
return result.map((f) => ({
|
|
4025
4040
|
path: f.path,
|
|
4026
4041
|
chunks: f.chunks.map(mapChunk),
|
|
4042
|
+
symbols: (f.symbols ?? []).map(mapParsedSymbol),
|
|
4027
4043
|
hash: f.hash
|
|
4028
4044
|
}));
|
|
4029
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
|
+
}
|
|
4030
4057
|
function mapChunk(c) {
|
|
4031
4058
|
return {
|
|
4032
4059
|
content: c.content,
|
|
@@ -6847,6 +6874,7 @@ var INDEX_METADATA_VERSION = "1";
|
|
|
6847
6874
|
var EMBEDDING_STRATEGY_VERSION = "2";
|
|
6848
6875
|
var SWIFT_PARSER_VERSION = "1";
|
|
6849
6876
|
var METAL_PARSER_VERSION = "1";
|
|
6877
|
+
var SYMBOL_EXTRACTOR_VERSION = "1";
|
|
6850
6878
|
var RANKING_TOKEN_CACHE_LIMIT = 4096;
|
|
6851
6879
|
var RANK_HYBRID_CACHE_LIMIT = 256;
|
|
6852
6880
|
function createPendingChunkStorageText(texts) {
|
|
@@ -7148,7 +7176,7 @@ function classifyQueryIntentRaw(query) {
|
|
|
7148
7176
|
return "neutral";
|
|
7149
7177
|
}
|
|
7150
7178
|
function isImplementationChunkType(chunkType) {
|
|
7151
|
-
return [
|
|
7179
|
+
return CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(chunkType) || [
|
|
7152
7180
|
"export_statement",
|
|
7153
7181
|
"function",
|
|
7154
7182
|
"function_declaration",
|
|
@@ -7593,7 +7621,7 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
|
|
|
7593
7621
|
const remainder = combined.filter((candidate) => !promotedIds.has(candidate.id));
|
|
7594
7622
|
return [...promoted, ...remainder];
|
|
7595
7623
|
}
|
|
7596
|
-
function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
7624
|
+
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
7597
7625
|
if (!prioritizeSourcePaths) {
|
|
7598
7626
|
return [];
|
|
7599
7627
|
}
|
|
@@ -7607,14 +7635,14 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7607
7635
|
const primaryHint = extractPrimaryIdentifierQueryHint(query);
|
|
7608
7636
|
const upsertChunkCandidate = (chunk, identifier, normalizedIdentifier, baseScore) => {
|
|
7609
7637
|
if (branchChunkIds && !branchChunkIds.has(chunk.chunkId)) {
|
|
7610
|
-
return;
|
|
7638
|
+
return false;
|
|
7611
7639
|
}
|
|
7612
7640
|
const chunkType = chunk.nodeType ?? "other";
|
|
7613
7641
|
if (!isImplementationChunkType(chunkType)) {
|
|
7614
|
-
return;
|
|
7642
|
+
return false;
|
|
7615
7643
|
}
|
|
7616
7644
|
if (!isLikelyImplementationPath2(chunk.filePath)) {
|
|
7617
|
-
return;
|
|
7645
|
+
return false;
|
|
7618
7646
|
}
|
|
7619
7647
|
const nameLower = (chunk.name ?? "").toLowerCase();
|
|
7620
7648
|
const exactName = nameLower === identifier || nameLower.replace(/_/g, "") === normalizedIdentifier;
|
|
@@ -7636,6 +7664,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7636
7664
|
}
|
|
7637
7665
|
});
|
|
7638
7666
|
}
|
|
7667
|
+
return true;
|
|
7639
7668
|
};
|
|
7640
7669
|
const normalizedHints = identifierHints.flatMap((hint) => [
|
|
7641
7670
|
hint,
|
|
@@ -7657,12 +7686,46 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7657
7686
|
dedupSymbols.set(symbol.id, symbol);
|
|
7658
7687
|
}
|
|
7659
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
|
+
}
|
|
7660
7695
|
const chunks = database.getChunksByFile(symbol.filePath);
|
|
7696
|
+
let foundCoveringChunk = false;
|
|
7661
7697
|
for (const chunk of chunks) {
|
|
7662
7698
|
if (chunk.startLine > symbol.startLine || chunk.endLine < symbol.endLine) {
|
|
7663
7699
|
continue;
|
|
7664
7700
|
}
|
|
7665
|
-
|
|
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
|
+
});
|
|
7666
7729
|
}
|
|
7667
7730
|
}
|
|
7668
7731
|
const dedupChunksByName = /* @__PURE__ */ new Map();
|
|
@@ -7670,6 +7733,9 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, limit, fallb
|
|
|
7670
7733
|
dedupChunksByName.set(chunk.chunkId, chunk);
|
|
7671
7734
|
}
|
|
7672
7735
|
for (const chunk of dedupChunksByName.values()) {
|
|
7736
|
+
if (filePathHint && !pathMatchesHint(chunk.filePath, filePathHint)) {
|
|
7737
|
+
continue;
|
|
7738
|
+
}
|
|
7673
7739
|
upsertChunkCandidate(chunk, identifier, normalizedIdentifier);
|
|
7674
7740
|
}
|
|
7675
7741
|
}
|
|
@@ -8211,6 +8277,10 @@ var Indexer = class _Indexer {
|
|
|
8211
8277
|
const projectHash = hashContent(path13.resolve(this.projectRoot)).slice(0, 16);
|
|
8212
8278
|
return `${key}.${projectHash}`;
|
|
8213
8279
|
}
|
|
8280
|
+
getSymbolExtractorVersionMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
8281
|
+
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
8282
|
+
return `index.symbolExtractorVersion.${hashContent(branchKey).slice(0, 24)}`;
|
|
8283
|
+
}
|
|
8214
8284
|
hasProjectForceReembedPending() {
|
|
8215
8285
|
return this.config.scope === "global" && this.database?.getMetadata(this.getProjectForceReembedMetadataKey()) === "true";
|
|
8216
8286
|
}
|
|
@@ -9554,7 +9624,8 @@ var Indexer = class _Indexer {
|
|
|
9554
9624
|
}
|
|
9555
9625
|
const branchKey = this.getBranchCatalogKey();
|
|
9556
9626
|
const alreadyIndexed = database.getBranchChunkIds(branchKey).length > 0 && database.getBranchSymbolIds(branchKey).length > 0;
|
|
9557
|
-
|
|
9627
|
+
const symbolsCurrent = database.getMetadata(this.getSymbolExtractorVersionMetadataKey()) === SYMBOL_EXTRACTOR_VERSION;
|
|
9628
|
+
if (alreadyIndexed && symbolsCurrent && this.getStoredBranchCommit(database) === normalizedCommit) {
|
|
9558
9629
|
return { prepared: false };
|
|
9559
9630
|
}
|
|
9560
9631
|
const stats = await this.indexUnlocked(onProgress, [], true);
|
|
@@ -9613,6 +9684,8 @@ var Indexer = class _Indexer {
|
|
|
9613
9684
|
const reparseCachedSwiftFiles = database.getMetadata(swiftParserMetadataKey) !== SWIFT_PARSER_VERSION;
|
|
9614
9685
|
const metalParserMetadataKey = this.getMetalParserVersionMetadataKey();
|
|
9615
9686
|
const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
|
|
9687
|
+
const symbolExtractorMetadataKey = this.getSymbolExtractorVersionMetadataKey();
|
|
9688
|
+
const refreshCachedSymbols = database.getMetadata(symbolExtractorMetadataKey) !== SYMBOL_EXTRACTOR_VERSION;
|
|
9616
9689
|
if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some(
|
|
9617
9690
|
(filePath) => path13.extname(filePath).toLowerCase() === ".swift"
|
|
9618
9691
|
)) {
|
|
@@ -9656,7 +9729,7 @@ var Indexer = class _Indexer {
|
|
|
9656
9729
|
);
|
|
9657
9730
|
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path13.extname(canonicalPath).toLowerCase() === ".swift";
|
|
9658
9731
|
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path13.extname(canonicalPath).toLowerCase() === ".metal";
|
|
9659
|
-
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade) {
|
|
9732
|
+
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
9660
9733
|
unchangedFilePaths.add(canonicalPath);
|
|
9661
9734
|
this.logger.recordCacheHit();
|
|
9662
9735
|
} else {
|
|
@@ -9867,37 +9940,27 @@ var Indexer = class _Indexer {
|
|
|
9867
9940
|
const parsed = parsedFiles[i];
|
|
9868
9941
|
const changedFile = changedFiles[i];
|
|
9869
9942
|
const fileSymbols = [];
|
|
9870
|
-
for (const
|
|
9871
|
-
if (!
|
|
9872
|
-
const existingMetalSymbol = chunk.language === "metal" ? fileSymbols.find(
|
|
9873
|
-
(symbol2) => symbol2.name === chunk.name && symbol2.kind === chunk.chunkType && symbol2.startLine <= chunk.endLine && chunk.startLine <= symbol2.endLine
|
|
9874
|
-
) : void 0;
|
|
9875
|
-
if (existingMetalSymbol) {
|
|
9876
|
-
existingMetalSymbol.startLine = Math.min(existingMetalSymbol.startLine, chunk.startLine);
|
|
9877
|
-
existingMetalSymbol.endLine = Math.max(existingMetalSymbol.endLine, chunk.endLine);
|
|
9878
|
-
existingMetalSymbol.startCol = Math.min(existingMetalSymbol.startCol, chunk.startCol ?? 0);
|
|
9879
|
-
existingMetalSymbol.endCol = Math.max(existingMetalSymbol.endCol, chunk.endCol ?? 0);
|
|
9880
|
-
continue;
|
|
9881
|
-
}
|
|
9943
|
+
for (const parsedSymbol of parsed.symbols) {
|
|
9944
|
+
if (!CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(parsedSymbol.kind)) continue;
|
|
9882
9945
|
const preparedNamespace = this.getPreparedBranchNamespace();
|
|
9883
9946
|
const symbolId = `sym_${hashContent(
|
|
9884
|
-
(preparedNamespace ? `${preparedNamespace}:` : "") + parsed.path + ":" +
|
|
9947
|
+
(preparedNamespace ? `${preparedNamespace}:` : "") + parsed.path + ":" + parsedSymbol.name + ":" + parsedSymbol.kind + ":" + parsedSymbol.startLine + ":" + parsedSymbol.startCol + ":" + changedFile.hash
|
|
9885
9948
|
).slice(0, 16)}`;
|
|
9886
9949
|
const symbol = {
|
|
9887
9950
|
id: symbolId,
|
|
9888
9951
|
filePath: parsed.path,
|
|
9889
|
-
name:
|
|
9890
|
-
kind:
|
|
9891
|
-
startLine:
|
|
9892
|
-
startCol:
|
|
9893
|
-
endLine:
|
|
9894
|
-
endCol:
|
|
9895
|
-
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
|
|
9896
9959
|
};
|
|
9897
9960
|
fileSymbols.push(symbol);
|
|
9898
9961
|
allSymbolIds.add(symbolId);
|
|
9899
9962
|
}
|
|
9900
|
-
const fileLanguage = parsed.chunks[0]?.language;
|
|
9963
|
+
const fileLanguage = parsed.symbols[0]?.language ?? parsed.chunks[0]?.language;
|
|
9901
9964
|
const isCaseInsensitiveLanguage = !!fileLanguage && CASE_INSENSITIVE_LANGUAGES.has(fileLanguage);
|
|
9902
9965
|
const normalizeSymbolKey = (name) => isCaseInsensitiveLanguage ? name.toLowerCase() : name;
|
|
9903
9966
|
const symbolsByName = /* @__PURE__ */ new Map();
|
|
@@ -10013,6 +10076,7 @@ var Indexer = class _Indexer {
|
|
|
10013
10076
|
}
|
|
10014
10077
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
10015
10078
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
10079
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
10016
10080
|
this.saveBranchCommit(database, indexedCommit);
|
|
10017
10081
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
10018
10082
|
this.indexCompatibility = { compatible: true };
|
|
@@ -10049,6 +10113,7 @@ var Indexer = class _Indexer {
|
|
|
10049
10113
|
}
|
|
10050
10114
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
10051
10115
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
10116
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
10052
10117
|
this.saveBranchCommit(database, indexedCommit);
|
|
10053
10118
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
10054
10119
|
this.indexCompatibility = { compatible: true };
|
|
@@ -10328,6 +10393,7 @@ var Indexer = class _Indexer {
|
|
|
10328
10393
|
}
|
|
10329
10394
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
10330
10395
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
10396
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
10331
10397
|
this.saveBranchCommit(database, indexedCommit);
|
|
10332
10398
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
10333
10399
|
this.indexCompatibility = { compatible: true };
|
|
@@ -10466,10 +10532,11 @@ var Indexer = class _Indexer {
|
|
|
10466
10532
|
const keywordResults = await this.keywordSearch(query, maxResults * 4, store, invertedIndex);
|
|
10467
10533
|
const keywordMs = performance2.now() - keywordStartTime;
|
|
10468
10534
|
let branchChunkIds = null;
|
|
10535
|
+
let branchSymbolIds = null;
|
|
10469
10536
|
if (filterByBranch && (this.config.scope === "global" || this.currentBranch !== "default")) {
|
|
10470
|
-
|
|
10471
|
-
|
|
10472
|
-
);
|
|
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)));
|
|
10473
10540
|
}
|
|
10474
10541
|
const prefilterStartTime = performance2.now();
|
|
10475
10542
|
const shouldPrefilterByBranch = branchChunkIds !== null && (this.config.scope === "global" || branchChunkIds.size > 0);
|
|
@@ -10541,6 +10608,7 @@ var Indexer = class _Indexer {
|
|
|
10541
10608
|
query,
|
|
10542
10609
|
database,
|
|
10543
10610
|
branchChunkIds,
|
|
10611
|
+
branchSymbolIds,
|
|
10544
10612
|
maxResults,
|
|
10545
10613
|
union,
|
|
10546
10614
|
sourceIntent
|
|
@@ -10554,7 +10622,7 @@ var Indexer = class _Indexer {
|
|
|
10554
10622
|
(r) => isLikelyImplementationPath2(r.metadata.filePath) && isImplementationChunkType(r.metadata.chunkType)
|
|
10555
10623
|
);
|
|
10556
10624
|
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly : baseFiltered).slice(0, maxResults);
|
|
10557
|
-
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) : [];
|
|
10558
10626
|
const finalResults = filtered.length > 0 ? filtered : identifierFallback;
|
|
10559
10627
|
const totalSearchMs = performance2.now() - searchStartTime;
|
|
10560
10628
|
this.logger.recordSearch(totalSearchMs, {
|
|
@@ -10711,7 +10779,7 @@ var Indexer = class _Indexer {
|
|
|
10711
10779
|
const extension = path13.extname(filePath).toLowerCase();
|
|
10712
10780
|
return extension === ".php" || extension === ".c" || extension === ".cc" || extension === ".cpp" || extension === ".cxx";
|
|
10713
10781
|
});
|
|
10714
|
-
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) {
|
|
10715
10783
|
return { readable: true, current: false, reason: "migration-required" };
|
|
10716
10784
|
}
|
|
10717
10785
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -11419,7 +11487,10 @@ var Indexer = class _Indexer {
|
|
|
11419
11487
|
const requestedRef = opts.pr !== void 0 ? expectedCommit : headRefName ?? resolvedBranch;
|
|
11420
11488
|
const storedCommit = this.getStoredBranchCommit(database, catalogIdentity);
|
|
11421
11489
|
const catalogIdentityMatches = storedCommit === expectedCommit;
|
|
11422
|
-
|
|
11490
|
+
const symbolsCurrent = database.getMetadata(
|
|
11491
|
+
this.getSymbolExtractorVersionMetadataKey(catalogIdentity)
|
|
11492
|
+
) === SYMBOL_EXTRACTOR_VERSION;
|
|
11493
|
+
if (branchSymbols.length === 0 || !catalogIdentityMatches || !symbolsCurrent) {
|
|
11423
11494
|
if (!resolvedBranch || resolvedBranch === "default") {
|
|
11424
11495
|
throw new Error("Run index_codebase first to build the call graph and symbol index for this project.");
|
|
11425
11496
|
}
|
|
@@ -11735,8 +11806,17 @@ function fitTextToContextBudget(text, tokenBudget) {
|
|
|
11735
11806
|
function normalizedLineRange(result) {
|
|
11736
11807
|
return result.startLine <= result.endLine ? { start: result.startLine, end: result.endLine } : { start: result.endLine, end: result.startLine };
|
|
11737
11808
|
}
|
|
11738
|
-
function rankContextCandidates(results) {
|
|
11739
|
-
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
|
+
});
|
|
11740
11820
|
}
|
|
11741
11821
|
function deduplicateContextCandidates(candidates) {
|
|
11742
11822
|
const acceptedByFile = /* @__PURE__ */ new Map();
|
|
@@ -11825,7 +11905,12 @@ function buildContextPack(results, options = {}) {
|
|
|
11825
11905
|
const maxResults = Math.max(0, Math.floor(options.maxResults ?? results.length));
|
|
11826
11906
|
const includeExactSearchHandoff = options.includeExactSearchHandoff ?? false;
|
|
11827
11907
|
const candidateCount = results.length;
|
|
11828
|
-
const deduplicated = deduplicateContextCandidates(
|
|
11908
|
+
const deduplicated = deduplicateContextCandidates(
|
|
11909
|
+
rankContextCandidates(
|
|
11910
|
+
results,
|
|
11911
|
+
options.preferImplementationPaths ?? false
|
|
11912
|
+
)
|
|
11913
|
+
);
|
|
11829
11914
|
const diversified = diversifyContextCandidates(deduplicated);
|
|
11830
11915
|
const duplicateCount = candidateCount - deduplicated.length;
|
|
11831
11916
|
const selectable = diversified.slice(0, maxResults);
|
|
@@ -12197,7 +12282,7 @@ ${truncateContent(r.content)}
|
|
|
12197
12282
|
}
|
|
12198
12283
|
|
|
12199
12284
|
// src/utils/effectiveness-metrics.ts
|
|
12200
|
-
var EFFECTIVENESS_METRICS_SCHEMA_VERSION =
|
|
12285
|
+
var EFFECTIVENESS_METRICS_SCHEMA_VERSION = 3;
|
|
12201
12286
|
var MAX_EFFECTIVENESS_COUNTER = 1e9;
|
|
12202
12287
|
var EFFECTIVENESS_TOOL_ROUTES = [
|
|
12203
12288
|
"context-conceptual",
|
|
@@ -12243,6 +12328,9 @@ var EFFECTIVENESS_SCOPE_RELAXATION_BUCKETS = ["none", "directory", "file-type",
|
|
|
12243
12328
|
function emptyCounterMap(values) {
|
|
12244
12329
|
return Object.fromEntries(values.map((value) => [value, 0]));
|
|
12245
12330
|
}
|
|
12331
|
+
function emptyRouteCounterMap(routes, values) {
|
|
12332
|
+
return Object.fromEntries(routes.map((route) => [route, emptyCounterMap(values)]));
|
|
12333
|
+
}
|
|
12246
12334
|
function boundedNumber(value) {
|
|
12247
12335
|
if (value === void 0 || !Number.isFinite(value)) return 0;
|
|
12248
12336
|
return Math.max(0, Math.floor(value));
|
|
@@ -12291,6 +12379,11 @@ function allowedValue(value, allowed, fallback) {
|
|
|
12291
12379
|
function cloneCounterMap(counters) {
|
|
12292
12380
|
return { ...counters };
|
|
12293
12381
|
}
|
|
12382
|
+
function cloneRouteCounterMap(counters) {
|
|
12383
|
+
return Object.fromEntries(
|
|
12384
|
+
EFFECTIVENESS_TOOL_ROUTES.map((route) => [route, cloneCounterMap(counters[route])])
|
|
12385
|
+
);
|
|
12386
|
+
}
|
|
12294
12387
|
var EffectivenessMetrics = class {
|
|
12295
12388
|
constructor(counterCap = MAX_EFFECTIVENESS_COUNTER) {
|
|
12296
12389
|
this.counterCap = counterCap;
|
|
@@ -12306,7 +12399,7 @@ var EffectivenessMetrics = class {
|
|
|
12306
12399
|
lifetime: "process",
|
|
12307
12400
|
reset: "index_metrics-reset-or-process-exit",
|
|
12308
12401
|
maxCounterValue: this.counterCap,
|
|
12309
|
-
dimensions: "bounded-
|
|
12402
|
+
dimensions: "bounded-route-and-bucketed-performance-only"
|
|
12310
12403
|
},
|
|
12311
12404
|
totalCalls: 0,
|
|
12312
12405
|
toolRoute: emptyCounterMap(EFFECTIVENESS_TOOL_ROUTES),
|
|
@@ -12318,7 +12411,14 @@ var EffectivenessMetrics = class {
|
|
|
12318
12411
|
tokenBudget: emptyCounterMap(EFFECTIVENESS_TOKEN_BUDGET_BUCKETS),
|
|
12319
12412
|
returnedTokenEstimate: emptyCounterMap(EFFECTIVENESS_RETURNED_TOKEN_BUCKETS),
|
|
12320
12413
|
exactHandoffEmitted: emptyCounterMap(EFFECTIVENESS_BOOLEAN_BUCKETS),
|
|
12321
|
-
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
|
+
)
|
|
12322
12422
|
};
|
|
12323
12423
|
}
|
|
12324
12424
|
increment(counters, key) {
|
|
@@ -12340,12 +12440,19 @@ var EffectivenessMetrics = class {
|
|
|
12340
12440
|
this.increment(this.snapshot.hostMode, host);
|
|
12341
12441
|
this.increment(this.snapshot.outcome, outcome);
|
|
12342
12442
|
this.increment(this.snapshot.recoveryUsed, recoveryUsed);
|
|
12343
|
-
|
|
12344
|
-
|
|
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);
|
|
12345
12448
|
this.increment(this.snapshot.tokenBudget, tokenBudgetBucket(event.tokenBudget));
|
|
12346
|
-
this.increment(this.snapshot.returnedTokenEstimate,
|
|
12449
|
+
this.increment(this.snapshot.returnedTokenEstimate, returnedTokenBucketValue);
|
|
12347
12450
|
this.increment(this.snapshot.exactHandoffEmitted, exactHandoffEmitted);
|
|
12348
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);
|
|
12349
12456
|
}
|
|
12350
12457
|
getSnapshot() {
|
|
12351
12458
|
return {
|
|
@@ -12360,7 +12467,11 @@ var EffectivenessMetrics = class {
|
|
|
12360
12467
|
tokenBudget: cloneCounterMap(this.snapshot.tokenBudget),
|
|
12361
12468
|
returnedTokenEstimate: cloneCounterMap(this.snapshot.returnedTokenEstimate),
|
|
12362
12469
|
exactHandoffEmitted: cloneCounterMap(this.snapshot.exactHandoffEmitted),
|
|
12363
|
-
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)
|
|
12364
12475
|
};
|
|
12365
12476
|
}
|
|
12366
12477
|
reset() {
|
|
@@ -12379,6 +12490,7 @@ function resetProcessEffectivenessMetrics() {
|
|
|
12379
12490
|
}
|
|
12380
12491
|
function formatEffectivenessMetrics(snapshot) {
|
|
12381
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("; ");
|
|
12382
12494
|
const lines = [
|
|
12383
12495
|
`Privacy-safe effectiveness (schema v${snapshot.schemaVersion}):`,
|
|
12384
12496
|
` Retention: ${snapshot.retention.storage}, ${snapshot.retention.lifetime}-lifetime; reset with index_metrics(reset=true) or process exit`,
|
|
@@ -12393,6 +12505,10 @@ function formatEffectivenessMetrics(snapshot) {
|
|
|
12393
12505
|
` Latency bucket: ${formatCounters(snapshot.latency)}`,
|
|
12394
12506
|
` Token-budget bucket: ${formatCounters(snapshot.tokenBudget)}`,
|
|
12395
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)}`,
|
|
12396
12512
|
` Exact handoff emitted: ${formatCounters(snapshot.exactHandoffEmitted)}`,
|
|
12397
12513
|
` Scope relaxation: ${formatCounters(snapshot.scopeRelaxation)}`,
|
|
12398
12514
|
" Privacy: no queries, response text, source, symbols, paths, repository names, user identity, or stable identifiers are retained."
|
|
@@ -12404,6 +12520,103 @@ function formatEffectivenessMetrics(snapshot) {
|
|
|
12404
12520
|
import { existsSync as existsSync8, realpathSync as realpathSync4 } from "fs";
|
|
12405
12521
|
import * as os6 from "os";
|
|
12406
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
|
|
12407
12620
|
var MAX_RETRY_DELAY_MS = 1e4;
|
|
12408
12621
|
var SHUTDOWN_WAIT_MS = 2e3;
|
|
12409
12622
|
var coordinators = /* @__PURE__ */ new Map();
|
|
@@ -12520,7 +12733,13 @@ var AutoIndexCoordinator = class {
|
|
|
12520
12733
|
activation = Promise.resolve();
|
|
12521
12734
|
inFlight = null;
|
|
12522
12735
|
activeRequest = null;
|
|
12736
|
+
batteryCheck = null;
|
|
12737
|
+
batteryIndexJob = null;
|
|
12738
|
+
batteryDeferredRequest = null;
|
|
12739
|
+
batteryRetryTimer = null;
|
|
12740
|
+
resolveBatteryRetry = null;
|
|
12523
12741
|
pendingRequest = null;
|
|
12742
|
+
pendingFollowUp = null;
|
|
12524
12743
|
abortController = null;
|
|
12525
12744
|
stopped = false;
|
|
12526
12745
|
constructor(registration) {
|
|
@@ -12533,6 +12752,7 @@ var AutoIndexCoordinator = class {
|
|
|
12533
12752
|
};
|
|
12534
12753
|
}
|
|
12535
12754
|
update(registration) {
|
|
12755
|
+
const pauseOnBatteryChanged = registration.config.indexing.pauseBackgroundIndexingOnBattery !== this.registration.config.indexing.pauseBackgroundIndexingOnBattery;
|
|
12536
12756
|
this.registration = registration;
|
|
12537
12757
|
this.status.enabled = registration.config.indexing.autoIndex;
|
|
12538
12758
|
this.status.blockedReason = registration.blockedReason;
|
|
@@ -12548,6 +12768,9 @@ var AutoIndexCoordinator = class {
|
|
|
12548
12768
|
this.setState("idle", { source: void 0 });
|
|
12549
12769
|
}
|
|
12550
12770
|
}
|
|
12771
|
+
if (pauseOnBatteryChanged) {
|
|
12772
|
+
this.cancelBatteryRetry();
|
|
12773
|
+
}
|
|
12551
12774
|
}
|
|
12552
12775
|
activateAfter(activation) {
|
|
12553
12776
|
this.activation = activation;
|
|
@@ -12569,7 +12792,26 @@ var AutoIndexCoordinator = class {
|
|
|
12569
12792
|
if (this.stopped) {
|
|
12570
12793
|
return Promise.resolve({ outcome: "stopped" });
|
|
12571
12794
|
}
|
|
12572
|
-
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;
|
|
12573
12815
|
}
|
|
12574
12816
|
enqueueRequest(request) {
|
|
12575
12817
|
if (this.stopped || !this.canRun(request)) {
|
|
@@ -12587,6 +12829,11 @@ var AutoIndexCoordinator = class {
|
|
|
12587
12829
|
}
|
|
12588
12830
|
if (request.source === "watcher") {
|
|
12589
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
|
+
});
|
|
12590
12837
|
}
|
|
12591
12838
|
return this.inFlight;
|
|
12592
12839
|
}
|
|
@@ -12603,6 +12850,8 @@ var AutoIndexCoordinator = class {
|
|
|
12603
12850
|
}
|
|
12604
12851
|
async stop(waitForCompletion = false) {
|
|
12605
12852
|
this.stopped = true;
|
|
12853
|
+
this.batteryDeferredRequest = null;
|
|
12854
|
+
this.cancelBatteryRetry();
|
|
12606
12855
|
this.pendingRequest = null;
|
|
12607
12856
|
this.abortController?.abort();
|
|
12608
12857
|
this.setState("stopped", {
|
|
@@ -12632,10 +12881,20 @@ var AutoIndexCoordinator = class {
|
|
|
12632
12881
|
this.inFlight = null;
|
|
12633
12882
|
this.activeRequest = null;
|
|
12634
12883
|
this.abortController = null;
|
|
12884
|
+
if (this.batteryIndexJob === job) {
|
|
12885
|
+
this.batteryIndexJob = null;
|
|
12886
|
+
this.batteryCheck = null;
|
|
12887
|
+
}
|
|
12635
12888
|
const pending = this.pendingRequest;
|
|
12636
12889
|
this.pendingRequest = null;
|
|
12637
12890
|
if (pending && !this.stopped) {
|
|
12638
|
-
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
|
+
});
|
|
12639
12898
|
}
|
|
12640
12899
|
});
|
|
12641
12900
|
return job;
|
|
@@ -12799,6 +13058,68 @@ var AutoIndexCoordinator = class {
|
|
|
12799
13058
|
}
|
|
12800
13059
|
return this.registration.safeToRun && this.registration.config.indexing.autoIndex;
|
|
12801
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
|
+
}
|
|
12802
13123
|
};
|
|
12803
13124
|
function getCoordinator(projectRoot, host) {
|
|
12804
13125
|
const key = coordinatorKeysByProject.get(projectLookupKey(projectRoot, host));
|
|
@@ -12808,6 +13129,9 @@ function configureAutoIndex(projectRoot, host, config, getIndexer) {
|
|
|
12808
13129
|
const projectKey = projectLookupKey(projectRoot, host);
|
|
12809
13130
|
const safety = getProjectSafety(projectRoot, config);
|
|
12810
13131
|
const registration = {
|
|
13132
|
+
backgroundIndexingPolicy: createBackgroundIndexingPolicy(
|
|
13133
|
+
config.indexing.pauseBackgroundIndexingOnBattery
|
|
13134
|
+
),
|
|
12811
13135
|
config,
|
|
12812
13136
|
getIndexer,
|
|
12813
13137
|
projectRoot,
|
|
@@ -14031,6 +14355,7 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
14031
14355
|
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed);
|
|
14032
14356
|
if (results.length > 0) {
|
|
14033
14357
|
const heading = buildPackHeading("conceptual", decisions);
|
|
14358
|
+
const intent = analyzeQueryIntent(attempt.queryText);
|
|
14034
14359
|
return toResult(
|
|
14035
14360
|
"conceptual",
|
|
14036
14361
|
attempt.queryText,
|
|
@@ -14038,7 +14363,8 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
14038
14363
|
tokenBudget,
|
|
14039
14364
|
maxResults: limit,
|
|
14040
14365
|
heading,
|
|
14041
|
-
includeExactSearchHandoff: true
|
|
14366
|
+
includeExactSearchHandoff: true,
|
|
14367
|
+
preferImplementationPaths: intent.preferSourcePaths || intent.primary !== "docs" && intent.primary !== "test"
|
|
14042
14368
|
})
|
|
14043
14369
|
);
|
|
14044
14370
|
}
|
|
@@ -14339,17 +14665,26 @@ function percentile(values, p) {
|
|
|
14339
14665
|
function normalizePath2(input) {
|
|
14340
14666
|
return normalizePathSeparators(input);
|
|
14341
14667
|
}
|
|
14342
|
-
function
|
|
14668
|
+
function uniqueResultsByEvidence(results) {
|
|
14343
14669
|
const seen = /* @__PURE__ */ new Set();
|
|
14344
14670
|
const unique = [];
|
|
14345
14671
|
for (const result of results) {
|
|
14346
|
-
const
|
|
14347
|
-
if (seen.has(
|
|
14348
|
-
seen.add(
|
|
14672
|
+
const key = `${normalizePath2(result.filePath)}::${result.name ?? ""}`;
|
|
14673
|
+
if (seen.has(key)) continue;
|
|
14674
|
+
seen.add(key);
|
|
14349
14675
|
unique.push(result);
|
|
14350
14676
|
}
|
|
14351
14677
|
return unique;
|
|
14352
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
|
+
}
|
|
14353
14688
|
function distinctTopKRatio(results, k) {
|
|
14354
14689
|
const top = results.slice(0, k);
|
|
14355
14690
|
if (top.length === 0) return 0;
|
|
@@ -14360,60 +14695,169 @@ function pathMatchesExpected(actualPath, expectedPath) {
|
|
|
14360
14695
|
const actual = normalizePath2(actualPath);
|
|
14361
14696
|
const expected = normalizePath2(expectedPath);
|
|
14362
14697
|
if (actual === expected) return true;
|
|
14363
|
-
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;
|
|
14364
14764
|
}
|
|
14365
|
-
function
|
|
14366
|
-
|
|
14367
|
-
|
|
14368
|
-
|
|
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;
|
|
14369
14776
|
}
|
|
14370
|
-
function
|
|
14371
|
-
return
|
|
14777
|
+
function hasGradeBasedEvidence(query) {
|
|
14778
|
+
return (query.expected.gradedEvidence?.length ?? 0) > 0;
|
|
14372
14779
|
}
|
|
14373
|
-
function
|
|
14374
|
-
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);
|
|
14375
14792
|
for (let i = 0; i < top.length; i += 1) {
|
|
14376
|
-
if (isRelevantResult(top[i].filePath,
|
|
14793
|
+
if (isRelevantResult(top[i].filePath, top[i].name, relevant, isSymbolIntendedQuery)) {
|
|
14377
14794
|
return 1 / (i + 1);
|
|
14378
14795
|
}
|
|
14379
14796
|
}
|
|
14380
14797
|
return 0;
|
|
14381
14798
|
}
|
|
14382
|
-
function ndcgAtK(results,
|
|
14383
|
-
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
|
+
);
|
|
14384
14804
|
const dcg = top.reduce((sum, result, i) => {
|
|
14385
|
-
|
|
14386
|
-
|
|
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);
|
|
14387
14819
|
}, 0);
|
|
14388
|
-
const
|
|
14389
|
-
|
|
14390
|
-
|
|
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),
|
|
14391
14826
|
0
|
|
14392
14827
|
);
|
|
14393
|
-
|
|
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;
|
|
14394
14836
|
}
|
|
14395
14837
|
function isDocsOrTestsPath(filePath) {
|
|
14396
14838
|
const lowered = normalizePath2(filePath).toLowerCase();
|
|
14397
14839
|
return lowered.includes("/docs/") || lowered.includes("/test/") || lowered.includes("/tests/") || lowered.includes("readme") || lowered.includes("/benchmarks/");
|
|
14398
14840
|
}
|
|
14399
14841
|
function classifyFailureBucket(query, results, k) {
|
|
14400
|
-
const
|
|
14401
|
-
const
|
|
14402
|
-
|
|
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
|
+
);
|
|
14403
14849
|
if (!hasRelevantTopK) {
|
|
14850
|
+
const hasExpectedFileTopK = top.some((result) => isExpectedFile(result.filePath, relevant));
|
|
14851
|
+
if (hasExpectedFileTopK && hasSymbolRequirement(query)) {
|
|
14852
|
+
return "wrong-symbol";
|
|
14853
|
+
}
|
|
14404
14854
|
return "no-relevant-hit-top-k";
|
|
14405
14855
|
}
|
|
14406
|
-
if (query.expected.symbol) {
|
|
14407
|
-
const hasSymbol = top.some(
|
|
14408
|
-
(result) => isRelevantResult(result.filePath, relevantPaths) && result.name === query.expected.symbol
|
|
14409
|
-
);
|
|
14410
|
-
if (!hasSymbol) return "wrong-symbol";
|
|
14411
|
-
}
|
|
14412
14856
|
const top1 = top[0];
|
|
14413
|
-
if (top1 && !
|
|
14857
|
+
if (top1 && !isExpectedFile(top1.filePath, relevant) && isDocsOrTestsPath(top1.filePath)) {
|
|
14414
14858
|
return "docs-tests-outranking-source";
|
|
14415
14859
|
}
|
|
14416
|
-
if (top1 && !
|
|
14860
|
+
if (top1 && !isExpectedFile(top1.filePath, relevant)) {
|
|
14417
14861
|
return "wrong-file";
|
|
14418
14862
|
}
|
|
14419
14863
|
return void 0;
|
|
@@ -14422,9 +14866,12 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
14422
14866
|
resolvedRoute: "search",
|
|
14423
14867
|
routedQuery: query.query
|
|
14424
14868
|
}, context) {
|
|
14425
|
-
const
|
|
14426
|
-
const
|
|
14427
|
-
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
|
+
);
|
|
14428
14875
|
const perQuery = {
|
|
14429
14876
|
id: query.id,
|
|
14430
14877
|
query: query.query,
|
|
@@ -14432,13 +14879,19 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
14432
14879
|
retrievalMode: query.retrievalMode ?? "search",
|
|
14433
14880
|
resolvedRoute: route.resolvedRoute,
|
|
14434
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,
|
|
14435
14888
|
latencyMs,
|
|
14436
14889
|
hitAt1: hitAt(1),
|
|
14437
14890
|
hitAt3: hitAt(3),
|
|
14438
14891
|
hitAt5: hitAt(5),
|
|
14439
14892
|
hitAt10: hitAt(10),
|
|
14440
|
-
reciprocalRankAt10: reciprocalRankAtK(deduped,
|
|
14441
|
-
ndcgAt10: ndcgAtK(deduped,
|
|
14893
|
+
reciprocalRankAt10: reciprocalRankAtK(deduped, relevant, isSymbolIntendedQuery, 10),
|
|
14894
|
+
ndcgAt10: ndcgAtK(query, deduped, relevant, isSymbolIntendedQuery, 10),
|
|
14442
14895
|
failureBucket: classifyFailureBucket(query, results, k),
|
|
14443
14896
|
rawTop3DistinctRatio: distinctTopKRatio(results, 3),
|
|
14444
14897
|
tokenBudget: context?.tokenBudget,
|
|
@@ -14456,6 +14909,11 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
14456
14909
|
function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingTokensUsed, costPer1MTokensUsd) {
|
|
14457
14910
|
const count = perQuery.length;
|
|
14458
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;
|
|
14459
14917
|
const sum = {
|
|
14460
14918
|
hitAt1: 0,
|
|
14461
14919
|
hitAt3: 0,
|
|
@@ -14477,27 +14935,52 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
14477
14935
|
const contextResponseTokens = contextQueries.map((item) => item.responseTokens);
|
|
14478
14936
|
const totalContextResponseTokens = contextResponseTokens.reduce((sum2, value) => sum2 + value, 0);
|
|
14479
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;
|
|
14480
14944
|
for (const query of perQuery) {
|
|
14481
|
-
if (query.
|
|
14482
|
-
|
|
14483
|
-
|
|
14484
|
-
|
|
14485
|
-
|
|
14486
|
-
|
|
14487
|
-
|
|
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);
|
|
14488
14954
|
sum.rawDistinctTop3Ratio += query.rawTop3DistinctRatio;
|
|
14489
14955
|
if (query.failureBucket) {
|
|
14490
14956
|
failureBuckets[query.failureBucket] += 1;
|
|
14491
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
|
+
}
|
|
14492
14972
|
}
|
|
14493
14973
|
const queryTokens = queries.reduce((acc, q) => acc + estimateTokens(q.query), 0);
|
|
14494
14974
|
return {
|
|
14495
|
-
hitAt1:
|
|
14496
|
-
hitAt3:
|
|
14497
|
-
hitAt5:
|
|
14498
|
-
hitAt10:
|
|
14499
|
-
mrrAt10:
|
|
14500
|
-
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,
|
|
14501
14984
|
distinctTop3Ratio: safeDiv(sum.distinctTop3Ratio),
|
|
14502
14985
|
rawDistinctTop3Ratio: safeDiv(sum.rawDistinctTop3Ratio),
|
|
14503
14986
|
latencyMs: {
|
|
@@ -14704,6 +15187,9 @@ function isRecord3(value) {
|
|
|
14704
15187
|
function isStringArray4(value) {
|
|
14705
15188
|
return Array.isArray(value) && value.every((item) => typeof item === "string");
|
|
14706
15189
|
}
|
|
15190
|
+
function isNonEmptyString(value) {
|
|
15191
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
15192
|
+
}
|
|
14707
15193
|
function asPositiveNumber(value, path29) {
|
|
14708
15194
|
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
14709
15195
|
throw new Error(`${path29} must be a non-negative number`);
|
|
@@ -14718,11 +15204,109 @@ function parseQueryType(value, path29) {
|
|
|
14718
15204
|
`${path29} must be one of: definition, implementation-intent, similarity, keyword-heavy, conceptual`
|
|
14719
15205
|
);
|
|
14720
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
|
+
}
|
|
14721
15267
|
function parseRetrievalMode(value, path29) {
|
|
14722
15268
|
if (value === void 0 || value === "search") return "search";
|
|
14723
15269
|
if (value === "context") return value;
|
|
14724
15270
|
throw new Error(`${path29} must be one of: search, context`);
|
|
14725
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
|
+
}
|
|
14726
15310
|
function parseExpected(input, path29) {
|
|
14727
15311
|
if (!isRecord3(input)) {
|
|
14728
15312
|
throw new Error(`${path29} must be an object`);
|
|
@@ -14731,10 +15315,18 @@ function parseExpected(input, path29) {
|
|
|
14731
15315
|
const acceptableFilesRaw = input.acceptableFiles;
|
|
14732
15316
|
const symbolRaw = input.symbol;
|
|
14733
15317
|
const branchRaw = input.branch;
|
|
14734
|
-
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`);
|
|
14735
15323
|
const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
14736
|
-
|
|
14737
|
-
|
|
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
|
+
);
|
|
14738
15330
|
}
|
|
14739
15331
|
if (acceptableFilesRaw !== void 0 && !isStringArray4(acceptableFilesRaw)) {
|
|
14740
15332
|
throw new Error(`${path29}.acceptableFiles must be an array of strings`);
|
|
@@ -14745,13 +15337,25 @@ function parseExpected(input, path29) {
|
|
|
14745
15337
|
if (branchRaw !== void 0 && typeof branchRaw !== "string") {
|
|
14746
15338
|
throw new Error(`${path29}.branch must be a string when provided`);
|
|
14747
15339
|
}
|
|
15340
|
+
const expectedRoute = parseExpectedRoute(expectedRouteRaw, `${path29}.expectedRoute`);
|
|
15341
|
+
const recoveryExpectation = parseRecoveryExpectation(
|
|
15342
|
+
recoveryExpectationRaw,
|
|
15343
|
+
`${path29}.recoveryExpectation`
|
|
15344
|
+
);
|
|
14748
15345
|
return {
|
|
14749
15346
|
filePath,
|
|
14750
15347
|
acceptableFiles,
|
|
14751
15348
|
symbol: typeof symbolRaw === "string" ? symbolRaw : void 0,
|
|
14752
|
-
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 } : {}
|
|
14753
15354
|
};
|
|
14754
15355
|
}
|
|
15356
|
+
function parseQueryLanguage(value, path29) {
|
|
15357
|
+
return parseStringOrUndefined(value, path29);
|
|
15358
|
+
}
|
|
14755
15359
|
function parseQuery(input, index) {
|
|
14756
15360
|
const path29 = `queries[${index}]`;
|
|
14757
15361
|
if (!isRecord3(input)) {
|
|
@@ -14762,6 +15366,10 @@ function parseQuery(input, index) {
|
|
|
14762
15366
|
const queryType = input.queryType;
|
|
14763
15367
|
const retrievalMode = input.retrievalMode;
|
|
14764
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;
|
|
14765
15373
|
if (typeof id !== "string" || id.trim().length === 0) {
|
|
14766
15374
|
throw new Error(`${path29}.id must be a non-empty string`);
|
|
14767
15375
|
}
|
|
@@ -14773,6 +15381,10 @@ function parseQuery(input, index) {
|
|
|
14773
15381
|
query,
|
|
14774
15382
|
queryType: parseQueryType(queryType, `${path29}.queryType`),
|
|
14775
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`),
|
|
14776
15388
|
expected: parseExpected(expected, `${path29}.expected`)
|
|
14777
15389
|
};
|
|
14778
15390
|
}
|
|
@@ -14784,9 +15396,7 @@ function parseGoldenDataset(raw, sourceLabel) {
|
|
|
14784
15396
|
const name = raw.name;
|
|
14785
15397
|
const description = raw.description;
|
|
14786
15398
|
const queriesRaw = raw.queries;
|
|
14787
|
-
|
|
14788
|
-
throw new Error(`${sourceLabel}.version must be a non-empty string`);
|
|
14789
|
-
}
|
|
15399
|
+
const validatedVersion = parseSemanticVersion(version, `${sourceLabel}.version`);
|
|
14790
15400
|
if (typeof name !== "string" || name.trim().length === 0) {
|
|
14791
15401
|
throw new Error(`${sourceLabel}.name must be a non-empty string`);
|
|
14792
15402
|
}
|
|
@@ -14808,7 +15418,7 @@ function parseGoldenDataset(raw, sourceLabel) {
|
|
|
14808
15418
|
idSet.add(query.id);
|
|
14809
15419
|
}
|
|
14810
15420
|
return {
|
|
14811
|
-
version,
|
|
15421
|
+
version: validatedVersion,
|
|
14812
15422
|
name,
|
|
14813
15423
|
description: typeof description === "string" ? description : void 0,
|
|
14814
15424
|
queries
|
|
@@ -14868,16 +15478,8 @@ function parseBudget(raw, sourceLabel) {
|
|
|
14868
15478
|
"p95LatencyMaxAbsoluteMs",
|
|
14869
15479
|
sourceLabel
|
|
14870
15480
|
),
|
|
14871
|
-
minHitAt5: parseThresholdValue(
|
|
14872
|
-
|
|
14873
|
-
"minHitAt5",
|
|
14874
|
-
sourceLabel
|
|
14875
|
-
),
|
|
14876
|
-
minMrrAt10: parseThresholdValue(
|
|
14877
|
-
thresholds.minMrrAt10,
|
|
14878
|
-
"minMrrAt10",
|
|
14879
|
-
sourceLabel
|
|
14880
|
-
),
|
|
15481
|
+
minHitAt5: parseThresholdValue(thresholds.minHitAt5, "minHitAt5", sourceLabel),
|
|
15482
|
+
minMrrAt10: parseThresholdValue(thresholds.minMrrAt10, "minMrrAt10", sourceLabel),
|
|
14881
15483
|
minRawDistinctTop3Ratio: parseThresholdValue(
|
|
14882
15484
|
thresholds.minRawDistinctTop3Ratio,
|
|
14883
15485
|
"minRawDistinctTop3Ratio",
|
|
@@ -14927,6 +15529,26 @@ function loadBudget(budgetPath) {
|
|
|
14927
15529
|
}
|
|
14928
15530
|
|
|
14929
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
|
+
}
|
|
14930
15552
|
async function runEvaluation(options) {
|
|
14931
15553
|
const datasetPath = toAbsolute(options.projectRoot, options.datasetPath);
|
|
14932
15554
|
const againstPath = options.againstPath ? toAbsolute(options.projectRoot, options.againstPath) : void 0;
|
|
@@ -14951,27 +15573,40 @@ async function runEvaluation(options) {
|
|
|
14951
15573
|
const start = performance3.now();
|
|
14952
15574
|
const contextResult = query.retrievalMode === "context" ? await resolveSearchContext({
|
|
14953
15575
|
query: query.query,
|
|
15576
|
+
symbol: query.args?.symbol,
|
|
15577
|
+
fileType: query.args?.fileType,
|
|
15578
|
+
directory: query.args?.directory,
|
|
14954
15579
|
limit: 10,
|
|
14955
15580
|
tokenBudget: DEFAULT_CONTEXT_PACK_TOKEN_BUDGET
|
|
14956
15581
|
}, {
|
|
14957
|
-
lookup: (symbol, limit,
|
|
15582
|
+
lookup: (symbol, limit, scope) => indexer.search(symbol, limit, {
|
|
14958
15583
|
metadataOnly: true,
|
|
14959
15584
|
filterByBranch: !!query.expected.branch,
|
|
14960
|
-
definitionIntent: true
|
|
15585
|
+
definitionIntent: true,
|
|
15586
|
+
fileType: scope.fileType,
|
|
15587
|
+
directory: scope.directory
|
|
14961
15588
|
}),
|
|
14962
|
-
search: (searchQuery, limit,
|
|
15589
|
+
search: (searchQuery, limit, scope) => indexer.search(searchQuery, limit, {
|
|
14963
15590
|
metadataOnly: true,
|
|
14964
15591
|
filterByBranch: !!query.expected.branch,
|
|
14965
|
-
definitionIntent: false
|
|
15592
|
+
definitionIntent: false,
|
|
15593
|
+
fileType: scope.fileType,
|
|
15594
|
+
directory: scope.directory
|
|
14966
15595
|
})
|
|
14967
15596
|
}) : void 0;
|
|
14968
15597
|
const result = contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
14969
15598
|
metadataOnly: true,
|
|
14970
|
-
filterByBranch: !!query.expected.branch
|
|
15599
|
+
filterByBranch: !!query.expected.branch,
|
|
15600
|
+
fileType: query.args?.fileType,
|
|
15601
|
+
directory: query.args?.directory
|
|
14971
15602
|
});
|
|
14972
15603
|
const elapsed = performance3.now() - start;
|
|
14973
15604
|
const resolvedRoute = contextResult?.details?.route === "definition" ? "definition" : "search";
|
|
14974
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);
|
|
14975
15610
|
const materialized = result.map((item) => ({
|
|
14976
15611
|
filePath: item.filePath,
|
|
14977
15612
|
startLine: item.startLine,
|
|
@@ -14988,7 +15623,9 @@ async function runEvaluation(options) {
|
|
|
14988
15623
|
responseTokens: contextResult.details.tokenEstimate,
|
|
14989
15624
|
candidateCount: contextResult.details.candidateCount ?? 0,
|
|
14990
15625
|
deduplicatedCount: contextResult.details.deduplicatedCount ?? 0,
|
|
14991
|
-
omittedCount: contextResult.details.omittedCount ?? 0
|
|
15626
|
+
omittedCount: contextResult.details.omittedCount ?? 0,
|
|
15627
|
+
recoveryUsed,
|
|
15628
|
+
recoveryRelaxed
|
|
14992
15629
|
} : void 0));
|
|
14993
15630
|
}
|
|
14994
15631
|
const logger = indexer.getLogger();
|
|
@@ -15000,6 +15637,7 @@ async function runEvaluation(options) {
|
|
|
15000
15637
|
datasetPath,
|
|
15001
15638
|
datasetName: dataset.name,
|
|
15002
15639
|
datasetVersion: dataset.version,
|
|
15640
|
+
datasetFingerprint: buildDatasetFingerprint(dataset),
|
|
15003
15641
|
queryCount: dataset.queries.length,
|
|
15004
15642
|
topK: 10,
|
|
15005
15643
|
searchConfig: {
|
|
@@ -17916,7 +18554,10 @@ function createWatcherWithIndexer(getIndexer, projectRoot, config, host = "openc
|
|
|
17916
18554
|
if (isGitRepo(projectRoot)) {
|
|
17917
18555
|
gitWatcher = new GitHeadWatcher(projectRoot);
|
|
17918
18556
|
gitWatcher.start(async (oldBranch, newBranch) => {
|
|
17919
|
-
|
|
18557
|
+
getIndexer().getLogger().branch("info", "Branch changed", {
|
|
18558
|
+
oldBranch,
|
|
18559
|
+
newBranch
|
|
18560
|
+
});
|
|
17920
18561
|
requestReindex();
|
|
17921
18562
|
});
|
|
17922
18563
|
}
|