@equationalapplications/core-llm-wiki 6.5.1 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { __privateAdd, MetadataRepository, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, entitySummaryMetaKey, WikiSourceRefHashCollision, validateManifest, generateId, BaseRepository, emptyManifest, resolveNodeType, WikiStrictOntologyViolation, validateInlineEdges, resolveEdgeDefinitions, typeSatisfies, normalizeTitleKey, WikiTransactionError, extractSqliteCode } from './chunk-YNFYSLR6.mjs';
2
- export { DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, EMBED_CHARS_CEILING, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, WikiBusyError, WikiDuplicateHashError, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, chunkText, configureRandomSource, parseEmbedding, safeSlice, validateManifest } from './chunk-YNFYSLR6.mjs';
1
+ import { __privateAdd, MetadataRepository, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, entitySummaryMetaKey, WikiSourceRefHashCollision, validateManifest, generateId, BaseRepository, emptyManifest, resolveNodeType, WikiStrictOntologyViolation, validateInlineEdges, resolveEdgeDefinitions, typeSatisfies, normalizeTitleKey, WikiTransactionError, extractSqliteCode } from './chunk-ZU7K6NAU.mjs';
2
+ export { DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, DEFAULT_MAX_EMBED_CHARS, EMBED_CHARS_CEILING, HEAL_ANCHORS_PER_CANDIDATE, HEAL_BATCH_SIZE, HEAL_MAX_FACT_BODY_CHARS_L3, HEAL_MAX_TASKS, HEAL_RECHECK_MS, HOOK_TIMEOUT_MARKER, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, WikiBusyError, WikiDuplicateHashError, WikiIngestEmptyError, WikiInvalidReadOptions, WikiParseError, WikiSourceRefHashCollision, WikiStrictOntologyViolation, WikiTransactionError, chunkText, configureRandomSource, parseEmbedding, safeSlice, validateManifest } from './chunk-ZU7K6NAU.mjs';
3
3
  import { appendRelatedSection, buildConceptDocument, buildLogMd, buildEntityIndexMd, buildRootIndexMd, isAllowedOkfPath, parseRootIndexMd, parseEntityIndexMd, parseConcept, splitRelatedSection, extractMarkdownLinks, parseLogMd, parseEventIdComment, latestVerified, formatSourcesJson, appendEventIdComment, parseVerifiedFlexible, parseCitationsList, deriveTrustTier, isStaleAfter } from '@equationalapplications/core-okf';
4
4
 
5
5
  // src/db/schema.ts
@@ -1519,6 +1519,20 @@ declare class JobManager {
1519
1519
  */
1520
1520
  private readonly lockKeyFns;
1521
1521
  private _isReembedActive;
1522
+ /**
1523
+ * True while any reembed sweep is in flight — per-entity or global.
1524
+ *
1525
+ * Read-only; acquires nothing. Callers that mutate embedding failure
1526
+ * markers across ALL entities (the dimension-promotion clear) must gate on
1527
+ * this and defer, rather than resurrect rows an in-flight sweep already
1528
+ * classified. See spec 2026-09-05-reembed-lock-scope-design.md §2.
1529
+ *
1530
+ * The global key `${prefix}:reembed` also ends in ':reembed', so the suffix
1531
+ * scan alone would already match it. The explicit check is kept because it
1532
+ * documents the intent and survives any future change to the global key's
1533
+ * format.
1534
+ */
1535
+ isAnyReembedActive(): boolean;
1522
1536
  private _isImportActiveFor;
1523
1537
  private _isForgetActiveFor;
1524
1538
  private _isAnyMaintenanceActiveWithSuffix;
@@ -1534,8 +1548,10 @@ declare class JobManager {
1534
1548
  */
1535
1549
  isBlocked(operation: OperationType, entityId: string): boolean;
1536
1550
  /**
1537
- * Auto-heal historically only gated on the heal self-key. Keep that behavior
1538
- * for write() auto-trigger paths while preserving stricter checks in acquireLock().
1551
+ * Auto-heal historically only gated on the heal self-key. Keep that
1552
+ * permissiveness for write() auto-trigger paths — stricter checks stay in
1553
+ * acquireLock() — with one exception: reembed sweeps, which are excluded
1554
+ * structurally rather than by accident (see below).
1539
1555
  */
1540
1556
  tryAcquireAutoHealLock(entityId: string): boolean;
1541
1557
  /**
@@ -1636,6 +1652,19 @@ declare class EmbeddingService {
1636
1652
  * Revived rows are NOT embedded here. They become eligible and are picked up
1637
1653
  * by the NEXT sweep, because runReembed calls this after its candidates were
1638
1654
  * already classified; same-sweep revival would be re-entrant.
1655
+ *
1656
+ * Caller contract (spec 2026-09-05 §3.2): callers must not invoke this while
1657
+ * a reembed sweep is in flight unless they hold that sweep's lock, because
1658
+ * the marker clear spans every entity. `runReembed`'s tail call is exempt —
1659
+ * by the tail, the sweep's classification phase is complete, so the clear
1660
+ * cannot resurrect a row THIS sweep classified; any overlap with a
1661
+ * concurrent import's entity-scoped, blob-guarded writes (an importDump of a
1662
+ * different entity can run during a per-entity sweep) is bounded redundant
1663
+ * work. External callers
1664
+ * gate on `JobManager.isAnyReembedActive()` and defer. This service
1665
+ * deliberately holds no `JobManager` reference: gating here instead of at
1666
+ * the call site would make `runReembed`'s own tail call see its own lock and
1667
+ * defer forever.
1639
1668
  */
1640
1669
  reconcileEmbeddingDimension(): Promise<void>;
1641
1670
  tryEmbedFact(fact: {
@@ -2026,6 +2055,15 @@ declare class MaintenanceService {
2026
2055
  * sets embedding_dimension_mismatch, so promotion never fires and markers
2027
2056
  * survive the swap. Use `runReembed({ force: true })` to clear that state —
2028
2057
  * `force` bypasses classification entirely and retries every row.
2058
+ *
2059
+ * Lock scope (spec 2026-09-05 §2): a marker mutation is legal while a sweep
2060
+ * is in flight if and only if it cannot resurrect a row the sweep has
2061
+ * classified. Mutations scoped to entities not under an active sweep key are
2062
+ * legal — the entity-scoped ingest/import lock checks enforce that. The one
2063
+ * mutation that spans every entity, `reconcileEmbeddingDimension`'s marker
2064
+ * clear, is made legal by DEFERRING (importDump gates on
2065
+ * `JobManager.isAnyReembedActive()`), never by widening a lock. Auto-heal is
2066
+ * excluded from sweeps for the same reason.
2029
2067
  */
2030
2068
  runReembed(entityId?: string, opts?: {
2031
2069
  force?: boolean;
@@ -1519,6 +1519,20 @@ declare class JobManager {
1519
1519
  */
1520
1520
  private readonly lockKeyFns;
1521
1521
  private _isReembedActive;
1522
+ /**
1523
+ * True while any reembed sweep is in flight — per-entity or global.
1524
+ *
1525
+ * Read-only; acquires nothing. Callers that mutate embedding failure
1526
+ * markers across ALL entities (the dimension-promotion clear) must gate on
1527
+ * this and defer, rather than resurrect rows an in-flight sweep already
1528
+ * classified. See spec 2026-09-05-reembed-lock-scope-design.md §2.
1529
+ *
1530
+ * The global key `${prefix}:reembed` also ends in ':reembed', so the suffix
1531
+ * scan alone would already match it. The explicit check is kept because it
1532
+ * documents the intent and survives any future change to the global key's
1533
+ * format.
1534
+ */
1535
+ isAnyReembedActive(): boolean;
1522
1536
  private _isImportActiveFor;
1523
1537
  private _isForgetActiveFor;
1524
1538
  private _isAnyMaintenanceActiveWithSuffix;
@@ -1534,8 +1548,10 @@ declare class JobManager {
1534
1548
  */
1535
1549
  isBlocked(operation: OperationType, entityId: string): boolean;
1536
1550
  /**
1537
- * Auto-heal historically only gated on the heal self-key. Keep that behavior
1538
- * for write() auto-trigger paths while preserving stricter checks in acquireLock().
1551
+ * Auto-heal historically only gated on the heal self-key. Keep that
1552
+ * permissiveness for write() auto-trigger paths — stricter checks stay in
1553
+ * acquireLock() — with one exception: reembed sweeps, which are excluded
1554
+ * structurally rather than by accident (see below).
1539
1555
  */
1540
1556
  tryAcquireAutoHealLock(entityId: string): boolean;
1541
1557
  /**
@@ -1636,6 +1652,19 @@ declare class EmbeddingService {
1636
1652
  * Revived rows are NOT embedded here. They become eligible and are picked up
1637
1653
  * by the NEXT sweep, because runReembed calls this after its candidates were
1638
1654
  * already classified; same-sweep revival would be re-entrant.
1655
+ *
1656
+ * Caller contract (spec 2026-09-05 §3.2): callers must not invoke this while
1657
+ * a reembed sweep is in flight unless they hold that sweep's lock, because
1658
+ * the marker clear spans every entity. `runReembed`'s tail call is exempt —
1659
+ * by the tail, the sweep's classification phase is complete, so the clear
1660
+ * cannot resurrect a row THIS sweep classified; any overlap with a
1661
+ * concurrent import's entity-scoped, blob-guarded writes (an importDump of a
1662
+ * different entity can run during a per-entity sweep) is bounded redundant
1663
+ * work. External callers
1664
+ * gate on `JobManager.isAnyReembedActive()` and defer. This service
1665
+ * deliberately holds no `JobManager` reference: gating here instead of at
1666
+ * the call site would make `runReembed`'s own tail call see its own lock and
1667
+ * defer forever.
1639
1668
  */
1640
1669
  reconcileEmbeddingDimension(): Promise<void>;
1641
1670
  tryEmbedFact(fact: {
@@ -2026,6 +2055,15 @@ declare class MaintenanceService {
2026
2055
  * sets embedding_dimension_mismatch, so promotion never fires and markers
2027
2056
  * survive the swap. Use `runReembed({ force: true })` to clear that state —
2028
2057
  * `force` bypasses classification entirely and retries every row.
2058
+ *
2059
+ * Lock scope (spec 2026-09-05 §2): a marker mutation is legal while a sweep
2060
+ * is in flight if and only if it cannot resurrect a row the sweep has
2061
+ * classified. Mutations scoped to entities not under an active sweep key are
2062
+ * legal — the entity-scoped ingest/import lock checks enforce that. The one
2063
+ * mutation that spans every entity, `reconcileEmbeddingDimension`'s marker
2064
+ * clear, is made legal by DEFERRING (importDump gates on
2065
+ * `JobManager.isAnyReembedActive()`), never by widening a lock. Auto-heal is
2066
+ * excluded from sweeps for the same reason.
2029
2067
  */
2030
2068
  runReembed(entityId?: string, opts?: {
2031
2069
  force?: boolean;
@@ -1,3 +1,3 @@
1
- export { a8 as EmbeddingService, a9 as ImportExportService, aa as IngestionService, ab as JobManager, ab as JobManagerType, ac as MaintenanceService, ad as RetrievalService, ae as SearchService, ae as SearchServiceType, a1 as WikiMemoryTestAccess, af as WriteService } from './testing-BtsN1pTK.mjs';
1
+ export { a8 as EmbeddingService, a9 as ImportExportService, aa as IngestionService, ab as JobManager, ab as JobManagerType, ac as MaintenanceService, ad as RetrievalService, ae as SearchService, ae as SearchServiceType, a1 as WikiMemoryTestAccess, af as WriteService } from './testing-CA7723qk.mjs';
2
2
  import '@equationalapplications/core-okf';
3
3
  import 'minisearch';
package/dist/testing.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { a8 as EmbeddingService, a9 as ImportExportService, aa as IngestionService, ab as JobManager, ab as JobManagerType, ac as MaintenanceService, ad as RetrievalService, ae as SearchService, ae as SearchServiceType, a1 as WikiMemoryTestAccess, af as WriteService } from './testing-BtsN1pTK.js';
1
+ export { a8 as EmbeddingService, a9 as ImportExportService, aa as IngestionService, ab as JobManager, ab as JobManagerType, ac as MaintenanceService, ad as RetrievalService, ae as SearchService, ae as SearchServiceType, a1 as WikiMemoryTestAccess, af as WriteService } from './testing-CA7723qk.js';
2
2
  import '@equationalapplications/core-okf';
3
3
  import 'minisearch';
package/dist/testing.js CHANGED
@@ -665,6 +665,19 @@ var EmbeddingService = class {
665
665
  * Revived rows are NOT embedded here. They become eligible and are picked up
666
666
  * by the NEXT sweep, because runReembed calls this after its candidates were
667
667
  * already classified; same-sweep revival would be re-entrant.
668
+ *
669
+ * Caller contract (spec 2026-09-05 §3.2): callers must not invoke this while
670
+ * a reembed sweep is in flight unless they hold that sweep's lock, because
671
+ * the marker clear spans every entity. `runReembed`'s tail call is exempt —
672
+ * by the tail, the sweep's classification phase is complete, so the clear
673
+ * cannot resurrect a row THIS sweep classified; any overlap with a
674
+ * concurrent import's entity-scoped, blob-guarded writes (an importDump of a
675
+ * different entity can run during a per-entity sweep) is bounded redundant
676
+ * work. External callers
677
+ * gate on `JobManager.isAnyReembedActive()` and defer. This service
678
+ * deliberately holds no `JobManager` reference: gating here instead of at
679
+ * the call site would make `runReembed`'s own tail call see its own lock and
680
+ * defer forever.
668
681
  */
669
682
  async reconcileEmbeddingDimension() {
670
683
  const mismatchValue = await this.metadataRepo.getMeta("embedding_dimension_mismatch");
@@ -1227,7 +1240,13 @@ var ImportExportService = class {
1227
1240
  this.db
1228
1241
  );
1229
1242
  }
1230
- await this.embeddingService.reconcileEmbeddingDimension();
1243
+ if (this.jobManager.isAnyReembedActive()) {
1244
+ console.info(
1245
+ "[WikiMemory] importDump: embedding-dimension promotion deferred; a reembed sweep is in flight"
1246
+ );
1247
+ } else {
1248
+ await this.embeddingService.reconcileEmbeddingDimension();
1249
+ }
1231
1250
  } else {
1232
1251
  await this.metadataRepo.setMeta(
1233
1252
  "embedding_dimension_mismatch",
@@ -2319,6 +2338,15 @@ var MaintenanceService = class {
2319
2338
  * sets embedding_dimension_mismatch, so promotion never fires and markers
2320
2339
  * survive the swap. Use `runReembed({ force: true })` to clear that state —
2321
2340
  * `force` bypasses classification entirely and retries every row.
2341
+ *
2342
+ * Lock scope (spec 2026-09-05 §2): a marker mutation is legal while a sweep
2343
+ * is in flight if and only if it cannot resurrect a row the sweep has
2344
+ * classified. Mutations scoped to entities not under an active sweep key are
2345
+ * legal — the entity-scoped ingest/import lock checks enforce that. The one
2346
+ * mutation that spans every entity, `reconcileEmbeddingDimension`'s marker
2347
+ * clear, is made legal by DEFERRING (importDump gates on
2348
+ * `JobManager.isAnyReembedActive()`), never by widening a lock. Auto-heal is
2349
+ * excluded from sweeps for the same reason.
2322
2350
  */
2323
2351
  async runReembed(entityId, opts) {
2324
2352
  const embedFn = this.options.llmProvider.embed;
@@ -4096,6 +4124,22 @@ var JobManager = class {
4096
4124
  _isReembedActive(entityId) {
4097
4125
  return this.activeMaintenanceJobs.has(this._reembedKey(entityId)) || this.activeMaintenanceJobs.has(this._globalReembedKey());
4098
4126
  }
4127
+ /**
4128
+ * True while any reembed sweep is in flight — per-entity or global.
4129
+ *
4130
+ * Read-only; acquires nothing. Callers that mutate embedding failure
4131
+ * markers across ALL entities (the dimension-promotion clear) must gate on
4132
+ * this and defer, rather than resurrect rows an in-flight sweep already
4133
+ * classified. See spec 2026-09-05-reembed-lock-scope-design.md §2.
4134
+ *
4135
+ * The global key `${prefix}:reembed` also ends in ':reembed', so the suffix
4136
+ * scan alone would already match it. The explicit check is kept because it
4137
+ * documents the intent and survives any future change to the global key's
4138
+ * format.
4139
+ */
4140
+ isAnyReembedActive() {
4141
+ return this.activeMaintenanceJobs.has(this._globalReembedKey()) || this._isAnyMaintenanceActiveWithSuffix(":reembed");
4142
+ }
4099
4143
  _isImportActiveFor(entityId) {
4100
4144
  return this.activeMaintenanceJobs.has(this._importKey(entityId)) || this.activeMaintenanceJobs.has(this._globalImportKey());
4101
4145
  }
@@ -4269,10 +4313,13 @@ var JobManager = class {
4269
4313
  }
4270
4314
  }
4271
4315
  /**
4272
- * Auto-heal historically only gated on the heal self-key. Keep that behavior
4273
- * for write() auto-trigger paths while preserving stricter checks in acquireLock().
4316
+ * Auto-heal historically only gated on the heal self-key. Keep that
4317
+ * permissiveness for write() auto-trigger paths — stricter checks stay in
4318
+ * acquireLock() — with one exception: reembed sweeps, which are excluded
4319
+ * structurally rather than by accident (see below).
4274
4320
  */
4275
4321
  tryAcquireAutoHealLock(entityId) {
4322
+ if (this.isAnyReembedActive()) return false;
4276
4323
  const healKey = this._healKey(entityId);
4277
4324
  if (this.activeMaintenanceJobs.has(healKey)) return false;
4278
4325
  this.activeMaintenanceJobs.add(healKey);