@johpaz/hive-sdk 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/README.md +78 -23
  3. package/bun.lock +55 -29
  4. package/bunfig.toml +4 -2
  5. package/docs/API-AGENTS.md +78 -27
  6. package/docs/API-CONTEXT-COMPILER.md +31 -34
  7. package/docs/API-TOOLS-SKILLS-CHANNELS.md +58 -22
  8. package/docs/HIVE-HARNESS.md +1 -1
  9. package/docs/INDEX.md +4 -4
  10. package/docs/TEMPLATE-HIVE-APP.md +10 -10
  11. package/package.json +17 -12
  12. package/packages/cli/package.json +2 -2
  13. package/packages/cli/src/commands/create-app.test.ts +36 -7
  14. package/packages/cli/src/commands/init.ts +3 -3
  15. package/packages/cli/src/commands/run.ts +1 -1
  16. package/packages/cli/src/commands/test.ts +37 -25
  17. package/packages/cli/src/commands/trace.ts +30 -28
  18. package/packages/cli/templates/hive-app/.env.example +10 -2
  19. package/packages/cli/templates/hive-app/README.md +103 -0
  20. package/packages/cli/templates/hive-app/hive.config.ts +9 -3
  21. package/packages/cli/templates/hive-app/src/agents/coordinator.ts +8 -1
  22. package/packages/cli/templates/hive-app/src/main.ts +12 -19
  23. package/packages/core/package.json +13 -12
  24. package/packages/core/src/agent/acceptance-checks.ts +172 -0
  25. package/packages/core/src/agent/agent-catalog.ts +348 -0
  26. package/packages/core/src/agent/agent-loop.ts +1373 -0
  27. package/packages/core/src/agent/capability-search.ts +186 -0
  28. package/packages/core/src/agent/catalog-selector.ts +103 -0
  29. package/packages/core/src/agent/{Compaction.ts → compaction.ts} +86 -63
  30. package/packages/core/src/agent/context-compiler.ts +689 -0
  31. package/packages/core/src/agent/conversation-store.ts +381 -0
  32. package/packages/core/src/agent/curator.ts +276 -0
  33. package/packages/core/src/agent/delegation-runtime.ts +241 -0
  34. package/packages/core/src/agent/goal-runner.ts +323 -0
  35. package/packages/core/src/agent/index.ts +17 -12
  36. package/packages/core/src/agent/llm-client.ts +266 -0
  37. package/packages/core/src/agent/llm-providers/anthropic.ts +264 -0
  38. package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
  39. package/packages/core/src/agent/{providers → llm-providers}/gemini.ts +98 -60
  40. package/packages/core/src/agent/llm-providers/groq.ts +5 -0
  41. package/packages/core/src/agent/llm-providers/hiveagents.ts +253 -0
  42. package/packages/core/src/agent/{providers → llm-providers}/interface.ts +73 -13
  43. package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
  44. package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
  45. package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
  46. package/packages/core/src/agent/llm-providers/modelscope.ts +5 -0
  47. package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
  48. package/packages/core/src/agent/{providers → llm-providers}/ollama.ts +31 -5
  49. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +418 -0
  50. package/packages/core/src/agent/llm-providers/openai.ts +5 -0
  51. package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
  52. package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
  53. package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
  54. package/packages/core/src/agent/llm-providers/z-ai.ts +5 -0
  55. package/packages/core/src/agent/minimal-loadout.ts +47 -0
  56. package/packages/core/src/agent/playbook-selector.ts +119 -0
  57. package/packages/core/src/agent/{PromptBuilder.ts → prompt-builder.ts} +21 -22
  58. package/packages/core/src/{harness → agent}/proof-packet.ts +16 -21
  59. package/packages/core/src/agent/providers/index.ts +35 -16
  60. package/packages/core/src/agent/reflector.ts +320 -0
  61. package/packages/core/src/agent/routing-intent.ts +22 -0
  62. package/packages/core/src/{harness → agent}/run-epoch.ts +4 -3
  63. package/packages/core/src/{harness → agent}/run-store.ts +142 -81
  64. package/packages/core/src/agent/{Service.ts → service.ts} +37 -26
  65. package/packages/core/src/agent/skill-selector.ts +374 -0
  66. package/packages/core/src/agent/stuck-loop.ts +209 -0
  67. package/packages/core/src/agent/{selectors/ToolSelector.ts → tool-selector.ts} +188 -178
  68. package/packages/core/src/{ace/Tracer.ts → agent/tracer.ts} +37 -27
  69. package/packages/core/src/api/createAgent.test.ts +139 -27
  70. package/packages/core/src/api/createAgent.ts +232 -44
  71. package/packages/core/src/artifacts/store.ts +162 -0
  72. package/packages/core/src/canvas/canvas-manager.ts +161 -0
  73. package/packages/core/src/canvas/canvas.test.ts +8 -4
  74. package/packages/core/src/canvas/emitter.ts +131 -80
  75. package/packages/core/src/canvas/index.ts +1 -3
  76. package/packages/core/src/channels/base.ts +9 -1
  77. package/packages/core/src/channels/discord.ts +5 -4
  78. package/packages/core/src/channels/manager.ts +122 -30
  79. package/packages/core/src/channels/slack.ts +5 -4
  80. package/packages/core/src/channels/telegram.ts +36 -6
  81. package/packages/core/src/channels/webchat.ts +11 -10
  82. package/packages/core/src/channels/whatsapp.ts +23 -7
  83. package/packages/core/src/config/index.ts +13 -2
  84. package/packages/core/src/config/loader.ts +76 -29
  85. package/packages/core/src/ethics/EthicsGuard.test.ts +90 -36
  86. package/packages/core/src/ethics/EthicsGuard.ts +51 -47
  87. package/packages/core/src/events/agent-bus.ts +44 -68
  88. package/packages/core/src/events/channel-narration.ts +150 -0
  89. package/packages/core/src/events/narration.ts +82 -0
  90. package/packages/core/src/events/tool-narration.ts +62 -0
  91. package/packages/core/src/gateway/delegation-groups.ts +258 -0
  92. package/packages/core/src/{harness → gateway}/durable-queue.ts +102 -42
  93. package/packages/core/src/{harness → gateway}/job-store.ts +85 -48
  94. package/packages/core/src/gateway/lane-queue.ts +173 -0
  95. package/packages/core/src/gateway/notification-inbox.ts +57 -0
  96. package/packages/core/src/gateway/server.ts +1 -1
  97. package/packages/core/src/harness/index.ts +46 -27
  98. package/packages/core/src/index.ts +33 -27
  99. package/packages/core/src/mcp/hot-reload.ts +32 -23
  100. package/packages/core/src/mcp/index.ts +6 -3
  101. package/packages/core/src/mcp/singleton.ts +1 -4
  102. package/packages/core/src/mcp/tool-sync.ts +138 -0
  103. package/packages/core/src/memory/Scratchpad.test.ts +39 -20
  104. package/packages/core/src/memory/Scratchpad.ts +27 -34
  105. package/packages/core/src/multimodal/vision-service.ts +44 -38
  106. package/packages/core/src/resilience/retry.ts +95 -0
  107. package/packages/core/src/scheduler/CronScheduler.ts +334 -287
  108. package/packages/core/src/scheduler/index.ts +9 -7
  109. package/packages/core/src/scheduler/integration.ts +46 -26
  110. package/packages/core/src/scheduler/scheduler.test.ts +9 -13
  111. package/packages/core/src/scheduler/types.ts +7 -2
  112. package/packages/core/src/security/Pairing.ts +1 -1
  113. package/packages/core/src/skills/bundled/a2ui/a2ui_dashboard/SKILL.md +176 -0
  114. package/packages/core/src/skills/bundled/a2ui/a2ui_form/SKILL.md +202 -0
  115. package/packages/core/src/skills/bundled/a2ui/a2ui_interactive/SKILL.md +206 -0
  116. package/packages/core/src/skills/bundled/agents/agent_spawner/SKILL.md +173 -0
  117. package/packages/core/src/skills/bundled/agents/memory_manager/SKILL.md +143 -0
  118. package/packages/core/src/skills/bundled/agents/research_and_remember/SKILL.md +139 -0
  119. package/packages/core/src/skills/bundled/agents/task_orchestrator/SKILL.md +98 -0
  120. package/packages/core/src/skills/bundled/api/api_client/SKILL.md +132 -0
  121. package/packages/core/src/skills/bundled/cli/cli_pipeline/SKILL.md +135 -0
  122. package/packages/core/src/skills/bundled/cli/cli_safe_exec/SKILL.md +125 -0
  123. package/packages/core/src/skills/bundled/cli/software_engineering/SKILL.md +23 -0
  124. package/packages/core/src/skills/bundled/cron_manager/SKILL.md +188 -0
  125. package/packages/core/src/skills/bundled/cron_reminder/SKILL.md +112 -0
  126. package/packages/core/src/skills/bundled/filesystem/file_manager/SKILL.md +118 -0
  127. package/packages/core/src/skills/bundled/filesystem/file_read_and_summarize/SKILL.md +109 -0
  128. package/packages/core/src/skills/bundled/filesystem/file_writer/SKILL.md +129 -0
  129. package/packages/core/src/skills/bundled/filesystem/workspace_file_operator/SKILL.md +22 -0
  130. package/packages/core/src/skills/bundled/office/office_document_manager/SKILL.md +262 -0
  131. package/packages/core/src/skills/bundled/search_knowledge/capability_discovery/SKILL.md +75 -0
  132. package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +120 -0
  133. package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +109 -0
  134. package/packages/core/src/skills/bundled/web/web_monitor/SKILL.md +127 -0
  135. package/packages/core/src/skills/bundled/web/web_research/SKILL.md +119 -0
  136. package/packages/core/src/skills/bundled-data.generated.ts +731 -2678
  137. package/packages/core/src/skills/skills.test.ts +52 -11
  138. package/packages/core/src/{harness → storage}/boot-id.ts +5 -2
  139. package/packages/core/src/storage/bootstrap.ts +151 -0
  140. package/packages/core/src/storage/causal-events.ts +84 -0
  141. package/packages/core/src/storage/collections.ts +680 -0
  142. package/packages/core/src/storage/crypto.ts +205 -74
  143. package/packages/core/src/{harness/db-helpers.ts → storage/hive.ts} +63 -7
  144. package/packages/core/src/storage/hivedb.ts +61 -0
  145. package/packages/core/src/storage/index.ts +111 -18
  146. package/packages/core/src/storage/model-id.ts +53 -0
  147. package/packages/core/src/storage/onboarding.ts +540 -972
  148. package/packages/core/src/storage/reconcile.ts +238 -0
  149. package/packages/core/src/storage/seed.ts +572 -406
  150. package/packages/core/src/storage/usage.ts +285 -225
  151. package/packages/core/src/storage/user-email.ts +11 -0
  152. package/packages/core/src/swarm/AgentExecutor.ts +1 -1
  153. package/packages/core/src/swarm/EventBridge.ts +1 -1
  154. package/packages/core/src/swarm/index.ts +12 -9
  155. package/packages/core/src/tool-runtime/index.ts +146 -23
  156. package/packages/core/src/tool-runtime/tool-worker.ts +2 -2
  157. package/packages/core/src/tool-runtime/worker-tools.ts +27 -0
  158. package/packages/core/src/{canvas/a2ui-tools.ts → tools/a2ui/index.ts} +17 -8
  159. package/packages/core/src/tools/agents/get-available-models.ts +36 -54
  160. package/packages/core/src/tools/agents/index.ts +784 -292
  161. package/packages/core/src/tools/api/api-request.test.ts +164 -0
  162. package/packages/core/src/tools/api/api-request.ts +174 -0
  163. package/packages/core/src/tools/api/index.ts +16 -0
  164. package/packages/core/src/tools/cli/index.ts +4 -0
  165. package/packages/core/src/tools/core/index.ts +281 -112
  166. package/packages/core/src/tools/cron/index.ts +121 -124
  167. package/packages/core/src/tools/index.ts +63 -78
  168. package/packages/core/src/tools/office/office-escribir-xlsx.ts +3 -1
  169. package/packages/core/src/tools/types.ts +3 -1
  170. package/packages/core/src/tools/web/artifact-inspect.ts +23 -0
  171. package/packages/core/src/tools/web/browser-backend.ts +129 -0
  172. package/packages/core/src/tools/web/browser-screenshot.ts +26 -5
  173. package/packages/core/src/tools/web/browser-service.ts +80 -35
  174. package/packages/core/src/tools/web/browser-type.ts +3 -8
  175. package/packages/core/src/tools/web/index.ts +4 -4
  176. package/packages/core/src/tools/web/webview-backend.ts +412 -0
  177. package/packages/core/src/voice/index.ts +89 -63
  178. package/packages/core/src/workers/agent.worker.ts +2 -2
  179. package/packages/core/src/workers/workers.test.ts +3 -10
  180. package/scripts/bump-version.ts +248 -0
  181. package/scripts/generate-skill-bundle.ts +108 -0
  182. package/test/acceptance-checks.test.ts +403 -0
  183. package/test/agent-loop-terminal-synthesis.test.ts +32 -0
  184. package/test/browser-backend.test.ts +308 -0
  185. package/test/catalog-agents-stay-enabled.test.ts +117 -0
  186. package/test/causal-events.test.ts +117 -0
  187. package/test/compaction.test.ts +105 -0
  188. package/test/context-compiler.test.ts +269 -0
  189. package/test/curator.test.ts +130 -0
  190. package/test/durable-queue.test.ts +114 -0
  191. package/test/harness-barrel.test.ts +64 -0
  192. package/test/hive-helpers.test.ts +130 -0
  193. package/test/hivedb-search.test.ts +189 -0
  194. package/test/internal-turns.test.ts +166 -0
  195. package/test/job-idempotency.test.ts +68 -0
  196. package/test/job-retry-backoff.test.ts +184 -0
  197. package/test/job-store.test.ts +381 -0
  198. package/test/llm-retry.test.ts +97 -0
  199. package/test/memory-perf.test.ts +774 -0
  200. package/test/minimal-loadout.test.ts +78 -0
  201. package/test/model-catalog.test.ts +105 -0
  202. package/test/preload.ts +12 -0
  203. package/test/reflector.test.ts +320 -0
  204. package/test/retention-cap.test.ts +91 -0
  205. package/test/retired-capabilities-pruned.test.ts +192 -0
  206. package/test/run-store.test.ts +355 -0
  207. package/test/scratchpad.test.ts +74 -0
  208. package/test/secrets-durability.test.ts +119 -0
  209. package/test/seed-model-reseed.test.ts +155 -0
  210. package/test/setup-agent-seed.test.ts +264 -0
  211. package/test/tool-inventory.test.ts +65 -0
  212. package/test/tool-runtime.test.ts +258 -0
  213. package/test/tool-selector-runtime-tools.test.ts +117 -0
  214. package/test/toon.test.ts +429 -0
  215. package/tsconfig.json +2 -0
  216. package/packages/core/src/ace/Curator.ts +0 -158
  217. package/packages/core/src/ace/Reflector.ts +0 -200
  218. package/packages/core/src/ace/index.ts +0 -4
  219. package/packages/core/src/agent/AgentRunner.ts +0 -711
  220. package/packages/core/src/agent/ContextCompiler.ts +0 -567
  221. package/packages/core/src/agent/ContextGuard.ts +0 -91
  222. package/packages/core/src/agent/ConversationStore.ts +0 -254
  223. package/packages/core/src/agent/Hooks.ts +0 -166
  224. package/packages/core/src/agent/StuckLoop.ts +0 -133
  225. package/packages/core/src/agent/providers/LLMClient.ts +0 -149
  226. package/packages/core/src/agent/providers/anthropic.ts +0 -212
  227. package/packages/core/src/agent/providers/openai-compat.ts +0 -231
  228. package/packages/core/src/agent/selectors/PlaybookSelector.ts +0 -121
  229. package/packages/core/src/agent/selectors/SkillSelector.ts +0 -322
  230. package/packages/core/src/agent/selectors/index.ts +0 -6
  231. package/packages/core/src/auth/auth.ts +0 -121
  232. package/packages/core/src/auth/index.ts +0 -1
  233. package/packages/core/src/canvas/CanvasManager.ts +0 -390
  234. package/packages/core/src/canvas/canvas-tools.ts +0 -448
  235. package/packages/core/src/harness/collections.ts +0 -98
  236. package/packages/core/src/harness/goal-verifier.ts +0 -141
  237. package/packages/core/src/harness/harness.test.ts +0 -236
  238. package/packages/core/src/harness/reconcile.ts +0 -149
  239. package/packages/core/src/mcp/MCPToolAdapter.ts +0 -176
  240. package/packages/core/src/multimodal/VisionService.ts +0 -293
  241. package/packages/core/src/scheduler/dag/AgentExecutor.ts +0 -53
  242. package/packages/core/src/scheduler/dag/DAGScheduler.ts +0 -250
  243. package/packages/core/src/scheduler/dag/EventBridge.ts +0 -122
  244. package/packages/core/src/scheduler/dag/TaskGraph.ts +0 -192
  245. package/packages/core/src/scheduler/dag/TaskNode.ts +0 -97
  246. package/packages/core/src/scheduler/dag/TaskResult.ts +0 -22
  247. package/packages/core/src/scheduler/dag/errors.ts +0 -37
  248. package/packages/core/src/scheduler/dag/index.ts +0 -26
  249. package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +0 -97
  250. package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +0 -21
  251. package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +0 -46
  252. package/packages/core/src/storage/HiveDBStorage.ts +0 -64
  253. package/packages/core/src/storage/SQLiteStorage.ts +0 -414
  254. package/packages/core/src/storage/hiveSeed.ts +0 -308
  255. package/packages/core/src/storage/hiveStorage.test.ts +0 -38
  256. package/packages/core/src/storage/schema.ts +0 -689
  257. package/packages/core/src/storage/storage.test.ts +0 -37
  258. package/packages/core/src/swarm/AgentBus.ts +0 -460
  259. package/packages/core/src/swarm/EventBus.ts +0 -169
  260. package/packages/core/src/swarm/WorkerPool.ts +0 -236
  261. package/packages/core/src/tools/bridge-events.ts +0 -26
  262. package/packages/core/src/tools/canvas/index.ts +0 -375
  263. package/packages/core/src/tools/codebridge/index.ts +0 -342
  264. package/packages/core/src/tools/meeting/index.ts +0 -353
  265. package/packages/core/src/tools/projects/index.ts +0 -37
  266. package/packages/core/src/tools/projects/project-create.ts +0 -94
  267. package/packages/core/src/tools/projects/project-done.ts +0 -66
  268. package/packages/core/src/tools/projects/project-fail.ts +0 -66
  269. package/packages/core/src/tools/projects/project-list.ts +0 -96
  270. package/packages/core/src/tools/projects/project-update.ts +0 -72
  271. package/packages/core/src/tools/projects/task-create.ts +0 -68
  272. package/packages/core/src/tools/projects/task-evaluate.ts +0 -93
  273. package/packages/core/src/tools/projects/task-update.ts +0 -93
  274. package/packages/core/src/tools/voice/index.ts +0 -104
  275. package/packages/core/src/tools/web/api-request.test.ts +0 -170
  276. package/packages/core/src/tools/web/api-request.ts +0 -239
  277. package/test/setup-db.ts +0 -216
  278. /package/packages/core/src/agent/{NativeTools.ts → native-tools.ts} +0 -0
@@ -0,0 +1,412 @@
1
+ /**
2
+ * WebViewBackend — `BrowserBackend` sobre `Bun.WebView` (Bun >= 1.3).
3
+ *
4
+ * Corre in-process: no hay subproceso, ni instalación de ~75 MB, ni descarga de
5
+ * Chrome. Un `evaluate` cuesta ~0.25 ms contra los ~68 ms de piso que tiene cada
6
+ * invocación del CLI de agent-browser. A cambio necesita entorno gráfico, así
7
+ * que no reemplaza a agent-browser en Docker ni en un servidor headless.
8
+ *
9
+ * Dos restricciones del motor mandan sobre el diseño de este archivo:
10
+ *
11
+ * 1. `Bun.WebView` acepta **una sola operación pendiente por vez**; dos
12
+ * llamadas solapadas fallan con `ERR_INVALID_STATE: a simple operation is
13
+ * already pending`. Todo pasa por una cola serializada.
14
+ * 2. No expone árbol de accesibilidad. `snapshot()` se sintetiza recorriendo
15
+ * el DOM, imitando el formato que emite agent-browser para que el modelo
16
+ * vea lo mismo con cualquiera de los dos backends.
17
+ */
18
+
19
+ import { logger } from "../../utils/logger.ts";
20
+ import { resolveWebViewEngine, type BrowserBackend, type ScreenshotOptions, type SnapshotOptions, type WebViewEngine } from "./browser-backend.ts";
21
+
22
+ const log = logger.child("webview-backend");
23
+
24
+ /** Tope del texto del snapshot: un DOM grande no puede comerse el contexto. */
25
+ const SNAPSHOT_CHAR_LIMIT = 20_000;
26
+
27
+ /**
28
+ * Forma real de `Bun.WebView` en 1.3.14, verificada contra el prototipo.
29
+ *
30
+ * No se usan los tipos de `bun-types` a propósito: declaran `back()`/`forward()`
31
+ * y el runtime expone `goBack()`/`goForward()`. Contra los tipos, la navegación
32
+ * hacia atrás compila y explota en ejecución.
33
+ */
34
+ interface BunWebView {
35
+ navigate(url: string): Promise<void>;
36
+ evaluate(script: string): Promise<unknown>;
37
+ screenshot(): Promise<Blob>;
38
+ cdp(method: string, params?: Record<string, unknown>): Promise<unknown>;
39
+ click(selector: string): Promise<void>;
40
+ type(text: string): Promise<void>;
41
+ press(key: string, modifiers?: Record<string, boolean>): Promise<void>;
42
+ scroll(dx: number, dy: number): Promise<void>;
43
+ scrollTo(selector: string): Promise<void>;
44
+ resize(width: number, height: number): Promise<void>;
45
+ goBack(): Promise<void>;
46
+ goForward(): Promise<void>;
47
+ reload(): Promise<void>;
48
+ close(): void;
49
+ readonly url: string;
50
+ readonly title: string;
51
+ readonly loading: boolean;
52
+ }
53
+
54
+ /**
55
+ * El script que sintetiza el árbol de accesibilidad. Se inyecta como texto, así
56
+ * que no puede cerrar sobre nada del scope de TypeScript: los parámetros entran
57
+ * interpolados como literales JSON.
58
+ */
59
+ function buildSnapshotScript(options: Required<SnapshotOptions>): string {
60
+ return `(() => {
61
+ const MAX_DEPTH = ${JSON.stringify(options.depth)};
62
+ const COMPACT = ${JSON.stringify(options.compact)};
63
+ const INTERACTIVE_ONLY = ${JSON.stringify(options.interactiveOnly)};
64
+ const LIMIT = ${SNAPSHOT_CHAR_LIMIT};
65
+
66
+ const ROLE_BY_TAG = {
67
+ A: "link", BUTTON: "button", P: "paragraph", IMG: "img", TEXTAREA: "textbox",
68
+ SELECT: "combobox", OPTION: "option", UL: "list", OL: "list", LI: "listitem",
69
+ TABLE: "table", TR: "row", TD: "cell", TH: "columnheader", FORM: "form",
70
+ NAV: "navigation", MAIN: "main", HEADER: "banner", FOOTER: "contentinfo",
71
+ ASIDE: "complementary", LABEL: "label", ARTICLE: "article", SECTION: "region",
72
+ DIALOG: "dialog", SUMMARY: "button", IFRAME: "iframe", VIDEO: "video", AUDIO: "audio",
73
+ };
74
+ const INTERACTIVE = new Set(["link", "button", "textbox", "checkbox", "radio", "combobox", "option", "searchbox"]);
75
+ const SKIP_TAGS = new Set(["SCRIPT", "STYLE", "NOSCRIPT", "TEMPLATE", "HEAD", "META", "LINK", "TITLE", "SVG", "PATH"]);
76
+
77
+ function inputRole(el) {
78
+ const type = (el.getAttribute("type") || "text").toLowerCase();
79
+ if (type === "checkbox") return "checkbox";
80
+ if (type === "radio") return "radio";
81
+ if (type === "search") return "searchbox";
82
+ if (type === "button" || type === "submit" || type === "reset" || type === "image") return "button";
83
+ if (type === "hidden") return null;
84
+ return "textbox";
85
+ }
86
+
87
+ function roleOf(el) {
88
+ const explicit = el.getAttribute("role");
89
+ if (explicit) return explicit.trim().split(/\\s+/)[0];
90
+ if (el.tagName === "INPUT") return inputRole(el);
91
+ if (/^H[1-6]$/.test(el.tagName)) return "heading";
92
+ if (el.tagName === "A") return el.hasAttribute("href") ? "link" : null;
93
+ return ROLE_BY_TAG[el.tagName] || null;
94
+ }
95
+
96
+ function ownText(el) {
97
+ let text = "";
98
+ for (const node of el.childNodes) {
99
+ if (node.nodeType === 3) text += node.nodeValue;
100
+ // Los inline sin rol propio son parte del nombre del padre, no nodos aparte.
101
+ else if (node.nodeType === 1 && !roleOf(node) && node.childElementCount === 0) {
102
+ text += node.textContent || "";
103
+ }
104
+ }
105
+ return text.replace(/\\s+/g, " ").trim();
106
+ }
107
+
108
+ function nameOf(el) {
109
+ const aria = el.getAttribute("aria-label");
110
+ if (aria && aria.trim()) return aria.trim();
111
+
112
+ const labelledBy = el.getAttribute("aria-labelledby");
113
+ if (labelledBy) {
114
+ const parts = labelledBy.split(/\\s+/)
115
+ .map((id) => { const target = document.getElementById(id); return target ? (target.textContent || "").trim() : ""; })
116
+ .filter(Boolean);
117
+ if (parts.length) return parts.join(" ").replace(/\\s+/g, " ");
118
+ }
119
+
120
+ if (el.tagName === "IMG") return (el.getAttribute("alt") || "").trim();
121
+ if (el.tagName === "INPUT" || el.tagName === "TEXTAREA") {
122
+ const type = (el.getAttribute("type") || "").toLowerCase();
123
+ if (type === "button" || type === "submit" || type === "reset") return (el.value || "").trim();
124
+ const placeholder = (el.getAttribute("placeholder") || "").trim();
125
+ if (placeholder) return placeholder;
126
+ if (el.labels && el.labels.length) return (el.labels[0].textContent || "").replace(/\\s+/g, " ").trim();
127
+ return (el.getAttribute("name") || "").trim();
128
+ }
129
+
130
+ const own = ownText(el);
131
+ if (own) return own;
132
+ return (el.getAttribute("title") || "").trim();
133
+ }
134
+
135
+ function visible(el) {
136
+ if (el.hasAttribute("hidden") || el.getAttribute("aria-hidden") === "true") return false;
137
+ const style = getComputedStyle(el);
138
+ if (style.display === "none" || style.visibility === "hidden") return false;
139
+ return true;
140
+ }
141
+
142
+ function attrsOf(el, role) {
143
+ const attrs = [];
144
+ if (role === "heading") attrs.push("level=" + el.tagName.slice(1));
145
+ if (el.disabled) attrs.push("disabled");
146
+ if (el.checked) attrs.push("checked");
147
+ if (el.getAttribute("aria-expanded")) attrs.push("expanded=" + el.getAttribute("aria-expanded"));
148
+ return attrs;
149
+ }
150
+
151
+ const lines = [];
152
+ let refSeq = 0;
153
+ let truncated = false;
154
+
155
+ function walk(el, depth) {
156
+ if (truncated) return;
157
+ for (const child of el.children) {
158
+ if (truncated) return;
159
+ if (SKIP_TAGS.has(child.tagName)) continue;
160
+ if (!visible(child)) continue;
161
+
162
+ const role = roleOf(child);
163
+ const name = role ? nameOf(child) : "";
164
+ const interactive = role ? INTERACTIVE.has(role) : false;
165
+ // Un nodo se emite si aporta algo: un rol con nombre, o algo accionable.
166
+ let emit = Boolean(role) && (Boolean(name) || interactive);
167
+ if (INTERACTIVE_ONLY && !interactive) emit = false;
168
+ if (COMPACT && role && !name && !interactive) emit = false;
169
+
170
+ if (emit && depth < MAX_DEPTH) {
171
+ const attrs = attrsOf(child, role);
172
+ if (name || interactive) attrs.push("ref=e" + ++refSeq);
173
+ let label = "- " + role;
174
+ if (name) {
175
+ const shown = COMPACT && name.length > 120 ? name.slice(0, 120) + "…" : name;
176
+ label += ' "' + shown.replace(/"/g, "'") + '"';
177
+ }
178
+ if (attrs.length) label += " [" + attrs.join(", ") + "]";
179
+ const line = " ".repeat(depth) + label;
180
+ if (lines.join("\\n").length + line.length > LIMIT) { truncated = true; return; }
181
+ lines.push(line);
182
+ }
183
+
184
+ // Sin línea propia, los hijos suben de nivel: así el árbol no se llena de
185
+ // sangría por cada <div> de maquetado.
186
+ walk(child, emit && depth < MAX_DEPTH ? depth + 1 : depth);
187
+ }
188
+ }
189
+
190
+ walk(document.body || document.documentElement, 0);
191
+ if (truncated) lines.push("… (snapshot truncado)");
192
+ return lines.join("\\n");
193
+ })()`;
194
+ }
195
+
196
+ export class WebViewBackend implements BrowserBackend {
197
+ private view: BunWebView | null = null;
198
+ private _url = "";
199
+ /** Cola de una sola vía: WebView rechaza operaciones solapadas. */
200
+ private queue: Promise<unknown> = Promise.resolve();
201
+
202
+ constructor(
203
+ private readonly options: {
204
+ width?: number;
205
+ height?: number;
206
+ show?: boolean;
207
+ engine?: WebViewEngine;
208
+ } = {},
209
+ ) {}
210
+
211
+ private ensureView(): BunWebView {
212
+ if (this.view) return this.view;
213
+
214
+ const WebView = (globalThis as { Bun?: { WebView?: unknown } }).Bun?.WebView;
215
+ if (typeof WebView !== "function") {
216
+ throw new Error("Bun.WebView no está disponible en este runtime (requiere Bun >= 1.3)");
217
+ }
218
+
219
+ const engine = this.options.engine ?? resolveWebViewEngine();
220
+ if (!engine) {
221
+ throw new Error(
222
+ "Bun.WebView no tiene motor utilizable: WebKit sólo existe en macOS y no se encontró Chrome. " +
223
+ "Instalá Chrome o definí BUN_CHROME_PATH.",
224
+ );
225
+ }
226
+
227
+ // `url: false` es obligatorio para automatización desatendida: sin eso el
228
+ // motor chrome intenta CONECTARSE a un Chrome que ya esté corriendo, y esa
229
+ // ruta abre un diálogo "Allow remote debugging?" que cuelga el proceso
230
+ // esperando un click que en un servidor no llega nunca.
231
+ const backend =
232
+ engine === "chrome"
233
+ ? { type: "chrome" as const, url: false as const, stderr: "ignore" as const }
234
+ : ("webkit" as const);
235
+
236
+ // Sin `url` inicial a propósito: construir con uno deja una navegación
237
+ // pendiente y el primer navigate() explota con ERR_INVALID_STATE.
238
+ const Ctor = WebView as unknown as new (opts: unknown) => BunWebView;
239
+ this.view = new Ctor({
240
+ backend,
241
+ show: this.options.show ?? false,
242
+ width: this.options.width ?? 1280,
243
+ height: this.options.height ?? 800,
244
+ });
245
+ log.info(`✅ WebView abierto (motor: ${engine}, in-process)`);
246
+ return this.view;
247
+ }
248
+
249
+ /** Serializa: cada operación espera a que termine la anterior. */
250
+ private run<T>(operation: (view: BunWebView) => Promise<T>): Promise<T> {
251
+ const next = this.queue.then(
252
+ () => operation(this.ensureView()),
253
+ () => operation(this.ensureView()),
254
+ );
255
+ // La cola no debe romperse porque una operación haya fallado.
256
+ this.queue = next.catch(() => undefined);
257
+ return next;
258
+ }
259
+
260
+ get url(): string {
261
+ return this.view?.url || this._url;
262
+ }
263
+
264
+ get title(): string {
265
+ return this.view?.title || "";
266
+ }
267
+
268
+ get loading(): boolean {
269
+ return this.view?.loading ?? false;
270
+ }
271
+
272
+ async navigate(url: string): Promise<void> {
273
+ const target = /^[a-z]+:/i.test(url) ? url : `https://${url}`;
274
+ await this.run((view) => view.navigate(target));
275
+ this._url = this.view?.url || target;
276
+ }
277
+
278
+ async evaluate<T = unknown>(script: string): Promise<T> {
279
+ const trimmed = script.trim();
280
+ let wrapped = script;
281
+ if (/\bawait\b/.test(script) && !trimmed.startsWith("(async") && !trimmed.startsWith("async function")) {
282
+ wrapped = trimmed.startsWith("return")
283
+ ? `(async () => { ${script} })()`
284
+ : `(async () => { return ${script}; })()`;
285
+ }
286
+ return (await this.run((view) => view.evaluate(wrapped))) as T;
287
+ }
288
+
289
+ async screenshot(_options?: ScreenshotOptions): Promise<string> {
290
+ const blob = await this.run((view) => view.screenshot());
291
+ return Buffer.from(await blob.arrayBuffer()).toString("base64");
292
+ }
293
+
294
+ async screenshotElement(selector: string): Promise<string> {
295
+ const box = await this.evaluate<{ x: number; y: number; width: number; height: number } | null>(
296
+ `(() => {
297
+ const el = document.querySelector(${JSON.stringify(selector)});
298
+ if (!el) return null;
299
+ el.scrollIntoView({ block: "center", inline: "center" });
300
+ const r = el.getBoundingClientRect();
301
+ return { x: r.x, y: r.y, width: r.width, height: r.height };
302
+ })()`,
303
+ );
304
+ if (!box || box.width <= 0 || box.height <= 0) {
305
+ throw new Error(`screenshot failed: elemento no visible o inexistente: ${selector}`);
306
+ }
307
+
308
+ // WebView.screenshot() no recorta, pero el puente CDP sí acepta `clip`.
309
+ const shot = await this.run((view) =>
310
+ view.cdp("Page.captureScreenshot", {
311
+ format: "png",
312
+ clip: { x: box.x, y: box.y, width: box.width, height: box.height, scale: 1 },
313
+ }),
314
+ );
315
+ const data = (shot as { data?: string })?.data;
316
+ if (!data) throw new Error(`screenshot failed: CDP no devolvió imagen para ${selector}`);
317
+ return data;
318
+ }
319
+
320
+ async snapshot(options?: SnapshotOptions): Promise<string> {
321
+ const script = buildSnapshotScript({
322
+ compact: options?.compact !== false,
323
+ depth: options?.depth ?? 12,
324
+ interactiveOnly: options?.interactiveOnly ?? false,
325
+ });
326
+ return (await this.evaluate<string>(script)) || "";
327
+ }
328
+
329
+ async click(selector: string, _options?: Record<string, unknown>): Promise<void> {
330
+ await this.run((view) => view.click(selector));
331
+ }
332
+
333
+ async type(text: string): Promise<void> {
334
+ await this.run((view) => view.type(text));
335
+ }
336
+
337
+ async typeIn(selector: string, text: string): Promise<void> {
338
+ const focused = await this.evaluate<boolean>(
339
+ `(() => { const el = document.querySelector(${JSON.stringify(selector)}); if (!el) return false; el.focus(); return true; })()`,
340
+ );
341
+ if (!focused) throw new Error(`type failed: elemento no encontrado: ${selector}`);
342
+ await this.run((view) => view.type(text));
343
+ }
344
+
345
+ async fill(selector: string, text: string): Promise<void> {
346
+ // `fill` reemplaza; `type` agrega. Se limpia primero y se disparan los
347
+ // eventos que esperan React y compañía para registrar el cambio.
348
+ const ok = await this.evaluate<boolean>(
349
+ `(() => {
350
+ const el = document.querySelector(${JSON.stringify(selector)});
351
+ if (!el) return false;
352
+ el.focus();
353
+ el.value = "";
354
+ el.dispatchEvent(new Event("input", { bubbles: true }));
355
+ return true;
356
+ })()`,
357
+ );
358
+ if (!ok) throw new Error(`fill failed: elemento no encontrado: ${selector}`);
359
+ await this.run((view) => view.type(text));
360
+ await this.evaluate(
361
+ `(() => {
362
+ const el = document.querySelector(${JSON.stringify(selector)});
363
+ if (el) el.dispatchEvent(new Event("change", { bubbles: true }));
364
+ })()`,
365
+ );
366
+ }
367
+
368
+ async press(key: string, options?: { modifiers?: string[] }): Promise<void> {
369
+ const modifiers: Record<string, boolean> = {};
370
+ for (const modifier of options?.modifiers ?? []) {
371
+ const normalized = modifier.toLowerCase();
372
+ if (normalized === "control" || normalized === "ctrl") modifiers.ctrl = true;
373
+ else if (normalized === "shift") modifiers.shift = true;
374
+ else if (normalized === "alt") modifiers.alt = true;
375
+ else if (normalized === "meta" || normalized === "cmd") modifiers.meta = true;
376
+ }
377
+ await this.run((view) => view.press(key, modifiers));
378
+ }
379
+
380
+ async scroll(dx: number, dy: number): Promise<void> {
381
+ await this.run((view) => view.scroll(dx, dy));
382
+ }
383
+
384
+ async scrollTo(selector: string, _options?: { behavior?: "smooth" | "instant" }): Promise<void> {
385
+ await this.run((view) => view.scrollTo(selector));
386
+ }
387
+
388
+ async back(): Promise<void> {
389
+ await this.run((view) => view.goBack());
390
+ }
391
+
392
+ async forward(): Promise<void> {
393
+ await this.run((view) => view.goForward());
394
+ }
395
+
396
+ async reload(): Promise<void> {
397
+ await this.run((view) => view.reload());
398
+ }
399
+
400
+ async resize(width: number, height: number): Promise<void> {
401
+ await this.run((view) => view.resize(width, height));
402
+ }
403
+
404
+ close(): void {
405
+ try {
406
+ this.view?.close();
407
+ } catch {
408
+ /* ya cerrado */
409
+ }
410
+ this.view = null;
411
+ }
412
+ }
@@ -1,4 +1,5 @@
1
- import { getDb } from "../storage/SQLiteStorage";
1
+ import { col } from "../storage/hive";
2
+ import type { ChannelDoc, ModelDoc } from "../storage/collections";
2
3
  import { loadProviderApiKey } from "../storage/crypto";
3
4
  import { logger } from "../utils/logger";
4
5
 
@@ -79,20 +80,11 @@ class VoiceService {
79
80
  return VoiceService.instance;
80
81
  }
81
82
 
82
- getChannelVoiceConfig(channelId: string): VoiceConfig {
83
- const db = getDb();
84
- const result = db.query(`
85
- SELECT voice_enabled, tts_enabled, stt_provider, tts_provider, tts_voice_id
86
- FROM channels WHERE id = ?
87
- `).get(channelId) as {
88
- voice_enabled: number;
89
- tts_enabled: number;
90
- stt_provider: string | null;
91
- tts_provider: string | null;
92
- tts_voice_id: string | null;
93
- } | undefined;
94
-
95
- if (!result) {
83
+ async getChannelVoiceConfig(channelId: string): Promise<VoiceConfig> {
84
+ const channelsCol = await col<ChannelDoc>("channels");
85
+ const entry = await channelsCol.get(channelId);
86
+
87
+ if (!entry) {
96
88
  return {
97
89
  voiceEnabled: false,
98
90
  ttsEnabled: false,
@@ -103,26 +95,62 @@ class VoiceService {
103
95
  }
104
96
 
105
97
  return {
106
- voiceEnabled: result.voice_enabled === 1,
107
- ttsEnabled: result.tts_enabled === 1,
108
- sttProvider: result.stt_provider,
109
- ttsProvider: result.tts_provider,
110
- ttsVoiceId: result.tts_voice_id,
98
+ voiceEnabled: entry.doc.voice_enabled,
99
+ ttsEnabled: entry.doc.tts_enabled,
100
+ sttProvider: entry.doc.stt_provider,
101
+ ttsProvider: entry.doc.tts_provider,
102
+ ttsVoiceId: entry.doc.tts_voice_id,
111
103
  };
112
104
  }
113
105
 
106
+ /** Provider de un modelo según la BD; acepta también un id de provider (canales antiguos). */
107
+ private async getModelProvider(modelId: string): Promise<string | null> {
108
+ try {
109
+ const modelsCol = await col<ModelDoc>("models");
110
+ const model = await modelsCol.get(modelId);
111
+ if (model?.doc.provider_id) return model.doc.provider_id;
112
+ const providersCol = await col<import("../storage/collections").ProviderDoc>("providers");
113
+ const provider = await providersCol.get(modelId);
114
+ return provider?.doc.id || null;
115
+ } catch {
116
+ return null;
117
+ }
118
+ }
119
+
120
+ /** Primer modelo STT/TTS activo de un provider activo, como fallback desde la BD. */
121
+ private async getFirstActiveVoiceModel(type: "stt" | "tts"): Promise<{ id: string; provider: string } | null> {
122
+ try {
123
+ const modelsCol = await col<ModelDoc>("models");
124
+ const providersCol = await col<import("../storage/collections").ProviderDoc>("providers");
125
+ const models = (await modelsCol.findBy("model_type", type)).filter(e => e.doc.active);
126
+ for (const m of models) {
127
+ const provider = await providersCol.get(m.doc.provider_id);
128
+ if (provider?.doc.active) return { id: m.doc.id, provider: m.doc.provider_id };
129
+ }
130
+ return null;
131
+ } catch {
132
+ return null;
133
+ }
134
+ }
135
+
114
136
  async transcribe(audio: AudioInput, modelId: string): Promise<string> {
115
- const isGroq = modelId.startsWith("whisper");
116
- const isOpenAi = modelId === "whisper-1";
117
-
118
- if (isGroq) {
119
- return this.transcribeWithGroq(audio, modelId);
120
- } else if (isOpenAi) {
121
- return this.transcribeWithOpenAIWhisper(audio);
137
+ let provider = await this.getModelProvider(modelId);
138
+ let resolvedModelId = modelId;
139
+
140
+ if (!provider) {
141
+ const fallback = await this.getFirstActiveVoiceModel("stt");
142
+ if (!fallback) throw new Error(`STT model "${modelId}" not found and no active STT models in the database`);
143
+ log.warn(`STT model ${modelId} not found in DB, falling back to ${fallback.provider}/${fallback.id}`);
144
+ provider = fallback.provider;
145
+ resolvedModelId = fallback.id;
146
+ }
147
+
148
+ switch (provider) {
149
+ case "groq": return this.transcribeWithGroq(audio, resolvedModelId);
150
+ case "openai": return this.transcribeWithOpenAIWhisper(audio);
151
+ default:
152
+ throw new Error(`STT not supported for provider "${provider}" (model ${resolvedModelId})`);
122
153
  }
123
-
124
- log.warn(`Unknown STT provider ${modelId}, defaulting to Groq Whisper`);
125
- return this.transcribeWithGroq(audio, "whisper-large-v3-turbo");
126
154
  }
127
155
 
128
156
  private async getProviderApiKey(providerId: string): Promise<string | null> {
@@ -229,26 +257,26 @@ class VoiceService {
229
257
  }
230
258
 
231
259
  async speak(text: string, modelId: string, voiceId?: string): Promise<AudioOutput> {
232
- const isElevenLabs = modelId.startsWith("eleven");
233
- const isOpenAI = modelId.startsWith("tts-") || modelId.startsWith("gpt-");
234
- const isGemini = modelId.startsWith("gemini");
235
- const isQwen = modelId.startsWith("qwen");
236
- const isPiper = modelId === "piper" || modelId === "piper-local";
237
-
238
- if (isPiper) {
239
- return this.speakWithPiper(text, voiceId);
240
- } else if (isElevenLabs) {
241
- return this.speakWithElevenLabs(text, modelId, voiceId);
242
- } else if (isOpenAI) {
243
- return this.speakWithOpenAI(text, modelId, voiceId);
244
- } else if (isGemini) {
245
- return this.speakWithGemini(text, modelId, voiceId);
246
- } else if (isQwen) {
247
- return this.speakWithQwen(text, modelId, voiceId);
248
- }
249
-
250
- log.warn(`Unknown TTS provider ${modelId}, defaulting to ElevenLabs Flash`);
251
- return this.speakWithElevenLabs(text, "eleven_flash_v2_5", voiceId);
260
+ let provider = modelId === "piper-local" ? "piper" : await this.getModelProvider(modelId);
261
+ let resolvedModelId = modelId;
262
+
263
+ if (!provider) {
264
+ const fallback = await this.getFirstActiveVoiceModel("tts");
265
+ if (!fallback) throw new Error(`TTS model "${modelId}" not found and no active TTS models in the database`);
266
+ log.warn(`TTS model ${modelId} not found in DB, falling back to ${fallback.provider}/${fallback.id}`);
267
+ provider = fallback.provider;
268
+ resolvedModelId = fallback.id;
269
+ }
270
+
271
+ switch (provider) {
272
+ case "piper": return this.speakWithPiper(text, voiceId);
273
+ case "elevenlabs": return this.speakWithElevenLabs(text, resolvedModelId, voiceId);
274
+ case "openai": return this.speakWithOpenAI(text, resolvedModelId, voiceId);
275
+ case "gemini": return this.speakWithGemini(text, resolvedModelId, voiceId);
276
+ case "qwen": return this.speakWithQwen(text, resolvedModelId, voiceId);
277
+ default:
278
+ throw new Error(`TTS not supported for provider "${provider}" (model ${resolvedModelId})`);
279
+ }
252
280
  }
253
281
 
254
282
  private async speakWithPiper(text: string, voiceId?: string): Promise<AudioOutput> {
@@ -448,21 +476,19 @@ class VoiceService {
448
476
  };
449
477
  }
450
478
 
451
- getConfiguredVoiceProviders(): { groq: boolean; elevenlabs: boolean; openai: boolean; gemini: boolean; qwen: boolean } {
452
- const db = getDb();
453
- const hasDbKey = (providerId: string): boolean => {
454
- const row = db.query(
455
- `SELECT api_key_encrypted FROM providers WHERE id = ? AND api_key_encrypted IS NOT NULL AND api_key_encrypted != ''`
456
- ).get(providerId) as { api_key_encrypted: string } | undefined;
457
- return !!row;
458
- };
479
+ async getConfiguredVoiceProviders(): Promise<{ groq: boolean; elevenlabs: boolean; openai: boolean; gemini: boolean; qwen: boolean }> {
480
+ const hasDbKey = async (providerId: string): Promise<boolean> => !!(await loadProviderApiKey(providerId));
481
+
482
+ const [groq, elevenlabs, openai, gemini, qwen] = await Promise.all([
483
+ hasDbKey("groq"), hasDbKey("elevenlabs"), hasDbKey("openai"), hasDbKey("gemini"), hasDbKey("qwen"),
484
+ ]);
459
485
 
460
486
  return {
461
- groq: hasDbKey("groq") || !!(process.env.GROQ_API_KEY),
462
- elevenlabs: hasDbKey("elevenlabs") || !!(process.env.ELEVENLABS_API_KEY),
463
- openai: hasDbKey("openai") || !!(process.env.OPENAI_API_KEY),
464
- gemini: hasDbKey("gemini") || !!(process.env.GEMINI_API_KEY),
465
- qwen: hasDbKey("qwen") || !!(process.env.DASHSCOPE_API_KEY),
487
+ groq: groq || !!(process.env.GROQ_API_KEY),
488
+ elevenlabs: elevenlabs || !!(process.env.ELEVENLABS_API_KEY),
489
+ openai: openai || !!(process.env.OPENAI_API_KEY),
490
+ gemini: gemini || !!(process.env.GEMINI_API_KEY),
491
+ qwen: qwen || !!(process.env.DASHSCOPE_API_KEY),
466
492
  };
467
493
  }
468
494
 
@@ -5,8 +5,8 @@
5
5
  * Sends: { type: "AGENT_RESULT", taskId, result } | { type: "AGENT_CHUNK", taskId, chunk }
6
6
  */
7
7
 
8
- import { runAgent } from "../agent/AgentRunner.ts";
9
- import type { StreamChunk } from "../agent/AgentRunner.ts";
8
+ import { runAgent } from "../agent/agent-loop.ts";
9
+ import type { StreamChunk } from "../agent/agent-loop.ts";
10
10
 
11
11
  declare var self: {
12
12
  onmessage: ((event: { data: WorkerMessage }) => void) | null;
@@ -1,16 +1,9 @@
1
- import { describe, expect, it, beforeAll, afterAll } from "bun:test";
1
+ process.env.HIVE_DB_PATH = ":memory:";
2
+
3
+ import { describe, expect, it } from "bun:test";
2
4
  import { createWorker, WorkerPool } from "./index.ts";
3
- import { setupTestDb, teardownTestDb, insertTestAgent, insertTestProvider } from "../../../../test/setup-db.ts";
4
5
 
5
6
  describe("createWorker", () => {
6
- beforeAll(() => {
7
- setupTestDb();
8
- });
9
-
10
- afterAll(() => {
11
- teardownTestDb();
12
- });
13
-
14
7
  it("creates a worker instance with config", () => {
15
8
  const worker = createWorker({
16
9
  name: "test-worker",