@equationalapplications/core-llm-wiki 4.0.0 → 4.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.
- package/README.md +35 -0
- package/dist/index.d.mts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +66 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +66 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -302,6 +302,41 @@ wikiMemory.clearVectorCache();
|
|
|
302
302
|
|
|
303
303
|
The cache is also automatically invalidated on any mutation (`runLibrarian`, `runHeal`, `runPrune`, `runReembed`, `ingestDocument`, `importDump`, `forget`).
|
|
304
304
|
|
|
305
|
+
## Entity Status
|
|
306
|
+
|
|
307
|
+
`WikiMemory` exposes the in-flight job state for a single entity through two complementary APIs.
|
|
308
|
+
|
|
309
|
+
### `getEntityStatus(entityId)`
|
|
310
|
+
|
|
311
|
+
Synchronous point-in-time snapshot:
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
const status = wikiMemory.getEntityStatus('user-42');
|
|
315
|
+
// { ingesting: boolean, librarian: boolean, heal: boolean }
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Use this when you only need the current value (e.g. inside a request handler).
|
|
319
|
+
|
|
320
|
+
### `subscribeEntityStatus(entityId, callback)`
|
|
321
|
+
|
|
322
|
+
Push-based change notification — the callback fires synchronously once with the current status, then again on every transition where any of the three booleans flips. There is no polling and no duplicate snapshots.
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
const unsubscribe = wikiMemory.subscribeEntityStatus('user-42', (status) => {
|
|
326
|
+
console.log(status); // { ingesting, librarian, heal }
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Later:
|
|
330
|
+
unsubscribe(); // idempotent — safe to call more than once
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Notes:
|
|
334
|
+
|
|
335
|
+
- The first invocation happens **before** `subscribeEntityStatus` returns. Treat it as the initial render value.
|
|
336
|
+
- Each emission may be a fresh object literal. Do not rely on referential equality between callbacks; equality of the three booleans is the contract.
|
|
337
|
+
- A throwing callback is caught (logged via `console.error`) and does not block other subscribers or the underlying job.
|
|
338
|
+
- Subscriptions are scoped to a single `entityId`. There is no wildcard or "all entities" form.
|
|
339
|
+
|
|
305
340
|
## Security
|
|
306
341
|
|
|
307
342
|
`@equationalapplications/core-llm-wiki` enforces multiple security layers:
|
package/dist/index.d.mts
CHANGED
|
@@ -52,6 +52,19 @@ interface ReadOptions {
|
|
|
52
52
|
*/
|
|
53
53
|
preFilterLimit?: number | null;
|
|
54
54
|
hybridWeight?: number;
|
|
55
|
+
/**
|
|
56
|
+
* (Reserved for Phase 2+ multi-entity weighted retrieval)
|
|
57
|
+
* entity_id -> score multiplier. Missing entries default to 1.0.
|
|
58
|
+
* Currently ignored by WikiMemory.read(); planned for implementation in Phase 2.
|
|
59
|
+
*/
|
|
60
|
+
tierWeights?: Record<string, number>;
|
|
61
|
+
/**
|
|
62
|
+
* (Reserved for Phase 2+ multi-entity weighted retrieval)
|
|
63
|
+
* false/default -> skip zero-weight entities during scored retrieval.
|
|
64
|
+
* true -> retrieve zero-weight entities and let them fill only if the pool is small.
|
|
65
|
+
* Currently ignored by WikiMemory.read(); planned for implementation in Phase 2.
|
|
66
|
+
*/
|
|
67
|
+
includeZeroWeightEntities?: boolean;
|
|
55
68
|
}
|
|
56
69
|
interface WikiFact {
|
|
57
70
|
id: string;
|
|
@@ -274,6 +287,12 @@ interface MemoryBundle {
|
|
|
274
287
|
facts: WikiFact[];
|
|
275
288
|
tasks: WikiTask[];
|
|
276
289
|
events: WikiEvent[];
|
|
290
|
+
factScores?: Record<string, number>;
|
|
291
|
+
metadata?: {
|
|
292
|
+
query: string;
|
|
293
|
+
entityIds: string[];
|
|
294
|
+
tierWeights?: Record<string, number>;
|
|
295
|
+
};
|
|
277
296
|
}
|
|
278
297
|
interface MemoryDump {
|
|
279
298
|
generatedAt: number;
|
|
@@ -337,6 +356,7 @@ declare class WikiMemory {
|
|
|
337
356
|
private options;
|
|
338
357
|
private activeMaintenanceJobs;
|
|
339
358
|
private activeIngestJobs;
|
|
359
|
+
private statusSubscribers;
|
|
340
360
|
private miniSearch;
|
|
341
361
|
private miniSearchEntryIdsByEntity;
|
|
342
362
|
/**
|
|
@@ -392,6 +412,8 @@ declare class WikiMemory {
|
|
|
392
412
|
private _isAnyMaintenanceActiveWithSuffix;
|
|
393
413
|
/** Returns true if any ingest job is active for the given entity. */
|
|
394
414
|
private _isIngestActiveFor;
|
|
415
|
+
private _copyEntityStatus;
|
|
416
|
+
private _notifyStatusSubscribers;
|
|
395
417
|
private _validatePruneDuration;
|
|
396
418
|
runPrune(entityId: string, options?: {
|
|
397
419
|
retainSoftDeletedFor?: number | null;
|
|
@@ -445,6 +467,17 @@ declare class WikiMemory {
|
|
|
445
467
|
failed: number;
|
|
446
468
|
}>;
|
|
447
469
|
getEntityStatus(entityId: string): EntityStatus;
|
|
470
|
+
/**
|
|
471
|
+
* Subscribe to {@link EntityStatus} changes for a single entity. The callback
|
|
472
|
+
* is invoked synchronously once with the current status before this method
|
|
473
|
+
* returns, then again on every transition where any of `ingesting`,
|
|
474
|
+
* `librarian`, or `heal` flips. No polling, no duplicate snapshots.
|
|
475
|
+
*
|
|
476
|
+
* Returns an idempotent unsubscribe function.
|
|
477
|
+
*
|
|
478
|
+
* See also {@link getEntityStatus} for a synchronous point-in-time read.
|
|
479
|
+
*/
|
|
480
|
+
subscribeEntityStatus(entityId: string, callback: (status: EntityStatus) => void): () => void;
|
|
448
481
|
clearVectorCache(): void;
|
|
449
482
|
private _getFullBundle;
|
|
450
483
|
exportDump(entityIds?: string[]): Promise<MemoryDump>;
|
package/dist/index.d.ts
CHANGED
|
@@ -52,6 +52,19 @@ interface ReadOptions {
|
|
|
52
52
|
*/
|
|
53
53
|
preFilterLimit?: number | null;
|
|
54
54
|
hybridWeight?: number;
|
|
55
|
+
/**
|
|
56
|
+
* (Reserved for Phase 2+ multi-entity weighted retrieval)
|
|
57
|
+
* entity_id -> score multiplier. Missing entries default to 1.0.
|
|
58
|
+
* Currently ignored by WikiMemory.read(); planned for implementation in Phase 2.
|
|
59
|
+
*/
|
|
60
|
+
tierWeights?: Record<string, number>;
|
|
61
|
+
/**
|
|
62
|
+
* (Reserved for Phase 2+ multi-entity weighted retrieval)
|
|
63
|
+
* false/default -> skip zero-weight entities during scored retrieval.
|
|
64
|
+
* true -> retrieve zero-weight entities and let them fill only if the pool is small.
|
|
65
|
+
* Currently ignored by WikiMemory.read(); planned for implementation in Phase 2.
|
|
66
|
+
*/
|
|
67
|
+
includeZeroWeightEntities?: boolean;
|
|
55
68
|
}
|
|
56
69
|
interface WikiFact {
|
|
57
70
|
id: string;
|
|
@@ -274,6 +287,12 @@ interface MemoryBundle {
|
|
|
274
287
|
facts: WikiFact[];
|
|
275
288
|
tasks: WikiTask[];
|
|
276
289
|
events: WikiEvent[];
|
|
290
|
+
factScores?: Record<string, number>;
|
|
291
|
+
metadata?: {
|
|
292
|
+
query: string;
|
|
293
|
+
entityIds: string[];
|
|
294
|
+
tierWeights?: Record<string, number>;
|
|
295
|
+
};
|
|
277
296
|
}
|
|
278
297
|
interface MemoryDump {
|
|
279
298
|
generatedAt: number;
|
|
@@ -337,6 +356,7 @@ declare class WikiMemory {
|
|
|
337
356
|
private options;
|
|
338
357
|
private activeMaintenanceJobs;
|
|
339
358
|
private activeIngestJobs;
|
|
359
|
+
private statusSubscribers;
|
|
340
360
|
private miniSearch;
|
|
341
361
|
private miniSearchEntryIdsByEntity;
|
|
342
362
|
/**
|
|
@@ -392,6 +412,8 @@ declare class WikiMemory {
|
|
|
392
412
|
private _isAnyMaintenanceActiveWithSuffix;
|
|
393
413
|
/** Returns true if any ingest job is active for the given entity. */
|
|
394
414
|
private _isIngestActiveFor;
|
|
415
|
+
private _copyEntityStatus;
|
|
416
|
+
private _notifyStatusSubscribers;
|
|
395
417
|
private _validatePruneDuration;
|
|
396
418
|
runPrune(entityId: string, options?: {
|
|
397
419
|
retainSoftDeletedFor?: number | null;
|
|
@@ -445,6 +467,17 @@ declare class WikiMemory {
|
|
|
445
467
|
failed: number;
|
|
446
468
|
}>;
|
|
447
469
|
getEntityStatus(entityId: string): EntityStatus;
|
|
470
|
+
/**
|
|
471
|
+
* Subscribe to {@link EntityStatus} changes for a single entity. The callback
|
|
472
|
+
* is invoked synchronously once with the current status before this method
|
|
473
|
+
* returns, then again on every transition where any of `ingesting`,
|
|
474
|
+
* `librarian`, or `heal` flips. No polling, no duplicate snapshots.
|
|
475
|
+
*
|
|
476
|
+
* Returns an idempotent unsubscribe function.
|
|
477
|
+
*
|
|
478
|
+
* See also {@link getEntityStatus} for a synchronous point-in-time read.
|
|
479
|
+
*/
|
|
480
|
+
subscribeEntityStatus(entityId: string, callback: (status: EntityStatus) => void): () => void;
|
|
448
481
|
clearVectorCache(): void;
|
|
449
482
|
private _getFullBundle;
|
|
450
483
|
exportDump(entityIds?: string[]): Promise<MemoryDump>;
|
package/dist/index.js
CHANGED
|
@@ -418,6 +418,7 @@ var _WikiMemory = class _WikiMemory {
|
|
|
418
418
|
constructor(db, options) {
|
|
419
419
|
this.activeMaintenanceJobs = /* @__PURE__ */ new Set();
|
|
420
420
|
this.activeIngestJobs = /* @__PURE__ */ new Set();
|
|
421
|
+
this.statusSubscribers = /* @__PURE__ */ new Map();
|
|
421
422
|
this.miniSearch = new MiniSearch__default.default({
|
|
422
423
|
fields: ["title", "body", "tags"],
|
|
423
424
|
storeFields: ["entity_id"],
|
|
@@ -822,6 +823,24 @@ After running the migration SQL, restart your application.`
|
|
|
822
823
|
}
|
|
823
824
|
return false;
|
|
824
825
|
}
|
|
826
|
+
_copyEntityStatus(s) {
|
|
827
|
+
return { ingesting: s.ingesting, librarian: s.librarian, heal: s.heal };
|
|
828
|
+
}
|
|
829
|
+
_notifyStatusSubscribers(entityId) {
|
|
830
|
+
const set = this.statusSubscribers.get(entityId);
|
|
831
|
+
if (!set || set.size === 0) return;
|
|
832
|
+
for (const entry of Array.from(set)) {
|
|
833
|
+
if (!set.has(entry)) continue;
|
|
834
|
+
const next = this.getEntityStatus(entityId);
|
|
835
|
+
if (entry.last.ingesting === next.ingesting && entry.last.librarian === next.librarian && entry.last.heal === next.heal) continue;
|
|
836
|
+
entry.last = this._copyEntityStatus(next);
|
|
837
|
+
try {
|
|
838
|
+
entry.callback(this._copyEntityStatus(next));
|
|
839
|
+
} catch (err) {
|
|
840
|
+
console.error(`[WikiMemory.subscribeEntityStatus] callback error for entityId="${entityId}" during transition emission`, err);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
825
844
|
_validatePruneDuration(value, name) {
|
|
826
845
|
if (value !== null && value !== void 0 && (typeof value !== "number" || !isFinite(value) || value < 0)) {
|
|
827
846
|
throw new Error(`Invalid ${name}: must be a non-negative finite number or null`);
|
|
@@ -1564,7 +1583,11 @@ After running the migration SQL, restart your application.`
|
|
|
1564
1583
|
const jobKey = this._librarianKey(entityId);
|
|
1565
1584
|
if (!this.activeMaintenanceJobs.has(jobKey) && !this.activeMaintenanceJobs.has(this._pruneKey(entityId)) && !this._isReembedActive(entityId) && !this._isImportActiveFor(entityId) && !this._isForgetActiveFor(entityId)) {
|
|
1566
1585
|
this.activeMaintenanceJobs.add(jobKey);
|
|
1567
|
-
this.
|
|
1586
|
+
this._notifyStatusSubscribers(entityId);
|
|
1587
|
+
this.runLibrarianThenMaybeHeal(entityId, count).catch(console.error).finally(() => {
|
|
1588
|
+
this.activeMaintenanceJobs.delete(jobKey);
|
|
1589
|
+
this._notifyStatusSubscribers(entityId);
|
|
1590
|
+
});
|
|
1568
1591
|
}
|
|
1569
1592
|
}
|
|
1570
1593
|
}
|
|
@@ -1583,6 +1606,7 @@ After running the migration SQL, restart your application.`
|
|
|
1583
1606
|
const healKey = this._healKey(entityId);
|
|
1584
1607
|
if (!this.activeMaintenanceJobs.has(healKey)) {
|
|
1585
1608
|
this.activeMaintenanceJobs.add(healKey);
|
|
1609
|
+
this._notifyStatusSubscribers(entityId);
|
|
1586
1610
|
try {
|
|
1587
1611
|
await this._doRunHeal(entityId);
|
|
1588
1612
|
await this.db.runAsync(`
|
|
@@ -1592,6 +1616,7 @@ After running the migration SQL, restart your application.`
|
|
|
1592
1616
|
`, [entityId, currentEventCount, currentEventCount]);
|
|
1593
1617
|
} finally {
|
|
1594
1618
|
this.activeMaintenanceJobs.delete(healKey);
|
|
1619
|
+
this._notifyStatusSubscribers(entityId);
|
|
1595
1620
|
}
|
|
1596
1621
|
}
|
|
1597
1622
|
}
|
|
@@ -1783,10 +1808,12 @@ The following document anchors are provided for contradiction detection only. Do
|
|
|
1783
1808
|
throw new WikiBusyError("forget", entityId);
|
|
1784
1809
|
}
|
|
1785
1810
|
this.activeMaintenanceJobs.add(jobKey);
|
|
1811
|
+
this._notifyStatusSubscribers(entityId);
|
|
1786
1812
|
try {
|
|
1787
1813
|
await this._doRunLibrarian(entityId);
|
|
1788
1814
|
} finally {
|
|
1789
1815
|
this.activeMaintenanceJobs.delete(jobKey);
|
|
1816
|
+
this._notifyStatusSubscribers(entityId);
|
|
1790
1817
|
}
|
|
1791
1818
|
}
|
|
1792
1819
|
async runHeal(entityId) {
|
|
@@ -1807,10 +1834,12 @@ The following document anchors are provided for contradiction detection only. Do
|
|
|
1807
1834
|
throw new WikiBusyError("forget", entityId);
|
|
1808
1835
|
}
|
|
1809
1836
|
this.activeMaintenanceJobs.add(jobKey);
|
|
1837
|
+
this._notifyStatusSubscribers(entityId);
|
|
1810
1838
|
try {
|
|
1811
1839
|
await this._doRunHeal(entityId);
|
|
1812
1840
|
} finally {
|
|
1813
1841
|
this.activeMaintenanceJobs.delete(jobKey);
|
|
1842
|
+
this._notifyStatusSubscribers(entityId);
|
|
1814
1843
|
}
|
|
1815
1844
|
}
|
|
1816
1845
|
async runReembed(entityId, opts) {
|
|
@@ -1950,6 +1979,40 @@ The following document anchors are provided for contradiction detection only. Do
|
|
|
1950
1979
|
heal: this.activeMaintenanceJobs.has(this._healKey(entityId))
|
|
1951
1980
|
};
|
|
1952
1981
|
}
|
|
1982
|
+
/**
|
|
1983
|
+
* Subscribe to {@link EntityStatus} changes for a single entity. The callback
|
|
1984
|
+
* is invoked synchronously once with the current status before this method
|
|
1985
|
+
* returns, then again on every transition where any of `ingesting`,
|
|
1986
|
+
* `librarian`, or `heal` flips. No polling, no duplicate snapshots.
|
|
1987
|
+
*
|
|
1988
|
+
* Returns an idempotent unsubscribe function.
|
|
1989
|
+
*
|
|
1990
|
+
* See also {@link getEntityStatus} for a synchronous point-in-time read.
|
|
1991
|
+
*/
|
|
1992
|
+
subscribeEntityStatus(entityId, callback) {
|
|
1993
|
+
const initial = this.getEntityStatus(entityId);
|
|
1994
|
+
let set = this.statusSubscribers.get(entityId);
|
|
1995
|
+
if (!set) {
|
|
1996
|
+
set = /* @__PURE__ */ new Set();
|
|
1997
|
+
this.statusSubscribers.set(entityId, set);
|
|
1998
|
+
}
|
|
1999
|
+
const entry = { callback, last: this._copyEntityStatus(initial) };
|
|
2000
|
+
set.add(entry);
|
|
2001
|
+
try {
|
|
2002
|
+
callback(this._copyEntityStatus(initial));
|
|
2003
|
+
} catch (err) {
|
|
2004
|
+
console.error(`[WikiMemory.subscribeEntityStatus] callback error for entityId="${entityId}" during initial emission`, err);
|
|
2005
|
+
}
|
|
2006
|
+
let active = true;
|
|
2007
|
+
return () => {
|
|
2008
|
+
if (!active) return;
|
|
2009
|
+
active = false;
|
|
2010
|
+
const s = this.statusSubscribers.get(entityId);
|
|
2011
|
+
if (!s) return;
|
|
2012
|
+
s.delete(entry);
|
|
2013
|
+
if (s.size === 0) this.statusSubscribers.delete(entityId);
|
|
2014
|
+
};
|
|
2015
|
+
}
|
|
1953
2016
|
clearVectorCache() {
|
|
1954
2017
|
this.vectorCache.clear();
|
|
1955
2018
|
}
|
|
@@ -2475,6 +2538,7 @@ The following document anchors are provided for contradiction detection only. Do
|
|
|
2475
2538
|
throw new WikiBusyError("forget", entityId);
|
|
2476
2539
|
}
|
|
2477
2540
|
this.activeIngestJobs.add(jobKey);
|
|
2541
|
+
this._notifyStatusSubscribers(entityId);
|
|
2478
2542
|
try {
|
|
2479
2543
|
const { chunks, truncated } = chunkText(params.documentChunk, maxChunkLength, chunkOverlap);
|
|
2480
2544
|
if (chunks.length === 0) {
|
|
@@ -2546,6 +2610,7 @@ ${chunk}`;
|
|
|
2546
2610
|
return { truncated, chunks: chunks.length };
|
|
2547
2611
|
} finally {
|
|
2548
2612
|
this.activeIngestJobs.delete(jobKey);
|
|
2613
|
+
this._notifyStatusSubscribers(entityId);
|
|
2549
2614
|
}
|
|
2550
2615
|
}
|
|
2551
2616
|
};
|