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,846 @@
1
+ // workflow-depth-resume-dock.test.mjs
2
+ //
3
+ // W2 combined suite: depth-gated canvas-agent spawning (P1), resume wiring
4
+ // (P2), dock typed actions (P3), and the updateEdge HTTP route (P4).
5
+ //
6
+ // P1 — Depth gate: POST /api/sessions rejects 403 DEPTH_LIMIT when the
7
+ // ACTING agent (payload.parentAgentId / parentNodeId / actor headers) is
8
+ // itself a canvas subagent (graph node carrying parentAgentId) and the
9
+ // payload creates an agent node (agentKind set). Root actors and
10
+ // anonymous / unresolvable actors stay allowed.
11
+ // P2 — Resume: agent.start on a node with a persisted previous agent
12
+ // session carries resume args (runtime-specific) into the spawn; restart
13
+ // defaults to resume=true; payload resume:false opts out; fresh/never-run
14
+ // sessions do not resume. The spawn threading is proven through a
15
+ // pty-adapter fixture (registerHooks redirect, same pattern as
16
+ // workflow-terminal-control.test.mjs) plus a helper server subprocess so
17
+ // the agent.start path reaches the real spawn branch (the test runner
18
+ // process auto-defers PTY spawns).
19
+ // P3 — Dock: agent.attachDock creates the graph.capsuleDockLinks entry
20
+ // (edges/connections derived from the existing edge between the pair),
21
+ // re-attach is idempotent (updates side), agent.setDockSide updates the
22
+ // side (404 DOCK_NOT_FOUND when missing), agent.detachDock removes
23
+ // (idempotent removed:false).
24
+ // P4 — agent.updateEdge: relation/direction/handle patch through the
25
+ // graph-actions route; empty patch 400 EMPTY_UPDATE; unknown edge 404
26
+ // EDGE_NOT_FOUND.
27
+ // CLI — node-map --action attachDock/detachDock/setDockSide payload shapes
28
+ // and workflow-node-action --resume (real subprocess vs recording server).
29
+ //
30
+ // Pattern: in-process node:test + HTTP server on port 0, temp roots, fake
31
+ // PTYs via a pty-adapter.mjs fixture (registerHooks), and a stub `codex`
32
+ // executable prepended to PATH so POST /api/sessions reaches the spawn
33
+ // branch (mirrors workflow-terminal-control.test.mjs).
34
+ import { describe, it, beforeEach, afterEach, before, after } from 'node:test';
35
+ import assert from 'node:assert/strict';
36
+ import fs from 'node:fs';
37
+ import path from 'node:path';
38
+ import http from 'node:http';
39
+ import { spawn } from 'node:child_process';
40
+ import { registerHooks } from 'node:module';
41
+ import { pathToFileURL } from 'node:url';
42
+ import { makeHarnessTempRoot } from '../../../tests/support/temp-root.js';
43
+ import { SessionRegistry } from '../session-registry.mjs';
44
+ import { registerPtyProcess, unregisterPtyProcess } from '../ws-terminal.mjs';
45
+ import { persistSession } from '../terminal-store.mjs';
46
+ import { connectNodes, updateEdge } from '../workflow-graph-store.mjs';
47
+ import { loadWorkflowGraphMap, writeWorkflowGraphMap } from '../a2a-store.mjs';
48
+
49
+ // ── pty-adapter fixture (deterministic spawn path) ──────────────────────────
50
+ // Replaces pty-adapter.mjs for this process AND for the helper server
51
+ // subprocess (R5): spawnPty records the spawn opts in memory and appends a
52
+ // JSON line to WF_TEST_SPAWN_LOG when set, so a child process can expose the
53
+ // command args it was spawned with.
54
+ const FIXTURE_SOURCE = [
55
+ '// Test fixture replacing pty-adapter.mjs (workflow-depth-resume-dock.test.mjs).',
56
+ 'import fs from "node:fs";',
57
+ 'const recorder = {',
58
+ ' spawnOpts: [],',
59
+ ' reset() { this.spawnOpts.length = 0; },',
60
+ '};',
61
+ 'export async function spawnPty(opts) {',
62
+ ' recorder.spawnOpts.push(opts);',
63
+ ' const logPath = process.env.WF_TEST_SPAWN_LOG || "";',
64
+ ' if (logPath) {',
65
+ ' fs.appendFileSync(logPath, JSON.stringify({ sessionId: opts.sessionId, runtime: opts.runtime, command: opts.command, commandArgs: opts.commandArgs || [] }) + "\\n");',
66
+ ' }',
67
+ ' const ptyProcess = {',
68
+ ' pid: 43000 + recorder.spawnOpts.length,',
69
+ ' write() {},',
70
+ ' kill() {},',
71
+ ' onData() {},',
72
+ ' onExit() {},',
73
+ ' };',
74
+ ' return { sessionId: opts.sessionId, pid: ptyProcess.pid, ptyProcess, ptyProvider: "test-fixture" };',
75
+ '}',
76
+ 'export { recorder };',
77
+ ].join('\n');
78
+
79
+ const fixtureDir = makeHarnessTempRoot('wf-depth-resume-dock-fixture-');
80
+ const fixturePath = path.join(fixtureDir, 'pty-adapter.mjs');
81
+ fs.writeFileSync(fixturePath, FIXTURE_SOURCE, 'utf8');
82
+ const fixtureUrl = pathToFileURL(fixturePath).href;
83
+
84
+ registerHooks({
85
+ resolve(specifier, context, nextResolve) {
86
+ if (specifier === './pty-adapter.mjs' && String(context.parentURL || '').endsWith('/src/wf-ui-server/server.mjs')) {
87
+ return { url: fixtureUrl, shortCircuit: true };
88
+ }
89
+ return nextResolve(specifier, context);
90
+ },
91
+ });
92
+
93
+ const { startServer, stopServer } = await import('../server.mjs');
94
+ const { recorder } = await import(fixtureUrl);
95
+
96
+ // ── runtime stub ────────────────────────────────────────────────────────────
97
+ // A stub `codex` on PATH makes createRuntimeSession take the spawn branch
98
+ // (runtimeInfo.path + launchable) so spawnPty runs for real (through the
99
+ // fixture) in the in-process tests and in the helper server subprocess.
100
+ const ORIGINAL_PATH = process.env.PATH || '';
101
+ let runtimeStubDir = '';
102
+
103
+ before(() => {
104
+ runtimeStubDir = makeHarnessTempRoot('wf-depth-resume-dock-runtime-');
105
+ if (process.platform === 'win32') {
106
+ fs.writeFileSync(path.join(runtimeStubDir, 'codex.cmd'), '@echo off\r\nrem wf-ui depth/resume/dock test stub\r\n');
107
+ } else {
108
+ const stub = path.join(runtimeStubDir, 'codex');
109
+ fs.writeFileSync(stub, '#!/bin/sh\n');
110
+ fs.chmodSync(stub, 0o755);
111
+ }
112
+ process.env.PATH = `${runtimeStubDir}${path.delimiter}${ORIGINAL_PATH}`;
113
+ });
114
+
115
+ after(() => {
116
+ process.env.PATH = ORIGINAL_PATH;
117
+ for (const dir of [runtimeStubDir, fixtureDir]) {
118
+ try { fs.rmSync(dir, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 }); } catch { /* best effort */ }
119
+ }
120
+ });
121
+
122
+ // ── HTTP helper ──
123
+ function jsonRequest(baseUrl, route, { method = 'GET', body, token = '', headers = {} } = {}) {
124
+ return new Promise((resolve, reject) => {
125
+ const url = new URL(route, baseUrl);
126
+ const payload = body ? JSON.stringify(body) : null;
127
+ const req = http.request({
128
+ hostname: url.hostname,
129
+ port: url.port,
130
+ path: `${url.pathname}${url.search}`,
131
+ method,
132
+ headers: {
133
+ ...(payload ? { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) } : {}),
134
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
135
+ ...headers,
136
+ },
137
+ }, (res) => {
138
+ let data = '';
139
+ res.on('data', chunk => { data += chunk; });
140
+ res.on('end', () => {
141
+ try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
142
+ catch { resolve({ status: res.statusCode, body: data }); }
143
+ });
144
+ });
145
+ req.on('error', reject);
146
+ if (payload) req.write(payload);
147
+ req.end();
148
+ });
149
+ }
150
+
151
+ // ── Project seeding helpers ──
152
+ function seedRoot(prefix) {
153
+ const root = makeHarnessTempRoot(prefix);
154
+ fs.mkdirSync(path.join(root, 'Harness', 'tasks'), { recursive: true });
155
+ fs.mkdirSync(path.join(root, 'Harness', 'a2a'), { recursive: true });
156
+ return root;
157
+ }
158
+
159
+ function seedGraph(root, nodes, edges = []) {
160
+ writeWorkflowGraphMap(root, {
161
+ schemaVersion: 1,
162
+ version: 1,
163
+ nodes,
164
+ edges,
165
+ capsuleDockLinks: [],
166
+ positions: Object.fromEntries(nodes.map(node => [node.nodeId, { x: 0, y: 0 }])),
167
+ });
168
+ }
169
+
170
+ function seedAgent(root, { nodeId, sessionId, agentKind = 'subagent', role = 'Agent', runtime = 'claude', status = 'running', parentAgentId = null, parentNodeId = null }) {
171
+ const graph = loadWorkflowGraphMap(root);
172
+ seedGraph(root, [
173
+ ...(graph.nodes || []).filter(node => (node.nodeId || node.id) !== nodeId),
174
+ {
175
+ nodeId,
176
+ sessionId,
177
+ kind: 'terminal-session',
178
+ runtime,
179
+ agentKind,
180
+ role,
181
+ label: role,
182
+ status,
183
+ ...(parentAgentId ? { parentAgentId } : {}),
184
+ ...(parentNodeId ? { parentNodeId } : {}),
185
+ },
186
+ ]);
187
+ persistSession(root, {
188
+ sessionId,
189
+ graphNodeId: nodeId,
190
+ runtime,
191
+ agentKind,
192
+ role,
193
+ status,
194
+ attachMode: true,
195
+ taskId: null,
196
+ ...(parentAgentId ? { parentAgentId } : {}),
197
+ ...(parentNodeId ? { parentNodeId } : {}),
198
+ });
199
+ }
200
+
201
+ async function waitFor(fn, { timeout = 5000, step = 25 } = {}) {
202
+ const deadline = Date.now() + timeout;
203
+ let last;
204
+ while (Date.now() < deadline) {
205
+ last = await fn();
206
+ if (last) return last;
207
+ await new Promise(resolve => setTimeout(resolve, step));
208
+ }
209
+ throw new Error(`waitFor timed out; last=${JSON.stringify(last)}`);
210
+ }
211
+
212
+ function spawnLogEntries(logPath) {
213
+ if (!fs.existsSync(logPath)) return [];
214
+ return fs.readFileSync(logPath, 'utf8')
215
+ .split(/\r?\n/)
216
+ .filter(Boolean)
217
+ .map(line => JSON.parse(line));
218
+ }
219
+
220
+ // ── Suite ──
221
+ describe('W2: depth gate, resume wiring, dock actions, updateEdge route', () => {
222
+ let root;
223
+ let registry;
224
+ let server;
225
+ let baseUrl;
226
+ const registeredPties = [];
227
+
228
+ beforeEach(async () => {
229
+ recorder.reset();
230
+ root = seedRoot('wf-drd-');
231
+ registry = new SessionRegistry();
232
+ const started = await startServer({
233
+ projectRoot: root,
234
+ host: '127.0.0.1',
235
+ port: 0,
236
+ sessionRegistry: registry,
237
+ eventsWs: false,
238
+ });
239
+ server = started.server;
240
+ baseUrl = `http://127.0.0.1:${started.port}`;
241
+ });
242
+
243
+ afterEach(async () => {
244
+ for (const sessionId of registeredPties.splice(0)) {
245
+ unregisterPtyProcess(sessionId);
246
+ }
247
+ if (server) {
248
+ await stopServer(server);
249
+ server = null;
250
+ }
251
+ if (root) {
252
+ fs.rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
253
+ root = null;
254
+ }
255
+ });
256
+
257
+ // ══════════════════════════════════════════════════════════════════
258
+ // P1 — Depth-gated canvas-agent spawning
259
+ // ══════════════════════════════════════════════════════════════════
260
+ describe('P1 depth-gated canvas-agent spawning (POST /api/sessions)', () => {
261
+ it('D1 - a root actor (no parentAgentId) may spawn a canvas agent', async () => {
262
+ seedAgent(root, { nodeId: 'd-main', sessionId: 'session-d-main', agentKind: 'main', role: 'Main Agent' });
263
+ const res = await jsonRequest(baseUrl, '/api/sessions', {
264
+ method: 'POST',
265
+ body: {
266
+ runtime: 'claude',
267
+ agentKind: 'subagent',
268
+ role: 'Subagent',
269
+ parentAgentId: 'session-d-main',
270
+ parentNodeId: 'd-main',
271
+ deferPtySpawn: true,
272
+ attachGraphNode: true,
273
+ },
274
+ });
275
+ assert.equal(res.status, 201, JSON.stringify(res.body));
276
+ assert.ok(res.body.sessionId, 'session must be created');
277
+ });
278
+
279
+ it('D2 - a depth-1 actor (has parentAgentId) is rejected with 403 DEPTH_LIMIT', async () => {
280
+ seedAgent(root, { nodeId: 'd-main', sessionId: 'session-d-main', agentKind: 'main', role: 'Main Agent' });
281
+ seedAgent(root, {
282
+ nodeId: 'd-sub',
283
+ sessionId: 'session-d-sub',
284
+ agentKind: 'subagent',
285
+ role: 'Subagent',
286
+ parentAgentId: 'session-d-main',
287
+ });
288
+ const res = await jsonRequest(baseUrl, '/api/sessions', {
289
+ method: 'POST',
290
+ body: {
291
+ runtime: 'claude',
292
+ agentKind: 'subagent',
293
+ role: 'Subagent',
294
+ parentAgentId: 'session-d-sub',
295
+ parentNodeId: 'd-sub',
296
+ deferPtySpawn: true,
297
+ attachGraphNode: true,
298
+ },
299
+ });
300
+ assert.equal(res.status, 403, JSON.stringify(res.body));
301
+ assert.equal(res.body.error.code, 'DEPTH_LIMIT');
302
+ assert.match(res.body.error.message, /Only the root agent can spawn canvas agents/);
303
+ // No session must have been created.
304
+ assert.equal(registry.getAll().length, 0, 'rejected spawn must not create a session');
305
+ });
306
+
307
+ it('D3 - anonymous create (no actor identity) stays allowed', async () => {
308
+ const res = await jsonRequest(baseUrl, '/api/sessions', {
309
+ method: 'POST',
310
+ body: {
311
+ runtime: 'claude',
312
+ agentKind: 'subagent',
313
+ role: 'Subagent',
314
+ deferPtySpawn: true,
315
+ attachGraphNode: true,
316
+ },
317
+ });
318
+ assert.equal(res.status, 201, JSON.stringify(res.body));
319
+ assert.ok(res.body.sessionId, 'anonymous session must be created');
320
+ });
321
+
322
+ it('D4 - a depth-1 actor identified via headers is rejected the same way', async () => {
323
+ seedAgent(root, { nodeId: 'd-main', sessionId: 'session-d-main', agentKind: 'main', role: 'Main Agent' });
324
+ seedAgent(root, {
325
+ nodeId: 'd-sub',
326
+ sessionId: 'session-d-sub',
327
+ agentKind: 'subagent',
328
+ role: 'Subagent',
329
+ parentAgentId: 'session-d-main',
330
+ });
331
+ const res = await jsonRequest(baseUrl, '/api/sessions', {
332
+ method: 'POST',
333
+ body: { runtime: 'claude', agentKind: 'subagent', deferPtySpawn: true, attachGraphNode: true },
334
+ headers: {
335
+ 'x-harness-session-id': 'session-d-sub',
336
+ 'x-harness-workflow-node-id': 'd-sub',
337
+ },
338
+ });
339
+ assert.equal(res.status, 403, JSON.stringify(res.body));
340
+ assert.equal(res.body.error.code, 'DEPTH_LIMIT');
341
+ });
342
+ });
343
+
344
+ // ══════════════════════════════════════════════════════════════════
345
+ // P2 — Resume wiring (agent.start / agent.restart)
346
+ // ══════════════════════════════════════════════════════════════════
347
+ describe('P2 resume wiring (agent.start / agent.restart)', () => {
348
+ async function startNode(nodeId, body = {}) {
349
+ return jsonRequest(baseUrl, `/api/workflow/nodes/${encodeURIComponent(nodeId)}/actions/agent.start`, {
350
+ method: 'POST',
351
+ body: { deferPtySpawn: true, ...body },
352
+ });
353
+ }
354
+
355
+ it('R1 - start with a persisted previous session defaults to resume with the runtime args', async () => {
356
+ seedAgent(root, { nodeId: 'r1-main', sessionId: 'r1-s1', runtime: 'claude', agentKind: 'main', role: 'Main Agent', status: 'exited' });
357
+ const res = await startNode('r1-main');
358
+ assert.equal(res.status, 200, JSON.stringify(res.body));
359
+ assert.equal(res.body.result.alreadyRunning, false);
360
+ assert.equal(res.body.result.resumeUsed, true);
361
+ assert.deepEqual(res.body.result.resumeArgs, ['--resume', 'r1-s1'], 'claude resume args must target the previous session');
362
+ });
363
+
364
+ it('R2 - resume:false opts out of resume args', async () => {
365
+ seedAgent(root, { nodeId: 'r2-main', sessionId: 'r2-s1', runtime: 'claude', agentKind: 'main', role: 'Main Agent', status: 'exited' });
366
+ const res = await startNode('r2-main', { resume: false });
367
+ assert.equal(res.status, 200, JSON.stringify(res.body));
368
+ assert.equal(res.body.result.resumeUsed, false);
369
+ assert.deepEqual(res.body.result.resumeArgs, []);
370
+ });
371
+
372
+ it('R3 - a node whose bound session never ran does not resume by default', async () => {
373
+ seedAgent(root, { nodeId: 'r3-main', sessionId: 'r3-s1', runtime: 'claude', agentKind: 'main', role: 'Main Agent', status: 'blocked' });
374
+ const res = await startNode('r3-main');
375
+ assert.equal(res.status, 200, JSON.stringify(res.body));
376
+ assert.equal(res.body.result.resumeUsed, false);
377
+ assert.deepEqual(res.body.result.resumeArgs, []);
378
+ });
379
+
380
+ it('R4 - restart defaults to resume=true and carries the replaced session id', async () => {
381
+ seedAgent(root, { nodeId: 'r4-main', sessionId: 'r4-s1', runtime: 'claude', agentKind: 'main', role: 'Main Agent', status: 'running' });
382
+ const res = await jsonRequest(baseUrl, `/api/workflow/nodes/${encodeURIComponent('r4-main')}/actions/agent.restart`, {
383
+ method: 'POST',
384
+ body: { deferPtySpawn: true },
385
+ });
386
+ assert.equal(res.status, 200, JSON.stringify(res.body));
387
+ assert.equal(res.body.result.resumeUsed, true);
388
+ assert.deepEqual(res.body.result.resumeArgs, ['--resume', 'r4-s1']);
389
+ assert.equal(res.body.result.previousSessionId, 'r4-s1');
390
+ });
391
+
392
+ it('R5 - end-to-end: agent.start spawn receives the resume args (helper server subprocess)', async () => {
393
+ const helperRoot = seedRoot('wf-drd-r5-');
394
+ seedAgent(helperRoot, { nodeId: 'r5-main', sessionId: 'r5-s1', runtime: 'codex', agentKind: 'main', role: 'Main Agent', status: 'exited' });
395
+ const spawnLog = path.join(helperRoot, 'spawn.log');
396
+ const helperPath = path.join(helperRoot, 'helper-server.mjs');
397
+ const helperSource = [
398
+ '// Helper server subprocess: real server.mjs with the pty fixture, so',
399
+ '// the agent.start spawn branch runs (the node --test runner process',
400
+ '// auto-defers PTY spawns).',
401
+ 'import { registerHooks } from "node:module";',
402
+ 'import { pathToFileURL } from "node:url";',
403
+ 'registerHooks({',
404
+ ' resolve(specifier, context, nextResolve) {',
405
+ ' if (specifier === "./pty-adapter.mjs" && String(context.parentURL || "").endsWith("/src/wf-ui-server/server.mjs")) {',
406
+ ' return { url: pathToFileURL(process.env.WF_TEST_FIXTURE_PATH).href, shortCircuit: true };',
407
+ ' }',
408
+ ' return nextResolve(specifier, context);',
409
+ ' },',
410
+ '});',
411
+ 'const { startServer } = await import(process.env.WF_TEST_SERVER_URL);',
412
+ 'const { SessionRegistry } = await import(process.env.WF_TEST_REGISTRY_URL);',
413
+ 'const started = await startServer({',
414
+ ' projectRoot: process.env.WF_TEST_PROJECT_ROOT,',
415
+ ' host: "127.0.0.1",',
416
+ ' port: 0,',
417
+ ' sessionRegistry: new SessionRegistry(),',
418
+ ' eventsWs: false,',
419
+ '});',
420
+ 'process.stdout.write(JSON.stringify({ port: started.port }) + "\\n");',
421
+ 'setInterval(() => {}, 1000);',
422
+ ].join('\n');
423
+ fs.writeFileSync(helperPath, helperSource, 'utf8');
424
+
425
+ const child = spawn(process.execPath, [helperPath], {
426
+ cwd: helperRoot,
427
+ env: {
428
+ ...process.env,
429
+ WF_TEST_FIXTURE_PATH: fixturePath,
430
+ WF_TEST_SERVER_URL: pathToFileURL(path.join(process.cwd(), 'src', 'wf-ui-server', 'server.mjs')).href,
431
+ WF_TEST_REGISTRY_URL: pathToFileURL(path.join(process.cwd(), 'src', 'wf-ui-server', 'session-registry.mjs')).href,
432
+ WF_TEST_PROJECT_ROOT: helperRoot,
433
+ WF_TEST_SPAWN_LOG: spawnLog,
434
+ },
435
+ windowsHide: true,
436
+ stdio: ['ignore', 'pipe', 'pipe'],
437
+ });
438
+ let stdout = '';
439
+ let stderr = '';
440
+ child.stdout.on('data', chunk => { stdout += chunk; });
441
+ child.stderr.on('data', chunk => { stderr += chunk; });
442
+ let helperBaseUrl = null;
443
+ try {
444
+ helperBaseUrl = await waitFor(async () => {
445
+ const line = String(stdout).split(/\r?\n/).find(text => text.startsWith('{'));
446
+ if (!line) return null;
447
+ const parsed = JSON.parse(line);
448
+ return `http://127.0.0.1:${parsed.port}`;
449
+ }, { timeout: 10000 });
450
+ } catch (e) {
451
+ child.kill();
452
+ throw new Error(`helper server failed to start: ${e.message}; stderr=${stderr}`);
453
+ }
454
+
455
+ try {
456
+ const res = await jsonRequest(helperBaseUrl, `/api/workflow/nodes/${encodeURIComponent('r5-main')}/actions/agent.start`, {
457
+ method: 'POST',
458
+ body: {},
459
+ });
460
+ assert.equal(res.status, 200, JSON.stringify(res.body));
461
+ assert.equal(res.body.result.resumeUsed, true);
462
+ assert.deepEqual(res.body.result.resumeArgs, ['resume', 'r5-s1'], 'codex resume args must target the previous session');
463
+
464
+ // The real spawn branch must have received the same args.
465
+ const entries = await waitFor(() => {
466
+ const rows = spawnLogEntries(spawnLog);
467
+ return rows.length > 0 ? rows : null;
468
+ });
469
+ assert.equal(entries.length, 1, `exactly one spawn expected: ${JSON.stringify(entries)}`);
470
+ assert.equal(entries[0].sessionId, res.body.result.sessionId);
471
+ assert.equal(entries[0].runtime, 'codex');
472
+ assert.deepEqual(entries[0].commandArgs, ['resume', 'r5-s1'], 'spawnPty commandArgs must carry the resume args');
473
+ } finally {
474
+ child.kill();
475
+ await new Promise(resolve => child.once('close', resolve));
476
+ try {
477
+ fs.rmSync(helperRoot, { recursive: true, force: true, maxRetries: 15, retryDelay: 200 });
478
+ } catch (e) {
479
+ // Windows may briefly hold session files after server close; the
480
+ // temp root lives under Harness/.temp and is reaped on demand.
481
+ if (process.platform === 'win32' && ['EPERM', 'EBUSY', 'ENOTEMPTY'].includes(e?.code)) return;
482
+ throw e;
483
+ }
484
+ }
485
+ });
486
+
487
+ it('R6 - createRuntimeSession threads resumeArgs into spawnPty commandArgs (default empty)', async () => {
488
+ // With resumeArgs in the payload, the spawn receives exactly those args.
489
+ const withArgs = await jsonRequest(baseUrl, '/api/sessions', {
490
+ method: 'POST',
491
+ body: { runtime: 'codex', agentKind: 'subagent', attachGraphNode: false, resumeArgs: ['resume', 'sess-abc'] },
492
+ });
493
+ assert.equal(withArgs.status, 201, JSON.stringify(withArgs.body));
494
+ const spawned = recorder.spawnOpts.find(entry => entry.sessionId === withArgs.body.sessionId);
495
+ assert.ok(spawned, 'spawnPty must have been reached');
496
+ assert.deepEqual(spawned.commandArgs, ['resume', 'sess-abc']);
497
+
498
+ // Without resumeArgs the default is empty (unchanged behavior).
499
+ recorder.reset();
500
+ const noArgs = await jsonRequest(baseUrl, '/api/sessions', {
501
+ method: 'POST',
502
+ body: { runtime: 'codex', agentKind: 'subagent', attachGraphNode: false },
503
+ });
504
+ assert.equal(noArgs.status, 201, JSON.stringify(noArgs.body));
505
+ const plain = recorder.spawnOpts.find(entry => entry.sessionId === noArgs.body.sessionId);
506
+ assert.ok(plain, 'spawnPty must have been reached');
507
+ assert.deepEqual(plain.commandArgs, []);
508
+ });
509
+ });
510
+
511
+ // ══════════════════════════════════════════════════════════════════
512
+ // P3 — Dock typed actions
513
+ // ══════════════════════════════════════════════════════════════════
514
+ describe('P3 dock typed actions (agent.attachDock / setDockSide / detachDock)', () => {
515
+ function seedDockGraph() {
516
+ seedAgent(root, { nodeId: 'k-main', sessionId: 'session-k-main', agentKind: 'main', role: 'Main Agent' });
517
+ seedAgent(root, { nodeId: 'k-b', sessionId: 'session-k-b', agentKind: 'subagent', role: 'Agent B' });
518
+ seedAgent(root, { nodeId: 'k-c', sessionId: 'session-k-c', agentKind: 'subagent', role: 'Agent C' });
519
+ const connected = connectNodes(root, { from: 'k-main', to: 'k-b', sourceHandle: 'right', targetHandle: 'left' });
520
+ return connected.edge;
521
+ }
522
+
523
+ function dockAction(action, body) {
524
+ return jsonRequest(baseUrl, `/api/workflow/nodes/${encodeURIComponent('k-main')}/actions/agent.${action}`, {
525
+ method: 'POST',
526
+ body: { actorNodeId: 'k-main', ...body },
527
+ });
528
+ }
529
+
530
+ function dockLinks() {
531
+ return loadWorkflowGraphMap(root).capsuleDockLinks || [];
532
+ }
533
+
534
+ it('DOCK1 - attachDock creates the capsuleDockLinks entry derived from the existing edge', async () => {
535
+ const edge = seedDockGraph();
536
+ const res = await dockAction('attachDock', { anchorId: 'k-main', draggedId: 'k-b', side: 'top' });
537
+ assert.equal(res.status, 200, JSON.stringify(res.body));
538
+ assert.equal(res.body.ok, true);
539
+ assert.equal(res.body.replaced, false);
540
+ const link = res.body.dockLink;
541
+ assert.equal(link.anchorId, 'k-main');
542
+ assert.equal(link.draggedId, 'k-b');
543
+ assert.equal(link.side, 'top');
544
+ assert.deepEqual(link.nodeIds, ['k-b', 'k-main']);
545
+ assert.deepEqual(link.edges, [{ edgeId: edge.id, retention: 'keep' }]);
546
+ assert.equal(link.connections.length, 1, 'connection must be derived from the edge');
547
+ assert.equal(link.connections[0].source, 'k-main');
548
+ assert.equal(link.connections[0].target, 'k-b');
549
+
550
+ const persisted = dockLinks();
551
+ assert.equal(persisted.length, 1, 'exactly one dock link must persist');
552
+ assert.equal(persisted[0].side, 'top');
553
+ assert.equal(persisted[0].anchorId, 'k-main');
554
+ });
555
+
556
+ it('DOCK2 - re-attaching the same pair is idempotent and updates the side', async () => {
557
+ seedDockGraph();
558
+ await dockAction('attachDock', { anchorId: 'k-main', draggedId: 'k-b', side: 'top' });
559
+ const again = await dockAction('attachDock', { anchorId: 'k-b', draggedId: 'k-main', side: 'left' });
560
+ assert.equal(again.status, 200, JSON.stringify(again.body));
561
+ assert.equal(again.body.replaced, true, 're-attach must replace the existing entry');
562
+ assert.equal(again.body.dockLink.side, 'left');
563
+ const persisted = dockLinks();
564
+ assert.equal(persisted.length, 1, 're-attach must not duplicate the entry');
565
+ assert.equal(persisted[0].side, 'left');
566
+ });
567
+
568
+ it('DOCK3 - setDockSide updates the side of an existing dock link', async () => {
569
+ seedDockGraph();
570
+ await dockAction('attachDock', { anchorId: 'k-main', draggedId: 'k-b', side: 'top' });
571
+ const res = await dockAction('setDockSide', { anchorId: 'k-main', draggedId: 'k-b', side: 'bottom' });
572
+ assert.equal(res.status, 200, JSON.stringify(res.body));
573
+ assert.equal(res.body.dockLink.side, 'bottom');
574
+ assert.equal(dockLinks()[0].side, 'bottom');
575
+ });
576
+
577
+ it('DOCK4 - setDockSide on a missing pair returns 404 DOCK_NOT_FOUND', async () => {
578
+ seedDockGraph();
579
+ const res = await dockAction('setDockSide', { anchorId: 'k-main', draggedId: 'k-c', side: 'right' });
580
+ assert.equal(res.status, 404, JSON.stringify(res.body));
581
+ assert.equal(res.body.error.code, 'DOCK_NOT_FOUND');
582
+ });
583
+
584
+ it('DOCK5 - detachDock removes the entry and is idempotent (removed:false)', async () => {
585
+ seedDockGraph();
586
+ await dockAction('attachDock', { anchorId: 'k-main', draggedId: 'k-b', side: 'top' });
587
+ assert.equal(dockLinks().length, 1);
588
+
589
+ const detached = await dockAction('detachDock', { anchorId: 'k-main', draggedId: 'k-b' });
590
+ assert.equal(detached.status, 200, JSON.stringify(detached.body));
591
+ assert.equal(detached.body.removed, true);
592
+ assert.equal(dockLinks().length, 0, 'detach must remove the entry');
593
+
594
+ const again = await dockAction('detachDock', { anchorId: 'k-main', draggedId: 'k-b' });
595
+ assert.equal(again.status, 200, JSON.stringify(again.body));
596
+ assert.equal(again.body.removed, false, 'missing entry must be idempotent');
597
+
598
+ // By dock id too.
599
+ await dockAction('attachDock', { anchorId: 'k-main', draggedId: 'k-b', side: 'right' });
600
+ const link = dockLinks()[0];
601
+ const byId = await dockAction('detachDock', { dockId: link.id });
602
+ assert.equal(byId.status, 200, JSON.stringify(byId.body));
603
+ assert.equal(byId.body.removed, true);
604
+ assert.equal(dockLinks().length, 0);
605
+ });
606
+
607
+ it('DOCK6 - attachDock without an existing edge writes empty edges/connections', async () => {
608
+ seedDockGraph();
609
+ const res = await dockAction('attachDock', { anchorId: 'k-main', draggedId: 'k-c', side: 'right' });
610
+ assert.equal(res.status, 200, JSON.stringify(res.body));
611
+ assert.deepEqual(res.body.dockLink.edges, []);
612
+ assert.deepEqual(res.body.dockLink.connections, []);
613
+ assert.equal(res.body.dockLink.side, 'right');
614
+ });
615
+ });
616
+
617
+ // ══════════════════════════════════════════════════════════════════
618
+ // P4 — updateEdge HTTP route (agent.updateEdge)
619
+ // ══════════════════════════════════════════════════════════════════
620
+ describe('P4 updateEdge HTTP route (agent.updateEdge)', () => {
621
+ function seedEdgeGraph() {
622
+ seedAgent(root, { nodeId: 'u-main', sessionId: 'session-u-main', agentKind: 'main', role: 'Main Agent' });
623
+ seedAgent(root, { nodeId: 'u-b', sessionId: 'session-u-b', agentKind: 'subagent', role: 'Agent B' });
624
+ const connected = connectNodes(root, { from: 'u-main', to: 'u-b', sourceHandle: 'bottom', targetHandle: 'top' });
625
+ return connected.edge;
626
+ }
627
+
628
+ function edgeAction(body) {
629
+ return jsonRequest(baseUrl, `/api/workflow/nodes/${encodeURIComponent('u-main')}/actions/agent.updateEdge`, {
630
+ method: 'POST',
631
+ body: { actorNodeId: 'u-main', ...body },
632
+ });
633
+ }
634
+
635
+ it('U1 - updates relation/handles through the route and persists the patch', async () => {
636
+ const edge = seedEdgeGraph();
637
+ const res = await edgeAction({ edgeId: edge.id, relation: 'reports', sourceHandle: 'right' });
638
+ assert.equal(res.status, 200, JSON.stringify(res.body));
639
+ assert.equal(res.body.ok, true);
640
+ assert.equal(res.body.edge.id, edge.id);
641
+ assert.equal(res.body.edge.relation, 'reports');
642
+ assert.equal(res.body.edge.sourceHandle, 'right');
643
+ assert.equal(res.body.edge.targetHandle, 'top', 'fields outside the patch must stay untouched');
644
+
645
+ const persisted = loadWorkflowGraphMap(root).edges.find(item => item.id === edge.id);
646
+ assert.equal(persisted.relation, 'reports');
647
+ assert.equal(persisted.sourceHandle, 'right');
648
+ });
649
+
650
+ it('U2 - an empty patch is rejected with 400 EMPTY_UPDATE', async () => {
651
+ const edge = seedEdgeGraph();
652
+ const res = await edgeAction({ edgeId: edge.id });
653
+ assert.equal(res.status, 400, JSON.stringify(res.body));
654
+ assert.equal(res.body.error.code, 'EMPTY_UPDATE');
655
+ });
656
+
657
+ it('U3 - an unknown edge returns 404 EDGE_NOT_FOUND', async () => {
658
+ seedEdgeGraph();
659
+ const res = await edgeAction({ edgeId: 'edge-unknown', relation: 'reports' });
660
+ assert.equal(res.status, 404, JSON.stringify(res.body));
661
+ assert.equal(res.body.error.code, 'EDGE_NOT_FOUND');
662
+ assert.match(res.body.error.message, /edge-unknown/);
663
+ });
664
+ });
665
+ });
666
+
667
+ // ── CLI mapping tests (real subprocess vs recording server) ─────────────────
668
+ describe('W2 CLI mapping (node-map dock actions, workflow-node-action --resume)', () => {
669
+ function runCli(cwd, args) {
670
+ return new Promise((resolve, reject) => {
671
+ const script = path.join(cwd, 'Harness', 'scripts', 'wf-ui-control.mjs');
672
+ const child = spawn(process.execPath, [script, ...args], {
673
+ cwd,
674
+ env: { ...process.env, HARNESS_AGENT_KIND: 'main' },
675
+ windowsHide: true,
676
+ stdio: ['ignore', 'pipe', 'pipe'],
677
+ });
678
+ let stdout = '';
679
+ let stderr = '';
680
+ const timer = setTimeout(() => {
681
+ child.kill();
682
+ reject(new Error(`wf-ui-control timeout: ${args.join(' ')}`));
683
+ }, 15000);
684
+ child.stdout.on('data', chunk => { stdout += chunk; });
685
+ child.stderr.on('data', chunk => { stderr += chunk; });
686
+ child.on('error', reject);
687
+ child.on('close', (status) => {
688
+ clearTimeout(timer);
689
+ let parsed = stdout.trim();
690
+ try { parsed = JSON.parse(parsed); } catch { /* raw text */ }
691
+ resolve({ status, stdout: parsed, stderr });
692
+ });
693
+ });
694
+ }
695
+
696
+ async function withRecordingServer(fn) {
697
+ const root = makeHarnessTempRoot('wf-drd-cli-');
698
+ const dest = path.join(root, 'Harness', 'scripts', 'wf-ui-control.mjs');
699
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
700
+ fs.copyFileSync(path.join(process.cwd(), 'Harness', 'scripts', 'wf-ui-control.mjs'), dest);
701
+
702
+ const requests = [];
703
+ const server = http.createServer((req, res) => {
704
+ let data = '';
705
+ req.on('data', chunk => { data += chunk; });
706
+ req.on('end', () => {
707
+ requests.push({ method: req.method, url: req.url, body: data ? JSON.parse(data) : null });
708
+ res.writeHead(200, { 'Content-Type': 'application/json' });
709
+ res.end(JSON.stringify({ ok: true, captured: true }));
710
+ });
711
+ });
712
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
713
+ const { port } = server.address();
714
+ const url = `http://127.0.0.1:${port}`;
715
+ const baseArgs = ['node-map', '--actor', 'main-node', '--url', url, '--token', 'test-token', '--project', root];
716
+ try {
717
+ await fn({ root, baseArgs, requests, run: (args) => runCli(root, args) });
718
+ } finally {
719
+ await new Promise(resolve => server.close(resolve));
720
+ fs.rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
721
+ }
722
+ }
723
+
724
+ it('C1 - node-map attachDock sends {actorNodeId, anchorId, draggedId, side} to the graph-actions endpoint', async () => {
725
+ await withRecordingServer(async ({ baseArgs, requests, run }) => {
726
+ const result = await run([
727
+ ...baseArgs,
728
+ '--action', 'attachDock',
729
+ '--anchor', 'main-node',
730
+ '--dragged', 'worker-node',
731
+ '--side', 'top',
732
+ ]);
733
+ assert.equal(result.status, 0, `CLI failed: ${result.stderr}`);
734
+ assert.equal(requests.length, 1);
735
+ const captured = requests[0];
736
+ assert.equal(captured.method, 'POST');
737
+ assert.match(captured.url, /\/api\/workflow\/nodes\/main-node\/actions\/agent\.attachDock$/);
738
+ assert.equal(captured.body.actorNodeId, 'main-node');
739
+ assert.equal(captured.body.anchorId, 'main-node');
740
+ assert.equal(captured.body.draggedId, 'worker-node');
741
+ assert.equal(captured.body.side, 'top');
742
+ });
743
+ });
744
+
745
+ it('C2 - node-map setDockSide and detachDock map their payloads; missing --anchor fails client-side', async () => {
746
+ await withRecordingServer(async ({ baseArgs, requests, run }) => {
747
+ const setSide = await run([
748
+ ...baseArgs,
749
+ '--action', 'setDockSide',
750
+ '--anchor', 'main-node',
751
+ '--dragged', 'worker-node',
752
+ '--side', 'left',
753
+ ]);
754
+ assert.equal(setSide.status, 0, `CLI failed: ${setSide.stderr}`);
755
+ assert.match(requests[0].url, /agent\.setDockSide$/);
756
+ assert.equal(requests[0].body.anchorId, 'main-node');
757
+ assert.equal(requests[0].body.draggedId, 'worker-node');
758
+ assert.equal(requests[0].body.side, 'left');
759
+
760
+ const detach = await run([
761
+ ...baseArgs,
762
+ '--action', 'detachDock',
763
+ '--anchor', 'main-node',
764
+ '--dragged', 'worker-node',
765
+ ]);
766
+ assert.equal(detach.status, 0, `CLI failed: ${detach.stderr}`);
767
+ assert.match(requests[1].url, /agent\.detachDock$/);
768
+ assert.equal(requests[1].body.anchorId, 'main-node');
769
+ assert.equal(requests[1].body.draggedId, 'worker-node');
770
+
771
+ // Missing anchor/dragged must fail client-side without a request.
772
+ const missing = await run([...baseArgs, '--action', 'attachDock']);
773
+ assert.notEqual(missing.status, 0, 'missing anchor must exit non-zero');
774
+ assert.match(String(missing.stderr), /--anchor/);
775
+ assert.equal(requests.length, 2, 'failed mapping must not reach the server');
776
+
777
+ // setDockSide without --side must fail client-side.
778
+ const noSide = await run([
779
+ ...baseArgs,
780
+ '--action', 'setDockSide',
781
+ '--anchor', 'main-node',
782
+ '--dragged', 'worker-node',
783
+ ]);
784
+ assert.notEqual(noSide.status, 0, 'missing side must exit non-zero');
785
+ assert.match(String(noSide.stderr), /--side/);
786
+ assert.equal(requests.length, 2);
787
+ });
788
+ });
789
+
790
+ it('C3 - workflow-node-action --resume lands the resume boolean on agent.start/agent.restart', async () => {
791
+ const root = makeHarnessTempRoot('wf-drd-cli-resume-');
792
+ const dest = path.join(root, 'Harness', 'scripts', 'wf-ui-control.mjs');
793
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
794
+ fs.copyFileSync(path.join(process.cwd(), 'Harness', 'scripts', 'wf-ui-control.mjs'), dest);
795
+
796
+ const requests = [];
797
+ const server = http.createServer((req, res) => {
798
+ let data = '';
799
+ req.on('data', chunk => { data += chunk; });
800
+ req.on('end', () => {
801
+ requests.push({ method: req.method, url: req.url, body: data ? JSON.parse(data) : null });
802
+ res.writeHead(200, { 'Content-Type': 'application/json' });
803
+ res.end(JSON.stringify({ ok: true, captured: true }));
804
+ });
805
+ });
806
+ await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
807
+ const { port } = server.address();
808
+ const url = `http://127.0.0.1:${port}`;
809
+ const baseArgs = ['--url', url, '--token', 'test-token', '--project', root];
810
+
811
+ try {
812
+ const withResume = await runCli(root, [
813
+ 'workflow-node-action',
814
+ ...baseArgs,
815
+ '--node', 'n1',
816
+ '--action', 'agent.start',
817
+ '--resume', 'true',
818
+ ]);
819
+ assert.equal(withResume.status, 0, `CLI failed: ${withResume.stderr}`);
820
+ assert.equal(requests.length, 1);
821
+ assert.equal(requests[0].body.resume, true);
822
+
823
+ const withoutResume = await runCli(root, [
824
+ 'workflow-node-action',
825
+ ...baseArgs,
826
+ '--node', 'n1',
827
+ '--action', 'agent.restart',
828
+ ]);
829
+ assert.equal(withoutResume.status, 0, `CLI failed: ${withoutResume.stderr}`);
830
+ assert.equal(requests[1].body.resume, undefined, 'no --resume flag must leave the payload untouched');
831
+
832
+ const resumeOff = await runCli(root, [
833
+ 'workflow-node-action',
834
+ ...baseArgs,
835
+ '--node', 'n1',
836
+ '--action', 'agent.start',
837
+ '--resume', 'false',
838
+ ]);
839
+ assert.equal(resumeOff.status, 0, `CLI failed: ${resumeOff.stderr}`);
840
+ assert.equal(requests[2].body.resume, false);
841
+ } finally {
842
+ await new Promise(resolve => server.close(resolve));
843
+ fs.rmSync(root, { recursive: true, force: true, maxRetries: 10, retryDelay: 100 });
844
+ }
845
+ });
846
+ });