@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,118 @@
1
+ import { getProviderProfile, listProviderProfiles } from './profiles/index.js';
2
+ /**
3
+ * Tier-1 default profile for a provider kind, read from the provider's
4
+ * declarative profile (e.g. Anthropic rejects requests without `max_tokens`,
5
+ * so its default lives in the profile, not as a magic constant).
6
+ * Unknown kinds get `{}` (fail-open: the caller validates the kind).
7
+ */
8
+ export function kindDefaultProfile(kind) {
9
+ return getProviderProfile(kind)?.defaultProfile ?? {};
10
+ }
11
+ /**
12
+ * All tier-1 defaults, keyed by provider name. Built lazily from the
13
+ * registry so third-party `registerProvider()` calls are honored.
14
+ */
15
+ export function allKindDefaultProfiles() {
16
+ const out = {};
17
+ for (const profile of listProviderProfiles()) {
18
+ out[profile.name] = profile.defaultProfile ?? {};
19
+ }
20
+ return out;
21
+ }
22
+ function isPlainRecord(value) {
23
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
24
+ return false;
25
+ const proto = Object.getPrototypeOf(value);
26
+ return proto === Object.prototype || proto === null;
27
+ }
28
+ /**
29
+ * Recursively merge two JSON-ish records. Plain objects merge key-by-key to
30
+ * any depth; arrays, scalars and class instances are replaced wholesale;
31
+ * `undefined` values never overwrite. Shared by profile merging and by the
32
+ * request builder applying `extraBody` onto the wire body.
33
+ */
34
+ export function deepMergeRecords(base, override) {
35
+ const out = { ...base };
36
+ for (const [key, value] of Object.entries(override)) {
37
+ if (value === undefined)
38
+ continue;
39
+ const prev = out[key];
40
+ out[key] = isPlainRecord(prev) && isPlainRecord(value) ? deepMergeRecords(prev, value) : value;
41
+ }
42
+ return out;
43
+ }
44
+ /** Copy only defined values from `override` onto a shallow clone of `base`. */
45
+ function mergeDefined(base, override) {
46
+ const out = { ...(base ?? {}) };
47
+ for (const [key, value] of Object.entries(override)) {
48
+ if (value !== undefined)
49
+ out[key] = value;
50
+ }
51
+ return out;
52
+ }
53
+ /**
54
+ * Deep-merge request profiles left-to-right: later profiles win.
55
+ * See the module docstring for the exact merge rules.
56
+ */
57
+ export function mergeRequestProfiles(...profiles) {
58
+ let out = {};
59
+ for (const profile of profiles) {
60
+ if (profile === undefined)
61
+ continue;
62
+ if (profile.maxTokens !== undefined)
63
+ out = { ...out, maxTokens: profile.maxTokens };
64
+ if (profile.temperature !== undefined)
65
+ out = { ...out, temperature: profile.temperature };
66
+ if (profile.topP !== undefined)
67
+ out = { ...out, topP: profile.topP };
68
+ if (profile.headers !== undefined) {
69
+ out = { ...out, headers: mergeDefined(out.headers, { ...profile.headers }) };
70
+ }
71
+ if (profile.extraBody !== undefined) {
72
+ out = { ...out, extraBody: deepMergeRecords(out.extraBody ?? {}, profile.extraBody) };
73
+ }
74
+ if (profile.reasoning !== undefined) {
75
+ out = { ...out, reasoning: mergeDefined(out.reasoning, { ...profile.reasoning }) };
76
+ }
77
+ }
78
+ return out;
79
+ }
80
+ /**
81
+ * Resolve the effective request profile for one model:
82
+ * kind defaults < provider.extra < provider.modelProfiles[model].
83
+ */
84
+ export function resolveRequestProfile(kind, providerExtra, modelProfiles, model) {
85
+ return mergeRequestProfiles(kindDefaultProfile(kind), providerExtra, modelProfiles?.[model]);
86
+ }
87
+ /** Structural check used by the provider service when validating stored/wire input. */
88
+ export function isRequestProfile(value) {
89
+ if (!isPlainRecord(value))
90
+ return false;
91
+ const v = value;
92
+ if (v['maxTokens'] !== undefined && (typeof v['maxTokens'] !== 'number' || !Number.isInteger(v['maxTokens']) || v['maxTokens'] <= 0))
93
+ return false;
94
+ if (v['temperature'] !== undefined && (typeof v['temperature'] !== 'number' || Number.isNaN(v['temperature']) || v['temperature'] < 0))
95
+ return false;
96
+ if (v['topP'] !== undefined && (typeof v['topP'] !== 'number' || Number.isNaN(v['topP']) || v['topP'] < 0 || v['topP'] > 1))
97
+ return false;
98
+ if (v['headers'] !== undefined) {
99
+ if (!isPlainRecord(v['headers']))
100
+ return false;
101
+ for (const headerValue of Object.values(v['headers'])) {
102
+ if (typeof headerValue !== 'string')
103
+ return false;
104
+ }
105
+ }
106
+ if (v['extraBody'] !== undefined && !isPlainRecord(v['extraBody']))
107
+ return false;
108
+ if (v['reasoning'] !== undefined) {
109
+ if (!isPlainRecord(v['reasoning']))
110
+ return false;
111
+ const r = v['reasoning'];
112
+ if (r['effort'] !== undefined && (typeof r['effort'] !== 'string' || r['effort'].length === 0))
113
+ return false;
114
+ if (r['budgetTokens'] !== undefined && (typeof r['budgetTokens'] !== 'number' || !Number.isInteger(r['budgetTokens']) || r['budgetTokens'] <= 0))
115
+ return false;
116
+ }
117
+ return true;
118
+ }
@@ -0,0 +1,2 @@
1
+ import { type ProviderService, type ProviderServiceOptions } from './types.js';
2
+ export declare function createProviderService(options: ProviderServiceOptions): ProviderService;
@@ -0,0 +1,418 @@
1
+ /**
2
+ * File-backed `ProviderService` implementation.
3
+ *
4
+ * State lives in a single JSON file (`{ version: 3, providers: [...] }`).
5
+ * v1 stores are migrated on load (the default chat `model` is derived from
6
+ * the first discovered model); v2 stores are migrated to the v3 key ring
7
+ * (the single `apiKeyCipher` becomes `apiKeyCiphers: [cipher]`). API keys are
8
+ * stored AES-256-GCM encrypted (see `cipher.ts`); the file is
9
+ * chmodded 0600 whenever it holds secret material (best-effort on Windows).
10
+ * Writes are atomic (temp file + rename) so a crashed process never leaves a
11
+ * half-written registry.
12
+ */
13
+ import { randomUUID } from 'node:crypto';
14
+ import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from 'node:fs';
15
+ import { dirname } from 'node:path';
16
+ import { decryptSecret, encryptSecret } from './cipher.js';
17
+ import { defaultBaseUrlFor } from './probe.js';
18
+ import { getProviderProfile, listProviderProfiles } from './profiles/index.js';
19
+ import { isRequestProfile } from './request-profile.js';
20
+ import { ProviderError, } from './types.js';
21
+ const STORE_VERSION = 3;
22
+ /** Oldest store version `load()` still reads (migrated forward on load). */
23
+ const STORE_VERSION_MIN = 1;
24
+ /** Sanity cap on the key ring: rotation is for a handful of keys, not hundreds. */
25
+ const MAX_API_KEYS_PER_PROVIDER = 32;
26
+ /**
27
+ * A kind is valid when a profile is registered for it — built-ins plus any
28
+ * user-defined custom providers (`custom-providers.json`). Registry-aware so
29
+ * custom kinds work without touching this file.
30
+ */
31
+ function isProviderKind(value) {
32
+ return typeof value === 'string' && getProviderProfile(value) !== undefined;
33
+ }
34
+ function validateApiKeys(apiKeys) {
35
+ if (apiKeys.length > MAX_API_KEYS_PER_PROVIDER) {
36
+ throw new ProviderError('VALIDATION', `apiKeys holds at most ${MAX_API_KEYS_PER_PROVIDER} keys (got ${apiKeys.length}).`);
37
+ }
38
+ if (apiKeys.some((key) => typeof key !== 'string' || key.length === 0)) {
39
+ throw new ProviderError('VALIDATION', 'apiKeys must be an array of non-empty strings.');
40
+ }
41
+ }
42
+ function validateRequestProfileField(value, field) {
43
+ if (!isRequestProfile(value)) {
44
+ throw new ProviderError('VALIDATION', `'${field}' is not a valid request profile.`);
45
+ }
46
+ }
47
+ function validateModelProfiles(value) {
48
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
49
+ throw new ProviderError('VALIDATION', "'modelProfiles' must be an object keyed by model id.");
50
+ }
51
+ for (const [model, profile] of Object.entries(value)) {
52
+ if (model.length === 0) {
53
+ throw new ProviderError('VALIDATION', "'modelProfiles' keys must be non-empty model ids.");
54
+ }
55
+ validateRequestProfileField(profile, `modelProfiles['${model}']`);
56
+ }
57
+ }
58
+ function validateInput(input) {
59
+ if (typeof input.name !== 'string' || input.name.trim().length === 0) {
60
+ throw new ProviderError('VALIDATION', 'Provider name must be a non-empty string.');
61
+ }
62
+ if (!isProviderKind(input.kind)) {
63
+ throw new ProviderError('VALIDATION', `Unknown provider kind '${String(input.kind)}'. Expected one of: ${listProviderProfiles()
64
+ .map((p) => p.name)
65
+ .join(', ')}.`);
66
+ }
67
+ if (input.baseUrl !== undefined) {
68
+ let url;
69
+ try {
70
+ url = new URL(input.baseUrl);
71
+ }
72
+ catch {
73
+ throw new ProviderError('VALIDATION', `Invalid baseUrl '${input.baseUrl}'.`);
74
+ }
75
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
76
+ throw new ProviderError('VALIDATION', 'baseUrl must use http(s).');
77
+ }
78
+ }
79
+ if (input.models !== undefined) {
80
+ if (!Array.isArray(input.models) || input.models.some((m) => typeof m !== 'string' || m.length === 0)) {
81
+ throw new ProviderError('VALIDATION', 'models must be an array of non-empty strings.');
82
+ }
83
+ }
84
+ if (input.model !== undefined && (typeof input.model !== 'string' || input.model.length === 0)) {
85
+ throw new ProviderError('VALIDATION', 'model must be a non-empty string.');
86
+ }
87
+ if (input.apiKey !== undefined && input.apiKeys !== undefined) {
88
+ throw new ProviderError('VALIDATION', "Pass either 'apiKey' or 'apiKeys', not both.");
89
+ }
90
+ if (input.apiKey !== undefined && typeof input.apiKey !== 'string') {
91
+ throw new ProviderError('VALIDATION', 'apiKey must be a string.');
92
+ }
93
+ if (input.apiKeys !== undefined)
94
+ validateApiKeys(input.apiKeys);
95
+ if (input.extra !== undefined)
96
+ validateRequestProfileField(input.extra, 'extra');
97
+ if (input.modelProfiles !== undefined)
98
+ validateModelProfiles(input.modelProfiles);
99
+ }
100
+ /** Decrypted ring size for activation checks (stored entries may be legacy). */
101
+ function ringSize(provider) {
102
+ return provider.apiKeyCiphers?.length ?? (provider.apiKeyCipher !== undefined ? 1 : 0);
103
+ }
104
+ function activationIssue(provider) {
105
+ if (!provider.baseUrl && defaultBaseUrlFor(provider.kind) === null) {
106
+ return `Provider kind '${provider.kind}' requires a baseUrl.`;
107
+ }
108
+ if (!provider.model && !provider.models?.[0]) {
109
+ return 'Provider requires a default model.';
110
+ }
111
+ if (provider.kind !== 'local' && ringSize(provider) === 0) {
112
+ return 'Provider requires an API key.';
113
+ }
114
+ return null;
115
+ }
116
+ function assertActivatable(provider) {
117
+ const issue = activationIssue(provider);
118
+ if (issue !== null)
119
+ throw new ProviderError('VALIDATION', issue);
120
+ }
121
+ function isStoredProvider(value) {
122
+ if (typeof value !== 'object' || value === null)
123
+ return false;
124
+ const v = value;
125
+ return (typeof v['id'] === 'string' &&
126
+ typeof v['name'] === 'string' &&
127
+ isProviderKind(v['kind']) &&
128
+ typeof v['active'] === 'boolean' &&
129
+ (v['model'] === undefined || typeof v['model'] === 'string') &&
130
+ (v['apiKeyCiphers'] === undefined ||
131
+ (Array.isArray(v['apiKeyCiphers']) && v['apiKeyCiphers'].every((c) => typeof c === 'string'))) &&
132
+ (v['keyCursor'] === undefined ||
133
+ (typeof v['keyCursor'] === 'number' && Number.isInteger(v['keyCursor']) && v['keyCursor'] >= 0)) &&
134
+ (v['extra'] === undefined || isRequestProfile(v['extra'])) &&
135
+ (v['modelProfiles'] === undefined ||
136
+ (typeof v['modelProfiles'] === 'object' &&
137
+ v['modelProfiles'] !== null &&
138
+ !Array.isArray(v['modelProfiles']) &&
139
+ Object.values(v['modelProfiles']).every((p) => isRequestProfile(p)))));
140
+ }
141
+ /**
142
+ * Migrate a v1 stored provider to v2: the default chat model becomes an
143
+ * explicit field. v1 only had the discovered `models` list (whose first
144
+ * entry the setup wizard / probe used as the de-facto default), so the
145
+ * migration preserves that meaning instead of dropping it.
146
+ */
147
+ function migrateV1ToV2(entry) {
148
+ if (entry.model !== undefined)
149
+ return entry;
150
+ const firstModel = entry.models?.[0];
151
+ return firstModel === undefined ? entry : { ...entry, model: firstModel };
152
+ }
153
+ /**
154
+ * Migrate a v2 stored provider to v3: the single `apiKeyCipher` becomes a
155
+ * one-entry `apiKeyCiphers` ring, and the rotation cursor starts at 0.
156
+ */
157
+ function migrateV2ToV3(entry) {
158
+ const { apiKeyCipher, ...rest } = entry;
159
+ return {
160
+ ...rest,
161
+ ...(apiKeyCipher !== undefined ? { apiKeyCiphers: [apiKeyCipher] } : {}),
162
+ keyCursor: entry.keyCursor ?? 0,
163
+ };
164
+ }
165
+ export function createProviderService(options) {
166
+ const { storagePath, masterKey } = options;
167
+ if (typeof storagePath !== 'string' || storagePath.length === 0) {
168
+ throw new ProviderError('VALIDATION', 'storagePath must be a non-empty string.');
169
+ }
170
+ if (typeof masterKey !== 'string' || masterKey.length === 0) {
171
+ throw new ProviderError('VALIDATION', 'masterKey must be a non-empty string.');
172
+ }
173
+ const providers = new Map();
174
+ let loaded = false;
175
+ function assertLoaded() {
176
+ if (!loaded)
177
+ throw new ProviderError('STORAGE', 'ProviderService.load() must be called first.');
178
+ }
179
+ function toPublic(stored) {
180
+ const ciphers = stored.apiKeyCiphers ?? [];
181
+ return {
182
+ id: stored.id,
183
+ name: stored.name,
184
+ kind: stored.kind,
185
+ ...(stored.baseUrl !== undefined ? { baseUrl: stored.baseUrl } : {}),
186
+ hasKey: ciphers.length > 0,
187
+ keyCount: ciphers.length,
188
+ ...(stored.keyCursor !== undefined ? { keyCursor: stored.keyCursor } : {}),
189
+ active: stored.active,
190
+ ...(stored.model !== undefined ? { model: stored.model } : {}),
191
+ ...(stored.models !== undefined ? { models: [...stored.models] } : {}),
192
+ ...(stored.extra !== undefined ? { extra: stored.extra } : {}),
193
+ ...(stored.modelProfiles !== undefined ? { modelProfiles: { ...stored.modelProfiles } } : {}),
194
+ };
195
+ }
196
+ function getStoredOrThrow(id) {
197
+ const stored = providers.get(id);
198
+ if (!stored)
199
+ throw new ProviderError('NOT_FOUND', `Provider '${id}' not found.`);
200
+ return stored;
201
+ }
202
+ async function persist() {
203
+ const file = { version: STORE_VERSION, providers: [...providers.values()] };
204
+ mkdirSync(dirname(storagePath), { recursive: true });
205
+ const tmpPath = `${storagePath}.tmp`;
206
+ writeFileSync(tmpPath, JSON.stringify(file, null, 2), 'utf8');
207
+ const holdsSecrets = [...providers.values()].some((p) => (p.apiKeyCiphers?.length ?? 0) > 0);
208
+ if (holdsSecrets) {
209
+ try {
210
+ chmodSync(tmpPath, 0o600);
211
+ }
212
+ catch {
213
+ // Best effort: Windows has no POSIX permission bits (chmodSync is a
214
+ // no-op for ACLs), so the store is NOT OS-protected there.
215
+ // See SECURITY.md §5.
216
+ }
217
+ }
218
+ renameSync(tmpPath, storagePath);
219
+ }
220
+ async function load() {
221
+ providers.clear();
222
+ if (!existsSync(storagePath)) {
223
+ loaded = true;
224
+ return;
225
+ }
226
+ let raw;
227
+ try {
228
+ raw = readFileSync(storagePath, 'utf8');
229
+ }
230
+ catch (err) {
231
+ throw new ProviderError('STORAGE', `Cannot read provider store at '${storagePath}': ${err instanceof Error ? err.message : String(err)}`);
232
+ }
233
+ let parsed;
234
+ try {
235
+ parsed = JSON.parse(raw);
236
+ }
237
+ catch {
238
+ throw new ProviderError('STORAGE', `Provider store at '${storagePath}' is not valid JSON.`);
239
+ }
240
+ if (typeof parsed !== 'object' || parsed === null) {
241
+ throw new ProviderError('STORAGE', `Provider store at '${storagePath}' has an unexpected shape.`);
242
+ }
243
+ const file = parsed;
244
+ if (typeof file.version !== 'number' ||
245
+ file.version < STORE_VERSION_MIN ||
246
+ file.version > STORE_VERSION ||
247
+ !Array.isArray(file.providers)) {
248
+ throw new ProviderError('STORAGE', `Provider store at '${storagePath}' has an unsupported version (expected ${STORE_VERSION_MIN}–${STORE_VERSION}).`);
249
+ }
250
+ const fileVersion = file.version;
251
+ for (const entry of file.providers) {
252
+ if (!isStoredProvider(entry)) {
253
+ throw new ProviderError('STORAGE', `Provider store at '${storagePath}' contains an invalid entry.`);
254
+ }
255
+ let stored = { ...entry };
256
+ if (fileVersion === 1)
257
+ stored = migrateV1ToV2(stored);
258
+ if (fileVersion <= 2)
259
+ stored = migrateV2ToV3(stored);
260
+ providers.set(entry.id, stored);
261
+ }
262
+ loaded = true;
263
+ const active = [...providers.values()].find((provider) => provider.active);
264
+ let repaired = false;
265
+ if (active !== undefined && activationIssue(active) !== null) {
266
+ for (const provider of providers.values())
267
+ provider.active = false;
268
+ const replacement = [...providers.values()].find((provider) => activationIssue(provider) === null);
269
+ if (replacement !== undefined)
270
+ replacement.active = true;
271
+ repaired = true;
272
+ }
273
+ if (fileVersion < STORE_VERSION || repaired) {
274
+ // Upgrade the file on disk so the next load is clean.
275
+ await persist();
276
+ }
277
+ }
278
+ async function list() {
279
+ assertLoaded();
280
+ return [...providers.values()].map(toPublic);
281
+ }
282
+ /**
283
+ * Resolve the key-ring update for a `save()`: `apiKey` (single) and
284
+ * `apiKeys` (ring) are mutually exclusive and both replace the whole ring.
285
+ * Returns `undefined` when the input carries no key fields (keep stored).
286
+ */
287
+ function resolveKeyRingUpdate(input) {
288
+ if (input.apiKey !== undefined) {
289
+ return input.apiKey.length > 0 ? [encryptSecret(masterKey, input.apiKey)] : [];
290
+ }
291
+ if (input.apiKeys !== undefined) {
292
+ return input.apiKeys.map((key) => encryptSecret(masterKey, key));
293
+ }
294
+ return undefined;
295
+ }
296
+ async function save(input) {
297
+ assertLoaded();
298
+ validateInput(input);
299
+ const existing = input.id !== undefined ? providers.get(input.id) : undefined;
300
+ if (existing) {
301
+ const updated = { ...existing, name: input.name, kind: input.kind };
302
+ if (input.baseUrl !== undefined)
303
+ updated.baseUrl = input.baseUrl;
304
+ if (input.model !== undefined)
305
+ updated.model = input.model;
306
+ if (input.models !== undefined)
307
+ updated.models = [...input.models];
308
+ const ring = resolveKeyRingUpdate(input);
309
+ if (ring !== undefined) {
310
+ updated.apiKeyCiphers = ring;
311
+ delete updated.apiKeyCipher;
312
+ // Keep the cursor inside the new ring.
313
+ updated.keyCursor = ring.length > 0 ? (updated.keyCursor ?? 0) % ring.length : 0;
314
+ }
315
+ if (input.extra !== undefined)
316
+ updated.extra = { ...input.extra };
317
+ if (input.modelProfiles !== undefined) {
318
+ updated.modelProfiles = Object.fromEntries(Object.entries(input.modelProfiles).map(([model, profile]) => [model, { ...profile }]));
319
+ }
320
+ updated.active = input.active ?? existing.active;
321
+ if (updated.active)
322
+ assertActivatable(updated);
323
+ providers.set(existing.id, updated);
324
+ if (updated.active) {
325
+ for (const p of providers.values())
326
+ p.active = p.id === updated.id;
327
+ }
328
+ await persist();
329
+ return toPublic(updated);
330
+ }
331
+ const id = input.id ?? randomUUID();
332
+ if (providers.has(id)) {
333
+ // Practically unreachable (randomUUID collision), but fail loudly.
334
+ throw new ProviderError('VALIDATION', `Provider id '${id}' is already taken.`);
335
+ }
336
+ const ring = resolveKeyRingUpdate(input) ?? [];
337
+ const stored = {
338
+ id,
339
+ name: input.name,
340
+ kind: input.kind,
341
+ ...(input.baseUrl !== undefined ? { baseUrl: input.baseUrl } : {}),
342
+ ...(ring.length > 0 ? { apiKeyCiphers: ring, keyCursor: 0 } : {}),
343
+ active: input.active ?? false,
344
+ ...(input.model !== undefined ? { model: input.model } : {}),
345
+ ...(input.models !== undefined ? { models: [...input.models] } : {}),
346
+ ...(input.extra !== undefined ? { extra: { ...input.extra } } : {}),
347
+ ...(input.modelProfiles !== undefined
348
+ ? {
349
+ modelProfiles: Object.fromEntries(Object.entries(input.modelProfiles).map(([model, profile]) => [model, { ...profile }])),
350
+ }
351
+ : {}),
352
+ };
353
+ if (input.active === true)
354
+ assertActivatable(stored);
355
+ if (input.active === undefined && ![...providers.values()].some((p) => p.active)) {
356
+ stored.active = activationIssue(stored) === null;
357
+ }
358
+ if (stored.active) {
359
+ for (const p of providers.values())
360
+ p.active = false;
361
+ }
362
+ providers.set(id, stored);
363
+ await persist();
364
+ return toPublic(stored);
365
+ }
366
+ function decryptRing(stored) {
367
+ const ciphers = stored.apiKeyCiphers ?? [];
368
+ try {
369
+ return ciphers.map((cipher) => decryptSecret(masterKey, cipher));
370
+ }
371
+ catch {
372
+ throw new ProviderError('STORAGE', `Stored API key for provider '${stored.id}' cannot be decrypted (wrong master key?).`);
373
+ }
374
+ }
375
+ async function getApiKey(id) {
376
+ assertLoaded();
377
+ const stored = getStoredOrThrow(id);
378
+ const keys = decryptRing(stored);
379
+ if (keys.length === 0)
380
+ return null;
381
+ // The cursor key is the ring's current head; fall back to the first key
382
+ // when the persisted cursor somehow points past the ring.
383
+ const cursor = stored.keyCursor ?? 0;
384
+ return keys[((cursor % keys.length) + keys.length) % keys.length] ?? keys[0] ?? null;
385
+ }
386
+ async function getApiKeys(id) {
387
+ assertLoaded();
388
+ return decryptRing(getStoredOrThrow(id));
389
+ }
390
+ async function setKeyCursor(id, cursor) {
391
+ assertLoaded();
392
+ const stored = getStoredOrThrow(id);
393
+ const size = stored.apiKeyCiphers?.length ?? 0;
394
+ stored.keyCursor = size > 0 ? ((cursor % size) + size) % size : 0;
395
+ await persist();
396
+ }
397
+ async function activate(id) {
398
+ assertLoaded();
399
+ const stored = getStoredOrThrow(id);
400
+ assertActivatable(stored);
401
+ for (const p of providers.values())
402
+ p.active = p.id === stored.id;
403
+ await persist();
404
+ return toPublic(stored);
405
+ }
406
+ async function deleteProvider(id) {
407
+ assertLoaded();
408
+ const stored = getStoredOrThrow(id);
409
+ providers.delete(id);
410
+ if (stored.active) {
411
+ const next = [...providers.values()].find((provider) => activationIssue(provider) === null);
412
+ if (next !== undefined)
413
+ next.active = true;
414
+ }
415
+ await persist();
416
+ }
417
+ return { load, list, save, getApiKey, getApiKeys, setKeyCursor, activate, delete: deleteProvider };
418
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Public types for `@boombot/protocol`.
3
+ *
4
+ * These shapes intentionally mirror the provider wire schemas in
5
+ * `@boombot/protocol` (`providerSchema` / `providerInputSchema`)
6
+ * structurally, without depending on that package: the gateway parses the
7
+ * wire payload with zod and hands the result straight to `ProviderService`.
8
+ * Keeping this package dependency-free (node builtins only) makes it a leaf
9
+ * the gateway, CLI and tools can all share.
10
+ */
11
+ import type { RequestProfile } from './request-profile.js';
12
+ /** LLM provider kinds. Must stay in sync with `providerKindSchema` in `@boombot/protocol`. */
13
+ export type ProviderKind = 'openai' | 'anthropic' | 'google' | 'mistral' | 'xai' | 'deepseek' | 'openai-compatible' | 'local';
14
+ /**
15
+ * All known provider kinds, derived from the profile registry
16
+ * (`./profiles/`). The registry is the single source of truth; this list
17
+ * stays in sync automatically as providers are added.
18
+ */
19
+ export declare const PROVIDER_KINDS: readonly ProviderKind[];
20
+ /**
21
+ * Provider record as exposed to callers. API keys are NEVER present here —
22
+ * `hasKey`/`keyCount` only report whether keys are stored. Matches the wire
23
+ * `providerSchema` (minus nothing, plus nothing).
24
+ */
25
+ export interface Provider {
26
+ readonly id: string;
27
+ readonly name: string;
28
+ readonly kind: ProviderKind;
29
+ readonly baseUrl?: string;
30
+ readonly hasKey: boolean;
31
+ /** Number of API keys in the provider's key ring (KeyRoulette). */
32
+ readonly keyCount: number;
33
+ /**
34
+ * Key-rotation cursor: index of the key the ring tries first. Operational
35
+ * state only, no secret material.
36
+ */
37
+ readonly keyCursor?: number;
38
+ readonly active: boolean;
39
+ /**
40
+ * Default chat model used for turns. Explicit, and — unlike `models` —
41
+ * never overwritten by `provider.test` discovery.
42
+ */
43
+ readonly model?: string;
44
+ /**
45
+ * Mutable on purpose: matches the wire `providerSchema` (`models?: string[]`)
46
+ * so handler responses typecheck without casts.
47
+ */
48
+ readonly models?: string[];
49
+ /**
50
+ * Provider-level request profile (tier 2 of 3: kind defaults <
51
+ * `extra` < per-model override). Applies to every model of this provider.
52
+ */
53
+ readonly extra?: RequestProfile;
54
+ /**
55
+ * Per-model request profile overrides (tier 3 of 3), keyed by model id.
56
+ * Wins over `extra` and the kind defaults for that one model.
57
+ */
58
+ readonly modelProfiles?: Readonly<Record<string, RequestProfile>>;
59
+ }
60
+ /**
61
+ * Input accepted by `ProviderService.save`. Mirrors the wire
62
+ * `providerInputSchema`: `apiKey`/`apiKeys` are write-only (stored
63
+ * encrypted), everything else is a plain field update. Omitted optional
64
+ * fields keep their stored value on update; pass `apiKey: ''` (or
65
+ * `apiKeys: []`) to explicitly clear the stored key ring.
66
+ */
67
+ export interface ProviderInput {
68
+ readonly id?: string;
69
+ readonly name: string;
70
+ readonly kind: ProviderKind;
71
+ readonly baseUrl?: string;
72
+ /** Default chat model for turns. Omitted on update = keep the stored value. */
73
+ readonly model?: string;
74
+ readonly models?: readonly string[];
75
+ readonly apiKey?: string;
76
+ /**
77
+ * Replace the whole key ring. Mutually exclusive with `apiKey` — passing
78
+ * both is a validation error.
79
+ */
80
+ readonly apiKeys?: readonly string[];
81
+ /** Provider-level request profile (tier 2). Omitted on update = keep. */
82
+ readonly extra?: RequestProfile;
83
+ /** Per-model profile overrides (tier 3). Omitted on update = keep. */
84
+ readonly modelProfiles?: Readonly<Record<string, RequestProfile>>;
85
+ readonly active?: boolean;
86
+ }
87
+ export interface ProviderServiceOptions {
88
+ /** JSON file the registry persists to, e.g. `<dataDir>/providers.json`. */
89
+ readonly storagePath: string;
90
+ /**
91
+ * Master secret used to encrypt API keys at rest (AES-256-GCM, key derived
92
+ * via SHA-256). Keep it in an env var (`BOOMBOT_MASTER_KEY`), never in the file.
93
+ */
94
+ readonly masterKey: string;
95
+ }
96
+ /** Error thrown by `ProviderService` for expected failure modes. */
97
+ export declare class ProviderError extends Error {
98
+ readonly code: 'NOT_FOUND' | 'VALIDATION' | 'STORAGE';
99
+ constructor(code: ProviderError['code'], message: string);
100
+ }
101
+ /**
102
+ * CRUD + key vault for LLM provider configs. At most one provider is active;
103
+ * `activate()` enforces that it is complete enough to serve turns.
104
+ */
105
+ export interface ProviderService {
106
+ /** Load persisted state from `storagePath`. Must be called before any other method. */
107
+ load(): Promise<void>;
108
+ /** All providers, newest-last, without secret material. */
109
+ list(): Promise<Provider[]>;
110
+ /** Create or update a provider. `apiKey`/`apiKeys` are write-only. */
111
+ save(input: ProviderInput): Promise<Provider>;
112
+ /** Decrypted API key, or `null` when none is stored. */
113
+ getApiKey(id: string): Promise<string | null>;
114
+ /**
115
+ * All decrypted API keys for a provider, in ring order. Empty when none
116
+ * is stored. KeyRoulette rotates through these on 401/429.
117
+ */
118
+ getApiKeys(id: string): Promise<string[]>;
119
+ /**
120
+ * Persist the key-rotation cursor (called by KeyRoulette when a key
121
+ * fails so rotation survives restarts).
122
+ */
123
+ setKeyCursor(id: string, cursor: number): Promise<void>;
124
+ /** Make exactly this provider active. */
125
+ activate(id: string): Promise<Provider>;
126
+ /** Remove a provider. If it was active, the oldest remaining becomes active. */
127
+ delete(id: string): Promise<void>;
128
+ }
129
+ /** Options for {@link probeProvider}. */
130
+ export interface ProbeOptions {
131
+ readonly kind: ProviderKind;
132
+ readonly baseUrl?: string;
133
+ }
134
+ /** Result of {@link probeProvider}. */
135
+ export interface ProbeResult {
136
+ readonly ok: boolean;
137
+ /** Model ids discovered via the provider's models catalog, when available. */
138
+ readonly models?: string[];
139
+ /** Human-readable failure reason. Never contains secret material. */
140
+ readonly error?: string;
141
+ }