@fgv/ts-agent-memory 5.1.0-39 → 5.1.0-41

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 (210) hide show
  1. package/.rush/temp/{b82cf6bdece20481260e6bab946179eeec9d7b46.tar.log → cbbdbe09515171b4eba8f2592be2dace1e4e8142.tar.log} +14 -2
  2. package/.rush/temp/chunked-rush-logs/ts-agent-memory.build.chunks.jsonl +2 -2
  3. package/.rush/temp/operation/build/all.log +2 -2
  4. package/.rush/temp/operation/build/log-chunks.jsonl +2 -2
  5. package/.rush/temp/operation/build/state.json +1 -1
  6. package/dist/packlets/converters/envelopeConverter.js +17 -3
  7. package/dist/packlets/converters/envelopeConverter.js.map +1 -1
  8. package/dist/packlets/index/memoryIndex.js +58 -10
  9. package/dist/packlets/index/memoryIndex.js.map +1 -1
  10. package/dist/packlets/ingest/cycleGuard.js +13 -6
  11. package/dist/packlets/ingest/cycleGuard.js.map +1 -1
  12. package/dist/packlets/ingest/hostStages.js.map +1 -1
  13. package/dist/packlets/ingest/model.js.map +1 -1
  14. package/dist/packlets/ingest/orchestrator.js +94 -49
  15. package/dist/packlets/ingest/orchestrator.js.map +1 -1
  16. package/dist/packlets/retrieve/hybridRetriever.js +13 -2
  17. package/dist/packlets/retrieve/hybridRetriever.js.map +1 -1
  18. package/dist/packlets/retrieve/linkTraversalRetriever.js +46 -57
  19. package/dist/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
  20. package/dist/packlets/retrieve/recencyRetriever.js +3 -3
  21. package/dist/packlets/retrieve/recencyRetriever.js.map +1 -1
  22. package/dist/packlets/retrieve/retriever.js +50 -7
  23. package/dist/packlets/retrieve/retriever.js.map +1 -1
  24. package/dist/packlets/retrieve/semanticRetriever.js +9 -3
  25. package/dist/packlets/retrieve/semanticRetriever.js.map +1 -1
  26. package/dist/packlets/retrieve/structuredFilterRetriever.js +3 -3
  27. package/dist/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
  28. package/dist/packlets/retrieve/tagRetriever.js +3 -3
  29. package/dist/packlets/retrieve/tagRetriever.js.map +1 -1
  30. package/dist/packlets/retrieve/temporalRetrievers.js +3 -3
  31. package/dist/packlets/retrieve/temporalRetrievers.js.map +1 -1
  32. package/dist/packlets/store/fileTreeMemoryStore.js +72 -16
  33. package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -1
  34. package/dist/packlets/tools/memoryTools.js +104 -21
  35. package/dist/packlets/tools/memoryTools.js.map +1 -1
  36. package/dist/packlets/types/envelope.js +13 -1
  37. package/dist/packlets/types/envelope.js.map +1 -1
  38. package/dist/packlets/vector/inMemoryCosineIndex.js +22 -17
  39. package/dist/packlets/vector/inMemoryCosineIndex.js.map +1 -1
  40. package/dist/packlets/vector/vectorIndex.js.map +1 -1
  41. package/dist/test/unit/converters/antagonistRoundTrip.test.js +3 -3
  42. package/dist/test/unit/converters/antagonistRoundTrip.test.js.map +1 -1
  43. package/dist/test/unit/converters/envelopeConverter.test.js +125 -8
  44. package/dist/test/unit/converters/envelopeConverter.test.js.map +1 -1
  45. package/dist/test/unit/index/memoryIndex.test.js +87 -25
  46. package/dist/test/unit/index/memoryIndex.test.js.map +1 -1
  47. package/dist/test/unit/ingest/antagonistCycleAndParity.test.js +20 -16
  48. package/dist/test/unit/ingest/antagonistCycleAndParity.test.js.map +1 -1
  49. package/dist/test/unit/ingest/cycleGuard.test.js +28 -1
  50. package/dist/test/unit/ingest/cycleGuard.test.js.map +1 -1
  51. package/dist/test/unit/ingest/orchestrator.test.js +187 -45
  52. package/dist/test/unit/ingest/orchestrator.test.js.map +1 -1
  53. package/dist/test/unit/retrieve/linkTraversalRetriever.test.js +106 -31
  54. package/dist/test/unit/retrieve/linkTraversalRetriever.test.js.map +1 -1
  55. package/dist/test/unit/retrieve/retrievers.test.js +299 -37
  56. package/dist/test/unit/retrieve/retrievers.test.js.map +1 -1
  57. package/dist/test/unit/store/embedOnWrite.test.js +69 -12
  58. package/dist/test/unit/store/embedOnWrite.test.js.map +1 -1
  59. package/dist/test/unit/store/listScoped.test.js +109 -0
  60. package/dist/test/unit/store/listScoped.test.js.map +1 -0
  61. package/dist/test/unit/store/rankAxis.test.js +254 -0
  62. package/dist/test/unit/store/rankAxis.test.js.map +1 -0
  63. package/dist/test/unit/tools/memoryTools.test.js +280 -11
  64. package/dist/test/unit/tools/memoryTools.test.js.map +1 -1
  65. package/dist/test/unit/types/writePolicy.test.js +9 -2
  66. package/dist/test/unit/types/writePolicy.test.js.map +1 -1
  67. package/dist/test/unit/vector/inMemoryCosineIndex.test.js +95 -35
  68. package/dist/test/unit/vector/inMemoryCosineIndex.test.js.map +1 -1
  69. package/dist/test/unit/vector/vectorIndex.test.js +24 -15
  70. package/dist/test/unit/vector/vectorIndex.test.js.map +1 -1
  71. package/dist/ts-agent-memory.d.ts +384 -97
  72. package/etc/ts-agent-memory.api.md +64 -22
  73. package/lib/packlets/converters/envelopeConverter.d.ts +8 -1
  74. package/lib/packlets/converters/envelopeConverter.d.ts.map +1 -1
  75. package/lib/packlets/converters/envelopeConverter.js +18 -4
  76. package/lib/packlets/converters/envelopeConverter.js.map +1 -1
  77. package/lib/packlets/index/memoryIndex.d.ts +42 -10
  78. package/lib/packlets/index/memoryIndex.d.ts.map +1 -1
  79. package/lib/packlets/index/memoryIndex.js +58 -10
  80. package/lib/packlets/index/memoryIndex.js.map +1 -1
  81. package/lib/packlets/ingest/cycleGuard.d.ts +5 -5
  82. package/lib/packlets/ingest/cycleGuard.d.ts.map +1 -1
  83. package/lib/packlets/ingest/cycleGuard.js +13 -6
  84. package/lib/packlets/ingest/cycleGuard.js.map +1 -1
  85. package/lib/packlets/ingest/hostStages.d.ts +3 -3
  86. package/lib/packlets/ingest/hostStages.d.ts.map +1 -1
  87. package/lib/packlets/ingest/hostStages.js.map +1 -1
  88. package/lib/packlets/ingest/model.d.ts +25 -14
  89. package/lib/packlets/ingest/model.d.ts.map +1 -1
  90. package/lib/packlets/ingest/model.js.map +1 -1
  91. package/lib/packlets/ingest/orchestrator.d.ts +18 -4
  92. package/lib/packlets/ingest/orchestrator.d.ts.map +1 -1
  93. package/lib/packlets/ingest/orchestrator.js +93 -48
  94. package/lib/packlets/ingest/orchestrator.js.map +1 -1
  95. package/lib/packlets/retrieve/hybridRetriever.d.ts.map +1 -1
  96. package/lib/packlets/retrieve/hybridRetriever.js +12 -1
  97. package/lib/packlets/retrieve/hybridRetriever.js.map +1 -1
  98. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts +18 -23
  99. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts.map +1 -1
  100. package/lib/packlets/retrieve/linkTraversalRetriever.js +45 -56
  101. package/lib/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
  102. package/lib/packlets/retrieve/recencyRetriever.js +2 -2
  103. package/lib/packlets/retrieve/recencyRetriever.js.map +1 -1
  104. package/lib/packlets/retrieve/retriever.d.ts +70 -11
  105. package/lib/packlets/retrieve/retriever.d.ts.map +1 -1
  106. package/lib/packlets/retrieve/retriever.js +52 -7
  107. package/lib/packlets/retrieve/retriever.js.map +1 -1
  108. package/lib/packlets/retrieve/semanticRetriever.d.ts.map +1 -1
  109. package/lib/packlets/retrieve/semanticRetriever.js +9 -3
  110. package/lib/packlets/retrieve/semanticRetriever.js.map +1 -1
  111. package/lib/packlets/retrieve/structuredFilterRetriever.js +2 -2
  112. package/lib/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
  113. package/lib/packlets/retrieve/tagRetriever.js +2 -2
  114. package/lib/packlets/retrieve/tagRetriever.js.map +1 -1
  115. package/lib/packlets/retrieve/temporalRetrievers.js +3 -3
  116. package/lib/packlets/retrieve/temporalRetrievers.js.map +1 -1
  117. package/lib/packlets/store/fileTreeMemoryStore.d.ts +57 -3
  118. package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
  119. package/lib/packlets/store/fileTreeMemoryStore.js +72 -16
  120. package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -1
  121. package/lib/packlets/tools/memoryTools.d.ts +24 -0
  122. package/lib/packlets/tools/memoryTools.d.ts.map +1 -1
  123. package/lib/packlets/tools/memoryTools.js +104 -21
  124. package/lib/packlets/tools/memoryTools.js.map +1 -1
  125. package/lib/packlets/types/envelope.d.ts +61 -8
  126. package/lib/packlets/types/envelope.d.ts.map +1 -1
  127. package/lib/packlets/types/envelope.js +14 -0
  128. package/lib/packlets/types/envelope.js.map +1 -1
  129. package/lib/packlets/vector/inMemoryCosineIndex.d.ts +9 -5
  130. package/lib/packlets/vector/inMemoryCosineIndex.d.ts.map +1 -1
  131. package/lib/packlets/vector/inMemoryCosineIndex.js +22 -17
  132. package/lib/packlets/vector/inMemoryCosineIndex.js.map +1 -1
  133. package/lib/packlets/vector/vectorIndex.d.ts +46 -20
  134. package/lib/packlets/vector/vectorIndex.d.ts.map +1 -1
  135. package/lib/packlets/vector/vectorIndex.js.map +1 -1
  136. package/lib/test/unit/converters/antagonistRoundTrip.test.js +3 -3
  137. package/lib/test/unit/converters/antagonistRoundTrip.test.js.map +1 -1
  138. package/lib/test/unit/converters/envelopeConverter.test.js +124 -7
  139. package/lib/test/unit/converters/envelopeConverter.test.js.map +1 -1
  140. package/lib/test/unit/index/memoryIndex.test.js +86 -24
  141. package/lib/test/unit/index/memoryIndex.test.js.map +1 -1
  142. package/lib/test/unit/ingest/antagonistCycleAndParity.test.js +20 -16
  143. package/lib/test/unit/ingest/antagonistCycleAndParity.test.js.map +1 -1
  144. package/lib/test/unit/ingest/cycleGuard.test.js +28 -1
  145. package/lib/test/unit/ingest/cycleGuard.test.js.map +1 -1
  146. package/lib/test/unit/ingest/orchestrator.test.js +186 -44
  147. package/lib/test/unit/ingest/orchestrator.test.js.map +1 -1
  148. package/lib/test/unit/retrieve/linkTraversalRetriever.test.js +106 -31
  149. package/lib/test/unit/retrieve/linkTraversalRetriever.test.js.map +1 -1
  150. package/lib/test/unit/retrieve/retrievers.test.js +298 -36
  151. package/lib/test/unit/retrieve/retrievers.test.js.map +1 -1
  152. package/lib/test/unit/store/embedOnWrite.test.js +68 -11
  153. package/lib/test/unit/store/embedOnWrite.test.js.map +1 -1
  154. package/lib/test/unit/store/listScoped.test.d.ts +2 -0
  155. package/lib/test/unit/store/listScoped.test.d.ts.map +1 -0
  156. package/lib/test/unit/store/listScoped.test.js +111 -0
  157. package/lib/test/unit/store/listScoped.test.js.map +1 -0
  158. package/lib/test/unit/store/rankAxis.test.d.ts +2 -0
  159. package/lib/test/unit/store/rankAxis.test.d.ts.map +1 -0
  160. package/lib/test/unit/store/rankAxis.test.js +256 -0
  161. package/lib/test/unit/store/rankAxis.test.js.map +1 -0
  162. package/lib/test/unit/tools/memoryTools.test.js +280 -11
  163. package/lib/test/unit/tools/memoryTools.test.js.map +1 -1
  164. package/lib/test/unit/types/writePolicy.test.js +9 -2
  165. package/lib/test/unit/types/writePolicy.test.js.map +1 -1
  166. package/lib/test/unit/vector/inMemoryCosineIndex.test.js +95 -35
  167. package/lib/test/unit/vector/inMemoryCosineIndex.test.js.map +1 -1
  168. package/lib/test/unit/vector/vectorIndex.test.js +24 -15
  169. package/lib/test/unit/vector/vectorIndex.test.js.map +1 -1
  170. package/package.json +7 -7
  171. package/rush-logs/ts-agent-memory.build.cache.log +1 -1
  172. package/rush-logs/ts-agent-memory.build.log +2 -2
  173. package/src/packlets/converters/envelopeConverter.ts +27 -4
  174. package/src/packlets/index/memoryIndex.ts +86 -22
  175. package/src/packlets/ingest/cycleGuard.ts +22 -11
  176. package/src/packlets/ingest/hostStages.ts +3 -3
  177. package/src/packlets/ingest/model.ts +25 -14
  178. package/src/packlets/ingest/orchestrator.ts +143 -67
  179. package/src/packlets/retrieve/hybridRetriever.ts +14 -1
  180. package/src/packlets/retrieve/linkTraversalRetriever.ts +51 -62
  181. package/src/packlets/retrieve/recencyRetriever.ts +3 -3
  182. package/src/packlets/retrieve/retriever.ts +97 -13
  183. package/src/packlets/retrieve/semanticRetriever.ts +10 -5
  184. package/src/packlets/retrieve/structuredFilterRetriever.ts +3 -3
  185. package/src/packlets/retrieve/tagRetriever.ts +3 -3
  186. package/src/packlets/retrieve/temporalRetrievers.ts +3 -3
  187. package/src/packlets/store/fileTreeMemoryStore.ts +117 -12
  188. package/src/packlets/tools/memoryTools.ts +152 -25
  189. package/src/packlets/types/envelope.ts +66 -8
  190. package/src/packlets/vector/inMemoryCosineIndex.ts +45 -22
  191. package/src/packlets/vector/vectorIndex.ts +47 -20
  192. package/src/test/unit/converters/antagonistRoundTrip.test.ts +3 -3
  193. package/src/test/unit/converters/envelopeConverter.test.ts +168 -11
  194. package/src/test/unit/index/memoryIndex.test.ts +99 -14
  195. package/src/test/unit/ingest/antagonistCycleAndParity.test.ts +23 -18
  196. package/src/test/unit/ingest/cycleGuard.test.ts +44 -2
  197. package/src/test/unit/ingest/orchestrator.test.ts +234 -41
  198. package/src/test/unit/retrieve/linkTraversalRetriever.test.ts +134 -35
  199. package/src/test/unit/retrieve/retrievers.test.ts +381 -25
  200. package/src/test/unit/store/embedOnWrite.test.ts +83 -11
  201. package/src/test/unit/store/listScoped.test.ts +138 -0
  202. package/src/test/unit/store/rankAxis.test.ts +349 -0
  203. package/src/test/unit/tools/memoryTools.test.ts +362 -13
  204. package/src/test/unit/types/writePolicy.test.ts +11 -2
  205. package/src/test/unit/vector/inMemoryCosineIndex.test.ts +115 -39
  206. package/src/test/unit/vector/vectorIndex.test.ts +33 -17
  207. package/temp/build/lint/_eslint-5eVG3S6w.json +41 -33
  208. package/temp/build/typescript/ts_8nwakTlr.json +1 -1
  209. package/temp/ts-agent-memory.api.json +1080 -136
  210. package/temp/ts-agent-memory.api.md +64 -22
@@ -251,6 +251,26 @@ export declare function defaultMemoryScopeEncoding(scope: MemoryScopeKey): Resul
251
251
  */
252
252
  export declare const edgeConverter: Converter<IEdge>;
253
253
 
254
+ /**
255
+ * Converter for a scope-qualified {@link IEdgeTarget}. Both `scope` and `id`
256
+ * are required — the whole point of the scoped target is that a bare id is
257
+ * ambiguous across scopes.
258
+ * @public
259
+ */
260
+ export declare const edgeTargetConverter: Converter<IEdgeTarget>;
261
+
262
+ /**
263
+ * The canonical composite-key string for an {@link IEdgeTarget}: scope + id,
264
+ * NUL-separated. NUL is excluded from both components (scope segments are
265
+ * filename-safe; {@link MemoryId} is portable-filename-safe), so it is a
266
+ * collision-proof separator. This is the ONE canonicalization every consumer
267
+ * that keys on a scoped target uses — the backlink index, the cycle guard, and
268
+ * the ingest edge-validation path all route through it so their notions of
269
+ * "same target" cannot drift.
270
+ * @public
271
+ */
272
+ export declare function edgeTargetKey(target: IEdgeTarget): string;
273
+
254
274
  /**
255
275
  * Consumer-supplied domain key. The stable entity identity across versions;
256
276
  * the package never mints identity. Equals {@link MemoryId} for non-temporal
@@ -292,6 +312,7 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
292
312
  private readonly _registry;
293
313
  private readonly _writePolicies;
294
314
  private readonly _codecs;
315
+ private readonly _rankProjectors;
295
316
  private readonly _defaultCodec;
296
317
  private readonly _defaultPolicy;
297
318
  private readonly _scopeEncoding;
@@ -334,6 +355,10 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
334
355
  getById(scope: MemoryScopeKey, id: MemoryId): Promise<Result<IMemoryRecord<unknown> | undefined>>;
335
356
  /** {@inheritDoc IMemoryStore.list} */
336
357
  list(filter?: IMemoryStoreListFilter): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
358
+ /** {@inheritDoc IMemoryStore.listScoped} */
359
+ listScoped(): Promise<Result<ReadonlyArray<IScopedMemoryRecord>>>;
360
+ /** {@inheritDoc IMemoryStore.asRecordSource} */
361
+ asRecordSource(): IMemoryRecordSource;
337
362
  /**
338
363
  * Collapse temporal records to the single version valid at `asOf` per entity;
339
364
  * non-temporal records are timeless and pass through unchanged (valid-time
@@ -413,7 +438,12 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
413
438
  * `accept` / `reject` decisions evict nothing.
414
439
  */
415
440
  private _applyEvictions;
416
- /** Best-effort vector removal for each evicted record (never fails the put). */
441
+ /**
442
+ * Best-effort vector removal for each evicted record (never fails the put).
443
+ * Every evicted record is in the same `scope` as the incoming write (the
444
+ * cull-oldest cohort is the incoming record's `(scope, kind)` cohort), so that
445
+ * scope qualifies each removal target.
446
+ */
417
447
  private _removeEvictedVectors;
418
448
  /**
419
449
  * Run a consumer-supplied vector hook, normalizing a thrown/rejected hook into a
@@ -544,6 +574,19 @@ export declare class FileTreeMemoryStore implements IMemoryStore {
544
574
  */
545
575
  private _isMutableMetadataUnchanged;
546
576
  private _contentHash;
577
+ /**
578
+ * Stamp the store-computed {@link IMemoryEnvelope.rank} onto a fully-built,
579
+ * fully-stamped record by running the kind's registered {@link RankProjector}.
580
+ * Runs on the SAME resolved (post-merge) record whose `contentHash` was just
581
+ * computed, so `rank` is always consistent with the current body — no separate
582
+ * write path and no consumer write-discipline rule. A no-op pass-through
583
+ * (byte-identical record) when the kind has no projector — the additive,
584
+ * zero-overhead-when-unwired default. The projector is a host callback: a throw
585
+ * is logged at `warn` and the record is stamped with NO `rank` (the field is
586
+ * explicitly cleared, so a throw on an update drops a now-stale prior rank rather
587
+ * than preserving it), so a ranking bug never loses an authoritative write.
588
+ */
589
+ private _stampRank;
547
590
  private _codecFor;
548
591
  private _policyFor;
549
592
  /**
@@ -720,8 +763,8 @@ export declare interface IBodyConverterRegistry {
720
763
  * @public
721
764
  */
722
765
  export declare interface ICandidateEdge {
723
- /** The reference id (codec `idStem`) of the candidate the edge originates from. */
724
- readonly source: MemoryId;
766
+ /** The scope-qualified reference (codec `(scope, idStem)`) of the candidate the edge originates from. */
767
+ readonly source: IEdgeTarget;
725
768
  /** The attributed edge (type / target / optional confidence / provenance). */
726
769
  readonly edge: IEdge;
727
770
  }
@@ -792,31 +835,47 @@ export declare interface ICreateMemoryToolsParams {
792
835
  * {@link MemoryId} is used.
793
836
  */
794
837
  readonly handleFor?: (record: IMemoryRecord<unknown>) => string;
838
+ /**
839
+ * Optional host projector mapping a record (and the requested detail tier) to
840
+ * its agent-visible {@link IMemoryToolResultItem}. When supplied, every
841
+ * `memory_search` / `memory_context` / `memory_read` result item is produced by
842
+ * this callback — the host owns how much of the body a `'gist'` vs `'full'`
843
+ * result carries, so it can bound the default (`'gist'`) path.
844
+ *
845
+ * When absent, the built-in default projection is used (full body plus the
846
+ * {@link ICreateMemoryToolsParams.handleFor | handleFor} handle), which ignores
847
+ * the detail tier — behavior is byte-identical to a build with no projector.
848
+ *
849
+ * The callback is guarded exactly like `handleFor`: a throw degrades to the
850
+ * default full-body projection for that item rather than failing the whole
851
+ * search.
852
+ */
853
+ readonly projectItem?: (record: IMemoryRecord<unknown>, detail: MemoryDetailTier) => IMemoryToolResultItem;
795
854
  }
796
855
 
797
856
  /**
798
- * A directed edge in the link graph the cycle guard reasons over: `source` links
799
- * to `target` under relation `type`.
857
+ * A directed edge in the link graph the cycle guard reasons over: scope-qualified
858
+ * `source` links to scope-qualified `target` under relation `type`.
800
859
  * @public
801
860
  */
802
861
  export declare interface ICycleGuardEdge {
803
- readonly source: MemoryId;
804
- readonly target: MemoryId;
862
+ readonly source: IEdgeTarget;
863
+ readonly target: IEdgeTarget;
805
864
  readonly type: LinkType;
806
865
  }
807
866
 
808
867
  /**
809
868
  * An attributed link between two records. Carries the relation type, the
810
- * target id, and optional confidence / provenance / world-truth validity.
811
- * Replaces bare string references (e.g. PersonAIlity's `IMtmRef` becomes an
812
- * `IEdge` with `type: LinkType('mtm-ref')`).
869
+ * scope-qualified {@link IEdgeTarget | target}, and optional confidence /
870
+ * provenance / world-truth validity. Replaces bare string references (e.g.
871
+ * PersonAIlity's `IMtmRef` becomes an `IEdge` with `type: LinkType('mtm-ref')`).
813
872
  * @public
814
873
  */
815
874
  export declare interface IEdge {
816
875
  /** Open-vocabulary relation type. */
817
876
  readonly type: LinkType;
818
- /** The linked-to record. */
819
- readonly target: MemoryId;
877
+ /** The scope-qualified address of the linked-to record. */
878
+ readonly target: IEdgeTarget;
820
879
  /** Optional confidence in `[0, 1]`. */
821
880
  readonly confidence?: number;
822
881
  /** Optional structured provenance for the link itself. */
@@ -830,6 +889,23 @@ export declare interface IEdge {
830
889
  readonly invalid_at?: number | null;
831
890
  }
832
891
 
892
+ /**
893
+ * The physical address of a linked-to record: the `(scope, id)` pair that
894
+ * uniquely identifies it. Both components are required because a bare
895
+ * {@link MemoryId} is NOT unique across scopes — per-scope codecs (e.g. the
896
+ * medium-term codec's `turn-<n>` stems) legally mint the same stem under
897
+ * different scopes, so an edge that carried only the id would be ambiguous.
898
+ * `(scope, id)` matches the store's `getById(scope, id)` addressing and the
899
+ * index's composite primary key.
900
+ * @public
901
+ */
902
+ export declare interface IEdgeTarget {
903
+ /** The scope the target record lives under. */
904
+ readonly scope: MemoryScopeKey;
905
+ /** The target record's stable file-stem id (unique WITHIN {@link IEdgeTarget.scope}). */
906
+ readonly id: MemoryId;
907
+ }
908
+
833
909
  /**
834
910
  * A near-duplicate candidate surfaced to the {@link IEntityResolver} by stage-4
835
911
  * layer-2 similarity search: an existing record whose embedding is within the
@@ -837,8 +913,14 @@ export declare interface IEdge {
837
913
  * @public
838
914
  */
839
915
  export declare interface IEntityResolutionCandidate {
840
- /** The existing record's id. */
841
- readonly id: MemoryId;
916
+ /**
917
+ * The existing record's scope-qualified `(scope, id)` address. Scope-qualified
918
+ * (not a bare {@link MemoryId}) because per-scope codecs legally mint the same
919
+ * stem under different scopes — the {@link ResolutionVerdict} target the resolver
920
+ * returns must round-trip this exact address so the write binds the intended
921
+ * record.
922
+ */
923
+ readonly target: IEdgeTarget;
842
924
  /** The existing record. */
843
925
  readonly record: IMemoryRecord<unknown>;
844
926
  /** The backend similarity score (higher = more similar). */
@@ -891,6 +973,19 @@ export declare interface IFileTreeMemoryStoreCreateParams {
891
973
  readonly writePolicies?: ReadonlyMap<Kind, IWritePolicy>;
892
974
  /** Per-kind identity codecs. */
893
975
  readonly codecs?: ReadonlyMap<Kind, IIdentityCodec>;
976
+ /**
977
+ * Optional per-kind host projector map. When a kind has an entry, the store
978
+ * runs the projector on the fully-resolved (post-merge) record on every put
979
+ * AND every update — in the same pass that recomputes `contentHash` — and
980
+ * stamps the numeric result into {@link IMemoryEnvelope.rank}, which the index's
981
+ * rank view and `orderBy: 'rank'` retrieval sort by (descending, absent last).
982
+ * Absent for a kind → that kind's records carry no `rank`. Purely additive and
983
+ * zero-overhead when unwired: an absent map leaves every write byte-identical.
984
+ * The projector is a host callback — a throw is logged at `warn` and the record
985
+ * is stamped with no `rank`, never failing the write (mirrors the store's other
986
+ * guard-host-callbacks conventions).
987
+ */
988
+ readonly rankProjectors?: ReadonlyMap<Kind, RankProjector>;
894
989
  /** Default codec for kinds without an explicit entry. */
895
990
  readonly defaultCodec?: IIdentityCodec;
896
991
  /** Scope encoding. Defaults to {@link defaultMemoryScopeEncoding}. */
@@ -1054,12 +1149,15 @@ export declare interface IIngestItem {
1054
1149
  */
1055
1150
  readonly content: unknown;
1056
1151
  /**
1057
- * Optional back-link to the memory record this item was derived from (e.g. the
1058
- * MTM turn an extracted fact came from). When present, fgv stamps it as
1059
- * {@link IProvenance.derivedFrom | provenance.derivedFrom} on every record
1060
- * ingested from this item (stage 6) — the cross-kind provenance spine.
1152
+ * Optional scope-qualified back-link to the memory record this item was derived
1153
+ * from (e.g. the MTM turn an extracted fact came from). When present, fgv stamps
1154
+ * it as {@link IProvenance.derivedFrom | provenance.derivedFrom} on every record
1155
+ * ingested from this item (stage 6) — the cross-kind provenance spine. A
1156
+ * scope-qualified {@link IEdgeTarget} (not a bare {@link MemoryId}) because
1157
+ * per-scope codecs legally reuse a stem across scopes, so a bare id would be
1158
+ * ambiguous.
1061
1159
  */
1062
- readonly sourceId?: MemoryId;
1160
+ readonly sourceId?: IEdgeTarget;
1063
1161
  /** Optional opaque metadata carried alongside the item; never interpreted by fgv. */
1064
1162
  readonly metadata?: Record<string, unknown>;
1065
1163
  }
@@ -1161,6 +1259,15 @@ export declare interface IMemoryEnvelope {
1161
1259
  * an exact match is a no-op upsert that returns the existing record.
1162
1260
  */
1163
1261
  readonly contentHash: string;
1262
+ /**
1263
+ * Store-computed host-defined ordering value, produced by the kind's
1264
+ * {@link RankProjector} on every put/update and stamped into the envelope in
1265
+ * the same pass that recomputes {@link IMemoryEnvelope.contentHash | contentHash}.
1266
+ * Absent when the kind has no registered projector (or the projector threw on
1267
+ * this record). Ordered retrieval (`orderBy: 'rank'`) and the index's rank view
1268
+ * sort by this value descending, placing records with an absent `rank` last.
1269
+ */
1270
+ readonly rank?: number;
1164
1271
  /** Structured provenance (never a flat enum). */
1165
1272
  readonly provenance: IProvenance;
1166
1273
  /** Bi-temporal validity. Present only on temporal kinds. */
@@ -1218,10 +1325,20 @@ export declare interface IMemoryIndex {
1218
1325
  /** All records in recency order (most-recently-updated first). */
1219
1326
  byRecency(): ReadonlyArray<IMemoryRecord<unknown>>;
1220
1327
  /**
1221
- * The ids of records whose `links` point AT `target` (inbound edges).
1222
- * The seed map for B2 link-traversal.
1328
+ * All records ordered by store-computed {@link IMemoryEnvelope.rank} descending,
1329
+ * with recency (most-recently-updated, then `seq`) as a tiebreak. Records with
1330
+ * an absent `rank` sort LAST (after every ranked record), then by recency among
1331
+ * themselves. Serves a bounded top-M ({@link IMemoryEnvelope.rank}-ordered) page
1332
+ * from the in-memory index with no full-vault (filesystem) scan.
1223
1333
  */
1224
- backlinks(target: MemoryId): ReadonlyArray<MemoryId>;
1334
+ byRank(): ReadonlyArray<IMemoryRecord<unknown>>;
1335
+ /**
1336
+ * The scope-qualified sources of records whose `links` point AT `target`
1337
+ * (inbound edges), keyed on the target's `(scope, id)` address. The seed map
1338
+ * for B2 link-traversal; results are {@link IEdgeTarget}s so a caller can feed
1339
+ * them straight back in as further traversal seeds.
1340
+ */
1341
+ backlinks(target: IEdgeTarget): ReadonlyArray<IEdgeTarget>;
1225
1342
  }
1226
1343
 
1227
1344
  /**
@@ -1409,12 +1526,24 @@ export declare interface IMemoryQuery {
1409
1526
  readonly scope?: MemoryScopeKey;
1410
1527
  /** Restrict to records carrying this tag (exact match). */
1411
1528
  readonly tag?: Tag;
1412
- /** Restrict to records of this kind. */
1529
+ /**
1530
+ * Restrict to records of this kind — the single-kind shorthand for
1531
+ * {@link IMemoryQuery.kinds | kinds}. When both are set they compose as AND
1532
+ * (the record's kind must satisfy both), so `kind` must itself be a member of
1533
+ * `kinds` for anything to match.
1534
+ */
1413
1535
  readonly kind?: Kind;
1414
- /** Restrict to records linked FROM this id (outbound). */
1415
- readonly linkedFrom?: MemoryId;
1416
- /** Restrict to records linked TO this id (inbound / backlinks). */
1417
- readonly linkedTo?: MemoryId;
1536
+ /**
1537
+ * Restrict to records in ANY of these kinds — the general (multi-kind) form of
1538
+ * {@link IMemoryQuery.kind | kind}. Absent no kind-set constraint (today's
1539
+ * behavior). An explicit empty array `[]` matches NOTHING (mirroring the
1540
+ * non-positive-`limit` "explicit empty" convention), never "match all".
1541
+ */
1542
+ readonly kinds?: ReadonlyArray<Kind>;
1543
+ /** Restrict to records linked FROM this scope-qualified seed (outbound). */
1544
+ readonly linkedFrom?: IEdgeTarget;
1545
+ /** Restrict to records linked TO this scope-qualified seed (inbound / backlinks). */
1546
+ readonly linkedTo?: IEdgeTarget;
1418
1547
  /** BFS hop count for link traversal. Default: 1. */
1419
1548
  readonly hops?: number;
1420
1549
  /**
@@ -1431,8 +1560,32 @@ export declare interface IMemoryQuery {
1431
1560
  * `Result.fail` — never a silent empty.
1432
1561
  */
1433
1562
  readonly asOf?: number;
1563
+ /**
1564
+ * Ordering for the result set. `'recency'` (the default when absent — today's
1565
+ * exact behavior) orders most-recently-updated first; `'rank'` orders by the
1566
+ * store-computed {@link IMemoryEnvelope.rank} descending (records with an absent
1567
+ * `rank` last), with recency as the tiebreak. Combined with `{ limit, offset }`
1568
+ * this yields a bounded top-M rank-ordered page with no full-vault scan.
1569
+ *
1570
+ * @remarks
1571
+ * `orderBy` governs the ordered non-semantic retrievers (recency / tag /
1572
+ * structured-filter / link-traversal) and the {@link HybridRetriever}'s
1573
+ * post-merge ordering. The {@link SemanticRetriever} is the sole exception: it
1574
+ * preserves its native vector-similarity order regardless of `orderBy` —
1575
+ * re-sorting semantic hits by `rank` would discard the similarity ranking that
1576
+ * is the whole point of that path; a consumer that wants rank ordering uses a
1577
+ * non-semantic query.
1578
+ */
1579
+ readonly orderBy?: 'recency' | 'rank';
1434
1580
  /** Maximum records to return. Applied after all other filters. */
1435
1581
  readonly limit?: number;
1582
+ /**
1583
+ * Records to skip after ordering, before `limit` — so `{ offset, limit }` is a
1584
+ * stable page window over the ordered result set. Default 0. A non-positive or
1585
+ * absent offset is today's behavior (no skip); an offset past the end yields an
1586
+ * empty page, never a throw.
1587
+ */
1588
+ readonly offset?: number;
1436
1589
  /** Arbitrary predicate applied after the scope / kind / tag pre-filter. */
1437
1590
  readonly filter?: (record: IMemoryRecord<unknown>) => boolean;
1438
1591
  }
@@ -1453,16 +1606,17 @@ export declare interface IMemoryRecord<TBody = unknown> {
1453
1606
 
1454
1607
  /**
1455
1608
  * The minimal record-source surface {@link InMemoryCosineIndex.rebuild} reads to
1456
- * re-embed an entire vault. {@link IMemoryStore} satisfies it structurally (its
1457
- * `list` accepts an optional filter, which is assignable to this no-argument
1458
- * shape), so a consumer passes the store directly without the vector packlet
1459
- * taking a dependency on the store packlet (which depends on the vector packlet
1460
- * for {@link IVectorIndex}, so the reverse import would be a cycle).
1609
+ * re-embed an entire vault. Each entry carries the record's scope-qualified
1610
+ * address (see {@link IScopedMemoryRecord}) so the rebuild keys the vector index
1611
+ * exactly as the incremental embed-on-write path does. A consumer backs this with
1612
+ * the store's scoped index the vector packlet does not import the store packlet
1613
+ * (which depends on the vector packlet for {@link IVectorIndex}, so the reverse
1614
+ * import would be a cycle).
1461
1615
  * @public
1462
1616
  */
1463
1617
  export declare interface IMemoryRecordSource {
1464
- /** List every record in the vault. */
1465
- list(): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
1618
+ /** List every record in the vault, each paired with its scoped address. */
1619
+ list(): Promise<Result<ReadonlyArray<IScopedMemoryRecord>>>;
1466
1620
  }
1467
1621
 
1468
1622
  /**
@@ -1519,6 +1673,24 @@ export declare interface IMemoryStore {
1519
1673
  * List records, filtered in-memory over the derived index.
1520
1674
  */
1521
1675
  list(filter?: IMemoryStoreListFilter): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
1676
+ /**
1677
+ * List EVERY record in the vault, each paired with its scope-qualified
1678
+ * `(scope, id)` address — the projection {@link IMemoryRecordSource} requires.
1679
+ * Unlike {@link IMemoryStore.list | list}, it takes no filter (whole-vault) and
1680
+ * returns {@link IScopedMemoryRecord}s so a re-index keys each entry on the same
1681
+ * scoped target the incremental embed-on-write path uses. Two records that share
1682
+ * a filename stem across scopes appear as distinct entries.
1683
+ */
1684
+ listScoped(): Promise<Result<ReadonlyArray<IScopedMemoryRecord>>>;
1685
+ /**
1686
+ * Adapt this store to the {@link IMemoryRecordSource} seam so it can drive
1687
+ * {@link IVectorIndex} rebuilds (e.g. `InMemoryCosineIndex.rebuild`). The
1688
+ * returned source's `list()` delegates to {@link IMemoryStore.listScoped}. The
1689
+ * store cannot implement {@link IMemoryRecordSource} directly because its
1690
+ * `list(filter?)` returns bare records (the ergonomic query surface) while the
1691
+ * seam's `list()` returns scope-qualified records.
1692
+ */
1693
+ asRecordSource(): IMemoryRecordSource;
1522
1694
  /**
1523
1695
  * Write a record. Validates the body, computes a content hash, deduplicates
1524
1696
  * (scope-wide, before policy), applies the kind's {@link IWritePolicy}, stamps
@@ -1649,6 +1821,11 @@ export declare type IngestDisposition = 'written' | 'deduped' | 'merged';
1649
1821
  * @public
1650
1822
  */
1651
1823
  export declare class InMemoryCosineIndex implements IVectorIndex {
1824
+ /**
1825
+ * Stored embeddings keyed by the canonical {@link edgeTargetKey} of the
1826
+ * record's scope-qualified address, so two records that share a filename stem
1827
+ * across scopes occupy distinct entries and never overwrite each other.
1828
+ */
1652
1829
  private readonly _vectors;
1653
1830
  /** The dimension of every stored vector; `undefined` until the first `add`. */
1654
1831
  private _dimension;
@@ -1658,9 +1835,9 @@ export declare class InMemoryCosineIndex implements IVectorIndex {
1658
1835
  /** Family-convention factory. */
1659
1836
  static create(): Result<InMemoryCosineIndex>;
1660
1837
  /** {@inheritDoc IVectorIndex.add} */
1661
- add(id: MemoryId, vector: Float32Array): Promise<Result<string>>;
1838
+ add(target: IEdgeTarget, vector: Float32Array): Promise<Result<string>>;
1662
1839
  /** {@inheritDoc IVectorIndex.remove} */
1663
- remove(id: MemoryId): Promise<Result<MemoryId>>;
1840
+ remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;
1664
1841
  /** {@inheritDoc IVectorIndex.query} */
1665
1842
  query(vector: Float32Array, topK: number): Promise<Result<ReadonlyArray<IVectorQueryHit>>>;
1666
1843
  /**
@@ -1673,8 +1850,7 @@ export declare class InMemoryCosineIndex implements IVectorIndex {
1673
1850
  * rather than left in a partially-rebuilt state — a caller that retries a query
1674
1851
  * after a failed rebuild sees a clean empty index, never a half-populated one.
1675
1852
  *
1676
- * @param source - The record source to re-embed (an {@link IMemoryStore}
1677
- * satisfies this structurally).
1853
+ * @param source - The scope-qualified record source to re-embed.
1678
1854
  * @param embed - The embedder applied to each record.
1679
1855
  */
1680
1856
  rebuild(source: IMemoryRecordSource, embed: MemoryEmbedder): Promise<Result<number>>;
@@ -1707,8 +1883,14 @@ export declare interface IProvenance {
1707
1883
  readonly model?: string;
1708
1884
  /** Optional confidence in `[0, 1]`. */
1709
1885
  readonly confidence?: number;
1710
- /** Back-link to the source experience record. Enables the cross-kind provenance spine. */
1711
- readonly derivedFrom?: MemoryId;
1886
+ /**
1887
+ * Scope-qualified back-link to the source record. Enables the cross-kind
1888
+ * provenance spine. A scope-qualified {@link IEdgeTarget} (not a bare
1889
+ * {@link MemoryId}) because per-scope codecs (e.g. the MTM codec's `turn-<n>`
1890
+ * stems) legally reuse a stem across scopes, so a bare id would be ambiguous —
1891
+ * the same reason {@link IEdge.target} is scope-qualified.
1892
+ */
1893
+ readonly derivedFrom?: IEdgeTarget;
1712
1894
  /** Opaque extension payload — consumer-owned, never interpreted by the store. */
1713
1895
  readonly [key: string]: unknown;
1714
1896
  }
@@ -1721,8 +1903,8 @@ export declare interface IProvenance {
1721
1903
  export declare interface IRelationCandidate {
1722
1904
  /** The candidate about to be written. */
1723
1905
  readonly candidate: ICandidateRecord;
1724
- /** Its resolved reference id (codec `idStem` — the stable entity reference). */
1725
- readonly id: MemoryId;
1906
+ /** Its resolved scope-qualified reference (codec `(scope, idStem)` — the stable entity address). */
1907
+ readonly id: IEdgeTarget;
1726
1908
  }
1727
1909
 
1728
1910
  /**
@@ -1755,6 +1937,21 @@ export declare interface IRelationExtractor {
1755
1937
  relate(context: IRelationContext): Promise<Result<ReadonlyArray<ICandidateEdge>>>;
1756
1938
  }
1757
1939
 
1940
+ /**
1941
+ * A record paired with its scope-qualified {@link IEdgeTarget | address}, as
1942
+ * yielded by {@link IMemoryRecordSource.list}. The address is required because
1943
+ * {@link InMemoryCosineIndex.rebuild} keys each re-embedded entry on the
1944
+ * scope-qualified target, not a bare {@link MemoryId} — two records that share a
1945
+ * filename stem across scopes must not collide when the whole vault is re-indexed.
1946
+ * @public
1947
+ */
1948
+ export declare interface IScopedMemoryRecord {
1949
+ /** The record's scope-qualified `(scope, id)` address. */
1950
+ readonly target: IEdgeTarget;
1951
+ /** The record itself, passed to the embedder. */
1952
+ readonly record: IMemoryRecord<unknown>;
1953
+ }
1954
+
1758
1955
  /**
1759
1956
  * The semantic backend wired into a {@link SemanticRetriever}: the vector index
1760
1957
  * to query and the embedder that turns the query text into a vector. Both are
@@ -1884,16 +2081,19 @@ export declare interface ITemporalVersionAddress {
1884
2081
  */
1885
2082
  export declare interface IVectorIndex {
1886
2083
  /**
1887
- * Add (or replace) the embedding for `id`. Returns the opaque
1888
- * {@link IMemoryEnvelope.embeddingRef | embeddingRef} the store stamps onto
1889
- * the envelope so a later read knows the record is embedded.
2084
+ * Add (or replace) the embedding for the scope-qualified `target`. Returns the
2085
+ * opaque {@link IMemoryEnvelope.embeddingRef | embeddingRef} the store stamps
2086
+ * onto the envelope so a later read knows the record is embedded. Keying on the
2087
+ * `(scope, id)` address (not a bare id) is load-bearing: two records that share
2088
+ * a filename stem across scopes must not clobber each other's embedding.
1890
2089
  */
1891
- add(id: MemoryId, vector: Float32Array): Promise<Result<string>>;
2090
+ add(target: IEdgeTarget, vector: Float32Array): Promise<Result<string>>;
1892
2091
  /**
1893
- * Remove the embedding for `id`. Returns the removed id. Idempotent — removing
1894
- * an id with no embedding still succeeds (returns the id).
2092
+ * Remove the embedding for the scope-qualified `target`. Returns the removed
2093
+ * target. Idempotent — removing a target with no embedding still succeeds
2094
+ * (returns the target).
1895
2095
  */
1896
- remove(id: MemoryId): Promise<Result<MemoryId>>;
2096
+ remove(target: IEdgeTarget): Promise<Result<IEdgeTarget>>;
1897
2097
  /**
1898
2098
  * Return the `topK` nearest records to `vector`, in descending score order.
1899
2099
  */
@@ -1901,14 +2101,22 @@ export declare interface IVectorIndex {
1901
2101
  }
1902
2102
 
1903
2103
  /**
1904
- * A single hit returned by {@link IVectorIndex.query}: the matched record id and
1905
- * the backend's similarity score (higher = more similar; the exact scale is
1906
- * backend-defined). Hits are returned in descending score order.
2104
+ * A single hit returned by {@link IVectorIndex.query}: the matched record's
2105
+ * scope-qualified {@link IEdgeTarget | address} and the backend's similarity
2106
+ * score (higher = more similar; the exact scale is backend-defined). Hits are
2107
+ * returned in descending score order.
2108
+ *
2109
+ * @remarks
2110
+ * The address is a `(scope, id)` pair, NOT a bare {@link MemoryId} — per-scope
2111
+ * codecs (e.g. the medium-term codec's `turn-<n>` stems) legally mint the same
2112
+ * stem under different scopes, so a bare id could not disambiguate two records
2113
+ * that share a stem. The caller re-resolves the hit against the record index by
2114
+ * the same scoped address.
1907
2115
  * @public
1908
2116
  */
1909
2117
  export declare interface IVectorQueryHit {
1910
- /** The id of the matched record. */
1911
- readonly id: MemoryId;
2118
+ /** The scope-qualified address of the matched record. */
2119
+ readonly target: IEdgeTarget;
1912
2120
  /** Backend similarity score; higher is more similar. */
1913
2121
  readonly score: number;
1914
2122
  }
@@ -2056,13 +2264,21 @@ export declare class KnowledgeLwwPolicy implements IWritePolicy {
2056
2264
  }
2057
2265
 
2058
2266
  /**
2059
- * Truncate to `query.limit` records (a no-op when `limit` is absent). Applied
2060
- * last, after ordering, so it always takes the top-N of the ordered result. A
2061
- * non-positive `limit` is public query input and means "no records" it returns
2062
- * an empty array rather than letting a negative value slip into `slice`.
2267
+ * Apply the `{ offset, limit }` page window to an ordered record set. Applied
2268
+ * last, after ordering, so it always takes a stable window of the ordered
2269
+ * result. `offset` is applied first (records to skip), then `limit` (top-N of
2270
+ * the remainder).
2271
+ *
2272
+ * @remarks
2273
+ * Both bounds are public query input and are guarded against non-positive
2274
+ * values slipping into `slice`:
2275
+ * - `offset` absent or non-positive → no skip (today's behavior). An offset past
2276
+ * the end yields an empty page rather than a throw.
2277
+ * - `limit` absent → no truncation; a non-positive `limit` means "no records"
2278
+ * and returns an empty array.
2063
2279
  * @public
2064
2280
  */
2065
- export declare function limitRecords(records: ReadonlyArray<IMemoryRecord<unknown>>, limit?: number): ReadonlyArray<IMemoryRecord<unknown>>;
2281
+ export declare function limitRecords(records: ReadonlyArray<IMemoryRecord<unknown>>, limit?: number, offset?: number): ReadonlyArray<IMemoryRecord<unknown>>;
2066
2282
 
2067
2283
  /**
2068
2284
  * The loud-degradation message returned when a link-traversal query supplies no
@@ -2080,20 +2296,23 @@ export declare const LINK_TRAVERSAL_NO_SEED_MESSAGE: string;
2080
2296
  export declare const LINK_TRAVERSAL_UNWIRED_MESSAGE: string;
2081
2297
 
2082
2298
  /**
2083
- * Breadth-first link-traversal retriever. From a seed {@link MemoryId} it walks
2084
- * the link graph up to `query.hops` levels and returns the records reached
2085
- * (excluding the seed), recency-ordered and limited.
2299
+ * Breadth-first link-traversal retriever. From a scope-qualified
2300
+ * {@link IEdgeTarget} seed it walks the link graph up to `query.hops` levels and
2301
+ * returns the records reached (excluding the seed), recency-ordered and limited.
2086
2302
  *
2087
2303
  * @remarks
2088
2304
  * - **Direction.** `linkedFrom` walks OUTBOUND edges (each record's
2089
2305
  * `envelope.links[].target`); `linkedTo` walks INBOUND edges (the index's
2090
2306
  * `backlinks`). Exactly one is the seed; `linkedFrom` wins if both are set.
2307
+ * - **Scope-qualified nodes.** Every graph node is an {@link IEdgeTarget}
2308
+ * `(scope, id)` pair, so following an edge to `turn-3` reaches ONLY the record
2309
+ * in the edge's own scope — never a same-stem record in another scope.
2091
2310
  * - **Bound + cycle safety.** Traversal is bounded by `hops` (default `1` — a
2092
- * single hop) and a visited-set guard. The graph is keyed by bare
2093
- * string {@link MemoryId}s, so a `Set<string>` visited-set is the exact,
2094
- * collision-free cycle key — no structural hashing (e.g. `Crc32Normalizer`) is
2095
- * needed. A self-loop or any multi-hop cycle terminates because a revisited id
2096
- * is never re-expanded.
2311
+ * single hop) and a visited-set guard. Nodes are canonicalized to their
2312
+ * `(scope, id)` string via {@link edgeTargetKey}, so a `Set<string>` visited-set
2313
+ * is the exact, collision-free cycle key — no structural hashing (e.g.
2314
+ * `Crc32Normalizer`) is needed. A self-loop or any multi-hop cycle terminates
2315
+ * because a revisited node is never re-expanded.
2097
2316
  * - **Post-filter.** The scope / kind / tag / predicate axes of the query are
2098
2317
  * applied to the reached records (the link axes are the traversal itself).
2099
2318
  * @public
@@ -2110,23 +2329,15 @@ export declare class LinkTraversalRetriever implements IMemoryRetriever {
2110
2329
  /** Run the bounded, cycle-safe BFS and post-filter the reached records. */
2111
2330
  private _traverse;
2112
2331
  /**
2113
- * Group the index's entries by bare {@link MemoryId}. An id can map to more
2114
- * than one entry when distinct scopes reuse a filename stem (e.g. `turn-0` in
2115
- * two conversations), so the value is an array.
2116
- *
2117
- * @remarks
2118
- * **Design note (links are globally-scoped identifiers in this phase).** An
2119
- * {@link IEdge.target} is a bare `MemoryId`, not a `(scope, id)` pair, so
2120
- * traversal resolves a target across ALL scopes that hold that id. When two
2121
- * scopes reuse a stem, following an edge to it reaches every match. This
2122
- * mirrors the `backlinks` index, which is also keyed by bare id. Scope-
2123
- * qualified link resolution is intentionally out of scope for Phase C and
2124
- * would be an additive change here (and to {@link IEdge} / the index).
2125
- */
2126
- private _indexById;
2127
- /** Outbound neighbors: the targets of every edge on the records with this id. */
2332
+ * Group the index's entries by their scope-qualified {@link edgeTargetKey}
2333
+ * `(scope, id)` composite. Each composite is the index's primary key, so it maps
2334
+ * to exactly one entry — two records that reuse a filename stem across scopes
2335
+ * (e.g. `turn-0` in two conversations) get distinct keys and never collide.
2336
+ */
2337
+ private _indexByKey;
2338
+ /** Outbound neighbors: the scope-qualified targets of every edge on the record at `node`. */
2128
2339
  private _outbound;
2129
- /** Inbound neighbors: the ids whose edges point AT this id (the backlinks). */
2340
+ /** Inbound neighbors: the scope-qualified sources whose edges point AT `node` (the backlinks). */
2130
2341
  private _inbound;
2131
2342
  }
2132
2343
 
@@ -2216,6 +2427,15 @@ export declare class MemoryCapCullPolicy implements IWritePolicy {
2216
2427
  private _rebuild;
2217
2428
  }
2218
2429
 
2430
+ /**
2431
+ * The detail tier a `memory_search` / `memory_context` result is projected at.
2432
+ * `'gist'` is the default (bounded) path; `'full'` is opt-in. Only meaningful
2433
+ * when a host {@link ICreateMemoryToolsParams.projectItem | projectItem} is
2434
+ * supplied — the built-in default projection returns the full body regardless.
2435
+ * @public
2436
+ */
2437
+ export declare type MemoryDetailTier = 'gist' | 'full';
2438
+
2219
2439
  /**
2220
2440
  * Embeds a complete record into a vector for the store's embed-on-write hook.
2221
2441
  * Async and `Result`-returning, since a real embedder does a network call (cloud
@@ -2248,10 +2468,13 @@ export declare class MemoryIndex implements IMemoryIndex {
2248
2468
  /** tag → set of composite keys. */
2249
2469
  private readonly _byTag;
2250
2470
  /**
2251
- * link target id → (source composite key → source id). Keyed by the source's
2252
- * `(scope, id)` composite NOT its bare id so two distinct source records
2253
- * that share an id across scopes (e.g. `turn-0` in different conversations)
2254
- * are tracked independently and removing one never drops the other's edge.
2471
+ * canonical target key (`edgeTargetKey`) → (source composite key → source
2472
+ * {@link IEdgeTarget}). The OUTER map is keyed on the scope-qualified target's
2473
+ * canonical `(scope, id)` string NOT the target's bare id — so an edge to
2474
+ * `turn-3` in one conversation is tracked separately from `turn-3` in another.
2475
+ * The INNER map is keyed by the source's `(scope, id)` composite so two distinct
2476
+ * source records that share an id across scopes are tracked independently and
2477
+ * removing one never drops the other's edge.
2255
2478
  */
2256
2479
  private readonly _backlinks;
2257
2480
  private constructor();
@@ -2276,19 +2499,38 @@ export declare class MemoryIndex implements IMemoryIndex {
2276
2499
  byTag(tag: Tag): ReadonlyArray<IMemoryRecord<unknown>>;
2277
2500
  /** {@inheritDoc IMemoryIndex.byRecency} */
2278
2501
  byRecency(): ReadonlyArray<IMemoryRecord<unknown>>;
2502
+ /** {@inheritDoc IMemoryIndex.byRank} */
2503
+ byRank(): ReadonlyArray<IMemoryRecord<unknown>>;
2279
2504
  /** {@inheritDoc IMemoryIndex.backlinks} */
2280
- backlinks(target: MemoryId): ReadonlyArray<MemoryId>;
2505
+ backlinks(target: IEdgeTarget): ReadonlyArray<IEdgeTarget>;
2281
2506
  /**
2282
2507
  * Resolve a set of composite keys to their records, ordered
2283
2508
  * most-recently-updated first (with a `seq` tiebreak so equal-`updated`
2284
2509
  * records sort deterministically).
2285
2510
  */
2286
2511
  private _recencyOrdered;
2512
+ /**
2513
+ * Resolve a set of composite keys to their records, ordered by
2514
+ * {@link IMemoryEnvelope.rank} descending with recency (`updated`, then `seq`)
2515
+ * as the tiebreak. Records with an absent `rank` sort LAST, then by recency
2516
+ * among themselves. Computed on call (mirrors {@link MemoryIndex._recencyOrdered}) —
2517
+ * no incremental rank-ordered view is maintained, matching the recency view's
2518
+ * approach; the sort is over the in-memory index, never a filesystem walk.
2519
+ */
2520
+ private _rankOrdered;
2521
+ /**
2522
+ * Rank-descending comparator with an absent-`rank`-last rule and a recency
2523
+ * (`updated`, then `seq`) tiebreak. Duplicated from the retrieve packlet's
2524
+ * `rankCompare` deliberately: the index must not depend on `retrieve` (that
2525
+ * package depends on the index), mirroring how `_recencyOrdered` inlines the
2526
+ * recency ordering rather than importing `recencyCompare`.
2527
+ */
2528
+ private static _compareByRank;
2287
2529
  /** Insert an entry and register all its derived associations. */
2288
2530
  private _add;
2289
2531
  /** Remove the entry at `key` (if present) and all its derived associations. */
2290
2532
  private _remove;
2291
- /** Register `sourceId` (keyed by its composite `sourceKey`) as linking at `target`. */
2533
+ /** Register `source` (keyed by its composite `sourceKey`) as linking at `target`. */
2292
2534
  private _addBacklink;
2293
2535
  /** Drop the backlink from `sourceKey` to `target`, removing the target map when empty. */
2294
2536
  private _removeBacklink;
@@ -2382,7 +2624,8 @@ export declare class MemoryIngestOrchestrator implements IMemoryIngestOrchestrat
2382
2624
  private _relate;
2383
2625
  /**
2384
2626
  * Validate stage-5 edges: each `source` must be a candidate being written; each
2385
- * `target` must resolve to a sibling candidate or an existing store record.
2627
+ * `target` must resolve to a sibling candidate or an existing store record. All
2628
+ * matching is on the canonical scope-qualified address.
2386
2629
  */
2387
2630
  private _validateEdges;
2388
2631
  /** Stage 6 — stamp provenance + edges, admit through the store, record the outcome. */
@@ -2397,10 +2640,23 @@ export declare class MemoryIngestOrchestrator implements IMemoryIngestOrchestrat
2397
2640
  private _resolveAddress;
2398
2641
  /** Run a host hook, normalizing a thrown/rejected hook into a Failure (never throws across the seam). */
2399
2642
  private _capture;
2400
- /** Index a record snapshot by id (last write wins on an id collision across scopes). */
2401
- private static _indexById;
2402
- /** Every existing outbound edge in the snapshot, as cycle-guard edges. */
2643
+ /**
2644
+ * Every existing outbound edge in the snapshot, as cycle-guard edges. The
2645
+ * source is the record's own scope-qualified address (already resolved by
2646
+ * {@link MemoryIngestOrchestrator._scopeRecords}); the target is the edge's
2647
+ * own scoped target. Both ends are scoped so the guard never conflates a stem
2648
+ * shared across scopes into one graph node.
2649
+ */
2403
2650
  private static _existingEdges;
2651
+ /**
2652
+ * Resolve every snapshot record's scope-qualified `(scope, id)` address via its
2653
+ * registered codec. Fails loudly if a record's kind has no resolvable codec —
2654
+ * the edge path cannot place an un-scopeable record in the graph, and a missing
2655
+ * codec for a stored kind is a real misconfiguration, not something to paper over.
2656
+ */
2657
+ private _scopeRecords;
2658
+ /** Human-readable `scope/id` rendering of a scoped target for edge-validation diagnostics. */
2659
+ private static _formatTarget;
2404
2660
  /** A provisional record for embedding a candidate (placeholder txn-time fields). */
2405
2661
  private static _provisionalRecord;
2406
2662
  /** Require a candidate body to be a string (the store persists only string bodies). */
@@ -2592,6 +2848,14 @@ export declare class MtmIdentityCodec implements IIdentityCodec {
2592
2848
  */
2593
2849
  export declare const NON_SEMANTIC_CAPABILITIES: IMemoryRetrieverCapabilities;
2594
2850
 
2851
+ /**
2852
+ * Select the record comparator for a query's {@link IMemoryQuery.orderBy | orderBy}
2853
+ * axis: {@link rankCompare} for `'rank'`, {@link recencyCompare} otherwise (the
2854
+ * default, byte-identical to the pre-`orderBy` behavior).
2855
+ * @public
2856
+ */
2857
+ export declare function orderingCompare(orderBy?: IMemoryQuery['orderBy']): (a: IMemoryRecord<unknown>, b: IMemoryRecord<unknown>) => number;
2858
+
2595
2859
  /**
2596
2860
  * Parse a complete memory file into a typed {@link IMemoryRecord}. Splits the
2597
2861
  * frontmatter, validates the envelope, and dispatches the body through the
@@ -2623,6 +2887,27 @@ export declare type ProvenanceSource = 'agent' | 'host-ingest' | 'human' | (stri
2623
2887
  */
2624
2888
  export declare type QueryEmbedder = (text: string) => Promise<Result<Float32Array>>;
2625
2889
 
2890
+ /**
2891
+ * Rank comparator: store-computed {@link IMemoryEnvelope.rank} descending, with
2892
+ * {@link recencyCompare} as the tiebreak. Records with an absent `rank` sort LAST
2893
+ * (after every ranked record), then by recency among themselves. Mirrors the
2894
+ * index's rank-view ordering.
2895
+ * @public
2896
+ */
2897
+ export declare function rankCompare(a: IMemoryRecord<unknown>, b: IMemoryRecord<unknown>): number;
2898
+
2899
+ /**
2900
+ * A per-kind host projection from a fully-resolved (post-merge) memory record
2901
+ * to a numeric ordering value. Registered per kind at store construction (see
2902
+ * `rankProjectors`); the store runs it on every put/update over the same
2903
+ * resolved record whose `contentHash` it computes, stamping the result into
2904
+ * {@link IMemoryEnvelope.rank}. The store never interprets the body — the host
2905
+ * owns what the number means. A projector that throws is treated as "no rank
2906
+ * for this record" (logged at `warn`), never failing the write.
2907
+ * @public
2908
+ */
2909
+ export declare type RankProjector = (record: IMemoryRecord<unknown>) => number;
2910
+
2626
2911
  /**
2627
2912
  * Recency comparator: most-recently-updated first, with a `seq` tiebreak so
2628
2913
  * equal-`updated` records sort deterministically. Mirrors the B1 index ordering.
@@ -2650,20 +2935,22 @@ export declare class RecencyRetriever implements IMemoryRetriever {
2650
2935
  /**
2651
2936
  * The four dedup verdicts a {@link IEntityResolver} (or fgv's exact-match layer)
2652
2937
  * returns for a candidate. See the design note §3 for the verdict → write
2653
- * disposition mapping.
2938
+ * disposition mapping. Each target-bearing arm carries a scope-qualified
2939
+ * {@link IEdgeTarget} (not a bare {@link MemoryId}) so the verdict resolves to a
2940
+ * single record even when a filename stem is reused across scopes.
2654
2941
  * @public
2655
2942
  */
2656
2943
  export declare type ResolutionVerdict = {
2657
2944
  readonly verdict: 'new';
2658
2945
  } | {
2659
2946
  readonly verdict: 'duplicate-of';
2660
- readonly target: MemoryId;
2947
+ readonly target: IEdgeTarget;
2661
2948
  } | {
2662
2949
  readonly verdict: 'supersede';
2663
- readonly target: MemoryId;
2950
+ readonly target: IEdgeTarget;
2664
2951
  } | {
2665
2952
  readonly verdict: 'merge-into';
2666
- readonly target: MemoryId;
2953
+ readonly target: IEdgeTarget;
2667
2954
  };
2668
2955
 
2669
2956
  /**