@equationalapplications/core-llm-wiki 4.0.0 → 4.2.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
@@ -412,6 +412,7 @@ var _WikiMemory = class _WikiMemory {
412
412
  constructor(db, options) {
413
413
  this.activeMaintenanceJobs = /* @__PURE__ */ new Set();
414
414
  this.activeIngestJobs = /* @__PURE__ */ new Set();
415
+ this.statusSubscribers = /* @__PURE__ */ new Map();
415
416
  this.miniSearch = new MiniSearch({
416
417
  fields: ["title", "body", "tags"],
417
418
  storeFields: ["entity_id"],
@@ -816,6 +817,24 @@ After running the migration SQL, restart your application.`
816
817
  }
817
818
  return false;
818
819
  }
820
+ _copyEntityStatus(s) {
821
+ return { ingesting: s.ingesting, librarian: s.librarian, heal: s.heal };
822
+ }
823
+ _notifyStatusSubscribers(entityId) {
824
+ const set = this.statusSubscribers.get(entityId);
825
+ if (!set || set.size === 0) return;
826
+ for (const entry of Array.from(set)) {
827
+ if (!set.has(entry)) continue;
828
+ const next = this.getEntityStatus(entityId);
829
+ if (entry.last.ingesting === next.ingesting && entry.last.librarian === next.librarian && entry.last.heal === next.heal) continue;
830
+ entry.last = this._copyEntityStatus(next);
831
+ try {
832
+ entry.callback(this._copyEntityStatus(next));
833
+ } catch (err) {
834
+ console.error(`[WikiMemory.subscribeEntityStatus] callback error for entityId="${entityId}" during transition emission`, err);
835
+ }
836
+ }
837
+ }
819
838
  _validatePruneDuration(value, name) {
820
839
  if (value !== null && value !== void 0 && (typeof value !== "number" || !isFinite(value) || value < 0)) {
821
840
  throw new Error(`Invalid ${name}: must be a non-negative finite number or null`);
@@ -1558,7 +1577,11 @@ After running the migration SQL, restart your application.`
1558
1577
  const jobKey = this._librarianKey(entityId);
1559
1578
  if (!this.activeMaintenanceJobs.has(jobKey) && !this.activeMaintenanceJobs.has(this._pruneKey(entityId)) && !this._isReembedActive(entityId) && !this._isImportActiveFor(entityId) && !this._isForgetActiveFor(entityId)) {
1560
1579
  this.activeMaintenanceJobs.add(jobKey);
1561
- this.runLibrarianThenMaybeHeal(entityId, count).catch(console.error).finally(() => this.activeMaintenanceJobs.delete(jobKey));
1580
+ this._notifyStatusSubscribers(entityId);
1581
+ this.runLibrarianThenMaybeHeal(entityId, count).catch(console.error).finally(() => {
1582
+ this.activeMaintenanceJobs.delete(jobKey);
1583
+ this._notifyStatusSubscribers(entityId);
1584
+ });
1562
1585
  }
1563
1586
  }
1564
1587
  }
@@ -1577,6 +1600,7 @@ After running the migration SQL, restart your application.`
1577
1600
  const healKey = this._healKey(entityId);
1578
1601
  if (!this.activeMaintenanceJobs.has(healKey)) {
1579
1602
  this.activeMaintenanceJobs.add(healKey);
1603
+ this._notifyStatusSubscribers(entityId);
1580
1604
  try {
1581
1605
  await this._doRunHeal(entityId);
1582
1606
  await this.db.runAsync(`
@@ -1586,6 +1610,7 @@ After running the migration SQL, restart your application.`
1586
1610
  `, [entityId, currentEventCount, currentEventCount]);
1587
1611
  } finally {
1588
1612
  this.activeMaintenanceJobs.delete(healKey);
1613
+ this._notifyStatusSubscribers(entityId);
1589
1614
  }
1590
1615
  }
1591
1616
  }
@@ -1777,10 +1802,12 @@ The following document anchors are provided for contradiction detection only. Do
1777
1802
  throw new WikiBusyError("forget", entityId);
1778
1803
  }
1779
1804
  this.activeMaintenanceJobs.add(jobKey);
1805
+ this._notifyStatusSubscribers(entityId);
1780
1806
  try {
1781
1807
  await this._doRunLibrarian(entityId);
1782
1808
  } finally {
1783
1809
  this.activeMaintenanceJobs.delete(jobKey);
1810
+ this._notifyStatusSubscribers(entityId);
1784
1811
  }
1785
1812
  }
1786
1813
  async runHeal(entityId) {
@@ -1801,10 +1828,12 @@ The following document anchors are provided for contradiction detection only. Do
1801
1828
  throw new WikiBusyError("forget", entityId);
1802
1829
  }
1803
1830
  this.activeMaintenanceJobs.add(jobKey);
1831
+ this._notifyStatusSubscribers(entityId);
1804
1832
  try {
1805
1833
  await this._doRunHeal(entityId);
1806
1834
  } finally {
1807
1835
  this.activeMaintenanceJobs.delete(jobKey);
1836
+ this._notifyStatusSubscribers(entityId);
1808
1837
  }
1809
1838
  }
1810
1839
  async runReembed(entityId, opts) {
@@ -1944,6 +1973,40 @@ The following document anchors are provided for contradiction detection only. Do
1944
1973
  heal: this.activeMaintenanceJobs.has(this._healKey(entityId))
1945
1974
  };
1946
1975
  }
1976
+ /**
1977
+ * Subscribe to {@link EntityStatus} changes for a single entity. The callback
1978
+ * is invoked synchronously once with the current status before this method
1979
+ * returns, then again on every transition where any of `ingesting`,
1980
+ * `librarian`, or `heal` flips. No polling, no duplicate snapshots.
1981
+ *
1982
+ * Returns an idempotent unsubscribe function.
1983
+ *
1984
+ * See also {@link getEntityStatus} for a synchronous point-in-time read.
1985
+ */
1986
+ subscribeEntityStatus(entityId, callback) {
1987
+ const initial = this.getEntityStatus(entityId);
1988
+ let set = this.statusSubscribers.get(entityId);
1989
+ if (!set) {
1990
+ set = /* @__PURE__ */ new Set();
1991
+ this.statusSubscribers.set(entityId, set);
1992
+ }
1993
+ const entry = { callback, last: this._copyEntityStatus(initial) };
1994
+ set.add(entry);
1995
+ try {
1996
+ callback(this._copyEntityStatus(initial));
1997
+ } catch (err) {
1998
+ console.error(`[WikiMemory.subscribeEntityStatus] callback error for entityId="${entityId}" during initial emission`, err);
1999
+ }
2000
+ let active = true;
2001
+ return () => {
2002
+ if (!active) return;
2003
+ active = false;
2004
+ const s = this.statusSubscribers.get(entityId);
2005
+ if (!s) return;
2006
+ s.delete(entry);
2007
+ if (s.size === 0) this.statusSubscribers.delete(entityId);
2008
+ };
2009
+ }
1947
2010
  clearVectorCache() {
1948
2011
  this.vectorCache.clear();
1949
2012
  }
@@ -2469,6 +2532,7 @@ The following document anchors are provided for contradiction detection only. Do
2469
2532
  throw new WikiBusyError("forget", entityId);
2470
2533
  }
2471
2534
  this.activeIngestJobs.add(jobKey);
2535
+ this._notifyStatusSubscribers(entityId);
2472
2536
  try {
2473
2537
  const { chunks, truncated } = chunkText(params.documentChunk, maxChunkLength, chunkOverlap);
2474
2538
  if (chunks.length === 0) {
@@ -2540,6 +2604,7 @@ ${chunk}`;
2540
2604
  return { truncated, chunks: chunks.length };
2541
2605
  } finally {
2542
2606
  this.activeIngestJobs.delete(jobKey);
2607
+ this._notifyStatusSubscribers(entityId);
2543
2608
  }
2544
2609
  }
2545
2610
  };