@johpaz/hive-sdk 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/CHANGELOG.md +97 -0
  2. package/README.md +78 -23
  3. package/bunfig.toml +4 -2
  4. package/docs/API-AGENTS.md +78 -27
  5. package/docs/API-CONTEXT-COMPILER.md +31 -34
  6. package/docs/API-TOOLS-SKILLS-CHANNELS.md +58 -22
  7. package/docs/HIVE-HARNESS.md +1 -1
  8. package/docs/INDEX.md +4 -4
  9. package/docs/TEMPLATE-HIVE-APP.md +10 -10
  10. package/package.json +9 -4
  11. package/packages/cli/package.json +2 -2
  12. package/packages/cli/src/commands/create-app.test.ts +36 -7
  13. package/packages/cli/src/commands/init.ts +3 -3
  14. package/packages/cli/src/commands/run.ts +1 -1
  15. package/packages/cli/src/commands/test.ts +37 -25
  16. package/packages/cli/src/commands/trace.ts +30 -28
  17. package/packages/cli/templates/hive-app/.env.example +10 -2
  18. package/packages/cli/templates/hive-app/README.md +103 -0
  19. package/packages/cli/templates/hive-app/hive.config.ts +9 -3
  20. package/packages/cli/templates/hive-app/src/agents/coordinator.ts +8 -1
  21. package/packages/cli/templates/hive-app/src/main.ts +12 -19
  22. package/packages/core/package.json +5 -4
  23. package/packages/core/src/agent/acceptance-checks.ts +166 -0
  24. package/packages/core/src/agent/agent-catalog.ts +348 -0
  25. package/packages/core/src/agent/agent-loop.ts +1373 -0
  26. package/packages/core/src/agent/capability-search.ts +186 -0
  27. package/packages/core/src/agent/catalog-selector.ts +103 -0
  28. package/packages/core/src/agent/{Compaction.ts → compaction.ts} +86 -63
  29. package/packages/core/src/agent/context-compiler.ts +689 -0
  30. package/packages/core/src/agent/conversation-store.ts +381 -0
  31. package/packages/core/src/agent/curator.ts +276 -0
  32. package/packages/core/src/agent/delegation-runtime.ts +241 -0
  33. package/packages/core/src/agent/goal-runner.ts +323 -0
  34. package/packages/core/src/agent/index.ts +17 -12
  35. package/packages/core/src/agent/llm-client.ts +266 -0
  36. package/packages/core/src/agent/llm-providers/anthropic.ts +264 -0
  37. package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
  38. package/packages/core/src/agent/{providers → llm-providers}/gemini.ts +98 -60
  39. package/packages/core/src/agent/llm-providers/groq.ts +5 -0
  40. package/packages/core/src/agent/llm-providers/hiveagents.ts +253 -0
  41. package/packages/core/src/agent/{providers → llm-providers}/interface.ts +73 -13
  42. package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
  43. package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
  44. package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
  45. package/packages/core/src/agent/llm-providers/modelscope.ts +5 -0
  46. package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
  47. package/packages/core/src/agent/{providers → llm-providers}/ollama.ts +31 -5
  48. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +418 -0
  49. package/packages/core/src/agent/llm-providers/openai.ts +5 -0
  50. package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
  51. package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
  52. package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
  53. package/packages/core/src/agent/llm-providers/z-ai.ts +5 -0
  54. package/packages/core/src/agent/minimal-loadout.ts +47 -0
  55. package/packages/core/src/agent/playbook-selector.ts +119 -0
  56. package/packages/core/src/agent/{PromptBuilder.ts → prompt-builder.ts} +21 -22
  57. package/packages/core/src/{harness → agent}/proof-packet.ts +16 -21
  58. package/packages/core/src/agent/providers/index.ts +35 -16
  59. package/packages/core/src/agent/reflector.ts +320 -0
  60. package/packages/core/src/agent/routing-intent.ts +22 -0
  61. package/packages/core/src/{harness → agent}/run-epoch.ts +4 -3
  62. package/packages/core/src/{harness → agent}/run-store.ts +142 -81
  63. package/packages/core/src/agent/{Service.ts → service.ts} +37 -26
  64. package/packages/core/src/agent/skill-selector.ts +374 -0
  65. package/packages/core/src/agent/stuck-loop.ts +209 -0
  66. package/packages/core/src/agent/{selectors/ToolSelector.ts → tool-selector.ts} +188 -178
  67. package/packages/core/src/{ace/Tracer.ts → agent/tracer.ts} +37 -27
  68. package/packages/core/src/api/createAgent.test.ts +139 -27
  69. package/packages/core/src/api/createAgent.ts +232 -44
  70. package/packages/core/src/artifacts/store.ts +162 -0
  71. package/packages/core/src/canvas/canvas-manager.ts +161 -0
  72. package/packages/core/src/canvas/canvas.test.ts +8 -4
  73. package/packages/core/src/canvas/emitter.ts +131 -80
  74. package/packages/core/src/canvas/index.ts +1 -3
  75. package/packages/core/src/channels/base.ts +9 -1
  76. package/packages/core/src/channels/discord.ts +5 -4
  77. package/packages/core/src/channels/manager.ts +122 -30
  78. package/packages/core/src/channels/slack.ts +5 -4
  79. package/packages/core/src/channels/telegram.ts +36 -6
  80. package/packages/core/src/channels/webchat.ts +11 -10
  81. package/packages/core/src/channels/whatsapp.ts +23 -7
  82. package/packages/core/src/config/index.ts +13 -2
  83. package/packages/core/src/config/loader.ts +71 -29
  84. package/packages/core/src/ethics/EthicsGuard.test.ts +90 -36
  85. package/packages/core/src/ethics/EthicsGuard.ts +51 -47
  86. package/packages/core/src/events/agent-bus.ts +44 -68
  87. package/packages/core/src/events/channel-narration.ts +150 -0
  88. package/packages/core/src/events/narration.ts +82 -0
  89. package/packages/core/src/events/tool-narration.ts +62 -0
  90. package/packages/core/src/gateway/delegation-groups.ts +258 -0
  91. package/packages/core/src/{harness → gateway}/durable-queue.ts +102 -42
  92. package/packages/core/src/{harness → gateway}/job-store.ts +85 -48
  93. package/packages/core/src/gateway/lane-queue.ts +173 -0
  94. package/packages/core/src/gateway/notification-inbox.ts +57 -0
  95. package/packages/core/src/gateway/server.ts +1 -1
  96. package/packages/core/src/harness/index.ts +46 -27
  97. package/packages/core/src/index.ts +33 -20
  98. package/packages/core/src/mcp/hot-reload.ts +32 -23
  99. package/packages/core/src/mcp/index.ts +6 -3
  100. package/packages/core/src/mcp/singleton.ts +1 -4
  101. package/packages/core/src/mcp/tool-sync.ts +138 -0
  102. package/packages/core/src/memory/Scratchpad.test.ts +39 -20
  103. package/packages/core/src/memory/Scratchpad.ts +27 -34
  104. package/packages/core/src/multimodal/vision-service.ts +44 -38
  105. package/packages/core/src/resilience/retry.ts +95 -0
  106. package/packages/core/src/scheduler/CronScheduler.ts +334 -287
  107. package/packages/core/src/scheduler/index.ts +9 -7
  108. package/packages/core/src/scheduler/integration.ts +46 -26
  109. package/packages/core/src/scheduler/scheduler.test.ts +9 -13
  110. package/packages/core/src/scheduler/types.ts +7 -2
  111. package/packages/core/src/security/Pairing.ts +1 -1
  112. package/packages/core/src/skills/bundled/a2ui/a2ui_dashboard/SKILL.md +176 -0
  113. package/packages/core/src/skills/bundled/a2ui/a2ui_form/SKILL.md +202 -0
  114. package/packages/core/src/skills/bundled/a2ui/a2ui_interactive/SKILL.md +206 -0
  115. package/packages/core/src/skills/bundled/agents/agent_spawner/SKILL.md +173 -0
  116. package/packages/core/src/skills/bundled/agents/memory_manager/SKILL.md +143 -0
  117. package/packages/core/src/skills/bundled/agents/research_and_remember/SKILL.md +139 -0
  118. package/packages/core/src/skills/bundled/agents/task_orchestrator/SKILL.md +98 -0
  119. package/packages/core/src/skills/bundled/api/api_client/SKILL.md +132 -0
  120. package/packages/core/src/skills/bundled/cli/cli_pipeline/SKILL.md +135 -0
  121. package/packages/core/src/skills/bundled/cli/cli_safe_exec/SKILL.md +125 -0
  122. package/packages/core/src/skills/bundled/cli/software_engineering/SKILL.md +23 -0
  123. package/packages/core/src/skills/bundled/cron_manager/SKILL.md +188 -0
  124. package/packages/core/src/skills/bundled/cron_reminder/SKILL.md +112 -0
  125. package/packages/core/src/skills/bundled/filesystem/file_manager/SKILL.md +118 -0
  126. package/packages/core/src/skills/bundled/filesystem/file_read_and_summarize/SKILL.md +109 -0
  127. package/packages/core/src/skills/bundled/filesystem/file_writer/SKILL.md +129 -0
  128. package/packages/core/src/skills/bundled/filesystem/workspace_file_operator/SKILL.md +22 -0
  129. package/packages/core/src/skills/bundled/office/office_document_manager/SKILL.md +262 -0
  130. package/packages/core/src/skills/bundled/search_knowledge/capability_discovery/SKILL.md +75 -0
  131. package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +120 -0
  132. package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +109 -0
  133. package/packages/core/src/skills/bundled/web/web_monitor/SKILL.md +127 -0
  134. package/packages/core/src/skills/bundled/web/web_research/SKILL.md +119 -0
  135. package/packages/core/src/skills/bundled-data.generated.ts +731 -2678
  136. package/packages/core/src/skills/skills.test.ts +52 -11
  137. package/packages/core/src/{harness → storage}/boot-id.ts +5 -2
  138. package/packages/core/src/storage/bootstrap.ts +151 -0
  139. package/packages/core/src/storage/causal-events.ts +84 -0
  140. package/packages/core/src/storage/collections.ts +680 -0
  141. package/packages/core/src/storage/crypto.ts +205 -74
  142. package/packages/core/src/{harness/db-helpers.ts → storage/hive.ts} +63 -7
  143. package/packages/core/src/storage/hivedb.ts +61 -0
  144. package/packages/core/src/storage/index.ts +111 -17
  145. package/packages/core/src/storage/model-id.ts +53 -0
  146. package/packages/core/src/storage/onboarding.ts +540 -972
  147. package/packages/core/src/storage/reconcile.ts +238 -0
  148. package/packages/core/src/storage/seed.ts +572 -406
  149. package/packages/core/src/storage/usage.ts +285 -225
  150. package/packages/core/src/storage/user-email.ts +11 -0
  151. package/packages/core/src/swarm/AgentExecutor.ts +1 -1
  152. package/packages/core/src/swarm/EventBridge.ts +1 -1
  153. package/packages/core/src/swarm/index.ts +12 -9
  154. package/packages/core/src/tool-runtime/index.ts +146 -23
  155. package/packages/core/src/tool-runtime/tool-worker.ts +2 -2
  156. package/packages/core/src/tool-runtime/worker-tools.ts +27 -0
  157. package/packages/core/src/{canvas/a2ui-tools.ts → tools/a2ui/index.ts} +17 -8
  158. package/packages/core/src/tools/agents/get-available-models.ts +36 -54
  159. package/packages/core/src/tools/agents/index.ts +784 -292
  160. package/packages/core/src/tools/api/api-request.test.ts +164 -0
  161. package/packages/core/src/tools/api/api-request.ts +174 -0
  162. package/packages/core/src/tools/api/index.ts +16 -0
  163. package/packages/core/src/tools/cli/index.ts +4 -0
  164. package/packages/core/src/tools/core/index.ts +281 -112
  165. package/packages/core/src/tools/cron/index.ts +121 -124
  166. package/packages/core/src/tools/index.ts +63 -78
  167. package/packages/core/src/tools/office/office-escribir-xlsx.ts +3 -1
  168. package/packages/core/src/tools/types.ts +3 -1
  169. package/packages/core/src/tools/web/artifact-inspect.ts +23 -0
  170. package/packages/core/src/tools/web/browser-screenshot.ts +26 -5
  171. package/packages/core/src/tools/web/browser-service.ts +5 -0
  172. package/packages/core/src/tools/web/browser-type.ts +3 -8
  173. package/packages/core/src/tools/web/index.ts +4 -4
  174. package/packages/core/src/voice/index.ts +89 -63
  175. package/packages/core/src/workers/agent.worker.ts +2 -2
  176. package/packages/core/src/workers/workers.test.ts +3 -10
  177. package/scripts/bump-version.ts +248 -0
  178. package/scripts/generate-skill-bundle.ts +108 -0
  179. package/test/agent-loop-terminal-synthesis.test.ts +32 -0
  180. package/test/catalog-agents-stay-enabled.test.ts +117 -0
  181. package/test/causal-events.test.ts +117 -0
  182. package/test/compaction.test.ts +105 -0
  183. package/test/context-compiler.test.ts +269 -0
  184. package/test/curator.test.ts +130 -0
  185. package/test/durable-queue.test.ts +114 -0
  186. package/test/harness-barrel.test.ts +64 -0
  187. package/test/hive-helpers.test.ts +130 -0
  188. package/test/hivedb-search.test.ts +189 -0
  189. package/test/internal-turns.test.ts +166 -0
  190. package/test/job-idempotency.test.ts +68 -0
  191. package/test/job-retry-backoff.test.ts +184 -0
  192. package/test/job-store.test.ts +381 -0
  193. package/test/llm-retry.test.ts +97 -0
  194. package/test/memory-perf.test.ts +774 -0
  195. package/test/minimal-loadout.test.ts +78 -0
  196. package/test/model-catalog.test.ts +105 -0
  197. package/test/preload.ts +12 -0
  198. package/test/reflector.test.ts +320 -0
  199. package/test/retention-cap.test.ts +91 -0
  200. package/test/retired-capabilities-pruned.test.ts +192 -0
  201. package/test/run-store.test.ts +355 -0
  202. package/test/scratchpad.test.ts +74 -0
  203. package/test/secrets-durability.test.ts +119 -0
  204. package/test/seed-model-reseed.test.ts +155 -0
  205. package/test/setup-agent-seed.test.ts +264 -0
  206. package/test/tool-inventory.test.ts +65 -0
  207. package/test/tool-runtime.test.ts +258 -0
  208. package/test/toon.test.ts +429 -0
  209. package/tsconfig.json +2 -0
  210. package/packages/core/src/ace/Curator.ts +0 -158
  211. package/packages/core/src/ace/Reflector.ts +0 -200
  212. package/packages/core/src/ace/index.ts +0 -4
  213. package/packages/core/src/agent/AgentRunner.ts +0 -711
  214. package/packages/core/src/agent/ContextCompiler.ts +0 -567
  215. package/packages/core/src/agent/ContextGuard.ts +0 -91
  216. package/packages/core/src/agent/ConversationStore.ts +0 -254
  217. package/packages/core/src/agent/Hooks.ts +0 -166
  218. package/packages/core/src/agent/StuckLoop.ts +0 -133
  219. package/packages/core/src/agent/providers/LLMClient.ts +0 -149
  220. package/packages/core/src/agent/providers/anthropic.ts +0 -212
  221. package/packages/core/src/agent/providers/openai-compat.ts +0 -231
  222. package/packages/core/src/agent/selectors/PlaybookSelector.ts +0 -121
  223. package/packages/core/src/agent/selectors/SkillSelector.ts +0 -322
  224. package/packages/core/src/agent/selectors/index.ts +0 -6
  225. package/packages/core/src/auth/auth.ts +0 -121
  226. package/packages/core/src/auth/index.ts +0 -1
  227. package/packages/core/src/canvas/CanvasManager.ts +0 -390
  228. package/packages/core/src/canvas/canvas-tools.ts +0 -448
  229. package/packages/core/src/harness/collections.ts +0 -98
  230. package/packages/core/src/harness/goal-verifier.ts +0 -141
  231. package/packages/core/src/harness/harness.test.ts +0 -236
  232. package/packages/core/src/harness/reconcile.ts +0 -149
  233. package/packages/core/src/mcp/MCPToolAdapter.ts +0 -176
  234. package/packages/core/src/multimodal/VisionService.ts +0 -293
  235. package/packages/core/src/scheduler/dag/AgentExecutor.ts +0 -53
  236. package/packages/core/src/scheduler/dag/DAGScheduler.ts +0 -250
  237. package/packages/core/src/scheduler/dag/EventBridge.ts +0 -122
  238. package/packages/core/src/scheduler/dag/TaskGraph.ts +0 -192
  239. package/packages/core/src/scheduler/dag/TaskNode.ts +0 -97
  240. package/packages/core/src/scheduler/dag/TaskResult.ts +0 -22
  241. package/packages/core/src/scheduler/dag/errors.ts +0 -37
  242. package/packages/core/src/scheduler/dag/index.ts +0 -26
  243. package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +0 -97
  244. package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +0 -21
  245. package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +0 -46
  246. package/packages/core/src/storage/HiveDBStorage.ts +0 -64
  247. package/packages/core/src/storage/SQLiteStorage.ts +0 -414
  248. package/packages/core/src/storage/hiveSeed.ts +0 -308
  249. package/packages/core/src/storage/hiveStorage.test.ts +0 -38
  250. package/packages/core/src/storage/schema.ts +0 -689
  251. package/packages/core/src/storage/storage.test.ts +0 -37
  252. package/packages/core/src/swarm/AgentBus.ts +0 -460
  253. package/packages/core/src/swarm/EventBus.ts +0 -169
  254. package/packages/core/src/swarm/WorkerPool.ts +0 -236
  255. package/packages/core/src/tools/bridge-events.ts +0 -26
  256. package/packages/core/src/tools/canvas/index.ts +0 -375
  257. package/packages/core/src/tools/codebridge/index.ts +0 -342
  258. package/packages/core/src/tools/meeting/index.ts +0 -353
  259. package/packages/core/src/tools/projects/index.ts +0 -37
  260. package/packages/core/src/tools/projects/project-create.ts +0 -94
  261. package/packages/core/src/tools/projects/project-done.ts +0 -66
  262. package/packages/core/src/tools/projects/project-fail.ts +0 -66
  263. package/packages/core/src/tools/projects/project-list.ts +0 -96
  264. package/packages/core/src/tools/projects/project-update.ts +0 -72
  265. package/packages/core/src/tools/projects/task-create.ts +0 -68
  266. package/packages/core/src/tools/projects/task-evaluate.ts +0 -93
  267. package/packages/core/src/tools/projects/task-update.ts +0 -93
  268. package/packages/core/src/tools/voice/index.ts +0 -104
  269. package/packages/core/src/tools/web/api-request.test.ts +0 -170
  270. package/packages/core/src/tools/web/api-request.ts +0 -239
  271. package/test/setup-db.ts +0 -216
  272. /package/packages/core/src/agent/{NativeTools.ts → native-tools.ts} +0 -0
@@ -1,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,9 @@ 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(),
122
123
  });
123
124
 
124
125
  const CanvasConfigSchema = z.object({
@@ -126,6 +127,13 @@ const CanvasConfigSchema = z.object({
126
127
  port: z.number().optional(),
127
128
  });
128
129
 
130
+ const WorkerPoolConfigSchema = z.object({
131
+ enabled: z.boolean().optional(),
132
+ maxWorkers: z.number().optional(),
133
+ toolTimeoutMs: z.number().optional(),
134
+ parallelToolCalls: z.boolean().optional(),
135
+ });
136
+
129
137
  const SandboxConfigSchema = z.object({
130
138
  dm: ToolRestrictionsSchema.optional(),
131
139
  group: ToolRestrictionsSchema.optional(),
@@ -138,7 +146,12 @@ const ToolsConfigSchema = z.object({
138
146
  web: WebConfigSchema.optional(),
139
147
  browser: BrowserConfigSchema.optional(),
140
148
  canvas: CanvasConfigSchema.optional(),
149
+ workerPool: WorkerPoolConfigSchema.optional(),
141
150
  sandbox: SandboxConfigSchema.optional(),
151
+ // Per-tool timeout overrides (ms) keyed by tool name. Falls back to
152
+ // workerPool.toolTimeoutMs when absent. Long-running tools like cli_exec
153
+ // should set a higher value (e.g. 600000 = 10min).
154
+ timeouts: z.record(z.string(), z.number()).optional(),
142
155
  });
143
156
 
144
157
  const ContextConfigSchema = z.object({
@@ -237,6 +250,13 @@ const CronConfigSchema = z.object({
237
250
  timezone: z.string().optional(),
238
251
  });
239
252
 
253
+ // G9 causal event log (HiveDB): IntentLogged/StateTransition/ToolCall emission
254
+ // from agent-loop.ts, consumed by reflector/curator/context-compiler. Off by
255
+ // default — each turn adds N+M+1 awaited db.append() calls to the critical path.
256
+ const CausalLogConfigSchema = z.object({
257
+ enabled: z.boolean().optional(),
258
+ });
259
+
240
260
  const RetryConfigSchema = z.object({
241
261
  maxAttempts: z.number().optional(),
242
262
  initialDelayMs: z.number().optional(),
@@ -244,6 +264,25 @@ const RetryConfigSchema = z.object({
244
264
  maxDelayMs: z.number().optional(),
245
265
  });
246
266
 
267
+ const JobRetryConfigSchema = z.object({
268
+ // Logical-failure retries (executor returned {ok:false}). Separate from
269
+ // JobDoc.attempts, which only counts crash/lease-expiry reclaims.
270
+ maxRetries: z.number().optional(),
271
+ initialDelayMs: z.number().optional(),
272
+ backoffMultiplier: z.number().optional(),
273
+ maxDelayMs: z.number().optional(),
274
+ jitter: z.number().optional(),
275
+ });
276
+
277
+ const HarnessConfigSchema = z.object({
278
+ maxGlobalConcurrency: z.number().optional(),
279
+ taskTimeoutMs: z.number().optional(),
280
+ jobLeaseMs: z.number().optional(),
281
+ runLeaseMs: z.number().optional(),
282
+ leaseRenewMs: z.number().optional(),
283
+ jobRetry: JobRetryConfigSchema.optional(),
284
+ });
285
+
247
286
  const HooksConfigSchema = z.object({
248
287
  scripts: z.object({
249
288
  before_model_resolve: z.string().optional(),
@@ -281,7 +320,7 @@ const GatewayConfigSchema = z.object({
281
320
  });
282
321
 
283
322
  const ModelsConfigSchema = z.object({
284
- defaultProvider: z.enum(["openai", "anthropic", "gemini", "mistral", "kimi", "ollama", "openrouter", "deepseek"]).optional(),
323
+ defaultProvider: z.enum(["openai", "anthropic", "gemini", "mistral", "kimi", "ollama", "openrouter", "deepseek", "hiveagents"]).optional(),
285
324
  defaults: z.record(z.string(), z.string()).optional(),
286
325
  providers: z.record(z.string(), ProviderConfigSchema).optional(),
287
326
  });
@@ -307,17 +346,6 @@ const SecurityConfigSchema = z.object({
307
346
  allowedUsers: z.array(z.string()).optional(),
308
347
  });
309
348
 
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
349
  const UserConfigSchema = z.object({
322
350
  id: z.string(),
323
351
  name: z.string(),
@@ -345,10 +373,11 @@ const ConfigSchema = z.object({
345
373
  mcp: MCPConfigSchema.optional(),
346
374
  memory: MemoryConfigSchema.optional(),
347
375
  cron: CronConfigSchema.optional(),
376
+ causalLog: CausalLogConfigSchema.optional(),
348
377
  retry: RetryConfigSchema.optional(),
378
+ harness: HarnessConfigSchema.optional(),
349
379
  security: SecurityConfigSchema.optional(),
350
380
  hooks: HooksConfigSchema.optional(),
351
- captcha: CaptchaConfigSchema.optional(),
352
381
  });
353
382
 
354
383
  export type Config = z.infer<typeof ConfigSchema>;
@@ -358,7 +387,6 @@ export type MCPServerConfig = z.infer<typeof MCPServerConfigSchema>;
358
387
  export type AgentEntry = z.infer<typeof AgentEntrySchema>;
359
388
  export type Binding = z.infer<typeof BindingSchema>;
360
389
  export type UserConfig = z.infer<typeof UserConfigSchema>;
361
- export type CaptchaConfig = z.infer<typeof CaptchaConfigSchema>;
362
390
 
363
391
  function buildDefaultConfig(): Config {
364
392
  const hiveDir = getHiveDir();
@@ -428,7 +456,7 @@ function buildDefaultConfig(): Config {
428
456
  allowlist: [],
429
457
  denylist: ["rm -rf /", "sudo", "chmod 777", "> /dev/", "mkfs"],
430
458
  timeoutSeconds: 30,
431
- workDir: path.join(process.env.HOME || "", "exec"), // Points to home for exec by default
459
+ workDir: path.join(homedir(), "exec"), // Points to home for exec by default
432
460
  },
433
461
  web: {
434
462
  allowlist: [],
@@ -437,14 +465,20 @@ function buildDefaultConfig(): Config {
437
465
  },
438
466
  browser: {
439
467
  enabled: true,
440
- sessionName: "hive",
441
468
  headless: true,
442
469
  timeoutMs: 30000,
470
+ sessionName: "hive",
443
471
  },
444
472
  canvas: {
445
473
  enabled: true,
446
474
  port: 18793,
447
475
  },
476
+ workerPool: {
477
+ enabled: true,
478
+ maxWorkers: Math.min(4, availableParallelism()),
479
+ toolTimeoutMs: 300000,
480
+ parallelToolCalls: true,
481
+ },
448
482
  sandbox: {
449
483
  dm: { allow: ["*"], deny: [] },
450
484
  group: { allow: ["*"], deny: [] },
@@ -480,12 +514,29 @@ function buildDefaultConfig(): Config {
480
514
  maxConcurrentJobs: 5,
481
515
  timezone: "UTC",
482
516
  },
517
+ causalLog: {
518
+ enabled: process.env.HIVE_CAUSAL_LOG === "true",
519
+ },
483
520
  retry: {
484
521
  maxAttempts: 3,
485
522
  initialDelayMs: 1000,
486
523
  backoffMultiplier: 2,
487
524
  maxDelayMs: 30000,
488
525
  },
526
+ harness: {
527
+ maxGlobalConcurrency: parseInt(process.env.HIVE_HARNESS_MAX_CONCURRENCY || "4", 10),
528
+ taskTimeoutMs: parseInt(process.env.HIVE_HARNESS_TASK_TIMEOUT_MS || String(30 * 60 * 1000), 10),
529
+ jobLeaseMs: parseInt(process.env.HIVE_HARNESS_JOB_LEASE_MS || String(30 * 60 * 1000), 10),
530
+ runLeaseMs: parseInt(process.env.HIVE_HARNESS_RUN_LEASE_MS || String(2 * 60 * 1000), 10),
531
+ leaseRenewMs: parseInt(process.env.HIVE_HARNESS_LEASE_RENEW_MS || "30000", 10),
532
+ jobRetry: {
533
+ maxRetries: parseInt(process.env.HIVE_HARNESS_JOB_MAX_RETRIES || "3", 10),
534
+ initialDelayMs: parseInt(process.env.HIVE_HARNESS_JOB_RETRY_INITIAL_MS || "1000", 10),
535
+ backoffMultiplier: parseFloat(process.env.HIVE_HARNESS_JOB_RETRY_MULTIPLIER || "2"),
536
+ maxDelayMs: parseInt(process.env.HIVE_HARNESS_JOB_RETRY_MAX_MS || String(5 * 60 * 1000), 10),
537
+ jitter: parseFloat(process.env.HIVE_HARNESS_JOB_RETRY_JITTER || "0.2"),
538
+ },
539
+ },
489
540
  security: {
490
541
  maxMessageLength: {
491
542
  telegram: 4096,
@@ -500,15 +551,6 @@ function buildDefaultConfig(): Config {
500
551
  hooks: {
501
552
  scripts: {},
502
553
  },
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
554
  };
513
555
  }
514
556
 
@@ -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