@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,6 +1,7 @@
1
1
  import * as z from "zod";
2
2
  import { mkdirSync, existsSync, readFileSync } from "node:fs";
3
3
  import * as path from "node:path";
4
+ import { availableParallelism, homedir } from "node:os";
4
5
 
5
6
  const LogLevelSchema = z.enum(["debug", "info", "warn", "error"]);
6
7
  const DMPolicySchema = z.enum(["open", "pairing", "allowlist"]);
@@ -32,7 +33,7 @@ export function getHiveDir(): string {
32
33
  // Priority 1: HIVE_HOME explicitly set
33
34
  if (process.env.HIVE_HOME) {
34
35
  const hiveDir = process.env.HIVE_HOME.startsWith("~")
35
- ? path.join(process.env.HOME || "", process.env.HIVE_HOME.slice(1))
36
+ ? path.join(homedir(), process.env.HIVE_HOME.slice(1))
36
37
  : process.env.HIVE_HOME;
37
38
  loadEnv(hiveDir);
38
39
  return hiveDir;
@@ -48,7 +49,7 @@ export function getHiveDir(): string {
48
49
  }
49
50
 
50
51
  // Priority 3: Default ~/.hive
51
- const defaultDir = path.join(process.env.HOME || "", ".hive");
52
+ const defaultDir = path.join(homedir(), ".hive");
52
53
  loadEnv(defaultDir);
53
54
  return defaultDir;
54
55
  }
@@ -59,7 +60,7 @@ const expandPath = (p: string): string => {
59
60
  return p.replace("~/.hive", hiveDir);
60
61
  }
61
62
  if (p.startsWith("~")) {
62
- return path.join(process.env.HOME || "", p.slice(1));
63
+ return path.join(homedir(), p.slice(1));
63
64
  }
64
65
  return p;
65
66
  };
@@ -116,9 +117,14 @@ const WebConfigSchema = z.object({
116
117
 
117
118
  const BrowserConfigSchema = z.object({
118
119
  enabled: z.boolean().optional(),
119
- sessionName: z.string().optional(),
120
120
  headless: z.boolean().optional(),
121
121
  timeoutMs: z.number().optional(),
122
+ sessionName: z.string().optional(),
123
+ // "agent-browser" (default) usa Chrome via CLI y sirve headless/Docker.
124
+ // "webview" usa Bun.WebView in-process — mucho más rápido y sin instalación,
125
+ // pero requiere Chrome instalado (o macOS con WebKit). "auto" toma webview
126
+ // sólo si hay motor. Lo pisa HIVE_BROWSER_BACKEND.
127
+ backend: z.enum(["agent-browser", "webview", "auto"]).optional(),
122
128
  });
123
129
 
124
130
  const CanvasConfigSchema = z.object({
@@ -126,6 +132,13 @@ const CanvasConfigSchema = z.object({
126
132
  port: z.number().optional(),
127
133
  });
128
134
 
135
+ const WorkerPoolConfigSchema = z.object({
136
+ enabled: z.boolean().optional(),
137
+ maxWorkers: z.number().optional(),
138
+ toolTimeoutMs: z.number().optional(),
139
+ parallelToolCalls: z.boolean().optional(),
140
+ });
141
+
129
142
  const SandboxConfigSchema = z.object({
130
143
  dm: ToolRestrictionsSchema.optional(),
131
144
  group: ToolRestrictionsSchema.optional(),
@@ -138,7 +151,12 @@ const ToolsConfigSchema = z.object({
138
151
  web: WebConfigSchema.optional(),
139
152
  browser: BrowserConfigSchema.optional(),
140
153
  canvas: CanvasConfigSchema.optional(),
154
+ workerPool: WorkerPoolConfigSchema.optional(),
141
155
  sandbox: SandboxConfigSchema.optional(),
156
+ // Per-tool timeout overrides (ms) keyed by tool name. Falls back to
157
+ // workerPool.toolTimeoutMs when absent. Long-running tools like cli_exec
158
+ // should set a higher value (e.g. 600000 = 10min).
159
+ timeouts: z.record(z.string(), z.number()).optional(),
142
160
  });
143
161
 
144
162
  const ContextConfigSchema = z.object({
@@ -237,6 +255,13 @@ const CronConfigSchema = z.object({
237
255
  timezone: z.string().optional(),
238
256
  });
239
257
 
258
+ // G9 causal event log (HiveDB): IntentLogged/StateTransition/ToolCall emission
259
+ // from agent-loop.ts, consumed by reflector/curator/context-compiler. Off by
260
+ // default — each turn adds N+M+1 awaited db.append() calls to the critical path.
261
+ const CausalLogConfigSchema = z.object({
262
+ enabled: z.boolean().optional(),
263
+ });
264
+
240
265
  const RetryConfigSchema = z.object({
241
266
  maxAttempts: z.number().optional(),
242
267
  initialDelayMs: z.number().optional(),
@@ -244,6 +269,25 @@ const RetryConfigSchema = z.object({
244
269
  maxDelayMs: z.number().optional(),
245
270
  });
246
271
 
272
+ const JobRetryConfigSchema = z.object({
273
+ // Logical-failure retries (executor returned {ok:false}). Separate from
274
+ // JobDoc.attempts, which only counts crash/lease-expiry reclaims.
275
+ maxRetries: z.number().optional(),
276
+ initialDelayMs: z.number().optional(),
277
+ backoffMultiplier: z.number().optional(),
278
+ maxDelayMs: z.number().optional(),
279
+ jitter: z.number().optional(),
280
+ });
281
+
282
+ const HarnessConfigSchema = z.object({
283
+ maxGlobalConcurrency: z.number().optional(),
284
+ taskTimeoutMs: z.number().optional(),
285
+ jobLeaseMs: z.number().optional(),
286
+ runLeaseMs: z.number().optional(),
287
+ leaseRenewMs: z.number().optional(),
288
+ jobRetry: JobRetryConfigSchema.optional(),
289
+ });
290
+
247
291
  const HooksConfigSchema = z.object({
248
292
  scripts: z.object({
249
293
  before_model_resolve: z.string().optional(),
@@ -281,7 +325,7 @@ const GatewayConfigSchema = z.object({
281
325
  });
282
326
 
283
327
  const ModelsConfigSchema = z.object({
284
- defaultProvider: z.enum(["openai", "anthropic", "gemini", "mistral", "kimi", "ollama", "openrouter", "deepseek"]).optional(),
328
+ defaultProvider: z.enum(["openai", "anthropic", "gemini", "mistral", "kimi", "ollama", "openrouter", "deepseek", "hiveagents"]).optional(),
285
329
  defaults: z.record(z.string(), z.string()).optional(),
286
330
  providers: z.record(z.string(), ProviderConfigSchema).optional(),
287
331
  });
@@ -307,17 +351,6 @@ const SecurityConfigSchema = z.object({
307
351
  allowedUsers: z.array(z.string()).optional(),
308
352
  });
309
353
 
310
- const CaptchaConfigSchema = z.object({
311
- enabled: z.boolean().optional(),
312
- autoSolve: z.boolean().optional(),
313
- visionProvider: z.enum(["gemini", "openai", "anthropic"]).optional(),
314
- visionModel: z.string().optional(),
315
- maxAttempts: z.number().optional(),
316
- maxRounds: z.number().optional(),
317
- apiKey: z.string().optional(),
318
- enabledSites: z.array(z.string()).optional(),
319
- });
320
-
321
354
  const UserConfigSchema = z.object({
322
355
  id: z.string(),
323
356
  name: z.string(),
@@ -345,10 +378,11 @@ const ConfigSchema = z.object({
345
378
  mcp: MCPConfigSchema.optional(),
346
379
  memory: MemoryConfigSchema.optional(),
347
380
  cron: CronConfigSchema.optional(),
381
+ causalLog: CausalLogConfigSchema.optional(),
348
382
  retry: RetryConfigSchema.optional(),
383
+ harness: HarnessConfigSchema.optional(),
349
384
  security: SecurityConfigSchema.optional(),
350
385
  hooks: HooksConfigSchema.optional(),
351
- captcha: CaptchaConfigSchema.optional(),
352
386
  });
353
387
 
354
388
  export type Config = z.infer<typeof ConfigSchema>;
@@ -358,7 +392,6 @@ export type MCPServerConfig = z.infer<typeof MCPServerConfigSchema>;
358
392
  export type AgentEntry = z.infer<typeof AgentEntrySchema>;
359
393
  export type Binding = z.infer<typeof BindingSchema>;
360
394
  export type UserConfig = z.infer<typeof UserConfigSchema>;
361
- export type CaptchaConfig = z.infer<typeof CaptchaConfigSchema>;
362
395
 
363
396
  function buildDefaultConfig(): Config {
364
397
  const hiveDir = getHiveDir();
@@ -428,7 +461,7 @@ function buildDefaultConfig(): Config {
428
461
  allowlist: [],
429
462
  denylist: ["rm -rf /", "sudo", "chmod 777", "> /dev/", "mkfs"],
430
463
  timeoutSeconds: 30,
431
- workDir: path.join(process.env.HOME || "", "exec"), // Points to home for exec by default
464
+ workDir: path.join(homedir(), "exec"), // Points to home for exec by default
432
465
  },
433
466
  web: {
434
467
  allowlist: [],
@@ -437,14 +470,20 @@ function buildDefaultConfig(): Config {
437
470
  },
438
471
  browser: {
439
472
  enabled: true,
440
- sessionName: "hive",
441
473
  headless: true,
442
474
  timeoutMs: 30000,
475
+ sessionName: "hive",
443
476
  },
444
477
  canvas: {
445
478
  enabled: true,
446
479
  port: 18793,
447
480
  },
481
+ workerPool: {
482
+ enabled: true,
483
+ maxWorkers: Math.min(4, availableParallelism()),
484
+ toolTimeoutMs: 300000,
485
+ parallelToolCalls: true,
486
+ },
448
487
  sandbox: {
449
488
  dm: { allow: ["*"], deny: [] },
450
489
  group: { allow: ["*"], deny: [] },
@@ -480,12 +519,29 @@ function buildDefaultConfig(): Config {
480
519
  maxConcurrentJobs: 5,
481
520
  timezone: "UTC",
482
521
  },
522
+ causalLog: {
523
+ enabled: process.env.HIVE_CAUSAL_LOG === "true",
524
+ },
483
525
  retry: {
484
526
  maxAttempts: 3,
485
527
  initialDelayMs: 1000,
486
528
  backoffMultiplier: 2,
487
529
  maxDelayMs: 30000,
488
530
  },
531
+ harness: {
532
+ maxGlobalConcurrency: parseInt(process.env.HIVE_HARNESS_MAX_CONCURRENCY || "4", 10),
533
+ taskTimeoutMs: parseInt(process.env.HIVE_HARNESS_TASK_TIMEOUT_MS || String(30 * 60 * 1000), 10),
534
+ jobLeaseMs: parseInt(process.env.HIVE_HARNESS_JOB_LEASE_MS || String(30 * 60 * 1000), 10),
535
+ runLeaseMs: parseInt(process.env.HIVE_HARNESS_RUN_LEASE_MS || String(2 * 60 * 1000), 10),
536
+ leaseRenewMs: parseInt(process.env.HIVE_HARNESS_LEASE_RENEW_MS || "30000", 10),
537
+ jobRetry: {
538
+ maxRetries: parseInt(process.env.HIVE_HARNESS_JOB_MAX_RETRIES || "3", 10),
539
+ initialDelayMs: parseInt(process.env.HIVE_HARNESS_JOB_RETRY_INITIAL_MS || "1000", 10),
540
+ backoffMultiplier: parseFloat(process.env.HIVE_HARNESS_JOB_RETRY_MULTIPLIER || "2"),
541
+ maxDelayMs: parseInt(process.env.HIVE_HARNESS_JOB_RETRY_MAX_MS || String(5 * 60 * 1000), 10),
542
+ jitter: parseFloat(process.env.HIVE_HARNESS_JOB_RETRY_JITTER || "0.2"),
543
+ },
544
+ },
489
545
  security: {
490
546
  maxMessageLength: {
491
547
  telegram: 4096,
@@ -500,15 +556,6 @@ function buildDefaultConfig(): Config {
500
556
  hooks: {
501
557
  scripts: {},
502
558
  },
503
- captcha: {
504
- enabled: false,
505
- autoSolve: true,
506
- visionProvider: 'gemini',
507
- visionModel: 'gemini-2.0-flash-exp',
508
- maxAttempts: 3,
509
- maxRounds: 5,
510
- enabledSites: [],
511
- },
512
559
  };
513
560
  }
514
561
 
@@ -1,54 +1,108 @@
1
- import { describe, it, expect, beforeAll, afterAll } from "bun:test";
1
+ process.env.HIVE_DB_PATH = ":memory:";
2
+
3
+ import { describe, it, expect, beforeEach, afterEach } from "bun:test";
2
4
  import { EthicsGuard } from "./EthicsGuard.ts";
3
- import { getDb, initializeDatabase, dbService } from "../storage/SQLiteStorage.ts";
5
+ import { closeHiveDb } from "../storage/hivedb.ts";
6
+ import { ensureHiveDb } from "../storage/bootstrap.ts";
7
+ import { col } from "../storage/hive.ts";
8
+ import { toIndexable } from "../storage/hive.ts";
9
+ import type { PlaybookDoc } from "../storage/collections.ts";
10
+
11
+ async function addRule(id: string, rule: string, category: string, opts?: {
12
+ applicableTo?: string;
13
+ helpfulCount?: number;
14
+ active?: boolean;
15
+ }) {
16
+ const playbookCol = await col<PlaybookDoc>("playbook");
17
+ const now = Date.now();
18
+ await playbookCol.put(id, {
19
+ id,
20
+ rule,
21
+ category,
22
+ applicable_to: opts?.applicableTo ?? null,
23
+ helpful_count: opts?.helpfulCount ?? 0,
24
+ harmful_count: 0,
25
+ active: opts?.active ?? true,
26
+ source_reflection_id: toIndexable(null),
27
+ created_at: now,
28
+ updated_at: now,
29
+ });
30
+ }
31
+
32
+ beforeEach(async () => {
33
+ closeHiveDb();
34
+ await ensureHiveDb();
35
+ });
36
+
37
+ afterEach(() => {
38
+ closeHiveDb();
39
+ });
4
40
 
5
41
  describe("EthicsGuard", () => {
6
- let db: any;
42
+ it("sólo devuelve reglas de response_quality activas", async () => {
43
+ await addRule("rq-1", "Verificá las fuentes antes de responder", "response_quality");
44
+ await addRule("rq-2", "Regla apagada", "response_quality", { active: false });
45
+ await addRule("otra", "Usá web_search para noticias", "tool_selection");
46
+
47
+ const rules = await new EthicsGuard().getRules();
7
48
 
8
- beforeAll(async () => {
9
- await initializeDatabase();
10
- db = getDb();
11
- db.run(`
12
- INSERT OR IGNORE INTO playbook (id, rule, category, applicable_to, helpful_count, active)
13
- VALUES (1, 'Siempre verificar fuentes antes de responder', 'response_quality', 'agent', 5, 1)
14
- `);
49
+ expect(rules.map((r) => r.id)).toEqual(["rq-1"]);
15
50
  });
16
51
 
17
- afterAll(() => {
18
- dbService.close();
52
+ it("ordena por helpful_count descendente", async () => {
53
+ await addRule("poco", "poco útil", "response_quality", { helpfulCount: 1 });
54
+ await addRule("mucho", "muy útil", "response_quality", { helpfulCount: 9 });
55
+
56
+ const rules = await new EthicsGuard().getRules();
57
+
58
+ expect(rules.map((r) => r.id)).toEqual(["mucho", "poco"]);
19
59
  });
20
60
 
21
- it("loads rules from DB", () => {
22
- const guard = new EthicsGuard(db);
23
- const rules = guard.getRules();
24
- expect(Array.isArray(rules)).toBe(true);
61
+ it("filtra por agentRole cuando alguna regla lo declara", async () => {
62
+ await addRule("para-coord", "regla del coordinador", "response_quality", {
63
+ applicableTo: JSON.stringify(["coordinator"]),
64
+ });
65
+ await addRule("para-worker", "regla del worker", "response_quality", {
66
+ applicableTo: JSON.stringify(["worker"]),
67
+ });
68
+
69
+ const rules = await new EthicsGuard().getRules("coordinator");
70
+
71
+ expect(rules.map((r) => r.id)).toEqual(["para-coord"]);
25
72
  });
26
73
 
27
- it("injectIntoPrompt appends rules to system prompt", () => {
28
- const guard = new EthicsGuard(db);
29
- const rules = guard.getRules();
30
- const result = guard.injectIntoPrompt("Eres un asistente.", rules);
31
- expect(result).toContain("Eres un asistente.");
32
- if (rules.length > 0) {
33
- expect(result).toContain("Calidad de Respuesta");
34
- }
74
+ it("cae a todas las reglas si ninguna declara ese rol", async () => {
75
+ // Sin esto, un `applicable_to` mal cargado dejaría al agente sin capa
76
+ // de calidad en vez de con una de más.
77
+ await addRule("generica", "regla general", "response_quality", {
78
+ applicableTo: JSON.stringify(["worker"]),
79
+ });
80
+
81
+ const rules = await new EthicsGuard().getRules("coordinator");
82
+
83
+ expect(rules.map((r) => r.id)).toEqual(["generica"]);
35
84
  });
36
85
 
37
- it("hasEthicsLayer detects response quality rules", () => {
38
- const guard = new EthicsGuard(db);
39
- const has = guard.hasEthicsLayer();
40
- expect(typeof has).toBe("boolean");
86
+ it("injectIntoPrompt agrega las reglas y conserva el prompt original", async () => {
87
+ await addRule("rq-1", "Verificá las fuentes", "response_quality");
88
+
89
+ const guard = new EthicsGuard();
90
+ const result = guard.injectIntoPrompt("Eres un asistente.", await guard.getRules());
91
+
92
+ expect(result).toContain("Eres un asistente.");
93
+ expect(result).toContain("## Reglas de Calidad de Respuesta");
94
+ expect(result).toContain("- Verificá las fuentes");
41
95
  });
42
96
 
43
- it("getRules accepts optional agentRole for FTS5 search", () => {
44
- const guard = new EthicsGuard(db);
45
- const rules = guard.getRules("agent");
46
- expect(Array.isArray(rules)).toBe(true);
97
+ it("injectIntoPrompt devuelve el prompt intacto sin reglas", () => {
98
+ expect(new EthicsGuard().injectIntoPrompt("Eres un asistente.", [])).toBe("Eres un asistente.");
47
99
  });
48
100
 
49
- it("getRules without agentRole returns all rules", () => {
50
- const guard = new EthicsGuard(db);
51
- const rules = guard.getRules();
52
- expect(Array.isArray(rules)).toBe(true);
101
+ it("hasEthicsLayer refleja si hay reglas cargadas", async () => {
102
+ const guard = new EthicsGuard();
103
+ expect(await guard.hasEthicsLayer()).toBe(false);
104
+
105
+ await addRule("rq-1", "Verificá las fuentes", "response_quality");
106
+ expect(await guard.hasEthicsLayer()).toBe(true);
53
107
  });
54
108
  });
@@ -1,66 +1,70 @@
1
+ /**
2
+ * EthicsGuard — capa de reglas de calidad de respuesta sobre el system prompt.
3
+ *
4
+ * Lee las reglas `category: "response_quality"` de la colección `playbook`.
5
+ * Hasta 0.1.5 esta clase recibía un handle de SQLite y armaba SQL a mano
6
+ * (incluyendo un JOIN contra la tabla virtual `playbook_fts`); esas tablas ya no
7
+ * existen. Ahora la fuente es HiveDB, igual que para el resto del catálogo.
8
+ *
9
+ * Nota: la ética "constitucional" de un agente no pasa por acá — vive en la
10
+ * colección `ethics` y la ensambla `agent/prompt-builder.ts` como primera
11
+ * sección del prompt. Este guard es un complemento opcional para hosts que
12
+ * quieran inyectar reglas aprendidas por ACE.
13
+ */
14
+
15
+ import { col } from "../storage/hive.ts";
16
+ import type { PlaybookDoc } from "../storage/collections.ts";
17
+
1
18
  export interface EthicsRule {
2
- id: number;
19
+ id: string;
3
20
  rule: string;
4
21
  category: string;
5
- applicable_to: string;
22
+ applicable_to: string | null;
6
23
  helpful_count: number;
7
- active: number;
24
+ active: boolean;
8
25
  }
9
26
 
10
- export class EthicsGuard {
11
- private db: any;
27
+ const RESPONSE_QUALITY = "response_quality";
12
28
 
13
- constructor(db: any) {
14
- this.db = db;
15
- }
16
-
17
- getRules(agentRole?: string): EthicsRule[] {
18
- if (!agentRole) {
19
- return this.db
20
- .query(
21
- `SELECT p.* FROM playbook p
22
- WHERE p.category = 'response_quality' AND p.active = 1
23
- ORDER BY p.helpful_count DESC`
24
- )
25
- .all() as EthicsRule[];
26
- }
29
+ function byUsefulness(a: EthicsRule, b: EthicsRule): number {
30
+ return b.helpful_count - a.helpful_count;
31
+ }
27
32
 
28
- try {
29
- const ftsRows = this.db
30
- .query(
31
- `SELECT p.* FROM playbook p
32
- JOIN playbook_fts fts ON p.rowid = fts.rowid
33
- WHERE fts.playbook_fts MATCH ?
34
- AND p.category = 'response_quality' AND p.active = 1
35
- ORDER BY p.helpful_count DESC`,
36
- [agentRole]
37
- )
38
- .all() as EthicsRule[];
33
+ export class EthicsGuard {
34
+ /**
35
+ * Reglas activas de calidad de respuesta.
36
+ *
37
+ * Con `agentRole` filtra por las que lo declaran en `applicable_to`; si
38
+ * ninguna coincide devuelve todas, para no dejar al agente sin capa por un
39
+ * `applicable_to` mal cargado.
40
+ */
41
+ async getRules(agentRole?: string): Promise<EthicsRule[]> {
42
+ const playbookCol = await col<PlaybookDoc>("playbook");
43
+ const all = (await playbookCol.scan({}))
44
+ .map((e) => ({
45
+ id: e.id,
46
+ rule: e.doc.rule,
47
+ category: e.doc.category,
48
+ applicable_to: e.doc.applicable_to,
49
+ helpful_count: e.doc.helpful_count ?? 0,
50
+ active: e.doc.active,
51
+ }))
52
+ .filter((r) => r.active && r.category === RESPONSE_QUALITY)
53
+ .sort(byUsefulness);
39
54
 
40
- if (ftsRows.length > 0) return ftsRows;
41
- } catch {}
55
+ if (!agentRole) return all;
42
56
 
43
- return this.db
44
- .query(
45
- `SELECT * FROM playbook
46
- WHERE category = 'response_quality' AND active = 1
47
- ORDER BY helpful_count DESC`
48
- )
49
- .all() as EthicsRule[];
57
+ const matching = all.filter((r) => (r.applicable_to ?? "").includes(agentRole));
58
+ return matching.length > 0 ? matching : all;
50
59
  }
51
60
 
52
61
  injectIntoPrompt(systemPrompt: string, rules: EthicsRule[]): string {
53
62
  if (rules.length === 0) return systemPrompt;
54
- const ethicsSection = rules
55
- .map(r => `- ${r.rule}`)
56
- .join("\n");
63
+ const ethicsSection = rules.map((r) => `- ${r.rule}`).join("\n");
57
64
  return `${systemPrompt}\n\n## Reglas de Calidad de Respuesta\n${ethicsSection}`;
58
65
  }
59
66
 
60
- hasEthicsLayer(): boolean {
61
- const count = this.db
62
- .query(`SELECT COUNT(*) as c FROM playbook WHERE category = 'response_quality' AND active = 1`)
63
- .get() as any;
64
- return (count?.c ?? 0) > 0;
67
+ async hasEthicsLayer(): Promise<boolean> {
68
+ return (await this.getRules()).length > 0;
65
69
  }
66
70
  }
@@ -11,7 +11,8 @@
11
11
 
12
12
  import { EventEmitter } from "events";
13
13
  import { logger } from "../utils/logger";
14
- import { getDb } from "../storage/SQLiteStorage";
14
+ import { col, nextId, toIndexable, fromIndexable, BROADCAST } from "../storage/hive";
15
+ import type { AgentBusMessageDoc, TaskDoc } from "../storage/collections";
15
16
 
16
17
  const log = logger.child("agent-bus");
17
18
 
@@ -122,8 +123,6 @@ export interface AgentBusMessage {
122
123
  * Guarda un mensaje en la base de datos para persistencia
123
124
  */
124
125
  function persistMessage(event: AgentBusEventKey, data: any, metadata?: Record<string, unknown>): void {
125
- const db = getDb();
126
-
127
126
  // Extraer IDs de worker según el tipo de evento
128
127
  let fromWorkerId: string | null = null;
129
128
  let toWorkerId: string | null = null;
@@ -167,87 +166,64 @@ function persistMessage(event: AgentBusEventKey, data: any, metadata?: Record<st
167
166
  content = JSON.stringify(data);
168
167
  }
169
168
 
170
- try {
171
- db.query(`
172
- INSERT OR IGNORE INTO agent_bus_messages
173
- (event_type, from_worker_id, to_worker_id, topic, content, metadata, created_at, read)
174
- VALUES (?, ?, ?, ?, ?, ?, unixepoch(), 0)
175
- `).run(
176
- event,
177
- fromWorkerId,
178
- toWorkerId,
179
- topic,
180
- content,
181
- metadata ? JSON.stringify(metadata) : null
182
- );
183
- } catch (err) {
184
- log.warn(`Failed to persist message (non-critical): ${(err as Error).message}`);
185
- }
169
+ Promise.resolve().then(async () => {
170
+ try {
171
+ const messagesCol = await col<AgentBusMessageDoc>("agentBusMessages");
172
+ const id = await nextId("agentBusMessages");
173
+ await messagesCol.put(id, {
174
+ id,
175
+ event_type: event,
176
+ from_worker_id: toIndexable(fromWorkerId),
177
+ to_worker_id: toWorkerId ? toWorkerId : BROADCAST,
178
+ topic,
179
+ content,
180
+ metadata: metadata ? JSON.stringify(metadata) : null,
181
+ created_at: Date.now(),
182
+ read: false,
183
+ }, { expectedVersion: 0 });
184
+ } catch (err) {
185
+ log.warn(`Failed to persist message (non-critical): ${(err as Error).message}`);
186
+ }
187
+ });
188
+ }
189
+
190
+ function docToMessage(doc: AgentBusMessageDoc): AgentBusMessage {
191
+ return {
192
+ id: parseInt(doc.id, 10),
193
+ event_type: doc.event_type,
194
+ from_worker_id: fromIndexable(doc.from_worker_id),
195
+ to_worker_id: doc.to_worker_id === BROADCAST ? null : doc.to_worker_id,
196
+ topic: doc.topic,
197
+ content: doc.content,
198
+ metadata: doc.metadata,
199
+ created_at: doc.created_at,
200
+ read: doc.read ? 1 : 0,
201
+ };
186
202
  }
187
203
 
188
204
  /**
189
205
  * Obtiene mensajes no leídos para un worker específico
190
206
  */
191
- export function getUnreadMessagesForWorker(workerId: string, limit: number = 50): AgentBusMessage[] {
192
- const db = getDb();
193
-
207
+ export async function getUnreadMessagesForWorker(workerId: string, limit: number = 50): Promise<AgentBusMessage[]> {
194
208
  try {
195
- const messages = db.query<any, [string, number]>(`
196
- SELECT * FROM agent_bus_messages
197
- WHERE (to_worker_id = ? OR to_worker_id IS NULL) AND read = 0
198
- ORDER BY created_at ASC
199
- LIMIT ?
200
- `).all(workerId, limit);
209
+ const messagesCol = await col<AgentBusMessageDoc>("agentBusMessages");
210
+ const entries = (await messagesCol.scan({}))
211
+ .filter(e => !e.doc.read && (e.doc.to_worker_id === workerId || e.doc.to_worker_id === BROADCAST))
212
+ .sort((a, b) => a.doc.created_at - b.doc.created_at)
213
+ .slice(0, limit);
201
214
 
202
215
  // Marcar como leídos
203
- if (messages.length > 0) {
204
- const ids = messages.map((m: AgentBusMessage) => m.id).join(",");
205
- db.query(`UPDATE agent_bus_messages SET read = 1 WHERE id IN (${ids})`).run();
216
+ for (const entry of entries) {
217
+ await messagesCol.put(entry.id, { ...entry.doc, read: true }, { expectedVersion: entry.version });
206
218
  }
207
219
 
208
- return messages;
220
+ return entries.map(e => docToMessage(e.doc));
209
221
  } catch (err) {
210
222
  log.error(`Failed to get unread messages: ${(err as Error).message}`);
211
223
  return [];
212
224
  }
213
225
  }
214
226
 
215
- /**
216
- * Obtiene el historial de mensajes de un proyecto
217
- */
218
- export function getProjectMessageHistory(projectId: string, limit: number = 100): AgentBusMessage[] {
219
- const db = getDb();
220
-
221
- try {
222
- // Primero obtenemos los task_ids del proyecto
223
- const tasks = db.query<any, [string]>(
224
- "SELECT id FROM tasks WHERE project_id = ?"
225
- ).all(projectId);
226
-
227
- if (tasks.length === 0) return [];
228
-
229
- // Obtenemos los agent_ids de las tareas
230
- const agentIds = tasks
231
- .map((t: any) => t.agent_id)
232
- .filter((id: string | null) => id !== null);
233
-
234
- if (agentIds.length === 0) return [];
235
-
236
- // Obtenemos mensajes relacionados a estos agents
237
- const placeholders = agentIds.map(() => "?").join(",");
238
- const messages = db.query<any, any[]>(`
239
- SELECT * FROM agent_bus_messages
240
- WHERE from_worker_id IN (${placeholders})
241
- ORDER BY created_at DESC
242
- LIMIT ?
243
- `).all([...agentIds, limit]);
244
-
245
- return messages;
246
- } catch (err) {
247
- log.error(`Failed to get project message history: ${(err as Error).message}`);
248
- return [];
249
- }
250
- }
251
227
 
252
228
  // ─── Agent Bus Implementation ────────────────────────────────────────────────
253
229