@johpaz/hive-agents 0.0.37 → 0.0.39

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 (223) hide show
  1. package/README.md +18 -18
  2. package/dist/hive.js +3529 -2702
  3. package/dist/tool-worker.js +2110 -1856
  4. package/dist/ui/assets/AgentCreateForm-BTCzFbca.js +1 -0
  5. package/dist/ui/assets/AgentDetailPage-o27TRSVw.js +1 -0
  6. package/dist/ui/assets/AgentNewPage-400cCpYt.js +1 -0
  7. package/dist/ui/{dist/assets/AgentsPage-YvSgWRiw.js → assets/AgentsPage-C-GSRk-N.js} +5 -5
  8. package/dist/ui/assets/ApiClientPage-BOTpz6oP.js +3 -0
  9. package/dist/ui/assets/{CanvasPage-DtMwGvxf.js → CanvasPage-Cvs5ctza.js} +7 -7
  10. package/dist/ui/assets/ChannelsPage-C5m_L7P9.js +8 -0
  11. package/dist/ui/{dist/assets/DashboardPage-ghl1ZguH.js → assets/DashboardPage-CztbRQdm.js} +2 -2
  12. package/dist/ui/assets/{LoginPage-CAmSI9Vy.js → LoginPage-OMsrx5oj.js} +1 -1
  13. package/dist/ui/assets/LogsPage-CcYYwjgF.js +1 -0
  14. package/dist/ui/{dist/assets/MeetingPage-WjjGOqqU.js → assets/MeetingPage-CrKVAfe6.js} +1 -1
  15. package/dist/ui/assets/{NotFound-BMeQSGcG.js → NotFound-GbAJDgoD.js} +1 -1
  16. package/dist/ui/assets/ProvidersPage-uqPcZUSV.js +1 -0
  17. package/dist/ui/{dist/assets/RecoverPage-DpW3l-yv.js → assets/RecoverPage-CwB2ByCU.js} +1 -1
  18. package/dist/ui/assets/SettingsPage-DKLlye0z.js +9 -0
  19. package/dist/ui/assets/SetupPage-DOVh1ldK.js +1 -0
  20. package/dist/ui/assets/WebChatPage-c-7S9jnT.js +16 -0
  21. package/dist/ui/assets/accordion-DAbcVQCn.js +1 -0
  22. package/dist/ui/{dist/assets/alert-C-NE-P3s.js → assets/alert-D_2Y3qjL.js} +1 -1
  23. package/dist/ui/{dist/assets/alert-dialog-C5mzbHdP.js → assets/alert-dialog-CpMxaNcu.js} +1 -1
  24. package/dist/ui/assets/{badge-ChpACfWO.js → badge-CxTPR6_t.js} +1 -1
  25. package/dist/ui/assets/bell-8BqRYmzf.js +1 -0
  26. package/dist/ui/assets/circle-x-Bv6WrUJo.js +1 -0
  27. package/dist/ui/assets/copy-dU94ZGsi.js +1 -0
  28. package/dist/ui/{dist/assets/dialog-QnZ0ad8O.js → assets/dialog-DfS3idb3.js} +1 -1
  29. package/dist/ui/{dist/assets/dropdown-menu-BK-CO3Od.js → assets/dropdown-menu-BdCbAW1z.js} +1 -1
  30. package/dist/ui/assets/{es-NQNoaWDx.js → es-Cz5h9_84.js} +1 -1
  31. package/dist/ui/assets/index-CmGm_r89.js +116 -0
  32. package/dist/ui/assets/index-T7HgphSn.css +2 -0
  33. package/dist/ui/{dist/assets/label-D2H1IR_J.js → assets/label-byJkqOYq.js} +1 -1
  34. package/dist/ui/assets/progress-Dtz-Mzys.js +1 -0
  35. package/dist/ui/assets/scroll-area-BXtLsE9E.js +1 -0
  36. package/dist/ui/assets/search-BGmPJ-6L.js +1 -0
  37. package/dist/ui/assets/select-Cl16QYa_.js +1 -0
  38. package/dist/ui/assets/send-BuQcUO-R.js +1 -0
  39. package/dist/ui/assets/shield-C-05qB-2.js +1 -0
  40. package/dist/ui/{dist/assets/slider-CsiUDxc3.js → assets/slider-D2I0qven.js} +1 -1
  41. package/dist/ui/assets/switch-h2SfQX4B.js +1 -0
  42. package/dist/ui/assets/table-CVkIRJKK.js +1 -0
  43. package/dist/ui/assets/tabs-C619jxbO.js +1 -0
  44. package/dist/ui/assets/textarea-wvA-FDjO.js +1 -0
  45. package/dist/ui/assets/useProviders-eEri6BAc.js +1 -0
  46. package/dist/ui/{dist/assets/vendor-radix-cw1bQaVC.js → assets/vendor-radix-D6rA7xKY.js} +4 -4
  47. package/dist/ui/assets/{vendor-react-D4s9E-zj.js → vendor-react-BU5iQU4f.js} +1 -1
  48. package/dist/ui/dist/assets/AgentCreateForm-BTCzFbca.js +1 -0
  49. package/dist/ui/dist/assets/AgentDetailPage-o27TRSVw.js +1 -0
  50. package/dist/ui/dist/assets/AgentNewPage-400cCpYt.js +1 -0
  51. package/dist/ui/{assets/AgentsPage-YvSgWRiw.js → dist/assets/AgentsPage-C-GSRk-N.js} +5 -5
  52. package/dist/ui/dist/assets/ApiClientPage-BOTpz6oP.js +3 -0
  53. package/dist/ui/dist/assets/{CanvasPage-DtMwGvxf.js → CanvasPage-Cvs5ctza.js} +7 -7
  54. package/dist/ui/dist/assets/ChannelsPage-C5m_L7P9.js +8 -0
  55. package/dist/ui/{assets/DashboardPage-ghl1ZguH.js → dist/assets/DashboardPage-CztbRQdm.js} +2 -2
  56. package/dist/ui/dist/assets/{LoginPage-CAmSI9Vy.js → LoginPage-OMsrx5oj.js} +1 -1
  57. package/dist/ui/dist/assets/LogsPage-CcYYwjgF.js +1 -0
  58. package/dist/ui/{assets/MeetingPage-WjjGOqqU.js → dist/assets/MeetingPage-CrKVAfe6.js} +1 -1
  59. package/dist/ui/dist/assets/{NotFound-BMeQSGcG.js → NotFound-GbAJDgoD.js} +1 -1
  60. package/dist/ui/dist/assets/ProvidersPage-uqPcZUSV.js +1 -0
  61. package/dist/ui/{assets/RecoverPage-DpW3l-yv.js → dist/assets/RecoverPage-CwB2ByCU.js} +1 -1
  62. package/dist/ui/dist/assets/SettingsPage-DKLlye0z.js +9 -0
  63. package/dist/ui/dist/assets/SetupPage-DOVh1ldK.js +1 -0
  64. package/dist/ui/dist/assets/WebChatPage-c-7S9jnT.js +16 -0
  65. package/dist/ui/dist/assets/accordion-DAbcVQCn.js +1 -0
  66. package/dist/ui/{assets/alert-C-NE-P3s.js → dist/assets/alert-D_2Y3qjL.js} +1 -1
  67. package/dist/ui/{assets/alert-dialog-C5mzbHdP.js → dist/assets/alert-dialog-CpMxaNcu.js} +1 -1
  68. package/dist/ui/dist/assets/{badge-ChpACfWO.js → badge-CxTPR6_t.js} +1 -1
  69. package/dist/ui/dist/assets/bell-8BqRYmzf.js +1 -0
  70. package/dist/ui/dist/assets/circle-x-Bv6WrUJo.js +1 -0
  71. package/dist/ui/dist/assets/copy-dU94ZGsi.js +1 -0
  72. package/dist/ui/{assets/dialog-QnZ0ad8O.js → dist/assets/dialog-DfS3idb3.js} +1 -1
  73. package/dist/ui/{assets/dropdown-menu-BK-CO3Od.js → dist/assets/dropdown-menu-BdCbAW1z.js} +1 -1
  74. package/dist/ui/dist/assets/{es-NQNoaWDx.js → es-Cz5h9_84.js} +1 -1
  75. package/dist/ui/dist/assets/index-CmGm_r89.js +116 -0
  76. package/dist/ui/dist/assets/index-T7HgphSn.css +2 -0
  77. package/dist/ui/{assets/label-D2H1IR_J.js → dist/assets/label-byJkqOYq.js} +1 -1
  78. package/dist/ui/dist/assets/progress-Dtz-Mzys.js +1 -0
  79. package/dist/ui/dist/assets/scroll-area-BXtLsE9E.js +1 -0
  80. package/dist/ui/dist/assets/search-BGmPJ-6L.js +1 -0
  81. package/dist/ui/dist/assets/select-Cl16QYa_.js +1 -0
  82. package/dist/ui/dist/assets/send-BuQcUO-R.js +1 -0
  83. package/dist/ui/dist/assets/shield-C-05qB-2.js +1 -0
  84. package/dist/ui/{assets/slider-CsiUDxc3.js → dist/assets/slider-D2I0qven.js} +1 -1
  85. package/dist/ui/dist/assets/switch-h2SfQX4B.js +1 -0
  86. package/dist/ui/dist/assets/table-CVkIRJKK.js +1 -0
  87. package/dist/ui/dist/assets/tabs-C619jxbO.js +1 -0
  88. package/dist/ui/dist/assets/textarea-wvA-FDjO.js +1 -0
  89. package/dist/ui/dist/assets/useProviders-eEri6BAc.js +1 -0
  90. package/dist/ui/{assets/vendor-radix-cw1bQaVC.js → dist/assets/vendor-radix-D6rA7xKY.js} +4 -4
  91. package/dist/ui/dist/assets/{vendor-react-D4s9E-zj.js → vendor-react-BU5iQU4f.js} +1 -1
  92. package/dist/ui/dist/index.html +6 -6
  93. package/dist/ui/index.html +6 -6
  94. package/package.json +1 -1
  95. package/packages/cli/src/adapters/binary.ts +8 -4
  96. package/packages/cli/src/adapters/bun-global.ts +5 -1
  97. package/packages/cli/src/adapters/config.ts +4 -3
  98. package/packages/cli/src/adapters/docker.ts +2 -1
  99. package/packages/cli/src/commands/gateway.ts +123 -9
  100. package/packages/cli/src/commands/logs.ts +2 -1
  101. package/packages/cli/src/commands/onboard.ts +1 -1
  102. package/packages/cli/src/commands/sessions.ts +2 -1
  103. package/packages/cli/src/commands/skills.ts +2 -1
  104. package/packages/core/src/agent/agent-loop.ts +4 -14
  105. package/packages/core/src/agent/context-compiler.ts +1 -1
  106. package/packages/core/src/agent/conversation-store.ts +4 -5
  107. package/packages/core/src/agent/llm-client.ts +4 -0
  108. package/packages/core/src/agent/llm-providers/anthropic.ts +23 -8
  109. package/packages/core/src/agent/llm-providers/interface.ts +5 -1
  110. package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
  111. package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
  112. package/packages/core/src/agent/providers/index.ts +3 -4
  113. package/packages/core/src/agent/tool-selector.ts +3 -4
  114. package/packages/core/src/channels/whatsapp.ts +13 -1
  115. package/packages/core/src/config/loader.ts +7 -7
  116. package/packages/core/src/gateway/helpers/path.ts +2 -1
  117. package/packages/core/src/gateway/initializer.ts +4 -4
  118. package/packages/core/src/gateway/llm-local/downloader.ts +130 -11
  119. package/packages/core/src/gateway/llm-local/index.ts +2 -0
  120. package/packages/core/src/gateway/llm-local/models.ts +4 -3
  121. package/packages/core/src/gateway/resolver.ts +5 -1
  122. package/packages/core/src/gateway/router.ts +7 -5
  123. package/packages/core/src/gateway/routes/chat.ts +16 -16
  124. package/packages/core/src/gateway/routes/http-client.ts +16 -0
  125. package/packages/core/src/gateway/routes/llm-local.ts +51 -5
  126. package/packages/core/src/gateway/routes/providers.ts +43 -2
  127. package/packages/core/src/gateway/server.ts +55 -47
  128. package/packages/core/src/gateway/tts/src/install.ts +17 -9
  129. package/packages/core/src/storage/crypto.ts +152 -20
  130. package/packages/core/src/storage/migrate.ts +51 -18
  131. package/packages/core/src/storage/seed.ts +77 -35
  132. package/packages/core/src/tool-runtime/index.ts +42 -1
  133. package/packages/core/src/tools/api/api-request.ts +174 -0
  134. package/packages/core/src/tools/api/index.ts +16 -0
  135. package/packages/core/src/tools/index.ts +12 -0
  136. package/packages/core/src/tools/web/browser-click.ts +2 -2
  137. package/packages/core/src/tools/web/browser-extract.ts +22 -6
  138. package/packages/core/src/tools/web/browser-navigate.ts +34 -18
  139. package/packages/core/src/tools/web/browser-screenshot.ts +40 -8
  140. package/packages/core/src/tools/web/browser-script.ts +2 -2
  141. package/packages/core/src/tools/web/browser-service.ts +295 -341
  142. package/packages/core/src/tools/web/browser-type.ts +5 -10
  143. package/packages/core/src/tools/web/browser-wait.ts +2 -2
  144. package/packages/core/src/tools/web/index.ts +1 -1
  145. package/packages/core/src/utils/logger.ts +2 -1
  146. package/packages/mcp/src/manager.ts +2 -1
  147. package/packages/skills/src/bundled/api/api_client/SKILL.md +132 -0
  148. package/packages/skills/src/bundled-data.generated.ts +1274 -1217
  149. package/packages/skills/src/loader.ts +2 -1
  150. package/dist/ui/assets/AgentCreateForm-tJZv9FZC.js +0 -1
  151. package/dist/ui/assets/AgentDetailPage-Du-mRcAX.js +0 -1
  152. package/dist/ui/assets/AgentNewPage-DIFYd_Ys.js +0 -1
  153. package/dist/ui/assets/ChannelsPage-BdBXWHjj.js +0 -8
  154. package/dist/ui/assets/LogsPage-DAPBHkwK.js +0 -1
  155. package/dist/ui/assets/ProvidersPage-Ct6HsAi1.js +0 -1
  156. package/dist/ui/assets/SettingsPage-DBJ7_E6C.js +0 -9
  157. package/dist/ui/assets/SetupPage-DKmLVUaj.js +0 -1
  158. package/dist/ui/assets/WebChatPage-CVRcKept.js +0 -16
  159. package/dist/ui/assets/accordion-C5d5Rm5z.js +0 -1
  160. package/dist/ui/assets/globe-DeCQTCDJ.js +0 -1
  161. package/dist/ui/assets/index-B2fCYtTS.css +0 -2
  162. package/dist/ui/assets/index-DMCjjdqf.js +0 -116
  163. package/dist/ui/assets/progress-BherYzY6.js +0 -1
  164. package/dist/ui/assets/scroll-area-DkeyX32e.js +0 -1
  165. package/dist/ui/assets/send-B0H5SEIE.js +0 -1
  166. package/dist/ui/assets/switch-BDwN8RYV.js +0 -1
  167. package/dist/ui/assets/table-CSc8ubon.js +0 -1
  168. package/dist/ui/assets/textarea-CXgXWKrT.js +0 -1
  169. package/dist/ui/assets/useProviders-C6_QHsEi.js +0 -1
  170. package/dist/ui/dist/assets/AgentCreateForm-tJZv9FZC.js +0 -1
  171. package/dist/ui/dist/assets/AgentDetailPage-Du-mRcAX.js +0 -1
  172. package/dist/ui/dist/assets/AgentNewPage-DIFYd_Ys.js +0 -1
  173. package/dist/ui/dist/assets/ChannelsPage-BdBXWHjj.js +0 -8
  174. package/dist/ui/dist/assets/LogsPage-DAPBHkwK.js +0 -1
  175. package/dist/ui/dist/assets/ProvidersPage-Ct6HsAi1.js +0 -1
  176. package/dist/ui/dist/assets/SettingsPage-DBJ7_E6C.js +0 -9
  177. package/dist/ui/dist/assets/SetupPage-DKmLVUaj.js +0 -1
  178. package/dist/ui/dist/assets/WebChatPage-CVRcKept.js +0 -16
  179. package/dist/ui/dist/assets/accordion-C5d5Rm5z.js +0 -1
  180. package/dist/ui/dist/assets/globe-DeCQTCDJ.js +0 -1
  181. package/dist/ui/dist/assets/index-B2fCYtTS.css +0 -2
  182. package/dist/ui/dist/assets/index-DMCjjdqf.js +0 -116
  183. package/dist/ui/dist/assets/progress-BherYzY6.js +0 -1
  184. package/dist/ui/dist/assets/scroll-area-DkeyX32e.js +0 -1
  185. package/dist/ui/dist/assets/send-B0H5SEIE.js +0 -1
  186. package/dist/ui/dist/assets/switch-BDwN8RYV.js +0 -1
  187. package/dist/ui/dist/assets/table-CSc8ubon.js +0 -1
  188. package/dist/ui/dist/assets/textarea-CXgXWKrT.js +0 -1
  189. package/dist/ui/dist/assets/useProviders-C6_QHsEi.js +0 -1
  190. /package/dist/ui/assets/{card-CNf6BS2e.js → card-CXAm46at.js} +0 -0
  191. /package/dist/ui/assets/{cpu-Cdgc_B1K.js → cpu-DSpPVLAz.js} +0 -0
  192. /package/dist/ui/assets/{download-C3ifGMjJ.js → download-D9ZyUZZR.js} +0 -0
  193. /package/dist/ui/assets/{external-link-BvxYeTP1.js → external-link-CHPbUorN.js} +0 -0
  194. /package/dist/ui/assets/{eye-DqNTU_GD.js → eye-epHJZ_nQ.js} +0 -0
  195. /package/dist/ui/assets/{file-text-BT_9S9SM.js → file-text-BEjEmgby.js} +0 -0
  196. /package/dist/ui/assets/{folder-open-BhH8y9ac.js → folder-open-iQMHVEqS.js} +0 -0
  197. /package/dist/ui/assets/{format-GVHeOyWI.js → format-oFACFaca.js} +0 -0
  198. /package/dist/ui/assets/{gateway-url-COCbW0IR.js → gateway-url-iG-C6Agn.js} +0 -0
  199. /package/dist/ui/assets/{gauge-D_TMa4i9.js → gauge-D0_GMEcq.js} +0 -0
  200. /package/dist/ui/assets/{settings-Ds4SqD8s.js → settings-CcMGI1iU.js} +0 -0
  201. /package/dist/ui/assets/{sparkles-yUEb-7oH.js → sparkles-D6fx8JC5.js} +0 -0
  202. /package/dist/ui/assets/{trash-2-CNjMkoq6.js → trash-2-BHRa5ft9.js} +0 -0
  203. /package/dist/ui/assets/{triangle-alert-C9Y8Ub4X.js → triangle-alert-D4nwAVbc.js} +0 -0
  204. /package/dist/ui/assets/{vendor-router-C9pIYwbJ.js → vendor-router-pCP7sjma.js} +0 -0
  205. /package/dist/ui/assets/{volume-2-CeSXNDv4.js → volume-2-B6tkRy2u.js} +0 -0
  206. /package/dist/ui/assets/{zap-hlXjpSeA.js → zap-QO7iWMRg.js} +0 -0
  207. /package/dist/ui/dist/assets/{card-CNf6BS2e.js → card-CXAm46at.js} +0 -0
  208. /package/dist/ui/dist/assets/{cpu-Cdgc_B1K.js → cpu-DSpPVLAz.js} +0 -0
  209. /package/dist/ui/dist/assets/{download-C3ifGMjJ.js → download-D9ZyUZZR.js} +0 -0
  210. /package/dist/ui/dist/assets/{external-link-BvxYeTP1.js → external-link-CHPbUorN.js} +0 -0
  211. /package/dist/ui/dist/assets/{eye-DqNTU_GD.js → eye-epHJZ_nQ.js} +0 -0
  212. /package/dist/ui/dist/assets/{file-text-BT_9S9SM.js → file-text-BEjEmgby.js} +0 -0
  213. /package/dist/ui/dist/assets/{folder-open-BhH8y9ac.js → folder-open-iQMHVEqS.js} +0 -0
  214. /package/dist/ui/dist/assets/{format-GVHeOyWI.js → format-oFACFaca.js} +0 -0
  215. /package/dist/ui/dist/assets/{gateway-url-COCbW0IR.js → gateway-url-iG-C6Agn.js} +0 -0
  216. /package/dist/ui/dist/assets/{gauge-D_TMa4i9.js → gauge-D0_GMEcq.js} +0 -0
  217. /package/dist/ui/dist/assets/{settings-Ds4SqD8s.js → settings-CcMGI1iU.js} +0 -0
  218. /package/dist/ui/dist/assets/{sparkles-yUEb-7oH.js → sparkles-D6fx8JC5.js} +0 -0
  219. /package/dist/ui/dist/assets/{trash-2-CNjMkoq6.js → trash-2-BHRa5ft9.js} +0 -0
  220. /package/dist/ui/dist/assets/{triangle-alert-C9Y8Ub4X.js → triangle-alert-D4nwAVbc.js} +0 -0
  221. /package/dist/ui/dist/assets/{vendor-router-C9pIYwbJ.js → vendor-router-pCP7sjma.js} +0 -0
  222. /package/dist/ui/dist/assets/{volume-2-CeSXNDv4.js → volume-2-B6tkRy2u.js} +0 -0
  223. /package/dist/ui/dist/assets/{zap-hlXjpSeA.js → zap-QO7iWMRg.js} +0 -0
@@ -4,6 +4,7 @@ import {
4
4
  loadProviderApiKey, storeProviderApiKey,
5
5
  loadProviderHeaders, storeProviderHeaders,
6
6
  } from "../../storage/crypto"
7
+ import { listLocalModels } from "../llm-local/downloader"
7
8
 
8
9
  export async function handleGetProviders(req: Request, addCorsHeaders: (r: Response, req: Request) => Response): Promise<Response> {
9
10
  const rawProviders = getDb().query(`
@@ -24,20 +25,60 @@ export async function handleGetProviders(req: Request, addCorsHeaders: (r: Respo
24
25
  })
25
26
  }
26
27
 
28
+ // Inyectar modelos locales descargados dinámicamente para local-llama
29
+ try {
30
+ const localModels = listLocalModels()
31
+ const downloadedLocalModels = localModels.filter(m => m.downloaded)
32
+ if (downloadedLocalModels.length > 0) {
33
+ const existingLocalModels = modelsByProvider["local-llama"] || []
34
+ for (const localModel of downloadedLocalModels) {
35
+ const existingIdx = existingLocalModels.findIndex((m: any) => m.id === localModel.id)
36
+ if (existingIdx === -1) {
37
+ existingLocalModels.push({
38
+ id: localModel.id,
39
+ name: localModel.name,
40
+ provider_id: "local-llama",
41
+ model_type: "llm",
42
+ context_window: 16000,
43
+ capabilities: JSON.stringify(["chat", "local"]),
44
+ enabled: true,
45
+ active: true,
46
+ })
47
+ } else {
48
+ // Modelo del seed ya existe pero está descargado: forzar activo
49
+ existingLocalModels[existingIdx] = {
50
+ ...existingLocalModels[existingIdx],
51
+ enabled: true,
52
+ active: true,
53
+ }
54
+ }
55
+ }
56
+ modelsByProvider["local-llama"] = existingLocalModels
57
+ }
58
+ } catch {
59
+ // Ignorar si no se pueden obtener modelos locales
60
+ }
61
+
27
62
  const providers = await Promise.all(rawProviders.map(async (p) => {
28
63
  const apiKey = await loadProviderApiKey(p.id as string)
29
64
  const headers = await loadProviderHeaders(p.id as string)
65
+ const providerModels = modelsByProvider[p.id as string] || []
66
+ // Auto-activar local-llama si tiene modelos descargados dinámicamente
67
+ let active = p.active
68
+ if (p.id === "local-llama" && providerModels.some((m: any) => m.active || m.enabled)) {
69
+ active = true
70
+ }
30
71
  return {
31
72
  id: p.id,
32
73
  name: p.name,
33
74
  base_url: p.base_url,
34
75
  enabled: p.enabled,
35
- active: p.active,
76
+ active,
36
77
  num_ctx: p.num_ctx ?? null,
37
78
  has_api_key: apiKey ? 1 : 0,
38
79
  has_headers: Object.keys(headers).length > 0 ? 1 : 0,
39
80
  masked_api_key: apiKey ? maskApiKey(apiKey) : null,
40
- models: modelsByProvider[p.id as string] || [],
81
+ models: providerModels,
41
82
  }
42
83
  }))
43
84
 
@@ -73,12 +73,13 @@ import { handleGetModels, handleCreateModel, handleToggleModel, handleGetModelsC
73
73
  import { handleGetVoiceProviders, handleGetConfiguredVoiceProviders, handleSaveVoiceProviderKey, handleTestVoice, handleGetChannelVoice, handleUpdateChannelVoice, handleGetVoiceProviderVoices } from "./routes/voice";
74
74
  import { handleGetVisionProviders, handleGetChannelVision, handleUpdateChannelVision, handleOcrImage } from "./routes/multimodal";
75
75
  import { handleGetLocalTTSStatus, handleGetLocalTTSLogs, handleInstallLocalTTS, handleStartLocalTTS, handleStopLocalTTS, handleSpeakLocalTTS, handleGetAvailableModels, handleGetInstalledVoices, handleDownloadModel, handleGetDownloadLogs, initializeLocalTTS } from "./routes/tts-local";
76
- import { handleGetLocalLLMStatus, handleGetLocalLLMLogs, handleInstallLocalLLM, handleStartLocalLLM, handleStopLocalLLM, handleDownloadLLMModel, initializeLocalLLM } from "./routes/llm-local";
76
+ import { handleGetLocalLLMStatus, handleGetLocalLLMLogs, handleInstallLocalLLM, handleStartLocalLLM, handleStopLocalLLM, handleDownloadLLMModel, handleGetDownloadProgress, initializeLocalLLM } from "./routes/llm-local";
77
77
  import { handleCreateMeeting, handleListMeetings, handleGetMeeting, handleAddMeetingSegment, handleStopMeeting } from "./routes/meeting";
78
78
  import { handleGetActivityStats, handleGetSystemStats, handleGetUsageStats, handleSystemReload, handleApiReload, handleGetVersion, handleTriggerUpdate } from "./routes/system";
79
79
  import { handleGetChatHistory, handleGetCanvas, handleGetNotes, handleUpdateNote } from "./routes/chat";
80
80
  import { handleChat as handlePostChat } from "./routes/chat";
81
81
  import { handleGetConfig } from "./routes/config";
82
+ import { handleHttpRequest } from "./routes/http-client";
82
83
  import { handleGetWorkspace, handleUpdateWorkspace, handleValidateWorkspace, handleCreateWorkspace, handleOpenWorkspace } from "./routes/workspace";
83
84
  import { getNarration, expandPath, addCorsHeaders, CORS_ORIGINS } from "./helpers";
84
85
  import { redactConfig } from "./helpers";
@@ -373,7 +374,7 @@ export async function startGateway(config: Config): Promise<void> {
373
374
 
374
375
  log.info(` Content: ${messageContent.substring(0, 150)}${messageContent.length > 150 ? "..." : ""}`);
375
376
 
376
- const { userId } = resolveContext({
377
+ const { userId, threadId: conversationThreadId } = resolveContext({
377
378
  channel: message.channel,
378
379
  channelUserId: message.sessionId,
379
380
  });
@@ -385,8 +386,8 @@ export async function startGateway(config: Config): Promise<void> {
385
386
  channelManager.startTyping(message.channel, message.sessionId),
386
387
  ]);
387
388
 
388
- // unifiedSessionId = userId del onboarding historial y thread LangGraph unificados
389
- const unifiedSessionId = userId;
389
+ // conversationThreadId = conversations.thread_id canónico compartido por todos los canales
390
+ const unifiedSessionId = conversationThreadId;
390
391
  // routingSessionId = peerId del canal → para enviar respuestas de vuelta al canal correcto
391
392
  const routingSessionId = message.sessionId;
392
393
 
@@ -961,6 +962,11 @@ export async function startGateway(config: Config): Promise<void> {
961
962
  }
962
963
  }
963
964
 
965
+ // ── HTTP Client API ────────────────────────────────────────────────
966
+ if ((url.pathname === "/api/http-request" || url.pathname === "/api/http-request/") && req.method === "POST") {
967
+ return await handleHttpRequest(req, addCorsHeaders)
968
+ }
969
+
964
970
  // ── Tasks API ─────────────────────────────────────────────────────
965
971
  if ((url.pathname === "/api/tasks" || url.pathname === "/api/tasks/") && req.method === "GET") {
966
972
  return await handleGetTasks(req, addCorsHeaders)
@@ -1626,6 +1632,9 @@ export async function startGateway(config: Config): Promise<void> {
1626
1632
  if (url.pathname === "/api/llm-local/download-model" && req.method === "POST") {
1627
1633
  return await handleDownloadLLMModel(req, addCorsHeaders)
1628
1634
  }
1635
+ if (url.pathname === "/api/llm-local/download-progress" && req.method === "GET") {
1636
+ return await handleGetDownloadProgress(req, addCorsHeaders)
1637
+ }
1629
1638
 
1630
1639
  // ── Meeting Transcription API ────────────────────────────────────────
1631
1640
  if (url.pathname === "/api/meetings" && req.method === "POST") {
@@ -1940,7 +1949,7 @@ export async function startGateway(config: Config): Promise<void> {
1940
1949
  return;
1941
1950
  }
1942
1951
  try {
1943
- const { userId } = resolveContext({ channel: "webchat", channelUserId: sessionId });
1952
+ const { userId, threadId: conversationThreadId } = resolveContext({ channel: "webchat", channelUserId: sessionId });
1944
1953
  const messages = [{ role: "user" as const, content: interactionMsg }];
1945
1954
  let streamedContent = "";
1946
1955
  const messageId = crypto.randomUUID();
@@ -1949,9 +1958,9 @@ export async function startGateway(config: Config): Promise<void> {
1949
1958
  provider: dbProvider as any,
1950
1959
  messages,
1951
1960
  maxTokens: 4096,
1952
- tools: prepareTools(agent, sessionId),
1961
+ tools: prepareTools(agent, conversationThreadId),
1953
1962
  maxSteps: 15,
1954
- threadId: sessionId,
1963
+ threadId: conversationThreadId,
1955
1964
  userId,
1956
1965
  onToken: async (token: string) => {
1957
1966
  if (signal.aborted) return;
@@ -2015,7 +2024,7 @@ export async function startGateway(config: Config): Promise<void> {
2015
2024
  return;
2016
2025
  }
2017
2026
  try {
2018
- const { userId } = resolveContext({ channel: "webchat", channelUserId: sessionId });
2027
+ const { userId, threadId: conversationThreadId } = resolveContext({ channel: "webchat", channelUserId: sessionId });
2019
2028
  const messages = [{ role: "user" as const, content: interactionMsg }];
2020
2029
  let streamedContent = "";
2021
2030
  const messageId = crypto.randomUUID();
@@ -2024,9 +2033,9 @@ export async function startGateway(config: Config): Promise<void> {
2024
2033
  provider: dbProvider as any,
2025
2034
  messages,
2026
2035
  maxTokens: 4096,
2027
- tools: prepareTools(agent, sessionId),
2036
+ tools: prepareTools(agent, conversationThreadId),
2028
2037
  maxSteps: 15,
2029
- threadId: sessionId,
2038
+ threadId: conversationThreadId,
2030
2039
  userId,
2031
2040
  onToken: async (token: string) => {
2032
2041
  if (signal.aborted) return;
@@ -2157,14 +2166,14 @@ export async function startGateway(config: Config): Promise<void> {
2157
2166
  }
2158
2167
 
2159
2168
  try {
2160
- const unifiedSessionId = msg.sessionId;
2161
- const messages = [{ role: "user" as const, content: messageContent }];
2162
- log.info(`Generating response for session ${unifiedSessionId}...`);
2163
-
2164
- const { userId } = resolveContext({
2169
+ const { userId, threadId: conversationThreadId } = resolveContext({
2165
2170
  channel: "webchat",
2166
2171
  channelUserId: msg.sessionId,
2167
2172
  });
2173
+ const unifiedSessionId = conversationThreadId;
2174
+ const routingSessionId = msg.sessionId;
2175
+ const messages = [{ role: "user" as const, content: messageContent }];
2176
+ log.info(`Generating response for session ${unifiedSessionId}...`);
2168
2177
 
2169
2178
  // Streaming: send tokens as they arrive
2170
2179
  let streamedContent = "";
@@ -2185,7 +2194,7 @@ export async function startGateway(config: Config): Promise<void> {
2185
2194
  ws.send(JSON.stringify({
2186
2195
  type: "message",
2187
2196
  id: messageId,
2188
- sessionId: unifiedSessionId,
2197
+ sessionId: routingSessionId,
2189
2198
  content: token,
2190
2199
  isChunk: true,
2191
2200
  isStep: false,
@@ -2200,7 +2209,7 @@ export async function startGateway(config: Config): Promise<void> {
2200
2209
  if (trimmedMessage) {
2201
2210
  ws.send(JSON.stringify({
2202
2211
  type: "progress",
2203
- sessionId: unifiedSessionId,
2212
+ sessionId: routingSessionId,
2204
2213
  content: trimmedMessage,
2205
2214
  } as OutboundMessage));
2206
2215
  }
@@ -2212,7 +2221,7 @@ export async function startGateway(config: Config): Promise<void> {
2212
2221
  const narration = getNarration(step.toolName);
2213
2222
  ws.send(JSON.stringify({
2214
2223
  type: "progress",
2215
- sessionId: unifiedSessionId,
2224
+ sessionId: routingSessionId,
2216
2225
  content: narration,
2217
2226
  } as OutboundMessage));
2218
2227
  return;
@@ -2227,7 +2236,7 @@ export async function startGateway(config: Config): Promise<void> {
2227
2236
  if (userMessage) {
2228
2237
  ws.send(JSON.stringify({
2229
2238
  type: "progress",
2230
- sessionId: unifiedSessionId,
2239
+ sessionId: routingSessionId,
2231
2240
  content: userMessage,
2232
2241
  } as OutboundMessage));
2233
2242
  }
@@ -2248,7 +2257,7 @@ export async function startGateway(config: Config): Promise<void> {
2248
2257
  let ttsProviderUsed: string | null = null;
2249
2258
  let ttsMimeType: string | null = null;
2250
2259
 
2251
- ws.send(JSON.stringify({ type: "typing", isTyping: false, sessionId: unifiedSessionId } as OutboundMessage));
2260
+ ws.send(JSON.stringify({ type: "typing", isTyping: false, sessionId: routingSessionId } as OutboundMessage));
2252
2261
 
2253
2262
  // Don't send text message if already streamed (content came via onToken)
2254
2263
  const alreadyStreamed = streamedContent.length > 0;
@@ -2258,7 +2267,7 @@ export async function startGateway(config: Config): Promise<void> {
2258
2267
  if (!voiceCfg.ttsProvider) {
2259
2268
  ws.send(JSON.stringify({
2260
2269
  type: "message",
2261
- sessionId: unifiedSessionId,
2270
+ sessionId: routingSessionId,
2262
2271
  content: `${content}\n\n🔊 Para recibir respuestas en audio, configura el proveedor TTS en Configuración > Canales > WebChat (ej: elevenlabs)`,
2263
2272
  isStep: false,
2264
2273
  } as OutboundMessage));
@@ -2273,7 +2282,7 @@ export async function startGateway(config: Config): Promise<void> {
2273
2282
  log.info(`Audio generated: ${base64Audio.length} bytes, mimeType: ${audioOutput.mimeType}`);
2274
2283
  ws.send(JSON.stringify({
2275
2284
  type: "message",
2276
- sessionId: unifiedSessionId,
2285
+ sessionId: routingSessionId,
2277
2286
  content,
2278
2287
  audio: base64Audio,
2279
2288
  mimeType: audioOutput.mimeType,
@@ -2281,11 +2290,11 @@ export async function startGateway(config: Config): Promise<void> {
2281
2290
  } as OutboundMessage));
2282
2291
  } catch (ttsError) {
2283
2292
  log.error(`TTS failed: ${(ttsError as Error).message}), sending text instead`);
2284
- ws.send(JSON.stringify({ type: "message", sessionId: unifiedSessionId, content, isStep: false } as OutboundMessage));
2293
+ ws.send(JSON.stringify({ type: "message", sessionId: routingSessionId, content, isStep: false } as OutboundMessage));
2285
2294
  }
2286
2295
  }
2287
2296
  } else {
2288
- ws.send(JSON.stringify({ type: "message", sessionId: unifiedSessionId, content, isStep: false } as OutboundMessage));
2297
+ ws.send(JSON.stringify({ type: "message", sessionId: routingSessionId, content, isStep: false } as OutboundMessage));
2289
2298
  }
2290
2299
  } else if (alreadyStreamed && shouldSpeak && voiceCfg.ttsProvider) {
2291
2300
  try {
@@ -2295,7 +2304,7 @@ export async function startGateway(config: Config): Promise<void> {
2295
2304
  log.info(`Audio generated after streaming: ${base64Audio.length} bytes`);
2296
2305
  ws.send(JSON.stringify({
2297
2306
  type: "message",
2298
- sessionId: unifiedSessionId,
2307
+ sessionId: routingSessionId,
2299
2308
  content,
2300
2309
  audio: base64Audio,
2301
2310
  mimeType: audioOutput.mimeType,
@@ -2349,7 +2358,12 @@ export async function startGateway(config: Config): Promise<void> {
2349
2358
  }
2350
2359
 
2351
2360
  try {
2352
- const unifiedSessionId = msg.sessionId;
2361
+ const { userId, threadId: conversationThreadId } = resolveContext({
2362
+ channel: "webchat",
2363
+ channelUserId: msg.sessionId,
2364
+ });
2365
+ const unifiedSessionId = conversationThreadId;
2366
+ const routingSessionId = msg.sessionId;
2353
2367
 
2354
2368
  // Multimodal: process image/document if present
2355
2369
  let finalMessageContent = msg.content;
@@ -2418,11 +2432,6 @@ export async function startGateway(config: Config): Promise<void> {
2418
2432
 
2419
2433
  log.info(`Generating response for session ${unifiedSessionId} (multimodal: ${!!(msg.image || msg.document)})...`);
2420
2434
 
2421
- const { userId } = resolveContext({
2422
- channel: "webchat",
2423
- channelUserId: msg.sessionId,
2424
- });
2425
-
2426
2435
  // Streaming: send tokens as they arrive
2427
2436
  let streamedContent = "";
2428
2437
  let messageId = crypto.randomUUID();
@@ -2443,7 +2452,7 @@ export async function startGateway(config: Config): Promise<void> {
2443
2452
  ws.send(JSON.stringify({
2444
2453
  type: "message",
2445
2454
  id: messageId,
2446
- sessionId: unifiedSessionId,
2455
+ sessionId: routingSessionId,
2447
2456
  content: token,
2448
2457
  isChunk: true,
2449
2458
  isStep: false,
@@ -2458,7 +2467,7 @@ export async function startGateway(config: Config): Promise<void> {
2458
2467
  if (trimmedMessage) {
2459
2468
  ws.send(JSON.stringify({
2460
2469
  type: "progress",
2461
- sessionId: unifiedSessionId,
2470
+ sessionId: routingSessionId,
2462
2471
  content: trimmedMessage,
2463
2472
  } as OutboundMessage));
2464
2473
  }
@@ -2470,7 +2479,7 @@ export async function startGateway(config: Config): Promise<void> {
2470
2479
  const narration = getNarration(step.toolName);
2471
2480
  ws.send(JSON.stringify({
2472
2481
  type: "progress",
2473
- sessionId: unifiedSessionId,
2482
+ sessionId: routingSessionId,
2474
2483
  content: narration,
2475
2484
  } as OutboundMessage));
2476
2485
  return;
@@ -2485,7 +2494,7 @@ export async function startGateway(config: Config): Promise<void> {
2485
2494
  if (userMessage) {
2486
2495
  ws.send(JSON.stringify({
2487
2496
  type: "progress",
2488
- sessionId: unifiedSessionId,
2497
+ sessionId: routingSessionId,
2489
2498
  content: userMessage,
2490
2499
  } as OutboundMessage));
2491
2500
  }
@@ -2506,7 +2515,7 @@ export async function startGateway(config: Config): Promise<void> {
2506
2515
  let ttsProviderUsed: string | null = null;
2507
2516
  let ttsMimeType: string | null = null;
2508
2517
 
2509
- ws.send(JSON.stringify({ type: "typing", isTyping: false, sessionId: unifiedSessionId } as OutboundMessage));
2518
+ ws.send(JSON.stringify({ type: "typing", isTyping: false, sessionId: routingSessionId } as OutboundMessage));
2510
2519
 
2511
2520
  // Don't send text message if already streamed (content came via onToken)
2512
2521
  const alreadyStreamed = streamedContent.length > 0;
@@ -2516,7 +2525,7 @@ export async function startGateway(config: Config): Promise<void> {
2516
2525
  if (!voiceConfig.ttsProvider) {
2517
2526
  ws.send(JSON.stringify({
2518
2527
  type: "message",
2519
- sessionId: unifiedSessionId,
2528
+ sessionId: routingSessionId,
2520
2529
  content: `${content}\n\n🔊 Para recibir respuestas en audio, configura el proveedor TTS en Configuración > Canales > WebChat (ej: elevenlabs)`,
2521
2530
  isStep: false
2522
2531
  } as OutboundMessage));
@@ -2530,7 +2539,7 @@ export async function startGateway(config: Config): Promise<void> {
2530
2539
  const base64Audio = (audioOutput.data as Buffer).toString("base64");
2531
2540
  ws.send(JSON.stringify({
2532
2541
  type: "message",
2533
- sessionId: unifiedSessionId,
2542
+ sessionId: routingSessionId,
2534
2543
  content,
2535
2544
  audio: base64Audio,
2536
2545
  mimeType: audioOutput.mimeType,
@@ -2538,11 +2547,11 @@ export async function startGateway(config: Config): Promise<void> {
2538
2547
  } as OutboundMessage));
2539
2548
  } catch (ttsError) {
2540
2549
  log.error(`TTS failed: ${(ttsError as Error).message}), sending text instead`);
2541
- ws.send(JSON.stringify({ type: "message", sessionId: unifiedSessionId, content, isStep: false } as OutboundMessage));
2550
+ ws.send(JSON.stringify({ type: "message", sessionId: routingSessionId, content, isStep: false } as OutboundMessage));
2542
2551
  }
2543
2552
  }
2544
2553
  } else {
2545
- ws.send(JSON.stringify({ type: "message", sessionId: unifiedSessionId, content, isStep: false } as OutboundMessage));
2554
+ ws.send(JSON.stringify({ type: "message", sessionId: routingSessionId, content, isStep: false } as OutboundMessage));
2546
2555
  }
2547
2556
  } else if (alreadyStreamed && shouldSpeak && voiceConfig.ttsProvider) {
2548
2557
  try {
@@ -2552,7 +2561,7 @@ export async function startGateway(config: Config): Promise<void> {
2552
2561
  log.info(`Audio generated after streaming: ${base64Audio.length} bytes`);
2553
2562
  ws.send(JSON.stringify({
2554
2563
  type: "message",
2555
- sessionId: unifiedSessionId,
2564
+ sessionId: routingSessionId,
2556
2565
  content,
2557
2566
  audio: base64Audio,
2558
2567
  mimeType: audioOutput.mimeType,
@@ -2563,15 +2572,14 @@ export async function startGateway(config: Config): Promise<void> {
2563
2572
  }
2564
2573
  }
2565
2574
  } catch (error) {
2566
- const unifiedSessionId = msg.sessionId;
2567
2575
  // Detener typing aunque falle — nunca dejar el spinner infinito
2568
- ws.send(JSON.stringify({ type: "typing", isTyping: false, sessionId: unifiedSessionId } as OutboundMessage));
2576
+ ws.send(JSON.stringify({ type: "typing", isTyping: false, sessionId: msg.sessionId } as OutboundMessage));
2569
2577
  ws.send(JSON.stringify({
2570
2578
  type: "error",
2571
- sessionId: unifiedSessionId,
2579
+ sessionId: msg.sessionId,
2572
2580
  error: (error as Error).message,
2573
2581
  } as OutboundMessage));
2574
- log.error(`Error for session ${unifiedSessionId}: ${(error as Error).message}`);
2582
+ log.error(`Error for session ${msg.sessionId}: ${(error as Error).message}`);
2575
2583
  }
2576
2584
  });
2577
2585
 
@@ -2730,7 +2738,7 @@ export async function startGateway(config: Config): Promise<void> {
2730
2738
  process.exit(0);
2731
2739
  });
2732
2740
 
2733
- process.on("SIGHUP", async () => {
2741
+ if (process.platform !== "win32") process.on("SIGHUP", async () => {
2734
2742
  log.info("Received SIGHUP, reloading configuration...");
2735
2743
  try {
2736
2744
  const newConfig = await loadConfig();
@@ -2741,4 +2749,4 @@ export async function startGateway(config: Config): Promise<void> {
2741
2749
  log.error(`Failed to reload configuration: ${(error as Error).message}`);
2742
2750
  }
2743
2751
  });
2744
- }
2752
+ }
@@ -4,7 +4,7 @@
4
4
  * Los datos se guardan en $HIVE_HOME/tts/ (por defecto ~/.hive/tts/).
5
5
  */
6
6
 
7
- import { existsSync, mkdirSync, readdirSync, renameSync } from "fs"
7
+ import { existsSync, mkdirSync, readdirSync, renameSync, unlinkSync, rmSync } from "fs"
8
8
  import { join } from "path"
9
9
  import { homedir } from "os"
10
10
  import {
@@ -37,16 +37,24 @@ async function extractTarGz(archivePath: string, destDir: string): Promise<void>
37
37
  stderr: "inherit",
38
38
  }).exited
39
39
  if (code !== 0) throw new Error(`tar falló con código ${code}`)
40
- await Bun.spawn(["rm", "-f", archivePath]).exited
40
+ unlinkSync(archivePath)
41
41
  }
42
42
 
43
43
  async function extractZip(archivePath: string, destDir: string): Promise<void> {
44
- const code = await Bun.spawn(["unzip", "-q", archivePath, "-d", destDir], {
45
- stdout: "inherit",
46
- stderr: "inherit",
47
- }).exited
48
- if (code !== 0) throw new Error(`unzip falló con código ${code}`)
49
- await Bun.spawn(["rm", "-f", archivePath]).exited
44
+ let code: number
45
+ if (process.platform === "win32") {
46
+ code = await Bun.spawn(
47
+ ["powershell", "-Command", `Expand-Archive -Path "${archivePath}" -DestinationPath "${destDir}" -Force`],
48
+ { stdout: "inherit", stderr: "inherit" }
49
+ ).exited
50
+ } else {
51
+ code = await Bun.spawn(["unzip", "-q", archivePath, "-d", destDir], {
52
+ stdout: "inherit",
53
+ stderr: "inherit",
54
+ }).exited
55
+ }
56
+ if (code !== 0) throw new Error(`Extracción ZIP falló con código ${code}`)
57
+ unlinkSync(archivePath)
50
58
  }
51
59
 
52
60
  /**
@@ -86,7 +94,7 @@ export async function runInstall(ttsRoot: string): Promise<void> {
86
94
  for (const entry of readdirSync(tempDir)) {
87
95
  renameSync(join(tempDir, entry), join(BIN_DIR, entry))
88
96
  }
89
- await Bun.spawn(["rm", "-rf", tempDir]).exited
97
+ rmSync(tempDir, { recursive: true, force: true })
90
98
  }
91
99
 
92
100
  if (!existsSync(binaryPath)) {