@equationalapplications/core-llm-wiki 4.15.3 → 4.16.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, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, generateId } from './chunk-J4GBC6CP.mjs';
2
- export { HOOK_TIMEOUT_MARKER, PromptService, PrunePartialFailureError, WikiBusyError, parseEmbedding } from './chunk-J4GBC6CP.mjs';
1
+ import { __privateAdd, EmbeddingService, SearchService, JobManager, PromptService, IngestionService, MaintenanceService, ImportExportService, RetrievalService, WriteService, __privateGet, __privateSet, normalizeSourceRef, normalizeSourceHash, generateId, validateManifest, emptyManifest, mergeOntologyUpdates, resolveNodeType, validateInlineEdges, resolveEdgeDefinition, normalizeTitleKey } from './chunk-2BGLPRT3.mjs';
2
+ export { HOOK_TIMEOUT_MARKER, PromptService, PrunePartialFailureError, WikiBusyError, parseEmbedding } from './chunk-2BGLPRT3.mjs';
3
3
  import { buildConceptDocument, buildLogMd, buildIndexMd, buildRootIndexMd, parseConcept, extractMarkdownLinks, parseLogMd } from '@equationalapplications/core-okf';
4
4
 
5
5
  // src/db/schema.ts
@@ -74,6 +74,13 @@ async function setupDatabase(db, prefix) {
74
74
  memory_checkpoint INTEGER NOT NULL DEFAULT 0
75
75
  );
76
76
 
77
+ CREATE TABLE IF NOT EXISTS ${prefix}entity_manifests (
78
+ entity_id TEXT PRIMARY KEY,
79
+ mode TEXT NOT NULL DEFAULT 'off',
80
+ manifest_json TEXT NOT NULL DEFAULT '{"node_types":[],"edge_types":[]}',
81
+ updated_at INTEGER NOT NULL
82
+ );
83
+
77
84
  CREATE TABLE IF NOT EXISTS ${prefix}meta (
78
85
  key TEXT PRIMARY KEY,
79
86
  value TEXT NOT NULL
@@ -183,6 +190,20 @@ var MIGRATIONS = [
183
190
  CREATE INDEX IF NOT EXISTS ${prefix}edges_entity_idx ON ${prefix}edges (entity_id);
184
191
  `);
185
192
  }
193
+ },
194
+ {
195
+ version: 6,
196
+ description: "Add entity_manifests table for per-entity ontology state",
197
+ run: async (db, prefix) => {
198
+ await db.execAsync(`
199
+ CREATE TABLE IF NOT EXISTS ${prefix}entity_manifests (
200
+ entity_id TEXT PRIMARY KEY,
201
+ mode TEXT NOT NULL DEFAULT 'off',
202
+ manifest_json TEXT NOT NULL DEFAULT '{"node_types":[],"edge_types":[]}',
203
+ updated_at INTEGER NOT NULL
204
+ );
205
+ `);
206
+ }
186
207
  }
187
208
  ];
188
209
  for (let i = 1; i < MIGRATIONS.length; i++) {
@@ -306,8 +327,8 @@ var EntryRepository = class extends BaseRepository {
306
327
  `INSERT INTO ${this.prefix}entries (
307
328
  id, entity_id, title, body, tags, confidence, source_type,
308
329
  source_hash, source_ref, created_at, updated_at, last_accessed_at, access_count,
309
- deleted_at, embedding_blob, embedding
310
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
330
+ deleted_at, embedding_blob, embedding, okf_type
331
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
311
332
  ON CONFLICT(id) DO UPDATE SET
312
333
  entity_id = excluded.entity_id,
313
334
  title = excluded.title,
@@ -322,7 +343,8 @@ var EntryRepository = class extends BaseRepository {
322
343
  access_count = excluded.access_count,
323
344
  deleted_at = excluded.deleted_at,
324
345
  embedding_blob = CASE WHEN excluded.embedding_blob IS NULL THEN embedding_blob ELSE excluded.embedding_blob END,
325
- embedding = NULL`,
346
+ embedding = NULL,
347
+ okf_type = excluded.okf_type`,
326
348
  [
327
349
  fact.id,
328
350
  fact.entity_id,
@@ -339,7 +361,8 @@ var EntryRepository = class extends BaseRepository {
339
361
  fact.access_count,
340
362
  fact.deleted_at ?? null,
341
363
  embeddingBlob ?? null,
342
- null
364
+ null,
365
+ fact.okf_type ?? null
343
366
  ]
344
367
  );
345
368
  await this.outbox.push({
@@ -1500,6 +1523,151 @@ var MetadataRepository = class extends BaseRepository {
1500
1523
  );
1501
1524
  return rows.map((r) => r.entity_id);
1502
1525
  }
1526
+ async getManifest(entityId, tx) {
1527
+ const executor = this.getExecutor(tx);
1528
+ const row = await executor.getFirstAsync(`SELECT mode, manifest_json FROM ${this.prefix}entity_manifests WHERE entity_id = ?`, [entityId]);
1529
+ if (!row) return null;
1530
+ if (row.mode !== "off" && row.mode !== "strict" && row.mode !== "emergent") {
1531
+ throw new Error(`Invalid ontology mode for entity ${entityId}: ${JSON.stringify(row.mode)}`);
1532
+ }
1533
+ let manifest;
1534
+ try {
1535
+ manifest = JSON.parse(row.manifest_json);
1536
+ } catch (error) {
1537
+ throw new Error(`Invalid manifest_json for entity ${entityId}: ${error.message}`);
1538
+ }
1539
+ validateManifest(manifest);
1540
+ return {
1541
+ mode: row.mode,
1542
+ manifest
1543
+ };
1544
+ }
1545
+ async setManifest(entityId, data, tx) {
1546
+ validateManifest(data.manifest);
1547
+ const executor = this.getExecutor(tx);
1548
+ await executor.runAsync(
1549
+ `INSERT INTO ${this.prefix}entity_manifests (entity_id, mode, manifest_json, updated_at)
1550
+ VALUES (?, ?, ?, ?)
1551
+ ON CONFLICT(entity_id) DO UPDATE SET mode = excluded.mode, manifest_json = excluded.manifest_json, updated_at = excluded.updated_at`,
1552
+ [entityId, data.mode, JSON.stringify(data.manifest), Date.now()]
1553
+ );
1554
+ }
1555
+ async mergeManifestUpdates(entityId, updates, tx) {
1556
+ const current = await this.getManifest(entityId, tx) ?? {
1557
+ mode: "emergent",
1558
+ manifest: emptyManifest()
1559
+ };
1560
+ const merged = mergeOntologyUpdates(current.manifest, updates);
1561
+ await this.setManifest(entityId, { mode: current.mode, manifest: merged }, tx);
1562
+ return merged;
1563
+ }
1564
+ };
1565
+
1566
+ // src/prompts/ontology.ts
1567
+ var FACT_ONTOLOGY_FIELDS = `
1568
+ Each fact may optionally include:
1569
+ - "okf_type": string \u2014 must be one of the node_types in the manifest
1570
+ - "edges": [{ "edge_type": string, "target_title": string }] \u2014 target_title must match another fact's title in this response or existing memory`;
1571
+ var EMERGENT_EXTRA = `
1572
+ You may also return "ontology_updates" to propose new types:
1573
+ "ontology_updates": {
1574
+ "node_types": [{ "type": "slug", "description": "..." }],
1575
+ "edge_types": [{ "type": "slug", "source_type": "...", "target_type": "...", "description": "..." }]
1576
+ }
1577
+ Only propose types not already in the manifest. Do not redefine existing types.`;
1578
+ function buildOntologyPromptAppendix(mode, manifestJson) {
1579
+ const strictRules = mode === "strict" ? "STRICT MODE: Use ONLY types defined in the manifest. If a fact does not fit any node_type, omit okf_type and edges entirely." : "EMERGENT MODE: Prefer manifest types. You may propose new types via ontology_updates when necessary.";
1580
+ const fallback = "If okf_type or an edge is invalid, omit them rather than inventing unlisted types.";
1581
+ const ontologyModeInstructions = [
1582
+ "## Ontology constraints",
1583
+ strictRules,
1584
+ fallback,
1585
+ FACT_ONTOLOGY_FIELDS,
1586
+ mode === "emergent" ? EMERGENT_EXTRA : "",
1587
+ "Manifest:",
1588
+ manifestJson
1589
+ ].filter(Boolean).join("\n");
1590
+ return { ontologyManifest: manifestJson, ontologyModeInstructions };
1591
+ }
1592
+
1593
+ // src/services/OntologyService.ts
1594
+ var OntologyService = class {
1595
+ constructor(metadataRepo, edgeRepo, ontologyConfig) {
1596
+ this.metadataRepo = metadataRepo;
1597
+ this.edgeRepo = edgeRepo;
1598
+ this.ontologyConfig = ontologyConfig;
1599
+ this.cache = /* @__PURE__ */ new Map();
1600
+ }
1601
+ resolveMode(storedMode) {
1602
+ return storedMode ?? this.ontologyConfig?.mode ?? "off";
1603
+ }
1604
+ invalidateCache(entityId) {
1605
+ this.cache.delete(entityId);
1606
+ }
1607
+ async getEffectiveState(entityId, tx) {
1608
+ if (!tx) {
1609
+ const cached = this.cache.get(entityId);
1610
+ if (cached) return cached;
1611
+ }
1612
+ const row = await this.metadataRepo.getManifest(entityId, tx);
1613
+ if (row) {
1614
+ const state = { mode: this.resolveMode(row.mode), manifest: row.manifest };
1615
+ if (!tx) this.cache.set(entityId, state);
1616
+ return state;
1617
+ }
1618
+ const seed = this.ontologyConfig?.seedManifests?.[entityId];
1619
+ if (seed) {
1620
+ const state = {
1621
+ mode: this.resolveMode(seed.mode),
1622
+ manifest: seed.manifest
1623
+ };
1624
+ if (tx) {
1625
+ await this.metadataRepo.setManifest(entityId, state, tx);
1626
+ } else {
1627
+ this.cache.set(entityId, state);
1628
+ }
1629
+ return state;
1630
+ }
1631
+ return { mode: "off", manifest: emptyManifest() };
1632
+ }
1633
+ async buildPromptContext(entityId) {
1634
+ const { mode, manifest } = await this.getEffectiveState(entityId);
1635
+ if (mode === "off") return null;
1636
+ const manifestJson = JSON.stringify(manifest, null, 2);
1637
+ return buildOntologyPromptAppendix(mode, manifestJson);
1638
+ }
1639
+ async mergeEmergentUpdates(entityId, updates, tx) {
1640
+ const merged = await this.metadataRepo.mergeManifestUpdates(entityId, updates, tx);
1641
+ this.invalidateCache(entityId);
1642
+ return merged;
1643
+ }
1644
+ validateAndNormalizeFact(fact, manifest) {
1645
+ const rawType = typeof fact.okf_type === "string" ? fact.okf_type : "";
1646
+ const canonical = resolveNodeType(rawType, manifest);
1647
+ if (!canonical) return { okf_type: null, edges: [] };
1648
+ const edges = validateInlineEdges(canonical, null, fact.edges ?? [], manifest);
1649
+ return { okf_type: canonical, edges };
1650
+ }
1651
+ async resolveAndPersistEdges(entityId, sourceId, sourceType, edges, manifest, titleIndex, tx, now) {
1652
+ if (!sourceType || edges.length === 0) return;
1653
+ for (const edge of edges) {
1654
+ const def = resolveEdgeDefinition(edge.edge_type, manifest);
1655
+ if (!def || def.source_type.toLowerCase() !== sourceType.toLowerCase()) continue;
1656
+ const targetKey = normalizeTitleKey(edge.target_title);
1657
+ const target = titleIndex.get(targetKey);
1658
+ if (!target) continue;
1659
+ if (def.target_type.toLowerCase() !== (target.okf_type ?? "").toLowerCase()) continue;
1660
+ const wikiEdge = {
1661
+ id: generateId(),
1662
+ entity_id: entityId,
1663
+ source_id: sourceId,
1664
+ target_id: target.id,
1665
+ edge_type: def.type,
1666
+ created_at: now
1667
+ };
1668
+ await this.edgeRepo.addIgnoreDuplicate(wikiEdge, tx);
1669
+ }
1670
+ }
1503
1671
  };
1504
1672
 
1505
1673
  // src/WikiMemory.ts
@@ -1523,6 +1691,11 @@ var WikiMemory = class {
1523
1691
  this.eventRepo = new EventRepository(db, this.prefix);
1524
1692
  this.edgeRepo = new EdgeRepository(db, this.prefix);
1525
1693
  this.metadataRepo = new MetadataRepository(db, this.prefix);
1694
+ this.ontologyService = new OntologyService(
1695
+ this.metadataRepo,
1696
+ this.edgeRepo,
1697
+ options.config?.ontology
1698
+ );
1526
1699
  this.embeddingService = new EmbeddingService(this.db, this.options, this.entryRepo, this.metadataRepo);
1527
1700
  this.searchService = new SearchService(this.entryRepo);
1528
1701
  this.jobManager = new JobManager(this.prefix);
@@ -1535,7 +1708,8 @@ var WikiMemory = class {
1535
1708
  this.searchService,
1536
1709
  this.jobManager,
1537
1710
  this.embeddingService,
1538
- this.promptService
1711
+ this.promptService,
1712
+ this.ontologyService
1539
1713
  );
1540
1714
  this.maintenanceService = new MaintenanceService(
1541
1715
  this.db,
@@ -1548,7 +1722,8 @@ var WikiMemory = class {
1548
1722
  this.searchService,
1549
1723
  this.jobManager,
1550
1724
  this.embeddingService,
1551
- this.promptService
1725
+ this.promptService,
1726
+ this.ontologyService
1552
1727
  );
1553
1728
  this.importExportService = new ImportExportService(
1554
1729
  this.db,
@@ -1743,6 +1918,34 @@ var WikiMemory = class {
1743
1918
  async markOutboxEventsProcessed(eventIds) {
1744
1919
  await this.outboxRepo.acknowledge(eventIds);
1745
1920
  }
1921
+ /**
1922
+ * Returns the effective ontology mode and manifest for an entity.
1923
+ * Resolution order: persisted DB row → `WikiConfig.ontology.seedManifests[entityId]` → `null`.
1924
+ */
1925
+ async getOntologyManifest(entityId) {
1926
+ const row = await this.metadataRepo.getManifest(entityId);
1927
+ if (row) return { mode: this.ontologyService.resolveMode(row.mode), manifest: row.manifest };
1928
+ const seed = this.options.config?.ontology?.seedManifests?.[entityId];
1929
+ if (seed) {
1930
+ return {
1931
+ mode: this.ontologyService.resolveMode(seed.mode),
1932
+ manifest: seed.manifest
1933
+ };
1934
+ }
1935
+ return null;
1936
+ }
1937
+ /**
1938
+ * Seeds or replaces an entity's ontology manifest and optional mode override.
1939
+ * Validates manifest invariants (unique type slugs, edge endpoints reference node types).
1940
+ * Invalidates the in-memory ontology cache for this entity.
1941
+ */
1942
+ async setOntologyManifest(entityId, manifest, options) {
1943
+ const mode = options?.mode ?? this.ontologyService.resolveMode();
1944
+ await this.db.withTransactionAsync(
1945
+ (tx) => this.metadataRepo.setManifest(entityId, { mode, manifest }, tx)
1946
+ );
1947
+ this.ontologyService.invalidateCache(entityId);
1948
+ }
1746
1949
  };
1747
1950
  _testAccessNonTestEnvWarned = new WeakMap();
1748
1951