opencode-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.cjs
CHANGED
|
@@ -724,6 +724,17 @@ var EMBEDDING_MODELS = {
|
|
|
724
724
|
maxTokens: 2048,
|
|
725
725
|
costPer1MTokens: 0.15,
|
|
726
726
|
taskAble: true
|
|
727
|
+
},
|
|
728
|
+
"gemini-embedding-2": {
|
|
729
|
+
provider: "google",
|
|
730
|
+
model: "gemini-embedding-2",
|
|
731
|
+
// Keep a conservative, testable default embedding dimension. Gemini Embedding 2 supports
|
|
732
|
+
// flexible dimensions via outputDimensionality.
|
|
733
|
+
dimensions: 1536,
|
|
734
|
+
maxTokens: 8192,
|
|
735
|
+
costPer1MTokens: 0.15,
|
|
736
|
+
taskAble: false,
|
|
737
|
+
promptStyle: "embedding-2"
|
|
727
738
|
}
|
|
728
739
|
},
|
|
729
740
|
"openai": {
|
|
@@ -757,26 +768,15 @@ var EMBEDDING_MODELS = {
|
|
|
757
768
|
maxTokens: 512,
|
|
758
769
|
costPer1MTokens: 0
|
|
759
770
|
}
|
|
760
|
-
},
|
|
761
|
-
"github-copilot": {
|
|
762
|
-
"text-embedding-3-small": {
|
|
763
|
-
provider: "github-copilot",
|
|
764
|
-
model: "text-embedding-3-small",
|
|
765
|
-
dimensions: 1536,
|
|
766
|
-
maxTokens: 8191,
|
|
767
|
-
costPer1MTokens: 0
|
|
768
|
-
}
|
|
769
771
|
}
|
|
770
772
|
};
|
|
771
773
|
var DEFAULT_PROVIDER_MODELS = {
|
|
772
|
-
"github-copilot": "text-embedding-3-small",
|
|
773
774
|
"openai": "text-embedding-3-small",
|
|
774
775
|
"google": "gemini-embedding-001",
|
|
775
776
|
"ollama": "nomic-embed-text"
|
|
776
777
|
};
|
|
777
778
|
var AUTO_DETECT_PROVIDER_ORDER = [
|
|
778
779
|
"ollama",
|
|
779
|
-
"github-copilot",
|
|
780
780
|
"openai",
|
|
781
781
|
"google"
|
|
782
782
|
];
|
|
@@ -802,6 +802,9 @@ function getDefaultIndexingConfig() {
|
|
|
802
802
|
maxDepth: 5,
|
|
803
803
|
maxFilesPerDirectory: 100,
|
|
804
804
|
fallbackToTextOnMaxChunks: true,
|
|
805
|
+
// Must stay in sync with DEFAULT_LINES_PER_CHUNK in native/src/lib.rs (the napi
|
|
806
|
+
// fallback used when a native caller omits the argument).
|
|
807
|
+
linesPerChunk: 30,
|
|
805
808
|
gitBlame: { enabled: false }
|
|
806
809
|
};
|
|
807
810
|
}
|
|
@@ -935,6 +938,7 @@ function parseConfig(raw) {
|
|
|
935
938
|
maxDepth: typeof rawIndexing.maxDepth === "number" ? rawIndexing.maxDepth < -1 ? -1 : rawIndexing.maxDepth : defaultIndexing.maxDepth,
|
|
936
939
|
maxFilesPerDirectory: typeof rawIndexing.maxFilesPerDirectory === "number" ? Math.max(1, rawIndexing.maxFilesPerDirectory) : defaultIndexing.maxFilesPerDirectory,
|
|
937
940
|
fallbackToTextOnMaxChunks: typeof rawIndexing.fallbackToTextOnMaxChunks === "boolean" ? rawIndexing.fallbackToTextOnMaxChunks : defaultIndexing.fallbackToTextOnMaxChunks,
|
|
941
|
+
linesPerChunk: typeof rawIndexing.linesPerChunk === "number" && Number.isFinite(rawIndexing.linesPerChunk) ? Math.min(Math.max(1, Math.floor(rawIndexing.linesPerChunk)), 4294967295) : defaultIndexing.linesPerChunk,
|
|
938
942
|
gitBlame: {
|
|
939
943
|
enabled: rawIndexing.gitBlame && typeof rawIndexing.gitBlame === "object" && typeof rawIndexing.gitBlame.enabled === "boolean" ? rawIndexing.gitBlame.enabled : defaultIndexing.gitBlame.enabled
|
|
940
944
|
}
|
|
@@ -977,6 +981,7 @@ function parseConfig(raw) {
|
|
|
977
981
|
let embeddingModel;
|
|
978
982
|
let customProvider;
|
|
979
983
|
let reranker;
|
|
984
|
+
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.';
|
|
980
985
|
if (embeddingProviderValue === "custom") {
|
|
981
986
|
embeddingProvider = "custom";
|
|
982
987
|
const rawCustom = input.customProvider && typeof input.customProvider === "object" ? input.customProvider : null;
|
|
@@ -1016,6 +1021,8 @@ function parseConfig(raw) {
|
|
|
1016
1021
|
} else if (rawEmbeddingModel) {
|
|
1017
1022
|
embeddingModel = DEFAULT_PROVIDER_MODELS[embeddingProvider];
|
|
1018
1023
|
}
|
|
1024
|
+
} else if (embeddingProviderValue === "github-copilot") {
|
|
1025
|
+
throw new Error(githubCopilotDeprecationMessage);
|
|
1019
1026
|
} else {
|
|
1020
1027
|
embeddingProvider = "auto";
|
|
1021
1028
|
}
|
|
@@ -1046,10 +1053,21 @@ function parseConfig(raw) {
|
|
|
1046
1053
|
timeoutMs: typeof rawReranker.timeoutMs === "number" ? Math.max(1e3, Math.floor(rawReranker.timeoutMs)) : 1e4
|
|
1047
1054
|
};
|
|
1048
1055
|
}
|
|
1056
|
+
const rawEmbedding = input.embedding && typeof input.embedding === "object" ? input.embedding : {};
|
|
1057
|
+
const rawEmbeddingBatch = rawEmbedding.batch && typeof rawEmbedding.batch === "object" ? rawEmbedding.batch : null;
|
|
1058
|
+
const embeddingMaxBatchItems = typeof rawEmbeddingBatch?.maxBatchItems === "number" && Number.isFinite(rawEmbeddingBatch.maxBatchItems) ? Math.max(1, Math.floor(rawEmbeddingBatch.maxBatchItems)) : void 0;
|
|
1059
|
+
const embeddingMaxBatchTokens = typeof rawEmbeddingBatch?.maxBatchTokens === "number" && Number.isFinite(rawEmbeddingBatch.maxBatchTokens) ? Math.max(1, Math.floor(rawEmbeddingBatch.maxBatchTokens)) : void 0;
|
|
1060
|
+
const embedding = embeddingMaxBatchItems !== void 0 || embeddingMaxBatchTokens !== void 0 ? {
|
|
1061
|
+
batch: {
|
|
1062
|
+
...embeddingMaxBatchItems !== void 0 ? { maxBatchItems: embeddingMaxBatchItems } : {},
|
|
1063
|
+
...embeddingMaxBatchTokens !== void 0 ? { maxBatchTokens: embeddingMaxBatchTokens } : {}
|
|
1064
|
+
}
|
|
1065
|
+
} : {};
|
|
1049
1066
|
return {
|
|
1050
1067
|
embeddingProvider,
|
|
1051
1068
|
embeddingModel,
|
|
1052
1069
|
customProvider,
|
|
1070
|
+
embedding,
|
|
1053
1071
|
scope: isValidScope(scopeValue) ? scopeValue : "project",
|
|
1054
1072
|
include: includeValue ?? DEFAULT_INCLUDE,
|
|
1055
1073
|
exclude: excludeValue ?? DEFAULT_EXCLUDE,
|
|
@@ -2316,6 +2334,10 @@ function scoreCandidate(query, intent, candidate, originalIndex) {
|
|
|
2316
2334
|
let boost = 0;
|
|
2317
2335
|
if (intent.primary === "conceptual") {
|
|
2318
2336
|
boost += Math.min(0.14, overlap * 0.14);
|
|
2337
|
+
if (intent.preferSourcePaths) {
|
|
2338
|
+
boost += implementationPath ? 0.32 : 0;
|
|
2339
|
+
if (testPath || fixturePath || docsPath) boost -= 0.35;
|
|
2340
|
+
}
|
|
2319
2341
|
if (generatedOrVendor) boost -= 0.18;
|
|
2320
2342
|
if (importChunk || weakContainer) boost -= 0.04;
|
|
2321
2343
|
} else if (intent.primary === "test") {
|
|
@@ -3283,6 +3305,19 @@ function parseOwner(value) {
|
|
|
3283
3305
|
if (typeof candidate.startedAt !== "string" || Number.isNaN(Date.parse(candidate.startedAt))) return null;
|
|
3284
3306
|
if (typeof candidate.operation !== "string" || !VALID_OPERATIONS.has(candidate.operation)) return null;
|
|
3285
3307
|
if (typeof candidate.token !== "string" || !UUID_PATTERN.test(candidate.token)) return null;
|
|
3308
|
+
if (candidate.recoveryProtocolVersion !== void 0 && candidate.recoveryProtocolVersion !== 1) return null;
|
|
3309
|
+
if (candidate.projectRoot !== void 0 && typeof candidate.projectRoot !== "string") return null;
|
|
3310
|
+
if (candidate.scopedRoots !== void 0) {
|
|
3311
|
+
if (!Array.isArray(candidate.scopedRoots) || candidate.scopedRoots.some((root) => typeof root !== "string")) {
|
|
3312
|
+
return null;
|
|
3313
|
+
}
|
|
3314
|
+
}
|
|
3315
|
+
if (candidate.clearRecovery !== void 0) {
|
|
3316
|
+
const recovery = candidate.clearRecovery;
|
|
3317
|
+
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") {
|
|
3318
|
+
return null;
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3286
3321
|
return candidate;
|
|
3287
3322
|
}
|
|
3288
3323
|
function parseReclaimOwner(value) {
|
|
@@ -3523,13 +3558,18 @@ function isTransientIndexLockContention(error) {
|
|
|
3523
3558
|
if (!isIndexLockContentionError(error) || !("reason" in error)) return false;
|
|
3524
3559
|
return error.reason === "active" || error.reason === "reclaiming";
|
|
3525
3560
|
}
|
|
3526
|
-
function acquireIndexLock(indexPath, operation) {
|
|
3561
|
+
function acquireIndexLock(indexPath, operation, recoveryScope) {
|
|
3527
3562
|
(0, import_fs5.mkdirSync)(indexPath, { recursive: true });
|
|
3528
3563
|
const canonicalIndexPath = import_fs5.realpathSync.native(indexPath);
|
|
3529
3564
|
const lockPath = path9.join(canonicalIndexPath, "indexing.lock");
|
|
3530
3565
|
cleanupDeadPublicationCandidates(canonicalIndexPath);
|
|
3531
3566
|
for (let attempt = 0; attempt < 6; attempt += 1) {
|
|
3532
|
-
const owner = createOwner(operation)
|
|
3567
|
+
const owner = recoveryScope === void 0 ? createOwner(operation) : {
|
|
3568
|
+
...createOwner(operation),
|
|
3569
|
+
recoveryProtocolVersion: 1,
|
|
3570
|
+
projectRoot: recoveryScope.projectRoot,
|
|
3571
|
+
scopedRoots: recoveryScope.scopedRoots
|
|
3572
|
+
};
|
|
3533
3573
|
if (publishJsonDirectory(lockPath, owner)) {
|
|
3534
3574
|
const lease = {
|
|
3535
3575
|
canonicalIndexPath,
|
|
@@ -3594,6 +3634,33 @@ function releaseIndexLock(lease) {
|
|
|
3594
3634
|
}
|
|
3595
3635
|
return true;
|
|
3596
3636
|
}
|
|
3637
|
+
function setIndexLockClearRecoveryState(lease, clearRecovery) {
|
|
3638
|
+
const currentOwner = readDirectoryOwner(lease.lockPath);
|
|
3639
|
+
if (!currentOwner || !sameOwner(currentOwner, lease.owner)) {
|
|
3640
|
+
throw new Error(`Lost ownership of index mutation lease ${lease.owner.token}`);
|
|
3641
|
+
}
|
|
3642
|
+
const nextOwner = { ...currentOwner };
|
|
3643
|
+
if (clearRecovery === null) {
|
|
3644
|
+
delete nextOwner.clearRecovery;
|
|
3645
|
+
} else {
|
|
3646
|
+
nextOwner.clearRecovery = clearRecovery;
|
|
3647
|
+
}
|
|
3648
|
+
const ownerPath = path9.join(lease.lockPath, OWNER_FILE_NAME);
|
|
3649
|
+
const temporaryPath = path9.join(
|
|
3650
|
+
lease.lockPath,
|
|
3651
|
+
`${OWNER_FILE_NAME}.tmp.${lease.owner.pid}.${lease.owner.token}.${(0, import_crypto.randomUUID)()}`
|
|
3652
|
+
);
|
|
3653
|
+
try {
|
|
3654
|
+
(0, import_fs5.writeFileSync)(temporaryPath, JSON.stringify(nextOwner), {
|
|
3655
|
+
encoding: "utf-8",
|
|
3656
|
+
flag: "wx",
|
|
3657
|
+
mode: 384
|
|
3658
|
+
});
|
|
3659
|
+
retryTransientFilesystemOperation(() => (0, import_fs5.renameSync)(temporaryPath, ownerPath));
|
|
3660
|
+
} finally {
|
|
3661
|
+
if ((0, import_fs5.existsSync)(temporaryPath)) (0, import_fs5.rmSync)(temporaryPath, { force: true });
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3597
3664
|
function createLeaseTemporaryPath(targetPath, owner, kind = "tmp") {
|
|
3598
3665
|
if (kind === "bak") return `${targetPath}.bak.${owner.pid}.${owner.token}`;
|
|
3599
3666
|
temporaryCounter += 1;
|
|
@@ -5737,8 +5804,6 @@ async function tryDetectProvider() {
|
|
|
5737
5804
|
}
|
|
5738
5805
|
async function getProviderCredentials(provider) {
|
|
5739
5806
|
switch (provider) {
|
|
5740
|
-
case "github-copilot":
|
|
5741
|
-
return getGitHubCopilotCredentials();
|
|
5742
5807
|
case "openai":
|
|
5743
5808
|
return getOpenAICredentials();
|
|
5744
5809
|
case "google":
|
|
@@ -5749,22 +5814,6 @@ async function getProviderCredentials(provider) {
|
|
|
5749
5814
|
return null;
|
|
5750
5815
|
}
|
|
5751
5816
|
}
|
|
5752
|
-
function getGitHubCopilotCredentials() {
|
|
5753
|
-
const authData = loadOpenCodeAuth();
|
|
5754
|
-
const copilotAuth = authData["github-copilot"] || authData["github-copilot-enterprise"];
|
|
5755
|
-
if (!copilotAuth || copilotAuth.type !== "oauth") {
|
|
5756
|
-
return null;
|
|
5757
|
-
}
|
|
5758
|
-
const auth = copilotAuth;
|
|
5759
|
-
const baseUrl = auth.enterpriseUrl ? `https://copilot-api.${auth.enterpriseUrl.replace(/^https?:\/\//, "").replace(/\/$/, "")}` : "https://models.github.ai";
|
|
5760
|
-
return {
|
|
5761
|
-
provider: "github-copilot",
|
|
5762
|
-
baseUrl,
|
|
5763
|
-
refreshToken: copilotAuth.refresh,
|
|
5764
|
-
accessToken: copilotAuth.access,
|
|
5765
|
-
tokenExpires: copilotAuth.expires
|
|
5766
|
-
};
|
|
5767
|
-
}
|
|
5768
5817
|
function getOpenAICredentials() {
|
|
5769
5818
|
const authData = loadOpenCodeAuth();
|
|
5770
5819
|
const openaiAuth = authData["openai"];
|
|
@@ -5890,8 +5939,6 @@ async function tryDetectOllamaProvider() {
|
|
|
5890
5939
|
}
|
|
5891
5940
|
function getProviderDisplayName(provider) {
|
|
5892
5941
|
switch (provider) {
|
|
5893
|
-
case "github-copilot":
|
|
5894
|
-
return "GitHub Copilot";
|
|
5895
5942
|
case "openai":
|
|
5896
5943
|
return "OpenAI";
|
|
5897
5944
|
case "google":
|
|
@@ -6116,44 +6163,6 @@ var CustomEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
|
6116
6163
|
}
|
|
6117
6164
|
};
|
|
6118
6165
|
|
|
6119
|
-
// src/embeddings/providers/github-copilot.ts
|
|
6120
|
-
var GitHubCopilotEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
6121
|
-
constructor(credentials, modelInfo) {
|
|
6122
|
-
super(credentials, modelInfo);
|
|
6123
|
-
}
|
|
6124
|
-
getToken() {
|
|
6125
|
-
if (!this.credentials.refreshToken) {
|
|
6126
|
-
throw new Error("No OAuth token available for GitHub");
|
|
6127
|
-
}
|
|
6128
|
-
return this.credentials.refreshToken;
|
|
6129
|
-
}
|
|
6130
|
-
async embedBatch(texts) {
|
|
6131
|
-
const token = this.getToken();
|
|
6132
|
-
const response = await fetch(`${this.credentials.baseUrl}/inference/embeddings`, {
|
|
6133
|
-
method: "POST",
|
|
6134
|
-
headers: {
|
|
6135
|
-
Authorization: `Bearer ${token}`,
|
|
6136
|
-
"Content-Type": "application/json",
|
|
6137
|
-
Accept: "application/vnd.github+json",
|
|
6138
|
-
"X-GitHub-Api-Version": "2022-11-28"
|
|
6139
|
-
},
|
|
6140
|
-
body: JSON.stringify({
|
|
6141
|
-
model: `openai/${this.modelInfo.model}`,
|
|
6142
|
-
input: texts
|
|
6143
|
-
})
|
|
6144
|
-
});
|
|
6145
|
-
if (!response.ok) {
|
|
6146
|
-
const error = (await response.text()).slice(0, 500);
|
|
6147
|
-
throw new Error(`GitHub Copilot embedding API error: ${response.status} - ${error}`);
|
|
6148
|
-
}
|
|
6149
|
-
const data = await response.json();
|
|
6150
|
-
return {
|
|
6151
|
-
embeddings: data.data.map((d) => d.embedding),
|
|
6152
|
-
totalTokensUsed: data.usage.total_tokens
|
|
6153
|
-
};
|
|
6154
|
-
}
|
|
6155
|
-
};
|
|
6156
|
-
|
|
6157
6166
|
// src/embeddings/providers/google.ts
|
|
6158
6167
|
var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddingProvider {
|
|
6159
6168
|
static BATCH_SIZE = 20;
|
|
@@ -6161,24 +6170,30 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
6161
6170
|
super(credentials, modelInfo);
|
|
6162
6171
|
}
|
|
6163
6172
|
async embedQuery(query) {
|
|
6164
|
-
const taskType = this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
6165
|
-
const
|
|
6173
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "CODE_RETRIEVAL_QUERY" : void 0;
|
|
6174
|
+
const texts = [
|
|
6175
|
+
this.modelInfo.model === "gemini-embedding-2" ? `task: code retrieval | query: ${query}` : query
|
|
6176
|
+
];
|
|
6177
|
+
const result = await this.embedWithTaskType(texts, taskType);
|
|
6166
6178
|
return {
|
|
6167
6179
|
embedding: result.embeddings[0],
|
|
6168
6180
|
tokensUsed: result.totalTokensUsed
|
|
6169
6181
|
};
|
|
6170
6182
|
}
|
|
6171
6183
|
async embedDocument(document) {
|
|
6172
|
-
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6173
|
-
const result = await this.embedWithTaskType([
|
|
6184
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6185
|
+
const result = await this.embedWithTaskType([
|
|
6186
|
+
this.modelInfo.model === "gemini-embedding-2" ? `title: none | text: ${document}` : document
|
|
6187
|
+
], taskType);
|
|
6174
6188
|
return {
|
|
6175
6189
|
embedding: result.embeddings[0],
|
|
6176
6190
|
tokensUsed: result.totalTokensUsed
|
|
6177
6191
|
};
|
|
6178
6192
|
}
|
|
6179
6193
|
async embedBatch(texts) {
|
|
6180
|
-
const taskType = this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6181
|
-
|
|
6194
|
+
const taskType = this.modelInfo.model === "gemini-embedding-001" && this.modelInfo.taskAble ? "RETRIEVAL_DOCUMENT" : void 0;
|
|
6195
|
+
const formattedTexts = this.modelInfo.model === "gemini-embedding-2" ? texts.map((text) => `title: none | text: ${text}`) : texts;
|
|
6196
|
+
return this.embedWithTaskType(formattedTexts, taskType);
|
|
6182
6197
|
}
|
|
6183
6198
|
async embedWithTaskType(texts, taskType) {
|
|
6184
6199
|
const batches = [];
|
|
@@ -6228,6 +6243,10 @@ var GoogleEmbeddingProvider = class _GoogleEmbeddingProvider extends BaseEmbeddi
|
|
|
6228
6243
|
var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddingProvider {
|
|
6229
6244
|
static MIN_TRUNCATION_CHARS = 512;
|
|
6230
6245
|
static REQUEST_TIMEOUT_MS = 12e4;
|
|
6246
|
+
// Set when /api/embed returns 404 so subsequent multi-text batches skip the
|
|
6247
|
+
// batched endpoint and go straight to the legacy per-text path (one probe per
|
|
6248
|
+
// old ollama install, not one probe per batch).
|
|
6249
|
+
batchEndpointUnavailable = false;
|
|
6231
6250
|
constructor(credentials, modelInfo) {
|
|
6232
6251
|
super(credentials, modelInfo);
|
|
6233
6252
|
}
|
|
@@ -6245,6 +6264,21 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
6245
6264
|
const message = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
6246
6265
|
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");
|
|
6247
6266
|
}
|
|
6267
|
+
// True for a 404 from the newer /api/embed endpoint, i.e. an ollama version that
|
|
6268
|
+
// does not provide it. embedBatch uses this to fall back to the legacy per-text
|
|
6269
|
+
// /api/embeddings path so old ollama installs do not regress.
|
|
6270
|
+
isBatchEndpointUnavailableError(error) {
|
|
6271
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6272
|
+
return message.includes("Ollama /api/embed not available");
|
|
6273
|
+
}
|
|
6274
|
+
// True for a malformed /api/embed response (wrong vector count or a bad vector).
|
|
6275
|
+
// embedBatch falls back to the per-text path on this so a bad batch response
|
|
6276
|
+
// re-embeds each text cleanly. A text that then fails per-text is not isolated
|
|
6277
|
+
// here; it is isolated on the recovery run, which re-embeds one text per request.
|
|
6278
|
+
isBatchValidationError(error) {
|
|
6279
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6280
|
+
return message.includes("invalid embedding batch");
|
|
6281
|
+
}
|
|
6248
6282
|
buildTruncationCandidates(text) {
|
|
6249
6283
|
const baseMaxChars = Math.max(1, this.modelInfo.maxTokens * 4);
|
|
6250
6284
|
const candidateLimits = /* @__PURE__ */ new Set();
|
|
@@ -6346,7 +6380,74 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
6346
6380
|
tokensUsed: this.estimateTokens(text)
|
|
6347
6381
|
};
|
|
6348
6382
|
}
|
|
6349
|
-
|
|
6383
|
+
// Embeds many texts in one POST /api/embed request (input: string[]). Ollama
|
|
6384
|
+
// encodes each input independently, so the model context length applies per input
|
|
6385
|
+
// (the upstream splitter already bounds each input), not over the batch. This
|
|
6386
|
+
// amortizes N HTTP round-trips into one.
|
|
6387
|
+
async embedMany(texts) {
|
|
6388
|
+
const controller = new AbortController();
|
|
6389
|
+
const timeout = setTimeout(
|
|
6390
|
+
() => controller.abort(),
|
|
6391
|
+
_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS
|
|
6392
|
+
);
|
|
6393
|
+
let response;
|
|
6394
|
+
try {
|
|
6395
|
+
response = await fetch(`${this.credentials.baseUrl}/api/embed`, {
|
|
6396
|
+
method: "POST",
|
|
6397
|
+
headers: {
|
|
6398
|
+
"Content-Type": "application/json"
|
|
6399
|
+
},
|
|
6400
|
+
body: JSON.stringify({
|
|
6401
|
+
model: this.modelInfo.model,
|
|
6402
|
+
input: texts,
|
|
6403
|
+
truncate: false
|
|
6404
|
+
}),
|
|
6405
|
+
signal: controller.signal
|
|
6406
|
+
});
|
|
6407
|
+
} catch (error) {
|
|
6408
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
6409
|
+
throw new Error(
|
|
6410
|
+
`Ollama embedding request timed out after ${_OllamaEmbeddingProvider.REQUEST_TIMEOUT_MS}ms`
|
|
6411
|
+
);
|
|
6412
|
+
}
|
|
6413
|
+
throw error;
|
|
6414
|
+
} finally {
|
|
6415
|
+
clearTimeout(timeout);
|
|
6416
|
+
}
|
|
6417
|
+
if (!response.ok) {
|
|
6418
|
+
const error = (await response.text()).slice(0, 500);
|
|
6419
|
+
if (response.status === 404) {
|
|
6420
|
+
throw new Error(`Ollama /api/embed not available: ${response.status} - ${error}`);
|
|
6421
|
+
}
|
|
6422
|
+
throw new Error(`Ollama embedding API error: ${response.status} - ${error}`);
|
|
6423
|
+
}
|
|
6424
|
+
let parsed;
|
|
6425
|
+
try {
|
|
6426
|
+
parsed = await response.json();
|
|
6427
|
+
} catch {
|
|
6428
|
+
throw new Error(
|
|
6429
|
+
`Ollama returned an invalid embedding batch; expected ${texts.length} vectors of ${this.modelInfo.dimensions} finite dimensions`
|
|
6430
|
+
);
|
|
6431
|
+
}
|
|
6432
|
+
const data = parsed && typeof parsed === "object" ? parsed : {};
|
|
6433
|
+
if (!Array.isArray(data.embeddings) || data.embeddings.length !== texts.length || data.embeddings.some(
|
|
6434
|
+
(value) => !Array.isArray(value) || value.length !== this.modelInfo.dimensions || value.some((v) => typeof v !== "number" || !Number.isFinite(v))
|
|
6435
|
+
)) {
|
|
6436
|
+
throw new Error(
|
|
6437
|
+
`Ollama returned an invalid embedding batch; expected ${texts.length} vectors of ${this.modelInfo.dimensions} finite dimensions`
|
|
6438
|
+
);
|
|
6439
|
+
}
|
|
6440
|
+
return {
|
|
6441
|
+
embeddings: data.embeddings,
|
|
6442
|
+
totalTokensUsed: texts.reduce((sum, text) => sum + this.estimateTokens(text), 0)
|
|
6443
|
+
};
|
|
6444
|
+
}
|
|
6445
|
+
// Per-text /api/embeddings path shared by the single-text fast path and the
|
|
6446
|
+
// batch fallback. Uses the legacy endpoint one text at a time, so each text gets
|
|
6447
|
+
// its own truncation safety net and a vector validated on its own. A text that
|
|
6448
|
+
// hard-fails per-text throws here and fails the whole request batch; the recovery
|
|
6449
|
+
// run re-embeds one text per request to isolate it.
|
|
6450
|
+
async embedOneByOne(texts) {
|
|
6350
6451
|
const results = [];
|
|
6351
6452
|
for (const text of texts) {
|
|
6352
6453
|
results.push(await this.embedSingleWithFallback(text));
|
|
@@ -6356,6 +6457,26 @@ var OllamaEmbeddingProvider = class _OllamaEmbeddingProvider extends BaseEmbeddi
|
|
|
6356
6457
|
totalTokensUsed: results.reduce((sum, r) => sum + r.tokensUsed, 0)
|
|
6357
6458
|
};
|
|
6358
6459
|
}
|
|
6460
|
+
async embedBatch(texts) {
|
|
6461
|
+
if (texts.length === 0) {
|
|
6462
|
+
return { embeddings: [], totalTokensUsed: 0 };
|
|
6463
|
+
}
|
|
6464
|
+
if (texts.length === 1 || this.batchEndpointUnavailable) {
|
|
6465
|
+
return this.embedOneByOne(texts);
|
|
6466
|
+
}
|
|
6467
|
+
try {
|
|
6468
|
+
return await this.embedMany(texts);
|
|
6469
|
+
} catch (error) {
|
|
6470
|
+
if (this.isBatchEndpointUnavailableError(error)) {
|
|
6471
|
+
this.batchEndpointUnavailable = true;
|
|
6472
|
+
return this.embedOneByOne(texts);
|
|
6473
|
+
}
|
|
6474
|
+
if (!this.isContextLengthError(error) && !this.isBatchValidationError(error)) {
|
|
6475
|
+
throw error;
|
|
6476
|
+
}
|
|
6477
|
+
return this.embedOneByOne(texts);
|
|
6478
|
+
}
|
|
6479
|
+
}
|
|
6359
6480
|
};
|
|
6360
6481
|
|
|
6361
6482
|
// src/embeddings/providers/openai.ts
|
|
@@ -6390,8 +6511,6 @@ var OpenAIEmbeddingProvider = class extends BaseEmbeddingProvider {
|
|
|
6390
6511
|
// src/embeddings/provider.ts
|
|
6391
6512
|
function createEmbeddingProvider(configuredProviderInfo) {
|
|
6392
6513
|
switch (configuredProviderInfo.provider) {
|
|
6393
|
-
case "github-copilot":
|
|
6394
|
-
return new GitHubCopilotEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
6395
6514
|
case "openai":
|
|
6396
6515
|
return new OpenAIEmbeddingProvider(configuredProviderInfo.credentials, configuredProviderInfo.modelInfo);
|
|
6397
6516
|
case "google":
|
|
@@ -7143,12 +7262,12 @@ try {
|
|
|
7143
7262
|
}
|
|
7144
7263
|
|
|
7145
7264
|
// src/native/parsing.ts
|
|
7146
|
-
function parseFileAsText(filePath, content) {
|
|
7147
|
-
const result = native.parseFileAsText(filePath, content);
|
|
7265
|
+
function parseFileAsText(filePath, content, linesPerChunk) {
|
|
7266
|
+
const result = native.parseFileAsText(filePath, content, linesPerChunk);
|
|
7148
7267
|
return result.map(mapChunk);
|
|
7149
7268
|
}
|
|
7150
|
-
function parseFiles(files) {
|
|
7151
|
-
const result = native.parseFiles(files);
|
|
7269
|
+
function parseFiles(files, linesPerChunk) {
|
|
7270
|
+
const result = native.parseFiles(files, linesPerChunk);
|
|
7152
7271
|
return result.map((f) => ({
|
|
7153
7272
|
path: f.path,
|
|
7154
7273
|
chunks: f.chunks.map(mapChunk),
|
|
@@ -7225,13 +7344,13 @@ var VectorStore = class {
|
|
|
7225
7344
|
const metadata = items.map((i) => JSON.stringify(i.metadata));
|
|
7226
7345
|
this.inner.addBatch(ids, vectors, metadata);
|
|
7227
7346
|
}
|
|
7228
|
-
search(queryVector, limit = 10) {
|
|
7347
|
+
search(queryVector, limit = 10, allowedIds) {
|
|
7229
7348
|
if (queryVector.length !== this.dimensions) {
|
|
7230
7349
|
throw new Error(
|
|
7231
7350
|
`Query vector dimension mismatch: expected ${this.dimensions}, got ${queryVector.length}`
|
|
7232
7351
|
);
|
|
7233
7352
|
}
|
|
7234
|
-
const results = this.inner.search(queryVector, limit);
|
|
7353
|
+
const results = allowedIds === void 0 ? this.inner.search(queryVector, limit) : this.inner.searchFiltered(queryVector, limit, allowedIds);
|
|
7235
7354
|
return results.map((r) => ({
|
|
7236
7355
|
id: r.id,
|
|
7237
7356
|
score: r.score,
|
|
@@ -7459,6 +7578,10 @@ var Database = class _Database {
|
|
|
7459
7578
|
this.throwIfClosed();
|
|
7460
7579
|
return this.inner.getBranchChunkIds(branch);
|
|
7461
7580
|
}
|
|
7581
|
+
getChunkIdsByBlameDate(since, until) {
|
|
7582
|
+
this.throwIfClosed();
|
|
7583
|
+
return this.inner.getChunkIdsByBlameDate(since, until);
|
|
7584
|
+
}
|
|
7462
7585
|
getBranchDelta(branch, baseBranch) {
|
|
7463
7586
|
this.throwIfClosed();
|
|
7464
7587
|
return this.inner.getBranchDelta(branch, baseBranch);
|
|
@@ -9347,6 +9470,18 @@ function createFailedBatchWriter(targetPath) {
|
|
|
9347
9470
|
temporaryPath
|
|
9348
9471
|
};
|
|
9349
9472
|
}
|
|
9473
|
+
function writeFailedBatchRecords(targetPath, records) {
|
|
9474
|
+
const writer = createFailedBatchWriter(targetPath);
|
|
9475
|
+
try {
|
|
9476
|
+
for (const record of records) {
|
|
9477
|
+
writer.write(record);
|
|
9478
|
+
}
|
|
9479
|
+
writer.commit();
|
|
9480
|
+
} catch (error) {
|
|
9481
|
+
writer.cleanup();
|
|
9482
|
+
throw error;
|
|
9483
|
+
}
|
|
9484
|
+
}
|
|
9350
9485
|
function* readLegacyFailedBatchRecords(filePath, options) {
|
|
9351
9486
|
const rawData = fs2.readFileSync(filePath, "utf-8");
|
|
9352
9487
|
const trimmed = stripLeadingBomAndWhitespace(rawData).trim();
|
|
@@ -9629,14 +9764,18 @@ function getSafeEmbeddingChunkTokenLimit(provider) {
|
|
|
9629
9764
|
const maxChunkTokens = Math.max(256, Math.floor(providerMaxTokens * 0.75));
|
|
9630
9765
|
return Math.min(2e3, maxChunkTokens);
|
|
9631
9766
|
}
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
};
|
|
9767
|
+
var DEFAULT_OLLAMA_MAX_BATCH_ITEMS = 16;
|
|
9768
|
+
var DEFAULT_OLLAMA_MAX_BATCH_TOKENS = 65536;
|
|
9769
|
+
function getDynamicBatchOptions(provider, embeddingBatch) {
|
|
9770
|
+
if (provider.provider !== "ollama") {
|
|
9771
|
+
return {};
|
|
9638
9772
|
}
|
|
9639
|
-
|
|
9773
|
+
const base = { maxBatchTokens: DEFAULT_OLLAMA_MAX_BATCH_TOKENS, maxBatchItems: DEFAULT_OLLAMA_MAX_BATCH_ITEMS };
|
|
9774
|
+
return {
|
|
9775
|
+
...base,
|
|
9776
|
+
...typeof embeddingBatch?.maxBatchTokens === "number" && Number.isFinite(embeddingBatch.maxBatchTokens) ? { maxBatchTokens: embeddingBatch.maxBatchTokens } : {},
|
|
9777
|
+
...typeof embeddingBatch?.maxBatchItems === "number" && Number.isFinite(embeddingBatch.maxBatchItems) ? { maxBatchItems: embeddingBatch.maxBatchItems } : {}
|
|
9778
|
+
};
|
|
9640
9779
|
}
|
|
9641
9780
|
function isSqliteCorruptionError(error) {
|
|
9642
9781
|
const message = getErrorMessage4(error).toLowerCase();
|
|
@@ -9654,6 +9793,14 @@ function getPendingChunkId(rawChunk) {
|
|
|
9654
9793
|
const id = rawChunk.id;
|
|
9655
9794
|
return typeof id === "string" ? id : null;
|
|
9656
9795
|
}
|
|
9796
|
+
function parseBlameTimestamp(value, endOfDay) {
|
|
9797
|
+
let timestampMs = Date.parse(value);
|
|
9798
|
+
if (Number.isNaN(timestampMs)) return null;
|
|
9799
|
+
if (endOfDay && /^\d{4}-\d{2}-\d{2}$/.test(value.trim())) {
|
|
9800
|
+
timestampMs += 24 * 60 * 60 * 1e3 - 1;
|
|
9801
|
+
}
|
|
9802
|
+
return Math.floor(timestampMs / 1e3);
|
|
9803
|
+
}
|
|
9657
9804
|
function metadataFromBlame(blame) {
|
|
9658
9805
|
if (!blame) {
|
|
9659
9806
|
return {};
|
|
@@ -9800,7 +9947,7 @@ function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCa
|
|
|
9800
9947
|
const remainder = combined.filter((candidate) => !promotedIds.has(candidate.id));
|
|
9801
9948
|
return [...promoted, ...remainder];
|
|
9802
9949
|
}
|
|
9803
|
-
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
9950
|
+
function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, limit, fallbackCandidates, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source", allowNonSourcePaths = false) {
|
|
9804
9951
|
if (!prioritizeSourcePaths) {
|
|
9805
9952
|
return [];
|
|
9806
9953
|
}
|
|
@@ -9820,7 +9967,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
9820
9967
|
if (!isImplementationChunkType(chunkType)) {
|
|
9821
9968
|
return false;
|
|
9822
9969
|
}
|
|
9823
|
-
if (!isLikelyImplementationPath2(chunk.filePath)) {
|
|
9970
|
+
if (!allowNonSourcePaths && !isLikelyImplementationPath2(chunk.filePath)) {
|
|
9824
9971
|
return false;
|
|
9825
9972
|
}
|
|
9826
9973
|
const nameLower = (chunk.name ?? "").toLowerCase();
|
|
@@ -9884,7 +10031,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
9884
10031
|
}
|
|
9885
10032
|
foundCoveringChunk = upsertChunkCandidate(chunk, identifier, normalizedIdentifier) || foundCoveringChunk;
|
|
9886
10033
|
}
|
|
9887
|
-
if (foundCoveringChunk || !isLikelyImplementationPath2(symbol.filePath)) {
|
|
10034
|
+
if (foundCoveringChunk || !allowNonSourcePaths && !isLikelyImplementationPath2(symbol.filePath)) {
|
|
9888
10035
|
continue;
|
|
9889
10036
|
}
|
|
9890
10037
|
const symbolName = symbol.name.toLowerCase();
|
|
@@ -9938,7 +10085,7 @@ function buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbol
|
|
|
9938
10085
|
const ranked = Array.from(symbolCandidates.values()).sort((a, b) => b.score - a.score || a.id.localeCompare(b.id));
|
|
9939
10086
|
if (ranked.length === 0) {
|
|
9940
10087
|
const implementationFallback = fallbackCandidates.filter(
|
|
9941
|
-
(candidate) => isImplementationChunkType(candidate.metadata.chunkType) && isLikelyImplementationPath2(candidate.metadata.filePath)
|
|
10088
|
+
(candidate) => isImplementationChunkType(candidate.metadata.chunkType) && (allowNonSourcePaths || isLikelyImplementationPath2(candidate.metadata.filePath))
|
|
9942
10089
|
);
|
|
9943
10090
|
for (const candidate of implementationFallback) {
|
|
9944
10091
|
const nameLower = (candidate.metadata.name ?? "").toLowerCase();
|
|
@@ -10054,10 +10201,16 @@ function matchesHardSearchFilters(candidate, options, projectRoot) {
|
|
|
10054
10201
|
return false;
|
|
10055
10202
|
}
|
|
10056
10203
|
if (options?.blameSince) {
|
|
10057
|
-
const
|
|
10058
|
-
if (
|
|
10204
|
+
const since = parseBlameTimestamp(options.blameSince, false);
|
|
10205
|
+
if (since === null) return false;
|
|
10206
|
+
const committedAt = candidate.metadata.blameCommittedAt;
|
|
10207
|
+
if (committedAt === void 0 || committedAt < since) return false;
|
|
10208
|
+
}
|
|
10209
|
+
if (options?.blameUntil) {
|
|
10210
|
+
const until = parseBlameTimestamp(options.blameUntil, true);
|
|
10211
|
+
if (until === null) return false;
|
|
10059
10212
|
const committedAt = candidate.metadata.blameCommittedAt;
|
|
10060
|
-
if (committedAt === void 0 || committedAt
|
|
10213
|
+
if (committedAt === void 0 || committedAt > until) return false;
|
|
10061
10214
|
}
|
|
10062
10215
|
return true;
|
|
10063
10216
|
}
|
|
@@ -10113,9 +10266,10 @@ var Indexer = class _Indexer {
|
|
|
10113
10266
|
writerArtifactFingerprint = null;
|
|
10114
10267
|
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
10115
10268
|
fileBatchLimits;
|
|
10269
|
+
checkpointIntervalChunks;
|
|
10116
10270
|
constructor(projectRoot, config, host, runtimeOptions = {}) {
|
|
10117
10271
|
this.projectRoot = projectRoot;
|
|
10118
|
-
this.projectIdentityHash =
|
|
10272
|
+
this.projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
10119
10273
|
this.materializedProjectRoot = runtimeOptions.materializedProjectRoot ?? projectRoot;
|
|
10120
10274
|
this.branchNameOverride = runtimeOptions.branchName;
|
|
10121
10275
|
this.catalogIdentityOverride = runtimeOptions.catalogIdentity;
|
|
@@ -10125,6 +10279,7 @@ var Indexer = class _Indexer {
|
|
|
10125
10279
|
this.expectedCommitOverride = runtimeOptions.expectedCommit?.toLowerCase();
|
|
10126
10280
|
this.indexPathOverride = runtimeOptions.indexPath;
|
|
10127
10281
|
this.fileBatchLimits = runtimeOptions.fileBatchLimits;
|
|
10282
|
+
this.checkpointIntervalChunks = runtimeOptions.checkpointIntervalChunks;
|
|
10128
10283
|
this.config = config;
|
|
10129
10284
|
this.host = host;
|
|
10130
10285
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -10236,6 +10391,9 @@ var Indexer = class _Indexer {
|
|
|
10236
10391
|
return path19.resolve(targetPath);
|
|
10237
10392
|
}
|
|
10238
10393
|
}
|
|
10394
|
+
getProjectIdentityHash(projectRoot) {
|
|
10395
|
+
return hashContent(this.getCanonicalPath(projectRoot)).slice(0, 16);
|
|
10396
|
+
}
|
|
10239
10397
|
isProjectOwnedIndexPath() {
|
|
10240
10398
|
return isProjectIndexPathOwnedByProject(this.projectRoot, this.indexPath, this.host);
|
|
10241
10399
|
}
|
|
@@ -10272,7 +10430,10 @@ var Indexer = class _Indexer {
|
|
|
10272
10430
|
}
|
|
10273
10431
|
async withIndexMutationLease(operation, callback) {
|
|
10274
10432
|
this.refreshBranchInfo();
|
|
10275
|
-
const lease = acquireIndexLock(this.indexPath, operation
|
|
10433
|
+
const lease = acquireIndexLock(this.indexPath, operation, {
|
|
10434
|
+
projectRoot: this.projectRoot,
|
|
10435
|
+
scopedRoots: this.getScopedRoots()
|
|
10436
|
+
});
|
|
10276
10437
|
this.indexPath = lease.canonicalIndexPath;
|
|
10277
10438
|
this.refreshRuntimeArtifactPaths();
|
|
10278
10439
|
this.activeIndexLease = lease;
|
|
@@ -10327,6 +10488,7 @@ var Indexer = class _Indexer {
|
|
|
10327
10488
|
}
|
|
10328
10489
|
loadFileHashCache() {
|
|
10329
10490
|
if (!(0, import_fs12.existsSync)(this.fileHashCachePath)) {
|
|
10491
|
+
this.fileHashCache = /* @__PURE__ */ new Map();
|
|
10330
10492
|
return;
|
|
10331
10493
|
}
|
|
10332
10494
|
try {
|
|
@@ -10366,10 +10528,10 @@ var Indexer = class _Indexer {
|
|
|
10366
10528
|
invertedIndex.serialize()
|
|
10367
10529
|
);
|
|
10368
10530
|
}
|
|
10369
|
-
getScopedRoots() {
|
|
10370
|
-
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(
|
|
10531
|
+
getScopedRoots(projectRoot = this.projectRoot) {
|
|
10532
|
+
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(projectRoot)]);
|
|
10371
10533
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
10372
|
-
roots.add(this.getCanonicalPath(path19.resolve(
|
|
10534
|
+
roots.add(this.getCanonicalPath(path19.resolve(projectRoot, kbRoot)));
|
|
10373
10535
|
}
|
|
10374
10536
|
return Array.from(roots);
|
|
10375
10537
|
}
|
|
@@ -10440,14 +10602,17 @@ var Indexer = class _Indexer {
|
|
|
10440
10602
|
getLegacyBranchCatalogKey() {
|
|
10441
10603
|
return this.currentBranch || "default";
|
|
10442
10604
|
}
|
|
10443
|
-
getLegacyMigrationMetadataKey() {
|
|
10444
|
-
return `index.globalBranchMigration.${
|
|
10605
|
+
getLegacyMigrationMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10606
|
+
return `index.globalBranchMigration.${projectIdentityHash}`;
|
|
10607
|
+
}
|
|
10608
|
+
getProjectEmbeddingStrategyMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10609
|
+
return `index.embeddingStrategyVersion.${projectIdentityHash}`;
|
|
10445
10610
|
}
|
|
10446
|
-
|
|
10447
|
-
return `index.
|
|
10611
|
+
getProjectForceReembedMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10612
|
+
return `index.forceReembed.${projectIdentityHash}`;
|
|
10448
10613
|
}
|
|
10449
|
-
|
|
10450
|
-
return `index.
|
|
10614
|
+
getProjectMigrationFinalizedMetadataKey(projectIdentityHash = this.projectIdentityHash) {
|
|
10615
|
+
return `index.migrationFinalized.${projectIdentityHash}`;
|
|
10451
10616
|
}
|
|
10452
10617
|
getBranchMigrationMetadataKey(prefix, catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
10453
10618
|
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
@@ -10553,7 +10718,7 @@ var Indexer = class _Indexer {
|
|
|
10553
10718
|
const legacy = this.getLegacyBranchCatalogKey();
|
|
10554
10719
|
return primary === legacy ? [primary] : [primary, legacy];
|
|
10555
10720
|
}
|
|
10556
|
-
getProjectLocalScopedOwnershipIds(roots) {
|
|
10721
|
+
getProjectLocalScopedOwnershipIds(roots, projectRoot = this.projectRoot) {
|
|
10557
10722
|
const chunkIds = /* @__PURE__ */ new Set();
|
|
10558
10723
|
const symbolIds = /* @__PURE__ */ new Set();
|
|
10559
10724
|
if (!this.database) {
|
|
@@ -10561,10 +10726,10 @@ var Indexer = class _Indexer {
|
|
|
10561
10726
|
}
|
|
10562
10727
|
const projectLocalFilePaths = /* @__PURE__ */ new Set([
|
|
10563
10728
|
...Array.from(this.fileHashCache.keys()).filter(
|
|
10564
|
-
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath)
|
|
10729
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath, projectRoot)
|
|
10565
10730
|
),
|
|
10566
10731
|
...(this.store?.getAllMetadata() ?? []).map(({ metadata }) => metadata.filePath).filter(
|
|
10567
|
-
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath)
|
|
10732
|
+
(filePath) => this.isFileInCurrentScope(filePath, roots) && this.isFileInProjectRoot(filePath, projectRoot)
|
|
10568
10733
|
)
|
|
10569
10734
|
]);
|
|
10570
10735
|
for (const filePath of projectLocalFilePaths) {
|
|
@@ -10577,15 +10742,16 @@ var Indexer = class _Indexer {
|
|
|
10577
10742
|
}
|
|
10578
10743
|
return { chunkIds, symbolIds };
|
|
10579
10744
|
}
|
|
10580
|
-
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds) {
|
|
10745
|
+
getProjectScopedBranchCatalogCleanupKeys(projectChunkIds, projectSymbolIds, projectRoot = this.projectRoot) {
|
|
10581
10746
|
if (this.config.scope !== "global") {
|
|
10582
10747
|
return this.getBranchCatalogCleanupKeys();
|
|
10583
10748
|
}
|
|
10584
10749
|
const keys = /* @__PURE__ */ new Set();
|
|
10585
10750
|
const projectChunkIdSet = new Set(projectChunkIds);
|
|
10586
10751
|
const projectSymbolIdSet = new Set(projectSymbolIds);
|
|
10752
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
10587
10753
|
for (const branchKey of this.database?.getAllBranches() ?? []) {
|
|
10588
|
-
if (branchKey.startsWith(`${
|
|
10754
|
+
if (branchKey.startsWith(`${projectIdentityHash}:`)) {
|
|
10589
10755
|
keys.add(branchKey);
|
|
10590
10756
|
continue;
|
|
10591
10757
|
}
|
|
@@ -10595,8 +10761,10 @@ var Indexer = class _Indexer {
|
|
|
10595
10761
|
keys.add(branchKey);
|
|
10596
10762
|
}
|
|
10597
10763
|
}
|
|
10598
|
-
|
|
10599
|
-
|
|
10764
|
+
if (projectRoot === this.projectRoot) {
|
|
10765
|
+
for (const branchKey of this.getBranchCatalogCleanupKeys()) {
|
|
10766
|
+
keys.add(branchKey);
|
|
10767
|
+
}
|
|
10600
10768
|
}
|
|
10601
10769
|
return Array.from(keys);
|
|
10602
10770
|
}
|
|
@@ -10604,10 +10772,10 @@ var Indexer = class _Indexer {
|
|
|
10604
10772
|
const canonicalFilePath = this.getCanonicalStoredFilePath(filePath);
|
|
10605
10773
|
return roots.some((root) => isPathWithinRoot2(canonicalFilePath, root));
|
|
10606
10774
|
}
|
|
10607
|
-
isFileInProjectRoot(filePath) {
|
|
10775
|
+
isFileInProjectRoot(filePath, projectRoot = this.projectRoot) {
|
|
10608
10776
|
return isPathWithinRoot2(
|
|
10609
10777
|
this.getCanonicalStoredFilePath(filePath),
|
|
10610
|
-
this.getCanonicalPath(
|
|
10778
|
+
this.getCanonicalPath(projectRoot)
|
|
10611
10779
|
);
|
|
10612
10780
|
}
|
|
10613
10781
|
clearScopedFileHashCache(roots) {
|
|
@@ -10649,12 +10817,12 @@ var Indexer = class _Indexer {
|
|
|
10649
10817
|
}
|
|
10650
10818
|
return false;
|
|
10651
10819
|
}
|
|
10652
|
-
hasForeignScopedBranchData() {
|
|
10820
|
+
hasForeignScopedBranchData(projectRoot = this.projectRoot, roots = this.getScopedRoots(projectRoot)) {
|
|
10653
10821
|
if (!this.database || this.config.scope !== "global") {
|
|
10654
10822
|
return false;
|
|
10655
10823
|
}
|
|
10656
|
-
const
|
|
10657
|
-
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots);
|
|
10824
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
10825
|
+
const { chunkIds: projectLocalChunkIds, symbolIds: projectLocalSymbolIds } = this.getProjectLocalScopedOwnershipIds(roots, projectRoot);
|
|
10658
10826
|
return this.database.getAllBranches().some(
|
|
10659
10827
|
(branchKey) => {
|
|
10660
10828
|
const branchChunkIds = this.database.getBranchChunkIds(branchKey);
|
|
@@ -10663,7 +10831,7 @@ var Indexer = class _Indexer {
|
|
|
10663
10831
|
if (!hasBranchData) {
|
|
10664
10832
|
return false;
|
|
10665
10833
|
}
|
|
10666
|
-
if (branchKey.startsWith(`${
|
|
10834
|
+
if (branchKey.startsWith(`${projectIdentityHash}:`)) {
|
|
10667
10835
|
return false;
|
|
10668
10836
|
}
|
|
10669
10837
|
const referencesCurrentProjectChunks = branchChunkIds.some((chunkId) => projectLocalChunkIds.has(chunkId));
|
|
@@ -10672,7 +10840,7 @@ var Indexer = class _Indexer {
|
|
|
10672
10840
|
}
|
|
10673
10841
|
);
|
|
10674
10842
|
}
|
|
10675
|
-
clearSharedIndexProjectData(store, invertedIndex, database, roots) {
|
|
10843
|
+
clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot = this.projectRoot) {
|
|
10676
10844
|
const allMetadata = store.getAllMetadata();
|
|
10677
10845
|
const scopedEntries = allMetadata.filter(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
10678
10846
|
const filePaths = /* @__PURE__ */ new Set([
|
|
@@ -10680,7 +10848,7 @@ var Indexer = class _Indexer {
|
|
|
10680
10848
|
...scopedEntries.map(({ metadata }) => metadata.filePath)
|
|
10681
10849
|
]);
|
|
10682
10850
|
const projectLocalFilePaths = new Set(
|
|
10683
|
-
Array.from(filePaths).filter((filePath) => this.isFileInProjectRoot(filePath))
|
|
10851
|
+
Array.from(filePaths).filter((filePath) => this.isFileInProjectRoot(filePath, projectRoot))
|
|
10684
10852
|
);
|
|
10685
10853
|
const removedChunkIds = new Set(scopedEntries.map(({ key }) => key));
|
|
10686
10854
|
for (const filePath of filePaths) {
|
|
@@ -10690,7 +10858,7 @@ var Indexer = class _Indexer {
|
|
|
10690
10858
|
}
|
|
10691
10859
|
const removedChunkIdList = Array.from(removedChunkIds);
|
|
10692
10860
|
const projectLocalChunkIds = new Set(
|
|
10693
|
-
scopedEntries.filter(({ metadata }) => this.isFileInProjectRoot(metadata.filePath)).map(({ key }) => key)
|
|
10861
|
+
scopedEntries.filter(({ metadata }) => this.isFileInProjectRoot(metadata.filePath, projectRoot)).map(({ key }) => key)
|
|
10694
10862
|
);
|
|
10695
10863
|
for (const filePath of projectLocalFilePaths) {
|
|
10696
10864
|
for (const chunk of database.getChunksByFile(filePath)) {
|
|
@@ -10709,7 +10877,8 @@ var Indexer = class _Indexer {
|
|
|
10709
10877
|
}
|
|
10710
10878
|
const branchCleanupKeys = this.getProjectScopedBranchCatalogCleanupKeys(
|
|
10711
10879
|
Array.from(projectLocalChunkIds),
|
|
10712
|
-
Array.from(projectLocalSymbolIds)
|
|
10880
|
+
Array.from(projectLocalSymbolIds),
|
|
10881
|
+
projectRoot
|
|
10713
10882
|
);
|
|
10714
10883
|
for (const branchKey of branchCleanupKeys) {
|
|
10715
10884
|
database.deleteBranchChunksForBranch(branchKey, removedChunkIdList);
|
|
@@ -10744,29 +10913,96 @@ var Indexer = class _Indexer {
|
|
|
10744
10913
|
database.gcOrphanSymbols();
|
|
10745
10914
|
database.gcOrphanEmbeddings();
|
|
10746
10915
|
database.gcOrphanChunks();
|
|
10747
|
-
store.save();
|
|
10748
10916
|
this.saveInvertedIndex(invertedIndex);
|
|
10917
|
+
store.save();
|
|
10749
10918
|
return {
|
|
10750
10919
|
removedChunkIds: removedChunkIdList,
|
|
10751
10920
|
hasForeignData: allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots))
|
|
10752
10921
|
};
|
|
10753
10922
|
}
|
|
10923
|
+
getCurrentClearRecoveryState() {
|
|
10924
|
+
if (!this.configuredProviderInfo) {
|
|
10925
|
+
throw new Error("Cannot persist clear recovery state before the embedding provider is initialized");
|
|
10926
|
+
}
|
|
10927
|
+
const compatibility = this.checkCompatibility();
|
|
10928
|
+
const compatibilityDecision = compatibility.compatible ? "compatible" : compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */ ? "embedding-strategy-mismatch" : "incompatible";
|
|
10929
|
+
return {
|
|
10930
|
+
phase: "clearing",
|
|
10931
|
+
embeddingProvider: this.configuredProviderInfo.provider,
|
|
10932
|
+
embeddingModel: this.configuredProviderInfo.modelInfo.model,
|
|
10933
|
+
embeddingDimensions: this.configuredProviderInfo.modelInfo.dimensions,
|
|
10934
|
+
embeddingStrategyVersion: EMBEDDING_STRATEGY_VERSION,
|
|
10935
|
+
compatibilityDecision
|
|
10936
|
+
};
|
|
10937
|
+
}
|
|
10938
|
+
beginClearRecoveryState() {
|
|
10939
|
+
const recovery = this.getCurrentClearRecoveryState();
|
|
10940
|
+
setIndexLockClearRecoveryState(this.requireActiveLease(), recovery);
|
|
10941
|
+
return recovery;
|
|
10942
|
+
}
|
|
10943
|
+
finishClearRecoveryState() {
|
|
10944
|
+
setIndexLockClearRecoveryState(this.requireActiveLease(), null);
|
|
10945
|
+
}
|
|
10946
|
+
matchesCurrentClearRecoveryConfiguration(recovery) {
|
|
10947
|
+
const configuredProviderInfo = this.configuredProviderInfo;
|
|
10948
|
+
return configuredProviderInfo !== null && recovery.embeddingProvider === configuredProviderInfo.provider && recovery.embeddingModel === configuredProviderInfo.modelInfo.model && recovery.embeddingDimensions === configuredProviderInfo.modelInfo.dimensions && recovery.embeddingStrategyVersion === EMBEDDING_STRATEGY_VERSION;
|
|
10949
|
+
}
|
|
10950
|
+
hasUnknownLegacyForceIndexClear(owner) {
|
|
10951
|
+
return owner.operation === "force-index" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1 && (0, import_fs12.existsSync)(path19.join(this.indexPath, "force-index-phase"));
|
|
10952
|
+
}
|
|
10754
10953
|
async recoverFromInterruptedIndexingUnlocked(owners) {
|
|
10755
10954
|
for (const owner of owners) {
|
|
10756
10955
|
this.logger.warn("Detected interrupted indexing session, recovering...", {
|
|
10757
10956
|
pid: owner.pid,
|
|
10758
10957
|
hostname: owner.hostname,
|
|
10759
10958
|
operation: owner.operation,
|
|
10760
|
-
startedAt: owner.startedAt
|
|
10959
|
+
startedAt: owner.startedAt,
|
|
10960
|
+
projectRoot: owner.projectRoot
|
|
10761
10961
|
});
|
|
10762
10962
|
}
|
|
10763
10963
|
if (this.config.scope === "global") {
|
|
10764
|
-
|
|
10765
|
-
|
|
10964
|
+
const clearScopes = [];
|
|
10965
|
+
for (const owner of owners) {
|
|
10966
|
+
if (this.hasUnknownLegacyForceIndexClear(owner)) {
|
|
10967
|
+
throw new Error(
|
|
10968
|
+
`Cannot automatically recover interrupted force-index ${owner.token}: the legacy clearing phase ownership is unknown. The recovery marker was retained for manual inspection.`
|
|
10969
|
+
);
|
|
10970
|
+
}
|
|
10971
|
+
if (owner.operation === "clear" && owner.clearRecovery === void 0 && owner.recoveryProtocolVersion !== 1) {
|
|
10972
|
+
throw new Error(
|
|
10973
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the originating recovery state is unknown. The recovery marker was retained for manual inspection.`
|
|
10974
|
+
);
|
|
10975
|
+
}
|
|
10976
|
+
if (owner.clearRecovery === void 0) continue;
|
|
10977
|
+
if (!owner.projectRoot || !owner.scopedRoots || owner.scopedRoots.length === 0) {
|
|
10978
|
+
throw new Error(
|
|
10979
|
+
`Cannot automatically recover interrupted global clear ${owner.token}: the originating project scope is unknown. The recovery marker was retained for manual inspection.`
|
|
10980
|
+
);
|
|
10981
|
+
}
|
|
10982
|
+
if (!this.matchesCurrentClearRecoveryConfiguration(owner.clearRecovery)) {
|
|
10983
|
+
throw new Error(
|
|
10984
|
+
`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.`
|
|
10985
|
+
);
|
|
10986
|
+
}
|
|
10987
|
+
clearScopes.push({
|
|
10988
|
+
projectRoot: owner.projectRoot,
|
|
10989
|
+
scopedRoots: owner.scopedRoots,
|
|
10990
|
+
compatibilityDecision: owner.clearRecovery.compatibilityDecision
|
|
10991
|
+
});
|
|
10992
|
+
}
|
|
10993
|
+
if (clearScopes.length > 0) {
|
|
10994
|
+
this.loadFileHashCache();
|
|
10995
|
+
}
|
|
10996
|
+
for (const { projectRoot, scopedRoots, compatibilityDecision } of clearScopes) {
|
|
10997
|
+
this.clearGlobalIndexUnlocked(projectRoot, scopedRoots, compatibilityDecision);
|
|
10766
10998
|
}
|
|
10767
10999
|
await this.healthCheckUnlocked();
|
|
11000
|
+
this.logger.info(
|
|
11001
|
+
clearScopes.length > 0 ? "Recovery complete, next index will rebuild all files" : "Recovery complete, next index will resume from the last checkpoint"
|
|
11002
|
+
);
|
|
11003
|
+
return;
|
|
10768
11004
|
}
|
|
10769
|
-
this.logger.info("Recovery complete, next index will
|
|
11005
|
+
this.logger.info("Recovery complete, next index will resume from the last checkpoint");
|
|
10770
11006
|
}
|
|
10771
11007
|
*loadSerializedFailedBatches() {
|
|
10772
11008
|
let warned = false;
|
|
@@ -10804,14 +11040,99 @@ var Indexer = class _Indexer {
|
|
|
10804
11040
|
state.writer.write(record);
|
|
10805
11041
|
state.recordsWritten += record.chunks.length;
|
|
10806
11042
|
}
|
|
10807
|
-
finalizeFailedBatchWriteState(state) {
|
|
11043
|
+
finalizeFailedBatchWriteState(state, resolvedChunkIds = /* @__PURE__ */ new Set()) {
|
|
10808
11044
|
if (state.recordsWritten > 0) {
|
|
10809
|
-
|
|
11045
|
+
const seenChunkIds = /* @__PURE__ */ new Set();
|
|
11046
|
+
const retained = [];
|
|
11047
|
+
const records = Array.from(readFailedBatchRecords(state.writer.temporaryPath));
|
|
11048
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
11049
|
+
const chunks = records[i].chunks.filter((rawChunk) => {
|
|
11050
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11051
|
+
if (chunkId !== null) {
|
|
11052
|
+
if (resolvedChunkIds.has(chunkId)) return false;
|
|
11053
|
+
if (seenChunkIds.has(chunkId)) return false;
|
|
11054
|
+
seenChunkIds.add(chunkId);
|
|
11055
|
+
}
|
|
11056
|
+
return true;
|
|
11057
|
+
});
|
|
11058
|
+
if (chunks.length > 0) {
|
|
11059
|
+
retained.unshift({ ...records[i], chunks });
|
|
11060
|
+
}
|
|
11061
|
+
}
|
|
11062
|
+
state.writer.cleanup();
|
|
11063
|
+
if (retained.length > 0) {
|
|
11064
|
+
writeFailedBatchRecords(this.failedBatchesPath, retained);
|
|
11065
|
+
} else {
|
|
11066
|
+
writeFailedBatchRecords(this.failedBatchesPath, []);
|
|
11067
|
+
this.clearFailedBatchState();
|
|
11068
|
+
}
|
|
10810
11069
|
return;
|
|
10811
11070
|
}
|
|
10812
|
-
state.writer.
|
|
11071
|
+
state.writer.commit();
|
|
10813
11072
|
this.clearFailedBatchState();
|
|
10814
11073
|
}
|
|
11074
|
+
getCheckpointIntervalChunks(totalChunks) {
|
|
11075
|
+
return Math.max(
|
|
11076
|
+
this.checkpointIntervalChunks ?? 2e3,
|
|
11077
|
+
Math.floor(totalChunks / 10)
|
|
11078
|
+
);
|
|
11079
|
+
}
|
|
11080
|
+
checkpointIndexRun(database, store, invertedIndex, failedProcessing, resolvedRetryChunkIds, currentFileHashes, committedFilePaths, scopedRoots, configuredProviderInfo) {
|
|
11081
|
+
if (!this.hasProjectForceReembedPending()) {
|
|
11082
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
11083
|
+
this.indexCompatibility = { compatible: true };
|
|
11084
|
+
}
|
|
11085
|
+
database.commitWriteTransaction();
|
|
11086
|
+
database.beginWriteTransaction();
|
|
11087
|
+
this.saveInvertedIndex(invertedIndex);
|
|
11088
|
+
store.save();
|
|
11089
|
+
if (failedProcessing.state.recordsWritten > 0 || failedProcessing.latestById.size > 0 || failedProcessing.discardedExistingRecords) {
|
|
11090
|
+
for (const metadata of failedProcessing.latestById.values()) {
|
|
11091
|
+
const alreadyMaterialized = metadata.chunks.some((rawChunk) => {
|
|
11092
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11093
|
+
return chunkId !== null && failedProcessing.materializedRetryIds.has(chunkId);
|
|
11094
|
+
});
|
|
11095
|
+
if (alreadyMaterialized) continue;
|
|
11096
|
+
this.writeFailedBatchRecord(failedProcessing.state, {
|
|
11097
|
+
chunks: metadata.chunks,
|
|
11098
|
+
attemptCount: metadata.attemptCount,
|
|
11099
|
+
error: metadata.error,
|
|
11100
|
+
lastAttempt: metadata.lastAttempt
|
|
11101
|
+
});
|
|
11102
|
+
for (const rawChunk of metadata.chunks) {
|
|
11103
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11104
|
+
if (chunkId !== null) {
|
|
11105
|
+
failedProcessing.materializedRetryIds.add(chunkId);
|
|
11106
|
+
}
|
|
11107
|
+
}
|
|
11108
|
+
}
|
|
11109
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
11110
|
+
failedProcessing.state = this.createFailedBatchWriteState();
|
|
11111
|
+
failedProcessing.discardedExistingRecords = false;
|
|
11112
|
+
for (const record of this.loadSerializedFailedBatches()) {
|
|
11113
|
+
for (const rawChunk of record.chunks) {
|
|
11114
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
11115
|
+
this.writeFailedBatchRecord(failedProcessing.state, { ...record, chunks: [rawChunk] });
|
|
11116
|
+
if (chunkId !== null) {
|
|
11117
|
+
failedProcessing.materializedRetryIds.add(chunkId);
|
|
11118
|
+
}
|
|
11119
|
+
}
|
|
11120
|
+
}
|
|
11121
|
+
}
|
|
11122
|
+
const partialHashes = /* @__PURE__ */ new Map();
|
|
11123
|
+
for (const filePath of committedFilePaths) {
|
|
11124
|
+
const hash = currentFileHashes.get(filePath);
|
|
11125
|
+
if (hash !== void 0) {
|
|
11126
|
+
partialHashes.set(filePath, hash);
|
|
11127
|
+
}
|
|
11128
|
+
}
|
|
11129
|
+
if (scopedRoots) {
|
|
11130
|
+
this.replaceScopedFileHashCache(partialHashes, scopedRoots);
|
|
11131
|
+
} else {
|
|
11132
|
+
this.fileHashCache = partialHashes;
|
|
11133
|
+
this.saveFileHashCache();
|
|
11134
|
+
}
|
|
11135
|
+
}
|
|
10815
11136
|
clearFailedBatchState() {
|
|
10816
11137
|
if ((0, import_fs12.existsSync)(this.failedBatchesPath)) {
|
|
10817
11138
|
try {
|
|
@@ -10838,6 +11159,7 @@ var Indexer = class _Indexer {
|
|
|
10838
11159
|
prepareFailedBatchProcessing(roots, shouldProcess) {
|
|
10839
11160
|
const state = this.createFailedBatchWriteState();
|
|
10840
11161
|
const latestById = /* @__PURE__ */ new Map();
|
|
11162
|
+
let discardedExistingRecords = false;
|
|
10841
11163
|
try {
|
|
10842
11164
|
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10843
11165
|
for (const rawChunk of batch.chunks) {
|
|
@@ -10848,10 +11170,12 @@ var Indexer = class _Indexer {
|
|
|
10848
11170
|
continue;
|
|
10849
11171
|
}
|
|
10850
11172
|
if (!shouldProcess(filePath)) {
|
|
11173
|
+
discardedExistingRecords = true;
|
|
10851
11174
|
continue;
|
|
10852
11175
|
}
|
|
10853
11176
|
const chunkId = getPendingChunkId(rawChunk);
|
|
10854
11177
|
if (!chunkId) {
|
|
11178
|
+
discardedExistingRecords = true;
|
|
10855
11179
|
continue;
|
|
10856
11180
|
}
|
|
10857
11181
|
const existing = latestById.get(chunkId);
|
|
@@ -10859,12 +11183,18 @@ var Indexer = class _Indexer {
|
|
|
10859
11183
|
latestById.set(chunkId, {
|
|
10860
11184
|
attemptCount: batch.attemptCount,
|
|
10861
11185
|
error: batch.error,
|
|
10862
|
-
lastAttempt: batch.lastAttempt
|
|
11186
|
+
lastAttempt: batch.lastAttempt,
|
|
11187
|
+
chunks: [rawChunk]
|
|
10863
11188
|
});
|
|
10864
11189
|
}
|
|
10865
11190
|
}
|
|
10866
11191
|
}
|
|
10867
|
-
return {
|
|
11192
|
+
return {
|
|
11193
|
+
state,
|
|
11194
|
+
latestById,
|
|
11195
|
+
materializedRetryIds: /* @__PURE__ */ new Set(),
|
|
11196
|
+
discardedExistingRecords
|
|
11197
|
+
};
|
|
10868
11198
|
} catch (error) {
|
|
10869
11199
|
state.writer.cleanup();
|
|
10870
11200
|
throw error;
|
|
@@ -10900,10 +11230,34 @@ var Indexer = class _Indexer {
|
|
|
10900
11230
|
}
|
|
10901
11231
|
}
|
|
10902
11232
|
}
|
|
11233
|
+
restoreMissingChunkRows(database, chunks) {
|
|
11234
|
+
const missing = [];
|
|
11235
|
+
for (const chunk of chunks) {
|
|
11236
|
+
if (database.getChunk(chunk.id)) {
|
|
11237
|
+
continue;
|
|
11238
|
+
}
|
|
11239
|
+
missing.push({
|
|
11240
|
+
chunkId: chunk.id,
|
|
11241
|
+
contentHash: chunk.contentHash,
|
|
11242
|
+
filePath: chunk.metadata.filePath,
|
|
11243
|
+
startLine: chunk.metadata.startLine,
|
|
11244
|
+
endLine: chunk.metadata.endLine,
|
|
11245
|
+
nodeType: chunk.metadata.chunkType,
|
|
11246
|
+
name: chunk.metadata.name,
|
|
11247
|
+
language: chunk.metadata.language,
|
|
11248
|
+
blameSha: chunk.metadata.blameSha,
|
|
11249
|
+
blameAuthor: chunk.metadata.blameAuthor,
|
|
11250
|
+
blameAuthorEmail: chunk.metadata.blameAuthorEmail,
|
|
11251
|
+
blameCommittedAt: chunk.metadata.blameCommittedAt,
|
|
11252
|
+
blameSummary: chunk.metadata.blameSummary
|
|
11253
|
+
});
|
|
11254
|
+
}
|
|
11255
|
+
if (missing.length > 0) {
|
|
11256
|
+
database.upsertChunksBatch(missing);
|
|
11257
|
+
}
|
|
11258
|
+
}
|
|
10903
11259
|
getProviderRateLimits(provider) {
|
|
10904
11260
|
switch (provider) {
|
|
10905
|
-
case "github-copilot":
|
|
10906
|
-
return { concurrency: 1, intervalMs: 4e3, minRetryMs: 5e3, maxRetryMs: 6e4 };
|
|
10907
11261
|
case "openai":
|
|
10908
11262
|
return { concurrency: 3, intervalMs: 500, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
10909
11263
|
case "google":
|
|
@@ -10972,10 +11326,11 @@ var Indexer = class _Indexer {
|
|
|
10972
11326
|
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
10973
11327
|
const completedVectorsByChunkId = /* @__PURE__ */ new Map();
|
|
10974
11328
|
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
10975
|
-
const
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
11329
|
+
const batchOptions = getDynamicBatchOptions(options.configuredProviderInfo, this.config.embedding?.batch);
|
|
11330
|
+
if (options.forceSingleItemBatches && options.configuredProviderInfo.provider === "ollama") {
|
|
11331
|
+
batchOptions.maxBatchItems = 1;
|
|
11332
|
+
}
|
|
11333
|
+
const requestBatches = createPendingEmbeddingRequestBatches(chunksNeedingEmbedding, batchOptions);
|
|
10979
11334
|
let fatalError;
|
|
10980
11335
|
for (const requestBatch of requestBatches) {
|
|
10981
11336
|
await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
|
|
@@ -11538,7 +11893,7 @@ var Indexer = class _Indexer {
|
|
|
11538
11893
|
}
|
|
11539
11894
|
if (!this.configuredProviderInfo) {
|
|
11540
11895
|
throw new Error(
|
|
11541
|
-
"No embedding provider available. Configure
|
|
11896
|
+
"No embedding provider available. Configure OpenAI, Google, Ollama, or a custom OpenAI-compatible endpoint."
|
|
11542
11897
|
);
|
|
11543
11898
|
}
|
|
11544
11899
|
this.logger.info("Initializing indexer", {
|
|
@@ -11569,7 +11924,20 @@ var Indexer = class _Indexer {
|
|
|
11569
11924
|
]);
|
|
11570
11925
|
}
|
|
11571
11926
|
if (recoveredOwners.length > 0 && this.config.scope === "project") {
|
|
11572
|
-
|
|
11927
|
+
const unknownLegacyForceIndex = recoveredOwners.find(
|
|
11928
|
+
(owner) => this.hasUnknownLegacyForceIndexClear(owner)
|
|
11929
|
+
);
|
|
11930
|
+
if (unknownLegacyForceIndex) {
|
|
11931
|
+
throw new Error(
|
|
11932
|
+
`Cannot automatically recover interrupted force-index ${unknownLegacyForceIndex.token}: the legacy clearing phase ownership is unknown. The recovery marker was retained for manual inspection.`
|
|
11933
|
+
);
|
|
11934
|
+
}
|
|
11935
|
+
const shouldReset = recoveredOwners.some(
|
|
11936
|
+
(owner) => owner.clearRecovery !== void 0 || owner.operation === "clear" && owner.recoveryProtocolVersion !== 1
|
|
11937
|
+
);
|
|
11938
|
+
if (shouldReset) {
|
|
11939
|
+
await this.resetLocalIndexArtifacts();
|
|
11940
|
+
}
|
|
11573
11941
|
}
|
|
11574
11942
|
this.store = new VectorStore(storePath, dimensions);
|
|
11575
11943
|
if ((0, import_fs12.existsSync)(storePath) || (0, import_fs12.existsSync)(vectorMetadataPath)) {
|
|
@@ -12205,7 +12573,17 @@ var Indexer = class _Indexer {
|
|
|
12205
12573
|
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
12206
12574
|
for (const file of files) {
|
|
12207
12575
|
const storedPath = this.toStoredFilePath(file.path);
|
|
12208
|
-
|
|
12576
|
+
let currentHash;
|
|
12577
|
+
try {
|
|
12578
|
+
currentHash = hashFile(file.path);
|
|
12579
|
+
} catch (error) {
|
|
12580
|
+
stats.skippedFiles.push({ path: this.toCanonicalFilePath(file.path), reason: "unreadable" });
|
|
12581
|
+
this.logger.warn("Skipped unreadable file during indexing", {
|
|
12582
|
+
path: file.path,
|
|
12583
|
+
error: getErrorMessage4(error)
|
|
12584
|
+
});
|
|
12585
|
+
continue;
|
|
12586
|
+
}
|
|
12209
12587
|
currentFileHashes.set(storedPath, currentHash);
|
|
12210
12588
|
const cachedHashMatches = this.fileHashCache.get(storedPath) === currentHash;
|
|
12211
12589
|
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
|
|
@@ -12213,7 +12591,8 @@ var Indexer = class _Indexer {
|
|
|
12213
12591
|
);
|
|
12214
12592
|
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path19.extname(storedPath).toLowerCase() === ".swift";
|
|
12215
12593
|
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path19.extname(storedPath).toLowerCase() === ".metal";
|
|
12216
|
-
|
|
12594
|
+
const inMigrationScope = forceScopedReembed && scopedRoots !== null && this.isFileInCurrentScope(storedPath, scopedRoots);
|
|
12595
|
+
if (cachedHashMatches && !inMigrationScope && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
12217
12596
|
unchangedFilePaths.add(storedPath);
|
|
12218
12597
|
this.logger.recordCacheHit();
|
|
12219
12598
|
} else {
|
|
@@ -12339,6 +12718,9 @@ var Indexer = class _Indexer {
|
|
|
12339
12718
|
}
|
|
12340
12719
|
}
|
|
12341
12720
|
let processedChangedFiles = 0;
|
|
12721
|
+
let lastCheckpointChunks = 0;
|
|
12722
|
+
const committedFilePaths = new Set(unchangedFilePaths);
|
|
12723
|
+
const resolvedRetryChunkIds = /* @__PURE__ */ new Set();
|
|
12342
12724
|
for (const descriptorBatch of iterateOrderedFileBatches(
|
|
12343
12725
|
changedFileDescriptors,
|
|
12344
12726
|
(descriptor) => descriptor.sourceBytes,
|
|
@@ -12352,7 +12734,7 @@ var Indexer = class _Indexer {
|
|
|
12352
12734
|
const loadedByPath = new Map(loadedFiles.map((file) => [file.path, file]));
|
|
12353
12735
|
const descriptorByPath = new Map(descriptorBatch.map((descriptor) => [descriptor.storedPath, descriptor]));
|
|
12354
12736
|
const parseStartTime = import_perf_hooks.performance.now();
|
|
12355
|
-
const parsedFiles = parseFiles(loadedFiles);
|
|
12737
|
+
const parsedFiles = parseFiles(loadedFiles, this.config.indexing.linesPerChunk);
|
|
12356
12738
|
const parseMs = import_perf_hooks.performance.now() - parseStartTime;
|
|
12357
12739
|
this.logger.recordFilesParsed(parsedFiles.length);
|
|
12358
12740
|
this.logger.recordParseDuration(parseMs);
|
|
@@ -12375,7 +12757,7 @@ var Indexer = class _Indexer {
|
|
|
12375
12757
|
}
|
|
12376
12758
|
let chunksToProcess = parsed.chunks;
|
|
12377
12759
|
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
12378
|
-
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content);
|
|
12760
|
+
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content, this.config.indexing.linesPerChunk);
|
|
12379
12761
|
}
|
|
12380
12762
|
chunksToProcess = selectIndexableChunks(
|
|
12381
12763
|
chunksToProcess,
|
|
@@ -12509,6 +12891,10 @@ var Indexer = class _Indexer {
|
|
|
12509
12891
|
}
|
|
12510
12892
|
if (symbolBatch.length > 0) {
|
|
12511
12893
|
database.upsertSymbolsBatch(symbolBatch);
|
|
12894
|
+
database.addSymbolsToBranchBatch(
|
|
12895
|
+
this.getBranchCatalogKey(),
|
|
12896
|
+
symbolBatch.map((symbol) => symbol.id)
|
|
12897
|
+
);
|
|
12512
12898
|
}
|
|
12513
12899
|
if (edgeBatch.length > 0) {
|
|
12514
12900
|
database.upsertCallEdgesBatch(edgeBatch);
|
|
@@ -12544,6 +12930,12 @@ var Indexer = class _Indexer {
|
|
|
12544
12930
|
forceReembed: forceScopedReembed,
|
|
12545
12931
|
reuseCachedEmbeddings: true,
|
|
12546
12932
|
incrementRepeatedFailures: true,
|
|
12933
|
+
onSucceeded: (succeededChunks) => {
|
|
12934
|
+
database.addChunksToBranchBatch(
|
|
12935
|
+
this.getBranchCatalogKey(),
|
|
12936
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
12937
|
+
);
|
|
12938
|
+
},
|
|
12547
12939
|
onProgress: (batchProgress) => onProgress?.({
|
|
12548
12940
|
phase: "embedding",
|
|
12549
12941
|
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
@@ -12562,6 +12954,27 @@ var Indexer = class _Indexer {
|
|
|
12562
12954
|
}
|
|
12563
12955
|
}
|
|
12564
12956
|
}
|
|
12957
|
+
for (const descriptor of descriptorBatch) {
|
|
12958
|
+
const existingFileChunks = existingChunksByFile.get(descriptor.storedPath);
|
|
12959
|
+
if (!existingFileChunks || existingFileChunks.size === 0) {
|
|
12960
|
+
committedFilePaths.add(descriptor.storedPath);
|
|
12961
|
+
}
|
|
12962
|
+
}
|
|
12963
|
+
const checkpointInterval = this.getCheckpointIntervalChunks(stats.totalChunks);
|
|
12964
|
+
if (stats.totalChunks - lastCheckpointChunks >= checkpointInterval) {
|
|
12965
|
+
lastCheckpointChunks = stats.totalChunks;
|
|
12966
|
+
this.checkpointIndexRun(
|
|
12967
|
+
database,
|
|
12968
|
+
store,
|
|
12969
|
+
invertedIndex,
|
|
12970
|
+
failedProcessing,
|
|
12971
|
+
resolvedRetryChunkIds,
|
|
12972
|
+
currentFileHashes,
|
|
12973
|
+
committedFilePaths,
|
|
12974
|
+
scopedRoots,
|
|
12975
|
+
configuredProviderInfo
|
|
12976
|
+
);
|
|
12977
|
+
}
|
|
12565
12978
|
}
|
|
12566
12979
|
const retryableFailedChunks = this.iterateLatestFailedChunks(
|
|
12567
12980
|
failedProcessing.latestById,
|
|
@@ -12582,6 +12995,7 @@ var Indexer = class _Indexer {
|
|
|
12582
12995
|
retryableChunksWithExistingData.add(chunk.id);
|
|
12583
12996
|
}
|
|
12584
12997
|
}
|
|
12998
|
+
this.restoreMissingChunkRows(database, pendingChunks);
|
|
12585
12999
|
stats.totalChunks += pendingChunks.length;
|
|
12586
13000
|
onProgress?.({
|
|
12587
13001
|
phase: "embedding",
|
|
@@ -12604,6 +13018,17 @@ var Indexer = class _Indexer {
|
|
|
12604
13018
|
forceReembed: forceScopedReembed,
|
|
12605
13019
|
reuseCachedEmbeddings: true,
|
|
12606
13020
|
incrementRepeatedFailures: true,
|
|
13021
|
+
forceSingleItemBatches: true,
|
|
13022
|
+
onSucceeded: (succeededChunks) => {
|
|
13023
|
+
database.addChunksToBranchBatch(
|
|
13024
|
+
this.getBranchCatalogKey(),
|
|
13025
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
13026
|
+
);
|
|
13027
|
+
for (const chunk of succeededChunks) {
|
|
13028
|
+
failedProcessing.latestById.delete(chunk.id);
|
|
13029
|
+
resolvedRetryChunkIds.add(chunk.id);
|
|
13030
|
+
}
|
|
13031
|
+
},
|
|
12607
13032
|
onProgress: (batchProgress) => onProgress?.({
|
|
12608
13033
|
phase: "embedding",
|
|
12609
13034
|
filesProcessed: files.length,
|
|
@@ -12621,6 +13046,20 @@ var Indexer = class _Indexer {
|
|
|
12621
13046
|
failedForcedChunkIds.add(chunkId);
|
|
12622
13047
|
}
|
|
12623
13048
|
}
|
|
13049
|
+
if (stats.totalChunks - lastCheckpointChunks >= this.getCheckpointIntervalChunks(stats.totalChunks)) {
|
|
13050
|
+
lastCheckpointChunks = stats.totalChunks;
|
|
13051
|
+
this.checkpointIndexRun(
|
|
13052
|
+
database,
|
|
13053
|
+
store,
|
|
13054
|
+
invertedIndex,
|
|
13055
|
+
failedProcessing,
|
|
13056
|
+
resolvedRetryChunkIds,
|
|
13057
|
+
currentFileHashes,
|
|
13058
|
+
committedFilePaths,
|
|
13059
|
+
scopedRoots,
|
|
13060
|
+
configuredProviderInfo
|
|
13061
|
+
);
|
|
13062
|
+
}
|
|
12624
13063
|
}
|
|
12625
13064
|
const removedChunkIds = [];
|
|
12626
13065
|
for (const [chunkId] of existingChunks) {
|
|
@@ -12657,13 +13096,6 @@ var Indexer = class _Indexer {
|
|
|
12657
13096
|
if (removedStoredChunks) {
|
|
12658
13097
|
this.saveInvertedIndex(invertedIndex);
|
|
12659
13098
|
}
|
|
12660
|
-
if (scopedRoots) {
|
|
12661
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12662
|
-
} else {
|
|
12663
|
-
this.fileHashCache = currentFileHashes;
|
|
12664
|
-
this.saveFileHashCache();
|
|
12665
|
-
}
|
|
12666
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12667
13099
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12668
13100
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12669
13101
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -12672,6 +13104,13 @@ var Indexer = class _Indexer {
|
|
|
12672
13104
|
this.indexCompatibility = { compatible: true };
|
|
12673
13105
|
database.commitWriteTransaction();
|
|
12674
13106
|
writeTransactionActive = false;
|
|
13107
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
13108
|
+
if (scopedRoots) {
|
|
13109
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
13110
|
+
} else {
|
|
13111
|
+
this.fileHashCache = currentFileHashes;
|
|
13112
|
+
this.saveFileHashCache();
|
|
13113
|
+
}
|
|
12675
13114
|
stats.durationMs = Date.now() - startTime;
|
|
12676
13115
|
onProgress?.({
|
|
12677
13116
|
phase: "complete",
|
|
@@ -12695,13 +13134,6 @@ var Indexer = class _Indexer {
|
|
|
12695
13134
|
);
|
|
12696
13135
|
store.save();
|
|
12697
13136
|
this.saveInvertedIndex(invertedIndex);
|
|
12698
|
-
if (scopedRoots) {
|
|
12699
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12700
|
-
} else {
|
|
12701
|
-
this.fileHashCache = currentFileHashes;
|
|
12702
|
-
this.saveFileHashCache();
|
|
12703
|
-
}
|
|
12704
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12705
13137
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12706
13138
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12707
13139
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -12710,6 +13142,13 @@ var Indexer = class _Indexer {
|
|
|
12710
13142
|
this.indexCompatibility = { compatible: true };
|
|
12711
13143
|
database.commitWriteTransaction();
|
|
12712
13144
|
writeTransactionActive = false;
|
|
13145
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
13146
|
+
if (scopedRoots) {
|
|
13147
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
13148
|
+
} else {
|
|
13149
|
+
this.fileHashCache = currentFileHashes;
|
|
13150
|
+
this.saveFileHashCache();
|
|
13151
|
+
}
|
|
12713
13152
|
stats.durationMs = Date.now() - startTime;
|
|
12714
13153
|
onProgress?.({
|
|
12715
13154
|
phase: "complete",
|
|
@@ -12744,15 +13183,15 @@ var Indexer = class _Indexer {
|
|
|
12744
13183
|
);
|
|
12745
13184
|
store.save();
|
|
12746
13185
|
this.saveInvertedIndex(invertedIndex);
|
|
13186
|
+
database.commitWriteTransaction();
|
|
13187
|
+
writeTransactionActive = false;
|
|
13188
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state, resolvedRetryChunkIds);
|
|
12747
13189
|
if (scopedRoots) {
|
|
12748
13190
|
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12749
13191
|
} else {
|
|
12750
13192
|
this.fileHashCache = currentFileHashes;
|
|
12751
13193
|
this.saveFileHashCache();
|
|
12752
13194
|
}
|
|
12753
|
-
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12754
|
-
database.commitWriteTransaction();
|
|
12755
|
-
writeTransactionActive = false;
|
|
12756
13195
|
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
12757
13196
|
const gcReset = await this.maybeRunOrphanGc();
|
|
12758
13197
|
if (gcReset) {
|
|
@@ -12776,6 +13215,9 @@ var Indexer = class _Indexer {
|
|
|
12776
13215
|
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
12777
13216
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12778
13217
|
}
|
|
13218
|
+
if (forceScopedReembed) {
|
|
13219
|
+
database.setMetadata(this.getProjectMigrationFinalizedMetadataKey(), "true");
|
|
13220
|
+
}
|
|
12779
13221
|
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12780
13222
|
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12781
13223
|
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
@@ -12886,26 +13328,41 @@ var Indexer = class _Indexer {
|
|
|
12886
13328
|
shouldPrefilterByBranch: branchChunkIds !== null && (this.config.scope === "global" || hasInitializedBranchCatalog)
|
|
12887
13329
|
};
|
|
12888
13330
|
}
|
|
12889
|
-
|
|
13331
|
+
searchCandidatesWithAllowedIds(initialLimit, totalCount, allowedChunkIds, shouldPrefilter, search, getChunkId) {
|
|
12890
13332
|
const normalizedLimit = Math.max(0, Math.floor(initialLimit));
|
|
12891
13333
|
if (normalizedLimit === 0) return [];
|
|
12892
|
-
if (!
|
|
13334
|
+
if (!shouldPrefilter || !allowedChunkIds) {
|
|
12893
13335
|
return search(normalizedLimit);
|
|
12894
13336
|
}
|
|
12895
|
-
const targetCount = Math.min(normalizedLimit,
|
|
13337
|
+
const targetCount = Math.min(normalizedLimit, allowedChunkIds.size);
|
|
12896
13338
|
if (targetCount === 0 || totalCount === 0) return [];
|
|
12897
13339
|
let requestedLimit = Math.min(normalizedLimit, totalCount);
|
|
12898
13340
|
while (true) {
|
|
12899
13341
|
const results = search(requestedLimit);
|
|
12900
|
-
const
|
|
12901
|
-
if (
|
|
12902
|
-
return
|
|
13342
|
+
const allowedResults = results.filter((candidate) => allowedChunkIds.has(getChunkId(candidate)));
|
|
13343
|
+
if (allowedResults.length >= targetCount || results.length < requestedLimit || requestedLimit >= totalCount) {
|
|
13344
|
+
return allowedResults;
|
|
12903
13345
|
}
|
|
12904
13346
|
const nextLimit = Math.min(totalCount, Math.max(requestedLimit + 1, requestedLimit * 2));
|
|
12905
|
-
if (nextLimit === requestedLimit) return
|
|
13347
|
+
if (nextLimit === requestedLimit) return allowedResults;
|
|
12906
13348
|
requestedLimit = nextLimit;
|
|
12907
13349
|
}
|
|
12908
13350
|
}
|
|
13351
|
+
getTemporalChunkIds(database, options) {
|
|
13352
|
+
if (!options?.blameSince && !options?.blameUntil) return null;
|
|
13353
|
+
const since = options.blameSince ? parseBlameTimestamp(options.blameSince, false) : void 0;
|
|
13354
|
+
const until = options.blameUntil ? parseBlameTimestamp(options.blameUntil, true) : void 0;
|
|
13355
|
+
if (since === null || until === null) {
|
|
13356
|
+
return /* @__PURE__ */ new Set();
|
|
13357
|
+
}
|
|
13358
|
+
return new Set(database.getChunkIdsByBlameDate(since, until));
|
|
13359
|
+
}
|
|
13360
|
+
intersectChunkIdSets(first, second) {
|
|
13361
|
+
if (first === null) return second;
|
|
13362
|
+
if (second === null) return first;
|
|
13363
|
+
const [smaller, larger] = first.size <= second.size ? [first, second] : [second, first];
|
|
13364
|
+
return new Set(Array.from(smaller).filter((chunkId) => larger.has(chunkId)));
|
|
13365
|
+
}
|
|
12909
13366
|
buildCandidateSnapshot(candidate) {
|
|
12910
13367
|
return {
|
|
12911
13368
|
id: candidate.id,
|
|
@@ -12920,13 +13377,16 @@ var Indexer = class _Indexer {
|
|
|
12920
13377
|
buildCandidateSnapshotList(candidates) {
|
|
12921
13378
|
return candidates.map((candidate) => this.buildCandidateSnapshot(candidate));
|
|
12922
13379
|
}
|
|
12923
|
-
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch) {
|
|
12924
|
-
|
|
12925
|
-
|
|
12926
|
-
|
|
13380
|
+
searchSemanticCandidates(store, embedding, initialLimit, branchChunkIds, shouldPrefilterByBranch, temporalChunkIds) {
|
|
13381
|
+
const availableCount = temporalChunkIds?.size ?? store.count();
|
|
13382
|
+
if (availableCount === 0) return [];
|
|
13383
|
+
const allowedIds = temporalChunkIds === null ? void 0 : Array.from(temporalChunkIds);
|
|
13384
|
+
return this.searchCandidatesWithAllowedIds(
|
|
13385
|
+
Math.min(initialLimit, availableCount),
|
|
13386
|
+
availableCount,
|
|
12927
13387
|
branchChunkIds,
|
|
12928
13388
|
shouldPrefilterByBranch,
|
|
12929
|
-
(requestedLimit) => store.search(embedding, requestedLimit),
|
|
13389
|
+
(requestedLimit) => store.search(embedding, requestedLimit, allowedIds),
|
|
12930
13390
|
(candidate) => candidate.id
|
|
12931
13391
|
);
|
|
12932
13392
|
}
|
|
@@ -12951,8 +13411,9 @@ var Indexer = class _Indexer {
|
|
|
12951
13411
|
const rerankTopN = this.config.search.rerankTopN;
|
|
12952
13412
|
const filterByBranch = options?.filterByBranch ?? true;
|
|
12953
13413
|
const sourceIntent = options?.definitionIntent === true || classifyQueryIntentRaw(query) === "source";
|
|
13414
|
+
const prioritizeSourcePaths = sourceIntent || options?.prioritizeSourcePaths === true;
|
|
12954
13415
|
const identifierHints = extractIdentifierHints(query);
|
|
12955
|
-
const candidateLimit = maxResults * (
|
|
13416
|
+
const candidateLimit = maxResults * (prioritizeSourcePaths ? 12 : 4);
|
|
12956
13417
|
this.logger.search("debug", "Starting search", {
|
|
12957
13418
|
query,
|
|
12958
13419
|
maxResults,
|
|
@@ -12983,6 +13444,7 @@ var Indexer = class _Indexer {
|
|
|
12983
13444
|
branchChunkIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchChunkIds(branchKey)));
|
|
12984
13445
|
branchSymbolIds = new Set(branchCatalogKeys.flatMap((branchKey) => database.getBranchSymbolIds(branchKey)));
|
|
12985
13446
|
}
|
|
13447
|
+
const temporalChunkIds = this.getTemporalChunkIds(database, options);
|
|
12986
13448
|
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
12987
13449
|
const prefilterMs = import_perf_hooks.performance.now() - prefilterStartTime;
|
|
12988
13450
|
const vectorStartTime = import_perf_hooks.performance.now();
|
|
@@ -12991,7 +13453,8 @@ var Indexer = class _Indexer {
|
|
|
12991
13453
|
embedding,
|
|
12992
13454
|
candidateLimit,
|
|
12993
13455
|
branchChunkIds,
|
|
12994
|
-
shouldPrefilterByBranch
|
|
13456
|
+
shouldPrefilterByBranch,
|
|
13457
|
+
temporalChunkIds
|
|
12995
13458
|
) : [];
|
|
12996
13459
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
12997
13460
|
const keywordStartTime = import_perf_hooks.performance.now();
|
|
@@ -13001,7 +13464,8 @@ var Indexer = class _Indexer {
|
|
|
13001
13464
|
store,
|
|
13002
13465
|
invertedIndex,
|
|
13003
13466
|
branchChunkIds,
|
|
13004
|
-
shouldPrefilterByBranch
|
|
13467
|
+
shouldPrefilterByBranch,
|
|
13468
|
+
temporalChunkIds
|
|
13005
13469
|
);
|
|
13006
13470
|
const keywordMs = import_perf_hooks.performance.now() - keywordStartTime;
|
|
13007
13471
|
const scopedSemanticCandidates = semanticCandidates.filter(
|
|
@@ -13023,7 +13487,7 @@ var Indexer = class _Indexer {
|
|
|
13023
13487
|
rerankTopN,
|
|
13024
13488
|
limit: maxResults,
|
|
13025
13489
|
hybridWeight: rankingHybridWeight,
|
|
13026
|
-
prioritizeSourcePaths
|
|
13490
|
+
prioritizeSourcePaths
|
|
13027
13491
|
});
|
|
13028
13492
|
const rerankedCombined = await this.rerankCandidatesWithApi(query, combined, {
|
|
13029
13493
|
definitionIntent: options?.definitionIntent === true,
|
|
@@ -13059,10 +13523,11 @@ var Indexer = class _Indexer {
|
|
|
13059
13523
|
branchSymbolIds,
|
|
13060
13524
|
maxResults,
|
|
13061
13525
|
union,
|
|
13062
|
-
sourceIntent
|
|
13526
|
+
sourceIntent,
|
|
13527
|
+
options?.definitionIntent === true && ((options.directory?.trim().length ?? 0) > 0 || (options.fileType?.trim().length ?? 0) > 0)
|
|
13063
13528
|
);
|
|
13064
13529
|
const prePrimaryLane = mergeTieredResults(deterministicIdentifierLane, identifierLane, maxResults * 4);
|
|
13065
|
-
const primaryLane = mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
13530
|
+
const primaryLane = options?.definitionIntent === true ? mergeTieredResults(symbolLane, prePrimaryLane, maxResults * 4) : mergeTieredResults(prePrimaryLane, symbolLane, maxResults * 4);
|
|
13066
13531
|
const tiered = mergeTieredResults(primaryLane, rescued, maxResults * 4);
|
|
13067
13532
|
const hasCodeHints = extractCodeTermHints(query).length > 0 || identifierHints.length > 0;
|
|
13068
13533
|
const baseFiltered = tiered.filter(
|
|
@@ -13157,14 +13622,18 @@ var Indexer = class _Indexer {
|
|
|
13157
13622
|
})
|
|
13158
13623
|
);
|
|
13159
13624
|
}
|
|
13160
|
-
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false) {
|
|
13625
|
+
async keywordSearch(query, limit, store, invertedIndex, branchChunkIds = null, shouldPrefilterByBranch = false, temporalChunkIds = null) {
|
|
13161
13626
|
const normalizedLimit = Math.max(0, Math.floor(limit));
|
|
13162
13627
|
if (normalizedLimit === 0) return [];
|
|
13163
|
-
const
|
|
13628
|
+
const allowedChunkIds = this.intersectChunkIdSets(
|
|
13629
|
+
shouldPrefilterByBranch ? branchChunkIds : null,
|
|
13630
|
+
temporalChunkIds
|
|
13631
|
+
);
|
|
13632
|
+
const scoreEntries = this.searchCandidatesWithAllowedIds(
|
|
13164
13633
|
normalizedLimit,
|
|
13165
13634
|
invertedIndex.getDocumentCount(),
|
|
13166
|
-
|
|
13167
|
-
|
|
13635
|
+
allowedChunkIds,
|
|
13636
|
+
allowedChunkIds !== null,
|
|
13168
13637
|
(requestedLimit) => Array.from(invertedIndex.search(query, requestedLimit)),
|
|
13169
13638
|
([chunkId]) => chunkId
|
|
13170
13639
|
);
|
|
@@ -13249,7 +13718,17 @@ var Indexer = class _Indexer {
|
|
|
13249
13718
|
);
|
|
13250
13719
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
13251
13720
|
for (const file of files) {
|
|
13252
|
-
|
|
13721
|
+
let hash;
|
|
13722
|
+
try {
|
|
13723
|
+
hash = hashFile(file.path);
|
|
13724
|
+
} catch (error) {
|
|
13725
|
+
this.logger.warn("Skipped unreadable file during freshness check", {
|
|
13726
|
+
path: file.path,
|
|
13727
|
+
error: getErrorMessage4(error)
|
|
13728
|
+
});
|
|
13729
|
+
return { readable: false, current: false, reason: "unreadable" };
|
|
13730
|
+
}
|
|
13731
|
+
currentFileHashes.set(this.toStoredFilePath(file.path), hash);
|
|
13253
13732
|
}
|
|
13254
13733
|
const scopedRoots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
13255
13734
|
const cachedFileHashes = scopedRoots ? new Map(Array.from(this.fileHashCache).filter(([filePath]) => this.isFileInCurrentScope(filePath, scopedRoots))) : this.fileHashCache;
|
|
@@ -13275,69 +13754,87 @@ var Indexer = class _Indexer {
|
|
|
13275
13754
|
async forceIndex(onProgress) {
|
|
13276
13755
|
return this.withIndexMutationLease("force-index", async (recoveredOwners) => {
|
|
13277
13756
|
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
13278
|
-
|
|
13757
|
+
const recovery = this.beginClearRecoveryState();
|
|
13758
|
+
await this.clearIndexUnlocked(recovery.compatibilityDecision);
|
|
13759
|
+
this.finishClearRecoveryState();
|
|
13279
13760
|
return this.indexUnlocked(onProgress, [], true);
|
|
13280
13761
|
});
|
|
13281
13762
|
}
|
|
13282
13763
|
async clearIndex() {
|
|
13283
13764
|
await this.withIndexMutationLease("clear", async (recoveredOwners) => {
|
|
13284
13765
|
await this.ensureInitializedUnlocked(recoveredOwners);
|
|
13285
|
-
|
|
13766
|
+
const recovery = this.beginClearRecoveryState();
|
|
13767
|
+
await this.clearIndexUnlocked(recovery.compatibilityDecision);
|
|
13286
13768
|
});
|
|
13287
13769
|
}
|
|
13288
|
-
|
|
13770
|
+
clearGlobalIndexDataUnlocked(projectRoot = this.projectRoot) {
|
|
13289
13771
|
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
13290
|
-
|
|
13291
|
-
|
|
13292
|
-
|
|
13293
|
-
|
|
13294
|
-
|
|
13295
|
-
|
|
13296
|
-
|
|
13297
|
-
|
|
13298
|
-
|
|
13299
|
-
|
|
13300
|
-
|
|
13301
|
-
|
|
13302
|
-
|
|
13303
|
-
|
|
13304
|
-
|
|
13772
|
+
const clearedBranchKeys = database.getAllBranches();
|
|
13773
|
+
store.clear();
|
|
13774
|
+
store.save();
|
|
13775
|
+
invertedIndex.clear();
|
|
13776
|
+
this.saveInvertedIndex(invertedIndex);
|
|
13777
|
+
this.fileHashCache.clear();
|
|
13778
|
+
this.saveFileHashCache();
|
|
13779
|
+
database.clearAllIndexedData();
|
|
13780
|
+
this.deleteBranchCommitMetadata(database, clearedBranchKeys);
|
|
13781
|
+
this.clearFailedBatchState();
|
|
13782
|
+
database.deleteMetadata("index.version");
|
|
13783
|
+
database.deleteMetadata("index.pathStorageVersion");
|
|
13784
|
+
database.deleteMetadata("index.embeddingProvider");
|
|
13785
|
+
database.deleteMetadata("index.embeddingModel");
|
|
13786
|
+
database.deleteMetadata("index.embeddingDimensions");
|
|
13787
|
+
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
13788
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
13789
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey(projectIdentityHash));
|
|
13790
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey(projectIdentityHash));
|
|
13791
|
+
database.deleteMetadata(this.getLegacyMigrationMetadataKey(projectIdentityHash));
|
|
13792
|
+
database.deleteMetadata("index.createdAt");
|
|
13793
|
+
database.deleteMetadata("index.updatedAt");
|
|
13794
|
+
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
13795
|
+
}
|
|
13796
|
+
clearGlobalIndexUnlocked(projectRoot = this.projectRoot, roots = this.getScopedRoots(), recoveryDecision) {
|
|
13797
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
13798
|
+
store.load();
|
|
13799
|
+
invertedIndex.load();
|
|
13800
|
+
this.loadFileHashCache();
|
|
13801
|
+
const compatibility = this.checkCompatibility();
|
|
13802
|
+
const compatibilityDecision = recoveryDecision ?? (compatibility.compatible ? "compatible" : compatibility.code === "EMBEDDING_STRATEGY_MISMATCH" /* EMBEDDING_STRATEGY_MISMATCH */ ? "embedding-strategy-mismatch" : "incompatible");
|
|
13803
|
+
const allMetadata = store.getAllMetadata();
|
|
13804
|
+
const hasForeignData = allMetadata.some(({ metadata }) => !this.isFileInCurrentScope(metadata.filePath, roots)) || this.hasForeignScopedBranchData(projectRoot, roots) || this.hasForeignScopedFileHashData(roots) || this.hasForeignScopedFailedBatches(roots);
|
|
13805
|
+
if (compatibilityDecision !== "compatible" && hasForeignData) {
|
|
13806
|
+
if (compatibilityDecision === "embedding-strategy-mismatch") {
|
|
13807
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot);
|
|
13808
|
+
this.clearScopedFileHashCache(roots);
|
|
13809
|
+
this.clearScopedFailedBatches(roots);
|
|
13810
|
+
const projectIdentityHash = this.getProjectIdentityHash(projectRoot);
|
|
13811
|
+
database.setMetadata(this.getProjectForceReembedMetadataKey(projectIdentityHash), "true");
|
|
13812
|
+
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey(projectIdentityHash));
|
|
13813
|
+
database.deleteMetadata(this.getProjectMigrationFinalizedMetadataKey(projectIdentityHash));
|
|
13814
|
+
if (projectRoot === this.projectRoot) {
|
|
13305
13815
|
this.indexCompatibility = { compatible: true };
|
|
13306
|
-
return;
|
|
13307
13816
|
}
|
|
13308
|
-
throw new Error(
|
|
13309
|
-
`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.`
|
|
13310
|
-
);
|
|
13311
|
-
}
|
|
13312
|
-
if (!hasForeignData) {
|
|
13313
|
-
const clearedBranchKeys2 = database.getAllBranches();
|
|
13314
|
-
store.clear();
|
|
13315
|
-
store.save();
|
|
13316
|
-
invertedIndex.clear();
|
|
13317
|
-
this.saveInvertedIndex(invertedIndex);
|
|
13318
|
-
this.fileHashCache.clear();
|
|
13319
|
-
this.saveFileHashCache();
|
|
13320
|
-
database.clearAllIndexedData();
|
|
13321
|
-
this.deleteBranchCommitMetadata(database, clearedBranchKeys2);
|
|
13322
|
-
this.clearFailedBatchState();
|
|
13323
|
-
database.deleteMetadata("index.version");
|
|
13324
|
-
database.deleteMetadata("index.pathStorageVersion");
|
|
13325
|
-
database.deleteMetadata("index.embeddingProvider");
|
|
13326
|
-
database.deleteMetadata("index.embeddingModel");
|
|
13327
|
-
database.deleteMetadata("index.embeddingDimensions");
|
|
13328
|
-
database.deleteMetadata("index.embeddingStrategyVersion");
|
|
13329
|
-
database.deleteMetadata(this.getProjectEmbeddingStrategyMetadataKey());
|
|
13330
|
-
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
13331
|
-
database.deleteMetadata(this.getLegacyMigrationMetadataKey());
|
|
13332
|
-
database.deleteMetadata("index.createdAt");
|
|
13333
|
-
database.deleteMetadata("index.updatedAt");
|
|
13334
|
-
this.indexCompatibility = this.validateIndexCompatibility(this.configuredProviderInfo);
|
|
13335
13817
|
return;
|
|
13336
13818
|
}
|
|
13337
|
-
|
|
13338
|
-
|
|
13339
|
-
|
|
13819
|
+
throw new Error(
|
|
13820
|
+
`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.`
|
|
13821
|
+
);
|
|
13822
|
+
}
|
|
13823
|
+
if (!hasForeignData) {
|
|
13824
|
+
this.clearGlobalIndexDataUnlocked(projectRoot);
|
|
13825
|
+
return;
|
|
13826
|
+
}
|
|
13827
|
+
this.clearSharedIndexProjectData(store, invertedIndex, database, roots, projectRoot);
|
|
13828
|
+
this.clearScopedFileHashCache(roots);
|
|
13829
|
+
this.clearScopedFailedBatches(roots);
|
|
13830
|
+
if (projectRoot === this.projectRoot) {
|
|
13340
13831
|
this.indexCompatibility = compatibility;
|
|
13832
|
+
}
|
|
13833
|
+
}
|
|
13834
|
+
async clearIndexUnlocked(recoveryDecision) {
|
|
13835
|
+
const { store, invertedIndex, database } = this.requireLoadedIndexState();
|
|
13836
|
+
if (this.config.scope === "global") {
|
|
13837
|
+
this.clearGlobalIndexUnlocked(this.projectRoot, this.getScopedRoots(), recoveryDecision);
|
|
13341
13838
|
return;
|
|
13342
13839
|
}
|
|
13343
13840
|
if (!this.isProjectOwnedIndexPath()) {
|
|
@@ -13503,6 +14000,7 @@ var Indexer = class _Indexer {
|
|
|
13503
14000
|
)) {
|
|
13504
14001
|
const chunks = retryBatch.map(({ chunk }) => chunk);
|
|
13505
14002
|
const attemptCounts = new Map(retryBatch.map(({ chunk, attemptCount }) => [chunk.id, attemptCount]));
|
|
14003
|
+
this.restoreMissingChunkRows(database, chunks);
|
|
13506
14004
|
const batchResult = await this.processPendingChunkBatch(chunks, {
|
|
13507
14005
|
store,
|
|
13508
14006
|
provider,
|
|
@@ -13517,6 +14015,7 @@ var Indexer = class _Indexer {
|
|
|
13517
14015
|
forceReembed: false,
|
|
13518
14016
|
reuseCachedEmbeddings: false,
|
|
13519
14017
|
incrementRepeatedFailures: false,
|
|
14018
|
+
forceSingleItemBatches: true,
|
|
13520
14019
|
onSucceeded: (succeededChunks) => {
|
|
13521
14020
|
database.addChunksToBranchBatch(
|
|
13522
14021
|
this.getBranchCatalogKey(),
|
|
@@ -13538,9 +14037,12 @@ var Indexer = class _Indexer {
|
|
|
13538
14037
|
this.saveInvertedIndex(invertedIndex);
|
|
13539
14038
|
}
|
|
13540
14039
|
if (roots && succeeded > 0 && remaining === 0 && this.hasProjectForceReembedPending()) {
|
|
13541
|
-
database.
|
|
13542
|
-
|
|
13543
|
-
|
|
14040
|
+
const migrationFinalized = database.getMetadata(this.getProjectMigrationFinalizedMetadataKey()) === "true";
|
|
14041
|
+
if (migrationFinalized) {
|
|
14042
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
14043
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
14044
|
+
this.indexCompatibility = { compatible: true };
|
|
14045
|
+
}
|
|
13544
14046
|
}
|
|
13545
14047
|
return { succeeded, failed, remaining };
|
|
13546
14048
|
}
|
|
@@ -13562,7 +14064,8 @@ var Indexer = class _Indexer {
|
|
|
13562
14064
|
latestById.set(chunkId, {
|
|
13563
14065
|
attemptCount: batch.attemptCount,
|
|
13564
14066
|
error: batch.error,
|
|
13565
|
-
lastAttempt: batch.lastAttempt
|
|
14067
|
+
lastAttempt: batch.lastAttempt,
|
|
14068
|
+
chunks: [rawChunk]
|
|
13566
14069
|
});
|
|
13567
14070
|
}
|
|
13568
14071
|
}
|
|
@@ -13629,6 +14132,7 @@ var Indexer = class _Indexer {
|
|
|
13629
14132
|
this.getBranchCatalogKeys().flatMap((branchKey) => database.getBranchChunkIds(branchKey))
|
|
13630
14133
|
);
|
|
13631
14134
|
}
|
|
14135
|
+
const temporalChunkIds = this.getTemporalChunkIds(database, options);
|
|
13632
14136
|
const { hasInitializedBranchCatalog, shouldPrefilterByBranch } = this.getBranchPrefilterState(database, branchChunkIds);
|
|
13633
14137
|
const prefilterMs = import_perf_hooks.performance.now() - prefilterStartTime;
|
|
13634
14138
|
const vectorStartTime = import_perf_hooks.performance.now();
|
|
@@ -13637,7 +14141,8 @@ var Indexer = class _Indexer {
|
|
|
13637
14141
|
embedding,
|
|
13638
14142
|
limit * 2,
|
|
13639
14143
|
branchChunkIds,
|
|
13640
|
-
shouldPrefilterByBranch
|
|
14144
|
+
shouldPrefilterByBranch,
|
|
14145
|
+
temporalChunkIds
|
|
13641
14146
|
);
|
|
13642
14147
|
const vectorMs = import_perf_hooks.performance.now() - vectorStartTime;
|
|
13643
14148
|
if (this.config.scope !== "global" && branchChunkIds && !hasInitializedBranchCatalog) {
|
|
@@ -14386,9 +14891,11 @@ async function searchCodebase(projectRoot, host, query, options = {}) {
|
|
|
14386
14891
|
contextLines: options.contextLines,
|
|
14387
14892
|
metadataOnly: options.metadataOnly,
|
|
14388
14893
|
definitionIntent: options.definitionIntent,
|
|
14894
|
+
prioritizeSourcePaths: options.prioritizeSourcePaths,
|
|
14389
14895
|
blameAuthor: options.blameAuthor,
|
|
14390
14896
|
blameSha: options.blameSha,
|
|
14391
14897
|
blameSince: options.blameSince,
|
|
14898
|
+
blameUntil: options.blameUntil,
|
|
14392
14899
|
trace: options.trace
|
|
14393
14900
|
});
|
|
14394
14901
|
}
|
|
@@ -14434,7 +14941,9 @@ async function findSimilarCode(projectRoot, host, code, options = {}) {
|
|
|
14434
14941
|
fileType: options.fileType,
|
|
14435
14942
|
directory: options.directory,
|
|
14436
14943
|
chunkType: options.chunkType,
|
|
14437
|
-
excludeFile: options.excludeFile
|
|
14944
|
+
excludeFile: options.excludeFile,
|
|
14945
|
+
blameSince: options.blameSince,
|
|
14946
|
+
blameUntil: options.blameUntil
|
|
14438
14947
|
});
|
|
14439
14948
|
}
|
|
14440
14949
|
async function implementationLookup(projectRoot, host, query, options = {}) {
|
|
@@ -17973,13 +18482,19 @@ async function resolveSearchContext(input, operations) {
|
|
|
17973
18482
|
(trace) => operations.lookup(symbol, MAX_CONTEXT_RESULT_LIMIT, scope, input.diagnostic ? trace : void 0)
|
|
17974
18483
|
);
|
|
17975
18484
|
};
|
|
17976
|
-
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt) => {
|
|
18485
|
+
const tryConceptualSearch = async (searchQuery, scope, relaxedFieldsForAttempt, prioritizeSourcePaths) => {
|
|
17977
18486
|
return recordAttempt(
|
|
17978
18487
|
"conceptual",
|
|
17979
18488
|
searchQuery,
|
|
17980
18489
|
scope,
|
|
17981
18490
|
relaxedFieldsForAttempt,
|
|
17982
|
-
(trace) => operations.search(
|
|
18491
|
+
(trace) => operations.search(
|
|
18492
|
+
searchQuery,
|
|
18493
|
+
MAX_CONTEXT_RESULT_LIMIT,
|
|
18494
|
+
scope,
|
|
18495
|
+
input.diagnostic ? trace : void 0,
|
|
18496
|
+
{ prioritizeSourcePaths }
|
|
18497
|
+
)
|
|
17983
18498
|
);
|
|
17984
18499
|
};
|
|
17985
18500
|
const findSuccessfulAttemptState = (route) => {
|
|
@@ -18107,10 +18622,12 @@ Explicit symbol lookup only; conceptual search was not attempted.`
|
|
|
18107
18622
|
}
|
|
18108
18623
|
}
|
|
18109
18624
|
for (const attempt of conceptualAttemptPlan) {
|
|
18625
|
+
const attemptIntent = analyzeQueryIntent(attempt.queryText);
|
|
18626
|
+
const prioritizeSourcePaths = attemptIntent.primary !== "docs" && attemptIntent.primary !== "test";
|
|
18110
18627
|
if (inferredSymbol && attempt.queryText === inferredSymbol && attempt.queryText !== query) {
|
|
18111
18628
|
decisions.fallbackFromOriginalConceptualToInferred = true;
|
|
18112
18629
|
}
|
|
18113
|
-
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed);
|
|
18630
|
+
const results = await tryConceptualSearch(attempt.queryText, attempt.scope, attempt.relaxed, prioritizeSourcePaths);
|
|
18114
18631
|
if (results.length > 0) {
|
|
18115
18632
|
const heading = buildPackHeading("conceptual", decisions);
|
|
18116
18633
|
const intent = analyzeQueryIntent(attempt.queryText);
|
|
@@ -18247,12 +18764,13 @@ async function resolveCodebaseContextUnmeasured(projectRoot, host, input) {
|
|
|
18247
18764
|
directory: scope.directory,
|
|
18248
18765
|
trace
|
|
18249
18766
|
}),
|
|
18250
|
-
search: (queryText, retrievalLimit, scope, trace) => searchCodebase(projectRoot, host, queryText, {
|
|
18767
|
+
search: (queryText, retrievalLimit, scope, trace, searchOptions) => searchCodebase(projectRoot, host, queryText, {
|
|
18251
18768
|
limit: retrievalLimit,
|
|
18252
18769
|
fileType: scope.fileType,
|
|
18253
18770
|
directory: scope.directory,
|
|
18254
18771
|
metadataOnly: true,
|
|
18255
|
-
trace
|
|
18772
|
+
trace,
|
|
18773
|
+
prioritizeSourcePaths: searchOptions?.prioritizeSourcePaths
|
|
18256
18774
|
})
|
|
18257
18775
|
});
|
|
18258
18776
|
}
|
|
@@ -19167,7 +19685,8 @@ var codebase_peek = tool({
|
|
|
19167
19685
|
chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
|
|
19168
19686
|
blameAuthor: z3.string().optional().describe("Filter by git blame author name or email"),
|
|
19169
19687
|
blameSha: z3.string().optional().describe("Filter by git blame commit SHA or prefix"),
|
|
19170
|
-
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
|
|
19688
|
+
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)"),
|
|
19689
|
+
blameUntil: z3.string().optional().describe("Filter to chunks last changed on or before this date (e.g., 2025-01-31)")
|
|
19171
19690
|
},
|
|
19172
19691
|
async execute(args, context) {
|
|
19173
19692
|
return searchCodebaseWithEffectiveness(context?.worktree, DEFAULT_HOST, "peek", args.query, {
|
|
@@ -19178,7 +19697,8 @@ var codebase_peek = tool({
|
|
|
19178
19697
|
metadataOnly: true,
|
|
19179
19698
|
blameAuthor: args.blameAuthor,
|
|
19180
19699
|
blameSha: args.blameSha,
|
|
19181
|
-
blameSince: args.blameSince
|
|
19700
|
+
blameSince: args.blameSince,
|
|
19701
|
+
blameUntil: args.blameUntil
|
|
19182
19702
|
}, (results) => {
|
|
19183
19703
|
const text = formatCodebasePeek(results);
|
|
19184
19704
|
return { output: text, text };
|
|
@@ -19240,7 +19760,9 @@ var find_similar = tool({
|
|
|
19240
19760
|
fileType: z3.string().optional().describe("Filter by file extension (e.g., 'ts', 'py', 'rs')"),
|
|
19241
19761
|
directory: z3.string().optional().describe("Filter by directory path (e.g., 'src/utils', 'lib')"),
|
|
19242
19762
|
chunkType: z3.enum(CHUNK_TYPE_VALUES).optional().describe("Filter by code chunk type"),
|
|
19243
|
-
excludeFile: z3.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)")
|
|
19763
|
+
excludeFile: z3.string().optional().describe("Exclude results from this file path (useful when searching for duplicates of code from a specific file)"),
|
|
19764
|
+
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)"),
|
|
19765
|
+
blameUntil: z3.string().optional().describe("Filter to chunks last changed on or before this date (e.g., 2025-01-31)")
|
|
19244
19766
|
},
|
|
19245
19767
|
async execute(args, context) {
|
|
19246
19768
|
const results = await findSimilarCode(context?.worktree, DEFAULT_HOST, args.code, {
|
|
@@ -19248,7 +19770,9 @@ var find_similar = tool({
|
|
|
19248
19770
|
fileType: args.fileType,
|
|
19249
19771
|
directory: args.directory,
|
|
19250
19772
|
chunkType: args.chunkType,
|
|
19251
|
-
excludeFile: args.excludeFile
|
|
19773
|
+
excludeFile: args.excludeFile,
|
|
19774
|
+
blameSince: args.blameSince,
|
|
19775
|
+
blameUntil: args.blameUntil
|
|
19252
19776
|
});
|
|
19253
19777
|
if (results.length === 0) {
|
|
19254
19778
|
return "No similar code found. Try a different snippet or run index_codebase first.";
|
|
@@ -19267,7 +19791,8 @@ var codebase_search = tool({
|
|
|
19267
19791
|
contextLines: z3.number().optional().describe("Number of extra lines to include before/after each match (default: 0)"),
|
|
19268
19792
|
blameAuthor: z3.string().optional().describe("Filter by git blame author name or email"),
|
|
19269
19793
|
blameSha: z3.string().optional().describe("Filter by git blame commit SHA or prefix"),
|
|
19270
|
-
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)")
|
|
19794
|
+
blameSince: z3.string().optional().describe("Filter to chunks last changed on or after this date (e.g., 2025-01-01)"),
|
|
19795
|
+
blameUntil: z3.string().optional().describe("Filter to chunks last changed on or before this date (e.g., 2025-01-31)")
|
|
19271
19796
|
},
|
|
19272
19797
|
async execute(args, context) {
|
|
19273
19798
|
return searchCodebaseWithEffectiveness(context?.worktree, DEFAULT_HOST, "search", args.query, {
|
|
@@ -19278,7 +19803,8 @@ var codebase_search = tool({
|
|
|
19278
19803
|
contextLines: args.contextLines,
|
|
19279
19804
|
blameAuthor: args.blameAuthor,
|
|
19280
19805
|
blameSha: args.blameSha,
|
|
19281
|
-
blameSince: args.blameSince
|
|
19806
|
+
blameSince: args.blameSince,
|
|
19807
|
+
blameUntil: args.blameUntil
|
|
19282
19808
|
}, (results) => {
|
|
19283
19809
|
const text = results.length === 0 ? "No matching code found. Try a different query or run index_codebase first." : formatSearchResults(results, "score");
|
|
19284
19810
|
return { output: text, text };
|
|
@@ -19491,6 +20017,12 @@ var PI_TOOL_NAMES = [
|
|
|
19491
20017
|
TOOL_NAME.PI_KNOWLEDGE_BASE_ADD,
|
|
19492
20018
|
TOOL_NAME.PI_KNOWLEDGE_BASE_REMOVE
|
|
19493
20019
|
];
|
|
20020
|
+
var MCP_TOOL_NAMES = [
|
|
20021
|
+
...PORTABLE_TOOL_NAMES,
|
|
20022
|
+
TOOL_NAME.ADD_KNOWLEDGE_BASE,
|
|
20023
|
+
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
20024
|
+
TOOL_NAME.REMOVE_KNOWLEDGE_BASE
|
|
20025
|
+
];
|
|
19494
20026
|
|
|
19495
20027
|
// src/commands/loader.ts
|
|
19496
20028
|
var import_fs16 = require("fs");
|