agentix-cli 0.26.0 → 0.28.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 (300) hide show
  1. package/README.md +67 -118
  2. package/apps/mac-helper/Sources/AgentXHelper/AXTree.swift +159 -0
  3. package/apps/mac-helper/Sources/AgentXHelper/Brand.swift +63 -0
  4. package/apps/mac-helper/Sources/AgentXHelper/Focus.swift +97 -0
  5. package/apps/mac-helper/Sources/AgentXHelper/HUD.swift +179 -0
  6. package/apps/mac-helper/Sources/AgentXHelper/OCR.swift +117 -0
  7. package/apps/mac-helper/Sources/AgentXHelper/Pointer.swift +309 -0
  8. package/apps/mac-helper/Sources/AgentXHelper/Screens.swift +67 -0
  9. package/apps/mac-helper/Sources/AgentXHelper/Typer.swift +185 -0
  10. package/apps/mac-helper/Sources/AgentXHelper/Vision.swift +201 -0
  11. package/apps/mac-helper/Sources/AgentXHelper/main.swift +265 -0
  12. package/apps/mac-helper/build.sh +39 -0
  13. package/apps/mac-voice/Resources/Info.plist +18 -0
  14. package/apps/mac-voice/Sources/AgentXVoice/AgentClient.swift +108 -0
  15. package/apps/mac-voice/Sources/AgentXVoice/Brand.swift +123 -0
  16. package/apps/mac-voice/Sources/AgentXVoice/Config.swift +68 -0
  17. package/apps/mac-voice/Sources/AgentXVoice/Hold.swift +74 -0
  18. package/apps/mac-voice/Sources/AgentXVoice/Hotkey.swift +76 -0
  19. package/apps/mac-voice/Sources/AgentXVoice/Markdown.swift +140 -0
  20. package/apps/mac-voice/Sources/AgentXVoice/Panel.swift +275 -0
  21. package/apps/mac-voice/Sources/AgentXVoice/Progress.swift +147 -0
  22. package/apps/mac-voice/Sources/AgentXVoice/Recorder.swift +139 -0
  23. package/apps/mac-voice/Sources/AgentXVoice/ResultCard.swift +162 -0
  24. package/apps/mac-voice/Sources/AgentXVoice/SmartPaste.swift +72 -0
  25. package/apps/mac-voice/Sources/AgentXVoice/Speech.swift +192 -0
  26. package/apps/mac-voice/Sources/AgentXVoice/StepLabel.swift +159 -0
  27. package/apps/mac-voice/Sources/AgentXVoice/main.swift +380 -0
  28. package/apps/mac-voice/build.sh +32 -0
  29. package/dist/{App-BPNHIGUX.js → App-CKPH3HHF.js} +2 -2
  30. package/dist/{ChatApp-6XSSZB4U.js → ChatApp-MX7IG4K2.js} +2 -2
  31. package/dist/agent-2U574KBZ.js +2 -0
  32. package/dist/article-fields-L4D2BOGY.js +2 -0
  33. package/dist/article-quality-44ONVGLN.js +3 -0
  34. package/dist/article-quality-44ONVGLN.js.map +1 -0
  35. package/dist/{audit-JU3HWISB.js → audit-4245CX6P.js} +2 -2
  36. package/dist/backfill-MTD2B62I.js +6 -0
  37. package/dist/backfill-MTD2B62I.js.map +1 -0
  38. package/dist/{board-dashboard-M3372FLO.js → board-dashboard-QR7CVTKA.js} +1411 -453
  39. package/dist/board-dashboard-QR7CVTKA.js.map +1 -0
  40. package/dist/{builtin-FRKAQMFS.js → builtin-B67DMJ3M.js} +2 -2
  41. package/dist/calibration-ILS5OI44.js +2 -0
  42. package/dist/{chunk-V3U4OF7L.js → chunk-3LYM22IV.js} +46 -48
  43. package/dist/chunk-3LYM22IV.js.map +1 -0
  44. package/dist/chunk-3SCPRYJB.js +5 -0
  45. package/dist/chunk-3SCPRYJB.js.map +1 -0
  46. package/dist/chunk-4BR7D2OU.js +36 -0
  47. package/dist/chunk-4BR7D2OU.js.map +1 -0
  48. package/dist/{chunk-P4VWIVKN.js → chunk-52ZPELD2.js} +403 -29
  49. package/dist/chunk-52ZPELD2.js.map +1 -0
  50. package/dist/chunk-5IA2CYHN.js +5 -0
  51. package/dist/{chunk-O6YPEOQF.js.map → chunk-5IA2CYHN.js.map} +1 -1
  52. package/dist/chunk-64CAIB3C.js +9 -0
  53. package/dist/chunk-64CAIB3C.js.map +1 -0
  54. package/dist/{chunk-5U35WAZF.js → chunk-77OV7346.js} +6 -6
  55. package/dist/chunk-77OV7346.js.map +1 -0
  56. package/dist/chunk-AJ5RMU7X.js +2 -0
  57. package/dist/chunk-AJ5RMU7X.js.map +1 -0
  58. package/dist/chunk-AJONZWLT.js +2 -0
  59. package/dist/chunk-AJONZWLT.js.map +1 -0
  60. package/dist/chunk-BH3GTCZ6.js +2 -0
  61. package/dist/chunk-BH3GTCZ6.js.map +1 -0
  62. package/dist/chunk-E3UJHQGN.js +1189 -0
  63. package/dist/chunk-E3UJHQGN.js.map +1 -0
  64. package/dist/chunk-EVWEG3VD.js +3 -0
  65. package/dist/chunk-EVWEG3VD.js.map +1 -0
  66. package/dist/chunk-F4HZTZHW.js +2 -0
  67. package/dist/chunk-F4HZTZHW.js.map +1 -0
  68. package/dist/chunk-F6CNF7V3.js +25 -0
  69. package/dist/chunk-F6CNF7V3.js.map +1 -0
  70. package/dist/chunk-FIOB3MEX.js +2 -0
  71. package/dist/{chunk-H5D7ATXS.js → chunk-FKBGJYWQ.js} +5 -5
  72. package/dist/chunk-FKBGJYWQ.js.map +1 -0
  73. package/dist/{chunk-BVGJSPW4.js → chunk-G2HMRDUQ.js} +7 -7
  74. package/dist/chunk-G2HMRDUQ.js.map +1 -0
  75. package/dist/chunk-H2QQU2XV.js +109 -0
  76. package/dist/chunk-H2QQU2XV.js.map +1 -0
  77. package/dist/{chunk-LDHPMJ7M.js → chunk-IWMW5432.js} +3 -3
  78. package/dist/chunk-IWMW5432.js.map +1 -0
  79. package/dist/chunk-K33GIBSE.js +2 -0
  80. package/dist/chunk-K33GIBSE.js.map +1 -0
  81. package/dist/chunk-L2WSPY6S.js +2 -0
  82. package/dist/chunk-L2WSPY6S.js.map +1 -0
  83. package/dist/chunk-LCIREYTD.js +54 -0
  84. package/dist/chunk-LCIREYTD.js.map +1 -0
  85. package/dist/chunk-MHIMGM4G.js +126 -0
  86. package/dist/chunk-MHIMGM4G.js.map +1 -0
  87. package/dist/chunk-P5ZBBEPL.js +13 -0
  88. package/dist/chunk-P5ZBBEPL.js.map +1 -0
  89. package/dist/{chunk-JTJIPNXY.js → chunk-PNWNVOEZ.js} +1 -1
  90. package/dist/chunk-PNWNVOEZ.js.map +1 -0
  91. package/dist/chunk-QHTQPBHU.js +45 -0
  92. package/dist/chunk-QHTQPBHU.js.map +1 -0
  93. package/dist/chunk-S2MNL5V6.js +2 -0
  94. package/dist/chunk-S2MNL5V6.js.map +1 -0
  95. package/dist/chunk-SCTIUFL4.js +5 -0
  96. package/dist/{chunk-K2CAPYES.js.map → chunk-SCTIUFL4.js.map} +1 -1
  97. package/dist/chunk-ULLAE6LE.js +157 -0
  98. package/dist/chunk-ULLAE6LE.js.map +1 -0
  99. package/dist/{chunk-DJEZ2HVU.js → chunk-VDMYBPYB.js} +1 -1
  100. package/dist/chunk-VDMYBPYB.js.map +1 -0
  101. package/dist/chunk-VIH7ARPF.js +547 -0
  102. package/dist/chunk-VIH7ARPF.js.map +1 -0
  103. package/dist/chunk-W5C2GDNC.js +32 -0
  104. package/dist/{chunk-JP5IEXR7.js.map → chunk-W5C2GDNC.js.map} +1 -1
  105. package/dist/chunk-WPMX3VGI.js +3 -0
  106. package/dist/chunk-WPMX3VGI.js.map +1 -0
  107. package/dist/chunk-YLBERT7N.js +24 -0
  108. package/dist/chunk-YLBERT7N.js.map +1 -0
  109. package/dist/chunk-YLGIZRHV.js +114 -0
  110. package/dist/chunk-YLGIZRHV.js.map +1 -0
  111. package/dist/chunk-ZMYGXHPT.js +121 -0
  112. package/dist/chunk-ZMYGXHPT.js.map +1 -0
  113. package/dist/chunk-ZTEU5JVQ.js +2 -0
  114. package/dist/chunk-ZTEU5JVQ.js.map +1 -0
  115. package/dist/{chunk-2WVX3LEC.js → chunk-ZVP6GB4G.js} +1 -1
  116. package/dist/chunk-ZVP6GB4G.js.map +1 -0
  117. package/dist/cli.js +1 -1
  118. package/dist/cli.js.map +1 -1
  119. package/dist/client-ZLSXGPTV.js +2 -0
  120. package/dist/{compaction-NWEMOSHA.js → compaction-FD4R23HZ.js} +4 -4
  121. package/dist/config-HMW3EBZL.js +2 -0
  122. package/dist/config-mutator-4SZ5YEVD.js +2 -0
  123. package/dist/{conflict-detector-D7VIYPDI.js → conflict-detector-ZPOT3M2C.js} +2 -2
  124. package/dist/conflict-detector-ZPOT3M2C.js.map +1 -0
  125. package/dist/context-planner-AQHJMVY2.js +17 -0
  126. package/dist/context-planner-AQHJMVY2.js.map +1 -0
  127. package/dist/debug-3MEUO2P2.js +2 -0
  128. package/dist/{discover-XJXQXXIO.js → discover-D4VVRNAI.js} +2 -2
  129. package/dist/discover-D4VVRNAI.js.map +1 -0
  130. package/dist/doctor-LRYV3QLK.js +2 -0
  131. package/dist/editor-chat-OB7IQOB5.js +137 -0
  132. package/dist/editor-chat-OB7IQOB5.js.map +1 -0
  133. package/dist/entry-triage-LK34TSQD.js +2 -0
  134. package/dist/entry-triage-LK34TSQD.js.map +1 -0
  135. package/dist/facts-YVEJOM7O.js +22 -0
  136. package/dist/facts-YVEJOM7O.js.map +1 -0
  137. package/dist/heal-FVLBHYP7.js +2 -0
  138. package/dist/index.d.ts +1040 -64
  139. package/dist/index.js +12 -19
  140. package/dist/index.js.map +1 -1
  141. package/dist/{ingest-whatsapp-LZE62INA.js → ingest-whatsapp-NS4OWDOU.js} +2 -2
  142. package/dist/loader-4ZGTK7LD.js +2 -0
  143. package/dist/loader-BOKE5M5S.js +2 -0
  144. package/dist/memory-extract-F5ZHWY5E.js +2 -0
  145. package/dist/{migrate-v2-QO7SLKIM.js → migrate-v2-V4WN2VVM.js} +2 -2
  146. package/dist/ntfy-X7EPB3TG.js +4 -0
  147. package/dist/ntfy-X7EPB3TG.js.map +1 -0
  148. package/dist/processes-WU23U2OJ.js +2 -0
  149. package/dist/program-GSZ5AYAM.js +2 -0
  150. package/dist/{projects-api-KRME2YKZ.js → projects-api-QE6L65SK.js} +2 -2
  151. package/dist/{projects-mutate-3NHVG437.js → projects-mutate-MY7JTLSZ.js} +2 -2
  152. package/dist/providers-KO2DODIO.js +2 -0
  153. package/dist/query-SQDHE4HV.js +2 -0
  154. package/dist/questions-4B4KSPXG.js +3 -0
  155. package/dist/questions-4B4KSPXG.js.map +1 -0
  156. package/dist/recipes-7S4MC5OJ.js +2 -0
  157. package/dist/registry-LE6FWYZC.js +2 -0
  158. package/dist/review-candidates-RQU5KNZM.js +4 -0
  159. package/dist/review-candidates-RQU5KNZM.js.map +1 -0
  160. package/dist/review-store-B4YE3EFK.js +4 -0
  161. package/dist/review-store-B4YE3EFK.js.map +1 -0
  162. package/dist/{rotation-memo-B3EXXSEH.js → rotation-memo-7T75BLEX.js} +2 -2
  163. package/dist/routing-ILERJXB6.js +2 -0
  164. package/dist/runner-YRBVNYTY.js +2 -0
  165. package/dist/scheduler-BFM3EINS.js +2 -0
  166. package/dist/seat-7WM7II2B.js +2 -0
  167. package/dist/select-X4KVMOP5.js +2 -0
  168. package/dist/select-X4KVMOP5.js.map +1 -0
  169. package/dist/store-MCOA6N4F.js +2 -0
  170. package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
  171. package/dist/{surface-usage-PRL6Z4U3.js → surface-usage-NS74AXSE.js} +2 -2
  172. package/dist/timers-PCB4ZIBD.js +2 -0
  173. package/dist/triage-backtest-JNO7RKX7.js +2 -0
  174. package/dist/triage-backtest-JNO7RKX7.js.map +1 -0
  175. package/dist/types-H5ENIUIT.js +2 -0
  176. package/dist/types-OIFU3KSQ.js +2 -0
  177. package/dist/types-OIFU3KSQ.js.map +1 -0
  178. package/dist/web/workflow-editor.global.js +1542 -0
  179. package/dist/web/workflow-editor.global.js.map +1 -0
  180. package/dist/{whatsapp-state-XMTCSRXU.js → whatsapp-state-6D6FHHD2.js} +2 -2
  181. package/dist/wiki-2AUFSIKO.js +2 -0
  182. package/dist/wiki-2AUFSIKO.js.map +1 -0
  183. package/dist/workflows/templates/branching.yaml +1 -1
  184. package/dist/{workspace-setup-TBQAHYHJ.js → workspace-setup-B3F3TUDE.js} +2 -2
  185. package/dist/workspace-setup-B3F3TUDE.js.map +1 -0
  186. package/package.json +14 -3
  187. package/scripts/postinstall.mjs +80 -0
  188. package/dist/agent-5QMITZT6.js +0 -2
  189. package/dist/architect-SEPWMWS4.js +0 -2
  190. package/dist/board-dashboard-M3372FLO.js.map +0 -1
  191. package/dist/chunk-2WVX3LEC.js.map +0 -1
  192. package/dist/chunk-3WQFCIB6.js +0 -24
  193. package/dist/chunk-3WQFCIB6.js.map +0 -1
  194. package/dist/chunk-4YCH6IZV.js +0 -2
  195. package/dist/chunk-53NRX2NA.js +0 -2
  196. package/dist/chunk-53NRX2NA.js.map +0 -1
  197. package/dist/chunk-5U35WAZF.js.map +0 -1
  198. package/dist/chunk-BFXONUUN.js +0 -108
  199. package/dist/chunk-BFXONUUN.js.map +0 -1
  200. package/dist/chunk-BFZUG66X.js +0 -45
  201. package/dist/chunk-BFZUG66X.js.map +0 -1
  202. package/dist/chunk-BTHHSY7D.js +0 -13
  203. package/dist/chunk-BTHHSY7D.js.map +0 -1
  204. package/dist/chunk-BVGJSPW4.js.map +0 -1
  205. package/dist/chunk-D46VZ7K7.js +0 -116
  206. package/dist/chunk-D46VZ7K7.js.map +0 -1
  207. package/dist/chunk-DJEZ2HVU.js.map +0 -1
  208. package/dist/chunk-E5CXCI33.js +0 -155
  209. package/dist/chunk-E5CXCI33.js.map +0 -1
  210. package/dist/chunk-FDWOECJY.js +0 -54
  211. package/dist/chunk-FDWOECJY.js.map +0 -1
  212. package/dist/chunk-H5D7ATXS.js.map +0 -1
  213. package/dist/chunk-HR4JSRZS.js +0 -3
  214. package/dist/chunk-HR4JSRZS.js.map +0 -1
  215. package/dist/chunk-ISEBLHWX.js +0 -118
  216. package/dist/chunk-ISEBLHWX.js.map +0 -1
  217. package/dist/chunk-JP5IEXR7.js +0 -32
  218. package/dist/chunk-JTJIPNXY.js.map +0 -1
  219. package/dist/chunk-K2CAPYES.js +0 -5
  220. package/dist/chunk-LDHPMJ7M.js.map +0 -1
  221. package/dist/chunk-METYYZKQ.js +0 -1115
  222. package/dist/chunk-METYYZKQ.js.map +0 -1
  223. package/dist/chunk-MTAPOCGA.js +0 -47
  224. package/dist/chunk-MTAPOCGA.js.map +0 -1
  225. package/dist/chunk-O6YPEOQF.js +0 -5
  226. package/dist/chunk-P4VWIVKN.js.map +0 -1
  227. package/dist/chunk-SR3X25TN.js +0 -3
  228. package/dist/chunk-SR3X25TN.js.map +0 -1
  229. package/dist/chunk-UHE5FCPB.js +0 -431
  230. package/dist/chunk-UHE5FCPB.js.map +0 -1
  231. package/dist/chunk-V3U4OF7L.js.map +0 -1
  232. package/dist/chunk-VJUTQAPJ.js +0 -2
  233. package/dist/chunk-VJUTQAPJ.js.map +0 -1
  234. package/dist/chunk-VT5XYFDD.js +0 -126
  235. package/dist/chunk-VT5XYFDD.js.map +0 -1
  236. package/dist/client-HGKAC6VP.js +0 -2
  237. package/dist/config-A4RWEJHV.js +0 -2
  238. package/dist/config-mutator-MNSFUN7D.js +0 -2
  239. package/dist/conflict-detector-D7VIYPDI.js.map +0 -1
  240. package/dist/context-planner-45GEWKGL.js +0 -17
  241. package/dist/context-planner-45GEWKGL.js.map +0 -1
  242. package/dist/debug-N3B5NVJU.js +0 -2
  243. package/dist/discover-XJXQXXIO.js.map +0 -1
  244. package/dist/doctor-3Q36ZFE6.js +0 -2
  245. package/dist/heal-HZ3EQD52.js +0 -2
  246. package/dist/loader-5CA3O5GM.js +0 -2
  247. package/dist/loader-6IHUCPVO.js +0 -2
  248. package/dist/memory-extract-AENFVVO5.js +0 -2
  249. package/dist/processes-UIC4XXA6.js +0 -2
  250. package/dist/program-SAUNRCCD.js +0 -2
  251. package/dist/providers-L27BJ5A6.js +0 -2
  252. package/dist/query-HZYYRCLU.js +0 -2
  253. package/dist/recipes-EPLE2SNT.js +0 -2
  254. package/dist/registry-HK3WQ7BS.js +0 -2
  255. package/dist/runner-BFYDWPUO.js +0 -2
  256. package/dist/scheduler-6T2SU5ST.js +0 -2
  257. package/dist/store-I3HEWVLQ.js +0 -2
  258. package/dist/timers-5LPZN55J.js +0 -2
  259. package/dist/types-VECTMYTO.js +0 -2
  260. package/dist/types-Y6ZCZ52Z.js +0 -2
  261. package/dist/wiki-M6YNRFC4.js +0 -2
  262. /package/dist/{App-BPNHIGUX.js.map → App-CKPH3HHF.js.map} +0 -0
  263. /package/dist/{ChatApp-6XSSZB4U.js.map → ChatApp-MX7IG4K2.js.map} +0 -0
  264. /package/dist/{agent-5QMITZT6.js.map → agent-2U574KBZ.js.map} +0 -0
  265. /package/dist/{architect-SEPWMWS4.js.map → article-fields-L4D2BOGY.js.map} +0 -0
  266. /package/dist/{audit-JU3HWISB.js.map → audit-4245CX6P.js.map} +0 -0
  267. /package/dist/{builtin-FRKAQMFS.js.map → builtin-B67DMJ3M.js.map} +0 -0
  268. /package/dist/{chunk-4YCH6IZV.js.map → calibration-ILS5OI44.js.map} +0 -0
  269. /package/dist/{client-HGKAC6VP.js.map → chunk-FIOB3MEX.js.map} +0 -0
  270. /package/dist/{config-A4RWEJHV.js.map → client-ZLSXGPTV.js.map} +0 -0
  271. /package/dist/{compaction-NWEMOSHA.js.map → compaction-FD4R23HZ.js.map} +0 -0
  272. /package/dist/{config-mutator-MNSFUN7D.js.map → config-HMW3EBZL.js.map} +0 -0
  273. /package/dist/{debug-N3B5NVJU.js.map → config-mutator-4SZ5YEVD.js.map} +0 -0
  274. /package/dist/{doctor-3Q36ZFE6.js.map → debug-3MEUO2P2.js.map} +0 -0
  275. /package/dist/{heal-HZ3EQD52.js.map → doctor-LRYV3QLK.js.map} +0 -0
  276. /package/dist/{loader-5CA3O5GM.js.map → heal-FVLBHYP7.js.map} +0 -0
  277. /package/dist/{ingest-whatsapp-LZE62INA.js.map → ingest-whatsapp-NS4OWDOU.js.map} +0 -0
  278. /package/dist/{loader-6IHUCPVO.js.map → loader-4ZGTK7LD.js.map} +0 -0
  279. /package/dist/{memory-extract-AENFVVO5.js.map → loader-BOKE5M5S.js.map} +0 -0
  280. /package/dist/{processes-UIC4XXA6.js.map → memory-extract-F5ZHWY5E.js.map} +0 -0
  281. /package/dist/{migrate-v2-QO7SLKIM.js.map → migrate-v2-V4WN2VVM.js.map} +0 -0
  282. /package/dist/{program-SAUNRCCD.js.map → processes-WU23U2OJ.js.map} +0 -0
  283. /package/dist/{providers-L27BJ5A6.js.map → program-GSZ5AYAM.js.map} +0 -0
  284. /package/dist/{projects-api-KRME2YKZ.js.map → projects-api-QE6L65SK.js.map} +0 -0
  285. /package/dist/{projects-mutate-3NHVG437.js.map → projects-mutate-MY7JTLSZ.js.map} +0 -0
  286. /package/dist/{query-HZYYRCLU.js.map → providers-KO2DODIO.js.map} +0 -0
  287. /package/dist/{recipes-EPLE2SNT.js.map → query-SQDHE4HV.js.map} +0 -0
  288. /package/dist/{registry-HK3WQ7BS.js.map → recipes-7S4MC5OJ.js.map} +0 -0
  289. /package/dist/{runner-BFYDWPUO.js.map → registry-LE6FWYZC.js.map} +0 -0
  290. /package/dist/{rotation-memo-B3EXXSEH.js.map → rotation-memo-7T75BLEX.js.map} +0 -0
  291. /package/dist/{scheduler-6T2SU5ST.js.map → routing-ILERJXB6.js.map} +0 -0
  292. /package/dist/{store-I3HEWVLQ.js.map → runner-YRBVNYTY.js.map} +0 -0
  293. /package/dist/{surface-usage-PRL6Z4U3.js.map → scheduler-BFM3EINS.js.map} +0 -0
  294. /package/dist/{timers-5LPZN55J.js.map → seat-7WM7II2B.js.map} +0 -0
  295. /package/dist/{types-VECTMYTO.js.map → store-MCOA6N4F.js.map} +0 -0
  296. /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
  297. /package/dist/{types-Y6ZCZ52Z.js.map → surface-usage-NS74AXSE.js.map} +0 -0
  298. /package/dist/{wiki-M6YNRFC4.js.map → timers-PCB4ZIBD.js.map} +0 -0
  299. /package/dist/{workspace-setup-TBQAHYHJ.js.map → types-H5ENIUIT.js.map} +0 -0
  300. /package/dist/{whatsapp-state-XMTCSRXU.js.map → whatsapp-state-6D6FHHD2.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/channels/handover-store.ts","../src/workflows/store.ts","../src/workflows/yaml.ts","../src/workflows/yaml-roundtrip.ts","../src/workflows/run-store.ts","../src/workflows/layout-store.ts","../src/workflows/matcher.ts","../src/workflows/engine.ts","../src/workflows/template.ts","../src/workflows/node-concurrency.ts","../src/workflows/nodes/handlers.ts","../src/workflows/signals.ts","../src/intent/sources/workflow.ts","../src/storage/traces.ts","../src/workflows/dispatcher.ts","../src/workflows/hooks.ts","../src/workflows/triggers.ts","../src/workflows/absorb.ts","../src/workflows/inputs.ts"],"sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync } from \"fs\"\nimport { resolve, dirname } from \"path\"\n\n// --- Runtime routing overrides (per-chat handovers) ------------------------\n//\n// A \"handover\" is an operator-initiated override: \"from now on, route\n// messages from <channel, chatId[, accountId]> to <toAgent>, regardless of\n// the config's static routes.\" Lightweight — persisted as plain JSON, read\n// at the top of the router's resolveAgent, consulted before config routes.\n//\n// Survival: on daemon restart, overrides stay in effect. The file IS the\n// source of truth.\n//\n// Context carryover: each override may carry a one-shot `summary` string.\n// The registry reads + clears it the first time it injects it into the\n// target agent's context, so the new agent sees \"[Handover note: …]\" once,\n// then subsequent messages just flow to the new agent without repetition.\n\nexport interface HandoverOverride {\n channel: string // \"telegram\" | \"whatsapp\" | ...\n chatId: string // DM: sender.id; group: group.id\n accountId?: string // telegram multi-account discriminator\n fromAgent: string\n toAgent: string\n summary?: string // one-shot note injected into target context\n createdAt: string // ISO\n createdBy?: string // operator that made the call\n /** When set, overrides past this time are treated as expired (ignored on\n * read, optionally pruned on access). null = indefinite. */\n expiresAt?: string | null\n /** Set after the summary has been injected once. Route stays active. */\n summaryConsumedAt?: string\n}\n\ninterface HandoverFile {\n version: number\n overrides: Record<string, HandoverOverride>\n}\n\nexport class HandoverStore {\n readonly filePath: string\n private log: (...args: unknown[]) => void\n\n constructor(opts: { baseDir?: string; log?: (...args: unknown[]) => void } = {}) {\n const base = opts.baseDir ?? resolve(process.cwd(), \".agentx\")\n this.filePath = resolve(base, \"handover.json\")\n this.log = opts.log ?? console.error.bind(console, \"[handover]\")\n mkdirSync(base, { recursive: true })\n }\n\n /** Canonical key for an override. Using \"-\" for missing accountId keeps\n * the key shape uniform across channels. */\n key(channel: string, chatId: string, accountId?: string): string {\n return `${channel}::${chatId}::${accountId || \"-\"}`\n }\n\n load(): HandoverFile {\n if (!existsSync(this.filePath)) return { version: 1, overrides: {} }\n try {\n const raw = readFileSync(this.filePath, \"utf-8\")\n const parsed = JSON.parse(raw) as HandoverFile\n if (!parsed || typeof parsed !== \"object\") return { version: 1, overrides: {} }\n if (!parsed.overrides) parsed.overrides = {}\n return parsed\n } catch (e: any) {\n this.log(`load failed, starting empty: ${e?.message || e}`)\n return { version: 1, overrides: {} }\n }\n }\n\n private save(file: HandoverFile): void {\n mkdirSync(dirname(this.filePath), { recursive: true })\n const tmp = `${this.filePath}.tmp-${process.pid}`\n writeFileSync(tmp, JSON.stringify(file, null, 2))\n renameSync(tmp, this.filePath)\n }\n\n /** Return the active override for a chat, or undefined. Skips expired. */\n get(channel: string, chatId: string, accountId?: string): HandoverOverride | undefined {\n const file = this.load()\n const o = file.overrides[this.key(channel, chatId, accountId)]\n if (!o) return undefined\n if (o.expiresAt && Date.parse(o.expiresAt) < Date.now()) return undefined\n return o\n }\n\n /** Create or replace an override. If `fromAgent` isn't supplied the caller\n * can pass \"\" and the router will fill it in from current routing. */\n set(o: Omit<HandoverOverride, \"createdAt\"> & { createdAt?: string }): HandoverOverride {\n if (!o.channel || !o.chatId || !o.toAgent) {\n throw new Error(\"channel, chatId and toAgent are required\")\n }\n const file = this.load()\n const rec: HandoverOverride = {\n ...o,\n createdAt: o.createdAt ?? new Date().toISOString(),\n }\n file.overrides[this.key(rec.channel, rec.chatId, rec.accountId)] = rec\n this.save(file)\n return rec\n }\n\n /** Remove an override (release the handover). Idempotent. */\n remove(channel: string, chatId: string, accountId?: string): boolean {\n const file = this.load()\n const k = this.key(channel, chatId, accountId)\n if (!(k in file.overrides)) return false\n delete file.overrides[k]\n this.save(file)\n return true\n }\n\n /** Mark the summary as consumed (after it's been injected once). Route\n * stays active; subsequent messages just skip the note. */\n consumeSummary(channel: string, chatId: string, accountId?: string): string | undefined {\n const file = this.load()\n const o = file.overrides[this.key(channel, chatId, accountId)]\n if (!o || !o.summary || o.summaryConsumedAt) return undefined\n const summary = o.summary\n o.summaryConsumedAt = new Date().toISOString()\n this.save(file)\n return summary\n }\n\n /** List all non-expired overrides. Used by the admin UI. */\n listActive(): HandoverOverride[] {\n const file = this.load()\n const now = Date.now()\n return Object.values(file.overrides).filter(\n (o) => !o.expiresAt || Date.parse(o.expiresAt) >= now,\n )\n }\n\n /** Overrides pointing at this agent (incoming) or away from it (outgoing). */\n listByAgent(agentId: string): { incoming: HandoverOverride[]; outgoing: HandoverOverride[] } {\n const all = this.listActive()\n return {\n incoming: all.filter((o) => o.toAgent === agentId),\n outgoing: all.filter((o) => o.fromAgent === agentId),\n }\n }\n}\n","import { existsSync, mkdirSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve, relative } from \"path\"\nimport { lintWorkflow, workflowSchema, type Workflow } from \"./types\"\nimport { parseYamlWorkflow, WorkflowYamlError } from \"./yaml\"\nimport { renderWorkflowYamlPreservingComments } from \"./yaml-roundtrip\"\n\n// --- WorkflowStore (V2) ---\n//\n// Filesystem persistence for workflow definitions. JSON on disk, one file\n// per workflow, same convention as .agentx/procedures/ and .agentx/wiki/.\n// V2 workflows are DAGs (nodes + edges); see types.ts. The matching\n// dispatcher lives in dispatcher.ts and walks wf.nodes for the trigger.\n//\n// Move C — YAML files (`.yaml`/`.yml`) are loaded alongside JSON. The\n// editor still saves JSON; YAML is read-only authoring. When a workflow\n// has BOTH `<id>.yaml` and `<id>.json`, list()/validateAll() report the\n// collision rather than silently picking one — operators delete one.\n\nexport interface WorkflowStoreOptions {\n baseDir?: string\n}\n\nexport interface WorkflowValidation {\n workflow: Workflow\n issues: string[]\n isValid: boolean\n}\n\nexport class WorkflowStore {\n readonly baseDir: string\n\n constructor(opts: WorkflowStoreOptions = {}) {\n this.baseDir = opts.baseDir ?? resolve(process.cwd(), \".agentx/workflows\")\n mkdirSync(this.baseDir, { recursive: true })\n }\n\n private pathFor(id: string): string {\n return this.pathForJson(id)\n }\n\n private pathForJson(id: string): string {\n return resolve(this.baseDir, `${id}.json`)\n }\n\n private pathForYaml(id: string): string {\n return resolve(this.baseDir, `${id}.yaml`)\n }\n\n /** True for any *.json / *.yaml / *.yml file that doesn't start with `_`\n * (which is how run logs / index files are filtered out). */\n private isWorkflowFile(name: string): boolean {\n if (name.startsWith(\"_\")) return false\n return name.endsWith(\".json\") || name.endsWith(\".yaml\") || name.endsWith(\".yml\")\n }\n\n /** Strip the format extension to recover the workflow id. */\n private idFromFilename(name: string): string {\n return name.replace(/\\.(json|ya?ml)$/, \"\")\n }\n\n /** Parse a workflow file from disk based on its extension. JSON is the\n * canonical shape; YAML runs through the desugar pass first. */\n private readAndParse(name: string): unknown {\n const full = resolve(this.baseDir, name)\n const text = readFileSync(full, \"utf-8\")\n if (name.endsWith(\".json\")) return JSON.parse(text)\n return parseYamlWorkflow(text, { filePath: name })\n }\n\n /** Returns every well-formed workflow. Malformed files are skipped; the\n * CLI `validate` subcommand is where authors get details.\n *\n * Coexisting `<id>.json` + `<id>.yaml` is treated as a hard ambiguity\n * — neither variant is loaded, and the id is omitted from list().\n * validateAll() surfaces the collision with a clear message. */\n list(): Workflow[] {\n if (!existsSync(this.baseDir)) return []\n const byId = new Map<string, string[]>()\n for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !this.isWorkflowFile(entry.name)) continue\n const id = this.idFromFilename(entry.name)\n const list = byId.get(id) ?? []\n list.push(entry.name)\n byId.set(id, list)\n }\n const out: Workflow[] = []\n for (const [, files] of byId) {\n // Skip ambiguous ids — list() must never pick a winner silently.\n if (files.length > 1) continue\n try {\n const raw = this.readAndParse(files[0])\n const parsed = workflowSchema.safeParse(raw)\n if (parsed.success) out.push(parsed.data)\n } catch {\n // intentional: skip malformed\n }\n }\n return out.sort((a, b) => a.id.localeCompare(b.id))\n }\n\n validateAll(): Array<WorkflowValidation | { id: string; path: string; issues: string[]; isValid: false }> {\n const results: Array<WorkflowValidation | { id: string; path: string; issues: string[]; isValid: false }> = []\n if (!existsSync(this.baseDir)) return results\n\n const byId = new Map<string, string[]>()\n for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !this.isWorkflowFile(entry.name)) continue\n const id = this.idFromFilename(entry.name)\n const list = byId.get(id) ?? []\n list.push(entry.name)\n byId.set(id, list)\n }\n\n for (const [id, files] of byId) {\n // Coexisting <id>.json + <id>.yaml — surface BOTH paths with a\n // duplicate-id error attached to each so the operator sees what\n // to delete. Sort for deterministic output.\n if (files.length > 1) {\n const sorted = [...files].sort()\n for (const name of sorted) {\n results.push({\n id,\n path: relative(this.baseDir, resolve(this.baseDir, name)),\n issues: [`duplicate workflow id \"${id}\" — found ${sorted.join(\" and \")}; delete one to disambiguate`],\n isValid: false,\n })\n }\n continue\n }\n\n const name = files[0]\n const full = resolve(this.baseDir, name)\n const rel = relative(this.baseDir, full)\n let raw: unknown\n try {\n raw = this.readAndParse(name)\n } catch (e: any) {\n const reason =\n e instanceof WorkflowYamlError\n ? e.message\n : `parse error: ${e?.message ?? e}`\n results.push({ id, path: rel, issues: [reason], isValid: false })\n continue\n }\n const parsed = workflowSchema.safeParse(raw)\n if (!parsed.success) {\n const issues = parsed.error.issues.map((i) => `${i.path.join(\".\") || \"<root>\"}: ${i.message}`)\n results.push({ id: String((raw as any)?.id ?? id), path: rel, issues, isValid: false })\n continue\n }\n const lintIssues = lintWorkflow(parsed.data)\n results.push({ workflow: parsed.data, issues: lintIssues, isValid: lintIssues.length === 0 })\n }\n return results\n }\n\n get(id: string): Workflow | null {\n const jsonPath = this.pathForJson(id)\n const yamlPath = this.pathForYaml(id)\n const ymlPath = resolve(this.baseDir, `${id}.yml`)\n const haveJson = existsSync(jsonPath)\n const haveYaml = existsSync(yamlPath) || existsSync(ymlPath)\n // Coexistence is ambiguous at validateAll time; here we refuse to\n // pick a winner so callers don't silently get whichever the file\n // system orders first. Returning null mirrors \"no such workflow\".\n if (haveJson && haveYaml) return null\n if (haveJson) {\n try {\n const raw = JSON.parse(readFileSync(jsonPath, \"utf-8\"))\n const parsed = workflowSchema.safeParse(raw)\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n const yPath = existsSync(yamlPath) ? yamlPath : (existsSync(ymlPath) ? ymlPath : null)\n if (yPath) {\n try {\n const text = readFileSync(yPath, \"utf-8\")\n const raw = parseYamlWorkflow(text, { filePath: relative(this.baseDir, yPath) })\n const parsed = workflowSchema.safeParse(raw)\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n // Filename ≠ id fallback. Authors sometimes pick a short filename\n // for an `id:` that's longer / differently namespaced (e.g.\n // `ksi-mr-fix-loop.yaml` containing `id: ksi-int-ksi-tn-mr-fix-loop`).\n // list() finds these because it scans the directory; the per-id\n // fast paths above don't. Scan as a last resort so the editor's\n // GET /api/workflows/:id can still load the workflow.\n if (!existsSync(this.baseDir)) return null\n for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !this.isWorkflowFile(entry.name)) continue\n const filenameId = this.idFromFilename(entry.name)\n if (filenameId === id) continue // already tried above\n try {\n const raw = this.readAndParse(entry.name)\n const parsed = workflowSchema.safeParse(raw)\n if (parsed.success && parsed.data.id === id) return parsed.data\n } catch {\n // skip malformed file\n }\n }\n return null\n }\n\n save(workflow: Workflow, opts: { convertFromYaml?: boolean } = {}): Workflow {\n const now = new Date().toISOString().slice(0, 10)\n const normalized = workflowSchema.parse({\n ...workflow,\n created: workflow.created || now,\n updated: now,\n })\n // YAML-authored workflows round-trip back to YAML so the editor\n // doesn't silently strip the operator's comments. yaml@2's Document\n // API preserves commentBefore on top-level keys and per-node items,\n // so per-node `# ...` blocks survive a structural edit. Comments tied\n // to deleted keys / removed nodes are dropped by design (no honest\n // place to relocate them).\n //\n // `convertFromYaml: true` is the explicit \"I want JSON\" opt-in: the\n // editor surfaces a confirm modal (\"you'll lose comments\"), then\n // retries with this flag set. We delete the YAML and write JSON.\n const yamlPath = this.pathForYaml(normalized.id)\n const ymlPath = resolve(this.baseDir, `${normalized.id}.yml`)\n let existingYaml = existsSync(yamlPath) ? yamlPath : (existsSync(ymlPath) ? ymlPath : null)\n // Filename ≠ id fallback (matches get()'s fallback): if no YAML at\n // <id>.yaml, scan for any *.ya?ml whose parsed `id:` equals this\n // workflow's id and round-trip THAT file. Without this, a YAML\n // named `ksi-mr-fix-loop.yaml` (id `ksi-int-ksi-tn-mr-fix-loop`)\n // would slip through to the JSON-write path below, leaving the\n // YAML as an orphan and creating a coexistence trap on next load.\n if (!existingYaml && existsSync(this.baseDir)) {\n for (const entry of readdirSync(this.baseDir, { withFileTypes: true })) {\n if (!entry.isFile() || !this.isWorkflowFile(entry.name)) continue\n if (entry.name.endsWith(\".json\")) continue\n try {\n const raw = this.readAndParse(entry.name)\n const parsed = workflowSchema.safeParse(raw)\n if (parsed.success && parsed.data.id === normalized.id) {\n existingYaml = resolve(this.baseDir, entry.name)\n break\n }\n } catch { /* skip malformed */ }\n }\n }\n if (existingYaml && opts.convertFromYaml) {\n unlinkSync(existingYaml)\n writeFileSync(this.pathForJson(normalized.id), JSON.stringify(normalized, null, 2) + \"\\n\")\n return normalized\n }\n if (existingYaml) {\n const originalText = readFileSync(existingYaml, \"utf-8\")\n try {\n const out = renderWorkflowYamlPreservingComments(originalText, normalized)\n writeFileSync(existingYaml, out)\n return normalized\n } catch (e: any) {\n // Round-trip should never fail on a file we just successfully\n // parsed elsewhere, but if the merge throws (e.g. the source has\n // a structure we don't understand), surface the failure rather\n // than silently fall through to JSON.\n const err = new Error(\n `yaml round-trip failed for \"${normalized.id}\" at ${existingYaml}: ${e?.message ?? e}`,\n ) as Error & { yamlPath?: string }\n err.yamlPath = existingYaml\n throw err\n }\n }\n writeFileSync(this.pathForJson(normalized.id), JSON.stringify(normalized, null, 2) + \"\\n\")\n return normalized\n }\n\n delete(id: string): boolean {\n const jsonPath = this.pathForJson(id)\n const yamlPath = this.pathForYaml(id)\n const ymlPath = resolve(this.baseDir, `${id}.yml`)\n let removed = false\n if (existsSync(jsonPath)) { unlinkSync(jsonPath); removed = true }\n if (existsSync(yamlPath)) { unlinkSync(yamlPath); removed = true }\n if (existsSync(ymlPath)) { unlinkSync(ymlPath); removed = true }\n return removed\n }\n}\n","import yaml from \"js-yaml\"\n\n// --- Move C — YAML workflow authoring ---\n//\n// Workflows are still defined by `workflowSchema` in types.ts; this\n// module only provides:\n// 1) parseYamlWorkflow(text) — js-yaml + a guard against multi-doc\n// streams (we only accept a single workflow per file in v1).\n// 2) desugarFlow(raw) — expand the optional top-level `flow: [a,b,c]`\n// sugar into explicit edges, then strip the `flow` key so Zod\n// validation sees the canonical shape.\n//\n// Why a `flow:` shorthand? Linear sequences are the common case for\n// authors and they're verbose to express via parallel `nodes:` +\n// `edges:` lists. `flow:` lets you read a workflow top-to-bottom.\n// Branches, parallel gateways, signals, and loops still require\n// explicit edges — those have semantics that no array can imply.\n//\n// Sugar limits (enforced at parse time so authors get a clear error\n// rather than a strange runtime behaviour):\n// - flow: cannot include node ids whose type is one of:\n// branch, gateway.parallel, rule, signal.wait,\n// subProcess, timer.boundary, checkpoint.\n// - flow: ids must reference real nodes in the same file.\n// - flow: and edges: may coexist; the result is union + dedup.\n// - Multi-document YAML (`---`) is rejected — one workflow per file.\n\n/** Node types that have multi-port semantics or human/timed\n * suspension and therefore must use explicit `edges:` rather than\n * `flow:`. Mirroring this list in the doc keeps authors honest. */\nconst FLOW_FORBIDDEN_TYPES = new Set([\n \"branch\",\n \"gateway.parallel\",\n \"rule\",\n \"signal.wait\",\n \"subProcess\",\n \"timer.boundary\",\n \"checkpoint\",\n])\n\nexport interface ParseYamlOptions {\n /** Optional file path used only to enrich error messages. */\n filePath?: string\n}\n\n/** Thrown when the YAML structure is invalid before Zod ever sees it.\n * Distinct class so the CLI can render parser errors with line/col\n * hints without conflating them with schema errors. */\nexport class WorkflowYamlError extends Error {\n constructor(message: string, public readonly mark?: yaml.Mark) {\n super(message)\n this.name = \"WorkflowYamlError\"\n }\n}\n\n/** Parse and desugar — returns the JSON-shaped workflow object ready\n * to feed `workflowSchema.safeParse()`. Does NOT validate the schema;\n * callers (CLI, store) run Zod and surface its issues separately. */\nexport function parseYamlWorkflow(text: string, opts: ParseYamlOptions = {}): unknown {\n if (!text || text.trim() === \"\") {\n throw new WorkflowYamlError(\n `${prefix(opts)}empty file — workflow YAML must contain at least an id, version, title, and one node.`,\n )\n }\n\n // js-yaml's loadAll detects multi-doc streams. We support exactly\n // one document per file in v1 — multi-doc encourages copy-paste-edit\n // patterns that hide later docs from the validator.\n let docs: unknown[]\n try {\n docs = yaml.loadAll(text)\n } catch (e: any) {\n const mark = e?.mark as yaml.Mark | undefined\n throw new WorkflowYamlError(\n `${prefix(opts)}YAML parse error: ${e?.reason ?? e?.message ?? e}` +\n (mark ? ` (line ${mark.line + 1}, column ${mark.column + 1})` : \"\"),\n mark,\n )\n }\n if (docs.length === 0 || (docs.length === 1 && docs[0] == null)) {\n throw new WorkflowYamlError(`${prefix(opts)}YAML produced no document.`)\n }\n if (docs.length > 1) {\n throw new WorkflowYamlError(\n `${prefix(opts)}multi-document YAML is not supported in v1; one workflow per file.`,\n )\n }\n const raw = docs[0]\n if (typeof raw !== \"object\" || raw === null || Array.isArray(raw)) {\n const got = Array.isArray(raw) ? \"array\" : typeof raw\n throw new WorkflowYamlError(`${prefix(opts)}top-level YAML must be a mapping (got ${got}).`)\n }\n\n return desugarFlow(raw as Record<string, unknown>, opts)\n}\n\n/** Expand `flow: [a,b,c]` into edges and strip the `flow` key. Also\n * validates the rules listed at the top of this file. Pure function\n * on the parsed-but-not-yet-validated object. */\nexport function desugarFlow(\n raw: Record<string, unknown>,\n opts: ParseYamlOptions = {},\n): unknown {\n if (!(\"flow\" in raw)) return raw\n\n const flow = raw.flow\n if (flow === undefined || flow === null) {\n // Treat null/undefined as if `flow:` weren't present — drop the key\n // and pass through.\n const out: Record<string, unknown> = { ...raw }\n delete out.flow\n return out\n }\n if (!Array.isArray(flow)) {\n throw new WorkflowYamlError(`${prefix(opts)}flow must be an array of node ids (got ${typeof flow}).`)\n }\n for (const item of flow) {\n if (typeof item !== \"string\") {\n throw new WorkflowYamlError(`${prefix(opts)}flow entries must be strings (got ${typeof item}).`)\n }\n }\n const flowIds = flow as string[]\n\n // Cross-check against the nodes[] in the same file. We only need id +\n // type for the flow-rule check; any malformed node entry is left for\n // Zod to surface with a precise path.\n const nodes = Array.isArray(raw.nodes) ? (raw.nodes as Array<Record<string, unknown>>) : []\n const idToType = new Map<string, string>()\n for (const n of nodes) {\n if (n && typeof n === \"object\" && typeof n.id === \"string\" && typeof n.type === \"string\") {\n idToType.set(n.id, n.type)\n }\n }\n\n for (const id of flowIds) {\n if (!idToType.has(id)) {\n throw new WorkflowYamlError(`${prefix(opts)}flow references unknown node \"${id}\".`)\n }\n const type = idToType.get(id)!\n if (FLOW_FORBIDDEN_TYPES.has(type)) {\n throw new WorkflowYamlError(\n `${prefix(opts)}flow cannot include ${type} node \"${id}\"; use explicit edges for ${type} nodes.`,\n )\n }\n }\n\n // Synthesize linear edges. Single-id flows produce nothing — their\n // \"edges\" are degenerate. Use Map keyed by from→fromPort→to so we can\n // dedup against any pre-existing entries in raw.edges.\n const synthesized: Array<{ from: string; to: string }> = []\n for (let i = 0; i < flowIds.length - 1; i++) {\n synthesized.push({ from: flowIds[i], to: flowIds[i + 1] })\n }\n\n // Union with explicit edges, deduping on (from, fromPort, to). Explicit\n // edges win when a synthesized edge would shadow them — the explicit\n // form may carry a label or a port the sugar doesn't know about.\n const explicit: Array<Record<string, unknown>> = Array.isArray(raw.edges)\n ? (raw.edges as Array<Record<string, unknown>>)\n : []\n const seen = new Set<string>()\n const unioned: Array<Record<string, unknown>> = []\n\n for (const e of explicit) {\n if (!e || typeof e !== \"object\") continue\n const k = edgeKey(e.from, e.fromPort, e.to)\n if (k && !seen.has(k)) {\n seen.add(k)\n unioned.push(e)\n } else if (!k) {\n // Pass through ill-formed edges so Zod can surface them with a\n // precise path. Don't drop silently.\n unioned.push(e)\n }\n }\n for (const e of synthesized) {\n const k = edgeKey(e.from, undefined, e.to)\n if (k && !seen.has(k)) {\n seen.add(k)\n unioned.push(e)\n }\n }\n\n const out: Record<string, unknown> = { ...raw, edges: unioned }\n delete out.flow\n return out\n}\n\nfunction edgeKey(from: unknown, fromPort: unknown, to: unknown): string | null {\n if (typeof from !== \"string\" || typeof to !== \"string\") return null\n return `${from}::${typeof fromPort === \"string\" ? fromPort : \"\"}::${to}`\n}\n\nfunction prefix(opts: ParseYamlOptions): string {\n return opts.filePath ? `${opts.filePath}: ` : \"\"\n}\n\n/** Render a JSON workflow back to YAML. One-way (not lossless against\n * `flow:` sugar — round-tripped output is always the canonical\n * nodes/edges form). Used by `agentx workflow show --format yaml`. */\nexport function renderWorkflowYaml(workflow: unknown): string {\n return yaml.dump(workflow, { noRefs: true, lineWidth: 100, sortKeys: false })\n}\n","import { Document, isMap, isSeq, isScalar, parseDocument, YAMLMap, YAMLSeq, type Node, type Pair } from \"yaml\"\nimport type { Workflow } from \"./types\"\n\n// --- YAML round-trip save (Move D) ---\n//\n// Preserves comments and key-ordering when the editor writes back to a\n// YAML-authored workflow file. Without this, every editor save would\n// either (a) refuse to overwrite the YAML (the old behaviour) or (b)\n// regenerate canonical YAML from JSON, dropping every `# ...` comment\n// the operator wrote in the file.\n//\n// Strategy: parse the existing file as a yaml@2 Document (which retains\n// per-key + per-item commentBefore/commentAfter metadata), then merge\n// the new workflow object into it in-place:\n//\n// * top-level keys: preserve order for keys present in both old and\n// new; append new keys; drop removed keys (e.g. `flow:` becomes\n// `edges:` after a structural edit)\n// * arrays-of-objects with an `id` field (nodes, agents, …): merge by\n// id, keeping the original Pair (and its commentBefore) when ids\n// match. Re-order to match the new array. Append entries for new\n// ids; drop entries whose id was removed.\n// * arrays-of-objects without `id` (edges): replace wholesale —\n// edges don't carry author comments in practice.\n// * scalars + other shapes: replace.\n//\n// Comments tied to deleted keys / removed nodes are lost (there's no\n// honest place to relocate them). The document-level header comment\n// (`doc.commentBefore`) is always kept.\n\nexport function renderWorkflowYamlPreservingComments(originalText: string, workflow: Workflow): string {\n const doc = parseDocument(originalText)\n if (!doc.contents || !isMap(doc.contents)) {\n // Source file isn't a mapping — fall back to a clean dump of the new\n // workflow. The header comment (if any) carries forward via\n // commentBefore on the new contents.\n const fresh = new Document(workflow as unknown)\n if (doc.commentBefore) fresh.commentBefore = doc.commentBefore\n return String(fresh)\n }\n mergeMapInPlace(doc, doc.contents, workflow as unknown as Record<string, unknown>)\n return String(doc)\n}\n\nfunction mergeMapInPlace(doc: Document, map: YAMLMap, next: Record<string, unknown>): void {\n const newKeys = Object.keys(next)\n const newKeySet = new Set(newKeys)\n const oldPairs = new Map<string, Pair>()\n for (const pair of map.items) {\n const k = scalarKey(pair)\n if (k !== null) oldPairs.set(k, pair)\n }\n\n const merged: Pair[] = []\n for (const key of newKeys) {\n const value = next[key]\n const existing = oldPairs.get(key)\n if (existing) {\n mergeValueInPlace(doc, existing, value)\n merged.push(existing)\n } else {\n merged.push(doc.createPair(key, value))\n }\n }\n // Keep dropped pairs out of the mapping. Their commentBefore is lost\n // by design — orphaned comments would render in the wrong place.\n void newKeySet\n map.items = merged\n}\n\nfunction mergeValueInPlace(doc: Document, pair: Pair, nextValue: unknown): void {\n const oldVal = pair.value as Node | null\n\n // null/undefined → blank scalar\n if (nextValue === null || nextValue === undefined) {\n pair.value = doc.createNode(null) as Node\n return\n }\n\n // primitive → scalar replacement (preserves the key's commentBefore\n // because we mutate `pair.value`, not the pair itself).\n if (typeof nextValue !== \"object\") {\n pair.value = doc.createNode(nextValue) as Node\n return\n }\n\n // array\n if (Array.isArray(nextValue)) {\n if (isSeq(oldVal)) {\n mergeSeqInPlace(doc, oldVal as YAMLSeq, nextValue)\n } else {\n pair.value = doc.createNode(nextValue) as Node\n }\n return\n }\n\n // object/mapping\n if (isMap(oldVal)) {\n mergeMapInPlace(doc, oldVal as YAMLMap, nextValue as Record<string, unknown>)\n } else {\n pair.value = doc.createNode(nextValue) as Node\n }\n}\n\nfunction mergeSeqInPlace(doc: Document, seq: YAMLSeq, nextItems: unknown[]): void {\n // If every item is an object with a stable string `id`, merge by id so\n // per-item commentBefore (e.g. the `# Capture start time …` block above\n // a workflow node) sticks with that node even if it moves.\n const allHaveId =\n nextItems.length > 0 &&\n nextItems.every((it) => it && typeof it === \"object\" && typeof (it as { id?: unknown }).id === \"string\")\n\n if (!allHaveId) {\n // Replace wholesale. Edge lists, envAllow strings, etc.\n const replacement = doc.createNode(nextItems) as YAMLSeq\n seq.items = replacement.items\n return\n }\n\n const oldById = new Map<string, Node>()\n for (const item of seq.items) {\n if (isMap(item)) {\n const idNode = item.get(\"id\", true)\n if (isScalar(idNode) && typeof idNode.value === \"string\") {\n oldById.set(idNode.value, item)\n }\n }\n }\n\n const merged: Node[] = []\n for (const next of nextItems) {\n const id = (next as { id: string }).id\n const existing = oldById.get(id)\n if (existing && isMap(existing)) {\n mergeMapInPlace(doc, existing as YAMLMap, next as Record<string, unknown>)\n merged.push(existing)\n } else {\n merged.push(doc.createNode(next) as Node)\n }\n }\n seq.items = merged\n}\n\nfunction scalarKey(pair: Pair): string | null {\n const k = pair.key\n if (isScalar(k) && typeof k.value === \"string\") return k.value\n if (typeof k === \"string\") return k\n return null\n}\n","import { createHash, randomUUID } from \"crypto\"\nimport { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport {\n type EntityRef,\n type NodeExecutionEntry,\n type PausedAt,\n type RunStatus,\n type WorkflowRun,\n} from \"./types\"\n\n// --- RunStore (V2) ---\n//\n// Home-node-only persistence for dataflow runs. The node that processes the\n// triggering event becomes the home node and owns:\n// _runs/<runId>.jsonl — append-only run events\n// _index/<backend>__<entityId>.json — entity -> active runId lookup\n//\n// Event lines come in two shapes:\n// { v: 2, kind: \"snapshot\", run } — full run state at a moment\n// { v: 2, kind: \"exec\", entry, context? } — one node execution\n//\n// Append a snapshot on create + on status changes (completed/failed/paused);\n// append an `exec` on every node completion. The authoritative run state is\n// reconstructed by walking the lines in order: start from the last snapshot,\n// fold subsequent `exec` entries into `history` + `context` + `pending`.\n//\n// This replaces the V1 transition-centric record. The entity-index +\n// home-node ownership semantics are unchanged.\n\nexport interface RunStoreOptions {\n baseDir?: string\n /** This daemon's mesh peer id. Stamped as `homeNode` on run creation so\n * later code can verify ownership before mutating. */\n nodeId: string\n}\n\ntype RunEventLine =\n | { v: 2; kind: \"snapshot\"; run: WorkflowRun }\n | {\n v: 2; kind: \"exec\"; runId: string; entry: NodeExecutionEntry;\n pending: string[];\n status?: RunStatus;\n pausedAt?: PausedAt | null;\n context?: Record<string, Record<string, unknown>>;\n joinCounters?: Record<string, string[]>;\n }\n\nfunction sanitize(s: string): string {\n // Keep the set narrow so the result is always a safe single filename\n // component — `/` is replaced because otherwise it creates unintended\n // subdirectories under _index/ on write.\n return s.replace(/[^a-zA-Z0-9._:#@-]/g, \"_\").slice(0, 200)\n}\n\n/** Stable idempotency key for a node execution given its triggering event.\n * Same event delivered twice -> same key -> engine drops the duplicate. */\nexport function idempotencyKey(runId: string, nodeId: string, eventId: string): string {\n return createHash(\"sha1\").update(`${runId}|${nodeId}|${eventId}`).digest(\"hex\").slice(0, 16)\n}\n\nexport class RunStore {\n readonly baseDir: string\n readonly runsDir: string\n readonly indexDir: string\n readonly nodeId: string\n\n constructor(opts: RunStoreOptions) {\n this.baseDir = opts.baseDir ?? resolve(process.cwd(), \".agentx/workflows\")\n this.runsDir = resolve(this.baseDir, \"_runs\")\n this.indexDir = resolve(this.baseDir, \"_index\")\n this.nodeId = opts.nodeId\n mkdirSync(this.runsDir, { recursive: true })\n mkdirSync(this.indexDir, { recursive: true })\n }\n\n private runPath(runId: string): string {\n return resolve(this.runsDir, `${runId}.jsonl`)\n }\n\n private indexPath(entity: EntityRef): string {\n return resolve(this.indexDir, `${entity.backend}__${sanitize(entity.id)}.json`)\n }\n\n /** Create a new run. Seeds the initial pending queue (typically the\n * trigger node's immediate successors) and writes an entity-index entry\n * so future events for the same entity land on this home node. */\n create(args: {\n workflowId: string\n initialPending: string[]\n entityRef: EntityRef\n initialContext?: Record<string, Record<string, unknown>>\n /** Optional parent-run linkage for sub-process children. */\n parentRunId?: string | null\n parentNodeId?: string | null\n rootRunId?: string | null\n depth?: number\n }): WorkflowRun {\n const now = new Date().toISOString()\n const id = randomUUID()\n const run: WorkflowRun = {\n id,\n workflowId: args.workflowId,\n workflowVersion: 2,\n homeNode: this.nodeId,\n status: \"running\",\n pausedAt: undefined,\n context: args.initialContext ?? {},\n pending: args.initialPending,\n entityRef: args.entityRef,\n history: [],\n parentRunId: args.parentRunId ?? null,\n parentNodeId: args.parentNodeId ?? null,\n rootRunId: args.rootRunId ?? id,\n depth: args.depth ?? 0,\n joinCounters: {},\n createdAt: now,\n updatedAt: now,\n }\n this.appendSnapshot(run)\n this.writeIndex(args.entityRef, run.id)\n return run\n }\n\n /** Read the latest snapshot of a run by replaying the jsonl log. */\n get(runId: string): WorkflowRun | null {\n const p = this.runPath(runId)\n if (!existsSync(p)) return null\n const raw = readFileSync(p, \"utf-8\").trim()\n if (!raw) return null\n let run: WorkflowRun | null = null\n for (const line of raw.split(\"\\n\")) {\n let evt: RunEventLine\n try { evt = JSON.parse(line) as RunEventLine } catch { continue }\n if (evt.v !== 2) continue\n if (evt.kind === \"snapshot\") run = evt.run\n else if (evt.kind === \"exec\" && run && run.id === evt.runId) {\n const current: WorkflowRun = run\n run = {\n ...current,\n history: [...current.history, evt.entry],\n pending: evt.pending,\n status: evt.status ?? current.status,\n pausedAt: evt.pausedAt === null ? undefined : (evt.pausedAt ?? current.pausedAt),\n context: evt.context ?? current.context,\n joinCounters: evt.joinCounters ?? current.joinCounters,\n updatedAt: evt.entry.at,\n }\n }\n }\n return run\n }\n\n /** Look up the active run id for an entity, if any. */\n getActiveByEntity(entity: EntityRef): string | null {\n const p = this.indexPath(entity)\n if (!existsSync(p)) return null\n try {\n const parsed = JSON.parse(readFileSync(p, \"utf-8\")) as { runId: string; homeNode: string; updatedAt: string }\n return parsed.runId\n } catch {\n return null\n }\n }\n\n /** Which node owns a given entity (null if unclaimed). Used by the\n * dispatcher to decide whether to process locally or forward via mesh. */\n getHomeNodeByEntity(entity: EntityRef): string | null {\n const p = this.indexPath(entity)\n if (!existsSync(p)) return null\n try {\n const parsed = JSON.parse(readFileSync(p, \"utf-8\")) as { runId: string; homeNode: string; updatedAt: string }\n return parsed.homeNode\n } catch {\n return null\n }\n }\n\n /** Record a single node's execution. Appends an `exec` line and returns\n * the updated run snapshot. Idempotent: if an entry with the same\n * idempotencyKey already exists, the call is a no-op. */\n recordExecution(args: {\n runId: string\n entry: NodeExecutionEntry\n nextPending: string[]\n status?: RunStatus\n pausedAt?: PausedAt | null // null = clear, undefined = unchanged\n /** Optional updated context snapshot. When provided, replaces the run's\n * context wholesale (used when a node's output bundle lands). When\n * omitted, context is carried forward unchanged. */\n context?: Record<string, Record<string, unknown>>\n /** Optional updated join-counter map. Undefined = carry forward. */\n joinCounters?: Record<string, string[]>\n }): WorkflowRun | null {\n const run = this.get(args.runId)\n if (!run) return null\n if (run.history.some((h) => h.idempotencyKey === args.entry.idempotencyKey)) return run\n\n const line: RunEventLine = {\n v: 2,\n kind: \"exec\",\n runId: args.runId,\n entry: args.entry,\n pending: args.nextPending,\n status: args.status,\n pausedAt: args.pausedAt === null ? null : args.pausedAt,\n context: args.context,\n joinCounters: args.joinCounters,\n }\n appendFileSync(this.runPath(args.runId), JSON.stringify(line) + \"\\n\")\n\n // Refresh the index: completed/failed/canceled runs clear it; running +\n // paused runs keep it so webhook re-entry can still find the home node.\n const nextStatus = args.status ?? run.status\n if (nextStatus === \"completed\" || nextStatus === \"failed\" || nextStatus === \"canceled\") {\n this.clearIndex(run.entityRef)\n } else {\n this.writeIndex(run.entityRef, run.id)\n }\n\n return this.get(args.runId)\n }\n\n /** Mutate status without adding a history entry — used for\n * pause / resume / cancel commands from the CLI or dashboard. */\n setStatus(runId: string, status: RunStatus): WorkflowRun | null {\n const run = this.get(runId)\n if (!run) return null\n const updated: WorkflowRun = { ...run, status, updatedAt: new Date().toISOString() }\n this.appendSnapshot(updated)\n if (status !== \"running\" && status !== \"paused\") this.clearIndex(updated.entityRef)\n return updated\n }\n\n /** List runs, newest first. */\n list(opts: { workflowId?: string; limit?: number } = {}): WorkflowRun[] {\n if (!existsSync(this.runsDir)) return []\n const files = readdirSync(this.runsDir)\n .filter((f) => f.endsWith(\".jsonl\"))\n .map((f) => ({ name: f, mtime: statSync(resolve(this.runsDir, f)).mtimeMs }))\n .sort((a, b) => b.mtime - a.mtime)\n const out: WorkflowRun[] = []\n for (const f of files) {\n const run = this.get(f.name.replace(/\\.jsonl$/, \"\"))\n if (!run) continue\n if (opts.workflowId && run.workflowId !== opts.workflowId) continue\n out.push(run)\n if (opts.limit && out.length >= opts.limit) break\n }\n return out\n }\n\n /** Retention: keep at most `maxRuns` completed/failed/canceled runs\n * younger than `maxDays`. Running/paused runs are never pruned. */\n prune(policy: { maxRuns: number; maxDays: number }): number {\n if (!existsSync(this.runsDir)) return 0\n const cutoff = Date.now() - policy.maxDays * 24 * 60 * 60 * 1000\n const candidates: Array<{ file: string; run: WorkflowRun; mtime: number }> = []\n for (const name of readdirSync(this.runsDir)) {\n if (!name.endsWith(\".jsonl\")) continue\n const path = resolve(this.runsDir, name)\n const mtime = statSync(path).mtimeMs\n const run = this.get(name.replace(/\\.jsonl$/, \"\"))\n if (!run) continue\n if (run.status === \"running\" || run.status === \"paused\") continue\n candidates.push({ file: path, run, mtime })\n }\n candidates.sort((a, b) => b.mtime - a.mtime)\n let pruned = 0\n for (let i = 0; i < candidates.length; i++) {\n const c = candidates[i]\n const tooOld = c.mtime < cutoff\n const tooMany = i >= policy.maxRuns\n if (tooOld || tooMany) {\n try { unlinkSync(c.file); pruned++ } catch { /* ignore */ }\n }\n }\n return pruned\n }\n\n private appendSnapshot(run: WorkflowRun): void {\n const line: RunEventLine = { v: 2, kind: \"snapshot\", run }\n appendFileSync(this.runPath(run.id), JSON.stringify(line) + \"\\n\")\n }\n\n private writeIndex(entity: EntityRef, runId: string): void {\n writeFileSync(this.indexPath(entity), JSON.stringify({\n runId,\n homeNode: this.nodeId,\n updatedAt: new Date().toISOString(),\n }, null, 2) + \"\\n\")\n }\n\n private clearIndex(entity: EntityRef): void {\n const p = this.indexPath(entity)\n if (existsSync(p)) {\n try { unlinkSync(p) } catch { /* ignore */ }\n }\n }\n}\n","import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { z } from \"zod\"\n\n// --- LayoutStore ---\n//\n// Persists node (state) positions for the visual editor alongside workflow\n// definitions, but in a sibling file so the Workflow schema stays pure\n// behavior. A missing layout just means \"no positions yet\" — the editor\n// falls back to auto-layout on load.\n//\n// Layout here is only x/y for state nodes. Edges derive their path from the\n// endpoint nodes at render time, so they don't need persisted coordinates.\n//\n// Shape on disk:\n// .agentx/workflows/_layouts/<id>.json\n// { \"version\": 1, \"nodes\": { \"<stateName>\": { \"x\": 120, \"y\": 80 } } }\n\nconst layoutSchema = z.object({\n version: z.literal(1),\n nodes: z.record(z.object({\n x: z.number(),\n y: z.number(),\n })),\n})\nexport type WorkflowLayout = z.infer<typeof layoutSchema>\n\nexport interface LayoutStoreOptions {\n baseDir?: string\n}\n\nexport class LayoutStore {\n readonly baseDir: string\n readonly layoutsDir: string\n\n constructor(opts: LayoutStoreOptions = {}) {\n this.baseDir = opts.baseDir ?? resolve(process.cwd(), \".agentx/workflows\")\n this.layoutsDir = resolve(this.baseDir, \"_layouts\")\n mkdirSync(this.layoutsDir, { recursive: true })\n }\n\n private pathFor(workflowId: string): string {\n // The workflow id grammar already excludes path-hostile chars, but guard\n // anyway in case a caller passes something unvalidated.\n const safe = workflowId.replace(/[^a-zA-Z0-9._-]/g, \"_\")\n return resolve(this.layoutsDir, `${safe}.json`)\n }\n\n get(workflowId: string): WorkflowLayout | null {\n const p = this.pathFor(workflowId)\n if (!existsSync(p)) return null\n try {\n const parsed = layoutSchema.safeParse(JSON.parse(readFileSync(p, \"utf-8\")))\n return parsed.success ? parsed.data : null\n } catch {\n return null\n }\n }\n\n /** Save node positions. Callers should pass only coordinates for states\n * that currently exist in the workflow — stale entries are kept until\n * the caller explicitly prunes them via `sync()`. */\n save(workflowId: string, layout: WorkflowLayout): WorkflowLayout {\n const parsed = layoutSchema.parse(layout)\n writeFileSync(this.pathFor(workflowId), JSON.stringify(parsed, null, 2) + \"\\n\")\n return parsed\n }\n\n /** Drop nodes from the layout whose ids aren't in `validIds`. Prevents\n * stale positions from lingering after a state is removed in the editor. */\n sync(workflowId: string, validIds: string[]): WorkflowLayout | null {\n const existing = this.get(workflowId)\n if (!existing) return null\n const keep: WorkflowLayout[\"nodes\"] = {}\n for (const id of validIds) if (existing.nodes[id]) keep[id] = existing.nodes[id]\n const next: WorkflowLayout = { version: 1, nodes: keep }\n return this.save(workflowId, next)\n }\n\n delete(workflowId: string): boolean {\n const p = this.pathFor(workflowId)\n if (!existsSync(p)) return false\n unlinkSync(p)\n return true\n }\n}\n","import type { Workflow } from \"./types\"\n\nexport interface WorkflowMatchInput {\n agentId: string\n channel?: string\n message: string\n intentPath?: string[]\n}\n\nexport interface WorkflowMatch {\n workflow: Workflow\n confidence: number\n reasons: string[]\n}\n\nfunction words(text: string): Set<string> {\n return new Set(\n text.toLowerCase()\n .replace(/[^a-z0-9\\s]/g, \" \")\n .split(/\\s+/)\n .filter((w) => w.length > 3),\n )\n}\n\nfunction overlap(a: Set<string>, b: Set<string>): number {\n if (a.size === 0 || b.size === 0) return 0\n let n = 0\n for (const w of a) if (b.has(w)) n++\n return n / Math.max(a.size, b.size)\n}\n\nexport function matchWorkflow(input: WorkflowMatchInput, workflows: Workflow[]): WorkflowMatch | null {\n const messageWords = words(input.message)\n const active = workflows.filter((wf) => wf.state === \"active\" && (wf.status ?? \"active\") === \"active\")\n let best: WorkflowMatch | null = null\n\n for (const wf of active) {\n let score = 0\n const reasons: string[] = []\n if (wf.ownerAgent && wf.ownerAgent === input.agentId) { score += 0.25; reasons.push(\"owner-agent\") }\n if (input.channel && wf.tags.includes(input.channel)) { score += 0.15; reasons.push(\"channel-tag\") }\n if (input.intentPath?.length && wf.intentPath.length) {\n const same = input.intentPath.filter((p, i) => wf.intentPath[i] === p).length\n if (same > 0) { score += Math.min(0.3, same * 0.1); reasons.push(\"intent-path\") }\n }\n const text = [wf.id, wf.title, wf.description ?? \"\", ...wf.tags].join(\" \")\n const wordScore = overlap(messageWords, words(text))\n if (wordScore > 0) { score += Math.min(0.35, wordScore); reasons.push(\"text-similarity\") }\n if ((wf.matchCount ?? 0) > 0) { score += Math.min(0.1, wf.matchCount * 0.01); reasons.push(\"prior-matches\") }\n const confidence = Math.max(0, Math.min(1, score))\n if (!best || confidence > best.confidence) best = { workflow: wf, confidence, reasons }\n }\n\n return best && best.confidence > 0 ? best : null\n}\n","import type { Condition, Workflow, WorkflowEdge, WorkflowNode } from \"./types\"\n\n// --- Engine (V2 — dataflow walk driver) ---\n//\n// Pure functions that decide what happens next given a workflow DAG + a\n// completed node's output. No side effects — the dispatcher owns run-store\n// writes, node handler execution, and mesh forwarding.\n//\n// The walk algorithm is simple: after a node completes, find its outgoing\n// edges; for each edge, decide if it fires (always, for linear nodes; based\n// on the selected port, for branch nodes); collect the target nodeIds into\n// `nextPending`. The dispatcher picks them up on the next turn.\n\nexport interface WalkInput {\n workflow: Workflow\n /** The node that just completed. */\n fromNodeId: string\n /** For `branch` nodes, the port that was selected (e.g. \"status\", \"new\",\n * \"fallback\"). For other node types, ignored. */\n selectedPort?: string\n}\n\nexport interface WalkResult {\n /** Node ids the run should execute next. Usually 0 or 1 entries; can be\n * >1 if the workflow deliberately fans out. */\n nextPending: string[]\n}\n\n/** Pure: given the workflow graph and the node that just completed, figure\n * out what should run next. */\nexport function nextNodes(input: WalkInput): WalkResult {\n const { workflow, fromNodeId, selectedPort } = input\n const fromNode = workflow.nodes.find((n) => n.id === fromNodeId)\n if (!fromNode) return { nextPending: [] }\n const outgoing = workflow.edges.filter((e) => e.from === fromNodeId)\n\n // Branch nodes: only the edge whose fromPort matches the selectedPort fires.\n // Linear nodes: all outgoing edges fire (rare in v1; defaults to one).\n const firing = fromNode.type === \"branch\"\n ? outgoing.filter((e) => (e.fromPort ?? \"\") === (selectedPort ?? \"\"))\n : outgoing\n\n return { nextPending: firing.map((e) => e.to) }\n}\n\n// --- Branch condition evaluation ---\n//\n// A `branch` node's config is:\n// { cases: [ { when: Condition, to: portName } ], default: portName }\n// Given the run's accumulated context, pick the first matching case or the\n// default. Returns the port name the edge should leave from.\n\nexport function evaluateBranch(node: WorkflowNode, context: Record<string, unknown>): string {\n const cases = (node.config.cases as Array<{ when: Condition; to: string }> | undefined) ?? []\n const defaultPort = (node.config.default as string | undefined) ?? \"\"\n for (const c of cases) {\n if (conditionMatches(c.when, context)) return c.to\n }\n return defaultPort\n}\n\nfunction conditionMatches(cond: Condition, context: Record<string, unknown>): boolean {\n const p = (cond.params ?? {}) as Record<string, unknown>\n const path = String(p.path ?? \"\")\n const value = getByPath(context, path)\n switch (cond.kind) {\n case \"equals\":\n return value === p.value\n case \"contains\": {\n if (typeof value === \"string\") return value.includes(String(p.value ?? \"\"))\n if (Array.isArray(value)) return value.some((v) => v === p.value)\n return false\n }\n case \"matches\": {\n if (typeof value !== \"string\") return false\n try { return new RegExp(String(p.regex ?? \"\")).test(value) } catch { return false }\n }\n case \"exists\":\n return value !== undefined && value !== null && value !== \"\"\n default:\n return false\n }\n}\n\n/** Tiny dotted-path lookup that stops at non-objects. */\nexport function getByPath(ctx: unknown, path: string): unknown {\n if (!path) return undefined\n const parts = path.split(\".\")\n let cur: unknown = ctx\n for (const p of parts) {\n if (cur === null || cur === undefined) return undefined\n if (typeof cur !== \"object\") return undefined\n cur = (cur as Record<string, unknown>)[p]\n }\n return cur\n}\n\n/** The initial pending queue for a new run: the trigger node's successors.\n * Called by the dispatcher when seeding a run's state. */\nexport function initialPendingFromTrigger(workflow: Workflow): { triggerId: string; pending: string[] } | null {\n const trigger = workflow.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n if (!trigger) return null\n const successors = workflow.edges.filter((e) => e.from === trigger.id).map((e) => e.to)\n return { triggerId: trigger.id, pending: successors }\n}\n\n/** Return a node by id. Convenience used by the dispatcher + tests. */\nexport function findNode(workflow: Workflow, id: string): WorkflowNode | undefined {\n return workflow.nodes.find((n) => n.id === id)\n}\n\n/** Edges leaving a node — used for debugging and the editor overlay. */\nexport function outgoingEdges(workflow: Workflow, fromId: string): WorkflowEdge[] {\n return workflow.edges.filter((e) => e.from === fromId)\n}\n","// --- Template rendering ---\n//\n// Named-helper substitution: {{a.b.c}} resolves dotted paths in a context\n// object, returning the string form of the leaf value. Unknown paths render\n// as \"\" (not left as literal {{...}}) so agents don't see template syntax.\n// No arbitrary expressions in v1 — see types.ts for the rationale.\n//\n// `envAllow` is an allowlist of env-var names that {{env.X}} can reach; any\n// other env access renders as \"\". Prevents leaking secrets through prompt\n// text when a workflow author writes {{env.SUPER_SECRET}} by mistake.\n\nexport interface RenderOptions {\n envAllow?: string[]\n}\n\nfunction lookup(path: string, ctx: Record<string, unknown>, envAllow: Set<string>): string {\n const parts = path.split(\".\")\n // env.* is special: only allow-listed names are readable.\n if (parts[0] === \"env\") {\n if (parts.length !== 2) return \"\"\n const name = parts[1]\n if (!envAllow.has(name)) return \"\"\n const v = process.env[name]\n return typeof v === \"string\" ? v : \"\"\n }\n let cur: unknown = ctx\n for (const p of parts) {\n if (cur === null || cur === undefined) return \"\"\n if (typeof cur !== \"object\") return \"\"\n cur = (cur as Record<string, unknown>)[p]\n }\n if (cur === null || cur === undefined) return \"\"\n if (typeof cur === \"object\") return JSON.stringify(cur)\n return String(cur)\n}\n\nexport function render(template: string, ctx: Record<string, unknown>, opts: RenderOptions = {}): string {\n const envAllow = new Set(opts.envAllow || [])\n // Character class matches the workflow node-id regex (letters, digits,\n // underscore, hyphen, plus the dotted-path separator). Without the\n // hyphen, auto-generated ids like \"n-fcjwrx\" silently fail to\n // resolve — the {{…}} literal leaks into rendered output.\n return template.replace(/\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_.-]*)\\s*\\}\\}/g, (_, path: string) => lookup(path, ctx, envAllow))\n}\n\n/** Walks a params object and renders every string-valued leaf. Used by\n * actions so params like `{ body: \"Issue #{{issue.iid}} done\" }` work\n * without per-action special-casing. */\nexport function renderParams(params: Record<string, unknown>, ctx: Record<string, unknown>, opts: RenderOptions = {}): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(params)) {\n if (typeof v === \"string\") out[k] = render(v, ctx, opts)\n else if (Array.isArray(v)) out[k] = v.map((x) => typeof x === \"string\" ? render(x, ctx, opts) : x)\n else if (v && typeof v === \"object\") out[k] = renderParams(v as Record<string, unknown>, ctx, opts)\n else out[k] = v\n }\n return out\n}\n","// --- Per-workflow-node concurrency gate ---\n//\n// Caps how many simultaneous executions a single node ID can have across\n// concurrent workflow runs. Without this, N runs of the same workflow can\n// each fan a hot node (e.g. agent invocations) at once, multiplying token\n// spend and overwhelming downstream agents.\n//\n// Scope is `${workflowId}:${nodeId}` — different nodes don't share the\n// budget; different workflows that happen to use the same node id stay\n// independent.\n//\n// Used by `agentHandler` in nodes/handlers.ts. Activated only when the\n// node config sets `maxConcurrent`. Without that field the gate no-ops.\n\ninterface Waiter {\n resolve: () => void\n}\n\nclass NodeConcurrencyGate {\n private active: Map<string, number> = new Map()\n private waiters: Map<string, Waiter[]> = new Map()\n\n /** Acquire a slot. Resolves immediately if under cap; otherwise queues\n * in FIFO order behind earlier waiters. The caller must invoke\n * `release(key)` exactly once per acquire (use a try/finally). */\n async acquire(key: string, max: number): Promise<void> {\n const cur = this.active.get(key) ?? 0\n if (cur < max) {\n this.active.set(key, cur + 1)\n return\n }\n return new Promise<void>((resolve) => {\n const queue = this.waiters.get(key) ?? []\n queue.push({\n resolve: () => {\n this.active.set(key, (this.active.get(key) ?? 0) + 1)\n resolve()\n },\n })\n this.waiters.set(key, queue)\n })\n }\n\n release(key: string): void {\n const cur = this.active.get(key) ?? 0\n this.active.set(key, Math.max(0, cur - 1))\n const queue = this.waiters.get(key)\n if (queue && queue.length > 0) {\n const next = queue.shift()!\n next.resolve()\n }\n }\n\n /** For tests / observability. */\n stats(key: string): { active: number; waiting: number } {\n return {\n active: this.active.get(key) ?? 0,\n waiting: this.waiters.get(key)?.length ?? 0,\n }\n }\n}\n\n/** Process-singleton — workflow nodes share the same gate. */\nexport const nodeConcurrencyGate = new NodeConcurrencyGate()\n\nexport function nodeKey(workflowId: string, nodeId: string): string {\n return `${workflowId}:${nodeId}`\n}\n","import { evaluateBranch, getByPath } from \"../engine\"\nimport { renderParams, render } from \"../template\"\nimport { nodeConcurrencyGate, nodeKey } from \"../node-concurrency\"\nimport type { NodeContext, NodeHandler, NodeResult } from \"./types\"\n\n// --- Node handlers (Phase 1 set) ---\n//\n// Phase 1 ships: trigger.* (passthrough), agent, branch, action.send,\n// action.createIssue, end. Phase 2 adds checkpoint + label verbs.\n// Phase 3 adds react / editMessage / logTime / callHTTP / createCard /\n// trigger.cron / trigger.hook / trigger.manual specialisations.\n//\n// Each handler validates the shape of `node.config` locally and narrows the\n// channel adapter it needs to just the method it calls. Failures log and\n// return { error }; the dispatcher converts that into a failed-run state.\n\n/** Trigger nodes' output is pre-seeded into `run.context[triggerId]` by the\n * dispatcher when the run is created — they never actually \"execute\" here.\n * This handler is just a passthrough for completeness. */\nconst triggerHandler: NodeHandler = async (ctx) => {\n const output = ctx.run.context[ctx.node.id] ?? {}\n return { output }\n}\n\nconst agentHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const agentId = String(cfg.agentId ?? \"\")\n const promptTemplate = String(cfg.prompt ?? \"\")\n if (!agentId) return { error: `agent node \"${ctx.node.id}\" missing config.agentId` }\n\n const prompt = render(promptTemplate, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const timeoutMinutes = typeof cfg.timeoutMinutes === \"number\" ? cfg.timeoutMinutes : undefined\n\n // Optional per-node concurrency cap. When N runs of this workflow all\n // arrive at the same hot node, the gate serializes execution past N\n // active. Prevents the scenario where a burst of triggers fans out\n // every concurrent run to the same agent and multiplies token spend.\n const maxConcurrent = typeof cfg.maxConcurrent === \"number\" && cfg.maxConcurrent > 0\n ? cfg.maxConcurrent\n : null\n const gateKey = maxConcurrent ? nodeKey(ctx.workflow.id, ctx.node.id) : null\n if (gateKey && maxConcurrent) {\n const before = nodeConcurrencyGate.stats(gateKey)\n if (before.active >= maxConcurrent) {\n ctx.log(`[node:${ctx.node.id}] gated: ${before.active} active (cap ${maxConcurrent}), ${before.waiting + 1} waiting`)\n }\n await nodeConcurrencyGate.acquire(gateKey, maxConcurrent)\n }\n\n const start = Date.now()\n try {\n const resp = await ctx.agents.execute({\n agentId,\n message: prompt,\n workflowRunId: ctx.run.id,\n timeoutMinutes,\n })\n const durationMs = Date.now() - start\n if (resp.error) {\n const kind = resp.errorKind ?? \"unknown\"\n ctx.log(`[node:${ctx.node.id}] agent \"${agentId}\" failed (${kind}): ${resp.error}`)\n // Prefix the kind onto the run-level error string so the workflow\n // execution log and dashboard surface a stable, parseable token.\n // The friendly message follows verbatim for operators reading the log.\n return { error: `[${kind}] ${resp.error}` }\n }\n const parser = String(cfg.resultParser ?? \"noqta-result-token\")\n const parsed = parser === \"json\" ? extractJsonBlock(resp.content) : parseResultToken(resp.content)\n return {\n output: {\n reply: resp.content,\n result: parsed.result,\n json: parsed.json,\n taskId: resp.taskId,\n durationMs,\n },\n }\n } catch (e: any) {\n ctx.log(`[node:${ctx.node.id}] agent \"${agentId}\" threw: ${e.message}`)\n return { error: e.message }\n } finally {\n if (gateKey) nodeConcurrencyGate.release(gateKey)\n }\n}\n\nconst branchHandler: NodeHandler = async (ctx) => {\n const port = evaluateBranch(ctx.node, ctx.run.context as unknown as Record<string, unknown>)\n if (!port) {\n ctx.log(`[node:${ctx.node.id}] branch matched no case and no default port`)\n return { output: { port: null } }\n }\n return { output: { port }, port }\n}\n\n/** Send a message via any channel adapter with a `send()` method.\n *\n * Account inheritance: when `accountId` isn't set on this node, fall back to\n * the trigger's `accountId` so workflow replies go out on the SAME bot the\n * message arrived on. Without this, multi-account adapters (Telegram) would\n * resolve the token via `chatAccountMap` — last bot to chat with the user —\n * and the same agent's reply could leak through any of N bots depending on\n * who messaged first. Authors can still override by setting `accountId`\n * explicitly in the node config. */\nconst sendHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"\")\n const chatId = String(rendered.chatId ?? \"\")\n const text = String(rendered.text ?? \"\")\n if (!channel || !chatId) {\n // Surface which template source was empty — usually the culprit is an\n // unallowed env var or a typo in a {{node.path}} reference. Without\n // this the author sees only \"needs channel + chatId\" and has to dig.\n const rawChat = String((ctx.node.config as Record<string, unknown>).chatId ?? \"\")\n const rawChan = String((ctx.node.config as Record<string, unknown>).channel ?? \"\")\n const hints: string[] = []\n if (!channel) hints.push(`channel config=\"${rawChan}\" rendered=\"\"`)\n if (!chatId) hints.push(`chatId config=\"${rawChat}\" rendered=\"\"`)\n if (/\\{\\{\\s*env\\./.test(rawChat) || /\\{\\{\\s*env\\./.test(rawChan)) {\n hints.push(`(env.* templates only resolve for names listed in workflow.envAllow and actually set in process.env)`)\n }\n return { error: `action.send \"${ctx.node.id}\" needs channel + chatId — ${hints.join(\"; \")}` }\n }\n\n const accountId = resolveAccountId(ctx, rendered.accountId)\n\n const adapter = ctx.channels[channel] as { send?: (m: { channel: string; chatId: string; text: string; accountId?: string }) => Promise<string | void> } | undefined\n\n // Local-first: if this node hosts the channel, send through the live\n // adapter. Otherwise fall back to the mesh forwarder (the channel may live\n // on a peer — workflow on macbook, whatsapp on clawd-server, etc.). Without\n // either, we hard-error rather than silently dropping.\n if (adapter?.send) {\n try {\n const messageId = await adapter.send({ channel, chatId, text, ...(accountId ? { accountId } : {}) })\n return { output: { messageId: messageId ?? null } }\n } catch (e: any) {\n return { error: `action.send failed: ${e.message}` }\n }\n }\n\n if (ctx.forwardChannelSend) {\n try {\n const r = await ctx.forwardChannelSend({ channel, chatId, text, accountId })\n return { output: { messageId: r.messageId, viaMesh: true } }\n } catch (e: any) {\n ctx.log(`[node:${ctx.node.id}] mesh-forward of \"${channel}\" send failed: ${e.message}`)\n return { error: `action.send via mesh failed: ${e.message}` }\n }\n }\n\n ctx.log(`[node:${ctx.node.id}] adapter \"${channel}\" not available locally and no mesh forwarder`)\n return { error: `channel \"${channel}\" not available (no local adapter, no mesh route)` }\n}\n\n/** Create an issue via the GitLab adapter's createIssue method. */\nconst createIssueHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"gitlab\")\n const project = String(rendered.project ?? \"\")\n const title = String(rendered.title ?? \"\")\n const description = String(rendered.description ?? \"\")\n const labels = Array.isArray(rendered.labels) ? (rendered.labels as string[]).map(String) : []\n const assignees = Array.isArray(rendered.assignees) ? (rendered.assignees as string[]).map(String) : []\n if (!project || !title) return { error: `action.createIssue \"${ctx.node.id}\" needs project + title` }\n\n // Resolve the originating agent id (if any) from the most recent upstream\n // agent-node execution, so per-agent token identity is preserved.\n const agentId = findUpstreamAgentId(ctx)\n\n const adapter = ctx.channels[channel] as {\n createIssue?: (args: {\n project: string; title: string; description?: string;\n labels?: string[]; assignees?: string[]; agentId?: string\n }) => Promise<{ iid: number; url: string } | null>\n } | undefined\n if (!adapter?.createIssue) {\n return { error: `channel \"${channel}\" does not support createIssue` }\n }\n try {\n const result = await adapter.createIssue({ project, title, description, labels, assignees, agentId })\n if (!result) return { error: \"createIssue returned null (see adapter log)\" }\n return {\n output: {\n issue: {\n iid: result.iid,\n url: result.url,\n webUrl: result.url,\n },\n },\n }\n } catch (e: any) {\n return { error: `action.createIssue failed: ${e.message}` }\n }\n}\n\nconst endHandler: NodeHandler = async () => ({ output: {} })\n\n/** Checkpoint: pauses the run with a named state + a resume filter. When an\n * event matching the filter arrives later (webhook, manual), the dispatcher\n * resumes the run by enqueueing the checkpoint's successors and seeding the\n * incoming event as this node's output. */\nconst checkpointHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const name = String(cfg.name ?? ctx.node.id)\n const waitFor = (cfg.waitFor ?? {}) as Record<string, unknown>\n const resumeMatch = (cfg.resumeMatch ?? {}) as Record<string, unknown>\n return {\n paused: true,\n pausedAt: {\n kind: \"checkpoint\",\n nodeId: ctx.node.id,\n checkpointName: name,\n // Consolidate waitFor + resumeMatch: when a future event arrives on\n // this run's entity, it's compared to both. waitFor is the canonical\n // shape (it mirrors a trigger.channel filter); resumeMatch is an\n // escape hatch for custom matches on event payload fields.\n resumeMatch: { ...waitFor, ...resumeMatch },\n },\n }\n}\n\n/** Action.setLabel: add / remove labels on a GitLab issue or MR. Looks up\n * the project + iid from the config; defaults to `{{trigger.project}}` +\n * `{{trigger.issue.iid}}` when those exist in the run context. */\nconst setLabelHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"gitlab\")\n const project = String(rendered.project ?? \"\")\n const iid = String(rendered.iid ?? \"\")\n const add = Array.isArray(rendered.add) ? (rendered.add as string[]).map(String) : []\n const remove = Array.isArray(rendered.remove) ? (rendered.remove as string[]).map(String) : []\n if (!project || !iid) return { error: `action.setLabel \"${ctx.node.id}\" needs project + iid (try {{trigger.project}} / {{trigger.issue.iid}})` }\n if (add.length === 0 && remove.length === 0) return { output: { skipped: true, reason: \"no labels to add or remove\" } }\n\n const adapter = ctx.channels[channel] as {\n setLabels?: (a: { project: string; kind?: \"issue\" | \"merge_request\"; iid: string; add?: string[]; remove?: string[]; agentId?: string }) => Promise<string[] | null>\n } | undefined\n if (!adapter?.setLabels) return { error: `channel \"${channel}\" does not support setLabels` }\n\n const agentId = findUpstreamAgentId(ctx)\n const kind = (rendered.kind === \"merge_request\" ? \"merge_request\" : \"issue\") as \"issue\" | \"merge_request\"\n const labels = await adapter.setLabels({ project, kind, iid, add, remove, agentId })\n return { output: { labels: labels ?? [], add, remove } }\n}\n\n/** Action.readLabel: fetch the current labels on an issue/MR so downstream\n * nodes can branch on them. Emits `{ labels: string[] }` to the run context. */\nconst readLabelHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"gitlab\")\n const project = String(rendered.project ?? \"\")\n const iid = String(rendered.iid ?? \"\")\n if (!project || !iid) return { error: `action.readLabel \"${ctx.node.id}\" needs project + iid` }\n\n const adapter = ctx.channels[channel] as {\n getLabels?: (a: { project: string; kind?: \"issue\" | \"merge_request\"; iid: string }) => Promise<string[] | null>\n } | undefined\n if (!adapter?.getLabels) return { error: `channel \"${channel}\" does not support getLabels` }\n\n const kind = (rendered.kind === \"merge_request\" ? \"merge_request\" : \"issue\") as \"issue\" | \"merge_request\"\n const labels = await adapter.getLabels({ project, kind, iid })\n return { output: { labels: labels ?? [] } }\n}\n\n/** Action.react: add an emoji reaction via the adapter's `react()` method.\n * Telegram + WhatsApp implement it; others return \"not supported\". */\nconst reactHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"\")\n const chatId = String(rendered.chatId ?? \"\")\n const messageId = String(rendered.messageId ?? \"\")\n const emoji = String(rendered.emoji ?? \"👀\")\n if (!channel || !chatId || !messageId) return { error: `action.react \"${ctx.node.id}\" needs channel + chatId + messageId` }\n\n const accountId = resolveAccountId(ctx, rendered.accountId)\n const adapter = ctx.channels[channel] as { react?: (chatId: string, messageId: string, emoji?: string, accountId?: string) => Promise<void> } | undefined\n if (!adapter?.react) return { error: `channel \"${channel}\" does not support react` }\n try { await adapter.react(chatId, messageId, emoji, accountId); return { output: { emoji } } }\n catch (e: any) { return { error: `action.react failed: ${e.message}` } }\n}\n\n/** Action.editMessage: edit a previously-sent message in place. */\nconst editMessageHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"\")\n const chatId = String(rendered.chatId ?? \"\")\n const messageId = String(rendered.messageId ?? \"\")\n const text = String(rendered.text ?? \"\")\n const parseMode = rendered.parseMode ? String(rendered.parseMode) : undefined\n if (!channel || !chatId || !messageId || !text) return { error: `action.editMessage \"${ctx.node.id}\" needs channel + chatId + messageId + text` }\n\n const accountId = resolveAccountId(ctx, rendered.accountId)\n const adapter = ctx.channels[channel] as { editMessage?: (chatId: string, messageId: string, text: string, parseMode?: string, accountId?: string) => Promise<boolean> } | undefined\n if (!adapter?.editMessage) return { error: `channel \"${channel}\" does not support editMessage` }\n try {\n const ok = await adapter.editMessage(chatId, messageId, text, parseMode, accountId)\n return { output: { edited: ok } }\n } catch (e: any) { return { error: `action.editMessage failed: ${e.message}` } }\n}\n\n/** Action.logTime: record time spent. Currently GitLab only — wraps the\n * adapter's logTimeSpent(chatId, durationMs, agentId?). The chatId\n * convention is \"project:issue:iid\" or \"project:merge_request:iid\". */\nconst logTimeHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const channel = String(rendered.channel ?? \"gitlab\")\n const chatId = String(rendered.chatId ?? \"\")\n const durationMs = Number(rendered.durationMs ?? 0)\n if (!chatId || !durationMs) return { error: `action.logTime \"${ctx.node.id}\" needs chatId + durationMs` }\n\n const adapter = ctx.channels[channel] as { logTimeSpent?: (chatId: string, durationMs: number, agentId?: string) => Promise<void> } | undefined\n if (!adapter?.logTimeSpent) return { error: `channel \"${channel}\" does not support logTimeSpent` }\n const agentId = findUpstreamAgentId(ctx)\n try {\n await adapter.logTimeSpent(chatId, durationMs, agentId)\n return { output: { durationMs } }\n } catch (e: any) { return { error: `action.logTime failed: ${e.message}` } }\n}\n\n/** Action.callHTTP: generic outbound request with templated params. Body is\n * rendered as a string (authors write JSON templates) or passed straight\n * if already an object. Response body is captured as `{ status, body }`\n * so downstream nodes can branch on it. */\nconst callHTTPHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const url = String(rendered.url ?? \"\")\n if (!url) return { error: `action.callHTTP \"${ctx.node.id}\" needs url` }\n const method = String(rendered.method ?? \"POST\").toUpperCase()\n const headers = (rendered.headers && typeof rendered.headers === \"object\")\n ? rendered.headers as Record<string, string>\n : { \"Content-Type\": \"application/json\" }\n let body: string | undefined\n if (rendered.body !== undefined) {\n body = typeof rendered.body === \"string\" ? rendered.body : JSON.stringify(rendered.body)\n }\n\n try {\n const res = await fetch(url, { method, headers, body })\n // Prefer parsed JSON, fall back to text.\n const rawText = await res.text()\n let parsed: unknown = rawText\n try { parsed = JSON.parse(rawText) } catch { /* leave as text */ }\n return {\n output: {\n ok: res.ok,\n status: res.status,\n body: parsed,\n },\n }\n } catch (e: any) {\n return { error: `action.callHTTP failed: ${e.message}` }\n }\n}\n\n/** Action.run: invoke a registered action from .agentx/actions/<id>.json\n * with templated inputs. Output is the full ActionRunResult so downstream\n * nodes can branch on `.ok`, `.status`, or read `.output` text. */\nconst actionRunHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const actionId = String(rendered.actionId ?? rendered.id ?? \"\")\n if (!actionId) return { error: `action.run \"${ctx.node.id}\" needs actionId` }\n const inputs = (rendered.inputs && typeof rendered.inputs === \"object\")\n ? rendered.inputs as Record<string, unknown>\n : {}\n const { ActionStore } = await import(\"../../actions/store\")\n const { runAction } = await import(\"../../actions/runner\")\n const action = new ActionStore().get(actionId)\n if (!action) return { error: `action.run \"${ctx.node.id}\": no action \"${actionId}\" in registry` }\n try {\n const result = await runAction(action, inputs)\n const output: Record<string, unknown> = {\n ok: result.ok,\n output: result.output,\n status: result.status,\n durationMs: result.durationMs,\n }\n if (result.errors !== undefined) output.errors = result.errors\n return result.ok\n ? { output }\n : { output, error: `action \"${actionId}\" failed (status=${result.status})` }\n } catch (e: any) {\n return { error: `action.run failed: ${e.message}` }\n }\n}\n\n/** Action.builtin: call a daemon-shipped typed built-in action by name\n * (improvement plan #6 + #9). Closes the loop on the typed-workflow-DSL\n * story — workflow YAMLs can compose http.fetch / extract.structured /\n * file.read_lines / mesh.delegate / etc. without operator-defined action\n * records. Input is templated against the run context the same way\n * action.run handles its `inputs` field.\n *\n * Config shape:\n * {\n * kind: \"action.builtin\",\n * config: {\n * name: \"http.fetch\", // built-in name (dotted)\n * input: { url: \"{{previousNodeId.url}}\" } // any templatable object\n * }\n * }\n *\n * Output: the built-in's typed response, exposed under the run context\n * as `<nodeId>` so downstream nodes can reference it via templates.\n * On error (Zod validation, timeout, handler throw) returns\n * `{ error: ... }` so branch / rule nodes can react.\n */\nconst actionBuiltinHandler: NodeHandler = async (ctx) => {\n const rendered = renderParams(ctx.node.config, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const name = String(rendered.name ?? \"\")\n if (!name) return { error: `action.builtin \"${ctx.node.id}\" needs a \"name\" (e.g. \"http.fetch\")` }\n const input = (rendered.input && typeof rendered.input === \"object\")\n ? rendered.input as Record<string, unknown>\n : {}\n const { runBuiltin, getBuiltin } = await import(\"../../actions/builtin\")\n if (!getBuiltin(name)) {\n return { error: `action.builtin \"${ctx.node.id}\": unknown built-in \"${name}\"` }\n }\n try {\n const output = await runBuiltin(name, input)\n return { output: output as Record<string, unknown> }\n } catch (e: any) {\n // ZodError surfaces as a validation failure with a path-aware\n // message. Other errors (network, timeout, handler throw) come\n // through with their own message. Either way, return as a node\n // error so the engine can branch / surface in the run log.\n const msg = e?.name === \"ZodError\" ? `validation: ${e.message}` : (e?.message || String(e))\n return { error: `action.builtin \"${name}\" failed: ${msg}` }\n }\n}\n\n/** Transform: derive a bundle from upstream context. V2 scope is tight — we\n * support two modes:\n * 1. `path`: pick a value from context by dotted path, expose as {value}\n * 2. `template`: render each key via the template engine so authors can\n * reshape + combine upstream outputs for the next node. */\nconst transformHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const ctxObj = ctx.run.context as unknown as Record<string, unknown>\n if (typeof cfg.path === \"string\" && cfg.path) {\n return { output: { value: getByPath(ctxObj, cfg.path) } }\n }\n if (cfg.template && typeof cfg.template === \"object\") {\n const rendered = renderParams(cfg.template as Record<string, unknown>, ctxObj, { envAllow: ctx.workflow.envAllow })\n return { output: rendered }\n }\n return { output: {} }\n}\n\n/** Convert an ISO-8601 duration (\"PT2H\", \"P1D\") or a plain number-of-minutes\n * into an absolute dueAt timestamp. Accepts { minutes: N } too for convenience. */\nfunction computeDueAt(dueIn: unknown): string | undefined {\n if (dueIn === undefined || dueIn === null) return undefined\n if (typeof dueIn === \"number\") return new Date(Date.now() + dueIn * 60_000).toISOString()\n if (typeof dueIn === \"object\" && dueIn && \"minutes\" in dueIn) {\n const m = Number((dueIn as { minutes: unknown }).minutes)\n if (Number.isFinite(m)) return new Date(Date.now() + m * 60_000).toISOString()\n }\n if (typeof dueIn === \"string\") {\n const match = /^P(?:([0-9]+)D)?(?:T(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+)S)?)?$/.exec(dueIn)\n if (match) {\n const days = Number(match[1] ?? 0)\n const hours = Number(match[2] ?? 0)\n const minutes = Number(match[3] ?? 0)\n const seconds = Number(match[4] ?? 0)\n const ms = ((days * 24 + hours) * 60 + minutes) * 60_000 + seconds * 1000\n if (ms > 0) return new Date(Date.now() + ms).toISOString()\n }\n }\n return undefined\n}\n\n/** subProcess: spawn a child workflow run, pause the parent until the child\n * reaches `end`. Enforces workflow.maxChildDepth safety rail at spawn time.\n * Actual spawn is performed by the dispatcher (walk loop observes\n * `spawnChild` on the result and creates + kicks the child run). */\nconst subProcessHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const rendered = renderParams(cfg as Record<string, unknown>, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n\n const childWorkflowId = String(rendered.workflowId ?? \"\")\n if (!childWorkflowId) return { error: `subProcess \"${ctx.node.id}\" missing config.workflowId` }\n\n // Depth safety rail: parent at depth D may spawn children up to\n // maxChildDepth - 1. I.e. a cap of 5 allows depths 0..4 to spawn 1..5.\n const parentDepth = ctx.run.depth ?? 0\n const cap = ctx.workflow.maxChildDepth ?? 5\n if (parentDepth + 1 >= cap) {\n return { error: `max child workflow depth ${cap} exceeded at ${ctx.workflow.id} → ${childWorkflowId} (parent depth ${parentDepth})` }\n }\n\n // Compute the child's initial context from inputMap. Map top-level keys\n // are child-context keys (typically the child's trigger node id); values\n // are objects whose leaves are rendered via the template engine.\n const rawInputMap = cfg.inputMap as unknown\n let inputBundle: Record<string, Record<string, unknown>> = {}\n if (rawInputMap === \"*\") {\n inputBundle = { ...(ctx.run.context as Record<string, Record<string, unknown>>) }\n } else if (rawInputMap && typeof rawInputMap === \"object\") {\n for (const [k, v] of Object.entries(rawInputMap as Record<string, unknown>)) {\n if (v && typeof v === \"object\" && !Array.isArray(v)) {\n inputBundle[k] = renderParams(v as Record<string, unknown>, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n } else if (typeof v === \"string\") {\n inputBundle[k] = { value: render(v, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow }) }\n }\n }\n }\n\n return {\n paused: true,\n pausedAt: {\n kind: \"subProcess\",\n nodeId: ctx.node.id,\n childRunId: \"\", // populated by dispatcher after spawn\n childWorkflowId,\n },\n spawnChild: { workflowId: childWorkflowId, input: inputBundle },\n }\n}\n\n/** trigger.form: same passthrough semantics as other triggers — the\n * dispatcher seeds the trigger node's output from the incoming form\n * submission when a run is created. */\nconst triggerFormHandler: NodeHandler = triggerHandler\n\n/** signal.emit: side-effect node — post a signal to the bus. Walk loop\n * reads `emitSignal` from the result and dispatches it before continuing. */\nconst signalEmitHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const rendered = renderParams(cfg as Record<string, unknown>, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const name = String(rendered.name ?? \"\")\n if (!name) return { error: `signal.emit \"${ctx.node.id}\" missing config.name` }\n const scope = rendered.scope === \"global\" ? \"global\" : \"workflow\"\n const payload = (rendered.payload && typeof rendered.payload === \"object\") ? rendered.payload as Record<string, unknown> : {}\n return {\n output: { emittedAt: new Date().toISOString(), name, scope, payload },\n emitSignal: { name, scope, payload },\n }\n}\n\n/** signal.wait: pause the run until a matching signal arrives. */\nconst signalWaitHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const rendered = renderParams(cfg as Record<string, unknown>, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const name = String(rendered.name ?? \"\")\n if (!name) return { error: `signal.wait \"${ctx.node.id}\" missing config.name` }\n const scope = rendered.scope === \"global\" ? \"global\" : \"workflow\"\n const match = (cfg.match && typeof cfg.match === \"object\") ? cfg.match as Record<string, unknown> : {}\n return {\n paused: true,\n pausedAt: {\n kind: \"signalWait\",\n nodeId: ctx.node.id,\n signalName: name,\n match,\n scope,\n },\n }\n}\n\n/** timer.boundary: pause the run until `after` elapses. The timer\n * subsystem fires a resume event on fireAt. Used both for SLA escalation\n * (attached to a prior node) and for intermediate waits. */\nconst timerBoundaryHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config\n const rendered = renderParams(cfg as Record<string, unknown>, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n const due = computeDueAt(rendered.after ?? cfg.after)\n if (!due) return { error: `timer.boundary \"${ctx.node.id}\" needs config.after (duration or minutes)` }\n return {\n paused: true,\n pausedAt: {\n kind: \"timerWait\",\n nodeId: ctx.node.id,\n fireAt: due,\n },\n }\n}\n\n/** gateway.parallel: v1 is a passthrough. Multi-branch join semantics\n * land in Phase 2 when the walk driver upgrades to concurrent-pending. */\nconst gatewayParallelHandler: NodeHandler = async () => ({ output: {} })\n\n/** DMN-style decision-table rule node.\n *\n * Config shape:\n * {\n * inputs: [\"{{path.a}}\", \"{{path.b}}\", ...] // N templated values\n * rules: [\n * { when: [\"*\", \"gold\", \">100\"], to: \"vip\", output: { tier: \"gold\" } },\n * { when: [\"nike\", \"*\", \"*\"], to: \"oem\", output: { brand: \"nike\" } },\n * ...\n * ],\n * default: { to: \"fallback\", output: {} }\n * }\n *\n * Each `when` cell matches the corresponding input by one of:\n * - \"*\" → wildcard (matches anything)\n * - \"value\" → exact string equality\n * - \">N\" / \"<N\" → numeric comparison\n * - \">=N\" / \"<=N\" → numeric comparison\n * - \"!=value\" → inequality\n * - \"/regex/\" → regex match\n *\n * First matching row wins. Falls through to `default` when no rule matches. */\nconst ruleHandler: NodeHandler = async (ctx) => {\n const cfg = ctx.node.config as {\n inputs?: unknown\n rules?: Array<{ when?: unknown; to?: string; output?: Record<string, unknown> }>\n default?: { to?: string; output?: Record<string, unknown> }\n }\n const inputTemplates = Array.isArray(cfg.inputs) ? cfg.inputs as unknown[] : []\n const inputs = inputTemplates.map((raw) =>\n typeof raw === \"string\"\n ? render(raw, ctx.run.context as unknown as Record<string, unknown>, { envAllow: ctx.workflow.envAllow })\n : raw\n )\n const rules = Array.isArray(cfg.rules) ? cfg.rules : []\n for (const rule of rules) {\n const when = Array.isArray(rule.when) ? rule.when as unknown[] : []\n if (when.length !== inputs.length) continue\n let all = true\n for (let i = 0; i < when.length; i++) {\n if (!matchCell(when[i], inputs[i])) { all = false; break }\n }\n if (all) {\n return {\n output: { ...(rule.output ?? {}), matchedPort: rule.to ?? \"\" },\n port: rule.to,\n }\n }\n }\n const def = cfg.default\n return {\n output: { ...(def?.output ?? {}), matchedPort: def?.to ?? \"\" },\n port: def?.to,\n }\n}\n\nfunction matchCell(when: unknown, input: unknown): boolean {\n if (when === undefined || when === null || when === \"*\") return true\n const w = String(when)\n const v = typeof input === \"number\" ? input : String(input ?? \"\")\n // Numeric comparators\n const numMatch = /^(>=|<=|>|<)\\s*(-?\\d+(?:\\.\\d+)?)$/.exec(w)\n if (numMatch) {\n const op = numMatch[1], n = Number(numMatch[2])\n const vNum = typeof v === \"number\" ? v : Number(v)\n if (Number.isNaN(vNum)) return false\n switch (op) {\n case \">\": return vNum > n\n case \"<\": return vNum < n\n case \">=\": return vNum >= n\n case \"<=\": return vNum <= n\n }\n }\n if (w.startsWith(\"!=\")) return String(v) !== w.slice(2)\n if (w.startsWith(\"/\") && w.endsWith(\"/\") && w.length > 2) {\n try { return new RegExp(w.slice(1, -1)).test(String(v)) } catch { return false }\n }\n return String(v) === w\n}\n\n// --- Registry ---\n\nexport const NODE_HANDLERS: Record<string, NodeHandler> = {\n \"trigger.channel\": triggerHandler,\n \"trigger.manual\": triggerHandler,\n \"trigger.cron\": triggerHandler,\n \"trigger.hook\": triggerHandler,\n \"trigger.form\": triggerFormHandler,\n \"agent\": agentHandler,\n \"branch\": branchHandler,\n \"transform\": transformHandler,\n \"gateway.parallel\": gatewayParallelHandler,\n \"rule\": ruleHandler,\n \"action.send\": sendHandler,\n \"action.createIssue\": createIssueHandler,\n \"action.setLabel\": setLabelHandler,\n \"action.readLabel\": readLabelHandler,\n \"action.react\": reactHandler,\n \"action.editMessage\": editMessageHandler,\n \"action.logTime\": logTimeHandler,\n \"action.callHTTP\": callHTTPHandler,\n \"action.run\": actionRunHandler,\n \"action.builtin\": actionBuiltinHandler,\n \"subProcess\": subProcessHandler,\n \"signal.emit\": signalEmitHandler,\n \"signal.wait\": signalWaitHandler,\n \"timer.boundary\": timerBoundaryHandler,\n \"checkpoint\": checkpointHandler,\n \"end\": endHandler,\n}\n\nexport function resolveHandler(type: string): NodeHandler | undefined {\n return NODE_HANDLERS[type]\n}\n\n// --- Helpers ---\n\n/** Resolve the accountId for a channel-action node. Explicit config wins; if\n * unset, inherit from the trigger payload so workflow replies stay on the\n * same bot/account that received the inbound message. Returns undefined when\n * neither is available — adapters that don't care about accountId (single-\n * account channels) handle it transparently. */\nfunction resolveAccountId(ctx: NodeContext, explicit: unknown): string | undefined {\n if (typeof explicit === \"string\" && explicit) return explicit\n const triggerNode = ctx.workflow.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n if (!triggerNode) return undefined\n const trigger = ctx.run.context[triggerNode.id] as { accountId?: unknown } | undefined\n const fromTrigger = trigger?.accountId\n return typeof fromTrigger === \"string\" && fromTrigger ? fromTrigger : undefined\n}\n\n/** Look back through the run's history for the most recently-executed\n * `agent` node and return its agentId. Used to attribute downstream\n * side effects (issue creation, labels, time logs) to the same agent. */\nfunction findUpstreamAgentId(ctx: NodeContext): string | undefined {\n for (let i = ctx.run.history.length - 1; i >= 0; i--) {\n const entry = ctx.run.history[i]\n const node = ctx.workflow.nodes.find((n) => n.id === entry.nodeId)\n if (node?.type === \"agent\") {\n return (node.config.agentId as string | undefined) ?? undefined\n }\n }\n return undefined\n}\n\n/** Agent contract: the reply MAY include a line like `RESULT: approved` or\n * `[APPROVED]` to signal a workflow result. Same parser as the V1 engine\n * (src/daemon/index.ts::parseAgentResult), lifted here so node handlers\n * don't depend on daemon code. */\nfunction parseResultToken(content: string | undefined): { result?: string; json?: unknown } {\n if (!content) return {}\n const explicit = content.match(/\\bRESULT:\\s*([a-z][a-z0-9_-]*)/i)\n if (explicit) return { result: explicit[1].toLowerCase() }\n const bracket = content.match(/\\[(APPROVED|REJECTED|CHANGES[-_]REQUESTED|DONE|FAILED|SKIPPED)\\]/i)\n if (bracket) return { result: bracket[1].toLowerCase().replace(\"_\", \"-\") }\n return {}\n}\n\n/** Extract the first fenced ```json ... ``` block and the RESULT: token.\n * Used when a workflow asks the agent for structured output (e.g. draft\n * issue title + body alongside a classification). */\nfunction extractJsonBlock(content: string | undefined): { result?: string; json?: unknown } {\n const { result } = parseResultToken(content)\n if (!content) return { result }\n const m = content.match(/```(?:json)?\\s*([\\s\\S]*?)```/)\n if (!m) return { result }\n try { return { result, json: JSON.parse(m[1]) } } catch { return { result } }\n}\n","// --- Signal bus (in-memory) ---\n//\n// Process-to-process eventing for workflows. Any running daemon instance\n// has one bus; `signal.emit` nodes post, `signal.wait` pauses subscribe\n// via the dispatcher's resume-on-signal callback. Two scopes:\n//\n// - workflow: a signal is only delivered to waiters in the same workflow\n// definition (workflowId). Default.\n// - global: delivered to every waiter matching name + match filter,\n// regardless of workflow.\n//\n// The bus is intentionally in-memory: signals fire now or never. Cross-\n// node delivery (mesh) is Phase 3. Durable \"signal log\" is out of scope.\n\nexport interface SignalEmission {\n name: string\n scope: \"workflow\" | \"global\"\n workflowId: string\n payload: Record<string, unknown>\n emittedAt: string\n}\n\nexport type SignalHandler = (emission: SignalEmission) => void | Promise<void>\n\nexport class SignalBus {\n private handlers: Set<SignalHandler> = new Set()\n\n subscribe(h: SignalHandler): () => void {\n this.handlers.add(h)\n return () => this.handlers.delete(h)\n }\n\n emit(emission: SignalEmission): void {\n for (const h of this.handlers) {\n try { void Promise.resolve(h(emission)).catch(() => {}) }\n catch { /* swallow — listeners must be robust */ }\n }\n }\n}\n\n/** Does a signal emission match a waiter's filter?\n * - Name must match exactly.\n * - If waiter scope is \"workflow\", the emission's workflowId must match\n * the waiter's workflowId.\n * - The match-record is shallow equality: every key in match must equal\n * the emission's payload at that key. Empty match accepts anything. */\nexport function matchesSignal(\n waiter: { name: string; scope: \"workflow\" | \"global\"; workflowId: string; match: Record<string, unknown> },\n emission: SignalEmission,\n): boolean {\n if (waiter.name !== emission.name) return false\n if (waiter.scope === \"workflow\" && waiter.workflowId !== emission.workflowId) return false\n for (const [k, v] of Object.entries(waiter.match ?? {})) {\n if (v === undefined || v === \"\" || v === \"*\") continue\n if ((emission.payload as Record<string, unknown>)[k] !== v) return false\n }\n return true\n}\n","import type { IntentLedger } from \"../ledger\"\nimport { decideAndCommit, type DispatchPolicy } from \"../decide\"\nimport { reportDivergence, type LegacyOutcome } from \"../divergence\"\nimport type { IntentDecision, IntentEventInput } from \"../types\"\n\n// Source adapter — workflow dispatcher. Phase 1 commit 6.c.\n//\n// One trigger event → at most one workflow run firing. The legacy\n// dispatchOne returns `{ claimed: boolean, run: WorkflowRun | null }`.\n// We project that into a LegacyOutcome and pass-through to the ledger:\n//\n// claimed=true, run!=null → \"dispatched\" (new or resumed run)\n// claimed=true, run==null → \"halted\" (forwarded or concurrent-drop)\n// claimed=false → \"halted\" (declined: no trigger, dead, etc.)\n//\n// The \"claimed but no run\" case is a legitimate dispatcher state (the\n// event was claimed by the workflow even though no node fired here —\n// either the home node is remote or a concurrent run owns the event).\n// Mapping to \"halted\" for the ledger means: from the ledger's POV no\n// agent was dispatched. The legacy reason captures the nuance for audit.\n\nexport interface WorkflowEventProjection {\n workflowId: string\n /** TriggerEvent.id — stable across retries. */\n eventId: string\n triggerSource: string\n /** Optional project axis from the trigger metadata (gitlab project,\n * etc.). When null, Inv-ActiveTaskSafety doesn't engage — same as the\n * router. */\n project: string | null\n entityRef: { backend: string; id: string }\n}\n\nexport interface WorkflowLegacyResult {\n claimed: boolean\n /** Set when the legacy path created or resumed a run. */\n runId: string | null\n /** Optional human-readable reason — preserved verbatim in the divergence\n * row's legacy_reason column. Helps triage during the soak. */\n reason?: string\n}\n\n/** Build the IntentEventInput for one workflow trigger. The\n * sourceEventId is per-(workflow, trigger event) so re-deliveries\n * collapse to one event. Subject scopes active-task safety to one\n * entity per workflow. */\nexport function buildWorkflowEventInput(\n proj: WorkflowEventProjection,\n rawJson: string,\n now: () => number = Date.now,\n): IntentEventInput {\n return {\n ts: now(),\n source: \"workflow\",\n sourceEventId: `${proj.workflowId}:${proj.eventId}`,\n project: proj.project,\n subject: `workflow:${proj.workflowId}:entity:${proj.entityRef.backend}:${proj.entityRef.id}`,\n intent: `workflow.${proj.triggerSource}`,\n rawJson,\n }\n}\n\n/** Project the workflow's `{ claimed, run }` shape into the LegacyOutcome\n * the divergence reporter consumes. */\nexport function workflowResultToLegacyOutcome(result: WorkflowLegacyResult): LegacyOutcome {\n if (result.claimed && result.runId) {\n return {\n agentId: `workflow-run:${result.runId}`,\n outcome: \"dispatched\",\n reason: result.reason ?? \"claimed\",\n }\n }\n if (result.claimed && !result.runId) {\n // Forwarded to remote or concurrent-drop — claimed but no local run.\n return {\n agentId: null,\n outcome: \"halted\",\n reason: result.reason ?? \"claimed-but-no-run\",\n }\n }\n return {\n agentId: null,\n outcome: \"halted\",\n reason: result.reason ?? \"not-claimed\",\n }\n}\n\n/** Pass-through policy: ledger decision mirrors legacy outcome.\n * Divergences come from ledger MECHANICS (idempotency on (source,\n * sourceEventId), active-task safety on (project, subject)). */\nexport function buildWorkflowPolicyFromLegacy(legacy: LegacyOutcome): DispatchPolicy {\n return {\n decidedBy: \"workflow-dispatcher\",\n decide: () => ({\n agentId: legacy.agentId,\n outcome: legacy.outcome === \"deduped\" ? \"halted\" : legacy.outcome,\n reason:\n legacy.outcome === \"deduped\"\n ? `legacy-dedup: ${legacy.reason ?? \"\"}`.trim()\n : legacy.reason ?? null,\n }),\n }\n}\n\n/** Record + report for one (trigger event, legacy result). Caller catches\n * exceptions — workflow dispatch must continue even if the ledger errors. */\nexport function recordWorkflowDispatch(\n ledger: IntentLedger,\n proj: WorkflowEventProjection,\n rawJson: string,\n result: WorkflowLegacyResult,\n now: () => number = Date.now,\n): IntentDecision {\n const eventInput = buildWorkflowEventInput(proj, rawJson, now)\n const legacyOutcome = workflowResultToLegacyOutcome(result)\n const policy = buildWorkflowPolicyFromLegacy(legacyOutcome)\n const ledgerDecision = decideAndCommit(ledger, eventInput, policy, now)\n reportDivergence(ledger, \"workflow\", ledgerDecision, legacyOutcome, now)\n return ledgerDecision\n}\n","import type Database from \"better-sqlite3\"\nimport { newEventId } from \"@/intent/ulid\"\n\n// --- Task trace store ---\n//\n// Improvement plan #2 — per-task observability backed by SQLite. One\n// task_traces row per executeTask invocation, an append-only stream of\n// task_trace_steps inside it. The store is intentionally dumb: it stores\n// what callers give it. Callers (capture sites in src/agents/runtime.ts)\n// are responsible for byte-capping large step payloads so the daemon\n// memory stays bounded under tool-heavy turns.\n//\n// Why a separate store layer (vs writing inline in runtime.ts)?\n// - Tests can exercise the trace lifecycle against a temp SQLite without\n// spinning up the agent runtime.\n// - The HTTP endpoints (GET /task/:id/trace) read through the same\n// functions producers write through, so there's a single source of\n// truth on field names + semantics.\n// - Future capture sites (sdk tier, orchestrator tier) reuse this API\n// without copying SQL.\n//\n// Why ULID for task_id?\n// - URL-safe in HTTP routes (GET /task/:id/trace).\n// - Time-sortable, so listTraces ORDER BY started_at and ORDER BY\n// task_id agree without an explicit timestamp column index.\n// - Already in-tree (src/intent/ulid.ts); no new dep.\n//\n// Cross-DB FK note:\n// intent_event_id and intent_decided_by are logical foreign keys into\n// the intent ledger at .agentx/intent/ledger.sqlite. better-sqlite3\n// doesn't enforce cross-database FKs and we don't ATTACH the ledger\n// here on purpose — the rescue plan's append-only contract on the\n// ledger is honoured by routing all writes through src/intent/ledger.ts.\n// Joins happen at the HTTP / CLI layer when needed.\n\nexport interface TraceStartInput {\n agentId: string\n channel?: string | null\n chatId?: string | null\n messagePreview?: string | null\n /** Full untruncated user message — used by `agentx trace replay <taskId>`\n * to re-fire a recorded task against the current agent config. NULL on\n * rows from before migration v8, in which case replay falls back to\n * messagePreview (which is capped at 200 chars). */\n originalMessage?: string | null\n workflowRunId?: string | null\n workflowId?: string | null\n workflowNodeId?: string | null\n intentEventId?: string | null\n intentDecidedBy?: string | null\n resumeSessionId?: string | null\n model?: string | null\n}\n\nexport interface TraceEndInput {\n status: \"ok\" | \"error\" | \"timeout\"\n finalSessionId?: string | null\n inputTokens?: number | null\n outputTokens?: number | null\n cacheReadTokens?: number | null\n cacheCreateTokens?: number | null\n error?: string | null\n /** The agent's final reply text. Stored verbatim so `replay --diff`\n * can compare original output vs the new run's output without\n * reconstructing it from the step ledger. NULL when the response\n * wasn't captured (older rows or non-text outputs). */\n finalResponse?: string | null\n}\n\nexport interface TraceStepInput {\n /** Coarse step kind. Conventional values: \"tool_use\" | \"tool_result\" |\n * \"llm_message\" | \"session_rotation\" | \"error\" | \"preflight\". Free-form\n * strings allowed so future capture sites don't need a schema bump. */\n name: string\n /** Fine-grained name within the kind. For \"tool_use\", this is the tool\n * name (e.g. \"Bash\", \"Edit\"). Optional. */\n action?: string | null\n status?: \"ok\" | \"error\" | \"in-flight\" | null\n /** JSON-stringified input or human-readable summary. Caller-byte-capped. */\n inputSummary?: string | null\n outputSummary?: string | null\n error?: string | null\n /** Step duration when known. Optional — many step kinds are point-in-time. */\n ms?: number | null\n}\n\nexport interface TraceRecord {\n taskId: string\n agentId: string\n channel: string | null\n chatId: string | null\n workflowRunId: string | null\n workflowId: string | null\n workflowNodeId: string | null\n intentEventId: string | null\n intentDecidedBy: string | null\n resumeSessionId: string | null\n finalSessionId: string | null\n model: string | null\n status: string\n startedAt: number\n finishedAt: number | null\n durationMs: number | null\n inputTokens: number | null\n outputTokens: number | null\n cacheReadTokens: number | null\n cacheCreateTokens: number | null\n error: string | null\n messagePreview: string | null\n /** Full untruncated user message — populated for traces from migration v8\n * onward. NULL on older rows; consumers should fall back to messagePreview. */\n originalMessage: string | null\n /** Agent's final reply text — populated for traces from migration v8\n * onward when the executor records it on end. */\n finalResponse: string | null\n}\n\nexport interface TraceStepRecord {\n taskId: string\n seq: number\n name: string\n action: string | null\n status: string | null\n inputSummary: string | null\n outputSummary: string | null\n error: string | null\n ms: number | null\n startedAt: number\n}\n\nexport interface ListTracesFilters {\n agentId?: string\n channel?: string\n chatId?: string\n workflowRunId?: string\n status?: string\n /** ms epoch */\n since?: number\n /** ms epoch */\n until?: number\n /** Default 100, capped at 1000. */\n limit?: number\n}\n\n/**\n * Insert an in-flight trace row and return its ULID. Callers thread the\n * returned id through the executor and call recordTraceEnd at finish time.\n *\n * `taskId` is optional — when supplied, the caller has already allocated\n * a ULID upstream (e.g. registry.execute generating it before the bus\n * event so the runtime can capture per-step rows under the same id).\n * When omitted, a fresh ULID is allocated here.\n */\nexport function recordTraceStart(\n db: Database.Database,\n input: TraceStartInput,\n taskId: string = newEventId(),\n): string {\n db.prepare(`\n INSERT INTO task_traces (\n task_id, agent_id, channel, chat_id, workflow_run_id, workflow_id,\n workflow_node_id, intent_event_id, intent_decided_by, resume_session_id,\n model, status, started_at, message_preview, original_message\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'in-flight', ?, ?, ?)\n `).run(\n taskId,\n input.agentId,\n input.channel ?? null,\n input.chatId ?? null,\n input.workflowRunId ?? null,\n input.workflowId ?? null,\n input.workflowNodeId ?? null,\n input.intentEventId ?? null,\n input.intentDecidedBy ?? null,\n input.resumeSessionId ?? null,\n input.model ?? null,\n Date.now(),\n input.messagePreview ?? null,\n input.originalMessage ?? null,\n )\n return taskId\n}\n\n/**\n * Finalize a trace. duration_ms is computed from finished_at - started_at\n * inside the UPDATE so the two columns can never disagree. Idempotent —\n * calling twice on the same task_id overwrites the prior end values; the\n * second call's status wins. (Soft-overwrite is intentional: timeout\n * handlers may write before a late stream-completion arrives.)\n */\nexport function recordTraceEnd(db: Database.Database, taskId: string, input: TraceEndInput): void {\n const finishedAt = Date.now()\n db.prepare(`\n UPDATE task_traces SET\n status = ?,\n final_session_id = ?,\n input_tokens = ?,\n output_tokens = ?,\n cache_read_tokens = ?,\n cache_create_tokens = ?,\n error = ?,\n final_response = COALESCE(?, final_response),\n finished_at = ?,\n duration_ms = ? - started_at\n WHERE task_id = ?\n `).run(\n input.status,\n input.finalSessionId ?? null,\n input.inputTokens ?? null,\n input.outputTokens ?? null,\n input.cacheReadTokens ?? null,\n input.cacheCreateTokens ?? null,\n input.error ?? null,\n input.finalResponse ?? null,\n finishedAt,\n finishedAt,\n taskId,\n )\n}\n\n/**\n * Append a step. seq is auto-allocated as MAX(seq)+1 within a transaction\n * so concurrent step writes for the same task_id can't collide. Returns\n * the assigned seq, useful for downstream correlation.\n */\nexport function recordTraceStep(db: Database.Database, taskId: string, input: TraceStepInput): number {\n const startedAt = Date.now()\n const allocateAndInsert = db.transaction((): number => {\n const row = db\n .prepare(\"SELECT MAX(seq) AS max_seq FROM task_trace_steps WHERE task_id = ?\")\n .get(taskId) as { max_seq: number | null } | undefined\n const seq = ((row?.max_seq ?? -1) as number) + 1\n db.prepare(`\n INSERT INTO task_trace_steps (\n task_id, seq, name, action, status, input_summary, output_summary, error, ms, started_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n `).run(\n taskId,\n seq,\n input.name,\n input.action ?? null,\n input.status ?? null,\n input.inputSummary ?? null,\n input.outputSummary ?? null,\n input.error ?? null,\n input.ms ?? null,\n startedAt,\n )\n return seq\n })\n return allocateAndInsert()\n}\n\nfunction rowToTrace(row: Record<string, unknown>): TraceRecord {\n return {\n taskId: row.task_id as string,\n agentId: row.agent_id as string,\n channel: (row.channel as string) ?? null,\n chatId: (row.chat_id as string) ?? null,\n workflowRunId: (row.workflow_run_id as string) ?? null,\n workflowId: (row.workflow_id as string) ?? null,\n workflowNodeId: (row.workflow_node_id as string) ?? null,\n intentEventId: (row.intent_event_id as string) ?? null,\n intentDecidedBy: (row.intent_decided_by as string) ?? null,\n resumeSessionId: (row.resume_session_id as string) ?? null,\n finalSessionId: (row.final_session_id as string) ?? null,\n model: (row.model as string) ?? null,\n status: row.status as string,\n startedAt: row.started_at as number,\n finishedAt: (row.finished_at as number) ?? null,\n durationMs: (row.duration_ms as number) ?? null,\n inputTokens: (row.input_tokens as number) ?? null,\n outputTokens: (row.output_tokens as number) ?? null,\n cacheReadTokens: (row.cache_read_tokens as number) ?? null,\n cacheCreateTokens: (row.cache_create_tokens as number) ?? null,\n error: (row.error as string) ?? null,\n messagePreview: (row.message_preview as string) ?? null,\n originalMessage: (row.original_message as string) ?? null,\n finalResponse: (row.final_response as string) ?? null,\n }\n}\n\nfunction rowToStep(row: Record<string, unknown>): TraceStepRecord {\n return {\n taskId: row.task_id as string,\n seq: row.seq as number,\n name: row.name as string,\n action: (row.action as string) ?? null,\n status: (row.status as string) ?? null,\n inputSummary: (row.input_summary as string) ?? null,\n outputSummary: (row.output_summary as string) ?? null,\n error: (row.error as string) ?? null,\n ms: (row.ms as number) ?? null,\n startedAt: row.started_at as number,\n }\n}\n\n/** Fetch a trace + its ordered steps. Returns null if no such task_id. */\nexport function getTrace(\n db: Database.Database,\n taskId: string,\n): { task: TraceRecord; steps: TraceStepRecord[] } | null {\n const row = db.prepare(\"SELECT * FROM task_traces WHERE task_id = ?\").get(taskId) as\n | Record<string, unknown>\n | undefined\n if (!row) return null\n const stepRows = db\n .prepare(\"SELECT * FROM task_trace_steps WHERE task_id = ? ORDER BY seq\")\n .all(taskId) as Record<string, unknown>[]\n return { task: rowToTrace(row), steps: stepRows.map(rowToStep) }\n}\n\n/**\n * Cancel any in-flight trace rows left over from a previous daemon\n * lifetime. Mirrors the intent ledger's startup cleanup (rescue plan\n * Phase 1 commit a8514d9): a hard-killed daemon never gets to fire\n * task:completed, so the row sits \"in-flight\" forever and contaminates\n * triage queries (`agentx trace list --status in-flight`) with stale\n * orphans. Run once at daemon boot before subscribers attach.\n *\n * Returns the number of rows updated. duration_ms is intentionally NOT\n * computed — we don't know when the task actually ended; preserving\n * NULL there is more honest than backdating to \"now\".\n */\nexport function cleanupOrphanedTraces(db: Database.Database): number {\n const now = Date.now()\n const r = db.prepare(`\n UPDATE task_traces\n SET status = 'canceled',\n error = 'daemon-restart (orphaned in-flight)',\n finished_at = ?\n WHERE status = 'in-flight'\n `).run(now)\n return (r.changes ?? 0) as number\n}\n\n/** Triage / dashboard surface — list traces newest-first, filtered. */\nexport function listTraces(db: Database.Database, filters: ListTracesFilters = {}): TraceRecord[] {\n const where: string[] = []\n const params: unknown[] = []\n if (filters.agentId) { where.push(\"agent_id = ?\"); params.push(filters.agentId) }\n if (filters.channel) { where.push(\"channel = ?\"); params.push(filters.channel) }\n if (filters.chatId) { where.push(\"chat_id = ?\"); params.push(filters.chatId) }\n if (filters.workflowRunId) { where.push(\"workflow_run_id = ?\"); params.push(filters.workflowRunId) }\n if (filters.status) { where.push(\"status = ?\"); params.push(filters.status) }\n if (filters.since !== undefined) { where.push(\"started_at >= ?\"); params.push(filters.since) }\n if (filters.until !== undefined) { where.push(\"started_at <= ?\"); params.push(filters.until) }\n\n const limit = Math.max(1, Math.min(filters.limit ?? 100, 1000))\n const sql = `\n SELECT * FROM task_traces\n ${where.length > 0 ? \"WHERE \" + where.join(\" AND \") : \"\"}\n ORDER BY started_at DESC\n LIMIT ?\n `\n params.push(limit)\n return (db.prepare(sql).all(...params) as Record<string, unknown>[]).map(rowToTrace)\n}\n","import { evaluateBranch, findNode, initialPendingFromTrigger, nextNodes } from \"./engine\"\nimport { resolveHandler } from \"./nodes/handlers\"\nimport type { AgentExecuteRequest, AgentExecuteResponse, NodeResult } from \"./nodes/types\"\nimport { RunStore, idempotencyKey } from \"./run-store\"\nimport type { WorkflowStore } from \"./store\"\nimport { TimerService, type TimerRecord } from \"./timers\"\nimport { SignalBus, matchesSignal, type SignalEmission } from \"./signals\"\nimport type { EventBus } from \"../daemon/event-bus\"\nimport type { EntityRef, NodeExecutionEntry, Workflow, WorkflowRun } from \"./types\"\nimport { getLedgerMode } from \"@/intent/mode\"\nimport { getDefaultLedger } from \"@/intent/instance\"\nimport { recordWorkflowDispatch } from \"@/intent/sources/workflow\"\nimport { openDb } from \"@/storage/sqlite\"\nimport { recordTraceStart, recordTraceEnd } from \"@/storage/traces\"\n\n// --- Dispatcher (V2) ---\n//\n// Entry point called by the hook layer. Given a triggering event:\n//\n// 1. Find workflows whose trigger filter matches.\n// 2. For each match, resolve or create a run keyed by the event's entityRef.\n// 3. If the run's home node is a remote peer, forward via mesh.\n// 4. Locally: seed the trigger node's output into run.context, then\n// walk the DAG — execute each pending node's handler, fold the output\n// into run.context, enqueue successors, loop until pending is empty\n// or a handler paused / failed the run.\n//\n// All side effects (adapter calls, agent dispatch) happen inside node\n// handlers. This module only orchestrates.\n\nexport interface MeshForwarder {\n forwardTransition(peer: string, payload: { workflowId: string; event: TriggerEvent; entityRef: EntityRef }): Promise<void>\n /** Broadcast a fresh trigger event to all healthy mesh peers that may host\n * remote-allowed workflows for this trigger's source. Implementations\n * should be best-effort: a failing peer must not block delivery to others\n * or surface back to the local dispatch path. Optional — when absent,\n * workflows are local-only as before. */\n broadcastTrigger?(payload: {\n trigger: { source: string; project?: string; repo?: string; chat?: string; labels?: string[] }\n entityRef: EntityRef\n event: TriggerEvent\n }): Promise<void>\n /** Forward an outbound channel send to whichever peer hosts the channel.\n * Returns the message id from the remote adapter (or null when not\n * available). Throws when no healthy peer hosts the channel — callers\n * should treat that as a hard error and surface it as the action's\n * failure. Optional — when absent, workflow `action.send` to a non-local\n * channel just errors out, preserving today's behaviour. */\n forwardChannelSend?(payload: {\n channel: string\n chatId: string\n text: string\n accountId?: string\n parseMode?: string\n replyTo?: string\n }): Promise<{ messageId: string | null }>\n}\n\nexport interface TriggerEvent {\n /** Stable event id — used by idempotency keys so retried webhooks\n * collapse into the same node execution. */\n id: string\n /** Output bundle produced by the trigger node. Hooks build this from\n * the raw channel payload. Goes directly into `run.context[triggerId]`. */\n payload: Record<string, unknown>\n}\n\nexport interface DispatcherOptions {\n store: WorkflowStore\n runs: RunStore\n nodeId: string\n forwarder?: MeshForwarder\n channels: Record<string, unknown>\n agents: { execute(req: AgentExecuteRequest): Promise<AgentExecuteResponse> }\n log?: (msg: string) => void\n /** Optional timer service. When provided, `timer.boundary` nodes\n * schedule against it and resume on fire. When omitted, a default is\n * constructed but its loop is NOT started — callers can start it via\n * `dispatcher.timers.start()` once (typically at daemon boot). */\n timers?: TimerService\n /** Optional signal bus. Workflow-scoped default if omitted. */\n signals?: SignalBus\n /** Optional observability bus. When present, the dispatcher emits\n * run-phase events (created / ok / failed / paused / resumed /\n * completed) so operators can watch live via SSE or CLI. No-op when\n * absent — the dispatcher runs exactly as before. */\n events?: EventBus\n}\n\n/** Subset of channel adapter API used for the auto-acknowledge lifecycle on\n * channel-triggered runs. Duck-typed so any adapter implementing react +\n * sendTyping (telegram today, future whatsapp/discord) gets the UX for free.\n * The accountId arg is forwarded so multi-account adapters reply on the\n * originating bot, not whichever bot the chat was last seen on. */\ninterface AckCapableAdapter {\n react?: (chatId: string, messageId: string, emoji?: string, accountId?: string) => Promise<void> | void\n sendTyping?: (chatId: string, accountId?: string) => Promise<void> | void\n}\n\nconst ACK_TYPING_INTERVAL_MS = 4000\n\n/** Max parallel node handlers per run. Caps fan-out storms (e.g.,\n * gateway.parallel(fanOut) → 100 branches) from exhausting agent\n * slots. Chosen to be meaningful for the common case (3–8 parallel\n * approvers / implementations) while never going wild. */\nconst MAX_PARALLEL_PER_RUN = 8\n\nexport class WorkflowDispatcher {\n private readonly store: WorkflowStore\n private readonly runs: RunStore\n private readonly nodeId: string\n private readonly forwarder?: MeshForwarder\n private readonly channels: Record<string, unknown>\n private readonly agents: { execute(req: AgentExecuteRequest): Promise<AgentExecuteResponse> }\n private readonly log: (msg: string) => void\n readonly timers: TimerService\n readonly signals: SignalBus\n readonly events?: EventBus\n /** Per-run typing timer. Started when a channel-triggered run is created or\n * resumed; stopped when the run terminates (completed / failed / canceled\n * / paused). Keyed by runId so concurrent channel runs don't stomp on each\n * other's lifecycles. */\n private readonly ackTypingTimers: Map<string, ReturnType<typeof setInterval>> = new Map()\n /** Per-run commit chain. The parallel walk loop fires node handlers\n * concurrently via Promise.allSettled, but every state-mutating\n * operation (recordExecution, joinCounter update, pause transition,\n * resume) must land atomically — runs are a single log and the\n * read-update-write on joinCounters + dedup check on idempotencyKey\n * need to see a consistent snapshot. Serialising commits through a\n * per-run promise chain gives us that guarantee without a broader\n * global lock; different runs still progress independently. */\n private readonly commitChains: Map<string, Promise<unknown>> = new Map()\n\n constructor(opts: DispatcherOptions) {\n this.store = opts.store\n this.runs = opts.runs\n this.nodeId = opts.nodeId\n this.forwarder = opts.forwarder\n this.channels = opts.channels\n this.agents = opts.agents\n this.log = opts.log ?? (() => {})\n this.timers = opts.timers ?? new TimerService({ log: (m) => this.log(m) })\n this.signals = opts.signals ?? new SignalBus()\n this.events = opts.events\n\n // Register the timer-fire callback once. TimerService is a per-node\n // singleton; re-registration would clobber prior instances, but the\n // dispatcher also is, so this is safe.\n this.timers.onFire((t) => this.resumeFromTimer(t))\n // Resume any paused signalWait runs whose filter matches a published\n // signal. Running a full scan of paused runs per emission is cheap in\n // v1 (runs are fs-backed and typically numbered in the hundreds).\n this.signals.subscribe((emission) => this.resumeFromSignal(emission))\n }\n\n /** Publish a run-phase event if an EventBus is configured. No-op\n * otherwise. Used by the walk loop + resume paths so operators can\n * Monitor runs live. */\n private emitRunEvent(args: {\n runId: string; workflowId: string; nodeId?: string; phase: string; status?: string; note?: string; homeNode?: string\n }): void {\n if (!this.events) return\n try {\n this.events.publish({\n kind: \"run\",\n runId: args.runId, workflowId: args.workflowId,\n nodeId: args.nodeId, phase: args.phase,\n status: args.status, note: args.note, homeNode: args.homeNode,\n })\n } catch { /* defensive — a bus failure never breaks the engine */ }\n }\n\n /** Serialise a state-mutating operation against all other commits for\n * the same run. Parallel branches execute their handlers concurrently,\n * but their commits land one after another — keeping joinCounters +\n * pending + pausedAt + recordExecution dedup consistent. */\n private async commit<T>(runId: string, fn: () => Promise<T> | T): Promise<T> {\n const prev = this.commitChains.get(runId) ?? Promise.resolve()\n const next = prev.then(() => fn(), () => fn())\n // Keep the chain alive until the caller's fn settles. We don't care\n // about fn's result type for the chain tail — `unknown` is enough.\n this.commitChains.set(runId, next.then(() => undefined, () => undefined))\n return next as Promise<T>\n }\n\n /** Public helper for the HTTP API to emit a signal programmatically. */\n emitSignal(args: { name: string; scope?: \"workflow\" | \"global\"; workflowId?: string; payload?: Record<string, unknown> }): SignalEmission {\n const emission: SignalEmission = {\n name: args.name,\n scope: args.scope ?? \"global\",\n workflowId: args.workflowId ?? \"\",\n payload: args.payload ?? {},\n emittedAt: new Date().toISOString(),\n }\n this.signals.emit(emission)\n if (this.events) {\n try {\n this.events.publish({\n kind: \"signal\",\n name: emission.name,\n scope: emission.scope,\n workflowId: emission.workflowId || undefined,\n payload: emission.payload,\n })\n } catch { /* ignore */ }\n }\n return emission\n }\n\n /** Callback when a signal is published. Finds paused `signalWait` runs\n * whose filter matches and resumes them. */\n private async resumeFromSignal(emission: SignalEmission): Promise<void> {\n const all = this.runs.list({ limit: 500 })\n for (const run of all) {\n if (run.status !== \"paused\" || !run.pausedAt || run.pausedAt.kind !== \"signalWait\") continue\n const waiter = {\n name: run.pausedAt.signalName,\n scope: run.pausedAt.scope,\n workflowId: run.workflowId,\n match: run.pausedAt.match ?? {},\n }\n if (!matchesSignal(waiter, emission)) continue\n const wf = this.store.list().find((w) => w.id === run.workflowId)\n if (!wf) continue\n // Resume under the per-run mutex so a concurrent walk batch (or\n // another signal firing at the same instant) can't see a half-\n // transitioned pausedAt/pending/status set.\n await this.commit(run.id, () => {\n const fresh = this.runs.get(run.id)\n if (!fresh || fresh.status !== \"paused\" || !fresh.pausedAt || fresh.pausedAt.kind !== \"signalWait\") return\n if (!matchesSignal({ name: fresh.pausedAt.signalName, scope: fresh.pausedAt.scope, workflowId: fresh.workflowId, match: fresh.pausedAt.match ?? {} }, emission)) return\n const node = findNode(wf, fresh.pausedAt.nodeId)\n const successors = node ? wf.edges.filter((e) => e.from === node.id).map((e) => e.to) : []\n const output = {\n receivedAt: new Date().toISOString(),\n name: emission.name,\n payload: emission.payload,\n }\n this.runs.recordExecution({\n runId: fresh.id,\n entry: {\n at: output.receivedAt,\n nodeId: fresh.pausedAt.nodeId,\n inputKeys: [],\n status: \"resumed\",\n output,\n idempotencyKey: idempotencyKey(fresh.id, fresh.pausedAt.nodeId, `signal:${emission.name}:${emission.emittedAt}`),\n },\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: { ...fresh.context, [fresh.pausedAt.nodeId]: output },\n })\n this.log(`[workflow:${wf.id}] run ${fresh.id} resumed from signal \"${emission.name}\"`)\n this.emitRunEvent({ runId: fresh.id, workflowId: wf.id, nodeId: fresh.pausedAt.nodeId, phase: \"resumed\", status: \"running\", note: `signal:${emission.name}` })\n })\n void this.walk(wf, run.id, `signal:${emission.name}`)\n .catch((e: any) => this.log(`[workflow:${wf.id}] walk-after-signal failed: ${e.message}`))\n }\n }\n\n /** Callback from the TimerService when a scheduled timer elapses. Reads\n * the paused run, verifies the pause is still on this timer's node, and\n * resumes by seeding the timer node's output with { firedAt } and\n * enqueueing successors. */\n private async resumeFromTimer(t: TimerRecord): Promise<void> {\n const wf = this.store.list().find((w) => w.id === (this.runs.get(t.runId)?.workflowId ?? \"\"))\n if (!wf) return\n await this.commit(t.runId, () => {\n const fresh = this.runs.get(t.runId)\n if (!fresh || fresh.status !== \"paused\") return\n if (!fresh.pausedAt || fresh.pausedAt.kind !== \"timerWait\" || fresh.pausedAt.nodeId !== t.nodeId) return\n const node = findNode(wf, t.nodeId)\n const successors = node ? wf.edges.filter((e) => e.from === node.id).map((e) => e.to) : []\n const firedAt = new Date().toISOString()\n const output = { firedAt, scheduledFor: t.fireAt }\n this.runs.recordExecution({\n runId: fresh.id,\n entry: {\n at: firedAt, nodeId: t.nodeId, inputKeys: [], status: \"resumed\", output,\n idempotencyKey: idempotencyKey(fresh.id, t.nodeId, `timer:${t.id}`),\n },\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: { ...fresh.context, [t.nodeId]: output },\n })\n this.log(`[workflow:${wf.id}] run ${fresh.id} resumed from timer \"${t.nodeId}\" (fired ${firedAt})`)\n this.emitRunEvent({ runId: fresh.id, workflowId: wf.id, nodeId: t.nodeId, phase: \"resumed\", status: \"running\", note: `timer:${t.id}` })\n })\n void this.walk(wf, t.runId, `timer:${t.id}`)\n .catch((e: any) => this.log(`[workflow:${wf.id}] walk-after-timer failed: ${e.message}`))\n }\n\n /** Fire a specific workflow by id, bypassing matchByTrigger. Used by\n * `trigger.hook` and `trigger.cron` subscribers — they already KNOW\n * which workflow they're firing, and the workflow's trigger config\n * may not carry a `source` field to match against the caller's event.\n * `trigger` is synthesized so the reused dispatchOne path still has\n * something to feed matchesResume/paused-run resume logic. */\n async dispatchWorkflow(args: {\n workflowId: string\n entityRef: EntityRef\n event: TriggerEvent\n trigger?: { source?: string; project?: string; repo?: string; chat?: string; labels?: string[] }\n }): Promise<{ claimed: boolean; run: WorkflowRun | null }> {\n const wf = this.store.list().find((w) => w.id === args.workflowId)\n if (!wf) { this.log(`[workflows] dispatchWorkflow: workflow \"${args.workflowId}\" not found`); return { claimed: false, run: null } }\n const triggerNode = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n const cfg = (triggerNode?.config ?? {}) as { source?: string; filter?: Record<string, unknown> }\n const effective = {\n source: args.trigger?.source ?? cfg.source ?? \"hook\",\n project: args.trigger?.project,\n repo: args.trigger?.repo,\n chat: args.trigger?.chat,\n labels: args.trigger?.labels,\n }\n return this.dispatchOne(wf, args.entityRef, args.event, effective)\n }\n\n /** Fire 👀 + start the typing loop for a channel-triggered run, mirroring\n * what MessageRouter does when it owns the conversation. Without this,\n * workflow-claimed messages lose the \"I see you, I'm working on it\" UX\n * that users learn to expect from the agent path.\n *\n * Best-effort throughout: a failing adapter call must not break the walk.\n * Idempotent: repeated calls for the same runId no-op (typing loop stays). */\n private startChannelAck(runId: string, payload: Record<string, unknown>): void {\n const channel = String(payload.channel ?? \"\")\n const chatId = String(payload.chatId ?? \"\")\n if (!channel || !chatId) return\n const adapter = this.channels[channel] as AckCapableAdapter | undefined\n if (!adapter) return\n const accountId = typeof payload.accountId === \"string\" ? payload.accountId : undefined\n const event = payload.event as { id?: unknown } | undefined\n const messageId = typeof event?.id === \"string\" ? event.id : (event?.id != null ? String(event.id) : undefined)\n\n if (messageId && adapter.react) {\n try { void Promise.resolve(adapter.react(chatId, messageId, \"👀\", accountId)).catch(() => {}) }\n catch { /* swallow — ack is decorative */ }\n }\n if (this.ackTypingTimers.has(runId)) return\n if (!adapter.sendTyping) return\n const tick = () => {\n try { void Promise.resolve(adapter.sendTyping!(chatId, accountId)).catch(() => {}) }\n catch { /* swallow */ }\n }\n tick()\n const timer = setInterval(tick, ACK_TYPING_INTERVAL_MS)\n this.ackTypingTimers.set(runId, timer)\n }\n\n /** Stop the typing loop for a run. Called when a run leaves the running\n * state (terminal status OR pause — paused runs are waiting on an external\n * event and should not appear \"still typing\"). */\n private stopChannelAck(runId: string): void {\n const timer = this.ackTypingTimers.get(runId)\n if (!timer) return\n clearInterval(timer)\n this.ackTypingTimers.delete(runId)\n }\n\n /** Main entry. The hook subscribers map channel events into this shape.\n * `claimed` lists workflows that are actively handling this event for the\n * entity — newly created, resumed, forwarded to a remote home, or dropped\n * as a concurrent duplicate of a running run. The pre:channel-message hook\n * uses this to suppress the router's default reply so the workflow owns\n * the conversation (no double-send). A paused run whose resumeMatch didn't\n * accept the event is deliberately NOT claimed, so unrelated chatter flows\n * through the default agent while the workflow waits for the signal it\n * cares about. */\n async dispatch(args: {\n trigger: { source: string; project?: string; repo?: string; chat?: string; labels?: string[] }\n entityRef: EntityRef\n event: TriggerEvent\n /** When true, this dispatch was initiated by a peer's broadcast (received\n * via /workflow/event with kind=trigger). Only workflows that have\n * explicitly opted in via `mesh.allowRemote: true` (and pass the\n * `mesh.peers` allowlist when set) match in that mode — local workflows\n * without the opt-in stay isolated. The flag also short-circuits the\n * outbound fan-out below so a remote-origin dispatch never re-broadcasts\n * back through the mesh (no echo loops). */\n fromRemote?: { peer: string }\n }): Promise<{ claimed: Workflow[]; runs: WorkflowRun[] }> {\n const matches = this.matchByTrigger(args.trigger, args.fromRemote)\n const willFan = matches.some((m) => m.fanOut)\n const toRun = willFan ? matches : matches.slice(0, 1)\n const claimed: Workflow[] = []\n const runs: WorkflowRun[] = []\n for (const wf of toRun) {\n const r = await this.dispatchOne(wf, args.entityRef, args.event, args.trigger)\n if (r.claimed) claimed.push(wf)\n if (r.run) runs.push(r.run)\n }\n\n // Mesh trigger fan-out. Only emit on local-origin dispatches; broadcasts\n // from peers (`fromRemote`) are never re-broadcast or we'd echo. The\n // forwarder decides per-peer whether to actually deliver — typically based\n // on cached peer agent-cards advertising allowRemote workflows for this\n // trigger source.\n if (!args.fromRemote && this.forwarder?.broadcastTrigger) {\n // Fire-and-forget: a slow / unhealthy peer must not delay the local\n // hook return path that the router awaits. Failures are logged inside\n // the forwarder.\n void Promise.resolve(this.forwarder.broadcastTrigger({\n trigger: args.trigger,\n entityRef: args.entityRef,\n event: args.event,\n })).catch((e: any) => this.log(`[mesh-broadcast] ${e?.message ?? e}`))\n }\n\n return { claimed, runs }\n }\n\n private matchByTrigger(\n t: { source: string; project?: string; repo?: string; chat?: string; labels?: string[] },\n fromRemote?: { peer: string },\n ): Workflow[] {\n const all = this.store.list()\n const out: Workflow[] = []\n for (const wf of all) {\n // Lifecycle gate: only `active` workflows match new triggers.\n // disabled (operator kill switch) and quarantined (set by the\n // conflict detector) both opt out of fresh dispatches; in-flight\n // runs created while active continue to advance.\n if (wf.state && wf.state !== \"active\") continue\n // Mesh isolation: remote-origin events only see workflows that opted in.\n if (fromRemote) {\n if (!wf.mesh?.allowRemote) continue\n const allowed = wf.mesh.peers\n if (allowed && allowed.length > 0 && !allowed.includes(fromRemote.peer)) continue\n }\n const trigger = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n if (!trigger) continue\n const cfg = trigger.config as {\n source?: string\n filter?: { project?: string; repo?: string; chat?: string; labels?: string[]; fromJid?: string }\n }\n if (cfg.source !== t.source) continue\n // Project-scope gate. A workflow's top-level `project:` field is\n // a hard scope: events from a different project never match. This\n // is what stops cross-tenant fan-out — e.g. an `on:gitlab-issue`\n // event from `ksi/int.ksi.tn` reaching `mtgl-pm-triage` (which is\n // tagged `project: mtgl/mtgl-system-v2`). Workflows with no\n // `project` field are global and match across projects (rare —\n // typically cross-project chores or templates). Events with no\n // `t.project` (manual / cron / 1:1 chat) bypass this check; the\n // project field only constrains project-scoped event sources.\n if (wf.project && t.project && wf.project !== t.project) continue\n const f = cfg.filter\n if (f) {\n if (f.project && f.project !== \"*\" && f.project !== t.project) continue\n if (f.repo && f.repo !== \"*\" && f.repo !== t.repo) continue\n if (f.chat && f.chat !== \"*\" && normalizeChat(t.source, f.chat) !== normalizeChat(t.source, t.chat)) continue\n if (f.labels?.length) {\n const have = new Set(t.labels ?? [])\n const hit = f.labels.some((l) => have.has(l))\n if (!hit) continue\n }\n }\n out.push(wf)\n }\n return out.sort((a, b) => b.priority - a.priority || a.id.localeCompare(b.id))\n }\n\n /**\n * Phase 1 commit 6.c — thin wrapper around the legacy dispatch. Calls\n * `dispatchOneLegacy` then, when `getLedgerMode(\"workflow\") !== \"off\"`,\n * records the dispatch to the intent ledger and reports any divergence\n * between ledger and legacy. Wrapped in try/catch so a ledger failure\n * never breaks workflow dispatch — legacy stays authoritative until\n * the 1c per-source promotion lands.\n */\n private async dispatchOne(\n workflow: Workflow,\n entityRef: EntityRef,\n event: TriggerEvent,\n trigger: { source: string; project?: string; repo?: string; chat?: string; labels?: string[] },\n ): Promise<{ claimed: boolean; run: WorkflowRun | null }> {\n const result = await this.dispatchOneLegacy(workflow, entityRef, event, trigger)\n if (getLedgerMode(\"workflow\") !== \"off\") {\n try {\n recordWorkflowDispatch(\n getDefaultLedger(),\n {\n workflowId: workflow.id,\n eventId: event.id,\n triggerSource: trigger.source,\n project: trigger.project ?? null,\n entityRef,\n },\n JSON.stringify({ event, trigger, entityRef }),\n {\n claimed: result.claimed,\n runId: result.run?.id ?? null,\n },\n )\n } catch (e: any) {\n this.log(`[ledger] workflow ${workflow.id} run ${result.run?.id ?? \"?\"} record failed: ${e?.message ?? e}`)\n }\n }\n return result\n }\n\n private async dispatchOneLegacy(\n workflow: Workflow,\n entityRef: EntityRef,\n event: TriggerEvent,\n trigger: { source: string; project?: string; repo?: string; chat?: string; labels?: string[] },\n ): Promise<{ claimed: boolean; run: WorkflowRun | null }> {\n const activeRunId = this.runs.getActiveByEntity(entityRef)\n const homeNode = activeRunId ? this.runs.getHomeNodeByEntity(entityRef) : null\n\n // Remote home node — forward via mesh and bail. The remote owns the\n // conversation, so we still claim the event locally (no default reply).\n if (activeRunId && homeNode && homeNode !== this.nodeId) {\n if (!this.forwarder) {\n this.log(`[workflow:${workflow.id}] run ${activeRunId} home'd on \"${homeNode}\" but no forwarder — dropping`)\n return { claimed: false, run: null }\n }\n try {\n await this.forwarder.forwardTransition(homeNode, { workflowId: workflow.id, event, entityRef })\n } catch (e: any) {\n this.log(`[workflow:${workflow.id}] forward to \"${homeNode}\" failed: ${e.message}`)\n }\n return { claimed: true, run: null }\n }\n\n let run = activeRunId ? this.runs.get(activeRunId) : null\n if (!run) {\n // New entity → create a run, seed trigger context, enqueue trigger's successors.\n const init = initialPendingFromTrigger(workflow)\n if (!init) { this.log(`[workflow:${workflow.id}] no trigger node found`); return { claimed: false, run: null } }\n run = this.runs.create({\n workflowId: workflow.id,\n initialPending: init.pending,\n entityRef,\n initialContext: { [init.triggerId]: event.payload },\n })\n this.log(`[workflow:${workflow.id}] run ${run.id} created from trigger \"${init.triggerId}\" for ${entityRef.id}`)\n this.emitRunEvent({ runId: run.id, workflowId: workflow.id, phase: \"created\", status: run.status, homeNode: run.homeNode })\n // Channel-triggered: kick off the react+typing lifecycle so users see\n // the same \"I'm on it\" affordances they get from the router's path.\n if (trigger.source.endsWith(\"-message\")) this.startChannelAck(run.id, event.payload)\n } else if (run.status === \"paused\" && run.pausedAt) {\n // Resume path: a matching event arrived for a paused run. Only\n // `checkpoint` pauses resume on channel events. userTask pauses on\n // form submit; subProcess on child end; signalWait on signal.emit;\n // timerWait on timer fire. Each of those has its own resume entry\n // point elsewhere — here we just drop the channel event and decline\n // to claim (so default routing handles it).\n if (run.pausedAt.kind !== \"checkpoint\") {\n this.log(`[workflow:${workflow.id}] run ${run.id} paused on ${run.pausedAt.kind} — channel event bypasses workflow`)\n return { claimed: false, run: null }\n }\n if (!matchesResume(run.pausedAt.resumeMatch, trigger, event)) {\n this.log(`[workflow:${workflow.id}] run ${run.id} paused but event doesn't match resumeMatch — dropping`)\n return { claimed: false, run: null }\n }\n const checkpointNode = findNode(workflow, run.pausedAt.nodeId)\n const successors = checkpointNode\n ? workflow.edges.filter((e) => e.from === checkpointNode.id).map((e) => e.to)\n : []\n const resumeEntry = {\n at: new Date().toISOString(),\n nodeId: run.pausedAt.nodeId,\n inputKeys: [],\n status: \"resumed\" as const,\n output: { event: event.payload },\n idempotencyKey: idempotencyKey(run.id, run.pausedAt.nodeId, `resume:${event.id}`),\n }\n const newContext: Record<string, Record<string, unknown>> = {\n ...run.context,\n [run.pausedAt.nodeId]: { event: event.payload },\n }\n const pausedCheckpointName = run.pausedAt.checkpointName\n const updated = this.runs.recordExecution({\n runId: run.id,\n entry: resumeEntry,\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: newContext,\n })\n if (updated) run = updated\n this.log(`[workflow:${workflow.id}] run ${run.id} resumed from checkpoint \"${pausedCheckpointName}\"`)\n if (trigger.source.endsWith(\"-message\")) this.startChannelAck(run.id, event.payload)\n } else if (run.status === \"running\") {\n // Concurrent message on an already-running run. v1 policy: drop —\n // but still claim the event so the router doesn't ALSO reply.\n this.log(`[workflow:${workflow.id}] run ${run.id} already running; dropping concurrent event ${event.id}`)\n return { claimed: true, run: null }\n } else {\n // completed / failed / canceled — dead run. The filter matched but\n // this conversation's workflow is over, so let default routing handle.\n this.log(`[workflow:${workflow.id}] run ${run.id} is ${run.status}; dropping event ${event.id}`)\n return { claimed: false, run: null }\n }\n\n // Background walk — the dispatch call already returned 200 to the\n // webhook source. Agent calls inside the walk can take minutes.\n void this.walk(workflow, run.id, event.id)\n return { claimed: true, run }\n }\n\n /** Execute the pending queue until empty, paused, or failed. Wraps the\n * inner walk in try/finally so the channel-ack typing loop is always\n * cleared on exit — regardless of which branch (failed / paused / end /\n * drained) returned. Without this wrapper, an early return inside the\n * loop would leave the typing indicator running forever. */\n private async walk(workflow: Workflow, runId: string, triggeringEventId: string): Promise<void> {\n try { await this.walkInner(workflow, runId, triggeringEventId) }\n finally { this.stopChannelAck(runId) }\n }\n\n private async walkInner(workflow: Workflow, runId: string, triggeringEventId: string): Promise<void> {\n let run = this.runs.get(runId)\n if (!run) return\n\n // Batch-parallel walk: dispatch up to MAX_PARALLEL_PER_RUN pending\n // node handlers concurrently via Promise.allSettled. Each node's\n // handler runs unguarded (parallel); only the commit (recordExecution\n // + joinCounters + pause transitions) runs under the per-run mutex.\n // For linear workflows (single pending) this collapses to the old\n // serial path — identical behaviour, identical history.\n while (run.status === \"running\" && run.pending.length > 0) {\n const batch = run.pending.slice(0, MAX_PARALLEL_PER_RUN)\n await Promise.allSettled(\n batch.map((nodeId) => this.executeNodeAndCommit(workflow, runId, nodeId, triggeringEventId)),\n )\n const fresh = this.runs.get(runId)\n if (!fresh) return\n run = fresh\n if (run.status !== \"running\") return\n }\n }\n\n /** Execute one node + commit its result. Handler runs outside the\n * mutex (so sibling branches run concurrently). Dedup + recordExecution\n * + joinCounters run inside commit() so state transitions never\n * interleave. */\n private async executeNodeAndCommit(\n workflow: Workflow,\n runId: string,\n nodeId: string,\n triggeringEventId: string,\n ): Promise<void> {\n const key = idempotencyKey(runId, nodeId, triggeringEventId)\n\n // --- phase 1 (guarded): dedup check + resolve handler ---\n const prelude = await this.commit(runId, () => {\n const run = this.runs.get(runId)\n if (!run) return { kind: \"done\" as const }\n if (run.status !== \"running\") return { kind: \"done\" as const }\n const node = findNode(workflow, nodeId)\n if (!node) {\n this.log(`[workflow:${workflow.id}] missing node \"${nodeId}\" — marking run failed`)\n this.runs.recordExecution({\n runId, entry: {\n at: new Date().toISOString(), nodeId, inputKeys: [], status: \"failed\",\n idempotencyKey: key, note: \"node definition missing from workflow\",\n },\n nextPending: run.pending.filter((x) => x !== nodeId),\n status: \"failed\",\n })\n return { kind: \"done\" as const }\n }\n const handler = resolveHandler(node.type)\n if (!handler) {\n this.log(`[workflow:${workflow.id}] no handler for node type \"${node.type}\"`)\n this.runs.recordExecution({\n runId, entry: {\n at: new Date().toISOString(), nodeId, inputKeys: [], status: \"failed\",\n idempotencyKey: key, note: `no handler for node type \"${node.type}\"`,\n },\n nextPending: run.pending.filter((x) => x !== nodeId),\n status: \"failed\",\n })\n return { kind: \"done\" as const }\n }\n if (run.history.some((h) => h.idempotencyKey === key)) {\n this.runs.recordExecution({\n runId, entry: {\n at: new Date().toISOString(), nodeId, inputKeys: [], status: \"skipped\",\n idempotencyKey: key + \"_dedup\", note: \"duplicate execution dropped\",\n },\n nextPending: run.pending.filter((x) => x !== nodeId),\n })\n return { kind: \"done\" as const }\n }\n return { kind: \"go\" as const, run, node, handler }\n })\n if (prelude.kind === \"done\") return\n\n // --- phase 2 (unguarded): actually run the handler, parallel-safe ---\n const { run, node, handler } = prelude\n const inputKeys = workflow.edges.filter((e) => e.to === nodeId).map((e) => e.from)\n\n // Improvement plan #9c — workflow steps populate task_traces so a\n // single `agentx trace list --workflow <runId>` shows every step,\n // joined by workflow_run_id. Agent nodes generate their own trace\n // via registry.execute (with workflow context already plumbed via\n // task.workflowRunId) so wrapping them here would double-count;\n // every other node type gets a synthetic trace row keyed by node\n // type so operators can answer \"which transform / branch / signal\n // / action.builtin step ran when, and how long?\"\n let workflowTraceTaskId: string | undefined\n if (node.type !== \"agent\") {\n const db = openDb()\n if (db) {\n try {\n workflowTraceTaskId = recordTraceStart(db, {\n agentId: `workflow:${node.type}`,\n channel: \"workflow\",\n chatId: `${workflow.id}:${nodeId}`,\n workflowRunId: runId,\n workflowId: workflow.id,\n workflowNodeId: nodeId,\n messagePreview: `${node.type} ${nodeId}`,\n })\n } catch { /* observability best-effort */ }\n }\n }\n\n // Improvement plan #9b — per-node retry on hard errors. Pause\n // results (userTask, signalWait, timerWait, subProcess) are\n // intentionally NOT retried — pausing is a normal lifecycle\n // transition. Only `{error}` results (handler returned an error\n // or threw) consume a retry budget.\n const retry = (node as { retry?: { maxAttempts: number; backoffMs: number } }).retry\n ?? { maxAttempts: 1, backoffMs: 1000 }\n let result: NodeResult = { error: \"retry-loop did not run\" }\n let attempt = 0\n while (attempt < retry.maxAttempts) {\n attempt++\n try {\n result = await handler({\n workflow, run, node,\n channels: this.channels,\n agents: this.agents,\n forwardChannelSend: this.forwarder?.forwardChannelSend?.bind(this.forwarder),\n log: this.log,\n })\n } catch (e: any) {\n this.log(`[workflow:${workflow.id}] handler \"${node.type}\" threw: ${e.message}`)\n result = { error: e.message }\n }\n // Stop on success or pause; keep going only on hard error.\n if (!result.error || result.paused) break\n if (attempt < retry.maxAttempts) {\n const wait = retry.backoffMs * Math.pow(2, attempt - 1)\n this.log(`[workflow:${workflow.id}] node \"${nodeId}\" failed (attempt ${attempt}/${retry.maxAttempts}): ${result.error.slice(0, 120)} — retrying in ${wait}ms`)\n await new Promise((r) => setTimeout(r, wait))\n }\n }\n\n // Finalize the workflow-step trace row, regardless of result.\n // Status mirrors the result shape: error → \"error\", paused → \"ok\"\n // (the node ran successfully and is waiting on external input;\n // pause is a normal lifecycle transition, not a failure), else \"ok\".\n if (workflowTraceTaskId) {\n const db = openDb()\n if (db) {\n try {\n recordTraceEnd(db, workflowTraceTaskId, {\n status: result.error ? \"error\" : \"ok\",\n error: result.error ?? null,\n })\n } catch { /* */ }\n }\n }\n\n // --- phase 3 (guarded): persist result + side effects ---\n await this.commit(runId, async () => {\n const fresh = this.runs.get(runId)\n if (!fresh) return\n const now = new Date().toISOString()\n const remainingFromPending = fresh.pending.filter((x) => x !== nodeId)\n\n if (result.error) {\n // If run is already paused/terminal from a sibling, just append\n // the exec entry without flipping status.\n const statusUpdate = fresh.status === \"running\" ? (\"failed\" as const) : undefined\n this.runs.recordExecution({\n runId, entry: {\n at: now, nodeId, inputKeys, status: \"failed\",\n idempotencyKey: key, note: result.error.slice(0, 200),\n },\n nextPending: remainingFromPending,\n status: statusUpdate,\n })\n this.emitRunEvent({ runId, workflowId: workflow.id, nodeId, phase: \"failed\", status: statusUpdate ?? fresh.status, note: result.error.slice(0, 200) })\n return\n }\n if (result.paused && result.pausedAt) {\n let pausedAt = result.pausedAt\n let spawnedChild: WorkflowRun | null = null\n if (result.spawnChild && pausedAt.kind === \"subProcess\") {\n spawnedChild = await this.spawnChild({\n parent: fresh, parentNodeId: nodeId,\n childWorkflowId: result.spawnChild.workflowId,\n input: result.spawnChild.input,\n })\n if (!spawnedChild) {\n this.runs.recordExecution({\n runId, entry: {\n at: now, nodeId, inputKeys, status: \"failed\",\n idempotencyKey: key,\n note: `subProcess: child workflow \"${result.spawnChild.workflowId}\" not found`,\n },\n nextPending: remainingFromPending, status: \"failed\",\n })\n return\n }\n pausedAt = { ...pausedAt, childRunId: spawnedChild.id }\n }\n this.runs.recordExecution({\n runId, entry: {\n at: now, nodeId, inputKeys, status: \"paused\", idempotencyKey: key,\n },\n nextPending: remainingFromPending,\n status: \"paused\",\n pausedAt,\n })\n this.emitRunEvent({ runId, workflowId: workflow.id, nodeId, phase: \"paused\", status: \"paused\", note: pausedAt.kind })\n if (spawnedChild) this.kickChildWalk(spawnedChild)\n if (pausedAt.kind === \"timerWait\") {\n try {\n this.timers.schedule({\n runId, workflowId: workflow.id, nodeId: pausedAt.nodeId,\n fireAt: pausedAt.fireAt,\n cancelKey: `${runId}:${pausedAt.nodeId}`,\n })\n } catch (e: any) {\n this.log(`[workflow:${workflow.id}] timer schedule failed: ${e.message}`)\n }\n }\n return\n }\n\n // Success path — fold output, join-gate, enqueue successors.\n const output: Record<string, unknown> = result.output ?? {}\n const newContext: Record<string, Record<string, unknown>> = { ...fresh.context, [nodeId]: output }\n const { nextPending } = nextNodes({ workflow, fromNodeId: nodeId, selectedPort: result.port })\n\n const readyNext: string[] = []\n const joinCounters: Record<string, string[]> = { ...(fresh.joinCounters ?? {}) }\n for (const succId of nextPending) {\n const succ = findNode(workflow, succId)\n const isJoin = succ?.type === \"gateway.parallel\" && String((succ.config as { mode?: unknown } | undefined)?.mode ?? \"fanOut\") === \"join\"\n if (!isJoin) { readyNext.push(succId); continue }\n const arrived = joinCounters[succId] ?? []\n if (!arrived.includes(nodeId)) arrived.push(nodeId)\n joinCounters[succId] = arrived\n const expected = workflow.edges.filter((e) => e.to === succId).length\n if (arrived.length >= expected) {\n readyNext.push(succId)\n delete joinCounters[succId]\n }\n }\n const merged = [...remainingFromPending, ...readyNext]\n const terminal = node.type === \"end\"\n\n // Respect a sibling branch's pause: if the run is already non-running,\n // don't flip status back to running. Still record the exec entry +\n // merged pending so the resume path can see the siblings' successors.\n const statusUpdate = fresh.status === \"running\"\n ? (terminal ? (String(node.config.status ?? \"completed\") as WorkflowRun[\"status\"]) : (\"running\" as const))\n : undefined\n\n const updated = this.runs.recordExecution({\n runId,\n entry: { at: now, nodeId, inputKeys, status: \"ok\", output, idempotencyKey: key },\n nextPending: terminal ? [] : merged,\n status: statusUpdate,\n context: newContext,\n joinCounters,\n })\n\n this.emitRunEvent({\n runId, workflowId: workflow.id, nodeId,\n phase: terminal ? \"completed\" : \"ok\",\n status: updated?.status ?? fresh.status,\n })\n\n if (result.emitSignal) {\n this.emitSignal({\n name: result.emitSignal.name,\n scope: result.emitSignal.scope,\n workflowId: workflow.id,\n payload: result.emitSignal.payload,\n })\n }\n\n if (terminal && updated && updated.parentRunId && updated.parentNodeId) {\n void this.resumeParent({\n parentRunId: updated.parentRunId,\n parentNodeId: updated.parentNodeId,\n childRun: updated,\n }).catch((e: any) => this.log(`[workflow:${workflow.id}] resumeParent failed: ${e.message}`))\n }\n })\n }\n\n // ---------------- Sub-process helpers ----------------\n\n /** Create a child run for a sub-process node. Returns null if the child\n * workflow id doesn't exist. Depth cap is enforced at the handler layer\n * before we get here. */\n private async spawnChild(args: {\n parent: WorkflowRun\n parentNodeId: string\n childWorkflowId: string\n input: Record<string, unknown>\n }): Promise<WorkflowRun | null> {\n const childWf = this.store.list().find((w) => w.id === args.childWorkflowId)\n if (!childWf) return null\n\n const init = initialPendingFromTrigger(childWf)\n if (!init) { this.log(`[workflow:${childWf.id}] no trigger node — cannot spawn as child`); return null }\n\n // Child gets a fresh context, seeded by the parent's inputMap. Top-level\n // keys are child-context keys; values are bundles. Parent linkage is\n // stamped onto the trigger node's bundle so downstream templates can\n // read {{trigger.parentRunId}} if useful.\n const childContext: Record<string, Record<string, unknown>> = {\n ...(args.input as Record<string, Record<string, unknown>>),\n }\n childContext[init.triggerId] = {\n ...(childContext[init.triggerId] ?? {}),\n parentRunId: args.parent.id,\n parentNodeId: args.parentNodeId,\n }\n\n const entityRef: EntityRef = {\n backend: \"agentx-internal\",\n id: `subprocess:${args.parent.id}:${args.parentNodeId}`,\n }\n\n const child = this.runs.create({\n workflowId: childWf.id,\n initialPending: init.pending,\n entityRef,\n initialContext: childContext,\n parentRunId: args.parent.id,\n parentNodeId: args.parentNodeId,\n rootRunId: args.parent.rootRunId ?? args.parent.id,\n depth: (args.parent.depth ?? 0) + 1,\n })\n this.log(`[workflow:${childWf.id}] child run ${child.id} spawned from ${args.parent.id}/${args.parentNodeId} (depth ${child.depth})`)\n // The caller (walk loop) kicks off the child walk AFTER persisting the\n // parent's paused state, so a fast child reaching end doesn't try to\n // resume a parent that hasn't been saved yet.\n return child\n }\n\n /** Kick a child run's walk loop — used by the walk loop after persisting\n * the parent's pause. Fire-and-forget. */\n private kickChildWalk(childRun: WorkflowRun): void {\n const childWf = this.store.list().find((w) => w.id === childRun.workflowId)\n if (!childWf) return\n void this.walk(childWf, childRun.id, `spawn:${childRun.parentRunId}:${childRun.parentNodeId}`)\n .catch((e: any) => this.log(`[workflow:${childWf.id}] child walk failed: ${e.message}`))\n }\n\n /** Resume a parent run whose subProcess node was awaiting this child. */\n async resumeParent(args: {\n parentRunId: string\n parentNodeId: string\n childRun: WorkflowRun\n }): Promise<void> {\n const preliminaryParent = this.runs.get(args.parentRunId)\n if (!preliminaryParent) return\n const parentWf = this.store.list().find((w) => w.id === preliminaryParent.workflowId)\n if (!parentWf) return\n\n await this.commit(args.parentRunId, () => {\n const parent = this.runs.get(args.parentRunId)\n if (!parent || parent.status !== \"paused\") return\n if (!parent.pausedAt || parent.pausedAt.kind !== \"subProcess\") return\n if (parent.pausedAt.nodeId !== args.parentNodeId) return\n if (parent.pausedAt.childRunId !== args.childRun.id) return\n\n const node = findNode(parentWf, args.parentNodeId)\n const successors = node ? parentWf.edges.filter((e) => e.from === node.id).map((e) => e.to) : []\n const childOutput: Record<string, unknown> = {\n childRunId: args.childRun.id,\n status: args.childRun.status,\n output: pickChildOutput(args.childRun),\n }\n this.runs.recordExecution({\n runId: parent.id,\n entry: {\n at: new Date().toISOString(),\n nodeId: args.parentNodeId,\n inputKeys: [],\n status: \"resumed\",\n output: childOutput,\n idempotencyKey: idempotencyKey(parent.id, args.parentNodeId, `child:${args.childRun.id}`),\n },\n nextPending: successors,\n status: \"running\",\n pausedAt: null,\n context: { ...parent.context, [args.parentNodeId]: childOutput },\n })\n this.log(`[workflow:${parentWf.id}] parent run ${parent.id} resumed from subProcess child ${args.childRun.id}`)\n this.emitRunEvent({ runId: parent.id, workflowId: parentWf.id, nodeId: args.parentNodeId, phase: \"resumed\", status: \"running\", note: `child:${args.childRun.id}` })\n })\n void this.walk(parentWf, args.parentRunId, `child:${args.childRun.id}`)\n }\n\n}\n\n/** Pick the most informative output bundle from a child run's history as\n * the `output` field exposed to the parent's subProcess node. We prefer\n * the last non-empty `ok` entry, which is typically the last meaningful\n * step before `end`. */\nfunction pickChildOutput(child: WorkflowRun): Record<string, unknown> {\n for (let i = child.history.length - 1; i >= 0; i--) {\n const h = child.history[i]\n if (h.status === \"ok\" && h.output && Object.keys(h.output).length > 0) {\n return h.output\n }\n }\n return {}\n}\n\n/** Does the incoming event's trigger fields match the checkpoint's\n * resumeMatch filter? Empty/missing fields are wildcards (match anything),\n * so the default `resumeMatch: {}` accepts any event on the entity. */\nfunction matchesResume(\n resumeMatch: Record<string, unknown>,\n trigger: { source?: string; project?: string; repo?: string; chat?: string; labels?: string[] },\n event: TriggerEvent,\n): boolean {\n const match = (k: keyof typeof trigger): boolean => {\n const want = resumeMatch[k]\n if (want === undefined || want === \"\" || want === \"*\") return true\n if (Array.isArray(want)) {\n const have = new Set(trigger.labels ?? [])\n return (want as string[]).some((l) => have.has(String(l)))\n }\n if (k === \"chat\") {\n return normalizeChat(trigger.source, String(want)) === normalizeChat(trigger.source, trigger.chat)\n }\n return trigger[k] === want\n }\n // Fields supported in v1: source, project, repo, chat, labels. Additional\n // keys in resumeMatch are ignored (won't accidentally block resume).\n if (!match(\"source\")) return false\n if (!match(\"project\")) return false\n if (!match(\"repo\")) return false\n if (!match(\"chat\")) return false\n // `eventIdLike`: optional substring match on the event id, useful for\n // resuming only on events that carry a specific marker.\n const eventIdLike = resumeMatch.eventIdLike\n if (typeof eventIdLike === \"string\" && eventIdLike && !event.id.includes(eventIdLike)) return false\n return true\n}\n\n/** Normalize a chat identifier for filter comparison. Channel-aware: WhatsApp\n * ids drift between formats (raw digits \"21624309128\", JID\n * \"21624309128@s.whatsapp.net\", group JID \"...-...@g.us\", and human-formatted\n * \"+216 24 309 128\") depending on whether they came from the adapter's\n * payload, a copy-paste from the WA UI, or an editor field. We collapse all\n * of these to the canonical bare-id form before equality so authors can write\n * filters in whichever form is convenient. Other channels (Telegram, GitLab,\n * ...) use stable id formats from their APIs and don't need normalization. */\nfunction normalizeChat(source: string | undefined, value: string | undefined): string {\n if (value == null) return \"\"\n if (source === \"whatsapp-message\") {\n return value.replace(/@s\\.whatsapp\\.net$|@g\\.us$/i, \"\").replace(/[\\s+()]/g, \"\")\n }\n return value\n}\n\nexport { idempotencyKey } from \"./run-store\"\n","import type { HookContext, HookEvent, HookResult } from \"@/hooks/types\"\nimport type { IncomingMessage } from \"@/channels/types\"\nimport type { WorkflowDispatcher } from \"./dispatcher\"\nimport type { EntityRef } from \"./types\"\n\n// --- Workflow hook subscribers (V2) ---\n//\n// Thin mappers: take whatever raw payload a hook fires with, produce a\n// typed trigger bundle, and call the dispatcher. No routing decisions\n// here — the dispatcher picks matching workflows, the engine walks the\n// DAG, node handlers do the work.\n//\n// For channel-triggered workflows, the subscriber returns\n// { blocked: true } when the event is claimed by a workflow so the router\n// skips its default agent reply — otherwise BOTH the workflow's action.send\n// AND the router's own reply would fire, producing duplicate messages.\n// GitLab/pipeline subscribers stay non-blocking because those events run\n// side-by-side with agent routing by design.\n//\n// A workflow can opt out per-trigger with `trigger.config.passthrough = true`\n// to let the default router reply alongside the workflow (useful for\n// observability-only workflows that just log or tag).\n\nexport function createWorkflowHookHandlers(dispatcher: WorkflowDispatcher): Partial<Record<HookEvent, (ctx: HookContext) => Promise<HookResult>>> {\n return {\n // Generic inbound channel message — WhatsApp, Telegram, Discord, Slack.\n // The router fires this before resolving an agent, so workflows see the\n // message with full structured detail (sender, group, media, etc.).\n \"pre:channel-message\": async (ctx) => {\n const msg = ctx.msg as IncomingMessage | undefined\n if (!msg) return {}\n const source = channelToTriggerSource(msg.channel)\n if (!source) return {}\n\n const chatId = msg.group?.id ?? msg.sender.id\n const entityRef: EntityRef = { backend: msg.channel, id: `${msg.channel}:${chatId}` }\n\n const { claimed } = await dispatcher.dispatch({\n trigger: {\n source,\n chat: chatId,\n },\n entityRef,\n event: {\n id: `${msg.channel}:${msg.accountId}:${msg.id}`,\n payload: buildChannelTriggerPayload(msg),\n },\n })\n\n if (claimed.length === 0) return {}\n\n // Opt-out: if every matched workflow declared `passthrough: true` on its\n // trigger node, let the router's default reply run alongside. Otherwise\n // the workflow owns the conversation — block the router.\n const allPassthrough = claimed.every((wf) => {\n const trigger = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n return (trigger?.config as { passthrough?: boolean } | undefined)?.passthrough === true\n })\n if (allPassthrough) return {}\n\n return {\n blocked: true,\n message: `handled by workflow${claimed.length > 1 ? \"s\" : \"\"}: ${claimed.map((w) => w.id).join(\", \")}`,\n }\n },\n\n // GitLab issue events — open, update, close. Existing subscriber shape\n // from V1 is preserved so the GitLab adapter doesn't need changes.\n \"on:gitlab-issue\": async (ctx) => {\n const project = ctx.project as string | undefined\n const iid = ctx.iid as string | number | undefined\n const issueEvent = ctx.issueEvent as any\n if (!project || iid === undefined) return {}\n\n const labels: string[] = Array.isArray(issueEvent?.object_attributes?.labels)\n ? issueEvent.object_attributes.labels.map((l: any) => typeof l === \"string\" ? l : l?.title).filter(Boolean)\n : (issueEvent?.labels?.map((l: any) => l?.title) ?? [])\n const assignees = issueEvent?.assignees ?? issueEvent?.object_attributes?.assignee_ids ?? []\n\n const entityRef: EntityRef = { backend: \"gitlab\", id: `${project}#${iid}` }\n await dispatcher.dispatch({\n trigger: { source: \"gitlab-issue\", project, labels },\n entityRef,\n event: {\n id: `gitlab-issue:${project}:${iid}:${issueEvent?.object_attributes?.updated_at || issueEvent?.object_attributes?.action || \"evt\"}`,\n payload: {\n issue: {\n iid,\n title: issueEvent?.object_attributes?.title,\n description: issueEvent?.object_attributes?.description,\n url: issueEvent?.object_attributes?.url,\n action: issueEvent?.object_attributes?.action,\n labels,\n assignees,\n },\n project,\n channel: \"gitlab\",\n chatId: `${project}:issue:${iid}`,\n },\n },\n })\n return {}\n },\n\n // GitLab MR events — open, reopen, update, approved, merge, close.\n // Mirrors on:gitlab-issue; the gitlab adapter fires this in handleMR\n // after the project rule's merge_request clause has approved the\n // event. Workflow subscribers see the typed mr payload and can route\n // off attrs.action (e.g. fire a review on `open`, fire deploy on\n // `merge`).\n \"on:gitlab-mr\": async (ctx) => {\n const project = ctx.project as string | undefined\n const iid = ctx.iid as string | number | undefined\n const mrEvent = ctx.mrEvent as any\n if (!project || iid === undefined) return {}\n\n const labels: string[] = Array.isArray(ctx.labels)\n ? (ctx.labels as string[])\n : (Array.isArray(mrEvent?.object_attributes?.labels)\n ? mrEvent.object_attributes.labels.map((l: any) => typeof l === \"string\" ? l : l?.title).filter(Boolean)\n : [])\n const assignees = mrEvent?.assignees ?? mrEvent?.object_attributes?.assignee_ids ?? []\n const reviewers = mrEvent?.reviewers ?? []\n\n const entityRef: EntityRef = { backend: \"gitlab\", id: `${project}!${iid}` }\n await dispatcher.dispatch({\n trigger: { source: \"gitlab-mr\", project, labels },\n entityRef,\n event: {\n id: `gitlab-mr:${project}:${iid}:${mrEvent?.object_attributes?.updated_at || mrEvent?.object_attributes?.action || \"evt\"}`,\n payload: {\n mr: {\n iid,\n title: ctx.title,\n description: ctx.description,\n url: ctx.url,\n action: ctx.action,\n state: ctx.state,\n source_branch: ctx.source_branch,\n target_branch: ctx.target_branch,\n labels,\n assignees,\n reviewers,\n },\n project,\n channel: \"gitlab\",\n chatId: `${project}:merge_request:${iid}`,\n iid,\n title: ctx.title,\n description: ctx.description,\n url: ctx.url,\n action: ctx.action,\n state: ctx.state,\n source_branch: ctx.source_branch,\n target_branch: ctx.target_branch,\n labels,\n },\n },\n })\n return {}\n },\n\n // GitLab note events — comments on issues / MRs / commits. Mirrors\n // on:gitlab-mr; the gitlab adapter fires this in handleNote AFTER the\n // project rule's note clause has approved the event AND cascade-\n // prevention checks (AgentX marker, sentNoteIds, isBotUser) have\n // passed. Workflow subscribers can branch off mentions / noteableType\n // (e.g. mr-fix-loop fires on noteableType=merge_request when a\n // reviewer's comment matches a request-changes pattern).\n \"on:gitlab-note\": async (ctx) => {\n const project = ctx.project as string | undefined\n const noteId = ctx.noteId as string | undefined\n const noteableType = ctx.noteableType as string | undefined\n const noteableIid = ctx.noteableIid as string | undefined\n const text = ctx.text as string | undefined\n const author = ctx.authorUsername as string | undefined\n const mentions = (ctx.mentions ?? []) as string[]\n if (!project || !noteId || !noteableType || !noteableIid) return {}\n\n const entityRef: EntityRef =\n noteableType === \"merge_request\"\n ? { backend: \"gitlab\", id: `${project}!${noteableIid}` }\n : { backend: \"gitlab\", id: `${project}#${noteableIid}` }\n\n await dispatcher.dispatch({\n // The trigger field carries only structural matchers; per-event\n // payload (mentions, etc.) lives in `event.payload`. Workflows\n // that need to filter on mentions look at `{{trigger.mentions}}`\n // resolved from the payload, not from this trigger object.\n trigger: { source: \"gitlab-note\", project },\n entityRef,\n event: {\n id: `gitlab-note:${project}:${noteableType}:${noteableIid}:${noteId}`,\n payload: {\n note: {\n id: noteId,\n text,\n author,\n mentions,\n noteableType,\n noteableIid,\n noteableTitle: ctx.noteableTitle,\n },\n project,\n channel: \"gitlab\",\n chatId: `${project}:${noteableType}:${noteableIid}`,\n noteId,\n noteableType,\n noteableIid,\n text,\n author,\n mentions,\n },\n },\n })\n return {}\n },\n\n // GitLab pipeline events (success / failed / canceled). Scoped to\n // MR-linked pipelines — pushes to branches without an MR don't enter\n // the workflow dispatcher.\n \"on:gitlab-pipeline\": async (ctx) => {\n const project = ctx.project as string | undefined\n const pipelineId = ctx.pipelineId as string | number | undefined\n const status = ctx.status as string | undefined\n const ref = ctx.ref as string | undefined\n if (!project || !status) return {}\n\n const raw = ctx.raw as any\n const mrIid = raw?.merge_request?.iid\n if (!mrIid) return {}\n\n const entityRef: EntityRef = { backend: \"gitlab\", id: `${project}!${mrIid}` }\n await dispatcher.dispatch({\n trigger: { source: \"gitlab-pipeline\", project },\n entityRef,\n event: {\n id: `gitlab-pipeline:${project}:${pipelineId}:${status}`,\n payload: {\n pipeline: { id: pipelineId, status, ref },\n project,\n channel: \"gitlab\",\n chatId: `${project}:merge_request:${mrIid}`,\n },\n },\n })\n return {}\n },\n }\n}\n\nfunction channelToTriggerSource(channel: string): string | null {\n switch (channel) {\n case \"telegram\": return \"telegram-message\"\n case \"whatsapp\": return \"whatsapp-message\"\n case \"discord\": return \"discord-message\"\n case \"slack\": return \"slack-message\"\n default: return null\n }\n}\n\nfunction buildChannelTriggerPayload(msg: IncomingMessage): Record<string, unknown> {\n const chatId = msg.group?.id ?? msg.sender.id\n return {\n channel: msg.channel,\n chatId,\n accountId: msg.accountId,\n text: msg.text,\n fromJid: msg.sender.id,\n sender: {\n id: msg.sender.id,\n name: msg.sender.name,\n username: msg.sender.username,\n },\n contact: msg.channelMeta?.facts ? { facts: msg.channelMeta.facts } : undefined,\n group: msg.group ? { id: msg.group.id, name: msg.group.name } : undefined,\n channelMeta: msg.channelMeta,\n replyTo: msg.replyTo,\n replyToText: msg.replyToText,\n media: msg.media,\n event: { id: msg.id, timestamp: msg.timestamp?.toISOString?.() ?? new Date().toISOString() },\n }\n}\n","import { getNextCronDate } from \"@/crons/scheduler\"\nimport type { WorkflowDispatcher } from \"./dispatcher\"\nimport type { WorkflowStore } from \"./store\"\nimport type { HookEvent } from \"@/hooks/types\"\nimport type { HookRegistry } from \"@/hooks\"\n\n// --- Trigger wiring for Phase 3 ---\n//\n// Workflows with `trigger.cron` or `trigger.hook` nodes don't map to\n// channel events, so the regular hook subscribers in hooks.ts don't fire\n// them. This module wires those two sources:\n//\n// - trigger.cron: compute next fire time via getNextCronDate + chain\n// setTimeout. On fire, call dispatcher with a synthetic trigger event.\n// Each workflow gets its own timer loop; rescheduled on every fire.\n//\n// - trigger.hook: register a HookRegistry handler on whichever on:* event\n// the trigger node configures. On fire, map the hook context payload\n// directly into a trigger event.\n//\n// Called once from daemon boot after the dispatcher is constructed. Teardown\n// isn't wired for v1 — daemon restart clears timers.\n\nexport interface CronTriggerOptions {\n workflowId: string\n triggerNodeId: string\n spec: string\n timezone: string\n}\n\nexport function startWorkflowTriggers(args: {\n store: WorkflowStore\n dispatcher: WorkflowDispatcher\n hooks: HookRegistry\n log: (msg: string) => void\n}): { cronTimers: number; hookSubscribers: number } {\n const workflows = args.store.list()\n let cronTimers = 0\n let hookSubscribers = 0\n\n for (const wf of workflows) {\n // Lifecycle gate: don't register triggers for non-active workflows. A\n // disabled or quarantined workflow's cron must not fire and its\n // trigger.hook subscribers must not run. State changes that flip a\n // workflow back to `active` re-run this registrar.\n if (wf.state && wf.state !== \"active\") {\n args.log(`[workflows] ${wf.id} state=${wf.state} — skipping trigger registration`)\n continue\n }\n const trigger = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n if (!trigger) continue\n\n if (trigger.type === \"trigger.cron\") {\n const cfg = trigger.config as { spec?: string; timezone?: string }\n if (!cfg.spec) { args.log(`[workflows] ${wf.id} trigger.cron missing config.spec — skipping`); continue }\n scheduleCron({\n workflowId: wf.id,\n triggerNodeId: trigger.id,\n spec: cfg.spec,\n timezone: cfg.timezone ?? \"UTC\",\n }, args.dispatcher, args.log)\n cronTimers++\n } else if (trigger.type === \"trigger.hook\") {\n const cfg = trigger.config as {\n event?: string\n passthrough?: boolean\n filter?: {\n /** Restrict to specific actions (gitlab-issue / gitlab-mr).\n * Example: [\"open\", \"reopen\"] keeps the workflow off update/close events. */\n action?: string[]\n /** Restrict on:gitlab-note to comments that @-mention at least one of\n * these usernames. Without this, every note on the project fires the\n * workflow — including ones that don't address any agent. Match is\n * case-insensitive on the bare username (no leading @). */\n mentions?: string[]\n /** Restrict on:gitlab-note to a noteable type — \"merge_request\" or\n * \"issue\". Without this, an mr-fix-loop whose only gate is\n * `mentions` also claims issue comments that @-mention the coder,\n * suppresses the legacy @-mention reply path, then no-ops (the\n * prompt bails on issues) — so the comment gets swallowed with no\n * reply. Gating here lets issue notes fall through to the legacy\n * resolver. Match is case-insensitive on `ctx.noteableType`. */\n noteableType?: string[]\n /** Fire only when one of these usernames was newly added as an\n * assignee. The adapter computes the diff from\n * `changes.assignees.{previous,current}` and exposes it as\n * `ctx.assigneesAdded`. Lets a \"coder-pickup\" workflow target the\n * moment a coder is assigned without the workflow needing to\n * re-derive the diff in its prompt. Username comparison is\n * case-insensitive, leading @ stripped. */\n assigneesAdded?: string[]\n /** Same shape, for MR reviewers. `ctx.reviewersAdded`. */\n reviewersAdded?: string[]\n /** Fire only when one of these labels was newly added on an\n * update event. The adapter computes the diff from\n * `changes.labels.{previous,current}` and exposes it as\n * `ctx.labelsAdded`. Labels are matched case-insensitive. */\n labelsAdded?: string[]\n }\n }\n if (!cfg.event || !cfg.event.startsWith(\"on:\")) {\n args.log(`[workflows] ${wf.id} trigger.hook config.event must start with \"on:\" — skipping`)\n continue\n }\n args.hooks.registerHandler(cfg.event as HookEvent, `workflows:${wf.id}`, async (ctx) => {\n try {\n // Project-scope gate. The hook subscriber is registered per-workflow,\n // so we go direct (skipping matchByTrigger) — but the workflow's\n // top-level `project:` still has to be honored, otherwise an\n // on:gitlab-issue from project A reaches every workflow tagged\n // for project B. matchByTrigger has the same gate; this is the\n // companion check on the per-workflow hook path.\n const ctxProject = typeof ctx.project === \"string\" ? ctx.project : undefined\n if (wf.project && ctxProject && wf.project !== ctxProject) return {}\n // Trigger-level filters. These move the \"ONLY ACT when …; exit\n // cleanly otherwise\" prompt-level guard up to the dispatch gate,\n // which means the agent isn't woken up + billed for a turn just\n // to print \"exit cleanly\".\n if (cfg.filter) {\n // filter.action — gitlab-issue / gitlab-mr action gate.\n if (Array.isArray(cfg.filter.action) && cfg.filter.action.length > 0) {\n const action = typeof ctx.action === \"string\" ? ctx.action : undefined\n if (!action || !cfg.filter.action.includes(action)) return {}\n }\n // filter.mentions — gitlab-note mention gate. Without this, the\n // workflow's hard-coded `agentId` runs no matter who the comment\n // @-mentioned, so e.g. ksi-mr-fix-loop spawns ksi-v2 even on a\n // comment addressed @pm-ksi. The legacy @-mention resolver still\n // routes those to the right agent on the second pass when the\n // workflow doesn't claim the event.\n if (Array.isArray(cfg.filter.mentions) && cfg.filter.mentions.length > 0) {\n const ctxMentions = Array.isArray(ctx.mentions)\n ? (ctx.mentions as string[]).map((m) => m.toLowerCase().replace(/^@/, \"\"))\n : []\n const wanted = cfg.filter.mentions.map((m) => m.toLowerCase().replace(/^@/, \"\"))\n const hit = wanted.some((w) => ctxMentions.includes(w))\n if (!hit) return {}\n }\n // filter.noteableType — gitlab-note type gate. Keeps an\n // mr-fix-loop off issue comments so they fall through to the\n // legacy @-mention reply path instead of being claimed + no-op'd.\n if (Array.isArray(cfg.filter.noteableType) && cfg.filter.noteableType.length > 0) {\n const ctxType = typeof ctx.noteableType === \"string\" ? ctx.noteableType.toLowerCase() : undefined\n const wanted = cfg.filter.noteableType.map((t) => t.toLowerCase())\n if (!ctxType || !wanted.includes(ctxType)) return {}\n }\n // filter.assigneesAdded — coder-pickup-style trigger. Fires only\n // when one of the listed usernames appears in ctx.assigneesAdded\n // (computed by the gitlab adapter from changes.assignees diff).\n if (Array.isArray(cfg.filter.assigneesAdded) && cfg.filter.assigneesAdded.length > 0) {\n const ctxAdded = Array.isArray(ctx.assigneesAdded)\n ? (ctx.assigneesAdded as string[]).map((u) => u.toLowerCase().replace(/^@/, \"\"))\n : []\n const wanted = cfg.filter.assigneesAdded.map((u) => u.toLowerCase().replace(/^@/, \"\"))\n const hit = wanted.some((w) => ctxAdded.includes(w))\n if (!hit) return {}\n }\n // filter.reviewersAdded — MR review-on-assignment trigger.\n if (Array.isArray(cfg.filter.reviewersAdded) && cfg.filter.reviewersAdded.length > 0) {\n const ctxAdded = Array.isArray(ctx.reviewersAdded)\n ? (ctx.reviewersAdded as string[]).map((u) => u.toLowerCase().replace(/^@/, \"\"))\n : []\n const wanted = cfg.filter.reviewersAdded.map((u) => u.toLowerCase().replace(/^@/, \"\"))\n const hit = wanted.some((w) => ctxAdded.includes(w))\n if (!hit) return {}\n }\n // filter.labelsAdded — fires when a specific label was added on\n // an update event. Useful for \"Blocked\"-label workflows.\n if (Array.isArray(cfg.filter.labelsAdded) && cfg.filter.labelsAdded.length > 0) {\n const ctxAdded = Array.isArray(ctx.labelsAdded)\n ? (ctx.labelsAdded as string[]).map((l) => l.toLowerCase())\n : []\n const wanted = cfg.filter.labelsAdded.map((l) => l.toLowerCase())\n const hit = wanted.some((w) => ctxAdded.includes(w))\n if (!hit) return {}\n }\n }\n // Go direct: this hook subscriber is registered per-workflow,\n // so we already know which workflow to fire. Going through\n // dispatch() + matchByTrigger() would drop the event unless the\n // workflow's trigger.hook node had a matching `source` field —\n // which users don't set (the event IS the filter).\n const entityId = entityKeyFromHookContext(wf.id, cfg.event!, ctx)\n await args.dispatcher.dispatchWorkflow({\n workflowId: wf.id,\n entityRef: { backend: \"hook\", id: entityId },\n event: {\n id: `hook:${cfg.event}:${Date.now().toString(36)}`,\n payload: { hookEvent: cfg.event, ...ctx },\n },\n })\n // Signal claim. When a workflow claims a project-scoped event,\n // the GitLab adapter (and other future adapters) must suppress\n // their legacy \"@-mention dispatch\" / \"default-route dispatch\"\n // path — otherwise the same agent gets spawned twice for one\n // event (once via the workflow run, once via the legacy\n // resolver). Read from `combinedModified.__workflowClaimed`\n // already accumulated by earlier per-workflow handlers; append\n // this workflow's id.\n // Opt-out: `trigger.config.passthrough = true` — the workflow is\n // observability-only and the legacy reply should still fire.\n if (cfg.passthrough) return {}\n const prevClaimed = (ctx as { __workflowClaimed?: unknown }).__workflowClaimed\n const claimedList = Array.isArray(prevClaimed) ? (prevClaimed as string[]) : []\n return { modified: { __workflowClaimed: [...claimedList, wf.id] } }\n } catch (e: any) {\n args.log(`[workflows] ${wf.id} hook dispatch failed: ${e.message}`)\n return {}\n }\n }, 60)\n hookSubscribers++\n }\n }\n\n return { cronTimers, hookSubscribers }\n}\n\n/** Derive a per-event entity id for a hook fire. Two unrelated issues\n * (or MRs, or messages) MUST resolve to different entities — otherwise\n * the second one collides with the first's paused/running run and\n * either gets dropped or hijacks the paused state. Falls back to a\n * per-fire unique id so unknown hook shapes never silently collide. */\nfunction entityKeyFromHookContext(workflowId: string, event: string, ctx: Record<string, unknown>): string {\n const project = typeof ctx.project === \"string\" ? ctx.project : \"\"\n const iid = ctx.iid != null ? String(ctx.iid) : \"\"\n if (event === \"on:gitlab-issue\" && project && iid) return `${workflowId}:${event}:${project}#${iid}`\n if (event === \"on:gitlab-mr\" && project && iid) return `${workflowId}:${event}:${project}!${iid}`\n if (event === \"on:gitlab-pipeline\" && project) {\n const pid = ctx.pipelineId != null ? String(ctx.pipelineId) : \"\"\n if (pid) return `${workflowId}:${event}:${project}@pipeline:${pid}`\n }\n // Unknown / shapeless hook payloads: per-fire entity so each fire spawns\n // its own run rather than colliding on a single shared key. Authors who\n // want stickiness can move to a custom matcher later.\n return `${workflowId}:${event}:${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`\n}\n\nfunction scheduleCron(\n opts: CronTriggerOptions,\n dispatcher: WorkflowDispatcher,\n log: (msg: string) => void,\n): void {\n const tick = () => {\n let next: Date\n try { next = getNextCronDate(opts.spec, new Date(), opts.timezone) }\n catch (e: any) { log(`[workflows] ${opts.workflowId} cron spec invalid: ${e.message}`); return }\n\n const delay = Math.max(1000, next.getTime() - Date.now())\n setTimeout(() => {\n void (async () => {\n const eventId = `cron:${opts.workflowId}:${next.toISOString()}`\n try {\n await dispatcher.dispatchWorkflow({\n workflowId: opts.workflowId,\n trigger: { source: \"cron\" },\n // Each scheduled fire gets a distinct entity so multiple runs\n // can coexist. If authors want a single \"rolling\" entity keyed\n // by the workflow, they can override via a transform node.\n entityRef: { backend: \"cron\", id: `${opts.workflowId}@${next.toISOString()}` },\n event: {\n id: eventId,\n payload: { now: next.toISOString(), spec: opts.spec, workflowId: opts.workflowId },\n },\n })\n } catch (e: any) {\n log(`[workflows] ${opts.workflowId} cron dispatch failed: ${e.message}`)\n } finally {\n // Reschedule for the next fire window regardless of success.\n tick()\n }\n })()\n }, delay)\n }\n tick()\n}\n","import Database from \"better-sqlite3\"\nimport { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from \"fs\"\nimport { basename, resolve } from \"path\"\nimport { getTrace, listTraces, type TraceRecord, type TraceStepRecord } from \"@/storage/traces\"\nimport { lintWorkflow, workflowSchema, type Workflow } from \"./types\"\nimport { renderWorkflowYaml, parseYamlWorkflow } from \"./yaml\"\n\nexport interface WorkflowDraftCandidate {\n id: string\n workflow: Workflow\n sourceTaskIds: string[]\n confidence: number\n reason: string\n}\n\nexport interface WorkflowDraftRecord {\n id: string\n path: string\n workflow: Workflow\n}\n\nfunction slugify(input: string, fallback = \"workflow\"): string {\n const slug = input\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .slice(0, 52)\n return slug || fallback\n}\n\nfunction cap(input: string | null | undefined, max = 900): string {\n const text = String(input ?? \"\").trim()\n return text.length > max ? `${text.slice(0, max)}...` : text\n}\n\n/** Maps platform-specific noteable kinds to short, stable labels in workflow\n * ids (so a GitHub PR and a GitLab merge_request both end up as `mr`). */\nconst KIND_ALIASES: Record<string, string> = {\n merge_request: \"mr\",\n pull_request: \"mr\",\n pr: \"mr\",\n push: \"push\",\n issue: \"issue\",\n issues: \"issue\",\n}\n\nconst ENV_KEYWORDS: Array<[string, RegExp]> = [\n [\"prod\", /\\b(prod(?:uction)?|live)\\b/],\n [\"staging\", /\\b(stag(?:ing|e)?|qa|preprod|pre-prod)\\b/],\n [\"dev\", /\\b(dev|development|local)\\b/],\n]\n\nconst INTENT_KEYWORDS: Array<[string, RegExp]> = [\n [\"rollback\", /\\b(rollback|revert|undo)\\b/],\n [\"deploy\", /\\b(deploy|deployment|rollout|release|ship)\\b/],\n [\"fix\", /\\b(fix|repair|patch|resolve|hotfix)\\b/],\n [\"migrate\", /\\b(migration|migrate)\\b/],\n [\"review\", /\\b(review|inspect|audit|approve)\\b/],\n [\"triage\", /\\b(triage|classify|categorize|label)\\b/],\n [\"implement\", /\\b(implement|add|build|create|introduce)\\b/],\n [\"refactor\", /\\b(refactor|rename|restructure|cleanup)\\b/],\n [\"test\", /\\b(test|verify|validate|qa)\\b/],\n [\"update\", /\\b(update|upgrade|bump)\\b/],\n]\n\ninterface InferredName {\n project?: string\n kind?: string\n intent?: string\n environment?: string\n variation?: string\n}\n\n/** Pull a workflow name out of a trace. Names should describe the work, not\n * the worker — so we drop the agent id entirely and lean on the entity\n * (project + kind from the chatId composite) plus an intent verb mined from\n * the message. Stable across agents: a GitLab MR triaged by `coder-agent`\n * and the same MR deployed by `devops-agent` cluster on the same project. */\nexport function inferWorkflowName(trace: TraceRecord, _steps: TraceStepRecord[] = []): InferredName {\n const out: InferredName = {}\n\n // Extract project + kind from the chatId composite. Today's known shapes:\n // GitLab issue: \"<group>/<project>:issue:<iid>\"\n // GitLab merge_request:\"<group>/<project>:merge_request:<iid>\"\n // GitHub push: \"<owner>/<repo>:push:refs/heads/<branch>\"\n // GitHub PR: \"<owner>/<repo>:pull_request:<n>\"\n // Telegram numeric: \"<chat-id>\" (no colons → no project mining)\n const chatId = trace.chatId || \"\"\n const parts = chatId.split(\":\")\n if (parts.length >= 2 && parts[0].includes(\"/\")) {\n const repoSlug = parts[0]\n const repoName = repoSlug.split(\"/\").pop() || repoSlug\n out.project = slugify(repoName, \"project\")\n out.kind = slugify(KIND_ALIASES[parts[1]] || parts[1], \"task\")\n }\n\n // Mine the message for verbs / environment, but ONLY when project+kind didn't\n // already give us strong identity. For notification-style events (GitHub\n // push, GitLab MR) the work IS the kind — looking for verbs in commit\n // messages or MR descriptions splits a coherent workflow into accidental\n // sub-clusters (`agentx-push-implement` vs `agentx-push-draft`). When we\n // have a project+kind, only environment is still useful (one project may\n // have separate staging vs prod procedures).\n const msg = String(trace.messagePreview || \"\").toLowerCase()\n const hasStrongIdentity = Boolean(out.project && out.kind)\n if (!hasStrongIdentity) {\n for (const [verb, pattern] of INTENT_KEYWORDS) {\n if (pattern.test(msg)) { out.intent = verb; break }\n }\n }\n for (const [env, pattern] of ENV_KEYWORDS) {\n if (pattern.test(msg)) { out.environment = env; break }\n }\n\n return out\n}\n\n/** Build a workflow id from inferred parts. Falls back to a hash of the\n * message when nothing structured can be extracted (telegram one-offs). */\nfunction buildWorkflowId(name: InferredName, trace: TraceRecord): string {\n const parts = [name.project, name.kind, name.intent, name.environment, name.variation].filter(Boolean)\n if (parts.length >= 2) return parts.map((p) => slugify(p as string)).join(\"-\") + \"-draft\"\n // Fallback: short slug of the message + taskId tail to disambiguate.\n const msgSlug = slugify(trace.messagePreview || \"\", \"task\").slice(0, 32)\n const tail = trace.taskId.slice(-6).toLowerCase()\n return `${msgSlug || \"task\"}-${tail}-draft`\n}\n\nfunction traceTags(trace: TraceRecord, steps: TraceStepRecord[] = []): string[] {\n const tags = new Set<string>()\n if (trace.channel) tags.add(trace.channel)\n if (trace.agentId) tags.add(trace.agentId)\n for (const s of steps) {\n if (s.action) tags.add(slugify(s.action, \"action\"))\n }\n return Array.from(tags).slice(0, 12)\n}\n\nfunction summarizeSteps(steps: TraceStepRecord[]): string {\n const useful = steps\n .filter((s) => s.status !== \"error\")\n .filter((s) => s.name !== \"system\")\n .slice(0, 16)\n if (useful.length === 0) return \"No structured steps were captured; use the original message and final result as the procedure evidence.\"\n return useful.map((s, idx) => {\n const label = [s.name, s.action].filter(Boolean).join(\":\")\n const out = cap(s.outputSummary || s.inputSummary || s.error || \"\", 220)\n return `${idx + 1}. ${label}${out ? ` — ${out}` : \"\"}`\n }).join(\"\\n\")\n}\n\nexport function buildWorkflowDraftFromTrace(\n trace: TraceRecord,\n steps: TraceStepRecord[] = [],\n opts: {\n id?: string\n sourceTaskIds?: string[]\n confidence?: number\n generatedFrom?: string\n } = {},\n): Workflow {\n const inferred = inferWorkflowName(trace, steps)\n const id = opts.id || buildWorkflowId(inferred, trace)\n const sourceTaskIds = opts.sourceTaskIds?.length ? opts.sourceTaskIds : [trace.taskId]\n const titleSeed = cap(trace.messagePreview, 72) || `Task ${trace.taskId.slice(0, 8)}`\n const prompt = [\n \"Replay the cleaned successful procedure represented by this absorbed task trace.\",\n \"\",\n \"Original task:\",\n `{{trigger.input.message}}`,\n \"\",\n \"Procedure evidence, with failed turns and incidental retries removed where possible:\",\n summarizeSteps(steps),\n \"\",\n \"Use the current run input values where provided. Keep the final answer concise and operational.\",\n ].join(\"\\n\")\n\n return workflowSchema.parse({\n id,\n version: 2,\n title: `Draft: ${titleSeed}`,\n description: `Generated from task trace ${trace.taskId}. Review before promotion.`,\n status: \"draft\",\n state: \"disabled\",\n tags: traceTags(trace, steps),\n entity: trace.chatId || undefined,\n generatedFrom: opts.generatedFrom ?? \"task-trace\",\n sourceTaskIds,\n confidence: opts.confidence ?? 0.55,\n workflowVersion: \"draft-1\",\n ownerAgent: trace.agentId,\n nodes: [\n {\n id: \"trigger\",\n type: \"trigger.manual\",\n config: {\n inputSchema: {\n type: \"object\",\n properties: {\n message: { type: \"string\" },\n taskId: { type: \"string\" },\n },\n },\n },\n },\n {\n id: \"run_cleaned_task\",\n type: \"agent\",\n config: {\n agentId: trace.agentId,\n prompt,\n },\n },\n {\n id: \"done\",\n type: \"end\",\n config: {\n status: \"completed\",\n output: \"{{run_cleaned_task.reply}}\",\n },\n },\n ],\n edges: [\n { from: \"trigger\", to: \"run_cleaned_task\" },\n { from: \"run_cleaned_task\", to: \"done\" },\n ],\n envAllow: [],\n retention: { maxRuns: 500, maxDays: 90 },\n maxChildDepth: 5,\n })\n}\n\n/** Async wrapper around buildWorkflowDraftFromTrace. Two architect backends:\n * - `opts.model`: direct Anthropic API (needs ANTHROPIC_API_KEY)\n * - `opts.viaAgent`: route through the agent registry (uses the agent's own\n * Claude Code / Codex session — no raw API key needed, mirrors the wiki\n * cron pattern). Trade-off: slower (~5-10s) but operationally simpler.\n *\n * If both are set, `viaAgent` wins. Falls back to the deterministic\n * single-agent-node draft on any architect failure. The fallback is silent\n * in the return value but emitted to `opts.log`. */\nexport async function architectOrBuildDraft(\n trace: TraceRecord,\n steps: TraceStepRecord[] = [],\n opts: {\n id?: string\n sourceTaskIds?: string[]\n confidence?: number\n generatedFrom?: string\n model?: string\n viaAgent?: string\n daemonUrl?: string\n log?: (msg: string) => void\n } = {},\n): Promise<{ workflow: Workflow; usedLlm: boolean; via?: string; error?: string }> {\n const inferred = inferWorkflowName(trace, steps)\n const id = opts.id || buildWorkflowId(inferred, trace)\n const sourceTaskIds = opts.sourceTaskIds?.length ? opts.sourceTaskIds : [trace.taskId]\n const confidence = opts.confidence ?? 0.55\n if (opts.viaAgent) {\n try {\n const { architectWorkflowViaAgent } = await import(\"./architect\")\n const result = await architectWorkflowViaAgent(trace, steps, id, sourceTaskIds, confidence, {\n agentId: opts.viaAgent,\n daemonUrl: opts.daemonUrl,\n })\n return { workflow: result.workflow, usedLlm: true, via: result.via }\n } catch (e: any) {\n const msg = e?.message || String(e)\n opts.log?.(`[architect] agent path (${opts.viaAgent}) failed for ${trace.taskId.slice(0, 8)}, falling back to deterministic: ${msg}`)\n return {\n workflow: buildWorkflowDraftFromTrace(trace, steps, opts),\n usedLlm: false,\n error: msg,\n }\n }\n }\n if (opts.model) {\n try {\n const { architectWorkflowFromTrace } = await import(\"./architect\")\n const result = await architectWorkflowFromTrace(trace, steps, id, sourceTaskIds, confidence, { model: opts.model })\n return { workflow: result.workflow, usedLlm: true, via: `direct:${result.model}` }\n } catch (e: any) {\n const msg = e?.message || String(e)\n opts.log?.(`[architect] direct API failed for ${trace.taskId.slice(0, 8)}, falling back to deterministic: ${msg}`)\n return {\n workflow: buildWorkflowDraftFromTrace(trace, steps, opts),\n usedLlm: false,\n error: msg,\n }\n }\n }\n return { workflow: buildWorkflowDraftFromTrace(trace, steps, opts), usedLlm: false }\n}\n\nexport function validateWorkflowDraft(workflow: Workflow): string[] {\n const parsed = workflowSchema.safeParse(workflow)\n if (!parsed.success) {\n return parsed.error.issues.map((i) => `${i.path.join(\".\") || \"<root>\"}: ${i.message}`)\n }\n return lintWorkflow(parsed.data)\n}\n\n/** Node types that count as \"real work\" — i.e. not just routing or\n * signalling. A draft made entirely of trigger + end (or trigger +\n * checkpoint + end) is not actionable; it's noise from absorbing a\n * notification trace that had no procedure to capture. */\nconst MEANINGFUL_NODE_TYPES = new Set<string>([\n \"agent\", \"action.run\", \"action.send\", \"action.builtin\", \"action.callHTTP\",\n \"action.createIssue\", \"action.setLabel\", \"action.readLabel\", \"action.react\",\n \"action.editMessage\", \"action.logTime\",\n \"transform\", \"extract.structured\",\n \"branch\", \"rule\",\n \"subProcess\",\n])\n\n/** True if a draft captures an actual procedure worth keeping. Rejects\n * trigger→end and trigger→checkpoint→end shapes which usually come from\n * notification-only traces (e.g. GitHub push events with no follow-up\n * agent action). Operators can still hand-write workflows that are\n * legitimately minimal — this only gates the auto-absorb path. */\nexport function isMeaningfulDraft(workflow: Workflow): boolean {\n const work = workflow.nodes.filter((n) => MEANINGFUL_NODE_TYPES.has(n.type))\n return work.length > 0 && workflow.nodes.length >= 3\n}\n\nexport function draftsDir(baseDir = process.cwd()): string {\n return resolve(baseDir, \".agentx/workflows/_drafts\")\n}\n\nexport function draftsDirForWorkflowDir(workflowDir: string): string {\n return resolve(workflowDir, \"_drafts\")\n}\n\nexport function rejectedDraftsDir(baseDir = process.cwd()): string {\n return resolve(baseDir, \".agentx/workflows/_drafts/_rejected\")\n}\n\nexport function rejectedDraftsDirForWorkflowDir(workflowDir: string): string {\n return resolve(draftsDirForWorkflowDir(workflowDir), \"_rejected\")\n}\n\nexport function draftPath(id: string, format: \"yaml\" | \"json\" = \"yaml\", baseDir = process.cwd()): string {\n return resolve(draftsDir(baseDir), `${id}.${format}`)\n}\n\nexport function draftPathForWorkflowDir(id: string, workflowDir: string, format: \"yaml\" | \"json\" = \"yaml\"): string {\n return resolve(draftsDirForWorkflowDir(workflowDir), `${id}.${format}`)\n}\n\nexport function writeWorkflowDraft(\n workflow: Workflow,\n opts: { format?: \"yaml\" | \"json\"; baseDir?: string; workflowDir?: string; force?: boolean } = {},\n): string {\n const format = opts.format ?? \"yaml\"\n const dir = opts.workflowDir ? draftsDirForWorkflowDir(opts.workflowDir) : draftsDir(opts.baseDir)\n mkdirSync(dir, { recursive: true })\n const path = opts.workflowDir\n ? draftPathForWorkflowDir(workflow.id, opts.workflowDir, format)\n : draftPath(workflow.id, format, opts.baseDir)\n if (!opts.force && existsSync(path)) throw new Error(`draft already exists: ${path}`)\n const text = format === \"json\" ? JSON.stringify(workflow, null, 2) + \"\\n\" : renderWorkflowYaml(workflow)\n writeFileSync(path, text)\n return path\n}\n\nfunction readWorkflowFile(path: string): Workflow {\n const text = readFileSync(path, \"utf-8\")\n const raw = /\\.(ya?ml)$/i.test(path) ? parseYamlWorkflow(text, { filePath: basename(path) }) : JSON.parse(text)\n return workflowSchema.parse(raw)\n}\n\nexport function listWorkflowDrafts(baseDir = process.cwd(), opts: { workflowDir?: string } = {}): WorkflowDraftRecord[] {\n const dir = opts.workflowDir ? draftsDirForWorkflowDir(opts.workflowDir) : draftsDir(baseDir)\n if (!existsSync(dir)) return []\n return readdirSync(dir, { withFileTypes: true })\n .filter((e) => e.isFile() && /\\.(json|ya?ml)$/i.test(e.name))\n .map((e) => {\n const path = resolve(dir, e.name)\n return { id: e.name.replace(/\\.(json|ya?ml)$/i, \"\"), path, workflow: readWorkflowFile(path) }\n })\n .sort((a, b) => a.id.localeCompare(b.id))\n}\n\nexport function getWorkflowDraft(id: string, baseDir = process.cwd(), opts: { workflowDir?: string } = {}): WorkflowDraftRecord | null {\n const dir = opts.workflowDir ? draftsDirForWorkflowDir(opts.workflowDir) : draftsDir(baseDir)\n for (const ext of [\"yaml\", \"yml\", \"json\"]) {\n const path = resolve(dir, `${id}.${ext}`)\n if (existsSync(path)) return { id, path, workflow: readWorkflowFile(path) }\n }\n return null\n}\n\nexport function promoteWorkflowDraft(\n id: string,\n opts: { baseDir?: string; workflowDir?: string; replace?: boolean; format?: \"yaml\" | \"json\" } = {},\n): { workflow: Workflow; from: string; to: string } {\n const draft = getWorkflowDraft(id, opts.baseDir, { workflowDir: opts.workflowDir })\n if (!draft) throw new Error(`draft not found: ${id}`)\n const format = opts.format ?? (draft.path.endsWith(\".json\") ? \"json\" : \"yaml\")\n const workflow = workflowSchema.parse({\n ...draft.workflow,\n status: \"active\",\n state: \"active\",\n updated: new Date().toISOString().slice(0, 10),\n })\n const issues = validateWorkflowDraft(workflow)\n if (issues.length) throw new Error(`draft is invalid: ${issues.join(\"; \")}`)\n const workflowsDir = opts.workflowDir ?? resolve(opts.baseDir ?? process.cwd(), \".agentx/workflows\")\n const dest = resolve(workflowsDir, `${workflow.id}.${format}`)\n if (!opts.replace && existsSync(dest)) throw new Error(`workflow already exists: ${dest}`)\n mkdirSync(workflowsDir, { recursive: true })\n writeFileSync(dest, format === \"json\" ? JSON.stringify(workflow, null, 2) + \"\\n\" : renderWorkflowYaml(workflow))\n unlinkSync(draft.path)\n return { workflow, from: draft.path, to: dest }\n}\n\nexport function rejectWorkflowDraft(id: string, baseDir = process.cwd(), opts: { workflowDir?: string } = {}): string {\n const draft = getWorkflowDraft(id, baseDir, { workflowDir: opts.workflowDir })\n if (!draft) throw new Error(`draft not found: ${id}`)\n const dir = opts.workflowDir ? rejectedDraftsDirForWorkflowDir(opts.workflowDir) : rejectedDraftsDir(baseDir)\n mkdirSync(dir, { recursive: true })\n const dest = resolve(dir, `${Date.now().toString(36)}-${basename(draft.path)}`)\n renameSync(draft.path, dest)\n return dest\n}\n\n/** Default minimum chars on `messagePreview` for a trace to be eligible for\n * workflow absorb. Filters out trivial chatter (\"yes\", \"ok\", short Q&A)\n * that produces low-value drafts. Operators can override with --min-message-length. */\nexport const DEFAULT_MIN_MESSAGE_LENGTH = 30\n\nexport function loadSuccessfulTraces(\n db: Database.Database,\n opts: { since?: number; agentId?: string; limit?: number; minMessageLength?: number } = {},\n): TraceRecord[] {\n const min = opts.minMessageLength ?? DEFAULT_MIN_MESSAGE_LENGTH\n return listTraces(db, {\n agentId: opts.agentId,\n status: \"ok\",\n since: opts.since,\n limit: opts.limit ?? 1000,\n })\n .filter((t) => !t.workflowRunId)\n .filter((t) => (t.messagePreview || \"\").trim().length >= min)\n // Drop architect-self-recursion: the LLM architect dispatches via\n // /task with chatId=\"architect-<taskId>\"; those tasks are recorded\n // as their own traces and would re-cluster as \"workflow architect\"\n // drafts on the next absorb run. Skip anything in that namespace.\n .filter((t) => !(t.chatId || \"\").startsWith(\"architect-\"))\n}\n\n/** Cluster key — describes WHAT the work is, not WHO did it. We deliberately\n * drop the agentId so a procedure run by `coder-agent` and the same procedure\n * later run by `devops-agent` cluster together. Falls back to message words\n * when no project/kind/intent can be mined. */\nfunction clusterKey(trace: TraceRecord): string {\n const inferred = inferWorkflowName(trace)\n if (inferred.project && inferred.kind) {\n const segs = [inferred.project, inferred.kind, inferred.intent, inferred.environment].filter(Boolean)\n return segs.join(\":\")\n }\n const words = (trace.messagePreview || \"\")\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, \" \")\n .split(/\\s+/)\n .filter((w) => w.length > 3)\n .slice(0, 6)\n .join(\"-\")\n return [trace.channel || \"unknown\", words || \"task\"].join(\":\")\n}\n\nexport function clusterWorkflowCandidates(\n traces: TraceRecord[],\n opts: { minClusterSize?: number; max?: number } = {},\n): Array<{ key: string; traces: TraceRecord[]; confidence: number }> {\n const groups = new Map<string, TraceRecord[]>()\n for (const trace of traces) {\n const key = clusterKey(trace)\n const list = groups.get(key) ?? []\n list.push(trace)\n groups.set(key, list)\n }\n const min = Math.max(1, opts.minClusterSize ?? 3)\n return Array.from(groups.entries())\n .map(([key, grouped]) => {\n // Confidence boosts for clusters with a real project+kind+intent\n // (vs. message-word fallbacks). A \"mtgl-mr-deploy\" cluster of 3 traces\n // is more reusable than a 3-trace \"telegram-yes-task\" cluster.\n const inferred = inferWorkflowName(grouped[0])\n const namingBoost =\n (inferred.project ? 0.05 : 0) +\n (inferred.kind ? 0.05 : 0) +\n (inferred.intent ? 0.1 : 0) +\n (inferred.environment ? 0.05 : 0)\n const sizeScore = 0.45 + grouped.length * 0.1\n return {\n key,\n traces: grouped.sort((a, b) => b.startedAt - a.startedAt),\n confidence: Math.min(0.95, sizeScore + namingBoost),\n }\n })\n .filter((c) => c.traces.length >= min)\n .sort((a, b) => b.confidence - a.confidence || b.traces.length - a.traces.length)\n .slice(0, Math.max(1, opts.max ?? 10))\n}\n\nexport function buildDraftsFromClusters(\n db: Database.Database,\n clusters: Array<{ key: string; traces: TraceRecord[]; confidence: number }>,\n): WorkflowDraftCandidate[] {\n const out: WorkflowDraftCandidate[] = []\n for (const cluster of clusters) {\n const representative = cluster.traces[0]\n const full = getTrace(db, representative.taskId)\n const sourceTaskIds = cluster.traces.map((t) => t.taskId)\n // Don't pass an explicit id here — buildWorkflowDraftFromTrace runs\n // inferWorkflowName() against the representative trace and gets a clean\n // <project>-<kind>-<intent>[-<env>] name. Falling back to slugifying the\n // cluster key would just re-introduce the problem we're solving.\n const workflow = buildWorkflowDraftFromTrace(representative, full?.steps ?? [], {\n sourceTaskIds,\n confidence: cluster.confidence,\n generatedFrom: \"workflow-absorb\",\n })\n out.push({\n id: workflow.id,\n workflow,\n sourceTaskIds,\n confidence: cluster.confidence,\n reason: `${cluster.traces.length} similar successful task traces`,\n })\n }\n return out\n}\n\n/** Async clusters → drafts pipeline with optional LLM architect. Pass\n * `opts.viaAgent` to route through an agent (preferred — no raw API key)\n * or `opts.model` for the direct Anthropic API path. Either way the\n * deterministic fallback runs on any failure so the cron never hard-fails. */\nexport async function buildDraftsFromClustersAsync(\n db: Database.Database,\n clusters: Array<{ key: string; traces: TraceRecord[]; confidence: number }>,\n opts: { model?: string; viaAgent?: string; daemonUrl?: string; log?: (msg: string) => void } = {},\n): Promise<Array<WorkflowDraftCandidate & { usedLlm: boolean; via?: string }>> {\n const out: Array<WorkflowDraftCandidate & { usedLlm: boolean; via?: string }> = []\n for (const cluster of clusters) {\n const representative = cluster.traces[0]\n const full = getTrace(db, representative.taskId)\n const sourceTaskIds = cluster.traces.map((t) => t.taskId)\n const result = await architectOrBuildDraft(representative, full?.steps ?? [], {\n sourceTaskIds,\n confidence: cluster.confidence,\n generatedFrom: opts.viaAgent || opts.model ? \"llm-architect\" : \"workflow-absorb\",\n model: opts.model,\n viaAgent: opts.viaAgent,\n daemonUrl: opts.daemonUrl,\n log: opts.log,\n })\n out.push({\n id: result.workflow.id,\n workflow: result.workflow,\n sourceTaskIds,\n confidence: cluster.confidence,\n reason: `${cluster.traces.length} similar successful task traces${result.usedLlm ? ` (LLM-architected via ${result.via})` : \"\"}`,\n usedLlm: result.usedLlm,\n via: result.via,\n })\n }\n return out\n}\n","import type { Workflow } from \"./types\"\n\n// --- Workflow input resolution for auto-match dispatch ---\n//\n// When workflows.matching.mode === \"auto\" and the matcher picks a workflow,\n// the workflow's trigger.config.inputSchema declares what payload it needs\n// (e.g. project, environment, host, ref). The matcher hands us a free-form\n// message + chatId + agent context; we have to bridge the two.\n//\n// v1 strategy: deterministic only.\n// 1. passthrough — copy any context fields the schema declares (message,\n// agentId, channel, chatId, senderId, senderUsername).\n// 2. chatId parse — known channel composites yield typed fields:\n// \"<group>/<project>:issue:<id>\" → { project, id }\n// \"<group>/<project>:merge_request:<id>\" → { project, id }\n// \"<group>/<project>:pull_request:<id>\" → { project, id }\n// \"<group>/<project>:push:refs/heads/<ref>\" → { project, ref }\n// 3. schema defaults — any remaining gaps fall back to the inputSchema's\n// `default` fields.\n// 4. required check — if any `required` field is still empty, signal to\n// caller (auto-runner falls back to suggest mode + agent execution).\n//\n// LLM-based extraction (passing the message + context through an agent\n// running extract.structured) is the natural follow-on but lives in a\n// separate module so this hot path stays cheap and predictable.\n\nexport interface InputSchemaProperty {\n type?: string\n description?: string\n default?: unknown\n enum?: unknown[]\n}\n\nexport interface InputSchemaShape {\n type?: string\n properties?: Record<string, InputSchemaProperty>\n required?: string[]\n}\n\nexport interface ResolveContext {\n chatId?: string\n message?: string\n agentId?: string\n channel?: string\n senderId?: string\n senderUsername?: string\n}\n\nexport interface ResolveResult {\n inputs: Record<string, unknown>\n missing: string[]\n /** Provenance per-field so log lines can explain what filled what. */\n filledFrom: { passthrough: string[]; chatId: string[]; defaults: string[] }\n}\n\n/** Pull the inputSchema off the workflow's trigger node. Returns null when\n * the workflow is unschematized — caller should pass the matcher bundle\n * through unchanged. */\nexport function getInputSchema(wf: Workflow): InputSchemaShape | null {\n const trig = wf.nodes.find((n) => n.type.startsWith(\"trigger.\"))\n const cfg = trig?.config as { inputSchema?: InputSchemaShape } | undefined\n return cfg?.inputSchema ?? null\n}\n\n/** Best-effort extraction from chatId composites. Only fields that exist\n * in `schema.properties` end up in the result — we don't invent inputs\n * the workflow didn't declare. */\nexport function fillFromChatId(\n chatId: string | undefined,\n schema: InputSchemaShape | null,\n): Record<string, unknown> {\n if (!chatId || !schema?.properties) return {}\n const m = chatId.match(/^([^:]+\\/[^:]+):([^:]+):(.+)$/)\n if (!m) return {}\n const [, project, kind, rest] = m\n const out: Record<string, unknown> = {}\n if (schema.properties.project) out.project = project\n if (kind === \"push\") {\n const refMatch = rest.match(/^refs\\/heads\\/(.+)$/)\n if (refMatch && schema.properties.ref) out.ref = refMatch[1]\n } else {\n // issue / merge_request / pull_request → rest is the numeric id.\n // Architects emit the id under several names depending on platform —\n // populate whichever one(s) the workflow declared.\n const idNum = Number(rest)\n if (Number.isFinite(idNum)) {\n if (schema.properties.id) out.id = idNum\n if (schema.properties.iid) out.iid = idNum\n if (schema.properties.mrId) out.mrId = idNum\n if (schema.properties.issueId) out.issueId = idNum\n if (schema.properties.prId) out.prId = idNum\n }\n }\n return out\n}\n\n/** Fill any not-yet-set field with its inputSchema `default`. */\nexport function applyDefaults(\n schema: InputSchemaShape | null,\n current: Record<string, unknown>,\n): Record<string, unknown> {\n if (!schema?.properties) return current\n const out = { ...current }\n for (const [k, v] of Object.entries(schema.properties)) {\n if (out[k] === undefined && v && \"default\" in v) out[k] = v.default\n }\n return out\n}\n\n/** Names of required fields still missing or empty after merging. */\nexport function missingRequired(\n schema: InputSchemaShape | null,\n populated: Record<string, unknown>,\n): string[] {\n if (!schema?.required || !Array.isArray(schema.required)) return []\n return schema.required.filter((k) => {\n const v = populated[k]\n return v === undefined || v === \"\" || v === null\n })\n}\n\n/** Main entry point. Merges passthrough + chatId-parse + defaults and\n * returns the result plus any required fields still missing. */\nexport function resolveAutoRunInputs(wf: Workflow, ctx: ResolveContext): ResolveResult {\n const schema = getInputSchema(wf)\n if (!schema?.properties) {\n // Unschematized workflow — hand the matcher bundle through. No\n // required check applies.\n const all: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(ctx)) if (v !== undefined) all[k] = v\n return { inputs: all, missing: [], filledFrom: { passthrough: Object.keys(all), chatId: [], defaults: [] } }\n }\n\n const passthrough: Record<string, unknown> = {}\n for (const k of [\"message\", \"agentId\", \"channel\", \"chatId\", \"senderId\", \"senderUsername\"] as const) {\n if (schema.properties[k] && ctx[k] !== undefined) passthrough[k] = ctx[k]\n }\n\n const fromChat = fillFromChatId(ctx.chatId, schema)\n // chatId-derived fields are higher fidelity than passthrough message.\n let merged = { ...passthrough, ...fromChat }\n const beforeDefaults = { ...merged }\n merged = applyDefaults(schema, merged)\n const filledFromDefaults = Object.keys(merged).filter(\n (k) => beforeDefaults[k] === undefined && merged[k] !== undefined,\n )\n\n return {\n inputs: merged,\n missing: missingRequired(schema, merged),\n filledFrom: {\n passthrough: Object.keys(passthrough),\n chatId: Object.keys(fromChat),\n defaults: filledFromDefaults,\n },\n }\n}\n"],"mappings":"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","names":["existsSync","mkdirSync","readFileSync","writeFileSync","renameSync","resolve","dirname","HandoverStore","opts","base","channel","chatId","accountId","raw","parsed","file","tmp","o","rec","k","summary","now","agentId","all","existsSync","mkdirSync","readdirSync","readFileSync","unlinkSync","writeFileSync","resolve","relative","yaml","FLOW_FORBIDDEN_TYPES","WorkflowYamlError","message","mark","parseYamlWorkflow","text","opts","prefix","docs","e","raw","got","desugarFlow","flow","out","item","flowIds","nodes","idToType","n","id","type","synthesized","i","explicit","seen","unioned","k","edgeKey","from","fromPort","to","renderWorkflowYaml","workflow","Document","isMap","isSeq","isScalar","parseDocument","renderWorkflowYamlPreservingComments","originalText","workflow","doc","fresh","mergeMapInPlace","map","next","newKeys","newKeySet","oldPairs","pair","k","scalarKey","merged","key","value","existing","mergeValueInPlace","nextValue","oldVal","mergeSeqInPlace","seq","nextItems","it","replacement","oldById","item","idNode","id","WorkflowStore","opts","resolve","mkdirSync","id","name","full","text","readFileSync","parseYamlWorkflow","existsSync","byId","entry","readdirSync","list","out","files","raw","parsed","workflowSchema","a","b","results","sorted","relative","rel","e","reason","WorkflowYamlError","issues","i","lintIssues","lintWorkflow","jsonPath","yamlPath","ymlPath","haveJson","haveYaml","yPath","workflow","now","normalized","existingYaml","unlinkSync","writeFileSync","originalText","renderWorkflowYamlPreservingComments","err","removed","createHash","randomUUID","appendFileSync","existsSync","mkdirSync","readdirSync","readFileSync","statSync","unlinkSync","writeFileSync","resolve","sanitize","s","idempotencyKey","runId","nodeId","eventId","RunStore","opts","entity","args","now","id","run","p","raw","line","evt","current","h","nextStatus","status","updated","files","f","a","b","out","policy","cutoff","candidates","name","path","mtime","pruned","i","c","tooOld","tooMany","existsSync","mkdirSync","readFileSync","unlinkSync","writeFileSync","resolve","z","layoutSchema","LayoutStore","opts","workflowId","safe","p","parsed","layout","validIds","existing","keep","id","next","words","text","w","overlap","a","b","n","matchWorkflow","input","workflows","messageWords","active","wf","best","score","reasons","same","p","i","wordScore","confidence","nextNodes","input","workflow","fromNodeId","selectedPort","fromNode","n","outgoing","e","evaluateBranch","node","context","cases","defaultPort","c","conditionMatches","cond","p","path","value","getByPath","v","ctx","parts","cur","initialPendingFromTrigger","trigger","successors","findNode","id","lookup","path","ctx","envAllow","parts","name","v","cur","p","render","template","opts","_","renderParams","params","out","k","x","NodeConcurrencyGate","key","max","cur","resolve","queue","nodeConcurrencyGate","nodeKey","workflowId","nodeId","triggerHandler","ctx","agentHandler","cfg","agentId","promptTemplate","prompt","render","timeoutMinutes","maxConcurrent","gateKey","nodeKey","before","nodeConcurrencyGate","start","resp","durationMs","kind","parsed","extractJsonBlock","parseResultToken","e","branchHandler","port","evaluateBranch","sendHandler","rendered","renderParams","channel","chatId","text","rawChat","rawChan","hints","accountId","resolveAccountId","adapter","createIssueHandler","project","title","description","labels","assignees","findUpstreamAgentId","result","endHandler","checkpointHandler","name","waitFor","resumeMatch","setLabelHandler","iid","add","remove","readLabelHandler","reactHandler","messageId","emoji","editMessageHandler","parseMode","logTimeHandler","callHTTPHandler","url","method","headers","body","res","rawText","actionRunHandler","actionId","inputs","ActionStore","runAction","action","output","actionBuiltinHandler","input","runBuiltin","getBuiltin","msg","transformHandler","ctxObj","getByPath","computeDueAt","dueIn","m","match","days","hours","minutes","seconds","ms","subProcessHandler","childWorkflowId","parentDepth","cap","rawInputMap","inputBundle","k","v","triggerFormHandler","signalEmitHandler","scope","payload","signalWaitHandler","timerBoundaryHandler","due","gatewayParallelHandler","ruleHandler","raw","rules","rule","when","all","i","matchCell","def","w","numMatch","op","n","vNum","NODE_HANDLERS","resolveHandler","type","explicit","triggerNode","fromTrigger","entry","node","content","bracket","SignalBus","h","emission","matchesSignal","waiter","k","v","buildWorkflowEventInput","proj","rawJson","now","workflowResultToLegacyOutcome","result","buildWorkflowPolicyFromLegacy","legacy","recordWorkflowDispatch","ledger","eventInput","legacyOutcome","policy","ledgerDecision","decideAndCommit","reportDivergence","recordTraceStart","db","input","taskId","newEventId","recordTraceEnd","finishedAt","recordTraceStep","startedAt","seq","rowToTrace","row","rowToStep","getTrace","stepRows","cleanupOrphanedTraces","now","listTraces","filters","where","params","limit","sql","ACK_TYPING_INTERVAL_MS","MAX_PARALLEL_PER_RUN","WorkflowDispatcher","opts","TimerService","m","SignalBus","emission","args","runId","fn","next","all","run","waiter","matchesSignal","wf","w","fresh","node","findNode","successors","e","output","idempotencyKey","t","firedAt","cfg","n","effective","payload","channel","chatId","adapter","accountId","event","messageId","tick","timer","matches","toRun","claimed","runs","r","fromRemote","out","allowed","trigger","f","normalizeChat","have","l","a","b","workflow","entityRef","result","getLedgerMode","recordWorkflowDispatch","getDefaultLedger","activeRunId","homeNode","matchesResume","checkpointNode","resumeEntry","newContext","pausedCheckpointName","updated","init","initialPendingFromTrigger","triggeringEventId","batch","nodeId","key","prelude","x","handler","resolveHandler","h","inputKeys","workflowTraceTaskId","db","openDb","recordTraceStart","retry","attempt","wait","recordTraceEnd","now","remainingFromPending","statusUpdate","pausedAt","spawnedChild","nextPending","nextNodes","readyNext","joinCounters","succId","succ","arrived","expected","merged","terminal","childWf","childContext","child","childRun","preliminaryParent","parentWf","parent","childOutput","pickChildOutput","i","resumeMatch","match","k","want","eventIdLike","source","value","createWorkflowHookHandlers","dispatcher","ctx","msg","source","channelToTriggerSource","chatId","entityRef","claimed","buildChannelTriggerPayload","wf","n","w","project","iid","issueEvent","labels","l","assignees","mrEvent","reviewers","noteId","noteableType","noteableIid","text","author","mentions","pipelineId","status","ref","mrIid","channel","startWorkflowTriggers","args","workflows","cronTimers","hookSubscribers","wf","trigger","n","cfg","scheduleCron","ctx","ctxProject","action","ctxMentions","m","w","ctxType","wanted","t","ctxAdded","l","entityId","entityKeyFromHookContext","prevClaimed","e","workflowId","event","project","iid","pid","opts","dispatcher","log","tick","next","getNextCronDate","delay","eventId","existsSync","mkdirSync","readFileSync","readdirSync","renameSync","unlinkSync","writeFileSync","basename","resolve","slugify","input","fallback","cap","max","text","KIND_ALIASES","ENV_KEYWORDS","INTENT_KEYWORDS","inferWorkflowName","trace","_steps","out","parts","repoSlug","repoName","msg","verb","pattern","env","buildWorkflowId","name","p","msgSlug","tail","traceTags","steps","tags","s","summarizeSteps","useful","idx","label","buildWorkflowDraftFromTrace","opts","inferred","id","sourceTaskIds","titleSeed","prompt","workflowSchema","architectOrBuildDraft","confidence","architectWorkflowViaAgent","result","e","architectWorkflowFromTrace","validateWorkflowDraft","workflow","parsed","lintWorkflow","i","MEANINGFUL_NODE_TYPES","isMeaningfulDraft","n","draftsDir","baseDir","resolve","draftsDirForWorkflowDir","workflowDir","rejectedDraftsDir","rejectedDraftsDirForWorkflowDir","draftPath","format","draftPathForWorkflowDir","writeWorkflowDraft","dir","mkdirSync","path","existsSync","renderWorkflowYaml","writeFileSync","readWorkflowFile","readFileSync","raw","parseYamlWorkflow","basename","listWorkflowDrafts","readdirSync","a","b","getWorkflowDraft","ext","promoteWorkflowDraft","draft","issues","workflowsDir","dest","unlinkSync","rejectWorkflowDraft","renameSync","DEFAULT_MIN_MESSAGE_LENGTH","loadSuccessfulTraces","db","min","listTraces","t","clusterKey","words","w","clusterWorkflowCandidates","traces","groups","key","list","grouped","namingBoost","sizeScore","c","buildDraftsFromClustersAsync","db","clusters","opts","out","cluster","representative","full","getTrace","sourceTaskIds","t","result","architectOrBuildDraft","getInputSchema","wf","n","fillFromChatId","chatId","schema","m","project","kind","rest","out","refMatch","idNum","applyDefaults","current","k","v","missingRequired","populated","resolveAutoRunInputs","ctx","all","passthrough","fromChat","merged","beforeDefaults","filledFromDefaults"]}