open-codebase-index 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/README.md +6 -5
- package/commands/peek.md +2 -1
- package/commands/search.md +2 -1
- package/dist/cli.cjs +1020 -260
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1020 -260
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +788 -256
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +788 -256
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +5440 -2157
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +5429 -2146
- 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 +3 -1
- package/skill/SKILL.md +12 -16
package/dist/index.js
CHANGED
|
@@ -712,6 +712,17 @@ var EMBEDDING_MODELS = {
|
|
|
712
712
|
maxTokens: 2048,
|
|
713
713
|
costPer1MTokens: 0.15,
|
|
714
714
|
taskAble: true
|
|
715
|
+
},
|
|
716
|
+
"gemini-embedding-2": {
|
|
717
|
+
provider: "google",
|
|
718
|
+
model: "gemini-embedding-2",
|
|
719
|
+
// Keep a conservative, testable default embedding dimension. Gemini Embedding 2 supports
|
|
720
|
+
// flexible dimensions via outputDimensionality.
|
|
721
|
+
dimensions: 1536,
|
|
722
|
+
maxTokens: 8192,
|
|
723
|
+
costPer1MTokens: 0.15,
|
|
724
|
+
taskAble: false,
|
|
725
|
+
promptStyle: "embedding-2"
|
|
715
726
|
}
|
|
716
727
|
},
|
|
717
728
|
"openai": {
|
|
@@ -745,26 +756,15 @@ var EMBEDDING_MODELS = {
|
|
|
745
756
|
maxTokens: 512,
|
|
746
757
|
costPer1MTokens: 0
|
|
747
758
|
}
|
|
748
|
-
},
|
|
749
|
-
"github-copilot": {
|
|
750
|
-
"text-embedding-3-small": {
|
|
751
|
-
provider: "github-copilot",
|
|
752
|
-
model: "text-embedding-3-small",
|
|
753
|
-
dimensions: 1536,
|
|
754
|
-
maxTokens: 8191,
|
|
755
|
-
costPer1MTokens: 0
|
|
756
|
-
}
|
|
757
759
|
}
|
|
758
760
|
};
|
|
759
761
|
var DEFAULT_PROVIDER_MODELS = {
|
|
760
|
-
"github-copilot": "text-embedding-3-small",
|
|
761
762
|
"openai": "text-embedding-3-small",
|
|
762
763
|
"google": "gemini-embedding-001",
|
|
763
764
|
"ollama": "nomic-embed-text"
|
|
764
765
|
};
|
|
765
766
|
var AUTO_DETECT_PROVIDER_ORDER = [
|
|
766
767
|
"ollama",
|
|
767
|
-
"github-copilot",
|
|
768
768
|
"openai",
|
|
769
769
|
"google"
|
|
770
770
|
];
|
|
@@ -790,6 +790,9 @@ function getDefaultIndexingConfig() {
|
|
|
790
790
|
maxDepth: 5,
|
|
791
791
|
maxFilesPerDirectory: 100,
|
|
792
792
|
fallbackToTextOnMaxChunks: true,
|
|
793
|
+
// Must stay in sync with DEFAULT_LINES_PER_CHUNK in native/src/lib.rs (the napi
|
|
794
|
+
// fallback used when a native caller omits the argument).
|
|
795
|
+
linesPerChunk: 30,
|
|
793
796
|
gitBlame: { enabled: false }
|
|
794
797
|
};
|
|
795
798
|
}
|
|
@@ -923,6 +926,7 @@ function parseConfig(raw) {
|
|
|
923
926
|
maxDepth: typeof rawIndexing.maxDepth === "number" ? rawIndexing.maxDepth < -1 ? -1 : rawIndexing.maxDepth : defaultIndexing.maxDepth,
|
|
924
927
|
maxFilesPerDirectory: typeof rawIndexing.maxFilesPerDirectory === "number" ? Math.max(1, rawIndexing.maxFilesPerDirectory) : defaultIndexing.maxFilesPerDirectory,
|
|
925
928
|
fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks,
|
|
929
|
+
linesPerChunk: typeof rawIndexing.linesPerChunk === "number" && Number.isFinite(rawIndexing.linesPerChunk) ? Math.min(Math.max(1, Math.floor(rawIndexing.linesPerChunk)), 4294967295) : defaultIndexing.linesPerChunk,
|
|
926
930
|
gitBlame: {
|
|
927
931
|
enabled: rawIndexing.gitBlame && typeof rawIndexing.gitBlame === "object" && typeof rawIndexing.gitBlame.enabled === "boolean" ? rawIndexing.gitBlame.enabled : defaultIndexing.gitBlame.enabled
|
|
928
932
|
}
|
|
@@ -965,6 +969,7 @@ function parseConfig(raw) {
|
|
|
965
969
|
let embeddingModel;
|
|
966
970
|
let customProvider;
|
|
967
971
|
let reranker;
|
|
972
|
+
const githubCopilotDeprecationMessage = '`embeddingProvider: "github-copilot"` is deprecated and no longer available. Migrate existing configs to `embeddingProvider: "google"` and select an explicit Google model. For existing indexes, run `index_codebase` with `force: true` after changing to `gemini-embedding-001` or `gemini-embedding-2` to rebuild embeddings. See docs/configuration.md for details.';
|
|
968
973
|
if (embeddingProviderValue === "custom") {
|
|
969
974
|
embeddingProvider = "custom";
|
|
970
975
|
const rawCustom = input.customProvider && typeof input.customProvider === "object" ? input.customProvider : null;
|
|
@@ -1004,6 +1009,8 @@ function parseConfig(raw) {
|
|
|
1004
1009
|
} else if (rawEmbeddingModel) {
|
|
1005
1010
|
embeddingModel = DEFAULT_PROVIDER_MODELS[embeddingProvider];
|
|
1006
1011
|
}
|
|
1012
|
+
} else if (embeddingProviderValue === "github-copilot") {
|
|
1013
|
+
throw new Error(githubCopilotDeprecationMessage);
|
|
1007
1014
|
} else {
|
|
1008
1015
|
embeddingProvider = "auto";
|
|
1009
1016
|
}
|
|
@@ -1034,10 +1041,21 @@ function parseConfig(raw) {
|
|
|
1034
1041
|
timeoutMs: typeof rawReranker.timeoutMs === "number" ? Math.max(1e3, Math.floor(rawReranker.timeoutMs)) : 1e4
|
|
1035
1042
|
};
|
|
1036
1043
|
}
|
|
1044
|
+
const rawEmbedding = input.embedding && typeof input.embedding === "object" ? input.embedding : {};
|
|
1045
|
+
const rawEmbeddingBatch = rawEmbedding.batch && typeof rawEmbedding.batch === "object" ? rawEmbedding.batch : null;
|
|
1046
|
+
const embeddingMaxBatchItems = typeof rawEmbeddingBatch?.maxBatchItems === "number" && Number.isFinite(rawEmbeddingBatch.maxBatchItems) ? Math.max(1, Math.floor(rawEmbeddingBatch.maxBatchItems)) : void 0;
|
|
1047
|
+
const embeddingMaxBatchTokens = typeof rawEmbeddingBatch?.maxBatchTokens === "number" && Number.isFinite(rawEmbeddingBatch.maxBatchTokens) ? Math.max(1, Math.floor(rawEmbeddingBatch.maxBatchTokens)) : void 0;
|
|
1048
|
+
const embedding = embeddingMaxBatchItems !== void 0 || embeddingMaxBatchTokens !== void 0 ? {
|
|
1049
|
+
batch: {
|
|
1050
|
+
...embeddingMaxBatchItems !== void 0 ? { maxBatchItems: embeddingMaxBatchItems } : {},
|
|
1051
|
+
...embeddingMaxBatchTokens !== void 0 ? { maxBatchTokens: embeddingMaxBatchTokens } : {}
|
|
1052
|
+
}
|
|
1053
|
+
} : {};
|
|
1037
1054
|
return {
|
|
1038
1055
|
embeddingProvider,
|
|
1039
1056
|
embeddingModel,
|
|
1040
1057
|
customProvider,
|
|
1058
|
+
embedding,
|
|
1041
1059
|
scope: isValidScope(scopeValue) ? scopeValue : "project",
|
|
1042
1060
|
include: includeValue ?? DEFAULT_INCLUDE,
|
|
1043
1061
|
exclude: excludeValue ?? DEFAULT_EXCLUDE,
|
|
@@ -2304,6 +2322,10 @@ function scoreCandidate(query, intent, candidate, originalIndex) {
|
|
|
2304
2322
|
let boost = 0;
|
|
2305
2323
|
if (intent.primary === "conceptual") {
|
|
2306
2324
|
boost += Math.min(0.14, overlap * 0.14);
|
|
2325
|
+
if (intent.preferSourcePaths) {
|
|
2326
|
+
boost += implementationPath ? 0.32 : 0;
|
|
2327
|
+
if (testPath || fixturePath || docsPath) boost -= 0.35;
|
|
2328
|
+
}
|
|
2307
2329
|
if (generatedOrVendor) boost -= 0.18;
|
|
2308
2330
|
if (importChunk || weakContainer) boost -= 0.04;
|
|
2309
2331
|
} else if (intent.primary === "test") {
|
|
@@ -3281,6 +3303,19 @@ function parseOwner(value) {
|
|
|
3281
3303
|
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
3282
3304
|
if (typeof candidate.operation !== "string" || !VALID_OPERATIONS.has(candidate.operation)) return null;
|
|
3283
3305
|
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
3306
|
+
if (candidate.recoveryProtocolVersion !== void 0 && candidate.recoveryProtocolVersion !== 1) return null;
|
|
3307
|
+
if (candidate.projectRoot !== void 0 && typeof candidate.projectRoot !== "string") return null;
|
|
3308
|
+
if (candidate.scopedRoots !== void 0) {
|
|
3309
|
+
if (!Array.isArray(candidate.scopedRoots) || candidate.scopedRoots.some((root) => typeof root !== "string")) {
|
|
3310
|
+
return null;
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
if (candidate.clearRecovery !== void 0) {
|
|
3314
|
+
const recovery = candidate.clearRecovery;
|
|
3315
|
+
if (typeof recovery !== "object" || recovery === null || recovery.phase !== "clearing" || typeof recovery.embeddingProvider !== "string" || recovery.embeddingProvider.length === 0 || typeof recovery.embeddingModel !== "string" || recovery.embeddingModel.length === 0 || !Number.isInteger(recovery.embeddingDimensions) || (recovery.embeddingDimensions ?? 0) <= 0 || typeof recovery.embeddingStrategyVersion !== "string" || recovery.embeddingStrategyVersion.length === 0 || recovery.compatibilityDecision !== "compatible" && recovery.compatibilityDecision !== "embedding-strategy-mismatch" && recovery.compatibilityDecision !== "incompatible" || candidate.operation !== "clear" && candidate.operation !== "force-index") {
|
|
3316
|
+
return null;
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3284
3319
|
return candidate;
|
|
3285
3320
|
}
|
|
3286
3321
|
function parseReclaimOwner(value) {
|
|
@@ -3521,13 +3556,18 @@ function isTransientIndexLockContention(error) {
|
|
|
3521
3556
|
if (!isIndexLockContentionError(error) || !("reason" in error)) return false;
|
|
3522
3557
|
return error.reason === "active" || error.reason === "reclaiming";
|
|
3523
3558
|
}
|
|
3524
|
-
function acquireIndexLock(indexPath, operation) {
|
|
3559
|
+
function acquireIndexLock(indexPath, operation, recoveryScope) {
|
|
3525
3560
|
mkdirSync(indexPath, { recursive: true });
|
|
3526
3561
|
const canonicalIndexPath = realpathSync2.native(indexPath);
|
|
3527
3562
|
const lockPath = path9.join(canonicalIndexPath, "indexing.lock");
|
|
3528
3563
|
cleanupDeadPublicationCandidates(canonicalIndexPath);
|
|
3529
3564
|
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
3530
|
-
const owner = createOwner(operation)
|
|
3565
|
+
const owner = recoveryScope === void 0 ? createOwner(operation) : {
|
|
3566
|
+
...createOwner(operation),
|
|
3567
|
+
recoveryProtocolVersion: 1,
|
|
3568
|
+
projectRoot: recoveryScope.projectRoot,
|
|
3569
|
+
scopedRoots: recoveryScope.scopedRoots
|
|
3570
|
+
};
|
|
3531
3571
|
if (publishJsonDirectory(lockPath, owner)) {
|
|
3532
3572
|
const lease = {
|
|
3533
3573
|
canonicalIndexPath,
|
|
@@ -3592,6 +3632,33 @@ function releaseIndexLock(lease) {
|
|
|
3592
3632
|
}
|
|
3593
3633
|
return true;
|
|
3594
3634
|
}
|
|
3635
|
+
function setIndexLockClearRecoveryState(lease, clearRecovery) {
|
|
3636
|
+
const currentOwner = readDirectoryOwner(lease.lockPath);
|
|
3637
|
+
if (!currentOwner || !sameOwner(currentOwner, lease.owner)) {
|
|
3638
|
+
throw new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
3639
|
+
}
|
|
3640
|
+
const nextOwner = { ...currentOwner };
|
|
3641
|
+
if (clearRecovery === null) {
|
|
3642
|
+
delete nextOwner.clearRecovery;
|
|
3643
|
+
} else {
|
|
3644
|
+
nextOwner.clearRecovery = clearRecovery;
|
|
3645
|
+
}
|
|
3646
|
+
const ownerPath = path9.join(lease.lockPath, OWNER_FILE_NAME);
|
|
3647
|
+
const temporaryPath = path9.join(
|
|
3648
|
+
lease.lockPath,
|
|
3649
|
+
`${OWNER_FILE_NAME}.tmp.${lease.owner.pid}.${lease.owner.token}.${randomUUID()}`
|
|
3650
|
+
);
|
|
3651
|
+
try {
|
|
3652
|
+
writeFileSync(temporaryPath, JSON.stringify(nextOwner), {
|
|
3653
|
+
encoding: "utf-8",
|
|
3654
|
+
flag: "wx",
|
|
3655
|
+
mode: 384
|
|
3656
|
+
});
|
|
3657
|
+
retryTransientFilesystemOperation(() => renameSync(temporaryPath, ownerPath));
|
|
3658
|
+
} finally {
|
|
3659
|
+
if (existsSync5(temporaryPath)) rmSync(temporaryPath, { force: true });
|
|
3660
|
+
}
|
|
3661
|
+
}
|
|
3595
3662
|
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
3596
3663
|
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
3597
3664
|
temporaryCounter += 1;
|
|
@@ -5735,8 +5802,6 @@ async function tryDetectProvider() {
|
|
|
5735
5802
|
}
|
|
5736
5803
|
async function getProviderCredentials(provider) {
|
|
5737
5804
|
switch (provider) {
|
|
5738
|
-
case "github-copilot":
|
|
5739
|
-
return getGitHubCopilotCredentials();
|
|
5740
5805
|
case "openai":
|
|
5741
5806
|
return getOpenAICredentials();
|
|
5742
5807
|
case "google":
|
|
@@ -5747,22 +5812,6 @@ async function getProviderCredentials(provider) {
|
|
|
5747
5812
|
return null;
|
|
5748
5813
|
}
|
|
5749
5814
|
}
|
|
5750
|
-
function getGitHubCopilotCredentials() {
|
|
5751
|
-
const authData = loadOpenCodeAuth();
|
|
5752
|
-
const copilotAuth = authData["github-copilot"] || authData["github-copilot-enterprise"];
|
|
5753
|
-
if (!copilotAuth || copilotAuth.type !== "oauth") {
|
|
5754
|
-
return null;
|
|
5755
|
-
}
|
|
5756
|
-
const auth = copilotAuth;
|
|
5757
|
-
const baseUrl = auth.enterpriseUrl ? `https://copilot-api.${auth.enterpriseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "")}` : "https://models.github.ai";
|
|
5758
|
-
return {
|
|
5759
|
-
provider: "github-copilot",
|
|
5760
|
-
baseUrl,
|
|
5761
|
-
refreshToken: copilotAuth.refresh,
|
|
5762
|
-
accessToken: copilotAuth.access,
|
|
5763
|
-
tokenExpires: copilotAuth.expires
|
|
5764
|
-
};
|
|
5765
|
-
}
|
|
5766
5815
|
function getOpenAICredentials() {
|
|
5767
5816
|
const authData = loadOpenCodeAuth();
|
|
5768
5817
|
const openaiAuth = authData["openai"];
|
|
@@ -5888,8 +5937,6 @@ async function tryDetectOllamaProvider() {
|
|
|
5888
5937
|
}
|
|
5889
5938
|
function getProviderDisplayName(provider) {
|
|
5890
5939
|
switch (provider) {
|
|
5891
|
-
case "github-copilot":
|
|
5892
|
-
return "GitHub Copilot";
|
|
5893
5940
|
case "openai":
|
|
5894
5941
|
return "OpenAI";
|
|
5895
5942
|
case "google":
|
|
@@ -6114,44 +6161,6 @@ var CustomEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
|
6114
6161
|
}
|
|
6115
6162
|
};
|
|
6116
6163
|
|
|
6117
|
-
// src/embeddings/providers/github-copilot.ts
|
|
6118
|
-
var GitHubCopilotEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
6119
|
-
constructor(credentials, modelInfo) {
|
|
6120
|
-
super(credentials, modelInfo);
|
|
6121
|
-
}
|
|
6122
|
-
getToken() {
|
|
6123
|
-
if (!this.credentials.refreshToken) {
|
|
6124
|
-
throw new Error("No OAuth token available for GitHub");
|
|
6125
|
-
}
|
|
6126
|
-
return this.credentials.refreshToken;
|
|
6127
|
-
}
|
|
6128
|
-
async embedBatch(texts) {
|
|
6129
|
-
const token = this.getToken();
|
|
6130
|
-
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
6131
|
-
method: "POST",
|
|
6132
|
-
headers: {
|
|
6133
|
-
Authorization: `Bearer ${token}`,
|
|
6134
|
-
"Content-Type": "application/json",
|
|
6135
|
-
Accept: "application/vnd.github+json",
|
|
6136
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
6137
|
-
},
|
|
6138
|
-
body: JSON.stringify({
|
|
6139
|
-
model: `openai/${this.modelInfo.model}`,
|
|
6140
|
-
input: texts
|
|
6141
|
-
})
|
|
6142
|
-
});
|
|
6143
|
-
if (!response.ok) {
|
|
6144
|
-
const error = (await response.text()).slice(0, 500);
|
|
6145
|
-
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
6146
|
-
}
|
|
6147
|
-
const data = await response.json();
|
|
6148
|
-
return {
|
|
6149
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
6150
|
-
totalTokensUsed: data.usage.total_tokens
|
|
6151
|
-
};
|
|
6152
|
-
}
|
|
6153
|
-
};
|
|
6154
|
-
|
|
6155
6164
|
// src/embeddings/providers/google.ts
|
|
6156
6165
|
var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddingProvider {
|
|
6157
6166
|
static BATCH_SIZE = 20;
|
|
@@ -6159,24 +6168,30 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
6159
6168
|
super(credentials, modelInfo);
|
|
6160
6169
|
}
|
|
6161
6170
|
async embedQuery(query) {
|
|
6162
|
-
const taskType = this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
6163
|
-
const
|
|
6171
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
6172
|
+
const texts = [
|
|
6173
|
+
this.modelInfo.model === "gemini-embedding-2" ? `task: code retrieval | query: ${query}` : query
|
|
6174
|
+
];
|
|
6175
|
+
const result = await this.embedWithTaskType(texts, taskType);
|
|
6164
6176
|
return {
|
|
6165
6177
|
embedding: result.embeddings[0],
|
|
6166
6178
|
tokensUsed: result.totalTokensUsed
|
|
6167
6179
|
};
|
|
6168
6180
|
}
|
|
6169
6181
|
async embedDocument(document) {
|
|
6170
|
-
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6171
|
-
const result = await this.embedWithTaskType([
|
|
6182
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6183
|
+
const result = await this.embedWithTaskType([
|
|
6184
|
+
this.modelInfo.model === "gemini-embedding-2" ? `title: none | text: ${document}` : document
|
|
6185
|
+
], taskType);
|
|
6172
6186
|
return {
|
|
6173
6187
|
embedding: result.embeddings[0],
|
|
6174
6188
|
tokensUsed: result.totalTokensUsed
|
|
6175
6189
|
};
|
|
6176
6190
|
}
|
|
6177
6191
|
async embedBatch(texts) {
|
|
6178
|
-
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6179
|
-
|
|
6192
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6193
|
+
const formattedTexts = this.modelInfo.model === "gemini-embedding-2" ? texts.map((text) => `title: none | text: ${text}`) : texts;
|
|
6194
|
+
return this.embedWithTaskType(formattedTexts, taskType);
|
|
6180
6195
|
}
|
|
6181
6196
|
async embedWithTaskType(texts, taskType) {
|
|
6182
6197
|
const batches = [];
|
|
@@ -6226,6 +6241,10 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
6226
6241
|
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
|
|
6227
6242
|
static MIN_TRUNCATION_CHARS = 512;
|
|
6228
6243
|
static REQUEST_TIMEOUT_MS = 12e4;
|
|
6244
|
+
// Set when /api/embed returns 404 so subsequent multi-text batches skip the
|
|
6245
|
+
// batched endpoint and go straight to the legacy per-text path (one probe per
|
|
6246
|
+
// old ollama install, not one probe per batch).
|
|
6247
|
+
batchEndpointUnavailable = false;
|
|
6229
6248
|
constructor(credentials, modelInfo) {
|
|
6230
6249
|
super(credentials, modelInfo);
|
|
6231
6250
|
}
|
|
@@ -6243,6 +6262,21 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
6243
6262
|
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
6244
6263
|
return message.includes("context length") && (message.includes("exceed") || message.includes("exceeded") || message.includes("too long")) || message.includes("input length exceeds the context length") || message.includes("context length exceeded");
|
|
6245
6264
|
}
|
|
6265
|
+
// True for a 404 from the newer /api/embed endpoint, i.e. an ollama version that
|
|
6266
|
+
// does not provide it. embedBatch uses this to fall back to the legacy per-text
|
|
6267
|
+
// /api/embeddings path so old ollama installs do not regress.
|
|
6268
|
+
isBatchEndpointUnavailableError(error) {
|
|
6269
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6270
|
+
return message.includes("Ollama /api/embed not available");
|
|
6271
|
+
}
|
|
6272
|
+
// True for a malformed /api/embed response (wrong vector count or a bad vector).
|
|
6273
|
+
// embedBatch falls back to the per-text path on this so a bad batch response
|
|
6274
|
+
// re-embeds each text cleanly. A text that then fails per-text is not isolated
|
|
6275
|
+
// here; it is isolated on the recovery run, which re-embeds one text per request.
|
|
6276
|
+
isBatchValidationError(error) {
|
|
6277
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6278
|
+
return message.includes("invalid embedding batch");
|
|
6279
|
+
}
|
|
6246
6280
|
buildTruncationCandidates(text) {
|
|
6247
6281
|
const baseMaxChars = Math.max(1, this.modelInfo.maxTokens * 4);
|
|
6248
6282
|
const candidateLimits = /* @__PURE__ */ new Set();
|
|
@@ -6344,7 +6378,74 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
6344
6378
|
tokensUsed: this.estimateTokens(text)
|
|
6345
6379
|
};
|
|
6346
6380
|
}
|
|
6347
|
-
|
|
6381
|
+
// Embeds many texts in one POST /api/embed request (input: string[]). Ollama
|
|
6382
|
+
// encodes each input independently, so the model context length applies per input
|
|
6383
|
+
// (the upstream splitter already bounds each input), not over the batch. This
|
|
6384
|
+
// amortizes N HTTP round-trips into one.
|
|
6385
|
+
async embedMany(texts) {
|
|
6386
|
+
const controller = new AbortController();
|
|
6387
|
+
const timeout = setTimeout(
|
|
6388
|
+
() => controller.abort(),
|
|
6389
|
+
_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS
|
|
6390
|
+
);
|
|
6391
|
+
let response;
|
|
6392
|
+
try {
|
|
6393
|
+
response = await fetch(`${this.credentials.baseUrl}/api/embed`, {
|
|
6394
|
+
method: "POST",
|
|
6395
|
+
headers: {
|
|
6396
|
+
"Content-Type": "application/json"
|
|
6397
|
+
},
|
|
6398
|
+
body: JSON.stringify({
|
|
6399
|
+
model: this.modelInfo.model,
|
|
6400
|
+
input: texts,
|
|
6401
|
+
truncate: false
|
|
6402
|
+
}),
|
|
6403
|
+
signal: controller.signal
|
|
6404
|
+
});
|
|
6405
|
+
} catch (error) {
|
|
6406
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
6407
|
+
throw new Error(
|
|
6408
|
+
`Ollama embedding request timed out after ${_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS}ms`
|
|
6409
|
+
);
|
|
6410
|
+
}
|
|
6411
|
+
throw error;
|
|
6412
|
+
} finally {
|
|
6413
|
+
clearTimeout(timeout);
|
|
6414
|
+
}
|
|
6415
|
+
if (!response.ok) {
|
|
6416
|
+
const error = (await response.text()).slice(0, 500);
|
|
6417
|
+
if (response.status === 404) {
|
|
6418
|
+
throw new Error(`Ollama /api/embed not available: ${response.status} - ${error}`);
|
|
6419
|
+
}
|
|
6420
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
6421
|
+
}
|
|
6422
|
+
let parsed;
|
|
6423
|
+
try {
|
|
6424
|
+
parsed = await response.json();
|
|
6425
|
+
} catch {
|
|
6426
|
+
throw new Error(
|
|
6427
|
+
`Ollama returned an invalid embedding batch; expected ${texts.length} vectors of ${this.modelInfo.dimensions} finite dimensions`
|
|
6428
|
+
);
|
|
6429
|
+
}
|
|
6430
|
+
const data = parsed && typeof parsed === "object" ? parsed : {};
|
|
6431
|
+
if (!Array.isArray(data.embeddings) || data.embeddings.length !== texts.length || data.embeddings.some(
|
|
6432
|
+
(value) => !Array.isArray(value) || value.length !== this.modelInfo.dimensions || value.some((v) => typeof v !== "number" || !Number.isFinite(v))
|
|
6433
|
+
)) {
|
|
6434
|
+
throw new Error(
|
|
6435
|
+
`Ollama returned an invalid embedding batch; expected ${texts.length} vectors of ${this.modelInfo.dimensions} finite dimensions`
|
|
6436
|
+
);
|
|
6437
|
+
}
|
|
6438
|
+
return {
|
|
6439
|
+
embeddings: data.embeddings,
|
|
6440
|
+
totalTokensUsed: texts.reduce((sum, text) => sum + this.estimateTokens(text), 0)
|
|
6441
|
+
};
|
|
6442
|
+
}
|
|
6443
|
+
// Per-text /api/embeddings path shared by the single-text fast path and the
|
|
6444
|
+
// batch fallback. Uses the legacy endpoint one text at a time, so each text gets
|
|
6445
|
+
// its own truncation safety net and a vector validated on its own. A text that
|
|
6446
|
+
// hard-fails per-text throws here and fails the whole request batch; the recovery
|
|
6447
|
+
// run re-embeds one text per request to isolate it.
|
|
6448
|
+
async embedOneByOne(texts) {
|
|
6348
6449
|
const results = [];
|
|
6349
6450
|
for (const text of texts) {
|
|
6350
6451
|
results.push(await this.embedSingleWithFallback(text));
|
|
@@ -6354,6 +6455,26 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
6354
6455
|
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
6355
6456
|
};
|
|
6356
6457
|
}
|
|
6458
|
+
async embedBatch(texts) {
|
|
6459
|
+
if (texts.length === 0) {
|
|
6460
|
+
return { embeddings: [], totalTokensUsed: 0 };
|
|
6461
|
+
}
|
|
6462
|
+
if (texts.length === 1 || this.batchEndpointUnavailable) {
|
|
6463
|
+
return this.embedOneByOne(texts);
|
|
6464
|
+
}
|
|
6465
|
+
try {
|
|
6466
|
+
return await this.embedMany(texts);
|
|
6467
|
+
} catch (error) {
|
|
6468
|
+
if (this.isBatchEndpointUnavailableError(error)) {
|
|
6469
|
+
this.batchEndpointUnavailable = true;
|
|
6470
|
+
return this.embedOneByOne(texts);
|
|
6471
|
+
}
|
|
6472
|
+
if (!this.isContextLengthError(error) && !this.isBatchValidationError(error)) {
|
|
6473
|
+
throw error;
|
|
6474
|
+
}
|
|
6475
|
+
return this.embedOneByOne(texts);
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6357
6478
|
};
|
|
6358
6479
|
|
|
6359
6480
|
// src/embeddings/providers/openai.ts
|
|
@@ -6388,8 +6509,6 @@ var OpenAIEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
|
6388
6509
|
// src/embeddings/provider.ts
|
|
6389
6510
|
function createEmbeddingProvider(configuredProviderInfo) {
|
|
6390
6511
|
switch (configuredProviderInfo.provider) {
|
|
6391
|
-
case "github-copilot":
|
|
6392
|
-
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
6393
6512
|
case "openai":
|
|
6394
6513
|
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
6395
6514
|
case "google":
|
|
@@ -7140,12 +7259,12 @@ try {
|
|
|
7140
7259
|
}
|
|
7141
7260
|
|
|
7142
7261
|
// src/native/parsing.ts
|
|
7143
|
-
function parseFileAsText(filePath, content) {
|
|
7144
|
-
const result = native.parseFileAsText(filePath, content);
|
|
7262
|
+
function parseFileAsText(filePath, content, linesPerChunk) {
|
|
7263
|
+
const result = native.parseFileAsText(filePath, content, linesPerChunk);
|
|
7145
7264
|
return result.map(mapChunk);
|
|
7146
7265
|
}
|
|
7147
|
-
function parseFiles(files) {
|
|
7148
|
-
const result = native.parseFiles(files);
|
|
7266
|
+
function parseFiles(files, linesPerChunk) {
|
|
7267
|
+
const result = native.parseFiles(files, linesPerChunk);
|
|
7149
7268
|
return result.map((f) => ({
|
|
7150
7269
|
path: f.path,
|
|
7151
7270
|
chunks: f.chunks.map(mapChunk),
|
|
@@ -7222,13 +7341,13 @@ var VectorStore = class {
|
|
|
7222
7341
|
const metadata = items.map((i) => JSON.stringify(i.metadata));
|
|
7223
7342
|
this.inner.addBatch(ids, vectors, metadata);
|
|
7224
7343
|
}
|
|
7225
|
-
search(queryVector, limit = 10) {
|
|
7344
|
+
search(queryVector, limit = 10, allowedIds) {
|
|
7226
7345
|
if (queryVector.length !== this.dimensions) {
|
|
7227
7346
|
throw new Error(
|
|
7228
7347
|
`Query vector dimension mismatch: expected ${this.dimensions}, got ${queryVector.length}`
|
|
7229
7348
|
);
|
|
7230
7349
|
}
|
|
7231
|
-
const results = this.inner.search(queryVector, limit);
|
|
7350
|
+
const results = allowedIds === void 0 ? this.inner.search(queryVector, limit) : this.inner.searchFiltered(queryVector, limit, allowedIds);
|
|
7232
7351
|
return results.map((r) => ({
|
|
7233
7352
|
id: r.id,
|
|
7234
7353
|
score: r.score,
|
|
@@ -7456,6 +7575,10 @@ var Database = class _Database {
|
|
|
7456
7575
|
this.throwIfClosed();
|
|
7457
7576
|
return this.inner.getBranchChunkIds(branch);
|
|
7458
7577
|
}
|
|
7578
|
+
getChunkIdsByBlameDate(since, until) {
|
|
7579
|
+
this.throwIfClosed();
|
|
7580
|
+
return this.inner.getChunkIdsByBlameDate(since, until);
|
|
7581
|
+
}
|
|
7459
7582
|
getBranchDelta(branch, baseBranch) {
|
|
7460
7583
|
this.throwIfClosed();
|
|
7461
7584
|
return this.inner.getBranchDelta(branch, baseBranch);
|
|
@@ -9344,6 +9467,18 @@ function createFailedBatchWriter(targetPath) {
|
|
|
9344
9467
|
temporaryPath
|
|
9345
9468
|
};
|
|
9346
9469
|
}
|
|
9470
|
+
function writeFailedBatchRecords(targetPath, records) {
|
|
9471
|
+
const writer = createFailedBatchWriter(targetPath);
|
|
9472
|
+
try {
|
|
9473
|
+
for (const record of records) {
|
|
9474
|
+
writer.write(record);
|
|
9475
|
+
}
|
|
9476
|
+
writer.commit();
|
|
9477
|
+
} catch (error) {
|
|
9478
|
+
writer.cleanup();
|
|
9479
|
+
throw error;
|
|
9480
|
+
}
|
|
9481
|
+
}
|
|
9347
9482
|
function* readLegacyFailedBatchRecords(filePath, options) {
|
|
9348
9483
|
const rawData = fs2.readFileSync(filePath, "utf-8");
|
|
9349
9484
|
const trimmed = stripLeadingBomAndWhitespace(rawData).trim();
|
|
@@ -9626,14 +9761,18 @@ function getSafeEmbeddingChunkTokenLimit(provider) {
|
|
|
9626
9761
|
const maxChunkTokens = Math.max(256, Math.floor(providerMaxTokens * 0.75));
|
|
9627
9762
|
return Math.min(2e3, maxChunkTokens);
|
|
9628
9763
|
}
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
};
|
|
9764
|
+
var DEFAULT_OLLAMA_MAX_BATCH_ITEMS = 16;
|
|
9765
|
+
var DEFAULT_OLLAMA_MAX_BATCH_TOKENS = 65536;
|
|
9766
|
+
function getDynamicBatchOptions(provider, embeddingBatch) {
|
|
9767
|
+
if (provider.provider !== "ollama") {
|
|
9768
|
+
return {};
|
|
9635
9769
|
}
|
|
9636
|
-
|
|
9770
|
+
const base = { maxBatchTokens: DEFAULT_OLLAMA_MAX_BATCH_TOKENS, maxBatchItems: DEFAULT_OLLAMA_MAX_BATCH_ITEMS };
|
|
9771
|
+
return {
|
|
9772
|
+
...base,
|
|
9773
|
+
...typeof embeddingBatch?.maxBatchTokens === "number" && Number.isFinite(embeddingBatch.maxBatchTokens) ? { maxBatchTokens: embeddingBatch.maxBatchTokens } : {},
|
|
9774
|
+
...typeof embeddingBatch?.maxBatchItems === "number" && Number.isFinite(embeddingBatch.maxBatchItems) ? { maxBatchItems: embeddingBatch.maxBatchItems } : {}
|
|
9775
|
+
};
|
|
9637
9776
|
}
|
|
9638
9777
|
function isSqliteCorruptionError(error) {
|
|
9639
9778
|
const message = getErrorMessage4(error).toLowerCase();
|
|
@@ -9651,6 +9790,14 @@ function getPendingChunkId(rawChunk) {
|
|
|
9651
9790
|
const id = rawChunk.id;
|
|
9652
9791
|
return typeof id === "string" ? id : null;
|
|
9653
9792
|
}
|
|
9793
|
+
function parseBlameTimestamp(value, endOfDay) {
|
|
9794
|
+
let timestampMs = Date.parse(value);
|
|
9795
|
+
if (Number.isNaN(timestampMs)) return null;
|
|
9796
|
+
if (endOfDay && /^\d{4}-\d{2}-\d{2}$/.test(value.trim())) {
|
|
9797
|
+
timestampMs += 24 * 60 * 60 * 1e3 - 1;
|
|
9798
|
+
}
|
|
9799
|
+
return Math.floor(timestampMs / 1e3);
|
|
9800
|
+
}
|
|
9654
9801
|
function metadataFromBlame(blame) {
|
|
9655
9802
|
if (!blame) {
|
|
9656
9803
|
return {};
|
|
@@ -9797,7 +9944,7 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
|
|
|
9797
9944
|
const remainder = combined.filter((candidate) => !promotedIds.has(candidate.id));
|
|
9798
9945
|
return [...promoted, ...remainder];
|
|
9799
9946
|
}
|
|
9800
|
-
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
9947
|
+
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source", allowNonSourcePaths = false) {
|
|
9801
9948
|
if (!prioritizeSourcePaths) {
|
|
9802
9949
|
return [];
|
|
9803
9950
|
}
|
|
@@ -9817,7 +9964,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
9817
9964
|
if (!isImplementationChunkType(chunkType)) {
|
|
9818
9965
|
return false;
|
|
9819
9966
|
}
|
|
9820
|
-
if (!isLikelyImplementationPath2(chunk.filePath)) {
|
|
9967
|
+
if (!allowNonSourcePaths && !isLikelyImplementationPath2(chunk.filePath)) {
|
|
9821
9968
|
return false;
|
|
9822
9969
|
}
|
|
9823
9970
|
const nameLower = (chunk.name ?? "").toLowerCase();
|
|
@@ -9881,7 +10028,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
9881
10028
|
}
|
|
9882
10029
|
foundCoveringChunk = upsertChunkCandidate(chunk, identifier, normalizedIdentifier) || foundCoveringChunk;
|
|
9883
10030
|
}
|
|
9884
|
-
if (foundCoveringChunk || !isLikelyImplementationPath2(symbol.filePath)) {
|
|
10031
|
+
if (foundCoveringChunk || !allowNonSourcePaths && !isLikelyImplementationPath2(symbol.filePath)) {
|
|
9885
10032
|
continue;
|
|
9886
10033
|
}
|
|
9887
10034
|
const symbolName = symbol.name.toLowerCase();
|
|
@@ -9935,7 +10082,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
9935
10082
|
const ranked = Array.from(symbolCandidates.values()).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
|
|
9936
10083
|
if (ranked.length === 0) {
|
|
9937
10084
|
const implementationFallback = fallbackCandidates.filter(
|
|
9938
|
-
(candidate) => isImplementationChunkType(candidate.metadata.chunkType) && isLikelyImplementationPath2(candidate.metadata.filePath)
|
|
10085
|
+
(candidate) => isImplementationChunkType(candidate.metadata.chunkType) && (allowNonSourcePaths || isLikelyImplementationPath2(candidate.metadata.filePath))
|
|
9939
10086
|
);
|
|
9940
10087
|
for (const candidate of implementationFallback) {
|
|
9941
10088
|
const nameLower = (candidate.metadata.name ?? "").toLowerCase();
|
|
@@ -10051,10 +10198,16 @@ function matchesHardSearchFilters(candidate, options, projectRoot) {
|
|
|
10051
10198
|
return false;
|
|
10052
10199
|
}
|
|
10053
10200
|
if (options?.blameSince) {
|
|
10054
|
-
const
|
|
10055
|
-
if (
|
|
10201
|
+
const since = parseBlameTimestamp(options.blameSince, false);
|
|
10202
|
+
if (since === null) return false;
|
|
10203
|
+
const committedAt = candidate.metadata.blameCommittedAt;
|
|
10204
|
+
if (committedAt === void 0 || committedAt < since) return false;
|
|
10205
|
+
}
|
|
10206
|
+
if (options?.blameUntil) {
|
|
10207
|
+
const until = parseBlameTimestamp(options.blameUntil, true);
|
|
10208
|
+
if (until === null) return false;
|
|
10056
10209
|
const committedAt = candidate.metadata.blameCommittedAt;
|
|
10057
|
-
if (committedAt === void 0 || committedAt
|
|
10210
|
+
if (committedAt === void 0 || committedAt > until) return false;
|
|
10058
10211
|
}
|
|
10059
10212
|
return true;
|
|
10060
10213
|
}
|
|
@@ -10110,9 +10263,10 @@ var Indexer = class _Indexer {
|
|
|
10110
10263
|
writerArtifactFingerprint = null;
|
|
10111
10264
|
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
10112
10265
|
fileBatchLimits;
|
|
10266
|
+
checkpointIntervalChunks;
|
|
10113
10267
|
constructor(projectRoot, config, host, runtimeOptions = {}) {
|
|
10114
10268
|
this.projectRoot = projectRoot;
|
|
10115
|
-
this.projectIdentityHash =
|
|
10269
|
+
this.projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
10116
10270
|
this.materializedProjectRoot = runtimeOptions.materializedProjectRoot ?? projectRoot;
|
|
10117
10271
|
this.branchNameOverride = runtimeOptions.branchName;
|
|
10118
10272
|
this.catalogIdentityOverride = runtimeOptions.catalogIdentity;
|
|
@@ -10122,6 +10276,7 @@ var Indexer = class _Indexer {
|
|
|
10122
10276
|
this.expectedCommitOverride = runtimeOptions.expectedCommit?.toLowerCase();
|
|
10123
10277
|
this.indexPathOverride = runtimeOptions.indexPath;
|
|
10124
10278
|
this.fileBatchLimits = runtimeOptions.fileBatchLimits;
|
|
10279
|
+
this.checkpointIntervalChunks = runtimeOptions.checkpointIntervalChunks;
|
|
10125
10280
|
this.config = config;
|
|
10126
10281
|
this.host = host;
|
|
10127
10282
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -10233,6 +10388,9 @@ var Indexer = class _Indexer {
|
|
|
10233
10388
|
return path19.resolve(targetPath);
|
|
10234
10389
|
}
|
|
10235
10390
|
}
|
|
10391
|
+
getProjectIdentityHash(projectRoot) {
|
|
10392
|
+
return hashContent(this.getCanonicalPath(projectRoot)).slice(0, 16);
|
|
10393
|
+
}
|
|
10236
10394
|
isProjectOwnedIndexPath() {
|
|
10237
10395
|
return isProjectIndexPathOwnedByProject(this.projectRoot, this.indexPath, this.host);
|
|
10238
10396
|
}
|
|
@@ -10269,7 +10427,10 @@ var Indexer = class _Indexer {
|
|
|
10269
10427
|
}
|
|
10270
10428
|
async withIndexMutationLease(operation, callback) {
|
|
10271
10429
|
this.refreshBranchInfo();
|
|
10272
|
-
const lease = acquireIndexLock(this.indexPath, operation
|
|
10430
|
+
const lease = acquireIndexLock(this.indexPath, operation, {
|
|
10431
|
+
projectRoot: this.projectRoot,
|
|
10432
|
+
scopedRoots: this.getScopedRoots()
|
|
10433
|
+
});
|
|
10273
10434
|
this.indexPath = lease.canonicalIndexPath;
|
|
10274
10435
|
this.refreshRuntimeArtifactPaths();
|
|
10275
10436
|
this.activeIndexLease = lease;
|
|
@@ -10324,6 +10485,7 @@ var Indexer = class _Indexer {
|
|
|
10324
10485
|
}
|
|
10325
10486
|
loadFileHashCache() {
|
|
10326
10487
|
if (!existsSync11(this.fileHashCachePath)) {
|
|
10488
|
+
this.fileHashCache = /* @__PURE__ */ new Map();
|
|
10327
10489
|
return;
|
|
10328
10490
|
}
|
|
10329
10491
|
try {
|
|
@@ -10363,10 +10525,10 @@ var Indexer = class _Indexer {
|
|
|
10363
10525
|
invertedIndex.serialize()
|
|
10364
10526
|
);
|
|
10365
10527
|
}
|
|
10366
|
-
getScopedRoots() {
|
|
10367
|
-
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(
|
|
10528
|
+
getScopedRoots(projectRoot = this.projectRoot) {
|
|
10529
|
+
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(projectRoot)]);
|
|
10368
10530
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
10369
|
-
roots.add(this.getCanonicalPath(path19.resolve(
|
|
10531
|
+
roots.add(this.getCanonicalPath(path19.resolve(projectRoot, kbRoot)));
|
|
10370
10532
|
}
|
|
10371
10533
|
return Array.from(roots);
|
|
10372
10534
|
}
|
|
@@ -10437,14 +10599,17 @@ var Indexer = class _Indexer {
|
|
|
10437
10599
|
getLegacyBranchCatalogKey() {
|
|
10438
10600
|
return this.currentBranch || "default";
|
|
10439
10601
|
}
|
|
10440
|
-
getLegacyMigrationMetadataKey() {
|
|
10441
|
-
return `index.globalBranchMigration.${
|
|
10602
|
+
getLegacyMigrationMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10603
|
+
return `index.globalBranchMigration.${projectIdentityHash}`;
|
|
10604
|
+
}
|
|
10605
|
+
getProjectEmbeddingStrategyMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10606
|
+
return `index.embeddingStrategyVersion.${projectIdentityHash}`;
|
|
10442
10607
|
}
|
|
10443
|
-
|
|
10444
|
-
return `index.
|
|
10608
|
+
getProjectForceReembedMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10609
|
+
return `index.forceReembed.${projectIdentityHash}`;
|
|
10445
10610
|
}
|
|
10446
|
-
|
|
10447
|
-
return `index.
|
|
10611
|
+
getProjectMigrationFinalizedMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10612
|
+
return `index.migrationFinalized.${projectIdentityHash}`;
|
|
10448
10613
|
}
|
|
10449
10614
|
getBranchMigrationMetadataKey(prefix, catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
10450
10615
|
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
@@ -10550,7 +10715,7 @@ var Indexer = class _Indexer {
|
|
|
10550
10715
|
const legacy = this.getLegacyBranchCatalogKey();
|
|
10551
10716
|
return primary === legacy ? [primary] : [primary, legacy];
|
|
10552
10717
|
}
|
|
10553
|
-
getProjectLocalScopedOwnershipIds(roots) {
|
|
10718
|
+
getProjectLocalScopedOwnershipIds(roots, projectRoot = this.projectRoot) {
|
|
10554
10719
|
const chunkIds = /* @__PURE__ */ new Set();
|
|
10555
10720
|
const symbolIds = /* @__PURE__ */ new Set();
|
|
10556
10721
|
if (!this.database) {
|
|
@@ -10558,10 +10723,10 @@ var Indexer = class _Indexer {
|
|
|
10558
10723
|
}
|
|
10559
10724
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
10560
10725
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
10561
|
-
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath)
|
|
10726
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath, projectRoot)
|
|
10562
10727
|
),
|
|
10563
10728
|
...(this.store?.getAllMetadata() ?? []).map(({ metadata }) => metadata.filePath).filter(
|
|
10564
|
-
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath)
|
|
10729
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath, projectRoot)
|
|
10565
10730
|
)
|
|
10566
10731
|
]);
|
|
10567
10732
|
for (const filePath of projectLocalFilePaths) {
|
|
@@ -10574,15 +10739,16 @@ var Indexer = class _Indexer {
|
|
|
10574
10739
|
}
|
|
10575
10740
|
return { chunkIds, symbolIds };
|
|
10576
10741
|
}
|
|
10577
|
-
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds) {
|
|
10742
|
+
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds, projectRoot = this.projectRoot) {
|
|
10578
10743
|
if (this.config.scope !== "global") {
|
|
10579
10744
|
return this.getBranchCatalogCleanupKeys();
|
|
10580
10745
|
}
|
|
10581
10746
|
const keys = /* @__PURE__ */ new Set();
|
|
10582
10747
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
10583
10748
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
10749
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
10584
10750
|
for (const branchKey of this.database?.getAllBranches() ?? []) {
|
|
10585
|
-
if (branchKey.startsWith(`${
|
|
10751
|
+
if (branchKey.startsWith(`${projectIdentityHash}:`)) {
|
|
10586
10752
|
keys.add(branchKey);
|
|
10587
10753
|
continue;
|
|
10588
10754
|
}
|
|
@@ -10592,8 +10758,10 @@ var Indexer = class _Indexer {
|
|
|
10592
10758
|
keys.add(branchKey);
|
|
10593
10759
|
}
|
|
10594
10760
|
}
|
|
10595
|
-
|
|
10596
|
-
|
|
10761
|
+
if (projectRoot === this.projectRoot) {
|
|
10762
|
+
for (const branchKey of this.getBranchCatalogCleanupKeys()) {
|
|
10763
|
+
keys.add(branchKey);
|
|
10764
|
+
}
|
|
10597
10765
|
}
|
|
10598
10766
|
return Array.from(keys);
|
|
10599
10767
|
}
|
|
@@ -10601,10 +10769,10 @@ var Indexer = class _Indexer {
|
|
|
10601
10769
|
const canonicalFilePath = this.getCanonicalStoredFilePath(filePath);
|
|
10602
10770
|
return roots.some((root) => isPathWithinRoot2(canonicalFilePath, root));
|
|
10603
10771
|
}
|
|
10604
|
-
isFileInProjectRoot(filePath) {
|
|
10772
|
+
isFileInProjectRoot(filePath, projectRoot = this.projectRoot) {
|
|
10605
10773
|
return isPathWithinRoot2(
|
|
10606
10774
|
this.getCanonicalStoredFilePath(filePath),
|
|
10607
|
-
this.getCanonicalPath(
|
|
10775
|
+
this.getCanonicalPath(projectRoot)
|
|
10608
10776
|
);
|
|
10609
10777
|
}
|
|
10610
10778
|
clearScopedFileHashCache(roots) {
|
|
@@ -10646,12 +10814,12 @@ var Indexer = class _Indexer {
|
|
|
10646
10814
|
}
|
|
10647
10815
|
return false;
|
|
10648
10816
|
}
|
|
10649
|
-
hasForeignScopedBranchData() {
|
|
10817
|
+
hasForeignScopedBranchData(projectRoot = this.projectRoot, roots = this.getScopedRoots(projectRoot)) {
|
|
10650
10818
|
if (!this.database || this.config.scope !== "global") {
|
|
10651
10819
|
return false;
|
|
10652
10820
|
}
|
|
10653
|
-
const
|
|
10654
|
-
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
10821
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
10822
|
+
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots, projectRoot);
|
|
10655
10823
|
return this.database.getAllBranches().some(
|
|
10656
10824
|
(branchKey) => {
|
|
10657
10825
|
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
@@ -10660,7 +10828,7 @@ var Indexer = class _Indexer {
|
|
|
10660
10828
|
if (!hasBranchData) {
|
|
10661
10829
|
return false;
|
|
10662
10830
|
}
|
|
10663
|
-
if (branchKey.startsWith(`${
|
|
10831
|
+
if (branchKey.startsWith(`${projectIdentityHash}:`)) {
|
|
10664
10832
|
return false;
|
|
10665
10833
|
}
|
|
10666
10834
|
const referencesCurrentProjectChunks = branchChunkIds.some((chunkId) => projectLocalChunkIds.has(chunkId));
|
|
@@ -10669,7 +10837,7 @@ var Indexer = class _Indexer {
|
|
|
10669
10837
|
}
|
|
10670
10838
|
);
|
|
10671
10839
|
}
|
|
10672
|
-
clearSharedIndexProjectData(store, invertedIndex, database, roots) {
|
|
10840
|
+
clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot = this.projectRoot) {
|
|
10673
10841
|
const allMetadata = store.getAllMetadata();
|
|
10674
10842
|
const scopedEntries = allMetadata.filter(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
10675
10843
|
const filePaths = /* @__PURE__ */ new Set([
|
|
@@ -10677,7 +10845,7 @@ var Indexer = class _Indexer {
|
|
|
10677
10845
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
10678
10846
|
]);
|
|
10679
10847
|
const projectLocalFilePaths = new Set(
|
|
10680
|
-
Array.from(filePaths).filter((filePath) => this.isFileInProjectRoot(filePath))
|
|
10848
|
+
Array.from(filePaths).filter((filePath) => this.isFileInProjectRoot(filePath, projectRoot))
|
|
10681
10849
|
);
|
|
10682
10850
|
const removedChunkIds = new Set(scopedEntries.map(({ key }) => key));
|
|
10683
10851
|
for (const filePath of filePaths) {
|
|
@@ -10687,7 +10855,7 @@ var Indexer = class _Indexer {
|
|
|
10687
10855
|
}
|
|
10688
10856
|
const removedChunkIdList = Array.from(removedChunkIds);
|
|
10689
10857
|
const projectLocalChunkIds = new Set(
|
|
10690
|
-
scopedEntries.filter(({ metadata }) => this.isFileInProjectRoot(metadata.filePath)).map(({ key }) => key)
|
|
10858
|
+
scopedEntries.filter(({ metadata }) => this.isFileInProjectRoot(metadata.filePath, projectRoot)).map(({ key }) => key)
|
|
10691
10859
|
);
|
|
10692
10860
|
for (const filePath of projectLocalFilePaths) {
|
|
10693
10861
|
for (const chunk of database.getChunksByFile(filePath)) {
|
|
@@ -10706,7 +10874,8 @@ var Indexer = class _Indexer {
|
|
|
10706
10874
|
}
|
|
10707
10875
|
const branchCleanupKeys = this.getProjectScopedBranchCatalogCleanupKeys(
|
|
10708
10876
|
Array.from(projectLocalChunkIds),
|
|
10709
|
-
Array.from(projectLocalSymbolIds)
|
|
10877
|
+
Array.from(projectLocalSymbolIds),
|
|
10878
|
+
projectRoot
|
|
10710
10879
|
);
|
|
10711
10880
|
for (const branchKey of branchCleanupKeys) {
|
|
10712
10881
|
database.deleteBranchChunksForBranch(branchKey, removedChunkIdList);
|
|
@@ -10741,29 +10910,96 @@ var Indexer = class _Indexer {
|
|
|
10741
10910
|
database.gcOrphanSymbols();
|
|
10742
10911
|
database.gcOrphanEmbeddings();
|
|
10743
10912
|
database.gcOrphanChunks();
|
|
10744
|
-
store.save();
|
|
10745
10913
|
this.saveInvertedIndex(invertedIndex);
|
|
10914
|
+
store.save();
|
|
10746
10915
|
return {
|
|
10747
10916
|
removedChunkIds: removedChunkIdList,
|
|
10748
10917
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
10749
10918
|
};
|
|
10750
10919
|
}
|
|
10920
|
+
getCurrentClearRecoveryState() {
|
|
10921
|
+
if (!this.configuredProviderInfo) {
|
|
10922
|
+
throw new Error("Cannot persist clear recovery state before the embedding provider is initialized");
|
|
10923
|
+
}
|
|
10924
|
+
const compatibility = this.checkCompatibility();
|
|
10925
|
+
const compatibilityDecision = compatibility.compatible ? "compatible" : compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */ ? "embedding-strategy-mismatch" : "incompatible";
|
|
10926
|
+
return {
|
|
10927
|
+
phase: "clearing",
|
|
10928
|
+
embeddingProvider: this.configuredProviderInfo.provider,
|
|
10929
|
+
embeddingModel: this.configuredProviderInfo.modelInfo.model,
|
|
10930
|
+
embeddingDimensions: this.configuredProviderInfo.modelInfo.dimensions,
|
|
10931
|
+
embeddingStrategyVersion: EMBEDDING_STRATEGY_VERSION,
|
|
10932
|
+
compatibilityDecision
|
|
10933
|
+
};
|
|
10934
|
+
}
|
|
10935
|
+
beginClearRecoveryState() {
|
|
10936
|
+
const recovery = this.getCurrentClearRecoveryState();
|
|
10937
|
+
setIndexLockClearRecoveryState(this.requireActiveLease(), recovery);
|
|
10938
|
+
return recovery;
|
|
10939
|
+
}
|
|
10940
|
+
finishClearRecoveryState() {
|
|
10941
|
+
setIndexLockClearRecoveryState(this.requireActiveLease(), null);
|
|
10942
|
+
}
|
|
10943
|
+
matchesCurrentClearRecoveryConfiguration(recovery) {
|
|
10944
|
+
const configuredProviderInfo = this.configuredProviderInfo;
|
|
10945
|
+
return configuredProviderInfo !== null && recovery.embeddingProvider === configuredProviderInfo.provider && recovery.embeddingModel === configuredProviderInfo.modelInfo.model && recovery.embeddingDimensions === configuredProviderInfo.modelInfo.dimensions && recovery.embeddingStrategyVersion === EMBEDDING_STRATEGY_VERSION;
|
|
10946
|
+
}
|
|
10947
|
+
hasUnknownLegacyForceIndexClear(owner) {
|
|
10948
|
+
return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && existsSync11(path19.join(this.indexPath, "force-index-phase"));
|
|
10949
|
+
}
|
|
10751
10950
|
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
10752
10951
|
for (const owner of owners) {
|
|
10753
10952
|
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
10754
10953
|
pid: owner.pid,
|
|
10755
10954
|
hostname: owner.hostname,
|
|
10756
10955
|
operation: owner.operation,
|
|
10757
|
-
startedAt: owner.startedAt
|
|
10956
|
+
startedAt: owner.startedAt,
|
|
10957
|
+
projectRoot: owner.projectRoot
|
|
10758
10958
|
});
|
|
10759
10959
|
}
|
|
10760
10960
|
if (this.config.scope === "global") {
|
|
10761
|
-
|
|
10762
|
-
|
|
10961
|
+
const clearScopes = [];
|
|
10962
|
+
for (const owner of owners) {
|
|
10963
|
+
if (this.hasUnknownLegacyForceIndexClear(owner)) {
|
|
10964
|
+
throw new Error(
|
|
10965
|
+
`Cannot automatically recover interrupted force-index ${owner.token}: the legacy clearing phase ownership is unknown. The recovery marker was retained for manual inspection.`
|
|
10966
|
+
);
|
|
10967
|
+
}
|
|
10968
|
+
if (owner.operation === "clear" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1) {
|
|
10969
|
+
throw new Error(
|
|
10970
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the originating recovery state is unknown. The recovery marker was retained for manual inspection.`
|
|
10971
|
+
);
|
|
10972
|
+
}
|
|
10973
|
+
if (owner.clearRecovery === void 0) continue;
|
|
10974
|
+
if (!owner.projectRoot || !owner.scopedRoots || owner.scopedRoots.length === 0) {
|
|
10975
|
+
throw new Error(
|
|
10976
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the originating project scope is unknown. The recovery marker was retained for manual inspection.`
|
|
10977
|
+
);
|
|
10978
|
+
}
|
|
10979
|
+
if (!this.matchesCurrentClearRecoveryConfiguration(owner.clearRecovery)) {
|
|
10980
|
+
throw new Error(
|
|
10981
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the current embedding configuration does not match the originating lease. The recovery marker was retained; retry from the originating project with matching settings.`
|
|
10982
|
+
);
|
|
10983
|
+
}
|
|
10984
|
+
clearScopes.push({
|
|
10985
|
+
projectRoot: owner.projectRoot,
|
|
10986
|
+
scopedRoots: owner.scopedRoots,
|
|
10987
|
+
compatibilityDecision: owner.clearRecovery.compatibilityDecision
|
|
10988
|
+
});
|
|
10989
|
+
}
|
|
10990
|
+
if (clearScopes.length > 0) {
|
|
10991
|
+
this.loadFileHashCache();
|
|
10992
|
+
}
|
|
10993
|
+
for (const { projectRoot, scopedRoots, compatibilityDecision } of clearScopes) {
|
|
10994
|
+
this.clearGlobalIndexUnlocked(projectRoot, scopedRoots, compatibilityDecision);
|
|
10763
10995
|
}
|
|
10764
10996
|
await this.healthCheckUnlocked();
|
|
10997
|
+
this.logger.info(
|
|
10998
|
+
clearScopes.length > 0 ? "Recovery complete, next index will rebuild all files" : "Recovery complete, next index will resume from the last checkpoint"
|
|
10999
|
+
);
|
|
11000
|
+
return;
|
|
10765
11001
|
}
|
|
10766
|
-
this.logger.info("Recovery complete, next index will
|
|
11002
|
+
this.logger.info("Recovery complete, next index will resume from the last checkpoint");
|
|
10767
11003
|
}
|
|
10768
11004
|
*loadSerializedFailedBatches() {
|
|
10769
11005
|
let warned = false;
|
|
@@ -10801,14 +11037,99 @@ var Indexer = class _Indexer {
|
|
|
10801
11037
|
state.writer.write(record);
|
|
10802
11038
|
state.recordsWritten += record.chunks.length;
|
|
10803
11039
|
}
|
|
10804
|
-
finalizeFailedBatchWriteState(state) {
|
|
11040
|
+
finalizeFailedBatchWriteState(state, resolvedChunkIds = /* @__PURE__ */ new Set()) {
|
|
10805
11041
|
if (state.recordsWritten > 0) {
|
|
10806
|
-
|
|
11042
|
+
const seenChunkIds = /* @__PURE__ */ new Set();
|
|
11043
|
+
const retained = [];
|
|
11044
|
+
const records = Array.from(readFailedBatchRecords(state.writer.temporaryPath));
|
|
11045
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
11046
|
+
const chunks = records[i].chunks.filter((rawChunk) => {
|
|
11047
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11048
|
+
if (chunkId !== null) {
|
|
11049
|
+
if (resolvedChunkIds.has(chunkId)) return false;
|
|
11050
|
+
if (seenChunkIds.has(chunkId)) return false;
|
|
11051
|
+
seenChunkIds.add(chunkId);
|
|
11052
|
+
}
|
|
11053
|
+
return true;
|
|
11054
|
+
});
|
|
11055
|
+
if (chunks.length > 0) {
|
|
11056
|
+
retained.unshift({ ...records[i], chunks });
|
|
11057
|
+
}
|
|
11058
|
+
}
|
|
11059
|
+
state.writer.cleanup();
|
|
11060
|
+
if (retained.length > 0) {
|
|
11061
|
+
writeFailedBatchRecords(this.failedBatchesPath, retained);
|
|
11062
|
+
} else {
|
|
11063
|
+
writeFailedBatchRecords(this.failedBatchesPath, []);
|
|
11064
|
+
this.clearFailedBatchState();
|
|
11065
|
+
}
|
|
10807
11066
|
return;
|
|
10808
11067
|
}
|
|
10809
|
-
state.writer.
|
|
11068
|
+
state.writer.commit();
|
|
10810
11069
|
this.clearFailedBatchState();
|
|
10811
11070
|
}
|
|
11071
|
+
getCheckpointIntervalChunks(totalChunks) {
|
|
11072
|
+
return Math.max(
|
|
11073
|
+
this.checkpointIntervalChunks ?? 2e3,
|
|
11074
|
+
Math.floor(totalChunks / 10)
|
|
11075
|
+
);
|
|
11076
|
+
}
|
|
11077
|
+
checkpointIndexRun(database, store, invertedIndex, failedProcessing, resolvedRetryChunkIds, currentFileHashes, committedFilePaths, scopedRoots, configuredProviderInfo) {
|
|
11078
|
+
if (!this.hasProjectForceReembedPending()) {
|
|
11079
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
11080
|
+
this.indexCompatibility = { compatible: true };
|
|
11081
|
+
}
|
|
11082
|
+
database.commitWriteTransaction();
|
|
11083
|
+
database.beginWriteTransaction();
|
|
11084
|
+
this.saveInvertedIndex(invertedIndex);
|
|
11085
|
+
store.save();
|
|
11086
|
+
if (failedProcessing.state.recordsWritten > 0 || failedProcessing.latestById.size > 0 || failedProcessing.discardedExistingRecords) {
|
|
11087
|
+
for (const metadata of failedProcessing.latestById.values()) {
|
|
11088
|
+
const alreadyMaterialized = metadata.chunks.some((rawChunk) => {
|
|
11089
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11090
|
+
return chunkId !== null && failedProcessing.materializedRetryIds.has(chunkId);
|
|
11091
|
+
});
|
|
11092
|
+
if (alreadyMaterialized) continue;
|
|
11093
|
+
this.writeFailedBatchRecord(failedProcessing.state, {
|
|
11094
|
+
chunks: metadata.chunks,
|
|
11095
|
+
attemptCount: metadata.attemptCount,
|
|
11096
|
+
error: metadata.error,
|
|
11097
|
+
lastAttempt: metadata.lastAttempt
|
|
11098
|
+
});
|
|
11099
|
+
for (const rawChunk of metadata.chunks) {
|
|
11100
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11101
|
+
if (chunkId !== null) {
|
|
11102
|
+
failedProcessing.materializedRetryIds.add(chunkId);
|
|
11103
|
+
}
|
|
11104
|
+
}
|
|
11105
|
+
}
|
|
11106
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
11107
|
+
failedProcessing.state = this.createFailedBatchWriteState();
|
|
11108
|
+
failedProcessing.discardedExistingRecords = false;
|
|
11109
|
+
for (const record of this.loadSerializedFailedBatches()) {
|
|
11110
|
+
for (const rawChunk of record.chunks) {
|
|
11111
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11112
|
+
this.writeFailedBatchRecord(failedProcessing.state, { ...record, chunks: [rawChunk] });
|
|
11113
|
+
if (chunkId !== null) {
|
|
11114
|
+
failedProcessing.materializedRetryIds.add(chunkId);
|
|
11115
|
+
}
|
|
11116
|
+
}
|
|
11117
|
+
}
|
|
11118
|
+
}
|
|
11119
|
+
const partialHashes = /* @__PURE__ */ new Map();
|
|
11120
|
+
for (const filePath of committedFilePaths) {
|
|
11121
|
+
const hash = currentFileHashes.get(filePath);
|
|
11122
|
+
if (hash !== void 0) {
|
|
11123
|
+
partialHashes.set(filePath, hash);
|
|
11124
|
+
}
|
|
11125
|
+
}
|
|
11126
|
+
if (scopedRoots) {
|
|
11127
|
+
this.replaceScopedFileHashCache(partialHashes, scopedRoots);
|
|
11128
|
+
} else {
|
|
11129
|
+
this.fileHashCache = partialHashes;
|
|
11130
|
+
this.saveFileHashCache();
|
|
11131
|
+
}
|
|
11132
|
+
}
|
|
10812
11133
|
clearFailedBatchState() {
|
|
10813
11134
|
if (existsSync11(this.failedBatchesPath)) {
|
|
10814
11135
|
try {
|
|
@@ -10835,6 +11156,7 @@ var Indexer = class _Indexer {
|
|
|
10835
11156
|
prepareFailedBatchProcessing(roots, shouldProcess) {
|
|
10836
11157
|
const state = this.createFailedBatchWriteState();
|
|
10837
11158
|
const latestById = /* @__PURE__ */ new Map();
|
|
11159
|
+
let discardedExistingRecords = false;
|
|
10838
11160
|
try {
|
|
10839
11161
|
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10840
11162
|
for (const rawChunk of batch.chunks) {
|
|
@@ -10845,10 +11167,12 @@ var Indexer = class _Indexer {
|
|
|
10845
11167
|
continue;
|
|
10846
11168
|
}
|
|
10847
11169
|
if (!shouldProcess(filePath)) {
|
|
11170
|
+
discardedExistingRecords = true;
|
|
10848
11171
|
continue;
|
|
10849
11172
|
}
|
|
10850
11173
|
const chunkId = getPendingChunkId(rawChunk);
|
|
10851
11174
|
if (!chunkId) {
|
|
11175
|
+
discardedExistingRecords = true;
|
|
10852
11176
|
continue;
|
|
10853
11177
|
}
|
|
10854
11178
|
const existing = latestById.get(chunkId);
|
|
@@ -10856,12 +11180,18 @@ var Indexer = class _Indexer {
|
|
|
10856
11180
|
latestById.set(chunkId, {
|
|
10857
11181
|
attemptCount: batch.attemptCount,
|
|
10858
11182
|
error: batch.error,
|
|
10859
|
-
lastAttempt: batch.lastAttempt
|
|
11183
|
+
lastAttempt: batch.lastAttempt,
|
|
11184
|
+
chunks: [rawChunk]
|
|
10860
11185
|
});
|
|
10861
11186
|
}
|
|
10862
11187
|
}
|
|
10863
11188
|
}
|
|
10864
|
-
return {
|
|
11189
|
+
return {
|
|
11190
|
+
state,
|
|
11191
|
+
latestById,
|
|
11192
|
+
materializedRetryIds: /* @__PURE__ */ new Set(),
|
|
11193
|
+
discardedExistingRecords
|
|
11194
|
+
};
|
|
10865
11195
|
} catch (error) {
|
|
10866
11196
|
state.writer.cleanup();
|
|
10867
11197
|
throw error;
|
|
@@ -10897,10 +11227,34 @@ var Indexer = class _Indexer {
|
|
|
10897
11227
|
}
|
|
10898
11228
|
}
|
|
10899
11229
|
}
|
|
11230
|
+
restoreMissingChunkRows(database, chunks) {
|
|
11231
|
+
const missing = [];
|
|
11232
|
+
for (const chunk of chunks) {
|
|
11233
|
+
if (database.getChunk(chunk.id)) {
|
|
11234
|
+
continue;
|
|
11235
|
+
}
|
|
11236
|
+
missing.push({
|
|
11237
|
+
chunkId: chunk.id,
|
|
11238
|
+
contentHash: chunk.contentHash,
|
|
11239
|
+
filePath: chunk.metadata.filePath,
|
|
11240
|
+
startLine: chunk.metadata.startLine,
|
|
11241
|
+
endLine: chunk.metadata.endLine,
|
|
11242
|
+
nodeType: chunk.metadata.chunkType,
|
|
11243
|
+
name: chunk.metadata.name,
|
|
11244
|
+
language: chunk.metadata.language,
|
|
11245
|
+
blameSha: chunk.metadata.blameSha,
|
|
11246
|
+
blameAuthor: chunk.metadata.blameAuthor,
|
|
11247
|
+
blameAuthorEmail: chunk.metadata.blameAuthorEmail,
|
|
11248
|
+
blameCommittedAt: chunk.metadata.blameCommittedAt,
|
|
11249
|
+
blameSummary: chunk.metadata.blameSummary
|
|
11250
|
+
});
|
|
11251
|
+
}
|
|
11252
|
+
if (missing.length > 0) {
|
|
11253
|
+
database.upsertChunksBatch(missing);
|
|
11254
|
+
}
|
|
11255
|
+
}
|
|
10900
11256
|
getProviderRateLimits(provider) {
|
|
10901
11257
|
switch (provider) {
|
|
10902
|
-
case "github-copilot":
|
|
10903
|
-
return { concurrency: 1, intervalMs: 4e3, minRetryMs: 5e3, maxRetryMs: 6e4 };
|
|
10904
11258
|
case "openai":
|
|
10905
11259
|
return { concurrency: 3, intervalMs: 500, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
10906
11260
|
case "google":
|
|
@@ -10969,10 +11323,11 @@ var Indexer = class _Indexer {
|
|
|
10969
11323
|
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
10970
11324
|
const completedVectorsByChunkId = /* @__PURE__ */ new Map();
|
|
10971
11325
|
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
10972
|
-
const
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
11326
|
+
const batchOptions = getDynamicBatchOptions(options.configuredProviderInfo, this.config.embedding?.batch);
|
|
11327
|
+
if (options.forceSingleItemBatches && options.configuredProviderInfo.provider === "ollama") {
|
|
11328
|
+
batchOptions.maxBatchItems = 1;
|
|
11329
|
+
}
|
|
11330
|
+
const requestBatches = createPendingEmbeddingRequestBatches(chunksNeedingEmbedding, batchOptions);
|
|
10976
11331
|
let fatalError;
|
|
10977
11332
|
for (const requestBatch of requestBatches) {
|
|
10978
11333
|
await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
|
|
@@ -11535,7 +11890,7 @@ var Indexer = class _Indexer {
|
|
|
11535
11890
|
}
|
|
11536
11891
|
if (!this.configuredProviderInfo) {
|
|
11537
11892
|
throw new Error(
|
|
11538
|
-
"No embedding provider available. Configure
|
|
11893
|
+
"No embedding provider available. Configure OpenAI, Google, Ollama, or a custom OpenAI-compatible endpoint."
|
|
11539
11894
|
);
|
|
11540
11895
|
}
|
|
11541
11896
|
this.logger.info("Initializing indexer", {
|
|
@@ -11566,7 +11921,20 @@ var Indexer = class _Indexer {
|
|
|
11566
11921
|
]);
|
|
11567
11922
|
}
|
|
11568
11923
|
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
11569
|
-
|
|
11924
|
+
const unknownLegacyForceIndex = recoveredOwners.find(
|
|
11925
|
+
(owner) => this.hasUnknownLegacyForceIndexClear(owner)
|
|
11926
|
+
);
|
|
11927
|
+
if (unknownLegacyForceIndex) {
|
|
11928
|
+
throw new Error(
|
|
11929
|
+
`Cannot automatically recover interrupted force-index ${unknownLegacyForceIndex.token}: the legacy clearing phase ownership is unknown. The recovery marker was retained for manual inspection.`
|
|
11930
|
+
);
|
|
11931
|
+
}
|
|
11932
|
+
const shouldReset = recoveredOwners.some(
|
|
11933
|
+
(owner) => owner.clearRecovery !== void 0 || owner.operation === "clear" && owner.recoveryProtocolVersion !== 1
|
|
11934
|
+
);
|
|
11935
|
+
if (shouldReset) {
|
|
11936
|
+
await this.resetLocalIndexArtifacts();
|
|
11937
|
+
}
|
|
11570
11938
|
}
|
|
11571
11939
|
this.store = new VectorStore(storePath, dimensions);
|
|
11572
11940
|
if (existsSync11(storePath) || existsSync11(vectorMetadataPath)) {
|
|
@@ -12202,7 +12570,17 @@ var Indexer = class _Indexer {
|
|
|
12202
12570
|
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
12203
12571
|
for (const file of files) {
|
|
12204
12572
|
const storedPath = this.toStoredFilePath(file.path);
|
|
12205
|
-
|
|
12573
|
+
let currentHash;
|
|
12574
|
+
try {
|
|
12575
|
+
currentHash = hashFile(file.path);
|
|
12576
|
+
} catch (error) {
|
|
12577
|
+
stats.skippedFiles.push({ path: this.toCanonicalFilePath(file.path), reason: "unreadable" });
|
|
12578
|
+
this.logger.warn("Skipped unreadable file during indexing", {
|
|
12579
|
+
path: file.path,
|
|
12580
|
+
error: getErrorMessage4(error)
|
|
12581
|
+
});
|
|
12582
|
+
continue;
|
|
12583
|
+
}
|
|
12206
12584
|
currentFileHashes.set(storedPath, currentHash);
|
|
12207
12585
|
const cachedHashMatches = this.fileHashCache.get(storedPath) === currentHash;
|
|
12208
12586
|
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
|
|
@@ -12210,7 +12588,8 @@ var Indexer = class _Indexer {
|
|
|
12210
12588
|
);
|
|
12211
12589
|
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path19.extname(storedPath).toLowerCase() === ".swift";
|
|
12212
12590
|
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path19.extname(storedPath).toLowerCase() === ".metal";
|
|
12213
|
-
|
|
12591
|
+
const inMigrationScope = forceScopedReembed && scopedRoots !== null && this.isFileInCurrentScope(storedPath, scopedRoots);
|
|
12592
|
+
if (cachedHashMatches && !inMigrationScope && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
12214
12593
|
unchangedFilePaths.add(storedPath);
|
|
12215
12594
|
this.logger.recordCacheHit();
|
|
12216
12595
|
} else {
|
|
@@ -12336,6 +12715,9 @@ var Indexer = class _Indexer {
|
|
|
12336
12715
|
}
|
|
12337
12716
|
}
|
|
12338
12717
|
let processedChangedFiles = 0;
|
|
12718
|
+
let lastCheckpointChunks = 0;
|
|
12719
|
+
const committedFilePaths = new Set(unchangedFilePaths);
|
|
12720
|
+
const resolvedRetryChunkIds = /* @__PURE__ */ new Set();
|
|
12339
12721
|
for (const descriptorBatch of iterateOrderedFileBatches(
|
|
12340
12722
|
changedFileDescriptors,
|
|
12341
12723
|
(descriptor) => descriptor.sourceBytes,
|
|
@@ -12349,7 +12731,7 @@ var Indexer = class _Indexer {
|
|
|
12349
12731
|
const loadedByPath = new Map(loadedFiles.map((file) => [file.path, file]));
|
|
12350
12732
|
const descriptorByPath = new Map(descriptorBatch.map((descriptor) => [descriptor.storedPath, descriptor]));
|
|
12351
12733
|
const parseStartTime = performance2.now();
|
|
12352
|
-
const parsedFiles = parseFiles(loadedFiles);
|
|
12734
|
+
const parsedFiles = parseFiles(loadedFiles, this.config.indexing.linesPerChunk);
|
|
12353
12735
|
const parseMs = performance2.now() - parseStartTime;
|
|
12354
12736
|
this.logger.recordFilesParsed(parsedFiles.length);
|
|
12355
12737
|
this.logger.recordParseDuration(parseMs);
|
|
@@ -12372,7 +12754,7 @@ var Indexer = class _Indexer {
|
|
|
12372
12754
|
}
|
|
12373
12755
|
let chunksToProcess = parsed.chunks;
|
|
12374
12756
|
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
12375
|
-
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content);
|
|
12757
|
+
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content, this.config.indexing.linesPerChunk);
|
|
12376
12758
|
}
|
|
12377
12759
|
chunksToProcess = selectIndexableChunks(
|
|
12378
12760
|
chunksToProcess,
|
|
@@ -12506,6 +12888,10 @@ var Indexer = class _Indexer {
|
|
|
12506
12888
|
}
|
|
12507
12889
|
if (symbolBatch.length > 0) {
|
|
12508
12890
|
database.upsertSymbolsBatch(symbolBatch);
|
|
12891
|
+
database.addSymbolsToBranchBatch(
|
|
12892
|
+
this.getBranchCatalogKey(),
|
|
12893
|
+
symbolBatch.map((symbol) => symbol.id)
|
|
12894
|
+
);
|
|
12509
12895
|
}
|
|
12510
12896
|
if (edgeBatch.length > 0) {
|
|
12511
12897
|
database.upsertCallEdgesBatch(edgeBatch);
|
|
@@ -12541,6 +12927,12 @@ var Indexer = class _Indexer {
|
|
|
12541
12927
|
forceReembed: forceScopedReembed,
|
|
12542
12928
|
reuseCachedEmbeddings: true,
|
|
12543
12929
|
incrementRepeatedFailures: true,
|
|
12930
|
+
onSucceeded: (succeededChunks) => {
|
|
12931
|
+
database.addChunksToBranchBatch(
|
|
12932
|
+
this.getBranchCatalogKey(),
|
|
12933
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
12934
|
+
);
|
|
12935
|
+
},
|
|
12544
12936
|
onProgress: (batchProgress) => onProgress?.({
|
|
12545
12937
|
phase: "embedding",
|
|
12546
12938
|
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
@@ -12559,6 +12951,27 @@ var Indexer = class _Indexer {
|
|
|
12559
12951
|
}
|
|
12560
12952
|
}
|
|
12561
12953
|
}
|
|
12954
|
+
for (const descriptor of descriptorBatch) {
|
|
12955
|
+
const existingFileChunks = existingChunksByFile.get(descriptor.storedPath);
|
|
12956
|
+
if (!existingFileChunks || existingFileChunks.size === 0) {
|
|
12957
|
+
committedFilePaths.add(descriptor.storedPath);
|
|
12958
|
+
}
|
|
12959
|
+
}
|
|
12960
|
+
const checkpointInterval = this.getCheckpointIntervalChunks(stats.totalChunks);
|
|
12961
|
+
if (stats.totalChunks - lastCheckpointChunks >= checkpointInterval) {
|
|
12962
|
+
lastCheckpointChunks = stats.totalChunks;
|
|
12963
|
+
this.checkpointIndexRun(
|
|
12964
|
+
database,
|
|
12965
|
+
store,
|
|
12966
|
+
invertedIndex,
|
|
12967
|
+
failedProcessing,
|
|
12968
|
+
resolvedRetryChunkIds,
|
|
12969
|
+
currentFileHashes,
|
|
12970
|
+
committedFilePaths,
|
|
12971
|
+
scopedRoots,
|
|
12972
|
+
configuredProviderInfo
|
|
12973
|
+
);
|
|
12974
|
+
}
|
|
12562
12975
|
}
|
|
12563
12976
|
const retryableFailedChunks = this.iterateLatestFailedChunks(
|
|
12564
12977
|
failedProcessing.latestById,
|
|
@@ -12579,6 +12992,7 @@ var Indexer = class _Indexer {
|
|
|
12579
12992
|
retryableChunksWithExistingData.add(chunk.id);
|
|
12580
12993
|
}
|
|
12581
12994
|
}
|
|
12995
|
+
this.restoreMissingChunkRows(database, pendingChunks);
|
|
12582
12996
|
stats.totalChunks += pendingChunks.length;
|
|
12583
12997
|
onProgress?.({
|
|
12584
12998
|
phase: "embedding",
|
|
@@ -12601,6 +13015,17 @@ var Indexer = class _Indexer {
|
|
|
12601
13015
|
forceReembed: forceScopedReembed,
|
|
12602
13016
|
reuseCachedEmbeddings: true,
|
|
12603
13017
|
incrementRepeatedFailures: true,
|
|
13018
|
+
forceSingleItemBatches: true,
|
|
13019
|
+
onSucceeded: (succeededChunks) => {
|
|
13020
|
+
database.addChunksToBranchBatch(
|
|
13021
|
+
this.getBranchCatalogKey(),
|
|
13022
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
13023
|
+
);
|
|
13024
|
+
for (const chunk of succeededChunks) {
|
|
13025
|
+
failedProcessing.latestById.delete(chunk.id);
|
|
13026
|
+
resolvedRetryChunkIds.add(chunk.id);
|
|
13027
|
+
}
|
|
13028
|
+
},
|
|
12604
13029
|
onProgress: (batchProgress) => onProgress?.({
|
|
12605
13030
|
phase: "embedding",
|
|
12606
13031
|
filesProcessed: files.length,
|
|
@@ -12618,6 +13043,20 @@ var Indexer = class _Indexer {
|
|
|
12618
13043
|
failedForcedChunkIds.add(chunkId);
|
|
12619
13044
|
}
|
|
12620
13045
|
}
|
|
13046
|
+
if (stats.totalChunks - lastCheckpointChunks >= this.getCheckpointIntervalChunks(stats.totalChunks)) {
|
|
13047
|
+
lastCheckpointChunks = stats.totalChunks;
|
|
13048
|
+
this.checkpointIndexRun(
|
|
13049
|
+
database,
|
|
13050
|
+
store,
|
|
13051
|
+
invertedIndex,
|
|
13052
|
+
failedProcessing,
|
|
13053
|
+
resolvedRetryChunkIds,
|
|
13054
|
+
currentFileHashes,
|
|
13055
|
+
committedFilePaths,
|
|
13056
|
+
scopedRoots,
|
|
13057
|
+
configuredProviderInfo
|
|
13058
|
+
);
|
|
13059
|
+
}
|
|
12621
13060
|
}
|
|
12622
13061
|
const removedChunkIds = [];
|
|
12623
13062
|
for (const [chunkId] of existingChunks) {
|
|
@@ -12654,13 +13093,6 @@ var Indexer = class _Indexer {
|
|
|
12654
13093
|
if (removedStoredChunks) {
|
|
12655
13094
|
this.saveInvertedIndex(invertedIndex);
|
|
12656
13095
|
}
|
|
12657
|
-
if (scopedRoots) {
|
|
12658
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12659
|
-
} else {
|
|
12660
|
-
this.fileHashCache = currentFileHashes;
|
|
12661
|
-
this.saveFileHashCache();
|
|
12662
|
-
}
|
|
12663
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12664
13096
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12665
13097
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12666
13098
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -12669,6 +13101,13 @@ var Indexer = class _Indexer {
|
|
|
12669
13101
|
this.indexCompatibility = { compatible: true };
|
|
12670
13102
|
database.commitWriteTransaction();
|
|
12671
13103
|
writeTransactionActive = false;
|
|
13104
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
13105
|
+
if (scopedRoots) {
|
|
13106
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
13107
|
+
} else {
|
|
13108
|
+
this.fileHashCache = currentFileHashes;
|
|
13109
|
+
this.saveFileHashCache();
|
|
13110
|
+
}
|
|
12672
13111
|
stats.durationMs = Date.now() - startTime;
|
|
12673
13112
|
onProgress?.({
|
|
12674
13113
|
phase: "complete",
|
|
@@ -12692,13 +13131,6 @@ var Indexer = class _Indexer {
|
|
|
12692
13131
|
);
|
|
12693
13132
|
store.save();
|
|
12694
13133
|
this.saveInvertedIndex(invertedIndex);
|
|
12695
|
-
if (scopedRoots) {
|
|
12696
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12697
|
-
} else {
|
|
12698
|
-
this.fileHashCache = currentFileHashes;
|
|
12699
|
-
this.saveFileHashCache();
|
|
12700
|
-
}
|
|
12701
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12702
13134
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12703
13135
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12704
13136
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -12707,6 +13139,13 @@ var Indexer = class _Indexer {
|
|
|
12707
13139
|
this.indexCompatibility = { compatible: true };
|
|
12708
13140
|
database.commitWriteTransaction();
|
|
12709
13141
|
writeTransactionActive = false;
|
|
13142
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
13143
|
+
if (scopedRoots) {
|
|
13144
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
13145
|
+
} else {
|
|
13146
|
+
this.fileHashCache = currentFileHashes;
|
|
13147
|
+
this.saveFileHashCache();
|
|
13148
|
+
}
|
|
12710
13149
|
stats.durationMs = Date.now() - startTime;
|
|
12711
13150
|
onProgress?.({
|
|
12712
13151
|
phase: "complete",
|
|
@@ -12741,15 +13180,15 @@ var Indexer = class _Indexer {
|
|
|
12741
13180
|
);
|
|
12742
13181
|
store.save();
|
|
12743
13182
|
this.saveInvertedIndex(invertedIndex);
|
|
13183
|
+
database.commitWriteTransaction();
|
|
13184
|
+
writeTransactionActive = false;
|
|
13185
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
12744
13186
|
if (scopedRoots) {
|
|
12745
13187
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12746
13188
|
} else {
|
|
12747
13189
|
this.fileHashCache = currentFileHashes;
|
|
12748
13190
|
this.saveFileHashCache();
|
|
12749
13191
|
}
|
|
12750
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12751
|
-
database.commitWriteTransaction();
|
|
12752
|
-
writeTransactionActive = false;
|
|
12753
13192
|
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
12754
13193
|
const gcReset = await this.maybeRunOrphanGc();
|
|
12755
13194
|
if (gcReset) {
|
|
@@ -12773,6 +13212,9 @@ var Indexer = class _Indexer {
|
|
|
12773
13212
|
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
12774
13213
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12775
13214
|
}
|
|
13215
|
+
if (forceScopedReembed) {
|
|
13216
|
+
database.setMetadata(this.getProjectMigrationFinalizedMetadataKey(), "true");
|
|
13217
|
+
}
|
|
12776
13218
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12777
13219
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12778
13220
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -12883,26 +13325,41 @@ var Indexer = class _Indexer {
|
|
|
12883
13325
|
shouldPrefilterByBranch: branchChunkIds !== null && (this.config.scope === "global" || hasInitializedBranchCatalog)
|
|
12884
13326
|
};
|
|
12885
13327
|
}
|
|
12886
|
-
|
|
13328
|
+
searchCandidatesWithAllowedIds(initialLimit, totalCount, allowedChunkIds, shouldPrefilter, search, getChunkId) {
|
|
12887
13329
|
const normalizedLimit = Math.max(0, Math.floor(initialLimit));
|
|
12888
13330
|
if (normalizedLimit === 0) return [];
|
|
12889
|
-
if (!
|
|
13331
|
+
if (!shouldPrefilter || !allowedChunkIds) {
|
|
12890
13332
|
return search(normalizedLimit);
|
|
12891
13333
|
}
|
|
12892
|
-
const targetCount = Math.min(normalizedLimit,
|
|
13334
|
+
const targetCount = Math.min(normalizedLimit, allowedChunkIds.size);
|
|
12893
13335
|
if (targetCount === 0 || totalCount === 0) return [];
|
|
12894
13336
|
let requestedLimit = Math.min(normalizedLimit, totalCount);
|
|
12895
13337
|
while (true) {
|
|
12896
13338
|
const results = search(requestedLimit);
|
|
12897
|
-
const
|
|
12898
|
-
if (
|
|
12899
|
-
return
|
|
13339
|
+
const allowedResults = results.filter((candidate) => allowedChunkIds.has(getChunkId(candidate)));
|
|
13340
|
+
if (allowedResults.length >= targetCount || results.length < requestedLimit || requestedLimit >= totalCount) {
|
|
13341
|
+
return allowedResults;
|
|
12900
13342
|
}
|
|
12901
13343
|
const nextLimit = Math.min(totalCount, Math.max(requestedLimit + 1, requestedLimit * 2));
|
|
12902
|
-
if (nextLimit === requestedLimit) return
|
|
13344
|
+
if (nextLimit === requestedLimit) return allowedResults;
|
|
12903
13345
|
requestedLimit = nextLimit;
|
|
12904
13346
|
}
|
|
12905
13347
|
}
|
|
13348
|
+
getTemporalChunkIds(database, options) {
|
|
13349
|
+
if (!options?.blameSince && !options?.blameUntil) return null;
|
|
13350
|
+
const since = options.blameSince ? parseBlameTimestamp(options.blameSince, false) : void 0;
|
|
13351
|
+
const until = options.blameUntil ? parseBlameTimestamp(options.blameUntil, true) : void 0;
|
|
13352
|
+
if (since === null || until === null) {
|
|
13353
|
+
return /* @__PURE__ */ new Set();
|
|
13354
|
+
}
|
|
13355
|
+
return new Set(database.getChunkIdsByBlameDate(since, until));
|
|
13356
|
+
}
|
|
13357
|
+
intersectChunkIdSets(first, second) {
|
|
13358
|
+
if (first === null) return second;
|
|
13359
|
+
if (second === null) return first;
|
|
13360
|
+
const [smaller, larger] = first.size <= second.size ? [first, second] : [second, first];
|
|
13361
|
+
return new Set(Array.from(smaller).filter((chunkId) => larger.has(chunkId)));
|
|
13362
|
+
}
|
|
12906
13363
|
buildCandidateSnapshot(candidate) {
|
|
12907
13364
|
return {
|
|
12908
13365
|
id: candidate.id,
|
|
@@ -12917,13 +13374,16 @@ var Indexer = class _Indexer {
|
|
|
12917
13374
|
buildCandidateSnapshotList(candidates) {
|
|
12918
13375
|
return candidates.map((candidate) => this.buildCandidateSnapshot(candidate));
|
|
12919
13376
|
}
|
|
12920
|
-
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch) {
|
|
12921
|
-
|
|
12922
|
-
|
|
12923
|
-
|
|
13377
|
+
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch, temporalChunkIds) {
|
|
13378
|
+
const availableCount = temporalChunkIds?.size ?? store.count();
|
|
13379
|
+
if (availableCount === 0) return [];
|
|
13380
|
+
const allowedIds = temporalChunkIds === null ? void 0 : Array.from(temporalChunkIds);
|
|
13381
|
+
return this.searchCandidatesWithAllowedIds(
|
|
13382
|
+
Math.min(initialLimit, availableCount),
|
|
13383
|
+
availableCount,
|
|
12924
13384
|
branchChunkIds,
|
|
12925
13385
|
shouldPrefilterByBranch,
|
|
12926
|
-
(requestedLimit) => store.search(embedding, requestedLimit),
|
|
13386
|
+
(requestedLimit) => store.search(embedding, requestedLimit, allowedIds),
|
|
12927
13387
|
(candidate) => candidate.id
|
|
12928
13388
|
);
|
|
12929
13389
|
}
|
|
@@ -12948,8 +13408,9 @@ var Indexer = class _Indexer {
|
|
|
12948
13408
|
const rerankTopN = this.config.search.rerankTopN;
|
|
12949
13409
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
12950
13410
|
const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
|
|
13411
|
+
const prioritizeSourcePaths = sourceIntent || options?.prioritizeSourcePaths === true;
|
|
12951
13412
|
const identifierHints = extractIdentifierHints(query);
|
|
12952
|
-
const candidateLimit = maxResults * (
|
|
13413
|
+
const candidateLimit = maxResults * (prioritizeSourcePaths ? 12 : 4);
|
|
12953
13414
|
this.logger.search("debug", "Starting search", {
|
|
12954
13415
|
query,
|
|
12955
13416
|
maxResults,
|
|
@@ -12980,6 +13441,7 @@ var Indexer = class _Indexer {
|
|
|
12980
13441
|
branchChunkIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchChunkIds(branchKey)));
|
|
12981
13442
|
branchSymbolIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchSymbolIds(branchKey)));
|
|
12982
13443
|
}
|
|
13444
|
+
const temporalChunkIds = this.getTemporalChunkIds(database, options);
|
|
12983
13445
|
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
12984
13446
|
const prefilterMs = performance2.now() - prefilterStartTime;
|
|
12985
13447
|
const vectorStartTime = performance2.now();
|
|
@@ -12988,7 +13450,8 @@ var Indexer = class _Indexer {
|
|
|
12988
13450
|
embedding,
|
|
12989
13451
|
candidateLimit,
|
|
12990
13452
|
branchChunkIds,
|
|
12991
|
-
shouldPrefilterByBranch
|
|
13453
|
+
shouldPrefilterByBranch,
|
|
13454
|
+
temporalChunkIds
|
|
12992
13455
|
) : [];
|
|
12993
13456
|
const vectorMs = performance2.now() - vectorStartTime;
|
|
12994
13457
|
const keywordStartTime = performance2.now();
|
|
@@ -12998,7 +13461,8 @@ var Indexer = class _Indexer {
|
|
|
12998
13461
|
store,
|
|
12999
13462
|
invertedIndex,
|
|
13000
13463
|
branchChunkIds,
|
|
13001
|
-
shouldPrefilterByBranch
|
|
13464
|
+
shouldPrefilterByBranch,
|
|
13465
|
+
temporalChunkIds
|
|
13002
13466
|
);
|
|
13003
13467
|
const keywordMs = performance2.now() - keywordStartTime;
|
|
13004
13468
|
const scopedSemanticCandidates = semanticCandidates.filter(
|
|
@@ -13020,7 +13484,7 @@ var Indexer = class _Indexer {
|
|
|
13020
13484
|
rerankTopN,
|
|
13021
13485
|
limit: maxResults,
|
|
13022
13486
|
hybridWeight: rankingHybridWeight,
|
|
13023
|
-
prioritizeSourcePaths
|
|
13487
|
+
prioritizeSourcePaths
|
|
13024
13488
|
});
|
|
13025
13489
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
13026
13490
|
definitionIntent: options?.definitionIntent === true,
|
|
@@ -13056,10 +13520,11 @@ var Indexer = class _Indexer {
|
|
|
13056
13520
|
branchSymbolIds,
|
|
13057
13521
|
maxResults,
|
|
13058
13522
|
union,
|
|
13059
|
-
sourceIntent
|
|
13523
|
+
sourceIntent,
|
|
13524
|
+
options?.definitionIntent === true && ((options.directory?.trim().length ?? 0) > 0 || (options.fileType?.trim().length ?? 0) > 0)
|
|
13060
13525
|
);
|
|
13061
13526
|
const prePrimaryLane = mergeTieredResults(deterministicIdentifierLane, identifierLane, maxResults * 4);
|
|
13062
|
-
const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
13527
|
+
const primaryLane = options?.definitionIntent === true ? mergeTieredResults(symbolLane, prePrimaryLane, maxResults * 4) : mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
13063
13528
|
const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
|
|
13064
13529
|
const hasCodeHints = extractCodeTermHints(query).length > 0 || identifierHints.length > 0;
|
|
13065
13530
|
const baseFiltered = tiered.filter(
|
|
@@ -13154,14 +13619,18 @@ var Indexer = class _Indexer {
|
|
|
13154
13619
|
})
|
|
13155
13620
|
);
|
|
13156
13621
|
}
|
|
13157
|
-
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false) {
|
|
13622
|
+
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false, temporalChunkIds = null) {
|
|
13158
13623
|
const normalizedLimit = Math.max(0, Math.floor(limit));
|
|
13159
13624
|
if (normalizedLimit === 0) return [];
|
|
13160
|
-
const
|
|
13625
|
+
const allowedChunkIds = this.intersectChunkIdSets(
|
|
13626
|
+
shouldPrefilterByBranch ? branchChunkIds : null,
|
|
13627
|
+
temporalChunkIds
|
|
13628
|
+
);
|
|
13629
|
+
const scoreEntries = this.searchCandidatesWithAllowedIds(
|
|
13161
13630
|
normalizedLimit,
|
|
13162
13631
|
invertedIndex.getDocumentCount(),
|
|
13163
|
-
|
|
13164
|
-
|
|
13632
|
+
allowedChunkIds,
|
|
13633
|
+
allowedChunkIds !== null,
|
|
13165
13634
|
(requestedLimit) => Array.from(invertedIndex.search(query, requestedLimit)),
|
|
13166
13635
|
([chunkId]) => chunkId
|
|
13167
13636
|
);
|
|
@@ -13246,7 +13715,17 @@ var Indexer = class _Indexer {
|
|
|
13246
13715
|
);
|
|
13247
13716
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
13248
13717
|
for (const file of files) {
|
|
13249
|
-
|
|
13718
|
+
let hash;
|
|
13719
|
+
try {
|
|
13720
|
+
hash = hashFile(file.path);
|
|
13721
|
+
} catch (error) {
|
|
13722
|
+
this.logger.warn("Skipped unreadable file during freshness check", {
|
|
13723
|
+
path: file.path,
|
|
13724
|
+
error: getErrorMessage4(error)
|
|
13725
|
+
});
|
|
13726
|
+
return { readable: false, current: false, reason: "unreadable" };
|
|
13727
|
+
}
|
|
13728
|
+
currentFileHashes.set(this.toStoredFilePath(file.path), hash);
|
|
13250
13729
|
}
|
|
13251
13730
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
13252
13731
|
const cachedFileHashes = scopedRoots ? new Map(Array.from(this.fileHashCache).filter(([filePath]) => this.isFileInCurrentScope(filePath, scopedRoots))) : this.fileHashCache;
|
|
@@ -13272,69 +13751,87 @@ var Indexer = class _Indexer {
|
|
|
13272
13751
|
async forceIndex(onProgress) {
|
|
13273
13752
|
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
13274
13753
|
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
13275
|
-
|
|
13754
|
+
const recovery = this.beginClearRecoveryState();
|
|
13755
|
+
await this.clearIndexUnlocked(recovery.compatibilityDecision);
|
|
13756
|
+
this.finishClearRecoveryState();
|
|
13276
13757
|
return this.indexUnlocked(onProgress, [], true);
|
|
13277
13758
|
});
|
|
13278
13759
|
}
|
|
13279
13760
|
async clearIndex() {
|
|
13280
13761
|
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
13281
13762
|
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
13282
|
-
|
|
13763
|
+
const recovery = this.beginClearRecoveryState();
|
|
13764
|
+
await this.clearIndexUnlocked(recovery.compatibilityDecision);
|
|
13283
13765
|
});
|
|
13284
13766
|
}
|
|
13285
|
-
|
|
13767
|
+
clearGlobalIndexDataUnlocked(projectRoot = this.projectRoot) {
|
|
13286
13768
|
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
13287
|
-
|
|
13288
|
-
|
|
13289
|
-
|
|
13290
|
-
|
|
13291
|
-
|
|
13292
|
-
|
|
13293
|
-
|
|
13294
|
-
|
|
13295
|
-
|
|
13296
|
-
|
|
13297
|
-
|
|
13298
|
-
|
|
13299
|
-
|
|
13300
|
-
|
|
13301
|
-
|
|
13769
|
+
const clearedBranchKeys = database.getAllBranches();
|
|
13770
|
+
store.clear();
|
|
13771
|
+
store.save();
|
|
13772
|
+
invertedIndex.clear();
|
|
13773
|
+
this.saveInvertedIndex(invertedIndex);
|
|
13774
|
+
this.fileHashCache.clear();
|
|
13775
|
+
this.saveFileHashCache();
|
|
13776
|
+
database.clearAllIndexedData();
|
|
13777
|
+
this.deleteBranchCommitMetadata(database, clearedBranchKeys);
|
|
13778
|
+
this.clearFailedBatchState();
|
|
13779
|
+
database.deleteMetadata("index.version");
|
|
13780
|
+
database.deleteMetadata("index.pathStorageVersion");
|
|
13781
|
+
database.deleteMetadata("index.embeddingProvider");
|
|
13782
|
+
database.deleteMetadata("index.embeddingModel");
|
|
13783
|
+
database.deleteMetadata("index.embeddingDimensions");
|
|
13784
|
+
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
13785
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
13786
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey(projectIdentityHash));
|
|
13787
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey(projectIdentityHash));
|
|
13788
|
+
database.deleteMetadata(this.getLegacyMigrationMetadataKey(projectIdentityHash));
|
|
13789
|
+
database.deleteMetadata("index.createdAt");
|
|
13790
|
+
database.deleteMetadata("index.updatedAt");
|
|
13791
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
13792
|
+
}
|
|
13793
|
+
clearGlobalIndexUnlocked(projectRoot = this.projectRoot, roots = this.getScopedRoots(), recoveryDecision) {
|
|
13794
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
13795
|
+
store.load();
|
|
13796
|
+
invertedIndex.load();
|
|
13797
|
+
this.loadFileHashCache();
|
|
13798
|
+
const compatibility = this.checkCompatibility();
|
|
13799
|
+
const compatibilityDecision = recoveryDecision ?? (compatibility.compatible ? "compatible" : compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */ ? "embedding-strategy-mismatch" : "incompatible");
|
|
13800
|
+
const allMetadata = store.getAllMetadata();
|
|
13801
|
+
const hasForeignData = allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots)) || this.hasForeignScopedBranchData(projectRoot, roots) || this.hasForeignScopedFileHashData(roots) || this.hasForeignScopedFailedBatches(roots);
|
|
13802
|
+
if (compatibilityDecision !== "compatible" && hasForeignData) {
|
|
13803
|
+
if (compatibilityDecision === "embedding-strategy-mismatch") {
|
|
13804
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot);
|
|
13805
|
+
this.clearScopedFileHashCache(roots);
|
|
13806
|
+
this.clearScopedFailedBatches(roots);
|
|
13807
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
13808
|
+
database.setMetadata(this.getProjectForceReembedMetadataKey(projectIdentityHash), "true");
|
|
13809
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey(projectIdentityHash));
|
|
13810
|
+
database.deleteMetadata(this.getProjectMigrationFinalizedMetadataKey(projectIdentityHash));
|
|
13811
|
+
if (projectRoot === this.projectRoot) {
|
|
13302
13812
|
this.indexCompatibility = { compatible: true };
|
|
13303
|
-
return;
|
|
13304
13813
|
}
|
|
13305
|
-
throw new Error(
|
|
13306
|
-
`Global index compatibility reset is unsafe because the shared index contains files from other projects. The current global index cannot be force-rebuilt for ${this.projectRoot} without deleting other repositories' indexed data. Use scope="project" for isolated rebuilds, or manually delete the shared global index if you intend to rebuild all projects.`
|
|
13307
|
-
);
|
|
13308
|
-
}
|
|
13309
|
-
if (!hasForeignData) {
|
|
13310
|
-
const clearedBranchKeys2 = database.getAllBranches();
|
|
13311
|
-
store.clear();
|
|
13312
|
-
store.save();
|
|
13313
|
-
invertedIndex.clear();
|
|
13314
|
-
this.saveInvertedIndex(invertedIndex);
|
|
13315
|
-
this.fileHashCache.clear();
|
|
13316
|
-
this.saveFileHashCache();
|
|
13317
|
-
database.clearAllIndexedData();
|
|
13318
|
-
this.deleteBranchCommitMetadata(database, clearedBranchKeys2);
|
|
13319
|
-
this.clearFailedBatchState();
|
|
13320
|
-
database.deleteMetadata("index.version");
|
|
13321
|
-
database.deleteMetadata("index.pathStorageVersion");
|
|
13322
|
-
database.deleteMetadata("index.embeddingProvider");
|
|
13323
|
-
database.deleteMetadata("index.embeddingModel");
|
|
13324
|
-
database.deleteMetadata("index.embeddingDimensions");
|
|
13325
|
-
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
13326
|
-
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
13327
|
-
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
13328
|
-
database.deleteMetadata(this.getLegacyMigrationMetadataKey());
|
|
13329
|
-
database.deleteMetadata("index.createdAt");
|
|
13330
|
-
database.deleteMetadata("index.updatedAt");
|
|
13331
|
-
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
13332
13814
|
return;
|
|
13333
13815
|
}
|
|
13334
|
-
|
|
13335
|
-
|
|
13336
|
-
|
|
13816
|
+
throw new Error(
|
|
13817
|
+
`Global index compatibility reset is unsafe because the shared index contains files from other projects. The current global index cannot be force-rebuilt for ${projectRoot} without deleting other repositories' indexed data. Use scope="project" for isolated rebuilds, or manually delete the shared global index if you intend to rebuild all projects.`
|
|
13818
|
+
);
|
|
13819
|
+
}
|
|
13820
|
+
if (!hasForeignData) {
|
|
13821
|
+
this.clearGlobalIndexDataUnlocked(projectRoot);
|
|
13822
|
+
return;
|
|
13823
|
+
}
|
|
13824
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot);
|
|
13825
|
+
this.clearScopedFileHashCache(roots);
|
|
13826
|
+
this.clearScopedFailedBatches(roots);
|
|
13827
|
+
if (projectRoot === this.projectRoot) {
|
|
13337
13828
|
this.indexCompatibility = compatibility;
|
|
13829
|
+
}
|
|
13830
|
+
}
|
|
13831
|
+
async clearIndexUnlocked(recoveryDecision) {
|
|
13832
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
13833
|
+
if (this.config.scope === "global") {
|
|
13834
|
+
this.clearGlobalIndexUnlocked(this.projectRoot, this.getScopedRoots(), recoveryDecision);
|
|
13338
13835
|
return;
|
|
13339
13836
|
}
|
|
13340
13837
|
if (!this.isProjectOwnedIndexPath()) {
|
|
@@ -13500,6 +13997,7 @@ var Indexer = class _Indexer {
|
|
|
13500
13997
|
)) {
|
|
13501
13998
|
const chunks = retryBatch.map(({ chunk }) => chunk);
|
|
13502
13999
|
const attemptCounts = new Map(retryBatch.map(({ chunk, attemptCount }) => [chunk.id, attemptCount]));
|
|
14000
|
+
this.restoreMissingChunkRows(database, chunks);
|
|
13503
14001
|
const batchResult = await this.processPendingChunkBatch(chunks, {
|
|
13504
14002
|
store,
|
|
13505
14003
|
provider,
|
|
@@ -13514,6 +14012,7 @@ var Indexer = class _Indexer {
|
|
|
13514
14012
|
forceReembed: false,
|
|
13515
14013
|
reuseCachedEmbeddings: false,
|
|
13516
14014
|
incrementRepeatedFailures: false,
|
|
14015
|
+
forceSingleItemBatches: true,
|
|
13517
14016
|
onSucceeded: (succeededChunks) => {
|
|
13518
14017
|
database.addChunksToBranchBatch(
|
|
13519
14018
|
this.getBranchCatalogKey(),
|
|
@@ -13535,9 +14034,12 @@ var Indexer = class _Indexer {
|
|
|
13535
14034
|
this.saveInvertedIndex(invertedIndex);
|
|
13536
14035
|
}
|
|
13537
14036
|
if (roots && succeeded > 0 && remaining === 0 && this.hasProjectForceReembedPending()) {
|
|
13538
|
-
database.
|
|
13539
|
-
|
|
13540
|
-
|
|
14037
|
+
const migrationFinalized = database.getMetadata(this.getProjectMigrationFinalizedMetadataKey()) === "true";
|
|
14038
|
+
if (migrationFinalized) {
|
|
14039
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
14040
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
14041
|
+
this.indexCompatibility = { compatible: true };
|
|
14042
|
+
}
|
|
13541
14043
|
}
|
|
13542
14044
|
return { succeeded, failed, remaining };
|
|
13543
14045
|
}
|
|
@@ -13559,7 +14061,8 @@ var Indexer = class _Indexer {
|
|
|
13559
14061
|
latestById.set(chunkId, {
|
|
13560
14062
|
attemptCount: batch.attemptCount,
|
|
13561
14063
|
error: batch.error,
|
|
13562
|
-
lastAttempt: batch.lastAttempt
|
|
14064
|
+
lastAttempt: batch.lastAttempt,
|
|
14065
|
+
chunks: [rawChunk]
|
|
13563
14066
|
});
|
|
13564
14067
|
}
|
|
13565
14068
|
}
|
|
@@ -13626,6 +14129,7 @@ var Indexer = class _Indexer {
|
|
|
13626
14129
|
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
13627
14130
|
);
|
|
13628
14131
|
}
|
|
14132
|
+
const temporalChunkIds = this.getTemporalChunkIds(database, options);
|
|
13629
14133
|
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
13630
14134
|
const prefilterMs = performance2.now() - prefilterStartTime;
|
|
13631
14135
|
const vectorStartTime = performance2.now();
|
|
@@ -13634,7 +14138,8 @@ var Indexer = class _Indexer {
|
|
|
13634
14138
|
embedding,
|
|
13635
14139
|
limit * 2,
|
|
13636
14140
|
branchChunkIds,
|
|
13637
|
-
shouldPrefilterByBranch
|
|
14141
|
+
shouldPrefilterByBranch,
|
|
14142
|
+
temporalChunkIds
|
|
13638
14143
|
);
|
|
13639
14144
|
const vectorMs = performance2.now() - vectorStartTime;
|
|
13640
14145
|
if (this.config.scope !== "global" && branchChunkIds && !hasInitializedBranchCatalog) {
|
|
@@ -14383,9 +14888,11 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
|
14383
14888
|
contextLines: options.contextLines,
|
|
14384
14889
|
metadataOnly: options.metadataOnly,
|
|
14385
14890
|
definitionIntent: options.definitionIntent,
|
|
14891
|
+
prioritizeSourcePaths: options.prioritizeSourcePaths,
|
|
14386
14892
|
blameAuthor: options.blameAuthor,
|
|
14387
14893
|
blameSha: options.blameSha,
|
|
14388
14894
|
blameSince: options.blameSince,
|
|
14895
|
+
blameUntil: options.blameUntil,
|
|
14389
14896
|
trace: options.trace
|
|
14390
14897
|
});
|
|
14391
14898
|
}
|
|
@@ -14431,7 +14938,9 @@ async function findSimilarCode(projectRoot, host, code, options = {}) {
|
|
|
14431
14938
|
fileType: options.fileType,
|
|
14432
14939
|
directory: options.directory,
|
|
14433
14940
|
chunkType: options.chunkType,
|
|
14434
|
-
excludeFile: options.excludeFile
|
|
14941
|
+
excludeFile: options.excludeFile,
|
|
14942
|
+
blameSince: options.blameSince,
|
|
14943
|
+
blameUntil: options.blameUntil
|
|
14435
14944
|
});
|
|
14436
14945
|
}
|
|
14437
14946
|
async function implementationLookup(projectRoot, host, query, options = {}) {
|
|
@@ -17970,13 +18479,19 @@ async function resolveSearchContext(input, operations) {
|
|
|
17970
18479
|
(trace) => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
17971
18480
|
);
|
|
17972
18481
|
};
|
|
17973
|
-
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt) => {
|
|
18482
|
+
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt, prioritizeSourcePaths) => {
|
|
17974
18483
|
return recordAttempt(
|
|
17975
18484
|
"conceptual",
|
|
17976
18485
|
searchQuery,
|
|
17977
18486
|
scope,
|
|
17978
18487
|
relaxedFieldsForAttempt,
|
|
17979
|
-
(trace) => operations.search(
|
|
18488
|
+
(trace) => operations.search(
|
|
18489
|
+
searchQuery,
|
|
18490
|
+
MAX_CONTEXT_RESULT_LIMIT,
|
|
18491
|
+
scope,
|
|
18492
|
+
input.diagnostic ? trace : void 0,
|
|
18493
|
+
{ prioritizeSourcePaths }
|
|
18494
|
+
)
|
|
17980
18495
|
);
|
|
17981
18496
|
};
|
|
17982
18497
|
const findSuccessfulAttemptState = (route) => {
|
|
@@ -18104,10 +18619,12 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
18104
18619
|
}
|
|
18105
18620
|
}
|
|
18106
18621
|
for (const attempt of conceptualAttemptPlan) {
|
|
18622
|
+
const attemptIntent = analyzeQueryIntent(attempt.queryText);
|
|
18623
|
+
const prioritizeSourcePaths = attemptIntent.primary !== "docs" && attemptIntent.primary !== "test";
|
|
18107
18624
|
if (inferredSymbol && attempt.queryText === inferredSymbol && attempt.queryText !== query) {
|
|
18108
18625
|
decisions.fallbackFromOriginalConceptualToInferred = true;
|
|
18109
18626
|
}
|
|
18110
|
-
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed);
|
|
18627
|
+
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed, prioritizeSourcePaths);
|
|
18111
18628
|
if (results.length > 0) {
|
|
18112
18629
|
const heading = buildPackHeading("conceptual", decisions);
|
|
18113
18630
|
const intent = analyzeQueryIntent(attempt.queryText);
|
|
@@ -18244,12 +18761,13 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
18244
18761
|
directory: scope.directory,
|
|
18245
18762
|
trace
|
|
18246
18763
|
}),
|
|
18247
|
-
search: (queryText, retrievalLimit, scope, trace) => searchCodebase(projectRoot, host, queryText, {
|
|
18764
|
+
search: (queryText, retrievalLimit, scope, trace, searchOptions) => searchCodebase(projectRoot, host, queryText, {
|
|
18248
18765
|
limit: retrievalLimit,
|
|
18249
18766
|
fileType: scope.fileType,
|
|
18250
18767
|
directory: scope.directory,
|
|
18251
18768
|
metadataOnly: true,
|
|
18252
|
-
trace
|
|
18769
|
+
trace,
|
|
18770
|
+
prioritizeSourcePaths: searchOptions?.prioritizeSourcePaths
|
|
18253
18771
|
})
|
|
18254
18772
|
});
|
|
18255
18773
|
}
|
|
@@ -19164,7 +19682,8 @@ var codebase_peek = tool({
|
|
|
19164
19682
|
chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
|
|
19165
19683
|
blameAuthor: z3.string().optional().describe("Filter by git blame author name or email"),
|
|
19166
19684
|
blameSha: z3.string().optional().describe("Filter by git blame commit SHA or prefix"),
|
|
19167
|
-
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
|
|
19685
|
+
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)"),
|
|
19686
|
+
blameUntil: z3.string().optional().describe("Filter to chunks last changed on or before this date (e.g., 2025-01-31)")
|
|
19168
19687
|
},
|
|
19169
19688
|
async execute(args, context) {
|
|
19170
19689
|
return searchCodebaseWithEffectiveness(context?.worktree, DEFAULT_HOST, "peek", args.query, {
|
|
@@ -19175,7 +19694,8 @@ var codebase_peek = tool({
|
|
|
19175
19694
|
metadataOnly: true,
|
|
19176
19695
|
blameAuthor: args.blameAuthor,
|
|
19177
19696
|
blameSha: args.blameSha,
|
|
19178
|
-
blameSince: args.blameSince
|
|
19697
|
+
blameSince: args.blameSince,
|
|
19698
|
+
blameUntil: args.blameUntil
|
|
19179
19699
|
}, (results) => {
|
|
19180
19700
|
const text = formatCodebasePeek(results);
|
|
19181
19701
|
return { output: text, text };
|
|
@@ -19237,7 +19757,9 @@ var find_similar = tool({
|
|
|
19237
19757
|
fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
19238
19758
|
directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
19239
19759
|
chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
|
|
19240
|
-
excludeFile: z3.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
|
|
19760
|
+
excludeFile: z3.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)"),
|
|
19761
|
+
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)"),
|
|
19762
|
+
blameUntil: z3.string().optional().describe("Filter to chunks last changed on or before this date (e.g., 2025-01-31)")
|
|
19241
19763
|
},
|
|
19242
19764
|
async execute(args, context) {
|
|
19243
19765
|
const results = await findSimilarCode(context?.worktree, DEFAULT_HOST, args.code, {
|
|
@@ -19245,7 +19767,9 @@ var find_similar = tool({
|
|
|
19245
19767
|
fileType: args.fileType,
|
|
19246
19768
|
directory: args.directory,
|
|
19247
19769
|
chunkType: args.chunkType,
|
|
19248
|
-
excludeFile: args.excludeFile
|
|
19770
|
+
excludeFile: args.excludeFile,
|
|
19771
|
+
blameSince: args.blameSince,
|
|
19772
|
+
blameUntil: args.blameUntil
|
|
19249
19773
|
});
|
|
19250
19774
|
if (results.length === 0) {
|
|
19251
19775
|
return "No similar code found. Try a different snippet or run index_codebase first.";
|
|
@@ -19264,7 +19788,8 @@ var codebase_search = tool({
|
|
|
19264
19788
|
contextLines: z3.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
|
|
19265
19789
|
blameAuthor: z3.string().optional().describe("Filter by git blame author name or email"),
|
|
19266
19790
|
blameSha: z3.string().optional().describe("Filter by git blame commit SHA or prefix"),
|
|
19267
|
-
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
|
|
19791
|
+
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)"),
|
|
19792
|
+
blameUntil: z3.string().optional().describe("Filter to chunks last changed on or before this date (e.g., 2025-01-31)")
|
|
19268
19793
|
},
|
|
19269
19794
|
async execute(args, context) {
|
|
19270
19795
|
return searchCodebaseWithEffectiveness(context?.worktree, DEFAULT_HOST, "search", args.query, {
|
|
@@ -19275,7 +19800,8 @@ var codebase_search = tool({
|
|
|
19275
19800
|
contextLines: args.contextLines,
|
|
19276
19801
|
blameAuthor: args.blameAuthor,
|
|
19277
19802
|
blameSha: args.blameSha,
|
|
19278
|
-
blameSince: args.blameSince
|
|
19803
|
+
blameSince: args.blameSince,
|
|
19804
|
+
blameUntil: args.blameUntil
|
|
19279
19805
|
}, (results) => {
|
|
19280
19806
|
const text = results.length === 0 ? "No matching code found. Try a different query or run index_codebase first." : formatSearchResults(results, "score");
|
|
19281
19807
|
return { output: text, text };
|
|
@@ -19488,6 +20014,12 @@ var PI_TOOL_NAMES = [
|
|
|
19488
20014
|
TOOL_NAME.PI_KNOWLEDGE_BASE_ADD,
|
|
19489
20015
|
TOOL_NAME.PI_KNOWLEDGE_BASE_REMOVE
|
|
19490
20016
|
];
|
|
20017
|
+
var MCP_TOOL_NAMES = [
|
|
20018
|
+
...PORTABLE_TOOL_NAMES,
|
|
20019
|
+
TOOL_NAME.ADD_KNOWLEDGE_BASE,
|
|
20020
|
+
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
20021
|
+
TOOL_NAME.REMOVE_KNOWLEDGE_BASE
|
|
20022
|
+
];
|
|
19491
20023
|
|
|
19492
20024
|
// src/commands/loader.ts
|
|
19493
20025
|
import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync9 } from "fs";
|