agentix-cli 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/dist/{App-BPNHIGUX.js → App-U52IPUK2.js} +2 -2
  2. package/dist/{ChatApp-6XSSZB4U.js → ChatApp-XP4OMHPL.js} +2 -2
  3. package/dist/agent-V2CA4TXD.js +2 -0
  4. package/dist/article-fields-L4D2BOGY.js +2 -0
  5. package/dist/article-quality-44ONVGLN.js +3 -0
  6. package/dist/article-quality-44ONVGLN.js.map +1 -0
  7. package/dist/{audit-JU3HWISB.js → audit-4245CX6P.js} +2 -2
  8. package/dist/backfill-MTD2B62I.js +6 -0
  9. package/dist/backfill-MTD2B62I.js.map +1 -0
  10. package/dist/{board-dashboard-M3372FLO.js → board-dashboard-TTMAYRLG.js} +1407 -447
  11. package/dist/board-dashboard-TTMAYRLG.js.map +1 -0
  12. package/dist/{builtin-FRKAQMFS.js → builtin-B67DMJ3M.js} +2 -2
  13. package/dist/calibration-ILS5OI44.js +2 -0
  14. package/dist/chunk-2T5W3ETS.js +5 -0
  15. package/dist/{chunk-K2CAPYES.js.map → chunk-2T5W3ETS.js.map} +1 -1
  16. package/dist/chunk-3SCPRYJB.js +5 -0
  17. package/dist/chunk-3SCPRYJB.js.map +1 -0
  18. package/dist/chunk-4BR7D2OU.js +36 -0
  19. package/dist/chunk-4BR7D2OU.js.map +1 -0
  20. package/dist/{chunk-P4VWIVKN.js → chunk-52ZPELD2.js} +403 -29
  21. package/dist/chunk-52ZPELD2.js.map +1 -0
  22. package/dist/{chunk-5U35WAZF.js → chunk-77OV7346.js} +6 -6
  23. package/dist/chunk-77OV7346.js.map +1 -0
  24. package/dist/chunk-AJ5RMU7X.js +2 -0
  25. package/dist/chunk-AJ5RMU7X.js.map +1 -0
  26. package/dist/chunk-AJONZWLT.js +2 -0
  27. package/dist/chunk-AJONZWLT.js.map +1 -0
  28. package/dist/chunk-AX2PWSRT.js +114 -0
  29. package/dist/chunk-AX2PWSRT.js.map +1 -0
  30. package/dist/chunk-BH3GTCZ6.js +2 -0
  31. package/dist/chunk-BH3GTCZ6.js.map +1 -0
  32. package/dist/chunk-BPIRO4YD.js +126 -0
  33. package/dist/chunk-BPIRO4YD.js.map +1 -0
  34. package/dist/chunk-CJQQIRPD.js +13 -0
  35. package/dist/chunk-CJQQIRPD.js.map +1 -0
  36. package/dist/chunk-DCDQZR7I.js +24 -0
  37. package/dist/chunk-DCDQZR7I.js.map +1 -0
  38. package/dist/chunk-EVWEG3VD.js +3 -0
  39. package/dist/chunk-EVWEG3VD.js.map +1 -0
  40. package/dist/chunk-F4HZTZHW.js +2 -0
  41. package/dist/chunk-F4HZTZHW.js.map +1 -0
  42. package/dist/chunk-F6CNF7V3.js +25 -0
  43. package/dist/chunk-F6CNF7V3.js.map +1 -0
  44. package/dist/chunk-FIEJDZVC.js +121 -0
  45. package/dist/chunk-FIEJDZVC.js.map +1 -0
  46. package/dist/chunk-FIOB3MEX.js +2 -0
  47. package/dist/{chunk-H5D7ATXS.js → chunk-FKBGJYWQ.js} +5 -5
  48. package/dist/chunk-FKBGJYWQ.js.map +1 -0
  49. package/dist/chunk-GDWJUGB5.js +9 -0
  50. package/dist/chunk-GDWJUGB5.js.map +1 -0
  51. package/dist/{chunk-BVGJSPW4.js → chunk-IB3F7VBE.js} +7 -7
  52. package/dist/{chunk-BVGJSPW4.js.map → chunk-IB3F7VBE.js.map} +1 -1
  53. package/dist/chunk-IBUSGCLQ.js +109 -0
  54. package/dist/chunk-IBUSGCLQ.js.map +1 -0
  55. package/dist/{chunk-LDHPMJ7M.js → chunk-IWMW5432.js} +3 -3
  56. package/dist/chunk-IWMW5432.js.map +1 -0
  57. package/dist/chunk-K33GIBSE.js +2 -0
  58. package/dist/chunk-K33GIBSE.js.map +1 -0
  59. package/dist/chunk-L2IZPR6P.js +45 -0
  60. package/dist/chunk-L2IZPR6P.js.map +1 -0
  61. package/dist/chunk-L2WSPY6S.js +2 -0
  62. package/dist/chunk-L2WSPY6S.js.map +1 -0
  63. package/dist/chunk-LCIREYTD.js +54 -0
  64. package/dist/chunk-LCIREYTD.js.map +1 -0
  65. package/dist/chunk-LNRB5MCU.js +530 -0
  66. package/dist/chunk-LNRB5MCU.js.map +1 -0
  67. package/dist/chunk-OPHIXXGI.js +157 -0
  68. package/dist/chunk-OPHIXXGI.js.map +1 -0
  69. package/dist/{chunk-JTJIPNXY.js → chunk-PNWNVOEZ.js} +1 -1
  70. package/dist/chunk-PNWNVOEZ.js.map +1 -0
  71. package/dist/{chunk-V3U4OF7L.js → chunk-UFAGYX73.js} +45 -47
  72. package/dist/chunk-UFAGYX73.js.map +1 -0
  73. package/dist/{chunk-DJEZ2HVU.js → chunk-VDMYBPYB.js} +1 -1
  74. package/dist/chunk-VDMYBPYB.js.map +1 -0
  75. package/dist/chunk-VTE7L6LO.js +5 -0
  76. package/dist/{chunk-O6YPEOQF.js.map → chunk-VTE7L6LO.js.map} +1 -1
  77. package/dist/chunk-W5C2GDNC.js +32 -0
  78. package/dist/{chunk-JP5IEXR7.js.map → chunk-W5C2GDNC.js.map} +1 -1
  79. package/dist/chunk-WPMX3VGI.js +3 -0
  80. package/dist/chunk-WPMX3VGI.js.map +1 -0
  81. package/dist/chunk-WZ72HX5C.js +1182 -0
  82. package/dist/chunk-WZ72HX5C.js.map +1 -0
  83. package/dist/chunk-ZTEU5JVQ.js +2 -0
  84. package/dist/chunk-ZTEU5JVQ.js.map +1 -0
  85. package/dist/{chunk-2WVX3LEC.js → chunk-ZVP6GB4G.js} +1 -1
  86. package/dist/chunk-ZVP6GB4G.js.map +1 -0
  87. package/dist/cli.js +1 -1
  88. package/dist/cli.js.map +1 -1
  89. package/dist/client-PX25MD47.js +2 -0
  90. package/dist/{compaction-NWEMOSHA.js → compaction-FD4R23HZ.js} +4 -4
  91. package/dist/config-HL5F36V4.js +2 -0
  92. package/dist/config-mutator-HBX53OMJ.js +2 -0
  93. package/dist/{conflict-detector-D7VIYPDI.js → conflict-detector-ZPOT3M2C.js} +2 -2
  94. package/dist/conflict-detector-ZPOT3M2C.js.map +1 -0
  95. package/dist/context-planner-AQHJMVY2.js +17 -0
  96. package/dist/context-planner-AQHJMVY2.js.map +1 -0
  97. package/dist/debug-3MEUO2P2.js +2 -0
  98. package/dist/{discover-XJXQXXIO.js → discover-D4VVRNAI.js} +2 -2
  99. package/dist/discover-D4VVRNAI.js.map +1 -0
  100. package/dist/doctor-PBAH6XL3.js +2 -0
  101. package/dist/editor-chat-OB7IQOB5.js +137 -0
  102. package/dist/editor-chat-OB7IQOB5.js.map +1 -0
  103. package/dist/entry-triage-LK34TSQD.js +2 -0
  104. package/dist/entry-triage-LK34TSQD.js.map +1 -0
  105. package/dist/facts-YVEJOM7O.js +22 -0
  106. package/dist/facts-YVEJOM7O.js.map +1 -0
  107. package/dist/heal-FVLBHYP7.js +2 -0
  108. package/dist/index.d.ts +1016 -65
  109. package/dist/index.js +13 -13
  110. package/dist/index.js.map +1 -1
  111. package/dist/{ingest-whatsapp-LZE62INA.js → ingest-whatsapp-NS4OWDOU.js} +2 -2
  112. package/dist/loader-4ZGTK7LD.js +2 -0
  113. package/dist/loader-BOKE5M5S.js +2 -0
  114. package/dist/memory-extract-F5ZHWY5E.js +2 -0
  115. package/dist/{migrate-v2-QO7SLKIM.js → migrate-v2-V4WN2VVM.js} +2 -2
  116. package/dist/ntfy-X7EPB3TG.js +4 -0
  117. package/dist/ntfy-X7EPB3TG.js.map +1 -0
  118. package/dist/processes-WU23U2OJ.js +2 -0
  119. package/dist/program-YGXVCV2Y.js +2 -0
  120. package/dist/{projects-api-KRME2YKZ.js → projects-api-QE6L65SK.js} +2 -2
  121. package/dist/{projects-mutate-3NHVG437.js → projects-mutate-MY7JTLSZ.js} +2 -2
  122. package/dist/providers-KO2DODIO.js +2 -0
  123. package/dist/query-ZKTFJMNE.js +2 -0
  124. package/dist/questions-4B4KSPXG.js +3 -0
  125. package/dist/questions-4B4KSPXG.js.map +1 -0
  126. package/dist/recipes-7S4MC5OJ.js +2 -0
  127. package/dist/registry-PSZDPFZK.js +2 -0
  128. package/dist/review-candidates-AEUXRQQC.js +4 -0
  129. package/dist/review-candidates-AEUXRQQC.js.map +1 -0
  130. package/dist/review-store-B4YE3EFK.js +4 -0
  131. package/dist/review-store-B4YE3EFK.js.map +1 -0
  132. package/dist/{rotation-memo-B3EXXSEH.js → rotation-memo-7T75BLEX.js} +2 -2
  133. package/dist/routing-4PV66BK6.js +2 -0
  134. package/dist/routing-4PV66BK6.js.map +1 -0
  135. package/dist/runner-YRBVNYTY.js +2 -0
  136. package/dist/scheduler-GZZAN6ZG.js +2 -0
  137. package/dist/seat-VZ7AVO27.js +2 -0
  138. package/dist/select-F5I5PO2O.js +2 -0
  139. package/dist/select-F5I5PO2O.js.map +1 -0
  140. package/dist/store-MCOA6N4F.js +2 -0
  141. package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
  142. package/dist/{surface-usage-PRL6Z4U3.js → surface-usage-NS74AXSE.js} +2 -2
  143. package/dist/timers-PCB4ZIBD.js +2 -0
  144. package/dist/triage-backtest-JNO7RKX7.js +2 -0
  145. package/dist/triage-backtest-JNO7RKX7.js.map +1 -0
  146. package/dist/types-H5ENIUIT.js +2 -0
  147. package/dist/types-OIFU3KSQ.js +2 -0
  148. package/dist/web/workflow-editor.global.js +1542 -0
  149. package/dist/web/workflow-editor.global.js.map +1 -0
  150. package/dist/{whatsapp-state-XMTCSRXU.js → whatsapp-state-6D6FHHD2.js} +2 -2
  151. package/dist/wiki-N5SMP3DT.js +2 -0
  152. package/dist/wiki-N5SMP3DT.js.map +1 -0
  153. package/dist/workflows/templates/branching.yaml +1 -1
  154. package/dist/{workspace-setup-TBQAHYHJ.js → workspace-setup-B3F3TUDE.js} +2 -2
  155. package/dist/workspace-setup-B3F3TUDE.js.map +1 -0
  156. package/package.json +1 -1
  157. package/dist/agent-5QMITZT6.js +0 -2
  158. package/dist/architect-SEPWMWS4.js +0 -2
  159. package/dist/board-dashboard-M3372FLO.js.map +0 -1
  160. package/dist/chunk-2WVX3LEC.js.map +0 -1
  161. package/dist/chunk-3WQFCIB6.js +0 -24
  162. package/dist/chunk-3WQFCIB6.js.map +0 -1
  163. package/dist/chunk-4YCH6IZV.js +0 -2
  164. package/dist/chunk-53NRX2NA.js +0 -2
  165. package/dist/chunk-53NRX2NA.js.map +0 -1
  166. package/dist/chunk-5U35WAZF.js.map +0 -1
  167. package/dist/chunk-BFXONUUN.js +0 -108
  168. package/dist/chunk-BFXONUUN.js.map +0 -1
  169. package/dist/chunk-BFZUG66X.js +0 -45
  170. package/dist/chunk-BFZUG66X.js.map +0 -1
  171. package/dist/chunk-BTHHSY7D.js +0 -13
  172. package/dist/chunk-BTHHSY7D.js.map +0 -1
  173. package/dist/chunk-D46VZ7K7.js +0 -116
  174. package/dist/chunk-D46VZ7K7.js.map +0 -1
  175. package/dist/chunk-DJEZ2HVU.js.map +0 -1
  176. package/dist/chunk-E5CXCI33.js +0 -155
  177. package/dist/chunk-E5CXCI33.js.map +0 -1
  178. package/dist/chunk-FDWOECJY.js +0 -54
  179. package/dist/chunk-FDWOECJY.js.map +0 -1
  180. package/dist/chunk-H5D7ATXS.js.map +0 -1
  181. package/dist/chunk-HR4JSRZS.js +0 -3
  182. package/dist/chunk-HR4JSRZS.js.map +0 -1
  183. package/dist/chunk-ISEBLHWX.js +0 -118
  184. package/dist/chunk-ISEBLHWX.js.map +0 -1
  185. package/dist/chunk-JP5IEXR7.js +0 -32
  186. package/dist/chunk-JTJIPNXY.js.map +0 -1
  187. package/dist/chunk-K2CAPYES.js +0 -5
  188. package/dist/chunk-LDHPMJ7M.js.map +0 -1
  189. package/dist/chunk-METYYZKQ.js +0 -1115
  190. package/dist/chunk-METYYZKQ.js.map +0 -1
  191. package/dist/chunk-MTAPOCGA.js +0 -47
  192. package/dist/chunk-MTAPOCGA.js.map +0 -1
  193. package/dist/chunk-O6YPEOQF.js +0 -5
  194. package/dist/chunk-P4VWIVKN.js.map +0 -1
  195. package/dist/chunk-SR3X25TN.js +0 -3
  196. package/dist/chunk-SR3X25TN.js.map +0 -1
  197. package/dist/chunk-UHE5FCPB.js +0 -431
  198. package/dist/chunk-UHE5FCPB.js.map +0 -1
  199. package/dist/chunk-V3U4OF7L.js.map +0 -1
  200. package/dist/chunk-VJUTQAPJ.js +0 -2
  201. package/dist/chunk-VJUTQAPJ.js.map +0 -1
  202. package/dist/chunk-VT5XYFDD.js +0 -126
  203. package/dist/chunk-VT5XYFDD.js.map +0 -1
  204. package/dist/client-HGKAC6VP.js +0 -2
  205. package/dist/config-A4RWEJHV.js +0 -2
  206. package/dist/config-mutator-MNSFUN7D.js +0 -2
  207. package/dist/conflict-detector-D7VIYPDI.js.map +0 -1
  208. package/dist/context-planner-45GEWKGL.js +0 -17
  209. package/dist/context-planner-45GEWKGL.js.map +0 -1
  210. package/dist/debug-N3B5NVJU.js +0 -2
  211. package/dist/discover-XJXQXXIO.js.map +0 -1
  212. package/dist/doctor-3Q36ZFE6.js +0 -2
  213. package/dist/heal-HZ3EQD52.js +0 -2
  214. package/dist/loader-5CA3O5GM.js +0 -2
  215. package/dist/loader-6IHUCPVO.js +0 -2
  216. package/dist/memory-extract-AENFVVO5.js +0 -2
  217. package/dist/processes-UIC4XXA6.js +0 -2
  218. package/dist/program-SAUNRCCD.js +0 -2
  219. package/dist/providers-L27BJ5A6.js +0 -2
  220. package/dist/query-HZYYRCLU.js +0 -2
  221. package/dist/recipes-EPLE2SNT.js +0 -2
  222. package/dist/registry-HK3WQ7BS.js +0 -2
  223. package/dist/runner-BFYDWPUO.js +0 -2
  224. package/dist/scheduler-6T2SU5ST.js +0 -2
  225. package/dist/store-I3HEWVLQ.js +0 -2
  226. package/dist/timers-5LPZN55J.js +0 -2
  227. package/dist/types-VECTMYTO.js +0 -2
  228. package/dist/types-Y6ZCZ52Z.js +0 -2
  229. package/dist/wiki-M6YNRFC4.js +0 -2
  230. /package/dist/{App-BPNHIGUX.js.map → App-U52IPUK2.js.map} +0 -0
  231. /package/dist/{ChatApp-6XSSZB4U.js.map → ChatApp-XP4OMHPL.js.map} +0 -0
  232. /package/dist/{agent-5QMITZT6.js.map → agent-V2CA4TXD.js.map} +0 -0
  233. /package/dist/{architect-SEPWMWS4.js.map → article-fields-L4D2BOGY.js.map} +0 -0
  234. /package/dist/{audit-JU3HWISB.js.map → audit-4245CX6P.js.map} +0 -0
  235. /package/dist/{builtin-FRKAQMFS.js.map → builtin-B67DMJ3M.js.map} +0 -0
  236. /package/dist/{chunk-4YCH6IZV.js.map → calibration-ILS5OI44.js.map} +0 -0
  237. /package/dist/{client-HGKAC6VP.js.map → chunk-FIOB3MEX.js.map} +0 -0
  238. /package/dist/{config-A4RWEJHV.js.map → client-PX25MD47.js.map} +0 -0
  239. /package/dist/{compaction-NWEMOSHA.js.map → compaction-FD4R23HZ.js.map} +0 -0
  240. /package/dist/{config-mutator-MNSFUN7D.js.map → config-HL5F36V4.js.map} +0 -0
  241. /package/dist/{debug-N3B5NVJU.js.map → config-mutator-HBX53OMJ.js.map} +0 -0
  242. /package/dist/{doctor-3Q36ZFE6.js.map → debug-3MEUO2P2.js.map} +0 -0
  243. /package/dist/{heal-HZ3EQD52.js.map → doctor-PBAH6XL3.js.map} +0 -0
  244. /package/dist/{loader-5CA3O5GM.js.map → heal-FVLBHYP7.js.map} +0 -0
  245. /package/dist/{ingest-whatsapp-LZE62INA.js.map → ingest-whatsapp-NS4OWDOU.js.map} +0 -0
  246. /package/dist/{loader-6IHUCPVO.js.map → loader-4ZGTK7LD.js.map} +0 -0
  247. /package/dist/{memory-extract-AENFVVO5.js.map → loader-BOKE5M5S.js.map} +0 -0
  248. /package/dist/{processes-UIC4XXA6.js.map → memory-extract-F5ZHWY5E.js.map} +0 -0
  249. /package/dist/{migrate-v2-QO7SLKIM.js.map → migrate-v2-V4WN2VVM.js.map} +0 -0
  250. /package/dist/{program-SAUNRCCD.js.map → processes-WU23U2OJ.js.map} +0 -0
  251. /package/dist/{providers-L27BJ5A6.js.map → program-YGXVCV2Y.js.map} +0 -0
  252. /package/dist/{projects-api-KRME2YKZ.js.map → projects-api-QE6L65SK.js.map} +0 -0
  253. /package/dist/{projects-mutate-3NHVG437.js.map → projects-mutate-MY7JTLSZ.js.map} +0 -0
  254. /package/dist/{query-HZYYRCLU.js.map → providers-KO2DODIO.js.map} +0 -0
  255. /package/dist/{recipes-EPLE2SNT.js.map → query-ZKTFJMNE.js.map} +0 -0
  256. /package/dist/{registry-HK3WQ7BS.js.map → recipes-7S4MC5OJ.js.map} +0 -0
  257. /package/dist/{runner-BFYDWPUO.js.map → registry-PSZDPFZK.js.map} +0 -0
  258. /package/dist/{rotation-memo-B3EXXSEH.js.map → rotation-memo-7T75BLEX.js.map} +0 -0
  259. /package/dist/{scheduler-6T2SU5ST.js.map → runner-YRBVNYTY.js.map} +0 -0
  260. /package/dist/{store-I3HEWVLQ.js.map → scheduler-GZZAN6ZG.js.map} +0 -0
  261. /package/dist/{surface-usage-PRL6Z4U3.js.map → seat-VZ7AVO27.js.map} +0 -0
  262. /package/dist/{timers-5LPZN55J.js.map → store-MCOA6N4F.js.map} +0 -0
  263. /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
  264. /package/dist/{types-VECTMYTO.js.map → surface-usage-NS74AXSE.js.map} +0 -0
  265. /package/dist/{types-Y6ZCZ52Z.js.map → timers-PCB4ZIBD.js.map} +0 -0
  266. /package/dist/{wiki-M6YNRFC4.js.map → types-H5ENIUIT.js.map} +0 -0
  267. /package/dist/{workspace-setup-TBQAHYHJ.js.map → types-OIFU3KSQ.js.map} +0 -0
  268. /package/dist/{whatsapp-state-XMTCSRXU.js.map → whatsapp-state-6D6FHHD2.js.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/decisions/questions.ts"],"sourcesContent":["import type {\n AnswerFor,\n AnyQuestion,\n ChoiceQuestion,\n NoulQuestion,\n Questions,\n ScoreQuestion,\n} from \"./types\"\n\n// Question builders.\n//\n// These exist for one reason: `choice({ billing: null, technical: null })`\n// must produce a question whose answer's `choice` field is typed\n// `\"billing\" | \"technical\"`, not `string`. That is what makes a `switch`\n// over a decision exhaustive at the call site.\n//\n// TypeScript here is 4.9.5 (package.json pins ^4.9.3), so there are no\n// `const` type parameters. We do not need them: the KEYS of an object\n// literal already infer as literal types. `score`'s array literal does\n// widen to `string[]`, which costs nothing because a score is a number.\n\n/** Jev caps a Choice at 255 options; anything larger needs two-stage\n * narrowing. Enforced here from day one so the constraint surfaces while\n * writing the question rather than on the day a Jev key arrives. */\nexport const MAX_CHOICE_OPTIONS = 255\n\nexport function noul(instructions?: string, criteria?: NoulQuestion[\"criteria\"]): NoulQuestion {\n const q: NoulQuestion = { type: \"noul\" }\n return { ...q, ...(instructions ? { instructions } : {}), ...(criteria ? { criteria } : {}) }\n}\n\nexport function choice<C extends Record<string, string | null>>(\n criteria: C,\n instructions?: string,\n): ChoiceQuestion<Extract<keyof C, string>> {\n const labels = Object.keys(criteria)\n if (labels.length < 2) {\n throw new Error(`choice() needs at least 2 options, got ${labels.length}`)\n }\n if (labels.length > MAX_CHOICE_OPTIONS) {\n throw new Error(\n `choice() supports at most ${MAX_CHOICE_OPTIONS} options (Jev's cap), got ${labels.length} — ` +\n `narrow the set first, or split into two stages`,\n )\n }\n return {\n type: \"choice\",\n ...(instructions ? { instructions } : {}),\n criteria: criteria as Readonly<Record<Extract<keyof C, string>, string | null>>,\n }\n}\n\nexport function score(criteria: readonly string[], instructions?: string): ScoreQuestion {\n if (criteria.length < 2) {\n throw new Error(`score() needs at least 2 rubric levels, got ${criteria.length}`)\n }\n return { type: \"score\", ...(instructions ? { instructions } : {}), criteria }\n}\n\n/** The labels a distribution-shaped question ranges over, in a stable order.\n * Choice: its option labels. Score: level indices as strings, \"0\"..\"n-1\".\n * Noul has no distribution and returns []. */\nexport function labelsOf(q: AnyQuestion): string[] {\n if (q.type === \"choice\") return Object.keys(q.criteria)\n if (q.type === \"score\") return q.criteria.map((_, i) => String(i))\n return []\n}\n\n/** Throws on any question the seat cannot honour, so a bad question fails\n * at the call site instead of costing a round trip. */\nexport function validateQuestions(questions: Questions): void {\n const names = Object.keys(questions)\n if (names.length === 0) throw new Error(\"at least one question is required\")\n for (const name of names) {\n const q = questions[name]\n if (q.type === \"choice\") {\n const n = Object.keys(q.criteria).length\n if (n < 2) throw new Error(`question \"${name}\": choice needs at least 2 options, got ${n}`)\n if (n > MAX_CHOICE_OPTIONS) {\n throw new Error(\n `question \"${name}\": choice has ${n} options, over the ${MAX_CHOICE_OPTIONS} cap`,\n )\n }\n } else if (q.type === \"score\") {\n if (q.criteria.length < 2) {\n throw new Error(\n `question \"${name}\": score needs at least 2 rubric levels, got ${q.criteria.length}`,\n )\n }\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Compile-time assertions\n// ---------------------------------------------------------------------------\n//\n// tsconfig.json only includes src/**, so `pnpm typecheck` never sees test/.\n// These live here so the inference contract is checked by the typechecker on\n// every build. The function is never called and never exported; esbuild drops\n// it. If TypeScript is upgraded and inference shifts, this breaks the build\n// rather than silently degrading `choice` to `string`.\n\ntype Equal<A, B> =\n (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : false\ntype Expect<T extends true> = T\n\nfunction _assertInferenceContract() {\n const q = choice({ billing: null, technical: \"needs an engineer\" })\n const _labels: Expect<Equal<AnswerFor<typeof q>[\"choice\"], \"billing\" | \"technical\">> = true\n\n const n = noul(\"is this urgent?\")\n // @ts-expect-error — noul answers carry no confidence; mirroring Jev means\n // reaching for one is a compile error today, not a bug on swap day.\n type _NoConfidence = AnswerFor<typeof n>[\"confidence\"]\n\n const s = score([\"poor\", \"fine\", \"good\"])\n const _score: Expect<Equal<AnswerFor<typeof s>[\"score\"], number>> = true\n\n return [_labels, _score] as const\n}\n"],"mappings":"wCA0BO,SAASA,EAAKC,EAAuBC,EAAmD,CAE7F,MAAO,CAAE,GADe,CAAE,KAAM,MAAO,EACxB,GAAID,EAAe,CAAE,aAAAA,CAAa,EAAI,CAAC,EAAI,GAAIC,EAAW,CAAE,SAAAA,CAAS,EAAI,CAAC,CAAG,CAC9F,CAEO,SAASC,EACdD,EACAD,EAC0C,CAC1C,IAAMG,EAAS,OAAO,KAAKF,CAAQ,EACnC,GAAIE,EAAO,OAAS,EAClB,MAAM,IAAI,MAAM,0CAA0CA,EAAO,QAAQ,EAE3E,GAAIA,EAAO,OAAS,IAClB,MAAM,IAAI,MACR,0DAA4EA,EAAO,8DAErF,EAEF,MAAO,CACL,KAAM,SACN,GAAIH,EAAe,CAAE,aAAAA,CAAa,EAAI,CAAC,EACvC,SAAUC,CACZ,CACF,CAEO,SAASG,EAAMH,EAA6BD,EAAsC,CACvF,GAAIC,EAAS,OAAS,EACpB,MAAM,IAAI,MAAM,+CAA+CA,EAAS,QAAQ,EAElF,MAAO,CAAE,KAAM,QAAS,GAAID,EAAe,CAAE,aAAAA,CAAa,EAAI,CAAC,EAAI,SAAAC,CAAS,CAC9E,CAKO,SAASI,EAASC,EAA0B,CACjD,OAAIA,EAAE,OAAS,SAAiB,OAAO,KAAKA,EAAE,QAAQ,EAClDA,EAAE,OAAS,QAAgBA,EAAE,SAAS,IAAI,CAACC,EAAGC,IAAM,OAAOA,CAAC,CAAC,EAC1D,CAAC,CACV,CAIO,SAASC,EAAkBC,EAA4B,CAC5D,IAAMC,EAAQ,OAAO,KAAKD,CAAS,EACnC,GAAIC,EAAM,SAAW,EAAG,MAAM,IAAI,MAAM,mCAAmC,EAC3E,QAAWC,KAAQD,EAAO,CACxB,IAAML,EAAII,EAAUE,CAAI,EACxB,GAAIN,EAAE,OAAS,SAAU,CACvB,IAAMO,EAAI,OAAO,KAAKP,EAAE,QAAQ,EAAE,OAClC,GAAIO,EAAI,EAAG,MAAM,IAAI,MAAM,aAAaD,4CAA+CC,GAAG,EAC1F,GAAIA,EAAI,IACN,MAAM,IAAI,MACR,aAAaD,kBAAqBC,6BACpC,UAEOP,EAAE,OAAS,SAChBA,EAAE,SAAS,OAAS,EACtB,MAAM,IAAI,MACR,aAAaM,iDAAoDN,EAAE,SAAS,QAC9E,EAIR,CA3FA,IAwBaQ,EAxBbC,EAAAC,EAAA,kBAwBaF,EAAqB","names":["noul","instructions","criteria","choice","labels","score","labelsOf","q","_","i","validateQuestions","questions","names","name","n","MAX_CHOICE_OPTIONS","init_questions","__esmMin"]}
@@ -0,0 +1,25 @@
1
+ import{readFileSync as C,writeFileSync as Y,existsSync as T,readdirSync as ce,statSync as de}from"fs";import{resolve as L,join as ue}from"path";import{existsSync as K,mkdirSync as te,readFileSync as se,writeFileSync as G}from"fs";import{dirname as ne,resolve as J}from"path";import{z as p}from"zod";var ie=p.object({type:p.enum(["gitlab","github"]),host:p.string().optional(),project:p.string(),iid:p.number().int().positive(),url:p.string()}),re=p.object({id:p.string(),title:p.string(),description:p.string().optional(),assignee:p.string().optional(),labels:p.array(p.string()).default([]),milestone:p.string().optional(),status:p.enum(["todo","doing","blocked","done"]).default("todo"),priority:p.number().int().optional(),estimatedSeconds:p.number().int().optional(),source:ie.optional(),importedAt:p.string().optional(),createdAt:p.string(),updatedAt:p.string(),blocker:p.string().optional(),doneNote:p.string().optional()}),oe=p.object({version:p.literal(1),items:p.array(re).default([])}),j=class{constructor(e){this.path=e}jsonPath(){return J(process.cwd(),this.path).replace(/\.md$/i,".json")}mdPath(){return J(process.cwd(),this.path).replace(/\.json$/i,".md")}exists(){return K(this.jsonPath())}load(){let e=this.jsonPath();if(!K(e))return{version:1,items:[]};let t=se(e,"utf-8");try{return oe.parse(JSON.parse(t))}catch(n){throw new Error(`Backlog file invalid (${e}): ${n.message}`)}}save(e){let t=this.jsonPath();te(ne(t),{recursive:!0}),G(t,JSON.stringify(e,null,2)+`
2
+ `),G(this.mdPath(),this.toMarkdown(e))}list(){return this.load().items}findById(e){return this.list().find(t=>t.id===e)}add(e){let t=this.load();if(t.items.some(r=>r.id===e.id))throw new Error(`backlog item ${e.id} already exists`);let n=new Date().toISOString(),s={...e,labels:e.labels??[],status:e.status??"todo",createdAt:e.createdAt??n,updatedAt:n};return t.items.push(s),this.save(t),s}addMany(e){let t=this.load(),n=new Set(t.items.map(o=>o.id)),s=new Date().toISOString(),r=[];for(let o of e){if(n.has(o.id))continue;let i={...o,labels:o.labels??[],status:o.status??"todo",createdAt:o.createdAt??s,updatedAt:s};t.items.push(i),r.push(i),n.add(i.id)}return r.length&&this.save(t),r}update(e,t){let n=this.load(),s=n.items.findIndex(o=>o.id===e);if(s<0)throw new Error(`backlog item ${e} not found`);let r={...n.items[s],...t,id:n.items[s].id,createdAt:n.items[s].createdAt,updatedAt:new Date().toISOString()};return n.items[s]=r,this.save(n),r}remove(e){let t=this.load(),n=t.items.length;return t.items=t.items.filter(s=>s.id!==e),t.items.length===n?!1:(this.save(t),!0)}toMarkdown(e){let t=["# Backlog","","_Generated from `backlog.json`. Do not edit by hand \u2014 use `agentx backlog` commands._",""],n={doing:[],blocked:[],todo:[],done:[]};for(let r of e.items)n[r.status].push(r);let s=[["doing","Doing"],["blocked","Blocked"],["todo","To Do"],["done","Done"]];for(let[r,o]of s)if(n[r].length){t.push(`## ${o}`,"");for(let i of n[r]){let l=r==="done"?"x":" ",c=i.assignee?`@${i.assignee} `:"",d=i.estimatedSeconds?` [time: ${Math.round(i.estimatedSeconds/60)}m]`:"",g=i.source?` (${i.source.type}: ${i.source.project}#${i.source.iid})`:"";t.push(`- [${l}] ${c}${i.title}${d}${g}`),i.source?.url&&t.push(` - <${i.source.url}>`),i.labels.length&&t.push(` - labels: ${i.labels.join(", ")}`),i.milestone&&t.push(` - milestone: ${i.milestone}`),i.blocker&&t.push(` - BLOCKED: ${i.blocker}`)}t.push("")}return t.join(`
3
+ `)}};function Le(a){return`${a.type}:${a.project}:${a.iid}`}function X(a,e,t){let n=[];if(e.type==="gitlab"){let s=t.channels.gitlab?.agentMappings??[];for(let r of a){let o=s.find(i=>i.agentId===r);o?.gitlabUsernames?.length?n.push(o.gitlabUsernames[0]):n.push(r)}}else if(e.type==="github"){let s=t.channels.github?.agentMappings??[];for(let r of a){let o=s.find(i=>i.agentId===r);o?.githubUsernames?.length?n.push(o.githubUsernames[0]):n.push(r)}}return n}async function R(a,e,t,n=()=>{}){return a.source?a.source.type==="gitlab"?ae(a.source,e,t,n):a.source.type==="github"?le(a.source,e,t,n):{ok:!1,error:`unknown source.type ${a.source.type}`,attempts:[]}:{ok:!0,attempts:[]}}async function ae(a,e,t,n){let s=t.channels.gitlab,r=s?.token,o=s?.host||(a.host?`https://${a.host}`:void 0);if(!r||!o)return{ok:!1,error:"gitlab token/host not configured",attempts:[]};let i=[],l=async(m,f={})=>fetch(`${o}/api/v4${m}`,{...f,headers:{"PRIVATE-TOKEN":r,"Content-Type":"application/json",...f.headers||{}}}),c=encodeURIComponent(a.project),d;e.assigneeUsernames!==void 0&&(d=(await Promise.all(e.assigneeUsernames.map(async f=>{try{let y=await fetch(`${o}/api/v4/users?username=${encodeURIComponent(f)}`,{headers:{"PRIVATE-TOKEN":r}});if(!y.ok)return null;let w=await y.json();return Array.isArray(w)&&w[0]?.id?w[0].id:null}catch{return null}}))).filter(f=>typeof f=="number"));let g;if(e.milestoneTitle!==void 0)if(e.milestoneTitle===null||e.milestoneTitle==="")g=0;else try{let m=await l(`/projects/${c}/milestones?title=${encodeURIComponent(e.milestoneTitle)}`);if(m.ok){let f=await m.json();Array.isArray(f)&&f[0]?.id&&(g=f[0].id)}}catch(m){n(`[backlog-sync] milestone lookup failed: ${m.message}`)}let u={};if(e.title!==void 0&&(u.title=e.title),e.description!==void 0&&(u.description=e.description),e.labels!==void 0&&(u.labels=e.labels.join(",")),e.addLabels?.length&&(u.add_labels=e.addLabels.join(",")),e.removeLabels?.length&&(u.remove_labels=e.removeLabels.join(",")),d!==void 0&&(u.assignee_ids=d),g!==void 0&&(u.milestone_id=g),e.state&&(u.state_event=e.state==="closed"?"close":"reopen"),Object.keys(u).length)try{let m=await l(`/projects/${c}/issues/${a.iid}`,{method:"PUT",body:JSON.stringify(u)});if(i.push({kind:"PUT issue",ok:m.ok,status:m.status}),!m.ok){let f=await m.text().catch(()=>"");return{ok:!1,attempts:i,error:`PUT issue ${m.status}: ${f.slice(0,200)}`,url:a.url}}}catch(m){return i.push({kind:"PUT issue",ok:!1,error:m.message}),{ok:!1,attempts:i,error:m.message,url:a.url}}if(e.note)try{let m=await l(`/projects/${c}/issues/${a.iid}/notes`,{method:"POST",body:JSON.stringify({body:e.note})});i.push({kind:"POST note",ok:m.ok,status:m.status})}catch(m){i.push({kind:"POST note",ok:!1,error:m.message})}return{ok:i.every(m=>m.ok),attempts:i,url:a.url}}async function le(a,e,t,n){let r=t.channels.github?.token;if(!r)return{ok:!1,error:"github token not configured",attempts:[]};let o=[],i=async(u,m={})=>fetch(`https://api.github.com${u}`,{...m,headers:{Authorization:`Bearer ${r}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28",...m.headers||{}}}),l=a.project,c;if(e.milestoneTitle!==void 0)if(e.milestoneTitle===null||e.milestoneTitle==="")c=null;else try{let u=await i(`/repos/${l}/milestones?state=all&per_page=100`);if(u.ok){let m=await u.json(),f=Array.isArray(m)?m.find(y=>y.title===e.milestoneTitle):null;f?.number&&(c=f.number)}}catch(u){n(`[backlog-sync] github milestone lookup failed: ${u.message}`)}let d;if(e.labels!==void 0)d=e.labels;else if(e.addLabels?.length||e.removeLabels?.length)try{let u=await i(`/repos/${l}/issues/${a.iid}`);if(u.ok){let m=await u.json(),f=Array.isArray(m.labels)?m.labels.map(w=>typeof w=="string"?w:w.name):[],y=new Set([...f,...e.addLabels??[]]);for(let w of e.removeLabels??[])y.delete(w);d=[...y]}}catch(u){n(`[backlog-sync] github label fetch failed: ${u.message}`)}let g={};if(e.title!==void 0&&(g.title=e.title),e.description!==void 0&&(g.body=e.description),d!==void 0&&(g.labels=d),e.assigneeUsernames!==void 0&&(g.assignees=e.assigneeUsernames),c!==void 0&&(g.milestone=c),e.state&&(g.state=e.state),Object.keys(g).length)try{let u=await i(`/repos/${l}/issues/${a.iid}`,{method:"PATCH",body:JSON.stringify(g)});if(o.push({kind:"PATCH issue",ok:u.ok,status:u.status}),!u.ok){let m=await u.text().catch(()=>"");return{ok:!1,attempts:o,error:`PATCH issue ${u.status}: ${m.slice(0,200)}`,url:a.url}}}catch(u){return o.push({kind:"PATCH issue",ok:!1,error:u.message}),{ok:!1,attempts:o,error:u.message,url:a.url}}if(e.note)try{let u=await i(`/repos/${l}/issues/${a.iid}/comments`,{method:"POST",body:JSON.stringify({body:e.note})});o.push({kind:"POST comment",ok:u.ok,status:u.status})}catch(u){o.push({kind:"POST comment",ok:!1,error:u.message})}return{ok:o.every(u=>u.ok),attempts:o,url:a.url}}var B=/^(\s*)-\s*\[( |x|X)\]\s*(.*)$/,x=/@([a-z0-9_-]+)/i,V=/\[time:\s*([0-9hm\s]+)\]/i;function me(a){let e=a.match(/(\d+)\s*h/i),t=a.match(/(\d+)\s*m/i);return(e?parseInt(e[1],10)*3600:0)+(t?parseInt(t[1],10)*60:0)}var E=class{constructor(e,t,n=()=>{}){this.path=e;this.daemon=t;this.log=n;this.store=new j(this.path)}type="backlog";capabilities={listAll:!0,create:!1,transition:!1,labels:!0,search:!1};store;getStore(){return this.store}readMd(){let e=L(process.cwd(),this.path);return T(e)?C(e,"utf-8").split(`
4
+ `):[]}writeMd(e){let t=L(process.cwd(),this.path);Y(t,e.join(`
5
+ `))}async listOpen(e){if(this.store.exists())return this.store.list().filter(r=>r.status!=="done"&&(!r.assignee||r.assignee.toLowerCase()===e.toLowerCase())).map(r=>this.toWorkItem(r));let t=this.readMd(),n=[];for(let s=0;s<t.length;s++){let r=t[s].match(B);if(!r||r[2].toLowerCase()==="x")continue;let i=r[3],l=i.match(x);if(l&&l[1].toLowerCase()!==e.toLowerCase())continue;let c=i.match(V),d=i.replace(x,"").replace(V,"").trim();n.push({id:`backlog:${s}`,title:d||`Task at line ${s+1}`,assignee:l?l[1]:void 0,estimatedSeconds:c?me(c[1]):void 0,priority:s})}return n}async listAll(e={}){return this.store.exists()?this.store.list().map(t=>this.toWorkItem(t)):[]}async claim(e,t){if(!this.store.exists())return;let n=this.store.findById(t);if(!n)return;let s=this.store.update(t,{assignee:n.assignee||e,status:"doing"});if(s.source&&this.daemon){let r=X([s.assignee||e],s.source,this.daemon);R(s,{assigneeUsernames:r,addLabels:["Doing"],removeLabels:["To Do"]},this.daemon,this.log).catch(o=>this.log(`[backlog-sync] claim failed for ${t}: ${o?.message??o}`))}}async report(e,t){if(this.store.exists()&&this.store.findById(e)){let i={};t.status==="done"?i.status="done":t.status==="blocked"?(i.status="blocked",i.blocker=t.blocker||t.note||void 0):t.status==="in-progress"&&(i.status="doing"),t.timeSeconds&&(i.estimatedSeconds=t.timeSeconds);let l=this.store.update(e,i);if(l.source&&this.daemon){let c={};t.status==="done"?(c.addLabels=["Done"],c.removeLabels=["Doing"]):t.status==="blocked"&&(c.addLabels=["Blocked"]),t.note&&(c.note=t.note),Object.keys(c).length&&R(l,c,this.daemon,this.log).catch(d=>this.log(`[backlog-sync] report failed for ${e}: ${d?.message??d}`))}return}if(!e.startsWith("backlog:"))throw new Error(`not a backlog id: ${e}`);let n=parseInt(e.slice(8),10),s=this.readMd(),r=s[n];if(!r)throw new Error(`backlog line ${n} missing`);if(!r.match(B))throw new Error(`backlog line ${n} not a checkbox`);if(t.status==="done"){s[n]=r.replace(/- \[ \]/,"- [x]");let i=t.timeSeconds?` <!-- done by agent, ${Math.round(t.timeSeconds/60)}m -->`:" <!-- done by agent -->";s.splice(n+1,0,i)}else t.status==="blocked"?s.splice(n+1,0,` <!-- BLOCKED: ${t.blocker||t.note||"?"} -->`):t.note&&s.splice(n+1,0,` <!-- ${t.note.replace(/-->/g,"\u2014 >")} -->`);this.writeMd(s)}toWorkItem(e){let t={todo:"todo",doing:"doing",blocked:"onhold",done:"done"}[e.status];return{id:e.id,title:e.title,description:e.description,assignee:e.assignee,estimatedSeconds:e.estimatedSeconds,priority:e.priority,labels:e.labels,stage:t,milestone:e.milestone,url:e.source?.url,updatedAt:e.updatedAt,state:e.status==="done"?"closed":"opened"}}},P=class{constructor(e,t){this.root=e;this.glob=t}type="wiki";capabilities={listAll:!1,create:!1,transition:!1,labels:!1,search:!1};walk(e,t){if(T(e))for(let n of ce(e)){let s=ue(e,n);de(s).isDirectory()?this.walk(s,t):n.endsWith(".md")&&t.push(s)}}async listOpen(e){let t=[];this.walk(L(process.cwd(),this.root),t);let n=[];for(let s of t){let r=C(s,"utf-8").split(`
6
+ `);for(let o=0;o<r.length;o++){let i=r[o].match(B);if(!i||i[2].toLowerCase()==="x")continue;let l=i[3].match(x);!l||l[1].toLowerCase()!==e.toLowerCase()||n.push({id:`wiki:${s}:${o}`,title:i[3].replace(x,"").trim(),assignee:l[1],priority:o})}}return n}async claim(){}async report(e,t){let n=e.split(":"),s=parseInt(n[n.length-1],10),r=n.slice(1,-1).join(":");if(!T(r))return;let o=C(r,"utf-8").split(`
7
+ `);t.status==="done"&&o[s]&&(o[s]=o[s].replace(/- \[ \]/,"- [x]"),Y(r,o.join(`
8
+ `)))}},U=class{constructor(e,t,n,s,r){this.host=e;this.token=t;this.projects=n;this.agentUsernames=s;this.log=r}type="gitlab";capabilities={listAll:!0,create:!0,transition:!0,labels:!0,search:!0};usernameToAgent(e){for(let[t,n]of Object.entries(this.agentUsernames))if(n.some(s=>s.toLowerCase()===e.toLowerCase()))return t}mapIssue(e,t){let n=e.references?.full?.split("#")[0]||t,s=[],r;Array.isArray(e.labels)&&(e.labels.length&&typeof e.labels[0]=="object"?(r=e.labels.map(c=>({name:c.name,color:c.color,text_color:c.text_color})),s=r.map(c=>c.name)):s=e.labels);let i=(Array.isArray(e.assignees)&&e.assignees.length?e.assignees:e.assignee?[e.assignee]:[]).map(c=>({username:c.username,name:c.name,avatarUrl:c.avatar_url})),l=i[0]?.username;return{id:`gitlab:${n}:issue:${e.iid}`,title:e.title,description:e.description||void 0,assignee:l?this.usernameToAgent(l)||l:void 0,assignees:i,url:e.web_url,priority:e.weight??99,estimatedSeconds:e.time_stats?.time_estimate||void 0,labels:s,labelDetails:r,updatedAt:e.updated_at,state:e.state==="closed"?"closed":"opened",milestone:e.milestone?.title||void 0}}async api(e,t={}){let n=await fetch(`${this.host}/api/v4${e}`,{...t,headers:{"PRIVATE-TOKEN":this.token,"Content-Type":"application/json",...t.headers||{}}});if(!n.ok)throw new Error(`GitLab ${n.status}: ${await n.text().catch(()=>"")}`);return n.json()}async listOpen(e){let t=this.agentUsernames[e]||[];if(!t.length)return[];let n=[],s=this.projects.length?this.projects:["__ALL__"];for(let r of t)for(let o of s)try{let i=`assignee_username=${encodeURIComponent(r)}&state=opened&scope=all&per_page=20`,l=o==="__ALL__"?`/issues?${i}`:`/projects/${encodeURIComponent(o)}/issues?${i}`,c=await this.api(l);for(let d of c){let g=this.mapIssue(d,o);g.assignee=e,n.push(g)}}catch(i){this.log(`[business] GitLab listOpen failed for ${r} on ${o}: ${i.message}`)}return n}async claim(e,t){let{project:n,iid:s,type:r}=this.parseId(t),o=`/projects/${encodeURIComponent(n)}/${r==="issue"?"issues":"merge_requests"}/${s}?add_labels=Doing&remove_labels=To Do`;try{await this.api(o,{method:"PUT"})}catch(i){this.log(`[business] claim label update failed: ${i.message}`)}}async report(e,t){let{project:n,iid:s,type:r}=this.parseId(e),o=r==="issue"?"issues":"merge_requests";if(t.timeSeconds&&t.timeSeconds>0){let i=Math.max(1,Math.round(t.timeSeconds/60)),l=i>=60?`${Math.floor(i/60)}h${i%60?` ${i%60}m`:""}`:`${i}m`;try{await this.api(`/projects/${encodeURIComponent(n)}/${o}/${s}/add_spent_time?duration=${encodeURIComponent(l)}`,{method:"POST"})}catch(c){this.log(`[business] add_spent_time failed: ${c.message}`)}}if(t.status==="done")try{await this.api(`/projects/${encodeURIComponent(n)}/${o}/${s}?add_labels=Done&remove_labels=Doing`,{method:"PUT"})}catch(i){this.log(`[business] done label update failed: ${i.message}`)}else if(t.status==="blocked")try{await this.api(`/projects/${encodeURIComponent(n)}/${o}/${s}?add_labels=Blocked`,{method:"PUT"})}catch(i){this.log(`[business] blocked label update failed: ${i.message}`)}if(t.note)try{await this.api(`/projects/${encodeURIComponent(n)}/${o}/${s}/notes`,{method:"POST",body:JSON.stringify({body:t.note})})}catch(i){this.log(`[business] report note failed: ${i.message}`)}}async listAll(e={}){let t=e.sinceDays??30,n=new Date(Date.now()-t*24*36e5).toISOString(),s=new URLSearchParams,r=e.state??"opened";s.set("state",r),s.set("updated_after",n),s.set("scope","all"),s.set("per_page","100"),s.set("with_labels_details","true"),s.set("order_by","updated_at"),s.set("sort","desc"),e.labels?.length&&s.set("labels",e.labels.join(",")),e.milestone&&s.set("milestone",e.milestone),e.search&&s.set("search",e.search);let o=this.projects.length?this.projects:["__ALL__"],i=[];for(let c of o)try{let d=c==="__ALL__"?`/issues?${s.toString()}`:`/projects/${encodeURIComponent(c)}/issues?${s.toString()}`,g=await this.api(d);for(let u of g)i.push(this.mapIssue(u,c))}catch(d){this.log(`[business] GitLab listAll failed on ${c}: ${d.message}`)}let l=i;if(e.notLabels?.length){let c=new Set(e.notLabels);l=l.filter(d=>!(d.labels||[]).some(g=>c.has(g)))}if(e.withoutScopedPrefix){let c=e.withoutScopedPrefix+"::";l=l.filter(d=>!(d.labels||[]).some(g=>g.startsWith(c)))}return l}async setState(e,t){let{project:n,iid:s,type:r}=this.parseId(e),o=r==="issue"?"issues":"merge_requests";await this.api(`/projects/${encodeURIComponent(n)}/${o}/${s}?state_event=${t}`,{method:"PUT"})}async getItem(e){let{project:t,iid:n}=this.parseId(e);try{let s=await this.api(`/projects/${encodeURIComponent(t)}/issues/${n}?with_labels_details=true`);return{...this.mapIssue(s,t),descriptionHtml:s.description_html||void 0,createdAt:s.created_at,author:s.author?{username:s.author.username,name:s.author.name,avatarUrl:s.author.avatar_url}:void 0,allLabels:Array.isArray(s.labels)?typeof s.labels[0]=="object"?s.labels.map(o=>o.name):s.labels:[]}}catch(s){return this.log(`[board] GitLab getItem failed for ${e}: ${s.message}`),null}}async updateItem(e,t){let{project:n,iid:s}=this.parseId(e),r;t.assigneeUsernames&&(r=(await Promise.all(t.assigneeUsernames.map(async d=>{try{let g=await this.api(`/users?username=${encodeURIComponent(d)}`);return Array.isArray(g)&&g[0]?.id?g[0].id:null}catch{return null}}))).filter(d=>typeof d=="number"));let o;if(t.milestoneTitle!==void 0)if(t.milestoneTitle===null||t.milestoneTitle==="")o=0;else try{let c=await this.api(`/projects/${encodeURIComponent(n)}/milestones?title=${encodeURIComponent(t.milestoneTitle)}`);Array.isArray(c)&&c[0]?.id&&(o=c[0].id)}catch(c){this.log(`[board] milestone lookup failed for "${t.milestoneTitle}": ${c.message}`)}let i={};t.title!==void 0&&(i.title=t.title),t.description!==void 0&&(i.description=t.description),t.labels!==void 0&&(i.labels=t.labels.join(",")),t.addLabels?.length&&(i.add_labels=t.addLabels.join(",")),t.removeLabels?.length&&(i.remove_labels=t.removeLabels.join(",")),r!==void 0&&(i.assignee_ids=r),o!==void 0&&(i.milestone_id=o);let l=await this.api(`/projects/${encodeURIComponent(n)}/issues/${s}`,{method:"PUT",body:JSON.stringify(i)});return this.mapIssue(l,n)}async listMembers(){let e=new Map;for(let t of this.projects)try{let n=await this.api(`/projects/${encodeURIComponent(t)}/members/all?per_page=100`);for(let s of n)s.username&&!e.has(s.username)&&e.set(s.username,{username:s.username,name:s.name,avatarUrl:s.avatar_url})}catch(n){this.log(`[board] listMembers failed for ${t}: ${n.message}`)}return[...e.values()].sort((t,n)=>t.username.localeCompare(n.username))}async listMilestones(){let e=new Map;for(let n of this.projects)try{let s=await this.api(`/projects/${encodeURIComponent(n)}/milestones?per_page=100`);for(let r of s)r.title&&!e.has(r.title)&&e.set(r.title,{id:r.id,title:r.title,state:r.state,dueDate:r.due_date})}catch(s){this.log(`[board] listMilestones failed for ${n}: ${s.message}`)}let t=[...e.values()];return t.sort((n,s)=>n.state!==s.state?n.state==="active"?-1:1:n.title.localeCompare(s.title)),t}async create(e){let t=e.projectHint||this.projects[0];if(!t)throw new Error("GitLabWorkSource.create requires projectHint or configured projects");let n,s=e.assigneeUsernames?.length?e.assigneeUsernames:e.assignee?(this.agentUsernames[e.assignee]||[]).slice(0,1):[];s.length&&(n=(await Promise.all(s.map(async c=>{try{let d=await this.api(`/users?username=${encodeURIComponent(c)}`);return Array.isArray(d)&&d[0]?.id?d[0].id:null}catch(d){return this.log(`[board] user lookup failed for ${c}: ${d.message}`),null}}))).filter(c=>typeof c=="number"));let r;if(e.milestoneTitle)try{let l=await this.api(`/projects/${encodeURIComponent(t)}/milestones?title=${encodeURIComponent(e.milestoneTitle)}`);Array.isArray(l)&&l[0]?.id&&(r=l[0].id)}catch(l){this.log(`[board] milestone lookup failed for "${e.milestoneTitle}": ${l.message}`)}let o={title:e.title,description:e.description,labels:(e.labels||[]).join(",")};n?.length&&(o.assignee_ids=n),r!==void 0&&(o.milestone_id=r);let i=await this.api(`/projects/${encodeURIComponent(t)}/issues`,{method:"POST",body:JSON.stringify(o)});return this.mapIssue(i,t)}async transition(e,t,n){await this.transitionMany(e,t?[t]:[],n?[n]:[])}async transitionMany(e,t,n){let{project:s,iid:r,type:o}=this.parseId(e),i=o==="issue"?"issues":"merge_requests",l=new URLSearchParams;t.length&&l.set("add_labels",t.join(",")),n.length&&l.set("remove_labels",n.join(",")),l.toString()&&await this.api(`/projects/${encodeURIComponent(s)}/${i}/${r}?${l.toString()}`,{method:"PUT"})}parseId(e){let n=e.replace(/^gitlab:/,"").split(":"),s=n.pop(),r=n.pop()==="mr"?"mr":"issue";return{project:n.join(":"),type:r,iid:s}}};function De(a,e,t){let n=a.workSource;switch(n.type){case"backlog":return new E(n.path,e,t);case"wiki":return new P(n.path,n.glob);case"gitlab":{let s=e.channels.gitlab;if(!s?.token)throw new Error("business.workSource.type=gitlab requires channels.gitlab.token");let r={};for(let i of s.agentMappings)i.gitlabUsernames.length&&(r[i.agentId]=i.gitlabUsernames);let o=n.projects.length?n.projects:s.routes.map(i=>i.project);return new U(s.host,s.token,o,r,t)}}}import{existsSync as ge,readdirSync as fe,readFileSync as pe,statSync as he}from"fs";import{resolve as be}from"path";function Ne(a,e,t=Date.now(),n=7){let s=n*864e5,r=new Map;for(let o of e){let i=r.get(o.workflowId);i?i.push(o):r.set(o.workflowId,[o])}return a.map(o=>{let i=r.get(o.id)??[],l=i.filter(f=>t-f.at<=s),c=i.filter(f=>t-f.at>s&&t-f.at<=s*2),d=l.filter(f=>f.status==="failed").length,g=i.filter(f=>f.status==="paused").length,u=i.length?Math.max(...i.map(f=>f.at)):null,m=i.length?c.length>0&&l.length===0?"dormant":l.length>0&&d*2>=l.length?"failing":l.length>0?"active":"quiet":"never";return{id:o.id,name:o.name||o.id,state:m,lastRunAt:u,recent:l.length,prior:c.length,failed:d,paused:g}}).sort((o,i)=>{let l={dormant:0,failing:1,active:2,quiet:3,never:4};return l[o.state]-l[i.state]||(i.lastRunAt??0)-(o.lastRunAt??0)})}var A=new Map;function He(a,e=1e3){if(!ge(a))return[];let t=fe(a).filter(r=>r.endsWith(".jsonl")).map(r=>{let o=be(a,r);return{path:o,mtime:he(o).mtimeMs}}).sort((r,o)=>o.mtime-r.mtime).slice(0,e),n=new Set(t.map(r=>r.path));for(let r of A.keys())n.has(r)||A.delete(r);let s=[];for(let{path:r,mtime:o}of t){let i=A.get(r);if(i&&i.mtime===o){i.summary&&s.push(i.summary);continue}let l=ke(r,o);A.set(r,{mtime:o,summary:l}),l&&s.push(l)}return s}function ke(a,e){try{let t=pe(a,"utf-8").split(`
9
+ `).filter(Boolean);if(!t.length)return null;let n=JSON.parse(t[0]),s=n.run?.workflowId;if(!s)return null;let r=n.run?.status??"unknown";for(let o=t.length-1;o>=0;o--){let i=JSON.parse(t[o]),l=i.status??i.run?.status;if(l){r=l;break}}return{workflowId:s,status:r,at:e}}catch{return null}}var W="unmapped";function O(a,e=[]){if(!a)return W;let t=e.find(s=>s.id===a);if(t?.client)return t.client;let n=a.indexOf("/");return n>0?a.slice(0,n):a}function ye(a){let e=new Map,t=a.projects??[],n=a.orgChart??{};for(let r of t)r.pm&&(e.has(r.pm)||e.set(r.pm,O(r.id,t)));let s=(r,o=new Set)=>{if(e.has(r))return e.get(r);if(o.has(r))return;o.add(r);let i=n[r]?.reportsTo;if(!i)return;let l=s(i,o);return l&&e.set(r,l),l};for(let r of Object.keys(n))s(r);return e}function we(a,e,t=[]){if(!t.length)return;let n=t.filter(s=>!s.channel||s.channel===a);return n.find(s=>s.chatId&&e.chatId&&s.chatId===e.chatId)??n.find(s=>s.username&&e.username&&s.username===e.username)??n.find(s=>s.senderId&&e.senderId&&s.senderId===e.senderId)??n.find(s=>s.channel&&!s.chatId&&!s.username&&!s.senderId)}function Ke(a,e){let t=we(a.channel,{chatId:a.chatId,username:a.username},e.contactMap);if(t)return t.client;if(a.project)return O(a.project,e.projects);if(a.agentId){let n=ye(e).get(a.agentId);if(n)return n}return W}function Ge(a,e){let t=String(a||"").split(":");if(t.length<3)return{agentId:e};let[n,s,...r]=t,o=r.join(":"),i={agentId:e||n,channel:s};return s==="gitlab"||s==="github"?i.project=o.split(":")[0]:s==="cron"||s==="api"||(i.chatId=o.split("@")[0]),i}var $e=a=>a===W?"internal":"client";function Se(a,e){let t=e.clients?.[a];return{id:a,name:t?.name||a,kind:t?.kind??$e(a),respondWithinMinutes:Ie(t?.respondWithin),standing:t?.standing??[],declared:!!t}}function Je(a){let e=new Set;for(let t of a.projects??[])e.add(O(t.id,a.projects));for(let t of a.contactMap??[])e.add(t.client);for(let t of Object.keys(a.clients??{}))e.add(t);return[...e].sort().map(t=>Se(t,a))}function Ie(a){let e=/^(\d+)\s*([mhd])$/.exec(String(a??"").trim());if(!e)return;let t=Number(e[1]);return e[2]==="m"?t:e[2]==="h"?t*60:t*1440}import{existsSync as b,readFileSync as F,writeFileSync as N,mkdirSync as _,readdirSync as z,rmSync as ve,statSync as k}from"fs";import{resolve as Z,relative as je,dirname as H,join as S,extname as v,basename as q}from"path";var h=200*1024,Q=new Set([".md",".markdown",".txt"]),I=new Set([".md",".markdown",".txt",".json",".jsonc",".yaml",".yml",".toml",".sh",".bash",".zsh",".py",".js",".mjs",".ts",".tsx",".jsx",".css",".html",".lock",".cfg",".ini",".env",".gitignore"]),xe=6,Ae=new Set(["CLAUDE.md","SOUL.md","IDENTITY.md","PERSONA.md","AGENT.md","BOOTSTRAP.md"]);function $(a,e){if(!a)throw new Error("Agent workspace is not configured.");let t=Z(a);if(b(t)||_(t,{recursive:!0}),!e||e.startsWith("/")||e.match(/^[a-zA-Z]:/))throw new Error(`Path must be relative to the workspace: ${e}`);let n=Z(t,e);if(je(t,n).startsWith(".."))throw new Error(`Path escapes the workspace: ${e}`);return n}function ee(a){let e=v(a).toLowerCase();if(!Q.has(e))throw new Error(`Only text files (.md, .markdown, .txt) can be edited from the admin panel (got ${e}).`)}function D(a,e){let t=a.split(/\r?\n/).find(n=>n.trim().startsWith("#"));return t&&t.replace(/^#+\s*/,"").trim().slice(0,120)||e}function Ze(a){let e=$(a,"."),t=[],n=new Set,s=[];try{s=z(e)}catch{}for(let i of Ae){let l=S(e,i);if(b(l)&&k(l).isFile()){let c=k(l).size,d=c>0&&c<h?M(l):"";t.push({path:i,title:D(d,i),size:c,exists:!0}),n.add(i)}else t.push({path:i,title:i,size:0,exists:!1}),n.add(i)}for(let i of s){if(n.has(i))continue;let l=S(e,i);try{let c=k(l);if(!c.isFile()||!Q.has(v(i).toLowerCase()))continue;let d=c.size>0&&c.size<h?M(l):"";t.push({path:i,title:D(d,i),size:c.size,exists:!0})}catch{}}let r=[],o=S(e,".claude","skills");if(b(o))try{for(let i of z(o)){let l=S(o,i),c;try{c=k(l)}catch{continue}if(!c.isDirectory())continue;let d=S(l,"SKILL.md");if(!b(d))continue;let g=k(d),u=g.size<h?M(d):"";r.push({slug:i,title:D(u,i),size:g.size,path:`.claude/skills/${i}/SKILL.md`})}}catch{}return r.sort((i,l)=>i.slug.localeCompare(l.slug)),{identity:t,skills:r,workspace:e}}function M(a){try{return F(a,"utf-8")}catch{return""}}function Qe(a,e){let t=$(a,e);if(ee(t),!b(t))return{path:e,content:""};if(k(t).size>h)throw new Error(`File is too large to edit in the admin panel (> ${h/1024} KB).`);return{path:e,content:F(t,"utf-8")}}function et(a,e,t){let n=$(a,e);ee(n);let s=Buffer.from(t,"utf-8");if(s.length>h)throw new Error(`File too large (${s.length} > ${h} bytes).`);return _(H(n),{recursive:!0}),N(n,s),{path:e,bytes:s.length}}function tt(a,e,t={}){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Skill slug must be lowercase (letters, digits, -, _) and start with a letter or digit.");let n=`.claude/skills/${e}/SKILL.md`,s=$(a,n);if(b(s))throw new Error(`Skill "${e}" already exists.`);_(H(s),{recursive:!0});let r=t.content?.trim()?t.content:_e(e,t.title);return N(s,r),{slug:e,path:n}}function st(a,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let t=$(a,`.claude/skills/${e}`);if(!b(t))throw new Error(`Skill "${e}" not found.`);if(!k(t).isDirectory())throw new Error(`Skill path is not a directory: ${e}`);return ve(t,{recursive:!0,force:!0}),{slug:e}}function nt(a,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let t=$(a,`.claude/skills/${e}`);if(!b(t))throw new Error(`Skill "${e}" not found.`);let n=[];return s(t,"",0,n),n.sort((r,o)=>r.kind!==o.kind?r.kind==="dir"?-1:1:r.path.localeCompare(o.path)),n;function s(r,o,i,l){if(i>xe)return;let c=[];try{c=z(r)}catch{return}for(let d of c){if(d==="node_modules"||d===".git")continue;let g=S(r,d),u=o?`${o}/${d}`:d,m;try{m=k(g)}catch{continue}if(m.isDirectory())l.push({path:`${u}/`,kind:"dir",size:0,editable:!1}),s(g,u,i+1,l);else if(m.isFile()){let f=v(d).toLowerCase(),y=I.has(f)||I.has(d.toLowerCase());l.push({path:u,kind:"file",size:m.size,editable:y&&m.size<h})}}}}function it(a,e,t){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);if(!t||t.includes(".."))throw new Error(`Invalid file path: ${t}`);let n=$(a,`.claude/skills/${e}/${t}`);if(!b(n)||!k(n).isFile())throw new Error(`File not found: ${t}`);let s=v(n).toLowerCase(),r=q(n).toLowerCase();if(!I.has(s)&&!I.has(r))throw new Error(`File type not editable: ${t}`);if(k(n).size>h)throw new Error(`File too large to edit (> ${h/1024} KB): ${t}`);return{path:t,content:F(n,"utf-8")}}function rt(a,e,t,n){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);if(!t||t.includes(".."))throw new Error(`Invalid file path: ${t}`);let s=$(a,`.claude/skills/${e}/${t}`),r=v(s).toLowerCase(),o=q(s).toLowerCase();if(!I.has(r)&&!I.has(o))throw new Error(`File type not editable: ${t}`);let i=Buffer.from(n,"utf-8");if(i.length>h)throw new Error(`File too large (${i.length} > ${h} bytes).`);return _(H(s),{recursive:!0}),N(s,i),{path:t,bytes:i.length}}function ot(a,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let t=$(a,`.claude/skills/${e}`);if(!b(t))throw new Error(`Skill "${e}" not found.`);let n=s=>b(S(t,s));return n("pnpm-lock.yaml")?{manager:"pnpm",file:"pnpm-lock.yaml",command:"pnpm install"}:n("yarn.lock")?{manager:"yarn",file:"yarn.lock",command:"yarn install"}:n("package.json")?{manager:"npm",file:"package.json",command:"npm install"}:n("requirements.txt")?{manager:"pip",file:"requirements.txt",command:"pip install -r requirements.txt"}:n("Gemfile")?{manager:"bundle",file:"Gemfile",command:"bundle install"}:{manager:null,file:null,command:null}}function _e(a,e){return`# ${e?.trim()||a}
10
+
11
+ Describe what this skill does and when the agent should use it. Keep it under ~5 lines \u2014 the agent reads this to decide whether to invoke the skill.
12
+
13
+ ## When to use
14
+ - Trigger condition 1
15
+ - Trigger condition 2
16
+
17
+ ## How to use
18
+ Document the arguments, commands, or files the skill expects. Example:
19
+
20
+ \`\`\`bash
21
+ # Example invocation
22
+ claude-code ${q(a)} --arg value
23
+ \`\`\`
24
+ `}export{Ne as a,He as b,O as c,ye as d,we as e,Ke as f,Ge as g,Je as h,j as i,Le as j,X as k,R as l,U as m,De as n,Ze as o,Qe as p,et as q,tt as r,st as s,nt as t,it as u,rt as v,ot as w};
25
+ //# sourceMappingURL=chunk-F6CNF7V3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/business/work-pool.ts","../src/business/backlog-store.ts","../src/business/backlog-sync.ts","../src/daemon/workflow-health.ts","../src/business/clients.ts","../src/daemon/file-ops.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from \"fs\"\nimport { resolve, join } from \"path\"\nimport type { BusinessConfig, BusinessWorkSource } from \"./config\"\nimport type { DaemonConfig } from \"@/daemon/config\"\nimport { BacklogStore } from \"./backlog-store\"\nimport { syncBacklogItemToSource, resolveAssigneesForSource, type BacklogPatch } from \"./backlog-sync\"\n\n// --- Work pool: pluggable source of tasks for the business layer ---\n\nexport interface WorkItem {\n id: string // stable id for claim/report (e.g. \"backlog:line-12\", \"gitlab:proj:issue:42\")\n title: string\n description?: string\n assignee?: string // agentId\n estimatedSeconds?: number\n url?: string\n priority?: number // lower = higher priority\n /** Board stage / kanban column id. Was a closed enum back when boards\n * had a fixed schema; widened to `string` because per-project boards\n * can declare arbitrary column ids. The well-known names below remain\n * the standard set most boards use. */\n stage?: string // typically: \"triage\" | \"todo\" | \"doing\" | \"onhold\" | \"review\" | \"done\"\n /** Raw labels from the source (GitLab). Empty array if source doesn't support labels. */\n labels?: string[]\n /** Label name+color when the source returns colored labels (GitLab with_labels_details). */\n labelDetails?: Array<{ name: string; color: string; text_color?: string }>\n /** ISO timestamp of last update on the source. */\n updatedAt?: string\n /** Open/closed state from the source. */\n state?: \"opened\" | \"closed\"\n /** Optional milestone title (GitLab: issue.milestone.title). */\n milestone?: string\n /** GitLab assignees (0..n). `assignee` above is the agentId alias for assignees[0]. */\n assignees?: Array<{ username: string; name?: string; avatarUrl?: string }>\n}\n\n/** Member of a source (GitLab project members) — used for assignee pickers. */\nexport interface WorkMember {\n username: string\n name?: string\n avatarUrl?: string\n}\n\n/** Milestone on a source — used for milestone pickers. */\nexport interface WorkMilestone {\n id: number | string\n title: string\n state?: \"active\" | \"closed\"\n dueDate?: string\n}\n\n/** Full issue detail — a superset of WorkItem for the detail view. */\nexport interface WorkDetail extends WorkItem {\n /** Raw body, may be markdown. */\n descriptionHtml?: string\n /** ISO timestamp of creation. */\n createdAt?: string\n /** Author (issue reporter). */\n author?: { username: string; name?: string; avatarUrl?: string }\n /** Full labels list (all of them). */\n allLabels?: string[]\n}\n\nexport interface WorkReport {\n status: \"in-progress\" | \"done\" | \"blocked\"\n note?: string\n timeSeconds?: number\n blocker?: string\n}\n\n/** What a WorkSource can do beyond the minimal listOpen/claim/report triple. */\nexport interface WorkSourceCapabilities {\n /** Enumerate items beyond assignee filter — required for a board view. */\n listAll: boolean\n /** Create new items (e.g. POST GitLab issue). */\n create: boolean\n /** Arbitrary column transitions (label add/remove pair). */\n transition: boolean\n /** Surfaces labels[] on returned items. */\n labels: boolean\n /** Server-side text search. */\n search: boolean\n}\n\nexport interface WorkCreateInput {\n title: string\n description?: string\n /** agentId */\n assignee?: string\n /** Raw GitLab usernames — bypass agentId resolution for UI-driven creates. */\n assigneeUsernames?: string[]\n labels?: string[]\n /** Milestone title (resolved to id). */\n milestoneTitle?: string\n /** For sources that span multiple projects (gitlab): \"group/project\". */\n projectHint?: string\n}\n\nexport interface ListAllOpts {\n sinceDays?: number\n labels?: string[]\n /** Exclude items carrying any of these labels (client-side filter). */\n notLabels?: string[]\n /** Exclude items whose labels carry this scoped prefix (e.g. \"Status\"). */\n withoutScopedPrefix?: string\n search?: string\n assignee?: string\n /** GitLab issue state. Defaults to \"opened\" (boards hide closed issues). */\n state?: \"opened\" | \"closed\" | \"all\"\n /** Milestone filter (GitLab \"milestone\" query param). */\n milestone?: string\n}\n\nexport interface WorkSource {\n type: string\n capabilities: WorkSourceCapabilities\n listOpen(agentId: string): Promise<WorkItem[]>\n claim(agentId: string, itemId: string): Promise<void>\n report(itemId: string, update: WorkReport): Promise<void>\n /** Optional — gated by capabilities.listAll. */\n listAll?(opts: ListAllOpts): Promise<WorkItem[]>\n /** Optional — gated by capabilities.create. */\n create?(input: WorkCreateInput): Promise<WorkItem>\n /**\n * Optional — gated by capabilities.transition. Add `toLabel` and remove\n * `fromLabel` (if provided) from the item. Used by the kanban board on drag.\n */\n transition?(itemId: string, toLabel: string, fromLabel?: string): Promise<void>\n}\n\n// ---------- BacklogWorkSource: GFM checklist file ----------\n// Format in .agentx/backlog.md (or configured path):\n//\n// - [ ] @alice Implement login form [time: 2h]\n// - [x] @alice Write unit tests (done)\n// - [ ] @bob Design pricing page [time: 1h30m]\n//\n// Line number is the stable id.\n\nconst CHECK_RE = /^(\\s*)-\\s*\\[( |x|X)\\]\\s*(.*)$/\nconst MENTION_RE = /@([a-z0-9_-]+)/i\nconst TIME_RE = /\\[time:\\s*([0-9hm\\s]+)\\]/i\n\nfunction parseDuration(s: string): number {\n // \"2h30m\" -> 9000 s. \"1h\" -> 3600. \"45m\" -> 2700.\n const hMatch = s.match(/(\\d+)\\s*h/i)\n const mMatch = s.match(/(\\d+)\\s*m/i)\n return (hMatch ? parseInt(hMatch[1], 10) * 3600 : 0) + (mMatch ? parseInt(mMatch[1], 10) * 60 : 0)\n}\n\nexport class BacklogWorkSource implements WorkSource {\n type = \"backlog\"\n capabilities: WorkSourceCapabilities = {\n listAll: true, create: false, transition: false, labels: true, search: false,\n }\n private store: BacklogStore\n\n constructor(\n private path: string,\n private daemon?: DaemonConfig,\n private log: (...args: unknown[]) => void = () => {},\n ) {\n this.store = new BacklogStore(this.path)\n }\n\n /** Expose the store so the CLI and sync-back can share state. */\n getStore(): BacklogStore {\n return this.store\n }\n\n private readMd(): string[] {\n const full = resolve(process.cwd(), this.path)\n if (!existsSync(full)) return []\n return readFileSync(full, \"utf-8\").split(\"\\n\")\n }\n\n private writeMd(lines: string[]): void {\n const full = resolve(process.cwd(), this.path)\n writeFileSync(full, lines.join(\"\\n\"))\n }\n\n async listOpen(agentId: string): Promise<WorkItem[]> {\n if (this.store.exists()) {\n const items = this.store.list().filter((it) =>\n it.status !== \"done\" &&\n (!it.assignee || it.assignee.toLowerCase() === agentId.toLowerCase()),\n )\n return items.map((it) => this.toWorkItem(it))\n }\n // Legacy markdown-checklist fallback (unchanged behavior).\n const lines = this.readMd()\n const items: WorkItem[] = []\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].match(CHECK_RE)\n if (!m) continue\n const checked = m[2].toLowerCase() === \"x\"\n if (checked) continue\n const body = m[3]\n const mention = body.match(MENTION_RE)\n if (mention && mention[1].toLowerCase() !== agentId.toLowerCase()) continue\n const timeMatch = body.match(TIME_RE)\n const title = body.replace(MENTION_RE, \"\").replace(TIME_RE, \"\").trim()\n items.push({\n id: `backlog:${i}`,\n title: title || `Task at line ${i + 1}`,\n assignee: mention ? mention[1] : undefined,\n estimatedSeconds: timeMatch ? parseDuration(timeMatch[1]) : undefined,\n priority: i,\n })\n }\n return items\n }\n\n async listAll(_opts: ListAllOpts = {}): Promise<WorkItem[]> {\n if (!this.store.exists()) return []\n return this.store.list().map((it) => this.toWorkItem(it))\n }\n\n async claim(agentId: string, itemId: string): Promise<void> {\n if (!this.store.exists()) return\n const item = this.store.findById(itemId)\n if (!item) return\n const updated = this.store.update(itemId, {\n assignee: item.assignee || agentId,\n status: \"doing\",\n })\n if (updated.source && this.daemon) {\n const usernames = resolveAssigneesForSource([updated.assignee || agentId], updated.source, this.daemon)\n void syncBacklogItemToSource(updated, {\n assigneeUsernames: usernames,\n addLabels: [\"Doing\"],\n removeLabels: [\"To Do\"],\n }, this.daemon, this.log).catch((e) =>\n this.log(`[backlog-sync] claim failed for ${itemId}: ${e?.message ?? e}`),\n )\n }\n }\n\n async report(itemId: string, update: WorkReport): Promise<void> {\n // Structured store path.\n if (this.store.exists() && this.store.findById(itemId)) {\n const patch: Partial<import(\"./backlog-store\").BacklogItem> = {}\n if (update.status === \"done\") patch.status = \"done\"\n else if (update.status === \"blocked\") {\n patch.status = \"blocked\"\n patch.blocker = update.blocker || update.note || undefined\n } else if (update.status === \"in-progress\") patch.status = \"doing\"\n if (update.timeSeconds) patch.estimatedSeconds = update.timeSeconds\n const item = this.store.update(itemId, patch)\n\n if (item.source && this.daemon) {\n const sourcePatch: BacklogPatch = {}\n if (update.status === \"done\") {\n sourcePatch.addLabels = [\"Done\"]\n sourcePatch.removeLabels = [\"Doing\"]\n } else if (update.status === \"blocked\") {\n sourcePatch.addLabels = [\"Blocked\"]\n }\n if (update.note) sourcePatch.note = update.note\n if (Object.keys(sourcePatch).length) {\n void syncBacklogItemToSource(item, sourcePatch, this.daemon, this.log).catch((e) =>\n this.log(`[backlog-sync] report failed for ${itemId}: ${e?.message ?? e}`),\n )\n }\n }\n return\n }\n\n // Legacy markdown path (line-number ids).\n if (!itemId.startsWith(\"backlog:\")) throw new Error(`not a backlog id: ${itemId}`)\n const lineNo = parseInt(itemId.slice(\"backlog:\".length), 10)\n const lines = this.readMd()\n const line = lines[lineNo]\n if (!line) throw new Error(`backlog line ${lineNo} missing`)\n const m = line.match(CHECK_RE)\n if (!m) throw new Error(`backlog line ${lineNo} not a checkbox`)\n\n if (update.status === \"done\") {\n lines[lineNo] = line.replace(/- \\[ \\]/, \"- [x]\")\n const note = update.timeSeconds\n ? ` <!-- done by agent, ${Math.round(update.timeSeconds / 60)}m -->`\n : \" <!-- done by agent -->\"\n lines.splice(lineNo + 1, 0, note)\n } else if (update.status === \"blocked\") {\n lines.splice(lineNo + 1, 0, ` <!-- BLOCKED: ${update.blocker || update.note || \"?\"} -->`)\n } else if (update.note) {\n lines.splice(lineNo + 1, 0, ` <!-- ${update.note.replace(/-->/g, \"— >\")} -->`)\n }\n this.writeMd(lines)\n }\n\n private toWorkItem(it: import(\"./backlog-store\").BacklogItem): WorkItem {\n const stage = ({ todo: \"todo\", doing: \"doing\", blocked: \"onhold\", done: \"done\" } as const)[it.status]\n return {\n id: it.id,\n title: it.title,\n description: it.description,\n assignee: it.assignee,\n estimatedSeconds: it.estimatedSeconds,\n priority: it.priority,\n labels: it.labels,\n stage,\n milestone: it.milestone,\n url: it.source?.url,\n updatedAt: it.updatedAt,\n state: it.status === \"done\" ? \"closed\" : \"opened\",\n }\n }\n}\n\n// ---------- WikiWorkSource: scan markdown files for @mention checkboxes ----------\n\nexport class WikiWorkSource implements WorkSource {\n type = \"wiki\"\n capabilities: WorkSourceCapabilities = {\n listAll: false, create: false, transition: false, labels: false, search: false,\n }\n constructor(private root: string, private glob: string) {}\n\n private walk(dir: string, out: string[]): void {\n if (!existsSync(dir)) return\n for (const entry of readdirSync(dir)) {\n const full = join(dir, entry)\n const st = statSync(full)\n if (st.isDirectory()) {\n this.walk(full, out)\n } else if (entry.endsWith(\".md\")) {\n out.push(full)\n }\n }\n }\n\n async listOpen(agentId: string): Promise<WorkItem[]> {\n const files: string[] = []\n this.walk(resolve(process.cwd(), this.root), files)\n const items: WorkItem[] = []\n for (const file of files) {\n const lines = readFileSync(file, \"utf-8\").split(\"\\n\")\n for (let i = 0; i < lines.length; i++) {\n const m = lines[i].match(CHECK_RE)\n if (!m || m[2].toLowerCase() === \"x\") continue\n const mention = m[3].match(MENTION_RE)\n if (!mention || mention[1].toLowerCase() !== agentId.toLowerCase()) continue\n items.push({\n id: `wiki:${file}:${i}`,\n title: m[3].replace(MENTION_RE, \"\").trim(),\n assignee: mention[1],\n priority: i,\n })\n }\n }\n return items\n }\n\n async claim(): Promise<void> { /* no-op */ }\n\n async report(itemId: string, update: WorkReport): Promise<void> {\n // wiki:<file>:<line>\n const parts = itemId.split(\":\")\n const lineNo = parseInt(parts[parts.length - 1], 10)\n const file = parts.slice(1, -1).join(\":\")\n if (!existsSync(file)) return\n const lines = readFileSync(file, \"utf-8\").split(\"\\n\")\n if (update.status === \"done\" && lines[lineNo]) {\n lines[lineNo] = lines[lineNo].replace(/- \\[ \\]/, \"- [x]\")\n writeFileSync(file, lines.join(\"\\n\"))\n }\n }\n}\n\n// ---------- GitLabWorkSource: assignee-filtered issues via GitLab API ----------\n\nexport class GitLabWorkSource implements WorkSource {\n type = \"gitlab\"\n capabilities: WorkSourceCapabilities = {\n listAll: true, create: true, transition: true, labels: true, search: true,\n }\n\n constructor(\n private host: string,\n private token: string,\n private projects: string[],\n /** Map from agentId -> GitLab username(s) */\n private agentUsernames: Record<string, string[]>,\n private log: (...args: unknown[]) => void,\n ) {}\n\n /** Reverse lookup: GitLab username -> agentId. Used when resolving board items\n * back to an agent (for the reconciler and card display). */\n private usernameToAgent(username: string): string | undefined {\n for (const [agentId, names] of Object.entries(this.agentUsernames)) {\n if (names.some((n) => n.toLowerCase() === username.toLowerCase())) return agentId\n }\n return undefined\n }\n\n private mapIssue(issue: any, project: string): WorkItem {\n const ns = issue.references?.full?.split(\"#\")[0] || project\n // `labels` may be a string[] or, with with_labels_details=true, an object[].\n let labels: string[] = []\n let labelDetails: Array<{ name: string; color: string; text_color?: string }> | undefined\n if (Array.isArray(issue.labels)) {\n if (issue.labels.length && typeof issue.labels[0] === \"object\") {\n labelDetails = issue.labels.map((l: any) => ({\n name: l.name, color: l.color, text_color: l.text_color,\n }))\n labels = labelDetails!.map((l) => l.name)\n } else {\n labels = issue.labels\n }\n }\n const rawAssignees: any[] = Array.isArray(issue.assignees) && issue.assignees.length\n ? issue.assignees\n : (issue.assignee ? [issue.assignee] : [])\n const assignees = rawAssignees.map((a) => ({\n username: a.username, name: a.name, avatarUrl: a.avatar_url,\n }))\n const firstUsername = assignees[0]?.username\n return {\n id: `gitlab:${ns}:issue:${issue.iid}`,\n title: issue.title,\n description: issue.description || undefined,\n // Prefer configured agentId mapping; fall back to the raw GitLab username\n // so cards still render an assignee even without agentMappings configured.\n assignee: firstUsername ? (this.usernameToAgent(firstUsername) || firstUsername) : undefined,\n assignees,\n url: issue.web_url,\n priority: issue.weight ?? 99,\n estimatedSeconds: issue.time_stats?.time_estimate || undefined,\n labels,\n labelDetails,\n updatedAt: issue.updated_at,\n state: issue.state === \"closed\" ? \"closed\" : \"opened\",\n milestone: issue.milestone?.title || undefined,\n }\n }\n\n private async api(path: string, init: RequestInit = {}): Promise<any> {\n const res = await fetch(`${this.host}/api/v4${path}`, {\n ...init,\n headers: {\n \"PRIVATE-TOKEN\": this.token,\n \"Content-Type\": \"application/json\",\n ...(init.headers || {}),\n },\n })\n if (!res.ok) throw new Error(`GitLab ${res.status}: ${await res.text().catch(() => \"\")}`)\n return res.json()\n }\n\n async listOpen(agentId: string): Promise<WorkItem[]> {\n const usernames = this.agentUsernames[agentId] || []\n if (!usernames.length) return []\n\n const items: WorkItem[] = []\n const projectList = this.projects.length\n ? this.projects\n : [\"__ALL__\"] // falls back to a global issues query\n\n for (const username of usernames) {\n for (const project of projectList) {\n try {\n const q = `assignee_username=${encodeURIComponent(username)}&state=opened&scope=all&per_page=20`\n const path = project === \"__ALL__\"\n ? `/issues?${q}`\n : `/projects/${encodeURIComponent(project)}/issues?${q}`\n const issues = await this.api(path)\n for (const issue of issues) {\n const item = this.mapIssue(issue, project)\n // Force assignee to the agent we queried for (listOpen is per-agent).\n item.assignee = agentId\n items.push(item)\n }\n } catch (e: any) {\n this.log(`[business] GitLab listOpen failed for ${username} on ${project}: ${e.message}`)\n }\n }\n }\n return items\n }\n\n async claim(agentId: string, itemId: string): Promise<void> {\n // itemId: \"gitlab:<project>:issue:<iid>\"\n const { project, iid, type } = this.parseId(itemId)\n // Transition labels: add \"Doing\", remove \"To Do\"\n const path = `/projects/${encodeURIComponent(project)}/${type === \"issue\" ? \"issues\" : \"merge_requests\"}/${iid}?add_labels=Doing&remove_labels=To Do`\n try {\n await this.api(path, { method: \"PUT\" })\n } catch (e: any) {\n this.log(`[business] claim label update failed: ${e.message}`)\n }\n }\n\n async report(itemId: string, update: WorkReport): Promise<void> {\n const { project, iid, type } = this.parseId(itemId)\n const seg = type === \"issue\" ? \"issues\" : \"merge_requests\"\n\n // Time tracking\n if (update.timeSeconds && update.timeSeconds > 0) {\n const mins = Math.max(1, Math.round(update.timeSeconds / 60))\n const dur = mins >= 60 ? `${Math.floor(mins / 60)}h${mins % 60 ? ` ${mins % 60}m` : \"\"}` : `${mins}m`\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}/add_spent_time?duration=${encodeURIComponent(dur)}`,\n { method: \"POST\" },\n )\n } catch (e: any) {\n this.log(`[business] add_spent_time failed: ${e.message}`)\n }\n }\n\n // Status → labels\n if (update.status === \"done\") {\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?add_labels=Done&remove_labels=Doing`,\n { method: \"PUT\" },\n )\n } catch (e: any) {\n this.log(`[business] done label update failed: ${e.message}`)\n }\n } else if (update.status === \"blocked\") {\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?add_labels=Blocked`,\n { method: \"PUT\" },\n )\n } catch (e: any) {\n this.log(`[business] blocked label update failed: ${e.message}`)\n }\n }\n\n // Note/comment\n if (update.note) {\n try {\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}/notes`,\n { method: \"POST\", body: JSON.stringify({ body: update.note }) },\n )\n } catch (e: any) {\n this.log(`[business] report note failed: ${e.message}`)\n }\n }\n }\n\n async listAll(opts: ListAllOpts = {}): Promise<WorkItem[]> {\n const sinceDays = opts.sinceDays ?? 30\n const updatedAfter = new Date(Date.now() - sinceDays * 24 * 3600_000).toISOString()\n\n const params = new URLSearchParams()\n const state = opts.state ?? \"opened\"\n params.set(\"state\", state)\n // `updated_after` makes sense for the Closed column (bounded window) and for\n // keeping the open list recent; for \"opened\" we pass it too but GitLab treats\n // the window loosely (any issue updated since counts, regardless of open-age).\n params.set(\"updated_after\", updatedAfter)\n params.set(\"scope\", \"all\")\n params.set(\"per_page\", \"100\")\n params.set(\"with_labels_details\", \"true\")\n params.set(\"order_by\", \"updated_at\")\n params.set(\"sort\", \"desc\")\n if (opts.labels?.length) params.set(\"labels\", opts.labels.join(\",\"))\n if (opts.milestone) params.set(\"milestone\", opts.milestone)\n if (opts.search) params.set(\"search\", opts.search)\n\n const projectList = this.projects.length ? this.projects : [\"__ALL__\"]\n const items: WorkItem[] = []\n for (const project of projectList) {\n try {\n const path = project === \"__ALL__\"\n ? `/issues?${params.toString()}`\n : `/projects/${encodeURIComponent(project)}/issues?${params.toString()}`\n const issues = await this.api(path)\n for (const issue of issues) items.push(this.mapIssue(issue, project))\n } catch (e: any) {\n this.log(`[business] GitLab listAll failed on ${project}: ${e.message}`)\n }\n }\n\n // Client-side negative-label filter (GitLab API supports `not[labels]` only\n // via the `not[]` nested query form, which isn't convenient via URLSearchParams).\n let result = items\n if (opts.notLabels?.length) {\n const deny = new Set(opts.notLabels)\n result = result.filter((i) => !(i.labels || []).some((l) => deny.has(l)))\n }\n if (opts.withoutScopedPrefix) {\n const prefix = opts.withoutScopedPrefix + \"::\"\n result = result.filter((i) => !(i.labels || []).some((l) => l.startsWith(prefix)))\n }\n return result\n }\n\n /** Close or reopen an issue via state_event. */\n async setState(itemId: string, to: \"close\" | \"reopen\"): Promise<void> {\n const { project, iid, type } = this.parseId(itemId)\n const seg = type === \"issue\" ? \"issues\" : \"merge_requests\"\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?state_event=${to}`,\n { method: \"PUT\" },\n )\n }\n\n /** Fetch a single issue's full detail. */\n async getItem(itemId: string): Promise<WorkDetail | null> {\n const { project, iid } = this.parseId(itemId)\n try {\n const issue = await this.api(\n `/projects/${encodeURIComponent(project)}/issues/${iid}?with_labels_details=true`,\n )\n const base = this.mapIssue(issue, project)\n return {\n ...base,\n descriptionHtml: issue.description_html || undefined,\n createdAt: issue.created_at,\n author: issue.author ? {\n username: issue.author.username, name: issue.author.name, avatarUrl: issue.author.avatar_url,\n } : undefined,\n allLabels: Array.isArray(issue.labels)\n ? (typeof issue.labels[0] === \"object\" ? issue.labels.map((l: any) => l.name) : issue.labels)\n : [],\n }\n } catch (e: any) {\n this.log(`[board] GitLab getItem failed for ${itemId}: ${e.message}`)\n return null\n }\n }\n\n /** Update fields on an issue. Accepts string usernames for assignees. */\n async updateItem(\n itemId: string,\n patch: {\n title?: string\n description?: string\n labels?: string[]\n addLabels?: string[]\n removeLabels?: string[]\n assigneeUsernames?: string[]\n milestoneTitle?: string | null\n },\n ): Promise<WorkItem> {\n const { project, iid } = this.parseId(itemId)\n\n // Resolve assignee usernames → user ids (parallel).\n let assigneeIds: number[] | undefined\n if (patch.assigneeUsernames) {\n const results = await Promise.all(patch.assigneeUsernames.map(async (u) => {\n try {\n const users = await this.api(`/users?username=${encodeURIComponent(u)}`)\n return Array.isArray(users) && users[0]?.id ? users[0].id : null\n } catch { return null }\n }))\n assigneeIds = results.filter((x): x is number => typeof x === \"number\")\n }\n\n // Resolve milestone title → id (null clears the milestone).\n let milestoneId: number | 0 | undefined\n if (patch.milestoneTitle !== undefined) {\n if (patch.milestoneTitle === null || patch.milestoneTitle === \"\") {\n milestoneId = 0\n } else {\n try {\n const ms = await this.api(\n `/projects/${encodeURIComponent(project)}/milestones?title=${encodeURIComponent(patch.milestoneTitle)}`,\n )\n if (Array.isArray(ms) && ms[0]?.id) milestoneId = ms[0].id\n } catch (e: any) {\n this.log(`[board] milestone lookup failed for \"${patch.milestoneTitle}\": ${e.message}`)\n }\n }\n }\n\n const body: Record<string, unknown> = {}\n if (patch.title !== undefined) body.title = patch.title\n if (patch.description !== undefined) body.description = patch.description\n if (patch.labels !== undefined) body.labels = patch.labels.join(\",\")\n if (patch.addLabels?.length) body.add_labels = patch.addLabels.join(\",\")\n if (patch.removeLabels?.length) body.remove_labels = patch.removeLabels.join(\",\")\n if (assigneeIds !== undefined) body.assignee_ids = assigneeIds\n if (milestoneId !== undefined) body.milestone_id = milestoneId\n\n const issue = await this.api(`/projects/${encodeURIComponent(project)}/issues/${iid}`, {\n method: \"PUT\",\n body: JSON.stringify(body),\n })\n return this.mapIssue(issue, project)\n }\n\n /** List project members (direct + inherited). Deduped by username across projects. */\n async listMembers(): Promise<WorkMember[]> {\n const byUsername = new Map<string, WorkMember>()\n for (const project of this.projects) {\n try {\n const members = await this.api(\n `/projects/${encodeURIComponent(project)}/members/all?per_page=100`,\n )\n for (const m of members) {\n if (m.username && !byUsername.has(m.username)) {\n byUsername.set(m.username, {\n username: m.username, name: m.name, avatarUrl: m.avatar_url,\n })\n }\n }\n } catch (e: any) {\n this.log(`[board] listMembers failed for ${project}: ${e.message}`)\n }\n }\n return [...byUsername.values()].sort((a, b) => a.username.localeCompare(b.username))\n }\n\n /** List milestones (active first, then closed). Deduped by title across projects. */\n async listMilestones(): Promise<WorkMilestone[]> {\n const byTitle = new Map<string, WorkMilestone>()\n for (const project of this.projects) {\n try {\n const ms = await this.api(\n `/projects/${encodeURIComponent(project)}/milestones?per_page=100`,\n )\n for (const m of ms) {\n if (m.title && !byTitle.has(m.title)) {\n byTitle.set(m.title, {\n id: m.id, title: m.title, state: m.state, dueDate: m.due_date,\n })\n }\n }\n } catch (e: any) {\n this.log(`[board] listMilestones failed for ${project}: ${e.message}`)\n }\n }\n const all = [...byTitle.values()]\n all.sort((a, b) => {\n if (a.state !== b.state) return a.state === \"active\" ? -1 : 1\n return a.title.localeCompare(b.title)\n })\n return all\n }\n\n async create(input: WorkCreateInput): Promise<WorkItem> {\n const project = input.projectHint || this.projects[0]\n if (!project) throw new Error(\"GitLabWorkSource.create requires projectHint or configured projects\")\n\n // Resolve assignee usernames → user ids. Prefer explicit `assigneeUsernames`,\n // fall back to agentId → configured mapping for legacy callers.\n let assignee_ids: number[] | undefined\n const usernames = input.assigneeUsernames?.length\n ? input.assigneeUsernames\n : (input.assignee ? (this.agentUsernames[input.assignee] || []).slice(0, 1) : [])\n if (usernames.length) {\n const results = await Promise.all(usernames.map(async (u) => {\n try {\n const users = await this.api(`/users?username=${encodeURIComponent(u)}`)\n return Array.isArray(users) && users[0]?.id ? users[0].id : null\n } catch (e: any) {\n this.log(`[board] user lookup failed for ${u}: ${e.message}`)\n return null\n }\n }))\n assignee_ids = results.filter((x): x is number => typeof x === \"number\")\n }\n\n // Resolve milestone title → id.\n let milestone_id: number | undefined\n if (input.milestoneTitle) {\n try {\n const ms = await this.api(\n `/projects/${encodeURIComponent(project)}/milestones?title=${encodeURIComponent(input.milestoneTitle)}`,\n )\n if (Array.isArray(ms) && ms[0]?.id) milestone_id = ms[0].id\n } catch (e: any) {\n this.log(`[board] milestone lookup failed for \"${input.milestoneTitle}\": ${e.message}`)\n }\n }\n\n const body: Record<string, unknown> = {\n title: input.title,\n description: input.description,\n labels: (input.labels || []).join(\",\"),\n }\n if (assignee_ids?.length) body.assignee_ids = assignee_ids\n if (milestone_id !== undefined) body.milestone_id = milestone_id\n\n const issue = await this.api(`/projects/${encodeURIComponent(project)}/issues`, {\n method: \"POST\",\n body: JSON.stringify(body),\n })\n return this.mapIssue(issue, project)\n }\n\n async transition(itemId: string, toLabel: string, fromLabel?: string): Promise<void> {\n // Legacy single add/remove — boards use transitionMany.\n await this.transitionMany(itemId, toLabel ? [toLabel] : [], fromLabel ? [fromLabel] : [])\n }\n\n /** Multi-label transition; used by board drag-drop (scoped labels produce pairs). */\n async transitionMany(itemId: string, add: string[], remove: string[]): Promise<void> {\n const { project, iid, type } = this.parseId(itemId)\n const seg = type === \"issue\" ? \"issues\" : \"merge_requests\"\n const params = new URLSearchParams()\n if (add.length) params.set(\"add_labels\", add.join(\",\"))\n if (remove.length) params.set(\"remove_labels\", remove.join(\",\"))\n if (!params.toString()) return\n await this.api(\n `/projects/${encodeURIComponent(project)}/${seg}/${iid}?${params.toString()}`,\n { method: \"PUT\" },\n )\n }\n\n private parseId(itemId: string): { project: string; type: \"issue\" | \"mr\"; iid: string } {\n // \"gitlab:<project>:issue:<iid>\" (project may contain a slash → it's joined)\n const body = itemId.replace(/^gitlab:/, \"\")\n const parts = body.split(\":\")\n const iid = parts.pop() as string\n const type = parts.pop() === \"mr\" ? \"mr\" : \"issue\"\n const project = parts.join(\":\")\n return { project, type, iid }\n }\n}\n\n// ---------- Factory ----------\n\nexport function createWorkSource(\n business: BusinessConfig,\n daemon: DaemonConfig,\n log: (...args: unknown[]) => void,\n): WorkSource {\n const ws: BusinessWorkSource = business.workSource\n switch (ws.type) {\n case \"backlog\":\n return new BacklogWorkSource(ws.path, daemon, log)\n\n case \"wiki\":\n return new WikiWorkSource(ws.path, ws.glob)\n\n case \"gitlab\": {\n const gl = daemon.channels.gitlab\n if (!gl?.token) {\n throw new Error(\"business.workSource.type=gitlab requires channels.gitlab.token\")\n }\n const agentUsernames: Record<string, string[]> = {}\n for (const mapping of gl.agentMappings) {\n if (mapping.gitlabUsernames.length) {\n agentUsernames[mapping.agentId] = mapping.gitlabUsernames\n }\n }\n const projects = ws.projects.length\n ? ws.projects\n : gl.routes.map((r) => r.project)\n return new GitLabWorkSource(gl.host, gl.token, projects, agentUsernames, log)\n }\n }\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"fs\"\nimport { dirname, resolve } from \"path\"\nimport { randomUUID } from \"crypto\"\nimport { z } from \"zod\"\n\n// --- Structured backlog store (Plan B + import) ---\n//\n// Replaces the legacy `.agentx/backlog.md` GFM checklist as the canonical\n// store when `.agentx/backlog.json` is present. The markdown view is\n// regenerated on every save so humans still get a readable diff.\n//\n// Why structured: importing issues from gitlab/github needs a stable\n// linkage back to the source (project + iid + url) so mutations\n// (assignee, labels, status, milestone) can be pushed upstream by the\n// sync-back layer. Encoding that in markdown comments is brittle.\n\nconst sourceSchema = z.object({\n type: z.enum([\"gitlab\", \"github\"]),\n host: z.string().optional(),\n project: z.string(),\n iid: z.number().int().positive(),\n url: z.string(),\n})\n\nconst itemSchema = z.object({\n id: z.string(),\n title: z.string(),\n description: z.string().optional(),\n assignee: z.string().optional(),\n labels: z.array(z.string()).default([]),\n milestone: z.string().optional(),\n status: z.enum([\"todo\", \"doing\", \"blocked\", \"done\"]).default(\"todo\"),\n priority: z.number().int().optional(),\n estimatedSeconds: z.number().int().optional(),\n source: sourceSchema.optional(),\n importedAt: z.string().optional(),\n createdAt: z.string(),\n updatedAt: z.string(),\n blocker: z.string().optional(),\n doneNote: z.string().optional(),\n})\n\nconst fileSchema = z.object({\n version: z.literal(1),\n items: z.array(itemSchema).default([]),\n})\n\nexport type BacklogSource = z.infer<typeof sourceSchema>\nexport type BacklogItem = z.infer<typeof itemSchema>\nexport type BacklogFile = z.infer<typeof fileSchema>\n\nexport class BacklogStore {\n constructor(private path: string) {}\n\n /** Absolute path of the JSON store. */\n jsonPath(): string {\n return resolve(process.cwd(), this.path).replace(/\\.md$/i, \".json\")\n }\n\n /** Absolute path of the markdown view (alongside the JSON store). */\n mdPath(): string {\n return resolve(process.cwd(), this.path).replace(/\\.json$/i, \".md\")\n }\n\n exists(): boolean {\n return existsSync(this.jsonPath())\n }\n\n load(): BacklogFile {\n const p = this.jsonPath()\n if (!existsSync(p)) return { version: 1, items: [] }\n const raw = readFileSync(p, \"utf-8\")\n try {\n return fileSchema.parse(JSON.parse(raw))\n } catch (e: any) {\n throw new Error(`Backlog file invalid (${p}): ${e.message}`)\n }\n }\n\n save(file: BacklogFile): void {\n const jsonP = this.jsonPath()\n mkdirSync(dirname(jsonP), { recursive: true })\n writeFileSync(jsonP, JSON.stringify(file, null, 2) + \"\\n\")\n writeFileSync(this.mdPath(), this.toMarkdown(file))\n }\n\n list(): BacklogItem[] {\n return this.load().items\n }\n\n findById(id: string): BacklogItem | undefined {\n return this.list().find((i) => i.id === id)\n }\n\n /** Add an item; rejects duplicates by id. Returns the saved item. */\n add(input: Omit<BacklogItem, \"createdAt\" | \"updatedAt\"> & { createdAt?: string }): BacklogItem {\n const file = this.load()\n if (file.items.some((i) => i.id === input.id)) {\n throw new Error(`backlog item ${input.id} already exists`)\n }\n const now = new Date().toISOString()\n const item: BacklogItem = {\n ...input,\n labels: input.labels ?? [],\n status: input.status ?? \"todo\",\n createdAt: input.createdAt ?? now,\n updatedAt: now,\n }\n file.items.push(item)\n this.save(file)\n return item\n }\n\n /** Add many; skips items whose id already exists, returns the inserted set. */\n addMany(inputs: Array<Omit<BacklogItem, \"createdAt\" | \"updatedAt\"> & { createdAt?: string }>): BacklogItem[] {\n const file = this.load()\n const existing = new Set(file.items.map((i) => i.id))\n const now = new Date().toISOString()\n const added: BacklogItem[] = []\n for (const input of inputs) {\n if (existing.has(input.id)) continue\n const item: BacklogItem = {\n ...input,\n labels: input.labels ?? [],\n status: input.status ?? \"todo\",\n createdAt: input.createdAt ?? now,\n updatedAt: now,\n }\n file.items.push(item)\n added.push(item)\n existing.add(item.id)\n }\n if (added.length) this.save(file)\n return added\n }\n\n update(id: string, patch: Partial<BacklogItem>): BacklogItem {\n const file = this.load()\n const idx = file.items.findIndex((i) => i.id === id)\n if (idx < 0) throw new Error(`backlog item ${id} not found`)\n const updated: BacklogItem = {\n ...file.items[idx],\n ...patch,\n id: file.items[idx].id,\n createdAt: file.items[idx].createdAt,\n updatedAt: new Date().toISOString(),\n }\n file.items[idx] = updated\n this.save(file)\n return updated\n }\n\n remove(id: string): boolean {\n const file = this.load()\n const before = file.items.length\n file.items = file.items.filter((i) => i.id !== id)\n if (file.items.length === before) return false\n this.save(file)\n return true\n }\n\n /** Render a human-readable markdown view (generated, not authoritative). */\n toMarkdown(file: BacklogFile): string {\n const lines: string[] = [\n \"# Backlog\",\n \"\",\n \"_Generated from `backlog.json`. Do not edit by hand — use `agentx backlog` commands._\",\n \"\",\n ]\n const buckets: Record<BacklogItem[\"status\"], BacklogItem[]> = {\n doing: [], blocked: [], todo: [], done: [],\n }\n for (const item of file.items) buckets[item.status].push(item)\n const sections: Array<[BacklogItem[\"status\"], string]> = [\n [\"doing\", \"Doing\"], [\"blocked\", \"Blocked\"], [\"todo\", \"To Do\"], [\"done\", \"Done\"],\n ]\n for (const [key, label] of sections) {\n if (!buckets[key].length) continue\n lines.push(`## ${label}`, \"\")\n for (const it of buckets[key]) {\n const check = key === \"done\" ? \"x\" : \" \"\n const mention = it.assignee ? `@${it.assignee} ` : \"\"\n const time = it.estimatedSeconds\n ? ` [time: ${Math.round(it.estimatedSeconds / 60)}m]`\n : \"\"\n const src = it.source ? ` (${it.source.type}: ${it.source.project}#${it.source.iid})` : \"\"\n lines.push(`- [${check}] ${mention}${it.title}${time}${src}`)\n if (it.source?.url) lines.push(` - <${it.source.url}>`)\n if (it.labels.length) lines.push(` - labels: ${it.labels.join(\", \")}`)\n if (it.milestone) lines.push(` - milestone: ${it.milestone}`)\n if (it.blocker) lines.push(` - BLOCKED: ${it.blocker}`)\n }\n lines.push(\"\")\n }\n return lines.join(\"\\n\")\n }\n}\n\n/** Generate a stable BacklogItem.id for a fresh manual item. */\nexport function manualBacklogId(): string {\n return `manual:${randomUUID()}`\n}\n\n/** Build the canonical id for an imported source issue. */\nexport function sourceBacklogId(source: BacklogSource): string {\n return `${source.type}:${source.project}:${source.iid}`\n}\n","import type { BacklogItem, BacklogSource } from \"./backlog-store\"\nimport type { DaemonConfig } from \"@/daemon/config\"\n\n// --- Sync-back layer: backlog item mutations → upstream issue ---\n//\n// When a backlog item carries a `source` ref (gitlab or github), changes\n// applied via the backlog work-source need to flow back to the original\n// issue: assignees, labels, title, description, milestone, open/close.\n// Otherwise the imported view drifts from upstream and humans browsing\n// the source see stale state.\n//\n// This module is intentionally stateless — callers pass the patch they\n// just applied to the local item; we forward the same shape to the\n// matching upstream API.\n\nexport interface BacklogPatch {\n title?: string\n description?: string\n /** Replace labels wholesale. Use addLabels/removeLabels for incremental. */\n labels?: string[]\n addLabels?: string[]\n removeLabels?: string[]\n /** Username on the source (NOT agentId — caller resolves the mapping). */\n assigneeUsernames?: string[]\n /** Title of the milestone, or empty string to clear. */\n milestoneTitle?: string | null\n /** Open/close transition. */\n state?: \"open\" | \"closed\"\n /** Optional comment to post on the upstream issue. */\n note?: string\n}\n\nexport interface SyncBackResult {\n ok: boolean\n url?: string\n error?: string\n /** Each upstream call we attempted, in order. */\n attempts: Array<{ kind: string; ok: boolean; status?: number; error?: string }>\n}\n\n/** Resolve agentIds in a patch to upstream usernames using the daemon mappings. */\nexport function resolveAssigneesForSource(\n agentIds: string[],\n source: BacklogSource,\n daemon: DaemonConfig,\n): string[] {\n const out: string[] = []\n if (source.type === \"gitlab\") {\n const mappings = daemon.channels.gitlab?.agentMappings ?? []\n for (const agentId of agentIds) {\n const m = mappings.find((x) => x.agentId === agentId)\n if (m?.gitlabUsernames?.length) out.push(m.gitlabUsernames[0])\n else out.push(agentId)\n }\n } else if (source.type === \"github\") {\n const mappings = daemon.channels.github?.agentMappings ?? []\n for (const agentId of agentIds) {\n const m = mappings.find((x) => x.agentId === agentId)\n if (m?.githubUsernames?.length) out.push(m.githubUsernames[0])\n else out.push(agentId)\n }\n }\n return out\n}\n\n/** Push a backlog patch to the upstream source referenced by the item. */\nexport async function syncBacklogItemToSource(\n item: BacklogItem,\n patch: BacklogPatch,\n daemon: DaemonConfig,\n log: (...args: unknown[]) => void = () => {},\n): Promise<SyncBackResult> {\n if (!item.source) return { ok: true, attempts: [] }\n if (item.source.type === \"gitlab\") return syncToGitLab(item.source, patch, daemon, log)\n if (item.source.type === \"github\") return syncToGitHub(item.source, patch, daemon, log)\n return { ok: false, error: `unknown source.type ${(item.source as any).type}`, attempts: [] }\n}\n\n// ---------- GitLab ----------\n\nasync function syncToGitLab(\n source: BacklogSource,\n patch: BacklogPatch,\n daemon: DaemonConfig,\n log: (...args: unknown[]) => void,\n): Promise<SyncBackResult> {\n const gl = daemon.channels.gitlab\n const token = gl?.token\n const host = gl?.host || (source.host ? `https://${source.host}` : undefined)\n if (!token || !host) {\n return { ok: false, error: \"gitlab token/host not configured\", attempts: [] }\n }\n const attempts: SyncBackResult[\"attempts\"] = []\n\n const api = async (path: string, init: RequestInit = {}): Promise<Response> => {\n return fetch(`${host}/api/v4${path}`, {\n ...init,\n headers: {\n \"PRIVATE-TOKEN\": token,\n \"Content-Type\": \"application/json\",\n ...(init.headers || {}),\n },\n })\n }\n\n const projectEnc = encodeURIComponent(source.project)\n\n // Resolve assignee usernames → ids (parallel) — only when we need them.\n let assigneeIds: number[] | undefined\n if (patch.assigneeUsernames !== undefined) {\n const ids = await Promise.all(patch.assigneeUsernames.map(async (u) => {\n try {\n const res = await fetch(`${host}/api/v4/users?username=${encodeURIComponent(u)}`, {\n headers: { \"PRIVATE-TOKEN\": token },\n })\n if (!res.ok) return null\n const arr: any = await res.json()\n return Array.isArray(arr) && arr[0]?.id ? arr[0].id : null\n } catch { return null }\n }))\n assigneeIds = ids.filter((x): x is number => typeof x === \"number\")\n }\n\n // Resolve milestone title → id.\n let milestoneId: number | 0 | undefined\n if (patch.milestoneTitle !== undefined) {\n if (patch.milestoneTitle === null || patch.milestoneTitle === \"\") {\n milestoneId = 0\n } else {\n try {\n const res = await api(`/projects/${projectEnc}/milestones?title=${encodeURIComponent(patch.milestoneTitle)}`)\n if (res.ok) {\n const arr: any = await res.json()\n if (Array.isArray(arr) && arr[0]?.id) milestoneId = arr[0].id\n }\n } catch (e: any) {\n log(`[backlog-sync] milestone lookup failed: ${e.message}`)\n }\n }\n }\n\n // Build the PUT body.\n const body: Record<string, unknown> = {}\n if (patch.title !== undefined) body.title = patch.title\n if (patch.description !== undefined) body.description = patch.description\n if (patch.labels !== undefined) body.labels = patch.labels.join(\",\")\n if (patch.addLabels?.length) body.add_labels = patch.addLabels.join(\",\")\n if (patch.removeLabels?.length) body.remove_labels = patch.removeLabels.join(\",\")\n if (assigneeIds !== undefined) body.assignee_ids = assigneeIds\n if (milestoneId !== undefined) body.milestone_id = milestoneId\n if (patch.state) body.state_event = patch.state === \"closed\" ? \"close\" : \"reopen\"\n\n if (Object.keys(body).length) {\n try {\n const res = await api(`/projects/${projectEnc}/issues/${source.iid}`, {\n method: \"PUT\",\n body: JSON.stringify(body),\n })\n attempts.push({ kind: \"PUT issue\", ok: res.ok, status: res.status })\n if (!res.ok) {\n const text = await res.text().catch(() => \"\")\n return { ok: false, attempts, error: `PUT issue ${res.status}: ${text.slice(0, 200)}`, url: source.url }\n }\n } catch (e: any) {\n attempts.push({ kind: \"PUT issue\", ok: false, error: e.message })\n return { ok: false, attempts, error: e.message, url: source.url }\n }\n }\n\n if (patch.note) {\n try {\n const res = await api(`/projects/${projectEnc}/issues/${source.iid}/notes`, {\n method: \"POST\",\n body: JSON.stringify({ body: patch.note }),\n })\n attempts.push({ kind: \"POST note\", ok: res.ok, status: res.status })\n } catch (e: any) {\n attempts.push({ kind: \"POST note\", ok: false, error: e.message })\n }\n }\n\n return { ok: attempts.every((a) => a.ok), attempts, url: source.url }\n}\n\n// ---------- GitHub ----------\n\nasync function syncToGitHub(\n source: BacklogSource,\n patch: BacklogPatch,\n daemon: DaemonConfig,\n log: (...args: unknown[]) => void,\n): Promise<SyncBackResult> {\n const gh = daemon.channels.github\n const token = gh?.token\n if (!token) {\n return { ok: false, error: \"github token not configured\", attempts: [] }\n }\n const attempts: SyncBackResult[\"attempts\"] = []\n\n const api = async (path: string, init: RequestInit = {}): Promise<Response> => {\n return fetch(`https://api.github.com${path}`, {\n ...init,\n headers: {\n \"Authorization\": `Bearer ${token}`,\n \"Accept\": \"application/vnd.github+json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n ...(init.headers || {}),\n },\n })\n }\n\n const repo = source.project // GitHub IDs use \"owner/repo\" verbatim in the URL path.\n\n // Resolve milestone title → number.\n let milestoneNumber: number | null | undefined\n if (patch.milestoneTitle !== undefined) {\n if (patch.milestoneTitle === null || patch.milestoneTitle === \"\") {\n milestoneNumber = null\n } else {\n try {\n const res = await api(`/repos/${repo}/milestones?state=all&per_page=100`)\n if (res.ok) {\n const arr: any = await res.json()\n const found = Array.isArray(arr) ? arr.find((m: any) => m.title === patch.milestoneTitle) : null\n if (found?.number) milestoneNumber = found.number\n }\n } catch (e: any) {\n log(`[backlog-sync] github milestone lookup failed: ${e.message}`)\n }\n }\n }\n\n // GitHub doesn't support add/remove labels in the issue PATCH the way\n // GitLab does — labels is a wholesale set. If only addLabels/removeLabels\n // were provided we read+merge.\n let nextLabels: string[] | undefined\n if (patch.labels !== undefined) {\n nextLabels = patch.labels\n } else if (patch.addLabels?.length || patch.removeLabels?.length) {\n try {\n const res = await api(`/repos/${repo}/issues/${source.iid}`)\n if (res.ok) {\n const issue: any = await res.json()\n const current: string[] = Array.isArray(issue.labels)\n ? issue.labels.map((l: any) => (typeof l === \"string\" ? l : l.name))\n : []\n const adds = new Set([...current, ...(patch.addLabels ?? [])])\n for (const r of patch.removeLabels ?? []) adds.delete(r)\n nextLabels = [...adds]\n }\n } catch (e: any) {\n log(`[backlog-sync] github label fetch failed: ${e.message}`)\n }\n }\n\n const body: Record<string, unknown> = {}\n if (patch.title !== undefined) body.title = patch.title\n if (patch.description !== undefined) body.body = patch.description\n if (nextLabels !== undefined) body.labels = nextLabels\n if (patch.assigneeUsernames !== undefined) body.assignees = patch.assigneeUsernames\n if (milestoneNumber !== undefined) body.milestone = milestoneNumber\n if (patch.state) body.state = patch.state\n\n if (Object.keys(body).length) {\n try {\n const res = await api(`/repos/${repo}/issues/${source.iid}`, {\n method: \"PATCH\",\n body: JSON.stringify(body),\n })\n attempts.push({ kind: \"PATCH issue\", ok: res.ok, status: res.status })\n if (!res.ok) {\n const text = await res.text().catch(() => \"\")\n return { ok: false, attempts, error: `PATCH issue ${res.status}: ${text.slice(0, 200)}`, url: source.url }\n }\n } catch (e: any) {\n attempts.push({ kind: \"PATCH issue\", ok: false, error: e.message })\n return { ok: false, attempts, error: e.message, url: source.url }\n }\n }\n\n if (patch.note) {\n try {\n const res = await api(`/repos/${repo}/issues/${source.iid}/comments`, {\n method: \"POST\",\n body: JSON.stringify({ body: patch.note }),\n })\n attempts.push({ kind: \"POST comment\", ok: res.ok, status: res.status })\n } catch (e: any) {\n attempts.push({ kind: \"POST comment\", ok: false, error: e.message })\n }\n }\n\n return { ok: attempts.every((a) => a.ok), attempts, url: source.url }\n}\n","// --- Is the automation still running? -------------------------------------\n//\n// A workflow that stops firing produces nothing: no error, no run, no page\n// entry. On this fleet 16 workflows produced 759 runs and then went quiet for\n// three days without anyone noticing, which is the failure this module exists\n// to make visible. Health is therefore about ABSENCE first and failure second.\n//\n// Cost note: RunStore.get() replays a whole run file. Polling that for every\n// run on every dashboard refresh is not affordable, so we read each file's\n// first line (the snapshot, which carries workflowId) and last line (the\n// terminal status), and cache per file keyed on mtime. Runs are append-only,\n// so after the first scan almost nothing is re-read.\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\"\nimport { resolve } from \"path\"\n\nexport interface RunSummary { workflowId: string; status: string; at: number }\n\n/** Absence first: a workflow that used to fire and no longer does is the\n * headline, because nothing else in the product will ever mention it. */\nexport type WorkflowState = \"never\" | \"failing\" | \"dormant\" | \"active\" | \"quiet\"\n\nexport interface WorkflowHealth {\n id: string\n name: string\n state: WorkflowState\n /** Last run of any status, ms since epoch. Null when it has never run. */\n lastRunAt: number | null\n recent: number\n prior: number\n failed: number\n /** Runs parked at a checkpoint. These are waiting on a human, not broken. */\n paused: number\n}\n\nexport function workflowHealth(\n workflows: Array<{ id: string; name?: string }>,\n runs: RunSummary[],\n now = Date.now(),\n windowDays = 7,\n): WorkflowHealth[] {\n // One day in ms, inlined: workflowHealth is stringified and shipped to the\n // workflows page, where a module-scope constant would be unbound.\n const span = windowDays * 86_400_000\n const byId = new Map<string, RunSummary[]>()\n for (const r of runs) {\n const list = byId.get(r.workflowId)\n if (list) list.push(r)\n else byId.set(r.workflowId, [r])\n }\n return workflows.map(w => {\n const mine = byId.get(w.id) ?? []\n const recent = mine.filter(r => now - r.at <= span)\n const prior = mine.filter(r => now - r.at > span && now - r.at <= span * 2)\n const failed = recent.filter(r => r.status === \"failed\").length\n const paused = mine.filter(r => r.status === \"paused\").length\n const lastRunAt = mine.length ? Math.max(...mine.map(r => r.at)) : null\n // Order matters: a workflow that has stopped firing is reported as dormant\n // even when its last few runs failed — the silence is the bigger signal.\n const state: WorkflowState =\n !mine.length ? \"never\"\n : prior.length > 0 && recent.length === 0 ? \"dormant\"\n : recent.length > 0 && failed * 2 >= recent.length ? \"failing\"\n : recent.length > 0 ? \"active\"\n : \"quiet\"\n return { id: w.id, name: w.name || w.id, state, lastRunAt, recent: recent.length, prior: prior.length, failed, paused }\n }).sort((a, b) => {\n // Inlined rather than a module constant: workflowHealth is stringified\n // and shipped to the workflows page, where module scope does not travel.\n // A hoisted RANK only failed with two or more workflows, because Array\n // .sort never calls the comparator on a single-element list.\n const rank: Record<string, number> = { dormant: 0, failing: 1, active: 2, quiet: 3, never: 4 }\n return rank[a.state] - rank[b.state] || (b.lastRunAt ?? 0) - (a.lastRunAt ?? 0)\n })\n}\n\n\nconst cache = new Map<string, { mtime: number; summary: RunSummary | null }>()\n\n/** Read workflowId + terminal status per run, re-reading only changed files. */\nexport function scanRuns(runsDir: string, limit = 1000): RunSummary[] {\n if (!existsSync(runsDir)) return []\n const files = readdirSync(runsDir)\n .filter(f => f.endsWith(\".jsonl\"))\n .map(f => { const p = resolve(runsDir, f); return { path: p, mtime: statSync(p).mtimeMs } })\n .sort((a, b) => b.mtime - a.mtime)\n .slice(0, limit)\n const live = new Set(files.map(f => f.path))\n for (const key of cache.keys()) if (!live.has(key)) cache.delete(key)\n const out: RunSummary[] = []\n for (const { path, mtime } of files) {\n const hit = cache.get(path)\n if (hit && hit.mtime === mtime) { if (hit.summary) out.push(hit.summary); continue }\n const summary = readSummary(path, mtime)\n cache.set(path, { mtime, summary })\n if (summary) out.push(summary)\n }\n return out\n}\n\nfunction readSummary(path: string, mtime: number): RunSummary | null {\n try {\n const lines = readFileSync(path, \"utf-8\").split(\"\\n\").filter(Boolean)\n if (!lines.length) return null\n const head = JSON.parse(lines[0]) as { run?: { workflowId?: string; status?: string } }\n const workflowId = head.run?.workflowId\n if (!workflowId) return null\n // The last line is either a status-bearing exec entry or another snapshot;\n // fall back to the snapshot's own status when it carries neither.\n let status = head.run?.status ?? \"unknown\"\n for (let i = lines.length - 1; i >= 0; i--) {\n const tail = JSON.parse(lines[i]) as { status?: string; run?: { status?: string } }\n const s = tail.status ?? tail.run?.status\n if (s) { status = s; break }\n }\n return { workflowId, status, at: mtime }\n } catch { return null }\n}\n","// --- Who is the work for? -------------------------------------------------\n//\n// The principal behind a piece of work is a CLIENT. That concept already\n// existed, spread across `business.projects[].client`, `business.contactMap[]`\n// and the `orgChart` reportsTo chain, resolved privately inside the activity\n// graph panel. This module is the single implementation both that panel and\n// the session monitor call, plus the policy layer the monitor needs.\n//\n// Clients are DERIVED, never declared: one appears the moment a project or a\n// contact mentions it. `business.clients` only carries overrides — a display\n// name, whether it is a paying client or your own house, how fast a delay\n// costs you, and what agents may do for it without asking. A client with no\n// entry there still resolves; it just gets defaults.\n\nexport interface ContactRule {\n channel?: string\n chatId?: string\n username?: string\n senderId?: string\n client: string\n project?: string\n displayName?: string\n}\nexport interface ProjectRule { id: string; pm?: string; client?: string }\nexport interface ClientPolicy {\n name?: string\n kind?: \"client\" | \"internal\" | \"own\"\n respondWithin?: string\n standing?: string[]\n}\nexport interface BusinessShape {\n projects?: ProjectRule[]\n contactMap?: ContactRule[]\n orgChart?: Record<string, { reportsTo?: string }>\n clients?: Record<string, ClientPolicy>\n}\nexport interface ResolvedClient {\n id: string\n name: string\n kind: \"client\" | \"internal\" | \"own\"\n /** Minutes before a wait starts costing; undefined = no external clock. */\n respondWithinMinutes?: number\n standing: string[]\n /** True when nothing in config mentions this client — derived only. */\n declared: boolean\n}\n\nexport const UNMAPPED = \"unmapped\"\n\n/** Explicit `business.projects[].client` wins; otherwise the leading segment\n * of the id. Works for \"owner/repo\" on any forge and for bare internal ids. */\nexport function clientFromProject(project: string | null | undefined, projects: ProjectRule[] = []): string {\n if (!project) return UNMAPPED\n const hit = projects.find(p => p.id === project)\n if (hit?.client) return hit.client\n const slash = project.indexOf(\"/\")\n return slash > 0 ? project.slice(0, slash) : project\n}\n\n/** agent id -> client, from project PMs then transitively down reportsTo.\n * A mesh dispatch carries no project, so without this it is unattributable. */\nexport function agentClients(business: BusinessShape): Map<string, string> {\n const out = new Map<string, string>()\n const projects = business.projects ?? []\n const orgChart = business.orgChart ?? {}\n for (const p of projects) {\n if (p.pm) if (!out.has(p.pm)) out.set(p.pm, clientFromProject(p.id, projects))\n }\n const climb = (agentId: string, seen = new Set<string>()): string | undefined => {\n if (out.has(agentId)) return out.get(agentId)\n if (seen.has(agentId)) return undefined\n seen.add(agentId)\n const up = orgChart[agentId]?.reportsTo\n if (!up) return undefined\n const c = climb(up, seen)\n if (c) out.set(agentId, c)\n return c\n }\n for (const agentId of Object.keys(orgChart)) climb(agentId)\n return out\n}\n\n/** chatId > username > senderId > channel-default. First match wins. */\nexport function matchContact(\n channel: string | undefined,\n who: { chatId?: string; username?: string; senderId?: string },\n contactMap: ContactRule[] = [],\n): ContactRule | undefined {\n if (!contactMap.length) return undefined\n const pool = contactMap.filter(m => !m.channel || m.channel === channel)\n return pool.find(m => m.chatId && who.chatId && m.chatId === who.chatId)\n ?? pool.find(m => m.username && who.username && m.username === who.username)\n ?? pool.find(m => m.senderId && who.senderId && m.senderId === who.senderId)\n ?? pool.find(m => m.channel && !m.chatId && !m.username && !m.senderId)\n}\n\nexport interface WorkRef { agentId?: string; channel?: string; project?: string; chatId?: string; username?: string }\n\n/** The one entry point. Conversation rules beat project shape, which beats\n * the agent's place in the org, because a rule someone wrote by hand is\n * always more deliberate than a heuristic. */\nexport function resolveClient(ref: WorkRef, business: BusinessShape): string {\n const contact = matchContact(ref.channel, { chatId: ref.chatId, username: ref.username }, business.contactMap)\n if (contact) return contact.client\n if (ref.project) return clientFromProject(ref.project, business.projects)\n if (ref.agentId) {\n const byAgent = agentClients(business).get(ref.agentId)\n if (byAgent) return byAgent\n }\n return UNMAPPED\n}\n\n/** A monitor session id is \"<agent>:<channel>:<target...>\". Forge targets\n * carry the project, chat targets carry the chat id. */\nexport function parseWorkRef(sessionId: string, agentId?: string): WorkRef {\n const parts = String(sessionId || \"\").split(\":\")\n if (parts.length < 3) return { agentId }\n const [agent, channel, ...rest] = parts\n const target = rest.join(\":\")\n const ref: WorkRef = { agentId: agentId || agent, channel }\n if (channel === \"gitlab\" || channel === \"github\") {\n // \"owner/repo:issue:94\" and \"owner/repo:pipeline:10897\" -> \"owner/repo\"\n ref.project = target.split(\":\")[0]\n } else if (channel === \"cron\" || channel === \"api\") {\n // Nothing addressable: fall through to the agent's own client.\n } else {\n ref.chatId = target.split(\"@\")[0]\n }\n return ref\n}\n\nconst KIND_DEFAULT = (id: string): \"client\" | \"internal\" | \"own\" => (id === UNMAPPED ? \"internal\" : \"client\")\n\nexport function clientPolicy(id: string, business: BusinessShape): ResolvedClient {\n const p = business.clients?.[id]\n return {\n id,\n name: p?.name || id,\n kind: p?.kind ?? KIND_DEFAULT(id),\n respondWithinMinutes: parseDuration(p?.respondWithin),\n standing: p?.standing ?? [],\n declared: Boolean(p),\n }\n}\n\n/** Every client the config can produce: from projects, contacts and overrides. */\nexport function listClients(business: BusinessShape): ResolvedClient[] {\n const ids = new Set<string>()\n for (const p of business.projects ?? []) ids.add(clientFromProject(p.id, business.projects))\n for (const c of business.contactMap ?? []) ids.add(c.client)\n for (const id of Object.keys(business.clients ?? {})) ids.add(id)\n return [...ids].sort().map(id => clientPolicy(id, business))\n}\n\n/** \"4h\", \"90m\", \"2d\" -> minutes. Anything else is treated as no clock, so a\n * typo can never silently become an aggressive deadline. */\nexport function parseDuration(v: string | undefined): number | undefined {\n const m = /^(\\d+)\\s*([mhd])$/.exec(String(v ?? \"\").trim())\n if (!m) return undefined\n const n = Number(m[1])\n return m[2] === \"m\" ? n : m[2] === \"h\" ? n * 60 : n * 1440\n}\n\n/** Whether an agent may act unattended for this client. Deny by default, and\n * never for a paying client regardless of what is listed — an outward-facing\n * mistake on someone else's repo is not ours to make on a timer. */\nexport function mayProceedUnattended(client: ResolvedClient, capability: string): boolean {\n if (client.kind === \"client\") return false\n return client.standing.includes(\"*\") || client.standing.includes(capability)\n}\n","import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, rmSync, statSync } from \"fs\"\nimport { resolve, relative, dirname, join, extname, basename } from \"path\"\n\n// --- Scoped file operations for the admin panel ---\n//\n// Lets the dashboard read/write identity files (CLAUDE.md / SOUL.md /\n// IDENTITY.md) and skill files (.claude/skills/<slug>/SKILL.md) WITHIN an\n// agent's workspace without ever touching the rest of the filesystem.\n// Every public function resolves the requested path against the agent's\n// workspace and refuses anything that would escape via .. symlinks, etc.\n\nconst MAX_FILE_BYTES = 200 * 1024 // 200 KB — plenty for CLAUDE.md / skills.\nconst ALLOWED_EXTENSIONS = new Set([\".md\", \".markdown\", \".txt\"])\n/** Extensions the skill-tree viewer is allowed to read/write. Broader than\n * identity editing — skills include shell scripts, lockfiles, configs. Kept\n * as an allow-list so a malicious prompt can't touch an arbitrary binary. */\nconst SKILL_EDITABLE_EXTENSIONS = new Set([\n \".md\", \".markdown\", \".txt\", \".json\", \".jsonc\", \".yaml\", \".yml\", \".toml\",\n \".sh\", \".bash\", \".zsh\", \".py\", \".js\", \".mjs\", \".ts\", \".tsx\", \".jsx\",\n \".css\", \".html\", \".lock\", \".cfg\", \".ini\", \".env\", \".gitignore\",\n])\n/** Depth limit when walking a skill dir tree — defensive against loops. */\nconst SKILL_TREE_MAX_DEPTH = 6\nconst IDENTITY_FILENAMES = new Set([\n \"CLAUDE.md\", \"SOUL.md\", \"IDENTITY.md\", \"PERSONA.md\", \"AGENT.md\", \"BOOTSTRAP.md\",\n])\n\nexport interface FileOverview {\n identity: Array<{\n path: string // relative to workspace, e.g. \"CLAUDE.md\"\n title: string\n size: number\n exists: boolean // canonical files can be listed as templates even if missing\n }>\n skills: Array<{\n slug: string // dir name under .claude/skills/\n title: string // first # heading in SKILL.md or fallback to slug\n size: number\n path: string // relative to workspace, e.g. \".claude/skills/foo/SKILL.md\"\n }>\n workspace: string // absolute, for display only\n}\n\n/**\n * Resolve a user-supplied path to an absolute path strictly within `workspace`.\n * Throws if the result would escape. Returns the normalised absolute path.\n */\nfunction resolveInWorkspace(workspace: string, relPath: string): string {\n if (!workspace) throw new Error(\"Agent workspace is not configured.\")\n const wsAbs = resolve(workspace)\n if (!existsSync(wsAbs)) {\n // Create the workspace lazily — a fresh agent scaffolded via the wizard\n // may have an empty directory created but not yet with file structure.\n mkdirSync(wsAbs, { recursive: true })\n }\n // Reject absolute-path attempts outright.\n if (!relPath || relPath.startsWith(\"/\") || relPath.match(/^[a-zA-Z]:/)) {\n throw new Error(`Path must be relative to the workspace: ${relPath}`)\n }\n const target = resolve(wsAbs, relPath)\n const rel = relative(wsAbs, target)\n // rel === \"\" means target IS the workspace dir — fine for list ops.\n // rel starts with \"..\" iff target is outside the workspace.\n if (rel.startsWith(\"..\")) {\n throw new Error(`Path escapes the workspace: ${relPath}`)\n }\n return target\n}\n\nfunction assertEditable(absPath: string): void {\n const ext = extname(absPath).toLowerCase()\n if (!ALLOWED_EXTENSIONS.has(ext)) {\n throw new Error(`Only text files (.md, .markdown, .txt) can be edited from the admin panel (got ${ext}).`)\n }\n}\n\nfunction extractTitle(markdown: string, fallback: string): string {\n const line = markdown.split(/\\r?\\n/).find((l) => l.trim().startsWith(\"#\"))\n if (!line) return fallback\n return line.replace(/^#+\\s*/, \"\").trim().slice(0, 120) || fallback\n}\n\n/**\n * List the identity files + skills for an agent. Identity \"canonical\" names\n * that don't exist are still reported (exists: false) so the UI can offer to\n * create them with a sensible default.\n */\nexport function listAgentFiles(workspace: string): FileOverview {\n const wsAbs = resolveInWorkspace(workspace, \".\")\n const identity: FileOverview[\"identity\"] = []\n const seen = new Set<string>()\n\n // Every .md in the workspace root is identity material; canonical names\n // sort first.\n let rootEntries: string[] = []\n try { rootEntries = readdirSync(wsAbs) } catch { /* empty */ }\n for (const name of IDENTITY_FILENAMES) {\n const abs = join(wsAbs, name)\n if (existsSync(abs) && statSync(abs).isFile()) {\n const size = statSync(abs).size\n const content = size > 0 && size < MAX_FILE_BYTES ? safeRead(abs) : \"\"\n identity.push({ path: name, title: extractTitle(content, name), size, exists: true })\n seen.add(name)\n } else {\n identity.push({ path: name, title: name, size: 0, exists: false })\n seen.add(name)\n }\n }\n for (const name of rootEntries) {\n if (seen.has(name)) continue\n const abs = join(wsAbs, name)\n try {\n const st = statSync(abs)\n if (!st.isFile()) continue\n if (!ALLOWED_EXTENSIONS.has(extname(name).toLowerCase())) continue\n const content = st.size > 0 && st.size < MAX_FILE_BYTES ? safeRead(abs) : \"\"\n identity.push({ path: name, title: extractTitle(content, name), size: st.size, exists: true })\n } catch { /* skip unreadable */ }\n }\n\n // Skills live under .claude/skills/<slug>/SKILL.md\n const skills: FileOverview[\"skills\"] = []\n const skillsRoot = join(wsAbs, \".claude\", \"skills\")\n if (existsSync(skillsRoot)) {\n try {\n for (const slug of readdirSync(skillsRoot)) {\n const dir = join(skillsRoot, slug)\n let st\n try { st = statSync(dir) } catch { continue }\n if (!st.isDirectory()) continue\n const file = join(dir, \"SKILL.md\")\n if (!existsSync(file)) continue\n const stf = statSync(file)\n const content = stf.size < MAX_FILE_BYTES ? safeRead(file) : \"\"\n skills.push({\n slug,\n title: extractTitle(content, slug),\n size: stf.size,\n path: `.claude/skills/${slug}/SKILL.md`,\n })\n }\n } catch { /* */ }\n }\n skills.sort((a, b) => a.slug.localeCompare(b.slug))\n\n return { identity, skills, workspace: wsAbs }\n}\n\nfunction safeRead(abs: string): string {\n try { return readFileSync(abs, \"utf-8\") } catch { return \"\" }\n}\n\n/**\n * Read a single file within the workspace. Throws if the path is outside the\n * workspace, has a disallowed extension, or doesn't exist.\n */\nexport function readAgentFile(workspace: string, relPath: string): { path: string; content: string } {\n const abs = resolveInWorkspace(workspace, relPath)\n assertEditable(abs)\n if (!existsSync(abs)) return { path: relPath, content: \"\" }\n if (statSync(abs).size > MAX_FILE_BYTES) {\n throw new Error(`File is too large to edit in the admin panel (> ${MAX_FILE_BYTES / 1024} KB).`)\n }\n return { path: relPath, content: readFileSync(abs, \"utf-8\") }\n}\n\n/**\n * Write a file. Creates parent dirs as needed. Rejects extensions / size\n * outside the allow-list the same way readAgentFile does.\n */\nexport function writeAgentFile(workspace: string, relPath: string, content: string): { path: string; bytes: number } {\n const abs = resolveInWorkspace(workspace, relPath)\n assertEditable(abs)\n const buf = Buffer.from(content, \"utf-8\")\n if (buf.length > MAX_FILE_BYTES) {\n throw new Error(`File too large (${buf.length} > ${MAX_FILE_BYTES} bytes).`)\n }\n mkdirSync(dirname(abs), { recursive: true })\n writeFileSync(abs, buf)\n return { path: relPath, bytes: buf.length }\n}\n\n/**\n * Create a new skill scaffold at .claude/skills/<slug>/SKILL.md.\n */\nexport function createAgentSkill(\n workspace: string,\n slug: string,\n opts: { title?: string; content?: string } = {},\n): { slug: string; path: string } {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) {\n throw new Error(\"Skill slug must be lowercase (letters, digits, -, _) and start with a letter or digit.\")\n }\n const relPath = `.claude/skills/${slug}/SKILL.md`\n const abs = resolveInWorkspace(workspace, relPath)\n if (existsSync(abs)) throw new Error(`Skill \"${slug}\" already exists.`)\n mkdirSync(dirname(abs), { recursive: true })\n const body = opts.content?.trim()\n ? opts.content\n : defaultSkillTemplate(slug, opts.title)\n writeFileSync(abs, body)\n return { slug, path: relPath }\n}\n\n/**\n * Remove a skill directory (.claude/skills/<slug>). Scoped to the workspace\n * so we can never touch anything else.\n */\nexport function deleteAgentSkill(workspace: string, slug: string): { slug: string } {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) throw new Error(`Invalid skill slug: ${slug}`)\n const abs = resolveInWorkspace(workspace, `.claude/skills/${slug}`)\n if (!existsSync(abs)) throw new Error(`Skill \"${slug}\" not found.`)\n const st = statSync(abs)\n if (!st.isDirectory()) throw new Error(`Skill path is not a directory: ${slug}`)\n rmSync(abs, { recursive: true, force: true })\n return { slug }\n}\n\n// --- Skill directory tree + per-file I/O ---\n//\n// A skill is `.claude/skills/<slug>/` — usually SKILL.md plus scripts,\n// lockfiles, configs. The admin UI lets the operator browse the directory\n// tree, edit any text file, and dispatch \"install deps\" through the agent.\n\nexport interface SkillTreeEntry {\n /** Path relative to the skill dir root. Directories end with \"/\". */\n path: string\n kind: \"file\" | \"dir\"\n size: number\n editable: boolean // false for binaries or disallowed extensions\n}\n\nexport interface SkillDepsHint {\n /** Which install command the operator should run (or have an agent run). */\n manager: \"npm\" | \"pnpm\" | \"yarn\" | \"pip\" | \"bundle\" | null\n /** Filename that triggered the detection, relative to skill dir. */\n file: string | null\n /** The exact command we'd recommend. */\n command: string | null\n}\n\n/** List every file in `.claude/skills/<slug>/`, recursively. Scoped so the\n * caller can't escape the skill dir via symlinks or \"..\" segments. */\nexport function listSkillTree(workspace: string, slug: string): SkillTreeEntry[] {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) throw new Error(`Invalid skill slug: ${slug}`)\n const skillRoot = resolveInWorkspace(workspace, `.claude/skills/${slug}`)\n if (!existsSync(skillRoot)) throw new Error(`Skill \"${slug}\" not found.`)\n const out: SkillTreeEntry[] = []\n walk(skillRoot, \"\", 0, out)\n out.sort((a, b) => {\n // dirs first, then alphabetical\n if (a.kind !== b.kind) return a.kind === \"dir\" ? -1 : 1\n return a.path.localeCompare(b.path)\n })\n return out\n\n function walk(absDir: string, relDir: string, depth: number, acc: SkillTreeEntry[]): void {\n if (depth > SKILL_TREE_MAX_DEPTH) return\n let entries: string[] = []\n try { entries = readdirSync(absDir) } catch { return }\n for (const name of entries) {\n if (name === \"node_modules\" || name === \".git\") continue\n const abs = join(absDir, name)\n const rel = relDir ? `${relDir}/${name}` : name\n let st\n try { st = statSync(abs) } catch { continue }\n if (st.isDirectory()) {\n acc.push({ path: `${rel}/`, kind: \"dir\", size: 0, editable: false })\n walk(abs, rel, depth + 1, acc)\n } else if (st.isFile()) {\n const ext = extname(name).toLowerCase()\n // name-only allowlist for dotfiles like .env / .gitignore (no extension)\n const allowedByName = SKILL_EDITABLE_EXTENSIONS.has(ext) ||\n SKILL_EDITABLE_EXTENSIONS.has(name.toLowerCase())\n acc.push({\n path: rel,\n kind: \"file\",\n size: st.size,\n editable: allowedByName && st.size < MAX_FILE_BYTES,\n })\n }\n }\n }\n}\n\n/** Read any file in a skill dir. Respects the skill-editable allowlist. */\nexport function readSkillFile(workspace: string, slug: string, relPath: string): { path: string; content: string } {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) throw new Error(`Invalid skill slug: ${slug}`)\n if (!relPath || relPath.includes(\"..\")) throw new Error(`Invalid file path: ${relPath}`)\n const abs = resolveInWorkspace(workspace, `.claude/skills/${slug}/${relPath}`)\n if (!existsSync(abs) || !statSync(abs).isFile()) {\n throw new Error(`File not found: ${relPath}`)\n }\n const ext = extname(abs).toLowerCase()\n const name = basename(abs).toLowerCase()\n if (!SKILL_EDITABLE_EXTENSIONS.has(ext) && !SKILL_EDITABLE_EXTENSIONS.has(name)) {\n throw new Error(`File type not editable: ${relPath}`)\n }\n if (statSync(abs).size > MAX_FILE_BYTES) {\n throw new Error(`File too large to edit (> ${MAX_FILE_BYTES / 1024} KB): ${relPath}`)\n }\n return { path: relPath, content: readFileSync(abs, \"utf-8\") }\n}\n\n/** Write any allowed file inside a skill dir. Creates parents. */\nexport function writeSkillFile(\n workspace: string,\n slug: string,\n relPath: string,\n content: string,\n): { path: string; bytes: number } {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) throw new Error(`Invalid skill slug: ${slug}`)\n if (!relPath || relPath.includes(\"..\")) throw new Error(`Invalid file path: ${relPath}`)\n const abs = resolveInWorkspace(workspace, `.claude/skills/${slug}/${relPath}`)\n const ext = extname(abs).toLowerCase()\n const name = basename(abs).toLowerCase()\n if (!SKILL_EDITABLE_EXTENSIONS.has(ext) && !SKILL_EDITABLE_EXTENSIONS.has(name)) {\n throw new Error(`File type not editable: ${relPath}`)\n }\n const buf = Buffer.from(content, \"utf-8\")\n if (buf.length > MAX_FILE_BYTES) {\n throw new Error(`File too large (${buf.length} > ${MAX_FILE_BYTES} bytes).`)\n }\n mkdirSync(dirname(abs), { recursive: true })\n writeFileSync(abs, buf)\n return { path: relPath, bytes: buf.length }\n}\n\n/** Detect which dependency manager a skill needs based on the files present. */\nexport function detectSkillDeps(workspace: string, slug: string): SkillDepsHint {\n if (!/^[a-z0-9][a-z0-9_-]*$/.test(slug)) throw new Error(`Invalid skill slug: ${slug}`)\n const skillRoot = resolveInWorkspace(workspace, `.claude/skills/${slug}`)\n if (!existsSync(skillRoot)) throw new Error(`Skill \"${slug}\" not found.`)\n const check = (name: string): boolean => existsSync(join(skillRoot, name))\n if (check(\"pnpm-lock.yaml\")) return { manager: \"pnpm\", file: \"pnpm-lock.yaml\", command: \"pnpm install\" }\n if (check(\"yarn.lock\")) return { manager: \"yarn\", file: \"yarn.lock\", command: \"yarn install\" }\n if (check(\"package.json\")) return { manager: \"npm\", file: \"package.json\", command: \"npm install\" }\n if (check(\"requirements.txt\")) return { manager: \"pip\", file: \"requirements.txt\", command: \"pip install -r requirements.txt\" }\n if (check(\"Gemfile\")) return { manager: \"bundle\", file: \"Gemfile\", command: \"bundle install\" }\n return { manager: null, file: null, command: null }\n}\n\nfunction defaultSkillTemplate(slug: string, title?: string): string {\n const displayTitle = title?.trim() || slug\n return `# ${displayTitle}\n\nDescribe what this skill does and when the agent should use it. Keep it under ~5 lines — the agent reads this to decide whether to invoke the skill.\n\n## When to use\n- Trigger condition 1\n- Trigger condition 2\n\n## How to use\nDocument the arguments, commands, or files the skill expects. Example:\n\n\\`\\`\\`bash\n# Example invocation\nclaude-code ${basename(slug)} --arg value\n\\`\\`\\`\n`\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","existsSync","readdirSync","statSync","resolve","join","existsSync","mkdirSync","readFileSync","writeFileSync","dirname","resolve","z","sourceSchema","itemSchema","fileSchema","BacklogStore","path","resolve","existsSync","p","raw","readFileSync","e","file","jsonP","mkdirSync","dirname","writeFileSync","id","i","input","now","item","inputs","existing","added","patch","idx","updated","before","lines","buckets","sections","key","label","it","check","mention","time","src","sourceBacklogId","source","resolveAssigneesForSource","agentIds","source","daemon","out","mappings","agentId","m","x","syncBacklogItemToSource","item","patch","log","syncToGitLab","syncToGitHub","gl","token","host","attempts","api","path","init","projectEnc","assigneeIds","u","res","arr","milestoneId","e","body","text","a","repo","milestoneNumber","found","nextLabels","issue","current","l","adds","r","CHECK_RE","MENTION_RE","TIME_RE","parseDuration","s","hMatch","mMatch","BacklogWorkSource","path","daemon","log","BacklogStore","full","resolve","existsSync","readFileSync","lines","writeFileSync","agentId","it","items","i","m","body","mention","timeMatch","title","_opts","itemId","item","updated","usernames","resolveAssigneesForSource","syncBacklogItemToSource","e","update","patch","sourcePatch","lineNo","line","note","stage","WikiWorkSource","root","glob","dir","out","entry","readdirSync","join","statSync","files","file","parts","GitLabWorkSource","host","token","projects","agentUsernames","username","names","n","issue","project","ns","labels","labelDetails","l","assignees","a","firstUsername","init","res","projectList","q","issues","iid","type","seg","mins","dur","opts","sinceDays","updatedAfter","params","state","result","deny","prefix","to","assigneeIds","u","users","x","milestoneId","ms","byUsername","members","b","byTitle","all","input","assignee_ids","milestone_id","toLabel","fromLabel","add","remove","createWorkSource","business","ws","gl","mapping","r","existsSync","readdirSync","readFileSync","statSync","resolve","workflowHealth","workflows","runs","now","windowDays","span","byId","r","list","w","mine","recent","prior","failed","paused","lastRunAt","state","a","b","rank","cache","scanRuns","runsDir","limit","files","f","p","live","key","out","path","mtime","hit","summary","readSummary","lines","head","workflowId","status","i","tail","s","UNMAPPED","clientFromProject","project","projects","hit","p","slash","agentClients","business","out","orgChart","climb","agentId","seen","up","c","matchContact","channel","who","contactMap","pool","m","resolveClient","ref","contact","byAgent","parseWorkRef","sessionId","parts","agent","rest","target","KIND_DEFAULT","id","clientPolicy","parseDuration","listClients","ids","v","n","existsSync","readFileSync","writeFileSync","mkdirSync","readdirSync","rmSync","statSync","resolve","relative","dirname","join","extname","basename","MAX_FILE_BYTES","ALLOWED_EXTENSIONS","SKILL_EDITABLE_EXTENSIONS","SKILL_TREE_MAX_DEPTH","IDENTITY_FILENAMES","resolveInWorkspace","workspace","relPath","wsAbs","target","assertEditable","absPath","ext","extractTitle","markdown","fallback","line","l","listAgentFiles","identity","seen","rootEntries","name","abs","size","content","safeRead","st","skills","skillsRoot","slug","dir","file","stf","a","b","readAgentFile","writeAgentFile","buf","createAgentSkill","opts","body","defaultSkillTemplate","deleteAgentSkill","listSkillTree","skillRoot","out","walk","absDir","relDir","depth","acc","entries","rel","allowedByName","readSkillFile","writeSkillFile","detectSkillDeps","check","title"]}
@@ -0,0 +1,121 @@
1
+ import{a as L,b as S}from"./chunk-2T5W3ETS.js";import{a as T,b as M}from"./chunk-ZTEU5JVQ.js";import{a as C,b as q}from"./chunk-L2WSPY6S.js";import{e as x}from"./chunk-FIOB3MEX.js";import{execFile as me}from"child_process";import{writeFileSync as V,mkdirSync as O,existsSync as b,readFileSync as he}from"fs";import{resolve as p}from"path";var Q=["off","shadow","authoritative"],Z={telegram:"INTENT_LEDGER_MODE_TELEGRAM",slack:"INTENT_LEDGER_MODE_SLACK",whatsapp:"INTENT_LEDGER_MODE_WHATSAPP",discord:"INTENT_LEDGER_MODE_DISCORD",gitlab:"INTENT_LEDGER_MODE_GITLAB",github:"INTENT_LEDGER_MODE_GITHUB",workflow:"INTENT_LEDGER_MODE_WORKFLOW",cron:"INTENT_LEDGER_MODE_CRON",mesh:"INTENT_LEDGER_MODE_MESH"},ee="INTENT_LEDGER_MODE";function X(r,e=process.env){let n=F(e[Z[r]]);if(n)return n;let t=F(e[ee]);return t||"off"}function F(r){if(typeof r!="string")return null;let e=r.trim().toLowerCase();return Q.includes(e)?e:null}M();import te from"better-sqlite3";import{existsSync as Te,mkdirSync as ne}from"fs";import{dirname as re,resolve as ie}from"path";var I=class{db;path;constructor(e={}){this.path=ie(process.cwd(),e.path??".agentx/intent/ledger.sqlite"),ne(re(this.path),{recursive:!0}),this.db=new te(this.path),this.db.pragma("journal_mode = WAL"),this.db.pragma("synchronous = NORMAL"),this.db.pragma("foreign_keys = ON"),ce(this.db)}close(){this.db.close()}schemaVersion(){return this.db.prepare("SELECT MAX(v) AS v FROM schema_version").get().v??0}recordEvent(e){if(e.sourceEventId!==null){let t=this.findEventBySourceId(e.source,e.sourceEventId);if(t)return t}let n={id:e.id??T(e.ts),ts:e.ts,source:e.source,sourceEventId:e.sourceEventId,project:e.project,subject:e.subject,intent:e.intent,rawJson:e.rawJson};return this.db.prepare(`INSERT INTO intent_events (id, ts, source, source_event_id, project, subject, intent, raw_json)
2
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(n.id,n.ts,n.source,n.sourceEventId,n.project,n.subject,n.intent,n.rawJson),n}findEventBySourceId(e,n){let t=this.db.prepare(`SELECT id, ts, source, source_event_id, project, subject, intent, raw_json
3
+ FROM intent_events WHERE source = ? AND source_event_id = ?`).get(e,n);return t?k(t):null}getEventById(e){let n=this.db.prepare(`SELECT id, ts, source, source_event_id, project, subject, intent, raw_json
4
+ FROM intent_events WHERE id = ?`).get(e);return n?k(n):null}recordDecision(e){this.db.prepare(`INSERT INTO intent_decisions (event_id, decided_at, decided_by, agent_id, outcome, reason)
5
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e.eventId,e.decidedAt,e.decidedBy,e.agentId,e.outcome,e.reason)}getDecisionsForEvent(e){return this.db.prepare(`SELECT event_id, decided_at, decided_by, agent_id, outcome, reason
6
+ FROM intent_decisions WHERE event_id = ? ORDER BY decided_at ASC`).all(e).map(B)}getActiveDecisionForSubject(e,n){if(e===null||n===null)return null;let t=this.db.prepare(`SELECT d.event_id, d.decided_at, d.decided_by, d.agent_id, d.outcome, d.reason
7
+ FROM intent_decisions d
8
+ JOIN intent_events e ON e.id = d.event_id
9
+ LEFT JOIN intent_resolutions r
10
+ ON r.decision_event_id = d.event_id AND r.decision_decided_by = d.decided_by
11
+ WHERE e.project = ? AND e.subject = ?
12
+ AND d.outcome = 'dispatched'
13
+ AND r.decision_event_id IS NULL
14
+ ORDER BY d.decided_at DESC
15
+ LIMIT 1`).get(e,n);return t?B(t):null}recordResolution(e){this.db.prepare(`INSERT INTO intent_resolutions (decision_event_id, decision_decided_by, resolved_at, status, duration_ms, result_summary)
16
+ VALUES (?, ?, ?, ?, ?, ?)`).run(e.decisionEventId,e.decisionDecidedBy,e.resolvedAt,e.status,e.durationMs,e.resultSummary)}cleanupOrphanedDispatches(e="daemon-restart",n=Date.now){let t=this.db.prepare(`
17
+ SELECT d.event_id, d.decided_by FROM intent_decisions d
18
+ LEFT JOIN intent_resolutions r
19
+ ON r.decision_event_id = d.event_id AND r.decision_decided_by = d.decided_by
20
+ WHERE d.outcome = 'dispatched' AND r.decision_event_id IS NULL
21
+ `).all(),i=n(),s=0;for(let o of t)try{this.recordResolution({decisionEventId:o.event_id,decisionDecidedBy:o.decided_by,resolvedAt:i,status:"canceled",durationMs:null,resultSummary:e}),s++}catch{}return s}getResolution(e,n){let t=this.db.prepare(`SELECT decision_event_id, decision_decided_by, resolved_at, status, duration_ms, result_summary
22
+ FROM intent_resolutions WHERE decision_event_id = ? AND decision_decided_by = ?`).get(e,n);return t?se(t):null}recordDivergence(e){this.db.prepare(`INSERT INTO intent_divergences (
23
+ id, ts, source, event_id, decided_by,
24
+ ledger_agent_id, ledger_outcome, ledger_reason,
25
+ legacy_agent_id, legacy_outcome, legacy_reason
26
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.id,e.ts,e.source,e.eventId,e.decidedBy,e.ledgerAgentId,e.ledgerOutcome,e.ledgerReason,e.legacyAgentId,e.legacyOutcome,e.legacyReason)}getDivergences(e={}){let n=[],t=[];e.source!==void 0&&(n.push("source = ?"),t.push(e.source)),e.since!==void 0&&(n.push("ts >= ?"),t.push(e.since));let i=n.length?`WHERE ${n.join(" AND ")}`:"",s=e.limit!==void 0?"LIMIT ?":"";return e.limit!==void 0&&t.push(e.limit),this.db.prepare(`SELECT id, ts, source, event_id, decided_by,
27
+ ledger_agent_id, ledger_outcome, ledger_reason,
28
+ legacy_agent_id, legacy_outcome, legacy_reason
29
+ FROM intent_divergences
30
+ ${i}
31
+ ORDER BY ts DESC
32
+ ${s}`).all(...t).map(oe)}};function k(r){return{id:r.id,ts:r.ts,source:r.source,sourceEventId:r.source_event_id,project:r.project,subject:r.subject,intent:r.intent,rawJson:r.raw_json}}function B(r){return{eventId:r.event_id,decidedAt:r.decided_at,decidedBy:r.decided_by,agentId:r.agent_id,outcome:r.outcome,reason:r.reason}}function se(r){return{decisionEventId:r.decision_event_id,decisionDecidedBy:r.decision_decided_by,resolvedAt:r.resolved_at,status:r.status,durationMs:r.duration_ms,resultSummary:r.result_summary}}function oe(r){return{id:r.id,ts:r.ts,source:r.source,eventId:r.event_id,decidedBy:r.decided_by,ledgerAgentId:r.ledger_agent_id,ledgerOutcome:r.ledger_outcome,ledgerReason:r.ledger_reason,legacyAgentId:r.legacy_agent_id,legacyOutcome:r.legacy_outcome,legacyReason:r.legacy_reason}}function ce(r){r.exec(`
33
+ CREATE TABLE IF NOT EXISTS schema_version (
34
+ v INTEGER PRIMARY KEY
35
+ );
36
+ `);let e=r.prepare("SELECT MAX(v) AS v FROM schema_version").get().v??0;e<1&&de(r),e<2&&ae(r)}function de(r){r.exec(`
37
+ CREATE TABLE IF NOT EXISTS intent_events (
38
+ -- TEXT PRIMARY KEY isn't auto-NOT-NULL in SQLite; spell it out so the
39
+ -- ULID invariant is enforced at the schema level rather than relying on
40
+ -- the application layer.
41
+ id TEXT NOT NULL PRIMARY KEY,
42
+ ts INTEGER NOT NULL,
43
+ source TEXT NOT NULL,
44
+ source_event_id TEXT,
45
+ project TEXT,
46
+ subject TEXT,
47
+ intent TEXT,
48
+ raw_json TEXT NOT NULL
49
+ );
50
+
51
+ CREATE INDEX IF NOT EXISTS idx_intent_events_subject
52
+ ON intent_events (project, subject);
53
+ CREATE INDEX IF NOT EXISTS idx_intent_events_source_event_id
54
+ ON intent_events (source, source_event_id);
55
+ CREATE INDEX IF NOT EXISTS idx_intent_events_ts
56
+ ON intent_events (ts);
57
+
58
+ CREATE TABLE IF NOT EXISTS intent_decisions (
59
+ event_id TEXT NOT NULL REFERENCES intent_events(id),
60
+ decided_at INTEGER NOT NULL,
61
+ decided_by TEXT NOT NULL,
62
+ agent_id TEXT,
63
+ outcome TEXT NOT NULL,
64
+ reason TEXT,
65
+ PRIMARY KEY (event_id, decided_by)
66
+ );
67
+
68
+ CREATE INDEX IF NOT EXISTS idx_intent_decisions_agent
69
+ ON intent_decisions (agent_id, decided_at);
70
+ CREATE INDEX IF NOT EXISTS idx_intent_decisions_outcome
71
+ ON intent_decisions (outcome, decided_at);
72
+
73
+ CREATE TABLE IF NOT EXISTS intent_resolutions (
74
+ decision_event_id TEXT NOT NULL,
75
+ decision_decided_by TEXT NOT NULL,
76
+ resolved_at INTEGER NOT NULL,
77
+ status TEXT NOT NULL,
78
+ duration_ms INTEGER,
79
+ result_summary TEXT,
80
+ PRIMARY KEY (decision_event_id, decision_decided_by),
81
+ FOREIGN KEY (decision_event_id, decision_decided_by)
82
+ REFERENCES intent_decisions(event_id, decided_by)
83
+ );
84
+
85
+ CREATE INDEX IF NOT EXISTS idx_intent_resolutions_status
86
+ ON intent_resolutions (status, resolved_at);
87
+ `),r.prepare("INSERT INTO schema_version (v) VALUES (1)").run()}function ae(r){r.exec(`
88
+ CREATE TABLE IF NOT EXISTS intent_divergences (
89
+ id TEXT NOT NULL PRIMARY KEY,
90
+ ts INTEGER NOT NULL,
91
+ source TEXT NOT NULL,
92
+ event_id TEXT NOT NULL,
93
+ decided_by TEXT NOT NULL,
94
+ ledger_agent_id TEXT,
95
+ ledger_outcome TEXT NOT NULL,
96
+ ledger_reason TEXT,
97
+ legacy_agent_id TEXT,
98
+ legacy_outcome TEXT NOT NULL,
99
+ legacy_reason TEXT,
100
+ FOREIGN KEY (event_id, decided_by)
101
+ REFERENCES intent_decisions(event_id, decided_by)
102
+ );
103
+
104
+ -- "show me recent divergences from gitlab" \u2014 the soak's primary query
105
+ CREATE INDEX IF NOT EXISTS idx_intent_divergences_source_ts
106
+ ON intent_divergences (source, ts);
107
+ -- "are any deciders consistently wrong" \u2014 per-decider analysis
108
+ CREATE INDEX IF NOT EXISTS idx_intent_divergences_decided_by
109
+ ON intent_divergences (decided_by, ts);
110
+ `),r.prepare("INSERT INTO schema_version (v) VALUES (2)").run()}var R;function J(r={}){return R||(R=new I({path:r.path}),R)}var P;function Le(r){P=r}function U(){return P}function G(r,e,n,t=Date.now,i){return r.db.transaction(o=>{let c=r.recordEvent(e),d=i??U(),u=d?.pmFor?.(c.project),a=u?`pm:${u}`:n.decidedBy,m=r.getDecisionsForEvent(c.id).find(h=>h.decidedBy===a);if(m)return m;let E=r.getActiveDecisionForSubject(c.project,c.subject);if(E){let h={eventId:c.id,decidedAt:o,decidedBy:a,agentId:null,outcome:"deduped",reason:`active dispatch in flight: ${E.decidedBy} \u2192 ${E.agentId??"?"}`};return r.recordDecision(h),h}let g=n.decide(c),l=g?g.agentId:null,_=g?g.outcome:"halted",f=g?g.reason:"no policy match";if(d?.canHandle&&_==="dispatched"&&l&&!d.canHandle(l,c.project,c.intent)){let N=l;l=null,_="halted",f=`org-chart: agent "${N}" cannot handle (${c.project??"no-project"}/${c.intent??"no-intent"})`}if(d?.withinDelegationBudget&&_==="dispatched"&&l&&!d.withinDelegationBudget(l,c.project,c.subject)){let h=l;l=null,_="halted",f=`delegation-budget: agent "${h}" depth-limit exceeded on ${c.project??"?"}/${c.subject??"?"}`}u&&_==="dispatched"&&l&&(f=f?`${f} (pm-gate: rubber-stamped by ${u})`:`pm-gate: rubber-stamped by ${u}`);let y={eventId:c.id,decidedAt:o,decidedBy:a,agentId:l,outcome:_,reason:f};return r.recordDecision(y),y})(t())}q();M();function j(r,e,n,t,i=Date.now){if(ue(n,t))return!1;let s=i(),o={id:T(s),ts:s,source:e,eventId:n.eventId,decidedBy:n.decidedBy,ledgerAgentId:n.agentId,ledgerOutcome:n.outcome,ledgerReason:n.reason,legacyAgentId:t.agentId,legacyOutcome:t.outcome,legacyReason:t.reason??null};return r.recordDivergence(o),C.warn(`[ledger-divergence] source=${e} event=${n.eventId} decided_by=${n.decidedBy} ledger=${n.outcome}/${n.agentId??"null"} legacy=${t.outcome}/${t.agentId??"null"}`),!0}function ue(r,e){return r.outcome===e.outcome&&Y(r.agentId)===Y(e.agentId)}function Y(r){if(!r)return null;let e=r.match(/^(workflow-run|mesh-fwd):/);return e?`${e[1]}:*`:r}function le(r,e,n=Date.now){return{ts:n(),source:"cron",sourceEventId:`${r.jobId}:${r.firedAt.getTime()}`,project:null,subject:`cron:${r.jobId}`,intent:"cron.fired",rawJson:e}}function ge(r){return{decidedBy:"cron-scheduler",decide:()=>({agentId:r.agentId,outcome:r.outcome==="deduped"?"halted":r.outcome,reason:r.outcome==="deduped"?`legacy-dedup: ${r.reason??""}`.trim():r.reason??null})}}function H(r,e,n,t,i=Date.now){let s=le(e,n,i),o=ge(t),c=G(r,s,o,i);return j(r,"cron",c,t,i),c}function K(r,e){if(!e)return r;let n=e*4;return`${r}
111
+
112
+ [Response budget]
113
+ Keep your response under ~${e} tokens (~${n} chars). Be concise \u2014 this is automated batch work, not a conversation.`}function v(r,e,n){let t=[];for(let i of r.split(","))if(i==="*")for(let s=e;s<=n;s++)t.push(s);else if(i.includes("/")){let[s,o]=i.split("/"),c=parseInt(o,10),d=s==="*"?e:parseInt(s,10);for(let u=d;u<=n;u+=c)t.push(u)}else if(i.includes("-")){let[s,o]=i.split("-").map(Number);for(let c=s;c<=o;c++)t.push(c)}else t.push(parseInt(i,10));return[...new Set(t)].sort((i,s)=>i-s)}function W(r,e,n){let t=r.trim().split(/\s+/);if(t.length!==5)throw new Error(`Invalid cron: ${r}`);let i=v(t[0],0,59),s=v(t[1],0,23),o=v(t[2],1,31),c=v(t[3],1,12),d=v(t[4],0,6),u=new Intl.DateTimeFormat("en-US",{timeZone:n,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hour12:!1}),a=new Date(e.getTime()+6e4);a.setSeconds(0,0);let m=new Date(a.getTime()+366*24*60*60*1e3);for(;a<m;){let E=u.formatToParts(a),g=N=>parseInt(E.find(z=>z.type===N)?.value||"0",10),l=g("minute"),_=g("hour"),f=g("day"),y=g("month"),h=a.getDay();if(i.includes(l)&&s.includes(_)&&o.includes(f)&&c.includes(y)&&d.includes(h))return a;a.setTime(a.getTime()+6e4)}throw new Error(`No next run found for cron "${r}" within 1 year`)}var $=5,w=[3e4,6e4,3e5,9e5,36e5],D=class{jobs=new Map;timers=new Map;registry;hooks;runsDir;lastRunFile;running=!1;notifyCallback;log;constructor(e,n,t,i=console.error.bind(console,"[cron]")){this.registry=n,this.hooks=t,this.log=i,this.runsDir=p(process.cwd(),".agentx/cron/runs"),this.lastRunFile=p(process.cwd(),".agentx/cron/last-runs.json");for(let[s,o]of Object.entries(e.crons))this.jobs.set(s,{id:s,enabled:o.enabled,schedule:o.schedule,timezone:o.timezone,agent:o.agent,prompt:o.prompt,command:o.command,timeout:o.timeout,model:o.model,maxOutputTokens:o.maxOutputTokens,onError:o.onError,consecutiveErrors:0,totalRuns:0,totalFailures:0})}setNotifyCallback(e){this.notifyCallback=e}async start(){this.running=!0,b(this.runsDir)||O(this.runsDir,{recursive:!0});let e=this.detectMissedRuns();if(e.length>0){this.log(`Detected ${e.length} missed cron run(s) while daemon was down:`);for(let{jobId:n,missedAt:t}of e)this.log(` "${n}" should have run at ${t.toISOString()}`);this.executeMissedRuns(e).catch(n=>{this.log(`Error executing missed runs: ${n.message}`)})}for(let[n,t]of this.jobs){if(!t.enabled){this.log(`Job "${n}" is disabled, skipping`);continue}this.scheduleNext(n)}this.log(`${this.jobs.size} cron job(s) loaded, ${Array.from(this.jobs.values()).filter(n=>n.enabled).length} enabled`)}async stop(){this.running=!1;for(let e of this.timers.values())clearTimeout(e);this.timers.clear(),this.saveLastRuns()}armTimer(e,n,t){let i=n-Date.now();if(i>D.MAX_TIMEOUT_MS){let o=setTimeout(()=>this.armTimer(e,n,t),D.MAX_TIMEOUT_MS);this.timers.set(e,o);return}let s=setTimeout(()=>{if(Date.now()<n){this.armTimer(e,n,t);return}t()},Math.max(0,i));this.timers.set(e,s)}scheduleNext(e){let n=this.jobs.get(e);if(!n||!n.enabled||!this.running)return;let t=this.timers.get(e);t&&clearTimeout(t);try{let i=W(n.schedule,new Date,n.timezone);n.nextRun=i,n.retryPending=!1;let s=i.getTime()-Date.now();this.log(`Job "${e}" next run: ${i.toISOString()} (in ${Math.round(s/1e3)}s)`),this.armTimer(e,i.getTime(),()=>this.executeJob(e))}catch(i){this.log(`Failed to schedule "${e}": ${i.message}`)}}scheduleRetry(e,n){let t=this.jobs.get(e);if(!t||!t.enabled||!this.running)return;if(n>=$){this.log(`Job "${e}" exhausted all ${$} retries, scheduling next regular run`),this.scheduleNext(e);return}let i=w[n]||w[w.length-1];t.retryPending=!0,this.log(`Job "${e}" retry ${n+1}/${$} in ${Math.round(i/1e3)}s`);let s=setTimeout(()=>this.executeJob(e,n+1),i);this.timers.set(e,s)}recordCronDecisionInLedger(e,n,t,i){if(X("cron")!=="off")try{let s=H(J(),{jobId:e,agentId:n,firedAt:t},JSON.stringify({jobId:e,agentId:n,firedAt:t.toISOString()}),i);return s.outcome==="dispatched"?{eventId:s.eventId,decidedBy:s.decidedBy}:void 0}catch(s){this.log(`[ledger] cron "${e}" record failed: ${s?.message??s}`);return}}async executeCommandJob(e,n,t,i){let s=e.command.trim();this.log(`${t?`[retry ${i}] `:""}Running command for "${e.id}"`);let o=!1,c="",d;try{let{stdout:a,stderr:m,code:E}=await fe(s,e.timeout*1e3);c=_e(`${a}${m?`
114
+ ${m}`:""}`,Ee),o=E===0,o||(d=`exit ${E}${c?`
115
+ ${c}`:""}`)}catch(a){d=a?.message??String(a)}let u={jobId:e.id,startedAt:n,completedAt:new Date,success:o,response:c||void 0,error:d,duration:Date.now()-n.getTime(),isRetry:t,retryAttempt:i};if(!o){if(e.consecutiveErrors++,e.totalFailures++,e.lastError=d,this.log(`Command job "${e.id}" failed (${e.consecutiveErrors} consecutive): ${d}`),await this.notifyFailure(e,d??"command failed"),e.onError.includes("disable")&&e.consecutiveErrors>=3){e.enabled=!1,this.log(`Job "${e.id}" disabled after ${e.consecutiveErrors} consecutive errors`);try{await L(a=>S(a,`crons.${e.id}.enabled`,!1),{reload:!1})}catch(a){this.log(`Failed to persist disable for "${e.id}": ${a.message}`)}await this.notifyDisabled(e)}this.logRun(u),this.scheduleRetry(e.id,i);return}e.consecutiveErrors>0&&this.log(`Job "${e.id}" recovered after ${e.consecutiveErrors} failure(s)`),e.consecutiveErrors=0,e.lastSuccess=new Date,e.lastError=void 0,e.retryPending=!1,this.log(`Command job "${e.id}" completed in ${u.duration}ms`),this.logRun(u),this.scheduleNext(e.id)}async executeJob(e,n=0){let t=this.jobs.get(e);if(!t||!this.running)return;let i=n>0,s=new Date;if(this.hooks?.has("pre:cron-run")){let d=await this.hooks.execute("pre:cron-run",{event:"pre:cron-run",jobId:e,agent:t.agent,prompt:t.prompt});if(d.blocked){this.log(`Job "${e}" blocked by hook: ${d.message}`),this.recordCronDecisionInLedger(e,t.agent,s,{agentId:null,outcome:"halted",reason:`pre-hook blocked: ${d.message??""}`.trim()}),this.scheduleNext(e);return}}this.log(t.command?.trim()?`${i?`[retry ${n}] `:""}Executing job "${e}" -> command`:`${i?`[retry ${n}] `:""}Executing job "${e}" -> agent "${t.agent}"`);let o=s;if(t.lastRun=o,t.totalRuns++,t.command?.trim()){await this.executeCommandJob(t,o,i,n);return}let c=this.recordCronDecisionInLedger(e,t.agent,s,{agentId:t.agent,outcome:"dispatched",reason:i?`retry ${n}`:null});try{let d=await this.registry.execute({message:K(t.prompt,t.maxOutputTokens),agentId:t.agent,model:t.model,intentRef:c,context:{channel:"cron",chatId:`cron:${e}`}}),u={jobId:e,startedAt:o,completedAt:new Date,success:!d.error,response:d.content,error:d.error,duration:d.duration||Date.now()-o.getTime(),isRetry:i,retryAttempt:n};if(d.error){if(t.consecutiveErrors++,t.totalFailures++,t.lastError=d.error,this.log(`Job "${e}" failed (${t.consecutiveErrors} consecutive): ${d.error}`),await this.notifyFailure(t,d.error),t.onError.includes("disable")&&t.consecutiveErrors>=3){t.enabled=!1,this.log(`Job "${e}" disabled after ${t.consecutiveErrors} consecutive errors`);try{let a=await L(m=>S(m,`crons.${e}.enabled`,!1),{reload:!1});a.success||this.log(`Failed to persist disable for "${e}": ${a.error}`)}catch(a){this.log(`Failed to persist disable for "${e}": ${a.message}`)}await this.notifyDisabled(t)}this.logRun(u),this.scheduleRetry(e,n);return}else t.consecutiveErrors>0&&this.log(`Job "${e}" recovered after ${t.consecutiveErrors} failure(s)`),t.consecutiveErrors=0,t.lastSuccess=new Date,t.lastError=void 0,t.retryPending=!1,this.log(`Job "${e}" completed in ${u.duration}ms`);this.logRun(u),this.hooks?.has("post:cron-run")&&await this.hooks.execute("post:cron-run",{event:"post:cron-run",jobId:e,success:u.success,duration:u.duration,error:u.error?new Error(u.error):void 0})}catch(d){t.consecutiveErrors++,t.totalFailures++,t.lastError=d.message,this.log(`Job "${e}" threw: ${d.message}`),await this.notifyFailure(t,d.message),this.scheduleRetry(e,n);return}this.saveLastRuns(),this.scheduleNext(e)}detectMissedRuns(){let e=[],n=this.loadLastRuns();if(!n)return e;for(let[t,i]of this.jobs){if(!i.enabled)continue;let s=n[t];if(!s)continue;let o=new Date(s);try{let c=W(i.schedule,o,i.timezone);c.getTime()<Date.now()&&e.push({jobId:t,missedAt:c})}catch{}}return e}async executeMissedRuns(e){for(let{jobId:n,missedAt:t}of e){let i=this.jobs.get(n);if(!(!i||!i.enabled)){this.log(`Running missed job "${n}" (was due at ${t.toISOString()})`);try{let s=await this.registry.execute({message:K(`[MISSED RUN \u2014 was scheduled for ${t.toISOString()}]
116
+
117
+ ${i.prompt}`,i.maxOutputTokens),agentId:i.agent,context:{channel:"cron",chatId:`cron:${n}`}}),o={jobId:n,startedAt:new Date,completedAt:new Date,success:!s.error,response:s.content,error:s.error,duration:s.duration||0,isRetry:!1};this.logRun(o),s.error?this.log(`Missed job "${n}" failed: ${s.error}`):(this.log(`Missed job "${n}" completed`),i.lastSuccess=new Date),i.lastRun=new Date,i.totalRuns++}catch(s){this.log(`Missed job "${n}" threw: ${s.message}`)}}}this.saveLastRuns()}async notifyFailure(e,n){if(!(!e.onError.includes("notify")&&e.consecutiveErrors<2)){if(!this.notifyCallback){this.log(`[ALERT] Cron "${e.id}" failed (${e.consecutiveErrors}x): ${n.slice(0,200)}`);return}try{await this.notifyCallback(e.id,e.agent,n,e.consecutiveErrors)}catch{}}}async notifyDisabled(e){if(!this.notifyCallback){this.log(`[ALERT] Cron "${e.id}" has been DISABLED after ${e.consecutiveErrors} consecutive failures`);return}try{await this.notifyCallback(e.id,e.agent,`AUTO-DISABLED after ${e.consecutiveErrors} consecutive failures. Last error: ${e.lastError||"unknown"}`,e.consecutiveErrors)}catch{}}saveLastRuns(){try{let e={};for(let[t,i]of this.jobs)i.lastRun&&(e[t]=i.lastRun.toISOString());let n=p(process.cwd(),".agentx/cron");b(n)||O(n,{recursive:!0}),V(this.lastRunFile,JSON.stringify(e,null,2))}catch{}}loadLastRuns(){try{return b(this.lastRunFile)?JSON.parse(he(this.lastRunFile,"utf-8")):null}catch{return null}}logRun(e){try{let n=p(this.runsDir,e.jobId);b(n)||O(n,{recursive:!0});let t=`${e.startedAt.toISOString().replace(/[:.]/g,"-")}.json`;V(p(n,t),JSON.stringify(e,null,2))}catch{}}list(){return Array.from(this.jobs.values())}health(){let e=Array.from(this.jobs.values()),n=this.detectMissedRuns();return{healthy:e.filter(t=>t.enabled&&t.consecutiveErrors===0).length,failing:e.filter(t=>t.enabled&&t.consecutiveErrors>0).length,disabled:e.filter(t=>!t.enabled).length,missed:n.length,jobs:e.map(t=>({id:t.id,status:t.enabled?t.retryPending?"retrying":t.consecutiveErrors>0?"failing":"healthy":"disabled",consecutiveErrors:t.consecutiveErrors,lastError:t.lastError}))}}},A=D;x(A,"MAX_TIMEOUT_MS",2147483647);var Ee=20;function _e(r,e){let n=r.replace(/\s+$/,"").split(`
118
+ `);return n.length<=e?n.join(`
119
+ `):n.slice(-e).join(`
120
+ `)}function fe(r,e){return new Promise((n,t)=>{me("/bin/sh",["-c",r],{timeout:e,maxBuffer:8*1024*1024},(i,s,o)=>{if(i&&i.killed){t(new Error(`command timed out after ${Math.round(e/1e3)}s`));return}n({stdout:String(s??""),stderr:String(o??""),code:i?typeof i.code=="number"?i.code:1:0})})})}export{I as a,J as b,X as c,Le as d,G as e,j as f,W as g,A as h};
121
+ //# sourceMappingURL=chunk-FIEJDZVC.js.map