@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.
- package/README.md +29 -69
- package/dist/hive.js +2522 -1927
- package/dist/tool-worker.js +1791 -1334
- package/dist/ui/assets/AgentCreateForm-b7xHyfNc.js +1 -0
- package/dist/ui/assets/AgentDetailPage-VHy3M7mI.js +1 -0
- package/dist/ui/assets/AgentNewPage-BnWImQMx.js +1 -0
- package/dist/ui/{dist/assets/AgentsPage-C-GSRk-N.js → assets/AgentsPage-WFy5abqx.js} +1 -1
- package/dist/ui/assets/ApiClientPage-BV7zLIL7.js +3 -0
- package/dist/ui/assets/{CanvasPage-Cvs5ctza.js → CanvasPage-B9zuIrTm.js} +1 -1
- package/dist/ui/assets/{ChannelsPage-C5m_L7P9.js → ChannelsPage-DiN3NvIM.js} +1 -1
- package/dist/ui/assets/{DashboardPage-CztbRQdm.js → DashboardPage-CHjARjVK.js} +1 -1
- package/dist/ui/assets/{LoginPage-OMsrx5oj.js → LoginPage-Dwd_XxoU.js} +1 -1
- package/dist/ui/{dist/assets/LogsPage-CcYYwjgF.js → assets/LogsPage-DW8Nnqe6.js} +1 -1
- package/dist/ui/assets/MeetingPage-D3bkiKYt.js +1 -0
- package/dist/ui/assets/{NotFound-GbAJDgoD.js → NotFound-BIUDlIXU.js} +1 -1
- package/dist/ui/assets/ProvidersPage-UqsDAxPQ.js +1 -0
- package/dist/ui/{dist/assets/RecoverPage-CwB2ByCU.js → assets/RecoverPage-8hTjr_JU.js} +1 -1
- package/dist/ui/assets/SettingsPage-DNa0jOkA.js +9 -0
- package/dist/ui/assets/{SetupPage-DOVh1ldK.js → SetupPage-Bdm2irQG.js} +1 -1
- package/dist/ui/assets/WebChatPage-DElJg6P2.js +16 -0
- package/dist/ui/assets/accordion-CnLzKNHK.js +1 -0
- package/dist/ui/{dist/assets/alert-D_2Y3qjL.js → assets/alert-DylmSCDJ.js} +1 -1
- package/dist/ui/{dist/assets/alert-dialog-CpMxaNcu.js → assets/alert-dialog-DNNWN_SI.js} +1 -1
- package/dist/ui/assets/{badge-CxTPR6_t.js → badge-ChENFgkC.js} +1 -1
- package/dist/ui/assets/chevron-down-DIosfU_U.js +1 -0
- package/dist/ui/assets/chevron-up-CI-W21Fy.js +1 -0
- package/dist/ui/{dist/assets/dialog-DfS3idb3.js → assets/dialog-BJ-npIv8.js} +1 -1
- package/dist/ui/assets/{dropdown-menu-BdCbAW1z.js → dropdown-menu-DDiaHg5y.js} +1 -1
- package/dist/ui/assets/{es-Cz5h9_84.js → es-ecSKCyB6.js} +1 -1
- package/dist/ui/assets/index-CawKP29y.js +116 -0
- package/dist/ui/assets/index-DIcsEkyd.css +2 -0
- package/dist/ui/{dist/assets/label-byJkqOYq.js → assets/label-Bi6udtSd.js} +1 -1
- package/dist/ui/assets/select-BQCOjM2j.js +1 -0
- package/dist/ui/assets/useProviders-Dlizq_8q.js +1 -0
- package/dist/ui/dist/assets/AgentCreateForm-b7xHyfNc.js +1 -0
- package/dist/ui/dist/assets/AgentDetailPage-VHy3M7mI.js +1 -0
- package/dist/ui/dist/assets/AgentNewPage-BnWImQMx.js +1 -0
- package/dist/ui/{assets/AgentsPage-C-GSRk-N.js → dist/assets/AgentsPage-WFy5abqx.js} +1 -1
- package/dist/ui/dist/assets/ApiClientPage-BV7zLIL7.js +3 -0
- package/dist/ui/dist/assets/{CanvasPage-Cvs5ctza.js → CanvasPage-B9zuIrTm.js} +1 -1
- package/dist/ui/dist/assets/{ChannelsPage-C5m_L7P9.js → ChannelsPage-DiN3NvIM.js} +1 -1
- package/dist/ui/dist/assets/{DashboardPage-CztbRQdm.js → DashboardPage-CHjARjVK.js} +1 -1
- package/dist/ui/dist/assets/{LoginPage-OMsrx5oj.js → LoginPage-Dwd_XxoU.js} +1 -1
- package/dist/ui/{assets/LogsPage-CcYYwjgF.js → dist/assets/LogsPage-DW8Nnqe6.js} +1 -1
- package/dist/ui/dist/assets/MeetingPage-D3bkiKYt.js +1 -0
- package/dist/ui/dist/assets/{NotFound-GbAJDgoD.js → NotFound-BIUDlIXU.js} +1 -1
- package/dist/ui/dist/assets/ProvidersPage-UqsDAxPQ.js +1 -0
- package/dist/ui/{assets/RecoverPage-CwB2ByCU.js → dist/assets/RecoverPage-8hTjr_JU.js} +1 -1
- package/dist/ui/dist/assets/SettingsPage-DNa0jOkA.js +9 -0
- package/dist/ui/dist/assets/{SetupPage-DOVh1ldK.js → SetupPage-Bdm2irQG.js} +1 -1
- package/dist/ui/dist/assets/WebChatPage-DElJg6P2.js +16 -0
- package/dist/ui/dist/assets/accordion-CnLzKNHK.js +1 -0
- package/dist/ui/{assets/alert-D_2Y3qjL.js → dist/assets/alert-DylmSCDJ.js} +1 -1
- package/dist/ui/{assets/alert-dialog-CpMxaNcu.js → dist/assets/alert-dialog-DNNWN_SI.js} +1 -1
- package/dist/ui/dist/assets/{badge-CxTPR6_t.js → badge-ChENFgkC.js} +1 -1
- package/dist/ui/dist/assets/chevron-down-DIosfU_U.js +1 -0
- package/dist/ui/dist/assets/chevron-up-CI-W21Fy.js +1 -0
- package/dist/ui/{assets/dialog-DfS3idb3.js → dist/assets/dialog-BJ-npIv8.js} +1 -1
- package/dist/ui/dist/assets/{dropdown-menu-BdCbAW1z.js → dropdown-menu-DDiaHg5y.js} +1 -1
- package/dist/ui/dist/assets/{es-Cz5h9_84.js → es-ecSKCyB6.js} +1 -1
- package/dist/ui/dist/assets/index-CawKP29y.js +116 -0
- package/dist/ui/dist/assets/index-DIcsEkyd.css +2 -0
- package/dist/ui/{assets/label-byJkqOYq.js → dist/assets/label-Bi6udtSd.js} +1 -1
- package/dist/ui/dist/assets/select-BQCOjM2j.js +1 -0
- package/dist/ui/dist/assets/useProviders-Dlizq_8q.js +1 -0
- package/dist/ui/dist/index.html +4 -4
- package/dist/ui/index.html +4 -4
- package/package.json +1 -1
- package/packages/cli/src/commands/gateway.ts +1 -1
- package/packages/cli/src/commands/onboard.ts +27 -1
- package/packages/core/src/agent/agent-loop.ts +104 -2
- package/packages/core/src/agent/context-compiler.ts +6 -0
- package/packages/core/src/agent/llm-client.ts +2 -0
- package/packages/core/src/agent/llm-providers/hiveagents.ts +248 -0
- package/packages/core/src/agent/llm-providers/interface.ts +2 -0
- package/packages/core/src/agent/llm-providers/openai-compat-base.ts +49 -25
- package/packages/core/src/agent/providers/index.ts +3 -2
- package/packages/core/src/agent/stuck-loop.ts +90 -14
- package/packages/core/src/channels/base.ts +7 -1
- package/packages/core/src/config/loader.ts +1 -1
- package/packages/core/src/gateway/routes/providers.ts +56 -0
- package/packages/core/src/gateway/server.ts +120 -55
- package/packages/core/src/gateway/slash-commands.ts +7 -1
- package/packages/core/src/storage/onboarding.ts +28 -0
- package/packages/core/src/storage/seed.ts +14 -0
- package/packages/skills/src/bundled-data.generated.ts +1357 -1357
- package/dist/ui/assets/AgentCreateForm-BTCzFbca.js +0 -1
- package/dist/ui/assets/AgentDetailPage-o27TRSVw.js +0 -1
- package/dist/ui/assets/AgentNewPage-400cCpYt.js +0 -1
- package/dist/ui/assets/ApiClientPage-BOTpz6oP.js +0 -3
- package/dist/ui/assets/MeetingPage-CrKVAfe6.js +0 -1
- package/dist/ui/assets/ProvidersPage-uqPcZUSV.js +0 -1
- package/dist/ui/assets/SettingsPage-DKLlye0z.js +0 -9
- package/dist/ui/assets/WebChatPage-c-7S9jnT.js +0 -16
- package/dist/ui/assets/accordion-DAbcVQCn.js +0 -1
- package/dist/ui/assets/chevron-up-BYhk0K2J.js +0 -1
- package/dist/ui/assets/index-CmGm_r89.js +0 -116
- package/dist/ui/assets/index-T7HgphSn.css +0 -2
- package/dist/ui/assets/select-Cl16QYa_.js +0 -1
- package/dist/ui/assets/useProviders-eEri6BAc.js +0 -1
- package/dist/ui/dist/assets/AgentCreateForm-BTCzFbca.js +0 -1
- package/dist/ui/dist/assets/AgentDetailPage-o27TRSVw.js +0 -1
- package/dist/ui/dist/assets/AgentNewPage-400cCpYt.js +0 -1
- package/dist/ui/dist/assets/ApiClientPage-BOTpz6oP.js +0 -3
- package/dist/ui/dist/assets/MeetingPage-CrKVAfe6.js +0 -1
- package/dist/ui/dist/assets/ProvidersPage-uqPcZUSV.js +0 -1
- package/dist/ui/dist/assets/SettingsPage-DKLlye0z.js +0 -9
- package/dist/ui/dist/assets/WebChatPage-c-7S9jnT.js +0 -16
- package/dist/ui/dist/assets/accordion-DAbcVQCn.js +0 -1
- package/dist/ui/dist/assets/chevron-up-BYhk0K2J.js +0 -1
- package/dist/ui/dist/assets/index-CmGm_r89.js +0 -116
- package/dist/ui/dist/assets/index-T7HgphSn.css +0 -2
- package/dist/ui/dist/assets/select-Cl16QYa_.js +0 -1
- package/dist/ui/dist/assets/useProviders-eEri6BAc.js +0 -1
- /package/dist/ui/assets/{card-CXAm46at.js → card-DFKnZ6ky.js} +0 -0
- /package/dist/ui/assets/{circle-alert-CyHDwUj8.js → circle-alert-KuAm2FWh.js} +0 -0
- /package/dist/ui/assets/{circle-check-Bb54Ebmu.js → circle-check-6Ard1-2z.js} +0 -0
- /package/dist/ui/assets/{circle-x-Bv6WrUJo.js → circle-x-DjLkFDO8.js} +0 -0
- /package/dist/ui/assets/{copy-dU94ZGsi.js → copy-Bu5d7C-0.js} +0 -0
- /package/dist/ui/assets/{cpu-DSpPVLAz.js → cpu-KDy6-FAI.js} +0 -0
- /package/dist/ui/assets/{download-D9ZyUZZR.js → download-Cjbk4Rek.js} +0 -0
- /package/dist/ui/assets/{external-link-CHPbUorN.js → external-link-6sTlRDUR.js} +0 -0
- /package/dist/ui/assets/{eye-epHJZ_nQ.js → eye-Df8o0tkC.js} +0 -0
- /package/dist/ui/assets/{file-text-BEjEmgby.js → file-text-lnxnjBp0.js} +0 -0
- /package/dist/ui/assets/{folder-open-iQMHVEqS.js → folder-open-DJBLDFjv.js} +0 -0
- /package/dist/ui/assets/{format-oFACFaca.js → format-BwdV8bB5.js} +0 -0
- /package/dist/ui/assets/{gateway-url-iG-C6Agn.js → gateway-url-DwzPmoc8.js} +0 -0
- /package/dist/ui/assets/{gauge-D0_GMEcq.js → gauge-B8Tj43rC.js} +0 -0
- /package/dist/ui/assets/{hexagon-DsGOUl-H.js → hexagon-6L79pgVK.js} +0 -0
- /package/dist/ui/assets/{history-BSG-Ypqf.js → history-CAF_R34_.js} +0 -0
- /package/dist/ui/assets/{info-NwLoa2Mj.js → info-WjromB4Y.js} +0 -0
- /package/dist/ui/assets/{key-3EP0dhkT.js → key-DyKOoQh5.js} +0 -0
- /package/dist/ui/assets/{loader-circle-CZNax6kS.js → loader-circle-BmBOgYze.js} +0 -0
- /package/dist/ui/assets/{lock-Ei1_J-Nq.js → lock-BS6OLXPv.js} +0 -0
- /package/dist/ui/assets/{pause-BUqah9Bi.js → pause-VqeUmp2Z.js} +0 -0
- /package/dist/ui/assets/{play-NcZ4swwL.js → play-zJpWuhrr.js} +0 -0
- /package/dist/ui/assets/{plus-CX1xyhp5.js → plus-BZQX26Dr.js} +0 -0
- /package/dist/ui/assets/{progress-Dtz-Mzys.js → progress-D5c-Eilm.js} +0 -0
- /package/dist/ui/assets/{refresh-cw-DaYdjQFk.js → refresh-cw-CCzDCAuz.js} +0 -0
- /package/dist/ui/assets/{save-CUdYyHNy.js → save-hUmZhceG.js} +0 -0
- /package/dist/ui/assets/{scroll-area-BXtLsE9E.js → scroll-area-CihOx0cb.js} +0 -0
- /package/dist/ui/assets/{search-BGmPJ-6L.js → search-DzDptO9s.js} +0 -0
- /package/dist/ui/assets/{send-BuQcUO-R.js → send-BPk9XbIq.js} +0 -0
- /package/dist/ui/assets/{settings-CcMGI1iU.js → settings-BGfrZ_zM.js} +0 -0
- /package/dist/ui/assets/{shield-C-05qB-2.js → shield-CxhcUT39.js} +0 -0
- /package/dist/ui/assets/{slider-D2I0qven.js → slider-bcUiUfx0.js} +0 -0
- /package/dist/ui/assets/{sparkles-D6fx8JC5.js → sparkles-BhwlS1pc.js} +0 -0
- /package/dist/ui/assets/{square-BD81nFtN.js → square-DMNWw4Hi.js} +0 -0
- /package/dist/ui/assets/{switch-h2SfQX4B.js → switch-BR30E4ej.js} +0 -0
- /package/dist/ui/assets/{table-CVkIRJKK.js → table-B3aGEaVp.js} +0 -0
- /package/dist/ui/assets/{tabs-C619jxbO.js → tabs-LQidMKRS.js} +0 -0
- /package/dist/ui/assets/{terminal-DN38Q456.js → terminal--7G943As.js} +0 -0
- /package/dist/ui/assets/{textarea-wvA-FDjO.js → textarea-B6Z1Zc6W.js} +0 -0
- /package/dist/ui/assets/{trash-2-BHRa5ft9.js → trash-2-xD2o4SgX.js} +0 -0
- /package/dist/ui/assets/{triangle-alert-D4nwAVbc.js → triangle-alert-pVIJGjga.js} +0 -0
- /package/dist/ui/assets/{vendor-router-pCP7sjma.js → vendor-router-gqiZ7xhx.js} +0 -0
- /package/dist/ui/assets/{volume-2-B6tkRy2u.js → volume-2-BekVQl6P.js} +0 -0
- /package/dist/ui/assets/{zap-QO7iWMRg.js → zap-B4RaNNO5.js} +0 -0
- /package/dist/ui/dist/assets/{card-CXAm46at.js → card-DFKnZ6ky.js} +0 -0
- /package/dist/ui/dist/assets/{circle-alert-CyHDwUj8.js → circle-alert-KuAm2FWh.js} +0 -0
- /package/dist/ui/dist/assets/{circle-check-Bb54Ebmu.js → circle-check-6Ard1-2z.js} +0 -0
- /package/dist/ui/dist/assets/{circle-x-Bv6WrUJo.js → circle-x-DjLkFDO8.js} +0 -0
- /package/dist/ui/dist/assets/{copy-dU94ZGsi.js → copy-Bu5d7C-0.js} +0 -0
- /package/dist/ui/dist/assets/{cpu-DSpPVLAz.js → cpu-KDy6-FAI.js} +0 -0
- /package/dist/ui/dist/assets/{download-D9ZyUZZR.js → download-Cjbk4Rek.js} +0 -0
- /package/dist/ui/dist/assets/{external-link-CHPbUorN.js → external-link-6sTlRDUR.js} +0 -0
- /package/dist/ui/dist/assets/{eye-epHJZ_nQ.js → eye-Df8o0tkC.js} +0 -0
- /package/dist/ui/dist/assets/{file-text-BEjEmgby.js → file-text-lnxnjBp0.js} +0 -0
- /package/dist/ui/dist/assets/{folder-open-iQMHVEqS.js → folder-open-DJBLDFjv.js} +0 -0
- /package/dist/ui/dist/assets/{format-oFACFaca.js → format-BwdV8bB5.js} +0 -0
- /package/dist/ui/dist/assets/{gateway-url-iG-C6Agn.js → gateway-url-DwzPmoc8.js} +0 -0
- /package/dist/ui/dist/assets/{gauge-D0_GMEcq.js → gauge-B8Tj43rC.js} +0 -0
- /package/dist/ui/dist/assets/{hexagon-DsGOUl-H.js → hexagon-6L79pgVK.js} +0 -0
- /package/dist/ui/dist/assets/{history-BSG-Ypqf.js → history-CAF_R34_.js} +0 -0
- /package/dist/ui/dist/assets/{info-NwLoa2Mj.js → info-WjromB4Y.js} +0 -0
- /package/dist/ui/dist/assets/{key-3EP0dhkT.js → key-DyKOoQh5.js} +0 -0
- /package/dist/ui/dist/assets/{loader-circle-CZNax6kS.js → loader-circle-BmBOgYze.js} +0 -0
- /package/dist/ui/dist/assets/{lock-Ei1_J-Nq.js → lock-BS6OLXPv.js} +0 -0
- /package/dist/ui/dist/assets/{pause-BUqah9Bi.js → pause-VqeUmp2Z.js} +0 -0
- /package/dist/ui/dist/assets/{play-NcZ4swwL.js → play-zJpWuhrr.js} +0 -0
- /package/dist/ui/dist/assets/{plus-CX1xyhp5.js → plus-BZQX26Dr.js} +0 -0
- /package/dist/ui/dist/assets/{progress-Dtz-Mzys.js → progress-D5c-Eilm.js} +0 -0
- /package/dist/ui/dist/assets/{refresh-cw-DaYdjQFk.js → refresh-cw-CCzDCAuz.js} +0 -0
- /package/dist/ui/dist/assets/{save-CUdYyHNy.js → save-hUmZhceG.js} +0 -0
- /package/dist/ui/dist/assets/{scroll-area-BXtLsE9E.js → scroll-area-CihOx0cb.js} +0 -0
- /package/dist/ui/dist/assets/{search-BGmPJ-6L.js → search-DzDptO9s.js} +0 -0
- /package/dist/ui/dist/assets/{send-BuQcUO-R.js → send-BPk9XbIq.js} +0 -0
- /package/dist/ui/dist/assets/{settings-CcMGI1iU.js → settings-BGfrZ_zM.js} +0 -0
- /package/dist/ui/dist/assets/{shield-C-05qB-2.js → shield-CxhcUT39.js} +0 -0
- /package/dist/ui/dist/assets/{slider-D2I0qven.js → slider-bcUiUfx0.js} +0 -0
- /package/dist/ui/dist/assets/{sparkles-D6fx8JC5.js → sparkles-BhwlS1pc.js} +0 -0
- /package/dist/ui/dist/assets/{square-BD81nFtN.js → square-DMNWw4Hi.js} +0 -0
- /package/dist/ui/dist/assets/{switch-h2SfQX4B.js → switch-BR30E4ej.js} +0 -0
- /package/dist/ui/dist/assets/{table-CVkIRJKK.js → table-B3aGEaVp.js} +0 -0
- /package/dist/ui/dist/assets/{tabs-C619jxbO.js → tabs-LQidMKRS.js} +0 -0
- /package/dist/ui/dist/assets/{terminal-DN38Q456.js → terminal--7G943As.js} +0 -0
- /package/dist/ui/dist/assets/{textarea-wvA-FDjO.js → textarea-B6Z1Zc6W.js} +0 -0
- /package/dist/ui/dist/assets/{trash-2-BHRa5ft9.js → trash-2-xD2o4SgX.js} +0 -0
- /package/dist/ui/dist/assets/{triangle-alert-D4nwAVbc.js → triangle-alert-pVIJGjga.js} +0 -0
- /package/dist/ui/dist/assets/{vendor-router-pCP7sjma.js → vendor-router-gqiZ7xhx.js} +0 -0
- /package/dist/ui/dist/assets/{volume-2-B6tkRy2u.js → volume-2-BekVQl6P.js} +0 -0
- /package/dist/ui/dist/assets/{zap-QO7iWMRg.js → zap-B4RaNNO5.js} +0 -0
|
@@ -17,6 +17,12 @@ export abstract class OpenAICompatBase implements LLMProvider {
|
|
|
17
17
|
/** Override to true for providers running on localhost. */
|
|
18
18
|
protected isLocalProvider(): boolean { return false }
|
|
19
19
|
|
|
20
|
+
/** Override to customize the OpenAI client (e.g. strip unwanted headers, add custom fetch). */
|
|
21
|
+
protected async resolveOpenAIClient(apiKey: string, baseURL: string | undefined): Promise<any> {
|
|
22
|
+
const { default: OpenAI } = await import("openai")
|
|
23
|
+
return new OpenAI({ apiKey, baseURL })
|
|
24
|
+
}
|
|
25
|
+
|
|
20
26
|
/** Hook called before each request. Override for e.g. auto-starting a local server. */
|
|
21
27
|
protected async beforeCall(_options: LLMCallOptions): Promise<void> {}
|
|
22
28
|
|
|
@@ -27,6 +33,9 @@ export abstract class OpenAICompatBase implements LLMProvider {
|
|
|
27
33
|
*/
|
|
28
34
|
protected injectToolsIntoPrompt(_body: any, _preparedTools: any[]): void {}
|
|
29
35
|
|
|
36
|
+
/** Override to add provider-specific fields to the request body (e.g. extra_body for llama.cpp chat_template_kwargs). */
|
|
37
|
+
protected modifyRequestBody(body: any, _options: LLMCallOptions): any { return body }
|
|
38
|
+
|
|
30
39
|
private _convertContentPart(part: ContentPart): any {
|
|
31
40
|
switch (part.type) {
|
|
32
41
|
case "text":
|
|
@@ -51,8 +60,6 @@ export abstract class OpenAICompatBase implements LLMProvider {
|
|
|
51
60
|
}
|
|
52
61
|
|
|
53
62
|
async call(options: LLMCallOptions): Promise<LLMResponse> {
|
|
54
|
-
const { default: OpenAI } = await import("openai")
|
|
55
|
-
|
|
56
63
|
const baseURL = options.baseUrl?.trim() || OPENAI_COMPAT_BASE_URLS[this.providerName] || undefined
|
|
57
64
|
const isLocal = this.isLocalProvider()
|
|
58
65
|
|
|
@@ -63,7 +70,7 @@ export abstract class OpenAICompatBase implements LLMProvider {
|
|
|
63
70
|
throw new Error(`API key missing for provider: ${this.providerName}. Configure it in Settings → Providers.`)
|
|
64
71
|
}
|
|
65
72
|
|
|
66
|
-
const client =
|
|
73
|
+
const client = await this.resolveOpenAIClient(apiKey, baseURL)
|
|
67
74
|
|
|
68
75
|
const sanitized = sanitizeMessages(options.messages)
|
|
69
76
|
const rawMessages = this.needsReasoningRoundtrip()
|
|
@@ -121,7 +128,7 @@ export abstract class OpenAICompatBase implements LLMProvider {
|
|
|
121
128
|
|
|
122
129
|
let response
|
|
123
130
|
try {
|
|
124
|
-
response = await client.chat.completions.create(body)
|
|
131
|
+
response = await client.chat.completions.create(this.modifyRequestBody(body, options))
|
|
125
132
|
} catch (err: any) {
|
|
126
133
|
const status = err?.status ?? err?.response?.status
|
|
127
134
|
const errMsg = (err?.error?.message ?? err?.message ?? "").toLowerCase()
|
|
@@ -133,7 +140,7 @@ export abstract class OpenAICompatBase implements LLMProvider {
|
|
|
133
140
|
delete bodyNoTools.tools
|
|
134
141
|
delete bodyNoTools.tool_choice
|
|
135
142
|
delete bodyNoTools.parallel_tool_calls
|
|
136
|
-
response = await client.chat.completions.create(bodyNoTools)
|
|
143
|
+
response = await client.chat.completions.create(this.modifyRequestBody(bodyNoTools, options))
|
|
137
144
|
}
|
|
138
145
|
// Retry 2: context overflow — compact messages and retry
|
|
139
146
|
else if (status === 400 && (errMsg.includes("context length") || errMsg.includes("input_tokens") || errMsg.includes("maximum input length"))) {
|
|
@@ -153,7 +160,7 @@ export abstract class OpenAICompatBase implements LLMProvider {
|
|
|
153
160
|
if (body.max_tokens) body.max_tokens = Math.min(body.max_tokens, 4096)
|
|
154
161
|
|
|
155
162
|
log.info(`[llm-client] ${this.providerName}: compacted ${compacted.length} msgs → ${body.messages.length} msgs, max_tokens=${body.max_tokens}`)
|
|
156
|
-
response = await client.chat.completions.create(body)
|
|
163
|
+
response = await client.chat.completions.create(this.modifyRequestBody(body, options))
|
|
157
164
|
}
|
|
158
165
|
else {
|
|
159
166
|
throw err
|
|
@@ -207,7 +214,7 @@ export abstract class OpenAICompatBase implements LLMProvider {
|
|
|
207
214
|
): Promise<LLMResponse> {
|
|
208
215
|
let stream
|
|
209
216
|
try {
|
|
210
|
-
stream = await client.chat.completions.create({ ...body, stream: true })
|
|
217
|
+
stream = await client.chat.completions.create({ ...this.modifyRequestBody(body, options), stream: true })
|
|
211
218
|
} catch (err: any) {
|
|
212
219
|
const status = err?.status ?? err?.response?.status
|
|
213
220
|
const errMsg = (err?.error?.message ?? err?.message ?? "").toLowerCase()
|
|
@@ -218,7 +225,7 @@ export abstract class OpenAICompatBase implements LLMProvider {
|
|
|
218
225
|
delete bodyNoTools.tools
|
|
219
226
|
delete bodyNoTools.tool_choice
|
|
220
227
|
delete bodyNoTools.parallel_tool_calls
|
|
221
|
-
stream = await client.chat.completions.create({ ...bodyNoTools, stream: true })
|
|
228
|
+
stream = await client.chat.completions.create({ ...this.modifyRequestBody(bodyNoTools, options), stream: true })
|
|
222
229
|
} else if (status === 400 && (errMsg.includes("context length") || errMsg.includes("input_tokens") || errMsg.includes("maximum input length"))) {
|
|
223
230
|
log.warn(`[llm-client] ${this.providerName}: context overflow — compacting messages and retrying stream`)
|
|
224
231
|
const compacted = [...body.messages]
|
|
@@ -233,7 +240,7 @@ export abstract class OpenAICompatBase implements LLMProvider {
|
|
|
233
240
|
body.messages = systemMsg ? [systemMsg, ...trimmed] : trimmed
|
|
234
241
|
if (body.max_tokens) body.max_tokens = Math.min(body.max_tokens, 4096)
|
|
235
242
|
log.info(`[llm-client] ${this.providerName}: compacted ${compacted.length} msgs → ${body.messages.length} msgs, max_tokens=${body.max_tokens}`)
|
|
236
|
-
stream = await client.chat.completions.create({ ...body, stream: true })
|
|
243
|
+
stream = await client.chat.completions.create({ ...this.modifyRequestBody(body, options), stream: true })
|
|
237
244
|
} else {
|
|
238
245
|
throw err
|
|
239
246
|
}
|
|
@@ -325,6 +332,7 @@ export function extractToolCallsFromText(
|
|
|
325
332
|
const tool_calls: LLMToolCall[] = []
|
|
326
333
|
let extractedContent = content
|
|
327
334
|
|
|
335
|
+
// Regexes for wrapped tool-call blocks.
|
|
328
336
|
const regexes = [
|
|
329
337
|
/<tool_call>\s*({[\s\S]*?})\s*<\/tool_call>/g,
|
|
330
338
|
/<function_call>\s*({[\s\S]*?})\s*<\/function_call>/g,
|
|
@@ -336,13 +344,20 @@ export function extractToolCallsFromText(
|
|
|
336
344
|
while ((match = regex.exec(content)) !== null) {
|
|
337
345
|
try {
|
|
338
346
|
const json = JSON.parse(match[1])
|
|
339
|
-
|
|
347
|
+
const calls = Array.isArray(json) ? json : [json]
|
|
348
|
+
for (const call of calls) {
|
|
349
|
+
if (!call) continue
|
|
350
|
+
// Accept both { name, arguments } and { function: { name, arguments } }
|
|
351
|
+
const fn = call.function || call
|
|
352
|
+
const name = fn.name ?? call.name
|
|
353
|
+
let args = fn.arguments ?? call.arguments ?? call.parameters
|
|
354
|
+
if (!name) continue
|
|
340
355
|
tool_calls.push({
|
|
341
356
|
id: crypto.randomUUID(),
|
|
342
357
|
type: "function",
|
|
343
358
|
function: {
|
|
344
|
-
name: toolNameMap.get(
|
|
345
|
-
arguments: typeof
|
|
359
|
+
name: toolNameMap.get(name) ?? name,
|
|
360
|
+
arguments: typeof args === "object" ? JSON.stringify(args) : (args || "{}"),
|
|
346
361
|
},
|
|
347
362
|
})
|
|
348
363
|
extractedContent = extractedContent.replace(match[0], "").trim()
|
|
@@ -356,19 +371,28 @@ export function extractToolCallsFromText(
|
|
|
356
371
|
// Fallback: entire output is a bare JSON tool call — only if name matches a known tool
|
|
357
372
|
if (tool_calls.length === 0 && knownToolNames && knownToolNames.size > 0) {
|
|
358
373
|
try {
|
|
359
|
-
const
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
374
|
+
const trimmed = content.trim()
|
|
375
|
+
// Strip common markdown fences before parsing.
|
|
376
|
+
const jsonText = trimmed.replace(/^```(?:json|tool_call)?\s*|\s*```$/g, "").trim()
|
|
377
|
+
const json = JSON.parse(jsonText)
|
|
378
|
+
const calls = Array.isArray(json) ? json : [json]
|
|
379
|
+
for (const call of calls) {
|
|
380
|
+
if (!call) continue
|
|
381
|
+
const fn = call.function || call
|
|
382
|
+
const name = fn.name ?? call.name
|
|
383
|
+
let args = fn.arguments ?? call.arguments ?? call.parameters
|
|
384
|
+
const resolvedName = toolNameMap.get(name) ?? name
|
|
385
|
+
if (name && knownToolNames.has(resolvedName) && (args !== undefined || calls.length === 1)) {
|
|
386
|
+
tool_calls.push({
|
|
387
|
+
id: crypto.randomUUID(),
|
|
388
|
+
type: "function",
|
|
389
|
+
function: {
|
|
390
|
+
name: resolvedName,
|
|
391
|
+
arguments: typeof args === "object" ? JSON.stringify(args) : (args || "{}"),
|
|
392
|
+
},
|
|
393
|
+
})
|
|
394
|
+
extractedContent = ""
|
|
395
|
+
}
|
|
372
396
|
}
|
|
373
397
|
} catch {
|
|
374
398
|
// not valid JSON
|
|
@@ -11,7 +11,7 @@ import { getAgentLoop } from "../agent-loop"
|
|
|
11
11
|
import { resolveUserId, resolveAgentId } from "../../storage/onboarding"
|
|
12
12
|
import type { ContentPart } from "../../multimodal/types"
|
|
13
13
|
|
|
14
|
-
export type Provider = "openai" | "anthropic" | "gemini" | "mistral" | "kimi" | "ollama" | "openrouter" | "deepseek" | "nvidia"
|
|
14
|
+
export type Provider = "openai" | "anthropic" | "gemini" | "mistral" | "kimi" | "ollama" | "openrouter" | "deepseek" | "nvidia" | "hiveagents"
|
|
15
15
|
|
|
16
16
|
export interface StepEvent {
|
|
17
17
|
type: "text" | "plan" | "tool_call" | "tool_result"
|
|
@@ -96,6 +96,7 @@ export class AgentRunner {
|
|
|
96
96
|
raw_user_message: options.rawUserMessage,
|
|
97
97
|
},
|
|
98
98
|
signal: options.signal,
|
|
99
|
+
onToken: options.onToken,
|
|
99
100
|
}
|
|
100
101
|
)
|
|
101
102
|
|
|
@@ -126,7 +127,7 @@ export class AgentRunner {
|
|
|
126
127
|
} else {
|
|
127
128
|
logger.debug(`[STREAM] Content empty or whitespace only, skipping accumulation`)
|
|
128
129
|
}
|
|
129
|
-
if (options.onToken) options.onToken(content)
|
|
130
|
+
if (options.onToken && !chunk.agent.streamed) options.onToken(content)
|
|
130
131
|
} else {
|
|
131
132
|
logger.debug(`[STREAM] No content in chunk, lastMsg.content is falsy`)
|
|
132
133
|
}
|
|
@@ -9,18 +9,26 @@ interface ToolCallRecord {
|
|
|
9
9
|
timestamp: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
interface
|
|
12
|
+
interface ProgressRecord {
|
|
13
|
+
hash: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface StuckLoopState {
|
|
13
18
|
detected: boolean;
|
|
14
19
|
toolName: string;
|
|
15
20
|
count: number;
|
|
16
21
|
lastError?: string;
|
|
22
|
+
kind: "loop" | "stall";
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
export class StuckLoopDetector {
|
|
20
26
|
private log = logger.child("stuck-loop");
|
|
21
27
|
private history: Map<string, ToolCallRecord[]> = new Map();
|
|
28
|
+
private progressHistory: Map<string, ProgressRecord[]> = new Map();
|
|
22
29
|
private readonly maxHistoryPerSession = 50;
|
|
23
30
|
private readonly triggerThreshold = 3;
|
|
31
|
+
private readonly progressThreshold = 3;
|
|
24
32
|
|
|
25
33
|
constructor(_config: Config) {}
|
|
26
34
|
|
|
@@ -54,9 +62,9 @@ export class StuckLoopDetector {
|
|
|
54
62
|
|
|
55
63
|
check(sessionId: string): StuckLoopState {
|
|
56
64
|
const sessionHistory = this.history.get(sessionId) ?? [];
|
|
57
|
-
|
|
65
|
+
|
|
58
66
|
if (sessionHistory.length < this.triggerThreshold) {
|
|
59
|
-
return { detected: false, toolName: "", count: 0 };
|
|
67
|
+
return { detected: false, toolName: "", count: 0, kind: "loop" };
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
const recent = sessionHistory.slice(-10);
|
|
@@ -65,7 +73,7 @@ export class StuckLoopDetector {
|
|
|
65
73
|
for (const record of recent) {
|
|
66
74
|
const key = `${record.toolName}:${record.argsHash}`;
|
|
67
75
|
const existing = counts.get(key);
|
|
68
|
-
|
|
76
|
+
|
|
69
77
|
if (existing) {
|
|
70
78
|
existing.count++;
|
|
71
79
|
if (record.errorMessage) {
|
|
@@ -79,33 +87,73 @@ export class StuckLoopDetector {
|
|
|
79
87
|
for (const [key, data] of counts) {
|
|
80
88
|
if (data.count >= this.triggerThreshold && data.error) {
|
|
81
89
|
const toolName = key.split(":")[0] ?? "unknown";
|
|
82
|
-
|
|
90
|
+
|
|
83
91
|
this.log.warn(`Stuck loop detected: ${toolName} called ${data.count} times with same args and error`);
|
|
84
|
-
|
|
92
|
+
|
|
85
93
|
return {
|
|
86
94
|
detected: true,
|
|
87
95
|
toolName,
|
|
88
96
|
count: data.count,
|
|
89
97
|
lastError: data.error,
|
|
98
|
+
kind: "loop",
|
|
90
99
|
};
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
102
|
|
|
94
|
-
return { detected: false, toolName: "", count: 0 };
|
|
103
|
+
return { detected: false, toolName: "", count: 0, kind: "loop" };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Record a progress snapshot hash. Use this to detect when the agent is not
|
|
108
|
+
* advancing even though no tool errors are occurring.
|
|
109
|
+
*/
|
|
110
|
+
recordProgress(sessionId: string, progressHash: string): void {
|
|
111
|
+
let sessionProgress = this.progressHistory.get(sessionId);
|
|
112
|
+
if (!sessionProgress) {
|
|
113
|
+
sessionProgress = [];
|
|
114
|
+
this.progressHistory.set(sessionId, sessionProgress);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
sessionProgress.push({ hash: progressHash, timestamp: Date.now() });
|
|
118
|
+
|
|
119
|
+
if (sessionProgress.length > this.maxHistoryPerSession) {
|
|
120
|
+
sessionProgress.shift();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.log.debug(`Recorded progress hash for session ${sessionId}: ${progressHash}`);
|
|
95
124
|
}
|
|
96
125
|
|
|
97
|
-
|
|
98
|
-
|
|
126
|
+
/**
|
|
127
|
+
* Detect lack of progress when the same hash appears repeatedly.
|
|
128
|
+
*/
|
|
129
|
+
checkProgress(sessionId: string, threshold?: number): StuckLoopState {
|
|
130
|
+
const sessionProgress = this.progressHistory.get(sessionId) ?? [];
|
|
131
|
+
const required = threshold ?? this.progressThreshold;
|
|
132
|
+
|
|
133
|
+
if (sessionProgress.length < required) {
|
|
134
|
+
return { detected: false, toolName: "", count: 0, kind: "stall" };
|
|
135
|
+
}
|
|
99
136
|
|
|
100
|
-
|
|
101
|
-
|
|
137
|
+
const recent = sessionProgress.slice(-required);
|
|
138
|
+
const firstHash = recent[0]?.hash;
|
|
139
|
+
const allSame = recent.every((r) => r.hash === firstHash);
|
|
140
|
+
|
|
141
|
+
if (allSame && firstHash) {
|
|
142
|
+
this.log.warn(`Stall detected: no progress for ${required} checks (hash ${firstHash})`);
|
|
143
|
+
return {
|
|
144
|
+
detected: true,
|
|
145
|
+
toolName: "NO_PROGRESS",
|
|
146
|
+
count: required,
|
|
147
|
+
kind: "stall",
|
|
148
|
+
};
|
|
102
149
|
}
|
|
103
150
|
|
|
104
|
-
return
|
|
151
|
+
return { detected: false, toolName: "", count: 0, kind: "stall" };
|
|
105
152
|
}
|
|
106
153
|
|
|
107
154
|
clear(sessionId: string): void {
|
|
108
155
|
this.history.delete(sessionId);
|
|
156
|
+
this.progressHistory.delete(sessionId);
|
|
109
157
|
this.log.debug(`Cleared stuck loop history for session ${sessionId}`);
|
|
110
158
|
}
|
|
111
159
|
|
|
@@ -114,8 +162,8 @@ export class StuckLoopDetector {
|
|
|
114
162
|
let pruned = 0;
|
|
115
163
|
|
|
116
164
|
for (const [sessionId, history] of this.history) {
|
|
117
|
-
const filtered = history.filter(r => now - r.timestamp < maxAgeMs);
|
|
118
|
-
|
|
165
|
+
const filtered = history.filter((r) => now - r.timestamp < maxAgeMs);
|
|
166
|
+
|
|
119
167
|
if (filtered.length === 0) {
|
|
120
168
|
this.history.delete(sessionId);
|
|
121
169
|
pruned++;
|
|
@@ -124,6 +172,17 @@ export class StuckLoopDetector {
|
|
|
124
172
|
}
|
|
125
173
|
}
|
|
126
174
|
|
|
175
|
+
for (const [sessionId, history] of this.progressHistory) {
|
|
176
|
+
const filtered = history.filter((r) => now - r.timestamp < maxAgeMs);
|
|
177
|
+
|
|
178
|
+
if (filtered.length === 0) {
|
|
179
|
+
this.progressHistory.delete(sessionId);
|
|
180
|
+
pruned++;
|
|
181
|
+
} else if (filtered.length !== history.length) {
|
|
182
|
+
this.progressHistory.set(sessionId, filtered);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
127
186
|
return pruned;
|
|
128
187
|
}
|
|
129
188
|
}
|
|
@@ -131,3 +190,20 @@ export class StuckLoopDetector {
|
|
|
131
190
|
export function createStuckLoopDetector(config: Config): StuckLoopDetector {
|
|
132
191
|
return new StuckLoopDetector(config);
|
|
133
192
|
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Build a human-readable intervention message for the model.
|
|
196
|
+
*/
|
|
197
|
+
export function getInterventionMessage(state: StuckLoopState): string {
|
|
198
|
+
if (!state.detected) return "";
|
|
199
|
+
|
|
200
|
+
if (state.kind === "stall") {
|
|
201
|
+
return `WARNING: Has estado sin avanzar durante ${state.count} ciclos consecutivos. Revisa tu plan, intenta una herramienta diferente o pide aclaración al usuario en lugar de repetir la misma secuencia.`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (state.count >= 4) {
|
|
205
|
+
return `CRITICAL: Has llamado ${state.toolName} ${state.count} veces con los mismos argumentos y sigue fallando con: "${state.lastError}". El usuario será notificado. Debes cambiar completamente de estrategia o pedir ayuda.`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return `WARNING: Has llamado ${state.toolName} ${state.count} veces con los mismos argumentos y sigue fallando. Debes probar un enfoque diferente en lugar de repetir la misma acción.`;
|
|
209
|
+
}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
export interface OutboundMessage {
|
|
2
|
-
type: "message" | "stream" | "status" | "error" | "pong" | "command_result" | "log" | "typing" | "audio";
|
|
2
|
+
type: "message" | "stream" | "status" | "error" | "pong" | "command_result" | "log" | "typing" | "audio" | "process";
|
|
3
3
|
sessionId: string;
|
|
4
4
|
id?: string; // Message ID for streaming
|
|
5
|
+
messageId?: string;
|
|
5
6
|
content?: string;
|
|
6
7
|
chunk?: string;
|
|
7
8
|
isChunk?: boolean; // True if this is a streaming chunk
|
|
8
9
|
isLast?: boolean;
|
|
9
10
|
isStep?: boolean;
|
|
11
|
+
processKind?: "analysis" | "tool" | "observation" | "writing";
|
|
12
|
+
processStatus?: "thinking" | "done" | "error";
|
|
13
|
+
label?: string;
|
|
14
|
+
detail?: string;
|
|
15
|
+
summary?: string;
|
|
10
16
|
stepType?: "plan" | "tool_call" | "tool_result" | "text";
|
|
11
17
|
audio?: {
|
|
12
18
|
buffer?: Buffer;
|
|
@@ -290,7 +290,7 @@ const GatewayConfigSchema = z.object({
|
|
|
290
290
|
});
|
|
291
291
|
|
|
292
292
|
const ModelsConfigSchema = z.object({
|
|
293
|
-
defaultProvider: z.enum(["openai", "anthropic", "gemini", "mistral", "kimi", "ollama", "openrouter", "deepseek"]).optional(),
|
|
293
|
+
defaultProvider: z.enum(["openai", "anthropic", "gemini", "mistral", "kimi", "ollama", "openrouter", "deepseek", "hiveagents"]).optional(),
|
|
294
294
|
defaults: z.record(z.string(), z.string()).optional(),
|
|
295
295
|
providers: z.record(z.string(), ProviderConfigSchema).optional(),
|
|
296
296
|
});
|
|
@@ -5,6 +5,10 @@ import {
|
|
|
5
5
|
loadProviderHeaders, storeProviderHeaders,
|
|
6
6
|
} from "../../storage/crypto"
|
|
7
7
|
import { listLocalModels } from "../llm-local/downloader"
|
|
8
|
+
import { loadHiveAgentsModel, getHiveAgentsModelStatus } from "../../agent/llm-providers/hiveagents"
|
|
9
|
+
import { logger } from "../../utils/logger"
|
|
10
|
+
|
|
11
|
+
const log = logger.child("gateway")
|
|
8
12
|
|
|
9
13
|
export async function handleGetProviders(req: Request, addCorsHeaders: (r: Response, req: Request) => Response): Promise<Response> {
|
|
10
14
|
const rawProviders = getDb().query(`
|
|
@@ -259,3 +263,55 @@ export async function handleSyncProviderModels(
|
|
|
259
263
|
}, { status: 502 }), req)
|
|
260
264
|
}
|
|
261
265
|
}
|
|
266
|
+
|
|
267
|
+
export async function handleLoadHiveAgentsModel(
|
|
268
|
+
req: Request,
|
|
269
|
+
addCorsHeaders: (r: Response, req: Request) => Response
|
|
270
|
+
): Promise<Response> {
|
|
271
|
+
const body = await req.json().catch(() => ({}))
|
|
272
|
+
const modelId = body.model_id || body.modelId
|
|
273
|
+
const ctx = typeof body.ctx === "number" && body.ctx > 0 ? body.ctx : 50000
|
|
274
|
+
if (!modelId) {
|
|
275
|
+
return addCorsHeaders(Response.json({ error: "model_id is required" }, { status: 400 }), req)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const providerRow = getDb().query("SELECT * FROM providers WHERE id = ?").get("hiveagents") as Record<string, unknown> | undefined
|
|
279
|
+
if (!providerRow) {
|
|
280
|
+
return addCorsHeaders(Response.json({ error: "Provider not found" }, { status: 404 }), req)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const apiKey = await loadProviderApiKey("hiveagents")
|
|
284
|
+
if (!apiKey) {
|
|
285
|
+
return addCorsHeaders(Response.json({ error: "API key not configured for hiveagents" }, { status: 400 }), req)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const baseUrl = providerRow.base_url as string | undefined
|
|
289
|
+
log.info(`[gateway] Loading hiveagents model ${modelId} with ctx=${ctx} via ${baseUrl || "https://llm.hiveagents.io"}`)
|
|
290
|
+
|
|
291
|
+
const result = await loadHiveAgentsModel(modelId as string, apiKey, baseUrl, ctx)
|
|
292
|
+
if (!result.success) {
|
|
293
|
+
log.error(`[gateway] Failed to load hiveagents model ${modelId}: ${result.error}`)
|
|
294
|
+
return addCorsHeaders(Response.json({ error: result.error }, { status: 502 }), req)
|
|
295
|
+
}
|
|
296
|
+
const isLoading = (result as any).loading === true
|
|
297
|
+
log.info(`[gateway] hiveagents load request accepted for ${modelId} with ctx=${ctx}${isLoading ? " (timeout/backend still loading)" : ""}`)
|
|
298
|
+
return addCorsHeaders(Response.json({ success: true, loading: isLoading, model_id: modelId, ctx }), req)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export async function handleGetHiveAgentsModelStatus(
|
|
302
|
+
req: Request,
|
|
303
|
+
addCorsHeaders: (r: Response, req: Request) => Response
|
|
304
|
+
): Promise<Response> {
|
|
305
|
+
const providerRow = getDb().query("SELECT * FROM providers WHERE id = ?").get("hiveagents") as Record<string, unknown> | undefined
|
|
306
|
+
if (!providerRow) {
|
|
307
|
+
return addCorsHeaders(Response.json({ error: "Provider not found" }, { status: 404 }), req)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const apiKey = await loadProviderApiKey("hiveagents")
|
|
311
|
+
if (!apiKey) {
|
|
312
|
+
return addCorsHeaders(Response.json({ error: "API key not configured for hiveagents" }, { status: 400 }), req)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const status = await getHiveAgentsModelStatus(apiKey, providerRow.base_url as string | undefined)
|
|
316
|
+
return addCorsHeaders(Response.json({ success: true, ...status }), req)
|
|
317
|
+
}
|