@equationalapplications/core-llm-wiki 7.2.1 → 7.4.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.
@@ -269,6 +269,11 @@ interface WikiConfig {
269
269
  traversalDirection?: 'inbound' | 'outbound' | 'both';
270
270
  /** Default source_type dead-end list for discovered traversal nodes. Default []. */
271
271
  excludeSourceTypes?: Array<WikiFact['source_type']>;
272
+ /**
273
+ * Engine default for `ReadOptions.excludeDrafts` and
274
+ * `GraphTraversalOptions.excludeDrafts`. Default false (drafts visible).
275
+ */
276
+ excludeDrafts?: boolean;
272
277
  }
273
278
  interface ReadOptions {
274
279
  maxResults?: number;
@@ -309,6 +314,12 @@ interface ReadOptions {
309
314
  * by `tierWeights: 0`, or a floor keyed to an entity not in `entityId`.
310
315
  */
311
316
  tierFloors?: Record<string, number>;
317
+ /**
318
+ * When true, facts whose `lifecycle_status` is `'draft'` are excluded before
319
+ * `maxResults`, `tierFloors` and every other cut, on every read path.
320
+ * Resolves call → `WikiConfig.excludeDrafts` → false.
321
+ */
322
+ excludeDrafts?: boolean;
312
323
  }
313
324
  interface WikiFact {
314
325
  id: string;
@@ -451,6 +462,12 @@ interface GraphTraversalOptions {
451
462
  minTraversalConfidence?: 'certain' | 'inferred' | 'tentative';
452
463
  /** source_type values to dead-end on for *discovered* nodes. Does not gate the anchor. Default []. */
453
464
  excludeSourceTypes?: Array<WikiFact['source_type']>;
465
+ /**
466
+ * When true, draft facts are dead ends for *discovered* nodes (not
467
+ * discovered, not traversed through). Does not gate the anchor.
468
+ * Resolves call → `WikiConfig.excludeDrafts` → false.
469
+ */
470
+ excludeDrafts?: boolean;
454
471
  }
455
472
  interface GraphNeighborhood {
456
473
  /** Anchor node first, then discovered neighbors ordered by depth ASC, then updated_at DESC. */
@@ -569,6 +586,48 @@ interface VectorRanker {
569
586
  * Fallback policy when rankBySimilarity rejects.
570
587
  */
571
588
  type VectorRankerFallback = 'js-cosine' | 'keyword' | 'empty' | 'throw';
589
+ /** Severity is fixed per code (spec §4.2.6). */
590
+ type WikiDiagnosticSeverity = 'info' | 'warn' | 'error';
591
+ /**
592
+ * Closed for this release series, but new codes are added in minor versions:
593
+ * hosts must tolerate codes they do not recognize.
594
+ */
595
+ type WikiDiagnosticCode = 'ingest_chunk_failed' | 'fact_rejected' | 'task_rejected' | 'fact_deduplicated' | 'edge_dropped' | 'embedding_failed' | 'hook_failed' | 'background_job_failed' | 'heal_skipped' | 'grounding_missing' | 'grounding_failed' | 'classification_low_confidence' | 'classification_invalid';
596
+ /** The service run that emitted the diagnostic. */
597
+ type WikiDiagnosticOperation = 'ingest' | 'upsertGraph' | 'librarian' | 'heal' | 'ontologyBackfill' | 'reembed' | 'importDump' | 'write';
598
+ /** `'auto'` when a write threshold started the run (auto-librarian / auto-heal); `'call'` when the host did. */
599
+ type WikiDiagnosticTrigger = 'call' | 'auto';
600
+ /** Identifiers only — never titles, bodies, quotes, LLM output, provider messages, or hashes of content. */
601
+ interface WikiDiagnosticDetail {
602
+ factId?: string;
603
+ sourceRef?: string;
604
+ chunkIndex?: number;
605
+ edgeType?: string;
606
+ /** Manifest slug of the edge source, when resolved. */
607
+ sourceNodeType?: string;
608
+ /** Manifest slug of the edge target, when resolved. */
609
+ targetNodeType?: string;
610
+ /** Position of a rejected item in the LLM response array. */
611
+ itemIndex?: number;
612
+ /** Machine-readable sub-reason, e.g. `'target_not_found'`. */
613
+ reason?: string;
614
+ /** Aggregated `ingest_chunk_failed` only; the first 20 failed chunk indexes. */
615
+ chunkIndexes?: number[];
616
+ /** Aggregated emissions only. */
617
+ count?: number;
618
+ }
619
+ interface WikiDiagnostic {
620
+ code: WikiDiagnosticCode;
621
+ severity: WikiDiagnosticSeverity;
622
+ operation: WikiDiagnosticOperation;
623
+ trigger: WikiDiagnosticTrigger;
624
+ entityId: string;
625
+ /** Epoch ms, sampled at emission. */
626
+ at: number;
627
+ /** Fixed template per code. Never contains content. */
628
+ message: string;
629
+ detail?: WikiDiagnosticDetail;
630
+ }
572
631
  interface WikiOptions {
573
632
  config?: WikiConfig;
574
633
  llmProvider: LLMProvider;
@@ -634,6 +693,14 @@ interface WikiOptions {
634
693
  * NOT GDPR-safe for live indexes. Default false.
635
694
  */
636
695
  forceDeleteIgnoreRankerHook?: boolean;
696
+ /**
697
+ * Receives typed, content-free diagnostics for events core previously
698
+ * dropped silently or only logged (spec §4). Synchronous; a returned promise
699
+ * is ignored. A throwing or rejecting hook never affects the operation that
700
+ * emitted the diagnostic. Existing console output is unchanged whether or
701
+ * not this is set.
702
+ */
703
+ onDiagnostic?: (diagnostic: WikiDiagnostic) => void;
637
704
  }
638
705
  interface MemoryBundle {
639
706
  facts: WikiFact[];
@@ -892,6 +959,22 @@ declare class WikiInvalidReadOptions extends Error {
892
959
  readonly reason: string;
893
960
  constructor(field: string, reason: string);
894
961
  }
962
+ /**
963
+ * Thrown by `promoteDraft` when no live draft with that id exists for the
964
+ * entity: missing, soft-deleted, owned by another entity, or not a draft.
965
+ * Contextless for the same reason as {@link WikiGraphNodeOwnershipConflict}.
966
+ * The `WIKI_` code keeps it clear of `extractSqliteCode`, so it passes through
967
+ * the serialized transaction wrapper unwrapped.
968
+ */
969
+ declare class WikiDraftNotFound extends Error {
970
+ readonly code: "WIKI_DRAFT_NOT_FOUND";
971
+ constructor();
972
+ }
973
+ /** One page of `listDrafts`. `nextCursor` is opaque; pass it back unchanged. */
974
+ interface DraftPage {
975
+ facts: WikiFact[];
976
+ nextCursor: string | null;
977
+ }
895
978
 
896
979
  interface WikiOutboxEvent<T = unknown> {
897
980
  id: string;
@@ -1267,7 +1350,18 @@ declare class EntryRepository extends BaseRepository {
1267
1350
  }>>;
1268
1351
  trackAccess(ids: readonly string[], now: number, tx?: SQLiteAdapter): Promise<void>;
1269
1352
  getLegacyMigrationSQL(): string;
1270
- findRecentByEntityIds(entityIds: readonly string[], limit: number, tx?: SQLiteAdapter): Promise<WikiFact[]>;
1353
+ findRecentByEntityIds(entityIds: readonly string[], limit: number, tx?: SQLiteAdapter, opts?: {
1354
+ excludeDrafts?: boolean;
1355
+ }): Promise<WikiFact[]>;
1356
+ /** IDs of live draft facts for the given entities (spec §5.1). Callers pass ≤ 100 entity ids. */
1357
+ findDraftIdsByEntityIds(entityIds: readonly string[], tx?: SQLiteAdapter): Promise<Set<string>>;
1358
+ /** Live drafts for one entity, newest first, keyset-paged by (created_at, id). */
1359
+ listDraftsByEntityId(entityId: string, limit: number, after: {
1360
+ createdAt: number;
1361
+ id: string;
1362
+ } | null, tx?: SQLiteAdapter): Promise<WikiFact[]>;
1363
+ /** True iff `entryId` is a live draft owned by `entityId`. */
1364
+ isLiveDraft(entryId: string, entityId: string, tx?: SQLiteAdapter): Promise<boolean>;
1271
1365
  /**
1272
1366
  * Live untyped facts eligible for ontology backfill, oldest first.
1273
1367
  * Skips facts checked within the recheck cooldown (ontology_checked_at > recheckCutoff).
@@ -1640,6 +1734,34 @@ declare class JobManager {
1640
1734
  private _notifyStatusSubscribers;
1641
1735
  }
1642
1736
 
1737
+ declare function validateManifest(manifest: OntologyManifest): void;
1738
+ type EdgeDropReason = 'no_source_type' | 'invalid_shape' | 'type_not_in_manifest' | 'target_not_found' | 'target_type_mismatch';
1739
+ /** A dropped LLM-proposed edge. Slugs only — `target_title` is content and is never recorded. */
1740
+ interface EdgeDrop {
1741
+ reason: EdgeDropReason;
1742
+ /** Source fact id when known; null while validating before the fact has an id. */
1743
+ sourceId: string | null;
1744
+ edgeType: string | null;
1745
+ sourceNodeType: string | null;
1746
+ targetNodeType: string | null;
1747
+ }
1748
+
1749
+ /** What a call site supplies; severity, message and timestamp are derived from `code`. */
1750
+ type WikiDiagnosticInput = Omit<WikiDiagnostic, 'severity' | 'message' | 'at'>;
1751
+ type DiagnosticTarget = Pick<WikiOptions, 'onDiagnostic'>;
1752
+ /**
1753
+ * Operation-scoped buffer (spec §4.2.4): push while the operation's
1754
+ * transaction is open, `flush` after it commits, `discard` (or simply drop the
1755
+ * buffer) when the operation throws.
1756
+ */
1757
+ declare class DiagnosticBuffer {
1758
+ private items;
1759
+ push(input: WikiDiagnosticInput): void;
1760
+ get size(): number;
1761
+ flush(options: DiagnosticTarget): void;
1762
+ discard(): void;
1763
+ }
1764
+
1643
1765
  interface NeighborhoodQueryOptions {
1644
1766
  maxDepth: number;
1645
1767
  direction: 'inbound' | 'outbound' | 'both';
@@ -1647,6 +1769,14 @@ interface NeighborhoodQueryOptions {
1647
1769
  minConfidence: 'certain' | 'inferred' | 'tentative';
1648
1770
  excludeSourceTypes: string[];
1649
1771
  maxNodes: number;
1772
+ /**
1773
+ * When true, draft facts are dead-ends for *discovered* nodes — the recursive
1774
+ * walk will not step onto a draft, and will not step *past* one either, since
1775
+ * a draft's neighbours are unreachable through it. The anchor node is exempt
1776
+ * (it is validated up-front and never gated by this flag), so callers can
1777
+ * intentionally start from a draft. Default false (drafts visible).
1778
+ */
1779
+ excludeDrafts?: boolean;
1650
1780
  }
1651
1781
  declare class EdgeRepository extends BaseRepository {
1652
1782
  /**
@@ -1690,6 +1820,10 @@ type EmbedFactResult = {
1690
1820
  ok: false;
1691
1821
  kind: EmbedFailureKind;
1692
1822
  };
1823
+ interface EmbedDiagnosticContext {
1824
+ operation: WikiDiagnosticOperation;
1825
+ trigger: WikiDiagnosticTrigger;
1826
+ }
1693
1827
  declare class EmbeddingService {
1694
1828
  private db;
1695
1829
  private options;
@@ -1732,7 +1866,10 @@ declare class EmbeddingService {
1732
1866
  title: string;
1733
1867
  body: string;
1734
1868
  tags: string | string[];
1735
- }): Promise<EmbedFactResult>;
1869
+ }, ctx?: EmbedDiagnosticContext): Promise<EmbedFactResult>;
1870
+ private reportEmbed;
1871
+ /** For callers that invoke `notifyEmbeddingPersisted` directly and catch its failure. */
1872
+ reportHookFailed(ctx: EmbedDiagnosticContext, entityId: string, factId: string): void;
1736
1873
  /** Marker writes must never fail the caller. Only marker-eligible kinds reach here. */
1737
1874
  private markFailure;
1738
1875
  /**
@@ -1746,7 +1883,7 @@ declare class EmbeddingService {
1746
1883
  title: string;
1747
1884
  body: string;
1748
1885
  tags: string | string[];
1749
- }): Promise<boolean>;
1886
+ }, ctx?: EmbedDiagnosticContext): Promise<boolean>;
1750
1887
  notifyEmbeddingPersisted(entityId: string, factId: string, vector: Float32Array | null): Promise<void>;
1751
1888
  notifyEmbeddingPersistedOrThrow(entityId: string, factId: string, vector: Float32Array | null): Promise<void>;
1752
1889
  }
@@ -1776,6 +1913,7 @@ declare class OntologyService {
1776
1913
  validateAndNormalizeFact(fact: ExtractedFactWithOntology, manifest: OntologyManifest, opts?: {
1777
1914
  strict?: boolean;
1778
1915
  entityId?: string;
1916
+ drops?: EdgeDrop[];
1779
1917
  }): {
1780
1918
  okf_type: string | null;
1781
1919
  edges: ExtractedFactEdge[];
@@ -1787,7 +1925,7 @@ declare class OntologyService {
1787
1925
  * writes inside their own transaction (e.g. `IngestionService.ingestDocument`
1788
1926
  * when delegating to `upsertGraphCore`).
1789
1927
  */
1790
- resolveEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, now: number): WikiEdge[];
1928
+ resolveEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, now: number, drops?: EdgeDrop[]): WikiEdge[];
1791
1929
  /**
1792
1930
  * Backwards-compatible wrapper: resolves edges via {@link resolveEdges},
1793
1931
  * then persists each via `edgeRepo.addIgnoreDuplicate` in the supplied
@@ -1795,7 +1933,7 @@ declare class OntologyService {
1795
1933
  * want self-contained edge persistence (e.g. MaintenanceService heal /
1796
1934
  * backfill callers that do not have a separate edge-write step).
1797
1935
  */
1798
- resolveAndPersistEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, tx: SQLiteAdapter, now: number): Promise<number>;
1936
+ resolveAndPersistEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, tx: SQLiteAdapter, now: number, drops?: EdgeDrop[]): Promise<number>;
1799
1937
  }
1800
1938
 
1801
1939
  declare class PromptService {
@@ -1943,6 +2081,10 @@ declare class IngestionService {
1943
2081
  }[];
1944
2082
  }, tx: SQLiteAdapter, opts?: {
1945
2083
  strict?: boolean;
2084
+ diag?: {
2085
+ buffer: DiagnosticBuffer;
2086
+ operation: 'ingest' | 'upsertGraph';
2087
+ };
1946
2088
  }): Promise<{
1947
2089
  nodesWritten: number;
1948
2090
  edgesWritten: number;
@@ -2189,7 +2331,7 @@ declare class MaintenanceService {
2189
2331
  */
2190
2332
  private forgetDryRun;
2191
2333
  /** Core librarian pass (locks handled by {@link runLibrarian}). Package-internal orchestration hook. */
2192
- doRunLibrarian(entityId: string, promptOverride?: string): Promise<void>;
2334
+ doRunLibrarian(entityId: string, promptOverride?: string, trigger?: WikiDiagnosticTrigger): Promise<void>;
2193
2335
  /**
2194
2336
  * Core heal pass (locks handled by {@link runHeal}). Package-internal orchestration hook.
2195
2337
  *
@@ -2201,6 +2343,7 @@ declare class MaintenanceService {
2201
2343
  promptOverride?: string;
2202
2344
  batchSize?: number;
2203
2345
  bodyTruncationChars?: number;
2346
+ trigger?: WikiDiagnosticTrigger;
2204
2347
  }): Promise<HealResult>;
2205
2348
  /** Core ontology backfill pass (locks handled by {@link runOntologyBackfill}). Package-internal orchestration hook. */
2206
2349
  doRunOntologyBackfill(entityId: string, options?: {
@@ -2283,6 +2426,7 @@ declare class RetrievalService {
2283
2426
  * Stable tie-break sort: score desc → access_count desc → updated_at desc → id asc.
2284
2427
  */
2285
2428
  private _tieBreakSort;
2429
+ private _withoutDrafts;
2286
2430
  /**
2287
2431
  * Comparator for score + deterministic tie-break fields.
2288
2432
  * Negative return means "a ranks ahead of b" for descending score order.
@@ -2319,6 +2463,7 @@ declare class WriteService {
2319
2463
  * {@link write}) so a partial pass retries on the next write.
2320
2464
  */
2321
2465
  private maybeRunHeal;
2466
+ private reportBackgroundFailure;
2322
2467
  }
2323
2468
 
2324
2469
  /**
@@ -2695,6 +2840,25 @@ declare class WikiMemory {
2695
2840
  }>): Promise<void>;
2696
2841
  /** Set a fact's OKF v0.2 lifecycle status. Does NOT touch `updated_at`. */
2697
2842
  setLifecycleStatus(entryId: string, entityId: string, status: 'draft' | 'stable' | 'deprecated'): Promise<void>;
2843
+ /**
2844
+ * Live draft facts for one entity, newest first (spec §5.2). Default page
2845
+ * size 50, clamped to [1, 500]. Pass `nextCursor` back unchanged for the next page.
2846
+ */
2847
+ listDrafts(entityId: string, options?: {
2848
+ limit?: number;
2849
+ cursor?: string;
2850
+ }): Promise<DraftPage>;
2851
+ /**
2852
+ * Promote a draft to `stable` and record who reviewed it, atomically
2853
+ * (spec §5.2). Metadata writes only: `updated_at` is not bumped and no
2854
+ * outbox event is pushed. Pass `by: 'human:<id>'` so `trustTier` becomes
2855
+ * `'human-reviewed'`.
2856
+ *
2857
+ * @throws WikiDraftNotFound when no live draft with that id exists for the entity.
2858
+ */
2859
+ promoteDraft(entryId: string, entityId: string, reviewer: {
2860
+ by: string;
2861
+ }): Promise<void>;
2698
2862
  /** Set a fact's stale_after (epoch ms) or clear it. Does NOT touch `updated_at`. */
2699
2863
  setStaleAfter(entryId: string, entityId: string, date: number | null): Promise<void>;
2700
2864
  /** Set a fact's generated_by actor string. Does NOT touch `updated_at`. */
@@ -2713,4 +2877,4 @@ declare class WikiMemory {
2713
2877
  setGeneratedByTask(taskId: string, entityId: string, actor: string): Promise<void>;
2714
2878
  }
2715
2879
 
2716
- export { WikiGraphNodeOwnershipConflict as $, type ReembedResult as A, type VectorRankerFallback as B, type ChunkFailure as C, type DegradedRecord as D, type EmbedFactResult as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, type IngestDocumentResult as I, type VectorRankerRankArgs as J, type VectorRankerSemanticResult as K, type LLMProvider as L, type MemoryBundle as M, WikiBusyError as N, type OntologyManifest as O, type PromptOverrides as P, type WikiBusyOperation as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiCheckpoint as T, type WikiConfig as U, type VectorRanker as V, type WikiOptions as W, WikiDuplicateHashError as X, type WikiEdge as Y, type WikiEvent as Z, type WikiFact as _, type MemoryDump as a, WikiIngestEmptyError as a0, WikiInvalidReadOptions as a1, type WikiMemoryTestAccess as a2, type WikiOutboxEvent as a3, WikiParseError as a4, WikiSourceRefHashCollision as a5, WikiStrictOntologyViolation as a6, type WikiTask as a7, WikiTransactionError as a8, EmbeddingService as a9, ImportExportService as aa, IngestionService as ab, JobManager as ac, MaintenanceService as ad, RetrievalService as ae, SearchService as af, WriteService as ag, type FormattedMemoryDump as b, WikiMemory as c, type EmbedFailureKind as d, type EmbeddingMarkerKind as e, type EntityStatus as f, type ExtractedFact as g, type ExtractedFactEdge as h, type ExtractedFactWithOntology as i, type ExtractedTask as j, type GraphTraversalOptions as k, HEAL_RECHECK_MS as l, HOOK_TIMEOUT_MARKER as m, type HealResult as n, ONTOLOGY_BACKFILL_BATCH_SIZE as o, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as p, ONTOLOGY_BACKFILL_RECHECK_MS as q, type OntologyBackfillResult as r, type OntologyConfig as s, type OntologyEdgeType as t, type OntologyMode as u, type OntologyNodeType as v, type OntologyPromptContext as w, type OntologyUpdates as x, PromptService as y, PrunePartialFailureError as z };
2880
+ export { type WikiDiagnosticOperation as $, PrunePartialFailureError as A, type ReembedResult as B, type ChunkFailure as C, type DegradedRecord as D, type EmbedFactResult as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, type IngestDocumentResult as I, type VectorRankerFallback as J, type VectorRankerRankArgs as K, type LLMProvider as L, type MemoryBundle as M, type VectorRankerSemanticResult as N, ONTOLOGY_BACKFILL_BATCH_SIZE as O, type PromptOverrides as P, WikiBusyError as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiBusyOperation as T, type WikiCheckpoint as U, type VectorRanker as V, type WikiOptions as W, type WikiConfig as X, type WikiDiagnostic as Y, type WikiDiagnosticCode as Z, type WikiDiagnosticDetail as _, type MemoryDump as a, type WikiDiagnosticSeverity as a0, type WikiDiagnosticTrigger as a1, WikiDraftNotFound as a2, WikiDuplicateHashError as a3, type WikiEdge as a4, type WikiEvent as a5, type WikiFact as a6, WikiGraphNodeOwnershipConflict as a7, WikiIngestEmptyError as a8, WikiInvalidReadOptions as a9, type WikiMemoryTestAccess as aa, type WikiOutboxEvent as ab, WikiParseError as ac, WikiSourceRefHashCollision as ad, WikiStrictOntologyViolation as ae, type WikiTask as af, WikiTransactionError as ag, validateManifest as ah, EmbeddingService as ai, ImportExportService as aj, IngestionService as ak, JobManager as al, MaintenanceService as am, RetrievalService as an, SearchService as ao, WriteService as ap, type FormattedMemoryDump as b, WikiMemory as c, type DraftPage as d, type EmbedFailureKind as e, type EmbeddingMarkerKind as f, type EntityStatus as g, type ExtractedFact as h, type ExtractedFactEdge as i, type ExtractedFactWithOntology as j, type ExtractedTask as k, type GraphTraversalOptions as l, HEAL_RECHECK_MS as m, HOOK_TIMEOUT_MARKER as n, type HealResult as o, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as p, ONTOLOGY_BACKFILL_RECHECK_MS as q, type OntologyBackfillResult as r, type OntologyConfig as s, type OntologyEdgeType as t, type OntologyManifest as u, type OntologyMode as v, type OntologyNodeType as w, type OntologyPromptContext as x, type OntologyUpdates as y, PromptService as z };
@@ -269,6 +269,11 @@ interface WikiConfig {
269
269
  traversalDirection?: 'inbound' | 'outbound' | 'both';
270
270
  /** Default source_type dead-end list for discovered traversal nodes. Default []. */
271
271
  excludeSourceTypes?: Array<WikiFact['source_type']>;
272
+ /**
273
+ * Engine default for `ReadOptions.excludeDrafts` and
274
+ * `GraphTraversalOptions.excludeDrafts`. Default false (drafts visible).
275
+ */
276
+ excludeDrafts?: boolean;
272
277
  }
273
278
  interface ReadOptions {
274
279
  maxResults?: number;
@@ -309,6 +314,12 @@ interface ReadOptions {
309
314
  * by `tierWeights: 0`, or a floor keyed to an entity not in `entityId`.
310
315
  */
311
316
  tierFloors?: Record<string, number>;
317
+ /**
318
+ * When true, facts whose `lifecycle_status` is `'draft'` are excluded before
319
+ * `maxResults`, `tierFloors` and every other cut, on every read path.
320
+ * Resolves call → `WikiConfig.excludeDrafts` → false.
321
+ */
322
+ excludeDrafts?: boolean;
312
323
  }
313
324
  interface WikiFact {
314
325
  id: string;
@@ -451,6 +462,12 @@ interface GraphTraversalOptions {
451
462
  minTraversalConfidence?: 'certain' | 'inferred' | 'tentative';
452
463
  /** source_type values to dead-end on for *discovered* nodes. Does not gate the anchor. Default []. */
453
464
  excludeSourceTypes?: Array<WikiFact['source_type']>;
465
+ /**
466
+ * When true, draft facts are dead ends for *discovered* nodes (not
467
+ * discovered, not traversed through). Does not gate the anchor.
468
+ * Resolves call → `WikiConfig.excludeDrafts` → false.
469
+ */
470
+ excludeDrafts?: boolean;
454
471
  }
455
472
  interface GraphNeighborhood {
456
473
  /** Anchor node first, then discovered neighbors ordered by depth ASC, then updated_at DESC. */
@@ -569,6 +586,48 @@ interface VectorRanker {
569
586
  * Fallback policy when rankBySimilarity rejects.
570
587
  */
571
588
  type VectorRankerFallback = 'js-cosine' | 'keyword' | 'empty' | 'throw';
589
+ /** Severity is fixed per code (spec §4.2.6). */
590
+ type WikiDiagnosticSeverity = 'info' | 'warn' | 'error';
591
+ /**
592
+ * Closed for this release series, but new codes are added in minor versions:
593
+ * hosts must tolerate codes they do not recognize.
594
+ */
595
+ type WikiDiagnosticCode = 'ingest_chunk_failed' | 'fact_rejected' | 'task_rejected' | 'fact_deduplicated' | 'edge_dropped' | 'embedding_failed' | 'hook_failed' | 'background_job_failed' | 'heal_skipped' | 'grounding_missing' | 'grounding_failed' | 'classification_low_confidence' | 'classification_invalid';
596
+ /** The service run that emitted the diagnostic. */
597
+ type WikiDiagnosticOperation = 'ingest' | 'upsertGraph' | 'librarian' | 'heal' | 'ontologyBackfill' | 'reembed' | 'importDump' | 'write';
598
+ /** `'auto'` when a write threshold started the run (auto-librarian / auto-heal); `'call'` when the host did. */
599
+ type WikiDiagnosticTrigger = 'call' | 'auto';
600
+ /** Identifiers only — never titles, bodies, quotes, LLM output, provider messages, or hashes of content. */
601
+ interface WikiDiagnosticDetail {
602
+ factId?: string;
603
+ sourceRef?: string;
604
+ chunkIndex?: number;
605
+ edgeType?: string;
606
+ /** Manifest slug of the edge source, when resolved. */
607
+ sourceNodeType?: string;
608
+ /** Manifest slug of the edge target, when resolved. */
609
+ targetNodeType?: string;
610
+ /** Position of a rejected item in the LLM response array. */
611
+ itemIndex?: number;
612
+ /** Machine-readable sub-reason, e.g. `'target_not_found'`. */
613
+ reason?: string;
614
+ /** Aggregated `ingest_chunk_failed` only; the first 20 failed chunk indexes. */
615
+ chunkIndexes?: number[];
616
+ /** Aggregated emissions only. */
617
+ count?: number;
618
+ }
619
+ interface WikiDiagnostic {
620
+ code: WikiDiagnosticCode;
621
+ severity: WikiDiagnosticSeverity;
622
+ operation: WikiDiagnosticOperation;
623
+ trigger: WikiDiagnosticTrigger;
624
+ entityId: string;
625
+ /** Epoch ms, sampled at emission. */
626
+ at: number;
627
+ /** Fixed template per code. Never contains content. */
628
+ message: string;
629
+ detail?: WikiDiagnosticDetail;
630
+ }
572
631
  interface WikiOptions {
573
632
  config?: WikiConfig;
574
633
  llmProvider: LLMProvider;
@@ -634,6 +693,14 @@ interface WikiOptions {
634
693
  * NOT GDPR-safe for live indexes. Default false.
635
694
  */
636
695
  forceDeleteIgnoreRankerHook?: boolean;
696
+ /**
697
+ * Receives typed, content-free diagnostics for events core previously
698
+ * dropped silently or only logged (spec §4). Synchronous; a returned promise
699
+ * is ignored. A throwing or rejecting hook never affects the operation that
700
+ * emitted the diagnostic. Existing console output is unchanged whether or
701
+ * not this is set.
702
+ */
703
+ onDiagnostic?: (diagnostic: WikiDiagnostic) => void;
637
704
  }
638
705
  interface MemoryBundle {
639
706
  facts: WikiFact[];
@@ -892,6 +959,22 @@ declare class WikiInvalidReadOptions extends Error {
892
959
  readonly reason: string;
893
960
  constructor(field: string, reason: string);
894
961
  }
962
+ /**
963
+ * Thrown by `promoteDraft` when no live draft with that id exists for the
964
+ * entity: missing, soft-deleted, owned by another entity, or not a draft.
965
+ * Contextless for the same reason as {@link WikiGraphNodeOwnershipConflict}.
966
+ * The `WIKI_` code keeps it clear of `extractSqliteCode`, so it passes through
967
+ * the serialized transaction wrapper unwrapped.
968
+ */
969
+ declare class WikiDraftNotFound extends Error {
970
+ readonly code: "WIKI_DRAFT_NOT_FOUND";
971
+ constructor();
972
+ }
973
+ /** One page of `listDrafts`. `nextCursor` is opaque; pass it back unchanged. */
974
+ interface DraftPage {
975
+ facts: WikiFact[];
976
+ nextCursor: string | null;
977
+ }
895
978
 
896
979
  interface WikiOutboxEvent<T = unknown> {
897
980
  id: string;
@@ -1267,7 +1350,18 @@ declare class EntryRepository extends BaseRepository {
1267
1350
  }>>;
1268
1351
  trackAccess(ids: readonly string[], now: number, tx?: SQLiteAdapter): Promise<void>;
1269
1352
  getLegacyMigrationSQL(): string;
1270
- findRecentByEntityIds(entityIds: readonly string[], limit: number, tx?: SQLiteAdapter): Promise<WikiFact[]>;
1353
+ findRecentByEntityIds(entityIds: readonly string[], limit: number, tx?: SQLiteAdapter, opts?: {
1354
+ excludeDrafts?: boolean;
1355
+ }): Promise<WikiFact[]>;
1356
+ /** IDs of live draft facts for the given entities (spec §5.1). Callers pass ≤ 100 entity ids. */
1357
+ findDraftIdsByEntityIds(entityIds: readonly string[], tx?: SQLiteAdapter): Promise<Set<string>>;
1358
+ /** Live drafts for one entity, newest first, keyset-paged by (created_at, id). */
1359
+ listDraftsByEntityId(entityId: string, limit: number, after: {
1360
+ createdAt: number;
1361
+ id: string;
1362
+ } | null, tx?: SQLiteAdapter): Promise<WikiFact[]>;
1363
+ /** True iff `entryId` is a live draft owned by `entityId`. */
1364
+ isLiveDraft(entryId: string, entityId: string, tx?: SQLiteAdapter): Promise<boolean>;
1271
1365
  /**
1272
1366
  * Live untyped facts eligible for ontology backfill, oldest first.
1273
1367
  * Skips facts checked within the recheck cooldown (ontology_checked_at > recheckCutoff).
@@ -1640,6 +1734,34 @@ declare class JobManager {
1640
1734
  private _notifyStatusSubscribers;
1641
1735
  }
1642
1736
 
1737
+ declare function validateManifest(manifest: OntologyManifest): void;
1738
+ type EdgeDropReason = 'no_source_type' | 'invalid_shape' | 'type_not_in_manifest' | 'target_not_found' | 'target_type_mismatch';
1739
+ /** A dropped LLM-proposed edge. Slugs only — `target_title` is content and is never recorded. */
1740
+ interface EdgeDrop {
1741
+ reason: EdgeDropReason;
1742
+ /** Source fact id when known; null while validating before the fact has an id. */
1743
+ sourceId: string | null;
1744
+ edgeType: string | null;
1745
+ sourceNodeType: string | null;
1746
+ targetNodeType: string | null;
1747
+ }
1748
+
1749
+ /** What a call site supplies; severity, message and timestamp are derived from `code`. */
1750
+ type WikiDiagnosticInput = Omit<WikiDiagnostic, 'severity' | 'message' | 'at'>;
1751
+ type DiagnosticTarget = Pick<WikiOptions, 'onDiagnostic'>;
1752
+ /**
1753
+ * Operation-scoped buffer (spec §4.2.4): push while the operation's
1754
+ * transaction is open, `flush` after it commits, `discard` (or simply drop the
1755
+ * buffer) when the operation throws.
1756
+ */
1757
+ declare class DiagnosticBuffer {
1758
+ private items;
1759
+ push(input: WikiDiagnosticInput): void;
1760
+ get size(): number;
1761
+ flush(options: DiagnosticTarget): void;
1762
+ discard(): void;
1763
+ }
1764
+
1643
1765
  interface NeighborhoodQueryOptions {
1644
1766
  maxDepth: number;
1645
1767
  direction: 'inbound' | 'outbound' | 'both';
@@ -1647,6 +1769,14 @@ interface NeighborhoodQueryOptions {
1647
1769
  minConfidence: 'certain' | 'inferred' | 'tentative';
1648
1770
  excludeSourceTypes: string[];
1649
1771
  maxNodes: number;
1772
+ /**
1773
+ * When true, draft facts are dead-ends for *discovered* nodes — the recursive
1774
+ * walk will not step onto a draft, and will not step *past* one either, since
1775
+ * a draft's neighbours are unreachable through it. The anchor node is exempt
1776
+ * (it is validated up-front and never gated by this flag), so callers can
1777
+ * intentionally start from a draft. Default false (drafts visible).
1778
+ */
1779
+ excludeDrafts?: boolean;
1650
1780
  }
1651
1781
  declare class EdgeRepository extends BaseRepository {
1652
1782
  /**
@@ -1690,6 +1820,10 @@ type EmbedFactResult = {
1690
1820
  ok: false;
1691
1821
  kind: EmbedFailureKind;
1692
1822
  };
1823
+ interface EmbedDiagnosticContext {
1824
+ operation: WikiDiagnosticOperation;
1825
+ trigger: WikiDiagnosticTrigger;
1826
+ }
1693
1827
  declare class EmbeddingService {
1694
1828
  private db;
1695
1829
  private options;
@@ -1732,7 +1866,10 @@ declare class EmbeddingService {
1732
1866
  title: string;
1733
1867
  body: string;
1734
1868
  tags: string | string[];
1735
- }): Promise<EmbedFactResult>;
1869
+ }, ctx?: EmbedDiagnosticContext): Promise<EmbedFactResult>;
1870
+ private reportEmbed;
1871
+ /** For callers that invoke `notifyEmbeddingPersisted` directly and catch its failure. */
1872
+ reportHookFailed(ctx: EmbedDiagnosticContext, entityId: string, factId: string): void;
1736
1873
  /** Marker writes must never fail the caller. Only marker-eligible kinds reach here. */
1737
1874
  private markFailure;
1738
1875
  /**
@@ -1746,7 +1883,7 @@ declare class EmbeddingService {
1746
1883
  title: string;
1747
1884
  body: string;
1748
1885
  tags: string | string[];
1749
- }): Promise<boolean>;
1886
+ }, ctx?: EmbedDiagnosticContext): Promise<boolean>;
1750
1887
  notifyEmbeddingPersisted(entityId: string, factId: string, vector: Float32Array | null): Promise<void>;
1751
1888
  notifyEmbeddingPersistedOrThrow(entityId: string, factId: string, vector: Float32Array | null): Promise<void>;
1752
1889
  }
@@ -1776,6 +1913,7 @@ declare class OntologyService {
1776
1913
  validateAndNormalizeFact(fact: ExtractedFactWithOntology, manifest: OntologyManifest, opts?: {
1777
1914
  strict?: boolean;
1778
1915
  entityId?: string;
1916
+ drops?: EdgeDrop[];
1779
1917
  }): {
1780
1918
  okf_type: string | null;
1781
1919
  edges: ExtractedFactEdge[];
@@ -1787,7 +1925,7 @@ declare class OntologyService {
1787
1925
  * writes inside their own transaction (e.g. `IngestionService.ingestDocument`
1788
1926
  * when delegating to `upsertGraphCore`).
1789
1927
  */
1790
- resolveEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, now: number): WikiEdge[];
1928
+ resolveEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, now: number, drops?: EdgeDrop[]): WikiEdge[];
1791
1929
  /**
1792
1930
  * Backwards-compatible wrapper: resolves edges via {@link resolveEdges},
1793
1931
  * then persists each via `edgeRepo.addIgnoreDuplicate` in the supplied
@@ -1795,7 +1933,7 @@ declare class OntologyService {
1795
1933
  * want self-contained edge persistence (e.g. MaintenanceService heal /
1796
1934
  * backfill callers that do not have a separate edge-write step).
1797
1935
  */
1798
- resolveAndPersistEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, tx: SQLiteAdapter, now: number): Promise<number>;
1936
+ resolveAndPersistEdges(entityId: string, sourceId: string, sourceType: string | null, edges: ExtractedFactEdge[], manifest: OntologyManifest, titleIndex: Map<string, TitleIndexEntry>, tx: SQLiteAdapter, now: number, drops?: EdgeDrop[]): Promise<number>;
1799
1937
  }
1800
1938
 
1801
1939
  declare class PromptService {
@@ -1943,6 +2081,10 @@ declare class IngestionService {
1943
2081
  }[];
1944
2082
  }, tx: SQLiteAdapter, opts?: {
1945
2083
  strict?: boolean;
2084
+ diag?: {
2085
+ buffer: DiagnosticBuffer;
2086
+ operation: 'ingest' | 'upsertGraph';
2087
+ };
1946
2088
  }): Promise<{
1947
2089
  nodesWritten: number;
1948
2090
  edgesWritten: number;
@@ -2189,7 +2331,7 @@ declare class MaintenanceService {
2189
2331
  */
2190
2332
  private forgetDryRun;
2191
2333
  /** Core librarian pass (locks handled by {@link runLibrarian}). Package-internal orchestration hook. */
2192
- doRunLibrarian(entityId: string, promptOverride?: string): Promise<void>;
2334
+ doRunLibrarian(entityId: string, promptOverride?: string, trigger?: WikiDiagnosticTrigger): Promise<void>;
2193
2335
  /**
2194
2336
  * Core heal pass (locks handled by {@link runHeal}). Package-internal orchestration hook.
2195
2337
  *
@@ -2201,6 +2343,7 @@ declare class MaintenanceService {
2201
2343
  promptOverride?: string;
2202
2344
  batchSize?: number;
2203
2345
  bodyTruncationChars?: number;
2346
+ trigger?: WikiDiagnosticTrigger;
2204
2347
  }): Promise<HealResult>;
2205
2348
  /** Core ontology backfill pass (locks handled by {@link runOntologyBackfill}). Package-internal orchestration hook. */
2206
2349
  doRunOntologyBackfill(entityId: string, options?: {
@@ -2283,6 +2426,7 @@ declare class RetrievalService {
2283
2426
  * Stable tie-break sort: score desc → access_count desc → updated_at desc → id asc.
2284
2427
  */
2285
2428
  private _tieBreakSort;
2429
+ private _withoutDrafts;
2286
2430
  /**
2287
2431
  * Comparator for score + deterministic tie-break fields.
2288
2432
  * Negative return means "a ranks ahead of b" for descending score order.
@@ -2319,6 +2463,7 @@ declare class WriteService {
2319
2463
  * {@link write}) so a partial pass retries on the next write.
2320
2464
  */
2321
2465
  private maybeRunHeal;
2466
+ private reportBackgroundFailure;
2322
2467
  }
2323
2468
 
2324
2469
  /**
@@ -2695,6 +2840,25 @@ declare class WikiMemory {
2695
2840
  }>): Promise<void>;
2696
2841
  /** Set a fact's OKF v0.2 lifecycle status. Does NOT touch `updated_at`. */
2697
2842
  setLifecycleStatus(entryId: string, entityId: string, status: 'draft' | 'stable' | 'deprecated'): Promise<void>;
2843
+ /**
2844
+ * Live draft facts for one entity, newest first (spec §5.2). Default page
2845
+ * size 50, clamped to [1, 500]. Pass `nextCursor` back unchanged for the next page.
2846
+ */
2847
+ listDrafts(entityId: string, options?: {
2848
+ limit?: number;
2849
+ cursor?: string;
2850
+ }): Promise<DraftPage>;
2851
+ /**
2852
+ * Promote a draft to `stable` and record who reviewed it, atomically
2853
+ * (spec §5.2). Metadata writes only: `updated_at` is not bumped and no
2854
+ * outbox event is pushed. Pass `by: 'human:<id>'` so `trustTier` becomes
2855
+ * `'human-reviewed'`.
2856
+ *
2857
+ * @throws WikiDraftNotFound when no live draft with that id exists for the entity.
2858
+ */
2859
+ promoteDraft(entryId: string, entityId: string, reviewer: {
2860
+ by: string;
2861
+ }): Promise<void>;
2698
2862
  /** Set a fact's stale_after (epoch ms) or clear it. Does NOT touch `updated_at`. */
2699
2863
  setStaleAfter(entryId: string, entityId: string, date: number | null): Promise<void>;
2700
2864
  /** Set a fact's generated_by actor string. Does NOT touch `updated_at`. */
@@ -2713,4 +2877,4 @@ declare class WikiMemory {
2713
2877
  setGeneratedByTask(taskId: string, entityId: string, actor: string): Promise<void>;
2714
2878
  }
2715
2879
 
2716
- export { WikiGraphNodeOwnershipConflict as $, type ReembedResult as A, type VectorRankerFallback as B, type ChunkFailure as C, type DegradedRecord as D, type EmbedFactResult as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, type IngestDocumentResult as I, type VectorRankerRankArgs as J, type VectorRankerSemanticResult as K, type LLMProvider as L, type MemoryBundle as M, WikiBusyError as N, type OntologyManifest as O, type PromptOverrides as P, type WikiBusyOperation as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiCheckpoint as T, type WikiConfig as U, type VectorRanker as V, type WikiOptions as W, WikiDuplicateHashError as X, type WikiEdge as Y, type WikiEvent as Z, type WikiFact as _, type MemoryDump as a, WikiIngestEmptyError as a0, WikiInvalidReadOptions as a1, type WikiMemoryTestAccess as a2, type WikiOutboxEvent as a3, WikiParseError as a4, WikiSourceRefHashCollision as a5, WikiStrictOntologyViolation as a6, type WikiTask as a7, WikiTransactionError as a8, EmbeddingService as a9, ImportExportService as aa, IngestionService as ab, JobManager as ac, MaintenanceService as ad, RetrievalService as ae, SearchService as af, WriteService as ag, type FormattedMemoryDump as b, WikiMemory as c, type EmbedFailureKind as d, type EmbeddingMarkerKind as e, type EntityStatus as f, type ExtractedFact as g, type ExtractedFactEdge as h, type ExtractedFactWithOntology as i, type ExtractedTask as j, type GraphTraversalOptions as k, HEAL_RECHECK_MS as l, HOOK_TIMEOUT_MARKER as m, type HealResult as n, ONTOLOGY_BACKFILL_BATCH_SIZE as o, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as p, ONTOLOGY_BACKFILL_RECHECK_MS as q, type OntologyBackfillResult as r, type OntologyConfig as s, type OntologyEdgeType as t, type OntologyMode as u, type OntologyNodeType as v, type OntologyPromptContext as w, type OntologyUpdates as x, PromptService as y, PrunePartialFailureError as z };
2880
+ export { type WikiDiagnosticOperation as $, PrunePartialFailureError as A, type ReembedResult as B, type ChunkFailure as C, type DegradedRecord as D, type EmbedFactResult as E, type FormatContextOptions as F, type GraphNeighborhood as G, HEAL_BATCH_SIZE as H, type IngestDocumentResult as I, type VectorRankerFallback as J, type VectorRankerRankArgs as K, type LLMProvider as L, type MemoryBundle as M, type VectorRankerSemanticResult as N, ONTOLOGY_BACKFILL_BATCH_SIZE as O, type PromptOverrides as P, WikiBusyError as Q, type ReadOptions as R, type SQLiteAdapter as S, type WikiBusyOperation as T, type WikiCheckpoint as U, type VectorRanker as V, type WikiOptions as W, type WikiConfig as X, type WikiDiagnostic as Y, type WikiDiagnosticCode as Z, type WikiDiagnosticDetail as _, type MemoryDump as a, type WikiDiagnosticSeverity as a0, type WikiDiagnosticTrigger as a1, WikiDraftNotFound as a2, WikiDuplicateHashError as a3, type WikiEdge as a4, type WikiEvent as a5, type WikiFact as a6, WikiGraphNodeOwnershipConflict as a7, WikiIngestEmptyError as a8, WikiInvalidReadOptions as a9, type WikiMemoryTestAccess as aa, type WikiOutboxEvent as ab, WikiParseError as ac, WikiSourceRefHashCollision as ad, WikiStrictOntologyViolation as ae, type WikiTask as af, WikiTransactionError as ag, validateManifest as ah, EmbeddingService as ai, ImportExportService as aj, IngestionService as ak, JobManager as al, MaintenanceService as am, RetrievalService as an, SearchService as ao, WriteService as ap, type FormattedMemoryDump as b, WikiMemory as c, type DraftPage as d, type EmbedFailureKind as e, type EmbeddingMarkerKind as f, type EntityStatus as g, type ExtractedFact as h, type ExtractedFactEdge as i, type ExtractedFactWithOntology as j, type ExtractedTask as k, type GraphTraversalOptions as l, HEAL_RECHECK_MS as m, HOOK_TIMEOUT_MARKER as n, type HealResult as o, ONTOLOGY_BACKFILL_MAX_PROMPT_CHARS as p, ONTOLOGY_BACKFILL_RECHECK_MS as q, type OntologyBackfillResult as r, type OntologyConfig as s, type OntologyEdgeType as t, type OntologyManifest as u, type OntologyMode as v, type OntologyNodeType as w, type OntologyPromptContext as x, type OntologyUpdates as y, PromptService as z };
@@ -1,3 +1,3 @@
1
- export { a9 as EmbeddingService, aa as ImportExportService, ab as IngestionService, ac as JobManager, ac as JobManagerType, ad as MaintenanceService, ae as RetrievalService, af as SearchService, af as SearchServiceType, a2 as WikiMemoryTestAccess, ag as WriteService } from './testing-DYeMd9d1.mjs';
1
+ export { ai as EmbeddingService, aj as ImportExportService, ak as IngestionService, al as JobManager, al as JobManagerType, am as MaintenanceService, an as RetrievalService, ao as SearchService, ao as SearchServiceType, aa as WikiMemoryTestAccess, ap as WriteService } from './testing-Dmh1kfkd.mjs';
2
2
  import '@equationalapplications/core-okf';
3
3
  import 'minisearch';