@fgv/ts-agent-memory 5.1.0-47 → 5.1.0-49

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 (376) hide show
  1. package/dist/packlets/ingest/orchestrator.js +11 -1
  2. package/dist/packlets/ingest/orchestrator.js.map +1 -1
  3. package/dist/packlets/observe/memoryObservationStore.js +6 -0
  4. package/dist/packlets/observe/memoryObservationStore.js.map +1 -1
  5. package/dist/packlets/observe/observer.js.map +1 -1
  6. package/dist/packlets/retrieve/linkTraversalRetriever.js +1 -1
  7. package/dist/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
  8. package/dist/packlets/retrieve/recencyRetriever.js +1 -1
  9. package/dist/packlets/retrieve/recencyRetriever.js.map +1 -1
  10. package/dist/packlets/retrieve/retriever.js +11 -5
  11. package/dist/packlets/retrieve/retriever.js.map +1 -1
  12. package/dist/packlets/retrieve/structuredFilterRetriever.js +27 -5
  13. package/dist/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
  14. package/dist/packlets/retrieve/tagRetriever.js +1 -1
  15. package/dist/packlets/retrieve/tagRetriever.js.map +1 -1
  16. package/dist/packlets/retrieve/temporalRetrievers.js +1 -1
  17. package/dist/packlets/retrieve/temporalRetrievers.js.map +1 -1
  18. package/dist/packlets/store/fileTreeMemoryStore.js +152 -132
  19. package/dist/packlets/store/fileTreeMemoryStore.js.map +1 -1
  20. package/dist/packlets/store/vectorMaintenance.js +251 -0
  21. package/dist/packlets/store/vectorMaintenance.js.map +1 -0
  22. package/dist/packlets/types/envelope.js.map +1 -1
  23. package/dist/packlets/vector/inMemoryCosineIndex.js +81 -18
  24. package/dist/packlets/vector/inMemoryCosineIndex.js.map +1 -1
  25. package/dist/packlets/vector/inMemoryFragmentCosineIndex.js +7 -0
  26. package/dist/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
  27. package/dist/packlets/vector/vectorIndex.js.map +1 -1
  28. package/dist/ts-agent-memory.d.ts +431 -79
  29. package/lib/packlets/ingest/orchestrator.d.ts.map +1 -1
  30. package/lib/packlets/ingest/orchestrator.js +11 -1
  31. package/lib/packlets/ingest/orchestrator.js.map +1 -1
  32. package/lib/packlets/observe/memoryObservationStore.d.ts.map +1 -1
  33. package/lib/packlets/observe/memoryObservationStore.js +6 -0
  34. package/lib/packlets/observe/memoryObservationStore.js.map +1 -1
  35. package/lib/packlets/observe/observer.d.ts +61 -0
  36. package/lib/packlets/observe/observer.d.ts.map +1 -1
  37. package/lib/packlets/observe/observer.js.map +1 -1
  38. package/lib/packlets/retrieve/linkTraversalRetriever.d.ts +1 -1
  39. package/lib/packlets/retrieve/linkTraversalRetriever.js +1 -1
  40. package/lib/packlets/retrieve/linkTraversalRetriever.js.map +1 -1
  41. package/lib/packlets/retrieve/recencyRetriever.d.ts +1 -1
  42. package/lib/packlets/retrieve/recencyRetriever.js +1 -1
  43. package/lib/packlets/retrieve/recencyRetriever.js.map +1 -1
  44. package/lib/packlets/retrieve/retriever.d.ts +26 -7
  45. package/lib/packlets/retrieve/retriever.d.ts.map +1 -1
  46. package/lib/packlets/retrieve/retriever.js +11 -5
  47. package/lib/packlets/retrieve/retriever.js.map +1 -1
  48. package/lib/packlets/retrieve/structuredFilterRetriever.d.ts +26 -4
  49. package/lib/packlets/retrieve/structuredFilterRetriever.d.ts.map +1 -1
  50. package/lib/packlets/retrieve/structuredFilterRetriever.js +27 -5
  51. package/lib/packlets/retrieve/structuredFilterRetriever.js.map +1 -1
  52. package/lib/packlets/retrieve/tagRetriever.d.ts +1 -1
  53. package/lib/packlets/retrieve/tagRetriever.js +1 -1
  54. package/lib/packlets/retrieve/tagRetriever.js.map +1 -1
  55. package/lib/packlets/retrieve/temporalRetrievers.js +1 -1
  56. package/lib/packlets/retrieve/temporalRetrievers.js.map +1 -1
  57. package/lib/packlets/store/fileTreeMemoryStore.d.ts +135 -59
  58. package/lib/packlets/store/fileTreeMemoryStore.d.ts.map +1 -1
  59. package/lib/packlets/store/fileTreeMemoryStore.js +151 -131
  60. package/lib/packlets/store/fileTreeMemoryStore.js.map +1 -1
  61. package/lib/packlets/store/vectorMaintenance.d.ts +149 -0
  62. package/lib/packlets/store/vectorMaintenance.d.ts.map +1 -0
  63. package/lib/packlets/store/vectorMaintenance.js +255 -0
  64. package/lib/packlets/store/vectorMaintenance.js.map +1 -0
  65. package/lib/packlets/types/envelope.d.ts +11 -0
  66. package/lib/packlets/types/envelope.d.ts.map +1 -1
  67. package/lib/packlets/types/envelope.js.map +1 -1
  68. package/lib/packlets/vector/inMemoryCosineIndex.d.ts +30 -7
  69. package/lib/packlets/vector/inMemoryCosineIndex.d.ts.map +1 -1
  70. package/lib/packlets/vector/inMemoryCosineIndex.js +80 -17
  71. package/lib/packlets/vector/inMemoryCosineIndex.js.map +1 -1
  72. package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts +7 -0
  73. package/lib/packlets/vector/inMemoryFragmentCosineIndex.d.ts.map +1 -1
  74. package/lib/packlets/vector/inMemoryFragmentCosineIndex.js +7 -0
  75. package/lib/packlets/vector/inMemoryFragmentCosineIndex.js.map +1 -1
  76. package/lib/packlets/vector/vectorIndex.d.ts +129 -1
  77. package/lib/packlets/vector/vectorIndex.d.ts.map +1 -1
  78. package/lib/packlets/vector/vectorIndex.js.map +1 -1
  79. package/package.json +17 -7
  80. package/.rush/temp/5667e36f1c0cc503a5c20a3ebff76e7b9a702d41.tar.log +0 -468
  81. package/.rush/temp/chunked-rush-logs/ts-agent-memory.build.chunks.jsonl +0 -9
  82. package/.rush/temp/operation/build/all.log +0 -9
  83. package/.rush/temp/operation/build/log-chunks.jsonl +0 -9
  84. package/.rush/temp/operation/build/state.json +0 -3
  85. package/.rush/temp/shrinkwrap-deps.json +0 -689
  86. package/config/api-extractor.json +0 -343
  87. package/config/jest.config.json +0 -14
  88. package/config/rig.json +0 -4
  89. package/dist/test/unit/converters/antagonistRoundTrip.test.js +0 -95
  90. package/dist/test/unit/converters/antagonistRoundTrip.test.js.map +0 -1
  91. package/dist/test/unit/converters/bodyConverterRegistry.test.js +0 -72
  92. package/dist/test/unit/converters/bodyConverterRegistry.test.js.map +0 -1
  93. package/dist/test/unit/converters/envelopeConverter.test.js +0 -313
  94. package/dist/test/unit/converters/envelopeConverter.test.js.map +0 -1
  95. package/dist/test/unit/index/memoryIndex.test.js +0 -214
  96. package/dist/test/unit/index/memoryIndex.test.js.map +0 -1
  97. package/dist/test/unit/ingest/antagonistCycleAndParity.test.js +0 -277
  98. package/dist/test/unit/ingest/antagonistCycleAndParity.test.js.map +0 -1
  99. package/dist/test/unit/ingest/cycleGuard.test.js +0 -81
  100. package/dist/test/unit/ingest/cycleGuard.test.js.map +0 -1
  101. package/dist/test/unit/ingest/dedupScope.test.js +0 -434
  102. package/dist/test/unit/ingest/dedupScope.test.js.map +0 -1
  103. package/dist/test/unit/ingest/orchestrator.test.js +0 -1058
  104. package/dist/test/unit/ingest/orchestrator.test.js.map +0 -1
  105. package/dist/test/unit/observe/memoryObservationStore.test.js +0 -118
  106. package/dist/test/unit/observe/memoryObservationStore.test.js.map +0 -1
  107. package/dist/test/unit/retrieve/fragmentSemanticRetriever.test.js +0 -116
  108. package/dist/test/unit/retrieve/fragmentSemanticRetriever.test.js.map +0 -1
  109. package/dist/test/unit/retrieve/linkTraversalRetriever.test.js +0 -257
  110. package/dist/test/unit/retrieve/linkTraversalRetriever.test.js.map +0 -1
  111. package/dist/test/unit/retrieve/retrievers.test.js +0 -768
  112. package/dist/test/unit/retrieve/retrievers.test.js.map +0 -1
  113. package/dist/test/unit/retrieve/temporalRetrievers.test.js +0 -182
  114. package/dist/test/unit/retrieve/temporalRetrievers.test.js.map +0 -1
  115. package/dist/test/unit/store/antagonistTemporalBoundary.test.js +0 -120
  116. package/dist/test/unit/store/antagonistTemporalBoundary.test.js.map +0 -1
  117. package/dist/test/unit/store/embedOnWrite.test.js +0 -317
  118. package/dist/test/unit/store/embedOnWrite.test.js.map +0 -1
  119. package/dist/test/unit/store/fileTreeMemoryStore.test.js +0 -778
  120. package/dist/test/unit/store/fileTreeMemoryStore.test.js.map +0 -1
  121. package/dist/test/unit/store/fragmentEmbedOnWrite.test.js +0 -255
  122. package/dist/test/unit/store/fragmentEmbedOnWrite.test.js.map +0 -1
  123. package/dist/test/unit/store/indexInjection.test.js +0 -432
  124. package/dist/test/unit/store/indexInjection.test.js.map +0 -1
  125. package/dist/test/unit/store/lenientOpen.test.js +0 -248
  126. package/dist/test/unit/store/lenientOpen.test.js.map +0 -1
  127. package/dist/test/unit/store/listScoped.test.js +0 -109
  128. package/dist/test/unit/store/listScoped.test.js.map +0 -1
  129. package/dist/test/unit/store/observations.test.js +0 -239
  130. package/dist/test/unit/store/observations.test.js.map +0 -1
  131. package/dist/test/unit/store/rankAxis.test.js +0 -254
  132. package/dist/test/unit/store/rankAxis.test.js.map +0 -1
  133. package/dist/test/unit/store/scopeEncoding.test.js +0 -24
  134. package/dist/test/unit/store/scopeEncoding.test.js.map +0 -1
  135. package/dist/test/unit/store/temporalStore.test.js +0 -398
  136. package/dist/test/unit/store/temporalStore.test.js.map +0 -1
  137. package/dist/test/unit/tools/memoryTools.test.js +0 -842
  138. package/dist/test/unit/tools/memoryTools.test.js.map +0 -1
  139. package/dist/test/unit/types/identityCodec.test.js +0 -187
  140. package/dist/test/unit/types/identityCodec.test.js.map +0 -1
  141. package/dist/test/unit/types/ids.test.js +0 -84
  142. package/dist/test/unit/types/ids.test.js.map +0 -1
  143. package/dist/test/unit/types/temporalCodec.test.js +0 -203
  144. package/dist/test/unit/types/temporalCodec.test.js.map +0 -1
  145. package/dist/test/unit/types/temporalPolicy.test.js +0 -83
  146. package/dist/test/unit/types/temporalPolicy.test.js.map +0 -1
  147. package/dist/test/unit/types/writePolicy.test.js +0 -331
  148. package/dist/test/unit/types/writePolicy.test.js.map +0 -1
  149. package/dist/test/unit/vector/fragmentConverters.test.js +0 -58
  150. package/dist/test/unit/vector/fragmentConverters.test.js.map +0 -1
  151. package/dist/test/unit/vector/inMemoryCosineIndex.test.js +0 -252
  152. package/dist/test/unit/vector/inMemoryCosineIndex.test.js.map +0 -1
  153. package/dist/test/unit/vector/inMemoryFragmentCosineIndex.test.js +0 -362
  154. package/dist/test/unit/vector/inMemoryFragmentCosineIndex.test.js.map +0 -1
  155. package/dist/test/unit/vector/vectorIndex.test.js +0 -51
  156. package/dist/test/unit/vector/vectorIndex.test.js.map +0 -1
  157. package/eslint.config.js +0 -15
  158. package/etc/ts-agent-memory.api.md +0 -940
  159. package/lib/test/unit/converters/antagonistRoundTrip.test.d.ts +0 -10
  160. package/lib/test/unit/converters/antagonistRoundTrip.test.d.ts.map +0 -1
  161. package/lib/test/unit/converters/antagonistRoundTrip.test.js +0 -97
  162. package/lib/test/unit/converters/antagonistRoundTrip.test.js.map +0 -1
  163. package/lib/test/unit/converters/bodyConverterRegistry.test.d.ts +0 -2
  164. package/lib/test/unit/converters/bodyConverterRegistry.test.d.ts.map +0 -1
  165. package/lib/test/unit/converters/bodyConverterRegistry.test.js +0 -74
  166. package/lib/test/unit/converters/bodyConverterRegistry.test.js.map +0 -1
  167. package/lib/test/unit/converters/envelopeConverter.test.d.ts +0 -2
  168. package/lib/test/unit/converters/envelopeConverter.test.d.ts.map +0 -1
  169. package/lib/test/unit/converters/envelopeConverter.test.js +0 -315
  170. package/lib/test/unit/converters/envelopeConverter.test.js.map +0 -1
  171. package/lib/test/unit/index/memoryIndex.test.d.ts +0 -2
  172. package/lib/test/unit/index/memoryIndex.test.d.ts.map +0 -1
  173. package/lib/test/unit/index/memoryIndex.test.js +0 -216
  174. package/lib/test/unit/index/memoryIndex.test.js.map +0 -1
  175. package/lib/test/unit/ingest/antagonistCycleAndParity.test.d.ts +0 -9
  176. package/lib/test/unit/ingest/antagonistCycleAndParity.test.d.ts.map +0 -1
  177. package/lib/test/unit/ingest/antagonistCycleAndParity.test.js +0 -279
  178. package/lib/test/unit/ingest/antagonistCycleAndParity.test.js.map +0 -1
  179. package/lib/test/unit/ingest/cycleGuard.test.d.ts +0 -2
  180. package/lib/test/unit/ingest/cycleGuard.test.d.ts.map +0 -1
  181. package/lib/test/unit/ingest/cycleGuard.test.js +0 -83
  182. package/lib/test/unit/ingest/cycleGuard.test.js.map +0 -1
  183. package/lib/test/unit/ingest/dedupScope.test.d.ts +0 -2
  184. package/lib/test/unit/ingest/dedupScope.test.d.ts.map +0 -1
  185. package/lib/test/unit/ingest/dedupScope.test.js +0 -436
  186. package/lib/test/unit/ingest/dedupScope.test.js.map +0 -1
  187. package/lib/test/unit/ingest/orchestrator.test.d.ts +0 -2
  188. package/lib/test/unit/ingest/orchestrator.test.d.ts.map +0 -1
  189. package/lib/test/unit/ingest/orchestrator.test.js +0 -1060
  190. package/lib/test/unit/ingest/orchestrator.test.js.map +0 -1
  191. package/lib/test/unit/observe/memoryObservationStore.test.d.ts +0 -2
  192. package/lib/test/unit/observe/memoryObservationStore.test.d.ts.map +0 -1
  193. package/lib/test/unit/observe/memoryObservationStore.test.js +0 -120
  194. package/lib/test/unit/observe/memoryObservationStore.test.js.map +0 -1
  195. package/lib/test/unit/retrieve/fragmentSemanticRetriever.test.d.ts +0 -2
  196. package/lib/test/unit/retrieve/fragmentSemanticRetriever.test.d.ts.map +0 -1
  197. package/lib/test/unit/retrieve/fragmentSemanticRetriever.test.js +0 -118
  198. package/lib/test/unit/retrieve/fragmentSemanticRetriever.test.js.map +0 -1
  199. package/lib/test/unit/retrieve/linkTraversalRetriever.test.d.ts +0 -2
  200. package/lib/test/unit/retrieve/linkTraversalRetriever.test.d.ts.map +0 -1
  201. package/lib/test/unit/retrieve/linkTraversalRetriever.test.js +0 -259
  202. package/lib/test/unit/retrieve/linkTraversalRetriever.test.js.map +0 -1
  203. package/lib/test/unit/retrieve/retrievers.test.d.ts +0 -2
  204. package/lib/test/unit/retrieve/retrievers.test.d.ts.map +0 -1
  205. package/lib/test/unit/retrieve/retrievers.test.js +0 -770
  206. package/lib/test/unit/retrieve/retrievers.test.js.map +0 -1
  207. package/lib/test/unit/retrieve/temporalRetrievers.test.d.ts +0 -2
  208. package/lib/test/unit/retrieve/temporalRetrievers.test.d.ts.map +0 -1
  209. package/lib/test/unit/retrieve/temporalRetrievers.test.js +0 -184
  210. package/lib/test/unit/retrieve/temporalRetrievers.test.js.map +0 -1
  211. package/lib/test/unit/store/antagonistTemporalBoundary.test.d.ts +0 -9
  212. package/lib/test/unit/store/antagonistTemporalBoundary.test.d.ts.map +0 -1
  213. package/lib/test/unit/store/antagonistTemporalBoundary.test.js +0 -122
  214. package/lib/test/unit/store/antagonistTemporalBoundary.test.js.map +0 -1
  215. package/lib/test/unit/store/embedOnWrite.test.d.ts +0 -2
  216. package/lib/test/unit/store/embedOnWrite.test.d.ts.map +0 -1
  217. package/lib/test/unit/store/embedOnWrite.test.js +0 -319
  218. package/lib/test/unit/store/embedOnWrite.test.js.map +0 -1
  219. package/lib/test/unit/store/fileTreeMemoryStore.test.d.ts +0 -2
  220. package/lib/test/unit/store/fileTreeMemoryStore.test.d.ts.map +0 -1
  221. package/lib/test/unit/store/fileTreeMemoryStore.test.js +0 -780
  222. package/lib/test/unit/store/fileTreeMemoryStore.test.js.map +0 -1
  223. package/lib/test/unit/store/fragmentEmbedOnWrite.test.d.ts +0 -2
  224. package/lib/test/unit/store/fragmentEmbedOnWrite.test.d.ts.map +0 -1
  225. package/lib/test/unit/store/fragmentEmbedOnWrite.test.js +0 -257
  226. package/lib/test/unit/store/fragmentEmbedOnWrite.test.js.map +0 -1
  227. package/lib/test/unit/store/indexInjection.test.d.ts +0 -2
  228. package/lib/test/unit/store/indexInjection.test.d.ts.map +0 -1
  229. package/lib/test/unit/store/indexInjection.test.js +0 -434
  230. package/lib/test/unit/store/indexInjection.test.js.map +0 -1
  231. package/lib/test/unit/store/lenientOpen.test.d.ts +0 -2
  232. package/lib/test/unit/store/lenientOpen.test.d.ts.map +0 -1
  233. package/lib/test/unit/store/lenientOpen.test.js +0 -250
  234. package/lib/test/unit/store/lenientOpen.test.js.map +0 -1
  235. package/lib/test/unit/store/listScoped.test.d.ts +0 -2
  236. package/lib/test/unit/store/listScoped.test.d.ts.map +0 -1
  237. package/lib/test/unit/store/listScoped.test.js +0 -111
  238. package/lib/test/unit/store/listScoped.test.js.map +0 -1
  239. package/lib/test/unit/store/observations.test.d.ts +0 -2
  240. package/lib/test/unit/store/observations.test.d.ts.map +0 -1
  241. package/lib/test/unit/store/observations.test.js +0 -241
  242. package/lib/test/unit/store/observations.test.js.map +0 -1
  243. package/lib/test/unit/store/rankAxis.test.d.ts +0 -2
  244. package/lib/test/unit/store/rankAxis.test.d.ts.map +0 -1
  245. package/lib/test/unit/store/rankAxis.test.js +0 -256
  246. package/lib/test/unit/store/rankAxis.test.js.map +0 -1
  247. package/lib/test/unit/store/scopeEncoding.test.d.ts +0 -2
  248. package/lib/test/unit/store/scopeEncoding.test.d.ts.map +0 -1
  249. package/lib/test/unit/store/scopeEncoding.test.js +0 -26
  250. package/lib/test/unit/store/scopeEncoding.test.js.map +0 -1
  251. package/lib/test/unit/store/temporalStore.test.d.ts +0 -2
  252. package/lib/test/unit/store/temporalStore.test.d.ts.map +0 -1
  253. package/lib/test/unit/store/temporalStore.test.js +0 -400
  254. package/lib/test/unit/store/temporalStore.test.js.map +0 -1
  255. package/lib/test/unit/tools/memoryTools.test.d.ts +0 -2
  256. package/lib/test/unit/tools/memoryTools.test.d.ts.map +0 -1
  257. package/lib/test/unit/tools/memoryTools.test.js +0 -844
  258. package/lib/test/unit/tools/memoryTools.test.js.map +0 -1
  259. package/lib/test/unit/types/identityCodec.test.d.ts +0 -2
  260. package/lib/test/unit/types/identityCodec.test.d.ts.map +0 -1
  261. package/lib/test/unit/types/identityCodec.test.js +0 -189
  262. package/lib/test/unit/types/identityCodec.test.js.map +0 -1
  263. package/lib/test/unit/types/ids.test.d.ts +0 -2
  264. package/lib/test/unit/types/ids.test.d.ts.map +0 -1
  265. package/lib/test/unit/types/ids.test.js +0 -86
  266. package/lib/test/unit/types/ids.test.js.map +0 -1
  267. package/lib/test/unit/types/temporalCodec.test.d.ts +0 -2
  268. package/lib/test/unit/types/temporalCodec.test.d.ts.map +0 -1
  269. package/lib/test/unit/types/temporalCodec.test.js +0 -205
  270. package/lib/test/unit/types/temporalCodec.test.js.map +0 -1
  271. package/lib/test/unit/types/temporalPolicy.test.d.ts +0 -2
  272. package/lib/test/unit/types/temporalPolicy.test.d.ts.map +0 -1
  273. package/lib/test/unit/types/temporalPolicy.test.js +0 -85
  274. package/lib/test/unit/types/temporalPolicy.test.js.map +0 -1
  275. package/lib/test/unit/types/writePolicy.test.d.ts +0 -2
  276. package/lib/test/unit/types/writePolicy.test.d.ts.map +0 -1
  277. package/lib/test/unit/types/writePolicy.test.js +0 -333
  278. package/lib/test/unit/types/writePolicy.test.js.map +0 -1
  279. package/lib/test/unit/vector/fragmentConverters.test.d.ts +0 -2
  280. package/lib/test/unit/vector/fragmentConverters.test.d.ts.map +0 -1
  281. package/lib/test/unit/vector/fragmentConverters.test.js +0 -60
  282. package/lib/test/unit/vector/fragmentConverters.test.js.map +0 -1
  283. package/lib/test/unit/vector/inMemoryCosineIndex.test.d.ts +0 -2
  284. package/lib/test/unit/vector/inMemoryCosineIndex.test.d.ts.map +0 -1
  285. package/lib/test/unit/vector/inMemoryCosineIndex.test.js +0 -254
  286. package/lib/test/unit/vector/inMemoryCosineIndex.test.js.map +0 -1
  287. package/lib/test/unit/vector/inMemoryFragmentCosineIndex.test.d.ts +0 -2
  288. package/lib/test/unit/vector/inMemoryFragmentCosineIndex.test.d.ts.map +0 -1
  289. package/lib/test/unit/vector/inMemoryFragmentCosineIndex.test.js +0 -364
  290. package/lib/test/unit/vector/inMemoryFragmentCosineIndex.test.js.map +0 -1
  291. package/lib/test/unit/vector/vectorIndex.test.d.ts +0 -2
  292. package/lib/test/unit/vector/vectorIndex.test.d.ts.map +0 -1
  293. package/lib/test/unit/vector/vectorIndex.test.js +0 -53
  294. package/lib/test/unit/vector/vectorIndex.test.js.map +0 -1
  295. package/rush-logs/ts-agent-memory.build.cache.log +0 -3
  296. package/rush-logs/ts-agent-memory.build.log +0 -9
  297. package/src/index.ts +0 -14
  298. package/src/packlets/converters/bodyConverterRegistry.ts +0 -105
  299. package/src/packlets/converters/envelopeConverter.ts +0 -233
  300. package/src/packlets/converters/index.ts +0 -7
  301. package/src/packlets/index/index.ts +0 -6
  302. package/src/packlets/index/memoryIndex.ts +0 -332
  303. package/src/packlets/ingest/cycleGuard.ts +0 -153
  304. package/src/packlets/ingest/hostStages.ts +0 -111
  305. package/src/packlets/ingest/index.ts +0 -9
  306. package/src/packlets/ingest/model.ts +0 -195
  307. package/src/packlets/ingest/orchestrator.ts +0 -968
  308. package/src/packlets/observe/index.ts +0 -7
  309. package/src/packlets/observe/memoryObservationStore.ts +0 -153
  310. package/src/packlets/observe/observer.ts +0 -119
  311. package/src/packlets/retrieve/fragmentSemanticRetriever.ts +0 -138
  312. package/src/packlets/retrieve/hybridRetriever.ts +0 -194
  313. package/src/packlets/retrieve/index.ts +0 -14
  314. package/src/packlets/retrieve/linkTraversalRetriever.ts +0 -158
  315. package/src/packlets/retrieve/recencyRetriever.ts +0 -54
  316. package/src/packlets/retrieve/retriever.ts +0 -291
  317. package/src/packlets/retrieve/semanticRetriever.ts +0 -152
  318. package/src/packlets/retrieve/structuredFilterRetriever.ts +0 -58
  319. package/src/packlets/retrieve/tagRetriever.ts +0 -58
  320. package/src/packlets/retrieve/temporalRetrievers.ts +0 -210
  321. package/src/packlets/store/fileTreeMemoryStore.ts +0 -1872
  322. package/src/packlets/store/index.ts +0 -7
  323. package/src/packlets/store/scopeEncoding.ts +0 -36
  324. package/src/packlets/tools/index.ts +0 -6
  325. package/src/packlets/tools/memoryTools.ts +0 -706
  326. package/src/packlets/types/envelope.ts +0 -196
  327. package/src/packlets/types/filenameSafety.ts +0 -57
  328. package/src/packlets/types/identityCodec.ts +0 -447
  329. package/src/packlets/types/ids.ts +0 -124
  330. package/src/packlets/types/index.ts +0 -11
  331. package/src/packlets/types/temporal.ts +0 -96
  332. package/src/packlets/types/writePolicy.ts +0 -612
  333. package/src/packlets/vector/fragmentConverters.ts +0 -55
  334. package/src/packlets/vector/inMemoryCosineIndex.ts +0 -196
  335. package/src/packlets/vector/inMemoryFragmentCosineIndex.ts +0 -289
  336. package/src/packlets/vector/index.ts +0 -9
  337. package/src/packlets/vector/vectorIndex.ts +0 -273
  338. package/src/test/unit/converters/antagonistRoundTrip.test.ts +0 -110
  339. package/src/test/unit/converters/bodyConverterRegistry.test.ts +0 -89
  340. package/src/test/unit/converters/envelopeConverter.test.ts +0 -418
  341. package/src/test/unit/index/memoryIndex.test.ts +0 -272
  342. package/src/test/unit/ingest/antagonistCycleAndParity.test.ts +0 -367
  343. package/src/test/unit/ingest/cycleGuard.test.ts +0 -110
  344. package/src/test/unit/ingest/dedupScope.test.ts +0 -542
  345. package/src/test/unit/ingest/orchestrator.test.ts +0 -1355
  346. package/src/test/unit/observe/memoryObservationStore.test.ts +0 -158
  347. package/src/test/unit/retrieve/fragmentSemanticRetriever.test.ts +0 -163
  348. package/src/test/unit/retrieve/linkTraversalRetriever.test.ts +0 -329
  349. package/src/test/unit/retrieve/retrievers.test.ts +0 -1018
  350. package/src/test/unit/retrieve/temporalRetrievers.test.ts +0 -226
  351. package/src/test/unit/store/antagonistTemporalBoundary.test.ts +0 -158
  352. package/src/test/unit/store/embedOnWrite.test.ts +0 -418
  353. package/src/test/unit/store/fileTreeMemoryStore.test.ts +0 -1041
  354. package/src/test/unit/store/fragmentEmbedOnWrite.test.ts +0 -349
  355. package/src/test/unit/store/indexInjection.test.ts +0 -550
  356. package/src/test/unit/store/lenientOpen.test.ts +0 -292
  357. package/src/test/unit/store/listScoped.test.ts +0 -138
  358. package/src/test/unit/store/observations.test.ts +0 -290
  359. package/src/test/unit/store/rankAxis.test.ts +0 -349
  360. package/src/test/unit/store/scopeEncoding.test.ts +0 -37
  361. package/src/test/unit/store/temporalStore.test.ts +0 -469
  362. package/src/test/unit/tools/memoryTools.test.ts +0 -1121
  363. package/src/test/unit/types/identityCodec.test.ts +0 -266
  364. package/src/test/unit/types/ids.test.ts +0 -94
  365. package/src/test/unit/types/temporalCodec.test.ts +0 -259
  366. package/src/test/unit/types/temporalPolicy.test.ts +0 -124
  367. package/src/test/unit/types/writePolicy.test.ts +0 -446
  368. package/src/test/unit/vector/fragmentConverters.test.ts +0 -80
  369. package/src/test/unit/vector/inMemoryCosineIndex.test.ts +0 -318
  370. package/src/test/unit/vector/inMemoryFragmentCosineIndex.test.ts +0 -477
  371. package/src/test/unit/vector/vectorIndex.test.ts +0 -64
  372. package/temp/build/lint/_eslint-5eVG3S6w.json +0 -306
  373. package/temp/build/typescript/ts_8nwakTlr.json +0 -1
  374. package/temp/ts-agent-memory.api.json +0 -21030
  375. package/temp/ts-agent-memory.api.md +0 -940
  376. package/tsconfig.json +0 -8
@@ -1,612 +0,0 @@
1
- /*
2
- * Copyright (c) 2026 Erik Fortune
3
- * SPDX-License-Identifier: MIT
4
- */
5
-
6
- import { Result, fail, succeed } from '@fgv/ts-utils';
7
- import { JsonObject } from '@fgv/ts-json-base';
8
- import { IJsonEditorOptions, JsonEditor } from '@fgv/ts-json';
9
- import { IEdge, IMemoryEnvelope, IMemoryRecord, IProvenance } from './envelope';
10
- import { MemoryId, Tag } from './ids';
11
-
12
- /**
13
- * The granularity at which the store deduplicates an incoming write against the
14
- * existing vault, declared per kind via {@link IWritePolicy.dedupScope}.
15
- *
16
- * - `'content'`: scope-wide cross-id content dedup. An identical
17
- * `{ kind, body, links }` triple ANYWHERE in the scope — even under a
18
- * different id — is a no-op that returns the existing record. The knowledge
19
- * kind family uses this.
20
- * - `'entity'`: same-id content dedup only. An identical re-put of the SAME
21
- * entity is a no-op, but two DISTINCT entities with identical content never
22
- * collapse. The experience (memory) kind families use this so that, e.g.,
23
- * two turns whose summaries happen to be byte-identical both persist.
24
- * @public
25
- */
26
- export type DedupScope = 'content' | 'entity';
27
-
28
- /**
29
- * The default {@link DedupScope} applied when a policy does not declare one.
30
- * Entity-scoped dedup is the safe default — it never silently collapses two
31
- * distinct entities with coincidentally-identical content.
32
- * @public
33
- */
34
- export const DEFAULT_DEDUP_SCOPE: DedupScope = 'entity';
35
-
36
- /**
37
- * The admission decision returned by {@link IWritePolicy.admit}.
38
- * @public
39
- */
40
- export type AdmissionDecision =
41
- | { readonly decision: 'accept' }
42
- | { readonly decision: 'reject'; readonly reason: string }
43
- | {
44
- readonly decision: 'cull-oldest';
45
- /** {@link MemoryId}s to evict before writing the incoming record. */
46
- readonly evict: ReadonlyArray<MemoryId>;
47
- };
48
-
49
- /**
50
- * A per-kind write policy, injected at store construction. Invoked AFTER
51
- * content-hash dedup (dedup is always pre-policy). The policy decides
52
- * admission and, on update, applies a JSON Merge Patch (RFC-7386) restricted
53
- * to the declared mutable fields.
54
- * @public
55
- */
56
- export interface IWritePolicy {
57
- /**
58
- * Names of the fields a merge-patch update may touch. Fields outside this
59
- * list are immutable; a change to one constitutes a new entity (its
60
- * `contentHash` differs). Used by {@link IWritePolicy.applyUpdate} to
61
- * restrict which fields the patch applies.
62
- */
63
- readonly mutableFields: ReadonlyArray<string>;
64
-
65
- /**
66
- * The granularity at which the store deduplicates an incoming write for this
67
- * kind. Optional; when absent the store applies {@link DEFAULT_DEDUP_SCOPE}
68
- * (`'entity'`). See {@link DedupScope}.
69
- */
70
- readonly dedupScope?: DedupScope;
71
-
72
- /**
73
- * Determine whether the incoming record is admitted.
74
- * @param incoming - The record about to be written.
75
- * @param existing - The admission cohort the kind's cap applies to: the
76
- * records in the same scope of the same kind, EXCLUDING the record at
77
- * `incoming`'s target id. Empty on a first write into an empty cohort.
78
- * Excluding the target id makes the post-write count uniform
79
- * (`existing.length + 1`) across first-writes and same-id updates. A
80
- * last-write-wins policy that has no cap ignores this argument.
81
- * @returns A {@link AdmissionDecision}.
82
- */
83
- admit(
84
- incoming: IMemoryRecord<unknown>,
85
- existing: ReadonlyArray<IMemoryRecord<unknown>>
86
- ): Result<AdmissionDecision>;
87
-
88
- /**
89
- * Apply a JSON Merge Patch (RFC-7386) to the mutable fields of an existing
90
- * record. Called when admission is `accept` AND a record with the same
91
- * `entityId` already exists (an update, not a first write).
92
- *
93
- * @remarks
94
- * **RFC-7386 semantics are the contract here, not an artifact of the shipped
95
- * policies' merge configuration.** Implementations are expected to honor them,
96
- * and consumers may rely on them:
97
- *
98
- * - **Objects merge per key.** A supplied key overwrites; an omitted key is
99
- * PRESERVED, not dropped. Patching one key of a nested object (e.g. one
100
- * field of `provenance`) leaves its siblings intact.
101
- * - **An explicit `null` on a sub-key clears exactly that sub-key.** This is
102
- * the sanctioned way to remove a single key from a nested object.
103
- * - **Arrays replace wholesale** — `tags` / `links` are never element-merged.
104
- * - **A whole-block `null` that would delete a REQUIRED field is rejected
105
- * loudly** (`Result.fail`), never silently accepted. `body` / `tags` /
106
- * `links` / `provenance` are required; `embeddingRef` is optional and a
107
- * `null` restores it to absent (NOT to `null`), so it stays hash-stable.
108
- *
109
- * Which fields these guarantees cover is **policy-dependent**: they apply to
110
- * the fields the policy declares in {@link IWritePolicy.mutableFields}, and a
111
- * field outside that list is inert — its patch key is dropped before the merge,
112
- * so a `null` on it neither clears the value nor raises an error.
113
- *
114
- * @param existing - The current persisted record.
115
- * @param patch - A partial JSON object in Merge Patch format. `null`
116
- * deletes the corresponding key; arrays replace wholesale; nested objects
117
- * deep-merge. Only keys in {@link IWritePolicy.mutableFields} are applied.
118
- * @returns The updated record (envelope + body).
119
- */
120
- applyUpdate(
121
- existing: IMemoryRecord<unknown>,
122
- patch: Record<string, unknown>
123
- ): Result<IMemoryRecord<unknown>>;
124
- }
125
-
126
- /**
127
- * RFC-7386-compliant merge options for {@link IWritePolicy.applyUpdate}.
128
- *
129
- * @remarks
130
- * Phase-A verification (design-lock §5.1) confirmed `@fgv/ts-json`'s
131
- * `JsonEditor` diverges from RFC-7386 under its defaults on two axes —
132
- * `nullAsDelete` defaults `false` and `arrayMergeBehavior` defaults
133
- * `'append'`. Both are corrected here via the existing option surface, so the
134
- * primitive is composed (not extended, not hand-rolled).
135
- */
136
- const MERGE_PATCH_OPTIONS: Partial<IJsonEditorOptions> = {
137
- merge: {
138
- nullAsDelete: true,
139
- arrayMergeBehavior: 'replace'
140
- }
141
- };
142
-
143
- /**
144
- * Last-write-wins write policy for the knowledge kind family. Admission
145
- * always accepts (no cap, no cull); updates apply an RFC-7386 merge patch
146
- * restricted to the knowledge mutable surface.
147
- *
148
- * @remarks
149
- * **Merge-surface pin (resolves design-lock §5.3's body-vs-envelope muddle).**
150
- * The declared `mutableFields` span both axes of a record: `body` is the
151
- * per-kind body, while `tags` / `links` / `provenance` / `embeddingRef` live
152
- * on the {@link IMemoryEnvelope}. `applyUpdate` projects exactly those fields
153
- * — each read from its canonical location — into a single record-level JSON
154
- * view, runs the merge over that view, then rebuilds a coherent record. The
155
- * identity and transaction-time envelope fields (`id`, `entityId`, `kind`,
156
- * `created`, `updated`, `seq`, `contentHash`) are NOT mutable and are
157
- * preserved verbatim; the store stamps `updated` / `seq` / `contentHash` on write.
158
- *
159
- * Because the surface is pinned rather than caller-supplied, the RFC-7386
160
- * guarantees documented on {@link IWritePolicy.applyUpdate} apply to every field
161
- * listed above — in particular to `provenance`, whose keys merge individually, a
162
- * `null` on any one of which clears that key alone, and a `null` on the whole
163
- * block of which is rejected loudly (it is a required field). Consumers may
164
- * depend on this; it is covered by tests.
165
- * @public
166
- */
167
- export class KnowledgeLwwPolicy implements IWritePolicy {
168
- /**
169
- * The knowledge mutable surface: the body plus the envelope metadata a
170
- * consumer may revise without minting a new entity. Pinned, not
171
- * caller-supplied — see the class remarks for what that guarantees.
172
- */
173
- public readonly mutableFields: ReadonlyArray<string> = [
174
- 'body',
175
- 'tags',
176
- 'links',
177
- 'provenance',
178
- 'embeddingRef'
179
- ];
180
-
181
- /**
182
- * Knowledge dedups scope-wide: an identical `{ kind, body, links }` triple
183
- * anywhere in the `knowledge` scope — even under a different `docId` — is a
184
- * no-op. Declared explicitly so the B1 content-dedup behavior (and its tests)
185
- * are unchanged by the {@link DedupScope} amendment.
186
- */
187
- public readonly dedupScope: DedupScope = 'content';
188
-
189
- /** Deep-clones the mutable view without RFC-7386 null-deletion semantics. */
190
- private readonly _cloneEditor: JsonEditor;
191
- /** Applies the RFC-7386 merge patch. */
192
- private readonly _mergeEditor: JsonEditor;
193
-
194
- private constructor(cloneEditor: JsonEditor, mergeEditor: JsonEditor) {
195
- this._cloneEditor = cloneEditor;
196
- this._mergeEditor = mergeEditor;
197
- }
198
-
199
- /**
200
- * Family-convention factory. Constructs the shared `JsonEditor` instances
201
- * (one for cloning, one for the RFC-7386 merge), both with the template /
202
- * conditional / multivalue / reference rules disabled (empty rules array).
203
- */
204
- public static create(): Result<KnowledgeLwwPolicy> {
205
- return JsonEditor.create({}, []).onSuccess((cloneEditor) =>
206
- JsonEditor.create(MERGE_PATCH_OPTIONS, []).onSuccess((mergeEditor) =>
207
- succeed(new KnowledgeLwwPolicy(cloneEditor, mergeEditor))
208
- )
209
- );
210
- }
211
-
212
- /** {@inheritDoc IWritePolicy.admit} */
213
- public admit(
214
- __incoming: IMemoryRecord<unknown>,
215
- __existing: ReadonlyArray<IMemoryRecord<unknown>>
216
- ): Result<AdmissionDecision> {
217
- // Last-write-wins: always accept. No cap, no cull.
218
- return succeed({ decision: 'accept' });
219
- }
220
-
221
- /** {@inheritDoc IWritePolicy.applyUpdate} */
222
- public applyUpdate(
223
- existing: IMemoryRecord<unknown>,
224
- patch: Record<string, unknown>
225
- ): Result<IMemoryRecord<unknown>> {
226
- // Project the mutable fields into a single record-level view, each sourced
227
- // from its canonical location. `embeddingRef` is omitted when `undefined`
228
- // (the editor rejects undefined property values).
229
- const view: Record<string, unknown> = {
230
- body: existing.body,
231
- tags: existing.envelope.tags,
232
- links: existing.envelope.links,
233
- provenance: existing.envelope.provenance
234
- };
235
- if (existing.envelope.embeddingRef !== undefined) {
236
- view.embeddingRef = existing.envelope.embeddingRef;
237
- }
238
-
239
- // Restrict the incoming patch to the declared mutable fields; out-of-scope
240
- // keys are never applied (the store enforces the same constraint).
241
- const scopedPatch: Record<string, unknown> = {};
242
- for (const field of this.mutableFields) {
243
- if (field in patch) {
244
- scopedPatch[field] = patch[field];
245
- }
246
- }
247
-
248
- // Clone the view (no null-deletion), then apply the RFC-7386 merge patch
249
- // onto the clone so the persisted record is never mutated in place.
250
- return this._cloneEditor
251
- .mergeObjectInPlace({}, view as JsonObject)
252
- .onSuccess((clone) => this._mergeEditor.mergeObjectInPlace(clone, scopedPatch as JsonObject))
253
- .onSuccess((merged) => this._rebuild(existing, merged));
254
- }
255
-
256
- /**
257
- * Reassemble a record from the merged mutable view. `body` / `tags` /
258
- * `links` / `provenance` are required and may not be deleted by a patch.
259
- * `embeddingRef` is optional: when it is absent from the merged view —
260
- * because the existing record never carried it OR a `null` patch deleted it
261
- * (RFC-7386) — it is restored as `undefined` (absent), NOT `null`. This keeps
262
- * an originally-absent `embeddingRef` from silently flipping to `null` on an
263
- * unrelated update, so the field round-trips hash-stably through the store's
264
- * content-hash recomputation.
265
- */
266
- private _rebuild(existing: IMemoryRecord<unknown>, merged: JsonObject): Result<IMemoryRecord<unknown>> {
267
- const required: ReadonlyArray<string> = ['body', 'tags', 'links', 'provenance'];
268
- const missing: ReadonlyArray<string> = required.filter((field) => !(field in merged));
269
- if (missing.length > 0) {
270
- return fail(`knowledge LWW: merge patch may not delete required field(s): ${missing.join(', ')}`);
271
- }
272
-
273
- // The merged values are JSON projections of the already-validated typed
274
- // record; restore the domain types. (The types packlet cannot import the
275
- // converters packlet without a cycle, so these are structural restorations
276
- // of fields the merge preserved, not fresh untrusted input.)
277
- const envelope: IMemoryEnvelope = {
278
- ...existing.envelope,
279
- tags: merged.tags as unknown as ReadonlyArray<Tag>,
280
- links: merged.links as unknown as ReadonlyArray<IEdge>,
281
- provenance: merged.provenance as unknown as IProvenance,
282
- embeddingRef: 'embeddingRef' in merged ? (merged.embeddingRef as string | null) : undefined
283
- };
284
- return succeed({ envelope, body: merged.body });
285
- }
286
- }
287
-
288
- /**
289
- * Parameters for {@link MemoryCapCullPolicy.create}.
290
- * @public
291
- */
292
- export interface IMemoryCapCullPolicyParams {
293
- /**
294
- * Maximum number of records the policy admits before culling the oldest.
295
- * Counted over the `existing` cohort passed to {@link IWritePolicy.admit}.
296
- * Absent = no cap (admission always accepts).
297
- */
298
- readonly maxRecords?: number;
299
- /**
300
- * The fields a merge-patch update may touch (drawn from the record-level
301
- * mutable vocabulary: `body` / `tags` / `links` / `provenance` /
302
- * `embeddingRef`). Fields outside this list are immutable.
303
- *
304
- * @remarks
305
- * This list — unlike {@link KnowledgeLwwPolicy}'s pinned surface — is
306
- * caller-supplied, so it is what decides which fields get the RFC-7386
307
- * guarantees documented on {@link IWritePolicy.applyUpdate}. Declare
308
- * `provenance` here to get per-key provenance merging and `null` sub-key
309
- * clearing; omit it and every provenance patch key is inert (dropped before
310
- * the merge, so the existing value is preserved verbatim and even a
311
- * whole-block `null` is a silent no-op rather than an error).
312
- */
313
- readonly mutableFields: ReadonlyArray<string>;
314
- }
315
-
316
- /**
317
- * The record-level fields a {@link MemoryCapCullPolicy} merge-patch may project,
318
- * mapped to their canonical location on a record. Mirrors the store's
319
- * mutable-field accessor vocabulary; a declared mutable field outside this set
320
- * is inert (the store cannot project it either).
321
- */
322
- const CAP_CULL_FIELD_READERS: ReadonlyMap<string, (record: IMemoryRecord<unknown>) => unknown> = new Map<
323
- string,
324
- (record: IMemoryRecord<unknown>) => unknown
325
- >([
326
- ['body', (r) => r.body],
327
- ['tags', (r) => r.envelope.tags],
328
- ['links', (r) => r.envelope.links],
329
- ['provenance', (r) => r.envelope.provenance],
330
- ['embeddingRef', (r) => r.envelope.embeddingRef]
331
- ]);
332
-
333
- /** The record-level mutable fields that may never be deleted by a merge patch. */
334
- const CAP_CULL_REQUIRED_FIELDS: ReadonlySet<string> = new Set<string>([
335
- 'body',
336
- 'tags',
337
- 'links',
338
- 'provenance'
339
- ]);
340
-
341
- /**
342
- * Bounded-ring write policy for the experience (memory) kind families.
343
- * Admission accepts until `maxRecords` is reached, then evicts the oldest
344
- * record(s) by `created` ascending (design-lock §5.3); updates apply the same
345
- * RFC-7386 merge patch as {@link KnowledgeLwwPolicy}, restricted to the declared
346
- * {@link IMemoryCapCullPolicyParams.mutableFields | mutableFields}.
347
- *
348
- * @remarks
349
- * - **Dedup scope.** Declares `dedupScope: 'entity'` — two distinct memory
350
- * entities (e.g. `turn-5` / `turn-9`) with identical `{ kind, body, links }`
351
- * never collapse; only an identical re-put of the SAME entity is a no-op.
352
- * - **Eviction boundary.** `admit` only DECIDES (returns the `MemoryId`s to
353
- * evict); the store executes the file deletions and index patches. The
354
- * `existing` cohort the cap counts against is whatever the store supplies to
355
- * `admit`.
356
- * @public
357
- */
358
- export class MemoryCapCullPolicy implements IWritePolicy {
359
- /** {@inheritDoc IWritePolicy.mutableFields} */
360
- public readonly mutableFields: ReadonlyArray<string>;
361
-
362
- /** Experience kinds dedup per-entity (see the class remarks). */
363
- public readonly dedupScope: DedupScope = 'entity';
364
-
365
- /** The admission cap; `undefined` = no cap. */
366
- private readonly _maxRecords: number | undefined;
367
- /** Deep-clones the mutable view without RFC-7386 null-deletion semantics. */
368
- private readonly _cloneEditor: JsonEditor;
369
- /** Applies the RFC-7386 merge patch. */
370
- private readonly _mergeEditor: JsonEditor;
371
-
372
- private constructor(params: IMemoryCapCullPolicyParams, cloneEditor: JsonEditor, mergeEditor: JsonEditor) {
373
- this.mutableFields = params.mutableFields;
374
- this._maxRecords = params.maxRecords;
375
- this._cloneEditor = cloneEditor;
376
- this._mergeEditor = mergeEditor;
377
- }
378
-
379
- /**
380
- * Family-convention factory. Constructs the shared `JsonEditor` instances
381
- * (one for cloning, one for the RFC-7386 merge) with the same merge config as
382
- * {@link KnowledgeLwwPolicy} (`nullAsDelete` true, `arrayMergeBehavior`
383
- * `'replace'`, rules disabled).
384
- */
385
- public static create(params: IMemoryCapCullPolicyParams): Result<MemoryCapCullPolicy> {
386
- return JsonEditor.create({}, []).onSuccess((cloneEditor) =>
387
- JsonEditor.create(MERGE_PATCH_OPTIONS, []).onSuccess((mergeEditor) =>
388
- succeed(new MemoryCapCullPolicy(params, cloneEditor, mergeEditor))
389
- )
390
- );
391
- }
392
-
393
- /** {@inheritDoc IWritePolicy.admit} */
394
- public admit(
395
- __incoming: IMemoryRecord<unknown>,
396
- existing: ReadonlyArray<IMemoryRecord<unknown>>
397
- ): Result<AdmissionDecision> {
398
- if (this._maxRecords === undefined || existing.length < this._maxRecords) {
399
- return succeed({ decision: 'accept' });
400
- }
401
- // Cap reached: evict the oldest by `created` ascending so the post-write
402
- // count is exactly `maxRecords` (existing.length - maxRecords + 1 victims).
403
- const evict: ReadonlyArray<MemoryId> = [...existing]
404
- .sort((a, b) => a.envelope.created - b.envelope.created)
405
- .slice(0, existing.length - this._maxRecords + 1)
406
- .map((record) => record.envelope.id);
407
- return succeed({ decision: 'cull-oldest', evict });
408
- }
409
-
410
- /** {@inheritDoc IWritePolicy.applyUpdate} */
411
- public applyUpdate(
412
- existing: IMemoryRecord<unknown>,
413
- patch: Record<string, unknown>
414
- ): Result<IMemoryRecord<unknown>> {
415
- // Project the declared mutable fields (restricted to the known record-level
416
- // vocabulary) into a single record-level view from their canonical
417
- // locations. An `undefined` value is omitted (the editor rejects undefined
418
- // property values).
419
- const view: Record<string, unknown> = {};
420
- for (const field of this.mutableFields) {
421
- const reader: ((record: IMemoryRecord<unknown>) => unknown) | undefined =
422
- CAP_CULL_FIELD_READERS.get(field);
423
- if (reader !== undefined) {
424
- const value: unknown = reader(existing);
425
- if (value !== undefined) {
426
- view[field] = value;
427
- }
428
- }
429
- }
430
-
431
- // Restrict the incoming patch to the declared mutable fields.
432
- const scopedPatch: Record<string, unknown> = {};
433
- for (const field of this.mutableFields) {
434
- if (field in patch) {
435
- scopedPatch[field] = patch[field];
436
- }
437
- }
438
-
439
- // Clone the view (no null-deletion), then apply the RFC-7386 merge patch
440
- // onto the clone so the persisted record is never mutated in place.
441
- return this._cloneEditor
442
- .mergeObjectInPlace({}, view as JsonObject)
443
- .onSuccess((clone) => this._mergeEditor.mergeObjectInPlace(clone, scopedPatch as JsonObject))
444
- .onSuccess((merged) => this._rebuild(existing, merged));
445
- }
446
-
447
- /**
448
- * Reassemble a record from the merged mutable view. Only the declared mutable
449
- * fields are taken from the merge; undeclared fields are preserved verbatim
450
- * from `existing`. A `null` patch that deletes a *declared mutable* required
451
- * field (`body` / `tags` / `links` / `provenance`) is an error — a required
452
- * field that is NOT declared mutable simply falls through to its `existing.*`
453
- * value and is never at risk. `embeddingRef`, when mutable, is restored as
454
- * `undefined` (absent) if the merge dropped it — same hash-stable semantics as
455
- * {@link KnowledgeLwwPolicy}.
456
- */
457
- private _rebuild(existing: IMemoryRecord<unknown>, merged: JsonObject): Result<IMemoryRecord<unknown>> {
458
- const deleted: ReadonlyArray<string> = this.mutableFields.filter(
459
- (field) => CAP_CULL_REQUIRED_FIELDS.has(field) && !(field in merged)
460
- );
461
- if (deleted.length > 0) {
462
- return fail(`memory cap-cull: merge patch may not delete required field(s): ${deleted.join(', ')}`);
463
- }
464
-
465
- // The merged values are JSON projections of the already-validated typed
466
- // record; restore the domain types. (The types packlet cannot import the
467
- // converters packlet without a cycle, so these are structural restorations
468
- // of fields the merge preserved, not fresh untrusted input — mirrors
469
- // KnowledgeLwwPolicy._rebuild.)
470
- const embeddingRefMutable: boolean = this.mutableFields.includes('embeddingRef');
471
- const envelope: IMemoryEnvelope = {
472
- ...existing.envelope,
473
- tags: 'tags' in merged ? (merged.tags as unknown as ReadonlyArray<Tag>) : existing.envelope.tags,
474
- links: 'links' in merged ? (merged.links as unknown as ReadonlyArray<IEdge>) : existing.envelope.links,
475
- provenance:
476
- 'provenance' in merged ? (merged.provenance as unknown as IProvenance) : existing.envelope.provenance,
477
- embeddingRef: embeddingRefMutable
478
- ? 'embeddingRef' in merged
479
- ? (merged.embeddingRef as string | null)
480
- : undefined
481
- : existing.envelope.embeddingRef
482
- };
483
- return succeed({ envelope, body: 'body' in merged ? merged.body : existing.body });
484
- }
485
- }
486
-
487
- /**
488
- * Write policy for a versioned (temporal) kind family, implementing
489
- * invalidate-don't-delete. Admission always accepts — history is retained, never
490
- * culled — and updates apply the same RFC-7386 merge patch as
491
- * {@link KnowledgeLwwPolicy}, restricted to the temporal mutable surface.
492
- *
493
- * @remarks
494
- * The policy does NOT perform the version file writes or set `invalid_at` — that
495
- * is the store's versioned write branch, driven by the kind's
496
- * {@link ITemporalIdentityCodec}. The policy's role is limited to admission and
497
- * the merge that forms the **new version's** content from the **current**
498
- * version plus the incoming patch (the merge-patch-under-versioning contract).
499
- *
500
- * - **Dedup scope.** `'entity'` — an identical re-put of the current content is a
501
- * no-op (the store compares the incoming content hash against the current
502
- * version), so identical writes do not spawn redundant versions.
503
- * - **Mutable surface.** `body` + the envelope metadata a consumer may revise
504
- * (`tags` / `links` / `provenance` / `embeddingRef`). `temporal` is NOT mutable
505
- * here — `valid_at` / `invalid_at` are set by the store's versioned branch.
506
- * @public
507
- */
508
- export class TemporalVersionedPolicy implements IWritePolicy {
509
- /** The temporal mutable surface (mirrors {@link KnowledgeLwwPolicy}). */
510
- public readonly mutableFields: ReadonlyArray<string> = [
511
- 'body',
512
- 'tags',
513
- 'links',
514
- 'provenance',
515
- 'embeddingRef'
516
- ];
517
-
518
- /** Versioned kinds dedup per-entity against the current version (see the class remarks). */
519
- public readonly dedupScope: DedupScope = 'entity';
520
-
521
- /** Deep-clones the mutable view without RFC-7386 null-deletion semantics. */
522
- private readonly _cloneEditor: JsonEditor;
523
- /** Applies the RFC-7386 merge patch. */
524
- private readonly _mergeEditor: JsonEditor;
525
-
526
- private constructor(cloneEditor: JsonEditor, mergeEditor: JsonEditor) {
527
- this._cloneEditor = cloneEditor;
528
- this._mergeEditor = mergeEditor;
529
- }
530
-
531
- /**
532
- * Family-convention factory. Constructs the shared `JsonEditor` instances (one
533
- * for cloning, one for the RFC-7386 merge), rules disabled — the same merge
534
- * config as the shipped policies.
535
- */
536
- public static create(): Result<TemporalVersionedPolicy> {
537
- return JsonEditor.create({}, []).onSuccess((cloneEditor) =>
538
- JsonEditor.create(MERGE_PATCH_OPTIONS, []).onSuccess((mergeEditor) =>
539
- succeed(new TemporalVersionedPolicy(cloneEditor, mergeEditor))
540
- )
541
- );
542
- }
543
-
544
- /** {@inheritDoc IWritePolicy.admit} */
545
- public admit(
546
- __incoming: IMemoryRecord<unknown>,
547
- __existing: ReadonlyArray<IMemoryRecord<unknown>>
548
- ): Result<AdmissionDecision> {
549
- // Invalidate-don't-delete: always accept. Superseded versions are retained
550
- // (invalidated), never culled.
551
- return succeed({ decision: 'accept' });
552
- }
553
-
554
- /** {@inheritDoc IWritePolicy.applyUpdate} */
555
- public applyUpdate(
556
- existing: IMemoryRecord<unknown>,
557
- patch: Record<string, unknown>
558
- ): Result<IMemoryRecord<unknown>> {
559
- // Project the mutable fields into a single record-level view, each sourced
560
- // from its canonical location. `embeddingRef` is omitted when `undefined`.
561
- const view: Record<string, unknown> = {
562
- body: existing.body,
563
- tags: existing.envelope.tags,
564
- links: existing.envelope.links,
565
- provenance: existing.envelope.provenance
566
- };
567
- if (existing.envelope.embeddingRef !== undefined) {
568
- view.embeddingRef = existing.envelope.embeddingRef;
569
- }
570
-
571
- // Restrict the incoming patch to the declared mutable fields.
572
- const scopedPatch: Record<string, unknown> = {};
573
- for (const field of this.mutableFields) {
574
- if (field in patch) {
575
- scopedPatch[field] = patch[field];
576
- }
577
- }
578
-
579
- // Clone the view (no null-deletion), then apply the RFC-7386 merge patch onto
580
- // the clone so the current version is never mutated in place.
581
- return this._cloneEditor
582
- .mergeObjectInPlace({}, view as JsonObject)
583
- .onSuccess((clone) => this._mergeEditor.mergeObjectInPlace(clone, scopedPatch as JsonObject))
584
- .onSuccess((merged) => this._rebuild(existing, merged));
585
- }
586
-
587
- /**
588
- * Reassemble a record from the merged mutable view. `body` / `tags` / `links` /
589
- * `provenance` are required and may not be deleted by a patch; `embeddingRef`,
590
- * when dropped by the merge, is restored as `undefined` (absent) — the same
591
- * hash-stable semantics as {@link KnowledgeLwwPolicy}.
592
- */
593
- private _rebuild(existing: IMemoryRecord<unknown>, merged: JsonObject): Result<IMemoryRecord<unknown>> {
594
- const required: ReadonlyArray<string> = ['body', 'tags', 'links', 'provenance'];
595
- const missing: ReadonlyArray<string> = required.filter((field) => !(field in merged));
596
- if (missing.length > 0) {
597
- return fail(`temporal versioned: merge patch may not delete required field(s): ${missing.join(', ')}`);
598
- }
599
-
600
- // The merged values are JSON projections of the already-validated typed
601
- // record; restore the domain types (structural restorations, not fresh
602
- // untrusted input — mirrors KnowledgeLwwPolicy._rebuild).
603
- const envelope: IMemoryEnvelope = {
604
- ...existing.envelope,
605
- tags: merged.tags as unknown as ReadonlyArray<Tag>,
606
- links: merged.links as unknown as ReadonlyArray<IEdge>,
607
- provenance: merged.provenance as unknown as IProvenance,
608
- embeddingRef: 'embeddingRef' in merged ? (merged.embeddingRef as string | null) : undefined
609
- };
610
- return succeed({ envelope, body: merged.body });
611
- }
612
- }
@@ -1,55 +0,0 @@
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 { IEmbeddedFragment, IFragmentLocator } from './vectorIndex';
8
-
9
- /**
10
- * Converter for an {@link IFragmentLocator}. Validates the two offsets only — the
11
- * span's *meaning* (character / byte / token unit) is the consumer's, and the index
12
- * never interprets it. Note the span is advisory: see {@link IFragmentLocator}.
13
- * @public
14
- */
15
- export const fragmentLocatorConverter: Converter<IFragmentLocator> = Converters.object<IFragmentLocator>({
16
- start: Converters.number,
17
- end: Converters.number
18
- });
19
-
20
- /** Validates an already-constructed `Float32Array` embedding vector in place. */
21
- const embeddingVectorConverter: Converter<Float32Array> = Converters.generic<Float32Array>(
22
- (from: unknown): Result<Float32Array> =>
23
- from instanceof Float32Array ? succeed(from) : fail('embedded fragment: vector must be a Float32Array')
24
- );
25
-
26
- /** The field-shape half of {@link embeddedFragmentConverter}; the invariant is layered on top. */
27
- const embeddedFragmentFieldsConverter: Converter<IEmbeddedFragment> = Converters.object<IEmbeddedFragment>(
28
- {
29
- locator: fragmentLocatorConverter.optional(),
30
- fragmentId: Converters.string.optional(),
31
- vector: embeddingVectorConverter
32
- },
33
- { optionalFields: ['locator', 'fragmentId'] }
34
- );
35
-
36
- /**
37
- * Converter for an {@link IEmbeddedFragment}, and the boundary that enforces the
38
- * fragment-identifiability invariant: **at least one of `locator` / `fragmentId`
39
- * must be present.**
40
- *
41
- * @remarks
42
- * The invariant lives here rather than in the type. A conditional-required union
43
- * (`{ locator; fragmentId? } | { locator?; fragmentId }`) was considered and
44
- * declined: it costs at every construction site and buys nothing at the read site,
45
- * where each field reads as `… | undefined` either way. What it must not cost is
46
- * identifiability — a fragment carrying neither identity cannot be resolved back to
47
- * anything by a consumer holding the query hit, so it is rejected here.
48
- * @public
49
- */
50
- export const embeddedFragmentConverter: Converter<IEmbeddedFragment> =
51
- embeddedFragmentFieldsConverter.withConstraint((fragment: IEmbeddedFragment) =>
52
- fragment.locator === undefined && fragment.fragmentId === undefined
53
- ? fail('embedded fragment: at least one of `locator` or `fragmentId` is required')
54
- : succeed(fragment)
55
- );