@equationalapplications/core-llm-wiki 4.19.0 → 4.21.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/README.md +59 -0
- package/dist/{chunk-3GMXRLKU.mjs → chunk-UYLYN5N4.mjs} +247 -32
- package/dist/chunk-UYLYN5N4.mjs.map +1 -0
- package/dist/index.d.mts +5 -3
- package/dist/index.d.ts +5 -3
- package/dist/index.js +416 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +169 -14
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-DP3lus54.d.mts → testing-i91HR1TG.d.mts} +122 -7
- package/dist/{testing-DP3lus54.d.ts → testing-i91HR1TG.d.ts} +122 -7
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +167 -6
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-3GMXRLKU.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -35,7 +35,8 @@ async function setupDatabase(db, prefix) {
|
|
|
35
35
|
deleted_at INTEGER,
|
|
36
36
|
embedding TEXT,
|
|
37
37
|
embedding_blob BLOB,
|
|
38
|
-
okf_type TEXT
|
|
38
|
+
okf_type TEXT,
|
|
39
|
+
ontology_checked_at INTEGER
|
|
39
40
|
);
|
|
40
41
|
|
|
41
42
|
CREATE INDEX IF NOT EXISTS ${prefix}entries_entity_idx ON ${prefix}entries(entity_id);
|
|
@@ -117,6 +118,127 @@ async function setupDatabase(db, prefix) {
|
|
|
117
118
|
`);
|
|
118
119
|
}
|
|
119
120
|
|
|
121
|
+
// src/db/sqliteCodes.ts
|
|
122
|
+
var SQLITE_RESULT_CODE_NAMES = {
|
|
123
|
+
1: "SQLITE_ERROR",
|
|
124
|
+
2: "SQLITE_INTERNAL",
|
|
125
|
+
3: "SQLITE_PERM",
|
|
126
|
+
4: "SQLITE_ABORT",
|
|
127
|
+
5: "SQLITE_BUSY",
|
|
128
|
+
6: "SQLITE_LOCKED",
|
|
129
|
+
7: "SQLITE_NOMEM",
|
|
130
|
+
8: "SQLITE_READONLY",
|
|
131
|
+
9: "SQLITE_INTERRUPT",
|
|
132
|
+
10: "SQLITE_IOERR",
|
|
133
|
+
11: "SQLITE_CORRUPT",
|
|
134
|
+
12: "SQLITE_NOTFOUND",
|
|
135
|
+
13: "SQLITE_FULL",
|
|
136
|
+
14: "SQLITE_CANTOPEN",
|
|
137
|
+
15: "SQLITE_PROTOCOL",
|
|
138
|
+
16: "SQLITE_EMPTY",
|
|
139
|
+
17: "SQLITE_SCHEMA",
|
|
140
|
+
18: "SQLITE_TOOBIG",
|
|
141
|
+
19: "SQLITE_CONSTRAINT",
|
|
142
|
+
20: "SQLITE_MISMATCH",
|
|
143
|
+
21: "SQLITE_MISUSE",
|
|
144
|
+
22: "SQLITE_NOLFS",
|
|
145
|
+
23: "SQLITE_AUTH",
|
|
146
|
+
24: "SQLITE_FORMAT",
|
|
147
|
+
25: "SQLITE_RANGE",
|
|
148
|
+
26: "SQLITE_NOTADB",
|
|
149
|
+
27: "SQLITE_NOTICE",
|
|
150
|
+
28: "SQLITE_WARNING",
|
|
151
|
+
100: "SQLITE_ROW",
|
|
152
|
+
101: "SQLITE_DONE"
|
|
153
|
+
};
|
|
154
|
+
function nameForSqliteResultCode(n) {
|
|
155
|
+
return SQLITE_RESULT_CODE_NAMES[n] ?? `SQLITE_${n}`;
|
|
156
|
+
}
|
|
157
|
+
function extractSqliteCode(err) {
|
|
158
|
+
if (typeof err !== "object" || err === null) return void 0;
|
|
159
|
+
const { code, message } = err;
|
|
160
|
+
if (typeof code === "string" && code.startsWith("SQLITE_")) return code;
|
|
161
|
+
if (typeof message === "string") {
|
|
162
|
+
const m = /^Error code (\d+):/.exec(message);
|
|
163
|
+
if (m) return nameForSqliteResultCode(Number(m[1]));
|
|
164
|
+
}
|
|
165
|
+
return void 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/types.ts
|
|
169
|
+
var WikiBusyError = class extends Error {
|
|
170
|
+
constructor(operation, entityId) {
|
|
171
|
+
super(`${operation} already running for entity ${entityId}`);
|
|
172
|
+
this.name = "WikiBusyError";
|
|
173
|
+
this.operation = operation;
|
|
174
|
+
this.entityId = entityId;
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
var WikiTransactionError = class extends Error {
|
|
178
|
+
constructor(message, options) {
|
|
179
|
+
super(message, options);
|
|
180
|
+
this.name = "WikiTransactionError";
|
|
181
|
+
this.sqliteErrorCode = extractSqliteCode(options.cause);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
var PrunePartialFailureError = class extends Error {
|
|
185
|
+
constructor(deleted, failedAt, remaining, cause, deletedTasks = 0, deletedEvents = 0) {
|
|
186
|
+
super(`Prune partially failed: deleted ${deleted}, failed at ${failedAt}, ${remaining} remaining`);
|
|
187
|
+
this.name = "PrunePartialFailureError";
|
|
188
|
+
this.deleted = deleted;
|
|
189
|
+
this.failedAt = failedAt;
|
|
190
|
+
this.remaining = remaining;
|
|
191
|
+
this.deletedTasks = deletedTasks;
|
|
192
|
+
this.deletedEvents = deletedEvents;
|
|
193
|
+
this.cause = cause;
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
var HOOK_TIMEOUT_MARKER = /* @__PURE__ */ Symbol("WikiMemoryHookTimeout");
|
|
197
|
+
|
|
198
|
+
// src/db/serializedAdapter.ts
|
|
199
|
+
function isDriverError(err) {
|
|
200
|
+
return extractSqliteCode(err) !== void 0;
|
|
201
|
+
}
|
|
202
|
+
function withOverrides(target, overrides) {
|
|
203
|
+
return new Proxy(target, {
|
|
204
|
+
get(obj, prop, receiver) {
|
|
205
|
+
if (prop in overrides) return Reflect.get(overrides, prop, receiver);
|
|
206
|
+
const value = Reflect.get(obj, prop, obj);
|
|
207
|
+
return typeof value === "function" ? value.bind(obj) : value;
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
function guardReentrancy(tx) {
|
|
212
|
+
return withOverrides(tx, {
|
|
213
|
+
withTransactionAsync() {
|
|
214
|
+
throw new Error(
|
|
215
|
+
"Nested withTransactionAsync is not supported: you are already inside a transaction. Pass the current `tx` down instead of opening a new transaction."
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
var DEADLOCK_WARN_MS = 1e4;
|
|
221
|
+
function withSerializedTransactions(db) {
|
|
222
|
+
let queue = Promise.resolve();
|
|
223
|
+
return withOverrides(db, {
|
|
224
|
+
withTransactionAsync(fn) {
|
|
225
|
+
const warn = setTimeout(() => {
|
|
226
|
+
console.warn(
|
|
227
|
+
"[core-llm-wiki] Transaction queued >10s \u2014 possible deadlock. Inside a transaction callback, use the `tx` parameter, never the outer database handle."
|
|
228
|
+
);
|
|
229
|
+
}, DEADLOCK_WARN_MS);
|
|
230
|
+
const run = queue.catch(() => void 0).then(() => {
|
|
231
|
+
clearTimeout(warn);
|
|
232
|
+
return db.withTransactionAsync((tx) => fn(guardReentrancy(tx ?? db)));
|
|
233
|
+
}).catch((e) => {
|
|
234
|
+
throw isDriverError(e) ? new WikiTransactionError("Transaction failed", { cause: e }) : e;
|
|
235
|
+
});
|
|
236
|
+
queue = run;
|
|
237
|
+
return run;
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
120
242
|
// src/db/migrations.ts
|
|
121
243
|
var MIGRATIONS = [
|
|
122
244
|
{
|
|
@@ -217,6 +339,20 @@ var MIGRATIONS = [
|
|
|
217
339
|
);
|
|
218
340
|
`);
|
|
219
341
|
}
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
version: 7,
|
|
345
|
+
description: "Add ontology_checked_at to entries for ontology backfill recheck cooldown",
|
|
346
|
+
run: async (db, prefix) => {
|
|
347
|
+
const cols = await db.getAllAsync(
|
|
348
|
+
`PRAGMA table_info(${prefix}entries)`
|
|
349
|
+
);
|
|
350
|
+
if (!cols.some((c) => c.name === "ontology_checked_at")) {
|
|
351
|
+
await db.execAsync(
|
|
352
|
+
`ALTER TABLE ${prefix}entries ADD COLUMN ontology_checked_at INTEGER`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
220
356
|
}
|
|
221
357
|
];
|
|
222
358
|
for (let i = 1; i < MIGRATIONS.length; i++) {
|
|
@@ -980,6 +1116,79 @@ var EntryRepository = class extends BaseRepository {
|
|
|
980
1116
|
);
|
|
981
1117
|
return rows.map(mapRowToFact);
|
|
982
1118
|
}
|
|
1119
|
+
/**
|
|
1120
|
+
* Live untyped facts eligible for ontology backfill, oldest first.
|
|
1121
|
+
* Skips facts checked within the recheck cooldown (ontology_checked_at > recheckCutoff).
|
|
1122
|
+
*/
|
|
1123
|
+
async findUntypedByEntityId(entityId, limit, recheckCutoff, tx) {
|
|
1124
|
+
const executor = this.getExecutor(tx);
|
|
1125
|
+
const rows = await executor.getAllAsync(
|
|
1126
|
+
`SELECT * FROM ${this.prefix}entries
|
|
1127
|
+
WHERE entity_id = ? AND okf_type IS NULL AND deleted_at IS NULL
|
|
1128
|
+
AND (ontology_checked_at IS NULL OR ontology_checked_at <= ?)
|
|
1129
|
+
ORDER BY updated_at ASC LIMIT ?`,
|
|
1130
|
+
[entityId, recheckCutoff, limit]
|
|
1131
|
+
);
|
|
1132
|
+
return rows.map(mapRowToFact);
|
|
1133
|
+
}
|
|
1134
|
+
/** Counts live untyped facts: eligible (past cooldown) vs deferred (in cooldown). */
|
|
1135
|
+
async countUntypedByEntityId(entityId, recheckCutoff, tx) {
|
|
1136
|
+
const executor = this.getExecutor(tx);
|
|
1137
|
+
const row = await executor.getFirstAsync(
|
|
1138
|
+
`SELECT
|
|
1139
|
+
SUM(CASE WHEN ontology_checked_at IS NULL OR ontology_checked_at <= ? THEN 1 ELSE 0 END) AS eligible,
|
|
1140
|
+
SUM(CASE WHEN ontology_checked_at IS NOT NULL AND ontology_checked_at > ? THEN 1 ELSE 0 END) AS deferred
|
|
1141
|
+
FROM ${this.prefix}entries
|
|
1142
|
+
WHERE entity_id = ? AND okf_type IS NULL AND deleted_at IS NULL`,
|
|
1143
|
+
[recheckCutoff, recheckCutoff, entityId]
|
|
1144
|
+
);
|
|
1145
|
+
return { eligible: Number(row?.eligible ?? 0), deferred: Number(row?.deferred ?? 0) };
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* Sets okf_type only when currently NULL — additive by construction: a
|
|
1149
|
+
* concurrently-typed fact is never overwritten. Returns changes for the caller
|
|
1150
|
+
* to distinguish applied vs skipped.
|
|
1151
|
+
*/
|
|
1152
|
+
async updateOkfType(id, entityId, okfType, tx) {
|
|
1153
|
+
const result = await tx.runAsync(
|
|
1154
|
+
`UPDATE ${this.prefix}entries SET okf_type = ?
|
|
1155
|
+
WHERE id = ? AND entity_id = ? AND okf_type IS NULL AND deleted_at IS NULL`,
|
|
1156
|
+
[okfType, id, entityId]
|
|
1157
|
+
);
|
|
1158
|
+
return { changes: result.changes };
|
|
1159
|
+
}
|
|
1160
|
+
/**
|
|
1161
|
+
* Stamps the backfill recheck cooldown. NEVER touches updated_at — import
|
|
1162
|
+
* merge resolution is last-write-wins on updated_at and a bump here would
|
|
1163
|
+
* make an unchanged local fact beat a genuinely newer remote edit.
|
|
1164
|
+
*/
|
|
1165
|
+
async markOntologyChecked(ids, entityId, now, tx) {
|
|
1166
|
+
if (ids.length === 0) return;
|
|
1167
|
+
for (let i = 0; i < ids.length; i += this.chunkSize) {
|
|
1168
|
+
const chunk = ids.slice(i, i + this.chunkSize);
|
|
1169
|
+
const placeholders = chunk.map(() => "?").join(",");
|
|
1170
|
+
await tx.runAsync(
|
|
1171
|
+
`UPDATE ${this.prefix}entries SET ontology_checked_at = ?
|
|
1172
|
+
WHERE id IN (${placeholders}) AND entity_id = ? AND deleted_at IS NULL`,
|
|
1173
|
+
[now, ...chunk, entityId]
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Lightweight full-breadth title index (id, title, okf_type) over all live
|
|
1179
|
+
* typed facts. Used by ontology backfill edge resolution — a recent-N window
|
|
1180
|
+
* would miss an old fact's contemporaries. Untyped rows are excluded: they
|
|
1181
|
+
* can never pass the edge target-type check, and batch facts typed during
|
|
1182
|
+
* the run are re-added to the in-memory index by the caller.
|
|
1183
|
+
*/
|
|
1184
|
+
async findTitleIndexByEntityId(entityId, tx) {
|
|
1185
|
+
const executor = this.getExecutor(tx);
|
|
1186
|
+
const rows = await executor.getAllAsync(
|
|
1187
|
+
`SELECT id, title, okf_type FROM ${this.prefix}entries WHERE entity_id = ? AND deleted_at IS NULL AND okf_type IS NOT NULL`,
|
|
1188
|
+
[entityId]
|
|
1189
|
+
);
|
|
1190
|
+
return rows.map((r) => ({ id: r.id, title: r.title, okf_type: r.okf_type ?? null }));
|
|
1191
|
+
}
|
|
983
1192
|
};
|
|
984
1193
|
|
|
985
1194
|
// src/utils/ids.ts
|
|
@@ -1424,6 +1633,7 @@ var CONFIDENCE_RANK = {
|
|
|
1424
1633
|
var EdgeRepository = class extends BaseRepository {
|
|
1425
1634
|
/**
|
|
1426
1635
|
* Insert an edge, silently skipping on primary-key or uniqueness conflicts.
|
|
1636
|
+
* Returns true when a row was inserted, false when skipped as a duplicate.
|
|
1427
1637
|
* Throws when the insert was skipped due to an id collision with a different edge tuple.
|
|
1428
1638
|
*/
|
|
1429
1639
|
async addIgnoreDuplicate(edge, tx) {
|
|
@@ -1433,17 +1643,18 @@ var EdgeRepository = class extends BaseRepository {
|
|
|
1433
1643
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
1434
1644
|
[edge.id, edge.entity_id, edge.source_id, edge.target_id, edge.edge_type, edge.created_at]
|
|
1435
1645
|
);
|
|
1436
|
-
if (result.changes > 0) return;
|
|
1646
|
+
if (result.changes > 0) return true;
|
|
1437
1647
|
const existing = await executor.getFirstAsync(
|
|
1438
1648
|
`SELECT entity_id, source_id, target_id, edge_type FROM ${this.prefix}edges WHERE id = ?`,
|
|
1439
1649
|
[edge.id]
|
|
1440
1650
|
);
|
|
1441
|
-
if (!existing) return;
|
|
1651
|
+
if (!existing) return false;
|
|
1442
1652
|
if (String(existing.entity_id) !== edge.entity_id || String(existing.source_id) !== edge.source_id || String(existing.target_id) !== edge.target_id || String(existing.edge_type) !== edge.edge_type) {
|
|
1443
1653
|
throw new Error(
|
|
1444
1654
|
`Edge id collision: ${JSON.stringify(edge.id)} already exists with a different (entity_id, source_id, target_id, edge_type) tuple`
|
|
1445
1655
|
);
|
|
1446
1656
|
}
|
|
1657
|
+
return false;
|
|
1447
1658
|
}
|
|
1448
1659
|
async getByEntityId(entityId, tx) {
|
|
1449
1660
|
const executor = this.getExecutor(tx);
|
|
@@ -2036,29 +2247,6 @@ _SearchService.MAX_VECTOR_CACHE_ENTITIES = 16;
|
|
|
2036
2247
|
_SearchService.MAX_VECTOR_CACHE_FACTS_PER_ENTITY = 500;
|
|
2037
2248
|
var SearchService = _SearchService;
|
|
2038
2249
|
|
|
2039
|
-
// src/types.ts
|
|
2040
|
-
var WikiBusyError = class extends Error {
|
|
2041
|
-
constructor(operation, entityId) {
|
|
2042
|
-
super(`${operation} already running for entity ${entityId}`);
|
|
2043
|
-
this.name = "WikiBusyError";
|
|
2044
|
-
this.operation = operation;
|
|
2045
|
-
this.entityId = entityId;
|
|
2046
|
-
}
|
|
2047
|
-
};
|
|
2048
|
-
var PrunePartialFailureError = class extends Error {
|
|
2049
|
-
constructor(deleted, failedAt, remaining, cause, deletedTasks = 0, deletedEvents = 0) {
|
|
2050
|
-
super(`Prune partially failed: deleted ${deleted}, failed at ${failedAt}, ${remaining} remaining`);
|
|
2051
|
-
this.name = "PrunePartialFailureError";
|
|
2052
|
-
this.deleted = deleted;
|
|
2053
|
-
this.failedAt = failedAt;
|
|
2054
|
-
this.remaining = remaining;
|
|
2055
|
-
this.deletedTasks = deletedTasks;
|
|
2056
|
-
this.deletedEvents = deletedEvents;
|
|
2057
|
-
this.cause = cause;
|
|
2058
|
-
}
|
|
2059
|
-
};
|
|
2060
|
-
var HOOK_TIMEOUT_MARKER = /* @__PURE__ */ Symbol("WikiMemoryHookTimeout");
|
|
2061
|
-
|
|
2062
2250
|
// src/services/JobManager.ts
|
|
2063
2251
|
var JobManager = class {
|
|
2064
2252
|
constructor(prefix) {
|
|
@@ -2078,7 +2266,8 @@ var JobManager = class {
|
|
|
2078
2266
|
heal: (id) => this._healKey(id),
|
|
2079
2267
|
reembed: (id) => this._reembedKey(id),
|
|
2080
2268
|
import: (id) => this._importKey(id),
|
|
2081
|
-
forget: (id) => this._forgetKey(id)
|
|
2269
|
+
forget: (id) => this._forgetKey(id),
|
|
2270
|
+
ontologyBackfill: (id) => this._ontologyBackfillKey(id)
|
|
2082
2271
|
};
|
|
2083
2272
|
}
|
|
2084
2273
|
_pruneKey(entityId) {
|
|
@@ -2105,6 +2294,9 @@ var JobManager = class {
|
|
|
2105
2294
|
_healKey(entityId) {
|
|
2106
2295
|
return `${this.prefix}:${entityId}:heal`;
|
|
2107
2296
|
}
|
|
2297
|
+
_ontologyBackfillKey(entityId) {
|
|
2298
|
+
return `${this.prefix}:${entityId}:ontologyBackfill`;
|
|
2299
|
+
}
|
|
2108
2300
|
_isReembedActive(entityId) {
|
|
2109
2301
|
return this.activeMaintenanceJobs.has(this._reembedKey(entityId)) || this.activeMaintenanceJobs.has(this._globalReembedKey());
|
|
2110
2302
|
}
|
|
@@ -2155,14 +2347,16 @@ var JobManager = class {
|
|
|
2155
2347
|
if (this.activeMaintenanceJobs.has(this._pruneKey(entityId))) blockingOperation = "prune";
|
|
2156
2348
|
else if (this.activeMaintenanceJobs.has(this._librarianKey(entityId))) blockingOperation = "librarian";
|
|
2157
2349
|
else if (this.activeMaintenanceJobs.has(this._healKey(entityId))) blockingOperation = "heal";
|
|
2350
|
+
else if (this.activeMaintenanceJobs.has(this._ontologyBackfillKey(entityId))) blockingOperation = "ontologyBackfill";
|
|
2158
2351
|
else if (this._isReembedActive(entityId)) blockingOperation = "reembed";
|
|
2159
2352
|
else if (this._isIngestActiveFor(entityId)) blockingOperation = "ingest";
|
|
2160
2353
|
else if (this._isImportActiveFor(entityId)) blockingOperation = "import";
|
|
2161
2354
|
else if (this._isForgetActiveFor(entityId)) blockingOperation = "forget";
|
|
2162
2355
|
break;
|
|
2163
2356
|
case "librarian":
|
|
2164
|
-
case "heal":
|
|
2165
|
-
|
|
2357
|
+
case "heal":
|
|
2358
|
+
case "ontologyBackfill": {
|
|
2359
|
+
const opKey = this.lockKeyFns[operation](entityId);
|
|
2166
2360
|
if (this.activeMaintenanceJobs.has(opKey)) blockingOperation = operation;
|
|
2167
2361
|
else if (this.activeMaintenanceJobs.has(this._pruneKey(entityId))) blockingOperation = "prune";
|
|
2168
2362
|
else if (this._isReembedActive(entityId)) blockingOperation = "reembed";
|
|
@@ -2176,6 +2370,7 @@ var JobManager = class {
|
|
|
2176
2370
|
else if (this.activeMaintenanceJobs.has(this._pruneKey(entityId))) blockingOperation = "prune";
|
|
2177
2371
|
else if (this.activeMaintenanceJobs.has(this._librarianKey(entityId))) blockingOperation = "librarian";
|
|
2178
2372
|
else if (this.activeMaintenanceJobs.has(this._healKey(entityId))) blockingOperation = "heal";
|
|
2373
|
+
else if (this.activeMaintenanceJobs.has(this._ontologyBackfillKey(entityId))) blockingOperation = "ontologyBackfill";
|
|
2179
2374
|
else if (this._isIngestActiveFor(entityId)) blockingOperation = "ingest";
|
|
2180
2375
|
else if (this._isImportActiveFor(entityId)) blockingOperation = "import";
|
|
2181
2376
|
else if (this._isForgetActiveFor(entityId)) blockingOperation = "forget";
|
|
@@ -2186,6 +2381,7 @@ var JobManager = class {
|
|
|
2186
2381
|
else if (this._isAnyMaintenanceActiveWithSuffix(":prune")) blockingOperation = "prune";
|
|
2187
2382
|
else if (this._isAnyMaintenanceActiveWithSuffix(":librarian")) blockingOperation = "librarian";
|
|
2188
2383
|
else if (this._isAnyMaintenanceActiveWithSuffix(":heal")) blockingOperation = "heal";
|
|
2384
|
+
else if (this._isAnyMaintenanceActiveWithSuffix(":ontologyBackfill")) blockingOperation = "ontologyBackfill";
|
|
2189
2385
|
else if (this.activeIngestJobs.size > 0) blockingOperation = "ingest";
|
|
2190
2386
|
else if (this._isAnyMaintenanceActiveWithSuffix(":import")) blockingOperation = "import";
|
|
2191
2387
|
else if (this._isAnyMaintenanceActiveWithSuffix(":forget")) blockingOperation = "forget";
|
|
@@ -2196,6 +2392,7 @@ var JobManager = class {
|
|
|
2196
2392
|
if (this.activeMaintenanceJobs.has(selfKey)) blockingOperation = operation;
|
|
2197
2393
|
else if (this.activeMaintenanceJobs.has(this._librarianKey(entityId))) blockingOperation = "librarian";
|
|
2198
2394
|
else if (this.activeMaintenanceJobs.has(this._healKey(entityId))) blockingOperation = "heal";
|
|
2395
|
+
else if (this.activeMaintenanceJobs.has(this._ontologyBackfillKey(entityId))) blockingOperation = "ontologyBackfill";
|
|
2199
2396
|
else if (this.activeMaintenanceJobs.has(this._pruneKey(entityId))) blockingOperation = "prune";
|
|
2200
2397
|
else if (this._isReembedActive(entityId)) blockingOperation = "reembed";
|
|
2201
2398
|
else if (this._isIngestActiveFor(entityId)) blockingOperation = "ingest";
|
|
@@ -2253,11 +2450,11 @@ var JobManager = class {
|
|
|
2253
2450
|
if (operation !== "global_import" && this.activeMaintenanceJobs.has(this._globalImportKey())) return true;
|
|
2254
2451
|
switch (operation) {
|
|
2255
2452
|
case "librarian":
|
|
2256
|
-
return this.activeMaintenanceJobs.has(this._librarianKey(entityId)) || this.activeMaintenanceJobs.has(this._pruneKey(entityId)) || this._isReembedActive(entityId) || this._isImportActiveFor(entityId) || this._isForgetActiveFor(entityId);
|
|
2257
2453
|
case "heal":
|
|
2258
|
-
|
|
2454
|
+
case "ontologyBackfill":
|
|
2455
|
+
return this.activeMaintenanceJobs.has(this.lockKeyFns[operation](entityId)) || this.activeMaintenanceJobs.has(this._pruneKey(entityId)) || this._isReembedActive(entityId) || this._isImportActiveFor(entityId) || this._isForgetActiveFor(entityId);
|
|
2259
2456
|
case "prune":
|
|
2260
|
-
return this.activeMaintenanceJobs.has(this._pruneKey(entityId)) || this.activeMaintenanceJobs.has(this._librarianKey(entityId)) || this.activeMaintenanceJobs.has(this._healKey(entityId)) || this._isReembedActive(entityId) || this._isIngestActiveFor(entityId) || this._isImportActiveFor(entityId) || this._isForgetActiveFor(entityId);
|
|
2457
|
+
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);
|
|
2261
2458
|
default:
|
|
2262
2459
|
return false;
|
|
2263
2460
|
}
|
|
@@ -2282,6 +2479,7 @@ var JobManager = class {
|
|
|
2282
2479
|
if (this.activeMaintenanceJobs.has(this._importKey(entityId))) throw new WikiBusyError("import", entityId);
|
|
2283
2480
|
if (this.activeMaintenanceJobs.has(this._librarianKey(entityId))) throw new WikiBusyError("librarian", entityId);
|
|
2284
2481
|
if (this.activeMaintenanceJobs.has(this._healKey(entityId))) throw new WikiBusyError("heal", entityId);
|
|
2482
|
+
if (this.activeMaintenanceJobs.has(this._ontologyBackfillKey(entityId))) throw new WikiBusyError("ontologyBackfill", entityId);
|
|
2285
2483
|
if (this.activeMaintenanceJobs.has(this._pruneKey(entityId))) throw new WikiBusyError("prune", entityId);
|
|
2286
2484
|
if (this._isReembedActive(entityId)) throw new WikiBusyError("reembed", entityId);
|
|
2287
2485
|
if (this._isIngestActiveFor(entityId)) throw new WikiBusyError("ingest", entityId);
|
|
@@ -2590,6 +2788,15 @@ Return ONLY a valid JSON object matching this schema:
|
|
|
2590
2788
|
"facts": [{ "title": "string (max 80 chars)", "body": "string (max 800 chars)", "tags": ["string"], "confidence": "certain|inferred|tentative" }]
|
|
2591
2789
|
}
|
|
2592
2790
|
Extract verbatim factual content. Do not return markdown, just raw JSON.`;
|
|
2791
|
+
var ONTOLOGY_BACKFILL_SYSTEM_PROMPT = `You are a knowledge classification agent. You will receive existing memory facts that currently have no ontology type. For each input fact { "id", "title", "body", "tags" }, assign the best matching okf_type from the ontology manifest and optionally propose edges to related facts by title.
|
|
2792
|
+
Return ONLY a valid JSON object matching this schema:
|
|
2793
|
+
{
|
|
2794
|
+
"classifications": [
|
|
2795
|
+
{ "id": "string (input fact id, copied verbatim)", "okf_type": "string (manifest node type slug)", "edges": [{ "edge_type": "string", "target_title": "string" }] }
|
|
2796
|
+
]
|
|
2797
|
+
}
|
|
2798
|
+
If no manifest type fits a fact, omit that fact from "classifications" entirely \u2014 do not guess.
|
|
2799
|
+
Do not return markdown, just raw JSON.`;
|
|
2593
2800
|
|
|
2594
2801
|
// src/services/PromptService.ts
|
|
2595
2802
|
var PromptService = class {
|
|
@@ -2679,6 +2886,22 @@ ${JSON.stringify(recentEvents, null, 2)}
|
|
|
2679
2886
|
The following document anchors are provided for contradiction detection only. Do not include them in \`downgraded\`, \`deleted\`, or \`newFacts\`.`
|
|
2680
2887
|
};
|
|
2681
2888
|
}
|
|
2889
|
+
buildOntologyBackfillPrompt(facts, runtimeOverride, ontologyContext) {
|
|
2890
|
+
const template = runtimeOverride ?? this.globalOverrides?.ontologyBackfillSystemPrompt ?? ONTOLOGY_BACKFILL_SYSTEM_PROMPT;
|
|
2891
|
+
const hasFacts = /\{\{\s*facts\s*\}\}/.test(template);
|
|
2892
|
+
if (hasFacts || this.hasOntologyPlaceholders(template)) {
|
|
2893
|
+
return {
|
|
2894
|
+
systemPrompt: this.buildSystemPrompt(template, { facts }, ontologyContext),
|
|
2895
|
+
userPrompt: hasFacts ? "Please classify the facts." : `Facts:
|
|
2896
|
+
${JSON.stringify(facts, null, 2)}`
|
|
2897
|
+
};
|
|
2898
|
+
}
|
|
2899
|
+
return {
|
|
2900
|
+
systemPrompt: this.appendOntology(template, ontologyContext),
|
|
2901
|
+
userPrompt: `Facts:
|
|
2902
|
+
${JSON.stringify(facts, null, 2)}`
|
|
2903
|
+
};
|
|
2904
|
+
}
|
|
2682
2905
|
};
|
|
2683
2906
|
|
|
2684
2907
|
// src/services/IngestionService.ts
|
|
@@ -2832,6 +3055,9 @@ var IngestionService = class {
|
|
|
2832
3055
|
// src/services/MaintenanceService.ts
|
|
2833
3056
|
var FUZZY_THRESHOLD = 0.5;
|
|
2834
3057
|
var MIN_TOKENS_TO_QUALIFY = 3;
|
|
3058
|
+
var ONTOLOGY_BACKFILL_BATCH_SIZE = 25;
|
|
3059
|
+
var ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS = 4e4;
|
|
3060
|
+
var ONTOLOGY_BACKFILL_RECHECK_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2835
3061
|
var MaintenanceService = class {
|
|
2836
3062
|
constructor(db, prefix, options, entryRepo, taskRepo, eventRepo, metadataRepo, searchService, jobManager, embeddingService, promptService, ontologyService) {
|
|
2837
3063
|
this.db = db;
|
|
@@ -2937,6 +3163,14 @@ var MaintenanceService = class {
|
|
|
2937
3163
|
this.jobManager.releaseLock("heal", entityId);
|
|
2938
3164
|
}
|
|
2939
3165
|
}
|
|
3166
|
+
async runOntologyBackfill(entityId, options) {
|
|
3167
|
+
this.jobManager.acquireLock("ontologyBackfill", entityId);
|
|
3168
|
+
try {
|
|
3169
|
+
return await this.doRunOntologyBackfill(entityId, options);
|
|
3170
|
+
} finally {
|
|
3171
|
+
this.jobManager.releaseLock("ontologyBackfill", entityId);
|
|
3172
|
+
}
|
|
3173
|
+
}
|
|
2940
3174
|
async runReembed(entityId, opts) {
|
|
2941
3175
|
const embedFn = this.options.llmProvider.embed;
|
|
2942
3176
|
if (!embedFn) return { embedded: 0, skipped: 0, failed: 0 };
|
|
@@ -3288,6 +3522,121 @@ var MaintenanceService = class {
|
|
|
3288
3522
|
}
|
|
3289
3523
|
this.searchService.evictCache(entityId);
|
|
3290
3524
|
}
|
|
3525
|
+
/** Core ontology backfill pass (locks handled by {@link runOntologyBackfill}). Package-internal orchestration hook. */
|
|
3526
|
+
async doRunOntologyBackfill(entityId, options) {
|
|
3527
|
+
const batchSize = options?.batchSize ?? ONTOLOGY_BACKFILL_BATCH_SIZE;
|
|
3528
|
+
if (!Number.isInteger(batchSize) || batchSize < 1) {
|
|
3529
|
+
throw new Error("Invalid batchSize: must be an integer >= 1");
|
|
3530
|
+
}
|
|
3531
|
+
const now = Date.now();
|
|
3532
|
+
const recheckCutoff = now - ONTOLOGY_BACKFILL_RECHECK_MS;
|
|
3533
|
+
const zeroed = { scanned: 0, typed: 0, failedValidation: 0, edgesAdded: 0 };
|
|
3534
|
+
const ontologyService = this.ontologyService;
|
|
3535
|
+
if (!ontologyService) {
|
|
3536
|
+
return { ...zeroed, remaining: 0, deferred: 0 };
|
|
3537
|
+
}
|
|
3538
|
+
const { mode } = await ontologyService.getEffectiveState(entityId);
|
|
3539
|
+
if (mode === "off") {
|
|
3540
|
+
const counts2 = await this.entryRepo.countUntypedByEntityId(entityId, recheckCutoff);
|
|
3541
|
+
return { ...zeroed, remaining: 0, deferred: counts2.deferred };
|
|
3542
|
+
}
|
|
3543
|
+
const candidates = await this.entryRepo.findUntypedByEntityId(entityId, batchSize, recheckCutoff);
|
|
3544
|
+
if (candidates.length === 0) {
|
|
3545
|
+
const counts2 = await this.entryRepo.countUntypedByEntityId(entityId, recheckCutoff);
|
|
3546
|
+
return { ...zeroed, remaining: counts2.eligible, deferred: counts2.deferred };
|
|
3547
|
+
}
|
|
3548
|
+
const ontologyContext = await ontologyService.buildPromptContext(entityId);
|
|
3549
|
+
const toPromptShape = (f) => ({ id: f.id, title: f.title, body: f.body, tags: f.tags });
|
|
3550
|
+
const buildPrompt = (facts) => this.promptService.buildOntologyBackfillPrompt(
|
|
3551
|
+
facts.map(toPromptShape),
|
|
3552
|
+
options?.promptOverride,
|
|
3553
|
+
ontologyContext
|
|
3554
|
+
);
|
|
3555
|
+
const batch = [candidates[0]];
|
|
3556
|
+
let built = buildPrompt(batch);
|
|
3557
|
+
for (let i = 1; i < candidates.length; i++) {
|
|
3558
|
+
const next = buildPrompt([...batch, candidates[i]]);
|
|
3559
|
+
if (next.systemPrompt.length + next.userPrompt.length > ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS) break;
|
|
3560
|
+
batch.push(candidates[i]);
|
|
3561
|
+
built = next;
|
|
3562
|
+
}
|
|
3563
|
+
const { systemPrompt, userPrompt } = built;
|
|
3564
|
+
const responseText = await this.options.llmProvider.generateText({ systemPrompt, userPrompt });
|
|
3565
|
+
const parsed = parseJsonResponse(responseText);
|
|
3566
|
+
const classifications = Array.isArray(parsed.classifications) ? parsed.classifications : [];
|
|
3567
|
+
let typed = 0;
|
|
3568
|
+
let failedValidation = 0;
|
|
3569
|
+
let edgesAdded = 0;
|
|
3570
|
+
let abortedOntologyOff = false;
|
|
3571
|
+
await this.db.withTransactionAsync(async (tx) => {
|
|
3572
|
+
let { mode: txMode, manifest } = await ontologyService.getEffectiveState(entityId, tx);
|
|
3573
|
+
if (txMode === "off") {
|
|
3574
|
+
abortedOntologyOff = true;
|
|
3575
|
+
return;
|
|
3576
|
+
}
|
|
3577
|
+
if (txMode === "emergent" && parsed.ontology_updates) {
|
|
3578
|
+
manifest = await ontologyService.mergeEmergentUpdates(entityId, parsed.ontology_updates, tx);
|
|
3579
|
+
}
|
|
3580
|
+
const titleRows = await this.entryRepo.findTitleIndexByEntityId(entityId, tx);
|
|
3581
|
+
const titleIndex = /* @__PURE__ */ new Map();
|
|
3582
|
+
for (const row of titleRows) {
|
|
3583
|
+
titleIndex.set(normalizeTitleKey(row.title), { id: row.id, okf_type: row.okf_type });
|
|
3584
|
+
}
|
|
3585
|
+
const batchById = new Map(batch.map((f) => [f.id, f]));
|
|
3586
|
+
const applied = /* @__PURE__ */ new Set();
|
|
3587
|
+
const pendingEdges = [];
|
|
3588
|
+
for (const classification of classifications) {
|
|
3589
|
+
const fact = typeof classification.id === "string" ? batchById.get(classification.id) : void 0;
|
|
3590
|
+
if (!fact || applied.has(fact.id)) {
|
|
3591
|
+
failedValidation++;
|
|
3592
|
+
continue;
|
|
3593
|
+
}
|
|
3594
|
+
applied.add(fact.id);
|
|
3595
|
+
const normalized = ontologyService.validateAndNormalizeFact(
|
|
3596
|
+
{
|
|
3597
|
+
title: fact.title,
|
|
3598
|
+
body: fact.body,
|
|
3599
|
+
tags: fact.tags,
|
|
3600
|
+
confidence: fact.confidence,
|
|
3601
|
+
okf_type: classification.okf_type,
|
|
3602
|
+
edges: classification.edges
|
|
3603
|
+
},
|
|
3604
|
+
manifest
|
|
3605
|
+
);
|
|
3606
|
+
if (!normalized.okf_type) {
|
|
3607
|
+
failedValidation++;
|
|
3608
|
+
continue;
|
|
3609
|
+
}
|
|
3610
|
+
const result = await this.entryRepo.updateOkfType(fact.id, entityId, normalized.okf_type, tx);
|
|
3611
|
+
if (result.changes === 0) continue;
|
|
3612
|
+
typed++;
|
|
3613
|
+
titleIndex.set(normalizeTitleKey(fact.title), { id: fact.id, okf_type: normalized.okf_type });
|
|
3614
|
+
if (normalized.edges.length > 0) {
|
|
3615
|
+
pendingEdges.push({ sourceId: fact.id, sourceType: normalized.okf_type, edges: normalized.edges });
|
|
3616
|
+
}
|
|
3617
|
+
}
|
|
3618
|
+
for (const item of pendingEdges) {
|
|
3619
|
+
edgesAdded += await ontologyService.resolveAndPersistEdges(
|
|
3620
|
+
entityId,
|
|
3621
|
+
item.sourceId,
|
|
3622
|
+
item.sourceType,
|
|
3623
|
+
item.edges,
|
|
3624
|
+
manifest,
|
|
3625
|
+
titleIndex,
|
|
3626
|
+
tx,
|
|
3627
|
+
now
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3630
|
+
await this.entryRepo.markOntologyChecked(batch.map((f) => f.id), entityId, now, tx);
|
|
3631
|
+
});
|
|
3632
|
+
if (abortedOntologyOff) {
|
|
3633
|
+
const counts2 = await this.entryRepo.countUntypedByEntityId(entityId, recheckCutoff);
|
|
3634
|
+
return { ...zeroed, remaining: 0, deferred: counts2.deferred };
|
|
3635
|
+
}
|
|
3636
|
+
this.searchService.evictCache(entityId);
|
|
3637
|
+
const counts = await this.entryRepo.countUntypedByEntityId(entityId, recheckCutoff);
|
|
3638
|
+
return { scanned: batch.length, typed, failedValidation, edgesAdded, remaining: counts.eligible, deferred: counts.deferred };
|
|
3639
|
+
}
|
|
3291
3640
|
_validatePruneDuration(value, name) {
|
|
3292
3641
|
if (value !== null && value !== void 0 && (typeof value !== "number" || !isFinite(value) || value < 0)) {
|
|
3293
3642
|
throw new Error(`Invalid ${name}: must be a non-negative finite number or null`);
|
|
@@ -4587,7 +4936,8 @@ var OntologyService = class {
|
|
|
4587
4936
|
return { okf_type: canonical, edges };
|
|
4588
4937
|
}
|
|
4589
4938
|
async resolveAndPersistEdges(entityId, sourceId, sourceType, edges, manifest, titleIndex, tx, now) {
|
|
4590
|
-
if (!sourceType || edges.length === 0) return;
|
|
4939
|
+
if (!sourceType || edges.length === 0) return 0;
|
|
4940
|
+
let persisted = 0;
|
|
4591
4941
|
for (const edge of edges) {
|
|
4592
4942
|
const def = resolveEdgeDefinition(edge.edge_type, manifest);
|
|
4593
4943
|
if (!def || def.source_type.toLowerCase() !== sourceType.toLowerCase()) continue;
|
|
@@ -4603,8 +4953,10 @@ var OntologyService = class {
|
|
|
4603
4953
|
edge_type: def.type,
|
|
4604
4954
|
created_at: now
|
|
4605
4955
|
};
|
|
4606
|
-
await this.edgeRepo.addIgnoreDuplicate(wikiEdge, tx);
|
|
4956
|
+
const inserted = await this.edgeRepo.addIgnoreDuplicate(wikiEdge, tx);
|
|
4957
|
+
if (inserted) persisted++;
|
|
4607
4958
|
}
|
|
4959
|
+
return persisted;
|
|
4608
4960
|
}
|
|
4609
4961
|
};
|
|
4610
4962
|
|
|
@@ -4647,7 +4999,7 @@ var WikiMemory = class {
|
|
|
4647
4999
|
constructor(db, options) {
|
|
4648
5000
|
/** Emits `__testAccess` console warning at most once per instance when NODE_ENV ≠ "test". */
|
|
4649
5001
|
__privateAdd(this, _testAccessNonTestEnvWarned, false);
|
|
4650
|
-
this.db = db;
|
|
5002
|
+
this.db = withSerializedTransactions(db);
|
|
4651
5003
|
this.options = options;
|
|
4652
5004
|
this.prefix = options.config?.tablePrefix ?? "llm_wiki_";
|
|
4653
5005
|
if (!TABLE_PREFIX_PATTERN.test(this.prefix)) {
|
|
@@ -4655,12 +5007,12 @@ var WikiMemory = class {
|
|
|
4655
5007
|
`Invalid tablePrefix: ${JSON.stringify(this.prefix)}. Must match ${TABLE_PREFIX_PATTERN} (letter, then alphanumeric/underscore, ending in "_", max 32 chars total).`
|
|
4656
5008
|
);
|
|
4657
5009
|
}
|
|
4658
|
-
this.outboxRepo = new OutboxRepository(db, this.prefix, !!options.config?.enableOutbox);
|
|
4659
|
-
this.entryRepo = new EntryRepository(db, this.prefix, this.outboxRepo);
|
|
4660
|
-
this.taskRepo = new TaskRepository(db, this.prefix, this.outboxRepo);
|
|
4661
|
-
this.eventRepo = new EventRepository(db, this.prefix);
|
|
4662
|
-
this.edgeRepo = new EdgeRepository(db, this.prefix);
|
|
4663
|
-
this.metadataRepo = new MetadataRepository(db, this.prefix);
|
|
5010
|
+
this.outboxRepo = new OutboxRepository(this.db, this.prefix, !!options.config?.enableOutbox);
|
|
5011
|
+
this.entryRepo = new EntryRepository(this.db, this.prefix, this.outboxRepo);
|
|
5012
|
+
this.taskRepo = new TaskRepository(this.db, this.prefix, this.outboxRepo);
|
|
5013
|
+
this.eventRepo = new EventRepository(this.db, this.prefix);
|
|
5014
|
+
this.edgeRepo = new EdgeRepository(this.db, this.prefix);
|
|
5015
|
+
this.metadataRepo = new MetadataRepository(this.db, this.prefix);
|
|
4664
5016
|
this.ontologyService = new OntologyService(
|
|
4665
5017
|
this.metadataRepo,
|
|
4666
5018
|
this.edgeRepo,
|
|
@@ -4844,6 +5196,24 @@ var WikiMemory = class {
|
|
|
4844
5196
|
async runHeal(entityId, options) {
|
|
4845
5197
|
return this.maintenanceService.runHeal(entityId, options);
|
|
4846
5198
|
}
|
|
5199
|
+
/**
|
|
5200
|
+
* Types already-persisted untyped facts (okf_type IS NULL) in place via one
|
|
5201
|
+
* librarian-style LLM call. Strictly additive: never creates, deletes, or
|
|
5202
|
+
* rewrites facts; never overwrites an existing okf_type. Free when there is
|
|
5203
|
+
* nothing to do (ontology off, or zero eligible untyped facts).
|
|
5204
|
+
*
|
|
5205
|
+
* Hosts own the trigger cadence (e.g. after each sync); loop `while
|
|
5206
|
+
* result.remaining > 0` for convergence. Throws WikiBusyError when another
|
|
5207
|
+
* backfill (or conflicting maintenance op) is running for the entity.
|
|
5208
|
+
*
|
|
5209
|
+
* @param options.promptOverride - Applies only to this call. For persistent
|
|
5210
|
+
* customization set `options.config.prompts.ontologyBackfillSystemPrompt`.
|
|
5211
|
+
* @param options.batchSize - Facts per run (default 25) for providers with
|
|
5212
|
+
* tighter context limits.
|
|
5213
|
+
*/
|
|
5214
|
+
async runOntologyBackfill(entityId, options) {
|
|
5215
|
+
return this.maintenanceService.runOntologyBackfill(entityId, options);
|
|
5216
|
+
}
|
|
4847
5217
|
async runReembed(entityId, opts) {
|
|
4848
5218
|
return this.maintenanceService.runReembed(entityId, opts);
|
|
4849
5219
|
}
|
|
@@ -5683,10 +6053,15 @@ function createWiki(db, options) {
|
|
|
5683
6053
|
|
|
5684
6054
|
exports.DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT = DEFAULT_LIBRARIAN_SYNTHESIS_PROMPT;
|
|
5685
6055
|
exports.HOOK_TIMEOUT_MARKER = HOOK_TIMEOUT_MARKER;
|
|
6056
|
+
exports.ONTOLOGY_BACKFILL_BATCH_SIZE = ONTOLOGY_BACKFILL_BATCH_SIZE;
|
|
6057
|
+
exports.ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS = ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS;
|
|
6058
|
+
exports.ONTOLOGY_BACKFILL_RECHECK_MS = ONTOLOGY_BACKFILL_RECHECK_MS;
|
|
6059
|
+
exports.ONTOLOGY_BACKFILL_SYSTEM_PROMPT = ONTOLOGY_BACKFILL_SYSTEM_PROMPT;
|
|
5686
6060
|
exports.PromptService = PromptService;
|
|
5687
6061
|
exports.PrunePartialFailureError = PrunePartialFailureError;
|
|
5688
6062
|
exports.WikiBusyError = WikiBusyError;
|
|
5689
6063
|
exports.WikiMemory = WikiMemory;
|
|
6064
|
+
exports.WikiTransactionError = WikiTransactionError;
|
|
5690
6065
|
exports.configureRandomSource = configureRandomSource;
|
|
5691
6066
|
exports.createWiki = createWiki;
|
|
5692
6067
|
exports.formatContext = formatContext;
|