@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
@@ -1,5 +1,6 @@
1
1
  import { existsSync } from "node:fs"
2
2
  import { fileURLToPath } from "node:url"
3
+ import { dirname, join } from "node:path"
3
4
  import { availableParallelism } from "node:os"
4
5
  import type { Config } from "../config/loader.ts"
5
6
  import { loadConfig } from "../config/loader.ts"
@@ -15,6 +16,8 @@ export type ToolCallLike = {
15
16
  export type RuntimeTool = {
16
17
  name: string
17
18
  execute?: (params: Record<string, unknown>, config?: any) => Promise<unknown>
19
+ /** Per-tool timeout (ms) override from the Tool definition. */
20
+ timeoutMs?: number
18
21
  }
19
22
 
20
23
  export type ToolRuntimeConfig = {
@@ -32,6 +35,12 @@ export type ExecuteToolBatchOptions = {
32
35
  thread_id?: string
33
36
  channel?: string
34
37
  workspace?: string | null
38
+ /** The currently-running agent's own id (agent-loop.ts's opts.agentId) — read by tools that need to know who's calling them (task_delegate's parent lookup, agent_create's parent_id, bus_publish's sender). */
39
+ agent_id?: string
40
+ run_id?: string
41
+ turn_id?: string
42
+ task_id?: string
43
+ session_id?: string
35
44
  }
36
45
  hiveConfig?: Config
37
46
  workerPool?: ToolRuntimeConfig
@@ -110,8 +119,28 @@ function resolveWorkerEntry(): string {
110
119
  }
111
120
  }
112
121
 
122
+ const fallbacks: string[] = []
123
+ const envPath = process.env.HIVE_TOOL_WORKER_PATH
124
+ if (envPath) fallbacks.push(envPath)
125
+
126
+ try {
127
+ const execDir = dirname(process.execPath)
128
+ fallbacks.push(join(execDir, "tool-worker.js"))
129
+ fallbacks.push(join(execDir, "packages", "core", "src", "tool-runtime", "tool-worker.js"))
130
+ } catch {
131
+ // process.execPath is not available — skip execDir-based fallbacks
132
+ }
133
+
134
+ fallbacks.push("/app/tool-worker.js")
135
+
136
+ for (const filePath of fallbacks) {
137
+ if (existsSync(filePath)) {
138
+ return filePath
139
+ }
140
+ }
141
+
113
142
  throw new Error(
114
- `Tool worker entry not found. Tried: ${candidates.map((candidate) => fileURLToPath(candidate)).join(", ")}`
143
+ `Tool worker entry not found. Tried: ${[...candidates.map((candidate) => fileURLToPath(candidate)), ...fallbacks].join(", ")}`
115
144
  )
116
145
  }
117
146
 
@@ -139,7 +168,7 @@ function toolErrorResult(
139
168
  }
140
169
 
141
170
  const DEFAULT_MAIN_THREAD_TOOL_NAMES = new Set([
142
- // These tools depend on process-local singleton state (SQLite DB, live
171
+ // These tools depend on process-local singleton state (HiveDB handle, live
143
172
  // channel senders, schedulers, browser sessions, or in-memory services).
144
173
  "search_knowledge",
145
174
  "save_note",
@@ -155,24 +184,14 @@ const DEFAULT_MAIN_THREAD_TOOL_NAMES = new Set([
155
184
  "bus_publish",
156
185
  "bus_read",
157
186
  "get_available_models",
158
- "meeting_start",
159
- "meeting_add_segment",
160
- "meeting_stop",
161
- "meeting_report",
162
187
  "browser_navigate",
163
188
  "browser_screenshot",
189
+ "artifact_inspect",
164
190
  "browser_click",
165
191
  "browser_type",
166
192
  "browser_extract",
167
193
  "browser_script",
168
194
  "browser_wait",
169
- "canvas_render",
170
- "canvas_ask",
171
- "canvas_confirm",
172
- "canvas_show_card",
173
- "canvas_show_progress",
174
- "canvas_show_list",
175
- "canvas_clear",
176
195
  "a2ui_create_surface",
177
196
  "a2ui_update_components",
178
197
  "a2ui_update_data_model",
@@ -188,15 +207,14 @@ const DEFAULT_MAIN_THREAD_TOOL_NAMES = new Set([
188
207
  "notify",
189
208
  "report_progress",
190
209
  "task_delegate",
191
- "task_delegate_code",
192
- "voice_transcribe",
193
- "voice_speak",
210
+ "task_list",
194
211
  ])
195
212
 
196
213
  async function executeInMainThread(job: {
197
214
  toolCall: ToolCallLike
198
215
  allTools: RuntimeTool[]
199
216
  toolConfig: ExecuteToolBatchOptions["toolConfig"]
217
+ signal?: AbortSignal
200
218
  }): Promise<unknown> {
201
219
  const toolName = job.toolCall.function.name
202
220
  const tool = job.allTools.find((candidate) => candidate.name === toolName)
@@ -208,16 +226,36 @@ async function executeInMainThread(job: {
208
226
  const args = typeof job.toolCall.function.arguments === "string"
209
227
  ? JSON.parse(job.toolCall.function.arguments)
210
228
  : job.toolCall.function.arguments
211
- return await tool.execute((args ?? {}) as Record<string, unknown>, { configurable: job.toolConfig })
229
+ return await tool.execute((args ?? {}) as Record<string, unknown>, { configurable: job.toolConfig, signal: job.signal })
212
230
  } catch (error) {
213
231
  return toolErrorResult(toolName, (error as Error).message)
214
232
  }
215
233
  }
216
234
 
235
+ /**
236
+ * Bounds a single tool execution to its own timeout window (per-operation, not
237
+ * an aggregate turn deadline). A slow tool loses its race and yields a normal
238
+ * error result — the underlying promise is left to settle on its own (main-thread
239
+ * tools have no cross-realm handle to kill), but the caller is freed to move on.
240
+ */
241
+ export function executeInMainThreadWithTimeout(
242
+ job: { toolCall: ToolCallLike; allTools: RuntimeTool[]; toolConfig: ExecuteToolBatchOptions["toolConfig"]; signal?: AbortSignal },
243
+ timeoutMs: number,
244
+ ): Promise<unknown> {
245
+ const toolName = job.toolCall.function.name
246
+ return Promise.race([
247
+ executeInMainThread(job),
248
+ new Promise<unknown>((resolve) => {
249
+ setTimeout(() => resolve(toolErrorResult(toolName, `Tool execution timed out after ${timeoutMs}ms`)), timeoutMs)
250
+ }),
251
+ ])
252
+ }
253
+
217
254
  class ToolWorkerPool {
218
255
  private workers: WorkerSlot[] = []
219
256
  private queue: QueuedJob[] = []
220
257
  private readonly maxWorkers: number
258
+ private disposed = false
221
259
 
222
260
  constructor(maxWorkers: number) {
223
261
  this.maxWorkers = Math.max(1, maxWorkers)
@@ -225,6 +263,18 @@ class ToolWorkerPool {
225
263
 
226
264
  execute(job: Omit<QueuedJob, "resolve" | "settled" | "startedAt">): Promise<ToolBatchResult> {
227
265
  return new Promise((resolve) => {
266
+ if (this.disposed) {
267
+ resolve({
268
+ toolCall: job.toolCall,
269
+ toolName: job.toolCall.function.name,
270
+ result: toolErrorResult(job.toolCall.function.name, "Tool runtime shut down"),
271
+ ok: false,
272
+ durationMs: 0,
273
+ error: { name: "AbortError", message: "Tool runtime shut down" },
274
+ aborted: true,
275
+ })
276
+ return
277
+ }
228
278
  this.queue.push({
229
279
  ...job,
230
280
  resolve,
@@ -272,14 +322,48 @@ class ToolWorkerPool {
272
322
  }
273
323
 
274
324
  dispose(): void {
325
+ this.disposed = true
326
+ const reason = "Tool runtime shut down"
327
+
328
+ for (const job of this.queue) {
329
+ if (job.settled) continue
330
+ job.settled = true
331
+ job.resolve({
332
+ toolCall: job.toolCall,
333
+ toolName: job.toolCall.function.name,
334
+ result: toolErrorResult(job.toolCall.function.name, reason),
335
+ ok: false,
336
+ durationMs: 0,
337
+ error: { name: "AbortError", message: reason },
338
+ aborted: true,
339
+ })
340
+ }
341
+ this.queue = []
342
+
275
343
  for (const slot of this.workers) {
344
+ const job = slot.job
345
+ if (job && !job.settled) {
346
+ job.settled = true
347
+ if (job.timer) clearTimeout(job.timer)
348
+ job.resolve({
349
+ toolCall: job.toolCall,
350
+ toolName: job.toolCall.function.name,
351
+ result: toolErrorResult(job.toolCall.function.name, reason),
352
+ ok: false,
353
+ durationMs: Math.round(performance.now() - job.startedAt),
354
+ error: { name: "AbortError", message: reason },
355
+ aborted: true,
356
+ })
357
+ }
358
+ slot.job = undefined
359
+ slot.busy = false
276
360
  slot.worker.terminate()
277
361
  }
278
362
  this.workers = []
279
- this.queue = []
280
363
  }
281
364
 
282
365
  private drain(): void {
366
+ if (this.disposed) return
283
367
  while (this.queue.length > 0) {
284
368
  const slot = this.getIdleSlot()
285
369
  if (!slot) return
@@ -381,6 +465,7 @@ class ToolWorkerPool {
381
465
  ): Promise<void> {
382
466
  const job = slot.job
383
467
  if (!job || job.id !== message.jobId || job.settled) return
468
+ const worker = slot.worker
384
469
 
385
470
  try {
386
471
  const result = await executeInMainThread({
@@ -394,9 +479,15 @@ class ToolWorkerPool {
394
479
  allTools: job.allTools,
395
480
  toolConfig: job.toolConfig,
396
481
  })
397
- slot.worker.postMessage({ type: "rpc_result", rpcId: message.rpcId, ok: true, result })
482
+ if (this.disposed || slot.job !== job || job.settled || slot.worker !== worker) return
483
+ worker.postMessage({ type: "rpc_result", rpcId: message.rpcId, ok: true, result })
398
484
  } catch (error) {
399
- slot.worker.postMessage({ type: "rpc_result", rpcId: message.rpcId, ok: false, error: serializeError(error) })
485
+ if (this.disposed || slot.job !== job || job.settled || slot.worker !== worker) return
486
+ try {
487
+ worker.postMessage({ type: "rpc_result", rpcId: message.rpcId, ok: false, error: serializeError(error) })
488
+ } catch {
489
+ // El worker pudo terminar entre la comprobación y el envío.
490
+ }
400
491
  }
401
492
  }
402
493
 
@@ -439,6 +530,26 @@ function resolveRuntimeConfig(config?: ToolRuntimeConfig): Required<ToolRuntimeC
439
530
  }
440
531
  }
441
532
 
533
+ /**
534
+ * Resolve the effective timeout (ms) for a single tool call.
535
+ * Priority: Tool.timeoutMs → config.tools.timeouts[name] → workerPool.toolTimeoutMs.
536
+ */
537
+ function resolveToolTimeout(
538
+ toolName: string,
539
+ allTools: RuntimeTool[],
540
+ hiveConfig: Config,
541
+ baseTimeoutMs: number,
542
+ ): number {
543
+ // 1. Tool definition timeoutMs (carried on the RuntimeTool via ContextTool cast)
544
+ const def = allTools.find((t) => t.name === toolName)
545
+ if (def?.timeoutMs && def.timeoutMs > 0) return def.timeoutMs
546
+ // 2. config.tools.timeouts[name]
547
+ const cfgOverride = hiveConfig?.tools?.timeouts?.[toolName]
548
+ if (typeof cfgOverride === "number" && cfgOverride > 0) return cfgOverride
549
+ // 3. base (workerPool.toolTimeoutMs)
550
+ return baseTimeoutMs
551
+ }
552
+
442
553
  function getPool(maxWorkers: number): ToolWorkerPool {
443
554
  if (!sharedPool || sharedPoolSize !== maxWorkers) {
444
555
  sharedPool = new ToolWorkerPool(maxWorkers)
@@ -471,11 +582,18 @@ export async function executeToolBatch(options: ExecuteToolBatchOptions): Promis
471
582
  const results: ToolBatchResult[] = []
472
583
  for (const toolCall of options.toolCalls) {
473
584
  const startedAt = performance.now()
474
- const result = await executeInMainThread({
585
+ const effectiveTimeout = resolveToolTimeout(
586
+ toolCall.function.name,
587
+ options.allTools,
588
+ hiveConfig,
589
+ runtimeConfig.toolTimeoutMs,
590
+ )
591
+ const result = await executeInMainThreadWithTimeout({
475
592
  toolCall,
476
593
  allTools: options.allTools,
477
594
  toolConfig: options.toolConfig,
478
- })
595
+ signal: options.signal,
596
+ }, effectiveTimeout)
479
597
  results.push({
480
598
  toolCall,
481
599
  toolName: toolCall.function.name,
@@ -502,7 +620,12 @@ export async function executeToolBatch(options: ExecuteToolBatchOptions): Promis
502
620
  toolConfig: options.toolConfig,
503
621
  hiveConfig,
504
622
  mainThreadToolNames,
505
- timeoutMs: runtimeConfig.toolTimeoutMs,
623
+ timeoutMs: resolveToolTimeout(
624
+ toolCall.function.name,
625
+ options.allTools,
626
+ hiveConfig,
627
+ runtimeConfig.toolTimeoutMs,
628
+ ),
506
629
  })))
507
630
 
508
631
  return results.sort((a, b) => {
@@ -1,4 +1,4 @@
1
- import { createAllTools } from "../tools/index.ts"
1
+ import { createWorkerTools } from "./worker-tools.ts"
2
2
  import type { Config } from "../config/loader.ts"
3
3
 
4
4
  type WorkerRunMessage = {
@@ -75,7 +75,7 @@ async function runTool(message: WorkerRunMessage): Promise<void> {
75
75
  try {
76
76
  const parsedArgs = parseArgs(message.args)
77
77
  const forceMainThread = message.mainThreadToolNames.includes(message.toolName)
78
- const allTools = forceMainThread ? [] : createAllTools(message.hiveConfig)
78
+ const allTools = forceMainThread ? [] : createWorkerTools(message.hiveConfig)
79
79
  const tool = allTools.find((candidate) => candidate.name === message.toolName)
80
80
 
81
81
  const result = tool?.execute
@@ -0,0 +1,27 @@
1
+ import type { Config } from "../config/loader.ts"
2
+ import type { Tool } from "../tools/types.ts"
3
+ import * as filesystem from "../tools/filesystem/index.ts"
4
+ import { webSearchTool } from "../tools/web/web-search.ts"
5
+ import { webFetchTool } from "../tools/web/web-fetch.ts"
6
+ import * as cli from "../tools/cli/index.ts"
7
+ import * as office from "../tools/office/index.ts"
8
+ import * as api from "../tools/api/index.ts"
9
+
10
+ /**
11
+ * Reconstruct only stateless tools inside worker realms.
12
+ *
13
+ * Importing the complete tool registry pulls process-local services (including
14
+ * HiveDB) into tool-worker.js even though those tools are always executed by
15
+ * RPC on the main thread. Keeping this registry explicit makes the worker
16
+ * bundle platform-neutral and prevents native bindings from leaking into it.
17
+ */
18
+ export function createWorkerTools(_config: Config): Tool[] {
19
+ return [
20
+ ...filesystem.createTools(),
21
+ webSearchTool,
22
+ webFetchTool,
23
+ ...cli.createTools(),
24
+ ...office.createTools(),
25
+ ...api.createTools(),
26
+ ]
27
+ }
@@ -1,14 +1,14 @@
1
- import type { Tool } from "../agent/NativeTools.ts";
2
- import type { Config } from "../config/loader.ts";
3
- import { canvasManager } from "./CanvasManager.ts";
4
- import { logger } from "../utils/logger.ts";
1
+ import type { Tool } from "../types.ts";
2
+ import type { Config } from "../../config/loader.ts";
3
+ import { canvasManager } from "../../canvas/canvas-manager.ts";
4
+ import { logger } from "../../utils/logger.ts";
5
5
 
6
6
  export function createA2UISurfaceTool(_config: Config): Tool {
7
7
  const log = logger.child("a2ui-surface");
8
8
 
9
9
  return {
10
10
  name: "a2ui_create_surface",
11
- description: "Create an A2UI v0.9 surface on the user's canvas for rendering rich interactive UIs",
11
+ description: "Create an A2UI v0.9 surface in the user's interactive panel",
12
12
  parameters: {
13
13
  type: "object",
14
14
  properties: {
@@ -58,7 +58,7 @@ export function createA2UISurfaceTool(_config: Config): Tool {
58
58
 
59
59
  if (!canvasManager.isSessionConnected(sessionId)) {
60
60
  const connected = canvasManager.getConnectedSessions();
61
- log.warn(`No canvas sessions connected. Rendering to ${sessionId} anyway. Available: ${connected.join(", ")}`);
61
+ log.warn(`No visual sessions connected. Rendering to ${sessionId} anyway. Available: ${connected.join(", ")}`);
62
62
  }
63
63
 
64
64
  await canvasManager.sendA2UIMessage(sessionId, "a2ui:createSurface", {
@@ -219,7 +219,7 @@ export function createA2UIDeleteSurfaceTool(_config: Config): Tool {
219
219
 
220
220
  return {
221
221
  name: "a2ui_delete_surface",
222
- description: "Delete an A2UI v0.9 surface and remove it from the user's canvas",
222
+ description: "Delete an A2UI v0.9 surface from the user's interactive panel",
223
223
  parameters: {
224
224
  type: "object",
225
225
  properties: {
@@ -252,4 +252,13 @@ export function createA2UIDeleteSurfaceTool(_config: Config): Tool {
252
252
  });
253
253
  },
254
254
  };
255
- }
255
+ }
256
+
257
+ export function createTools(config: Config): Tool[] {
258
+ return [
259
+ createA2UISurfaceTool(config),
260
+ createA2UIUpdateComponentsTool(config),
261
+ createA2UIUpdateDataModelTool(config),
262
+ createA2UIDeleteSurfaceTool(config),
263
+ ];
264
+ }
@@ -8,12 +8,12 @@
8
8
  */
9
9
 
10
10
  import type { Tool } from "../types.ts";
11
- import { getHiveDB } from "../../storage/HiveDBStorage.ts";
12
- import type { HiveProviderDoc, HiveModelDoc } from "../../storage/hiveSeed.ts";
11
+ import { col } from "../../storage/hive.ts";
12
+ import type { ProviderDoc, ModelDoc } from "../../storage/collections.ts";
13
13
 
14
14
  export const getAvailableModelsTool: Tool = {
15
15
  name: "get_available_models",
16
- description: "Obtener lista de providers y modelos activos de la base de datos. Sinónimos: ver modelos, listar providers, modelos disponibles, consultar modelos, provider activo, qué modelos tengo, modelos para código, modelos para chat",
16
+ description: "Obtener el catálogo completo de modelos de los providers configurados (con credenciales activas) para elegir el más adecuado según capacidad, contexto o costo — no solo el modelo por defecto del usuario. Sinónimos: ver modelos, listar providers, modelos disponibles, consultar modelos, provider activo, qué modelos tengo, modelos para código, modelos para chat",
17
17
  parameters: {
18
18
  type: "object",
19
19
  properties: {
@@ -39,70 +39,52 @@ export const getAvailableModelsTool: Tool = {
39
39
  };
40
40
 
41
41
  try {
42
- const db = await getHiveDB();
43
- const providersCol = db.collection<HiveProviderDoc>("providers");
44
- const modelsCol = db.collection<HiveModelDoc>("models");
42
+ const providersCol = await col<ProviderDoc>("providers");
43
+ const providersById = new Map(
44
+ (await providersCol.scan({}))
45
+ .map(e => e.doc)
46
+ .filter(p => p.enabled && p.active)
47
+ .map(p => [p.id, p])
48
+ );
45
49
 
46
- const [providers, models] = await Promise.all([
47
- providersCol.scan(),
48
- modelsCol.scan(),
49
- ]);
50
+ // Gate on the *provider* being configured (enabled + active, i.e. it has valid
51
+ // credentials) — not on the individual model's own `active` flag. `active` on a
52
+ // ModelDoc only marks the single model onboarding/voice-setup picked as the
53
+ // user's current default; it is not "this model is unusable". Once a provider is
54
+ // configured, every model under it should be visible so the caller can pick the
55
+ // one that actually fits the task (capability, context window, cost), instead of
56
+ // being stuck with whichever model happened to be flagged as the default.
57
+ const modelsCol = await col<ModelDoc>("models");
58
+ let models = (await modelsCol.scan({}))
59
+ .map(e => e.doc)
60
+ .filter(m => m.enabled && providersById.has(m.provider_id));
50
61
 
51
- const activeProviders = new Map(
52
- providers
53
- .filter(p => p.doc.enabled && p.doc.active)
54
- .map(p => [p.id, p.doc])
55
- );
62
+ if (providerId) models = models.filter(m => m.provider_id === providerId);
63
+ if (modelType) models = models.filter(m => m.model_type === modelType);
64
+ if (capabilities) models = models.filter(m => (m.capabilities ?? "").includes(capabilities));
56
65
 
57
- let rows = models
58
- .filter(m => m.doc.enabled && m.doc.active)
66
+ // Transformar a formato amigable
67
+ const result = models
59
68
  .map(m => {
60
- const provider = activeProviders.get(m.doc.providerId);
61
- if (!provider) return null;
69
+ const provider = providersById.get(m.provider_id)!;
62
70
  return {
63
71
  providerId: provider.id,
64
72
  providerName: provider.name,
65
73
  providerCategory: provider.category,
66
- modelId: m.doc.id,
67
- modelName: m.doc.name,
68
- modelType: m.doc.modelType,
69
- contextWindow: m.doc.contextWindow ?? null,
70
- capabilities: m.doc.capabilities ?? null,
74
+ modelId: m.id,
75
+ modelName: m.name,
76
+ modelType: m.model_type,
77
+ contextWindow: m.context_window,
78
+ capabilities: m.capabilities ? JSON.parse(m.capabilities) : null,
79
+ isDefault: m.active,
71
80
  };
72
81
  })
73
- .filter(Boolean) as Array<{
74
- providerId: string;
75
- providerName: string;
76
- providerCategory: string;
77
- modelId: string;
78
- modelName: string;
79
- modelType: string;
80
- contextWindow: number | null;
81
- capabilities: string[] | null;
82
- }>;
83
-
84
- if (providerId) {
85
- rows = rows.filter(r => r.providerId === providerId);
86
- }
87
-
88
- if (modelType) {
89
- rows = rows.filter(r => r.modelType === modelType);
90
- }
91
-
92
- if (capabilities) {
93
- const cap = capabilities.toLowerCase();
94
- rows = rows.filter(r => r.capabilities?.some(c => c.toLowerCase().includes(cap)));
95
- }
96
-
97
- rows.sort((a, b) => {
98
- if (a.providerName !== b.providerName) return a.providerName.localeCompare(b.providerName);
99
- return a.modelName.localeCompare(b.modelName);
100
- });
82
+ .sort((a, b) => (Number(b.isDefault) - Number(a.isDefault)) || a.providerName.localeCompare(b.providerName) || a.modelName.localeCompare(b.modelName));
101
83
 
102
84
  return {
103
85
  ok: true,
104
- count: rows.length,
105
- models: rows,
86
+ count: result.length,
87
+ models: result,
106
88
  };
107
89
  } catch (error) {
108
90
  return {