@askexenow/exe-os 0.9.271 → 0.9.273

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 (562) hide show
  1. package/dist/active-agent-BDYXURXQ.js +26 -0
  2. package/dist/active-agent-YWBGAKGU.js +25 -0
  3. package/dist/agentic-ontology-56VHSVS3.js +25 -0
  4. package/dist/backfill-metadata-A3534S32.js +597 -0
  5. package/dist/backfill-metadata-B6F2KJJV.js +597 -0
  6. package/dist/backfill-metadata-BOM2MXLI.js +597 -0
  7. package/dist/backfill-metadata-G46ABBVR.js +597 -0
  8. package/dist/backfill-metadata-TAU33HJS.js +597 -0
  9. package/dist/backfill-metadata-VAV27KJK.js +597 -0
  10. package/dist/behaviors-USUTDXVA.js +25 -0
  11. package/dist/bin/agentic-ontology-backfill.js +5 -5
  12. package/dist/bin/agentic-reflection-backfill.js +6 -6
  13. package/dist/bin/agentic-semantic-label.js +5 -5
  14. package/dist/bin/backfill-conversations.js +4 -4
  15. package/dist/bin/backfill-responses.js +4 -4
  16. package/dist/bin/backfill-vectors.js +5 -5
  17. package/dist/bin/bulk-sync-postgres.js +6 -6
  18. package/dist/bin/cc-doctor.js +4 -4
  19. package/dist/bin/cleanup-stale-review-tasks.js +10 -10
  20. package/dist/bin/cli.js +16 -16
  21. package/dist/bin/exe-agent-config.js +3 -3
  22. package/dist/bin/exe-agent.js +4 -4
  23. package/dist/bin/exe-assign.js +5 -5
  24. package/dist/bin/exe-boot.js +17 -17
  25. package/dist/bin/exe-call.js +4 -4
  26. package/dist/bin/exe-cloud.js +4 -4
  27. package/dist/bin/exe-dispatch.js +10 -10
  28. package/dist/bin/exe-doctor.js +1 -1
  29. package/dist/bin/exe-export-behaviors.js +7 -7
  30. package/dist/bin/exe-forget.js +6 -6
  31. package/dist/bin/exe-gateway.js +7 -7
  32. package/dist/bin/exe-healthcheck.js +4 -4
  33. package/dist/bin/exe-heartbeat.js +10 -10
  34. package/dist/bin/exe-kill.js +13 -13
  35. package/dist/bin/exe-launch-agent.js +37 -19
  36. package/dist/bin/exe-new-employee.js +6 -6
  37. package/dist/bin/exe-pending-messages.js +11 -11
  38. package/dist/bin/exe-pending-notifications.js +33 -18
  39. package/dist/bin/exe-pending-reviews.js +10 -10
  40. package/dist/bin/exe-rename.js +4 -4
  41. package/dist/bin/exe-review.js +12 -12
  42. package/dist/bin/exe-search.js +5 -5
  43. package/dist/bin/exe-session-cleanup.js +15 -15
  44. package/dist/bin/exe-settings.js +5 -5
  45. package/dist/bin/exe-start-codex.js +11 -11
  46. package/dist/bin/exe-start-opencode.js +8 -8
  47. package/dist/bin/exe-status.js +11 -11
  48. package/dist/bin/exe-team.js +3 -3
  49. package/dist/bin/git-sweep.js +11 -11
  50. package/dist/bin/graph-backfill.js +4 -4
  51. package/dist/bin/graph-export.js +5 -5
  52. package/dist/bin/import-history.js +7 -7
  53. package/dist/bin/install.js +6 -6
  54. package/dist/bin/intercom-check.js +4 -4
  55. package/dist/bin/mcp-sessions.js +2 -2
  56. package/dist/bin/orchestration-metrics.js +4 -4
  57. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  58. package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
  59. package/dist/bin/scan-tasks.js +10 -10
  60. package/dist/bin/setup.js +1 -1
  61. package/dist/bin/shard-migrate.js +4 -4
  62. package/dist/capacity-monitor-2GJOFXGB.js +49 -0
  63. package/dist/capacity-monitor-3Z7W4K25.js +49 -0
  64. package/dist/capacity-monitor-BENS3N7B.js +49 -0
  65. package/dist/capacity-monitor-IFVRCIM7.js +49 -0
  66. package/dist/capacity-monitor-MQUUEZKB.js +49 -0
  67. package/dist/capacity-monitor-Q47GBDSY.js +49 -0
  68. package/dist/catchup-brief-B4KGAIPU.js +151 -0
  69. package/dist/catchup-brief-NMOV3SSP.js +151 -0
  70. package/dist/catchup-brief-RP4QHXNT.js +151 -0
  71. package/dist/catchup-brief-TKA6TEK4.js +151 -0
  72. package/dist/catchup-brief-VMF3ESTZ.js +151 -0
  73. package/dist/catchup-brief-ZL7V3BXC.js +151 -0
  74. package/dist/chunk-23KJ2LXY.js +58 -0
  75. package/dist/chunk-2KWVJV6I.js +171 -0
  76. package/dist/chunk-2NQQP3FF.js +630 -0
  77. package/dist/chunk-3A4SOC66.js +551 -0
  78. package/dist/chunk-3FU5I3KV.js +526 -0
  79. package/dist/chunk-3GSGDPLK.js +171 -0
  80. package/dist/chunk-3IM3JNQV.js +377 -0
  81. package/dist/chunk-3OM3V545.js +448 -0
  82. package/dist/chunk-3T27ZQT6.js +495 -0
  83. package/dist/chunk-3VI3QIHU.js +214 -0
  84. package/dist/chunk-3WG3RRWA.js +1345 -0
  85. package/dist/chunk-42A3JV3A.js +128 -0
  86. package/dist/chunk-46IEEKPU.js +13696 -0
  87. package/dist/chunk-46WLFLGP.js +1073 -0
  88. package/dist/chunk-4JMPQB7K.js +1148 -0
  89. package/dist/chunk-4L25LLQM.js +382 -0
  90. package/dist/chunk-4Q7X3SAM.js +204 -0
  91. package/dist/chunk-4UAUCFHA.js +526 -0
  92. package/dist/chunk-4VRJX2SP.js +495 -0
  93. package/dist/chunk-57RBAR2A.js +214 -0
  94. package/dist/chunk-57UAFTO2.js +3958 -0
  95. package/dist/chunk-5AS622MM.js +3958 -0
  96. package/dist/chunk-5JF5OQQU.js +89 -0
  97. package/dist/chunk-5LTY4GLX.js +13745 -0
  98. package/dist/chunk-5M5RYJ22.js +3955 -0
  99. package/dist/chunk-5YO2FER3.js +76 -0
  100. package/dist/chunk-62DEE65H.js +371 -0
  101. package/dist/chunk-62YI2JOC.js +333 -0
  102. package/dist/chunk-64T6DFSS.js +447 -0
  103. package/dist/chunk-6AGPWYFC.js +447 -0
  104. package/dist/chunk-6BWDP63Z.js +197 -0
  105. package/dist/chunk-6CH7TYBG.js +58 -0
  106. package/dist/chunk-6CHHFVRQ.js +284 -0
  107. package/dist/chunk-6D64562N.js +330 -0
  108. package/dist/chunk-6F35WOSR.js +447 -0
  109. package/dist/chunk-6GPYL7TX.js +214 -0
  110. package/dist/chunk-6HQ22FC6.js +81 -0
  111. package/dist/chunk-6KWLUVFL.js +54 -0
  112. package/dist/chunk-6N5ISWBF.js +1148 -0
  113. package/dist/chunk-6OD7PVMC.js +333 -0
  114. package/dist/chunk-6ZSH2BZR.js +244 -0
  115. package/dist/chunk-74MF4T3T.js +3962 -0
  116. package/dist/chunk-77H7IO3O.js +382 -0
  117. package/dist/chunk-7BUWNG6M.js +159 -0
  118. package/dist/chunk-7ET5CYTD.js +382 -0
  119. package/dist/chunk-7IWLKR6N.js +76 -0
  120. package/dist/chunk-7OEUOJL5.js +1021 -0
  121. package/dist/chunk-7YEQI2WF.js +13745 -0
  122. package/dist/chunk-AIRJTKDK.js +204 -0
  123. package/dist/chunk-AJ63GPM7.js +54 -0
  124. package/dist/chunk-ATJ3NXDP.js +244 -0
  125. package/dist/chunk-B5IS7LE4.js +128 -0
  126. package/dist/chunk-BFJ45HQT.js +244 -0
  127. package/dist/chunk-BKINEQVI.js +244 -0
  128. package/dist/chunk-BMHE3UQU.js +495 -0
  129. package/dist/chunk-BNTUZVPS.js +1921 -0
  130. package/dist/chunk-BOJV6NI3.js +128 -0
  131. package/dist/chunk-BPHWI6N2.js +284 -0
  132. package/dist/chunk-BXCQWWJP.js +185 -0
  133. package/dist/chunk-BZ6K7AY3.js +50 -0
  134. package/dist/chunk-C54KIFLS.js +214 -0
  135. package/dist/chunk-C6ODVGTC.js +818 -0
  136. package/dist/chunk-C6OYEJJI.js +260 -0
  137. package/dist/chunk-CHBGCQXG.js +333 -0
  138. package/dist/chunk-CHBHR5W6.js +3556 -0
  139. package/dist/chunk-CHUOANKE.js +346 -0
  140. package/dist/chunk-CSF4RUCN.js +58 -0
  141. package/dist/chunk-CXKHWCNN.js +204 -0
  142. package/dist/chunk-CZR6Z5D7.js +330 -0
  143. package/dist/chunk-D24ANCWY.js +204 -0
  144. package/dist/chunk-D2T3272U.js +171 -0
  145. package/dist/chunk-DBJCWK6T.js +377 -0
  146. package/dist/chunk-DCHEIVGT.js +221 -0
  147. package/dist/chunk-DF4SM6ZX.js +128 -0
  148. package/dist/chunk-DHIBLMSP.js +30 -0
  149. package/dist/chunk-DOAC6CLC.js +127 -0
  150. package/dist/chunk-DOGNJ4VR.js +818 -0
  151. package/dist/chunk-DYXJFUCI.js +818 -0
  152. package/dist/chunk-E2AF2WYY.js +346 -0
  153. package/dist/chunk-E2KZEZZW.js +1090 -0
  154. package/dist/chunk-E4KWB4WM.js +348 -0
  155. package/dist/chunk-EGR2NYID.js +50 -0
  156. package/dist/chunk-ENU7URWK.js +1073 -0
  157. package/dist/chunk-EQ5UBJGX.js +81 -0
  158. package/dist/chunk-EZ7KAZMC.js +132 -0
  159. package/dist/chunk-F4FSSHR4.js +1073 -0
  160. package/dist/chunk-FBRQGHSU.js +377 -0
  161. package/dist/chunk-FPXU56FG.js +346 -0
  162. package/dist/chunk-FY7HHR5I.js +128 -0
  163. package/dist/chunk-FZ42OCSP.js +333 -0
  164. package/dist/chunk-G2S2UMU4.js +159 -0
  165. package/dist/chunk-G33BHQCO.js +70 -0
  166. package/dist/chunk-G5HWDSBH.js +50 -0
  167. package/dist/chunk-GCBG5TFS.js +1345 -0
  168. package/dist/chunk-GESN6IDC.js +127 -0
  169. package/dist/chunk-GHD7QG6P.js +58 -0
  170. package/dist/chunk-GJAILPCX.js +171 -0
  171. package/dist/chunk-GJQTL7RX.js +127 -0
  172. package/dist/chunk-GKUODJS7.js +214 -0
  173. package/dist/chunk-GLCKDEM2.js +97 -0
  174. package/dist/chunk-GLDM2FOM.js +76 -0
  175. package/dist/chunk-GMA34SXV.js +240 -0
  176. package/dist/chunk-GMM2BLFB.js +127 -0
  177. package/dist/chunk-GNM75IOI.js +159 -0
  178. package/dist/chunk-GVAVEBYR.js +2091 -0
  179. package/dist/chunk-GYIX2HLD.js +81 -0
  180. package/dist/chunk-H4LLEQ3F.js +551 -0
  181. package/dist/chunk-HBVCBBDA.js +127 -0
  182. package/dist/chunk-HBYRWOH5.js +171 -0
  183. package/dist/chunk-HFINM2JG.js +284 -0
  184. package/dist/chunk-HJGHALOG.js +1345 -0
  185. package/dist/chunk-HOSJTLBQ.js +513 -0
  186. package/dist/chunk-HRB5CP43.js +13745 -0
  187. package/dist/chunk-IC6HVAS3.js +56 -0
  188. package/dist/chunk-IDCLPPIM.js +3959 -0
  189. package/dist/chunk-IDFJNO44.js +1051 -0
  190. package/dist/chunk-II5SVNBN.js +551 -0
  191. package/dist/chunk-IIRLKWNZ.js +50 -0
  192. package/dist/chunk-ILFJMEY5.js +97 -0
  193. package/dist/chunk-IQXLUTWC.js +50 -0
  194. package/dist/chunk-ISQAOSL3.js +1921 -0
  195. package/dist/chunk-IWXTFDLS.js +244 -0
  196. package/dist/chunk-J2TGVCPE.js +1090 -0
  197. package/dist/chunk-J4Z5GAJ4.js +551 -0
  198. package/dist/chunk-J6V2DCZK.js +382 -0
  199. package/dist/chunk-JJSDZFKM.js +1148 -0
  200. package/dist/chunk-JMN2KOC4.js +128 -0
  201. package/dist/chunk-JP4CLFLR.js +1148 -0
  202. package/dist/chunk-JQVYPBR2.js +81 -0
  203. package/dist/chunk-JTIOZHWG.js +58 -0
  204. package/dist/chunk-KDICWAYV.js +1345 -0
  205. package/dist/chunk-KMU7PFO3.js +1148 -0
  206. package/dist/chunk-KOBIB6WG.js +159 -0
  207. package/dist/chunk-KQFDDQB6.js +13696 -0
  208. package/dist/chunk-KZNSOHCB.js +280 -0
  209. package/dist/chunk-LDDCAATQ.js +1090 -0
  210. package/dist/chunk-LJN2O5IG.js +197 -0
  211. package/dist/chunk-LSIYHKDS.js +54 -0
  212. package/dist/chunk-LVMBYP3C.js +171 -0
  213. package/dist/chunk-M2WQW5NC.js +227 -0
  214. package/dist/chunk-MREDKOS4.js +731 -0
  215. package/dist/chunk-MSF2Y5MS.js +346 -0
  216. package/dist/chunk-MY647ZHR.js +448 -0
  217. package/dist/chunk-MY6SP5NZ.js +551 -0
  218. package/dist/chunk-MZ5CEHPQ.js +89 -0
  219. package/dist/chunk-N2ACW2ZG.js +363 -0
  220. package/dist/chunk-NESTX6DR.js +76 -0
  221. package/dist/chunk-NQZORF6L.js +731 -0
  222. package/dist/chunk-NSMJDATI.js +495 -0
  223. package/dist/chunk-NSQ5JE23.js +1090 -0
  224. package/dist/chunk-NWEFAFJS.js +197 -0
  225. package/dist/chunk-NYF7GHC5.js +526 -0
  226. package/dist/chunk-NZGGRM4P.js +731 -0
  227. package/dist/chunk-NZL567WG.js +81 -0
  228. package/dist/chunk-NZM4E6Y3.js +89 -0
  229. package/dist/chunk-O5OMH6LI.js +244 -0
  230. package/dist/chunk-O6XF6NUN.js +1090 -0
  231. package/dist/chunk-OBUV3W7L.js +163 -0
  232. package/dist/chunk-OF4KG3L7.js +1090 -0
  233. package/dist/chunk-OLDS7LJN.js +495 -0
  234. package/dist/chunk-OO2I22RX.js +38 -0
  235. package/dist/chunk-OPUUT33V.js +447 -0
  236. package/dist/chunk-OQZPSWVN.js +526 -0
  237. package/dist/chunk-OR6KJ5HH.js +58 -0
  238. package/dist/chunk-OT3VMTKB.js +50 -0
  239. package/dist/chunk-OV6NT6QX.js +128 -0
  240. package/dist/chunk-P6RVIOVA.js +157 -0
  241. package/dist/chunk-PDTR3YUU.js +54 -0
  242. package/dist/chunk-PEGTV6EJ.js +1345 -0
  243. package/dist/chunk-PITVTSQW.js +333 -0
  244. package/dist/chunk-PSUAO4MZ.js +345 -0
  245. package/dist/chunk-PUA5564C.js +210 -0
  246. package/dist/chunk-PUQLKLQX.js +731 -0
  247. package/dist/chunk-PWQIS5E5.js +382 -0
  248. package/dist/chunk-PXXHKWDH.js +818 -0
  249. package/dist/chunk-QOC46BDY.js +346 -0
  250. package/dist/chunk-QROKS65G.js +76 -0
  251. package/dist/chunk-R54I2N2T.js +818 -0
  252. package/dist/chunk-RCFYQHUP.js +818 -0
  253. package/dist/chunk-RJTND4YS.js +284 -0
  254. package/dist/chunk-RTA6KSSK.js +89 -0
  255. package/dist/chunk-SBLHQMMZ.js +81 -0
  256. package/dist/chunk-SBX6HSEO.js +159 -0
  257. package/dist/chunk-SEUST6U5.js +284 -0
  258. package/dist/chunk-SG2ANG5C.js +123 -0
  259. package/dist/chunk-SUEQF3ZS.js +214 -0
  260. package/dist/chunk-SVFNKSZV.js +333 -0
  261. package/dist/chunk-SWNAM2NW.js +526 -0
  262. package/dist/chunk-TAQT2DC7.js +330 -0
  263. package/dist/chunk-TB7HFW7M.js +127 -0
  264. package/dist/chunk-TBJP46RP.js +1148 -0
  265. package/dist/chunk-TS7NGPU4.js +1073 -0
  266. package/dist/chunk-TUPDOPMG.js +731 -0
  267. package/dist/chunk-TYKUZVCA.js +1921 -0
  268. package/dist/chunk-TZMXJVZV.js +345 -0
  269. package/dist/chunk-U2DCN7M6.js +1073 -0
  270. package/dist/chunk-UJZPLZLU.js +197 -0
  271. package/dist/chunk-UKRKOJQZ.js +54 -0
  272. package/dist/chunk-UUKDAIH2.js +731 -0
  273. package/dist/chunk-V6VEFEEH.js +1345 -0
  274. package/dist/chunk-VCVGE7HK.js +1921 -0
  275. package/dist/chunk-VIO2ALGH.js +290 -0
  276. package/dist/chunk-VK6YZ6K7.js +1073 -0
  277. package/dist/chunk-VKCUSNJW.js +377 -0
  278. package/dist/chunk-VKT4N6WM.js +495 -0
  279. package/dist/chunk-VQUEP7UA.js +244 -0
  280. package/dist/chunk-VRPPJFIQ.js +1921 -0
  281. package/dist/chunk-VXODHQXB.js +377 -0
  282. package/dist/chunk-WHK7GXFR.js +13745 -0
  283. package/dist/chunk-WP3PVBBP.js +204 -0
  284. package/dist/chunk-WQEUY7DC.js +129 -0
  285. package/dist/chunk-WWPJTPPQ.js +197 -0
  286. package/dist/chunk-WXMXUKCA.js +262 -0
  287. package/dist/chunk-WYZSWV6A.js +346 -0
  288. package/dist/chunk-X2WBH2IO.js +297 -0
  289. package/dist/chunk-X33TSJNO.js +394 -0
  290. package/dist/chunk-X7MMI2UI.js +89 -0
  291. package/dist/chunk-XD6VOXK3.js +159 -0
  292. package/dist/chunk-XG3BQZIK.js +85 -0
  293. package/dist/chunk-XIKBIAOS.js +75 -0
  294. package/dist/chunk-XPEB545Q.js +54 -0
  295. package/dist/chunk-XWH2MLWS.js +330 -0
  296. package/dist/chunk-YH7V73XW.js +89 -0
  297. package/dist/chunk-YHSATGMH.js +3955 -0
  298. package/dist/chunk-YJBCGD46.js +13745 -0
  299. package/dist/chunk-YLOJPYCJ.js +284 -0
  300. package/dist/chunk-YMLM5D65.js +135 -0
  301. package/dist/chunk-YNJPRQ6J.js +377 -0
  302. package/dist/chunk-YSNEHBI6.js +551 -0
  303. package/dist/chunk-Z33XSFND.js +76 -0
  304. package/dist/chunk-ZA7N3ZTA.js +1921 -0
  305. package/dist/chunk-ZD6BMW2K.js +33 -0
  306. package/dist/chunk-ZFRG2MNB.js +382 -0
  307. package/dist/chunk-ZKG5IYCG.js +668 -0
  308. package/dist/chunk-ZRRRSVQF.js +204 -0
  309. package/dist/chunk-ZU4K7ZNX.js +197 -0
  310. package/dist/co-activation-HZMJC34P.js +72 -0
  311. package/dist/co-occurrence-AVYXRV4L.js +74 -0
  312. package/dist/core-memory-554Q3YN5.js +110 -0
  313. package/dist/core-memory-BC4YN5F4.js +110 -0
  314. package/dist/core-memory-NID6R3YR.js +110 -0
  315. package/dist/core-memory-NPJCVUMF.js +110 -0
  316. package/dist/core-memory-OKGXL33Z.js +110 -0
  317. package/dist/core-memory-XHIC5NAB.js +110 -0
  318. package/dist/crdt-sync-ZCH55JNR.js +33 -0
  319. package/dist/crm-webhook-6OMVUUGR.js +10 -0
  320. package/dist/crm-webhook-MHZTXU5N.js +10 -0
  321. package/dist/crm-webhook-TMWJT2Z5.js +10 -0
  322. package/dist/crm-webhook-UCWF3XDB.js +10 -0
  323. package/dist/crm-webhook-XISULXI7.js +10 -0
  324. package/dist/crm-webhook-YJ5A7F2E.js +10 -0
  325. package/dist/cto-delegation-gate-4PMJZL2T.js +206 -0
  326. package/dist/cto-delegation-gate-A7YKXTRO.js +206 -0
  327. package/dist/cto-delegation-gate-JFZFZGC2.js +206 -0
  328. package/dist/cto-delegation-gate-K32M4GVM.js +206 -0
  329. package/dist/cto-delegation-gate-OREBAHUM.js +206 -0
  330. package/dist/cto-delegation-gate-POHESML5.js +206 -0
  331. package/dist/daemon-orchestration-2Q7BYOHC.js +135 -0
  332. package/dist/daemon-orchestration-4RJ2CZJL.js +135 -0
  333. package/dist/daemon-orchestration-HXYPHSYU.js +135 -0
  334. package/dist/daemon-orchestration-I5BE46P3.js +135 -0
  335. package/dist/daemon-orchestration-NKE4FYQS.js +135 -0
  336. package/dist/daemon-orchestration-YWEXRAZA.js +135 -0
  337. package/dist/db-backup-5GA2YFDX.js +33 -0
  338. package/dist/dreaming-3F72ROTL.js +32 -0
  339. package/dist/dreaming-I6KXO6E2.js +32 -0
  340. package/dist/dreaming-JD7MNJGS.js +32 -0
  341. package/dist/dreaming-LCKPA3B4.js +32 -0
  342. package/dist/dreaming-NJBK5ILR.js +32 -0
  343. package/dist/dreaming-SDS5IQYC.js +32 -0
  344. package/dist/exe-drift-VSMIMHL4.js +68 -0
  345. package/dist/exe-export-DVHHIA6Y.js +73 -0
  346. package/dist/exe-export-GIVQDENS.js +73 -0
  347. package/dist/exe-export-IZ2OYMT4.js +73 -0
  348. package/dist/exe-export-JNWX6ZCQ.js +73 -0
  349. package/dist/exe-export-OQXCJLWB.js +73 -0
  350. package/dist/exe-export-YLVAZQAV.js +73 -0
  351. package/dist/exe-import-7N46LSMQ.js +76 -0
  352. package/dist/exe-import-AEJYBLA7.js +76 -0
  353. package/dist/exe-import-FINYUV5T.js +76 -0
  354. package/dist/exe-import-HWPYARCG.js +76 -0
  355. package/dist/exe-import-K4TWTG24.js +76 -0
  356. package/dist/exe-import-LZKZQ54C.js +76 -0
  357. package/dist/exe-key-6FPQHBW6.js +579 -0
  358. package/dist/exe-key-H45JY44F.js +579 -0
  359. package/dist/exe-key-MAEQGTB7.js +579 -0
  360. package/dist/exe-key-N3XYSEXP.js +579 -0
  361. package/dist/exe-key-Q3ZNYT6L.js +579 -0
  362. package/dist/exe-key-Q47RPB45.js +579 -0
  363. package/dist/exe-snapshot-2USE2HHM.js +164 -0
  364. package/dist/exe-snapshot-3TEM3BFD.js +164 -0
  365. package/dist/exe-snapshot-HECGUHL3.js +164 -0
  366. package/dist/exe-snapshot-HZU66HXX.js +164 -0
  367. package/dist/exe-snapshot-L7OQWZUH.js +164 -0
  368. package/dist/exe-snapshot-X5N5KIVJ.js +164 -0
  369. package/dist/fast-db-init-3CNTADVO.js +7 -0
  370. package/dist/fast-db-init-C6IPNVPU.js +7 -0
  371. package/dist/fast-db-init-HXCS2AP5.js +7 -0
  372. package/dist/fast-db-init-I7CMGBAN.js +7 -0
  373. package/dist/fast-db-init-P6YESOUL.js +7 -0
  374. package/dist/fast-db-init-VDNEFVQF.js +7 -0
  375. package/dist/gateway/index.js +8 -8
  376. package/dist/git-staleness-YCEBBIVK.js +110 -0
  377. package/dist/git-task-sweep-C4OV2CEY.js +40 -0
  378. package/dist/git-task-sweep-H34STRNT.js +40 -0
  379. package/dist/git-task-sweep-J66SYJMW.js +40 -0
  380. package/dist/git-task-sweep-JYCD3ZKQ.js +40 -0
  381. package/dist/git-task-sweep-O723DB7F.js +40 -0
  382. package/dist/git-task-sweep-YL7NLDCK.js +40 -0
  383. package/dist/global-procedures-IHZM6C2K.js +20 -0
  384. package/dist/graph-auto-extract-RZQ3MHP2.js +162 -0
  385. package/dist/hooks/bug-report-worker.js +12 -12
  386. package/dist/hooks/codex-stop-task-finalizer.js +12 -12
  387. package/dist/hooks/commit-complete.js +12 -12
  388. package/dist/hooks/error-recall.js +6 -6
  389. package/dist/hooks/exe-heartbeat-hook.js +3 -3
  390. package/dist/hooks/ingest.js +6 -6
  391. package/dist/hooks/instructions-loaded.js +4 -4
  392. package/dist/hooks/manifest.json +19 -19
  393. package/dist/hooks/notification.js +4 -4
  394. package/dist/hooks/post-compact.js +11 -11
  395. package/dist/hooks/post-tool-combined.js +5 -5
  396. package/dist/hooks/pre-compact.js +12 -12
  397. package/dist/hooks/pre-tool-use.js +15 -15
  398. package/dist/hooks/prompt-submit.js +21 -21
  399. package/dist/hooks/session-end.js +16 -16
  400. package/dist/hooks/session-start.js +10 -10
  401. package/dist/hooks/stop.js +15 -15
  402. package/dist/hooks/subagent-stop.js +11 -11
  403. package/dist/hooks/summary-worker.js +15 -15
  404. package/dist/index.js +18 -18
  405. package/dist/installer-4EW5ZDGD.js +296 -0
  406. package/dist/installer-B2JTQO55.js +38 -0
  407. package/dist/installer-MIL352T7.js +342 -0
  408. package/dist/lib/agent-config.js +9 -3
  409. package/dist/lib/cloud-sync.js +4 -4
  410. package/dist/lib/consolidation.js +5 -5
  411. package/dist/lib/database.js +2 -2
  412. package/dist/lib/db.js +2 -2
  413. package/dist/lib/employee-templates.js +4 -4
  414. package/dist/lib/employees.js +2 -2
  415. package/dist/lib/exe-daemon.js +35 -34
  416. package/dist/lib/hybrid-search.js +5 -5
  417. package/dist/lib/identity.js +2 -2
  418. package/dist/lib/messaging.js +12 -10
  419. package/dist/lib/reminders.js +3 -3
  420. package/dist/lib/schedules.js +5 -5
  421. package/dist/lib/session-registry.js +4 -4
  422. package/dist/lib/skill-learning.js +4 -4
  423. package/dist/lib/store.js +4 -4
  424. package/dist/lib/task-router.js +3 -3
  425. package/dist/lib/tasks.js +11 -11
  426. package/dist/lib/tmux-routing.js +9 -9
  427. package/dist/lib/token-spend.js +3 -3
  428. package/dist/mcp/register-tools.js +54 -54
  429. package/dist/mcp/server.js +55 -55
  430. package/dist/mcp/tools/complete-reminder.js +4 -4
  431. package/dist/mcp/tools/create-reminder.js +4 -4
  432. package/dist/mcp/tools/create-task.js +13 -13
  433. package/dist/mcp/tools/deactivate-behavior.js +5 -5
  434. package/dist/mcp/tools/list-reminders.js +4 -4
  435. package/dist/mcp/tools/list-tasks.js +13 -13
  436. package/dist/mcp/tools/send-message.js +12 -12
  437. package/dist/mcp/tools/update-task.js +12 -12
  438. package/dist/mcp-http-config-OJQR246S.js +27 -0
  439. package/dist/memory-cards-IPULSQFA.js +174 -0
  440. package/dist/memory-graph-extractor-3TZZOKHY.js +17 -0
  441. package/dist/memory-poisoning-defense-SGUGR5YJ.js +225 -0
  442. package/dist/memory-reflection-H3WGCEM6.js +238 -0
  443. package/dist/notifications-65STXW6N.js +45 -0
  444. package/dist/notifications-K3JDUPL5.js +45 -0
  445. package/dist/notifications-KQOD66ZK.js +45 -0
  446. package/dist/notifications-PFK5OQEF.js +45 -0
  447. package/dist/notifications-VWPO6NJF.js +45 -0
  448. package/dist/notifications-WCSRQN2V.js +45 -0
  449. package/dist/orchestration-events-O5PSDEIO.js +25 -0
  450. package/dist/orchestrator-3D7QEVGP.js +33 -0
  451. package/dist/orchestrator-CC32RZO5.js +33 -0
  452. package/dist/orchestrator-DWAYSAFR.js +33 -0
  453. package/dist/orchestrator-RAPEJUOI.js +33 -0
  454. package/dist/orchestrator-TL37EAWA.js +33 -0
  455. package/dist/orchestrator-XPG6LJAI.js +33 -0
  456. package/dist/pipeline-router-5NT6FUC3.js +13 -0
  457. package/dist/pipeline-router-ADLTS6DZ.js +13 -0
  458. package/dist/pipeline-router-KSUXONDT.js +13 -0
  459. package/dist/pipeline-router-O6ZLSM6U.js +13 -0
  460. package/dist/pipeline-router-QKLYUYU7.js +13 -0
  461. package/dist/pipeline-router-W2W5XDND.js +13 -0
  462. package/dist/plan-limits-53NXLNDQ.js +26 -0
  463. package/dist/project-boot-ITN3FZMM.js +299 -0
  464. package/dist/projection-worker-27XX5M2W.js +964 -0
  465. package/dist/reranker-3KLYAHO4.js +19 -0
  466. package/dist/reranker-64KDRYPP.js +19 -0
  467. package/dist/reranker-GU7L2PJX.js +19 -0
  468. package/dist/reranker-MGY5A7BQ.js +19 -0
  469. package/dist/reranker-TZEXIJAN.js +19 -0
  470. package/dist/reranker-ZBX6HSU2.js +19 -0
  471. package/dist/review-polling-3ZZ2T26N.js +124 -0
  472. package/dist/review-polling-BBQUF54Q.js +124 -0
  473. package/dist/review-polling-FA2J2Q5O.js +124 -0
  474. package/dist/review-polling-MLS4BQ3N.js +124 -0
  475. package/dist/review-polling-RXQZPGRY.js +124 -0
  476. package/dist/review-polling-YBB6DKA5.js +124 -0
  477. package/dist/runtime/index.js +12 -12
  478. package/dist/session-events-EAODNMNR.js +36 -0
  479. package/dist/session-events-MVO6JNUL.js +36 -0
  480. package/dist/session-events-PRVDH3QS.js +36 -0
  481. package/dist/session-events-PU5OQKMB.js +36 -0
  482. package/dist/session-events-WWGF3B2N.js +36 -0
  483. package/dist/session-events-ZHXXAH6B.js +36 -0
  484. package/dist/session-kill-telemetry-O4TJHHOZ.js +29 -0
  485. package/dist/session-scope-CQXB7VMH.js +86 -0
  486. package/dist/session-scope-HHUMJYF6.js +86 -0
  487. package/dist/session-scope-M47JR2SD.js +86 -0
  488. package/dist/session-scope-MRQYSD5S.js +86 -0
  489. package/dist/session-scope-TAH5BUYW.js +86 -0
  490. package/dist/session-scope-UXZ6RUWC.js +86 -0
  491. package/dist/setup-wizard-UM2RHSBJ.js +12 -0
  492. package/dist/skill-refinement-447DZWNK.js +157 -0
  493. package/dist/skill-refinement-567JSF7L.js +157 -0
  494. package/dist/skill-refinement-6JVQ3TMS.js +157 -0
  495. package/dist/skill-refinement-MJPOHYD5.js +157 -0
  496. package/dist/skill-refinement-NVUBRK22.js +157 -0
  497. package/dist/skill-refinement-XNGD3C62.js +157 -0
  498. package/dist/stack-release-BAPCXMXW.js +713 -0
  499. package/dist/stack-release-W4TWTEZP.js +731 -0
  500. package/dist/steward-gate-VLE7OCKO.js +13 -0
  501. package/dist/task-enforcement-AZEO67N6.js +391 -0
  502. package/dist/task-enforcement-EOYP6IO4.js +391 -0
  503. package/dist/task-enforcement-FUHDL6UR.js +391 -0
  504. package/dist/task-enforcement-L5XQKFOV.js +391 -0
  505. package/dist/task-enforcement-QL3K4N3F.js +391 -0
  506. package/dist/task-enforcement-RJPWWEAE.js +391 -0
  507. package/dist/task-scope-DRQRNWB7.js +35 -0
  508. package/dist/task-scope-GS7TS3UV.js +35 -0
  509. package/dist/task-scope-KQNCP42W.js +35 -0
  510. package/dist/task-scope-SM5F6RD3.js +35 -0
  511. package/dist/task-scope-TZYMB634.js +35 -0
  512. package/dist/task-scope-ZVLUBS4C.js +35 -0
  513. package/dist/tasks-crud-4MSLJWXE.js +77 -0
  514. package/dist/tasks-crud-6TWWETGB.js +77 -0
  515. package/dist/tasks-crud-DBHYO4MM.js +77 -0
  516. package/dist/tasks-crud-EFYWPPEI.js +77 -0
  517. package/dist/tasks-crud-HIPXKRKX.js +77 -0
  518. package/dist/tasks-crud-JIS5B4GZ.js +77 -0
  519. package/dist/tasks-notify-7JBUNE7R.js +38 -0
  520. package/dist/tasks-notify-GEJKT5TO.js +38 -0
  521. package/dist/tasks-notify-OW3JDPLK.js +38 -0
  522. package/dist/tasks-notify-UPIJ3L4O.js +38 -0
  523. package/dist/tasks-notify-W5WVP2FG.js +38 -0
  524. package/dist/tasks-notify-YKEOYOKN.js +38 -0
  525. package/dist/tasks-review-5SJSFTUB.js +47 -0
  526. package/dist/tasks-review-DRKN34HO.js +47 -0
  527. package/dist/tasks-review-IQSAXXXE.js +47 -0
  528. package/dist/tasks-review-JHSYBR5I.js +47 -0
  529. package/dist/tasks-review-KWELLLS3.js +47 -0
  530. package/dist/tasks-review-SUJ6AKAS.js +47 -0
  531. package/dist/telemetry-upload-BSGOXGUP.js +739 -0
  532. package/dist/telemetry-upload-FPQAB6ZU.js +739 -0
  533. package/dist/telemetry-upload-LTX3C5HZ.js +739 -0
  534. package/dist/telemetry-upload-MYVBVUGE.js +739 -0
  535. package/dist/telemetry-upload-UPAABLGK.js +739 -0
  536. package/dist/telemetry-upload-UYEHBFGO.js +739 -0
  537. package/dist/token-budget-2CDWQU3Q.js +84 -0
  538. package/dist/tool-telemetry-7YS7EN7B.js +17 -0
  539. package/dist/tui/App.js +17 -17
  540. package/dist/tui-data-GDGBOS6G.js +258 -0
  541. package/dist/tui-data-LYUZFNO4.js +258 -0
  542. package/dist/tui-data-QM5BOKRF.js +258 -0
  543. package/dist/tui-data-VAE43SM3.js +258 -0
  544. package/dist/tui-data-VXF2RBVM.js +258 -0
  545. package/dist/tui-data-X7HT3FXF.js +258 -0
  546. package/dist/wiki-acl-MJIMXRQV.js +111 -0
  547. package/dist/worker-gate-BZBWTMCY.js +21 -0
  548. package/dist/worker-gate-CHVL6UGT.js +21 -0
  549. package/dist/worker-gate-KQFS4RJE.js +21 -0
  550. package/dist/worker-gate-NRP7CMS7.js +21 -0
  551. package/dist/worker-gate-WQGTZOSM.js +21 -0
  552. package/dist/worker-gate-X2YDTKTL.js +21 -0
  553. package/dist/workflow-engine-AKKOMJJQ.js +28 -0
  554. package/dist/workflow-engine-CYXRZXBM.js +28 -0
  555. package/dist/workflow-engine-EHWQO3LX.js +28 -0
  556. package/dist/workflow-engine-I3OUMSF4.js +28 -0
  557. package/dist/workflow-engine-KMLAXVA4.js +28 -0
  558. package/dist/workflow-engine-PHTLEAXP.js +28 -0
  559. package/dist/worktree-NLSKVRNC.js +26 -0
  560. package/dist/worktree-sweep-44TMEPLE.js +19 -0
  561. package/package.json +1 -1
  562. package/release-notes.json +46 -35
@@ -0,0 +1,1921 @@
1
+ import {
2
+ recordSessionKill
3
+ } from "./chunk-SG2ANG5C.js";
4
+ import {
5
+ updateTask
6
+ } from "./chunk-3OM3V545.js";
7
+ import {
8
+ ensureEmployee,
9
+ extractRootExe,
10
+ getSessionState,
11
+ isExeSession,
12
+ queryTaskRows,
13
+ sendIntercom,
14
+ sessionScopeFilter,
15
+ strictSessionScopeFilter,
16
+ writeNotification
17
+ } from "./chunk-YHSATGMH.js";
18
+ import {
19
+ queueIntercom
20
+ } from "./chunk-5CHYEKMH.js";
21
+ import {
22
+ listSessions
23
+ } from "./chunk-OBUV3W7L.js";
24
+ import {
25
+ getTransport
26
+ } from "./chunk-MVW62NIZ.js";
27
+ import {
28
+ listTmuxSessions,
29
+ parseContextPercentage
30
+ } from "./chunk-CX6GL3ZJ.js";
31
+ import {
32
+ recordOrchestrationEventBestEffort
33
+ } from "./chunk-2NQQP3FF.js";
34
+ import {
35
+ getAgentRuntime
36
+ } from "./chunk-EZ7KAZMC.js";
37
+ import {
38
+ baseAgentName,
39
+ isCoordinatorName,
40
+ shouldAutoInstance
41
+ } from "./chunk-CHBHR5W6.js";
42
+ import {
43
+ loadConfigSync
44
+ } from "./chunk-VXIMSRTO.js";
45
+
46
+ // src/lib/daemon-orchestration.ts
47
+ import { execSync } from "child_process";
48
+ import { randomUUID } from "crypto";
49
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync, unlinkSync, statSync } from "fs";
50
+ import { homedir, loadavg, freemem, cpus as osCpus } from "os";
51
+ import { join } from "path";
52
+
53
+ // src/lib/agent-signals.ts
54
+ import { readFileSync, existsSync } from "fs";
55
+ import os from "os";
56
+ import path from "path";
57
+ var CONSERVATIVE_ON_ERROR = true;
58
+ async function hasOpenTasks(client, agentId, sessionScope) {
59
+ try {
60
+ const scope = sessionScopeFilter(sessionScope);
61
+ const result = await client.execute({
62
+ sql: `SELECT 1 FROM tasks
63
+ WHERE assigned_to = ? AND status IN ('open', 'in_progress', 'blocked')${scope.sql}
64
+ LIMIT 1`,
65
+ args: [agentId, ...scope.args]
66
+ });
67
+ return result.rows.length > 0;
68
+ } catch {
69
+ return CONSERVATIVE_ON_ERROR;
70
+ }
71
+ }
72
+ async function hasNeedsReview(client, agentId, sessionScope) {
73
+ try {
74
+ const scope = sessionScopeFilter(sessionScope);
75
+ const result = await client.execute({
76
+ sql: `SELECT 1 FROM tasks
77
+ WHERE assigned_to = ? AND status = 'needs_review'${scope.sql}
78
+ LIMIT 1`,
79
+ args: [agentId, ...scope.args]
80
+ });
81
+ return result.rows.length > 0;
82
+ } catch {
83
+ return CONSERVATIVE_ON_ERROR;
84
+ }
85
+ }
86
+ async function hasUnreadInbox(client, agentId, sessionScope) {
87
+ try {
88
+ const scope = strictSessionScopeFilter(sessionScope);
89
+ const result = await client.execute({
90
+ sql: `SELECT 1 FROM messages
91
+ WHERE target_agent = ? AND status = 'pending'
92
+ ${scope.sql}
93
+ LIMIT 1`,
94
+ args: [agentId, ...scope.args]
95
+ });
96
+ return result.rows.length > 0;
97
+ } catch {
98
+ return CONSERVATIVE_ON_ERROR;
99
+ }
100
+ }
101
+ function hadRecentIntercomAck(sessionName, windowMs, nowMs = Date.now(), intercomLog = path.join(os.homedir(), ".exe-os", "intercom.log")) {
102
+ if (!existsSync(intercomLog)) return false;
103
+ try {
104
+ const raw = readFileSync(intercomLog, "utf8");
105
+ const lines = raw.split("\n");
106
+ for (let i = lines.length - 1; i >= 0; i--) {
107
+ const line = lines[i];
108
+ if (!line || !line.includes(sessionName)) continue;
109
+ const tsMatch = line.match(/^(\d{4}-\d{2}-\d{2}T[\d:.]+Z)/);
110
+ if (!tsMatch) continue;
111
+ const ts = Date.parse(tsMatch[1]);
112
+ if (!Number.isFinite(ts)) continue;
113
+ if (nowMs - ts <= windowMs) return true;
114
+ return false;
115
+ }
116
+ return false;
117
+ } catch {
118
+ return true;
119
+ }
120
+ }
121
+ async function collectAgentSignals(client, agentId, sessionName, intercomAckWindowMs, nowMs = Date.now()) {
122
+ const coordinatorScope = extractRootExe(sessionName) ?? null;
123
+ const [open, review, inbox] = await Promise.all([
124
+ hasOpenTasks(client, agentId, coordinatorScope),
125
+ hasNeedsReview(client, agentId, coordinatorScope),
126
+ hasUnreadInbox(client, agentId, coordinatorScope)
127
+ ]);
128
+ return {
129
+ hasOpenTasks: open,
130
+ hasNeedsReview: review,
131
+ hasUnreadInbox: inbox,
132
+ hadRecentIntercomAck: hadRecentIntercomAck(sessionName, intercomAckWindowMs, nowMs)
133
+ };
134
+ }
135
+
136
+ // src/lib/daemon-orchestration.ts
137
+ var cpuCount = () => osCpus().length;
138
+ var IDLE_NUDGE_DEDUP_MS = Number(process.env.EXE_NUDGE_INTERVAL_MS) || 6e4;
139
+ var SESSION_TTL_HOURS = Number(process.env.EXE_SESSION_TTL_HOURS) || 8;
140
+ var SESSION_CONTEXT_THRESHOLD_PCT = Number(process.env.EXE_CONTEXT_KILL_PCT) || 80;
141
+ var IDLE_KILL_INTERCOM_ACK_WINDOW_MS = Number(process.env.EXE_INTERCOM_ACK_WINDOW_MS) || 6e4;
142
+ function shouldNudgeEmployee(sessionState, hasOpenTasks2, lastNudgeMs, nowMs, dedupMs) {
143
+ if (sessionState !== "idle") return false;
144
+ if (!hasOpenTasks2) return false;
145
+ if (nowMs - lastNudgeMs < dedupMs) return false;
146
+ return true;
147
+ }
148
+ function shouldKillSession(ageHours, contextPct) {
149
+ return classifyTtlKillReason(ageHours, contextPct) !== null;
150
+ }
151
+ function classifyTtlKillReason(ageHours, contextPct) {
152
+ if (ageHours > SESSION_TTL_HOURS) return "ttl_age";
153
+ if (contextPct !== null && contextPct > SESSION_CONTEXT_THRESHOLD_PCT) return "ttl_context";
154
+ return null;
155
+ }
156
+ function shouldKillIdleSession(input) {
157
+ if (!input.enabled) return false;
158
+ if (isExeSession(input.sessionName)) return false;
159
+ if (input.state !== "idle" && input.state !== "no_claude") return false;
160
+ if (input.signals.hasOpenTasks) return false;
161
+ if (input.signals.hasNeedsReview) return false;
162
+ if (input.signals.hasUnreadInbox) return false;
163
+ if (input.signals.hadRecentIntercomAck) return false;
164
+ if (input.idleTicks < input.ticksRequired) return false;
165
+ return true;
166
+ }
167
+ async function pollIdleEmployees(deps, lastNudge) {
168
+ const registered = deps.listRegisteredSessions().filter(
169
+ (s) => !isCoordinatorName(s.agentId)
170
+ );
171
+ if (registered.length === 0) return [];
172
+ let liveSessions;
173
+ try {
174
+ liveSessions = new Set(deps.listTmuxSessions());
175
+ } catch (e) {
176
+ process.stderr.write("[daemon-orch] list tmux sessions for idle nudge: " + (e instanceof Error ? e.message : String(e)) + "\n");
177
+ return [];
178
+ }
179
+ const nudged = [];
180
+ const now = Date.now();
181
+ for (const entry of registered) {
182
+ if (!liveSessions.has(entry.windowName)) continue;
183
+ const state = deps.getSessionState(entry.windowName);
184
+ const lastMs = lastNudge.get(entry.windowName) ?? 0;
185
+ if (state !== "idle") continue;
186
+ if (now - lastMs < IDLE_NUDGE_DEDUP_MS) continue;
187
+ const employeeScope = entry.windowName.includes("-") ? entry.windowName.slice(entry.windowName.indexOf("-") + 1) : null;
188
+ const task = await deps.queryOpenTask(entry.agentId, employeeScope);
189
+ if (!task) continue;
190
+ if (shouldNudgeEmployee(state, true, lastMs, now, IDLE_NUDGE_DEDUP_MS)) {
191
+ const runtime = deps.getAgentRuntime?.(entry.agentId) ?? "claude";
192
+ if (runtime !== "claude" && deps.killSession) {
193
+ process.stderr.write(
194
+ `[idle-nudge] Killing idle ${runtime} session ${entry.windowName} (intercom not supported) \u2014 auto-wake will respawn
195
+ `
196
+ );
197
+ deps.killSession(entry.windowName);
198
+ } else {
199
+ deps.sendIntercom(entry.windowName);
200
+ }
201
+ lastNudge.set(entry.windowName, now);
202
+ nudged.push(entry.windowName);
203
+ }
204
+ }
205
+ return nudged;
206
+ }
207
+ async function checkSessionTTL(deps) {
208
+ const registered = deps.listRegisteredSessions().filter(
209
+ (s) => !isCoordinatorName(s.agentId) && !isExeSession(s.windowName)
210
+ );
211
+ if (registered.length === 0) return [];
212
+ let liveSessions;
213
+ try {
214
+ liveSessions = new Set(deps.listTmuxSessions());
215
+ } catch (e) {
216
+ process.stderr.write("[daemon-orch] list tmux sessions for TTL check: " + (e instanceof Error ? e.message : String(e)) + "\n");
217
+ return [];
218
+ }
219
+ const killed = [];
220
+ for (const entry of registered) {
221
+ if (!liveSessions.has(entry.windowName)) continue;
222
+ if (isCoordinatorName(entry.agentId) || isExeSession(entry.windowName)) continue;
223
+ const createdEpoch = deps.getSessionCreatedEpoch(entry.windowName);
224
+ if (createdEpoch === null) continue;
225
+ const ageHours = (Date.now() / 1e3 - createdEpoch) / 3600;
226
+ const contextPct = deps.parseContextPercentage(entry.windowName);
227
+ const killReason = classifyTtlKillReason(ageHours, contextPct);
228
+ if (killReason !== null) {
229
+ const isContextFull = contextPct !== null && contextPct > 85;
230
+ if (deps.hasActiveTasks && !isContextFull) {
231
+ try {
232
+ const active = await deps.hasActiveTasks(entry.agentId);
233
+ if (active) {
234
+ process.stderr.write(
235
+ `[exed] Session TTL: SKIPPING ${entry.windowName} (age=${ageHours.toFixed(1)}h, reason=${killReason}) \u2014 agent has active tasks
236
+ `
237
+ );
238
+ continue;
239
+ }
240
+ } catch {
241
+ process.stderr.write(
242
+ `[exed] Session TTL: SKIPPING ${entry.windowName} \u2014 hasActiveTasks query failed (conservative)
243
+ `
244
+ );
245
+ continue;
246
+ }
247
+ }
248
+ if (isContextFull) {
249
+ process.stderr.write(
250
+ `[exed] Session TTL: context-full override for ${entry.windowName} (${contextPct}% used) \u2014 killing despite active tasks
251
+ `
252
+ );
253
+ }
254
+ process.stderr.write(
255
+ `[exed] Session TTL: killing ${entry.windowName} (age=${ageHours.toFixed(1)}h, context=${contextPct}%, reason=${killReason})
256
+ `
257
+ );
258
+ deps.recordKill?.({
259
+ sessionName: entry.windowName,
260
+ agentId: entry.agentId,
261
+ reason: killReason
262
+ });
263
+ recordOrchestrationEventBestEffort({
264
+ eventType: killReason === "ttl_age" ? "session.ttl_kill" : "session.context_kill",
265
+ source: "daemon-orchestration.checkSessionTTL",
266
+ agentId: entry.agentId,
267
+ tmuxSession: entry.windowName,
268
+ sessionScope: entry.windowName.includes("-") ? entry.windowName.slice(entry.windowName.indexOf("-") + 1) : null,
269
+ payload: { ageHours: parseFloat(ageHours.toFixed(2)), contextPct, killReason }
270
+ });
271
+ deps.killSession(entry.windowName);
272
+ killed.push(entry.windowName);
273
+ }
274
+ }
275
+ return killed;
276
+ }
277
+ async function pollIdleKill(deps, idleTickCounts, opts) {
278
+ if (!opts.enabled) return [];
279
+ const registered = deps.listRegisteredSessions().filter(
280
+ (s) => !isCoordinatorName(s.agentId) && !isExeSession(s.windowName)
281
+ );
282
+ if (registered.length === 0) return [];
283
+ let liveSessions;
284
+ try {
285
+ liveSessions = new Set(deps.listTmuxSessions());
286
+ } catch (e) {
287
+ process.stderr.write("[daemon-orch] list tmux sessions for idle kill: " + (e instanceof Error ? e.message : String(e)) + "\n");
288
+ return [];
289
+ }
290
+ const killed = [];
291
+ for (const entry of registered) {
292
+ if (!liveSessions.has(entry.windowName)) {
293
+ idleTickCounts.delete(entry.windowName);
294
+ continue;
295
+ }
296
+ const state = deps.getSessionState(entry.windowName);
297
+ if (state !== "idle" && state !== "no_claude") {
298
+ idleTickCounts.delete(entry.windowName);
299
+ continue;
300
+ }
301
+ const signals = await deps.collectSignals(entry.agentId, entry.windowName);
302
+ if (signals.hasOpenTasks) {
303
+ const instanceName = entry.windowName.split("-")[0] ?? entry.agentId;
304
+ if (instanceName !== entry.agentId) {
305
+ try {
306
+ const markerPath = join(
307
+ homedir(),
308
+ ".exe-os",
309
+ "session-cache",
310
+ `current-task-${instanceName}.json`
311
+ );
312
+ if (!existsSync2(markerPath)) {
313
+ signals.hasOpenTasks = false;
314
+ }
315
+ } catch {
316
+ }
317
+ }
318
+ }
319
+ if (entry.registeredAt) {
320
+ const ageMs = Date.now() - new Date(entry.registeredAt).getTime();
321
+ if (ageMs < 5 * 60 * 1e3) {
322
+ idleTickCounts.delete(entry.windowName);
323
+ continue;
324
+ }
325
+ }
326
+ if (signals.hasOpenTasks || signals.hasNeedsReview || signals.hasUnreadInbox || signals.hadRecentIntercomAck) {
327
+ idleTickCounts.delete(entry.windowName);
328
+ continue;
329
+ }
330
+ const prevTicks = idleTickCounts.get(entry.windowName) ?? 0;
331
+ const nextTicks = prevTicks + 1;
332
+ const shouldKill = shouldKillIdleSession({
333
+ sessionName: entry.windowName,
334
+ state,
335
+ signals,
336
+ idleTicks: nextTicks,
337
+ ticksRequired: opts.ticksRequired,
338
+ enabled: opts.enabled
339
+ });
340
+ const warningThreshold = Math.max(1, Math.floor(opts.ticksRequired * 0.7));
341
+ if (nextTicks === warningThreshold && !shouldKill) {
342
+ try {
343
+ const { execFileSync: warnExec } = await import("child_process");
344
+ const shutdownMsg = [
345
+ "SHUTDOWN WARNING: Your session will be terminated in ~90 seconds due to inactivity.",
346
+ "All your tasks are complete. Before shutdown:",
347
+ "1. store_memory() any important decisions, discoveries, or context from this session",
348
+ "2. If you learned anything non-obvious, commit it to memory for future sessions",
349
+ "3. If you have in-progress thoughts or plans, checkpoint them now",
350
+ "This is your last chance to persist state. After shutdown, this context is lost."
351
+ ].join(" ");
352
+ warnExec("tmux", ["send-keys", "-t", entry.windowName, "-l", shutdownMsg], { timeout: 2e3 });
353
+ warnExec("tmux", ["send-keys", "-t", entry.windowName, "Enter"], { timeout: 1e3 });
354
+ process.stderr.write(`[exed] Idle wind-down warning sent to ${entry.windowName} (tick ${nextTicks}/${opts.ticksRequired})
355
+ `);
356
+ } catch {
357
+ }
358
+ }
359
+ if (shouldKill) {
360
+ process.stderr.write(
361
+ `[exed] Idle kill: ${entry.windowName} (agentId=${entry.agentId}, ticks=${nextTicks})
362
+ `
363
+ );
364
+ deps.recordKill?.({
365
+ sessionName: entry.windowName,
366
+ agentId: entry.agentId,
367
+ reason: "idle",
368
+ ticksIdle: nextTicks
369
+ });
370
+ recordOrchestrationEventBestEffort({
371
+ eventType: "session.idle_kill",
372
+ source: "daemon-orchestration.pollIdleKill",
373
+ agentId: entry.agentId,
374
+ tmuxSession: entry.windowName,
375
+ sessionScope: entry.windowName.includes("-") ? entry.windowName.slice(entry.windowName.indexOf("-") + 1) : null,
376
+ payload: { ticksIdle: nextTicks }
377
+ });
378
+ deps.killSession(entry.windowName);
379
+ try {
380
+ const { execFileSync: verifyExec } = await import("child_process");
381
+ verifyExec("tmux", ["has-session", "-t", entry.windowName], { timeout: 2e3 });
382
+ process.stderr.write(
383
+ `[exed] Idle kill FAILED for ${entry.windowName} \u2014 session still alive, retrying with force
384
+ `
385
+ );
386
+ verifyExec("tmux", ["kill-session", "-t", entry.windowName], { timeout: 3e3 });
387
+ } catch {
388
+ }
389
+ killed.push(entry.windowName);
390
+ idleTickCounts.delete(entry.windowName);
391
+ } else {
392
+ idleTickCounts.set(entry.windowName, nextTicks);
393
+ }
394
+ }
395
+ return killed;
396
+ }
397
+ var REVIEW_NUDGE_COOLDOWN_MS = 3e5;
398
+ async function pollReviewNudge(deps, state) {
399
+ let sessions;
400
+ try {
401
+ sessions = deps.listTmuxSessions().filter((s) => isExeSession(s));
402
+ } catch (e) {
403
+ process.stderr.write("[daemon-orch] list tmux sessions for review nudge: " + (e instanceof Error ? e.message : String(e)) + "\n");
404
+ return [];
405
+ }
406
+ if (sessions.length === 0) return [];
407
+ const nudged = [];
408
+ const now = Date.now();
409
+ for (const exeSession of sessions) {
410
+ const prev = state.lastNudge.get(exeSession);
411
+ if (prev && now - prev.at < REVIEW_NUDGE_COOLDOWN_MS) continue;
412
+ const sessionState = deps.getSessionState(exeSession);
413
+ if (sessionState !== "idle") continue;
414
+ let count;
415
+ try {
416
+ count = await deps.countReviewsForScope(exeSession);
417
+ } catch (e) {
418
+ process.stderr.write("[daemon-orch] count reviews for scope: " + (e instanceof Error ? e.message : String(e)) + "\n");
419
+ continue;
420
+ }
421
+ if (count === 0) continue;
422
+ if (prev && prev.count === count) continue;
423
+ try {
424
+ deps.sendNudge(exeSession);
425
+ state.lastNudge.set(exeSession, { at: now, count });
426
+ nudged.push(exeSession);
427
+ } catch (e) {
428
+ process.stderr.write("[daemon-orch] send review nudge: " + (e instanceof Error ? e.message : String(e)) + "\n");
429
+ }
430
+ }
431
+ if (nudged.length > 0 && deps.persistState) {
432
+ try {
433
+ deps.persistState(state);
434
+ } catch {
435
+ }
436
+ }
437
+ return nudged;
438
+ }
439
+ var NUDGE_STATE_PATH = join(homedir(), ".exe-os", "review-nudge-state.json");
440
+ function loadNudgeState() {
441
+ const state = { lastNudge: /* @__PURE__ */ new Map() };
442
+ try {
443
+ if (!existsSync2(NUDGE_STATE_PATH)) return state;
444
+ const raw = JSON.parse(readFileSync2(NUDGE_STATE_PATH, "utf8"));
445
+ if (Array.isArray(raw)) {
446
+ for (const [key, val] of raw) {
447
+ if (key && typeof val?.at === "number" && typeof val?.count === "number") {
448
+ state.lastNudge.set(key, val);
449
+ }
450
+ }
451
+ }
452
+ } catch {
453
+ }
454
+ return state;
455
+ }
456
+ function saveNudgeState(state) {
457
+ const entries = Array.from(state.lastNudge.entries());
458
+ writeFileSync(NUDGE_STATE_PATH, JSON.stringify(entries), "utf8");
459
+ }
460
+ function createReviewNudgeRealDeps(_getClient) {
461
+ return {
462
+ listTmuxSessions: () => listTmuxSessions(),
463
+ getSessionState: (sessionName) => getSessionState(sessionName),
464
+ countReviewsForScope: async (sessionScope) => {
465
+ const rows = await queryTaskRows({
466
+ status: "needs_review",
467
+ columns: "COUNT(*) as cnt",
468
+ sessionScope,
469
+ projectName: null,
470
+ limit: 1,
471
+ orderBy: "1"
472
+ });
473
+ return Number(rows[0]?.cnt ?? 0);
474
+ },
475
+ sendNudge: (sessionName) => {
476
+ queueIntercom(sessionName, "review nudge: pending reviews", "completion");
477
+ },
478
+ persistState: saveNudgeState
479
+ };
480
+ }
481
+ function createIdleNudgeRealDeps(_getClient) {
482
+ return {
483
+ listRegisteredSessions: () => listSessions(),
484
+ listTmuxSessions: () => listTmuxSessions(),
485
+ getSessionState: (sessionName) => getSessionState(sessionName),
486
+ queryOpenTask: async (agentId, sessionScope) => {
487
+ const rows = await queryTaskRows({
488
+ assignedTo: agentId,
489
+ status: ["open", "in_progress"],
490
+ columns: "id, title, priority",
491
+ sessionScope: sessionScope ?? null,
492
+ projectName: null,
493
+ orderBy: "CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 ELSE 2 END",
494
+ limit: 1
495
+ });
496
+ if (rows.length === 0) return null;
497
+ return rows[0];
498
+ },
499
+ sendIntercom: (sessionName) => sendIntercom(sessionName),
500
+ killSession: (sessionName) => {
501
+ getTransport().kill(sessionName);
502
+ },
503
+ getAgentRuntime: (agentId) => {
504
+ try {
505
+ return getAgentRuntime(agentId).runtime;
506
+ } catch {
507
+ return "claude";
508
+ }
509
+ }
510
+ };
511
+ }
512
+ function createSessionTTLRealDeps(getClient) {
513
+ return {
514
+ listRegisteredSessions: () => listSessions(),
515
+ listTmuxSessions: () => listTmuxSessions(),
516
+ getSessionCreatedEpoch: (sessionName) => {
517
+ try {
518
+ const sessions = listSessions();
519
+ const entry = sessions.find((s) => s.windowName === sessionName);
520
+ if (entry?.registeredAt) {
521
+ const epoch = new Date(entry.registeredAt).getTime() / 1e3;
522
+ if (!isNaN(epoch)) return epoch;
523
+ }
524
+ } catch {
525
+ }
526
+ try {
527
+ const out = execSync(
528
+ `tmux display-message -t ${JSON.stringify(sessionName)} -p '#{session_created}' 2>/dev/null`,
529
+ { encoding: "utf8", timeout: 3e3 }
530
+ ).trim();
531
+ const epoch = parseInt(out, 10);
532
+ return isNaN(epoch) ? null : epoch;
533
+ } catch {
534
+ return null;
535
+ }
536
+ },
537
+ parseContextPercentage: (sessionName) => parseContextPercentage(sessionName),
538
+ killSession: (sessionName) => {
539
+ getTransport().kill(sessionName);
540
+ },
541
+ hasActiveTasks: getClient ? async (agentId) => {
542
+ const rows = await queryTaskRows({
543
+ assignedTo: agentId,
544
+ status: ["open", "in_progress"],
545
+ columns: "1",
546
+ sessionScope: null,
547
+ // null = no session filter (check all)
548
+ projectName: null,
549
+ limit: 1
550
+ });
551
+ return rows.length > 0;
552
+ } : void 0,
553
+ recordKill: (input) => {
554
+ void recordSessionKill(input);
555
+ }
556
+ };
557
+ }
558
+ function createIdleKillRealDeps(getClient, intercomAckWindowMs) {
559
+ return {
560
+ listRegisteredSessions: () => listSessions(),
561
+ listTmuxSessions: () => listTmuxSessions(),
562
+ getSessionState: (sessionName) => getSessionState(sessionName),
563
+ collectSignals: async (agentId, sessionName) => {
564
+ return collectAgentSignals(getClient(), agentId, sessionName, intercomAckWindowMs);
565
+ },
566
+ killSession: (sessionName) => {
567
+ getTransport().kill(sessionName);
568
+ },
569
+ recordKill: (input) => {
570
+ void recordSessionKill(input);
571
+ }
572
+ };
573
+ }
574
+ var AUTO_WAKE_COOLDOWN_MS = 5 * 60 * 1e3;
575
+ var AUTO_WAKE_MAX_RETRIES = 3;
576
+ var AUTO_WAKE_STATE_PATH = join(homedir(), ".exe-os", "session-cache", "auto-wake-state.json");
577
+ var CRASH_LOOP_WINDOW_MS = 15 * 60 * 1e3;
578
+ var CRASH_LOOP_THRESHOLD = 3;
579
+ var BACKOFF_BASE_MS = 2 * 60 * 1e3;
580
+ var BACKOFF_CAP_MS = 30 * 60 * 1e3;
581
+ var CPU_PRESSURE_THRESHOLD = 80;
582
+ var FREE_RAM_MIN_GB = 4;
583
+ function loadAutoWakeState() {
584
+ try {
585
+ if (existsSync2(AUTO_WAKE_STATE_PATH)) {
586
+ const raw = JSON.parse(readFileSync2(AUTO_WAKE_STATE_PATH, "utf8"));
587
+ return {
588
+ lastSpawn: raw.lastSpawn ?? {},
589
+ taskRetries: raw.taskRetries ?? {},
590
+ recentSpawns: raw.recentSpawns ?? {},
591
+ crashLoopCount: raw.crashLoopCount ?? {},
592
+ lastSaved: raw.lastSaved ?? 0
593
+ };
594
+ }
595
+ } catch {
596
+ }
597
+ return { lastSpawn: {}, taskRetries: {}, recentSpawns: {}, crashLoopCount: {}, lastSaved: 0 };
598
+ }
599
+ function saveAutoWakeState(state) {
600
+ try {
601
+ state.lastSaved = Date.now();
602
+ writeFileSync(AUTO_WAKE_STATE_PATH, JSON.stringify(state), "utf8");
603
+ } catch {
604
+ }
605
+ }
606
+ var _autoWakeState = loadAutoWakeState();
607
+ var _autoWakeLastSpawn = {
608
+ get: (key) => _autoWakeState.lastSpawn[key],
609
+ set: (key, val) => {
610
+ _autoWakeState.lastSpawn[key] = val;
611
+ }
612
+ };
613
+ var _autoWakeTaskRetries = {
614
+ get: (key) => _autoWakeState.taskRetries[key],
615
+ set: (key, val) => {
616
+ _autoWakeState.taskRetries[key] = val;
617
+ }
618
+ };
619
+ var _autoWakeRecentSpawns = {
620
+ get: (key) => _autoWakeState.recentSpawns[key],
621
+ set: (key, val) => {
622
+ _autoWakeState.recentSpawns[key] = val;
623
+ }
624
+ };
625
+ function _resetAutoWakeState() {
626
+ _autoWakeState = { lastSpawn: {}, taskRetries: {}, recentSpawns: {}, crashLoopCount: {}, lastSaved: 0 };
627
+ try {
628
+ unlinkSync(AUTO_WAKE_STATE_PATH);
629
+ } catch {
630
+ }
631
+ }
632
+ function checkAutoWakeGates(key, nowMs) {
633
+ try {
634
+ const lagMarker = join(homedir(), ".exe-os", "session-cache", "event-loop-blocked.marker");
635
+ if (existsSync2(lagMarker)) {
636
+ const age = nowMs - statSync(lagMarker).mtimeMs;
637
+ if (age < 6e4) return "daemon_event_loop_blocked";
638
+ }
639
+ } catch {
640
+ }
641
+ const loopCount = _autoWakeState.crashLoopCount[key] ?? 0;
642
+ if (loopCount > 0) {
643
+ const backoffMs = Math.min(BACKOFF_BASE_MS * Math.pow(2, loopCount - 1), BACKOFF_CAP_MS);
644
+ const lastSpawn = _autoWakeState.lastSpawn[key] ?? 0;
645
+ if (nowMs - lastSpawn < backoffMs) {
646
+ return `crash_loop_backoff_${Math.round(backoffMs / 6e4)}min`;
647
+ }
648
+ }
649
+ try {
650
+ const loadAvg = loadavg()[0];
651
+ const cpus = cpuCount();
652
+ const loadPct = loadAvg / cpus * 100;
653
+ if (loadPct > CPU_PRESSURE_THRESHOLD) return `cpu_pressure_${Math.round(loadPct)}pct`;
654
+ const freeGB = freemem() / 1024 ** 3;
655
+ if (freeGB < FREE_RAM_MIN_GB) return `low_ram_${freeGB.toFixed(1)}gb`;
656
+ } catch {
657
+ }
658
+ return null;
659
+ }
660
+ function shouldAutoWake(input) {
661
+ if (isCoordinatorName(input.agentId)) return false;
662
+ if (input.nowMs - input.lastSpawnMs < input.cooldownMs) return false;
663
+ const running = input.runningInstances ?? (input.hasRunningSession ? 1 : 0);
664
+ const pending = input.pendingTaskGroups ?? 1;
665
+ if (running >= pending) return false;
666
+ return true;
667
+ }
668
+ async function pollOrphanedTasks(deps, nowMs = Date.now()) {
669
+ const checkGate = deps.checkGate ?? checkAutoWakeGates;
670
+ const globalGate = checkGate("__global__", nowMs);
671
+ if (globalGate) {
672
+ process.stderr.write(`[auto-wake] GLOBAL GATE BLOCKED: ${globalGate} \u2014 skipping all spawns
673
+ `);
674
+ return [];
675
+ }
676
+ let liveSessions;
677
+ try {
678
+ liveSessions = deps.listTmuxSessions();
679
+ } catch (e) {
680
+ process.stderr.write("[daemon-orch] list tmux sessions for orphan poll: " + (e instanceof Error ? e.message : String(e)) + "\n");
681
+ return [];
682
+ }
683
+ const liveAgentScopedCounts = /* @__PURE__ */ new Map();
684
+ for (const session of liveSessions) {
685
+ const agent = deps.parseAgentFromSession(session);
686
+ if (agent) {
687
+ const dashIdx = session.indexOf("-");
688
+ const scope = dashIdx >= 0 ? session.slice(dashIdx + 1) : session;
689
+ const scopedKey = `${agent}::${scope}`;
690
+ liveAgentScopedCounts.set(scopedKey, (liveAgentScopedCounts.get(scopedKey) ?? 0) + 1);
691
+ }
692
+ }
693
+ const coordinatorSessions = liveSessions.filter((s) => isExeSession(s));
694
+ let tasksByAgent;
695
+ try {
696
+ tasksByAgent = await deps.queryTasksByAgent();
697
+ } catch (e) {
698
+ process.stderr.write("[daemon-orch] query tasks by agent: " + (e instanceof Error ? e.message : String(e)) + "\n");
699
+ return [];
700
+ }
701
+ const coordinatorSet = new Set(coordinatorSessions);
702
+ const VALID_SESSION_SCOPE = /^[a-zA-Z0-9_]+$/;
703
+ const agentTasks = /* @__PURE__ */ new Map();
704
+ for (const t of tasksByAgent) {
705
+ const rawScope = t.sessionScope;
706
+ if (rawScope && !VALID_SESSION_SCOPE.test(rawScope)) {
707
+ process.stderr.write(
708
+ `[auto-wake] IGNORING corrupted session_scope for ${t.agentId} task ${t.taskId}: "${rawScope.slice(0, 60)}"
709
+ `
710
+ );
711
+ t.sessionScope = null;
712
+ }
713
+ const scope = t.sessionScope || coordinatorSessions[coordinatorSessions.length - 1] || null;
714
+ if (!scope) continue;
715
+ if (t.sessionScope && !coordinatorSet.has(t.sessionScope)) {
716
+ process.stderr.write(
717
+ `[auto-wake] Skipping ${t.agentId} task ${t.taskId} \u2014 coordinator session "${t.sessionScope}" is dead
718
+ `
719
+ );
720
+ continue;
721
+ }
722
+ const key = `${t.agentId}::${scope}`;
723
+ const existing = agentTasks.get(key) ?? [];
724
+ existing.push({ taskId: t.taskId, priority: t.priority, sessionScope: scope });
725
+ agentTasks.set(key, existing);
726
+ }
727
+ const woken = [];
728
+ for (const [key, tasks] of agentTasks) {
729
+ const agentId = key.split("::")[0];
730
+ const sessionScope = tasks[0].sessionScope;
731
+ if (!shouldAutoWake({
732
+ agentId,
733
+ hasRunningSession: liveAgentScopedCounts.has(key),
734
+ lastSpawnMs: _autoWakeLastSpawn.get(key) ?? 0,
735
+ nowMs,
736
+ cooldownMs: AUTO_WAKE_COOLDOWN_MS,
737
+ runningInstances: liveAgentScopedCounts.get(key) ?? 0,
738
+ pendingTaskGroups: tasks.length
739
+ })) {
740
+ continue;
741
+ }
742
+ const recentSpawns = _autoWakeRecentSpawns.get(key) ?? [];
743
+ const recentCount = recentSpawns.filter((ts) => nowMs - ts < CRASH_LOOP_WINDOW_MS).length;
744
+ if (recentCount >= CRASH_LOOP_THRESHOLD) {
745
+ const loopCount = (_autoWakeState.crashLoopCount[key] ?? 0) + 1;
746
+ _autoWakeState.crashLoopCount[key] = loopCount;
747
+ const backoffMs = Math.min(BACKOFF_BASE_MS * Math.pow(2, loopCount - 1), BACKOFF_CAP_MS);
748
+ process.stderr.write(
749
+ `[auto-wake] CRASH LOOP #${loopCount}: ${agentId} spawned ${recentCount}x in ${CRASH_LOOP_WINDOW_MS / 6e4}min \u2014 backoff ${Math.round(backoffMs / 6e4)}min
750
+ `
751
+ );
752
+ saveAutoWakeState(_autoWakeState);
753
+ try {
754
+ await writeNotification({
755
+ agentId,
756
+ agentRole: "employee",
757
+ event: "error_spike",
758
+ project: String(tasks[0]?.sessionScope ?? ""),
759
+ summary: `Crash loop #${loopCount}: ${agentId} died ${recentCount}x. Backoff ${Math.round(backoffMs / 6e4)}min. Check session-deaths.jsonl.`,
760
+ sessionScope
761
+ });
762
+ } catch {
763
+ }
764
+ continue;
765
+ }
766
+ const agentGate = checkGate(key, nowMs);
767
+ if (agentGate) {
768
+ process.stderr.write(`[auto-wake] AGENT GATE BLOCKED: ${agentId} \u2014 ${agentGate}
769
+ `);
770
+ continue;
771
+ }
772
+ const topTask = tasks[0];
773
+ const retries = _autoWakeTaskRetries.get(topTask.taskId) ?? 0;
774
+ if (retries >= AUTO_WAKE_MAX_RETRIES) {
775
+ try {
776
+ await deps.markTaskNeedsReview(
777
+ topTask.taskId,
778
+ `Auto-wake failed ${AUTO_WAKE_MAX_RETRIES} times \u2014 auto-closed: session ended without explicit completion. Needs manual review.`
779
+ );
780
+ process.stderr.write(
781
+ `[auto-wake] ${agentId} task ${topTask.taskId} exceeded ${AUTO_WAKE_MAX_RETRIES} retries \u2014 marked needs_review
782
+ `
783
+ );
784
+ } catch (e) {
785
+ process.stderr.write("[daemon-orch] mark task needs_review after retries: " + (e instanceof Error ? e.message : String(e)) + "\n");
786
+ }
787
+ try {
788
+ await writeNotification({
789
+ agentId,
790
+ agentRole: "employee",
791
+ event: "orphan_task",
792
+ project: String(topTask.sessionScope ?? ""),
793
+ summary: `\u26A0\uFE0F Task ${topTask.taskId} for ${agentId} needs review \u2014 auto-wake failed ${AUTO_WAKE_MAX_RETRIES} times. Session could not be recovered.`,
794
+ sessionScope
795
+ });
796
+ } catch {
797
+ }
798
+ continue;
799
+ }
800
+ process.stderr.write(
801
+ `[auto-wake] ${agentId} has ${tasks.length} pending task(s) but no session \u2014 spawning in ${sessionScope} (attempt ${retries + 1} for task ${topTask.taskId})
802
+ `
803
+ );
804
+ try {
805
+ const result = deps.ensureEmployee(agentId, sessionScope);
806
+ if (result.status === "failed") {
807
+ process.stderr.write(
808
+ `[auto-wake] Failed to spawn ${agentId}: ${result.error ?? "unknown"}
809
+ `
810
+ );
811
+ continue;
812
+ }
813
+ _autoWakeLastSpawn.set(key, nowMs);
814
+ _autoWakeTaskRetries.set(topTask.taskId, retries + 1);
815
+ const spawns = _autoWakeRecentSpawns.get(key) ?? [];
816
+ spawns.push(nowMs);
817
+ while (spawns.length > 0 && nowMs - spawns[0] > CRASH_LOOP_WINDOW_MS) spawns.shift();
818
+ _autoWakeRecentSpawns.set(key, spawns);
819
+ saveAutoWakeState(_autoWakeState);
820
+ woken.push(agentId);
821
+ } catch (err) {
822
+ process.stderr.write(
823
+ `[auto-wake] Error spawning ${agentId}: ${err instanceof Error ? err.message : String(err)}
824
+ `
825
+ );
826
+ }
827
+ }
828
+ return woken;
829
+ }
830
+ var STUCK_TASK_GRACE_MS = 5 * 60 * 1e3;
831
+ async function releaseStuckTasks(deps, nowMs = Date.now()) {
832
+ let liveSessions;
833
+ try {
834
+ liveSessions = deps.listTmuxSessions();
835
+ } catch (e) {
836
+ process.stderr.write("[daemon-orch] list tmux sessions for stuck tasks: " + (e instanceof Error ? e.message : String(e)) + "\n");
837
+ return [];
838
+ }
839
+ const liveAgents = /* @__PURE__ */ new Set();
840
+ for (const session of liveSessions) {
841
+ const agent = deps.parseAgentFromSession(session);
842
+ if (agent) liveAgents.add(agent);
843
+ }
844
+ for (const session of liveSessions) {
845
+ if (isExeSession(session)) liveAgents.add(session);
846
+ }
847
+ let tasks;
848
+ try {
849
+ tasks = await deps.queryInProgressTasks();
850
+ } catch (e) {
851
+ process.stderr.write("[daemon-orch] query in-progress tasks: " + (e instanceof Error ? e.message : String(e)) + "\n");
852
+ return [];
853
+ }
854
+ const released = [];
855
+ const STALE_TASK_ESCALATION_MS = 60 * 60 * 1e3;
856
+ for (const t of tasks) {
857
+ const updatedMs = new Date(t.updatedAt).getTime();
858
+ if (isNaN(updatedMs)) continue;
859
+ const ageMinutes = Math.round((nowMs - updatedMs) / 6e4);
860
+ if (!liveAgents.has(t.agentId)) {
861
+ const leaseMs = t.leaseExpiresAt ? new Date(t.leaseExpiresAt).getTime() : NaN;
862
+ const leaseExpired = !isNaN(leaseMs) && leaseMs < nowMs;
863
+ if (!leaseExpired && nowMs - updatedMs < STUCK_TASK_GRACE_MS) continue;
864
+ const reason = leaseExpired ? `Auto-closed: agent "${t.agentId}" task lease expired (${t.leaseExpiresAt}) and no live local session exists. Needs manual review.` : `Auto-closed: agent "${t.agentId}" session ended without explicit completion (in_progress for ${ageMinutes}m, no live session). Needs manual review.`;
865
+ try {
866
+ await deps.markTaskNeedsReview(t.taskId, reason);
867
+ released.push(t.taskId);
868
+ process.stderr.write(
869
+ `[stuck-release] Task ${t.taskId} (${t.agentId}) \u2014 in_progress for ${ageMinutes}m, agent dead \u2192 needs_review
870
+ `
871
+ );
872
+ } catch (err) {
873
+ process.stderr.write(`[stuck-release] Failed to mark task ${t.taskId} needs_review: ${err instanceof Error ? err.message : String(err)}
874
+ `);
875
+ }
876
+ if (deps.notifyOrphan) {
877
+ try {
878
+ await deps.notifyOrphan(t.taskId, t.agentId, t.sessionScope);
879
+ } catch {
880
+ }
881
+ }
882
+ continue;
883
+ }
884
+ if (nowMs - updatedMs > STALE_TASK_ESCALATION_MS) {
885
+ process.stderr.write(
886
+ `[stuck-release] STALE WARNING: Task ${t.taskId} (${t.agentId}) \u2014 in_progress for ${ageMinutes}m, session alive but no update. Escalating to coordinator.
887
+ `
888
+ );
889
+ if (deps.notifyOrphan) {
890
+ try {
891
+ await deps.notifyOrphan(t.taskId, t.agentId, t.sessionScope);
892
+ } catch {
893
+ }
894
+ }
895
+ continue;
896
+ }
897
+ }
898
+ return released;
899
+ }
900
+ function createStuckTaskRealDeps(getClient) {
901
+ return {
902
+ listTmuxSessions: () => listTmuxSessions(),
903
+ queryInProgressTasks: async () => {
904
+ const { loadDeviceId } = await import("./lib/license.js");
905
+ const deviceId = loadDeviceId();
906
+ const client = getClient();
907
+ const nowIso = (/* @__PURE__ */ new Date()).toISOString();
908
+ const rows = await client.execute({
909
+ sql: `SELECT id, assigned_to, session_scope, updated_at, lease_expires_at
910
+ FROM tasks
911
+ WHERE status = 'in_progress'
912
+ AND (device_id = ? OR device_id IS NULL OR lease_expires_at < ?)
913
+ ORDER BY updated_at ASC
914
+ LIMIT 1000`,
915
+ args: [deviceId, nowIso]
916
+ });
917
+ return rows.rows.map((r) => ({
918
+ taskId: String(r.id),
919
+ agentId: String(r.assigned_to),
920
+ sessionScope: r.session_scope ? String(r.session_scope) : null,
921
+ updatedAt: String(r.updated_at),
922
+ leaseExpiresAt: r.lease_expires_at ? String(r.lease_expires_at) : null
923
+ }));
924
+ },
925
+ markTaskNeedsReview: async (taskId, reason) => {
926
+ try {
927
+ await updateTask({ taskId, status: "needs_review", result: reason });
928
+ } catch {
929
+ const client = getClient();
930
+ await client.execute({
931
+ sql: `UPDATE tasks SET status = 'needs_review', result = ?, updated_at = ? WHERE id = ?`,
932
+ args: [reason, (/* @__PURE__ */ new Date()).toISOString(), taskId]
933
+ });
934
+ }
935
+ },
936
+ parseAgentFromSession: (sessionName) => {
937
+ if (!sessionName.includes("-")) return null;
938
+ const agentPart = sessionName.split("-")[0];
939
+ return baseAgentName(agentPart);
940
+ },
941
+ notifyOrphan: async (taskId, agentId, sessionScope) => {
942
+ await writeNotification({
943
+ agentId,
944
+ agentRole: "employee",
945
+ event: "orphan_task",
946
+ project: "",
947
+ summary: `Agent "${agentId}" session died \u2014 task ${taskId.slice(0, 8)} needs review (auto-closed: session ended without completion)`,
948
+ sessionScope: sessionScope ?? void 0
949
+ });
950
+ }
951
+ };
952
+ }
953
+ var DEFAULT_STALE_REVIEW_DAYS = 7;
954
+ async function cleanupStaleReviews(deps) {
955
+ const thresholdDays = deps.getStaleReviewThreshold();
956
+ let stale;
957
+ try {
958
+ stale = await deps.queryStaleReviews(thresholdDays);
959
+ } catch (e) {
960
+ process.stderr.write(`[stale-review] query failed: ${e instanceof Error ? e.message : String(e)}
961
+ `);
962
+ return [];
963
+ }
964
+ const cleaned = [];
965
+ for (const t of stale) {
966
+ const reason = `auto-closed: stale review \u2014 no reviewer action for ${thresholdDays} days`;
967
+ try {
968
+ await deps.cancelTask(t.taskId, reason);
969
+ cleaned.push(t.taskId);
970
+ process.stderr.write(
971
+ `[stale-review] Cancelled task ${t.taskId.slice(0, 8)} "${t.title}" (${t.assignedTo}) \u2014 needs_review since ${t.updatedAt}
972
+ `
973
+ );
974
+ } catch (err) {
975
+ process.stderr.write(`[stale-review] Failed to cancel ${t.taskId}: ${err instanceof Error ? err.message : String(err)}
976
+ `);
977
+ }
978
+ }
979
+ return cleaned;
980
+ }
981
+ function createStaleReviewRealDeps(getClient) {
982
+ return {
983
+ queryStaleReviews: async (thresholdDays) => {
984
+ const client = getClient();
985
+ const scopeRows = await client.execute({
986
+ sql: `SELECT DISTINCT session_scope FROM tasks
987
+ WHERE session_scope IS NOT NULL AND status NOT IN ('closed', 'cancelled')`,
988
+ args: []
989
+ });
990
+ const scopes = scopeRows.rows.map((r) => r.session_scope ? String(r.session_scope) : null);
991
+ scopes.push(null);
992
+ const allRows = [];
993
+ for (const scope of scopes) {
994
+ const rows = await queryTaskRows({
995
+ status: "needs_review",
996
+ columns: "id, title, assigned_to, updated_at",
997
+ sessionScope: scope,
998
+ strictSession: scope !== null,
999
+ projectName: null,
1000
+ orderBy: "updated_at ASC",
1001
+ limit: 1e3,
1002
+ extraConditions: ["updated_at < datetime('now', '-' || ? || ' days')"],
1003
+ extraArgs: [String(thresholdDays)]
1004
+ });
1005
+ allRows.push(...rows);
1006
+ }
1007
+ const seen = /* @__PURE__ */ new Set();
1008
+ return allRows.filter((r) => {
1009
+ const id = String(r.id);
1010
+ if (seen.has(id)) return false;
1011
+ seen.add(id);
1012
+ return true;
1013
+ }).map((r) => ({
1014
+ taskId: String(r.id),
1015
+ title: String(r.title),
1016
+ assignedTo: String(r.assigned_to),
1017
+ updatedAt: String(r.updated_at)
1018
+ }));
1019
+ },
1020
+ cancelTask: async (taskId, reason) => {
1021
+ try {
1022
+ await updateTask({ taskId, status: "cancelled", result: reason });
1023
+ } catch {
1024
+ const client = getClient();
1025
+ await client.execute({
1026
+ sql: `UPDATE tasks SET status = 'cancelled', result = ?, updated_at = ? WHERE id = ?`,
1027
+ args: [reason, (/* @__PURE__ */ new Date()).toISOString(), taskId]
1028
+ });
1029
+ }
1030
+ },
1031
+ getStaleReviewThreshold: () => {
1032
+ try {
1033
+ const cfg = loadConfigSync();
1034
+ return cfg.staleReviewDays ?? DEFAULT_STALE_REVIEW_DAYS;
1035
+ } catch {
1036
+ return DEFAULT_STALE_REVIEW_DAYS;
1037
+ }
1038
+ }
1039
+ };
1040
+ }
1041
+ function createAutoWakeRealDeps(getClient, projectDir) {
1042
+ return {
1043
+ listTmuxSessions: () => listTmuxSessions(),
1044
+ queryTasksByAgent: async () => {
1045
+ const { loadDeviceId } = await import("./lib/license.js");
1046
+ const deviceId = loadDeviceId();
1047
+ const rows = await queryTaskRows({
1048
+ status: ["open", "in_progress"],
1049
+ columns: "assigned_to, id, priority, session_scope",
1050
+ sessionScope: null,
1051
+ // daemon-level: no session filter
1052
+ projectName: null,
1053
+ deviceId,
1054
+ orderBy: "CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END, created_at ASC",
1055
+ limit: 1e3
1056
+ });
1057
+ return rows.map((r) => ({
1058
+ agentId: String(r.assigned_to),
1059
+ taskId: String(r.id),
1060
+ priority: String(r.priority),
1061
+ sessionScope: r.session_scope ? String(r.session_scope) : null
1062
+ }));
1063
+ },
1064
+ ensureEmployee: (agentName, scope) => {
1065
+ return ensureEmployee(agentName, scope, projectDir, shouldAutoInstance(agentName));
1066
+ },
1067
+ markTaskNeedsReview: async (taskId, reason) => {
1068
+ try {
1069
+ await updateTask({ taskId, status: "needs_review", result: reason });
1070
+ } catch {
1071
+ const client = getClient();
1072
+ await client.execute({
1073
+ sql: `UPDATE tasks SET status = 'needs_review', result = ?, updated_at = ? WHERE id = ?`,
1074
+ args: [reason, (/* @__PURE__ */ new Date()).toISOString(), taskId]
1075
+ });
1076
+ }
1077
+ },
1078
+ parseAgentFromSession: (sessionName) => {
1079
+ if (!sessionName.includes("-")) return null;
1080
+ const agentPart = sessionName.split("-")[0];
1081
+ return baseAgentName(agentPart);
1082
+ }
1083
+ };
1084
+ }
1085
+ var COO_CONTEXT_THRESHOLD_PCT = Number(process.env.EXE_COO_CONTEXT_THRESHOLD_PCT) || 85;
1086
+ var COO_RESTART_COOLDOWN_MS = Number(process.env.EXE_COO_RESTART_COOLDOWN_MS) || 5 * 60 * 1e3;
1087
+ var _cooLastRestart = /* @__PURE__ */ new Map();
1088
+ function _resetCooRestartState() {
1089
+ _cooLastRestart.clear();
1090
+ }
1091
+ async function pollCoordinatorContextRestart(deps, projectDir) {
1092
+ let liveSessions;
1093
+ try {
1094
+ liveSessions = deps.listTmuxSessions();
1095
+ } catch (e) {
1096
+ process.stderr.write(`[daemon-orch] list tmux sessions for coordinator context: ${e instanceof Error ? e.message : String(e)}
1097
+ `);
1098
+ return [];
1099
+ }
1100
+ const coordinatorSessions = liveSessions.filter((s) => isExeSession(s));
1101
+ if (coordinatorSessions.length === 0) return [];
1102
+ const restarted = [];
1103
+ const now = Date.now();
1104
+ for (const sessionName of coordinatorSessions) {
1105
+ const lastRestart = _cooLastRestart.get(sessionName) ?? 0;
1106
+ if (now - lastRestart < COO_RESTART_COOLDOWN_MS) continue;
1107
+ const contextPct = deps.parseContextPercentage(sessionName);
1108
+ if (contextPct === null || contextPct < COO_CONTEXT_THRESHOLD_PCT) continue;
1109
+ try {
1110
+ if (await deps.hasUnsavedWork(sessionName)) {
1111
+ process.stderr.write(
1112
+ `[exed] Coordinator context restart: SKIPPING ${sessionName} (context=${contextPct}%) \u2014 has unsaved work
1113
+ `
1114
+ );
1115
+ continue;
1116
+ }
1117
+ } catch {
1118
+ process.stderr.write(
1119
+ `[exed] Coordinator context restart: SKIPPING ${sessionName} \u2014 unsaved work check failed (conservative)
1120
+ `
1121
+ );
1122
+ continue;
1123
+ }
1124
+ process.stderr.write(
1125
+ `[exed] Coordinator context restart: ${sessionName} at ${contextPct}% (threshold: ${COO_CONTEXT_THRESHOLD_PCT}%). Checkpointing and restarting.
1126
+ `
1127
+ );
1128
+ try {
1129
+ await deps.storeCheckpoint(sessionName, contextPct);
1130
+ deps.recordKill?.({
1131
+ sessionName,
1132
+ agentId: sessionName,
1133
+ reason: "ttl_context"
1134
+ });
1135
+ deps.killSession(sessionName);
1136
+ const result = deps.respawnCoordinator(sessionName, projectDir);
1137
+ if (result.status === "failed") {
1138
+ process.stderr.write(
1139
+ `[exed] Coordinator context restart: respawn FAILED for ${sessionName}: ${result.error ?? "unknown"}
1140
+ `
1141
+ );
1142
+ } else {
1143
+ _cooLastRestart.set(sessionName, now);
1144
+ restarted.push(sessionName);
1145
+ process.stderr.write(
1146
+ `[exed] Coordinator context restart: ${sessionName} respawned successfully.
1147
+ `
1148
+ );
1149
+ }
1150
+ } catch (err) {
1151
+ process.stderr.write(
1152
+ `[exed] Coordinator context restart: error for ${sessionName}: ${err instanceof Error ? err.message : String(err)}
1153
+ `
1154
+ );
1155
+ }
1156
+ }
1157
+ return restarted;
1158
+ }
1159
+ function createCoordinatorRestartRealDeps(getClient) {
1160
+ return {
1161
+ listTmuxSessions: () => {
1162
+ return listTmuxSessions();
1163
+ },
1164
+ parseContextPercentage: (sessionName) => {
1165
+ return parseContextPercentage(sessionName);
1166
+ },
1167
+ killSession: (sessionName) => {
1168
+ getTransport().kill(sessionName);
1169
+ },
1170
+ respawnCoordinator: (sessionName, projectDir) => {
1171
+ try {
1172
+ execSync(
1173
+ `tmux new-session -d -s ${JSON.stringify(sessionName)} -c ${JSON.stringify(projectDir)} 'claude --resume'`,
1174
+ { encoding: "utf8", timeout: 1e4 }
1175
+ );
1176
+ return { status: "ok" };
1177
+ } catch (err) {
1178
+ return { status: "failed", error: err instanceof Error ? err.message : String(err) };
1179
+ }
1180
+ },
1181
+ storeCheckpoint: async (sessionName, contextPct) => {
1182
+ let paneContent = "";
1183
+ try {
1184
+ const { execFile } = await import("child_process");
1185
+ const { promisify } = await import("util");
1186
+ const execFileAsync = promisify(execFile);
1187
+ const { stdout } = await execFileAsync("tmux", [
1188
+ "capture-pane",
1189
+ "-t",
1190
+ sessionName,
1191
+ "-p"
1192
+ ], { encoding: "utf8", timeout: 3e3 });
1193
+ paneContent = stdout.split("\n").slice(-30).join("\n").trim();
1194
+ } catch {
1195
+ }
1196
+ const client = getClient();
1197
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1198
+ const summary = [
1199
+ `CONTEXT CHECKPOINT [auto-restart]: Coordinator ${sessionName} at ${contextPct}% context capacity.`,
1200
+ `Auto-restarted by daemon at ${now}.`,
1201
+ paneContent ? `Last activity:
1202
+ ${paneContent.slice(0, 500)}` : ""
1203
+ ].filter(Boolean).join("\n");
1204
+ await client.execute({
1205
+ sql: `INSERT INTO memories (id, agent_id, agent_role, text, importance, memory_type, project_name, tool_name, created_at, updated_at, session_scope)
1206
+ VALUES (?, ?, 'coordinator', ?, 9, 'observation', ?, 'daemon', ?, ?, NULL)`,
1207
+ args: [
1208
+ randomUUID(),
1209
+ sessionName,
1210
+ summary,
1211
+ "exe-os",
1212
+ now,
1213
+ now
1214
+ ]
1215
+ });
1216
+ },
1217
+ hasUnsavedWork: async (sessionName) => {
1218
+ try {
1219
+ const { execFile } = await import("child_process");
1220
+ const { promisify } = await import("util");
1221
+ const execFileAsync = promisify(execFile);
1222
+ const { stdout } = await execFileAsync("tmux", [
1223
+ "capture-pane",
1224
+ "-t",
1225
+ sessionName,
1226
+ "-p"
1227
+ ], { encoding: "utf8", timeout: 3e3 });
1228
+ const tail = stdout.split("\n").slice(-10).join("\n");
1229
+ if (/(?:git commit|committing|staging)/i.test(tail)) return true;
1230
+ } catch {
1231
+ }
1232
+ return false;
1233
+ },
1234
+ recordKill: (input) => {
1235
+ void recordSessionKill(input);
1236
+ }
1237
+ };
1238
+ }
1239
+ var ORPHAN_SIGKILL_DELAY_MS = 5e3;
1240
+ var ORPHAN_MAX_AGE_SECS = 120;
1241
+ var ORPHAN_PATTERNS = [
1242
+ "exe-os/dist/mcp/server.js",
1243
+ "exe-mem/dist/mcp/server.js",
1244
+ "exe-os/dist/hooks/ingest-worker.js",
1245
+ "exe-mem/dist/hooks/ingest-worker.js",
1246
+ // Hook processes that become orphaned and never get reaped.
1247
+ // These accumulate at ~70MB each and caused 1GB+ memory waste.
1248
+ "codex-stop-task-finalizer.js",
1249
+ "scan-tasks.js",
1250
+ "exe-pending-reviews.js",
1251
+ "exe-pending-messages.js",
1252
+ "exe-pending-notifications.js"
1253
+ ];
1254
+ var HOOK_PROCESS_PATTERNS = [
1255
+ "codex-stop-task-finalizer.js",
1256
+ "scan-tasks.js",
1257
+ "exe-pending-reviews.js",
1258
+ "exe-pending-messages.js",
1259
+ "exe-pending-notifications.js",
1260
+ // Hook scripts that can zombie (added 2026-06-01 — orphan bug a4eaf106)
1261
+ "prompt-submit.js",
1262
+ "stop.js",
1263
+ "subagent-stop.js",
1264
+ "pre-tool-use.js",
1265
+ "post-tool-combined.js",
1266
+ "ingest.js",
1267
+ "intercom-check.js",
1268
+ "exe-heartbeat-hook.js"
1269
+ ];
1270
+ var AGENT_CLI_EXECUTABLES = /* @__PURE__ */ new Set(["claude", "codex", "opencode"]);
1271
+ function getProcessExecutable(args) {
1272
+ const first = args.trim().split(/\s+/)[0] ?? "";
1273
+ return first.split("/").pop() ?? first;
1274
+ }
1275
+ function getProcessCommandPrefix(args) {
1276
+ return args.split(/\s+--system-prompt\b/)[0] ?? args;
1277
+ }
1278
+ var ZOMBIE_AGENT_MAX_AGE_SECS = 300;
1279
+ function reapZombieAgentProcesses(deps) {
1280
+ let liveSessions;
1281
+ try {
1282
+ liveSessions = new Set(deps.listTmuxSessions());
1283
+ } catch {
1284
+ return [];
1285
+ }
1286
+ const lines = deps.listProcesses();
1287
+ const reaped = [];
1288
+ const procMap = /* @__PURE__ */ new Map();
1289
+ for (const line of lines) {
1290
+ const match = line.trim().match(/^(\d+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(.+)$/);
1291
+ if (!match) continue;
1292
+ procMap.set(parseInt(match[1], 10), {
1293
+ ppid: parseInt(match[2], 10),
1294
+ etime: match[3],
1295
+ tty: match[4],
1296
+ args: match[5]
1297
+ });
1298
+ }
1299
+ const AGENT_PATTERNS = [
1300
+ /\bclaude\b/,
1301
+ // Claude Code CLI
1302
+ /\bcodex\b/,
1303
+ // OpenAI Codex CLI
1304
+ /\bopencode\b/
1305
+ // OpenCode CLI
1306
+ ];
1307
+ for (const [pid, info] of procMap) {
1308
+ if (pid === deps.selfPid) continue;
1309
+ const isAgent = AGENT_PATTERNS.some((pat) => pat.test(info.args));
1310
+ if (!isAgent) continue;
1311
+ const ageSecs = parseEtime(info.etime);
1312
+ if (ageSecs < ZOMBIE_AGENT_MAX_AGE_SECS) continue;
1313
+ if (info.tty !== "??" && info.tty !== "-") continue;
1314
+ const wrapperInfo = procMap.get(info.ppid);
1315
+ if (wrapperInfo && wrapperInfo.tty !== "??" && wrapperInfo.tty !== "-") continue;
1316
+ let hasLiveSession = false;
1317
+ let cur = info.ppid;
1318
+ for (let depth = 0; depth < 5; depth++) {
1319
+ const parent = procMap.get(cur);
1320
+ if (!parent) break;
1321
+ cur = parent.ppid;
1322
+ }
1323
+ const wrapperPid = info.ppid;
1324
+ for (const session of liveSessions) {
1325
+ try {
1326
+ const panePids = deps.getPanePids ? deps.getPanePids(session) : execSync(
1327
+ `tmux list-panes -t ${JSON.stringify(session)} -F '#{pane_pid}' 2>/dev/null`,
1328
+ { encoding: "utf8", timeout: 3e3 }
1329
+ ).trim().split("\n").map(Number);
1330
+ if (panePids.includes(wrapperPid)) {
1331
+ hasLiveSession = true;
1332
+ break;
1333
+ }
1334
+ let ancestor = wrapperPid;
1335
+ for (let i = 0; i < 3; i++) {
1336
+ const p = procMap.get(ancestor);
1337
+ if (!p) break;
1338
+ if (panePids.includes(p.ppid)) {
1339
+ hasLiveSession = true;
1340
+ break;
1341
+ }
1342
+ ancestor = p.ppid;
1343
+ }
1344
+ if (hasLiveSession) break;
1345
+ } catch {
1346
+ }
1347
+ }
1348
+ if (hasLiveSession) continue;
1349
+ const coordinatorSessions = [...liveSessions].filter(
1350
+ (s) => isCoordinatorName(s.replace(/^\w+-/, "")) || isExeSession(s)
1351
+ );
1352
+ if (coordinatorSessions.length > 0) {
1353
+ let belongsToCoordinator = false;
1354
+ for (const cs of coordinatorSessions) {
1355
+ try {
1356
+ const panePids = (deps.getPanePids ? deps.getPanePids(cs) : execSync(
1357
+ `tmux list-panes -t ${JSON.stringify(cs)} -F '#{pane_pid}' 2>/dev/null`,
1358
+ { encoding: "utf8", timeout: 3e3 }
1359
+ ).trim().split("\n").map(Number)).filter(Boolean);
1360
+ for (const panePid of panePids) {
1361
+ let walk = pid;
1362
+ for (let d = 0; d < 10; d++) {
1363
+ if (walk === panePid) {
1364
+ belongsToCoordinator = true;
1365
+ break;
1366
+ }
1367
+ const p = procMap.get(walk);
1368
+ if (!p || p.ppid <= 1) break;
1369
+ walk = p.ppid;
1370
+ }
1371
+ if (belongsToCoordinator) break;
1372
+ }
1373
+ } catch {
1374
+ }
1375
+ if (belongsToCoordinator) break;
1376
+ }
1377
+ if (belongsToCoordinator) {
1378
+ process.stderr.write(
1379
+ `[zombie-agent-reaper] SKIPPING PID ${pid} \u2014 belongs to coordinator session (${info.args.slice(0, 60)})
1380
+ `
1381
+ );
1382
+ continue;
1383
+ }
1384
+ }
1385
+ const rssKb = getRssKb(pid);
1386
+ const rssMb = rssKb ? Math.round(rssKb / 1024) : "?";
1387
+ const desc = `PID ${pid} (age=${ageSecs}s, RSS=${rssMb}MB, ${info.args.slice(0, 80)})`;
1388
+ try {
1389
+ deps.killProcess(pid, "SIGTERM");
1390
+ } catch {
1391
+ continue;
1392
+ }
1393
+ reaped.push(desc);
1394
+ process.stderr.write(`[zombie-agent-reaper] Killed ${desc}
1395
+ `);
1396
+ if (wrapperPid > 1 && procMap.has(wrapperPid)) {
1397
+ try {
1398
+ deps.killProcess(wrapperPid, "SIGTERM");
1399
+ process.stderr.write(`[zombie-agent-reaper] Killed wrapper PID ${wrapperPid}
1400
+ `);
1401
+ } catch {
1402
+ }
1403
+ }
1404
+ deps.scheduleKill(pid, ORPHAN_SIGKILL_DELAY_MS, () => {
1405
+ try {
1406
+ deps.killProcess(pid, "SIGKILL");
1407
+ } catch {
1408
+ }
1409
+ });
1410
+ if (wrapperPid > 1) {
1411
+ deps.scheduleKill(wrapperPid, ORPHAN_SIGKILL_DELAY_MS, () => {
1412
+ try {
1413
+ deps.killProcess(wrapperPid, "SIGKILL");
1414
+ } catch {
1415
+ }
1416
+ });
1417
+ }
1418
+ }
1419
+ return reaped;
1420
+ }
1421
+ function getRssKb(pid) {
1422
+ try {
1423
+ const out = execSync(`ps -o rss= -p ${pid} 2>/dev/null`, { encoding: "utf8", timeout: 2e3 });
1424
+ const val = parseInt(out.trim(), 10);
1425
+ return isNaN(val) ? null : val;
1426
+ } catch {
1427
+ return null;
1428
+ }
1429
+ }
1430
+ function createZombieAgentReaperRealDeps() {
1431
+ const panePidMap = /* @__PURE__ */ new Map();
1432
+ let panePidCacheReady = false;
1433
+ try {
1434
+ const output = execSync("tmux list-panes -a -F '#{session_name} #{pane_pid}' 2>/dev/null", {
1435
+ encoding: "utf8",
1436
+ timeout: 5e3
1437
+ });
1438
+ for (const line of output.split("\n")) {
1439
+ const [sessionName, pidText] = line.trim().split(/\s+/);
1440
+ const pid = Number(pidText);
1441
+ if (!sessionName || !Number.isFinite(pid)) continue;
1442
+ const arr = panePidMap.get(sessionName) ?? [];
1443
+ arr.push(pid);
1444
+ panePidMap.set(sessionName, arr);
1445
+ }
1446
+ panePidCacheReady = true;
1447
+ } catch {
1448
+ }
1449
+ return {
1450
+ listProcesses: () => {
1451
+ const output = execSync("ps -eo pid,ppid,etime,tty,args", {
1452
+ encoding: "utf8",
1453
+ timeout: 5e3
1454
+ });
1455
+ return output.split("\n");
1456
+ },
1457
+ listTmuxSessions: () => {
1458
+ const output = execSync("tmux list-sessions -F '#{session_name}' 2>/dev/null", {
1459
+ encoding: "utf8",
1460
+ timeout: 3e3
1461
+ });
1462
+ return output.trim().split("\n").filter(Boolean);
1463
+ },
1464
+ killProcess: (pid, signal) => {
1465
+ process.kill(pid, signal);
1466
+ },
1467
+ ...panePidCacheReady ? { getPanePids: (sessionName) => panePidMap.get(sessionName) ?? [] } : {},
1468
+ scheduleKill: (_pid, delayMs, cb) => {
1469
+ setTimeout(() => {
1470
+ try {
1471
+ cb();
1472
+ } catch {
1473
+ }
1474
+ }, delayMs).unref();
1475
+ },
1476
+ selfPid: process.pid
1477
+ };
1478
+ }
1479
+ function parseEtime(etime) {
1480
+ const trimmed = etime.trim();
1481
+ const dayMatch = trimmed.match(/^(\d+)-(\d+):(\d+):(\d+)$/);
1482
+ if (dayMatch) {
1483
+ return parseInt(dayMatch[1], 10) * 86400 + parseInt(dayMatch[2], 10) * 3600 + parseInt(dayMatch[3], 10) * 60 + parseInt(dayMatch[4], 10);
1484
+ }
1485
+ const parts = trimmed.split(":").map((p) => parseInt(p, 10));
1486
+ if (parts.length === 3) return parts[0] * 3600 + parts[1] * 60 + parts[2];
1487
+ if (parts.length === 2) return parts[0] * 60 + parts[1];
1488
+ if (parts.length === 1) return parts[0];
1489
+ return 0;
1490
+ }
1491
+ function reapOrphanedMcpProcesses(deps) {
1492
+ const lines = deps.listProcesses();
1493
+ const reaped = [];
1494
+ for (const line of lines) {
1495
+ const trimmed = line.trim();
1496
+ const match = trimmed.match(/^(\d+)\s+(\d+)\s+(\S+)\s+(.+)$/);
1497
+ if (!match) continue;
1498
+ const pid = parseInt(match[1], 10);
1499
+ const ppid = parseInt(match[2], 10);
1500
+ const etime = match[3];
1501
+ const args = match[4];
1502
+ if (pid === deps.selfPid) continue;
1503
+ if (AGENT_CLI_EXECUTABLES.has(getProcessExecutable(args))) continue;
1504
+ const matchScope = getProcessCommandPrefix(args);
1505
+ const isPpid1Orphan = ppid === 1 && ORPHAN_PATTERNS.some((pat) => matchScope.includes(pat));
1506
+ const ageSecs = parseEtime(etime);
1507
+ const isAgedHookOrphan = ageSecs > ORPHAN_MAX_AGE_SECS && HOOK_PROCESS_PATTERNS.some((pat) => matchScope.includes(pat));
1508
+ if (!isPpid1Orphan && !isAgedHookOrphan) continue;
1509
+ try {
1510
+ deps.killProcess(pid, "SIGTERM");
1511
+ } catch {
1512
+ continue;
1513
+ }
1514
+ const reason = isPpid1Orphan ? "ppid=1" : `age=${ageSecs}s`;
1515
+ const desc = `PID ${pid} (${reason}, ${args.slice(0, 100)})`;
1516
+ reaped.push(desc);
1517
+ process.stderr.write(`[orphan-reaper] Killed ${desc}
1518
+ `);
1519
+ deps.scheduleKill(pid, ORPHAN_SIGKILL_DELAY_MS, () => {
1520
+ try {
1521
+ deps.killProcess(pid, "SIGKILL");
1522
+ } catch {
1523
+ }
1524
+ });
1525
+ }
1526
+ return reaped;
1527
+ }
1528
+ function createOrphanReaperRealDeps() {
1529
+ return {
1530
+ listProcesses: () => {
1531
+ const output = execSync("ps -eo pid,ppid,etime,args", {
1532
+ encoding: "utf8",
1533
+ timeout: 5e3
1534
+ });
1535
+ return output.split("\n");
1536
+ },
1537
+ killProcess: (pid, signal) => {
1538
+ process.kill(pid, signal);
1539
+ },
1540
+ scheduleKill: (_pid, delayMs, cb) => {
1541
+ setTimeout(() => {
1542
+ try {
1543
+ cb();
1544
+ } catch {
1545
+ }
1546
+ }, delayMs).unref();
1547
+ },
1548
+ selfPid: process.pid
1549
+ };
1550
+ }
1551
+ var WORKTREE_REAPER_INTERVAL_MS = 30 * 60 * 1e3;
1552
+ var WORKTREE_MIN_AGE_MS = 60 * 60 * 1e3;
1553
+ var WORKTREE_MAX_PRUNES_PER_TICK = 5;
1554
+ async function reapOrphanedWorktrees(deps, nowMs = Date.now()) {
1555
+ const result = { pruned: [], skipped: [] };
1556
+ let sessions;
1557
+ try {
1558
+ sessions = deps.listTmuxSessions();
1559
+ } catch {
1560
+ return result;
1561
+ }
1562
+ const liveAgents = /* @__PURE__ */ new Set();
1563
+ for (const s of sessions) {
1564
+ if (!s.includes("-")) continue;
1565
+ const agentPart = s.split("-")[0];
1566
+ liveAgents.add(baseAgentName(agentPart));
1567
+ }
1568
+ const repoRoots = /* @__PURE__ */ new Set();
1569
+ for (const s of sessions) {
1570
+ const cwd = deps.getPaneCwd(s);
1571
+ if (!cwd) continue;
1572
+ const root = deps.getGitRoot(cwd);
1573
+ if (root) repoRoots.add(root);
1574
+ }
1575
+ for (const repoRoot of repoRoots) {
1576
+ if (result.pruned.length >= WORKTREE_MAX_PRUNES_PER_TICK) break;
1577
+ let worktrees;
1578
+ try {
1579
+ worktrees = deps.listWorktrees(repoRoot);
1580
+ } catch {
1581
+ continue;
1582
+ }
1583
+ for (const wt of worktrees) {
1584
+ if (result.pruned.length >= WORKTREE_MAX_PRUNES_PER_TICK) break;
1585
+ if (!wt.path.includes("/.worktrees/")) continue;
1586
+ const agentName = deps.parseAgentFromWorktreePath(wt.path);
1587
+ if (!agentName) {
1588
+ result.skipped.push({ path: wt.path, reason: "could not parse agent name" });
1589
+ recordOrchestrationEventBestEffort({
1590
+ eventType: "worktree.skipped",
1591
+ source: "daemon-orchestration.reapOrphanedWorktrees",
1592
+ severity: "warn",
1593
+ payload: { reason: "could not parse agent name" }
1594
+ });
1595
+ continue;
1596
+ }
1597
+ if (liveAgents.has(agentName)) continue;
1598
+ const mtimeMs = deps.getDirMtimeMs(wt.path);
1599
+ if (mtimeMs === null) continue;
1600
+ const ageMs = nowMs - mtimeMs;
1601
+ if (ageMs < WORKTREE_MIN_AGE_MS) {
1602
+ result.skipped.push({ path: wt.path, reason: `too young (${Math.round(ageMs / 6e4)}m < 60m)` });
1603
+ recordOrchestrationEventBestEffort({
1604
+ eventType: "worktree.skipped",
1605
+ source: "daemon-orchestration.reapOrphanedWorktrees",
1606
+ agentId: agentName,
1607
+ payload: { reason: "too young", ageMinutes: Math.round(ageMs / 6e4) }
1608
+ });
1609
+ continue;
1610
+ }
1611
+ if (deps.isWorktreeDirty(wt.path)) {
1612
+ const ageH2 = Math.round(ageMs / 36e5);
1613
+ process.stderr.write(
1614
+ `[worktree-reaper] WARNING: Orphan worktree has uncommitted changes, skipping: ${wt.path} (agent: ${agentName}, age: ${ageH2}h)
1615
+ `
1616
+ );
1617
+ result.skipped.push({ path: wt.path, reason: "uncommitted changes" });
1618
+ recordOrchestrationEventBestEffort({
1619
+ eventType: "worktree.skipped",
1620
+ source: "daemon-orchestration.reapOrphanedWorktrees",
1621
+ severity: "warn",
1622
+ agentId: agentName,
1623
+ payload: { reason: "uncommitted changes", ageHours: ageH2 }
1624
+ });
1625
+ continue;
1626
+ }
1627
+ const ageH = Math.round(ageMs / 36e5);
1628
+ const removed = deps.removeWorktree(repoRoot, wt.path);
1629
+ if (removed) {
1630
+ deps.deleteBranch(repoRoot, wt.branch);
1631
+ deps.deleteRemoteBranch(repoRoot, wt.branch);
1632
+ process.stderr.write(
1633
+ `[worktree-reaper] Pruned orphan worktree: ${wt.path} (agent: ${agentName}, age: ${ageH}h)
1634
+ `
1635
+ );
1636
+ recordOrchestrationEventBestEffort({
1637
+ eventType: "worktree.pruned",
1638
+ source: "daemon-orchestration.reapOrphanedWorktrees",
1639
+ agentId: agentName,
1640
+ payload: { branch: wt.branch, ageHours: ageH }
1641
+ });
1642
+ result.pruned.push(wt.path);
1643
+ } else {
1644
+ result.skipped.push({ path: wt.path, reason: "git worktree remove failed" });
1645
+ recordOrchestrationEventBestEffort({
1646
+ eventType: "worktree.failed",
1647
+ source: "daemon-orchestration.reapOrphanedWorktrees",
1648
+ severity: "warn",
1649
+ agentId: agentName,
1650
+ errorCode: "git_worktree_remove_failed",
1651
+ payload: { branch: wt.branch, ageHours: ageH }
1652
+ });
1653
+ }
1654
+ }
1655
+ }
1656
+ return result;
1657
+ }
1658
+ async function createWorktreeReaperRealDeps() {
1659
+ const { getPaneCwdAsync } = await import("./lib/tmux-status.js");
1660
+ const { getGitRoot, isWorktreeDirty: isDirty } = await import("./worktree-NLSKVRNC.js");
1661
+ const { statSync: statSync2 } = await import("fs");
1662
+ const { basename } = await import("path");
1663
+ const { promisify } = await import("util");
1664
+ const { execFile: execFileCb } = await import("child_process");
1665
+ const execFileAsync = promisify(execFileCb);
1666
+ const sessions = listTmuxSessions();
1667
+ const cwdMap = /* @__PURE__ */ new Map();
1668
+ const cwdPromises = sessions.map(async (s) => {
1669
+ try {
1670
+ const cwd = await getPaneCwdAsync(s);
1671
+ if (cwd) cwdMap.set(s, cwd);
1672
+ } catch {
1673
+ }
1674
+ });
1675
+ await Promise.all(cwdPromises);
1676
+ const repoRoots = /* @__PURE__ */ new Set();
1677
+ for (const cwd of cwdMap.values()) {
1678
+ const root = getGitRoot(cwd);
1679
+ if (root) repoRoots.add(root);
1680
+ }
1681
+ const worktreeCache = /* @__PURE__ */ new Map();
1682
+ const wtPromises = [...repoRoots].map(async (repoRoot) => {
1683
+ try {
1684
+ const { stdout } = await execFileAsync("git", ["worktree", "list", "--porcelain"], {
1685
+ cwd: repoRoot,
1686
+ timeout: 1e4
1687
+ });
1688
+ const worktrees = [];
1689
+ let currentPath = "";
1690
+ for (const line of stdout.split("\n")) {
1691
+ if (line.startsWith("worktree ")) {
1692
+ currentPath = line.slice("worktree ".length);
1693
+ } else if (line.startsWith("branch ") && currentPath) {
1694
+ const branch = line.slice("branch refs/heads/".length);
1695
+ worktrees.push({ path: currentPath, branch });
1696
+ currentPath = "";
1697
+ }
1698
+ }
1699
+ worktreeCache.set(repoRoot, worktrees);
1700
+ } catch {
1701
+ }
1702
+ });
1703
+ await Promise.all(wtPromises);
1704
+ return {
1705
+ listTmuxSessions: () => sessions,
1706
+ getPaneCwd: (s) => cwdMap.get(s),
1707
+ listWorktrees: (repoRoot) => worktreeCache.get(repoRoot) ?? [],
1708
+ isWorktreeDirty: (wtPath) => isDirty(wtPath),
1709
+ getDirMtimeMs: (dirPath) => {
1710
+ try {
1711
+ return statSync2(dirPath).mtimeMs;
1712
+ } catch {
1713
+ return null;
1714
+ }
1715
+ },
1716
+ removeWorktree: (repoRoot, wtPath) => {
1717
+ try {
1718
+ execSync(`git worktree remove ${JSON.stringify(wtPath)} --force`, {
1719
+ cwd: repoRoot,
1720
+ encoding: "utf-8",
1721
+ timeout: 1e4,
1722
+ stdio: ["pipe", "pipe", "pipe"]
1723
+ });
1724
+ return true;
1725
+ } catch {
1726
+ return false;
1727
+ }
1728
+ },
1729
+ deleteBranch: (repoRoot, branch) => {
1730
+ try {
1731
+ execSync(`git branch -D ${JSON.stringify(branch)}`, {
1732
+ cwd: repoRoot,
1733
+ encoding: "utf-8",
1734
+ timeout: 1e4,
1735
+ stdio: ["pipe", "pipe", "pipe"]
1736
+ });
1737
+ return true;
1738
+ } catch {
1739
+ return false;
1740
+ }
1741
+ },
1742
+ deleteRemoteBranch: (repoRoot, branch) => {
1743
+ try {
1744
+ execSync(`git push origin --delete ${JSON.stringify(branch)} 2>/dev/null`, {
1745
+ cwd: repoRoot,
1746
+ encoding: "utf-8",
1747
+ timeout: 15e3,
1748
+ stdio: ["pipe", "pipe", "pipe"]
1749
+ });
1750
+ } catch {
1751
+ }
1752
+ },
1753
+ parseAgentFromWorktreePath: (wtPath) => {
1754
+ const dirName = basename(wtPath);
1755
+ if (!dirName) return null;
1756
+ return baseAgentName(dirName);
1757
+ },
1758
+ getGitRoot: (dir) => getGitRoot(dir)
1759
+ };
1760
+ }
1761
+ var _lastStaleCheckpointGcMs = 0;
1762
+ var STALE_CHECKPOINT_GC_INTERVAL_MS = 6 * 60 * 60 * 1e3;
1763
+ async function gcStaleCheckpoints() {
1764
+ const now = Date.now();
1765
+ if (now - _lastStaleCheckpointGcMs < STALE_CHECKPOINT_GC_INTERVAL_MS) return 0;
1766
+ _lastStaleCheckpointGcMs = now;
1767
+ try {
1768
+ const { getClient } = await import("./lib/database.js");
1769
+ const client = getClient();
1770
+ const scope = sessionScopeFilter();
1771
+ const result = await client.execute({
1772
+ sql: `UPDATE tasks SET checkpoint = NULL, checkpoint_count = 0
1773
+ WHERE checkpoint IS NOT NULL
1774
+ AND status IN ('done', 'closed', 'cancelled', 'needs_review')
1775
+ ${scope.sql}`,
1776
+ args: scope.args
1777
+ });
1778
+ const cleared = Number(result.rowsAffected ?? 0);
1779
+ if (cleared > 0) {
1780
+ process.stderr.write(`[daemon-orchestration] GC'd ${cleared} stale checkpoint(s)
1781
+ `);
1782
+ recordOrchestrationEventBestEffort({
1783
+ eventType: "checkpoint.gc",
1784
+ source: "daemon-orchestration.gcStaleCheckpoints",
1785
+ payload: { cleared }
1786
+ });
1787
+ }
1788
+ return cleared;
1789
+ } catch (err) {
1790
+ if (process.env.EXE_DEBUG === "1") {
1791
+ process.stderr.write(
1792
+ `[daemon-orchestration] gcStaleCheckpoints error: ${err instanceof Error ? err.message : String(err)}
1793
+ `
1794
+ );
1795
+ }
1796
+ return 0;
1797
+ }
1798
+ }
1799
+ async function pollTaskGroupBarriers() {
1800
+ try {
1801
+ const { checkAndFireBarriers } = await import("./tasks-crud-EFYWPPEI.js");
1802
+ const firedGroupIds = await checkAndFireBarriers();
1803
+ if (firedGroupIds.length === 0) return 0;
1804
+ const { getClient } = await import("./lib/database.js");
1805
+ const client = getClient();
1806
+ for (const groupId of firedGroupIds) {
1807
+ try {
1808
+ const groupRow = await client.execute({
1809
+ sql: "SELECT title, coordinator, session_scope, aggregated_result FROM task_groups WHERE id = ?",
1810
+ args: [groupId]
1811
+ });
1812
+ if (groupRow.rows.length === 0) continue;
1813
+ const group = groupRow.rows[0];
1814
+ const coordinator = String(group.coordinator);
1815
+ const sessionScope = group.session_scope != null ? String(group.session_scope) : null;
1816
+ const title = String(group.title);
1817
+ let summary = `Task group "${title}" barrier fired.`;
1818
+ try {
1819
+ const agg = JSON.parse(String(group.aggregated_result ?? "{}"));
1820
+ summary = `Task group "${title}" complete: ${agg.succeeded ?? 0}/${agg.total ?? 0} succeeded`;
1821
+ if (agg.cancelled) summary += `, ${agg.cancelled} cancelled`;
1822
+ if (agg.timed_out) summary += " (timed out)";
1823
+ } catch {
1824
+ }
1825
+ if (sessionScope) {
1826
+ try {
1827
+ if (isCoordinatorName(coordinator) && isExeSession(sessionScope)) {
1828
+ sendIntercom(sessionScope, { force: true, reason: "completion" });
1829
+ } else {
1830
+ const { employeeSessionName } = await import("./lib/tmux-routing.js");
1831
+ sendIntercom(employeeSessionName(coordinator, sessionScope), { force: true, reason: "completion" });
1832
+ }
1833
+ } catch {
1834
+ }
1835
+ }
1836
+ try {
1837
+ await writeNotification({
1838
+ agentId: coordinator,
1839
+ agentRole: isCoordinatorName(coordinator) ? "COO" : "manager",
1840
+ event: "task_group_barrier",
1841
+ project: "",
1842
+ summary,
1843
+ sessionScope: sessionScope ?? void 0
1844
+ });
1845
+ } catch {
1846
+ }
1847
+ recordOrchestrationEventBestEffort({
1848
+ eventType: "task_group.barrier_notified",
1849
+ source: "daemon-orchestration.pollTaskGroupBarriers",
1850
+ agentId: coordinator,
1851
+ sessionScope,
1852
+ payload: { groupId, title, summary }
1853
+ });
1854
+ } catch {
1855
+ }
1856
+ }
1857
+ return firedGroupIds.length;
1858
+ } catch (err) {
1859
+ if (process.env.EXE_DEBUG === "1") {
1860
+ process.stderr.write(
1861
+ `[daemon-orchestration] pollTaskGroupBarriers error: ${err instanceof Error ? err.message : String(err)}
1862
+ `
1863
+ );
1864
+ }
1865
+ return 0;
1866
+ }
1867
+ }
1868
+
1869
+ export {
1870
+ IDLE_NUDGE_DEDUP_MS,
1871
+ SESSION_TTL_HOURS,
1872
+ SESSION_CONTEXT_THRESHOLD_PCT,
1873
+ IDLE_KILL_INTERCOM_ACK_WINDOW_MS,
1874
+ shouldNudgeEmployee,
1875
+ shouldKillSession,
1876
+ classifyTtlKillReason,
1877
+ shouldKillIdleSession,
1878
+ pollIdleEmployees,
1879
+ checkSessionTTL,
1880
+ pollIdleKill,
1881
+ REVIEW_NUDGE_COOLDOWN_MS,
1882
+ pollReviewNudge,
1883
+ loadNudgeState,
1884
+ saveNudgeState,
1885
+ createReviewNudgeRealDeps,
1886
+ createIdleNudgeRealDeps,
1887
+ createSessionTTLRealDeps,
1888
+ createIdleKillRealDeps,
1889
+ AUTO_WAKE_COOLDOWN_MS,
1890
+ AUTO_WAKE_MAX_RETRIES,
1891
+ CRASH_LOOP_WINDOW_MS,
1892
+ CRASH_LOOP_THRESHOLD,
1893
+ _resetAutoWakeState,
1894
+ checkAutoWakeGates,
1895
+ shouldAutoWake,
1896
+ pollOrphanedTasks,
1897
+ STUCK_TASK_GRACE_MS,
1898
+ releaseStuckTasks,
1899
+ createStuckTaskRealDeps,
1900
+ cleanupStaleReviews,
1901
+ createStaleReviewRealDeps,
1902
+ createAutoWakeRealDeps,
1903
+ COO_CONTEXT_THRESHOLD_PCT,
1904
+ COO_RESTART_COOLDOWN_MS,
1905
+ _resetCooRestartState,
1906
+ pollCoordinatorContextRestart,
1907
+ createCoordinatorRestartRealDeps,
1908
+ ORPHAN_SIGKILL_DELAY_MS,
1909
+ ORPHAN_MAX_AGE_SECS,
1910
+ ORPHAN_PATTERNS,
1911
+ HOOK_PROCESS_PATTERNS,
1912
+ reapZombieAgentProcesses,
1913
+ createZombieAgentReaperRealDeps,
1914
+ reapOrphanedMcpProcesses,
1915
+ createOrphanReaperRealDeps,
1916
+ WORKTREE_REAPER_INTERVAL_MS,
1917
+ reapOrphanedWorktrees,
1918
+ createWorktreeReaperRealDeps,
1919
+ gcStaleCheckpoints,
1920
+ pollTaskGroupBarriers
1921
+ };