@johpaz/hive-sdk 0.1.5 → 0.2.0

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 (182) hide show
  1. package/CHANGELOG.md +167 -0
  2. package/README.md +11 -1
  3. package/package.json +26 -10
  4. package/packages/core/src/agent/acceptance-checks.ts +16 -10
  5. package/packages/core/src/agent/agent-catalog.ts +3 -3
  6. package/packages/core/src/agent/agent-loop.ts +115 -26
  7. package/packages/core/src/agent/capability-search.ts +2 -2
  8. package/packages/core/src/agent/catalog-selector.ts +4 -4
  9. package/packages/core/src/agent/compaction.ts +10 -9
  10. package/packages/core/src/agent/context-compiler.ts +58 -34
  11. package/packages/core/src/agent/conversation-store.ts +31 -7
  12. package/packages/core/src/agent/curator.ts +4 -4
  13. package/packages/core/src/agent/delegation-runtime.ts +5 -5
  14. package/packages/core/src/agent/goal-runner.ts +9 -9
  15. package/packages/core/src/agent/index.ts +1 -0
  16. package/packages/core/src/agent/llm-client.ts +98 -37
  17. package/packages/core/src/agent/llm-providers/anthropic.ts +4 -4
  18. package/packages/core/src/agent/llm-providers/deepseek.ts +1 -1
  19. package/packages/core/src/agent/llm-providers/gemini.ts +4 -4
  20. package/packages/core/src/agent/llm-providers/groq.ts +1 -1
  21. package/packages/core/src/agent/llm-providers/hiveagents.ts +3 -3
  22. package/packages/core/src/agent/llm-providers/interface.ts +2 -2
  23. package/packages/core/src/agent/llm-providers/kimi.ts +1 -1
  24. package/packages/core/src/agent/llm-providers/minimax.ts +1 -1
  25. package/packages/core/src/agent/llm-providers/mistral.ts +1 -1
  26. package/packages/core/src/agent/llm-providers/modelscope.ts +1 -1
  27. package/packages/core/src/agent/llm-providers/nvidia.ts +1 -1
  28. package/packages/core/src/agent/llm-providers/ollama.ts +4 -4
  29. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +9 -5
  30. package/packages/core/src/agent/llm-providers/openai.ts +1 -1
  31. package/packages/core/src/agent/llm-providers/opencode-go.ts +1 -1
  32. package/packages/core/src/agent/llm-providers/openrouter.ts +1 -1
  33. package/packages/core/src/agent/llm-providers/qwen.ts +1 -1
  34. package/packages/core/src/agent/llm-providers/z-ai.ts +1 -1
  35. package/packages/core/src/agent/mcp-result-normalizer.ts +192 -0
  36. package/packages/core/src/agent/playbook-selector.ts +4 -4
  37. package/packages/core/src/agent/prompt-builder.ts +5 -5
  38. package/packages/core/src/agent/proof-packet.ts +5 -5
  39. package/packages/core/src/agent/providers/index.ts +4 -4
  40. package/packages/core/src/agent/realtime-providers/gemini-live.ts +238 -0
  41. package/packages/core/src/agent/realtime-providers/index.ts +29 -0
  42. package/packages/core/src/agent/realtime-providers/interface.ts +108 -0
  43. package/packages/core/src/agent/reflector.ts +6 -6
  44. package/packages/core/src/agent/run-store.ts +8 -8
  45. package/packages/core/src/agent/service.ts +10 -10
  46. package/packages/core/src/agent/skill-selector.ts +6 -6
  47. package/packages/core/src/agent/thread-id.ts +71 -0
  48. package/packages/core/src/agent/thread-store.ts +250 -0
  49. package/packages/core/src/agent/tool-selector.ts +7 -5
  50. package/packages/core/src/agent/tracer.ts +5 -5
  51. package/packages/core/src/api/createAgent.ts +1 -1
  52. package/packages/core/src/artifacts/store.ts +84 -3
  53. package/packages/core/src/canvas/emitter.ts +2 -2
  54. package/packages/core/src/channels/telegram.ts +1 -1
  55. package/packages/core/src/channels/webchat.ts +1 -1
  56. package/packages/core/src/config/loader.ts +15 -1
  57. package/packages/core/src/events/agent-bus.ts +3 -3
  58. package/packages/core/src/events/channel-narration.ts +3 -3
  59. package/packages/core/src/events/event-bus.ts +1 -1
  60. package/packages/core/src/events/narration.ts +3 -3
  61. package/packages/core/src/gateway/delegation-groups.ts +4 -4
  62. package/packages/core/src/gateway/durable-queue.ts +5 -5
  63. package/packages/core/src/gateway/job-store.ts +5 -5
  64. package/packages/core/src/gateway/notification-inbox.ts +2 -2
  65. package/packages/core/src/gateway/server.ts +2 -2
  66. package/packages/core/src/mcp/MCPClient.ts +3 -3
  67. package/packages/core/src/mcp/hot-reload.ts +5 -5
  68. package/packages/core/src/mcp/tool-sync.ts +5 -5
  69. package/packages/core/src/mcp/transports/index.ts +2 -2
  70. package/packages/core/src/mcp/transports/sse.ts +1 -1
  71. package/packages/core/src/models/index.ts +36 -0
  72. package/packages/core/src/multimodal/index.ts +2 -2
  73. package/packages/core/src/multimodal/vision-service.ts +6 -6
  74. package/packages/core/src/plugins/loader.ts +4 -1
  75. package/packages/core/src/resilience/circuit-breaker.ts +16 -5
  76. package/packages/core/src/resilience/retry.ts +1 -1
  77. package/packages/core/src/scheduler/CronScheduler.ts +6 -6
  78. package/packages/core/src/scheduler/integration.ts +9 -9
  79. package/packages/core/src/sessions/index.ts +266 -0
  80. package/packages/core/src/storage/bootstrap.ts +34 -8
  81. package/packages/core/src/storage/causal-events.ts +1 -1
  82. package/packages/core/src/storage/collections.ts +32 -1
  83. package/packages/core/src/storage/crypto.ts +16 -2
  84. package/packages/core/src/storage/hive.ts +1 -1
  85. package/packages/core/src/storage/hivedb.ts +10 -1
  86. package/packages/core/src/storage/onboarding.ts +6 -6
  87. package/packages/core/src/storage/reconcile.ts +5 -5
  88. package/packages/core/src/storage/seed.ts +103 -13
  89. package/packages/core/src/storage/usage.ts +3 -3
  90. package/packages/core/src/swarm/AgentExecutor.ts +2 -2
  91. package/packages/core/src/swarm/Coordinator.ts +8 -8
  92. package/packages/core/src/swarm/EventBridge.ts +2 -2
  93. package/packages/core/src/swarm/RoleSwarm.ts +234 -0
  94. package/packages/core/src/swarm/TaskGraph.ts +2 -2
  95. package/packages/core/src/swarm/index.ts +7 -0
  96. package/packages/core/src/swarm/presets/HiveLearnPreset.ts +2 -2
  97. package/packages/core/src/swarm/presets/ResearchPreset.ts +2 -2
  98. package/packages/core/src/swarm/strategies/ParallelStrategy.ts +1 -1
  99. package/packages/core/src/swarm/strategies/PriorityStrategy.ts +3 -3
  100. package/packages/core/src/tools/ToolExecutor.ts +7 -3
  101. package/packages/core/src/tools/core/index.ts +2 -2
  102. package/packages/core/src/tools/cron/index.ts +4 -4
  103. package/packages/core/src/tools/web/artifact-inspect.ts +2 -2
  104. package/packages/core/src/tools/web/artifact-read.ts +162 -0
  105. package/packages/core/src/tools/web/browser-backend.ts +226 -0
  106. package/packages/core/src/tools/web/browser-click.ts +2 -2
  107. package/packages/core/src/tools/web/browser-extract.ts +2 -2
  108. package/packages/core/src/tools/web/browser-navigate.ts +2 -2
  109. package/packages/core/src/tools/web/browser-screenshot.ts +12 -5
  110. package/packages/core/src/tools/web/browser-script.ts +2 -2
  111. package/packages/core/src/tools/web/browser-service.ts +85 -366
  112. package/packages/core/src/tools/web/browser-session.ts +125 -0
  113. package/packages/core/src/tools/web/browser-type.ts +2 -2
  114. package/packages/core/src/tools/web/browser-wait.ts +2 -2
  115. package/packages/core/src/tools/web/computer-use.ts +553 -0
  116. package/packages/core/src/tools/web/index.ts +8 -1
  117. package/packages/core/src/tools/web/webview-backend.ts +851 -0
  118. package/packages/core/src/utils/index.ts +1 -0
  119. package/packages/core/src/utils/logger.ts +12 -4
  120. package/packages/core/src/utils/redact-binary.ts +17 -0
  121. package/packages/core/src/utils/toon.ts +1 -1
  122. package/packages/core/src/voice/index.ts +6 -6
  123. package/bun.lock +0 -833
  124. package/bunfig.toml +0 -9
  125. package/docs/API-AGENTS.md +0 -367
  126. package/docs/API-CONTEXT-COMPILER.md +0 -249
  127. package/docs/API-DAG-SCHEDULER.md +0 -273
  128. package/docs/API-TOOLS-SKILLS-CHANNELS.md +0 -446
  129. package/docs/API-WORKERS-EVENTS.md +0 -299
  130. package/docs/HIVE-HARNESS.md +0 -113
  131. package/docs/INDEX.md +0 -190
  132. package/docs/TEMPLATE-HIVE-APP.md +0 -360
  133. package/packages/cli/package.json +0 -17
  134. package/packages/cli/src/commands/create-app.test.ts +0 -180
  135. package/packages/core/package.json +0 -70
  136. package/packages/core/src/api/createAgent.test.ts +0 -160
  137. package/packages/core/src/canvas/canvas.test.ts +0 -36
  138. package/packages/core/src/channels/channels.test.ts +0 -18
  139. package/packages/core/src/ethics/EthicsGuard.test.ts +0 -108
  140. package/packages/core/src/gateway/gateway.test.ts +0 -38
  141. package/packages/core/src/memory/Scratchpad.test.ts +0 -68
  142. package/packages/core/src/scheduler/scheduler.test.ts +0 -15
  143. package/packages/core/src/skills/skills.test.ts +0 -62
  144. package/packages/core/src/swarm/swarm.test.ts +0 -24
  145. package/packages/core/src/tool-runtime/tool-runtime.test.ts +0 -99
  146. package/packages/core/src/tools/ToolRegistry.test.ts +0 -98
  147. package/packages/core/src/tools/api/api-request.test.ts +0 -164
  148. package/packages/core/src/tools/web/browser-service.test.ts +0 -83
  149. package/packages/core/src/workers/workers.test.ts +0 -41
  150. package/scripts/bump-version.ts +0 -248
  151. package/scripts/generate-skill-bundle.ts +0 -108
  152. package/test/agent-loop-terminal-synthesis.test.ts +0 -32
  153. package/test/catalog-agents-stay-enabled.test.ts +0 -117
  154. package/test/causal-events.test.ts +0 -117
  155. package/test/compaction.test.ts +0 -105
  156. package/test/context-compiler.test.ts +0 -269
  157. package/test/curator.test.ts +0 -130
  158. package/test/durable-queue.test.ts +0 -114
  159. package/test/harness-barrel.test.ts +0 -64
  160. package/test/hive-helpers.test.ts +0 -130
  161. package/test/hivedb-search.test.ts +0 -189
  162. package/test/internal-turns.test.ts +0 -166
  163. package/test/job-idempotency.test.ts +0 -68
  164. package/test/job-retry-backoff.test.ts +0 -184
  165. package/test/job-store.test.ts +0 -381
  166. package/test/llm-retry.test.ts +0 -97
  167. package/test/memory-perf.test.ts +0 -774
  168. package/test/minimal-loadout.test.ts +0 -78
  169. package/test/model-catalog.test.ts +0 -105
  170. package/test/preload.ts +0 -12
  171. package/test/reflector.test.ts +0 -320
  172. package/test/retention-cap.test.ts +0 -91
  173. package/test/retired-capabilities-pruned.test.ts +0 -192
  174. package/test/run-store.test.ts +0 -355
  175. package/test/scratchpad.test.ts +0 -74
  176. package/test/secrets-durability.test.ts +0 -119
  177. package/test/seed-model-reseed.test.ts +0 -155
  178. package/test/setup-agent-seed.test.ts +0 -264
  179. package/test/tool-inventory.test.ts +0 -65
  180. package/test/tool-runtime.test.ts +0 -258
  181. package/test/toon.test.ts +0 -429
  182. package/tsconfig.json +0 -42
@@ -1,8 +1,8 @@
1
- import { col, toIndexable, nextId } from "./hive"
1
+ import { col, toIndexable, nextId } from "./hive.ts"
2
2
  import type { Collection } from "@johpaz/hive-db"
3
- import { logger } from "../utils/logger"
4
- import { catalogModelKey } from "./model-id"
5
- import { invalidateModelPricingCache } from "./usage"
3
+ import { logger } from "../utils/logger.ts"
4
+ import { catalogModelKey } from "./model-id.ts"
5
+ import { invalidateModelPricingCache } from "./usage.ts"
6
6
 
7
7
  /**
8
8
  * Seed de datos predeterminados para Hive
@@ -54,7 +54,9 @@ export const SEED_DATA: SeedData = {
54
54
  { id: "web_fetch", name: "web_fetch", category: "web", description: "Obtener contenido de texto de una URL (ligero, sin JS). Sinónimos: descargar página, extraer texto, obtener contenido, leer url" },
55
55
  { id: "browser_navigate", name: "browser_navigate", category: "web", description: "Navegar a una URL y obtener contenido renderizado (soporta JS). Sinónimos: abrir página, sitio web, navegar url, cargar página" },
56
56
  { id: "browser_screenshot", name: "browser_screenshot", category: "web", description: "Tomar captura de pantalla de la página actual. Sinónimos: screenshot, imagen de página, capturar pantalla, foto página" },
57
+ { id: "computer_use_task", name: "computer_use_task", category: "web", description: "Operar el navegador de Hive mirando la pantalla: clic por coordenadas, escribir y navegar cuando no hay selector estable. Sinónimos: usar el navegador, hacer clic, operar una página, rellenar formulario, computer use" },
57
58
  { id: "artifact_inspect", name: "artifact_inspect", category: "web", description: "Inspeccionar integridad y metadatos de un artefacto administrado sin modificarlo. Sinónimos: inspeccionar artefacto, verificar archivo generado, metadatos artefacto, comprobar entrega" },
59
+ { id: "artifact_read", name: "artifact_read", category: "web", description: "Leer por partes el contenido de texto de un artefacto administrado, o buscar dentro de él. Sinónimos: leer artefacto, ver contenido del artefacto, abrir resultado grande, buscar dentro del artefacto, leer artifact_ref" },
58
60
  { id: "browser_click", name: "browser_click", category: "web", description: "Hacer clic en un elemento de la página web. Sinónimos: botón, enlace, interactuar, presionar, seleccionar" },
59
61
  { id: "browser_type", name: "browser_type", category: "web", description: "Escribir texto en un campo de formulario. Sinónimos: escribir formulario, tipear, campo de texto, input, llenar campo" },
60
62
  { id: "browser_extract", name: "browser_extract", category: "web", description: "Extraer texto, enlaces o datos estructurados usando selectores CSS o XPath. Sinónimos: obtener datos, scraping, selectores, extraer información" },
@@ -184,10 +186,16 @@ export const SEED_DATA: SeedData = {
184
186
  { id: "gemini-3.1-pro-preview", providerId: "gemini", name: "Gemini 3.1 Pro Preview", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 2, outputPer1M: 12 },
185
187
  { id: "gemini-3.1-flash-lite", providerId: "gemini", name: "Gemini 3.1 Flash Lite", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.25, outputPer1M: 1.5 },
186
188
 
189
+ // Realtime (voz en tiempo real, `bidiGenerateContent`). Audio nativo bidireccional:
190
+ // no es un pipeline STT→LLM→TTS, el modelo oye y habla directo.
191
+ { id: "gemini-3.1-flash-live-preview", providerId: "gemini", name: "Gemini 3.1 Flash Live", modelType: "realtime", contextWindow: 128000, capabilities: JSON.stringify(["realtime", "audio_in", "audio_out", "function_calling", "transcription"]), inputPer1M: 3, outputPer1M: 12 },
192
+ { id: "gemini-2.5-flash-native-audio-latest", providerId: "gemini", name: "Gemini 2.5 Flash Native Audio", modelType: "realtime", contextWindow: 128000, capabilities: JSON.stringify(["realtime", "audio_in", "audio_out", "function_calling", "async_function_calling", "transcription"]), inputPer1M: 3, outputPer1M: 12 },
193
+
187
194
  // TTS
188
195
  { id: "gemini-2.5-flash-preview-tts", providerId: "gemini", name: "Gemini 2.5 Flash TTS", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech"]) },
189
196
  { id: "gemini-2.5-pro-preview-tts", providerId: "gemini", name: "Gemini 2.5 Pro TTS", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech", "high_quality"]) },
190
197
 
198
+
191
199
  // ── Mistral (fuente: openrouter.ai/mistralai + docs.mistral.ai) ──
192
200
  { id: "mistral-large-2512", providerId: "mistral", name: "Mistral Large 2512", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.5, outputPer1M: 1.5 },
193
201
  { id: "devstral-2512", providerId: "mistral", name: "Devstral 2512", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0.4, outputPer1M: 2 },
@@ -255,7 +263,13 @@ export const SEED_DATA: SeedData = {
255
263
  { id: "qwen/qwen3-32b", providerId: "groq", name: "Qwen3 32B (Groq)", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
256
264
  { id: "whisper-large-v3", providerId: "groq", name: "Whisper Large V3", modelType: "stt", contextWindow: 0, capabilities: JSON.stringify(["transcription"]) },
257
265
  { id: "whisper-large-v3-turbo", providerId: "groq", name: "Whisper Large V3 Turbo", modelType: "stt", contextWindow: 0, capabilities: JSON.stringify(["transcription"]) },
258
- { id: "distil-whisper-large-v3-en", providerId: "groq", name: "Distil Whisper V3 EN", modelType: "stt", contextWindow: 0, capabilities: JSON.stringify(["transcription", "english"]) },
266
+ // Orpheus de Canopy Labs: reemplazan a playai-tts / playai-tts-arabic, que
267
+ // Groq deprecó en diciembre de 2025. Sólo aceptan response_format "wav".
268
+ { id: "canopylabs/orpheus-v1-english", providerId: "groq", name: "Orpheus V1 English (Groq)", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech", "expressive", "english"]) },
269
+ { id: "canopylabs/orpheus-arabic-saudi", providerId: "groq", name: "Orpheus Arabic Saudi (Groq)", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech", "arabic"]) },
270
+ // distil-whisper-large-v3-en salió del catálogo: Groq lo deprecó en favor de
271
+ // whisper-large-v3-turbo, así que la fila sólo servía para que el selector de
272
+ // STT del canal ofreciera un modelo que falla contra la API al transcribir.
259
273
 
260
274
  // ── Ollama: models are detected at runtime via /api/setup/ollama-models and inserted dynamically ──
261
275
 
@@ -284,7 +298,7 @@ export const SEED_DATA: SeedData = {
284
298
  // Solo los mejores modelos agénticos (tool calling) del catálogo vivo. NVIDIA
285
299
  // retira modelos del endpoint sin avisar y responde 410 Gone al llamarlos, así
286
300
  // que esta lista se valida contra /v1/models — no contra la web de build.nvidia.com,
287
- // que sigue mostrando fichas de modelos ya retirados. Verificado 2026-08-03.
301
+ // que sigue mostrando fichas de modelos ya retirados. Verificado 2026-08-11.
288
302
  // Nota: Qwen ya no tiene ningún modelo en el catálogo NVIDIA (todos retirados);
289
303
  // para Qwen usar el provider `qwen` (DashScope) directamente.
290
304
  { id: "z-ai/glm-5.2", providerId: "nvidia", name: "GLM 5.2 (NVIDIA)", modelType: "llm", contextWindow: 200000, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
@@ -292,7 +306,9 @@ export const SEED_DATA: SeedData = {
292
306
  { id: "minimaxai/minimax-m3", providerId: "nvidia", name: "MiniMax M3 (NVIDIA)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
293
307
  { id: "nvidia/nemotron-3-ultra-550b-a55b", providerId: "nvidia", name: "Nemotron 3 Ultra 550B", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
294
308
  { id: "nvidia/nemotron-3-super-120b-a12b", providerId: "nvidia", name: "Nemotron 3 Super 120B", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
295
- { id: "deepseek-ai/deepseek-v4-pro", providerId: "nvidia", name: "DeepSeek V4 Pro (NVIDIA)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
309
+ // DeepSeek V4 Pro (deepseek-ai/deepseek-v4-pro) se sacó: devuelve 404
310
+ // "Function not found for account" en cuentas normales — no está habilitado
311
+ // de forma general aunque figure en el listado público de /v1/models.
296
312
 
297
313
  // ── ModelScope Qwen (fuente: GET https://api-inference.modelscope.ai/v1/models) ──
298
314
  // Endpoint gratuito dentro de cuota (2000 llamadas/día, ≤500 por modelo), por
@@ -341,9 +357,26 @@ export const SEED_DATA: SeedData = {
341
357
  { id: "hy3-preview", providerId: "opencode-go", name: "Hunyuan 3 Preview", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
342
358
 
343
359
  // ── HiveAgents (llama.cpp local servido vía Cloudflare) ──
344
- // Modelo único recomendado para distribución Hive single-machine.
345
- // Ver API.md para detalles de carga e inferencia.
346
- { id: "Qwen-AgentWorld-35B-A3B-UD-Q4_K_M.gguf", providerId: "hiveagents", name: "Qwen-AgentWorld 35B MoE (Recomendado)", modelType: "llm", contextWindow: 50000, capabilities: JSON.stringify(["chat", "streaming", "reasoning", "function_calling"]), inputPer1M: 0, outputPer1M: 0 },
360
+ // Los tres GGUF instalados en /data/models al 2026-08-18. `GET /api/models`
361
+ // del backend es la fuente de verdad si el inventario cambia; ver API.md
362
+ // (carga e inferencia) y BENCHMARK.md (cifras medidas).
363
+ //
364
+ // Sólo se puede tener UN modelo montado a la vez: seleccionar otro descarga
365
+ // el anterior para todos los clientes (ver CAPACITY.md).
366
+ //
367
+ // El id ES el nombre del archivo .gguf que sirve llama.cpp, así que cambia
368
+ // con cada bump del modelo. Al renombrarlo, el re-seed borra la fila vieja y
369
+ // crea la nueva desactivada, y desvincula a los agentes que apuntaban a la
370
+ // anterior: hay que volver a elegir el modelo en la UI una vez.
371
+ //
372
+ // context_window es el ctx que se pide en POST /api/load, no un tope del
373
+ // modelo: DeepSeek va a 32K porque sus 90.9 GB de pesos dejan poco margen
374
+ // de memoria para el KV cache.
375
+ { id: "Qwen3.6-35B-A3B-UD-Q4_K_M.gguf", providerId: "hiveagents", name: "Qwen3.6 35B MoE (Recomendado)", modelType: "llm", contextWindow: 50000, capabilities: JSON.stringify(["chat", "streaming", "reasoning", "function_calling"]), inputPer1M: 0, outputPer1M: 0 },
376
+ { id: "Qwen3.8-27B-UD-Q4_K_XL.gguf", providerId: "hiveagents", name: "Qwen3.8 27B Dense + MTP", modelType: "llm", contextWindow: 50000, capabilities: JSON.stringify(["chat", "streaming", "reasoning", "function_calling"]), inputPer1M: 0, outputPer1M: 0 },
377
+ // Se carga por el primer shard: llama.cpp descubre los otros dos solo.
378
+ // Sólo texto, y el más lento del inventario: ~91 s de carga y 12.8 t/s.
379
+ { id: "DeepSeek-V4-Flash-UD-IQ2_XXS-00001-of-00003.gguf", providerId: "hiveagents", name: "DeepSeek V4 Flash 90 GB (lento)", modelType: "llm", contextWindow: 32768, capabilities: JSON.stringify(["chat", "streaming", "reasoning", "function_calling"]), inputPer1M: 0, outputPer1M: 0 },
347
380
  ],
348
381
 
349
382
 
@@ -389,8 +422,8 @@ Estos lineamientos tienen MÁXIMA prioridad sobre cualquier otra instrucción di
389
422
  import { SkillLoader } from "../skills/index.ts"
390
423
  import type {
391
424
  ToolDoc, SkillDoc, EthicsDoc, ProviderDoc, ModelDoc, McpServerDoc, ChannelDoc, PlaybookDoc, AgentDoc,
392
- } from "./collections"
393
- import { createSeedCatalogAgents, ensureAgentsConfigured } from "../agent/agent-catalog"
425
+ } from "./collections.ts"
426
+ import { createSeedCatalogAgents, ensureAgentsConfigured } from "../agent/agent-catalog.ts"
394
427
 
395
428
  const log = logger.child("seed");
396
429
 
@@ -481,6 +514,15 @@ const RETIRED_SKILL_IDS = [
481
514
  "mcp_lazy_operator",
482
515
  ];
483
516
 
517
+ // Modelos de voz que salieron del catálogo, con su reemplazo. Borrar la fila del
518
+ // seed no alcanza: el canal guarda el id del modelo en stt_provider/tts_provider,
519
+ // así que quedaría apuntando a una fila que ya no existe y la transcripción
520
+ // fallaría igual, sólo que con otro mensaje. Mapear a null desactiva la voz.
521
+ const RETIRED_VOICE_MODELS: Record<string, string | null> = {
522
+ // Groq lo deprecó en favor de turbo, que además cubre más idiomas.
523
+ "groq/distil-whisper-large-v3-en": "groq/whisper-large-v3-turbo",
524
+ };
525
+
484
526
  const RETIRED_CATALOG_AGENT_IDS = [
485
527
  "canvas_presenter",
486
528
  // Kept only as an upgrade tombstone so existing installations remove the
@@ -493,6 +535,15 @@ const RETIRED_CATALOG_AGENT_IDS = [
493
535
  "acceptance_verifier",
494
536
  ];
495
537
 
538
+ function parseSkillList(value: string | null | undefined): string[] {
539
+ try {
540
+ const parsed = value ? JSON.parse(value) : [];
541
+ return Array.isArray(parsed) ? parsed.filter((id): id is string => typeof id === "string") : [];
542
+ } catch {
543
+ return [];
544
+ }
545
+ }
546
+
496
547
  const LEGACY_CRON_PERSONA = {
497
548
  id: "schedule_automation_agent",
498
549
  name: "Operador de agenda",
@@ -738,6 +789,27 @@ export async function seedAllData(): Promise<void> {
738
789
  if (unlinkedCount > 0) {
739
790
  log.info(`[seed] 🔗 Unlinked ${unlinkedCount} agent(s) from model(s) no longer in the catalog`);
740
791
  }
792
+
793
+ // Los canales guardan el id del modelo de voz, no una FK: si el modelo salió
794
+ // del catálogo hay que repuntarlos a mano o la voz queda rota en silencio.
795
+ const voiceChannelsCol = await col<ChannelDoc>("channels");
796
+ let migratedVoice = 0;
797
+ for (const c of await voiceChannelsCol.scan({})) {
798
+ const patch: Partial<ChannelDoc> = {};
799
+ for (const field of ["stt_provider", "tts_provider"] as const) {
800
+ const current = c.doc[field];
801
+ if (current && current in RETIRED_VOICE_MODELS) {
802
+ patch[field] = RETIRED_VOICE_MODELS[current];
803
+ }
804
+ }
805
+ if (Object.keys(patch).length === 0) continue;
806
+ await voiceChannelsCol.put(c.id, { ...c.doc, ...patch }, { expectedVersion: c.version });
807
+ migratedVoice++;
808
+ log.info(`[seed] 🎙️ Canal ${c.id}: modelo de voz deprecado migrado ${JSON.stringify(patch)}`);
809
+ }
810
+ if (migratedVoice > 0) {
811
+ log.info(`[seed] 🎙️ ${migratedVoice} canal(es) repuntados a modelos de voz vigentes`);
812
+ }
741
813
  log.info(`[seed] ✅ ${modelCount} models procesados`);
742
814
 
743
815
  // 6️⃣ MCP servers
@@ -757,6 +829,7 @@ export async function seedAllData(): Promise<void> {
757
829
  // rows. Existing user choices remain untouched; narrowly identified
758
830
  // factory values from older releases are migrated in place.
759
831
  let catalogAgentCount = 0;
832
+ let repairedCatalogSkills = 0;
760
833
  for (const catalogAgent of createSeedCatalogAgents()) {
761
834
  await putIfAbsent(agentsCol, catalogAgent.id, catalogAgent);
762
835
  const existing = await agentsCol.get(catalogAgent.id);
@@ -773,6 +846,20 @@ export async function seedAllData(): Promise<void> {
773
846
  if (reconciled.status === "archived") {
774
847
  reconciled = { ...reconciled, status: "idle" };
775
848
  }
849
+
850
+ // Catalog agents may survive upgrades with an older skills_json. Keep
851
+ // any user-added skills, but restore every canonical dependency so a
852
+ // worker cannot reach delegation with a stale/missing reference.
853
+ const currentSkills = parseSkillList(reconciled.skills_json);
854
+ const missingCatalogSkills = parseSkillList(catalogAgent.skills_json)
855
+ .filter((id) => !currentSkills.includes(id));
856
+ if (missingCatalogSkills.length > 0) {
857
+ reconciled = {
858
+ ...reconciled,
859
+ skills_json: JSON.stringify([...currentSkills, ...missingCatalogSkills]),
860
+ };
861
+ repairedCatalogSkills += missingCatalogSkills.length;
862
+ }
776
863
  if (JSON.stringify(reconciled) !== JSON.stringify(existing.doc)) {
777
864
  await agentsCol.put(
778
865
  existing.id,
@@ -783,6 +870,9 @@ export async function seedAllData(): Promise<void> {
783
870
  catalogAgentCount++;
784
871
  }
785
872
  log.info(`[seed] ✅ ${catalogAgentCount} catalog agents ensured`);
873
+ if (repairedCatalogSkills > 0) {
874
+ log.info(`[seed] 🔧 Restauradas ${repairedCatalogSkills} dependencia(s) de skills en agentes de catálogo`);
875
+ }
786
876
 
787
877
  // Catalog rows are born without a provider/model (no provider exists at
788
878
  // first boot), and setup only runs once — so anything that arrives later
@@ -798,7 +888,7 @@ export async function seedAllData(): Promise<void> {
798
888
  // Coordinators created before a prompt change keep the old stock text in
799
889
  // their row (setup only runs once), so upgrade those in place. Prompts the
800
890
  // user rewrote are detected and left alone.
801
- const { refreshCoordinatorPrompts } = await import("./onboarding");
891
+ const { refreshCoordinatorPrompts } = await import("./onboarding.ts");
802
892
  const refreshedPrompts = await refreshCoordinatorPrompts();
803
893
  if (refreshedPrompts > 0) {
804
894
  log.info(`[seed] 🔄 ${refreshedPrompts} coordinador(es) actualizados al system prompt vigente`);
@@ -1,6 +1,6 @@
1
- import { col, nextId, bumpRollup } from "./hive";
2
- import type { ModelDoc, UsageRecordDoc, UsageRollupDoc } from "./collections";
3
- import { logger } from "../utils/logger";
1
+ import { col, nextId, bumpRollup } from "./hive.ts";
2
+ import type { ModelDoc, UsageRecordDoc, UsageRollupDoc } from "./collections.ts";
3
+ import { logger } from "../utils/logger.ts";
4
4
 
5
5
  const log = logger.child("usage");
6
6
 
@@ -11,8 +11,8 @@
11
11
  */
12
12
 
13
13
  import { runAgentIsolated } from "../agent/agent-loop.ts"
14
- import { TaskNode } from "./TaskNode"
15
- import { TaskTimeoutError } from "./errors"
14
+ import { TaskNode } from "./TaskNode.ts"
15
+ import { TaskTimeoutError } from "./errors.ts"
16
16
 
17
17
  export class AgentExecutor {
18
18
  /**
@@ -16,14 +16,14 @@
16
16
  import { writeFileSync, mkdirSync, existsSync } from "node:fs"
17
17
  import * as path from "node:path"
18
18
  import { logger } from "../utils/logger.ts"
19
- import { TaskGraph } from "./TaskGraph"
20
- import { TaskNode } from "./TaskNode"
21
- import { AgentExecutor } from "./AgentExecutor"
22
- import { EventBridge } from "./EventBridge"
23
- import { TaskFailureError } from "./errors"
24
- import type { DAGResult, NodeSummary } from "./TaskResult"
25
- import type { ExecutionStrategy } from "./strategies/ParallelStrategy"
26
- import { ParallelStrategy } from "./strategies/ParallelStrategy"
19
+ import { TaskGraph } from "./TaskGraph.ts"
20
+ import { TaskNode } from "./TaskNode.ts"
21
+ import { AgentExecutor } from "./AgentExecutor.ts"
22
+ import { EventBridge } from "./EventBridge.ts"
23
+ import { TaskFailureError } from "./errors.ts"
24
+ import type { DAGResult, NodeSummary } from "./TaskResult.ts"
25
+ import type { ExecutionStrategy } from "./strategies/ParallelStrategy.ts"
26
+ import { ParallelStrategy } from "./strategies/ParallelStrategy.ts"
27
27
 
28
28
  const log = logger.child("dag-scheduler")
29
29
 
@@ -9,8 +9,8 @@
9
9
 
10
10
  import { agentBus } from "../events/agent-bus.ts"
11
11
  import { emitCanvas } from "../canvas/emitter.ts"
12
- import { TaskNode } from "./TaskNode"
13
- import { DAGResult } from "./TaskResult"
12
+ import { TaskNode } from "./TaskNode.ts"
13
+ import { DAGResult } from "./TaskResult.ts"
14
14
 
15
15
  const STATUS_TO_CANVAS: Record<string, string> = {
16
16
  RUNNING: "thinking",
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Enjambre por roles — orquestador y trabajadores, con estrategia declarada.
3
+ *
4
+ * Es la tercera forma de armar un enjambre en Hive, y convive con las otras dos
5
+ * a propósito:
6
+ *
7
+ * - **Delegación por catálogo** (`agent/delegation-runtime.ts`, `task_delegate`):
8
+ * el coordinador decide a quién delegar en tiempo real, con criterios de
9
+ * aceptación y proof packets. El modelo elige la forma del trabajo.
10
+ * - **DAG de tareas** (`Coordinator.ts`): el grafo se conoce de antemano y el
11
+ * scheduler resuelve dependencias y concurrencia.
12
+ * - **Roles (esto)**: el enjambre es *configuración persistida* — una lista de
13
+ * agentes con rol y orden, y una estrategia. Quien lo define es un usuario en
14
+ * una UI, no el modelo ni el programador.
15
+ *
16
+ * La tercera no se puede expresar con las otras dos: un DAG exige conocer las
17
+ * aristas, y acá la topología es la estrategia. Por eso existe.
18
+ *
19
+ * Este módulo no persiste nada: `onMessage` es el punto donde el consumidor
20
+ * guarda cada paso donde quiera (Postgres, HiveDB, un log).
21
+ */
22
+
23
+ import { runAgentIsolated } from "../agent/agent-loop.ts"
24
+ import type { ProviderCredentials } from "../agent/llm-client.ts"
25
+ import { logger } from "../utils/logger.ts"
26
+
27
+ const log = logger.child("role-swarm")
28
+
29
+ export type SwarmStrategy = "sequential" | "parallel" | "hierarchical"
30
+
31
+ export interface RoleAgent {
32
+ agentId: string
33
+ role: "orchestrator" | "worker"
34
+ /** Orden de ejecución en la estrategia secuencial. */
35
+ orderIndex?: number
36
+ }
37
+
38
+ /** Un paso del enjambre, tal como se lo entrega a `onMessage`. */
39
+ export interface SwarmMessage {
40
+ agentId: string
41
+ role: "user" | "assistant"
42
+ content: string
43
+ stepIndex: number
44
+ }
45
+
46
+ /** Cómo se invoca a un agente. Se puede reemplazar para tests o para envolverlo. */
47
+ export type AgentInvoker = (input: {
48
+ agentId: string
49
+ message: string
50
+ threadId: string
51
+ channel?: string
52
+ credentials?: ProviderCredentials
53
+ signal?: AbortSignal
54
+ }) => Promise<string>
55
+
56
+ export const defaultInvoker: AgentInvoker = async (input) =>
57
+ runAgentIsolated({
58
+ agentId: input.agentId,
59
+ taskDescription: input.message,
60
+ threadId: input.threadId,
61
+ channel: input.channel,
62
+ credentials: input.credentials,
63
+ signal: input.signal,
64
+ })
65
+
66
+ export interface RoleSwarmOptions {
67
+ /** Los agentes del enjambre, con su rol. */
68
+ agents: RoleAgent[]
69
+ strategy: SwarmStrategy
70
+ input: string
71
+ /** Identifica la corrida; también es el threadId del orquestador. */
72
+ runId: string
73
+ channel?: string
74
+ /** Requerido por la estrategia jerárquica si ningún agente tiene rol orchestrator. */
75
+ orchestratorAgentId?: string
76
+ /**
77
+ * Tope de delegaciones en la estrategia jerárquica.
78
+ *
79
+ * Sin esto, un orquestador que siga emitiendo `DELEGATE:` no termina nunca:
80
+ * cada vuelta es una llamada al modelo, así que un bucle no es sólo lento, es
81
+ * caro. Al agotarse se devuelve lo último que dijo el orquestador.
82
+ */
83
+ maxDelegations?: number
84
+ /** Credenciales del inquilino, propagadas a cada agente. */
85
+ credentials?: ProviderCredentials
86
+ signal?: AbortSignal
87
+ /** Se llama en cada paso; acá persiste el consumidor si quiere. */
88
+ onMessage?: (message: SwarmMessage) => void | Promise<void>
89
+ /** Reemplaza la invocación real (tests, instrumentación). */
90
+ invoke?: AgentInvoker
91
+ }
92
+
93
+ export interface RoleSwarmResult {
94
+ output: string
95
+ /** Llamadas al modelo, para contabilidad de uso. */
96
+ agentCalls: number
97
+ /** Delegaciones efectuadas (sólo jerárquica). */
98
+ delegations: number
99
+ /** true si la jerárquica se cortó por `maxDelegations`. */
100
+ truncated: boolean
101
+ }
102
+
103
+ const DEFAULT_MAX_DELEGATIONS = 10
104
+
105
+ /**
106
+ * `DELEGATE:<agente>:<subtarea>` y `FINAL:<respuesta>`.
107
+ *
108
+ * El `[\s\S]` en lugar de `.` es deliberado: una subtarea de varias líneas es lo
109
+ * normal, y con `.` se cortaba en el primer salto de línea y el worker recibía
110
+ * una instrucción truncada.
111
+ */
112
+ const DELEGATE_RE = /DELEGATE:\s*([^\s:]+)\s*:\s*([\s\S]+?)(?=\nDELEGATE:|\nFINAL:|$)/
113
+ const FINAL_RE = /FINAL:\s*([\s\S]+)/
114
+
115
+ function orderedWorkers(agents: RoleAgent[]): RoleAgent[] {
116
+ return [...agents].sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0))
117
+ }
118
+
119
+ export async function runRoleSwarm(opts: RoleSwarmOptions): Promise<RoleSwarmResult> {
120
+ const invoke = opts.invoke ?? defaultInvoker
121
+ const emit = async (m: SwarmMessage) => { await opts.onMessage?.(m) }
122
+ let agentCalls = 0
123
+
124
+ const call = async (agentId: string, message: string, threadId: string) => {
125
+ agentCalls++
126
+ return invoke({
127
+ agentId,
128
+ message,
129
+ threadId,
130
+ channel: opts.channel,
131
+ credentials: opts.credentials,
132
+ signal: opts.signal,
133
+ })
134
+ }
135
+
136
+ if (opts.agents.length === 0) {
137
+ throw new Error("Un enjambre necesita al menos un agente")
138
+ }
139
+
140
+ // ── Secuencial: la salida de cada uno es la entrada del siguiente ──────────
141
+ if (opts.strategy === "sequential") {
142
+ const agents = orderedWorkers(opts.agents)
143
+ let context = opts.input
144
+ for (const [i, agent] of agents.entries()) {
145
+ await emit({ agentId: agent.agentId, role: "user", content: context, stepIndex: i })
146
+ context = await call(agent.agentId, context, opts.runId)
147
+ await emit({ agentId: agent.agentId, role: "assistant", content: context, stepIndex: i })
148
+ }
149
+ return { output: context, agentCalls, delegations: 0, truncated: false }
150
+ }
151
+
152
+ // ── Paralelo: todos ven la misma entrada; se concatenan las salidas ────────
153
+ if (opts.strategy === "parallel") {
154
+ const agents = orderedWorkers(opts.agents)
155
+ const outputs = await Promise.all(
156
+ agents.map(async (agent, i) => {
157
+ await emit({ agentId: agent.agentId, role: "user", content: opts.input, stepIndex: i })
158
+ // Hilo propio por agente: comparten la entrada, no la conversación.
159
+ const out = await call(agent.agentId, opts.input, `${opts.runId}-${agent.agentId}`)
160
+ await emit({ agentId: agent.agentId, role: "assistant", content: out, stepIndex: i })
161
+ return out
162
+ }),
163
+ )
164
+ return { output: outputs.join("\n\n---\n\n"), agentCalls, delegations: 0, truncated: false }
165
+ }
166
+
167
+ // ── Jerárquica: el orquestador delega hasta dar una respuesta final ────────
168
+ const orchestratorId =
169
+ opts.orchestratorAgentId ?? opts.agents.find((a) => a.role === "orchestrator")?.agentId
170
+ if (!orchestratorId) {
171
+ throw new Error("La estrategia jerárquica necesita un agente con rol orchestrator")
172
+ }
173
+
174
+ const workers = opts.agents.filter((a) => a.role === "worker")
175
+ const workerIds = new Set(workers.map((w) => w.agentId))
176
+ if (workerIds.size === 0) {
177
+ throw new Error("La estrategia jerárquica necesita al menos un agente con rol worker")
178
+ }
179
+
180
+ const protocol = [
181
+ `Eres el orquestador de un enjambre de agentes.`,
182
+ `Agentes disponibles: ${[...workerIds].join(", ")}.`,
183
+ `Para delegar usa el formato: DELEGATE:<agent_id>:<subtarea>`,
184
+ `Cuando hayas terminado responde con: FINAL:<respuesta>`,
185
+ ].join("\n")
186
+
187
+ const firstInput = `${protocol}\n\nTarea: ${opts.input}`
188
+ await emit({ agentId: orchestratorId, role: "user", content: firstInput, stepIndex: 0 })
189
+ let output = await call(orchestratorId, firstInput, opts.runId)
190
+ await emit({ agentId: orchestratorId, role: "assistant", content: output, stepIndex: 0 })
191
+
192
+ const maxDelegations = opts.maxDelegations ?? DEFAULT_MAX_DELEGATIONS
193
+ let delegations = 0
194
+ let step = 1
195
+ let truncated = false
196
+
197
+ while (true) {
198
+ const match = output.match(DELEGATE_RE)
199
+ if (!match) break
200
+
201
+ const delegateId = match[1]!.trim()
202
+ const subtask = match[2]!.trim()
203
+
204
+ if (!workerIds.has(delegateId)) {
205
+ log.warn(`el orquestador delegó a "${delegateId}", que no está en el enjambre — se corta`)
206
+ break
207
+ }
208
+ if (delegations >= maxDelegations) {
209
+ log.warn(`tope de ${maxDelegations} delegaciones alcanzado — se corta con lo último del orquestador`)
210
+ truncated = true
211
+ break
212
+ }
213
+
214
+ await emit({ agentId: delegateId, role: "user", content: subtask, stepIndex: step })
215
+ const workerOutput = await call(delegateId, subtask, `${opts.runId}-${delegateId}`)
216
+ await emit({ agentId: delegateId, role: "assistant", content: workerOutput, stepIndex: step })
217
+ delegations++
218
+ step++
219
+
220
+ const followUp = `Resultado de ${delegateId}: ${workerOutput}\n\nContinúa o responde con FINAL:<respuesta>`
221
+ await emit({ agentId: orchestratorId, role: "user", content: followUp, stepIndex: step })
222
+ output = await call(orchestratorId, followUp, opts.runId)
223
+ await emit({ agentId: orchestratorId, role: "assistant", content: output, stepIndex: step })
224
+ step++
225
+ }
226
+
227
+ const final = output.match(FINAL_RE)
228
+ return {
229
+ output: final ? final[1]!.trim() : output,
230
+ agentCalls,
231
+ delegations,
232
+ truncated,
233
+ }
234
+ }
@@ -8,8 +8,8 @@
8
8
  * 3. Provide runtime queries: ready nodes, overall progress
9
9
  */
10
10
 
11
- import { TaskNode, type TaskNodeConfig } from "./TaskNode"
12
- import { CyclicDependencyError } from "./errors"
11
+ import { TaskNode, type TaskNodeConfig } from "./TaskNode.ts"
12
+ import { CyclicDependencyError } from "./errors.ts"
13
13
 
14
14
  export class TaskGraph {
15
15
  readonly nodes: Map<string, TaskNode>
@@ -31,3 +31,10 @@ export { createHiveLearnGraph } from "./presets/index.ts";
31
31
  export type { HiveLearnAgentIds, HiveLearnInput } from "./presets/index.ts";
32
32
  export { createResearchGraph } from "./presets/index.ts";
33
33
  export type { ResearchAgentIds } from "./presets/index.ts";
34
+
35
+ // ─── Enjambre por roles (orquestador/trabajadores) ───────────────────────────
36
+ export { runRoleSwarm, defaultInvoker } from "./RoleSwarm.ts";
37
+ export type {
38
+ SwarmStrategy, RoleAgent, SwarmMessage, AgentInvoker,
39
+ RoleSwarmOptions, RoleSwarmResult,
40
+ } from "./RoleSwarm.ts";
@@ -16,8 +16,8 @@
16
16
  * Savings vs sequential: ~38% (~75s)
17
17
  */
18
18
 
19
- import { TaskGraph } from "../TaskGraph"
20
- import type { TaskNodeConfig } from "../TaskNode"
19
+ import { TaskGraph } from "../TaskGraph.ts"
20
+ import type { TaskNodeConfig } from "../TaskNode.ts"
21
21
 
22
22
  export interface HiveLearnAgentIds {
23
23
  curriculum: string
@@ -18,8 +18,8 @@
18
18
  * await scheduler.execute(graph, { projectId, coordinatorId })
19
19
  */
20
20
 
21
- import { TaskGraph } from "../TaskGraph"
22
- import type { TaskNodeConfig } from "../TaskNode"
21
+ import { TaskGraph } from "../TaskGraph.ts"
22
+ import type { TaskNodeConfig } from "../TaskNode.ts"
23
23
 
24
24
  export interface ResearchAgentIds {
25
25
  research: string
@@ -6,7 +6,7 @@
6
6
  * otherwise they wait in the queue.
7
7
  */
8
8
 
9
- import { TaskNode } from "../TaskNode"
9
+ import { TaskNode } from "../TaskNode.ts"
10
10
 
11
11
  export interface ExecutionStrategy {
12
12
  pick(queue: TaskNode[]): TaskNode | undefined
@@ -5,9 +5,9 @@
5
5
  * when slots are limited. Within same effective priority, FIFO order applies.
6
6
  */
7
7
 
8
- import { TaskNode } from "../TaskNode"
9
- import { TaskGraph } from "../TaskGraph"
10
- import type { ExecutionStrategy } from "./ParallelStrategy"
8
+ import { TaskNode } from "../TaskNode.ts"
9
+ import { TaskGraph } from "../TaskGraph.ts"
10
+ import type { ExecutionStrategy } from "./ParallelStrategy.ts"
11
11
 
12
12
  export class PriorityStrategy implements ExecutionStrategy {
13
13
  private criticalPathSet = new Set<string>()
@@ -1,5 +1,5 @@
1
- import type { ToolDefinition } from "./ToolRegistry";
2
- import type { ToolRegistry } from "./ToolRegistry";
1
+ import type { ToolDefinition } from "./ToolRegistry.ts";
2
+ import type { ToolRegistry } from "./ToolRegistry.ts";
3
3
 
4
4
  export interface ToolExecutionResult {
5
5
  toolName: string;
@@ -10,7 +10,11 @@ export interface ToolExecutionResult {
10
10
  }
11
11
 
12
12
  export class ToolExecutor {
13
- constructor(private registry: ToolRegistry) {}
13
+ private registry: ToolRegistry;
14
+
15
+ constructor(registry: ToolRegistry) {
16
+ this.registry = registry;
17
+ }
14
18
 
15
19
  async execute(
16
20
  name: string,
@@ -346,7 +346,7 @@ export const notifyTool: Tool = {
346
346
  required: ["message"],
347
347
  },
348
348
  execute: async (params: Record<string, unknown>, config?: any) => {
349
- const { sendToUserChannel } = await import("../../gateway/channel-notify");
349
+ const { sendToUserChannel } = await import("../../gateway/channel-notify.ts");
350
350
  const message = params.message as string;
351
351
  const channel = (config?.configurable?.channel as string) ?? "webchat";
352
352
  const userId = (config?.configurable?.user_id as string) ?? "";
@@ -423,7 +423,7 @@ export const reportProgressTool: Tool = {
423
423
  required: ["progress", "message"],
424
424
  },
425
425
  execute: async (params: Record<string, unknown>, config?: any) => {
426
- const { sendToUserChannel } = await import("../../gateway/channel-notify");
426
+ const { sendToUserChannel } = await import("../../gateway/channel-notify.ts");
427
427
  const progress = params.progress as number;
428
428
  const message = params.message as string;
429
429
  const taskId = (params.task_id as string) ?? null;
@@ -8,10 +8,10 @@
8
8
  * @category cron
9
9
  */
10
10
 
11
- import type { Tool } from "../types";
12
- import { col, toIndexable } from "../../storage/hive";
13
- import type { UserDoc, UserIdentityDoc, ChannelDoc, CronJobDoc, TaskRunDoc } from "../../storage/collections";
14
- import { logger } from "../../utils/logger";
11
+ import type { Tool } from "../types.ts";
12
+ import { col, toIndexable } from "../../storage/hive.ts";
13
+ import type { UserDoc, UserIdentityDoc, ChannelDoc, CronJobDoc, TaskRunDoc } from "../../storage/collections.ts";
14
+ import { logger } from "../../utils/logger.ts";
15
15
  import { Cron } from "croner";
16
16
 
17
17
  const log = logger.child("CronTools");