lex-agentic-memory 0.1.0

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 (267) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +5 -0
  4. data/LICENSE +21 -0
  5. data/README.md +13 -0
  6. data/lex-agentic-memory.gemspec +30 -0
  7. data/lib/legion/extensions/agentic/memory/archaeology/client.rb +19 -0
  8. data/lib/legion/extensions/agentic/memory/archaeology/helpers/archaeology_engine.rb +184 -0
  9. data/lib/legion/extensions/agentic/memory/archaeology/helpers/artifact.rb +146 -0
  10. data/lib/legion/extensions/agentic/memory/archaeology/helpers/constants.rb +95 -0
  11. data/lib/legion/extensions/agentic/memory/archaeology/helpers/excavation_site.rb +97 -0
  12. data/lib/legion/extensions/agentic/memory/archaeology/runners/cognitive_archaeology.rb +86 -0
  13. data/lib/legion/extensions/agentic/memory/archaeology/version.rb +13 -0
  14. data/lib/legion/extensions/agentic/memory/archaeology.rb +22 -0
  15. data/lib/legion/extensions/agentic/memory/compression/client.rb +15 -0
  16. data/lib/legion/extensions/agentic/memory/compression/helpers/compression_engine.rb +145 -0
  17. data/lib/legion/extensions/agentic/memory/compression/helpers/constants.rb +44 -0
  18. data/lib/legion/extensions/agentic/memory/compression/helpers/information_chunk.rb +81 -0
  19. data/lib/legion/extensions/agentic/memory/compression/runners/cognitive_compression.rb +76 -0
  20. data/lib/legion/extensions/agentic/memory/compression/version.rb +13 -0
  21. data/lib/legion/extensions/agentic/memory/compression.rb +19 -0
  22. data/lib/legion/extensions/agentic/memory/echo/client.rb +19 -0
  23. data/lib/legion/extensions/agentic/memory/echo/helpers/constants.rb +57 -0
  24. data/lib/legion/extensions/agentic/memory/echo/helpers/echo.rb +95 -0
  25. data/lib/legion/extensions/agentic/memory/echo/helpers/echo_engine.rb +136 -0
  26. data/lib/legion/extensions/agentic/memory/echo/runners/cognitive_echo.rb +81 -0
  27. data/lib/legion/extensions/agentic/memory/echo/version.rb +13 -0
  28. data/lib/legion/extensions/agentic/memory/echo.rb +19 -0
  29. data/lib/legion/extensions/agentic/memory/echo_chamber/client.rb +19 -0
  30. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/chamber.rb +134 -0
  31. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/chamber_engine.rb +156 -0
  32. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/constants.rb +60 -0
  33. data/lib/legion/extensions/agentic/memory/echo_chamber/helpers/echo.rb +96 -0
  34. data/lib/legion/extensions/agentic/memory/echo_chamber/runners/cognitive_echo_chamber.rb +84 -0
  35. data/lib/legion/extensions/agentic/memory/echo_chamber/version.rb +13 -0
  36. data/lib/legion/extensions/agentic/memory/echo_chamber.rb +21 -0
  37. data/lib/legion/extensions/agentic/memory/episodic/actors/decay.rb +45 -0
  38. data/lib/legion/extensions/agentic/memory/episodic/client.rb +25 -0
  39. data/lib/legion/extensions/agentic/memory/episodic/helpers/constants.rb +36 -0
  40. data/lib/legion/extensions/agentic/memory/episodic/helpers/episode.rb +104 -0
  41. data/lib/legion/extensions/agentic/memory/episodic/helpers/episodic_binding.rb +57 -0
  42. data/lib/legion/extensions/agentic/memory/episodic/helpers/episodic_store.rb +141 -0
  43. data/lib/legion/extensions/agentic/memory/episodic/runners/episodic_buffer.rb +108 -0
  44. data/lib/legion/extensions/agentic/memory/episodic/version.rb +13 -0
  45. data/lib/legion/extensions/agentic/memory/episodic.rb +20 -0
  46. data/lib/legion/extensions/agentic/memory/hologram/client.rb +23 -0
  47. data/lib/legion/extensions/agentic/memory/hologram/helpers/constants.rb +67 -0
  48. data/lib/legion/extensions/agentic/memory/hologram/helpers/hologram.rb +118 -0
  49. data/lib/legion/extensions/agentic/memory/hologram/helpers/hologram_engine.rb +117 -0
  50. data/lib/legion/extensions/agentic/memory/hologram/helpers/holographic_fragment.rb +70 -0
  51. data/lib/legion/extensions/agentic/memory/hologram/runners/cognitive_hologram.rb +110 -0
  52. data/lib/legion/extensions/agentic/memory/hologram/version.rb +13 -0
  53. data/lib/legion/extensions/agentic/memory/hologram.rb +22 -0
  54. data/lib/legion/extensions/agentic/memory/immune_memory/client.rb +19 -0
  55. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/constants.rb +84 -0
  56. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/encounter.rb +54 -0
  57. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/immune_memory_engine.rb +171 -0
  58. data/lib/legion/extensions/agentic/memory/immune_memory/helpers/memory_cell.rb +97 -0
  59. data/lib/legion/extensions/agentic/memory/immune_memory/runners/cognitive_immune_memory.rb +76 -0
  60. data/lib/legion/extensions/agentic/memory/immune_memory/version.rb +13 -0
  61. data/lib/legion/extensions/agentic/memory/immune_memory.rb +20 -0
  62. data/lib/legion/extensions/agentic/memory/nostalgia/client.rb +31 -0
  63. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/constants.rb +68 -0
  64. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_engine.rb +148 -0
  65. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_event.rb +44 -0
  66. data/lib/legion/extensions/agentic/memory/nostalgia/helpers/nostalgic_memory.rb +97 -0
  67. data/lib/legion/extensions/agentic/memory/nostalgia/runners/analysis.rb +61 -0
  68. data/lib/legion/extensions/agentic/memory/nostalgia/runners/recall.rb +60 -0
  69. data/lib/legion/extensions/agentic/memory/nostalgia/version.rb +13 -0
  70. data/lib/legion/extensions/agentic/memory/nostalgia.rb +21 -0
  71. data/lib/legion/extensions/agentic/memory/offloading/client.rb +29 -0
  72. data/lib/legion/extensions/agentic/memory/offloading/helpers/constants.rb +55 -0
  73. data/lib/legion/extensions/agentic/memory/offloading/helpers/external_store.rb +82 -0
  74. data/lib/legion/extensions/agentic/memory/offloading/helpers/offloaded_item.rb +65 -0
  75. data/lib/legion/extensions/agentic/memory/offloading/helpers/offloading_engine.rb +138 -0
  76. data/lib/legion/extensions/agentic/memory/offloading/runners/cognitive_offloading.rb +102 -0
  77. data/lib/legion/extensions/agentic/memory/offloading/version.rb +13 -0
  78. data/lib/legion/extensions/agentic/memory/offloading.rb +20 -0
  79. data/lib/legion/extensions/agentic/memory/paleontology/client.rb +19 -0
  80. data/lib/legion/extensions/agentic/memory/paleontology/helpers/constants.rb +69 -0
  81. data/lib/legion/extensions/agentic/memory/paleontology/helpers/excavation.rb +62 -0
  82. data/lib/legion/extensions/agentic/memory/paleontology/helpers/fossil.rb +125 -0
  83. data/lib/legion/extensions/agentic/memory/paleontology/helpers/paleontology_engine.rb +170 -0
  84. data/lib/legion/extensions/agentic/memory/paleontology/runners/cognitive_paleontology.rb +93 -0
  85. data/lib/legion/extensions/agentic/memory/paleontology/version.rb +13 -0
  86. data/lib/legion/extensions/agentic/memory/paleontology.rb +22 -0
  87. data/lib/legion/extensions/agentic/memory/palimpsest/client.rb +19 -0
  88. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/belief_layer.rb +81 -0
  89. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/constants.rb +56 -0
  90. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest.rb +124 -0
  91. data/lib/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest_engine.rb +117 -0
  92. data/lib/legion/extensions/agentic/memory/palimpsest/runners/cognitive_palimpsest.rb +105 -0
  93. data/lib/legion/extensions/agentic/memory/palimpsest/version.rb +13 -0
  94. data/lib/legion/extensions/agentic/memory/palimpsest.rb +21 -0
  95. data/lib/legion/extensions/agentic/memory/reserve/client.rb +19 -0
  96. data/lib/legion/extensions/agentic/memory/reserve/helpers/constants.rb +58 -0
  97. data/lib/legion/extensions/agentic/memory/reserve/helpers/pathway.rb +112 -0
  98. data/lib/legion/extensions/agentic/memory/reserve/helpers/reserve_engine.rb +163 -0
  99. data/lib/legion/extensions/agentic/memory/reserve/runners/cognitive_reserve.rb +92 -0
  100. data/lib/legion/extensions/agentic/memory/reserve/version.rb +13 -0
  101. data/lib/legion/extensions/agentic/memory/reserve.rb +19 -0
  102. data/lib/legion/extensions/agentic/memory/semantic/actors/decay.rb +45 -0
  103. data/lib/legion/extensions/agentic/memory/semantic/client.rb +28 -0
  104. data/lib/legion/extensions/agentic/memory/semantic/helpers/concept.rb +101 -0
  105. data/lib/legion/extensions/agentic/memory/semantic/helpers/constants.rb +51 -0
  106. data/lib/legion/extensions/agentic/memory/semantic/helpers/knowledge_store.rb +145 -0
  107. data/lib/legion/extensions/agentic/memory/semantic/runners/semantic_memory.rb +94 -0
  108. data/lib/legion/extensions/agentic/memory/semantic/version.rb +13 -0
  109. data/lib/legion/extensions/agentic/memory/semantic.rb +20 -0
  110. data/lib/legion/extensions/agentic/memory/semantic_priming/client.rb +19 -0
  111. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/connection.rb +77 -0
  112. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/constants.rb +68 -0
  113. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/priming_network.rb +206 -0
  114. data/lib/legion/extensions/agentic/memory/semantic_priming/helpers/semantic_node.rb +81 -0
  115. data/lib/legion/extensions/agentic/memory/semantic_priming/runners/semantic_priming.rb +120 -0
  116. data/lib/legion/extensions/agentic/memory/semantic_priming/version.rb +13 -0
  117. data/lib/legion/extensions/agentic/memory/semantic_priming.rb +20 -0
  118. data/lib/legion/extensions/agentic/memory/semantic_satiation/client.rb +28 -0
  119. data/lib/legion/extensions/agentic/memory/semantic_satiation/helpers/concept.rb +82 -0
  120. data/lib/legion/extensions/agentic/memory/semantic_satiation/helpers/constants.rb +37 -0
  121. data/lib/legion/extensions/agentic/memory/semantic_satiation/helpers/satiation_engine.rb +103 -0
  122. data/lib/legion/extensions/agentic/memory/semantic_satiation/runners/semantic_satiation.rb +92 -0
  123. data/lib/legion/extensions/agentic/memory/semantic_satiation/version.rb +13 -0
  124. data/lib/legion/extensions/agentic/memory/semantic_satiation.rb +19 -0
  125. data/lib/legion/extensions/agentic/memory/source_monitoring/actors/decay.rb +31 -0
  126. data/lib/legion/extensions/agentic/memory/source_monitoring/client.rb +28 -0
  127. data/lib/legion/extensions/agentic/memory/source_monitoring/helpers/constants.rb +66 -0
  128. data/lib/legion/extensions/agentic/memory/source_monitoring/helpers/source_record.rb +94 -0
  129. data/lib/legion/extensions/agentic/memory/source_monitoring/helpers/source_tracker.rb +119 -0
  130. data/lib/legion/extensions/agentic/memory/source_monitoring/runners/source_monitoring.rb +95 -0
  131. data/lib/legion/extensions/agentic/memory/source_monitoring/version.rb +13 -0
  132. data/lib/legion/extensions/agentic/memory/source_monitoring.rb +19 -0
  133. data/lib/legion/extensions/agentic/memory/trace/actors/decay.rb +45 -0
  134. data/lib/legion/extensions/agentic/memory/trace/actors/tier_migration.rb +45 -0
  135. data/lib/legion/extensions/agentic/memory/trace/batch_decay.rb +44 -0
  136. data/lib/legion/extensions/agentic/memory/trace/client.rb +32 -0
  137. data/lib/legion/extensions/agentic/memory/trace/helpers/cache_store.rb +167 -0
  138. data/lib/legion/extensions/agentic/memory/trace/helpers/decay.rb +68 -0
  139. data/lib/legion/extensions/agentic/memory/trace/helpers/error_tracer.rb +94 -0
  140. data/lib/legion/extensions/agentic/memory/trace/helpers/store.rb +260 -0
  141. data/lib/legion/extensions/agentic/memory/trace/helpers/trace.rb +106 -0
  142. data/lib/legion/extensions/agentic/memory/trace/local_migrations/20260316000001_create_memory_traces.rb +31 -0
  143. data/lib/legion/extensions/agentic/memory/trace/local_migrations/20260316000002_create_memory_associations.rb +13 -0
  144. data/lib/legion/extensions/agentic/memory/trace/persistent_store.rb +94 -0
  145. data/lib/legion/extensions/agentic/memory/trace/quota.rb +55 -0
  146. data/lib/legion/extensions/agentic/memory/trace/runners/consolidation.rb +121 -0
  147. data/lib/legion/extensions/agentic/memory/trace/runners/traces.rb +105 -0
  148. data/lib/legion/extensions/agentic/memory/trace/version.rb +13 -0
  149. data/lib/legion/extensions/agentic/memory/trace.rb +54 -0
  150. data/lib/legion/extensions/agentic/memory/transfer/client.rb +28 -0
  151. data/lib/legion/extensions/agentic/memory/transfer/helpers/constants.rb +34 -0
  152. data/lib/legion/extensions/agentic/memory/transfer/helpers/domain_knowledge.rb +74 -0
  153. data/lib/legion/extensions/agentic/memory/transfer/helpers/transfer_engine.rb +188 -0
  154. data/lib/legion/extensions/agentic/memory/transfer/runners/transfer_learning.rb +77 -0
  155. data/lib/legion/extensions/agentic/memory/transfer/version.rb +13 -0
  156. data/lib/legion/extensions/agentic/memory/transfer.rb +19 -0
  157. data/lib/legion/extensions/agentic/memory/version.rb +11 -0
  158. data/lib/legion/extensions/agentic/memory.rb +35 -0
  159. data/spec/legion/extensions/agentic/memory/archaeology/client_spec.rb +38 -0
  160. data/spec/legion/extensions/agentic/memory/archaeology/cognitive_archaeology_spec.rb +7 -0
  161. data/spec/legion/extensions/agentic/memory/archaeology/helpers/archaeology_engine_spec.rb +165 -0
  162. data/spec/legion/extensions/agentic/memory/archaeology/helpers/artifact_spec.rb +196 -0
  163. data/spec/legion/extensions/agentic/memory/archaeology/helpers/constants_spec.rb +42 -0
  164. data/spec/legion/extensions/agentic/memory/archaeology/helpers/excavation_site_spec.rb +115 -0
  165. data/spec/legion/extensions/agentic/memory/archaeology/runners/cognitive_archaeology_spec.rb +96 -0
  166. data/spec/legion/extensions/agentic/memory/compression/helpers/compression_engine_spec.rb +138 -0
  167. data/spec/legion/extensions/agentic/memory/compression/helpers/constants_spec.rb +50 -0
  168. data/spec/legion/extensions/agentic/memory/compression/helpers/information_chunk_spec.rb +123 -0
  169. data/spec/legion/extensions/agentic/memory/compression/runners/cognitive_compression_spec.rb +80 -0
  170. data/spec/legion/extensions/agentic/memory/echo/client_spec.rb +20 -0
  171. data/spec/legion/extensions/agentic/memory/echo/cognitive_echo_spec.rb +7 -0
  172. data/spec/legion/extensions/agentic/memory/echo/helpers/echo_engine_spec.rb +152 -0
  173. data/spec/legion/extensions/agentic/memory/echo/helpers/echo_spec.rb +128 -0
  174. data/spec/legion/extensions/agentic/memory/echo/runners_spec.rb +80 -0
  175. data/spec/legion/extensions/agentic/memory/echo_chamber/client_spec.rb +84 -0
  176. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/chamber_engine_spec.rb +266 -0
  177. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/chamber_spec.rb +249 -0
  178. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/constants_spec.rb +130 -0
  179. data/spec/legion/extensions/agentic/memory/echo_chamber/helpers/echo_spec.rb +252 -0
  180. data/spec/legion/extensions/agentic/memory/echo_chamber/runners/cognitive_echo_chamber_spec.rb +185 -0
  181. data/spec/legion/extensions/agentic/memory/episodic/client_spec.rb +80 -0
  182. data/spec/legion/extensions/agentic/memory/episodic/episodic_buffer_spec.rb +15 -0
  183. data/spec/legion/extensions/agentic/memory/episodic/helpers/constants_spec.rb +86 -0
  184. data/spec/legion/extensions/agentic/memory/episodic/helpers/episode_spec.rb +188 -0
  185. data/spec/legion/extensions/agentic/memory/episodic/helpers/episodic_binding_spec.rb +114 -0
  186. data/spec/legion/extensions/agentic/memory/episodic/helpers/episodic_store_spec.rb +201 -0
  187. data/spec/legion/extensions/agentic/memory/episodic/runners/episodic_buffer_spec.rb +208 -0
  188. data/spec/legion/extensions/agentic/memory/hologram/client_spec.rb +89 -0
  189. data/spec/legion/extensions/agentic/memory/hologram/cognitive_hologram_spec.rb +39 -0
  190. data/spec/legion/extensions/agentic/memory/hologram/helpers/constants_spec.rb +143 -0
  191. data/spec/legion/extensions/agentic/memory/hologram/helpers/hologram_engine_spec.rb +268 -0
  192. data/spec/legion/extensions/agentic/memory/hologram/helpers/hologram_spec.rb +256 -0
  193. data/spec/legion/extensions/agentic/memory/hologram/helpers/holographic_fragment_spec.rb +213 -0
  194. data/spec/legion/extensions/agentic/memory/hologram/runners/cognitive_hologram_spec.rb +239 -0
  195. data/spec/legion/extensions/agentic/memory/immune_memory/client_spec.rb +18 -0
  196. data/spec/legion/extensions/agentic/memory/immune_memory/cognitive_immune_memory_spec.rb +7 -0
  197. data/spec/legion/extensions/agentic/memory/immune_memory/helpers/encounter_spec.rb +85 -0
  198. data/spec/legion/extensions/agentic/memory/immune_memory/helpers/immune_memory_engine_spec.rb +203 -0
  199. data/spec/legion/extensions/agentic/memory/immune_memory/helpers/memory_cell_spec.rb +190 -0
  200. data/spec/legion/extensions/agentic/memory/immune_memory/runners_spec.rb +83 -0
  201. data/spec/legion/extensions/agentic/memory/nostalgia/client_spec.rb +71 -0
  202. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/constants_spec.rb +65 -0
  203. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_engine_spec.rb +191 -0
  204. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/nostalgia_event_spec.rb +59 -0
  205. data/spec/legion/extensions/agentic/memory/nostalgia/helpers/nostalgic_memory_spec.rb +134 -0
  206. data/spec/legion/extensions/agentic/memory/nostalgia/runners/analysis_spec.rb +97 -0
  207. data/spec/legion/extensions/agentic/memory/nostalgia/runners/recall_spec.rb +81 -0
  208. data/spec/legion/extensions/agentic/memory/offloading/client_spec.rb +30 -0
  209. data/spec/legion/extensions/agentic/memory/offloading/helpers/constants_spec.rb +71 -0
  210. data/spec/legion/extensions/agentic/memory/offloading/helpers/external_store_spec.rb +158 -0
  211. data/spec/legion/extensions/agentic/memory/offloading/helpers/offloaded_item_spec.rb +123 -0
  212. data/spec/legion/extensions/agentic/memory/offloading/helpers/offloading_engine_spec.rb +250 -0
  213. data/spec/legion/extensions/agentic/memory/offloading/runners/cognitive_offloading_spec.rb +194 -0
  214. data/spec/legion/extensions/agentic/memory/paleontology/client_spec.rb +27 -0
  215. data/spec/legion/extensions/agentic/memory/paleontology/cognitive_paleontology_spec.rb +7 -0
  216. data/spec/legion/extensions/agentic/memory/paleontology/helpers/constants_spec.rb +33 -0
  217. data/spec/legion/extensions/agentic/memory/paleontology/helpers/excavation_spec.rb +71 -0
  218. data/spec/legion/extensions/agentic/memory/paleontology/helpers/fossil_spec.rb +134 -0
  219. data/spec/legion/extensions/agentic/memory/paleontology/helpers/paleontology_engine_spec.rb +148 -0
  220. data/spec/legion/extensions/agentic/memory/paleontology/runners/cognitive_paleontology_spec.rb +89 -0
  221. data/spec/legion/extensions/agentic/memory/palimpsest/client_spec.rb +58 -0
  222. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/belief_layer_spec.rb +150 -0
  223. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/constants_spec.rb +63 -0
  224. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest_engine_spec.rb +164 -0
  225. data/spec/legion/extensions/agentic/memory/palimpsest/helpers/palimpsest_spec.rb +172 -0
  226. data/spec/legion/extensions/agentic/memory/palimpsest/runners/cognitive_palimpsest_spec.rb +165 -0
  227. data/spec/legion/extensions/agentic/memory/reserve/client_spec.rb +37 -0
  228. data/spec/legion/extensions/agentic/memory/reserve/helpers/pathway_spec.rb +165 -0
  229. data/spec/legion/extensions/agentic/memory/reserve/helpers/reserve_engine_spec.rb +196 -0
  230. data/spec/legion/extensions/agentic/memory/reserve/runners/cognitive_reserve_spec.rb +96 -0
  231. data/spec/legion/extensions/agentic/memory/semantic/client_spec.rb +35 -0
  232. data/spec/legion/extensions/agentic/memory/semantic/helpers/concept_spec.rb +119 -0
  233. data/spec/legion/extensions/agentic/memory/semantic/helpers/knowledge_store_spec.rb +140 -0
  234. data/spec/legion/extensions/agentic/memory/semantic/runners/semantic_memory_spec.rb +103 -0
  235. data/spec/legion/extensions/agentic/memory/semantic_priming/helpers/connection_spec.rb +98 -0
  236. data/spec/legion/extensions/agentic/memory/semantic_priming/helpers/priming_network_spec.rb +208 -0
  237. data/spec/legion/extensions/agentic/memory/semantic_priming/helpers/semantic_node_spec.rb +125 -0
  238. data/spec/legion/extensions/agentic/memory/semantic_priming/semantic_priming_spec.rb +7 -0
  239. data/spec/legion/extensions/agentic/memory/semantic_satiation/client_spec.rb +19 -0
  240. data/spec/legion/extensions/agentic/memory/semantic_satiation/helpers/concept_spec.rb +167 -0
  241. data/spec/legion/extensions/agentic/memory/semantic_satiation/helpers/constants_spec.rb +81 -0
  242. data/spec/legion/extensions/agentic/memory/semantic_satiation/helpers/satiation_engine_spec.rb +189 -0
  243. data/spec/legion/extensions/agentic/memory/semantic_satiation/runners/semantic_satiation_spec.rb +176 -0
  244. data/spec/legion/extensions/agentic/memory/source_monitoring/client_spec.rb +25 -0
  245. data/spec/legion/extensions/agentic/memory/source_monitoring/helpers/source_record_spec.rb +152 -0
  246. data/spec/legion/extensions/agentic/memory/source_monitoring/helpers/source_tracker_spec.rb +139 -0
  247. data/spec/legion/extensions/agentic/memory/source_monitoring/runners/source_monitoring_spec.rb +102 -0
  248. data/spec/legion/extensions/agentic/memory/trace/actors/decay_spec.rb +62 -0
  249. data/spec/legion/extensions/agentic/memory/trace/actors/tier_migration_spec.rb +62 -0
  250. data/spec/legion/extensions/agentic/memory/trace/batch_decay_spec.rb +26 -0
  251. data/spec/legion/extensions/agentic/memory/trace/client_spec.rb +62 -0
  252. data/spec/legion/extensions/agentic/memory/trace/helpers/decay_spec.rb +134 -0
  253. data/spec/legion/extensions/agentic/memory/trace/helpers/store_spec.rb +217 -0
  254. data/spec/legion/extensions/agentic/memory/trace/helpers/trace_spec.rb +120 -0
  255. data/spec/legion/extensions/agentic/memory/trace/local_persistence_spec.rb +255 -0
  256. data/spec/legion/extensions/agentic/memory/trace/memory_spec.rb +11 -0
  257. data/spec/legion/extensions/agentic/memory/trace/persistent_store_spec.rb +50 -0
  258. data/spec/legion/extensions/agentic/memory/trace/quota_spec.rb +56 -0
  259. data/spec/legion/extensions/agentic/memory/trace/runners/consolidation_spec.rb +118 -0
  260. data/spec/legion/extensions/agentic/memory/trace/runners/traces_spec.rb +69 -0
  261. data/spec/legion/extensions/agentic/memory/transfer/client_spec.rb +18 -0
  262. data/spec/legion/extensions/agentic/memory/transfer/helpers/constants_spec.rb +41 -0
  263. data/spec/legion/extensions/agentic/memory/transfer/helpers/domain_knowledge_spec.rb +134 -0
  264. data/spec/legion/extensions/agentic/memory/transfer/helpers/transfer_engine_spec.rb +299 -0
  265. data/spec/legion/extensions/agentic/memory/transfer/runners/transfer_learning_spec.rb +144 -0
  266. data/spec/spec_helper.rb +46 -0
  267. metadata +351 -0
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Trace
8
+ module BatchDecay
9
+ DEFAULTS = {
10
+ rate: 0.999,
11
+ min_confidence: 0.01,
12
+ interval_hours: 1
13
+ }.freeze
14
+
15
+ class << self
16
+ def apply!(agent_id: nil, rate: DEFAULTS[:rate], min_confidence: DEFAULTS[:min_confidence])
17
+ return { updated: 0, evicted: 0 } unless db_ready?
18
+
19
+ conn = Legion::Data.connection
20
+ ds = conn[PersistentStore::TABLE]
21
+ ds = ds.where(agent_id: agent_id) if agent_id
22
+
23
+ updated = ds.where { confidence > min_confidence }
24
+ .update(confidence: Sequel[:confidence] * rate)
25
+
26
+ evicted = ds.where { confidence <= min_confidence }.delete
27
+
28
+ { updated: updated, evicted: evicted }
29
+ end
30
+
31
+ private
32
+
33
+ def db_ready?
34
+ defined?(Legion::Data) && Legion::Data.connection&.table_exists?(PersistentStore::TABLE)
35
+ rescue StandardError
36
+ false
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'legion/extensions/agentic/memory/trace/helpers/trace'
4
+ require 'legion/extensions/agentic/memory/trace/helpers/decay'
5
+ require 'legion/extensions/agentic/memory/trace/helpers/store'
6
+ require 'legion/extensions/agentic/memory/trace/runners/traces'
7
+ require 'legion/extensions/agentic/memory/trace/runners/consolidation'
8
+
9
+ module Legion
10
+ module Extensions
11
+ module Agentic
12
+ module Memory
13
+ module Trace
14
+ class Client
15
+ include Legion::Extensions::Agentic::Memory::Trace::Runners::Traces
16
+ include Legion::Extensions::Agentic::Memory::Trace::Runners::Consolidation
17
+
18
+ attr_reader :store
19
+
20
+ def initialize(store: nil, **)
21
+ @default_store = store || Legion::Extensions::Agentic::Memory::Trace.shared_store
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :default_store
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Trace
8
+ module Helpers
9
+ # Cache-backed store using Legion::Cache (Memcached/Redis).
10
+ # Keeps a local copy in memory, syncs to/from cache on load/flush.
11
+ # Call `flush` after a batch of writes, or it auto-flushes when dirty.
12
+ # Call `reload` to pull latest state from cache (e.g. after another process wrote).
13
+ class CacheStore
14
+ TRACES_KEY = 'legion:memory:traces'
15
+ ASSOC_KEY = 'legion:memory:associations'
16
+ TTL = 86_400 # 24 hours
17
+
18
+ attr_reader :traces, :associations
19
+
20
+ def initialize
21
+ Legion::Logging.info '[memory] CacheStore initialized (memcached-backed)'
22
+ @traces = Legion::Cache.get(TRACES_KEY) || {}
23
+ @associations = Legion::Cache.get(ASSOC_KEY) || {}
24
+ @dirty = false
25
+ Legion::Logging.info "[memory] CacheStore loaded #{@traces.size} traces from cache"
26
+ end
27
+
28
+ def store(trace)
29
+ @traces[trace[:trace_id]] = trace
30
+ @dirty = true
31
+ trace[:trace_id]
32
+ end
33
+
34
+ def get(trace_id)
35
+ @traces[trace_id]
36
+ end
37
+
38
+ def delete(trace_id)
39
+ @traces.delete(trace_id)
40
+ @associations.delete(trace_id)
41
+ @associations.each_value { |links| links.delete(trace_id) }
42
+ @dirty = true
43
+ end
44
+
45
+ def retrieve_by_type(type, min_strength: 0.0, limit: 100)
46
+ @traces.values
47
+ .select { |t| t[:trace_type] == type && t[:strength] >= min_strength }
48
+ .sort_by { |t| -t[:strength] }
49
+ .first(limit)
50
+ end
51
+
52
+ def retrieve_by_domain(domain_tag, min_strength: 0.0, limit: 100)
53
+ @traces.values
54
+ .select { |t| t[:domain_tags].include?(domain_tag) && t[:strength] >= min_strength }
55
+ .sort_by { |t| -t[:strength] }
56
+ .first(limit)
57
+ end
58
+
59
+ def retrieve_associated(trace_id, min_strength: 0.0, limit: 20)
60
+ trace = @traces[trace_id]
61
+ return [] unless trace
62
+
63
+ trace[:associated_traces]
64
+ .filter_map { |id| @traces[id] }
65
+ .select { |t| t[:strength] >= min_strength }
66
+ .sort_by { |t| -t[:strength] }
67
+ .first(limit)
68
+ end
69
+
70
+ def record_coactivation(trace_id_a, trace_id_b)
71
+ return if trace_id_a == trace_id_b
72
+
73
+ @associations[trace_id_a] ||= {}
74
+ @associations[trace_id_b] ||= {}
75
+ @associations[trace_id_a][trace_id_b] = (@associations[trace_id_a][trace_id_b] || 0) + 1
76
+ @associations[trace_id_b][trace_id_a] = (@associations[trace_id_b][trace_id_a] || 0) + 1
77
+
78
+ threshold = Helpers::Trace::COACTIVATION_THRESHOLD
79
+ link_traces(trace_id_a, trace_id_b) if @associations[trace_id_a][trace_id_b] >= threshold
80
+ @dirty = true
81
+ end
82
+
83
+ def all_traces(min_strength: 0.0)
84
+ @traces.values.select { |t| t[:strength] >= min_strength }
85
+ end
86
+
87
+ def count
88
+ @traces.size
89
+ end
90
+
91
+ def firmware_traces
92
+ retrieve_by_type(:firmware)
93
+ end
94
+
95
+ def walk_associations(start_id:, max_hops: 12, min_strength: 0.1)
96
+ return [] unless @traces.key?(start_id)
97
+
98
+ results = []
99
+ visited = Set.new([start_id])
100
+ queue = [[start_id, 0, [start_id]]]
101
+
102
+ until queue.empty?
103
+ current_id, depth, path = queue.shift
104
+ current = @traces[current_id]
105
+ next unless current
106
+
107
+ current[:associated_traces].each do |neighbor_id|
108
+ next if visited.include?(neighbor_id)
109
+
110
+ neighbor = @traces[neighbor_id]
111
+ next unless neighbor
112
+ next unless neighbor[:strength] >= min_strength
113
+
114
+ visited << neighbor_id
115
+ neighbor_path = path + [neighbor_id]
116
+ results << { trace_id: neighbor_id, depth: depth + 1, path: neighbor_path }
117
+ queue << [neighbor_id, depth + 1, neighbor_path] if depth + 1 < max_hops
118
+ end
119
+ end
120
+
121
+ results
122
+ end
123
+
124
+ # Write local state to cache
125
+ def flush
126
+ return unless @dirty
127
+
128
+ Legion::Cache.set(TRACES_KEY, @traces, TTL)
129
+ Legion::Cache.set(ASSOC_KEY, strip_default_procs(@associations), TTL)
130
+ @dirty = false
131
+ Legion::Logging.debug "[memory] CacheStore flushed #{@traces.size} traces to cache"
132
+ end
133
+
134
+ # Pull latest state from cache (after another process wrote)
135
+ def reload
136
+ @traces = Legion::Cache.get(TRACES_KEY) || {}
137
+ @associations = Legion::Cache.get(ASSOC_KEY) || {}
138
+ @dirty = false
139
+ Legion::Logging.debug "[memory] CacheStore reloaded #{@traces.size} traces from cache"
140
+ end
141
+
142
+ private
143
+
144
+ def strip_default_procs(hash)
145
+ hash.each_with_object({}) do |(k, v), plain|
146
+ plain[k] = v.is_a?(Hash) ? {}.merge(v) : v
147
+ end
148
+ end
149
+
150
+ def link_traces(id_a, id_b)
151
+ trace_a = @traces[id_a]
152
+ trace_b = @traces[id_b]
153
+ return unless trace_a && trace_b
154
+
155
+ max = Helpers::Trace::MAX_ASSOCIATIONS
156
+ trace_a[:associated_traces] << id_b unless trace_a[:associated_traces].include?(id_b) || trace_a[:associated_traces].size >= max
157
+ return if trace_b[:associated_traces].include?(id_a) || trace_b[:associated_traces].size >= max
158
+
159
+ trace_b[:associated_traces] << id_a
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Trace
8
+ module Helpers
9
+ module Decay
10
+ module_function
11
+
12
+ # Power-law decay formula from spec:
13
+ # new_strength = peak_strength * (ticks_since_access + 1)^(-base_decay_rate / (1 + emotional_intensity * E_WEIGHT))
14
+ def compute_decay(peak_strength:, base_decay_rate:, ticks_since_access:, emotional_intensity: 0.0, **)
15
+ return peak_strength if base_decay_rate.zero?
16
+ return 0.0 if peak_strength.zero?
17
+
18
+ e_weight = Helpers::Trace::E_WEIGHT
19
+ effective_rate = base_decay_rate / (1.0 + (emotional_intensity * e_weight))
20
+ new_strength = peak_strength * ((ticks_since_access + 1).to_f**(-effective_rate))
21
+ new_strength.clamp(0.0, 1.0)
22
+ end
23
+
24
+ # Reinforcement formula from spec:
25
+ # new_strength = min(1.0, current_strength + R_AMOUNT * IMPRINT_MULTIPLIER_if_applicable)
26
+ def compute_reinforcement(current_strength:, imprint_active: false, **)
27
+ r_amount = Helpers::Trace::R_AMOUNT
28
+ multiplier = imprint_active ? Helpers::Trace::IMPRINT_MULTIPLIER : 1.0
29
+ new_strength = current_strength + (r_amount * multiplier)
30
+ new_strength.clamp(0.0, 1.0)
31
+ end
32
+
33
+ # Composite retrieval score from spec:
34
+ # score = strength * recency_factor * emotional_weight * (1 + association_bonus)
35
+ def compute_retrieval_score(trace:, query_time: nil, associated: false, **)
36
+ query_time ||= Time.now.utc
37
+ seconds_since = [query_time - trace[:last_reinforced], 0].max
38
+ half_life = Helpers::Trace::RETRIEVAL_RECENCY_HALF.to_f
39
+
40
+ recency_factor = 2.0**(-seconds_since / half_life)
41
+ emotional_weight = 1.0 + trace[:emotional_intensity]
42
+ assoc_bonus = associated ? (1.0 + Helpers::Trace::ASSOCIATION_BONUS) : 1.0
43
+
44
+ trace[:strength] * recency_factor * emotional_weight * assoc_bonus
45
+ end
46
+
47
+ # Determine storage tier based on last access time
48
+ def compute_storage_tier(trace:, now: nil, **)
49
+ now ||= Time.now.utc
50
+ seconds_since = now - trace[:last_reinforced]
51
+
52
+ if trace[:strength] <= Helpers::Trace::PRUNE_THRESHOLD
53
+ :erased
54
+ elsif seconds_since <= Helpers::Trace::HOT_TIER_WINDOW
55
+ :hot
56
+ elsif seconds_since <= Helpers::Trace::WARM_TIER_WINDOW
57
+ :warm
58
+ else
59
+ :cold
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Agentic
6
+ module Memory
7
+ module Trace
8
+ module Helpers
9
+ module ErrorTracer
10
+ DEBOUNCE_WINDOW = 60 # seconds
11
+ ERROR_VALENCE = -0.6
12
+ ERROR_INTENSITY = 0.7
13
+ FATAL_VALENCE = -0.8
14
+ FATAL_INTENSITY = 0.9
15
+
16
+ class << self
17
+ def setup
18
+ return if @active
19
+
20
+ @recent = {}
21
+ @runner = Object.new.extend(Legion::Extensions::Agentic::Memory::Trace::Runners::Traces)
22
+ wrap_logging_methods
23
+ @active = true
24
+ Legion::Logging.info '[memory] ErrorTracer active — errors/fatals will become episodic traces'
25
+ end
26
+
27
+ def active?
28
+ @active == true
29
+ end
30
+
31
+ private
32
+
33
+ def wrap_logging_methods
34
+ original_error = Legion::Logging.method(:error)
35
+ original_fatal = Legion::Logging.method(:fatal)
36
+
37
+ Legion::Logging.define_singleton_method(:error) do |message = nil, &block|
38
+ message = block.call if message.nil? && block
39
+ original_error.call(message)
40
+ ErrorTracer.send(:record_trace, message, :error) if message.is_a?(String)
41
+ end
42
+
43
+ Legion::Logging.define_singleton_method(:fatal) do |message = nil, &block|
44
+ message = block.call if message.nil? && block
45
+ original_fatal.call(message)
46
+ ErrorTracer.send(:record_trace, message, :fatal) if message.is_a?(String)
47
+ end
48
+ end
49
+
50
+ def record_trace(message, level)
51
+ return unless message.is_a?(String) && !message.empty?
52
+
53
+ # Debounce: skip if same message within window
54
+ now = Time.now.utc
55
+ key = "#{level}:#{message[0..100]}"
56
+ return if @recent[key] && (now - @recent[key]) < DEBOUNCE_WINDOW
57
+
58
+ @recent[key] = now
59
+
60
+ # Clean old entries periodically
61
+ @recent.delete_if { |_, t| (now - t) > DEBOUNCE_WINDOW } if @recent.size > 500
62
+
63
+ # Extract component from [bracket] prefix
64
+ component = message.match(/\A\[([^\]]+)\]/)&.captures&.first || 'unknown'
65
+
66
+ valence = level == :fatal ? FATAL_VALENCE : ERROR_VALENCE
67
+ intensity = level == :fatal ? FATAL_INTENSITY : ERROR_INTENSITY
68
+
69
+ @runner.store_trace(
70
+ type: :episodic,
71
+ content_payload: message,
72
+ domain_tags: ['error', component.downcase],
73
+ origin: :direct_experience,
74
+ emotional_valence: valence,
75
+ emotional_intensity: intensity,
76
+ unresolved: true,
77
+ confidence: 0.9
78
+ )
79
+
80
+ # Flush if cache-backed
81
+ store = @runner.send(:default_store)
82
+ store.flush if store.respond_to?(:flush)
83
+ rescue StandardError
84
+ # Never let trace creation break the logging pipeline
85
+ nil
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,260 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Agentic
8
+ module Memory
9
+ module Trace
10
+ module Helpers
11
+ # In-memory store for development and testing.
12
+ # Production deployments should use a PostgreSQL + Redis backed store.
13
+ class Store
14
+ attr_reader :traces, :associations
15
+
16
+ def initialize
17
+ @traces = {}
18
+ @associations = Hash.new { |h, k| h[k] = Hash.new(0) }
19
+ load_from_local
20
+ end
21
+
22
+ def store(trace)
23
+ @traces[trace[:trace_id]] = trace
24
+ trace[:trace_id]
25
+ end
26
+
27
+ def get(trace_id)
28
+ @traces[trace_id]
29
+ end
30
+
31
+ def delete(trace_id)
32
+ @traces.delete(trace_id)
33
+ @associations.delete(trace_id)
34
+ @associations.each_value { |links| links.delete(trace_id) }
35
+ end
36
+
37
+ def retrieve_by_type(type, min_strength: 0.0, limit: 100)
38
+ @traces.values
39
+ .select { |t| t[:trace_type] == type && t[:strength] >= min_strength }
40
+ .sort_by { |t| -t[:strength] }
41
+ .first(limit)
42
+ end
43
+
44
+ def retrieve_by_domain(domain_tag, min_strength: 0.0, limit: 100)
45
+ @traces.values
46
+ .select { |t| t[:domain_tags].include?(domain_tag) && t[:strength] >= min_strength }
47
+ .sort_by { |t| -t[:strength] }
48
+ .first(limit)
49
+ end
50
+
51
+ def retrieve_associated(trace_id, min_strength: 0.0, limit: 20)
52
+ trace = @traces[trace_id]
53
+ return [] unless trace
54
+
55
+ trace[:associated_traces]
56
+ .filter_map { |id| @traces[id] }
57
+ .select { |t| t[:strength] >= min_strength }
58
+ .sort_by { |t| -t[:strength] }
59
+ .first(limit)
60
+ end
61
+
62
+ def record_coactivation(trace_id_a, trace_id_b)
63
+ return if trace_id_a == trace_id_b
64
+
65
+ @associations[trace_id_a][trace_id_b] += 1
66
+ @associations[trace_id_b][trace_id_a] += 1
67
+
68
+ threshold = Helpers::Trace::COACTIVATION_THRESHOLD
69
+
70
+ return unless @associations[trace_id_a][trace_id_b] >= threshold
71
+
72
+ link_traces(trace_id_a, trace_id_b)
73
+ end
74
+
75
+ def all_traces(min_strength: 0.0)
76
+ @traces.values.select { |t| t[:strength] >= min_strength }
77
+ end
78
+
79
+ def count
80
+ @traces.size
81
+ end
82
+
83
+ def firmware_traces
84
+ retrieve_by_type(:firmware)
85
+ end
86
+
87
+ def walk_associations(start_id:, max_hops: 12, min_strength: 0.1)
88
+ return [] unless @traces.key?(start_id)
89
+
90
+ results = []
91
+ visited = Set.new([start_id])
92
+ queue = [[start_id, 0, [start_id]]]
93
+
94
+ until queue.empty?
95
+ current_id, depth, path = queue.shift
96
+ next unless (current = @traces[current_id])
97
+
98
+ current[:associated_traces].each do |neighbor_id|
99
+ next if visited.include?(neighbor_id)
100
+
101
+ neighbor = @traces[neighbor_id]
102
+ next unless neighbor
103
+ next unless neighbor[:strength] >= min_strength
104
+
105
+ visited << neighbor_id
106
+ neighbor_path = path + [neighbor_id]
107
+ results << { trace_id: neighbor_id, depth: depth + 1, path: neighbor_path }
108
+ queue << [neighbor_id, depth + 1, neighbor_path] if depth + 1 < max_hops
109
+ end
110
+ end
111
+
112
+ results
113
+ end
114
+
115
+ def save_to_local
116
+ return unless defined?(Legion::Data::Local) && Legion::Data::Local.connected?
117
+ return unless Legion::Data::Local.connection.table_exists?(:memory_traces)
118
+
119
+ db = Legion::Data::Local.connection
120
+
121
+ @traces.each_value do |trace|
122
+ row = serialize_trace_for_db(trace)
123
+ existing = db[:memory_traces].where(trace_id: trace[:trace_id]).first
124
+ if existing
125
+ db[:memory_traces].where(trace_id: trace[:trace_id]).update(row)
126
+ else
127
+ db[:memory_traces].insert(row)
128
+ end
129
+ end
130
+
131
+ db_trace_ids = db[:memory_traces].select_map(:trace_id)
132
+ memory_trace_ids = @traces.keys
133
+ stale_ids = db_trace_ids - memory_trace_ids
134
+ db[:memory_traces].where(trace_id: stale_ids).delete unless stale_ids.empty?
135
+
136
+ db[:memory_associations].delete
137
+ @associations.each do |id_a, targets|
138
+ targets.each do |id_b, count|
139
+ db[:memory_associations].insert(trace_id_a: id_a, trace_id_b: id_b, coactivation_count: count)
140
+ end
141
+ end
142
+ end
143
+
144
+ def load_from_local
145
+ return unless defined?(Legion::Data::Local) && Legion::Data::Local.connected?
146
+ return unless Legion::Data::Local.connection.table_exists?(:memory_traces)
147
+
148
+ db = Legion::Data::Local.connection
149
+
150
+ db[:memory_traces].each do |row|
151
+ @traces[row[:trace_id]] = deserialize_trace_from_db(row)
152
+ end
153
+
154
+ db[:memory_associations].each do |row|
155
+ @associations[row[:trace_id_a]] ||= {}
156
+ @associations[row[:trace_id_a]][row[:trace_id_b]] = row[:coactivation_count]
157
+ end
158
+ end
159
+
160
+ private
161
+
162
+ def serialize_trace_for_db(trace)
163
+ payload = trace[:content_payload] || trace[:content]
164
+ {
165
+ trace_id: trace[:trace_id],
166
+ trace_type: trace[:trace_type].to_s,
167
+ content: payload.is_a?(Hash) ? ::JSON.generate(payload) : payload.to_s,
168
+ strength: trace[:strength],
169
+ peak_strength: trace[:peak_strength],
170
+ base_decay_rate: trace[:base_decay_rate],
171
+ emotional_valence: trace[:emotional_valence].is_a?(Hash) ? ::JSON.generate(trace[:emotional_valence]) : nil,
172
+ emotional_intensity: trace[:emotional_intensity],
173
+ domain_tags: trace[:domain_tags].is_a?(Array) ? ::JSON.generate(trace[:domain_tags]) : nil,
174
+ origin: trace[:origin].to_s,
175
+ created_at: trace[:created_at],
176
+ last_reinforced: trace[:last_reinforced],
177
+ last_decayed: trace[:last_decayed],
178
+ reinforcement_count: trace[:reinforcement_count],
179
+ confidence: trace[:confidence],
180
+ storage_tier: trace[:storage_tier].to_s,
181
+ partition_id: trace[:partition_id],
182
+ associated_traces: trace[:associated_traces].is_a?(Array) ? ::JSON.generate(trace[:associated_traces]) : nil,
183
+ parent_id: trace[:parent_trace_id] || trace[:parent_id],
184
+ child_ids: (trace[:child_trace_ids] || trace[:child_ids]).then do |v|
185
+ v.is_a?(Array) ? ::JSON.generate(v) : nil
186
+ end,
187
+ unresolved: trace[:unresolved] || false,
188
+ consolidation_candidate: trace[:consolidation_candidate] || false
189
+ }
190
+ end
191
+
192
+ def deserialize_trace_from_db(row)
193
+ content_raw = row[:content]
194
+ content = begin
195
+ parsed = ::JSON.parse(content_raw, symbolize_names: true)
196
+ parsed.is_a?(Hash) ? parsed : content_raw
197
+ rescue StandardError
198
+ content_raw
199
+ end
200
+ {
201
+ trace_id: row[:trace_id],
202
+ trace_type: row[:trace_type]&.to_sym,
203
+ content_payload: content,
204
+ content: content,
205
+ strength: row[:strength],
206
+ peak_strength: row[:peak_strength],
207
+ base_decay_rate: row[:base_decay_rate],
208
+ emotional_valence: begin
209
+ ::JSON.parse(row[:emotional_valence], symbolize_names: true)
210
+ rescue StandardError
211
+ 0.0
212
+ end,
213
+ emotional_intensity: row[:emotional_intensity],
214
+ domain_tags: begin
215
+ ::JSON.parse(row[:domain_tags])
216
+ rescue StandardError
217
+ []
218
+ end,
219
+ origin: row[:origin]&.to_sym,
220
+ created_at: row[:created_at],
221
+ last_reinforced: row[:last_reinforced],
222
+ last_decayed: row[:last_decayed],
223
+ reinforcement_count: row[:reinforcement_count],
224
+ confidence: row[:confidence],
225
+ storage_tier: row[:storage_tier]&.to_sym,
226
+ partition_id: row[:partition_id],
227
+ associated_traces: begin
228
+ ::JSON.parse(row[:associated_traces])
229
+ rescue StandardError
230
+ []
231
+ end,
232
+ parent_trace_id: row[:parent_id],
233
+ child_trace_ids: begin
234
+ ::JSON.parse(row[:child_ids])
235
+ rescue StandardError
236
+ []
237
+ end,
238
+ unresolved: row[:unresolved] || false,
239
+ consolidation_candidate: row[:consolidation_candidate] || false
240
+ }
241
+ end
242
+
243
+ def link_traces(id_a, id_b)
244
+ trace_a = @traces[id_a]
245
+ trace_b = @traces[id_b]
246
+ return unless trace_a && trace_b
247
+
248
+ max = Helpers::Trace::MAX_ASSOCIATIONS
249
+ trace_a[:associated_traces] << id_b unless trace_a[:associated_traces].include?(id_b) || trace_a[:associated_traces].size >= max
250
+ return if trace_b[:associated_traces].include?(id_a) || trace_b[:associated_traces].size >= max
251
+
252
+ trace_b[:associated_traces] << id_a
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end
259
+ end
260
+ end