@equationalapplications/core-llm-wiki 7.7.4 → 7.7.6
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/dist/{chunk-JV2OQ5CM.mjs → chunk-PBUZLSOM.mjs} +127 -8
- package/dist/chunk-PBUZLSOM.mjs.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +158 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +36 -6
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-CH_Kk34-.d.mts → testing-BIFNLUva.d.mts} +69 -0
- package/dist/{testing-CH_Kk34-.d.ts → testing-BIFNLUva.d.ts} +69 -0
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +125 -6
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-JV2OQ5CM.mjs.map +0 -1
|
@@ -432,7 +432,31 @@ var _SearchService = class _SearchService {
|
|
|
432
432
|
* auto-vacuum debt behind the TypeError in #64.
|
|
433
433
|
*/
|
|
434
434
|
this.syncChain = Promise.resolve();
|
|
435
|
-
|
|
435
|
+
/**
|
|
436
|
+
* Entities whose index may have drifted from SQLite: an incremental update
|
|
437
|
+
* failed part-way, or core wrote rows it could not index (upsertGraph runs in
|
|
438
|
+
* the host's transaction). Their next syncEntries rebuilds the entity in full.
|
|
439
|
+
* See spec 2026-09-28 §5.
|
|
440
|
+
*/
|
|
441
|
+
this.staleEntities = /* @__PURE__ */ new Set();
|
|
442
|
+
/**
|
|
443
|
+
* Per-entity count of markStale() calls. A rebuild clears an entity's stale
|
|
444
|
+
* flag only when the count it snapshotted before its read still holds
|
|
445
|
+
* afterwards: markStale() runs inside the host's still-open transaction, so
|
|
446
|
+
* a call landing mid-read belongs to rows the read cannot have seen, and
|
|
447
|
+
* its flag must outlive the turn (#233 review).
|
|
448
|
+
*/
|
|
449
|
+
this.staleEpochs = /* @__PURE__ */ new Map();
|
|
450
|
+
this.miniSearch = this.createMiniSearch();
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* A fresh index with the production options. clearAll() swaps one in because
|
|
454
|
+
* MiniSearch.removeAll() empties the index but leaves dirtCount (and its
|
|
455
|
+
* vacuum bookkeeping) at its old value, which would trip syncEntries'
|
|
456
|
+
* conditional vacuum early after a clear.
|
|
457
|
+
*/
|
|
458
|
+
createMiniSearch() {
|
|
459
|
+
return new MiniSearch({
|
|
436
460
|
fields: ["title", "body", "tags"],
|
|
437
461
|
storeFields: ["entity_id"],
|
|
438
462
|
// Vacuuming is driven explicitly at the end of each serialized rebuild
|
|
@@ -459,7 +483,19 @@ var _SearchService = class _SearchService {
|
|
|
459
483
|
const work = this.syncChain.then(async () => {
|
|
460
484
|
try {
|
|
461
485
|
try {
|
|
486
|
+
const epochsBefore = new Map(this.staleEpochs);
|
|
462
487
|
await this.rebuildIndex(entityId);
|
|
488
|
+
if (entityId) {
|
|
489
|
+
if ((this.staleEpochs.get(entityId) ?? 0) === (epochsBefore.get(entityId) ?? 0)) {
|
|
490
|
+
this.staleEntities.delete(entityId);
|
|
491
|
+
}
|
|
492
|
+
} else {
|
|
493
|
+
for (const id of [...this.staleEntities]) {
|
|
494
|
+
if ((this.staleEpochs.get(id) ?? 0) === (epochsBefore.get(id) ?? 0)) {
|
|
495
|
+
this.staleEntities.delete(id);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
463
499
|
await this.miniSearch.vacuum();
|
|
464
500
|
} finally {
|
|
465
501
|
this.evictCache(entityId);
|
|
@@ -471,6 +507,68 @@ var _SearchService = class _SearchService {
|
|
|
471
507
|
this.syncChain = work;
|
|
472
508
|
return work;
|
|
473
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Re-indexes only `ids` for `entityId`: drops each from the index, then
|
|
512
|
+
* re-adds the ones still live in SQLite, so soft-deleted or missing ids end
|
|
513
|
+
* up absent. Costs O(ids), where sync(entityId) costs O(entity) — callers that
|
|
514
|
+
* know what a write touched use this so chunked imports stay linear (#232).
|
|
515
|
+
*
|
|
516
|
+
* Serialized with sync() on the same chain and, like it, never rejects. An
|
|
517
|
+
* entity that is stale or has never been indexed gets a full rebuild instead
|
|
518
|
+
* — including on an empty `ids` list, which is a no-op only for an entity
|
|
519
|
+
* the index already tracks, and even then one that waits for rebuilds
|
|
520
|
+
* already queued on the chain.
|
|
521
|
+
*/
|
|
522
|
+
async syncEntries(entityId, ids) {
|
|
523
|
+
const uniqueIds = [...new Set(ids)];
|
|
524
|
+
if (uniqueIds.length === 0 && !this.staleEntities.has(entityId) && this.miniSearchEntryIdsByEntity.has(entityId)) {
|
|
525
|
+
return this.syncChain;
|
|
526
|
+
}
|
|
527
|
+
const work = this.syncChain.then(async () => {
|
|
528
|
+
try {
|
|
529
|
+
try {
|
|
530
|
+
const epochsBefore = new Map(this.staleEpochs);
|
|
531
|
+
const needsRebuild = () => this.staleEntities.has(entityId) || !this.miniSearchEntryIdsByEntity.has(entityId);
|
|
532
|
+
if (!needsRebuild()) {
|
|
533
|
+
const rows = await this.entryRepo.findMiniSearchRowsByIds(entityId, uniqueIds);
|
|
534
|
+
const tracked = this.miniSearchEntryIdsByEntity.get(entityId);
|
|
535
|
+
if (tracked && !this.staleEntities.has(entityId)) {
|
|
536
|
+
for (const id of uniqueIds) {
|
|
537
|
+
if (tracked.delete(id) && this.miniSearch.has(id)) this.miniSearch.discard(id);
|
|
538
|
+
}
|
|
539
|
+
const documents = rows.map((row) => this.normalizeMiniSearchRow(row));
|
|
540
|
+
if (documents.length > 0) this.miniSearch.addAll(documents);
|
|
541
|
+
for (const document of documents) tracked.add(document.id);
|
|
542
|
+
if (this.miniSearch.dirtCount > 0) {
|
|
543
|
+
await this.miniSearch.vacuum();
|
|
544
|
+
}
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
await this.rebuildIndex(entityId);
|
|
549
|
+
if ((this.staleEpochs.get(entityId) ?? 0) === (epochsBefore.get(entityId) ?? 0)) {
|
|
550
|
+
this.staleEntities.delete(entityId);
|
|
551
|
+
}
|
|
552
|
+
await this.miniSearch.vacuum();
|
|
553
|
+
} finally {
|
|
554
|
+
this.evictCache(entityId);
|
|
555
|
+
}
|
|
556
|
+
} catch (err) {
|
|
557
|
+
this.staleEntities.add(entityId);
|
|
558
|
+
console.warn(`[WikiMemory] search index incremental sync failed for ${entityId}:`, err);
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
this.syncChain = work;
|
|
562
|
+
return work;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Forces the entity's next syncEntries to rebuild it in full. For writes core
|
|
566
|
+
* cannot index itself, such as upsertGraph inside a host transaction.
|
|
567
|
+
*/
|
|
568
|
+
markStale(entityId) {
|
|
569
|
+
this.staleEntities.add(entityId);
|
|
570
|
+
this.staleEpochs.set(entityId, (this.staleEpochs.get(entityId) ?? 0) + 1);
|
|
571
|
+
}
|
|
474
572
|
/**
|
|
475
573
|
* Clears the parsed vector cache. Useful for mid-loop flush guarantees
|
|
476
574
|
* or memory pressure evictions.
|
|
@@ -487,8 +585,10 @@ var _SearchService = class _SearchService {
|
|
|
487
585
|
*/
|
|
488
586
|
clearAll() {
|
|
489
587
|
this.vectorCache.clear();
|
|
490
|
-
this.miniSearch.
|
|
588
|
+
this.miniSearch = this.createMiniSearch();
|
|
491
589
|
this.miniSearchEntryIdsByEntity.clear();
|
|
590
|
+
this.staleEntities.clear();
|
|
591
|
+
this.staleEpochs.clear();
|
|
492
592
|
}
|
|
493
593
|
/**
|
|
494
594
|
* Executes a keyword search against the active MiniSearch index.
|
|
@@ -499,7 +599,7 @@ var _SearchService = class _SearchService {
|
|
|
499
599
|
filter: (r) => entityIdSet.has(r.entity_id),
|
|
500
600
|
combineWith: "OR"
|
|
501
601
|
});
|
|
502
|
-
return results.slice(0, limit);
|
|
602
|
+
return results.sort((a, b) => this._compareSearchResults(a, b)).slice(0, limit);
|
|
503
603
|
}
|
|
504
604
|
/**
|
|
505
605
|
* Pre-fetches MiniSearch scores for candidate hydration, used during hybrid weighting.
|
|
@@ -509,7 +609,7 @@ var _SearchService = class _SearchService {
|
|
|
509
609
|
let results = this.miniSearch.search(query, {
|
|
510
610
|
filter: (r) => entityIdSet.has(r.entity_id),
|
|
511
611
|
combineWith: "OR"
|
|
512
|
-
});
|
|
612
|
+
}).sort((a, b) => this._compareSearchResults(a, b));
|
|
513
613
|
if (preFilterLimit !== void 0) {
|
|
514
614
|
results = results.slice(0, preFilterLimit);
|
|
515
615
|
}
|
|
@@ -637,6 +737,18 @@ var _SearchService = class _SearchService {
|
|
|
637
737
|
if (updatedAtDiff !== 0) return updatedAtDiff;
|
|
638
738
|
return a.id.localeCompare(b.id);
|
|
639
739
|
}
|
|
740
|
+
/**
|
|
741
|
+
* MiniSearch breaks equal-score ties by internal insertion order, which
|
|
742
|
+
* syncEntries changes: a discarded-and-re-added id moves to the end. Every
|
|
743
|
+
* caller truncates these results to a limit, so which ids survive a tie at
|
|
744
|
+
* the boundary must not depend on write history. Re-sort exact score ties
|
|
745
|
+
* by id — the same final tie-break _compareScoredRows applies.
|
|
746
|
+
*/
|
|
747
|
+
_compareSearchResults(a, b) {
|
|
748
|
+
const scoreDiff = b.score - a.score;
|
|
749
|
+
if (!Number.isNaN(scoreDiff) && scoreDiff !== 0) return scoreDiff;
|
|
750
|
+
return a.id.localeCompare(b.id);
|
|
751
|
+
}
|
|
640
752
|
};
|
|
641
753
|
/**
|
|
642
754
|
* Maximum number of entities whose parsed embedding vectors are held in
|
|
@@ -2290,7 +2402,10 @@ var IngestionService = class {
|
|
|
2290
2402
|
return zeroChunkResult(canonical);
|
|
2291
2403
|
}
|
|
2292
2404
|
diagBuffer.flush(this.options);
|
|
2293
|
-
await this.searchService.
|
|
2405
|
+
await this.searchService.syncEntries(
|
|
2406
|
+
entityId,
|
|
2407
|
+
[...deletedSourceFactIds, ...insertedFacts.map((fact) => fact.id)]
|
|
2408
|
+
);
|
|
2294
2409
|
if (failedChunks === 0) {
|
|
2295
2410
|
const uniqueDeletedSourceFactIds = Array.from(new Set(deletedSourceFactIds));
|
|
2296
2411
|
for (const factId of uniqueDeletedSourceFactIds) {
|
|
@@ -4479,7 +4594,11 @@ var ImportExportService = class {
|
|
|
4479
4594
|
);
|
|
4480
4595
|
}
|
|
4481
4596
|
});
|
|
4482
|
-
|
|
4597
|
+
if (merge) {
|
|
4598
|
+
await this.searchService.syncEntries(entityId, [...upsertedFactIds]);
|
|
4599
|
+
} else {
|
|
4600
|
+
await this.searchService.sync(entityId);
|
|
4601
|
+
}
|
|
4483
4602
|
for (const fact of bundle.facts) {
|
|
4484
4603
|
if (!fact.deleted_at && upsertedFactIds.has(fact.id) && !factsWithPreservedBlob.has(fact.id)) {
|
|
4485
4604
|
const clipped = clippedTextByFactId.get(fact.id);
|
|
@@ -5663,5 +5782,5 @@ var WriteService = class {
|
|
|
5663
5782
|
};
|
|
5664
5783
|
|
|
5665
5784
|
export { BaseRepository, DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, DiagnosticBuffer, EMBED_CHARS_CEILING, EmbeddingService, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ImportExportService, IngestionService, JobManager, MaintenanceService, MetadataRepository, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, RetrievalService, SearchService, WikiBusyError, WikiDraftNotFound, WikiDuplicateHashError, WikiGraphNodeOwnershipConflict, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, edgeTripleAllowed, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveGrounding, resolveNodeType, safeSlice, typeSatisfies, validateInlineEdges, validateManifest };
|
|
5666
|
-
//# sourceMappingURL=chunk-
|
|
5667
|
-
//# sourceMappingURL=chunk-
|
|
5785
|
+
//# sourceMappingURL=chunk-PBUZLSOM.mjs.map
|
|
5786
|
+
//# sourceMappingURL=chunk-PBUZLSOM.mjs.map
|