@johpaz/hive-sdk 0.1.4 → 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 -27
  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 -18
  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,41 +1,105 @@
1
+ import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto"
2
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
3
+ import * as path from "node:path"
4
+ import { getHiveDir } from "../config/loader.ts"
1
5
  import { logger } from "../utils/logger"
6
+ import { col } from "./hive"
2
7
 
3
8
  const log = logger.child("crypto")
4
9
  const SERVICE = "hive"
5
10
 
6
- // ─── Keychain with in-memory fallback ────────────────────────────────────────
7
- // On Linux headless (no GNOME Keyring / libsecret) Bun.secrets throws.
8
- // Fall back to in-memory storage so the server stays functional, but log a
9
- // warning so operators know secrets won't survive a restart in that mode.
11
+ interface SecretDoc {
12
+ ciphertext: string
13
+ iv: string
14
+ }
15
+
16
+ // ─── Durable secret store ────────────────────────────────────────────────────
17
+ // The `secrets` collection is the source of truth: AES-256-GCM ciphertext in
18
+ // the database, keyed by <HIVE_HOME>/.master.key. It lives inside the data
19
+ // directory, so whatever persists the database (a Docker volume, a backup)
20
+ // persists the secrets with it.
21
+ //
22
+ // The OS keychain (Bun.secrets) is only a best-effort mirror. It throws on
23
+ // headless Linux/Docker (no libsecret/D-Bus), and on desktops it can be a
24
+ // session keyring that is discarded when the session ends — so a secret
25
+ // written *only* there does not survive a server restart. That is exactly
26
+ // what was wiping every provider API key, channel token and MCP header on
27
+ // restart in production.
10
28
 
11
29
  const _mem = new Map<string, string>()
12
30
  let _keychainOk: boolean | null = null // null = untested
13
31
 
14
32
  async function _get(name: string): Promise<string | null> {
15
- if (_keychainOk === false) return _mem.get(name) ?? null
33
+ const cached = _mem.get(name)
34
+ if (cached !== undefined) return cached
35
+
36
+ // Durable store first — it is the one every write goes to.
37
+ const stored = await _readCollectionSecret(name)
38
+ if (stored) return stored
39
+
40
+ // Legacy/desktop installs may only have the value in the OS keychain.
41
+ const fromKeychain = await _keychainGet(name)
42
+ if (fromKeychain) _mem.set(name, fromKeychain)
43
+ return fromKeychain
44
+ }
45
+
46
+ /**
47
+ * Persist a secret. Always writes the durable store; the keychain is
48
+ * mirrored on top when available. Returns false only when the value ended up
49
+ * nowhere but this process's memory — callers can use that to warn instead of
50
+ * silently accepting a secret that dies with the process.
51
+ */
52
+ async function _set(name: string, value: string): Promise<boolean> {
53
+ _mem.set(name, value)
54
+ const durable = await persistSecretToCollection(name, value)
55
+ const mirrored = await _keychainSet(name, value)
56
+ if (!durable && !mirrored) {
57
+ log.error(`[secrets] ${name} could not be persisted — it will be lost on restart`)
58
+ }
59
+ return durable || mirrored
60
+ }
61
+
62
+ /**
63
+ * Read a secret from the `secrets` HiveDB collection — the durable store.
64
+ * Decrypted values are cached in memory for the rest of the process.
65
+ */
66
+ async function _readCollectionSecret(name: string): Promise<string | null> {
67
+ try {
68
+ const secrets = await col<SecretDoc>("secrets")
69
+ const entry = await secrets.get(name)
70
+ if (!entry) return null
71
+ const plain = decryptSecret(entry.doc.ciphertext, entry.doc.iv)
72
+ if (plain) {
73
+ // Cache in memory for subsequent lookups in this process
74
+ _mem.set(name, plain)
75
+ }
76
+ return plain || null
77
+ } catch {
78
+ return null
79
+ }
80
+ }
81
+
82
+ async function _keychainGet(name: string): Promise<string | null> {
83
+ if (_keychainOk === false) return null
16
84
  try {
17
85
  const val = await (Bun as any).secrets.get({ service: SERVICE, name })
18
86
  _keychainOk = true
19
87
  return val ?? null
20
88
  } catch {
21
89
  _keychainOk = false
22
- return _mem.get(name) ?? null
90
+ return null
23
91
  }
24
92
  }
25
93
 
26
- async function _set(name: string, value: string): Promise<void> {
27
- if (_keychainOk === false) {
28
- log.warn(`[secrets] OS keychain unavailable — in-memory fallback (secret lost on restart): ${name}`)
29
- _mem.set(name, value)
30
- return
31
- }
94
+ async function _keychainSet(name: string, value: string): Promise<boolean> {
95
+ if (_keychainOk === false) return false
32
96
  try {
33
97
  await (Bun as any).secrets.set({ service: SERVICE, name, value })
34
98
  _keychainOk = true
99
+ return true
35
100
  } catch {
36
101
  _keychainOk = false
37
- log.warn(`[secrets] OS keychain unavailable — in-memory fallback (secret lost on restart): ${name}`)
38
- _mem.set(name, value)
102
+ return false
39
103
  }
40
104
  }
41
105
 
@@ -46,6 +110,28 @@ async function _del(name: string): Promise<void> {
46
110
  } catch {
47
111
  // ignore — might not exist or keychain unavailable
48
112
  }
113
+ try {
114
+ const secrets = await col<SecretDoc>("secrets")
115
+ await secrets.delete(name)
116
+ } catch {
117
+ // ignore — might not exist
118
+ }
119
+ }
120
+
121
+ /**
122
+ * Mint the master key (if this is a fresh install) and report where secrets
123
+ * will be stored. Called once at boot so an operator sees the answer before
124
+ * typing an API key, not after a restart lost it.
125
+ */
126
+ export function ensureSecretsBackend(): { durable: boolean } {
127
+ const durable = getMasterKey() !== null
128
+ if (!durable) {
129
+ log.error(
130
+ `[secrets] No master key available — API keys and channel tokens will NOT survive a restart. ` +
131
+ `Set HIVE_MASTER_KEY or make ${getHiveDir()} writable.`
132
+ )
133
+ }
134
+ return { durable }
49
135
  }
50
136
 
51
137
  // ─── Primitive API ────────────────────────────────────────────────────────────
@@ -64,16 +150,22 @@ export async function deleteSecret(name: string): Promise<void> {
64
150
 
65
151
  // ─── Provider secrets ────────────────────────────────────────────────────────
66
152
 
67
- export async function storeProviderApiKey(id: string, apiKey: string): Promise<void> {
68
- await _set(`provider:${id}:api_key`, apiKey)
153
+ /**
154
+ * Returns true if the secret was persisted to a durable store (OS keychain
155
+ * or the `secrets` collection fallback). Returns false if it ended up in the
156
+ * per-process in-memory map only — useful so callers can avoid destructive
157
+ * actions that would lose data on restart.
158
+ */
159
+ export async function storeProviderApiKey(id: string, apiKey: string): Promise<boolean> {
160
+ return await _set(`provider:${id}:api_key`, apiKey)
69
161
  }
70
162
 
71
163
  export async function loadProviderApiKey(id: string): Promise<string> {
72
164
  return (await _get(`provider:${id}:api_key`)) ?? ""
73
165
  }
74
166
 
75
- export async function storeProviderHeaders(id: string, headers: Record<string, unknown>): Promise<void> {
76
- await _set(`provider:${id}:headers`, JSON.stringify(headers))
167
+ export async function storeProviderHeaders(id: string, headers: Record<string, unknown>): Promise<boolean> {
168
+ return await _set(`provider:${id}:headers`, JSON.stringify(headers))
77
169
  }
78
170
 
79
171
  export async function loadProviderHeaders(id: string): Promise<Record<string, unknown>> {
@@ -90,8 +182,8 @@ export async function deleteProviderSecrets(id: string): Promise<void> {
90
182
 
91
183
  // ─── Channel secrets ─────────────────────────────────────────────────────────
92
184
 
93
- export async function storeChannelConfig(id: string, config: Record<string, unknown>): Promise<void> {
94
- await _set(`channel:${id}:config`, JSON.stringify(config))
185
+ export async function storeChannelConfig(id: string, config: Record<string, unknown>): Promise<boolean> {
186
+ return await _set(`channel:${id}:config`, JSON.stringify(config))
95
187
  }
96
188
 
97
189
  export async function loadChannelConfig(id: string): Promise<Record<string, unknown>> {
@@ -105,8 +197,8 @@ export async function deleteChannelSecrets(id: string): Promise<void> {
105
197
 
106
198
  // ─── MCP secrets ──────────────────────────────────────────────────────────────
107
199
 
108
- export async function storeMcpHeaders(id: string, headers: Record<string, unknown>): Promise<void> {
109
- await _set(`mcp:${id}:headers`, JSON.stringify(headers))
200
+ export async function storeMcpHeaders(id: string, headers: Record<string, unknown>): Promise<boolean> {
201
+ return await _set(`mcp:${id}:headers`, JSON.stringify(headers))
110
202
  }
111
203
 
112
204
  export async function loadMcpHeaders(id: string): Promise<Record<string, unknown>> {
@@ -114,8 +206,8 @@ export async function loadMcpHeaders(id: string): Promise<Record<string, unknown
114
206
  return raw ? JSON.parse(raw) : {}
115
207
  }
116
208
 
117
- export async function storeMcpEnv(id: string, env: Record<string, string>): Promise<void> {
118
- await _set(`mcp:${id}:env`, JSON.stringify(env))
209
+ export async function storeMcpEnv(id: string, env: Record<string, string>): Promise<boolean> {
210
+ return await _set(`mcp:${id}:env`, JSON.stringify(env))
119
211
  }
120
212
 
121
213
  export async function loadMcpEnv(id: string): Promise<Record<string, string>> {
@@ -132,8 +224,8 @@ export async function deleteMcpSecrets(id: string): Promise<void> {
132
224
 
133
225
  // ─── Agent secrets ────────────────────────────────────────────────────────────
134
226
 
135
- export async function storeAgentHeaders(id: string, headers: Record<string, unknown>): Promise<void> {
136
- await _set(`agent:${id}:headers`, JSON.stringify(headers))
227
+ export async function storeAgentHeaders(id: string, headers: Record<string, unknown>): Promise<boolean> {
228
+ return await _set(`agent:${id}:headers`, JSON.stringify(headers))
137
229
  }
138
230
 
139
231
  export async function loadAgentHeaders(id: string): Promise<Record<string, unknown>> {
@@ -164,31 +256,15 @@ export function verifyPassword(password: string, hash: string): boolean {
164
256
  return hasher.digest("hex") === hash
165
257
  }
166
258
 
167
- // ─── Legacy AES-256-GCM decryption ──────────────────────────────────────────
168
- // Used only by the one-shot migration in storage/migrate.ts.
169
- // Safe to remove after all installs have run the migration once.
170
-
171
- export function legacyDecryptAES(encrypted: string, iv: string): string {
172
- const nodeCrypto = require("node:crypto")
173
- const nodeFs = require("node:fs")
174
- const nodePath = require("node:path")
175
- const nodeOs = require("node:os")
176
-
177
- let key: Buffer
178
- const masterKey = process.env.HIVE_MASTER_KEY
179
- if (masterKey) {
180
- key = Buffer.from(masterKey.slice(0, 32).padEnd(32, "0"), "utf8")
181
- } else {
182
- const hiveDir = process.env.HIVE_HOME || nodePath.join(nodeOs.homedir(), ".hive")
183
- const keyPath = nodePath.join(hiveDir, ".master.key")
184
- if (!nodeFs.existsSync(keyPath)) return ""
185
- key = Buffer.from(nodeFs.readFileSync(keyPath, "utf-8").trim(), "hex")
186
- }
259
+ // ─── AES-256-GCM for the collection-backed secret fallback ──────────────────
187
260
 
261
+ export function decryptSecret(encrypted: string, iv: string): string {
262
+ const key = getMasterKey()
263
+ if (!key) return ""
188
264
  try {
189
265
  const ivBuf = Buffer.from(iv, "hex")
190
266
  const [encData, authTag] = encrypted.split(":")
191
- const decipher = nodeCrypto.createDecipheriv("aes-256-gcm", key, ivBuf)
267
+ const decipher = createDecipheriv("aes-256-gcm", key, ivBuf)
192
268
  decipher.setAuthTag(Buffer.from(authTag, "hex"))
193
269
  return decipher.update(encData, "hex", "utf8") + decipher.final("utf8")
194
270
  } catch {
@@ -196,38 +272,93 @@ export function legacyDecryptAES(encrypted: string, iv: string): string {
196
272
  }
197
273
  }
198
274
 
199
- // ─── SDK compatibility functions ─────────────────────────────────────────────
200
-
201
- export interface EncryptedData {
202
- encrypted: string;
203
- iv: string;
204
- }
205
-
206
- export function encrypt(text: string): EncryptedData {
207
- const iv = Buffer.from(crypto.getRandomValues(new Uint8Array(16))).toString("hex").slice(0, 16);
208
- const key = crypto.getRandomValues(new Uint8Array(32));
209
- const encoder = new TextEncoder();
210
- const data = encoder.encode(text);
211
- // Simplified AES-GCM using Web Crypto
212
- return { encrypted: text, iv };
213
- }
275
+ let _masterKey: Buffer | null = null
276
+
277
+ /**
278
+ * The AES key protecting the `secrets` collection.
279
+ *
280
+ * `HIVE_MASTER_KEY` wins when set (the recommended way to run in production:
281
+ * the key never touches the data volume). Otherwise the key is minted on
282
+ * first use and written 0600 to `<HIVE_HOME>/.master.key`, next to the
283
+ * database a fresh install becomes durable without the user configuring
284
+ * anything, and restoring the data directory restores the ability to read
285
+ * the secrets inside it.
286
+ */
287
+ function getMasterKey(): Buffer | null {
288
+ if (_masterKey) return _masterKey
289
+
290
+ const fromEnv = process.env.HIVE_MASTER_KEY
291
+ if (fromEnv) {
292
+ _masterKey = Buffer.from(fromEnv.slice(0, 32).padEnd(32, "0"), "utf8")
293
+ return _masterKey
294
+ }
214
295
 
215
- export function decrypt(data: EncryptedData): string {
216
- return data.encrypted;
296
+ const keyPath = path.join(getHiveDir(), ".master.key")
297
+ try {
298
+ if (!existsSync(keyPath)) {
299
+ mkdirSync(path.dirname(keyPath), { recursive: true })
300
+ try {
301
+ // "wx" so two processes starting at once can't overwrite each other's key
302
+ writeFileSync(keyPath, randomBytes(32).toString("hex"), { mode: 0o600, flag: "wx" })
303
+ log.info(`[secrets] Master key generated at ${keyPath}`)
304
+ } catch (err) {
305
+ if ((err as NodeJS.ErrnoException).code !== "EEXIST") throw err
306
+ }
307
+ }
308
+ const key = Buffer.from(readFileSync(keyPath, "utf8").trim(), "hex")
309
+ if (key.length !== 32) {
310
+ log.error(`[secrets] ${keyPath} is not a 32-byte hex key — secrets cannot be read or written`)
311
+ return null
312
+ }
313
+ _masterKey = key
314
+ return _masterKey
315
+ } catch (err) {
316
+ log.error(`[secrets] Could not read or create ${keyPath}: ${(err as Error).message}`)
317
+ return null
318
+ }
217
319
  }
218
320
 
219
- export function encryptApiKey(apiKey: string): { encrypted: string; iv: string } {
220
- return encrypt(apiKey);
321
+ /**
322
+ * Encrypt a plaintext string with AES-256-GCM. Format:
323
+ * `<encDataHex>:<authTagHex>`. Used for every document in the `secrets`
324
+ * collection.
325
+ */
326
+ export function encryptSecret(plain: string, ivHex: string): string {
327
+ const key = getMasterKey()
328
+ if (!key) return ""
329
+ try {
330
+ const iv = Buffer.from(ivHex, "hex")
331
+ const cipher = createCipheriv("aes-256-gcm", key, iv)
332
+ const encData = cipher.update(plain, "utf8", "hex") + cipher.final("hex")
333
+ const authTag = cipher.getAuthTag().toString("hex")
334
+ return `${encData}:${authTag}`
335
+ } catch {
336
+ return ""
337
+ }
221
338
  }
222
339
 
223
- export function decryptApiKey(encrypted: string, iv: string): string {
224
- return decrypt({ encrypted, iv });
225
- }
340
+ /**
341
+ * Persist a secret to the `secrets` collection — the durable store — keyed by
342
+ * the canonical secret name (e.g. `provider:openai:api_key`). Returns true if
343
+ * the document was written.
344
+ */
345
+ async function persistSecretToCollection(name: string, value: string): Promise<boolean> {
346
+ const key = getMasterKey()
347
+ if (!key) {
348
+ log.warn(`[secrets] No master key — cannot persist ${name} durably`)
349
+ return false
350
+ }
226
351
 
227
- export function encryptConfig(config: Record<string, unknown>): { encrypted: string; iv: string } {
228
- return encrypt(JSON.stringify(config));
229
- }
352
+ const iv = randomBytes(12).toString("hex")
353
+ const ciphertext = encryptSecret(value, iv)
354
+ if (!ciphertext) return false
230
355
 
231
- export function decryptConfig(encrypted: string, iv: string): Record<string, unknown> {
232
- return JSON.parse(decrypt({ encrypted, iv }));
356
+ try {
357
+ const secrets = await col<SecretDoc>("secrets")
358
+ await secrets.put(name, { ciphertext, iv })
359
+ return true
360
+ } catch (err) {
361
+ log.warn(`[secrets] Durable write failed for ${name}: ${(err as Error).message}`)
362
+ return false
363
+ }
233
364
  }
@@ -1,16 +1,20 @@
1
1
  /**
2
- * Reusable HiveDB collection helpers for the harness module — ported from
3
- * `hive`'s `storage/hive.ts`. Provides the primitives HiveDB's `Collection`
4
- * API doesn't have directly: autoincrement ids (`nextId`), whitelisted
5
- * partial UPDATEs (`updateDoc`), and `WHERE field IN (...)` (`findByAny`).
2
+ * Reusable HiveDB collection helpers.
3
+ *
4
+ * These replace the SQL patterns that don't have a direct HiveDB primitive:
5
+ * autoincrement ids (`nextId`), whitelisted partial UPDATEs (`updateDoc`,
6
+ * `updateManyByIndex`), `WHERE field IN (...)` (`findByAny`), and
7
+ * SUM/AVG/GROUP BY (`bumpRollup`).
6
8
  */
7
9
 
8
- import { hiveCollection } from "../storage/HiveDBStorage.ts";
10
+ import { getHiveDb } from "./hivedb";
9
11
 
10
12
  const MAX_RETRIES = 5;
11
13
 
12
14
  /** Sentinel for nullable FK-like fields used in equality indexes (`findBy`/`createIndex` reject `null`). */
13
15
  export const NO_PARENT = "__none__";
16
+ /** Sentinel for a broadcast recipient (e.g. `agentBusMessages.to_worker_id`). */
17
+ export const BROADCAST = "*";
14
18
 
15
19
  /** Encode a nullable FK-like value for storage in an indexed field. */
16
20
  export function toIndexable(value: string | null | undefined): string {
@@ -23,7 +27,8 @@ export function fromIndexable(value: string): string | null {
23
27
  }
24
28
 
25
29
  export async function col<T>(name: string) {
26
- return hiveCollection<T>(name);
30
+ const db = await getHiveDb();
31
+ return db.collection<T>(name);
27
32
  }
28
33
 
29
34
  /**
@@ -32,7 +37,7 @@ export async function col<T>(name: string) {
32
37
  * conflicts (another writer bumped the same counter concurrently).
33
38
  */
34
39
  export async function nextId(counterName: string): Promise<string> {
35
- const counters = await col<{ value: number }>("harness_counters");
40
+ const counters = await col<{ value: number }>("counters");
36
41
  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
37
42
  const cur = await counters.get(counterName);
38
43
  const next = (cur?.doc.value ?? 0) + 1;
@@ -71,6 +76,23 @@ export async function updateDoc<T extends object>(
71
76
  throw new Error(`updateDoc: too much contention on ${collection}/${id}`);
72
77
  }
73
78
 
79
+ /**
80
+ * Apply the same patch to every document whose indexed `field` equals
81
+ * `value` — replaces `UPDATE x SET ... WHERE fk = ?`. Requires a prior
82
+ * `createIndex(field)` on the collection.
83
+ */
84
+ export async function updateManyByIndex<T extends object>(
85
+ collection: string,
86
+ field: string,
87
+ value: string | number | boolean,
88
+ patch: Partial<T>
89
+ ): Promise<number> {
90
+ const c = await col<T>(collection);
91
+ const rows = await c.findBy(field, value);
92
+ for (const r of rows) await updateDoc<T>(collection, r.id, patch);
93
+ return rows.length;
94
+ }
95
+
74
96
  /**
75
97
  * Fetch documents whose indexed `field` matches any of `values` — emulates
76
98
  * `WHERE field IN (...)`. Requires a prior `createIndex(field)`.
@@ -85,3 +107,37 @@ export async function findByAny<T>(
85
107
  const chunks = await Promise.all(uniq.map((v) => c.findBy(field, v)));
86
108
  return chunks.flat();
87
109
  }
110
+
111
+ /**
112
+ * Increment numeric fields on a rollup/counter document — replaces
113
+ * SUM/AVG/GROUP BY. `nested` additionally bumps a per-key breakdown (e.g.
114
+ * `byProvider[provider]`) inside the same document. Creates the document on
115
+ * first use. Retries on optimistic-concurrency conflicts.
116
+ */
117
+ export async function bumpRollup(
118
+ collection: string,
119
+ id: string,
120
+ delta: Record<string, number>,
121
+ nested?: { field: string; key: string }
122
+ ): Promise<void> {
123
+ const c = await col<Record<string, any>>(collection);
124
+ for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
125
+ const existing = await c.get(id);
126
+ const doc: Record<string, any> = existing ? { ...existing.doc } : {};
127
+ for (const [k, v] of Object.entries(delta)) doc[k] = (doc[k] ?? 0) + v;
128
+ if (nested) {
129
+ doc[nested.field] = { ...(doc[nested.field] ?? {}) };
130
+ doc[nested.field][nested.key] = { ...(doc[nested.field][nested.key] ?? {}) };
131
+ for (const [k, v] of Object.entries(delta)) {
132
+ doc[nested.field][nested.key][k] = (doc[nested.field][nested.key][k] ?? 0) + v;
133
+ }
134
+ }
135
+ try {
136
+ await c.put(id, doc, { expectedVersion: existing?.version ?? 0 });
137
+ return;
138
+ } catch {
139
+ // Version conflict — retry with a fresh read.
140
+ }
141
+ }
142
+ throw new Error(`bumpRollup: too much contention on ${collection}/${id}`);
143
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * HiveDB — singleton accessor
3
+ *
4
+ * HiveDB (@johpaz/hive-db) is the embedded Rust engine (redb + tantivy BM25 +
5
+ * hnsw_rs) that is the sole data store for Hive: capability search (tools,
6
+ * skills, playbook, MCP tools) via its BM25/hybrid index, and every other
7
+ * piece of relational-shaped data (users, agents, providers, models,
8
+ * conversations, cron, projects, ACE/playbook, ...) via its document
9
+ * collections (`db.collection<T>(name)`).
10
+ *
11
+ * The database lives at ~/.hive/data/hivedb (or ~/.hive-dev/data/hivedb in
12
+ * dev).
13
+ */
14
+
15
+ import path from "node:path";
16
+ import { HiveDB } from "@johpaz/hive-db";
17
+ import { getHiveDir } from "../config/loader.ts";
18
+ import { logger } from "../utils/logger";
19
+
20
+ const log = logger.child("hivedb");
21
+
22
+ let db: HiveDB | null = null;
23
+ let opening: Promise<HiveDB> | null = null;
24
+
25
+ export function getHiveDbPath(): string {
26
+ // Tests can point the database at an ephemeral (":memory:") instance.
27
+ if (process.env.HIVE_DB_PATH) return process.env.HIVE_DB_PATH;
28
+ return path.join(getHiveDir(), "data", "hivedb");
29
+ }
30
+
31
+ /**
32
+ * Get the shared HiveDB instance, opening it on first use.
33
+ * Concurrent callers share the same open() promise.
34
+ */
35
+ export async function getHiveDb(): Promise<HiveDB> {
36
+ if (db) return db;
37
+ if (!opening) {
38
+ const dbPath = getHiveDbPath();
39
+ opening = HiveDB.open(dbPath).then((opened) => {
40
+ db = opened;
41
+ log.info(`[hivedb] Opened at ${dbPath}`);
42
+ return opened;
43
+ });
44
+ opening.catch(() => {
45
+ opening = null;
46
+ });
47
+ }
48
+ return opening;
49
+ }
50
+
51
+ export function closeHiveDb(): void {
52
+ if (db) {
53
+ try {
54
+ db.close();
55
+ } catch (err) {
56
+ log.warn(`[hivedb] Error closing database: ${(err as Error).message}`);
57
+ }
58
+ db = null;
59
+ opening = null;
60
+ }
61
+ }
@@ -1,21 +1,114 @@
1
- export { getDbPathLazy, getDb, initializeDatabase, DatabaseService, dbService } from "./SQLiteStorage.ts";
2
- export { SCHEMA, PROJECTS_SCHEMA, CONTEXT_ENGINE_SCHEMA, MEETING_SCHEMA } from "./schema.ts";
3
- export { recordUsage, getUsageStats, getProviderPricing, estimateCostForTokens, getAverageTokenCost, recordToonSavings } from "./usage.ts";
1
+ /**
2
+ * Superficie pública de storage.
3
+ *
4
+ * HiveDB es la única fuente de verdad. La capa SQLite síncrona (`SQLiteStorage`,
5
+ * `schema.ts`, `hiveSeed.ts`) desapareció en 0.1.5: convivían dos backends y el
6
+ * seed de HiveDB salía fire-and-forget desde el de SQLite, así que cuál de los
7
+ * dos ganaba dependía del timing.
8
+ */
9
+
10
+ // ─── Conexión y bootstrap ────────────────────────────────────────────────────
11
+ export { getHiveDbPath, getHiveDb, closeHiveDb } from "./hivedb.ts";
12
+ export { ensureHiveDb, isBootstrapped } from "./bootstrap.ts";
13
+
14
+ // ─── Acceso a colecciones ────────────────────────────────────────────────────
15
+ export {
16
+ col,
17
+ nextId,
18
+ updateDoc,
19
+ updateManyByIndex,
20
+ findByAny,
21
+ bumpRollup,
22
+ toIndexable,
23
+ fromIndexable,
24
+ NO_PARENT,
25
+ BROADCAST,
26
+ } from "./hive.ts";
27
+
28
+ // ─── Shapes de documento ─────────────────────────────────────────────────────
29
+ export type * from "./collections.ts";
30
+
31
+ // ─── Claves del catálogo de modelos ──────────────────────────────────────────
32
+ // El prefijo de revendedor evita que dos providers que sirven el mismo modelo
33
+ // se pisen la fila entre sí — ver el JSDoc de model-id.ts.
34
+ export { catalogModelKey, wireModelId, isResellerProvider } from "./model-id.ts";
35
+
36
+ // ─── Seed del catálogo ───────────────────────────────────────────────────────
37
+ export type { SeedData } from "./seed.ts";
38
+ export {
39
+ SEED_DATA,
40
+ seedAllData,
41
+ seedToolsAndSkills,
42
+ activateElement,
43
+ deactivateElement,
44
+ getAllElements,
45
+ getActiveElements,
46
+ } from "./seed.ts";
47
+
48
+ // ─── Consumo y costos ────────────────────────────────────────────────────────
49
+ // El precio vive en la fila del modelo (`input_per_1m` / `output_per_1m`), no en
50
+ // un mapa hardcodeado: `MODEL_PRICING` era una segunda lista que se desfasaba
51
+ // del catálogo en silencio.
4
52
  export type { UsageRecord, UsageSummary } from "./usage.ts";
53
+ export {
54
+ recordUsage,
55
+ getUsageStats,
56
+ calculateCost,
57
+ invalidateModelPricingCache,
58
+ recordToonSavings,
59
+ hourBucket,
60
+ } from "./usage.ts";
61
+
62
+ // ─── Secretos ────────────────────────────────────────────────────────────────
63
+ export {
64
+ ensureSecretsBackend,
65
+ storeSecret,
66
+ loadSecret,
67
+ deleteSecret,
68
+ storeProviderApiKey,
69
+ loadProviderApiKey,
70
+ storeProviderHeaders,
71
+ loadProviderHeaders,
72
+ deleteProviderSecrets,
73
+ storeChannelConfig,
74
+ loadChannelConfig,
75
+ deleteChannelSecrets,
76
+ storeMcpHeaders,
77
+ loadMcpHeaders,
78
+ storeMcpEnv,
79
+ loadMcpEnv,
80
+ deleteMcpSecrets,
81
+ storeAgentHeaders,
82
+ loadAgentHeaders,
83
+ deleteAgentSecrets,
84
+ maskApiKey,
85
+ hashPassword,
86
+ verifyPassword,
87
+ } from "./crypto.ts";
88
+
89
+ // ─── Onboarding e identidad ──────────────────────────────────────────────────
5
90
  export type { OnboardingSection } from "./onboarding.ts";
6
- export { resolveUserId, resolveAgentId, initOnboardingDb } from "./onboarding.ts";
7
- export type { EncryptedData } from "./crypto.ts";
8
- export { encrypt, decrypt, encryptApiKey, decryptApiKey, encryptConfig, decryptConfig } from "./crypto.ts";
9
- export type { SeedData } from "./seed.ts";
10
- export { SEED_DATA, seedAllData } from "./seed.ts";
11
91
  export {
12
- getHiveDbPath,
13
- openHiveDB,
14
- getHiveDB,
15
- closeHiveDB,
16
- hiveCollection,
17
- hiveAppend,
18
- hiveRead,
19
- isHiveDBInitialized,
20
- } from "./HiveDBStorage.ts";
21
- export { seedHiveDB } from "./hiveSeed.ts";
92
+ resolveUserId,
93
+ resolveAgentId,
94
+ initOnboardingDb,
95
+ saveUserProfile,
96
+ saveProviderConfig,
97
+ saveAgentConfig,
98
+ activateProvider,
99
+ activateModel,
100
+ deactivateProvider,
101
+ deactivateModel,
102
+ getAllProviders,
103
+ getAllModels,
104
+ } from "./onboarding.ts";
105
+ export { normalizeUserEmail } from "./user-email.ts";
106
+
107
+ // ─── Durabilidad entre arranques ─────────────────────────────────────────────
108
+ export { getBootId, resetBootId } from "./boot-id.ts";
109
+ export type { ReconcileResult } from "./reconcile.ts";
110
+ export { reconcileOnBoot } from "./reconcile.ts";
111
+
112
+ // ─── Log causal (G9) ─────────────────────────────────────────────────────────
113
+ export type { CausalEvent, CausalEventPattern } from "./causal-events.ts";
114
+ export { watchCausalEvents, formatCausalEvent } from "./causal-events.ts";