@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.
@@ -13,6 +13,15 @@ interface SQLiteAdapter {
13
13
  }>;
14
14
  getAllAsync<T>(sql: string, params?: unknown[]): Promise<T[]>;
15
15
  getFirstAsync<T>(sql: string, params?: unknown[]): Promise<T | null>;
16
+ /**
17
+ * Runs `fn` inside a transaction. A bare adapter implementation is not required to
18
+ * serialize concurrent calls itself — `WikiMemory` wraps every adapter it's given in
19
+ * `withSerializedTransactions` (see `db/serializedAdapter.ts`), which is what makes
20
+ * transactions serialize in practice. Once wrapped, inside the callback use ONLY the
21
+ * provided `tx` handle — never the outer database handle (captured via closure or
22
+ * `this.db`). Calling the outer handle deadlocks against the transaction mutex;
23
+ * calling `tx.withTransactionAsync` throws (nested transactions are unsupported).
24
+ */
16
25
  withTransactionAsync<T>(fn: (tx: SQLiteAdapter) => Promise<T>): Promise<T>;
17
26
  closeAsync(): Promise<void>;
18
27
  }
@@ -20,6 +29,7 @@ interface PromptOverrides {
20
29
  ingestSystemPrompt?: string;
21
30
  librarianSystemPrompt?: string;
22
31
  healSystemPrompt?: string;
32
+ ontologyBackfillSystemPrompt?: string;
23
33
  }
24
34
  type OntologyMode = 'strict' | 'emergent' | 'off';
25
35
  interface OntologyNodeType {
@@ -69,6 +79,24 @@ interface OntologyPromptContext {
69
79
  ontologyManifest: string;
70
80
  ontologyModeInstructions: string;
71
81
  }
82
+ /** Result of a single ontology backfill run. Omissions (facts the model declined
83
+ * to classify) are derivable as scanned − typed − failedValidation. */
84
+ interface OntologyBackfillResult {
85
+ /** Untyped facts sent to the model this run. */
86
+ scanned: number;
87
+ /** Facts that received an okf_type. */
88
+ typed: number;
89
+ /** Model classifications rejected (unknown/duplicate id, non-manifest type). */
90
+ failedValidation: number;
91
+ /** Edges persisted. */
92
+ edgesAdded: number;
93
+ /** Untyped facts still eligible after this run — safe host convergence signal: loop while > 0.
94
+ * Always 0 when ontology mode is 'off' (nothing is eligible for typing while
95
+ * disabled), so 0 does not imply queue exhaustion in that mode. */
96
+ remaining: number;
97
+ /** Untyped facts inside the recheck cooldown. */
98
+ deferred: number;
99
+ }
72
100
  interface WikiConfig {
73
101
  /**
74
102
  * Prefix applied to every SQL table/index/trigger name. Must match
@@ -456,10 +484,11 @@ interface EntityStatus {
456
484
  *
457
485
  * @remarks **Breaking change from v2.x** — the union previously only contained
458
486
  * `'ingest' | 'librarian' | 'heal' | 'prune' | 'reembed'`. The values `'import'`
459
- * and `'forget'` were added in v3.0. Exhaustive `switch` / narrowing on this type
460
- * must be updated (or given a `default` arm) to compile without errors.
487
+ * and `'forget'` were added in v3.0. `'ontologyBackfill'` was added afterward.
488
+ * Exhaustive `switch` / narrowing on this type must be updated (or given a
489
+ * `default` arm) to compile without errors.
461
490
  */
462
- type WikiBusyOperation = 'ingest' | 'librarian' | 'heal' | 'prune' | 'reembed' | 'import' | 'forget';
491
+ type WikiBusyOperation = 'ingest' | 'librarian' | 'heal' | 'prune' | 'reembed' | 'import' | 'forget' | 'ontologyBackfill';
463
492
  /**
464
493
  * Thrown when a background mutator is already running for the requested entity.
465
494
  */
@@ -468,6 +497,19 @@ declare class WikiBusyError extends Error {
468
497
  readonly entityId: string;
469
498
  constructor(operation: WikiBusyOperation, entityId: string);
470
499
  }
500
+ /**
501
+ * Thrown by the serialized transaction wrapper when a SQLite driver error
502
+ * escapes a transaction callback (nested BEGIN, SQLITE_BUSY, constraint
503
+ * violation). Domain errors thrown from callback logic pass through unwrapped.
504
+ * Stable `instanceof` target for observability, mirroring {@link WikiBusyError}.
505
+ */
506
+ declare class WikiTransactionError extends Error {
507
+ /** Best-effort SQLite code lifted from the driver error, e.g. 'SQLITE_BUSY'. */
508
+ readonly sqliteErrorCode?: string;
509
+ constructor(message: string, options: {
510
+ cause: unknown;
511
+ });
512
+ }
471
513
  declare class PrunePartialFailureError extends Error {
472
514
  readonly deleted: number;
473
515
  readonly failedAt: string;
@@ -695,6 +737,42 @@ declare class EntryRepository extends BaseRepository {
695
737
  trackAccess(ids: readonly string[], now: number, tx?: SQLiteAdapter): Promise<void>;
696
738
  getLegacyMigrationSQL(): string;
697
739
  findRecentByEntityIds(entityIds: readonly string[], limit: number, tx?: SQLiteAdapter): Promise<WikiFact[]>;
740
+ /**
741
+ * Live untyped facts eligible for ontology backfill, oldest first.
742
+ * Skips facts checked within the recheck cooldown (ontology_checked_at > recheckCutoff).
743
+ */
744
+ findUntypedByEntityId(entityId: string, limit: number, recheckCutoff: number, tx?: SQLiteAdapter): Promise<WikiFact[]>;
745
+ /** Counts live untyped facts: eligible (past cooldown) vs deferred (in cooldown). */
746
+ countUntypedByEntityId(entityId: string, recheckCutoff: number, tx?: SQLiteAdapter): Promise<{
747
+ eligible: number;
748
+ deferred: number;
749
+ }>;
750
+ /**
751
+ * Sets okf_type only when currently NULL — additive by construction: a
752
+ * concurrently-typed fact is never overwritten. Returns changes for the caller
753
+ * to distinguish applied vs skipped.
754
+ */
755
+ updateOkfType(id: string, entityId: string, okfType: string, tx: SQLiteAdapter): Promise<{
756
+ changes: number;
757
+ }>;
758
+ /**
759
+ * Stamps the backfill recheck cooldown. NEVER touches updated_at — import
760
+ * merge resolution is last-write-wins on updated_at and a bump here would
761
+ * make an unchanged local fact beat a genuinely newer remote edit.
762
+ */
763
+ markOntologyChecked(ids: string[], entityId: string, now: number, tx: SQLiteAdapter): Promise<void>;
764
+ /**
765
+ * Lightweight full-breadth title index (id, title, okf_type) over all live
766
+ * typed facts. Used by ontology backfill edge resolution — a recent-N window
767
+ * would miss an old fact's contemporaries. Untyped rows are excluded: they
768
+ * can never pass the edge target-type check, and batch facts typed during
769
+ * the run are re-added to the in-memory index by the caller.
770
+ */
771
+ findTitleIndexByEntityId(entityId: string, tx?: SQLiteAdapter): Promise<Array<{
772
+ id: string;
773
+ title: string;
774
+ okf_type: string | null;
775
+ }>>;
698
776
  }
699
777
 
700
778
  declare class MetadataRepository extends BaseRepository {
@@ -801,7 +879,7 @@ declare class SearchService {
801
879
  private _compareScoredRows;
802
880
  }
803
881
 
804
- type OperationType = 'prune' | 'librarian' | 'heal' | 'ingest' | 'reembed' | 'global_reembed' | 'import' | 'global_import' | 'forget';
882
+ type OperationType = 'prune' | 'librarian' | 'heal' | 'ingest' | 'reembed' | 'global_reembed' | 'import' | 'global_import' | 'forget' | 'ontologyBackfill';
805
883
  declare class JobManager {
806
884
  private prefix;
807
885
  private activeMaintenanceJobs;
@@ -816,6 +894,7 @@ declare class JobManager {
816
894
  private _forgetKey;
817
895
  private _librarianKey;
818
896
  private _healKey;
897
+ private _ontologyBackfillKey;
819
898
  /**
820
899
  * Lookup table for acquireLock/releaseLock's dynamic-dispatch branch.
821
900
  * Excludes 'ingest' | 'global_reembed' | 'global_import', which those
@@ -886,9 +965,10 @@ interface NeighborhoodQueryOptions {
886
965
  declare class EdgeRepository extends BaseRepository {
887
966
  /**
888
967
  * Insert an edge, silently skipping on primary-key or uniqueness conflicts.
968
+ * Returns true when a row was inserted, false when skipped as a duplicate.
889
969
  * Throws when the insert was skipped due to an id collision with a different edge tuple.
890
970
  */
891
- addIgnoreDuplicate(edge: WikiEdge, tx?: SQLiteAdapter): Promise<void>;
971
+ addIgnoreDuplicate(edge: WikiEdge, tx?: SQLiteAdapter): Promise<boolean>;
892
972
  getByEntityId(entityId: string, tx?: SQLiteAdapter): Promise<WikiEdge[]>;
893
973
  /** Hard delete — edges have no soft-delete concept, only presence/absence. `tx` is REQUIRED. */
894
974
  bulkDeleteByEntityId(entityId: string, tx: SQLiteAdapter): Promise<void>;
@@ -930,7 +1010,7 @@ declare class OntologyService {
930
1010
  okf_type: string | null;
931
1011
  edges: ExtractedFactEdge[];
932
1012
  };
933
- resolveAndPersistEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, tx: SQLiteAdapter, now: number): Promise<void>;
1013
+ resolveAndPersistEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, tx: SQLiteAdapter, now: number): Promise<number>;
934
1014
  }
935
1015
 
936
1016
  declare class PromptService {
@@ -952,6 +1032,10 @@ declare class PromptService {
952
1032
  systemPrompt: string;
953
1033
  userPrompt: string;
954
1034
  };
1035
+ buildOntologyBackfillPrompt(facts: unknown[], runtimeOverride?: string, ontologyContext?: OntologyPromptContext | null): {
1036
+ systemPrompt: string;
1037
+ userPrompt: string;
1038
+ };
955
1039
  }
956
1040
 
957
1041
  declare class IngestionService {
@@ -1072,6 +1156,9 @@ declare class EventRepository extends BaseRepository {
1072
1156
  getByEntityId(entityId: string, limit?: number): Promise<WikiEvent[]>;
1073
1157
  }
1074
1158
 
1159
+ declare const ONTOLOGY_BACKFILL_BATCH_SIZE = 25;
1160
+ declare const ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS = 40000;
1161
+ declare const ONTOLOGY_BACKFILL_RECHECK_MS: number;
1075
1162
  declare class MaintenanceService {
1076
1163
  private db;
1077
1164
  private prefix;
@@ -1101,6 +1188,10 @@ declare class MaintenanceService {
1101
1188
  runHeal(entityId: string, options?: {
1102
1189
  promptOverride?: string;
1103
1190
  }): Promise<void>;
1191
+ runOntologyBackfill(entityId: string, options?: {
1192
+ promptOverride?: string;
1193
+ batchSize?: number;
1194
+ }): Promise<OntologyBackfillResult>;
1104
1195
  runReembed(entityId?: string, opts?: {
1105
1196
  force?: boolean;
1106
1197
  skipExisting?: boolean;
@@ -1125,6 +1216,11 @@ declare class MaintenanceService {
1125
1216
  doRunLibrarian(entityId: string, promptOverride?: string): Promise<void>;
1126
1217
  /** Core heal pass (locks handled by {@link runHeal}). Package-internal orchestration hook. */
1127
1218
  doRunHeal(entityId: string, promptOverride?: string): Promise<void>;
1219
+ /** Core ontology backfill pass (locks handled by {@link runOntologyBackfill}). Package-internal orchestration hook. */
1220
+ doRunOntologyBackfill(entityId: string, options?: {
1221
+ promptOverride?: string;
1222
+ batchSize?: number;
1223
+ }): Promise<OntologyBackfillResult>;
1128
1224
  private _validatePruneDuration;
1129
1225
  private _sanitizeRankerError;
1130
1226
  }
@@ -1291,6 +1387,25 @@ declare class WikiMemory {
1291
1387
  runHeal(entityId: string, options?: {
1292
1388
  promptOverride?: string;
1293
1389
  }): Promise<void>;
1390
+ /**
1391
+ * Types already-persisted untyped facts (okf_type IS NULL) in place via one
1392
+ * librarian-style LLM call. Strictly additive: never creates, deletes, or
1393
+ * rewrites facts; never overwrites an existing okf_type. Free when there is
1394
+ * nothing to do (ontology off, or zero eligible untyped facts).
1395
+ *
1396
+ * Hosts own the trigger cadence (e.g. after each sync); loop `while
1397
+ * result.remaining > 0` for convergence. Throws WikiBusyError when another
1398
+ * backfill (or conflicting maintenance op) is running for the entity.
1399
+ *
1400
+ * @param options.promptOverride - Applies only to this call. For persistent
1401
+ * customization set `options.config.prompts.ontologyBackfillSystemPrompt`.
1402
+ * @param options.batchSize - Facts per run (default 25) for providers with
1403
+ * tighter context limits.
1404
+ */
1405
+ runOntologyBackfill(entityId: string, options?: {
1406
+ promptOverride?: string;
1407
+ batchSize?: number;
1408
+ }): Promise<OntologyBackfillResult>;
1294
1409
  runReembed(entityId?: string, opts?: {
1295
1410
  force?: boolean;
1296
1411
  skipExisting?: boolean;
@@ -1365,4 +1480,4 @@ declare class WikiMemory {
1365
1480
  }): Promise<void>;
1366
1481
  }
1367
1482
 
1368
- export { type WikiMemoryTestAccess as A, type WikiOutboxEvent as B, type WikiTask as C, EmbeddingService as D, type EntityStatus as E, type FormatContextOptions as F, type GraphNeighborhood as G, HOOK_TIMEOUT_MARKER as H, ImportExportService as I, IngestionService as J, JobManager as K, type LLMProvider as L, type MemoryBundle as M, MaintenanceService as N, type OntologyConfig as O, type PromptOverrides as P, RetrievalService as Q, type ReadOptions as R, type SQLiteAdapter as S, SearchService as T, WriteService as U, type VectorRanker as V, type WikiOptions as W, type MemoryDump as a, 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, type OntologyEdgeType as i, type OntologyManifest as j, type OntologyMode as k, type OntologyNodeType as l, type OntologyPromptContext as m, type OntologyUpdates as n, PromptService as o, PrunePartialFailureError as p, type VectorRankerFallback as q, type VectorRankerRankArgs as r, type VectorRankerSemanticResult as s, WikiBusyError as t, type WikiBusyOperation as u, type WikiCheckpoint as v, type WikiConfig as w, type WikiEdge as x, type WikiEvent as y, type WikiFact as z };
1483
+ export { WriteService as $, type WikiConfig as A, type WikiEdge as B, type WikiEvent as C, type WikiFact as D, type EntityStatus as E, type FormatContextOptions as F, type GraphNeighborhood as G, HOOK_TIMEOUT_MARKER as H, type WikiMemoryTestAccess as I, type WikiOutboxEvent as J, type WikiTask as K, type LLMProvider as L, type MemoryBundle as M, WikiTransactionError as N, ONTOLOGY_BACKFILL_BATCH_SIZE as O, type PromptOverrides as P, EmbeddingService as Q, type ReadOptions as R, type SQLiteAdapter as S, ImportExportService as T, IngestionService as U, type VectorRanker as V, type WikiOptions as W, JobManager as X, MaintenanceService as Y, RetrievalService as Z, SearchService as _, type MemoryDump as a, 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, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as i, ONTOLOGY_BACKFILL_RECHECK_MS as j, type OntologyBackfillResult as k, type OntologyConfig as l, type OntologyEdgeType as m, type OntologyManifest as n, type OntologyMode as o, type OntologyNodeType as p, type OntologyPromptContext as q, type OntologyUpdates as r, PromptService as s, PrunePartialFailureError as t, type VectorRankerFallback as u, type VectorRankerRankArgs as v, type VectorRankerSemanticResult as w, WikiBusyError as x, type WikiBusyOperation as y, type WikiCheckpoint as z };
@@ -13,6 +13,15 @@ interface SQLiteAdapter {
13
13
  }>;
14
14
  getAllAsync<T>(sql: string, params?: unknown[]): Promise<T[]>;
15
15
  getFirstAsync<T>(sql: string, params?: unknown[]): Promise<T | null>;
16
+ /**
17
+ * Runs `fn` inside a transaction. A bare adapter implementation is not required to
18
+ * serialize concurrent calls itself — `WikiMemory` wraps every adapter it's given in
19
+ * `withSerializedTransactions` (see `db/serializedAdapter.ts`), which is what makes
20
+ * transactions serialize in practice. Once wrapped, inside the callback use ONLY the
21
+ * provided `tx` handle — never the outer database handle (captured via closure or
22
+ * `this.db`). Calling the outer handle deadlocks against the transaction mutex;
23
+ * calling `tx.withTransactionAsync` throws (nested transactions are unsupported).
24
+ */
16
25
  withTransactionAsync<T>(fn: (tx: SQLiteAdapter) => Promise<T>): Promise<T>;
17
26
  closeAsync(): Promise<void>;
18
27
  }
@@ -20,6 +29,7 @@ interface PromptOverrides {
20
29
  ingestSystemPrompt?: string;
21
30
  librarianSystemPrompt?: string;
22
31
  healSystemPrompt?: string;
32
+ ontologyBackfillSystemPrompt?: string;
23
33
  }
24
34
  type OntologyMode = 'strict' | 'emergent' | 'off';
25
35
  interface OntologyNodeType {
@@ -69,6 +79,24 @@ interface OntologyPromptContext {
69
79
  ontologyManifest: string;
70
80
  ontologyModeInstructions: string;
71
81
  }
82
+ /** Result of a single ontology backfill run. Omissions (facts the model declined
83
+ * to classify) are derivable as scanned − typed − failedValidation. */
84
+ interface OntologyBackfillResult {
85
+ /** Untyped facts sent to the model this run. */
86
+ scanned: number;
87
+ /** Facts that received an okf_type. */
88
+ typed: number;
89
+ /** Model classifications rejected (unknown/duplicate id, non-manifest type). */
90
+ failedValidation: number;
91
+ /** Edges persisted. */
92
+ edgesAdded: number;
93
+ /** Untyped facts still eligible after this run — safe host convergence signal: loop while > 0.
94
+ * Always 0 when ontology mode is 'off' (nothing is eligible for typing while
95
+ * disabled), so 0 does not imply queue exhaustion in that mode. */
96
+ remaining: number;
97
+ /** Untyped facts inside the recheck cooldown. */
98
+ deferred: number;
99
+ }
72
100
  interface WikiConfig {
73
101
  /**
74
102
  * Prefix applied to every SQL table/index/trigger name. Must match
@@ -456,10 +484,11 @@ interface EntityStatus {
456
484
  *
457
485
  * @remarks **Breaking change from v2.x** — the union previously only contained
458
486
  * `'ingest' | 'librarian' | 'heal' | 'prune' | 'reembed'`. The values `'import'`
459
- * and `'forget'` were added in v3.0. Exhaustive `switch` / narrowing on this type
460
- * must be updated (or given a `default` arm) to compile without errors.
487
+ * and `'forget'` were added in v3.0. `'ontologyBackfill'` was added afterward.
488
+ * Exhaustive `switch` / narrowing on this type must be updated (or given a
489
+ * `default` arm) to compile without errors.
461
490
  */
462
- type WikiBusyOperation = 'ingest' | 'librarian' | 'heal' | 'prune' | 'reembed' | 'import' | 'forget';
491
+ type WikiBusyOperation = 'ingest' | 'librarian' | 'heal' | 'prune' | 'reembed' | 'import' | 'forget' | 'ontologyBackfill';
463
492
  /**
464
493
  * Thrown when a background mutator is already running for the requested entity.
465
494
  */
@@ -468,6 +497,19 @@ declare class WikiBusyError extends Error {
468
497
  readonly entityId: string;
469
498
  constructor(operation: WikiBusyOperation, entityId: string);
470
499
  }
500
+ /**
501
+ * Thrown by the serialized transaction wrapper when a SQLite driver error
502
+ * escapes a transaction callback (nested BEGIN, SQLITE_BUSY, constraint
503
+ * violation). Domain errors thrown from callback logic pass through unwrapped.
504
+ * Stable `instanceof` target for observability, mirroring {@link WikiBusyError}.
505
+ */
506
+ declare class WikiTransactionError extends Error {
507
+ /** Best-effort SQLite code lifted from the driver error, e.g. 'SQLITE_BUSY'. */
508
+ readonly sqliteErrorCode?: string;
509
+ constructor(message: string, options: {
510
+ cause: unknown;
511
+ });
512
+ }
471
513
  declare class PrunePartialFailureError extends Error {
472
514
  readonly deleted: number;
473
515
  readonly failedAt: string;
@@ -695,6 +737,42 @@ declare class EntryRepository extends BaseRepository {
695
737
  trackAccess(ids: readonly string[], now: number, tx?: SQLiteAdapter): Promise<void>;
696
738
  getLegacyMigrationSQL(): string;
697
739
  findRecentByEntityIds(entityIds: readonly string[], limit: number, tx?: SQLiteAdapter): Promise<WikiFact[]>;
740
+ /**
741
+ * Live untyped facts eligible for ontology backfill, oldest first.
742
+ * Skips facts checked within the recheck cooldown (ontology_checked_at > recheckCutoff).
743
+ */
744
+ findUntypedByEntityId(entityId: string, limit: number, recheckCutoff: number, tx?: SQLiteAdapter): Promise<WikiFact[]>;
745
+ /** Counts live untyped facts: eligible (past cooldown) vs deferred (in cooldown). */
746
+ countUntypedByEntityId(entityId: string, recheckCutoff: number, tx?: SQLiteAdapter): Promise<{
747
+ eligible: number;
748
+ deferred: number;
749
+ }>;
750
+ /**
751
+ * Sets okf_type only when currently NULL — additive by construction: a
752
+ * concurrently-typed fact is never overwritten. Returns changes for the caller
753
+ * to distinguish applied vs skipped.
754
+ */
755
+ updateOkfType(id: string, entityId: string, okfType: string, tx: SQLiteAdapter): Promise<{
756
+ changes: number;
757
+ }>;
758
+ /**
759
+ * Stamps the backfill recheck cooldown. NEVER touches updated_at — import
760
+ * merge resolution is last-write-wins on updated_at and a bump here would
761
+ * make an unchanged local fact beat a genuinely newer remote edit.
762
+ */
763
+ markOntologyChecked(ids: string[], entityId: string, now: number, tx: SQLiteAdapter): Promise<void>;
764
+ /**
765
+ * Lightweight full-breadth title index (id, title, okf_type) over all live
766
+ * typed facts. Used by ontology backfill edge resolution — a recent-N window
767
+ * would miss an old fact's contemporaries. Untyped rows are excluded: they
768
+ * can never pass the edge target-type check, and batch facts typed during
769
+ * the run are re-added to the in-memory index by the caller.
770
+ */
771
+ findTitleIndexByEntityId(entityId: string, tx?: SQLiteAdapter): Promise<Array<{
772
+ id: string;
773
+ title: string;
774
+ okf_type: string | null;
775
+ }>>;
698
776
  }
699
777
 
700
778
  declare class MetadataRepository extends BaseRepository {
@@ -801,7 +879,7 @@ declare class SearchService {
801
879
  private _compareScoredRows;
802
880
  }
803
881
 
804
- type OperationType = 'prune' | 'librarian' | 'heal' | 'ingest' | 'reembed' | 'global_reembed' | 'import' | 'global_import' | 'forget';
882
+ type OperationType = 'prune' | 'librarian' | 'heal' | 'ingest' | 'reembed' | 'global_reembed' | 'import' | 'global_import' | 'forget' | 'ontologyBackfill';
805
883
  declare class JobManager {
806
884
  private prefix;
807
885
  private activeMaintenanceJobs;
@@ -816,6 +894,7 @@ declare class JobManager {
816
894
  private _forgetKey;
817
895
  private _librarianKey;
818
896
  private _healKey;
897
+ private _ontologyBackfillKey;
819
898
  /**
820
899
  * Lookup table for acquireLock/releaseLock's dynamic-dispatch branch.
821
900
  * Excludes 'ingest' | 'global_reembed' | 'global_import', which those
@@ -886,9 +965,10 @@ interface NeighborhoodQueryOptions {
886
965
  declare class EdgeRepository extends BaseRepository {
887
966
  /**
888
967
  * Insert an edge, silently skipping on primary-key or uniqueness conflicts.
968
+ * Returns true when a row was inserted, false when skipped as a duplicate.
889
969
  * Throws when the insert was skipped due to an id collision with a different edge tuple.
890
970
  */
891
- addIgnoreDuplicate(edge: WikiEdge, tx?: SQLiteAdapter): Promise<void>;
971
+ addIgnoreDuplicate(edge: WikiEdge, tx?: SQLiteAdapter): Promise<boolean>;
892
972
  getByEntityId(entityId: string, tx?: SQLiteAdapter): Promise<WikiEdge[]>;
893
973
  /** Hard delete — edges have no soft-delete concept, only presence/absence. `tx` is REQUIRED. */
894
974
  bulkDeleteByEntityId(entityId: string, tx: SQLiteAdapter): Promise<void>;
@@ -930,7 +1010,7 @@ declare class OntologyService {
930
1010
  okf_type: string | null;
931
1011
  edges: ExtractedFactEdge[];
932
1012
  };
933
- resolveAndPersistEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, tx: SQLiteAdapter, now: number): Promise<void>;
1013
+ resolveAndPersistEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, tx: SQLiteAdapter, now: number): Promise<number>;
934
1014
  }
935
1015
 
936
1016
  declare class PromptService {
@@ -952,6 +1032,10 @@ declare class PromptService {
952
1032
  systemPrompt: string;
953
1033
  userPrompt: string;
954
1034
  };
1035
+ buildOntologyBackfillPrompt(facts: unknown[], runtimeOverride?: string, ontologyContext?: OntologyPromptContext | null): {
1036
+ systemPrompt: string;
1037
+ userPrompt: string;
1038
+ };
955
1039
  }
956
1040
 
957
1041
  declare class IngestionService {
@@ -1072,6 +1156,9 @@ declare class EventRepository extends BaseRepository {
1072
1156
  getByEntityId(entityId: string, limit?: number): Promise<WikiEvent[]>;
1073
1157
  }
1074
1158
 
1159
+ declare const ONTOLOGY_BACKFILL_BATCH_SIZE = 25;
1160
+ declare const ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS = 40000;
1161
+ declare const ONTOLOGY_BACKFILL_RECHECK_MS: number;
1075
1162
  declare class MaintenanceService {
1076
1163
  private db;
1077
1164
  private prefix;
@@ -1101,6 +1188,10 @@ declare class MaintenanceService {
1101
1188
  runHeal(entityId: string, options?: {
1102
1189
  promptOverride?: string;
1103
1190
  }): Promise<void>;
1191
+ runOntologyBackfill(entityId: string, options?: {
1192
+ promptOverride?: string;
1193
+ batchSize?: number;
1194
+ }): Promise<OntologyBackfillResult>;
1104
1195
  runReembed(entityId?: string, opts?: {
1105
1196
  force?: boolean;
1106
1197
  skipExisting?: boolean;
@@ -1125,6 +1216,11 @@ declare class MaintenanceService {
1125
1216
  doRunLibrarian(entityId: string, promptOverride?: string): Promise<void>;
1126
1217
  /** Core heal pass (locks handled by {@link runHeal}). Package-internal orchestration hook. */
1127
1218
  doRunHeal(entityId: string, promptOverride?: string): Promise<void>;
1219
+ /** Core ontology backfill pass (locks handled by {@link runOntologyBackfill}). Package-internal orchestration hook. */
1220
+ doRunOntologyBackfill(entityId: string, options?: {
1221
+ promptOverride?: string;
1222
+ batchSize?: number;
1223
+ }): Promise<OntologyBackfillResult>;
1128
1224
  private _validatePruneDuration;
1129
1225
  private _sanitizeRankerError;
1130
1226
  }
@@ -1291,6 +1387,25 @@ declare class WikiMemory {
1291
1387
  runHeal(entityId: string, options?: {
1292
1388
  promptOverride?: string;
1293
1389
  }): Promise<void>;
1390
+ /**
1391
+ * Types already-persisted untyped facts (okf_type IS NULL) in place via one
1392
+ * librarian-style LLM call. Strictly additive: never creates, deletes, or
1393
+ * rewrites facts; never overwrites an existing okf_type. Free when there is
1394
+ * nothing to do (ontology off, or zero eligible untyped facts).
1395
+ *
1396
+ * Hosts own the trigger cadence (e.g. after each sync); loop `while
1397
+ * result.remaining > 0` for convergence. Throws WikiBusyError when another
1398
+ * backfill (or conflicting maintenance op) is running for the entity.
1399
+ *
1400
+ * @param options.promptOverride - Applies only to this call. For persistent
1401
+ * customization set `options.config.prompts.ontologyBackfillSystemPrompt`.
1402
+ * @param options.batchSize - Facts per run (default 25) for providers with
1403
+ * tighter context limits.
1404
+ */
1405
+ runOntologyBackfill(entityId: string, options?: {
1406
+ promptOverride?: string;
1407
+ batchSize?: number;
1408
+ }): Promise<OntologyBackfillResult>;
1294
1409
  runReembed(entityId?: string, opts?: {
1295
1410
  force?: boolean;
1296
1411
  skipExisting?: boolean;
@@ -1365,4 +1480,4 @@ declare class WikiMemory {
1365
1480
  }): Promise<void>;
1366
1481
  }
1367
1482
 
1368
- export { type WikiMemoryTestAccess as A, type WikiOutboxEvent as B, type WikiTask as C, EmbeddingService as D, type EntityStatus as E, type FormatContextOptions as F, type GraphNeighborhood as G, HOOK_TIMEOUT_MARKER as H, ImportExportService as I, IngestionService as J, JobManager as K, type LLMProvider as L, type MemoryBundle as M, MaintenanceService as N, type OntologyConfig as O, type PromptOverrides as P, RetrievalService as Q, type ReadOptions as R, type SQLiteAdapter as S, SearchService as T, WriteService as U, type VectorRanker as V, type WikiOptions as W, type MemoryDump as a, 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, type OntologyEdgeType as i, type OntologyManifest as j, type OntologyMode as k, type OntologyNodeType as l, type OntologyPromptContext as m, type OntologyUpdates as n, PromptService as o, PrunePartialFailureError as p, type VectorRankerFallback as q, type VectorRankerRankArgs as r, type VectorRankerSemanticResult as s, WikiBusyError as t, type WikiBusyOperation as u, type WikiCheckpoint as v, type WikiConfig as w, type WikiEdge as x, type WikiEvent as y, type WikiFact as z };
1483
+ export { WriteService as $, type WikiConfig as A, type WikiEdge as B, type WikiEvent as C, type WikiFact as D, type EntityStatus as E, type FormatContextOptions as F, type GraphNeighborhood as G, HOOK_TIMEOUT_MARKER as H, type WikiMemoryTestAccess as I, type WikiOutboxEvent as J, type WikiTask as K, type LLMProvider as L, type MemoryBundle as M, WikiTransactionError as N, ONTOLOGY_BACKFILL_BATCH_SIZE as O, type PromptOverrides as P, EmbeddingService as Q, type ReadOptions as R, type SQLiteAdapter as S, ImportExportService as T, IngestionService as U, type VectorRanker as V, type WikiOptions as W, JobManager as X, MaintenanceService as Y, RetrievalService as Z, SearchService as _, type MemoryDump as a, 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, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as i, ONTOLOGY_BACKFILL_RECHECK_MS as j, type OntologyBackfillResult as k, type OntologyConfig as l, type OntologyEdgeType as m, type OntologyManifest as n, type OntologyMode as o, type OntologyNodeType as p, type OntologyPromptContext as q, type OntologyUpdates as r, PromptService as s, PrunePartialFailureError as t, type VectorRankerFallback as u, type VectorRankerRankArgs as v, type VectorRankerSemanticResult as w, WikiBusyError as x, type WikiBusyOperation as y, type WikiCheckpoint as z };
@@ -1,2 +1,2 @@
1
- export { D as EmbeddingService, I as ImportExportService, J as IngestionService, K as JobManager, K as JobManagerType, N as MaintenanceService, Q as RetrievalService, T as SearchService, T as SearchServiceType, A as WikiMemoryTestAccess, U as WriteService } from './testing-DP3lus54.mjs';
1
+ export { Q as EmbeddingService, T as ImportExportService, U as IngestionService, X as JobManager, X as JobManagerType, Y as MaintenanceService, Z as RetrievalService, _ as SearchService, _ as SearchServiceType, I as WikiMemoryTestAccess, $ as WriteService } from './testing-i91HR1TG.mjs';
2
2
  import 'minisearch';
package/dist/testing.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { D as EmbeddingService, I as ImportExportService, J as IngestionService, K as JobManager, K as JobManagerType, N as MaintenanceService, Q as RetrievalService, T as SearchService, T as SearchServiceType, A as WikiMemoryTestAccess, U as WriteService } from './testing-DP3lus54.js';
1
+ export { Q as EmbeddingService, T as ImportExportService, U as IngestionService, X as JobManager, X as JobManagerType, Y as MaintenanceService, Z as RetrievalService, _ as SearchService, _ as SearchServiceType, I as WikiMemoryTestAccess, $ as WriteService } from './testing-i91HR1TG.js';
2
2
  import 'minisearch';