@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
@@ -0,0 +1,269 @@
1
+ /**
2
+ * context-compiler tests: G9 causal context window (buildAgentContext).
3
+ *
4
+ * Exercises the real compileContext() path with a real (in-memory) HiveDB —
5
+ * only the pieces that would otherwise need network/process I/O
6
+ * (MCP manager, native tool executors) are left at their defaults since
7
+ * createAllTools() works standalone in-process.
8
+ */
9
+
10
+ process.env.HIVE_DB_PATH = ":memory:";
11
+
12
+ import { describe, test, expect, beforeEach, afterEach } from "bun:test";
13
+ import { closeHiveDb, getHiveDb } from "../packages/core/src/storage/hivedb";
14
+ import { ensureHiveDb } from "../packages/core/src/storage/bootstrap";
15
+ import { resetBootId } from "../packages/core/src/storage/boot-id";
16
+ import { col, toIndexable } from "../packages/core/src/storage/hive";
17
+ import { addMessage, saveSummary } from "../packages/core/src/agent/conversation-store";
18
+ import { compileContext } from "../packages/core/src/agent/context-compiler";
19
+ import type { AgentDoc, ModelDoc, ProviderDoc, UserDoc } from "../packages/core/src/storage/collections";
20
+
21
+ async function seedAgentWithSmallContextWindow() {
22
+ const usersCol = await col<UserDoc>("users");
23
+ await usersCol.put("test-user", {
24
+ id: "test-user",
25
+ name: "Test User",
26
+ language: "es",
27
+ timezone: null,
28
+ occupation: null,
29
+ notes: null,
30
+ master_key_hash: null,
31
+ email: null,
32
+ password_hash: null,
33
+ preferred_cron_channel: "webchat",
34
+ created_at: Date.now(),
35
+ });
36
+
37
+ const agentsCol = await col<AgentDoc>("agents");
38
+ await agentsCol.put("test-agent", {
39
+ id: "test-agent",
40
+ user_id: "test-user",
41
+ name: "Test Agent",
42
+ description: null,
43
+ system_prompt: "Eres un agente de prueba.",
44
+ tone: null,
45
+ role: "coordinator",
46
+ status: "idle",
47
+ enabled: true,
48
+ provider_id: toIndexable("hiveagents"),
49
+ model_id: toIndexable("test-model"),
50
+ tools_json: null,
51
+ skills_json: null,
52
+ parent_id: toIndexable(null),
53
+ max_iterations: 10,
54
+ workspace: null,
55
+ lastTraceAt: null,
56
+ created_at: Date.now(),
57
+ updated_at: Date.now(),
58
+ });
59
+
60
+ const providersCol = await col<ProviderDoc>("providers");
61
+ await providersCol.put("hiveagents", {
62
+ id: "hiveagents",
63
+ name: "HiveAgents",
64
+ enabled: true,
65
+ active: true,
66
+ base_url: "https://fake.api.com/v1",
67
+ category: "llm",
68
+ num_ctx: null,
69
+ num_gpu: 0,
70
+ created_at: Date.now(),
71
+ });
72
+
73
+ // Small context window so a handful of messages is enough to cross
74
+ // compactThreshold (window * 0.8) and force the summary/compaction path.
75
+ const modelsCol = await col<ModelDoc>("models");
76
+ await modelsCol.put("test-model", {
77
+ id: "test-model",
78
+ provider_id: "hiveagents",
79
+ name: "Test Model",
80
+ model_type: "llm",
81
+ active: true,
82
+ enabled: true,
83
+ context_window: 1000,
84
+ capabilities: null,
85
+ });
86
+ }
87
+
88
+ // KEEP_LAST_N_MESSAGES in context-compiler.ts is 30 — insert enough messages
89
+ // that the recent-messages window is a strict suffix starting AFTER the
90
+ // summary's last_message_id. That's what makes a summary "apply": the window
91
+ // no longer reaches back far enough to cover what the summary already does.
92
+ async function forceCompaction(threadId: string) {
93
+ for (let i = 0; i < 35; i++) {
94
+ await addMessage(threadId, i % 2 === 0 ? "user" : "assistant", `msg-${i}`);
95
+ }
96
+ await saveSummary(threadId, "Resumen de la conversación previa.", 5, 5);
97
+ }
98
+
99
+ beforeEach(async () => {
100
+ closeHiveDb();
101
+ resetBootId();
102
+ await ensureHiveDb();
103
+ await seedAgentWithSmallContextWindow();
104
+ });
105
+
106
+ afterEach(() => {
107
+ closeHiveDb();
108
+ delete process.env.HIVE_CAUSAL_LOG;
109
+ });
110
+
111
+ describe("context-compiler: G9 causal context window", () => {
112
+ test("injects a # CAUSAL CONTEXT section once compaction fires and a causal stream has decisions", async () => {
113
+ process.env.HIVE_CAUSAL_LOG = "true";
114
+ const db = await getHiveDb();
115
+
116
+ const streamId = "ctx-stream-1";
117
+ const intentSeq = await db.append({
118
+ agentId: "test-agent",
119
+ streamId,
120
+ kind: "IntentLogged",
121
+ payload: JSON.stringify({ actor: "test-agent", intent: "deploy the checkout service" }),
122
+ });
123
+ await db.append({
124
+ agentId: "test-agent",
125
+ streamId,
126
+ kind: "StateTransition",
127
+ payload: JSON.stringify({ description: "Calling deploy_service on checkout" }),
128
+ causation: intentSeq,
129
+ });
130
+
131
+ await forceCompaction("thread-ctx-1");
132
+
133
+ const ctx = await compileContext({
134
+ agentId: "test-agent",
135
+ threadId: "thread-ctx-1",
136
+ userMessage: "Seguí con el deploy",
137
+ causalStreamId: streamId,
138
+ });
139
+
140
+ expect(ctx.systemPrompt).toContain("# CAUSAL CONTEXT");
141
+ expect(ctx.systemPrompt).toContain("Calling deploy_service on checkout");
142
+ });
143
+
144
+ test("does not inject a causal context section when causalLog is disabled", async () => {
145
+ process.env.HIVE_CAUSAL_LOG = "false";
146
+ const db = await getHiveDb();
147
+
148
+ const streamId = "ctx-stream-2";
149
+ const intentSeq = await db.append({
150
+ agentId: "test-agent",
151
+ streamId,
152
+ kind: "IntentLogged",
153
+ payload: JSON.stringify({ actor: "test-agent", intent: "deploy the checkout service" }),
154
+ });
155
+ await db.append({
156
+ agentId: "test-agent",
157
+ streamId,
158
+ kind: "StateTransition",
159
+ payload: JSON.stringify({ description: "Calling deploy_service on checkout" }),
160
+ causation: intentSeq,
161
+ });
162
+
163
+ await forceCompaction("thread-ctx-2");
164
+
165
+ const ctx = await compileContext({
166
+ agentId: "test-agent",
167
+ threadId: "thread-ctx-2",
168
+ userMessage: "Seguí con el deploy",
169
+ causalStreamId: streamId,
170
+ });
171
+
172
+ expect(ctx.systemPrompt).not.toContain("# CAUSAL CONTEXT");
173
+ });
174
+
175
+ test("does not inject a causal context section when compaction hasn't fired", async () => {
176
+ process.env.HIVE_CAUSAL_LOG = "true";
177
+ const db = await getHiveDb();
178
+
179
+ const streamId = "ctx-stream-3";
180
+ const intentSeq = await db.append({
181
+ agentId: "test-agent",
182
+ streamId,
183
+ kind: "IntentLogged",
184
+ payload: JSON.stringify({ actor: "test-agent", intent: "deploy the checkout service" }),
185
+ });
186
+ await db.append({
187
+ agentId: "test-agent",
188
+ streamId,
189
+ kind: "StateTransition",
190
+ payload: JSON.stringify({ description: "Calling deploy_service on checkout" }),
191
+ causation: intentSeq,
192
+ });
193
+
194
+ // No forceCompaction() call — conversation is short, no summary exists.
195
+
196
+ const ctx = await compileContext({
197
+ agentId: "test-agent",
198
+ threadId: "thread-ctx-3",
199
+ userMessage: "Hola",
200
+ causalStreamId: streamId,
201
+ });
202
+
203
+ expect(ctx.systemPrompt).not.toContain("# CAUSAL CONTEXT");
204
+ });
205
+ });
206
+
207
+ describe("context-compiler: conversation summary + internal events", () => {
208
+ test("folds the summary into systemPrompt as # RESUMEN DE LA CONVERSACIÓN when it applies", async () => {
209
+ await forceCompaction("thread-summary-1");
210
+
211
+ const ctx = await compileContext({
212
+ agentId: "test-agent",
213
+ threadId: "thread-summary-1",
214
+ userMessage: "Continuemos",
215
+ });
216
+
217
+ expect(ctx.systemPrompt).toContain("# RESUMEN DE LA CONVERSACIÓN");
218
+ expect(ctx.systemPrompt).toContain("Resumen de la conversación previa.");
219
+ expect(ctx.conversationSummarySection).not.toBe("");
220
+ });
221
+
222
+ test("does not include a summary section when none applies", async () => {
223
+ await addMessage("thread-summary-2", "user", "Hola");
224
+
225
+ const ctx = await compileContext({
226
+ agentId: "test-agent",
227
+ threadId: "thread-summary-2",
228
+ userMessage: "Hola de nuevo",
229
+ });
230
+
231
+ expect(ctx.systemPrompt).not.toContain("# RESUMEN DE LA CONVERSACIÓN");
232
+ expect(ctx.conversationSummarySection).toBe("");
233
+ });
234
+
235
+ test("never emits a second role:system message in ctx.messages, even when a summary applies", async () => {
236
+ await forceCompaction("thread-summary-3");
237
+
238
+ const ctx = await compileContext({
239
+ agentId: "test-agent",
240
+ threadId: "thread-summary-3",
241
+ userMessage: "Continuemos",
242
+ });
243
+
244
+ expect(ctx.messages.every((m) => m.role !== "system")).toBe(true);
245
+ });
246
+
247
+ test("an internal event keeps its chronological position and role:user in ctx.messages", async () => {
248
+ const threadId = "thread-internal-1";
249
+ await addMessage(threadId, "user", "Delegá esto a un worker");
250
+ await addMessage(threadId, "assistant", "Listo, delegado.");
251
+ await addMessage(threadId, "user", "El agente completó la tarea X.", { source: "task_complete" });
252
+ await addMessage(threadId, "assistant", "El worker terminó la tarea X exitosamente.");
253
+
254
+ const ctx = await compileContext({
255
+ agentId: "test-agent",
256
+ threadId,
257
+ userMessage: "¿Cómo va todo?",
258
+ });
259
+
260
+ expect(ctx.messages.every((m) => m.role !== "system")).toBe(true);
261
+
262
+ const idx = ctx.messages.findIndex(
263
+ (m) => typeof m.content === "string" && m.content.includes("hive:internal_event")
264
+ );
265
+ expect(idx).toBeGreaterThan(0);
266
+ expect(idx).toBeLessThan(ctx.messages.length - 1);
267
+ expect(ctx.messages[idx].role).toBe("user");
268
+ });
269
+ });
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Curator tests: baseline regression coverage for the existing
3
+ * reflection → playbook pipeline (none existed before), plus the new
4
+ * category mapping for G9 evaluateHarness() insight types
5
+ * (root_cause/learning_proposal — see reflector.ts's analyzeCausalThreads).
6
+ */
7
+
8
+ process.env.HIVE_DB_PATH = ":memory:";
9
+
10
+ import { describe, test, expect, beforeEach, afterEach } from "bun:test";
11
+ import { closeHiveDb } from "../packages/core/src/storage/hivedb";
12
+ import { ensureHiveDb } from "../packages/core/src/storage/bootstrap";
13
+ import { resetBootId } from "../packages/core/src/storage/boot-id";
14
+ import { col, nextId } from "../packages/core/src/storage/hive";
15
+ import type { ReflectionDoc, PlaybookDoc } from "../packages/core/src/storage/collections";
16
+ import { runCurator } from "../packages/core/src/agent/curator";
17
+
18
+ async function seedReflection(overrides: Partial<ReflectionDoc>) {
19
+ const reflectionsCol = await col<ReflectionDoc>("reflections");
20
+ const id = await nextId("reflections");
21
+ await reflectionsCol.put(id, {
22
+ id,
23
+ trace_ids: "[]",
24
+ insight_type: "failure_pattern",
25
+ description: "Tool 'flaky_tool' failed 5 times recently.",
26
+ affected_tools: null,
27
+ affected_agents: null,
28
+ confidence: 0.5,
29
+ created_at: Date.now(),
30
+ ...overrides,
31
+ });
32
+ return id;
33
+ }
34
+
35
+ beforeEach(async () => {
36
+ closeHiveDb();
37
+ resetBootId();
38
+ await ensureHiveDb();
39
+ });
40
+
41
+ afterEach(() => {
42
+ closeHiveDb();
43
+ });
44
+
45
+ describe("curator: baseline reflection → playbook pipeline", () => {
46
+ test("a new reflection creates a new active playbook rule", async () => {
47
+ await seedReflection({ description: "Tool 'flaky_tool' failed 5 times recently." });
48
+
49
+ await runCurator();
50
+
51
+ const playbookCol = await col<PlaybookDoc>("playbook");
52
+ const all = await playbookCol.scan({});
53
+ const rule = all.find((e) => e.doc.rule === "Tool 'flaky_tool' failed 5 times recently.");
54
+ expect(rule).toBeDefined();
55
+ expect(rule!.doc.category).toBe("error_avoidance");
56
+ expect(rule!.doc.helpful_count).toBe(1);
57
+ expect(rule!.doc.harmful_count).toBe(0);
58
+ expect(rule!.doc.active).toBe(true);
59
+ });
60
+
61
+ test("a reflection matching an existing rule's prefix reinforces it instead of duplicating", async () => {
62
+ const description = "Tool 'flaky_tool' failed 5 times recently. Consider verifying its configuration.";
63
+ await seedReflection({ description });
64
+ await runCurator();
65
+
66
+ // Second, near-identical reflection (same first-60-chars prefix)
67
+ await seedReflection({ description: description + " Extra detail." });
68
+ await runCurator();
69
+
70
+ const playbookCol = await col<PlaybookDoc>("playbook");
71
+ const all = await playbookCol.scan({});
72
+ const matching = all.filter((e) => e.doc.rule.startsWith(description.substring(0, 60)));
73
+ expect(matching.length).toBe(1);
74
+ expect(matching[0].doc.helpful_count).toBe(2);
75
+ });
76
+
77
+ test("a rule with harmful_count >= 3 and > helpful_count is deactivated on the next curator run", async () => {
78
+ const playbookCol = await col<PlaybookDoc>("playbook");
79
+ const now = Date.now();
80
+ await playbookCol.put("bad-rule", {
81
+ id: "bad-rule",
82
+ rule: "This rule turned out to be harmful.",
83
+ category: "optimization",
84
+ applicable_to: null,
85
+ helpful_count: 1,
86
+ harmful_count: 3,
87
+ active: true,
88
+ source_reflection_id: "NO_PARENT",
89
+ created_at: now,
90
+ updated_at: now,
91
+ }, { expectedVersion: 0 });
92
+
93
+ await runCurator();
94
+
95
+ const entry = await playbookCol.get("bad-rule");
96
+ expect(entry!.doc.active).toBe(false);
97
+ });
98
+ });
99
+
100
+ describe("curator: G9 evaluateHarness() insight category mapping", () => {
101
+ test("root_cause insights map to the error_avoidance category", async () => {
102
+ await seedReflection({
103
+ insight_type: "root_cause",
104
+ description: "Root cause in stream s1: called deploy_prod without running tests first.",
105
+ });
106
+
107
+ await runCurator();
108
+
109
+ const playbookCol = await col<PlaybookDoc>("playbook");
110
+ const all = await playbookCol.scan({});
111
+ const rule = all.find((e) => e.doc.rule.startsWith("Root cause in stream s1"));
112
+ expect(rule).toBeDefined();
113
+ expect(rule!.doc.category).toBe("error_avoidance");
114
+ });
115
+
116
+ test("learning_proposal insights map to the response_quality category", async () => {
117
+ await seedReflection({
118
+ insight_type: "learning_proposal",
119
+ description: "Add pre-checks before calling tool 'flaky_tool' to avoid repeated errors",
120
+ });
121
+
122
+ await runCurator();
123
+
124
+ const playbookCol = await col<PlaybookDoc>("playbook");
125
+ const all = await playbookCol.scan({});
126
+ const rule = all.find((e) => e.doc.rule.startsWith("Add pre-checks before calling tool"));
127
+ expect(rule).toBeDefined();
128
+ expect(rule!.doc.category).toBe("response_quality");
129
+ });
130
+ });
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Tests: DurableLaneQueue — dispatch, boot re-dispatch, forced reclaim.
3
+ *
4
+ * 1. enqueue → dispatch → executor runs → job completed
5
+ * 2. start() dispatches jobs left pending by a previous boot
6
+ * 3. reconcileOnBoot reclaims "running" jobs immediately (single-process:
7
+ * any running row at boot belongs to a dead process, lease ignored)
8
+ * 4. reclaimOrInterrupt force interrupts when attempts are exhausted
9
+ */
10
+
11
+ process.env.HIVE_DB_PATH = ":memory:";
12
+
13
+ import { describe, test, expect, beforeEach, afterEach } from "bun:test";
14
+ import { closeHiveDb } from "../packages/core/src/storage/hivedb";
15
+ import { ensureHiveDb } from "../packages/core/src/storage/bootstrap";
16
+ import { resetBootId, getBootId } from "../packages/core/src/storage/boot-id";
17
+ import { DurableLaneQueue, registerExecutor } from "../packages/core/src/gateway/durable-queue";
18
+ import { createJob, claimJob, getJob, reclaimOrInterrupt } from "../packages/core/src/gateway/job-store";
19
+ import { reconcileOnBoot } from "../packages/core/src/storage/reconcile";
20
+
21
+ let queue: DurableLaneQueue | null = null;
22
+
23
+ beforeEach(async () => {
24
+ closeHiveDb();
25
+ resetBootId();
26
+ await ensureHiveDb();
27
+ });
28
+
29
+ afterEach(() => {
30
+ queue?.stop();
31
+ queue = null;
32
+ closeHiveDb();
33
+ });
34
+
35
+ async function waitFor(predicate: () => Promise<boolean>, timeoutMs = 3000): Promise<void> {
36
+ const start = Date.now();
37
+ while (Date.now() - start < timeoutMs) {
38
+ if (await predicate()) return;
39
+ await new Promise((r) => setTimeout(r, 25));
40
+ }
41
+ throw new Error("waitFor timed out");
42
+ }
43
+
44
+ describe("durable-queue: dispatch + reclaim", () => {
45
+ test("enqueue dispatches the job and persists the executor result", async () => {
46
+ const executed: string[] = [];
47
+ registerExecutor("worker_task", async (job) => {
48
+ executed.push(job.id);
49
+ return { ok: true, result: "done!" };
50
+ });
51
+
52
+ queue = new DurableLaneQueue({ maxGlobalConcurrency: 2 });
53
+ const job = await queue.enqueue({
54
+ lane: "task:test-1",
55
+ type: "worker_task",
56
+ run_id: "run-x",
57
+ payload: { hello: "world" },
58
+ });
59
+
60
+ await waitFor(async () => (await getJob(job.id))?.status === "completed");
61
+
62
+ const finished = await getJob(job.id);
63
+ expect(executed).toContain(job.id);
64
+ expect(finished!.status).toBe("completed");
65
+ expect(JSON.parse(finished!.result_json!)).toBe("done!");
66
+ expect(finished!.boot_id).toBeNull();
67
+ });
68
+
69
+ test("start() re-dispatches jobs left pending by a previous boot", async () => {
70
+ const executed: string[] = [];
71
+ registerExecutor("worker_task", async (job) => {
72
+ executed.push(job.id);
73
+ return { ok: true, result: null };
74
+ });
75
+
76
+ // Jobs created directly (as if the process died right after enqueue)
77
+ const j1 = await createJob({ lane: "task:a", type: "worker_task", payload: {}, run_id: "r1" });
78
+ const j2 = await createJob({ lane: "task:b", type: "worker_task", payload: {}, run_id: "r2" });
79
+
80
+ queue = new DurableLaneQueue({ maxGlobalConcurrency: 2 });
81
+ queue.start();
82
+
83
+ await waitFor(async () =>
84
+ (await getJob(j1.id))?.status === "completed" && (await getJob(j2.id))?.status === "completed"
85
+ );
86
+ expect(executed.sort()).toEqual([j1.id, j2.id].sort());
87
+ });
88
+
89
+ test("reconcileOnBoot reclaims a running job immediately, ignoring its lease", async () => {
90
+ const job = await createJob({ lane: "task:c", type: "worker_task", payload: {}, run_id: "r3" });
91
+ const claimed = await claimJob(job.id, "dead-boot");
92
+ expect(claimed!.status).toBe("running");
93
+ // Lease is still fresh (30 min) — at boot it must be reclaimed anyway
94
+ expect(claimed!.lease_expires_at! > Date.now()).toBe(true);
95
+
96
+ await reconcileOnBoot(getBootId());
97
+
98
+ const after = await getJob(job.id);
99
+ expect(after!.status).toBe("pending");
100
+ expect(after!.boot_id).toBeNull();
101
+ expect(after!.attempts).toBe(1);
102
+ });
103
+
104
+ test("forced reclaim interrupts the job when attempts are exhausted", async () => {
105
+ const job = await createJob({ lane: "task:d", type: "worker_task", payload: {}, run_id: "r4", max_attempts: 2 });
106
+ await claimJob(job.id, "boot-1"); // attempts = 1
107
+ await reclaimOrInterrupt(job.id, { force: true }); // back to pending
108
+ await claimJob(job.id, "boot-2"); // attempts = 2
109
+ const final = await reclaimOrInterrupt(job.id, { force: true });
110
+
111
+ expect(final!.status).toBe("interrupted");
112
+ expect(final!.error).toContain("Max attempts");
113
+ });
114
+ });
@@ -0,0 +1,64 @@
1
+ /**
2
+ * `harness/` pasó de tener implementación propia a ser un barrel.
3
+ *
4
+ * Hasta 0.1.5 el módulo traía sus propias copias de `db-helpers`, `boot-id`,
5
+ * `reconcile`, `collections`, `run-store`, `run-epoch` y `proof-packet`, en
6
+ * paralelo con las de `storage/` y `agent/`. Dos almacenes de jobs sobre las
7
+ * mismas colecciones de HiveDB son una sola cosa con dos estados posibles.
8
+ *
9
+ * Lo que este test protege no es el comportamiento (eso lo cubren
10
+ * job-store/durable-queue/run-store.test.ts) sino el contrato del subpath
11
+ * `@johpaz/hive-sdk/harness`: que siga exportando los mismos nombres y que
12
+ * apunten a la implementación única.
13
+ */
14
+
15
+ process.env.HIVE_DB_PATH = ":memory:";
16
+
17
+ import { describe, test, expect } from "bun:test";
18
+ import * as harness from "../packages/core/src/harness/index.ts";
19
+ import { createJob } from "../packages/core/src/gateway/job-store.ts";
20
+ import { createRun } from "../packages/core/src/agent/run-store.ts";
21
+ import { col } from "../packages/core/src/storage/hive.ts";
22
+
23
+ describe("subpath @johpaz/hive-sdk/harness", () => {
24
+ test("sigue exportando la superficie que tenía", () => {
25
+ for (const name of [
26
+ "createJob",
27
+ "getJob",
28
+ "registerExecutor",
29
+ "getDurableQueue",
30
+ "initDurableQueue",
31
+ "DurableLaneQueue",
32
+ "createRun",
33
+ "checkpoint",
34
+ "completeRun",
35
+ "failRun",
36
+ "interruptRun",
37
+ "reclaimRun",
38
+ "getRun",
39
+ "buildRunEpoch",
40
+ "buildProofPacket",
41
+ "verifyGoal",
42
+ "getBootId",
43
+ "resetBootId",
44
+ "reconcileOnBoot",
45
+ "ensureHarnessIndexes",
46
+ "col",
47
+ "nextId",
48
+ "updateDoc",
49
+ "findByAny",
50
+ "toIndexable",
51
+ "fromIndexable",
52
+ "NO_PARENT",
53
+ ]) {
54
+ expect(harness[name as keyof typeof harness], `falta el export ${name}`).toBeDefined();
55
+ }
56
+ });
57
+
58
+ test("re-exporta la misma función, no una copia", () => {
59
+ // Si esto falla es que harness/ volvió a tener implementación propia.
60
+ expect(harness.createJob).toBe(createJob);
61
+ expect(harness.createRun).toBe(createRun);
62
+ expect(harness.col).toBe(col);
63
+ });
64
+ });