@equationalapplications/core-llm-wiki 4.23.0 → 5.0.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, generateId, BaseRepository, emptyManifest, resolveNodeType, validateInlineEdges, resolveEdgeDefinitions, normalizeTitleKey, WikiTransactionError, extractSqliteCode } from './chunk-MYZJLVX4.mjs';
2
- export { HOOK_TIMEOUT_MARKER, ONTOLOGY_BACKFILL_BATCH_SIZE, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS, ONTOLOGY_BACKFILL_RECHECK_MS, ONTOLOGY_BACKFILL_SYSTEM_PROMPT, PromptService, PrunePartialFailureError, WikiBusyError, WikiTransactionError, configureRandomSource, parseEmbedding, validateManifest } from './chunk-MYZJLVX4.mjs';
1
+ import { __privateAdd, MetadataRepository, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, entitySummaryMetaKey, generateId, BaseRepository, emptyManifest, resolveNodeType, validateInlineEdges, resolveEdgeDefinitions, normalizeTitleKey, WikiTransactionError, extractSqliteCode } from './chunk-W53E3I44.mjs';
2
+ export { HEAL_BATCH_SIZE, 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, WikiTransactionError, configureRandomSource, parseEmbedding, validateManifest } from './chunk-W53E3I44.mjs';
3
3
  import { appendRelatedSection, buildConceptDocument, buildLogMd, buildEntityIndexMd, buildRootIndexMd, isAllowedOkfPath, parseRootIndexMd, parseEntityIndexMd, parseConcept, splitRelatedSection, extractMarkdownLinks, parseLogMd, parseEventIdComment, appendEventIdComment } from '@equationalapplications/core-okf';
4
4
 
5
5
  // src/db/schema.ts
@@ -23,7 +23,8 @@ async function setupDatabase(db, prefix) {
23
23
  embedding TEXT,
24
24
  embedding_blob BLOB,
25
25
  okf_type TEXT,
26
- ontology_checked_at INTEGER
26
+ ontology_checked_at INTEGER,
27
+ heal_checked_at INTEGER
27
28
  );
28
29
 
29
30
  CREATE INDEX IF NOT EXISTS ${prefix}entries_entity_idx ON ${prefix}entries(entity_id);
@@ -263,6 +264,20 @@ var MIGRATIONS = [
263
264
  );
264
265
  }
265
266
  }
267
+ },
268
+ {
269
+ version: 8,
270
+ description: "Add heal_checked_at to entries for heal recheck cooldown",
271
+ run: async (db, prefix) => {
272
+ const cols = await db.getAllAsync(
273
+ `PRAGMA table_info(${prefix}entries)`
274
+ );
275
+ if (!cols.some((c) => c.name === "heal_checked_at")) {
276
+ await db.execAsync(
277
+ `ALTER TABLE ${prefix}entries ADD COLUMN heal_checked_at INTEGER`
278
+ );
279
+ }
280
+ }
266
281
  }
267
282
  ];
268
283
  for (let i = 1; i < MIGRATIONS.length; i++) {
@@ -604,17 +619,21 @@ var EntryRepository = class extends BaseRepository {
604
619
  }
605
620
  /**
606
621
  * Fetch live, mutable entries for an entity — everything heal is allowed to
607
- * downgrade or delete. Heal previously loaded every row via
608
- * findAllByEntityId and filtered in JS, which on a document-heavy corpus
609
- * meant loading 2560 rows to keep 31.
622
+ * downgrade or delete — oldest first, capped at `limit`, skipping anything
623
+ * stamped inside the recheck cooldown (heal_checked_at > recheckCutoff).
624
+ *
625
+ * Oldest-first rather than newest-first: with a cooldown in place, newest-first
626
+ * would keep re-selecting recently-touched facts every pass while older ones
627
+ * wait indefinitely to even enter a batch.
610
628
  */
611
- async findHealCandidatesByEntityId(entityId, tx) {
629
+ async findHealCandidatesByEntityId(entityId, limit, recheckCutoff, tx) {
612
630
  const executor = this.getExecutor(tx);
613
631
  const rows = await executor.getAllAsync(
614
632
  `SELECT * FROM ${this.prefix}entries
615
633
  WHERE entity_id = ? AND deleted_at IS NULL AND source_type != 'immutable_document'
616
- ORDER BY updated_at DESC`,
617
- [entityId]
634
+ AND (heal_checked_at IS NULL OR heal_checked_at <= ?)
635
+ ORDER BY updated_at ASC LIMIT ?`,
636
+ [entityId, recheckCutoff, limit]
618
637
  );
619
638
  return rows.map(mapRowToFact);
620
639
  }
@@ -777,6 +796,10 @@ var EntryRepository = class extends BaseRepository {
777
796
  /**
778
797
  * Downgrade stale inferred entries to 'tentative'.
779
798
  * Used by MaintenanceService.doRunHeal().
799
+ *
800
+ * Returns the ids it downgraded so the caller can fold them into
801
+ * {@link HealResult.downgraded} without double-counting a fact the model also
802
+ * downgraded in the same pass.
780
803
  */
781
804
  async downgradeStaleInferred(entityId, staleThreshold, tx) {
782
805
  const executor = this.getExecutor(tx);
@@ -788,8 +811,8 @@ var EntryRepository = class extends BaseRepository {
788
811
  AND source_type != 'immutable_document' AND deleted_at IS NULL`,
789
812
  [entityId, staleThreshold, staleThreshold]
790
813
  );
791
- if (eligibleRows.length === 0) return 0;
792
- const result = await executor.runAsync(
814
+ if (eligibleRows.length === 0) return [];
815
+ await executor.runAsync(
793
816
  `UPDATE ${this.prefix}entries
794
817
  SET confidence = 'tentative', updated_at = ?
795
818
  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`,
@@ -804,7 +827,7 @@ var EntryRepository = class extends BaseRepository {
804
827
  payload: { id: row.id, entity_id: entityId, confidence: "tentative", updated_at: now }
805
828
  }, tx);
806
829
  }
807
- return result.changes;
830
+ return eligibleRows.map((r) => r.id);
808
831
  }
809
832
  /**
810
833
  * Downgrade specific entries to 'tentative' by IDs.
@@ -1123,6 +1146,58 @@ var EntryRepository = class extends BaseRepository {
1123
1146
  );
1124
1147
  return rows.map((r) => ({ id: r.id, title: r.title, okf_type: r.okf_type ?? null }));
1125
1148
  }
1149
+ /** Counts live mutable facts: eligible (past cooldown) vs deferred (in cooldown). */
1150
+ async countHealCandidatesByEntityId(entityId, recheckCutoff, tx) {
1151
+ const executor = this.getExecutor(tx);
1152
+ const row = await executor.getFirstAsync(
1153
+ `SELECT
1154
+ SUM(CASE WHEN heal_checked_at IS NULL OR heal_checked_at <= ? THEN 1 ELSE 0 END) AS eligible,
1155
+ SUM(CASE WHEN heal_checked_at IS NOT NULL AND heal_checked_at > ? THEN 1 ELSE 0 END) AS deferred
1156
+ FROM ${this.prefix}entries
1157
+ WHERE entity_id = ? AND deleted_at IS NULL AND source_type != 'immutable_document'`,
1158
+ [recheckCutoff, recheckCutoff, entityId]
1159
+ );
1160
+ return { eligible: Number(row?.eligible ?? 0), deferred: Number(row?.deferred ?? 0) };
1161
+ }
1162
+ /**
1163
+ * Stamps the heal recheck cooldown. NEVER touches updated_at — import merge
1164
+ * resolution is last-write-wins on updated_at and a bump here would make an
1165
+ * unchanged local fact beat a genuinely newer remote edit.
1166
+ *
1167
+ * Soft-deleted rows are skipped: a candidate heal deleted earlier in the same
1168
+ * transaction is no longer a candidate under any future pass, so its cooldown
1169
+ * value is irrelevant. This means the stamped count can be lower than the
1170
+ * offered-candidate count.
1171
+ */
1172
+ async markHealChecked(ids, entityId, now, tx) {
1173
+ if (ids.length === 0) return;
1174
+ for (let i = 0; i < ids.length; i += this.chunkSize) {
1175
+ const chunk = ids.slice(i, i + this.chunkSize);
1176
+ const placeholders = chunk.map(() => "?").join(",");
1177
+ await tx.runAsync(
1178
+ `UPDATE ${this.prefix}entries SET heal_checked_at = ?
1179
+ WHERE id IN (${placeholders}) AND entity_id = ? AND deleted_at IS NULL`,
1180
+ [now, ...chunk, entityId]
1181
+ );
1182
+ }
1183
+ }
1184
+ /**
1185
+ * Lightweight full-breadth index (id, title) over all live librarian_inferred
1186
+ * facts. This is heal's fuzzy-dedupe corpus, deliberately kept independent of
1187
+ * the bounded candidate window: seeding dedupe from a batchSize-limited read
1188
+ * would let a synthesized fact duplicating a fact outside the window pass the
1189
+ * Jaccard check, and a convergence loop would multiply those duplicates across
1190
+ * passes. Mirrors findTitleIndexByEntityId — full breadth, two columns, cheap.
1191
+ */
1192
+ async findInferredTitlesByEntityId(entityId, tx) {
1193
+ const executor = this.getExecutor(tx);
1194
+ const rows = await executor.getAllAsync(
1195
+ `SELECT id, title FROM ${this.prefix}entries
1196
+ WHERE entity_id = ? AND deleted_at IS NULL AND source_type = 'librarian_inferred'`,
1197
+ [entityId]
1198
+ );
1199
+ return rows.map((r) => ({ id: r.id, title: r.title }));
1200
+ }
1126
1201
  };
1127
1202
 
1128
1203
  // src/repositories/OutboxRepository.ts
@@ -2021,9 +2096,23 @@ var WikiMemory = class {
2021
2096
  return this.maintenanceService.runLibrarian(entityId, options);
2022
2097
  }
2023
2098
  /**
2099
+ * Reviews stored facts with the LLM: removes orphans, downgrades stale
2100
+ * inferences, synthesizes corrections.
2101
+ *
2102
+ * Bounded per call: covers at most `batchSize` candidates (default 25), so
2103
+ * one call no longer sweeps the whole entity. Hosts own the cadence; loop
2104
+ * `while (result.remaining > 0)` for convergence.
2105
+ *
2106
+ * `remaining === 0` means "every mutable fact is inside the 7-day recheck
2107
+ * cooldown", not "there is no more work" — heal's candidate set is the live
2108
+ * mutable corpus, not a draining backlog, so it climbs back as the cooldown
2109
+ * lapses and as new facts are written. Do not write a loop expecting a drain.
2110
+ *
2024
2111
  * @param options.promptOverride - Applies only to this manual call. Does NOT affect
2025
2112
  * WriteService-triggered auto-runs. For persistent prompt customization across auto-runs,
2026
2113
  * set `options.config.prompts.healSystemPrompt` at WikiMemory construction time.
2114
+ * @param options.batchSize - Candidates per run (default 25) for providers with
2115
+ * tighter context limits.
2027
2116
  */
2028
2117
  async runHeal(entityId, options) {
2029
2118
  return this.maintenanceService.runHeal(entityId, options);