create-harness-vibe-coding 0.8.19 → 0.9.3

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 (656) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README-CN.md +4 -2
  3. package/README.md +23 -4
  4. package/package.json +17 -2
  5. package/src/generator.js +19 -6
  6. package/src/index.js +531 -38
  7. package/src/prompts.js +4 -4
  8. package/src/ui/dist/assets/AgentsRoute-BGGs90wH.js +7 -0
  9. package/src/ui/dist/assets/Assistant-Bold-gm-uSS1B.woff2 +0 -0
  10. package/src/ui/dist/assets/Assistant-Medium-DrcxCXg3.woff2 +0 -0
  11. package/src/ui/dist/assets/Assistant-Regular-DVxZuzxb.woff2 +0 -0
  12. package/src/ui/dist/assets/Assistant-SemiBold-SCI4bEL9.woff2 +0 -0
  13. package/src/ui/dist/assets/Combination-DhBLEmof.js +42 -0
  14. package/src/ui/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  15. package/src/ui/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  16. package/src/ui/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  17. package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  18. package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  19. package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  20. package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  21. package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  22. package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  23. package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  24. package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  25. package/src/ui/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  26. package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  27. package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  28. package/src/ui/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  29. package/src/ui/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  30. package/src/ui/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  31. package/src/ui/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  32. package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  33. package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  34. package/src/ui/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  35. package/src/ui/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  36. package/src/ui/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  37. package/src/ui/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  38. package/src/ui/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  39. package/src/ui/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  40. package/src/ui/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  41. package/src/ui/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  42. package/src/ui/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  43. package/src/ui/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  44. package/src/ui/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  45. package/src/ui/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  46. package/src/ui/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  47. package/src/ui/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  48. package/src/ui/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  49. package/src/ui/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  50. package/src/ui/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  51. package/src/ui/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  52. package/src/ui/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  53. package/src/ui/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  54. package/src/ui/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  55. package/src/ui/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  56. package/src/ui/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  57. package/src/ui/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  58. package/src/ui/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  59. package/src/ui/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  60. package/src/ui/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  61. package/src/ui/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  62. package/src/ui/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  63. package/src/ui/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  64. package/src/ui/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  65. package/src/ui/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  66. package/src/ui/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  67. package/src/ui/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  68. package/src/ui/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  69. package/src/ui/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  70. package/src/ui/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  71. package/src/ui/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  72. package/src/ui/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  73. package/src/ui/dist/assets/RolesRoute-DnodCSz0.js +2 -0
  74. package/src/ui/dist/assets/RuntimePicker-CucD8TGn.js +7 -0
  75. package/src/ui/dist/assets/SettingsRoute-DquMWjac.js +2 -0
  76. package/src/ui/dist/assets/TaskList-Bxc0Uktx.js +7 -0
  77. package/src/ui/dist/assets/TerminalDrawer-CDusYltc.js +36 -0
  78. package/src/ui/dist/assets/WorkflowRoute-BN8yHmUs.css +1 -0
  79. package/src/ui/dist/assets/WorkflowRoute-D-0wVDpY.js +334 -0
  80. package/src/ui/dist/assets/abnfDiagram-VRR7QNED-zkRKkaxO.js +2 -0
  81. package/src/ui/dist/assets/apl-B4CMkyY2.js +2 -0
  82. package/src/ui/dist/assets/ar-SA-G6X2FPQ2-BiHaArx5.js +11 -0
  83. package/src/ui/dist/assets/arc-CBnLtOFO.js +2 -0
  84. package/src/ui/dist/assets/architectureDiagram-ZJ3FMSHR-DWXRXsh1.js +37 -0
  85. package/src/ui/dist/assets/asciiarmor-Df11BRmG.js +2 -0
  86. package/src/ui/dist/assets/asn1-EdZsLKOL.js +2 -0
  87. package/src/ui/dist/assets/asterisk-B-8jnY81.js +2 -0
  88. package/src/ui/dist/assets/az-AZ-76LH7QW2-CiT05voO.js +2 -0
  89. package/src/ui/dist/assets/bg-BG-XCXSNQG7-L-UM9tNf.js +6 -0
  90. package/src/ui/dist/assets/blockDiagram-677ZJIJ3-BIyh3Bry.js +133 -0
  91. package/src/ui/dist/assets/bn-BD-2XOGV67Q-NdgbA0bC.js +6 -0
  92. package/src/ui/dist/assets/brainfuck-C4LP7Hcl.js +2 -0
  93. package/src/ui/dist/assets/c4Diagram-LMCZKHZV-CA54vWWM.js +11 -0
  94. package/src/ui/dist/assets/ca-ES-6MX7JW3Y-BKZZsQ5J.js +9 -0
  95. package/src/ui/dist/assets/channel-Da_-zcAu.js +2 -0
  96. package/src/ui/dist/assets/chunk-2Q5K7J3B-DE4cxr1v.js +2 -0
  97. package/src/ui/dist/assets/chunk-32BRIVSS-i7FwbEU9.js +2 -0
  98. package/src/ui/dist/assets/chunk-5VM5RSS4-BhIcOYSu.js +16 -0
  99. package/src/ui/dist/assets/chunk-EX3LRPZG-Ci9zZRIs.js +232 -0
  100. package/src/ui/dist/assets/chunk-JWPE2WC7-BFdkcz-G.js +2 -0
  101. package/src/ui/dist/assets/chunk-MOJQB5TN-Dzmapl_S.js +89 -0
  102. package/src/ui/dist/assets/chunk-RYQCIY6F-RTmp7FOy.js +2 -0
  103. package/src/ui/dist/assets/chunk-V7JOEXUC-DInU3v3D.js +207 -0
  104. package/src/ui/dist/assets/chunk-VR4S4FIN-BHoyGBQO.js +2 -0
  105. package/src/ui/dist/assets/chunk-XXDRQBXY-DfeLZNZx.js +2 -0
  106. package/src/ui/dist/assets/classDiagram-OUVF2IWQ-FKbUS0bu.js +2 -0
  107. package/src/ui/dist/assets/classDiagram-v2-EOCWNBFH-FKbUS0bu.js +2 -0
  108. package/src/ui/dist/assets/clike-B9uivgTg.js +2 -0
  109. package/src/ui/dist/assets/clojure-BMjYHr_A.js +2 -0
  110. package/src/ui/dist/assets/cmake-BQqOBYOt.js +2 -0
  111. package/src/ui/dist/assets/cobol-CWcv1MsR.js +2 -0
  112. package/src/ui/dist/assets/coffeescript-S37ZYGWr.js +2 -0
  113. package/src/ui/dist/assets/commonlisp-DBKNyK5s.js +2 -0
  114. package/src/ui/dist/assets/cose-bilkent-JH36ORCC-_5PUJ615.js +2 -0
  115. package/src/ui/dist/assets/crystal-SjHAIU92.js +2 -0
  116. package/src/ui/dist/assets/cs-CZ-2BRQDIVT-DnkfnhDU.js +12 -0
  117. package/src/ui/dist/assets/css-BnMrqG3P.js +2 -0
  118. package/src/ui/dist/assets/cynefin-VYW2F7L2-CmkcFv-b.js +179 -0
  119. package/src/ui/dist/assets/cynefinDiagram-TSTJHNR4-D72VZn8H.js +63 -0
  120. package/src/ui/dist/assets/cypher-C_CwsFkJ.js +2 -0
  121. package/src/ui/dist/assets/cytoscape.esm-DTSO7Bv0.js +332 -0
  122. package/src/ui/dist/assets/d-pRatUO7H.js +2 -0
  123. package/src/ui/dist/assets/da-DK-5WZEPLOC-C54tQUgd.js +6 -0
  124. package/src/ui/dist/assets/dagre-VKFMJZFB-0BsAkrmo.js +5 -0
  125. package/src/ui/dist/assets/de-DE-XR44H4JA-DGfZlR10.js +9 -0
  126. package/src/ui/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
  127. package/src/ui/dist/assets/diagram-FQU43EPY-C1ZJDOca.js +4 -0
  128. package/src/ui/dist/assets/diagram-G47NLZAW-D3pr3FSP.js +25 -0
  129. package/src/ui/dist/assets/diagram-NH7WQ7WH-Bwry0NV-.js +25 -0
  130. package/src/ui/dist/assets/diagram-OA4YK3LP-L6g09cmV.js +31 -0
  131. package/src/ui/dist/assets/diagram-WEI45ONY-Bh1puYVX.js +42 -0
  132. package/src/ui/dist/assets/diff-DbItnlRl.js +2 -0
  133. package/src/ui/dist/assets/directory-open-01563666-DWU9wJ6I.js +2 -0
  134. package/src/ui/dist/assets/directory-open-4ed118d0-CunoC1EB.js +2 -0
  135. package/src/ui/dist/assets/dockerfile-BKs6k2Af.js +2 -0
  136. package/src/ui/dist/assets/dtd-DF_7sFjM.js +2 -0
  137. package/src/ui/dist/assets/dylan-DwRh75JA.js +2 -0
  138. package/src/ui/dist/assets/ebnf-CDyGwa7X.js +2 -0
  139. package/src/ui/dist/assets/ebnfDiagram-CCIWWBDH-BC_mBRR5.js +2 -0
  140. package/src/ui/dist/assets/ecl-Cabwm37j.js +2 -0
  141. package/src/ui/dist/assets/eiffel-CnydiIhH.js +2 -0
  142. package/src/ui/dist/assets/el-GR-BZB4AONW-DsnwWweL.js +11 -0
  143. package/src/ui/dist/assets/elm-vLlmbW-K.js +2 -0
  144. package/src/ui/dist/assets/erDiagram-Q63AITRT-Bkxam36l.js +86 -0
  145. package/src/ui/dist/assets/erlang-BNw1qcRV.js +2 -0
  146. package/src/ui/dist/assets/es-ES-U4NZUMDT-CRxBcQgC.js +10 -0
  147. package/src/ui/dist/assets/eu-ES-A7QVB2H4-CTd_E7YR.js +12 -0
  148. package/src/ui/dist/assets/excalidraw-default-bMWIgNCy.svg +1 -0
  149. package/src/ui/dist/assets/fa-IR-HGAKTJCU-DO56k7eG.js +9 -0
  150. package/src/ui/dist/assets/factor-kuTfRLto.js +2 -0
  151. package/src/ui/dist/assets/fcl-Kvtd6kyn.js +2 -0
  152. package/src/ui/dist/assets/fi-FI-Z5N7JZ37-DEDCF9WB.js +7 -0
  153. package/src/ui/dist/assets/file-open-002ab408-DIuFHtCF.js +2 -0
  154. package/src/ui/dist/assets/file-open-7c801643-684qeFg4.js +2 -0
  155. package/src/ui/dist/assets/file-save-3189631c-C1wFhQhH.js +2 -0
  156. package/src/ui/dist/assets/file-save-745eba88-Bb9F9Kg7.js +2 -0
  157. package/src/ui/dist/assets/flowDiagram-23GEKE2U-C3SAEao7.js +157 -0
  158. package/src/ui/dist/assets/forth-Ffai-XNe.js +2 -0
  159. package/src/ui/dist/assets/fortran-DYz_wnZ1.js +2 -0
  160. package/src/ui/dist/assets/fr-FR-RHASNOE6-DE2nPyDt.js +10 -0
  161. package/src/ui/dist/assets/ganttDiagram-NO4QXBWP-U-J_Xi-_.js +293 -0
  162. package/src/ui/dist/assets/gas-Bneqetm1.js +2 -0
  163. package/src/ui/dist/assets/gherkin-heZmZLOM.js +2 -0
  164. package/src/ui/dist/assets/gitGraphDiagram-IHSO6WYX-BADVbD-a.js +107 -0
  165. package/src/ui/dist/assets/gl-ES-HMX3MZ6V-mCqVgQHC.js +11 -0
  166. package/src/ui/dist/assets/graph-C9eacEi8.js +2 -0
  167. package/src/ui/dist/assets/groovy-D9Dt4D0W.js +2 -0
  168. package/src/ui/dist/assets/haskell-Cw1EW3IL.js +2 -0
  169. package/src/ui/dist/assets/haxe-H-WmDvRZ.js +2 -0
  170. package/src/ui/dist/assets/he-IL-6SHJWFNN-KnaLrCzw.js +11 -0
  171. package/src/ui/dist/assets/hi-IN-IWLTKZ5I-wuqiaaWt.js +5 -0
  172. package/src/ui/dist/assets/http-DBlCnlav.js +2 -0
  173. package/src/ui/dist/assets/hu-HU-A5ZG7DT2-DLg_zm22.js +8 -0
  174. package/src/ui/dist/assets/id-ID-SAP4L64H-MB2mXqcR.js +11 -0
  175. package/src/ui/dist/assets/idl-BEugSyMb.js +2 -0
  176. package/src/ui/dist/assets/image-blob-reduce.esm-HwCm7IZ0.js +8 -0
  177. package/src/ui/dist/assets/index-39SJtrt0.js +2 -0
  178. package/src/ui/dist/assets/index-BQ0LIrW-.js +2 -0
  179. package/src/ui/dist/assets/index-BcAUh2Nm.css +1 -0
  180. package/src/ui/dist/assets/index-BcThOK-i.js +331 -0
  181. package/src/ui/dist/assets/index-BnxV3k_S.js +3 -0
  182. package/src/ui/dist/assets/index-C7MZBHbT.js +117 -0
  183. package/src/ui/dist/assets/index-CCvKI2rM.js +2 -0
  184. package/src/ui/dist/assets/index-CZvmcCab.js +8 -0
  185. package/src/ui/dist/assets/index-Cp3hLKNl.js +2 -0
  186. package/src/ui/dist/assets/index-D7ayFxcx.js +2 -0
  187. package/src/ui/dist/assets/index-DAYxSQGU.js +2 -0
  188. package/src/ui/dist/assets/index-DD-HeMV0.css +1 -0
  189. package/src/ui/dist/assets/index-DPMxd3Qn.js +2 -0
  190. package/src/ui/dist/assets/index-DSe2jx3k.js +4 -0
  191. package/src/ui/dist/assets/index-DUirCkgW.js +2 -0
  192. package/src/ui/dist/assets/index-DaKPQ2hN.js +159 -0
  193. package/src/ui/dist/assets/index-DzpuR0Jg.js +2 -0
  194. package/src/ui/dist/assets/index-POndwsCI.js +2 -0
  195. package/src/ui/dist/assets/index-R8NM9I_9.js +2 -0
  196. package/src/ui/dist/assets/index-TCDuDhBj.js +2 -0
  197. package/src/ui/dist/assets/index-TckzASdM.js +2 -0
  198. package/src/ui/dist/assets/index-Yi5ytFFr.js +2 -0
  199. package/src/ui/dist/assets/infoDiagram-FWYZ7A6U-CiGBlFqH.js +3 -0
  200. package/src/ui/dist/assets/init-Gi6I4Gst.js +2 -0
  201. package/src/ui/dist/assets/ishikawaDiagram-FXEZZL3T-B1aheRbz.js +71 -0
  202. package/src/ui/dist/assets/it-IT-JPQ66NNP-DT3HSU7X.js +12 -0
  203. package/src/ui/dist/assets/ja-JP-DBVTYXUO-CsfJjjWt.js +9 -0
  204. package/src/ui/dist/assets/javascript-iXu5QeM3.js +2 -0
  205. package/src/ui/dist/assets/journeyDiagram-5HDEW3XC-DtnJPxub.js +140 -0
  206. package/src/ui/dist/assets/julia-DuME0IfC.js +2 -0
  207. package/src/ui/dist/assets/kaa-6HZHGXH3-DQz-1-Gn.js +2 -0
  208. package/src/ui/dist/assets/kab-KAB-ZGHBKWFO-CaH-LSoK.js +9 -0
  209. package/src/ui/dist/assets/kanban-definition-HUTT4EX6-CZatSm3e.js +90 -0
  210. package/src/ui/dist/assets/katex-C5jXJg4s.js +258 -0
  211. package/src/ui/dist/assets/kk-KZ-P5N5QNE5-BMXmcm55.js +2 -0
  212. package/src/ui/dist/assets/km-KH-HSX4SM5Z-ZeoJNBpN.js +12 -0
  213. package/src/ui/dist/assets/ko-KR-MTYHY66A-BeaLRKph.js +10 -0
  214. package/src/ui/dist/assets/ku-TR-6OUDTVRD-B_28KrkR.js +10 -0
  215. package/src/ui/dist/assets/layout-DEXfKzaS.js +2 -0
  216. package/src/ui/dist/assets/linear-CpEDnQRC.js +2 -0
  217. package/src/ui/dist/assets/livescript-BwQOo05w.js +2 -0
  218. package/src/ui/dist/assets/lt-LT-XHIRWOB4-BxDcnUhp.js +4 -0
  219. package/src/ui/dist/assets/lua-VAEuO923.js +2 -0
  220. package/src/ui/dist/assets/lv-LV-5QDEKY6T-DpKDl1_y.js +8 -0
  221. package/src/ui/dist/assets/map-Czzmt4hB.js +2 -0
  222. package/src/ui/dist/assets/mathematica-DTrFuWx2.js +2 -0
  223. package/src/ui/dist/assets/mbox-CNhZ1qSd.js +2 -0
  224. package/src/ui/dist/assets/mermaidLoader-CBO31t0J.js +305 -0
  225. package/src/ui/dist/assets/mermaidLoader-CEK31ho9.css +1 -0
  226. package/src/ui/dist/assets/mindmap-definition-LN4V7U3C-CQ0TccEr.js +97 -0
  227. package/src/ui/dist/assets/mirc-CjQqDB4T.js +2 -0
  228. package/src/ui/dist/assets/mllike-CXdrOF99.js +2 -0
  229. package/src/ui/dist/assets/modelica-Dc1JOy9r.js +2 -0
  230. package/src/ui/dist/assets/mr-IN-CRQNXWMA-BKCZT58p.js +14 -0
  231. package/src/ui/dist/assets/mscgen-BA5vi2Kp.js +2 -0
  232. package/src/ui/dist/assets/mumps-BT43cFF4.js +2 -0
  233. package/src/ui/dist/assets/my-MM-5M5IBNSE-D34k0G9T.js +2 -0
  234. package/src/ui/dist/assets/nb-NO-T6EIAALU-DXr_KW8J.js +11 -0
  235. package/src/ui/dist/assets/nginx-DdIZxoE0.js +2 -0
  236. package/src/ui/dist/assets/nl-NL-IS3SIHDZ-C1L0aVDR.js +9 -0
  237. package/src/ui/dist/assets/nn-NO-6E72VCQL-CLWVSxbZ.js +9 -0
  238. package/src/ui/dist/assets/nsis-LdVXkNf5.js +2 -0
  239. package/src/ui/dist/assets/ntriples-BfvgReVJ.js +2 -0
  240. package/src/ui/dist/assets/oc-FR-POXYY2M6-nf8_J3eR.js +9 -0
  241. package/src/ui/dist/assets/octave-Ck1zUtKM.js +2 -0
  242. package/src/ui/dist/assets/ordinal-Cboi1Yqb.js +2 -0
  243. package/src/ui/dist/assets/oz-BzwKVEFT.js +2 -0
  244. package/src/ui/dist/assets/pa-IN-N4M65BXN-CAMJ-xd6.js +5 -0
  245. package/src/ui/dist/assets/pascal--L3eBynH.js +2 -0
  246. package/src/ui/dist/assets/pegDiagram-2B236MQR-Bj2RcmRd.js +2 -0
  247. package/src/ui/dist/assets/percentages-BXMCSKIN-s9yWFdr6.js +160 -0
  248. package/src/ui/dist/assets/perl-CdXCOZ3F.js +2 -0
  249. package/src/ui/dist/assets/pica-BGv_AhAB.js +8 -0
  250. package/src/ui/dist/assets/pieDiagram-ENE6RG2P-obpzz8dC.js +40 -0
  251. package/src/ui/dist/assets/pig-CevX1Tat.js +2 -0
  252. package/src/ui/dist/assets/pl-PL-T2D74RX3-DKJ5H85v.js +10 -0
  253. package/src/ui/dist/assets/powershell-CFHJl5sT.js +2 -0
  254. package/src/ui/dist/assets/properties-C78fOPTZ.js +2 -0
  255. package/src/ui/dist/assets/protobuf-ChK-085T.js +2 -0
  256. package/src/ui/dist/assets/{proxy-D0vUbds5.js → proxy-BSmwef4H.js} +2 -2
  257. package/src/ui/dist/assets/pt-BR-5N22H2LF-B5kow-kN.js +10 -0
  258. package/src/ui/dist/assets/pt-PT-UZXXM6DQ-B2G34fso.js +10 -0
  259. package/src/ui/dist/assets/pug-DeIclll2.js +2 -0
  260. package/src/ui/dist/assets/puppet-DMA9R1ak.js +2 -0
  261. package/src/ui/dist/assets/python-BuPzkPfP.js +2 -0
  262. package/src/ui/dist/assets/q-pXgVlZs6.js +2 -0
  263. package/src/ui/dist/assets/quadrantDiagram-ABIIQ3AL-Cl-T7NkJ.js +8 -0
  264. package/src/ui/dist/assets/r-B6wPVr8A.js +2 -0
  265. package/src/ui/dist/assets/railroadDiagram-RFXS5EU6-DMq82RHg.js +2 -0
  266. package/src/ui/dist/assets/{refresh-cw-JNmExijd.js → refresh-cw-CbFd7guV.js} +2 -2
  267. package/src/ui/dist/assets/requirementDiagram-TGXJPOKE-tGInMDgU.js +85 -0
  268. package/src/ui/dist/assets/ro-RO-JPDTUUEW-BwLdZW91.js +12 -0
  269. package/src/ui/dist/assets/roundRect-0PYZxl1G.js +2 -0
  270. package/src/ui/dist/assets/rpm-CTu-6PCP.js +2 -0
  271. package/src/ui/dist/assets/ru-RU-B4JR7IUQ-DpdAgIJc.js +10 -0
  272. package/src/ui/dist/assets/ruby-B2Rjki9n.js +2 -0
  273. package/src/ui/dist/assets/runtimeBrand-BoKPimsz.js +2 -0
  274. package/src/ui/dist/assets/sankeyDiagram-HTMAVEWB-C7X-ET4m.js +41 -0
  275. package/src/ui/dist/assets/sas-B4kiWyti.js +2 -0
  276. package/src/ui/dist/assets/save-D1CXHwBw.js +12 -0
  277. package/src/ui/dist/assets/scheme-C41bIUwD.js +2 -0
  278. package/src/ui/dist/assets/sequenceDiagram-DBY2YBRQ-Bn24i_nY.js +163 -0
  279. package/src/ui/dist/assets/settings-2-DtSNAAcU.js +22 -0
  280. package/src/ui/dist/assets/shell-CjFT_Tl9.js +2 -0
  281. package/src/ui/dist/assets/shield-check-BVbJ8T_N.js +7 -0
  282. package/src/ui/dist/assets/si-LK-N5RQ5JYF-naJaUy5u.js +2 -0
  283. package/src/ui/dist/assets/sieve-C3Gn_uJK.js +2 -0
  284. package/src/ui/dist/assets/simple-mode-GW_nhZxv.js +2 -0
  285. package/src/ui/dist/assets/sizeCapture-X5ZJPWSS-DAQHfm82.js +2 -0
  286. package/src/ui/dist/assets/sk-SK-C5VTKIMK-1NPz1J_d.js +7 -0
  287. package/src/ui/dist/assets/sl-SI-NN7IZMDC-D8RfmNQw.js +7 -0
  288. package/src/ui/dist/assets/smalltalk-CnHTOXQT.js +2 -0
  289. package/src/ui/dist/assets/solr-DehyRSwq.js +2 -0
  290. package/src/ui/dist/assets/sparql-DkYu6x3z.js +2 -0
  291. package/src/ui/dist/assets/spreadsheet-BCZA_wO0.js +2 -0
  292. package/src/ui/dist/assets/sql-D0XecflT.js +2 -0
  293. package/src/ui/dist/assets/stateDiagram-2N3HPSRC-Ck48D1ck.js +2 -0
  294. package/src/ui/dist/assets/stateDiagram-v2-6OUMAXLB-CdfRCJAJ.js +2 -0
  295. package/src/ui/dist/assets/stex-C3f8Ysf7.js +2 -0
  296. package/src/ui/dist/assets/style-C3QI7VPf.css +1 -0
  297. package/src/ui/dist/assets/stylus-B533Al4x.js +2 -0
  298. package/src/ui/dist/assets/subset-shared.chunk-eAtLHmbi.js +85 -0
  299. package/src/ui/dist/assets/subset-worker.chunk-DeHCTnTk.js +2 -0
  300. package/src/ui/dist/assets/sv-SE-XGPEYMSR-BeOYCU-8.js +11 -0
  301. package/src/ui/dist/assets/swift-BzpIVaGY.js +2 -0
  302. package/src/ui/dist/assets/swimlanes-5IMT3BWC-DOy22UjP.js +3 -0
  303. package/src/ui/dist/assets/swimlanesDiagram-G3AALYLV-sicPbVOg.js +9 -0
  304. package/src/ui/dist/assets/ta-IN-2NMHFXQM-CtS68WDd.js +10 -0
  305. package/src/ui/dist/assets/tcl-DVfN8rqt.js +2 -0
  306. package/src/ui/dist/assets/{terminal-BBQvnOAK.js → terminal-DEby5Y_P.js} +2 -2
  307. package/src/ui/dist/assets/terminalControl-BkFeDVXo.js +134 -0
  308. package/src/ui/dist/assets/textile-CnDTJFAw.js +2 -0
  309. package/src/ui/dist/assets/th-TH-HPSO5L25-DCHY9-Co.js +3 -0
  310. package/src/ui/dist/assets/tiddlywiki-DO-Gjzrf.js +2 -0
  311. package/src/ui/dist/assets/tiki-DGYXhP31.js +2 -0
  312. package/src/ui/dist/assets/timeline-definition-FHXFAJF6-DVkG72qO.js +121 -0
  313. package/src/ui/dist/assets/toml-Bm5Em-hy.js +2 -0
  314. package/src/ui/dist/assets/tr-TR-DEFEU3FU-y5-vjTv8.js +8 -0
  315. package/src/ui/dist/assets/troff-wAsdV37c.js +2 -0
  316. package/src/ui/dist/assets/ttcn-CfJYG6tj.js +2 -0
  317. package/src/ui/dist/assets/ttcn-cfg-B9xdYoR4.js +2 -0
  318. package/src/ui/dist/assets/turtle-B1tBg_DP.js +2 -0
  319. package/src/ui/dist/assets/uk-UA-QMV73CPH-BRbR5It6.js +7 -0
  320. package/src/ui/dist/assets/useReducedMotion-D07XP52Q.js +12 -0
  321. package/src/ui/dist/assets/vb-CmGdzxic.js +2 -0
  322. package/src/ui/dist/assets/vbscript-BuJXcnF6.js +2 -0
  323. package/src/ui/dist/assets/velocity-D8B20fx6.js +2 -0
  324. package/src/ui/dist/assets/vennDiagram-L72KCM5P-DwMwBlf-.js +35 -0
  325. package/src/ui/dist/assets/verilog-C6RDOZhf.js +2 -0
  326. package/src/ui/dist/assets/vhdl-lSbBsy5d.js +2 -0
  327. package/src/ui/dist/assets/vi-VN-M7AON7JQ-s6wSBv2s.js +6 -0
  328. package/src/ui/dist/assets/wardleyDiagram-EHGQE667-CNuuvzEg.js +79 -0
  329. package/src/ui/dist/assets/webidl-ZXfAyPTL.js +2 -0
  330. package/src/ui/dist/assets/{x-DoIOGAJz.js → x-B84wq78k.js} +2 -2
  331. package/src/ui/dist/assets/xquery-DzFWVndE.js +2 -0
  332. package/src/ui/dist/assets/xychartDiagram-FW5EYKEG-dUpZxmn6.js +8 -0
  333. package/src/ui/dist/assets/yacas-BJ4BC0dw.js +2 -0
  334. package/src/ui/dist/assets/z80-Hz9HOZM7.js +2 -0
  335. package/src/ui/dist/assets/zh-CN-LNUGB5OW-Cu_-POQN.js +11 -0
  336. package/src/ui/dist/assets/zh-HK-E62DVLB3-BCx4W4qZ.js +2 -0
  337. package/src/ui/dist/assets/zh-TW-RAJ6MFWO-02wx_Mrj.js +10 -0
  338. package/src/ui/dist/icons/excalidraw.svg +1 -0
  339. package/src/ui/dist/icons/markdown.svg +1 -0
  340. package/src/ui/dist/index.html +2 -2
  341. package/src/ui/package.json +10 -0
  342. package/src/ui/playwright.config.ts +36 -0
  343. package/src/ui/pnpm-workspace.yaml +2 -0
  344. package/src/ui/public/icons/excalidraw.svg +1 -0
  345. package/src/ui/public/icons/markdown.svg +1 -0
  346. package/src/ui/tsconfig.json +2 -1
  347. package/src/ui/vite.config.ts +1 -0
  348. package/src/wf-ui-server/__tests__/a2a-store.test.mjs +432 -15
  349. package/src/wf-ui-server/__tests__/action-registry-loader.test.mjs +156 -0
  350. package/src/wf-ui-server/__tests__/claude-stream-json.test.mjs +397 -0
  351. package/src/wf-ui-server/__tests__/codex-appserver.test.mjs +392 -0
  352. package/src/wf-ui-server/__tests__/component-node-api.test.mjs +881 -0
  353. package/src/wf-ui-server/__tests__/component-node-store.test.mjs +401 -0
  354. package/src/wf-ui-server/__tests__/control-plane-acceptance.test.mjs +2027 -0
  355. package/src/wf-ui-server/__tests__/control-plane-cli-smoke.test.mjs +1014 -0
  356. package/src/wf-ui-server/__tests__/dispatch-lifecycle.test.mjs +330 -0
  357. package/src/wf-ui-server/__tests__/effort-compatibility-red-092.test.mjs +151 -0
  358. package/src/wf-ui-server/__tests__/graph-layout-agent-tree.test.mjs +254 -0
  359. package/src/wf-ui-server/__tests__/graph-layout-tidy.test.mjs +260 -0
  360. package/src/wf-ui-server/__tests__/graph-layout.test.mjs +84 -0
  361. package/src/wf-ui-server/__tests__/node-config-api.test.mjs +384 -0
  362. package/src/wf-ui-server/__tests__/opencode-server.test.mjs +526 -0
  363. package/src/wf-ui-server/__tests__/peer-capsule.test.mjs +2 -2
  364. package/src/wf-ui-server/__tests__/pty-adapter.test.mjs +71 -1
  365. package/src/wf-ui-server/__tests__/runtime-config.test.mjs +3 -3
  366. package/src/wf-ui-server/__tests__/runtime-detector.test.mjs +21 -0
  367. package/src/wf-ui-server/__tests__/runtime-review.test.mjs +828 -0
  368. package/src/wf-ui-server/__tests__/server.integration.test.mjs +1463 -11
  369. package/src/wf-ui-server/__tests__/session-cleanup.test.mjs +159 -0
  370. package/src/wf-ui-server/__tests__/session-registry.test.mjs +52 -3
  371. package/src/wf-ui-server/__tests__/settings.test.mjs +12 -2
  372. package/src/wf-ui-server/__tests__/spawn-gate.test.mjs +78 -0
  373. package/src/wf-ui-server/__tests__/task-group-parser.test.mjs +120 -0
  374. package/src/wf-ui-server/__tests__/task-group-tag.test.mjs +264 -0
  375. package/src/wf-ui-server/__tests__/task-parser.test.mjs +49 -3
  376. package/src/wf-ui-server/__tests__/terminal-context-hardening.test.mjs +122 -0
  377. package/src/wf-ui-server/__tests__/terminal-context.test.mjs +164 -0
  378. package/src/wf-ui-server/__tests__/terminal-store.test.mjs +4 -4
  379. package/src/wf-ui-server/__tests__/wf-browser-api.test.mjs +599 -0
  380. package/src/wf-ui-server/__tests__/wf-browser-store.test.mjs +184 -0
  381. package/src/wf-ui-server/__tests__/workflow-action-registry.test.mjs +282 -0
  382. package/src/wf-ui-server/__tests__/workflow-agent-communication.test.mjs +411 -0
  383. package/src/wf-ui-server/__tests__/workflow-agent-context.test.mjs +323 -0
  384. package/src/wf-ui-server/__tests__/workflow-agent-cooperation-permissions.test.mjs +318 -0
  385. package/src/wf-ui-server/__tests__/workflow-agent-messages-structured.test.mjs +458 -0
  386. package/src/wf-ui-server/__tests__/workflow-agent-routing.test.mjs +434 -0
  387. package/src/wf-ui-server/__tests__/workflow-agent-runtime.test.mjs +324 -0
  388. package/src/wf-ui-server/__tests__/workflow-api-integration.test.mjs +263 -0
  389. package/src/wf-ui-server/__tests__/workflow-api-phase3.test.mjs +1213 -0
  390. package/src/wf-ui-server/__tests__/workflow-claude-materialize.test.mjs +209 -0
  391. package/src/wf-ui-server/__tests__/workflow-cli-registry-manuals.test.mjs +173 -0
  392. package/src/wf-ui-server/__tests__/workflow-cli-surface.test.mjs +176 -0
  393. package/src/wf-ui-server/__tests__/workflow-codex-resume-id.test.mjs +556 -0
  394. package/src/wf-ui-server/__tests__/workflow-cooperation-enforcement.test.mjs +553 -0
  395. package/src/wf-ui-server/__tests__/workflow-depth-resume-dock.test.mjs +846 -0
  396. package/src/wf-ui-server/__tests__/workflow-display-node.test.mjs +150 -0
  397. package/src/wf-ui-server/__tests__/workflow-edge-update.test.mjs +296 -0
  398. package/src/wf-ui-server/__tests__/workflow-file-cache-lock-watch.test.mjs +380 -0
  399. package/src/wf-ui-server/__tests__/workflow-file-formats.test.mjs +994 -0
  400. package/src/wf-ui-server/__tests__/workflow-file-node-actions.test.mjs +294 -0
  401. package/src/wf-ui-server/__tests__/workflow-goal-actions.test.mjs +357 -0
  402. package/src/wf-ui-server/__tests__/workflow-goal-task-autoclose.test.mjs +256 -0
  403. package/src/wf-ui-server/__tests__/workflow-graceful-stop.test.mjs +108 -0
  404. package/src/wf-ui-server/__tests__/workflow-graph-undo.test.mjs +524 -0
  405. package/src/wf-ui-server/__tests__/workflow-layout.test.mjs +732 -0
  406. package/src/wf-ui-server/__tests__/workflow-magnetic-topology.test.mjs +222 -0
  407. package/src/wf-ui-server/__tests__/workflow-markdown-lock.test.mjs +359 -0
  408. package/src/wf-ui-server/__tests__/workflow-mcp-hub.test.mjs +174 -0
  409. package/src/wf-ui-server/__tests__/workflow-node-manual-schema.test.mjs +241 -0
  410. package/src/wf-ui-server/__tests__/workflow-node-runtime.test.mjs +1991 -0
  411. package/src/wf-ui-server/__tests__/workflow-node-store-utils.test.mjs +125 -0
  412. package/src/wf-ui-server/__tests__/workflow-registry-drift.test.mjs +296 -0
  413. package/src/wf-ui-server/__tests__/workflow-resource-handle-four-side.test.mjs +217 -0
  414. package/src/wf-ui-server/__tests__/workflow-role-profile.test.mjs +234 -0
  415. package/src/wf-ui-server/__tests__/workflow-set-model.test.mjs +261 -0
  416. package/src/wf-ui-server/__tests__/workflow-skill-group-context-enabled.test.mjs +131 -0
  417. package/src/wf-ui-server/__tests__/workflow-skill-group-enable-disable.test.mjs +186 -0
  418. package/src/wf-ui-server/__tests__/workflow-skills-hub.test.mjs +125 -0
  419. package/src/wf-ui-server/__tests__/workflow-state-reconcile.test.mjs +139 -0
  420. package/src/wf-ui-server/__tests__/workflow-subagent-strategy-matrix.test.mjs +529 -0
  421. package/src/wf-ui-server/__tests__/workflow-team-flow.test.mjs +278 -0
  422. package/src/wf-ui-server/__tests__/workflow-terminal-control-live.test.mjs +294 -0
  423. package/src/wf-ui-server/__tests__/workflow-terminal-control.test.mjs +631 -0
  424. package/src/wf-ui-server/__tests__/workflow-timer-wakeup.test.mjs +318 -0
  425. package/src/wf-ui-server/__tests__/workflow-unified-resume.test.mjs +537 -0
  426. package/src/wf-ui-server/__tests__/workspace-api-hardening.test.mjs +187 -0
  427. package/src/wf-ui-server/__tests__/workspace-api.test.mjs +382 -0
  428. package/src/wf-ui-server/__tests__/workspace-reveal.test.mjs +138 -0
  429. package/src/wf-ui-server/__tests__/workspace-store-hardening.test.mjs +255 -0
  430. package/src/wf-ui-server/__tests__/workspace-store.test.mjs +208 -0
  431. package/src/wf-ui-server/__tests__/ws-events.integration.test.mjs +52 -25
  432. package/src/wf-ui-server/__tests__/ws-terminal.integration.test.mjs +12 -6
  433. package/src/wf-ui-server/__tests__/ws-wf-browser.integration.test.mjs +488 -0
  434. package/src/wf-ui-server/a2a-store.mjs +3007 -89
  435. package/src/wf-ui-server/action-registry.mjs +277 -0
  436. package/src/wf-ui-server/bridge-store.mjs +276 -0
  437. package/src/wf-ui-server/chat-driver.mjs +220 -0
  438. package/src/wf-ui-server/chat-store.mjs +109 -0
  439. package/src/wf-ui-server/claude-transcript-materializer.mjs +235 -0
  440. package/src/wf-ui-server/codex-update-prompt.mjs +53 -0
  441. package/src/wf-ui-server/component-node-store.mjs +689 -0
  442. package/src/wf-ui-server/composition-api.mjs +436 -0
  443. package/src/wf-ui-server/control-plane-token.mjs +17 -0
  444. package/src/wf-ui-server/dispatch-contract.mjs +150 -0
  445. package/src/wf-ui-server/drivers/claude-stream-json.mjs +371 -0
  446. package/src/wf-ui-server/drivers/codex-appserver.mjs +513 -0
  447. package/src/wf-ui-server/drivers/opencode-server.mjs +486 -0
  448. package/src/wf-ui-server/excalidraw-svg.mjs +141 -0
  449. package/src/wf-ui-server/file-format-adapters.mjs +511 -0
  450. package/src/wf-ui-server/file-watcher.mjs +215 -0
  451. package/src/wf-ui-server/graph-layout.mjs +778 -0
  452. package/src/wf-ui-server/harness-env.mjs +46 -0
  453. package/src/wf-ui-server/json-store-utils.mjs +48 -0
  454. package/src/wf-ui-server/model-capability.mjs +773 -0
  455. package/src/wf-ui-server/node-config-store.mjs +233 -0
  456. package/src/wf-ui-server/node-store-utils.mjs +88 -0
  457. package/src/wf-ui-server/peer-capsule.mjs +56 -1
  458. package/src/wf-ui-server/pty-adapter.mjs +513 -11
  459. package/src/wf-ui-server/reveal-foreground.mjs +69 -0
  460. package/src/wf-ui-server/runtime-detector.mjs +52 -6
  461. package/src/wf-ui-server/server.mjs +5613 -403
  462. package/src/wf-ui-server/session-cleanup.mjs +363 -0
  463. package/src/wf-ui-server/session-registry.mjs +179 -3
  464. package/src/wf-ui-server/settings.mjs +25 -0
  465. package/src/wf-ui-server/spawn-gate.mjs +46 -0
  466. package/src/wf-ui-server/task-parser.mjs +88 -3
  467. package/src/wf-ui-server/terminal-store.mjs +225 -11
  468. package/src/wf-ui-server/timer-wakeup-scheduler.mjs +226 -0
  469. package/src/wf-ui-server/wf-browser-store.mjs +733 -0
  470. package/src/wf-ui-server/workflow-agent-context.mjs +1731 -0
  471. package/src/wf-ui-server/workflow-capability-node-store.mjs +676 -0
  472. package/src/wf-ui-server/workflow-event-node-store.mjs +623 -0
  473. package/src/wf-ui-server/workflow-goal-node-store.mjs +328 -0
  474. package/src/wf-ui-server/workflow-graph-store.mjs +623 -0
  475. package/src/wf-ui-server/workflow-mcp-hub.mjs +286 -0
  476. package/src/wf-ui-server/workflow-node-runtime.mjs +1767 -0
  477. package/src/wf-ui-server/workflow-node-settings-store.mjs +339 -0
  478. package/src/wf-ui-server/workflow-node-types/agent-node.mjs +920 -0
  479. package/src/wf-ui-server/workflow-node-types/display-node.mjs +151 -0
  480. package/src/wf-ui-server/workflow-node-types/excalidraw-node.mjs +145 -0
  481. package/src/wf-ui-server/workflow-node-types/file-node.mjs +484 -0
  482. package/src/wf-ui-server/workflow-node-types/github-trigger-node.mjs +207 -0
  483. package/src/wf-ui-server/workflow-node-types/goal-node.mjs +491 -0
  484. package/src/wf-ui-server/workflow-node-types/markdown-node.mjs +225 -0
  485. package/src/wf-ui-server/workflow-node-types/mcp-connector-node.mjs +71 -0
  486. package/src/wf-ui-server/workflow-node-types/role-profile-store.mjs +191 -0
  487. package/src/wf-ui-server/workflow-node-types/skill-group-node.mjs +98 -0
  488. package/src/wf-ui-server/workflow-node-types/timer-node.mjs +607 -0
  489. package/src/wf-ui-server/workflow-ontology.mjs +681 -0
  490. package/src/wf-ui-server/workflow-operation-store.mjs +115 -0
  491. package/src/wf-ui-server/workflow-skills-hub.mjs +366 -0
  492. package/src/wf-ui-server/workflow-skills-market.mjs +723 -0
  493. package/src/wf-ui-server/workspace-store.mjs +951 -0
  494. package/src/wf-ui-server/ws-chat.mjs +299 -0
  495. package/src/wf-ui-server/ws-events.mjs +45 -20
  496. package/src/wf-ui-server/ws-terminal.mjs +165 -22
  497. package/src/wf-ui-server/ws-wf-browser.mjs +419 -0
  498. package/templates/common/.claude/agents/review-manager.md +33 -29
  499. package/templates/common/.claude/commands/wf-browser.md +1 -1
  500. package/templates/common/.claude/commands/wf-help.md +5 -3
  501. package/templates/common/.claude/commands/wf-init.md +44 -0
  502. package/templates/common/.claude/commands/wf-learn.md +34 -8
  503. package/templates/common/.claude/commands/wf-max.md +19 -9
  504. package/templates/common/.claude/commands/wf-review.md +3 -3
  505. package/templates/common/.claude/commands/wf-search.md +55 -0
  506. package/templates/common/.claude/commands/wf-task-archive.md +5 -4
  507. package/templates/common/.claude/commands/wf-task-list.md +6 -3
  508. package/templates/common/.claude/commands/wf-task-record.md +10 -2
  509. package/templates/common/.claude/commands/wf-ui.md +12 -5
  510. package/templates/common/.claude/commands/wf-update.md +4 -0
  511. package/templates/common/.claude/commands/wf.md +12 -1
  512. package/templates/common/.claude/rules/ecc/common.md +10 -1
  513. package/templates/common/.claude/skills/wf/SKILL.md +36 -0
  514. package/templates/common/.claude/skills/wf-browser/SKILL.md +427 -122
  515. package/templates/common/.claude/skills/wf-browser/references/BROWSER_CONTROL_PATTERNS.md +43 -0
  516. package/templates/common/.claude/skills/wf-init/SKILL.md +33 -0
  517. package/templates/common/.claude/skills/wf-learn/SKILL.md +106 -26
  518. package/templates/common/.claude/skills/wf-max/SKILL.md +45 -11
  519. package/templates/common/.claude/skills/wf-review/SKILL.md +26 -54
  520. package/templates/common/.claude/skills/wf-search/SKILL.md +122 -0
  521. package/templates/common/.claude/skills/wf-search/references/evidence-contract.md +77 -0
  522. package/templates/common/.claude/skills/wf-search/references/policies/compare.md +38 -0
  523. package/templates/common/.claude/skills/wf-search/references/policies/fact.md +39 -0
  524. package/templates/common/.claude/skills/wf-search/references/policies/troubleshoot.md +39 -0
  525. package/templates/common/.claude/skills/wf-search/references/policies/verify.md +44 -0
  526. package/templates/common/.claude/skills/wf-search/references/source-routing.md +40 -0
  527. package/templates/common/.claude/skills/wf-search/references/tool-adapters.md +38 -0
  528. package/templates/common/.claude/skills/wf-ui/SKILL.md +146 -12
  529. package/templates/common/.claude/skills/wf-update/SKILL.md +5 -1
  530. package/templates/common/.harness-version +188 -85
  531. package/templates/common/.opencode/agents/review-manager.md +31 -27
  532. package/templates/common/.opencode/commands/wf-browser.md +1 -1
  533. package/templates/common/.opencode/commands/wf-help.md +5 -3
  534. package/templates/common/.opencode/commands/wf-init.md +44 -0
  535. package/templates/common/.opencode/commands/wf-learn.md +34 -8
  536. package/templates/common/.opencode/commands/wf-max.md +19 -9
  537. package/templates/common/.opencode/commands/wf-review.md +3 -3
  538. package/templates/common/.opencode/commands/wf-search.md +55 -0
  539. package/templates/common/.opencode/commands/wf-task-archive.md +5 -7
  540. package/templates/common/.opencode/commands/wf-task-list.md +6 -6
  541. package/templates/common/.opencode/commands/wf-task-record.md +10 -5
  542. package/templates/common/.opencode/commands/wf-ui.md +12 -5
  543. package/templates/common/.opencode/commands/wf-update.md +4 -0
  544. package/templates/common/.opencode/commands/wf.md +12 -1
  545. package/templates/common/CLAUDE.md +61 -3
  546. package/templates/common/Harness/MEMORY.md +8 -5
  547. package/templates/common/Harness/README.md +14 -10
  548. package/templates/common/Harness/a2a/action-registry.json +4397 -0
  549. package/templates/common/Harness/a2a/role-graph.json +16 -3
  550. package/templates/common/Harness/a2a/skills/terminal-control.json +45 -1
  551. package/templates/common/Harness/a2a/skills/wf-ui-map.json +50 -0
  552. package/templates/common/Harness/a2a/skills/workflow-agent-node.json +143 -0
  553. package/templates/common/Harness/a2a/skills/workflow-context.json +95 -0
  554. package/templates/common/Harness/a2a/skills/workflow-diagram-node.json +60 -0
  555. package/templates/common/Harness/a2a/skills/workflow-display-node.json +69 -0
  556. package/templates/common/Harness/a2a/skills/workflow-file-node.json +92 -0
  557. package/templates/common/Harness/a2a/skills/workflow-goal-node.json +76 -0
  558. package/templates/common/Harness/a2a/skills/workflow-markdown-node.json +79 -0
  559. package/templates/common/Harness/a2a/skills/workflow-mcp-connector-node.json +59 -0
  560. package/templates/common/Harness/a2a/skills/workflow-node-actions.json +105 -0
  561. package/templates/common/Harness/a2a/skills/workflow-node-map.json +71 -0
  562. package/templates/common/Harness/a2a/skills/workflow-ontology.json +54 -0
  563. package/templates/common/Harness/a2a/skills/workflow-resource-node.json +68 -0
  564. package/templates/common/Harness/a2a/skills/workflow-skill-group-node.json +56 -0
  565. package/templates/common/Harness/a2a/skills/workflow-timer-node.json +81 -0
  566. package/templates/common/Harness/a2a/workflows.json +33 -0
  567. package/templates/common/Harness/ownership.manifest.json +263 -2
  568. package/templates/common/Harness/project/architecture.md +499 -0
  569. package/templates/common/Harness/research/README.md +20 -0
  570. package/templates/common/Harness/scripts/README.md +70 -6
  571. package/templates/common/Harness/scripts/a2a-terminal.mjs +12 -4
  572. package/templates/common/Harness/scripts/context-budget.mjs +7 -3
  573. package/templates/common/Harness/scripts/lib/memory-context.mjs +400 -0
  574. package/templates/common/Harness/scripts/lib/path-scope.mjs +77 -0
  575. package/templates/common/Harness/scripts/lib/research-policy.mjs +99 -0
  576. package/templates/common/Harness/scripts/lib/wf-search.mjs +189 -0
  577. package/templates/common/Harness/scripts/memory-context.mjs +63 -0
  578. package/templates/common/Harness/scripts/research-policy.mjs +27 -0
  579. package/templates/common/Harness/scripts/task-context.mjs +547 -0
  580. package/templates/common/Harness/scripts/task-group-index.mjs +242 -0
  581. package/templates/common/Harness/scripts/task-state.mjs +659 -95
  582. package/templates/common/Harness/scripts/validate-harness.mjs +70 -28
  583. package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +24 -0
  584. package/templates/common/Harness/scripts/wf-remove.mjs +10 -0
  585. package/templates/common/Harness/scripts/wf-search.mjs +10 -0
  586. package/templates/common/Harness/scripts/wf-ui-control.mjs +3144 -0
  587. package/templates/common/Harness/scripts/wf-update-check.mjs +274 -51
  588. package/templates/common/Harness/scripts/wf-update-runner.mjs +53 -16
  589. package/templates/common/Harness/settings.json +10 -0
  590. package/templates/common/Harness/specs/guides/SETUP.md +4 -3
  591. package/templates/common/Harness/specs/protocols/HARNESS_BRIDGE.md +429 -48
  592. package/templates/common/Harness/specs/protocols/MEMORY_PROTOCOL.md +24 -0
  593. package/templates/common/Harness/specs/protocols/TASK_ARCHIVE.md +14 -11
  594. package/templates/common/Harness/specs/runtime/command-surface.json +28 -0
  595. package/templates/common/Harness/specs/runtime/context-loading.md +6 -0
  596. package/templates/common/Harness/specs/runtime/dispatch.md +9 -1
  597. package/templates/common/Harness/specs/runtime/subagents.md +20 -5
  598. package/templates/common/Harness/specs/workflows/WF-AUTO-SPARK.md +19 -0
  599. package/templates/common/Harness/specs/workflows/WF-AUTO.md +21 -0
  600. package/templates/common/Harness/specs/workflows/WF-KERNEL.md +4 -2
  601. package/templates/common/Harness/specs/workflows/WF-MAX.md +39 -12
  602. package/templates/common/Harness/specs/workflows/WF-STATE.md +62 -14
  603. package/templates/common/Harness/specs/workflows/WF.md +32 -0
  604. package/templates/common/Harness/tasks/INDEX.json +7 -0
  605. package/templates/common/Harness/tasks/INDEX.md +12 -0
  606. package/templates/common/Harness/tasks/_template/PLAN.md +6 -0
  607. package/templates/common/Harness/tasks/_template/PROGRESS.md +5 -0
  608. package/templates/common/Harness/tasks/_template/STATE.json +22 -0
  609. package/templates/common/Harness/templates/TEST_PLAN.template.md +5 -4
  610. package/templates/common/Harness/templates/UI_CONTRACT.template.md +7 -5
  611. package/templates/common/Harness/wf-browser/README.md +69 -0
  612. package/templates/common/README.md +17 -0
  613. package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js +0 -17
  614. package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js.map +0 -1
  615. package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js +0 -7
  616. package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js.map +0 -1
  617. package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js +0 -12
  618. package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js.map +0 -1
  619. package/src/ui/dist/assets/TaskList-BN4r8s1i.js +0 -50
  620. package/src/ui/dist/assets/TaskList-BN4r8s1i.js.map +0 -1
  621. package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js +0 -74
  622. package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js.map +0 -1
  623. package/src/ui/dist/assets/WorkflowRoute-BnuhLJ6X.css +0 -1
  624. package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js +0 -39
  625. package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js.map +0 -1
  626. package/src/ui/dist/assets/index-BOVYWntB.js +0 -117
  627. package/src/ui/dist/assets/index-BOVYWntB.js.map +0 -1
  628. package/src/ui/dist/assets/index-DaUObq0H.css +0 -1
  629. package/src/ui/dist/assets/maximize-2-LNrr_rKr.js +0 -7
  630. package/src/ui/dist/assets/maximize-2-LNrr_rKr.js.map +0 -1
  631. package/src/ui/dist/assets/plus-QuTJyoT3.js +0 -7
  632. package/src/ui/dist/assets/plus-QuTJyoT3.js.map +0 -1
  633. package/src/ui/dist/assets/proxy-D0vUbds5.js.map +0 -1
  634. package/src/ui/dist/assets/refresh-cw-JNmExijd.js.map +0 -1
  635. package/src/ui/dist/assets/terminal-BBQvnOAK.js.map +0 -1
  636. package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js +0 -7
  637. package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js.map +0 -1
  638. package/src/ui/dist/assets/x-DoIOGAJz.js.map +0 -1
  639. package/src/ui/pnpm-lock.yaml +0 -1582
  640. package/src/ui/src/App.tsx +0 -86
  641. package/src/ui/src/api.ts +0 -93
  642. package/src/ui/src/components/AgentsRoute.tsx +0 -503
  643. package/src/ui/src/components/Footer.tsx +0 -69
  644. package/src/ui/src/components/Header.tsx +0 -175
  645. package/src/ui/src/components/LoadingView.tsx +0 -22
  646. package/src/ui/src/components/RolesRoute.tsx +0 -169
  647. package/src/ui/src/components/SettingsRoute.tsx +0 -166
  648. package/src/ui/src/components/TaskList.tsx +0 -388
  649. package/src/ui/src/components/TerminalDrawer.tsx +0 -611
  650. package/src/ui/src/components/WorkflowRoute.tsx +0 -670
  651. package/src/ui/src/hooks/useReducedMotion.ts +0 -17
  652. package/src/ui/src/hooks/useServerConnection.ts +0 -114
  653. package/src/ui/src/index.css +0 -281
  654. package/src/ui/src/main.tsx +0 -11
  655. package/src/ui/src/types.ts +0 -108
  656. /package/src/ui/dist/assets/{TerminalDrawer-6GBZ9nXN.css → terminalControl-6GBZ9nXN.css} +0 -0
@@ -0,0 +1,46 @@
1
+ // spawn-gate.mjs
2
+ //
3
+ // Bounds concurrent PTY spawns. Each spawn boots a heavy CLI process
4
+ // (claude/codex/opencode — hundreds of MB each), and clients can request many
5
+ // starts at once (canvas auto-start, CLI batches, agent fan-out). A small
6
+ // sliding window keeps the machine from being stampeded: excess starts queue
7
+ // FIFO and enter as earlier spawns settle.
8
+
9
+ export const DEFAULT_MAX_CONCURRENT_SPAWNS = 2;
10
+ export const DEFAULT_SPAWN_TIMEOUT_MS = 120_000;
11
+
12
+ export function createSpawnGate({
13
+ maxConcurrent = DEFAULT_MAX_CONCURRENT_SPAWNS,
14
+ timeoutMs = DEFAULT_SPAWN_TIMEOUT_MS,
15
+ } = {}) {
16
+ let active = 0;
17
+ const waiters = [];
18
+
19
+ const release = () => {
20
+ const next = waiters.shift();
21
+ if (next) next();
22
+ };
23
+
24
+ return async function withSlot(fn) {
25
+ if (active >= maxConcurrent) {
26
+ await new Promise((resolve) => waiters.push(resolve));
27
+ }
28
+ active += 1;
29
+ let timer = null;
30
+ try {
31
+ // Deadlock breaker: a hung spawn must not hold the window forever. On
32
+ // timeout the caller gets an error and the slot frees; the abandoned
33
+ // spawn settles (or not) in the background.
34
+ return await Promise.race([
35
+ fn(),
36
+ new Promise((_, reject) => {
37
+ timer = setTimeout(() => reject(new Error(`PTY spawn timed out after ${timeoutMs}ms`)), timeoutMs);
38
+ }),
39
+ ]);
40
+ } finally {
41
+ if (timer) clearTimeout(timer);
42
+ active -= 1;
43
+ release();
44
+ }
45
+ };
46
+ }
@@ -1,6 +1,28 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
+ export const OPEN_TASK_STATUSES = new Set(['active', 'blocked']);
5
+ const STATUS_ALIASES = new Map([
6
+ ['in-progress', 'active'], ['inprogress', 'active'], ['in_progress', 'active'],
7
+ ['running', 'active'], ['pending', 'active'],
8
+ ['needs-user-decision', 'blocked'], ['needs_user_decision', 'blocked'], ['failed', 'blocked'],
9
+ ['complete', 'closed'], ['completed', 'closed'], ['verified', 'closed'], ['archived', 'closed'],
10
+ ['abandoned', 'closed'], ['obsolete', 'closed'], ['done', 'closed'], ['closeout', 'closed'],
11
+ ['skipped', 'closed'],
12
+ ]);
13
+ // Only task-owning modes participate in durable WF lifecycle resume.
14
+ export const WF_MANAGED_MODES = new Set(['wf', 'wf-max']);
15
+
16
+ export function canonicalTaskStatus(value) {
17
+ const raw = String(value || '').trim().toLowerCase();
18
+ return STATUS_ALIASES.get(raw) || (OPEN_TASK_STATUSES.has(raw) || raw === 'closed' ? raw : '');
19
+ }
20
+
21
+ function canonicalMode(value) {
22
+ const mode = String(value || '').trim().toLowerCase();
23
+ return mode || 'direct';
24
+ }
25
+
4
26
  function readState(taskDir) {
5
27
  const statePath = path.join(taskDir, 'STATE.json');
6
28
  if (!fs.existsSync(statePath)) return null;
@@ -8,16 +30,56 @@ function readState(taskDir) {
8
30
  catch (err) { throw new Error(`STATE.json at ${statePath} contains malformed JSON: ${err.message}`); }
9
31
  }
10
32
 
33
+ function readJson(filePath) {
34
+ try {
35
+ if (!fs.existsSync(filePath)) return null;
36
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
37
+ } catch {
38
+ return null;
39
+ }
40
+ }
41
+
42
+ function pushRuntime(runtimes, runtime) {
43
+ const value = String(runtime || '').trim();
44
+ if (!value) return;
45
+ if (!runtimes.some(item => item.toLowerCase() === value.toLowerCase())) {
46
+ runtimes.push(value);
47
+ }
48
+ }
49
+
50
+ function runtimeHistoryForTask(state, taskDir) {
51
+ const runtimes = [];
52
+ for (const key of ['defaultRuntime', 'defaultAgentRuntime', 'agentRuntime', 'cliAgent']) {
53
+ pushRuntime(runtimes, state?.[key]);
54
+ }
55
+ for (const key of ['runtimeHistory', 'runtimes', 'terminalRuntimes']) {
56
+ if (Array.isArray(state?.[key])) {
57
+ for (const runtime of state[key]) pushRuntime(runtimes, runtime);
58
+ }
59
+ }
60
+
61
+ const sessionsDir = path.join(taskDir, 'sessions');
62
+ let entries = [];
63
+ try { entries = fs.readdirSync(sessionsDir, { withFileTypes: true }); }
64
+ catch { entries = []; }
65
+ for (const entry of entries) {
66
+ if (!entry.isDirectory()) continue;
67
+ const sessionState = readJson(path.join(sessionsDir, entry.name, 'STATE.json'));
68
+ pushRuntime(runtimes, sessionState?.runtime);
69
+ }
70
+ return runtimes;
71
+ }
72
+
11
73
  function normalizeAcceptance(acceptance) {
12
74
  if (!Array.isArray(acceptance)) return [];
13
75
  return acceptance.map((ac) => {
14
- if (typeof ac === 'string') return { id: '', text: ac, status: 'pending' };
76
+ if (typeof ac === 'string') return { id: ac, text: '', status: 'tracked' };
15
77
  if (ac && typeof ac === 'object') return {
16
78
  id: ac.id || '',
17
79
  text: ac.text || JSON.stringify(ac),
18
- status: ac.status || 'pending',
80
+ status: ac.status || 'tracked',
19
81
  };
20
- return { id: '', text: String(ac), status: 'pending' };
82
+ return { id: String(ac), text: '', status: 'tracked' };
21
83
  });
22
84
  }
23
85
 
@@ -28,6 +90,8 @@ export function parseTaskCapsule(taskDir) {
28
90
  throw new Error(`STATE.json at ${taskDir} is missing required field: schemaVersion`);
29
91
  }
30
92
  const links = state.links || { dependsOn: [], blocks: [], related: [] };
93
+ const mode = canonicalMode(state.mode);
94
+ const status = canonicalTaskStatus(state.status) || 'active';
31
95
  return {
32
96
  taskId: state.taskId || path.basename(taskDir),
33
97
  status: state.status || 'unknown',
@@ -35,10 +99,19 @@ export function parseTaskCapsule(taskDir) {
35
99
  gate: state.gate || null,
36
100
  tier: state.tier || null,
37
101
  mode: state.mode || null,
102
+ project: String(state.project || state.group || 'default').trim() || 'default',
103
+ group: state.group || null,
104
+ tags: Array.isArray(state.tags) ? state.tags.map(tag => String(tag).trim()).filter(Boolean) : [],
105
+ createdAt: state.createdAt || null,
106
+ startedAt: state.startedAt || null,
38
107
  updatedAt: state.updatedAt || null,
108
+ closedAt: state.closedAt || null,
109
+ wfManaged: WF_MANAGED_MODES.has(mode),
110
+ resumeRequired: WF_MANAGED_MODES.has(mode) && OPEN_TASK_STATUSES.has(status),
39
111
  activeQuestion: state.activeQuestion || null,
40
112
  nextAction: state.nextAction || null,
41
113
  defaultRuntime: state.defaultRuntime || state.defaultAgentRuntime || state.agentRuntime || state.cliAgent || null,
114
+ runtimeHistory: runtimeHistoryForTask(state, taskDir),
42
115
  acceptance: normalizeAcceptance(state.acceptance),
43
116
  dependsOn: Array.isArray(links.dependsOn) ? links.dependsOn : [],
44
117
  blocks: Array.isArray(links.blocks) ? links.blocks : [],
@@ -67,6 +140,18 @@ export function parseTaskList(tasksRoot) {
67
140
  return capsules;
68
141
  }
69
142
 
143
+ /** Read the single durable Harness focus pointer without selecting a task. */
144
+ export function readActiveTaskId(projectRoot) {
145
+ try {
146
+ const content = fs.readFileSync(path.join(projectRoot, 'Harness', 'PROGRESS.md'), 'utf8');
147
+ const match = content.match(/## Active Task\s+^- ([^\r\n]+)/m);
148
+ const taskId = match?.[1]?.trim() || '';
149
+ return taskId && !/^none$/i.test(taskId) ? taskId : null;
150
+ } catch {
151
+ return null;
152
+ }
153
+ }
154
+
70
155
  /** Parse archived tasks from _archive/YYYY/task-name/ */
71
156
  export function parseArchivedTasks(tasksRoot) {
72
157
  const archiveRoot = path.join(tasksRoot, '_archive');
@@ -3,6 +3,8 @@ import path from 'node:path';
3
3
  import { validateTaskId } from './security.mjs';
4
4
 
5
5
  const MAX_READ_LIMIT = 2000;
6
+ const MAX_DROPPED_FILES = 12;
7
+ const MAX_DROPPED_FILE_BYTES = 10 * 1024 * 1024;
6
8
 
7
9
  function validateSessionId(sessionId) {
8
10
  return validateTaskId(sessionId);
@@ -56,6 +58,83 @@ function appendJsonl(filePath, data) {
56
58
  fs.appendFileSync(filePath, `${JSON.stringify(data)}\n`, 'utf8');
57
59
  }
58
60
 
61
+ // ── In-memory session index ──────────────────────────────────────────────────
62
+ // Maintained by every persistSession/appendTerminalData call and rebuilt once at
63
+ // backend startup. Snapshot fingerprinting (P1a) and the startup orphan
64
+ // downgrade (P1b) read this instead of scanning every session dir per request.
65
+ const sessionIndex = new Map();
66
+
67
+ export function touchSessionIndex(record, { activityOnly = false } = {}) {
68
+ if (!record?.sessionId) return;
69
+ const entry = sessionIndex.get(record.sessionId) || {};
70
+ // activityOnly (terminal chunks/flushes) updates seq but NOT updatedAt: the
71
+ // snapshot fingerprint must not churn with streamed output, or every 5s
72
+ // poll rebuilds the snapshot and re-renders the canvas while any terminal
73
+ // is active.
74
+ const updatedAt = activityOnly
75
+ ? (entry.updatedAt || new Date().toISOString())
76
+ : (record.updatedAt ?? entry.updatedAt ?? new Date().toISOString());
77
+ sessionIndex.set(record.sessionId, {
78
+ ...entry,
79
+ sessionId: record.sessionId,
80
+ taskId: record.taskId ?? entry.taskId ?? null,
81
+ runtime: record.runtime ?? entry.runtime,
82
+ status: record.status ?? entry.status,
83
+ updatedAt,
84
+ terminalSeq: Number(record.terminalSeq ?? entry.terminalSeq ?? 0),
85
+ });
86
+ }
87
+
88
+ export function getSessionIndexSummary() {
89
+ let latestUpdatedAt = '';
90
+ for (const entry of sessionIndex.values()) {
91
+ if (String(entry.updatedAt || '') > latestUpdatedAt) latestUpdatedAt = String(entry.updatedAt);
92
+ }
93
+ return { count: sessionIndex.size, latestUpdatedAt };
94
+ }
95
+
96
+ export function getSessionIndexRecord(sessionId) {
97
+ return sessionIndex.get(sessionId) || null;
98
+ }
99
+
100
+ // Rebuilds the index with a single scan of all session dirs. Called once at
101
+ // backend startup; per-request scans are gone.
102
+ export function buildSessionIndex(projectRoot) {
103
+ sessionIndex.clear();
104
+ for (const found of allSessionDirs(projectRoot)) {
105
+ const state = readJson(path.join(found.dir, 'STATE.json'), null);
106
+ if (state) touchSessionIndex({ ...state, taskId: found.taskId });
107
+ }
108
+ return sessionIndex.size;
109
+ }
110
+
111
+ // ── One-time startup migration: orphaned live sessions ───────────────────────
112
+ // At backend startup the in-memory registry manages nothing, so every disk
113
+ // record with a live status is orphaned by definition (its PTY died with the
114
+ // previous server). Persist the downgrade so snapshot, delete guards, and the
115
+ // UI all see one consistent truth instead of "fake-live" sessions.
116
+ export function downgradeOrphanedDiskSessions(projectRoot) {
117
+ const downgraded = [];
118
+ for (const found of allSessionDirs(projectRoot)) {
119
+ const state = readJson(path.join(found.dir, 'STATE.json'), null);
120
+ if (!state || !['running', 'starting'].includes(state.status)) continue;
121
+ const updated = {
122
+ ...state,
123
+ status: 'stopped',
124
+ blockedReason: state.blockedReason || 'not-managed-by-current-wf-ui',
125
+ orphanedAt: new Date().toISOString(),
126
+ updatedAt: new Date().toISOString(),
127
+ // No clients can be attached right after a restart.
128
+ wsClientCount: 0,
129
+ attachMode: false,
130
+ };
131
+ writeJson(path.join(found.dir, 'STATE.json'), updated);
132
+ touchSessionIndex({ ...updated, taskId: found.taskId });
133
+ downgraded.push(found.sessionId);
134
+ }
135
+ return downgraded;
136
+ }
137
+
59
138
  export function persistSession(projectRoot, session, patch = {}) {
60
139
  const dir = sessionDir(projectRoot, session.taskId, session.sessionId);
61
140
  const current = readJson(path.join(dir, 'STATE.json'), {});
@@ -66,6 +145,7 @@ export function persistSession(projectRoot, session, patch = {}) {
66
145
  updatedAt: new Date().toISOString(),
67
146
  };
68
147
  writeJson(path.join(dir, 'STATE.json'), state);
148
+ touchSessionIndex(state);
69
149
  return state;
70
150
  }
71
151
 
@@ -80,11 +160,51 @@ export function appendSessionEvent(projectRoot, session, event) {
80
160
  });
81
161
  }
82
162
 
83
- export function appendTerminalData(projectRoot, session, data, stream = 'stdout') {
84
- const dir = sessionDir(projectRoot, session.taskId, session.sessionId);
163
+ // ── Batched terminal writes ──────────────────────────────────────────────────
164
+ // Per-chunk sync file I/O (read STATE.json + append terminal.jsonl + rewrite
165
+ // STATE.json per chunk) saturated the event loop when many terminals streamed
166
+ // at once. Entries now buffer per session and flush as ONE append + ONE state
167
+ // write every 500ms (or at 64 entries / 64KB, whichever first). Readers flush
168
+ // before reading, so disk reads always see the latest entry.
169
+ const TERMINAL_FLUSH_DELAY_MS = 500;
170
+ const TERMINAL_FLUSH_MAX_ENTRIES = 64;
171
+ const TERMINAL_FLUSH_MAX_BYTES = 64 * 1024;
172
+ const terminalFlushTimers = new Map();
173
+ const terminalBuffers = new Map();
174
+
175
+ export function flushTerminalBuffer(projectRoot, sessionId) {
176
+ const pending = terminalBuffers.get(sessionId);
177
+ if (!pending) return 0;
178
+ terminalBuffers.delete(sessionId);
179
+ const timer = terminalFlushTimers.get(sessionId);
180
+ if (timer) {
181
+ clearTimeout(timer);
182
+ terminalFlushTimers.delete(sessionId);
183
+ }
184
+ const dir = sessionDir(projectRoot, pending.taskId, sessionId);
85
185
  const statePath = path.join(dir, 'STATE.json');
86
- const state = readJson(statePath, session);
87
- const nextSeq = Number(state.terminalSeq || 0) + 1;
186
+ const state = readJson(statePath, pending.session);
187
+ const lines = pending.entries.map((entry) => `${JSON.stringify(entry)}\n`).join('');
188
+ fs.mkdirSync(dir, { recursive: true });
189
+ fs.appendFileSync(path.join(dir, 'terminal.jsonl'), lines, 'utf8');
190
+ const last = pending.entries[pending.entries.length - 1];
191
+ const updatedState = {
192
+ ...state,
193
+ terminalSeq: last.seq,
194
+ lastActivityAt: last.ts,
195
+ updatedAt: last.ts,
196
+ };
197
+ writeJson(statePath, updatedState);
198
+ touchSessionIndex(updatedState, { activityOnly: true });
199
+ return pending.entries.length;
200
+ }
201
+
202
+ export function appendTerminalData(projectRoot, session, data, stream = 'stdout') {
203
+ const indexRecord = getSessionIndexRecord(session.sessionId);
204
+ const baseSeq = indexRecord
205
+ ? Number(indexRecord.terminalSeq || 0)
206
+ : Number(readJson(path.join(sessionDir(projectRoot, session.taskId, session.sessionId), 'STATE.json'), {}).terminalSeq || 0);
207
+ const nextSeq = baseSeq + 1;
88
208
  const entry = {
89
209
  seq: nextSeq,
90
210
  ts: new Date().toISOString(),
@@ -94,13 +214,27 @@ export function appendTerminalData(projectRoot, session, data, stream = 'stdout'
94
214
  stream,
95
215
  data: String(data || ''),
96
216
  };
97
- appendJsonl(path.join(dir, 'terminal.jsonl'), entry);
98
- writeJson(statePath, {
99
- ...state,
100
- terminalSeq: nextSeq,
101
- lastActivityAt: entry.ts,
102
- updatedAt: entry.ts,
103
- });
217
+ const pending = terminalBuffers.get(session.sessionId) || {
218
+ taskId: session.taskId,
219
+ session,
220
+ entries: [],
221
+ bytes: 0,
222
+ };
223
+ pending.entries.push(entry);
224
+ pending.bytes += entry.data.length;
225
+ terminalBuffers.set(session.sessionId, pending);
226
+ touchSessionIndex({ ...session, terminalSeq: nextSeq, updatedAt: entry.ts }, { activityOnly: true });
227
+
228
+ if (pending.entries.length >= TERMINAL_FLUSH_MAX_ENTRIES || pending.bytes >= TERMINAL_FLUSH_MAX_BYTES) {
229
+ flushTerminalBuffer(projectRoot, session.sessionId);
230
+ } else if (!terminalFlushTimers.has(session.sessionId)) {
231
+ const timer = setTimeout(() => {
232
+ terminalFlushTimers.delete(session.sessionId);
233
+ flushTerminalBuffer(projectRoot, session.sessionId);
234
+ }, TERMINAL_FLUSH_DELAY_MS);
235
+ timer.unref?.();
236
+ terminalFlushTimers.set(session.sessionId, timer);
237
+ }
104
238
  return entry;
105
239
  }
106
240
 
@@ -165,6 +299,8 @@ export function findTerminalSession(projectRoot, sessionId) {
165
299
  }
166
300
 
167
301
  export function readTerminalRange(projectRoot, { sessionId, fromSeq, toSeq, tail } = {}) {
302
+ // Disk reads always see the latest entry: flush the pending buffer first.
303
+ flushTerminalBuffer(projectRoot, sessionId);
168
304
  const found = findTerminalSession(projectRoot, sessionId);
169
305
  if (!found) return { sessionId, entries: [] };
170
306
  let entries = readJsonl(path.join(found.dir, 'terminal.jsonl'));
@@ -223,3 +359,81 @@ export function recordInputRequest(projectRoot, sessionId, text) {
223
359
  appendJsonl(path.join(found.dir, 'input-requests.jsonl'), event);
224
360
  return event;
225
361
  }
362
+
363
+ export function writeDroppedTerminalFiles(projectRoot, sessionId, files = []) {
364
+ const found = findTerminalSession(projectRoot, sessionId);
365
+ if (!found) {
366
+ const err = new Error(`Session not found: ${sessionId}`);
367
+ err.code = 'NOT_FOUND';
368
+ throw err;
369
+ }
370
+ if (!Array.isArray(files)) throw new Error('files must be an array');
371
+ if (files.length > MAX_DROPPED_FILES) throw new Error(`Too many dropped files; max ${MAX_DROPPED_FILES}`);
372
+
373
+ const dropRoot = path.join(found.dir, 'drops');
374
+ fs.mkdirSync(dropRoot, { recursive: true });
375
+
376
+ const written = files.map((file, index) => {
377
+ const name = safeDropFileName(file?.name, index);
378
+ const buffer = decodeDropContent(file?.contentBase64);
379
+ if (buffer.byteLength > MAX_DROPPED_FILE_BYTES) {
380
+ throw new Error(`Dropped file is too large: ${name}`);
381
+ }
382
+ const targetPath = uniqueDropPath(dropRoot, name);
383
+ fs.writeFileSync(targetPath, buffer);
384
+ return {
385
+ name: path.basename(targetPath),
386
+ path: targetPath,
387
+ size: buffer.byteLength,
388
+ terminalText: quoteTerminalPath(targetPath),
389
+ };
390
+ });
391
+
392
+ const state = readJson(path.join(found.dir, 'STATE.json'), { sessionId, taskId: found.taskId });
393
+ appendSessionEvent(projectRoot, state, {
394
+ type: 'terminal.files.dropped',
395
+ count: written.length,
396
+ files: written.map(file => ({ name: file.name, path: file.path, size: file.size })),
397
+ });
398
+ return {
399
+ files: written,
400
+ terminalInput: written.map(file => file.terminalText).join(' '),
401
+ };
402
+ }
403
+
404
+ function safeDropFileName(value, index) {
405
+ const raw = String(value || `dropped-file-${index + 1}`);
406
+ const basename = raw.split(/[\\/]+/).pop() || `dropped-file-${index + 1}`;
407
+ const cleaned = basename
408
+ .replace(/[<>:"/\\|?*\x00-\x1f]/g, '_')
409
+ .replace(/^\.+/, '')
410
+ .trim();
411
+ return (cleaned || `dropped-file-${index + 1}`).slice(0, 160);
412
+ }
413
+
414
+ function decodeDropContent(value) {
415
+ const encoded = String(value || '');
416
+ if (encoded.length > Math.ceil(MAX_DROPPED_FILE_BYTES * 1.4)) {
417
+ throw new Error('Dropped file payload is too large');
418
+ }
419
+ return Buffer.from(encoded, 'base64');
420
+ }
421
+
422
+ function uniqueDropPath(dropRoot, filename) {
423
+ const ext = path.extname(filename);
424
+ const stem = path.basename(filename, ext) || 'dropped-file';
425
+ let candidate = path.join(dropRoot, filename);
426
+ for (let i = 2; fs.existsSync(candidate); i++) {
427
+ candidate = path.join(dropRoot, `${stem}-${i}${ext}`);
428
+ }
429
+ const resolvedRoot = path.resolve(dropRoot);
430
+ const resolvedCandidate = path.resolve(candidate);
431
+ if (resolvedCandidate !== resolvedRoot && !resolvedCandidate.startsWith(`${resolvedRoot}${path.sep}`)) {
432
+ throw new Error('Dropped file path escapes drop directory');
433
+ }
434
+ return resolvedCandidate;
435
+ }
436
+
437
+ function quoteTerminalPath(filePath) {
438
+ return `'${String(filePath).replace(/'/g, "''")}'`;
439
+ }
@@ -0,0 +1,226 @@
1
+ import { fire as fireTimer, dispatchWakeup as dispatchTimerWakeup } from './workflow-node-types/timer-node.mjs'
2
+ import { getEventNode, listEventNodes, updateEventNode } from './workflow-event-node-store.mjs'
3
+
4
+ // Bounded timer wakeup scheduler (D1, M5, AC-011/AC-012, T10/T11 backend part).
5
+ //
6
+ // The loop is bounded, never resident:
7
+ // - it runs only while at least one enabled timer exists (syncTimerScheduler
8
+ // starts/stops it from event-node mutations, and the tick self-stops when
9
+ // getTimers() returns no enabled timers);
10
+ // - the interval handle is unref'd, so it never keeps a closed server
11
+ // process alive;
12
+ // - stopTimerScheduler() clears the handle; server.mjs stopServer() invokes
13
+ // it on server close, and tests call it directly on teardown.
14
+ //
15
+ // Each tick: for every enabled timer whose heartbeat.base.nextDueAt is due,
16
+ // run the timer.fire flow, then dispatch a wakeup envelope into the message
17
+ // queues of the connected/magnetic-group agent nodes (never PTY stdin).
18
+
19
+ const DEFAULT_INTERVAL_MS = 1000
20
+
21
+ let activeHandle = null
22
+
23
+ // Set while the scheduler itself performs a store update (nextDueAt advance),
24
+ // so event-node-store mutations made by the scheduler do not re-sync/re-arm
25
+ // the loop and clobber the caller-chosen intervalMs config.
26
+ let suppressingSync = false
27
+
28
+ function parseTime(value) {
29
+ const time = Date.parse(String(value || ''))
30
+ return Number.isFinite(time) ? time : 0
31
+ }
32
+
33
+ function clampInteger(value, { min = 0, max = Number.MAX_SAFE_INTEGER, fallback = 0 } = {}) {
34
+ const number = Number(value)
35
+ if (!Number.isFinite(number)) return fallback
36
+ return Math.max(min, Math.min(max, Math.floor(number)))
37
+ }
38
+
39
+ // Builds the due-check data + bound fire flow for every enabled timer in the
40
+ // project. Read-only apart from the `fire` closure, which is invoked by the
41
+ // scheduler tick and advances the runtime clock after firing.
42
+ //
43
+ // Bounded loop semantics (F14/D11): a timer whose `loop.maxIterations` is set
44
+ // stops advancing once `loop.runCount` reaches it — the entry is skipped and
45
+ // the loop is marked complete (loop.enabled=false) through the event-node
46
+ // store API, never by mutating state files directly. `loop.stopOnFailure`
47
+ // marks the loop complete when a fire throws. Timers without a bounded loop
48
+ // keep the existing interval behavior.
49
+ function enabledTimerEntries(projectRoot) {
50
+ const entries = []
51
+ for (const node of listEventNodes(projectRoot)) {
52
+ if (String(node.type || '') !== 'timer') continue
53
+ const timerNodeId = node.nodeId
54
+ const current = getEventNode(projectRoot, timerNodeId)
55
+ const state = current.state
56
+ if (!state.enabled) continue
57
+ const loop = state.loop || {}
58
+ const maxIterations = clampInteger(loop.maxIterations, { min: 0, fallback: 0 })
59
+ const runCount = clampInteger(loop.runCount, { min: 0, fallback: 0 })
60
+ if (maxIterations > 0 && runCount >= maxIterations) continue // loop exhausted — stop advancing (F14)
61
+ const base = state.heartbeat?.base || {}
62
+ const nextDueAt = String(base.nextDueAt || '')
63
+ const dueAt = parseTime(nextDueAt)
64
+ if (!dueAt) continue // base lane not armed (disabled or not running)
65
+ const intervalSeconds = clampInteger(
66
+ base.intervalSeconds ?? state.schedule?.intervalSeconds,
67
+ { min: 1, fallback: 60 },
68
+ )
69
+ const mode = String(state.schedule?.mode || 'manual').toLowerCase()
70
+ const stopOnFailure = loop.stopOnFailure !== false
71
+ entries.push({
72
+ timerNodeId,
73
+ projectRoot,
74
+ enabled: true,
75
+ nextDueAt,
76
+ dueAt,
77
+ intervalSeconds,
78
+ mode,
79
+ fire: (firedAt) => {
80
+ try {
81
+ const fired = fireTimer(timerNodeId, projectRoot, {})
82
+ const heartbeat = current.state.heartbeat || {}
83
+ const nextRunCount = maxIterations > 0 ? runCount + 1 : runCount
84
+ const loopComplete = maxIterations > 0 && nextRunCount >= maxIterations
85
+ const advancePatch = {
86
+ revision: fired.revision,
87
+ heartbeat: {
88
+ ...heartbeat,
89
+ base: { ...(heartbeat.base || {}), nextDueAt: new Date(parseTime(firedAt) + intervalSeconds * 1000).toISOString() },
90
+ },
91
+ }
92
+ if (maxIterations > 0) {
93
+ advancePatch.loop = {
94
+ ...loop,
95
+ runCount: nextRunCount,
96
+ // Loop-complete marker persisted through the store API (F14).
97
+ enabled: loopComplete ? false : loop.enabled,
98
+ }
99
+ }
100
+ if (mode === 'once') advancePatch.enabled = false
101
+ suppressingSync = true
102
+ try {
103
+ updateEventNode(projectRoot, timerNodeId, advancePatch)
104
+ } finally {
105
+ suppressingSync = false
106
+ }
107
+ return fired
108
+ } catch (error) {
109
+ if (stopOnFailure && maxIterations > 0) {
110
+ try {
111
+ const failed = getEventNode(projectRoot, timerNodeId)
112
+ suppressingSync = true
113
+ try {
114
+ updateEventNode(projectRoot, timerNodeId, {
115
+ revision: failed.node.revision,
116
+ loop: { ...(failed.state.loop || {}), enabled: false },
117
+ })
118
+ } finally {
119
+ suppressingSync = false
120
+ }
121
+ } catch {
122
+ // A failed loop-complete marker must never crash the server loop.
123
+ }
124
+ }
125
+ throw error
126
+ }
127
+ },
128
+ })
129
+ }
130
+ return entries
131
+ }
132
+
133
+ function runTick({ getTimers, dispatchWakeup }) {
134
+ let timers
135
+ try {
136
+ timers = Array.isArray(getTimers()) ? getTimers() : []
137
+ } catch {
138
+ return
139
+ }
140
+ if (timers.length === 0) {
141
+ stopTimerScheduler()
142
+ return
143
+ }
144
+ const now = Date.now()
145
+ for (const timer of timers) {
146
+ if (!timer.enabled || !timer.dueAt || timer.dueAt > now) continue
147
+ const firedAt = new Date().toISOString()
148
+ const scheduledAt = timer.nextDueAt
149
+ let fired = null
150
+ try {
151
+ if (typeof timer.fire === 'function') fired = timer.fire(firedAt)
152
+ } catch {
153
+ // A failed fire must never crash the server loop; the next tick retries.
154
+ continue
155
+ }
156
+ if (!fired) continue
157
+ try {
158
+ dispatchWakeup({
159
+ projectRoot: timer.projectRoot,
160
+ timerNodeId: timer.timerNodeId,
161
+ scheduledAt,
162
+ firedAt,
163
+ intervalSeconds: timer.intervalSeconds,
164
+ })
165
+ } catch {
166
+ // Wakeup delivery must never crash the server loop.
167
+ }
168
+ }
169
+ }
170
+
171
+ export function startTimerScheduler({ getTimers, dispatchWakeup, intervalMs = DEFAULT_INTERVAL_MS } = {}) {
172
+ if (typeof getTimers !== 'function' || typeof dispatchWakeup !== 'function') return null
173
+ stopTimerScheduler()
174
+ const interval = setInterval(() => runTick({ getTimers, dispatchWakeup }), Math.max(Number(intervalMs) || DEFAULT_INTERVAL_MS, 5))
175
+ interval.unref?.()
176
+ activeHandle = { interval, getTimers, dispatchWakeup, intervalMs: Math.max(Number(intervalMs) || DEFAULT_INTERVAL_MS, 5) }
177
+ return activeHandle
178
+ }
179
+
180
+ export function stopTimerScheduler() {
181
+ if (!activeHandle) return { stopped: true, wasActive: false }
182
+ clearInterval(activeHandle.interval)
183
+ activeHandle = null
184
+ return { stopped: true, wasActive: true }
185
+ }
186
+
187
+ export function isTimerSchedulerActive() {
188
+ return activeHandle !== null
189
+ }
190
+
191
+ export function isTimerSchedulerSyncSuppressed() {
192
+ return suppressingSync
193
+ }
194
+
195
+ // Hook wired by workflow-event-node-store.mjs on event-node create/update/
196
+ // delete/restore. Starts the bounded loop when any enabled timer exists,
197
+ // stops it when none remain, and keeps the running configuration intact when
198
+ // nothing changed (so scheduler-internal store updates do not re-arm the loop).
199
+ export function syncTimerScheduler(projectRoot, opts = {}) {
200
+ if (suppressingSync) return { active: isTimerSchedulerActive() }
201
+ const intervalMs = Number(opts.intervalMs ?? DEFAULT_INTERVAL_MS)
202
+ let timers = []
203
+ try {
204
+ timers = enabledTimerEntries(projectRoot)
205
+ } catch {
206
+ return { active: isTimerSchedulerActive() }
207
+ }
208
+ if (timers.length === 0) {
209
+ stopTimerScheduler()
210
+ return { active: false, timers: 0 }
211
+ }
212
+ if (
213
+ activeHandle
214
+ && activeHandle.projectRoot === projectRoot
215
+ && Number(activeHandle.intervalMs) === intervalMs
216
+ ) {
217
+ return { active: true, timers: timers.length }
218
+ }
219
+ const handle = startTimerScheduler({
220
+ getTimers: () => enabledTimerEntries(projectRoot),
221
+ dispatchWakeup: (envelope) => dispatchTimerWakeup(envelope.timerNodeId, envelope.projectRoot, envelope),
222
+ intervalMs,
223
+ })
224
+ if (handle) handle.projectRoot = projectRoot
225
+ return { active: isTimerSchedulerActive(), timers: timers.length }
226
+ }