@equationalapplications/core-llm-wiki 4.23.1 → 5.1.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 +29 -2
- package/dist/{chunk-MYZJLVX4.mjs → chunk-DM7BKDCV.mjs} +220 -154
- package/dist/chunk-DM7BKDCV.mjs.map +1 -0
- package/dist/index.d.mts +64 -3
- package/dist/index.d.ts +64 -3
- package/dist/index.js +184 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +101 -12
- package/dist/index.mjs.map +1 -1
- package/dist/{testing-CBjAuTSl.d.mts → testing-DCRNie7k.d.mts} +118 -10
- package/dist/{testing-CBjAuTSl.d.ts → testing-DCRNie7k.d.ts} +118 -10
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +79 -13
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +2 -2
- package/dist/chunk-MYZJLVX4.mjs.map +0 -1
|
@@ -100,6 +100,39 @@ interface OntologyBackfillResult {
|
|
|
100
100
|
* Stamped with the recheck cooldown, so they reappear as `deferred` next pass. */
|
|
101
101
|
skipped: number;
|
|
102
102
|
}
|
|
103
|
+
/** Result of a single heal run. */
|
|
104
|
+
interface HealResult {
|
|
105
|
+
/**
|
|
106
|
+
* Heal candidates sent to the model this run. Counts candidates whose batch
|
|
107
|
+
* came back unusable and landed in `skipped` as well — they reached the
|
|
108
|
+
* provider too, so this is provider exposure, not successful throughput.
|
|
109
|
+
*/
|
|
110
|
+
scanned: number;
|
|
111
|
+
/**
|
|
112
|
+
* Facts whose confidence was lowered to `tentative` — the stale-inferred SQL
|
|
113
|
+
* pass (`inferred` → `tentative`) plus any model-directed downgrades, deduped.
|
|
114
|
+
*/
|
|
115
|
+
downgraded: number;
|
|
116
|
+
/** Facts soft-deleted — the orphan-marking SQL pass plus model-directed deletes, deduped. */
|
|
117
|
+
deleted: number;
|
|
118
|
+
/** New facts synthesized by heal. */
|
|
119
|
+
newFactsCreated: number;
|
|
120
|
+
/** Candidates a batch could not process even alone, skipped so the pass could finish. */
|
|
121
|
+
skipped: number;
|
|
122
|
+
/**
|
|
123
|
+
* Heal candidates still eligible after this run — convergence signal: loop while > 0.
|
|
124
|
+
*
|
|
125
|
+
* This does NOT mean what it means for OntologyBackfillResult. Untyped facts are
|
|
126
|
+
* a finite backlog that drains permanently; heal's candidates are the live mutable
|
|
127
|
+
* corpus. `remaining === 0` means "every mutable fact is inside the recheck
|
|
128
|
+
* cooldown", not "there is no more work" — it climbs back as the cooldown lapses
|
|
129
|
+
* and as new facts are written. A host loop terminates, but it converges to
|
|
130
|
+
* "corpus swept once this cooldown window", not to a drained queue.
|
|
131
|
+
*/
|
|
132
|
+
remaining: number;
|
|
133
|
+
/** Heal candidates inside the recheck cooldown. */
|
|
134
|
+
deferred: number;
|
|
135
|
+
}
|
|
103
136
|
interface WikiConfig {
|
|
104
137
|
/**
|
|
105
138
|
* Prefix applied to every SQL table/index/trigger name. Must match
|
|
@@ -664,11 +697,14 @@ declare class EntryRepository extends BaseRepository {
|
|
|
664
697
|
findAllByEntityId(entityId: string, tx?: SQLiteAdapter): Promise<WikiFact[]>;
|
|
665
698
|
/**
|
|
666
699
|
* Fetch live, mutable entries for an entity — everything heal is allowed to
|
|
667
|
-
* downgrade or delete
|
|
668
|
-
*
|
|
669
|
-
*
|
|
700
|
+
* downgrade or delete — oldest first, capped at `limit`, skipping anything
|
|
701
|
+
* stamped inside the recheck cooldown (heal_checked_at > recheckCutoff).
|
|
702
|
+
*
|
|
703
|
+
* Oldest-first rather than newest-first: with a cooldown in place, newest-first
|
|
704
|
+
* would keep re-selecting recently-touched facts every pass while older ones
|
|
705
|
+
* wait indefinitely to even enter a batch.
|
|
670
706
|
*/
|
|
671
|
-
findHealCandidatesByEntityId(entityId: string, tx?: SQLiteAdapter): Promise<WikiFact[]>;
|
|
707
|
+
findHealCandidatesByEntityId(entityId: string, limit: number, recheckCutoff: number, tx?: SQLiteAdapter): Promise<WikiFact[]>;
|
|
672
708
|
/**
|
|
673
709
|
* Resolve search hits to document anchors. The MiniSearch index holds every
|
|
674
710
|
* fact, not only immutable_document rows, so the source-type restriction has
|
|
@@ -719,8 +755,12 @@ declare class EntryRepository extends BaseRepository {
|
|
|
719
755
|
/**
|
|
720
756
|
* Downgrade stale inferred entries to 'tentative'.
|
|
721
757
|
* Used by MaintenanceService.doRunHeal().
|
|
758
|
+
*
|
|
759
|
+
* Returns the ids it downgraded so the caller can fold them into
|
|
760
|
+
* {@link HealResult.downgraded} without double-counting a fact the model also
|
|
761
|
+
* downgraded in the same pass.
|
|
722
762
|
*/
|
|
723
|
-
downgradeStaleInferred(entityId: string, staleThreshold: number, tx: SQLiteAdapter): Promise<
|
|
763
|
+
downgradeStaleInferred(entityId: string, staleThreshold: number, tx: SQLiteAdapter): Promise<string[]>;
|
|
724
764
|
/**
|
|
725
765
|
* Downgrade specific entries to 'tentative' by IDs.
|
|
726
766
|
* Used by MaintenanceService.doRunHeal().
|
|
@@ -804,6 +844,34 @@ declare class EntryRepository extends BaseRepository {
|
|
|
804
844
|
title: string;
|
|
805
845
|
okf_type: string | null;
|
|
806
846
|
}>>;
|
|
847
|
+
/** Counts live mutable facts: eligible (past cooldown) vs deferred (in cooldown). */
|
|
848
|
+
countHealCandidatesByEntityId(entityId: string, recheckCutoff: number, tx?: SQLiteAdapter): Promise<{
|
|
849
|
+
eligible: number;
|
|
850
|
+
deferred: number;
|
|
851
|
+
}>;
|
|
852
|
+
/**
|
|
853
|
+
* Stamps the heal recheck cooldown. NEVER touches updated_at — import merge
|
|
854
|
+
* resolution is last-write-wins on updated_at and a bump here would make an
|
|
855
|
+
* unchanged local fact beat a genuinely newer remote edit.
|
|
856
|
+
*
|
|
857
|
+
* Soft-deleted rows are skipped: a candidate heal deleted earlier in the same
|
|
858
|
+
* transaction is no longer a candidate under any future pass, so its cooldown
|
|
859
|
+
* value is irrelevant. This means the stamped count can be lower than the
|
|
860
|
+
* offered-candidate count.
|
|
861
|
+
*/
|
|
862
|
+
markHealChecked(ids: string[], entityId: string, now: number, tx: SQLiteAdapter): Promise<void>;
|
|
863
|
+
/**
|
|
864
|
+
* Lightweight full-breadth index (id, title) over all live librarian_inferred
|
|
865
|
+
* facts. This is heal's fuzzy-dedupe corpus, deliberately kept independent of
|
|
866
|
+
* the bounded candidate window: seeding dedupe from a batchSize-limited read
|
|
867
|
+
* would let a synthesized fact duplicating a fact outside the window pass the
|
|
868
|
+
* Jaccard check, and a convergence loop would multiply those duplicates across
|
|
869
|
+
* passes. Mirrors findTitleIndexByEntityId — full breadth, two columns, cheap.
|
|
870
|
+
*/
|
|
871
|
+
findInferredTitlesByEntityId(entityId: string, tx?: SQLiteAdapter): Promise<Array<{
|
|
872
|
+
id: string;
|
|
873
|
+
title: string;
|
|
874
|
+
}>>;
|
|
807
875
|
}
|
|
808
876
|
|
|
809
877
|
declare class MetadataRepository extends BaseRepository {
|
|
@@ -1203,6 +1271,15 @@ declare class EventRepository extends BaseRepository {
|
|
|
1203
1271
|
declare const ONTOLOGY_BACKFILL_BATCH_SIZE = 25;
|
|
1204
1272
|
declare const ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS = 40000;
|
|
1205
1273
|
declare const ONTOLOGY_BACKFILL_RECHECK_MS: number;
|
|
1274
|
+
/**
|
|
1275
|
+
* Heal candidates fetched per pass. Bounds the pool runBatched draws from, not
|
|
1276
|
+
* the size of an individual LLM call — runBatched still packs ~10 candidates per
|
|
1277
|
+
* request and splits further on failure. At this default one pass costs roughly
|
|
1278
|
+
* 2-3 provider calls instead of an unbounded count (#67).
|
|
1279
|
+
*/
|
|
1280
|
+
declare const HEAL_BATCH_SIZE = 25;
|
|
1281
|
+
/** Cooldown before an already-healed fact is offered again. Matches ontology backfill. */
|
|
1282
|
+
declare const HEAL_RECHECK_MS: number;
|
|
1206
1283
|
declare class MaintenanceService {
|
|
1207
1284
|
private db;
|
|
1208
1285
|
private prefix;
|
|
@@ -1231,7 +1308,8 @@ declare class MaintenanceService {
|
|
|
1231
1308
|
}): Promise<void>;
|
|
1232
1309
|
runHeal(entityId: string, options?: {
|
|
1233
1310
|
promptOverride?: string;
|
|
1234
|
-
|
|
1311
|
+
batchSize?: number;
|
|
1312
|
+
}): Promise<HealResult>;
|
|
1235
1313
|
runOntologyBackfill(entityId: string, options?: {
|
|
1236
1314
|
promptOverride?: string;
|
|
1237
1315
|
batchSize?: number;
|
|
@@ -1258,8 +1336,17 @@ declare class MaintenanceService {
|
|
|
1258
1336
|
}>;
|
|
1259
1337
|
/** Core librarian pass (locks handled by {@link runLibrarian}). Package-internal orchestration hook. */
|
|
1260
1338
|
doRunLibrarian(entityId: string, promptOverride?: string): Promise<void>;
|
|
1261
|
-
/**
|
|
1262
|
-
|
|
1339
|
+
/**
|
|
1340
|
+
* Core heal pass (locks handled by {@link runHeal}). Package-internal orchestration hook.
|
|
1341
|
+
*
|
|
1342
|
+
* Bounded: at most `batchSize` candidates per pass (#67). Loop on
|
|
1343
|
+
* `result.remaining > 0` for convergence — see {@link HealResult.remaining}
|
|
1344
|
+
* for what convergence means here.
|
|
1345
|
+
*/
|
|
1346
|
+
doRunHeal(entityId: string, options?: {
|
|
1347
|
+
promptOverride?: string;
|
|
1348
|
+
batchSize?: number;
|
|
1349
|
+
}): Promise<HealResult>;
|
|
1263
1350
|
/** Core ontology backfill pass (locks handled by {@link runOntologyBackfill}). Package-internal orchestration hook. */
|
|
1264
1351
|
doRunOntologyBackfill(entityId: string, options?: {
|
|
1265
1352
|
promptOverride?: string;
|
|
@@ -1371,6 +1458,12 @@ declare class WriteService {
|
|
|
1371
1458
|
constructor(db: SQLiteAdapter, options: WikiOptions, entryRepo: EntryRepository, eventRepo: EventRepository, metadataRepo: MetadataRepository, jobManager: JobManager, maintenanceService: MaintenanceService);
|
|
1372
1459
|
write(entityId: string, event: Omit<WikiEvent, 'id' | 'entity_id' | 'created_at'>): Promise<void>;
|
|
1373
1460
|
private runLibrarianThenMaybeHeal;
|
|
1461
|
+
/**
|
|
1462
|
+
* Run one bounded auto-heal pass if the heal checkpoint has fallen
|
|
1463
|
+
* `autoHealThreshold` events behind. Called after every write (see
|
|
1464
|
+
* {@link write}) so a partial pass retries on the next write.
|
|
1465
|
+
*/
|
|
1466
|
+
private maybeRunHeal;
|
|
1374
1467
|
}
|
|
1375
1468
|
|
|
1376
1469
|
/**
|
|
@@ -1452,13 +1545,28 @@ declare class WikiMemory {
|
|
|
1452
1545
|
promptOverride?: string;
|
|
1453
1546
|
}): Promise<void>;
|
|
1454
1547
|
/**
|
|
1548
|
+
* Reviews stored facts with the LLM: removes orphans, downgrades stale
|
|
1549
|
+
* inferences, synthesizes corrections.
|
|
1550
|
+
*
|
|
1551
|
+
* Bounded per call: covers at most `batchSize` candidates (default 25), so
|
|
1552
|
+
* one call no longer sweeps the whole entity. Hosts own the cadence; loop
|
|
1553
|
+
* `while (result.remaining > 0)` for convergence.
|
|
1554
|
+
*
|
|
1555
|
+
* `remaining === 0` means "every mutable fact is inside the 7-day recheck
|
|
1556
|
+
* cooldown", not "there is no more work" — heal's candidate set is the live
|
|
1557
|
+
* mutable corpus, not a draining backlog, so it climbs back as the cooldown
|
|
1558
|
+
* lapses and as new facts are written. Do not write a loop expecting a drain.
|
|
1559
|
+
*
|
|
1455
1560
|
* @param options.promptOverride - Applies only to this manual call. Does NOT affect
|
|
1456
1561
|
* WriteService-triggered auto-runs. For persistent prompt customization across auto-runs,
|
|
1457
1562
|
* set `options.config.prompts.healSystemPrompt` at WikiMemory construction time.
|
|
1563
|
+
* @param options.batchSize - Candidates per run (default 25) for providers with
|
|
1564
|
+
* tighter context limits.
|
|
1458
1565
|
*/
|
|
1459
1566
|
runHeal(entityId: string, options?: {
|
|
1460
1567
|
promptOverride?: string;
|
|
1461
|
-
|
|
1568
|
+
batchSize?: number;
|
|
1569
|
+
}): Promise<HealResult>;
|
|
1462
1570
|
/**
|
|
1463
1571
|
* Types already-persisted untyped facts (okf_type IS NULL) in place via one
|
|
1464
1572
|
* librarian-style LLM call. Strictly additive: never creates, deletes, or
|
|
@@ -1552,4 +1660,4 @@ declare class WikiMemory {
|
|
|
1552
1660
|
}): Promise<void>;
|
|
1553
1661
|
}
|
|
1554
1662
|
|
|
1555
|
-
export {
|
|
1663
|
+
export { MaintenanceService as $, WikiBusyError as A, type WikiBusyOperation as B, type WikiCheckpoint as C, type WikiConfig as D, type EntityStatus as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, type WikiEdge as I, type WikiEvent as J, type WikiFact as K, type LLMProvider as L, type MemoryBundle as M, type WikiMemoryTestAccess as N, type OntologyManifest as O, type PromptOverrides as P, type WikiOutboxEvent as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiTask as T, WikiTransactionError as U, type VectorRanker as V, type WikiOptions as W, EmbeddingService as X, ImportExportService as Y, IngestionService as Z, JobManager as _, type MemoryDump as a, RetrievalService as a0, SearchService as a1, WriteService as a2, type FormattedMemoryDump as b, WikiMemory as c, type ExtractedFact as d, type ExtractedFactEdge as e, type ExtractedFactWithOntology as f, type ExtractedTask as g, type GraphTraversalOptions as h, HEAL_RECHECK_MS as i, HOOK_TIMEOUT_MARKER as j, type HealResult as k, ONTOLOGY_BACKFILL_BATCH_SIZE as l, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as m, ONTOLOGY_BACKFILL_RECHECK_MS as n, type OntologyBackfillResult as o, type OntologyConfig as p, type OntologyEdgeType as q, type OntologyMode as r, type OntologyNodeType as s, type OntologyPromptContext as t, type OntologyUpdates as u, PromptService as v, PrunePartialFailureError as w, type VectorRankerFallback as x, type VectorRankerRankArgs as y, type VectorRankerSemanticResult as z };
|
|
@@ -100,6 +100,39 @@ interface OntologyBackfillResult {
|
|
|
100
100
|
* Stamped with the recheck cooldown, so they reappear as `deferred` next pass. */
|
|
101
101
|
skipped: number;
|
|
102
102
|
}
|
|
103
|
+
/** Result of a single heal run. */
|
|
104
|
+
interface HealResult {
|
|
105
|
+
/**
|
|
106
|
+
* Heal candidates sent to the model this run. Counts candidates whose batch
|
|
107
|
+
* came back unusable and landed in `skipped` as well — they reached the
|
|
108
|
+
* provider too, so this is provider exposure, not successful throughput.
|
|
109
|
+
*/
|
|
110
|
+
scanned: number;
|
|
111
|
+
/**
|
|
112
|
+
* Facts whose confidence was lowered to `tentative` — the stale-inferred SQL
|
|
113
|
+
* pass (`inferred` → `tentative`) plus any model-directed downgrades, deduped.
|
|
114
|
+
*/
|
|
115
|
+
downgraded: number;
|
|
116
|
+
/** Facts soft-deleted — the orphan-marking SQL pass plus model-directed deletes, deduped. */
|
|
117
|
+
deleted: number;
|
|
118
|
+
/** New facts synthesized by heal. */
|
|
119
|
+
newFactsCreated: number;
|
|
120
|
+
/** Candidates a batch could not process even alone, skipped so the pass could finish. */
|
|
121
|
+
skipped: number;
|
|
122
|
+
/**
|
|
123
|
+
* Heal candidates still eligible after this run — convergence signal: loop while > 0.
|
|
124
|
+
*
|
|
125
|
+
* This does NOT mean what it means for OntologyBackfillResult. Untyped facts are
|
|
126
|
+
* a finite backlog that drains permanently; heal's candidates are the live mutable
|
|
127
|
+
* corpus. `remaining === 0` means "every mutable fact is inside the recheck
|
|
128
|
+
* cooldown", not "there is no more work" — it climbs back as the cooldown lapses
|
|
129
|
+
* and as new facts are written. A host loop terminates, but it converges to
|
|
130
|
+
* "corpus swept once this cooldown window", not to a drained queue.
|
|
131
|
+
*/
|
|
132
|
+
remaining: number;
|
|
133
|
+
/** Heal candidates inside the recheck cooldown. */
|
|
134
|
+
deferred: number;
|
|
135
|
+
}
|
|
103
136
|
interface WikiConfig {
|
|
104
137
|
/**
|
|
105
138
|
* Prefix applied to every SQL table/index/trigger name. Must match
|
|
@@ -664,11 +697,14 @@ declare class EntryRepository extends BaseRepository {
|
|
|
664
697
|
findAllByEntityId(entityId: string, tx?: SQLiteAdapter): Promise<WikiFact[]>;
|
|
665
698
|
/**
|
|
666
699
|
* Fetch live, mutable entries for an entity — everything heal is allowed to
|
|
667
|
-
* downgrade or delete
|
|
668
|
-
*
|
|
669
|
-
*
|
|
700
|
+
* downgrade or delete — oldest first, capped at `limit`, skipping anything
|
|
701
|
+
* stamped inside the recheck cooldown (heal_checked_at > recheckCutoff).
|
|
702
|
+
*
|
|
703
|
+
* Oldest-first rather than newest-first: with a cooldown in place, newest-first
|
|
704
|
+
* would keep re-selecting recently-touched facts every pass while older ones
|
|
705
|
+
* wait indefinitely to even enter a batch.
|
|
670
706
|
*/
|
|
671
|
-
findHealCandidatesByEntityId(entityId: string, tx?: SQLiteAdapter): Promise<WikiFact[]>;
|
|
707
|
+
findHealCandidatesByEntityId(entityId: string, limit: number, recheckCutoff: number, tx?: SQLiteAdapter): Promise<WikiFact[]>;
|
|
672
708
|
/**
|
|
673
709
|
* Resolve search hits to document anchors. The MiniSearch index holds every
|
|
674
710
|
* fact, not only immutable_document rows, so the source-type restriction has
|
|
@@ -719,8 +755,12 @@ declare class EntryRepository extends BaseRepository {
|
|
|
719
755
|
/**
|
|
720
756
|
* Downgrade stale inferred entries to 'tentative'.
|
|
721
757
|
* Used by MaintenanceService.doRunHeal().
|
|
758
|
+
*
|
|
759
|
+
* Returns the ids it downgraded so the caller can fold them into
|
|
760
|
+
* {@link HealResult.downgraded} without double-counting a fact the model also
|
|
761
|
+
* downgraded in the same pass.
|
|
722
762
|
*/
|
|
723
|
-
downgradeStaleInferred(entityId: string, staleThreshold: number, tx: SQLiteAdapter): Promise<
|
|
763
|
+
downgradeStaleInferred(entityId: string, staleThreshold: number, tx: SQLiteAdapter): Promise<string[]>;
|
|
724
764
|
/**
|
|
725
765
|
* Downgrade specific entries to 'tentative' by IDs.
|
|
726
766
|
* Used by MaintenanceService.doRunHeal().
|
|
@@ -804,6 +844,34 @@ declare class EntryRepository extends BaseRepository {
|
|
|
804
844
|
title: string;
|
|
805
845
|
okf_type: string | null;
|
|
806
846
|
}>>;
|
|
847
|
+
/** Counts live mutable facts: eligible (past cooldown) vs deferred (in cooldown). */
|
|
848
|
+
countHealCandidatesByEntityId(entityId: string, recheckCutoff: number, tx?: SQLiteAdapter): Promise<{
|
|
849
|
+
eligible: number;
|
|
850
|
+
deferred: number;
|
|
851
|
+
}>;
|
|
852
|
+
/**
|
|
853
|
+
* Stamps the heal recheck cooldown. NEVER touches updated_at — import merge
|
|
854
|
+
* resolution is last-write-wins on updated_at and a bump here would make an
|
|
855
|
+
* unchanged local fact beat a genuinely newer remote edit.
|
|
856
|
+
*
|
|
857
|
+
* Soft-deleted rows are skipped: a candidate heal deleted earlier in the same
|
|
858
|
+
* transaction is no longer a candidate under any future pass, so its cooldown
|
|
859
|
+
* value is irrelevant. This means the stamped count can be lower than the
|
|
860
|
+
* offered-candidate count.
|
|
861
|
+
*/
|
|
862
|
+
markHealChecked(ids: string[], entityId: string, now: number, tx: SQLiteAdapter): Promise<void>;
|
|
863
|
+
/**
|
|
864
|
+
* Lightweight full-breadth index (id, title) over all live librarian_inferred
|
|
865
|
+
* facts. This is heal's fuzzy-dedupe corpus, deliberately kept independent of
|
|
866
|
+
* the bounded candidate window: seeding dedupe from a batchSize-limited read
|
|
867
|
+
* would let a synthesized fact duplicating a fact outside the window pass the
|
|
868
|
+
* Jaccard check, and a convergence loop would multiply those duplicates across
|
|
869
|
+
* passes. Mirrors findTitleIndexByEntityId — full breadth, two columns, cheap.
|
|
870
|
+
*/
|
|
871
|
+
findInferredTitlesByEntityId(entityId: string, tx?: SQLiteAdapter): Promise<Array<{
|
|
872
|
+
id: string;
|
|
873
|
+
title: string;
|
|
874
|
+
}>>;
|
|
807
875
|
}
|
|
808
876
|
|
|
809
877
|
declare class MetadataRepository extends BaseRepository {
|
|
@@ -1203,6 +1271,15 @@ declare class EventRepository extends BaseRepository {
|
|
|
1203
1271
|
declare const ONTOLOGY_BACKFILL_BATCH_SIZE = 25;
|
|
1204
1272
|
declare const ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS = 40000;
|
|
1205
1273
|
declare const ONTOLOGY_BACKFILL_RECHECK_MS: number;
|
|
1274
|
+
/**
|
|
1275
|
+
* Heal candidates fetched per pass. Bounds the pool runBatched draws from, not
|
|
1276
|
+
* the size of an individual LLM call — runBatched still packs ~10 candidates per
|
|
1277
|
+
* request and splits further on failure. At this default one pass costs roughly
|
|
1278
|
+
* 2-3 provider calls instead of an unbounded count (#67).
|
|
1279
|
+
*/
|
|
1280
|
+
declare const HEAL_BATCH_SIZE = 25;
|
|
1281
|
+
/** Cooldown before an already-healed fact is offered again. Matches ontology backfill. */
|
|
1282
|
+
declare const HEAL_RECHECK_MS: number;
|
|
1206
1283
|
declare class MaintenanceService {
|
|
1207
1284
|
private db;
|
|
1208
1285
|
private prefix;
|
|
@@ -1231,7 +1308,8 @@ declare class MaintenanceService {
|
|
|
1231
1308
|
}): Promise<void>;
|
|
1232
1309
|
runHeal(entityId: string, options?: {
|
|
1233
1310
|
promptOverride?: string;
|
|
1234
|
-
|
|
1311
|
+
batchSize?: number;
|
|
1312
|
+
}): Promise<HealResult>;
|
|
1235
1313
|
runOntologyBackfill(entityId: string, options?: {
|
|
1236
1314
|
promptOverride?: string;
|
|
1237
1315
|
batchSize?: number;
|
|
@@ -1258,8 +1336,17 @@ declare class MaintenanceService {
|
|
|
1258
1336
|
}>;
|
|
1259
1337
|
/** Core librarian pass (locks handled by {@link runLibrarian}). Package-internal orchestration hook. */
|
|
1260
1338
|
doRunLibrarian(entityId: string, promptOverride?: string): Promise<void>;
|
|
1261
|
-
/**
|
|
1262
|
-
|
|
1339
|
+
/**
|
|
1340
|
+
* Core heal pass (locks handled by {@link runHeal}). Package-internal orchestration hook.
|
|
1341
|
+
*
|
|
1342
|
+
* Bounded: at most `batchSize` candidates per pass (#67). Loop on
|
|
1343
|
+
* `result.remaining > 0` for convergence — see {@link HealResult.remaining}
|
|
1344
|
+
* for what convergence means here.
|
|
1345
|
+
*/
|
|
1346
|
+
doRunHeal(entityId: string, options?: {
|
|
1347
|
+
promptOverride?: string;
|
|
1348
|
+
batchSize?: number;
|
|
1349
|
+
}): Promise<HealResult>;
|
|
1263
1350
|
/** Core ontology backfill pass (locks handled by {@link runOntologyBackfill}). Package-internal orchestration hook. */
|
|
1264
1351
|
doRunOntologyBackfill(entityId: string, options?: {
|
|
1265
1352
|
promptOverride?: string;
|
|
@@ -1371,6 +1458,12 @@ declare class WriteService {
|
|
|
1371
1458
|
constructor(db: SQLiteAdapter, options: WikiOptions, entryRepo: EntryRepository, eventRepo: EventRepository, metadataRepo: MetadataRepository, jobManager: JobManager, maintenanceService: MaintenanceService);
|
|
1372
1459
|
write(entityId: string, event: Omit<WikiEvent, 'id' | 'entity_id' | 'created_at'>): Promise<void>;
|
|
1373
1460
|
private runLibrarianThenMaybeHeal;
|
|
1461
|
+
/**
|
|
1462
|
+
* Run one bounded auto-heal pass if the heal checkpoint has fallen
|
|
1463
|
+
* `autoHealThreshold` events behind. Called after every write (see
|
|
1464
|
+
* {@link write}) so a partial pass retries on the next write.
|
|
1465
|
+
*/
|
|
1466
|
+
private maybeRunHeal;
|
|
1374
1467
|
}
|
|
1375
1468
|
|
|
1376
1469
|
/**
|
|
@@ -1452,13 +1545,28 @@ declare class WikiMemory {
|
|
|
1452
1545
|
promptOverride?: string;
|
|
1453
1546
|
}): Promise<void>;
|
|
1454
1547
|
/**
|
|
1548
|
+
* Reviews stored facts with the LLM: removes orphans, downgrades stale
|
|
1549
|
+
* inferences, synthesizes corrections.
|
|
1550
|
+
*
|
|
1551
|
+
* Bounded per call: covers at most `batchSize` candidates (default 25), so
|
|
1552
|
+
* one call no longer sweeps the whole entity. Hosts own the cadence; loop
|
|
1553
|
+
* `while (result.remaining > 0)` for convergence.
|
|
1554
|
+
*
|
|
1555
|
+
* `remaining === 0` means "every mutable fact is inside the 7-day recheck
|
|
1556
|
+
* cooldown", not "there is no more work" — heal's candidate set is the live
|
|
1557
|
+
* mutable corpus, not a draining backlog, so it climbs back as the cooldown
|
|
1558
|
+
* lapses and as new facts are written. Do not write a loop expecting a drain.
|
|
1559
|
+
*
|
|
1455
1560
|
* @param options.promptOverride - Applies only to this manual call. Does NOT affect
|
|
1456
1561
|
* WriteService-triggered auto-runs. For persistent prompt customization across auto-runs,
|
|
1457
1562
|
* set `options.config.prompts.healSystemPrompt` at WikiMemory construction time.
|
|
1563
|
+
* @param options.batchSize - Candidates per run (default 25) for providers with
|
|
1564
|
+
* tighter context limits.
|
|
1458
1565
|
*/
|
|
1459
1566
|
runHeal(entityId: string, options?: {
|
|
1460
1567
|
promptOverride?: string;
|
|
1461
|
-
|
|
1568
|
+
batchSize?: number;
|
|
1569
|
+
}): Promise<HealResult>;
|
|
1462
1570
|
/**
|
|
1463
1571
|
* Types already-persisted untyped facts (okf_type IS NULL) in place via one
|
|
1464
1572
|
* librarian-style LLM call. Strictly additive: never creates, deletes, or
|
|
@@ -1552,4 +1660,4 @@ declare class WikiMemory {
|
|
|
1552
1660
|
}): Promise<void>;
|
|
1553
1661
|
}
|
|
1554
1662
|
|
|
1555
|
-
export {
|
|
1663
|
+
export { MaintenanceService as $, WikiBusyError as A, type WikiBusyOperation as B, type WikiCheckpoint as C, type WikiConfig as D, type EntityStatus as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, type WikiEdge as I, type WikiEvent as J, type WikiFact as K, type LLMProvider as L, type MemoryBundle as M, type WikiMemoryTestAccess as N, type OntologyManifest as O, type PromptOverrides as P, type WikiOutboxEvent as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiTask as T, WikiTransactionError as U, type VectorRanker as V, type WikiOptions as W, EmbeddingService as X, ImportExportService as Y, IngestionService as Z, JobManager as _, type MemoryDump as a, RetrievalService as a0, SearchService as a1, WriteService as a2, type FormattedMemoryDump as b, WikiMemory as c, type ExtractedFact as d, type ExtractedFactEdge as e, type ExtractedFactWithOntology as f, type ExtractedTask as g, type GraphTraversalOptions as h, HEAL_RECHECK_MS as i, HOOK_TIMEOUT_MARKER as j, type HealResult as k, ONTOLOGY_BACKFILL_BATCH_SIZE as l, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as m, ONTOLOGY_BACKFILL_RECHECK_MS as n, type OntologyBackfillResult as o, type OntologyConfig as p, type OntologyEdgeType as q, type OntologyMode as r, type OntologyNodeType as s, type OntologyPromptContext as t, type OntologyUpdates as u, PromptService as v, PrunePartialFailureError as w, type VectorRankerFallback as x, type VectorRankerRankArgs as y, type VectorRankerSemanticResult as z };
|
package/dist/testing.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { X as EmbeddingService, Y as ImportExportService, Z as IngestionService, _ as JobManager, _ as JobManagerType, $ as MaintenanceService, a0 as RetrievalService, a1 as SearchService, a1 as SearchServiceType, N as WikiMemoryTestAccess, a2 as WriteService } from './testing-DCRNie7k.mjs';
|
|
2
2
|
import 'minisearch';
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { X as EmbeddingService, Y as ImportExportService, Z as IngestionService, _ as JobManager, _ as JobManagerType, $ as MaintenanceService, a0 as RetrievalService, a1 as SearchService, a1 as SearchServiceType, N as WikiMemoryTestAccess, a2 as WriteService } from './testing-DCRNie7k.js';
|
|
2
2
|
import 'minisearch';
|
package/dist/testing.js
CHANGED
|
@@ -969,6 +969,10 @@ ${JSON.stringify(facts, null, 2)}`
|
|
|
969
969
|
}
|
|
970
970
|
};
|
|
971
971
|
|
|
972
|
+
// src/utils/chunkingDefaults.ts
|
|
973
|
+
var DEFAULT_MAX_CHUNK_LENGTH = 12e3;
|
|
974
|
+
var DEFAULT_CHUNK_OVERLAP = 400;
|
|
975
|
+
|
|
972
976
|
// src/services/IngestionService.ts
|
|
973
977
|
var IngestionService = class {
|
|
974
978
|
constructor(db, prefix, options, entryRepo, searchService, jobManager, embeddingService, promptService, ontologyService) {
|
|
@@ -987,10 +991,10 @@ var IngestionService = class {
|
|
|
987
991
|
if (!sourceRef) throw new Error("Invalid sourceRef");
|
|
988
992
|
const sourceHash = normalizeSourceHash(params.sourceHash);
|
|
989
993
|
if (!sourceHash) throw new Error("Invalid sourceHash (must be 64-char hex string)");
|
|
990
|
-
const maxChunkLength = params.maxChunkLength ?? this.options.config?.maxChunkLength ??
|
|
991
|
-
const rawOverlap = params.chunkOverlap ?? this.options.config?.chunkOverlap ??
|
|
994
|
+
const maxChunkLength = params.maxChunkLength ?? this.options.config?.maxChunkLength ?? DEFAULT_MAX_CHUNK_LENGTH;
|
|
995
|
+
const rawOverlap = params.chunkOverlap ?? this.options.config?.chunkOverlap ?? DEFAULT_CHUNK_OVERLAP;
|
|
992
996
|
const chunkOverlap = Math.min(
|
|
993
|
-
Number.isFinite(rawOverlap) && rawOverlap >= 0 ? Math.floor(rawOverlap) :
|
|
997
|
+
Number.isFinite(rawOverlap) && rawOverlap >= 0 ? Math.floor(rawOverlap) : DEFAULT_CHUNK_OVERLAP,
|
|
994
998
|
maxChunkLength - 1
|
|
995
999
|
);
|
|
996
1000
|
const rawConcurrency = params.chunkConcurrency ?? this.options.config?.chunkConcurrency ?? 1;
|
|
@@ -1261,6 +1265,8 @@ var ONTOLOGY_BACKFILL_RECHECK_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
|
1261
1265
|
var HEAL_MAX_ANCHORS = 50;
|
|
1262
1266
|
var HEAL_ANCHOR_SEARCH_OVERFETCH = 4;
|
|
1263
1267
|
var HEAL_MAX_PROMPT_CHARS = 4e4;
|
|
1268
|
+
var HEAL_BATCH_SIZE = 25;
|
|
1269
|
+
var HEAL_RECHECK_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
1264
1270
|
var MaintenanceService = class {
|
|
1265
1271
|
constructor(db, prefix, options, entryRepo, taskRepo, eventRepo, metadataRepo, searchService, jobManager, embeddingService, promptService, ontologyService) {
|
|
1266
1272
|
this.db = db;
|
|
@@ -1361,7 +1367,7 @@ var MaintenanceService = class {
|
|
|
1361
1367
|
async runHeal(entityId, options) {
|
|
1362
1368
|
this.jobManager.acquireLock("heal", entityId);
|
|
1363
1369
|
try {
|
|
1364
|
-
await this.doRunHeal(entityId, options
|
|
1370
|
+
return await this.doRunHeal(entityId, options);
|
|
1365
1371
|
} finally {
|
|
1366
1372
|
this.jobManager.releaseLock("heal", entityId);
|
|
1367
1373
|
}
|
|
@@ -1613,9 +1619,21 @@ var MaintenanceService = class {
|
|
|
1613
1619
|
}
|
|
1614
1620
|
this.searchService.evictCache(entityId);
|
|
1615
1621
|
}
|
|
1616
|
-
/**
|
|
1617
|
-
|
|
1622
|
+
/**
|
|
1623
|
+
* Core heal pass (locks handled by {@link runHeal}). Package-internal orchestration hook.
|
|
1624
|
+
*
|
|
1625
|
+
* Bounded: at most `batchSize` candidates per pass (#67). Loop on
|
|
1626
|
+
* `result.remaining > 0` for convergence — see {@link HealResult.remaining}
|
|
1627
|
+
* for what convergence means here.
|
|
1628
|
+
*/
|
|
1629
|
+
async doRunHeal(entityId, options) {
|
|
1630
|
+
const promptOverride = options?.promptOverride;
|
|
1631
|
+
const batchSize = options?.batchSize ?? HEAL_BATCH_SIZE;
|
|
1632
|
+
if (!Number.isInteger(batchSize) || batchSize < 1) {
|
|
1633
|
+
throw new Error("Invalid batchSize: must be an integer >= 1");
|
|
1634
|
+
}
|
|
1618
1635
|
const now = Date.now();
|
|
1636
|
+
const recheckCutoff = now - HEAL_RECHECK_MS;
|
|
1619
1637
|
const orphanAfterDays = this.options.config?.orphanAfterDays !== void 0 ? this.options.config?.orphanAfterDays : 30;
|
|
1620
1638
|
const staleInferredAfterDays = this.options.config?.staleInferredAfterDays !== void 0 ? this.options.config?.staleInferredAfterDays : 60;
|
|
1621
1639
|
const MS_PER_DAY = 24 * 60 * 60 * 1e3;
|
|
@@ -1626,6 +1644,7 @@ var MaintenanceService = class {
|
|
|
1626
1644
|
throw new Error("Invalid staleInferredAfterDays: must be a finite number >= 0 or null");
|
|
1627
1645
|
}
|
|
1628
1646
|
const orphanedIds = [];
|
|
1647
|
+
const staleDowngradedIds = [];
|
|
1629
1648
|
await this.db.withTransactionAsync(async (tx) => {
|
|
1630
1649
|
if (orphanAfterDays !== null) {
|
|
1631
1650
|
const orphanThreshold = now - orphanAfterDays * MS_PER_DAY;
|
|
@@ -1633,7 +1652,7 @@ var MaintenanceService = class {
|
|
|
1633
1652
|
}
|
|
1634
1653
|
if (staleInferredAfterDays !== null) {
|
|
1635
1654
|
const staleThreshold = now - staleInferredAfterDays * MS_PER_DAY;
|
|
1636
|
-
await this.entryRepo.downgradeStaleInferred(entityId, staleThreshold, tx);
|
|
1655
|
+
staleDowngradedIds.push(...await this.entryRepo.downgradeStaleInferred(entityId, staleThreshold, tx));
|
|
1637
1656
|
}
|
|
1638
1657
|
});
|
|
1639
1658
|
for (const factId of orphanedIds) {
|
|
@@ -1643,7 +1662,21 @@ var MaintenanceService = class {
|
|
|
1643
1662
|
console.warn(`[WikiMemory] onEmbeddingPersisted hook failed during heal orphan pass for ${factId}:`, hookErr);
|
|
1644
1663
|
}
|
|
1645
1664
|
}
|
|
1646
|
-
const healCandidates = await this.entryRepo.findHealCandidatesByEntityId(entityId);
|
|
1665
|
+
const healCandidates = await this.entryRepo.findHealCandidatesByEntityId(entityId, batchSize, recheckCutoff);
|
|
1666
|
+
if (healCandidates.length === 0) {
|
|
1667
|
+
await this.searchService.sync(entityId);
|
|
1668
|
+
this.searchService.evictCache(entityId);
|
|
1669
|
+
const counts2 = await this.entryRepo.countHealCandidatesByEntityId(entityId, recheckCutoff);
|
|
1670
|
+
return {
|
|
1671
|
+
scanned: 0,
|
|
1672
|
+
downgraded: staleDowngradedIds.length,
|
|
1673
|
+
deleted: orphanedIds.length,
|
|
1674
|
+
newFactsCreated: 0,
|
|
1675
|
+
skipped: 0,
|
|
1676
|
+
remaining: counts2.eligible,
|
|
1677
|
+
deferred: counts2.deferred
|
|
1678
|
+
};
|
|
1679
|
+
}
|
|
1647
1680
|
const allTasks = await this.taskRepo.findAllPending([entityId]);
|
|
1648
1681
|
const recentEvents = await this.eventRepo.getRecent(entityId, 20);
|
|
1649
1682
|
const toPromptShape = (f) => {
|
|
@@ -1696,7 +1729,7 @@ var MaintenanceService = class {
|
|
|
1696
1729
|
const validNewFacts = newFacts.map(validateFact).filter((f) => f !== null);
|
|
1697
1730
|
const insertedFacts = [];
|
|
1698
1731
|
const uniqueDeletedFactIds = Array.from(new Set(safeDeleted));
|
|
1699
|
-
const healFactsForDedupe =
|
|
1732
|
+
const healFactsForDedupe = (await this.entryRepo.findInferredTitlesByEntityId(entityId)).filter((f) => !safeDeletedSet.has(f.id));
|
|
1700
1733
|
await this.db.withTransactionAsync(async (tx) => {
|
|
1701
1734
|
await this.entryRepo.downgradeByIds(safeDowngraded, entityId, tx);
|
|
1702
1735
|
await this.entryRepo.softDeleteByIds(safeDeleted, entityId, tx);
|
|
@@ -1705,7 +1738,6 @@ var MaintenanceService = class {
|
|
|
1705
1738
|
let skip = false;
|
|
1706
1739
|
if (newTokens.size >= MIN_TOKENS_TO_QUALIFY) {
|
|
1707
1740
|
for (const existing of healFactsForDedupe) {
|
|
1708
|
-
if (existing.source_type !== "librarian_inferred") continue;
|
|
1709
1741
|
const existingTokens = titleTokens(existing.title);
|
|
1710
1742
|
if (existingTokens.size >= MIN_TOKENS_TO_QUALIFY) {
|
|
1711
1743
|
if (jaccardScore(newTokens, existingTokens) >= FUZZY_THRESHOLD) {
|
|
@@ -1735,8 +1767,14 @@ var MaintenanceService = class {
|
|
|
1735
1767
|
};
|
|
1736
1768
|
await this.entryRepo.upsert(factObj, tx);
|
|
1737
1769
|
insertedFacts.push({ id, entity_id: entityId, title: fact.title, body: fact.body, tags: JSON.stringify(fact.tags) });
|
|
1738
|
-
healFactsForDedupe.push(
|
|
1770
|
+
healFactsForDedupe.push({ id, title: fact.title });
|
|
1739
1771
|
}
|
|
1772
|
+
await this.entryRepo.markHealChecked(
|
|
1773
|
+
[...healCandidates.map((f) => f.id), ...insertedFacts.map((f) => f.id)],
|
|
1774
|
+
entityId,
|
|
1775
|
+
now,
|
|
1776
|
+
tx
|
|
1777
|
+
);
|
|
1740
1778
|
});
|
|
1741
1779
|
await this.searchService.sync(entityId);
|
|
1742
1780
|
for (const factId of uniqueDeletedFactIds) {
|
|
@@ -1750,6 +1788,20 @@ var MaintenanceService = class {
|
|
|
1750
1788
|
await this.embeddingService.embedFact(fact);
|
|
1751
1789
|
}
|
|
1752
1790
|
this.searchService.evictCache(entityId);
|
|
1791
|
+
let scanned = outcome.skipped.length;
|
|
1792
|
+
for (const batchResult of outcome.results) scanned += batchResult.batch.length;
|
|
1793
|
+
const allDowngraded = /* @__PURE__ */ new Set([...staleDowngradedIds, ...safeDowngraded]);
|
|
1794
|
+
const allDeleted = /* @__PURE__ */ new Set([...orphanedIds, ...uniqueDeletedFactIds]);
|
|
1795
|
+
const counts = await this.entryRepo.countHealCandidatesByEntityId(entityId, recheckCutoff);
|
|
1796
|
+
return {
|
|
1797
|
+
scanned,
|
|
1798
|
+
downgraded: allDowngraded.size,
|
|
1799
|
+
deleted: allDeleted.size,
|
|
1800
|
+
newFactsCreated: insertedFacts.length,
|
|
1801
|
+
skipped: outcome.skipped.length,
|
|
1802
|
+
remaining: counts.eligible,
|
|
1803
|
+
deferred: counts.deferred
|
|
1804
|
+
};
|
|
1753
1805
|
}
|
|
1754
1806
|
/** Core ontology backfill pass (locks handled by {@link runOntologyBackfill}). Package-internal orchestration hook. */
|
|
1755
1807
|
async doRunOntologyBackfill(entityId, options) {
|
|
@@ -3113,6 +3165,7 @@ var WriteService = class {
|
|
|
3113
3165
|
let shouldRunLibrarian = false;
|
|
3114
3166
|
let librarianCount = 0;
|
|
3115
3167
|
let prevMemoryCheckpoint = 0;
|
|
3168
|
+
let eventCount = 0;
|
|
3116
3169
|
await this.db.withTransactionAsync(async (tx) => {
|
|
3117
3170
|
await this.eventRepo.add(newEvent, tx);
|
|
3118
3171
|
const threshold = this.options.config?.autoLibrarianThreshold || 20;
|
|
@@ -3120,6 +3173,7 @@ var WriteService = class {
|
|
|
3120
3173
|
this.eventRepo.count(entityId, tx),
|
|
3121
3174
|
this.metadataRepo.getCheckpoint(entityId, tx)
|
|
3122
3175
|
]);
|
|
3176
|
+
eventCount = count;
|
|
3123
3177
|
let memoryCheckpoint = cp.memory ?? 0;
|
|
3124
3178
|
if (memoryCheckpoint > count) memoryCheckpoint = 0;
|
|
3125
3179
|
if (count - memoryCheckpoint >= threshold) {
|
|
@@ -3141,6 +3195,8 @@ var WriteService = class {
|
|
|
3141
3195
|
if (!(e instanceof WikiBusyError)) throw e;
|
|
3142
3196
|
await this.metadataRepo.updateCheckpoint(entityId, { memory: prevMemoryCheckpoint }, this.db);
|
|
3143
3197
|
}
|
|
3198
|
+
} else if (!this.jobManager.isBlocked("librarian", entityId)) {
|
|
3199
|
+
this.maybeRunHeal(entityId, eventCount).catch(console.error);
|
|
3144
3200
|
}
|
|
3145
3201
|
}
|
|
3146
3202
|
async runLibrarianThenMaybeHeal(entityId, currentEventCount, prevCheckpoint) {
|
|
@@ -3151,6 +3207,14 @@ var WriteService = class {
|
|
|
3151
3207
|
await this.metadataRepo.updateCheckpoint(entityId, { memory: prevCheckpoint }, this.db);
|
|
3152
3208
|
throw e;
|
|
3153
3209
|
}
|
|
3210
|
+
await this.maybeRunHeal(entityId, currentEventCount);
|
|
3211
|
+
}
|
|
3212
|
+
/**
|
|
3213
|
+
* Run one bounded auto-heal pass if the heal checkpoint has fallen
|
|
3214
|
+
* `autoHealThreshold` events behind. Called after every write (see
|
|
3215
|
+
* {@link write}) so a partial pass retries on the next write.
|
|
3216
|
+
*/
|
|
3217
|
+
async maybeRunHeal(entityId, currentEventCount) {
|
|
3154
3218
|
const autoHealThreshold = this.options.config?.autoHealThreshold || 100;
|
|
3155
3219
|
const cp = await this.metadataRepo.getCheckpoint(entityId, this.db);
|
|
3156
3220
|
let healCheckpoint = cp.heal ?? 0;
|
|
@@ -3158,8 +3222,10 @@ var WriteService = class {
|
|
|
3158
3222
|
const shouldRunHeal = currentEventCount - healCheckpoint >= autoHealThreshold;
|
|
3159
3223
|
if (shouldRunHeal && this.jobManager.tryAcquireAutoHealLock(entityId)) {
|
|
3160
3224
|
try {
|
|
3161
|
-
await this.maintenanceService.doRunHeal(entityId);
|
|
3162
|
-
|
|
3225
|
+
const result = await this.maintenanceService.doRunHeal(entityId);
|
|
3226
|
+
if (result.remaining === 0) {
|
|
3227
|
+
await this.metadataRepo.updateCheckpoint(entityId, { heal: currentEventCount }, this.db);
|
|
3228
|
+
}
|
|
3163
3229
|
} finally {
|
|
3164
3230
|
this.jobManager.releaseLock("heal", entityId);
|
|
3165
3231
|
}
|