@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
@@ -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
+ }