@johpaz/hive-agents 0.0.39 → 0.0.40

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 (202) hide show
  1. package/README.md +29 -69
  2. package/dist/hive.js +2522 -1927
  3. package/dist/tool-worker.js +1791 -1334
  4. package/dist/ui/assets/AgentCreateForm-b7xHyfNc.js +1 -0
  5. package/dist/ui/assets/AgentDetailPage-VHy3M7mI.js +1 -0
  6. package/dist/ui/assets/AgentNewPage-BnWImQMx.js +1 -0
  7. package/dist/ui/{dist/assets/AgentsPage-C-GSRk-N.js → assets/AgentsPage-WFy5abqx.js} +1 -1
  8. package/dist/ui/assets/ApiClientPage-BV7zLIL7.js +3 -0
  9. package/dist/ui/assets/{CanvasPage-Cvs5ctza.js → CanvasPage-B9zuIrTm.js} +1 -1
  10. package/dist/ui/assets/{ChannelsPage-C5m_L7P9.js → ChannelsPage-DiN3NvIM.js} +1 -1
  11. package/dist/ui/assets/{DashboardPage-CztbRQdm.js → DashboardPage-CHjARjVK.js} +1 -1
  12. package/dist/ui/assets/{LoginPage-OMsrx5oj.js → LoginPage-Dwd_XxoU.js} +1 -1
  13. package/dist/ui/{dist/assets/LogsPage-CcYYwjgF.js → assets/LogsPage-DW8Nnqe6.js} +1 -1
  14. package/dist/ui/assets/MeetingPage-D3bkiKYt.js +1 -0
  15. package/dist/ui/assets/{NotFound-GbAJDgoD.js → NotFound-BIUDlIXU.js} +1 -1
  16. package/dist/ui/assets/ProvidersPage-UqsDAxPQ.js +1 -0
  17. package/dist/ui/{dist/assets/RecoverPage-CwB2ByCU.js → assets/RecoverPage-8hTjr_JU.js} +1 -1
  18. package/dist/ui/assets/SettingsPage-DNa0jOkA.js +9 -0
  19. package/dist/ui/assets/{SetupPage-DOVh1ldK.js → SetupPage-Bdm2irQG.js} +1 -1
  20. package/dist/ui/assets/WebChatPage-DElJg6P2.js +16 -0
  21. package/dist/ui/assets/accordion-CnLzKNHK.js +1 -0
  22. package/dist/ui/{dist/assets/alert-D_2Y3qjL.js → assets/alert-DylmSCDJ.js} +1 -1
  23. package/dist/ui/{dist/assets/alert-dialog-CpMxaNcu.js → assets/alert-dialog-DNNWN_SI.js} +1 -1
  24. package/dist/ui/assets/{badge-CxTPR6_t.js → badge-ChENFgkC.js} +1 -1
  25. package/dist/ui/assets/chevron-down-DIosfU_U.js +1 -0
  26. package/dist/ui/assets/chevron-up-CI-W21Fy.js +1 -0
  27. package/dist/ui/{dist/assets/dialog-DfS3idb3.js → assets/dialog-BJ-npIv8.js} +1 -1
  28. package/dist/ui/assets/{dropdown-menu-BdCbAW1z.js → dropdown-menu-DDiaHg5y.js} +1 -1
  29. package/dist/ui/assets/{es-Cz5h9_84.js → es-ecSKCyB6.js} +1 -1
  30. package/dist/ui/assets/index-CawKP29y.js +116 -0
  31. package/dist/ui/assets/index-DIcsEkyd.css +2 -0
  32. package/dist/ui/{dist/assets/label-byJkqOYq.js → assets/label-Bi6udtSd.js} +1 -1
  33. package/dist/ui/assets/select-BQCOjM2j.js +1 -0
  34. package/dist/ui/assets/useProviders-Dlizq_8q.js +1 -0
  35. package/dist/ui/dist/assets/AgentCreateForm-b7xHyfNc.js +1 -0
  36. package/dist/ui/dist/assets/AgentDetailPage-VHy3M7mI.js +1 -0
  37. package/dist/ui/dist/assets/AgentNewPage-BnWImQMx.js +1 -0
  38. package/dist/ui/{assets/AgentsPage-C-GSRk-N.js → dist/assets/AgentsPage-WFy5abqx.js} +1 -1
  39. package/dist/ui/dist/assets/ApiClientPage-BV7zLIL7.js +3 -0
  40. package/dist/ui/dist/assets/{CanvasPage-Cvs5ctza.js → CanvasPage-B9zuIrTm.js} +1 -1
  41. package/dist/ui/dist/assets/{ChannelsPage-C5m_L7P9.js → ChannelsPage-DiN3NvIM.js} +1 -1
  42. package/dist/ui/dist/assets/{DashboardPage-CztbRQdm.js → DashboardPage-CHjARjVK.js} +1 -1
  43. package/dist/ui/dist/assets/{LoginPage-OMsrx5oj.js → LoginPage-Dwd_XxoU.js} +1 -1
  44. package/dist/ui/{assets/LogsPage-CcYYwjgF.js → dist/assets/LogsPage-DW8Nnqe6.js} +1 -1
  45. package/dist/ui/dist/assets/MeetingPage-D3bkiKYt.js +1 -0
  46. package/dist/ui/dist/assets/{NotFound-GbAJDgoD.js → NotFound-BIUDlIXU.js} +1 -1
  47. package/dist/ui/dist/assets/ProvidersPage-UqsDAxPQ.js +1 -0
  48. package/dist/ui/{assets/RecoverPage-CwB2ByCU.js → dist/assets/RecoverPage-8hTjr_JU.js} +1 -1
  49. package/dist/ui/dist/assets/SettingsPage-DNa0jOkA.js +9 -0
  50. package/dist/ui/dist/assets/{SetupPage-DOVh1ldK.js → SetupPage-Bdm2irQG.js} +1 -1
  51. package/dist/ui/dist/assets/WebChatPage-DElJg6P2.js +16 -0
  52. package/dist/ui/dist/assets/accordion-CnLzKNHK.js +1 -0
  53. package/dist/ui/{assets/alert-D_2Y3qjL.js → dist/assets/alert-DylmSCDJ.js} +1 -1
  54. package/dist/ui/{assets/alert-dialog-CpMxaNcu.js → dist/assets/alert-dialog-DNNWN_SI.js} +1 -1
  55. package/dist/ui/dist/assets/{badge-CxTPR6_t.js → badge-ChENFgkC.js} +1 -1
  56. package/dist/ui/dist/assets/chevron-down-DIosfU_U.js +1 -0
  57. package/dist/ui/dist/assets/chevron-up-CI-W21Fy.js +1 -0
  58. package/dist/ui/{assets/dialog-DfS3idb3.js → dist/assets/dialog-BJ-npIv8.js} +1 -1
  59. package/dist/ui/dist/assets/{dropdown-menu-BdCbAW1z.js → dropdown-menu-DDiaHg5y.js} +1 -1
  60. package/dist/ui/dist/assets/{es-Cz5h9_84.js → es-ecSKCyB6.js} +1 -1
  61. package/dist/ui/dist/assets/index-CawKP29y.js +116 -0
  62. package/dist/ui/dist/assets/index-DIcsEkyd.css +2 -0
  63. package/dist/ui/{assets/label-byJkqOYq.js → dist/assets/label-Bi6udtSd.js} +1 -1
  64. package/dist/ui/dist/assets/select-BQCOjM2j.js +1 -0
  65. package/dist/ui/dist/assets/useProviders-Dlizq_8q.js +1 -0
  66. package/dist/ui/dist/index.html +4 -4
  67. package/dist/ui/index.html +4 -4
  68. package/package.json +1 -1
  69. package/packages/cli/src/commands/gateway.ts +1 -1
  70. package/packages/cli/src/commands/onboard.ts +27 -1
  71. package/packages/core/src/agent/agent-loop.ts +104 -2
  72. package/packages/core/src/agent/context-compiler.ts +6 -0
  73. package/packages/core/src/agent/llm-client.ts +2 -0
  74. package/packages/core/src/agent/llm-providers/hiveagents.ts +248 -0
  75. package/packages/core/src/agent/llm-providers/interface.ts +2 -0
  76. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +49 -25
  77. package/packages/core/src/agent/providers/index.ts +3 -2
  78. package/packages/core/src/agent/stuck-loop.ts +90 -14
  79. package/packages/core/src/channels/base.ts +7 -1
  80. package/packages/core/src/config/loader.ts +1 -1
  81. package/packages/core/src/gateway/routes/providers.ts +56 -0
  82. package/packages/core/src/gateway/server.ts +120 -55
  83. package/packages/core/src/gateway/slash-commands.ts +7 -1
  84. package/packages/core/src/storage/onboarding.ts +28 -0
  85. package/packages/core/src/storage/seed.ts +14 -0
  86. package/packages/skills/src/bundled-data.generated.ts +1357 -1357
  87. package/dist/ui/assets/AgentCreateForm-BTCzFbca.js +0 -1
  88. package/dist/ui/assets/AgentDetailPage-o27TRSVw.js +0 -1
  89. package/dist/ui/assets/AgentNewPage-400cCpYt.js +0 -1
  90. package/dist/ui/assets/ApiClientPage-BOTpz6oP.js +0 -3
  91. package/dist/ui/assets/MeetingPage-CrKVAfe6.js +0 -1
  92. package/dist/ui/assets/ProvidersPage-uqPcZUSV.js +0 -1
  93. package/dist/ui/assets/SettingsPage-DKLlye0z.js +0 -9
  94. package/dist/ui/assets/WebChatPage-c-7S9jnT.js +0 -16
  95. package/dist/ui/assets/accordion-DAbcVQCn.js +0 -1
  96. package/dist/ui/assets/chevron-up-BYhk0K2J.js +0 -1
  97. package/dist/ui/assets/index-CmGm_r89.js +0 -116
  98. package/dist/ui/assets/index-T7HgphSn.css +0 -2
  99. package/dist/ui/assets/select-Cl16QYa_.js +0 -1
  100. package/dist/ui/assets/useProviders-eEri6BAc.js +0 -1
  101. package/dist/ui/dist/assets/AgentCreateForm-BTCzFbca.js +0 -1
  102. package/dist/ui/dist/assets/AgentDetailPage-o27TRSVw.js +0 -1
  103. package/dist/ui/dist/assets/AgentNewPage-400cCpYt.js +0 -1
  104. package/dist/ui/dist/assets/ApiClientPage-BOTpz6oP.js +0 -3
  105. package/dist/ui/dist/assets/MeetingPage-CrKVAfe6.js +0 -1
  106. package/dist/ui/dist/assets/ProvidersPage-uqPcZUSV.js +0 -1
  107. package/dist/ui/dist/assets/SettingsPage-DKLlye0z.js +0 -9
  108. package/dist/ui/dist/assets/WebChatPage-c-7S9jnT.js +0 -16
  109. package/dist/ui/dist/assets/accordion-DAbcVQCn.js +0 -1
  110. package/dist/ui/dist/assets/chevron-up-BYhk0K2J.js +0 -1
  111. package/dist/ui/dist/assets/index-CmGm_r89.js +0 -116
  112. package/dist/ui/dist/assets/index-T7HgphSn.css +0 -2
  113. package/dist/ui/dist/assets/select-Cl16QYa_.js +0 -1
  114. package/dist/ui/dist/assets/useProviders-eEri6BAc.js +0 -1
  115. /package/dist/ui/assets/{card-CXAm46at.js → card-DFKnZ6ky.js} +0 -0
  116. /package/dist/ui/assets/{circle-alert-CyHDwUj8.js → circle-alert-KuAm2FWh.js} +0 -0
  117. /package/dist/ui/assets/{circle-check-Bb54Ebmu.js → circle-check-6Ard1-2z.js} +0 -0
  118. /package/dist/ui/assets/{circle-x-Bv6WrUJo.js → circle-x-DjLkFDO8.js} +0 -0
  119. /package/dist/ui/assets/{copy-dU94ZGsi.js → copy-Bu5d7C-0.js} +0 -0
  120. /package/dist/ui/assets/{cpu-DSpPVLAz.js → cpu-KDy6-FAI.js} +0 -0
  121. /package/dist/ui/assets/{download-D9ZyUZZR.js → download-Cjbk4Rek.js} +0 -0
  122. /package/dist/ui/assets/{external-link-CHPbUorN.js → external-link-6sTlRDUR.js} +0 -0
  123. /package/dist/ui/assets/{eye-epHJZ_nQ.js → eye-Df8o0tkC.js} +0 -0
  124. /package/dist/ui/assets/{file-text-BEjEmgby.js → file-text-lnxnjBp0.js} +0 -0
  125. /package/dist/ui/assets/{folder-open-iQMHVEqS.js → folder-open-DJBLDFjv.js} +0 -0
  126. /package/dist/ui/assets/{format-oFACFaca.js → format-BwdV8bB5.js} +0 -0
  127. /package/dist/ui/assets/{gateway-url-iG-C6Agn.js → gateway-url-DwzPmoc8.js} +0 -0
  128. /package/dist/ui/assets/{gauge-D0_GMEcq.js → gauge-B8Tj43rC.js} +0 -0
  129. /package/dist/ui/assets/{hexagon-DsGOUl-H.js → hexagon-6L79pgVK.js} +0 -0
  130. /package/dist/ui/assets/{history-BSG-Ypqf.js → history-CAF_R34_.js} +0 -0
  131. /package/dist/ui/assets/{info-NwLoa2Mj.js → info-WjromB4Y.js} +0 -0
  132. /package/dist/ui/assets/{key-3EP0dhkT.js → key-DyKOoQh5.js} +0 -0
  133. /package/dist/ui/assets/{loader-circle-CZNax6kS.js → loader-circle-BmBOgYze.js} +0 -0
  134. /package/dist/ui/assets/{lock-Ei1_J-Nq.js → lock-BS6OLXPv.js} +0 -0
  135. /package/dist/ui/assets/{pause-BUqah9Bi.js → pause-VqeUmp2Z.js} +0 -0
  136. /package/dist/ui/assets/{play-NcZ4swwL.js → play-zJpWuhrr.js} +0 -0
  137. /package/dist/ui/assets/{plus-CX1xyhp5.js → plus-BZQX26Dr.js} +0 -0
  138. /package/dist/ui/assets/{progress-Dtz-Mzys.js → progress-D5c-Eilm.js} +0 -0
  139. /package/dist/ui/assets/{refresh-cw-DaYdjQFk.js → refresh-cw-CCzDCAuz.js} +0 -0
  140. /package/dist/ui/assets/{save-CUdYyHNy.js → save-hUmZhceG.js} +0 -0
  141. /package/dist/ui/assets/{scroll-area-BXtLsE9E.js → scroll-area-CihOx0cb.js} +0 -0
  142. /package/dist/ui/assets/{search-BGmPJ-6L.js → search-DzDptO9s.js} +0 -0
  143. /package/dist/ui/assets/{send-BuQcUO-R.js → send-BPk9XbIq.js} +0 -0
  144. /package/dist/ui/assets/{settings-CcMGI1iU.js → settings-BGfrZ_zM.js} +0 -0
  145. /package/dist/ui/assets/{shield-C-05qB-2.js → shield-CxhcUT39.js} +0 -0
  146. /package/dist/ui/assets/{slider-D2I0qven.js → slider-bcUiUfx0.js} +0 -0
  147. /package/dist/ui/assets/{sparkles-D6fx8JC5.js → sparkles-BhwlS1pc.js} +0 -0
  148. /package/dist/ui/assets/{square-BD81nFtN.js → square-DMNWw4Hi.js} +0 -0
  149. /package/dist/ui/assets/{switch-h2SfQX4B.js → switch-BR30E4ej.js} +0 -0
  150. /package/dist/ui/assets/{table-CVkIRJKK.js → table-B3aGEaVp.js} +0 -0
  151. /package/dist/ui/assets/{tabs-C619jxbO.js → tabs-LQidMKRS.js} +0 -0
  152. /package/dist/ui/assets/{terminal-DN38Q456.js → terminal--7G943As.js} +0 -0
  153. /package/dist/ui/assets/{textarea-wvA-FDjO.js → textarea-B6Z1Zc6W.js} +0 -0
  154. /package/dist/ui/assets/{trash-2-BHRa5ft9.js → trash-2-xD2o4SgX.js} +0 -0
  155. /package/dist/ui/assets/{triangle-alert-D4nwAVbc.js → triangle-alert-pVIJGjga.js} +0 -0
  156. /package/dist/ui/assets/{vendor-router-pCP7sjma.js → vendor-router-gqiZ7xhx.js} +0 -0
  157. /package/dist/ui/assets/{volume-2-B6tkRy2u.js → volume-2-BekVQl6P.js} +0 -0
  158. /package/dist/ui/assets/{zap-QO7iWMRg.js → zap-B4RaNNO5.js} +0 -0
  159. /package/dist/ui/dist/assets/{card-CXAm46at.js → card-DFKnZ6ky.js} +0 -0
  160. /package/dist/ui/dist/assets/{circle-alert-CyHDwUj8.js → circle-alert-KuAm2FWh.js} +0 -0
  161. /package/dist/ui/dist/assets/{circle-check-Bb54Ebmu.js → circle-check-6Ard1-2z.js} +0 -0
  162. /package/dist/ui/dist/assets/{circle-x-Bv6WrUJo.js → circle-x-DjLkFDO8.js} +0 -0
  163. /package/dist/ui/dist/assets/{copy-dU94ZGsi.js → copy-Bu5d7C-0.js} +0 -0
  164. /package/dist/ui/dist/assets/{cpu-DSpPVLAz.js → cpu-KDy6-FAI.js} +0 -0
  165. /package/dist/ui/dist/assets/{download-D9ZyUZZR.js → download-Cjbk4Rek.js} +0 -0
  166. /package/dist/ui/dist/assets/{external-link-CHPbUorN.js → external-link-6sTlRDUR.js} +0 -0
  167. /package/dist/ui/dist/assets/{eye-epHJZ_nQ.js → eye-Df8o0tkC.js} +0 -0
  168. /package/dist/ui/dist/assets/{file-text-BEjEmgby.js → file-text-lnxnjBp0.js} +0 -0
  169. /package/dist/ui/dist/assets/{folder-open-iQMHVEqS.js → folder-open-DJBLDFjv.js} +0 -0
  170. /package/dist/ui/dist/assets/{format-oFACFaca.js → format-BwdV8bB5.js} +0 -0
  171. /package/dist/ui/dist/assets/{gateway-url-iG-C6Agn.js → gateway-url-DwzPmoc8.js} +0 -0
  172. /package/dist/ui/dist/assets/{gauge-D0_GMEcq.js → gauge-B8Tj43rC.js} +0 -0
  173. /package/dist/ui/dist/assets/{hexagon-DsGOUl-H.js → hexagon-6L79pgVK.js} +0 -0
  174. /package/dist/ui/dist/assets/{history-BSG-Ypqf.js → history-CAF_R34_.js} +0 -0
  175. /package/dist/ui/dist/assets/{info-NwLoa2Mj.js → info-WjromB4Y.js} +0 -0
  176. /package/dist/ui/dist/assets/{key-3EP0dhkT.js → key-DyKOoQh5.js} +0 -0
  177. /package/dist/ui/dist/assets/{loader-circle-CZNax6kS.js → loader-circle-BmBOgYze.js} +0 -0
  178. /package/dist/ui/dist/assets/{lock-Ei1_J-Nq.js → lock-BS6OLXPv.js} +0 -0
  179. /package/dist/ui/dist/assets/{pause-BUqah9Bi.js → pause-VqeUmp2Z.js} +0 -0
  180. /package/dist/ui/dist/assets/{play-NcZ4swwL.js → play-zJpWuhrr.js} +0 -0
  181. /package/dist/ui/dist/assets/{plus-CX1xyhp5.js → plus-BZQX26Dr.js} +0 -0
  182. /package/dist/ui/dist/assets/{progress-Dtz-Mzys.js → progress-D5c-Eilm.js} +0 -0
  183. /package/dist/ui/dist/assets/{refresh-cw-DaYdjQFk.js → refresh-cw-CCzDCAuz.js} +0 -0
  184. /package/dist/ui/dist/assets/{save-CUdYyHNy.js → save-hUmZhceG.js} +0 -0
  185. /package/dist/ui/dist/assets/{scroll-area-BXtLsE9E.js → scroll-area-CihOx0cb.js} +0 -0
  186. /package/dist/ui/dist/assets/{search-BGmPJ-6L.js → search-DzDptO9s.js} +0 -0
  187. /package/dist/ui/dist/assets/{send-BuQcUO-R.js → send-BPk9XbIq.js} +0 -0
  188. /package/dist/ui/dist/assets/{settings-CcMGI1iU.js → settings-BGfrZ_zM.js} +0 -0
  189. /package/dist/ui/dist/assets/{shield-C-05qB-2.js → shield-CxhcUT39.js} +0 -0
  190. /package/dist/ui/dist/assets/{slider-D2I0qven.js → slider-bcUiUfx0.js} +0 -0
  191. /package/dist/ui/dist/assets/{sparkles-D6fx8JC5.js → sparkles-BhwlS1pc.js} +0 -0
  192. /package/dist/ui/dist/assets/{square-BD81nFtN.js → square-DMNWw4Hi.js} +0 -0
  193. /package/dist/ui/dist/assets/{switch-h2SfQX4B.js → switch-BR30E4ej.js} +0 -0
  194. /package/dist/ui/dist/assets/{table-CVkIRJKK.js → table-B3aGEaVp.js} +0 -0
  195. /package/dist/ui/dist/assets/{tabs-C619jxbO.js → tabs-LQidMKRS.js} +0 -0
  196. /package/dist/ui/dist/assets/{terminal-DN38Q456.js → terminal--7G943As.js} +0 -0
  197. /package/dist/ui/dist/assets/{textarea-wvA-FDjO.js → textarea-B6Z1Zc6W.js} +0 -0
  198. /package/dist/ui/dist/assets/{trash-2-BHRa5ft9.js → trash-2-xD2o4SgX.js} +0 -0
  199. /package/dist/ui/dist/assets/{triangle-alert-D4nwAVbc.js → triangle-alert-pVIJGjga.js} +0 -0
  200. /package/dist/ui/dist/assets/{vendor-router-pCP7sjma.js → vendor-router-gqiZ7xhx.js} +0 -0
  201. /package/dist/ui/dist/assets/{volume-2-B6tkRy2u.js → volume-2-BekVQl6P.js} +0 -0
  202. /package/dist/ui/dist/assets/{zap-QO7iWMRg.js → zap-B4RaNNO5.js} +0 -0
@@ -0,0 +1 @@
1
+ import{r as e}from"./rolldown-runtime-S-ySWqyJ.js";import{_ as t}from"./vendor-charts-Bu2lyBKP.js";import{$t as n,Gt as r,Ht as i,Jt as a,Kt as o,Qt as s,Ut as c,Vt as l,Wt as u,Xt as d,Yt as f,Zt as p,en as m,qt as h,tn as g}from"./vendor-radix-D6rA7xKY.js";import{o as _}from"./vendor-query-DsWPbQdG.js";import{r as v,t as y}from"./utils-3pnRFmFe.js";import{t as b}from"./chevron-down-DIosfU_U.js";import{t as x}from"./chevron-up-CI-W21Fy.js";var S=v(`Check`,[[`path`,{d:`M20 6 9 17l-5-5`,key:`1gmf2c`}]]),C=e(t(),1),w=_(),T=f,E=i,D=m,O=C.forwardRef(({className:e,children:t,...r},i)=>(0,w.jsxs)(n,{ref:i,className:y(`flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 cursor-pointer`,e),...r,children:[t,(0,w.jsx)(c,{asChild:!0,children:(0,w.jsx)(b,{className:`h-4 w-4 opacity-50`})})]}));O.displayName=n.displayName;var k=C.forwardRef(({className:e,...t},n)=>(0,w.jsx)(p,{ref:n,className:y(`flex cursor-default items-center justify-center py-1`,e),...t,children:(0,w.jsx)(x,{className:`h-4 w-4`})}));k.displayName=p.displayName;var A=C.forwardRef(({className:e,...t},n)=>(0,w.jsx)(d,{ref:n,className:y(`flex cursor-default items-center justify-center py-1`,e),...t,children:(0,w.jsx)(b,{className:`h-4 w-4`})}));A.displayName=d.displayName;var j=C.forwardRef(({className:e,children:t,position:n=`popper`,align:r=`center`,...i},o)=>(0,w.jsx)(a,{children:(0,w.jsxs)(l,{ref:o,className:y(`relative z-[10001] max-h-[--radix-select-content-available-height] min-w-[8rem] origin-[--radix-select-content-transform-origin] overflow-x-hidden overflow-y-auto rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2`,n===`popper`&&`data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1`,e),position:n,align:r,...i,children:[(0,w.jsx)(k,{}),(0,w.jsx)(g,{className:y(`p-1`,n===`popper`&&`h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1`),children:t}),(0,w.jsx)(A,{})]})}));j.displayName=l.displayName;var M=C.forwardRef(({className:e,...t},n)=>(0,w.jsx)(h,{ref:n,className:y(`py-1.5 pl-8 pr-2 text-sm font-semibold`,e),...t}));M.displayName=h.displayName;var N=C.forwardRef(({className:e,children:t,...n},i)=>(0,w.jsxs)(u,{ref:i,className:y(`relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 focus:bg-accent focus:text-accent-foreground`,e),...n,children:[(0,w.jsx)(`span`,{className:`absolute left-2 flex h-3.5 w-3.5 items-center justify-center`,children:(0,w.jsx)(r,{children:(0,w.jsx)(S,{className:`h-4 w-4`})})}),(0,w.jsx)(o,{children:t})]}));N.displayName=u.displayName;var P=C.forwardRef(({className:e,...t},n)=>(0,w.jsx)(s,{ref:n,className:y(`-mx-1 my-1 h-px bg-muted`,e),...t}));P.displayName=s.displayName;export{M as a,S as c,N as i,j as n,O as o,E as r,D as s,T as t};
@@ -0,0 +1 @@
1
+ import"./index-CawKP29y.js";
@@ -21,16 +21,16 @@
21
21
  <meta name="twitter:title" content="Hive">
22
22
  <meta property="og:description" content="Hive Agents">
23
23
  <meta name="twitter:description" content="Hive Agents">
24
- <script type="module" crossorigin src="/assets/index-CmGm_r89.js"></script>
24
+ <script type="module" crossorigin src="/assets/index-CawKP29y.js"></script>
25
25
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-S-ySWqyJ.js">
26
26
  <link rel="modulepreload" crossorigin href="/assets/vendor-charts-Bu2lyBKP.js">
27
- <link rel="modulepreload" crossorigin href="/assets/vendor-router-pCP7sjma.js">
27
+ <link rel="modulepreload" crossorigin href="/assets/vendor-router-gqiZ7xhx.js">
28
28
  <link rel="modulepreload" crossorigin href="/assets/vendor-query-DsWPbQdG.js">
29
29
  <link rel="modulepreload" crossorigin href="/assets/vendor-radix-D6rA7xKY.js">
30
30
  <link rel="modulepreload" crossorigin href="/assets/utils-3pnRFmFe.js">
31
31
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BU5iQU4f.js">
32
- <link rel="modulepreload" crossorigin href="/assets/gateway-url-iG-C6Agn.js">
33
- <link rel="stylesheet" crossorigin href="/assets/index-T7HgphSn.css">
32
+ <link rel="modulepreload" crossorigin href="/assets/gateway-url-DwzPmoc8.js">
33
+ <link rel="stylesheet" crossorigin href="/assets/index-DIcsEkyd.css">
34
34
  </head>
35
35
 
36
36
  <body>
@@ -21,16 +21,16 @@
21
21
  <meta name="twitter:title" content="Hive">
22
22
  <meta property="og:description" content="Hive Agents">
23
23
  <meta name="twitter:description" content="Hive Agents">
24
- <script type="module" crossorigin src="/assets/index-CmGm_r89.js"></script>
24
+ <script type="module" crossorigin src="/assets/index-CawKP29y.js"></script>
25
25
  <link rel="modulepreload" crossorigin href="/assets/rolldown-runtime-S-ySWqyJ.js">
26
26
  <link rel="modulepreload" crossorigin href="/assets/vendor-charts-Bu2lyBKP.js">
27
- <link rel="modulepreload" crossorigin href="/assets/vendor-router-pCP7sjma.js">
27
+ <link rel="modulepreload" crossorigin href="/assets/vendor-router-gqiZ7xhx.js">
28
28
  <link rel="modulepreload" crossorigin href="/assets/vendor-query-DsWPbQdG.js">
29
29
  <link rel="modulepreload" crossorigin href="/assets/vendor-radix-D6rA7xKY.js">
30
30
  <link rel="modulepreload" crossorigin href="/assets/utils-3pnRFmFe.js">
31
31
  <link rel="modulepreload" crossorigin href="/assets/vendor-react-BU5iQU4f.js">
32
- <link rel="modulepreload" crossorigin href="/assets/gateway-url-iG-C6Agn.js">
33
- <link rel="stylesheet" crossorigin href="/assets/index-T7HgphSn.css">
32
+ <link rel="modulepreload" crossorigin href="/assets/gateway-url-DwzPmoc8.js">
33
+ <link rel="stylesheet" crossorigin href="/assets/index-DIcsEkyd.css">
34
34
  </head>
35
35
 
36
36
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johpaz/hive-agents",
3
- "version": "0.0.39",
3
+ "version": "0.0.40",
4
4
  "description": "Tu colmena de agentes IA. Local-first. Multi-canal. Open source. Construido desde Colombia para el mundo.",
5
5
  "private": false,
6
6
  "bin": {
@@ -365,7 +365,7 @@ export async function start(flags: string[]): Promise<void> {
365
365
  ║ ██║ ██║██║ ╚████╔╝ ███████╗ ║
366
366
  ║ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚══════╝ ║
367
367
  ║ ║
368
- ║ Personal Swarm AI Gateway — v0.0.39
368
+ ║ Personal Swarm AI Gateway — v0.0.40
369
369
  ╚════════════════════════════════════════════╝
370
370
 
371
371
  📦 Installation: ${adapter.name}
@@ -50,7 +50,7 @@ function reloadEnvToProcess(hiveDir: string): void {
50
50
  }
51
51
  }
52
52
 
53
- const VERSION = "0.0.39";
53
+ const VERSION = "0.0.40";
54
54
 
55
55
  const DEFAULT_MODELS: Record<string, string> = {
56
56
  anthropic: "claude-sonnet-4-6",
@@ -62,6 +62,7 @@ const DEFAULT_MODELS: Record<string, string> = {
62
62
  openrouter: "meta-llama/llama-3.3-70b-instruct",
63
63
  ollama: "llama3.3:8b",
64
64
  nvidia: "meta/llama-3.3-70b-instruct",
65
+ hiveagents: "Qwen3.6-35B-A3B-UD-Q4_K_M.gguf",
65
66
  };
66
67
 
67
68
  const PROVIDER_BASE_URLS: Record<string, string> = {
@@ -74,6 +75,7 @@ const PROVIDER_BASE_URLS: Record<string, string> = {
74
75
  openrouter: "https://openrouter.ai/api",
75
76
  ollama: "http://localhost:11434",
76
77
  nvidia: "https://integrate.api.nvidia.com/v1",
78
+ hiveagents: "https://llm.hiveagents.io/v1",
77
79
  };
78
80
 
79
81
  const API_KEY_PLACEHOLDERS: Record<string, string> = {
@@ -86,6 +88,7 @@ const API_KEY_PLACEHOLDERS: Record<string, string> = {
86
88
  openrouter: "sk-or-...",
87
89
  ollama: "",
88
90
  nvidia: "nvapi-...",
91
+ hiveagents: "Bearer token...",
89
92
  };
90
93
 
91
94
  const API_KEY_LINKS: Record<string, string> = {
@@ -98,6 +101,7 @@ const API_KEY_LINKS: Record<string, string> = {
98
101
  openrouter: "https://openrouter.ai/keys",
99
102
  ollama: "",
100
103
  nvidia: "https://build.nvidia.com/",
104
+ hiveagents: "",
101
105
  };
102
106
 
103
107
  const AVAILABLE_MODELS: Record<string, Array<{ value: string; label: string; hint?: string }>> = {
@@ -154,6 +158,17 @@ const AVAILABLE_MODELS: Record<string, Array<{ value: string; label: string; hin
154
158
  { value: "google/gemma-4-31b-it", label: "Gemma 4 31B", hint: "Multimodal — 256K contexto" },
155
159
  { value: "moonshotai/kimi-k2-thinking", label: "Kimi K2 Thinking", hint: "Razonamiento profundo — 256K" },
156
160
  ],
161
+ hiveagents: [
162
+ { value: "Qwen3.6-35B-A3B-UD-Q4_K_M.gguf", label: "Qwen3.6 35B MoE Q4 (Recomendado)", hint: "MoE 22.7GB — 62.8 t/s, el más rápido" },
163
+ { value: "Qwen3.6-35B-A3B-UD-Q6_K.gguf", label: "Qwen3.6 35B MoE Q6", hint: "MoE 30GB — 57.7 t/s" },
164
+ { value: "Qwen3-Coder-Next-UD-Q4_K_M.gguf", label: "Qwen3 Coder Next MoE Q4", hint: "MoE 49.3GB — 50.9 t/s, ocupa toda la VRAM" },
165
+ { value: "gemma-4-26B-A4B-UD-Q4_K_M.gguf", label: "Gemma 4 26B MoE Q4", hint: "MoE 16.9GB — 51.5 t/s" },
166
+ { value: "gemma-4-26B-A4B-UD-Q6_K_XL.gguf", label: "Gemma 4 26B MoE Q6", hint: "MoE 23.3GB — 47.8 t/s" },
167
+ { value: "gemma-4-12b-it-UD-Q4_K_XL.gguf", label: "Gemma 4 12B Dense Q4", hint: "Dense 7.4GB — 27.7 t/s" },
168
+ { value: "Qwopus3.6-27B-v2-MTP-Q6_K.gguf", label: "Qwopus 27B Dense+MTP Q6", hint: "Dense+MTP 22.4GB — 17.9 t/s" },
169
+ { value: "Qwen3.6-27B-UD-Q6_K_XL.gguf", label: "Qwen3.6 27B Dense+MTP Q6", hint: "Dense+MTP 26GB — 16 t/s" },
170
+ { value: "gemma-4-31B-it-UD-Q6_K_XL.gguf", label: "Gemma 4 31B Dense Q6", hint: "Dense 27.5GB — 7.6 t/s, mayor calidad" },
171
+ ],
157
172
  };
158
173
 
159
174
  const BUNDLED_SKILLS = [
@@ -390,6 +405,15 @@ async function testLLMConnection(provider: string, apiKey: string, model: string
390
405
  return response.ok;
391
406
  }
392
407
 
408
+ if (provider === "hiveagents") {
409
+ try {
410
+ const response = await fetch("https://llm.hiveagents.io/health");
411
+ return response.ok;
412
+ } catch {
413
+ return false;
414
+ }
415
+ }
416
+
393
417
  return false;
394
418
  } catch {
395
419
  return false;
@@ -542,6 +566,7 @@ async function runUpdateWizard(existing: ExistingConfig): Promise<void> {
542
566
  { value: "nvidia", label: "NVIDIA NIM", hint: "Modelos gratuitos" },
543
567
  { value: "openrouter", label: "OpenRouter", hint: "Multi-modelo" },
544
568
  { value: "ollama", label: "Ollama (local)", hint: "Sin costo" },
569
+ { value: "hiveagents", label: "HiveAgents LLM", hint: "Qwen3.6 / Gemma4 vía Cloudflare" },
545
570
  ],
546
571
  }) as string;
547
572
 
@@ -1057,6 +1082,7 @@ async function runFullWizard(): Promise<void> {
1057
1082
  { value: "ollama", label: "Ollama (Local)", hint: "Gratis, corre localmente" },
1058
1083
  { value: "nvidia", label: "NVIDIA NIM", hint: "Modelos gratuitos en build.nvidia.com" },
1059
1084
  { value: "openrouter", label: "OpenRouter", hint: "Múltiples proveedores" },
1085
+ { value: "hiveagents", label: "HiveAgents LLM", hint: "Local — Qwen3.6 / Gemma4 vía Cloudflare" },
1060
1086
  ],
1061
1087
  initialValue: state.provider,
1062
1088
  });
@@ -28,9 +28,12 @@ import { resolveUserId, resolveAgentId } from "../storage/onboarding"
28
28
  import type { ContentPart } from "../multimodal/types"
29
29
  import { loadConfig } from "../config/loader"
30
30
  import { executeToolBatch } from "../tool-runtime"
31
+ import { createStuckLoopDetector, getInterventionMessage, type StuckLoopState } from "./stuck-loop"
31
32
 
32
33
  const log = logger.child("agent-loop")
33
34
 
35
+ const DEFAULT_MAX_WALL_CLOCK_MS = 5 * 60 * 1000
36
+
34
37
  // ─── Types ────────────────────────────────────────────────────────────────────
35
38
 
36
39
  export interface AgentLoopOptions {
@@ -45,14 +48,19 @@ export interface AgentLoopOptions {
45
48
  isolated?: boolean
46
49
  taskContext?: string | ContentPart[]
47
50
  onStep?: (step: StepEvent) => Promise<void>
51
+ onToken?: (token: string) => void
48
52
  /** User ID for context propagation */
49
53
  userId?: string
50
54
  /** Abort signal to stop generation mid-execution */
51
55
  signal?: AbortSignal
52
56
  /** Clean text for FTS5 and tracing (extracted from userMessage if multimodal) */
53
57
  rawUserMessage?: string
58
+ /** Extra tools to force into the LLM loadout (used by tests/evals). */
59
+ extraTools?: any[]
54
60
  }
55
61
 
62
+ export type { StepEvent as AgentStepEvent }
63
+
56
64
  export interface StepEvent {
57
65
  type: "text" | "tool_call" | "tool_result"
58
66
  message: string
@@ -63,7 +71,7 @@ export interface StepEvent {
63
71
  // ─── Stream chunk types (compatible with providers/index.ts) ─────────────────
64
72
 
65
73
  export interface StreamChunk {
66
- agent?: { messages: any[] }
74
+ agent?: { messages: any[]; streamed?: boolean }
67
75
  tools?: { messages: any[] }
68
76
  usage?: { input_tokens: number; output_tokens: number }
69
77
  }
@@ -82,6 +90,12 @@ export async function* runAgent(
82
90
 
83
91
  const agentName = agent.name || opts.agentId
84
92
  const maxIterations = agent.max_iterations || 10
93
+ const maxWallClockMs = agent.max_wall_clock_ms || DEFAULT_MAX_WALL_CLOCK_MS
94
+ const wallClockDeadline = Date.now() + maxWallClockMs
95
+
96
+ // Stuck-loop protection
97
+ const stuckDetector = createStuckLoopDetector(loadConfig())
98
+ let stuckState: StuckLoopState | undefined
85
99
 
86
100
  // Resolve LLM provider config
87
101
  const providerCfg = await resolveProviderConfig(
@@ -122,6 +136,19 @@ export async function* runAgent(
122
136
  userId: opts.userId,
123
137
  })
124
138
 
139
+ // Force extra tools into the loadout (tests/evals)
140
+ if (opts.extraTools?.length) {
141
+ const existingNames = new Set(ctx.tools.map((t: any) => t.function?.name))
142
+ for (const tool of opts.extraTools) {
143
+ const name = tool.function?.name || tool.name
144
+ if (name && !existingNames.has(name)) {
145
+ ctx.tools.push(tool)
146
+ existingNames.add(name)
147
+ log.info(`[agent-loop] Force-injected tool into loadout: ${name}`)
148
+ }
149
+ }
150
+ }
151
+
125
152
  const systemPrompt = opts.systemPromptOverride || ctx.systemPrompt
126
153
 
127
154
  // Build initial messages array for the model
@@ -143,6 +170,9 @@ export async function* runAgent(
143
170
  let lastToolSignature = ""
144
171
  let consecutiveRepeat = 0
145
172
  let loopDetected = false
173
+ // Stall detection: iterations without a forward-progress tool call (write/click/navigate)
174
+ let idleIterations = 0
175
+ const PROGRESS_TOOLS = new Set(["browser_type", "browser_click", "browser_navigate"])
146
176
 
147
177
  // ── The loop ────────────────────────────────────────────────────────────
148
178
  while (iterations < maxIterations) {
@@ -152,12 +182,25 @@ export async function* runAgent(
152
182
  break
153
183
  }
154
184
 
185
+ if (Date.now() > wallClockDeadline) {
186
+ log.warn(`[agent-loop] Wall-clock timeout exceeded (${maxWallClockMs}ms). Breaking.`)
187
+ finalContent = "La tarea tomó demasiado tiempo. Te sugiero dividirla en pasos más pequeños o darme más detalles para continuar."
188
+ break
189
+ }
190
+
155
191
  iterations++
156
192
 
193
+ let streamedThisCall = false
157
194
  const response = await callLLM({
158
195
  ...providerCfg,
159
196
  messages: clearOldToolResults(messages) as LLMMessage[],
160
197
  tools: ctx.tools.length > 0 ? ctx.tools : undefined,
198
+ onToken: opts.onToken
199
+ ? (token: string) => {
200
+ streamedThisCall = true
201
+ opts.onToken?.(token)
202
+ }
203
+ : undefined,
161
204
  })
162
205
 
163
206
  // Accumulate usage
@@ -169,7 +212,7 @@ export async function* runAgent(
169
212
  // Emit agent chunk (compatible with providers/index.ts)
170
213
  const agentMsg: any = { content: response.content }
171
214
  if (response.tool_calls?.length) agentMsg.tool_calls = response.tool_calls
172
- yield { agent: { messages: [agentMsg] } }
215
+ yield { agent: { messages: [agentMsg], streamed: streamedThisCall } }
173
216
 
174
217
  // Notify onStep for narration text
175
218
  if (opts.onStep && response.content) {
@@ -287,6 +330,10 @@ export async function* runAgent(
287
330
  tool_call_id: tc.id,
288
331
  })
289
332
 
333
+ // Record tool call for stuck-loop detection
334
+ const errorMessage = toolResultLLM.startsWith("[Tool Error]") ? toolResultLLM : undefined
335
+ stuckDetector.recordToolCall(opts.threadId, toolName, tc.function.arguments as Record<string, unknown>, errorMessage)
336
+
290
337
  // Dynamic tool injection: when search_knowledge finds tools (native or MCP), add them to ctx.tools
291
338
  if (toolName === "search_knowledge") {
292
339
  // Use JS object directly (no parse needed)
@@ -463,6 +510,54 @@ export async function* runAgent(
463
510
 
464
511
  if (loopDetected) break
465
512
 
513
+ // Check for stuck loop after each iteration
514
+ stuckState = stuckDetector.check(opts.threadId)
515
+ if (stuckState.detected) {
516
+ const intervention = getInterventionMessage(stuckState)
517
+ log.warn(`[agent-loop] ${intervention}`)
518
+
519
+ if (stuckState.count >= 4) {
520
+ // Critical: break and notify user instead of looping forever
521
+ finalContent = intervention
522
+ loopDetected = true
523
+ emitCanvas("canvas:node_update", {
524
+ nodeId: opts.agentId,
525
+ changes: { status: "stuck", currentTool: stuckState.toolName },
526
+ })
527
+ break
528
+ } else {
529
+ // Warning: inject intervention message so the model changes strategy
530
+ messages.push({
531
+ role: "user",
532
+ content: intervention,
533
+ })
534
+ }
535
+ }
536
+
537
+ // Stall detection: no forward-progress tool for several iterations
538
+ const hadProgress = toolResults.some(
539
+ (r) => PROGRESS_TOOLS.has(r.toolName) && !String(r.result).startsWith("[Tool Error]")
540
+ )
541
+ if (hadProgress) {
542
+ idleIterations = 0
543
+ } else if (toolResults.length > 0) {
544
+ idleIterations++
545
+ }
546
+ if (idleIterations >= 3 && idleIterations < 5) {
547
+ const stallMsg = "ADVERTENCIA: Llevas varios pasos sin modificar la página. Si ya completaste el formulario, responde al usuario. Si no, avanza con browser_type/browser_click en lugar de seguir inspeccionando."
548
+ log.warn(`[agent-loop] ${stallMsg}`)
549
+ messages.push({ role: "user", content: stallMsg })
550
+ } else if (idleIterations >= 5) {
551
+ const stallMsg = "No logré avanzar en el formulario después de varios intentos. Puede que la página no sea compatible o que falten instrucciones. Te sugiero revisar la URL o darme más detalles."
552
+ log.warn(`[agent-loop] Stall break: ${stallMsg}`)
553
+ finalContent = stallMsg
554
+ emitCanvas("canvas:node_update", {
555
+ nodeId: opts.agentId,
556
+ changes: { status: "stuck", currentTool: "NO_PROGRESS" },
557
+ })
558
+ break
559
+ }
560
+
466
561
  emitCanvas("canvas:node_update", {
467
562
  nodeId: opts.agentId,
468
563
  changes: { status: "thinking", currentTool: null },
@@ -603,6 +698,10 @@ export class AgentLoop {
603
698
  raw_user_message?: string
604
699
  }
605
700
  signal?: AbortSignal
701
+ onToken?: (token: string) => void
702
+ onStep?: (step: StepEvent) => Promise<void>
703
+ /** Extra tools to force into the LLM loadout (tests/evals). */
704
+ extraTools?: any[]
606
705
  }
607
706
  ): AsyncIterable<StreamChunk> {
608
707
  // Resolve from database with priority: explicit param → DB lookup → single user/agent
@@ -647,6 +746,9 @@ export class AgentLoop {
647
746
  mcpManager: this.mcpManager,
648
747
  userId,
649
748
  signal: config.signal,
749
+ onToken: config.onToken,
750
+ onStep: config.onStep,
751
+ extraTools: config.extraTools,
650
752
  })
651
753
  }
652
754
 
@@ -183,6 +183,12 @@ export async function compileContext(opts: {
183
183
  // Sanitized name valid for all LLM providers (no spaces, max 64 chars)
184
184
  const fullName = mcpToolFullName(server.name, mcpTool.name)
185
185
 
186
+ // Skip tools whose sanitized name is empty or fails provider validation
187
+ if (!fullName || !/^[a-zA-Z0-9_-]{1,64}$/.test(fullName)) {
188
+ log.warn(`[context-compiler] Skipping MCP tool with unsupported name: "${mcpTool.name}" (server: ${server.name}, sanitized: "${fullName}")`)
189
+ continue
190
+ }
191
+
186
192
  // Executor for agent-loop (has the real call)
187
193
  mcpToolExecutors.push({
188
194
  name: fullName,
@@ -26,6 +26,7 @@ import { NvidiaProvider } from "./llm-providers/nvidia"
26
26
  import { QwenProvider } from "./llm-providers/qwen"
27
27
  import { MiniMaxProvider } from "./llm-providers/minimax"
28
28
  import { OpenCodeGoProvider } from "./llm-providers/opencode-go"
29
+ import { HiveAgentsProvider } from "./llm-providers/hiveagents"
29
30
  import type { LLMProvider } from "./llm-providers/interface"
30
31
 
31
32
  const log = logger.child("llm-client")
@@ -113,6 +114,7 @@ function getProvider(provider: string): LLMProvider {
113
114
  case "qwen": return new QwenProvider()
114
115
  case "minimax": return new MiniMaxProvider()
115
116
  case "opencode-go": return new OpenCodeGoProvider()
117
+ case "hiveagents": return new HiveAgentsProvider()
116
118
  default:
117
119
  log.warn(`[llm-client] Unknown provider "${provider}" — falling back to OpenAI-compatible endpoint`)
118
120
  return new OpenAIProvider()
@@ -0,0 +1,248 @@
1
+ import { logger } from "../../utils/logger"
2
+ import { OpenAICompatBase } from "./openai-compat-base"
3
+ import type { LLMCallOptions, LLMResponse } from "./interface"
4
+
5
+ const log = logger.child("llm-client")
6
+
7
+ const DEFAULT_BASE = "https://llm.hiveagents.io"
8
+
9
+ /** Contexto por defecto que se solicita al backend de HiveAgents al cargar un modelo. */
10
+ export const HIVEAGENTS_DEFAULT_LOAD_CTX = 50000
11
+
12
+ // Cloudflare blocks requests with the OpenAI SDK User-Agent and x-stainless-* fingerprint headers.
13
+ const BLOCKED_HEADERS = [
14
+ "user-agent",
15
+ "x-stainless-lang",
16
+ "x-stainless-package-version",
17
+ "x-stainless-runtime",
18
+ "x-stainless-runtime-version",
19
+ "x-stainless-arch",
20
+ "x-stainless-os",
21
+ ]
22
+
23
+ export interface HiveAgentsLoadResult {
24
+ success: boolean
25
+ loading?: boolean
26
+ error?: string
27
+ }
28
+
29
+ export interface HiveAgentsStatusResult {
30
+ loaded: boolean
31
+ model?: { name?: string; ctx?: number; n_ctx?: number }
32
+ }
33
+
34
+ function getApiBase(baseUrl?: string): string {
35
+ return (baseUrl?.replace(/\/v1\/?$/, "") || DEFAULT_BASE)
36
+ }
37
+
38
+ function getAuthHeaders(apiKey: string): Record<string, string> {
39
+ return {
40
+ "Content-Type": "application/json",
41
+ Authorization: `Bearer ${apiKey}`,
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Solicita la carga de un modelo GGUF en el backend de HiveAgents.
47
+ * Siempre pide ctx=HIVEAGENTS_LOAD_CTX para maximizar la ventana de contexto disponible.
48
+ */
49
+ /** Timeout para la petición de carga. Menor al límite de Cloudflare (100s) para evitar 524. */
50
+ const HIVEAGENTS_LOAD_FETCH_TIMEOUT_MS = 90000
51
+
52
+ export async function loadHiveAgentsModel(
53
+ modelId: string,
54
+ apiKey: string,
55
+ baseUrl?: string,
56
+ ctx = HIVEAGENTS_DEFAULT_LOAD_CTX
57
+ ): Promise<HiveAgentsLoadResult> {
58
+ const apiBase = getApiBase(baseUrl)
59
+ const headers = getAuthHeaders(apiKey)
60
+ const loadBody = {
61
+ model: modelId,
62
+ config: { ctx },
63
+ }
64
+
65
+ try {
66
+ log.info(`[hiveagents] → POST ${apiBase}/api/load`)
67
+ log.info(`[hiveagents] → Body: ${JSON.stringify(loadBody)}`)
68
+ const res = await fetch(`${apiBase}/api/load`, {
69
+ method: "POST",
70
+ headers,
71
+ body: JSON.stringify(loadBody),
72
+ signal: AbortSignal.timeout(HIVEAGENTS_LOAD_FETCH_TIMEOUT_MS),
73
+ })
74
+ const responseText = await res.text().catch(() => "")
75
+ if (!res.ok) {
76
+ // 524 = Cloudflare timeout. El backend puede seguir cargando, así que lo tratamos como "en progreso".
77
+ // 530 = Cloudflare Tunnel error (origen no resoluble); también puede ser transitorio.
78
+ const isTransientCloudflareError = [502, 503, 504, 524, 530].includes(res.status)
79
+ if (isTransientCloudflareError) {
80
+ log.warn(`[hiveagents] ← Load request hit transient error (HTTP ${res.status}); backend may still be loading`)
81
+ return { success: true, loading: true }
82
+ }
83
+ log.error(`[hiveagents] ← Load failed: HTTP ${res.status} ${res.statusText} — ${responseText}`)
84
+ return { success: false, error: `Load failed: HTTP ${res.status} — ${responseText || res.statusText}` }
85
+ }
86
+ log.info(`[hiveagents] ← Load accepted: ${responseText}`)
87
+ return { success: true }
88
+ } catch (err) {
89
+ const msg = (err as Error).message || ""
90
+ // AbortError por timeout interno: el backend puede seguir cargando.
91
+ if (msg.includes("timed out") || msg.includes("abort") || msg.includes("AbortError")) {
92
+ log.warn(`[hiveagents] ← Load request timed out after ${HIVEAGENTS_LOAD_FETCH_TIMEOUT_MS}ms; backend may still be loading`)
93
+ return { success: true, loading: true }
94
+ }
95
+ return { success: false, error: msg }
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Consulta el estado actual del backend de HiveAgents.
101
+ */
102
+ export async function getHiveAgentsModelStatus(
103
+ apiKey: string,
104
+ baseUrl?: string
105
+ ): Promise<HiveAgentsStatusResult> {
106
+ const apiBase = getApiBase(baseUrl)
107
+ const headers = getAuthHeaders(apiKey)
108
+
109
+ try {
110
+ const res = await fetch(`${apiBase}/api/status`, { headers })
111
+ if (!res.ok) return { loaded: false }
112
+ const data = await res.json() as any
113
+ return {
114
+ loaded: !!data.loaded,
115
+ model: data.model,
116
+ }
117
+ } catch {
118
+ return { loaded: false }
119
+ }
120
+ }
121
+
122
+ export class HiveAgentsProvider extends OpenAICompatBase {
123
+ private _currentModelId = ""
124
+
125
+ constructor() { super("hiveagents") }
126
+
127
+ private _isGemma4(modelId: string): boolean { return /^gemma-?4/i.test(modelId) }
128
+ private _isQwen3(modelId: string): boolean { return /^qwen3?/i.test(modelId) }
129
+
130
+ // Cloudflare WAF blocks requests carrying x-stainless-* headers from the OpenAI SDK.
131
+ // Strip them via a custom fetch wrapper so they never reach the WAF.
132
+ protected async resolveOpenAIClient(apiKey: string, baseURL: string | undefined): Promise<any> {
133
+ const { default: OpenAI } = await import("openai")
134
+ return new OpenAI({
135
+ apiKey,
136
+ baseURL,
137
+ fetch: async (url: RequestInfo | URL, init?: RequestInit) => {
138
+ const headers = new Headers(init?.headers as HeadersInit | undefined)
139
+ for (const h of BLOCKED_HEADERS) headers.delete(h)
140
+
141
+ // Debug: log exact request so we can replicate with curl
142
+ const headersObj: Record<string, string> = {}
143
+ headers.forEach((v, k) => { headersObj[k] = k.toLowerCase() === "authorization" ? `Bearer ••••${v.slice(-6)}` : v })
144
+ log.info(`[hiveagents] → POST ${url}`)
145
+ log.info(`[hiveagents] → Headers: ${JSON.stringify(headersObj)}`)
146
+ if (init?.body) {
147
+ try {
148
+ const parsed = JSON.parse(init.body as string)
149
+ const summary = { model: parsed.model, messages: parsed.messages?.length, tools: parsed.tools?.length, max_tokens: parsed.max_tokens, temperature: parsed.temperature, tool_choice: parsed.tool_choice, extra_body: parsed.extra_body }
150
+ log.info(`[hiveagents] → Body summary: ${JSON.stringify(summary)}`)
151
+ } catch { /* ignore */ }
152
+ }
153
+
154
+ const res = await fetch(url, { ...init, headers })
155
+ log.info(`[hiveagents] ← Response: ${res.status} ${res.statusText}`)
156
+ return res
157
+ },
158
+ })
159
+ }
160
+
161
+ async call(options: LLMCallOptions): Promise<LLMResponse> {
162
+ const realModelId = options.model.replace(/^hiveagents\//i, "")
163
+ if (realModelId && realModelId !== "local") {
164
+ await this._ensureModelLoaded(realModelId, options)
165
+ }
166
+ this._currentModelId = realModelId
167
+
168
+ let callOptions = { ...options, model: "hiveagents/local" }
169
+
170
+ // Qwen3: inject /no_think when thinking is explicitly disabled
171
+ if (this._isQwen3(realModelId) && options.thinking?.enabled === false) {
172
+ const msgs = callOptions.messages.map(m => ({ ...m }))
173
+ const sysMsg = msgs.find(m => m.role === "system")
174
+ if (sysMsg && typeof sysMsg.content === "string") {
175
+ if (!sysMsg.content.startsWith("/no_think"))
176
+ sysMsg.content = "/no_think\n" + sysMsg.content
177
+ } else {
178
+ msgs.unshift({ role: "system", content: "/no_think" })
179
+ }
180
+ callOptions = { ...callOptions, messages: msgs }
181
+ }
182
+
183
+ return super.call(callOptions)
184
+ }
185
+
186
+ // Gemma 4: inject chat_template_kwargs.enable_thinking via extra_body.
187
+ // Default is true (thinking ON) when options.thinking is not set.
188
+ protected modifyRequestBody(body: any, options: LLMCallOptions): any {
189
+ if (this._isGemma4(this._currentModelId)) {
190
+ const enableThinking = options.thinking?.enabled !== false
191
+ body.extra_body = {
192
+ ...(body.extra_body ?? {}),
193
+ chat_template_kwargs: { enable_thinking: enableThinking },
194
+ }
195
+ }
196
+ return body
197
+ }
198
+
199
+ /**
200
+ * Fallback defensivo: si la UI no cargó el modelo previamente,
201
+ * lo intenta cargar justo antes de inferir.
202
+ */
203
+ private async _ensureModelLoaded(modelId: string, options: LLMCallOptions): Promise<void> {
204
+ const status = await getHiveAgentsModelStatus(options.apiKey, options.baseUrl)
205
+ if (status.loaded && status.model?.name === modelId) {
206
+ log.info(`[hiveagents] Model ${modelId} already loaded`)
207
+ return
208
+ }
209
+ log.warn(`[hiveagents] Model ${modelId} not loaded. Triggering load with ctx=${HIVEAGENTS_DEFAULT_LOAD_CTX}`)
210
+ const result = await loadHiveAgentsModel(modelId, options.apiKey, options.baseUrl)
211
+ if (!result.success) {
212
+ log.warn(`[hiveagents] Auto-load failed for ${modelId}: ${result.error}`)
213
+ }
214
+ }
215
+
216
+ protected injectToolsIntoPrompt(body: any, preparedTools: any[]): void {
217
+ // When the backend already receives native OpenAI-style tools, do not confuse
218
+ // the model with an alternate <tool_call> text format. HiveAgents supports
219
+ // native tool_calls when the model/chat-template supports them.
220
+ if (body.tools && body.tools.length > 0) {
221
+ return
222
+ }
223
+
224
+ // Fallback for models/backends that do not expose native tool calling:
225
+ // inject the tool descriptions as text and instruct the model to emit
226
+ // a single JSON block wrapped in <tool_call> tags.
227
+ const toolDescriptions = preparedTools.map(t => JSON.stringify(t.function)).join("\n")
228
+ const instruction = [
229
+ "You have access to the following tools.",
230
+ "When you need to use a tool, output EXACTLY one JSON block wrapped in <tool_call> tags and NOTHING ELSE in that turn:",
231
+ "",
232
+ "<tool_call>",
233
+ '{"name": "browser_navigate", "arguments": {"url": "https://example.com"}}',
234
+ "</tool_call>",
235
+ "",
236
+ "Use the exact tool name and argument names from the list below. Do not add extra text, markdown, or explanations inside the tool_call block.",
237
+ "",
238
+ "Tools:",
239
+ toolDescriptions,
240
+ ].join("\n")
241
+ const sysMsg = body.messages.find((m: any) => m.role === "system")
242
+ if (sysMsg) {
243
+ sysMsg.content += "\n\n" + instruction
244
+ } else {
245
+ body.messages.unshift({ role: "system", content: instruction })
246
+ }
247
+ }
248
+ }
@@ -30,6 +30,7 @@ export const OPENAI_COMPAT_BASE_URLS: Record<string, string> = {
30
30
  qwen: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1",
31
31
  minimax: "https://api.minimaxi.com/v1",
32
32
  "opencode-go": "https://opencode.ai/zen/go/v1",
33
+ hiveagents: "https://llm.hiveagents.io/v1",
33
34
  }
34
35
 
35
36
  // ─── Provider profiles ────────────────────────────────────────────────────────
@@ -70,6 +71,7 @@ export const PROVIDER_PROFILES: Record<string, ProviderProfile> = {
70
71
  "local-llama": { ...DEFAULT_PROFILE },
71
72
  minimax: { ...DEFAULT_PROFILE, normalizeToolNames: true, retryWithoutToolsOnCodes: [400, 422] },
72
73
  "opencode-go": { ...DEFAULT_PROFILE, normalizeToolNames: true, retryWithoutToolsOnCodes: [400, 422] },
74
+ hiveagents: { ...DEFAULT_PROFILE, retryWithoutToolsOnCodes: [400, 422] },
73
75
  }
74
76
 
75
77
  export function getProviderProfile(provider: string): ProviderProfile {