@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.
- package/README.md +18 -18
- package/dist/hive.js +3529 -2702
- package/dist/tool-worker.js +2110 -1856
- package/dist/ui/assets/AgentCreateForm-BTCzFbca.js +1 -0
- package/dist/ui/assets/AgentDetailPage-o27TRSVw.js +1 -0
- package/dist/ui/assets/AgentNewPage-400cCpYt.js +1 -0
- package/dist/ui/{dist/assets/AgentsPage-YvSgWRiw.js → assets/AgentsPage-C-GSRk-N.js} +5 -5
- package/dist/ui/assets/ApiClientPage-BOTpz6oP.js +3 -0
- package/dist/ui/assets/{CanvasPage-DtMwGvxf.js → CanvasPage-Cvs5ctza.js} +7 -7
- package/dist/ui/assets/ChannelsPage-C5m_L7P9.js +8 -0
- package/dist/ui/{dist/assets/DashboardPage-ghl1ZguH.js → assets/DashboardPage-CztbRQdm.js} +2 -2
- package/dist/ui/assets/{LoginPage-CAmSI9Vy.js → LoginPage-OMsrx5oj.js} +1 -1
- package/dist/ui/assets/LogsPage-CcYYwjgF.js +1 -0
- package/dist/ui/{dist/assets/MeetingPage-WjjGOqqU.js → assets/MeetingPage-CrKVAfe6.js} +1 -1
- package/dist/ui/assets/{NotFound-BMeQSGcG.js → NotFound-GbAJDgoD.js} +1 -1
- package/dist/ui/assets/ProvidersPage-uqPcZUSV.js +1 -0
- package/dist/ui/{dist/assets/RecoverPage-DpW3l-yv.js → assets/RecoverPage-CwB2ByCU.js} +1 -1
- package/dist/ui/assets/SettingsPage-DKLlye0z.js +9 -0
- package/dist/ui/assets/SetupPage-DOVh1ldK.js +1 -0
- package/dist/ui/assets/WebChatPage-c-7S9jnT.js +16 -0
- package/dist/ui/assets/accordion-DAbcVQCn.js +1 -0
- package/dist/ui/{dist/assets/alert-C-NE-P3s.js → assets/alert-D_2Y3qjL.js} +1 -1
- package/dist/ui/{dist/assets/alert-dialog-C5mzbHdP.js → assets/alert-dialog-CpMxaNcu.js} +1 -1
- package/dist/ui/assets/{badge-ChpACfWO.js → badge-CxTPR6_t.js} +1 -1
- package/dist/ui/assets/bell-8BqRYmzf.js +1 -0
- package/dist/ui/assets/circle-x-Bv6WrUJo.js +1 -0
- package/dist/ui/assets/copy-dU94ZGsi.js +1 -0
- package/dist/ui/{dist/assets/dialog-QnZ0ad8O.js → assets/dialog-DfS3idb3.js} +1 -1
- package/dist/ui/{dist/assets/dropdown-menu-BK-CO3Od.js → assets/dropdown-menu-BdCbAW1z.js} +1 -1
- package/dist/ui/assets/{es-NQNoaWDx.js → es-Cz5h9_84.js} +1 -1
- package/dist/ui/assets/index-CmGm_r89.js +116 -0
- package/dist/ui/assets/index-T7HgphSn.css +2 -0
- package/dist/ui/{dist/assets/label-D2H1IR_J.js → assets/label-byJkqOYq.js} +1 -1
- package/dist/ui/assets/progress-Dtz-Mzys.js +1 -0
- package/dist/ui/assets/scroll-area-BXtLsE9E.js +1 -0
- package/dist/ui/assets/search-BGmPJ-6L.js +1 -0
- package/dist/ui/assets/select-Cl16QYa_.js +1 -0
- package/dist/ui/assets/send-BuQcUO-R.js +1 -0
- package/dist/ui/assets/shield-C-05qB-2.js +1 -0
- package/dist/ui/{dist/assets/slider-CsiUDxc3.js → assets/slider-D2I0qven.js} +1 -1
- package/dist/ui/assets/switch-h2SfQX4B.js +1 -0
- package/dist/ui/assets/table-CVkIRJKK.js +1 -0
- package/dist/ui/assets/tabs-C619jxbO.js +1 -0
- package/dist/ui/assets/textarea-wvA-FDjO.js +1 -0
- package/dist/ui/assets/useProviders-eEri6BAc.js +1 -0
- package/dist/ui/{dist/assets/vendor-radix-cw1bQaVC.js → assets/vendor-radix-D6rA7xKY.js} +4 -4
- package/dist/ui/assets/{vendor-react-D4s9E-zj.js → vendor-react-BU5iQU4f.js} +1 -1
- package/dist/ui/dist/assets/AgentCreateForm-BTCzFbca.js +1 -0
- package/dist/ui/dist/assets/AgentDetailPage-o27TRSVw.js +1 -0
- package/dist/ui/dist/assets/AgentNewPage-400cCpYt.js +1 -0
- package/dist/ui/{assets/AgentsPage-YvSgWRiw.js → dist/assets/AgentsPage-C-GSRk-N.js} +5 -5
- package/dist/ui/dist/assets/ApiClientPage-BOTpz6oP.js +3 -0
- package/dist/ui/dist/assets/{CanvasPage-DtMwGvxf.js → CanvasPage-Cvs5ctza.js} +7 -7
- package/dist/ui/dist/assets/ChannelsPage-C5m_L7P9.js +8 -0
- package/dist/ui/{assets/DashboardPage-ghl1ZguH.js → dist/assets/DashboardPage-CztbRQdm.js} +2 -2
- package/dist/ui/dist/assets/{LoginPage-CAmSI9Vy.js → LoginPage-OMsrx5oj.js} +1 -1
- package/dist/ui/dist/assets/LogsPage-CcYYwjgF.js +1 -0
- package/dist/ui/{assets/MeetingPage-WjjGOqqU.js → dist/assets/MeetingPage-CrKVAfe6.js} +1 -1
- package/dist/ui/dist/assets/{NotFound-BMeQSGcG.js → NotFound-GbAJDgoD.js} +1 -1
- package/dist/ui/dist/assets/ProvidersPage-uqPcZUSV.js +1 -0
- package/dist/ui/{assets/RecoverPage-DpW3l-yv.js → dist/assets/RecoverPage-CwB2ByCU.js} +1 -1
- package/dist/ui/dist/assets/SettingsPage-DKLlye0z.js +9 -0
- package/dist/ui/dist/assets/SetupPage-DOVh1ldK.js +1 -0
- package/dist/ui/dist/assets/WebChatPage-c-7S9jnT.js +16 -0
- package/dist/ui/dist/assets/accordion-DAbcVQCn.js +1 -0
- package/dist/ui/{assets/alert-C-NE-P3s.js → dist/assets/alert-D_2Y3qjL.js} +1 -1
- package/dist/ui/{assets/alert-dialog-C5mzbHdP.js → dist/assets/alert-dialog-CpMxaNcu.js} +1 -1
- package/dist/ui/dist/assets/{badge-ChpACfWO.js → badge-CxTPR6_t.js} +1 -1
- package/dist/ui/dist/assets/bell-8BqRYmzf.js +1 -0
- package/dist/ui/dist/assets/circle-x-Bv6WrUJo.js +1 -0
- package/dist/ui/dist/assets/copy-dU94ZGsi.js +1 -0
- package/dist/ui/{assets/dialog-QnZ0ad8O.js → dist/assets/dialog-DfS3idb3.js} +1 -1
- package/dist/ui/{assets/dropdown-menu-BK-CO3Od.js → dist/assets/dropdown-menu-BdCbAW1z.js} +1 -1
- package/dist/ui/dist/assets/{es-NQNoaWDx.js → es-Cz5h9_84.js} +1 -1
- package/dist/ui/dist/assets/index-CmGm_r89.js +116 -0
- package/dist/ui/dist/assets/index-T7HgphSn.css +2 -0
- package/dist/ui/{assets/label-D2H1IR_J.js → dist/assets/label-byJkqOYq.js} +1 -1
- package/dist/ui/dist/assets/progress-Dtz-Mzys.js +1 -0
- package/dist/ui/dist/assets/scroll-area-BXtLsE9E.js +1 -0
- package/dist/ui/dist/assets/search-BGmPJ-6L.js +1 -0
- package/dist/ui/dist/assets/select-Cl16QYa_.js +1 -0
- package/dist/ui/dist/assets/send-BuQcUO-R.js +1 -0
- package/dist/ui/dist/assets/shield-C-05qB-2.js +1 -0
- package/dist/ui/{assets/slider-CsiUDxc3.js → dist/assets/slider-D2I0qven.js} +1 -1
- package/dist/ui/dist/assets/switch-h2SfQX4B.js +1 -0
- package/dist/ui/dist/assets/table-CVkIRJKK.js +1 -0
- package/dist/ui/dist/assets/tabs-C619jxbO.js +1 -0
- package/dist/ui/dist/assets/textarea-wvA-FDjO.js +1 -0
- package/dist/ui/dist/assets/useProviders-eEri6BAc.js +1 -0
- package/dist/ui/{assets/vendor-radix-cw1bQaVC.js → dist/assets/vendor-radix-D6rA7xKY.js} +4 -4
- package/dist/ui/dist/assets/{vendor-react-D4s9E-zj.js → vendor-react-BU5iQU4f.js} +1 -1
- package/dist/ui/dist/index.html +6 -6
- package/dist/ui/index.html +6 -6
- package/package.json +1 -1
- package/packages/cli/src/adapters/binary.ts +8 -4
- package/packages/cli/src/adapters/bun-global.ts +5 -1
- package/packages/cli/src/adapters/config.ts +4 -3
- package/packages/cli/src/adapters/docker.ts +2 -1
- package/packages/cli/src/commands/gateway.ts +123 -9
- package/packages/cli/src/commands/logs.ts +2 -1
- package/packages/cli/src/commands/onboard.ts +1 -1
- package/packages/cli/src/commands/sessions.ts +2 -1
- package/packages/cli/src/commands/skills.ts +2 -1
- package/packages/core/src/agent/agent-loop.ts +4 -14
- package/packages/core/src/agent/context-compiler.ts +1 -1
- package/packages/core/src/agent/conversation-store.ts +4 -5
- package/packages/core/src/agent/llm-client.ts +4 -0
- package/packages/core/src/agent/llm-providers/anthropic.ts +23 -8
- package/packages/core/src/agent/llm-providers/interface.ts +5 -1
- package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
- package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
- package/packages/core/src/agent/providers/index.ts +3 -4
- package/packages/core/src/agent/tool-selector.ts +3 -4
- package/packages/core/src/channels/whatsapp.ts +13 -1
- package/packages/core/src/config/loader.ts +7 -7
- package/packages/core/src/gateway/helpers/path.ts +2 -1
- package/packages/core/src/gateway/initializer.ts +4 -4
- package/packages/core/src/gateway/llm-local/downloader.ts +130 -11
- package/packages/core/src/gateway/llm-local/index.ts +2 -0
- package/packages/core/src/gateway/llm-local/models.ts +4 -3
- package/packages/core/src/gateway/resolver.ts +5 -1
- package/packages/core/src/gateway/router.ts +7 -5
- package/packages/core/src/gateway/routes/chat.ts +16 -16
- package/packages/core/src/gateway/routes/http-client.ts +16 -0
- package/packages/core/src/gateway/routes/llm-local.ts +51 -5
- package/packages/core/src/gateway/routes/providers.ts +43 -2
- package/packages/core/src/gateway/server.ts +55 -47
- package/packages/core/src/gateway/tts/src/install.ts +17 -9
- package/packages/core/src/storage/crypto.ts +152 -20
- package/packages/core/src/storage/migrate.ts +51 -18
- package/packages/core/src/storage/seed.ts +77 -35
- package/packages/core/src/tool-runtime/index.ts +42 -1
- package/packages/core/src/tools/api/api-request.ts +174 -0
- package/packages/core/src/tools/api/index.ts +16 -0
- package/packages/core/src/tools/index.ts +12 -0
- package/packages/core/src/tools/web/browser-click.ts +2 -2
- package/packages/core/src/tools/web/browser-extract.ts +22 -6
- package/packages/core/src/tools/web/browser-navigate.ts +34 -18
- package/packages/core/src/tools/web/browser-screenshot.ts +40 -8
- package/packages/core/src/tools/web/browser-script.ts +2 -2
- package/packages/core/src/tools/web/browser-service.ts +295 -341
- package/packages/core/src/tools/web/browser-type.ts +5 -10
- package/packages/core/src/tools/web/browser-wait.ts +2 -2
- package/packages/core/src/tools/web/index.ts +1 -1
- package/packages/core/src/utils/logger.ts +2 -1
- package/packages/mcp/src/manager.ts +2 -1
- package/packages/skills/src/bundled/api/api_client/SKILL.md +132 -0
- package/packages/skills/src/bundled-data.generated.ts +1274 -1217
- package/packages/skills/src/loader.ts +2 -1
- package/dist/ui/assets/AgentCreateForm-tJZv9FZC.js +0 -1
- package/dist/ui/assets/AgentDetailPage-Du-mRcAX.js +0 -1
- package/dist/ui/assets/AgentNewPage-DIFYd_Ys.js +0 -1
- package/dist/ui/assets/ChannelsPage-BdBXWHjj.js +0 -8
- package/dist/ui/assets/LogsPage-DAPBHkwK.js +0 -1
- package/dist/ui/assets/ProvidersPage-Ct6HsAi1.js +0 -1
- package/dist/ui/assets/SettingsPage-DBJ7_E6C.js +0 -9
- package/dist/ui/assets/SetupPage-DKmLVUaj.js +0 -1
- package/dist/ui/assets/WebChatPage-CVRcKept.js +0 -16
- package/dist/ui/assets/accordion-C5d5Rm5z.js +0 -1
- package/dist/ui/assets/globe-DeCQTCDJ.js +0 -1
- package/dist/ui/assets/index-B2fCYtTS.css +0 -2
- package/dist/ui/assets/index-DMCjjdqf.js +0 -116
- package/dist/ui/assets/progress-BherYzY6.js +0 -1
- package/dist/ui/assets/scroll-area-DkeyX32e.js +0 -1
- package/dist/ui/assets/send-B0H5SEIE.js +0 -1
- package/dist/ui/assets/switch-BDwN8RYV.js +0 -1
- package/dist/ui/assets/table-CSc8ubon.js +0 -1
- package/dist/ui/assets/textarea-CXgXWKrT.js +0 -1
- package/dist/ui/assets/useProviders-C6_QHsEi.js +0 -1
- package/dist/ui/dist/assets/AgentCreateForm-tJZv9FZC.js +0 -1
- package/dist/ui/dist/assets/AgentDetailPage-Du-mRcAX.js +0 -1
- package/dist/ui/dist/assets/AgentNewPage-DIFYd_Ys.js +0 -1
- package/dist/ui/dist/assets/ChannelsPage-BdBXWHjj.js +0 -8
- package/dist/ui/dist/assets/LogsPage-DAPBHkwK.js +0 -1
- package/dist/ui/dist/assets/ProvidersPage-Ct6HsAi1.js +0 -1
- package/dist/ui/dist/assets/SettingsPage-DBJ7_E6C.js +0 -9
- package/dist/ui/dist/assets/SetupPage-DKmLVUaj.js +0 -1
- package/dist/ui/dist/assets/WebChatPage-CVRcKept.js +0 -16
- package/dist/ui/dist/assets/accordion-C5d5Rm5z.js +0 -1
- package/dist/ui/dist/assets/globe-DeCQTCDJ.js +0 -1
- package/dist/ui/dist/assets/index-B2fCYtTS.css +0 -2
- package/dist/ui/dist/assets/index-DMCjjdqf.js +0 -116
- package/dist/ui/dist/assets/progress-BherYzY6.js +0 -1
- package/dist/ui/dist/assets/scroll-area-DkeyX32e.js +0 -1
- package/dist/ui/dist/assets/send-B0H5SEIE.js +0 -1
- package/dist/ui/dist/assets/switch-BDwN8RYV.js +0 -1
- package/dist/ui/dist/assets/table-CSc8ubon.js +0 -1
- package/dist/ui/dist/assets/textarea-CXgXWKrT.js +0 -1
- package/dist/ui/dist/assets/useProviders-C6_QHsEi.js +0 -1
- /package/dist/ui/assets/{card-CNf6BS2e.js → card-CXAm46at.js} +0 -0
- /package/dist/ui/assets/{cpu-Cdgc_B1K.js → cpu-DSpPVLAz.js} +0 -0
- /package/dist/ui/assets/{download-C3ifGMjJ.js → download-D9ZyUZZR.js} +0 -0
- /package/dist/ui/assets/{external-link-BvxYeTP1.js → external-link-CHPbUorN.js} +0 -0
- /package/dist/ui/assets/{eye-DqNTU_GD.js → eye-epHJZ_nQ.js} +0 -0
- /package/dist/ui/assets/{file-text-BT_9S9SM.js → file-text-BEjEmgby.js} +0 -0
- /package/dist/ui/assets/{folder-open-BhH8y9ac.js → folder-open-iQMHVEqS.js} +0 -0
- /package/dist/ui/assets/{format-GVHeOyWI.js → format-oFACFaca.js} +0 -0
- /package/dist/ui/assets/{gateway-url-COCbW0IR.js → gateway-url-iG-C6Agn.js} +0 -0
- /package/dist/ui/assets/{gauge-D_TMa4i9.js → gauge-D0_GMEcq.js} +0 -0
- /package/dist/ui/assets/{settings-Ds4SqD8s.js → settings-CcMGI1iU.js} +0 -0
- /package/dist/ui/assets/{sparkles-yUEb-7oH.js → sparkles-D6fx8JC5.js} +0 -0
- /package/dist/ui/assets/{trash-2-CNjMkoq6.js → trash-2-BHRa5ft9.js} +0 -0
- /package/dist/ui/assets/{triangle-alert-C9Y8Ub4X.js → triangle-alert-D4nwAVbc.js} +0 -0
- /package/dist/ui/assets/{vendor-router-C9pIYwbJ.js → vendor-router-pCP7sjma.js} +0 -0
- /package/dist/ui/assets/{volume-2-CeSXNDv4.js → volume-2-B6tkRy2u.js} +0 -0
- /package/dist/ui/assets/{zap-hlXjpSeA.js → zap-QO7iWMRg.js} +0 -0
- /package/dist/ui/dist/assets/{card-CNf6BS2e.js → card-CXAm46at.js} +0 -0
- /package/dist/ui/dist/assets/{cpu-Cdgc_B1K.js → cpu-DSpPVLAz.js} +0 -0
- /package/dist/ui/dist/assets/{download-C3ifGMjJ.js → download-D9ZyUZZR.js} +0 -0
- /package/dist/ui/dist/assets/{external-link-BvxYeTP1.js → external-link-CHPbUorN.js} +0 -0
- /package/dist/ui/dist/assets/{eye-DqNTU_GD.js → eye-epHJZ_nQ.js} +0 -0
- /package/dist/ui/dist/assets/{file-text-BT_9S9SM.js → file-text-BEjEmgby.js} +0 -0
- /package/dist/ui/dist/assets/{folder-open-BhH8y9ac.js → folder-open-iQMHVEqS.js} +0 -0
- /package/dist/ui/dist/assets/{format-GVHeOyWI.js → format-oFACFaca.js} +0 -0
- /package/dist/ui/dist/assets/{gateway-url-COCbW0IR.js → gateway-url-iG-C6Agn.js} +0 -0
- /package/dist/ui/dist/assets/{gauge-D_TMa4i9.js → gauge-D0_GMEcq.js} +0 -0
- /package/dist/ui/dist/assets/{settings-Ds4SqD8s.js → settings-CcMGI1iU.js} +0 -0
- /package/dist/ui/dist/assets/{sparkles-yUEb-7oH.js → sparkles-D6fx8JC5.js} +0 -0
- /package/dist/ui/dist/assets/{trash-2-CNjMkoq6.js → trash-2-BHRa5ft9.js} +0 -0
- /package/dist/ui/dist/assets/{triangle-alert-C9Y8Ub4X.js → triangle-alert-D4nwAVbc.js} +0 -0
- /package/dist/ui/dist/assets/{vendor-router-C9pIYwbJ.js → vendor-router-pCP7sjma.js} +0 -0
- /package/dist/ui/dist/assets/{volume-2-CeSXNDv4.js → volume-2-B6tkRy2u.js} +0 -0
- /package/dist/ui/dist/assets/{zap-hlXjpSeA.js → zap-QO7iWMRg.js} +0 -0
|
@@ -29,7 +29,6 @@
|
|
|
29
29
|
*
|
|
30
30
|
* 6. Tool categorization: Tools are categorized by semantic domain:
|
|
31
31
|
* - scheduling (cron tools)
|
|
32
|
-
* - projects (project/task management)
|
|
33
32
|
* - filesystem (file operations)
|
|
34
33
|
* - web (search/fetch)
|
|
35
34
|
* - browser (browser automation)
|
|
@@ -527,10 +526,10 @@ function enrichToolDescription(tool: ToolDescriptor): string {
|
|
|
527
526
|
*
|
|
528
527
|
* Canonical format: `{safeServer}__{safeTool}` (double underscore as separator)
|
|
529
528
|
*/
|
|
530
|
-
export function mcpToolFullName(
|
|
529
|
+
export function mcpToolFullName(serverName: string, toolName: string): string {
|
|
531
530
|
const safe = (s: string) => s.replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_\-]/g, '_')
|
|
532
|
-
const
|
|
533
|
-
const trimmed =
|
|
531
|
+
const full = `${safe(serverName)}__${safe(toolName)}`
|
|
532
|
+
const trimmed = full.length > 64 ? full.substring(0, 64) : full
|
|
534
533
|
return /^[a-zA-Z_]/.test(trimmed) ? trimmed : `_${trimmed}`.substring(0, 64)
|
|
535
534
|
}
|
|
536
535
|
|
|
@@ -11,6 +11,7 @@ import type { ChannelConfig, IncomingMessage, OutboundMessage } from "./base.ts"
|
|
|
11
11
|
import { BaseChannel } from "./base.ts";
|
|
12
12
|
import { existsSync, mkdirSync, rmSync } from "node:fs";
|
|
13
13
|
import * as path from "node:path";
|
|
14
|
+
import { homedir } from "node:os";
|
|
14
15
|
import { logger } from "../utils/logger.ts";
|
|
15
16
|
import { getDb } from "../storage/sqlite.ts";
|
|
16
17
|
// @ts-ignore — no type definitions for qrcode-terminal
|
|
@@ -69,7 +70,7 @@ export class WhatsAppChannel extends BaseChannel {
|
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
private getAuthPath(agentId: string, accountId: string): string {
|
|
72
|
-
const baseDir =
|
|
73
|
+
const baseDir = homedir();
|
|
73
74
|
const authDir = path.join(baseDir, ".hive", "agents", agentId, "whatsapp", accountId);
|
|
74
75
|
|
|
75
76
|
if (!existsSync(authDir)) {
|
|
@@ -117,11 +118,22 @@ export class WhatsAppChannel extends BaseChannel {
|
|
|
117
118
|
this.connectionState.waVersion = version.join(".");
|
|
118
119
|
this.log.info(`Using WhatsApp Web v${version.join(".")}`);
|
|
119
120
|
|
|
121
|
+
const baileysLogger = {
|
|
122
|
+
level: "silent",
|
|
123
|
+
child: () => baileysLogger,
|
|
124
|
+
trace: () => {},
|
|
125
|
+
debug: () => {},
|
|
126
|
+
info: (msg: unknown) => { if (typeof msg === "object" && msg !== null) this.log.debug((msg as any).msg ?? JSON.stringify(msg)); },
|
|
127
|
+
warn: (msg: unknown) => { if (typeof msg === "object" && msg !== null) this.log.warn((msg as any).msg ?? JSON.stringify(msg)); },
|
|
128
|
+
error: (msg: unknown) => { if (typeof msg === "object" && msg !== null) this.log.error((msg as any).msg ?? JSON.stringify(msg)); },
|
|
129
|
+
};
|
|
130
|
+
|
|
120
131
|
this.socket = makeWASocket({
|
|
121
132
|
version,
|
|
122
133
|
auth: state,
|
|
123
134
|
printQRInTerminal: false,
|
|
124
135
|
syncFullHistory: false,
|
|
136
|
+
logger: baileysLogger as any,
|
|
125
137
|
getMessage: async () => ({ conversation: "" }),
|
|
126
138
|
});
|
|
127
139
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
2
|
import { mkdirSync, existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { availableParallelism } from "node:os";
|
|
4
|
+
import { availableParallelism, homedir } from "node:os";
|
|
5
5
|
|
|
6
6
|
const LogLevelSchema = z.enum(["debug", "info", "warn", "error"]);
|
|
7
7
|
const DMPolicySchema = z.enum(["open", "pairing", "allowlist"]);
|
|
@@ -33,7 +33,7 @@ export function getHiveDir(): string {
|
|
|
33
33
|
// Priority 1: HIVE_HOME explicitly set
|
|
34
34
|
if (process.env.HIVE_HOME) {
|
|
35
35
|
const hiveDir = process.env.HIVE_HOME.startsWith("~")
|
|
36
|
-
? path.join(
|
|
36
|
+
? path.join(homedir(), process.env.HIVE_HOME.slice(1))
|
|
37
37
|
: process.env.HIVE_HOME;
|
|
38
38
|
loadEnv(hiveDir);
|
|
39
39
|
return hiveDir;
|
|
@@ -49,7 +49,7 @@ export function getHiveDir(): string {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// Priority 3: Default ~/.hive
|
|
52
|
-
const defaultDir = path.join(
|
|
52
|
+
const defaultDir = path.join(homedir(), ".hive");
|
|
53
53
|
loadEnv(defaultDir);
|
|
54
54
|
return defaultDir;
|
|
55
55
|
}
|
|
@@ -60,7 +60,7 @@ const expandPath = (p: string): string => {
|
|
|
60
60
|
return p.replace("~/.hive", hiveDir);
|
|
61
61
|
}
|
|
62
62
|
if (p.startsWith("~")) {
|
|
63
|
-
return path.join(
|
|
63
|
+
return path.join(homedir(), p.slice(1));
|
|
64
64
|
}
|
|
65
65
|
return p;
|
|
66
66
|
};
|
|
@@ -117,9 +117,9 @@ const WebConfigSchema = z.object({
|
|
|
117
117
|
|
|
118
118
|
const BrowserConfigSchema = z.object({
|
|
119
119
|
enabled: z.boolean().optional(),
|
|
120
|
-
cdpUrl: z.string().optional(),
|
|
121
120
|
headless: z.boolean().optional(),
|
|
122
121
|
timeoutMs: z.number().optional(),
|
|
122
|
+
sessionName: z.string().optional(),
|
|
123
123
|
});
|
|
124
124
|
|
|
125
125
|
const CanvasConfigSchema = z.object({
|
|
@@ -437,7 +437,7 @@ function buildDefaultConfig(): Config {
|
|
|
437
437
|
allowlist: [],
|
|
438
438
|
denylist: ["rm -rf /", "sudo", "chmod 777", "> /dev/", "mkfs"],
|
|
439
439
|
timeoutSeconds: 30,
|
|
440
|
-
workDir: path.join(
|
|
440
|
+
workDir: path.join(homedir(), "exec"), // Points to home for exec by default
|
|
441
441
|
},
|
|
442
442
|
web: {
|
|
443
443
|
allowlist: [],
|
|
@@ -446,9 +446,9 @@ function buildDefaultConfig(): Config {
|
|
|
446
446
|
},
|
|
447
447
|
browser: {
|
|
448
448
|
enabled: true,
|
|
449
|
-
cdpUrl: "ws://127.0.0.1:9222",
|
|
450
449
|
headless: true,
|
|
451
450
|
timeoutMs: 30000,
|
|
451
|
+
sessionName: "hive",
|
|
452
452
|
},
|
|
453
453
|
canvas: {
|
|
454
454
|
enabled: true,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
|
+
import { homedir } from "node:os";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Expands a path that starts with ~ to the user's home directory.
|
|
@@ -7,7 +8,7 @@ import * as path from "node:path";
|
|
|
7
8
|
*/
|
|
8
9
|
export function expandPath(p: string): string {
|
|
9
10
|
if (p.startsWith("~")) {
|
|
10
|
-
return path.join(
|
|
11
|
+
return path.join(homedir(), p.slice(1));
|
|
11
12
|
}
|
|
12
13
|
return p;
|
|
13
14
|
}
|
|
@@ -250,11 +250,11 @@ export async function initializeGateway(
|
|
|
250
250
|
const agent = createAgentService();
|
|
251
251
|
await agent.initialize();
|
|
252
252
|
|
|
253
|
-
// 5b. Initialize Browser Service (
|
|
253
|
+
// 5b. Initialize Browser Service (agent-browser CLI)
|
|
254
254
|
let browserAvailable = false;
|
|
255
255
|
|
|
256
256
|
try {
|
|
257
|
-
log.info("
|
|
257
|
+
log.info("Initializing browser automation (agent-browser)...");
|
|
258
258
|
|
|
259
259
|
const browserService = initializeBrowserService(config);
|
|
260
260
|
browserAvailable = await browserService.start();
|
|
@@ -262,8 +262,8 @@ export async function initializeGateway(
|
|
|
262
262
|
if (browserAvailable) {
|
|
263
263
|
activateBrowserTools();
|
|
264
264
|
} else {
|
|
265
|
-
log.warn("⚠️
|
|
266
|
-
log.warn("
|
|
265
|
+
log.warn("⚠️ agent-browser no disponible - browser tools desactivadas");
|
|
266
|
+
log.warn(" Se instalará automáticamente en primer uso o manual: bun add -g agent-browser");
|
|
267
267
|
}
|
|
268
268
|
} catch (error) {
|
|
269
269
|
log.warn(`Browser Service initialization skipped: ${(error as Error).message}`);
|
|
@@ -18,22 +18,116 @@ export const MODELS_DIR = join(LLM_ROOT, "models")
|
|
|
18
18
|
if (!existsSync(BIN_DIR)) mkdirSync(BIN_DIR, { recursive: true })
|
|
19
19
|
if (!existsSync(MODELS_DIR)) mkdirSync(MODELS_DIR, { recursive: true })
|
|
20
20
|
|
|
21
|
-
/** URLs de modelos en HuggingFace (Gemma 4 según
|
|
21
|
+
/** URLs de modelos en HuggingFace (Gemma 4 y Qwen 3.5 según Unsloth) */
|
|
22
22
|
export const HF_MODEL_URLS = {
|
|
23
|
+
// mmproj legacy (E4B)
|
|
23
24
|
mmproj: "https://huggingface.co/unsloth/gemma-4-E4B-it-GGUF/resolve/main/mmproj-BF16.gguf",
|
|
25
|
+
// mmproj Gemma 4
|
|
26
|
+
mmproj_gemma4_12b: "https://huggingface.co/unsloth/gemma-4-12b-it-GGUF/resolve/main/mmproj-BF16.gguf",
|
|
27
|
+
mmproj_gemma4_26b: "https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF/resolve/main/mmproj-BF16.gguf",
|
|
28
|
+
mmproj_gemma4_31b: "https://huggingface.co/unsloth/gemma-4-31B-it-GGUF/resolve/main/mmproj-BF16.gguf",
|
|
29
|
+
// mmproj Qwen 3.5
|
|
30
|
+
mmproj_qwen3_5_2b: "https://huggingface.co/unsloth/Qwen3.5-2B-GGUF/resolve/main/mmproj-F16.gguf",
|
|
31
|
+
mmproj_qwen3_5_4b: "https://huggingface.co/unsloth/Qwen3.5-4B-GGUF/resolve/main/mmproj-F16.gguf",
|
|
32
|
+
mmproj_qwen3_5_9b: "https://huggingface.co/unsloth/Qwen3.5-9B-GGUF/resolve/main/mmproj-F16.gguf",
|
|
33
|
+
mmproj_qwen3_5_27b: "https://huggingface.co/unsloth/Qwen3.5-27B-GGUF/resolve/main/mmproj-F16.gguf",
|
|
34
|
+
mmproj_qwen3_5_35b: "https://huggingface.co/unsloth/Qwen3.5-35B-A3B-GGUF/resolve/main/mmproj-F16.gguf",
|
|
35
|
+
// modelos Gemma 4
|
|
24
36
|
e2b_Q4_K_XL: "https://huggingface.co/unsloth/gemma-4-E2B-it-GGUF/resolve/main/gemma-4-E2B-it-UD-Q4_K_XL.gguf",
|
|
25
|
-
e4b_Q4_K_XL: "https://huggingface.co/unsloth/gemma-4-E4B-it-GGUF/resolve/main/gemma-4-E4B-it-UD-Q4_K_XL.gguf"
|
|
37
|
+
e4b_Q4_K_XL: "https://huggingface.co/unsloth/gemma-4-E4B-it-GGUF/resolve/main/gemma-4-E4B-it-UD-Q4_K_XL.gguf",
|
|
38
|
+
gemma4_12b_Q4_K_XL: "https://huggingface.co/unsloth/gemma-4-12b-it-GGUF/resolve/main/gemma-4-12b-it-UD-Q4_K_XL.gguf",
|
|
39
|
+
gemma4_26b_Q4_K_M: "https://huggingface.co/unsloth/gemma-4-26B-A4B-it-GGUF/resolve/main/gemma-4-26B-A4B-it-UD-Q4_K_M.gguf",
|
|
40
|
+
gemma4_31b_Q4_K_XL: "https://huggingface.co/unsloth/gemma-4-31B-it-GGUF/resolve/main/gemma-4-31B-it-UD-Q4_K_XL.gguf",
|
|
41
|
+
// modelos Qwen 3.5
|
|
42
|
+
qwen3_5_2b_Q4_K_XL: "https://huggingface.co/unsloth/Qwen3.5-2B-GGUF/resolve/main/Qwen3.5-2B-UD-Q4_K_XL.gguf",
|
|
43
|
+
qwen3_5_4b_Q4_K_XL: "https://huggingface.co/unsloth/Qwen3.5-4B-GGUF/resolve/main/Qwen3.5-4B-UD-Q4_K_XL.gguf",
|
|
44
|
+
qwen3_5_9b_Q4_K_XL: "https://huggingface.co/unsloth/Qwen3.5-9B-GGUF/resolve/main/Qwen3.5-9B-UD-Q4_K_XL.gguf",
|
|
45
|
+
qwen3_5_27b_Q4_K_XL: "https://huggingface.co/unsloth/Qwen3.5-27B-GGUF/resolve/main/Qwen3.5-27B-UD-Q4_K_XL.gguf",
|
|
46
|
+
qwen3_5_35b_Q4_K_XL: "https://huggingface.co/unsloth/Qwen3.5-35B-A3B-GGUF/resolve/main/Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf",
|
|
26
47
|
}
|
|
27
48
|
|
|
28
|
-
export type ModelId =
|
|
49
|
+
export type ModelId =
|
|
50
|
+
| "mmproj"
|
|
51
|
+
| "mmproj_gemma4_12b"
|
|
52
|
+
| "mmproj_gemma4_26b"
|
|
53
|
+
| "mmproj_gemma4_31b"
|
|
54
|
+
| "mmproj_qwen3_5_2b"
|
|
55
|
+
| "mmproj_qwen3_5_4b"
|
|
56
|
+
| "mmproj_qwen3_5_9b"
|
|
57
|
+
| "mmproj_qwen3_5_27b"
|
|
58
|
+
| "mmproj_qwen3_5_35b"
|
|
59
|
+
| "e2b_Q4_K_XL"
|
|
60
|
+
| "e4b_Q4_K_XL"
|
|
61
|
+
| "gemma4_12b_Q4_K_XL"
|
|
62
|
+
| "gemma4_26b_Q4_K_M"
|
|
63
|
+
| "gemma4_31b_Q4_K_XL"
|
|
64
|
+
| "qwen3_5_2b_Q4_K_XL"
|
|
65
|
+
| "qwen3_5_4b_Q4_K_XL"
|
|
66
|
+
| "qwen3_5_9b_Q4_K_XL"
|
|
67
|
+
| "qwen3_5_27b_Q4_K_XL"
|
|
68
|
+
| "qwen3_5_35b_Q4_K_XL"
|
|
29
69
|
|
|
30
70
|
/** Modelos que requieren proyector de visión (mmproj) */
|
|
31
|
-
export const VISION_MODELS: ModelId[] = [
|
|
71
|
+
export const VISION_MODELS: ModelId[] = [
|
|
72
|
+
"e4b_Q4_K_XL",
|
|
73
|
+
"gemma4_12b_Q4_K_XL",
|
|
74
|
+
"gemma4_26b_Q4_K_M",
|
|
75
|
+
"gemma4_31b_Q4_K_XL",
|
|
76
|
+
"qwen3_5_2b_Q4_K_XL",
|
|
77
|
+
"qwen3_5_4b_Q4_K_XL",
|
|
78
|
+
"qwen3_5_9b_Q4_K_XL",
|
|
79
|
+
"qwen3_5_27b_Q4_K_XL",
|
|
80
|
+
"qwen3_5_35b_Q4_K_XL",
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
/** Mapeo de cada modelo a su mmproj correspondiente */
|
|
84
|
+
export const MODEL_MMPROJ_MAP: Record<ModelId, ModelId | undefined> = {
|
|
85
|
+
mmproj: undefined,
|
|
86
|
+
mmproj_gemma4_12b: undefined,
|
|
87
|
+
mmproj_gemma4_26b: undefined,
|
|
88
|
+
mmproj_gemma4_31b: undefined,
|
|
89
|
+
mmproj_qwen3_5_2b: undefined,
|
|
90
|
+
mmproj_qwen3_5_4b: undefined,
|
|
91
|
+
mmproj_qwen3_5_9b: undefined,
|
|
92
|
+
mmproj_qwen3_5_27b: undefined,
|
|
93
|
+
mmproj_qwen3_5_35b: undefined,
|
|
94
|
+
e2b_Q4_K_XL: undefined,
|
|
95
|
+
e4b_Q4_K_XL: "mmproj",
|
|
96
|
+
gemma4_12b_Q4_K_XL: "mmproj_gemma4_12b",
|
|
97
|
+
gemma4_26b_Q4_K_M: "mmproj_gemma4_26b",
|
|
98
|
+
gemma4_31b_Q4_K_XL: "mmproj_gemma4_31b",
|
|
99
|
+
qwen3_5_2b_Q4_K_XL: "mmproj_qwen3_5_2b",
|
|
100
|
+
qwen3_5_4b_Q4_K_XL: "mmproj_qwen3_5_4b",
|
|
101
|
+
qwen3_5_9b_Q4_K_XL: "mmproj_qwen3_5_9b",
|
|
102
|
+
qwen3_5_27b_Q4_K_XL: "mmproj_qwen3_5_27b",
|
|
103
|
+
qwen3_5_35b_Q4_K_XL: "mmproj_qwen3_5_35b",
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Devuelve el ID del mmproj asociado a un modelo, o undefined si no requiere visión */
|
|
107
|
+
export function getModelMMProjId(modelId: ModelId): ModelId | undefined {
|
|
108
|
+
return MODEL_MMPROJ_MAP[modelId]
|
|
109
|
+
}
|
|
32
110
|
|
|
33
111
|
export const MODEL_FILES: Record<ModelId, string> = {
|
|
34
112
|
mmproj: "mmproj-BF16.gguf",
|
|
113
|
+
mmproj_gemma4_12b: "mmproj-gemma4-12b-BF16.gguf",
|
|
114
|
+
mmproj_gemma4_26b: "mmproj-gemma4-26b-BF16.gguf",
|
|
115
|
+
mmproj_gemma4_31b: "mmproj-gemma4-31b-BF16.gguf",
|
|
116
|
+
mmproj_qwen3_5_2b: "mmproj-qwen3.5-2b-F16.gguf",
|
|
117
|
+
mmproj_qwen3_5_4b: "mmproj-qwen3.5-4b-F16.gguf",
|
|
118
|
+
mmproj_qwen3_5_9b: "mmproj-qwen3.5-9b-F16.gguf",
|
|
119
|
+
mmproj_qwen3_5_27b: "mmproj-qwen3.5-27b-F16.gguf",
|
|
120
|
+
mmproj_qwen3_5_35b: "mmproj-qwen3.5-35b-F16.gguf",
|
|
35
121
|
e2b_Q4_K_XL: "gemma-4-E2B-it-UD-Q4_K_XL.gguf",
|
|
36
122
|
e4b_Q4_K_XL: "gemma-4-E4B-it-UD-Q4_K_XL.gguf",
|
|
123
|
+
gemma4_12b_Q4_K_XL: "gemma-4-12b-it-UD-Q4_K_XL.gguf",
|
|
124
|
+
gemma4_26b_Q4_K_M: "gemma-4-26B-A4B-it-UD-Q4_K_M.gguf",
|
|
125
|
+
gemma4_31b_Q4_K_XL: "gemma-4-31B-it-UD-Q4_K_XL.gguf",
|
|
126
|
+
qwen3_5_2b_Q4_K_XL: "Qwen3.5-2B-UD-Q4_K_XL.gguf",
|
|
127
|
+
qwen3_5_4b_Q4_K_XL: "Qwen3.5-4B-UD-Q4_K_XL.gguf",
|
|
128
|
+
qwen3_5_9b_Q4_K_XL: "Qwen3.5-9B-UD-Q4_K_XL.gguf",
|
|
129
|
+
qwen3_5_27b_Q4_K_XL: "Qwen3.5-27B-UD-Q4_K_XL.gguf",
|
|
130
|
+
qwen3_5_35b_Q4_K_XL: "Qwen3.5-35B-A3B-UD-Q4_K_XL.gguf",
|
|
37
131
|
}
|
|
38
132
|
|
|
39
133
|
export function getModelPath(modelId: ModelId): string {
|
|
@@ -65,13 +159,14 @@ export async function downloadFile(
|
|
|
65
159
|
const { done, value } = await reader.read()
|
|
66
160
|
if (done) break
|
|
67
161
|
|
|
68
|
-
writer.write(value)
|
|
162
|
+
await writer.write(value)
|
|
69
163
|
downloaded += value.byteLength
|
|
70
164
|
if (onProgress) onProgress(downloaded, total)
|
|
71
165
|
}
|
|
166
|
+
await writer.flush()
|
|
72
167
|
await writer.end()
|
|
73
168
|
} catch (err) {
|
|
74
|
-
await writer.end()
|
|
169
|
+
try { await writer.end() } catch { /* ignore */ }
|
|
75
170
|
throw err
|
|
76
171
|
}
|
|
77
172
|
}
|
|
@@ -191,9 +286,10 @@ export async function downloadModel(
|
|
|
191
286
|
await downloadFile(url, dest, onProgress)
|
|
192
287
|
console.log(`[hive-local] ✓ Modelo ${modelId} descargado`)
|
|
193
288
|
|
|
194
|
-
// Advertir si el modelo requiere visión y no tenemos mmproj (NO descargar automáticamente)
|
|
195
|
-
|
|
196
|
-
|
|
289
|
+
// Advertir si el modelo requiere visión y no tenemos su mmproj (NO descargar automáticamente)
|
|
290
|
+
const mmprojId = getModelMMProjId(modelId)
|
|
291
|
+
if (mmprojId && !isModelDownloaded(mmprojId)) {
|
|
292
|
+
console.warn(`[hive-local] ⚠️ El modelo ${modelId} requiere el proyector de visión (${mmprojId}). Descárgalo manualmente desde la UI.`)
|
|
197
293
|
}
|
|
198
294
|
|
|
199
295
|
return dest
|
|
@@ -206,11 +302,34 @@ export async function installMMProj(): Promise<string> {
|
|
|
206
302
|
|
|
207
303
|
/** Lista modelos disponibles localmente (solo modelos de texto/visión finales) */
|
|
208
304
|
export function listLocalModels(): { id: ModelId; name: string; size: string; downloaded: boolean }[] {
|
|
209
|
-
const models: ModelId[] = [
|
|
305
|
+
const models: ModelId[] = [
|
|
306
|
+
"e2b_Q4_K_XL",
|
|
307
|
+
"e4b_Q4_K_XL",
|
|
308
|
+
"gemma4_12b_Q4_K_XL",
|
|
309
|
+
"gemma4_26b_Q4_K_M",
|
|
310
|
+
"gemma4_31b_Q4_K_XL",
|
|
311
|
+
"qwen3_5_2b_Q4_K_XL",
|
|
312
|
+
"qwen3_5_4b_Q4_K_XL",
|
|
313
|
+
"qwen3_5_9b_Q4_K_XL",
|
|
314
|
+
"qwen3_5_27b_Q4_K_XL",
|
|
315
|
+
"qwen3_5_35b_Q4_K_XL",
|
|
316
|
+
]
|
|
317
|
+
const sizeMap: Record<string, string> = {
|
|
318
|
+
e2b_Q4_K_XL: "~3 GB",
|
|
319
|
+
e4b_Q4_K_XL: "~5 GB",
|
|
320
|
+
gemma4_12b_Q4_K_XL: "~7 GB",
|
|
321
|
+
gemma4_26b_Q4_K_M: "~10 GB",
|
|
322
|
+
gemma4_31b_Q4_K_XL: "~14 GB",
|
|
323
|
+
qwen3_5_2b_Q4_K_XL: "~1.2 GB",
|
|
324
|
+
qwen3_5_4b_Q4_K_XL: "~2.5 GB",
|
|
325
|
+
qwen3_5_9b_Q4_K_XL: "~5.4 GB",
|
|
326
|
+
qwen3_5_27b_Q4_K_XL: "~16 GB",
|
|
327
|
+
qwen3_5_35b_Q4_K_XL: "~21 GB",
|
|
328
|
+
}
|
|
210
329
|
return models.map((id) => ({
|
|
211
330
|
id,
|
|
212
331
|
name: MODEL_FILES[id],
|
|
213
|
-
size: id
|
|
332
|
+
size: sizeMap[id] || "~?",
|
|
214
333
|
downloaded: isModelDownloaded(id),
|
|
215
334
|
}))
|
|
216
335
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
import { existsSync } from "fs"
|
|
8
|
-
import { MODELS_DIR, getModelPath, type ModelId } from "./downloader"
|
|
8
|
+
import { MODELS_DIR, getModelPath, getModelMMProjId, type ModelId } from "./downloader"
|
|
9
9
|
|
|
10
10
|
export interface ModelConfig {
|
|
11
11
|
modelPath: string
|
|
@@ -43,8 +43,9 @@ export function getModelConfig(modelId: ModelId, port: number = 8081): ModelConf
|
|
|
43
43
|
throw new Error(`Modelo no descargado: ${modelId}. Ejecuta downloadModel("${modelId}") primero.`)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
const
|
|
47
|
-
const
|
|
46
|
+
const mmprojId = getModelMMProjId(modelId)
|
|
47
|
+
const mmprojPath = mmprojId ? getModelPath(mmprojId) : undefined
|
|
48
|
+
const hasMMProj = mmprojPath ? existsSync(mmprojPath) : false
|
|
48
49
|
|
|
49
50
|
return {
|
|
50
51
|
modelPath,
|
|
@@ -2,6 +2,7 @@ import { getDb } from "../storage/sqlite"
|
|
|
2
2
|
|
|
3
3
|
export interface ResolveContextResult {
|
|
4
4
|
userId: string
|
|
5
|
+
threadId: string
|
|
5
6
|
agentId: string
|
|
6
7
|
isNewUser: boolean
|
|
7
8
|
}
|
|
@@ -51,8 +52,11 @@ export function resolveContext(options: ResolveContextOptions): ResolveContextRe
|
|
|
51
52
|
.get()
|
|
52
53
|
|
|
53
54
|
const agentId = coordinatorAgent?.id || "bee"
|
|
55
|
+
// One canonical conversation thread is shared across channels. Transport
|
|
56
|
+
// session IDs route replies; conversations.thread_id owns agent context.
|
|
57
|
+
const threadId = userId
|
|
54
58
|
|
|
55
|
-
return { userId, agentId, isNewUser }
|
|
59
|
+
return { userId, threadId, agentId, isNewUser }
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
export function getDefaultAgentId(): string {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { Config, Binding } from "../config/loader.ts";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import * as path from "node:path";
|
|
2
4
|
|
|
3
5
|
export interface RoutingContext {
|
|
4
6
|
channel: string;
|
|
@@ -113,12 +115,12 @@ export class Router {
|
|
|
113
115
|
const agent = agents.find((a) => a.id === agentId);
|
|
114
116
|
|
|
115
117
|
if (agent?.workspace) {
|
|
116
|
-
return agent.workspace.replace(/^~/,
|
|
118
|
+
return agent.workspace.replace(/^~/, homedir());
|
|
117
119
|
}
|
|
118
120
|
|
|
119
|
-
const baseDir = this.config.agent?.baseDir?.replace(/^~/,
|
|
120
|
-
??
|
|
121
|
-
|
|
122
|
-
return
|
|
121
|
+
const baseDir = this.config.agent?.baseDir?.replace(/^~/, homedir())
|
|
122
|
+
?? path.join(homedir(), ".hive", "agents");
|
|
123
|
+
|
|
124
|
+
return path.join(baseDir, agentId, "workspace");
|
|
123
125
|
}
|
|
124
126
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* POST /api/chat
|
|
5
5
|
* {
|
|
6
6
|
* "message": "Mensaje para el coordinador",
|
|
7
|
-
* "thread_id": "
|
|
7
|
+
* "thread_id": "conversations.thread_id (opcional, usa el thread canónico del usuario si no existe)",
|
|
8
8
|
* "channel": "canal (opcional, default: webchat)"
|
|
9
9
|
* }
|
|
10
10
|
*/
|
|
@@ -12,12 +12,11 @@
|
|
|
12
12
|
import { getDb } from "../../storage/sqlite";
|
|
13
13
|
import { resolveUserId, resolveAgentId } from "../../storage/onboarding";
|
|
14
14
|
import { laneQueue } from "../lane-queue";
|
|
15
|
-
import { getRecentMessages } from "../../agent/conversation-store";
|
|
16
15
|
import { AgentRunner } from "../../agent/providers";
|
|
17
16
|
import { logger } from "../../utils/logger";
|
|
18
|
-
import { getUserDate, getUserTime } from "../../utils/date";
|
|
19
17
|
|
|
20
18
|
const log = logger.child("api:chat");
|
|
19
|
+
export const DEFAULT_CHAT_HISTORY_LIMIT = 40;
|
|
21
20
|
|
|
22
21
|
export interface ChatRequest {
|
|
23
22
|
message: string;
|
|
@@ -34,6 +33,11 @@ export interface ChatResponse {
|
|
|
34
33
|
error?: string;
|
|
35
34
|
}
|
|
36
35
|
|
|
36
|
+
export function resolveChatThreadId(finalUserId: string, requestedThreadId?: string): string {
|
|
37
|
+
const trimmedThreadId = requestedThreadId?.trim();
|
|
38
|
+
return trimmedThreadId || finalUserId || "default";
|
|
39
|
+
}
|
|
40
|
+
|
|
37
41
|
export async function handleChat(
|
|
38
42
|
req: Request,
|
|
39
43
|
addCorsHeaders: (res: Response, req: Request) => Response
|
|
@@ -60,8 +64,8 @@ export async function handleChat(
|
|
|
60
64
|
// Resolve agent ID (coordinator by default)
|
|
61
65
|
const finalAgentId = agentId || resolveAgentId(null) || "main";
|
|
62
66
|
|
|
63
|
-
//
|
|
64
|
-
const threadId = thread_id
|
|
67
|
+
// conversations.thread_id is the context key; never generate a per-request thread.
|
|
68
|
+
const threadId = resolveChatThreadId(finalUserId, thread_id);
|
|
65
69
|
|
|
66
70
|
log.info(`[chat] Processing message from user=${finalUserId} agent=${finalAgentId} thread=${threadId}`);
|
|
67
71
|
|
|
@@ -86,15 +90,10 @@ export async function handleChat(
|
|
|
86
90
|
// Format message with timestamp
|
|
87
91
|
const messageContent = `[Timestamp: ${exactTime} (${userTimezone})]\n${message}`;
|
|
88
92
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
role: row.role as "user" | "assistant" | "system",
|
|
94
|
-
content: row.content,
|
|
95
|
-
})),
|
|
96
|
-
{ role: "user" as const, content: messageContent }
|
|
97
|
-
];
|
|
93
|
+
// AgentLoop persists this user message, then compileContext loads the last
|
|
94
|
+
// 15 messages from conversations by threadId. Prepending history here is
|
|
95
|
+
// ineffective because AgentLoop.stream only consumes the latest user input.
|
|
96
|
+
const messages = [{ role: "user" as const, content: messageContent }];
|
|
98
97
|
|
|
99
98
|
// Get provider config from DB
|
|
100
99
|
const agent = db.query<any, [string]>(
|
|
@@ -124,6 +123,7 @@ export async function handleChat(
|
|
|
124
123
|
maxSteps: 15,
|
|
125
124
|
threadId,
|
|
126
125
|
userId: finalUserId,
|
|
126
|
+
agentId: finalAgentId,
|
|
127
127
|
channel,
|
|
128
128
|
onStep: async (step) => {
|
|
129
129
|
if (step.type === "text" && step.message) {
|
|
@@ -200,12 +200,12 @@ export async function handleChat(
|
|
|
200
200
|
export async function handleGetChatHistory(req: Request, addCorsHeaders: (r: Response, req: Request) => Response): Promise<Response> {
|
|
201
201
|
const url = new URL(req.url)
|
|
202
202
|
const threadId = url.searchParams.get("sessionId") || url.searchParams.get("threadId") || "default"
|
|
203
|
-
const limit = parseInt(url.searchParams.get("limit") ||
|
|
203
|
+
const limit = parseInt(url.searchParams.get("limit") || String(DEFAULT_CHAT_HISTORY_LIMIT))
|
|
204
204
|
|
|
205
205
|
const messages = getDb().query(`
|
|
206
206
|
SELECT id, thread_id, channel, role, content, tool_calls_json, tool_call_id, reasoning_content, token_count, created_at, updated_at FROM conversations
|
|
207
207
|
WHERE thread_id = ? AND role IN ('user', 'assistant')
|
|
208
|
-
ORDER BY
|
|
208
|
+
ORDER BY id DESC
|
|
209
209
|
LIMIT ?
|
|
210
210
|
`).all(threadId, limit) as Record<string, unknown>[]
|
|
211
211
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { apiRequestTool } from "../../tools/api/api-request.ts";
|
|
2
|
+
|
|
3
|
+
export async function handleHttpRequest(req: Request, addCorsHeaders: (r: Response, req: Request) => Response): Promise<Response> {
|
|
4
|
+
try {
|
|
5
|
+
const body = await req.json().catch(() => ({}));
|
|
6
|
+
|
|
7
|
+
const result = await apiRequestTool.execute(body, { configurable: {} });
|
|
8
|
+
|
|
9
|
+
return addCorsHeaders(Response.json(result), req);
|
|
10
|
+
} catch (error) {
|
|
11
|
+
return addCorsHeaders(Response.json({
|
|
12
|
+
ok: false,
|
|
13
|
+
error: `Request failed: ${(error as Error).message}`,
|
|
14
|
+
}), req);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -19,6 +19,15 @@ let installing = false
|
|
|
19
19
|
let installLogs: string[] = []
|
|
20
20
|
let downloadingModelId: string | null = null
|
|
21
21
|
|
|
22
|
+
interface DownloadProgress {
|
|
23
|
+
modelId: string
|
|
24
|
+
downloaded: number
|
|
25
|
+
total: number
|
|
26
|
+
percent: number
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let currentDownloadProgress: DownloadProgress | null = null
|
|
30
|
+
|
|
22
31
|
async function getInstalledStatus() {
|
|
23
32
|
const { LLAMA_CPP_DEFAULT_VER } = await import("../llm-local/detector")
|
|
24
33
|
const gpu = await detectGPU()
|
|
@@ -183,7 +192,12 @@ export async function handleDownloadLLMModel(
|
|
|
183
192
|
;(async () => {
|
|
184
193
|
try {
|
|
185
194
|
await downloadModel(modelId, (d, t) => {
|
|
186
|
-
|
|
195
|
+
currentDownloadProgress = {
|
|
196
|
+
modelId,
|
|
197
|
+
downloaded: d,
|
|
198
|
+
total: t,
|
|
199
|
+
percent: t > 0 ? Math.round((d / t) * 100) : 0,
|
|
200
|
+
}
|
|
187
201
|
})
|
|
188
202
|
installLogs.push(`✓ Modelo ${modelId} descargado exitosamente`)
|
|
189
203
|
} catch (err) {
|
|
@@ -191,6 +205,7 @@ export async function handleDownloadLLMModel(
|
|
|
191
205
|
installLogs.push(`[error] Error descargando ${modelId}: ${msg}`)
|
|
192
206
|
} finally {
|
|
193
207
|
downloadingModelId = null
|
|
208
|
+
currentDownloadProgress = null
|
|
194
209
|
}
|
|
195
210
|
})()
|
|
196
211
|
|
|
@@ -207,7 +222,15 @@ export async function handleStartLocalLLM(
|
|
|
207
222
|
} catch { /* ignore if no body */ }
|
|
208
223
|
|
|
209
224
|
const mode = body.mode || "TEXT"
|
|
210
|
-
|
|
225
|
+
// If no modelId specified, prefer the recommended model; fall back to the
|
|
226
|
+
// first downloaded model so the user doesn't get an error when they only
|
|
227
|
+
// downloaded a non-default model.
|
|
228
|
+
const recommended = getRecommendedModel(mode.toLowerCase() as any)
|
|
229
|
+
const allModels = listLocalModels()
|
|
230
|
+
const downloadedModels = allModels.filter(m => m.downloaded)
|
|
231
|
+
const resolvedModelId: string =
|
|
232
|
+
body.modelId ||
|
|
233
|
+
(downloadedModels.find(m => m.id === recommended) ? recommended : (downloadedModels[0]?.id ?? recommended))
|
|
211
234
|
|
|
212
235
|
const status = await getInstalledStatus()
|
|
213
236
|
if (!status.installed && !status.binaryExists) {
|
|
@@ -217,11 +240,18 @@ export async function handleStartLocalLLM(
|
|
|
217
240
|
)
|
|
218
241
|
}
|
|
219
242
|
|
|
243
|
+
if (downloadedModels.length === 0) {
|
|
244
|
+
return addCors(
|
|
245
|
+
Response.json({ started: false, reason: "No hay modelos descargados. Descarga un modelo primero desde la sección de modelos." }, { status: 400 }),
|
|
246
|
+
req
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
|
|
220
250
|
try {
|
|
221
|
-
installLogs.push(`[llm-server] Iniciando servidor en modo ${mode} con modelo ${
|
|
222
|
-
await llamaManager.start(mode,
|
|
251
|
+
installLogs.push(`[llm-server] Iniciando servidor en modo ${mode} con modelo ${resolvedModelId}...`)
|
|
252
|
+
await llamaManager.start(mode, resolvedModelId as any)
|
|
223
253
|
installLogs.push(`[llm-server] Servidor ${mode} iniciado correctamente.`)
|
|
224
|
-
return addCors(Response.json({ started: true, mode }), req)
|
|
254
|
+
return addCors(Response.json({ started: true, mode, modelId: resolvedModelId }), req)
|
|
225
255
|
} catch (err) {
|
|
226
256
|
const msg = err instanceof Error ? err.message : String(err)
|
|
227
257
|
installLogs.push(`[error] Servidor LLM falló al iniciar: ${msg}`)
|
|
@@ -251,6 +281,22 @@ export async function handleStopLocalLLM(
|
|
|
251
281
|
* Verifica el estado del LLM local al iniciar el gateway.
|
|
252
282
|
* NO auto-inicia el servidor ni descarga nada — el usuario debe hacerlo manualmente.
|
|
253
283
|
*/
|
|
284
|
+
export async function handleGetDownloadProgress(
|
|
285
|
+
req: Request,
|
|
286
|
+
addCors: (r: Response, req: Request) => Response
|
|
287
|
+
): Promise<Response> {
|
|
288
|
+
if (!currentDownloadProgress) {
|
|
289
|
+
return addCors(Response.json({ active: false }), req)
|
|
290
|
+
}
|
|
291
|
+
return addCors(
|
|
292
|
+
Response.json({
|
|
293
|
+
active: true,
|
|
294
|
+
...currentDownloadProgress,
|
|
295
|
+
}),
|
|
296
|
+
req
|
|
297
|
+
)
|
|
298
|
+
}
|
|
299
|
+
|
|
254
300
|
export async function initializeLocalLLM() {
|
|
255
301
|
try {
|
|
256
302
|
const status = await getInstalledStatus()
|