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.js
CHANGED
|
@@ -1277,6 +1277,7 @@ function createSummaryMarkdown(summary, comparison, gate, sweep) {
|
|
|
1277
1277
|
lines.push(`| Hit@10 | ${formatPct(summary.metrics.hitAt10)} |`);
|
|
1278
1278
|
lines.push(`| MRR@10 | ${summary.metrics.mrrAt10.toFixed(4)} |`);
|
|
1279
1279
|
lines.push(`| nDCG@10 | ${summary.metrics.ndcgAt10.toFixed(4)} |`);
|
|
1280
|
+
lines.push(`| Graph-neighbor recall | ${(summary.metrics.graphNeighborRecall ?? 0).toFixed(4)} |`);
|
|
1280
1281
|
lines.push(`| Distinct Top@3 | ${formatPct(summary.metrics.distinctTop3Ratio)} |`);
|
|
1281
1282
|
lines.push(`| Raw Distinct Top@3 | ${formatPct(summary.metrics.rawDistinctTop3Ratio)} |`);
|
|
1282
1283
|
lines.push(`| Latency p50 | ${formatMs(summary.metrics.latencyMs.p50)} |`);
|
|
@@ -6640,12 +6641,12 @@ function diversifyGroupBySymbol(entries, getCandidate) {
|
|
|
6640
6641
|
return [...primary, ...remainder];
|
|
6641
6642
|
}
|
|
6642
6643
|
function buildDiversityKey(metadata) {
|
|
6643
|
-
const
|
|
6644
|
+
const normalizedPath3 = metadata.filePath.toLowerCase();
|
|
6644
6645
|
const normalizedName = (metadata.name ?? "").trim().toLowerCase();
|
|
6645
6646
|
if (normalizedName.length > 0) {
|
|
6646
|
-
return `${
|
|
6647
|
+
return `${normalizedPath3}#${normalizedName}`;
|
|
6647
6648
|
}
|
|
6648
|
-
return
|
|
6649
|
+
return normalizedPath3;
|
|
6649
6650
|
}
|
|
6650
6651
|
function rankHybridResults(query, semanticResults, keywordResults, options) {
|
|
6651
6652
|
const prioritizeSourcePaths = options.prioritizeSourcePaths ?? classifyQueryIntentRaw(query) === "source";
|
|
@@ -7051,6 +7052,29 @@ function extractPrimaryIdentifierQueryHint(query) {
|
|
|
7051
7052
|
const best = codeTerms.find((term) => term.length >= 6);
|
|
7052
7053
|
return best ?? null;
|
|
7053
7054
|
}
|
|
7055
|
+
function pathSegmentsForAffinityMatch(filePath) {
|
|
7056
|
+
const normalizedPath3 = normalizeRankingText(filePath).replace(/\\/g, "/");
|
|
7057
|
+
const segments = normalizedPath3.split("/").filter((segment) => segment.length > 0);
|
|
7058
|
+
if (segments.length === 0) {
|
|
7059
|
+
return [];
|
|
7060
|
+
}
|
|
7061
|
+
const basename8 = segments[segments.length - 1] ?? "";
|
|
7062
|
+
const basenameWithoutExt = basename8.replace(/\.[^/.]+$/u, "");
|
|
7063
|
+
const normalizedSegments = segments.map((segment) => segment.toLowerCase());
|
|
7064
|
+
return Array.from(/* @__PURE__ */ new Set([
|
|
7065
|
+
...normalizedSegments,
|
|
7066
|
+
basenameWithoutExt.toLowerCase()
|
|
7067
|
+
]));
|
|
7068
|
+
}
|
|
7069
|
+
function hasModuleAffinity(filePath, exactIdentifierVariants) {
|
|
7070
|
+
const haystack = pathSegmentsForAffinityMatch(filePath);
|
|
7071
|
+
return exactIdentifierVariants.some((variant) => {
|
|
7072
|
+
if (!variant || variant.length < 2) {
|
|
7073
|
+
return false;
|
|
7074
|
+
}
|
|
7075
|
+
return haystack.includes(variant);
|
|
7076
|
+
});
|
|
7077
|
+
}
|
|
7054
7078
|
var FILE_PATH_HINT_EXTENSIONS = [
|
|
7055
7079
|
"ts",
|
|
7056
7080
|
"tsx",
|
|
@@ -7094,9 +7118,9 @@ function normalizeFilePathForHintMatch(filePath) {
|
|
|
7094
7118
|
return filePath.replace(/\\/g, "/").toLowerCase().replace(/^\.\//, "");
|
|
7095
7119
|
}
|
|
7096
7120
|
function pathMatchesHint(filePath, hint) {
|
|
7097
|
-
const
|
|
7121
|
+
const normalizedPath3 = normalizeFilePathForHintMatch(filePath);
|
|
7098
7122
|
const normalizedHint = normalizeFilePathForHintMatch(hint);
|
|
7099
|
-
return
|
|
7123
|
+
return normalizedPath3.endsWith(normalizedHint) || normalizedPath3.includes(`/${normalizedHint}`) || normalizedPath3.includes(normalizedHint);
|
|
7100
7124
|
}
|
|
7101
7125
|
function extractFilePathHint(query) {
|
|
7102
7126
|
const match = query.match(FILE_PATH_HINT_SUFFIX_REGEX);
|
|
@@ -7126,10 +7150,13 @@ function buildDeterministicIdentifierPass(query, candidates, limit, prioritizeSo
|
|
|
7126
7150
|
).map((candidate) => {
|
|
7127
7151
|
const nameLower = (candidate.metadata.name ?? "").toLowerCase();
|
|
7128
7152
|
const pathLower = candidate.metadata.filePath.toLowerCase();
|
|
7129
|
-
|
|
7130
|
-
const
|
|
7153
|
+
const exactIdentifierVariants = primaryVariants.filter((value) => value.length >= 2);
|
|
7154
|
+
const exactMatch = exactIdentifierVariants.some(
|
|
7131
7155
|
(variant) => nameLower === variant || nameLower.replace(/[^a-z0-9]/g, "") === variant.replace(/[^a-z0-9]/g, "")
|
|
7132
7156
|
);
|
|
7157
|
+
let maxMatch = 0;
|
|
7158
|
+
const nameMatchesPrimary = exactMatch;
|
|
7159
|
+
const pathAffinity = exactMatch ? hasModuleAffinity(candidate.metadata.filePath, exactIdentifierVariants) : false;
|
|
7133
7160
|
const pathMatchesFileHint = filePathHint ? pathMatchesHint(candidate.metadata.filePath, filePathHint) : false;
|
|
7134
7161
|
for (const hint of hints) {
|
|
7135
7162
|
const variants = normalizeIdentifierVariants(hint);
|
|
@@ -7150,12 +7177,17 @@ function buildDeterministicIdentifierPass(query, candidates, limit, prioritizeSo
|
|
|
7150
7177
|
candidate,
|
|
7151
7178
|
maxMatch,
|
|
7152
7179
|
pathMatchesFileHint,
|
|
7153
|
-
nameMatchesPrimary
|
|
7180
|
+
nameMatchesPrimary,
|
|
7181
|
+
pathAffinity
|
|
7154
7182
|
};
|
|
7155
7183
|
}).filter((entry) => entry.maxMatch >= 0.7).sort((a, b) => {
|
|
7156
7184
|
const aAnchored = a.pathMatchesFileHint && a.nameMatchesPrimary ? 1 : 0;
|
|
7157
7185
|
const bAnchored = b.pathMatchesFileHint && b.nameMatchesPrimary ? 1 : 0;
|
|
7158
7186
|
if (aAnchored !== bAnchored) return bAnchored - aAnchored;
|
|
7187
|
+
if (a.nameMatchesPrimary !== b.nameMatchesPrimary) {
|
|
7188
|
+
return b.nameMatchesPrimary ? 1 : -1;
|
|
7189
|
+
}
|
|
7190
|
+
if (a.pathAffinity !== b.pathAffinity) return b.pathAffinity ? 1 : -1;
|
|
7159
7191
|
if (b.maxMatch !== a.maxMatch) return b.maxMatch - a.maxMatch;
|
|
7160
7192
|
if (b.candidate.score !== a.candidate.score) return b.candidate.score - a.candidate.score;
|
|
7161
7193
|
return a.candidate.id.localeCompare(b.candidate.id);
|
|
@@ -11370,6 +11402,20 @@ var Indexer = class _Indexer {
|
|
|
11370
11402
|
requestedLimit = nextLimit;
|
|
11371
11403
|
}
|
|
11372
11404
|
}
|
|
11405
|
+
buildCandidateSnapshot(candidate) {
|
|
11406
|
+
return {
|
|
11407
|
+
id: candidate.id,
|
|
11408
|
+
filePath: candidate.metadata.filePath,
|
|
11409
|
+
startLine: candidate.metadata.startLine,
|
|
11410
|
+
endLine: candidate.metadata.endLine,
|
|
11411
|
+
score: candidate.score,
|
|
11412
|
+
chunkType: candidate.metadata.chunkType,
|
|
11413
|
+
name: candidate.metadata.name
|
|
11414
|
+
};
|
|
11415
|
+
}
|
|
11416
|
+
buildCandidateSnapshotList(candidates) {
|
|
11417
|
+
return candidates.map((candidate) => this.buildCandidateSnapshot(candidate));
|
|
11418
|
+
}
|
|
11373
11419
|
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch) {
|
|
11374
11420
|
return this.searchCandidatesWithBranchPrefilter(
|
|
11375
11421
|
initialLimit,
|
|
@@ -11561,6 +11607,16 @@ var Indexer = class _Indexer {
|
|
|
11561
11607
|
prefilterMs: Math.round(prefilterMs * 100) / 100,
|
|
11562
11608
|
fusionMs: Math.round(fusionMs * 100) / 100
|
|
11563
11609
|
});
|
|
11610
|
+
if (options?.trace) {
|
|
11611
|
+
options.trace({
|
|
11612
|
+
semanticCandidates: this.buildCandidateSnapshotList(scopedSemanticCandidates),
|
|
11613
|
+
keywordCandidates: this.buildCandidateSnapshotList(scopedKeywordCandidates),
|
|
11614
|
+
hybridCandidates: this.buildCandidateSnapshotList(combined),
|
|
11615
|
+
postExternalRerankCandidates: this.buildCandidateSnapshotList(rerankedCombined),
|
|
11616
|
+
tieredCandidates: this.buildCandidateSnapshotList(tiered),
|
|
11617
|
+
finalCandidates: this.buildCandidateSnapshotList(finalResults)
|
|
11618
|
+
});
|
|
11619
|
+
}
|
|
11564
11620
|
const metadataOnly = options?.metadataOnly ?? false;
|
|
11565
11621
|
return Promise.all(
|
|
11566
11622
|
finalResults.map(async (r) => {
|
|
@@ -12609,6 +12665,42 @@ var Indexer = class _Indexer {
|
|
|
12609
12665
|
}
|
|
12610
12666
|
};
|
|
12611
12667
|
|
|
12668
|
+
// src/tools/contracts.ts
|
|
12669
|
+
var CHUNK_TYPES = [
|
|
12670
|
+
"function",
|
|
12671
|
+
"class",
|
|
12672
|
+
"method",
|
|
12673
|
+
"interface",
|
|
12674
|
+
"type",
|
|
12675
|
+
"enum",
|
|
12676
|
+
"struct",
|
|
12677
|
+
"impl",
|
|
12678
|
+
"trait",
|
|
12679
|
+
"module",
|
|
12680
|
+
"other"
|
|
12681
|
+
];
|
|
12682
|
+
var CALL_GRAPH_DIRECTIONS = ["callers", "callees"];
|
|
12683
|
+
var RELATIONSHIP_TYPES = [
|
|
12684
|
+
"Call",
|
|
12685
|
+
"MethodCall",
|
|
12686
|
+
"Constructor",
|
|
12687
|
+
"Import",
|
|
12688
|
+
"Inherits",
|
|
12689
|
+
"Implements"
|
|
12690
|
+
];
|
|
12691
|
+
var INDEX_LOG_CATEGORIES = ["search", "embedding", "cache", "gc", "branch", "general"];
|
|
12692
|
+
var INDEX_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
12693
|
+
var MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT = 1;
|
|
12694
|
+
var MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT = 20;
|
|
12695
|
+
var DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT = 5;
|
|
12696
|
+
var CODE_COMMUNITIES_MIN_SIZE = 1;
|
|
12697
|
+
var CODE_COMMUNITIES_DEFAULT_LIMIT = 20;
|
|
12698
|
+
var CODE_COMMUNITIES_MAX_LIMIT = 100;
|
|
12699
|
+
var CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD = 5;
|
|
12700
|
+
var CODE_COMMUNITIES_MIN_COUPLING = 1;
|
|
12701
|
+
var CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT = 20;
|
|
12702
|
+
var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
|
|
12703
|
+
|
|
12612
12704
|
// src/tools/operations.ts
|
|
12613
12705
|
import { existsSync as existsSync12, realpathSync as realpathSync5, statSync as statSync5 } from "fs";
|
|
12614
12706
|
import * as path21 from "path";
|
|
@@ -12737,39 +12829,6 @@ function formatCodeCommunities(result) {
|
|
|
12737
12829
|
return lines.join("\n");
|
|
12738
12830
|
}
|
|
12739
12831
|
|
|
12740
|
-
// src/tools/contracts.ts
|
|
12741
|
-
var CHUNK_TYPES = [
|
|
12742
|
-
"function",
|
|
12743
|
-
"class",
|
|
12744
|
-
"method",
|
|
12745
|
-
"interface",
|
|
12746
|
-
"type",
|
|
12747
|
-
"enum",
|
|
12748
|
-
"struct",
|
|
12749
|
-
"impl",
|
|
12750
|
-
"trait",
|
|
12751
|
-
"module",
|
|
12752
|
-
"other"
|
|
12753
|
-
];
|
|
12754
|
-
var CALL_GRAPH_DIRECTIONS = ["callers", "callees"];
|
|
12755
|
-
var RELATIONSHIP_TYPES = [
|
|
12756
|
-
"Call",
|
|
12757
|
-
"MethodCall",
|
|
12758
|
-
"Constructor",
|
|
12759
|
-
"Import",
|
|
12760
|
-
"Inherits",
|
|
12761
|
-
"Implements"
|
|
12762
|
-
];
|
|
12763
|
-
var INDEX_LOG_CATEGORIES = ["search", "embedding", "cache", "gc", "branch", "general"];
|
|
12764
|
-
var INDEX_LOG_LEVELS = ["error", "warn", "info", "debug"];
|
|
12765
|
-
var CODE_COMMUNITIES_MIN_SIZE = 1;
|
|
12766
|
-
var CODE_COMMUNITIES_DEFAULT_LIMIT = 20;
|
|
12767
|
-
var CODE_COMMUNITIES_MAX_LIMIT = 100;
|
|
12768
|
-
var CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD = 5;
|
|
12769
|
-
var CODE_COMMUNITIES_MIN_COUPLING = 1;
|
|
12770
|
-
var CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT = 20;
|
|
12771
|
-
var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
|
|
12772
|
-
|
|
12773
12832
|
// src/tools/context-pack.ts
|
|
12774
12833
|
import { get_encoding } from "tiktoken";
|
|
12775
12834
|
var MIN_CONTEXT_PACK_TOKEN_BUDGET = 128;
|
|
@@ -12871,6 +12930,16 @@ function compactEvidenceValue(value, maxChars) {
|
|
|
12871
12930
|
}
|
|
12872
12931
|
var MAX_EXACT_SEARCH_HANDOFF_NAMES = 3;
|
|
12873
12932
|
var MAX_EXACT_SEARCH_HANDOFF_NAME_CHARS = 64;
|
|
12933
|
+
function toContextPackTraceCandidate(result) {
|
|
12934
|
+
return {
|
|
12935
|
+
filePath: result.filePath,
|
|
12936
|
+
startLine: result.startLine,
|
|
12937
|
+
endLine: result.endLine,
|
|
12938
|
+
score: result.score,
|
|
12939
|
+
chunkType: result.chunkType,
|
|
12940
|
+
name: result.name
|
|
12941
|
+
};
|
|
12942
|
+
}
|
|
12874
12943
|
function formatExactSearchHandoff(results) {
|
|
12875
12944
|
const suggestedNames = [];
|
|
12876
12945
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -12919,13 +12988,13 @@ function buildContextPack(results, options = {}) {
|
|
|
12919
12988
|
const maxResults = Math.max(0, Math.floor(options.maxResults ?? results.length));
|
|
12920
12989
|
const includeExactSearchHandoff = options.includeExactSearchHandoff ?? false;
|
|
12921
12990
|
const candidateCount = results.length;
|
|
12922
|
-
const
|
|
12923
|
-
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
12927
|
-
);
|
|
12928
|
-
const
|
|
12991
|
+
const preserveInputOrder = options.preserveInputOrder ?? false;
|
|
12992
|
+
const ranked = preserveInputOrder ? results.map((result, originalIndex) => ({ result, originalIndex })) : rankContextCandidates(results, options.preferImplementationPaths ?? false);
|
|
12993
|
+
const rankedCandidates = ranked.map((entry) => toContextPackTraceCandidate(entry.result));
|
|
12994
|
+
const deduplicated = deduplicateContextCandidates(ranked);
|
|
12995
|
+
const deduplicatedCandidates = deduplicated.map((result) => toContextPackTraceCandidate(result));
|
|
12996
|
+
const diversified = preserveInputOrder ? deduplicated : diversifyContextCandidates(deduplicated);
|
|
12997
|
+
const diversifiedCandidates = diversified.map((result) => toContextPackTraceCandidate(result));
|
|
12929
12998
|
const duplicateCount = candidateCount - deduplicated.length;
|
|
12930
12999
|
const selectable = diversified.slice(0, maxResults);
|
|
12931
13000
|
const limitOmittedCount = deduplicated.length - selectable.length;
|
|
@@ -12958,6 +13027,15 @@ function buildContextPack(results, options = {}) {
|
|
|
12958
13027
|
const fitted = fitTextToContextBudget(text, tokenBudget);
|
|
12959
13028
|
const budgetOmittedCount = selectable.length - selected.length;
|
|
12960
13029
|
const omittedCount = candidateCount - selected.length;
|
|
13030
|
+
if (options.trace) {
|
|
13031
|
+
options.trace({
|
|
13032
|
+
inputCandidates: results.map(toContextPackTraceCandidate),
|
|
13033
|
+
rankedCandidates,
|
|
13034
|
+
deduplicatedCandidates,
|
|
13035
|
+
diversifiedCandidates,
|
|
13036
|
+
selectedCandidates: selected.map(toContextPackTraceCandidate)
|
|
13037
|
+
});
|
|
13038
|
+
}
|
|
12961
13039
|
return {
|
|
12962
13040
|
requestedTokenBudget,
|
|
12963
13041
|
tokenBudget,
|
|
@@ -14705,7 +14783,8 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
|
14705
14783
|
definitionIntent: options.definitionIntent,
|
|
14706
14784
|
blameAuthor: options.blameAuthor,
|
|
14707
14785
|
blameSha: options.blameSha,
|
|
14708
|
-
blameSince: options.blameSince
|
|
14786
|
+
blameSince: options.blameSince,
|
|
14787
|
+
trace: options.trace
|
|
14709
14788
|
});
|
|
14710
14789
|
}
|
|
14711
14790
|
async function searchCodebaseWithEffectiveness(projectRoot, host, route, query, options, render) {
|
|
@@ -14759,15 +14838,19 @@ async function implementationLookup(projectRoot, host, query, options = {}) {
|
|
|
14759
14838
|
return indexer.search(query, options.limit, {
|
|
14760
14839
|
fileType: options.fileType,
|
|
14761
14840
|
directory: options.directory,
|
|
14762
|
-
definitionIntent: true
|
|
14841
|
+
definitionIntent: true,
|
|
14842
|
+
trace: options.trace
|
|
14763
14843
|
});
|
|
14764
14844
|
}
|
|
14765
14845
|
async function getCallGraphData(projectRoot, host, params) {
|
|
14766
14846
|
await ensureAutoIndexReadyForRetrieval(projectRoot, host);
|
|
14767
14847
|
const root = getProjectRoot(projectRoot, host);
|
|
14768
14848
|
const indexer = getIndexerForProject(root, host);
|
|
14849
|
+
return getCallGraphDataForIndexer(indexer, root, params);
|
|
14850
|
+
}
|
|
14851
|
+
async function getCallGraphDataForIndexer(indexer, projectRoot, params) {
|
|
14769
14852
|
const symbols = await indexer.getCallGraphSymbols();
|
|
14770
|
-
const resolution = resolveCallGraphSymbol(symbols,
|
|
14853
|
+
const resolution = resolveCallGraphSymbol(symbols, projectRoot, params.name, params.filePath, params.symbolId);
|
|
14771
14854
|
const direction = params.direction === "callees" ? "callees" : "callers";
|
|
14772
14855
|
if (resolution.status !== "resolved") {
|
|
14773
14856
|
return { direction, resolution, callers: [], callees: [], relationshipType: params.relationshipType };
|
|
@@ -15095,6 +15178,27 @@ function buildRecoveryDetails(attempts, successIndex) {
|
|
|
15095
15178
|
successfulAttemptIndex: successIndex
|
|
15096
15179
|
};
|
|
15097
15180
|
}
|
|
15181
|
+
function serializeAttempts(attempts) {
|
|
15182
|
+
return attempts.map((attempt) => ({
|
|
15183
|
+
kind: attempt.kind,
|
|
15184
|
+
scope: attempt.scope,
|
|
15185
|
+
resultCount: attempt.resultCount,
|
|
15186
|
+
relaxedFields: attempt.relaxedFields
|
|
15187
|
+
}));
|
|
15188
|
+
}
|
|
15189
|
+
function buildSearchDiagnostic(attempt) {
|
|
15190
|
+
if (!attempt) {
|
|
15191
|
+
return void 0;
|
|
15192
|
+
}
|
|
15193
|
+
return {
|
|
15194
|
+
route: attempt.kind,
|
|
15195
|
+
routedQuery: attempt.query,
|
|
15196
|
+
searchQuery: attempt.query,
|
|
15197
|
+
searchScope: attempt.scopeFilter,
|
|
15198
|
+
searchTrace: attempt.searchTrace,
|
|
15199
|
+
contextPackTrace: attempt.contextPackTrace
|
|
15200
|
+
};
|
|
15201
|
+
}
|
|
15098
15202
|
function trimOrUndefined2(value) {
|
|
15099
15203
|
const normalized = value?.trim();
|
|
15100
15204
|
if (!normalized) {
|
|
@@ -15134,6 +15238,7 @@ async function resolveSearchContext(input, operations) {
|
|
|
15134
15238
|
const hasFilters = Boolean(fileType || directory);
|
|
15135
15239
|
const relaxedFields = relaxedHintFields(fileType, directory);
|
|
15136
15240
|
const attempts = [];
|
|
15241
|
+
const attemptStates = [];
|
|
15137
15242
|
const decisions = {
|
|
15138
15243
|
inferredDefinitionMiss: false,
|
|
15139
15244
|
fallbackFromOriginalConceptualToInferred: false,
|
|
@@ -15162,8 +15267,20 @@ async function resolveSearchContext(input, operations) {
|
|
|
15162
15267
|
if (seenAttempts.has(key)) {
|
|
15163
15268
|
return [];
|
|
15164
15269
|
}
|
|
15165
|
-
const
|
|
15270
|
+
const attemptState = {
|
|
15271
|
+
kind,
|
|
15272
|
+
scope: describeScope(scope.fileType, scope.directory),
|
|
15273
|
+
resultCount: 0,
|
|
15274
|
+
relaxedFields: [...relaxedFieldsForAttempt],
|
|
15275
|
+
query: attemptQuery,
|
|
15276
|
+
scopeFilter: scope
|
|
15277
|
+
};
|
|
15278
|
+
const results = await runAttempt((trace) => {
|
|
15279
|
+
attemptState.searchTrace = trace;
|
|
15280
|
+
});
|
|
15281
|
+
attemptState.resultCount = results.length;
|
|
15166
15282
|
seenAttempts.add(key);
|
|
15283
|
+
attemptStates.push(attemptState);
|
|
15167
15284
|
attempts.push({
|
|
15168
15285
|
kind,
|
|
15169
15286
|
scope: describeScope(scope.fileType, scope.directory),
|
|
@@ -15183,7 +15300,7 @@ async function resolveSearchContext(input, operations) {
|
|
|
15183
15300
|
symbol,
|
|
15184
15301
|
scope,
|
|
15185
15302
|
relaxedFieldsForAttempt,
|
|
15186
|
-
() => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope)
|
|
15303
|
+
(trace) => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
15187
15304
|
);
|
|
15188
15305
|
};
|
|
15189
15306
|
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt) => {
|
|
@@ -15192,13 +15309,23 @@ async function resolveSearchContext(input, operations) {
|
|
|
15192
15309
|
searchQuery,
|
|
15193
15310
|
scope,
|
|
15194
15311
|
relaxedFieldsForAttempt,
|
|
15195
|
-
() => operations.search(searchQuery, MAX_CONTEXT_RESULT_LIMIT, scope)
|
|
15312
|
+
(trace) => operations.search(searchQuery, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
15196
15313
|
);
|
|
15197
15314
|
};
|
|
15198
|
-
const
|
|
15315
|
+
const findSuccessfulAttemptState = (route) => {
|
|
15316
|
+
for (let index = attemptStates.length - 1; index >= 0; index -= 1) {
|
|
15317
|
+
const attempt = attemptStates[index];
|
|
15318
|
+
if (attempt.kind === route && attempt.resultCount > 0) {
|
|
15319
|
+
return attempt;
|
|
15320
|
+
}
|
|
15321
|
+
}
|
|
15322
|
+
return void 0;
|
|
15323
|
+
};
|
|
15324
|
+
const toResult = (route, routedQuery, pack, successfulAttempt) => {
|
|
15199
15325
|
const base = packedResult(route, routedQuery, pack);
|
|
15200
15326
|
const baseDetails = base.details;
|
|
15201
15327
|
const successIndex = findSuccessfulAttemptIndex(route, attempts);
|
|
15328
|
+
const successState = successfulAttempt ?? findSuccessfulAttemptState(route);
|
|
15202
15329
|
return {
|
|
15203
15330
|
text: base.text,
|
|
15204
15331
|
details: {
|
|
@@ -15206,7 +15333,10 @@ async function resolveSearchContext(input, operations) {
|
|
|
15206
15333
|
tokenBudget: baseDetails.tokenBudget,
|
|
15207
15334
|
tokenEstimate: baseDetails.tokenEstimate,
|
|
15208
15335
|
truncated: false,
|
|
15209
|
-
recovery: buildRecoveryDetails(
|
|
15336
|
+
recovery: buildRecoveryDetails(serializeAttempts(attemptStates), successIndex),
|
|
15337
|
+
...input.diagnostic && {
|
|
15338
|
+
diagnostic: buildSearchDiagnostic(successState)
|
|
15339
|
+
}
|
|
15210
15340
|
}
|
|
15211
15341
|
};
|
|
15212
15342
|
};
|
|
@@ -15220,8 +15350,18 @@ async function resolveSearchContext(input, operations) {
|
|
|
15220
15350
|
buildContextPack(scopedDefinitionResults, {
|
|
15221
15351
|
tokenBudget,
|
|
15222
15352
|
maxResults: limit,
|
|
15223
|
-
heading
|
|
15224
|
-
|
|
15353
|
+
heading,
|
|
15354
|
+
preserveInputOrder: true,
|
|
15355
|
+
...input.diagnostic ? {
|
|
15356
|
+
trace: (trace) => {
|
|
15357
|
+
const attemptState = findSuccessfulAttemptState("definition");
|
|
15358
|
+
if (attemptState) {
|
|
15359
|
+
attemptState.contextPackTrace = trace;
|
|
15360
|
+
}
|
|
15361
|
+
}
|
|
15362
|
+
} : void 0
|
|
15363
|
+
}),
|
|
15364
|
+
findSuccessfulAttemptState("definition")
|
|
15225
15365
|
);
|
|
15226
15366
|
}
|
|
15227
15367
|
if (explicitSymbol) {
|
|
@@ -15239,8 +15379,18 @@ async function resolveSearchContext(input, operations) {
|
|
|
15239
15379
|
buildContextPack(unscopedDefinitionResults, {
|
|
15240
15380
|
tokenBudget,
|
|
15241
15381
|
maxResults: limit,
|
|
15242
|
-
heading: heading2
|
|
15243
|
-
|
|
15382
|
+
heading: heading2,
|
|
15383
|
+
preserveInputOrder: true,
|
|
15384
|
+
...input.diagnostic ? {
|
|
15385
|
+
trace: (trace) => {
|
|
15386
|
+
const attemptState = findSuccessfulAttemptState("definition");
|
|
15387
|
+
if (attemptState) {
|
|
15388
|
+
attemptState.contextPackTrace = trace;
|
|
15389
|
+
}
|
|
15390
|
+
}
|
|
15391
|
+
} : void 0
|
|
15392
|
+
}),
|
|
15393
|
+
findSuccessfulAttemptState("definition")
|
|
15244
15394
|
);
|
|
15245
15395
|
}
|
|
15246
15396
|
}
|
|
@@ -15259,7 +15409,10 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15259
15409
|
tokenBudget: heading.tokenBudget,
|
|
15260
15410
|
tokenEstimate: heading.tokenEstimate,
|
|
15261
15411
|
truncated: heading.truncated,
|
|
15262
|
-
recovery: buildRecoveryDetails(
|
|
15412
|
+
recovery: buildRecoveryDetails(serializeAttempts(attemptStates), null),
|
|
15413
|
+
...input.diagnostic && {
|
|
15414
|
+
diagnostic: buildSearchDiagnostic(attemptStates[attemptStates.length - 1])
|
|
15415
|
+
}
|
|
15263
15416
|
}
|
|
15264
15417
|
};
|
|
15265
15418
|
}
|
|
@@ -15299,8 +15452,17 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15299
15452
|
maxResults: limit,
|
|
15300
15453
|
heading,
|
|
15301
15454
|
includeExactSearchHandoff: true,
|
|
15302
|
-
preferImplementationPaths: intent.preferSourcePaths || intent.primary !== "docs" && intent.primary !== "test"
|
|
15303
|
-
|
|
15455
|
+
preferImplementationPaths: intent.preferSourcePaths || intent.primary !== "docs" && intent.primary !== "test",
|
|
15456
|
+
...input.diagnostic ? {
|
|
15457
|
+
trace: (trace) => {
|
|
15458
|
+
const attemptState = findSuccessfulAttemptState("conceptual");
|
|
15459
|
+
if (attemptState) {
|
|
15460
|
+
attemptState.contextPackTrace = trace;
|
|
15461
|
+
}
|
|
15462
|
+
}
|
|
15463
|
+
} : void 0
|
|
15464
|
+
}),
|
|
15465
|
+
findSuccessfulAttemptState("conceptual")
|
|
15304
15466
|
);
|
|
15305
15467
|
}
|
|
15306
15468
|
}
|
|
@@ -15316,7 +15478,10 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
15316
15478
|
tokenBudget: fallbackText.tokenBudget,
|
|
15317
15479
|
tokenEstimate: fallbackText.tokenEstimate,
|
|
15318
15480
|
truncated: fallbackText.truncated,
|
|
15319
|
-
recovery: buildRecoveryDetails(
|
|
15481
|
+
recovery: buildRecoveryDetails(serializeAttempts(attemptStates), null),
|
|
15482
|
+
...input.diagnostic && {
|
|
15483
|
+
diagnostic: buildSearchDiagnostic(attemptStates[attemptStates.length - 1])
|
|
15484
|
+
}
|
|
15320
15485
|
}
|
|
15321
15486
|
};
|
|
15322
15487
|
}
|
|
@@ -15397,17 +15562,27 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
15397
15562
|
details: fittedDetails("path", fitted, 0)
|
|
15398
15563
|
};
|
|
15399
15564
|
}
|
|
15400
|
-
return resolveSearchContext({
|
|
15401
|
-
|
|
15565
|
+
return resolveSearchContext({
|
|
15566
|
+
query: input.query,
|
|
15567
|
+
symbol,
|
|
15568
|
+
limit,
|
|
15569
|
+
tokenBudget,
|
|
15570
|
+
fileType,
|
|
15571
|
+
directory,
|
|
15572
|
+
diagnostic: input.diagnostic
|
|
15573
|
+
}, {
|
|
15574
|
+
lookup: (lookupSymbol, retrievalLimit, scope, trace) => implementationLookup(projectRoot, host, lookupSymbol, {
|
|
15402
15575
|
limit: retrievalLimit,
|
|
15403
15576
|
fileType: scope.fileType,
|
|
15404
|
-
directory: scope.directory
|
|
15577
|
+
directory: scope.directory,
|
|
15578
|
+
trace
|
|
15405
15579
|
}),
|
|
15406
|
-
search: (queryText, retrievalLimit, scope) => searchCodebase(projectRoot, host, queryText, {
|
|
15580
|
+
search: (queryText, retrievalLimit, scope, trace) => searchCodebase(projectRoot, host, queryText, {
|
|
15407
15581
|
limit: retrievalLimit,
|
|
15408
15582
|
fileType: scope.fileType,
|
|
15409
15583
|
directory: scope.directory,
|
|
15410
|
-
metadataOnly: true
|
|
15584
|
+
metadataOnly: true,
|
|
15585
|
+
trace
|
|
15411
15586
|
})
|
|
15412
15587
|
});
|
|
15413
15588
|
}
|
|
@@ -15472,6 +15647,181 @@ async function resolveCodebaseContext(projectRoot, host, input) {
|
|
|
15472
15647
|
}
|
|
15473
15648
|
}
|
|
15474
15649
|
|
|
15650
|
+
// src/tools/edit-context.ts
|
|
15651
|
+
function edgeLimit(value) {
|
|
15652
|
+
if (value === null || value === void 0 || !Number.isFinite(value)) {
|
|
15653
|
+
return DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT;
|
|
15654
|
+
}
|
|
15655
|
+
return Math.min(
|
|
15656
|
+
MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT,
|
|
15657
|
+
Math.max(MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT, Math.floor(value))
|
|
15658
|
+
);
|
|
15659
|
+
}
|
|
15660
|
+
function normalizedPath(value) {
|
|
15661
|
+
return value.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
15662
|
+
}
|
|
15663
|
+
function pathsMatch(left, right) {
|
|
15664
|
+
const normalizedLeft = normalizedPath(left);
|
|
15665
|
+
const normalizedRight = normalizedPath(right);
|
|
15666
|
+
return normalizedLeft === normalizedRight || normalizedLeft.endsWith(`/${normalizedRight}`) || normalizedRight.endsWith(`/${normalizedLeft}`);
|
|
15667
|
+
}
|
|
15668
|
+
function targetSource(results, resolution) {
|
|
15669
|
+
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);
|
|
15670
|
+
}
|
|
15671
|
+
function formatSource(result) {
|
|
15672
|
+
const name = result.name ? ` ${result.name}` : "";
|
|
15673
|
+
return [
|
|
15674
|
+
"## Target implementation",
|
|
15675
|
+
`${result.filePath}:${result.startLine}-${result.endLine} (${result.chunkType}${name})`,
|
|
15676
|
+
"```",
|
|
15677
|
+
result.content,
|
|
15678
|
+
"```"
|
|
15679
|
+
].join("\n");
|
|
15680
|
+
}
|
|
15681
|
+
function formatCallers(edges) {
|
|
15682
|
+
if (edges.length === 0) return "## Direct callers\nNone found.";
|
|
15683
|
+
return [
|
|
15684
|
+
"## Direct callers",
|
|
15685
|
+
...edges.map((edge) => `- ${edge.fromSymbolName ?? "<unknown>"} at ${edge.fromSymbolFilePath ?? "<unknown file>"}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`)
|
|
15686
|
+
].join("\n");
|
|
15687
|
+
}
|
|
15688
|
+
function formatCallees(edges, sourceFilePath) {
|
|
15689
|
+
if (edges.length === 0) return "## Direct callees\nNone found.";
|
|
15690
|
+
return [
|
|
15691
|
+
"## Direct callees",
|
|
15692
|
+
...edges.map((edge) => `- ${edge.targetName} from ${sourceFilePath}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`)
|
|
15693
|
+
].join("\n");
|
|
15694
|
+
}
|
|
15695
|
+
function formatResolutionRisk(resolution) {
|
|
15696
|
+
if (resolution.status === "ambiguous") {
|
|
15697
|
+
const candidates = resolution.candidates.map((candidate) => `${candidate.filePath}:${candidate.startLine}`).join(", ");
|
|
15698
|
+
return `Risk: symbol "${resolution.name}" is ambiguous. Pass filePath to select a target.${candidates ? ` Candidates: ${candidates}.` : ""}`;
|
|
15699
|
+
}
|
|
15700
|
+
if (resolution.filePath && resolution.totalCandidates > 0) {
|
|
15701
|
+
return `Risk: symbol "${resolution.name}" did not resolve at filePath="${resolution.filePath}". Review the conceptual evidence before editing.`;
|
|
15702
|
+
}
|
|
15703
|
+
return `Risk: symbol "${resolution.name}" could not be resolved. Review the conceptual evidence before editing.`;
|
|
15704
|
+
}
|
|
15705
|
+
async function fallbackPack(dependencies, query, risk, resolution, tokenBudget, candidateSource = []) {
|
|
15706
|
+
const conceptual = await dependencies.searchCodebase(query, { limit: 5 });
|
|
15707
|
+
const pack = buildContextPack([...candidateSource, ...conceptual], {
|
|
15708
|
+
tokenBudget: tokenBudget ?? void 0,
|
|
15709
|
+
heading: "## Conceptual evidence",
|
|
15710
|
+
maxResults: 5,
|
|
15711
|
+
includeExactSearchHandoff: false,
|
|
15712
|
+
preferImplementationPaths: true
|
|
15713
|
+
});
|
|
15714
|
+
const fitted = fitTextToContextBudget(`${risk}
|
|
15715
|
+
|
|
15716
|
+
${pack.text}`, tokenBudget ?? void 0);
|
|
15717
|
+
return {
|
|
15718
|
+
text: fitted.text,
|
|
15719
|
+
details: {
|
|
15720
|
+
resolution,
|
|
15721
|
+
tokenBudget: fitted.tokenBudget,
|
|
15722
|
+
tokenEstimate: fitted.tokenEstimate,
|
|
15723
|
+
truncated: fitted.truncated,
|
|
15724
|
+
sourceIncluded: candidateSource.length > 0,
|
|
15725
|
+
callerCount: 0,
|
|
15726
|
+
calleeCount: 0
|
|
15727
|
+
}
|
|
15728
|
+
};
|
|
15729
|
+
}
|
|
15730
|
+
async function resolveCodebaseEditContextWithDependencies(input, dependencies) {
|
|
15731
|
+
const symbol = input.symbol?.trim();
|
|
15732
|
+
if (!symbol) {
|
|
15733
|
+
return fallbackPack(
|
|
15734
|
+
dependencies,
|
|
15735
|
+
input.query,
|
|
15736
|
+
"Risk: no authoritative symbol was supplied. Review the conceptual evidence before editing.",
|
|
15737
|
+
"not_requested",
|
|
15738
|
+
input.tokenBudget
|
|
15739
|
+
);
|
|
15740
|
+
}
|
|
15741
|
+
let callersResult;
|
|
15742
|
+
try {
|
|
15743
|
+
callersResult = await dependencies.getCallGraphData({
|
|
15744
|
+
name: symbol,
|
|
15745
|
+
filePath: input.filePath ?? void 0,
|
|
15746
|
+
direction: "callers"
|
|
15747
|
+
});
|
|
15748
|
+
} catch (error) {
|
|
15749
|
+
const candidates = await dependencies.implementationLookup(symbol, { limit: 5 });
|
|
15750
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
15751
|
+
return fallbackPack(
|
|
15752
|
+
dependencies,
|
|
15753
|
+
input.query,
|
|
15754
|
+
`Risk: graph data is unavailable (${message}). Target and dependencies are not graph-verified.`,
|
|
15755
|
+
"graph_unavailable",
|
|
15756
|
+
input.tokenBudget,
|
|
15757
|
+
candidates
|
|
15758
|
+
);
|
|
15759
|
+
}
|
|
15760
|
+
if (callersResult.resolution.status !== "resolved") {
|
|
15761
|
+
return fallbackPack(
|
|
15762
|
+
dependencies,
|
|
15763
|
+
input.query,
|
|
15764
|
+
formatResolutionRisk(callersResult.resolution),
|
|
15765
|
+
callersResult.resolution.status,
|
|
15766
|
+
input.tokenBudget
|
|
15767
|
+
);
|
|
15768
|
+
}
|
|
15769
|
+
const resolution = callersResult.resolution;
|
|
15770
|
+
const [definitionsResult, calleesResult] = await Promise.allSettled([
|
|
15771
|
+
dependencies.implementationLookup(symbol, { limit: 10 }),
|
|
15772
|
+
dependencies.getCallGraphData({
|
|
15773
|
+
name: symbol,
|
|
15774
|
+
filePath: input.filePath ?? resolution.filePath,
|
|
15775
|
+
direction: "callees"
|
|
15776
|
+
})
|
|
15777
|
+
]);
|
|
15778
|
+
if (definitionsResult.status === "rejected") throw definitionsResult.reason;
|
|
15779
|
+
let graphRisk;
|
|
15780
|
+
let callees = [];
|
|
15781
|
+
if (calleesResult.status === "rejected") {
|
|
15782
|
+
const message = calleesResult.reason instanceof Error ? calleesResult.reason.message : String(calleesResult.reason);
|
|
15783
|
+
graphRisk = `Risk: callee graph data is unavailable (${message}). Dependency evidence is incomplete.`;
|
|
15784
|
+
} else if (calleesResult.value.resolution.status !== "resolved") {
|
|
15785
|
+
graphRisk = "Risk: the target resolved for callers but not callees. Dependency evidence is incomplete.";
|
|
15786
|
+
} else {
|
|
15787
|
+
callees = calleesResult.value.callees.slice(0, edgeLimit(input.calleeLimit));
|
|
15788
|
+
}
|
|
15789
|
+
const source = targetSource(definitionsResult.value, resolution);
|
|
15790
|
+
const callers = callersResult.callers.slice(0, edgeLimit(input.callerLimit));
|
|
15791
|
+
const sourceBudget = Math.max(
|
|
15792
|
+
MIN_CONTEXT_PACK_TOKEN_BUDGET,
|
|
15793
|
+
Math.floor((input.tokenBudget ?? DEFAULT_CONTEXT_PACK_TOKEN_BUDGET) * 0.6)
|
|
15794
|
+
);
|
|
15795
|
+
const sourceText = source ? fitTextToContextBudget(formatSource(source), sourceBudget).text : `## Target implementation
|
|
15796
|
+
Risk: no implementation source matched the resolved target at ${resolution.filePath}:${resolution.startLine}.`;
|
|
15797
|
+
const fitted = fitTextToContextBudget([
|
|
15798
|
+
`# Pre-edit context for ${resolution.name}`,
|
|
15799
|
+
graphRisk,
|
|
15800
|
+
sourceText,
|
|
15801
|
+
formatCallers(callers),
|
|
15802
|
+
formatCallees(callees, resolution.filePath)
|
|
15803
|
+
].filter((section) => section !== void 0).join("\n\n"), input.tokenBudget ?? void 0);
|
|
15804
|
+
return {
|
|
15805
|
+
text: fitted.text,
|
|
15806
|
+
details: {
|
|
15807
|
+
resolution: "resolved",
|
|
15808
|
+
tokenBudget: fitted.tokenBudget,
|
|
15809
|
+
tokenEstimate: fitted.tokenEstimate,
|
|
15810
|
+
truncated: fitted.truncated,
|
|
15811
|
+
sourceIncluded: source !== void 0,
|
|
15812
|
+
callerCount: callers.length,
|
|
15813
|
+
calleeCount: callees.length
|
|
15814
|
+
}
|
|
15815
|
+
};
|
|
15816
|
+
}
|
|
15817
|
+
async function resolveCodebaseEditContext(projectRoot, host, input) {
|
|
15818
|
+
return resolveCodebaseEditContextWithDependencies(input, {
|
|
15819
|
+
searchCodebase: (query, options) => searchCodebase(projectRoot, host, query, options),
|
|
15820
|
+
implementationLookup: (query, options) => implementationLookup(projectRoot, host, query, options),
|
|
15821
|
+
getCallGraphData: (params) => getCallGraphData(projectRoot, host, params)
|
|
15822
|
+
});
|
|
15823
|
+
}
|
|
15824
|
+
|
|
15475
15825
|
// src/eval/budget.ts
|
|
15476
15826
|
function evaluateBudgetGate(budget, summary, comparison) {
|
|
15477
15827
|
const BASELINE_P95_EPSILON_MS = 1e-3;
|
|
@@ -15495,6 +15845,24 @@ function evaluateBudgetGate(budget, summary, comparison) {
|
|
|
15495
15845
|
message: `Raw Distinct Top@3 ${summary.metrics.rawDistinctTop3Ratio.toFixed(4)} is below minimum ${thresholds.minRawDistinctTop3Ratio.toFixed(4)}`
|
|
15496
15846
|
});
|
|
15497
15847
|
}
|
|
15848
|
+
if (thresholds.minGraphNeighborRecall !== void 0 && summary.metrics.graphNeighborRecall !== void 0 && summary.metrics.graphNeighborRecall < thresholds.minGraphNeighborRecall) {
|
|
15849
|
+
violations.push({
|
|
15850
|
+
metric: "minGraphNeighborRecall",
|
|
15851
|
+
message: `Graph-neighbor recall ${summary.metrics.graphNeighborRecall.toFixed(4)} is below minimum ${thresholds.minGraphNeighborRecall.toFixed(4)}`
|
|
15852
|
+
});
|
|
15853
|
+
}
|
|
15854
|
+
if (thresholds.minRouteAccuracy !== void 0 && summary.metrics.routeAccuracy < thresholds.minRouteAccuracy) {
|
|
15855
|
+
violations.push({
|
|
15856
|
+
metric: "minRouteAccuracy",
|
|
15857
|
+
message: `Route accuracy ${summary.metrics.routeAccuracy.toFixed(4)} is below minimum ${thresholds.minRouteAccuracy.toFixed(4)}`
|
|
15858
|
+
});
|
|
15859
|
+
}
|
|
15860
|
+
if (thresholds.minOutcomeAccuracy !== void 0 && summary.metrics.outcomeAccuracy < thresholds.minOutcomeAccuracy) {
|
|
15861
|
+
violations.push({
|
|
15862
|
+
metric: "minOutcomeAccuracy",
|
|
15863
|
+
message: `Outcome accuracy ${summary.metrics.outcomeAccuracy.toFixed(4)} is below minimum ${thresholds.minOutcomeAccuracy.toFixed(4)}`
|
|
15864
|
+
});
|
|
15865
|
+
}
|
|
15498
15866
|
if (comparison) {
|
|
15499
15867
|
if (thresholds.hitAt5MaxDrop !== void 0 && comparison.deltas.hitAt5.absolute < -thresholds.hitAt5MaxDrop) {
|
|
15500
15868
|
violations.push({
|
|
@@ -15672,6 +16040,14 @@ function isSymbolIntended(query) {
|
|
|
15672
16040
|
function isExpectedFile(filePath, relevant) {
|
|
15673
16041
|
return relevant.some((entry) => pathMatchesExpected(filePath, entry.path));
|
|
15674
16042
|
}
|
|
16043
|
+
function graphNeighborMatches(query, result) {
|
|
16044
|
+
const expected = query.expected.graphNeighbor;
|
|
16045
|
+
if (!expected || result.graphDirection !== expected.direction) return false;
|
|
16046
|
+
if (expected.filePath !== void 0 && !pathMatchesExpected(result.filePath, expected.filePath)) {
|
|
16047
|
+
return false;
|
|
16048
|
+
}
|
|
16049
|
+
return expected.symbol === void 0 || result.name === expected.symbol;
|
|
16050
|
+
}
|
|
15675
16051
|
function resultRelevance(filePath, symbol, relevant, isSymbolIntendedQuery) {
|
|
15676
16052
|
let relevance = 0;
|
|
15677
16053
|
for (const entry of relevant) {
|
|
@@ -15819,6 +16195,7 @@ function buildPerQueryResult(query, results, latencyMs, k, route = {
|
|
|
15819
16195
|
routeMatched: query.expected.expectedRoute ? query.expected.expectedRoute === route.resolvedRoute : void 0,
|
|
15820
16196
|
outcomeMatched: query.expected.expectedOutcome === void 0 ? void 0 : query.expected.expectedOutcome === "results" ? deduped.length > 0 : deduped.length === 0,
|
|
15821
16197
|
recoveryMatched: query.expected.recoveryExpectation === void 0 ? void 0 : query.expected.recoveryExpectation === "filter-relaxed" ? context?.recoveryRelaxed === true : context?.recoveryUsed !== true,
|
|
16198
|
+
graphNeighborMatched: query.expected.graphNeighbor === void 0 ? void 0 : results.some((result) => graphNeighborMatches(query, result)),
|
|
15822
16199
|
language: query.language,
|
|
15823
16200
|
difficulty: query.difficulty,
|
|
15824
16201
|
tags: query.tags,
|
|
@@ -15868,7 +16245,7 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15868
16245
|
"no-relevant-hit-top-k": 0
|
|
15869
16246
|
};
|
|
15870
16247
|
const latencies = perQuery.map((item) => item.latencyMs);
|
|
15871
|
-
const contextQueries = perQuery.filter((item) => item.retrievalMode
|
|
16248
|
+
const contextQueries = perQuery.filter((item) => item.retrievalMode !== "search");
|
|
15872
16249
|
const contextResponseTokens = contextQueries.map((item) => item.responseTokens);
|
|
15873
16250
|
const totalContextResponseTokens = contextResponseTokens.reduce((sum2, value) => sum2 + value, 0);
|
|
15874
16251
|
const contextTokenUnits = totalContextResponseTokens / 1e3;
|
|
@@ -15878,6 +16255,8 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15878
16255
|
let outcomeExpectedCount = 0;
|
|
15879
16256
|
let recoveryMatchedCount = 0;
|
|
15880
16257
|
let recoveryExpectedCount = 0;
|
|
16258
|
+
let graphNeighborMatchedCount = 0;
|
|
16259
|
+
let graphNeighborExpectedCount = 0;
|
|
15881
16260
|
for (const query of perQuery) {
|
|
15882
16261
|
if (positiveQueryIds.has(query.id)) {
|
|
15883
16262
|
if (query.hitAt1) sum.hitAt1 += 1;
|
|
@@ -15906,6 +16285,10 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15906
16285
|
recoveryExpectedCount += 1;
|
|
15907
16286
|
if (query.recoveryMatched) recoveryMatchedCount += 1;
|
|
15908
16287
|
}
|
|
16288
|
+
if (query.graphNeighborMatched !== void 0) {
|
|
16289
|
+
graphNeighborExpectedCount += 1;
|
|
16290
|
+
if (query.graphNeighborMatched) graphNeighborMatchedCount += 1;
|
|
16291
|
+
}
|
|
15909
16292
|
}
|
|
15910
16293
|
const queryTokens = queries.reduce((acc, q) => acc + estimateTokens(q.query), 0);
|
|
15911
16294
|
return {
|
|
@@ -15918,6 +16301,7 @@ function computeEvalMetrics(queries, perQuery, embeddingCallCount, embeddingToke
|
|
|
15918
16301
|
routeAccuracy: routeExpectedCount === 0 ? 0 : routeMatchedCount / routeExpectedCount,
|
|
15919
16302
|
outcomeAccuracy: outcomeExpectedCount === 0 ? 0 : outcomeMatchedCount / outcomeExpectedCount,
|
|
15920
16303
|
recoveryAccuracy: recoveryExpectedCount === 0 ? 0 : recoveryMatchedCount / recoveryExpectedCount,
|
|
16304
|
+
graphNeighborRecall: graphNeighborExpectedCount === 0 ? 0 : graphNeighborMatchedCount / graphNeighborExpectedCount,
|
|
15921
16305
|
distinctTop3Ratio: safeDiv(sum.distinctTop3Ratio),
|
|
15922
16306
|
rawDistinctTop3Ratio: safeDiv(sum.rawDistinctTop3Ratio),
|
|
15923
16307
|
latencyMs: {
|
|
@@ -16183,14 +16567,29 @@ function parseQueryArgs(value, path31) {
|
|
|
16183
16567
|
throw new Error(`${path31} must be an object`);
|
|
16184
16568
|
}
|
|
16185
16569
|
const symbol = parseStringOrUndefined(value.symbol, `${path31}.symbol`);
|
|
16570
|
+
const filePath = parseStringOrUndefined(value.filePath, `${path31}.filePath`);
|
|
16186
16571
|
const fileType = parseStringOrUndefined(value.fileType, `${path31}.fileType`);
|
|
16187
16572
|
const directory = parseStringOrUndefined(value.directory, `${path31}.directory`);
|
|
16573
|
+
const callerLimit = parsePositiveIntegerOrUndefined(value.callerLimit, `${path31}.callerLimit`);
|
|
16574
|
+
const calleeLimit = parsePositiveIntegerOrUndefined(value.calleeLimit, `${path31}.calleeLimit`);
|
|
16575
|
+
const tokenBudget = parsePositiveIntegerOrUndefined(value.tokenBudget, `${path31}.tokenBudget`);
|
|
16188
16576
|
return {
|
|
16189
16577
|
...symbol !== void 0 ? { symbol } : {},
|
|
16578
|
+
...filePath !== void 0 ? { filePath } : {},
|
|
16190
16579
|
...fileType !== void 0 ? { fileType } : {},
|
|
16191
|
-
...directory !== void 0 ? { directory } : {}
|
|
16580
|
+
...directory !== void 0 ? { directory } : {},
|
|
16581
|
+
...callerLimit !== void 0 ? { callerLimit } : {},
|
|
16582
|
+
...calleeLimit !== void 0 ? { calleeLimit } : {},
|
|
16583
|
+
...tokenBudget !== void 0 ? { tokenBudget } : {}
|
|
16192
16584
|
};
|
|
16193
16585
|
}
|
|
16586
|
+
function parsePositiveIntegerOrUndefined(value, path31) {
|
|
16587
|
+
if (value === void 0 || value === null) return void 0;
|
|
16588
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
16589
|
+
throw new Error(`${path31} must be a positive integer`);
|
|
16590
|
+
}
|
|
16591
|
+
return value;
|
|
16592
|
+
}
|
|
16194
16593
|
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-]+)*)?$/;
|
|
16195
16594
|
function parseSemanticVersion(value, path31) {
|
|
16196
16595
|
if (!isNonEmptyString(value)) {
|
|
@@ -16203,8 +16602,8 @@ function parseSemanticVersion(value, path31) {
|
|
|
16203
16602
|
}
|
|
16204
16603
|
function parseRetrievalMode(value, path31) {
|
|
16205
16604
|
if (value === void 0 || value === "search") return "search";
|
|
16206
|
-
if (value === "context") return value;
|
|
16207
|
-
throw new Error(`${path31} must be one of: search, context`);
|
|
16605
|
+
if (value === "context" || value === "edit-context") return value;
|
|
16606
|
+
throw new Error(`${path31} must be one of: search, context, edit-context`);
|
|
16208
16607
|
}
|
|
16209
16608
|
function parseStringOrUndefined(value, path31) {
|
|
16210
16609
|
if (value === void 0 || value === null) return void 0;
|
|
@@ -16244,6 +16643,25 @@ function parseEvidenceRelevance(value, path31) {
|
|
|
16244
16643
|
}
|
|
16245
16644
|
return value;
|
|
16246
16645
|
}
|
|
16646
|
+
function parseExpectedGraphNeighbor(value, path31) {
|
|
16647
|
+
if (value === void 0) return void 0;
|
|
16648
|
+
if (!isRecord3(value)) {
|
|
16649
|
+
throw new Error(`${path31} must be an object`);
|
|
16650
|
+
}
|
|
16651
|
+
if (value.direction !== "caller" && value.direction !== "callee") {
|
|
16652
|
+
throw new Error(`${path31}.direction must be one of: caller, callee`);
|
|
16653
|
+
}
|
|
16654
|
+
const filePath = parseStringOrUndefined(value.filePath, `${path31}.filePath`);
|
|
16655
|
+
const symbol = parseStringOrUndefined(value.symbol, `${path31}.symbol`);
|
|
16656
|
+
if (filePath === void 0 && symbol === void 0) {
|
|
16657
|
+
throw new Error(`${path31} must include filePath or symbol`);
|
|
16658
|
+
}
|
|
16659
|
+
return {
|
|
16660
|
+
direction: value.direction,
|
|
16661
|
+
...filePath !== void 0 ? { filePath } : {},
|
|
16662
|
+
...symbol !== void 0 ? { symbol } : {}
|
|
16663
|
+
};
|
|
16664
|
+
}
|
|
16247
16665
|
function parseExpected(input, path31) {
|
|
16248
16666
|
if (!isRecord3(input)) {
|
|
16249
16667
|
throw new Error(`${path31} must be an object`);
|
|
@@ -16256,9 +16674,11 @@ function parseExpected(input, path31) {
|
|
|
16256
16674
|
const expectedOutcomeRaw = input.expectedOutcome;
|
|
16257
16675
|
const recoveryExpectationRaw = input.recoveryExpectation;
|
|
16258
16676
|
const gradedEvidenceRaw = input.gradedEvidence;
|
|
16677
|
+
const graphNeighborRaw = input.graphNeighbor;
|
|
16259
16678
|
const filePath = parseStringOrUndefined(filePathRaw, `${path31}.filePath`);
|
|
16260
16679
|
const acceptableFiles = isStringArray4(acceptableFilesRaw) ? acceptableFilesRaw : void 0;
|
|
16261
16680
|
const gradedEvidence = parseGradedEvidence(gradedEvidenceRaw, `${path31}.gradedEvidence`);
|
|
16681
|
+
const graphNeighbor = parseExpectedGraphNeighbor(graphNeighborRaw, `${path31}.graphNeighbor`);
|
|
16262
16682
|
const expectedOutcome = parseExpectedOutcome(expectedOutcomeRaw, `${path31}.expectedOutcome`);
|
|
16263
16683
|
if (expectedOutcome !== "no-results" && !filePath && (!acceptableFiles || acceptableFiles.length === 0) && gradedEvidence.length === 0) {
|
|
16264
16684
|
throw new Error(
|
|
@@ -16287,7 +16707,8 @@ function parseExpected(input, path31) {
|
|
|
16287
16707
|
expectedRoute,
|
|
16288
16708
|
expectedOutcome,
|
|
16289
16709
|
recoveryExpectation,
|
|
16290
|
-
...gradedEvidence.length > 0 ? { gradedEvidence } : {}
|
|
16710
|
+
...gradedEvidence.length > 0 ? { gradedEvidence } : {},
|
|
16711
|
+
...graphNeighbor !== void 0 ? { graphNeighbor } : {}
|
|
16291
16712
|
};
|
|
16292
16713
|
}
|
|
16293
16714
|
function parseQueryLanguage(value, path31) {
|
|
@@ -16422,6 +16843,21 @@ function parseBudget(raw, sourceLabel) {
|
|
|
16422
16843
|
"minRawDistinctTop3Ratio",
|
|
16423
16844
|
sourceLabel
|
|
16424
16845
|
),
|
|
16846
|
+
minGraphNeighborRecall: parseThresholdValue(
|
|
16847
|
+
thresholds.minGraphNeighborRecall,
|
|
16848
|
+
"minGraphNeighborRecall",
|
|
16849
|
+
sourceLabel
|
|
16850
|
+
),
|
|
16851
|
+
minRouteAccuracy: parseThresholdValue(
|
|
16852
|
+
thresholds.minRouteAccuracy,
|
|
16853
|
+
"minRouteAccuracy",
|
|
16854
|
+
sourceLabel
|
|
16855
|
+
),
|
|
16856
|
+
minOutcomeAccuracy: parseThresholdValue(
|
|
16857
|
+
thresholds.minOutcomeAccuracy,
|
|
16858
|
+
"minOutcomeAccuracy",
|
|
16859
|
+
sourceLabel
|
|
16860
|
+
),
|
|
16425
16861
|
maxContextResponseTokensAverage: parseThresholdValue(
|
|
16426
16862
|
thresholds.maxContextResponseTokensAverage,
|
|
16427
16863
|
"maxContextResponseTokensAverage",
|
|
@@ -16486,6 +16922,120 @@ function buildDatasetFingerprint(dataset) {
|
|
|
16486
16922
|
const canonical = JSON.stringify(normalizeForFingerprint(dataset));
|
|
16487
16923
|
return crypto2.createHash("sha256").update(canonical).digest("hex");
|
|
16488
16924
|
}
|
|
16925
|
+
function normalizedPath2(value) {
|
|
16926
|
+
return value.replaceAll("\\", "/").replace(/^\.\//, "");
|
|
16927
|
+
}
|
|
16928
|
+
function pathsMatch2(left, right) {
|
|
16929
|
+
const normalizedLeft = normalizedPath2(left);
|
|
16930
|
+
const normalizedRight = normalizedPath2(right);
|
|
16931
|
+
return normalizedLeft === normalizedRight || normalizedLeft.endsWith(`/${normalizedRight}`) || normalizedRight.endsWith(`/${normalizedLeft}`);
|
|
16932
|
+
}
|
|
16933
|
+
function toEvalSearchResult(result) {
|
|
16934
|
+
return {
|
|
16935
|
+
filePath: result.filePath,
|
|
16936
|
+
startLine: result.startLine,
|
|
16937
|
+
endLine: result.endLine,
|
|
16938
|
+
score: result.score,
|
|
16939
|
+
chunkType: result.chunkType,
|
|
16940
|
+
name: result.name
|
|
16941
|
+
};
|
|
16942
|
+
}
|
|
16943
|
+
function selectResolvedTarget(definitions, resolution) {
|
|
16944
|
+
if (resolution?.status !== "resolved") return definitions[0];
|
|
16945
|
+
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];
|
|
16946
|
+
}
|
|
16947
|
+
function callerResult(edge) {
|
|
16948
|
+
if (!edge.fromSymbolFilePath) return void 0;
|
|
16949
|
+
return {
|
|
16950
|
+
filePath: edge.fromSymbolFilePath,
|
|
16951
|
+
startLine: edge.line,
|
|
16952
|
+
endLine: edge.line,
|
|
16953
|
+
score: 0,
|
|
16954
|
+
chunkType: "graph-caller",
|
|
16955
|
+
name: edge.fromSymbolName,
|
|
16956
|
+
graphDirection: "caller"
|
|
16957
|
+
};
|
|
16958
|
+
}
|
|
16959
|
+
function calleeResult(edge, symbols) {
|
|
16960
|
+
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;
|
|
16961
|
+
if (!symbol) return void 0;
|
|
16962
|
+
return {
|
|
16963
|
+
filePath: symbol.filePath,
|
|
16964
|
+
startLine: symbol.startLine,
|
|
16965
|
+
endLine: symbol.endLine,
|
|
16966
|
+
score: 0,
|
|
16967
|
+
chunkType: "graph-callee",
|
|
16968
|
+
name: symbol.name,
|
|
16969
|
+
graphDirection: "callee"
|
|
16970
|
+
};
|
|
16971
|
+
}
|
|
16972
|
+
async function runEditContextQuery(indexer, projectRoot, query) {
|
|
16973
|
+
let definitions = [];
|
|
16974
|
+
let conceptual = [];
|
|
16975
|
+
let callers;
|
|
16976
|
+
let callees;
|
|
16977
|
+
const editContext = await resolveCodebaseEditContextWithDependencies({
|
|
16978
|
+
query: query.query,
|
|
16979
|
+
symbol: query.args?.symbol,
|
|
16980
|
+
filePath: query.args?.filePath ?? query.expected.filePath,
|
|
16981
|
+
callerLimit: query.args?.callerLimit,
|
|
16982
|
+
calleeLimit: query.args?.calleeLimit,
|
|
16983
|
+
tokenBudget: query.args?.tokenBudget
|
|
16984
|
+
}, {
|
|
16985
|
+
searchCodebase: async (searchQuery, options) => {
|
|
16986
|
+
conceptual = await indexer.search(searchQuery, options?.limit, {
|
|
16987
|
+
filterByBranch: !!query.expected.branch
|
|
16988
|
+
});
|
|
16989
|
+
return conceptual;
|
|
16990
|
+
},
|
|
16991
|
+
implementationLookup: async (symbol, options) => {
|
|
16992
|
+
definitions = await indexer.search(symbol, options?.limit, {
|
|
16993
|
+
filterByBranch: !!query.expected.branch,
|
|
16994
|
+
definitionIntent: true
|
|
16995
|
+
});
|
|
16996
|
+
return definitions;
|
|
16997
|
+
},
|
|
16998
|
+
getCallGraphData: async (params) => {
|
|
16999
|
+
const result = await getCallGraphDataForIndexer(indexer, projectRoot, params);
|
|
17000
|
+
if (params.direction === "callers") callers = result;
|
|
17001
|
+
else callees = result;
|
|
17002
|
+
return result;
|
|
17003
|
+
}
|
|
17004
|
+
});
|
|
17005
|
+
const resolution = callers?.resolution;
|
|
17006
|
+
const target = selectResolvedTarget(definitions, resolution);
|
|
17007
|
+
const targetCandidates = target ? [target] : [...definitions, ...conceptual];
|
|
17008
|
+
const results = targetCandidates.filter((candidate) => (resolution?.status !== "resolved" || editContext.details.sourceIncluded) && editContext.text.includes(
|
|
17009
|
+
`${candidate.filePath}:${candidate.startLine}-${candidate.endLine}`
|
|
17010
|
+
)).map(toEvalSearchResult);
|
|
17011
|
+
if (query.expected.graphNeighbor) {
|
|
17012
|
+
const symbols = await indexer.getCallGraphSymbols();
|
|
17013
|
+
const callerLimit = query.args?.callerLimit ?? DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT;
|
|
17014
|
+
const calleeLimit = query.args?.calleeLimit ?? DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT;
|
|
17015
|
+
const publishedCallers = (callers?.callers ?? []).slice(0, callerLimit).filter((edge) => editContext.text.includes(
|
|
17016
|
+
`${edge.fromSymbolName ?? "<unknown>"} at ${edge.fromSymbolFilePath ?? "<unknown file>"}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`
|
|
17017
|
+
));
|
|
17018
|
+
const publishedCallees = (callees?.callees ?? []).slice(0, calleeLimit).filter((edge) => resolution?.status === "resolved" && editContext.text.includes(
|
|
17019
|
+
`${edge.targetName} from ${resolution.filePath}:${edge.line} (${edge.callType}, ${edge.isResolved ? "resolved" : "unresolved"})`
|
|
17020
|
+
));
|
|
17021
|
+
results.push(
|
|
17022
|
+
...publishedCallers.map(callerResult).filter((item) => item !== void 0),
|
|
17023
|
+
...publishedCallees.map((edge) => calleeResult(edge, symbols)).filter((item) => item !== void 0)
|
|
17024
|
+
);
|
|
17025
|
+
}
|
|
17026
|
+
return {
|
|
17027
|
+
results,
|
|
17028
|
+
resolvedRoute: resolution?.status === "resolved" ? "definition" : "search",
|
|
17029
|
+
routedQuery: query.args?.symbol ?? query.query,
|
|
17030
|
+
context: {
|
|
17031
|
+
tokenBudget: editContext.details.tokenBudget,
|
|
17032
|
+
responseTokens: editContext.details.tokenEstimate,
|
|
17033
|
+
candidateCount: results.length,
|
|
17034
|
+
deduplicatedCount: results.length,
|
|
17035
|
+
omittedCount: 0
|
|
17036
|
+
}
|
|
17037
|
+
};
|
|
17038
|
+
}
|
|
16489
17039
|
async function runEvaluation(options) {
|
|
16490
17040
|
const datasetPath = toAbsolute(options.projectRoot, options.datasetPath);
|
|
16491
17041
|
const againstPath = options.againstPath ? toAbsolute(options.projectRoot, options.againstPath) : void 0;
|
|
@@ -16517,6 +17067,7 @@ async function runEvaluation(options) {
|
|
|
16517
17067
|
);
|
|
16518
17068
|
}
|
|
16519
17069
|
const start = performance3.now();
|
|
17070
|
+
const editContextResult = query.retrievalMode === "edit-context" ? await runEditContextQuery(indexer, options.projectRoot, query) : void 0;
|
|
16520
17071
|
const contextResult = query.retrievalMode === "context" ? await resolveSearchContext({
|
|
16521
17072
|
query: query.query,
|
|
16522
17073
|
symbol: query.args?.symbol,
|
|
@@ -16540,31 +17091,32 @@ async function runEvaluation(options) {
|
|
|
16540
17091
|
directory: scope.directory
|
|
16541
17092
|
})
|
|
16542
17093
|
}) : void 0;
|
|
16543
|
-
const result = contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
17094
|
+
const result = editContextResult?.results ?? contextResult?.details?.results ?? await indexer.search(query.query, 10, {
|
|
16544
17095
|
metadataOnly: true,
|
|
16545
17096
|
filterByBranch: !!query.expected.branch,
|
|
16546
17097
|
fileType: query.args?.fileType,
|
|
16547
17098
|
directory: query.args?.directory
|
|
16548
17099
|
});
|
|
16549
17100
|
const elapsed = performance3.now() - start;
|
|
16550
|
-
const resolvedRoute = contextResult?.details?.route === "definition" ? "definition" : "search";
|
|
16551
|
-
const routedQuery = contextResult?.details?.routedQuery ?? query.query;
|
|
17101
|
+
const resolvedRoute = editContextResult?.resolvedRoute ?? (contextResult?.details?.route === "definition" ? "definition" : "search");
|
|
17102
|
+
const routedQuery = editContextResult?.routedQuery ?? contextResult?.details?.routedQuery ?? query.query;
|
|
16552
17103
|
const successfulRecoveryAttempt = contextResult?.details?.recovery?.successfulAttemptIndex;
|
|
16553
17104
|
const recoveryAttempts = contextResult?.details?.recovery?.attempts ?? [];
|
|
16554
17105
|
const recoveryRelaxed = successfulRecoveryAttempt === void 0 ? false : (recoveryAttempts[successfulRecoveryAttempt]?.relaxedFields.length ?? 0) > 0;
|
|
16555
17106
|
const recoveryUsed = recoveryAttempts.length > 1 || recoveryAttempts.some((attempt) => attempt.relaxedFields.length > 0);
|
|
16556
|
-
const materialized = result.map((item) =>
|
|
16557
|
-
|
|
16558
|
-
|
|
16559
|
-
|
|
16560
|
-
|
|
16561
|
-
|
|
16562
|
-
|
|
16563
|
-
|
|
16564
|
-
|
|
16565
|
-
|
|
16566
|
-
|
|
16567
|
-
}
|
|
17107
|
+
const materialized = result.map((item) => {
|
|
17108
|
+
const graphDirection = "graphDirection" in item && (item.graphDirection === "caller" || item.graphDirection === "callee") ? item.graphDirection : void 0;
|
|
17109
|
+
return {
|
|
17110
|
+
filePath: item.filePath,
|
|
17111
|
+
startLine: item.startLine,
|
|
17112
|
+
endLine: item.endLine,
|
|
17113
|
+
score: item.score,
|
|
17114
|
+
chunkType: item.chunkType,
|
|
17115
|
+
name: item.name,
|
|
17116
|
+
graphDirection
|
|
17117
|
+
};
|
|
17118
|
+
});
|
|
17119
|
+
const contextMeasurement = editContextResult?.context ?? (contextResult?.details ? {
|
|
16568
17120
|
tokenBudget: contextResult.details.tokenBudget,
|
|
16569
17121
|
responseTokens: contextResult.details.tokenEstimate,
|
|
16570
17122
|
candidateCount: contextResult.details.candidateCount ?? 0,
|
|
@@ -16572,7 +17124,11 @@ async function runEvaluation(options) {
|
|
|
16572
17124
|
omittedCount: contextResult.details.omittedCount ?? 0,
|
|
16573
17125
|
recoveryUsed,
|
|
16574
17126
|
recoveryRelaxed
|
|
16575
|
-
} : void 0)
|
|
17127
|
+
} : void 0);
|
|
17128
|
+
perQuery.push(buildPerQueryResult(query, materialized, elapsed, 10, {
|
|
17129
|
+
resolvedRoute,
|
|
17130
|
+
routedQuery
|
|
17131
|
+
}, contextMeasurement));
|
|
16576
17132
|
}
|
|
16577
17133
|
const logger = indexer.getLogger();
|
|
16578
17134
|
const metricSnapshot = logger.getMetrics();
|
|
@@ -17108,7 +17664,11 @@ import { z as z2 } from "zod";
|
|
|
17108
17664
|
|
|
17109
17665
|
// src/tools/execute-common.ts
|
|
17110
17666
|
async function executeCodebaseContext(projectRoot, host, args) {
|
|
17111
|
-
|
|
17667
|
+
const result = await resolveCodebaseContext(projectRoot, host, args);
|
|
17668
|
+
return { text: result.text, details: result.details };
|
|
17669
|
+
}
|
|
17670
|
+
async function executeCodebaseEditContext(projectRoot, host, args) {
|
|
17671
|
+
return { text: (await resolveCodebaseEditContext(projectRoot, host, args)).text };
|
|
17112
17672
|
}
|
|
17113
17673
|
async function executeIndexCodebase(projectRoot, host, args, onProgress) {
|
|
17114
17674
|
const result = await runIndexCodebase(projectRoot, host, args, onProgress);
|
|
@@ -17224,6 +17784,7 @@ function formatPrImpact(result) {
|
|
|
17224
17784
|
// src/tools/tool-names.ts
|
|
17225
17785
|
var TOOL_NAME = {
|
|
17226
17786
|
CODEBASE_CONTEXT: "codebase_context",
|
|
17787
|
+
CODEBASE_EDIT_CONTEXT: "codebase_edit_context",
|
|
17227
17788
|
CODEBASE_SEARCH: "codebase_search",
|
|
17228
17789
|
CODEBASE_PEEK: "codebase_peek",
|
|
17229
17790
|
FIND_SIMILAR: "find_similar",
|
|
@@ -17247,6 +17808,7 @@ var TOOL_NAME = {
|
|
|
17247
17808
|
};
|
|
17248
17809
|
var PORTABLE_TOOL_NAMES = [
|
|
17249
17810
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
17811
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17250
17812
|
TOOL_NAME.CODEBASE_SEARCH,
|
|
17251
17813
|
TOOL_NAME.CODEBASE_PEEK,
|
|
17252
17814
|
TOOL_NAME.INDEX_CODEBASE,
|
|
@@ -17263,6 +17825,7 @@ var PORTABLE_TOOL_NAMES = [
|
|
|
17263
17825
|
];
|
|
17264
17826
|
var OPENCODE_TOOL_NAMES = [
|
|
17265
17827
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
17828
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17266
17829
|
TOOL_NAME.CODEBASE_SEARCH,
|
|
17267
17830
|
TOOL_NAME.CODEBASE_PEEK,
|
|
17268
17831
|
TOOL_NAME.INDEX_CODEBASE,
|
|
@@ -17283,6 +17846,7 @@ var OPENCODE_TOOL_NAMES = [
|
|
|
17283
17846
|
];
|
|
17284
17847
|
var PI_TOOL_NAMES = [
|
|
17285
17848
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
17849
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17286
17850
|
TOOL_NAME.CODEBASE_SEARCH,
|
|
17287
17851
|
TOOL_NAME.CODEBASE_PEEK,
|
|
17288
17852
|
TOOL_NAME.FIND_SIMILAR,
|
|
@@ -17326,10 +17890,30 @@ function registerMcpTools(server, runtime) {
|
|
|
17326
17890
|
directory: allowNullAsUndefined(z2.string().optional()).describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
17327
17891
|
tokenBudget: allowNullAsUndefined(
|
|
17328
17892
|
z2.number().int().min(MIN_CONTEXT_PACK_TOKEN_BUDGET).max(MAX_CONTEXT_PACK_TOKEN_BUDGET).optional().default(DEFAULT_CONTEXT_PACK_TOKEN_BUDGET)
|
|
17329
|
-
).describe(`Maximum response tokens for this context pack (${MIN_CONTEXT_PACK_TOKEN_BUDGET}-${MAX_CONTEXT_PACK_TOKEN_BUDGET})`)
|
|
17893
|
+
).describe(`Maximum response tokens for this context pack (${MIN_CONTEXT_PACK_TOKEN_BUDGET}-${MAX_CONTEXT_PACK_TOKEN_BUDGET})`),
|
|
17894
|
+
diagnostic: z2.boolean().optional().describe("Collect diagnostic routing and search traces without changing normal text output.")
|
|
17330
17895
|
},
|
|
17331
17896
|
async (args) => {
|
|
17332
17897
|
const result = await executeCodebaseContext(runtime.projectRoot, runtime.host, args);
|
|
17898
|
+
return {
|
|
17899
|
+
content: [{ type: "text", text: result.text }],
|
|
17900
|
+
...args.diagnostic ? { structuredContent: result.details } : {}
|
|
17901
|
+
};
|
|
17902
|
+
}
|
|
17903
|
+
);
|
|
17904
|
+
server.tool(
|
|
17905
|
+
TOOL_NAME.CODEBASE_EDIT_CONTEXT,
|
|
17906
|
+
"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.",
|
|
17907
|
+
{
|
|
17908
|
+
query: z2.string().describe("The requested change or target behavior."),
|
|
17909
|
+
symbol: allowNullAsUndefined(z2.string().optional()).describe("Authoritative target symbol when known."),
|
|
17910
|
+
filePath: allowNullAsUndefined(z2.string().optional()).describe("Optional file path used to disambiguate duplicate symbol names."),
|
|
17911
|
+
callerLimit: allowNullAsUndefined(z2.number().int().min(MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).max(MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).optional().default(DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT)),
|
|
17912
|
+
calleeLimit: allowNullAsUndefined(z2.number().int().min(MIN_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).max(MAX_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT).optional().default(DEFAULT_CODEBASE_EDIT_CONTEXT_EDGE_LIMIT)),
|
|
17913
|
+
tokenBudget: allowNullAsUndefined(z2.number().int().min(MIN_CONTEXT_PACK_TOKEN_BUDGET).max(MAX_CONTEXT_PACK_TOKEN_BUDGET).optional().default(DEFAULT_CONTEXT_PACK_TOKEN_BUDGET))
|
|
17914
|
+
},
|
|
17915
|
+
async (args) => {
|
|
17916
|
+
const result = await executeCodebaseEditContext(runtime.projectRoot, runtime.host, args);
|
|
17333
17917
|
return { content: [{ type: "text", text: result.text }] };
|
|
17334
17918
|
}
|
|
17335
17919
|
);
|
|
@@ -19786,9 +20370,9 @@ function parseGitActivity(output) {
|
|
|
19786
20370
|
if (!filePath || addedRaw === "-" || deletedRaw === "-") continue;
|
|
19787
20371
|
const churn = Number(addedRaw) + Number(deletedRaw);
|
|
19788
20372
|
if (!Number.isFinite(churn) || churn <= 0) continue;
|
|
19789
|
-
const
|
|
19790
|
-
const previous = activity.get(
|
|
19791
|
-
activity.set(
|
|
20373
|
+
const normalizedPath3 = normalizePath4(filePath);
|
|
20374
|
+
const previous = activity.get(normalizedPath3);
|
|
20375
|
+
activity.set(normalizedPath3, {
|
|
19792
20376
|
churn: (previous?.churn ?? 0) + churn,
|
|
19793
20377
|
commits: (previous?.commits ?? 0) + 1,
|
|
19794
20378
|
latestDate: previous?.latestDate ?? latestDate,
|
|
@@ -20378,8 +20962,8 @@ function transformForVisualization(symbols, edges, options = {}) {
|
|
|
20378
20962
|
const normalizedAbsoluteSuffix = `/${normalizedDirWithSlash}`;
|
|
20379
20963
|
filteredSymbols = symbols.filter(
|
|
20380
20964
|
(s) => {
|
|
20381
|
-
const
|
|
20382
|
-
return
|
|
20965
|
+
const normalizedPath3 = s.filePath.replace(/\\/g, "/");
|
|
20966
|
+
return normalizedPath3 === normalizedDir || normalizedPath3.startsWith(normalizedDirWithSlash) || normalizedPath3.endsWith(`/${normalizedDir}`) || normalizedPath3.includes(normalizedAbsoluteSuffix);
|
|
20383
20967
|
}
|
|
20384
20968
|
);
|
|
20385
20969
|
}
|