@theowlops/boombot 0.0.0-stage → 1.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 (544) hide show
  1. package/README.md +69 -3
  2. package/dist/LICENSE +21 -0
  3. package/dist/bin/boombot.js +16 -0
  4. package/dist/cli/api-key.js +123 -0
  5. package/dist/cli/bin-bun.js +17 -0
  6. package/dist/cli/bin.js +34 -0
  7. package/dist/cli/cli/cli-error.js +61 -0
  8. package/dist/cli/cli/dispatch.js +138 -0
  9. package/dist/cli/cli/help.js +83 -0
  10. package/dist/cli/cli/parser.js +89 -0
  11. package/dist/cli/cli/registry.js +185 -0
  12. package/dist/cli/cli/spec.js +41 -0
  13. package/dist/cli/cli/suggest.js +125 -0
  14. package/dist/cli/cli/version.js +30 -0
  15. package/dist/cli/cli/vocabulary.js +87 -0
  16. package/dist/cli/commands/doctor.js +365 -0
  17. package/dist/cli/commands/gateway.js +290 -0
  18. package/dist/cli/commands/migrate.js +270 -0
  19. package/dist/cli/commands/plugin.js +97 -0
  20. package/dist/cli/commands/setup.js +230 -0
  21. package/dist/cli/config-file.js +133 -0
  22. package/dist/cli/master-key.js +78 -0
  23. package/dist/cli/open-browser.js +59 -0
  24. package/dist/cli/web-assets.js +58 -0
  25. package/dist/gateway/auth.js +46 -0
  26. package/dist/gateway/config-format.js +202 -0
  27. package/dist/gateway/config.js +427 -0
  28. package/dist/gateway/context.js +1 -0
  29. package/dist/gateway/env-file.js +129 -0
  30. package/dist/gateway/events.js +172 -0
  31. package/dist/gateway/guard/tagging.js +505 -0
  32. package/dist/gateway/handlers/index.js +66 -0
  33. package/dist/gateway/handlers/memory.js +54 -0
  34. package/dist/gateway/handlers/providers.js +73 -0
  35. package/dist/gateway/handlers/registry.js +39 -0
  36. package/dist/gateway/handlers/sessions.js +188 -0
  37. package/dist/gateway/handlers/system.js +12 -0
  38. package/dist/gateway/handlers/tasks.js +191 -0
  39. package/dist/gateway/handlers/turn.js +100 -0
  40. package/dist/gateway/index.js +126 -0
  41. package/dist/gateway/main.js +23 -0
  42. package/dist/gateway/server.js +354 -0
  43. package/dist/gateway/services/context-limits.js +75 -0
  44. package/dist/gateway/services/history-normalizer.js +42 -0
  45. package/dist/gateway/services/image-pipeline.js +56 -0
  46. package/dist/gateway/services/key-roulette-client.js +43 -0
  47. package/dist/gateway/services/llm-client.js +593 -0
  48. package/dist/gateway/services/llm-decoder.js +200 -0
  49. package/dist/gateway/services/local-fallback.js +135 -0
  50. package/dist/gateway/services/memory.js +511 -0
  51. package/dist/gateway/services/persistent-memory.js +196 -0
  52. package/dist/gateway/services/providers.js +44 -0
  53. package/dist/gateway/services/tasks.js +237 -0
  54. package/dist/gateway/services/verification.js +124 -0
  55. package/dist/gateway/services/vision-payload.js +48 -0
  56. package/dist/gateway/services.js +257 -0
  57. package/dist/gateway/session-snapshot.js +19 -0
  58. package/dist/gateway/sessions.js +701 -0
  59. package/dist/gateway/static.js +239 -0
  60. package/dist/gateway/turns/clarify-tool.js +57 -0
  61. package/dist/gateway/turns/cronjob-tool.js +96 -0
  62. package/dist/gateway/turns/deferred-tools.js +154 -0
  63. package/dist/gateway/turns/execute-code-tool.js +84 -0
  64. package/dist/gateway/turns/file-tool-defs.js +178 -0
  65. package/dist/gateway/turns/file-tool-ops.js +178 -0
  66. package/dist/gateway/turns/file-tool-scan.js +182 -0
  67. package/dist/gateway/turns/file-tool.js +302 -0
  68. package/dist/gateway/turns/index.js +416 -0
  69. package/dist/gateway/turns/jev-middleware.js +91 -0
  70. package/dist/gateway/turns/local-reply.js +156 -0
  71. package/dist/gateway/turns/mcp-client.js +643 -0
  72. package/dist/gateway/turns/memory-tool.js +171 -0
  73. package/dist/gateway/turns/middleware.js +96 -0
  74. package/dist/gateway/turns/python-core.js +397 -0
  75. package/dist/gateway/turns/routing/index.js +3 -0
  76. package/dist/gateway/turns/routing/intent-classifier.js +60 -0
  77. package/dist/gateway/turns/routing/planner-adapter.js +91 -0
  78. package/dist/gateway/turns/routing/types.js +1 -0
  79. package/dist/gateway/turns/runtime-deps.js +228 -0
  80. package/dist/gateway/turns/self-config-tool.js +277 -0
  81. package/dist/gateway/turns/service-types.js +11 -0
  82. package/dist/gateway/turns/service.js +820 -0
  83. package/dist/gateway/turns/session-adapter.js +158 -0
  84. package/dist/gateway/turns/session-tool.js +49 -0
  85. package/dist/gateway/turns/shell-tool.js +190 -0
  86. package/dist/gateway/turns/skill-tool.js +239 -0
  87. package/dist/gateway/turns/spillover.js +201 -0
  88. package/dist/gateway/turns/subagent-tool.js +140 -0
  89. package/dist/gateway/turns/system-prompt.js +121 -0
  90. package/dist/gateway/turns/tool-schema.js +90 -0
  91. package/dist/gateway/turns/turn-utils.js +58 -0
  92. package/dist/gateway/turns/vision-tool.js +120 -0
  93. package/dist/gateway/types.js +1 -0
  94. package/dist/native/win32-x64/boombot-native-host.exe +0 -0
  95. package/dist/node_modules/@boombot/channels/handlers.d.ts +9 -0
  96. package/dist/node_modules/@boombot/channels/handlers.js +109 -0
  97. package/dist/node_modules/@boombot/channels/index.d.ts +12 -0
  98. package/dist/node_modules/@boombot/channels/index.js +12 -0
  99. package/dist/node_modules/@boombot/channels/package.json +9 -0
  100. package/dist/node_modules/@boombot/channels/service.d.ts +62 -0
  101. package/dist/node_modules/@boombot/channels/service.js +608 -0
  102. package/dist/node_modules/@boombot/channels/tool.d.ts +10 -0
  103. package/dist/node_modules/@boombot/channels/tool.js +46 -0
  104. package/dist/node_modules/@boombot/channels/transports/common/dedup.d.ts +17 -0
  105. package/dist/node_modules/@boombot/channels/transports/common/dedup.js +42 -0
  106. package/dist/node_modules/@boombot/channels/transports/common/handoff.d.ts +19 -0
  107. package/dist/node_modules/@boombot/channels/transports/common/handoff.js +32 -0
  108. package/dist/node_modules/@boombot/channels/transports/common/index.d.ts +3 -0
  109. package/dist/node_modules/@boombot/channels/transports/common/index.js +3 -0
  110. package/dist/node_modules/@boombot/channels/transports/common/types.d.ts +99 -0
  111. package/dist/node_modules/@boombot/channels/transports/common/types.js +54 -0
  112. package/dist/node_modules/@boombot/channels/transports/common.d.ts +2 -0
  113. package/dist/node_modules/@boombot/channels/transports/common.js +2 -0
  114. package/dist/node_modules/@boombot/channels/transports/dedup/index.d.ts +1 -0
  115. package/dist/node_modules/@boombot/channels/transports/dedup/index.js +1 -0
  116. package/dist/node_modules/@boombot/channels/transports/dedup.d.ts +2 -0
  117. package/dist/node_modules/@boombot/channels/transports/dedup.js +2 -0
  118. package/dist/node_modules/@boombot/channels/transports/discord/index.d.ts +1 -0
  119. package/dist/node_modules/@boombot/channels/transports/discord/index.js +1 -0
  120. package/dist/node_modules/@boombot/channels/transports/discord/transport.d.ts +36 -0
  121. package/dist/node_modules/@boombot/channels/transports/discord/transport.js +199 -0
  122. package/dist/node_modules/@boombot/channels/transports/discord.d.ts +2 -0
  123. package/dist/node_modules/@boombot/channels/transports/discord.js +2 -0
  124. package/dist/node_modules/@boombot/channels/transports/handoff.d.ts +2 -0
  125. package/dist/node_modules/@boombot/channels/transports/handoff.js +2 -0
  126. package/dist/node_modules/@boombot/channels/transports/messenger/index.d.ts +1 -0
  127. package/dist/node_modules/@boombot/channels/transports/messenger/index.js +1 -0
  128. package/dist/node_modules/@boombot/channels/transports/messenger/transport.d.ts +34 -0
  129. package/dist/node_modules/@boombot/channels/transports/messenger/transport.js +180 -0
  130. package/dist/node_modules/@boombot/channels/transports/messenger.d.ts +2 -0
  131. package/dist/node_modules/@boombot/channels/transports/messenger.js +2 -0
  132. package/dist/node_modules/@boombot/channels/transports/slack/index.d.ts +1 -0
  133. package/dist/node_modules/@boombot/channels/transports/slack/index.js +1 -0
  134. package/dist/node_modules/@boombot/channels/transports/slack/transport.d.ts +35 -0
  135. package/dist/node_modules/@boombot/channels/transports/slack/transport.js +183 -0
  136. package/dist/node_modules/@boombot/channels/transports/slack.d.ts +2 -0
  137. package/dist/node_modules/@boombot/channels/transports/slack.js +2 -0
  138. package/dist/node_modules/@boombot/channels/transports/streamer/index.d.ts +1 -0
  139. package/dist/node_modules/@boombot/channels/transports/streamer/index.js +1 -0
  140. package/dist/node_modules/@boombot/channels/transports/streamer/streamer.d.ts +31 -0
  141. package/dist/node_modules/@boombot/channels/transports/streamer/streamer.js +141 -0
  142. package/dist/node_modules/@boombot/channels/transports/streamer.d.ts +2 -0
  143. package/dist/node_modules/@boombot/channels/transports/streamer.js +2 -0
  144. package/dist/node_modules/@boombot/channels/transports/telegram/index.d.ts +1 -0
  145. package/dist/node_modules/@boombot/channels/transports/telegram/index.js +1 -0
  146. package/dist/node_modules/@boombot/channels/transports/telegram/transport.d.ts +75 -0
  147. package/dist/node_modules/@boombot/channels/transports/telegram/transport.js +395 -0
  148. package/dist/node_modules/@boombot/channels/transports/telegram.d.ts +2 -0
  149. package/dist/node_modules/@boombot/channels/transports/telegram.js +2 -0
  150. package/dist/node_modules/@boombot/channels/transports/tiktok/index.d.ts +1 -0
  151. package/dist/node_modules/@boombot/channels/transports/tiktok/index.js +1 -0
  152. package/dist/node_modules/@boombot/channels/transports/tiktok/transport.d.ts +27 -0
  153. package/dist/node_modules/@boombot/channels/transports/tiktok/transport.js +97 -0
  154. package/dist/node_modules/@boombot/channels/transports/tiktok.d.ts +2 -0
  155. package/dist/node_modules/@boombot/channels/transports/tiktok.js +2 -0
  156. package/dist/node_modules/@boombot/channels/transports/whatsapp/index.d.ts +1 -0
  157. package/dist/node_modules/@boombot/channels/transports/whatsapp/index.js +1 -0
  158. package/dist/node_modules/@boombot/channels/transports/whatsapp/transport.d.ts +38 -0
  159. package/dist/node_modules/@boombot/channels/transports/whatsapp/transport.js +189 -0
  160. package/dist/node_modules/@boombot/channels/transports/whatsapp.d.ts +2 -0
  161. package/dist/node_modules/@boombot/channels/transports/whatsapp.js +2 -0
  162. package/dist/node_modules/@boombot/channels/transports/zalo/index.d.ts +2 -0
  163. package/dist/node_modules/@boombot/channels/transports/zalo/index.js +2 -0
  164. package/dist/node_modules/@boombot/channels/transports/zalo/oa.d.ts +28 -0
  165. package/dist/node_modules/@boombot/channels/transports/zalo/oa.js +94 -0
  166. package/dist/node_modules/@boombot/channels/transports/zalo/personal.d.ts +106 -0
  167. package/dist/node_modules/@boombot/channels/transports/zalo/personal.js +171 -0
  168. package/dist/node_modules/@boombot/channels/transports/zalo-personal.d.ts +2 -0
  169. package/dist/node_modules/@boombot/channels/transports/zalo-personal.js +2 -0
  170. package/dist/node_modules/@boombot/channels/transports/zalo.d.ts +2 -0
  171. package/dist/node_modules/@boombot/channels/transports/zalo.js +2 -0
  172. package/dist/node_modules/@boombot/core/agent-core/index.d.ts +12 -0
  173. package/dist/node_modules/@boombot/core/agent-core/index.js +12 -0
  174. package/dist/node_modules/@boombot/core/agent-core/middleware.d.ts +60 -0
  175. package/dist/node_modules/@boombot/core/agent-core/middleware.js +152 -0
  176. package/dist/node_modules/@boombot/core/agent-core/routing.d.ts +128 -0
  177. package/dist/node_modules/@boombot/core/agent-core/routing.js +437 -0
  178. package/dist/node_modules/@boombot/core/agent-core/runtime-deps.d.ts +88 -0
  179. package/dist/node_modules/@boombot/core/agent-core/runtime-deps.js +8 -0
  180. package/dist/node_modules/@boombot/core/agent-core/tool-errors.d.ts +44 -0
  181. package/dist/node_modules/@boombot/core/agent-core/tool-errors.js +60 -0
  182. package/dist/node_modules/@boombot/core/agent-core/turn-loop.d.ts +21 -0
  183. package/dist/node_modules/@boombot/core/agent-core/turn-loop.js +561 -0
  184. package/dist/node_modules/@boombot/core/agent-core/types.d.ts +189 -0
  185. package/dist/node_modules/@boombot/core/agent-core/types.js +7 -0
  186. package/dist/node_modules/@boombot/core/compaction/compactor.d.ts +19 -0
  187. package/dist/node_modules/@boombot/core/compaction/compactor.js +127 -0
  188. package/dist/node_modules/@boombot/core/compaction/estimator.d.ts +10 -0
  189. package/dist/node_modules/@boombot/core/compaction/estimator.js +48 -0
  190. package/dist/node_modules/@boombot/core/compaction/index.d.ts +3 -0
  191. package/dist/node_modules/@boombot/core/compaction/index.js +3 -0
  192. package/dist/node_modules/@boombot/core/compaction/types.d.ts +31 -0
  193. package/dist/node_modules/@boombot/core/compaction/types.js +7 -0
  194. package/dist/node_modules/@boombot/core/index.d.ts +9 -0
  195. package/dist/node_modules/@boombot/core/index.js +9 -0
  196. package/dist/node_modules/@boombot/core/jev/index.d.ts +2 -0
  197. package/dist/node_modules/@boombot/core/jev/index.js +2 -0
  198. package/dist/node_modules/@boombot/core/jev/jev.d.ts +30 -0
  199. package/dist/node_modules/@boombot/core/jev/jev.js +164 -0
  200. package/dist/node_modules/@boombot/core/jev/types.d.ts +22 -0
  201. package/dist/node_modules/@boombot/core/jev/types.js +1 -0
  202. package/dist/node_modules/@boombot/core/memory/consolidation/budget.d.ts +63 -0
  203. package/dist/node_modules/@boombot/core/memory/consolidation/budget.js +94 -0
  204. package/dist/node_modules/@boombot/core/memory/consolidation/consolidator.d.ts +152 -0
  205. package/dist/node_modules/@boombot/core/memory/consolidation/consolidator.js +318 -0
  206. package/dist/node_modules/@boombot/core/memory/consolidation/index.d.ts +12 -0
  207. package/dist/node_modules/@boombot/core/memory/consolidation/index.js +12 -0
  208. package/dist/node_modules/@boombot/core/memory/curation.d.ts +86 -0
  209. package/dist/node_modules/@boombot/core/memory/curation.js +120 -0
  210. package/dist/node_modules/@boombot/core/memory/errors.d.ts +12 -0
  211. package/dist/node_modules/@boombot/core/memory/errors.js +20 -0
  212. package/dist/node_modules/@boombot/core/memory/hybrid-search.d.ts +29 -0
  213. package/dist/node_modules/@boombot/core/memory/hybrid-search.js +52 -0
  214. package/dist/node_modules/@boombot/core/memory/index.d.ts +23 -0
  215. package/dist/node_modules/@boombot/core/memory/index.js +23 -0
  216. package/dist/node_modules/@boombot/core/memory/indexer.d.ts +47 -0
  217. package/dist/node_modules/@boombot/core/memory/indexer.js +138 -0
  218. package/dist/node_modules/@boombot/core/memory/lifecycle.d.ts +12 -0
  219. package/dist/node_modules/@boombot/core/memory/lifecycle.js +66 -0
  220. package/dist/node_modules/@boombot/core/memory/provider.d.ts +84 -0
  221. package/dist/node_modules/@boombot/core/memory/provider.js +117 -0
  222. package/dist/node_modules/@boombot/core/memory/recall.d.ts +7 -0
  223. package/dist/node_modules/@boombot/core/memory/recall.js +111 -0
  224. package/dist/node_modules/@boombot/core/memory/scope-resolver.d.ts +27 -0
  225. package/dist/node_modules/@boombot/core/memory/scope-resolver.js +60 -0
  226. package/dist/node_modules/@boombot/core/memory/search.d.ts +37 -0
  227. package/dist/node_modules/@boombot/core/memory/search.js +158 -0
  228. package/dist/node_modules/@boombot/core/memory/store.d.ts +60 -0
  229. package/dist/node_modules/@boombot/core/memory/store.js +289 -0
  230. package/dist/node_modules/@boombot/core/memory/types.d.ts +121 -0
  231. package/dist/node_modules/@boombot/core/memory/types.js +21 -0
  232. package/dist/node_modules/@boombot/core/memory/vector-index.d.ts +34 -0
  233. package/dist/node_modules/@boombot/core/memory/vector-index.js +65 -0
  234. package/dist/node_modules/@boombot/core/package.json +9 -0
  235. package/dist/node_modules/@boombot/core/reflection/classifier.d.ts +13 -0
  236. package/dist/node_modules/@boombot/core/reflection/classifier.js +77 -0
  237. package/dist/node_modules/@boombot/core/reflection/index.d.ts +2 -0
  238. package/dist/node_modules/@boombot/core/reflection/index.js +2 -0
  239. package/dist/node_modules/@boombot/core/reflection/types.d.ts +13 -0
  240. package/dist/node_modules/@boombot/core/reflection/types.js +4 -0
  241. package/dist/node_modules/@boombot/core/research/config.d.ts +89 -0
  242. package/dist/node_modules/@boombot/core/research/config.js +206 -0
  243. package/dist/node_modules/@boombot/core/research/crawl.d.ts +36 -0
  244. package/dist/node_modules/@boombot/core/research/crawl.js +179 -0
  245. package/dist/node_modules/@boombot/core/research/factory.d.ts +26 -0
  246. package/dist/node_modules/@boombot/core/research/factory.js +46 -0
  247. package/dist/node_modules/@boombot/core/research/http.d.ts +87 -0
  248. package/dist/node_modules/@boombot/core/research/http.js +250 -0
  249. package/dist/node_modules/@boombot/core/research/index.d.ts +17 -0
  250. package/dist/node_modules/@boombot/core/research/index.js +10 -0
  251. package/dist/node_modules/@boombot/core/research/orchestrator.d.ts +113 -0
  252. package/dist/node_modules/@boombot/core/research/orchestrator.js +252 -0
  253. package/dist/node_modules/@boombot/core/research/parse/docx.d.ts +24 -0
  254. package/dist/node_modules/@boombot/core/research/parse/docx.js +183 -0
  255. package/dist/node_modules/@boombot/core/research/parse/guards.d.ts +89 -0
  256. package/dist/node_modules/@boombot/core/research/parse/guards.js +171 -0
  257. package/dist/node_modules/@boombot/core/research/parse/index.d.ts +68 -0
  258. package/dist/node_modules/@boombot/core/research/parse/index.js +219 -0
  259. package/dist/node_modules/@boombot/core/research/parse/pdf.d.ts +38 -0
  260. package/dist/node_modules/@boombot/core/research/parse/pdf.js +567 -0
  261. package/dist/node_modules/@boombot/core/research/providers/arxiv.d.ts +9 -0
  262. package/dist/node_modules/@boombot/core/research/providers/arxiv.js +72 -0
  263. package/dist/node_modules/@boombot/core/research/providers/brave.d.ts +11 -0
  264. package/dist/node_modules/@boombot/core/research/providers/brave.js +36 -0
  265. package/dist/node_modules/@boombot/core/research/providers/duckduckgo.d.ts +9 -0
  266. package/dist/node_modules/@boombot/core/research/providers/duckduckgo.js +78 -0
  267. package/dist/node_modules/@boombot/core/research/providers/exa.d.ts +11 -0
  268. package/dist/node_modules/@boombot/core/research/providers/exa.js +34 -0
  269. package/dist/node_modules/@boombot/core/research/providers/firecrawl.d.ts +16 -0
  270. package/dist/node_modules/@boombot/core/research/providers/firecrawl.js +47 -0
  271. package/dist/node_modules/@boombot/core/research/providers/index.d.ts +17 -0
  272. package/dist/node_modules/@boombot/core/research/providers/index.js +11 -0
  273. package/dist/node_modules/@boombot/core/research/providers/jina.d.ts +13 -0
  274. package/dist/node_modules/@boombot/core/research/providers/jina.js +114 -0
  275. package/dist/node_modules/@boombot/core/research/providers/openalex.d.ts +14 -0
  276. package/dist/node_modules/@boombot/core/research/providers/openalex.js +66 -0
  277. package/dist/node_modules/@boombot/core/research/providers/semantic-scholar.d.ts +9 -0
  278. package/dist/node_modules/@boombot/core/research/providers/semantic-scholar.js +56 -0
  279. package/dist/node_modules/@boombot/core/research/providers/tavily.d.ts +11 -0
  280. package/dist/node_modules/@boombot/core/research/providers/tavily.js +35 -0
  281. package/dist/node_modules/@boombot/core/research/providers/wikipedia.d.ts +12 -0
  282. package/dist/node_modules/@boombot/core/research/providers/wikipedia.js +47 -0
  283. package/dist/node_modules/@boombot/core/research/tools.d.ts +27 -0
  284. package/dist/node_modules/@boombot/core/research/tools.js +251 -0
  285. package/dist/node_modules/@boombot/core/research/types.d.ts +133 -0
  286. package/dist/node_modules/@boombot/core/research/types.js +8 -0
  287. package/dist/node_modules/@boombot/core/research/wiring.d.ts +25 -0
  288. package/dist/node_modules/@boombot/core/research/wiring.js +51 -0
  289. package/dist/node_modules/@boombot/core/scheduler/cron.d.ts +34 -0
  290. package/dist/node_modules/@boombot/core/scheduler/cron.js +147 -0
  291. package/dist/node_modules/@boombot/core/scheduler/evaluator.d.ts +42 -0
  292. package/dist/node_modules/@boombot/core/scheduler/evaluator.js +148 -0
  293. package/dist/node_modules/@boombot/core/scheduler/index.d.ts +15 -0
  294. package/dist/node_modules/@boombot/core/scheduler/index.js +15 -0
  295. package/dist/node_modules/@boombot/core/scheduler/registry.d.ts +81 -0
  296. package/dist/node_modules/@boombot/core/scheduler/registry.js +263 -0
  297. package/dist/node_modules/@boombot/core/scheduler/runner.d.ts +59 -0
  298. package/dist/node_modules/@boombot/core/scheduler/runner.js +257 -0
  299. package/dist/node_modules/@boombot/core/scheduler/store.d.ts +150 -0
  300. package/dist/node_modules/@boombot/core/scheduler/store.js +526 -0
  301. package/dist/node_modules/@boombot/core/scheduler/timezone.d.ts +35 -0
  302. package/dist/node_modules/@boombot/core/scheduler/timezone.js +96 -0
  303. package/dist/node_modules/@boombot/core/scheduler/types.d.ts +157 -0
  304. package/dist/node_modules/@boombot/core/scheduler/types.js +89 -0
  305. package/dist/node_modules/@boombot/core/task-runner/index.d.ts +7 -0
  306. package/dist/node_modules/@boombot/core/task-runner/index.js +5 -0
  307. package/dist/node_modules/@boombot/core/task-runner/planner.d.ts +37 -0
  308. package/dist/node_modules/@boombot/core/task-runner/planner.js +154 -0
  309. package/dist/node_modules/@boombot/core/task-runner/runner.d.ts +8 -0
  310. package/dist/node_modules/@boombot/core/task-runner/runner.js +564 -0
  311. package/dist/node_modules/@boombot/core/task-runner/store.d.ts +28 -0
  312. package/dist/node_modules/@boombot/core/task-runner/store.js +126 -0
  313. package/dist/node_modules/@boombot/core/task-runner/types.d.ts +138 -0
  314. package/dist/node_modules/@boombot/core/task-runner/types.js +11 -0
  315. package/dist/node_modules/@boombot/core/workflow/dag.d.ts +16 -0
  316. package/dist/node_modules/@boombot/core/workflow/dag.js +109 -0
  317. package/dist/node_modules/@boombot/core/workflow/engine.d.ts +34 -0
  318. package/dist/node_modules/@boombot/core/workflow/engine.js +304 -0
  319. package/dist/node_modules/@boombot/core/workflow/index.d.ts +3 -0
  320. package/dist/node_modules/@boombot/core/workflow/index.js +3 -0
  321. package/dist/node_modules/@boombot/core/workflow/types.d.ts +98 -0
  322. package/dist/node_modules/@boombot/core/workflow/types.js +21 -0
  323. package/dist/node_modules/@boombot/cronjobs/handlers.d.ts +11 -0
  324. package/dist/node_modules/@boombot/cronjobs/handlers.js +174 -0
  325. package/dist/node_modules/@boombot/cronjobs/index.d.ts +2 -0
  326. package/dist/node_modules/@boombot/cronjobs/index.js +2 -0
  327. package/dist/node_modules/@boombot/cronjobs/package.json +9 -0
  328. package/dist/node_modules/@boombot/cronjobs/service.d.ts +53 -0
  329. package/dist/node_modules/@boombot/cronjobs/service.js +200 -0
  330. package/dist/node_modules/@boombot/plugins/index.d.ts +2 -0
  331. package/dist/node_modules/@boombot/plugins/index.js +2 -0
  332. package/dist/node_modules/@boombot/plugins/installer.d.ts +45 -0
  333. package/dist/node_modules/@boombot/plugins/installer.js +487 -0
  334. package/dist/node_modules/@boombot/plugins/manager.d.ts +23 -0
  335. package/dist/node_modules/@boombot/plugins/manager.js +243 -0
  336. package/dist/node_modules/@boombot/plugins/package.json +9 -0
  337. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-channels.d.ts +125 -0
  338. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-channels.js +76 -0
  339. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-plugins.d.ts +220 -0
  340. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-plugins.js +60 -0
  341. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-scheduler.d.ts +33 -0
  342. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands-scheduler.js +25 -0
  343. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands.d.ts +4349 -0
  344. package/dist/node_modules/@boombot/protocol/gateway-protocol/commands.js +563 -0
  345. package/dist/node_modules/@boombot/protocol/gateway-protocol/envelope.d.ts +32 -0
  346. package/dist/node_modules/@boombot/protocol/gateway-protocol/envelope.js +61 -0
  347. package/dist/node_modules/@boombot/protocol/gateway-protocol/errors.d.ts +13 -0
  348. package/dist/node_modules/@boombot/protocol/gateway-protocol/errors.js +16 -0
  349. package/dist/node_modules/@boombot/protocol/gateway-protocol/events.d.ts +567 -0
  350. package/dist/node_modules/@boombot/protocol/gateway-protocol/events.js +395 -0
  351. package/dist/node_modules/@boombot/protocol/gateway-protocol/index.d.ts +20 -0
  352. package/dist/node_modules/@boombot/protocol/gateway-protocol/index.js +13 -0
  353. package/dist/node_modules/@boombot/protocol/gateway-protocol/types.d.ts +91 -0
  354. package/dist/node_modules/@boombot/protocol/gateway-protocol/types.js +7 -0
  355. package/dist/node_modules/@boombot/protocol/index.d.ts +4 -0
  356. package/dist/node_modules/@boombot/protocol/index.js +3 -0
  357. package/dist/node_modules/@boombot/protocol/package.json +9 -0
  358. package/dist/node_modules/@boombot/protocol/plugin-sdk/channel.d.ts +41 -0
  359. package/dist/node_modules/@boombot/protocol/plugin-sdk/channel.js +17 -0
  360. package/dist/node_modules/@boombot/protocol/plugin-sdk/index.d.ts +10 -0
  361. package/dist/node_modules/@boombot/protocol/plugin-sdk/index.js +10 -0
  362. package/dist/node_modules/@boombot/protocol/plugin-sdk/manifest.d.ts +58 -0
  363. package/dist/node_modules/@boombot/protocol/plugin-sdk/manifest.js +116 -0
  364. package/dist/node_modules/@boombot/protocol/plugin-sdk/plugin.d.ts +25 -0
  365. package/dist/node_modules/@boombot/protocol/plugin-sdk/plugin.js +1 -0
  366. package/dist/node_modules/@boombot/protocol/providers/cipher.d.ts +7 -0
  367. package/dist/node_modules/@boombot/protocol/providers/cipher.js +52 -0
  368. package/dist/node_modules/@boombot/protocol/providers/index.d.ts +19 -0
  369. package/dist/node_modules/@boombot/protocol/providers/index.js +19 -0
  370. package/dist/node_modules/@boombot/protocol/providers/key-roulette.d.ts +79 -0
  371. package/dist/node_modules/@boombot/protocol/providers/key-roulette.js +81 -0
  372. package/dist/node_modules/@boombot/protocol/providers/probe.d.ts +29 -0
  373. package/dist/node_modules/@boombot/protocol/providers/probe.js +96 -0
  374. package/dist/node_modules/@boombot/protocol/providers/profiles/anthropic.d.ts +11 -0
  375. package/dist/node_modules/@boombot/protocol/providers/profiles/anthropic.js +11 -0
  376. package/dist/node_modules/@boombot/protocol/providers/profiles/custom.d.ts +22 -0
  377. package/dist/node_modules/@boombot/protocol/providers/profiles/custom.js +222 -0
  378. package/dist/node_modules/@boombot/protocol/providers/profiles/deepseek.d.ts +7 -0
  379. package/dist/node_modules/@boombot/protocol/providers/profiles/deepseek.js +9 -0
  380. package/dist/node_modules/@boombot/protocol/providers/profiles/google.d.ts +10 -0
  381. package/dist/node_modules/@boombot/protocol/providers/profiles/google.js +10 -0
  382. package/dist/node_modules/@boombot/protocol/providers/profiles/index.d.ts +31 -0
  383. package/dist/node_modules/@boombot/protocol/providers/profiles/index.js +56 -0
  384. package/dist/node_modules/@boombot/protocol/providers/profiles/local.d.ts +10 -0
  385. package/dist/node_modules/@boombot/protocol/providers/profiles/local.js +11 -0
  386. package/dist/node_modules/@boombot/protocol/providers/profiles/mistral.d.ts +7 -0
  387. package/dist/node_modules/@boombot/protocol/providers/profiles/mistral.js +9 -0
  388. package/dist/node_modules/@boombot/protocol/providers/profiles/openai-compatible.d.ts +10 -0
  389. package/dist/node_modules/@boombot/protocol/providers/profiles/openai-compatible.js +10 -0
  390. package/dist/node_modules/@boombot/protocol/providers/profiles/openai.d.ts +7 -0
  391. package/dist/node_modules/@boombot/protocol/providers/profiles/openai.js +11 -0
  392. package/dist/node_modules/@boombot/protocol/providers/profiles/types.d.ts +58 -0
  393. package/dist/node_modules/@boombot/protocol/providers/profiles/types.js +1 -0
  394. package/dist/node_modules/@boombot/protocol/providers/profiles/xai.d.ts +7 -0
  395. package/dist/node_modules/@boombot/protocol/providers/profiles/xai.js +10 -0
  396. package/dist/node_modules/@boombot/protocol/providers/request-profile.d.ts +102 -0
  397. package/dist/node_modules/@boombot/protocol/providers/request-profile.js +118 -0
  398. package/dist/node_modules/@boombot/protocol/providers/service.d.ts +2 -0
  399. package/dist/node_modules/@boombot/protocol/providers/service.js +418 -0
  400. package/dist/node_modules/@boombot/protocol/providers/types.d.ts +141 -0
  401. package/dist/node_modules/@boombot/protocol/providers/types.js +16 -0
  402. package/dist/node_modules/@boombot/sandbox/fs-path.d.ts +13 -0
  403. package/dist/node_modules/@boombot/sandbox/fs-path.js +79 -0
  404. package/dist/node_modules/@boombot/sandbox/index.d.ts +13 -0
  405. package/dist/node_modules/@boombot/sandbox/index.js +13 -0
  406. package/dist/node_modules/@boombot/sandbox/native.d.ts +39 -0
  407. package/dist/node_modules/@boombot/sandbox/native.js +224 -0
  408. package/dist/node_modules/@boombot/sandbox/package.json +9 -0
  409. package/dist/node_modules/@boombot/sandbox/sandbox.d.ts +92 -0
  410. package/dist/node_modules/@boombot/sandbox/sandbox.js +229 -0
  411. package/dist/node_modules/@boombot/security/harness-guard/boundary.d.ts +22 -0
  412. package/dist/node_modules/@boombot/security/harness-guard/boundary.js +105 -0
  413. package/dist/node_modules/@boombot/security/harness-guard/gates.d.ts +90 -0
  414. package/dist/node_modules/@boombot/security/harness-guard/gates.js +791 -0
  415. package/dist/node_modules/@boombot/security/harness-guard/index.d.ts +23 -0
  416. package/dist/node_modules/@boombot/security/harness-guard/index.js +23 -0
  417. package/dist/node_modules/@boombot/security/harness-guard/policy.d.ts +20 -0
  418. package/dist/node_modules/@boombot/security/harness-guard/policy.js +70 -0
  419. package/dist/node_modules/@boombot/security/harness-guard/sanitizer.d.ts +53 -0
  420. package/dist/node_modules/@boombot/security/harness-guard/sanitizer.js +145 -0
  421. package/dist/node_modules/@boombot/security/harness-guard/types.d.ts +71 -0
  422. package/dist/node_modules/@boombot/security/harness-guard/types.js +10 -0
  423. package/dist/node_modules/@boombot/security/harness-guard/verifier.d.ts +59 -0
  424. package/dist/node_modules/@boombot/security/harness-guard/verifier.js +169 -0
  425. package/dist/node_modules/@boombot/security/index.d.ts +5 -0
  426. package/dist/node_modules/@boombot/security/index.js +9 -0
  427. package/dist/node_modules/@boombot/security/package.json +9 -0
  428. package/dist/node_modules/@boombot/security/policy/approval.d.ts +117 -0
  429. package/dist/node_modules/@boombot/security/policy/approval.js +223 -0
  430. package/dist/node_modules/@boombot/security/reasoning-guard/claim-extractor.d.ts +10 -0
  431. package/dist/node_modules/@boombot/security/reasoning-guard/claim-extractor.js +124 -0
  432. package/dist/node_modules/@boombot/security/reasoning-guard/evidence-ledger.d.ts +25 -0
  433. package/dist/node_modules/@boombot/security/reasoning-guard/evidence-ledger.js +70 -0
  434. package/dist/node_modules/@boombot/security/reasoning-guard/gate.d.ts +38 -0
  435. package/dist/node_modules/@boombot/security/reasoning-guard/gate.js +61 -0
  436. package/dist/node_modules/@boombot/security/reasoning-guard/grounding-check.d.ts +12 -0
  437. package/dist/node_modules/@boombot/security/reasoning-guard/grounding-check.js +234 -0
  438. package/dist/node_modules/@boombot/security/reasoning-guard/index.d.ts +17 -0
  439. package/dist/node_modules/@boombot/security/reasoning-guard/index.js +13 -0
  440. package/dist/node_modules/@boombot/security/reasoning-guard/policy.d.ts +15 -0
  441. package/dist/node_modules/@boombot/security/reasoning-guard/policy.js +39 -0
  442. package/dist/node_modules/@boombot/security/reasoning-guard/types.d.ts +46 -0
  443. package/dist/node_modules/@boombot/security/reasoning-guard/types.js +7 -0
  444. package/dist/node_modules/@boombot/security/security/approval.d.ts +63 -0
  445. package/dist/node_modules/@boombot/security/security/approval.js +140 -0
  446. package/dist/node_modules/@boombot/security/security/audit.d.ts +39 -0
  447. package/dist/node_modules/@boombot/security/security/audit.js +70 -0
  448. package/dist/node_modules/@boombot/security/security/index.d.ts +12 -0
  449. package/dist/node_modules/@boombot/security/security/index.js +12 -0
  450. package/dist/node_modules/@boombot/security/security/trust.d.ts +79 -0
  451. package/dist/node_modules/@boombot/security/security/trust.js +87 -0
  452. package/dist/package.json +5 -0
  453. package/dist/proto/agent_core.proto +56 -0
  454. package/dist/python/agent-core-python/pyproject.toml +40 -0
  455. package/dist/python/agent-core-python/src/boombot_agent/__init__.py +5 -0
  456. package/dist/python/agent-core-python/src/boombot_agent/generated/__init__.py +0 -0
  457. package/dist/python/agent-core-python/src/boombot_agent/generated/agent_core_pb2.py +54 -0
  458. package/dist/python/agent-core-python/src/boombot_agent/generated/agent_core_pb2_grpc.py +140 -0
  459. package/dist/python/agent-core-python/src/boombot_agent/rpc.py +265 -0
  460. package/dist/python/agent-core-python/src/boombot_agent/turn.py +577 -0
  461. package/dist/python/agent-core-python/src/boombot_ml/__init__.py +12 -0
  462. package/dist/python/agent-core-python/src/boombot_ml/embeddings.py +58 -0
  463. package/dist/python/agent-core-python/src/boombot_ml/training.py +156 -0
  464. package/dist/python/agent-core-python/uv.lock +2856 -0
  465. package/dist/web/assets/ChatView-BZrSD-64.js +262 -0
  466. package/dist/web/assets/ChatView-Cahbjez6.css +1 -0
  467. package/dist/web/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  468. package/dist/web/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  469. package/dist/web/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  470. package/dist/web/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  471. package/dist/web/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  472. package/dist/web/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  473. package/dist/web/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  474. package/dist/web/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  475. package/dist/web/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  476. package/dist/web/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  477. package/dist/web/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  478. package/dist/web/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  479. package/dist/web/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  480. package/dist/web/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  481. package/dist/web/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  482. package/dist/web/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  483. package/dist/web/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  484. package/dist/web/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  485. package/dist/web/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  486. package/dist/web/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  487. package/dist/web/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  488. package/dist/web/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  489. package/dist/web/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  490. package/dist/web/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  491. package/dist/web/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  492. package/dist/web/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  493. package/dist/web/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  494. package/dist/web/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  495. package/dist/web/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  496. package/dist/web/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  497. package/dist/web/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  498. package/dist/web/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  499. package/dist/web/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  500. package/dist/web/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  501. package/dist/web/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  502. package/dist/web/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  503. package/dist/web/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  504. package/dist/web/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  505. package/dist/web/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  506. package/dist/web/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  507. package/dist/web/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  508. package/dist/web/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  509. package/dist/web/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  510. package/dist/web/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  511. package/dist/web/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  512. package/dist/web/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  513. package/dist/web/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  514. package/dist/web/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  515. package/dist/web/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  516. package/dist/web/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  517. package/dist/web/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  518. package/dist/web/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  519. package/dist/web/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  520. package/dist/web/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  521. package/dist/web/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  522. package/dist/web/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  523. package/dist/web/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  524. package/dist/web/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  525. package/dist/web/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  526. package/dist/web/assets/MemoryView-BJbXqqs7.css +1 -0
  527. package/dist/web/assets/MemoryView-BfMq_6CD.js +1 -0
  528. package/dist/web/assets/SchedulesView-B22QmPR-.js +1 -0
  529. package/dist/web/assets/SchedulesView-C6XK7A8a.css +1 -0
  530. package/dist/web/assets/SessionsView-C1dqdhxI.css +1 -0
  531. package/dist/web/assets/SessionsView-DS0-aH2a.js +1 -0
  532. package/dist/web/assets/TasksView-Dtasg4hd.css +1 -0
  533. package/dist/web/assets/TasksView-ey0FLsW8.js +2 -0
  534. package/dist/web/assets/Textarea-DN3Zybl1.js +1 -0
  535. package/dist/web/assets/Tooltip-Bn2cqFPz.css +1 -0
  536. package/dist/web/assets/Tooltip-MzV2Gqnc.js +1 -0
  537. package/dist/web/assets/browser-Bnt73-Ub.js +8 -0
  538. package/dist/web/assets/index-BL8bi7Yt.css +1 -0
  539. package/dist/web/assets/index-CuPR73Tn.js +73 -0
  540. package/dist/web/favicon.svg +4 -0
  541. package/dist/web/index.html +26 -0
  542. package/dist/web/logo.jpg +0 -0
  543. package/dist/web/logo.png +0 -0
  544. package/package.json +51 -4
@@ -0,0 +1,318 @@
1
+ /**
2
+ * @boombot/core — LLM memory consolidation as a background job.
3
+ *
4
+ * Lesson from Hermes (and ChatGPT "Dreaming" / Mem0 research): consolidation
5
+ * — extracting durable facts from past conversations into long-term memory —
6
+ * must run as a background job with its own cursor, NEVER inside the agent
7
+ * turn loop. The loop is latency-sensitive and every extraction costs an LLM
8
+ * call; a per-turn hook would burn calls on small talk and touch the hot
9
+ * path. The host drives `runConsolidation` (or `ConsolidationJob`) on a
10
+ * timer, far away from turns.
11
+ *
12
+ * Design (non-destructive, Mem0-style):
13
+ * - Extraction is per conversation, via an injected LLM. The core never
14
+ * imports agent-worker: the model arrives as `ConsolidationLlm`.
15
+ * - Writes are append-only through `MemoryProvider.save()` with
16
+ * `provenance.source = 'model'`. There is no update path and no atomic
17
+ * swap — superseding, if ever needed, is a host concern.
18
+ * - Deduplication is rule-based (normalized exact match, Mem0's NOOP):
19
+ * no extra LLM call is spent deciding whether a fact already exists.
20
+ * - A relevance pre-filter skips conversations shorter than
21
+ * `budget.minConversationChars` before any LLM call (AgentX-style).
22
+ * - Progress is a watermark cursor under `CONSOLIDATION_STATE_ID`, advanced
23
+ * only past fully-processed conversations, so a crash or restart never
24
+ * loses work and never reprocesses.
25
+ * - Four hard budget axes (conversations / input chars / LLM calls /
26
+ * wall-clock) stop the run cleanly with a reason. The job never throws
27
+ * for LLM or budget reasons — it reports.
28
+ *
29
+ * Pure with respect to the agent loop: this module never sees a turn.
30
+ */
31
+ import { DEFAULT_CONSOLIDATION_BUDGET, ConsolidationBudgetTracker, } from './budget.js';
32
+ /**
33
+ * Sentinel memory id under which consolidation run state (the watermark
34
+ * cursor) is kept. Mirrors the host's `memory:curation` convention: the
35
+ * state lives in the store's audit trail, not in a separate KV table.
36
+ */
37
+ export const CONSOLIDATION_STATE_ID = 'memory:consolidation';
38
+ /** Actor id recorded in provenance for consolidation-written records. */
39
+ export const CONSOLIDATION_ACTOR_ID = 'memory-consolidation';
40
+ /** Upper bound on one extracted fact (chars). Extraction output is capped. */
41
+ const MAX_FACT_CHARS = 280;
42
+ /** Sanity cap on facts accepted from a single conversation. */
43
+ const MAX_FACTS_PER_CONVERSATION = 50;
44
+ /** Hard cap on raw extraction output accumulated from a streaming LLM. */
45
+ const MAX_EXTRACTION_CHARS = 32_768;
46
+ /**
47
+ * Build the extraction prompt for one conversation transcript. Exported so
48
+ * hosts (and tests) can see the exact contract, including the no-secrets
49
+ * guardrail: consolidation reads raw transcripts, so the extraction prompt
50
+ * must explicitly forbid persisting credentials.
51
+ */
52
+ export function buildExtractionPrompt(transcript) {
53
+ return [
54
+ 'You extract durable facts about the user from a conversation transcript.',
55
+ 'Durable means still true and useful in a future session: preferences,',
56
+ 'constraints, stable personal context, long-running projects.',
57
+ 'Skip greetings, small talk, one-off questions, and anything already obvious.',
58
+ 'SECURITY RULE: never extract secrets — API keys, tokens, passwords,',
59
+ 'credentials, private keys, session cookies. If a candidate fact contains',
60
+ 'one, drop it silently.',
61
+ 'Output ONLY a JSON array of objects with a "text" field, e.g.',
62
+ '[{"text": "User prefers dark mode in every app."}].',
63
+ 'No prose, no markdown fences, no commentary. One sentence per fact,',
64
+ 'max 280 characters each.',
65
+ '',
66
+ 'Transcript:',
67
+ transcript,
68
+ ].join('\n');
69
+ }
70
+ /**
71
+ * Parse the model's extraction output into fact strings. Tolerant: accepts
72
+ * a bare array, objects with a "text" field, or plain strings; extracts the
73
+ * first `[...]` block when the model adds prose anyway. Unparseable output
74
+ * yields an empty list (the conversation is skipped, never fatal).
75
+ */
76
+ export function parseExtractedFacts(raw) {
77
+ const text = raw.trim();
78
+ if (text.length === 0)
79
+ return [];
80
+ let parsed = undefined;
81
+ try {
82
+ parsed = JSON.parse(text);
83
+ }
84
+ catch {
85
+ const start = text.indexOf('[');
86
+ const end = text.lastIndexOf(']');
87
+ if (start !== -1 && end > start) {
88
+ try {
89
+ parsed = JSON.parse(text.slice(start, end + 1));
90
+ }
91
+ catch {
92
+ return [];
93
+ }
94
+ }
95
+ else {
96
+ return [];
97
+ }
98
+ }
99
+ if (!Array.isArray(parsed))
100
+ return [];
101
+ const facts = [];
102
+ const seen = new Set();
103
+ for (const item of parsed.slice(0, MAX_FACTS_PER_CONVERSATION)) {
104
+ let fact;
105
+ if (typeof item === 'string') {
106
+ fact = item;
107
+ }
108
+ else if (item !== null && typeof item === 'object' && 'text' in item) {
109
+ const t = item.text;
110
+ if (typeof t === 'string')
111
+ fact = t;
112
+ }
113
+ if (fact === undefined)
114
+ continue;
115
+ const normalized = fact.trim().slice(0, MAX_FACT_CHARS);
116
+ if (normalized.length === 0 || seen.has(normalized))
117
+ continue;
118
+ seen.add(normalized);
119
+ facts.push(normalized);
120
+ }
121
+ return facts;
122
+ }
123
+ function normalizeForDedupe(text) {
124
+ return text.toLowerCase().replace(/\s+/g, ' ').trim();
125
+ }
126
+ /**
127
+ * Rule-based exact-match dedupe (Mem0's NOOP without the extra LLM call).
128
+ * Loads the scope's live records once per run, then answers from the cache;
129
+ * newly written facts are registered so a run never duplicates itself. When
130
+ * the provider has no `list`, falls back to a best-effort FTS probe per fact.
131
+ */
132
+ class DedupeIndex {
133
+ store;
134
+ cache = new Map();
135
+ constructor(store) {
136
+ this.store = store;
137
+ }
138
+ scopeKey(scope) {
139
+ return `${scope.kind}\n${scope.id}`;
140
+ }
141
+ load(scope) {
142
+ const key = this.scopeKey(scope);
143
+ let set = this.cache.get(key);
144
+ if (set === undefined) {
145
+ set = new Set();
146
+ try {
147
+ for (const record of this.store.list?.(scope, 5000) ?? []) {
148
+ set.add(normalizeForDedupe(record.text));
149
+ }
150
+ }
151
+ catch {
152
+ // Transient failure: per-fact FTS fallback below still applies when
153
+ // the provider has no list; otherwise we accept best-effort.
154
+ }
155
+ this.cache.set(key, set);
156
+ }
157
+ return set;
158
+ }
159
+ isDuplicate(scope, text) {
160
+ const norm = normalizeForDedupe(text);
161
+ if (norm === '')
162
+ return true;
163
+ const set = this.load(scope);
164
+ if (set.has(norm))
165
+ return true;
166
+ if (this.store.list === undefined) {
167
+ try {
168
+ for (const hit of this.store.search(text.slice(0, 200), 50)) {
169
+ const hitNorm = normalizeForDedupe(hit.record.text);
170
+ set.add(hitNorm);
171
+ if (hitNorm === norm)
172
+ return true;
173
+ }
174
+ }
175
+ catch {
176
+ // Best effort only — a failed probe must not block the write.
177
+ }
178
+ }
179
+ return false;
180
+ }
181
+ markStored(scope, text) {
182
+ this.load(scope).add(normalizeForDedupe(text));
183
+ }
184
+ }
185
+ /**
186
+ * Run one consolidation pass: read the watermark, extract durable facts from
187
+ * each new conversation via the injected LLM, and append them to memory.
188
+ *
189
+ * Never throws for LLM or budget reasons — failures are reported in
190
+ * `stopReason`. The watermark advances only past fully-processed
191
+ * conversations, so an LLM failure leaves the failed conversation for the
192
+ * next run while keeping everything already written.
193
+ */
194
+ export async function runConsolidation(deps) {
195
+ const budget = deps.budget ?? DEFAULT_CONSOLIDATION_BUDGET;
196
+ const tracker = new ConsolidationBudgetTracker(budget);
197
+ const dedupe = new DedupeIndex(deps.store);
198
+ let processed = 0;
199
+ let factsCreated = 0;
200
+ let skippedShort = 0;
201
+ let skippedDuplicate = 0;
202
+ let stopReason = 'completed';
203
+ const watermark = deps.watermarks.get(CONSOLIDATION_STATE_ID);
204
+ let cursor = watermark;
205
+ const advance = (endedAt) => {
206
+ if (cursor === null || endedAt > cursor)
207
+ cursor = endedAt;
208
+ };
209
+ const conversations = await deps.conversations.listEndedAfter(watermark, budget.maxConversationsPerRun);
210
+ for (const conv of conversations) {
211
+ const convStop = tracker.admitConversation(conv.transcript.length);
212
+ if (convStop !== null) {
213
+ stopReason = convStop;
214
+ break;
215
+ }
216
+ if (tracker.isBelowMinChars(conv.transcript.length)) {
217
+ skippedShort += 1;
218
+ processed += 1;
219
+ advance(conv.endedAt);
220
+ continue;
221
+ }
222
+ const llmStop = tracker.admitLlmCall();
223
+ if (llmStop !== null) {
224
+ stopReason = llmStop;
225
+ break;
226
+ }
227
+ let raw;
228
+ try {
229
+ raw = await deps.llm.completeText(buildExtractionPrompt(conv.transcript), {
230
+ maxTokens: 1024,
231
+ });
232
+ }
233
+ catch {
234
+ // Keep the watermark at the last fully-processed conversation: the
235
+ // failed one retries on the next run. Already-written facts stay.
236
+ stopReason = 'llm-error';
237
+ break;
238
+ }
239
+ for (const fact of parseExtractedFacts(raw.slice(0, MAX_EXTRACTION_CHARS))) {
240
+ if (dedupe.isDuplicate(conv.scope, fact)) {
241
+ skippedDuplicate += 1;
242
+ continue;
243
+ }
244
+ deps.store.save({
245
+ scope: conv.scope,
246
+ text: fact,
247
+ provenance: {
248
+ source: 'model',
249
+ reference: conv.id,
250
+ actorId: CONSOLIDATION_ACTOR_ID,
251
+ },
252
+ });
253
+ dedupe.markStored(conv.scope, fact);
254
+ factsCreated += 1;
255
+ }
256
+ processed += 1;
257
+ advance(conv.endedAt);
258
+ }
259
+ if (cursor !== null && cursor !== watermark) {
260
+ deps.watermarks.set(CONSOLIDATION_STATE_ID, cursor);
261
+ }
262
+ return {
263
+ at: new Date().toISOString(),
264
+ processed,
265
+ factsCreated,
266
+ skippedShort,
267
+ skippedDuplicate,
268
+ stopReason,
269
+ usage: tracker.usage,
270
+ };
271
+ }
272
+ const DEFAULT_JOB_INTERVAL_MS = 1_800_000; // 30 min
273
+ /**
274
+ * Background job that runs consolidation on a timer. Start it once at host
275
+ * boot; it never touches the agent loop. `runOnce()` exists for tests and
276
+ * for hosts that drive scheduling themselves (e.g. cron). Mirrors
277
+ * `CurationJob`, except the run is async (the LLM is remote).
278
+ */
279
+ export class ConsolidationJob {
280
+ options;
281
+ timer;
282
+ running = false;
283
+ constructor(options) {
284
+ this.options = options;
285
+ }
286
+ start() {
287
+ if (this.running)
288
+ return;
289
+ this.running = true;
290
+ const intervalMs = this.options.intervalMs ?? DEFAULT_JOB_INTERVAL_MS;
291
+ this.timer = setInterval(() => {
292
+ this.runOnce().catch((error) => {
293
+ this.options.onError?.(error);
294
+ });
295
+ }, intervalMs);
296
+ if (typeof this.timer.unref === 'function')
297
+ this.timer.unref();
298
+ }
299
+ stop() {
300
+ this.running = false;
301
+ if (this.timer !== undefined) {
302
+ clearInterval(this.timer);
303
+ this.timer = undefined;
304
+ }
305
+ }
306
+ get isRunning() {
307
+ return this.running;
308
+ }
309
+ runOnce() {
310
+ return runConsolidation({
311
+ llm: this.options.llm,
312
+ store: this.options.store,
313
+ conversations: this.options.conversations,
314
+ watermarks: this.options.watermarks,
315
+ budget: this.options.budget,
316
+ });
317
+ }
318
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @boombot/core — LLM memory consolidation (background job).
3
+ *
4
+ * Public surface:
5
+ * - `runConsolidation` / `ConsolidationJob`: the pass and its timer.
6
+ * - `buildExtractionPrompt` / `parseExtractedFacts`: the LLM contract.
7
+ * - `CONSOLIDATION_STATE_ID`: the watermark key (audit-trail convention).
8
+ * - budget: `ConsolidationBudget`, `DEFAULT_CONSOLIDATION_BUDGET`,
9
+ * `ConsolidationBudgetTracker`.
10
+ */
11
+ export { CONSOLIDATION_STATE_ID, CONSOLIDATION_ACTOR_ID, ConsolidationJob, buildExtractionPrompt, parseExtractedFacts, runConsolidation, type ConsolidationJobOptions, type ConsolidationLlm, type ConsolidationReport, type ConversationSource, type EndedConversation, type RunConsolidationDeps, type WatermarkStore, } from './consolidator.js';
12
+ export { DEFAULT_CONSOLIDATION_BUDGET, ConsolidationBudgetTracker, type BudgetStopReason, type BudgetUsage, type ConsolidationBudget, } from './budget.js';
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @boombot/core — LLM memory consolidation (background job).
3
+ *
4
+ * Public surface:
5
+ * - `runConsolidation` / `ConsolidationJob`: the pass and its timer.
6
+ * - `buildExtractionPrompt` / `parseExtractedFacts`: the LLM contract.
7
+ * - `CONSOLIDATION_STATE_ID`: the watermark key (audit-trail convention).
8
+ * - budget: `ConsolidationBudget`, `DEFAULT_CONSOLIDATION_BUDGET`,
9
+ * `ConsolidationBudgetTracker`.
10
+ */
11
+ export { CONSOLIDATION_STATE_ID, CONSOLIDATION_ACTOR_ID, ConsolidationJob, buildExtractionPrompt, parseExtractedFacts, runConsolidation, } from './consolidator.js';
12
+ export { DEFAULT_CONSOLIDATION_BUDGET, ConsolidationBudgetTracker, } from './budget.js';
@@ -0,0 +1,86 @@
1
+ /**
2
+ * @boombot/core — memory curation as a background job.
3
+ *
4
+ * Lesson from Hermes/OpenClaw analysis (#6): curation (pin/archive/
5
+ * consolidate) must run as a background job with its own state — NEVER
6
+ * inside the agent turn loop. This module defines the curator interface,
7
+ * one honest TTL-based implementation, and the `CurationJob` that
8
+ * schedules it on a timer outside the loop.
9
+ */
10
+ import { type MemoryProvider } from './provider.js';
11
+ import { type MemoryScope } from './types.js';
12
+ /** What the curator decided for one record. */
13
+ export type CurationDecision = {
14
+ kind: 'keep';
15
+ id: string;
16
+ scope: MemoryScope;
17
+ } | {
18
+ kind: 'archive';
19
+ id: string;
20
+ scope: MemoryScope;
21
+ reason: string;
22
+ } | {
23
+ kind: 'skip';
24
+ id: string;
25
+ scope: MemoryScope;
26
+ reason: string;
27
+ };
28
+ export interface CurationPolicy {
29
+ /** Archive records older than this (days). Must be >= 1. */
30
+ maxAgeDays: number;
31
+ /** Max records scanned per scope per run. Default 500. */
32
+ batchSize?: number;
33
+ /** When true, report decisions without archiving anything. */
34
+ dryRun?: boolean;
35
+ /** Scopes to curate. Defaults to the provider's principal + global scope. */
36
+ scopes?: readonly MemoryScope[];
37
+ }
38
+ export interface CurationReport {
39
+ at: string;
40
+ dryRun: boolean;
41
+ scanned: number;
42
+ kept: number;
43
+ archived: string[];
44
+ skipped: number;
45
+ decisions: readonly CurationDecision[];
46
+ }
47
+ /**
48
+ * A curation strategy. Implementations decide pin/archive/keep per
49
+ * record; the job only drives scheduling. Strategies must be pure with
50
+ * respect to the agent loop — they never see a turn.
51
+ */
52
+ export interface MemoryCurator {
53
+ readonly name: string;
54
+ curate(provider: MemoryProvider, policy: CurationPolicy): CurationReport;
55
+ }
56
+ /**
57
+ * TTL curator: archives records older than `maxAgeDays` that are not
58
+ * pinned. Archiving = tombstone (revoke), never hard delete — the audit
59
+ * trail survives and the record can be restored by re-saving.
60
+ */
61
+ export declare class TtlMemoryCurator implements MemoryCurator {
62
+ readonly name = "ttl";
63
+ curate(provider: MemoryProvider, policy: CurationPolicy): CurationReport;
64
+ }
65
+ export interface CurationJobOptions {
66
+ curator: MemoryCurator;
67
+ provider: MemoryProvider;
68
+ policy: CurationPolicy;
69
+ /** Interval between runs in ms. Default 1 hour. */
70
+ intervalMs?: number;
71
+ }
72
+ /**
73
+ * Background job that runs a `MemoryCurator` on a timer. Start it once at
74
+ * host boot; it never touches the agent loop. `runOnce()` exists for
75
+ * tests and for hosts that drive scheduling themselves (e.g. cron).
76
+ */
77
+ export declare class CurationJob {
78
+ private readonly options;
79
+ private timer;
80
+ private running;
81
+ constructor(options: CurationJobOptions);
82
+ start(): void;
83
+ stop(): void;
84
+ get isRunning(): boolean;
85
+ runOnce(): CurationReport;
86
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * @boombot/core — memory curation as a background job.
3
+ *
4
+ * Lesson from Hermes/OpenClaw analysis (#6): curation (pin/archive/
5
+ * consolidate) must run as a background job with its own state — NEVER
6
+ * inside the agent turn loop. This module defines the curator interface,
7
+ * one honest TTL-based implementation, and the `CurationJob` that
8
+ * schedules it on a timer outside the loop.
9
+ */
10
+ import {} from './provider.js';
11
+ import {} from './types.js';
12
+ function toScope(scope) {
13
+ return { kind: scope.kind, id: scope.id };
14
+ }
15
+ function ageDays(createdAt, nowMs) {
16
+ const created = Date.parse(createdAt);
17
+ if (Number.isNaN(created))
18
+ return 0;
19
+ return (nowMs - created) / 86_400_000;
20
+ }
21
+ /**
22
+ * TTL curator: archives records older than `maxAgeDays` that are not
23
+ * pinned. Archiving = tombstone (revoke), never hard delete — the audit
24
+ * trail survives and the record can be restored by re-saving.
25
+ */
26
+ export class TtlMemoryCurator {
27
+ name = 'ttl';
28
+ curate(provider, policy) {
29
+ if (!Number.isFinite(policy.maxAgeDays) || policy.maxAgeDays < 1) {
30
+ throw new Error('TtlMemoryCurator: maxAgeDays must be >= 1');
31
+ }
32
+ const batchSize = Math.min(Math.max(Math.floor(policy.batchSize ?? 500), 1), 10_000);
33
+ const dryRun = policy.dryRun === true;
34
+ const nowMs = Date.now();
35
+ const decisions = [];
36
+ let scanned = 0;
37
+ let skipped = 0;
38
+ const scopes = policy.scopes;
39
+ if (scopes === undefined || scopes.length === 0) {
40
+ throw new Error('TtlMemoryCurator: policy.scopes must be non-empty');
41
+ }
42
+ for (const scope of scopes) {
43
+ const storeScope = toScope(scope);
44
+ const records = provider.list?.(storeScope, batchSize) ?? [];
45
+ for (const record of records) {
46
+ scanned += 1;
47
+ if (provider.isPinned?.(record.id, storeScope) === true) {
48
+ skipped += 1;
49
+ decisions.push({ kind: 'skip', id: record.id, scope: storeScope, reason: 'pinned' });
50
+ continue;
51
+ }
52
+ if (ageDays(record.createdAt, nowMs) < policy.maxAgeDays) {
53
+ decisions.push({ kind: 'keep', id: record.id, scope: storeScope });
54
+ continue;
55
+ }
56
+ const reason = `ttl: older than ${policy.maxAgeDays}d`;
57
+ if (dryRun) {
58
+ decisions.push({ kind: 'archive', id: record.id, scope: storeScope, reason: `${reason} (dry-run)` });
59
+ }
60
+ else if (provider.archive !== undefined) {
61
+ provider.archive(record.id, storeScope, reason);
62
+ decisions.push({ kind: 'archive', id: record.id, scope: storeScope, reason });
63
+ }
64
+ else {
65
+ skipped += 1;
66
+ decisions.push({ kind: 'skip', id: record.id, scope: storeScope, reason: 'provider has no archive' });
67
+ }
68
+ }
69
+ }
70
+ const archived = decisions
71
+ .filter((d) => d.kind === 'archive' && !dryRun)
72
+ .map((d) => d.id);
73
+ return {
74
+ at: new Date(nowMs).toISOString(),
75
+ dryRun,
76
+ scanned,
77
+ kept: decisions.filter((d) => d.kind === 'keep').length,
78
+ archived,
79
+ skipped,
80
+ decisions,
81
+ };
82
+ }
83
+ }
84
+ /**
85
+ * Background job that runs a `MemoryCurator` on a timer. Start it once at
86
+ * host boot; it never touches the agent loop. `runOnce()` exists for
87
+ * tests and for hosts that drive scheduling themselves (e.g. cron).
88
+ */
89
+ export class CurationJob {
90
+ options;
91
+ timer;
92
+ running = false;
93
+ constructor(options) {
94
+ this.options = options;
95
+ }
96
+ start() {
97
+ if (this.running)
98
+ return;
99
+ this.running = true;
100
+ const intervalMs = this.options.intervalMs ?? 3_600_000;
101
+ this.timer = setInterval(() => {
102
+ this.runOnce();
103
+ }, intervalMs);
104
+ if (typeof this.timer.unref === 'function')
105
+ this.timer.unref();
106
+ }
107
+ stop() {
108
+ this.running = false;
109
+ if (this.timer !== undefined) {
110
+ clearInterval(this.timer);
111
+ this.timer = undefined;
112
+ }
113
+ }
114
+ get isRunning() {
115
+ return this.running;
116
+ }
117
+ runOnce() {
118
+ return this.options.curator.curate(this.options.provider, this.options.policy);
119
+ }
120
+ }
@@ -0,0 +1,12 @@
1
+ /** Typed errors for the memory-fts module. */
2
+ export type MemoryFtsErrorCode = 'MEMORY_FTS_VALIDATION' | 'MEMORY_FTS_NOT_FOUND' | 'MEMORY_FTS_FORBIDDEN' | 'MEMORY_FTS_CONFLICT' | 'MEMORY_FTS_STORAGE';
3
+ export declare class MemoryFtsError extends Error {
4
+ readonly code: MemoryFtsErrorCode;
5
+ /** Never includes record content or raw scope ids of other principals. */
6
+ constructor(code: MemoryFtsErrorCode, message: string, options?: {
7
+ cause?: unknown;
8
+ });
9
+ }
10
+ export declare function validationError(message: string): MemoryFtsError;
11
+ export declare function notFoundError(id: string): MemoryFtsError;
12
+ export declare function forbiddenError(message: string): MemoryFtsError;
@@ -0,0 +1,20 @@
1
+ /** Typed errors for the memory-fts module. */
2
+ export class MemoryFtsError extends Error {
3
+ code;
4
+ /** Never includes record content or raw scope ids of other principals. */
5
+ constructor(code, message, options) {
6
+ super(message, options);
7
+ this.name = 'MemoryFtsError';
8
+ this.code = code;
9
+ }
10
+ }
11
+ export function validationError(message) {
12
+ return new MemoryFtsError('MEMORY_FTS_VALIDATION', message);
13
+ }
14
+ export function notFoundError(id) {
15
+ // Deliberately vague: existence of another principal's record must not leak.
16
+ return new MemoryFtsError('MEMORY_FTS_NOT_FOUND', `memory record '${id}' not found in the requested scope`);
17
+ }
18
+ export function forbiddenError(message) {
19
+ return new MemoryFtsError('MEMORY_FTS_FORBIDDEN', message);
20
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Hybrid Search Fusion (Reciprocal Rank Fusion - RRF).
3
+ *
4
+ * Merges lexical (BM25 FTS) and semantic (Vector Cosine) ranking lists
5
+ * into a single unified relevance score.
6
+ */
7
+ export interface RankedItem<T = unknown> {
8
+ readonly id: string;
9
+ readonly item: T;
10
+ readonly score: number;
11
+ }
12
+ export interface HybridScoreResult<T = unknown> {
13
+ readonly id: string;
14
+ readonly item: T;
15
+ readonly rrfScore: number;
16
+ readonly lexicalRank?: number;
17
+ readonly semanticRank?: number;
18
+ }
19
+ /** Default RRF smoothing constant (standard IR benchmark value: 60). */
20
+ export declare const DEFAULT_RRF_K = 60;
21
+ /**
22
+ * Merges two ranked lists using Reciprocal Rank Fusion.
23
+ *
24
+ * Formula: RRF_score(d) = 1 / (k + rank_lexical(d)) + 1 / (k + rank_semantic(d))
25
+ */
26
+ export declare function reciprocalRankFusion<T>(lexicalList: readonly RankedItem<T>[], semanticList: readonly RankedItem<T>[], options?: {
27
+ k?: number;
28
+ limit?: number;
29
+ }): HybridScoreResult<T>[];
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Hybrid Search Fusion (Reciprocal Rank Fusion - RRF).
3
+ *
4
+ * Merges lexical (BM25 FTS) and semantic (Vector Cosine) ranking lists
5
+ * into a single unified relevance score.
6
+ */
7
+ /** Default RRF smoothing constant (standard IR benchmark value: 60). */
8
+ export const DEFAULT_RRF_K = 60;
9
+ /**
10
+ * Merges two ranked lists using Reciprocal Rank Fusion.
11
+ *
12
+ * Formula: RRF_score(d) = 1 / (k + rank_lexical(d)) + 1 / (k + rank_semantic(d))
13
+ */
14
+ export function reciprocalRankFusion(lexicalList, semanticList, options = {}) {
15
+ const k = options.k ?? DEFAULT_RRF_K;
16
+ const limit = options.limit ?? 20;
17
+ const itemMap = new Map();
18
+ const lexicalRanks = new Map();
19
+ const semanticRanks = new Map();
20
+ const combinedScores = new Map();
21
+ // Process lexical rankings (1-based rank)
22
+ for (let i = 0; i < lexicalList.length; i++) {
23
+ const entry = lexicalList[i];
24
+ const rank = i + 1;
25
+ itemMap.set(entry.id, entry.item);
26
+ lexicalRanks.set(entry.id, rank);
27
+ const score = 1 / (k + rank);
28
+ combinedScores.set(entry.id, (combinedScores.get(entry.id) ?? 0) + score);
29
+ }
30
+ // Process semantic rankings (1-based rank)
31
+ for (let i = 0; i < semanticList.length; i++) {
32
+ const entry = semanticList[i];
33
+ const rank = i + 1;
34
+ itemMap.set(entry.id, entry.item);
35
+ semanticRanks.set(entry.id, rank);
36
+ const score = 1 / (k + rank);
37
+ combinedScores.set(entry.id, (combinedScores.get(entry.id) ?? 0) + score);
38
+ }
39
+ const results = [];
40
+ for (const [id, rrfScore] of combinedScores.entries()) {
41
+ results.push({
42
+ id,
43
+ item: itemMap.get(id),
44
+ rrfScore,
45
+ lexicalRank: lexicalRanks.get(id),
46
+ semanticRank: semanticRanks.get(id),
47
+ });
48
+ }
49
+ // Sort descending by RRF score
50
+ results.sort((a, b) => b.rrfScore - a.rrfScore);
51
+ return results.slice(0, limit);
52
+ }