opencode-codebase-index 0.22.3 → 0.22.5
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/dist/cli.cjs +681 -97
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +681 -97
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +455 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +455 -58
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +435 -51
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +435 -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 +13 -9
package/dist/cli.cjs
CHANGED
|
@@ -1291,6 +1291,7 @@ function createSummaryMarkdown(summary, comparison, gate, sweep) {
|
|
|
1291
1291
|
lines.push(`| Hit@10 | ${formatPct(summary.metrics.hitAt10)} |`);
|
|
1292
1292
|
lines.push(`| MRR@10 | ${summary.metrics.mrrAt10.toFixed(4)} |`);
|
|
1293
1293
|
lines.push(`| nDCG@10 | ${summary.metrics.ndcgAt10.toFixed(4)} |`);
|
|
1294
|
+
lines.push(`| Graph-neighbor recall | ${(summary.metrics.graphNeighborRecall ?? 0).toFixed(4)} |`);
|
|
1294
1295
|
lines.push(`| Distinct Top@3 | ${formatPct(summary.metrics.distinctTop3Ratio)} |`);
|
|
1295
1296
|
lines.push(`| Raw Distinct Top@3 | ${formatPct(summary.metrics.rawDistinctTop3Ratio)} |`);
|
|
1296
1297
|
lines.push(`| Latency p50 | ${formatMs(summary.metrics.latencyMs.p50)} |`);
|
|
@@ -6655,12 +6656,12 @@ function diversifyGroupBySymbol(entries, getCandidate) {
|
|
|
6655
6656
|
return [...primary, ...remainder];
|
|
6656
6657
|
}
|
|
6657
6658
|
function buildDiversityKey(metadata) {
|
|
6658
|
-
const
|
|
6659
|
+
const normalizedPath3 = metadata.filePath.toLowerCase();
|
|
6659
6660
|
const normalizedName = (metadata.name ?? "").trim().toLowerCase();
|
|
6660
6661
|
if (normalizedName.length > 0) {
|
|
6661
|
-
return `${
|
|
6662
|
+
return `${normalizedPath3}#${normalizedName}`;
|
|
6662
6663
|
}
|
|
6663
|
-
return
|
|
6664
|
+
return normalizedPath3;
|
|
6664
6665
|
}
|
|
6665
6666
|
function rankHybridResults(query, semanticResults, keywordResults, options) {
|
|
6666
6667
|
const prioritizeSourcePaths = options.prioritizeSourcePaths ?? classifyQueryIntentRaw(query) === "source";
|
|
@@ -7066,6 +7067,29 @@ function extractPrimaryIdentifierQueryHint(query) {
|
|
|
7066
7067
|
const best = codeTerms.find((term) => term.length >= 6);
|
|
7067
7068
|
return best ?? null;
|
|
7068
7069
|
}
|
|
7070
|
+
function pathSegmentsForAffinityMatch(filePath) {
|
|
7071
|
+
const normalizedPath3 = normalizeRankingText(filePath).replace(/\\/g, "/");
|
|
7072
|
+
const segments = normalizedPath3.split("/").filter((segment) => segment.length > 0);
|
|
7073
|
+
if (segments.length === 0) {
|
|
7074
|
+
return [];
|
|
7075
|
+
}
|
|
7076
|
+
const basename8 = segments[segments.length - 1] ?? "";
|
|
7077
|
+
const basenameWithoutExt = basename8.replace(/\.[^/.]+$/u, "");
|
|
7078
|
+
const normalizedSegments = segments.map((segment) => segment.toLowerCase());
|
|
7079
|
+
return Array.from(/* @__PURE__ */ new Set([
|
|
7080
|
+
...normalizedSegments,
|
|
7081
|
+
basenameWithoutExt.toLowerCase()
|
|
7082
|
+
]));
|
|
7083
|
+
}
|
|
7084
|
+
function hasModuleAffinity(filePath, exactIdentifierVariants) {
|
|
7085
|
+
const haystack = pathSegmentsForAffinityMatch(filePath);
|
|
7086
|
+
return exactIdentifierVariants.some((variant) => {
|
|
7087
|
+
if (!variant || variant.length < 2) {
|
|
7088
|
+
return false;
|
|
7089
|
+
}
|
|
7090
|
+
return haystack.includes(variant);
|
|
7091
|
+
});
|
|
7092
|
+
}
|
|
7069
7093
|
var FILE_PATH_HINT_EXTENSIONS = [
|
|
7070
7094
|
"ts",
|
|
7071
7095
|
"tsx",
|
|
@@ -7109,9 +7133,9 @@ function normalizeFilePathForHintMatch(filePath) {
|
|
|
7109
7133
|
return filePath.replace(/\\/g, "/").toLowerCase().replace(/^\.\//, "");
|
|
7110
7134
|
}
|
|
7111
7135
|
function pathMatchesHint(filePath, hint) {
|
|
7112
|
-
const
|
|
7136
|
+
const normalizedPath3 = normalizeFilePathForHintMatch(filePath);
|
|
7113
7137
|
const normalizedHint = normalizeFilePathForHintMatch(hint);
|
|
7114
|
-
return
|
|
7138
|
+
return normalizedPath3.endsWith(normalizedHint) || normalizedPath3.includes(`/${normalizedHint}`) || normalizedPath3.includes(normalizedHint);
|
|
7115
7139
|
}
|
|
7116
7140
|
function extractFilePathHint(query) {
|
|
7117
7141
|
const match = query.match(FILE_PATH_HINT_SUFFIX_REGEX);
|
|
@@ -7141,10 +7165,13 @@ function buildDeterministicIdentifierPass(query, candidates, limit, prioritizeSo
|
|
|
7141
7165
|
).map((candidate) => {
|
|
7142
7166
|
const nameLower = (candidate.metadata.name ?? "").toLowerCase();
|
|
7143
7167
|
const pathLower = candidate.metadata.filePath.toLowerCase();
|
|
7144
|
-
|
|
7145
|
-
const
|
|
7168
|
+
const exactIdentifierVariants = primaryVariants.filter((value) => value.length >= 2);
|
|
7169
|
+
const exactMatch = exactIdentifierVariants.some(
|
|
7146
7170
|
(variant) => nameLower === variant || nameLower.replace(/[^a-z0-9]/g, "") === variant.replace(/[^a-z0-9]/g, "")
|
|
7147
7171
|
);
|
|
7172
|
+
let maxMatch = 0;
|
|
7173
|
+
const nameMatchesPrimary = exactMatch;
|
|
7174
|
+
const pathAffinity = exactMatch ? hasModuleAffinity(candidate.metadata.filePath, exactIdentifierVariants) : false;
|
|
7148
7175
|
const pathMatchesFileHint = filePathHint ? pathMatchesHint(candidate.metadata.filePath, filePathHint) : false;
|
|
7149
7176
|
for (const hint of hints) {
|
|
7150
7177
|
const variants = normalizeIdentifierVariants(hint);
|
|
@@ -7165,12 +7192,17 @@ function buildDeterministicIdentifierPass(query, candidates, limit, prioritizeSo
|
|
|
7165
7192
|
candidate,
|
|
7166
7193
|
maxMatch,
|
|
7167
7194
|
pathMatchesFileHint,
|
|
7168
|
-
nameMatchesPrimary
|
|
7195
|
+
nameMatchesPrimary,
|
|
7196
|
+
pathAffinity
|
|
7169
7197
|
};
|
|
7170
7198
|
}).filter((entry) => entry.maxMatch >= 0.7).sort((a, b) => {
|
|
7171
7199
|
const aAnchored = a.pathMatchesFileHint && a.nameMatchesPrimary ? 1 : 0;
|
|
7172
7200
|
const bAnchored = b.pathMatchesFileHint && b.nameMatchesPrimary ? 1 : 0;
|
|
7173
7201
|
if (aAnchored !== bAnchored) return bAnchored - aAnchored;
|
|
7202
|
+
if (a.nameMatchesPrimary !== b.nameMatchesPrimary) {
|
|
7203
|
+
return b.nameMatchesPrimary ? 1 : -1;
|
|
7204
|
+
}
|
|
7205
|
+
if (a.pathAffinity !== b.pathAffinity) return b.pathAffinity ? 1 : -1;
|
|
7174
7206
|
if (b.maxMatch !== a.maxMatch) return b.maxMatch - a.maxMatch;
|
|
7175
7207
|
if (b.candidate.score !== a.candidate.score) return b.candidate.score - a.candidate.score;
|
|
7176
7208
|
return a.candidate.id.localeCompare(b.candidate.id);
|
|
@@ -11375,6 +11407,20 @@ var Indexer = class _Indexer {
|
|
|
11375
11407
|
requestedLimit = nextLimit;
|
|
11376
11408
|
}
|
|
11377
11409
|
}
|
|
11410
|
+
buildCandidateSnapshot(candidate) {
|
|
11411
|
+
return {
|
|
11412
|
+
id: candidate.id,
|
|
11413
|
+
filePath: candidate.metadata.filePath,
|
|
11414
|
+
startLine: candidate.metadata.startLine,
|
|
11415
|
+
endLine: candidate.metadata.endLine,
|
|
11416
|
+
score: candidate.score,
|
|
11417
|
+
chunkType: candidate.metadata.chunkType,
|
|
11418
|
+
name: candidate.metadata.name
|
|
11419
|
+
};
|
|
11420
|
+
}
|
|
11421
|
+
buildCandidateSnapshotList(candidates) {
|
|
11422
|
+
return candidates.map((candidate) => this.buildCandidateSnapshot(candidate));
|
|
11423
|
+
}
|
|
11378
11424
|
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch) {
|
|
11379
11425
|
return this.searchCandidatesWithBranchPrefilter(
|
|
11380
11426
|
initialLimit,
|
|
@@ -11566,6 +11612,16 @@ var Indexer = class _Indexer {
|
|
|
11566
11612
|
prefilterMs: Math.round(prefilterMs * 100) / 100,
|
|
11567
11613
|
fusionMs: Math.round(fusionMs * 100) / 100
|
|
11568
11614
|
});
|
|
11615
|
+
if (options?.trace) {
|
|
11616
|
+
options.trace({
|
|
11617
|
+
semanticCandidates: this.buildCandidateSnapshotList(scopedSemanticCandidates),
|
|
11618
|
+
keywordCandidates: this.buildCandidateSnapshotList(scopedKeywordCandidates),
|
|
11619
|
+
hybridCandidates: this.buildCandidateSnapshotList(combined),
|
|
11620
|
+
postExternalRerankCandidates: this.buildCandidateSnapshotList(rerankedCombined),
|
|
11621
|
+
tieredCandidates: this.buildCandidateSnapshotList(tiered),
|
|
11622
|
+
finalCandidates: this.buildCandidateSnapshotList(finalResults)
|
|
11623
|
+
});
|
|
11624
|
+
}
|
|
11569
11625
|
const metadataOnly = options?.metadataOnly ?? false;
|
|
11570
11626
|
return Promise.all(
|
|
11571
11627
|
finalResults.map(async (r) => {
|
|
@@ -12614,6 +12670,42 @@ var Indexer = class _Indexer {
|
|
|
12614
12670
|
}
|
|
12615
12671
|
};
|
|
12616
12672
|
|
|
12673
|
+
// src/tools/contracts.ts
|
|
12674
|
+
var CHUNK_TYPES = [
|
|
12675
|
+
"function",
|
|
12676
|
+
"class",
|
|
12677
|
+
"method",
|
|
12678
|
+
"interface",
|
|
12679
|
+
"type",
|
|
12680
|
+
"enum",
|
|
12681
|
+
"struct",
|
|
12682
|
+
"impl",
|
|
12683
|
+
"trait",
|
|
12684
|
+
"module",
|
|
12685
|
+
"other"
|
|
12686
|
+
];
|
|
12687
|
+
var CALL_GRAPH_DIRECTIONS = ["callers", "callees"];
|
|
12688
|
+
var RELATIONSHIP_TYPES = [
|
|
12689
|
+
"Call",
|
|
12690
|
+
"MethodCall",
|
|
12691
|
+
"Constructor",
|
|
12692
|
+
"Import",
|
|
12693
|
+
"Inherits",
|
|
12694
|
+
"Implements"
|
|
12695
|
+
];
|
|
12696
|
+
var INDEX_LOG_CATEGORIES = ["search", "embedding", "cache", "gc", "branch", "general"];
|
|
12697
|
+
var INDEX_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
12698
|
+
var MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT = 1;
|
|
12699
|
+
var MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT = 20;
|
|
12700
|
+
var DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT = 5;
|
|
12701
|
+
var CODE_COMMUNITIES_MIN_SIZE = 1;
|
|
12702
|
+
var CODE_COMMUNITIES_DEFAULT_LIMIT = 20;
|
|
12703
|
+
var CODE_COMMUNITIES_MAX_LIMIT = 100;
|
|
12704
|
+
var CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD = 5;
|
|
12705
|
+
var CODE_COMMUNITIES_MIN_COUPLING = 1;
|
|
12706
|
+
var CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT = 20;
|
|
12707
|
+
var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
|
|
12708
|
+
|
|
12617
12709
|
// src/tools/operations.ts
|
|
12618
12710
|
var import_fs14 = require("fs");
|
|
12619
12711
|
var path21 = __toESM(require("path"), 1);
|
|
@@ -12742,39 +12834,6 @@ function formatCodeCommunities(result) {
|
|
|
12742
12834
|
return lines.join("\n");
|
|
12743
12835
|
}
|
|
12744
12836
|
|
|
12745
|
-
// src/tools/contracts.ts
|
|
12746
|
-
var CHUNK_TYPES = [
|
|
12747
|
-
"function",
|
|
12748
|
-
"class",
|
|
12749
|
-
"method",
|
|
12750
|
-
"interface",
|
|
12751
|
-
"type",
|
|
12752
|
-
"enum",
|
|
12753
|
-
"struct",
|
|
12754
|
-
"impl",
|
|
12755
|
-
"trait",
|
|
12756
|
-
"module",
|
|
12757
|
-
"other"
|
|
12758
|
-
];
|
|
12759
|
-
var CALL_GRAPH_DIRECTIONS = ["callers", "callees"];
|
|
12760
|
-
var RELATIONSHIP_TYPES = [
|
|
12761
|
-
"Call",
|
|
12762
|
-
"MethodCall",
|
|
12763
|
-
"Constructor",
|
|
12764
|
-
"Import",
|
|
12765
|
-
"Inherits",
|
|
12766
|
-
"Implements"
|
|
12767
|
-
];
|
|
12768
|
-
var INDEX_LOG_CATEGORIES = ["search", "embedding", "cache", "gc", "branch", "general"];
|
|
12769
|
-
var INDEX_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
12770
|
-
var CODE_COMMUNITIES_MIN_SIZE = 1;
|
|
12771
|
-
var CODE_COMMUNITIES_DEFAULT_LIMIT = 20;
|
|
12772
|
-
var CODE_COMMUNITIES_MAX_LIMIT = 100;
|
|
12773
|
-
var CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD = 5;
|
|
12774
|
-
var CODE_COMMUNITIES_MIN_COUPLING = 1;
|
|
12775
|
-
var CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT = 20;
|
|
12776
|
-
var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
|
|
12777
|
-
|
|
12778
12837
|
// src/tools/context-pack.ts
|
|
12779
12838
|
var import_tiktoken = require("tiktoken");
|
|
12780
12839
|
var MIN_CONTEXT_PACK_TOKEN_BUDGET = 128;
|
|
@@ -12876,6 +12935,16 @@ function compactEvidenceValue(value, maxChars) {
|
|
|
12876
12935
|
}
|
|
12877
12936
|
var MAX_EXACT_SEARCH_HANDOFF_NAMES = 3;
|
|
12878
12937
|
var MAX_EXACT_SEARCH_HANDOFF_NAME_CHARS = 64;
|
|
12938
|
+
function toContextPackTraceCandidate(result) {
|
|
12939
|
+
return {
|
|
12940
|
+
filePath: result.filePath,
|
|
12941
|
+
startLine: result.startLine,
|
|
12942
|
+
endLine: result.endLine,
|
|
12943
|
+
score: result.score,
|
|
12944
|
+
chunkType: result.chunkType,
|
|
12945
|
+
name: result.name
|
|
12946
|
+
};
|
|
12947
|
+
}
|
|
12879
12948
|
function formatExactSearchHandoff(results) {
|
|
12880
12949
|
const suggestedNames = [];
|
|
12881
12950
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -12924,13 +12993,13 @@ function buildContextPack(results, options = {}) {
|
|
|
12924
12993
|
const maxResults = Math.max(0, Math.floor(options.maxResults ?? results.length));
|
|
12925
12994
|
const includeExactSearchHandoff = options.includeExactSearchHandoff ?? false;
|
|
12926
12995
|
const candidateCount = results.length;
|
|
12927
|
-
const
|
|
12928
|
-
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
);
|
|
12933
|
-
const
|
|
12996
|
+
const preserveInputOrder = options.preserveInputOrder ?? false;
|
|
12997
|
+
const ranked = preserveInputOrder ? results.map((result, originalIndex) => ({ result, originalIndex })) : rankContextCandidates(results, options.preferImplementationPaths ?? false);
|
|
12998
|
+
const rankedCandidates = ranked.map((entry) => toContextPackTraceCandidate(entry.result));
|
|
12999
|
+
const deduplicated = deduplicateContextCandidates(ranked);
|
|
13000
|
+
const deduplicatedCandidates = deduplicated.map((result) => toContextPackTraceCandidate(result));
|
|
13001
|
+
const diversified = preserveInputOrder ? deduplicated : diversifyContextCandidates(deduplicated);
|
|
13002
|
+
const diversifiedCandidates = diversified.map((result) => toContextPackTraceCandidate(result));
|
|
12934
13003
|
const duplicateCount = candidateCount - deduplicated.length;
|
|
12935
13004
|
const selectable = diversified.slice(0, maxResults);
|
|
12936
13005
|
const limitOmittedCount = deduplicated.length - selectable.length;
|
|
@@ -12963,6 +13032,15 @@ function buildContextPack(results, options = {}) {
|
|
|
12963
13032
|
const fitted = fitTextToContextBudget(text, tokenBudget);
|
|
12964
13033
|
const budgetOmittedCount = selectable.length - selected.length;
|
|
12965
13034
|
const omittedCount = candidateCount - selected.length;
|
|
13035
|
+
if (options.trace) {
|
|
13036
|
+
options.trace({
|
|
13037
|
+
inputCandidates: results.map(toContextPackTraceCandidate),
|
|
13038
|
+
rankedCandidates,
|
|
13039
|
+
deduplicatedCandidates,
|
|
13040
|
+
diversifiedCandidates,
|
|
13041
|
+
selectedCandidates: selected.map(toContextPackTraceCandidate)
|
|
13042
|
+
});
|
|
13043
|
+
}
|
|
12966
13044
|
return {
|
|
12967
13045
|
requestedTokenBudget,
|
|
12968
13046
|
tokenBudget,
|
|
@@ -14710,7 +14788,8 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
|
14710
14788
|
definitionIntent: options.definitionIntent,
|
|
14711
14789
|
blameAuthor: options.blameAuthor,
|
|
14712
14790
|
blameSha: options.blameSha,
|
|
14713
|
-
blameSince: options.blameSince
|
|
14791
|
+
blameSince: options.blameSince,
|
|
14792
|
+
trace: options.trace
|
|
14714
14793
|
});
|
|
14715
14794
|
}
|
|
14716
14795
|
async function searchCodebaseWithEffectiveness(projectRoot, host, route, query, options, render) {
|
|
@@ -14764,15 +14843,19 @@ async function implementationLookup(projectRoot, host, query, options = {}) {
|
|
|
14764
14843
|
return indexer.search(query, options.limit, {
|
|
14765
14844
|
fileType: options.fileType,
|
|
14766
14845
|
directory: options.directory,
|
|
14767
|
-
definitionIntent: true
|
|
14846
|
+
definitionIntent: true,
|
|
14847
|
+
trace: options.trace
|
|
14768
14848
|
});
|
|
14769
14849
|
}
|
|
14770
14850
|
async function getCallGraphData(projectRoot, host, params) {
|
|
14771
14851
|
await ensureAutoIndexReadyForRetrieval(projectRoot, host);
|
|
14772
14852
|
const root = getProjectRoot(projectRoot, host);
|
|
14773
14853
|
const indexer = getIndexerForProject(root, host);
|
|
14854
|
+
return getCallGraphDataForIndexer(indexer, root, params);
|
|
14855
|
+
}
|
|
14856
|
+
async function getCallGraphDataForIndexer(indexer, projectRoot, params) {
|
|
14774
14857
|
const symbols = await indexer.getCallGraphSymbols();
|
|
14775
|
-
const resolution = resolveCallGraphSymbol(symbols,
|
|
14858
|
+
const resolution = resolveCallGraphSymbol(symbols, projectRoot, params.name, params.filePath, params.symbolId);
|
|
14776
14859
|
const direction = params.direction === "callees" ? "callees" : "callers";
|
|
14777
14860
|
if (resolution.status !== "resolved") {
|
|
14778
14861
|
return { direction, resolution, callers: [], callees: [], relationshipType: params.relationshipType };
|
|
@@ -15100,6 +15183,27 @@ function buildRecoveryDetails(attempts, successIndex) {
|
|
|
15100
15183
|
successfulAttemptIndex: successIndex
|
|
15101
15184
|
};
|
|
15102
15185
|
}
|
|
15186
|
+
function serializeAttempts(attempts) {
|
|
15187
|
+
return attempts.map((attempt) => ({
|
|
15188
|
+
kind: attempt.kind,
|
|
15189
|
+
scope: attempt.scope,
|
|
15190
|
+
resultCount: attempt.resultCount,
|
|
15191
|
+
relaxedFields: attempt.relaxedFields
|
|
15192
|
+
}));
|
|
15193
|
+
}
|
|
15194
|
+
function buildSearchDiagnostic(attempt) {
|
|
15195
|
+
if (!attempt) {
|
|
15196
|
+
return void 0;
|
|
15197
|
+
}
|
|
15198
|
+
return {
|
|
15199
|
+
route: attempt.kind,
|
|
15200
|
+
routedQuery: attempt.query,
|
|
15201
|
+
searchQuery: attempt.query,
|
|
15202
|
+
searchScope: attempt.scopeFilter,
|
|
15203
|
+
searchTrace: attempt.searchTrace,
|
|
15204
|
+
contextPackTrace: attempt.contextPackTrace
|
|
15205
|
+
};
|
|
15206
|
+
}
|
|
15103
15207
|
function trimOrUndefined2(value) {
|
|
15104
15208
|
const normalized = value?.trim();
|
|
15105
15209
|
if (!normalized) {
|
|
@@ -15139,6 +15243,7 @@ async function resolveSearchContext(input, operations) {
|
|
|
15139
15243
|
const hasFilters = Boolean(fileType || directory);
|
|
15140
15244
|
const relaxedFields = relaxedHintFields(fileType, directory);
|
|
15141
15245
|
const attempts = [];
|
|
15246
|
+
const attemptStates = [];
|
|
15142
15247
|
const decisions = {
|
|
15143
15248
|
inferredDefinitionMiss: false,
|
|
15144
15249
|
fallbackFromOriginalConceptualToInferred: false,
|
|
@@ -15167,8 +15272,20 @@ async function resolveSearchContext(input, operations) {
|
|
|
15167
15272
|
if (seenAttempts.has(key)) {
|
|
15168
15273
|
return [];
|
|
15169
15274
|
}
|
|
15170
|
-
const
|
|
15275
|
+
const attemptState = {
|
|
15276
|
+
kind,
|
|
15277
|
+
scope: describeScope(scope.fileType, scope.directory),
|
|
15278
|
+
resultCount: 0,
|
|
15279
|
+
relaxedFields: [...relaxedFieldsForAttempt],
|
|
15280
|
+
query: attemptQuery,
|
|
15281
|
+
scopeFilter: scope
|
|
15282
|
+
};
|
|
15283
|
+
const results = await runAttempt((trace) => {
|
|
15284
|
+
attemptState.searchTrace = trace;
|
|
15285
|
+
});
|
|
15286
|
+
attemptState.resultCount = results.length;
|
|
15171
15287
|
seenAttempts.add(key);
|
|
15288
|
+
attemptStates.push(attemptState);
|
|
15172
15289
|
attempts.push({
|
|
15173
15290
|
kind,
|
|
15174
15291
|
scope: describeScope(scope.fileType, scope.directory),
|
|
@@ -15188,7 +15305,7 @@ async function resolveSearchContext(input, operations) {
|
|
|
15188
15305
|
symbol,
|
|
15189
15306
|
scope,
|
|
15190
15307
|
relaxedFieldsForAttempt,
|
|
15191
|
-
() => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope)
|
|
15308
|
+
(trace) => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
15192
15309
|
);
|
|
15193
15310
|
};
|
|
15194
15311
|
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt) => {
|
|
@@ -15197,13 +15314,23 @@ async function resolveSearchContext(input, operations) {
|
|
|
15197
15314
|
searchQuery,
|
|
15198
15315
|
scope,
|
|
15199
15316
|
relaxedFieldsForAttempt,
|
|
15200
|
-
() => operations.search(searchQuery, MAX_CONTEXT_RESULT_LIMIT, scope)
|
|
15317
|
+
(trace) => operations.search(searchQuery, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
15201
15318
|
);
|
|
15202
15319
|
};
|
|
15203
|
-
const
|
|
15320
|
+
const findSuccessfulAttemptState = (route) => {
|
|
15321
|
+
for (let index = attemptStates.length - 1; index >= 0; index -= 1) {
|
|
15322
|
+
const attempt = attemptStates[index];
|
|
15323
|
+
if (attempt.kind === route && attempt.resultCount > 0) {
|
|
15324
|
+
return attempt;
|
|
15325
|
+
}
|
|
15326
|
+
}
|
|
15327
|
+
return void 0;
|
|
15328
|
+
};
|
|
15329
|
+
const toResult = (route, routedQuery, pack, successfulAttempt) => {
|
|
15204
15330
|
const base = packedResult(route, routedQuery, pack);
|
|
15205
15331
|
const baseDetails = base.details;
|
|
15206
15332
|
const successIndex = findSuccessfulAttemptIndex(route, attempts);
|
|
15333
|
+
const successState = successfulAttempt ?? findSuccessfulAttemptState(route);
|
|
15207
15334
|
return {
|
|
15208
15335
|
text: base.text,
|
|
15209
15336
|
details: {
|
|
@@ -15211,7 +15338,10 @@ async function resolveSearchContext(input, operations) {
|
|
|
15211
15338
|
tokenBudget: baseDetails.tokenBudget,
|
|
15212
15339
|
tokenEstimate: baseDetails.tokenEstimate,
|
|
15213
15340
|
truncated: false,
|
|
15214
|
-
recovery: buildRecoveryDetails(
|
|
15341
|
+
recovery: buildRecoveryDetails(serializeAttempts(attemptStates), successIndex),
|
|
15342
|
+
...input.diagnostic && {
|
|
15343
|
+
diagnostic: buildSearchDiagnostic(successState)
|
|
15344
|
+
}
|
|
15215
15345
|
}
|
|
15216
15346
|
};
|
|
15217
15347
|
};
|
|
@@ -15225,8 +15355,18 @@ async function resolveSearchContext(input, operations) {
|
|
|
15225
15355
|
buildContextPack(scopedDefinitionResults, {
|
|
15226
15356
|
tokenBudget,
|
|
15227
15357
|
maxResults: limit,
|
|
15228
|
-
heading
|
|
15229
|
-
|
|
15358
|
+
heading,
|
|
15359
|
+
preserveInputOrder: true,
|
|
15360
|
+
...input.diagnostic ? {
|
|
15361
|
+
trace: (trace) => {
|
|
15362
|
+
const attemptState = findSuccessfulAttemptState("definition");
|
|
15363
|
+
if (attemptState) {
|
|
15364
|
+
attemptState.contextPackTrace = trace;
|
|
15365
|
+
}
|
|
15366
|
+
}
|
|
15367
|
+
} : void 0
|
|
15368
|
+
}),
|
|
15369
|
+
findSuccessfulAttemptState("definition")
|
|
15230
15370
|
);
|
|
15231
15371
|
}
|
|
15232
15372
|
if (explicitSymbol) {
|
|
@@ -15244,8 +15384,18 @@ async function resolveSearchContext(input, operations) {
|
|
|
15244
15384
|
buildContextPack(unscopedDefinitionResults, {
|
|
15245
15385
|
tokenBudget,
|
|
15246
15386
|
maxResults: limit,
|
|
15247
|
-
heading: heading2
|
|
15248
|
-
|
|
15387
|
+
heading: heading2,
|
|
15388
|
+
preserveInputOrder: true,
|
|
15389
|
+
...input.diagnostic ? {
|
|
15390
|
+
trace: (trace) => {
|
|
15391
|
+
const attemptState = findSuccessfulAttemptState("definition");
|
|
15392
|
+
if (attemptState) {
|
|
15393
|
+
attemptState.contextPackTrace = trace;
|
|
15394
|
+
}
|
|
15395
|
+
}
|
|
15396
|
+
} : void 0
|
|
15397
|
+
}),
|
|
15398
|
+
findSuccessfulAttemptState("definition")
|
|
15249
15399
|
);
|
|
15250
15400
|
}
|
|
15251
15401
|
}
|
|
@@ -15264,7 +15414,10 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15264
15414
|
tokenBudget: heading.tokenBudget,
|
|
15265
15415
|
tokenEstimate: heading.tokenEstimate,
|
|
15266
15416
|
truncated: heading.truncated,
|
|
15267
|
-
recovery: buildRecoveryDetails(
|
|
15417
|
+
recovery: buildRecoveryDetails(serializeAttempts(attemptStates), null),
|
|
15418
|
+
...input.diagnostic && {
|
|
15419
|
+
diagnostic: buildSearchDiagnostic(attemptStates[attemptStates.length - 1])
|
|
15420
|
+
}
|
|
15268
15421
|
}
|
|
15269
15422
|
};
|
|
15270
15423
|
}
|
|
@@ -15304,8 +15457,17 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15304
15457
|
maxResults: limit,
|
|
15305
15458
|
heading,
|
|
15306
15459
|
includeExactSearchHandoff: true,
|
|
15307
|
-
preferImplementationPaths: intent.preferSourcePaths || intent.primary !== "docs" && intent.primary !== "test"
|
|
15308
|
-
|
|
15460
|
+
preferImplementationPaths: intent.preferSourcePaths || intent.primary !== "docs" && intent.primary !== "test",
|
|
15461
|
+
...input.diagnostic ? {
|
|
15462
|
+
trace: (trace) => {
|
|
15463
|
+
const attemptState = findSuccessfulAttemptState("conceptual");
|
|
15464
|
+
if (attemptState) {
|
|
15465
|
+
attemptState.contextPackTrace = trace;
|
|
15466
|
+
}
|
|
15467
|
+
}
|
|
15468
|
+
} : void 0
|
|
15469
|
+
}),
|
|
15470
|
+
findSuccessfulAttemptState("conceptual")
|
|
15309
15471
|
);
|
|
15310
15472
|
}
|
|
15311
15473
|
}
|
|
@@ -15321,7 +15483,10 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15321
15483
|
tokenBudget: fallbackText.tokenBudget,
|
|
15322
15484
|
tokenEstimate: fallbackText.tokenEstimate,
|
|
15323
15485
|
truncated: fallbackText.truncated,
|
|
15324
|
-
recovery: buildRecoveryDetails(
|
|
15486
|
+
recovery: buildRecoveryDetails(serializeAttempts(attemptStates), null),
|
|
15487
|
+
...input.diagnostic && {
|
|
15488
|
+
diagnostic: buildSearchDiagnostic(attemptStates[attemptStates.length - 1])
|
|
15489
|
+
}
|
|
15325
15490
|
}
|
|
15326
15491
|
};
|
|
15327
15492
|
}
|
|
@@ -15402,17 +15567,27 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
15402
15567
|
details: fittedDetails("path", fitted, 0)
|
|
15403
15568
|
};
|
|
15404
15569
|
}
|
|
15405
|
-
return resolveSearchContext({
|
|
15406
|
-
|
|
15570
|
+
return resolveSearchContext({
|
|
15571
|
+
query: input.query,
|
|
15572
|
+
symbol,
|
|
15573
|
+
limit,
|
|
15574
|
+
tokenBudget,
|
|
15575
|
+
fileType,
|
|
15576
|
+
directory,
|
|
15577
|
+
diagnostic: input.diagnostic
|
|
15578
|
+
}, {
|
|
15579
|
+
lookup: (lookupSymbol, retrievalLimit, scope, trace) => implementationLookup(projectRoot, host, lookupSymbol, {
|
|
15407
15580
|
limit: retrievalLimit,
|
|
15408
15581
|
fileType: scope.fileType,
|
|
15409
|
-
directory: scope.directory
|
|
15582
|
+
directory: scope.directory,
|
|
15583
|
+
trace
|
|
15410
15584
|
}),
|
|
15411
|
-
search: (queryText, retrievalLimit, scope) => searchCodebase(projectRoot, host, queryText, {
|
|
15585
|
+
search: (queryText, retrievalLimit, scope, trace) => searchCodebase(projectRoot, host, queryText, {
|
|
15412
15586
|
limit: retrievalLimit,
|
|
15413
15587
|
fileType: scope.fileType,
|
|
15414
15588
|
directory: scope.directory,
|
|
15415
|
-
metadataOnly: true
|
|
15589
|
+
metadataOnly: true,
|
|
15590
|
+
trace
|
|
15416
15591
|
})
|
|
15417
15592
|
});
|
|
15418
15593
|
}
|
|
@@ -15477,6 +15652,181 @@ async function resolveCodebaseContext(projectRoot, host, input) {
|
|
|
15477
15652
|
}
|
|
15478
15653
|
}
|
|
15479
15654
|
|
|
15655
|
+
// src/tools/edit-context.ts
|
|
15656
|
+
function edgeLimit(value) {
|
|
15657
|
+
if (value === null || value === void 0 || !Number.isFinite(value)) {
|
|
15658
|
+
return DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT;
|
|
15659
|
+
}
|
|
15660
|
+
return Math.min(
|
|
15661
|
+
MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT,
|
|
15662
|
+
Math.max(MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT, Math.floor(value))
|
|
15663
|
+
);
|
|
15664
|
+
}
|
|
15665
|
+
function normalizedPath(value) {
|
|
15666
|
+
return value.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
15667
|
+
}
|
|
15668
|
+
function pathsMatch(left, right) {
|
|
15669
|
+
const normalizedLeft = normalizedPath(left);
|
|
15670
|
+
const normalizedRight = normalizedPath(right);
|
|
15671
|
+
return normalizedLeft === normalizedRight || normalizedLeft.endsWith(`/${normalizedRight}`) || normalizedRight.endsWith(`/${normalizedLeft}`);
|
|
15672
|
+
}
|
|
15673
|
+
function targetSource(results, resolution) {
|
|
15674
|
+
return results.find((result) => pathsMatch(result.filePath, resolution.filePath) && result.startLine <= resolution.startLine && result.endLine >= resolution.startLine) ?? results.find((result) => pathsMatch(result.filePath, resolution.filePath) && result.name === resolution.name);
|
|
15675
|
+
}
|
|
15676
|
+
function formatSource(result) {
|
|
15677
|
+
const name = result.name ? ` ${result.name}` : "";
|
|
15678
|
+
return [
|
|
15679
|
+
"## Target implementation",
|
|
15680
|
+
`${result.filePath}:${result.startLine}-${result.endLine} (${result.chunkType}${name})`,
|
|
15681
|
+
"```",
|
|
15682
|
+
result.content,
|
|
15683
|
+
"```"
|
|
15684
|
+
].join("\n");
|
|
15685
|
+
}
|
|
15686
|
+
function formatCallers(edges) {
|
|
15687
|
+
if (edges.length === 0) return "## Direct callers\nNone found.";
|
|
15688
|
+
return [
|
|
15689
|
+
"## Direct callers",
|
|
15690
|
+
...edges.map((edge) => `- ${edge.fromSymbolName ?? "<unknown>"} at ${edge.fromSymbolFilePath ?? "<unknown file>"}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`)
|
|
15691
|
+
].join("\n");
|
|
15692
|
+
}
|
|
15693
|
+
function formatCallees(edges, sourceFilePath) {
|
|
15694
|
+
if (edges.length === 0) return "## Direct callees\nNone found.";
|
|
15695
|
+
return [
|
|
15696
|
+
"## Direct callees",
|
|
15697
|
+
...edges.map((edge) => `- ${edge.targetName} from ${sourceFilePath}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`)
|
|
15698
|
+
].join("\n");
|
|
15699
|
+
}
|
|
15700
|
+
function formatResolutionRisk(resolution) {
|
|
15701
|
+
if (resolution.status === "ambiguous") {
|
|
15702
|
+
const candidates = resolution.candidates.map((candidate) => `${candidate.filePath}:${candidate.startLine}`).join(", ");
|
|
15703
|
+
return `Risk: symbol "${resolution.name}" is ambiguous. Pass filePath to select a target.${candidates ? ` Candidates: ${candidates}.` : ""}`;
|
|
15704
|
+
}
|
|
15705
|
+
if (resolution.filePath && resolution.totalCandidates > 0) {
|
|
15706
|
+
return `Risk: symbol "${resolution.name}" did not resolve at filePath="${resolution.filePath}". Review the conceptual evidence before editing.`;
|
|
15707
|
+
}
|
|
15708
|
+
return `Risk: symbol "${resolution.name}" could not be resolved. Review the conceptual evidence before editing.`;
|
|
15709
|
+
}
|
|
15710
|
+
async function fallbackPack(dependencies, query, risk, resolution, tokenBudget, candidateSource = []) {
|
|
15711
|
+
const conceptual = await dependencies.searchCodebase(query, { limit: 5 });
|
|
15712
|
+
const pack = buildContextPack([...candidateSource, ...conceptual], {
|
|
15713
|
+
tokenBudget: tokenBudget ?? void 0,
|
|
15714
|
+
heading: "## Conceptual evidence",
|
|
15715
|
+
maxResults: 5,
|
|
15716
|
+
includeExactSearchHandoff: false,
|
|
15717
|
+
preferImplementationPaths: true
|
|
15718
|
+
});
|
|
15719
|
+
const fitted = fitTextToContextBudget(`${risk}
|
|
15720
|
+
|
|
15721
|
+
${pack.text}`, tokenBudget ?? void 0);
|
|
15722
|
+
return {
|
|
15723
|
+
text: fitted.text,
|
|
15724
|
+
details: {
|
|
15725
|
+
resolution,
|
|
15726
|
+
tokenBudget: fitted.tokenBudget,
|
|
15727
|
+
tokenEstimate: fitted.tokenEstimate,
|
|
15728
|
+
truncated: fitted.truncated,
|
|
15729
|
+
sourceIncluded: candidateSource.length > 0,
|
|
15730
|
+
callerCount: 0,
|
|
15731
|
+
calleeCount: 0
|
|
15732
|
+
}
|
|
15733
|
+
};
|
|
15734
|
+
}
|
|
15735
|
+
async function resolveCodebaseEditContextWithDependencies(input, dependencies) {
|
|
15736
|
+
const symbol = input.symbol?.trim();
|
|
15737
|
+
if (!symbol) {
|
|
15738
|
+
return fallbackPack(
|
|
15739
|
+
dependencies,
|
|
15740
|
+
input.query,
|
|
15741
|
+
"Risk: no authoritative symbol was supplied. Review the conceptual evidence before editing.",
|
|
15742
|
+
"not_requested",
|
|
15743
|
+
input.tokenBudget
|
|
15744
|
+
);
|
|
15745
|
+
}
|
|
15746
|
+
let callersResult;
|
|
15747
|
+
try {
|
|
15748
|
+
callersResult = await dependencies.getCallGraphData({
|
|
15749
|
+
name: symbol,
|
|
15750
|
+
filePath: input.filePath ?? void 0,
|
|
15751
|
+
direction: "callers"
|
|
15752
|
+
});
|
|
15753
|
+
} catch (error) {
|
|
15754
|
+
const candidates = await dependencies.implementationLookup(symbol, { limit: 5 });
|
|
15755
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15756
|
+
return fallbackPack(
|
|
15757
|
+
dependencies,
|
|
15758
|
+
input.query,
|
|
15759
|
+
`Risk: graph data is unavailable (${message}). Target and dependencies are not graph-verified.`,
|
|
15760
|
+
"graph_unavailable",
|
|
15761
|
+
input.tokenBudget,
|
|
15762
|
+
candidates
|
|
15763
|
+
);
|
|
15764
|
+
}
|
|
15765
|
+
if (callersResult.resolution.status !== "resolved") {
|
|
15766
|
+
return fallbackPack(
|
|
15767
|
+
dependencies,
|
|
15768
|
+
input.query,
|
|
15769
|
+
formatResolutionRisk(callersResult.resolution),
|
|
15770
|
+
callersResult.resolution.status,
|
|
15771
|
+
input.tokenBudget
|
|
15772
|
+
);
|
|
15773
|
+
}
|
|
15774
|
+
const resolution = callersResult.resolution;
|
|
15775
|
+
const [definitionsResult, calleesResult] = await Promise.allSettled([
|
|
15776
|
+
dependencies.implementationLookup(symbol, { limit: 10 }),
|
|
15777
|
+
dependencies.getCallGraphData({
|
|
15778
|
+
name: symbol,
|
|
15779
|
+
filePath: input.filePath ?? resolution.filePath,
|
|
15780
|
+
direction: "callees"
|
|
15781
|
+
})
|
|
15782
|
+
]);
|
|
15783
|
+
if (definitionsResult.status === "rejected") throw definitionsResult.reason;
|
|
15784
|
+
let graphRisk;
|
|
15785
|
+
let callees = [];
|
|
15786
|
+
if (calleesResult.status === "rejected") {
|
|
15787
|
+
const message = calleesResult.reason instanceof Error ? calleesResult.reason.message : String(calleesResult.reason);
|
|
15788
|
+
graphRisk = `Risk: callee graph data is unavailable (${message}). Dependency evidence is incomplete.`;
|
|
15789
|
+
} else if (calleesResult.value.resolution.status !== "resolved") {
|
|
15790
|
+
graphRisk = "Risk: the target resolved for callers but not callees. Dependency evidence is incomplete.";
|
|
15791
|
+
} else {
|
|
15792
|
+
callees = calleesResult.value.callees.slice(0, edgeLimit(input.calleeLimit));
|
|
15793
|
+
}
|
|
15794
|
+
const source = targetSource(definitionsResult.value, resolution);
|
|
15795
|
+
const callers = callersResult.callers.slice(0, edgeLimit(input.callerLimit));
|
|
15796
|
+
const sourceBudget = Math.max(
|
|
15797
|
+
MIN_CONTEXT_PACK_TOKEN_BUDGET,
|
|
15798
|
+
Math.floor((input.tokenBudget ?? DEFAULT_CONTEXT_PACK_TOKEN_BUDGET) * 0.6)
|
|
15799
|
+
);
|
|
15800
|
+
const sourceText = source ? fitTextToContextBudget(formatSource(source), sourceBudget).text : `## Target implementation
|
|
15801
|
+
Risk: no implementation source matched the resolved target at ${resolution.filePath}:${resolution.startLine}.`;
|
|
15802
|
+
const fitted = fitTextToContextBudget([
|
|
15803
|
+
`# Pre-edit context for ${resolution.name}`,
|
|
15804
|
+
graphRisk,
|
|
15805
|
+
sourceText,
|
|
15806
|
+
formatCallers(callers),
|
|
15807
|
+
formatCallees(callees, resolution.filePath)
|
|
15808
|
+
].filter((section) => section !== void 0).join("\n\n"), input.tokenBudget ?? void 0);
|
|
15809
|
+
return {
|
|
15810
|
+
text: fitted.text,
|
|
15811
|
+
details: {
|
|
15812
|
+
resolution: "resolved",
|
|
15813
|
+
tokenBudget: fitted.tokenBudget,
|
|
15814
|
+
tokenEstimate: fitted.tokenEstimate,
|
|
15815
|
+
truncated: fitted.truncated,
|
|
15816
|
+
sourceIncluded: source !== void 0,
|
|
15817
|
+
callerCount: callers.length,
|
|
15818
|
+
calleeCount: callees.length
|
|
15819
|
+
}
|
|
15820
|
+
};
|
|
15821
|
+
}
|
|
15822
|
+
async function resolveCodebaseEditContext(projectRoot, host, input) {
|
|
15823
|
+
return resolveCodebaseEditContextWithDependencies(input, {
|
|
15824
|
+
searchCodebase: (query, options) => searchCodebase(projectRoot, host, query, options),
|
|
15825
|
+
implementationLookup: (query, options) => implementationLookup(projectRoot, host, query, options),
|
|
15826
|
+
getCallGraphData: (params) => getCallGraphData(projectRoot, host, params)
|
|
15827
|
+
});
|
|
15828
|
+
}
|
|
15829
|
+
|
|
15480
15830
|
// src/eval/budget.ts
|
|
15481
15831
|
function evaluateBudgetGate(budget, summary, comparison) {
|
|
15482
15832
|
const BASELINE_P95_EPSILON_MS = 1e-3;
|
|
@@ -15500,6 +15850,24 @@ function evaluateBudgetGate(budget, summary, comparison) {
|
|
|
15500
15850
|
message: `Raw Distinct Top@3 ${summary.metrics.rawDistinctTop3Ratio.toFixed(4)} is below minimum ${thresholds.minRawDistinctTop3Ratio.toFixed(4)}`
|
|
15501
15851
|
});
|
|
15502
15852
|
}
|
|
15853
|
+
if (thresholds.minGraphNeighborRecall !== void 0 && summary.metrics.graphNeighborRecall !== void 0 && summary.metrics.graphNeighborRecall < thresholds.minGraphNeighborRecall) {
|
|
15854
|
+
violations.push({
|
|
15855
|
+
metric: "minGraphNeighborRecall",
|
|
15856
|
+
message: `Graph-neighbor recall ${summary.metrics.graphNeighborRecall.toFixed(4)} is below minimum ${thresholds.minGraphNeighborRecall.toFixed(4)}`
|
|
15857
|
+
});
|
|
15858
|
+
}
|
|
15859
|
+
if (thresholds.minRouteAccuracy !== void 0 && summary.metrics.routeAccuracy < thresholds.minRouteAccuracy) {
|
|
15860
|
+
violations.push({
|
|
15861
|
+
metric: "minRouteAccuracy",
|
|
15862
|
+
message: `Route accuracy ${summary.metrics.routeAccuracy.toFixed(4)} is below minimum ${thresholds.minRouteAccuracy.toFixed(4)}`
|
|
15863
|
+
});
|
|
15864
|
+
}
|
|
15865
|
+
if (thresholds.minOutcomeAccuracy !== void 0 && summary.metrics.outcomeAccuracy < thresholds.minOutcomeAccuracy) {
|
|
15866
|
+
violations.push({
|
|
15867
|
+
metric: "minOutcomeAccuracy",
|
|
15868
|
+
message: `Outcome accuracy ${summary.metrics.outcomeAccuracy.toFixed(4)} is below minimum ${thresholds.minOutcomeAccuracy.toFixed(4)}`
|
|
15869
|
+
});
|
|
15870
|
+
}
|
|
15503
15871
|
if (comparison) {
|
|
15504
15872
|
if (thresholds.hitAt5MaxDrop !== void 0 && comparison.deltas.hitAt5.absolute < -thresholds.hitAt5MaxDrop) {
|
|
15505
15873
|
violations.push({
|
|
@@ -15677,6 +16045,14 @@ function isSymbolIntended(query) {
|
|
|
15677
16045
|
function isExpectedFile(filePath, relevant) {
|
|
15678
16046
|
return relevant.some((entry) => pathMatchesExpected(filePath, entry.path));
|
|
15679
16047
|
}
|
|
16048
|
+
function graphNeighborMatches(query, result) {
|
|
16049
|
+
const expected = query.expected.graphNeighbor;
|
|
16050
|
+
if (!expected || result.graphDirection !== expected.direction) return false;
|
|
16051
|
+
if (expected.filePath !== void 0 && !pathMatchesExpected(result.filePath, expected.filePath)) {
|
|
16052
|
+
return false;
|
|
16053
|
+
}
|
|
16054
|
+
return expected.symbol === void 0 || result.name === expected.symbol;
|
|
16055
|
+
}
|
|
15680
16056
|
function resultRelevance(filePath, symbol, relevant, isSymbolIntendedQuery) {
|
|
15681
16057
|
let relevance = 0;
|
|
15682
16058
|
for (const entry of relevant) {
|
|
@@ -15824,6 +16200,7 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
15824
16200
|
routeMatched: query.expected.expectedRoute ? query.expected.expectedRoute === route.resolvedRoute : void 0,
|
|
15825
16201
|
outcomeMatched: query.expected.expectedOutcome === void 0 ? void 0 : query.expected.expectedOutcome === "results" ? deduped.length > 0 : deduped.length === 0,
|
|
15826
16202
|
recoveryMatched: query.expected.recoveryExpectation === void 0 ? void 0 : query.expected.recoveryExpectation === "filter-relaxed" ? context?.recoveryRelaxed === true : context?.recoveryUsed !== true,
|
|
16203
|
+
graphNeighborMatched: query.expected.graphNeighbor === void 0 ? void 0 : results.some((result) => graphNeighborMatches(query, result)),
|
|
15827
16204
|
language: query.language,
|
|
15828
16205
|
difficulty: query.difficulty,
|
|
15829
16206
|
tags: query.tags,
|
|
@@ -15873,7 +16250,7 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15873
16250
|
"no-relevant-hit-top-k": 0
|
|
15874
16251
|
};
|
|
15875
16252
|
const latencies = perQuery.map((item) => item.latencyMs);
|
|
15876
|
-
const contextQueries = perQuery.filter((item) => item.retrievalMode
|
|
16253
|
+
const contextQueries = perQuery.filter((item) => item.retrievalMode !== "search");
|
|
15877
16254
|
const contextResponseTokens = contextQueries.map((item) => item.responseTokens);
|
|
15878
16255
|
const totalContextResponseTokens = contextResponseTokens.reduce((sum2, value) => sum2 + value, 0);
|
|
15879
16256
|
const contextTokenUnits = totalContextResponseTokens / 1e3;
|
|
@@ -15883,6 +16260,8 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15883
16260
|
let outcomeExpectedCount = 0;
|
|
15884
16261
|
let recoveryMatchedCount = 0;
|
|
15885
16262
|
let recoveryExpectedCount = 0;
|
|
16263
|
+
let graphNeighborMatchedCount = 0;
|
|
16264
|
+
let graphNeighborExpectedCount = 0;
|
|
15886
16265
|
for (const query of perQuery) {
|
|
15887
16266
|
if (positiveQueryIds.has(query.id)) {
|
|
15888
16267
|
if (query.hitAt1) sum.hitAt1 += 1;
|
|
@@ -15911,6 +16290,10 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15911
16290
|
recoveryExpectedCount += 1;
|
|
15912
16291
|
if (query.recoveryMatched) recoveryMatchedCount += 1;
|
|
15913
16292
|
}
|
|
16293
|
+
if (query.graphNeighborMatched !== void 0) {
|
|
16294
|
+
graphNeighborExpectedCount += 1;
|
|
16295
|
+
if (query.graphNeighborMatched) graphNeighborMatchedCount += 1;
|
|
16296
|
+
}
|
|
15914
16297
|
}
|
|
15915
16298
|
const queryTokens = queries.reduce((acc, q) => acc + estimateTokens(q.query), 0);
|
|
15916
16299
|
return {
|
|
@@ -15923,6 +16306,7 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15923
16306
|
routeAccuracy: routeExpectedCount === 0 ? 0 : routeMatchedCount / routeExpectedCount,
|
|
15924
16307
|
outcomeAccuracy: outcomeExpectedCount === 0 ? 0 : outcomeMatchedCount / outcomeExpectedCount,
|
|
15925
16308
|
recoveryAccuracy: recoveryExpectedCount === 0 ? 0 : recoveryMatchedCount / recoveryExpectedCount,
|
|
16309
|
+
graphNeighborRecall: graphNeighborExpectedCount === 0 ? 0 : graphNeighborMatchedCount / graphNeighborExpectedCount,
|
|
15926
16310
|
distinctTop3Ratio: safeDiv(sum.distinctTop3Ratio),
|
|
15927
16311
|
rawDistinctTop3Ratio: safeDiv(sum.rawDistinctTop3Ratio),
|
|
15928
16312
|
latencyMs: {
|
|
@@ -16188,14 +16572,29 @@ function parseQueryArgs(value, path31) {
|
|
|
16188
16572
|
throw new Error(`${path31} must be an object`);
|
|
16189
16573
|
}
|
|
16190
16574
|
const symbol = parseStringOrUndefined(value.symbol, `${path31}.symbol`);
|
|
16575
|
+
const filePath = parseStringOrUndefined(value.filePath, `${path31}.filePath`);
|
|
16191
16576
|
const fileType = parseStringOrUndefined(value.fileType, `${path31}.fileType`);
|
|
16192
16577
|
const directory = parseStringOrUndefined(value.directory, `${path31}.directory`);
|
|
16578
|
+
const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path31}.callerLimit`);
|
|
16579
|
+
const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path31}.calleeLimit`);
|
|
16580
|
+
const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path31}.tokenBudget`);
|
|
16193
16581
|
return {
|
|
16194
16582
|
...symbol !== void 0 ? { symbol } : {},
|
|
16583
|
+
...filePath !== void 0 ? { filePath } : {},
|
|
16195
16584
|
...fileType !== void 0 ? { fileType } : {},
|
|
16196
|
-
...directory !== void 0 ? { directory } : {}
|
|
16585
|
+
...directory !== void 0 ? { directory } : {},
|
|
16586
|
+
...callerLimit !== void 0 ? { callerLimit } : {},
|
|
16587
|
+
...calleeLimit !== void 0 ? { calleeLimit } : {},
|
|
16588
|
+
...tokenBudget !== void 0 ? { tokenBudget } : {}
|
|
16197
16589
|
};
|
|
16198
16590
|
}
|
|
16591
|
+
function parsePositiveIntegerOrUndefined(value, path31) {
|
|
16592
|
+
if (value === void 0 || value === null) return void 0;
|
|
16593
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
16594
|
+
throw new Error(`${path31} must be a positive integer`);
|
|
16595
|
+
}
|
|
16596
|
+
return value;
|
|
16597
|
+
}
|
|
16199
16598
|
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-]+)*)?$/;
|
|
16200
16599
|
function parseSemanticVersion(value, path31) {
|
|
16201
16600
|
if (!isNonEmptyString(value)) {
|
|
@@ -16208,8 +16607,8 @@ function parseSemanticVersion(value, path31) {
|
|
|
16208
16607
|
}
|
|
16209
16608
|
function parseRetrievalMode(value, path31) {
|
|
16210
16609
|
if (value === void 0 || value === "search") return "search";
|
|
16211
|
-
if (value === "context") return value;
|
|
16212
|
-
throw new Error(`${path31} must be one of: search, context`);
|
|
16610
|
+
if (value === "context" || value === "edit-context") return value;
|
|
16611
|
+
throw new Error(`${path31} must be one of: search, context, edit-context`);
|
|
16213
16612
|
}
|
|
16214
16613
|
function parseStringOrUndefined(value, path31) {
|
|
16215
16614
|
if (value === void 0 || value === null) return void 0;
|
|
@@ -16249,6 +16648,25 @@ function parseEvidenceRelevance(value, path31) {
|
|
|
16249
16648
|
}
|
|
16250
16649
|
return value;
|
|
16251
16650
|
}
|
|
16651
|
+
function parseExpectedGraphNeighbor(value, path31) {
|
|
16652
|
+
if (value === void 0) return void 0;
|
|
16653
|
+
if (!isRecord3(value)) {
|
|
16654
|
+
throw new Error(`${path31} must be an object`);
|
|
16655
|
+
}
|
|
16656
|
+
if (value.direction !== "caller" && value.direction !== "callee") {
|
|
16657
|
+
throw new Error(`${path31}.direction must be one of: caller, callee`);
|
|
16658
|
+
}
|
|
16659
|
+
const filePath = parseStringOrUndefined(value.filePath, `${path31}.filePath`);
|
|
16660
|
+
const symbol = parseStringOrUndefined(value.symbol, `${path31}.symbol`);
|
|
16661
|
+
if (filePath === void 0 && symbol === void 0) {
|
|
16662
|
+
throw new Error(`${path31} must include filePath or symbol`);
|
|
16663
|
+
}
|
|
16664
|
+
return {
|
|
16665
|
+
direction: value.direction,
|
|
16666
|
+
...filePath !== void 0 ? { filePath } : {},
|
|
16667
|
+
...symbol !== void 0 ? { symbol } : {}
|
|
16668
|
+
};
|
|
16669
|
+
}
|
|
16252
16670
|
function parseExpected(input, path31) {
|
|
16253
16671
|
if (!isRecord3(input)) {
|
|
16254
16672
|
throw new Error(`${path31} must be an object`);
|
|
@@ -16261,9 +16679,11 @@ function parseExpected(input, path31) {
|
|
|
16261
16679
|
const expectedOutcomeRaw = input.expectedOutcome;
|
|
16262
16680
|
const recoveryExpectationRaw = input.recoveryExpectation;
|
|
16263
16681
|
const gradedEvidenceRaw = input.gradedEvidence;
|
|
16682
|
+
const graphNeighborRaw = input.graphNeighbor;
|
|
16264
16683
|
const filePath = parseStringOrUndefined(filePathRaw, `${path31}.filePath`);
|
|
16265
16684
|
const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
16266
16685
|
const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path31}.gradedEvidence`);
|
|
16686
|
+
const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path31}.graphNeighbor`);
|
|
16267
16687
|
const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path31}.expectedOutcome`);
|
|
16268
16688
|
if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
|
|
16269
16689
|
throw new Error(
|
|
@@ -16292,7 +16712,8 @@ function parseExpected(input, path31) {
|
|
|
16292
16712
|
expectedRoute,
|
|
16293
16713
|
expectedOutcome,
|
|
16294
16714
|
recoveryExpectation,
|
|
16295
|
-
...gradedEvidence.length > 0 ? { gradedEvidence } : {}
|
|
16715
|
+
...gradedEvidence.length > 0 ? { gradedEvidence } : {},
|
|
16716
|
+
...graphNeighbor !== void 0 ? { graphNeighbor } : {}
|
|
16296
16717
|
};
|
|
16297
16718
|
}
|
|
16298
16719
|
function parseQueryLanguage(value, path31) {
|
|
@@ -16427,6 +16848,21 @@ function parseBudget(raw, sourceLabel) {
|
|
|
16427
16848
|
"minRawDistinctTop3Ratio",
|
|
16428
16849
|
sourceLabel
|
|
16429
16850
|
),
|
|
16851
|
+
minGraphNeighborRecall: parseThresholdValue(
|
|
16852
|
+
thresholds.minGraphNeighborRecall,
|
|
16853
|
+
"minGraphNeighborRecall",
|
|
16854
|
+
sourceLabel
|
|
16855
|
+
),
|
|
16856
|
+
minRouteAccuracy: parseThresholdValue(
|
|
16857
|
+
thresholds.minRouteAccuracy,
|
|
16858
|
+
"minRouteAccuracy",
|
|
16859
|
+
sourceLabel
|
|
16860
|
+
),
|
|
16861
|
+
minOutcomeAccuracy: parseThresholdValue(
|
|
16862
|
+
thresholds.minOutcomeAccuracy,
|
|
16863
|
+
"minOutcomeAccuracy",
|
|
16864
|
+
sourceLabel
|
|
16865
|
+
),
|
|
16430
16866
|
maxContextResponseTokensAverage: parseThresholdValue(
|
|
16431
16867
|
thresholds.maxContextResponseTokensAverage,
|
|
16432
16868
|
"maxContextResponseTokensAverage",
|
|
@@ -16491,6 +16927,120 @@ function buildDatasetFingerprint(dataset) {
|
|
|
16491
16927
|
const canonical = JSON.stringify(normalizeForFingerprint(dataset));
|
|
16492
16928
|
return crypto2.createHash("sha256").update(canonical).digest("hex");
|
|
16493
16929
|
}
|
|
16930
|
+
function normalizedPath2(value) {
|
|
16931
|
+
return value.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
16932
|
+
}
|
|
16933
|
+
function pathsMatch2(left, right) {
|
|
16934
|
+
const normalizedLeft = normalizedPath2(left);
|
|
16935
|
+
const normalizedRight = normalizedPath2(right);
|
|
16936
|
+
return normalizedLeft === normalizedRight || normalizedLeft.endsWith(`/${normalizedRight}`) || normalizedRight.endsWith(`/${normalizedLeft}`);
|
|
16937
|
+
}
|
|
16938
|
+
function toEvalSearchResult(result) {
|
|
16939
|
+
return {
|
|
16940
|
+
filePath: result.filePath,
|
|
16941
|
+
startLine: result.startLine,
|
|
16942
|
+
endLine: result.endLine,
|
|
16943
|
+
score: result.score,
|
|
16944
|
+
chunkType: result.chunkType,
|
|
16945
|
+
name: result.name
|
|
16946
|
+
};
|
|
16947
|
+
}
|
|
16948
|
+
function selectResolvedTarget(definitions, resolution) {
|
|
16949
|
+
if (resolution?.status !== "resolved") return definitions[0];
|
|
16950
|
+
return definitions.find((result) => pathsMatch2(result.filePath, resolution.filePath) && result.startLine <= resolution.startLine && result.endLine >= resolution.startLine) ?? definitions.find((result) => pathsMatch2(result.filePath, resolution.filePath) && result.name === resolution.name) ?? definitions[0];
|
|
16951
|
+
}
|
|
16952
|
+
function callerResult(edge) {
|
|
16953
|
+
if (!edge.fromSymbolFilePath) return void 0;
|
|
16954
|
+
return {
|
|
16955
|
+
filePath: edge.fromSymbolFilePath,
|
|
16956
|
+
startLine: edge.line,
|
|
16957
|
+
endLine: edge.line,
|
|
16958
|
+
score: 0,
|
|
16959
|
+
chunkType: "graph-caller",
|
|
16960
|
+
name: edge.fromSymbolName,
|
|
16961
|
+
graphDirection: "caller"
|
|
16962
|
+
};
|
|
16963
|
+
}
|
|
16964
|
+
function calleeResult(edge, symbols) {
|
|
16965
|
+
const symbol = edge.toSymbolId ? symbols.find((candidate) => candidate.id === edge.toSymbolId) : symbols.filter((candidate) => candidate.name === edge.targetName).length === 1 ? symbols.find((candidate) => candidate.name === edge.targetName) : void 0;
|
|
16966
|
+
if (!symbol) return void 0;
|
|
16967
|
+
return {
|
|
16968
|
+
filePath: symbol.filePath,
|
|
16969
|
+
startLine: symbol.startLine,
|
|
16970
|
+
endLine: symbol.endLine,
|
|
16971
|
+
score: 0,
|
|
16972
|
+
chunkType: "graph-callee",
|
|
16973
|
+
name: symbol.name,
|
|
16974
|
+
graphDirection: "callee"
|
|
16975
|
+
};
|
|
16976
|
+
}
|
|
16977
|
+
async function runEditContextQuery(indexer, projectRoot, query) {
|
|
16978
|
+
let definitions = [];
|
|
16979
|
+
let conceptual = [];
|
|
16980
|
+
let callers;
|
|
16981
|
+
let callees;
|
|
16982
|
+
const editContext = await resolveCodebaseEditContextWithDependencies({
|
|
16983
|
+
query: query.query,
|
|
16984
|
+
symbol: query.args?.symbol,
|
|
16985
|
+
filePath: query.args?.filePath ?? query.expected.filePath,
|
|
16986
|
+
callerLimit: query.args?.callerLimit,
|
|
16987
|
+
calleeLimit: query.args?.calleeLimit,
|
|
16988
|
+
tokenBudget: query.args?.tokenBudget
|
|
16989
|
+
}, {
|
|
16990
|
+
searchCodebase: async (searchQuery, options) => {
|
|
16991
|
+
conceptual = await indexer.search(searchQuery, options?.limit, {
|
|
16992
|
+
filterByBranch: !!query.expected.branch
|
|
16993
|
+
});
|
|
16994
|
+
return conceptual;
|
|
16995
|
+
},
|
|
16996
|
+
implementationLookup: async (symbol, options) => {
|
|
16997
|
+
definitions = await indexer.search(symbol, options?.limit, {
|
|
16998
|
+
filterByBranch: !!query.expected.branch,
|
|
16999
|
+
definitionIntent: true
|
|
17000
|
+
});
|
|
17001
|
+
return definitions;
|
|
17002
|
+
},
|
|
17003
|
+
getCallGraphData: async (params) => {
|
|
17004
|
+
const result = await getCallGraphDataForIndexer(indexer, projectRoot, params);
|
|
17005
|
+
if (params.direction === "callers") callers = result;
|
|
17006
|
+
else callees = result;
|
|
17007
|
+
return result;
|
|
17008
|
+
}
|
|
17009
|
+
});
|
|
17010
|
+
const resolution = callers?.resolution;
|
|
17011
|
+
const target = selectResolvedTarget(definitions, resolution);
|
|
17012
|
+
const targetCandidates = target ? [target] : [...definitions, ...conceptual];
|
|
17013
|
+
const results = targetCandidates.filter((candidate) => (resolution?.status !== "resolved" || editContext.details.sourceIncluded) && editContext.text.includes(
|
|
17014
|
+
`${candidate.filePath}:${candidate.startLine}-${candidate.endLine}`
|
|
17015
|
+
)).map(toEvalSearchResult);
|
|
17016
|
+
if (query.expected.graphNeighbor) {
|
|
17017
|
+
const symbols = await indexer.getCallGraphSymbols();
|
|
17018
|
+
const callerLimit = query.args?.callerLimit ?? DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT;
|
|
17019
|
+
const calleeLimit = query.args?.calleeLimit ?? DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT;
|
|
17020
|
+
const publishedCallers = (callers?.callers ?? []).slice(0, callerLimit).filter((edge) => editContext.text.includes(
|
|
17021
|
+
`${edge.fromSymbolName ?? "<unknown>"} at ${edge.fromSymbolFilePath ?? "<unknown file>"}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`
|
|
17022
|
+
));
|
|
17023
|
+
const publishedCallees = (callees?.callees ?? []).slice(0, calleeLimit).filter((edge) => resolution?.status === "resolved" && editContext.text.includes(
|
|
17024
|
+
`${edge.targetName} from ${resolution.filePath}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`
|
|
17025
|
+
));
|
|
17026
|
+
results.push(
|
|
17027
|
+
...publishedCallers.map(callerResult).filter((item) => item !== void 0),
|
|
17028
|
+
...publishedCallees.map((edge) => calleeResult(edge, symbols)).filter((item) => item !== void 0)
|
|
17029
|
+
);
|
|
17030
|
+
}
|
|
17031
|
+
return {
|
|
17032
|
+
results,
|
|
17033
|
+
resolvedRoute: resolution?.status === "resolved" ? "definition" : "search",
|
|
17034
|
+
routedQuery: query.args?.symbol ?? query.query,
|
|
17035
|
+
context: {
|
|
17036
|
+
tokenBudget: editContext.details.tokenBudget,
|
|
17037
|
+
responseTokens: editContext.details.tokenEstimate,
|
|
17038
|
+
candidateCount: results.length,
|
|
17039
|
+
deduplicatedCount: results.length,
|
|
17040
|
+
omittedCount: 0
|
|
17041
|
+
}
|
|
17042
|
+
};
|
|
17043
|
+
}
|
|
16494
17044
|
async function runEvaluation(options) {
|
|
16495
17045
|
const datasetPath = toAbsolute(options.projectRoot, options.datasetPath);
|
|
16496
17046
|
const againstPath = options.againstPath ? toAbsolute(options.projectRoot, options.againstPath) : void 0;
|
|
@@ -16522,6 +17072,7 @@ async function runEvaluation(options) {
|
|
|
16522
17072
|
);
|
|
16523
17073
|
}
|
|
16524
17074
|
const start = import_perf_hooks2.performance.now();
|
|
17075
|
+
const editContextResult = query.retrievalMode === "edit-context" ? await runEditContextQuery(indexer, options.projectRoot, query) : void 0;
|
|
16525
17076
|
const contextResult = query.retrievalMode === "context" ? await resolveSearchContext({
|
|
16526
17077
|
query: query.query,
|
|
16527
17078
|
symbol: query.args?.symbol,
|
|
@@ -16545,31 +17096,32 @@ async function runEvaluation(options) {
|
|
|
16545
17096
|
directory: scope.directory
|
|
16546
17097
|
})
|
|
16547
17098
|
}) : void 0;
|
|
16548
|
-
const result = contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
17099
|
+
const result = editContextResult?.results ?? contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
16549
17100
|
metadataOnly: true,
|
|
16550
17101
|
filterByBranch: !!query.expected.branch,
|
|
16551
17102
|
fileType: query.args?.fileType,
|
|
16552
17103
|
directory: query.args?.directory
|
|
16553
17104
|
});
|
|
16554
17105
|
const elapsed = import_perf_hooks2.performance.now() - start;
|
|
16555
|
-
const resolvedRoute = contextResult?.details?.route === "definition" ? "definition" : "search";
|
|
16556
|
-
const routedQuery = contextResult?.details?.routedQuery ?? query.query;
|
|
17106
|
+
const resolvedRoute = editContextResult?.resolvedRoute ?? (contextResult?.details?.route === "definition" ? "definition" : "search");
|
|
17107
|
+
const routedQuery = editContextResult?.routedQuery ?? contextResult?.details?.routedQuery ?? query.query;
|
|
16557
17108
|
const successfulRecoveryAttempt = contextResult?.details?.recovery?.successfulAttemptIndex;
|
|
16558
17109
|
const recoveryAttempts = contextResult?.details?.recovery?.attempts ?? [];
|
|
16559
17110
|
const recoveryRelaxed = successfulRecoveryAttempt === void 0 ? false : (recoveryAttempts[successfulRecoveryAttempt]?.relaxedFields.length ?? 0) > 0;
|
|
16560
17111
|
const recoveryUsed = recoveryAttempts.length > 1 || recoveryAttempts.some((attempt) => attempt.relaxedFields.length > 0);
|
|
16561
|
-
const materialized = result.map((item) =>
|
|
16562
|
-
|
|
16563
|
-
|
|
16564
|
-
|
|
16565
|
-
|
|
16566
|
-
|
|
16567
|
-
|
|
16568
|
-
|
|
16569
|
-
|
|
16570
|
-
|
|
16571
|
-
|
|
16572
|
-
}
|
|
17112
|
+
const materialized = result.map((item) => {
|
|
17113
|
+
const graphDirection = "graphDirection" in item && (item.graphDirection === "caller" || item.graphDirection === "callee") ? item.graphDirection : void 0;
|
|
17114
|
+
return {
|
|
17115
|
+
filePath: item.filePath,
|
|
17116
|
+
startLine: item.startLine,
|
|
17117
|
+
endLine: item.endLine,
|
|
17118
|
+
score: item.score,
|
|
17119
|
+
chunkType: item.chunkType,
|
|
17120
|
+
name: item.name,
|
|
17121
|
+
graphDirection
|
|
17122
|
+
};
|
|
17123
|
+
});
|
|
17124
|
+
const contextMeasurement = editContextResult?.context ?? (contextResult?.details ? {
|
|
16573
17125
|
tokenBudget: contextResult.details.tokenBudget,
|
|
16574
17126
|
responseTokens: contextResult.details.tokenEstimate,
|
|
16575
17127
|
candidateCount: contextResult.details.candidateCount ?? 0,
|
|
@@ -16577,7 +17129,11 @@ async function runEvaluation(options) {
|
|
|
16577
17129
|
omittedCount: contextResult.details.omittedCount ?? 0,
|
|
16578
17130
|
recoveryUsed,
|
|
16579
17131
|
recoveryRelaxed
|
|
16580
|
-
} : void 0)
|
|
17132
|
+
} : void 0);
|
|
17133
|
+
perQuery.push(buildPerQueryResult(query, materialized, elapsed, 10, {
|
|
17134
|
+
resolvedRoute,
|
|
17135
|
+
routedQuery
|
|
17136
|
+
}, contextMeasurement));
|
|
16581
17137
|
}
|
|
16582
17138
|
const logger = indexer.getLogger();
|
|
16583
17139
|
const metricSnapshot = logger.getMetrics();
|
|
@@ -17114,7 +17670,11 @@ var import_zod2 = require("zod");
|
|
|
17114
17670
|
|
|
17115
17671
|
// src/tools/execute-common.ts
|
|
17116
17672
|
async function executeCodebaseContext(projectRoot, host, args) {
|
|
17117
|
-
|
|
17673
|
+
const result = await resolveCodebaseContext(projectRoot, host, args);
|
|
17674
|
+
return { text: result.text, details: result.details };
|
|
17675
|
+
}
|
|
17676
|
+
async function executeCodebaseEditContext(projectRoot, host, args) {
|
|
17677
|
+
return { text: (await resolveCodebaseEditContext(projectRoot, host, args)).text };
|
|
17118
17678
|
}
|
|
17119
17679
|
async function executeIndexCodebase(projectRoot, host, args, onProgress) {
|
|
17120
17680
|
const result = await runIndexCodebase(projectRoot, host, args, onProgress);
|
|
@@ -17230,6 +17790,7 @@ function formatPrImpact(result) {
|
|
|
17230
17790
|
// src/tools/tool-names.ts
|
|
17231
17791
|
var TOOL_NAME = {
|
|
17232
17792
|
CODEBASE_CONTEXT: "codebase_context",
|
|
17793
|
+
CODEBASE_EDIT_CONTEXT: "codebase_edit_context",
|
|
17233
17794
|
CODEBASE_SEARCH: "codebase_search",
|
|
17234
17795
|
CODEBASE_PEEK: "codebase_peek",
|
|
17235
17796
|
FIND_SIMILAR: "find_similar",
|
|
@@ -17253,6 +17814,7 @@ var TOOL_NAME = {
|
|
|
17253
17814
|
};
|
|
17254
17815
|
var PORTABLE_TOOL_NAMES = [
|
|
17255
17816
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
17817
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17256
17818
|
TOOL_NAME.CODEBASE_SEARCH,
|
|
17257
17819
|
TOOL_NAME.CODEBASE_PEEK,
|
|
17258
17820
|
TOOL_NAME.INDEX_CODEBASE,
|
|
@@ -17269,6 +17831,7 @@ var PORTABLE_TOOL_NAMES = [
|
|
|
17269
17831
|
];
|
|
17270
17832
|
var OPENCODE_TOOL_NAMES = [
|
|
17271
17833
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
17834
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17272
17835
|
TOOL_NAME.CODEBASE_SEARCH,
|
|
17273
17836
|
TOOL_NAME.CODEBASE_PEEK,
|
|
17274
17837
|
TOOL_NAME.INDEX_CODEBASE,
|
|
@@ -17289,6 +17852,7 @@ var OPENCODE_TOOL_NAMES = [
|
|
|
17289
17852
|
];
|
|
17290
17853
|
var PI_TOOL_NAMES = [
|
|
17291
17854
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
17855
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17292
17856
|
TOOL_NAME.CODEBASE_SEARCH,
|
|
17293
17857
|
TOOL_NAME.CODEBASE_PEEK,
|
|
17294
17858
|
TOOL_NAME.FIND_SIMILAR,
|
|
@@ -17332,10 +17896,30 @@ function registerMcpTools(server, runtime) {
|
|
|
17332
17896
|
directory: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
17333
17897
|
tokenBudget: allowNullAsUndefined(
|
|
17334
17898
|
import_zod2.z.number().int().min(MIN_CONTEXT_PACK_TOKEN_BUDGET).max(MAX_CONTEXT_PACK_TOKEN_BUDGET).optional().default(DEFAULT_CONTEXT_PACK_TOKEN_BUDGET)
|
|
17335
|
-
).describe(`Maximum response tokens for this context pack (${MIN_CONTEXT_PACK_TOKEN_BUDGET}-${MAX_CONTEXT_PACK_TOKEN_BUDGET})`)
|
|
17899
|
+
).describe(`Maximum response tokens for this context pack (${MIN_CONTEXT_PACK_TOKEN_BUDGET}-${MAX_CONTEXT_PACK_TOKEN_BUDGET})`),
|
|
17900
|
+
diagnostic: import_zod2.z.boolean().optional().describe("Collect diagnostic routing and search traces without changing normal text output.")
|
|
17336
17901
|
},
|
|
17337
17902
|
async (args) => {
|
|
17338
17903
|
const result = await executeCodebaseContext(runtime.projectRoot, runtime.host, args);
|
|
17904
|
+
return {
|
|
17905
|
+
content: [{ type: "text", text: result.text }],
|
|
17906
|
+
...args.diagnostic ? { structuredContent: result.details } : {}
|
|
17907
|
+
};
|
|
17908
|
+
}
|
|
17909
|
+
);
|
|
17910
|
+
server.tool(
|
|
17911
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17912
|
+
"PRE-EDIT TOOL for a known or suspected symbol. Returns token-bounded target source, direct callers and callees, or a risk-marked conceptual fallback when resolution is unsafe.",
|
|
17913
|
+
{
|
|
17914
|
+
query: import_zod2.z.string().describe("The requested change or target behavior."),
|
|
17915
|
+
symbol: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Authoritative target symbol when known."),
|
|
17916
|
+
filePath: allowNullAsUndefined(import_zod2.z.string().optional()).describe("Optional file path used to disambiguate duplicate symbol names."),
|
|
17917
|
+
callerLimit: allowNullAsUndefined(import_zod2.z.number().int().min(MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).max(MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).optional().default(DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT)),
|
|
17918
|
+
calleeLimit: allowNullAsUndefined(import_zod2.z.number().int().min(MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).max(MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).optional().default(DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT)),
|
|
17919
|
+
tokenBudget: allowNullAsUndefined(import_zod2.z.number().int().min(MIN_CONTEXT_PACK_TOKEN_BUDGET).max(MAX_CONTEXT_PACK_TOKEN_BUDGET).optional().default(DEFAULT_CONTEXT_PACK_TOKEN_BUDGET))
|
|
17920
|
+
},
|
|
17921
|
+
async (args) => {
|
|
17922
|
+
const result = await executeCodebaseEditContext(runtime.projectRoot, runtime.host, args);
|
|
17339
17923
|
return { content: [{ type: "text", text: result.text }] };
|
|
17340
17924
|
}
|
|
17341
17925
|
);
|
|
@@ -19792,9 +20376,9 @@ function parseGitActivity(output) {
|
|
|
19792
20376
|
if (!filePath || addedRaw === "-" || deletedRaw === "-") continue;
|
|
19793
20377
|
const churn = Number(addedRaw) + Number(deletedRaw);
|
|
19794
20378
|
if (!Number.isFinite(churn) || churn <= 0) continue;
|
|
19795
|
-
const
|
|
19796
|
-
const previous = activity.get(
|
|
19797
|
-
activity.set(
|
|
20379
|
+
const normalizedPath3 = normalizePath4(filePath);
|
|
20380
|
+
const previous = activity.get(normalizedPath3);
|
|
20381
|
+
activity.set(normalizedPath3, {
|
|
19798
20382
|
churn: (previous?.churn ?? 0) + churn,
|
|
19799
20383
|
commits: (previous?.commits ?? 0) + 1,
|
|
19800
20384
|
latestDate: previous?.latestDate ?? latestDate,
|
|
@@ -20384,8 +20968,8 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
20384
20968
|
const normalizedAbsoluteSuffix = `/${normalizedDirWithSlash}`;
|
|
20385
20969
|
filteredSymbols = symbols.filter(
|
|
20386
20970
|
(s) => {
|
|
20387
|
-
const
|
|
20388
|
-
return
|
|
20971
|
+
const normalizedPath3 = s.filePath.replace(/\\/g, "/");
|
|
20972
|
+
return normalizedPath3 === normalizedDir || normalizedPath3.startsWith(normalizedDirWithSlash) || normalizedPath3.endsWith(`/${normalizedDir}`) || normalizedPath3.includes(normalizedAbsoluteSuffix);
|
|
20389
20973
|
}
|
|
20390
20974
|
);
|
|
20391
20975
|
}
|