opencode-codebase-index 0.21.0 → 0.22.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/dist/cli.cjs +2154 -1391
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2237 -1474
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +1975 -1365
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2017 -1407
- package/dist/index.js.map +1 -1
- package/dist/pi-extension.cjs +1699 -1087
- package/dist/pi-extension.cjs.map +1 -1
- package/dist/pi-extension.js +1736 -1124
- 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 +5 -3
package/dist/pi-extension.js
CHANGED
|
@@ -328,7 +328,7 @@ var require_ignore = __commonJS({
|
|
|
328
328
|
// path matching.
|
|
329
329
|
// - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
|
|
330
330
|
// @returns {TestResult} true if a file is ignored
|
|
331
|
-
test(
|
|
331
|
+
test(path21, checkUnignored, mode) {
|
|
332
332
|
let ignored = false;
|
|
333
333
|
let unignored = false;
|
|
334
334
|
let matchedRule;
|
|
@@ -337,7 +337,7 @@ var require_ignore = __commonJS({
|
|
|
337
337
|
if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
|
|
338
338
|
return;
|
|
339
339
|
}
|
|
340
|
-
const matched = rule[mode].test(
|
|
340
|
+
const matched = rule[mode].test(path21);
|
|
341
341
|
if (!matched) {
|
|
342
342
|
return;
|
|
343
343
|
}
|
|
@@ -358,17 +358,17 @@ var require_ignore = __commonJS({
|
|
|
358
358
|
var throwError = (message, Ctor) => {
|
|
359
359
|
throw new Ctor(message);
|
|
360
360
|
};
|
|
361
|
-
var checkPath = (
|
|
362
|
-
if (!isString(
|
|
361
|
+
var checkPath = (path21, originalPath, doThrow) => {
|
|
362
|
+
if (!isString(path21)) {
|
|
363
363
|
return doThrow(
|
|
364
364
|
`path must be a string, but got \`${originalPath}\``,
|
|
365
365
|
TypeError
|
|
366
366
|
);
|
|
367
367
|
}
|
|
368
|
-
if (!
|
|
368
|
+
if (!path21) {
|
|
369
369
|
return doThrow(`path must not be empty`, TypeError);
|
|
370
370
|
}
|
|
371
|
-
if (checkPath.isNotRelative(
|
|
371
|
+
if (checkPath.isNotRelative(path21)) {
|
|
372
372
|
const r = "`path.relative()`d";
|
|
373
373
|
return doThrow(
|
|
374
374
|
`path should be a ${r} string, but got "${originalPath}"`,
|
|
@@ -377,7 +377,7 @@ var require_ignore = __commonJS({
|
|
|
377
377
|
}
|
|
378
378
|
return true;
|
|
379
379
|
};
|
|
380
|
-
var isNotRelative = (
|
|
380
|
+
var isNotRelative = (path21) => REGEX_TEST_INVALID_PATH.test(path21);
|
|
381
381
|
checkPath.isNotRelative = isNotRelative;
|
|
382
382
|
checkPath.convert = (p) => p;
|
|
383
383
|
var Ignore2 = class {
|
|
@@ -407,19 +407,19 @@ var require_ignore = __commonJS({
|
|
|
407
407
|
}
|
|
408
408
|
// @returns {TestResult}
|
|
409
409
|
_test(originalPath, cache, checkUnignored, slices) {
|
|
410
|
-
const
|
|
410
|
+
const path21 = originalPath && checkPath.convert(originalPath);
|
|
411
411
|
checkPath(
|
|
412
|
-
|
|
412
|
+
path21,
|
|
413
413
|
originalPath,
|
|
414
414
|
this._strictPathCheck ? throwError : RETURN_FALSE
|
|
415
415
|
);
|
|
416
|
-
return this._t(
|
|
416
|
+
return this._t(path21, cache, checkUnignored, slices);
|
|
417
417
|
}
|
|
418
|
-
checkIgnore(
|
|
419
|
-
if (!REGEX_TEST_TRAILING_SLASH.test(
|
|
420
|
-
return this.test(
|
|
418
|
+
checkIgnore(path21) {
|
|
419
|
+
if (!REGEX_TEST_TRAILING_SLASH.test(path21)) {
|
|
420
|
+
return this.test(path21);
|
|
421
421
|
}
|
|
422
|
-
const slices =
|
|
422
|
+
const slices = path21.split(SLASH).filter(Boolean);
|
|
423
423
|
slices.pop();
|
|
424
424
|
if (slices.length) {
|
|
425
425
|
const parent = this._t(
|
|
@@ -432,18 +432,18 @@ var require_ignore = __commonJS({
|
|
|
432
432
|
return parent;
|
|
433
433
|
}
|
|
434
434
|
}
|
|
435
|
-
return this._rules.test(
|
|
435
|
+
return this._rules.test(path21, false, MODE_CHECK_IGNORE);
|
|
436
436
|
}
|
|
437
|
-
_t(
|
|
438
|
-
if (
|
|
439
|
-
return cache[
|
|
437
|
+
_t(path21, cache, checkUnignored, slices) {
|
|
438
|
+
if (path21 in cache) {
|
|
439
|
+
return cache[path21];
|
|
440
440
|
}
|
|
441
441
|
if (!slices) {
|
|
442
|
-
slices =
|
|
442
|
+
slices = path21.split(SLASH).filter(Boolean);
|
|
443
443
|
}
|
|
444
444
|
slices.pop();
|
|
445
445
|
if (!slices.length) {
|
|
446
|
-
return cache[
|
|
446
|
+
return cache[path21] = this._rules.test(path21, checkUnignored, MODE_IGNORE);
|
|
447
447
|
}
|
|
448
448
|
const parent = this._t(
|
|
449
449
|
slices.join(SLASH) + SLASH,
|
|
@@ -451,29 +451,29 @@ var require_ignore = __commonJS({
|
|
|
451
451
|
checkUnignored,
|
|
452
452
|
slices
|
|
453
453
|
);
|
|
454
|
-
return cache[
|
|
454
|
+
return cache[path21] = parent.ignored ? parent : this._rules.test(path21, checkUnignored, MODE_IGNORE);
|
|
455
455
|
}
|
|
456
|
-
ignores(
|
|
457
|
-
return this._test(
|
|
456
|
+
ignores(path21) {
|
|
457
|
+
return this._test(path21, this._ignoreCache, false).ignored;
|
|
458
458
|
}
|
|
459
459
|
createFilter() {
|
|
460
|
-
return (
|
|
460
|
+
return (path21) => !this.ignores(path21);
|
|
461
461
|
}
|
|
462
462
|
filter(paths) {
|
|
463
463
|
return makeArray(paths).filter(this.createFilter());
|
|
464
464
|
}
|
|
465
465
|
// @returns {TestResult}
|
|
466
|
-
test(
|
|
467
|
-
return this._test(
|
|
466
|
+
test(path21) {
|
|
467
|
+
return this._test(path21, this._testCache, true);
|
|
468
468
|
}
|
|
469
469
|
};
|
|
470
470
|
var factory = (options) => new Ignore2(options);
|
|
471
|
-
var isPathValid = (
|
|
471
|
+
var isPathValid = (path21) => checkPath(path21 && checkPath.convert(path21), path21, RETURN_FALSE);
|
|
472
472
|
var setupWindows = () => {
|
|
473
473
|
const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
|
|
474
474
|
checkPath.convert = makePosix;
|
|
475
475
|
const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
|
|
476
|
-
checkPath.isNotRelative = (
|
|
476
|
+
checkPath.isNotRelative = (path21) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path21) || isNotRelative(path21);
|
|
477
477
|
};
|
|
478
478
|
if (
|
|
479
479
|
// Detect `process` so that it can run in browsers.
|
|
@@ -804,7 +804,8 @@ function getDefaultSearchConfig() {
|
|
|
804
804
|
contextLines: 0,
|
|
805
805
|
routingHints: true,
|
|
806
806
|
routingGraphHandoffHints: false,
|
|
807
|
-
routingHintRole: "system"
|
|
807
|
+
routingHintRole: "system",
|
|
808
|
+
communityBoost: 0
|
|
808
809
|
};
|
|
809
810
|
}
|
|
810
811
|
function getDefaultRerankerBaseUrl(provider) {
|
|
@@ -937,7 +938,8 @@ function parseConfig(raw) {
|
|
|
937
938
|
contextLines: typeof rawSearch.contextLines === "number" ? Math.min(50, Math.max(0, rawSearch.contextLines)) : defaultSearch.contextLines,
|
|
938
939
|
routingHints: typeof rawSearch.routingHints === "boolean" ? rawSearch.routingHints : defaultSearch.routingHints,
|
|
939
940
|
routingGraphHandoffHints: typeof rawSearch.routingGraphHandoffHints === "boolean" ? rawSearch.routingGraphHandoffHints : defaultSearch.routingGraphHandoffHints,
|
|
940
|
-
routingHintRole: rawSearch.routingHintRole === "developer" || rawSearch.routingHintRole === "system" ? rawSearch.routingHintRole : defaultSearch.routingHintRole
|
|
941
|
+
routingHintRole: rawSearch.routingHintRole === "developer" || rawSearch.routingHintRole === "system" ? rawSearch.routingHintRole : defaultSearch.routingHintRole,
|
|
942
|
+
communityBoost: typeof rawSearch.communityBoost === "number" && Number.isFinite(rawSearch.communityBoost) ? Math.min(1, Math.max(0, rawSearch.communityBoost)) : defaultSearch.communityBoost
|
|
941
943
|
};
|
|
942
944
|
const rawDebug = input.debug && typeof input.debug === "object" ? input.debug : {};
|
|
943
945
|
const debug = {
|
|
@@ -1437,9 +1439,122 @@ function formatPrImpact(result) {
|
|
|
1437
1439
|
return lines.join("\n");
|
|
1438
1440
|
}
|
|
1439
1441
|
|
|
1442
|
+
// src/tools/format-communities.ts
|
|
1443
|
+
function compareText(left, right) {
|
|
1444
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
1445
|
+
}
|
|
1446
|
+
function buildCodeCommunitiesResult(communities, centrality, couplings = [], options = {}) {
|
|
1447
|
+
const minSize = options.minSize ?? 1;
|
|
1448
|
+
const limit = options.limit ?? 20;
|
|
1449
|
+
const hubThreshold = options.hubThreshold ?? 5;
|
|
1450
|
+
const minCoupling = options.minCoupling ?? 1;
|
|
1451
|
+
const couplingLimit = options.couplingLimit ?? 20;
|
|
1452
|
+
const communityMap = /* @__PURE__ */ new Map();
|
|
1453
|
+
for (const c of communities) {
|
|
1454
|
+
let entry = communityMap.get(c.communityId);
|
|
1455
|
+
if (!entry) {
|
|
1456
|
+
entry = { label: c.communityLabel, members: [] };
|
|
1457
|
+
communityMap.set(c.communityId, entry);
|
|
1458
|
+
}
|
|
1459
|
+
entry.members.push(c);
|
|
1460
|
+
}
|
|
1461
|
+
const sortedCommunities = Array.from(communityMap.entries()).map(([id, entry]) => ({
|
|
1462
|
+
id,
|
|
1463
|
+
label: entry.label,
|
|
1464
|
+
symbolCount: entry.members.length,
|
|
1465
|
+
members: entry.members.map((m) => ({
|
|
1466
|
+
symbolId: m.symbolId,
|
|
1467
|
+
symbolName: m.symbolName,
|
|
1468
|
+
filePath: m.filePath
|
|
1469
|
+
})).sort((a, b) => compareText(a.symbolName, b.symbolName) || compareText(a.symbolId, b.symbolId))
|
|
1470
|
+
})).filter((c) => c.symbolCount >= minSize).sort((a, b) => b.symbolCount - a.symbolCount || compareText(a.label, b.label) || a.id - b.id).slice(0, limit);
|
|
1471
|
+
const communityBySymbol = new Map(communities.map((community) => [community.symbolId, community]));
|
|
1472
|
+
const communityLabelById = new Map(communities.map((community) => [community.communityId, community.communityLabel]));
|
|
1473
|
+
const hubNodes = centrality.map((c) => ({
|
|
1474
|
+
symbolId: c.symbolId,
|
|
1475
|
+
symbolName: c.symbolName,
|
|
1476
|
+
filePath: c.filePath,
|
|
1477
|
+
callerCount: c.callerCount,
|
|
1478
|
+
calleeCount: c.calleeCount,
|
|
1479
|
+
totalConnections: c.totalConnections,
|
|
1480
|
+
crossCommunityConnections: communityBySymbol.get(c.symbolId)?.crossCommunityConnections ?? 0
|
|
1481
|
+
})).filter((h) => h.crossCommunityConnections >= hubThreshold).sort(
|
|
1482
|
+
(a, b) => b.crossCommunityConnections - a.crossCommunityConnections || b.totalConnections - a.totalConnections || compareText(a.symbolId, b.symbolId)
|
|
1483
|
+
).slice(0, limit);
|
|
1484
|
+
const canonicalCoupling = (value) => Math.trunc(value);
|
|
1485
|
+
const couplingItems = couplings.map((entry) => {
|
|
1486
|
+
const relationships = entry.relationships ?? entry.representativeRelationships ?? [];
|
|
1487
|
+
const normalizedRelationships = relationships.map((relationship) => ({
|
|
1488
|
+
fromSymbolId: relationship.fromSymbolId,
|
|
1489
|
+
fromSymbolName: relationship.fromSymbolName,
|
|
1490
|
+
fromFilePath: relationship.fromFilePath,
|
|
1491
|
+
toSymbolId: relationship.toSymbolId,
|
|
1492
|
+
toSymbolName: relationship.toSymbolName,
|
|
1493
|
+
toFilePath: relationship.toFilePath
|
|
1494
|
+
})).sort(
|
|
1495
|
+
(left, right) => compareText(left.fromSymbolName, right.fromSymbolName) || compareText(left.fromSymbolId, right.fromSymbolId) || compareText(left.toSymbolName, right.toSymbolName) || compareText(left.toSymbolId, right.toSymbolId) || compareText(left.fromFilePath, right.fromFilePath) || compareText(left.toFilePath, right.toFilePath)
|
|
1496
|
+
).slice(0, 5);
|
|
1497
|
+
const communityA = canonicalCoupling(Math.min(entry.communityA, entry.communityB));
|
|
1498
|
+
const communityB = canonicalCoupling(Math.max(entry.communityA, entry.communityB));
|
|
1499
|
+
return {
|
|
1500
|
+
communityA,
|
|
1501
|
+
communityB,
|
|
1502
|
+
communityAName: communityLabelById.get(communityA) ?? `Community ${communityA}`,
|
|
1503
|
+
communityBName: communityLabelById.get(communityB) ?? `Community ${communityB}`,
|
|
1504
|
+
distinctConnections: canonicalCoupling(entry.count),
|
|
1505
|
+
representativeRelationships: normalizedRelationships
|
|
1506
|
+
};
|
|
1507
|
+
}).filter((entry) => entry.distinctConnections >= minCoupling).sort(
|
|
1508
|
+
(left, right) => right.distinctConnections - left.distinctConnections || compareText(left.communityAName, right.communityAName) || compareText(left.communityBName, right.communityBName) || compareText(left.communityAName + left.communityBName, right.communityAName + right.communityBName) || left.communityA - right.communityA || left.communityB - right.communityB
|
|
1509
|
+
).slice(0, Math.max(1, Math.floor(couplingLimit)));
|
|
1510
|
+
return {
|
|
1511
|
+
communities: sortedCommunities,
|
|
1512
|
+
hubNodes,
|
|
1513
|
+
totalSymbols: communities.length,
|
|
1514
|
+
totalCommunities: communityMap.size,
|
|
1515
|
+
couplings: couplingItems
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
function formatCodeCommunities(result) {
|
|
1519
|
+
const lines = [];
|
|
1520
|
+
lines.push(`\u2192 Communities: ${result.totalCommunities} (${result.communities.length} shown, ${result.totalSymbols} symbols total)`);
|
|
1521
|
+
for (const community of result.communities) {
|
|
1522
|
+
lines.push(` Community ${community.id} (${community.label}): ${community.symbolCount} symbols`);
|
|
1523
|
+
const shownMembers = community.members.slice(0, 8);
|
|
1524
|
+
for (const m of shownMembers) {
|
|
1525
|
+
lines.push(` - ${m.symbolName} (${m.filePath})`);
|
|
1526
|
+
}
|
|
1527
|
+
if (community.members.length > 8) {
|
|
1528
|
+
lines.push(` ... and ${community.members.length - 8} more`);
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
if (result.hubNodes.length > 0) {
|
|
1532
|
+
lines.push(`\u2192 Hub nodes (${result.hubNodes.length} shown, cross-community connections):`);
|
|
1533
|
+
for (const hub of result.hubNodes) {
|
|
1534
|
+
lines.push(
|
|
1535
|
+
` - ${hub.symbolName} (${hub.crossCommunityConnections} cross-community, ${hub.callerCount} callers, ${hub.calleeCount} callees) at ${hub.filePath}`
|
|
1536
|
+
);
|
|
1537
|
+
}
|
|
1538
|
+
} else {
|
|
1539
|
+
lines.push("\u2192 Hub nodes: none with significant cross-community connections");
|
|
1540
|
+
}
|
|
1541
|
+
if (result.couplings.length > 0) {
|
|
1542
|
+
lines.push(`\u2192 Community couplings: ${result.couplings.length} shown`);
|
|
1543
|
+
for (const coupling of result.couplings) {
|
|
1544
|
+
lines.push(` - ${coupling.communityAName} \u2194 ${coupling.communityBName}: ${coupling.distinctConnections} distinct connections`);
|
|
1545
|
+
for (const relationship of coupling.representativeRelationships) {
|
|
1546
|
+
lines.push(` - ${relationship.fromSymbolName} (${relationship.fromFilePath}) -> ${relationship.toSymbolName} (${relationship.toFilePath})`);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
} else {
|
|
1550
|
+
lines.push("\u2192 Community couplings: none above minCoupling threshold");
|
|
1551
|
+
}
|
|
1552
|
+
return lines.join("\n");
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1440
1555
|
// src/tools/operations.ts
|
|
1441
|
-
import { existsSync as
|
|
1442
|
-
import * as
|
|
1556
|
+
import { existsSync as existsSync12, realpathSync as realpathSync5, statSync as statSync5 } from "fs";
|
|
1557
|
+
import * as path20 from "path";
|
|
1443
1558
|
|
|
1444
1559
|
// src/config/paths.ts
|
|
1445
1560
|
import { existsSync as existsSync4 } from "fs";
|
|
@@ -1925,6 +2040,15 @@ function findKnowledgeBasePathIndex(knowledgeBases, inputPath, projectRoot3) {
|
|
|
1925
2040
|
);
|
|
1926
2041
|
}
|
|
1927
2042
|
|
|
2043
|
+
// src/tools/contracts.ts
|
|
2044
|
+
var CODE_COMMUNITIES_MIN_SIZE = 1;
|
|
2045
|
+
var CODE_COMMUNITIES_DEFAULT_LIMIT = 20;
|
|
2046
|
+
var CODE_COMMUNITIES_MAX_LIMIT = 100;
|
|
2047
|
+
var CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD = 5;
|
|
2048
|
+
var CODE_COMMUNITIES_MIN_COUPLING = 1;
|
|
2049
|
+
var CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT = 20;
|
|
2050
|
+
var CODE_COMMUNITIES_MAX_COUPLING_LIMIT = 100;
|
|
2051
|
+
|
|
1928
2052
|
// src/tools/context-pack.ts
|
|
1929
2053
|
import { get_encoding } from "tiktoken";
|
|
1930
2054
|
|
|
@@ -2568,8 +2692,8 @@ function formatExactSearchHandoff(results) {
|
|
|
2568
2692
|
}
|
|
2569
2693
|
function formatContextEvidence(result, index) {
|
|
2570
2694
|
const symbol = result.name ? ` ${JSON.stringify(compactEvidenceValue(result.name, 80))}` : "";
|
|
2571
|
-
const
|
|
2572
|
-
return `[${index}] ${result.chunkType}${symbol} in ${
|
|
2695
|
+
const path21 = compactEvidenceValue(result.filePath, 120);
|
|
2696
|
+
return `[${index}] ${result.chunkType}${symbol} in ${path21}:${result.startLine}-${result.endLine} (score ${result.score.toFixed(2)})`;
|
|
2573
2697
|
}
|
|
2574
2698
|
function formatContextPack(heading, selected, candidateCount, duplicateCount, limitOmittedCount, budgetOmittedCount, includeExactSearchHandoff) {
|
|
2575
2699
|
const lines = selected.map((result, index) => formatContextEvidence(result, index + 1));
|
|
@@ -4752,8 +4876,8 @@ function saveConfig(projectRoot3, config, host) {
|
|
|
4752
4876
|
}
|
|
4753
4877
|
|
|
4754
4878
|
// src/indexer/index.ts
|
|
4755
|
-
import { existsSync as
|
|
4756
|
-
import * as
|
|
4879
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8, statSync as statSync4, writeFileSync as writeFileSync3, renameSync as renameSync3, unlinkSync as unlinkSync2, mkdirSync as mkdirSync4, promises as fsPromises3 } from "fs";
|
|
4880
|
+
import * as path19 from "path";
|
|
4757
4881
|
import { performance as performance2 } from "perf_hooks";
|
|
4758
4882
|
import { execFile as execFile5 } from "child_process";
|
|
4759
4883
|
import { promisify as promisify4 } from "util";
|
|
@@ -7020,6 +7144,9 @@ function createMockNativeBinding() {
|
|
|
7020
7144
|
detectCommunities() {
|
|
7021
7145
|
throw error;
|
|
7022
7146
|
}
|
|
7147
|
+
detectCommunityCouplings() {
|
|
7148
|
+
throw error;
|
|
7149
|
+
}
|
|
7023
7150
|
computeCentrality() {
|
|
7024
7151
|
throw error;
|
|
7025
7152
|
}
|
|
@@ -7257,6 +7384,18 @@ var Database = class _Database {
|
|
|
7257
7384
|
}
|
|
7258
7385
|
this.closed = true;
|
|
7259
7386
|
}
|
|
7387
|
+
beginWriteTransaction() {
|
|
7388
|
+
this.throwIfClosed();
|
|
7389
|
+
this.inner.beginWriteTransaction();
|
|
7390
|
+
}
|
|
7391
|
+
commitWriteTransaction() {
|
|
7392
|
+
this.throwIfClosed();
|
|
7393
|
+
this.inner.commitWriteTransaction();
|
|
7394
|
+
}
|
|
7395
|
+
rollbackWriteTransaction() {
|
|
7396
|
+
this.throwIfClosed();
|
|
7397
|
+
this.inner.rollbackWriteTransaction();
|
|
7398
|
+
}
|
|
7260
7399
|
embeddingExists(contentHash) {
|
|
7261
7400
|
this.throwIfClosed();
|
|
7262
7401
|
return this.inner.embeddingExists(contentHash);
|
|
@@ -7516,6 +7655,13 @@ var Database = class _Database {
|
|
|
7516
7655
|
this.throwIfClosed();
|
|
7517
7656
|
return this.inner.computeCentrality(branch);
|
|
7518
7657
|
}
|
|
7658
|
+
detectCommunityCouplings(branch) {
|
|
7659
|
+
this.throwIfClosed();
|
|
7660
|
+
return this.inner.detectCommunityCouplings(branch).map((entry) => ({
|
|
7661
|
+
...entry,
|
|
7662
|
+
relationships: entry.representativeRelationships ?? []
|
|
7663
|
+
}));
|
|
7664
|
+
}
|
|
7519
7665
|
};
|
|
7520
7666
|
|
|
7521
7667
|
// src/git/branch-materialization.ts
|
|
@@ -8285,6 +8431,21 @@ async function getChunkGitBlame(projectRoot3, filePath, startLine, endLine) {
|
|
|
8285
8431
|
}
|
|
8286
8432
|
|
|
8287
8433
|
// src/indexer/search-ranking.ts
|
|
8434
|
+
function applyCommunityBoost(candidates, sameCommunityCandidateIds, boost) {
|
|
8435
|
+
if (boost <= 0 || sameCommunityCandidateIds.size === 0 || candidates.length <= 1) {
|
|
8436
|
+
return candidates;
|
|
8437
|
+
}
|
|
8438
|
+
const result = candidates.map((candidate) => sameCommunityCandidateIds.has(candidate.id) ? { ...candidate, score: candidate.score * (1 + boost) } : candidate);
|
|
8439
|
+
for (let index = 1; index < result.length; index += 1) {
|
|
8440
|
+
const candidate = result[index];
|
|
8441
|
+
const previous = result[index - 1];
|
|
8442
|
+
if (candidate && previous && sameCommunityCandidateIds.has(candidate.id) && !sameCommunityCandidateIds.has(previous.id) && candidate.score > previous.score) {
|
|
8443
|
+
result[index - 1] = candidate;
|
|
8444
|
+
result[index] = previous;
|
|
8445
|
+
}
|
|
8446
|
+
}
|
|
8447
|
+
return result;
|
|
8448
|
+
}
|
|
8288
8449
|
var RANK_HYBRID_CACHE_LIMIT = 256;
|
|
8289
8450
|
var rankHybridResultsCache = /* @__PURE__ */ new WeakMap();
|
|
8290
8451
|
function classifyQueryIntentRaw(query) {
|
|
@@ -8463,6 +8624,117 @@ function rankSemanticOnlyResults(query, semanticResults, options) {
|
|
|
8463
8624
|
});
|
|
8464
8625
|
}
|
|
8465
8626
|
|
|
8627
|
+
// src/tools/symbol-inference.ts
|
|
8628
|
+
var IDENTIFIER_RE = /[A-Za-z_$][A-Za-z0-9_$]*/g;
|
|
8629
|
+
var QUOTED_BACKTICK_RE = /`([^`]+)`/g;
|
|
8630
|
+
var QUOTED_SINGLE_RE = /'([^'\\]+)'/g;
|
|
8631
|
+
var QUOTED_DOUBLE_RE = /"([^"]+)"/g;
|
|
8632
|
+
var SYMBOL_LIKE_RE = /^(?:[A-Za-z_$][A-Za-z0-9_$]*)$/;
|
|
8633
|
+
var CAMEL_CASE_RE = /^[a-z_][A-Za-z0-9_$]*[A-Z][A-Za-z0-9_$]*$/;
|
|
8634
|
+
var PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9_$]*$/;
|
|
8635
|
+
var SNAKE_CASE_RE = /^[a-z][a-z0-9_]*_[a-z0-9_]+$/;
|
|
8636
|
+
var DEFINITION_INTENT_RE = /\b(where|defined|definition|define|declaration|symbol|function|method|class|interface|type)\b/i;
|
|
8637
|
+
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
8638
|
+
"a",
|
|
8639
|
+
"an",
|
|
8640
|
+
"and",
|
|
8641
|
+
"are",
|
|
8642
|
+
"at",
|
|
8643
|
+
"for",
|
|
8644
|
+
"find",
|
|
8645
|
+
"how",
|
|
8646
|
+
"i",
|
|
8647
|
+
"in",
|
|
8648
|
+
"is",
|
|
8649
|
+
"it",
|
|
8650
|
+
"of",
|
|
8651
|
+
"on",
|
|
8652
|
+
"that",
|
|
8653
|
+
"the",
|
|
8654
|
+
"definition",
|
|
8655
|
+
"show",
|
|
8656
|
+
"to",
|
|
8657
|
+
"where",
|
|
8658
|
+
"which",
|
|
8659
|
+
"what",
|
|
8660
|
+
"you",
|
|
8661
|
+
"your",
|
|
8662
|
+
"with"
|
|
8663
|
+
]);
|
|
8664
|
+
function stripCallSuffix(token) {
|
|
8665
|
+
return token.replace(/\(\s*\)$/, "");
|
|
8666
|
+
}
|
|
8667
|
+
function isLikelySymbolName(token) {
|
|
8668
|
+
if (!SYMBOL_LIKE_RE.test(token)) {
|
|
8669
|
+
return false;
|
|
8670
|
+
}
|
|
8671
|
+
if (STOP_WORDS.has(token.toLowerCase())) {
|
|
8672
|
+
return false;
|
|
8673
|
+
}
|
|
8674
|
+
return CAMEL_CASE_RE.test(token) || PASCAL_CASE_RE.test(token) || SNAKE_CASE_RE.test(token);
|
|
8675
|
+
}
|
|
8676
|
+
function extractQuotedIdentifiers(query) {
|
|
8677
|
+
const identifiers = /* @__PURE__ */ new Set();
|
|
8678
|
+
for (const match of query.matchAll(QUOTED_BACKTICK_RE)) {
|
|
8679
|
+
const candidate = stripCallSuffix(match[1].trim());
|
|
8680
|
+
if (candidate && isLikelySymbolName(candidate)) {
|
|
8681
|
+
identifiers.add(candidate);
|
|
8682
|
+
}
|
|
8683
|
+
}
|
|
8684
|
+
for (const match of query.matchAll(QUOTED_SINGLE_RE)) {
|
|
8685
|
+
const candidate = stripCallSuffix(match[1].trim());
|
|
8686
|
+
if (candidate && isLikelySymbolName(candidate)) {
|
|
8687
|
+
identifiers.add(candidate);
|
|
8688
|
+
}
|
|
8689
|
+
}
|
|
8690
|
+
for (const match of query.matchAll(QUOTED_DOUBLE_RE)) {
|
|
8691
|
+
const candidate = stripCallSuffix(match[1].trim());
|
|
8692
|
+
if (candidate && isLikelySymbolName(candidate)) {
|
|
8693
|
+
identifiers.add(candidate);
|
|
8694
|
+
}
|
|
8695
|
+
}
|
|
8696
|
+
return [...identifiers];
|
|
8697
|
+
}
|
|
8698
|
+
function extractBareIdentifiers(query) {
|
|
8699
|
+
const unquoted = query.replace(QUOTED_BACKTICK_RE, " ").replace(QUOTED_SINGLE_RE, " ").replace(QUOTED_DOUBLE_RE, " ");
|
|
8700
|
+
const identifiers = /* @__PURE__ */ new Set();
|
|
8701
|
+
for (const match of unquoted.matchAll(IDENTIFIER_RE)) {
|
|
8702
|
+
const candidate = stripCallSuffix(match[0]);
|
|
8703
|
+
if (isLikelySymbolName(candidate)) {
|
|
8704
|
+
identifiers.add(candidate);
|
|
8705
|
+
}
|
|
8706
|
+
}
|
|
8707
|
+
return [...identifiers];
|
|
8708
|
+
}
|
|
8709
|
+
function isSingleMeaningfulToken(query, symbol) {
|
|
8710
|
+
const tokens = query.replace(/[`'"()]/g, " ").split(/[^A-Za-z0-9_$]+/).map((token) => token.trim().toLowerCase()).filter((token) => token.length > 0).filter((token) => !STOP_WORDS.has(token));
|
|
8711
|
+
return tokens.length === 1 && tokens[0] === symbol.toLowerCase();
|
|
8712
|
+
}
|
|
8713
|
+
function inferExactSymbolFromQuery(query) {
|
|
8714
|
+
if (analyzeQueryIntent(query).explicitArtifactIntent) {
|
|
8715
|
+
return void 0;
|
|
8716
|
+
}
|
|
8717
|
+
const quoted = extractQuotedIdentifiers(query);
|
|
8718
|
+
if (quoted.length === 1) {
|
|
8719
|
+
return quoted[0];
|
|
8720
|
+
}
|
|
8721
|
+
if (quoted.length > 1) {
|
|
8722
|
+
return void 0;
|
|
8723
|
+
}
|
|
8724
|
+
const candidates = extractBareIdentifiers(query);
|
|
8725
|
+
if (candidates.length !== 1) {
|
|
8726
|
+
return void 0;
|
|
8727
|
+
}
|
|
8728
|
+
const candidate = candidates[0];
|
|
8729
|
+
if (DEFINITION_INTENT_RE.test(query)) {
|
|
8730
|
+
return candidate;
|
|
8731
|
+
}
|
|
8732
|
+
if (isSingleMeaningfulToken(query, candidate)) {
|
|
8733
|
+
return candidate;
|
|
8734
|
+
}
|
|
8735
|
+
return void 0;
|
|
8736
|
+
}
|
|
8737
|
+
|
|
8466
8738
|
// src/indexer/call-graph-constants.ts
|
|
8467
8739
|
var CALL_GRAPH_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
8468
8740
|
"function_declaration",
|
|
@@ -8965,22 +9237,6 @@ function getUniquePendingChunksFromRequests(requests) {
|
|
|
8965
9237
|
}
|
|
8966
9238
|
return Array.from(uniqueChunks.values());
|
|
8967
9239
|
}
|
|
8968
|
-
function coalesceFailedBatches(batches) {
|
|
8969
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
8970
|
-
for (const batch of batches) {
|
|
8971
|
-
const key = `${batch.attemptCount}:${batch.lastAttempt}:${batch.error}`;
|
|
8972
|
-
const existing = grouped.get(key);
|
|
8973
|
-
if (!existing) {
|
|
8974
|
-
grouped.set(key, {
|
|
8975
|
-
...batch,
|
|
8976
|
-
chunks: [...batch.chunks]
|
|
8977
|
-
});
|
|
8978
|
-
continue;
|
|
8979
|
-
}
|
|
8980
|
-
existing.chunks.push(...batch.chunks);
|
|
8981
|
-
}
|
|
8982
|
-
return Array.from(grouped.values());
|
|
8983
|
-
}
|
|
8984
9240
|
function poolEmbeddingVectors(vectors, weights) {
|
|
8985
9241
|
const firstVector = vectors[0];
|
|
8986
9242
|
if (!firstVector) {
|
|
@@ -9013,9 +9269,268 @@ function hasAllEmbeddingParts(parts, expectedPartCount) {
|
|
|
9013
9269
|
return true;
|
|
9014
9270
|
}
|
|
9015
9271
|
|
|
9272
|
+
// src/indexer/failed-state-persistence.ts
|
|
9273
|
+
import * as fs2 from "fs";
|
|
9274
|
+
import { createHash, randomBytes as randomBytes3 } from "crypto";
|
|
9275
|
+
import * as path18 from "path";
|
|
9276
|
+
import { StringDecoder } from "string_decoder";
|
|
9277
|
+
var CURRENT_FAILED_BATCH_VERSION = 1;
|
|
9278
|
+
var DEFAULT_MALFORMED_LINE_ACTION = "skip";
|
|
9279
|
+
function* readFailedBatchRecords(filePath, options = {}) {
|
|
9280
|
+
if (!fs2.existsSync(filePath)) {
|
|
9281
|
+
return;
|
|
9282
|
+
}
|
|
9283
|
+
const fileFormat = detectFailedBatchFileFormat(filePath);
|
|
9284
|
+
if (fileFormat === "legacy") {
|
|
9285
|
+
yield* readLegacyFailedBatchRecords(filePath, options);
|
|
9286
|
+
return;
|
|
9287
|
+
}
|
|
9288
|
+
yield* readJsonlFailedBatchRecords(filePath, options);
|
|
9289
|
+
}
|
|
9290
|
+
function createFailedBatchWriter(targetPath) {
|
|
9291
|
+
const temporaryPath = createTemporaryPath(targetPath);
|
|
9292
|
+
let finalized = false;
|
|
9293
|
+
fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
|
|
9294
|
+
fs2.closeSync(fs2.openSync(temporaryPath, "w"));
|
|
9295
|
+
const write = (record) => {
|
|
9296
|
+
if (finalized) {
|
|
9297
|
+
throw new Error("Failed batch writer has been finalized");
|
|
9298
|
+
}
|
|
9299
|
+
const lines = record.chunks.map((chunk) => {
|
|
9300
|
+
const lineRecord = {
|
|
9301
|
+
version: CURRENT_FAILED_BATCH_VERSION,
|
|
9302
|
+
chunks: [chunk],
|
|
9303
|
+
error: record.error,
|
|
9304
|
+
attemptCount: record.attemptCount,
|
|
9305
|
+
lastAttempt: record.lastAttempt
|
|
9306
|
+
};
|
|
9307
|
+
return JSON.stringify(lineRecord);
|
|
9308
|
+
});
|
|
9309
|
+
if (lines.length === 0) {
|
|
9310
|
+
return;
|
|
9311
|
+
}
|
|
9312
|
+
fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
|
|
9313
|
+
fs2.appendFileSync(temporaryPath, `${lines.join("\n")}
|
|
9314
|
+
`, "utf-8");
|
|
9315
|
+
};
|
|
9316
|
+
const commit = () => {
|
|
9317
|
+
if (finalized) {
|
|
9318
|
+
return;
|
|
9319
|
+
}
|
|
9320
|
+
fs2.mkdirSync(path18.dirname(targetPath), { recursive: true });
|
|
9321
|
+
fs2.renameSync(temporaryPath, targetPath);
|
|
9322
|
+
finalized = true;
|
|
9323
|
+
};
|
|
9324
|
+
const cleanup = () => {
|
|
9325
|
+
if (finalized) {
|
|
9326
|
+
return;
|
|
9327
|
+
}
|
|
9328
|
+
fs2.rmSync(temporaryPath, { force: true });
|
|
9329
|
+
};
|
|
9330
|
+
return {
|
|
9331
|
+
write,
|
|
9332
|
+
commit,
|
|
9333
|
+
cleanup,
|
|
9334
|
+
temporaryPath
|
|
9335
|
+
};
|
|
9336
|
+
}
|
|
9337
|
+
function* readLegacyFailedBatchRecords(filePath, options) {
|
|
9338
|
+
const rawData = fs2.readFileSync(filePath, "utf-8");
|
|
9339
|
+
const trimmed = stripLeadingBomAndWhitespace(rawData).trim();
|
|
9340
|
+
if (trimmed.length === 0) {
|
|
9341
|
+
return;
|
|
9342
|
+
}
|
|
9343
|
+
let parsed;
|
|
9344
|
+
try {
|
|
9345
|
+
parsed = JSON.parse(trimmed);
|
|
9346
|
+
} catch (error) {
|
|
9347
|
+
handleMalformedLine(filePath, 1, trimmed, error, options);
|
|
9348
|
+
return;
|
|
9349
|
+
}
|
|
9350
|
+
if (!Array.isArray(parsed)) {
|
|
9351
|
+
handleMalformedLine(filePath, 1, trimmed, new Error("Expected legacy failed-batch file to contain a JSON array"), options);
|
|
9352
|
+
return;
|
|
9353
|
+
}
|
|
9354
|
+
for (const entry of parsed) {
|
|
9355
|
+
const normalized = normalizeFailedBatchRecord(entry);
|
|
9356
|
+
if (normalized) {
|
|
9357
|
+
yield normalized;
|
|
9358
|
+
}
|
|
9359
|
+
}
|
|
9360
|
+
}
|
|
9361
|
+
function* readJsonlFailedBatchRecords(filePath, options) {
|
|
9362
|
+
const handle = fs2.openSync(filePath, "r");
|
|
9363
|
+
const decoder = new StringDecoder("utf8");
|
|
9364
|
+
const readBuffer = Buffer.allocUnsafe(64 * 1024);
|
|
9365
|
+
let buffer = "";
|
|
9366
|
+
let lineNumber = 0;
|
|
9367
|
+
try {
|
|
9368
|
+
let bytesRead = 0;
|
|
9369
|
+
do {
|
|
9370
|
+
bytesRead = fs2.readSync(handle, readBuffer, 0, readBuffer.length, null);
|
|
9371
|
+
buffer += decoder.write(readBuffer.subarray(0, bytesRead));
|
|
9372
|
+
let newlineIndex = buffer.indexOf("\n");
|
|
9373
|
+
while (newlineIndex >= 0) {
|
|
9374
|
+
const rawLine = buffer.slice(0, newlineIndex);
|
|
9375
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
9376
|
+
lineNumber += 1;
|
|
9377
|
+
const normalized = parseFailedBatchLine(rawLine, filePath, lineNumber, options);
|
|
9378
|
+
if (normalized) {
|
|
9379
|
+
yield normalized;
|
|
9380
|
+
}
|
|
9381
|
+
newlineIndex = buffer.indexOf("\n");
|
|
9382
|
+
}
|
|
9383
|
+
} while (bytesRead > 0);
|
|
9384
|
+
buffer += decoder.end();
|
|
9385
|
+
const finalLine = buffer.trimEnd();
|
|
9386
|
+
if (finalLine.length > 0) {
|
|
9387
|
+
lineNumber += 1;
|
|
9388
|
+
const normalized = parseFailedBatchLine(finalLine, filePath, lineNumber, options);
|
|
9389
|
+
if (normalized) {
|
|
9390
|
+
yield normalized;
|
|
9391
|
+
}
|
|
9392
|
+
}
|
|
9393
|
+
} finally {
|
|
9394
|
+
fs2.closeSync(handle);
|
|
9395
|
+
}
|
|
9396
|
+
}
|
|
9397
|
+
function parseFailedBatchLine(rawLine, filePath, lineNumber, options) {
|
|
9398
|
+
const line = rawLine.trimEnd();
|
|
9399
|
+
if (line.length === 0) {
|
|
9400
|
+
return null;
|
|
9401
|
+
}
|
|
9402
|
+
try {
|
|
9403
|
+
const parsed = JSON.parse(line);
|
|
9404
|
+
const normalized = normalizeFailedBatchRecord(parsed);
|
|
9405
|
+
if (!normalized) {
|
|
9406
|
+
handleMalformedLine(filePath, lineNumber, line, new Error("Malformed failed-batch record"), options);
|
|
9407
|
+
return null;
|
|
9408
|
+
}
|
|
9409
|
+
return normalized;
|
|
9410
|
+
} catch (error) {
|
|
9411
|
+
handleMalformedLine(filePath, lineNumber, line, error, options);
|
|
9412
|
+
return null;
|
|
9413
|
+
}
|
|
9414
|
+
}
|
|
9415
|
+
function normalizeFailedBatchRecord(rawRecord) {
|
|
9416
|
+
if (!rawRecord || typeof rawRecord !== "object" || Array.isArray(rawRecord)) {
|
|
9417
|
+
return null;
|
|
9418
|
+
}
|
|
9419
|
+
const typed = rawRecord;
|
|
9420
|
+
const chunks = Array.isArray(typed.chunks) ? typed.chunks : null;
|
|
9421
|
+
if (!chunks || chunks.length === 0) {
|
|
9422
|
+
return null;
|
|
9423
|
+
}
|
|
9424
|
+
return {
|
|
9425
|
+
version: typeof typed.version === "number" && Number.isFinite(typed.version) ? typed.version : CURRENT_FAILED_BATCH_VERSION,
|
|
9426
|
+
chunks,
|
|
9427
|
+
error: typeof typed.error === "string" ? typed.error : "Unknown embedding error",
|
|
9428
|
+
attemptCount: typeof typed.attemptCount === "number" && Number.isFinite(typed.attemptCount) ? typed.attemptCount : 1,
|
|
9429
|
+
lastAttempt: typeof typed.lastAttempt === "string" ? typed.lastAttempt : (/* @__PURE__ */ new Date()).toISOString()
|
|
9430
|
+
};
|
|
9431
|
+
}
|
|
9432
|
+
function detectFailedBatchFileFormat(filePath) {
|
|
9433
|
+
const handle = fs2.openSync(filePath, "r");
|
|
9434
|
+
try {
|
|
9435
|
+
const buffer = Buffer.alloc(4096);
|
|
9436
|
+
const bytesRead = fs2.readSync(handle, buffer, 0, buffer.length, 0);
|
|
9437
|
+
if (bytesRead <= 0) {
|
|
9438
|
+
return "jsonl";
|
|
9439
|
+
}
|
|
9440
|
+
const prefix = stripLeadingBomAndWhitespace(buffer.subarray(0, bytesRead).toString("utf-8"));
|
|
9441
|
+
return prefix.startsWith("[") ? "legacy" : "jsonl";
|
|
9442
|
+
} finally {
|
|
9443
|
+
fs2.closeSync(handle);
|
|
9444
|
+
}
|
|
9445
|
+
}
|
|
9446
|
+
function stripLeadingBomAndWhitespace(value) {
|
|
9447
|
+
let result = value.trimStart();
|
|
9448
|
+
if (result.charCodeAt(0) === 65279) {
|
|
9449
|
+
result = result.slice(1);
|
|
9450
|
+
}
|
|
9451
|
+
return result;
|
|
9452
|
+
}
|
|
9453
|
+
function createTemporaryPath(targetPath) {
|
|
9454
|
+
const randomId = createHash("sha1").update(`${Date.now()}:${randomBytes3(8).toString("hex")}`).digest("hex");
|
|
9455
|
+
const targetDir = path18.dirname(targetPath);
|
|
9456
|
+
const baseName = path18.basename(targetPath);
|
|
9457
|
+
return path18.join(targetDir, `.${baseName}.${randomId}.tmp`);
|
|
9458
|
+
}
|
|
9459
|
+
function handleMalformedLine(filePath, lineNumber, line, error, options) {
|
|
9460
|
+
const action = options.malformedLineAction ?? DEFAULT_MALFORMED_LINE_ACTION;
|
|
9461
|
+
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
9462
|
+
if (options.onMalformedLine) {
|
|
9463
|
+
options.onMalformedLine(normalizedError, line, lineNumber, filePath);
|
|
9464
|
+
}
|
|
9465
|
+
if (action === "fail") {
|
|
9466
|
+
throw normalizedError;
|
|
9467
|
+
}
|
|
9468
|
+
}
|
|
9469
|
+
|
|
9470
|
+
// src/indexer/file-batches.ts
|
|
9471
|
+
var INDEX_FILE_BATCH_LIMITS = Object.freeze({
|
|
9472
|
+
maxFiles: 64,
|
|
9473
|
+
maxBytes: 8 * 1024 * 1024
|
|
9474
|
+
});
|
|
9475
|
+
function* iterateOrderedFileBatches(items, getBytes, limits = INDEX_FILE_BATCH_LIMITS) {
|
|
9476
|
+
const maxFiles = Math.max(1, Math.floor(limits.maxFiles));
|
|
9477
|
+
const maxBytes = Math.max(1, Math.floor(limits.maxBytes));
|
|
9478
|
+
let batch = [];
|
|
9479
|
+
let batchBytes = 0;
|
|
9480
|
+
for (const item of items) {
|
|
9481
|
+
const itemBytes = Math.max(0, Math.floor(getBytes(item)));
|
|
9482
|
+
if (batch.length > 0 && (batch.length >= maxFiles || batchBytes + itemBytes > maxBytes)) {
|
|
9483
|
+
yield batch;
|
|
9484
|
+
batch = [];
|
|
9485
|
+
batchBytes = 0;
|
|
9486
|
+
}
|
|
9487
|
+
batch.push(item);
|
|
9488
|
+
batchBytes += itemBytes;
|
|
9489
|
+
}
|
|
9490
|
+
if (batch.length > 0) {
|
|
9491
|
+
yield batch;
|
|
9492
|
+
}
|
|
9493
|
+
}
|
|
9494
|
+
|
|
9016
9495
|
// src/indexer/index.ts
|
|
9017
9496
|
var CALL_GRAPH_LANGUAGES = /* @__PURE__ */ new Set(["typescript", "tsx", "javascript", "jsx", "python", "go", "rust", "swift", "php", "apex", "zig", "gdscript", "matlab", "bash", "c", "cpp", "metal"]);
|
|
9018
9497
|
var CASE_INSENSITIVE_LANGUAGES = /* @__PURE__ */ new Set(["apex", "php"]);
|
|
9498
|
+
function candidateOverlapsSymbol(candidate, symbol) {
|
|
9499
|
+
return candidate.metadata.filePath === symbol.filePath && candidate.metadata.startLine <= symbol.endLine && candidate.metadata.endLine >= symbol.startLine;
|
|
9500
|
+
}
|
|
9501
|
+
function resolveSameCommunityCandidateIds(query, candidates, database, branchCatalogKeys) {
|
|
9502
|
+
const anchorName = inferExactSymbolFromQuery(query);
|
|
9503
|
+
if (!anchorName || candidates.length === 0) {
|
|
9504
|
+
return /* @__PURE__ */ new Set();
|
|
9505
|
+
}
|
|
9506
|
+
const catalogs = branchCatalogKeys.map((branchKey) => ({
|
|
9507
|
+
branchKey,
|
|
9508
|
+
symbols: database.getSymbolsForBranch(branchKey)
|
|
9509
|
+
}));
|
|
9510
|
+
const exactAnchors = catalogs.flatMap(({ branchKey, symbols }) => symbols.filter((symbol) => symbol.name === anchorName).map((symbol) => ({ branchKey, symbol })));
|
|
9511
|
+
const anchors = exactAnchors.length > 0 ? exactAnchors : catalogs.flatMap(({ branchKey, symbols }) => symbols.filter((symbol) => symbol.name.toLowerCase() === anchorName.toLowerCase()).map((symbol) => ({ branchKey, symbol })));
|
|
9512
|
+
const uniqueAnchors = new Map(anchors.map((anchor2) => [anchor2.symbol.id, anchor2]));
|
|
9513
|
+
if (uniqueAnchors.size !== 1) {
|
|
9514
|
+
return /* @__PURE__ */ new Set();
|
|
9515
|
+
}
|
|
9516
|
+
const anchor = uniqueAnchors.values().next().value;
|
|
9517
|
+
const branchSymbols = catalogs.find((catalog) => catalog.branchKey === anchor.branchKey)?.symbols ?? [];
|
|
9518
|
+
const candidateSymbols = branchSymbols.filter(
|
|
9519
|
+
(symbol) => candidates.some((candidate) => candidateOverlapsSymbol(candidate, symbol))
|
|
9520
|
+
);
|
|
9521
|
+
const assignments = database.detectCommunities(
|
|
9522
|
+
anchor.branchKey,
|
|
9523
|
+
[anchor.symbol.id, ...candidateSymbols.map((symbol) => symbol.id)]
|
|
9524
|
+
);
|
|
9525
|
+
const anchorCommunity = assignments.find((assignment) => assignment.symbolId === anchor.symbol.id)?.communityId;
|
|
9526
|
+
if (anchorCommunity === void 0) {
|
|
9527
|
+
return /* @__PURE__ */ new Set();
|
|
9528
|
+
}
|
|
9529
|
+
const sameCommunitySymbolIds = new Set(assignments.filter((assignment) => assignment.communityId === anchorCommunity).map((assignment) => assignment.symbolId));
|
|
9530
|
+
return new Set(candidates.filter((candidate) => candidateSymbols.some(
|
|
9531
|
+
(symbol) => sameCommunitySymbolIds.has(symbol.id) && candidateOverlapsSymbol(candidate, symbol)
|
|
9532
|
+
)).map((candidate) => candidate.id));
|
|
9533
|
+
}
|
|
9019
9534
|
var CALL_GRAPH_RESOLUTION_VERSION = "4";
|
|
9020
9535
|
var PHP_FUNCTION_SYMBOL_CHUNK_TYPES = /* @__PURE__ */ new Set([
|
|
9021
9536
|
"function_declaration",
|
|
@@ -9116,6 +9631,16 @@ function isSqliteCorruptionError(error) {
|
|
|
9116
9631
|
}
|
|
9117
9632
|
var STARTUP_WARNING_METADATA_KEY = "index.startupWarning";
|
|
9118
9633
|
var READER_ARTIFACT_RETRY_INTERVAL_MS = 1e3;
|
|
9634
|
+
function getFailedBatchGroupKey(record) {
|
|
9635
|
+
return `${record.attemptCount}:${record.lastAttempt}:${record.error}`;
|
|
9636
|
+
}
|
|
9637
|
+
function getPendingChunkId(rawChunk) {
|
|
9638
|
+
if (!rawChunk || typeof rawChunk !== "object") {
|
|
9639
|
+
return null;
|
|
9640
|
+
}
|
|
9641
|
+
const id = rawChunk.id;
|
|
9642
|
+
return typeof id === "string" ? id : null;
|
|
9643
|
+
}
|
|
9119
9644
|
function metadataFromBlame(blame) {
|
|
9120
9645
|
if (!blame) {
|
|
9121
9646
|
return {};
|
|
@@ -9163,9 +9688,9 @@ var SWIFT_PARSER_VERSION = "1";
|
|
|
9163
9688
|
var METAL_PARSER_VERSION = "1";
|
|
9164
9689
|
var SYMBOL_EXTRACTOR_VERSION = "1";
|
|
9165
9690
|
function isPathWithinRoot2(filePath, rootPath) {
|
|
9166
|
-
const normalizedFilePath =
|
|
9167
|
-
const normalizedRoot =
|
|
9168
|
-
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${
|
|
9691
|
+
const normalizedFilePath = path19.resolve(filePath);
|
|
9692
|
+
const normalizedRoot = path19.resolve(rootPath);
|
|
9693
|
+
return normalizedFilePath === normalizedRoot || normalizedFilePath.startsWith(`${normalizedRoot}${path19.sep}`);
|
|
9169
9694
|
}
|
|
9170
9695
|
function promoteIdentifierMatches(query, combined, semanticCandidates, keywordCandidates, database, branchChunkIds, prioritizeSourcePaths = classifyQueryIntentRaw(query) === "source") {
|
|
9171
9696
|
if (combined.length === 0) {
|
|
@@ -9496,10 +10021,10 @@ function matchesHardSearchFilters(candidate, options, projectRoot3) {
|
|
|
9496
10021
|
}
|
|
9497
10022
|
if (options?.directory) {
|
|
9498
10023
|
const candidatePath = canonicalizePathForComparison(
|
|
9499
|
-
|
|
10024
|
+
path19.resolve(projectRoot3, candidate.metadata.filePath.replace(/\\/g, path19.sep))
|
|
9500
10025
|
);
|
|
9501
10026
|
const directoryPath = canonicalizePathForComparison(
|
|
9502
|
-
|
|
10027
|
+
path19.resolve(projectRoot3, options.directory.trim().replace(/\\/g, path19.sep))
|
|
9503
10028
|
);
|
|
9504
10029
|
if (!isPathWithinRoot2(candidatePath, directoryPath)) return false;
|
|
9505
10030
|
}
|
|
@@ -9575,6 +10100,7 @@ var Indexer = class _Indexer {
|
|
|
9575
10100
|
readerArtifactFingerprint = null;
|
|
9576
10101
|
writerArtifactFingerprint = null;
|
|
9577
10102
|
readerArtifactRetryAfter = /* @__PURE__ */ new Map();
|
|
10103
|
+
fileBatchLimits;
|
|
9578
10104
|
constructor(projectRoot3, config, host, runtimeOptions = {}) {
|
|
9579
10105
|
this.projectRoot = projectRoot3;
|
|
9580
10106
|
this.projectIdentityHash = hashContent(this.getCanonicalPath(projectRoot3)).slice(0, 16);
|
|
@@ -9586,6 +10112,7 @@ var Indexer = class _Indexer {
|
|
|
9586
10112
|
}
|
|
9587
10113
|
this.expectedCommitOverride = runtimeOptions.expectedCommit?.toLowerCase();
|
|
9588
10114
|
this.indexPathOverride = runtimeOptions.indexPath;
|
|
10115
|
+
this.fileBatchLimits = runtimeOptions.fileBatchLimits;
|
|
9589
10116
|
this.config = config;
|
|
9590
10117
|
this.host = host;
|
|
9591
10118
|
if (isGitRepo(this.materializedProjectRoot)) {
|
|
@@ -9603,26 +10130,26 @@ var Indexer = class _Indexer {
|
|
|
9603
10130
|
return this.indexPathOverride ?? resolveProjectIndexPath(this.projectRoot, this.config.scope, this.host);
|
|
9604
10131
|
}
|
|
9605
10132
|
toCanonicalFilePath(filePath) {
|
|
9606
|
-
if (!
|
|
10133
|
+
if (!path19.isAbsolute(filePath)) {
|
|
9607
10134
|
return this.resolveStoredFilePath(filePath, this.projectRoot);
|
|
9608
10135
|
}
|
|
9609
|
-
if (
|
|
10136
|
+
if (path19.resolve(this.materializedProjectRoot) === path19.resolve(this.projectRoot) || !isPathWithinRoot2(filePath, this.materializedProjectRoot)) {
|
|
9610
10137
|
return filePath;
|
|
9611
10138
|
}
|
|
9612
|
-
return
|
|
10139
|
+
return path19.resolve(this.projectRoot, path19.relative(this.materializedProjectRoot, filePath));
|
|
9613
10140
|
}
|
|
9614
10141
|
toStoredFilePath(filePath) {
|
|
9615
10142
|
const canonicalFilePath = this.toCanonicalFilePath(filePath);
|
|
9616
10143
|
if (this.config.scope !== "project" || !isPathWithinRoot2(canonicalFilePath, this.projectRoot)) {
|
|
9617
10144
|
return canonicalFilePath;
|
|
9618
10145
|
}
|
|
9619
|
-
return
|
|
10146
|
+
return path19.relative(this.projectRoot, canonicalFilePath).split(path19.sep).join("/");
|
|
9620
10147
|
}
|
|
9621
10148
|
resolveStoredFilePath(filePath, rootPath = this.projectRoot) {
|
|
9622
|
-
if (
|
|
10149
|
+
if (path19.isAbsolute(filePath)) {
|
|
9623
10150
|
return filePath;
|
|
9624
10151
|
}
|
|
9625
|
-
const resolvedPath =
|
|
10152
|
+
const resolvedPath = path19.resolve(rootPath, ...filePath.split("/"));
|
|
9626
10153
|
if (!isPathWithinRoot2(resolvedPath, rootPath)) {
|
|
9627
10154
|
throw new Error(`Stored project path escapes project root: ${JSON.stringify(filePath)}`);
|
|
9628
10155
|
}
|
|
@@ -9646,7 +10173,7 @@ var Indexer = class _Indexer {
|
|
|
9646
10173
|
}
|
|
9647
10174
|
toMaterializedFilePath(filePath) {
|
|
9648
10175
|
const storedFilePath = this.toStoredFilePath(filePath);
|
|
9649
|
-
if (
|
|
10176
|
+
if (path19.isAbsolute(storedFilePath)) {
|
|
9650
10177
|
return storedFilePath;
|
|
9651
10178
|
}
|
|
9652
10179
|
return this.resolveStoredFilePath(storedFilePath, this.materializedProjectRoot);
|
|
@@ -9663,10 +10190,10 @@ var Indexer = class _Indexer {
|
|
|
9663
10190
|
}
|
|
9664
10191
|
getRuntimeArtifactPath(fileName) {
|
|
9665
10192
|
const namespace = this.getRuntimeArtifactNamespace();
|
|
9666
|
-
if (!namespace) return
|
|
9667
|
-
const extension =
|
|
10193
|
+
if (!namespace) return path19.join(this.indexPath, fileName);
|
|
10194
|
+
const extension = path19.extname(fileName);
|
|
9668
10195
|
const baseName = fileName.slice(0, fileName.length - extension.length);
|
|
9669
|
-
return
|
|
10196
|
+
return path19.join(this.indexPath, `${baseName}.${namespace}${extension}`);
|
|
9670
10197
|
}
|
|
9671
10198
|
refreshRuntimeArtifactPaths() {
|
|
9672
10199
|
this.fileHashCachePath = this.getRuntimeArtifactPath("file-hashes.json");
|
|
@@ -9679,14 +10206,14 @@ var Indexer = class _Indexer {
|
|
|
9679
10206
|
getMaterializedKnowledgeBases() {
|
|
9680
10207
|
const canonicalProjectRoot = this.getCanonicalPath(this.projectRoot);
|
|
9681
10208
|
return this.config.knowledgeBases.map((knowledgeBase) => {
|
|
9682
|
-
const configuredPath =
|
|
10209
|
+
const configuredPath = path19.isAbsolute(knowledgeBase) ? knowledgeBase : path19.resolve(this.projectRoot, knowledgeBase);
|
|
9683
10210
|
const canonicalPath = this.getCanonicalPath(configuredPath);
|
|
9684
10211
|
if (!isPathWithinRoot2(canonicalPath, canonicalProjectRoot)) {
|
|
9685
10212
|
return canonicalPath;
|
|
9686
10213
|
}
|
|
9687
|
-
return
|
|
10214
|
+
return path19.resolve(
|
|
9688
10215
|
this.materializedProjectRoot,
|
|
9689
|
-
|
|
10216
|
+
path19.relative(canonicalProjectRoot, canonicalPath)
|
|
9690
10217
|
);
|
|
9691
10218
|
});
|
|
9692
10219
|
}
|
|
@@ -9694,7 +10221,7 @@ var Indexer = class _Indexer {
|
|
|
9694
10221
|
try {
|
|
9695
10222
|
return canonicalizePathForComparison(targetPath);
|
|
9696
10223
|
} catch {
|
|
9697
|
-
return
|
|
10224
|
+
return path19.resolve(targetPath);
|
|
9698
10225
|
}
|
|
9699
10226
|
}
|
|
9700
10227
|
isProjectOwnedIndexPath() {
|
|
@@ -9770,7 +10297,7 @@ var Indexer = class _Indexer {
|
|
|
9770
10297
|
} catch (error) {
|
|
9771
10298
|
releaseError = error;
|
|
9772
10299
|
this.writerArtifactFingerprint = null;
|
|
9773
|
-
if (!
|
|
10300
|
+
if (!existsSync11(lease.lockPath) && this.activeIndexLease?.owner.token === lease.owner.token) {
|
|
9774
10301
|
this.activeIndexLease = null;
|
|
9775
10302
|
}
|
|
9776
10303
|
}
|
|
@@ -9788,11 +10315,11 @@ var Indexer = class _Indexer {
|
|
|
9788
10315
|
return this.activeIndexLease;
|
|
9789
10316
|
}
|
|
9790
10317
|
loadFileHashCache() {
|
|
9791
|
-
if (!
|
|
10318
|
+
if (!existsSync11(this.fileHashCachePath)) {
|
|
9792
10319
|
return;
|
|
9793
10320
|
}
|
|
9794
10321
|
try {
|
|
9795
|
-
const data =
|
|
10322
|
+
const data = readFileSync8(this.fileHashCachePath, "utf-8");
|
|
9796
10323
|
const parsed = JSON.parse(data);
|
|
9797
10324
|
this.fileHashCache = new Map(Object.entries(parsed));
|
|
9798
10325
|
} catch (error) {
|
|
@@ -9814,24 +10341,24 @@ var Indexer = class _Indexer {
|
|
|
9814
10341
|
atomicWriteSync(targetPath, data) {
|
|
9815
10342
|
const lease = this.requireActiveLease();
|
|
9816
10343
|
const tempPath = createLeaseTemporaryPath(targetPath, lease.owner, "tmp");
|
|
9817
|
-
|
|
10344
|
+
mkdirSync4(path19.dirname(targetPath), { recursive: true });
|
|
9818
10345
|
try {
|
|
9819
10346
|
writeFileSync3(tempPath, data);
|
|
9820
|
-
|
|
10347
|
+
renameSync3(tempPath, targetPath);
|
|
9821
10348
|
} finally {
|
|
9822
10349
|
removeLeaseTemporaryPath(tempPath);
|
|
9823
10350
|
}
|
|
9824
10351
|
}
|
|
9825
10352
|
saveInvertedIndex(invertedIndex) {
|
|
9826
10353
|
this.atomicWriteSync(
|
|
9827
|
-
|
|
10354
|
+
path19.join(this.indexPath, "inverted-index.json"),
|
|
9828
10355
|
invertedIndex.serialize()
|
|
9829
10356
|
);
|
|
9830
10357
|
}
|
|
9831
10358
|
getScopedRoots() {
|
|
9832
10359
|
const roots = /* @__PURE__ */ new Set([this.getCanonicalPath(this.projectRoot)]);
|
|
9833
10360
|
for (const kbRoot of this.config.knowledgeBases) {
|
|
9834
|
-
roots.add(this.getCanonicalPath(
|
|
10361
|
+
roots.add(this.getCanonicalPath(path19.resolve(this.projectRoot, kbRoot)));
|
|
9835
10362
|
}
|
|
9836
10363
|
return Array.from(roots);
|
|
9837
10364
|
}
|
|
@@ -9847,6 +10374,9 @@ var Indexer = class _Indexer {
|
|
|
9847
10374
|
}
|
|
9848
10375
|
return `${this.projectIdentityHash}:${branchName}`;
|
|
9849
10376
|
}
|
|
10377
|
+
resolveBranchCatalogKey(branchName) {
|
|
10378
|
+
return branchName === void 0 ? this.getBranchCatalogKey() : this.getBranchCatalogKeyFor(branchName);
|
|
10379
|
+
}
|
|
9850
10380
|
getBranchCommitMetadataKey(catalogIdentity = this.getBranchCatalogIdentity()) {
|
|
9851
10381
|
const branchKey = this.getBranchCatalogKeyFor(catalogIdentity);
|
|
9852
10382
|
return `index.branchCommit.${hashContent(branchKey).slice(0, 24)}`;
|
|
@@ -9941,13 +10471,13 @@ var Indexer = class _Indexer {
|
|
|
9941
10471
|
if (Array.from(this.fileHashCache.keys()).some((filePath) => this.isFileInCurrentScope(filePath, roots))) {
|
|
9942
10472
|
return true;
|
|
9943
10473
|
}
|
|
9944
|
-
|
|
9945
|
-
(batch
|
|
10474
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10475
|
+
if (batch.chunks.some((chunk) => {
|
|
9946
10476
|
const filePath = getPendingChunkFilePath(chunk);
|
|
9947
10477
|
return filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
9948
|
-
})
|
|
9949
|
-
|
|
9950
|
-
|
|
10478
|
+
})) {
|
|
10479
|
+
return true;
|
|
10480
|
+
}
|
|
9951
10481
|
}
|
|
9952
10482
|
if (!this.database) {
|
|
9953
10483
|
return false;
|
|
@@ -10088,40 +10618,25 @@ var Indexer = class _Indexer {
|
|
|
10088
10618
|
}
|
|
10089
10619
|
this.saveFileHashCache();
|
|
10090
10620
|
}
|
|
10091
|
-
partitionFailedBatches(roots, maxChunkTokens) {
|
|
10092
|
-
const scoped = [];
|
|
10093
|
-
const retained = [];
|
|
10094
|
-
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10095
|
-
const scopedChunks = batch.chunks.filter((chunk) => {
|
|
10096
|
-
const filePath = getPendingChunkFilePath(chunk);
|
|
10097
|
-
return filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
10098
|
-
});
|
|
10099
|
-
const retainedChunks = batch.chunks.filter((chunk) => {
|
|
10100
|
-
const filePath = getPendingChunkFilePath(chunk);
|
|
10101
|
-
return filePath === null || !this.isFileInCurrentScope(filePath, roots);
|
|
10102
|
-
});
|
|
10103
|
-
if (scopedChunks.length > 0) {
|
|
10104
|
-
const normalizedBatch = normalizeFailedBatch({ ...batch, chunks: scopedChunks }, maxChunkTokens);
|
|
10105
|
-
if (normalizedBatch) {
|
|
10106
|
-
scoped.push(normalizedBatch);
|
|
10107
|
-
}
|
|
10108
|
-
}
|
|
10109
|
-
if (retainedChunks.length > 0) {
|
|
10110
|
-
retained.push({ ...batch, chunks: retainedChunks });
|
|
10111
|
-
}
|
|
10112
|
-
}
|
|
10113
|
-
return { scoped, retained };
|
|
10114
|
-
}
|
|
10115
10621
|
clearScopedFailedBatches(roots) {
|
|
10116
|
-
|
|
10117
|
-
|
|
10622
|
+
this.rewriteFailedBatchState((chunk) => {
|
|
10623
|
+
const filePath = getPendingChunkFilePath(chunk);
|
|
10624
|
+
return filePath === null || !this.isFileInCurrentScope(filePath, roots);
|
|
10625
|
+
});
|
|
10118
10626
|
}
|
|
10119
10627
|
hasForeignScopedFileHashData(roots) {
|
|
10120
10628
|
return Array.from(this.fileHashCache.keys()).some((filePath) => !this.isFileInCurrentScope(filePath, roots));
|
|
10121
10629
|
}
|
|
10122
10630
|
hasForeignScopedFailedBatches(roots) {
|
|
10123
|
-
const
|
|
10124
|
-
|
|
10631
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10632
|
+
if (batch.chunks.some((chunk) => {
|
|
10633
|
+
const filePath = getPendingChunkFilePath(chunk);
|
|
10634
|
+
return filePath === null || !this.isFileInCurrentScope(filePath, roots);
|
|
10635
|
+
})) {
|
|
10636
|
+
return true;
|
|
10637
|
+
}
|
|
10638
|
+
}
|
|
10639
|
+
return false;
|
|
10125
10640
|
}
|
|
10126
10641
|
hasForeignScopedBranchData() {
|
|
10127
10642
|
if (!this.database || this.config.scope !== "global") {
|
|
@@ -10146,10 +10661,6 @@ var Indexer = class _Indexer {
|
|
|
10146
10661
|
}
|
|
10147
10662
|
);
|
|
10148
10663
|
}
|
|
10149
|
-
saveScopedFailedBatches(batches, roots) {
|
|
10150
|
-
const { retained } = this.partitionFailedBatches(roots);
|
|
10151
|
-
this.saveFailedBatches([...retained, ...batches]);
|
|
10152
|
-
}
|
|
10153
10664
|
clearSharedIndexProjectData(store, invertedIndex, database, roots) {
|
|
10154
10665
|
const allMetadata = store.getAllMetadata();
|
|
10155
10666
|
const scopedEntries = allMetadata.filter(({ metadata }) => this.isFileInCurrentScope(metadata.filePath, roots));
|
|
@@ -10239,77 +10750,144 @@ var Indexer = class _Indexer {
|
|
|
10239
10750
|
});
|
|
10240
10751
|
}
|
|
10241
10752
|
if (this.config.scope === "global") {
|
|
10242
|
-
if (
|
|
10753
|
+
if (existsSync11(this.fileHashCachePath)) {
|
|
10243
10754
|
unlinkSync2(this.fileHashCachePath);
|
|
10244
10755
|
}
|
|
10245
10756
|
await this.healthCheckUnlocked();
|
|
10246
10757
|
}
|
|
10247
10758
|
this.logger.info("Recovery complete, next index will re-process all files");
|
|
10248
10759
|
}
|
|
10249
|
-
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10760
|
+
*loadSerializedFailedBatches() {
|
|
10761
|
+
let warned = false;
|
|
10762
|
+
const warn = (error) => {
|
|
10763
|
+
if (warned) return;
|
|
10764
|
+
warned = true;
|
|
10254
10765
|
this.logger.warn("Failed to load failed batch state, skipping persisted retries", {
|
|
10255
10766
|
failedBatchesPath: this.failedBatchesPath,
|
|
10256
|
-
error:
|
|
10767
|
+
error: getErrorMessage4(error)
|
|
10257
10768
|
});
|
|
10258
|
-
|
|
10769
|
+
};
|
|
10770
|
+
try {
|
|
10771
|
+
for (const record of readFailedBatchRecords(this.failedBatchesPath, {
|
|
10772
|
+
malformedLineAction: "skip",
|
|
10773
|
+
onMalformedLine: (error) => warn(error)
|
|
10774
|
+
})) {
|
|
10775
|
+
yield {
|
|
10776
|
+
chunks: record.chunks,
|
|
10777
|
+
error: record.error,
|
|
10778
|
+
attemptCount: record.attemptCount,
|
|
10779
|
+
lastAttempt: record.lastAttempt
|
|
10780
|
+
};
|
|
10781
|
+
}
|
|
10782
|
+
} catch (error) {
|
|
10783
|
+
warn(error);
|
|
10259
10784
|
}
|
|
10260
10785
|
}
|
|
10261
|
-
|
|
10262
|
-
|
|
10263
|
-
|
|
10786
|
+
createFailedBatchWriteState() {
|
|
10787
|
+
return {
|
|
10788
|
+
writer: createFailedBatchWriter(this.failedBatchesPath),
|
|
10789
|
+
recordsWritten: 0
|
|
10790
|
+
};
|
|
10791
|
+
}
|
|
10792
|
+
writeFailedBatchRecord(state, record) {
|
|
10793
|
+
state.writer.write(record);
|
|
10794
|
+
state.recordsWritten += record.chunks.length;
|
|
10795
|
+
}
|
|
10796
|
+
finalizeFailedBatchWriteState(state) {
|
|
10797
|
+
if (state.recordsWritten > 0) {
|
|
10798
|
+
state.writer.commit();
|
|
10799
|
+
return;
|
|
10264
10800
|
}
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
|
|
10270
|
-
|
|
10801
|
+
state.writer.cleanup();
|
|
10802
|
+
this.clearFailedBatchState();
|
|
10803
|
+
}
|
|
10804
|
+
clearFailedBatchState() {
|
|
10805
|
+
if (existsSync11(this.failedBatchesPath)) {
|
|
10806
|
+
try {
|
|
10807
|
+
unlinkSync2(this.failedBatchesPath);
|
|
10808
|
+
} catch {
|
|
10271
10809
|
}
|
|
10272
|
-
|
|
10273
|
-
chunks,
|
|
10274
|
-
error: typeof batch.error === "string" ? batch.error : "Unknown embedding error",
|
|
10275
|
-
attemptCount: typeof batch.attemptCount === "number" ? batch.attemptCount : 1,
|
|
10276
|
-
lastAttempt: typeof batch.lastAttempt === "string" ? batch.lastAttempt : (/* @__PURE__ */ new Date()).toISOString()
|
|
10277
|
-
};
|
|
10278
|
-
}).filter((batch) => batch !== null);
|
|
10810
|
+
}
|
|
10279
10811
|
}
|
|
10280
|
-
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
10284
|
-
|
|
10285
|
-
|
|
10812
|
+
rewriteFailedBatchState(shouldRetain) {
|
|
10813
|
+
const state = this.createFailedBatchWriteState();
|
|
10814
|
+
try {
|
|
10815
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10816
|
+
const retainedChunks = batch.chunks.filter(shouldRetain);
|
|
10817
|
+
if (retainedChunks.length > 0) {
|
|
10818
|
+
this.writeFailedBatchRecord(state, { ...batch, chunks: retainedChunks });
|
|
10286
10819
|
}
|
|
10287
10820
|
}
|
|
10288
|
-
|
|
10821
|
+
this.finalizeFailedBatchWriteState(state);
|
|
10822
|
+
} catch (error) {
|
|
10823
|
+
state.writer.cleanup();
|
|
10824
|
+
throw error;
|
|
10825
|
+
}
|
|
10826
|
+
}
|
|
10827
|
+
prepareFailedBatchProcessing(roots, shouldProcess) {
|
|
10828
|
+
const state = this.createFailedBatchWriteState();
|
|
10829
|
+
const latestById = /* @__PURE__ */ new Map();
|
|
10830
|
+
try {
|
|
10831
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10832
|
+
for (const rawChunk of batch.chunks) {
|
|
10833
|
+
const filePath = getPendingChunkFilePath(rawChunk);
|
|
10834
|
+
const inScope = roots === null || filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
10835
|
+
if (!inScope) {
|
|
10836
|
+
this.writeFailedBatchRecord(state, { ...batch, chunks: [rawChunk] });
|
|
10837
|
+
continue;
|
|
10838
|
+
}
|
|
10839
|
+
if (!shouldProcess(filePath)) {
|
|
10840
|
+
continue;
|
|
10841
|
+
}
|
|
10842
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
10843
|
+
if (!chunkId) {
|
|
10844
|
+
continue;
|
|
10845
|
+
}
|
|
10846
|
+
const existing = latestById.get(chunkId);
|
|
10847
|
+
if (!existing || batch.attemptCount >= existing.attemptCount) {
|
|
10848
|
+
latestById.set(chunkId, {
|
|
10849
|
+
attemptCount: batch.attemptCount,
|
|
10850
|
+
error: batch.error,
|
|
10851
|
+
lastAttempt: batch.lastAttempt
|
|
10852
|
+
});
|
|
10853
|
+
}
|
|
10854
|
+
}
|
|
10855
|
+
}
|
|
10856
|
+
return { state, latestById };
|
|
10857
|
+
} catch (error) {
|
|
10858
|
+
state.writer.cleanup();
|
|
10859
|
+
throw error;
|
|
10289
10860
|
}
|
|
10290
|
-
this.atomicWriteSync(this.failedBatchesPath, JSON.stringify(batches, null, 2));
|
|
10291
10861
|
}
|
|
10292
|
-
|
|
10293
|
-
const
|
|
10294
|
-
for (const batch of this.
|
|
10295
|
-
for (const
|
|
10296
|
-
const
|
|
10297
|
-
if (!
|
|
10862
|
+
*iterateLatestFailedChunks(latestById, roots, shouldProcess, maxChunkTokens) {
|
|
10863
|
+
const yielded = /* @__PURE__ */ new Set();
|
|
10864
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
10865
|
+
for (const rawChunk of batch.chunks) {
|
|
10866
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
10867
|
+
if (!chunkId || yielded.has(chunkId)) {
|
|
10298
10868
|
continue;
|
|
10299
10869
|
}
|
|
10300
|
-
|
|
10870
|
+
const latest = latestById.get(chunkId);
|
|
10871
|
+
if (!latest || latest.attemptCount !== batch.attemptCount || latest.error !== batch.error || latest.lastAttempt !== batch.lastAttempt) {
|
|
10301
10872
|
continue;
|
|
10302
10873
|
}
|
|
10303
|
-
const
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
|
|
10307
|
-
|
|
10308
|
-
|
|
10874
|
+
const filePath = getPendingChunkFilePath(rawChunk);
|
|
10875
|
+
const inScope = roots === null || filePath !== null && this.isFileInCurrentScope(filePath, roots);
|
|
10876
|
+
if (!inScope || !shouldProcess(filePath)) {
|
|
10877
|
+
continue;
|
|
10878
|
+
}
|
|
10879
|
+
const normalized = normalizeFailedBatch({ ...batch, chunks: [rawChunk] }, maxChunkTokens);
|
|
10880
|
+
const chunk = normalized?.chunks[0];
|
|
10881
|
+
if (!chunk) {
|
|
10882
|
+
continue;
|
|
10309
10883
|
}
|
|
10884
|
+
yielded.add(chunkId);
|
|
10885
|
+
yield {
|
|
10886
|
+
chunk,
|
|
10887
|
+
attemptCount: batch.attemptCount
|
|
10888
|
+
};
|
|
10310
10889
|
}
|
|
10311
10890
|
}
|
|
10312
|
-
return Array.from(retryableById.values());
|
|
10313
10891
|
}
|
|
10314
10892
|
getProviderRateLimits(provider) {
|
|
10315
10893
|
switch (provider) {
|
|
@@ -10334,6 +10912,242 @@ var Indexer = class _Indexer {
|
|
|
10334
10912
|
return { concurrency: 3, intervalMs: 1e3, minRetryMs: 1e3, maxRetryMs: 3e4 };
|
|
10335
10913
|
}
|
|
10336
10914
|
}
|
|
10915
|
+
async processPendingChunkBatch(chunks, options) {
|
|
10916
|
+
const result = {
|
|
10917
|
+
indexedChunks: 0,
|
|
10918
|
+
failedChunks: 0,
|
|
10919
|
+
tokensUsed: 0,
|
|
10920
|
+
failedChunkIds: /* @__PURE__ */ new Set()
|
|
10921
|
+
};
|
|
10922
|
+
if (chunks.length === 0) {
|
|
10923
|
+
return result;
|
|
10924
|
+
}
|
|
10925
|
+
const chunksNeedingEmbedding = [];
|
|
10926
|
+
let cachedChunkCount = 0;
|
|
10927
|
+
if (options.reuseCachedEmbeddings && !options.forceReembed) {
|
|
10928
|
+
const missingHashes = new Set(options.database.getMissingEmbeddings(chunks.map((chunk) => chunk.contentHash)));
|
|
10929
|
+
for (const chunk of chunks) {
|
|
10930
|
+
if (missingHashes.has(chunk.contentHash)) {
|
|
10931
|
+
chunksNeedingEmbedding.push(chunk);
|
|
10932
|
+
continue;
|
|
10933
|
+
}
|
|
10934
|
+
const embeddingBuffer = options.database.getEmbedding(chunk.contentHash);
|
|
10935
|
+
if (!embeddingBuffer) {
|
|
10936
|
+
chunksNeedingEmbedding.push(chunk);
|
|
10937
|
+
continue;
|
|
10938
|
+
}
|
|
10939
|
+
options.store.add(chunk.id, Array.from(bufferToFloat32Array(embeddingBuffer)), chunk.metadata);
|
|
10940
|
+
options.invertedIndex.removeChunk(chunk.id);
|
|
10941
|
+
options.invertedIndex.addChunk(chunk.id, chunk.content);
|
|
10942
|
+
options.onSucceeded?.([chunk]);
|
|
10943
|
+
result.indexedChunks += 1;
|
|
10944
|
+
cachedChunkCount += 1;
|
|
10945
|
+
}
|
|
10946
|
+
} else {
|
|
10947
|
+
chunksNeedingEmbedding.push(...chunks);
|
|
10948
|
+
}
|
|
10949
|
+
this.logger.cache("info", "Embedding cache lookup", {
|
|
10950
|
+
needsEmbedding: chunksNeedingEmbedding.length,
|
|
10951
|
+
fromCache: cachedChunkCount
|
|
10952
|
+
});
|
|
10953
|
+
if (cachedChunkCount > 0) {
|
|
10954
|
+
this.logger.recordChunksFromCache(cachedChunkCount);
|
|
10955
|
+
options.onProgress?.(result);
|
|
10956
|
+
}
|
|
10957
|
+
if (chunksNeedingEmbedding.length === 0) {
|
|
10958
|
+
return result;
|
|
10959
|
+
}
|
|
10960
|
+
const pendingChunksById = new Map(chunksNeedingEmbedding.map((chunk) => [chunk.id, chunk]));
|
|
10961
|
+
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
10962
|
+
const completedVectorsByChunkId = /* @__PURE__ */ new Map();
|
|
10963
|
+
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
10964
|
+
const requestBatches = createPendingEmbeddingRequestBatches(
|
|
10965
|
+
chunksNeedingEmbedding,
|
|
10966
|
+
getDynamicBatchOptions(options.configuredProviderInfo)
|
|
10967
|
+
);
|
|
10968
|
+
let fatalError;
|
|
10969
|
+
for (const requestBatch of requestBatches) {
|
|
10970
|
+
await options.queue.onSizeLessThan(Math.max(1, options.providerRateLimits.concurrency));
|
|
10971
|
+
const task = options.queue.add(async () => {
|
|
10972
|
+
if (options.rateLimitState.backoffMs > 0) {
|
|
10973
|
+
await new Promise((resolve13) => setTimeout(resolve13, options.rateLimitState.backoffMs));
|
|
10974
|
+
}
|
|
10975
|
+
try {
|
|
10976
|
+
const embeddingResult = await pRetry(
|
|
10977
|
+
async () => {
|
|
10978
|
+
const texts = requestBatch.map((request) => request.text);
|
|
10979
|
+
return options.provider.embedBatch(texts);
|
|
10980
|
+
},
|
|
10981
|
+
{
|
|
10982
|
+
retries: this.config.indexing.retries,
|
|
10983
|
+
minTimeout: Math.max(this.config.indexing.retryDelayMs, options.providerRateLimits.minRetryMs),
|
|
10984
|
+
maxTimeout: options.providerRateLimits.maxRetryMs,
|
|
10985
|
+
factor: 2,
|
|
10986
|
+
shouldRetry: (error) => !(error.error instanceof CustomProviderNonRetryableError),
|
|
10987
|
+
onFailedAttempt: (error) => {
|
|
10988
|
+
const message = getErrorMessage4(error);
|
|
10989
|
+
if (isRateLimitError(error)) {
|
|
10990
|
+
options.rateLimitState.backoffMs = Math.min(
|
|
10991
|
+
options.providerRateLimits.maxRetryMs,
|
|
10992
|
+
(options.rateLimitState.backoffMs || options.providerRateLimits.minRetryMs) * 2
|
|
10993
|
+
);
|
|
10994
|
+
this.logger.embedding("warn", "Rate limited, backing off", {
|
|
10995
|
+
attempt: error.attemptNumber,
|
|
10996
|
+
retriesLeft: error.retriesLeft,
|
|
10997
|
+
backoffMs: options.rateLimitState.backoffMs
|
|
10998
|
+
});
|
|
10999
|
+
} else {
|
|
11000
|
+
this.logger.embedding("error", "Embedding batch failed", {
|
|
11001
|
+
attempt: error.attemptNumber,
|
|
11002
|
+
error: message
|
|
11003
|
+
});
|
|
11004
|
+
}
|
|
11005
|
+
}
|
|
11006
|
+
}
|
|
11007
|
+
);
|
|
11008
|
+
if (options.rateLimitState.backoffMs > 0) {
|
|
11009
|
+
options.rateLimitState.backoffMs = Math.max(0, options.rateLimitState.backoffMs - 2e3);
|
|
11010
|
+
}
|
|
11011
|
+
const touchedChunkIds = /* @__PURE__ */ new Set();
|
|
11012
|
+
requestBatch.forEach((request, index) => {
|
|
11013
|
+
if (result.failedChunkIds.has(request.chunk.id) || completedChunkIds.has(request.chunk.id)) {
|
|
11014
|
+
return;
|
|
11015
|
+
}
|
|
11016
|
+
const vector = embeddingResult.embeddings[index];
|
|
11017
|
+
if (!vector) {
|
|
11018
|
+
throw new Error(`Embedding API returned too few vectors for chunk ${request.chunk.id}`);
|
|
11019
|
+
}
|
|
11020
|
+
const parts = embeddingPartsByChunk.get(request.chunk.id) ?? [];
|
|
11021
|
+
parts[request.partIndex] = {
|
|
11022
|
+
vector,
|
|
11023
|
+
tokenCount: request.tokenCount
|
|
11024
|
+
};
|
|
11025
|
+
embeddingPartsByChunk.set(request.chunk.id, parts);
|
|
11026
|
+
touchedChunkIds.add(request.chunk.id);
|
|
11027
|
+
});
|
|
11028
|
+
const pooledResults = [];
|
|
11029
|
+
for (const chunkId of touchedChunkIds) {
|
|
11030
|
+
if (result.failedChunkIds.has(chunkId) || completedChunkIds.has(chunkId)) {
|
|
11031
|
+
continue;
|
|
11032
|
+
}
|
|
11033
|
+
const chunk = pendingChunksById.get(chunkId);
|
|
11034
|
+
if (!chunk) {
|
|
11035
|
+
continue;
|
|
11036
|
+
}
|
|
11037
|
+
const parts = embeddingPartsByChunk.get(chunk.id) ?? [];
|
|
11038
|
+
if (!hasAllEmbeddingParts(parts, chunk.texts.length)) {
|
|
11039
|
+
continue;
|
|
11040
|
+
}
|
|
11041
|
+
const orderedParts = parts;
|
|
11042
|
+
pooledResults.push({
|
|
11043
|
+
chunk,
|
|
11044
|
+
vector: poolEmbeddingVectors(
|
|
11045
|
+
orderedParts.map((part) => part.vector),
|
|
11046
|
+
orderedParts.map((part) => part.tokenCount)
|
|
11047
|
+
)
|
|
11048
|
+
});
|
|
11049
|
+
}
|
|
11050
|
+
if (pooledResults.length > 0) {
|
|
11051
|
+
options.database.upsertEmbeddingsBatch(pooledResults.map(({ chunk, vector }) => ({
|
|
11052
|
+
contentHash: chunk.contentHash,
|
|
11053
|
+
embedding: float32ArrayToBuffer(vector),
|
|
11054
|
+
chunkText: chunk.storageText,
|
|
11055
|
+
model: options.configuredProviderInfo.modelInfo.model
|
|
11056
|
+
})));
|
|
11057
|
+
const succeededChunks = pooledResults.map(({ chunk }) => chunk);
|
|
11058
|
+
for (const { chunk, vector } of pooledResults) {
|
|
11059
|
+
completedVectorsByChunkId.set(chunk.id, vector);
|
|
11060
|
+
}
|
|
11061
|
+
for (const chunk of succeededChunks) {
|
|
11062
|
+
completedChunkIds.add(chunk.id);
|
|
11063
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
11064
|
+
}
|
|
11065
|
+
}
|
|
11066
|
+
result.tokensUsed += embeddingResult.totalTokensUsed;
|
|
11067
|
+
this.logger.recordEmbeddingApiCall(embeddingResult.totalTokensUsed);
|
|
11068
|
+
this.logger.embedding("debug", "Embedded batch", {
|
|
11069
|
+
batchSize: pooledResults.length,
|
|
11070
|
+
requestCount: requestBatch.length,
|
|
11071
|
+
tokens: embeddingResult.totalTokensUsed
|
|
11072
|
+
});
|
|
11073
|
+
} catch (error) {
|
|
11074
|
+
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id)).filter((chunk) => options.incrementRepeatedFailures || !result.failedChunkIds.has(chunk.id));
|
|
11075
|
+
const failureMessage = getErrorMessage4(error);
|
|
11076
|
+
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11077
|
+
for (const chunk of failedChunks) {
|
|
11078
|
+
if (!result.failedChunkIds.has(chunk.id)) {
|
|
11079
|
+
result.failedChunkIds.add(chunk.id);
|
|
11080
|
+
result.failedChunks += 1;
|
|
11081
|
+
}
|
|
11082
|
+
embeddingPartsByChunk.delete(chunk.id);
|
|
11083
|
+
const attemptCount = (options.attemptCounts.get(chunk.id) ?? 0) + 1;
|
|
11084
|
+
options.attemptCounts.set(chunk.id, attemptCount);
|
|
11085
|
+
this.writeFailedBatchRecord(options.failedState, {
|
|
11086
|
+
chunks: [chunk],
|
|
11087
|
+
error: failureMessage,
|
|
11088
|
+
attemptCount,
|
|
11089
|
+
lastAttempt: failureTimestamp
|
|
11090
|
+
});
|
|
11091
|
+
}
|
|
11092
|
+
this.logger.recordEmbeddingError();
|
|
11093
|
+
this.logger.embedding("error", "Failed to embed batch after retries", {
|
|
11094
|
+
batchSize: failedChunks.length,
|
|
11095
|
+
requestCount: requestBatch.length,
|
|
11096
|
+
error: failureMessage
|
|
11097
|
+
});
|
|
11098
|
+
}
|
|
11099
|
+
options.onProgress?.(result);
|
|
11100
|
+
});
|
|
11101
|
+
void task.catch((error) => {
|
|
11102
|
+
fatalError ??= error;
|
|
11103
|
+
});
|
|
11104
|
+
}
|
|
11105
|
+
await options.queue.onIdle();
|
|
11106
|
+
if (fatalError !== void 0) {
|
|
11107
|
+
throw fatalError;
|
|
11108
|
+
}
|
|
11109
|
+
const orderedSucceededChunks = chunksNeedingEmbedding.filter((chunk) => completedVectorsByChunkId.has(chunk.id));
|
|
11110
|
+
if (orderedSucceededChunks.length > 0) {
|
|
11111
|
+
try {
|
|
11112
|
+
options.store.addBatch(orderedSucceededChunks.map((chunk) => ({
|
|
11113
|
+
id: chunk.id,
|
|
11114
|
+
vector: completedVectorsByChunkId.get(chunk.id),
|
|
11115
|
+
metadata: chunk.metadata
|
|
11116
|
+
})));
|
|
11117
|
+
for (const chunk of orderedSucceededChunks) {
|
|
11118
|
+
options.invertedIndex.removeChunk(chunk.id);
|
|
11119
|
+
options.invertedIndex.addChunk(chunk.id, chunk.content);
|
|
11120
|
+
}
|
|
11121
|
+
options.onSucceeded?.(orderedSucceededChunks);
|
|
11122
|
+
result.indexedChunks += orderedSucceededChunks.length;
|
|
11123
|
+
this.logger.recordChunksEmbedded(orderedSucceededChunks.length);
|
|
11124
|
+
} catch (error) {
|
|
11125
|
+
const failureMessage = getErrorMessage4(error);
|
|
11126
|
+
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11127
|
+
for (const chunk of orderedSucceededChunks) {
|
|
11128
|
+
options.store.remove(chunk.id);
|
|
11129
|
+
options.invertedIndex.removeChunk(chunk.id);
|
|
11130
|
+
result.failedChunkIds.add(chunk.id);
|
|
11131
|
+
result.failedChunks += 1;
|
|
11132
|
+
const attemptCount = (options.attemptCounts.get(chunk.id) ?? 0) + 1;
|
|
11133
|
+
options.attemptCounts.set(chunk.id, attemptCount);
|
|
11134
|
+
this.writeFailedBatchRecord(options.failedState, {
|
|
11135
|
+
chunks: [chunk],
|
|
11136
|
+
error: failureMessage,
|
|
11137
|
+
attemptCount,
|
|
11138
|
+
lastAttempt: failureTimestamp
|
|
11139
|
+
});
|
|
11140
|
+
}
|
|
11141
|
+
this.logger.recordEmbeddingError();
|
|
11142
|
+
this.logger.embedding("error", "Failed to publish embedded chunks", {
|
|
11143
|
+
batchSize: orderedSucceededChunks.length,
|
|
11144
|
+
error: failureMessage
|
|
11145
|
+
});
|
|
11146
|
+
}
|
|
11147
|
+
options.onProgress?.(result);
|
|
11148
|
+
}
|
|
11149
|
+
return result;
|
|
11150
|
+
}
|
|
10337
11151
|
async rerankCandidatesWithApi(query, candidates, options) {
|
|
10338
11152
|
const reranker = this.config.reranker;
|
|
10339
11153
|
if (!reranker || !reranker.enabled || candidates.length <= 1) {
|
|
@@ -10575,12 +11389,12 @@ var Indexer = class _Indexer {
|
|
|
10575
11389
|
}
|
|
10576
11390
|
}
|
|
10577
11391
|
captureReaderArtifactFingerprint() {
|
|
10578
|
-
const storePath =
|
|
11392
|
+
const storePath = path19.join(this.indexPath, "vectors");
|
|
10579
11393
|
return {
|
|
10580
11394
|
vectors: `${this.getReaderFileFingerprint(storePath)}|${this.getReaderFileFingerprint(`${storePath}.meta.json`)}`,
|
|
10581
|
-
keyword: this.getReaderFileFingerprint(
|
|
10582
|
-
database: this.getReaderFileFingerprint(
|
|
10583
|
-
databaseIdentity: this.getReaderFileFingerprint(
|
|
11395
|
+
keyword: this.getReaderFileFingerprint(path19.join(this.indexPath, "inverted-index.json")),
|
|
11396
|
+
database: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db")),
|
|
11397
|
+
databaseIdentity: this.getReaderFileFingerprint(path19.join(this.indexPath, "codebase.db"), true)
|
|
10584
11398
|
};
|
|
10585
11399
|
}
|
|
10586
11400
|
refreshReaderArtifacts() {
|
|
@@ -10605,13 +11419,13 @@ var Indexer = class _Indexer {
|
|
|
10605
11419
|
issues.set(component, this.createReadIssue(component, message));
|
|
10606
11420
|
this.readerArtifactRetryAfter.set(component, Date.now() + READER_ARTIFACT_RETRY_INTERVAL_MS);
|
|
10607
11421
|
};
|
|
10608
|
-
const storePath =
|
|
11422
|
+
const storePath = path19.join(this.indexPath, "vectors");
|
|
10609
11423
|
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
10610
|
-
const invertedIndexPath =
|
|
10611
|
-
const dbPath =
|
|
11424
|
+
const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
|
|
11425
|
+
const dbPath = path19.join(this.indexPath, "codebase.db");
|
|
10612
11426
|
if (vectorsChanged || retryDue("vectors")) {
|
|
10613
|
-
const vectorStoreExists =
|
|
10614
|
-
const vectorMetadataExists =
|
|
11427
|
+
const vectorStoreExists = existsSync11(storePath);
|
|
11428
|
+
const vectorMetadataExists = existsSync11(vectorMetadataPath);
|
|
10615
11429
|
if (vectorStoreExists && vectorMetadataExists) {
|
|
10616
11430
|
try {
|
|
10617
11431
|
const store = new VectorStore(storePath, this.configuredProviderInfo.modelInfo.dimensions);
|
|
@@ -10626,8 +11440,8 @@ var Indexer = class _Indexer {
|
|
|
10626
11440
|
setIssue("vectors", this.getVectorReadIssueMessage());
|
|
10627
11441
|
}
|
|
10628
11442
|
}
|
|
10629
|
-
if (keywordChanged || retryDue("keyword") || !
|
|
10630
|
-
if (
|
|
11443
|
+
if (keywordChanged || retryDue("keyword") || !existsSync11(invertedIndexPath) && (this.store?.count() ?? 0) > 0) {
|
|
11444
|
+
if (existsSync11(invertedIndexPath)) {
|
|
10631
11445
|
try {
|
|
10632
11446
|
const invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
10633
11447
|
invertedIndex.load();
|
|
@@ -10642,7 +11456,7 @@ var Indexer = class _Indexer {
|
|
|
10642
11456
|
}
|
|
10643
11457
|
}
|
|
10644
11458
|
if (databaseReplaced || databaseChanged && issues.has("database") || retryDue("database")) {
|
|
10645
|
-
if (
|
|
11459
|
+
if (existsSync11(dbPath)) {
|
|
10646
11460
|
try {
|
|
10647
11461
|
const database = Database.openReadOnly(dbPath);
|
|
10648
11462
|
if (this.database) {
|
|
@@ -10733,11 +11547,11 @@ var Indexer = class _Indexer {
|
|
|
10733
11547
|
}
|
|
10734
11548
|
}
|
|
10735
11549
|
const dimensions = this.configuredProviderInfo.modelInfo.dimensions;
|
|
10736
|
-
const storePath =
|
|
11550
|
+
const storePath = path19.join(this.indexPath, "vectors");
|
|
10737
11551
|
const vectorMetadataPath = `${storePath}.meta.json`;
|
|
10738
|
-
const invertedIndexPath =
|
|
10739
|
-
const dbPath =
|
|
10740
|
-
let dbIsNew = !
|
|
11552
|
+
const invertedIndexPath = path19.join(this.indexPath, "inverted-index.json");
|
|
11553
|
+
const dbPath = path19.join(this.indexPath, "codebase.db");
|
|
11554
|
+
let dbIsNew = !existsSync11(dbPath);
|
|
10741
11555
|
const readerArtifactFingerprint = mode === "reader" ? this.captureReaderArtifactFingerprint() : null;
|
|
10742
11556
|
if (mode === "writer") {
|
|
10743
11557
|
await fsPromises3.mkdir(this.indexPath, { recursive: true });
|
|
@@ -10756,14 +11570,14 @@ var Indexer = class _Indexer {
|
|
|
10756
11570
|
await this.resetLocalIndexArtifacts();
|
|
10757
11571
|
}
|
|
10758
11572
|
this.store = new VectorStore(storePath, dimensions);
|
|
10759
|
-
if (
|
|
11573
|
+
if (existsSync11(storePath) || existsSync11(vectorMetadataPath)) {
|
|
10760
11574
|
this.store.load();
|
|
10761
11575
|
}
|
|
10762
11576
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
10763
11577
|
try {
|
|
10764
11578
|
this.invertedIndex.load();
|
|
10765
11579
|
} catch {
|
|
10766
|
-
if (
|
|
11580
|
+
if (existsSync11(invertedIndexPath)) {
|
|
10767
11581
|
await fsPromises3.unlink(invertedIndexPath);
|
|
10768
11582
|
}
|
|
10769
11583
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
@@ -10781,8 +11595,8 @@ var Indexer = class _Indexer {
|
|
|
10781
11595
|
}
|
|
10782
11596
|
} else {
|
|
10783
11597
|
this.store = new VectorStore(storePath, dimensions);
|
|
10784
|
-
const vectorStoreExists =
|
|
10785
|
-
const vectorMetadataExists =
|
|
11598
|
+
const vectorStoreExists = existsSync11(storePath);
|
|
11599
|
+
const vectorMetadataExists = existsSync11(vectorMetadataPath);
|
|
10786
11600
|
const vectorReadFailureMessage = this.getVectorReadIssueMessage();
|
|
10787
11601
|
if (vectorStoreExists !== vectorMetadataExists) {
|
|
10788
11602
|
this.recordReadIssue("vectors", vectorReadFailureMessage);
|
|
@@ -10795,7 +11609,7 @@ var Indexer = class _Indexer {
|
|
|
10795
11609
|
}
|
|
10796
11610
|
}
|
|
10797
11611
|
this.invertedIndex = new InvertedIndex(invertedIndexPath);
|
|
10798
|
-
if (
|
|
11612
|
+
if (existsSync11(invertedIndexPath)) {
|
|
10799
11613
|
try {
|
|
10800
11614
|
this.invertedIndex.load();
|
|
10801
11615
|
} catch (error) {
|
|
@@ -10809,7 +11623,7 @@ var Indexer = class _Indexer {
|
|
|
10809
11623
|
} else if (this.store.count() > 0) {
|
|
10810
11624
|
this.recordReadIssue("keyword", this.getKeywordReadIssueMessage());
|
|
10811
11625
|
}
|
|
10812
|
-
if (
|
|
11626
|
+
if (existsSync11(dbPath)) {
|
|
10813
11627
|
try {
|
|
10814
11628
|
this.database = Database.openReadOnly(dbPath);
|
|
10815
11629
|
} catch (error) {
|
|
@@ -10901,7 +11715,7 @@ var Indexer = class _Indexer {
|
|
|
10901
11715
|
if (await this.tryResetCorruptedIndex("running automatic orphan garbage collection", error)) {
|
|
10902
11716
|
return {
|
|
10903
11717
|
resetCorruptedIndex: true,
|
|
10904
|
-
warning: this.getCorruptedIndexWarning(
|
|
11718
|
+
warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
|
|
10905
11719
|
};
|
|
10906
11720
|
}
|
|
10907
11721
|
throw error;
|
|
@@ -10916,7 +11730,7 @@ var Indexer = class _Indexer {
|
|
|
10916
11730
|
return;
|
|
10917
11731
|
}
|
|
10918
11732
|
const retainedEntries = store.getAllMetadata().filter(({ key }) => !excludedSet.has(key));
|
|
10919
|
-
const storeBasePath =
|
|
11733
|
+
const storeBasePath = path19.join(this.indexPath, "vectors");
|
|
10920
11734
|
const storeIndexPath = storeBasePath;
|
|
10921
11735
|
const storeMetadataPath = `${storeBasePath}.meta.json`;
|
|
10922
11736
|
const lease = this.requireActiveLease();
|
|
@@ -10926,19 +11740,19 @@ var Indexer = class _Indexer {
|
|
|
10926
11740
|
let backedUpMetadata = false;
|
|
10927
11741
|
let rebuiltCount = 0;
|
|
10928
11742
|
let skippedCount = 0;
|
|
10929
|
-
if (
|
|
11743
|
+
if (existsSync11(backupIndexPath)) {
|
|
10930
11744
|
unlinkSync2(backupIndexPath);
|
|
10931
11745
|
}
|
|
10932
|
-
if (
|
|
11746
|
+
if (existsSync11(backupMetadataPath)) {
|
|
10933
11747
|
unlinkSync2(backupMetadataPath);
|
|
10934
11748
|
}
|
|
10935
11749
|
try {
|
|
10936
|
-
if (
|
|
10937
|
-
|
|
11750
|
+
if (existsSync11(storeIndexPath)) {
|
|
11751
|
+
renameSync3(storeIndexPath, backupIndexPath);
|
|
10938
11752
|
backedUpIndex = true;
|
|
10939
11753
|
}
|
|
10940
|
-
if (
|
|
10941
|
-
|
|
11754
|
+
if (existsSync11(storeMetadataPath)) {
|
|
11755
|
+
renameSync3(storeMetadataPath, backupMetadataPath);
|
|
10942
11756
|
backedUpMetadata = true;
|
|
10943
11757
|
}
|
|
10944
11758
|
store.clear();
|
|
@@ -10958,10 +11772,10 @@ var Indexer = class _Indexer {
|
|
|
10958
11772
|
rebuiltCount += 1;
|
|
10959
11773
|
}
|
|
10960
11774
|
store.save();
|
|
10961
|
-
if (backedUpIndex &&
|
|
11775
|
+
if (backedUpIndex && existsSync11(backupIndexPath)) {
|
|
10962
11776
|
unlinkSync2(backupIndexPath);
|
|
10963
11777
|
}
|
|
10964
|
-
if (backedUpMetadata &&
|
|
11778
|
+
if (backedUpMetadata && existsSync11(backupMetadataPath)) {
|
|
10965
11779
|
unlinkSync2(backupMetadataPath);
|
|
10966
11780
|
}
|
|
10967
11781
|
this.logger.gc("info", "Rebuilt vector store to avoid native remove", {
|
|
@@ -10974,17 +11788,17 @@ var Indexer = class _Indexer {
|
|
|
10974
11788
|
store.clear();
|
|
10975
11789
|
} catch {
|
|
10976
11790
|
}
|
|
10977
|
-
if (
|
|
11791
|
+
if (existsSync11(storeIndexPath)) {
|
|
10978
11792
|
unlinkSync2(storeIndexPath);
|
|
10979
11793
|
}
|
|
10980
|
-
if (
|
|
11794
|
+
if (existsSync11(storeMetadataPath)) {
|
|
10981
11795
|
unlinkSync2(storeMetadataPath);
|
|
10982
11796
|
}
|
|
10983
|
-
if (backedUpIndex &&
|
|
10984
|
-
|
|
11797
|
+
if (backedUpIndex && existsSync11(backupIndexPath)) {
|
|
11798
|
+
renameSync3(backupIndexPath, storeIndexPath);
|
|
10985
11799
|
}
|
|
10986
|
-
if (backedUpMetadata &&
|
|
10987
|
-
|
|
11800
|
+
if (backedUpMetadata && existsSync11(backupMetadataPath)) {
|
|
11801
|
+
renameSync3(backupMetadataPath, storeMetadataPath);
|
|
10988
11802
|
}
|
|
10989
11803
|
if (backedUpIndex || backedUpMetadata) {
|
|
10990
11804
|
store.load();
|
|
@@ -10999,11 +11813,11 @@ var Indexer = class _Indexer {
|
|
|
10999
11813
|
return `Detected a corrupted local SQLite index at ${dbPath} and reset the local index. Run index_codebase to rebuild search data.`;
|
|
11000
11814
|
}
|
|
11001
11815
|
async removeProjectRuntimeStateArtifacts() {
|
|
11002
|
-
if (!
|
|
11816
|
+
if (!existsSync11(this.indexPath)) return;
|
|
11003
11817
|
const names = await fsPromises3.readdir(this.indexPath);
|
|
11004
11818
|
const runtimeStatePattern = /^(?:file-hashes|failed-batches)(?:\.[a-f0-9]{16})?\.json$/;
|
|
11005
11819
|
await Promise.all(
|
|
11006
|
-
names.filter((name) => runtimeStatePattern.test(name)).map((name) => fsPromises3.rm(
|
|
11820
|
+
names.filter((name) => runtimeStatePattern.test(name)).map((name) => fsPromises3.rm(path19.join(this.indexPath, name), { force: true }))
|
|
11007
11821
|
);
|
|
11008
11822
|
}
|
|
11009
11823
|
async resetLocalIndexArtifacts() {
|
|
@@ -11019,13 +11833,13 @@ var Indexer = class _Indexer {
|
|
|
11019
11833
|
this.readerArtifactRetryAfter.clear();
|
|
11020
11834
|
this.fileHashCache.clear();
|
|
11021
11835
|
const resetPaths = [
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
11025
|
-
|
|
11026
|
-
|
|
11027
|
-
|
|
11028
|
-
|
|
11836
|
+
path19.join(this.indexPath, "codebase.db"),
|
|
11837
|
+
path19.join(this.indexPath, "codebase.db-shm"),
|
|
11838
|
+
path19.join(this.indexPath, "codebase.db-wal"),
|
|
11839
|
+
path19.join(this.indexPath, "vectors"),
|
|
11840
|
+
path19.join(this.indexPath, "vectors.usearch"),
|
|
11841
|
+
path19.join(this.indexPath, "vectors.meta.json"),
|
|
11842
|
+
path19.join(this.indexPath, "inverted-index.json")
|
|
11029
11843
|
];
|
|
11030
11844
|
await Promise.all(resetPaths.map((targetPath) => fsPromises3.rm(targetPath, { recursive: true, force: true })));
|
|
11031
11845
|
await this.removeProjectRuntimeStateArtifacts();
|
|
@@ -11035,7 +11849,7 @@ var Indexer = class _Indexer {
|
|
|
11035
11849
|
if (!isSqliteCorruptionError(error)) {
|
|
11036
11850
|
return false;
|
|
11037
11851
|
}
|
|
11038
|
-
const dbPath =
|
|
11852
|
+
const dbPath = path19.join(this.indexPath, "codebase.db");
|
|
11039
11853
|
const warning = this.getCorruptedIndexWarning(dbPath);
|
|
11040
11854
|
const errorMessage = getErrorMessage4(error);
|
|
11041
11855
|
if (this.config.scope === "global") {
|
|
@@ -11344,7 +12158,6 @@ var Indexer = class _Indexer {
|
|
|
11344
12158
|
skippedFiles: [],
|
|
11345
12159
|
parseFailures: []
|
|
11346
12160
|
};
|
|
11347
|
-
const failedBatchesForCurrentRun = [];
|
|
11348
12161
|
onProgress?.({
|
|
11349
12162
|
phase: "scanning",
|
|
11350
12163
|
filesProcessed: 0,
|
|
@@ -11359,14 +12172,10 @@ var Indexer = class _Indexer {
|
|
|
11359
12172
|
const reparseCachedMetalFiles = database.getMetadata(metalParserMetadataKey) !== METAL_PARSER_VERSION;
|
|
11360
12173
|
const symbolExtractorMetadataKey = this.getSymbolExtractorVersionMetadataKey();
|
|
11361
12174
|
const refreshCachedSymbols = database.getMetadata(symbolExtractorMetadataKey) !== SYMBOL_EXTRACTOR_VERSION;
|
|
11362
|
-
if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some(
|
|
11363
|
-
(filePath) => path18.extname(filePath).toLowerCase() === ".swift"
|
|
11364
|
-
)) {
|
|
12175
|
+
if (reparseCachedSwiftFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path19.extname(filePath).toLowerCase() === ".swift")) {
|
|
11365
12176
|
this.logger.info("Reindexing cached Swift files for parser support");
|
|
11366
12177
|
}
|
|
11367
|
-
if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some(
|
|
11368
|
-
(filePath) => path18.extname(filePath).toLowerCase() === ".metal"
|
|
11369
|
-
)) {
|
|
12178
|
+
if (reparseCachedMetalFiles && Array.from(this.fileHashCache.keys()).some((filePath) => path19.extname(filePath).toLowerCase() === ".metal")) {
|
|
11370
12179
|
this.logger.info("Reindexing cached Metal files for parser support");
|
|
11371
12180
|
}
|
|
11372
12181
|
const includePatterns = [...this.config.include, ...this.config.additionalInclude];
|
|
@@ -11388,46 +12197,44 @@ var Indexer = class _Indexer {
|
|
|
11388
12197
|
totalFiles: files.length,
|
|
11389
12198
|
skippedFiles: skipped.length
|
|
11390
12199
|
});
|
|
11391
|
-
const
|
|
12200
|
+
const changedFileDescriptors = [];
|
|
11392
12201
|
const unchangedFilePaths = /* @__PURE__ */ new Set();
|
|
11393
12202
|
const currentFileHashes = /* @__PURE__ */ new Map();
|
|
11394
12203
|
const needsCallGraphResolutionMigration = database.getMetadata(this.getCallGraphResolutionMetadataKey()) !== CALL_GRAPH_RESOLUTION_VERSION;
|
|
11395
|
-
for (const
|
|
11396
|
-
const storedPath = this.toStoredFilePath(
|
|
11397
|
-
const currentHash = hashFile(
|
|
12204
|
+
for (const file of files) {
|
|
12205
|
+
const storedPath = this.toStoredFilePath(file.path);
|
|
12206
|
+
const currentHash = hashFile(file.path);
|
|
11398
12207
|
currentFileHashes.set(storedPath, currentHash);
|
|
11399
12208
|
const cachedHashMatches = this.fileHashCache.get(storedPath) === currentHash;
|
|
11400
12209
|
const needsCallGraphRefresh = cachedHashMatches && needsCallGraphResolutionMigration && database.getChunksByFile(storedPath).some(
|
|
11401
12210
|
(chunk) => chunk.language === "php" || chunk.language === "c" || chunk.language === "cpp"
|
|
11402
12211
|
);
|
|
11403
|
-
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles &&
|
|
11404
|
-
const requiresMetalParserUpgrade = reparseCachedMetalFiles &&
|
|
12212
|
+
const requiresSwiftParserUpgrade = reparseCachedSwiftFiles && path19.extname(storedPath).toLowerCase() === ".swift";
|
|
12213
|
+
const requiresMetalParserUpgrade = reparseCachedMetalFiles && path19.extname(storedPath).toLowerCase() === ".metal";
|
|
11405
12214
|
if (cachedHashMatches && !needsCallGraphRefresh && !requiresSwiftParserUpgrade && !requiresMetalParserUpgrade && !refreshCachedSymbols) {
|
|
11406
12215
|
unchangedFilePaths.add(storedPath);
|
|
11407
12216
|
this.logger.recordCacheHit();
|
|
11408
12217
|
} else {
|
|
11409
|
-
|
|
11410
|
-
|
|
12218
|
+
changedFileDescriptors.push({
|
|
12219
|
+
storedPath,
|
|
12220
|
+
materializedPath: file.path,
|
|
12221
|
+
hash: currentHash,
|
|
12222
|
+
sourceBytes: file.size
|
|
12223
|
+
});
|
|
11411
12224
|
this.logger.recordCacheMiss();
|
|
11412
12225
|
}
|
|
11413
12226
|
}
|
|
11414
12227
|
this.logger.cache("info", "File hash cache results", {
|
|
11415
12228
|
unchanged: unchangedFilePaths.size,
|
|
11416
|
-
changed:
|
|
12229
|
+
changed: changedFileDescriptors.length
|
|
11417
12230
|
});
|
|
11418
12231
|
onProgress?.({
|
|
11419
12232
|
phase: "parsing",
|
|
11420
|
-
filesProcessed:
|
|
12233
|
+
filesProcessed: unchangedFilePaths.size,
|
|
11421
12234
|
totalFiles: files.length,
|
|
11422
12235
|
chunksProcessed: 0,
|
|
11423
12236
|
totalChunks: 0
|
|
11424
12237
|
});
|
|
11425
|
-
const parseStartTime = performance2.now();
|
|
11426
|
-
const parsedFiles = parseFiles(changedFiles);
|
|
11427
|
-
const parseMs = performance2.now() - parseStartTime;
|
|
11428
|
-
this.logger.recordFilesParsed(parsedFiles.length);
|
|
11429
|
-
this.logger.recordParseDuration(parseMs);
|
|
11430
|
-
this.logger.debug("Parsed changed files", { parsedCount: parsedFiles.length, parseMs: parseMs.toFixed(2) });
|
|
11431
12238
|
const existingChunks = /* @__PURE__ */ new Map();
|
|
11432
12239
|
const existingChunksByFile = /* @__PURE__ */ new Map();
|
|
11433
12240
|
const existingMetadataById = /* @__PURE__ */ new Map();
|
|
@@ -11443,17 +12250,17 @@ var Indexer = class _Indexer {
|
|
|
11443
12250
|
}
|
|
11444
12251
|
existingChunks.set(key, metadata.hash);
|
|
11445
12252
|
existingMetadataById.set(key, metadata);
|
|
11446
|
-
const fileChunks = existingChunksByFile.get(metadata.filePath)
|
|
12253
|
+
const fileChunks = existingChunksByFile.get(metadata.filePath) ?? /* @__PURE__ */ new Set();
|
|
11447
12254
|
fileChunks.add(key);
|
|
11448
12255
|
existingChunksByFile.set(metadata.filePath, fileChunks);
|
|
11449
12256
|
}
|
|
11450
12257
|
const currentChunkIds = /* @__PURE__ */ new Set();
|
|
11451
|
-
const
|
|
11452
|
-
const
|
|
12258
|
+
const allSymbolIds = /* @__PURE__ */ new Set();
|
|
12259
|
+
const failedChunkIds = /* @__PURE__ */ new Set();
|
|
12260
|
+
const retryableChunksWithExistingData = /* @__PURE__ */ new Set();
|
|
11453
12261
|
const gitBlameEnabled = this.config.indexing.gitBlame.enabled && isGitRepo(this.materializedProjectRoot);
|
|
11454
12262
|
let backfilledBlameMetadata = false;
|
|
11455
12263
|
for (const filePath of unchangedFilePaths) {
|
|
11456
|
-
currentFilePaths.add(filePath);
|
|
11457
12264
|
const fileChunks = existingChunksByFile.get(filePath);
|
|
11458
12265
|
if (fileChunks) {
|
|
11459
12266
|
for (const chunkId of fileChunks) {
|
|
@@ -11461,635 +12268,552 @@ var Indexer = class _Indexer {
|
|
|
11461
12268
|
}
|
|
11462
12269
|
}
|
|
11463
12270
|
}
|
|
11464
|
-
const
|
|
11465
|
-
|
|
11466
|
-
|
|
11467
|
-
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11471
|
-
|
|
11472
|
-
|
|
11473
|
-
|
|
11474
|
-
|
|
12271
|
+
const shouldRetryFailedPath = (filePath) => filePath !== null && currentFileHashes.has(filePath) && unchangedFilePaths.has(filePath);
|
|
12272
|
+
const failedProcessing = this.prepareFailedBatchProcessing(scopedRoots, shouldRetryFailedPath);
|
|
12273
|
+
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
12274
|
+
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
12275
|
+
const queue = new PQueue({
|
|
12276
|
+
concurrency: providerRateLimits.concurrency,
|
|
12277
|
+
interval: providerRateLimits.intervalMs,
|
|
12278
|
+
intervalCap: providerRateLimits.concurrency
|
|
12279
|
+
});
|
|
12280
|
+
const rateLimitState = { backoffMs: 0 };
|
|
12281
|
+
let writeTransactionActive = false;
|
|
12282
|
+
try {
|
|
12283
|
+
database.beginWriteTransaction();
|
|
12284
|
+
writeTransactionActive = true;
|
|
12285
|
+
const blameChunkDataBatch = [];
|
|
12286
|
+
if (gitBlameEnabled) {
|
|
12287
|
+
const backfillItems = [];
|
|
12288
|
+
for (const chunkId of currentChunkIds) {
|
|
12289
|
+
const metadata = existingMetadataById.get(chunkId);
|
|
12290
|
+
if (!metadata || hasBlameMetadata(metadata)) {
|
|
12291
|
+
continue;
|
|
12292
|
+
}
|
|
12293
|
+
const chunk = database.getChunk(chunkId);
|
|
12294
|
+
if (!chunk) {
|
|
12295
|
+
continue;
|
|
12296
|
+
}
|
|
12297
|
+
const blame = await getChunkGitBlame(
|
|
12298
|
+
this.materializedProjectRoot,
|
|
12299
|
+
this.toMaterializedFilePath(chunk.filePath),
|
|
12300
|
+
chunk.startLine,
|
|
12301
|
+
chunk.endLine
|
|
12302
|
+
);
|
|
12303
|
+
const blameMetadata = metadataFromBlame(blame);
|
|
12304
|
+
if (!blameMetadata.blameSha) {
|
|
12305
|
+
continue;
|
|
12306
|
+
}
|
|
12307
|
+
blameChunkDataBatch.push({
|
|
12308
|
+
...chunk,
|
|
12309
|
+
blameSha: blameMetadata.blameSha,
|
|
12310
|
+
blameAuthor: blameMetadata.blameAuthor,
|
|
12311
|
+
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
12312
|
+
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
12313
|
+
blameSummary: blameMetadata.blameSummary
|
|
12314
|
+
});
|
|
12315
|
+
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
12316
|
+
if (embeddingBuffer) {
|
|
12317
|
+
backfillItems.push({
|
|
12318
|
+
id: chunkId,
|
|
12319
|
+
vector: Array.from(bufferToFloat32Array(embeddingBuffer)),
|
|
12320
|
+
metadata: { ...metadata, ...blameMetadata }
|
|
12321
|
+
});
|
|
12322
|
+
}
|
|
11475
12323
|
}
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
this.toMaterializedFilePath(chunk.filePath),
|
|
11479
|
-
chunk.startLine,
|
|
11480
|
-
chunk.endLine
|
|
11481
|
-
);
|
|
11482
|
-
const blameMetadata = metadataFromBlame(blame);
|
|
11483
|
-
if (!blameMetadata.blameSha) {
|
|
11484
|
-
continue;
|
|
12324
|
+
if (blameChunkDataBatch.length > 0) {
|
|
12325
|
+
database.upsertChunksBatch(blameChunkDataBatch);
|
|
11485
12326
|
}
|
|
11486
|
-
|
|
11487
|
-
|
|
11488
|
-
|
|
11489
|
-
blameAuthor: blameMetadata.blameAuthor,
|
|
11490
|
-
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
11491
|
-
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
11492
|
-
blameSummary: blameMetadata.blameSummary
|
|
11493
|
-
});
|
|
11494
|
-
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
11495
|
-
if (!embeddingBuffer) {
|
|
11496
|
-
continue;
|
|
12327
|
+
if (backfillItems.length > 0) {
|
|
12328
|
+
store.addBatch(backfillItems);
|
|
12329
|
+
backfilledBlameMetadata = true;
|
|
11497
12330
|
}
|
|
11498
|
-
backfillItems.push({
|
|
11499
|
-
id: chunkId,
|
|
11500
|
-
vector: Array.from(bufferToFloat32Array(embeddingBuffer)),
|
|
11501
|
-
metadata: {
|
|
11502
|
-
...metadata,
|
|
11503
|
-
...blameMetadata
|
|
11504
|
-
}
|
|
11505
|
-
});
|
|
11506
12331
|
}
|
|
11507
|
-
|
|
11508
|
-
|
|
11509
|
-
|
|
11510
|
-
|
|
11511
|
-
|
|
11512
|
-
for (const parsed of parsedFiles) {
|
|
11513
|
-
currentFilePaths.add(parsed.path);
|
|
11514
|
-
if (parsed.chunks.length === 0) {
|
|
11515
|
-
stats.parseFailures.push(path18.isAbsolute(parsed.path) ? path18.relative(this.projectRoot, parsed.path) : parsed.path);
|
|
11516
|
-
}
|
|
11517
|
-
let chunksToProcess = parsed.chunks;
|
|
11518
|
-
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
11519
|
-
const changedFile = changedFiles.find((f) => f.path === parsed.path);
|
|
11520
|
-
if (changedFile) {
|
|
11521
|
-
const textChunks = parseFileAsText(parsed.path, changedFile.content);
|
|
11522
|
-
chunksToProcess = textChunks;
|
|
12332
|
+
for (const filePath of unchangedFilePaths) {
|
|
12333
|
+
for (const symbol of database.getSymbolsByFile(filePath)) {
|
|
12334
|
+
if (!restrictExistingChunksToBranch || previousBranchSymbolIdSet.has(symbol.id)) {
|
|
12335
|
+
allSymbolIds.add(symbol.id);
|
|
12336
|
+
}
|
|
11523
12337
|
}
|
|
11524
12338
|
}
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
const
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
filePath: parsed.path,
|
|
11547
|
-
startLine: chunk.startLine,
|
|
11548
|
-
endLine: chunk.endLine,
|
|
11549
|
-
nodeType: chunk.chunkType,
|
|
11550
|
-
name: chunk.name,
|
|
11551
|
-
language: chunk.language,
|
|
11552
|
-
blameSha: blameMetadata.blameSha,
|
|
11553
|
-
blameAuthor: blameMetadata.blameAuthor,
|
|
11554
|
-
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
11555
|
-
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
11556
|
-
blameSummary: blameMetadata.blameSummary
|
|
12339
|
+
let processedChangedFiles = 0;
|
|
12340
|
+
for (const descriptorBatch of iterateOrderedFileBatches(
|
|
12341
|
+
changedFileDescriptors,
|
|
12342
|
+
(descriptor) => descriptor.sourceBytes,
|
|
12343
|
+
this.fileBatchLimits
|
|
12344
|
+
)) {
|
|
12345
|
+
const loadedFiles = await Promise.all(descriptorBatch.map(async (descriptor) => ({
|
|
12346
|
+
path: descriptor.storedPath,
|
|
12347
|
+
content: await fsPromises3.readFile(descriptor.materializedPath, "utf-8"),
|
|
12348
|
+
hash: descriptor.hash
|
|
12349
|
+
})));
|
|
12350
|
+
const loadedByPath = new Map(loadedFiles.map((file) => [file.path, file]));
|
|
12351
|
+
const descriptorByPath = new Map(descriptorBatch.map((descriptor) => [descriptor.storedPath, descriptor]));
|
|
12352
|
+
const parseStartTime = performance2.now();
|
|
12353
|
+
const parsedFiles = parseFiles(loadedFiles);
|
|
12354
|
+
const parseMs = performance2.now() - parseStartTime;
|
|
12355
|
+
this.logger.recordFilesParsed(parsedFiles.length);
|
|
12356
|
+
this.logger.recordParseDuration(parseMs);
|
|
12357
|
+
this.logger.debug("Parsed changed file batch", {
|
|
12358
|
+
parsedCount: parsedFiles.length,
|
|
12359
|
+
parseMs: parseMs.toFixed(2)
|
|
11557
12360
|
});
|
|
11558
|
-
|
|
11559
|
-
|
|
12361
|
+
const chunkDataBatch = [];
|
|
12362
|
+
const pendingChunks = [];
|
|
12363
|
+
const symbolBatch = [];
|
|
12364
|
+
const edgeBatch = [];
|
|
12365
|
+
for (const parsed of parsedFiles) {
|
|
12366
|
+
const loadedFile = loadedByPath.get(parsed.path);
|
|
12367
|
+
const descriptor = descriptorByPath.get(parsed.path);
|
|
12368
|
+
if (!loadedFile || !descriptor) {
|
|
12369
|
+
throw new Error(`Parsed file was not present in its source batch: ${parsed.path}`);
|
|
12370
|
+
}
|
|
12371
|
+
if (parsed.chunks.length === 0) {
|
|
12372
|
+
stats.parseFailures.push(path19.isAbsolute(parsed.path) ? path19.relative(this.projectRoot, parsed.path) : parsed.path);
|
|
12373
|
+
}
|
|
12374
|
+
let chunksToProcess = parsed.chunks;
|
|
12375
|
+
if (this.config.indexing.fallbackToTextOnMaxChunks && chunksToProcess.length > this.config.indexing.maxChunksPerFile) {
|
|
12376
|
+
chunksToProcess = parseFileAsText(parsed.path, loadedFile.content);
|
|
12377
|
+
}
|
|
12378
|
+
chunksToProcess = selectIndexableChunks(
|
|
12379
|
+
chunksToProcess,
|
|
12380
|
+
this.config.indexing.maxChunksPerFile,
|
|
12381
|
+
this.config.indexing.semanticOnly
|
|
12382
|
+
);
|
|
12383
|
+
for (const chunk of chunksToProcess) {
|
|
12384
|
+
const id = this.getPreparedChunkId(generateChunkId(parsed.path, chunk));
|
|
12385
|
+
const contentHash = generateChunkHash(chunk);
|
|
12386
|
+
const existingContentHash = existingChunks.get(id);
|
|
12387
|
+
const existingChunk = gitBlameEnabled ? database.getChunk(id) : null;
|
|
12388
|
+
const blame = gitBlameEnabled && existingContentHash !== contentHash ? await getChunkGitBlame(
|
|
12389
|
+
this.materializedProjectRoot,
|
|
12390
|
+
descriptor.materializedPath,
|
|
12391
|
+
chunk.startLine,
|
|
12392
|
+
chunk.endLine
|
|
12393
|
+
) : blameFromChunkData(existingChunk);
|
|
12394
|
+
const blameMetadata = metadataFromBlame(blame);
|
|
12395
|
+
currentChunkIds.add(id);
|
|
12396
|
+
chunkDataBatch.push({
|
|
12397
|
+
chunkId: id,
|
|
12398
|
+
contentHash,
|
|
12399
|
+
filePath: parsed.path,
|
|
12400
|
+
startLine: chunk.startLine,
|
|
12401
|
+
endLine: chunk.endLine,
|
|
12402
|
+
nodeType: chunk.chunkType,
|
|
12403
|
+
name: chunk.name,
|
|
12404
|
+
language: chunk.language,
|
|
12405
|
+
blameSha: blameMetadata.blameSha,
|
|
12406
|
+
blameAuthor: blameMetadata.blameAuthor,
|
|
12407
|
+
blameAuthorEmail: blameMetadata.blameAuthorEmail,
|
|
12408
|
+
blameCommittedAt: blameMetadata.blameCommittedAt,
|
|
12409
|
+
blameSummary: blameMetadata.blameSummary
|
|
12410
|
+
});
|
|
12411
|
+
if (existingContentHash === contentHash) {
|
|
12412
|
+
continue;
|
|
12413
|
+
}
|
|
12414
|
+
const texts = createEmbeddingTexts(chunk, parsed.path, maxChunkTokens).map((text3) => ({
|
|
12415
|
+
text: text3,
|
|
12416
|
+
tokenCount: estimateTokens(text3)
|
|
12417
|
+
}));
|
|
12418
|
+
pendingChunks.push({
|
|
12419
|
+
id,
|
|
12420
|
+
texts,
|
|
12421
|
+
storageText: createPendingChunkStorageText(texts),
|
|
12422
|
+
content: chunk.content,
|
|
12423
|
+
contentHash,
|
|
12424
|
+
metadata: {
|
|
12425
|
+
filePath: parsed.path,
|
|
12426
|
+
startLine: chunk.startLine,
|
|
12427
|
+
endLine: chunk.endLine,
|
|
12428
|
+
chunkType: chunk.chunkType,
|
|
12429
|
+
name: chunk.name,
|
|
12430
|
+
language: chunk.language,
|
|
12431
|
+
hash: contentHash,
|
|
12432
|
+
...blameMetadata
|
|
12433
|
+
}
|
|
12434
|
+
});
|
|
12435
|
+
}
|
|
12436
|
+
const fileSymbols = [];
|
|
12437
|
+
for (const parsedSymbol of parsed.symbols) {
|
|
12438
|
+
if (!CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(parsedSymbol.kind)) {
|
|
12439
|
+
continue;
|
|
12440
|
+
}
|
|
12441
|
+
const preparedNamespace = this.getPreparedBranchNamespace();
|
|
12442
|
+
const symbolId = `sym_${hashContent(
|
|
12443
|
+
(preparedNamespace ? `${preparedNamespace}:` : "") + parsed.path + ":" + parsedSymbol.name + ":" + parsedSymbol.kind + ":" + parsedSymbol.startLine + ":" + parsedSymbol.startCol + ":" + descriptor.hash
|
|
12444
|
+
).slice(0, 16)}`;
|
|
12445
|
+
const symbol = {
|
|
12446
|
+
id: symbolId,
|
|
12447
|
+
filePath: parsed.path,
|
|
12448
|
+
name: parsedSymbol.name,
|
|
12449
|
+
kind: parsedSymbol.kind,
|
|
12450
|
+
startLine: parsedSymbol.startLine,
|
|
12451
|
+
startCol: parsedSymbol.startCol,
|
|
12452
|
+
endLine: parsedSymbol.endLine,
|
|
12453
|
+
endCol: parsedSymbol.endCol,
|
|
12454
|
+
language: parsedSymbol.language
|
|
12455
|
+
};
|
|
12456
|
+
fileSymbols.push(symbol);
|
|
12457
|
+
symbolBatch.push(symbol);
|
|
12458
|
+
allSymbolIds.add(symbolId);
|
|
12459
|
+
}
|
|
12460
|
+
const fileLanguage = parsed.symbols[0]?.language ?? parsed.chunks[0]?.language;
|
|
12461
|
+
if (!fileLanguage || !CALL_GRAPH_LANGUAGES.has(fileLanguage)) {
|
|
12462
|
+
continue;
|
|
12463
|
+
}
|
|
12464
|
+
const isCaseInsensitiveLanguage = CASE_INSENSITIVE_LANGUAGES.has(fileLanguage);
|
|
12465
|
+
const normalizeSymbolKey = (name) => isCaseInsensitiveLanguage ? name.toLowerCase() : name;
|
|
12466
|
+
const symbolsByName = /* @__PURE__ */ new Map();
|
|
12467
|
+
for (const symbol of fileSymbols) {
|
|
12468
|
+
const key = normalizeSymbolKey(symbol.name);
|
|
12469
|
+
const symbols = symbolsByName.get(key) ?? [];
|
|
12470
|
+
symbols.push(symbol);
|
|
12471
|
+
symbolsByName.set(key, symbols);
|
|
12472
|
+
}
|
|
12473
|
+
for (const site of extractCalls(loadedFile.content, fileLanguage)) {
|
|
12474
|
+
const enclosingSymbol = findEnclosingSymbol(fileSymbols, site.line, site.column);
|
|
12475
|
+
if (!enclosingSymbol) {
|
|
12476
|
+
continue;
|
|
12477
|
+
}
|
|
12478
|
+
let candidates = symbolsByName.get(normalizeSymbolKey(site.calleeName));
|
|
12479
|
+
if (fileLanguage === "php" && candidates) {
|
|
12480
|
+
if (site.callType === "Constructor") {
|
|
12481
|
+
candidates = candidates.filter((candidate) => PHP_CLASS_SYMBOL_CHUNK_TYPES.has(candidate.kind));
|
|
12482
|
+
} else if (site.callType === "Call") {
|
|
12483
|
+
candidates = candidates.filter((candidate) => PHP_FUNCTION_SYMBOL_CHUNK_TYPES.has(candidate.kind));
|
|
12484
|
+
}
|
|
12485
|
+
}
|
|
12486
|
+
candidates = candidates?.filter(
|
|
12487
|
+
(symbol) => isCompatibleCFamilyCallTarget(fileLanguage, site.callType, symbol.kind)
|
|
12488
|
+
);
|
|
12489
|
+
const resolvedTarget = candidates?.length === 1 ? candidates[0] : void 0;
|
|
12490
|
+
edgeBatch.push({
|
|
12491
|
+
id: `edge_${hashContent(
|
|
12492
|
+
enclosingSymbol.id + ":" + site.calleeName + ":" + site.line + ":" + site.column
|
|
12493
|
+
).slice(0, 16)}`,
|
|
12494
|
+
fromSymbolId: enclosingSymbol.id,
|
|
12495
|
+
targetName: site.calleeName,
|
|
12496
|
+
toSymbolId: resolvedTarget?.id,
|
|
12497
|
+
callType: site.callType,
|
|
12498
|
+
confidence: site.confidence,
|
|
12499
|
+
line: site.line,
|
|
12500
|
+
col: site.column,
|
|
12501
|
+
isResolved: resolvedTarget !== void 0
|
|
12502
|
+
});
|
|
12503
|
+
}
|
|
11560
12504
|
}
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
tokenCount: estimateTokens(text3)
|
|
11564
|
-
}));
|
|
11565
|
-
const metadata = {
|
|
11566
|
-
filePath: parsed.path,
|
|
11567
|
-
startLine: chunk.startLine,
|
|
11568
|
-
endLine: chunk.endLine,
|
|
11569
|
-
chunkType: chunk.chunkType,
|
|
11570
|
-
name: chunk.name,
|
|
11571
|
-
language: chunk.language,
|
|
11572
|
-
hash: contentHash,
|
|
11573
|
-
...blameMetadata
|
|
11574
|
-
};
|
|
11575
|
-
pendingChunks.push({
|
|
11576
|
-
id,
|
|
11577
|
-
texts,
|
|
11578
|
-
storageText: createPendingChunkStorageText(texts),
|
|
11579
|
-
content: chunk.content,
|
|
11580
|
-
contentHash,
|
|
11581
|
-
metadata
|
|
11582
|
-
});
|
|
11583
|
-
}
|
|
11584
|
-
}
|
|
11585
|
-
const retryableFailedChunks = this.collectRetryableFailedChunks(
|
|
11586
|
-
currentFileHashes,
|
|
11587
|
-
unchangedFilePaths,
|
|
11588
|
-
getSafeEmbeddingChunkTokenLimit(configuredProviderInfo)
|
|
11589
|
-
);
|
|
11590
|
-
const retryableFailedAttemptCounts = /* @__PURE__ */ new Map();
|
|
11591
|
-
const retryableChunksWithExistingData = /* @__PURE__ */ new Set();
|
|
11592
|
-
if (retryableFailedChunks.length > 0) {
|
|
11593
|
-
const pendingChunkIds = new Set(pendingChunks.map((chunk) => chunk.id));
|
|
11594
|
-
for (const { chunk, attemptCount } of retryableFailedChunks) {
|
|
11595
|
-
retryableFailedAttemptCounts.set(chunk.id, attemptCount);
|
|
11596
|
-
if (existingChunks.has(chunk.id)) {
|
|
11597
|
-
retryableChunksWithExistingData.add(chunk.id);
|
|
12505
|
+
if (chunkDataBatch.length > 0) {
|
|
12506
|
+
database.upsertChunksBatch(chunkDataBatch);
|
|
11598
12507
|
}
|
|
11599
|
-
if (
|
|
11600
|
-
|
|
11601
|
-
pendingChunkIds.add(chunk.id);
|
|
11602
|
-
currentChunkIds.add(chunk.id);
|
|
12508
|
+
if (symbolBatch.length > 0) {
|
|
12509
|
+
database.upsertSymbolsBatch(symbolBatch);
|
|
11603
12510
|
}
|
|
11604
|
-
|
|
11605
|
-
|
|
11606
|
-
|
|
11607
|
-
|
|
11608
|
-
|
|
11609
|
-
|
|
11610
|
-
|
|
11611
|
-
|
|
11612
|
-
|
|
11613
|
-
|
|
11614
|
-
|
|
11615
|
-
for (const parsedSymbol of parsed.symbols) {
|
|
11616
|
-
if (!CALL_GRAPH_SYMBOL_CHUNK_TYPES.has(parsedSymbol.kind)) continue;
|
|
11617
|
-
const preparedNamespace = this.getPreparedBranchNamespace();
|
|
11618
|
-
const symbolId = `sym_${hashContent(
|
|
11619
|
-
(preparedNamespace ? `${preparedNamespace}:` : "") + parsed.path + ":" + parsedSymbol.name + ":" + parsedSymbol.kind + ":" + parsedSymbol.startLine + ":" + parsedSymbol.startCol + ":" + changedFile.hash
|
|
11620
|
-
).slice(0, 16)}`;
|
|
11621
|
-
const symbol = {
|
|
11622
|
-
id: symbolId,
|
|
11623
|
-
filePath: parsed.path,
|
|
11624
|
-
name: parsedSymbol.name,
|
|
11625
|
-
kind: parsedSymbol.kind,
|
|
11626
|
-
startLine: parsedSymbol.startLine,
|
|
11627
|
-
startCol: parsedSymbol.startCol,
|
|
11628
|
-
endLine: parsedSymbol.endLine,
|
|
11629
|
-
endCol: parsedSymbol.endCol,
|
|
11630
|
-
language: parsedSymbol.language
|
|
11631
|
-
};
|
|
11632
|
-
fileSymbols.push(symbol);
|
|
11633
|
-
allSymbolIds.add(symbolId);
|
|
11634
|
-
}
|
|
11635
|
-
const fileLanguage = parsed.symbols[0]?.language ?? parsed.chunks[0]?.language;
|
|
11636
|
-
const isCaseInsensitiveLanguage = !!fileLanguage && CASE_INSENSITIVE_LANGUAGES.has(fileLanguage);
|
|
11637
|
-
const normalizeSymbolKey = (name) => isCaseInsensitiveLanguage ? name.toLowerCase() : name;
|
|
11638
|
-
const symbolsByName = /* @__PURE__ */ new Map();
|
|
11639
|
-
for (const symbol of fileSymbols) {
|
|
11640
|
-
const key = normalizeSymbolKey(symbol.name);
|
|
11641
|
-
const existing = symbolsByName.get(key) ?? [];
|
|
11642
|
-
existing.push(symbol);
|
|
11643
|
-
symbolsByName.set(key, existing);
|
|
11644
|
-
}
|
|
11645
|
-
if (fileSymbols.length > 0) {
|
|
11646
|
-
database.upsertSymbolsBatch(fileSymbols);
|
|
11647
|
-
symbolsByFile.set(parsed.path, fileSymbols);
|
|
11648
|
-
}
|
|
11649
|
-
if (!fileLanguage || !CALL_GRAPH_LANGUAGES.has(fileLanguage)) continue;
|
|
11650
|
-
const callSites = extractCalls(changedFile.content, fileLanguage);
|
|
11651
|
-
if (callSites.length === 0) continue;
|
|
11652
|
-
const edges = [];
|
|
11653
|
-
for (const site of callSites) {
|
|
11654
|
-
const enclosingSymbol = findEnclosingSymbol(
|
|
11655
|
-
fileSymbols,
|
|
11656
|
-
site.line,
|
|
11657
|
-
site.column
|
|
11658
|
-
);
|
|
11659
|
-
if (!enclosingSymbol) continue;
|
|
11660
|
-
const edgeId = `edge_${hashContent(enclosingSymbol.id + ":" + site.calleeName + ":" + site.line + ":" + site.column).slice(0, 16)}`;
|
|
11661
|
-
edges.push({
|
|
11662
|
-
id: edgeId,
|
|
11663
|
-
fromSymbolId: enclosingSymbol.id,
|
|
11664
|
-
targetName: site.calleeName,
|
|
11665
|
-
toSymbolId: void 0,
|
|
11666
|
-
callType: site.callType,
|
|
11667
|
-
confidence: site.confidence,
|
|
11668
|
-
line: site.line,
|
|
11669
|
-
col: site.column,
|
|
11670
|
-
isResolved: false
|
|
12511
|
+
if (edgeBatch.length > 0) {
|
|
12512
|
+
database.upsertCallEdgesBatch(edgeBatch);
|
|
12513
|
+
}
|
|
12514
|
+
processedChangedFiles += descriptorBatch.length;
|
|
12515
|
+
stats.totalChunks += pendingChunks.length;
|
|
12516
|
+
onProgress?.({
|
|
12517
|
+
phase: "parsing",
|
|
12518
|
+
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
12519
|
+
totalFiles: files.length,
|
|
12520
|
+
chunksProcessed: stats.indexedChunks,
|
|
12521
|
+
totalChunks: stats.totalChunks
|
|
11671
12522
|
});
|
|
11672
|
-
|
|
11673
|
-
|
|
11674
|
-
|
|
11675
|
-
|
|
11676
|
-
|
|
11677
|
-
|
|
11678
|
-
|
|
11679
|
-
|
|
11680
|
-
|
|
11681
|
-
|
|
11682
|
-
|
|
11683
|
-
|
|
11684
|
-
|
|
11685
|
-
|
|
12523
|
+
if (pendingChunks.length > 0) {
|
|
12524
|
+
onProgress?.({
|
|
12525
|
+
phase: "embedding",
|
|
12526
|
+
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
12527
|
+
totalFiles: files.length,
|
|
12528
|
+
chunksProcessed: stats.indexedChunks,
|
|
12529
|
+
totalChunks: stats.totalChunks
|
|
12530
|
+
});
|
|
12531
|
+
const batchResult = await this.processPendingChunkBatch(pendingChunks, {
|
|
12532
|
+
store,
|
|
12533
|
+
provider,
|
|
12534
|
+
invertedIndex,
|
|
12535
|
+
database,
|
|
12536
|
+
configuredProviderInfo,
|
|
12537
|
+
queue,
|
|
12538
|
+
providerRateLimits,
|
|
12539
|
+
rateLimitState,
|
|
12540
|
+
failedState: failedProcessing.state,
|
|
12541
|
+
attemptCounts: /* @__PURE__ */ new Map(),
|
|
12542
|
+
forceReembed: forceScopedReembed,
|
|
12543
|
+
reuseCachedEmbeddings: true,
|
|
12544
|
+
incrementRepeatedFailures: true,
|
|
12545
|
+
onProgress: (batchProgress) => onProgress?.({
|
|
12546
|
+
phase: "embedding",
|
|
12547
|
+
filesProcessed: unchangedFilePaths.size + processedChangedFiles,
|
|
12548
|
+
totalFiles: files.length,
|
|
12549
|
+
chunksProcessed: stats.indexedChunks + batchProgress.indexedChunks,
|
|
12550
|
+
totalChunks: stats.totalChunks
|
|
12551
|
+
})
|
|
12552
|
+
});
|
|
12553
|
+
stats.indexedChunks += batchResult.indexedChunks;
|
|
12554
|
+
stats.failedChunks += batchResult.failedChunks;
|
|
12555
|
+
stats.tokensUsed += batchResult.tokensUsed;
|
|
12556
|
+
for (const chunkId of batchResult.failedChunkIds) {
|
|
12557
|
+
failedChunkIds.add(chunkId);
|
|
12558
|
+
if (forceScopedReembed) {
|
|
12559
|
+
failedForcedChunkIds.add(chunkId);
|
|
11686
12560
|
}
|
|
11687
12561
|
}
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
|
|
12562
|
+
}
|
|
12563
|
+
}
|
|
12564
|
+
const retryableFailedChunks = this.iterateLatestFailedChunks(
|
|
12565
|
+
failedProcessing.latestById,
|
|
12566
|
+
scopedRoots,
|
|
12567
|
+
shouldRetryFailedPath,
|
|
12568
|
+
maxChunkTokens
|
|
12569
|
+
);
|
|
12570
|
+
for (const retryBatch of iterateOrderedFileBatches(
|
|
12571
|
+
retryableFailedChunks,
|
|
12572
|
+
({ chunk }) => Buffer.byteLength(chunk.content, "utf-8"),
|
|
12573
|
+
this.fileBatchLimits
|
|
12574
|
+
)) {
|
|
12575
|
+
const pendingChunks = retryBatch.map(({ chunk }) => chunk);
|
|
12576
|
+
const attemptCounts = new Map(retryBatch.map(({ chunk, attemptCount }) => [chunk.id, attemptCount]));
|
|
12577
|
+
for (const chunk of pendingChunks) {
|
|
12578
|
+
currentChunkIds.add(chunk.id);
|
|
12579
|
+
if (existingChunks.has(chunk.id)) {
|
|
12580
|
+
retryableChunksWithExistingData.add(chunk.id);
|
|
12581
|
+
}
|
|
12582
|
+
}
|
|
12583
|
+
stats.totalChunks += pendingChunks.length;
|
|
12584
|
+
onProgress?.({
|
|
12585
|
+
phase: "embedding",
|
|
12586
|
+
filesProcessed: files.length,
|
|
12587
|
+
totalFiles: files.length,
|
|
12588
|
+
chunksProcessed: stats.indexedChunks,
|
|
12589
|
+
totalChunks: stats.totalChunks
|
|
12590
|
+
});
|
|
12591
|
+
const batchResult = await this.processPendingChunkBatch(pendingChunks, {
|
|
12592
|
+
store,
|
|
12593
|
+
provider,
|
|
12594
|
+
invertedIndex,
|
|
12595
|
+
database,
|
|
12596
|
+
configuredProviderInfo,
|
|
12597
|
+
queue,
|
|
12598
|
+
providerRateLimits,
|
|
12599
|
+
rateLimitState,
|
|
12600
|
+
failedState: failedProcessing.state,
|
|
12601
|
+
attemptCounts,
|
|
12602
|
+
forceReembed: forceScopedReembed,
|
|
12603
|
+
reuseCachedEmbeddings: true,
|
|
12604
|
+
incrementRepeatedFailures: true,
|
|
12605
|
+
onProgress: (batchProgress) => onProgress?.({
|
|
12606
|
+
phase: "embedding",
|
|
12607
|
+
filesProcessed: files.length,
|
|
12608
|
+
totalFiles: files.length,
|
|
12609
|
+
chunksProcessed: stats.indexedChunks + batchProgress.indexedChunks,
|
|
12610
|
+
totalChunks: stats.totalChunks
|
|
12611
|
+
})
|
|
12612
|
+
});
|
|
12613
|
+
stats.indexedChunks += batchResult.indexedChunks;
|
|
12614
|
+
stats.failedChunks += batchResult.failedChunks;
|
|
12615
|
+
stats.tokensUsed += batchResult.tokensUsed;
|
|
12616
|
+
for (const chunkId of batchResult.failedChunkIds) {
|
|
12617
|
+
failedChunkIds.add(chunkId);
|
|
12618
|
+
if (forceScopedReembed) {
|
|
12619
|
+
failedForcedChunkIds.add(chunkId);
|
|
11693
12620
|
}
|
|
11694
12621
|
}
|
|
11695
12622
|
}
|
|
11696
|
-
|
|
11697
|
-
|
|
11698
|
-
|
|
11699
|
-
|
|
11700
|
-
if (!restrictExistingChunksToBranch || previousBranchSymbolIdSet.has(sym.id)) {
|
|
11701
|
-
allSymbolIds.add(sym.id);
|
|
12623
|
+
const removedChunkIds = [];
|
|
12624
|
+
for (const [chunkId] of existingChunks) {
|
|
12625
|
+
if (!currentChunkIds.has(chunkId)) {
|
|
12626
|
+
removedChunkIds.push(chunkId);
|
|
11702
12627
|
}
|
|
11703
12628
|
}
|
|
11704
|
-
|
|
11705
|
-
|
|
11706
|
-
|
|
11707
|
-
|
|
11708
|
-
|
|
12629
|
+
const removedCount = removedChunkIds.length;
|
|
12630
|
+
stats.existingChunks = currentChunkIds.size - stats.totalChunks;
|
|
12631
|
+
stats.removedChunks = removedCount;
|
|
12632
|
+
this.logger.recordChunksProcessed(currentChunkIds.size);
|
|
12633
|
+
this.logger.recordChunksRemoved(removedCount);
|
|
12634
|
+
this.logger.info("Chunk analysis complete", {
|
|
12635
|
+
pending: stats.totalChunks,
|
|
12636
|
+
existing: stats.existingChunks,
|
|
12637
|
+
removed: removedCount
|
|
12638
|
+
});
|
|
12639
|
+
if (stats.totalChunks === 0 && removedCount === 0) {
|
|
12640
|
+
const removedStoredChunks = this.replaceBranchCatalog(
|
|
12641
|
+
store,
|
|
12642
|
+
invertedIndex,
|
|
12643
|
+
database,
|
|
12644
|
+
branchCatalogKey,
|
|
12645
|
+
previousBranchChunkIds,
|
|
12646
|
+
Array.from(currentChunkIds),
|
|
12647
|
+
previousBranchSymbolIds,
|
|
12648
|
+
Array.from(allSymbolIds)
|
|
12649
|
+
);
|
|
12650
|
+
const vectorPath = path19.join(this.indexPath, "vectors");
|
|
12651
|
+
const shouldFingerprintLegacyPair = !store.hasFingerprint() && existsSync11(vectorPath) && existsSync11(`${vectorPath}.meta.json`);
|
|
12652
|
+
if (backfilledBlameMetadata || shouldFingerprintLegacyPair || removedStoredChunks) {
|
|
12653
|
+
store.save();
|
|
12654
|
+
}
|
|
12655
|
+
if (removedStoredChunks) {
|
|
12656
|
+
this.saveInvertedIndex(invertedIndex);
|
|
12657
|
+
}
|
|
12658
|
+
if (scopedRoots) {
|
|
12659
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12660
|
+
} else {
|
|
12661
|
+
this.fileHashCache = currentFileHashes;
|
|
12662
|
+
this.saveFileHashCache();
|
|
12663
|
+
}
|
|
12664
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12665
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12666
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12667
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
12668
|
+
this.saveBranchCommit(database, indexedCommit);
|
|
12669
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
12670
|
+
this.indexCompatibility = { compatible: true };
|
|
12671
|
+
database.commitWriteTransaction();
|
|
12672
|
+
writeTransactionActive = false;
|
|
12673
|
+
stats.durationMs = Date.now() - startTime;
|
|
12674
|
+
onProgress?.({
|
|
12675
|
+
phase: "complete",
|
|
12676
|
+
filesProcessed: files.length,
|
|
12677
|
+
totalFiles: files.length,
|
|
12678
|
+
chunksProcessed: 0,
|
|
12679
|
+
totalChunks: 0
|
|
12680
|
+
});
|
|
12681
|
+
return stats;
|
|
11709
12682
|
}
|
|
11710
|
-
|
|
11711
|
-
|
|
11712
|
-
|
|
11713
|
-
|
|
11714
|
-
|
|
11715
|
-
|
|
11716
|
-
|
|
11717
|
-
|
|
11718
|
-
|
|
11719
|
-
|
|
11720
|
-
|
|
11721
|
-
});
|
|
11722
|
-
if (pendingChunks.length === 0 && removedCount === 0) {
|
|
11723
|
-
const removedStoredChunks = this.replaceBranchCatalog(
|
|
11724
|
-
store,
|
|
11725
|
-
invertedIndex,
|
|
11726
|
-
database,
|
|
11727
|
-
branchCatalogKey,
|
|
11728
|
-
previousBranchChunkIds,
|
|
11729
|
-
Array.from(currentChunkIds),
|
|
11730
|
-
previousBranchSymbolIds,
|
|
11731
|
-
Array.from(allSymbolIds)
|
|
11732
|
-
);
|
|
11733
|
-
const vectorPath = path18.join(this.indexPath, "vectors");
|
|
11734
|
-
const shouldFingerprintLegacyPair = !store.hasFingerprint() && existsSync10(vectorPath) && existsSync10(`${vectorPath}.meta.json`);
|
|
11735
|
-
if (backfilledBlameMetadata || shouldFingerprintLegacyPair || removedStoredChunks) {
|
|
12683
|
+
if (stats.totalChunks === 0) {
|
|
12684
|
+
this.replaceBranchCatalog(
|
|
12685
|
+
store,
|
|
12686
|
+
invertedIndex,
|
|
12687
|
+
database,
|
|
12688
|
+
branchCatalogKey,
|
|
12689
|
+
previousBranchChunkIds,
|
|
12690
|
+
Array.from(currentChunkIds),
|
|
12691
|
+
previousBranchSymbolIds,
|
|
12692
|
+
Array.from(allSymbolIds)
|
|
12693
|
+
);
|
|
11736
12694
|
store.save();
|
|
11737
|
-
}
|
|
11738
|
-
if (removedStoredChunks) {
|
|
11739
12695
|
this.saveInvertedIndex(invertedIndex);
|
|
12696
|
+
if (scopedRoots) {
|
|
12697
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12698
|
+
} else {
|
|
12699
|
+
this.fileHashCache = currentFileHashes;
|
|
12700
|
+
this.saveFileHashCache();
|
|
12701
|
+
}
|
|
12702
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12703
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12704
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12705
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
12706
|
+
this.saveBranchCommit(database, indexedCommit);
|
|
12707
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
12708
|
+
this.indexCompatibility = { compatible: true };
|
|
12709
|
+
database.commitWriteTransaction();
|
|
12710
|
+
writeTransactionActive = false;
|
|
12711
|
+
stats.durationMs = Date.now() - startTime;
|
|
12712
|
+
onProgress?.({
|
|
12713
|
+
phase: "complete",
|
|
12714
|
+
filesProcessed: files.length,
|
|
12715
|
+
totalFiles: files.length,
|
|
12716
|
+
chunksProcessed: 0,
|
|
12717
|
+
totalChunks: 0
|
|
12718
|
+
});
|
|
12719
|
+
return stats;
|
|
11740
12720
|
}
|
|
11741
|
-
if (scopedRoots) {
|
|
11742
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11743
|
-
this.clearScopedFailedBatches(scopedRoots);
|
|
11744
|
-
} else {
|
|
11745
|
-
this.fileHashCache = currentFileHashes;
|
|
11746
|
-
this.saveFileHashCache();
|
|
11747
|
-
this.saveFailedBatches([]);
|
|
11748
|
-
}
|
|
11749
|
-
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
11750
|
-
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
11751
|
-
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
11752
|
-
this.saveBranchCommit(database, indexedCommit);
|
|
11753
|
-
this.saveIndexMetadata(configuredProviderInfo);
|
|
11754
|
-
this.indexCompatibility = { compatible: true };
|
|
11755
|
-
stats.durationMs = Date.now() - startTime;
|
|
11756
12721
|
onProgress?.({
|
|
11757
|
-
phase: "
|
|
12722
|
+
phase: "storing",
|
|
11758
12723
|
filesProcessed: files.length,
|
|
11759
12724
|
totalFiles: files.length,
|
|
11760
|
-
chunksProcessed:
|
|
11761
|
-
totalChunks:
|
|
12725
|
+
chunksProcessed: stats.indexedChunks,
|
|
12726
|
+
totalChunks: stats.totalChunks
|
|
12727
|
+
});
|
|
12728
|
+
const branchChunkIds = Array.from(currentChunkIds).filter((chunkId) => {
|
|
12729
|
+
const isNewlyFailed = failedChunkIds.has(chunkId) && !retryableChunksWithExistingData.has(chunkId);
|
|
12730
|
+
const isForcedFailed = forceScopedReembed && failedForcedChunkIds.has(chunkId);
|
|
12731
|
+
return !isNewlyFailed && !isForcedFailed;
|
|
11762
12732
|
});
|
|
11763
|
-
return stats;
|
|
11764
|
-
}
|
|
11765
|
-
if (pendingChunks.length === 0) {
|
|
11766
12733
|
this.replaceBranchCatalog(
|
|
11767
12734
|
store,
|
|
11768
12735
|
invertedIndex,
|
|
11769
12736
|
database,
|
|
11770
12737
|
branchCatalogKey,
|
|
11771
12738
|
previousBranchChunkIds,
|
|
11772
|
-
|
|
12739
|
+
branchChunkIds,
|
|
11773
12740
|
previousBranchSymbolIds,
|
|
11774
12741
|
Array.from(allSymbolIds)
|
|
11775
12742
|
);
|
|
11776
12743
|
store.save();
|
|
11777
12744
|
this.saveInvertedIndex(invertedIndex);
|
|
11778
|
-
if (scopedRoots) {
|
|
11779
|
-
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
11780
|
-
|
|
11781
|
-
|
|
11782
|
-
this.
|
|
11783
|
-
|
|
11784
|
-
|
|
11785
|
-
|
|
11786
|
-
|
|
11787
|
-
|
|
11788
|
-
|
|
11789
|
-
|
|
11790
|
-
|
|
11791
|
-
|
|
11792
|
-
|
|
11793
|
-
|
|
11794
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
11797
|
-
|
|
11798
|
-
|
|
11799
|
-
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
onProgress?.({
|
|
11803
|
-
phase: "embedding",
|
|
11804
|
-
filesProcessed: files.length,
|
|
11805
|
-
totalFiles: files.length,
|
|
11806
|
-
chunksProcessed: 0,
|
|
11807
|
-
totalChunks: pendingChunks.length
|
|
11808
|
-
});
|
|
11809
|
-
const allContentHashes = pendingChunks.map((c) => c.contentHash);
|
|
11810
|
-
const missingHashes = new Set(database.getMissingEmbeddings(allContentHashes));
|
|
11811
|
-
const forcedReembedChunkIds = forceScopedReembed ? new Set(pendingChunks.map((chunk) => chunk.id)) : /* @__PURE__ */ new Set();
|
|
11812
|
-
const chunksNeedingEmbedding = pendingChunks.filter((c) => forcedReembedChunkIds.has(c.id) || missingHashes.has(c.contentHash));
|
|
11813
|
-
const chunksWithExistingEmbedding = pendingChunks.filter((c) => !forcedReembedChunkIds.has(c.id) && !missingHashes.has(c.contentHash));
|
|
11814
|
-
this.logger.cache("info", "Embedding cache lookup", {
|
|
11815
|
-
needsEmbedding: chunksNeedingEmbedding.length,
|
|
11816
|
-
fromCache: chunksWithExistingEmbedding.length
|
|
11817
|
-
});
|
|
11818
|
-
this.logger.recordChunksFromCache(chunksWithExistingEmbedding.length);
|
|
11819
|
-
for (const chunk of chunksWithExistingEmbedding) {
|
|
11820
|
-
const embeddingBuffer = database.getEmbedding(chunk.contentHash);
|
|
11821
|
-
if (embeddingBuffer) {
|
|
11822
|
-
const vector = bufferToFloat32Array(embeddingBuffer);
|
|
11823
|
-
store.add(chunk.id, Array.from(vector), chunk.metadata);
|
|
11824
|
-
invertedIndex.removeChunk(chunk.id);
|
|
11825
|
-
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
11826
|
-
stats.indexedChunks++;
|
|
11827
|
-
}
|
|
11828
|
-
}
|
|
11829
|
-
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
11830
|
-
const queue = new PQueue({
|
|
11831
|
-
concurrency: providerRateLimits.concurrency,
|
|
11832
|
-
interval: providerRateLimits.intervalMs,
|
|
11833
|
-
intervalCap: providerRateLimits.concurrency
|
|
11834
|
-
});
|
|
11835
|
-
const pendingChunksById = new Map(chunksNeedingEmbedding.map((chunk) => [chunk.id, chunk]));
|
|
11836
|
-
const embeddingPartsByChunk = /* @__PURE__ */ new Map();
|
|
11837
|
-
const completedChunkIds = /* @__PURE__ */ new Set();
|
|
11838
|
-
const failedChunkIds = /* @__PURE__ */ new Set();
|
|
11839
|
-
const requestBatches = createPendingEmbeddingRequestBatches(
|
|
11840
|
-
chunksNeedingEmbedding,
|
|
11841
|
-
getDynamicBatchOptions(configuredProviderInfo)
|
|
11842
|
-
);
|
|
11843
|
-
let rateLimitBackoffMs = 0;
|
|
11844
|
-
for (const requestBatch of requestBatches) {
|
|
11845
|
-
queue.add(async () => {
|
|
11846
|
-
if (rateLimitBackoffMs > 0) {
|
|
11847
|
-
await new Promise((resolve13) => setTimeout(resolve13, rateLimitBackoffMs));
|
|
11848
|
-
}
|
|
11849
|
-
try {
|
|
11850
|
-
const result = await pRetry(
|
|
11851
|
-
async () => {
|
|
11852
|
-
const texts = requestBatch.map((request) => request.text);
|
|
11853
|
-
return provider.embedBatch(texts);
|
|
11854
|
-
},
|
|
11855
|
-
{
|
|
11856
|
-
retries: this.config.indexing.retries,
|
|
11857
|
-
minTimeout: Math.max(this.config.indexing.retryDelayMs, providerRateLimits.minRetryMs),
|
|
11858
|
-
maxTimeout: providerRateLimits.maxRetryMs,
|
|
11859
|
-
factor: 2,
|
|
11860
|
-
shouldRetry: (error) => !(error.error instanceof CustomProviderNonRetryableError),
|
|
11861
|
-
onFailedAttempt: (error) => {
|
|
11862
|
-
const message = getErrorMessage4(error);
|
|
11863
|
-
if (isRateLimitError(error)) {
|
|
11864
|
-
rateLimitBackoffMs = Math.min(providerRateLimits.maxRetryMs, (rateLimitBackoffMs || providerRateLimits.minRetryMs) * 2);
|
|
11865
|
-
this.logger.embedding("warn", `Rate limited, backing off`, {
|
|
11866
|
-
attempt: error.attemptNumber,
|
|
11867
|
-
retriesLeft: error.retriesLeft,
|
|
11868
|
-
backoffMs: rateLimitBackoffMs
|
|
11869
|
-
});
|
|
11870
|
-
} else {
|
|
11871
|
-
this.logger.embedding("error", `Embedding batch failed`, {
|
|
11872
|
-
attempt: error.attemptNumber,
|
|
11873
|
-
error: message
|
|
11874
|
-
});
|
|
11875
|
-
}
|
|
11876
|
-
}
|
|
11877
|
-
}
|
|
11878
|
-
);
|
|
11879
|
-
if (rateLimitBackoffMs > 0) {
|
|
11880
|
-
rateLimitBackoffMs = Math.max(0, rateLimitBackoffMs - 2e3);
|
|
11881
|
-
}
|
|
11882
|
-
const touchedChunkIds = /* @__PURE__ */ new Set();
|
|
11883
|
-
requestBatch.forEach((request, idx) => {
|
|
11884
|
-
if (failedChunkIds.has(request.chunk.id) || completedChunkIds.has(request.chunk.id)) {
|
|
11885
|
-
return;
|
|
11886
|
-
}
|
|
11887
|
-
const vector = result.embeddings[idx];
|
|
11888
|
-
if (!vector) {
|
|
11889
|
-
throw new Error(`Embedding API returned too few vectors for chunk ${request.chunk.id}`);
|
|
11890
|
-
}
|
|
11891
|
-
const parts = embeddingPartsByChunk.get(request.chunk.id) ?? [];
|
|
11892
|
-
parts[request.partIndex] = {
|
|
11893
|
-
vector,
|
|
11894
|
-
tokenCount: request.tokenCount
|
|
11895
|
-
};
|
|
11896
|
-
embeddingPartsByChunk.set(request.chunk.id, parts);
|
|
11897
|
-
touchedChunkIds.add(request.chunk.id);
|
|
11898
|
-
});
|
|
11899
|
-
const pooledResults = [];
|
|
11900
|
-
for (const chunkId of touchedChunkIds) {
|
|
11901
|
-
if (failedChunkIds.has(chunkId) || completedChunkIds.has(chunkId)) {
|
|
11902
|
-
continue;
|
|
11903
|
-
}
|
|
11904
|
-
const chunk = pendingChunksById.get(chunkId);
|
|
11905
|
-
if (!chunk) {
|
|
11906
|
-
continue;
|
|
11907
|
-
}
|
|
11908
|
-
const parts = embeddingPartsByChunk.get(chunk.id) ?? [];
|
|
11909
|
-
if (!hasAllEmbeddingParts(parts, chunk.texts.length)) {
|
|
11910
|
-
continue;
|
|
11911
|
-
}
|
|
11912
|
-
const orderedParts = parts;
|
|
11913
|
-
pooledResults.push({
|
|
11914
|
-
chunk,
|
|
11915
|
-
vector: poolEmbeddingVectors(
|
|
11916
|
-
orderedParts.map((part) => part.vector),
|
|
11917
|
-
orderedParts.map((part) => part.tokenCount)
|
|
11918
|
-
)
|
|
11919
|
-
});
|
|
11920
|
-
}
|
|
11921
|
-
if (pooledResults.length > 0) {
|
|
11922
|
-
const items = pooledResults.map(({ chunk, vector }) => ({
|
|
11923
|
-
id: chunk.id,
|
|
11924
|
-
vector,
|
|
11925
|
-
metadata: chunk.metadata
|
|
11926
|
-
}));
|
|
11927
|
-
store.addBatch(items);
|
|
11928
|
-
const embeddingBatchItems = pooledResults.map(({ chunk, vector }) => ({
|
|
11929
|
-
contentHash: chunk.contentHash,
|
|
11930
|
-
embedding: float32ArrayToBuffer(vector),
|
|
11931
|
-
chunkText: chunk.storageText,
|
|
11932
|
-
model: configuredProviderInfo.modelInfo.model
|
|
11933
|
-
}));
|
|
11934
|
-
try {
|
|
11935
|
-
database.upsertEmbeddingsBatch(embeddingBatchItems);
|
|
11936
|
-
} catch (dbError) {
|
|
11937
|
-
this.rebuildVectorStoreExcludingChunkIds(
|
|
11938
|
-
store,
|
|
11939
|
-
database,
|
|
11940
|
-
pooledResults.map(({ chunk }) => chunk.id)
|
|
11941
|
-
);
|
|
11942
|
-
throw dbError;
|
|
11943
|
-
}
|
|
11944
|
-
for (const { chunk } of pooledResults) {
|
|
11945
|
-
invertedIndex.removeChunk(chunk.id);
|
|
11946
|
-
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
11947
|
-
completedChunkIds.add(chunk.id);
|
|
11948
|
-
embeddingPartsByChunk.delete(chunk.id);
|
|
11949
|
-
}
|
|
11950
|
-
stats.indexedChunks += pooledResults.length;
|
|
11951
|
-
this.logger.recordChunksEmbedded(pooledResults.length);
|
|
11952
|
-
}
|
|
11953
|
-
stats.tokensUsed += result.totalTokensUsed;
|
|
11954
|
-
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
11955
|
-
this.logger.embedding("debug", `Embedded batch`, {
|
|
11956
|
-
batchSize: pooledResults.length,
|
|
11957
|
-
requestCount: requestBatch.length,
|
|
11958
|
-
tokens: result.totalTokensUsed
|
|
11959
|
-
});
|
|
11960
|
-
onProgress?.({
|
|
11961
|
-
phase: "embedding",
|
|
11962
|
-
filesProcessed: files.length,
|
|
11963
|
-
totalFiles: files.length,
|
|
11964
|
-
chunksProcessed: stats.indexedChunks,
|
|
11965
|
-
totalChunks: pendingChunks.length
|
|
11966
|
-
});
|
|
11967
|
-
} catch (error) {
|
|
11968
|
-
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id));
|
|
11969
|
-
const failureMessage = getErrorMessage4(error);
|
|
11970
|
-
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
11971
|
-
for (const chunk of failedChunks) {
|
|
11972
|
-
if (!failedChunkIds.has(chunk.id)) {
|
|
11973
|
-
failedChunkIds.add(chunk.id);
|
|
11974
|
-
stats.failedChunks += 1;
|
|
11975
|
-
}
|
|
11976
|
-
if (forceScopedReembed) {
|
|
11977
|
-
failedForcedChunkIds.add(chunk.id);
|
|
11978
|
-
}
|
|
11979
|
-
embeddingPartsByChunk.delete(chunk.id);
|
|
11980
|
-
const existingFailedBatchIndex = failedBatchesForCurrentRun.findIndex(
|
|
11981
|
-
(failedBatch2) => failedBatch2.chunks[0]?.id === chunk.id
|
|
11982
|
-
);
|
|
11983
|
-
const existingFailedBatch = existingFailedBatchIndex === -1 ? void 0 : failedBatchesForCurrentRun[existingFailedBatchIndex];
|
|
11984
|
-
const failedBatch = {
|
|
11985
|
-
chunks: [chunk],
|
|
11986
|
-
error: failureMessage,
|
|
11987
|
-
attemptCount: (existingFailedBatch?.attemptCount ?? retryableFailedAttemptCounts.get(chunk.id) ?? 0) + 1,
|
|
11988
|
-
lastAttempt: failureTimestamp
|
|
11989
|
-
};
|
|
11990
|
-
if (existingFailedBatchIndex === -1) {
|
|
11991
|
-
failedBatchesForCurrentRun.push(failedBatch);
|
|
11992
|
-
} else {
|
|
11993
|
-
failedBatchesForCurrentRun[existingFailedBatchIndex] = failedBatch;
|
|
11994
|
-
}
|
|
11995
|
-
}
|
|
11996
|
-
this.logger.recordEmbeddingError();
|
|
11997
|
-
this.logger.embedding("error", `Failed to embed batch after retries`, {
|
|
11998
|
-
batchSize: failedChunks.length,
|
|
11999
|
-
requestCount: requestBatch.length,
|
|
12000
|
-
error: failureMessage
|
|
12745
|
+
if (scopedRoots) {
|
|
12746
|
+
this.replaceScopedFileHashCache(currentFileHashes, scopedRoots);
|
|
12747
|
+
} else {
|
|
12748
|
+
this.fileHashCache = currentFileHashes;
|
|
12749
|
+
this.saveFileHashCache();
|
|
12750
|
+
}
|
|
12751
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12752
|
+
database.commitWriteTransaction();
|
|
12753
|
+
writeTransactionActive = false;
|
|
12754
|
+
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
12755
|
+
const gcReset = await this.maybeRunOrphanGc();
|
|
12756
|
+
if (gcReset) {
|
|
12757
|
+
stats.durationMs = Date.now() - startTime;
|
|
12758
|
+
stats.warning = gcReset.warning;
|
|
12759
|
+
stats.resetCorruptedIndex = true;
|
|
12760
|
+
this.logger.recordIndexingEnd();
|
|
12761
|
+
this.logger.warn("Indexing ended after resetting corrupted local index during automatic GC", {
|
|
12762
|
+
files: stats.totalFiles,
|
|
12763
|
+
indexed: stats.indexedChunks,
|
|
12764
|
+
existing: stats.existingChunks,
|
|
12765
|
+
removed: stats.removedChunks,
|
|
12766
|
+
failed: stats.failedChunks,
|
|
12767
|
+
tokens: stats.tokensUsed,
|
|
12768
|
+
durationMs: stats.durationMs
|
|
12001
12769
|
});
|
|
12770
|
+
return stats;
|
|
12002
12771
|
}
|
|
12772
|
+
}
|
|
12773
|
+
stats.durationMs = Date.now() - startTime;
|
|
12774
|
+
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
12775
|
+
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12776
|
+
}
|
|
12777
|
+
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12778
|
+
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12779
|
+
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
12780
|
+
this.saveBranchCommit(database, indexedCommit);
|
|
12781
|
+
this.saveIndexMetadata(configuredProviderInfo);
|
|
12782
|
+
this.indexCompatibility = { compatible: true };
|
|
12783
|
+
this.logger.recordIndexingEnd();
|
|
12784
|
+
this.logger.info("Indexing complete", {
|
|
12785
|
+
files: stats.totalFiles,
|
|
12786
|
+
indexed: stats.indexedChunks,
|
|
12787
|
+
existing: stats.existingChunks,
|
|
12788
|
+
removed: stats.removedChunks,
|
|
12789
|
+
failed: stats.failedChunks,
|
|
12790
|
+
tokens: stats.tokensUsed,
|
|
12791
|
+
durationMs: stats.durationMs
|
|
12003
12792
|
});
|
|
12004
|
-
|
|
12005
|
-
|
|
12006
|
-
if (scopedRoots) {
|
|
12007
|
-
this.saveScopedFailedBatches(coalesceFailedBatches(failedBatchesForCurrentRun), scopedRoots);
|
|
12008
|
-
} else {
|
|
12009
|
-
this.saveFailedBatches(coalesceFailedBatches(failedBatchesForCurrentRun));
|
|
12010
|
-
}
|
|
12011
|
-
onProgress?.({
|
|
12012
|
-
phase: "storing",
|
|
12013
|
-
filesProcessed: files.length,
|
|
12014
|
-
totalFiles: files.length,
|
|
12015
|
-
chunksProcessed: stats.indexedChunks,
|
|
12016
|
-
totalChunks: pendingChunks.length
|
|
12017
|
-
});
|
|
12018
|
-
const branchChunkIds = Array.from(currentChunkIds).filter(
|
|
12019
|
-
(chunkId) => {
|
|
12020
|
-
const isNewlyFailed = failedChunkIds.has(chunkId) && !retryableChunksWithExistingData.has(chunkId);
|
|
12021
|
-
const isForcedFailed = forceScopedReembed && failedForcedChunkIds.has(chunkId);
|
|
12022
|
-
return !isNewlyFailed && !isForcedFailed;
|
|
12793
|
+
if (stats.failedChunks > 0) {
|
|
12794
|
+
stats.failedBatchesPath = this.failedBatchesPath;
|
|
12023
12795
|
}
|
|
12024
|
-
|
|
12025
|
-
|
|
12026
|
-
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12030
|
-
|
|
12031
|
-
|
|
12032
|
-
|
|
12033
|
-
|
|
12034
|
-
|
|
12035
|
-
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
}
|
|
12043
|
-
if (this.config.indexing.autoGc && stats.removedChunks > 0) {
|
|
12044
|
-
const gcReset = await this.maybeRunOrphanGc();
|
|
12045
|
-
if (gcReset) {
|
|
12046
|
-
stats.durationMs = Date.now() - startTime;
|
|
12047
|
-
stats.warning = gcReset.warning;
|
|
12048
|
-
stats.resetCorruptedIndex = true;
|
|
12049
|
-
this.logger.recordIndexingEnd();
|
|
12050
|
-
this.logger.warn("Indexing ended after resetting corrupted local index during automatic GC", {
|
|
12051
|
-
files: stats.totalFiles,
|
|
12052
|
-
indexed: stats.indexedChunks,
|
|
12053
|
-
existing: stats.existingChunks,
|
|
12054
|
-
removed: stats.removedChunks,
|
|
12055
|
-
failed: stats.failedChunks,
|
|
12056
|
-
tokens: stats.tokensUsed,
|
|
12057
|
-
durationMs: stats.durationMs
|
|
12058
|
-
});
|
|
12059
|
-
return stats;
|
|
12796
|
+
onProgress?.({
|
|
12797
|
+
phase: "complete",
|
|
12798
|
+
filesProcessed: files.length,
|
|
12799
|
+
totalFiles: files.length,
|
|
12800
|
+
chunksProcessed: stats.indexedChunks,
|
|
12801
|
+
totalChunks: stats.totalChunks
|
|
12802
|
+
});
|
|
12803
|
+
return stats;
|
|
12804
|
+
} catch (error) {
|
|
12805
|
+
failedProcessing.state.writer.cleanup();
|
|
12806
|
+
if (writeTransactionActive) {
|
|
12807
|
+
try {
|
|
12808
|
+
database.rollbackWriteTransaction();
|
|
12809
|
+
} catch (rollbackError) {
|
|
12810
|
+
this.logger.error("Failed to roll back indexing database transaction", {
|
|
12811
|
+
error: getErrorMessage4(rollbackError)
|
|
12812
|
+
});
|
|
12813
|
+
}
|
|
12060
12814
|
}
|
|
12815
|
+
throw error;
|
|
12061
12816
|
}
|
|
12062
|
-
stats.durationMs = Date.now() - startTime;
|
|
12063
|
-
if (forceScopedReembed && failedForcedChunkIds.size === 0) {
|
|
12064
|
-
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12065
|
-
}
|
|
12066
|
-
database.setMetadata(swiftParserMetadataKey, SWIFT_PARSER_VERSION);
|
|
12067
|
-
database.setMetadata(metalParserMetadataKey, METAL_PARSER_VERSION);
|
|
12068
|
-
database.setMetadata(symbolExtractorMetadataKey, SYMBOL_EXTRACTOR_VERSION);
|
|
12069
|
-
this.saveBranchCommit(database, indexedCommit);
|
|
12070
|
-
this.saveIndexMetadata(configuredProviderInfo);
|
|
12071
|
-
this.indexCompatibility = { compatible: true };
|
|
12072
|
-
this.logger.recordIndexingEnd();
|
|
12073
|
-
this.logger.info("Indexing complete", {
|
|
12074
|
-
files: stats.totalFiles,
|
|
12075
|
-
indexed: stats.indexedChunks,
|
|
12076
|
-
existing: stats.existingChunks,
|
|
12077
|
-
removed: stats.removedChunks,
|
|
12078
|
-
failed: stats.failedChunks,
|
|
12079
|
-
tokens: stats.tokensUsed,
|
|
12080
|
-
durationMs: stats.durationMs
|
|
12081
|
-
});
|
|
12082
|
-
if (stats.failedChunks > 0) {
|
|
12083
|
-
stats.failedBatchesPath = this.failedBatchesPath;
|
|
12084
|
-
}
|
|
12085
|
-
onProgress?.({
|
|
12086
|
-
phase: "complete",
|
|
12087
|
-
filesProcessed: files.length,
|
|
12088
|
-
totalFiles: files.length,
|
|
12089
|
-
chunksProcessed: stats.indexedChunks,
|
|
12090
|
-
totalChunks: pendingChunks.length
|
|
12091
|
-
});
|
|
12092
|
-
return stats;
|
|
12093
12817
|
}
|
|
12094
12818
|
async getQueryEmbedding(query, provider) {
|
|
12095
12819
|
const now2 = Date.now();
|
|
@@ -12327,10 +13051,31 @@ var Indexer = class _Indexer {
|
|
|
12327
13051
|
const baseFiltered = tiered.filter(
|
|
12328
13052
|
(r) => matchesSearchFilters(r, options, this.config.search.minScore, this.projectRoot)
|
|
12329
13053
|
);
|
|
12330
|
-
|
|
13054
|
+
let communityRanked = baseFiltered;
|
|
13055
|
+
if (this.config.search.communityBoost > 0) {
|
|
13056
|
+
try {
|
|
13057
|
+
const sameCommunityCandidateIds = resolveSameCommunityCandidateIds(
|
|
13058
|
+
query,
|
|
13059
|
+
baseFiltered,
|
|
13060
|
+
database,
|
|
13061
|
+
this.getBranchCatalogKeys()
|
|
13062
|
+
);
|
|
13063
|
+
communityRanked = applyCommunityBoost(
|
|
13064
|
+
baseFiltered,
|
|
13065
|
+
sameCommunityCandidateIds,
|
|
13066
|
+
this.config.search.communityBoost
|
|
13067
|
+
);
|
|
13068
|
+
} catch (error) {
|
|
13069
|
+
this.logger.search("debug", "Community-aware ranking unavailable; using existing ranking", {
|
|
13070
|
+
query,
|
|
13071
|
+
error: getErrorMessage4(error)
|
|
13072
|
+
});
|
|
13073
|
+
}
|
|
13074
|
+
}
|
|
13075
|
+
const implementationOnly = communityRanked.filter(
|
|
12331
13076
|
(r) => isLikelyImplementationPath2(r.metadata.filePath) && isImplementationChunkType(r.metadata.chunkType)
|
|
12332
13077
|
);
|
|
12333
|
-
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly :
|
|
13078
|
+
const filtered = (sourceIntent && hasCodeHints && implementationOnly.length > 0 ? implementationOnly : communityRanked).slice(0, maxResults);
|
|
12334
13079
|
const identifierFallback = !options?.definitionIntent && filtered.length === 0 && identifierHints.length > 0 ? buildSymbolDefinitionLane(query, database, branchChunkIds, branchSymbolIds, maxResults, union, true).filter((r) => matchesSearchFilters(r, options, this.config.search.minScore, this.projectRoot)).slice(0, maxResults) : [];
|
|
12335
13080
|
const finalResults = filtered.length > 0 ? filtered : identifierFallback;
|
|
12336
13081
|
const totalSearchMs = performance2.now() - searchStartTime;
|
|
@@ -12547,7 +13292,7 @@ var Indexer = class _Indexer {
|
|
|
12547
13292
|
this.saveFileHashCache();
|
|
12548
13293
|
database.clearAllIndexedData();
|
|
12549
13294
|
this.deleteBranchCommitMetadata(database, clearedBranchKeys2);
|
|
12550
|
-
this.
|
|
13295
|
+
this.clearFailedBatchState();
|
|
12551
13296
|
database.deleteMetadata("index.version");
|
|
12552
13297
|
database.deleteMetadata("index.pathStorageVersion");
|
|
12553
13298
|
database.deleteMetadata("index.embeddingProvider");
|
|
@@ -12615,7 +13360,7 @@ var Indexer = class _Indexer {
|
|
|
12615
13360
|
const missingChunkKeys = [];
|
|
12616
13361
|
const chunkKeysByRemovedFile = /* @__PURE__ */ new Map();
|
|
12617
13362
|
for (const [filePath, chunkKeys] of filePathsToChunkKeys) {
|
|
12618
|
-
if (!
|
|
13363
|
+
if (!existsSync11(this.toMaterializedFilePath(filePath))) {
|
|
12619
13364
|
chunkKeysByRemovedFile.set(filePath, chunkKeys);
|
|
12620
13365
|
for (const key of chunkKeys) {
|
|
12621
13366
|
missingChunkKeys.push(key);
|
|
@@ -12678,7 +13423,7 @@ var Indexer = class _Indexer {
|
|
|
12678
13423
|
gcOrphanSymbols: 0,
|
|
12679
13424
|
gcOrphanCallEdges: 0,
|
|
12680
13425
|
resetCorruptedIndex: true,
|
|
12681
|
-
warning: this.getCorruptedIndexWarning(
|
|
13426
|
+
warning: this.getCorruptedIndexWarning(path19.join(this.indexPath, "codebase.db"))
|
|
12682
13427
|
};
|
|
12683
13428
|
}
|
|
12684
13429
|
this.logger.recordGc(removedCount, gcOrphanChunks, gcOrphanEmbeddings);
|
|
@@ -12708,180 +13453,94 @@ var Indexer = class _Indexer {
|
|
|
12708
13453
|
const maxChunkTokens = getSafeEmbeddingChunkTokenLimit(configuredProviderInfo);
|
|
12709
13454
|
const providerRateLimits = this.getProviderRateLimits(configuredProviderInfo.provider);
|
|
12710
13455
|
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
12711
|
-
const
|
|
12712
|
-
|
|
12713
|
-
|
|
13456
|
+
const failedProcessing = this.prepareFailedBatchProcessing(roots, () => true);
|
|
13457
|
+
if (failedProcessing.latestById.size === 0) {
|
|
13458
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
12714
13459
|
return { succeeded: 0, failed: 0, remaining: 0 };
|
|
12715
13460
|
}
|
|
13461
|
+
const queue = new PQueue({ concurrency: 1 });
|
|
13462
|
+
const rateLimitState = { backoffMs: 0 };
|
|
12716
13463
|
let succeeded = 0;
|
|
12717
13464
|
let failed = 0;
|
|
12718
|
-
|
|
12719
|
-
|
|
12720
|
-
|
|
12721
|
-
|
|
12722
|
-
|
|
12723
|
-
|
|
12724
|
-
|
|
12725
|
-
const
|
|
12726
|
-
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
12730
|
-
);
|
|
12731
|
-
|
|
12732
|
-
|
|
12733
|
-
|
|
12734
|
-
|
|
12735
|
-
|
|
12736
|
-
|
|
12737
|
-
|
|
12738
|
-
|
|
12739
|
-
|
|
12740
|
-
|
|
12741
|
-
|
|
12742
|
-
|
|
12743
|
-
|
|
12744
|
-
|
|
12745
|
-
|
|
12746
|
-
|
|
12747
|
-
|
|
12748
|
-
|
|
12749
|
-
|
|
12750
|
-
}
|
|
12751
|
-
const vector = result.embeddings[idx];
|
|
12752
|
-
if (!vector) {
|
|
12753
|
-
throw new Error(`Embedding API returned too few vectors for chunk ${request.chunk.id}`);
|
|
12754
|
-
}
|
|
12755
|
-
const parts = embeddingPartsByChunk.get(request.chunk.id) ?? [];
|
|
12756
|
-
parts[request.partIndex] = {
|
|
12757
|
-
vector,
|
|
12758
|
-
tokenCount: request.tokenCount
|
|
12759
|
-
};
|
|
12760
|
-
embeddingPartsByChunk.set(request.chunk.id, parts);
|
|
12761
|
-
touchedChunkIds.add(request.chunk.id);
|
|
12762
|
-
});
|
|
12763
|
-
for (const chunkId of touchedChunkIds) {
|
|
12764
|
-
if (failedChunkIds.has(chunkId) || completedChunkIds.has(chunkId)) {
|
|
12765
|
-
continue;
|
|
12766
|
-
}
|
|
12767
|
-
const chunk = batchChunksById.get(chunkId);
|
|
12768
|
-
if (!chunk) {
|
|
12769
|
-
continue;
|
|
12770
|
-
}
|
|
12771
|
-
const parts = embeddingPartsByChunk.get(chunk.id) ?? [];
|
|
12772
|
-
if (!hasAllEmbeddingParts(parts, chunk.texts.length)) {
|
|
12773
|
-
continue;
|
|
12774
|
-
}
|
|
12775
|
-
const orderedParts = parts;
|
|
12776
|
-
pooledResults.push({
|
|
12777
|
-
chunk,
|
|
12778
|
-
vector: poolEmbeddingVectors(
|
|
12779
|
-
orderedParts.map((part) => part.vector),
|
|
12780
|
-
orderedParts.map((part) => part.tokenCount)
|
|
12781
|
-
)
|
|
12782
|
-
});
|
|
12783
|
-
}
|
|
12784
|
-
this.logger.recordEmbeddingApiCall(result.totalTokensUsed);
|
|
12785
|
-
} catch (error) {
|
|
12786
|
-
const failureMessage = String(error);
|
|
12787
|
-
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
12788
|
-
const failedChunks = getUniquePendingChunksFromRequests(requestBatch).filter((chunk) => !completedChunkIds.has(chunk.id) && !failedChunkIds.has(chunk.id));
|
|
12789
|
-
for (const chunk of failedChunks) {
|
|
12790
|
-
failedChunkIds.add(chunk.id);
|
|
12791
|
-
embeddingPartsByChunk.delete(chunk.id);
|
|
12792
|
-
failedChunksForBatch.set(chunk.id, {
|
|
12793
|
-
chunks: [chunk],
|
|
12794
|
-
attemptCount: batch.attemptCount + 1,
|
|
12795
|
-
lastAttempt: failureTimestamp,
|
|
12796
|
-
error: failureMessage
|
|
12797
|
-
});
|
|
12798
|
-
}
|
|
12799
|
-
failed += failedChunks.length;
|
|
12800
|
-
this.logger.recordEmbeddingError();
|
|
12801
|
-
}
|
|
12802
|
-
}
|
|
12803
|
-
const successfulResults = pooledResults.filter(({ chunk }) => !failedChunkIds.has(chunk.id));
|
|
12804
|
-
const items = successfulResults.map(({ chunk, vector }) => ({
|
|
12805
|
-
id: chunk.id,
|
|
12806
|
-
vector,
|
|
12807
|
-
metadata: chunk.metadata
|
|
12808
|
-
}));
|
|
12809
|
-
if (items.length > 0) {
|
|
12810
|
-
store.addBatch(items);
|
|
12811
|
-
}
|
|
12812
|
-
if (successfulResults.length > 0) {
|
|
12813
|
-
try {
|
|
12814
|
-
database.upsertEmbeddingsBatch(
|
|
12815
|
-
successfulResults.map(({ chunk, vector }) => ({
|
|
12816
|
-
contentHash: chunk.contentHash,
|
|
12817
|
-
embedding: float32ArrayToBuffer(vector),
|
|
12818
|
-
chunkText: chunk.storageText,
|
|
12819
|
-
model: configuredProviderInfo.modelInfo.model
|
|
12820
|
-
}))
|
|
12821
|
-
);
|
|
12822
|
-
} catch (dbError) {
|
|
12823
|
-
this.rebuildVectorStoreExcludingChunkIds(
|
|
12824
|
-
store,
|
|
12825
|
-
database,
|
|
12826
|
-
successfulResults.map(({ chunk }) => chunk.id)
|
|
13465
|
+
try {
|
|
13466
|
+
const retryableChunks = this.iterateLatestFailedChunks(
|
|
13467
|
+
failedProcessing.latestById,
|
|
13468
|
+
roots,
|
|
13469
|
+
() => true,
|
|
13470
|
+
maxChunkTokens
|
|
13471
|
+
);
|
|
13472
|
+
for (const retryBatch of iterateOrderedFileBatches(
|
|
13473
|
+
retryableChunks,
|
|
13474
|
+
({ chunk }) => Buffer.byteLength(chunk.content, "utf-8"),
|
|
13475
|
+
this.fileBatchLimits
|
|
13476
|
+
)) {
|
|
13477
|
+
const chunks = retryBatch.map(({ chunk }) => chunk);
|
|
13478
|
+
const attemptCounts = new Map(retryBatch.map(({ chunk, attemptCount }) => [chunk.id, attemptCount]));
|
|
13479
|
+
const batchResult = await this.processPendingChunkBatch(chunks, {
|
|
13480
|
+
store,
|
|
13481
|
+
provider,
|
|
13482
|
+
invertedIndex,
|
|
13483
|
+
database,
|
|
13484
|
+
configuredProviderInfo,
|
|
13485
|
+
queue,
|
|
13486
|
+
providerRateLimits,
|
|
13487
|
+
rateLimitState,
|
|
13488
|
+
failedState: failedProcessing.state,
|
|
13489
|
+
attemptCounts,
|
|
13490
|
+
forceReembed: false,
|
|
13491
|
+
reuseCachedEmbeddings: false,
|
|
13492
|
+
incrementRepeatedFailures: false,
|
|
13493
|
+
onSucceeded: (succeededChunks) => {
|
|
13494
|
+
database.addChunksToBranchBatch(
|
|
13495
|
+
this.getBranchCatalogKey(),
|
|
13496
|
+
succeededChunks.map((chunk) => chunk.id)
|
|
12827
13497
|
);
|
|
12828
|
-
throw dbError;
|
|
12829
13498
|
}
|
|
12830
|
-
}
|
|
12831
|
-
|
|
12832
|
-
|
|
12833
|
-
invertedIndex.addChunk(chunk.id, chunk.content);
|
|
12834
|
-
completedChunkIds.add(chunk.id);
|
|
12835
|
-
embeddingPartsByChunk.delete(chunk.id);
|
|
12836
|
-
}
|
|
12837
|
-
database.addChunksToBranchBatch(
|
|
12838
|
-
this.getBranchCatalogKey(),
|
|
12839
|
-
successfulResults.map(({ chunk }) => chunk.id)
|
|
12840
|
-
);
|
|
12841
|
-
this.logger.recordChunksEmbedded(successfulResults.length);
|
|
12842
|
-
succeeded += successfulResults.length;
|
|
12843
|
-
stillFailing.push(...failedChunksForBatch.values());
|
|
12844
|
-
} catch (error) {
|
|
12845
|
-
const failureMessage = getErrorMessage4(error);
|
|
12846
|
-
const failureTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
12847
|
-
const unaccountedChunks = batch.chunks.filter(
|
|
12848
|
-
(chunk) => !failedChunksForBatch.has(chunk.id) && !completedChunkIds.has(chunk.id)
|
|
12849
|
-
);
|
|
12850
|
-
for (const chunk of unaccountedChunks) {
|
|
12851
|
-
failedChunksForBatch.set(chunk.id, {
|
|
12852
|
-
chunks: [chunk],
|
|
12853
|
-
attemptCount: batch.attemptCount + 1,
|
|
12854
|
-
lastAttempt: failureTimestamp,
|
|
12855
|
-
error: failureMessage
|
|
12856
|
-
});
|
|
12857
|
-
}
|
|
12858
|
-
failed += unaccountedChunks.length;
|
|
12859
|
-
this.logger.recordEmbeddingError();
|
|
12860
|
-
stillFailing.push(...coalesceFailedBatches(Array.from(failedChunksForBatch.values())));
|
|
13499
|
+
});
|
|
13500
|
+
succeeded += batchResult.indexedChunks;
|
|
13501
|
+
failed += batchResult.failedChunks;
|
|
12861
13502
|
}
|
|
13503
|
+
this.finalizeFailedBatchWriteState(failedProcessing.state);
|
|
13504
|
+
} catch (error) {
|
|
13505
|
+
failedProcessing.state.writer.cleanup();
|
|
13506
|
+
throw error;
|
|
12862
13507
|
}
|
|
12863
|
-
const
|
|
12864
|
-
if (roots) {
|
|
12865
|
-
this.saveFailedBatches([...retainedFailedBatches, ...persistedStillFailing]);
|
|
12866
|
-
} else {
|
|
12867
|
-
this.saveFailedBatches(persistedStillFailing);
|
|
12868
|
-
}
|
|
13508
|
+
const remaining = this.getFailedBatchesCount();
|
|
12869
13509
|
if (succeeded > 0) {
|
|
12870
13510
|
store.save();
|
|
12871
13511
|
this.saveInvertedIndex(invertedIndex);
|
|
12872
13512
|
}
|
|
12873
|
-
if (roots && succeeded > 0 &&
|
|
13513
|
+
if (roots && succeeded > 0 && remaining === 0 && this.hasProjectForceReembedPending()) {
|
|
12874
13514
|
database.deleteMetadata(this.getProjectForceReembedMetadataKey());
|
|
12875
13515
|
this.saveIndexMetadata(configuredProviderInfo);
|
|
12876
13516
|
this.indexCompatibility = { compatible: true };
|
|
12877
13517
|
}
|
|
12878
|
-
return { succeeded, failed, remaining
|
|
13518
|
+
return { succeeded, failed, remaining };
|
|
12879
13519
|
}
|
|
12880
13520
|
getFailedBatchesCount() {
|
|
12881
|
-
|
|
12882
|
-
|
|
13521
|
+
const roots = this.config.scope === "global" ? this.getScopedRoots() : null;
|
|
13522
|
+
const latestById = /* @__PURE__ */ new Map();
|
|
13523
|
+
for (const batch of this.loadSerializedFailedBatches()) {
|
|
13524
|
+
for (const rawChunk of batch.chunks) {
|
|
13525
|
+
const filePath = getPendingChunkFilePath(rawChunk);
|
|
13526
|
+
if (roots && (filePath === null || !this.isFileInCurrentScope(filePath, roots))) {
|
|
13527
|
+
continue;
|
|
13528
|
+
}
|
|
13529
|
+
const chunkId = getPendingChunkId(rawChunk);
|
|
13530
|
+
if (!chunkId) {
|
|
13531
|
+
continue;
|
|
13532
|
+
}
|
|
13533
|
+
const existing = latestById.get(chunkId);
|
|
13534
|
+
if (!existing || batch.attemptCount >= existing.attemptCount) {
|
|
13535
|
+
latestById.set(chunkId, {
|
|
13536
|
+
attemptCount: batch.attemptCount,
|
|
13537
|
+
error: batch.error,
|
|
13538
|
+
lastAttempt: batch.lastAttempt
|
|
13539
|
+
});
|
|
13540
|
+
}
|
|
13541
|
+
}
|
|
12883
13542
|
}
|
|
12884
|
-
return
|
|
13543
|
+
return new Set(Array.from(latestById.values(), getFailedBatchGroupKey)).size;
|
|
12885
13544
|
}
|
|
12886
13545
|
getCurrentBranch() {
|
|
12887
13546
|
return this.currentBranch;
|
|
@@ -13069,9 +13728,9 @@ var Indexer = class _Indexer {
|
|
|
13069
13728
|
this.requireReadableComponents(readIssues, "database");
|
|
13070
13729
|
let shortest = [];
|
|
13071
13730
|
for (const branchKey of this.getBranchCatalogKeys()) {
|
|
13072
|
-
const
|
|
13073
|
-
if (
|
|
13074
|
-
shortest =
|
|
13731
|
+
const path21 = database.findShortestPath(fromName, toName, branchKey, maxDepth);
|
|
13732
|
+
if (path21.length > 0 && (shortest.length === 0 || path21.length < shortest.length)) {
|
|
13733
|
+
shortest = path21;
|
|
13075
13734
|
}
|
|
13076
13735
|
}
|
|
13077
13736
|
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
@@ -13119,13 +13778,13 @@ var Indexer = class _Indexer {
|
|
|
13119
13778
|
}
|
|
13120
13779
|
}
|
|
13121
13780
|
if (!found) continue;
|
|
13122
|
-
const
|
|
13781
|
+
const path21 = [];
|
|
13123
13782
|
let currentSymbolId = toSymbolId;
|
|
13124
13783
|
while (true) {
|
|
13125
13784
|
const symbol = symbolsById.get(currentSymbolId);
|
|
13126
13785
|
if (!symbol) break;
|
|
13127
13786
|
const parent = parentBySymbolId.get(currentSymbolId);
|
|
13128
|
-
|
|
13787
|
+
path21.push({
|
|
13129
13788
|
symbolId: symbol.id,
|
|
13130
13789
|
symbolName: symbol.name,
|
|
13131
13790
|
filePath: symbol.filePath,
|
|
@@ -13135,9 +13794,9 @@ var Indexer = class _Indexer {
|
|
|
13135
13794
|
if (!parent) break;
|
|
13136
13795
|
currentSymbolId = parent.parentId;
|
|
13137
13796
|
}
|
|
13138
|
-
|
|
13139
|
-
if (
|
|
13140
|
-
shortest =
|
|
13797
|
+
path21.reverse();
|
|
13798
|
+
if (path21.length > 0 && (shortest.length === 0 || path21.length < shortest.length)) {
|
|
13799
|
+
shortest = path21;
|
|
13141
13800
|
}
|
|
13142
13801
|
}
|
|
13143
13802
|
return shortest.map((hop) => this.resolveFilePathRecord(hop));
|
|
@@ -13156,13 +13815,13 @@ var Indexer = class _Indexer {
|
|
|
13156
13815
|
async getSymbolsForBranch(branch) {
|
|
13157
13816
|
const { database, readIssues } = await this.ensureInitialized();
|
|
13158
13817
|
this.requireReadableComponents(readIssues, "database");
|
|
13159
|
-
const resolvedBranch =
|
|
13818
|
+
const resolvedBranch = this.resolveBranchCatalogKey(branch);
|
|
13160
13819
|
return database.getSymbolsForBranch(resolvedBranch).map((symbol) => this.resolveFilePathRecord(symbol));
|
|
13161
13820
|
}
|
|
13162
13821
|
async getSymbolsForFiles(filePaths, branch) {
|
|
13163
13822
|
const { database, readIssues } = await this.ensureInitialized();
|
|
13164
13823
|
this.requireReadableComponents(readIssues, "database");
|
|
13165
|
-
const resolvedBranch =
|
|
13824
|
+
const resolvedBranch = this.resolveBranchCatalogKey(branch);
|
|
13166
13825
|
const storedFilePaths = filePaths.map((filePath) => this.toStoredFilePath(filePath));
|
|
13167
13826
|
return database.getSymbolsForFiles(storedFilePaths, resolvedBranch).map((symbol) => this.resolveFilePathRecord(symbol));
|
|
13168
13827
|
}
|
|
@@ -13175,13 +13834,26 @@ var Indexer = class _Indexer {
|
|
|
13175
13834
|
async detectCommunities(branch, symbolIds) {
|
|
13176
13835
|
const { database, readIssues } = await this.ensureInitialized();
|
|
13177
13836
|
this.requireReadableComponents(readIssues, "database");
|
|
13178
|
-
const resolvedBranch =
|
|
13837
|
+
const resolvedBranch = this.resolveBranchCatalogKey(branch);
|
|
13179
13838
|
return database.detectCommunities(resolvedBranch, symbolIds).map((entry) => this.resolveFilePathRecord(entry));
|
|
13180
13839
|
}
|
|
13840
|
+
async detectCommunityCouplings(branch) {
|
|
13841
|
+
const { database, readIssues } = await this.ensureInitialized();
|
|
13842
|
+
this.requireReadableComponents(readIssues, "database");
|
|
13843
|
+
const resolvedBranch = this.resolveBranchCatalogKey(branch);
|
|
13844
|
+
return database.detectCommunityCouplings(resolvedBranch).map((entry) => ({
|
|
13845
|
+
...entry,
|
|
13846
|
+
relationships: (entry.relationships ?? entry.representativeRelationships ?? []).map((relationship) => ({
|
|
13847
|
+
...relationship,
|
|
13848
|
+
fromFilePath: this.resolveStoredFilePath(relationship.fromFilePath),
|
|
13849
|
+
toFilePath: this.resolveStoredFilePath(relationship.toFilePath)
|
|
13850
|
+
}))
|
|
13851
|
+
}));
|
|
13852
|
+
}
|
|
13181
13853
|
async computeCentrality(branch) {
|
|
13182
13854
|
const { database, readIssues } = await this.ensureInitialized();
|
|
13183
13855
|
this.requireReadableComponents(readIssues, "database");
|
|
13184
|
-
const resolvedBranch =
|
|
13856
|
+
const resolvedBranch = this.resolveBranchCatalogKey(branch);
|
|
13185
13857
|
return database.computeCentrality(resolvedBranch).map((entry) => this.resolveFilePathRecord(entry));
|
|
13186
13858
|
}
|
|
13187
13859
|
async getPrImpact(opts, onPreparationProgress) {
|
|
@@ -13275,7 +13947,7 @@ var Indexer = class _Indexer {
|
|
|
13275
13947
|
);
|
|
13276
13948
|
}
|
|
13277
13949
|
}
|
|
13278
|
-
const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(
|
|
13950
|
+
const toStoredChangedFiles = (filePaths) => filePaths.map((filePath) => this.toStoredFilePath(path19.resolve(this.projectRoot, filePath)));
|
|
13279
13951
|
const storedChangedFiles = toStoredChangedFiles(changedFiles);
|
|
13280
13952
|
const directSymbols = database.getSymbolsForFiles(storedChangedFiles, branchKey);
|
|
13281
13953
|
const directIds = directSymbols.map((s) => s.id);
|
|
@@ -13424,12 +14096,12 @@ var Indexer = class _Indexer {
|
|
|
13424
14096
|
if (meta.filePath) filePaths.add(meta.filePath);
|
|
13425
14097
|
}
|
|
13426
14098
|
const directory = options?.directory?.replace(/\/$/, "");
|
|
13427
|
-
const absoluteDirectoryFilter = directory ?
|
|
14099
|
+
const absoluteDirectoryFilter = directory ? path19.resolve(this.projectRoot, directory) : void 0;
|
|
13428
14100
|
for (const filePath of filePaths) {
|
|
13429
14101
|
if (directory) {
|
|
13430
14102
|
const absoluteFilePath = this.resolveStoredFilePath(filePath);
|
|
13431
14103
|
const matchesRelative = filePath === directory || filePath.startsWith(directory + "/");
|
|
13432
|
-
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter +
|
|
14104
|
+
const matchesProjectRelative = absoluteDirectoryFilter !== void 0 && (absoluteFilePath === absoluteDirectoryFilter || absoluteFilePath.startsWith(absoluteDirectoryFilter + path19.sep));
|
|
13433
14105
|
if (!matchesRelative && !matchesProjectRelative) {
|
|
13434
14106
|
continue;
|
|
13435
14107
|
}
|
|
@@ -13589,7 +14261,7 @@ function trimOrUndefined(value) {
|
|
|
13589
14261
|
return normalized || void 0;
|
|
13590
14262
|
}
|
|
13591
14263
|
function normalizeCallGraphPath(value) {
|
|
13592
|
-
let normalized =
|
|
14264
|
+
let normalized = path20.posix.normalize(value.trim().replaceAll("\\", "/"));
|
|
13593
14265
|
if (normalized.startsWith("./")) {
|
|
13594
14266
|
normalized = normalized.slice(2);
|
|
13595
14267
|
}
|
|
@@ -13773,12 +14445,12 @@ async function getCallGraphPath(projectRoot3, host, from, to, maxDepth, fromFile
|
|
|
13773
14445
|
if (fromResolution.status !== "resolved" || toResolution.status !== "resolved") {
|
|
13774
14446
|
return { from: fromResolution, to: toResolution, path: [] };
|
|
13775
14447
|
}
|
|
13776
|
-
const
|
|
14448
|
+
const path21 = await indexer.findCallPathBySymbolIds(
|
|
13777
14449
|
fromResolution.symbolId,
|
|
13778
14450
|
toResolution.symbolId,
|
|
13779
14451
|
maxDepth
|
|
13780
14452
|
);
|
|
13781
|
-
return { from: fromResolution, to: toResolution, path:
|
|
14453
|
+
return { from: fromResolution, to: toResolution, path: path21 };
|
|
13782
14454
|
}
|
|
13783
14455
|
async function runIndexCodebase(projectRoot3, host, args, onProgress) {
|
|
13784
14456
|
const root = getProjectRoot(projectRoot3, host);
|
|
@@ -13854,6 +14526,34 @@ async function getPrImpact(projectRoot3, host, params) {
|
|
|
13854
14526
|
direction: params.direction
|
|
13855
14527
|
});
|
|
13856
14528
|
}
|
|
14529
|
+
async function getCodeCommunities(projectRoot3, host, params) {
|
|
14530
|
+
await ensureAutoIndexReadyForRetrieval(projectRoot3, host);
|
|
14531
|
+
const indexer = getIndexerForProject(projectRoot3, host);
|
|
14532
|
+
const [communities, centrality, couplings] = await Promise.all([
|
|
14533
|
+
indexer.detectCommunities(params.branch),
|
|
14534
|
+
indexer.computeCentrality(params.branch),
|
|
14535
|
+
indexer.detectCommunityCouplings(params.branch)
|
|
14536
|
+
]);
|
|
14537
|
+
return buildCodeCommunitiesResult(communities, centrality, couplings, {
|
|
14538
|
+
minSize: Math.max(CODE_COMMUNITIES_MIN_SIZE, Math.floor(params.minSize ?? CODE_COMMUNITIES_MIN_SIZE)),
|
|
14539
|
+
limit: Math.min(
|
|
14540
|
+
CODE_COMMUNITIES_MAX_LIMIT,
|
|
14541
|
+
Math.max(1, Math.floor(params.limit ?? CODE_COMMUNITIES_DEFAULT_LIMIT))
|
|
14542
|
+
),
|
|
14543
|
+
hubThreshold: Math.max(
|
|
14544
|
+
0,
|
|
14545
|
+
Math.floor(params.hubThreshold ?? CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD)
|
|
14546
|
+
),
|
|
14547
|
+
minCoupling: Math.max(
|
|
14548
|
+
CODE_COMMUNITIES_MIN_COUPLING,
|
|
14549
|
+
Math.floor(params.minCoupling ?? CODE_COMMUNITIES_MIN_COUPLING)
|
|
14550
|
+
),
|
|
14551
|
+
couplingLimit: Math.min(
|
|
14552
|
+
CODE_COMMUNITIES_MAX_COUPLING_LIMIT,
|
|
14553
|
+
Math.max(1, Math.floor(params.couplingLimit ?? CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT))
|
|
14554
|
+
)
|
|
14555
|
+
});
|
|
14556
|
+
}
|
|
13857
14557
|
async function getIndexMetrics(projectRoot3, host, args = {}) {
|
|
13858
14558
|
const root = getProjectRoot(projectRoot3, host);
|
|
13859
14559
|
const key = getIndexerCacheKey(root, host);
|
|
@@ -13945,10 +14645,10 @@ async function getIndexLogs(projectRoot3, host, args) {
|
|
|
13945
14645
|
function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
|
|
13946
14646
|
const root = getProjectRoot(projectRoot3, host);
|
|
13947
14647
|
const inputPath = knowledgeBasePath.trim();
|
|
13948
|
-
const normalizedPath =
|
|
13949
|
-
|
|
14648
|
+
const normalizedPath = path20.resolve(
|
|
14649
|
+
path20.isAbsolute(inputPath) ? inputPath : resolveKnowledgeBasePath(inputPath, root)
|
|
13950
14650
|
);
|
|
13951
|
-
if (!
|
|
14651
|
+
if (!existsSync12(normalizedPath)) {
|
|
13952
14652
|
return `Error: Directory does not exist: ${normalizedPath}`;
|
|
13953
14653
|
}
|
|
13954
14654
|
let realPath;
|
|
@@ -13982,7 +14682,7 @@ function addKnowledgeBase(projectRoot3, host, knowledgeBasePath) {
|
|
|
13982
14682
|
}
|
|
13983
14683
|
}
|
|
13984
14684
|
for (const dotDir of sensitiveDotDirs) {
|
|
13985
|
-
const sensitiveDir =
|
|
14685
|
+
const sensitiveDir = path20.join(homeDir, dotDir);
|
|
13986
14686
|
if (sensitiveDir && (realPath === sensitiveDir || realPath.startsWith(`${sensitiveDir}/`))) {
|
|
13987
14687
|
return `Error: Adding sensitive directory as knowledge base is not allowed: ${normalizedPath}`;
|
|
13988
14688
|
}
|
|
@@ -14028,7 +14728,7 @@ function listKnowledgeBases(projectRoot3, host) {
|
|
|
14028
14728
|
for (let i = 0; i < knowledgeBases.length; i++) {
|
|
14029
14729
|
const kb = knowledgeBases[i];
|
|
14030
14730
|
const resolvedPath = resolveKnowledgeBasePath(kb, root);
|
|
14031
|
-
const exists =
|
|
14731
|
+
const exists = existsSync12(resolvedPath);
|
|
14032
14732
|
result += `[${i + 1}] ${kb}
|
|
14033
14733
|
`;
|
|
14034
14734
|
result += ` Resolved: ${resolvedPath}
|
|
@@ -14045,7 +14745,7 @@ function listKnowledgeBases(projectRoot3, host) {
|
|
|
14045
14745
|
}
|
|
14046
14746
|
result += "\n";
|
|
14047
14747
|
}
|
|
14048
|
-
const hasHostConfig =
|
|
14748
|
+
const hasHostConfig = existsSync12(path20.join(root, getHostProjectConfigRelativePath(host)));
|
|
14049
14749
|
if (hasHostConfig) {
|
|
14050
14750
|
result += `
|
|
14051
14751
|
Config sources: 1 file(s).`;
|
|
@@ -14078,117 +14778,6 @@ Run /index to rebuild the index without the removed knowledge base.`;
|
|
|
14078
14778
|
return result;
|
|
14079
14779
|
}
|
|
14080
14780
|
|
|
14081
|
-
// src/tools/symbol-inference.ts
|
|
14082
|
-
var IDENTIFIER_RE = /[A-Za-z_$][A-Za-z0-9_$]*/g;
|
|
14083
|
-
var QUOTED_BACKTICK_RE = /`([^`]+)`/g;
|
|
14084
|
-
var QUOTED_SINGLE_RE = /'([^'\\]+)'/g;
|
|
14085
|
-
var QUOTED_DOUBLE_RE = /"([^"]+)"/g;
|
|
14086
|
-
var SYMBOL_LIKE_RE = /^(?:[A-Za-z_$][A-Za-z0-9_$]*)$/;
|
|
14087
|
-
var CAMEL_CASE_RE = /^[a-z_][A-Za-z0-9_$]*[A-Z][A-Za-z0-9_$]*$/;
|
|
14088
|
-
var PASCAL_CASE_RE = /^[A-Z][A-Za-z0-9_$]*$/;
|
|
14089
|
-
var SNAKE_CASE_RE = /^[a-z][a-z0-9_]*_[a-z0-9_]+$/;
|
|
14090
|
-
var DEFINITION_INTENT_RE = /\b(where|defined|definition|define|declaration|symbol|function|method|class|interface|type)\b/i;
|
|
14091
|
-
var STOP_WORDS = /* @__PURE__ */ new Set([
|
|
14092
|
-
"a",
|
|
14093
|
-
"an",
|
|
14094
|
-
"and",
|
|
14095
|
-
"are",
|
|
14096
|
-
"at",
|
|
14097
|
-
"for",
|
|
14098
|
-
"find",
|
|
14099
|
-
"how",
|
|
14100
|
-
"i",
|
|
14101
|
-
"in",
|
|
14102
|
-
"is",
|
|
14103
|
-
"it",
|
|
14104
|
-
"of",
|
|
14105
|
-
"on",
|
|
14106
|
-
"that",
|
|
14107
|
-
"the",
|
|
14108
|
-
"definition",
|
|
14109
|
-
"show",
|
|
14110
|
-
"to",
|
|
14111
|
-
"where",
|
|
14112
|
-
"which",
|
|
14113
|
-
"what",
|
|
14114
|
-
"you",
|
|
14115
|
-
"your",
|
|
14116
|
-
"with"
|
|
14117
|
-
]);
|
|
14118
|
-
function stripCallSuffix(token) {
|
|
14119
|
-
return token.replace(/\(\s*\)$/, "");
|
|
14120
|
-
}
|
|
14121
|
-
function isLikelySymbolName(token) {
|
|
14122
|
-
if (!SYMBOL_LIKE_RE.test(token)) {
|
|
14123
|
-
return false;
|
|
14124
|
-
}
|
|
14125
|
-
if (STOP_WORDS.has(token.toLowerCase())) {
|
|
14126
|
-
return false;
|
|
14127
|
-
}
|
|
14128
|
-
return CAMEL_CASE_RE.test(token) || PASCAL_CASE_RE.test(token) || SNAKE_CASE_RE.test(token);
|
|
14129
|
-
}
|
|
14130
|
-
function extractQuotedIdentifiers(query) {
|
|
14131
|
-
const identifiers = /* @__PURE__ */ new Set();
|
|
14132
|
-
for (const match of query.matchAll(QUOTED_BACKTICK_RE)) {
|
|
14133
|
-
const candidate = stripCallSuffix(match[1].trim());
|
|
14134
|
-
if (candidate && isLikelySymbolName(candidate)) {
|
|
14135
|
-
identifiers.add(candidate);
|
|
14136
|
-
}
|
|
14137
|
-
}
|
|
14138
|
-
for (const match of query.matchAll(QUOTED_SINGLE_RE)) {
|
|
14139
|
-
const candidate = stripCallSuffix(match[1].trim());
|
|
14140
|
-
if (candidate && isLikelySymbolName(candidate)) {
|
|
14141
|
-
identifiers.add(candidate);
|
|
14142
|
-
}
|
|
14143
|
-
}
|
|
14144
|
-
for (const match of query.matchAll(QUOTED_DOUBLE_RE)) {
|
|
14145
|
-
const candidate = stripCallSuffix(match[1].trim());
|
|
14146
|
-
if (candidate && isLikelySymbolName(candidate)) {
|
|
14147
|
-
identifiers.add(candidate);
|
|
14148
|
-
}
|
|
14149
|
-
}
|
|
14150
|
-
return [...identifiers];
|
|
14151
|
-
}
|
|
14152
|
-
function extractBareIdentifiers(query) {
|
|
14153
|
-
const unquoted = query.replace(QUOTED_BACKTICK_RE, " ").replace(QUOTED_SINGLE_RE, " ").replace(QUOTED_DOUBLE_RE, " ");
|
|
14154
|
-
const identifiers = /* @__PURE__ */ new Set();
|
|
14155
|
-
for (const match of unquoted.matchAll(IDENTIFIER_RE)) {
|
|
14156
|
-
const candidate = stripCallSuffix(match[0]);
|
|
14157
|
-
if (isLikelySymbolName(candidate)) {
|
|
14158
|
-
identifiers.add(candidate);
|
|
14159
|
-
}
|
|
14160
|
-
}
|
|
14161
|
-
return [...identifiers];
|
|
14162
|
-
}
|
|
14163
|
-
function isSingleMeaningfulToken(query, symbol) {
|
|
14164
|
-
const tokens = query.replace(/[`'"()]/g, " ").split(/[^A-Za-z0-9_$]+/).map((token) => token.trim().toLowerCase()).filter((token) => token.length > 0).filter((token) => !STOP_WORDS.has(token));
|
|
14165
|
-
return tokens.length === 1 && tokens[0] === symbol.toLowerCase();
|
|
14166
|
-
}
|
|
14167
|
-
function inferExactSymbolFromQuery(query) {
|
|
14168
|
-
if (analyzeQueryIntent(query).explicitArtifactIntent) {
|
|
14169
|
-
return void 0;
|
|
14170
|
-
}
|
|
14171
|
-
const quoted = extractQuotedIdentifiers(query);
|
|
14172
|
-
if (quoted.length === 1) {
|
|
14173
|
-
return quoted[0];
|
|
14174
|
-
}
|
|
14175
|
-
if (quoted.length > 1) {
|
|
14176
|
-
return void 0;
|
|
14177
|
-
}
|
|
14178
|
-
const candidates = extractBareIdentifiers(query);
|
|
14179
|
-
if (candidates.length !== 1) {
|
|
14180
|
-
return void 0;
|
|
14181
|
-
}
|
|
14182
|
-
const candidate = candidates[0];
|
|
14183
|
-
if (DEFINITION_INTENT_RE.test(query)) {
|
|
14184
|
-
return candidate;
|
|
14185
|
-
}
|
|
14186
|
-
if (isSingleMeaningfulToken(query, candidate)) {
|
|
14187
|
-
return candidate;
|
|
14188
|
-
}
|
|
14189
|
-
return void 0;
|
|
14190
|
-
}
|
|
14191
|
-
|
|
14192
14781
|
// src/tools/context-search.ts
|
|
14193
14782
|
var MIN_CONTEXT_RESULT_LIMIT = 1;
|
|
14194
14783
|
var MAX_CONTEXT_RESULT_LIMIT = 100;
|
|
@@ -14539,7 +15128,7 @@ async function resolveCodebaseContextUnmeasured(projectRoot3, host, input) {
|
|
|
14539
15128
|
const directory = input.directory ?? void 0;
|
|
14540
15129
|
const tokenBudget = input.tokenBudget ?? void 0;
|
|
14541
15130
|
if (from && to) {
|
|
14542
|
-
const
|
|
15131
|
+
const path21 = await getCallGraphPath(
|
|
14543
15132
|
projectRoot3,
|
|
14544
15133
|
host,
|
|
14545
15134
|
from,
|
|
@@ -14548,25 +15137,25 @@ async function resolveCodebaseContextUnmeasured(projectRoot3, host, input) {
|
|
|
14548
15137
|
fromFilePath,
|
|
14549
15138
|
toFilePath
|
|
14550
15139
|
);
|
|
14551
|
-
const pathText = formatCallGraphPathResult(
|
|
14552
|
-
if (
|
|
15140
|
+
const pathText = formatCallGraphPathResult(path21);
|
|
15141
|
+
if (path21.path.length > 0) {
|
|
14553
15142
|
const fitted2 = fitTextToContextBudget(
|
|
14554
15143
|
pathText,
|
|
14555
15144
|
tokenBudget
|
|
14556
15145
|
);
|
|
14557
15146
|
return {
|
|
14558
15147
|
text: fitted2.text,
|
|
14559
|
-
details: fittedDetails("path", fitted2,
|
|
15148
|
+
details: fittedDetails("path", fitted2, path21.path.length)
|
|
14560
15149
|
};
|
|
14561
15150
|
}
|
|
14562
|
-
if (
|
|
15151
|
+
if (path21.from.status !== "resolved" || path21.to.status !== "resolved") {
|
|
14563
15152
|
const fitted2 = fitTextToContextBudget(pathText, tokenBudget);
|
|
14564
15153
|
return {
|
|
14565
15154
|
text: fitted2.text,
|
|
14566
15155
|
details: fittedDetails("path", fitted2, 0)
|
|
14567
15156
|
};
|
|
14568
15157
|
}
|
|
14569
|
-
const resolvedFrom =
|
|
15158
|
+
const resolvedFrom = path21.from;
|
|
14570
15159
|
const { callers } = await getCallGraphData(projectRoot3, host, {
|
|
14571
15160
|
name: to,
|
|
14572
15161
|
direction: "callers",
|
|
@@ -14686,6 +15275,7 @@ var TOOL_NAME = {
|
|
|
14686
15275
|
CALL_GRAPH: "call_graph",
|
|
14687
15276
|
CALL_GRAPH_PATH: "call_graph_path",
|
|
14688
15277
|
PR_IMPACT: "pr_impact",
|
|
15278
|
+
CODE_COMMUNITIES: "code_communities",
|
|
14689
15279
|
ADD_KNOWLEDGE_BASE: "add_knowledge_base",
|
|
14690
15280
|
LIST_KNOWLEDGE_BASES: "list_knowledge_bases",
|
|
14691
15281
|
REMOVE_KNOWLEDGE_BASE: "remove_knowledge_base",
|
|
@@ -14707,7 +15297,8 @@ var PORTABLE_TOOL_NAMES = [
|
|
|
14707
15297
|
TOOL_NAME.IMPLEMENTATION_LOOKUP,
|
|
14708
15298
|
TOOL_NAME.CALL_GRAPH,
|
|
14709
15299
|
TOOL_NAME.CALL_GRAPH_PATH,
|
|
14710
|
-
TOOL_NAME.PR_IMPACT
|
|
15300
|
+
TOOL_NAME.PR_IMPACT,
|
|
15301
|
+
TOOL_NAME.CODE_COMMUNITIES
|
|
14711
15302
|
];
|
|
14712
15303
|
var OPENCODE_TOOL_NAMES = [
|
|
14713
15304
|
TOOL_NAME.CODEBASE_CONTEXT,
|
|
@@ -14726,6 +15317,7 @@ var OPENCODE_TOOL_NAMES = [
|
|
|
14726
15317
|
TOOL_NAME.LIST_KNOWLEDGE_BASES,
|
|
14727
15318
|
TOOL_NAME.REMOVE_KNOWLEDGE_BASE,
|
|
14728
15319
|
TOOL_NAME.PR_IMPACT,
|
|
15320
|
+
TOOL_NAME.CODE_COMMUNITIES,
|
|
14729
15321
|
TOOL_NAME.INDEX_VISUALIZE
|
|
14730
15322
|
];
|
|
14731
15323
|
var PI_TOOL_NAMES = [
|
|
@@ -14742,6 +15334,7 @@ var PI_TOOL_NAMES = [
|
|
|
14742
15334
|
TOOL_NAME.CALL_GRAPH,
|
|
14743
15335
|
TOOL_NAME.CALL_GRAPH_PATH,
|
|
14744
15336
|
TOOL_NAME.PR_IMPACT,
|
|
15337
|
+
TOOL_NAME.CODE_COMMUNITIES,
|
|
14745
15338
|
TOOL_NAME.PI_KNOWLEDGE_BASE_LIST,
|
|
14746
15339
|
TOOL_NAME.PI_KNOWLEDGE_BASE_ADD,
|
|
14747
15340
|
TOOL_NAME.PI_KNOWLEDGE_BASE_REMOVE
|
|
@@ -15036,6 +15629,25 @@ Check index_status first when index readiness is unknown. For repository questio
|
|
|
15036
15629
|
return text2(formatPrImpact(result), result);
|
|
15037
15630
|
}
|
|
15038
15631
|
});
|
|
15632
|
+
pi.registerTool({
|
|
15633
|
+
name: TOOL_NAME.CODE_COMMUNITIES,
|
|
15634
|
+
label: "Code Communities",
|
|
15635
|
+
description: "Discover natural module boundaries and hub symbols using graph community detection. Clusters symbols by call-graph connectivity to reveal architecture.",
|
|
15636
|
+
parameters: Type2.Object({
|
|
15637
|
+
branch: Type2.Optional(Type2.String()),
|
|
15638
|
+
minSize: Type2.Optional(Type2.Integer({ minimum: CODE_COMMUNITIES_MIN_SIZE, default: CODE_COMMUNITIES_MIN_SIZE })),
|
|
15639
|
+
limit: Type2.Optional(Type2.Integer({ minimum: 1, maximum: CODE_COMMUNITIES_MAX_LIMIT, default: CODE_COMMUNITIES_DEFAULT_LIMIT })),
|
|
15640
|
+
hubThreshold: Type2.Optional(Type2.Integer({ minimum: 0, default: CODE_COMMUNITIES_DEFAULT_HUB_THRESHOLD })),
|
|
15641
|
+
minCoupling: Type2.Optional(Type2.Integer({ minimum: CODE_COMMUNITIES_MIN_COUPLING, default: CODE_COMMUNITIES_MIN_COUPLING })),
|
|
15642
|
+
couplingLimit: Type2.Optional(
|
|
15643
|
+
Type2.Integer({ minimum: 1, maximum: CODE_COMMUNITIES_MAX_COUPLING_LIMIT, default: CODE_COMMUNITIES_DEFAULT_COUPLING_LIMIT })
|
|
15644
|
+
)
|
|
15645
|
+
}),
|
|
15646
|
+
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
15647
|
+
const result = await getCodeCommunities(projectRoot2(ctx), HOST2, params);
|
|
15648
|
+
return text2(formatCodeCommunities(result), result);
|
|
15649
|
+
}
|
|
15650
|
+
});
|
|
15039
15651
|
pi.registerTool({
|
|
15040
15652
|
name: TOOL_NAME.PI_KNOWLEDGE_BASE_LIST,
|
|
15041
15653
|
label: "List Knowledge Bases",
|