@johpaz/hive-agents 0.0.35 → 0.0.37

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 (441) hide show
  1. package/README.md +64 -39
  2. package/dist/hive.js +3231 -3189
  3. package/dist/tool-worker.js +218406 -0
  4. package/dist/ui/assets/{AgentCreateForm-B4eK7efF.js → AgentCreateForm-tJZv9FZC.js} +1 -1
  5. package/dist/ui/assets/{AgentDetailPage-BD2uoJWk.js → AgentDetailPage-Du-mRcAX.js} +1 -1
  6. package/dist/ui/assets/AgentNewPage-DIFYd_Ys.js +1 -0
  7. package/dist/ui/assets/{AgentsPage-4JUZXvkA.js → AgentsPage-YvSgWRiw.js} +6 -6
  8. package/dist/ui/assets/CanvasPage-DtMwGvxf.js +33 -0
  9. package/dist/ui/assets/{ChannelsPage-BUn7-nhV.js → ChannelsPage-BdBXWHjj.js} +1 -1
  10. package/dist/ui/assets/DashboardPage-ghl1ZguH.js +6 -0
  11. package/dist/ui/assets/{LoginPage-C8j_urUD.js → LoginPage-CAmSI9Vy.js} +1 -1
  12. package/dist/ui/assets/LogsPage-DAPBHkwK.js +1 -0
  13. package/dist/ui/assets/MeetingPage-WjjGOqqU.js +1 -0
  14. package/dist/ui/assets/{NotFound-Drh-sJPN.js → NotFound-BMeQSGcG.js} +1 -1
  15. package/dist/ui/assets/ProvidersPage-Ct6HsAi1.js +1 -0
  16. package/dist/ui/assets/{RecoverPage-DNb1Pr8h.js → RecoverPage-DpW3l-yv.js} +1 -1
  17. package/dist/ui/assets/SettingsPage-DBJ7_E6C.js +9 -0
  18. package/dist/ui/assets/SetupPage-DKmLVUaj.js +1 -0
  19. package/dist/ui/assets/{WebChatPage-R-YOwA4F.js → WebChatPage-CVRcKept.js} +2 -2
  20. package/dist/ui/assets/accordion-C5d5Rm5z.js +1 -0
  21. package/dist/ui/assets/{alert-U8FsgWi7.js → alert-C-NE-P3s.js} +1 -1
  22. package/dist/ui/assets/{alert-dialog-CRdMkkmk.js → alert-dialog-C5mzbHdP.js} +1 -1
  23. package/dist/ui/assets/{badge-Cli1jnH5.js → badge-ChpACfWO.js} +1 -1
  24. package/dist/ui/assets/chevron-up-BYhk0K2J.js +1 -0
  25. package/dist/ui/assets/{dialog-DQ3s-LuO.js → dialog-QnZ0ad8O.js} +1 -1
  26. package/dist/ui/assets/dropdown-menu-BK-CO3Od.js +1 -0
  27. package/dist/ui/assets/{es-DcMjrpbA.js → es-NQNoaWDx.js} +1 -1
  28. package/dist/ui/assets/index-B2fCYtTS.css +2 -0
  29. package/dist/ui/assets/index-DMCjjdqf.js +116 -0
  30. package/dist/ui/assets/{label-0BvGVXvZ.js → label-D2H1IR_J.js} +1 -1
  31. package/dist/ui/assets/progress-BherYzY6.js +1 -0
  32. package/dist/ui/assets/scroll-area-DkeyX32e.js +1 -0
  33. package/dist/ui/assets/{slider-D47dOrRa.js → slider-CsiUDxc3.js} +1 -1
  34. package/dist/ui/assets/switch-BDwN8RYV.js +1 -0
  35. package/dist/ui/assets/{table-DhowbNxQ.js → table-CSc8ubon.js} +1 -1
  36. package/dist/ui/assets/terminal-DN38Q456.js +1 -0
  37. package/dist/ui/assets/useProviders-C6_QHsEi.js +1 -0
  38. package/dist/ui/assets/{vendor-radix-JY4ncZrD.js → vendor-radix-cw1bQaVC.js} +4 -4
  39. package/dist/ui/assets/{vendor-react-CscwQerf.js → vendor-react-D4s9E-zj.js} +1 -1
  40. package/dist/ui/dist/assets/AgentCreateForm-tJZv9FZC.js +1 -0
  41. package/dist/ui/dist/assets/AgentDetailPage-Du-mRcAX.js +1 -0
  42. package/dist/ui/dist/assets/AgentNewPage-DIFYd_Ys.js +1 -0
  43. package/dist/ui/dist/assets/AgentsPage-YvSgWRiw.js +10 -0
  44. package/dist/ui/dist/assets/CanvasPage-DtMwGvxf.js +33 -0
  45. package/dist/ui/dist/assets/ChannelsPage-BdBXWHjj.js +8 -0
  46. package/dist/ui/dist/assets/DashboardPage-ghl1ZguH.js +6 -0
  47. package/dist/ui/dist/assets/LoginPage-CAmSI9Vy.js +1 -0
  48. package/dist/ui/dist/assets/LogsPage-DAPBHkwK.js +1 -0
  49. package/dist/ui/dist/assets/MeetingPage-WjjGOqqU.js +1 -0
  50. package/dist/ui/dist/assets/NotFound-BMeQSGcG.js +1 -0
  51. package/dist/ui/dist/assets/ProvidersPage-Ct6HsAi1.js +1 -0
  52. package/dist/ui/dist/assets/RecoverPage-DpW3l-yv.js +1 -0
  53. package/dist/ui/dist/assets/SettingsPage-DBJ7_E6C.js +9 -0
  54. package/dist/ui/dist/assets/SetupPage-DKmLVUaj.js +1 -0
  55. package/dist/ui/dist/assets/WebChatPage-CVRcKept.js +16 -0
  56. package/dist/ui/dist/assets/accordion-C5d5Rm5z.js +1 -0
  57. package/dist/ui/dist/assets/activity-c3pNngT_.js +1 -0
  58. package/dist/ui/dist/assets/alert-C-NE-P3s.js +1 -0
  59. package/dist/ui/dist/assets/alert-dialog-C5mzbHdP.js +1 -0
  60. package/dist/ui/dist/assets/arrow-left-CBcbX5EZ.js +1 -0
  61. package/dist/ui/dist/assets/badge-ChpACfWO.js +1 -0
  62. package/dist/ui/dist/assets/calendar-B-KZ9RQO.js +1 -0
  63. package/dist/ui/dist/assets/card-CNf6BS2e.js +1 -0
  64. package/dist/ui/dist/assets/chevron-left-D4U-5A27.js +1 -0
  65. package/dist/ui/dist/assets/chevron-right-CR4Skrf3.js +1 -0
  66. package/dist/ui/dist/assets/chevron-up-BYhk0K2J.js +1 -0
  67. package/dist/ui/dist/assets/circle-alert-CyHDwUj8.js +1 -0
  68. package/dist/ui/dist/assets/circle-check-Bb54Ebmu.js +1 -0
  69. package/dist/ui/dist/assets/cpu-Cdgc_B1K.js +1 -0
  70. package/dist/ui/dist/assets/dialog-QnZ0ad8O.js +1 -0
  71. package/dist/ui/dist/assets/download-C3ifGMjJ.js +1 -0
  72. package/dist/ui/dist/assets/dropdown-menu-BK-CO3Od.js +1 -0
  73. package/dist/ui/dist/assets/es-NQNoaWDx.js +1 -0
  74. package/dist/ui/dist/assets/external-link-BvxYeTP1.js +1 -0
  75. package/dist/ui/dist/assets/eye-DqNTU_GD.js +1 -0
  76. package/dist/ui/dist/assets/file-text-BT_9S9SM.js +1 -0
  77. package/dist/ui/dist/assets/folder-open-BhH8y9ac.js +1 -0
  78. package/dist/ui/dist/assets/format-GVHeOyWI.js +1 -0
  79. package/dist/ui/dist/assets/gateway-url-COCbW0IR.js +1 -0
  80. package/dist/ui/dist/assets/gauge-D_TMa4i9.js +1 -0
  81. package/dist/ui/dist/assets/globe-DeCQTCDJ.js +1 -0
  82. package/dist/ui/dist/assets/hexagon-DsGOUl-H.js +1 -0
  83. package/dist/ui/dist/assets/history-BSG-Ypqf.js +1 -0
  84. package/dist/ui/dist/assets/index-B2fCYtTS.css +2 -0
  85. package/dist/ui/dist/assets/index-DMCjjdqf.js +116 -0
  86. package/dist/ui/dist/assets/info-NwLoa2Mj.js +1 -0
  87. package/dist/ui/dist/assets/key-3EP0dhkT.js +1 -0
  88. package/dist/ui/dist/assets/label-D2H1IR_J.js +1 -0
  89. package/dist/ui/dist/assets/loader-circle-CZNax6kS.js +1 -0
  90. package/dist/ui/dist/assets/lock-Ei1_J-Nq.js +1 -0
  91. package/dist/ui/dist/assets/pause-BUqah9Bi.js +1 -0
  92. package/dist/ui/dist/assets/play-NcZ4swwL.js +1 -0
  93. package/dist/ui/dist/assets/plus-CX1xyhp5.js +1 -0
  94. package/dist/ui/dist/assets/progress-BherYzY6.js +1 -0
  95. package/dist/ui/dist/assets/refresh-cw-DaYdjQFk.js +1 -0
  96. package/dist/ui/dist/assets/rolldown-runtime-S-ySWqyJ.js +1 -0
  97. package/dist/ui/dist/assets/save-CUdYyHNy.js +1 -0
  98. package/dist/ui/dist/assets/scroll-area-DkeyX32e.js +1 -0
  99. package/dist/ui/dist/assets/send-B0H5SEIE.js +1 -0
  100. package/dist/ui/dist/assets/settings-Ds4SqD8s.js +1 -0
  101. package/dist/ui/dist/assets/slider-CsiUDxc3.js +14 -0
  102. package/dist/ui/dist/assets/sparkles-yUEb-7oH.js +1 -0
  103. package/dist/ui/dist/assets/square-BD81nFtN.js +1 -0
  104. package/dist/ui/dist/assets/switch-BDwN8RYV.js +1 -0
  105. package/dist/ui/dist/assets/table-CSc8ubon.js +1 -0
  106. package/dist/ui/dist/assets/terminal-DN38Q456.js +1 -0
  107. package/dist/ui/dist/assets/textarea-CXgXWKrT.js +1 -0
  108. package/dist/ui/dist/assets/trash-2-CNjMkoq6.js +1 -0
  109. package/dist/ui/dist/assets/triangle-alert-C9Y8Ub4X.js +1 -0
  110. package/dist/ui/dist/assets/useProviders-C6_QHsEi.js +1 -0
  111. package/dist/ui/dist/assets/utils-3pnRFmFe.js +1 -0
  112. package/dist/ui/dist/assets/vendor-charts-Bu2lyBKP.js +65 -0
  113. package/dist/ui/dist/assets/vendor-query-DsWPbQdG.js +1 -0
  114. package/dist/ui/dist/assets/vendor-radix-cw1bQaVC.js +63 -0
  115. package/dist/ui/dist/assets/vendor-react-D4s9E-zj.js +1 -0
  116. package/dist/ui/dist/assets/vendor-router-C9pIYwbJ.js +3 -0
  117. package/dist/ui/dist/assets/volume-2-CeSXNDv4.js +1 -0
  118. package/dist/ui/dist/assets/zap-hlXjpSeA.js +1 -0
  119. package/dist/ui/dist/favicon.ico +0 -0
  120. package/dist/ui/dist/index.html +40 -0
  121. package/dist/ui/dist/placeholder.svg +1 -0
  122. package/dist/ui/index.html +6 -6
  123. package/package.json +138 -13
  124. package/packages/cli/src/adapters/binary.ts +461 -0
  125. package/packages/cli/src/adapters/bun-global.ts +378 -0
  126. package/packages/cli/src/adapters/config.ts +314 -0
  127. package/packages/cli/src/adapters/docker.ts +308 -0
  128. package/packages/cli/src/adapters/factory.ts +168 -0
  129. package/packages/cli/src/adapters/index.ts +80 -0
  130. package/packages/cli/src/adapters/types.ts +218 -0
  131. package/packages/cli/src/commands/agent-run.ts +168 -0
  132. package/packages/cli/src/commands/agents.ts +398 -0
  133. package/packages/cli/src/commands/chat.ts +142 -0
  134. package/packages/cli/src/commands/config.ts +49 -0
  135. package/packages/cli/src/commands/cron.ts +487 -0
  136. package/packages/cli/src/commands/dev.ts +58 -0
  137. package/packages/cli/src/commands/doctor.ts +320 -0
  138. package/packages/cli/src/commands/gateway.ts +719 -0
  139. package/packages/cli/src/commands/logs.ts +57 -0
  140. package/packages/cli/src/commands/mcp.ts +175 -0
  141. package/packages/cli/src/commands/message.ts +77 -0
  142. package/packages/cli/src/commands/migrate.ts +90 -0
  143. package/packages/cli/src/commands/onboard.ts +1656 -0
  144. package/packages/cli/src/commands/security.ts +144 -0
  145. package/packages/cli/src/commands/service.ts +50 -0
  146. package/packages/cli/src/commands/sessions.ts +116 -0
  147. package/packages/cli/src/commands/skills.ts +215 -0
  148. package/packages/cli/src/commands/update.ts +203 -0
  149. package/packages/cli/src/index.ts +210 -0
  150. package/packages/cli/src/ui-bundle.generated.ts +3 -0
  151. package/packages/cli/src/utils/token.ts +6 -0
  152. package/packages/core/src/agent/agent-loop.ts +691 -0
  153. package/packages/core/src/agent/compaction.ts +240 -0
  154. package/packages/core/src/agent/context-compiler.ts +467 -0
  155. package/packages/core/src/agent/context-guard.ts +91 -0
  156. package/packages/core/src/agent/conversation-store.ts +244 -0
  157. package/packages/core/src/agent/curator.ts +158 -0
  158. package/packages/core/src/agent/hooks.ts +166 -0
  159. package/packages/core/src/agent/llm-client.ts +167 -0
  160. package/packages/core/src/agent/llm-providers/anthropic.ts +212 -0
  161. package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
  162. package/packages/core/src/agent/llm-providers/gemini.ts +215 -0
  163. package/packages/core/src/agent/llm-providers/groq.ts +5 -0
  164. package/packages/core/src/agent/llm-providers/interface.ts +195 -0
  165. package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
  166. package/packages/core/src/agent/llm-providers/local-llama.ts +37 -0
  167. package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
  168. package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
  169. package/packages/core/src/agent/llm-providers/ollama.ts +175 -0
  170. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +379 -0
  171. package/packages/core/src/agent/llm-providers/openai.ts +5 -0
  172. package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
  173. package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
  174. package/packages/core/src/agent/native-tools.ts +31 -0
  175. package/packages/core/src/agent/playbook-selector.ts +147 -0
  176. package/packages/core/src/agent/prompt-builder.ts +169 -0
  177. package/packages/core/src/agent/providers/index.ts +204 -0
  178. package/packages/core/src/agent/providers.ts +1 -0
  179. package/packages/core/src/agent/reflector.ts +200 -0
  180. package/packages/core/src/agent/service.ts +267 -0
  181. package/packages/core/src/agent/skill-selector.ts +479 -0
  182. package/packages/core/src/agent/stuck-loop.ts +133 -0
  183. package/packages/core/src/agent/tool-selector.ts +569 -0
  184. package/packages/core/src/agent/tracer.ts +100 -0
  185. package/packages/core/src/auth/auth.ts +108 -0
  186. package/packages/core/src/auth/index.ts +1 -0
  187. package/packages/core/src/canvas/a2ui-tools.ts +255 -0
  188. package/packages/core/src/canvas/canvas-manager.ts +390 -0
  189. package/packages/core/src/canvas/canvas-tools.ts +448 -0
  190. package/packages/core/src/canvas/emitter.ts +149 -0
  191. package/packages/core/src/canvas/index.ts +3 -0
  192. package/packages/core/src/channels/base.ts +154 -0
  193. package/packages/core/src/channels/discord.ts +273 -0
  194. package/packages/core/src/channels/index.ts +7 -0
  195. package/packages/core/src/channels/manager.ts +450 -0
  196. package/packages/core/src/channels/slack.ts +323 -0
  197. package/packages/core/src/channels/telegram.ts +612 -0
  198. package/packages/core/src/channels/webchat.ts +139 -0
  199. package/packages/core/src/channels/whatsapp.ts +548 -0
  200. package/packages/core/src/config/index.ts +12 -0
  201. package/packages/core/src/config/loader.ts +569 -0
  202. package/packages/core/src/events/agent-bus.ts +460 -0
  203. package/packages/core/src/events/event-bus.ts +169 -0
  204. package/packages/core/src/gateway/channel-notify.ts +64 -0
  205. package/packages/core/src/gateway/helpers/cors.ts +32 -0
  206. package/packages/core/src/gateway/helpers/index.ts +4 -0
  207. package/packages/core/src/gateway/helpers/narration.ts +57 -0
  208. package/packages/core/src/gateway/helpers/path.ts +13 -0
  209. package/packages/core/src/gateway/helpers/redact.ts +61 -0
  210. package/packages/core/src/gateway/index.ts +5 -0
  211. package/packages/core/src/gateway/initializer.ts +363 -0
  212. package/packages/core/src/gateway/lane-queue.ts +169 -0
  213. package/packages/core/src/gateway/llm-local/client.ts +94 -0
  214. package/packages/core/src/gateway/llm-local/detector.ts +321 -0
  215. package/packages/core/src/gateway/llm-local/downloader.ts +216 -0
  216. package/packages/core/src/gateway/llm-local/index.ts +34 -0
  217. package/packages/core/src/gateway/llm-local/manager.ts +186 -0
  218. package/packages/core/src/gateway/llm-local/models.ts +149 -0
  219. package/packages/core/src/gateway/llm-local/server.ts +179 -0
  220. package/packages/core/src/gateway/resolver.ts +108 -0
  221. package/packages/core/src/gateway/router.ts +124 -0
  222. package/packages/core/src/gateway/routes/agents.ts +210 -0
  223. package/packages/core/src/gateway/routes/auth.ts +244 -0
  224. package/packages/core/src/gateway/routes/channels.ts +484 -0
  225. package/packages/core/src/gateway/routes/chat.ts +241 -0
  226. package/packages/core/src/gateway/routes/config.ts +12 -0
  227. package/packages/core/src/gateway/routes/cron-api.ts +544 -0
  228. package/packages/core/src/gateway/routes/ethics.ts +46 -0
  229. package/packages/core/src/gateway/routes/llm-local.ts +271 -0
  230. package/packages/core/src/gateway/routes/mcp.ts +319 -0
  231. package/packages/core/src/gateway/routes/meeting.ts +232 -0
  232. package/packages/core/src/gateway/routes/models.ts +163 -0
  233. package/packages/core/src/gateway/routes/multimodal.ts +93 -0
  234. package/packages/core/src/gateway/routes/providers.ts +220 -0
  235. package/packages/core/src/gateway/routes/setup.ts +441 -0
  236. package/packages/core/src/gateway/routes/skills.ts +115 -0
  237. package/packages/core/src/gateway/routes/system.ts +469 -0
  238. package/packages/core/src/gateway/routes/tasks.ts +44 -0
  239. package/packages/core/src/gateway/routes/tools.ts +59 -0
  240. package/packages/core/src/gateway/routes/tts-local.ts +388 -0
  241. package/packages/core/src/gateway/routes/users.ts +122 -0
  242. package/packages/core/src/gateway/routes/voice.ts +189 -0
  243. package/packages/core/src/gateway/routes/workspace.ts +281 -0
  244. package/packages/core/src/gateway/server.ts +2744 -0
  245. package/packages/core/src/gateway/session.ts +95 -0
  246. package/packages/core/src/gateway/slash-commands.ts +207 -0
  247. package/packages/core/src/gateway/tts/README.md +94 -0
  248. package/packages/core/src/gateway/tts/package.json +25 -0
  249. package/packages/core/src/gateway/tts/src/client.ts +59 -0
  250. package/packages/core/src/gateway/tts/src/detect.ts +42 -0
  251. package/packages/core/src/gateway/tts/src/index.ts +15 -0
  252. package/packages/core/src/gateway/tts/src/install.ts +129 -0
  253. package/packages/core/src/gateway/tts/src/models.ts +50 -0
  254. package/packages/core/src/gateway/tts/src/server.ts +252 -0
  255. package/packages/core/src/gateway/tts/voices/.gitkeep +0 -0
  256. package/packages/core/src/heartbeat/index.ts +157 -0
  257. package/packages/core/src/index.ts +56 -0
  258. package/packages/core/src/mcp/hot-reload.ts +148 -0
  259. package/packages/core/src/mcp/singleton.ts +21 -0
  260. package/packages/core/src/mcp/tool-sync.ts +176 -0
  261. package/packages/core/src/multimodal/index.ts +2 -0
  262. package/packages/core/src/multimodal/types.ts +28 -0
  263. package/packages/core/src/multimodal/vision-service.ts +283 -0
  264. package/packages/core/src/plugins/api.ts +128 -0
  265. package/packages/core/src/plugins/index.ts +2 -0
  266. package/packages/core/src/plugins/loader.ts +365 -0
  267. package/packages/core/src/resilience/circuit-breaker.ts +225 -0
  268. package/packages/core/src/scheduler/CronScheduler.ts +699 -0
  269. package/packages/core/src/scheduler/dag/AgentExecutor.ts +53 -0
  270. package/packages/core/src/scheduler/dag/DAGScheduler.ts +250 -0
  271. package/packages/core/src/scheduler/dag/EventBridge.ts +122 -0
  272. package/packages/core/src/scheduler/dag/TaskGraph.ts +192 -0
  273. package/packages/core/src/scheduler/dag/TaskNode.ts +97 -0
  274. package/packages/core/src/scheduler/dag/TaskResult.ts +22 -0
  275. package/packages/core/src/scheduler/dag/errors.ts +37 -0
  276. package/packages/core/src/scheduler/dag/index.ts +26 -0
  277. package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +97 -0
  278. package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +21 -0
  279. package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +46 -0
  280. package/packages/core/src/scheduler/index.ts +22 -0
  281. package/packages/core/src/scheduler/integration.ts +237 -0
  282. package/packages/core/src/scheduler/types.ts +164 -0
  283. package/packages/core/src/security/google-chat.ts +269 -0
  284. package/packages/core/src/security/index.ts +192 -0
  285. package/packages/core/src/security/pairing.ts +250 -0
  286. package/packages/core/src/security/rate-limit.ts +270 -0
  287. package/packages/core/src/security/signal.ts +321 -0
  288. package/packages/core/src/state/store.ts +312 -0
  289. package/packages/core/src/storage/crypto.ts +197 -0
  290. package/packages/core/src/storage/migrate.ts +147 -0
  291. package/packages/core/src/storage/onboarding.ts +1506 -0
  292. package/packages/core/src/storage/schema.ts +666 -0
  293. package/packages/core/src/storage/seed.ts +628 -0
  294. package/packages/core/src/storage/sqlite.ts +407 -0
  295. package/packages/core/src/storage/usage.ts +374 -0
  296. package/packages/core/src/tool-runtime/index.ts +502 -0
  297. package/packages/core/src/tool-runtime/tool-worker.ts +125 -0
  298. package/packages/core/src/tools/agents/get-available-models.ts +118 -0
  299. package/packages/core/src/tools/agents/index.ts +610 -0
  300. package/packages/core/src/tools/canvas/index.ts +420 -0
  301. package/packages/core/src/tools/cli/index.ts +142 -0
  302. package/packages/core/src/tools/core/index.ts +478 -0
  303. package/packages/core/src/tools/cron/index.ts +635 -0
  304. package/packages/core/src/tools/filesystem/fs-delete.ts +78 -0
  305. package/packages/core/src/tools/filesystem/fs-edit.ts +106 -0
  306. package/packages/core/src/tools/filesystem/fs-exists.ts +63 -0
  307. package/packages/core/src/tools/filesystem/fs-glob.ts +108 -0
  308. package/packages/core/src/tools/filesystem/fs-list.ts +129 -0
  309. package/packages/core/src/tools/filesystem/fs-read.ts +72 -0
  310. package/packages/core/src/tools/filesystem/fs-write.ts +67 -0
  311. package/packages/core/src/tools/filesystem/index.ts +34 -0
  312. package/packages/core/src/tools/filesystem/workspace-guard.ts +62 -0
  313. package/packages/core/src/tools/index.ts +197 -0
  314. package/packages/core/src/tools/meeting/index.ts +363 -0
  315. package/packages/core/src/tools/office/index.ts +47 -0
  316. package/packages/core/src/tools/office/office-escribir-docx.ts +192 -0
  317. package/packages/core/src/tools/office/office-escribir-pdf.ts +172 -0
  318. package/packages/core/src/tools/office/office-escribir-pptx.ts +174 -0
  319. package/packages/core/src/tools/office/office-escribir-xlsx.ts +116 -0
  320. package/packages/core/src/tools/office/office-leer-docx.ts +93 -0
  321. package/packages/core/src/tools/office/office-leer-pdf.ts +114 -0
  322. package/packages/core/src/tools/office/office-leer-pptx.ts +136 -0
  323. package/packages/core/src/tools/office/office-leer-xlsx.ts +124 -0
  324. package/packages/core/src/tools/types.ts +39 -0
  325. package/packages/core/src/tools/voice/index.ts +104 -0
  326. package/packages/core/src/tools/web/browser-click.ts +78 -0
  327. package/packages/core/src/tools/web/browser-extract.ts +139 -0
  328. package/packages/core/src/tools/web/browser-navigate.ts +106 -0
  329. package/packages/core/src/tools/web/browser-screenshot.ts +87 -0
  330. package/packages/core/src/tools/web/browser-script.ts +88 -0
  331. package/packages/core/src/tools/web/browser-service.ts +554 -0
  332. package/packages/core/src/tools/web/browser-type.ts +101 -0
  333. package/packages/core/src/tools/web/browser-wait.ts +136 -0
  334. package/packages/core/src/tools/web/index.ts +41 -0
  335. package/packages/core/src/tools/web/web-fetch.ts +78 -0
  336. package/packages/core/src/tools/web/web-search.ts +123 -0
  337. package/packages/core/src/utils/benchmark.ts +80 -0
  338. package/packages/core/src/utils/crypto.ts +73 -0
  339. package/packages/core/src/utils/date.ts +42 -0
  340. package/packages/core/src/utils/index.ts +5 -0
  341. package/packages/core/src/utils/logger.ts +389 -0
  342. package/packages/core/src/utils/retry.ts +70 -0
  343. package/packages/core/src/utils/toon.ts +253 -0
  344. package/packages/core/src/voice/index.ts +643 -0
  345. package/packages/mcp/src/config.ts +13 -0
  346. package/packages/mcp/src/index.ts +1 -0
  347. package/packages/mcp/src/logger.ts +47 -0
  348. package/packages/mcp/src/manager.ts +439 -0
  349. package/packages/mcp/src/transports/index.ts +67 -0
  350. package/packages/mcp/src/transports/sse.ts +238 -0
  351. package/packages/mcp/src/transports/websocket.ts +159 -0
  352. package/packages/skills/src/bundled/agents/agent_spawner/SKILL.md +167 -0
  353. package/packages/skills/src/bundled/agents/code_delegator/SKILL.md +156 -0
  354. package/packages/skills/src/bundled/agents/memory_manager/SKILL.md +143 -0
  355. package/packages/skills/src/bundled/agents/research_and_remember/SKILL.md +139 -0
  356. package/packages/skills/src/bundled/agents/task_orchestrator/SKILL.md +198 -0
  357. package/packages/skills/src/bundled/canvas/a2ui_dashboard/SKILL.md +176 -0
  358. package/packages/skills/src/bundled/canvas/a2ui_form/SKILL.md +202 -0
  359. package/packages/skills/src/bundled/canvas/a2ui_interactive/SKILL.md +206 -0
  360. package/packages/skills/src/bundled/canvas/canvas_dashboard/SKILL.md +146 -0
  361. package/packages/skills/src/bundled/canvas/canvas_interact/SKILL.md +148 -0
  362. package/packages/skills/src/bundled/canvas/canvas_report/SKILL.md +146 -0
  363. package/packages/skills/src/bundled/cli/cli_pipeline/SKILL.md +136 -0
  364. package/packages/skills/src/bundled/cli/cli_safe_exec/SKILL.md +125 -0
  365. package/packages/skills/src/bundled/cron_manager/SKILL.md +188 -0
  366. package/packages/skills/src/bundled/cron_reminder/SKILL.md +112 -0
  367. package/packages/skills/src/bundled/filesystem/file_manager/SKILL.md +118 -0
  368. package/packages/skills/src/bundled/filesystem/file_read_and_summarize/SKILL.md +108 -0
  369. package/packages/skills/src/bundled/filesystem/file_writer/SKILL.md +135 -0
  370. package/packages/skills/src/bundled/meeting/meeting_transcription/SKILL.md +213 -0
  371. package/packages/skills/src/bundled/office/office_document_manager/SKILL.md +262 -0
  372. package/packages/skills/src/bundled/search_knowledge/busqueda_fts5/SKILL.md +74 -0
  373. package/packages/skills/src/bundled/voice/voice_assistant/SKILL.md +174 -0
  374. package/packages/skills/src/bundled/voice/voice_input/SKILL.md +146 -0
  375. package/packages/skills/src/bundled/voice/voice_output/SKILL.md +151 -0
  376. package/packages/skills/src/bundled/web/browser_automate/SKILL.md +120 -0
  377. package/packages/skills/src/bundled/web/browser_scrape/SKILL.md +109 -0
  378. package/packages/skills/src/bundled/web/web_monitor/SKILL.md +127 -0
  379. package/packages/skills/src/bundled/web/web_research/SKILL.md +119 -0
  380. package/packages/skills/src/bundled-data.generated.ts +1964 -0
  381. package/packages/skills/src/index.ts +1 -0
  382. package/packages/skills/src/loader.ts +388 -0
  383. package/dist/ui/assets/AgentNewPage-GB-tVN50.js +0 -1
  384. package/dist/ui/assets/BridgePage-DDcDILKu.js +0 -1
  385. package/dist/ui/assets/CanvasPage-oOk2sGOD.js +0 -33
  386. package/dist/ui/assets/DashboardPage-DV_2qWYJ.js +0 -6
  387. package/dist/ui/assets/LogsPage-DayYjh01.js +0 -1
  388. package/dist/ui/assets/MeetingPage-C01uPuqj.js +0 -1
  389. package/dist/ui/assets/ProjectsPage-B8_am_Ib.js +0 -1
  390. package/dist/ui/assets/ProvidersPage-DBzi66e4.js +0 -1
  391. package/dist/ui/assets/SettingsPage-CFA_Tknl.js +0 -9
  392. package/dist/ui/assets/SetupPage-BrUWbhvT.js +0 -1
  393. package/dist/ui/assets/accordion-DdAEfIXR.js +0 -1
  394. package/dist/ui/assets/chevron-down-DIosfU_U.js +0 -1
  395. package/dist/ui/assets/chevron-up-CI-W21Fy.js +0 -1
  396. package/dist/ui/assets/circle-S0-ouLz-.js +0 -1
  397. package/dist/ui/assets/circle-minus-CE0iJrl8.js +0 -1
  398. package/dist/ui/assets/circle-x-jUJ5zZvQ.js +0 -1
  399. package/dist/ui/assets/dropdown-menu-C2CXM1VE.js +0 -1
  400. package/dist/ui/assets/index-BN0875JH.css +0 -2
  401. package/dist/ui/assets/index-CH6sBa3Q.js +0 -116
  402. package/dist/ui/assets/pencil-5VdSj-h5.js +0 -1
  403. package/dist/ui/assets/progress-JN30I5fF.js +0 -1
  404. package/dist/ui/assets/scroll-area-BQQPitM8.js +0 -1
  405. package/dist/ui/assets/search-ChPgnVKj.js +0 -1
  406. package/dist/ui/assets/switch-C7W2-KEx.js +0 -1
  407. package/dist/ui/assets/terminal-C-R5Fckz.js +0 -1
  408. package/dist/ui/assets/useProviders-TBnWn-Hq.js +0 -1
  409. /package/dist/ui/assets/{card-DFKnZ6ky.js → card-CNf6BS2e.js} +0 -0
  410. /package/dist/ui/assets/{circle-alert-KuAm2FWh.js → circle-alert-CyHDwUj8.js} +0 -0
  411. /package/dist/ui/assets/{circle-check-6Ard1-2z.js → circle-check-Bb54Ebmu.js} +0 -0
  412. /package/dist/ui/assets/{cpu-KDy6-FAI.js → cpu-Cdgc_B1K.js} +0 -0
  413. /package/dist/ui/assets/{download-Cjbk4Rek.js → download-C3ifGMjJ.js} +0 -0
  414. /package/dist/ui/assets/{external-link-HtrFM63g.js → external-link-BvxYeTP1.js} +0 -0
  415. /package/dist/ui/assets/{eye-D1dB40_o.js → eye-DqNTU_GD.js} +0 -0
  416. /package/dist/ui/assets/{file-text-CE58EfH0.js → file-text-BT_9S9SM.js} +0 -0
  417. /package/dist/ui/assets/{folder-open-DIPKeiI_.js → folder-open-BhH8y9ac.js} +0 -0
  418. /package/dist/ui/assets/{format-BwdV8bB5.js → format-GVHeOyWI.js} +0 -0
  419. /package/dist/ui/assets/{gateway-url-D5uj6Nxg.js → gateway-url-COCbW0IR.js} +0 -0
  420. /package/dist/ui/assets/{gauge-DmQmJHEg.js → gauge-D_TMa4i9.js} +0 -0
  421. /package/dist/ui/assets/{globe-_hUGxQF4.js → globe-DeCQTCDJ.js} +0 -0
  422. /package/dist/ui/assets/{hexagon-BaNGQlQj.js → hexagon-DsGOUl-H.js} +0 -0
  423. /package/dist/ui/assets/{history-BfZVGlZa.js → history-BSG-Ypqf.js} +0 -0
  424. /package/dist/ui/assets/{info-CBZ5-AlC.js → info-NwLoa2Mj.js} +0 -0
  425. /package/dist/ui/assets/{key-Bv5DdTPh.js → key-3EP0dhkT.js} +0 -0
  426. /package/dist/ui/assets/{loader-circle-C4hhXLgp.js → loader-circle-CZNax6kS.js} +0 -0
  427. /package/dist/ui/assets/{lock-CkZYexqw.js → lock-Ei1_J-Nq.js} +0 -0
  428. /package/dist/ui/assets/{pause-Bpy1_s7y.js → pause-BUqah9Bi.js} +0 -0
  429. /package/dist/ui/assets/{play-Cj4osqJZ.js → play-NcZ4swwL.js} +0 -0
  430. /package/dist/ui/assets/{plus-BQhgZN3A.js → plus-CX1xyhp5.js} +0 -0
  431. /package/dist/ui/assets/{refresh-cw-BfREHVQM.js → refresh-cw-DaYdjQFk.js} +0 -0
  432. /package/dist/ui/assets/{save-FFTD4dMp.js → save-CUdYyHNy.js} +0 -0
  433. /package/dist/ui/assets/{settings-BdHKUL92.js → settings-Ds4SqD8s.js} +0 -0
  434. /package/dist/ui/assets/{sparkles-r4uJbJAl.js → sparkles-yUEb-7oH.js} +0 -0
  435. /package/dist/ui/assets/{square-G7Hyufqm.js → square-BD81nFtN.js} +0 -0
  436. /package/dist/ui/assets/{textarea-5kyuD04X.js → textarea-CXgXWKrT.js} +0 -0
  437. /package/dist/ui/assets/{trash-2-DXVBRWfh.js → trash-2-CNjMkoq6.js} +0 -0
  438. /package/dist/ui/assets/{triangle-alert-Bu5seg9O.js → triangle-alert-C9Y8Ub4X.js} +0 -0
  439. /package/dist/ui/assets/{vendor-router-CCECILJ0.js → vendor-router-C9pIYwbJ.js} +0 -0
  440. /package/dist/ui/assets/{volume-2-s9DuS696.js → volume-2-CeSXNDv4.js} +0 -0
  441. /package/dist/ui/assets/{zap-BPHZzXKV.js → zap-hlXjpSeA.js} +0 -0
@@ -0,0 +1,479 @@
1
+ /**
2
+ * FTS5-based Dynamic Skill Selector Module
3
+ *
4
+ * Context Compiler Level 4 - Intelligent Skill Selection
5
+ *
6
+ * This module uses SQLite FTS5 bm25() scoring to select the most relevant
7
+ * skills (0-5) based on the user message, similar to tool selection.
8
+ *
9
+ * DESIGN DECISIONS:
10
+ *
11
+ * 1. Reads from skills table in database (not hardcoded catalog)
12
+ * 2. Maximum 5 skills per turn for balanced context injection
13
+ * 3. Relevance threshold for conversational messages
14
+ * 4. Uses skill descriptions for FTS5 matching
15
+ * 5. Returns skill content for injection into system prompt
16
+ */
17
+
18
+ import { getDb } from "../storage/sqlite"
19
+ import { logger } from "../utils/logger"
20
+
21
+ const log = logger.child("skill-selector")
22
+
23
+ // ─── Minimal Skill Set ─────────────────────────────────────────────────────────
24
+
25
+ /**
26
+ * Skills mínimas que SIEMPRE están disponibles (asociadas a las 4 tools iniciales)
27
+ * - memory_manager: usa save_note (notas persistentes)
28
+ * - canvas_report: usa report_progress (reportes de progreso)
29
+ * - task_orchestrator: usa notify (comunicación entre agentes)
30
+ */
31
+ export const MINIMAL_SKILL_NAMES = new Set([
32
+ "busqueda_fts5", // Core: cómo descubrir tools, MCP, skills, playbook
33
+ "memory_manager", // Asociada a save_note
34
+ "canvas_report", // Asociada a report_progress
35
+ "task_orchestrator", // Asociada a notify y agent coordination
36
+ ])
37
+
38
+ // ─── Types ───────────────────────────────────────────────────────────────────────
39
+
40
+ export interface SkillDescriptor {
41
+ id: string
42
+ name: string
43
+ description: string
44
+ category: string
45
+ tools: string
46
+ triggers: string
47
+ preferred_agents: string
48
+ body: string
49
+ version: string
50
+ version_num: number
51
+ active: number
52
+ }
53
+
54
+ export interface SelectedSkill {
55
+ id: string
56
+ name: string
57
+ score: number
58
+ category: string
59
+ description: string
60
+ body: string
61
+ }
62
+
63
+ export interface SkillSelectorResult {
64
+ skills: SkillDescriptor[]
65
+ selected: SelectedSkill[]
66
+ reasoning: string
67
+ timingMs: number
68
+ }
69
+
70
+ // ─── Configuration ─────────────────────────────────────────────────────────
71
+
72
+ /** Maximum skills to return per message */
73
+ const MAX_SKILLS_PER_TURN = 4 // Increased from 2 to allow more skills
74
+
75
+ /**
76
+ * Minimum bm25 score threshold. Below this = conversational, no skills needed.
77
+ *
78
+ * CRITICAL: bm25() returns NEGATIVE scores where closer to 0 = more relevant.
79
+ * - Score of -5 is MORE relevant than -20
80
+ * - We use -15 as threshold to allow reasonable matching while filtering noise
81
+ */
82
+ const MIN_RELEVANCE_THRESHOLD = -15 // Increased from -5 to allow more matches
83
+
84
+ /** Stopwords to filter out before FTS5 query construction */
85
+ const STOPWORDS = new Set([
86
+ "que", "con", "para", "por", "una", "uno", "los", "las", "del",
87
+ "como", "esta", "esto", "ese", "eso", "the", "and", "for",
88
+ "with", "this", "that", "have", "will", "also", "de", "en",
89
+ "el", "la", "se", "su", "sus", "al", "es", "son", "pero",
90
+ "más", "mas", "ya", "yo", "tu", "te", "ti", "mi", "me",
91
+ "hola", "hi", "hello", "hey", "gracias", "thank", "please",
92
+ "ok", "okay", "yes", "si", "no", "bien", "good", "great",
93
+ "puedes", "necesito", "quiero", "podés", "necesitás", "querés",
94
+ ])
95
+
96
+ /** Conversational patterns that should return empty skill list */
97
+ const CONVERSATIONAL_PATTERNS = [
98
+ /^(hola|hi|hello|hey|buenos? días?|buenas? noches?|qué tal|howdy)/i,
99
+ /^(gracias|thank you|thanks|muchas gracias|muchas thanks)/i,
100
+ /^(cómo estás?|how are you?|qué流水|you doing|qué cuentas)/i,
101
+ /^(sí|yes|ok|okay|de acuerdo|perfecto|claro|por supuesto)/i,
102
+ /^(adiós|bye|nos vemos|see you|later|chau)/i,
103
+ /^(entiendo|understand|i see|ya veo|got it)/i,
104
+ /^(bien|good|great|excelente|awesome|perfect)/i,
105
+ /^(?:\?|¿)$/, // Just a question mark
106
+ ]
107
+
108
+ // ─── Helper Functions ───────────────────────────────────────────────────────
109
+
110
+ /**
111
+ * Check if message is purely conversational (no skills needed)
112
+ */
113
+ function isConversational(message: string): boolean {
114
+ const trimmed = message.trim()
115
+
116
+ // Empty or very short messages
117
+ if (trimmed.length < 2) return true
118
+
119
+ // Check conversational patterns
120
+ for (const pattern of CONVERSATIONAL_PATTERNS) {
121
+ if (pattern.test(trimmed)) {
122
+ log.debug(`[skill-selector] Message matched conversational pattern: ${pattern}`)
123
+ return true
124
+ }
125
+ }
126
+
127
+ // Check if all words are stopwords (likely conversational)
128
+ const words = trimmed.toLowerCase().split(/\s+/)
129
+ const meaningfulWords = words.filter(w => w.length > 2 && !STOPWORDS.has(w))
130
+ if (meaningfulWords.length === 0) {
131
+ log.debug(`[skill-selector] All words are stopwords - conversational`)
132
+ return true
133
+ }
134
+
135
+ return false
136
+ }
137
+
138
+ /**
139
+ * Build FTS5 query from user message
140
+ *
141
+ * Uses prefix matching for better recall:
142
+ * - "generar" matches "generando", "generación", "genera"
143
+ * - "código" matches "codigos", "codificar"
144
+ */
145
+ function buildFTSQuery(message: string): string {
146
+ const words = message
147
+ .toLowerCase()
148
+ .replace(/[^\p{L}\p{N}\s]/gu, " ")
149
+ .split(/\s+/)
150
+ .filter((w) => w.length > 2 && !STOPWORDS.has(w))
151
+ .slice(0, 8)
152
+
153
+ if (words.length === 0) return ""
154
+
155
+ // Use prefix matching for better recall (e.g., "gener*" matches "generar", "generando", "generación")
156
+ return words.map(w => `${w}*`).join(" OR ")
157
+ }
158
+
159
+ /**
160
+ * Check if message matches explicit triggers from a skill
161
+ */
162
+ function matchTriggers(message: string, triggersJson: string | null): boolean {
163
+ if (!triggersJson) return false
164
+
165
+ try {
166
+ // Triggers are stored as comma-separated string in DB (e.g., "trigger1,trigger2")
167
+ const triggers: string[] = triggersJson.split(",").map(t => t.trim()).filter(t => t.length > 0)
168
+ if (triggers.length === 0) return false
169
+
170
+ const lowerMessage = message.toLowerCase()
171
+ return triggers.some(trigger =>
172
+ lowerMessage.includes(trigger.toLowerCase())
173
+ )
174
+ } catch (err) {
175
+ log.warn(`[skill-selector] Failed to parse triggers: ${(err as Error).message}`)
176
+ return false
177
+ }
178
+ }
179
+
180
+ // ─── Main Selection Function ─────────────────────────────────────────────────
181
+
182
+ /**
183
+ * Select skills for a given user message using hybrid matching:
184
+ * 1. First check explicit triggers (high confidence match)
185
+ * 2. Fallback to FTS5 bm25() scoring for semantic matching
186
+ *
187
+ * @param userMessage - The raw user message
188
+ * @returns Array of 0-5 selected skills with scores
189
+ *
190
+ * ALGORITHM:
191
+ * 1. If conversational → return []
192
+ * 2. Check explicit triggers from all enabled skills
193
+ * 3. If trigger match found → return matching skill immediately
194
+ * 4. Build FTS5 query from message keywords
195
+ * 5. Query skills_fts with bm25() scoring
196
+ * 6. Filter results below MIN_RELEVANCE_THRESHOLD
197
+ * 7. Return top MAX_SKILLS_PER_TURN results
198
+ */
199
+ export function selectSkills(userMessage: string): SkillDescriptor[] {
200
+ const startTime = performance.now()
201
+
202
+ log.debug(`[skill-selector] Processing user message: "${userMessage.substring(0, 100)}"`)
203
+
204
+ // Step 1: Check if conversational
205
+ if (isConversational(userMessage)) {
206
+ log.debug(`[skill-selector] Conversational message, returning empty array`)
207
+ return []
208
+ }
209
+
210
+ // Step 2: Check explicit triggers first (high priority)
211
+ const db = getDb()
212
+ const allSkills = db.query(`
213
+ SELECT id, name, description, category, tools, triggers, preferred_agents, body, version, version_num, active
214
+ FROM skills
215
+ WHERE active = 1
216
+ `).all() as SkillDescriptor[]
217
+
218
+ // Check trigger match - if found, return immediately with high confidence
219
+ for (const skill of allSkills) {
220
+ if (skill.triggers && matchTriggers(userMessage, skill.triggers)) {
221
+ log.info(`[skill-selector] Trigger match found: ${skill.name}`)
222
+ return [skill]
223
+ }
224
+ }
225
+
226
+ // Step 3: Build FTS5 query for semantic matching
227
+ const ftsQuery = buildFTSQuery(userMessage)
228
+ if (!ftsQuery) {
229
+ log.debug(`[skill-selector] No valid FTS query terms, returning empty array`)
230
+ return []
231
+ }
232
+
233
+ log.debug(`[skill-selector] FTS query: "${ftsQuery}"`)
234
+
235
+ // Step 4: Execute FTS5 query with bm25 scoring
236
+ // Use bm25() with column weights for relevance scoring
237
+ // FTS5 table columns: id, name, description, category, tools, triggers, body
238
+ // Weights: id=1.0, name=4.0, description=5.0, category=1.0, tools=1.0, triggers=5.0, body=2.0
239
+ // Higher weight on description (5.0) and triggers (5.0) for best semantic matching
240
+ const ftsResults = db.query(`
241
+ SELECT id, bm25(skills_fts, 1.0, 4.0, 5.0, 1.0, 1.0, 5.0, 2.0) as bm25_score
242
+ FROM skills_fts
243
+ WHERE skills_fts MATCH ?
244
+ ORDER BY bm25_score ASC
245
+ LIMIT 20
246
+ `).all(ftsQuery) as { id: string; bm25_score: number }[]
247
+
248
+ if (ftsResults.length === 0) {
249
+ log.debug(`[skill-selector] No FTS matches, returning empty array`)
250
+ return []
251
+ }
252
+
253
+ // Log raw scores for debugging
254
+ log.info(`[skill-selector] Raw FTS scores: ${ftsResults.slice(0, 10).map(r => `id=${r.id}, score=${r.bm25_score.toFixed(2)}`).join(", ")}`)
255
+
256
+ // Step 5: Apply relevance threshold filter
257
+ const relevantResults = ftsResults.filter(r => r.bm25_score >= MIN_RELEVANCE_THRESHOLD)
258
+
259
+ if (relevantResults.length === 0) {
260
+ log.debug(`[skill-selector] All results below threshold ${MIN_RELEVANCE_THRESHOLD}, returning empty`)
261
+ return []
262
+ }
263
+
264
+ // Step 6: Fetch full skill details from database
265
+ const skillIds = relevantResults.map(r => r.id)
266
+
267
+ let dbSkills: SkillDescriptor[] = []
268
+ try {
269
+ const db = getDb()
270
+ dbSkills = db.query(`
271
+ SELECT id, name, description, category, tools, triggers, preferred_agents, body, version, version_num, active
272
+ FROM skills
273
+ WHERE id IN (${skillIds.map(() => '?').join(',')})
274
+ AND active = 1
275
+ `).all(...skillIds) as SkillDescriptor[]
276
+ } catch (err) {
277
+ log.warn(`[skill-selector] Failed to fetch skills from DB:`, err)
278
+ return []
279
+ }
280
+
281
+ // Map scores to skills
282
+ const skillMap = new Map(dbSkills.map(s => [s.id, s]))
283
+ const scoredSkills: SelectedSkill[] = []
284
+
285
+ for (const result of relevantResults) {
286
+ const skill = skillMap.get(result.id)
287
+ if (skill) {
288
+ scoredSkills.push({
289
+ id: skill.id,
290
+ name: skill.name,
291
+ score: result.bm25_score,
292
+ category: skill.category,
293
+ description: skill.description || "",
294
+ body: skill.body,
295
+ })
296
+ }
297
+ }
298
+
299
+ // Step 7: Take top N skills
300
+ const topSkills = scoredSkills.slice(0, MAX_SKILLS_PER_TURN)
301
+
302
+ // Step 8: Return as SkillDescriptor array
303
+ const result = topSkills.map(t => skillMap.get(t.id)!).filter(Boolean)
304
+
305
+ const timing = performance.now() - startTime
306
+
307
+ if (result.length > 0) {
308
+ log.info(`[skill-selector] Selected ${result.length} skills in ${timing.toFixed(2)}ms:`,
309
+ result.map(s => ({ name: s.name, category: s.category })))
310
+ } else {
311
+ log.debug(`[skill-selector] No skills selected, returning empty array in ${timing.toFixed(2)}ms`)
312
+ }
313
+
314
+ return result
315
+ }
316
+
317
+ // ─── Minimal Skills Loader ───────────────────────────────────────────────────
318
+
319
+ /**
320
+ * Load minimal skills that are ALWAYS available (associated with MINIMAL_TOOLS)
321
+ * These are loaded at startup, not via FTS5 search.
322
+ *
323
+ * @returns Array of minimal skills (memory_manager, canvas_report, task_orchestrator)
324
+ */
325
+ export function getMinimalSkills(): SkillDescriptor[] {
326
+ const db = getDb()
327
+
328
+ try {
329
+ const placeholders = Array.from(MINIMAL_SKILL_NAMES).map(() => "?").join(",")
330
+ const skills = db.query(`
331
+ SELECT id, name, description, category, tools, triggers, preferred_agents, body, version, version_num, active
332
+ FROM skills
333
+ WHERE name IN (${placeholders})
334
+ AND active = 1
335
+ `).all(...MINIMAL_SKILL_NAMES) as SkillDescriptor[]
336
+
337
+ log.info(`[skill-selector] Loaded ${skills.length} minimal skills: ${skills.map(s => s.name).join(", ")}`)
338
+ return skills
339
+ } catch (err) {
340
+ log.error(`[skill-selector] Failed to load minimal skills:`, err)
341
+ return []
342
+ }
343
+ }
344
+
345
+ // ─── Sync Skills to FTS5 ───────────────────────────────────────────────────
346
+
347
+ /**
348
+ * Sync all enabled skills from database to FTS5
349
+ * Should be called on initialization from gateway/initializer.ts
350
+ * The skills_fts table is created by schema.ts (v0.0.28 includes description)
351
+ */
352
+ export async function syncSkillsToFTS(): Promise<void> {
353
+ const db = getDb()
354
+
355
+ try {
356
+ // Step 1: Get all enabled skills from database (v0.0.28 schema with description)
357
+ const dbSkills = db.query(`
358
+ SELECT id, name, description, category, tools, triggers, body
359
+ FROM skills
360
+ WHERE active = 1
361
+ `).all() as Array<{
362
+ id: string
363
+ name: string
364
+ description: string
365
+ category: string
366
+ tools: string
367
+ triggers: string
368
+ body: string
369
+ }>
370
+
371
+ if (dbSkills.length === 0) {
372
+ log.debug(`[skill-selector] No skills found in DB to sync`)
373
+ }
374
+
375
+ // Step 2: Atomic transaction for FTS5 sync
376
+ const syncTransaction = db.transaction(() => {
377
+ // Verify table exists
378
+ const tableCheck = db.query("SELECT name FROM sqlite_master WHERE type='table' AND name='skills_fts'").get()
379
+ if (!tableCheck) {
380
+ throw new Error("skills_fts table does not exist!")
381
+ }
382
+
383
+ // A: Clear existing data
384
+ db.run("DELETE FROM skills_fts")
385
+
386
+ // B: Prepare insertion (v0.0.28 schema with description)
387
+ const insert = db.prepare(`
388
+ INSERT INTO skills_fts(id, name, description, category, tools, triggers, body)
389
+ VALUES (?, ?, ?, ?, ?, ?, ?)
390
+ `)
391
+
392
+ // C: Re-populate
393
+ for (const skill of dbSkills) {
394
+ insert.run(
395
+ skill.id,
396
+ skill.name,
397
+ skill.description || "",
398
+ skill.category,
399
+ skill.tools,
400
+ skill.triggers,
401
+ skill.body
402
+ )
403
+ }
404
+ })
405
+
406
+ // Execute transaction
407
+ syncTransaction()
408
+
409
+ log.info(`[skill-selector] Atomic sync complete: ${dbSkills.length} skills indexed in FTS5`)
410
+
411
+ } catch (err) {
412
+ log.error(`[skill-selector] Transactional sync failed:`, err)
413
+ throw err // Re-throw to inform initializer
414
+ }
415
+ }
416
+ // ─── Initialization ───────────────────────────────────────────────────────
417
+
418
+ /**
419
+ * Initialize the skill selector
420
+ * DEPRECATED: syncSkillsToFTS() is now called from gateway/initializer.ts
421
+ * This function is kept for backward compatibility but is no longer needed
422
+ */
423
+ export function initializeSkillSelector(): void {
424
+ log.info(`[skill-selector] Initializing skill selector (deprecated - sync is done in gateway/initializer.ts)`)
425
+ // syncSkillsToFTS() - No longer needed here, done in gateway/initializer.ts
426
+ }
427
+
428
+ // ─── Debug/Test Helpers ─────────────────────────────────────────────────────
429
+
430
+ /**
431
+ * Get all enabled skills from database (for debugging/testing)
432
+ */
433
+ export function getAllSkillsFromDB(): SkillDescriptor[] {
434
+ try {
435
+ const db = getDb()
436
+ return db.query(`
437
+ SELECT id, name, description, category, tools, triggers, preferred_agents, body, version, version_num, active
438
+ FROM skills
439
+ WHERE active = 1
440
+ `).all() as SkillDescriptor[]
441
+ } catch (err) {
442
+ log.error(`[skill-selector] Failed to fetch skills:`, err)
443
+ return []
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Get skill by name
449
+ */
450
+ export function getSkillByName(name: string): SkillDescriptor | undefined {
451
+ try {
452
+ const db = getDb()
453
+ return db.query(`
454
+ SELECT id, name, description, category, tools, triggers, preferred_agents, body, version, version_num, active
455
+ FROM skills
456
+ WHERE name = ? AND active = 1
457
+ `).get(name) as SkillDescriptor | undefined
458
+ } catch (err) {
459
+ log.error(`[skill-selector] Failed to fetch skill by name:`, err)
460
+ return undefined
461
+ }
462
+ }
463
+
464
+ /**
465
+ * Get skills by category
466
+ */
467
+ export function getSkillsByCategory(category: string): SkillDescriptor[] {
468
+ try {
469
+ const db = getDb()
470
+ return db.query(`
471
+ SELECT id, name, description, category, tools, triggers, preferred_agents, body, version, version_num, active
472
+ FROM skills
473
+ WHERE category = ? AND active = 1
474
+ `).all(category) as SkillDescriptor[]
475
+ } catch (err) {
476
+ log.error(`[skill-selector] Failed to fetch skills by category:`, err)
477
+ return []
478
+ }
479
+ }
@@ -0,0 +1,133 @@
1
+ import type { Config } from "../config/loader.ts";
2
+ import { logger } from "../utils/logger.ts";
3
+ import { hashObject } from "../utils/crypto.ts";
4
+
5
+ interface ToolCallRecord {
6
+ toolName: string;
7
+ argsHash: string;
8
+ errorMessage?: string;
9
+ timestamp: number;
10
+ }
11
+
12
+ interface StuckLoopState {
13
+ detected: boolean;
14
+ toolName: string;
15
+ count: number;
16
+ lastError?: string;
17
+ }
18
+
19
+ export class StuckLoopDetector {
20
+ private log = logger.child("stuck-loop");
21
+ private history: Map<string, ToolCallRecord[]> = new Map();
22
+ private readonly maxHistoryPerSession = 50;
23
+ private readonly triggerThreshold = 3;
24
+
25
+ constructor(_config: Config) {}
26
+
27
+ recordToolCall(
28
+ sessionId: string,
29
+ toolName: string,
30
+ args: Record<string, unknown>,
31
+ error?: string
32
+ ): void {
33
+ let sessionHistory = this.history.get(sessionId);
34
+ if (!sessionHistory) {
35
+ sessionHistory = [];
36
+ this.history.set(sessionId, sessionHistory);
37
+ }
38
+
39
+ const record: ToolCallRecord = {
40
+ toolName,
41
+ argsHash: hashObject(args),
42
+ errorMessage: error,
43
+ timestamp: Date.now(),
44
+ };
45
+
46
+ sessionHistory.push(record);
47
+
48
+ if (sessionHistory.length > this.maxHistoryPerSession) {
49
+ sessionHistory.shift();
50
+ }
51
+
52
+ this.log.debug(`Recorded tool call: ${toolName} for session ${sessionId}`);
53
+ }
54
+
55
+ check(sessionId: string): StuckLoopState {
56
+ const sessionHistory = this.history.get(sessionId) ?? [];
57
+
58
+ if (sessionHistory.length < this.triggerThreshold) {
59
+ return { detected: false, toolName: "", count: 0 };
60
+ }
61
+
62
+ const recent = sessionHistory.slice(-10);
63
+ const counts = new Map<string, { count: number; error?: string }>();
64
+
65
+ for (const record of recent) {
66
+ const key = `${record.toolName}:${record.argsHash}`;
67
+ const existing = counts.get(key);
68
+
69
+ if (existing) {
70
+ existing.count++;
71
+ if (record.errorMessage) {
72
+ existing.error = record.errorMessage;
73
+ }
74
+ } else {
75
+ counts.set(key, { count: 1, error: record.errorMessage });
76
+ }
77
+ }
78
+
79
+ for (const [key, data] of counts) {
80
+ if (data.count >= this.triggerThreshold && data.error) {
81
+ const toolName = key.split(":")[0] ?? "unknown";
82
+
83
+ this.log.warn(`Stuck loop detected: ${toolName} called ${data.count} times with same args and error`);
84
+
85
+ return {
86
+ detected: true,
87
+ toolName,
88
+ count: data.count,
89
+ lastError: data.error,
90
+ };
91
+ }
92
+ }
93
+
94
+ return { detected: false, toolName: "", count: 0 };
95
+ }
96
+
97
+ getInterventionMessage(state: StuckLoopState): string | null {
98
+ if (!state.detected) return null;
99
+
100
+ if (state.count >= this.triggerThreshold + 1) {
101
+ return `CRITICAL: You have called ${state.toolName} ${state.count} times with the same arguments and it keeps failing with: "${state.lastError}". The user has been notified. You MUST try a completely different approach or ask the user for guidance.`;
102
+ }
103
+
104
+ return `WARNING: You have called ${state.toolName} ${state.count} times with the same arguments and it keeps failing. You MUST try a completely different approach instead of repeating the same action.`;
105
+ }
106
+
107
+ clear(sessionId: string): void {
108
+ this.history.delete(sessionId);
109
+ this.log.debug(`Cleared stuck loop history for session ${sessionId}`);
110
+ }
111
+
112
+ prune(maxAgeMs: number = 30 * 60 * 1000): number {
113
+ const now = Date.now();
114
+ let pruned = 0;
115
+
116
+ for (const [sessionId, history] of this.history) {
117
+ const filtered = history.filter(r => now - r.timestamp < maxAgeMs);
118
+
119
+ if (filtered.length === 0) {
120
+ this.history.delete(sessionId);
121
+ pruned++;
122
+ } else if (filtered.length !== history.length) {
123
+ this.history.set(sessionId, filtered);
124
+ }
125
+ }
126
+
127
+ return pruned;
128
+ }
129
+ }
130
+
131
+ export function createStuckLoopDetector(config: Config): StuckLoopDetector {
132
+ return new StuckLoopDetector(config);
133
+ }