@equationalapplications/core-llm-wiki 7.9.2 → 7.10.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.
@@ -314,6 +314,19 @@ interface WikiConfig {
314
314
  ontology?: OntologyConfig;
315
315
  /** Default node cap for traverseGraph(), unless overridden per-call. Default 20. */
316
316
  maxTraversalNodes?: number;
317
+ /**
318
+ * Keyword-index backend (#257).
319
+ * - `'fts5'`: always use SQLite FTS5; `setup()` throws if the adapter's SQLite lacks FTS5 or json1.
320
+ * - `'minisearch'`: always use the in-RAM MiniSearch index. Memory grows with stored text.
321
+ * - `'auto'`: probe at `setup()`; use FTS5 when present, else MiniSearch.
322
+ *
323
+ * FTS5 keeps its index inside the database file, fed by triggers on the
324
+ * entries table, so memory stays flat at any size. The first `setup()` that
325
+ * attaches FTS5 to an existing database rebuilds the index in one
326
+ * transaction, holding the write lock for its whole duration.
327
+ * @default 'minisearch'
328
+ */
329
+ indexStrategy?: 'fts5' | 'minisearch' | 'auto';
317
330
  /** Default minimum confidence tier for discovered traversal nodes. Default 'tentative'. */
318
331
  minTraversalConfidence?: 'certain' | 'inferred' | 'tentative';
319
332
  /** Default traversal direction. Default 'both'. */
@@ -2022,6 +2035,22 @@ interface IndexStrategy {
2022
2035
  * Returning `undefined` means "unknown — take the rebuild path".
2023
2036
  */
2024
2037
  hasIndexedEntity?(entityId: string): boolean | undefined;
2038
+ /**
2039
+ * Create or verify the strategy's backing schema and run state changes.
2040
+ * Called once by `createIndexStrategy`, before the strategy is installed.
2041
+ */
2042
+ init?(): Promise<void>;
2043
+ /**
2044
+ * Apply every change recorded in the strategy's durable ledger, in bounded
2045
+ * chunks. A strategy that implements this keeps itself consistent with
2046
+ * SQLite on its own: `SearchService` routes every sync to `drain()` and
2047
+ * never feeds it rows (spec 2026-10-05 §PR-2 revision). MUST NOT be
2048
+ * called from inside a `withTransactionAsync` callback; it opens its own
2049
+ * transactions.
2050
+ */
2051
+ drain?(): Promise<void>;
2052
+ /** Rebuild entirely in SQL from the source tables; no rows pass through JS. */
2053
+ rebuildFromSource?(): Promise<void>;
2025
2054
  }
2026
2055
 
2027
2056
  interface ScoredRow {
@@ -2089,6 +2118,17 @@ declare class SearchService {
2089
2118
  */
2090
2119
  private staleEpochs;
2091
2120
  constructor(entryRepo: EntryRepository, indexStrategy: IndexStrategy);
2121
+ /**
2122
+ * Install the strategy resolved by `WikiMemory.setup()`. Before setup, the
2123
+ * service runs on an empty MiniSearch placeholder, as it always has.
2124
+ */
2125
+ setIndexStrategy(strategy: IndexStrategy): void;
2126
+ /**
2127
+ * One serialized drain turn for strategies with a durable ledger. Like
2128
+ * sync(), it never rejects: the keyword index is rebuildable, and a failed
2129
+ * drain leaves the ledger rows in place for the next turn.
2130
+ */
2131
+ private drainTurn;
2092
2132
  /**
2093
2133
  * Rebuilds the search index and clears the vector cache for a given entity.
2094
2134
  * A direct replacement for manually syncing state after a DB transaction.
@@ -3611,6 +3651,11 @@ declare class WikiMemory {
3611
3651
  * entity stays stale and the next call retries it. Does not compute
3612
3652
  * embeddings; that remains the maintenance sweep's job.
3613
3653
  *
3654
+ * With the FTS5 index strategy this drains the index ledger in its own
3655
+ * transactions, so it MUST be called after the host transaction commits,
3656
+ * never from inside a `withTransactionAsync` callback. Calling it inside
3657
+ * one waits on the transaction mutex that the callback holds.
3658
+ *
3614
3659
  * @throws {TypeError} when `entityId` is given but is not a non-empty string.
3615
3660
  */
3616
3661
  syncSearchIndex(entityId?: string): Promise<void>;
@@ -314,6 +314,19 @@ interface WikiConfig {
314
314
  ontology?: OntologyConfig;
315
315
  /** Default node cap for traverseGraph(), unless overridden per-call. Default 20. */
316
316
  maxTraversalNodes?: number;
317
+ /**
318
+ * Keyword-index backend (#257).
319
+ * - `'fts5'`: always use SQLite FTS5; `setup()` throws if the adapter's SQLite lacks FTS5 or json1.
320
+ * - `'minisearch'`: always use the in-RAM MiniSearch index. Memory grows with stored text.
321
+ * - `'auto'`: probe at `setup()`; use FTS5 when present, else MiniSearch.
322
+ *
323
+ * FTS5 keeps its index inside the database file, fed by triggers on the
324
+ * entries table, so memory stays flat at any size. The first `setup()` that
325
+ * attaches FTS5 to an existing database rebuilds the index in one
326
+ * transaction, holding the write lock for its whole duration.
327
+ * @default 'minisearch'
328
+ */
329
+ indexStrategy?: 'fts5' | 'minisearch' | 'auto';
317
330
  /** Default minimum confidence tier for discovered traversal nodes. Default 'tentative'. */
318
331
  minTraversalConfidence?: 'certain' | 'inferred' | 'tentative';
319
332
  /** Default traversal direction. Default 'both'. */
@@ -2022,6 +2035,22 @@ interface IndexStrategy {
2022
2035
  * Returning `undefined` means "unknown — take the rebuild path".
2023
2036
  */
2024
2037
  hasIndexedEntity?(entityId: string): boolean | undefined;
2038
+ /**
2039
+ * Create or verify the strategy's backing schema and run state changes.
2040
+ * Called once by `createIndexStrategy`, before the strategy is installed.
2041
+ */
2042
+ init?(): Promise<void>;
2043
+ /**
2044
+ * Apply every change recorded in the strategy's durable ledger, in bounded
2045
+ * chunks. A strategy that implements this keeps itself consistent with
2046
+ * SQLite on its own: `SearchService` routes every sync to `drain()` and
2047
+ * never feeds it rows (spec 2026-10-05 §PR-2 revision). MUST NOT be
2048
+ * called from inside a `withTransactionAsync` callback; it opens its own
2049
+ * transactions.
2050
+ */
2051
+ drain?(): Promise<void>;
2052
+ /** Rebuild entirely in SQL from the source tables; no rows pass through JS. */
2053
+ rebuildFromSource?(): Promise<void>;
2025
2054
  }
2026
2055
 
2027
2056
  interface ScoredRow {
@@ -2089,6 +2118,17 @@ declare class SearchService {
2089
2118
  */
2090
2119
  private staleEpochs;
2091
2120
  constructor(entryRepo: EntryRepository, indexStrategy: IndexStrategy);
2121
+ /**
2122
+ * Install the strategy resolved by `WikiMemory.setup()`. Before setup, the
2123
+ * service runs on an empty MiniSearch placeholder, as it always has.
2124
+ */
2125
+ setIndexStrategy(strategy: IndexStrategy): void;
2126
+ /**
2127
+ * One serialized drain turn for strategies with a durable ledger. Like
2128
+ * sync(), it never rejects: the keyword index is rebuildable, and a failed
2129
+ * drain leaves the ledger rows in place for the next turn.
2130
+ */
2131
+ private drainTurn;
2092
2132
  /**
2093
2133
  * Rebuilds the search index and clears the vector cache for a given entity.
2094
2134
  * A direct replacement for manually syncing state after a DB transaction.
@@ -3611,6 +3651,11 @@ declare class WikiMemory {
3611
3651
  * entity stays stale and the next call retries it. Does not compute
3612
3652
  * embeddings; that remains the maintenance sweep's job.
3613
3653
  *
3654
+ * With the FTS5 index strategy this drains the index ledger in its own
3655
+ * transactions, so it MUST be called after the host transaction commits,
3656
+ * never from inside a `withTransactionAsync` callback. Calling it inside
3657
+ * one waits on the transaction mutex that the callback holds.
3658
+ *
3614
3659
  * @throws {TypeError} when `entityId` is given but is not a non-empty string.
3615
3660
  */
3616
3661
  syncSearchIndex(entityId?: string): Promise<void>;
@@ -1,2 +1,2 @@
1
- export { aB as EmbeddingService, aC as ImportExportService, aD as IngestionService, aE as JobManager, aE as JobManagerType, aF as MaintenanceService, aG as RetrievalService, aH as SearchService, aH as SearchServiceType, ar as WikiMemoryTestAccess, aI as WriteService } from './testing-Bf6etdUy.mjs';
1
+ export { aB as EmbeddingService, aC as ImportExportService, aD as IngestionService, aE as JobManager, aE as JobManagerType, aF as MaintenanceService, aG as RetrievalService, aH as SearchService, aH as SearchServiceType, ar as WikiMemoryTestAccess, aI as WriteService } from './testing-SxCsMt1q.mjs';
2
2
  import '@equationalapplications/core-okf';
package/dist/testing.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { aB as EmbeddingService, aC as ImportExportService, aD as IngestionService, aE as JobManager, aE as JobManagerType, aF as MaintenanceService, aG as RetrievalService, aH as SearchService, aH as SearchServiceType, ar as WikiMemoryTestAccess, aI as WriteService } from './testing-Bf6etdUy.js';
1
+ export { aB as EmbeddingService, aC as ImportExportService, aD as IngestionService, aE as JobManager, aE as JobManagerType, aF as MaintenanceService, aG as RetrievalService, aH as SearchService, aH as SearchServiceType, ar as WikiMemoryTestAccess, aI as WriteService } from './testing-SxCsMt1q.js';
2
2
  import '@equationalapplications/core-okf';
package/dist/testing.js CHANGED
@@ -5452,6 +5452,33 @@ var _SearchService = class _SearchService {
5452
5452
  this.staleEpochs = /* @__PURE__ */ new Map();
5453
5453
  this.indexStrategy = indexStrategy;
5454
5454
  }
5455
+ /**
5456
+ * Install the strategy resolved by `WikiMemory.setup()`. Before setup, the
5457
+ * service runs on an empty MiniSearch placeholder, as it always has.
5458
+ */
5459
+ setIndexStrategy(strategy) {
5460
+ this.indexStrategy = strategy;
5461
+ }
5462
+ /**
5463
+ * One serialized drain turn for strategies with a durable ledger. Like
5464
+ * sync(), it never rejects: the keyword index is rebuildable, and a failed
5465
+ * drain leaves the ledger rows in place for the next turn.
5466
+ */
5467
+ drainTurn(entityId) {
5468
+ const work = this.syncChain.then(async () => {
5469
+ try {
5470
+ try {
5471
+ await this.indexStrategy.drain();
5472
+ } finally {
5473
+ this.evictCache(entityId);
5474
+ }
5475
+ } catch (err) {
5476
+ console.warn(`[WikiMemory] search index drain failed for ${entityId ?? "*"}:`, err);
5477
+ }
5478
+ });
5479
+ this.syncChain = work;
5480
+ return work;
5481
+ }
5455
5482
  /**
5456
5483
  * Rebuilds the search index and clears the vector cache for a given entity.
5457
5484
  * A direct replacement for manually syncing state after a DB transaction.
@@ -5461,6 +5488,7 @@ var _SearchService = class _SearchService {
5461
5488
  * correct failure mode and killing the host process is not.
5462
5489
  */
5463
5490
  async sync(entityId) {
5491
+ if (this.indexStrategy.drain) return this.drainTurn(entityId);
5464
5492
  const work = this.syncChain.then(async () => {
5465
5493
  try {
5466
5494
  try {
@@ -5503,6 +5531,7 @@ var _SearchService = class _SearchService {
5503
5531
  * already queued on the chain.
5504
5532
  */
5505
5533
  async syncEntries(entityId, ids) {
5534
+ if (this.indexStrategy.drain) return this.drainTurn(entityId);
5506
5535
  const uniqueIds = [...new Set(ids)];
5507
5536
  const indexed = this.indexStrategy.hasIndexedEntity?.(entityId);
5508
5537
  if (uniqueIds.length === 0 && !this.staleEntities.has(entityId) && indexed === true) {
@@ -5542,6 +5571,7 @@ var _SearchService = class _SearchService {
5542
5571
  * cannot index itself, such as upsertGraph inside a host transaction.
5543
5572
  */
5544
5573
  markStale(entityId) {
5574
+ if (this.indexStrategy.drain) return;
5545
5575
  this.staleEntities.add(entityId);
5546
5576
  this.staleEpochs.set(entityId, (this.staleEpochs.get(entityId) ?? 0) + 1);
5547
5577
  }
@@ -5552,6 +5582,7 @@ var _SearchService = class _SearchService {
5552
5582
  * stale it resolves at once, without waiting on work already on the chain.
5553
5583
  */
5554
5584
  async syncStale() {
5585
+ if (this.indexStrategy.drain) return this.drainTurn();
5555
5586
  const ids = [...this.staleEntities];
5556
5587
  await Promise.all(ids.map((id) => this.syncEntries(id, [])));
5557
5588
  }