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
@@ -1,5 +1,4 @@
1
1
  import crypto from 'node:crypto';
2
- import { validateToken } from './token.mjs';
3
2
 
4
3
  const KEEPALIVE_INTERVAL = 15000;
5
4
  const PING_TIMEOUT = 5000;
@@ -11,6 +10,13 @@ const WS_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
11
10
  /** @type {Map<string, object>} */
12
11
  const ptyRegistry = new Map();
13
12
 
13
+ // ── Revived session records ──
14
+ // Sessions revived from disk (backend restarted while the PTY session
15
+ // survived) are keyed here with minimal info so WS clients can reattach
16
+ // even though the in-memory registry does not know them.
17
+ /** @type {Map<string, object>} */
18
+ const revivedSessions = new Map();
19
+
14
20
  /**
15
21
  * Register a PTY process for a session so ws-terminal can write/resize/kill it.
16
22
  * @param {string} sessionId
@@ -43,6 +49,77 @@ export function killPtyProcess(sessionId) {
43
49
  return true;
44
50
  }
45
51
 
52
+ // ── Graceful stop ──
53
+ // claude-code only persists its conversation transcript on graceful exit
54
+ // (the /exit TUI path writes ~/.claude/projects/<encoded-cwd>/<uuid>.jsonl);
55
+ // a hard PTY kill loses it and --resume <uuid> then fails with "No
56
+ // conversation found". codex persists its fs rollout continuously and
57
+ // opencode writes its SQLite session rows during the session, so a hard kill
58
+ // is safe for both — graceful exit applies to claude/cc ONLY.
59
+ const GRACEFUL_STOP_TIMEOUT_MS = 3000;
60
+ const GRACEFUL_STOP_RUNTIMES = new Set(['claude', 'cc']);
61
+
62
+ /**
63
+ * Resolve true when the registered PTY process exits (or when nothing is
64
+ * attached), false when it is still alive after timeoutMs.
65
+ * @param {string} sessionId
66
+ * @param {number} timeoutMs
67
+ * @returns {Promise<boolean>}
68
+ */
69
+ function waitForPtyExit(sessionId, timeoutMs) {
70
+ return new Promise((resolve) => {
71
+ const ptyProcess = ptyRegistry.get(sessionId);
72
+ if (!ptyProcess) return resolve(true);
73
+ if (typeof ptyProcess.onExit !== 'function') return resolve(false);
74
+ const timer = setTimeout(() => resolve(false), timeoutMs);
75
+ ptyProcess.onExit(() => {
76
+ clearTimeout(timer);
77
+ resolve(true);
78
+ });
79
+ });
80
+ }
81
+
82
+ /**
83
+ * Stop a PTY process, preferring a graceful runtime exit so the runtime can
84
+ * persist its session state (claude/cc transcript files).
85
+ *
86
+ * claude/cc sequence: write `/exit\r` and wait up to GRACEFUL_STOP_TIMEOUT_MS;
87
+ * if the process is still alive write Ctrl+C (`\x03`) and wait again; if it
88
+ * still has not exited, fall back to killPtyProcess. Total wait is bounded
89
+ * (2 × 3s) so the stop API contract — the process always ends within a
90
+ * bounded time — is preserved. All other runtimes (codex, opencode, …) go
91
+ * straight to killPtyProcess.
92
+ *
93
+ * Never rejects; the hard-kill fallback guarantees termination.
94
+ * @param {string} sessionId
95
+ * @param {{ runtime?: string }} [options]
96
+ * @returns {Promise<{ killed: boolean, graceful: boolean }>}
97
+ */
98
+ export function gracefulStopPty(sessionId, { runtime } = {}) {
99
+ if (!GRACEFUL_STOP_RUNTIMES.has(String(runtime || '').toLowerCase())) {
100
+ return Promise.resolve({ killed: killPtyProcess(sessionId), graceful: false });
101
+ }
102
+ const ptyProcess = ptyRegistry.get(sessionId);
103
+ if (!ptyProcess || typeof ptyProcess.write !== 'function') {
104
+ return Promise.resolve({ killed: false, graceful: false });
105
+ }
106
+ return (async () => {
107
+ ptyProcess.write('/exit');
108
+ ptyProcess.write('\r');
109
+ let exited = await waitForPtyExit(sessionId, GRACEFUL_STOP_TIMEOUT_MS);
110
+ if (!exited) {
111
+ ptyProcess.write('\x03');
112
+ exited = await waitForPtyExit(sessionId, GRACEFUL_STOP_TIMEOUT_MS);
113
+ }
114
+ if (!exited) {
115
+ killPtyProcess(sessionId);
116
+ return { killed: true, graceful: false };
117
+ }
118
+ unregisterPtyProcess(sessionId);
119
+ return { killed: false, graceful: true };
120
+ })();
121
+ }
122
+
46
123
  // ── Frame helpers ──
47
124
 
48
125
  /**
@@ -162,17 +239,19 @@ function sendHttpError(sock, statusCode, message) {
162
239
  /**
163
240
  * Attach a WebSocket /ws/terminal/:sessionId endpoint to an existing http.Server.
164
241
  *
165
- * On upgrade: validates `?token=` query param, validates sessionId exists in
166
- * registry, performs WS upgrade, sends `session:state` handshake, and
242
+ * On upgrade: validates sessionId exists in registry, performs WS upgrade,
243
+ * sends `session:state` handshake, and
167
244
  * broadcasts PTY data to all WS clients for that session.
168
245
  *
169
246
  * Default mode is watch (read-only). pty:input is rejected unless
170
247
  * session.attachMode === true.
171
248
  *
172
249
  * @param {import('http').Server} httpServer
173
- * @param {string} expectedToken - The token clients must present.
250
+ * @param {string} expectedToken - Compatibility parameter; no token validation is performed.
174
251
  * @param {import('./session-registry.mjs').SessionRegistry} sessionRegistry
175
- * @param {{ keepaliveInterval?: number, pingTimeout?: number }} [options]
252
+ * @param {{ keepaliveInterval?: number, pingTimeout?: number, reviveSession?: (sessionId: string) => object|null }} [options]
253
+ * reviveSession: optional disk lookup for sessions missing from the registry
254
+ * (falls back to sessionRegistry.reviveSession when set by the server).
176
255
  * @returns {{ close: () => Promise<void>, broadcastToSession: (sessionId: string, msg: object) => void }}
177
256
  */
178
257
  export function attachTerminalWs(httpServer, expectedToken, sessionRegistry, options = {}) {
@@ -199,13 +278,13 @@ export function attachTerminalWs(httpServer, expectedToken, sessionRegistry, opt
199
278
 
200
279
  const sessionId = match[1];
201
280
 
202
- if (!validateToken(url.searchParams.get('token'), expectedToken)) {
203
- sendHttpError(socket, 401, 'Invalid or missing token');
204
- return;
281
+ // Validate session exists. Sessions not in the in-memory registry (e.g.
282
+ // the backend restarted while the PTY session survived on disk) are
283
+ // revived from the persisted session record so the WS can reattach.
284
+ let session = sessionRegistry.get(sessionId);
285
+ if (!session) {
286
+ session = reviveDiskSession(sessionId);
205
287
  }
206
-
207
- // Validate session exists
208
- const session = sessionRegistry.get(sessionId);
209
288
  if (!session) {
210
289
  sendHttpError(socket, 404, `Session "${sessionId}" not found`);
211
290
  return;
@@ -237,7 +316,7 @@ export function attachTerminalWs(httpServer, expectedToken, sessionRegistry, opt
237
316
  sessionClients.get(sessionId).add(client);
238
317
 
239
318
  // Increment wsClientCount
240
- sessionRegistry.update(sessionId, { wsClientCount: (session.wsClientCount || 0) + 1 });
319
+ adjustWsClientCount(sessionId, session, 1);
241
320
 
242
321
  // Send session:state handshake
243
322
  sendFrame(socket, JSON.stringify({
@@ -301,7 +380,7 @@ export function attachTerminalWs(httpServer, expectedToken, sessionRegistry, opt
301
380
  * @param {import('net').Socket} socket
302
381
  */
303
382
  function handleMessage(sessionId, msg, socket) {
304
- const session = sessionRegistry.get(sessionId);
383
+ const session = sessionRegistry.get(sessionId) || revivedSessions.get(sessionId);
305
384
  if (!session) return;
306
385
 
307
386
  switch (msg.type) {
@@ -309,7 +388,7 @@ export function attachTerminalWs(httpServer, expectedToken, sessionRegistry, opt
309
388
  const cols = typeof msg.cols === 'number' ? msg.cols : session.cols;
310
389
  const rows = typeof msg.rows === 'number' ? msg.rows : session.rows;
311
390
  if (cols === session.cols && rows === session.rows) break;
312
- sessionRegistry.update(sessionId, { cols, rows });
391
+ storeSessionPatch(sessionId, { cols, rows });
313
392
 
314
393
  // If a real PTY process exists, resize it
315
394
  const ptyProcess = ptyRegistry.get(sessionId);
@@ -353,7 +432,7 @@ export function attachTerminalWs(httpServer, expectedToken, sessionRegistry, opt
353
432
 
354
433
  case 'control:attach-mode': {
355
434
  const attachMode = Boolean(msg.attachMode);
356
- sessionRegistry.update(sessionId, { attachMode });
435
+ storeSessionPatch(sessionId, { attachMode });
357
436
  if (typeof options.onAttachModeChange === 'function') {
358
437
  options.onAttachModeChange(sessionRegistry.get(sessionId), attachMode);
359
438
  }
@@ -374,7 +453,7 @@ export function attachTerminalWs(httpServer, expectedToken, sessionRegistry, opt
374
453
  sessionId,
375
454
  state: 'exited',
376
455
  }));
377
- sessionRegistry.update(sessionId, { status: 'exited' });
456
+ storeSessionPatch(sessionId, { status: 'exited' });
378
457
  if (typeof options.onSessionState === 'function') {
379
458
  options.onSessionState(sessionRegistry.get(sessionId), 'exited');
380
459
  }
@@ -405,10 +484,74 @@ export function attachTerminalWs(httpServer, expectedToken, sessionRegistry, opt
405
484
  }
406
485
  }
407
486
 
408
- const session = sessionRegistry.get(sessionId);
487
+ const session = sessionRegistry.get(sessionId) || revivedSessions.get(sessionId);
409
488
  if (session) {
410
- const count = Math.max(0, (session.wsClientCount || 1) - 1);
411
- sessionRegistry.update(sessionId, { wsClientCount: count });
489
+ adjustWsClientCount(sessionId, session, -1);
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Revive a session that is not in the in-memory registry from its
495
+ * persisted record (Harness/a2a/sessions/<id>/STATE.json). The PTY process
496
+ * may still be alive after a backend restart; a WS client can reattach to
497
+ * watch it (input stays disabled — no live PTY handle). Returns the record
498
+ * or null when the session is unknown or already exited.
499
+ * @param {string} sessionId
500
+ * @returns {object|null}
501
+ */
502
+ function reviveDiskSession(sessionId) {
503
+ const cached = revivedSessions.get(sessionId);
504
+ if (cached) return cached;
505
+
506
+ const revive = (typeof options.reviveSession === 'function' && options.reviveSession)
507
+ || (typeof sessionRegistry.reviveSession === 'function' && sessionRegistry.reviveSession.bind(sessionRegistry));
508
+ if (typeof revive !== 'function') return null;
509
+
510
+ let record = null;
511
+ try {
512
+ record = revive(sessionId);
513
+ } catch {
514
+ record = null;
515
+ }
516
+ if (!record || !record.sessionId || record.status === 'exited') return null;
517
+
518
+ record = { ...record, status: record.status || 'starting' };
519
+ revivedSessions.set(sessionId, record);
520
+ return record;
521
+ }
522
+
523
+ /**
524
+ * Apply a patch to a session record, preferring the in-memory registry and
525
+ * falling back to a revived disk record.
526
+ * @param {string} sessionId
527
+ * @param {object} patch
528
+ */
529
+ function storeSessionPatch(sessionId, patch) {
530
+ if (sessionRegistry.get(sessionId)) {
531
+ sessionRegistry.update(sessionId, patch);
532
+ return;
533
+ }
534
+ const current = revivedSessions.get(sessionId);
535
+ if (current) {
536
+ revivedSessions.set(sessionId, { ...current, ...patch, updatedAt: new Date().toISOString() });
537
+ }
538
+ }
539
+
540
+ /**
541
+ * Adjust wsClientCount by delta in whichever store owns the session.
542
+ * @param {string} sessionId
543
+ * @param {object} session
544
+ * @param {number} delta
545
+ */
546
+ function adjustWsClientCount(sessionId, session, delta) {
547
+ const next = Math.max(0, (session?.wsClientCount || 0) + delta);
548
+ if (sessionRegistry.get(sessionId)) {
549
+ sessionRegistry.update(sessionId, { wsClientCount: next });
550
+ return;
551
+ }
552
+ const current = revivedSessions.get(sessionId);
553
+ if (current) {
554
+ revivedSessions.set(sessionId, { ...current, wsClientCount: next });
412
555
  }
413
556
  }
414
557
 
@@ -434,14 +577,14 @@ export function attachTerminalWs(httpServer, expectedToken, sessionRegistry, opt
434
577
  // ── keepalive timer ──
435
578
  keepaliveTimer = setInterval(() => {
436
579
  const now = Date.now();
437
- for (const [, clients] of sessionClients) {
580
+ for (const [sessionId, clients] of sessionClients) {
438
581
  for (const c of [...clients]) {
439
582
  try {
440
583
  if (c.waitingPong) {
441
584
  if (now - c.pingSentAt >= pingTimeoutMs) {
442
585
  sendCloseFrame(c.socket, 1001);
443
586
  c.socket.end();
444
- clients.delete(c);
587
+ cleanupClient(sessionId, c);
445
588
  }
446
589
  } else if (now - c.lastActivity >= keepaliveInterval) {
447
590
  sendFrame(c.socket, '', 0x9);
@@ -449,7 +592,7 @@ export function attachTerminalWs(httpServer, expectedToken, sessionRegistry, opt
449
592
  c.pingSentAt = now;
450
593
  }
451
594
  } catch {
452
- clients.delete(c);
595
+ cleanupClient(sessionId, c);
453
596
  }
454
597
  }
455
598
  }
@@ -0,0 +1,419 @@
1
+ import crypto from 'node:crypto';
2
+
3
+ const KEEPALIVE_INTERVAL = 15000;
4
+ const PING_TIMEOUT = 5000;
5
+ const WS_GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
6
+ const DEFAULT_COMMAND_TIMEOUT_MS = 8000;
7
+ const MAX_COMMAND_TIMEOUT_MS = 30000;
8
+ const DEFAULT_MAX_MESSAGE_BYTES = 1024 * 1024;
9
+
10
+ export class WfBrowserWsError extends Error {
11
+ constructor(message, { statusCode = 400, code = 'BAD_REQUEST' } = {}) {
12
+ super(message);
13
+ this.name = 'WfBrowserWsError';
14
+ this.statusCode = statusCode;
15
+ this.code = code;
16
+ }
17
+ }
18
+
19
+ function sendFrame(socket, text, opcode = 0x1) {
20
+ const data = Buffer.from(text, 'utf8');
21
+ const len = data.length;
22
+ let frame;
23
+ if (len < 126) {
24
+ frame = Buffer.alloc(2 + len);
25
+ frame[0] = 0x80 | opcode;
26
+ frame[1] = len;
27
+ data.copy(frame, 2);
28
+ } else if (len < 65536) {
29
+ frame = Buffer.alloc(4 + len);
30
+ frame[0] = 0x80 | opcode;
31
+ frame[1] = 126;
32
+ frame.writeUInt16BE(len, 2);
33
+ data.copy(frame, 4);
34
+ } else {
35
+ frame = Buffer.alloc(10 + len);
36
+ frame[0] = 0x80 | opcode;
37
+ frame[1] = 127;
38
+ frame.writeBigUInt64BE(BigInt(len), 2);
39
+ data.copy(frame, 10);
40
+ }
41
+ socket.write(frame);
42
+ }
43
+
44
+ function sendJsonFrame(socket, payload) {
45
+ sendFrame(socket, JSON.stringify(payload));
46
+ }
47
+
48
+ function sendCloseFrame(socket, code = 1000) {
49
+ const payload = Buffer.alloc(2);
50
+ payload.writeUInt16BE(code, 0);
51
+ const header = Buffer.alloc(2);
52
+ header[0] = 0x88;
53
+ header[1] = 2;
54
+ socket.write(Buffer.concat([header, payload]));
55
+ }
56
+
57
+ function parseFrames(buffer) {
58
+ const frames = [];
59
+ let offset = 0;
60
+ while (offset + 2 <= buffer.length) {
61
+ const b1 = buffer[offset];
62
+ const opcode = b1 & 0x0F;
63
+ const b2 = buffer[offset + 1];
64
+ const masked = (b2 & 0x80) !== 0;
65
+ let payloadLen = b2 & 0x7F;
66
+ let headerLen = 2;
67
+ if (payloadLen === 126) {
68
+ if (offset + 4 > buffer.length) break;
69
+ payloadLen = buffer.readUInt16BE(offset + 2);
70
+ headerLen = 4;
71
+ } else if (payloadLen === 127) {
72
+ if (offset + 10 > buffer.length) break;
73
+ payloadLen = Number(buffer.readBigUInt64BE(offset + 2));
74
+ headerLen = 10;
75
+ }
76
+ const maskLen = masked ? 4 : 0;
77
+ if (offset + headerLen + maskLen + payloadLen > buffer.length) break;
78
+ offset += headerLen;
79
+ let payload;
80
+ if (masked) {
81
+ const mask = buffer.slice(offset, offset + 4);
82
+ offset += 4;
83
+ payload = Buffer.allocUnsafe(payloadLen);
84
+ for (let i = 0; i < payloadLen; i += 1) {
85
+ payload[i] = buffer[offset + i] ^ mask[i % 4];
86
+ }
87
+ } else {
88
+ payload = buffer.slice(offset, offset + payloadLen);
89
+ }
90
+ offset += payloadLen;
91
+ frames.push({ opcode, payload });
92
+ }
93
+ return { frames, remainder: buffer.slice(offset) };
94
+ }
95
+
96
+ function sendHttpError(sock, statusCode, message) {
97
+ let code;
98
+ if (statusCode === 401) code = 'UNAUTHORIZED';
99
+ else if (statusCode === 404) code = 'NOT_FOUND';
100
+ else code = 'ERROR';
101
+ const body = JSON.stringify({ error: { code, message } });
102
+ const reason = statusCode === 401 ? 'Unauthorized' : statusCode === 404 ? 'Not Found' : 'Error';
103
+ sock.write(
104
+ `HTTP/1.1 ${statusCode} ${reason}\r\n` +
105
+ 'Content-Type: application/json\r\n' +
106
+ `Content-Length: ${body.length}\r\n` +
107
+ 'Connection: close\r\n\r\n' + body
108
+ );
109
+ sock.end();
110
+ }
111
+
112
+ function connectionKey(runId, windowId) {
113
+ return `${runId || ''}:${windowId || ''}`;
114
+ }
115
+
116
+ function cleanString(value, fallback = '') {
117
+ const text = String(value || '').trim();
118
+ return text || fallback;
119
+ }
120
+
121
+ function normalizeCommand(payload = {}) {
122
+ const command = payload.command && typeof payload.command === 'object' ? payload.command : {};
123
+ const commandId = cleanString(payload.commandId || command.commandId, `command-${Date.now().toString(36)}-${crypto.randomBytes(3).toString('hex')}`);
124
+ const primitive = cleanString(payload.primitive || command.primitive, '');
125
+ if (!primitive) {
126
+ throw new WfBrowserWsError('wf-browser command requires a primitive');
127
+ }
128
+ return {
129
+ commandId,
130
+ primitive,
131
+ agentId: cleanString(payload.agentId || command.agentId, ''),
132
+ sessionId: cleanString(payload.sessionId || command.sessionId, ''),
133
+ runId: cleanString(payload.runId || command.runId, ''),
134
+ windowId: cleanString(payload.windowId || command.windowId, ''),
135
+ leaseId: cleanString(payload.leaseId || command.leaseId, ''),
136
+ payload: payload.payload ?? command.payload ?? {},
137
+ timeoutMs: Math.min(Math.max(Number(payload.timeoutMs || command.timeoutMs || DEFAULT_COMMAND_TIMEOUT_MS), 500), MAX_COMMAND_TIMEOUT_MS),
138
+ };
139
+ }
140
+
141
+ export function attachWfBrowserWs(httpServer, expectedToken, projectRoot, options = {}) {
142
+ const keepaliveInterval = options.keepaliveInterval || KEEPALIVE_INTERVAL;
143
+ const pingTimeoutMs = options.pingTimeout || PING_TIMEOUT;
144
+ const tickMs = Math.min(keepaliveInterval, pingTimeoutMs) / 2;
145
+ const maxMessageBytes = options.maxMessageBytes || DEFAULT_MAX_MESSAGE_BYTES;
146
+ const clients = new Set();
147
+ const frontendByWindow = new Map();
148
+ const pendingCommands = new Map();
149
+ let keepaliveTimer = null;
150
+
151
+ httpServer.on('upgrade', (req, socket, head) => {
152
+ const url = new URL(req.url, `http://${req.headers.host || '127.0.0.1'}`);
153
+ if (url.pathname !== '/ws/wf-browser') return;
154
+
155
+ const key = req.headers['sec-websocket-key'];
156
+ const digest = crypto.createHash('sha1').update(key + WS_GUID).digest('base64');
157
+ socket.write(
158
+ 'HTTP/1.1 101 Switching Protocols\r\n' +
159
+ 'Upgrade: websocket\r\n' +
160
+ 'Connection: Upgrade\r\n' +
161
+ 'Sec-WebSocket-Accept: ' + digest + '\r\n\r\n'
162
+ );
163
+
164
+ const connectionId = crypto.randomBytes(16).toString('hex');
165
+ const client = {
166
+ socket,
167
+ connectionId,
168
+ role: 'unknown',
169
+ runId: '',
170
+ windowId: '',
171
+ agentId: '',
172
+ route: '',
173
+ capabilities: [],
174
+ connectedAt: new Date().toISOString(),
175
+ lastActivity: Date.now(),
176
+ waitingPong: false,
177
+ pingSentAt: 0,
178
+ };
179
+ clients.add(client);
180
+
181
+ sendJsonFrame(socket, {
182
+ type: 'wf-browser.connected',
183
+ protocolVersion: 1,
184
+ connectionId,
185
+ projectRoot,
186
+ maxMessageBytes,
187
+ ts: new Date().toISOString(),
188
+ });
189
+
190
+ let buffer = Buffer.alloc(0);
191
+ if (head && head.length > 0) buffer = Buffer.concat([buffer, head]);
192
+
193
+ socket.on('data', (chunk) => {
194
+ buffer = Buffer.concat([buffer, chunk]);
195
+ client.lastActivity = Date.now();
196
+ const result = parseFrames(buffer);
197
+ buffer = result.remainder;
198
+ for (const frame of result.frames) {
199
+ if (frame.payload.length > maxMessageBytes) {
200
+ sendCloseFrame(socket, 1009);
201
+ cleanupClient(client);
202
+ socket.end();
203
+ return;
204
+ }
205
+ if (frame.opcode === 0x9) {
206
+ sendFrame(socket, '', 0xA);
207
+ } else if (frame.opcode === 0xA) {
208
+ client.waitingPong = false;
209
+ } else if (frame.opcode === 0x8) {
210
+ sendCloseFrame(socket, 1000);
211
+ cleanupClient(client);
212
+ socket.end();
213
+ return;
214
+ } else if (frame.opcode === 0x1) {
215
+ try {
216
+ handleMessage(client, JSON.parse(frame.payload.toString('utf8')));
217
+ } catch (err) {
218
+ sendJsonFrame(socket, {
219
+ type: 'error',
220
+ code: err?.code || 'BAD_MESSAGE',
221
+ message: err?.message || 'Malformed wf-browser message',
222
+ });
223
+ }
224
+ }
225
+ }
226
+ });
227
+
228
+ socket.on('close', () => cleanupClient(client));
229
+ socket.on('end', () => cleanupClient(client));
230
+ socket.on('error', () => cleanupClient(client));
231
+ });
232
+
233
+ function handleMessage(client, msg) {
234
+ if (!msg || typeof msg !== 'object') return;
235
+ if (msg.type === 'hello') {
236
+ registerClient(client, msg);
237
+ return;
238
+ }
239
+ if (msg.type === 'heartbeat') {
240
+ sendJsonFrame(client.socket, {
241
+ type: 'heartbeat.ack',
242
+ connectionId: client.connectionId,
243
+ ts: new Date().toISOString(),
244
+ });
245
+ return;
246
+ }
247
+ if (msg.type === 'ack') {
248
+ const pending = pendingCommands.get(msg.commandId);
249
+ if (pending) pending.ack = { ...msg, receivedAt: new Date().toISOString() };
250
+ return;
251
+ }
252
+ if (msg.type === 'result') {
253
+ const pending = pendingCommands.get(msg.commandId);
254
+ if (!pending) return;
255
+ clearTimeout(pending.timer);
256
+ pendingCommands.delete(msg.commandId);
257
+ pending.resolve({
258
+ commandId: msg.commandId,
259
+ status: msg.status || 'ok',
260
+ result: msg.result ?? null,
261
+ artifacts: Array.isArray(msg.artifacts) ? msg.artifacts : [],
262
+ events: Array.isArray(msg.events) ? msg.events : [],
263
+ error: msg.error || null,
264
+ ack: pending.ack || null,
265
+ receivedAt: new Date().toISOString(),
266
+ });
267
+ }
268
+ }
269
+
270
+ function registerClient(client, msg) {
271
+ const previousKey = client.runId && client.windowId ? connectionKey(client.runId, client.windowId) : '';
272
+ if (previousKey && frontendByWindow.get(previousKey) === client) frontendByWindow.delete(previousKey);
273
+ client.role = cleanString(msg.role, 'frontend');
274
+ client.runId = cleanString(msg.runId, '');
275
+ client.windowId = cleanString(msg.windowId, '');
276
+ client.agentId = cleanString(msg.agentId, '');
277
+ client.route = cleanString(msg.route?.pathname || msg.route, '');
278
+ client.capabilities = Array.isArray(msg.capabilities) ? msg.capabilities.slice(0, 500) : [];
279
+ if (client.role === 'frontend' && client.runId && client.windowId) {
280
+ frontendByWindow.set(connectionKey(client.runId, client.windowId), client);
281
+ }
282
+ sendJsonFrame(client.socket, {
283
+ type: 'hello.ack',
284
+ status: 'registered',
285
+ connectionId: client.connectionId,
286
+ role: client.role,
287
+ runId: client.runId,
288
+ windowId: client.windowId,
289
+ capabilityCount: client.capabilities.length,
290
+ ts: new Date().toISOString(),
291
+ });
292
+ }
293
+
294
+ function cleanupClient(client) {
295
+ if (client._cleanedUp) return;
296
+ client._cleanedUp = true;
297
+ clients.delete(client);
298
+ const key = client.runId && client.windowId ? connectionKey(client.runId, client.windowId) : '';
299
+ if (key && frontendByWindow.get(key) === client) frontendByWindow.delete(key);
300
+ for (const [commandId, pending] of [...pendingCommands.entries()]) {
301
+ if (pending.client === client) {
302
+ clearTimeout(pending.timer);
303
+ pendingCommands.delete(commandId);
304
+ pending.reject(new WfBrowserWsError('wf-browser frontend disconnected before command completed', {
305
+ statusCode: 409,
306
+ code: 'WINDOW_DISCONNECTED',
307
+ }));
308
+ }
309
+ }
310
+ }
311
+
312
+ function sendCommand(payload = {}) {
313
+ const command = normalizeCommand(payload);
314
+ const client = frontendByWindow.get(connectionKey(command.runId, command.windowId));
315
+ if (!client || client.socket.destroyed) {
316
+ throw new WfBrowserWsError(`wf-browser window is not connected: ${command.windowId}`, {
317
+ statusCode: 409,
318
+ code: 'WINDOW_NOT_CONNECTED',
319
+ });
320
+ }
321
+ return new Promise((resolve, reject) => {
322
+ const timer = setTimeout(() => {
323
+ pendingCommands.delete(command.commandId);
324
+ reject(new WfBrowserWsError(`wf-browser command timed out: ${command.commandId}`, {
325
+ statusCode: 504,
326
+ code: 'COMMAND_TIMEOUT',
327
+ }));
328
+ }, command.timeoutMs);
329
+ pendingCommands.set(command.commandId, {
330
+ client,
331
+ command,
332
+ resolve,
333
+ reject,
334
+ timer,
335
+ ack: null,
336
+ });
337
+ try {
338
+ sendJsonFrame(client.socket, {
339
+ type: 'command',
340
+ protocolVersion: 1,
341
+ commandId: command.commandId,
342
+ primitive: command.primitive,
343
+ runId: command.runId,
344
+ windowId: command.windowId,
345
+ leaseId: command.leaseId,
346
+ agentId: command.agentId,
347
+ sessionId: command.sessionId,
348
+ payload: command.payload,
349
+ ts: new Date().toISOString(),
350
+ });
351
+ } catch (err) {
352
+ clearTimeout(timer);
353
+ pendingCommands.delete(command.commandId);
354
+ reject(err);
355
+ }
356
+ });
357
+ }
358
+
359
+ function listConnections() {
360
+ return [...clients].map(client => ({
361
+ connectionId: client.connectionId,
362
+ role: client.role,
363
+ runId: client.runId,
364
+ windowId: client.windowId,
365
+ agentId: client.agentId,
366
+ route: client.route,
367
+ capabilityCount: client.capabilities.length,
368
+ connectedAt: client.connectedAt,
369
+ lastActivityAt: new Date(client.lastActivity).toISOString(),
370
+ }));
371
+ }
372
+
373
+ keepaliveTimer = setInterval(() => {
374
+ const now = Date.now();
375
+ for (const client of [...clients]) {
376
+ try {
377
+ if (client.waitingPong) {
378
+ if (now - client.pingSentAt >= pingTimeoutMs) {
379
+ sendCloseFrame(client.socket, 1001);
380
+ client.socket.end();
381
+ cleanupClient(client);
382
+ }
383
+ } else if (now - client.lastActivity >= keepaliveInterval) {
384
+ sendFrame(client.socket, '', 0x9);
385
+ client.waitingPong = true;
386
+ client.pingSentAt = now;
387
+ }
388
+ } catch {
389
+ cleanupClient(client);
390
+ }
391
+ }
392
+ }, Math.max(tickMs, 100));
393
+
394
+ function close() {
395
+ return new Promise((resolve) => {
396
+ if (keepaliveTimer) clearInterval(keepaliveTimer);
397
+ for (const client of [...clients]) {
398
+ try { sendCloseFrame(client.socket, 1001); client.socket.end(); } catch { /* ignore */ }
399
+ }
400
+ clients.clear();
401
+ frontendByWindow.clear();
402
+ for (const [, pending] of pendingCommands) {
403
+ clearTimeout(pending.timer);
404
+ pending.reject(new WfBrowserWsError('wf-browser bridge closed', {
405
+ statusCode: 409,
406
+ code: 'BRIDGE_CLOSED',
407
+ }));
408
+ }
409
+ pendingCommands.clear();
410
+ resolve();
411
+ });
412
+ }
413
+
414
+ return {
415
+ close,
416
+ sendCommand,
417
+ listConnections,
418
+ };
419
+ }