@fgv/ts-agent-memory 5.1.0-36

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 (331) hide show
  1. package/.rush/temp/chunked-rush-logs/ts-agent-memory.build.chunks.jsonl +9 -0
  2. package/.rush/temp/f6a88bfdd66517ccb98c2c7ae1be6e6fe9e15d38.tar.log +282 -0
  3. package/.rush/temp/operation/build/all.log +9 -0
  4. package/.rush/temp/operation/build/log-chunks.jsonl +9 -0
  5. package/.rush/temp/operation/build/state.json +3 -0
  6. package/.rush/temp/shrinkwrap-deps.json +688 -0
  7. package/LICENSE +21 -0
  8. package/README.md +45 -0
  9. package/config/api-extractor.json +343 -0
  10. package/config/jest.config.json +14 -0
  11. package/config/rig.json +4 -0
  12. package/dist/index.js +12 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/packlets/converters/bodyConverterRegistry.js +51 -0
  15. package/dist/packlets/converters/bodyConverterRegistry.js.map +1 -0
  16. package/dist/packlets/converters/envelopeConverter.js +159 -0
  17. package/dist/packlets/converters/envelopeConverter.js.map +1 -0
  18. package/dist/packlets/converters/index.js +7 -0
  19. package/dist/packlets/converters/index.js.map +1 -0
  20. package/dist/packlets/index/index.js +6 -0
  21. package/dist/packlets/index/index.js.map +1 -0
  22. package/dist/packlets/index/memoryIndex.js +170 -0
  23. package/dist/packlets/index/memoryIndex.js.map +1 -0
  24. package/dist/packlets/observe/index.js +7 -0
  25. package/dist/packlets/observe/index.js.map +1 -0
  26. package/dist/packlets/observe/memoryObservationStore.js +119 -0
  27. package/dist/packlets/observe/memoryObservationStore.js.map +1 -0
  28. package/dist/packlets/observe/observer.js +6 -0
  29. package/dist/packlets/observe/observer.js.map +1 -0
  30. package/dist/packlets/retrieve/hybridRetriever.js +135 -0
  31. package/dist/packlets/retrieve/hybridRetriever.js.map +1 -0
  32. package/dist/packlets/retrieve/index.js +12 -0
  33. package/dist/packlets/retrieve/index.js.map +1 -0
  34. package/dist/packlets/retrieve/linkTraversalRetriever.js +143 -0
  35. package/dist/packlets/retrieve/linkTraversalRetriever.js.map +1 -0
  36. package/dist/packlets/retrieve/recencyRetriever.js +33 -0
  37. package/dist/packlets/retrieve/recencyRetriever.js.map +1 -0
  38. package/dist/packlets/retrieve/retriever.js +110 -0
  39. package/dist/packlets/retrieve/retriever.js.map +1 -0
  40. package/dist/packlets/retrieve/semanticRetriever.js +86 -0
  41. package/dist/packlets/retrieve/semanticRetriever.js.map +1 -0
  42. package/dist/packlets/retrieve/structuredFilterRetriever.js +37 -0
  43. package/dist/packlets/retrieve/structuredFilterRetriever.js.map +1 -0
  44. package/dist/packlets/retrieve/tagRetriever.js +37 -0
  45. package/dist/packlets/retrieve/tagRetriever.js.map +1 -0
  46. package/dist/packlets/store/fileTreeMemoryStore.js +698 -0
  47. package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -0
  48. package/dist/packlets/store/index.js +7 -0
  49. package/dist/packlets/store/index.js.map +1 -0
  50. package/dist/packlets/store/scopeEncoding.js +31 -0
  51. package/dist/packlets/store/scopeEncoding.js.map +1 -0
  52. package/dist/packlets/types/envelope.js +6 -0
  53. package/dist/packlets/types/envelope.js.map +1 -0
  54. package/dist/packlets/types/filenameSafety.js +52 -0
  55. package/dist/packlets/types/filenameSafety.js.map +1 -0
  56. package/dist/packlets/types/identityCodec.js +184 -0
  57. package/dist/packlets/types/identityCodec.js.map +1 -0
  58. package/dist/packlets/types/ids.js +67 -0
  59. package/dist/packlets/types/ids.js.map +1 -0
  60. package/dist/packlets/types/index.js +10 -0
  61. package/dist/packlets/types/index.js.map +1 -0
  62. package/dist/packlets/types/writePolicy.js +263 -0
  63. package/dist/packlets/types/writePolicy.js.map +1 -0
  64. package/dist/packlets/vector/inMemoryCosineIndex.js +150 -0
  65. package/dist/packlets/vector/inMemoryCosineIndex.js.map +1 -0
  66. package/dist/packlets/vector/index.js +7 -0
  67. package/dist/packlets/vector/index.js.map +1 -0
  68. package/dist/packlets/vector/vectorIndex.js +6 -0
  69. package/dist/packlets/vector/vectorIndex.js.map +1 -0
  70. package/dist/test/unit/converters/bodyConverterRegistry.test.js +72 -0
  71. package/dist/test/unit/converters/bodyConverterRegistry.test.js.map +1 -0
  72. package/dist/test/unit/converters/envelopeConverter.test.js +196 -0
  73. package/dist/test/unit/converters/envelopeConverter.test.js.map +1 -0
  74. package/dist/test/unit/index/memoryIndex.test.js +152 -0
  75. package/dist/test/unit/index/memoryIndex.test.js.map +1 -0
  76. package/dist/test/unit/observe/memoryObservationStore.test.js +118 -0
  77. package/dist/test/unit/observe/memoryObservationStore.test.js.map +1 -0
  78. package/dist/test/unit/retrieve/linkTraversalRetriever.test.js +182 -0
  79. package/dist/test/unit/retrieve/linkTraversalRetriever.test.js.map +1 -0
  80. package/dist/test/unit/retrieve/retrievers.test.js +506 -0
  81. package/dist/test/unit/retrieve/retrievers.test.js.map +1 -0
  82. package/dist/test/unit/store/embedOnWrite.test.js +260 -0
  83. package/dist/test/unit/store/embedOnWrite.test.js.map +1 -0
  84. package/dist/test/unit/store/fileTreeMemoryStore.test.js +647 -0
  85. package/dist/test/unit/store/fileTreeMemoryStore.test.js.map +1 -0
  86. package/dist/test/unit/store/observations.test.js +239 -0
  87. package/dist/test/unit/store/observations.test.js.map +1 -0
  88. package/dist/test/unit/store/scopeEncoding.test.js +24 -0
  89. package/dist/test/unit/store/scopeEncoding.test.js.map +1 -0
  90. package/dist/test/unit/types/identityCodec.test.js +187 -0
  91. package/dist/test/unit/types/identityCodec.test.js.map +1 -0
  92. package/dist/test/unit/types/ids.test.js +84 -0
  93. package/dist/test/unit/types/ids.test.js.map +1 -0
  94. package/dist/test/unit/types/writePolicy.test.js +241 -0
  95. package/dist/test/unit/types/writePolicy.test.js.map +1 -0
  96. package/dist/test/unit/vector/inMemoryCosineIndex.test.js +192 -0
  97. package/dist/test/unit/vector/inMemoryCosineIndex.test.js.map +1 -0
  98. package/dist/test/unit/vector/vectorIndex.test.js +42 -0
  99. package/dist/test/unit/vector/vectorIndex.test.js.map +1 -0
  100. package/dist/ts-agent-memory.d.ts +1901 -0
  101. package/dist/tsdoc-metadata.json +11 -0
  102. package/eslint.config.js +15 -0
  103. package/etc/ts-agent-memory.api.md +525 -0
  104. package/lib/index.d.ts +8 -0
  105. package/lib/index.d.ts.map +1 -0
  106. package/lib/index.js +28 -0
  107. package/lib/index.js.map +1 -0
  108. package/lib/packlets/converters/bodyConverterRegistry.d.ts +64 -0
  109. package/lib/packlets/converters/bodyConverterRegistry.d.ts.map +1 -0
  110. package/lib/packlets/converters/bodyConverterRegistry.js +55 -0
  111. package/lib/packlets/converters/bodyConverterRegistry.js.map +1 -0
  112. package/lib/packlets/converters/envelopeConverter.d.ts +70 -0
  113. package/lib/packlets/converters/envelopeConverter.d.ts.map +1 -0
  114. package/lib/packlets/converters/envelopeConverter.js +166 -0
  115. package/lib/packlets/converters/envelopeConverter.js.map +1 -0
  116. package/lib/packlets/converters/index.d.ts +3 -0
  117. package/lib/packlets/converters/index.d.ts.map +1 -0
  118. package/lib/packlets/converters/index.js +23 -0
  119. package/lib/packlets/converters/index.js.map +1 -0
  120. package/lib/packlets/index/index.d.ts +2 -0
  121. package/lib/packlets/index/index.d.ts.map +1 -0
  122. package/lib/packlets/index/index.js +22 -0
  123. package/lib/packlets/index/index.js.map +1 -0
  124. package/lib/packlets/index/memoryIndex.d.ts +127 -0
  125. package/lib/packlets/index/memoryIndex.d.ts.map +1 -0
  126. package/lib/packlets/index/memoryIndex.js +174 -0
  127. package/lib/packlets/index/memoryIndex.js.map +1 -0
  128. package/lib/packlets/observe/index.d.ts +3 -0
  129. package/lib/packlets/observe/index.d.ts.map +1 -0
  130. package/lib/packlets/observe/index.js +23 -0
  131. package/lib/packlets/observe/index.js.map +1 -0
  132. package/lib/packlets/observe/memoryObservationStore.d.ts +91 -0
  133. package/lib/packlets/observe/memoryObservationStore.d.ts.map +1 -0
  134. package/lib/packlets/observe/memoryObservationStore.js +123 -0
  135. package/lib/packlets/observe/memoryObservationStore.js.map +1 -0
  136. package/lib/packlets/observe/observer.d.ts +110 -0
  137. package/lib/packlets/observe/observer.d.ts.map +1 -0
  138. package/lib/packlets/observe/observer.js +7 -0
  139. package/lib/packlets/observe/observer.js.map +1 -0
  140. package/lib/packlets/retrieve/hybridRetriever.d.ts +79 -0
  141. package/lib/packlets/retrieve/hybridRetriever.d.ts.map +1 -0
  142. package/lib/packlets/retrieve/hybridRetriever.js +140 -0
  143. package/lib/packlets/retrieve/hybridRetriever.js.map +1 -0
  144. package/lib/packlets/retrieve/index.d.ts +8 -0
  145. package/lib/packlets/retrieve/index.d.ts.map +1 -0
  146. package/lib/packlets/retrieve/index.js +28 -0
  147. package/lib/packlets/retrieve/index.js.map +1 -0
  148. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts +61 -0
  149. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts.map +1 -0
  150. package/lib/packlets/retrieve/linkTraversalRetriever.js +147 -0
  151. package/lib/packlets/retrieve/linkTraversalRetriever.js.map +1 -0
  152. package/lib/packlets/retrieve/recencyRetriever.d.ts +21 -0
  153. package/lib/packlets/retrieve/recencyRetriever.d.ts.map +1 -0
  154. package/lib/packlets/retrieve/recencyRetriever.js +37 -0
  155. package/lib/packlets/retrieve/recencyRetriever.js.map +1 -0
  156. package/lib/packlets/retrieve/retriever.d.ts +133 -0
  157. package/lib/packlets/retrieve/retriever.d.ts.map +1 -0
  158. package/lib/packlets/retrieve/retriever.js +119 -0
  159. package/lib/packlets/retrieve/retriever.js.map +1 -0
  160. package/lib/packlets/retrieve/semanticRetriever.d.ts +69 -0
  161. package/lib/packlets/retrieve/semanticRetriever.d.ts.map +1 -0
  162. package/lib/packlets/retrieve/semanticRetriever.js +90 -0
  163. package/lib/packlets/retrieve/semanticRetriever.js.map +1 -0
  164. package/lib/packlets/retrieve/structuredFilterRetriever.d.ts +22 -0
  165. package/lib/packlets/retrieve/structuredFilterRetriever.d.ts.map +1 -0
  166. package/lib/packlets/retrieve/structuredFilterRetriever.js +41 -0
  167. package/lib/packlets/retrieve/structuredFilterRetriever.js.map +1 -0
  168. package/lib/packlets/retrieve/tagRetriever.d.ts +22 -0
  169. package/lib/packlets/retrieve/tagRetriever.d.ts.map +1 -0
  170. package/lib/packlets/retrieve/tagRetriever.js +41 -0
  171. package/lib/packlets/retrieve/tagRetriever.js.map +1 -0
  172. package/lib/packlets/store/fileTreeMemoryStore.d.ts +327 -0
  173. package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -0
  174. package/lib/packlets/store/fileTreeMemoryStore.js +702 -0
  175. package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -0
  176. package/lib/packlets/store/index.d.ts +3 -0
  177. package/lib/packlets/store/index.d.ts.map +1 -0
  178. package/lib/packlets/store/index.js +23 -0
  179. package/lib/packlets/store/index.js.map +1 -0
  180. package/lib/packlets/store/scopeEncoding.d.ts +19 -0
  181. package/lib/packlets/store/scopeEncoding.d.ts.map +1 -0
  182. package/lib/packlets/store/scopeEncoding.js +34 -0
  183. package/lib/packlets/store/scopeEncoding.js.map +1 -0
  184. package/lib/packlets/types/envelope.d.ts +119 -0
  185. package/lib/packlets/types/envelope.d.ts.map +1 -0
  186. package/lib/packlets/types/envelope.js +7 -0
  187. package/lib/packlets/types/envelope.js.map +1 -0
  188. package/lib/packlets/types/filenameSafety.d.ts +16 -0
  189. package/lib/packlets/types/filenameSafety.d.ts.map +1 -0
  190. package/lib/packlets/types/filenameSafety.js +55 -0
  191. package/lib/packlets/types/filenameSafety.js.map +1 -0
  192. package/lib/packlets/types/identityCodec.d.ts +136 -0
  193. package/lib/packlets/types/identityCodec.d.ts.map +1 -0
  194. package/lib/packlets/types/identityCodec.js +190 -0
  195. package/lib/packlets/types/identityCodec.js.map +1 -0
  196. package/lib/packlets/types/ids.d.ts +55 -0
  197. package/lib/packlets/types/ids.d.ts.map +1 -0
  198. package/lib/packlets/types/ids.js +70 -0
  199. package/lib/packlets/types/ids.js.map +1 -0
  200. package/lib/packlets/types/index.d.ts +6 -0
  201. package/lib/packlets/types/index.d.ts.map +1 -0
  202. package/lib/packlets/types/index.js +26 -0
  203. package/lib/packlets/types/index.js.map +1 -0
  204. package/lib/packlets/types/writePolicy.d.ts +213 -0
  205. package/lib/packlets/types/writePolicy.d.ts.map +1 -0
  206. package/lib/packlets/types/writePolicy.js +268 -0
  207. package/lib/packlets/types/writePolicy.js.map +1 -0
  208. package/lib/packlets/vector/inMemoryCosineIndex.d.ts +69 -0
  209. package/lib/packlets/vector/inMemoryCosineIndex.d.ts.map +1 -0
  210. package/lib/packlets/vector/inMemoryCosineIndex.js +154 -0
  211. package/lib/packlets/vector/inMemoryCosineIndex.js.map +1 -0
  212. package/lib/packlets/vector/index.d.ts +3 -0
  213. package/lib/packlets/vector/index.d.ts.map +1 -0
  214. package/lib/packlets/vector/index.js +23 -0
  215. package/lib/packlets/vector/index.js.map +1 -0
  216. package/lib/packlets/vector/vectorIndex.d.ts +68 -0
  217. package/lib/packlets/vector/vectorIndex.d.ts.map +1 -0
  218. package/lib/packlets/vector/vectorIndex.js +7 -0
  219. package/lib/packlets/vector/vectorIndex.js.map +1 -0
  220. package/lib/test/unit/converters/bodyConverterRegistry.test.d.ts +2 -0
  221. package/lib/test/unit/converters/bodyConverterRegistry.test.d.ts.map +1 -0
  222. package/lib/test/unit/converters/bodyConverterRegistry.test.js +74 -0
  223. package/lib/test/unit/converters/bodyConverterRegistry.test.js.map +1 -0
  224. package/lib/test/unit/converters/envelopeConverter.test.d.ts +2 -0
  225. package/lib/test/unit/converters/envelopeConverter.test.d.ts.map +1 -0
  226. package/lib/test/unit/converters/envelopeConverter.test.js +198 -0
  227. package/lib/test/unit/converters/envelopeConverter.test.js.map +1 -0
  228. package/lib/test/unit/index/memoryIndex.test.d.ts +2 -0
  229. package/lib/test/unit/index/memoryIndex.test.d.ts.map +1 -0
  230. package/lib/test/unit/index/memoryIndex.test.js +154 -0
  231. package/lib/test/unit/index/memoryIndex.test.js.map +1 -0
  232. package/lib/test/unit/observe/memoryObservationStore.test.d.ts +2 -0
  233. package/lib/test/unit/observe/memoryObservationStore.test.d.ts.map +1 -0
  234. package/lib/test/unit/observe/memoryObservationStore.test.js +120 -0
  235. package/lib/test/unit/observe/memoryObservationStore.test.js.map +1 -0
  236. package/lib/test/unit/retrieve/linkTraversalRetriever.test.d.ts +2 -0
  237. package/lib/test/unit/retrieve/linkTraversalRetriever.test.d.ts.map +1 -0
  238. package/lib/test/unit/retrieve/linkTraversalRetriever.test.js +184 -0
  239. package/lib/test/unit/retrieve/linkTraversalRetriever.test.js.map +1 -0
  240. package/lib/test/unit/retrieve/retrievers.test.d.ts +2 -0
  241. package/lib/test/unit/retrieve/retrievers.test.d.ts.map +1 -0
  242. package/lib/test/unit/retrieve/retrievers.test.js +508 -0
  243. package/lib/test/unit/retrieve/retrievers.test.js.map +1 -0
  244. package/lib/test/unit/store/embedOnWrite.test.d.ts +2 -0
  245. package/lib/test/unit/store/embedOnWrite.test.d.ts.map +1 -0
  246. package/lib/test/unit/store/embedOnWrite.test.js +262 -0
  247. package/lib/test/unit/store/embedOnWrite.test.js.map +1 -0
  248. package/lib/test/unit/store/fileTreeMemoryStore.test.d.ts +2 -0
  249. package/lib/test/unit/store/fileTreeMemoryStore.test.d.ts.map +1 -0
  250. package/lib/test/unit/store/fileTreeMemoryStore.test.js +649 -0
  251. package/lib/test/unit/store/fileTreeMemoryStore.test.js.map +1 -0
  252. package/lib/test/unit/store/observations.test.d.ts +2 -0
  253. package/lib/test/unit/store/observations.test.d.ts.map +1 -0
  254. package/lib/test/unit/store/observations.test.js +241 -0
  255. package/lib/test/unit/store/observations.test.js.map +1 -0
  256. package/lib/test/unit/store/scopeEncoding.test.d.ts +2 -0
  257. package/lib/test/unit/store/scopeEncoding.test.d.ts.map +1 -0
  258. package/lib/test/unit/store/scopeEncoding.test.js +26 -0
  259. package/lib/test/unit/store/scopeEncoding.test.js.map +1 -0
  260. package/lib/test/unit/types/identityCodec.test.d.ts +2 -0
  261. package/lib/test/unit/types/identityCodec.test.d.ts.map +1 -0
  262. package/lib/test/unit/types/identityCodec.test.js +189 -0
  263. package/lib/test/unit/types/identityCodec.test.js.map +1 -0
  264. package/lib/test/unit/types/ids.test.d.ts +2 -0
  265. package/lib/test/unit/types/ids.test.d.ts.map +1 -0
  266. package/lib/test/unit/types/ids.test.js +86 -0
  267. package/lib/test/unit/types/ids.test.js.map +1 -0
  268. package/lib/test/unit/types/writePolicy.test.d.ts +2 -0
  269. package/lib/test/unit/types/writePolicy.test.d.ts.map +1 -0
  270. package/lib/test/unit/types/writePolicy.test.js +243 -0
  271. package/lib/test/unit/types/writePolicy.test.js.map +1 -0
  272. package/lib/test/unit/vector/inMemoryCosineIndex.test.d.ts +2 -0
  273. package/lib/test/unit/vector/inMemoryCosineIndex.test.d.ts.map +1 -0
  274. package/lib/test/unit/vector/inMemoryCosineIndex.test.js +194 -0
  275. package/lib/test/unit/vector/inMemoryCosineIndex.test.js.map +1 -0
  276. package/lib/test/unit/vector/vectorIndex.test.d.ts +2 -0
  277. package/lib/test/unit/vector/vectorIndex.test.d.ts.map +1 -0
  278. package/lib/test/unit/vector/vectorIndex.test.js +44 -0
  279. package/lib/test/unit/vector/vectorIndex.test.js.map +1 -0
  280. package/package.json +81 -0
  281. package/rush-logs/ts-agent-memory.build.cache.log +3 -0
  282. package/rush-logs/ts-agent-memory.build.log +9 -0
  283. package/src/index.ts +12 -0
  284. package/src/packlets/converters/bodyConverterRegistry.ts +105 -0
  285. package/src/packlets/converters/envelopeConverter.ts +210 -0
  286. package/src/packlets/converters/index.ts +7 -0
  287. package/src/packlets/index/index.ts +6 -0
  288. package/src/packlets/index/memoryIndex.ts +268 -0
  289. package/src/packlets/observe/index.ts +7 -0
  290. package/src/packlets/observe/memoryObservationStore.ts +153 -0
  291. package/src/packlets/observe/observer.ts +119 -0
  292. package/src/packlets/retrieve/hybridRetriever.ts +181 -0
  293. package/src/packlets/retrieve/index.ts +12 -0
  294. package/src/packlets/retrieve/linkTraversalRetriever.ts +169 -0
  295. package/src/packlets/retrieve/recencyRetriever.ts +54 -0
  296. package/src/packlets/retrieve/retriever.ts +207 -0
  297. package/src/packlets/retrieve/semanticRetriever.ts +147 -0
  298. package/src/packlets/retrieve/structuredFilterRetriever.ts +58 -0
  299. package/src/packlets/retrieve/tagRetriever.ts +58 -0
  300. package/src/packlets/store/fileTreeMemoryStore.ts +1073 -0
  301. package/src/packlets/store/index.ts +7 -0
  302. package/src/packlets/store/scopeEncoding.ts +36 -0
  303. package/src/packlets/types/envelope.ts +138 -0
  304. package/src/packlets/types/filenameSafety.ts +57 -0
  305. package/src/packlets/types/identityCodec.ts +263 -0
  306. package/src/packlets/types/ids.ts +124 -0
  307. package/src/packlets/types/index.ts +10 -0
  308. package/src/packlets/types/writePolicy.ts +447 -0
  309. package/src/packlets/vector/inMemoryCosineIndex.ts +173 -0
  310. package/src/packlets/vector/index.ts +7 -0
  311. package/src/packlets/vector/vectorIndex.ts +78 -0
  312. package/src/test/unit/converters/bodyConverterRegistry.test.ts +89 -0
  313. package/src/test/unit/converters/envelopeConverter.test.ts +261 -0
  314. package/src/test/unit/index/memoryIndex.test.ts +187 -0
  315. package/src/test/unit/observe/memoryObservationStore.test.ts +158 -0
  316. package/src/test/unit/retrieve/linkTraversalRetriever.test.ts +230 -0
  317. package/src/test/unit/retrieve/retrievers.test.ts +662 -0
  318. package/src/test/unit/store/embedOnWrite.test.ts +346 -0
  319. package/src/test/unit/store/fileTreeMemoryStore.test.ts +875 -0
  320. package/src/test/unit/store/observations.test.ts +290 -0
  321. package/src/test/unit/store/scopeEncoding.test.ts +37 -0
  322. package/src/test/unit/types/identityCodec.test.ts +266 -0
  323. package/src/test/unit/types/ids.test.ts +94 -0
  324. package/src/test/unit/types/writePolicy.test.ts +325 -0
  325. package/src/test/unit/vector/inMemoryCosineIndex.test.ts +242 -0
  326. package/src/test/unit/vector/vectorIndex.test.ts +48 -0
  327. package/temp/build/lint/_eslint-5eVG3S6w.json +182 -0
  328. package/temp/build/typescript/ts_8nwakTlr.json +1 -0
  329. package/temp/ts-agent-memory.api.json +12438 -0
  330. package/temp/ts-agent-memory.api.md +525 -0
  331. package/tsconfig.json +8 -0
@@ -0,0 +1,79 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { IMemoryRecord } from '../types';
3
+ import { IMemoryQuery, IMemoryRetriever, IMemoryRetrieverCapabilities } from './retriever';
4
+ /**
5
+ * Strategy for combining the result sets of the retrievers a
6
+ * {@link HybridRetriever} composes. Injectable so a consumer can weight,
7
+ * re-rank, or intersect instead of the default union.
8
+ * @public
9
+ */
10
+ export interface IMergeStrategy {
11
+ /**
12
+ * Merge the per-retriever result sets into a single ordered result.
13
+ * @param resultSets - One entry per composed retriever, in composition order.
14
+ */
15
+ merge(resultSets: ReadonlyArray<ReadonlyArray<IMemoryRecord<unknown>>>): Result<ReadonlyArray<IMemoryRecord<unknown>>>;
16
+ }
17
+ /**
18
+ * The reference {@link IMergeStrategy}: a score-union. Every record is scored by
19
+ * the number of composed result sets it appears in (deduplicated by
20
+ * {@link IMemoryEnvelope.id | id}); the merged result is ordered by descending
21
+ * score, then by recency. Records surfaced by more retrievers rank higher.
22
+ *
23
+ * @remarks
24
+ * Dedup is by `id` alone, which is unambiguous for the flat (one-file-per-entity)
25
+ * kinds B2 ships. Phase-C versioned kinds that reuse a stem across scopes will
26
+ * extend the dedup key — additive, no API change.
27
+ * @public
28
+ */
29
+ export declare class ScoreUnionMergeStrategy implements IMergeStrategy {
30
+ private constructor();
31
+ /** Family-convention factory. */
32
+ static create(): Result<ScoreUnionMergeStrategy>;
33
+ /** {@inheritDoc IMergeStrategy.merge} */
34
+ merge(resultSets: ReadonlyArray<ReadonlyArray<IMemoryRecord<unknown>>>): Result<ReadonlyArray<IMemoryRecord<unknown>>>;
35
+ }
36
+ /**
37
+ * Composes several retrievers, dispatches a query to each, and merges their
38
+ * results via an injectable {@link IMergeStrategy}. Its capabilities are the
39
+ * union of the composed retrievers' capabilities.
40
+ *
41
+ * @remarks
42
+ * The hybrid enforces the loud-degradation contract against its OWN (union)
43
+ * capabilities, then projects the query for each child: a child that does not
44
+ * support the `semantic` (or `asOf`) axis is handed a query with that axis
45
+ * stripped, so it returns its normal results instead of loud-failing on a field
46
+ * a sibling handles. A child that genuinely fails (e.g. a wired semantic backend
47
+ * erroring) propagates — the hybrid never silently drops a failure.
48
+ * @public
49
+ */
50
+ export declare class HybridRetriever implements IMemoryRetriever {
51
+ private readonly _retrievers;
52
+ private readonly _mergeStrategy;
53
+ private readonly _capabilities;
54
+ private constructor();
55
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
56
+ get capabilities(): IMemoryRetrieverCapabilities;
57
+ /**
58
+ * Family-convention factory.
59
+ * @param retrievers - The retrievers to compose (at least one).
60
+ * @param mergeStrategy - How to combine their results.
61
+ */
62
+ static create(retrievers: ReadonlyArray<IMemoryRetriever>, mergeStrategy: IMergeStrategy): Result<HybridRetriever>;
63
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
64
+ retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
65
+ /**
66
+ * Project the query for one child retriever. Two adjustments:
67
+ *
68
+ * - Strip axes the child does not support (`semantic` / `topK` for a
69
+ * non-semantic child, `asOf` for a non-temporal child, the link axes for a
70
+ * non-link child) so it returns its normal results rather than loud-failing
71
+ * on a field a sibling handles.
72
+ * - Strip `limit` unconditionally: limit is a post-merge concern. A child that
73
+ * pre-truncated its result set would starve the merge strategy of candidates
74
+ * it needs to score correctly (a record both children would surface must
75
+ * reach the merge to score 2). The hybrid applies `limit` once, after merge.
76
+ */
77
+ private _projectQuery;
78
+ }
79
+ //# sourceMappingURL=hybridRetriever.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybridRetriever.d.ts","sourceRoot":"","sources":["../../../src/packlets/retrieve/hybridRetriever.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAA6B,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,aAAa,EAAY,MAAM,UAAU,CAAC;AACnD,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,4BAA4B,EAI7B,MAAM,aAAa,CAAC;AAErB;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,KAAK,CACH,UAAU,EAAE,aAAa,CAAC,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,GAC/D,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;CAClD;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,uBAAwB,YAAW,cAAc;IAC5D,OAAO;IAEP,iCAAiC;WACnB,MAAM,IAAI,MAAM,CAAC,uBAAuB,CAAC;IAIvD,yCAAyC;IAClC,KAAK,CACV,UAAU,EAAE,aAAa,CAAC,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,GAC/D,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;CAyBjD;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;IACtD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkC;IAC9D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA+B;IAE7D,OAAO;IAUP,kDAAkD;IAClD,IAAW,YAAY,IAAI,4BAA4B,CAEtD;IAED;;;;OAIG;WACW,MAAM,CAClB,UAAU,EAAE,aAAa,CAAC,gBAAgB,CAAC,EAC3C,aAAa,EAAE,cAAc,GAC5B,MAAM,CAAC,eAAe,CAAC;IAe1B,8CAA8C;IACjC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAWlG;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,aAAa;CAiBtB"}
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2026 Erik Fortune
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.HybridRetriever = exports.ScoreUnionMergeStrategy = void 0;
8
+ const ts_utils_1 = require("@fgv/ts-utils");
9
+ const retriever_1 = require("./retriever");
10
+ /**
11
+ * The reference {@link IMergeStrategy}: a score-union. Every record is scored by
12
+ * the number of composed result sets it appears in (deduplicated by
13
+ * {@link IMemoryEnvelope.id | id}); the merged result is ordered by descending
14
+ * score, then by recency. Records surfaced by more retrievers rank higher.
15
+ *
16
+ * @remarks
17
+ * Dedup is by `id` alone, which is unambiguous for the flat (one-file-per-entity)
18
+ * kinds B2 ships. Phase-C versioned kinds that reuse a stem across scopes will
19
+ * extend the dedup key — additive, no API change.
20
+ * @public
21
+ */
22
+ class ScoreUnionMergeStrategy {
23
+ constructor() { }
24
+ /** Family-convention factory. */
25
+ static create() {
26
+ return (0, ts_utils_1.succeed)(new ScoreUnionMergeStrategy());
27
+ }
28
+ /** {@inheritDoc IMergeStrategy.merge} */
29
+ merge(resultSets) {
30
+ const scored = new Map();
31
+ for (const set of resultSets) {
32
+ // Count each id at most once per result set, so the score is "how many
33
+ // retrievers surfaced this record" rather than "how many copies total".
34
+ const seenInSet = new Set();
35
+ for (const record of set) {
36
+ const id = record.envelope.id;
37
+ if (seenInSet.has(id)) {
38
+ continue;
39
+ }
40
+ seenInSet.add(id);
41
+ const existing = scored.get(id);
42
+ if (existing === undefined) {
43
+ scored.set(id, { record, score: 1 });
44
+ }
45
+ else {
46
+ existing.score += 1;
47
+ }
48
+ }
49
+ }
50
+ const merged = Array.from(scored.values())
51
+ .sort((a, b) => (b.score !== a.score ? b.score - a.score : (0, retriever_1.recencyCompare)(a.record, b.record)))
52
+ .map((entry) => entry.record);
53
+ return (0, ts_utils_1.succeed)(merged);
54
+ }
55
+ }
56
+ exports.ScoreUnionMergeStrategy = ScoreUnionMergeStrategy;
57
+ /**
58
+ * Composes several retrievers, dispatches a query to each, and merges their
59
+ * results via an injectable {@link IMergeStrategy}. Its capabilities are the
60
+ * union of the composed retrievers' capabilities.
61
+ *
62
+ * @remarks
63
+ * The hybrid enforces the loud-degradation contract against its OWN (union)
64
+ * capabilities, then projects the query for each child: a child that does not
65
+ * support the `semantic` (or `asOf`) axis is handed a query with that axis
66
+ * stripped, so it returns its normal results instead of loud-failing on a field
67
+ * a sibling handles. A child that genuinely fails (e.g. a wired semantic backend
68
+ * erroring) propagates — the hybrid never silently drops a failure.
69
+ * @public
70
+ */
71
+ class HybridRetriever {
72
+ constructor(retrievers, mergeStrategy, capabilities) {
73
+ this._retrievers = retrievers;
74
+ this._mergeStrategy = mergeStrategy;
75
+ this._capabilities = capabilities;
76
+ }
77
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
78
+ get capabilities() {
79
+ return this._capabilities;
80
+ }
81
+ /**
82
+ * Family-convention factory.
83
+ * @param retrievers - The retrievers to compose (at least one).
84
+ * @param mergeStrategy - How to combine their results.
85
+ */
86
+ static create(retrievers, mergeStrategy) {
87
+ if (retrievers.length === 0) {
88
+ return (0, ts_utils_1.fail)('HybridRetriever: at least one retriever is required');
89
+ }
90
+ // Snapshot the caller's array so a later mutation cannot make `retrieve()`
91
+ // and the cached `capabilities` disagree.
92
+ const stableRetrievers = [...retrievers];
93
+ const capabilities = {
94
+ supportsSemanticRecall: stableRetrievers.some((r) => r.capabilities.supportsSemanticRecall),
95
+ supportsTemporalQuery: stableRetrievers.some((r) => r.capabilities.supportsTemporalQuery),
96
+ supportsLinkTraversal: stableRetrievers.some((r) => r.capabilities.supportsLinkTraversal)
97
+ };
98
+ return (0, ts_utils_1.succeed)(new HybridRetriever(stableRetrievers, mergeStrategy, capabilities));
99
+ }
100
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
101
+ async retrieve(query) {
102
+ return (0, retriever_1.guardRetrieverCapabilities)(query, this._capabilities).thenOnSuccess(async () => {
103
+ const perRetriever = await Promise.all(this._retrievers.map((retriever) => retriever.retrieve(this._projectQuery(query, retriever))));
104
+ return (0, ts_utils_1.mapResults)(perRetriever)
105
+ .onSuccess((resultSets) => this._mergeStrategy.merge(resultSets))
106
+ .onSuccess((merged) => (0, ts_utils_1.succeed)((0, retriever_1.limitRecords)(merged, query.limit)));
107
+ });
108
+ }
109
+ /**
110
+ * Project the query for one child retriever. Two adjustments:
111
+ *
112
+ * - Strip axes the child does not support (`semantic` / `topK` for a
113
+ * non-semantic child, `asOf` for a non-temporal child, the link axes for a
114
+ * non-link child) so it returns its normal results rather than loud-failing
115
+ * on a field a sibling handles.
116
+ * - Strip `limit` unconditionally: limit is a post-merge concern. A child that
117
+ * pre-truncated its result set would starve the merge strategy of candidates
118
+ * it needs to score correctly (a record both children would surface must
119
+ * reach the merge to score 2). The hybrid applies `limit` once, after merge.
120
+ */
121
+ _projectQuery(query, retriever) {
122
+ const projected = Object.assign({}, query);
123
+ delete projected.limit;
124
+ if (!retriever.capabilities.supportsSemanticRecall) {
125
+ delete projected.semantic;
126
+ delete projected.topK;
127
+ }
128
+ if (!retriever.capabilities.supportsTemporalQuery) {
129
+ delete projected.asOf;
130
+ }
131
+ if (!retriever.capabilities.supportsLinkTraversal) {
132
+ delete projected.linkedFrom;
133
+ delete projected.linkedTo;
134
+ delete projected.hops;
135
+ }
136
+ return projected;
137
+ }
138
+ }
139
+ exports.HybridRetriever = HybridRetriever;
140
+ //# sourceMappingURL=hybridRetriever.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hybridRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/hybridRetriever.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, fail, mapResults, succeed } from '@fgv/ts-utils';\nimport { IMemoryRecord, MemoryId } from '../types';\nimport {\n IMemoryQuery,\n IMemoryRetriever,\n IMemoryRetrieverCapabilities,\n guardRetrieverCapabilities,\n limitRecords,\n recencyCompare\n} from './retriever';\n\n/**\n * Strategy for combining the result sets of the retrievers a\n * {@link HybridRetriever} composes. Injectable so a consumer can weight,\n * re-rank, or intersect instead of the default union.\n * @public\n */\nexport interface IMergeStrategy {\n /**\n * Merge the per-retriever result sets into a single ordered result.\n * @param resultSets - One entry per composed retriever, in composition order.\n */\n merge(\n resultSets: ReadonlyArray<ReadonlyArray<IMemoryRecord<unknown>>>\n ): Result<ReadonlyArray<IMemoryRecord<unknown>>>;\n}\n\n/**\n * The reference {@link IMergeStrategy}: a score-union. Every record is scored by\n * the number of composed result sets it appears in (deduplicated by\n * {@link IMemoryEnvelope.id | id}); the merged result is ordered by descending\n * score, then by recency. Records surfaced by more retrievers rank higher.\n *\n * @remarks\n * Dedup is by `id` alone, which is unambiguous for the flat (one-file-per-entity)\n * kinds B2 ships. Phase-C versioned kinds that reuse a stem across scopes will\n * extend the dedup key — additive, no API change.\n * @public\n */\nexport class ScoreUnionMergeStrategy implements IMergeStrategy {\n private constructor() {}\n\n /** Family-convention factory. */\n public static create(): Result<ScoreUnionMergeStrategy> {\n return succeed(new ScoreUnionMergeStrategy());\n }\n\n /** {@inheritDoc IMergeStrategy.merge} */\n public merge(\n resultSets: ReadonlyArray<ReadonlyArray<IMemoryRecord<unknown>>>\n ): Result<ReadonlyArray<IMemoryRecord<unknown>>> {\n const scored: Map<MemoryId, { readonly record: IMemoryRecord<unknown>; score: number }> = new Map();\n for (const set of resultSets) {\n // Count each id at most once per result set, so the score is \"how many\n // retrievers surfaced this record\" rather than \"how many copies total\".\n const seenInSet: Set<MemoryId> = new Set<MemoryId>();\n for (const record of set) {\n const id: MemoryId = record.envelope.id;\n if (seenInSet.has(id)) {\n continue;\n }\n seenInSet.add(id);\n const existing = scored.get(id);\n if (existing === undefined) {\n scored.set(id, { record, score: 1 });\n } else {\n existing.score += 1;\n }\n }\n }\n const merged: IMemoryRecord<unknown>[] = Array.from(scored.values())\n .sort((a, b) => (b.score !== a.score ? b.score - a.score : recencyCompare(a.record, b.record)))\n .map((entry) => entry.record);\n return succeed(merged);\n }\n}\n\n/**\n * Composes several retrievers, dispatches a query to each, and merges their\n * results via an injectable {@link IMergeStrategy}. Its capabilities are the\n * union of the composed retrievers' capabilities.\n *\n * @remarks\n * The hybrid enforces the loud-degradation contract against its OWN (union)\n * capabilities, then projects the query for each child: a child that does not\n * support the `semantic` (or `asOf`) axis is handed a query with that axis\n * stripped, so it returns its normal results instead of loud-failing on a field\n * a sibling handles. A child that genuinely fails (e.g. a wired semantic backend\n * erroring) propagates — the hybrid never silently drops a failure.\n * @public\n */\nexport class HybridRetriever implements IMemoryRetriever {\n private readonly _retrievers: ReadonlyArray<IMemoryRetriever>;\n private readonly _mergeStrategy: IMergeStrategy;\n private readonly _capabilities: IMemoryRetrieverCapabilities;\n\n private constructor(\n retrievers: ReadonlyArray<IMemoryRetriever>,\n mergeStrategy: IMergeStrategy,\n capabilities: IMemoryRetrieverCapabilities\n ) {\n this._retrievers = retrievers;\n this._mergeStrategy = mergeStrategy;\n this._capabilities = capabilities;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return this._capabilities;\n }\n\n /**\n * Family-convention factory.\n * @param retrievers - The retrievers to compose (at least one).\n * @param mergeStrategy - How to combine their results.\n */\n public static create(\n retrievers: ReadonlyArray<IMemoryRetriever>,\n mergeStrategy: IMergeStrategy\n ): Result<HybridRetriever> {\n if (retrievers.length === 0) {\n return fail('HybridRetriever: at least one retriever is required');\n }\n // Snapshot the caller's array so a later mutation cannot make `retrieve()`\n // and the cached `capabilities` disagree.\n const stableRetrievers: ReadonlyArray<IMemoryRetriever> = [...retrievers];\n const capabilities: IMemoryRetrieverCapabilities = {\n supportsSemanticRecall: stableRetrievers.some((r) => r.capabilities.supportsSemanticRecall),\n supportsTemporalQuery: stableRetrievers.some((r) => r.capabilities.supportsTemporalQuery),\n supportsLinkTraversal: stableRetrievers.some((r) => r.capabilities.supportsLinkTraversal)\n };\n return succeed(new HybridRetriever(stableRetrievers, mergeStrategy, capabilities));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public async retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return guardRetrieverCapabilities(query, this._capabilities).thenOnSuccess(async () => {\n const perRetriever: Result<ReadonlyArray<IMemoryRecord<unknown>>>[] = await Promise.all(\n this._retrievers.map((retriever) => retriever.retrieve(this._projectQuery(query, retriever)))\n );\n return mapResults(perRetriever)\n .onSuccess((resultSets) => this._mergeStrategy.merge(resultSets))\n .onSuccess((merged) => succeed(limitRecords(merged, query.limit)));\n });\n }\n\n /**\n * Project the query for one child retriever. Two adjustments:\n *\n * - Strip axes the child does not support (`semantic` / `topK` for a\n * non-semantic child, `asOf` for a non-temporal child, the link axes for a\n * non-link child) so it returns its normal results rather than loud-failing\n * on a field a sibling handles.\n * - Strip `limit` unconditionally: limit is a post-merge concern. A child that\n * pre-truncated its result set would starve the merge strategy of candidates\n * it needs to score correctly (a record both children would surface must\n * reach the merge to score 2). The hybrid applies `limit` once, after merge.\n */\n private _projectQuery(query: IMemoryQuery, retriever: IMemoryRetriever): IMemoryQuery {\n const projected: { -readonly [K in keyof IMemoryQuery]: IMemoryQuery[K] } = { ...query };\n delete projected.limit;\n if (!retriever.capabilities.supportsSemanticRecall) {\n delete projected.semantic;\n delete projected.topK;\n }\n if (!retriever.capabilities.supportsTemporalQuery) {\n delete projected.asOf;\n }\n if (!retriever.capabilities.supportsLinkTraversal) {\n delete projected.linkedFrom;\n delete projected.linkedTo;\n delete projected.hops;\n }\n return projected;\n }\n}\n"]}
@@ -0,0 +1,8 @@
1
+ export * from './retriever';
2
+ export * from './recencyRetriever';
3
+ export * from './linkTraversalRetriever';
4
+ export * from './tagRetriever';
5
+ export * from './structuredFilterRetriever';
6
+ export * from './semanticRetriever';
7
+ export * from './hybridRetriever';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/packlets/retrieve/index.ts"],"names":[],"mappings":"AAKA,cAAc,aAAa,CAAC;AAC5B,cAAc,oBAAoB,CAAC;AACnC,cAAc,0BAA0B,CAAC;AACzC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,6BAA6B,CAAC;AAC5C,cAAc,qBAAqB,CAAC;AACpC,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2026 Erik Fortune
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ __exportStar(require("./retriever"), exports);
22
+ __exportStar(require("./recencyRetriever"), exports);
23
+ __exportStar(require("./linkTraversalRetriever"), exports);
24
+ __exportStar(require("./tagRetriever"), exports);
25
+ __exportStar(require("./structuredFilterRetriever"), exports);
26
+ __exportStar(require("./semanticRetriever"), exports);
27
+ __exportStar(require("./hybridRetriever"), exports);
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;AAEH,8CAA4B;AAC5B,qDAAmC;AACnC,2DAAyC;AACzC,iDAA+B;AAC/B,8DAA4C;AAC5C,sDAAoC;AACpC,oDAAkC","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nexport * from './retriever';\nexport * from './recencyRetriever';\nexport * from './linkTraversalRetriever';\nexport * from './tagRetriever';\nexport * from './structuredFilterRetriever';\nexport * from './semanticRetriever';\nexport * from './hybridRetriever';\n"]}
@@ -0,0 +1,61 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { IMemoryRecord } from '../types';
3
+ import { IMemoryIndex } from '../index';
4
+ import { IMemoryQuery, IMemoryRetriever, IMemoryRetrieverCapabilities } from './retriever';
5
+ /**
6
+ * The loud-degradation message returned when a link-traversal query supplies no
7
+ * seed (`linkedFrom` / `linkedTo`).
8
+ * @public
9
+ */
10
+ export declare const LINK_TRAVERSAL_NO_SEED_MESSAGE: string;
11
+ /**
12
+ * Breadth-first link-traversal retriever. From a seed {@link MemoryId} it walks
13
+ * the link graph up to `query.hops` levels and returns the records reached
14
+ * (excluding the seed), recency-ordered and limited.
15
+ *
16
+ * @remarks
17
+ * - **Direction.** `linkedFrom` walks OUTBOUND edges (each record's
18
+ * `envelope.links[].target`); `linkedTo` walks INBOUND edges (the index's
19
+ * `backlinks`). Exactly one is the seed; `linkedFrom` wins if both are set.
20
+ * - **Bound + cycle safety.** Traversal is bounded by `hops` (default `1` — a
21
+ * single hop) and a visited-set guard. The graph is keyed by bare
22
+ * string {@link MemoryId}s, so a `Set<string>` visited-set is the exact,
23
+ * collision-free cycle key — no structural hashing (e.g. `Crc32Normalizer`) is
24
+ * needed. A self-loop or any multi-hop cycle terminates because a revisited id
25
+ * is never re-expanded.
26
+ * - **Post-filter.** The scope / kind / tag / predicate axes of the query are
27
+ * applied to the reached records (the link axes are the traversal itself).
28
+ * @public
29
+ */
30
+ export declare class LinkTraversalRetriever implements IMemoryRetriever {
31
+ private readonly _index;
32
+ private constructor();
33
+ /** Family-convention factory. */
34
+ static create(index: IMemoryIndex): Result<LinkTraversalRetriever>;
35
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
36
+ get capabilities(): IMemoryRetrieverCapabilities;
37
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
38
+ retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
39
+ /** Run the bounded, cycle-safe BFS and post-filter the reached records. */
40
+ private _traverse;
41
+ /**
42
+ * Group the index's entries by bare {@link MemoryId}. An id can map to more
43
+ * than one entry when distinct scopes reuse a filename stem (e.g. `turn-0` in
44
+ * two conversations), so the value is an array.
45
+ *
46
+ * @remarks
47
+ * **Design note (links are globally-scoped identifiers in this phase).** An
48
+ * {@link IEdge.target} is a bare `MemoryId`, not a `(scope, id)` pair, so
49
+ * traversal resolves a target across ALL scopes that hold that id. When two
50
+ * scopes reuse a stem, following an edge to it reaches every match. This
51
+ * mirrors the `backlinks` index, which is also keyed by bare id. Scope-
52
+ * qualified link resolution is intentionally out of scope for Phase C and
53
+ * would be an additive change here (and to {@link IEdge} / the index).
54
+ */
55
+ private _indexById;
56
+ /** Outbound neighbors: the targets of every edge on the records with this id. */
57
+ private _outbound;
58
+ /** Inbound neighbors: the ids whose edges point AT this id (the backlinks). */
59
+ private _inbound;
60
+ }
61
+ //# sourceMappingURL=linkTraversalRetriever.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linkTraversalRetriever.d.ts","sourceRoot":"","sources":["../../../src/packlets/retrieve/linkTraversalRetriever.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAiB,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,aAAa,EAAY,MAAM,UAAU,CAAC;AACnD,OAAO,EAAwB,YAAY,EAAE,MAAM,UAAU,CAAC;AAC9D,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,4BAA4B,EAK7B,MAAM,aAAa,CAAC;AAYrB;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,MACiB,CAAC;AAE/D;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,sBAAuB,YAAW,gBAAgB;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IAEtC,OAAO;IAIP,iCAAiC;WACnB,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAAC,sBAAsB,CAAC;IAIzE,kDAAkD;IAClD,IAAW,YAAY,IAAI,4BAA4B,CAEtD;IAED,8CAA8C;IACvC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAM5F,2EAA2E;IAC3E,OAAO,CAAC,SAAS;IA0CjB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,UAAU;IAclB,iFAAiF;IACjF,OAAO,CAAC,SAAS;IAajB,+EAA+E;IAC/E,OAAO,CAAC,QAAQ;CAGjB"}
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2026 Erik Fortune
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.LinkTraversalRetriever = exports.LINK_TRAVERSAL_NO_SEED_MESSAGE = void 0;
8
+ const ts_utils_1 = require("@fgv/ts-utils");
9
+ const retriever_1 = require("./retriever");
10
+ /** The capabilities a link-traversal retriever exposes (link traversal only). */
11
+ const LINK_TRAVERSAL_CAPABILITIES = {
12
+ supportsSemanticRecall: false,
13
+ supportsTemporalQuery: false,
14
+ supportsLinkTraversal: true
15
+ };
16
+ /** Default BFS hop count when `query.hops` is not supplied. */
17
+ const DEFAULT_HOPS = 1;
18
+ /**
19
+ * The loud-degradation message returned when a link-traversal query supplies no
20
+ * seed (`linkedFrom` / `linkedTo`).
21
+ * @public
22
+ */
23
+ exports.LINK_TRAVERSAL_NO_SEED_MESSAGE = 'link traversal requires a seed id (linkedFrom or linkedTo)';
24
+ /**
25
+ * Breadth-first link-traversal retriever. From a seed {@link MemoryId} it walks
26
+ * the link graph up to `query.hops` levels and returns the records reached
27
+ * (excluding the seed), recency-ordered and limited.
28
+ *
29
+ * @remarks
30
+ * - **Direction.** `linkedFrom` walks OUTBOUND edges (each record's
31
+ * `envelope.links[].target`); `linkedTo` walks INBOUND edges (the index's
32
+ * `backlinks`). Exactly one is the seed; `linkedFrom` wins if both are set.
33
+ * - **Bound + cycle safety.** Traversal is bounded by `hops` (default `1` — a
34
+ * single hop) and a visited-set guard. The graph is keyed by bare
35
+ * string {@link MemoryId}s, so a `Set<string>` visited-set is the exact,
36
+ * collision-free cycle key — no structural hashing (e.g. `Crc32Normalizer`) is
37
+ * needed. A self-loop or any multi-hop cycle terminates because a revisited id
38
+ * is never re-expanded.
39
+ * - **Post-filter.** The scope / kind / tag / predicate axes of the query are
40
+ * applied to the reached records (the link axes are the traversal itself).
41
+ * @public
42
+ */
43
+ class LinkTraversalRetriever {
44
+ constructor(index) {
45
+ this._index = index;
46
+ }
47
+ /** Family-convention factory. */
48
+ static create(index) {
49
+ return (0, ts_utils_1.succeed)(new LinkTraversalRetriever(index));
50
+ }
51
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
52
+ get capabilities() {
53
+ return LINK_TRAVERSAL_CAPABILITIES;
54
+ }
55
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
56
+ retrieve(query) {
57
+ return Promise.resolve((0, retriever_1.guardRetrieverCapabilities)(query, this.capabilities).onSuccess(() => this._traverse(query)));
58
+ }
59
+ /** Run the bounded, cycle-safe BFS and post-filter the reached records. */
60
+ _traverse(query) {
61
+ var _a, _b;
62
+ const outbound = query.linkedFrom !== undefined;
63
+ const seed = (_a = query.linkedFrom) !== null && _a !== void 0 ? _a : query.linkedTo;
64
+ if (seed === undefined) {
65
+ return (0, ts_utils_1.fail)(exports.LINK_TRAVERSAL_NO_SEED_MESSAGE);
66
+ }
67
+ const hops = (_b = query.hops) !== null && _b !== void 0 ? _b : DEFAULT_HOPS;
68
+ const byId = this._indexById();
69
+ // The visited-set IS the cycle guard: ids are strings, so set membership is
70
+ // an exact identity check. The seed is pre-marked so it is never re-added.
71
+ const visited = new Set([seed]);
72
+ const reached = [];
73
+ let frontier = [seed];
74
+ for (let hop = 0; hop < hops && frontier.length > 0; hop++) {
75
+ const next = [];
76
+ for (const id of frontier) {
77
+ for (const neighbor of outbound ? this._outbound(id, byId) : this._inbound(id)) {
78
+ if (!visited.has(neighbor)) {
79
+ visited.add(neighbor);
80
+ reached.push(neighbor);
81
+ next.push(neighbor);
82
+ }
83
+ }
84
+ }
85
+ frontier = next;
86
+ }
87
+ const entries = [];
88
+ for (const id of reached) {
89
+ const matches = byId.get(id);
90
+ if (matches !== undefined) {
91
+ entries.push(...matches);
92
+ }
93
+ }
94
+ const ordered = entries
95
+ .filter((entry) => (0, retriever_1.indexedRecordMatchesQuery)(entry, query))
96
+ .map((entry) => entry.record)
97
+ .sort(retriever_1.recencyCompare);
98
+ return (0, ts_utils_1.succeed)((0, retriever_1.limitRecords)(ordered, query.limit));
99
+ }
100
+ /**
101
+ * Group the index's entries by bare {@link MemoryId}. An id can map to more
102
+ * than one entry when distinct scopes reuse a filename stem (e.g. `turn-0` in
103
+ * two conversations), so the value is an array.
104
+ *
105
+ * @remarks
106
+ * **Design note (links are globally-scoped identifiers in this phase).** An
107
+ * {@link IEdge.target} is a bare `MemoryId`, not a `(scope, id)` pair, so
108
+ * traversal resolves a target across ALL scopes that hold that id. When two
109
+ * scopes reuse a stem, following an edge to it reaches every match. This
110
+ * mirrors the `backlinks` index, which is also keyed by bare id. Scope-
111
+ * qualified link resolution is intentionally out of scope for Phase C and
112
+ * would be an additive change here (and to {@link IEdge} / the index).
113
+ */
114
+ _indexById() {
115
+ const byId = new Map();
116
+ for (const entry of this._index.entries()) {
117
+ const id = entry.record.envelope.id;
118
+ const existing = byId.get(id);
119
+ if (existing === undefined) {
120
+ byId.set(id, [entry]);
121
+ }
122
+ else {
123
+ existing.push(entry);
124
+ }
125
+ }
126
+ return byId;
127
+ }
128
+ /** Outbound neighbors: the targets of every edge on the records with this id. */
129
+ _outbound(id, byId) {
130
+ const targets = [];
131
+ const matches = byId.get(id);
132
+ if (matches !== undefined) {
133
+ for (const entry of matches) {
134
+ for (const edge of entry.record.envelope.links) {
135
+ targets.push(edge.target);
136
+ }
137
+ }
138
+ }
139
+ return targets;
140
+ }
141
+ /** Inbound neighbors: the ids whose edges point AT this id (the backlinks). */
142
+ _inbound(id) {
143
+ return this._index.backlinks(id);
144
+ }
145
+ }
146
+ exports.LinkTraversalRetriever = LinkTraversalRetriever;
147
+ //# sourceMappingURL=linkTraversalRetriever.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linkTraversalRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/linkTraversalRetriever.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, fail, succeed } from '@fgv/ts-utils';\nimport { IMemoryRecord, MemoryId } from '../types';\nimport { IIndexedMemoryRecord, IMemoryIndex } from '../index';\nimport {\n IMemoryQuery,\n IMemoryRetriever,\n IMemoryRetrieverCapabilities,\n guardRetrieverCapabilities,\n indexedRecordMatchesQuery,\n limitRecords,\n recencyCompare\n} from './retriever';\n\n/** The capabilities a link-traversal retriever exposes (link traversal only). */\nconst LINK_TRAVERSAL_CAPABILITIES: IMemoryRetrieverCapabilities = {\n supportsSemanticRecall: false,\n supportsTemporalQuery: false,\n supportsLinkTraversal: true\n};\n\n/** Default BFS hop count when `query.hops` is not supplied. */\nconst DEFAULT_HOPS: number = 1;\n\n/**\n * The loud-degradation message returned when a link-traversal query supplies no\n * seed (`linkedFrom` / `linkedTo`).\n * @public\n */\nexport const LINK_TRAVERSAL_NO_SEED_MESSAGE: string =\n 'link traversal requires a seed id (linkedFrom or linkedTo)';\n\n/**\n * Breadth-first link-traversal retriever. From a seed {@link MemoryId} it walks\n * the link graph up to `query.hops` levels and returns the records reached\n * (excluding the seed), recency-ordered and limited.\n *\n * @remarks\n * - **Direction.** `linkedFrom` walks OUTBOUND edges (each record's\n * `envelope.links[].target`); `linkedTo` walks INBOUND edges (the index's\n * `backlinks`). Exactly one is the seed; `linkedFrom` wins if both are set.\n * - **Bound + cycle safety.** Traversal is bounded by `hops` (default `1` — a\n * single hop) and a visited-set guard. The graph is keyed by bare\n * string {@link MemoryId}s, so a `Set<string>` visited-set is the exact,\n * collision-free cycle key — no structural hashing (e.g. `Crc32Normalizer`) is\n * needed. A self-loop or any multi-hop cycle terminates because a revisited id\n * is never re-expanded.\n * - **Post-filter.** The scope / kind / tag / predicate axes of the query are\n * applied to the reached records (the link axes are the traversal itself).\n * @public\n */\nexport class LinkTraversalRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n\n private constructor(index: IMemoryIndex) {\n this._index = index;\n }\n\n /** Family-convention factory. */\n public static create(index: IMemoryIndex): Result<LinkTraversalRetriever> {\n return succeed(new LinkTraversalRetriever(index));\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return LINK_TRAVERSAL_CAPABILITIES;\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(\n guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => this._traverse(query))\n );\n }\n\n /** Run the bounded, cycle-safe BFS and post-filter the reached records. */\n private _traverse(query: IMemoryQuery): Result<ReadonlyArray<IMemoryRecord<unknown>>> {\n const outbound: boolean = query.linkedFrom !== undefined;\n const seed: MemoryId | undefined = query.linkedFrom ?? query.linkedTo;\n if (seed === undefined) {\n return fail(LINK_TRAVERSAL_NO_SEED_MESSAGE);\n }\n const hops: number = query.hops ?? DEFAULT_HOPS;\n const byId: ReadonlyMap<MemoryId, IIndexedMemoryRecord[]> = this._indexById();\n\n // The visited-set IS the cycle guard: ids are strings, so set membership is\n // an exact identity check. The seed is pre-marked so it is never re-added.\n const visited: Set<string> = new Set<string>([seed]);\n const reached: MemoryId[] = [];\n let frontier: MemoryId[] = [seed];\n for (let hop = 0; hop < hops && frontier.length > 0; hop++) {\n const next: MemoryId[] = [];\n for (const id of frontier) {\n for (const neighbor of outbound ? this._outbound(id, byId) : this._inbound(id)) {\n if (!visited.has(neighbor)) {\n visited.add(neighbor);\n reached.push(neighbor);\n next.push(neighbor);\n }\n }\n }\n frontier = next;\n }\n\n const entries: IIndexedMemoryRecord[] = [];\n for (const id of reached) {\n const matches: IIndexedMemoryRecord[] | undefined = byId.get(id);\n if (matches !== undefined) {\n entries.push(...matches);\n }\n }\n const ordered: IMemoryRecord<unknown>[] = entries\n .filter((entry) => indexedRecordMatchesQuery(entry, query))\n .map((entry) => entry.record)\n .sort(recencyCompare);\n return succeed(limitRecords(ordered, query.limit));\n }\n\n /**\n * Group the index's entries by bare {@link MemoryId}. An id can map to more\n * than one entry when distinct scopes reuse a filename stem (e.g. `turn-0` in\n * two conversations), so the value is an array.\n *\n * @remarks\n * **Design note (links are globally-scoped identifiers in this phase).** An\n * {@link IEdge.target} is a bare `MemoryId`, not a `(scope, id)` pair, so\n * traversal resolves a target across ALL scopes that hold that id. When two\n * scopes reuse a stem, following an edge to it reaches every match. This\n * mirrors the `backlinks` index, which is also keyed by bare id. Scope-\n * qualified link resolution is intentionally out of scope for Phase C and\n * would be an additive change here (and to {@link IEdge} / the index).\n */\n private _indexById(): ReadonlyMap<MemoryId, IIndexedMemoryRecord[]> {\n const byId: Map<MemoryId, IIndexedMemoryRecord[]> = new Map<MemoryId, IIndexedMemoryRecord[]>();\n for (const entry of this._index.entries()) {\n const id: MemoryId = entry.record.envelope.id;\n const existing: IIndexedMemoryRecord[] | undefined = byId.get(id);\n if (existing === undefined) {\n byId.set(id, [entry]);\n } else {\n existing.push(entry);\n }\n }\n return byId;\n }\n\n /** Outbound neighbors: the targets of every edge on the records with this id. */\n private _outbound(id: MemoryId, byId: ReadonlyMap<MemoryId, IIndexedMemoryRecord[]>): MemoryId[] {\n const targets: MemoryId[] = [];\n const matches: IIndexedMemoryRecord[] | undefined = byId.get(id);\n if (matches !== undefined) {\n for (const entry of matches) {\n for (const edge of entry.record.envelope.links) {\n targets.push(edge.target);\n }\n }\n }\n return targets;\n }\n\n /** Inbound neighbors: the ids whose edges point AT this id (the backlinks). */\n private _inbound(id: MemoryId): ReadonlyArray<MemoryId> {\n return this._index.backlinks(id);\n }\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import { Result } from '@fgv/ts-utils';
2
+ import { IMemoryRecord } from '../types';
3
+ import { IMemoryIndex } from '../index';
4
+ import { IMemoryQuery, IMemoryRetriever, IMemoryRetrieverCapabilities } from './retriever';
5
+ /**
6
+ * Returns records matching the query's scope / kind / tag / predicate filters,
7
+ * ordered most-recently-updated first. The universal v1 retriever — an empty
8
+ * query returns the whole vault in recency order.
9
+ * @public
10
+ */
11
+ export declare class RecencyRetriever implements IMemoryRetriever {
12
+ private readonly _index;
13
+ private constructor();
14
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
15
+ get capabilities(): IMemoryRetrieverCapabilities;
16
+ /** Family-convention factory. */
17
+ static create(index: IMemoryIndex): Result<RecencyRetriever>;
18
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
19
+ retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>>;
20
+ }
21
+ //# sourceMappingURL=recencyRetriever.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recencyRetriever.d.ts","sourceRoot":"","sources":["../../../src/packlets/retrieve/recencyRetriever.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,MAAM,EAAW,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EACL,YAAY,EACZ,gBAAgB,EAChB,4BAA4B,EAM7B,MAAM,aAAa,CAAC;AAErB;;;;;GAKG;AACH,qBAAa,gBAAiB,YAAW,gBAAgB;IACvD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IAEtC,OAAO;IAIP,kDAAkD;IAClD,IAAW,YAAY,IAAI,4BAA4B,CAEtD;IAED,iCAAiC;WACnB,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAAC,gBAAgB,CAAC;IAInE,8CAA8C;IACvC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;CAU7F"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) 2026 Erik Fortune
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.RecencyRetriever = void 0;
8
+ const ts_utils_1 = require("@fgv/ts-utils");
9
+ const retriever_1 = require("./retriever");
10
+ /**
11
+ * Returns records matching the query's scope / kind / tag / predicate filters,
12
+ * ordered most-recently-updated first. The universal v1 retriever — an empty
13
+ * query returns the whole vault in recency order.
14
+ * @public
15
+ */
16
+ class RecencyRetriever {
17
+ constructor(index) {
18
+ this._index = index;
19
+ }
20
+ /** {@inheritDoc IMemoryRetriever.capabilities} */
21
+ get capabilities() {
22
+ return retriever_1.NON_SEMANTIC_CAPABILITIES;
23
+ }
24
+ /** Family-convention factory. */
25
+ static create(index) {
26
+ return (0, ts_utils_1.succeed)(new RecencyRetriever(index));
27
+ }
28
+ /** {@inheritDoc IMemoryRetriever.retrieve} */
29
+ retrieve(query) {
30
+ return Promise.resolve((0, retriever_1.guardRetrieverCapabilities)(query, this.capabilities).onSuccess(() => {
31
+ const ordered = (0, retriever_1.selectByQuery)(this._index.entries(), query).sort(retriever_1.recencyCompare);
32
+ return (0, ts_utils_1.succeed)((0, retriever_1.limitRecords)(ordered, query.limit));
33
+ }));
34
+ }
35
+ }
36
+ exports.RecencyRetriever = RecencyRetriever;
37
+ //# sourceMappingURL=recencyRetriever.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recencyRetriever.js","sourceRoot":"","sources":["../../../src/packlets/retrieve/recencyRetriever.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,4CAAgD;AAGhD,2CASqB;AAErB;;;;;GAKG;AACH,MAAa,gBAAgB;IAG3B,YAAoB,KAAmB;QACrC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;IACtB,CAAC;IAED,kDAAkD;IAClD,IAAW,YAAY;QACrB,OAAO,qCAAyB,CAAC;IACnC,CAAC;IAED,iCAAiC;IAC1B,MAAM,CAAC,MAAM,CAAC,KAAmB;QACtC,OAAO,IAAA,kBAAO,EAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,8CAA8C;IACvC,QAAQ,CAAC,KAAmB;QACjC,OAAO,OAAO,CAAC,OAAO,CACpB,IAAA,sCAA0B,EAAC,KAAK,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,SAAS,CAAC,GAAG,EAAE;YAClE,MAAM,OAAO,GAA6B,IAAA,yBAAa,EAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC,IAAI,CACxF,0BAAc,CACf,CAAC;YACF,OAAO,IAAA,kBAAO,EAAC,IAAA,wBAAY,EAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACrD,CAAC,CAAC,CACH,CAAC;IACJ,CAAC;CACF;AA5BD,4CA4BC","sourcesContent":["/*\n * Copyright (c) 2026 Erik Fortune\n * SPDX-License-Identifier: MIT\n */\n\nimport { Result, succeed } from '@fgv/ts-utils';\nimport { IMemoryRecord } from '../types';\nimport { IMemoryIndex } from '../index';\nimport {\n IMemoryQuery,\n IMemoryRetriever,\n IMemoryRetrieverCapabilities,\n NON_SEMANTIC_CAPABILITIES,\n guardRetrieverCapabilities,\n limitRecords,\n recencyCompare,\n selectByQuery\n} from './retriever';\n\n/**\n * Returns records matching the query's scope / kind / tag / predicate filters,\n * ordered most-recently-updated first. The universal v1 retriever — an empty\n * query returns the whole vault in recency order.\n * @public\n */\nexport class RecencyRetriever implements IMemoryRetriever {\n private readonly _index: IMemoryIndex;\n\n private constructor(index: IMemoryIndex) {\n this._index = index;\n }\n\n /** {@inheritDoc IMemoryRetriever.capabilities} */\n public get capabilities(): IMemoryRetrieverCapabilities {\n return NON_SEMANTIC_CAPABILITIES;\n }\n\n /** Family-convention factory. */\n public static create(index: IMemoryIndex): Result<RecencyRetriever> {\n return succeed(new RecencyRetriever(index));\n }\n\n /** {@inheritDoc IMemoryRetriever.retrieve} */\n public retrieve(query: IMemoryQuery): Promise<Result<ReadonlyArray<IMemoryRecord<unknown>>>> {\n return Promise.resolve(\n guardRetrieverCapabilities(query, this.capabilities).onSuccess(() => {\n const ordered: IMemoryRecord<unknown>[] = selectByQuery(this._index.entries(), query).sort(\n recencyCompare\n );\n return succeed(limitRecords(ordered, query.limit));\n })\n );\n }\n}\n"]}