@equationalapplications/core-llm-wiki 5.1.0 → 5.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.
@@ -64,6 +64,15 @@ var WikiBusyError = class extends Error {
64
64
  this.entityId = entityId;
65
65
  }
66
66
  };
67
+ var WikiDuplicateHashError = class extends Error {
68
+ constructor(params) {
69
+ super(`Duplicate source hash for entity ${params.entityId}; another ref already holds this content`);
70
+ this.name = "WikiDuplicateHashError";
71
+ this.canonical = params.canonical;
72
+ this.sourceHash = params.sourceHash;
73
+ this.entityId = params.entityId;
74
+ }
75
+ };
67
76
  var WikiTransactionError = class extends Error {
68
77
  constructor(message, options) {
69
78
  super(message, options);
@@ -693,6 +702,19 @@ var JobManager = class {
693
702
  return this.activeMaintenanceJobs.has(this.lockKeyFns[operation](entityId)) || this.activeMaintenanceJobs.has(this._pruneKey(entityId)) || this._isReembedActive(entityId) || this._isImportActiveFor(entityId) || this._isForgetActiveFor(entityId);
694
703
  case "prune":
695
704
  return this.activeMaintenanceJobs.has(this._pruneKey(entityId)) || this.activeMaintenanceJobs.has(this._librarianKey(entityId)) || this.activeMaintenanceJobs.has(this._healKey(entityId)) || this.activeMaintenanceJobs.has(this._ontologyBackfillKey(entityId)) || this._isReembedActive(entityId) || this._isIngestActiveFor(entityId) || this._isImportActiveFor(entityId) || this._isForgetActiveFor(entityId);
705
+ case "reembed":
706
+ return this.activeMaintenanceJobs.has(this._reembedKey(entityId)) || this.activeMaintenanceJobs.has(this._globalReembedKey()) || this.activeMaintenanceJobs.has(this._pruneKey(entityId)) || this.activeMaintenanceJobs.has(this._librarianKey(entityId)) || this.activeMaintenanceJobs.has(this._healKey(entityId)) || this.activeMaintenanceJobs.has(this._ontologyBackfillKey(entityId)) || this._isIngestActiveFor(entityId) || this._isImportActiveFor(entityId) || this._isForgetActiveFor(entityId);
707
+ case "global_reembed":
708
+ return this.activeMaintenanceJobs.has(this._globalReembedKey()) || this._isAnyMaintenanceActiveWithSuffix(":reembed") || this._isAnyMaintenanceActiveWithSuffix(":prune") || this._isAnyMaintenanceActiveWithSuffix(":librarian") || this._isAnyMaintenanceActiveWithSuffix(":heal") || this._isAnyMaintenanceActiveWithSuffix(":ontologyBackfill") || this.activeIngestJobs.size > 0 || this._isAnyMaintenanceActiveWithSuffix(":import") || this._isAnyMaintenanceActiveWithSuffix(":forget");
709
+ case "import":
710
+ case "forget": {
711
+ const selfKey = operation === "import" ? this._importKey(entityId) : this._forgetKey(entityId);
712
+ return this.activeMaintenanceJobs.has(selfKey) || this.activeMaintenanceJobs.has(this._librarianKey(entityId)) || this.activeMaintenanceJobs.has(this._healKey(entityId)) || this.activeMaintenanceJobs.has(this._ontologyBackfillKey(entityId)) || this.activeMaintenanceJobs.has(this._pruneKey(entityId)) || this._isReembedActive(entityId) || this._isIngestActiveFor(entityId) || this._isImportActiveFor(entityId) || this._isForgetActiveFor(entityId);
713
+ }
714
+ case "global_import":
715
+ return this.activeMaintenanceJobs.has(this._globalImportKey());
716
+ case "ingest":
717
+ return this._hasIngestJob(entityId) || this.activeMaintenanceJobs.has(this._pruneKey(entityId)) || this._isReembedActive(entityId) || this._isImportActiveFor(entityId) || this._isForgetActiveFor(entityId);
696
718
  default:
697
719
  return false;
698
720
  }
@@ -1159,11 +1181,23 @@ var IngestionService = class {
1159
1181
  this.ontologyService = ontologyService;
1160
1182
  this.promptService = promptService ?? new PromptService(this.options.config?.prompts);
1161
1183
  }
1162
- async ingestDocument(entityId, params) {
1184
+ async ingestDocument(entityId, params, opts) {
1163
1185
  const sourceRef = normalizeSourceRef(params.sourceRef);
1164
1186
  if (!sourceRef) throw new Error("Invalid sourceRef");
1165
1187
  const sourceHash = normalizeSourceHash(params.sourceHash);
1166
1188
  if (!sourceHash) throw new Error("Invalid sourceHash (must be 64-char hex string)");
1189
+ const onDuplicateHash = opts?.onDuplicateHash ?? "ingest";
1190
+ if (onDuplicateHash !== "ingest") {
1191
+ const refs = await this.entryRepo.findSourceRefsByHash(entityId, sourceHash);
1192
+ const others = refs.filter((r) => r !== sourceRef);
1193
+ if (others.length > 0) {
1194
+ const canonical = others[0];
1195
+ if (onDuplicateHash === "throw") {
1196
+ throw new WikiDuplicateHashError({ canonical, sourceHash, entityId });
1197
+ }
1198
+ return { truncated: false, chunks: 0, duplicateOf: canonical };
1199
+ }
1200
+ }
1167
1201
  const maxChunkLength = params.maxChunkLength ?? this.options.config?.maxChunkLength ?? DEFAULT_MAX_CHUNK_LENGTH;
1168
1202
  const rawOverlap = params.chunkOverlap ?? this.options.config?.chunkOverlap ?? DEFAULT_CHUNK_OVERLAP;
1169
1203
  const chunkOverlap = Math.min(
@@ -1739,10 +1773,13 @@ var MaintenanceService = class {
1739
1773
  this.jobManager.releaseLock(op, entityId ?? "*");
1740
1774
  }
1741
1775
  }
1742
- async forget(entityId, params) {
1776
+ async forget(entityId, params, opts) {
1743
1777
  if (params.clearAll && (params.entryId !== void 0 || params.taskId !== void 0 || params.sourceRef !== void 0 || params.sourceHash !== void 0)) {
1744
1778
  throw new Error("forget() clearAll is mutually exclusive with entryId, taskId, sourceRef, and sourceHash");
1745
1779
  }
1780
+ if (opts?.dryRun === true) {
1781
+ return this.forgetDryRun(entityId, params);
1782
+ }
1746
1783
  this.jobManager.acquireLock("forget", entityId);
1747
1784
  try {
1748
1785
  const now = Date.now();
@@ -1803,11 +1840,42 @@ var MaintenanceService = class {
1803
1840
  throw new Error(`forget(${entityId}/${factId}) failed: ANN cleanup hook rejected`, { cause: this._sanitizeRankerError(hookErr) });
1804
1841
  }
1805
1842
  }
1806
- return { deleted: { entries: deletedEntries, tasks: deletedTasks } };
1843
+ return params.clearAll ? { deleted: { entries: deletedEntries, tasks: deletedTasks }, metadataReset: true } : { deleted: { entries: deletedEntries, tasks: deletedTasks } };
1807
1844
  } finally {
1808
1845
  this.jobManager.releaseLock("forget", entityId);
1809
1846
  }
1810
1847
  }
1848
+ /**
1849
+ * Read-only dry-run preview of {@link forget}. Returns the same shape as a
1850
+ * real call would, without acquiring the lock, opening a transaction, staging
1851
+ * outbox events, or firing embedding hooks. Counts may be off-by-N during a
1852
+ * concurrent real forget — this is accepted, not considered a bug.
1853
+ *
1854
+ * `metadataReset` mirrors the real call: only `clearAll: true` returns true,
1855
+ * because only `clearAll` actually resets the metadata checkpoint.
1856
+ * `standard` (sourceRef/sourceHash) returns no metadataReset field, just like
1857
+ * the real call. `entryId`/`taskId` selectors are rejected with a clear error
1858
+ * (the spec keeps dry-run narrow).
1859
+ */
1860
+ async forgetDryRun(entityId, params) {
1861
+ if (params.entryId !== void 0 || params.taskId !== void 0) {
1862
+ throw new Error("forget({ dryRun: true }) does not support entryId/taskId selectors; use sourceRef/sourceHash or clearAll");
1863
+ }
1864
+ if (params.clearAll) {
1865
+ const entries2 = await this.entryRepo.countLiveByEntityId(entityId);
1866
+ const tasks = await this.taskRepo.countLiveByEntityId(entityId);
1867
+ return { deleted: { entries: entries2, tasks }, metadataReset: true };
1868
+ }
1869
+ const sourceRef = params.sourceRef !== void 0 ? normalizeSourceRef(params.sourceRef) : null;
1870
+ if (params.sourceRef !== void 0 && !sourceRef) throw new Error("Invalid sourceRef");
1871
+ const sourceHash = params.sourceHash !== void 0 ? normalizeSourceHash(params.sourceHash) : null;
1872
+ if (params.sourceHash !== void 0 && !sourceHash) throw new Error("Invalid sourceHash (must be 64-char hex string)");
1873
+ if (sourceRef === null && sourceHash === null) {
1874
+ return { deleted: { entries: 0, tasks: 0 } };
1875
+ }
1876
+ const entries = await this.entryRepo.countLiveBySource(entityId, sourceRef, sourceHash);
1877
+ return { deleted: { entries, tasks: 0 } };
1878
+ }
1811
1879
  /** Core librarian pass (locks handled by {@link runLibrarian}). Package-internal orchestration hook. */
1812
1880
  async doRunLibrarian(entityId, promptOverride) {
1813
1881
  const events = await this.eventRepo.getRecent(entityId, 50);
@@ -2037,10 +2105,10 @@ var MaintenanceService = class {
2037
2105
  const validNewFacts = newFacts.map(validateFact).filter((f) => f !== null);
2038
2106
  const insertedFacts = [];
2039
2107
  const uniqueDeletedFactIds = Array.from(new Set(safeDeleted));
2040
- const healFactsForDedupe = (await this.entryRepo.findInferredTitlesByEntityId(entityId)).filter((f) => !safeDeletedSet.has(f.id));
2041
2108
  await this.db.withTransactionAsync(async (tx) => {
2042
2109
  await this.entryRepo.downgradeByIds(safeDowngraded, entityId, tx);
2043
2110
  await this.entryRepo.softDeleteByIds(safeDeleted, entityId, tx);
2111
+ const healFactsForDedupe = await this.entryRepo.findInferredTitlesByEntityId(entityId, tx);
2044
2112
  for (const fact of validNewFacts) {
2045
2113
  const newTokens = titleTokens(fact.title);
2046
2114
  let skip = false;
@@ -2531,6 +2599,15 @@ var ImportExportService = class {
2531
2599
  const safeTitle = clip(String(fact.title ?? ""), IMPORT_TITLE_MAX);
2532
2600
  const safeBody = clip(String(fact.body ?? ""), IMPORT_BODY_MAX);
2533
2601
  clippedTextByFactId.set(fact.id, { title: safeTitle, body: safeBody });
2602
+ let normalizedSourceRef = null;
2603
+ if (fact.source_ref !== null && fact.source_ref !== void 0) {
2604
+ normalizedSourceRef = normalizeSourceRef(fact.source_ref);
2605
+ if (normalizedSourceRef === null) {
2606
+ throw new Error(
2607
+ `importDump: invalid source_ref ${JSON.stringify(fact.source_ref)} for entity "${entityId}" fact "${fact.id}" (must normalize to a non-empty string; see ingestDocument's sourceRef validation)`
2608
+ );
2609
+ }
2610
+ }
2534
2611
  const factObj = {
2535
2612
  id: fact.id,
2536
2613
  entity_id: entityId,
@@ -2540,7 +2617,7 @@ var ImportExportService = class {
2540
2617
  confidence: fact.confidence,
2541
2618
  source_type: sourceType,
2542
2619
  source_hash: fact.source_hash,
2543
- source_ref: fact.source_ref,
2620
+ source_ref: normalizedSourceRef,
2544
2621
  created_at: fact.created_at,
2545
2622
  updated_at: safeUpdatedAt,
2546
2623
  last_accessed_at: fact.last_accessed_at,
@@ -3553,6 +3630,6 @@ var WriteService = class {
3553
3630
  }
3554
3631
  };
3555
3632
 
3556
- export { BaseRepository, DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, EmbeddingService, HEAL_BATCH_SIZE, 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, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveNodeType, safeSlice, validateInlineEdges, validateManifest };
3557
- //# sourceMappingURL=chunk-DM7BKDCV.mjs.map
3558
- //# sourceMappingURL=chunk-DM7BKDCV.mjs.map
3633
+ export { BaseRepository, DEFAULT_CHUNK_OVERLAP, DEFAULT_MAX_CHUNK_LENGTH, EmbeddingService, HEAL_BATCH_SIZE, 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, WikiDuplicateHashError, WikiTransactionError, WriteService, __privateAdd, __privateGet, __privateSet, chunkText, configureRandomSource, emptyManifest, entitySummaryMetaKey, extractSqliteCode, generateId, normalizeSourceHash, normalizeSourceRef, normalizeTitleKey, parseEmbedding, resolveEdgeDefinitions, resolveNodeType, safeSlice, validateInlineEdges, validateManifest };
3634
+ //# sourceMappingURL=chunk-J3N3WRK7.mjs.map
3635
+ //# sourceMappingURL=chunk-J3N3WRK7.mjs.map