steroids-api 0.2.7

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 (328) hide show
  1. package/dist/API/src/index.d.ts +10 -0
  2. package/dist/API/src/index.d.ts.map +1 -0
  3. package/dist/API/src/index.js +130 -0
  4. package/dist/API/src/index.js.map +1 -0
  5. package/dist/API/src/routes/activity.d.ts +7 -0
  6. package/dist/API/src/routes/activity.d.ts.map +1 -0
  7. package/dist/API/src/routes/activity.js +252 -0
  8. package/dist/API/src/routes/activity.js.map +1 -0
  9. package/dist/API/src/routes/config.d.ts +7 -0
  10. package/dist/API/src/routes/config.d.ts.map +1 -0
  11. package/dist/API/src/routes/config.js +521 -0
  12. package/dist/API/src/routes/config.js.map +1 -0
  13. package/dist/API/src/routes/health.d.ts +7 -0
  14. package/dist/API/src/routes/health.d.ts.map +1 -0
  15. package/dist/API/src/routes/health.js +172 -0
  16. package/dist/API/src/routes/health.js.map +1 -0
  17. package/dist/API/src/routes/incidents.d.ts +7 -0
  18. package/dist/API/src/routes/incidents.d.ts.map +1 -0
  19. package/dist/API/src/routes/incidents.js +117 -0
  20. package/dist/API/src/routes/incidents.js.map +1 -0
  21. package/dist/API/src/routes/projects.d.ts +7 -0
  22. package/dist/API/src/routes/projects.d.ts.map +1 -0
  23. package/dist/API/src/routes/projects.js +398 -0
  24. package/dist/API/src/routes/projects.js.map +1 -0
  25. package/dist/API/src/routes/runners.d.ts +7 -0
  26. package/dist/API/src/routes/runners.d.ts.map +1 -0
  27. package/dist/API/src/routes/runners.js +242 -0
  28. package/dist/API/src/routes/runners.js.map +1 -0
  29. package/dist/API/src/routes/tasks.d.ts +7 -0
  30. package/dist/API/src/routes/tasks.d.ts.map +1 -0
  31. package/dist/API/src/routes/tasks.js +1007 -0
  32. package/dist/API/src/routes/tasks.js.map +1 -0
  33. package/dist/API/src/utils/validation.d.ts +22 -0
  34. package/dist/API/src/utils/validation.d.ts.map +1 -0
  35. package/dist/API/src/utils/validation.js +50 -0
  36. package/dist/API/src/utils/validation.js.map +1 -0
  37. package/dist/index.d.ts +10 -0
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +184 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/routes/activity.d.ts +7 -0
  42. package/dist/routes/activity.d.ts.map +1 -0
  43. package/dist/routes/activity.js +252 -0
  44. package/dist/routes/activity.js.map +1 -0
  45. package/dist/routes/config.d.ts +7 -0
  46. package/dist/routes/config.d.ts.map +1 -0
  47. package/dist/routes/config.js +647 -0
  48. package/dist/routes/config.js.map +1 -0
  49. package/dist/routes/credit-alerts.d.ts +2 -0
  50. package/dist/routes/credit-alerts.d.ts.map +1 -0
  51. package/dist/routes/credit-alerts.js +97 -0
  52. package/dist/routes/credit-alerts.js.map +1 -0
  53. package/dist/routes/health.d.ts +7 -0
  54. package/dist/routes/health.d.ts.map +1 -0
  55. package/dist/routes/health.js +200 -0
  56. package/dist/routes/health.js.map +1 -0
  57. package/dist/routes/incidents.d.ts +7 -0
  58. package/dist/routes/incidents.d.ts.map +1 -0
  59. package/dist/routes/incidents.js +117 -0
  60. package/dist/routes/incidents.js.map +1 -0
  61. package/dist/routes/projects.d.ts +7 -0
  62. package/dist/routes/projects.d.ts.map +1 -0
  63. package/dist/routes/projects.js +643 -0
  64. package/dist/routes/projects.js.map +1 -0
  65. package/dist/routes/runners.d.ts +7 -0
  66. package/dist/routes/runners.d.ts.map +1 -0
  67. package/dist/routes/runners.js +299 -0
  68. package/dist/routes/runners.js.map +1 -0
  69. package/dist/routes/skills.d.ts +3 -0
  70. package/dist/routes/skills.d.ts.map +1 -0
  71. package/dist/routes/skills.js +109 -0
  72. package/dist/routes/skills.js.map +1 -0
  73. package/dist/routes/storage.d.ts +7 -0
  74. package/dist/routes/storage.d.ts.map +1 -0
  75. package/dist/routes/storage.js +93 -0
  76. package/dist/routes/storage.js.map +1 -0
  77. package/dist/routes/tasks.d.ts +7 -0
  78. package/dist/routes/tasks.d.ts.map +1 -0
  79. package/dist/routes/tasks.js +1145 -0
  80. package/dist/routes/tasks.js.map +1 -0
  81. package/dist/src/cleanup/invocation-logs.d.ts +30 -0
  82. package/dist/src/cleanup/invocation-logs.d.ts.map +1 -0
  83. package/dist/src/cleanup/invocation-logs.js +66 -0
  84. package/dist/src/cleanup/invocation-logs.js.map +1 -0
  85. package/dist/src/commands/loop-phases.d.ts +11 -0
  86. package/dist/src/commands/loop-phases.d.ts.map +1 -0
  87. package/dist/src/commands/loop-phases.js +304 -0
  88. package/dist/src/commands/loop-phases.js.map +1 -0
  89. package/dist/src/config/loader.d.ts +160 -0
  90. package/dist/src/config/loader.d.ts.map +1 -0
  91. package/dist/src/config/loader.js +276 -0
  92. package/dist/src/config/loader.js.map +1 -0
  93. package/dist/src/database/connection.d.ts +35 -0
  94. package/dist/src/database/connection.d.ts.map +1 -0
  95. package/dist/src/database/connection.js +197 -0
  96. package/dist/src/database/connection.js.map +1 -0
  97. package/dist/src/database/queries.d.ts +220 -0
  98. package/dist/src/database/queries.d.ts.map +1 -0
  99. package/dist/src/database/queries.js +589 -0
  100. package/dist/src/database/queries.js.map +1 -0
  101. package/dist/src/database/schema.d.ts +8 -0
  102. package/dist/src/database/schema.d.ts.map +1 -0
  103. package/dist/src/database/schema.js +184 -0
  104. package/dist/src/database/schema.js.map +1 -0
  105. package/dist/src/git/push.d.ts +26 -0
  106. package/dist/src/git/push.d.ts.map +1 -0
  107. package/dist/src/git/push.js +91 -0
  108. package/dist/src/git/push.js.map +1 -0
  109. package/dist/src/git/status.d.ts +83 -0
  110. package/dist/src/git/status.d.ts.map +1 -0
  111. package/dist/src/git/status.js +315 -0
  112. package/dist/src/git/status.js.map +1 -0
  113. package/dist/src/health/stuck-task-detector.d.ts +131 -0
  114. package/dist/src/health/stuck-task-detector.d.ts.map +1 -0
  115. package/dist/src/health/stuck-task-detector.js +233 -0
  116. package/dist/src/health/stuck-task-detector.js.map +1 -0
  117. package/dist/src/health/stuck-task-recovery.d.ts +45 -0
  118. package/dist/src/health/stuck-task-recovery.d.ts.map +1 -0
  119. package/dist/src/health/stuck-task-recovery.js +309 -0
  120. package/dist/src/health/stuck-task-recovery.js.map +1 -0
  121. package/dist/src/index.d.ts +10 -0
  122. package/dist/src/index.d.ts.map +1 -0
  123. package/dist/src/index.js +130 -0
  124. package/dist/src/index.js.map +1 -0
  125. package/dist/src/locking/queries.d.ts +116 -0
  126. package/dist/src/locking/queries.d.ts.map +1 -0
  127. package/dist/src/locking/queries.js +232 -0
  128. package/dist/src/locking/queries.js.map +1 -0
  129. package/dist/src/locking/section-lock.d.ts +74 -0
  130. package/dist/src/locking/section-lock.d.ts.map +1 -0
  131. package/dist/src/locking/section-lock.js +196 -0
  132. package/dist/src/locking/section-lock.js.map +1 -0
  133. package/dist/src/locking/task-lock.d.ts +92 -0
  134. package/dist/src/locking/task-lock.d.ts.map +1 -0
  135. package/dist/src/locking/task-lock.js +233 -0
  136. package/dist/src/locking/task-lock.js.map +1 -0
  137. package/dist/src/migrations/index.d.ts +7 -0
  138. package/dist/src/migrations/index.d.ts.map +1 -0
  139. package/dist/src/migrations/index.js +9 -0
  140. package/dist/src/migrations/index.js.map +1 -0
  141. package/dist/src/migrations/manifest.d.ts +92 -0
  142. package/dist/src/migrations/manifest.d.ts.map +1 -0
  143. package/dist/src/migrations/manifest.js +255 -0
  144. package/dist/src/migrations/manifest.js.map +1 -0
  145. package/dist/src/migrations/runner.d.ts +84 -0
  146. package/dist/src/migrations/runner.d.ts.map +1 -0
  147. package/dist/src/migrations/runner.js +338 -0
  148. package/dist/src/migrations/runner.js.map +1 -0
  149. package/dist/src/orchestrator/coder.d.ts +32 -0
  150. package/dist/src/orchestrator/coder.d.ts.map +1 -0
  151. package/dist/src/orchestrator/coder.js +170 -0
  152. package/dist/src/orchestrator/coder.js.map +1 -0
  153. package/dist/src/orchestrator/coordinator.d.ts +28 -0
  154. package/dist/src/orchestrator/coordinator.d.ts.map +1 -0
  155. package/dist/src/orchestrator/coordinator.js +252 -0
  156. package/dist/src/orchestrator/coordinator.js.map +1 -0
  157. package/dist/src/orchestrator/fallback-handler.d.ts +24 -0
  158. package/dist/src/orchestrator/fallback-handler.d.ts.map +1 -0
  159. package/dist/src/orchestrator/fallback-handler.js +280 -0
  160. package/dist/src/orchestrator/fallback-handler.js.map +1 -0
  161. package/dist/src/orchestrator/invoke.d.ts +14 -0
  162. package/dist/src/orchestrator/invoke.d.ts.map +1 -0
  163. package/dist/src/orchestrator/invoke.js +76 -0
  164. package/dist/src/orchestrator/invoke.js.map +1 -0
  165. package/dist/src/orchestrator/post-coder.d.ts +10 -0
  166. package/dist/src/orchestrator/post-coder.d.ts.map +1 -0
  167. package/dist/src/orchestrator/post-coder.js +198 -0
  168. package/dist/src/orchestrator/post-coder.js.map +1 -0
  169. package/dist/src/orchestrator/post-reviewer.d.ts +10 -0
  170. package/dist/src/orchestrator/post-reviewer.d.ts.map +1 -0
  171. package/dist/src/orchestrator/post-reviewer.js +199 -0
  172. package/dist/src/orchestrator/post-reviewer.js.map +1 -0
  173. package/dist/src/orchestrator/reviewer.d.ts +35 -0
  174. package/dist/src/orchestrator/reviewer.d.ts.map +1 -0
  175. package/dist/src/orchestrator/reviewer.js +237 -0
  176. package/dist/src/orchestrator/reviewer.js.map +1 -0
  177. package/dist/src/orchestrator/schemas.d.ts +10 -0
  178. package/dist/src/orchestrator/schemas.d.ts.map +1 -0
  179. package/dist/src/orchestrator/schemas.js +81 -0
  180. package/dist/src/orchestrator/schemas.js.map +1 -0
  181. package/dist/src/orchestrator/task-selector.d.ts +102 -0
  182. package/dist/src/orchestrator/task-selector.d.ts.map +1 -0
  183. package/dist/src/orchestrator/task-selector.js +326 -0
  184. package/dist/src/orchestrator/task-selector.js.map +1 -0
  185. package/dist/src/orchestrator/types.d.ts +74 -0
  186. package/dist/src/orchestrator/types.d.ts.map +1 -0
  187. package/dist/src/orchestrator/types.js +5 -0
  188. package/dist/src/orchestrator/types.js.map +1 -0
  189. package/dist/src/prompts/coder.d.ts +36 -0
  190. package/dist/src/prompts/coder.d.ts.map +1 -0
  191. package/dist/src/prompts/coder.js +303 -0
  192. package/dist/src/prompts/coder.js.map +1 -0
  193. package/dist/src/prompts/prompt-helpers.d.ts +51 -0
  194. package/dist/src/prompts/prompt-helpers.d.ts.map +1 -0
  195. package/dist/src/prompts/prompt-helpers.js +299 -0
  196. package/dist/src/prompts/prompt-helpers.js.map +1 -0
  197. package/dist/src/prompts/reviewer.d.ts +40 -0
  198. package/dist/src/prompts/reviewer.d.ts.map +1 -0
  199. package/dist/src/prompts/reviewer.js +416 -0
  200. package/dist/src/prompts/reviewer.js.map +1 -0
  201. package/dist/src/providers/claude.d.ts +53 -0
  202. package/dist/src/providers/claude.d.ts.map +1 -0
  203. package/dist/src/providers/claude.js +227 -0
  204. package/dist/src/providers/claude.js.map +1 -0
  205. package/dist/src/providers/codex.d.ts +53 -0
  206. package/dist/src/providers/codex.d.ts.map +1 -0
  207. package/dist/src/providers/codex.js +253 -0
  208. package/dist/src/providers/codex.js.map +1 -0
  209. package/dist/src/providers/gemini.d.ts +58 -0
  210. package/dist/src/providers/gemini.d.ts.map +1 -0
  211. package/dist/src/providers/gemini.js +240 -0
  212. package/dist/src/providers/gemini.js.map +1 -0
  213. package/dist/src/providers/interface.d.ts +185 -0
  214. package/dist/src/providers/interface.d.ts.map +1 -0
  215. package/dist/src/providers/interface.js +92 -0
  216. package/dist/src/providers/interface.js.map +1 -0
  217. package/dist/src/providers/invocation-logger.d.ts +97 -0
  218. package/dist/src/providers/invocation-logger.d.ts.map +1 -0
  219. package/dist/src/providers/invocation-logger.js +378 -0
  220. package/dist/src/providers/invocation-logger.js.map +1 -0
  221. package/dist/src/providers/openai.d.ts +53 -0
  222. package/dist/src/providers/openai.d.ts.map +1 -0
  223. package/dist/src/providers/openai.js +230 -0
  224. package/dist/src/providers/openai.js.map +1 -0
  225. package/dist/src/providers/registry.d.ts +100 -0
  226. package/dist/src/providers/registry.d.ts.map +1 -0
  227. package/dist/src/providers/registry.js +170 -0
  228. package/dist/src/providers/registry.js.map +1 -0
  229. package/dist/src/routes/activity.d.ts +7 -0
  230. package/dist/src/routes/activity.d.ts.map +1 -0
  231. package/dist/src/routes/activity.js +252 -0
  232. package/dist/src/routes/activity.js.map +1 -0
  233. package/dist/src/routes/config.d.ts +7 -0
  234. package/dist/src/routes/config.d.ts.map +1 -0
  235. package/dist/src/routes/config.js +521 -0
  236. package/dist/src/routes/config.js.map +1 -0
  237. package/dist/src/routes/health.d.ts +7 -0
  238. package/dist/src/routes/health.d.ts.map +1 -0
  239. package/dist/src/routes/health.js +172 -0
  240. package/dist/src/routes/health.js.map +1 -0
  241. package/dist/src/routes/incidents.d.ts +7 -0
  242. package/dist/src/routes/incidents.d.ts.map +1 -0
  243. package/dist/src/routes/incidents.js +117 -0
  244. package/dist/src/routes/incidents.js.map +1 -0
  245. package/dist/src/routes/projects.d.ts +7 -0
  246. package/dist/src/routes/projects.d.ts.map +1 -0
  247. package/dist/src/routes/projects.js +398 -0
  248. package/dist/src/routes/projects.js.map +1 -0
  249. package/dist/src/routes/runners.d.ts +7 -0
  250. package/dist/src/routes/runners.d.ts.map +1 -0
  251. package/dist/src/routes/runners.js +242 -0
  252. package/dist/src/routes/runners.js.map +1 -0
  253. package/dist/src/routes/tasks.d.ts +7 -0
  254. package/dist/src/routes/tasks.d.ts.map +1 -0
  255. package/dist/src/routes/tasks.js +1007 -0
  256. package/dist/src/routes/tasks.js.map +1 -0
  257. package/dist/src/runners/activity-log.d.ts +65 -0
  258. package/dist/src/runners/activity-log.d.ts.map +1 -0
  259. package/dist/src/runners/activity-log.js +140 -0
  260. package/dist/src/runners/activity-log.js.map +1 -0
  261. package/dist/src/runners/cron.d.ts +30 -0
  262. package/dist/src/runners/cron.d.ts.map +1 -0
  263. package/dist/src/runners/cron.js +333 -0
  264. package/dist/src/runners/cron.js.map +1 -0
  265. package/dist/src/runners/daemon.d.ts +71 -0
  266. package/dist/src/runners/daemon.d.ts.map +1 -0
  267. package/dist/src/runners/daemon.js +233 -0
  268. package/dist/src/runners/daemon.js.map +1 -0
  269. package/dist/src/runners/global-db.d.ts +31 -0
  270. package/dist/src/runners/global-db.d.ts.map +1 -0
  271. package/dist/src/runners/global-db.js +220 -0
  272. package/dist/src/runners/global-db.js.map +1 -0
  273. package/dist/src/runners/hang-detector.d.ts +38 -0
  274. package/dist/src/runners/hang-detector.d.ts.map +1 -0
  275. package/dist/src/runners/hang-detector.js +130 -0
  276. package/dist/src/runners/hang-detector.js.map +1 -0
  277. package/dist/src/runners/heartbeat.d.ts +39 -0
  278. package/dist/src/runners/heartbeat.d.ts.map +1 -0
  279. package/dist/src/runners/heartbeat.js +71 -0
  280. package/dist/src/runners/heartbeat.js.map +1 -0
  281. package/dist/src/runners/lock.d.ts +47 -0
  282. package/dist/src/runners/lock.d.ts.map +1 -0
  283. package/dist/src/runners/lock.js +140 -0
  284. package/dist/src/runners/lock.js.map +1 -0
  285. package/dist/src/runners/orchestrator-loop.d.ts +20 -0
  286. package/dist/src/runners/orchestrator-loop.d.ts.map +1 -0
  287. package/dist/src/runners/orchestrator-loop.js +208 -0
  288. package/dist/src/runners/orchestrator-loop.js.map +1 -0
  289. package/dist/src/runners/projects.d.ts +96 -0
  290. package/dist/src/runners/projects.d.ts.map +1 -0
  291. package/dist/src/runners/projects.js +243 -0
  292. package/dist/src/runners/projects.js.map +1 -0
  293. package/dist/src/runners/wakeup.d.ts +37 -0
  294. package/dist/src/runners/wakeup.d.ts.map +1 -0
  295. package/dist/src/runners/wakeup.js +355 -0
  296. package/dist/src/runners/wakeup.js.map +1 -0
  297. package/dist/src/utils/validation.d.ts +22 -0
  298. package/dist/src/utils/validation.d.ts.map +1 -0
  299. package/dist/src/utils/validation.js +50 -0
  300. package/dist/src/utils/validation.js.map +1 -0
  301. package/dist/utils/sqlite.d.ts +17 -0
  302. package/dist/utils/sqlite.d.ts.map +1 -0
  303. package/dist/utils/sqlite.js +27 -0
  304. package/dist/utils/sqlite.js.map +1 -0
  305. package/dist/utils/storage-cache.d.ts +33 -0
  306. package/dist/utils/storage-cache.d.ts.map +1 -0
  307. package/dist/utils/storage-cache.js +81 -0
  308. package/dist/utils/storage-cache.js.map +1 -0
  309. package/dist/utils/validation.d.ts +22 -0
  310. package/dist/utils/validation.d.ts.map +1 -0
  311. package/dist/utils/validation.js +51 -0
  312. package/dist/utils/validation.js.map +1 -0
  313. package/package.json +39 -0
  314. package/src/index.ts +199 -0
  315. package/src/routes/activity.ts +302 -0
  316. package/src/routes/config.ts +723 -0
  317. package/src/routes/credit-alerts.ts +73 -0
  318. package/src/routes/health.ts +219 -0
  319. package/src/routes/incidents.ts +131 -0
  320. package/src/routes/projects.ts +854 -0
  321. package/src/routes/runners.ts +357 -0
  322. package/src/routes/skills.ts +127 -0
  323. package/src/routes/storage.ts +108 -0
  324. package/src/routes/tasks.ts +1372 -0
  325. package/src/utils/sqlite.ts +36 -0
  326. package/src/utils/storage-cache.ts +107 -0
  327. package/src/utils/validation.ts +61 -0
  328. package/tsconfig.json +20 -0
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Core stuck-task detection logic (detection only, no recovery).
3
+ *
4
+ * This implementation intentionally uses the project's current schemas:
5
+ * - Project DB: tasks, audit, task_invocations
6
+ * - Global DB: runners
7
+ *
8
+ * The design doc (docs/stuck-task-detection.md) describes additional fields/tables
9
+ * (incidents, invocation started/completed timestamps, last_tool_execution, etc.).
10
+ * Those are not present in the current repo schema, so we derive signals from
11
+ * existing timestamps (tasks.updated_at, task_invocations.created_at, runners.heartbeat_at).
12
+ */
13
+ const DEFAULTS = {
14
+ orphanedTaskTimeoutSec: 600,
15
+ maxCoderDurationSec: 1800,
16
+ maxReviewerDurationSec: 900,
17
+ runnerHeartbeatTimeoutSec: 300,
18
+ invocationStalenessSec: 600,
19
+ dbInconsistencyRecentUpdateSec: 60,
20
+ };
21
+ function isProcessAliveBestEffort(pid) {
22
+ try {
23
+ // Signal 0: existence check only.
24
+ process.kill(pid, 0);
25
+ return true;
26
+ }
27
+ catch {
28
+ return false;
29
+ }
30
+ }
31
+ /**
32
+ * Parse SQLite datetime('now') strings (YYYY-MM-DD HH:MM:SS) as UTC.
33
+ * Node's Date parser can treat "YYYY-MM-DD HH:MM:SS" as local time depending on runtime,
34
+ * so we normalize to ISO 8601 Zulu.
35
+ */
36
+ export function parseSqliteDateTimeUtc(value) {
37
+ // Already ISO? (e.g., 2026-02-10T13:45:00.000Z)
38
+ if (value.includes('T'))
39
+ return new Date(value);
40
+ // SQLite "YYYY-MM-DD HH:MM:SS" (UTC) => "YYYY-MM-DDTHH:MM:SSZ"
41
+ return new Date(value.replace(' ', 'T') + 'Z');
42
+ }
43
+ /**
44
+ * Format a Date as SQLite datetime('now')-compatible UTC string: YYYY-MM-DD HH:MM:SS
45
+ * This keeps comparisons lexicographically safe against stored SQLite timestamps.
46
+ */
47
+ export function formatSqliteDateTimeUtc(date) {
48
+ // Date#toISOString is always UTC; trim milliseconds and replace T with space.
49
+ return date.toISOString().slice(0, 19).replace('T', ' ');
50
+ }
51
+ function secondsBetween(a, b) {
52
+ return Math.max(0, Math.floor((a.getTime() - b.getTime()) / 1000));
53
+ }
54
+ function mergeConfig(config) {
55
+ return { ...DEFAULTS, ...(config ?? {}) };
56
+ }
57
+ export function detectStuckTasks(options) {
58
+ const now = options.now ?? new Date();
59
+ const cfg = mergeConfig(options.config);
60
+ const isPidAlive = options.isPidAlive ?? isProcessAliveBestEffort;
61
+ const zombieRunners = detectZombieRunnersInternal(options.globalDb, options.projectPath, cfg, now, isPidAlive);
62
+ const deadRunners = detectDeadRunnersInternal(options.globalDb, options.projectPath, cfg, now, isPidAlive);
63
+ const { orphanedTasks, hangingInvocations, dbInconsistencies } = detectTaskSignalsInternal(options.projectDb, options.globalDb, options.projectPath, cfg, now, isPidAlive);
64
+ return {
65
+ timestamp: now,
66
+ orphanedTasks,
67
+ hangingInvocations,
68
+ zombieRunners,
69
+ deadRunners,
70
+ dbInconsistencies,
71
+ };
72
+ }
73
+ function detectZombieRunnersInternal(globalDb, projectPath, cfg, now, isPidAlive) {
74
+ const cutoff = formatSqliteDateTimeUtc(new Date(now.getTime() - cfg.runnerHeartbeatTimeoutSec * 1000));
75
+ const rows = globalDb.prepare(`SELECT id, status, pid, project_path, current_task_id, heartbeat_at
76
+ FROM runners
77
+ WHERE status = 'running'
78
+ AND project_path = ?
79
+ AND heartbeat_at < ?`).all(projectPath, cutoff);
80
+ const result = [];
81
+ for (const row of rows) {
82
+ if (row.pid !== null && isPidAlive(row.pid)) {
83
+ const hb = parseSqliteDateTimeUtc(row.heartbeat_at);
84
+ result.push({
85
+ failureMode: 'zombie_runner',
86
+ runnerId: row.id,
87
+ pid: row.pid,
88
+ status: row.status,
89
+ projectPath: row.project_path,
90
+ currentTaskId: row.current_task_id,
91
+ heartbeatAt: hb,
92
+ secondsSinceHeartbeat: secondsBetween(now, hb),
93
+ });
94
+ }
95
+ }
96
+ return result;
97
+ }
98
+ function detectDeadRunnersInternal(globalDb, projectPath, cfg, now, isPidAlive) {
99
+ const rows = globalDb.prepare(`SELECT id, status, pid, project_path, current_task_id, heartbeat_at
100
+ FROM runners
101
+ WHERE status = 'running'
102
+ AND project_path = ?`).all(projectPath);
103
+ const result = [];
104
+ for (const row of rows) {
105
+ const alive = row.pid !== null && isPidAlive(row.pid);
106
+ if (!alive) {
107
+ const hb = parseSqliteDateTimeUtc(row.heartbeat_at);
108
+ result.push({
109
+ failureMode: 'dead_runner',
110
+ runnerId: row.id,
111
+ pid: row.pid,
112
+ status: row.status,
113
+ projectPath: row.project_path,
114
+ currentTaskId: row.current_task_id,
115
+ heartbeatAt: hb,
116
+ secondsSinceHeartbeat: secondsBetween(now, hb),
117
+ });
118
+ }
119
+ }
120
+ return result;
121
+ }
122
+ function detectTaskSignalsInternal(projectDb, globalDb, projectPath, cfg, now, isPidAlive) {
123
+ const orphanedTasks = [];
124
+ const hangingInvocations = [];
125
+ const dbInconsistencies = [];
126
+ // DB inconsistency (transient): in_progress, no invocations, very recently updated.
127
+ {
128
+ const cutoff = formatSqliteDateTimeUtc(new Date(now.getTime() - cfg.dbInconsistencyRecentUpdateSec * 1000));
129
+ const rows = projectDb.prepare(`SELECT t.id, t.title, t.status, t.updated_at
130
+ FROM tasks t
131
+ LEFT JOIN task_invocations i ON i.task_id = t.id AND i.role = 'coder'
132
+ WHERE t.status = 'in_progress'
133
+ GROUP BY t.id
134
+ HAVING COUNT(i.id) = 0
135
+ AND t.updated_at >= ?`).all(cutoff);
136
+ for (const row of rows) {
137
+ const updatedAt = parseSqliteDateTimeUtc(row.updated_at);
138
+ dbInconsistencies.push({
139
+ failureMode: 'db_inconsistency',
140
+ taskId: row.id,
141
+ title: row.title,
142
+ status: 'in_progress',
143
+ updatedAt,
144
+ secondsSinceUpdate: secondsBetween(now, updatedAt),
145
+ invocationCount: 0,
146
+ });
147
+ }
148
+ }
149
+ // Orphaned tasks: in_progress, stale updated_at, and no recent invocations, and no active runner assigned.
150
+ {
151
+ const taskCutoff = formatSqliteDateTimeUtc(new Date(now.getTime() - cfg.orphanedTaskTimeoutSec * 1000));
152
+ const invocationCutoff = formatSqliteDateTimeUtc(new Date(now.getTime() - cfg.invocationStalenessSec * 1000));
153
+ const rows = projectDb.prepare(`SELECT
154
+ t.id,
155
+ t.title,
156
+ t.status,
157
+ t.updated_at,
158
+ COUNT(i.id) as invocation_count,
159
+ MAX(i.created_at) as last_invocation_at
160
+ FROM tasks t
161
+ LEFT JOIN task_invocations i ON i.task_id = t.id AND i.role = 'coder'
162
+ WHERE t.status = 'in_progress'
163
+ AND t.updated_at < ?
164
+ GROUP BY t.id
165
+ HAVING COUNT(i.id) = 0
166
+ OR MAX(i.created_at) < ?`).all(taskCutoff, invocationCutoff);
167
+ for (const row of rows) {
168
+ const updatedAt = parseSqliteDateTimeUtc(row.updated_at);
169
+ const lastInvocationAt = row.last_invocation_at ? parseSqliteDateTimeUtc(row.last_invocation_at) : null;
170
+ const activeRunner = getActiveRunnerForTask(globalDb, projectPath, row.id, cfg, now, isPidAlive);
171
+ if (!activeRunner) {
172
+ orphanedTasks.push({
173
+ failureMode: 'orphaned_task',
174
+ taskId: row.id,
175
+ title: row.title,
176
+ status: 'in_progress',
177
+ updatedAt,
178
+ secondsSinceUpdate: secondsBetween(now, updatedAt),
179
+ invocationCount: row.invocation_count,
180
+ lastInvocationAt,
181
+ hasActiveRunner: false,
182
+ });
183
+ }
184
+ }
185
+ }
186
+ // Hanging invocations: in_progress/review for too long with an active runner currently executing the task.
187
+ // Approximates "invocation started but not completed" using task age + runner current_task_id.
188
+ {
189
+ const coderCutoff = formatSqliteDateTimeUtc(new Date(now.getTime() - cfg.maxCoderDurationSec * 1000));
190
+ const reviewerCutoff = formatSqliteDateTimeUtc(new Date(now.getTime() - cfg.maxReviewerDurationSec * 1000));
191
+ const rows = projectDb.prepare(`SELECT t.id, t.title, t.status, t.updated_at
192
+ FROM tasks t
193
+ WHERE (t.status = 'in_progress' AND t.updated_at < ?)
194
+ OR (t.status = 'review' AND t.updated_at < ?)`).all(coderCutoff, reviewerCutoff);
195
+ for (const row of rows) {
196
+ const activeRunner = getActiveRunnerForTask(globalDb, projectPath, row.id, cfg, now, isPidAlive);
197
+ if (!activeRunner)
198
+ continue;
199
+ const updatedAt = parseSqliteDateTimeUtc(row.updated_at);
200
+ const hbAt = parseSqliteDateTimeUtc(activeRunner.heartbeat_at);
201
+ hangingInvocations.push({
202
+ failureMode: 'hanging_invocation',
203
+ phase: row.status === 'review' ? 'reviewer' : 'coder',
204
+ taskId: row.id,
205
+ title: row.title,
206
+ status: row.status,
207
+ updatedAt,
208
+ secondsSinceUpdate: secondsBetween(now, updatedAt),
209
+ runnerId: activeRunner.id,
210
+ runnerPid: activeRunner.pid,
211
+ runnerHeartbeatAt: hbAt,
212
+ });
213
+ }
214
+ }
215
+ return { orphanedTasks, hangingInvocations, dbInconsistencies };
216
+ }
217
+ function getActiveRunnerForTask(globalDb, projectPath, taskId, cfg, now, isPidAlive) {
218
+ const cutoff = formatSqliteDateTimeUtc(new Date(now.getTime() - cfg.runnerHeartbeatTimeoutSec * 1000));
219
+ const row = globalDb.prepare(`SELECT id, pid, heartbeat_at
220
+ FROM runners
221
+ WHERE project_path = ?
222
+ AND current_task_id = ?
223
+ AND status = 'running'
224
+ AND heartbeat_at >= ?
225
+ ORDER BY heartbeat_at DESC
226
+ LIMIT 1`).get(projectPath, taskId, cutoff);
227
+ if (!row)
228
+ return null;
229
+ if (row.pid !== null && !isPidAlive(row.pid))
230
+ return null;
231
+ return row;
232
+ }
233
+ //# sourceMappingURL=stuck-task-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stuck-task-detector.js","sourceRoot":"","sources":["../../../../src/health/stuck-task-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAgIH,MAAM,QAAQ,GAAuC;IACnD,sBAAsB,EAAE,GAAG;IAC3B,mBAAmB,EAAE,IAAI;IACzB,sBAAsB,EAAE,GAAG;IAC3B,yBAAyB,EAAE,GAAG;IAC9B,sBAAsB,EAAE,GAAG;IAC3B,8BAA8B,EAAE,EAAE;CACnC,CAAC;AAEF,SAAS,wBAAwB,CAAC,GAAW;IAC3C,IAAI,CAAC;QACH,kCAAkC;QAClC,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,gDAAgD;IAChD,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,+DAA+D;IAC/D,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAU;IAChD,8EAA8E;IAC9E,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,cAAc,CAAC,CAAO,EAAE,CAAO;IACtC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,SAAS,WAAW,CAAC,MAAiC;IACpD,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAAgC;IAC/D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,IAAI,EAAE,CAAC;IACtC,MAAM,GAAG,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,wBAAwB,CAAC;IAElE,MAAM,aAAa,GAAG,2BAA2B,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,WAAW,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IAC/G,MAAM,WAAW,GAAG,yBAAyB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,WAAW,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;IAC3G,MAAM,EAAE,aAAa,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,GAAG,yBAAyB,CACxF,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,WAAW,EACnB,GAAG,EACH,GAAG,EACH,UAAU,CACX,CAAC;IAEF,OAAO;QACL,SAAS,EAAE,GAAG;QACd,aAAa;QACb,kBAAkB;QAClB,aAAa;QACb,WAAW;QACX,iBAAiB;KAClB,CAAC;AACJ,CAAC;AAED,SAAS,2BAA2B,CAClC,QAA2B,EAC3B,WAAmB,EACnB,GAAuC,EACvC,GAAS,EACT,UAAoC;IAEpC,MAAM,MAAM,GAAG,uBAAuB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAAC,CAAC;IACvG,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAC3B;;;;4BAIwB,CACzB,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,CAOvB,CAAC;IAEH,MAAM,MAAM,GAAyB,EAAE,CAAC;IACxC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5C,MAAM,EAAE,GAAG,sBAAsB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YACpD,MAAM,CAAC,IAAI,CAAC;gBACV,WAAW,EAAE,eAAe;gBAC5B,QAAQ,EAAE,GAAG,CAAC,EAAE;gBAChB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,WAAW,EAAE,GAAG,CAAC,YAAY;gBAC7B,aAAa,EAAE,GAAG,CAAC,eAAe;gBAClC,WAAW,EAAE,EAAE;gBACf,qBAAqB,EAAE,cAAc,CAAC,GAAG,EAAE,EAAE,CAAC;aAC/C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,yBAAyB,CAChC,QAA2B,EAC3B,WAAmB,EACnB,GAAuC,EACvC,GAAS,EACT,UAAoC;IAEpC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAC3B;;;4BAGwB,CACzB,CAAC,GAAG,CAAC,WAAW,CAOf,CAAC;IAEH,MAAM,MAAM,GAAuB,EAAE,CAAC;IACtC,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,KAAK,IAAI,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACtD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,EAAE,GAAG,sBAAsB,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YACpD,MAAM,CAAC,IAAI,CAAC;gBACV,WAAW,EAAE,aAAa;gBAC1B,QAAQ,EAAE,GAAG,CAAC,EAAE;gBAChB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,WAAW,EAAE,GAAG,CAAC,YAAY;gBAC7B,aAAa,EAAE,GAAG,CAAC,eAAe;gBAClC,WAAW,EAAE,EAAE;gBACf,qBAAqB,EAAE,cAAc,CAAC,GAAG,EAAE,EAAE,CAAC;aAC/C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,yBAAyB,CAChC,SAA4B,EAC5B,QAA2B,EAC3B,WAAmB,EACnB,GAAuC,EACvC,GAAS,EACT,UAAoC;IAMpC,MAAM,aAAa,GAAyB,EAAE,CAAC;IAC/C,MAAM,kBAAkB,GAAwB,EAAE,CAAC;IACnD,MAAM,iBAAiB,GAA4B,EAAE,CAAC;IAEtD,oFAAoF;IACpF,CAAC;QACC,MAAM,MAAM,GAAG,uBAAuB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAAC,CAAC;QAC5G,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAC5B;;;;;;gCAM0B,CAC3B,CAAC,GAAG,CAAC,MAAM,CAAoF,CAAC;QAEjG,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,SAAS,GAAG,sBAAsB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACzD,iBAAiB,CAAC,IAAI,CAAC;gBACrB,WAAW,EAAE,kBAAkB;gBAC/B,MAAM,EAAE,GAAG,CAAC,EAAE;gBACd,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,MAAM,EAAE,aAAa;gBACrB,SAAS;gBACT,kBAAkB,EAAE,cAAc,CAAC,GAAG,EAAE,SAAS,CAAC;gBAClD,eAAe,EAAE,CAAC;aACnB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,2GAA2G;IAC3G,CAAC;QACC,MAAM,UAAU,GAAG,uBAAuB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC,CAAC;QACxG,MAAM,gBAAgB,GAAG,uBAAuB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC,CAAC;QAE9G,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAC5B;;;;;;;;;;;;;oCAa8B,CAC/B,CAAC,GAAG,CAAC,UAAU,EAAE,gBAAgB,CAOhC,CAAC;QAEH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,SAAS,GAAG,sBAAsB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACzD,MAAM,gBAAgB,GAAG,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,sBAAsB,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YACxG,MAAM,YAAY,GAAG,sBAAsB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;YAEjG,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,aAAa,CAAC,IAAI,CAAC;oBACjB,WAAW,EAAE,eAAe;oBAC5B,MAAM,EAAE,GAAG,CAAC,EAAE;oBACd,KAAK,EAAE,GAAG,CAAC,KAAK;oBAChB,MAAM,EAAE,aAAa;oBACrB,SAAS;oBACT,kBAAkB,EAAE,cAAc,CAAC,GAAG,EAAE,SAAS,CAAC;oBAClD,eAAe,EAAE,GAAG,CAAC,gBAAgB;oBACrC,gBAAgB;oBAChB,eAAe,EAAE,KAAK;iBACvB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,2GAA2G;IAC3G,+FAA+F;IAC/F,CAAC;QACC,MAAM,WAAW,GAAG,uBAAuB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC,CAAC;QACtG,MAAM,cAAc,GAAG,uBAAuB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC,CAAC;QAE5G,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAC5B;;;wDAGkD,CACnD,CAAC,GAAG,CAAC,WAAW,EAAE,cAAc,CAA+F,CAAC;QAEjI,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,YAAY,GAAG,sBAAsB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;YACjG,IAAI,CAAC,YAAY;gBAAE,SAAS;YAE5B,MAAM,SAAS,GAAG,sBAAsB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACzD,MAAM,IAAI,GAAG,sBAAsB,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;YAE/D,kBAAkB,CAAC,IAAI,CAAC;gBACtB,WAAW,EAAE,oBAAoB;gBACjC,KAAK,EAAE,GAAG,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO;gBACrD,MAAM,EAAE,GAAG,CAAC,EAAE;gBACd,KAAK,EAAE,GAAG,CAAC,KAAK;gBAChB,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,SAAS;gBACT,kBAAkB,EAAE,cAAc,CAAC,GAAG,EAAE,SAAS,CAAC;gBAClD,QAAQ,EAAE,YAAY,CAAC,EAAE;gBACzB,SAAS,EAAE,YAAY,CAAC,GAAG;gBAC3B,iBAAiB,EAAE,IAAI;aACxB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,CAAC;AAClE,CAAC;AAED,SAAS,sBAAsB,CAC7B,QAA2B,EAC3B,WAAmB,EACnB,MAAc,EACd,GAAuC,EACvC,GAAS,EACT,UAAoC;IAEpC,MAAM,MAAM,GAAG,uBAAuB,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAAC,CAAC;IAEvG,MAAM,GAAG,GAAG,QAAQ,CAAC,OAAO,CAC1B;;;;;;;aAOS,CACV,CAAC,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,CAAyE,CAAC;IAE3G,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,IAAI,GAAG,CAAC,GAAG,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1D,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Automatic recovery actions for stuck tasks.
3
+ *
4
+ * This builds on detectStuckTasks() and applies a conservative set of actions:
5
+ * - orphaned_task: reset task to pending, release any task lock, increment failure_count, log incident
6
+ * - hanging_invocation: kill runner process, remove runner row, reset task (or skip after repeated failures)
7
+ * - zombie_runner/dead_runner: stop runner (best-effort) and reset its current_task_id (if any)
8
+ *
9
+ * Note: The repo intentionally approximates "invocation started/completed" using tasks.updated_at,
10
+ * task_invocations.created_at, and runners.heartbeat_at. Recovery follows those same signals.
11
+ */
12
+ import type Database from 'better-sqlite3';
13
+ import type { SteroidsConfig } from '../config/loader.js';
14
+ import { type StuckTaskDetectionConfig, type StuckTaskDetectionReport } from './stuck-task-detector.js';
15
+ export interface StuckTaskRecoveryConfig extends StuckTaskDetectionConfig {
16
+ autoRecover?: boolean;
17
+ maxRecoveryAttempts?: number;
18
+ maxIncidentsPerHour?: number;
19
+ killGraceMs?: number;
20
+ }
21
+ export type RecoveryResolution = 'auto_restart' | 'skipped' | 'escalated' | 'none';
22
+ export interface RecoveryAction {
23
+ kind: 'task' | 'runner';
24
+ targetId: string;
25
+ failureMode: string;
26
+ resolution: RecoveryResolution;
27
+ reason: string;
28
+ }
29
+ export interface RecoverStuckTasksOptions {
30
+ projectPath: string;
31
+ projectDb: Database.Database;
32
+ globalDb: Database.Database;
33
+ config?: SteroidsConfig;
34
+ now?: Date;
35
+ dryRun?: boolean;
36
+ isPidAlive?: (pid: number) => boolean;
37
+ killPid?: (pid: number, graceMs: number) => Promise<boolean>;
38
+ }
39
+ export interface RecoverStuckTasksResult {
40
+ report: StuckTaskDetectionReport;
41
+ actions: RecoveryAction[];
42
+ skippedDueToSafetyLimit: boolean;
43
+ }
44
+ export declare function recoverStuckTasks(options: RecoverStuckTasksOptions): Promise<RecoverStuckTasksResult>;
45
+ //# sourceMappingURL=stuck-task-recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stuck-task-recovery.d.ts","sourceRoot":"","sources":["../../../../src/health/stuck-task-recovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,QAAQ,MAAM,gBAAgB,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAG1D,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAK9B,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,uBAAwB,SAAQ,wBAAwB;IACvE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,kBAAkB,GAAG,cAAc,GAAG,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;AAEnF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,GAAG,QAAQ,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,kBAAkB,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,wBAAwB;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC;IAC7B,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC;IAC5B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,GAAG,CAAC,EAAE,IAAI,CAAC;IACX,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,wBAAwB,CAAC;IACjC,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,uBAAuB,EAAE,OAAO,CAAC;CAClC;AAuTD,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAqD3G"}
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Automatic recovery actions for stuck tasks.
3
+ *
4
+ * This builds on detectStuckTasks() and applies a conservative set of actions:
5
+ * - orphaned_task: reset task to pending, release any task lock, increment failure_count, log incident
6
+ * - hanging_invocation: kill runner process, remove runner row, reset task (or skip after repeated failures)
7
+ * - zombie_runner/dead_runner: stop runner (best-effort) and reset its current_task_id (if any)
8
+ *
9
+ * Note: The repo intentionally approximates "invocation started/completed" using tasks.updated_at,
10
+ * task_invocations.created_at, and runners.heartbeat_at. Recovery follows those same signals.
11
+ */
12
+ import { v4 as uuidv4 } from 'uuid';
13
+ import { updateTaskStatus } from '../database/queries.js';
14
+ import { forceReleaseTaskLock } from '../locking/queries.js';
15
+ import { detectStuckTasks, } from './stuck-task-detector.js';
16
+ const DEFAULTS = {
17
+ autoRecover: true,
18
+ maxRecoveryAttempts: 3,
19
+ maxIncidentsPerHour: 10,
20
+ killGraceMs: 10_000,
21
+ };
22
+ function delay(ms) {
23
+ return new Promise((resolve) => setTimeout(resolve, ms));
24
+ }
25
+ function isPidAliveBestEffort(pid) {
26
+ try {
27
+ process.kill(pid, 0);
28
+ return true;
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ async function killPidBestEffort(pid, graceMs, isAlive) {
35
+ try {
36
+ process.kill(pid, 'SIGTERM');
37
+ }
38
+ catch {
39
+ return false;
40
+ }
41
+ const start = Date.now();
42
+ while (Date.now() - start < graceMs) {
43
+ if (!isAlive(pid))
44
+ return true;
45
+ await delay(250);
46
+ }
47
+ try {
48
+ process.kill(pid, 'SIGKILL');
49
+ }
50
+ catch {
51
+ // ignore
52
+ }
53
+ return !isAlive(pid);
54
+ }
55
+ function readRecoveryConfig(config) {
56
+ const h = config?.health ?? {};
57
+ return {
58
+ orphanedTaskTimeoutSec: h.orphanedTaskTimeout ?? 600,
59
+ maxCoderDurationSec: h.maxCoderDuration ?? 1800,
60
+ maxReviewerDurationSec: h.maxReviewerDuration ?? 900,
61
+ runnerHeartbeatTimeoutSec: h.runnerHeartbeatTimeout ?? 300,
62
+ invocationStalenessSec: h.invocationStaleness ?? 600,
63
+ dbInconsistencyRecentUpdateSec: 60,
64
+ autoRecover: h.autoRecover ?? DEFAULTS.autoRecover,
65
+ maxRecoveryAttempts: h.maxRecoveryAttempts ?? DEFAULTS.maxRecoveryAttempts,
66
+ maxIncidentsPerHour: h.maxIncidentsPerHour ?? DEFAULTS.maxIncidentsPerHour,
67
+ killGraceMs: DEFAULTS.killGraceMs,
68
+ };
69
+ }
70
+ function incidentsInLastHour(projectDb) {
71
+ try {
72
+ const row = projectDb
73
+ .prepare(`SELECT COUNT(*) as count FROM incidents WHERE detected_at >= datetime('now', '-1 hour')`)
74
+ .get();
75
+ return row?.count ?? 0;
76
+ }
77
+ catch {
78
+ return 0;
79
+ }
80
+ }
81
+ function insertIncident(projectDb, args) {
82
+ try {
83
+ const id = uuidv4();
84
+ const details = args.details === undefined ? null : JSON.stringify(args.details);
85
+ projectDb
86
+ .prepare(`INSERT INTO incidents (id, task_id, runner_id, failure_mode, detected_at, resolved_at, resolution, details)
87
+ VALUES (?, ?, ?, ?, datetime('now'), datetime('now'), ?, ?)`)
88
+ .run(id, args.taskId ?? null, args.runnerId ?? null, args.failureMode, args.resolution, details);
89
+ }
90
+ catch {
91
+ // Best-effort: incidents table may not exist if migrations are disabled.
92
+ }
93
+ }
94
+ function getFailureCount(projectDb, taskId) {
95
+ try {
96
+ const row = projectDb.prepare('SELECT failure_count as c FROM tasks WHERE id = ?').get(taskId);
97
+ return row?.c ?? 0;
98
+ }
99
+ catch {
100
+ return 0;
101
+ }
102
+ }
103
+ function bumpFailureCount(projectDb, taskId) {
104
+ try {
105
+ projectDb
106
+ .prepare(`UPDATE tasks
107
+ SET failure_count = COALESCE(failure_count, 0) + 1,
108
+ last_failure_at = datetime('now')
109
+ WHERE id = ?`)
110
+ .run(taskId);
111
+ }
112
+ catch {
113
+ // ignore
114
+ }
115
+ }
116
+ function deleteRunnerRow(globalDb, runnerId) {
117
+ try {
118
+ globalDb.prepare('DELETE FROM runners WHERE id = ?').run(runnerId);
119
+ }
120
+ catch {
121
+ // ignore
122
+ }
123
+ }
124
+ function recoverOrphanedTask(projectDb, signal, cfg, dryRun) {
125
+ const currentFailures = getFailureCount(projectDb, signal.taskId);
126
+ const nextFailures = currentFailures + 1;
127
+ const shouldEscalate = nextFailures >= cfg.maxRecoveryAttempts;
128
+ const resolution = shouldEscalate ? 'skipped' : 'auto_restart';
129
+ const newStatus = shouldEscalate ? 'skipped' : 'pending';
130
+ if (!dryRun) {
131
+ const tx = projectDb.transaction(() => {
132
+ forceReleaseTaskLock(projectDb, signal.taskId);
133
+ updateTaskStatus(projectDb, signal.taskId, newStatus, 'system:stuck-task-recovery', shouldEscalate
134
+ ? `Auto-recovery escalated after ${nextFailures} failure(s): orphaned task`
135
+ : `Auto-recovery: orphaned task reset to pending`);
136
+ bumpFailureCount(projectDb, signal.taskId);
137
+ insertIncident(projectDb, {
138
+ taskId: signal.taskId,
139
+ failureMode: 'orphaned_task',
140
+ resolution,
141
+ details: {
142
+ secondsSinceUpdate: signal.secondsSinceUpdate,
143
+ invocationCount: signal.invocationCount,
144
+ lastInvocationAt: signal.lastInvocationAt?.toISOString() ?? null,
145
+ },
146
+ });
147
+ });
148
+ tx();
149
+ }
150
+ return {
151
+ kind: 'task',
152
+ targetId: signal.taskId,
153
+ failureMode: 'orphaned_task',
154
+ resolution,
155
+ reason: shouldEscalate
156
+ ? `orphaned task escalated (failure_count ${nextFailures})`
157
+ : `orphaned task reset to pending (failure_count ${nextFailures})`,
158
+ };
159
+ }
160
+ async function recoverHangingInvocation(projectDb, globalDb, signal, cfg, dryRun, killPid) {
161
+ const currentFailures = getFailureCount(projectDb, signal.taskId);
162
+ const nextFailures = currentFailures + 1;
163
+ const shouldEscalate = nextFailures >= cfg.maxRecoveryAttempts;
164
+ const resolution = shouldEscalate ? 'skipped' : 'auto_restart';
165
+ const newStatus = shouldEscalate ? 'skipped' : 'pending';
166
+ if (!dryRun) {
167
+ // Stop the runner first (best-effort), then reset the task.
168
+ if (signal.runnerPid !== null) {
169
+ await killPid(signal.runnerPid, cfg.killGraceMs ?? DEFAULTS.killGraceMs);
170
+ }
171
+ deleteRunnerRow(globalDb, signal.runnerId);
172
+ const tx = projectDb.transaction(() => {
173
+ forceReleaseTaskLock(projectDb, signal.taskId);
174
+ updateTaskStatus(projectDb, signal.taskId, newStatus, 'system:stuck-task-recovery', shouldEscalate
175
+ ? `Auto-recovery escalated after ${nextFailures} failure(s): hanging ${signal.phase}`
176
+ : `Auto-recovery: killed hanging ${signal.phase}, reset to pending`);
177
+ bumpFailureCount(projectDb, signal.taskId);
178
+ insertIncident(projectDb, {
179
+ taskId: signal.taskId,
180
+ runnerId: signal.runnerId,
181
+ failureMode: 'hanging_invocation',
182
+ resolution,
183
+ details: {
184
+ phase: signal.phase,
185
+ secondsSinceUpdate: signal.secondsSinceUpdate,
186
+ runnerPid: signal.runnerPid,
187
+ runnerHeartbeatAt: signal.runnerHeartbeatAt.toISOString(),
188
+ },
189
+ });
190
+ });
191
+ tx();
192
+ }
193
+ return {
194
+ kind: 'task',
195
+ targetId: signal.taskId,
196
+ failureMode: 'hanging_invocation',
197
+ resolution,
198
+ reason: shouldEscalate
199
+ ? `hanging ${signal.phase} escalated (failure_count ${nextFailures})`
200
+ : `killed hanging ${signal.phase}, reset to pending (failure_count ${nextFailures})`,
201
+ };
202
+ }
203
+ async function recoverZombieOrDeadRunner(projectDb, globalDb, signal, dryRun, killPid) {
204
+ if (!dryRun) {
205
+ if (signal.failureMode === 'zombie_runner' && signal.pid !== null) {
206
+ await killPid(signal.pid, DEFAULTS.killGraceMs);
207
+ }
208
+ // Reset the runner's current task (if any) so the project can continue.
209
+ if (signal.currentTaskId) {
210
+ const taskExists = (() => {
211
+ try {
212
+ const row = projectDb.prepare('SELECT 1 FROM tasks WHERE id = ?').get(signal.currentTaskId);
213
+ return row !== undefined;
214
+ }
215
+ catch {
216
+ return false;
217
+ }
218
+ })();
219
+ const tx = projectDb.transaction(() => {
220
+ forceReleaseTaskLock(projectDb, signal.currentTaskId);
221
+ if (taskExists) {
222
+ updateTaskStatus(projectDb, signal.currentTaskId, 'pending', 'system:stuck-task-recovery', `Auto-recovery: ${signal.failureMode} cleared runner and reset task to pending`);
223
+ bumpFailureCount(projectDb, signal.currentTaskId);
224
+ }
225
+ insertIncident(projectDb, {
226
+ taskId: signal.currentTaskId,
227
+ runnerId: signal.runnerId,
228
+ failureMode: signal.failureMode,
229
+ resolution: 'auto_restart',
230
+ details: {
231
+ runnerPid: signal.pid,
232
+ secondsSinceHeartbeat: signal.secondsSinceHeartbeat,
233
+ taskExists,
234
+ },
235
+ });
236
+ });
237
+ tx();
238
+ }
239
+ else {
240
+ insertIncident(projectDb, {
241
+ runnerId: signal.runnerId,
242
+ failureMode: signal.failureMode,
243
+ resolution: 'auto_restart',
244
+ details: {
245
+ runnerPid: signal.pid,
246
+ secondsSinceHeartbeat: signal.secondsSinceHeartbeat,
247
+ },
248
+ });
249
+ }
250
+ deleteRunnerRow(globalDb, signal.runnerId);
251
+ }
252
+ return {
253
+ kind: 'runner',
254
+ targetId: signal.runnerId,
255
+ failureMode: signal.failureMode,
256
+ resolution: 'auto_restart',
257
+ reason: `${signal.failureMode} runner removed`,
258
+ };
259
+ }
260
+ export async function recoverStuckTasks(options) {
261
+ const cfg = readRecoveryConfig(options.config);
262
+ const now = options.now ?? new Date();
263
+ const dryRun = options.dryRun ?? false;
264
+ const isPidAlive = options.isPidAlive ?? isPidAliveBestEffort;
265
+ const killPid = options.killPid ??
266
+ ((pid, graceMs) => killPidBestEffort(pid, graceMs, isPidAlive));
267
+ const report = detectStuckTasks({
268
+ projectPath: options.projectPath,
269
+ projectDb: options.projectDb,
270
+ globalDb: options.globalDb,
271
+ now,
272
+ isPidAlive,
273
+ config: cfg,
274
+ });
275
+ const actions = [];
276
+ const handledTaskIds = new Set();
277
+ // Safety limit: too many incidents per hour => stop auto-recovery.
278
+ const recentIncidents = incidentsInLastHour(options.projectDb);
279
+ if ((cfg.maxIncidentsPerHour ?? DEFAULTS.maxIncidentsPerHour) > 0 && recentIncidents >= (cfg.maxIncidentsPerHour ?? DEFAULTS.maxIncidentsPerHour)) {
280
+ return { report, actions, skippedDueToSafetyLimit: true };
281
+ }
282
+ if (!cfg.autoRecover) {
283
+ return { report, actions, skippedDueToSafetyLimit: false };
284
+ }
285
+ // Runner-level recovery first, to avoid repeatedly detecting "hanging" with a zombie/dead runner.
286
+ for (const r of report.zombieRunners) {
287
+ actions.push(await recoverZombieOrDeadRunner(options.projectDb, options.globalDb, r, dryRun, killPid));
288
+ if (r.currentTaskId)
289
+ handledTaskIds.add(r.currentTaskId);
290
+ }
291
+ for (const r of report.deadRunners) {
292
+ actions.push(await recoverZombieOrDeadRunner(options.projectDb, options.globalDb, r, dryRun, killPid));
293
+ if (r.currentTaskId)
294
+ handledTaskIds.add(r.currentTaskId);
295
+ }
296
+ // Task-level recovery.
297
+ for (const t of report.orphanedTasks) {
298
+ if (handledTaskIds.has(t.taskId))
299
+ continue;
300
+ actions.push(recoverOrphanedTask(options.projectDb, t, cfg, dryRun));
301
+ }
302
+ for (const h of report.hangingInvocations) {
303
+ if (handledTaskIds.has(h.taskId))
304
+ continue;
305
+ actions.push(await recoverHangingInvocation(options.projectDb, options.globalDb, h, cfg, dryRun, killPid));
306
+ }
307
+ return { report, actions, skippedDueToSafetyLimit: false };
308
+ }
309
+ //# sourceMappingURL=stuck-task-recovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stuck-task-recovery.js","sourceRoot":"","sources":["../../../../src/health/stuck-task-recovery.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Steroids API Server
3
+ * REST API for multi-project monitoring and management
4
+ */
5
+ import express from 'express';
6
+ export declare function createApp(): express.Express;
7
+ declare const app: express.Express;
8
+ export declare function startServer(): void;
9
+ export default app;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,OAAO,MAAM,SAAS,CAAC;AAgB9B,wBAAgB,SAAS,IAAI,OAAO,CAAC,OAAO,CAyF3C;AAED,QAAA,MAAM,GAAG,iBAAc,CAAC;AAExB,wBAAgB,WAAW,IAAI,IAAI,CAOlC;AAqBD,eAAe,GAAG,CAAC"}