@equationalapplications/core-llm-wiki 3.2.0 → 4.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
@@ -10,7 +10,7 @@ async function setupDatabase(db, prefix) {
10
10
  body TEXT NOT NULL,
11
11
  tags TEXT NOT NULL DEFAULT '[]',
12
12
  confidence TEXT NOT NULL DEFAULT 'inferred',
13
- source_type TEXT NOT NULL DEFAULT 'agent_inferred',
13
+ source_type TEXT NOT NULL DEFAULT 'librarian_inferred',
14
14
  source_hash TEXT,
15
15
  source_ref TEXT,
16
16
  created_at INTEGER NOT NULL,
@@ -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"],
@@ -587,6 +588,44 @@ var _WikiMemory = class _WikiMemory {
587
588
  _warnCrossEntityCollision(type, id, existingEntityId, targetEntityId) {
588
589
  console.warn(`[WikiMemory] importDump: ${type} id "${id}" already belongs to entity "${existingEntityId}"; skipping for entity "${targetEntityId}"`);
589
590
  }
591
+ /** Maps pre-rename enum strings from older dumps to current source_type values. */
592
+ _normalizeImportedSourceType(raw, ctx) {
593
+ if (raw === "user_document") return "immutable_document";
594
+ if (raw === "agent_inferred") return "librarian_inferred";
595
+ const allowed = ["user_stated", "librarian_inferred", "user_confirmed", "immutable_document"];
596
+ if (allowed.includes(raw)) return raw;
597
+ const where = ctx !== void 0 ? ` for entity "${ctx.entityId}" fact "${ctx.factId}"` : "";
598
+ throw new Error(
599
+ `importDump: invalid source_type "${raw}"${where} (expected one of: ${allowed.join(", ")}, or legacy aliases user_document / agent_inferred)`
600
+ );
601
+ }
602
+ async assertNoLegacySourceTypes() {
603
+ const legacyProbe = await this.db.getFirstAsync(
604
+ `SELECT 1 AS one FROM ${this.prefix}entries
605
+ WHERE source_type IN ('user_document', 'agent_inferred')
606
+ LIMIT 1`,
607
+ []
608
+ );
609
+ if (!legacyProbe) return;
610
+ const legacyCount = await this.db.getFirstAsync(
611
+ `SELECT COUNT(*) as count FROM ${this.prefix}entries
612
+ WHERE source_type IN ('user_document', 'agent_inferred')`,
613
+ []
614
+ );
615
+ const count = legacyCount?.count ?? 0;
616
+ const migrationSQL = `
617
+ -- Migrate legacy source_type values (targets your WikiMemory prefix: ${this.prefix})
618
+ UPDATE ${this.prefix}entries SET source_type = 'immutable_document' WHERE source_type = 'user_document';
619
+ UPDATE ${this.prefix}entries SET source_type = 'librarian_inferred' WHERE source_type = 'agent_inferred';
620
+ `.trim();
621
+ throw new Error(
622
+ `Database contains ${count} entries with legacy source_type values ('user_document' or 'agent_inferred'). These enum values were renamed in this release. Running without migration would allow legacy 'user_document' facts to bypass immutability guards, causing data corruption.
623
+
624
+ ${migrationSQL}
625
+
626
+ After running the migration SQL, restart your application.`
627
+ );
628
+ }
590
629
  async _notifyEmbeddingPersisted(entityId, factId, vector) {
591
630
  if (!this.options.vectorRanker?.onEmbeddingPersisted) return;
592
631
  const vectorCopy = vector ? vector.slice() : null;
@@ -688,6 +727,9 @@ var _WikiMemory = class _WikiMemory {
688
727
  );
689
728
  }
690
729
  }
730
+ if (entriesExistedBeforeSetup) {
731
+ await this.assertNoLegacySourceTypes();
732
+ }
691
733
  const rows = await this.db.getAllAsync(`
692
734
  SELECT rowid, source_ref FROM ${this.prefix}entries
693
735
  WHERE source_ref IS NOT NULL
@@ -775,6 +817,24 @@ var _WikiMemory = class _WikiMemory {
775
817
  }
776
818
  return false;
777
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
+ }
778
838
  _validatePruneDuration(value, name) {
779
839
  if (value !== null && value !== void 0 && (typeof value !== "number" || !isFinite(value) || value < 0)) {
780
840
  throw new Error(`Invalid ${name}: must be a non-negative finite number or null`);
@@ -824,7 +884,7 @@ var _WikiMemory = class _WikiMemory {
824
884
  const cutoff = now - retainSoftDeletedFor * 864e5;
825
885
  const entriesToDelete = await this.db.getAllAsync(
826
886
  `SELECT id, entity_id FROM ${this.prefix}entries
827
- WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at < ?`,
887
+ WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at <= ?`,
828
888
  [entityId, cutoff]
829
889
  );
830
890
  const succeeded = [];
@@ -844,7 +904,7 @@ var _WikiMemory = class _WikiMemory {
844
904
  const chunk = succeeded.slice(i, i + chunkSize);
845
905
  const placeholders = chunk.map(() => "?").join(",");
846
906
  const entryResult = await this.db.runAsync(
847
- `DELETE FROM ${this.prefix}entries WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at < ? AND id IN (${placeholders})`,
907
+ `DELETE FROM ${this.prefix}entries WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at <= ? AND id IN (${placeholders})`,
848
908
  [entityId, cutoff, ...chunk.map((r) => r.id)]
849
909
  );
850
910
  deletedEntries += entryResult.changes;
@@ -852,7 +912,7 @@ var _WikiMemory = class _WikiMemory {
852
912
  }
853
913
  const taskResult = await this.db.runAsync(
854
914
  `DELETE FROM ${this.prefix}tasks
855
- WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at < ?`,
915
+ WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at <= ?`,
856
916
  [entityId, cutoff]
857
917
  );
858
918
  deletedTasks = taskResult.changes;
@@ -890,7 +950,7 @@ var _WikiMemory = class _WikiMemory {
890
950
  const cutoff = now - retainEventsFor * 864e5;
891
951
  const eventResult = await this.db.runAsync(
892
952
  `DELETE FROM ${this.prefix}events
893
- WHERE entity_id = ? AND created_at < ?`,
953
+ WHERE entity_id = ? AND created_at <= ?`,
894
954
  [entityId, cutoff]
895
955
  );
896
956
  deletedEvents = eventResult.changes;
@@ -1517,7 +1577,11 @@ var _WikiMemory = class _WikiMemory {
1517
1577
  const jobKey = this._librarianKey(entityId);
1518
1578
  if (!this.activeMaintenanceJobs.has(jobKey) && !this.activeMaintenanceJobs.has(this._pruneKey(entityId)) && !this._isReembedActive(entityId) && !this._isImportActiveFor(entityId) && !this._isForgetActiveFor(entityId)) {
1519
1579
  this.activeMaintenanceJobs.add(jobKey);
1520
- 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
+ });
1521
1585
  }
1522
1586
  }
1523
1587
  }
@@ -1536,6 +1600,7 @@ var _WikiMemory = class _WikiMemory {
1536
1600
  const healKey = this._healKey(entityId);
1537
1601
  if (!this.activeMaintenanceJobs.has(healKey)) {
1538
1602
  this.activeMaintenanceJobs.add(healKey);
1603
+ this._notifyStatusSubscribers(entityId);
1539
1604
  try {
1540
1605
  await this._doRunHeal(entityId);
1541
1606
  await this.db.runAsync(`
@@ -1545,6 +1610,7 @@ var _WikiMemory = class _WikiMemory {
1545
1610
  `, [entityId, currentEventCount, currentEventCount]);
1546
1611
  } finally {
1547
1612
  this.activeMaintenanceJobs.delete(healKey);
1613
+ this._notifyStatusSubscribers(entityId);
1548
1614
  }
1549
1615
  }
1550
1616
  }
@@ -1591,7 +1657,7 @@ ${JSON.stringify(currentFacts, null, 2)}`;
1591
1657
  let skip = false;
1592
1658
  if (newTokens.size >= MIN_TOKENS_TO_QUALIFY) {
1593
1659
  for (const existing of currentFactsRows) {
1594
- if (existing.source_type !== "agent_inferred") continue;
1660
+ if (existing.source_type !== "librarian_inferred") continue;
1595
1661
  const existingTokens = titleTokens(existing.title);
1596
1662
  if (existingTokens.size >= MIN_TOKENS_TO_QUALIFY) {
1597
1663
  if (jaccardScore(newTokens, existingTokens) >= FUZZY_THRESHOLD) {
@@ -1606,7 +1672,7 @@ ${JSON.stringify(currentFacts, null, 2)}`;
1606
1672
  await this.db.runAsync(`
1607
1673
  INSERT INTO ${this.prefix}entries (id, entity_id, title, body, tags, confidence, source_type, created_at, updated_at)
1608
1674
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1609
- `, [id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "agent_inferred", now, now]);
1675
+ `, [id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "librarian_inferred", now, now]);
1610
1676
  insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
1611
1677
  }
1612
1678
  for (const task of validTasks) {
@@ -1639,25 +1705,25 @@ ${JSON.stringify(currentFacts, null, 2)}`;
1639
1705
  if (orphanAfterDays !== null) {
1640
1706
  const orphanThreshold = now - orphanAfterDays * MS_PER_DAY;
1641
1707
  await this.db.runAsync(`
1642
- UPDATE ${this.prefix}entries
1643
- SET deleted_at = ?, updated_at = ?
1644
- WHERE entity_id = ? AND access_count = 0 AND created_at < ? AND source_type != 'user_document' AND deleted_at IS NULL
1708
+ UPDATE ${this.prefix}entries
1709
+ SET deleted_at = ?, updated_at = ?
1710
+ WHERE entity_id = ? AND access_count = 0 AND created_at <= ? AND source_type != 'immutable_document' AND deleted_at IS NULL
1645
1711
  `, [now, now, entityId, orphanThreshold]);
1646
1712
  }
1647
1713
  if (staleInferredAfterDays !== null) {
1648
1714
  const staleThreshold = now - staleInferredAfterDays * MS_PER_DAY;
1649
1715
  await this.db.runAsync(`
1650
- UPDATE ${this.prefix}entries
1651
- SET confidence = 'tentative', updated_at = ?
1652
- WHERE entity_id = ? AND confidence = 'inferred' AND (last_accessed_at < ? OR (last_accessed_at IS NULL AND created_at < ?)) AND source_type != 'user_document' AND deleted_at IS NULL
1716
+ UPDATE ${this.prefix}entries
1717
+ SET confidence = 'tentative', updated_at = ?
1718
+ WHERE entity_id = ? AND confidence = 'inferred' AND (last_accessed_at <= ? OR (last_accessed_at IS NULL AND created_at <= ?)) AND source_type != 'immutable_document' AND deleted_at IS NULL
1653
1719
  `, [now, entityId, staleThreshold, staleThreshold]);
1654
1720
  }
1655
1721
  });
1656
1722
  const allFactsRows = await this.db.getAllAsync(`SELECT * FROM ${this.prefix}entries WHERE entity_id = ? AND deleted_at IS NULL`, [entityId]);
1657
1723
  const allTasks = await this.db.getAllAsync(`SELECT * FROM ${this.prefix}tasks WHERE entity_id = ? AND status IN ('pending', 'in_progress') AND deleted_at IS NULL`, [entityId]);
1658
1724
  const recentEvents = await this.db.getAllAsync(`SELECT * FROM ${this.prefix}events WHERE entity_id = ? ORDER BY created_at DESC LIMIT 20`, [entityId]);
1659
- const healCandidates = allFactsRows.filter((f) => f.source_type !== "user_document");
1660
- const documentAnchors = allFactsRows.filter((f) => f.source_type === "user_document").map(({ id, title, source_ref }) => ({ id, title, source_ref }));
1725
+ const healCandidates = allFactsRows.filter((f) => f.source_type !== "immutable_document");
1726
+ const documentAnchors = allFactsRows.filter((f) => f.source_type === "immutable_document").map(({ id, title, source_ref }) => ({ id, title, source_ref }));
1661
1727
  const userPrompt = `Heal Candidates:
1662
1728
  ${JSON.stringify(healCandidates.map((f) => {
1663
1729
  const { embedding: _embedding, embedding_blob: _blob, ...rest } = f;
@@ -1700,7 +1766,7 @@ The following document anchors are provided for contradiction detection only. Do
1700
1766
  await this.db.runAsync(`
1701
1767
  INSERT INTO ${this.prefix}entries (id, entity_id, title, body, tags, confidence, source_type, created_at, updated_at)
1702
1768
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
1703
- `, [id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "agent_inferred", now, now]);
1769
+ `, [id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "librarian_inferred", now, now]);
1704
1770
  insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
1705
1771
  }
1706
1772
  });
@@ -1736,10 +1802,12 @@ The following document anchors are provided for contradiction detection only. Do
1736
1802
  throw new WikiBusyError("forget", entityId);
1737
1803
  }
1738
1804
  this.activeMaintenanceJobs.add(jobKey);
1805
+ this._notifyStatusSubscribers(entityId);
1739
1806
  try {
1740
1807
  await this._doRunLibrarian(entityId);
1741
1808
  } finally {
1742
1809
  this.activeMaintenanceJobs.delete(jobKey);
1810
+ this._notifyStatusSubscribers(entityId);
1743
1811
  }
1744
1812
  }
1745
1813
  async runHeal(entityId) {
@@ -1760,10 +1828,12 @@ The following document anchors are provided for contradiction detection only. Do
1760
1828
  throw new WikiBusyError("forget", entityId);
1761
1829
  }
1762
1830
  this.activeMaintenanceJobs.add(jobKey);
1831
+ this._notifyStatusSubscribers(entityId);
1763
1832
  try {
1764
1833
  await this._doRunHeal(entityId);
1765
1834
  } finally {
1766
1835
  this.activeMaintenanceJobs.delete(jobKey);
1836
+ this._notifyStatusSubscribers(entityId);
1767
1837
  }
1768
1838
  }
1769
1839
  async runReembed(entityId, opts) {
@@ -1903,6 +1973,40 @@ The following document anchors are provided for contradiction detection only. Do
1903
1973
  heal: this.activeMaintenanceJobs.has(this._healKey(entityId))
1904
1974
  };
1905
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
+ }
1906
2010
  clearVectorCache() {
1907
2011
  this.vectorCache.clear();
1908
2012
  }
@@ -2000,6 +2104,7 @@ The following document anchors are provided for contradiction detection only. Do
2000
2104
  this.activeMaintenanceJobs.add(this._importKey(entityId));
2001
2105
  }
2002
2106
  try {
2107
+ await this.assertNoLegacySourceTypes();
2003
2108
  for (const [entityId, bundle] of Object.entries(dump.entities)) {
2004
2109
  await this._doImportEntity(entityId, bundle, merge);
2005
2110
  }
@@ -2053,6 +2158,10 @@ The following document anchors are provided for contradiction detection only. Do
2053
2158
  }
2054
2159
  }
2055
2160
  for (const fact of bundle.facts) {
2161
+ const sourceType = this._normalizeImportedSourceType(String(fact.source_type), {
2162
+ entityId,
2163
+ factId: fact.id
2164
+ });
2056
2165
  const tagsJson = JSON.stringify(Array.isArray(fact.tags) ? fact.tags : []);
2057
2166
  const safeUpdatedAt = Number.isFinite(fact.updated_at) ? fact.updated_at : 0;
2058
2167
  const existing = existingFactsById.get(fact.id);
@@ -2101,14 +2210,14 @@ The following document anchors are provided for contradiction detection only. Do
2101
2210
  if (blobData != null) {
2102
2211
  await this.db.runAsync(
2103
2212
  `UPDATE ${this.prefix}entries SET entity_id = ?, title = ?, body = ?, tags = ?, confidence = ?, source_type = ?, source_hash = ?, source_ref = ?, created_at = ?, updated_at = ?, last_accessed_at = ?, access_count = ?, deleted_at = ?, embedding_blob = ?, embedding = NULL WHERE id = ?`,
2104
- [entityId, fact.title, fact.body, tagsJson, fact.confidence, fact.source_type, fact.source_hash, fact.source_ref, fact.created_at, safeUpdatedAt, fact.last_accessed_at, fact.access_count, fact.deleted_at, blobData, fact.id]
2213
+ [entityId, fact.title, fact.body, tagsJson, fact.confidence, sourceType, fact.source_hash, fact.source_ref, fact.created_at, safeUpdatedAt, fact.last_accessed_at, fact.access_count, fact.deleted_at, blobData, fact.id]
2105
2214
  );
2106
2215
  factsWithPreservedBlob.set(fact.id, blobData);
2107
2216
  if (!fact.deleted_at) preservedBlobDims.add(blobData.byteLength / 4);
2108
2217
  } else {
2109
2218
  await this.db.runAsync(
2110
2219
  `UPDATE ${this.prefix}entries SET entity_id = ?, title = ?, body = ?, tags = ?, confidence = ?, source_type = ?, source_hash = ?, source_ref = ?, created_at = ?, updated_at = ?, last_accessed_at = ?, access_count = ?, deleted_at = ?, embedding_blob = NULL, embedding = NULL WHERE id = ?`,
2111
- [entityId, fact.title, fact.body, tagsJson, fact.confidence, fact.source_type, fact.source_hash, fact.source_ref, fact.created_at, safeUpdatedAt, fact.last_accessed_at, fact.access_count, fact.deleted_at, fact.id]
2220
+ [entityId, fact.title, fact.body, tagsJson, fact.confidence, sourceType, fact.source_hash, fact.source_ref, fact.created_at, safeUpdatedAt, fact.last_accessed_at, fact.access_count, fact.deleted_at, fact.id]
2112
2221
  );
2113
2222
  }
2114
2223
  existingFactsById.set(fact.id, { id: fact.id, entity_id: entityId, updated_at: safeUpdatedAt });
@@ -2118,14 +2227,14 @@ The following document anchors are provided for contradiction detection only. Do
2118
2227
  if (blobData != null) {
2119
2228
  await this.db.runAsync(
2120
2229
  `INSERT INTO ${this.prefix}entries (id, entity_id, title, body, tags, confidence, source_type, source_hash, source_ref, created_at, updated_at, last_accessed_at, access_count, deleted_at, embedding_blob) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2121
- [fact.id, entityId, fact.title, fact.body, tagsJson, fact.confidence, fact.source_type, fact.source_hash, fact.source_ref, fact.created_at, safeUpdatedAt, fact.last_accessed_at, fact.access_count, fact.deleted_at, blobData]
2230
+ [fact.id, entityId, fact.title, fact.body, tagsJson, fact.confidence, sourceType, fact.source_hash, fact.source_ref, fact.created_at, safeUpdatedAt, fact.last_accessed_at, fact.access_count, fact.deleted_at, blobData]
2122
2231
  );
2123
2232
  factsWithPreservedBlob.set(fact.id, blobData);
2124
2233
  if (!fact.deleted_at) preservedBlobDims.add(blobData.byteLength / 4);
2125
2234
  } else {
2126
2235
  await this.db.runAsync(
2127
2236
  `INSERT INTO ${this.prefix}entries (id, entity_id, title, body, tags, confidence, source_type, source_hash, source_ref, created_at, updated_at, last_accessed_at, access_count, deleted_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2128
- [fact.id, entityId, fact.title, fact.body, tagsJson, fact.confidence, fact.source_type, fact.source_hash, fact.source_ref, fact.created_at, safeUpdatedAt, fact.last_accessed_at, fact.access_count, fact.deleted_at]
2237
+ [fact.id, entityId, fact.title, fact.body, tagsJson, fact.confidence, sourceType, fact.source_hash, fact.source_ref, fact.created_at, safeUpdatedAt, fact.last_accessed_at, fact.access_count, fact.deleted_at]
2129
2238
  );
2130
2239
  }
2131
2240
  existingFactsById.set(fact.id, { id: fact.id, entity_id: entityId, updated_at: safeUpdatedAt });
@@ -2423,6 +2532,7 @@ The following document anchors are provided for contradiction detection only. Do
2423
2532
  throw new WikiBusyError("forget", entityId);
2424
2533
  }
2425
2534
  this.activeIngestJobs.add(jobKey);
2535
+ this._notifyStatusSubscribers(entityId);
2426
2536
  try {
2427
2537
  const { chunks, truncated } = chunkText(params.documentChunk, maxChunkLength, chunkOverlap);
2428
2538
  if (chunks.length === 0) {
@@ -2472,7 +2582,7 @@ ${chunk}`;
2472
2582
  await this.db.runAsync(
2473
2583
  `INSERT INTO ${this.prefix}entries (id, entity_id, title, body, tags, confidence, source_type, source_hash, source_ref, created_at, updated_at)
2474
2584
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2475
- [id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "user_document", sourceHash, sourceRef, now, now]
2585
+ [id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "immutable_document", sourceHash, sourceRef, now, now]
2476
2586
  );
2477
2587
  insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
2478
2588
  }
@@ -2494,6 +2604,7 @@ ${chunk}`;
2494
2604
  return { truncated, chunks: chunks.length };
2495
2605
  } finally {
2496
2606
  this.activeIngestJobs.delete(jobKey);
2607
+ this._notifyStatusSubscribers(entityId);
2497
2608
  }
2498
2609
  }
2499
2610
  };
@@ -2731,6 +2842,6 @@ function createWiki(db, options) {
2731
2842
  return new WikiMemory(db, options);
2732
2843
  }
2733
2844
 
2734
- export { PrunePartialFailureError, WikiBusyError, WikiMemory, createWiki, formatContext, formatMemoryDump };
2845
+ export { PrunePartialFailureError, WikiBusyError, WikiMemory, createWiki, formatContext, formatMemoryDump, parseEmbedding };
2735
2846
  //# sourceMappingURL=index.mjs.map
2736
2847
  //# sourceMappingURL=index.mjs.map