@telora/daemon 0.12.33

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 (473) hide show
  1. package/.env.example +64 -0
  2. package/README.md +229 -0
  3. package/build-info.json +4 -0
  4. package/dist/activity-tracker.d.ts +13 -0
  5. package/dist/activity-tracker.d.ts.map +1 -0
  6. package/dist/activity-tracker.js +19 -0
  7. package/dist/activity-tracker.js.map +1 -0
  8. package/dist/agent-state.d.ts +45 -0
  9. package/dist/agent-state.d.ts.map +1 -0
  10. package/dist/agent-state.js +61 -0
  11. package/dist/agent-state.js.map +1 -0
  12. package/dist/audit-hooks.d.ts +12 -0
  13. package/dist/audit-hooks.d.ts.map +1 -0
  14. package/dist/audit-hooks.js +45 -0
  15. package/dist/audit-hooks.js.map +1 -0
  16. package/dist/auto-update.d.ts +42 -0
  17. package/dist/auto-update.d.ts.map +1 -0
  18. package/dist/auto-update.js +96 -0
  19. package/dist/auto-update.js.map +1 -0
  20. package/dist/branch-status.d.ts +40 -0
  21. package/dist/branch-status.d.ts.map +1 -0
  22. package/dist/branch-status.js +107 -0
  23. package/dist/branch-status.js.map +1 -0
  24. package/dist/completion-detector.d.ts +87 -0
  25. package/dist/completion-detector.d.ts.map +1 -0
  26. package/dist/completion-detector.js +160 -0
  27. package/dist/completion-detector.js.map +1 -0
  28. package/dist/completion-handler.d.ts +48 -0
  29. package/dist/completion-handler.d.ts.map +1 -0
  30. package/dist/completion-handler.js +200 -0
  31. package/dist/completion-handler.js.map +1 -0
  32. package/dist/condition-evaluators.d.ts +31 -0
  33. package/dist/condition-evaluators.d.ts.map +1 -0
  34. package/dist/condition-evaluators.js +416 -0
  35. package/dist/condition-evaluators.js.map +1 -0
  36. package/dist/config.d.ts +55 -0
  37. package/dist/config.d.ts.map +1 -0
  38. package/dist/config.js +311 -0
  39. package/dist/config.js.map +1 -0
  40. package/dist/control-state.d.ts +41 -0
  41. package/dist/control-state.d.ts.map +1 -0
  42. package/dist/control-state.js +204 -0
  43. package/dist/control-state.js.map +1 -0
  44. package/dist/crash-recovery-cleanup.d.ts +21 -0
  45. package/dist/crash-recovery-cleanup.d.ts.map +1 -0
  46. package/dist/crash-recovery-cleanup.js +198 -0
  47. package/dist/crash-recovery-cleanup.js.map +1 -0
  48. package/dist/crash-recovery-scan.d.ts +19 -0
  49. package/dist/crash-recovery-scan.d.ts.map +1 -0
  50. package/dist/crash-recovery-scan.js +145 -0
  51. package/dist/crash-recovery-scan.js.map +1 -0
  52. package/dist/crash-recovery-types.d.ts +54 -0
  53. package/dist/crash-recovery-types.d.ts.map +1 -0
  54. package/dist/crash-recovery-types.js +13 -0
  55. package/dist/crash-recovery-types.js.map +1 -0
  56. package/dist/crash-recovery.d.ts +88 -0
  57. package/dist/crash-recovery.d.ts.map +1 -0
  58. package/dist/crash-recovery.js +448 -0
  59. package/dist/crash-recovery.js.map +1 -0
  60. package/dist/daemon-logs.d.ts +19 -0
  61. package/dist/daemon-logs.d.ts.map +1 -0
  62. package/dist/daemon-logs.js +81 -0
  63. package/dist/daemon-logs.js.map +1 -0
  64. package/dist/daemon-process.d.ts +154 -0
  65. package/dist/daemon-process.d.ts.map +1 -0
  66. package/dist/daemon-process.js +427 -0
  67. package/dist/daemon-process.js.map +1 -0
  68. package/dist/dag-validator.d.ts +52 -0
  69. package/dist/dag-validator.d.ts.map +1 -0
  70. package/dist/dag-validator.js +199 -0
  71. package/dist/dag-validator.js.map +1 -0
  72. package/dist/delivery-guards.d.ts +41 -0
  73. package/dist/delivery-guards.d.ts.map +1 -0
  74. package/dist/delivery-guards.js +195 -0
  75. package/dist/delivery-guards.js.map +1 -0
  76. package/dist/delivery-lifecycle.d.ts +110 -0
  77. package/dist/delivery-lifecycle.d.ts.map +1 -0
  78. package/dist/delivery-lifecycle.js +353 -0
  79. package/dist/delivery-lifecycle.js.map +1 -0
  80. package/dist/delivery-merge.d.ts +17 -0
  81. package/dist/delivery-merge.d.ts.map +1 -0
  82. package/dist/delivery-merge.js +89 -0
  83. package/dist/delivery-merge.js.map +1 -0
  84. package/dist/dependency-resolver.d.ts +77 -0
  85. package/dist/dependency-resolver.d.ts.map +1 -0
  86. package/dist/dependency-resolver.js +337 -0
  87. package/dist/dependency-resolver.js.map +1 -0
  88. package/dist/evaluation-context.d.ts +49 -0
  89. package/dist/evaluation-context.d.ts.map +1 -0
  90. package/dist/evaluation-context.js +98 -0
  91. package/dist/evaluation-context.js.map +1 -0
  92. package/dist/git-activity.d.ts +24 -0
  93. package/dist/git-activity.d.ts.map +1 -0
  94. package/dist/git-activity.js +97 -0
  95. package/dist/git-activity.js.map +1 -0
  96. package/dist/git-branch.d.ts +33 -0
  97. package/dist/git-branch.d.ts.map +1 -0
  98. package/dist/git-branch.js +88 -0
  99. package/dist/git-branch.js.map +1 -0
  100. package/dist/git-integration.d.ts +27 -0
  101. package/dist/git-integration.d.ts.map +1 -0
  102. package/dist/git-integration.js +82 -0
  103. package/dist/git-integration.js.map +1 -0
  104. package/dist/git-merge-helpers.d.ts +48 -0
  105. package/dist/git-merge-helpers.d.ts.map +1 -0
  106. package/dist/git-merge-helpers.js +105 -0
  107. package/dist/git-merge-helpers.js.map +1 -0
  108. package/dist/git-merge-lock.d.ts +67 -0
  109. package/dist/git-merge-lock.d.ts.map +1 -0
  110. package/dist/git-merge-lock.js +157 -0
  111. package/dist/git-merge-lock.js.map +1 -0
  112. package/dist/git-merge-strategies.d.ts +39 -0
  113. package/dist/git-merge-strategies.d.ts.map +1 -0
  114. package/dist/git-merge-strategies.js +127 -0
  115. package/dist/git-merge-strategies.js.map +1 -0
  116. package/dist/git-merge.d.ts +80 -0
  117. package/dist/git-merge.d.ts.map +1 -0
  118. package/dist/git-merge.js +373 -0
  119. package/dist/git-merge.js.map +1 -0
  120. package/dist/git-state-detector.d.ts +24 -0
  121. package/dist/git-state-detector.d.ts.map +1 -0
  122. package/dist/git-state-detector.js +122 -0
  123. package/dist/git-state-detector.js.map +1 -0
  124. package/dist/git-types.d.ts +40 -0
  125. package/dist/git-types.d.ts.map +1 -0
  126. package/dist/git-types.js +23 -0
  127. package/dist/git-types.js.map +1 -0
  128. package/dist/git-utils.d.ts +28 -0
  129. package/dist/git-utils.d.ts.map +1 -0
  130. package/dist/git-utils.js +57 -0
  131. package/dist/git-utils.js.map +1 -0
  132. package/dist/git.d.ts +24 -0
  133. package/dist/git.d.ts.map +1 -0
  134. package/dist/git.js +64 -0
  135. package/dist/git.js.map +1 -0
  136. package/dist/guard-engine.d.ts +19 -0
  137. package/dist/guard-engine.d.ts.map +1 -0
  138. package/dist/guard-engine.js +21 -0
  139. package/dist/guard-engine.js.map +1 -0
  140. package/dist/guard-evaluator.d.ts +47 -0
  141. package/dist/guard-evaluator.d.ts.map +1 -0
  142. package/dist/guard-evaluator.js +193 -0
  143. package/dist/guard-evaluator.js.map +1 -0
  144. package/dist/heartbeat.d.ts +73 -0
  145. package/dist/heartbeat.d.ts.map +1 -0
  146. package/dist/heartbeat.js +306 -0
  147. package/dist/heartbeat.js.map +1 -0
  148. package/dist/index.d.ts +32 -0
  149. package/dist/index.d.ts.map +1 -0
  150. package/dist/index.js +493 -0
  151. package/dist/index.js.map +1 -0
  152. package/dist/listener-auto-advance.d.ts +29 -0
  153. package/dist/listener-auto-advance.d.ts.map +1 -0
  154. package/dist/listener-auto-advance.js +172 -0
  155. package/dist/listener-auto-advance.js.map +1 -0
  156. package/dist/listener-review.d.ts +37 -0
  157. package/dist/listener-review.d.ts.map +1 -0
  158. package/dist/listener-review.js +217 -0
  159. package/dist/listener-review.js.map +1 -0
  160. package/dist/listener.d.ts +57 -0
  161. package/dist/listener.d.ts.map +1 -0
  162. package/dist/listener.js +361 -0
  163. package/dist/listener.js.map +1 -0
  164. package/dist/log-manager.d.ts +18 -0
  165. package/dist/log-manager.d.ts.map +1 -0
  166. package/dist/log-manager.js +18 -0
  167. package/dist/log-manager.js.map +1 -0
  168. package/dist/otlp-log-parser.d.ts +21 -0
  169. package/dist/otlp-log-parser.d.ts.map +1 -0
  170. package/dist/otlp-log-parser.js +143 -0
  171. package/dist/otlp-log-parser.js.map +1 -0
  172. package/dist/otlp-metric-parser.d.ts +20 -0
  173. package/dist/otlp-metric-parser.d.ts.map +1 -0
  174. package/dist/otlp-metric-parser.js +113 -0
  175. package/dist/otlp-metric-parser.js.map +1 -0
  176. package/dist/otlp-port-manager.d.ts +26 -0
  177. package/dist/otlp-port-manager.d.ts.map +1 -0
  178. package/dist/otlp-port-manager.js +130 -0
  179. package/dist/otlp-port-manager.js.map +1 -0
  180. package/dist/otlp-receiver.d.ts +51 -0
  181. package/dist/otlp-receiver.d.ts.map +1 -0
  182. package/dist/otlp-receiver.js +663 -0
  183. package/dist/otlp-receiver.js.map +1 -0
  184. package/dist/otlp-types.d.ts +92 -0
  185. package/dist/otlp-types.d.ts.map +1 -0
  186. package/dist/otlp-types.js +133 -0
  187. package/dist/otlp-types.js.map +1 -0
  188. package/dist/output-monitor.d.ts +33 -0
  189. package/dist/output-monitor.d.ts.map +1 -0
  190. package/dist/output-monitor.js +67 -0
  191. package/dist/output-monitor.js.map +1 -0
  192. package/dist/planning-prompt-builder.d.ts +67 -0
  193. package/dist/planning-prompt-builder.d.ts.map +1 -0
  194. package/dist/planning-prompt-builder.js +515 -0
  195. package/dist/planning-prompt-builder.js.map +1 -0
  196. package/dist/prompt-builder.d.ts +14 -0
  197. package/dist/prompt-builder.d.ts.map +1 -0
  198. package/dist/prompt-builder.js +174 -0
  199. package/dist/prompt-builder.js.map +1 -0
  200. package/dist/qa-crash-recovery.d.ts +77 -0
  201. package/dist/qa-crash-recovery.d.ts.map +1 -0
  202. package/dist/qa-crash-recovery.js +243 -0
  203. package/dist/qa-crash-recovery.js.map +1 -0
  204. package/dist/qa-dev-server.d.ts +73 -0
  205. package/dist/qa-dev-server.d.ts.map +1 -0
  206. package/dist/qa-dev-server.js +279 -0
  207. package/dist/qa-dev-server.js.map +1 -0
  208. package/dist/qa-orchestrator.d.ts +79 -0
  209. package/dist/qa-orchestrator.d.ts.map +1 -0
  210. package/dist/qa-orchestrator.js +349 -0
  211. package/dist/qa-orchestrator.js.map +1 -0
  212. package/dist/qa-port-allocator.d.ts +34 -0
  213. package/dist/qa-port-allocator.d.ts.map +1 -0
  214. package/dist/qa-port-allocator.js +75 -0
  215. package/dist/qa-port-allocator.js.map +1 -0
  216. package/dist/qa-provisioner.d.ts +33 -0
  217. package/dist/qa-provisioner.d.ts.map +1 -0
  218. package/dist/qa-provisioner.js +141 -0
  219. package/dist/qa-provisioner.js.map +1 -0
  220. package/dist/qa-state.d.ts +93 -0
  221. package/dist/qa-state.d.ts.map +1 -0
  222. package/dist/qa-state.js +74 -0
  223. package/dist/qa-state.js.map +1 -0
  224. package/dist/queries/control-state.d.ts +25 -0
  225. package/dist/queries/control-state.d.ts.map +1 -0
  226. package/dist/queries/control-state.js +34 -0
  227. package/dist/queries/control-state.js.map +1 -0
  228. package/dist/queries/daemon-connection.d.ts +25 -0
  229. package/dist/queries/daemon-connection.d.ts.map +1 -0
  230. package/dist/queries/daemon-connection.js +28 -0
  231. package/dist/queries/daemon-connection.js.map +1 -0
  232. package/dist/queries/deliveries.d.ts +100 -0
  233. package/dist/queries/deliveries.d.ts.map +1 -0
  234. package/dist/queries/deliveries.js +184 -0
  235. package/dist/queries/deliveries.js.map +1 -0
  236. package/dist/queries/git-activity.d.ts +20 -0
  237. package/dist/queries/git-activity.d.ts.map +1 -0
  238. package/dist/queries/git-activity.js +22 -0
  239. package/dist/queries/git-activity.js.map +1 -0
  240. package/dist/queries/guards.d.ts +47 -0
  241. package/dist/queries/guards.d.ts.map +1 -0
  242. package/dist/queries/guards.js +138 -0
  243. package/dist/queries/guards.js.map +1 -0
  244. package/dist/queries/index.d.ts +19 -0
  245. package/dist/queries/index.d.ts.map +1 -0
  246. package/dist/queries/index.js +17 -0
  247. package/dist/queries/index.js.map +1 -0
  248. package/dist/queries/issues.d.ts +41 -0
  249. package/dist/queries/issues.d.ts.map +1 -0
  250. package/dist/queries/issues.js +67 -0
  251. package/dist/queries/issues.js.map +1 -0
  252. package/dist/queries/qa.d.ts +79 -0
  253. package/dist/queries/qa.d.ts.map +1 -0
  254. package/dist/queries/qa.js +85 -0
  255. package/dist/queries/qa.js.map +1 -0
  256. package/dist/queries/roles.d.ts +13 -0
  257. package/dist/queries/roles.d.ts.map +1 -0
  258. package/dist/queries/roles.js +39 -0
  259. package/dist/queries/roles.js.map +1 -0
  260. package/dist/queries/schemas.d.ts +777 -0
  261. package/dist/queries/schemas.d.ts.map +1 -0
  262. package/dist/queries/schemas.js +391 -0
  263. package/dist/queries/schemas.js.map +1 -0
  264. package/dist/queries/sessions.d.ts +64 -0
  265. package/dist/queries/sessions.d.ts.map +1 -0
  266. package/dist/queries/sessions.js +100 -0
  267. package/dist/queries/sessions.js.map +1 -0
  268. package/dist/queries/shared.d.ts +61 -0
  269. package/dist/queries/shared.d.ts.map +1 -0
  270. package/dist/queries/shared.js +187 -0
  271. package/dist/queries/shared.js.map +1 -0
  272. package/dist/queries/strategies.d.ts +69 -0
  273. package/dist/queries/strategies.d.ts.map +1 -0
  274. package/dist/queries/strategies.js +80 -0
  275. package/dist/queries/strategies.js.map +1 -0
  276. package/dist/queries/workflows.d.ts +17 -0
  277. package/dist/queries/workflows.d.ts.map +1 -0
  278. package/dist/queries/workflows.js +49 -0
  279. package/dist/queries/workflows.js.map +1 -0
  280. package/dist/queries/worktrees.d.ts +38 -0
  281. package/dist/queries/worktrees.d.ts.map +1 -0
  282. package/dist/queries/worktrees.js +37 -0
  283. package/dist/queries/worktrees.js.map +1 -0
  284. package/dist/self-update.d.ts +94 -0
  285. package/dist/self-update.d.ts.map +1 -0
  286. package/dist/self-update.js +438 -0
  287. package/dist/self-update.js.map +1 -0
  288. package/dist/session-lifecycle.d.ts +77 -0
  289. package/dist/session-lifecycle.d.ts.map +1 -0
  290. package/dist/session-lifecycle.js +379 -0
  291. package/dist/session-lifecycle.js.map +1 -0
  292. package/dist/shutdown-state.d.ts +17 -0
  293. package/dist/shutdown-state.d.ts.map +1 -0
  294. package/dist/shutdown-state.js +22 -0
  295. package/dist/shutdown-state.js.map +1 -0
  296. package/dist/spawn-cooldown.d.ts +14 -0
  297. package/dist/spawn-cooldown.d.ts.map +1 -0
  298. package/dist/spawn-cooldown.js +34 -0
  299. package/dist/spawn-cooldown.js.map +1 -0
  300. package/dist/spawn-environment.d.ts +35 -0
  301. package/dist/spawn-environment.d.ts.map +1 -0
  302. package/dist/spawn-environment.js +48 -0
  303. package/dist/spawn-environment.js.map +1 -0
  304. package/dist/spawner-liveness.d.ts +23 -0
  305. package/dist/spawner-liveness.d.ts.map +1 -0
  306. package/dist/spawner-liveness.js +99 -0
  307. package/dist/spawner-liveness.js.map +1 -0
  308. package/dist/spawner-resolution.d.ts +27 -0
  309. package/dist/spawner-resolution.d.ts.map +1 -0
  310. package/dist/spawner-resolution.js +99 -0
  311. package/dist/spawner-resolution.js.map +1 -0
  312. package/dist/spawner-timeout.d.ts +32 -0
  313. package/dist/spawner-timeout.d.ts.map +1 -0
  314. package/dist/spawner-timeout.js +124 -0
  315. package/dist/spawner-timeout.js.map +1 -0
  316. package/dist/spawner.d.ts +77 -0
  317. package/dist/spawner.d.ts.map +1 -0
  318. package/dist/spawner.js +734 -0
  319. package/dist/spawner.js.map +1 -0
  320. package/dist/strategy-completion.d.ts +110 -0
  321. package/dist/strategy-completion.d.ts.map +1 -0
  322. package/dist/strategy-completion.js +434 -0
  323. package/dist/strategy-completion.js.map +1 -0
  324. package/dist/strategy-engine.d.ts +47 -0
  325. package/dist/strategy-engine.d.ts.map +1 -0
  326. package/dist/strategy-engine.js +419 -0
  327. package/dist/strategy-engine.js.map +1 -0
  328. package/dist/strategy-executor.d.ts +93 -0
  329. package/dist/strategy-executor.d.ts.map +1 -0
  330. package/dist/strategy-executor.js +775 -0
  331. package/dist/strategy-executor.js.map +1 -0
  332. package/dist/strategy-lifecycle.d.ts +61 -0
  333. package/dist/strategy-lifecycle.d.ts.map +1 -0
  334. package/dist/strategy-lifecycle.js +516 -0
  335. package/dist/strategy-lifecycle.js.map +1 -0
  336. package/dist/strategy-merge.d.ts +72 -0
  337. package/dist/strategy-merge.d.ts.map +1 -0
  338. package/dist/strategy-merge.js +371 -0
  339. package/dist/strategy-merge.js.map +1 -0
  340. package/dist/strategy-prompt-builder.d.ts +62 -0
  341. package/dist/strategy-prompt-builder.d.ts.map +1 -0
  342. package/dist/strategy-prompt-builder.js +538 -0
  343. package/dist/strategy-prompt-builder.js.map +1 -0
  344. package/dist/strategy-provisioning.d.ts +16 -0
  345. package/dist/strategy-provisioning.d.ts.map +1 -0
  346. package/dist/strategy-provisioning.js +119 -0
  347. package/dist/strategy-provisioning.js.map +1 -0
  348. package/dist/strategy-team-state.d.ts +24 -0
  349. package/dist/strategy-team-state.d.ts.map +1 -0
  350. package/dist/strategy-team-state.js +43 -0
  351. package/dist/strategy-team-state.js.map +1 -0
  352. package/dist/strategy-teardown.d.ts +24 -0
  353. package/dist/strategy-teardown.d.ts.map +1 -0
  354. package/dist/strategy-teardown.js +158 -0
  355. package/dist/strategy-teardown.js.map +1 -0
  356. package/dist/strategy-worktree-state.d.ts +47 -0
  357. package/dist/strategy-worktree-state.d.ts.map +1 -0
  358. package/dist/strategy-worktree-state.js +104 -0
  359. package/dist/strategy-worktree-state.js.map +1 -0
  360. package/dist/supabase.d.ts +36 -0
  361. package/dist/supabase.d.ts.map +1 -0
  362. package/dist/supabase.js +50 -0
  363. package/dist/supabase.js.map +1 -0
  364. package/dist/task-converter.d.ts +61 -0
  365. package/dist/task-converter.d.ts.map +1 -0
  366. package/dist/task-converter.js +286 -0
  367. package/dist/task-converter.js.map +1 -0
  368. package/dist/task-dag-builder.d.ts +14 -0
  369. package/dist/task-dag-builder.d.ts.map +1 -0
  370. package/dist/task-dag-builder.js +17 -0
  371. package/dist/task-dag-builder.js.map +1 -0
  372. package/dist/team-prompt-base.d.ts +114 -0
  373. package/dist/team-prompt-base.d.ts.map +1 -0
  374. package/dist/team-prompt-base.js +531 -0
  375. package/dist/team-prompt-base.js.map +1 -0
  376. package/dist/team-prompt-variants.d.ts +27 -0
  377. package/dist/team-prompt-variants.d.ts.map +1 -0
  378. package/dist/team-prompt-variants.js +134 -0
  379. package/dist/team-prompt-variants.js.map +1 -0
  380. package/dist/team-spawner.d.ts +50 -0
  381. package/dist/team-spawner.d.ts.map +1 -0
  382. package/dist/team-spawner.js +410 -0
  383. package/dist/team-spawner.js.map +1 -0
  384. package/dist/telemetry-writer.d.ts +66 -0
  385. package/dist/telemetry-writer.d.ts.map +1 -0
  386. package/dist/telemetry-writer.js +96 -0
  387. package/dist/telemetry-writer.js.map +1 -0
  388. package/dist/trigger-executor.d.ts +56 -0
  389. package/dist/trigger-executor.d.ts.map +1 -0
  390. package/dist/trigger-executor.js +313 -0
  391. package/dist/trigger-executor.js.map +1 -0
  392. package/dist/types/config.d.ts +60 -0
  393. package/dist/types/config.d.ts.map +1 -0
  394. package/dist/types/config.js +5 -0
  395. package/dist/types/config.js.map +1 -0
  396. package/dist/types/dag.d.ts +53 -0
  397. package/dist/types/dag.d.ts.map +1 -0
  398. package/dist/types/dag.js +5 -0
  399. package/dist/types/dag.js.map +1 -0
  400. package/dist/types/delivery.d.ts +71 -0
  401. package/dist/types/delivery.d.ts.map +1 -0
  402. package/dist/types/delivery.js +5 -0
  403. package/dist/types/delivery.js.map +1 -0
  404. package/dist/types/index.d.ts +15 -0
  405. package/dist/types/index.d.ts.map +1 -0
  406. package/dist/types/index.js +15 -0
  407. package/dist/types/index.js.map +1 -0
  408. package/dist/types/issue.d.ts +22 -0
  409. package/dist/types/issue.d.ts.map +1 -0
  410. package/dist/types/issue.js +5 -0
  411. package/dist/types/issue.js.map +1 -0
  412. package/dist/types/merge.d.ts +28 -0
  413. package/dist/types/merge.d.ts.map +1 -0
  414. package/dist/types/merge.js +5 -0
  415. package/dist/types/merge.js.map +1 -0
  416. package/dist/types/session.d.ts +98 -0
  417. package/dist/types/session.d.ts.map +1 -0
  418. package/dist/types/session.js +5 -0
  419. package/dist/types/session.js.map +1 -0
  420. package/dist/types/strategy.d.ts +175 -0
  421. package/dist/types/strategy.d.ts.map +1 -0
  422. package/dist/types/strategy.js +5 -0
  423. package/dist/types/strategy.js.map +1 -0
  424. package/dist/types/workflow.d.ts +34 -0
  425. package/dist/types/workflow.d.ts.map +1 -0
  426. package/dist/types/workflow.js +9 -0
  427. package/dist/types/workflow.js.map +1 -0
  428. package/dist/types.d.ts +9 -0
  429. package/dist/types.d.ts.map +1 -0
  430. package/dist/types.js +9 -0
  431. package/dist/types.js.map +1 -0
  432. package/dist/unified-init.d.ts +16 -0
  433. package/dist/unified-init.d.ts.map +1 -0
  434. package/dist/unified-init.js +183 -0
  435. package/dist/unified-init.js.map +1 -0
  436. package/dist/unified-shell-config.d.ts +34 -0
  437. package/dist/unified-shell-config.d.ts.map +1 -0
  438. package/dist/unified-shell-config.js +238 -0
  439. package/dist/unified-shell-config.js.map +1 -0
  440. package/dist/unified-shell-status.d.ts +15 -0
  441. package/dist/unified-shell-status.d.ts.map +1 -0
  442. package/dist/unified-shell-status.js +100 -0
  443. package/dist/unified-shell-status.js.map +1 -0
  444. package/dist/unified-shell.d.ts +50 -0
  445. package/dist/unified-shell.d.ts.map +1 -0
  446. package/dist/unified-shell.js +682 -0
  447. package/dist/unified-shell.js.map +1 -0
  448. package/dist/version-check.d.ts +19 -0
  449. package/dist/version-check.d.ts.map +1 -0
  450. package/dist/version-check.js +67 -0
  451. package/dist/version-check.js.map +1 -0
  452. package/dist/workflow-engine.d.ts +95 -0
  453. package/dist/workflow-engine.d.ts.map +1 -0
  454. package/dist/workflow-engine.js +165 -0
  455. package/dist/workflow-engine.js.map +1 -0
  456. package/dist/worktree-merge.d.ts +23 -0
  457. package/dist/worktree-merge.d.ts.map +1 -0
  458. package/dist/worktree-merge.js +57 -0
  459. package/dist/worktree-merge.js.map +1 -0
  460. package/dist/worktree-safety.d.ts +48 -0
  461. package/dist/worktree-safety.d.ts.map +1 -0
  462. package/dist/worktree-safety.js +113 -0
  463. package/dist/worktree-safety.js.map +1 -0
  464. package/dist/worktree-strategy.d.ts +69 -0
  465. package/dist/worktree-strategy.d.ts.map +1 -0
  466. package/dist/worktree-strategy.js +214 -0
  467. package/dist/worktree-strategy.js.map +1 -0
  468. package/dist/worktree.d.ts +159 -0
  469. package/dist/worktree.d.ts.map +1 -0
  470. package/dist/worktree.js +512 -0
  471. package/dist/worktree.js.map +1 -0
  472. package/package.json +76 -0
  473. package/scripts/telora-daemon-wrapper.sh +31 -0
@@ -0,0 +1,734 @@
1
+ /**
2
+ * Claude Code spawner - manages spawning and tracking agent processes.
3
+ *
4
+ * Spawns agents at delivery level with full context including:
5
+ * - Product info (name, vision, description)
6
+ * - Delivery details (name, description, acceptance criteria, tech context)
7
+ * - All context groups and tasks within the delivery
8
+ *
9
+ * Each agent runs in an isolated git worktree created from the integration branch.
10
+ * On successful completion, the agent's branch is merged into integration.
11
+ */
12
+ import { spawn } from 'node:child_process';
13
+ import { mkdirSync, existsSync, readFileSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ import { runGitSync } from './git-types.js';
16
+ import { updateSession, createSession, getDeliveryContext, fetchEffectiveWorkflow, getResolvedTransitionBlockForDelivery, reportGitState } from './supabase.js';
17
+ import { buildSpawnEnvironment } from './spawn-environment.js';
18
+ import { sanitizeGitSegment } from './git-utils.js';
19
+ import { createWorktree, removeWorktree, syncIntegrationWithMain } from './git.js';
20
+ import { recordSessionCompleted, recordActivity } from './heartbeat.js';
21
+ import { getStageByName } from './workflow-engine.js';
22
+ import { StreamJsonParser, sendMessage, buildStreamJsonArgs, stripClaudeCodeEnvVars, createLogStream } from '@telora/daemon-core';
23
+ import { CompletionDetector } from './completion-detector.js';
24
+ import { buildDeliveryPrompt } from './prompt-builder.js';
25
+ import { handleAgentCompletion } from './completion-handler.js';
26
+ import { formatEventForLog } from '@telora/daemon-core';
27
+ import { ActivityTracker } from './activity-tracker.js';
28
+ // Running agents map: sessionId -> RunningAgent
29
+ const runningAgents = new Map();
30
+ // Resource governor (optional, injected by StrategyEngine)
31
+ let governor = null;
32
+ /** Inject the resource governor for global concurrency limiting. */
33
+ export function initGovernor(gov) {
34
+ governor = gov;
35
+ }
36
+ /**
37
+ * Get map of running agents.
38
+ */
39
+ export function getRunningAgents() {
40
+ return runningAgents;
41
+ }
42
+ /**
43
+ * Safely send a signal to a process, ignoring errors if the process has already exited.
44
+ */
45
+ function safeSendSignal(pid, signal) {
46
+ try {
47
+ process.kill(pid, signal);
48
+ }
49
+ catch (e) {
50
+ console.debug(`[spawner] safeSendSignal(${pid}, ${signal}) failed (process may have exited):`, e.message);
51
+ }
52
+ }
53
+ /**
54
+ * Safely close a writable stdin stream, ignoring errors if already closed.
55
+ */
56
+ function safeCloseStdin(stdin) {
57
+ try {
58
+ stdin.end();
59
+ }
60
+ catch (e) {
61
+ console.debug('[spawner] safeCloseStdin failed (stdin may already be closed):', e.message);
62
+ }
63
+ }
64
+ /**
65
+ * Build Claude Code CLI arguments for stream-json I/O.
66
+ *
67
+ * Uses the shared base args from daemon-core, then appends daemon-specific flags.
68
+ */
69
+ function buildClaudeCodeArgs(config) {
70
+ const args = buildStreamJsonArgs({ mcpConfigPath: config.mcpConfigPath });
71
+ // Skip user-level settings (hooks etc.) that can block headless execution
72
+ args.push('--setting-sources', 'project,local');
73
+ console.log(` Stream-JSON mode (structured I/O)`);
74
+ // Agent Teams always enabled -- strategy-level execution model
75
+ args.push('--teammate-mode', 'in-process');
76
+ console.log(` + Agent Teams (in-process teammates)`);
77
+ return args;
78
+ }
79
+ /**
80
+ * Set up per-agent timeout enforcement with warning at 80% and escalating shutdown.
81
+ *
82
+ * Returns a `timedOut` accessor function (closure over mutable state).
83
+ */
84
+ function setupTimeoutEnforcement(config, runningAgent, deliveryName, sessionId) {
85
+ let timedOut = false;
86
+ const timeoutMs = config.sessionTimeoutMs;
87
+ if (timeoutMs <= 0) {
88
+ return { getTimedOut: () => timedOut };
89
+ }
90
+ // Warning at 80% of timeout -- send warning via stdin
91
+ const warningMs = Math.floor(timeoutMs * 0.8);
92
+ runningAgent.warningTimer = setTimeout(() => {
93
+ const remaining = Math.ceil((timeoutMs - warningMs) / 60000);
94
+ console.warn(`[timeout] Agent for "${deliveryName}" at 80% of timeout limit. ` +
95
+ `Sending warning via stdin. ~${remaining} min remaining.`);
96
+ sendMessage(runningAgent.stdin, `WARNING: You have approximately ${remaining} minutes remaining before the session ` +
97
+ `is terminated. Please wrap up your current work, commit changes, and prepare to exit.`);
98
+ }, warningMs);
99
+ // Hard timeout -- escalating shutdown: message -> close stdin -> SIGTERM -> SIGKILL
100
+ runningAgent.timeoutTimer = setTimeout(() => {
101
+ timedOut = true;
102
+ console.error(`[timeout] Agent for "${deliveryName}" exceeded ${timeoutMs / 60000} min timeout. ` +
103
+ `Sending shutdown message...`);
104
+ runningAgent.streamState = 'shutting_down';
105
+ sendMessage(runningAgent.stdin, `TIMEOUT: Session time limit reached. Stop all work immediately, commit any changes, and exit now.`);
106
+ scheduleTimeoutEscalation(runningAgent, deliveryName, sessionId);
107
+ }, timeoutMs);
108
+ return { getTimedOut: () => timedOut };
109
+ }
110
+ /**
111
+ * Schedule escalating shutdown steps after the initial timeout message:
112
+ * stdin close -> SIGTERM -> SIGKILL
113
+ */
114
+ function scheduleTimeoutEscalation(agent, deliveryName, sessionId) {
115
+ // Close stdin after a short grace period to signal EOF
116
+ setTimeout(() => safeCloseStdin(agent.stdin), 10000);
117
+ // SIGTERM after stdin close grace period
118
+ setTimeout(() => {
119
+ if (!runningAgents.has(sessionId))
120
+ return;
121
+ console.error(`[timeout] Agent for "${deliveryName}" did not exit after stdin close. Sending SIGTERM...`);
122
+ safeSendSignal(agent.pid, 'SIGTERM');
123
+ }, 20000);
124
+ // SIGKILL as absolute last resort
125
+ setTimeout(() => {
126
+ if (!runningAgents.has(sessionId))
127
+ return;
128
+ console.error(`[timeout] Agent for "${deliveryName}" still running. Sending SIGKILL...`);
129
+ safeSendSignal(agent.pid, 'SIGKILL');
130
+ }, 30000);
131
+ }
132
+ /**
133
+ * Fetch the guard directive for a delivery, if one exists.
134
+ * When a delivery resumes after a transition block is resolved,
135
+ * the directive template is injected into the agent prompt.
136
+ * Returns null if no directive is found or on any error.
137
+ */
138
+ async function fetchGuardDirective(deliveryId) {
139
+ try {
140
+ const resolvedBlock = await getResolvedTransitionBlockForDelivery(deliveryId);
141
+ if (!resolvedBlock?.directive_template)
142
+ return null;
143
+ console.log(` Injecting guard directive from block ${resolvedBlock.block.id} (path: ${resolvedBlock.block.decision_path})`);
144
+ return resolvedBlock.directive_template;
145
+ }
146
+ catch (e) {
147
+ console.debug(`[spawner] Guard directive lookup failed for delivery ${deliveryId} (non-fatal):`, e.message);
148
+ return null;
149
+ }
150
+ }
151
+ /**
152
+ * Generate branch name for delivery work.
153
+ */
154
+ function generateBranchName(role, deliveryName) {
155
+ const sanitizedRoleName = sanitizeGitSegment(role.name);
156
+ const sanitizedDeliveryName = sanitizeGitSegment(deliveryName);
157
+ return `agent/${sanitizedRoleName}/${sanitizedDeliveryName}`;
158
+ }
159
+ /**
160
+ * Spawn a Claude Code agent to work on a delivery.
161
+ *
162
+ * The agent runs in an isolated git worktree created from the integration branch.
163
+ * On successful completion, the agent's changes are merged back to integration.
164
+ */
165
+ export async function spawnAgentForDelivery(config, deliveryId, role, strategyId) {
166
+ // Get full delivery context — failures are caught and skipped (retried next cycle)
167
+ let context;
168
+ try {
169
+ context = await getDeliveryContext(deliveryId);
170
+ }
171
+ catch (err) {
172
+ console.error(`Error fetching context for delivery ${deliveryId}:`, err.message);
173
+ return; // Will be retried next polling cycle
174
+ }
175
+ if (!context) {
176
+ console.error(`Failed to get context for delivery ${deliveryId}`);
177
+ return;
178
+ }
179
+ // Fetch effective workflow (cached for this session's lifetime)
180
+ let workflow;
181
+ try {
182
+ workflow = await fetchEffectiveWorkflow(deliveryId);
183
+ }
184
+ catch (err) {
185
+ console.error(`Error fetching workflow for delivery ${deliveryId}:`, err.message);
186
+ return; // Will be retried next polling cycle
187
+ }
188
+ const branchName = generateBranchName(role, context.delivery.name);
189
+ // Ensure log directory exists with restrictive permissions (owner-only)
190
+ if (!existsSync(config.logDir)) {
191
+ mkdirSync(config.logDir, { recursive: true, mode: 0o700 });
192
+ }
193
+ // Check for resolved transition block directive (when resuming after human routing)
194
+ const guardDirective = await fetchGuardDirective(deliveryId);
195
+ // Build prompt with full delivery context (+ optional guard directive)
196
+ const prompt = buildDeliveryPrompt(role, context, guardDirective);
197
+ // Log file paths
198
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
199
+ const stdoutPath = join(config.logDir, `${branchName.replace(/\//g, '-')}-${timestamp}.stdout.log`);
200
+ const stderrPath = join(config.logDir, `${branchName.replace(/\//g, '-')}-${timestamp}.stderr.log`);
201
+ // Create session in database — failures are caught and skipped
202
+ let session;
203
+ try {
204
+ const codingStageForSession = getStageByName(workflow, 'coding');
205
+ session = await createSession({
206
+ organizationId: context.delivery.organization_id,
207
+ roleId: role.id,
208
+ issueId: null, // No single issue - working on whole delivery
209
+ deliveryId,
210
+ branchName,
211
+ workflowStageId: codingStageForSession?.id ?? null,
212
+ });
213
+ }
214
+ catch (err) {
215
+ console.error(`Error creating session for delivery "${context.delivery.name}":`, err.message);
216
+ return; // Will be retried next polling cycle
217
+ }
218
+ recordActivity();
219
+ console.log(`Spawning agent for delivery "${context.delivery.name}"`);
220
+ console.log(` Role: ${role.name}`);
221
+ console.log(` Session: ${session.id}`);
222
+ console.log(` Branch: ${branchName}`);
223
+ console.log(` Workflow: ${workflow.id} (${workflow.stages.map(s => s.name).join(' -> ')})`);
224
+ console.log(` Context Groups/Items: ${context.contextGroups.length}`);
225
+ // Sync integration branch with main before creating worktree
226
+ try {
227
+ const syncResult = await syncIntegrationWithMain(config);
228
+ if (syncResult.success) {
229
+ console.log(` Synced integration with main before worktree creation`);
230
+ }
231
+ else {
232
+ console.warn(` Could not sync integration with main: ${syncResult.error}`);
233
+ // Continue anyway — stale code is better than not spawning
234
+ }
235
+ }
236
+ catch (err) {
237
+ console.warn(` Sync failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
238
+ }
239
+ // Create worktree for this agent (branches from integration)
240
+ let worktreePath;
241
+ try {
242
+ worktreePath = await createWorktree(config, branchName);
243
+ console.log(` Worktree: ${worktreePath}`);
244
+ reportGitState(deliveryId, 'worktree_created', branchName).catch(err => console.warn('[spawner] reportGitState worktree_created failed:', err.message));
245
+ }
246
+ catch (err) {
247
+ console.error(`Failed to create worktree for ${branchName}:`, err instanceof Error ? err.message : String(err));
248
+ await updateSession(session.id, {
249
+ status: 'failed',
250
+ exit_reason: 'Failed to create worktree',
251
+ ended_at: new Date().toISOString(),
252
+ });
253
+ return;
254
+ }
255
+ // Build Claude Code arguments and spawn environment
256
+ const args = buildClaudeCodeArgs(config);
257
+ const spawnEnv = buildSpawnEnvironment(config, {
258
+ orgId: config.organizationId,
259
+ strategyId,
260
+ deliveryId,
261
+ sessionId: session.id,
262
+ });
263
+ // Update session to starting
264
+ await updateSession(session.id, {
265
+ status: 'starting',
266
+ started_at: new Date().toISOString(),
267
+ stdout_path: stdoutPath,
268
+ stderr_path: stderrPath,
269
+ });
270
+ // Open log files with restrictive permissions (owner-only read/write)
271
+ const stdoutLogStream = createLogStream(stdoutPath);
272
+ const stderrStream = createLogStream(stderrPath);
273
+ // Raw NDJSON stream log for all agents
274
+ const jsonlPath = join(config.logDir, `${branchName.replace(/\//g, '-')}-${timestamp}.stream.jsonl`);
275
+ const jsonlStream = createLogStream(jsonlPath);
276
+ // Acquire governor slot (if governor is configured)
277
+ if (governor) {
278
+ try {
279
+ await governor.acquireSlot('strategy');
280
+ }
281
+ catch (err) {
282
+ console.warn(`[spawner] Governor denied slot for "${context.delivery.name}":`, err.message);
283
+ await updateSession(session.id, {
284
+ status: 'failed',
285
+ exit_reason: 'Governor denied slot',
286
+ ended_at: new Date().toISOString(),
287
+ });
288
+ removeWorktree(config.repoPath, worktreePath, {
289
+ branchName,
290
+ integrationBranch: config.integrationBranch,
291
+ deliveryName: context.delivery.name,
292
+ mergeSucceeded: false,
293
+ });
294
+ return;
295
+ }
296
+ }
297
+ // Spawn Claude Code — stdin is always piped for stream-json prompt delivery
298
+ const proc = spawn(config.claudeCodePath, args, {
299
+ cwd: worktreePath,
300
+ env: spawnEnv,
301
+ stdio: ['pipe', 'pipe', 'pipe'],
302
+ });
303
+ // Track running agent — declared before parser setup so closures can reference it
304
+ const streamParser = new StreamJsonParser();
305
+ const completionDetector = new CompletionDetector();
306
+ const runningAgent = {
307
+ sessionId: session.id,
308
+ roleId: role.id,
309
+ roleName: role.name,
310
+ strategyId,
311
+ deliveryId,
312
+ deliveryName: context.delivery.name,
313
+ workflow,
314
+ branchName,
315
+ worktreePath,
316
+ repoPath: config.repoPath,
317
+ pid: proc.pid,
318
+ startedAt: new Date(),
319
+ stdoutPath,
320
+ stderrPath,
321
+ enableTeams: true,
322
+ stdin: proc.stdin,
323
+ streamParser,
324
+ streamState: 'starting',
325
+ };
326
+ runningAgents.set(session.id, runningAgent);
327
+ // Wire up stream parsing, logging, and event tracking
328
+ const activityTracker = attachStreamHandlers({
329
+ proc, streamParser, completionDetector, runningAgent,
330
+ deliveryId, deliveryName: context.delivery.name, branchName,
331
+ stdoutLogStream, jsonlStream,
332
+ });
333
+ // Send the initial prompt via stdin as stream-json
334
+ sendMessage(proc.stdin, prompt);
335
+ // Stderr always pipes directly to log file
336
+ proc.stderr?.pipe(stderrStream);
337
+ // Set up per-agent timeout enforcement
338
+ const { getTimedOut } = setupTimeoutEnforcement(config, runningAgent, context.delivery.name, session.id);
339
+ // Update session with PID
340
+ await updateSession(session.id, {
341
+ status: 'running',
342
+ pid: proc.pid,
343
+ });
344
+ // Handle process exit and error — delegates to completion-handler module
345
+ attachProcessHandlers({
346
+ proc, config, context: context, runningAgent, workflow,
347
+ branchName, worktreePath, deliveryId,
348
+ sessionId: session.id, completionDetector, activityTracker,
349
+ stdoutLogStream, stderrStream, jsonlStream,
350
+ getTimedOut,
351
+ });
352
+ }
353
+ /**
354
+ * Attach stream parser, logging, activity tracking, and completion detection
355
+ * event handlers to a spawned agent process.
356
+ */
357
+ function attachStreamHandlers(params) {
358
+ const { proc, streamParser, completionDetector, runningAgent, deliveryId, deliveryName, branchName, stdoutLogStream, jsonlStream } = params;
359
+ streamParser.attach(proc.stdout);
360
+ completionDetector.attach(streamParser);
361
+ const activityTracker = new ActivityTracker(runningAgent.sessionId);
362
+ activityTracker.attach(streamParser);
363
+ // Raw NDJSON log
364
+ streamParser.on('event', (event) => {
365
+ jsonlStream.write(JSON.stringify(event) + '\n');
366
+ });
367
+ // Human-readable log
368
+ streamParser.on('event', (event) => {
369
+ const line = formatEventForLog(event);
370
+ if (line)
371
+ stdoutLogStream.write(line + '\n');
372
+ });
373
+ // Session state tracking
374
+ streamParser.on('init', (event) => {
375
+ runningAgent.claudeSessionId = event.session_id;
376
+ runningAgent.streamState = 'active';
377
+ console.log(` Session initialized (model: ${event.model}, tools: ${event.tools.length})`);
378
+ reportGitState(deliveryId, 'worktree_active', branchName).catch(err => console.warn('[spawner] reportGitState worktree_active failed:', err.message));
379
+ });
380
+ streamParser.on('teammate', (event) => {
381
+ if (event.subtype === 'teammate_spawned') {
382
+ console.log(` [team] Teammate spawned: ${event.agent_name}`);
383
+ }
384
+ else if (event.subtype === 'teammate_completed') {
385
+ const status = event.is_error ? 'FAILED' : 'completed';
386
+ console.log(` [team] Teammate ${status}: ${event.agent_name}`);
387
+ }
388
+ const state = completionDetector.getState();
389
+ runningAgent.activeTeammateCount = state.activeTeammateCount;
390
+ runningAgent.activeTeammates = state.activeTeammates;
391
+ });
392
+ streamParser.on('result', (result) => {
393
+ const cost = result.total_cost_usd?.toFixed(4) ?? '?';
394
+ console.log(` [result] ${result.is_error ? 'ERROR' : 'Success'}: ${result.num_turns} turns, $${cost}`);
395
+ });
396
+ streamParser.on('error', (err, line) => {
397
+ console.warn(` [stream-json] Parse error: ${err.message} — line: ${line.slice(0, 200)}`);
398
+ });
399
+ completionDetector.on('complete', (info) => {
400
+ console.log(`Agent for "${deliveryName}" completed: ` +
401
+ `${info.turnCount} turns, $${info.totalCostUsd.toFixed(4)}, ` +
402
+ `all teammates done: ${info.allTeammatesComplete}`);
403
+ runningAgent.streamState = 'idle';
404
+ initiateGracefulShutdown(runningAgent, deliveryName);
405
+ });
406
+ completionDetector.on('idle', (info) => {
407
+ console.warn(`Agent for "${deliveryName}" idle for ${info.idleDurationMs / 1000}s ` +
408
+ `with ${info.activeTeammateCount} active teammates`);
409
+ });
410
+ return activityTracker;
411
+ }
412
+ /**
413
+ * Attach process close and error handlers to a spawned agent.
414
+ */
415
+ function attachProcessHandlers(params) {
416
+ const { proc, config, context, runningAgent, workflow, branchName, worktreePath, deliveryId, sessionId, completionDetector, activityTracker, stdoutLogStream, stderrStream, jsonlStream, getTimedOut, } = params;
417
+ const codingStage = getStageByName(workflow, 'coding');
418
+ const currentStageId = codingStage?.id;
419
+ proc.on('close', async (code, signal) => {
420
+ governor?.releaseSlot('strategy');
421
+ try {
422
+ await activityTracker.finalFlush();
423
+ }
424
+ catch (err) {
425
+ console.warn('[spawner] activityTracker.finalFlush failed:', err.message);
426
+ }
427
+ handleAgentCompletion(code, signal, {
428
+ config,
429
+ context,
430
+ runningAgent,
431
+ workflow,
432
+ branchName,
433
+ currentStageId,
434
+ timedOut: getTimedOut(),
435
+ timeoutMs: config.sessionTimeoutMs,
436
+ sessionId,
437
+ deliveryId,
438
+ completionDetector,
439
+ stdoutLogStream,
440
+ stderrStream,
441
+ jsonlStream,
442
+ runningAgents,
443
+ });
444
+ });
445
+ proc.on('error', async (err) => {
446
+ console.error(`Agent for "${context.delivery.name}" error:`, err.message);
447
+ governor?.releaseSlot('strategy');
448
+ if (runningAgent.warningTimer)
449
+ clearTimeout(runningAgent.warningTimer);
450
+ if (runningAgent.timeoutTimer)
451
+ clearTimeout(runningAgent.timeoutTimer);
452
+ completionDetector?.destroy();
453
+ runningAgents.delete(sessionId);
454
+ removeWorktree(config.repoPath, worktreePath, {
455
+ branchName,
456
+ integrationBranch: config.integrationBranch,
457
+ deliveryName: context.delivery.name,
458
+ mergeSucceeded: false,
459
+ });
460
+ await updateSession(sessionId, {
461
+ status: 'failed',
462
+ exit_reason: 'Agent process error',
463
+ ended_at: new Date().toISOString(),
464
+ });
465
+ });
466
+ }
467
+ /**
468
+ * Initiate graceful shutdown for an agent session.
469
+ *
470
+ * Sends a wrap-up message via stdin, waits for the process to exit,
471
+ * and escalates to SIGTERM/SIGKILL if needed.
472
+ */
473
+ function initiateGracefulShutdown(agent, deliveryName) {
474
+ const GRACE_PERIOD_MS = 60000; // 60s for agent to wrap up
475
+ const ESCALATION_STEP_MS = 10000; // 10s between escalation steps
476
+ // Send wrap-up message
477
+ sendMessage(agent.stdin, 'All work is complete. Please commit any remaining changes, clean up, and exit.');
478
+ // Flat escalation chain using absolute offsets from now
479
+ // Step 1: Close stdin after grace period
480
+ const graceTimer = setTimeout(() => {
481
+ if (!runningAgents.has(agent.sessionId))
482
+ return;
483
+ console.warn(`[shutdown] Agent for "${deliveryName}" did not exit within grace period. Closing stdin...`);
484
+ safeCloseStdin(agent.stdin);
485
+ }, GRACE_PERIOD_MS);
486
+ // Step 2: SIGTERM after grace period + one escalation step
487
+ setTimeout(() => {
488
+ if (!runningAgents.has(agent.sessionId))
489
+ return;
490
+ console.warn(`[shutdown] Agent for "${deliveryName}" did not exit after stdin close. Sending SIGTERM...`);
491
+ safeSendSignal(agent.pid, 'SIGTERM');
492
+ }, GRACE_PERIOD_MS + ESCALATION_STEP_MS);
493
+ // Step 3: SIGKILL after grace period + two escalation steps
494
+ setTimeout(() => {
495
+ if (!runningAgents.has(agent.sessionId))
496
+ return;
497
+ console.error(`[shutdown] Agent for "${deliveryName}" still running. Sending SIGKILL...`);
498
+ safeSendSignal(agent.pid, 'SIGKILL');
499
+ }, GRACE_PERIOD_MS + ESCALATION_STEP_MS * 2);
500
+ // Clean up timer if process exits before grace period
501
+ const checkExit = setInterval(() => {
502
+ if (!runningAgents.has(agent.sessionId)) {
503
+ clearTimeout(graceTimer);
504
+ clearInterval(checkExit);
505
+ }
506
+ }, 1000);
507
+ }
508
+ /**
509
+ * Terminate a running agent.
510
+ * Sends a shutdown message via stdin, then escalates to SIGTERM/SIGKILL.
511
+ */
512
+ export function terminateAgent(sessionId) {
513
+ const agent = runningAgents.get(sessionId);
514
+ if (!agent)
515
+ return false;
516
+ // Clear timeout timers since we're manually terminating
517
+ if (agent.warningTimer)
518
+ clearTimeout(agent.warningTimer);
519
+ if (agent.timeoutTimer)
520
+ clearTimeout(agent.timeoutTimer);
521
+ try {
522
+ // Send shutdown message, then escalate with flat setTimeout chain
523
+ agent.streamState = 'shutting_down';
524
+ sendMessage(agent.stdin, 'Session is being terminated. Commit your work and exit immediately.');
525
+ // Step 1: Close stdin after 5s
526
+ setTimeout(() => safeCloseStdin(agent.stdin), 5000);
527
+ // Step 2: SIGTERM after 10s (5s after stdin close)
528
+ setTimeout(() => {
529
+ if (!runningAgents.has(sessionId))
530
+ return;
531
+ safeSendSignal(agent.pid, 'SIGTERM');
532
+ }, 10000);
533
+ return true;
534
+ }
535
+ catch (err) {
536
+ console.error(`Failed to terminate agent ${sessionId}:`, err instanceof Error ? err.message : String(err));
537
+ return false;
538
+ }
539
+ }
540
+ /**
541
+ * Terminate all running agents.
542
+ */
543
+ export function terminateAllAgents() {
544
+ for (const [sessionId] of runningAgents) {
545
+ terminateAgent(sessionId);
546
+ }
547
+ }
548
+ /**
549
+ * Check if an agent is running for a session.
550
+ */
551
+ export function isAgentRunning(sessionId) {
552
+ return runningAgents.has(sessionId);
553
+ }
554
+ /**
555
+ * Get count of running agents.
556
+ */
557
+ export function getRunningAgentCount() {
558
+ return runningAgents.size;
559
+ }
560
+ // ── Merge Conflict Resolution Agent ──────────────────────────────────
561
+ /** Timeout for the resolution agent (10 minutes). */
562
+ const RESOLUTION_AGENT_TIMEOUT_MS = 10 * 60 * 1000;
563
+ /**
564
+ * Spawn a short-lived, single-purpose Claude Code agent for merge conflict resolution.
565
+ *
566
+ * Used when no active team exists for a strategy but merge conflicts need resolution.
567
+ * The agent receives a focused prompt with conflict details, resolves in the existing
568
+ * worktree, and exits. No session DB record, no streaming, no team mode.
569
+ */
570
+ export async function spawnResolutionAgent(params) {
571
+ const { config, worktreePath, branchName, integrationBranch, conflictFiles, strategyDescription } = params;
572
+ const prompt = [
573
+ `You are resolving merge conflicts in a git worktree.`,
574
+ ``,
575
+ `**Worktree:** ${worktreePath}`,
576
+ `**Branch:** ${branchName}`,
577
+ `**Merge source:** origin/${integrationBranch}`,
578
+ ``,
579
+ `**Conflicted files:**`,
580
+ ...conflictFiles.map(f => `- ${f}`),
581
+ ``,
582
+ `**Strategy context:** ${strategyDescription}`,
583
+ ``,
584
+ `**Instructions:**`,
585
+ `1. Open each conflicted file and resolve the conflict markers (<<<<<<< / ======= / >>>>>>>)`,
586
+ `2. Stage each resolved file with: git add <file>`,
587
+ `3. Commit the merge resolution: git commit --no-edit`,
588
+ `4. Run: npm run build && npm test`,
589
+ `5. If build or test fails, fix the issues, commit, and re-run until passing`,
590
+ ``,
591
+ `Do NOT run git merge --abort. Your goal is to produce a clean merge commit that passes build and tests.`,
592
+ `Follow the coding standards in CLAUDE.md.`,
593
+ ].join('\n');
594
+ const args = [
595
+ '--print',
596
+ '--dangerously-skip-permissions',
597
+ '--setting-sources', 'project,local',
598
+ prompt,
599
+ ];
600
+ // Build a clean environment (strip Claude Code session vars)
601
+ const spawnEnv = stripClaudeCodeEnvVars(process.env);
602
+ console.log(`[spawner] Spawning resolution agent for branch ${branchName} (${conflictFiles.length} conflicts)`);
603
+ return new Promise((resolve) => {
604
+ const proc = spawn(config.claudeCodePath, args, {
605
+ cwd: worktreePath,
606
+ env: spawnEnv,
607
+ stdio: ['pipe', 'pipe', 'pipe'],
608
+ });
609
+ // Close stdin immediately -- prompt is passed as CLI arg
610
+ proc.stdin?.end();
611
+ // Timeout with escalating shutdown
612
+ const timeout = setTimeout(() => {
613
+ console.warn(`[spawner] Resolution agent timed out after ${RESOLUTION_AGENT_TIMEOUT_MS / 1000}s, killing`);
614
+ try {
615
+ process.kill(proc.pid, 'SIGTERM');
616
+ }
617
+ catch { /* ignore */ }
618
+ setTimeout(() => {
619
+ try {
620
+ process.kill(proc.pid, 'SIGKILL');
621
+ }
622
+ catch { /* ignore */ }
623
+ }, 10000);
624
+ }, RESOLUTION_AGENT_TIMEOUT_MS);
625
+ proc.on('close', (code) => {
626
+ clearTimeout(timeout);
627
+ // Verify resolution by checking for remaining unmerged files
628
+ const unmergedResult = runGitSync(['diff', '--name-only', '--diff-filter=U'], worktreePath);
629
+ const unmergedFiles = unmergedResult.success
630
+ ? unmergedResult.output.split('\n').filter(Boolean)
631
+ : [];
632
+ if (unmergedFiles.length === 0) {
633
+ console.log(`[spawner] Resolution agent succeeded for branch ${branchName} (exit code: ${code})`);
634
+ resolve({ success: true });
635
+ }
636
+ else {
637
+ const msg = `Conflicts remain in: ${unmergedFiles.join(', ')}`;
638
+ console.warn(`[spawner] Resolution agent failed for branch ${branchName}: ${msg}`);
639
+ resolve({ success: false, error: msg });
640
+ }
641
+ });
642
+ proc.on('error', (err) => {
643
+ clearTimeout(timeout);
644
+ console.error(`[spawner] Resolution agent spawn error: ${err.message}`);
645
+ resolve({ success: false, error: `Spawn error: ${err.message}` });
646
+ });
647
+ });
648
+ }
649
+ // ── Stale Agent Process Detection ────────────────────────────────────
650
+ /**
651
+ * Check if a process is alive. Returns false if the process doesn't exist
652
+ * or is a zombie on Linux.
653
+ */
654
+ export function isProcessAlive(pid) {
655
+ // Check if PID exists
656
+ try {
657
+ process.kill(pid, 0);
658
+ }
659
+ catch (e) {
660
+ console.debug(`[spawner] isProcessAlive(${pid}): process not found (ESRCH):`, e.message);
661
+ return false;
662
+ }
663
+ // On Linux, check for zombie state via /proc/{pid}/status
664
+ if (process.platform === 'linux') {
665
+ try {
666
+ const status = readFileSync(`/proc/${pid}/status`, 'utf-8');
667
+ const stateMatch = status.match(/^State:\s+(\S)/m);
668
+ if (stateMatch && stateMatch[1] === 'Z') {
669
+ return false; // Zombie — won't produce an exit event
670
+ }
671
+ }
672
+ catch (e) {
673
+ console.debug(`[spawner] isProcessAlive(${pid}): /proc read failed, treating as dead:`, e.message);
674
+ return false;
675
+ }
676
+ }
677
+ return true;
678
+ }
679
+ /**
680
+ * Check all running agents for PID liveness.
681
+ *
682
+ * If an agent's process has disappeared (OOM kill, external kill -9, etc.)
683
+ * without firing a 'close' event, clean up the agent entry and update the
684
+ * session in the database.
685
+ *
686
+ * Guards against double-cleanup by checking runningAgents.has() before acting.
687
+ */
688
+ export async function checkAgentLiveness(config) {
689
+ if (runningAgents.size === 0)
690
+ return;
691
+ // Snapshot keys to avoid mutating the map while iterating
692
+ const entries = [...runningAgents.entries()];
693
+ for (const [sessionId, agent] of entries) {
694
+ // Guard: agent may have been cleaned up by a normal exit event since we started
695
+ if (!runningAgents.has(sessionId))
696
+ continue;
697
+ if (isProcessAlive(agent.pid))
698
+ continue;
699
+ // PID is gone — stale agent detected
700
+ const runtime = Math.round((Date.now() - agent.startedAt.getTime()) / 1000);
701
+ console.error(`[liveness] Stale agent detected: "${agent.deliveryName}" ` +
702
+ `(session: ${sessionId}, PID: ${agent.pid}, runtime: ${runtime}s). ` +
703
+ `Process disappeared — possible OOM or external kill.`);
704
+ // Clear timeout timers
705
+ if (agent.warningTimer)
706
+ clearTimeout(agent.warningTimer);
707
+ if (agent.timeoutTimer)
708
+ clearTimeout(agent.timeoutTimer);
709
+ // Remove from tracking map
710
+ runningAgents.delete(sessionId);
711
+ // Update session in database
712
+ try {
713
+ await updateSession(sessionId, {
714
+ status: 'failed',
715
+ exit_reason: 'Process disappeared — possible OOM or external kill',
716
+ ended_at: new Date().toISOString(),
717
+ });
718
+ }
719
+ catch (err) {
720
+ console.error(`[liveness] Failed to update session ${sessionId}:`, err.message);
721
+ }
722
+ // Record session completion for heartbeat counters
723
+ recordSessionCompleted(0, 0);
724
+ // Clean up worktree (safety guards in removeWorktree protect uncommitted work)
725
+ // Use agent.repoPath (captured at spawn time) so multi-product agents clean up in the correct repo
726
+ removeWorktree(agent.repoPath, agent.worktreePath, {
727
+ branchName: agent.branchName,
728
+ integrationBranch: config.integrationBranch,
729
+ deliveryName: agent.deliveryName,
730
+ mergeSucceeded: false,
731
+ });
732
+ }
733
+ }
734
+ //# sourceMappingURL=spawner.js.map