@equationalapplications/core-llm-wiki 3.2.0 → 4.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.d.mts +15 -2
- package/dist/index.d.ts +15 -2
- package/dist/index.js +68 -21
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +68 -22
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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 '
|
|
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,
|
|
@@ -587,6 +587,44 @@ var _WikiMemory = class _WikiMemory {
|
|
|
587
587
|
_warnCrossEntityCollision(type, id, existingEntityId, targetEntityId) {
|
|
588
588
|
console.warn(`[WikiMemory] importDump: ${type} id "${id}" already belongs to entity "${existingEntityId}"; skipping for entity "${targetEntityId}"`);
|
|
589
589
|
}
|
|
590
|
+
/** Maps pre-rename enum strings from older dumps to current source_type values. */
|
|
591
|
+
_normalizeImportedSourceType(raw, ctx) {
|
|
592
|
+
if (raw === "user_document") return "immutable_document";
|
|
593
|
+
if (raw === "agent_inferred") return "librarian_inferred";
|
|
594
|
+
const allowed = ["user_stated", "librarian_inferred", "user_confirmed", "immutable_document"];
|
|
595
|
+
if (allowed.includes(raw)) return raw;
|
|
596
|
+
const where = ctx !== void 0 ? ` for entity "${ctx.entityId}" fact "${ctx.factId}"` : "";
|
|
597
|
+
throw new Error(
|
|
598
|
+
`importDump: invalid source_type "${raw}"${where} (expected one of: ${allowed.join(", ")}, or legacy aliases user_document / agent_inferred)`
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
async assertNoLegacySourceTypes() {
|
|
602
|
+
const legacyProbe = await this.db.getFirstAsync(
|
|
603
|
+
`SELECT 1 AS one FROM ${this.prefix}entries
|
|
604
|
+
WHERE source_type IN ('user_document', 'agent_inferred')
|
|
605
|
+
LIMIT 1`,
|
|
606
|
+
[]
|
|
607
|
+
);
|
|
608
|
+
if (!legacyProbe) return;
|
|
609
|
+
const legacyCount = await this.db.getFirstAsync(
|
|
610
|
+
`SELECT COUNT(*) as count FROM ${this.prefix}entries
|
|
611
|
+
WHERE source_type IN ('user_document', 'agent_inferred')`,
|
|
612
|
+
[]
|
|
613
|
+
);
|
|
614
|
+
const count = legacyCount?.count ?? 0;
|
|
615
|
+
const migrationSQL = `
|
|
616
|
+
-- Migrate legacy source_type values (targets your WikiMemory prefix: ${this.prefix})
|
|
617
|
+
UPDATE ${this.prefix}entries SET source_type = 'immutable_document' WHERE source_type = 'user_document';
|
|
618
|
+
UPDATE ${this.prefix}entries SET source_type = 'librarian_inferred' WHERE source_type = 'agent_inferred';
|
|
619
|
+
`.trim();
|
|
620
|
+
throw new Error(
|
|
621
|
+
`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.
|
|
622
|
+
|
|
623
|
+
${migrationSQL}
|
|
624
|
+
|
|
625
|
+
After running the migration SQL, restart your application.`
|
|
626
|
+
);
|
|
627
|
+
}
|
|
590
628
|
async _notifyEmbeddingPersisted(entityId, factId, vector) {
|
|
591
629
|
if (!this.options.vectorRanker?.onEmbeddingPersisted) return;
|
|
592
630
|
const vectorCopy = vector ? vector.slice() : null;
|
|
@@ -688,6 +726,9 @@ var _WikiMemory = class _WikiMemory {
|
|
|
688
726
|
);
|
|
689
727
|
}
|
|
690
728
|
}
|
|
729
|
+
if (entriesExistedBeforeSetup) {
|
|
730
|
+
await this.assertNoLegacySourceTypes();
|
|
731
|
+
}
|
|
691
732
|
const rows = await this.db.getAllAsync(`
|
|
692
733
|
SELECT rowid, source_ref FROM ${this.prefix}entries
|
|
693
734
|
WHERE source_ref IS NOT NULL
|
|
@@ -824,7 +865,7 @@ var _WikiMemory = class _WikiMemory {
|
|
|
824
865
|
const cutoff = now - retainSoftDeletedFor * 864e5;
|
|
825
866
|
const entriesToDelete = await this.db.getAllAsync(
|
|
826
867
|
`SELECT id, entity_id FROM ${this.prefix}entries
|
|
827
|
-
WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at
|
|
868
|
+
WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at <= ?`,
|
|
828
869
|
[entityId, cutoff]
|
|
829
870
|
);
|
|
830
871
|
const succeeded = [];
|
|
@@ -844,7 +885,7 @@ var _WikiMemory = class _WikiMemory {
|
|
|
844
885
|
const chunk = succeeded.slice(i, i + chunkSize);
|
|
845
886
|
const placeholders = chunk.map(() => "?").join(",");
|
|
846
887
|
const entryResult = await this.db.runAsync(
|
|
847
|
-
`DELETE FROM ${this.prefix}entries WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at
|
|
888
|
+
`DELETE FROM ${this.prefix}entries WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at <= ? AND id IN (${placeholders})`,
|
|
848
889
|
[entityId, cutoff, ...chunk.map((r) => r.id)]
|
|
849
890
|
);
|
|
850
891
|
deletedEntries += entryResult.changes;
|
|
@@ -852,7 +893,7 @@ var _WikiMemory = class _WikiMemory {
|
|
|
852
893
|
}
|
|
853
894
|
const taskResult = await this.db.runAsync(
|
|
854
895
|
`DELETE FROM ${this.prefix}tasks
|
|
855
|
-
WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at
|
|
896
|
+
WHERE entity_id = ? AND deleted_at IS NOT NULL AND deleted_at <= ?`,
|
|
856
897
|
[entityId, cutoff]
|
|
857
898
|
);
|
|
858
899
|
deletedTasks = taskResult.changes;
|
|
@@ -890,7 +931,7 @@ var _WikiMemory = class _WikiMemory {
|
|
|
890
931
|
const cutoff = now - retainEventsFor * 864e5;
|
|
891
932
|
const eventResult = await this.db.runAsync(
|
|
892
933
|
`DELETE FROM ${this.prefix}events
|
|
893
|
-
WHERE entity_id = ? AND created_at
|
|
934
|
+
WHERE entity_id = ? AND created_at <= ?`,
|
|
894
935
|
[entityId, cutoff]
|
|
895
936
|
);
|
|
896
937
|
deletedEvents = eventResult.changes;
|
|
@@ -1591,7 +1632,7 @@ ${JSON.stringify(currentFacts, null, 2)}`;
|
|
|
1591
1632
|
let skip = false;
|
|
1592
1633
|
if (newTokens.size >= MIN_TOKENS_TO_QUALIFY) {
|
|
1593
1634
|
for (const existing of currentFactsRows) {
|
|
1594
|
-
if (existing.source_type !== "
|
|
1635
|
+
if (existing.source_type !== "librarian_inferred") continue;
|
|
1595
1636
|
const existingTokens = titleTokens(existing.title);
|
|
1596
1637
|
if (existingTokens.size >= MIN_TOKENS_TO_QUALIFY) {
|
|
1597
1638
|
if (jaccardScore(newTokens, existingTokens) >= FUZZY_THRESHOLD) {
|
|
@@ -1606,7 +1647,7 @@ ${JSON.stringify(currentFacts, null, 2)}`;
|
|
|
1606
1647
|
await this.db.runAsync(`
|
|
1607
1648
|
INSERT INTO ${this.prefix}entries (id, entity_id, title, body, tags, confidence, source_type, created_at, updated_at)
|
|
1608
1649
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1609
|
-
`, [id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "
|
|
1650
|
+
`, [id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "librarian_inferred", now, now]);
|
|
1610
1651
|
insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
|
|
1611
1652
|
}
|
|
1612
1653
|
for (const task of validTasks) {
|
|
@@ -1639,25 +1680,25 @@ ${JSON.stringify(currentFacts, null, 2)}`;
|
|
|
1639
1680
|
if (orphanAfterDays !== null) {
|
|
1640
1681
|
const orphanThreshold = now - orphanAfterDays * MS_PER_DAY;
|
|
1641
1682
|
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
|
|
1683
|
+
UPDATE ${this.prefix}entries
|
|
1684
|
+
SET deleted_at = ?, updated_at = ?
|
|
1685
|
+
WHERE entity_id = ? AND access_count = 0 AND created_at <= ? AND source_type != 'immutable_document' AND deleted_at IS NULL
|
|
1645
1686
|
`, [now, now, entityId, orphanThreshold]);
|
|
1646
1687
|
}
|
|
1647
1688
|
if (staleInferredAfterDays !== null) {
|
|
1648
1689
|
const staleThreshold = now - staleInferredAfterDays * MS_PER_DAY;
|
|
1649
1690
|
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
|
|
1691
|
+
UPDATE ${this.prefix}entries
|
|
1692
|
+
SET confidence = 'tentative', updated_at = ?
|
|
1693
|
+
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
1694
|
`, [now, entityId, staleThreshold, staleThreshold]);
|
|
1654
1695
|
}
|
|
1655
1696
|
});
|
|
1656
1697
|
const allFactsRows = await this.db.getAllAsync(`SELECT * FROM ${this.prefix}entries WHERE entity_id = ? AND deleted_at IS NULL`, [entityId]);
|
|
1657
1698
|
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
1699
|
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 !== "
|
|
1660
|
-
const documentAnchors = allFactsRows.filter((f) => f.source_type === "
|
|
1700
|
+
const healCandidates = allFactsRows.filter((f) => f.source_type !== "immutable_document");
|
|
1701
|
+
const documentAnchors = allFactsRows.filter((f) => f.source_type === "immutable_document").map(({ id, title, source_ref }) => ({ id, title, source_ref }));
|
|
1661
1702
|
const userPrompt = `Heal Candidates:
|
|
1662
1703
|
${JSON.stringify(healCandidates.map((f) => {
|
|
1663
1704
|
const { embedding: _embedding, embedding_blob: _blob, ...rest } = f;
|
|
@@ -1700,7 +1741,7 @@ The following document anchors are provided for contradiction detection only. Do
|
|
|
1700
1741
|
await this.db.runAsync(`
|
|
1701
1742
|
INSERT INTO ${this.prefix}entries (id, entity_id, title, body, tags, confidence, source_type, created_at, updated_at)
|
|
1702
1743
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1703
|
-
`, [id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "
|
|
1744
|
+
`, [id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "librarian_inferred", now, now]);
|
|
1704
1745
|
insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
|
|
1705
1746
|
}
|
|
1706
1747
|
});
|
|
@@ -2000,6 +2041,7 @@ The following document anchors are provided for contradiction detection only. Do
|
|
|
2000
2041
|
this.activeMaintenanceJobs.add(this._importKey(entityId));
|
|
2001
2042
|
}
|
|
2002
2043
|
try {
|
|
2044
|
+
await this.assertNoLegacySourceTypes();
|
|
2003
2045
|
for (const [entityId, bundle] of Object.entries(dump.entities)) {
|
|
2004
2046
|
await this._doImportEntity(entityId, bundle, merge);
|
|
2005
2047
|
}
|
|
@@ -2053,6 +2095,10 @@ The following document anchors are provided for contradiction detection only. Do
|
|
|
2053
2095
|
}
|
|
2054
2096
|
}
|
|
2055
2097
|
for (const fact of bundle.facts) {
|
|
2098
|
+
const sourceType = this._normalizeImportedSourceType(String(fact.source_type), {
|
|
2099
|
+
entityId,
|
|
2100
|
+
factId: fact.id
|
|
2101
|
+
});
|
|
2056
2102
|
const tagsJson = JSON.stringify(Array.isArray(fact.tags) ? fact.tags : []);
|
|
2057
2103
|
const safeUpdatedAt = Number.isFinite(fact.updated_at) ? fact.updated_at : 0;
|
|
2058
2104
|
const existing = existingFactsById.get(fact.id);
|
|
@@ -2101,14 +2147,14 @@ The following document anchors are provided for contradiction detection only. Do
|
|
|
2101
2147
|
if (blobData != null) {
|
|
2102
2148
|
await this.db.runAsync(
|
|
2103
2149
|
`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,
|
|
2150
|
+
[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
2151
|
);
|
|
2106
2152
|
factsWithPreservedBlob.set(fact.id, blobData);
|
|
2107
2153
|
if (!fact.deleted_at) preservedBlobDims.add(blobData.byteLength / 4);
|
|
2108
2154
|
} else {
|
|
2109
2155
|
await this.db.runAsync(
|
|
2110
2156
|
`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,
|
|
2157
|
+
[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
2158
|
);
|
|
2113
2159
|
}
|
|
2114
2160
|
existingFactsById.set(fact.id, { id: fact.id, entity_id: entityId, updated_at: safeUpdatedAt });
|
|
@@ -2118,14 +2164,14 @@ The following document anchors are provided for contradiction detection only. Do
|
|
|
2118
2164
|
if (blobData != null) {
|
|
2119
2165
|
await this.db.runAsync(
|
|
2120
2166
|
`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,
|
|
2167
|
+
[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
2168
|
);
|
|
2123
2169
|
factsWithPreservedBlob.set(fact.id, blobData);
|
|
2124
2170
|
if (!fact.deleted_at) preservedBlobDims.add(blobData.byteLength / 4);
|
|
2125
2171
|
} else {
|
|
2126
2172
|
await this.db.runAsync(
|
|
2127
2173
|
`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,
|
|
2174
|
+
[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
2175
|
);
|
|
2130
2176
|
}
|
|
2131
2177
|
existingFactsById.set(fact.id, { id: fact.id, entity_id: entityId, updated_at: safeUpdatedAt });
|
|
@@ -2472,7 +2518,7 @@ ${chunk}`;
|
|
|
2472
2518
|
await this.db.runAsync(
|
|
2473
2519
|
`INSERT INTO ${this.prefix}entries (id, entity_id, title, body, tags, confidence, source_type, source_hash, source_ref, created_at, updated_at)
|
|
2474
2520
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
2475
|
-
[id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "
|
|
2521
|
+
[id, entityId, fact.title, fact.body, JSON.stringify(fact.tags), fact.confidence, "immutable_document", sourceHash, sourceRef, now, now]
|
|
2476
2522
|
);
|
|
2477
2523
|
insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
|
|
2478
2524
|
}
|
|
@@ -2731,6 +2777,6 @@ function createWiki(db, options) {
|
|
|
2731
2777
|
return new WikiMemory(db, options);
|
|
2732
2778
|
}
|
|
2733
2779
|
|
|
2734
|
-
export { PrunePartialFailureError, WikiBusyError, WikiMemory, createWiki, formatContext, formatMemoryDump };
|
|
2780
|
+
export { PrunePartialFailureError, WikiBusyError, WikiMemory, createWiki, formatContext, formatMemoryDump, parseEmbedding };
|
|
2735
2781
|
//# sourceMappingURL=index.mjs.map
|
|
2736
2782
|
//# sourceMappingURL=index.mjs.map
|