@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,210 @@
1
+ /*
2
+ * Copyright (c) 2026 Erik Fortune
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { Converter, Converters, Result, fail, succeed } from '@fgv/ts-utils';
7
+ import { Yaml } from '@fgv/ts-extras';
8
+ import { Convert, IEdge, IMemoryEnvelope, IMemoryRecord, IProvenance, ITemporalBlock } from '../types';
9
+ import { IBodyConverterRegistry } from './bodyConverterRegistry';
10
+
11
+ /** Matches exactly the JSON `null` literal. */
12
+ // eslint-disable-next-line @rushstack/no-new-null -- the envelope nullable fields (invalid_at, embeddingRef) carry meaningful null; design-lock §2.3-2.5
13
+ const nullConverter: Converter<null> = Converters.generic<null>((from: unknown) =>
14
+ from === null ? succeed(null) : fail('expected null')
15
+ );
16
+
17
+ /** A nullable epoch-ms value (`number | null`). */
18
+ // eslint-disable-next-line @rushstack/no-new-null -- meaningful null for IEdge/ITemporalBlock.invalid_at; design-lock §2.3-2.4
19
+ const numberOrNull: Converter<number | null> = Converters.oneOf<number | null>([
20
+ Converters.number,
21
+ nullConverter
22
+ ]);
23
+
24
+ /** A nullable string value (`string | null`). */
25
+ // eslint-disable-next-line @rushstack/no-new-null -- meaningful null for IMemoryEnvelope.embeddingRef; design-lock §2.5
26
+ const stringOrNull: Converter<string | null> = Converters.oneOf<string | null>([
27
+ Converters.string,
28
+ nullConverter
29
+ ]);
30
+
31
+ /**
32
+ * Converter for the known {@link IProvenance} fields. The full
33
+ * {@link provenanceConverter} layers extension-key preservation on top.
34
+ */
35
+ const knownProvenanceConverter: Converter<IProvenance> = Converters.object<IProvenance>(
36
+ {
37
+ source: Converters.string,
38
+ by: Converters.string.optional(),
39
+ model: Converters.string.optional(),
40
+ confidence: Converters.number.optional(),
41
+ derivedFrom: Convert.memoryId.optional()
42
+ },
43
+ { optionalFields: ['by', 'model', 'confidence', 'derivedFrom'] }
44
+ );
45
+
46
+ /**
47
+ * Converter for {@link IProvenance}. Validates the known fields and preserves
48
+ * any extension keys verbatim (the `[key: string]: unknown` opaque payload),
49
+ * so a round-trip never drops consumer-attached data.
50
+ * @public
51
+ */
52
+ export const provenanceConverter: Converter<IProvenance> = Converters.generic<IProvenance>(
53
+ (from: unknown): Result<IProvenance> =>
54
+ knownProvenanceConverter.convert(from).onSuccess((known) => {
55
+ // `from` is guaranteed to be a non-null object here — the known-field
56
+ // converter only succeeds on objects. Spread the original keys first,
57
+ // then the validated/normalized known fields on top, so extension keys
58
+ // survive while the known fields carry their converted values.
59
+ const merged: IProvenance = { ...(from as Record<string, unknown>), ...known };
60
+ return succeed(merged);
61
+ })
62
+ );
63
+
64
+ /**
65
+ * Converter for an attributed {@link IEdge}.
66
+ * @public
67
+ */
68
+ export const edgeConverter: Converter<IEdge> = Converters.object<IEdge>(
69
+ {
70
+ type: Convert.linkType,
71
+ target: Convert.memoryId,
72
+ confidence: Converters.number.optional(),
73
+ provenance: provenanceConverter.optional(),
74
+ valid_at: Converters.number.optional(),
75
+ invalid_at: numberOrNull.optional()
76
+ },
77
+ { optionalFields: ['confidence', 'provenance', 'valid_at', 'invalid_at'] }
78
+ );
79
+
80
+ /**
81
+ * Converter for the optional {@link ITemporalBlock}.
82
+ * @public
83
+ */
84
+ export const temporalConverter: Converter<ITemporalBlock> = Converters.object<ITemporalBlock>(
85
+ {
86
+ valid_at: Converters.number.optional(),
87
+ invalid_at: numberOrNull.optional()
88
+ },
89
+ { optionalFields: ['valid_at', 'invalid_at'] }
90
+ );
91
+
92
+ /**
93
+ * Converter for the invariant {@link IMemoryEnvelope}. Validates a plain
94
+ * object (e.g. parsed YAML frontmatter) into a typed envelope.
95
+ * @public
96
+ */
97
+ export const envelopeConverter: Converter<IMemoryEnvelope> = Converters.object<IMemoryEnvelope>(
98
+ {
99
+ id: Convert.memoryId,
100
+ entityId: Convert.entityId,
101
+ kind: Convert.kind,
102
+ tags: Converters.arrayOf(Convert.tag),
103
+ links: Converters.arrayOf(edgeConverter),
104
+ created: Converters.number,
105
+ updated: Converters.number,
106
+ seq: Converters.number,
107
+ contentHash: Converters.string,
108
+ provenance: provenanceConverter,
109
+ temporal: temporalConverter.optional(),
110
+ embeddingRef: stringOrNull.optional()
111
+ },
112
+ { optionalFields: ['temporal', 'embeddingRef'] }
113
+ );
114
+
115
+ /**
116
+ * Converter that parses a YAML frontmatter string into an
117
+ * {@link IMemoryEnvelope}.
118
+ * @public
119
+ */
120
+ export const envelopeYamlConverter: Converter<IMemoryEnvelope> = Yaml.yamlConverter(envelopeConverter);
121
+
122
+ /** The frontmatter delimiter line. */
123
+ const FRONTMATTER_DELIMITER: string = '---';
124
+
125
+ /**
126
+ * The two parts of a memory file: the YAML frontmatter (between the `---`
127
+ * delimiters) and the markdown body (everything after the closing delimiter).
128
+ * @public
129
+ */
130
+ export interface IMemoryFileParts {
131
+ /** The raw YAML frontmatter (delimiters stripped). */
132
+ readonly frontmatter: string;
133
+ /** The raw body text following the closing delimiter. */
134
+ readonly body: string;
135
+ }
136
+
137
+ /**
138
+ * Split a `---\n<yaml>\n---\n<body>` memory file into its frontmatter and
139
+ * body parts. Pure string handling — no external dependency.
140
+ * @public
141
+ */
142
+ export function splitFrontmatter(raw: string): Result<IMemoryFileParts> {
143
+ // Split on '\n' and strip a trailing '\r' per line so CRLF-authored files
144
+ // parse identically to LF — otherwise the '\r' would ride along into the
145
+ // returned frontmatter (perturbing YAML parsing) and body (corrupting
146
+ // round-trip fidelity).
147
+ const lines: string[] = raw.split('\n').map((line) => (line.endsWith('\r') ? line.slice(0, -1) : line));
148
+ if (lines[0].trim() !== FRONTMATTER_DELIMITER) {
149
+ return fail("memory file: missing opening frontmatter delimiter '---'");
150
+ }
151
+ let closeIndex: number = -1;
152
+ for (let i = 1; i < lines.length; i++) {
153
+ if (lines[i].trim() === FRONTMATTER_DELIMITER) {
154
+ closeIndex = i;
155
+ break;
156
+ }
157
+ }
158
+ if (closeIndex === -1) {
159
+ return fail("memory file: missing closing frontmatter delimiter '---'");
160
+ }
161
+ return succeed({
162
+ frontmatter: lines.slice(1, closeIndex).join('\n'),
163
+ body: lines.slice(closeIndex + 1).join('\n')
164
+ });
165
+ }
166
+
167
+ /**
168
+ * Join a YAML frontmatter string and a body into the canonical
169
+ * `---\n<yaml>\n---\n<body>` memory-file format.
170
+ * @public
171
+ */
172
+ export function joinFrontmatter(frontmatter: string, body: string): string {
173
+ const normalized: string = frontmatter.endsWith('\n') ? frontmatter : `${frontmatter}\n`;
174
+ return `${FRONTMATTER_DELIMITER}\n${normalized}${FRONTMATTER_DELIMITER}\n${body}`;
175
+ }
176
+
177
+ /**
178
+ * Parse a complete memory file into a typed {@link IMemoryRecord}. Splits the
179
+ * frontmatter, validates the envelope, and dispatches the body through the
180
+ * registered Converter for the envelope's kind.
181
+ * @public
182
+ */
183
+ export function parseMemoryFile(
184
+ raw: string,
185
+ registry: IBodyConverterRegistry
186
+ ): Result<IMemoryRecord<unknown>> {
187
+ return splitFrontmatter(raw).onSuccess((parts) =>
188
+ envelopeYamlConverter
189
+ .convert(parts.frontmatter)
190
+ .withErrorFormat((msg) => `memory file: invalid envelope: ${msg}`)
191
+ .onSuccess((envelope) =>
192
+ registry
193
+ .convert(envelope.kind, parts.body)
194
+ .withErrorFormat((msg) => `memory file '${envelope.id}': invalid body: ${msg}`)
195
+ .onSuccess((body) => succeed({ envelope, body }))
196
+ )
197
+ );
198
+ }
199
+
200
+ /**
201
+ * Serialize an envelope and its rendered body text into a memory file. The
202
+ * envelope is emitted as YAML frontmatter; the body is written verbatim after
203
+ * the closing delimiter.
204
+ * @public
205
+ */
206
+ export function serializeMemoryFile(envelope: IMemoryEnvelope, body: string): Result<string> {
207
+ return Yaml.yamlStringify(envelope)
208
+ .withErrorFormat((msg) => `memory file '${envelope.id}': failed to serialize envelope: ${msg}`)
209
+ .onSuccess((frontmatter) => succeed(joinFrontmatter(frontmatter, body)));
210
+ }
@@ -0,0 +1,7 @@
1
+ /*
2
+ * Copyright (c) 2026 Erik Fortune
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export * from './bodyConverterRegistry';
7
+ export * from './envelopeConverter';
@@ -0,0 +1,6 @@
1
+ /*
2
+ * Copyright (c) 2026 Erik Fortune
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export * from './memoryIndex';
@@ -0,0 +1,268 @@
1
+ /*
2
+ * Copyright (c) 2026 Erik Fortune
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { Result, succeed } from '@fgv/ts-utils';
7
+ import { IMemoryRecord, Kind, MemoryId, MemoryScopeKey, Tag } from '../types';
8
+
9
+ /**
10
+ * The mutation a {@link IMemoryIndex.patch | patch} applies: a record was
11
+ * written (`'put'`) or removed (`'delete'`).
12
+ * @public
13
+ */
14
+ export type MemoryIndexPatchOp = 'put' | 'delete';
15
+
16
+ /** Shared empty key set for `byKind` / `byTag` misses (avoids per-call allocation). */
17
+ const EMPTY_KEY_SET: ReadonlySet<string> = new Set<string>();
18
+
19
+ /**
20
+ * A memory record paired with the {@link MemoryScopeKey | scope} it lives
21
+ * under. The scope is not carried on the {@link IMemoryEnvelope} (it is derived
22
+ * from the entity id by the codec), so the store threads it alongside the
23
+ * record when patching or rebuilding the index. The `(scope, id)` pair is the
24
+ * index's primary key, keeping records distinct across scopes that reuse a
25
+ * filename stem (e.g. `turn-0` under different conversations in Phase C).
26
+ * @public
27
+ */
28
+ export interface IIndexedMemoryRecord {
29
+ /** The scope the record is stored under. */
30
+ readonly scope: MemoryScopeKey;
31
+ /** The memory record itself. */
32
+ readonly record: IMemoryRecord<unknown>;
33
+ }
34
+
35
+ /**
36
+ * The derived, in-memory secondary indexes the store maintains over its
37
+ * records. Never the source of truth — the FileTree is. The index is fully
38
+ * rebuildable from a walk of the store ({@link IMemoryIndex.rebuild}) and is
39
+ * patched incrementally on every write ({@link IMemoryIndex.patch}).
40
+ *
41
+ * @remarks
42
+ * B1 builds the maps; link-traversal BFS over {@link IMemoryIndex.backlinks}
43
+ * is B2. The accessors return records (not bare ids) so the B2 retrievers can
44
+ * consume them directly.
45
+ * @public
46
+ */
47
+ export interface IMemoryIndex {
48
+ /**
49
+ * Replace the entire index from a full set of records (a store walk).
50
+ * @returns The number of records indexed.
51
+ */
52
+ rebuild(entries: ReadonlyArray<IIndexedMemoryRecord>): Result<number>;
53
+
54
+ /**
55
+ * Apply a single incremental change. `'put'` inserts or replaces the entry
56
+ * at its `(scope, id)` key (removing any prior associations first); `'delete'`
57
+ * removes it.
58
+ * @returns The entry that was applied.
59
+ */
60
+ patch(op: MemoryIndexPatchOp, entry: IIndexedMemoryRecord): Result<IIndexedMemoryRecord>;
61
+
62
+ /** Every indexed entry (scope + record). Primary read surface for the store. */
63
+ entries(): ReadonlyArray<IIndexedMemoryRecord>;
64
+
65
+ /** Records of the given kind, in recency order (most-recently-updated first). */
66
+ byKind(kind: Kind): ReadonlyArray<IMemoryRecord<unknown>>;
67
+
68
+ /** Records carrying the given tag, in recency order. */
69
+ byTag(tag: Tag): ReadonlyArray<IMemoryRecord<unknown>>;
70
+
71
+ /** All records in recency order (most-recently-updated first). */
72
+ byRecency(): ReadonlyArray<IMemoryRecord<unknown>>;
73
+
74
+ /**
75
+ * The ids of records whose `links` point AT `target` (inbound edges).
76
+ * The seed map for B2 link-traversal.
77
+ */
78
+ backlinks(target: MemoryId): ReadonlyArray<MemoryId>;
79
+ }
80
+
81
+ /**
82
+ * Default in-memory {@link IMemoryIndex}. Maintains the derived `byKind` /
83
+ * `byTag` / `byRecency` / `backlinks` views incrementally; a `'put'` for an
84
+ * already-indexed key first removes the prior entry's associations so a changed
85
+ * kind / tag / link set never leaves a stale reference behind.
86
+ * @public
87
+ */
88
+ export class MemoryIndex implements IMemoryIndex {
89
+ /** Primary store: `(scope, id)` composite key → indexed entry. */
90
+ private readonly _byKey: Map<string, IIndexedMemoryRecord>;
91
+ /** kind → set of composite keys. */
92
+ private readonly _byKind: Map<Kind, Set<string>>;
93
+ /** tag → set of composite keys. */
94
+ private readonly _byTag: Map<Tag, Set<string>>;
95
+ /**
96
+ * link target id → (source composite key → source id). Keyed by the source's
97
+ * `(scope, id)` composite — NOT its bare id — so two distinct source records
98
+ * that share an id across scopes (e.g. `turn-0` in different conversations)
99
+ * are tracked independently and removing one never drops the other's edge.
100
+ */
101
+ private readonly _backlinks: Map<MemoryId, Map<string, MemoryId>>;
102
+
103
+ private constructor() {
104
+ this._byKey = new Map<string, IIndexedMemoryRecord>();
105
+ this._byKind = new Map<Kind, Set<string>>();
106
+ this._byTag = new Map<Tag, Set<string>>();
107
+ this._backlinks = new Map<MemoryId, Map<string, MemoryId>>();
108
+ }
109
+
110
+ /** Family-convention factory. */
111
+ public static create(): Result<MemoryIndex> {
112
+ return succeed(new MemoryIndex());
113
+ }
114
+
115
+ /**
116
+ * The composite primary key for an entry: scope + id, NUL-separated. NUL
117
+ * is excluded from both components (scope segments pass
118
+ * `assertPortableFilenameStem`; `MemoryId` is portable-filename-safe), so it
119
+ * is a collision-proof separator across every scope/id pair the codecs produce.
120
+ */
121
+ private static _keyOf(scope: MemoryScopeKey, id: MemoryId): string {
122
+ return `${scope}\0${id}`;
123
+ }
124
+
125
+ /** {@inheritDoc IMemoryIndex.rebuild} */
126
+ public rebuild(entries: ReadonlyArray<IIndexedMemoryRecord>): Result<number> {
127
+ this._byKey.clear();
128
+ this._byKind.clear();
129
+ this._byTag.clear();
130
+ this._backlinks.clear();
131
+ for (const entry of entries) {
132
+ this._add(entry);
133
+ }
134
+ return succeed(this._byKey.size);
135
+ }
136
+
137
+ /** {@inheritDoc IMemoryIndex.patch} */
138
+ public patch(op: MemoryIndexPatchOp, entry: IIndexedMemoryRecord): Result<IIndexedMemoryRecord> {
139
+ const key: string = MemoryIndex._keyOf(entry.scope, entry.record.envelope.id);
140
+ // Always drop any prior associations for this key first, so a 'put' that
141
+ // changes kind/tags/links cannot strand a stale reference.
142
+ this._remove(key);
143
+ if (op === 'put') {
144
+ this._add(entry);
145
+ }
146
+ return succeed(entry);
147
+ }
148
+
149
+ /** {@inheritDoc IMemoryIndex.entries} */
150
+ public entries(): ReadonlyArray<IIndexedMemoryRecord> {
151
+ return Array.from(this._byKey.values());
152
+ }
153
+
154
+ /** {@inheritDoc IMemoryIndex.byKind} */
155
+ public byKind(kind: Kind): ReadonlyArray<IMemoryRecord<unknown>> {
156
+ return this._recencyOrdered(this._byKind.get(kind) ?? EMPTY_KEY_SET);
157
+ }
158
+
159
+ /** {@inheritDoc IMemoryIndex.byTag} */
160
+ public byTag(tag: Tag): ReadonlyArray<IMemoryRecord<unknown>> {
161
+ return this._recencyOrdered(this._byTag.get(tag) ?? EMPTY_KEY_SET);
162
+ }
163
+
164
+ /** {@inheritDoc IMemoryIndex.byRecency} */
165
+ public byRecency(): ReadonlyArray<IMemoryRecord<unknown>> {
166
+ return this._recencyOrdered(this._byKey.keys());
167
+ }
168
+
169
+ /** {@inheritDoc IMemoryIndex.backlinks} */
170
+ public backlinks(target: MemoryId): ReadonlyArray<MemoryId> {
171
+ const sources: Map<string, MemoryId> | undefined = this._backlinks.get(target);
172
+ return sources === undefined ? [] : Array.from(sources.values());
173
+ }
174
+
175
+ /**
176
+ * Resolve a set of composite keys to their records, ordered
177
+ * most-recently-updated first (with a `seq` tiebreak so equal-`updated`
178
+ * records sort deterministically).
179
+ */
180
+ private _recencyOrdered(keys: Iterable<string>): ReadonlyArray<IMemoryRecord<unknown>> {
181
+ const records: IMemoryRecord<unknown>[] = [];
182
+ for (const key of keys) {
183
+ const entry: IIndexedMemoryRecord | undefined = this._byKey.get(key);
184
+ if (entry !== undefined) {
185
+ records.push(entry.record);
186
+ }
187
+ }
188
+ return records.sort((a, b) => {
189
+ const byUpdated: number = b.envelope.updated - a.envelope.updated;
190
+ return byUpdated !== 0 ? byUpdated : b.envelope.seq - a.envelope.seq;
191
+ });
192
+ }
193
+
194
+ /** Insert an entry and register all its derived associations. */
195
+ private _add(entry: IIndexedMemoryRecord): void {
196
+ const key: string = MemoryIndex._keyOf(entry.scope, entry.record.envelope.id);
197
+ const envelope: IMemoryRecord<unknown>['envelope'] = entry.record.envelope;
198
+ this._byKey.set(key, entry);
199
+ this._addToSetMap(this._byKind, envelope.kind, key);
200
+ for (const tag of envelope.tags) {
201
+ this._addToSetMap(this._byTag, tag, key);
202
+ }
203
+ for (const edge of envelope.links) {
204
+ this._addBacklink(edge.target, key, envelope.id);
205
+ }
206
+ }
207
+
208
+ /** Remove the entry at `key` (if present) and all its derived associations. */
209
+ private _remove(key: string): void {
210
+ const entry: IIndexedMemoryRecord | undefined = this._byKey.get(key);
211
+ if (entry === undefined) {
212
+ return;
213
+ }
214
+ const envelope: IMemoryRecord<unknown>['envelope'] = entry.record.envelope;
215
+ this._byKey.delete(key);
216
+ this._removeFromSetMap(this._byKind, envelope.kind, key);
217
+ for (const tag of envelope.tags) {
218
+ this._removeFromSetMap(this._byTag, tag, key);
219
+ }
220
+ for (const edge of envelope.links) {
221
+ this._removeBacklink(edge.target, key);
222
+ }
223
+ }
224
+
225
+ /** Register `sourceId` (keyed by its composite `sourceKey`) as linking at `target`. */
226
+ private _addBacklink(target: MemoryId, sourceKey: string, sourceId: MemoryId): void {
227
+ const existing: Map<string, MemoryId> | undefined = this._backlinks.get(target);
228
+ if (existing === undefined) {
229
+ this._backlinks.set(target, new Map<string, MemoryId>([[sourceKey, sourceId]]));
230
+ } else {
231
+ existing.set(sourceKey, sourceId);
232
+ }
233
+ }
234
+
235
+ /** Drop the backlink from `sourceKey` to `target`, removing the target map when empty. */
236
+ private _removeBacklink(target: MemoryId, sourceKey: string): void {
237
+ const existing: Map<string, MemoryId> | undefined = this._backlinks.get(target);
238
+ if (existing === undefined) {
239
+ return;
240
+ }
241
+ existing.delete(sourceKey);
242
+ if (existing.size === 0) {
243
+ this._backlinks.delete(target);
244
+ }
245
+ }
246
+
247
+ /** Add `member` to the set at `mapKey`, creating the set on first use. */
248
+ private _addToSetMap<K, M>(map: Map<K, Set<M>>, mapKey: K, member: M): void {
249
+ const existing: Set<M> | undefined = map.get(mapKey);
250
+ if (existing === undefined) {
251
+ map.set(mapKey, new Set<M>([member]));
252
+ } else {
253
+ existing.add(member);
254
+ }
255
+ }
256
+
257
+ /** Remove `member` from the set at `mapKey`, dropping the set when empty. */
258
+ private _removeFromSetMap<K, M>(map: Map<K, Set<M>>, mapKey: K, member: M): void {
259
+ const existing: Set<M> | undefined = map.get(mapKey);
260
+ if (existing === undefined) {
261
+ return;
262
+ }
263
+ existing.delete(member);
264
+ if (existing.size === 0) {
265
+ map.delete(mapKey);
266
+ }
267
+ }
268
+ }
@@ -0,0 +1,7 @@
1
+ /*
2
+ * Copyright (c) 2026 Erik Fortune
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ export * from './observer';
7
+ export * from './memoryObservationStore';
@@ -0,0 +1,153 @@
1
+ /*
2
+ * Copyright (c) 2026 Erik Fortune
3
+ * SPDX-License-Identifier: MIT
4
+ */
5
+
6
+ import { Collections, Result, fail, succeed } from '@fgv/ts-utils';
7
+ import { IMemoryObservationQuery, IMemoryObservationRecord, IMemoryObserver } from './observer';
8
+
9
+ /**
10
+ * Construction options for {@link MemoryObservationStore.create}.
11
+ * @public
12
+ */
13
+ export interface IMemoryObservationStoreCreateParams {
14
+ /**
15
+ * Maximum number of observation records retained before the oldest is
16
+ * overwritten. Defaults to `1000`. Must be a positive integer if supplied.
17
+ */
18
+ readonly maxRecords?: number;
19
+ }
20
+
21
+ /**
22
+ * The default in-memory observation store: an {@link IMemoryObserver} that
23
+ * retains the records it observes in a bounded ring and answers schema-aware
24
+ * {@link MemoryObservationStore.query | queries} over them.
25
+ *
26
+ * @remarks
27
+ * **Privacy posture — this store is most-permissive by design.** It retains
28
+ * every field of every record verbatim, including any `provenance` and
29
+ * `querySnapshot`. The library bakes in **no** redaction, retention, or
30
+ * field-stripping policy — that is deployment policy, not library policy. A
31
+ * deployment that must redact wraps this store with its own
32
+ * {@link IMemoryObserver} that transforms records before forwarding, or
33
+ * substitutes a different observer entirely. Size is the only bounded
34
+ * dimension, via `maxRecords`.
35
+ *
36
+ * The store composes `RetainingRingBuffer` from `@fgv/ts-utils` (it does not
37
+ * hand-roll a ring) and implements {@link IMemoryObserver} directly — `observe`
38
+ * (the hook) and `query` (the read surface) live on the same class. Wire it via
39
+ * {@link IFileTreeMemoryStoreCreateParams.observers}. `seq` and `timestamp` are
40
+ * assigned by the firing authority (the store) before `observe`, so this store
41
+ * never mints them.
42
+ * @public
43
+ */
44
+ export class MemoryObservationStore implements IMemoryObserver {
45
+ /**
46
+ * The bounded ring of observed records. The firing authority assigns each
47
+ * record's `seq`, so the ring's monotonic-`seq` cursor contract is satisfied
48
+ * by that authority's per-instance counter.
49
+ * @internal
50
+ */
51
+ private readonly _buffer: Collections.RetainingRingBuffer<IMemoryObservationRecord>;
52
+
53
+ /**
54
+ * @param buffer - The pre-constructed backing ring buffer.
55
+ * @internal
56
+ */
57
+ private constructor(buffer: Collections.RetainingRingBuffer<IMemoryObservationRecord>) {
58
+ this._buffer = buffer;
59
+ }
60
+
61
+ /**
62
+ * The highest `seq` observed so far. Hold this value and pass it as
63
+ * `sinceSeq` to {@link MemoryObservationStore.query | query} to page only
64
+ * records observed afterward. Stable across ring eviction and
65
+ * {@link MemoryObservationStore.clear | clear}.
66
+ */
67
+ public get lastSeq(): number {
68
+ return this._buffer.lastSeq;
69
+ }
70
+
71
+ /**
72
+ * The number of records currently retained.
73
+ */
74
+ public get size(): number {
75
+ return this._buffer.size;
76
+ }
77
+
78
+ /**
79
+ * Family-convention factory.
80
+ * @param params - {@link IMemoryObservationStoreCreateParams | Construction options}.
81
+ * @returns On success, a new store. Fails if `maxRecords` is supplied and is
82
+ * not a positive integer.
83
+ */
84
+ public static create(params?: IMemoryObservationStoreCreateParams): Result<MemoryObservationStore> {
85
+ const maxRecords: number | undefined = params?.maxRecords;
86
+ if (maxRecords !== undefined && (!Number.isInteger(maxRecords) || maxRecords < 1)) {
87
+ return fail(`MemoryObservationStore: maxRecords must be a positive integer (got ${maxRecords})`);
88
+ }
89
+ return succeed(
90
+ new MemoryObservationStore(
91
+ new Collections.RetainingRingBuffer<IMemoryObservationRecord>({ maxRecords })
92
+ )
93
+ );
94
+ }
95
+
96
+ /**
97
+ * {@inheritDoc IMemoryObserver.observe}
98
+ */
99
+ public observe(record: IMemoryObservationRecord): Promise<Result<unknown>> {
100
+ this._buffer.push(record);
101
+ return Promise.resolve(succeed(record));
102
+ }
103
+
104
+ /**
105
+ * Returns retained records, oldest-first, narrowed by the supplied criteria.
106
+ * @param criteria - {@link IMemoryObservationQuery | AND-combined filter criteria}.
107
+ * @returns The matching records, oldest-first.
108
+ */
109
+ public query(criteria?: IMemoryObservationQuery): ReadonlyArray<IMemoryObservationRecord> {
110
+ return this._buffer.query({
111
+ sinceSeq: criteria?.sinceSeq,
112
+ limit: criteria?.limit,
113
+ filter:
114
+ criteria === undefined ? undefined : (record) => MemoryObservationStore._matches(record, criteria)
115
+ });
116
+ }
117
+
118
+ /**
119
+ * Clears all retained records. Does NOT reset
120
+ * {@link MemoryObservationStore.lastSeq | lastSeq}, so a held `sinceSeq`
121
+ * cursor never re-sees a sequence number.
122
+ */
123
+ public clear(): void {
124
+ this._buffer.clear();
125
+ }
126
+
127
+ /**
128
+ * Tests a record against the non-`seq`/`limit` criteria (those are applied by
129
+ * the ring buffer itself).
130
+ * @internal
131
+ */
132
+ private static _matches(record: IMemoryObservationRecord, criteria: IMemoryObservationQuery): boolean {
133
+ if (criteria.since !== undefined && record.timestamp < criteria.since) {
134
+ return false;
135
+ }
136
+ if (criteria.until !== undefined && record.timestamp > criteria.until) {
137
+ return false;
138
+ }
139
+ if (criteria.scope !== undefined && record.scope !== criteria.scope) {
140
+ return false;
141
+ }
142
+ if (criteria.kind !== undefined && record.kind !== criteria.kind) {
143
+ return false;
144
+ }
145
+ if (criteria.phase !== undefined && record.phase !== criteria.phase) {
146
+ return false;
147
+ }
148
+ if (criteria.outcome !== undefined && record.outcome !== criteria.outcome) {
149
+ return false;
150
+ }
151
+ return true;
152
+ }
153
+ }