@johpaz/hive-sdk 0.1.4 → 0.1.6

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 (278) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/README.md +78 -23
  3. package/bun.lock +55 -29
  4. package/bunfig.toml +4 -2
  5. package/docs/API-AGENTS.md +78 -27
  6. package/docs/API-CONTEXT-COMPILER.md +31 -34
  7. package/docs/API-TOOLS-SKILLS-CHANNELS.md +58 -22
  8. package/docs/HIVE-HARNESS.md +1 -1
  9. package/docs/INDEX.md +4 -4
  10. package/docs/TEMPLATE-HIVE-APP.md +10 -10
  11. package/package.json +17 -12
  12. package/packages/cli/package.json +2 -2
  13. package/packages/cli/src/commands/create-app.test.ts +36 -7
  14. package/packages/cli/src/commands/init.ts +3 -3
  15. package/packages/cli/src/commands/run.ts +1 -1
  16. package/packages/cli/src/commands/test.ts +37 -25
  17. package/packages/cli/src/commands/trace.ts +30 -28
  18. package/packages/cli/templates/hive-app/.env.example +10 -2
  19. package/packages/cli/templates/hive-app/README.md +103 -0
  20. package/packages/cli/templates/hive-app/hive.config.ts +9 -3
  21. package/packages/cli/templates/hive-app/src/agents/coordinator.ts +8 -1
  22. package/packages/cli/templates/hive-app/src/main.ts +12 -19
  23. package/packages/core/package.json +13 -12
  24. package/packages/core/src/agent/acceptance-checks.ts +172 -0
  25. package/packages/core/src/agent/agent-catalog.ts +348 -0
  26. package/packages/core/src/agent/agent-loop.ts +1373 -0
  27. package/packages/core/src/agent/capability-search.ts +186 -0
  28. package/packages/core/src/agent/catalog-selector.ts +103 -0
  29. package/packages/core/src/agent/{Compaction.ts → compaction.ts} +86 -63
  30. package/packages/core/src/agent/context-compiler.ts +689 -0
  31. package/packages/core/src/agent/conversation-store.ts +381 -0
  32. package/packages/core/src/agent/curator.ts +276 -0
  33. package/packages/core/src/agent/delegation-runtime.ts +241 -0
  34. package/packages/core/src/agent/goal-runner.ts +323 -0
  35. package/packages/core/src/agent/index.ts +17 -12
  36. package/packages/core/src/agent/llm-client.ts +266 -0
  37. package/packages/core/src/agent/llm-providers/anthropic.ts +264 -0
  38. package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
  39. package/packages/core/src/agent/{providers → llm-providers}/gemini.ts +98 -60
  40. package/packages/core/src/agent/llm-providers/groq.ts +5 -0
  41. package/packages/core/src/agent/llm-providers/hiveagents.ts +253 -0
  42. package/packages/core/src/agent/{providers → llm-providers}/interface.ts +73 -13
  43. package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
  44. package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
  45. package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
  46. package/packages/core/src/agent/llm-providers/modelscope.ts +5 -0
  47. package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
  48. package/packages/core/src/agent/{providers → llm-providers}/ollama.ts +31 -5
  49. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +418 -0
  50. package/packages/core/src/agent/llm-providers/openai.ts +5 -0
  51. package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
  52. package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
  53. package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
  54. package/packages/core/src/agent/llm-providers/z-ai.ts +5 -0
  55. package/packages/core/src/agent/minimal-loadout.ts +47 -0
  56. package/packages/core/src/agent/playbook-selector.ts +119 -0
  57. package/packages/core/src/agent/{PromptBuilder.ts → prompt-builder.ts} +21 -22
  58. package/packages/core/src/{harness → agent}/proof-packet.ts +16 -21
  59. package/packages/core/src/agent/providers/index.ts +35 -16
  60. package/packages/core/src/agent/reflector.ts +320 -0
  61. package/packages/core/src/agent/routing-intent.ts +22 -0
  62. package/packages/core/src/{harness → agent}/run-epoch.ts +4 -3
  63. package/packages/core/src/{harness → agent}/run-store.ts +142 -81
  64. package/packages/core/src/agent/{Service.ts → service.ts} +37 -26
  65. package/packages/core/src/agent/skill-selector.ts +374 -0
  66. package/packages/core/src/agent/stuck-loop.ts +209 -0
  67. package/packages/core/src/agent/{selectors/ToolSelector.ts → tool-selector.ts} +188 -178
  68. package/packages/core/src/{ace/Tracer.ts → agent/tracer.ts} +37 -27
  69. package/packages/core/src/api/createAgent.test.ts +139 -27
  70. package/packages/core/src/api/createAgent.ts +232 -44
  71. package/packages/core/src/artifacts/store.ts +162 -0
  72. package/packages/core/src/canvas/canvas-manager.ts +161 -0
  73. package/packages/core/src/canvas/canvas.test.ts +8 -4
  74. package/packages/core/src/canvas/emitter.ts +131 -80
  75. package/packages/core/src/canvas/index.ts +1 -3
  76. package/packages/core/src/channels/base.ts +9 -1
  77. package/packages/core/src/channels/discord.ts +5 -4
  78. package/packages/core/src/channels/manager.ts +122 -30
  79. package/packages/core/src/channels/slack.ts +5 -4
  80. package/packages/core/src/channels/telegram.ts +36 -6
  81. package/packages/core/src/channels/webchat.ts +11 -10
  82. package/packages/core/src/channels/whatsapp.ts +23 -7
  83. package/packages/core/src/config/index.ts +13 -2
  84. package/packages/core/src/config/loader.ts +76 -29
  85. package/packages/core/src/ethics/EthicsGuard.test.ts +90 -36
  86. package/packages/core/src/ethics/EthicsGuard.ts +51 -47
  87. package/packages/core/src/events/agent-bus.ts +44 -68
  88. package/packages/core/src/events/channel-narration.ts +150 -0
  89. package/packages/core/src/events/narration.ts +82 -0
  90. package/packages/core/src/events/tool-narration.ts +62 -0
  91. package/packages/core/src/gateway/delegation-groups.ts +258 -0
  92. package/packages/core/src/{harness → gateway}/durable-queue.ts +102 -42
  93. package/packages/core/src/{harness → gateway}/job-store.ts +85 -48
  94. package/packages/core/src/gateway/lane-queue.ts +173 -0
  95. package/packages/core/src/gateway/notification-inbox.ts +57 -0
  96. package/packages/core/src/gateway/server.ts +1 -1
  97. package/packages/core/src/harness/index.ts +46 -27
  98. package/packages/core/src/index.ts +33 -27
  99. package/packages/core/src/mcp/hot-reload.ts +32 -23
  100. package/packages/core/src/mcp/index.ts +6 -3
  101. package/packages/core/src/mcp/singleton.ts +1 -4
  102. package/packages/core/src/mcp/tool-sync.ts +138 -0
  103. package/packages/core/src/memory/Scratchpad.test.ts +39 -20
  104. package/packages/core/src/memory/Scratchpad.ts +27 -34
  105. package/packages/core/src/multimodal/vision-service.ts +44 -38
  106. package/packages/core/src/resilience/retry.ts +95 -0
  107. package/packages/core/src/scheduler/CronScheduler.ts +334 -287
  108. package/packages/core/src/scheduler/index.ts +9 -7
  109. package/packages/core/src/scheduler/integration.ts +46 -26
  110. package/packages/core/src/scheduler/scheduler.test.ts +9 -13
  111. package/packages/core/src/scheduler/types.ts +7 -2
  112. package/packages/core/src/security/Pairing.ts +1 -1
  113. package/packages/core/src/skills/bundled/a2ui/a2ui_dashboard/SKILL.md +176 -0
  114. package/packages/core/src/skills/bundled/a2ui/a2ui_form/SKILL.md +202 -0
  115. package/packages/core/src/skills/bundled/a2ui/a2ui_interactive/SKILL.md +206 -0
  116. package/packages/core/src/skills/bundled/agents/agent_spawner/SKILL.md +173 -0
  117. package/packages/core/src/skills/bundled/agents/memory_manager/SKILL.md +143 -0
  118. package/packages/core/src/skills/bundled/agents/research_and_remember/SKILL.md +139 -0
  119. package/packages/core/src/skills/bundled/agents/task_orchestrator/SKILL.md +98 -0
  120. package/packages/core/src/skills/bundled/api/api_client/SKILL.md +132 -0
  121. package/packages/core/src/skills/bundled/cli/cli_pipeline/SKILL.md +135 -0
  122. package/packages/core/src/skills/bundled/cli/cli_safe_exec/SKILL.md +125 -0
  123. package/packages/core/src/skills/bundled/cli/software_engineering/SKILL.md +23 -0
  124. package/packages/core/src/skills/bundled/cron_manager/SKILL.md +188 -0
  125. package/packages/core/src/skills/bundled/cron_reminder/SKILL.md +112 -0
  126. package/packages/core/src/skills/bundled/filesystem/file_manager/SKILL.md +118 -0
  127. package/packages/core/src/skills/bundled/filesystem/file_read_and_summarize/SKILL.md +109 -0
  128. package/packages/core/src/skills/bundled/filesystem/file_writer/SKILL.md +129 -0
  129. package/packages/core/src/skills/bundled/filesystem/workspace_file_operator/SKILL.md +22 -0
  130. package/packages/core/src/skills/bundled/office/office_document_manager/SKILL.md +262 -0
  131. package/packages/core/src/skills/bundled/search_knowledge/capability_discovery/SKILL.md +75 -0
  132. package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +120 -0
  133. package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +109 -0
  134. package/packages/core/src/skills/bundled/web/web_monitor/SKILL.md +127 -0
  135. package/packages/core/src/skills/bundled/web/web_research/SKILL.md +119 -0
  136. package/packages/core/src/skills/bundled-data.generated.ts +731 -2678
  137. package/packages/core/src/skills/skills.test.ts +52 -11
  138. package/packages/core/src/{harness → storage}/boot-id.ts +5 -2
  139. package/packages/core/src/storage/bootstrap.ts +151 -0
  140. package/packages/core/src/storage/causal-events.ts +84 -0
  141. package/packages/core/src/storage/collections.ts +680 -0
  142. package/packages/core/src/storage/crypto.ts +205 -74
  143. package/packages/core/src/{harness/db-helpers.ts → storage/hive.ts} +63 -7
  144. package/packages/core/src/storage/hivedb.ts +61 -0
  145. package/packages/core/src/storage/index.ts +111 -18
  146. package/packages/core/src/storage/model-id.ts +53 -0
  147. package/packages/core/src/storage/onboarding.ts +540 -972
  148. package/packages/core/src/storage/reconcile.ts +238 -0
  149. package/packages/core/src/storage/seed.ts +572 -406
  150. package/packages/core/src/storage/usage.ts +285 -225
  151. package/packages/core/src/storage/user-email.ts +11 -0
  152. package/packages/core/src/swarm/AgentExecutor.ts +1 -1
  153. package/packages/core/src/swarm/EventBridge.ts +1 -1
  154. package/packages/core/src/swarm/index.ts +12 -9
  155. package/packages/core/src/tool-runtime/index.ts +146 -23
  156. package/packages/core/src/tool-runtime/tool-worker.ts +2 -2
  157. package/packages/core/src/tool-runtime/worker-tools.ts +27 -0
  158. package/packages/core/src/{canvas/a2ui-tools.ts → tools/a2ui/index.ts} +17 -8
  159. package/packages/core/src/tools/agents/get-available-models.ts +36 -54
  160. package/packages/core/src/tools/agents/index.ts +784 -292
  161. package/packages/core/src/tools/api/api-request.test.ts +164 -0
  162. package/packages/core/src/tools/api/api-request.ts +174 -0
  163. package/packages/core/src/tools/api/index.ts +16 -0
  164. package/packages/core/src/tools/cli/index.ts +4 -0
  165. package/packages/core/src/tools/core/index.ts +281 -112
  166. package/packages/core/src/tools/cron/index.ts +121 -124
  167. package/packages/core/src/tools/index.ts +63 -78
  168. package/packages/core/src/tools/office/office-escribir-xlsx.ts +3 -1
  169. package/packages/core/src/tools/types.ts +3 -1
  170. package/packages/core/src/tools/web/artifact-inspect.ts +23 -0
  171. package/packages/core/src/tools/web/browser-backend.ts +129 -0
  172. package/packages/core/src/tools/web/browser-screenshot.ts +26 -5
  173. package/packages/core/src/tools/web/browser-service.ts +80 -35
  174. package/packages/core/src/tools/web/browser-type.ts +3 -8
  175. package/packages/core/src/tools/web/index.ts +4 -4
  176. package/packages/core/src/tools/web/webview-backend.ts +412 -0
  177. package/packages/core/src/voice/index.ts +89 -63
  178. package/packages/core/src/workers/agent.worker.ts +2 -2
  179. package/packages/core/src/workers/workers.test.ts +3 -10
  180. package/scripts/bump-version.ts +248 -0
  181. package/scripts/generate-skill-bundle.ts +108 -0
  182. package/test/acceptance-checks.test.ts +403 -0
  183. package/test/agent-loop-terminal-synthesis.test.ts +32 -0
  184. package/test/browser-backend.test.ts +308 -0
  185. package/test/catalog-agents-stay-enabled.test.ts +117 -0
  186. package/test/causal-events.test.ts +117 -0
  187. package/test/compaction.test.ts +105 -0
  188. package/test/context-compiler.test.ts +269 -0
  189. package/test/curator.test.ts +130 -0
  190. package/test/durable-queue.test.ts +114 -0
  191. package/test/harness-barrel.test.ts +64 -0
  192. package/test/hive-helpers.test.ts +130 -0
  193. package/test/hivedb-search.test.ts +189 -0
  194. package/test/internal-turns.test.ts +166 -0
  195. package/test/job-idempotency.test.ts +68 -0
  196. package/test/job-retry-backoff.test.ts +184 -0
  197. package/test/job-store.test.ts +381 -0
  198. package/test/llm-retry.test.ts +97 -0
  199. package/test/memory-perf.test.ts +774 -0
  200. package/test/minimal-loadout.test.ts +78 -0
  201. package/test/model-catalog.test.ts +105 -0
  202. package/test/preload.ts +12 -0
  203. package/test/reflector.test.ts +320 -0
  204. package/test/retention-cap.test.ts +91 -0
  205. package/test/retired-capabilities-pruned.test.ts +192 -0
  206. package/test/run-store.test.ts +355 -0
  207. package/test/scratchpad.test.ts +74 -0
  208. package/test/secrets-durability.test.ts +119 -0
  209. package/test/seed-model-reseed.test.ts +155 -0
  210. package/test/setup-agent-seed.test.ts +264 -0
  211. package/test/tool-inventory.test.ts +65 -0
  212. package/test/tool-runtime.test.ts +258 -0
  213. package/test/tool-selector-runtime-tools.test.ts +117 -0
  214. package/test/toon.test.ts +429 -0
  215. package/tsconfig.json +2 -0
  216. package/packages/core/src/ace/Curator.ts +0 -158
  217. package/packages/core/src/ace/Reflector.ts +0 -200
  218. package/packages/core/src/ace/index.ts +0 -4
  219. package/packages/core/src/agent/AgentRunner.ts +0 -711
  220. package/packages/core/src/agent/ContextCompiler.ts +0 -567
  221. package/packages/core/src/agent/ContextGuard.ts +0 -91
  222. package/packages/core/src/agent/ConversationStore.ts +0 -254
  223. package/packages/core/src/agent/Hooks.ts +0 -166
  224. package/packages/core/src/agent/StuckLoop.ts +0 -133
  225. package/packages/core/src/agent/providers/LLMClient.ts +0 -149
  226. package/packages/core/src/agent/providers/anthropic.ts +0 -212
  227. package/packages/core/src/agent/providers/openai-compat.ts +0 -231
  228. package/packages/core/src/agent/selectors/PlaybookSelector.ts +0 -121
  229. package/packages/core/src/agent/selectors/SkillSelector.ts +0 -322
  230. package/packages/core/src/agent/selectors/index.ts +0 -6
  231. package/packages/core/src/auth/auth.ts +0 -121
  232. package/packages/core/src/auth/index.ts +0 -1
  233. package/packages/core/src/canvas/CanvasManager.ts +0 -390
  234. package/packages/core/src/canvas/canvas-tools.ts +0 -448
  235. package/packages/core/src/harness/collections.ts +0 -98
  236. package/packages/core/src/harness/goal-verifier.ts +0 -141
  237. package/packages/core/src/harness/harness.test.ts +0 -236
  238. package/packages/core/src/harness/reconcile.ts +0 -149
  239. package/packages/core/src/mcp/MCPToolAdapter.ts +0 -176
  240. package/packages/core/src/multimodal/VisionService.ts +0 -293
  241. package/packages/core/src/scheduler/dag/AgentExecutor.ts +0 -53
  242. package/packages/core/src/scheduler/dag/DAGScheduler.ts +0 -250
  243. package/packages/core/src/scheduler/dag/EventBridge.ts +0 -122
  244. package/packages/core/src/scheduler/dag/TaskGraph.ts +0 -192
  245. package/packages/core/src/scheduler/dag/TaskNode.ts +0 -97
  246. package/packages/core/src/scheduler/dag/TaskResult.ts +0 -22
  247. package/packages/core/src/scheduler/dag/errors.ts +0 -37
  248. package/packages/core/src/scheduler/dag/index.ts +0 -26
  249. package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +0 -97
  250. package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +0 -21
  251. package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +0 -46
  252. package/packages/core/src/storage/HiveDBStorage.ts +0 -64
  253. package/packages/core/src/storage/SQLiteStorage.ts +0 -414
  254. package/packages/core/src/storage/hiveSeed.ts +0 -308
  255. package/packages/core/src/storage/hiveStorage.test.ts +0 -38
  256. package/packages/core/src/storage/schema.ts +0 -689
  257. package/packages/core/src/storage/storage.test.ts +0 -37
  258. package/packages/core/src/swarm/AgentBus.ts +0 -460
  259. package/packages/core/src/swarm/EventBus.ts +0 -169
  260. package/packages/core/src/swarm/WorkerPool.ts +0 -236
  261. package/packages/core/src/tools/bridge-events.ts +0 -26
  262. package/packages/core/src/tools/canvas/index.ts +0 -375
  263. package/packages/core/src/tools/codebridge/index.ts +0 -342
  264. package/packages/core/src/tools/meeting/index.ts +0 -353
  265. package/packages/core/src/tools/projects/index.ts +0 -37
  266. package/packages/core/src/tools/projects/project-create.ts +0 -94
  267. package/packages/core/src/tools/projects/project-done.ts +0 -66
  268. package/packages/core/src/tools/projects/project-fail.ts +0 -66
  269. package/packages/core/src/tools/projects/project-list.ts +0 -96
  270. package/packages/core/src/tools/projects/project-update.ts +0 -72
  271. package/packages/core/src/tools/projects/task-create.ts +0 -68
  272. package/packages/core/src/tools/projects/task-evaluate.ts +0 -93
  273. package/packages/core/src/tools/projects/task-update.ts +0 -93
  274. package/packages/core/src/tools/voice/index.ts +0 -104
  275. package/packages/core/src/tools/web/api-request.test.ts +0 -170
  276. package/packages/core/src/tools/web/api-request.ts +0 -239
  277. package/test/setup-db.ts +0 -216
  278. /package/packages/core/src/agent/{NativeTools.ts → native-tools.ts} +0 -0
@@ -5,38 +5,151 @@
5
5
  */
6
6
 
7
7
  import type { Tool } from "../types.ts";
8
- import { getDb } from "../../storage/SQLiteStorage.ts";
9
- import { getHiveDB } from "../../storage/HiveDBStorage.ts";
8
+ import { col } from "../../storage/hive.ts";
9
+ import type { ToolDoc, SkillDoc, PlaybookDoc, McpToolDoc, TaskDoc, AgentDoc } from "../../storage/collections.ts";
10
10
  import { logger } from "../../utils/logger.ts";
11
+ import {
12
+ searchCapabilities,
13
+ type CapabilityHit,
14
+ type CapabilityType,
15
+ } from "../../agent/capability-search.ts";
16
+ import { CORE_TOOL_CATALOG } from "../../agent/tool-selector.ts";
17
+ import { saveScratchpadNote } from "../../agent/conversation-store.ts";
11
18
 
12
19
  const log = logger.child("core");
13
20
 
14
- // ─── search_knowledge ────────────────────────────────────────────────────────
21
+ // ─── Bilingual dictionary: Spanish → English ────────────────────────────────
22
+
23
+ const ES_EN_DICT: Record<string, string[]> = {
24
+ // Acciones
25
+ "buscar": ["search", "find", "list", "get", "query"],
26
+ "listar": ["list", "get", "fetch", "retrieve"],
27
+ "crear": ["create", "add", "insert", "new", "make"],
28
+ "actualizar": ["update", "edit", "modify", "change"],
29
+ "eliminar": ["delete", "remove", "destroy"],
30
+ "obtener": ["get", "fetch", "retrieve", "read"],
31
+ "enviar": ["send", "post", "submit", "push"],
32
+ "leer": ["read", "get", "fetch"],
33
+ "escribir": ["write", "create", "save"],
34
+ "modificar": ["update", "modify", "edit", "change"],
35
+ "ejecutar": ["execute", "run", "invoke"],
36
+ "conectar": ["connect", "link"],
37
+ "desconectar": ["disconnect", "remove"],
38
+ "descargar": ["download", "export", "fetch"],
39
+ "subir": ["upload", "import", "create"],
40
+ "analizar": ["analyze", "review", "examine"],
41
+ "generar": ["generate", "create", "produce"],
42
+ "convertir": ["convert", "transform", "translate"],
43
+ "validar": ["validate", "verify", "check"],
44
+ "importar": ["import", "load", "ingest"],
45
+ "exportar": ["export", "download", "extract"],
46
+ "comprimir": ["compress", "zip", "archive"],
47
+ "extraer": ["extract", "get", "retrieve", "parse"],
48
+ "reemplazar": ["replace", "update", "swap"],
49
+ "cargar": ["load", "import", "upload"],
50
+ "guardar": ["save", "store", "create"],
51
+ "consultar": ["query", "search", "get", "list"],
52
+ "registrar": ["register", "create", "log", "record"],
53
+ "programar": ["schedule", "plan", "cron"],
54
+ "notificar": ["notify", "alert", "send"],
55
+ "reiniciar": ["restart", "reset", "reboot"],
56
+ "configurar": ["configure", "setup", "set"],
57
+ "autenticar": ["authenticate", "login", "auth"],
58
+ "publicar": ["publish", "deploy", "release"],
59
+ "desplegar": ["deploy", "publish", "release"],
60
+ "copiar": ["copy", "clone", "duplicate"],
61
+ "mover": ["move", "transfer", "migrate"],
62
+ "comparar": ["compare", "diff", "match"],
63
+ "fusionar": ["merge", "combine", "join"],
64
+ "dividir": ["split", "divide", "partition"],
65
+ "filtrar": ["filter", "search", "query"],
66
+ "ordenar": ["sort", "order", "arrange"],
67
+ "traducir": ["translate", "convert"],
68
+
69
+ // Entidades
70
+ "base": ["base", "database", "db"],
71
+ "bases": ["bases", "databases"],
72
+ "datos": ["data", "records", "rows", "entries"],
73
+ "registro": ["record", "entry", "row", "item"],
74
+ "registros": ["records", "entries", "rows", "items"],
75
+ "tabla": ["table", "schema", "collection"],
76
+ "tablas": ["tables", "schemas"],
77
+ "campo": ["field", "column", "property"],
78
+ "campos": ["fields", "columns", "properties"],
79
+ "usuario": ["user", "account"],
80
+ "usuarios": ["users", "accounts"],
81
+ "proyecto": ["project", "repo", "workspace"],
82
+ "proyectos": ["projects", "repos", "workspaces"],
83
+ "archivo": ["file", "document"],
84
+ "archivos": ["files", "documents"],
85
+ "correo": ["email", "mail", "message"],
86
+ "correos": ["emails", "mails", "messages"],
87
+ "noticia": ["news", "article", "post"],
88
+ "noticias": ["news", "articles", "posts"],
89
+ "contenido": ["content", "data", "text"],
90
+ "tarea": ["task", "job", "issue", "ticket"],
91
+ "tareas": ["tasks", "jobs", "issues", "tickets"],
92
+ "pagina": ["page", "site", "web"],
93
+ "enlace": ["link", "url", "reference"],
94
+ "imagen": ["image", "picture", "photo"],
95
+ "video": ["video", "media"],
96
+ "audio": ["audio", "sound", "media"],
97
+ "categoria": ["category", "tag", "label"],
98
+ "estado": ["status", "state", "condition"],
99
+ "error": ["error", "exception", "fault"],
100
+ "fuente": ["source", "origin", "reference"],
101
+ "esquema": ["schema", "structure", "model"],
102
+ "respuesta": ["response", "reply", "answer"],
103
+ "solicitud": ["request", "query", "call"],
104
+ "repositorio": ["repository", "repo"],
105
+ "seguridad": ["security", "auth", "permission"],
106
+ "permiso": ["permission", "role", "access"],
107
+ "acceso": ["access", "login", "entry"],
108
+ "servidor": ["server", "host", "service"],
109
+ "conexion": ["connection", "link", "integration"],
110
+ "integracion": ["integration", "connector", "plugin"],
111
+ "herramienta": ["tool", "utility", "function"],
112
+ "informacion": ["info", "information", "details"],
113
+ "lista": ["list", "collection", "array"],
114
+ "reporte": ["report", "summary", "analytics"],
115
+ "metrica": ["metric", "stat", "analytics"],
116
+ "contacto": ["contact", "lead", "person"],
117
+ };
118
+
119
+ /**
120
+ * Translate a Spanish query to English equivalents for the bilingual BM25 fallback.
121
+ * Returns an array of English keyword tokens.
122
+ */
123
+ function translateQueryToEnglish(query: string): string {
124
+ const words = query.toLowerCase().replace(/_/g, " ").split(/\s+/).filter(w => w.length > 1);
125
+ const translated: string[] = [];
126
+
127
+ for (const word of words) {
128
+ const equivalents = ES_EN_DICT[word];
129
+ if (equivalents) {
130
+ translated.push(...equivalents);
131
+ }
132
+ }
15
133
 
16
- function buildSearchQuery(query: string): string {
17
- return query
18
- .toLowerCase()
19
- .replace(/[^\p{L}\p{N}\s]/gu, " ")
20
- .split(/\s+/)
21
- .filter(w => w.length > 2)
22
- .slice(0, 8)
23
- .join(" ");
134
+ return [...new Set(translated)].join(" ");
24
135
  }
25
136
 
137
+ // ─── search_knowledge ────────────────────────────────────────────────────────
138
+
26
139
  export const searchKnowledgeTool: Tool = {
27
140
  name: "search_knowledge",
28
- description: "Busca herramientas NATIVAS (tools), MCP (tools externas), habilidades (skills) o reglas del playbook en la base de conocimientos. Usa búsqueda híbrida (BM25 + vector). type='mcp' para herramientas MCP, type='all' para buscar en todo.",
141
+ description: "Busca en TODO el conocimiento de Hive: tools nativas, MCP, skills, agentes de catálogo y playbook.",
29
142
  parameters: {
30
143
  type: "object",
31
144
  properties: {
32
145
  query: {
33
146
  type: "string",
34
- description: "Término de búsqueda (nombre, descripción, categoría).",
147
+ description: "Una palabra clave. Ejemplos: 'email', 'github', 'pdf', 'browser', 'calendar'. Busca en nombres, descripciones y categorías.",
35
148
  },
36
149
  type: {
37
150
  type: "string",
38
- enum: ["all", "tools", "skills", "playbook", "mcp"],
39
- description: "Tipo de conocimiento a buscar",
151
+ enum: ["all", "tools", "skills", "playbook", "mcp", "agents"],
152
+ description: "Opcional. Por defecto 'all' (busca en todo). Usa 'mcp' para filtrar solo herramientas externas, 'tools' para solo nativas.",
40
153
  },
41
154
  limit: {
42
155
  type: "number",
@@ -46,108 +159,166 @@ export const searchKnowledgeTool: Tool = {
46
159
  required: ["query"],
47
160
  },
48
161
  execute: async (params: Record<string, unknown>) => {
49
- const db = await getHiveDB();
50
162
  const query = params.query as string;
51
163
  const type = (params.type as string) ?? "all";
52
164
  const limit = (params.limit as number) ?? 10;
165
+ const MIN_RESULTS_FOR_BILINGUAL = 2;
166
+
167
+ // Map the tool's `type` param onto capability types
168
+ const typeMap: Record<string, CapabilityType[] | undefined> = {
169
+ all: undefined,
170
+ tools: ["tool"],
171
+ skills: ["skill"],
172
+ playbook: ["playbook"],
173
+ mcp: ["mcp"],
174
+ agents: ["agent"],
175
+ };
176
+ const types = typeMap[type] ?? undefined;
53
177
 
54
178
  try {
55
- const searchQuery = buildSearchQuery(query);
56
- if (!searchQuery) {
57
- return { ok: true, query, type, tools: [], skills: [], playbook: [], toolsmcp: [], totalResults: 0 };
179
+ if (!query) {
180
+ log.info(`[search_knowledge] Empty query — returning empty results`)
181
+ return { query, type, tools: [], skills: [], playbook: [], toolsmcp: [], agents: [] }
58
182
  }
183
+ // HiveDB parses raw text leniently (accents, quotes, operators are all
184
+ // safe), so the query goes in as-is — only underscores become spaces so
185
+ // tool ids like "send_email" match their tokens.
186
+ const normalizedQuery = query.replace(/_/g, " ").trim();
59
187
 
60
- const result: any = { query, type, tools: [], skills: [], playbook: [], toolsmcp: [] };
188
+ const result: any = { query, type, tools: [], skills: [], playbook: [], toolsmcp: [], agents: [] };
61
189
 
62
- async function searchByType(docType: string) {
63
- return db.queryHybrid({
64
- text: searchQuery,
65
- k: limit,
66
- filters: [{ field: "type", value: docType }],
67
- boosts: { name: 5.0, body: 3.0, tags: 2.0 },
68
- });
69
- }
190
+ // ─── Hydration from HiveDB collections (index stores only ids + search text) ──
70
191
 
71
- if (type === "all" || type === "tools") {
72
- const hits = await searchByType("tool");
73
- const toolsCol = db.collection<{ name: string; description: string; category: string; enabled: boolean; active: boolean }>("tools");
74
- for (const hit of hits) {
75
- const entry = await toolsCol.get(hit.id);
76
- const doc = entry?.doc;
77
- result.tools.push({
78
- id: hit.id,
79
- name: doc?.name ?? hit.id,
80
- description: doc?.description ?? "",
81
- category: doc?.category ?? "core",
82
- enabled: doc?.enabled ?? true,
83
- active: doc?.active ?? true,
84
- rank: hit.score,
85
- });
192
+ const coreCatalog = new Map(CORE_TOOL_CATALOG.map(t => [t.name, t]));
193
+
194
+ const toolsCol = await col<ToolDoc>("tools");
195
+ const skillsCol = await col<SkillDoc>("skills");
196
+ const playbookCol = await col<PlaybookDoc>("playbook");
197
+ const mcpToolsCol = await col<McpToolDoc>("mcpTools");
198
+ const agentsCol = await col<AgentDoc>("agents");
199
+
200
+ async function hydrateTool(hit: CapabilityHit): Promise<any | null> {
201
+ const entry = await toolsCol.get(hit.rawId);
202
+ if (entry) {
203
+ const row = entry.doc;
204
+ return {
205
+ id: row.id, name: row.name, description: row.description, category: row.category,
206
+ enabled: row.enabled, active: row.active, rank: hit.score,
207
+ };
86
208
  }
209
+ // Core catalog tools may not exist in the tools collection
210
+ const cat = coreCatalog.get(hit.rawId);
211
+ if (!cat) return null;
212
+ return {
213
+ id: cat.name, name: cat.name, description: cat.description, category: cat.category,
214
+ enabled: true, active: true, rank: hit.score,
215
+ };
87
216
  }
88
217
 
89
- if (type === "all" || type === "skills") {
90
- const hits = await searchByType("skill");
91
- const skillsCol = db.collection<{ name: string; description: string; category: string; body: string; tools: string[]; triggers: string[]; active: boolean }>("skills");
92
- for (const hit of hits) {
93
- const entry = await skillsCol.get(hit.id);
94
- const doc = entry?.doc;
95
- result.skills.push({
96
- id: hit.id,
97
- name: doc?.name ?? hit.id,
98
- description: doc?.description ?? "",
99
- category: doc?.category ?? "general",
100
- tools: doc?.tools ?? [],
101
- triggers: doc?.triggers ?? [],
102
- body: doc?.body ? (doc.body.length > 400 ? doc.body.substring(0, 400) + "…" : doc.body) : undefined,
103
- active: doc?.active ?? true,
104
- rank: hit.score,
105
- });
106
- }
218
+ async function hydrateSkill(hit: CapabilityHit): Promise<any | null> {
219
+ const entry = await skillsCol.get(hit.rawId);
220
+ const s = entry?.doc;
221
+ if (!s || !s.active) return null;
222
+ return {
223
+ id: s.id, name: s.name, description: s.description, category: s.category,
224
+ tools: s.tools, triggers: s.triggers,
225
+ preferred_agents: s.preferred_agents ? JSON.parse(s.preferred_agents) : [],
226
+ body: s.body ? (s.body.length > 1500 ? s.body.substring(0, 1500) + "…" : s.body) : undefined,
227
+ active: s.active, rank: hit.score,
228
+ };
229
+ }
230
+
231
+ async function hydratePlaybook(hit: CapabilityHit): Promise<any | null> {
232
+ const entry = await playbookCol.get(hit.rawId);
233
+ const p = entry?.doc;
234
+ if (!p || !p.active) return null;
235
+ return {
236
+ id: p.id, rule: p.rule, category: p.category,
237
+ applicable_to: p.applicable_to ? JSON.parse(p.applicable_to) : null,
238
+ helpful_count: p.helpful_count, harmful_count: p.harmful_count,
239
+ active: p.active, rank: hit.score,
240
+ };
241
+ }
242
+
243
+ async function hydrateMcp(hit: CapabilityHit): Promise<any | null> {
244
+ const entry = await mcpToolsCol.get(hit.rawId);
245
+ const t = entry?.doc;
246
+ if (!t || !t.active) return null;
247
+ return {
248
+ id: t.id, full_name: t.id, server_id: t.server_id, server_name: t.server_name, tool_name: t.tool_name,
249
+ description: t.description, category: t.category,
250
+ active: t.active, rank: hit.score,
251
+ };
107
252
  }
108
253
 
109
- if (type === "all" || type === "playbook") {
110
- const hits = await searchByType("playbook");
111
- const playbookCol = db.collection<{ rule: string; category: string; applicableTo?: string[]; active: boolean }>("playbook");
254
+ async function hydrateAgent(hit: CapabilityHit): Promise<any | null> {
255
+ const entry = await agentsCol.get(hit.rawId);
256
+ const agent = entry?.doc;
257
+ if (!agent || agent.source !== "catalog" || !agent.enabled) return null;
258
+ return {
259
+ id: agent.id,
260
+ name: agent.name,
261
+ description: agent.description,
262
+ default_acceptance: agent.default_acceptance_json ? JSON.parse(agent.default_acceptance_json) : [],
263
+ active: agent.enabled,
264
+ rank: hit.score,
265
+ };
266
+ }
267
+
268
+ const seenIds = new Set<string>();
269
+
270
+ async function mergeHits(hits: CapabilityHit[]): Promise<number> {
271
+ let added = 0;
112
272
  for (const hit of hits) {
113
- const entry = await playbookCol.get(hit.id);
114
- const doc = entry?.doc;
115
- result.playbook.push({
116
- id: hit.id,
117
- rule: doc?.rule ?? "",
118
- category: doc?.category ?? "",
119
- applicable_to: doc?.applicableTo ?? null,
120
- active: doc?.active ?? true,
121
- rank: hit.score,
122
- });
273
+ if (seenIds.has(hit.id)) continue;
274
+ let entry: any = null;
275
+ let bucket: any[] | null = null;
276
+ switch (hit.type) {
277
+ case "tool":
278
+ if (result.tools.length < limit) { entry = await hydrateTool(hit); bucket = result.tools; }
279
+ break;
280
+ case "skill":
281
+ if (result.skills.length < limit) { entry = await hydrateSkill(hit); bucket = result.skills; }
282
+ break;
283
+ case "playbook":
284
+ if (result.playbook.length < limit) { entry = await hydratePlaybook(hit); bucket = result.playbook; }
285
+ break;
286
+ case "mcp":
287
+ if (result.toolsmcp.length < limit) { entry = await hydrateMcp(hit); bucket = result.toolsmcp; }
288
+ break;
289
+ case "agent":
290
+ if (result.agents.length < limit) { entry = await hydrateAgent(hit); bucket = result.agents; }
291
+ break;
292
+ }
293
+ if (entry && bucket) {
294
+ bucket.push(entry);
295
+ seenIds.add(hit.id);
296
+ added++;
297
+ }
123
298
  }
299
+ return added;
124
300
  }
125
301
 
126
- if (type === "all" || type === "mcp") {
127
- const mcpCol = db.collection<{ serverName: string; toolName: string; description: string; category: string; active: boolean }>("mcp_tools");
128
- const entries = await mcpCol.scan();
129
- const q = searchQuery.toLowerCase();
130
- const matches = entries
131
- .map(e => e.doc)
132
- .filter(m => m.active && (
133
- m.toolName.toLowerCase().includes(q) ||
134
- m.description.toLowerCase().includes(q) ||
135
- m.category.toLowerCase().includes(q)
136
- ))
137
- .slice(0, limit);
138
- result.toolsmcp = matches.map(m => ({
139
- id: `${m.serverName}__${m.toolName}`,
140
- full_name: `${m.serverName}__${m.toolName}`,
141
- server_name: m.serverName,
142
- tool_name: m.toolName,
143
- description: m.description,
144
- category: m.category,
145
- active: true,
146
- rank: 0,
147
- }));
302
+ // ─── Pass 1: Search with original query ─────────────────────────
303
+ // `rank` is the raw BM25 score: positive, higher = more relevant.
304
+
305
+ const hits1 = await searchCapabilities(normalizedQuery, { types, k: limit * 4 });
306
+ const totalFirst = await mergeHits(hits1);
307
+
308
+ // ─── Pass 2: Bilingual fallback (ES → EN) ──────────────────────
309
+ // HiveDB stems Spanish but does not translate: "correo" still won't
310
+ // match English-only descriptions ("email") without this dictionary.
311
+
312
+ if (totalFirst < MIN_RESULTS_FOR_BILINGUAL) {
313
+ const englishQuery = translateQueryToEnglish(normalizedQuery);
314
+ if (englishQuery.length > 0) {
315
+ log.info(`[search_knowledge] Bilingual fallback: "${normalizedQuery}" → "${englishQuery}" (first pass: ${totalFirst} results)`);
316
+ const hits2 = await searchCapabilities(englishQuery, { types, k: limit * 4 });
317
+ await mergeHits(hits2);
318
+ }
148
319
  }
149
320
 
150
- result.totalResults = result.tools.length + result.skills.length + result.playbook.length + result.toolsmcp.length;
321
+ result.totalResults = result.tools.length + result.skills.length + result.playbook.length + result.toolsmcp.length + result.agents.length;
151
322
 
152
323
  return { ok: true, ...result };
153
324
  } catch (error) {
@@ -175,7 +346,7 @@ export const notifyTool: Tool = {
175
346
  required: ["message"],
176
347
  },
177
348
  execute: async (params: Record<string, unknown>, config?: any) => {
178
- const { sendToUserChannel } = await import("../../gateway/channel-notify.ts");
349
+ const { sendToUserChannel } = await import("../../gateway/channel-notify");
179
350
  const message = params.message as string;
180
351
  const channel = (config?.configurable?.channel as string) ?? "webchat";
181
352
  const userId = (config?.configurable?.user_id as string) ?? "";
@@ -190,13 +361,6 @@ export const notifyTool: Tool = {
190
361
 
191
362
  // ─── save_note (scratchpad) ──────────────────────────────────────────────────
192
363
 
193
- interface ScratchpadDoc {
194
- threadId: string;
195
- key: string;
196
- value: string;
197
- updatedAt: number;
198
- }
199
-
200
364
  export const saveNoteTool: Tool = {
201
365
  name: "save_note",
202
366
  description: "Save a note to the scratchpad (survives context compression).",
@@ -219,15 +383,12 @@ export const saveNoteTool: Tool = {
219
383
  required: ["key", "value"],
220
384
  },
221
385
  execute: async (params: Record<string, unknown>, config?: any) => {
222
- const db = await getHiveDB();
223
386
  const key = params.key as string;
224
387
  const value = params.value as string;
225
388
  const threadId = (params.thread_id as string) ?? config?.configurable?.thread_id ?? "default";
226
389
 
227
390
  try {
228
- const col = db.collection<ScratchpadDoc>("scratchpad");
229
- await col.put(`${threadId}:${key}`, { threadId, key, value, updatedAt: Date.now() });
230
-
391
+ await saveScratchpadNote(threadId, key, value, "agent");
231
392
  return { ok: true, key, message: "Note saved." };
232
393
  } catch (error) {
233
394
  return {
@@ -262,7 +423,7 @@ export const reportProgressTool: Tool = {
262
423
  required: ["progress", "message"],
263
424
  },
264
425
  execute: async (params: Record<string, unknown>, config?: any) => {
265
- const { sendToUserChannel } = await import("../../gateway/channel-notify.ts");
426
+ const { sendToUserChannel } = await import("../../gateway/channel-notify");
266
427
  const progress = params.progress as number;
267
428
  const message = params.message as string;
268
429
  const taskId = (params.task_id as string) ?? null;
@@ -273,8 +434,16 @@ export const reportProgressTool: Tool = {
273
434
 
274
435
  // Update task progress in DB if task_id provided
275
436
  if (taskId) {
276
- const db = getDb();
277
- db.query(`UPDATE tasks SET progress = ?, updated_at = unixepoch() WHERE id = ?`).run(progress, taskId);
437
+ try {
438
+ const tasksCol = await col<TaskDoc>("tasks");
439
+ const id = /^\d+$/.test(taskId) ? taskId.padStart(15, "0") : taskId;
440
+ const existing = await tasksCol.get(id);
441
+ if (existing) {
442
+ await tasksCol.put(id, { ...existing.doc, progress, updated_at: Date.now() }, { expectedVersion: existing.version });
443
+ }
444
+ } catch {
445
+ // Best-effort — task_id may not correspond to a real task.
446
+ }
278
447
  }
279
448
 
280
449
  // Send real-time update to the user's channel