@johpaz/hive-sdk 0.1.3 → 0.1.5

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 (272) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +78 -23
  3. package/bunfig.toml +4 -2
  4. package/docs/API-AGENTS.md +78 -27
  5. package/docs/API-CONTEXT-COMPILER.md +31 -34
  6. package/docs/API-TOOLS-SKILLS-CHANNELS.md +58 -22
  7. package/docs/HIVE-HARNESS.md +1 -1
  8. package/docs/INDEX.md +4 -4
  9. package/docs/TEMPLATE-HIVE-APP.md +10 -10
  10. package/package.json +9 -4
  11. package/packages/cli/package.json +2 -2
  12. package/packages/cli/src/commands/create-app.test.ts +36 -7
  13. package/packages/cli/src/commands/init.ts +3 -3
  14. package/packages/cli/src/commands/run.ts +1 -1
  15. package/packages/cli/src/commands/test.ts +37 -25
  16. package/packages/cli/src/commands/trace.ts +30 -28
  17. package/packages/cli/templates/hive-app/.env.example +10 -2
  18. package/packages/cli/templates/hive-app/README.md +103 -0
  19. package/packages/cli/templates/hive-app/hive.config.ts +9 -3
  20. package/packages/cli/templates/hive-app/src/agents/coordinator.ts +8 -1
  21. package/packages/cli/templates/hive-app/src/main.ts +12 -19
  22. package/packages/core/package.json +5 -4
  23. package/packages/core/src/agent/acceptance-checks.ts +166 -0
  24. package/packages/core/src/agent/agent-catalog.ts +348 -0
  25. package/packages/core/src/agent/agent-loop.ts +1373 -0
  26. package/packages/core/src/agent/capability-search.ts +186 -0
  27. package/packages/core/src/agent/catalog-selector.ts +103 -0
  28. package/packages/core/src/agent/{Compaction.ts → compaction.ts} +86 -63
  29. package/packages/core/src/agent/context-compiler.ts +689 -0
  30. package/packages/core/src/agent/conversation-store.ts +381 -0
  31. package/packages/core/src/agent/curator.ts +276 -0
  32. package/packages/core/src/agent/delegation-runtime.ts +241 -0
  33. package/packages/core/src/agent/goal-runner.ts +323 -0
  34. package/packages/core/src/agent/index.ts +17 -12
  35. package/packages/core/src/agent/llm-client.ts +266 -0
  36. package/packages/core/src/agent/llm-providers/anthropic.ts +264 -0
  37. package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
  38. package/packages/core/src/agent/{providers → llm-providers}/gemini.ts +98 -60
  39. package/packages/core/src/agent/llm-providers/groq.ts +5 -0
  40. package/packages/core/src/agent/llm-providers/hiveagents.ts +253 -0
  41. package/packages/core/src/agent/{providers → llm-providers}/interface.ts +73 -13
  42. package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
  43. package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
  44. package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
  45. package/packages/core/src/agent/llm-providers/modelscope.ts +5 -0
  46. package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
  47. package/packages/core/src/agent/{providers → llm-providers}/ollama.ts +31 -5
  48. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +418 -0
  49. package/packages/core/src/agent/llm-providers/openai.ts +5 -0
  50. package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
  51. package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
  52. package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
  53. package/packages/core/src/agent/llm-providers/z-ai.ts +5 -0
  54. package/packages/core/src/agent/minimal-loadout.ts +47 -0
  55. package/packages/core/src/agent/playbook-selector.ts +119 -0
  56. package/packages/core/src/agent/{PromptBuilder.ts → prompt-builder.ts} +21 -22
  57. package/packages/core/src/{harness → agent}/proof-packet.ts +16 -21
  58. package/packages/core/src/agent/providers/index.ts +35 -16
  59. package/packages/core/src/agent/reflector.ts +320 -0
  60. package/packages/core/src/agent/routing-intent.ts +22 -0
  61. package/packages/core/src/{harness → agent}/run-epoch.ts +4 -3
  62. package/packages/core/src/{harness → agent}/run-store.ts +142 -81
  63. package/packages/core/src/agent/{Service.ts → service.ts} +37 -26
  64. package/packages/core/src/agent/skill-selector.ts +374 -0
  65. package/packages/core/src/agent/stuck-loop.ts +209 -0
  66. package/packages/core/src/agent/{selectors/ToolSelector.ts → tool-selector.ts} +188 -178
  67. package/packages/core/src/{ace/Tracer.ts → agent/tracer.ts} +37 -27
  68. package/packages/core/src/api/createAgent.test.ts +139 -27
  69. package/packages/core/src/api/createAgent.ts +232 -44
  70. package/packages/core/src/artifacts/store.ts +162 -0
  71. package/packages/core/src/canvas/canvas-manager.ts +161 -0
  72. package/packages/core/src/canvas/canvas.test.ts +8 -4
  73. package/packages/core/src/canvas/emitter.ts +131 -80
  74. package/packages/core/src/canvas/index.ts +1 -3
  75. package/packages/core/src/channels/base.ts +9 -1
  76. package/packages/core/src/channels/discord.ts +5 -4
  77. package/packages/core/src/channels/manager.ts +122 -30
  78. package/packages/core/src/channels/slack.ts +5 -4
  79. package/packages/core/src/channels/telegram.ts +36 -6
  80. package/packages/core/src/channels/webchat.ts +11 -10
  81. package/packages/core/src/channels/whatsapp.ts +23 -7
  82. package/packages/core/src/config/index.ts +13 -2
  83. package/packages/core/src/config/loader.ts +71 -29
  84. package/packages/core/src/ethics/EthicsGuard.test.ts +90 -36
  85. package/packages/core/src/ethics/EthicsGuard.ts +51 -47
  86. package/packages/core/src/events/agent-bus.ts +44 -68
  87. package/packages/core/src/events/channel-narration.ts +150 -0
  88. package/packages/core/src/events/narration.ts +82 -0
  89. package/packages/core/src/events/tool-narration.ts +62 -0
  90. package/packages/core/src/gateway/delegation-groups.ts +258 -0
  91. package/packages/core/src/{harness → gateway}/durable-queue.ts +102 -42
  92. package/packages/core/src/{harness → gateway}/job-store.ts +85 -48
  93. package/packages/core/src/gateway/lane-queue.ts +173 -0
  94. package/packages/core/src/gateway/notification-inbox.ts +57 -0
  95. package/packages/core/src/gateway/server.ts +1 -1
  96. package/packages/core/src/harness/index.ts +46 -27
  97. package/packages/core/src/index.ts +33 -20
  98. package/packages/core/src/mcp/hot-reload.ts +32 -23
  99. package/packages/core/src/mcp/index.ts +6 -3
  100. package/packages/core/src/mcp/singleton.ts +1 -4
  101. package/packages/core/src/mcp/tool-sync.ts +138 -0
  102. package/packages/core/src/memory/Scratchpad.test.ts +39 -20
  103. package/packages/core/src/memory/Scratchpad.ts +27 -34
  104. package/packages/core/src/multimodal/vision-service.ts +44 -38
  105. package/packages/core/src/resilience/retry.ts +95 -0
  106. package/packages/core/src/scheduler/CronScheduler.ts +334 -287
  107. package/packages/core/src/scheduler/index.ts +9 -7
  108. package/packages/core/src/scheduler/integration.ts +46 -26
  109. package/packages/core/src/scheduler/scheduler.test.ts +9 -13
  110. package/packages/core/src/scheduler/types.ts +7 -2
  111. package/packages/core/src/security/Pairing.ts +1 -1
  112. package/packages/core/src/skills/bundled/a2ui/a2ui_dashboard/SKILL.md +176 -0
  113. package/packages/core/src/skills/bundled/a2ui/a2ui_form/SKILL.md +202 -0
  114. package/packages/core/src/skills/bundled/a2ui/a2ui_interactive/SKILL.md +206 -0
  115. package/packages/core/src/skills/bundled/agents/agent_spawner/SKILL.md +173 -0
  116. package/packages/core/src/skills/bundled/agents/memory_manager/SKILL.md +143 -0
  117. package/packages/core/src/skills/bundled/agents/research_and_remember/SKILL.md +139 -0
  118. package/packages/core/src/skills/bundled/agents/task_orchestrator/SKILL.md +98 -0
  119. package/packages/core/src/skills/bundled/api/api_client/SKILL.md +132 -0
  120. package/packages/core/src/skills/bundled/cli/cli_pipeline/SKILL.md +135 -0
  121. package/packages/core/src/skills/bundled/cli/cli_safe_exec/SKILL.md +125 -0
  122. package/packages/core/src/skills/bundled/cli/software_engineering/SKILL.md +23 -0
  123. package/packages/core/src/skills/bundled/cron_manager/SKILL.md +188 -0
  124. package/packages/core/src/skills/bundled/cron_reminder/SKILL.md +112 -0
  125. package/packages/core/src/skills/bundled/filesystem/file_manager/SKILL.md +118 -0
  126. package/packages/core/src/skills/bundled/filesystem/file_read_and_summarize/SKILL.md +109 -0
  127. package/packages/core/src/skills/bundled/filesystem/file_writer/SKILL.md +129 -0
  128. package/packages/core/src/skills/bundled/filesystem/workspace_file_operator/SKILL.md +22 -0
  129. package/packages/core/src/skills/bundled/office/office_document_manager/SKILL.md +262 -0
  130. package/packages/core/src/skills/bundled/search_knowledge/capability_discovery/SKILL.md +75 -0
  131. package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +120 -0
  132. package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +109 -0
  133. package/packages/core/src/skills/bundled/web/web_monitor/SKILL.md +127 -0
  134. package/packages/core/src/skills/bundled/web/web_research/SKILL.md +119 -0
  135. package/packages/core/src/skills/bundled-data.generated.ts +731 -2678
  136. package/packages/core/src/skills/skills.test.ts +52 -11
  137. package/packages/core/src/{harness → storage}/boot-id.ts +5 -2
  138. package/packages/core/src/storage/bootstrap.ts +151 -0
  139. package/packages/core/src/storage/causal-events.ts +84 -0
  140. package/packages/core/src/storage/collections.ts +680 -0
  141. package/packages/core/src/storage/crypto.ts +205 -74
  142. package/packages/core/src/{harness/db-helpers.ts → storage/hive.ts} +63 -7
  143. package/packages/core/src/storage/hivedb.ts +61 -0
  144. package/packages/core/src/storage/index.ts +111 -17
  145. package/packages/core/src/storage/model-id.ts +53 -0
  146. package/packages/core/src/storage/onboarding.ts +540 -972
  147. package/packages/core/src/storage/reconcile.ts +238 -0
  148. package/packages/core/src/storage/seed.ts +572 -406
  149. package/packages/core/src/storage/usage.ts +285 -225
  150. package/packages/core/src/storage/user-email.ts +11 -0
  151. package/packages/core/src/swarm/AgentExecutor.ts +1 -1
  152. package/packages/core/src/swarm/EventBridge.ts +1 -1
  153. package/packages/core/src/swarm/index.ts +12 -9
  154. package/packages/core/src/tool-runtime/index.ts +146 -23
  155. package/packages/core/src/tool-runtime/tool-worker.ts +2 -2
  156. package/packages/core/src/tool-runtime/worker-tools.ts +27 -0
  157. package/packages/core/src/{canvas/a2ui-tools.ts → tools/a2ui/index.ts} +17 -8
  158. package/packages/core/src/tools/agents/get-available-models.ts +36 -54
  159. package/packages/core/src/tools/agents/index.ts +784 -292
  160. package/packages/core/src/tools/api/api-request.test.ts +164 -0
  161. package/packages/core/src/tools/api/api-request.ts +174 -0
  162. package/packages/core/src/tools/api/index.ts +16 -0
  163. package/packages/core/src/tools/cli/index.ts +4 -0
  164. package/packages/core/src/tools/core/index.ts +281 -112
  165. package/packages/core/src/tools/cron/index.ts +121 -124
  166. package/packages/core/src/tools/index.ts +63 -78
  167. package/packages/core/src/tools/office/office-escribir-xlsx.ts +3 -1
  168. package/packages/core/src/tools/types.ts +3 -1
  169. package/packages/core/src/tools/web/artifact-inspect.ts +23 -0
  170. package/packages/core/src/tools/web/browser-screenshot.ts +26 -5
  171. package/packages/core/src/tools/web/browser-service.ts +5 -0
  172. package/packages/core/src/tools/web/browser-type.ts +3 -8
  173. package/packages/core/src/tools/web/index.ts +4 -4
  174. package/packages/core/src/voice/index.ts +89 -63
  175. package/packages/core/src/workers/agent.worker.ts +2 -2
  176. package/packages/core/src/workers/workers.test.ts +3 -10
  177. package/scripts/bump-version.ts +248 -0
  178. package/scripts/generate-skill-bundle.ts +108 -0
  179. package/test/agent-loop-terminal-synthesis.test.ts +32 -0
  180. package/test/catalog-agents-stay-enabled.test.ts +117 -0
  181. package/test/causal-events.test.ts +117 -0
  182. package/test/compaction.test.ts +105 -0
  183. package/test/context-compiler.test.ts +269 -0
  184. package/test/curator.test.ts +130 -0
  185. package/test/durable-queue.test.ts +114 -0
  186. package/test/harness-barrel.test.ts +64 -0
  187. package/test/hive-helpers.test.ts +130 -0
  188. package/test/hivedb-search.test.ts +189 -0
  189. package/test/internal-turns.test.ts +166 -0
  190. package/test/job-idempotency.test.ts +68 -0
  191. package/test/job-retry-backoff.test.ts +184 -0
  192. package/test/job-store.test.ts +381 -0
  193. package/test/llm-retry.test.ts +97 -0
  194. package/test/memory-perf.test.ts +774 -0
  195. package/test/minimal-loadout.test.ts +78 -0
  196. package/test/model-catalog.test.ts +105 -0
  197. package/test/preload.ts +12 -0
  198. package/test/reflector.test.ts +320 -0
  199. package/test/retention-cap.test.ts +91 -0
  200. package/test/retired-capabilities-pruned.test.ts +192 -0
  201. package/test/run-store.test.ts +355 -0
  202. package/test/scratchpad.test.ts +74 -0
  203. package/test/secrets-durability.test.ts +119 -0
  204. package/test/seed-model-reseed.test.ts +155 -0
  205. package/test/setup-agent-seed.test.ts +264 -0
  206. package/test/tool-inventory.test.ts +65 -0
  207. package/test/tool-runtime.test.ts +258 -0
  208. package/test/toon.test.ts +429 -0
  209. package/tsconfig.json +2 -0
  210. package/packages/core/src/ace/Curator.ts +0 -158
  211. package/packages/core/src/ace/Reflector.ts +0 -200
  212. package/packages/core/src/ace/index.ts +0 -4
  213. package/packages/core/src/agent/AgentRunner.ts +0 -711
  214. package/packages/core/src/agent/ContextCompiler.ts +0 -567
  215. package/packages/core/src/agent/ContextGuard.ts +0 -91
  216. package/packages/core/src/agent/ConversationStore.ts +0 -254
  217. package/packages/core/src/agent/Hooks.ts +0 -166
  218. package/packages/core/src/agent/StuckLoop.ts +0 -133
  219. package/packages/core/src/agent/providers/LLMClient.ts +0 -149
  220. package/packages/core/src/agent/providers/anthropic.ts +0 -212
  221. package/packages/core/src/agent/providers/openai-compat.ts +0 -231
  222. package/packages/core/src/agent/selectors/PlaybookSelector.ts +0 -121
  223. package/packages/core/src/agent/selectors/SkillSelector.ts +0 -322
  224. package/packages/core/src/agent/selectors/index.ts +0 -6
  225. package/packages/core/src/auth/auth.ts +0 -121
  226. package/packages/core/src/auth/index.ts +0 -1
  227. package/packages/core/src/canvas/CanvasManager.ts +0 -390
  228. package/packages/core/src/canvas/canvas-tools.ts +0 -448
  229. package/packages/core/src/harness/collections.ts +0 -98
  230. package/packages/core/src/harness/goal-verifier.ts +0 -141
  231. package/packages/core/src/harness/harness.test.ts +0 -236
  232. package/packages/core/src/harness/reconcile.ts +0 -149
  233. package/packages/core/src/mcp/MCPToolAdapter.ts +0 -176
  234. package/packages/core/src/multimodal/VisionService.ts +0 -293
  235. package/packages/core/src/scheduler/dag/AgentExecutor.ts +0 -53
  236. package/packages/core/src/scheduler/dag/DAGScheduler.ts +0 -250
  237. package/packages/core/src/scheduler/dag/EventBridge.ts +0 -122
  238. package/packages/core/src/scheduler/dag/TaskGraph.ts +0 -192
  239. package/packages/core/src/scheduler/dag/TaskNode.ts +0 -97
  240. package/packages/core/src/scheduler/dag/TaskResult.ts +0 -22
  241. package/packages/core/src/scheduler/dag/errors.ts +0 -37
  242. package/packages/core/src/scheduler/dag/index.ts +0 -26
  243. package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +0 -97
  244. package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +0 -21
  245. package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +0 -46
  246. package/packages/core/src/storage/HiveDBStorage.ts +0 -64
  247. package/packages/core/src/storage/SQLiteStorage.ts +0 -414
  248. package/packages/core/src/storage/hiveSeed.ts +0 -308
  249. package/packages/core/src/storage/hiveStorage.test.ts +0 -38
  250. package/packages/core/src/storage/schema.ts +0 -689
  251. package/packages/core/src/storage/storage.test.ts +0 -37
  252. package/packages/core/src/swarm/AgentBus.ts +0 -460
  253. package/packages/core/src/swarm/EventBus.ts +0 -169
  254. package/packages/core/src/swarm/WorkerPool.ts +0 -236
  255. package/packages/core/src/tools/bridge-events.ts +0 -26
  256. package/packages/core/src/tools/canvas/index.ts +0 -375
  257. package/packages/core/src/tools/codebridge/index.ts +0 -342
  258. package/packages/core/src/tools/meeting/index.ts +0 -353
  259. package/packages/core/src/tools/projects/index.ts +0 -37
  260. package/packages/core/src/tools/projects/project-create.ts +0 -94
  261. package/packages/core/src/tools/projects/project-done.ts +0 -66
  262. package/packages/core/src/tools/projects/project-fail.ts +0 -66
  263. package/packages/core/src/tools/projects/project-list.ts +0 -96
  264. package/packages/core/src/tools/projects/project-update.ts +0 -72
  265. package/packages/core/src/tools/projects/task-create.ts +0 -68
  266. package/packages/core/src/tools/projects/task-evaluate.ts +0 -93
  267. package/packages/core/src/tools/projects/task-update.ts +0 -93
  268. package/packages/core/src/tools/voice/index.ts +0 -104
  269. package/packages/core/src/tools/web/api-request.test.ts +0 -170
  270. package/packages/core/src/tools/web/api-request.ts +0 -239
  271. package/test/setup-db.ts +0 -216
  272. /package/packages/core/src/agent/{NativeTools.ts → native-tools.ts} +0 -0
@@ -1,5 +1,8 @@
1
- import { getDb } from "./SQLiteStorage.ts"
2
- import { logger } from "../utils/logger.ts"
1
+ import { col, toIndexable, nextId } from "./hive"
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
6
 
4
7
  /**
5
8
  * Seed de datos predeterminados para Hive
@@ -10,12 +13,23 @@ import { logger } from "../utils/logger.ts"
10
13
  export interface SeedData {
11
14
  tools: Array<{ id: string; name: string; category: string; description: string; enabled?: boolean }>
12
15
  providers: Array<{ id: string; name: string; baseUrl?: string; category?: string }>
13
- models: Array<{ id: string; providerId: string; name: string; modelType: string; contextWindow?: number; capabilities?: string }>
16
+ /**
17
+ * Catálogo de modelos — única fuente de verdad, incluidos los precios.
18
+ *
19
+ * `id` es el nombre del modelo tal como lo llama su propietario. La clave real
20
+ * en la BD la deriva `catalogModelKey()` (storage/model-id.ts), que prefija a
21
+ * los providers revendedores para que dos servicios puedan ofrecer el mismo
22
+ * modelo sin pisarse.
23
+ *
24
+ * `inputPer1M` / `outputPer1M` son USD por millón de tokens y alimentan el
25
+ * costo del dashboard. Obligatorios en los modelos `llm`: omitirlos hace que
26
+ * el modelo aparezca gratis, que es indistinguible de un endpoint sin costo.
27
+ * Los endpoints realmente gratuitos (NVIDIA NIM, Ollama, HiveAgents) van con 0.
28
+ */
29
+ models: Array<{ id: string; providerId: string; name: string; modelType: string; contextWindow?: number; capabilities?: string; inputPer1M?: number; outputPer1M?: number }>
14
30
  mcpServers: Array<{ id: string; name: string; transport: string; command?: string; args?: string[]; builtin: boolean }>
15
31
  channels: Array<{ id: string; type: string }>
16
32
  ethics: Array<{ id: string; name: string; description: string; content: string; isDefault: boolean }>
17
- codeBridge: Array<{ id: string; name: string; cliCommand: string; port: number }>
18
- codeBridgeConfig: Array<{ id: string; key: string; value: string }>
19
33
  }
20
34
 
21
35
  export const SEED_DATA: SeedData = {
@@ -35,10 +49,12 @@ export const SEED_DATA: SeedData = {
35
49
  // ─────────────────────────────────────────
36
50
  // 2. WEB — Búsqueda, navegación + automatización
37
51
  // ─────────────────────────────────────────
52
+ { id: "api_request", name: "api_request", category: "api", description: "Ejecutar una petición HTTP autorizada contra un endpoint REST y validar la respuesta. Sinónimos: llamar api, request rest, consumir endpoint, petición http, hacer get, hacer post" },
38
53
  { id: "web_search", name: "web_search", category: "web", description: "Buscar en la web información actual y noticias. Sinónimos: búsqueda web, noticias, información, buscar en internet, google" },
39
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" },
40
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" },
41
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: "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" },
42
58
  { 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" },
43
59
  { 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" },
44
60
  { 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" },
@@ -46,36 +62,24 @@ export const SEED_DATA: SeedData = {
46
62
  { id: "browser_wait", name: "browser_wait", category: "web", description: "Esperar a que aparezca un elemento o se cumpla una condición. Sinónimos: esperar, condición, elemento, selector, pausa" },
47
63
 
48
64
  // ─────────────────────────────────────────
49
- // 3. PROJECTSProyectos y tareas en BD
65
+ // 3. CRONTareas programadas (Croner-based)
50
66
  // ─────────────────────────────────────────
51
- { id: "project_create", name: "project_create", category: "projects", description: "Crear un nuevo proyecto con tareas en la base de datos. Sinónimos: nuevo proyecto, iniciar plan, crear proyecto" },
52
- { id: "project_list", name: "project_list", category: "projects", description: "Listar todos los proyectos con su estado. Sinónimos: ver proyectos, historial, listar proyectos, mostrar proyectos" },
53
- { id: "project_update", name: "project_update", category: "projects", description: "Actualizar progreso o metadatos del proyecto. Sinónimos: avance, porcentaje, estado, actualizar proyecto" },
54
- { id: "project_done", name: "project_done", category: "projects", description: "Marcar proyecto como completado y archivarlo. Sinónimos: proyecto terminado, cerrar proyecto, completado, finalizar" },
55
- { id: "project_fail", name: "project_fail", category: "projects", description: "Marcar proyecto como fallido y registrar razón. Sinónimos: proyecto fallido, marcar fracaso, error, falló proyecto" },
56
- { id: "task_create", name: "task_create", category: "projects", description: "Agregar una tarea o subtarea a un proyecto existente. Sinónimos: crear tarea, agregar tarea, subtarea, pendiente" },
57
- { id: "task_update", name: "task_update", category: "projects", description: "Actualizar estado de tarea (pendiente, en_progreso, hecho). Sinónimos: actualizar tarea, marcar completa, en progreso" },
58
- { id: "task_evaluate", name: "task_evaluate", category: "projects", description: "Evaluar resultado de tarea contra criterios de aceptación. Sinónimos: validar resultado, criterios de aceptación, revisar tarea" },
67
+ { id: "cron.create", name: "cron.create", category: "cron", description: "Crear una automatización de Hive programada: recurrente (expresión cron) o ejecución futura única (fire_at). Requiere 'task'. Sinónimos: programar tarea, crear automatización, ejecutar después, tarea recurrente, una vez" },
68
+ { id: "cron.list", name: "cron.list", category: "cron", description: "Listar todas las tareas programadas con próximos horarios de ejecución. Sinónimos: ver tareas programadas, listar cronograma, próximas ejecuciones" },
69
+ { id: "cron.update", name: "cron.update", category: "cron", description: "Actualizar tarea programada existente: cambiar expresión, instrucción, canal, ventana temporal. Sinónimos: modificar cron, editar recordatorio, cambiar horario, actualizar tarea" },
70
+ { id: "cron.pause", name: "cron.pause", category: "cron", description: "Pausar temporalmente una tarea programada sin eliminarla. Sinónimos: pausar tarea programada, detener temporalmente, suspender recordatorio" },
71
+ { id: "cron.resume", name: "cron.resume", category: "cron", description: "Reanudar una tarea programada previamente pausada. Sinónimos: reanudar tarea, continuar tarea pausada, activar recordatorio" },
72
+ { id: "cron.delete", name: "cron.delete", category: "cron", description: "Eliminar una tarea programada permanentemente. Sinónimos: eliminar tarea programada, borrar recordatorio, cancelar tarea" },
73
+ { id: "cron.trigger", name: "cron.trigger", category: "cron", description: "Ejecutar manualmente una tarea programada de forma inmediata. Sinónimos: ejecutar tarea ahora, forzar ejecución, disparar manualmente" },
74
+ { id: "cron.history", name: "cron.history", category: "cron", description: "Obtener historial de ejecuciones y logs de una tarea programada. Sinónimos: historial ejecuciones, logs tarea, registro ejecuciones" },
59
75
 
60
76
  // ─────────────────────────────────────────
61
- // 4. CRONTareas programadas (Croner-based)
62
- // ─────────────────────────────────────────
63
- { id: "cron_create", name: "cron_create", category: "cron", description: "Crear tarea programada: recurrente (expresión cron) o única (fire_at). Requiere campo 'task' con instrucción para el agente. Sinónimos: programar tarea, crear recordatorio, agendar, automatizar horario, tarea recurrente, una vez" },
64
- { id: "cron_list", name: "cron_list", category: "cron", description: "Listar todas las tareas programadas con próximos horarios de ejecución. Sinónimos: ver tareas programadas, listar cronograma, próximas ejecuciones" },
65
- { id: "cron_update", name: "cron_update", category: "cron", description: "Actualizar tarea programada existente: cambiar expresión, instrucción, canal, ventana temporal. Sinónimos: modificar cron, editar recordatorio, cambiar horario, actualizar tarea" },
66
- { id: "cron_pause", name: "cron_pause", category: "cron", description: "Pausar temporalmente una tarea programada sin eliminarla. Sinónimos: pausar tarea programada, detener temporalmente, suspender recordatorio" },
67
- { id: "cron_resume", name: "cron_resume", category: "cron", description: "Reanudar una tarea programada previamente pausada. Sinónimos: reanudar tarea, continuar tarea pausada, activar recordatorio" },
68
- { id: "cron_delete", name: "cron_delete", category: "cron", description: "Eliminar una tarea programada permanentemente. Sinónimos: eliminar tarea programada, borrar recordatorio, cancelar tarea" },
69
- { id: "cron_trigger", name: "cron_trigger", category: "cron", description: "Ejecutar manualmente una tarea programada de forma inmediata. Sinónimos: ejecutar tarea ahora, forzar ejecución, disparar manualmente" },
70
- { id: "cron_history", name: "cron_history", category: "cron", description: "Obtener historial de ejecuciones y logs de una tarea programada. Sinónimos: historial ejecuciones, logs tarea, registro ejecuciones" },
71
-
72
- // ─────────────────────────────────────────
73
- // 5. CLI — Ejecución de comandos
77
+ // 4. CLIEjecución de comandos
74
78
  // ─────────────────────────────────────────
75
79
  { id: "cli_exec", name: "cli_exec", category: "cli", description: "Ejecutar comandos shell/bash en el entorno del agente. NOTA: NO usar para tareas programadas, usar cron.create. Sinónimos: ejecutar comando, terminal, bash, script, consola" },
76
80
 
77
81
  // ─────────────────────────────────────────
78
- // 6. AGENTS — Memoria, workers y delegación
82
+ // 5. AGENTS — Memoria, workers y delegación
79
83
  // ─────────────────────────────────────────
80
84
  { id: "memory_write", name: "memory_write", category: "agents", description: "Guardar información en memoria persistente a largo plazo. Sinónimos: guardar memoria, recordar, guardar dato, memoria persistente" },
81
85
  { id: "memory_read", name: "memory_read", category: "agents", description: "Recuperar una entrada de memoria por identificador. Sinónimos: leer memoria, recuperar dato, obtener memoria" },
@@ -83,79 +87,34 @@ export const SEED_DATA: SeedData = {
83
87
  { id: "memory_search", name: "memory_search", category: "agents", description: "Buscar memorias por palabra clave. Sinónimos: buscar memoria, encontrar recuerdo, buscar dato guardado" },
84
88
  { id: "memory_delete", name: "memory_delete", category: "agents", description: "Eliminar una entrada de memoria específica. Sinónimos: borrar memoria, eliminar recuerdo, quitar dato" },
85
89
  { id: "get_available_models", name: "get_available_models", category: "agents", description: "Obtener lista de providers y modelos activos de la BD. Sinónimos: ver modelos, listar providers, modelos disponibles, consultar modelos, provider activo, qué modelos tengo, modelos para código, modelos para chat" },
86
- { id: "agent_create", name: "agent_create", category: "agents", description: "Crear un nuevo agente worker especializado. Sinónimos: crear agente, nuevo worker, nuevo trabajador" },
87
- { id: "agent_find", name: "agent_find", category: "agents", description: "Buscar agentes worker existentes en ejecución o inactivos. Sinónimos: buscar agente, encontrar worker, localizar agente" },
90
+ { id: "agent_create", name: "agent_create", category: "agents", description: "Crear un nuevo agente worker especializado; puede asignar un servidor MCP persistente después de la confirmación del usuario. Sinónimos: crear agente, nuevo worker, nuevo trabajador" },
91
+ { id: "agent_find", name: "agent_find", category: "agents", description: "Descubrir agentes worker disponibles: catálogo global del sistema y workers privados del usuario. No indica ejecución; para eso usar task_list. Sinónimos: buscar agente, encontrar worker, localizar agente" },
88
92
  { id: "agent_archive", name: "agent_archive", category: "agents", description: "Archivar o terminar un agente worker. Sinónimos: archivar agente, terminar worker, desactivar agente" },
89
93
  { id: "task_delegate", name: "task_delegate", category: "agents", description: "Delegar una tarea general a un agente worker específico. Sinónimos: delegar tarea, asignar worker, ejecutar por agente" },
90
- { id: "task_delegate_code", name: "task_delegate_code", category: "agents", description: "Delegar tarea de código a un subagente CLI (Qwen, Claude, etc.) vía Code Bridge. Sinónimos: delegar código, subagente CLI, programación, Qwen" },
94
+ { id: "task_revise", name: "task_revise", category: "agents", description: "Devolver una tarea delegada a su worker con feedback cuando no cumple sus criterios de aceptación. Sinónimos: corregir tarea, devolver al worker, pedir corrección, reencolar tarea" },
95
+ { id: "task_list", name: "task_list", category: "agents", description: "Listar ejecuciones reales de tareas delegadas del usuario, consultando tareas y jobs persistidos. Sinónimos: listar tareas activas, ver subagentes trabajando, ejecuciones reales" },
91
96
  { id: "task_status", name: "task_status", category: "agents", description: "Obtener estado de ejecución de tareas delegadas. Sinónimos: estado tarea delegada, verificar progreso, consultar tarea" },
92
97
  { id: "bus_publish", name: "bus_publish", category: "agents", description: "Publicar mensaje en el Agent Bus para comunicación worker-to-worker. Sinónimos: publicar mensaje, comunicar workers, enviar bus" },
93
98
  { id: "bus_read", name: "bus_read", category: "agents", description: "Leer mensajes no leídos del Agent Bus. Sinónimos: leer mensajes bus, recibir mensajes, verificar bus" },
94
- { id: "project_updates", name: "project_updates", category: "agents", description: "Obtener actualizaciones recientes de workers en el mismo proyecto. Sinónimos: actualizaciones proyecto, estado workers, progreso equipo" },
95
-
96
- // ─────────────────────────────────────────
97
- // 7. CANVAS — UI interactiva
98
- // ─────────────────────────────────────────
99
- { id: "canvas_render", name: "canvas_render", category: "canvas", description: "Renderizar un componente o visualización en el canvas. Sinónimos: renderizar, visualizar, gráfico, diagrama" },
100
- { id: "canvas_ask", name: "canvas_ask", category: "canvas", description: "Mostrar formulario interactivo y esperar input del usuario. Sinónimos: formulario interactivo, preguntar usuario, input" },
101
- { id: "canvas_confirm", name: "canvas_confirm", category: "canvas", description: "Mostrar diálogo de confirmación antes de ejecutar una acción. Sinónimos: confirmar acción, diálogo, aprobar" },
102
- { id: "canvas_show_card", name: "canvas_show_card", category: "canvas", description: "Mostrar información estructurada en formato de tarjeta. Sinónimos: mostrar tarjeta, card, información estructurada" },
103
- { id: "canvas_show_progress", name: "canvas_show_progress", category: "canvas", description: "Mostrar barra de progreso o indicador de estado. Sinónimos: barra de progreso, indicador, progreso visual" },
104
- { id: "canvas_show_list", name: "canvas_show_list", category: "canvas", description: "Mostrar información en lista clave-valor. Sinónimos: lista clave-valor, mostrar lista, información en lista" },
105
- { id: "canvas_clear", name: "canvas_clear", category: "canvas", description: "Limpiar contenido actual del canvas. Sinónimos: limpiar canvas, borrar visualización, resetear" },
106
99
 
107
100
  // ─────────────────────────────────────────
108
- // 7b. CANVAS A2UI v0.9 — Superficies interactivas ricas
101
+ // 6. A2UI v0.9 — Panel interactivo
109
102
  // ─────────────────────────────────────────
110
103
  { id: "a2ui_create_surface", name: "a2ui_create_surface", category: "a2ui", description: "Crear superficie A2UI v0.9 para UI interactiva rica: formularios, dashboards, wizards, flujos multi-paso. Siempre llamar ANTES de a2ui_update_components. Requiere surfaceId y catalogId='https://a2ui.org/specification/v0_9/basic_catalog.json'. Sinónimos: crear superficie A2UI, iniciar UI A2UI, crear form A2UI, interfaz interactiva, crear dashboard A2UI" },
111
104
  { id: "a2ui_update_components", name: "a2ui_update_components", category: "a2ui", description: "Enviar componentes A2UI v0.9 como lista plana (adjacency list). Tipos: Text, Button, TextField, Row, Column, Card, List, Tabs, Modal, ChoicePicker, Slider, CheckBox, DateTimeInput, Image, Divider. Reglas: children usa explicitList (NO array), ChoicePicker usa selections (NO value), TextField usa textFieldType (NO variant), Tabs.tabItems.title es string plano. Sinónimos: actualizar componentes A2UI, enviar UI A2UI, renderizar componentes A2UI, layout A2UI" },
112
105
  { id: "a2ui_update_data_model", name: "a2ui_update_data_model", category: "a2ui", description: "Actualizar data model de superficie A2UI v0.9 via JSON Pointer (/ruta/campo). Omitir path reemplaza todo el modelo. Los componentes con {path:'/...'} se actualizan automáticamente en el cliente. Sinónimos: actualizar datos A2UI, poblar formulario A2UI, inicializar estado A2UI, data model, binding" },
113
- { id: "a2ui_delete_surface", name: "a2ui_delete_surface", category: "a2ui", description: "Eliminar superficie A2UI v0.9 del canvas. Usar al completar o cancelar el flujo para liberar recursos. Sinónimos: eliminar superficie A2UI, borrar UI A2UI, cerrar formulario A2UI, limpiar canvas A2UI" },
106
+ { id: "a2ui_delete_surface", name: "a2ui_delete_surface", category: "a2ui", description: "Eliminar una superficie A2UI v0.9 del panel interactivo. Usar al completar o cancelar el flujo para liberar recursos. Sinónimos: eliminar superficie A2UI, borrar UI A2UI, cerrar formulario A2UI, limpiar panel A2UI" },
114
107
 
115
- // ─────────────────────────────────────────
116
- // 8. CODEBRIDGE — Subagentes CLI de código externos
117
- // Conecta con: Claude Code, Qwen CLI, Gemini CLI, OpenCode CLI
118
- // ─────────────────────────────────────────
119
- {
120
- id: "codebridge_launch",
121
- name: "codebridge_launch",
122
- category: "codebridge",
123
- description: "Lanzar un subagente externo de código (Claude Code, Qwen CLI, Gemini CLI, OpenCode) para ejecutar tarea localmente. Retorna ID de proceso para trackear. Sinónimos: lanzar agente de código, iniciar Claude Code, Qwen CLI, Gemini CLI, OpenCode, subagente externo de programación"
124
- },
125
- {
126
- id: "codebridge_status",
127
- name: "codebridge_status",
128
- category: "codebridge",
129
- description: "Verificar estado y salida de un subagente CodeBridge en ejecución. Sinónimos: estado agente de código, verificar Claude Code, progreso subagente externo"
130
- },
131
- {
132
- id: "codebridge_cancel",
133
- name: "codebridge_cancel",
134
- category: "codebridge",
135
- description: "Cancelar y terminar un proceso de subagente CodeBridge en ejecución. Sinónimos: cancelar agente de código, detener Claude Code, terminar subagente externo"
136
- },
137
- {
138
- id: "codebridge_feedback",
139
- name: "codebridge_feedback",
140
- category: "codebridge",
141
- description: "Enviar feedback o instrucciones adicionales a un subagente CodeBridge en ejecución. Usar para correcciones de rumbo, aclaraciones o mejoras iterativas durante tareas largas de código. Sinónimos: enviar feedback, corregir rumbo, aclaraciones, mejoras iterativas"
142
- },
143
- // ─────────────────────────────────────────
144
- // 9. VOICE — Voz
145
- // ─────────────────────────────────────────
146
- { id: "voice_transcribe", name: "voice_transcribe", category: "voice", description: "Transcribir entrada de audio a texto. Sinónimos: transcribir audio, voz a texto, reconocimiento de voz" },
147
- { id: "voice_speak", name: "voice_speak", category: "voice", description: "Convertir texto a voz sintetizada. Sinónimos: texto a voz, sintetizar, hablar, leer en voz alta" },
148
-
149
- // 10. SEARCH-KNOWLEDGE
108
+ // 8. SEARCH-KNOWLEDGE
150
109
  { id: "search_knowledge", name: "search_knowledge", category: "search-knowledge", description: "Buscar en la base de conocimientos. Sinónimos: buscar conocimiento, buscar en la base" },
151
110
 
152
- // 11. CORE — Notificaciones y notas
111
+ // 9. CORE — Notificaciones y notas
153
112
  { id: "notify", name: "notify", category: "core", description: "Enviar notificación al usuario. Sinónimos: notificar, enviar notificación, alertar, aviso" },
154
113
  { id: "save_note", name: "save_note", category: "core", description: "Guardar nota persistente en el scratchpad. Sinónimos: guardar nota, escribir nota, recordatorio rápido, apuntar" },
155
114
  { id: "report_progress", name: "report_progress", category: "core", description: "Reportar progreso actual al usuario. Sinónimos: reportar progreso, informar estado, actualizar progreso, porcentaje" },
156
115
 
157
116
  // ─────────────────────────────────────────
158
- // 12. OFFICE — Archivos Office (PDF, DOCX, XLSX, PPTX)
117
+ // 10. OFFICE — Archivos Office (PDF, DOCX, XLSX, PPTX)
159
118
  // ─────────────────────────────────────────
160
119
  { id: "office_leer_pdf", name: "office_leer_pdf", category: "office", description: "Leer contenido de un archivo PDF y retornar texto plano con metadata. Sinónimos: leer pdf, abrir pdf, extraer texto de pdf, contenido pdf, pdf a texto" },
161
120
  { id: "office_escribir_pdf", name: "office_escribir_pdf", category: "office", description: "Generar un archivo PDF desde texto con configuración de márgenes y tamaño de página. Sinónimos: crear pdf, generar pdf, escribir pdf, exportar a pdf" },
@@ -178,117 +137,128 @@ export const SEED_DATA: SeedData = {
178
137
  { id: "openrouter", name: "OpenRouter", baseUrl: "https://openrouter.ai/api/v1" },
179
138
  { id: "ollama", name: "Ollama (Local)", baseUrl: "http://localhost:11434" },
180
139
  { id: "groq", name: "Groq", baseUrl: "https://api.groq.com/openai/v1" },
181
- { id: "local-llama", name: "Local LLM (llama-server)", baseUrl: "http://localhost:8080/v1" },
182
- { id: "elevenlabs", name: "ElevenLabs", baseUrl: "https://api.elevenlabs.io/v1" },
140
+ { id: "elevenlabs", name: "ElevenLabs", baseUrl: "https://api.elevenlabs.io/v1", category: "tts" },
183
141
  { id: "qwen", name: "Qwen (Alibaba)", baseUrl: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1", category: "llm" },
184
142
  { id: "nvidia", name: "NVIDIA NIM", baseUrl: "https://integrate.api.nvidia.com/v1" },
143
+ { id: "minimax", name: "MiniMax", baseUrl: "https://api.minimaxi.com/v1" },
144
+ { id: "z-ai", name: "Z.ai (GLM)", baseUrl: "https://api.z.ai/api/paas/v4" },
145
+ // `.ai` (internacional), NO `.cn`. Son plataformas separadas con cuentas y
146
+ // tokens propios: un token internacional responde 401 en el endpoint chino.
147
+ // Confunde porque GET /v1/models da 200 en ambos — ese listado es público y
148
+ // no valida la key; el 401 recién aparece al invocar el modelo.
149
+ { id: "modelscope", name: "ModelScope Qwen", baseUrl: "https://api-inference.modelscope.ai/v1" },
150
+ { id: "opencode-go", name: "OpenCode Go", baseUrl: "https://opencode.ai/zen/go/v1" },
151
+ { id: "piper", name: "Piper (Local TTS)", category: "tts" },
152
+ { id: "hiveagents", name: "HiveAgents LLM (Cloudflare)", baseUrl: "https://llm.hiveagents.io/v1", category: "llm" },
185
153
  ],
186
154
 
187
155
  models: [
188
- // ── Anthropic (fuente: docs.anthropic.com/en/docs/about-claude/models) ──
189
- { id: "claude-opus-4-6", providerId: "anthropic", name: "Claude Opus 4.6", modelType: "llm", contextWindow: 200000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]) },
190
- { id: "claude-sonnet-4-6", providerId: "anthropic", name: "Claude Sonnet 4.6", modelType: "llm", contextWindow: 200000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]) },
191
- { id: "claude-haiku-4-5-20251001", providerId: "anthropic", name: "Claude Haiku 4.5", modelType: "llm", contextWindow: 200000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
192
-
193
- // ── OpenAI (fuente: openrouter.ai/openai) ──
194
- // Chat / Reasoning
195
- { id: "gpt-4o", providerId: "openai", name: "GPT-4o", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]) },
196
- { id: "gpt-4o-mini", providerId: "openai", name: "GPT-4o Mini", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
197
- { id: "gpt-5.4", providerId: "openai", name: "GPT-5.4", modelType: "llm", contextWindow: 1050000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]) },
198
- { id: "gpt-5.4-pro", providerId: "openai", name: "GPT-5.4 Pro", modelType: "llm", contextWindow: 1050000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]) },
199
- { id: "gpt-5.3", providerId: "openai", name: "GPT-5.3", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
200
- { id: "gpt-5.2", providerId: "openai", name: "GPT-5.2", modelType: "llm", contextWindow: 400000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]) },
201
- { id: "o4-mini", providerId: "openai", name: "o4-mini", modelType: "llm", contextWindow: 200000, capabilities: JSON.stringify(["chat", "reasoning", "streaming"]) },
156
+ // ── Anthropic (fuente: platform.claude.com/docs/en/about-claude/models/overview) ──
157
+ // Generación actual (4.6/4.7/4.8 pasaron a "legacy"). Los IDs sin fecha ya son
158
+ // snapshots fijos, no alias evergreen.
159
+ { id: "claude-opus-5", providerId: "anthropic", name: "Claude Opus 5", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 5, outputPer1M: 25 },
160
+ { id: "claude-sonnet-5", providerId: "anthropic", name: "Claude Sonnet 5", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 3, outputPer1M: 15 },
161
+ { id: "claude-fable-5", providerId: "anthropic", name: "Claude Fable 5", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 10, outputPer1M: 50 },
162
+ { id: "claude-haiku-4-5-20251001", providerId: "anthropic", name: "Claude Haiku 4.5", modelType: "llm", contextWindow: 200000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 1, outputPer1M: 5 },
163
+
164
+ // ── OpenAI (fuente: developers.openai.com/api/docs/models) ──
165
+ // Serie 5.6: Sol (frontier), Terra (equilibrio) y Luna (alto volumen / bajo costo).
166
+ // Las tres comparten 1.05M de contexto; se quitó la familia GPT-4o (2024).
167
+ { id: "gpt-5.6-luna", providerId: "openai", name: "GPT-5.6 Luna", modelType: "llm", contextWindow: 1050000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 0.1, outputPer1M: 0.6 },
168
+ { id: "gpt-5.6-terra", providerId: "openai", name: "GPT-5.6 Terra", modelType: "llm", contextWindow: 1050000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 1, outputPer1M: 6 },
169
+ { id: "gpt-5.6-sol", providerId: "openai", name: "GPT-5.6 Sol", modelType: "llm", contextWindow: 1050000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 5, outputPer1M: 30 },
202
170
  // STT / TTS
203
171
  { id: "whisper-1", providerId: "openai", name: "Whisper 1", modelType: "stt", contextWindow: 0, capabilities: JSON.stringify(["transcription", "translation"]) },
204
172
  { id: "tts-1", providerId: "openai", name: "TTS-1", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech"]) },
205
173
  { id: "tts-1-hd", providerId: "openai", name: "TTS-1 HD", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech", "high_quality"]) },
206
174
  { id: "gpt-4o-mini-tts", providerId: "openai", name: "GPT-4o Mini TTS", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech"]) },
207
-
208
- // ── Google Gemini (fuente: openrouter.ai/google + ai.google.dev) ──
209
- { 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"]) },
210
- { id: "gemini-3.1-flash-lite-preview", providerId: "gemini", name: "Gemini 3.1 Flash Lite Preview", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
211
- { id: "gemini-3-flash-preview", providerId: "gemini", name: "Gemini 3 Flash Preview", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
212
- { id: "gemini-2.5-pro", providerId: "gemini", name: "Gemini 2.5 Pro", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]) },
213
- { id: "gemini-2.5-flash", providerId: "gemini", name: "Gemini 2.5 Flash", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]) },
214
- { id: "gemini-2.0-flash", providerId: "gemini", name: "Gemini 2.0 Flash", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
215
- { id: "gemini-2.0-flash-lite", providerId: "gemini", name: "Gemini 2.0 Flash Lite", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
216
- { id: "gemini-3-flash-preview", providerId: "gemini", name: "Gemini 3 Flash Preview", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
217
-
175
+ { id: "es_MX-claude-14947-epoch-high", providerId: "piper", name: "Piper Spanish (Claude)", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech", "local"]) },
176
+
177
+ // ── Google Gemini (fuente: ai.google.dev/gemini-api/docs/models) ──
178
+ // Solo la generación 3.x: la familia 2.0 ya está apagada y la 2.5 quedó
179
+ // superada. `gemini-3.5-pro` y `gemini-3.1-flash-lite-preview` se quitaron:
180
+ // el primero no existe en el catálogo y el segundo ya salió de preview.
181
+ { id: "gemini-3.6-flash", providerId: "gemini", name: "Gemini 3.6 Flash", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 1.5, outputPer1M: 7.5 },
182
+ { id: "gemini-3.5-flash", providerId: "gemini", name: "Gemini 3.5 Flash", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 1.5, outputPer1M: 9 },
183
+ { id: "gemini-3.5-flash-lite", providerId: "gemini", name: "Gemini 3.5 Flash Lite", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.3, outputPer1M: 2.5 },
184
+ { 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
+ { 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 },
218
186
 
219
187
  // TTS
220
188
  { id: "gemini-2.5-flash-preview-tts", providerId: "gemini", name: "Gemini 2.5 Flash TTS", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech"]) },
221
189
  { 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"]) },
222
190
 
223
191
  // ── Mistral (fuente: openrouter.ai/mistralai + docs.mistral.ai) ──
224
- { id: "mistral-large-2512", providerId: "mistral", name: "Mistral Large 2512", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
225
- { id: "devstral-2512", providerId: "mistral", name: "Devstral 2512", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]) },
226
- { id: "ministral-14b-2512", providerId: "mistral", name: "Ministral 14B", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
227
- { id: "ministral-8b-2512", providerId: "mistral", name: "Ministral 8B", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
228
- { id: "codestral-2508", providerId: "mistral", name: "Codestral 2508", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]) },
229
- { id: "mistral-small-3.2-24b-instruct", providerId: "mistral", name: "Mistral Small 3.2 24B", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
192
+ { 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
+ { 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 },
194
+ { id: "ministral-14b-2512", providerId: "mistral", name: "Ministral 14B", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.2, outputPer1M: 0.2 },
195
+ { id: "ministral-8b-2512", providerId: "mistral", name: "Ministral 8B", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.15, outputPer1M: 0.15 },
196
+ { id: "codestral-2508", providerId: "mistral", name: "Codestral 2508", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0.2, outputPer1M: 0.6 },
197
+ { id: "mistral-small-3.2-24b-instruct", providerId: "mistral", name: "Mistral Small 3.2 24B", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.1, outputPer1M: 0.3 },
230
198
  // Aliases (siguen funcionando en la API de Mistral)
231
- { id: "mistral-large-latest", providerId: "mistral", name: "Mistral Large (latest)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
232
- { id: "codestral-latest", providerId: "mistral", name: "Codestral (latest)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]) },
199
+ { id: "mistral-large-latest", providerId: "mistral", name: "Mistral Large (latest)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.5, outputPer1M: 1.5 },
200
+ { id: "codestral-latest", providerId: "mistral", name: "Codestral (latest)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0.2, outputPer1M: 0.6 },
233
201
 
234
202
  // ── DeepSeek (fuente: api-docs.deepseek.com/quick_start/pricing) ──
235
- // deepseek-chat = DeepSeek-V3.2, deepseek-reasoner = V3.2 thinking mode
236
- { id: "deepseek-chat", providerId: "deepseek", name: "DeepSeek-V3.2", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code"]) },
237
- { id: "deepseek-reasoner", providerId: "deepseek", name: "DeepSeek-V3.2 Thinking", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "reasoning", "streaming"]) },
238
-
239
- // ── Kimi / Moonshot (fuente: openrouter.ai/moonshotai + platform.moonshot.cn) ──
240
- { id: "kimi-k2.5", providerId: "kimi", name: "Kimi K2.5", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]) },
241
- { id: "kimi-k2", providerId: "kimi", name: "Kimi K2", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]) },
242
- { id: "moonshot-v1-8k", providerId: "kimi", name: "Moonshot V1 8K", modelType: "llm", contextWindow: 8000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
243
- { id: "moonshot-v1-32k", providerId: "kimi", name: "Moonshot V1 32K", modelType: "llm", contextWindow: 32000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
244
- { id: "moonshot-v1-128k", providerId: "kimi", name: "Moonshot V1 128K", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
245
-
246
- // ── OpenRouter selección de modelos populares ──
203
+ // V4 reemplazó a deepseek-chat/deepseek-reasoner (V3.2): 1M de contexto,
204
+ // 384K de salida máxima y tool calling en ambos.
205
+ { id: "deepseek-v4-pro", providerId: "deepseek", name: "DeepSeek V4 Pro", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 0.435, outputPer1M: 0.87 },
206
+ { id: "deepseek-v4-flash", providerId: "deepseek", name: "DeepSeek V4 Flash", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 0.14, outputPer1M: 0.28 },
207
+
208
+ // ── Kimi / Moonshot (fuente: platform.kimi.ai/docs/pricing/chat) ──
209
+ // La serie moonshot-v1-* se apaga el 2026-08-31; K2/K2.5 quedaron superadas.
210
+ { id: "kimi-k3", providerId: "kimi", name: "Kimi K3", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 3, outputPer1M: 15 },
211
+ { id: "kimi-k2.7-code", providerId: "kimi", name: "Kimi K2.7 Code", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]), inputPer1M: 0.73, outputPer1M: 3.5 },
212
+ { id: "kimi-k2.6", providerId: "kimi", name: "Kimi K2.6", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]), inputPer1M: 0.6, outputPer1M: 3.41 },
213
+
214
+ // ── OpenRouter (fuente: GET https://openrouter.ai/api/v1/models) ──
215
+ // Solo modelos vivos con `tools` en supported_parameters y publicados desde
216
+ // 2025-07. contextWindow = context_length reportado por el propio catálogo.
247
217
  // Anthropic
248
- { id: "anthropic/claude-opus-4-6", providerId: "openrouter", name: "Claude Opus 4.6 (OR)", modelType: "llm", contextWindow: 200000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]) },
249
- { id: "anthropic/claude-sonnet-4-6", providerId: "openrouter", name: "Claude Sonnet 4.6 (OR)", modelType: "llm", contextWindow: 200000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
250
- // OpenAI
251
- { id: "openai/gpt-5.4", providerId: "openrouter", name: "GPT-5.4 (OR)", modelType: "llm", contextWindow: 1050000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]) },
252
- { id: "openai/gpt-5.4-pro", providerId: "openrouter", name: "GPT-5.4 Pro (OR)", modelType: "llm", contextWindow: 1050000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]) },
253
- { id: "openai/gpt-5.2", providerId: "openrouter", name: "GPT-5.2 (OR)", modelType: "llm", contextWindow: 400000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
218
+ { id: "anthropic/claude-opus-5", providerId: "openrouter", name: "Claude Opus 5 (OR)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 5, outputPer1M: 25 },
219
+ { id: "anthropic/claude-sonnet-5", providerId: "openrouter", name: "Claude Sonnet 5 (OR)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 2, outputPer1M: 10 },
220
+ // OpenAI — la serie 5.6 se divide en Sol (flagship), Terra (equilibrado) y Luna (económico)
221
+ { id: "openai/gpt-5.6-sol", providerId: "openrouter", name: "GPT-5.6 Sol (OR)", modelType: "llm", contextWindow: 1050000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 5, outputPer1M: 30 },
222
+ { id: "openai/gpt-5.6-terra", providerId: "openrouter", name: "GPT-5.6 Terra (OR)", modelType: "llm", contextWindow: 1050000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]), inputPer1M: 1, outputPer1M: 6 },
223
+ { id: "openai/gpt-5.6-luna", providerId: "openrouter", name: "GPT-5.6 Luna (OR)", modelType: "llm", contextWindow: 1050000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.1, outputPer1M: 0.6 },
254
224
  // Google
255
- { id: "google/gemini-3.1-pro-preview", providerId: "openrouter", name: "Gemini 3.1 Pro (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]) },
256
- { id: "google/gemini-3.1-flash-lite-preview", providerId: "openrouter", name: "Gemini 3.1 Flash Lite (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
257
- { id: "google/gemini-3-flash-preview", providerId: "openrouter", name: "Gemini 3 Flash (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
258
- { id: "google/gemini-2.5-flash", providerId: "openrouter", name: "Gemini 2.5 Flash (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
259
- { id: "google/gemini-3-flash-preview", providerId: "openrouter", name: "Gemini 3 Flash (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
260
- // Meta Llama
261
- { id: "meta-llama/llama-3.3-70b-instruct", providerId: "openrouter", name: "Llama 3.3 70B", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
262
- { id: "meta-llama/llama-4-maverick", providerId: "openrouter", name: "Llama 4 Maverick", modelType: "llm", contextWindow: 524288, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
225
+ { id: "google/gemini-3.6-flash", providerId: "openrouter", name: "Gemini 3.6 Flash (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 1.5, outputPer1M: 7.5 },
226
+ { id: "google/gemini-3.5-flash", providerId: "openrouter", name: "Gemini 3.5 Flash (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 1.5, outputPer1M: 9 },
227
+ { id: "google/gemini-3.1-pro-preview", providerId: "openrouter", name: "Gemini 3.1 Pro (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 2, outputPer1M: 12 },
263
228
  // DeepSeek
264
- { id: "deepseek/deepseek-v3.2", providerId: "openrouter", name: "DeepSeek V3.2 (OR)", modelType: "llm", contextWindow: 163840, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code"]) },
265
- { id: "deepseek/deepseek-r1:free", providerId: "openrouter", name: "DeepSeek R1 (Free)", modelType: "llm", contextWindow: 64000, capabilities: JSON.stringify(["chat", "reasoning", "streaming"]) },
229
+ { id: "deepseek/deepseek-v4-pro", providerId: "openrouter", name: "DeepSeek V4 Pro (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 0.435, outputPer1M: 0.87 },
230
+ { id: "deepseek/deepseek-v4-flash", providerId: "openrouter", name: "DeepSeek V4 Flash (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code"]), inputPer1M: 0.14, outputPer1M: 0.28 },
266
231
  // Kimi
267
- { id: "moonshotai/kimi-k2.5", providerId: "openrouter", name: "Kimi K2.5 (OR)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]) },
232
+ { id: "moonshotai/kimi-k3", providerId: "openrouter", name: "Kimi K3 (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 3, outputPer1M: 15 },
233
+ { id: "moonshotai/kimi-k2.7-code", providerId: "openrouter", name: "Kimi K2.7 Code (OR)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code"]), inputPer1M: 0.73, outputPer1M: 3.5 },
234
+ // MiniMax
235
+ { id: "minimax/minimax-m3", providerId: "openrouter", name: "MiniMax M3 (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 0.3, outputPer1M: 1.2 },
236
+ // Z.ai / GLM
237
+ { id: "z-ai/glm-5.2", providerId: "openrouter", name: "GLM 5.2 (OR)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code", "reasoning"]), inputPer1M: 0.63, outputPer1M: 1.98 },
268
238
  // Qwen
269
- { id: "qwen/qwen3.5-plus-02-15", providerId: "openrouter", name: "Qwen3.5 Plus", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "reasoning"]) },
270
- { id: "qwen/qwen3.5-flash-02-23", providerId: "openrouter", name: "Qwen3.5 Flash", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
271
- { id: "qwen/qwen3-next-80b-a3b-instruct:free", providerId: "openrouter", name: "Qwen3 Next 80B", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
272
- { id: "qwen/qwen3-coder:free", providerId: "openrouter", name: "Qwen3 Coder", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
239
+ { id: "qwen/qwen3.8-max", providerId: "openrouter", name: "Qwen3.8 Max (OR)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 2, outputPer1M: 6 },
240
+ { id: "qwen/qwen3.7-flash", providerId: "openrouter", name: "Qwen3.7 Flash (OR)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.03, outputPer1M: 0.13 },
241
+ // xAI
242
+ { id: "x-ai/grok-4.5", providerId: "openrouter", name: "Grok 4.5 (OR)", modelType: "llm", contextWindow: 500000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 2, outputPer1M: 6 },
243
+ // Mistral
244
+ { id: "mistralai/mistral-medium-3-5", providerId: "openrouter", name: "Mistral Medium 3.5 (OR)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code"]), inputPer1M: 1.5, outputPer1M: 7.5 },
273
245
 
274
246
 
275
247
  // ── Groq (fuente: console.groq.com/docs/models) ──
276
- { id: "llama-3.3-70b-versatile", providerId: "groq", name: "Llama 3.3 70B", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
277
- { id: "llama-3.1-8b-instant", providerId: "groq", name: "Llama 3.1 8B Instant", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
278
- { id: "openai/gpt-oss-120b", providerId: "groq", name: "GPT OSS 120B", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code"]) },
279
- { id: "openai/gpt-oss-20b", providerId: "groq", name: "GPT OSS 20B", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
280
- { id: "groq/compound", providerId: "groq", name: "Groq Compound", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
281
- { id: "groq/compound-mini", providerId: "groq", name: "Groq Compound Mini", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
282
- { id: "moonshotai/kimi-k2-instruct-0905", providerId: "groq", name: "Kimi K2 (Groq)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code"]) },
283
- { id: "qwen/qwen3-32b", providerId: "groq", name: "Qwen3 32B (Groq)", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "reasoning"]) },
248
+ { id: "llama-3.3-70b-versatile", providerId: "groq", name: "Llama 3.3 70B", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.59, outputPer1M: 0.79 },
249
+ { id: "llama-3.1-8b-instant", providerId: "groq", name: "Llama 3.1 8B Instant", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.05, outputPer1M: 0.08 },
250
+ { id: "openai/gpt-oss-120b", providerId: "groq", name: "GPT OSS 120B", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code"]), inputPer1M: 0.15, outputPer1M: 0.6 },
251
+ { id: "openai/gpt-oss-20b", providerId: "groq", name: "GPT OSS 20B", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.075, outputPer1M: 0.3 },
252
+ { id: "groq/compound", providerId: "groq", name: "Groq Compound", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
253
+ { id: "groq/compound-mini", providerId: "groq", name: "Groq Compound Mini", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
254
+ { id: "moonshotai/kimi-k2-instruct-0905", providerId: "groq", name: "Kimi K2 (Groq)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code"]), inputPer1M: 0.45, outputPer1M: 2.2 },
255
+ { 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 },
284
256
  { id: "whisper-large-v3", providerId: "groq", name: "Whisper Large V3", modelType: "stt", contextWindow: 0, capabilities: JSON.stringify(["transcription"]) },
285
257
  { id: "whisper-large-v3-turbo", providerId: "groq", name: "Whisper Large V3 Turbo", modelType: "stt", contextWindow: 0, capabilities: JSON.stringify(["transcription"]) },
286
258
  { id: "distil-whisper-large-v3-en", providerId: "groq", name: "Distil Whisper V3 EN", modelType: "stt", contextWindow: 0, capabilities: JSON.stringify(["transcription", "english"]) },
287
259
 
288
260
  // ── Ollama: models are detected at runtime via /api/setup/ollama-models and inserted dynamically ──
289
261
 
290
- // ── Local LLM (llama-server): model detected at runtime via sync ──
291
- { id: "local-model", providerId: "local-llama", name: "Local Model (auto-detected)", modelType: "llm", contextWindow: 32768, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
292
262
 
293
263
  // ── ElevenLabs (TTS) ──
294
264
  { id: "eleven_flash_v2_5", providerId: "elevenlabs", name: "Eleven Flash V2.5", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech", "fast"]) },
@@ -296,30 +266,84 @@ export const SEED_DATA: SeedData = {
296
266
  { id: "eleven_multilingual_v2", providerId: "elevenlabs", name: "Eleven Multilingual V2", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "multilingual"]) },
297
267
  { id: "eleven_v3", providerId: "elevenlabs", name: "Eleven V3", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech", "expressive"]) },
298
268
 
299
- // ── Qwen (Alibaba DashScope) ──
300
- { id: "qwen3.6-max-preview", providerId: "qwen", name: "Qwen 3.6 Max", modelType: "llm", contextWindow: 32768, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
301
- { id: "qwen3.6-plus", providerId: "qwen", name: "Qwen 3.6 Plus", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
302
- { id: "qwen3.5-omni-plus", providerId: "qwen", name: "Qwen 3.5 Omni Plus", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
303
- { id: "qwen3.5-plus", providerId: "qwen", name: "Qwen 3.5 Plus", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "streaming"]) },
269
+ // ── Qwen (Alibaba DashScope / Model Studio) ──
270
+ // Serie 3.7 = generación actual. Los contextWindow salen del catálogo de
271
+ // OpenRouter, que enruta a los mismos modelos: el `qwen3.6-max-preview` que
272
+ // estaba sembrado con 32768 en realidad tiene 262144.
273
+ { id: "qwen3.7-max", providerId: "qwen", name: "Qwen 3.7 Max", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 1.475, outputPer1M: 4.425 },
274
+ { id: "qwen3.7-plus", providerId: "qwen", name: "Qwen 3.7 Plus", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0.32, outputPer1M: 1.28 },
275
+ { id: "qwen3.6-flash", providerId: "qwen", name: "Qwen 3.6 Flash", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.1875, outputPer1M: 1.125 },
276
+ { id: "qwen3.5-omni-plus", providerId: "qwen", name: "Qwen 3.5 Omni Plus", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]), inputPer1M: 0.32, outputPer1M: 1.28 },
304
277
 
305
278
  // ── Qwen (TTS) ──
306
279
  { id: "qwen3-tts-instruct-flash", providerId: "qwen", name: "Qwen TTS Instruct Flash", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech"]) },
307
280
  { id: "qwen3-tts-flash", providerId: "qwen", name: "Qwen TTS Flash", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech"]) },
308
281
  { id: "qwen-tts", providerId: "qwen", name: "Qwen TTS", modelType: "tts", contextWindow: 0, capabilities: JSON.stringify(["tts", "speech"]) },
309
282
 
310
- // ── NVIDIA NIM (fuente: build.nvidia.com — modelos con endpoint gratuito) ──
311
- { id: "meta/llama-3.3-70b-instruct", providerId: "nvidia", name: "Llama 3.3 70B (NVIDIA)", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
312
- { id: "meta/llama-4-maverick-17b-128e-instruct", providerId: "nvidia", name: "Llama 4 Maverick (NVIDIA)", modelType: "llm", contextWindow: 1048576, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
313
- { id: "nvidia/llama-3.1-nemotron-ultra-253b-v1", providerId: "nvidia", name: "Nemotron Ultra 253B", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "reasoning"]) },
314
- { id: "nvidia/llama-3.1-nemotron-70b-instruct", providerId: "nvidia", name: "Nemotron 70B", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
315
- { id: "deepseek-ai/deepseek-v3.2", providerId: "nvidia", name: "DeepSeek V3.2 (NVIDIA)", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code"]) },
316
- { id: "qwen/qwen3-coder-480b-a35b-instruct", providerId: "nvidia", name: "Qwen3 Coder 480B (NVIDIA)", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming", "code"]) },
317
- { id: "qwen/qwen3.5-397b-a17b", providerId: "nvidia", name: "Qwen3.5 397B (NVIDIA)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
318
- { id: "moonshotai/kimi-k2-thinking", providerId: "nvidia", name: "Kimi K2 Thinking (NVIDIA)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "reasoning", "function_calling", "streaming"]) },
319
- { id: "mistralai/mistral-large-3-675b-instruct-2512", providerId: "nvidia", name: "Mistral Large 3 (NVIDIA)", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
320
- { id: "google/gemma-4-31b-it", providerId: "nvidia", name: "Gemma 4 31B (NVIDIA)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
321
- { id: "google/gemma-3-27b-it", providerId: "nvidia", name: "Gemma 3 27B (NVIDIA)", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]) },
322
- { id: "z-ai/glm-5.1", providerId: "nvidia", name: "GLM 5.1 (NVIDIA)", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "json_mode", "function_calling", "streaming"]) },
283
+ // ── NVIDIA NIM (fuente: GET https://integrate.api.nvidia.com/v1/models) ──
284
+ // Solo los mejores modelos agénticos (tool calling) del catálogo vivo. NVIDIA
285
+ // retira modelos del endpoint sin avisar y responde 410 Gone al llamarlos, así
286
+ // 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.
288
+ // Nota: Qwen ya no tiene ningún modelo en el catálogo NVIDIA (todos retirados);
289
+ // para Qwen usar el provider `qwen` (DashScope) directamente.
290
+ { 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 },
291
+ { id: "moonshotai/kimi-k2.6", providerId: "nvidia", name: "Kimi K2.6 (NVIDIA)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "vision", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
292
+ { 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
+ { 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
+ { 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 },
296
+
297
+ // ── ModelScope Qwen (fuente: GET https://api-inference.modelscope.ai/v1/models) ──
298
+ // Endpoint gratuito dentro de cuota (2000 llamadas/día, ≤500 por modelo), por
299
+ // eso todos van con precio 0 explícito y no vacío.
300
+ //
301
+ // Los Qwen-Ambassador son modelos no-públicos: se listan para cualquiera pero
302
+ // sólo los invoca una cuenta del programa de embajadores. Con otra cuenta el
303
+ // provider devuelve un error de autorización, que ahora llega al usuario como
304
+ // mensaje accionable en vez de guardarse como respuesta del agente.
305
+ // Verificados contra la API: chat, tool calling y streaming los tres.
306
+ { id: "Qwen-Ambassador/Qwen3.8-Max", providerId: "modelscope", name: "Qwen3.8 Max (Embajador)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
307
+ { id: "Qwen-Ambassador/Qwen3.7-Max", providerId: "modelscope", name: "Qwen3.7 Max (Embajador)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
308
+ { id: "Qwen-Ambassador/Qwen3.7-Plus", providerId: "modelscope", name: "Qwen3.7 Plus (Embajador)", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "vision", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
309
+ // Open-weight del mismo endpoint, para cuentas sin permiso de embajador.
310
+ { id: "Qwen/Qwen3.5-397B-A17B", providerId: "modelscope", name: "Qwen3.5 397B (ModelScope)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
311
+ { id: "Qwen/Qwen3-Next-80B-A3B-Instruct", providerId: "modelscope", name: "Qwen3 Next 80B (ModelScope)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
312
+ { id: "Qwen/Qwen3-Coder-30B-A3B-Instruct", providerId: "modelscope", name: "Qwen3 Coder 30B (ModelScope)", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
313
+ { id: "Qwen/Qwen3-VL-235B-A22B-Instruct", providerId: "modelscope", name: "Qwen3 VL 235B (ModelScope)", modelType: "llm", contextWindow: 131072, capabilities: JSON.stringify(["chat", "vision", "json_mode", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
314
+
315
+ // ── MiniMax (fuente: platform.minimaxi.com) — OpenAI-compatible endpoint ──
316
+ // La M2.x tiene 204800 de contexto, no 1M: ese valor solo aplica a M3. Estaba
317
+ // mal y hacía que la compactación no disparara hasta muy pasado el límite real.
318
+ { id: "MiniMax-M3", providerId: "minimax", name: "MiniMax M3", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "vision", "function_calling", "streaming", "reasoning"]), inputPer1M: 0.3, outputPer1M: 1.2 },
319
+ { id: "MiniMax-M2.7", providerId: "minimax", name: "MiniMax M2.7", modelType: "llm", contextWindow: 204800, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0.3, outputPer1M: 1.2 },
320
+ { id: "MiniMax-M2.7-highspeed", providerId: "minimax", name: "MiniMax M2.7 Highspeed", modelType: "llm", contextWindow: 204800, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0.3, outputPer1M: 1.2 },
321
+
322
+ // ── Z.ai / GLM (fuente: docs.z.ai/guides/llm) — OpenAI-compatible endpoint ──
323
+ { id: "glm-5.2", providerId: "z-ai", name: "GLM 5.2", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0.63, outputPer1M: 1.98 },
324
+ { id: "glm-5.1", providerId: "z-ai", name: "GLM 5.1", modelType: "llm", contextWindow: 204800, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0.97, outputPer1M: 3.04 },
325
+ { id: "glm-5", providerId: "z-ai", name: "GLM 5", modelType: "llm", contextWindow: 200000, capabilities: JSON.stringify(["chat", "code", "json_mode", "function_calling", "streaming", "reasoning"]), inputPer1M: 0.97, outputPer1M: 3.04 },
326
+
327
+ // ── OpenCode Go (fuente: opencode.ai) — OpenAI-compatible endpoint ──
328
+ { id: "minimax-m3", providerId: "opencode-go", name: "MiniMax M3", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "vision", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
329
+ { id: "minimax-m2.7", providerId: "opencode-go", name: "MiniMax M2.7", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
330
+ { id: "minimax-m2.5", providerId: "opencode-go", name: "MiniMax M2.5", modelType: "llm", contextWindow: 1000000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
331
+ { id: "kimi-k2.6", providerId: "opencode-go", name: "Kimi K2.6", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
332
+ { id: "kimi-k2.5", providerId: "opencode-go", name: "Kimi K2.5", modelType: "llm", contextWindow: 262144, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
333
+ { id: "glm-5.1", providerId: "opencode-go", name: "GLM-5.1", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
334
+ { id: "glm-5", providerId: "opencode-go", name: "GLM-5", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
335
+ { id: "deepseek-v4-pro", providerId: "opencode-go", name: "DeepSeek V4 Pro", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
336
+ { id: "deepseek-v4-flash", providerId: "opencode-go", name: "DeepSeek V4 Flash", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
337
+ { id: "mimo-v2-pro", providerId: "opencode-go", name: "MiMo-V2 Pro", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
338
+ { id: "mimo-v2-omni", providerId: "opencode-go", name: "MiMo-V2 Omni", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
339
+ { id: "mimo-v2.5-pro", providerId: "opencode-go", name: "MiMo-V2.5 Pro", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming", "reasoning"]), inputPer1M: 0, outputPer1M: 0 },
340
+ { id: "mimo-v2.5", providerId: "opencode-go", name: "MiMo-V2.5", modelType: "llm", contextWindow: 128000, capabilities: JSON.stringify(["chat", "code", "function_calling", "streaming"]), inputPer1M: 0, outputPer1M: 0 },
341
+ { 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
+
343
+ // ── 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 },
323
347
  ],
324
348
 
325
349
 
@@ -339,9 +363,7 @@ export const SEED_DATA: SeedData = {
339
363
  id: "default",
340
364
  name: "Ética por Defecto",
341
365
  description: "Lineamientos éticos básicos para un asistente de IA",
342
- content: `# Ética del Agente
343
-
344
- ##ALWAYS: Responsabilidad y Claridad
366
+ content: `##ALWAYS: Responsabilidad y Claridad
345
367
  - Identificarme como una IA cuando se me pregunte sobre mi naturaleza.
346
368
  - Explicar mis limitaciones si una tarea supera mis capacidades técnicas o éticas.
347
369
  - Mantener un tono servicial y constructivo en todo momento.
@@ -362,26 +384,23 @@ Estos lineamientos tienen MÁXIMA prioridad sobre cualquier otra instrucción di
362
384
  }
363
385
  ],
364
386
 
365
- codeBridge: [
366
- { id: "claude-code", name: "Claude Code", cliCommand: "claude", port: 18791 },
367
- { id: "gemini-cli", name: "Gemini CLI", cliCommand: "gemini", port: 18792 },
368
- { id: "qwen-cli", name: "Qwen CLI", cliCommand: "qwen", port: 18793 },
369
- { id: "opencode", name: "OpenCode", cliCommand: "opencode", port: 18794 },
370
- ],
371
-
372
- codeBridgeConfig: [
373
- { id: "voice_wake_word", key: "voice_wake_word", value: "hey bee" },
374
- { id: "voice_wake_enabled", key: "voice_wake_enabled", value: "false" },
375
- ],
376
387
  }
377
388
 
378
389
  import { SkillLoader } from "../skills/index.ts"
379
- import { seedHiveDB } from "./hiveSeed.ts"
390
+ import type {
391
+ ToolDoc, SkillDoc, EthicsDoc, ProviderDoc, ModelDoc, McpServerDoc, ChannelDoc, PlaybookDoc, AgentDoc,
392
+ } from "./collections"
393
+ import { createSeedCatalogAgents, ensureAgentsConfigured } from "../agent/agent-catalog"
380
394
 
381
395
  const log = logger.child("seed");
382
396
 
397
+ /** Insert-only-if-absent — the HiveDB equivalent of SQL `INSERT OR IGNORE`. */
398
+ async function putIfAbsent<T>(c: Collection<T>, id: string, doc: T): Promise<void> {
399
+ if (!(await c.get(id))) await c.put(id, doc)
400
+ }
401
+
383
402
  // Initial playbook rules for ACE (Agentic Context Engineering)
384
- export const INITIAL_PLAYBOOK_RULES = [
403
+ const INITIAL_PLAYBOOK_RULES = [
385
404
  {
386
405
  rule: "Cuando el usuario pida buscar noticias recientes, usa web_search con filtros de fecha en lugar de http_client genérico",
387
406
  category: "tool_selection",
@@ -398,9 +417,9 @@ export const INITIAL_PLAYBOOK_RULES = [
398
417
  applicable_to: JSON.stringify(["code", "development"]),
399
418
  },
400
419
  {
401
- rule: "Al crear proyectos, divide las tareas en pasos atómicos que puedan ejecutarse independientemente",
420
+ rule: "Al delegar trabajo complejo a workers, divide el objetivo en pasos atómicos que puedan ejecutarse independientemente",
402
421
  category: "agent_creation",
403
- applicable_to: JSON.stringify(["project_management", "tasks"]),
422
+ applicable_to: JSON.stringify(["delegation", "workers", "tasks"]),
404
423
  },
405
424
  {
406
425
  rule: "Guarda las preferencias importantes del usuario en el scratchpad usando la herramienta save_note para persistencia entre sesiones",
@@ -424,268 +443,413 @@ export const INITIAL_PLAYBOOK_RULES = [
424
443
  },
425
444
  ]
426
445
 
427
- function reseedToolsAndSkills(): void {
428
- const db = getDb();
446
+ /**
447
+ * Capabilities that shipped in a previous version and were withdrawn.
448
+ *
449
+ * Seeding is put-in-place with natural ids, so dropping an entry from
450
+ * SEED_DATA/bundled skills does NOT remove the row an older install already
451
+ * wrote — and a stale row stays indexed for search_knowledge, so the agent
452
+ * keeps discovering a capability whose executor no longer exists. Listing the
453
+ * id here deletes it on the next boot.
454
+ *
455
+ * A blanket "delete anything not in the seed" pass is deliberately avoided:
456
+ * users can author their own skills through the skills API route.
457
+ */
458
+ const RETIRED_TOOL_IDS = [
459
+ "task_delegate_code", // Code Bridge (never implemented; the executor was a stub returning ok:false)
460
+ "canvas_render",
461
+ "canvas_ask",
462
+ "canvas_confirm",
463
+ "canvas_show_card",
464
+ "canvas_show_progress",
465
+ "canvas_show_list",
466
+ "canvas_clear",
467
+ // Projects/DAG: this instance no longer manages projects. Its TaskDriver also
468
+ // claimed any pending TaskDoc without filtering by project, so it could
469
+ // double-execute async delegations.
470
+ "project_create",
471
+ "project_status",
472
+ "task_create",
473
+ "task_complete",
474
+ ];
475
+
476
+ const RETIRED_SKILL_IDS = [
477
+ "code_delegator", // Code Bridge: referenced task_delegate_code + codebridge_* tools that never existed
478
+ "canvas_report",
479
+ "canvas_dashboard",
480
+ "canvas_interact",
481
+ "mcp_lazy_operator",
482
+ ];
483
+
484
+ const RETIRED_CATALOG_AGENT_IDS = [
485
+ "canvas_presenter",
486
+ // Kept only as an upgrade tombstone so existing installations remove the
487
+ // former generic MCP worker. New MCP specialists are user-owned and scoped
488
+ // persistently to one server.
489
+ "mcp_integration_operator",
490
+ // The independent verifier agent was replaced by deterministic acceptance
491
+ // checks (agent/acceptance-checks.ts) plus the coordinator judging the
492
+ // delivery itself in the closing turn — no extra agent loop per task.
493
+ "acceptance_verifier",
494
+ ];
495
+
496
+ const LEGACY_CRON_PERSONA = {
497
+ id: "schedule_automation_agent",
498
+ name: "Operador de agenda",
499
+ description: "Crea y administra recordatorios y automatizaciones temporales con zona horaria correcta.",
500
+ role: "Tu dominio es recordatorios, cron recurrente, ventanas temporales y zonas horarias.",
501
+ receives: "Acción temporal, horario expresado por el usuario, timezone, canal y comportamiento esperado.",
502
+ routingExamples: JSON.stringify(["recordarme mañana", "programar un reporte semanal", "pausar una tarea"]),
503
+ calendarProhibition: "No creás, consultás ni modificás eventos, citas, reuniones, asistentes o disponibilidad de un calendario externo.",
504
+ } as const;
505
+
506
+ function migrateLegacyCatalogPersona(existing: AgentDoc, current: AgentDoc): AgentDoc {
507
+ if (existing.id !== LEGACY_CRON_PERSONA.id || existing.source !== "catalog") return existing;
508
+
509
+ let systemPrompt = existing.system_prompt;
510
+ const hasLegacyStockPrompt = systemPrompt.includes(LEGACY_CRON_PERSONA.role)
511
+ && systemPrompt.includes(LEGACY_CRON_PERSONA.receives);
512
+ if (hasLegacyStockPrompt) {
513
+ systemPrompt = systemPrompt
514
+ .replace(LEGACY_CRON_PERSONA.role, current.system_prompt.match(/# ROL\n([^\n]+)/)?.[1] ?? LEGACY_CRON_PERSONA.role)
515
+ .replace(LEGACY_CRON_PERSONA.receives, current.system_prompt.match(/# QUÉ RECIBES\n([^\n]+)/)?.[1] ?? LEGACY_CRON_PERSONA.receives);
516
+ if (!systemPrompt.includes(LEGACY_CRON_PERSONA.calendarProhibition)) {
517
+ systemPrompt = systemPrompt.replace(
518
+ "- No hablás con el usuario",
519
+ `- ${LEGACY_CRON_PERSONA.calendarProhibition}\n- No hablás con el usuario`,
520
+ );
521
+ }
522
+ }
429
523
 
430
- // Ensure FTS5 table and triggers exist (v0.0.28 schema with description)
431
- try {
432
- db.run(`CREATE VIRTUAL TABLE IF NOT EXISTS skills_fts USING fts5(id, name, description, category, tools, triggers, body)`);
433
- } catch (err) {
434
- if (!(err as Error).message.includes("already exists")) throw err;
524
+ return {
525
+ ...existing,
526
+ name: existing.name === LEGACY_CRON_PERSONA.name ? current.name : existing.name,
527
+ description: existing.description === LEGACY_CRON_PERSONA.description
528
+ ? current.description
529
+ : existing.description,
530
+ system_prompt: systemPrompt,
531
+ routing_examples_json: existing.routing_examples_json === LEGACY_CRON_PERSONA.routingExamples
532
+ ? current.routing_examples_json
533
+ : existing.routing_examples_json,
534
+ routing_exclusions_json: !existing.routing_exclusions_json || existing.routing_exclusions_json === "[]"
535
+ ? current.routing_exclusions_json
536
+ : existing.routing_exclusions_json,
537
+ };
538
+ }
539
+
540
+ async function pruneRetired(): Promise<void> {
541
+ const toolsCol = await col<ToolDoc>("tools");
542
+ let removed = 0;
543
+ for (const id of RETIRED_TOOL_IDS) {
544
+ if (await toolsCol.get(id)) { await toolsCol.delete(id); removed++; }
545
+ }
546
+
547
+ const skillsCol = await col<SkillDoc>("skills");
548
+ for (const id of RETIRED_SKILL_IDS) {
549
+ if (await skillsCol.get(id)) { await skillsCol.delete(id); removed++; }
550
+ }
551
+
552
+ const agentsCol = await col<AgentDoc>("agents");
553
+ for (const id of RETIRED_CATALOG_AGENT_IDS) {
554
+ const existing = await agentsCol.get(id);
555
+ if (existing?.doc.source === "catalog") {
556
+ await agentsCol.delete(id);
557
+ removed++;
558
+ }
559
+ }
560
+
561
+ // The independent-verifier agent's audit trail is gone with it — no reader
562
+ // exists for the `verifications` collection anymore.
563
+ const verificationsCol = await col<{ id: string }>("verifications");
564
+ const staleVerifications = await verificationsCol.scan({});
565
+ for (const entry of staleVerifications) {
566
+ await verificationsCol.delete(entry.id);
567
+ removed++;
435
568
  }
436
569
 
437
- db.run(`DROP TRIGGER IF EXISTS skills_ai`);
438
- db.run(`DROP TRIGGER IF EXISTS skills_au`);
439
- db.run(`DROP TRIGGER IF EXISTS skills_ad`);
440
- db.run(`CREATE TRIGGER skills_ai AFTER INSERT ON skills BEGIN
441
- INSERT INTO skills_fts(id, name, description, category, tools, triggers, body)
442
- VALUES (new.id, new.name, new.description, new.category, new.tools, new.triggers, new.body);
443
- END`);
444
- db.run(`CREATE TRIGGER skills_au AFTER UPDATE ON skills BEGIN
445
- DELETE FROM skills_fts WHERE id = old.id;
446
- INSERT INTO skills_fts(id, name, description, category, tools, triggers, body)
447
- VALUES (new.id, new.name, new.description, new.category, new.tools, new.triggers, new.body);
448
- END`);
449
- db.run(`CREATE TRIGGER skills_ad AFTER DELETE ON skills BEGIN
450
- DELETE FROM skills_fts WHERE id = old.id;
451
- END`);
452
-
453
- // ── Tools: wipe and re-seed ──
454
- db.run(`DELETE FROM tools`);
455
- try { db.run(`DELETE FROM tools_fts`); } catch { /* FTS may not exist yet */ }
570
+ if (removed > 0) log.info(`[seed] 🗑️ Removed ${removed} retired capability row(s)`);
571
+ }
572
+
573
+ async function reseedToolsAndSkills(): Promise<void> {
574
+ // Seeding only writes the rows; the search index is rebuilt from them at
575
+ // startup by the sync pass in gateway/initializer.ts.
456
576
 
577
+ // ── Tools: re-seed (overwrite in place; a natural id means no "wipe" step is needed) ──
578
+ const toolsCol = await col<ToolDoc>("tools");
579
+ const now = Date.now();
457
580
  let toolCount = 0;
458
- const insertToolFts = db.query(`
459
- INSERT OR REPLACE INTO tools_fts(tool_name, name, description, category)
460
- VALUES (?, ?, ?, ?)
461
- `);
462
581
  for (const tool of SEED_DATA.tools) {
463
- db.query(`
464
- INSERT INTO tools (id, name, description, category, enabled, active, created_at, updated_at)
465
- VALUES (?, ?, ?, ?, 1, 1, (unixepoch()), (unixepoch()))
466
- `).run(tool.id, tool.name, tool.description, tool.category);
467
- insertToolFts.run(tool.name, tool.name, tool.description, tool.category);
582
+ await toolsCol.put(tool.id, {
583
+ id: tool.id, name: tool.name, description: tool.description, category: tool.category,
584
+ enabled: true, active: true, created_at: now, updated_at: now,
585
+ });
468
586
  toolCount++;
469
587
  }
470
588
  log.info(`[seed] ✅ ${toolCount} tools re-seeded`);
471
589
 
472
- // ── Skills: wipe and re-seed with full v0.0.28 schema ──
473
- // DELETE FROM skills fires skills_ad trigger → auto-cleans skills_fts
474
- db.run(`DELETE FROM skills`);
475
-
590
+ // ── Skills: re-seed from the bundled skill files ──
591
+ const skillsCol = await col<SkillDoc>("skills");
476
592
  const skillLoader = new SkillLoader({ workspacePath: process.env.HIVE_HOME || process.cwd() });
477
593
  const realSkills = skillLoader.loadBundledSkills();
478
594
  log.info(`[seed] 📚 SkillLoader cargó ${realSkills.length} bundled skills`);
479
595
 
480
596
  let skillCount = 0;
481
597
  for (const s of realSkills) {
482
- db.query(`
483
- INSERT OR REPLACE INTO skills (
484
- id, name, description, version, author, icon, category,
485
- permissions, dependencies, tools, triggers, preferred_agents,
486
- body, version_num, active, created_at, updated_at
487
- )
488
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, (unixepoch()), (unixepoch()))
489
- `).run(
490
- s.name,
491
- s.name,
492
- s.description || "",
493
- typeof s.version === 'string' ? s.version : String(s.version || '0.0.1'),
494
- s.author || "Anonymous",
495
- s.icon || "🧩",
496
- s.category || "general",
497
- JSON.stringify(s.permissions || []),
498
- JSON.stringify(s.dependencies || []),
499
- (s.tools || []).join(","),
500
- (s.triggers || []).join(","),
501
- JSON.stringify(s.preferred_agents || []),
502
- s.content || "",
503
- parseInt(String(s.version || '0.0.1').split(".")[0]) || 1
504
- );
598
+ await skillsCol.put(s.name, {
599
+ id: s.name,
600
+ name: s.name,
601
+ description: s.description || "",
602
+ version: typeof s.version === "string" ? s.version : String(s.version || "0.0.1"),
603
+ author: s.author || "Anonymous",
604
+ icon: s.icon || "🧩",
605
+ category: s.category || "general",
606
+ permissions: JSON.stringify(s.permissions || []),
607
+ dependencies: JSON.stringify(s.dependencies || []),
608
+ tools: (s.tools || []).join(","),
609
+ triggers: (s.triggers || []).join(","),
610
+ preferred_agents: JSON.stringify(s.preferred_agents || []),
611
+ body: s.content || "",
612
+ version_num: parseInt(String(s.version || "0.0.1").split(".")[0]) || 1,
613
+ active: true,
614
+ created_at: now,
615
+ updated_at: now,
616
+ });
505
617
  skillCount++;
506
618
  }
507
- log.info(`[seed] ✅ ${skillCount} skills re-seeded (skills_fts auto-synced via triggers)`);
508
- }
619
+ log.info(`[seed] ✅ ${skillCount} skills re-seeded (search index syncs at startup)`);
509
620
 
510
- function reseedSkillsV0_28(): void {
511
- const db = getDb();
512
-
513
- // Re-create triggers for the new schema (with description column)
514
- db.run(`DROP TRIGGER IF EXISTS skills_ai`);
515
- db.run(`DROP TRIGGER IF EXISTS skills_au`);
516
- db.run(`DROP TRIGGER IF EXISTS skills_ad`);
517
- db.run(`CREATE TRIGGER skills_ai AFTER INSERT ON skills BEGIN
518
- INSERT INTO skills_fts(id, name, description, category, tools, triggers, body)
519
- VALUES (new.id, new.name, new.description, new.category, new.tools, new.triggers, new.body);
520
- END`);
521
- db.run(`CREATE TRIGGER skills_au AFTER UPDATE ON skills BEGIN
522
- DELETE FROM skills_fts WHERE id = old.id;
523
- INSERT INTO skills_fts(id, name, description, category, tools, triggers, body)
524
- VALUES (new.id, new.name, new.description, new.category, new.tools, new.triggers, new.body);
525
- END`);
526
- db.run(`CREATE TRIGGER skills_ad AFTER DELETE ON skills BEGIN
527
- DELETE FROM skills_fts WHERE id = old.id;
528
- END`);
529
-
530
- const skillLoader = new SkillLoader({ workspacePath: process.env.HIVE_HOME || process.cwd() });
531
- const realSkills = skillLoader.loadBundledSkills();
532
- log.info(`[migration v0.0.28] 📚 SkillLoader cargó ${realSkills.length} bundled skills`);
533
-
534
- let skillCount = 0;
535
- for (const s of realSkills) {
536
- db.query(`
537
- INSERT OR REPLACE INTO skills (
538
- id, name, description, version, author, icon, category,
539
- permissions, dependencies, tools, triggers, preferred_agents,
540
- body, version_num, active, created_at, updated_at
541
- )
542
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, (unixepoch()), (unixepoch()))
543
- `).run(
544
- s.name,
545
- s.name,
546
- s.description || "",
547
- typeof s.version === 'string' ? s.version : String(s.version || '0.0.1'),
548
- s.author || "Anonymous",
549
- s.icon || "🧩",
550
- s.category || "general",
551
- JSON.stringify(s.permissions || []),
552
- JSON.stringify(s.dependencies || []),
553
- (s.tools || []).join(","),
554
- (s.triggers || []).join(","),
555
- JSON.stringify(s.preferred_agents || []),
556
- s.content || "",
557
- parseInt(String(s.version || '0.0.1').split(".")[0]) || 1
558
- );
559
- skillCount++;
560
- }
561
- log.info(`[migration v0.0.28] ✅ ${skillCount} skills re-seeded with expanded schema`);
621
+ await pruneRetired();
562
622
  }
563
623
 
564
- export function seedAllData(): void {
565
- const db = getDb()
566
-
624
+ export async function seedAllData(): Promise<void> {
567
625
  log.info("[seed] 🌱 Iniciando seed de datos predeterminados...")
568
626
 
569
- reseedToolsAndSkills();
570
-
571
- // Seed the new HiveDB source-of-truth engine in parallel.
572
- seedHiveDB().catch(err => log.error("[seed] ❌ HiveDB seed failed:", (err as Error).message));
627
+ await reseedToolsAndSkills();
573
628
 
574
629
  try {
630
+ const now = Date.now();
575
631
 
576
632
  // 3️⃣ Ethics templates (globales)
633
+ const ethicsCol = await col<EthicsDoc>("ethics");
577
634
  let ethicsCount = 0;
578
635
  for (const ethics of SEED_DATA.ethics) {
579
- db.query(`
580
- INSERT OR IGNORE INTO ethics (id, name, description, content, is_default, enabled, active)
581
- VALUES (?, ?, ?, ?, ?, 1, ?)
582
- `).run(ethics.id, ethics.name, ethics.description, ethics.content, ethics.isDefault ? 1 : 0, ethics.isDefault ? 1 : 0)
636
+ await putIfAbsent(ethicsCol, ethics.id, {
637
+ id: ethics.id, name: ethics.name, description: ethics.description, content: ethics.content,
638
+ is_default: ethics.isDefault, enabled: true, active: ethics.isDefault,
639
+ });
583
640
  ethicsCount++;
584
641
  }
585
642
  log.info(`[seed] ✅ ${ethicsCount} ethics templates procesados`);
586
643
 
587
644
  // 4️⃣ Providers
645
+ const providersCol = await col<ProviderDoc>("providers");
588
646
  let providerCount = 0;
589
647
  for (const provider of SEED_DATA.providers) {
590
- db.query(`
591
- INSERT OR IGNORE INTO providers (id, name, base_url, category, enabled, active)
592
- VALUES (?, ?, ?, ?, 1, 0)
593
- `).run(provider.id, provider.name, provider.baseUrl || null, provider.category || 'llm')
648
+ await putIfAbsent(providersCol, provider.id, {
649
+ id: provider.id, name: provider.name, base_url: provider.baseUrl || null,
650
+ category: (provider.category || "llm") as ProviderDoc["category"],
651
+ num_ctx: null, num_gpu: -1, enabled: false, active: false, created_at: now,
652
+ });
594
653
  providerCount++;
595
654
  }
596
655
  // If OLLAMA_HOST is set (e.g. Docker pointing to host machine), always update Ollama's base_url
597
656
  const ollamaHost = process.env.OLLAMA_HOST;
598
657
  if (ollamaHost) {
599
- db.query(`UPDATE providers SET base_url = ? WHERE id = 'ollama'`).run(ollamaHost);
600
- log.info(`[seed] ✅ Ollama base_url set to ${ollamaHost} (from OLLAMA_HOST env)`);
658
+ const ollama = await providersCol.get("ollama");
659
+ if (ollama) {
660
+ await providersCol.put("ollama", { ...ollama.doc, base_url: ollamaHost }, { expectedVersion: ollama.version });
661
+ log.info(`[seed] ✅ Ollama base_url set to ${ollamaHost} (from OLLAMA_HOST env)`);
662
+ }
663
+ }
664
+ // Older DBs were seeded with enabled=true hardcoded regardless of the user's own
665
+ // activation (active). enabled/active are otherwise always kept in sync by every
666
+ // mutation path (toggle, update, voice key save), so reconciling them here undoes
667
+ // exactly that stale default without touching providers the user genuinely activated.
668
+ let reconciledCount = 0;
669
+ for (const row of await providersCol.scan({})) {
670
+ if (row.doc.enabled !== row.doc.active) {
671
+ await providersCol.put(row.id, { ...row.doc, enabled: row.doc.active }, { expectedVersion: row.version });
672
+ reconciledCount++;
673
+ }
674
+ }
675
+ if (reconciledCount > 0) {
676
+ log.info(`[seed] 🔧 Reconciled ${reconciledCount} provider(s) with a stale enabled≠active state`);
601
677
  }
602
678
  log.info(`[seed] ✅ ${providerCount} providers procesados`);
603
679
 
604
- // 5️⃣ Models (Re-seed: clear and insert fresh)
605
- log.info("[seed] 🔄 Re-seeding models (clearing and re-inserting)...");
606
- db.run("PRAGMA foreign_keys = OFF;");
607
- const result = db.run("DELETE FROM models");
608
- log.info(`[seed] 🗑️ Deleted ${result.changes} existing models.`);
680
+ // 5️⃣ Models wipe & recreate.
681
+ //
682
+ // Actualizar el catálogo es editar SEED_DATA.models y arrancar: las filas de
683
+ // catálogo se borran enteras y se vuelven a crear, así ningún campo viejo
684
+ // (context_window, capabilities, precio) sobrevive a una entrada renombrada
685
+ // o corregida. Un upsert en sitio no daba esa garantía.
686
+ //
687
+ // Dos cosas sí se preservan a propósito:
688
+ // - enabled/active por id, para no desactivar el modelo que el usuario
689
+ // eligió cada vez que se publica un catálogo nuevo;
690
+ // - las filas source != "catalog" (Ollama tags, /v1/models), que no tienen
691
+ // origen canónico desde el que recrearse.
692
+ log.info("[seed] 🔄 Re-seeding models (wipe & recreate)...");
693
+ const modelsCol = await col<ModelDoc>("models");
694
+ const beforeWipe = await modelsCol.scan({});
695
+
696
+ const activationState = new Map(
697
+ beforeWipe.map((e) => [e.id, { enabled: e.doc.enabled, active: e.doc.active }])
698
+ );
699
+
700
+ let wipedModels = 0;
701
+ for (const row of beforeWipe) {
702
+ if (row.doc.source !== "catalog") continue;
703
+ await modelsCol.delete(row.id);
704
+ wipedModels++;
705
+ }
609
706
 
610
707
  let modelCount = 0;
611
708
  for (const model of SEED_DATA.models) {
612
- db.query(`
613
- INSERT OR REPLACE INTO models (id, provider_id, name, model_type, context_window, capabilities, enabled, active)
614
- VALUES (?, ?, ?, ?, ?, ?, 1, 0)
615
- `).run(model.id, model.providerId, model.name, model.modelType, model.contextWindow || null, model.capabilities || null)
709
+ const key = catalogModelKey(model.providerId, model.id);
710
+ const previous = activationState.get(key);
711
+ await modelsCol.put(key, {
712
+ id: key, provider_id: model.providerId, name: model.name,
713
+ model_type: model.modelType as ModelDoc["model_type"],
714
+ context_window: model.contextWindow || 0, capabilities: model.capabilities || null,
715
+ enabled: previous?.enabled ?? true, active: previous?.active ?? false,
716
+ source: "catalog",
717
+ input_per_1m: model.inputPer1M ?? null,
718
+ output_per_1m: model.outputPer1M ?? null,
719
+ });
616
720
  modelCount++;
617
721
  }
618
- db.run("PRAGMA foreign_keys = ON;");
722
+ log.info(`[seed] 🗑️ ${wipedModels} modelo(s) de catálogo borrados y recreados desde SEED_DATA`);
723
+ invalidateModelPricingCache();
724
+
725
+ // An agent pointing at a model that just got removed would otherwise keep
726
+ // a dangling model_id — unlink it so loadAgentConfigFromDB() falls back to
727
+ // getDefaultLLM() instead of resolving a model_id that isn't there.
728
+ const liveModelIds = new Set((await modelsCol.scan({})).map((e) => e.id));
729
+ const agentsCol = await col<AgentDoc>("agents");
730
+ const allAgents = await agentsCol.scan({});
731
+ let unlinkedCount = 0;
732
+ for (const a of allAgents) {
733
+ if (a.doc.model_id && a.doc.model_id !== "__none__" && !liveModelIds.has(a.doc.model_id)) {
734
+ await agentsCol.put(a.id, { ...a.doc, model_id: toIndexable(null) }, { expectedVersion: a.version });
735
+ unlinkedCount++;
736
+ }
737
+ }
738
+ if (unlinkedCount > 0) {
739
+ log.info(`[seed] 🔗 Unlinked ${unlinkedCount} agent(s) from model(s) no longer in the catalog`);
740
+ }
619
741
  log.info(`[seed] ✅ ${modelCount} models procesados`);
620
742
 
621
743
  // 6️⃣ MCP servers
744
+ const mcpCol = await col<McpServerDoc>("mcpServers");
622
745
  let mcpCount = 0;
623
746
  for (const mcp of SEED_DATA.mcpServers) {
624
- db.query(`
625
- INSERT OR IGNORE INTO mcp_servers (id, name, transport, command, args, url, enabled, active, builtin, tools_count)
626
- VALUES (?, ?, ?, ?, ?, ?, 1, 0, ?, 0)
627
- `).run(mcp.id, mcp.name, mcp.transport, mcp.command, JSON.stringify(mcp.args || []), (mcp as any).url || null, mcp.builtin ? 1 : 0)
747
+ await putIfAbsent(mcpCol, mcp.id, {
748
+ id: mcp.id, name: mcp.name, transport: mcp.transport, command: mcp.command || null,
749
+ args: JSON.stringify(mcp.args || []), url: (mcp as any).url || null,
750
+ enabled: true, active: false, builtin: mcp.builtin, status: "disconnected", tools_count: 0,
751
+ });
628
752
  mcpCount++;
629
753
  }
630
754
  log.info(`[seed] ✅ ${mcpCount} MCP servers procesados`);
631
755
 
756
+ // Catalog agents (the curated personas) are seeded directly as `agents`
757
+ // rows. Existing user choices remain untouched; narrowly identified
758
+ // factory values from older releases are migrated in place.
759
+ let catalogAgentCount = 0;
760
+ for (const catalogAgent of createSeedCatalogAgents()) {
761
+ await putIfAbsent(agentsCol, catalogAgent.id, catalogAgent);
762
+ const existing = await agentsCol.get(catalogAgent.id);
763
+ if (!existing || existing.doc.source !== "catalog") {
764
+ catalogAgentCount++;
765
+ continue;
766
+ }
767
+
768
+ let reconciled = migrateLegacyCatalogPersona(existing.doc, catalogAgent);
769
+ // Older releases could mark permanent catalog capabilities as archived.
770
+ // Archiving is no longer automatic, and catalog rows are never valid
771
+ // archive targets, so repair only that stale status on every boot while
772
+ // preserving the user's enabled/disabled choice.
773
+ if (reconciled.status === "archived") {
774
+ reconciled = { ...reconciled, status: "idle" };
775
+ }
776
+ if (JSON.stringify(reconciled) !== JSON.stringify(existing.doc)) {
777
+ await agentsCol.put(
778
+ existing.id,
779
+ { ...reconciled, updated_at: now },
780
+ { expectedVersion: existing.version },
781
+ );
782
+ }
783
+ catalogAgentCount++;
784
+ }
785
+ log.info(`[seed] ✅ ${catalogAgentCount} catalog agents ensured`);
786
+
787
+ // Catalog rows are born without a provider/model (no provider exists at
788
+ // first boot), and setup only runs once — so anything that arrives later
789
+ // (a persona added by an upgrade, an install configured before setup
790
+ // seeded the models, an agent unlinked above) would stay blank forever.
791
+ // Fill those from the configured coordinator; rows that already have their
792
+ // own pair are left alone. No-op while there is no coordinator yet.
793
+ const configuredAgents = await ensureAgentsConfigured();
794
+ if (configuredAgents > 0) {
795
+ log.info(`[seed] 🔧 ${configuredAgents} agent(s) configurados con el modelo del coordinador`);
796
+ }
797
+
798
+ // Coordinators created before a prompt change keep the old stock text in
799
+ // their row (setup only runs once), so upgrade those in place. Prompts the
800
+ // user rewrote are detected and left alone.
801
+ const { refreshCoordinatorPrompts } = await import("./onboarding");
802
+ const refreshedPrompts = await refreshCoordinatorPrompts();
803
+ if (refreshedPrompts > 0) {
804
+ log.info(`[seed] 🔄 ${refreshedPrompts} coordinador(es) actualizados al system prompt vigente`);
805
+ }
806
+
632
807
  // 7️⃣ Channels
808
+ const channelsCol = await col<ChannelDoc>("channels");
633
809
  let channelCount = 0;
634
810
  for (const channel of SEED_DATA.channels) {
635
- db.query(`
636
- INSERT OR IGNORE INTO channels (id, type, enabled, active, status)
637
- VALUES (?, ?, 1, 0, 'disconnected')
638
- `).run(channel.id, channel.type)
811
+ await putIfAbsent(channelsCol, channel.id, {
812
+ id: channel.id, user_id: toIndexable(null), type: channel.type, enabled: true, active: false,
813
+ status: "disconnected", last_active: null, voice_enabled: false, tts_enabled: false,
814
+ stt_provider: null, tts_provider: null, tts_voice_id: null, step_delivery_mode: "milestones",
815
+ vision_enabled: false, ocr_provider: null, vision_provider: null, vision_model_id: null,
816
+ });
639
817
  channelCount++;
640
818
  }
641
819
  log.info(`[seed] ✅ ${channelCount} channels procesados`);
642
820
 
643
821
  // WebChat siempre activo — no requiere credenciales
644
- db.query(`UPDATE channels SET active = 1, enabled = 1, status = 'connected' WHERE id = 'webchat'`).run();
645
- log.info("[seed] ✅ webchat activado por defecto");
646
-
647
- // 8️⃣ Code Bridge
648
- let cbCount = 0;
649
- for (const cb of SEED_DATA.codeBridge) {
650
- db.query(`
651
- INSERT OR IGNORE INTO code_bridge (id, name, cli_command, port, enabled, active)
652
- VALUES (?, ?, ?, ?, 0, 0)
653
- `).run(cb.id, cb.name, cb.cliCommand, cb.port);
654
- cbCount++;
655
- }
656
- log.info(`[seed] ✅ ${cbCount} Code Bridge CLIs procesados`);
657
-
658
- // 8️⃣ Code Bridge Config (voice_wake_word, etc.)
659
- let cbConfigCount = 0;
660
- for (const config of SEED_DATA.codeBridgeConfig) {
661
- db.query(`
662
- INSERT OR IGNORE INTO code_bridge_config (id, key, value)
663
- VALUES (?, ?, ?)
664
- `).run(config.id, config.key, config.value);
665
- cbConfigCount++;
822
+ const webchat = await channelsCol.get("webchat");
823
+ if (webchat) {
824
+ await channelsCol.put("webchat", { ...webchat.doc, active: true, enabled: true, status: "connected" }, { expectedVersion: webchat.version });
666
825
  }
667
- log.info(`[seed] ✅ ${cbConfigCount} Code Bridge Config entries procesados`);
668
-
826
+ log.info("[seed] ✅ webchat activado por defecto");
669
827
 
670
828
  // 🔟 ACE Playbook - Initial rules for Agentic Context Engineering
829
+ const playbookCol = await col<PlaybookDoc>("playbook");
830
+ const existingPlaybook = await playbookCol.scan({});
831
+ const byRule = new Map(existingPlaybook.map((e) => [e.doc.rule, e]));
832
+
671
833
  let playbookCount = 0
672
834
  for (const rule of INITIAL_PLAYBOOK_RULES) {
673
- db.query(`
674
- INSERT OR REPLACE INTO playbook (rule, category, applicable_to, helpful_count, harmful_count, active)
675
- VALUES (?, ?, ?, 1, 0, 1)
676
- `).run(rule.rule, rule.category, rule.applicable_to)
835
+ const existing = byRule.get(rule.rule);
836
+ if (existing) {
837
+ await playbookCol.put(existing.id, {
838
+ ...existing.doc, category: rule.category, applicable_to: rule.applicable_to, active: true, updated_at: now,
839
+ }, { expectedVersion: existing.version });
840
+ } else {
841
+ const id = await nextId("playbook");
842
+ await playbookCol.put(id, {
843
+ id, rule: rule.rule, category: rule.category, applicable_to: rule.applicable_to,
844
+ helpful_count: 1, harmful_count: 0, active: true,
845
+ source_reflection_id: toIndexable(null), created_at: now, updated_at: now,
846
+ });
847
+ }
677
848
  playbookCount++
678
849
  }
679
850
  log.info(`[seed] ✅ ${playbookCount} ACE playbook rules seeded`);
680
851
 
681
- const insertPlaybookFts = db.prepare(`
682
- INSERT OR REPLACE INTO playbook_fts(rule, category, applicable_to)
683
- VALUES (?, ?, ?)
684
- `);
685
- for (const rule of INITIAL_PLAYBOOK_RULES) {
686
- insertPlaybookFts.run(rule.rule, rule.category, rule.applicable_to);
687
- }
688
- log.info(`[seed] ✅ ${playbookCount} reglas playbook sincronizadas a playbook_fts`);
852
+ // Playbook search indexing happens at startup via syncPlaybookToIndex (HiveDB)
689
853
 
690
854
  log.info("[seed] ✨ Seed completado exitosamente.");
691
855
  } catch (err) {
@@ -693,52 +857,54 @@ export function seedAllData(): void {
693
857
  }
694
858
  }
695
859
 
696
- export function seedToolsAndSkills(): void {
697
- seedAllData()
860
+ export async function seedToolsAndSkills(): Promise<void> {
861
+ await seedAllData()
862
+ }
863
+
864
+ const TABLE_TO_COLLECTION: Record<string, string> = {
865
+ providers: "providers", models: "models", tools: "tools", skills: "skills",
866
+ mcp_servers: "mcpServers", channels: "channels", ethics: "ethics",
698
867
  }
699
868
 
700
869
  /**
701
870
  * Activa un elemento específico (los datos son globales, solo actualizamos active)
702
871
  */
703
- export function activateElement(
704
- table: "providers" | "models" | "tools" | "skills" | "mcp_servers" | "channels" | "integrations",
872
+ export async function activateElement(
873
+ table: "providers" | "models" | "tools" | "skills" | "mcp_servers" | "channels" | "ethics",
705
874
  elementId: string
706
- ): void {
707
- const db = getDb()
708
- db.query(`UPDATE ${table} SET active = 1, enabled = 1 WHERE id = ?`).run(elementId)
875
+ ): Promise<void> {
876
+ const c = await col<any>(TABLE_TO_COLLECTION[table] || table)
877
+ const existing = await c.get(elementId)
878
+ if (existing) await c.put(elementId, { ...existing.doc, active: true, enabled: true }, { expectedVersion: existing.version })
709
879
  log.info(`[seed] ✅ Activado ${elementId} en ${table}`)
710
880
  }
711
881
 
712
882
  /**
713
883
  * Desactiva un elemento específico
714
884
  */
715
- export function deactivateElement(
885
+ export async function deactivateElement(
716
886
  table: "providers" | "models" | "tools" | "skills" | "mcp_servers" | "channels",
717
887
  elementId: string
718
- ): void {
719
- const db = getDb()
720
- db.query(`UPDATE ${table} SET active = 0, enabled = 0 WHERE id = ?`).run(elementId)
888
+ ): Promise<void> {
889
+ const c = await col<any>(TABLE_TO_COLLECTION[table] || table)
890
+ const existing = await c.get(elementId)
891
+ if (existing) await c.put(elementId, { ...existing.doc, active: false, enabled: false }, { expectedVersion: existing.version })
721
892
  log.warn(`[seed] ⚠️ Desactivado ${elementId} en ${table}`)
722
893
  }
723
894
 
724
895
  /**
725
896
  * Obtiene todos los elementos disponibles (activos e inactivos)
726
897
  */
727
- export function getAllElements<T extends Record<string, any>>(
728
- table: string
729
- ): T[] {
730
- const db = getDb()
731
- const results = db.query<T, []>(`SELECT * FROM ${table}`).all()
732
- return results
898
+ export async function getAllElements<T>(table: string): Promise<T[]> {
899
+ const c = await col<T>(TABLE_TO_COLLECTION[table] || table)
900
+ const entries = await c.scan({})
901
+ return entries.map((e) => e.doc)
733
902
  }
734
903
 
735
904
  /**
736
905
  * Obtiene todos los elementos activos
737
906
  */
738
- export function getActiveElements<T extends Record<string, any>>(
739
- table: string
740
- ): T[] {
741
- const db = getDb()
742
- const results = db.query<T, []>(`SELECT * FROM ${table} WHERE active = 1`).all()
743
- return results
907
+ export async function getActiveElements<T extends { active: boolean }>(table: string): Promise<T[]> {
908
+ const all = await getAllElements<T>(table)
909
+ return all.filter((e) => e.active)
744
910
  }