@fgv/ts-agent-memory 5.1.0-48 → 5.1.0-50

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.
Files changed (164) hide show
  1. package/dist/packlets/index/memoryIndex.js +23 -16
  2. package/dist/packlets/index/memoryIndex.js.map +1 -1
  3. package/dist/packlets/ingest/orchestrator.js +13 -1
  4. package/dist/packlets/ingest/orchestrator.js.map +1 -1
  5. package/dist/packlets/retrieve/linkTraversalRetriever.js +13 -27
  6. package/dist/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
  7. package/dist/packlets/retrieve/recencyRetriever.js +8 -8
  8. package/dist/packlets/retrieve/recencyRetriever.js.map +1 -1
  9. package/dist/packlets/retrieve/retriever.js +98 -11
  10. package/dist/packlets/retrieve/retriever.js.map +1 -1
  11. package/dist/packlets/retrieve/semanticRetriever.js +16 -16
  12. package/dist/packlets/retrieve/semanticRetriever.js.map +1 -1
  13. package/dist/packlets/retrieve/structuredFilterRetriever.js +34 -12
  14. package/dist/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
  15. package/dist/packlets/retrieve/tagRetriever.js +8 -8
  16. package/dist/packlets/retrieve/tagRetriever.js.map +1 -1
  17. package/dist/packlets/retrieve/temporalRetrievers.js +24 -21
  18. package/dist/packlets/retrieve/temporalRetrievers.js.map +1 -1
  19. package/dist/packlets/store/coverage.js +6 -0
  20. package/dist/packlets/store/coverage.js.map +1 -0
  21. package/dist/packlets/store/fileTreeMemoryStore.js +303 -62
  22. package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -1
  23. package/dist/packlets/store/index.js +4 -0
  24. package/dist/packlets/store/index.js.map +1 -1
  25. package/dist/packlets/store/listSelection.js +36 -0
  26. package/dist/packlets/store/listSelection.js.map +1 -0
  27. package/dist/packlets/store/memoryStore.js +6 -0
  28. package/dist/packlets/store/memoryStore.js.map +1 -0
  29. package/dist/packlets/store/reconcile.js +6 -0
  30. package/dist/packlets/store/reconcile.js.map +1 -0
  31. package/dist/packlets/store/storeCoverage.js +102 -0
  32. package/dist/packlets/store/storeCoverage.js.map +1 -0
  33. package/dist/packlets/store/storeReconcile.js +122 -0
  34. package/dist/packlets/store/storeReconcile.js.map +1 -0
  35. package/dist/packlets/store/vectorMaintenance.js +116 -8
  36. package/dist/packlets/store/vectorMaintenance.js.map +1 -1
  37. package/dist/packlets/store/vectorRecordSource.js +44 -0
  38. package/dist/packlets/store/vectorRecordSource.js.map +1 -0
  39. package/dist/packlets/tools/memoryTools.js +25 -2
  40. package/dist/packlets/tools/memoryTools.js.map +1 -1
  41. package/dist/packlets/types/envelope.js +25 -0
  42. package/dist/packlets/types/envelope.js.map +1 -1
  43. package/dist/packlets/types/index.js +1 -0
  44. package/dist/packlets/types/index.js.map +1 -1
  45. package/dist/packlets/types/recordResolver.js +6 -0
  46. package/dist/packlets/types/recordResolver.js.map +1 -0
  47. package/dist/packlets/types/temporal.js.map +1 -1
  48. package/dist/packlets/vector/inMemoryCosineIndex.js +39 -18
  49. package/dist/packlets/vector/inMemoryCosineIndex.js.map +1 -1
  50. package/dist/packlets/vector/inMemoryFragmentCosineIndex.js +67 -12
  51. package/dist/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
  52. package/dist/packlets/vector/rebuildHelpers.js +38 -0
  53. package/dist/packlets/vector/rebuildHelpers.js.map +1 -0
  54. package/dist/packlets/vector/vectorIndex.js.map +1 -1
  55. package/dist/ts-agent-memory.d.ts +1140 -90
  56. package/lib/packlets/index/memoryIndex.d.ts +118 -27
  57. package/lib/packlets/index/memoryIndex.d.ts.map +1 -1
  58. package/lib/packlets/index/memoryIndex.js +23 -16
  59. package/lib/packlets/index/memoryIndex.js.map +1 -1
  60. package/lib/packlets/ingest/orchestrator.d.ts.map +1 -1
  61. package/lib/packlets/ingest/orchestrator.js +13 -1
  62. package/lib/packlets/ingest/orchestrator.js.map +1 -1
  63. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts +4 -11
  64. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts.map +1 -1
  65. package/lib/packlets/retrieve/linkTraversalRetriever.js +12 -26
  66. package/lib/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
  67. package/lib/packlets/retrieve/recencyRetriever.d.ts +4 -4
  68. package/lib/packlets/retrieve/recencyRetriever.d.ts.map +1 -1
  69. package/lib/packlets/retrieve/recencyRetriever.js +7 -7
  70. package/lib/packlets/retrieve/recencyRetriever.js.map +1 -1
  71. package/lib/packlets/retrieve/retriever.d.ts +113 -13
  72. package/lib/packlets/retrieve/retriever.d.ts.map +1 -1
  73. package/lib/packlets/retrieve/retriever.js +101 -10
  74. package/lib/packlets/retrieve/retriever.js.map +1 -1
  75. package/lib/packlets/retrieve/semanticRetriever.d.ts +3 -5
  76. package/lib/packlets/retrieve/semanticRetriever.d.ts.map +1 -1
  77. package/lib/packlets/retrieve/semanticRetriever.js +15 -15
  78. package/lib/packlets/retrieve/semanticRetriever.js.map +1 -1
  79. package/lib/packlets/retrieve/structuredFilterRetriever.d.ts +29 -7
  80. package/lib/packlets/retrieve/structuredFilterRetriever.d.ts.map +1 -1
  81. package/lib/packlets/retrieve/structuredFilterRetriever.js +33 -11
  82. package/lib/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
  83. package/lib/packlets/retrieve/tagRetriever.d.ts +4 -4
  84. package/lib/packlets/retrieve/tagRetriever.d.ts.map +1 -1
  85. package/lib/packlets/retrieve/tagRetriever.js +7 -7
  86. package/lib/packlets/retrieve/tagRetriever.js.map +1 -1
  87. package/lib/packlets/retrieve/temporalRetrievers.d.ts +7 -5
  88. package/lib/packlets/retrieve/temporalRetrievers.d.ts.map +1 -1
  89. package/lib/packlets/retrieve/temporalRetrievers.js +23 -20
  90. package/lib/packlets/retrieve/temporalRetrievers.js.map +1 -1
  91. package/lib/packlets/store/coverage.d.ts +102 -0
  92. package/lib/packlets/store/coverage.d.ts.map +1 -0
  93. package/lib/packlets/store/coverage.js +7 -0
  94. package/lib/packlets/store/coverage.js.map +1 -0
  95. package/lib/packlets/store/fileTreeMemoryStore.d.ts +88 -132
  96. package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
  97. package/lib/packlets/store/fileTreeMemoryStore.js +302 -61
  98. package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -1
  99. package/lib/packlets/store/index.d.ts +4 -0
  100. package/lib/packlets/store/index.d.ts.map +1 -1
  101. package/lib/packlets/store/index.js +4 -0
  102. package/lib/packlets/store/index.js.map +1 -1
  103. package/lib/packlets/store/listSelection.d.ts +101 -0
  104. package/lib/packlets/store/listSelection.d.ts.map +1 -0
  105. package/lib/packlets/store/listSelection.js +40 -0
  106. package/lib/packlets/store/listSelection.js.map +1 -0
  107. package/lib/packlets/store/memoryStore.d.ts +237 -0
  108. package/lib/packlets/store/memoryStore.d.ts.map +1 -0
  109. package/lib/packlets/store/memoryStore.js +7 -0
  110. package/lib/packlets/store/memoryStore.js.map +1 -0
  111. package/lib/packlets/store/reconcile.d.ts +82 -0
  112. package/lib/packlets/store/reconcile.d.ts.map +1 -0
  113. package/lib/packlets/store/reconcile.js +7 -0
  114. package/lib/packlets/store/reconcile.js.map +1 -0
  115. package/lib/packlets/store/storeCoverage.d.ts +45 -0
  116. package/lib/packlets/store/storeCoverage.d.ts.map +1 -0
  117. package/lib/packlets/store/storeCoverage.js +105 -0
  118. package/lib/packlets/store/storeCoverage.js.map +1 -0
  119. package/lib/packlets/store/storeReconcile.d.ts +41 -0
  120. package/lib/packlets/store/storeReconcile.d.ts.map +1 -0
  121. package/lib/packlets/store/storeReconcile.js +125 -0
  122. package/lib/packlets/store/storeReconcile.js.map +1 -0
  123. package/lib/packlets/store/vectorMaintenance.d.ts +74 -0
  124. package/lib/packlets/store/vectorMaintenance.d.ts.map +1 -1
  125. package/lib/packlets/store/vectorMaintenance.js +117 -8
  126. package/lib/packlets/store/vectorMaintenance.js.map +1 -1
  127. package/lib/packlets/store/vectorRecordSource.d.ts +36 -0
  128. package/lib/packlets/store/vectorRecordSource.d.ts.map +1 -0
  129. package/lib/packlets/store/vectorRecordSource.js +47 -0
  130. package/lib/packlets/store/vectorRecordSource.js.map +1 -0
  131. package/lib/packlets/tools/memoryTools.d.ts.map +1 -1
  132. package/lib/packlets/tools/memoryTools.js +25 -2
  133. package/lib/packlets/tools/memoryTools.js.map +1 -1
  134. package/lib/packlets/types/envelope.d.ts +33 -0
  135. package/lib/packlets/types/envelope.d.ts.map +1 -1
  136. package/lib/packlets/types/envelope.js +26 -0
  137. package/lib/packlets/types/envelope.js.map +1 -1
  138. package/lib/packlets/types/index.d.ts +1 -0
  139. package/lib/packlets/types/index.d.ts.map +1 -1
  140. package/lib/packlets/types/index.js +1 -0
  141. package/lib/packlets/types/index.js.map +1 -1
  142. package/lib/packlets/types/recordResolver.d.ts +39 -0
  143. package/lib/packlets/types/recordResolver.d.ts.map +1 -0
  144. package/lib/packlets/types/recordResolver.js +7 -0
  145. package/lib/packlets/types/recordResolver.js.map +1 -0
  146. package/lib/packlets/types/temporal.d.ts +26 -6
  147. package/lib/packlets/types/temporal.d.ts.map +1 -1
  148. package/lib/packlets/types/temporal.js.map +1 -1
  149. package/lib/packlets/vector/inMemoryCosineIndex.d.ts +9 -2
  150. package/lib/packlets/vector/inMemoryCosineIndex.d.ts.map +1 -1
  151. package/lib/packlets/vector/inMemoryCosineIndex.js +40 -19
  152. package/lib/packlets/vector/inMemoryCosineIndex.js.map +1 -1
  153. package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts +6 -3
  154. package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts.map +1 -1
  155. package/lib/packlets/vector/inMemoryFragmentCosineIndex.js +66 -11
  156. package/lib/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
  157. package/lib/packlets/vector/rebuildHelpers.d.ts +30 -0
  158. package/lib/packlets/vector/rebuildHelpers.d.ts.map +1 -0
  159. package/lib/packlets/vector/rebuildHelpers.js +42 -0
  160. package/lib/packlets/vector/rebuildHelpers.js.map +1 -0
  161. package/lib/packlets/vector/vectorIndex.d.ts +270 -15
  162. package/lib/packlets/vector/vectorIndex.d.ts.map +1 -1
  163. package/lib/packlets/vector/vectorIndex.js.map +1 -1
  164. package/package.json +7 -7
@@ -1,6 +1,7 @@
1
1
  import { AiAssist } from '@fgv/ts-extras';
2
2
  import { Brand } from '@fgv/ts-utils';
3
3
  import { Converter } from '@fgv/ts-utils';
4
+ import { DetailedResult } from '@fgv/ts-utils';
4
5
  import { FileTree } from '@fgv/ts-json-base';
5
6
  import { JsonSchema } from '@fgv/ts-json-base';
6
7
  import { Logging } from '@fgv/ts-utils';
@@ -31,11 +32,12 @@ export declare type AdmissionDecision = {
31
32
  */
32
33
  export declare class AsOfRetriever implements IMemoryRetriever {
33
34
  private readonly _index;
35
+ private readonly _resolver;
34
36
  private constructor();
35
37
  /** {@inheritDoc IMemoryRetriever.capabilities} */
36
38
  get capabilities(): IMemoryRetrieverCapabilities;
37
39
  /** Family-convention factory. */
38
- static create(index: IMemoryIndex): Result<AsOfRetriever>;
40
+ static create(params: IRetrieverCreateParams): Result<AsOfRetriever>;
39
41
  /** {@inheritDoc IMemoryRetriever.retrieve} */
40
42
  retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
41
43
  }
@@ -164,11 +166,12 @@ export declare function createMemoryTools(params: ICreateMemoryToolsParams): Rea
164
166
  */
165
167
  export declare class CurrentValidRetriever implements IMemoryRetriever {
166
168
  private readonly _index;
169
+ private readonly _resolver;
167
170
  private constructor();
168
171
  /** {@inheritDoc IMemoryRetriever.capabilities} */
169
172
  get capabilities(): IMemoryRetrieverCapabilities;
170
173
  /** Family-convention factory. */
171
- static create(index: IMemoryIndex): Result<CurrentValidRetriever>;
174
+ static create(params: IRetrieverCreateParams): Result<CurrentValidRetriever>;
172
175
  /** {@inheritDoc IMemoryRetriever.retrieve} */
173
176
  retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
174
177
  }
@@ -245,6 +248,22 @@ export declare const DEFAULT_SIMILARITY_TOP_K: number;
245
248
  */
246
249
  export declare function defaultMemoryScopeEncoding(scope: MemoryScopeKey): Result<string>;
247
250
 
251
+ /**
252
+ * Which derived artifact a {@link IMemoryStore.reconcile} call repairs.
253
+ *
254
+ * @remarks
255
+ * **The operation names its artifact rather than repairing everything wired**,
256
+ * and the reasons are structural rather than stylistic: the record and fragment
257
+ * lanes are independently wirable (so an unnamed repair on a fragment-only store
258
+ * would have to no-op or guess), their units are incommensurable (one vector per
259
+ * record vs. N), and their costs differ by orders of magnitude — a measured case
260
+ * put 68 fragments behind a single 56 KB record. An operation whose cost varies
261
+ * that much along a dimension it does not name is the accidental-expense shape
262
+ * `IMemoryStore.list`'s required selection exists to prevent.
263
+ * @public
264
+ */
265
+ export declare type DerivedArtifact = 'rank' | 'record-vector' | 'fragment-vector';
266
+
248
267
  /**
249
268
  * Converter for an attributed {@link IEdge}.
250
269
  * @public
@@ -287,6 +306,29 @@ export declare function edgeTargetKey(target: IEdgeTarget): string;
287
306
  */
288
307
  export declare const embeddedFragmentConverter: Converter<IEmbeddedFragment>;
289
308
 
309
+ /**
310
+ * The envelope's embedding reference if it carries a usable one, `undefined`
311
+ * otherwise — **the one place `null`-vs-absent is collapsed.**
312
+ *
313
+ * @remarks
314
+ * {@link IMemoryEnvelope.embeddingRef} is `string | null | undefined`, where
315
+ * `null` and absent both mean *not embedded* (`null` is the explicit sentinel;
316
+ * absent is the backwards-compat seam). That makes the obvious presence check
317
+ * wrong in **both** directions, and both mistakes were shipped before this
318
+ * accessor existed: `!== undefined` counts a `null` as an embedding that is not
319
+ * there, and `=== undefined` misses a `null` when looking for one that is
320
+ * missing. Neither is a type error, and neither is visible to a coverage gate,
321
+ * because the sentinel is a *value* rather than a branch.
322
+ *
323
+ * Returning the reference rather than a boolean is deliberate: a caller that
324
+ * needs the string gets the check for free, so there is no second, weaker way
325
+ * to ask.
326
+ *
327
+ * **Do not test `embeddingRef` for presence directly — call this.**
328
+ * @public
329
+ */
330
+ export declare function embeddingRefOf(envelope: IMemoryEnvelope): string | undefined;
331
+
290
332
  /**
291
333
  * Consumer-supplied domain key. The stable entity identity across versions;
292
334
  * the package never mints identity. Equals {@link MemoryId} for non-temporal
@@ -394,9 +436,49 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
394
436
  /** {@inheritDoc IMemoryStore.getById} */
395
437
  getById(scope: MemoryScopeKey, id: MemoryId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
396
438
  /** {@inheritDoc IMemoryStore.list} */
397
- list(filter?: IMemoryStoreListFilter): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
439
+ list(selection: MemoryListSelection): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
440
+ /** {@inheritDoc IMemoryStore.coverage} */
441
+ coverage(): Promise<Result<IDerivedStateCoverage>>;
442
+ /** {@inheritDoc IMemoryStore.listEntries} */
443
+ listEntries(): Promise<Result<ReadonlyArray<IIndexedMemoryEntry>>>;
398
444
  /** {@inheritDoc IMemoryStore.listScoped} */
399
445
  listScoped(): Promise<Result<ReadonlyArray<IScopedMemoryRecord>>>;
446
+ /** {@inheritDoc IMemoryRecordResolver.resolveRecord} */
447
+ resolveRecord(scope: MemoryScopeKey, id: MemoryId): Result<IMemoryRecord<unknown> | undefined>;
448
+ /**
449
+ * Materialize a selected set of entries into records, dropping any that have
450
+ * vanished since selection.
451
+ *
452
+ * @remarks
453
+ * A miss is not a failure. Selection reads the in-memory index and
454
+ * materialization reads storage, so a record deleted in between is a legitimate
455
+ * race and yields a shorter list rather than an error. A read that FAILS is a
456
+ * real fault and propagates.
457
+ */
458
+ private _materialize;
459
+ /**
460
+ * Materialize one entry, treating "gone" as a fault rather than a miss.
461
+ *
462
+ * @remarks
463
+ * For paths where a vanished record really does mean the index and the vault
464
+ * disagree, rather than that something legitimately removed it in between.
465
+ *
466
+ * Three of the four callers hold the write lock, so nothing can have removed
467
+ * the record since the entry was read. `get()`'s versioned path does not, and
468
+ * is safe only because temporal kinds never physically delete a version — they
469
+ * invalidate in place, and cap-cull does not apply to them. **If eviction is
470
+ * ever added to the temporal path, that caller must change**, or it
471
+ * reintroduces the race this method exists to detect.
472
+ *
473
+ * `listScoped` also does not hold the lock, and uses this deliberately anyway:
474
+ * it feeds a coverage report, so a silent drop there is worse than a loud
475
+ * failure. See its comment, and `docs/FUTURE.md` for the eviction window.
476
+ *
477
+ * The drop-tolerant counterpart is {@link FileTreeMemoryStore._materialize},
478
+ * for readers where a record that vanished between selection and
479
+ * materialization is a miss rather than a fault.
480
+ */
481
+ private _resolveRequired;
400
482
  /** {@inheritDoc IMemoryStore.asRecordSource} */
401
483
  asRecordSource(): IMemoryRecordSource;
402
484
  /**
@@ -407,6 +489,8 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
407
489
  * contributes nothing.
408
490
  */
409
491
  private static _projectAsOf;
492
+ /** {@inheritDoc IMemoryStore.reconcile} */
493
+ reconcile(kind: Kind, artifact: DerivedArtifact): Promise<Result<ReconcileReport>>;
410
494
  /** {@inheritDoc IMemoryStore.put} */
411
495
  put(record: IMemoryRecord<unknown>): Promise<Result<IMemoryRecord<unknown>>>;
412
496
  /** {@inheritDoc IMemoryStore.delete} */
@@ -581,6 +665,45 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
581
665
  */
582
666
  private _isMutableMetadataUnchanged;
583
667
  private _contentHash;
668
+ /**
669
+ * The rank branch of {@link FileTreeMemoryStore.reconcile}, under the write lock.
670
+ *
671
+ * @remarks
672
+ * Re-reads each record's file rather than trusting the in-memory index, for
673
+ * two reasons: the index holds converted bodies on some paths and raw ones on
674
+ * others, and re-serializing a *converted* body could change the bytes on disk
675
+ * — a reconcile of an ordering field has no business rewriting content.
676
+ * `splitFrontmatter` hands back the body text unconverted, so the round trip
677
+ * carries the authored characters through untouched and only the envelope
678
+ * moves. The parsed record is additionally put through `_verifyLoaded`, the
679
+ * same id-vs-filename and scope-derived-entityId check the load paths apply —
680
+ * without it, reconcile would be the one path that accepts and rewrites a file
681
+ * the store would refuse to load.
682
+ *
683
+ * "Untouched" is not quite "byte-identical", and the exception is line
684
+ * endings: `splitFrontmatter` strips a trailing `\r` per line and
685
+ * `joinFrontmatter` writes `\n` delimiters, so a CRLF-authored file comes back
686
+ * LF-normalized. That is **the store's behavior on every write path, not
687
+ * something reconcile introduces** — an ordinary `put` normalizes the same way
688
+ * — so reconcile does not rewrite content that a subsequent write would have
689
+ * left alone. The property being claimed here is the narrower and load-bearing
690
+ * one: no body is round-tripped through its registered Converter.
691
+ *
692
+ * The projector is fed an `IMemoryRecord<string>` carrying that raw body,
693
+ * which is exactly the shape {@link FileTreeMemoryStore._stampRank} hands it
694
+ * on the write path — so a projector cannot see one thing on a write and
695
+ * another on a reconcile. `_stampRank` itself is reused verbatim, which also
696
+ * inherits its throw semantics (logged at `warn`, `rank` cleared).
697
+ */
698
+ private _reconcileLocked;
699
+ /** The rank branch of {@link FileTreeMemoryStore._reconcileLocked}. */
700
+ private _reconcileRankLocked;
701
+ /**
702
+ * Re-apply the rank projector to one record on disk. Returns whether `rank`
703
+ * actually changed — an unchanged rank writes nothing, so a reconcile over an
704
+ * already-consistent store touches no files.
705
+ */
706
+ private _rewriteEnvelope;
584
707
  /**
585
708
  * Stamp the store-computed {@link IMemoryEnvelope.rank} onto a fully-built,
586
709
  * fully-stamped record by running the kind's registered {@link RankProjector}.
@@ -749,11 +872,12 @@ export declare function guardRetrieverCapabilities(query: IMemoryQuery, capabili
749
872
  */
750
873
  export declare class HistoryRetriever implements IMemoryRetriever {
751
874
  private readonly _index;
875
+ private readonly _resolver;
752
876
  private constructor();
753
877
  /** {@inheritDoc IMemoryRetriever.capabilities} */
754
878
  get capabilities(): IMemoryRetrieverCapabilities;
755
879
  /** Family-convention factory. */
756
- static create(index: IMemoryIndex): Result<HistoryRetriever>;
880
+ static create(params: IRetrieverCreateParams): Result<HistoryRetriever>;
757
881
  /** {@inheritDoc IMemoryRetriever.retrieve} */
758
882
  retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
759
883
  /** A version's world-truth start: its `valid_at`, defaulting to `created` when absent. */
@@ -816,6 +940,31 @@ export declare class HybridRetriever implements IMemoryRetriever {
816
940
  private _projectQuery;
817
941
  }
818
942
 
943
+ /**
944
+ * How much of one derived artifact exists, for one {@link Kind}.
945
+ *
946
+ * @remarks
947
+ * Two numbers rather than a percentage, deliberately: a ratio cannot express
948
+ * *"nothing was expected"*, and a kind that is intentionally not derived (an
949
+ * excluded kind, a kind with no projector) is a normal and healthy state that a
950
+ * `0%` would render as an alarm.
951
+ * @public
952
+ */
953
+ export declare interface IArtifactCoverage {
954
+ /**
955
+ * Records of this kind the store would derive this artifact for, **after** any
956
+ * exclusion. Read against {@link IDerivedStateCoverage.records} for the same
957
+ * kind: `records: 40, expected: 0` is the exclusion story, stated rather than
958
+ * inferred.
959
+ */
960
+ readonly expected: number;
961
+ /**
962
+ * Of those, how many the **store believes** are covered. See
963
+ * {@link IIndexCoverage.indexSize} for why the word "believes" is load-bearing.
964
+ */
965
+ readonly covered: number;
966
+ }
967
+
819
968
  /**
820
969
  * Registry of per-kind body Converters. Each memory {@link Kind} registers the
821
970
  * validated shape of its body; the store dispatches an `unknown` body through
@@ -959,6 +1108,35 @@ export declare interface ICycleGuardEdge {
959
1108
  readonly type: LinkType;
960
1109
  }
961
1110
 
1111
+ /**
1112
+ * A snapshot of how much of the store's **derived state** exists, resolved by
1113
+ * {@link Kind} — the answer to *"is my derived state consistent with my
1114
+ * records?"*.
1115
+ *
1116
+ * @remarks
1117
+ * **Absent is not zero.** Each artifact member is optional, and `undefined` means
1118
+ * *this artifact is not derived here at all* — no rank projector is registered,
1119
+ * or that index lane is not wired. It never means *nothing is covered*. Folding
1120
+ * the two would make a health surface render a confident `0%` for a feature the
1121
+ * deployment deliberately did not turn on, which is the same defect as
1122
+ * `embeddingRef`'s three-way ambiguity one level up.
1123
+ *
1124
+ * So: `fragmentVectors: undefined` is a store with no fragment index and is not a
1125
+ * problem. `fragmentVectors: { indexRecordCount: 0, indexFragmentCount: 0 }` is a
1126
+ * wired fragment index holding nothing, and probably is.
1127
+ * @public
1128
+ */
1129
+ export declare interface IDerivedStateCoverage {
1130
+ /** Records per kind — the denominator every other number is read against. */
1131
+ readonly records: ReadonlyMap<Kind, number>;
1132
+ /** Absent when no kind has a registered {@link RankProjector}. */
1133
+ readonly rank?: ReadonlyMap<Kind, IArtifactCoverage>;
1134
+ /** Absent when the record-vector lane is not wired. */
1135
+ readonly recordVectors?: IIndexCoverage;
1136
+ /** Absent when the fragment lane is not wired. */
1137
+ readonly fragmentVectors?: IFragmentIndexCoverage;
1138
+ }
1139
+
962
1140
  /**
963
1141
  * An attributed link between two records. Carries the relation type, the
964
1142
  * scope-qualified {@link IEdgeTarget | target}, and optional confidence /
@@ -1082,6 +1260,27 @@ export declare interface IEntityResolver {
1082
1260
  resolve(candidate: ICandidateRecord, similar: ReadonlyArray<IEntityResolutionCandidate>): Promise<Result<ResolutionVerdict>>;
1083
1261
  }
1084
1262
 
1263
+ /**
1264
+ * The minimum a temporal helper needs: something carrying an envelope.
1265
+ *
1266
+ * @remarks
1267
+ * Every predicate and selector in this module reads `envelope.temporal`,
1268
+ * `envelope.created` and `envelope.seq` and **nothing else** — no body has ever
1269
+ * been consulted. Taking the structural shape rather than `IMemoryRecord` lets
1270
+ * the same helpers serve both an `IMemoryRecord<unknown>` (which satisfies it)
1271
+ * and an `IIndexedMemoryEntry` from the index's projected read surface, so the
1272
+ * temporal `asOf` projection can run over envelopes and materialize only the
1273
+ * versions that survive it.
1274
+ *
1275
+ * The selectors are generic in this shape so they return exactly what they were
1276
+ * given rather than widening to the constraint.
1277
+ * @public
1278
+ */
1279
+ export declare interface IEnvelopeCarrier {
1280
+ /** The envelope the temporal predicates read. */
1281
+ readonly envelope: IMemoryEnvelope;
1282
+ }
1283
+
1085
1284
  /**
1086
1285
  * Stage 3 — the host's fact extractor. Turns a classified item into zero or more
1087
1286
  * {@link ICandidateRecord}s. Each candidate's body is validated against the
@@ -1289,6 +1488,31 @@ export declare interface IFileTreeMemoryStoreCreateParams {
1289
1488
  readonly onRecordError?: MemoryRecordErrorMode;
1290
1489
  }
1291
1490
 
1491
+ /**
1492
+ * Coverage of the fragment-granular vector index.
1493
+ *
1494
+ * @remarks
1495
+ * **Aggregate only, and the reason is structural rather than an omission.** The
1496
+ * record lane has a per-record marker on the envelope (`embeddingRef`), so its
1497
+ * numerator falls out of the same free walk that produces the denominator. The
1498
+ * fragment lane has **no envelope marker at all** — nothing on a record says
1499
+ * whether it has fragments — so a per-kind numerator would cost one
1500
+ * `IFragmentVectorIndex.has` call per record, and coverage is contractually
1501
+ * cheap (see {@link IMemoryStore.coverage}).
1502
+ *
1503
+ * The per-kind **denominator** is still available on
1504
+ * {@link IDerivedStateCoverage.records}. A caller who needs the per-kind
1505
+ * numerator runs `reconcile(kind, 'fragment-vector')`, which reports it because
1506
+ * it is already paying for the walk.
1507
+ * @public
1508
+ */
1509
+ export declare interface IFragmentIndexCoverage {
1510
+ /** Records with at least one fragment held. Mirrors `IFragmentVectorIndex.recordCount`. */
1511
+ readonly indexRecordCount: number;
1512
+ /** Total fragments held — the fan-out. Mirrors `IFragmentVectorIndex.fragmentCount`. */
1513
+ readonly indexFragmentCount: number;
1514
+ }
1515
+
1292
1516
  /**
1293
1517
  * A half-open `[start, end)` span into a record's body — the in-record locator a
1294
1518
  * {@link IFragmentVectorIndex} may carry on a fragment. `start` is inclusive,
@@ -1336,6 +1560,25 @@ export declare interface IFragmentQuery {
1336
1560
  readonly maxPerRecord?: number;
1337
1561
  }
1338
1562
 
1563
+ /**
1564
+ * A fragment-vector reconcile.
1565
+ *
1566
+ * @remarks
1567
+ * Note there is no `restamped`: the fragment lane has **no envelope marker**, so
1568
+ * there is no reference that can go missing and nothing to restamp. The
1569
+ * asymmetry with {@link IVectorReconcileReport} is real rather than an oversight.
1570
+ * @public
1571
+ */
1572
+ export declare interface IFragmentReconcileReport extends IReconcileReportBase {
1573
+ readonly artifact: 'fragment-vector';
1574
+ /** Records already represented, needing no embedder call. */
1575
+ readonly alreadyIndexed: number;
1576
+ /** Records whose embedder produced no fragments — this lane's decline. */
1577
+ readonly declined: number;
1578
+ /** Fragments written by this call — the fan-out `repaired` cannot express. */
1579
+ readonly fragments: number;
1580
+ }
1581
+
1339
1582
  /**
1340
1583
  * What a {@link FragmentSemanticRetriever} can do given its wiring.
1341
1584
  * @public
@@ -1400,6 +1643,114 @@ export declare interface IFragmentVectorIndex {
1400
1643
  * `topK` cut) so one long document cannot crowd out others.
1401
1644
  */
1402
1645
  query(vector: Float32Array, topK: number, maxPerRecord?: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;
1646
+ /**
1647
+ * Whether this index holds **any** fragment for the scope-qualified `target`.
1648
+ *
1649
+ * @remarks
1650
+ * The record-granular rationale on {@link IVectorIndex.has} applies verbatim —
1651
+ * a repair that trusts `embeddingRef` is not a repair. Note the granularity
1652
+ * this deliberately does **not** offer: it answers *"is this record
1653
+ * represented?"*, not *"is this particular fragment present?"*. Fragment
1654
+ * writes are whole-record-replace, so a record is either represented by the
1655
+ * current fragment set or not represented at all; a per-fragment membership
1656
+ * check would imply an incremental write path that does not exist.
1657
+ */
1658
+ has(target: IEdgeTarget): Promise<Result<boolean>>;
1659
+ /**
1660
+ * The number of **records** with at least one fragment held.
1661
+ *
1662
+ * @remarks
1663
+ * Deliberately **not** named `size`, unlike {@link IVectorIndex.size}. This
1664
+ * index is one-to-many, so `size` has two defensible readings and a reader
1665
+ * arriving from the record-granular sibling — where `size` counts vectors —
1666
+ * would take the wrong one silently. Two explicitly-named counts cost one extra
1667
+ * member and cannot be misread.
1668
+ */
1669
+ readonly recordCount: number;
1670
+ /**
1671
+ * The total number of **fragments** held across all records.
1672
+ *
1673
+ * @remarks
1674
+ * The fan-out, and the number a caller actually watches: fragments-per-record
1675
+ * is what makes a fragment reconcile expensive, and neither `recordCount` nor a
1676
+ * record-granular count answers it.
1677
+ */
1678
+ readonly fragmentCount: number;
1679
+ /**
1680
+ * Re-embed every record from `source` and rebuild the fragment index from
1681
+ * scratch — the **backfill / reconcile** operation, sibling to
1682
+ * {@link IVectorIndex.rebuild}.
1683
+ *
1684
+ * @remarks
1685
+ * On the contract for exactly the reasons its record-granular sibling is, and
1686
+ * the fragment lane was worse off: `rebuild` existed only on the bundled
1687
+ * in-memory class, and the durable `SqliteVecFragmentIndex` had **no backfill
1688
+ * at all**, so a persistent fragment index could not be reconciled by any
1689
+ * route — contractual or concrete. Records written while it was unwired, a
1690
+ * re-embed after a segmenter change, and reconciliation after a swallowed
1691
+ * fragment-embed failure were all unreachable.
1692
+ *
1693
+ * Semantics are kept observably identical to the record-granular sibling so a
1694
+ * caller who has learned one has learned both: a `source.list()` failure is
1695
+ * fatal and carries no detail (nothing was attempted, and the existing index is
1696
+ * untouched); a genuine rebuild then resets first; `onRecordError` defaults to
1697
+ * `'fail'`; and a failure carries whatever the attempt had established on the
1698
+ * `detail`.
1699
+ *
1700
+ * **A rebuild does NOT re-establish the vector dimension on a persistent
1701
+ * index, and this is the one place the two shipped implementations genuinely
1702
+ * differ.** The in-memory indexes forget their dimension when they reset, so a
1703
+ * rebuild with a different-dimension embedder simply re-establishes it. A
1704
+ * `vec0`-backed index cannot: its reset is a `DELETE FROM`, the table's
1705
+ * declared dimension is part of its schema, and `vec0` has no `ALTER TABLE`.
1706
+ * So a rebuild that changes dimension **succeeds in memory and fails on
1707
+ * SQLite**, where it needs the same drop-and-re-index the package README
1708
+ * prescribes for any schema change (drop the table, or point the index at a
1709
+ * fresh `tableName`). Only embedding time is at risk — vectors are derived and
1710
+ * the vault records remain authoritative.
1711
+ */
1712
+ rebuild(source: IMemoryRecordSource, embed: FragmentEmbedder, options?: IVectorRebuildOptions): Promise<DetailedResult<IFragmentVectorRebuildReport, IFragmentVectorRebuildReport>>;
1713
+ }
1714
+
1715
+ /**
1716
+ * What an {@link IFragmentVectorIndex.rebuild} established, resolved by
1717
+ * {@link Kind} — the fragment-granular sibling of {@link IVectorRebuildReport}.
1718
+ *
1719
+ * @remarks
1720
+ * The rule stated on {@link IVectorRebuildReport} applies here verbatim and is
1721
+ * not re-opened: **every count is resolved by kind unless there is a stated
1722
+ * reason it cannot be**, `excluded` is optional because only the source can know
1723
+ * it, and the report is carried on a failure as well as a success.
1724
+ *
1725
+ * The one member with no record-granular analogue is
1726
+ * {@link IFragmentVectorRebuildReport.fragments | fragments} — the fan-out. It is
1727
+ * the number that distinguishes this lane: `indexed: 40` says forty records are
1728
+ * represented and says nothing about whether that cost forty embedding round
1729
+ * trips or four thousand, which is the difference between a reconcile that
1730
+ * finishes in a second and one that blocks a request past thirty.
1731
+ *
1732
+ * A **declined** record is one whose {@link FragmentEmbedder} returned an empty
1733
+ * array — the fragment lane's way of saying *intentionally not embedded*. Note
1734
+ * this is a different mechanism from a {@link MemoryEmbedder} decline: an empty
1735
+ * array still performs a real whole-record-replace (which is what clears any
1736
+ * stale fragments), where a record-granular decline skips the index entirely.
1737
+ * @public
1738
+ */
1739
+ export declare interface IFragmentVectorRebuildReport {
1740
+ /** Records that ended with at least one fragment held, per kind. */
1741
+ readonly indexed: ReadonlyMap<Kind, number>;
1742
+ /** Fragments held, per kind — the fan-out `indexed` cannot express. */
1743
+ readonly fragments: ReadonlyMap<Kind, number>;
1744
+ /** Records whose embedder intentionally produced no fragments, per kind. */
1745
+ readonly declined: ReadonlyMap<Kind, number>;
1746
+ /**
1747
+ * Records the source filtered out before the rebuild saw them, per kind.
1748
+ * `undefined` means *this source does not report exclusions*; an empty map
1749
+ * means *it does, and excluded nothing*.
1750
+ */
1751
+ readonly excluded?: ReadonlyMap<Kind, number>;
1752
+ /** Records that failed, per record, with the error — a fault, never a decline. */
1753
+ readonly skipped: ReadonlyArray<ISkippedVectorRecord>;
1403
1754
  }
1404
1755
 
1405
1756
  /**
@@ -1443,6 +1794,58 @@ export declare interface IIdentityCodecResult {
1443
1794
  readonly isVersioned: boolean;
1444
1795
  }
1445
1796
 
1797
+ /**
1798
+ * Coverage of the record-granular vector index.
1799
+ * @public
1800
+ */
1801
+ export declare interface IIndexCoverage {
1802
+ /** Per-kind expected/covered, derived from the envelope walk. */
1803
+ readonly perKind: ReadonlyMap<Kind, IArtifactCoverage>;
1804
+ /**
1805
+ * What the index **actually holds**, whole-index.
1806
+ *
1807
+ * @remarks
1808
+ * **This is a fact and {@link IArtifactCoverage.covered} is a belief, and the
1809
+ * two are reported separately because their disagreement is the only free
1810
+ * signal that distinguishes a persistent index from a fresh one.** `covered`
1811
+ * counts envelopes carrying an `embeddingRef`; `indexSize` counts vectors. With
1812
+ * a persistent index they agree. With an in-memory index at open they do not —
1813
+ * the envelopes still claim references from previous sessions while the index
1814
+ * holds nothing, so `covered` **lies, in the confident direction**.
1815
+ *
1816
+ * Collapsing them into one "coverage %" would destroy that signal, which is why
1817
+ * this type does not offer one.
1818
+ */
1819
+ readonly indexSize: number;
1820
+ }
1821
+
1822
+ /**
1823
+ * What the index HOLDS and what every index read returns: a record's scope and
1824
+ * its {@link IMemoryEnvelope}, and **no body**.
1825
+ *
1826
+ * @remarks
1827
+ * The index is a derived *selection* structure, and selection has never needed a
1828
+ * body — every filter the store and the retrievers apply reads envelope fields
1829
+ * (`scope` / `kind` / `tags` / `contentHash` / `provenance` / `links` /
1830
+ * `temporal` / `updated` / `seq` / `rank`). Returning whole records made every
1831
+ * conforming index hold every body by construction, which was the store's
1832
+ * resident-memory ceiling; returning envelopes removes it from the contract
1833
+ * rather than from one implementation.
1834
+ *
1835
+ * A caller that needs the body **materializes it explicitly** — through
1836
+ * {@link IMemoryStore.getById}, or an `IMemoryRecordResolver` where one is
1837
+ * wired. That is deliberately visible: a lazy `body` getter would have kept
1838
+ * every call site compiling while turning a memory read into a file read behind
1839
+ * an unchanged type, which is a silent performance cliff rather than a migration.
1840
+ * @public
1841
+ */
1842
+ export declare interface IIndexedMemoryEntry {
1843
+ /** The scope the record is stored under. */
1844
+ readonly scope: MemoryScopeKey;
1845
+ /** The record's envelope. No body — see the remarks. */
1846
+ readonly envelope: IMemoryEnvelope;
1847
+ }
1848
+
1446
1849
  /**
1447
1850
  * A memory record paired with the {@link MemoryScopeKey | scope} it lives
1448
1851
  * under. The scope is not carried on the {@link IMemoryEnvelope} (it is derived
@@ -1644,6 +2047,14 @@ export declare interface IMemoryEnvelope {
1644
2047
  * Absent when the kind has no registered projector (or the projector threw on
1645
2048
  * this record). Ordered retrieval (`orderBy: 'rank'`) and the index's rank view
1646
2049
  * sort by this value descending, placing records with an absent `rank` last.
2050
+ *
2051
+ * **The projector runs on the write path only — registering one does not rank
2052
+ * records already in the store.** Because absent sorts last, those records land
2053
+ * below every subsequently-written one regardless of what the projector would
2054
+ * have scored them, so the result is not a partial ordering but one inverted
2055
+ * with respect to the projector's intent, with nothing failing to say so. Call
2056
+ * `IMemoryStore.reconcile(kind, 'rank')` after registering a projector against a
2057
+ * populated store.
1647
2058
  */
1648
2059
  readonly rank?: number;
1649
2060
  /** Structured provenance (never a flat enum). */
@@ -1676,40 +2087,98 @@ export declare interface IMemoryFileParts {
1676
2087
  * patched incrementally on every write ({@link IMemoryIndex.patch}).
1677
2088
  *
1678
2089
  * @remarks
1679
- * B1 builds the maps; link-traversal BFS over {@link IMemoryIndex.backlinks}
1680
- * is B2. The accessors return records (not bare ids) so the B2 retrievers can
1681
- * consume them directly.
2090
+ * Every read returns the projected {@link IIndexedMemoryEntry} scope and
2091
+ * envelope, no body. See that type for why.
2092
+ *
2093
+ * **The conformance rule: an index is a derived, COMPLETE, FAITHFUL projection
2094
+ * of the vault.** An implementation may change *where* entries are stored and
2095
+ * *how* they are looked up; it may not change *which* entries exist or *what any
2096
+ * envelope says*. Concretely, {@link IMemoryIndex.entries} must return exactly
2097
+ * one entry per record the store has written and not deleted, and each entry's
2098
+ * envelope must be the one the store patched in.
2099
+ *
2100
+ * An index that filters, truncates, deduplicates, or synthesizes entries is not
2101
+ * a conforming implementation, and the reason is not tidiness: **the store's
2102
+ * write path derives from these reads** — content-hash dedup, write-policy
2103
+ * admission cohorts, and temporal version histories all read the index. An index
2104
+ * that hides an entry does not merely hide it from queries; it changes what the
2105
+ * next write does. That is why the previous guidance said only a faithful
2106
+ * delegating decorator was safe to inject. This invariant is what that guidance
2107
+ * was reaching for, stated so that a genuinely different implementation (a
2108
+ * SQLite-backed index, a lazily-paged one) is permitted while the reshaping that
2109
+ * was the actual hazard stays out.
2110
+ *
2111
+ * **Ordering is NOT part of the contract.** {@link IMemoryIndex.entries} may
2112
+ * return entries in any order and callers that need one sort explicitly. Note
2113
+ * this is a *behavioural* freedom the compiler cannot police: the bundled
2114
+ * {@link MemoryIndex} iterates a `Map` and so returns insertion order, which is
2115
+ * stable and observable, so code that came to rely on it keeps compiling and
2116
+ * changes results. The ordered accessors ({@link IMemoryIndex.byRecency},
2117
+ * {@link IMemoryIndex.byRank}, and the recency-ordered `byKind` / `byTag`) are
2118
+ * the supported way to ask for an order.
1682
2119
  * @public
1683
2120
  */
1684
2121
  export declare interface IMemoryIndex {
1685
2122
  /**
1686
- * Replace the entire index from a full set of records (a store walk).
1687
- * @returns The number of records indexed.
2123
+ * Replace the entire index from a full set of entries (a store walk).
2124
+ *
2125
+ * @remarks
2126
+ * Takes the **projected** {@link IIndexedMemoryEntry} form, not whole records,
2127
+ * and the distinction is load-bearing rather than cosmetic: **`patch` writes,
2128
+ * `rebuild` reads.** A rebuild is a whole-vault read that happens to terminate
2129
+ * in the index, so requiring whole records here would force every caller — the
2130
+ * store's own open path included — to materialize N bodies purely to feed a
2131
+ * structure that projects the envelope back out and discards them. `patch`
2132
+ * keeps whole records because it carries exactly one, which its caller already
2133
+ * holds.
2134
+ *
2135
+ * @returns The number of entries indexed.
1688
2136
  */
1689
- rebuild(entries: ReadonlyArray<IIndexedMemoryRecord>): Result<number>;
2137
+ rebuild(entries: ReadonlyArray<IIndexedMemoryEntry>): Result<number>;
1690
2138
  /**
1691
2139
  * Apply a single incremental change. `'put'` inserts or replaces the entry
1692
2140
  * at its `(scope, id)` key (removing any prior associations first); `'delete'`
1693
2141
  * removes it.
2142
+ *
2143
+ * @remarks
2144
+ * Takes the whole record — see {@link IMemoryIndex.rebuild} for why this one
2145
+ * does and that one does not. It costs nothing (the caller is mid-write and
2146
+ * holds the record already) and it is the single point at which an index
2147
+ * maintaining a body-derived view could observe content without a re-read.
2148
+ * What is *held* is still only the projection.
2149
+ *
1694
2150
  * @returns The entry that was applied.
1695
2151
  */
1696
2152
  patch(op: MemoryIndexPatchOp, entry: IIndexedMemoryRecord): Result<IIndexedMemoryRecord>;
1697
- /** Every indexed entry (scope + record). Primary read surface for the store. */
1698
- entries(): ReadonlyArray<IIndexedMemoryRecord>;
1699
- /** Records of the given kind, in recency order (most-recently-updated first). */
1700
- byKind(kind: Kind): ReadonlyArray<IMemoryRecord<unknown>>;
1701
- /** Records carrying the given tag, in recency order. */
1702
- byTag(tag: Tag): ReadonlyArray<IMemoryRecord<unknown>>;
1703
- /** All records in recency order (most-recently-updated first). */
1704
- byRecency(): ReadonlyArray<IMemoryRecord<unknown>>;
1705
- /**
1706
- * All records ordered by store-computed {@link IMemoryEnvelope.rank} descending,
1707
- * with recency (most-recently-updated, then `seq`) as a tiebreak. Records with
1708
- * an absent `rank` sort LAST (after every ranked record), then by recency among
2153
+ /** Every indexed entry (scope + envelope). Primary read surface for the store. */
2154
+ entries(): ReadonlyArray<IIndexedMemoryEntry>;
2155
+ /**
2156
+ * The entry at a scope-qualified address, or `undefined` if none.
2157
+ *
2158
+ * @remarks
2159
+ * On the contract because its absence made every caller that wanted **one**
2160
+ * entry rebuild a map of **all** of them: both `SemanticRetriever` (resolving
2161
+ * at most `topK` hits) and `LinkTraversalRetriever` (resolving a BFS frontier)
2162
+ * built a full-index `Map` per query for want of this. The index already keys
2163
+ * on `(scope, id)` internally, so this exposes a lookup it was doing anyway.
2164
+ */
2165
+ get(target: IEdgeTarget): IIndexedMemoryEntry | undefined;
2166
+ /** Entries of the given kind, in recency order (most-recently-updated first). */
2167
+ byKind(kind: Kind): ReadonlyArray<IIndexedMemoryEntry>;
2168
+ /** Entries carrying the given tag, in recency order. */
2169
+ byTag(tag: Tag): ReadonlyArray<IIndexedMemoryEntry>;
2170
+ /** All entries in recency order (most-recently-updated first). */
2171
+ byRecency(): ReadonlyArray<IIndexedMemoryEntry>;
2172
+ /**
2173
+ * All entries ordered by store-computed {@link IMemoryEnvelope.rank} descending,
2174
+ * with recency (most-recently-updated, then `seq`) as a tiebreak. Entries with
2175
+ * an absent `rank` sort LAST (after every ranked entry), then by recency among
1709
2176
  * themselves. Serves a bounded top-M ({@link IMemoryEnvelope.rank}-ordered) page
1710
- * from the in-memory index with no full-vault (filesystem) scan.
2177
+ * from the in-memory index with no full-vault (filesystem) scan — and since the
2178
+ * page is envelope-only, a caller taking the top M materializes M bodies rather
2179
+ * than the vault.
1711
2180
  */
1712
- byRank(): ReadonlyArray<IMemoryRecord<unknown>>;
2181
+ byRank(): ReadonlyArray<IIndexedMemoryEntry>;
1713
2182
  /**
1714
2183
  * The scope-qualified sources of records whose `links` point AT `target`
1715
2184
  * (inbound edges), keyed on the target's `(scope, id)` address. The seed map
@@ -1916,6 +2385,23 @@ export declare interface IMemoryQuery {
1916
2385
  readonly scope?: MemoryScopeKey;
1917
2386
  /** Restrict to records carrying this tag (exact match). */
1918
2387
  readonly tag?: Tag;
2388
+ /**
2389
+ * Restrict to records whose {@link IProvenance.source | provenance.source} is
2390
+ * exactly this value — the "show me everything this source produced" axis, for
2391
+ * review, attribution, and retraction after a bad ingest.
2392
+ *
2393
+ * @remarks
2394
+ * Exact match on `source` only, deliberately: not a subset match over the whole
2395
+ * {@link IProvenance} block, not a presence check, and not a match on `by` /
2396
+ * `model` / the consumer-owned extension keys. `source` is the only field of
2397
+ * that block the library assigns meaning to, and every record carries one
2398
+ * (`envelope.provenance` is required), so this axis is total — it partitions
2399
+ * the vault rather than filtering out records that merely omit a field.
2400
+ *
2401
+ * Applied in the shared pre-filter alongside `scope` / `kind` / `tag`, so every
2402
+ * retriever honours it and combining axes composes as AND.
2403
+ */
2404
+ readonly provenanceSource?: ProvenanceSource;
1919
2405
  /**
1920
2406
  * Restrict to records of this kind — the single-kind shorthand for
1921
2407
  * {@link IMemoryQuery.kinds | kinds}. When both are set they compose as AND
@@ -1976,7 +2462,7 @@ export declare interface IMemoryQuery {
1976
2462
  * empty page, never a throw.
1977
2463
  */
1978
2464
  readonly offset?: number;
1979
- /** Arbitrary predicate applied after the scope / kind / tag pre-filter. */
2465
+ /** Arbitrary predicate applied after the scope / kind / tag / provenance-source pre-filter. */
1980
2466
  readonly filter?: (record: IMemoryRecord<unknown>) => boolean;
1981
2467
  }
1982
2468
 
@@ -1994,6 +2480,72 @@ export declare interface IMemoryRecord<TBody = unknown> {
1994
2480
  readonly body: TBody;
1995
2481
  }
1996
2482
 
2483
+ /**
2484
+ * What a {@link IMemoryRecordSource.list} call yields: the records the rebuild
2485
+ * should embed, plus — when the source can say — what it filtered out on the way.
2486
+ *
2487
+ * @remarks
2488
+ * The exclusion count originates here because **this is the layer where the
2489
+ * decision is made**. A rebuild never sees an excluded record, so it cannot count
2490
+ * one; a report assembled without this would silently undercount coverage, and
2491
+ * undercount in the direction of looking healthier.
2492
+ *
2493
+ * A store accessor answering "how many are excluded right now" was considered and
2494
+ * declined: it answers a *different question* than the report does — "excluded
2495
+ * right now" versus "excluded in this reconcile" — and the two legitimately differ
2496
+ * whenever records are written between reconciles. Two correct-and-unequal numbers
2497
+ * are worse than one absent number: they invite treating a real difference as a
2498
+ * bug, or picking whichever supports the conclusion already held.
2499
+ * @public
2500
+ */
2501
+ export declare interface IMemoryRecordListing {
2502
+ /** Every record the rebuild should embed, each paired with its scoped address. */
2503
+ readonly records: ReadonlyArray<IScopedMemoryRecord>;
2504
+ /**
2505
+ * Records this source filtered out, counted by {@link Kind}. Omit it entirely if
2506
+ * the source does not track exclusions — that reads as *"cannot say"* on
2507
+ * {@link IVectorRebuildReport.excluded}, which is distinct from an empty map
2508
+ * (*"nothing was excluded"*).
2509
+ */
2510
+ readonly excluded?: ReadonlyMap<Kind, number>;
2511
+ }
2512
+
2513
+ /**
2514
+ * Materializes one record's body from its scope-qualified address — the other
2515
+ * half of the partial-read split, and the narrowest seam that can be.
2516
+ *
2517
+ * @remarks
2518
+ * The index holds `IIndexedMemoryEntry` (scope + envelope, no body), so anything
2519
+ * that *selects* works from envelopes and anything that must *return records*
2520
+ * resolves the survivors through this. `FileTreeMemoryStore` implements it over
2521
+ * the same read path `getById` uses, so a resolved record is byte-identical to a
2522
+ * keyed read, verification included.
2523
+ *
2524
+ * **Deliberately one method, and deliberately not the store.** A retriever needs
2525
+ * exactly this capability; handing it an `IMemoryStore` would hand it `put` and
2526
+ * `delete` as well, and would invert the construction direction (the store builds
2527
+ * retrievers today, not the reverse). One method also keeps a test double
2528
+ * trivial.
2529
+ *
2530
+ * Synchronous `Result` rather than `Promise<Result>` because every shipped
2531
+ * `FileTree` backend resolves without awaiting, and an async signature here would
2532
+ * have rippled through retrievers that are otherwise synchronous over the index.
2533
+ * A future backend that genuinely needs I/O should be adapted at its own boundary
2534
+ * rather than by making this contract async for everyone.
2535
+ *
2536
+ * Resolving an address the vault does not hold is `succeed(undefined)`, not a
2537
+ * failure — an entry can legitimately vanish between selection and
2538
+ * materialization (a concurrent delete), and that is a miss rather than a fault.
2539
+ * A failure means the record is there and could not be read.
2540
+ * @public
2541
+ */
2542
+ export declare interface IMemoryRecordResolver {
2543
+ /**
2544
+ * The record at `(scope, id)`, or `undefined` if the vault does not hold one.
2545
+ */
2546
+ resolveRecord(scope: MemoryScopeKey, id: MemoryId): Result<IMemoryRecord<unknown> | undefined>;
2547
+ }
2548
+
1997
2549
  /**
1998
2550
  * The minimal record-source surface {@link InMemoryCosineIndex.rebuild} reads to
1999
2551
  * re-embed an entire vault. Each entry carries the record's scope-qualified
@@ -2005,8 +2557,12 @@ export declare interface IMemoryRecord<TBody = unknown> {
2005
2557
  * @public
2006
2558
  */
2007
2559
  export declare interface IMemoryRecordSource {
2008
- /** List every record in the vault, each paired with its scoped address. */
2009
- list(): Promise<Result<ReadonlyArray<IScopedMemoryRecord>>>;
2560
+ /**
2561
+ * List every record the rebuild should embed, each paired with its scoped
2562
+ * address, plus the exclusions this source applied if it tracks them. See
2563
+ * {@link IMemoryRecordListing}.
2564
+ */
2565
+ list(): Promise<Result<IMemoryRecordListing>>;
2010
2566
  }
2011
2567
 
2012
2568
  /**
@@ -2044,15 +2600,23 @@ export declare interface IMemoryRetrieverCapabilities {
2044
2600
 
2045
2601
  /**
2046
2602
  * The writable, FileTree-backed, content-hash-deduped memory store.
2603
+ *
2604
+ * @remarks
2605
+ * Extends {@link IMemoryRecordResolver} because the retrievers take a resolver
2606
+ * and a store is the obvious one to hand them — `{ index, resolver: store }` is
2607
+ * the documented wiring, and it has to type-check for the `IMemoryStore` handle
2608
+ * a consumer actually holds, not only for the concrete `FileTreeMemoryStore`.
2047
2609
  * @public
2048
2610
  */
2049
- export declare interface IMemoryStore {
2611
+ export declare interface IMemoryStore extends IMemoryRecordResolver {
2050
2612
  /**
2051
2613
  * Keyed read by entity id. Resolves `entityId` to a storage address via the
2052
2614
  * registered {@link IIdentityCodec} for `kind`. Returns `undefined` when no
2053
- * record exists. For a versioned (temporal) kind this returns the current
2054
- * version, resolved from the derived in-memory index (not re-read/re-verified
2055
- * from disk per callthe index is kept in sync with every write).
2615
+ * record exists. For a versioned (temporal) kind the current version is
2616
+ * *selected* from the derived in-memory index the version history is walked
2617
+ * over envelopes, never bodiesand then that one version is read from
2618
+ * storage. So the selection costs no file reads and the result costs exactly
2619
+ * one, rather than one per version.
2056
2620
  */
2057
2621
  get(kind: Kind, entityId: EntityId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
2058
2622
  /**
@@ -2060,9 +2624,77 @@ export declare interface IMemoryStore {
2060
2624
  */
2061
2625
  getById(scope: MemoryScopeKey, id: MemoryId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
2062
2626
  /**
2063
- * List records, filtered in-memory over the derived index.
2627
+ * List records: select over the derived index, then materialize the survivors.
2628
+ *
2629
+ * @remarks
2630
+ * **The selection is required and must narrow.** Since the index holds
2631
+ * envelopes only, every returned record is read from storage — so a call that
2632
+ * narrows nothing reads the whole vault, and that has to be a decision rather
2633
+ * than a default. Omitting the argument is a compile error; passing one with no
2634
+ * `scope` / `kind` / `tag` fails with a message naming {@link scanEveryRecord},
2635
+ * which is how a caller says it meant it.
2636
+ *
2637
+ * The requirement buys **explicitness, not a cost bound** — `{ kind }` on a
2638
+ * vault dominated by that kind still materializes most of it. What it prevents
2639
+ * is the whole-vault read nobody chose.
2640
+ *
2641
+ * If you only need to select, use {@link IMemoryStore.listEntries} instead: no
2642
+ * selection, no file reads.
2064
2643
  */
2065
- list(filter?: IMemoryStoreListFilter): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
2644
+ list(selection: MemoryListSelection): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
2645
+ /**
2646
+ * How much of the store's **derived state** exists, resolved by {@link Kind}.
2647
+ *
2648
+ * @remarks
2649
+ * Answers *"is my derived state consistent with my records, and if not by how
2650
+ * much?"* for every artifact the store derives — `rank`, record vectors, and
2651
+ * fragment vectors — in one call.
2652
+ *
2653
+ * **Cheap and total, by contract rather than by implementation.** Every input is
2654
+ * an envelope field or an index-side count: it reads **no record bodies** and
2655
+ * calls **no embedder**, and the walk over the vault's own state touches the
2656
+ * filesystem not at all. That is why it takes no selection, unlike
2657
+ * {@link IMemoryStore.list} — the guard there exists because an unnarrowed list
2658
+ * reads the vault, and putting one here would decorate a free operation and make
2659
+ * that guard mean less.
2660
+ *
2661
+ * **The index-side counts are the one exception, and it is the caller's own
2662
+ * index that spends it.** A persistent index answers `size` / `recordCount` /
2663
+ * `fragmentCount` with a query — `SqliteVecVectorIndex` runs a prepared `COUNT`
2664
+ * — so on a durable backend this call does I/O and can **fail**. It is bounded
2665
+ * (one count per wired index, never per record) and it is why the return is a
2666
+ * `Result` rather than a bare value.
2667
+ *
2668
+ * *If a future addition to the report would require reading a record body, it
2669
+ * does not belong on this report.*
2670
+ *
2671
+ * The counterpart is {@link IMemoryStore.reconcile}: coverage says **how big**
2672
+ * the gap is, cheaply; reconcile says **what it was** and closes what it can, at
2673
+ * the cost of re-running the embedder. Neither substitutes for the other.
2674
+ *
2675
+ * **A lane is reported here whenever its *index* is wired**, which is weaker
2676
+ * than what reconcile requires (index **and** embedder). That is intended: an
2677
+ * index without an embedder still holds vectors and still answers queries, so
2678
+ * its coverage is a real number worth reporting. The consequence to expect is a
2679
+ * half-wired store that reports a gap `reconcile` will refuse to close, naming
2680
+ * the missing embedder.
2681
+ */
2682
+ coverage(): Promise<Result<IDerivedStateCoverage>>;
2683
+ /**
2684
+ * Every entry in the vault — scope and envelope, **no bodies**. Reads no files
2685
+ * and requires no selection, because there is nothing to be careful about: it
2686
+ * returns what the index already holds.
2687
+ *
2688
+ * @remarks
2689
+ * This is the whole-vault read most callers actually want. Selection, grouping,
2690
+ * counting, "which kinds are in here", "what links at this" — all of it is
2691
+ * envelope work. Reach for {@link IMemoryStore.list} with
2692
+ * {@link scanEveryRecord} only when you genuinely need every body.
2693
+ *
2694
+ * Synchronous in spirit but `Promise`-returning for consistency with the rest
2695
+ * of the store surface.
2696
+ */
2697
+ listEntries(): Promise<Result<ReadonlyArray<IIndexedMemoryEntry>>>;
2066
2698
  /**
2067
2699
  * List EVERY record in the vault, each paired with its scope-qualified
2068
2700
  * `(scope, id)` address — the projection {@link IMemoryRecordSource} requires.
@@ -2084,6 +2716,11 @@ export declare interface IMemoryStore {
2084
2716
  * every kind participates and the filter is the identity. `listScoped` itself is
2085
2717
  * **not** filtered and remains the whole-vault surface.
2086
2718
  *
2719
+ * The filter also **counts what it drops**, onto
2720
+ * {@link IMemoryRecordListing.excluded} — this is the layer where the exclusion
2721
+ * decision is made and so the only one that can. The count is always present
2722
+ * (empty when nothing was excluded).
2723
+ *
2087
2724
  * The store cannot implement {@link IMemoryRecordSource} directly because its
2088
2725
  * `list(filter?)` returns bare records (the ergonomic query surface) while the
2089
2726
  * seam's `list()` returns scope-qualified records.
@@ -2144,6 +2781,43 @@ export declare interface IMemoryStore {
2144
2781
  * existing record unchanged on a dedup no-op.
2145
2782
  */
2146
2783
  put(record: IMemoryRecord<unknown>): Promise<Result<IMemoryRecord<unknown>>>;
2784
+ /**
2785
+ * Repair one derived artifact for one {@link Kind} — **targeted and
2786
+ * non-destructive**, unlike `IVectorIndex.rebuild`, which resets the index and
2787
+ * re-embeds everything.
2788
+ *
2789
+ * @remarks
2790
+ * The counterpart to {@link IMemoryStore.coverage}: coverage says **how big**
2791
+ * the gap is and costs nothing; reconcile says **what it was** and closes what
2792
+ * it can, at the cost of reading bodies and re-running the embedder. Neither
2793
+ * substitutes for the other — in particular, only reconcile can distinguish a
2794
+ * *declined* record from a *failed* one, because learning that requires calling
2795
+ * the embedder again.
2796
+ *
2797
+ * **It only touches what is missing.** For the vector lanes it asks
2798
+ * `has(target)` per record and skips the ones already held, so a repair after a
2799
+ * brief outage costs a handful of embedder calls rather than a whole vault.
2800
+ * That check is also the only way to see a record whose vector the index holds
2801
+ * but whose envelope lost its `embeddingRef` — which needs a restamp and no
2802
+ * embedder call at all, and which an `embeddingRef`-only repair cannot detect.
2803
+ *
2804
+ * **`artifact` is required and names one lane.** See {@link DerivedArtifact}
2805
+ * for why an operation repairing "everything wired" would be the wrong shape.
2806
+ *
2807
+ * **A vector lane must be wired on BOTH halves — index *and* embedder — or this
2808
+ * fails, and it is deliberately stricter than {@link IMemoryStore.coverage}.**
2809
+ * The two ask different questions of the same wiring. Coverage asks *what does
2810
+ * the index hold*, which an index alone can answer: an index wired without an
2811
+ * embedder is a legal store (queries work; writes simply do not embed), so
2812
+ * coverage reports that lane rather than pretending it is absent. Reconcile
2813
+ * asks to *produce* vectors, which needs the embedder. So a half-wired store
2814
+ * legitimately reports a coverage gap it cannot repair, and reconcile names the
2815
+ * missing half rather than returning a cheerful success with every record in
2816
+ * `failed`.
2817
+ *
2818
+ * Runs under the store's write lock, like any other mutation.
2819
+ */
2820
+ reconcile(kind: Kind, artifact: DerivedArtifact): Promise<Result<ReconcileReport>>;
2147
2821
  /**
2148
2822
  * Delete a record by `(kind, entityId)`. Non-temporal kinds physically delete
2149
2823
  * the file and return the deleted record's {@link MemoryId}. Temporal
@@ -2156,9 +2830,29 @@ export declare interface IMemoryStore {
2156
2830
 
2157
2831
  /**
2158
2832
  * Filter for {@link IMemoryStore.list}. All present fields are ANDed together.
2833
+ *
2834
+ * @remarks
2835
+ * **At least one of `scope` / `kind` / `tag` must be present.** `asOf` is a
2836
+ * temporal *projection*, not a narrowing — it collapses versions rather than
2837
+ * excluding entities — so it does not satisfy the requirement on its own. A
2838
+ * selection that narrows nothing fails with a message naming
2839
+ * {@link scanEveryRecord}.
2159
2840
  * @public
2160
2841
  */
2161
2842
  export declare interface IMemoryStoreListFilter {
2843
+ /**
2844
+ * Never present on a narrowing filter.
2845
+ *
2846
+ * @remarks
2847
+ * Exclusivity marker, paired with the `never`s on {@link IWholeVaultScan}.
2848
+ * Without it `{ scanEveryRecord: true, kind }` type-checks — TypeScript's
2849
+ * excess-property check on a union admits any property declared by *any*
2850
+ * member — and `list` then takes the scan branch and silently discards the
2851
+ * narrowing. Since the whole point of requiring a selection is that a
2852
+ * whole-vault read must be deliberate, a call that reads the whole vault while
2853
+ * *looking* narrowed is the one outcome this surface must not permit.
2854
+ */
2855
+ readonly scanEveryRecord?: never;
2162
2856
  /** Restrict to records in this scope. */
2163
2857
  readonly scope?: MemoryScopeKey;
2164
2858
  /** Restrict to records of this kind. */
@@ -2223,12 +2917,13 @@ export declare interface IMergeStrategy {
2223
2917
  }
2224
2918
 
2225
2919
  /**
2226
- * Whether an indexed entry satisfies a query's scope / kind / tag / predicate
2227
- * pre-filter (the axes shared by every v1 retriever). The `semantic` / `asOf` /
2228
- * link axes are NOT applied here — those are each retriever's own concern.
2920
+ * Whether an indexed entry satisfies a query's scope / kind / tag /
2921
+ * provenance-source / predicate pre-filter (the axes shared by every v1
2922
+ * retriever). The `semantic` / `asOf` / link axes are NOT applied here — those
2923
+ * are each retriever's own concern.
2229
2924
  * @public
2230
2925
  */
2231
- export declare function indexedRecordMatchesQuery(entry: IIndexedMemoryRecord, query: IMemoryQuery): boolean;
2926
+ export declare function indexedRecordMatchesQuery(entry: IIndexedMemoryEntry, query: IMemoryQuery): boolean;
2232
2927
 
2233
2928
  /**
2234
2929
  * How a candidate was ultimately written (or not) after resolution.
@@ -2281,6 +2976,8 @@ export declare class InMemoryCosineIndex implements IVectorIndex {
2281
2976
  static create(): Result<InMemoryCosineIndex>;
2282
2977
  /** {@inheritDoc IVectorIndex.add} */
2283
2978
  add(target: IEdgeTarget, vector: Float32Array): Promise<Result<string>>;
2979
+ /** {@inheritDoc IVectorIndex.has} */
2980
+ has(target: IEdgeTarget): Promise<Result<boolean>>;
2284
2981
  /** {@inheritDoc IVectorIndex.remove} */
2285
2982
  remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;
2286
2983
  /** {@inheritDoc IVectorIndex.query} */
@@ -2313,6 +3010,11 @@ export declare class InMemoryCosineIndex implements IVectorIndex {
2313
3010
  * A {@link MemoryEmbedder} decline is not a failure under either mode: it is
2314
3011
  * counted on {@link IVectorRebuildReport.declined} and never appears in `skipped`.
2315
3012
  *
3013
+ * **A `'fail'` failure carries the partial report on its `detail`** — the
3014
+ * rollback still runs, so that report describes the aborted attempt rather than
3015
+ * the (now empty) index. The one failure with no detail is a `list` failure,
3016
+ * which disturbs nothing and has nothing to describe.
3017
+ *
2316
3018
  * Both consumer-supplied hooks are capture-wrapped, so a `source` or `embed`
2317
3019
  * that throws or rejects becomes a `Failure` on the path above rather than an
2318
3020
  * exception escaping mid-rebuild — which would bypass the rollback entirely.
@@ -2321,7 +3023,7 @@ export declare class InMemoryCosineIndex implements IVectorIndex {
2321
3023
  * @param embed - The embedder applied to each record.
2322
3024
  * @param options - Rebuild options; omit for the historical `'fail'` behavior.
2323
3025
  */
2324
- rebuild(source: IMemoryRecordSource, embed: MemoryEmbedder, options?: IVectorRebuildOptions): Promise<Result<IVectorRebuildReport>>;
3026
+ rebuild(source: IMemoryRecordSource, embed: MemoryEmbedder, options?: IVectorRebuildOptions): Promise<DetailedResult<IVectorRebuildReport, IVectorRebuildReport>>;
2325
3027
  /** Empty the index and forget the established dimension. */
2326
3028
  private _reset;
2327
3029
  /** The Euclidean magnitude (L2 norm) of a vector. */
@@ -2379,6 +3081,8 @@ export declare class InMemoryFragmentCosineIndex implements IFragmentVectorIndex
2379
3081
  static create(): Result<InMemoryFragmentCosineIndex>;
2380
3082
  /** {@inheritDoc IFragmentVectorIndex.addFragments} */
2381
3083
  addFragments(target: IEdgeTarget, fragments: ReadonlyArray<IEmbeddedFragment>): Promise<Result<number>>;
3084
+ /** {@inheritDoc IFragmentVectorIndex.has} */
3085
+ has(target: IEdgeTarget): Promise<Result<boolean>>;
2382
3086
  /** {@inheritDoc IFragmentVectorIndex.remove} */
2383
3087
  remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;
2384
3088
  /** {@inheritDoc IFragmentVectorIndex.query} */
@@ -2402,7 +3106,8 @@ export declare class InMemoryFragmentCosineIndex implements IFragmentVectorIndex
2402
3106
  * @param source - The scope-qualified record source to re-embed.
2403
3107
  * @param embed - The fragment embedder applied to each record.
2404
3108
  */
2405
- rebuild(source: IMemoryRecordSource, embed: FragmentEmbedder): Promise<Result<number>>;
3109
+ /** {@inheritDoc IFragmentVectorIndex.rebuild} */
3110
+ rebuild(source: IMemoryRecordSource, embed: FragmentEmbedder, options?: IVectorRebuildOptions): Promise<DetailedResult<IFragmentVectorRebuildReport, IFragmentVectorRebuildReport>>;
2406
3111
  /** Empty the index and forget the established dimension. */
2407
3112
  private _reset;
2408
3113
  /** The Euclidean magnitude (L2 norm) of a vector. */
@@ -2443,6 +3148,23 @@ export declare interface IProvenance {
2443
3148
  readonly [key: string]: unknown;
2444
3149
  }
2445
3150
 
3151
+ /** A `rank` reconcile: re-run the projector and restamp what changed. @public */
3152
+ export declare interface IRankReconcileReport extends IReconcileReportBase {
3153
+ readonly artifact: 'rank';
3154
+ }
3155
+
3156
+ /** What every reconcile reports, whatever the artifact. @public */
3157
+ export declare interface IReconcileReportBase {
3158
+ /** The kind reconciled — named by the caller, so every count below is scalar. */
3159
+ readonly kind: Kind;
3160
+ /** Records of this kind considered. The denominator for everything else. */
3161
+ readonly examined: number;
3162
+ /** Records whose derived artifact was (re)produced by this call. */
3163
+ readonly repaired: number;
3164
+ /** Records that failed, with the error. A fault, never a decline. */
3165
+ readonly failed: ReadonlyArray<ISkippedVectorRecord>;
3166
+ }
3167
+
2446
3168
  /**
2447
3169
  * A candidate paired with its resolved reference id, handed to the relation
2448
3170
  * extractor so it can source edges from it.
@@ -2485,6 +3207,24 @@ export declare interface IRelationExtractor {
2485
3207
  relate(context: IRelationContext): Promise<Result<ReadonlyArray<ICandidateEdge>>>;
2486
3208
  }
2487
3209
 
3210
+ /**
3211
+ * What every index-backed retriever needs: the index it selects over, and the
3212
+ * resolver it materializes survivors through.
3213
+ *
3214
+ * @remarks
3215
+ * The resolver is the whole of the partial-read migration for a caller that
3216
+ * constructs retrievers directly — the index projection itself is invisible to
3217
+ * anyone who only consumes `retrieve()`. `FileTreeMemoryStore` implements
3218
+ * {@link IMemoryRecordResolver}, so wiring is `{ index, resolver: store }`.
3219
+ * @public
3220
+ */
3221
+ export declare interface IRetrieverCreateParams {
3222
+ /** The derived index to select over. */
3223
+ readonly index: IMemoryIndex;
3224
+ /** Materializes the selected entries' bodies. */
3225
+ readonly resolver: IMemoryRecordResolver;
3226
+ }
3227
+
2488
3228
  /**
2489
3229
  * A record paired with its scope-qualified {@link IEdgeTarget | address}, as
2490
3230
  * yielded by {@link IMemoryRecordSource.list}. The address is required because
@@ -2518,9 +3258,7 @@ export declare interface ISemanticBackend {
2518
3258
  * Construction options for {@link SemanticRetriever.create}.
2519
3259
  * @public
2520
3260
  */
2521
- export declare interface ISemanticRetrieverCreateParams {
2522
- /** The record index, used to resolve vector hits back to full records. */
2523
- readonly index: IMemoryIndex;
3261
+ export declare interface ISemanticRetrieverCreateParams extends IRetrieverCreateParams {
2524
3262
  /**
2525
3263
  * The semantic backend. When absent, the retriever reports
2526
3264
  * `supportsSemanticRecall: false` and a `query.semantic` request degrades
@@ -2573,7 +3311,7 @@ export declare function isTemporalIdentityCodec(codec: IIdentityCodec): codec is
2573
3311
  * divergence (MTM is flat yet has `entityId !== id`).
2574
3312
  * @public
2575
3313
  */
2576
- export declare function isTemporalRecord(record: IMemoryRecord<unknown>): boolean;
3314
+ export declare function isTemporalRecord(record: IEnvelopeCarrier): boolean;
2577
3315
 
2578
3316
  /**
2579
3317
  * Whether a temporal record is a *current* version — its `temporal.invalid_at`
@@ -2581,7 +3319,7 @@ export declare function isTemporalRecord(record: IMemoryRecord<unknown>): boolea
2581
3319
  * current in this sense (returns `false`).
2582
3320
  * @public
2583
3321
  */
2584
- export declare function isVersionCurrent(record: IMemoryRecord<unknown>): boolean;
3322
+ export declare function isVersionCurrent(record: IEnvelopeCarrier): boolean;
2585
3323
 
2586
3324
  /**
2587
3325
  * Whether a temporal record's validity interval contains `asOf` (epoch ms):
@@ -2590,7 +3328,13 @@ export declare function isVersionCurrent(record: IMemoryRecord<unknown>): boolea
2590
3328
  * record is never "valid at" a point (returns `false`).
2591
3329
  * @public
2592
3330
  */
2593
- export declare function isVersionValidAt(record: IMemoryRecord<unknown>, asOf: number): boolean;
3331
+ export declare function isVersionValidAt(record: IEnvelopeCarrier, asOf: number): boolean;
3332
+
3333
+ /**
3334
+ * Whether a selection is the explicit whole-vault opt-out.
3335
+ * @public
3336
+ */
3337
+ export declare function isWholeVaultScan(selection: MemoryListSelection): selection is IWholeVaultScan;
2594
3338
 
2595
3339
  /**
2596
3340
  * Optional bi-temporal validity block on an envelope. Present only on
@@ -2674,6 +3418,32 @@ export declare interface IVectorIndex {
2674
3418
  * Return the `topK` nearest records to `vector`, in descending score order.
2675
3419
  */
2676
3420
  query(vector: Float32Array, topK: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;
3421
+ /**
3422
+ * Whether this index holds a vector for the scope-qualified `target`.
3423
+ *
3424
+ * @remarks
3425
+ * On the contract because it is what makes a **targeted** repair possible, and
3426
+ * the reason is sharper than convenience: the only other way to ask *"is this
3427
+ * record indexed?"* is {@link IMemoryEnvelope.embeddingRef} — and that field is
3428
+ * the store's **belief**, which is wrong in precisely the situation a repair
3429
+ * runs in. A reopened vault backed by a fresh in-memory index carries an
3430
+ * `embeddingRef` on every record while holding no vectors at all. **A repair
3431
+ * that trusts the field it is repairing is not a repair.**
3432
+ *
3433
+ * It also makes a case *detectable* that an `embeddingRef`-only check cannot
3434
+ * see at all: the index holds the vector but the envelope lost its reference
3435
+ * (a failure swallowed after the vector was committed). That record needs its
3436
+ * reference restamped and **no embedder call**, which is only knowable by
3437
+ * asking the index.
3438
+ *
3439
+ * `Promise<Result<boolean>>`, unlike the synchronous {@link IVectorIndex.size},
3440
+ * because the two are not the same kind of accessor and should not be made to
3441
+ * look alike: `size` is a count both shipped implementations hold or can read
3442
+ * without a failure mode, while `has` on a durable backend is a keyed query
3443
+ * that can fail. Idempotent and side-effect-free; an absent target is
3444
+ * `succeed(false)`, never a failure.
3445
+ */
3446
+ has(target: IEdgeTarget): Promise<Result<boolean>>;
2677
3447
  /**
2678
3448
  * The number of vectors currently held.
2679
3449
  *
@@ -2711,8 +3481,36 @@ export declare interface IVectorIndex {
2711
3481
  *
2712
3482
  * See {@link IVectorRebuildReport} for what it reports and
2713
3483
  * {@link IVectorRebuildOptions} for the failure mode.
2714
- */
2715
- rebuild(source: IMemoryRecordSource, embed: MemoryEmbedder, options?: IVectorRebuildOptions): Promise<Result<IVectorRebuildReport>>;
3484
+ *
3485
+ * **A failure carries the report too, on the `detail`** — coverage is most
3486
+ * wanted exactly when a rebuild did not complete, and withholding it there made
3487
+ * the answer depend on the error-handling mode rather than on the question. The
3488
+ * `'fail'` contract itself is unchanged: it still resets, still aborts, still
3489
+ * returns a failure. It simply also says what it had established before it
3490
+ * stopped. See {@link IVectorRebuildReport} for how to read a report that
3491
+ * arrived on a failure — it describes the attempt, not the surviving index.
3492
+ *
3493
+ * On success the report is the **value** — that is where it belongs, and the
3494
+ * `detail` is not also populated. A failure carries no report when nothing was
3495
+ * attempted: always a `source.list()` failure, and additionally whatever
3496
+ * pre-loop step an implementation needs before it can start (the durable
3497
+ * `SqliteVecVectorIndex` must clear its table, and a failure to do so is such a
3498
+ * case). Those leave the existing index untouched, so an all-zero report would
3499
+ * describe an index the call never disturbed.
3500
+ *
3501
+ * **A rebuild does NOT re-establish the vector dimension on a persistent
3502
+ * index, and this is the one place the two shipped implementations genuinely
3503
+ * differ.** The in-memory indexes forget their dimension when they reset, so a
3504
+ * rebuild with a different-dimension embedder simply re-establishes it. A
3505
+ * `vec0`-backed index cannot: its reset is a `DELETE FROM`, the table's
3506
+ * declared dimension is part of its schema, and `vec0` has no `ALTER TABLE`.
3507
+ * So a rebuild that changes dimension **succeeds in memory and fails on
3508
+ * SQLite**, where it needs the same drop-and-re-index the package README
3509
+ * prescribes for any schema change (drop the table, or point the index at a
3510
+ * fresh `tableName`). Only embedding time is at risk — vectors are derived and
3511
+ * the vault records remain authoritative.
3512
+ */
3513
+ rebuild(source: IMemoryRecordSource, embed: MemoryEmbedder, options?: IVectorRebuildOptions): Promise<DetailedResult<IVectorRebuildReport, IVectorRebuildReport>>;
2716
3514
  }
2717
3515
 
2718
3516
  /**
@@ -2788,26 +3586,141 @@ export declare interface IVectorRebuildOptions {
2788
3586
  * What a rebuild actually did — the structural answer to "is this index complete?".
2789
3587
  *
2790
3588
  * @remarks
2791
- * A bare count cannot distinguish the three ways a record can be absent from the
2792
- * index, and that distinction is the entire point: **`declined` was intentional,
2793
- * `skipped` was a fault, and neither is the same as "never attempted"**. A caller
2794
- * deriving coverage from a count alone cannot tell an embedder outage from a
2795
- * deliberate policy, which is precisely the confusion this type exists to end.
3589
+ * A bare count cannot distinguish the ways a record can be absent from the index,
3590
+ * and that distinction is the entire point: **`declined` was intentional,
3591
+ * `excluded` was never offered, `skipped` was a fault, and none of them is the
3592
+ * same as "never attempted"**. A caller deriving coverage from a count alone
3593
+ * cannot tell an embedder outage from a deliberate policy, which is precisely the
3594
+ * confusion this type exists to end.
3595
+ *
3596
+ * **Every count in this report is resolved by kind.** A coverage report exists to
3597
+ * answer *"is my coverage what I intended?"*, and a bare total cannot:
3598
+ * `indexed: 500` reads identically whether the right kinds were indexed or a
3599
+ * policy drift silently redirected coverage, and the same is true of every other
3600
+ * count here. Totals are derivable by summing; the per-kind breakdown is not
3601
+ * derivable from anything else — {@link IVectorQueryHit} carries no `kind`,
3602
+ * {@link IVectorIndex.query} answers "what is near this" rather than "what is in
3603
+ * here", and {@link IVectorIndex.size} is a scalar, so the index cannot be
3604
+ * interrogated after the fact for any of them. **A new count added to this report
3605
+ * is resolved by kind unless there is a stated reason it cannot be.**
3606
+ *
3607
+ * `indexed` is the count most tempting to leave bare and the most dangerous to,
3608
+ * because it is the number a coverage surface actually renders: 500 bookkeeping
3609
+ * rows and zero knowledge rows is a healthy-looking number for a catastrophically
3610
+ * broken index.
3611
+ *
3612
+ * **Reading a report that arrived on a failure.** Under
3613
+ * {@link VectorRebuildErrorMode | `onRecordError: 'fail'`} the report is handed
3614
+ * back on the failure's `detail` — *after* the rollback has already run. It
3615
+ * describes the attempt, not the surviving index: `indexed` names what had been
3616
+ * established when the rebuild stopped, and the index itself now holds nothing. It
3617
+ * is a diagnostic ("we were 340 knowledge rows in when the embedder died"), not a
3618
+ * coverage statement. Only a report from a **successful** rebuild describes what
3619
+ * the index holds.
2796
3620
  * @public
2797
3621
  */
2798
3622
  export declare interface IVectorRebuildReport {
2799
- /** Records embedded and added to the index. */
2800
- readonly indexed: number;
2801
- /** Records the embedder deliberately declined (resolved `undefined`). */
2802
- readonly declined: number;
3623
+ /**
3624
+ * Records embedded and added to the index, counted by {@link Kind}.
3625
+ *
3626
+ * @remarks
3627
+ * A count of successful `add` calls, so it lines up with its per-record
3628
+ * siblings and the buckets sum back to the listing. It is deliberately **not**
3629
+ * read back off {@link IVectorIndex.size} at the end, which no implementation
3630
+ * could resolve by kind anyway. The trade that makes: a `source` that lists the
3631
+ * same `(scope, id)` twice contributes twice here while the index holds one
3632
+ * vector, where a size read would have self-corrected. A source that does that
3633
+ * is malformed, and a total that silently disagreed with the per-kind
3634
+ * breakdown would be the worse failure.
3635
+ */
3636
+ readonly indexed: ReadonlyMap<Kind, number>;
3637
+ /**
3638
+ * Records the embedder deliberately declined (resolved `undefined`), counted by
3639
+ * {@link Kind}. The embedder was called and answered — contrast `excluded`,
3640
+ * where it never was.
3641
+ */
3642
+ readonly declined: ReadonlyMap<Kind, number>;
3643
+ /**
3644
+ * Records the `source` filtered out before the rebuild ever saw them, counted by
3645
+ * {@link Kind} — for a store-backed source, the kinds outside
3646
+ * {@link IMemoryStore.embedsKind | embedsKind}.
3647
+ *
3648
+ * **Optional, and the optionality is semantic rather than cosmetic**: it is the
3649
+ * one count a rebuild genuinely cannot know for itself, because the decision is
3650
+ * made upstream in the source. `undefined` means *this source does not report
3651
+ * exclusions* — distinct from an empty map, which means *this source reports
3652
+ * them and excluded nothing*. `indexed` and `declined` are knowable by
3653
+ * construction (the rebuild either added the vector or the embedder answered)
3654
+ * and so are never optional.
3655
+ */
3656
+ readonly excluded?: ReadonlyMap<Kind, number>;
2803
3657
  /**
2804
3658
  * Records whose embedding or add FAILED and were skipped. Non-empty only under
2805
3659
  * {@link VectorRebuildErrorMode | `onRecordError: 'skip'`} — under `'fail'` the
2806
- * first failure aborts the rebuild and no report is returned at all.
3660
+ * first failure aborts the rebuild, so a `'fail'` report names the casualty in
3661
+ * its failure message rather than here.
3662
+ *
3663
+ * Per-record and carrying the error, so it already implies the per-kind
3664
+ * breakdown the counts above spell out; that is the stated reason this one field
3665
+ * is not a `ReadonlyMap<Kind, number>`.
2807
3666
  */
2808
3667
  readonly skipped: ReadonlyArray<ISkippedVectorRecord>;
2809
3668
  }
2810
3669
 
3670
+ /**
3671
+ * A record-vector reconcile.
3672
+ * @public
3673
+ */
3674
+ export declare interface IVectorReconcileReport extends IReconcileReportBase {
3675
+ readonly artifact: 'record-vector';
3676
+ /**
3677
+ * Records the index already held and which needed **no embedder call** — the
3678
+ * whole point of a targeted repair, and the number that shows what it saved
3679
+ * against a `rebuild`.
3680
+ */
3681
+ readonly alreadyIndexed: number;
3682
+ /**
3683
+ * Records whose vector the index held but whose envelope had **lost its
3684
+ * `embeddingRef`** — repaired by restamping the reference, with no embedder
3685
+ * call.
3686
+ *
3687
+ * @remarks
3688
+ * This case is invisible to an `embeddingRef`-only check, which is one of the
3689
+ * two reasons `IVectorIndex.has` is on the contract: a reference-absent record
3690
+ * looks identical to a never-embedded one until you ask the index.
3691
+ */
3692
+ readonly restamped: number;
3693
+ /** Records the embedder intentionally declined. Not a failure, not a gap. */
3694
+ readonly declined: number;
3695
+ }
3696
+
3697
+ /**
3698
+ * The named, deliberately uncomfortable opt-out from
3699
+ * {@link IMemoryStore.list}'s narrowing requirement — build one with
3700
+ * {@link scanEveryRecord}.
3701
+ * @public
3702
+ */
3703
+ export declare interface IWholeVaultScan {
3704
+ /** Discriminator. Always `true`; produced only by {@link scanEveryRecord}. */
3705
+ readonly scanEveryRecord: true;
3706
+ /**
3707
+ * Never present on a whole-vault scan — see
3708
+ * {@link IMemoryStoreListFilter.scanEveryRecord} for why these markers exist.
3709
+ * A scan that also carried a narrowing axis would have that axis dropped.
3710
+ */
3711
+ readonly scope?: never;
3712
+ /** Never present on a whole-vault scan. See {@link IWholeVaultScan.scope}. */
3713
+ readonly kind?: never;
3714
+ /** Never present on a whole-vault scan. See {@link IWholeVaultScan.scope}. */
3715
+ readonly tag?: never;
3716
+ /**
3717
+ * Optional temporal projection, exactly as on {@link IMemoryStoreListFilter}.
3718
+ * NOT excluded, because `asOf` projects rather than narrows and composes with
3719
+ * a whole-vault read exactly as it does with a filtered one.
3720
+ */
3721
+ readonly asOf?: number;
3722
+ }
3723
+
2811
3724
  /**
2812
3725
  * A per-kind write policy, injected at store construction. Invoked AFTER
2813
3726
  * content-hash dedup (dedup is always pre-policy). The policy decides
@@ -2979,6 +3892,13 @@ export declare class KnowledgeLwwPolicy implements IWritePolicy {
2979
3892
  private _rebuild;
2980
3893
  }
2981
3894
 
3895
+ /**
3896
+ * The {@link limitRecords} window, applied to entries — used on the
3897
+ * no-body-filter path so paging happens before anything is read.
3898
+ * @public
3899
+ */
3900
+ export declare function limitEntries(entries: ReadonlyArray<IIndexedMemoryEntry>, limit?: number, offset?: number): ReadonlyArray<IIndexedMemoryEntry>;
3901
+
2982
3902
  /**
2983
3903
  * Apply the `{ offset, limit }` page window to an ordered record set. Applied
2984
3904
  * last, after ordering, so it always takes a stable window of the ordered
@@ -3029,28 +3949,22 @@ export declare const LINK_TRAVERSAL_UNWIRED_MESSAGE: string;
3029
3949
  * is the exact, collision-free cycle key — no structural hashing (e.g.
3030
3950
  * `Crc32Normalizer`) is needed. A self-loop or any multi-hop cycle terminates
3031
3951
  * because a revisited node is never re-expanded.
3032
- * - **Post-filter.** The scope / kind / tag / predicate axes of the query are
3952
+ * - **Post-filter.** The scope / kind / tag / provenance-source / predicate axes of the query are
3033
3953
  * applied to the reached records (the link axes are the traversal itself).
3034
3954
  * @public
3035
3955
  */
3036
3956
  export declare class LinkTraversalRetriever implements IMemoryRetriever {
3037
3957
  private readonly _index;
3958
+ private readonly _resolver;
3038
3959
  private constructor();
3039
3960
  /** Family-convention factory. */
3040
- static create(index: IMemoryIndex): Result<LinkTraversalRetriever>;
3961
+ static create(params: IRetrieverCreateParams): Result<LinkTraversalRetriever>;
3041
3962
  /** {@inheritDoc IMemoryRetriever.capabilities} */
3042
3963
  get capabilities(): IMemoryRetrieverCapabilities;
3043
3964
  /** {@inheritDoc IMemoryRetriever.retrieve} */
3044
3965
  retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
3045
3966
  /** Run the bounded, cycle-safe BFS and post-filter the reached records. */
3046
3967
  private _traverse;
3047
- /**
3048
- * Group the index's entries by their scope-qualified {@link edgeTargetKey}
3049
- * `(scope, id)` composite. Each composite is the index's primary key, so it maps
3050
- * to exactly one entry — two records that reuse a filename stem across scopes
3051
- * (e.g. `turn-0` in two conversations) get distinct keys and never collide.
3052
- */
3053
- private _indexByKey;
3054
3968
  /** Outbound neighbors: the scope-qualified targets of every edge on the record at `node`. */
3055
3969
  private _outbound;
3056
3970
  /** Inbound neighbors: the scope-qualified sources whose edges point AT `node` (the backlinks). */
@@ -3090,6 +4004,45 @@ export declare class LtmIdentityCodec implements IIdentityCodec {
3090
4004
  verifyRoundTrip(scope: MemoryScopeKey, stem: string): Result<true>;
3091
4005
  }
3092
4006
 
4007
+ /**
4008
+ * Materialize entries through the resolver, dropping any that have vanished
4009
+ * since selection — a concurrent delete between selecting an envelope and
4010
+ * reading its body is a legitimate race and yields a shorter list, not an error.
4011
+ * A read that FAILS is a real fault and propagates.
4012
+ * @public
4013
+ */
4014
+ export declare function materializeEntries(entries: ReadonlyArray<IIndexedMemoryEntry>, resolver: IMemoryRecordResolver): Result<ReadonlyArray<IMemoryRecord<unknown>>>;
4015
+
4016
+ /**
4017
+ * Materialize a selected, ordered page — **applying `query.filter` and paging in
4018
+ * the right order**, which is the whole reason this is shared rather than
4019
+ * open-coded per retriever.
4020
+ *
4021
+ * @remarks
4022
+ * Two paths, and the choice is forced by where the predicate can run:
4023
+ *
4024
+ * - **No `filter`** — order and page over *envelopes*, then read only the page.
4025
+ * `limit` bounds the READ, not just the result.
4026
+ * - **With `filter`** — the predicate takes a whole record, so every
4027
+ * envelope-survivor must be read first, then filtered, then paged. Paging
4028
+ * before filtering would return fewer than `limit` rows for no reason a caller
4029
+ * could see.
4030
+ *
4031
+ * **Every retriever must route through this.** `indexedRecordMatchesQuery`
4032
+ * structurally *cannot* apply `filter` — it is handed an envelope — so a
4033
+ * retriever that pre-filters with it and then materializes on its own silently
4034
+ * ignores the predicate. That regression shipped once, in the stream that moved
4035
+ * `filter` out of the pre-filter; this function exists so it cannot recur.
4036
+ *
4037
+ * @param selected - Entries surviving the envelope pre-filter.
4038
+ * @param query - The query whose `filter` / `orderBy` / `limit` / `offset` apply.
4039
+ * @param resolver - Body resolver.
4040
+ * @param order - Applied to whichever collection is paged; identity is legal for
4041
+ * a retriever whose ordering is intrinsic (semantic score, traversal order).
4042
+ * @public
4043
+ */
4044
+ export declare function materializePage<T extends IIndexedMemoryEntry>(selected: ReadonlyArray<T>, query: IMemoryQuery, resolver: IMemoryRecordResolver, order?: (candidates: ReadonlyArray<T>) => ReadonlyArray<T>): Result<ReadonlyArray<IMemoryRecord<unknown>>>;
4045
+
3093
4046
  /**
3094
4047
  * Bounded-ring write policy for the experience (memory) kind families.
3095
4048
  * Admission accepts until `maxRecords` is reached, then evicts the oldest
@@ -3249,7 +4202,10 @@ export declare type MemoryId = Brand<string, 'MemoryId'>;
3249
4202
  * @public
3250
4203
  */
3251
4204
  export declare class MemoryIndex implements IMemoryIndex {
3252
- /** Primary store: `(scope, id)` composite key → indexed entry. */
4205
+ /**
4206
+ * Primary store: `(scope, id)` composite key → indexed entry. Holds the
4207
+ * PROJECTED form, so the index never retains a body.
4208
+ */
3253
4209
  private readonly _byKey;
3254
4210
  /** kind → set of composite keys. */
3255
4211
  private readonly _byKind;
@@ -3276,19 +4232,21 @@ export declare class MemoryIndex implements IMemoryIndex {
3276
4232
  */
3277
4233
  private static _keyOf;
3278
4234
  /** {@inheritDoc IMemoryIndex.rebuild} */
3279
- rebuild(entries: ReadonlyArray<IIndexedMemoryRecord>): Result<number>;
4235
+ rebuild(entries: ReadonlyArray<IIndexedMemoryEntry>): Result<number>;
3280
4236
  /** {@inheritDoc IMemoryIndex.patch} */
3281
4237
  patch(op: MemoryIndexPatchOp, entry: IIndexedMemoryRecord): Result<IIndexedMemoryRecord>;
3282
4238
  /** {@inheritDoc IMemoryIndex.entries} */
3283
- entries(): ReadonlyArray<IIndexedMemoryRecord>;
4239
+ entries(): ReadonlyArray<IIndexedMemoryEntry>;
4240
+ /** {@inheritDoc IMemoryIndex.get} */
4241
+ get(target: IEdgeTarget): IIndexedMemoryEntry | undefined;
3284
4242
  /** {@inheritDoc IMemoryIndex.byKind} */
3285
- byKind(kind: Kind): ReadonlyArray<IMemoryRecord<unknown>>;
4243
+ byKind(kind: Kind): ReadonlyArray<IIndexedMemoryEntry>;
3286
4244
  /** {@inheritDoc IMemoryIndex.byTag} */
3287
- byTag(tag: Tag): ReadonlyArray<IMemoryRecord<unknown>>;
4245
+ byTag(tag: Tag): ReadonlyArray<IIndexedMemoryEntry>;
3288
4246
  /** {@inheritDoc IMemoryIndex.byRecency} */
3289
- byRecency(): ReadonlyArray<IMemoryRecord<unknown>>;
4247
+ byRecency(): ReadonlyArray<IIndexedMemoryEntry>;
3290
4248
  /** {@inheritDoc IMemoryIndex.byRank} */
3291
- byRank(): ReadonlyArray<IMemoryRecord<unknown>>;
4249
+ byRank(): ReadonlyArray<IIndexedMemoryEntry>;
3292
4250
  /** {@inheritDoc IMemoryIndex.backlinks} */
3293
4251
  backlinks(target: IEdgeTarget): ReadonlyArray<IEdgeTarget>;
3294
4252
  /**
@@ -3306,6 +4264,8 @@ export declare class MemoryIndex implements IMemoryIndex {
3306
4264
  * approach; the sort is over the in-memory index, never a filesystem walk.
3307
4265
  */
3308
4266
  private _rankOrdered;
4267
+ /** Resolve composite keys to their entries, skipping any that are absent. */
4268
+ private static _resolve;
3309
4269
  /**
3310
4270
  * Rank-descending comparator with an absent-`rank`-last rule and a recency
3311
4271
  * (`updated`, then `seq`) tiebreak. Duplicated from the retrieve packlet's
@@ -3508,6 +4468,13 @@ export declare class MemoryIngestOrchestrator implements IMemoryIngestOrchestrat
3508
4468
  private static _asStringBody;
3509
4469
  }
3510
4470
 
4471
+ /**
4472
+ * What {@link IMemoryStore.list} accepts: a narrowing filter, or the explicit
4473
+ * whole-vault scan.
4474
+ * @public
4475
+ */
4476
+ export declare type MemoryListSelection = IMemoryStoreListFilter | IWholeVaultScan;
4477
+
3511
4478
  /**
3512
4479
  * The outcome of the observed operation.
3513
4480
  * @public
@@ -3717,7 +4684,7 @@ export declare const NON_SEMANTIC_CAPABILITIES: IMemoryRetrieverCapabilities;
3717
4684
  * default, byte-identical to the pre-`orderBy` behavior).
3718
4685
  * @public
3719
4686
  */
3720
- export declare function orderingCompare(orderBy?: IMemoryQuery['orderBy']): (a: IMemoryRecord<unknown>, b: IMemoryRecord<unknown>) => number;
4687
+ export declare function orderingCompare(orderBy?: IMemoryQuery['orderBy']): (a: IEnvelopeCarrier, b: IEnvelopeCarrier) => number;
3721
4688
 
3722
4689
  /**
3723
4690
  * Parse a complete memory file into a typed {@link IMemoryRecord}. Splits the
@@ -3757,7 +4724,7 @@ export declare type QueryEmbedder = (text: string) => Promise<Result<Float32Arra
3757
4724
  * index's rank-view ordering.
3758
4725
  * @public
3759
4726
  */
3760
- export declare function rankCompare(a: IMemoryRecord<unknown>, b: IMemoryRecord<unknown>): number;
4727
+ export declare function rankCompare(a: IEnvelopeCarrier, b: IEnvelopeCarrier): number;
3761
4728
 
3762
4729
  /**
3763
4730
  * A per-kind host projection from a fully-resolved (post-merge) memory record
@@ -3767,6 +4734,9 @@ export declare function rankCompare(a: IMemoryRecord<unknown>, b: IMemoryRecord<
3767
4734
  * {@link IMemoryEnvelope.rank}. The store never interprets the body — the host
3768
4735
  * owns what the number means. A projector that throws is treated as "no rank
3769
4736
  * for this record" (logged at `warn`), never failing the write.
4737
+ *
4738
+ * Runs on writes only. To apply a newly-registered projector to records that
4739
+ * already exist, call `IMemoryStore.reconcile(kind, 'rank')`.
3770
4740
  * @public
3771
4741
  */
3772
4742
  export declare type RankProjector = (record: IMemoryRecord<unknown>) => number;
@@ -3776,25 +4746,32 @@ export declare type RankProjector = (record: IMemoryRecord<unknown>) => number;
3776
4746
  * equal-`updated` records sort deterministically. Mirrors the B1 index ordering.
3777
4747
  * @public
3778
4748
  */
3779
- export declare function recencyCompare(a: IMemoryRecord<unknown>, b: IMemoryRecord<unknown>): number;
4749
+ export declare function recencyCompare(a: IEnvelopeCarrier, b: IEnvelopeCarrier): number;
3780
4750
 
3781
4751
  /**
3782
- * Returns records matching the query's scope / kind / tag / predicate filters,
4752
+ * Returns records matching the query's scope / kind / tag / provenance-source / predicate filters,
3783
4753
  * ordered most-recently-updated first. The universal v1 retriever — an empty
3784
4754
  * query returns the whole vault in recency order.
3785
4755
  * @public
3786
4756
  */
3787
4757
  export declare class RecencyRetriever implements IMemoryRetriever {
3788
4758
  private readonly _index;
4759
+ private readonly _resolver;
3789
4760
  private constructor();
3790
4761
  /** {@inheritDoc IMemoryRetriever.capabilities} */
3791
4762
  get capabilities(): IMemoryRetrieverCapabilities;
3792
4763
  /** Family-convention factory. */
3793
- static create(index: IMemoryIndex): Result<RecencyRetriever>;
4764
+ static create(params: IRetrieverCreateParams): Result<RecencyRetriever>;
3794
4765
  /** {@inheritDoc IMemoryRetriever.retrieve} */
3795
4766
  retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
3796
4767
  }
3797
4768
 
4769
+ /**
4770
+ * What a {@link IMemoryStore.reconcile} established, discriminated by artifact.
4771
+ * @public
4772
+ */
4773
+ export declare type ReconcileReport = IRankReconcileReport | IVectorReconcileReport | IFragmentReconcileReport;
4774
+
3798
4775
  /**
3799
4776
  * The four dedup verdicts a {@link IEntityResolver} (or fgv's exact-match layer)
3800
4777
  * returns for a candidate. See the design note §3 for the verdict → write
@@ -3816,6 +4793,53 @@ export declare type ResolutionVerdict = {
3816
4793
  readonly target: IEdgeTarget;
3817
4794
  };
3818
4795
 
4796
+ /**
4797
+ * The shared select → order → page → materialize pipeline every non-semantic
4798
+ * retriever runs, and the one place the body-vs-envelope ordering decision lives.
4799
+ *
4800
+ * @public
4801
+ * @remarks
4802
+ * **Without `query.filter`, ordering and paging happen over envelopes and only
4803
+ * the page is materialized** — so `limit` genuinely bounds the number of records
4804
+ * read, not merely the number returned. That is what makes `limit` a legitimate
4805
+ * narrowing axis rather than a loophole, and it is conditional on the ordering
4806
+ * key being an envelope field: both shipped comparators (`recencyCompare`,
4807
+ * `rankCompare`) read `updated` / `seq` / `rank` and qualify. **An ordering that
4808
+ * keyed on a body field could not use this path** and would have to sort after
4809
+ * materialization, at which point `limit` bounds the result and not the read.
4810
+ *
4811
+ * **With `query.filter`, every envelope-survivor must be materialized first**,
4812
+ * because the predicate takes a whole record. Semantics are preserved exactly;
4813
+ * the cost is not. Pair `filter` with an envelope axis when the read cost
4814
+ * matters.
4815
+ */
4816
+ export declare function resolveQuery(entries: ReadonlyArray<IIndexedMemoryEntry>, query: IMemoryQuery, resolver: IMemoryRecordResolver): Result<ReadonlyArray<IMemoryRecord<unknown>>>;
4817
+
4818
+ /**
4819
+ * Ask {@link IMemoryStore.list} for **every record in the vault**, bodies
4820
+ * included.
4821
+ *
4822
+ * @remarks
4823
+ * `list` requires a selection and rejects one that narrows nothing, so this is
4824
+ * the only way to get the whole vault — and that is the point. Since the index
4825
+ * holds envelopes only, `list` materializes every survivor from storage, so an
4826
+ * unnarrowed call reads one file per record. Making it impossible to write by
4827
+ * accident is worth more than making it fast.
4828
+ *
4829
+ * It is named for what it costs rather than for what it returns, and it is
4830
+ * greppable: `scanEveryRecord` enumerates every whole-vault read in a codebase in
4831
+ * one search. This mirrors `@fgv/ts-extras`' `safer-fetch`, where `addressGuard`
4832
+ * is required with no default and `allowAnyAddress()` is the named opt-out.
4833
+ *
4834
+ * **If you only need to select, you do not need this.** Use
4835
+ * {@link IMemoryStore.listEntries}, which returns every entry's scope and
4836
+ * envelope, reads no files, and needs no selection.
4837
+ * @public
4838
+ */
4839
+ export declare function scanEveryRecord(options?: {
4840
+ readonly asOf?: number;
4841
+ }): IWholeVaultScan;
4842
+
3819
4843
  /**
3820
4844
  * The reference {@link IMergeStrategy}: a score-union. Every record is scored by
3821
4845
  * the number of composed result sets it appears in (deduplicated by
@@ -3837,11 +4861,12 @@ export declare class ScoreUnionMergeStrategy implements IMergeStrategy {
3837
4861
  }
3838
4862
 
3839
4863
  /**
3840
- * Apply the shared scope / kind / tag / predicate pre-filter to a set of indexed
3841
- * entries, returning the surviving records (unordered, unlimited).
4864
+ * Apply the shared scope / kind / tag / provenance-source / predicate pre-filter
4865
+ * to a set of indexed entries, returning the surviving records (unordered,
4866
+ * unlimited).
3842
4867
  * @public
3843
4868
  */
3844
- export declare function selectByQuery(entries: ReadonlyArray<IIndexedMemoryRecord>, query: IMemoryQuery): IMemoryRecord<unknown>[];
4869
+ export declare function selectByQuery(entries: ReadonlyArray<IIndexedMemoryEntry>, query: IMemoryQuery): IIndexedMemoryEntry[];
3845
4870
 
3846
4871
  /**
3847
4872
  * Select the current version from a set of an entity's versions: the newest
@@ -3849,7 +4874,7 @@ export declare function selectByQuery(entries: ReadonlyArray<IIndexedMemoryRecor
3849
4874
  * entity has no current version (fully invalidated / soft-deleted, or empty).
3850
4875
  * @public
3851
4876
  */
3852
- export declare function selectCurrentVersion(versions: ReadonlyArray<IMemoryRecord<unknown>>): IMemoryRecord<unknown> | undefined;
4877
+ export declare function selectCurrentVersion<T extends IEnvelopeCarrier>(versions: ReadonlyArray<T>): T | undefined;
3853
4878
 
3854
4879
  /**
3855
4880
  * Select the version of an entity valid at `asOf` (epoch ms): the newest
@@ -3857,7 +4882,7 @@ export declare function selectCurrentVersion(versions: ReadonlyArray<IMemoryReco
3857
4882
  * when no version was valid at that instant.
3858
4883
  * @public
3859
4884
  */
3860
- export declare function selectVersionAsOf(versions: ReadonlyArray<IMemoryRecord<unknown>>, asOf: number): IMemoryRecord<unknown> | undefined;
4885
+ export declare function selectVersionAsOf<T extends IEnvelopeCarrier>(versions: ReadonlyArray<T>, asOf: number): T | undefined;
3861
4886
 
3862
4887
  /**
3863
4888
  * The loud-degradation message a retriever returns when `query.semantic` is set
@@ -3880,6 +4905,7 @@ export declare const SEMANTIC_UNWIRED_MESSAGE: string;
3880
4905
  */
3881
4906
  export declare class SemanticRetriever implements IMemoryRetriever {
3882
4907
  private readonly _index;
4908
+ private readonly _resolver;
3883
4909
  private readonly _backend;
3884
4910
  private constructor();
3885
4911
  /** {@inheritDoc IMemoryRetriever.capabilities} */
@@ -3921,19 +4947,42 @@ export declare function splitFrontmatter(raw: string): Result<IMemoryFileParts>;
3921
4947
  export declare type StoreStampedEnvelopeField = 'id' | 'seq' | 'contentHash' | 'created' | 'updated';
3922
4948
 
3923
4949
  /**
3924
- * Returns records matching `query.filter`, narrowed by any scope / kind / tag
3925
- * pre-filter and recency-ordered. The predicate is this retriever's axis: a
3926
- * query without a `filter` is not its concern and yields an empty success (so it
3927
- * contributes nothing to a {@link HybridRetriever}, rather than failing).
4950
+ * Returns records matching `query.filter` or `query.provenanceSource`, narrowed
4951
+ * by any scope / kind / tag / provenance-source pre-filter and recency-ordered.
4952
+ * Those two are this retriever's axes: a query carrying neither is not its
4953
+ * concern and yields an empty success (so it contributes nothing to a
4954
+ * {@link HybridRetriever}, rather than failing).
4955
+ *
4956
+ * Note `provenanceSource` appears on both sides of that sentence, and the
4957
+ * duplication is real rather than sloppy: it is one of this retriever's two
4958
+ * *dispatch* axes (it decides whether the query is this retriever's concern at
4959
+ * all) **and** a member of the shared *pre-filter* (it narrows the result set,
4960
+ * as it does for every other retriever). `filter` is likewise both.
4961
+ *
4962
+ * @remarks
4963
+ * `provenanceSource` is *applied* by the shared pre-filter, so every retriever
4964
+ * narrows by it. What this retriever adds is *answering* a query whose only axis
4965
+ * is `provenanceSource` — the "show me everything this source produced" request,
4966
+ * which would otherwise fall through the `filter`-absent guard and come back
4967
+ * empty.
4968
+ *
4969
+ * Consequently, inside a {@link HybridRetriever} composed with the universal
4970
+ * {@link RecencyRetriever}, a `provenanceSource`-only query is answered by both
4971
+ * children and every matching record scores twice under a score-union merge.
4972
+ * That is the established behavior for a dedicated-axis retriever composed with
4973
+ * the universal one — {@link TagRetriever} double-scores a `tag`-only query the
4974
+ * same way — and is intentional here, not an artifact of grafting a second axis
4975
+ * onto a retriever whose original concern was arbitrary predicates.
3928
4976
  * @public
3929
4977
  */
3930
4978
  export declare class StructuredFilterRetriever implements IMemoryRetriever {
3931
4979
  private readonly _index;
4980
+ private readonly _resolver;
3932
4981
  private constructor();
3933
4982
  /** {@inheritDoc IMemoryRetriever.capabilities} */
3934
4983
  get capabilities(): IMemoryRetrieverCapabilities;
3935
4984
  /** Family-convention factory. */
3936
- static create(index: IMemoryIndex): Result<StructuredFilterRetriever>;
4985
+ static create(params: IRetrieverCreateParams): Result<StructuredFilterRetriever>;
3937
4986
  /** {@inheritDoc IMemoryRetriever.retrieve} */
3938
4987
  retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
3939
4988
  }
@@ -3946,18 +4995,19 @@ export declare type Tag = Brand<string, 'Tag'>;
3946
4995
 
3947
4996
  /**
3948
4997
  * Returns records carrying `query.tag`, recency-ordered within the tag and
3949
- * narrowed by any scope / kind / predicate filters. Tag is this retriever's
4998
+ * narrowed by any scope / kind / provenance-source / predicate filters. Tag is this retriever's
3950
4999
  * axis: a query without a `tag` is not its concern and yields an empty success
3951
5000
  * (so it contributes nothing to a {@link HybridRetriever}, rather than failing).
3952
5001
  * @public
3953
5002
  */
3954
5003
  export declare class TagRetriever implements IMemoryRetriever {
3955
5004
  private readonly _index;
5005
+ private readonly _resolver;
3956
5006
  private constructor();
3957
5007
  /** {@inheritDoc IMemoryRetriever.capabilities} */
3958
5008
  get capabilities(): IMemoryRetrieverCapabilities;
3959
5009
  /** Family-convention factory. */
3960
- static create(index: IMemoryIndex): Result<TagRetriever>;
5010
+ static create(params: IRetrieverCreateParams): Result<TagRetriever>;
3961
5011
  /** {@inheritDoc IMemoryRetriever.retrieve} */
3962
5012
  retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
3963
5013
  }