night-orch 0.1.0

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 (911) hide show
  1. package/README.md +138 -0
  2. package/dist/cli/commands/cleanup.d.ts +14 -0
  3. package/dist/cli/commands/cleanup.d.ts.map +1 -0
  4. package/dist/cli/commands/cleanup.js +62 -0
  5. package/dist/cli/commands/cleanup.js.map +1 -0
  6. package/dist/cli/commands/continue.d.ts +9 -0
  7. package/dist/cli/commands/continue.d.ts.map +1 -0
  8. package/dist/cli/commands/continue.js +70 -0
  9. package/dist/cli/commands/continue.js.map +1 -0
  10. package/dist/cli/commands/cost-override.d.ts +11 -0
  11. package/dist/cli/commands/cost-override.d.ts.map +1 -0
  12. package/dist/cli/commands/cost-override.js +76 -0
  13. package/dist/cli/commands/cost-override.js.map +1 -0
  14. package/dist/cli/commands/daily-cost-override.d.ts +11 -0
  15. package/dist/cli/commands/daily-cost-override.d.ts.map +1 -0
  16. package/dist/cli/commands/daily-cost-override.js +71 -0
  17. package/dist/cli/commands/daily-cost-override.js.map +1 -0
  18. package/dist/cli/commands/delete-entry.d.ts +12 -0
  19. package/dist/cli/commands/delete-entry.d.ts.map +1 -0
  20. package/dist/cli/commands/delete-entry.js +68 -0
  21. package/dist/cli/commands/delete-entry.js.map +1 -0
  22. package/dist/cli/commands/doctor.d.ts +9 -0
  23. package/dist/cli/commands/doctor.d.ts.map +1 -0
  24. package/dist/cli/commands/doctor.js +227 -0
  25. package/dist/cli/commands/doctor.js.map +1 -0
  26. package/dist/cli/commands/init.d.ts +2 -0
  27. package/dist/cli/commands/init.d.ts.map +1 -0
  28. package/dist/cli/commands/init.js +73 -0
  29. package/dist/cli/commands/init.js.map +1 -0
  30. package/dist/cli/commands/labels-init.d.ts +9 -0
  31. package/dist/cli/commands/labels-init.d.ts.map +1 -0
  32. package/dist/cli/commands/labels-init.js +52 -0
  33. package/dist/cli/commands/labels-init.js.map +1 -0
  34. package/dist/cli/commands/mcp.d.ts +9 -0
  35. package/dist/cli/commands/mcp.d.ts.map +1 -0
  36. package/dist/cli/commands/mcp.js +52 -0
  37. package/dist/cli/commands/mcp.js.map +1 -0
  38. package/dist/cli/commands/monitoring-init.d.ts +2 -0
  39. package/dist/cli/commands/monitoring-init.d.ts.map +1 -0
  40. package/dist/cli/commands/monitoring-init.js +43 -0
  41. package/dist/cli/commands/monitoring-init.js.map +1 -0
  42. package/dist/cli/commands/notify-test.d.ts +9 -0
  43. package/dist/cli/commands/notify-test.d.ts.map +1 -0
  44. package/dist/cli/commands/notify-test.js +41 -0
  45. package/dist/cli/commands/notify-test.js.map +1 -0
  46. package/dist/cli/commands/rebase.d.ts +9 -0
  47. package/dist/cli/commands/rebase.d.ts.map +1 -0
  48. package/dist/cli/commands/rebase.js +63 -0
  49. package/dist/cli/commands/rebase.js.map +1 -0
  50. package/dist/cli/commands/retry.d.ts +14 -0
  51. package/dist/cli/commands/retry.d.ts.map +1 -0
  52. package/dist/cli/commands/retry.js +52 -0
  53. package/dist/cli/commands/retry.js.map +1 -0
  54. package/dist/cli/commands/run-once.d.ts +9 -0
  55. package/dist/cli/commands/run-once.d.ts.map +1 -0
  56. package/dist/cli/commands/run-once.js +62 -0
  57. package/dist/cli/commands/run-once.js.map +1 -0
  58. package/dist/cli/commands/run.d.ts +9 -0
  59. package/dist/cli/commands/run.d.ts.map +1 -0
  60. package/dist/cli/commands/run.js +142 -0
  61. package/dist/cli/commands/run.js.map +1 -0
  62. package/dist/cli/commands/serve.d.ts +14 -0
  63. package/dist/cli/commands/serve.d.ts.map +1 -0
  64. package/dist/cli/commands/serve.js +51 -0
  65. package/dist/cli/commands/serve.js.map +1 -0
  66. package/dist/cli/commands/settings.d.ts +10 -0
  67. package/dist/cli/commands/settings.d.ts.map +1 -0
  68. package/dist/cli/commands/settings.js +100 -0
  69. package/dist/cli/commands/settings.js.map +1 -0
  70. package/dist/cli/commands/status.d.ts +8 -0
  71. package/dist/cli/commands/status.d.ts.map +1 -0
  72. package/dist/cli/commands/status.js +153 -0
  73. package/dist/cli/commands/status.js.map +1 -0
  74. package/dist/cli/commands/sync.d.ts +9 -0
  75. package/dist/cli/commands/sync.d.ts.map +1 -0
  76. package/dist/cli/commands/sync.js +60 -0
  77. package/dist/cli/commands/sync.js.map +1 -0
  78. package/dist/cli/commands/update.d.ts +4 -0
  79. package/dist/cli/commands/update.d.ts.map +1 -0
  80. package/dist/cli/commands/update.js +29 -0
  81. package/dist/cli/commands/update.js.map +1 -0
  82. package/dist/cli/commands/watch.d.ts +9 -0
  83. package/dist/cli/commands/watch.d.ts.map +1 -0
  84. package/dist/cli/commands/watch.js +56 -0
  85. package/dist/cli/commands/watch.js.map +1 -0
  86. package/dist/cli/commands/web.d.ts +16 -0
  87. package/dist/cli/commands/web.d.ts.map +1 -0
  88. package/dist/cli/commands/web.js +206 -0
  89. package/dist/cli/commands/web.js.map +1 -0
  90. package/dist/cli/index.d.ts +3 -0
  91. package/dist/cli/index.d.ts.map +1 -0
  92. package/dist/cli/index.js +213 -0
  93. package/dist/cli/index.js.map +1 -0
  94. package/dist/cli/init/detector.d.ts +7 -0
  95. package/dist/cli/init/detector.d.ts.map +1 -0
  96. package/dist/cli/init/detector.js +40 -0
  97. package/dist/cli/init/detector.js.map +1 -0
  98. package/dist/cli/init/templates.d.ts +10 -0
  99. package/dist/cli/init/templates.d.ts.map +1 -0
  100. package/dist/cli/init/templates.js +30 -0
  101. package/dist/cli/init/templates.js.map +1 -0
  102. package/dist/cli/tui/actions-bar.d.ts +21 -0
  103. package/dist/cli/tui/actions-bar.d.ts.map +1 -0
  104. package/dist/cli/tui/actions-bar.js +91 -0
  105. package/dist/cli/tui/actions-bar.js.map +1 -0
  106. package/dist/cli/tui/active-runs.d.ts +9 -0
  107. package/dist/cli/tui/active-runs.d.ts.map +1 -0
  108. package/dist/cli/tui/active-runs.js +23 -0
  109. package/dist/cli/tui/active-runs.js.map +1 -0
  110. package/dist/cli/tui/agent-stream.d.ts +10 -0
  111. package/dist/cli/tui/agent-stream.d.ts.map +1 -0
  112. package/dist/cli/tui/agent-stream.js +83 -0
  113. package/dist/cli/tui/agent-stream.js.map +1 -0
  114. package/dist/cli/tui/app.d.ts +40 -0
  115. package/dist/cli/tui/app.d.ts.map +1 -0
  116. package/dist/cli/tui/app.js +995 -0
  117. package/dist/cli/tui/app.js.map +1 -0
  118. package/dist/cli/tui/constants.d.ts +15 -0
  119. package/dist/cli/tui/constants.d.ts.map +1 -0
  120. package/dist/cli/tui/constants.js +102 -0
  121. package/dist/cli/tui/constants.js.map +1 -0
  122. package/dist/cli/tui/cost-bar.d.ts +10 -0
  123. package/dist/cli/tui/cost-bar.d.ts.map +1 -0
  124. package/dist/cli/tui/cost-bar.js +17 -0
  125. package/dist/cli/tui/cost-bar.js.map +1 -0
  126. package/dist/cli/tui/data.d.ts +56 -0
  127. package/dist/cli/tui/data.d.ts.map +1 -0
  128. package/dist/cli/tui/data.js +296 -0
  129. package/dist/cli/tui/data.js.map +1 -0
  130. package/dist/cli/tui/format.d.ts +11 -0
  131. package/dist/cli/tui/format.d.ts.map +1 -0
  132. package/dist/cli/tui/format.js +83 -0
  133. package/dist/cli/tui/format.js.map +1 -0
  134. package/dist/cli/tui/header.d.ts +16 -0
  135. package/dist/cli/tui/header.d.ts.map +1 -0
  136. package/dist/cli/tui/header.js +22 -0
  137. package/dist/cli/tui/header.js.map +1 -0
  138. package/dist/cli/tui/logs-view.d.ts +19 -0
  139. package/dist/cli/tui/logs-view.d.ts.map +1 -0
  140. package/dist/cli/tui/logs-view.js +63 -0
  141. package/dist/cli/tui/logs-view.js.map +1 -0
  142. package/dist/cli/tui/merge-queue-panel.d.ts +9 -0
  143. package/dist/cli/tui/merge-queue-panel.d.ts.map +1 -0
  144. package/dist/cli/tui/merge-queue-panel.js +14 -0
  145. package/dist/cli/tui/merge-queue-panel.js.map +1 -0
  146. package/dist/cli/tui/projects-view.d.ts +25 -0
  147. package/dist/cli/tui/projects-view.d.ts.map +1 -0
  148. package/dist/cli/tui/projects-view.js +145 -0
  149. package/dist/cli/tui/projects-view.js.map +1 -0
  150. package/dist/cli/tui/recent-runs.d.ts +9 -0
  151. package/dist/cli/tui/recent-runs.d.ts.map +1 -0
  152. package/dist/cli/tui/recent-runs.js +20 -0
  153. package/dist/cli/tui/recent-runs.js.map +1 -0
  154. package/dist/cli/tui/runs-view.d.ts +22 -0
  155. package/dist/cli/tui/runs-view.d.ts.map +1 -0
  156. package/dist/cli/tui/runs-view.js +48 -0
  157. package/dist/cli/tui/runs-view.js.map +1 -0
  158. package/dist/cli/tui/settings-view.d.ts +9 -0
  159. package/dist/cli/tui/settings-view.d.ts.map +1 -0
  160. package/dist/cli/tui/settings-view.js +21 -0
  161. package/dist/cli/tui/settings-view.js.map +1 -0
  162. package/dist/cli/tui/stats-view.d.ts +11 -0
  163. package/dist/cli/tui/stats-view.d.ts.map +1 -0
  164. package/dist/cli/tui/stats-view.js +48 -0
  165. package/dist/cli/tui/stats-view.js.map +1 -0
  166. package/dist/cli/tui/titles.d.ts +36 -0
  167. package/dist/cli/tui/titles.d.ts.map +1 -0
  168. package/dist/cli/tui/titles.js +50 -0
  169. package/dist/cli/tui/titles.js.map +1 -0
  170. package/dist/cli/tui/types.d.ts +10 -0
  171. package/dist/cli/tui/types.d.ts.map +1 -0
  172. package/dist/cli/tui/types.js +2 -0
  173. package/dist/cli/tui/types.js.map +1 -0
  174. package/dist/cli/tui/view-model.d.ts +20 -0
  175. package/dist/cli/tui/view-model.d.ts.map +1 -0
  176. package/dist/cli/tui/view-model.js +80 -0
  177. package/dist/cli/tui/view-model.js.map +1 -0
  178. package/dist/components/button/button.tui.d.ts +4 -0
  179. package/dist/components/button/button.tui.d.ts.map +1 -0
  180. package/dist/components/button/button.tui.js +16 -0
  181. package/dist/components/button/button.tui.js.map +1 -0
  182. package/dist/components/button/button.web.d.ts +4 -0
  183. package/dist/components/button/button.web.d.ts.map +1 -0
  184. package/dist/components/button/button.web.js +7 -0
  185. package/dist/components/button/button.web.js.map +1 -0
  186. package/dist/components/button/index.d.ts +5 -0
  187. package/dist/components/button/index.d.ts.map +1 -0
  188. package/dist/components/button/index.js +4 -0
  189. package/dist/components/button/index.js.map +1 -0
  190. package/dist/components/button/types.d.ts +28 -0
  191. package/dist/components/button/types.d.ts.map +1 -0
  192. package/dist/components/button/types.js +2 -0
  193. package/dist/components/button/types.js.map +1 -0
  194. package/dist/components/button/view-model.d.ts +3 -0
  195. package/dist/components/button/view-model.d.ts.map +1 -0
  196. package/dist/components/button/view-model.js +53 -0
  197. package/dist/components/button/view-model.js.map +1 -0
  198. package/dist/components/card/card.tui.d.ts +4 -0
  199. package/dist/components/card/card.tui.d.ts.map +1 -0
  200. package/dist/components/card/card.tui.js +41 -0
  201. package/dist/components/card/card.tui.js.map +1 -0
  202. package/dist/components/card/card.web.d.ts +4 -0
  203. package/dist/components/card/card.web.d.ts.map +1 -0
  204. package/dist/components/card/card.web.js +12 -0
  205. package/dist/components/card/card.web.js.map +1 -0
  206. package/dist/components/card/index.d.ts +5 -0
  207. package/dist/components/card/index.d.ts.map +1 -0
  208. package/dist/components/card/index.js +4 -0
  209. package/dist/components/card/index.js.map +1 -0
  210. package/dist/components/card/types.d.ts +22 -0
  211. package/dist/components/card/types.d.ts.map +1 -0
  212. package/dist/components/card/types.js +2 -0
  213. package/dist/components/card/types.js.map +1 -0
  214. package/dist/components/card/view-model.d.ts +3 -0
  215. package/dist/components/card/view-model.d.ts.map +1 -0
  216. package/dist/components/card/view-model.js +35 -0
  217. package/dist/components/card/view-model.js.map +1 -0
  218. package/dist/components/index.d.ts +5 -0
  219. package/dist/components/index.d.ts.map +1 -0
  220. package/dist/components/index.js +5 -0
  221. package/dist/components/index.js.map +1 -0
  222. package/dist/components/issue-row/index.d.ts +5 -0
  223. package/dist/components/issue-row/index.d.ts.map +1 -0
  224. package/dist/components/issue-row/index.js +4 -0
  225. package/dist/components/issue-row/index.js.map +1 -0
  226. package/dist/components/issue-row/issue-row.tui.d.ts +4 -0
  227. package/dist/components/issue-row/issue-row.tui.d.ts.map +1 -0
  228. package/dist/components/issue-row/issue-row.tui.js +15 -0
  229. package/dist/components/issue-row/issue-row.tui.js.map +1 -0
  230. package/dist/components/issue-row/issue-row.web.d.ts +4 -0
  231. package/dist/components/issue-row/issue-row.web.d.ts.map +1 -0
  232. package/dist/components/issue-row/issue-row.web.js +14 -0
  233. package/dist/components/issue-row/issue-row.web.js.map +1 -0
  234. package/dist/components/issue-row/types.d.ts +18 -0
  235. package/dist/components/issue-row/types.d.ts.map +1 -0
  236. package/dist/components/issue-row/types.js +2 -0
  237. package/dist/components/issue-row/types.js.map +1 -0
  238. package/dist/components/issue-row/view-model.d.ts +3 -0
  239. package/dist/components/issue-row/view-model.d.ts.map +1 -0
  240. package/dist/components/issue-row/view-model.js +31 -0
  241. package/dist/components/issue-row/view-model.js.map +1 -0
  242. package/dist/components/modal/index.d.ts +3 -0
  243. package/dist/components/modal/index.d.ts.map +1 -0
  244. package/dist/components/modal/index.js +2 -0
  245. package/dist/components/modal/index.js.map +1 -0
  246. package/dist/components/modal/modal.web.d.ts +16 -0
  247. package/dist/components/modal/modal.web.d.ts.map +1 -0
  248. package/dist/components/modal/modal.web.js +92 -0
  249. package/dist/components/modal/modal.web.js.map +1 -0
  250. package/dist/components/modal/types.d.ts +15 -0
  251. package/dist/components/modal/types.d.ts.map +1 -0
  252. package/dist/components/modal/types.js +2 -0
  253. package/dist/components/modal/types.js.map +1 -0
  254. package/dist/config/loader.d.ts +23 -0
  255. package/dist/config/loader.d.ts.map +1 -0
  256. package/dist/config/loader.js +136 -0
  257. package/dist/config/loader.js.map +1 -0
  258. package/dist/config/paths.d.ts +6 -0
  259. package/dist/config/paths.d.ts.map +1 -0
  260. package/dist/config/paths.js +24 -0
  261. package/dist/config/paths.js.map +1 -0
  262. package/dist/config/schema.d.ts +3088 -0
  263. package/dist/config/schema.d.ts.map +1 -0
  264. package/dist/config/schema.js +328 -0
  265. package/dist/config/schema.js.map +1 -0
  266. package/dist/discovery/commands.d.ts +33 -0
  267. package/dist/discovery/commands.d.ts.map +1 -0
  268. package/dist/discovery/commands.js +82 -0
  269. package/dist/discovery/commands.js.map +1 -0
  270. package/dist/discovery/decomposer.d.ts +6 -0
  271. package/dist/discovery/decomposer.d.ts.map +1 -0
  272. package/dist/discovery/decomposer.js +102 -0
  273. package/dist/discovery/decomposer.js.map +1 -0
  274. package/dist/discovery/discover.d.ts +21 -0
  275. package/dist/discovery/discover.d.ts.map +1 -0
  276. package/dist/discovery/discover.js +98 -0
  277. package/dist/discovery/discover.js.map +1 -0
  278. package/dist/discovery/followup.d.ts +14 -0
  279. package/dist/discovery/followup.d.ts.map +1 -0
  280. package/dist/discovery/followup.js +27 -0
  281. package/dist/discovery/followup.js.map +1 -0
  282. package/dist/discovery/roles.d.ts +15 -0
  283. package/dist/discovery/roles.d.ts.map +1 -0
  284. package/dist/discovery/roles.js +36 -0
  285. package/dist/discovery/roles.js.map +1 -0
  286. package/dist/discovery/selector.d.ts +13 -0
  287. package/dist/discovery/selector.d.ts.map +1 -0
  288. package/dist/discovery/selector.js +27 -0
  289. package/dist/discovery/selector.js.map +1 -0
  290. package/dist/discovery/triage.d.ts +29 -0
  291. package/dist/discovery/triage.d.ts.map +1 -0
  292. package/dist/discovery/triage.js +61 -0
  293. package/dist/discovery/triage.js.map +1 -0
  294. package/dist/environment/bootstrap.d.ts +19 -0
  295. package/dist/environment/bootstrap.d.ts.map +1 -0
  296. package/dist/environment/bootstrap.js +89 -0
  297. package/dist/environment/bootstrap.js.map +1 -0
  298. package/dist/environment/dedicated.d.ts +17 -0
  299. package/dist/environment/dedicated.d.ts.map +1 -0
  300. package/dist/environment/dedicated.js +68 -0
  301. package/dist/environment/dedicated.js.map +1 -0
  302. package/dist/environment/env-file.d.ts +16 -0
  303. package/dist/environment/env-file.d.ts.map +1 -0
  304. package/dist/environment/env-file.js +74 -0
  305. package/dist/environment/env-file.js.map +1 -0
  306. package/dist/environment/manager.d.ts +33 -0
  307. package/dist/environment/manager.d.ts.map +1 -0
  308. package/dist/environment/manager.js +113 -0
  309. package/dist/environment/manager.js.map +1 -0
  310. package/dist/environment/port.d.ts +15 -0
  311. package/dist/environment/port.d.ts.map +1 -0
  312. package/dist/environment/port.js +21 -0
  313. package/dist/environment/port.js.map +1 -0
  314. package/dist/environment/shared.d.ts +6 -0
  315. package/dist/environment/shared.d.ts.map +1 -0
  316. package/dist/environment/shared.js +30 -0
  317. package/dist/environment/shared.js.map +1 -0
  318. package/dist/events/bus.d.ts +18 -0
  319. package/dist/events/bus.d.ts.map +1 -0
  320. package/dist/events/bus.js +70 -0
  321. package/dist/events/bus.js.map +1 -0
  322. package/dist/events/observability.d.ts +32 -0
  323. package/dist/events/observability.d.ts.map +1 -0
  324. package/dist/events/observability.js +155 -0
  325. package/dist/events/observability.js.map +1 -0
  326. package/dist/events/types.d.ts +18 -0
  327. package/dist/events/types.d.ts.map +1 -0
  328. package/dist/events/types.js +2 -0
  329. package/dist/events/types.js.map +1 -0
  330. package/dist/forge/bot-comment.d.ts +17 -0
  331. package/dist/forge/bot-comment.d.ts.map +1 -0
  332. package/dist/forge/bot-comment.js +30 -0
  333. package/dist/forge/bot-comment.js.map +1 -0
  334. package/dist/forge/factory.d.ts +4 -0
  335. package/dist/forge/factory.d.ts.map +1 -0
  336. package/dist/forge/factory.js +31 -0
  337. package/dist/forge/factory.js.map +1 -0
  338. package/dist/forge/forgejo-client.d.ts +20 -0
  339. package/dist/forge/forgejo-client.d.ts.map +1 -0
  340. package/dist/forge/forgejo-client.js +114 -0
  341. package/dist/forge/forgejo-client.js.map +1 -0
  342. package/dist/forge/forgejo-labels.d.ts +13 -0
  343. package/dist/forge/forgejo-labels.d.ts.map +1 -0
  344. package/dist/forge/forgejo-labels.js +51 -0
  345. package/dist/forge/forgejo-labels.js.map +1 -0
  346. package/dist/forge/forgejo.d.ts +31 -0
  347. package/dist/forge/forgejo.d.ts.map +1 -0
  348. package/dist/forge/forgejo.js +264 -0
  349. package/dist/forge/forgejo.js.map +1 -0
  350. package/dist/forge/github.d.ts +31 -0
  351. package/dist/forge/github.d.ts.map +1 -0
  352. package/dist/forge/github.js +438 -0
  353. package/dist/forge/github.js.map +1 -0
  354. package/dist/forge/status-comment.d.ts +19 -0
  355. package/dist/forge/status-comment.d.ts.map +1 -0
  356. package/dist/forge/status-comment.js +44 -0
  357. package/dist/forge/status-comment.js.map +1 -0
  358. package/dist/forge/types.d.ts +118 -0
  359. package/dist/forge/types.d.ts.map +1 -0
  360. package/dist/forge/types.js +2 -0
  361. package/dist/forge/types.js.map +1 -0
  362. package/dist/git/process.d.ts +15 -0
  363. package/dist/git/process.d.ts.map +1 -0
  364. package/dist/git/process.js +38 -0
  365. package/dist/git/process.js.map +1 -0
  366. package/dist/git/repo.d.ts +64 -0
  367. package/dist/git/repo.d.ts.map +1 -0
  368. package/dist/git/repo.js +158 -0
  369. package/dist/git/repo.js.map +1 -0
  370. package/dist/git/slug.d.ts +13 -0
  371. package/dist/git/slug.d.ts.map +1 -0
  372. package/dist/git/slug.js +28 -0
  373. package/dist/git/slug.js.map +1 -0
  374. package/dist/git/worktree.d.ts +23 -0
  375. package/dist/git/worktree.d.ts.map +1 -0
  376. package/dist/git/worktree.js +200 -0
  377. package/dist/git/worktree.js.map +1 -0
  378. package/dist/labels/bootstrap.d.ts +12 -0
  379. package/dist/labels/bootstrap.d.ts.map +1 -0
  380. package/dist/labels/bootstrap.js +101 -0
  381. package/dist/labels/bootstrap.js.map +1 -0
  382. package/dist/labels/config.d.ts +6 -0
  383. package/dist/labels/config.d.ts.map +1 -0
  384. package/dist/labels/config.js +56 -0
  385. package/dist/labels/config.js.map +1 -0
  386. package/dist/labels/manager.d.ts +10 -0
  387. package/dist/labels/manager.d.ts.map +1 -0
  388. package/dist/labels/manager.js +30 -0
  389. package/dist/labels/manager.js.map +1 -0
  390. package/dist/labels/transitions.d.ts +33 -0
  391. package/dist/labels/transitions.d.ts.map +1 -0
  392. package/dist/labels/transitions.js +81 -0
  393. package/dist/labels/transitions.js.map +1 -0
  394. package/dist/loop/checkpoint.d.ts +60 -0
  395. package/dist/loop/checkpoint.d.ts.map +1 -0
  396. package/dist/loop/checkpoint.js +226 -0
  397. package/dist/loop/checkpoint.js.map +1 -0
  398. package/dist/loop/commit.d.ts +17 -0
  399. package/dist/loop/commit.d.ts.map +1 -0
  400. package/dist/loop/commit.js +65 -0
  401. package/dist/loop/commit.js.map +1 -0
  402. package/dist/loop/context.d.ts +11 -0
  403. package/dist/loop/context.d.ts.map +1 -0
  404. package/dist/loop/context.js +26 -0
  405. package/dist/loop/context.js.map +1 -0
  406. package/dist/loop/cost.d.ts +79 -0
  407. package/dist/loop/cost.d.ts.map +1 -0
  408. package/dist/loop/cost.js +223 -0
  409. package/dist/loop/cost.js.map +1 -0
  410. package/dist/loop/decision.d.ts +22 -0
  411. package/dist/loop/decision.d.ts.map +1 -0
  412. package/dist/loop/decision.js +118 -0
  413. package/dist/loop/decision.js.map +1 -0
  414. package/dist/loop/diff-guard.d.ts +21 -0
  415. package/dist/loop/diff-guard.d.ts.map +1 -0
  416. package/dist/loop/diff-guard.js +52 -0
  417. package/dist/loop/diff-guard.js.map +1 -0
  418. package/dist/loop/engine.d.ts +32 -0
  419. package/dist/loop/engine.d.ts.map +1 -0
  420. package/dist/loop/engine.js +376 -0
  421. package/dist/loop/engine.js.map +1 -0
  422. package/dist/loop/parallel.d.ts +20 -0
  423. package/dist/loop/parallel.d.ts.map +1 -0
  424. package/dist/loop/parallel.js +144 -0
  425. package/dist/loop/parallel.js.map +1 -0
  426. package/dist/loop/plan-summary-comment.d.ts +5 -0
  427. package/dist/loop/plan-summary-comment.d.ts.map +1 -0
  428. package/dist/loop/plan-summary-comment.js +89 -0
  429. package/dist/loop/plan-summary-comment.js.map +1 -0
  430. package/dist/loop/pricing.d.ts +23 -0
  431. package/dist/loop/pricing.d.ts.map +1 -0
  432. package/dist/loop/pricing.js +64 -0
  433. package/dist/loop/pricing.js.map +1 -0
  434. package/dist/loop/review-feedback.d.ts +12 -0
  435. package/dist/loop/review-feedback.d.ts.map +1 -0
  436. package/dist/loop/review-feedback.js +55 -0
  437. package/dist/loop/review-feedback.js.map +1 -0
  438. package/dist/loop/step-executor.d.ts +70 -0
  439. package/dist/loop/step-executor.d.ts.map +1 -0
  440. package/dist/loop/step-executor.js +328 -0
  441. package/dist/loop/step-executor.js.map +1 -0
  442. package/dist/loop/supervisor.d.ts +9 -0
  443. package/dist/loop/supervisor.d.ts.map +1 -0
  444. package/dist/loop/supervisor.js +22 -0
  445. package/dist/loop/supervisor.js.map +1 -0
  446. package/dist/loop/types.d.ts +66 -0
  447. package/dist/loop/types.d.ts.map +1 -0
  448. package/dist/loop/types.js +2 -0
  449. package/dist/loop/types.js.map +1 -0
  450. package/dist/loop/verifier.d.ts +9 -0
  451. package/dist/loop/verifier.d.ts.map +1 -0
  452. package/dist/loop/verifier.js +59 -0
  453. package/dist/loop/verifier.js.map +1 -0
  454. package/dist/loop/workflow.d.ts +38 -0
  455. package/dist/loop/workflow.d.ts.map +1 -0
  456. package/dist/loop/workflow.js +64 -0
  457. package/dist/loop/workflow.js.map +1 -0
  458. package/dist/mcp/http.d.ts +8 -0
  459. package/dist/mcp/http.d.ts.map +1 -0
  460. package/dist/mcp/http.js +76 -0
  461. package/dist/mcp/http.js.map +1 -0
  462. package/dist/mcp/resources/index.d.ts +11 -0
  463. package/dist/mcp/resources/index.d.ts.map +1 -0
  464. package/dist/mcp/resources/index.js +137 -0
  465. package/dist/mcp/resources/index.js.map +1 -0
  466. package/dist/mcp/server.d.ts +16 -0
  467. package/dist/mcp/server.d.ts.map +1 -0
  468. package/dist/mcp/server.js +54 -0
  469. package/dist/mcp/server.js.map +1 -0
  470. package/dist/mcp/tools/index.d.ts +19 -0
  471. package/dist/mcp/tools/index.d.ts.map +1 -0
  472. package/dist/mcp/tools/index.js +847 -0
  473. package/dist/mcp/tools/index.js.map +1 -0
  474. package/dist/mentions/manager.d.ts +12 -0
  475. package/dist/mentions/manager.d.ts.map +1 -0
  476. package/dist/mentions/manager.js +50 -0
  477. package/dist/mentions/manager.js.map +1 -0
  478. package/dist/mentions/resolver.d.ts +12 -0
  479. package/dist/mentions/resolver.d.ts.map +1 -0
  480. package/dist/mentions/resolver.js +26 -0
  481. package/dist/mentions/resolver.js.map +1 -0
  482. package/dist/mentions/tracker.d.ts +8 -0
  483. package/dist/mentions/tracker.d.ts.map +1 -0
  484. package/dist/mentions/tracker.js +18 -0
  485. package/dist/mentions/tracker.js.map +1 -0
  486. package/dist/merge-queue/batch.d.ts +19 -0
  487. package/dist/merge-queue/batch.d.ts.map +1 -0
  488. package/dist/merge-queue/batch.js +100 -0
  489. package/dist/merge-queue/batch.js.map +1 -0
  490. package/dist/merge-queue/bisect.d.ts +10 -0
  491. package/dist/merge-queue/bisect.d.ts.map +1 -0
  492. package/dist/merge-queue/bisect.js +18 -0
  493. package/dist/merge-queue/bisect.js.map +1 -0
  494. package/dist/merge-queue/eligibility.d.ts +25 -0
  495. package/dist/merge-queue/eligibility.d.ts.map +1 -0
  496. package/dist/merge-queue/eligibility.js +125 -0
  497. package/dist/merge-queue/eligibility.js.map +1 -0
  498. package/dist/merge-queue/finalize.d.ts +7 -0
  499. package/dist/merge-queue/finalize.d.ts.map +1 -0
  500. package/dist/merge-queue/finalize.js +36 -0
  501. package/dist/merge-queue/finalize.js.map +1 -0
  502. package/dist/merge-queue/runner.d.ts +13 -0
  503. package/dist/merge-queue/runner.d.ts.map +1 -0
  504. package/dist/merge-queue/runner.js +246 -0
  505. package/dist/merge-queue/runner.js.map +1 -0
  506. package/dist/merge-queue/staging.d.ts +13 -0
  507. package/dist/merge-queue/staging.d.ts.map +1 -0
  508. package/dist/merge-queue/staging.js +88 -0
  509. package/dist/merge-queue/staging.js.map +1 -0
  510. package/dist/merge-queue/types.d.ts +23 -0
  511. package/dist/merge-queue/types.d.ts.map +1 -0
  512. package/dist/merge-queue/types.js +2 -0
  513. package/dist/merge-queue/types.js.map +1 -0
  514. package/dist/metrics/collectors.d.ts +24 -0
  515. package/dist/metrics/collectors.d.ts.map +1 -0
  516. package/dist/metrics/collectors.js +132 -0
  517. package/dist/metrics/collectors.js.map +1 -0
  518. package/dist/metrics/server.d.ts +8 -0
  519. package/dist/metrics/server.d.ts.map +1 -0
  520. package/dist/metrics/server.js +41 -0
  521. package/dist/metrics/server.js.map +1 -0
  522. package/dist/metrics/service.d.ts +26 -0
  523. package/dist/metrics/service.d.ts.map +1 -0
  524. package/dist/metrics/service.js +161 -0
  525. package/dist/metrics/service.js.map +1 -0
  526. package/dist/notify/channels/console.d.ts +10 -0
  527. package/dist/notify/channels/console.d.ts.map +1 -0
  528. package/dist/notify/channels/console.js +17 -0
  529. package/dist/notify/channels/console.js.map +1 -0
  530. package/dist/notify/channels/discord.d.ts +13 -0
  531. package/dist/notify/channels/discord.d.ts.map +1 -0
  532. package/dist/notify/channels/discord.js +183 -0
  533. package/dist/notify/channels/discord.js.map +1 -0
  534. package/dist/notify/channels/github-comment.d.ts +13 -0
  535. package/dist/notify/channels/github-comment.d.ts.map +1 -0
  536. package/dist/notify/channels/github-comment.js +52 -0
  537. package/dist/notify/channels/github-comment.js.map +1 -0
  538. package/dist/notify/channels/smtp.d.ts +17 -0
  539. package/dist/notify/channels/smtp.d.ts.map +1 -0
  540. package/dist/notify/channels/smtp.js +65 -0
  541. package/dist/notify/channels/smtp.js.map +1 -0
  542. package/dist/notify/channels/webhook-common.d.ts +19 -0
  543. package/dist/notify/channels/webhook-common.d.ts.map +1 -0
  544. package/dist/notify/channels/webhook-common.js +111 -0
  545. package/dist/notify/channels/webhook-common.js.map +1 -0
  546. package/dist/notify/channels/webhook.d.ts +13 -0
  547. package/dist/notify/channels/webhook.d.ts.map +1 -0
  548. package/dist/notify/channels/webhook.js +72 -0
  549. package/dist/notify/channels/webhook.js.map +1 -0
  550. package/dist/notify/dispatcher.d.ts +11 -0
  551. package/dist/notify/dispatcher.d.ts.map +1 -0
  552. package/dist/notify/dispatcher.js +66 -0
  553. package/dist/notify/dispatcher.js.map +1 -0
  554. package/dist/notify/factory.d.ts +5 -0
  555. package/dist/notify/factory.d.ts.map +1 -0
  556. package/dist/notify/factory.js +46 -0
  557. package/dist/notify/factory.js.map +1 -0
  558. package/dist/notify/payload.d.ts +8 -0
  559. package/dist/notify/payload.d.ts.map +1 -0
  560. package/dist/notify/payload.js +37 -0
  561. package/dist/notify/payload.js.map +1 -0
  562. package/dist/notify/types.d.ts +34 -0
  563. package/dist/notify/types.d.ts.map +1 -0
  564. package/dist/notify/types.js +2 -0
  565. package/dist/notify/types.js.map +1 -0
  566. package/dist/ops/auto-cleanup.d.ts +14 -0
  567. package/dist/ops/auto-cleanup.d.ts.map +1 -0
  568. package/dist/ops/auto-cleanup.js +58 -0
  569. package/dist/ops/auto-cleanup.js.map +1 -0
  570. package/dist/ops/cleanup.d.ts +32 -0
  571. package/dist/ops/cleanup.d.ts.map +1 -0
  572. package/dist/ops/cleanup.js +208 -0
  573. package/dist/ops/cleanup.js.map +1 -0
  574. package/dist/ops/continue.d.ts +12 -0
  575. package/dist/ops/continue.d.ts.map +1 -0
  576. package/dist/ops/continue.js +240 -0
  577. package/dist/ops/continue.js.map +1 -0
  578. package/dist/ops/cost-override.d.ts +16 -0
  579. package/dist/ops/cost-override.d.ts.map +1 -0
  580. package/dist/ops/cost-override.js +28 -0
  581. package/dist/ops/cost-override.js.map +1 -0
  582. package/dist/ops/daily-cost-override.d.ts +15 -0
  583. package/dist/ops/daily-cost-override.d.ts.map +1 -0
  584. package/dist/ops/daily-cost-override.js +23 -0
  585. package/dist/ops/daily-cost-override.js.map +1 -0
  586. package/dist/ops/delete-entry.d.ts +44 -0
  587. package/dist/ops/delete-entry.d.ts.map +1 -0
  588. package/dist/ops/delete-entry.js +381 -0
  589. package/dist/ops/delete-entry.js.map +1 -0
  590. package/dist/ops/labels-init.d.ts +43 -0
  591. package/dist/ops/labels-init.d.ts.map +1 -0
  592. package/dist/ops/labels-init.js +149 -0
  593. package/dist/ops/labels-init.js.map +1 -0
  594. package/dist/ops/rebase-and-check.d.ts +34 -0
  595. package/dist/ops/rebase-and-check.d.ts.map +1 -0
  596. package/dist/ops/rebase-and-check.js +110 -0
  597. package/dist/ops/rebase-and-check.js.map +1 -0
  598. package/dist/ops/rebase.d.ts +18 -0
  599. package/dist/ops/rebase.d.ts.map +1 -0
  600. package/dist/ops/rebase.js +67 -0
  601. package/dist/ops/rebase.js.map +1 -0
  602. package/dist/ops/retention.d.ts +29 -0
  603. package/dist/ops/retention.d.ts.map +1 -0
  604. package/dist/ops/retention.js +120 -0
  605. package/dist/ops/retention.js.map +1 -0
  606. package/dist/ops/retry.d.ts +19 -0
  607. package/dist/ops/retry.d.ts.map +1 -0
  608. package/dist/ops/retry.js +106 -0
  609. package/dist/ops/retry.js.map +1 -0
  610. package/dist/ops/summary.d.ts +42 -0
  611. package/dist/ops/summary.d.ts.map +1 -0
  612. package/dist/ops/summary.js +86 -0
  613. package/dist/ops/summary.js.map +1 -0
  614. package/dist/ops/sync.d.ts +47 -0
  615. package/dist/ops/sync.d.ts.map +1 -0
  616. package/dist/ops/sync.js +445 -0
  617. package/dist/ops/sync.js.map +1 -0
  618. package/dist/planning/mode.d.ts +14 -0
  619. package/dist/planning/mode.d.ts.map +1 -0
  620. package/dist/planning/mode.js +33 -0
  621. package/dist/planning/mode.js.map +1 -0
  622. package/dist/poller/control.d.ts +21 -0
  623. package/dist/poller/control.d.ts.map +1 -0
  624. package/dist/poller/control.js +42 -0
  625. package/dist/poller/control.js.map +1 -0
  626. package/dist/poller/shutdown.d.ts +20 -0
  627. package/dist/poller/shutdown.d.ts.map +1 -0
  628. package/dist/poller/shutdown.js +94 -0
  629. package/dist/poller/shutdown.js.map +1 -0
  630. package/dist/publishing/pr-body.d.ts +25 -0
  631. package/dist/publishing/pr-body.d.ts.map +1 -0
  632. package/dist/publishing/pr-body.js +119 -0
  633. package/dist/publishing/pr-body.js.map +1 -0
  634. package/dist/publishing/publisher.d.ts +19 -0
  635. package/dist/publishing/publisher.d.ts.map +1 -0
  636. package/dist/publishing/publisher.js +116 -0
  637. package/dist/publishing/publisher.js.map +1 -0
  638. package/dist/publishing/push.d.ts +13 -0
  639. package/dist/publishing/push.d.ts.map +1 -0
  640. package/dist/publishing/push.js +56 -0
  641. package/dist/publishing/push.js.map +1 -0
  642. package/dist/reactions/handler.d.ts +20 -0
  643. package/dist/reactions/handler.d.ts.map +1 -0
  644. package/dist/reactions/handler.js +50 -0
  645. package/dist/reactions/handler.js.map +1 -0
  646. package/dist/reactions/scanner.d.ts +13 -0
  647. package/dist/reactions/scanner.d.ts.map +1 -0
  648. package/dist/reactions/scanner.js +141 -0
  649. package/dist/reactions/scanner.js.map +1 -0
  650. package/dist/reactions/types.d.ts +41 -0
  651. package/dist/reactions/types.d.ts.map +1 -0
  652. package/dist/reactions/types.js +2 -0
  653. package/dist/reactions/types.js.map +1 -0
  654. package/dist/runner/poller.d.ts +19 -0
  655. package/dist/runner/poller.d.ts.map +1 -0
  656. package/dist/runner/poller.js +1358 -0
  657. package/dist/runner/poller.js.map +1 -0
  658. package/dist/settings/registry.d.ts +37 -0
  659. package/dist/settings/registry.d.ts.map +1 -0
  660. package/dist/settings/registry.js +299 -0
  661. package/dist/settings/registry.js.map +1 -0
  662. package/dist/settings/runtime.d.ts +33 -0
  663. package/dist/settings/runtime.d.ts.map +1 -0
  664. package/dist/settings/runtime.js +148 -0
  665. package/dist/settings/runtime.js.map +1 -0
  666. package/dist/state/db.d.ts +3 -0
  667. package/dist/state/db.d.ts.map +1 -0
  668. package/dist/state/db.js +80 -0
  669. package/dist/state/db.js.map +1 -0
  670. package/dist/state/issues.d.ts +47 -0
  671. package/dist/state/issues.d.ts.map +1 -0
  672. package/dist/state/issues.js +188 -0
  673. package/dist/state/issues.js.map +1 -0
  674. package/dist/state/leases.d.ts +27 -0
  675. package/dist/state/leases.d.ts.map +1 -0
  676. package/dist/state/leases.js +75 -0
  677. package/dist/state/leases.js.map +1 -0
  678. package/dist/state/migrations/001-initial.d.ts +3 -0
  679. package/dist/state/migrations/001-initial.d.ts.map +1 -0
  680. package/dist/state/migrations/001-initial.js +70 -0
  681. package/dist/state/migrations/001-initial.js.map +1 -0
  682. package/dist/state/migrations/002-placeholder.d.ts +7 -0
  683. package/dist/state/migrations/002-placeholder.d.ts.map +1 -0
  684. package/dist/state/migrations/002-placeholder.js +8 -0
  685. package/dist/state/migrations/002-placeholder.js.map +1 -0
  686. package/dist/state/migrations/003-mention-tracking.d.ts +3 -0
  687. package/dist/state/migrations/003-mention-tracking.d.ts.map +1 -0
  688. package/dist/state/migrations/003-mention-tracking.js +13 -0
  689. package/dist/state/migrations/003-mention-tracking.js.map +1 -0
  690. package/dist/state/migrations/004-command-tracking.d.ts +3 -0
  691. package/dist/state/migrations/004-command-tracking.d.ts.map +1 -0
  692. package/dist/state/migrations/004-command-tracking.js +13 -0
  693. package/dist/state/migrations/004-command-tracking.js.map +1 -0
  694. package/dist/state/migrations/005-block-reason.d.ts +3 -0
  695. package/dist/state/migrations/005-block-reason.d.ts.map +1 -0
  696. package/dist/state/migrations/005-block-reason.js +4 -0
  697. package/dist/state/migrations/005-block-reason.js.map +1 -0
  698. package/dist/state/migrations/006-parent-run.d.ts +3 -0
  699. package/dist/state/migrations/006-parent-run.d.ts.map +1 -0
  700. package/dist/state/migrations/006-parent-run.js +4 -0
  701. package/dist/state/migrations/006-parent-run.js.map +1 -0
  702. package/dist/state/migrations/007-merge-queue.d.ts +3 -0
  703. package/dist/state/migrations/007-merge-queue.d.ts.map +1 -0
  704. package/dist/state/migrations/007-merge-queue.js +21 -0
  705. package/dist/state/migrations/007-merge-queue.js.map +1 -0
  706. package/dist/state/migrations/008-agent-events.d.ts +3 -0
  707. package/dist/state/migrations/008-agent-events.d.ts.map +1 -0
  708. package/dist/state/migrations/008-agent-events.js +15 -0
  709. package/dist/state/migrations/008-agent-events.js.map +1 -0
  710. package/dist/state/migrations/009-run-titles.d.ts +3 -0
  711. package/dist/state/migrations/009-run-titles.d.ts.map +1 -0
  712. package/dist/state/migrations/009-run-titles.js +11 -0
  713. package/dist/state/migrations/009-run-titles.js.map +1 -0
  714. package/dist/state/migrations/010-issues.d.ts +9 -0
  715. package/dist/state/migrations/010-issues.d.ts.map +1 -0
  716. package/dist/state/migrations/010-issues.js +193 -0
  717. package/dist/state/migrations/010-issues.js.map +1 -0
  718. package/dist/state/migrations/011-rebuild-issues-from-latest-run.d.ts +8 -0
  719. package/dist/state/migrations/011-rebuild-issues-from-latest-run.d.ts.map +1 -0
  720. package/dist/state/migrations/011-rebuild-issues-from-latest-run.js +125 -0
  721. package/dist/state/migrations/011-rebuild-issues-from-latest-run.js.map +1 -0
  722. package/dist/state/migrations/012-settings-overrides.d.ts +3 -0
  723. package/dist/state/migrations/012-settings-overrides.d.ts.map +1 -0
  724. package/dist/state/migrations/012-settings-overrides.js +14 -0
  725. package/dist/state/migrations/012-settings-overrides.js.map +1 -0
  726. package/dist/state/migrations/013-token-usage.d.ts +3 -0
  727. package/dist/state/migrations/013-token-usage.d.ts.map +1 -0
  728. package/dist/state/migrations/013-token-usage.js +21 -0
  729. package/dist/state/migrations/013-token-usage.js.map +1 -0
  730. package/dist/state/migrations/014-daily-run-usage.d.ts +3 -0
  731. package/dist/state/migrations/014-daily-run-usage.d.ts.map +1 -0
  732. package/dist/state/migrations/014-daily-run-usage.js +14 -0
  733. package/dist/state/migrations/014-daily-run-usage.js.map +1 -0
  734. package/dist/state/migrations/015-run-cost-override.d.ts +11 -0
  735. package/dist/state/migrations/015-run-cost-override.d.ts.map +1 -0
  736. package/dist/state/migrations/015-run-cost-override.js +20 -0
  737. package/dist/state/migrations/015-run-cost-override.js.map +1 -0
  738. package/dist/state/migrations/016-daily-cost-cap-override.d.ts +15 -0
  739. package/dist/state/migrations/016-daily-cost-cap-override.d.ts.map +1 -0
  740. package/dist/state/migrations/016-daily-cost-cap-override.js +24 -0
  741. package/dist/state/migrations/016-daily-cost-cap-override.js.map +1 -0
  742. package/dist/state/migrations/017-merge-batch-merged-prs.d.ts +13 -0
  743. package/dist/state/migrations/017-merge-batch-merged-prs.d.ts.map +1 -0
  744. package/dist/state/migrations/017-merge-batch-merged-prs.js +22 -0
  745. package/dist/state/migrations/017-merge-batch-merged-prs.js.map +1 -0
  746. package/dist/state/migrations/018-run-retry-count.d.ts +13 -0
  747. package/dist/state/migrations/018-run-retry-count.d.ts.map +1 -0
  748. package/dist/state/migrations/018-run-retry-count.js +22 -0
  749. package/dist/state/migrations/018-run-retry-count.js.map +1 -0
  750. package/dist/state/migrations/019-runs-active-index-top-level.d.ts +16 -0
  751. package/dist/state/migrations/019-runs-active-index-top-level.d.ts.map +1 -0
  752. package/dist/state/migrations/019-runs-active-index-top-level.js +23 -0
  753. package/dist/state/migrations/019-runs-active-index-top-level.js.map +1 -0
  754. package/dist/state/runs.d.ts +100 -0
  755. package/dist/state/runs.d.ts.map +1 -0
  756. package/dist/state/runs.js +321 -0
  757. package/dist/state/runs.js.map +1 -0
  758. package/dist/state/settings.d.ts +16 -0
  759. package/dist/state/settings.d.ts.map +1 -0
  760. package/dist/state/settings.js +55 -0
  761. package/dist/state/settings.js.map +1 -0
  762. package/dist/state/stats.d.ts +133 -0
  763. package/dist/state/stats.d.ts.map +1 -0
  764. package/dist/state/stats.js +419 -0
  765. package/dist/state/stats.js.map +1 -0
  766. package/dist/supervisor/health.d.ts +24 -0
  767. package/dist/supervisor/health.d.ts.map +1 -0
  768. package/dist/supervisor/health.js +186 -0
  769. package/dist/supervisor/health.js.map +1 -0
  770. package/dist/supervisor/index.d.ts +31 -0
  771. package/dist/supervisor/index.d.ts.map +1 -0
  772. package/dist/supervisor/index.js +387 -0
  773. package/dist/supervisor/index.js.map +1 -0
  774. package/dist/supervisor/status.d.ts +18 -0
  775. package/dist/supervisor/status.d.ts.map +1 -0
  776. package/dist/supervisor/status.js +30 -0
  777. package/dist/supervisor/status.js.map +1 -0
  778. package/dist/supervisor/updater.d.ts +18 -0
  779. package/dist/supervisor/updater.d.ts.map +1 -0
  780. package/dist/supervisor/updater.js +108 -0
  781. package/dist/supervisor/updater.js.map +1 -0
  782. package/dist/utils/build-info.d.ts +6 -0
  783. package/dist/utils/build-info.d.ts.map +1 -0
  784. package/dist/utils/build-info.js +56 -0
  785. package/dist/utils/build-info.js.map +1 -0
  786. package/dist/utils/command.d.ts +8 -0
  787. package/dist/utils/command.d.ts.map +1 -0
  788. package/dist/utils/command.js +71 -0
  789. package/dist/utils/command.js.map +1 -0
  790. package/dist/utils/ids.d.ts +4 -0
  791. package/dist/utils/ids.d.ts.map +1 -0
  792. package/dist/utils/ids.js +17 -0
  793. package/dist/utils/ids.js.map +1 -0
  794. package/dist/utils/install-method.d.ts +4 -0
  795. package/dist/utils/install-method.d.ts.map +1 -0
  796. package/dist/utils/install-method.js +13 -0
  797. package/dist/utils/install-method.js.map +1 -0
  798. package/dist/utils/issue-repo.d.ts +2 -0
  799. package/dist/utils/issue-repo.d.ts.map +1 -0
  800. package/dist/utils/issue-repo.js +7 -0
  801. package/dist/utils/issue-repo.js.map +1 -0
  802. package/dist/utils/logger.d.ts +7 -0
  803. package/dist/utils/logger.d.ts.map +1 -0
  804. package/dist/utils/logger.js +63 -0
  805. package/dist/utils/logger.js.map +1 -0
  806. package/dist/utils/project-root.d.ts +8 -0
  807. package/dist/utils/project-root.d.ts.map +1 -0
  808. package/dist/utils/project-root.js +22 -0
  809. package/dist/utils/project-root.js.map +1 -0
  810. package/dist/utils/sanitize-error.d.ts +36 -0
  811. package/dist/utils/sanitize-error.d.ts.map +1 -0
  812. package/dist/utils/sanitize-error.js +89 -0
  813. package/dist/utils/sanitize-error.js.map +1 -0
  814. package/dist/utils/time.d.ts +7 -0
  815. package/dist/utils/time.d.ts.map +1 -0
  816. package/dist/utils/time.js +47 -0
  817. package/dist/utils/time.js.map +1 -0
  818. package/dist/web/server.d.ts +14 -0
  819. package/dist/web/server.d.ts.map +1 -0
  820. package/dist/web/server.js +1185 -0
  821. package/dist/web/server.js.map +1 -0
  822. package/dist/workers/acp.d.ts +9 -0
  823. package/dist/workers/acp.d.ts.map +1 -0
  824. package/dist/workers/acp.js +190 -0
  825. package/dist/workers/acp.js.map +1 -0
  826. package/dist/workers/acpx-imports.d.ts +18 -0
  827. package/dist/workers/acpx-imports.d.ts.map +1 -0
  828. package/dist/workers/acpx-imports.js +43 -0
  829. package/dist/workers/acpx-imports.js.map +1 -0
  830. package/dist/workers/claude.d.ts +9 -0
  831. package/dist/workers/claude.d.ts.map +1 -0
  832. package/dist/workers/claude.js +341 -0
  833. package/dist/workers/claude.js.map +1 -0
  834. package/dist/workers/codex.d.ts +21 -0
  835. package/dist/workers/codex.d.ts.map +1 -0
  836. package/dist/workers/codex.js +337 -0
  837. package/dist/workers/codex.js.map +1 -0
  838. package/dist/workers/command.d.ts +6 -0
  839. package/dist/workers/command.d.ts.map +1 -0
  840. package/dist/workers/command.js +15 -0
  841. package/dist/workers/command.js.map +1 -0
  842. package/dist/workers/env.d.ts +18 -0
  843. package/dist/workers/env.d.ts.map +1 -0
  844. package/dist/workers/env.js +172 -0
  845. package/dist/workers/env.js.map +1 -0
  846. package/dist/workers/events.d.ts +7 -0
  847. package/dist/workers/events.d.ts.map +1 -0
  848. package/dist/workers/events.js +30 -0
  849. package/dist/workers/events.js.map +1 -0
  850. package/dist/workers/factory.d.ts +6 -0
  851. package/dist/workers/factory.d.ts.map +1 -0
  852. package/dist/workers/factory.js +13 -0
  853. package/dist/workers/factory.js.map +1 -0
  854. package/dist/workers/parsers/coder.d.ts +6 -0
  855. package/dist/workers/parsers/coder.d.ts.map +1 -0
  856. package/dist/workers/parsers/coder.js +59 -0
  857. package/dist/workers/parsers/coder.js.map +1 -0
  858. package/dist/workers/parsers/decomposer.d.ts +16 -0
  859. package/dist/workers/parsers/decomposer.d.ts.map +1 -0
  860. package/dist/workers/parsers/decomposer.js +35 -0
  861. package/dist/workers/parsers/decomposer.js.map +1 -0
  862. package/dist/workers/parsers/extract.d.ts +41 -0
  863. package/dist/workers/parsers/extract.d.ts.map +1 -0
  864. package/dist/workers/parsers/extract.js +231 -0
  865. package/dist/workers/parsers/extract.js.map +1 -0
  866. package/dist/workers/parsers/planner.d.ts +6 -0
  867. package/dist/workers/parsers/planner.d.ts.map +1 -0
  868. package/dist/workers/parsers/planner.js +77 -0
  869. package/dist/workers/parsers/planner.js.map +1 -0
  870. package/dist/workers/parsers/reviewer.d.ts +6 -0
  871. package/dist/workers/parsers/reviewer.d.ts.map +1 -0
  872. package/dist/workers/parsers/reviewer.js +105 -0
  873. package/dist/workers/parsers/reviewer.js.map +1 -0
  874. package/dist/workers/prompt/compiler.d.ts +11 -0
  875. package/dist/workers/prompt/compiler.d.ts.map +1 -0
  876. package/dist/workers/prompt/compiler.js +199 -0
  877. package/dist/workers/prompt/compiler.js.map +1 -0
  878. package/dist/workers/prompt/templates.d.ts +13 -0
  879. package/dist/workers/prompt/templates.d.ts.map +1 -0
  880. package/dist/workers/prompt/templates.js +110 -0
  881. package/dist/workers/prompt/templates.js.map +1 -0
  882. package/dist/workers/registry.d.ts +9 -0
  883. package/dist/workers/registry.d.ts.map +1 -0
  884. package/dist/workers/registry.js +21 -0
  885. package/dist/workers/registry.js.map +1 -0
  886. package/dist/workers/streaming-exec.d.ts +26 -0
  887. package/dist/workers/streaming-exec.d.ts.map +1 -0
  888. package/dist/workers/streaming-exec.js +166 -0
  889. package/dist/workers/streaming-exec.js.map +1 -0
  890. package/dist/workers/timeout.d.ts +17 -0
  891. package/dist/workers/timeout.d.ts.map +1 -0
  892. package/dist/workers/timeout.js +37 -0
  893. package/dist/workers/timeout.js.map +1 -0
  894. package/dist/workers/types.d.ts +120 -0
  895. package/dist/workers/types.d.ts.map +1 -0
  896. package/dist/workers/types.js +2 -0
  897. package/dist/workers/types.js.map +1 -0
  898. package/docker-compose.example.yaml +29 -0
  899. package/examples/config.example.yaml +256 -0
  900. package/examples/night-orch.service +40 -0
  901. package/monitoring/grafana/dashboards/night-orch.json +140 -0
  902. package/monitoring/grafana/provisioning/dashboards/dashboards.yml +10 -0
  903. package/monitoring/grafana/provisioning/datasources/prometheus.yml +9 -0
  904. package/monitoring/prometheus.yml +8 -0
  905. package/package.json +104 -0
  906. package/web/dist/assets/index-CBFNqVuV.js +9 -0
  907. package/web/dist/assets/index-RCNGmuI2.css +1 -0
  908. package/web/dist/icon.svg +10 -0
  909. package/web/dist/index.html +22 -0
  910. package/web/dist/manifest.webmanifest +16 -0
  911. package/web/dist/sw.js +58 -0
@@ -0,0 +1,1358 @@
1
+ import { createForgeAdapter } from '../forge/factory.js';
2
+ import { LeaseManager } from '../state/leases.js';
3
+ import { RunManager } from '../state/runs.js';
4
+ import { IssueManager } from '../state/issues.js';
5
+ import { discoverEligibleIssues } from '../discovery/discover.js';
6
+ import { resolveRoles } from '../discovery/roles.js';
7
+ import { adjustLimitsForTriage } from '../discovery/triage.js';
8
+ import { getOrPinSlug, buildWorktreePath } from '../git/slug.js';
9
+ import { createWorktreeManager } from '../git/worktree.js';
10
+ import { resolveEnvironmentMode, setupEnvironment, teardownEnvironment, } from '../environment/manager.js';
11
+ import { createWorkerAdapter } from '../workers/factory.js';
12
+ import { executeLoop } from '../loop/engine.js';
13
+ import { resolveWorkflow } from '../loop/workflow.js';
14
+ import { publishPR } from '../publishing/publisher.js';
15
+ import { MergeConflictError } from '../publishing/push.js';
16
+ import { transitionLabels } from '../labels/manager.js';
17
+ import { buildLabelConfig } from '../labels/config.js';
18
+ import { NotificationDispatcher } from '../notify/dispatcher.js';
19
+ import { createChannels } from '../notify/factory.js';
20
+ import { CostTracker } from '../loop/cost.js';
21
+ import { branchName } from '../utils/ids.js';
22
+ import { logger } from '../utils/logger.js';
23
+ import { nowUtcIso } from '../utils/time.js';
24
+ import { postPlanSummaryComment } from '../loop/plan-summary-comment.js';
25
+ import { executeRebase, queueRebase } from '../ops/rebase-and-check.js';
26
+ import { queueContinue } from '../ops/continue.js';
27
+ import { markerTag, upsertBotComment } from '../forge/bot-comment.js';
28
+ import { formatStatusComment } from '../forge/status-comment.js';
29
+ import { scanForReactions } from '../reactions/scanner.js';
30
+ import { handleReaction } from '../reactions/handler.js';
31
+ import { processMergeQueue } from '../merge-queue/runner.js';
32
+ import { decomposeIssue, shouldAttemptDecompose } from '../discovery/decomposer.js';
33
+ import { executeParallelSubtasks } from '../loop/parallel.js';
34
+ import { buildWorkerEnv } from '../workers/env.js';
35
+ import { isPlanningIssue } from '../planning/mode.js';
36
+ import { resolveIssueRepo } from '../utils/issue-repo.js';
37
+ import { isCommandProcessed, markCommandProcessed, parseOrchCommands, } from '../discovery/commands.js';
38
+ import { AgentObservability, setActiveAgentObservability, clearActiveAgentObservability, } from '../events/observability.js';
39
+ const STATUS_MARKER = markerTag('status');
40
+ /**
41
+ * Process one poll cycle: discover eligible issues, claim and process.
42
+ * Repositories are processed in parallel; each repo runs up to
43
+ * `repo.maxConcurrentRuns` issues concurrently (default: 1).
44
+ */
45
+ export async function pollOnce(config, db, dryRun, metrics, targetIssue) {
46
+ const leaseManager = new LeaseManager(db);
47
+ const runManager = new RunManager(db);
48
+ const issueManager = new IssueManager(db);
49
+ const worktreeManager = createWorktreeManager();
50
+ const costTracker = new CostTracker(db);
51
+ let processed = 0;
52
+ let errors = 0;
53
+ const immediateFollowupRepos = new Set();
54
+ const observability = new AgentObservability(db, config);
55
+ setActiveAgentObservability(observability);
56
+ try {
57
+ // Update active runs gauge
58
+ try {
59
+ const activeRuns = runManager.getActive();
60
+ metrics?.setActiveRuns(activeRuns.length);
61
+ metrics?.setDailyCost(costTracker.getDailyCost());
62
+ }
63
+ catch { /* best-effort */ }
64
+ // Clean expired leases
65
+ leaseManager.cleanExpired();
66
+ const reposToProcess = targetIssue
67
+ ? config.repos.filter((repoConfig) => {
68
+ const issueRepos = new Set([repoConfig.repo, ...(repoConfig.linkedProjects ?? [])]);
69
+ return issueRepos.has(targetIssue.repo);
70
+ })
71
+ : config.repos;
72
+ const usedPortsInPass = [];
73
+ const repoResults = await Promise.all(reposToProcess.map(async (repoConfig) => {
74
+ let repoProcessed = 0;
75
+ let repoErrors = 0;
76
+ const repoImmediateFollowupRepos = new Set();
77
+ try {
78
+ const forge = createForgeAdapter(repoConfig, config);
79
+ const channels = createChannels(config.notifications, forge);
80
+ const notifier = new NotificationDispatcher(channels, config.notifications.events);
81
+ // Resolve bot user for comment upserts (best-effort, fallback to empty string)
82
+ let botUser = '';
83
+ try {
84
+ const authInfo = await forge.validateAuth();
85
+ botUser = authInfo.user;
86
+ }
87
+ catch {
88
+ logger.debug({ repo: repoConfig.repo }, 'Could not resolve bot user for comment upserts');
89
+ }
90
+ // --- Reaction scan: check review_ready PRs for CI failures or human reviews ---
91
+ try {
92
+ await scanAndHandleReactions({
93
+ db, forge, runManager, repoConfig, botUser,
94
+ });
95
+ }
96
+ catch (err) {
97
+ logger.warn({ repo: repoConfig.repo, err }, 'Reaction scan failed — continuing with issue discovery');
98
+ }
99
+ // --- Merge queue: process pending merges before discovering new work ---
100
+ try {
101
+ await processMergeQueue(db, forge, repoConfig);
102
+ }
103
+ catch (err) {
104
+ logger.warn({ repo: repoConfig.repo, err }, 'Merge queue processing failed — continuing');
105
+ }
106
+ // --- Comment commands: /orch retry|rebase|continue|cancel ---
107
+ try {
108
+ await processCommentCommands({
109
+ config,
110
+ db,
111
+ forge,
112
+ runManager,
113
+ leaseManager,
114
+ repoConfig,
115
+ botUser,
116
+ });
117
+ }
118
+ catch (err) {
119
+ logger.warn({ repo: repoConfig.repo, err }, 'Comment command processing failed — continuing');
120
+ }
121
+ const discoveredAll = await discoverEligibleIssues(repoConfig, forge, leaseManager);
122
+ const discovered = targetIssue
123
+ ? discoveredAll.filter((d) => {
124
+ const issueRepo = d.issueRepo || d.issue.repo || repoConfig.repo;
125
+ return d.issue.number === targetIssue.issueNumber && issueRepo === targetIssue.repo;
126
+ })
127
+ : prioritizeDiscoveredIssues(runManager, repoConfig.repo, discoveredAll);
128
+ issueManager.upsertDiscovered(discovered.map((d) => ({
129
+ repo: d.issueRepo || d.issue.repo || repoConfig.repo,
130
+ issueNumber: d.issue.number,
131
+ issueNodeId: d.issue.nodeId,
132
+ issueTitle: d.issue.title,
133
+ })));
134
+ try {
135
+ metrics?.setEligibleIssues(repoConfig.repo, discovered.length);
136
+ }
137
+ catch { /* best-effort */ }
138
+ if (discovered.length === 0) {
139
+ logger.debug({ repo: repoConfig.repo }, 'No eligible issues');
140
+ return {
141
+ processed: repoProcessed,
142
+ errors: repoErrors,
143
+ immediateFollowupRepos: [],
144
+ };
145
+ }
146
+ if (dryRun) {
147
+ for (const d of discovered) {
148
+ logger.info({ issue: d.issue.number, triage: d.triage.level, title: d.issue.title }, '[dry-run] Discovered issue');
149
+ }
150
+ return {
151
+ processed: repoProcessed,
152
+ errors: repoErrors,
153
+ immediateFollowupRepos: [],
154
+ };
155
+ }
156
+ const maxConcurrentRuns = targetIssue ? 1 : (repoConfig.maxConcurrentRuns ?? 1);
157
+ const discoveredQueue = [...discovered];
158
+ const workerCount = Math.min(maxConcurrentRuns, discoveredQueue.length);
159
+ await Promise.all(Array.from({ length: workerCount }, async () => {
160
+ while (true) {
161
+ const discoveredIssue = discoveredQueue.shift();
162
+ if (!discoveredIssue) {
163
+ break;
164
+ }
165
+ const issueRepo = discoveredIssue.issueRepo || discoveredIssue.issue.repo || repoConfig.repo;
166
+ if (discoveredIssue.triage.level === 'architectural') {
167
+ const labelConfig = buildLabelConfig(repoConfig, discoveredIssue.issue.labels);
168
+ await forge.addLabels(issueRepo, discoveredIssue.issue.number, [labelConfig.needsHuman]);
169
+ const archBody = formatStatusComment({ blockReason: 'This issue is classified as architectural and requires human guidance.' });
170
+ if (botUser) {
171
+ await upsertBotComment(forge, issueRepo, discoveredIssue.issue.number, STATUS_MARKER, archBody, botUser);
172
+ }
173
+ else {
174
+ await forge.commentOnIssue(issueRepo, discoveredIssue.issue.number, `🏗️ **night-orch**: This issue is classified as architectural and requires human guidance.`);
175
+ }
176
+ continue;
177
+ }
178
+ // Lease duration is deliberately short (30 min) so a
179
+ // crashed poller's leases expire promptly. The engine
180
+ // bumps the deadline on every phase checkpoint via
181
+ // leaseHeartbeat below, so a long run is not at risk of
182
+ // expiring mid-work.
183
+ if (!leaseManager.acquire(issueRepo, discoveredIssue.issue.number, 'poller', 1800)) {
184
+ continue;
185
+ }
186
+ let runId = null;
187
+ let envSetup = null;
188
+ let activeWorktreePath = null;
189
+ try {
190
+ const workflow = resolveWorkflow(repoConfig, config, discoveredIssue.issue.labels, discoveredIssue.triage.level);
191
+ const repoConfigForRun = applyWorkflowAgentOverrides(repoConfig, workflow);
192
+ const roleDefaults = applyWorkflowRoleDefaults(repoConfigForRun.defaults, workflow, repoConfigForRun, config);
193
+ const resolvedRoles = resolveRoles(discoveredIssue.issue.labels, roleDefaults);
194
+ const queuedRun = runManager.getLatestQueuedByIssue(repoConfig.repo, discoveredIssue.issue.number);
195
+ const replayableRun = queuedRun
196
+ ? null
197
+ : selectReplayableRun(runManager.getByRepoAndIssue(repoConfig.repo, discoveredIssue.issue.number));
198
+ const activeRun = queuedRun ?? replayableRun;
199
+ const roles = activeRun
200
+ ? {
201
+ planner: coerceAgentName(activeRun.planner, resolvedRoles.planner),
202
+ coder: coerceAgentName(activeRun.coder, resolvedRoles.coder),
203
+ reviewer: coerceAgentName(activeRun.reviewer, resolvedRoles.reviewer),
204
+ }
205
+ : resolvedRoles;
206
+ const slug = getOrPinSlug(db, repoConfig.repo, discoveredIssue.issue.number, discoveredIssue.issue.title);
207
+ const branch = branchName(repoConfig.branchPrefix, discoveredIssue.issue.number, slug);
208
+ const worktreePath = buildWorktreePath(config.storage.worktreeRoot, repoConfig.repo, discoveredIssue.issue.number);
209
+ activeWorktreePath = worktreePath;
210
+ const run = activeRun ?? runManager.create({
211
+ repo: repoConfig.repo,
212
+ issueNumber: discoveredIssue.issue.number,
213
+ issueTitle: discoveredIssue.issue.title,
214
+ issueNodeId: discoveredIssue.issue.nodeId,
215
+ planner: roles.planner,
216
+ coder: roles.coder,
217
+ reviewer: roles.reviewer,
218
+ });
219
+ const startingIteration = activeRun ? Math.max(activeRun.iterationCount, 1) : 1;
220
+ const previousRunStatus = run.status;
221
+ if (replayableRun) {
222
+ logger.info({ repo: repoConfig.repo, issue: discoveredIssue.issue.number, runId: run.id, status: replayableRun.status }, 'Re-queuing active run for rediscovered ready issue');
223
+ }
224
+ runId = run.id;
225
+ runManager.update(run.id, {
226
+ status: 'running',
227
+ iterationCount: startingIteration,
228
+ issueTitle: discoveredIssue.issue.title,
229
+ branchName: branch,
230
+ branchSlug: slug,
231
+ worktreePath,
232
+ phaseData: {
233
+ ...(run.phaseData ?? {}),
234
+ issueRepo,
235
+ },
236
+ endedAt: null,
237
+ lastError: null,
238
+ blockReason: null,
239
+ });
240
+ // Label transition
241
+ await transitionLabels(forge, issueRepo, discoveredIssue.issue.number, discoveredIssue.issue.labels, previousRunStatus, 'running', buildLabelConfig(repoConfig, discoveredIssue.issue.labels));
242
+ // Notify
243
+ await notifier.dispatch(makePayload('run_started', repoConfig.repo, discoveredIssue.issue));
244
+ // Detect if this queued run needs a forced branch reset (e.g., after merge conflict)
245
+ const forceReset = activeRun?.blockReason === 'merge_conflict';
246
+ // Detect rebase mode from queued run's phaseData
247
+ // If force-resetting, ignore stale rebase context — we're starting fresh
248
+ const reactionType = activeRun?.phaseData?.reactionType;
249
+ const isRebaseRun = !forceReset && (reactionType === 'rebase' || reactionType === 'merge_conflict');
250
+ const followupPromptFeedback = extractFollowupPromptFeedback(activeRun?.phaseData);
251
+ // Check if prior run left tainted work that should be discarded
252
+ // Never reset to base for rebase runs — we need the existing branch
253
+ const planningMode = isPlanningIssue(discoveredIssue.issue.labels, repoConfigForRun);
254
+ const resetToBase = forceReset || (!isRebaseRun && (planningMode || shouldResetBranch(runManager, repoConfig.repo, discoveredIssue.issue.number, run.id)));
255
+ // Create worktree
256
+ await worktreeManager.ensure({
257
+ repoLocalPath: repoConfig.localPath,
258
+ baseBranch: repoConfig.baseBranch,
259
+ branchName: branch,
260
+ worktreePath,
261
+ resetToBase,
262
+ });
263
+ // Execute rebase if this is a rebase-queued run
264
+ if (isRebaseRun) {
265
+ logger.info({ repo: repoConfig.repo, issue: discoveredIssue.issue.number, runId: run.id }, 'Executing rebase for queued rebase run');
266
+ const verifyCommands = repoConfig.verify ?? [];
267
+ const rebaseResult = await executeRebase(repoConfig.localPath, worktreePath, branch, repoConfig.baseBranch, issueRepo, discoveredIssue.issue.number, verifyCommands);
268
+ if (rebaseResult.conflict) {
269
+ // Rebase had conflicts — block the run; retry will reset the branch and re-implement
270
+ runManager.update(run.id, {
271
+ status: 'blocked',
272
+ blockReason: 'merge_conflict',
273
+ lastError: 'Rebase failed due to merge conflicts — retry will reset the branch and re-implement from scratch',
274
+ endedAt: nowUtcIso(),
275
+ });
276
+ const latestIssue = await forge.getIssue(issueRepo, discoveredIssue.issue.number);
277
+ await transitionLabels(forge, issueRepo, discoveredIssue.issue.number, latestIssue.labels, 'running', 'blocked', buildLabelConfig(repoConfig, latestIssue.labels), 'merge_conflict');
278
+ await postStatusComment({
279
+ forge,
280
+ issueRepo,
281
+ issueNumber: discoveredIssue.issue.number,
282
+ botUser,
283
+ body: formatStatusComment({
284
+ blockReason: 'Rebase failed due to merge conflicts while replaying the branch onto the latest base.',
285
+ nextStep: 'Run /orch retry to reset the branch to base and re-implement on top of latest main.',
286
+ }),
287
+ warnMessage: 'Failed to post rebase merge-conflict status comment',
288
+ });
289
+ await notifier.dispatch(makePayload('blocked', repoConfig.repo, discoveredIssue.issue, {
290
+ summary: 'Rebase failed due to merge conflicts',
291
+ blockingReason: 'merge_conflict',
292
+ }));
293
+ leaseManager.release(issueRepo, discoveredIssue.issue.number);
294
+ repoErrors++;
295
+ continue;
296
+ }
297
+ if (rebaseResult.rebased && rebaseResult.verifyPassed) {
298
+ // Rebase succeeded and verify passes — done, transition back to review_ready
299
+ logger.info({ repo: repoConfig.repo, issue: discoveredIssue.issue.number }, 'Rebase succeeded, verify passed — returning to review_ready');
300
+ runManager.update(run.id, {
301
+ status: 'review_ready',
302
+ endedAt: nowUtcIso(),
303
+ lastError: null,
304
+ });
305
+ const latestIssue = await forge.getIssue(issueRepo, discoveredIssue.issue.number);
306
+ await transitionLabels(forge, issueRepo, discoveredIssue.issue.number, latestIssue.labels, 'running', 'review_ready', buildLabelConfig(repoConfig, latestIssue.labels));
307
+ await notifier.dispatch(makePayload('pr_ready', repoConfig.repo, discoveredIssue.issue, {
308
+ summary: 'Rebased successfully, verify passed',
309
+ }));
310
+ leaseManager.release(issueRepo, discoveredIssue.issue.number);
311
+ repoProcessed++;
312
+ continue;
313
+ }
314
+ if (!rebaseResult.rebased && rebaseResult.verifyPassed) {
315
+ // Already up-to-date and verify passes — nothing to do
316
+ logger.info({ repo: repoConfig.repo, issue: discoveredIssue.issue.number }, 'Branch already up to date — returning to review_ready');
317
+ runManager.update(run.id, {
318
+ status: 'review_ready',
319
+ endedAt: nowUtcIso(),
320
+ lastError: null,
321
+ });
322
+ const latestIssue = await forge.getIssue(issueRepo, discoveredIssue.issue.number);
323
+ await transitionLabels(forge, issueRepo, discoveredIssue.issue.number, latestIssue.labels, 'running', 'review_ready', buildLabelConfig(repoConfig, latestIssue.labels));
324
+ leaseManager.release(issueRepo, discoveredIssue.issue.number);
325
+ repoProcessed++;
326
+ continue;
327
+ }
328
+ // Rebase succeeded but verify failed — fall through to the loop engine
329
+ // so the coder can fix the issues introduced by upstream changes
330
+ logger.info({ repo: repoConfig.repo, issue: discoveredIssue.issue.number }, 'Rebase done but verify failed — entering code loop to fix');
331
+ }
332
+ if (repoConfigForRun.environment) {
333
+ const mode = resolveEnvironmentMode(discoveredIssue.issue.labels, repoConfigForRun);
334
+ envSetup = await setupEnvironment({
335
+ worktreePath,
336
+ issueNumber: discoveredIssue.issue.number,
337
+ repoConfig: repoConfigForRun,
338
+ mode,
339
+ usedPorts: usedPortsInPass,
340
+ });
341
+ }
342
+ // Get worker adapters
343
+ const plannerProfile = resolveWorkerProfileForAgent(roles.planner, repoConfigForRun, config);
344
+ const coderProfile = resolveWorkerProfileForAgent(roles.coder, repoConfigForRun, config);
345
+ const reviewerProfile = resolveWorkerProfileForAgent(roles.reviewer, repoConfigForRun, config);
346
+ const adjustedLimits = adjustLimitsForTriage(config.loop, plannerProfile?.workerTimeoutSeconds ?? 1800, discoveredIssue.triage);
347
+ if (!plannerProfile || !coderProfile || !reviewerProfile) {
348
+ throw new Error('Missing worker profiles for resolved roles');
349
+ }
350
+ const initialCtx = {
351
+ runId: run.id,
352
+ repo: repoConfig.repo,
353
+ issueRepo,
354
+ issueNumber: discoveredIssue.issue.number,
355
+ issue: discoveredIssue.issue,
356
+ repoConfig: repoConfigForRun,
357
+ roles,
358
+ triageResult: discoveredIssue.triage,
359
+ adjustedLimits,
360
+ branchName: branch,
361
+ worktreePath,
362
+ plan: null,
363
+ codeResult: null,
364
+ diff: null,
365
+ verifyResults: [],
366
+ reviewResult: null,
367
+ reviewFindings: [],
368
+ iteration: startingIteration,
369
+ totalAgentPasses: 0,
370
+ estimatedCostUsd: 0,
371
+ currentPhase: workflow.steps[0]?.id ?? 'plan',
372
+ terminalStatus: 'running',
373
+ phaseHistory: [],
374
+ dryRun: false,
375
+ runMode: isRebaseRun ? 'rebase' : followupPromptFeedback ? 'followup' : 'fresh',
376
+ blockReason: null,
377
+ prReviewFeedback: followupPromptFeedback,
378
+ sessionIds: {},
379
+ stepOutputs: {},
380
+ };
381
+ // Check if decomposition is enabled and appropriate
382
+ const shouldDecompose = config.loop.decompose
383
+ && discoveredIssue.triage.level === 'standard'
384
+ && !planningMode
385
+ && shouldAttemptDecompose(discoveredIssue.issue);
386
+ if (shouldDecompose) {
387
+ logger.info({ repo: repoConfig.repo, issue: discoveredIssue.issue.number }, 'Attempting issue decomposition');
388
+ const decomposition = await decomposeIssue(discoveredIssue.issue, createWorkerAdapter(plannerProfile), plannerProfile, buildWorkerEnv(plannerProfile, envSetup?.envOverrides ?? {}), worktreePath, config.loop.maxSubtasks);
389
+ if (decomposition.shouldDecompose && decomposition.subtasks.length > 1) {
390
+ logger.info({ repo: repoConfig.repo, issue: discoveredIssue.issue.number, subtasks: decomposition.subtasks.length }, 'Decomposed issue into sub-tasks — executing in parallel');
391
+ const loopDeps = {
392
+ db, config,
393
+ adapters: {
394
+ planner: createWorkerAdapter(plannerProfile),
395
+ coder: createWorkerAdapter(coderProfile),
396
+ reviewer: createWorkerAdapter(reviewerProfile),
397
+ },
398
+ workflow,
399
+ envOverrides: envSetup?.envOverrides ?? {},
400
+ metrics,
401
+ onAgentEvent: (event) => observability.record(event),
402
+ };
403
+ const subResults = await executeParallelSubtasks(initialCtx, decomposition.subtasks, loopDeps, config.loop.maxConcurrentSubtasks);
404
+ const allSucceeded = subResults.every((r) => r.success);
405
+ if (allSucceeded) {
406
+ runManager.update(run.id, { status: 'review_ready', endedAt: nowUtcIso() });
407
+ const latestIssue = await forge.getIssue(issueRepo, discoveredIssue.issue.number);
408
+ await transitionLabels(forge, issueRepo, discoveredIssue.issue.number, latestIssue.labels, 'running', 'review_ready', buildLabelConfig(repoConfig, latestIssue.labels));
409
+ await notifier.dispatch(makePayload('pr_ready', repoConfig.repo, discoveredIssue.issue, {
410
+ summary: `Decomposed into ${decomposition.subtasks.length} sub-tasks, all completed`,
411
+ }));
412
+ repoProcessed++;
413
+ }
414
+ else {
415
+ const failed = subResults.filter((r) => !r.success).length;
416
+ runManager.update(run.id, {
417
+ status: 'blocked',
418
+ lastError: `${failed}/${decomposition.subtasks.length} sub-tasks failed`,
419
+ endedAt: nowUtcIso(),
420
+ });
421
+ const latestIssue = await forge.getIssue(issueRepo, discoveredIssue.issue.number);
422
+ await transitionLabels(forge, issueRepo, discoveredIssue.issue.number, latestIssue.labels, 'running', 'blocked', buildLabelConfig(repoConfig, latestIssue.labels));
423
+ repoErrors++;
424
+ }
425
+ continue;
426
+ }
427
+ }
428
+ // Execute loop (single-issue path)
429
+ const loopStart = Date.now();
430
+ const finalCtx = await executeLoop(initialCtx, {
431
+ db,
432
+ config,
433
+ adapters: {
434
+ planner: createWorkerAdapter(plannerProfile),
435
+ coder: createWorkerAdapter(coderProfile),
436
+ reviewer: createWorkerAdapter(reviewerProfile),
437
+ },
438
+ workflow,
439
+ envOverrides: envSetup?.envOverrides ?? {},
440
+ metrics,
441
+ onAgentEvent: (event) => observability.record(event),
442
+ onPlanReady: async (ctx) => {
443
+ await postPlanSummaryComment(forge, ctx.issueRepo ?? ctx.repo, ctx.issueNumber, ctx.plan, botUser);
444
+ },
445
+ leaseHeartbeat: () => leaseManager.heartbeat(issueRepo, discoveredIssue.issue.number, 'poller', 1800),
446
+ });
447
+ const runDurationSec = (Date.now() - loopStart) / 1000;
448
+ const outcome = await finalizeRunOutcome({
449
+ finalCtx,
450
+ runId: run.id,
451
+ issue: discoveredIssue.issue,
452
+ runDurationSec,
453
+ repo: repoConfig.repo,
454
+ repoConfig,
455
+ issueRepo,
456
+ issueNumber: discoveredIssue.issue.number,
457
+ db,
458
+ forge,
459
+ runManager,
460
+ notifier,
461
+ metrics,
462
+ maxAutoRetries: config.loop.maxAutoRetries,
463
+ botUser,
464
+ });
465
+ if (outcome === 'processed')
466
+ repoProcessed++;
467
+ else
468
+ repoErrors++;
469
+ // Release per-run observability resources (session log
470
+ // streams + event bus history) once the run has reached
471
+ // a terminal state — without this both Maps grow for the
472
+ // daemon's entire lifetime.
473
+ try {
474
+ await observability.closeRun(run.id);
475
+ }
476
+ catch (closeErr) {
477
+ logger.debug({ runId: run.id, err: closeErr }, 'closeRun failed (best-effort)');
478
+ }
479
+ // Evict per-issue caches so they don't grow unbounded.
480
+ cleanupRunCaches(repoConfig.repo, discoveredIssue.issue.number);
481
+ }
482
+ catch (err) {
483
+ logger.error({ repo: repoConfig.repo, issue: discoveredIssue.issue.number, err }, 'Failed to process issue');
484
+ if (runId) {
485
+ const errorMessage = toErrorMessage(err);
486
+ const existing = runManager.getById(runId);
487
+ const currentRetries = existing?.retryCount ?? 0;
488
+ const maxRetries = config.loop.maxAutoRetries;
489
+ const canAutoRetry = currentRetries < maxRetries;
490
+ const attemptCount = currentRetries + 1;
491
+ runManager.update(runId, {
492
+ status: 'error',
493
+ lastError: errorMessage,
494
+ endedAt: nowUtcIso(),
495
+ });
496
+ if (canAutoRetry) {
497
+ // Bump retry_count on the same row so repeated replay
498
+ // retries converge on maxRetries. Without this the
499
+ // same run row cycles error → queued → error forever.
500
+ runManager.incrementRetryCount(runId);
501
+ // Auto-retry: transition back to queued so the next poll picks it up
502
+ logger.info({ repo: repoConfig.repo, issue: discoveredIssue.issue.number, attempt: attemptCount, maxRetries }, 'Infra error — auto-retrying (transitioning back to ready)');
503
+ try {
504
+ const latestIssue = await forge.getIssue(issueRepo, discoveredIssue.issue.number);
505
+ await transitionLabels(forge, issueRepo, discoveredIssue.issue.number, latestIssue.labels, 'running', 'queued', buildLabelConfig(repoConfig, latestIssue.labels));
506
+ }
507
+ catch (labelErr) {
508
+ logger.warn({ repo: repoConfig.repo, issue: discoveredIssue.issue.number, err: labelErr }, 'Failed to transition labels for auto-retry');
509
+ }
510
+ await postErrorStatusComment({
511
+ forge,
512
+ issueRepo,
513
+ issueNumber: discoveredIssue.issue.number,
514
+ botUser,
515
+ error: `Attempt ${attemptCount} failed. Last error: ${errorMessage}`,
516
+ retryCount: attemptCount,
517
+ maxRetries,
518
+ nextStep: 'Automatic retry queued. night-orch will retry this issue on the next poll cycle.',
519
+ warnMessage: 'Failed to post auto-retry status comment',
520
+ });
521
+ }
522
+ else {
523
+ // Retries exhausted: mark as error, require human
524
+ logger.warn({ repo: repoConfig.repo, issue: discoveredIssue.issue.number, currentRetries, maxRetries }, 'Auto-retry limit reached — marking as error');
525
+ try {
526
+ const latestIssue = await forge.getIssue(issueRepo, discoveredIssue.issue.number);
527
+ await transitionLabels(forge, issueRepo, discoveredIssue.issue.number, latestIssue.labels, 'running', 'error', buildLabelConfig(repoConfig, latestIssue.labels));
528
+ }
529
+ catch (labelErr) {
530
+ logger.warn({ repo: repoConfig.repo, issue: discoveredIssue.issue.number, err: labelErr }, 'Failed to transition labels after retry exhaustion');
531
+ }
532
+ await postErrorStatusComment({
533
+ forge,
534
+ issueRepo,
535
+ issueNumber: discoveredIssue.issue.number,
536
+ botUser,
537
+ error: `Failed after ${attemptCount} attempts. Last error: ${errorMessage}`,
538
+ retryCount: attemptCount,
539
+ maxRetries,
540
+ nextStep: 'Manual action required: inspect the failure, then run /orch retry or /orch continue.',
541
+ warnMessage: 'Failed to post retry-exhausted status comment',
542
+ });
543
+ const sanitizedErrorForSummary = sanitizeErrorForComment(errorMessage);
544
+ try {
545
+ await notifier.dispatch(makePayload('retry_exhausted', repoConfig.repo, discoveredIssue.issue, {
546
+ summary: `Failed after ${attemptCount} attempts: ${sanitizedErrorForSummary}`,
547
+ }));
548
+ }
549
+ catch (notifyErr) {
550
+ logger.warn({ repo: repoConfig.repo, issue: discoveredIssue.issue.number, err: notifyErr }, 'Failed to send retry exhaustion notification');
551
+ }
552
+ }
553
+ }
554
+ repoErrors++;
555
+ }
556
+ finally {
557
+ if (runId) {
558
+ const finalRun = runManager.getById(runId);
559
+ if (finalRun && isImmediateFollowupStatus(finalRun.status)) {
560
+ repoImmediateFollowupRepos.add(finalRun.repo);
561
+ }
562
+ }
563
+ if (envSetup && activeWorktreePath) {
564
+ try {
565
+ await teardownEnvironment({
566
+ worktreePath: activeWorktreePath,
567
+ issueNumber: discoveredIssue.issue.number,
568
+ repoConfig,
569
+ mode: envSetup.mode,
570
+ composeProjectName: envSetup.composeProjectName,
571
+ });
572
+ }
573
+ catch (envErr) {
574
+ logger.warn({ repo: repoConfig.repo, issue: discoveredIssue.issue.number, err: envErr }, 'Failed to tear down environment');
575
+ }
576
+ }
577
+ leaseManager.release(issueRepo, discoveredIssue.issue.number);
578
+ }
579
+ }
580
+ }));
581
+ return {
582
+ processed: repoProcessed,
583
+ errors: repoErrors,
584
+ immediateFollowupRepos: [...repoImmediateFollowupRepos],
585
+ };
586
+ }
587
+ catch (err) {
588
+ logger.error({ repo: repoConfig.repo, err }, 'Repository poll failed');
589
+ return {
590
+ processed: repoProcessed,
591
+ errors: repoErrors + 1,
592
+ immediateFollowupRepos: [...repoImmediateFollowupRepos],
593
+ };
594
+ }
595
+ }));
596
+ for (const repoResult of repoResults) {
597
+ processed += repoResult.processed;
598
+ errors += repoResult.errors;
599
+ for (const repo of repoResult.immediateFollowupRepos) {
600
+ immediateFollowupRepos.add(repo);
601
+ }
602
+ }
603
+ return { processed, errors, immediateFollowupRepos: [...immediateFollowupRepos] };
604
+ }
605
+ finally {
606
+ clearActiveAgentObservability(observability);
607
+ await observability.close();
608
+ }
609
+ }
610
+ async function finalizeRunOutcome(params) {
611
+ const { finalCtx, runId, issue, runDurationSec, repo, repoConfig, issueRepo, issueNumber, db, forge, runManager, notifier, metrics, maxAutoRetries, botUser, } = params;
612
+ if (finalCtx.terminalStatus === 'publish') {
613
+ try {
614
+ const publishResult = await publishPR(finalCtx, forge, db);
615
+ runManager.update(runId, {
616
+ status: 'review_ready',
617
+ iterationCount: finalCtx.iteration,
618
+ prNumber: publishResult.prNumber,
619
+ prTitle: publishResult.prTitle,
620
+ endedAt: nowUtcIso(),
621
+ });
622
+ const latestIssue = await forge.getIssue(issueRepo, issueNumber);
623
+ await transitionLabels(forge, issueRepo, issueNumber, latestIssue.labels, 'running', 'review_ready', buildLabelConfig(repoConfig, latestIssue.labels));
624
+ const notificationEvent = publishResult.created ? 'pr_ready' : 'pr_updated';
625
+ const notifyResult = await notifier.dispatch(makePayload(notificationEvent, repo, issue, {
626
+ prUrl: publishResult.prUrl,
627
+ prNumber: publishResult.prNumber,
628
+ summary: publishResult.created
629
+ ? `PR ready: ${publishResult.prUrl}`
630
+ : `PR updated: ${publishResult.prUrl}`,
631
+ }));
632
+ try {
633
+ metrics?.incRunsTotal('completed');
634
+ metrics?.observeRunDuration(runDurationSec);
635
+ metrics?.incPROperations(publishResult.created ? 'created' : 'updated');
636
+ for (const s of notifyResult.sent) {
637
+ metrics?.incNotifications(s.channel, s.success ? 'sent' : 'failed');
638
+ }
639
+ }
640
+ catch { /* best-effort */ }
641
+ return 'processed';
642
+ }
643
+ catch (err) {
644
+ logger.error({ err }, 'Failed to publish PR');
645
+ const errorMessage = toErrorMessage(err);
646
+ // Merge conflicts during push get structured block reason so retry resets the branch
647
+ if (err instanceof MergeConflictError) {
648
+ runManager.update(runId, {
649
+ status: 'blocked',
650
+ iterationCount: finalCtx.iteration,
651
+ blockReason: 'merge_conflict',
652
+ lastError: err.message,
653
+ endedAt: nowUtcIso(),
654
+ });
655
+ const latestIssue = await forge.getIssue(issueRepo, issueNumber);
656
+ await transitionLabels(forge, issueRepo, issueNumber, latestIssue.labels, 'running', 'blocked', buildLabelConfig(repoConfig, latestIssue.labels), 'merge_conflict');
657
+ await postStatusComment({
658
+ forge,
659
+ issueRepo,
660
+ issueNumber,
661
+ botUser,
662
+ body: formatStatusComment({
663
+ blockReason: 'Publish failed due to merge conflicts while pushing branch updates.',
664
+ nextStep: 'Run /orch retry to reset the branch to base and re-implement on top of latest main.',
665
+ }),
666
+ warnMessage: 'Failed to post publish merge-conflict status comment',
667
+ });
668
+ try {
669
+ metrics?.incRunsTotal('blocked');
670
+ metrics?.observeRunDuration(runDurationSec);
671
+ }
672
+ catch { /* best-effort */ }
673
+ return 'error';
674
+ }
675
+ const currentRun = runManager.getById(runId);
676
+ const currentRetries = currentRun?.retryCount ?? 0;
677
+ runManager.update(runId, { status: 'error', iterationCount: finalCtx.iteration, lastError: errorMessage, endedAt: nowUtcIso() });
678
+ const latestIssue = await forge.getIssue(issueRepo, issueNumber);
679
+ if (currentRetries < maxAutoRetries) {
680
+ runManager.incrementRetryCount(runId);
681
+ await transitionLabels(forge, issueRepo, issueNumber, latestIssue.labels, 'running', 'queued', buildLabelConfig(repoConfig, latestIssue.labels));
682
+ logger.info({ repo, issueNumber, attempt: currentRetries + 1, maxAutoRetries }, 'Publish failed — auto-retrying');
683
+ await postErrorStatusComment({
684
+ forge,
685
+ issueRepo,
686
+ issueNumber,
687
+ botUser,
688
+ error: `Publish failed. Last error: ${errorMessage}`,
689
+ retryCount: currentRetries + 1,
690
+ maxRetries: maxAutoRetries,
691
+ nextStep: 'Automatic retry queued. night-orch will retry this issue on the next poll cycle.',
692
+ warnMessage: 'Failed to post publish auto-retry status comment',
693
+ });
694
+ }
695
+ else {
696
+ await transitionLabels(forge, issueRepo, issueNumber, latestIssue.labels, 'running', 'error', buildLabelConfig(repoConfig, latestIssue.labels));
697
+ const attemptCount = currentRetries + 1;
698
+ await postErrorStatusComment({
699
+ forge,
700
+ issueRepo,
701
+ issueNumber,
702
+ botUser,
703
+ error: `Failed after ${attemptCount} attempts. Last error: ${errorMessage}`,
704
+ retryCount: attemptCount,
705
+ maxRetries: maxAutoRetries,
706
+ nextStep: 'Manual action required: inspect the failure, then run /orch retry or /orch continue.',
707
+ warnMessage: 'Failed to post publish retry-exhausted status comment',
708
+ });
709
+ const sanitizedErrorForSummary = sanitizeErrorForComment(errorMessage);
710
+ try {
711
+ await notifier.dispatch(makePayload('retry_exhausted', repo, issue, {
712
+ summary: `Publish failed after ${attemptCount} attempts: ${sanitizedErrorForSummary}`,
713
+ }));
714
+ }
715
+ catch (notifyErr) {
716
+ logger.warn({ repo, issueNumber, err: notifyErr }, 'Failed to send publish retry exhaustion notification');
717
+ }
718
+ }
719
+ try {
720
+ metrics?.incRunsTotal('error');
721
+ metrics?.observeRunDuration(runDurationSec);
722
+ }
723
+ catch { /* best-effort */ }
724
+ return 'error';
725
+ }
726
+ }
727
+ if (finalCtx.terminalStatus === 'blocked') {
728
+ const blockReason = buildBlockReason(finalCtx);
729
+ runManager.update(runId, {
730
+ status: 'blocked',
731
+ iterationCount: finalCtx.iteration,
732
+ lastError: blockReason,
733
+ blockReason: finalCtx.blockReason ?? null,
734
+ endedAt: nowUtcIso(),
735
+ });
736
+ const latestIssue = await forge.getIssue(issueRepo, issueNumber);
737
+ await transitionLabels(forge, issueRepo, issueNumber, latestIssue.labels, 'running', 'blocked', buildLabelConfig(repoConfig, latestIssue.labels), finalCtx.blockReason ?? undefined);
738
+ // Upsert status comment with block reason
739
+ try {
740
+ const statusBody = formatStatusComment({
741
+ blockReason,
742
+ iteration: finalCtx.iteration,
743
+ maxIterations: finalCtx.adjustedLimits.maxReviewIterations,
744
+ cost: finalCtx.estimatedCostUsd,
745
+ });
746
+ if (botUser) {
747
+ await upsertBotComment(forge, issueRepo, issueNumber, STATUS_MARKER, statusBody, botUser);
748
+ }
749
+ else {
750
+ await forge.commentOnIssue(issueRepo, issueNumber, formatBlockComment(blockReason, finalCtx));
751
+ }
752
+ }
753
+ catch (commentErr) {
754
+ logger.warn({ repo, issueNumber, err: commentErr }, 'Failed to post block reason comment');
755
+ }
756
+ const notifyResult = await notifier.dispatch(makePayload('blocked', repo, issue, {
757
+ summary: blockReason,
758
+ blockingReason: blockReason,
759
+ reviewSummary: finalCtx.reviewResult?.summary ?? null,
760
+ }));
761
+ try {
762
+ metrics?.incRunsTotal('blocked');
763
+ metrics?.observeRunDuration(runDurationSec);
764
+ for (const s of notifyResult.sent) {
765
+ metrics?.incNotifications(s.channel, s.success ? 'sent' : 'failed');
766
+ }
767
+ }
768
+ catch { /* best-effort */ }
769
+ return 'processed';
770
+ }
771
+ const unexpectedError = `Loop ended in unexpected state: ${finalCtx.terminalStatus}/${finalCtx.currentPhase}`;
772
+ const currentRunForUnexpected = runManager.getById(runId);
773
+ const currentRetriesUnexpected = currentRunForUnexpected?.retryCount ?? 0;
774
+ runManager.update(runId, { status: 'error', iterationCount: finalCtx.iteration, lastError: unexpectedError, endedAt: nowUtcIso() });
775
+ const latestIssue = await forge.getIssue(issueRepo, issueNumber);
776
+ if (currentRetriesUnexpected < maxAutoRetries) {
777
+ runManager.incrementRetryCount(runId);
778
+ await transitionLabels(forge, issueRepo, issueNumber, latestIssue.labels, 'running', 'queued', buildLabelConfig(repoConfig, latestIssue.labels));
779
+ logger.info({ repo, issueNumber, attempt: currentRetriesUnexpected + 1, maxAutoRetries }, 'Unexpected state — auto-retrying');
780
+ await postErrorStatusComment({
781
+ forge,
782
+ issueRepo,
783
+ issueNumber,
784
+ botUser,
785
+ error: `Loop entered unexpected state: ${finalCtx.terminalStatus}/${finalCtx.currentPhase}`,
786
+ retryCount: currentRetriesUnexpected + 1,
787
+ maxRetries: maxAutoRetries,
788
+ nextStep: 'Automatic retry queued. night-orch will retry this issue on the next poll cycle.',
789
+ warnMessage: 'Failed to post unexpected-state auto-retry status comment',
790
+ });
791
+ }
792
+ else {
793
+ await transitionLabels(forge, issueRepo, issueNumber, latestIssue.labels, 'running', 'error', buildLabelConfig(repoConfig, latestIssue.labels));
794
+ const attemptCount = currentRetriesUnexpected + 1;
795
+ await postErrorStatusComment({
796
+ forge,
797
+ issueRepo,
798
+ issueNumber,
799
+ botUser,
800
+ error: `Failed after ${attemptCount} attempts. Last error: ${unexpectedError}`,
801
+ retryCount: attemptCount,
802
+ maxRetries: maxAutoRetries,
803
+ nextStep: 'Manual action required: inspect the failure, then run /orch retry or /orch continue.',
804
+ warnMessage: 'Failed to post unexpected-state retry-exhausted status comment',
805
+ });
806
+ }
807
+ try {
808
+ metrics?.incRunsTotal('error');
809
+ metrics?.observeRunDuration(runDurationSec);
810
+ }
811
+ catch { /* best-effort */ }
812
+ return 'error';
813
+ }
814
+ function coerceAgentName(value, fallback) {
815
+ if (value === 'claude' || value === 'codex') {
816
+ return value;
817
+ }
818
+ return fallback;
819
+ }
820
+ function isImmediateFollowupStatus(status) {
821
+ return status === 'review_ready'
822
+ || status === 'blocked'
823
+ || status === 'error'
824
+ || status === 'completed';
825
+ }
826
+ function applyWorkflowAgentOverrides(repoConfig, workflow) {
827
+ if (!workflow.agents || Object.keys(workflow.agents).length === 0) {
828
+ return repoConfig;
829
+ }
830
+ return {
831
+ ...repoConfig,
832
+ agents: {
833
+ ...repoConfig.agents,
834
+ ...workflow.agents,
835
+ },
836
+ };
837
+ }
838
+ function applyWorkflowRoleDefaults(repoDefaults, workflow, repoConfig, config) {
839
+ if (!workflow.roles) {
840
+ return repoDefaults;
841
+ }
842
+ const merged = {
843
+ ...repoDefaults,
844
+ ...workflow.roles,
845
+ };
846
+ for (const role of ['planner', 'coder', 'reviewer']) {
847
+ const preferredAgent = merged[role];
848
+ if (canResolveAgent(preferredAgent, repoConfig, config))
849
+ continue;
850
+ merged[role] = repoDefaults[role];
851
+ }
852
+ return merged;
853
+ }
854
+ function canResolveAgent(agent, repoConfig, config) {
855
+ return resolveWorkerProfileForAgent(agent, repoConfig, config) !== null;
856
+ }
857
+ function resolveWorkerProfileForAgent(agent, repoConfig, config) {
858
+ const mappedProfileName = repoConfig.agents[agent];
859
+ if (mappedProfileName) {
860
+ const mappedProfile = config.workerProfiles[mappedProfileName];
861
+ if (mappedProfile)
862
+ return mappedProfile;
863
+ }
864
+ return Object.values(config.workerProfiles).find((profile) => profile.type === agent) ?? null;
865
+ }
866
+ function buildBlockReason(ctx) {
867
+ const blockMessage = ctx.stepOutputs?.['blockMessage'];
868
+ if (typeof blockMessage === 'string' && blockMessage.trim().length > 0) {
869
+ return blockMessage;
870
+ }
871
+ if (ctx.reviewResult) {
872
+ const findings = ctx.reviewResult.findings
873
+ .filter((f) => f.severity === 'critical' || f.severity === 'major')
874
+ .map((f) => `[${f.severity}] ${f.message}`)
875
+ .join('; ');
876
+ return findings
877
+ ? `${ctx.reviewResult.summary} — ${findings}`
878
+ : ctx.reviewResult.summary;
879
+ }
880
+ if (ctx.blockReason) {
881
+ return blockReasonSummary(ctx.blockReason, ctx);
882
+ }
883
+ return `Blocked in phase ${ctx.currentPhase} (no review result available)`;
884
+ }
885
+ function blockReasonSummary(reason, ctx) {
886
+ switch (reason) {
887
+ case 'cost_limit':
888
+ // The engine writes a precise, limit-specific message into stepOutputs.blockMessage
889
+ // (see src/loop/engine.ts cost check). This branch is only reached when that
890
+ // structured message is missing — keep it vague so we do not claim the per-run
891
+ // limit tripped when it might have been the daily cap.
892
+ return `Cost limit exceeded for this run (estimated run cost: $${ctx.estimatedCostUsd.toFixed(4)})`;
893
+ case 'iteration_limit':
894
+ return `Maximum review iterations reached (${ctx.iteration}/${ctx.adjustedLimits.maxReviewIterations})`;
895
+ case 'agent_pass_limit':
896
+ return `Maximum total agent passes reached (${ctx.totalAgentPasses}/${ctx.adjustedLimits.maxTotalAgentPasses})`;
897
+ case 'reviewer_blocked':
898
+ return 'Reviewer marked this run as blocked';
899
+ case 'ambiguous_review':
900
+ return 'Review output was not parseable and blockOnAmbiguousReview is enabled';
901
+ case 'verify_config':
902
+ return 'Verification is required but verify commands or results are unavailable';
903
+ case 'merge_conflict':
904
+ return 'Merge conflict encountered while applying updates';
905
+ default:
906
+ return `Blocked in phase ${ctx.currentPhase}`;
907
+ }
908
+ }
909
+ function formatBlockComment(reason, ctx) {
910
+ const parts = [`⛔ **night-orch**: Run blocked.\n\n**Reason:** ${reason}`];
911
+ if (ctx.reviewResult?.findings && ctx.reviewResult.findings.length > 0) {
912
+ parts.push('\n**Findings:**');
913
+ for (const f of ctx.reviewResult.findings) {
914
+ const fix = f.suggestedFix ? ` → ${f.suggestedFix}` : '';
915
+ parts.push(`- **${f.severity}**: ${f.message}${fix}`);
916
+ }
917
+ }
918
+ parts.push(`\n*Iteration ${ctx.iteration}, cost: $${ctx.estimatedCostUsd.toFixed(4)}*`);
919
+ return parts.join('\n');
920
+ }
921
+ function makePayload(event, repo, issue, extra = {}) {
922
+ return {
923
+ event,
924
+ repo,
925
+ issueNumber: issue.number,
926
+ issueTitle: issue.title,
927
+ issueUrl: issue.url ?? null,
928
+ state: event,
929
+ prUrl: null,
930
+ prNumber: null,
931
+ summary: `${event}: #${issue.number} ${issue.title}`,
932
+ blockingReason: null,
933
+ reviewSummary: null,
934
+ iterationCount: 0,
935
+ timestamp: nowUtcIso(),
936
+ ...extra,
937
+ };
938
+ }
939
+ async function postStatusComment(params) {
940
+ const { forge, issueRepo, issueNumber, botUser, body, warnMessage, } = params;
941
+ try {
942
+ if (botUser) {
943
+ await upsertBotComment(forge, issueRepo, issueNumber, STATUS_MARKER, body, botUser);
944
+ }
945
+ else {
946
+ await forge.commentOnIssue(issueRepo, issueNumber, body);
947
+ }
948
+ }
949
+ catch (commentErr) {
950
+ logger.warn({ repo: issueRepo, issueNumber, err: commentErr }, warnMessage);
951
+ }
952
+ }
953
+ const ERROR_COMMENT_MAX_LENGTH = 400;
954
+ const TOKEN_REDACTION_PATTERNS = [
955
+ /\bgh[pousr]_[A-Za-z0-9_]{20,}\b/g,
956
+ /\bgithub_pat_[A-Za-z0-9_]{20,}\b/g,
957
+ /\bsk-[A-Za-z0-9]{20,}\b/g,
958
+ /\bAKIA[0-9A-Z]{16}\b/g,
959
+ /\bASIA[0-9A-Z]{16}\b/g,
960
+ /\bAIza[0-9A-Za-z\-_]{20,}\b/g,
961
+ /\bxox[baprs]-[A-Za-z0-9-]{20,}\b/g,
962
+ ];
963
+ async function postErrorStatusComment(params) {
964
+ const { forge, issueRepo, issueNumber, botUser, error, retryCount, maxRetries, nextStep, warnMessage, } = params;
965
+ const sanitizedError = sanitizeErrorForComment(error);
966
+ const body = formatStatusComment({
967
+ error: sanitizedError,
968
+ retryCount,
969
+ maxRetries,
970
+ nextStep,
971
+ });
972
+ await postStatusComment({
973
+ forge,
974
+ issueRepo,
975
+ issueNumber,
976
+ botUser,
977
+ body,
978
+ warnMessage,
979
+ });
980
+ }
981
+ function toErrorMessage(err) {
982
+ if (err instanceof Error && typeof err.message === 'string' && err.message.trim().length > 0) {
983
+ return err.message;
984
+ }
985
+ return String(err);
986
+ }
987
+ function sanitizeErrorForComment(errorMessage) {
988
+ let sanitized = errorMessage
989
+ .replace(/[\r\n]+/g, ' ');
990
+ sanitized = stripControlChars(sanitized);
991
+ sanitized = sanitized
992
+ .replace(/\b(token|secret|password|passwd|api[_-]?key)\s*[:=]\s*([^\s,;]+)/gi, '$1=[REDACTED]')
993
+ .trim();
994
+ for (const pattern of TOKEN_REDACTION_PATTERNS) {
995
+ sanitized = sanitized.replace(pattern, '[REDACTED]');
996
+ }
997
+ sanitized = sanitized.replace(/\s+/g, ' ').trim();
998
+ if (!sanitized)
999
+ return 'unknown error';
1000
+ const clipped = sanitized.length > ERROR_COMMENT_MAX_LENGTH
1001
+ ? `${sanitized.slice(0, ERROR_COMMENT_MAX_LENGTH - 1)}…`
1002
+ : sanitized;
1003
+ return escapeMarkdownForComment(clipped);
1004
+ }
1005
+ function escapeMarkdownForComment(value) {
1006
+ return value
1007
+ .replace(/\\/g, '\\\\')
1008
+ .replace(/([`*_#[\]])/g, '\\$1')
1009
+ .replace(/</g, '&lt;')
1010
+ .replace(/>/g, '&gt;')
1011
+ .replace(/@/g, '@\u200B');
1012
+ }
1013
+ function stripControlChars(value) {
1014
+ let out = '';
1015
+ for (const ch of value) {
1016
+ const code = ch.charCodeAt(0);
1017
+ if ((code >= 0 && code <= 31) || code === 127) {
1018
+ out += ' ';
1019
+ continue;
1020
+ }
1021
+ out += ch;
1022
+ }
1023
+ return out;
1024
+ }
1025
+ /** Issues that returned 404 during comment scan in this process lifecycle.
1026
+ * Bounded: entries are evicted when the key's run reaches a terminal state
1027
+ * via {@link cleanupRunCaches}. */
1028
+ const missingCommentCommandIssues = new Set();
1029
+ function getHttpStatus(err) {
1030
+ if (typeof err !== 'object' || err === null)
1031
+ return null;
1032
+ const e = err;
1033
+ if (typeof e.status === 'number')
1034
+ return e.status;
1035
+ if (typeof e.response?.status === 'number')
1036
+ return e.response.status;
1037
+ return null;
1038
+ }
1039
+ async function processCommentCommands(params) {
1040
+ const { config, db, forge, runManager, leaseManager, repoConfig, botUser, } = params;
1041
+ const commandSettings = config.commentCommands ?? { enabled: true, requireCollaborator: false };
1042
+ if (!commandSettings.enabled)
1043
+ return;
1044
+ // Warn once per poll when comment commands accept non-collaborators —
1045
+ // for public repos this means any GitHub user can trigger retry/rebase/
1046
+ // continue/delete operations.
1047
+ if (!commandSettings.requireCollaborator) {
1048
+ logger.warn({ repo: repoConfig.repo }, 'commentCommands.requireCollaborator=false — /orch commands accept any commenter. Enable on public repos.');
1049
+ }
1050
+ const activeRuns = runManager
1051
+ .getActive()
1052
+ .filter((run) => run.repo === repoConfig.repo);
1053
+ const issueRows = [...new Map(activeRuns.map((run) => {
1054
+ const issueRepo = resolveIssueRepo(run.phaseData, repoConfig.repo);
1055
+ return [`${issueRepo}#${run.issueNumber}`, { issue_number: run.issueNumber, issue_repo: issueRepo }];
1056
+ })).values()]
1057
+ .sort((a, b) => a.issue_repo.localeCompare(b.issue_repo) || a.issue_number - b.issue_number);
1058
+ if (issueRows.length === 0)
1059
+ return;
1060
+ const collaboratorCache = new Map();
1061
+ for (const row of issueRows) {
1062
+ const issueKey = `${row.issue_repo}#${row.issue_number}`;
1063
+ if (missingCommentCommandIssues.has(issueKey)) {
1064
+ continue;
1065
+ }
1066
+ let comments;
1067
+ try {
1068
+ comments = await forge.listIssueComments(row.issue_repo, row.issue_number);
1069
+ }
1070
+ catch (err) {
1071
+ if (getHttpStatus(err) === 404) {
1072
+ missingCommentCommandIssues.add(issueKey);
1073
+ logger.debug({ repo: row.issue_repo, issueNumber: row.issue_number }, 'Skipping comment command scan for missing or inaccessible issue');
1074
+ continue;
1075
+ }
1076
+ throw err;
1077
+ }
1078
+ const parsed = parseOrchCommands(comments, '1970-01-01T00:00:00Z');
1079
+ for (const item of parsed) {
1080
+ if (isCommandProcessed(db, row.issue_repo, row.issue_number, item.commentId))
1081
+ continue;
1082
+ // Track whether the command reached a terminal outcome — applied,
1083
+ // denied (policy decision), or rejected (validated failure). Only
1084
+ // terminal outcomes mark the command as processed; transient
1085
+ // failures must remain retriable on the next poll cycle.
1086
+ let commandStatus = null;
1087
+ try {
1088
+ const allowed = await canExecuteCommentCommand({
1089
+ forge,
1090
+ repo: row.issue_repo,
1091
+ user: item.user,
1092
+ requireCollaborator: commandSettings.requireCollaborator,
1093
+ cache: collaboratorCache,
1094
+ });
1095
+ if (!allowed) {
1096
+ commandStatus = 'denied';
1097
+ logger.info({ repo: repoConfig.repo, issueNumber: row.issue_number, user: item.user, commentId: item.commentId }, 'Ignoring comment command from non-collaborator');
1098
+ continue;
1099
+ }
1100
+ const result = await executeCommentCommand({
1101
+ command: item.command,
1102
+ db,
1103
+ forge,
1104
+ runManager,
1105
+ leaseManager,
1106
+ repoConfig,
1107
+ issueRepo: row.issue_repo,
1108
+ issueNumber: row.issue_number,
1109
+ botUser,
1110
+ user: item.user,
1111
+ });
1112
+ if (!result.ok) {
1113
+ commandStatus = 'rejected';
1114
+ logger.info({ repo: repoConfig.repo, issueNumber: row.issue_number, command: item.command.type, reason: result.reason }, 'Comment command rejected');
1115
+ }
1116
+ else {
1117
+ commandStatus = 'applied';
1118
+ logger.info({ repo: repoConfig.repo, issueNumber: row.issue_number, command: item.command.type, user: item.user }, 'Comment command applied');
1119
+ }
1120
+ }
1121
+ catch (err) {
1122
+ // Transient failure: leave commandStatus=null so the command
1123
+ // remains unprocessed and will be retried on the next poll.
1124
+ logger.warn({ repo: repoConfig.repo, issueNumber: row.issue_number, commentId: item.commentId, command: item.command.type, err }, 'Comment command failed (transient — will retry)');
1125
+ }
1126
+ finally {
1127
+ if (commandStatus !== null) {
1128
+ markCommandProcessed(db, row.issue_repo, row.issue_number, item.commentId, `${item.command.type}:${commandStatus}`);
1129
+ }
1130
+ }
1131
+ }
1132
+ }
1133
+ }
1134
+ async function canExecuteCommentCommand(params) {
1135
+ const { forge, repo, user, requireCollaborator, cache } = params;
1136
+ if (!requireCollaborator)
1137
+ return true;
1138
+ if (!user)
1139
+ return false;
1140
+ // Cache key must include the repo — a user might be a collaborator on
1141
+ // one linked project but not another, and reusing a single-user cache
1142
+ // across repos in the same scan would erroneously grant or deny access.
1143
+ const cacheKey = `${repo}\n${user}`;
1144
+ const cached = cache.get(cacheKey);
1145
+ if (cached !== undefined)
1146
+ return cached;
1147
+ if (!forge.isCollaborator) {
1148
+ logger.warn({ repo, user }, 'requireCollaborator=true but forge adapter has no isCollaborator() implementation');
1149
+ cache.set(cacheKey, false);
1150
+ return false;
1151
+ }
1152
+ try {
1153
+ const allowed = await forge.isCollaborator(repo, user);
1154
+ cache.set(cacheKey, allowed);
1155
+ return allowed;
1156
+ }
1157
+ catch (err) {
1158
+ logger.warn({ repo, user, err }, 'Failed collaborator check for comment command user');
1159
+ cache.set(cacheKey, false);
1160
+ return false;
1161
+ }
1162
+ }
1163
+ async function executeCommentCommand(params) {
1164
+ const { command, db, forge, runManager, leaseManager, repoConfig, issueRepo, issueNumber, botUser, user, } = params;
1165
+ switch (command.type) {
1166
+ case 'retry':
1167
+ return queueRetryFromComment({
1168
+ runManager,
1169
+ leaseManager,
1170
+ forge,
1171
+ repoConfig,
1172
+ issueRepo,
1173
+ issueNumber,
1174
+ resetPlan: command.resetPlan,
1175
+ });
1176
+ case 'continue':
1177
+ {
1178
+ const result = await queueContinue(db, forge, repoConfig, issueNumber, botUser, { issueRepo });
1179
+ return result.queued ? { ok: true } : { ok: false, reason: result.reason };
1180
+ }
1181
+ case 'rebase': {
1182
+ // queueRebase currently always verifies after rebase; keep behavior stable.
1183
+ const result = await queueRebase(db, forge, repoConfig, issueNumber, botUser);
1184
+ return result.queued ? { ok: true } : { ok: false, reason: result.reason };
1185
+ }
1186
+ case 'cancel':
1187
+ return cancelRunFromComment({
1188
+ runManager,
1189
+ leaseManager,
1190
+ forge,
1191
+ repoConfig,
1192
+ issueRepo,
1193
+ issueNumber,
1194
+ user,
1195
+ });
1196
+ default: {
1197
+ const exhaustive = command;
1198
+ return { ok: false, reason: `Unsupported command: ${String(exhaustive)}` };
1199
+ }
1200
+ }
1201
+ }
1202
+ async function queueRetryFromComment(params) {
1203
+ const { runManager, leaseManager, forge, repoConfig, issueRepo, issueNumber, resetPlan } = params;
1204
+ const run = runManager.getByRepoAndIssue(repoConfig.repo, issueNumber);
1205
+ if (!run)
1206
+ return { ok: false, reason: 'No run found for issue' };
1207
+ if (run.status === 'running')
1208
+ return { ok: false, reason: 'Run is currently running' };
1209
+ if (!['blocked', 'error', 'review_ready'].includes(run.status)) {
1210
+ return { ok: false, reason: `Retry not allowed from status ${run.status}` };
1211
+ }
1212
+ runManager.update(run.id, {
1213
+ status: 'queued',
1214
+ currentPhase: null,
1215
+ endedAt: null,
1216
+ lastError: null,
1217
+ phaseData: resetPlan ? null : run.phaseData,
1218
+ blockReason: null,
1219
+ });
1220
+ leaseManager.release(issueRepo, issueNumber);
1221
+ if (issueRepo !== repoConfig.repo) {
1222
+ leaseManager.release(repoConfig.repo, issueNumber);
1223
+ }
1224
+ const issue = await forge.getIssue(issueRepo, issueNumber);
1225
+ await transitionLabels(forge, issueRepo, issueNumber, issue.labels, run.status, 'queued', buildLabelConfig(repoConfig, issue.labels));
1226
+ return { ok: true };
1227
+ }
1228
+ async function cancelRunFromComment(params) {
1229
+ const { runManager, leaseManager, forge, repoConfig, issueRepo, issueNumber, user } = params;
1230
+ const run = runManager.getByRepoAndIssue(repoConfig.repo, issueNumber);
1231
+ if (!run)
1232
+ return { ok: false, reason: 'No run found for issue' };
1233
+ if (run.status !== 'running' && run.status !== 'queued') {
1234
+ return { ok: false, reason: `Cancel only supports running/queued runs (current: ${run.status})` };
1235
+ }
1236
+ runManager.update(run.id, {
1237
+ status: 'blocked',
1238
+ endedAt: nowUtcIso(),
1239
+ lastError: `Cancelled by @${user} via comment command`,
1240
+ blockReason: null,
1241
+ });
1242
+ leaseManager.release(issueRepo, issueNumber);
1243
+ if (issueRepo !== repoConfig.repo) {
1244
+ leaseManager.release(repoConfig.repo, issueNumber);
1245
+ }
1246
+ const issue = await forge.getIssue(issueRepo, issueNumber);
1247
+ await transitionLabels(forge, issueRepo, issueNumber, issue.labels, run.status, 'blocked', buildLabelConfig(repoConfig, issue.labels));
1248
+ return { ok: true };
1249
+ }
1250
+ /**
1251
+ * Block reasons that indicate the coder's work is broken and the branch
1252
+ * should be hard-reset to base on the next attempt. Salvageable states
1253
+ * (reviewer_blocked, iteration_limit, ambiguous_review, verify_config)
1254
+ * preserve the branch so existing work can be continued.
1255
+ */
1256
+ const TAINTED_BLOCK_REASONS = new Set(['agent_pass_limit', 'cost_limit', 'merge_conflict']);
1257
+ // --- Reaction scanning ---
1258
+ /** In-memory reaction cursors, keyed by "repo#issueNumber".
1259
+ * Bounded: entries are evicted via {@link cleanupRunCaches}. */
1260
+ const reactionCursors = new Map();
1261
+ /**
1262
+ * Evict entries from process-global caches that are keyed by repo+issue.
1263
+ * Called when a run reaches a terminal state so the caches don't grow
1264
+ * unbounded over the daemon's lifetime.
1265
+ */
1266
+ function cleanupRunCaches(repo, issueNumber) {
1267
+ const key = `${repo}#${issueNumber}`;
1268
+ missingCommentCommandIssues.delete(key);
1269
+ reactionCursors.delete(key);
1270
+ }
1271
+ async function scanAndHandleReactions(params) {
1272
+ const { db, forge, runManager, repoConfig, botUser } = params;
1273
+ // Find review_ready issues with PRs for this repo.
1274
+ const rows = runManager
1275
+ .getActive()
1276
+ .filter((run) => run.repo === repoConfig.repo && run.status === 'review_ready' && run.prNumber !== null)
1277
+ .map((run) => ({
1278
+ id: run.id,
1279
+ repo: run.repo,
1280
+ issue_number: run.issueNumber,
1281
+ pr_number: run.prNumber,
1282
+ }));
1283
+ for (const row of rows) {
1284
+ const cursorKey = `${row.repo}#${row.issue_number}`;
1285
+ const cursor = reactionCursors.get(cursorKey);
1286
+ const result = await scanForReactions(forge, row.repo, row.pr_number, row.issue_number, botUser, cursor);
1287
+ // Update cursor regardless of reactions
1288
+ reactionCursors.set(cursorKey, result.cursor);
1289
+ // Handle each reaction
1290
+ for (const reaction of result.reactions) {
1291
+ try {
1292
+ await handleReaction(reaction, { db, forge, runManager, repoConfig });
1293
+ }
1294
+ catch (err) {
1295
+ logger.warn({ repo: row.repo, issueNumber: row.issue_number, reactionType: reaction.type, err }, 'Failed to handle reaction');
1296
+ }
1297
+ }
1298
+ }
1299
+ }
1300
+ function extractFollowupPromptFeedback(phaseData) {
1301
+ if (!phaseData)
1302
+ return null;
1303
+ const context = phaseData['reactionContext'];
1304
+ if (typeof context !== 'string' || context.trim().length === 0)
1305
+ return null;
1306
+ const type = typeof phaseData['reactionType'] === 'string' && phaseData['reactionType'].trim().length > 0
1307
+ ? phaseData['reactionType']
1308
+ : 'continue';
1309
+ const summary = typeof phaseData['reactionSummary'] === 'string' && phaseData['reactionSummary'].trim().length > 0
1310
+ ? phaseData['reactionSummary']
1311
+ : 'Follow-up context available';
1312
+ return { type, summary, context };
1313
+ }
1314
+ /**
1315
+ * Prioritize follow-up work over fresh issues so reactive runs (especially
1316
+ * merge conflict rebases) are handled promptly and don't starve behind newer
1317
+ * ready issues.
1318
+ */
1319
+ function prioritizeDiscoveredIssues(runManager, repo, discovered) {
1320
+ const ranked = discovered.map((item) => ({
1321
+ item,
1322
+ rank: getIssueQueuePriority(runManager, repo, item.issue.number),
1323
+ }));
1324
+ ranked.sort((a, b) => a.rank - b.rank);
1325
+ return ranked.map((entry) => entry.item);
1326
+ }
1327
+ function getIssueQueuePriority(runManager, repo, issueNumber) {
1328
+ const queuedRun = runManager.getLatestQueuedByIssue(repo, issueNumber);
1329
+ if (!queuedRun)
1330
+ return 3;
1331
+ const reactionType = queuedRun.phaseData?.reactionType;
1332
+ if (reactionType === 'merge_conflict' || reactionType === 'rebase')
1333
+ return 0;
1334
+ if (typeof reactionType === 'string' && reactionType.length > 0)
1335
+ return 1;
1336
+ return 2;
1337
+ }
1338
+ function selectReplayableRun(run) {
1339
+ if (!run)
1340
+ return null;
1341
+ if (run.status === 'blocked' || run.status === 'review_ready' || run.status === 'error') {
1342
+ return run;
1343
+ }
1344
+ return null;
1345
+ }
1346
+ function shouldResetBranch(runManager, repo, issueNumber, currentRunId) {
1347
+ const prior = runManager.getLatestFinishedByIssue(repo, issueNumber, currentRunId);
1348
+ if (!prior)
1349
+ return false;
1350
+ // Infrastructure errors — work is unreliable
1351
+ if (prior.status === 'error')
1352
+ return true;
1353
+ // Blocked with tainted block reason — coder couldn't produce working code
1354
+ if (prior.status === 'blocked' && prior.blockReason && TAINTED_BLOCK_REASONS.has(prior.blockReason))
1355
+ return true;
1356
+ return false;
1357
+ }
1358
+ //# sourceMappingURL=poller.js.map