@johpaz/hive-sdk 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/CHANGELOG.md +129 -0
  2. package/README.md +78 -23
  3. package/bun.lock +55 -29
  4. package/bunfig.toml +4 -2
  5. package/docs/API-AGENTS.md +78 -27
  6. package/docs/API-CONTEXT-COMPILER.md +31 -34
  7. package/docs/API-TOOLS-SKILLS-CHANNELS.md +58 -22
  8. package/docs/HIVE-HARNESS.md +1 -1
  9. package/docs/INDEX.md +4 -4
  10. package/docs/TEMPLATE-HIVE-APP.md +10 -10
  11. package/package.json +17 -12
  12. package/packages/cli/package.json +2 -2
  13. package/packages/cli/src/commands/create-app.test.ts +36 -7
  14. package/packages/cli/src/commands/init.ts +3 -3
  15. package/packages/cli/src/commands/run.ts +1 -1
  16. package/packages/cli/src/commands/test.ts +37 -25
  17. package/packages/cli/src/commands/trace.ts +30 -28
  18. package/packages/cli/templates/hive-app/.env.example +10 -2
  19. package/packages/cli/templates/hive-app/README.md +103 -0
  20. package/packages/cli/templates/hive-app/hive.config.ts +9 -3
  21. package/packages/cli/templates/hive-app/src/agents/coordinator.ts +8 -1
  22. package/packages/cli/templates/hive-app/src/main.ts +12 -19
  23. package/packages/core/package.json +13 -12
  24. package/packages/core/src/agent/acceptance-checks.ts +172 -0
  25. package/packages/core/src/agent/agent-catalog.ts +348 -0
  26. package/packages/core/src/agent/agent-loop.ts +1373 -0
  27. package/packages/core/src/agent/capability-search.ts +186 -0
  28. package/packages/core/src/agent/catalog-selector.ts +103 -0
  29. package/packages/core/src/agent/{Compaction.ts → compaction.ts} +86 -63
  30. package/packages/core/src/agent/context-compiler.ts +689 -0
  31. package/packages/core/src/agent/conversation-store.ts +381 -0
  32. package/packages/core/src/agent/curator.ts +276 -0
  33. package/packages/core/src/agent/delegation-runtime.ts +241 -0
  34. package/packages/core/src/agent/goal-runner.ts +323 -0
  35. package/packages/core/src/agent/index.ts +17 -12
  36. package/packages/core/src/agent/llm-client.ts +266 -0
  37. package/packages/core/src/agent/llm-providers/anthropic.ts +264 -0
  38. package/packages/core/src/agent/llm-providers/deepseek.ts +8 -0
  39. package/packages/core/src/agent/{providers → llm-providers}/gemini.ts +98 -60
  40. package/packages/core/src/agent/llm-providers/groq.ts +5 -0
  41. package/packages/core/src/agent/llm-providers/hiveagents.ts +253 -0
  42. package/packages/core/src/agent/{providers → llm-providers}/interface.ts +73 -13
  43. package/packages/core/src/agent/llm-providers/kimi.ts +8 -0
  44. package/packages/core/src/agent/llm-providers/minimax.ts +13 -0
  45. package/packages/core/src/agent/llm-providers/mistral.ts +5 -0
  46. package/packages/core/src/agent/llm-providers/modelscope.ts +5 -0
  47. package/packages/core/src/agent/llm-providers/nvidia.ts +5 -0
  48. package/packages/core/src/agent/{providers → llm-providers}/ollama.ts +31 -5
  49. package/packages/core/src/agent/llm-providers/openai-compat-base.ts +418 -0
  50. package/packages/core/src/agent/llm-providers/openai.ts +5 -0
  51. package/packages/core/src/agent/llm-providers/opencode-go.ts +9 -0
  52. package/packages/core/src/agent/llm-providers/openrouter.ts +5 -0
  53. package/packages/core/src/agent/llm-providers/qwen.ts +5 -0
  54. package/packages/core/src/agent/llm-providers/z-ai.ts +5 -0
  55. package/packages/core/src/agent/minimal-loadout.ts +47 -0
  56. package/packages/core/src/agent/playbook-selector.ts +119 -0
  57. package/packages/core/src/agent/{PromptBuilder.ts → prompt-builder.ts} +21 -22
  58. package/packages/core/src/{harness → agent}/proof-packet.ts +16 -21
  59. package/packages/core/src/agent/providers/index.ts +35 -16
  60. package/packages/core/src/agent/reflector.ts +320 -0
  61. package/packages/core/src/agent/routing-intent.ts +22 -0
  62. package/packages/core/src/{harness → agent}/run-epoch.ts +4 -3
  63. package/packages/core/src/{harness → agent}/run-store.ts +142 -81
  64. package/packages/core/src/agent/{Service.ts → service.ts} +37 -26
  65. package/packages/core/src/agent/skill-selector.ts +374 -0
  66. package/packages/core/src/agent/stuck-loop.ts +209 -0
  67. package/packages/core/src/agent/{selectors/ToolSelector.ts → tool-selector.ts} +188 -178
  68. package/packages/core/src/{ace/Tracer.ts → agent/tracer.ts} +37 -27
  69. package/packages/core/src/api/createAgent.test.ts +139 -27
  70. package/packages/core/src/api/createAgent.ts +232 -44
  71. package/packages/core/src/artifacts/store.ts +162 -0
  72. package/packages/core/src/canvas/canvas-manager.ts +161 -0
  73. package/packages/core/src/canvas/canvas.test.ts +8 -4
  74. package/packages/core/src/canvas/emitter.ts +131 -80
  75. package/packages/core/src/canvas/index.ts +1 -3
  76. package/packages/core/src/channels/base.ts +9 -1
  77. package/packages/core/src/channels/discord.ts +5 -4
  78. package/packages/core/src/channels/manager.ts +122 -30
  79. package/packages/core/src/channels/slack.ts +5 -4
  80. package/packages/core/src/channels/telegram.ts +36 -6
  81. package/packages/core/src/channels/webchat.ts +11 -10
  82. package/packages/core/src/channels/whatsapp.ts +23 -7
  83. package/packages/core/src/config/index.ts +13 -2
  84. package/packages/core/src/config/loader.ts +76 -29
  85. package/packages/core/src/ethics/EthicsGuard.test.ts +90 -36
  86. package/packages/core/src/ethics/EthicsGuard.ts +51 -47
  87. package/packages/core/src/events/agent-bus.ts +44 -68
  88. package/packages/core/src/events/channel-narration.ts +150 -0
  89. package/packages/core/src/events/narration.ts +82 -0
  90. package/packages/core/src/events/tool-narration.ts +62 -0
  91. package/packages/core/src/gateway/delegation-groups.ts +258 -0
  92. package/packages/core/src/{harness → gateway}/durable-queue.ts +102 -42
  93. package/packages/core/src/{harness → gateway}/job-store.ts +85 -48
  94. package/packages/core/src/gateway/lane-queue.ts +173 -0
  95. package/packages/core/src/gateway/notification-inbox.ts +57 -0
  96. package/packages/core/src/gateway/server.ts +1 -1
  97. package/packages/core/src/harness/index.ts +46 -27
  98. package/packages/core/src/index.ts +33 -27
  99. package/packages/core/src/mcp/hot-reload.ts +32 -23
  100. package/packages/core/src/mcp/index.ts +6 -3
  101. package/packages/core/src/mcp/singleton.ts +1 -4
  102. package/packages/core/src/mcp/tool-sync.ts +138 -0
  103. package/packages/core/src/memory/Scratchpad.test.ts +39 -20
  104. package/packages/core/src/memory/Scratchpad.ts +27 -34
  105. package/packages/core/src/multimodal/vision-service.ts +44 -38
  106. package/packages/core/src/resilience/retry.ts +95 -0
  107. package/packages/core/src/scheduler/CronScheduler.ts +334 -287
  108. package/packages/core/src/scheduler/index.ts +9 -7
  109. package/packages/core/src/scheduler/integration.ts +46 -26
  110. package/packages/core/src/scheduler/scheduler.test.ts +9 -13
  111. package/packages/core/src/scheduler/types.ts +7 -2
  112. package/packages/core/src/security/Pairing.ts +1 -1
  113. package/packages/core/src/skills/bundled/a2ui/a2ui_dashboard/SKILL.md +176 -0
  114. package/packages/core/src/skills/bundled/a2ui/a2ui_form/SKILL.md +202 -0
  115. package/packages/core/src/skills/bundled/a2ui/a2ui_interactive/SKILL.md +206 -0
  116. package/packages/core/src/skills/bundled/agents/agent_spawner/SKILL.md +173 -0
  117. package/packages/core/src/skills/bundled/agents/memory_manager/SKILL.md +143 -0
  118. package/packages/core/src/skills/bundled/agents/research_and_remember/SKILL.md +139 -0
  119. package/packages/core/src/skills/bundled/agents/task_orchestrator/SKILL.md +98 -0
  120. package/packages/core/src/skills/bundled/api/api_client/SKILL.md +132 -0
  121. package/packages/core/src/skills/bundled/cli/cli_pipeline/SKILL.md +135 -0
  122. package/packages/core/src/skills/bundled/cli/cli_safe_exec/SKILL.md +125 -0
  123. package/packages/core/src/skills/bundled/cli/software_engineering/SKILL.md +23 -0
  124. package/packages/core/src/skills/bundled/cron_manager/SKILL.md +188 -0
  125. package/packages/core/src/skills/bundled/cron_reminder/SKILL.md +112 -0
  126. package/packages/core/src/skills/bundled/filesystem/file_manager/SKILL.md +118 -0
  127. package/packages/core/src/skills/bundled/filesystem/file_read_and_summarize/SKILL.md +109 -0
  128. package/packages/core/src/skills/bundled/filesystem/file_writer/SKILL.md +129 -0
  129. package/packages/core/src/skills/bundled/filesystem/workspace_file_operator/SKILL.md +22 -0
  130. package/packages/core/src/skills/bundled/office/office_document_manager/SKILL.md +262 -0
  131. package/packages/core/src/skills/bundled/search_knowledge/capability_discovery/SKILL.md +75 -0
  132. package/packages/core/src/skills/bundled/web/browser_automate/SKILL.md +120 -0
  133. package/packages/core/src/skills/bundled/web/browser_scrape/SKILL.md +109 -0
  134. package/packages/core/src/skills/bundled/web/web_monitor/SKILL.md +127 -0
  135. package/packages/core/src/skills/bundled/web/web_research/SKILL.md +119 -0
  136. package/packages/core/src/skills/bundled-data.generated.ts +731 -2678
  137. package/packages/core/src/skills/skills.test.ts +52 -11
  138. package/packages/core/src/{harness → storage}/boot-id.ts +5 -2
  139. package/packages/core/src/storage/bootstrap.ts +151 -0
  140. package/packages/core/src/storage/causal-events.ts +84 -0
  141. package/packages/core/src/storage/collections.ts +680 -0
  142. package/packages/core/src/storage/crypto.ts +205 -74
  143. package/packages/core/src/{harness/db-helpers.ts → storage/hive.ts} +63 -7
  144. package/packages/core/src/storage/hivedb.ts +61 -0
  145. package/packages/core/src/storage/index.ts +111 -18
  146. package/packages/core/src/storage/model-id.ts +53 -0
  147. package/packages/core/src/storage/onboarding.ts +540 -972
  148. package/packages/core/src/storage/reconcile.ts +238 -0
  149. package/packages/core/src/storage/seed.ts +572 -406
  150. package/packages/core/src/storage/usage.ts +285 -225
  151. package/packages/core/src/storage/user-email.ts +11 -0
  152. package/packages/core/src/swarm/AgentExecutor.ts +1 -1
  153. package/packages/core/src/swarm/EventBridge.ts +1 -1
  154. package/packages/core/src/swarm/index.ts +12 -9
  155. package/packages/core/src/tool-runtime/index.ts +146 -23
  156. package/packages/core/src/tool-runtime/tool-worker.ts +2 -2
  157. package/packages/core/src/tool-runtime/worker-tools.ts +27 -0
  158. package/packages/core/src/{canvas/a2ui-tools.ts → tools/a2ui/index.ts} +17 -8
  159. package/packages/core/src/tools/agents/get-available-models.ts +36 -54
  160. package/packages/core/src/tools/agents/index.ts +784 -292
  161. package/packages/core/src/tools/api/api-request.test.ts +164 -0
  162. package/packages/core/src/tools/api/api-request.ts +174 -0
  163. package/packages/core/src/tools/api/index.ts +16 -0
  164. package/packages/core/src/tools/cli/index.ts +4 -0
  165. package/packages/core/src/tools/core/index.ts +281 -112
  166. package/packages/core/src/tools/cron/index.ts +121 -124
  167. package/packages/core/src/tools/index.ts +63 -78
  168. package/packages/core/src/tools/office/office-escribir-xlsx.ts +3 -1
  169. package/packages/core/src/tools/types.ts +3 -1
  170. package/packages/core/src/tools/web/artifact-inspect.ts +23 -0
  171. package/packages/core/src/tools/web/browser-backend.ts +129 -0
  172. package/packages/core/src/tools/web/browser-screenshot.ts +26 -5
  173. package/packages/core/src/tools/web/browser-service.ts +80 -35
  174. package/packages/core/src/tools/web/browser-type.ts +3 -8
  175. package/packages/core/src/tools/web/index.ts +4 -4
  176. package/packages/core/src/tools/web/webview-backend.ts +412 -0
  177. package/packages/core/src/voice/index.ts +89 -63
  178. package/packages/core/src/workers/agent.worker.ts +2 -2
  179. package/packages/core/src/workers/workers.test.ts +3 -10
  180. package/scripts/bump-version.ts +248 -0
  181. package/scripts/generate-skill-bundle.ts +108 -0
  182. package/test/acceptance-checks.test.ts +403 -0
  183. package/test/agent-loop-terminal-synthesis.test.ts +32 -0
  184. package/test/browser-backend.test.ts +308 -0
  185. package/test/catalog-agents-stay-enabled.test.ts +117 -0
  186. package/test/causal-events.test.ts +117 -0
  187. package/test/compaction.test.ts +105 -0
  188. package/test/context-compiler.test.ts +269 -0
  189. package/test/curator.test.ts +130 -0
  190. package/test/durable-queue.test.ts +114 -0
  191. package/test/harness-barrel.test.ts +64 -0
  192. package/test/hive-helpers.test.ts +130 -0
  193. package/test/hivedb-search.test.ts +189 -0
  194. package/test/internal-turns.test.ts +166 -0
  195. package/test/job-idempotency.test.ts +68 -0
  196. package/test/job-retry-backoff.test.ts +184 -0
  197. package/test/job-store.test.ts +381 -0
  198. package/test/llm-retry.test.ts +97 -0
  199. package/test/memory-perf.test.ts +774 -0
  200. package/test/minimal-loadout.test.ts +78 -0
  201. package/test/model-catalog.test.ts +105 -0
  202. package/test/preload.ts +12 -0
  203. package/test/reflector.test.ts +320 -0
  204. package/test/retention-cap.test.ts +91 -0
  205. package/test/retired-capabilities-pruned.test.ts +192 -0
  206. package/test/run-store.test.ts +355 -0
  207. package/test/scratchpad.test.ts +74 -0
  208. package/test/secrets-durability.test.ts +119 -0
  209. package/test/seed-model-reseed.test.ts +155 -0
  210. package/test/setup-agent-seed.test.ts +264 -0
  211. package/test/tool-inventory.test.ts +65 -0
  212. package/test/tool-runtime.test.ts +258 -0
  213. package/test/tool-selector-runtime-tools.test.ts +117 -0
  214. package/test/toon.test.ts +429 -0
  215. package/tsconfig.json +2 -0
  216. package/packages/core/src/ace/Curator.ts +0 -158
  217. package/packages/core/src/ace/Reflector.ts +0 -200
  218. package/packages/core/src/ace/index.ts +0 -4
  219. package/packages/core/src/agent/AgentRunner.ts +0 -711
  220. package/packages/core/src/agent/ContextCompiler.ts +0 -567
  221. package/packages/core/src/agent/ContextGuard.ts +0 -91
  222. package/packages/core/src/agent/ConversationStore.ts +0 -254
  223. package/packages/core/src/agent/Hooks.ts +0 -166
  224. package/packages/core/src/agent/StuckLoop.ts +0 -133
  225. package/packages/core/src/agent/providers/LLMClient.ts +0 -149
  226. package/packages/core/src/agent/providers/anthropic.ts +0 -212
  227. package/packages/core/src/agent/providers/openai-compat.ts +0 -231
  228. package/packages/core/src/agent/selectors/PlaybookSelector.ts +0 -121
  229. package/packages/core/src/agent/selectors/SkillSelector.ts +0 -322
  230. package/packages/core/src/agent/selectors/index.ts +0 -6
  231. package/packages/core/src/auth/auth.ts +0 -121
  232. package/packages/core/src/auth/index.ts +0 -1
  233. package/packages/core/src/canvas/CanvasManager.ts +0 -390
  234. package/packages/core/src/canvas/canvas-tools.ts +0 -448
  235. package/packages/core/src/harness/collections.ts +0 -98
  236. package/packages/core/src/harness/goal-verifier.ts +0 -141
  237. package/packages/core/src/harness/harness.test.ts +0 -236
  238. package/packages/core/src/harness/reconcile.ts +0 -149
  239. package/packages/core/src/mcp/MCPToolAdapter.ts +0 -176
  240. package/packages/core/src/multimodal/VisionService.ts +0 -293
  241. package/packages/core/src/scheduler/dag/AgentExecutor.ts +0 -53
  242. package/packages/core/src/scheduler/dag/DAGScheduler.ts +0 -250
  243. package/packages/core/src/scheduler/dag/EventBridge.ts +0 -122
  244. package/packages/core/src/scheduler/dag/TaskGraph.ts +0 -192
  245. package/packages/core/src/scheduler/dag/TaskNode.ts +0 -97
  246. package/packages/core/src/scheduler/dag/TaskResult.ts +0 -22
  247. package/packages/core/src/scheduler/dag/errors.ts +0 -37
  248. package/packages/core/src/scheduler/dag/index.ts +0 -26
  249. package/packages/core/src/scheduler/dag/presets/ResearchPreset.ts +0 -97
  250. package/packages/core/src/scheduler/dag/strategies/ParallelStrategy.ts +0 -21
  251. package/packages/core/src/scheduler/dag/strategies/PriorityStrategy.ts +0 -46
  252. package/packages/core/src/storage/HiveDBStorage.ts +0 -64
  253. package/packages/core/src/storage/SQLiteStorage.ts +0 -414
  254. package/packages/core/src/storage/hiveSeed.ts +0 -308
  255. package/packages/core/src/storage/hiveStorage.test.ts +0 -38
  256. package/packages/core/src/storage/schema.ts +0 -689
  257. package/packages/core/src/storage/storage.test.ts +0 -37
  258. package/packages/core/src/swarm/AgentBus.ts +0 -460
  259. package/packages/core/src/swarm/EventBus.ts +0 -169
  260. package/packages/core/src/swarm/WorkerPool.ts +0 -236
  261. package/packages/core/src/tools/bridge-events.ts +0 -26
  262. package/packages/core/src/tools/canvas/index.ts +0 -375
  263. package/packages/core/src/tools/codebridge/index.ts +0 -342
  264. package/packages/core/src/tools/meeting/index.ts +0 -353
  265. package/packages/core/src/tools/projects/index.ts +0 -37
  266. package/packages/core/src/tools/projects/project-create.ts +0 -94
  267. package/packages/core/src/tools/projects/project-done.ts +0 -66
  268. package/packages/core/src/tools/projects/project-fail.ts +0 -66
  269. package/packages/core/src/tools/projects/project-list.ts +0 -96
  270. package/packages/core/src/tools/projects/project-update.ts +0 -72
  271. package/packages/core/src/tools/projects/task-create.ts +0 -68
  272. package/packages/core/src/tools/projects/task-evaluate.ts +0 -93
  273. package/packages/core/src/tools/projects/task-update.ts +0 -93
  274. package/packages/core/src/tools/voice/index.ts +0 -104
  275. package/packages/core/src/tools/web/api-request.test.ts +0 -170
  276. package/packages/core/src/tools/web/api-request.ts +0 -239
  277. package/test/setup-db.ts +0 -216
  278. /package/packages/core/src/agent/{NativeTools.ts → native-tools.ts} +0 -0
@@ -0,0 +1,238 @@
1
+ /**
2
+ * reconcileOnBoot — centralized repair of rows left in inconsistent states
3
+ * by a previous crash/kill.
4
+ *
5
+ * Currently repairs:
6
+ * - taskRuns: running → timeout (moved from server.ts inline)
7
+ * - meetingSessions: active (orphaned) → stopped
8
+ * - agentRuns: running + expired lease → interrupted (chat) or mark for re-enqueue
9
+ * - jobQueue: running + expired lease → pending (reclaim) or interrupted
10
+ *
11
+ * This is called early in the boot sequence, before the scheduler and
12
+ * durable queue are activated.
13
+ */
14
+
15
+ import { col, updateDoc } from "./hive";
16
+ import type { TaskRunDoc, MeetingSessionDoc, AgentRunDoc, JobDoc, TaskDoc } from "./collections";
17
+ import { logger } from "../utils/logger";
18
+ import { reclaimOrInterrupt } from "../gateway/job-store.ts";
19
+ import { interruptRun } from "../agent/run-store";
20
+ import { sendToUserChannel } from "../gateway/channel-notify";
21
+
22
+ const log = logger.child("reconcile");
23
+
24
+ export interface ReconcileResult {
25
+ bootId: string;
26
+ taskRunsTimedOut: number;
27
+ meetingsStopped: number;
28
+ runsInterrupted: number;
29
+ runsReEnqueueable: number;
30
+ jobsReclaimed: number;
31
+ jobsInterrupted: number;
32
+ tasksRequeued: number;
33
+ tasksFailed: number;
34
+ }
35
+
36
+ export async function reconcileOnBoot(bootId: string): Promise<ReconcileResult> {
37
+ log.info(`[reconcileOnBoot] Starting with boot_id=${bootId}`);
38
+ const result: ReconcileResult = {
39
+ bootId,
40
+ taskRunsTimedOut: 0,
41
+ meetingsStopped: 0,
42
+ runsInterrupted: 0,
43
+ runsReEnqueueable: 0,
44
+ jobsReclaimed: 0,
45
+ jobsInterrupted: 0,
46
+ tasksRequeued: 0,
47
+ tasksFailed: 0,
48
+ };
49
+
50
+ // 1. taskRuns: running → timeout (moved from server.ts:211-220)
51
+ try {
52
+ const taskRunsCol = await col<TaskRunDoc>("taskRuns");
53
+ const orphanedRuns = (await taskRunsCol.scan({})).filter((e) => e.doc.status === "running");
54
+ for (const run of orphanedRuns) {
55
+ await taskRunsCol.put(
56
+ run.id,
57
+ { ...run.doc, status: "timeout", finished_at: new Date().toISOString() },
58
+ { expectedVersion: run.version }
59
+ );
60
+ result.taskRunsTimedOut++;
61
+ }
62
+ if (result.taskRunsTimedOut > 0) {
63
+ log.info(`[reconcileOnBoot] ${result.taskRunsTimedOut} orphaned taskRuns → timeout`);
64
+ }
65
+ } catch (err) {
66
+ log.warn(`[reconcileOnBoot] Failed to repair taskRuns: ${(err as Error).message}`);
67
+ }
68
+
69
+ // 2. meetingSessions: active (orphaned) → stopped
70
+ try {
71
+ const meetingsCol = await col<MeetingSessionDoc>("meetingSessions");
72
+ const activeMeetings = await meetingsCol.findBy("status", "active");
73
+ for (const m of activeMeetings) {
74
+ await meetingsCol.put(
75
+ m.id,
76
+ { ...m.doc, status: "stopped", stopped_at: Date.now() },
77
+ { expectedVersion: m.version }
78
+ );
79
+ result.meetingsStopped++;
80
+ }
81
+ if (result.meetingsStopped > 0) {
82
+ log.info(`[reconcileOnBoot] ${result.meetingsStopped} orphaned meetings → stopped`);
83
+ }
84
+ } catch (err) {
85
+ log.warn(`[reconcileOnBoot] Failed to repair meetings: ${(err as Error).message}`);
86
+ }
87
+
88
+ // 3. agentRuns: any "running" row is orphaned at boot — HiveDB is
89
+ // single-process and this process just started, so no run can actually be
90
+ // executing. Waiting out the lease would leave fast restarts (< lease
91
+ // duration) unrepaired forever.
92
+ try {
93
+ const agentRunsCol = await col<AgentRunDoc>("agentRuns");
94
+ const runningRuns = await agentRunsCol.findBy("status", "running");
95
+ for (const entry of runningRuns) {
96
+ const run = entry.doc;
97
+
98
+ if (run.kind === "chat") {
99
+ await interruptRun(run.id, "Process restarted while chat turn was in flight");
100
+ result.runsInterrupted++;
101
+ try {
102
+ if (run.channel && run.user_id) {
103
+ await sendToUserChannel(run.channel, run.user_id, "Se interrumpió un turno en progreso por un reinicio del proceso.");
104
+ }
105
+ } catch {
106
+ // non-critical
107
+ }
108
+ } else {
109
+ await interruptRun(run.id, "Process restarted — job will be re-enqueued if durable");
110
+ result.runsReEnqueueable++;
111
+ }
112
+ }
113
+ if (result.runsInterrupted > 0 || result.runsReEnqueueable > 0) {
114
+ log.info(
115
+ `[reconcileOnBoot] ${result.runsInterrupted} chat runs interrupted, ${result.runsReEnqueueable} durable runs flagged for re-enqueue`
116
+ );
117
+ }
118
+ } catch (err) {
119
+ log.warn(`[reconcileOnBoot] Failed to repair agentRuns: ${(err as Error).message}`);
120
+ }
121
+
122
+ // 4. jobQueue: any "running" row is orphaned at boot (same single-process
123
+ // argument as above) → reclaim (pending) or interrupt, ignoring the lease.
124
+ try {
125
+ const jobsCol = await col<JobDoc>("jobQueue");
126
+ const runningJobs = await jobsCol.findBy("status", "running");
127
+ for (const entry of runningJobs) {
128
+ const job = entry.doc;
129
+ const result_doc = await reclaimOrInterrupt(job.id, { force: true });
130
+ if (result_doc?.status === "pending") result.jobsReclaimed++;
131
+ else if (result_doc?.status === "interrupted") result.jobsInterrupted++;
132
+ }
133
+ if (result.jobsReclaimed > 0 || result.jobsInterrupted > 0) {
134
+ log.info(
135
+ `[reconcileOnBoot] ${result.jobsReclaimed} jobs reclaimed to pending, ${result.jobsInterrupted} jobs interrupted (attempts exhausted)`
136
+ );
137
+ }
138
+ } catch (err) {
139
+ log.warn(`[reconcileOnBoot] Failed to repair jobQueue: ${(err as Error).message}`);
140
+ }
141
+
142
+ // 5. tasks: in_progress without a live job → pending (attempts+1) or failed
143
+ try {
144
+ const tasksCol = await col<TaskDoc>("tasks");
145
+ const inProgress = await tasksCol.findBy("status", "in_progress");
146
+ const { getJob } = await import("../gateway/job-store.ts");
147
+ for (const entry of inProgress) {
148
+ const task = entry.doc;
149
+ let hasLiveJob = false;
150
+ if (task.job_id) {
151
+ const job = await getJob(task.job_id);
152
+ // pending counts as live: the durable queue re-dispatches it at boot
153
+ if (job && (job.status === "running" || job.status === "pending")) {
154
+ hasLiveJob = true;
155
+ }
156
+ }
157
+ if (!hasLiveJob) {
158
+ const attempts = (task.attempts ?? 0) + 1;
159
+ if (attempts >= 2) {
160
+ await tasksCol.put(entry.id, {
161
+ ...task,
162
+ status: "failed",
163
+ error: "Max attempts reached after process restart",
164
+ attempts,
165
+ updated_at: Date.now(),
166
+ }, { expectedVersion: entry.version });
167
+ result.tasksFailed++;
168
+ } else {
169
+ await tasksCol.put(entry.id, {
170
+ ...task,
171
+ status: "pending",
172
+ attempts,
173
+ updated_at: Date.now(),
174
+ }, { expectedVersion: entry.version });
175
+ result.tasksRequeued++;
176
+ }
177
+ }
178
+ }
179
+ if (result.tasksRequeued > 0 || result.tasksFailed > 0) {
180
+ log.info(`[reconcileOnBoot] ${result.tasksRequeued} tasks requeued, ${result.tasksFailed} tasks failed (attempts exhausted)`);
181
+ }
182
+ } catch (err) {
183
+ log.warn(`[reconcileOnBoot] Failed to repair tasks: ${(err as Error).message}`);
184
+ }
185
+
186
+ // 6. Retention cap: keep only the most recent 500 agentRuns and 500 jobs per thread/run
187
+ const RETENTION_LIMIT = 500;
188
+ try {
189
+ const agentRunsCol = await col<AgentRunDoc>("agentRuns");
190
+ const allRuns = await agentRunsCol.scan({});
191
+ const runsByThread = new Map<string, typeof allRuns>();
192
+ for (const entry of allRuns) {
193
+ const threadId = entry.doc.thread_id ?? entry.doc.agent_id ?? "_default";
194
+ const list = runsByThread.get(threadId) ?? [];
195
+ list.push(entry);
196
+ runsByThread.set(threadId, list);
197
+ }
198
+ let runsPruned = 0;
199
+ for (const [, runs] of runsByThread) {
200
+ if (runs.length <= RETENTION_LIMIT) continue;
201
+ const sorted = runs.sort((a, b) => (b.doc.created_at ?? 0) - (a.doc.created_at ?? 0));
202
+ for (const entry of sorted.slice(RETENTION_LIMIT)) {
203
+ await agentRunsCol.delete(entry.id);
204
+ runsPruned++;
205
+ }
206
+ }
207
+ if (runsPruned > 0) {
208
+ log.info(`[reconcileOnBoot] Retention: pruned ${runsPruned} old agentRuns (cap=${RETENTION_LIMIT}/thread)`);
209
+ }
210
+
211
+ const jobsCol = await col<JobDoc>("jobQueue");
212
+ const allJobs = await jobsCol.scan({});
213
+ const jobsByRun = new Map<string, typeof allJobs>();
214
+ for (const entry of allJobs) {
215
+ const runId = entry.doc.run_id ?? "_default";
216
+ const list = jobsByRun.get(runId) ?? [];
217
+ list.push(entry);
218
+ jobsByRun.set(runId, list);
219
+ }
220
+ let jobsPruned = 0;
221
+ for (const [, jobs] of jobsByRun) {
222
+ if (jobs.length <= RETENTION_LIMIT) continue;
223
+ const sorted = jobs.sort((a, b) => (b.doc.created_at ?? 0) - (a.doc.created_at ?? 0));
224
+ for (const entry of sorted.slice(RETENTION_LIMIT)) {
225
+ await jobsCol.delete(entry.id);
226
+ jobsPruned++;
227
+ }
228
+ }
229
+ if (jobsPruned > 0) {
230
+ log.info(`[reconcileOnBoot] Retention: pruned ${jobsPruned} old jobs (cap=${RETENTION_LIMIT}/run)`);
231
+ }
232
+ } catch (err) {
233
+ log.warn(`[reconcileOnBoot] Failed to enforce retention cap: ${(err as Error).message}`);
234
+ }
235
+
236
+ log.info(`[reconcileOnBoot] Done`, result);
237
+ return result;
238
+ }