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,778 @@
1
+ // graph-layout.mjs
2
+ //
3
+ // Occupancy-aware auto-layout for the workflow graph. Two entry points:
4
+ // - autoPlaceNode(): a free slot for ONE new node — placed near its parent
5
+ // (row below, columns scanning outward) or on the root row when parentless.
6
+ // Guarantees a new auto-placed node never overlaps ANY existing node,
7
+ // including manually dragged ones.
8
+ // - layeredTreePositions(): full-graph relayout — layered tree with subtree
9
+ // widths (children centered under their parent), roots side by side on the
10
+ // top row, every subtree in its own column block. Deterministic: same
11
+ // input always yields the same output.
12
+
13
+ export const GRAPH_SLOT_W = 280;
14
+ export const GRAPH_SLOT_H = 180;
15
+ export const GRAPH_ORIGIN = { x: 120, y: 120 };
16
+ const MAX_SCAN_COLS = 16;
17
+ const MAX_SCAN_ROWS = 24;
18
+ const MIN_CLEARANCE = 16;
19
+
20
+ // Per-kind visual sizes (HIGH-1): clearance is computed against the REAL
21
+ // rendered extents, not the 280x180 layout slot. Agent nodes render at
22
+ // 560x358 (client TERMINAL_NODE_W/H); every other kind uses the slot size.
23
+ // Keyed by graph node kind; a node's agentKind marks an agent node.
24
+ export const NODE_KIND_SIZES = Object.freeze({
25
+ 'terminal-session': { w: 560, h: 358 },
26
+ agent: { w: 560, h: 358 },
27
+ });
28
+
29
+ export function nodeVisualSize(node = {}) {
30
+ const kind = String(node?.kind || '').toLowerCase();
31
+ const agentKind = String(node?.agentKind || '').toLowerCase();
32
+ const sized = NODE_KIND_SIZES[kind]
33
+ || ((agentKind === 'main' || agentKind === 'subagent') ? NODE_KIND_SIZES.agent : null);
34
+ return sized || { w: GRAPH_SLOT_W, h: GRAPH_SLOT_H };
35
+ }
36
+
37
+ function slotX(col) {
38
+ return GRAPH_ORIGIN.x + col * GRAPH_SLOT_W;
39
+ }
40
+ function slotY(row) {
41
+ return GRAPH_ORIGIN.y + row * GRAPH_SLOT_H;
42
+ }
43
+
44
+ // True when a rect at (x, y) sized w x h keeps >= `gap` clearance from every
45
+ // existing rect in `rects` (AABB separation along x or y).
46
+ function rectsClearAt(x, y, w, h, rects, gap) {
47
+ for (const rect of rects || []) {
48
+ if (!rect || !Number.isFinite(Number(rect.x)) || !Number.isFinite(Number(rect.y))) continue;
49
+ const rw = Number.isFinite(Number(rect.w)) ? Number(rect.w) : GRAPH_SLOT_W;
50
+ const rh = Number.isFinite(Number(rect.h)) ? Number(rect.h) : GRAPH_SLOT_H;
51
+ if (
52
+ x - gap < Number(rect.x) + rw
53
+ && x + w + gap > Number(rect.x)
54
+ && y - gap < Number(rect.y) + rh
55
+ && y + h + gap > Number(rect.y)
56
+ ) return false;
57
+ }
58
+ return true;
59
+ }
60
+
61
+ // A cell is occupied when any existing node center lands inside the cell's
62
+ // rectangle. Auto-placed nodes sit exactly on cells, so two auto nodes can
63
+ // never share a cell; a dragged node is honored when its center intersects.
64
+ function cellOccupied(row, col, existingPositions) {
65
+ const cx = slotX(col);
66
+ const cy = slotY(row);
67
+ for (const pos of Object.values(existingPositions || {})) {
68
+ if (!pos || !Number.isFinite(Number(pos.x)) || !Number.isFinite(Number(pos.y))) continue;
69
+ if (Math.abs(Number(pos.x) - cx) < GRAPH_SLOT_W / 2 && Math.abs(Number(pos.y) - cy) < GRAPH_SLOT_H / 2) {
70
+ return true;
71
+ }
72
+ }
73
+ return false;
74
+ }
75
+
76
+ function firstFreeOnRow(row, startCol, existingPositions) {
77
+ // Scan from startCol outward (startCol, startCol-1, startCol+1, ...) so
78
+ // siblings cluster around the parent's column.
79
+ for (let span = 0; span < MAX_SCAN_COLS; span += 1) {
80
+ const cols = span === 0 ? [startCol] : [startCol - span, startCol + span];
81
+ for (const col of cols) {
82
+ if (col < 0) continue;
83
+ if (!cellOccupied(row, col, existingPositions)) return col;
84
+ }
85
+ }
86
+ return null;
87
+ }
88
+
89
+ // Finds the first free slot for a new node. With a parent position, prefers
90
+ // the row directly below the parent (scanned outward around the parent's
91
+ // column), then falls back to row-by-row scanning. Parentless nodes take the
92
+ // first free root row slot. Deterministic; never returns an occupied cell.
93
+ //
94
+ // Size-aware mode (HIGH-1): when `selfSize` and `existingRects` are supplied
95
+ // (real node extents per kind), the scan tests rect clearance instead of cell
96
+ // occupancy, so a 560x358 agent can never land at the 280px slot pitch that
97
+ // would overlap an existing agent. Without them the classic cell scan runs
98
+ // unchanged.
99
+ export function autoPlaceNode(existingPositions, { parent = null, selfSize = null, existingRects = null } = {}) {
100
+ const positions = existingPositions || {};
101
+ const rects = Array.isArray(existingRects) ? existingRects : null;
102
+ if (rects) {
103
+ const w = selfSize && Number.isFinite(Number(selfSize.w)) ? Number(selfSize.w) : GRAPH_SLOT_W;
104
+ const h = selfSize && Number.isFinite(Number(selfSize.h)) ? Number(selfSize.h) : GRAPH_SLOT_H;
105
+ const firstClearXOnRow = (row, startCol) => {
106
+ for (let span = 0; span < MAX_SCAN_COLS; span += 1) {
107
+ const cols = span === 0 ? [startCol] : [startCol - span, startCol + span];
108
+ for (const col of cols) {
109
+ if (col < 0) continue;
110
+ if (rectsClearAt(slotX(col), slotY(row), w, h, rects, MIN_CLEARANCE)) return slotX(col);
111
+ }
112
+ }
113
+ return null;
114
+ };
115
+ if (parent && Number.isFinite(Number(parent.x)) && Number.isFinite(Number(parent.y))) {
116
+ const parentRow = Math.round((Number(parent.y) - GRAPH_ORIGIN.y) / GRAPH_SLOT_H);
117
+ const parentCol = Math.round((Number(parent.x) - GRAPH_ORIGIN.x) / GRAPH_SLOT_W);
118
+ const rows = [parentRow + 1, parentRow + 2, parentRow, parentRow - 1];
119
+ for (const row of rows) {
120
+ if (row < 0) continue;
121
+ const x = firstClearXOnRow(row, parentCol);
122
+ if (x !== null) return { x, y: slotY(row) };
123
+ }
124
+ for (let row = 0; row < MAX_SCAN_ROWS; row += 1) {
125
+ const x = firstClearXOnRow(row, parentCol);
126
+ if (x !== null) return { x, y: slotY(row) };
127
+ }
128
+ return { x: slotX(0), y: slotY(MAX_SCAN_ROWS) };
129
+ }
130
+ for (let row = 0; row < MAX_SCAN_ROWS; row += 1) {
131
+ const x = firstClearXOnRow(row, 0);
132
+ if (x !== null) return { x, y: slotY(row) };
133
+ }
134
+ return { x: GRAPH_ORIGIN.x, y: slotY(MAX_SCAN_ROWS) };
135
+ }
136
+ if (parent && Number.isFinite(Number(parent.x)) && Number.isFinite(Number(parent.y))) {
137
+ const parentRow = Math.round((Number(parent.y) - GRAPH_ORIGIN.y) / GRAPH_SLOT_H);
138
+ const parentCol = Math.round((Number(parent.x) - GRAPH_ORIGIN.x) / GRAPH_SLOT_W);
139
+ const rows = [parentRow + 1, parentRow + 2, parentRow, parentRow - 1];
140
+ for (const row of rows) {
141
+ if (row < 0) continue;
142
+ const col = firstFreeOnRow(row, parentCol, positions);
143
+ if (col !== null) return { x: slotX(col), y: slotY(row) };
144
+ }
145
+ // Extreme fallback: absolute scan.
146
+ for (let row = 0; row < MAX_SCAN_ROWS; row += 1) {
147
+ const col = firstFreeOnRow(row, parentCol, positions);
148
+ if (col !== null) return { x: slotX(col), y: slotY(row) };
149
+ }
150
+ return { x: slotX(0), y: slotY(MAX_SCAN_ROWS) };
151
+ }
152
+ for (let row = 0; row < MAX_SCAN_ROWS; row += 1) {
153
+ const col = firstFreeOnRow(row, 0, positions);
154
+ if (col !== null) return { x: slotX(col), y: slotY(row) };
155
+ }
156
+ return { x: GRAPH_ORIGIN.x, y: slotY(MAX_SCAN_ROWS) };
157
+ }
158
+
159
+ // Outward square spiral around `requested` (HIGH-1): returns the first
160
+ // top-left corner whose rect keeps >= `gap` clearance from every existing
161
+ // rect. Deterministic; bounded so the fallback always terminates.
162
+ export function findClearPosition({ requested = null, selfSize = null, existingRects = [], gap = MIN_CLEARANCE } = {}) {
163
+ const w = selfSize && Number.isFinite(Number(selfSize.w)) ? Number(selfSize.w) : GRAPH_SLOT_W;
164
+ const h = selfSize && Number.isFinite(Number(selfSize.h)) ? Number(selfSize.h) : GRAPH_SLOT_H;
165
+ const rects = Array.isArray(existingRects) ? existingRects : [];
166
+ const originX = requested && Number.isFinite(Number(requested.x)) ? Number(requested.x) : GRAPH_ORIGIN.x;
167
+ const originY = requested && Number.isFinite(Number(requested.y)) ? Number(requested.y) : GRAPH_ORIGIN.y;
168
+ const step = Math.max(gap, 1);
169
+ if (rectsClearAt(originX, originY, w, h, rects, gap)) return { x: originX, y: originY };
170
+ const MAX_RINGS = 64;
171
+ for (let ring = 1; ring <= MAX_RINGS; ring += 1) {
172
+ for (let k = 0; k < 2 * ring; k += 1) {
173
+ const candidates = [
174
+ [-ring + k, -ring], // top edge, left -> right
175
+ [ring, -ring + k], // right edge, top -> bottom
176
+ [ring - k, ring], // bottom edge, right -> left
177
+ [-ring, ring - k], // left edge, bottom -> top
178
+ ];
179
+ for (const [dx, dy] of candidates) {
180
+ const x = originX + dx * step;
181
+ const y = originY + dy * step;
182
+ if (rectsClearAt(x, y, w, h, rects, gap)) return { x, y };
183
+ }
184
+ }
185
+ }
186
+ return { x: originX + (MAX_RINGS + 1) * step, y: originY + (MAX_RINGS + 1) * step };
187
+ }
188
+
189
+ function nodeIdOf(node) {
190
+ return node.nodeId || node.id || '';
191
+ }
192
+
193
+ function parentIdOf(node, nodesById) {
194
+ const parentNodeId = String(node.parentNodeId || '');
195
+ const parentAgentId = String(node.parentAgentId || '');
196
+ if (parentNodeId && nodesById.has(parentNodeId)) return parentNodeId;
197
+ if (parentNodeId && nodesById.has(parentNodeId.replace(/^session-/, ''))) {
198
+ // graph ids may be session-<id> while parentNodeId carries the bare id
199
+ return parentNodeId.replace(/^session-/, '');
200
+ }
201
+ if (parentAgentId) {
202
+ const bySession = nodesById.get(parentAgentId);
203
+ if (bySession) return bySession.nodeId;
204
+ // parentAgentId may be the bare session id while the node id is session-<id>
205
+ const bySessionPrefixed = nodesById.get(`session-${parentAgentId}`);
206
+ if (bySessionPrefixed) return bySessionPrefixed.nodeId;
207
+ }
208
+ return '';
209
+ }
210
+
211
+ // Full-graph layered tree relayout. Roots side by side on row 0; each child
212
+ // row is depth+1; every subtree occupies its own column block sized by its
213
+ // leaf count, so sibling subtrees never collide.
214
+ export function layeredTreePositions(nodes, { originX = GRAPH_ORIGIN.x, originY = GRAPH_ORIGIN.y } = {}) {
215
+ const nodesById = new Map();
216
+ for (const node of nodes) nodesById.set(nodeIdOf(node), { nodeId: nodeIdOf(node), node });
217
+ const childrenOf = new Map();
218
+ const roots = [];
219
+ for (const node of nodes) {
220
+ const parentId = parentIdOf(node, nodesById);
221
+ if (!parentId) {
222
+ roots.push(node);
223
+ continue;
224
+ }
225
+ const children = childrenOf.get(parentId) || [];
226
+ children.push(node);
227
+ childrenOf.set(parentId, children);
228
+ }
229
+
230
+ const leafCount = (id) => {
231
+ const children = childrenOf.get(id) || [];
232
+ if (!children.length) return 1;
233
+ return children.reduce((sum, child) => sum + leafCount(nodeIdOf(child)), 0);
234
+ };
235
+
236
+ const positions = {};
237
+ let nextRootX = originX;
238
+ const place = (node, x, depth) => {
239
+ const id = nodeIdOf(node);
240
+ positions[id] = { x, y: originY + depth * GRAPH_SLOT_H };
241
+ const children = childrenOf.get(id) || [];
242
+ const totalLeaves = children.reduce((sum, child) => sum + leafCount(nodeIdOf(child)), 0) || 1;
243
+ // Center the children block under the parent: walk the block's left edge
244
+ // and place each child at the center of its own sub-block.
245
+ let childX = x - (totalLeaves * GRAPH_SLOT_W) / 2;
246
+ for (const child of children) {
247
+ const leaves = leafCount(nodeIdOf(child));
248
+ place(child, childX + (leaves * GRAPH_SLOT_W) / 2, depth + 1);
249
+ childX += leaves * GRAPH_SLOT_W;
250
+ }
251
+ };
252
+
253
+ for (const root of roots) {
254
+ place(root, nextRootX, 0);
255
+ const leaves = leafCount(nodeIdOf(root));
256
+ nextRootX += Math.max(leaves, 1) * GRAPH_SLOT_W;
257
+ nextRootX += GRAPH_SLOT_W; // inter-tree gutter
258
+ }
259
+ return positions;
260
+ }
261
+
262
+ // Shared dock-chain machinery (extracted from tidyPositions, MED-4). Chains
263
+ // are walked into ONE glued group per anchor (anchor = first member, each
264
+ // slave = anchor + accumulated offset), so a slave that is itself another
265
+ // pair's anchor still glues exactly. Dock CYCLES (a backward walk revisits an
266
+ // id) are detected with a visited set; cycle members are laid out as SEPARATE
267
+ // units with normal clearance — never stacked on one point. The unit bbox
268
+ // covers the FULL union extents, including negative (upward / leftward)
269
+ // offsets, so row advance and column stacking reserve space.
270
+ function dockChainUnits(ids, dockedPairs, sizeOf) {
271
+ const linkOf = new Map(); // anchorId -> { slaveId, offX, offY } (first link per anchor)
272
+ for (const pair of dockedPairs) {
273
+ const aId = String(pair?.aId ?? '');
274
+ const bId = String(pair?.bId ?? '');
275
+ if (!aId || !bId || aId === bId) continue;
276
+ if (!sizeOf.has(aId) || !sizeOf.has(bId)) continue;
277
+ if (linkOf.has(aId)) continue;
278
+ linkOf.set(aId, {
279
+ slaveId: bId,
280
+ offX: Number.isFinite(Number(pair?.offset?.x)) ? Number(pair.offset.x) : 0,
281
+ offY: Number.isFinite(Number(pair?.offset?.y)) ? Number(pair.offset.y) : 0,
282
+ });
283
+ }
284
+ const anchorOf = new Map(); // slaveId -> anchorId
285
+ for (const [aId, link] of linkOf) anchorOf.set(link.slaveId, aId);
286
+ const headOf = new Map(); // chain member id -> chain head id
287
+ const cycleIds = new Set();
288
+ for (const id of ids) {
289
+ if (headOf.has(id) || cycleIds.has(id)) continue;
290
+ const seen = new Set([id]);
291
+ let cur = id;
292
+ while (anchorOf.has(cur)) {
293
+ const next = anchorOf.get(cur);
294
+ if (seen.has(next)) {
295
+ for (const member of seen) cycleIds.add(member);
296
+ break;
297
+ }
298
+ seen.add(next);
299
+ cur = next;
300
+ }
301
+ if (!cycleIds.has(id)) {
302
+ for (const member of seen) headOf.set(member, cur);
303
+ }
304
+ }
305
+ const isGluedMember = (id) => headOf.has(id) && headOf.get(id) !== id && !cycleIds.has(id);
306
+
307
+ // Chain resolution: walk forward from each head accumulating offsets.
308
+ const chainMembersOf = new Map(); // headId -> [{ id, offX, offY }]
309
+ const chainExtentOf = new Map(); // headId -> { up, down, left, right }
310
+ for (const id of ids) {
311
+ if (headOf.get(id) !== id) continue;
312
+ const members = [{ id, offX: 0, offY: 0 }];
313
+ const walked = new Set([id]);
314
+ let cur = id;
315
+ while (linkOf.has(cur)) {
316
+ const link = linkOf.get(cur);
317
+ if (walked.has(link.slaveId)) break; // back link inside a resolvable chain
318
+ const previous = members[members.length - 1];
319
+ members.push({
320
+ id: link.slaveId,
321
+ offX: previous.offX + link.offX,
322
+ offY: previous.offY + link.offY,
323
+ });
324
+ walked.add(link.slaveId);
325
+ cur = link.slaveId;
326
+ }
327
+ chainMembersOf.set(id, members);
328
+ const anchorSize = sizeOf.get(id);
329
+ let up = 0;
330
+ let down = anchorSize.h;
331
+ let left = 0;
332
+ let right = anchorSize.w;
333
+ for (const member of members) {
334
+ const size = sizeOf.get(member.id);
335
+ up = Math.min(up, member.offY);
336
+ down = Math.max(down, member.offY + size.h);
337
+ left = Math.min(left, member.offX);
338
+ right = Math.max(right, member.offX + size.w);
339
+ }
340
+ chainExtentOf.set(id, { up, down, left, right });
341
+ }
342
+ const unitExtent = (id) => chainExtentOf.get(id) || null;
343
+ return {
344
+ isGluedMember,
345
+ chainMembersOf,
346
+ unitExtent,
347
+ unitUp: (id) => (unitExtent(id) ? unitExtent(id).up : 0),
348
+ unitDown: (id) => (unitExtent(id) ? unitExtent(id).down : sizeOf.get(id).h),
349
+ unitLeft: (id) => (unitExtent(id) ? unitExtent(id).left : 0),
350
+ unitRight: (id) => (unitExtent(id) ? unitExtent(id).right : sizeOf.get(id).w),
351
+ unitWidth: (id) => unitExtent(id) ? (unitExtent(id).right - unitExtent(id).left) : sizeOf.get(id).w,
352
+ };
353
+ }
354
+
355
+ // ── Tidy: hierarchical edge-direction relayout ──────────────────────────────
356
+ //
357
+ // tidyPositions(): full-graph relayout driven by EDGE DIRECTION: source-to-
358
+ // target edges point downward (layer(to) = layer(from) + 1), bidirectional
359
+ // edges impose no layer order (both endpoints stay on the same row). Layers
360
+ // come from longest-path relaxation over the acyclic skeleton (DFS back-edge
361
+ // exclusion), so cyclic graphs still place every node exactly once.
362
+ // Deterministic: same input always yields the same output.
363
+ //
364
+ // Rows advance by max(gapY, tallest unit on the row above + 16px clearance);
365
+ // within a row, units stack left-to-right with gapX spacing. A docked pair
366
+ // {aId, bId, offset} is a single unit: the anchor is laid out normally and
367
+ // the slave is placed EXACTLY at anchor + offset; the pair's bounding box
368
+ // drives row height and column width, so glued pairs never collide with
369
+ // anything else.
370
+
371
+ const TIDY_DEFAULT_ORIGIN = { x: 260, y: 220 };
372
+ const TIDY_DEFAULT_GAP = { x: 420, y: 140 };
373
+ const TIDY_MIN_CLEARANCE = 16;
374
+
375
+ function tidyNodeSize(node) {
376
+ const width = Number(node?.width);
377
+ const height = Number(node?.height);
378
+ return {
379
+ w: Number.isFinite(width) && width > 0 ? width : GRAPH_SLOT_W,
380
+ h: Number.isFinite(height) && height > 0 ? height : GRAPH_SLOT_H,
381
+ };
382
+ }
383
+
384
+ export function tidyPositions(nodes, edges = [], opts = {}) {
385
+ const origin = (opts && typeof opts === 'object' && opts.origin) || {};
386
+ const originX = Number.isFinite(Number(origin.x)) ? Number(origin.x) : TIDY_DEFAULT_ORIGIN.x;
387
+ const originY = Number.isFinite(Number(origin.y)) ? Number(origin.y) : TIDY_DEFAULT_ORIGIN.y;
388
+ // LOW-9: explicit gaps below the 16px minimum clearance are clamped up.
389
+ const gapX = Math.max(TIDY_MIN_CLEARANCE, Number.isFinite(Number(opts?.gapX)) ? Number(opts.gapX) : TIDY_DEFAULT_GAP.x);
390
+ const gapY = Math.max(TIDY_MIN_CLEARANCE, Number.isFinite(Number(opts?.gapY)) ? Number(opts.gapY) : TIDY_DEFAULT_GAP.y);
391
+ const dockedPairs = Array.isArray(opts?.dockedPairs) ? opts.dockedPairs : [];
392
+ const nodeList = Array.isArray(nodes) ? nodes : [];
393
+ const edgeList = Array.isArray(edges) ? edges : [];
394
+
395
+ const ids = [];
396
+ const sizeOf = new Map();
397
+ for (const node of nodeList) {
398
+ const id = String(node?.id ?? '');
399
+ if (!id || sizeOf.has(id)) continue;
400
+ ids.push(id);
401
+ sizeOf.set(id, tidyNodeSize(node));
402
+ }
403
+
404
+ // Only source-to-target edges constrain layering.
405
+ const dagEdges = [];
406
+ for (const edge of edgeList) {
407
+ const from = String(edge?.from ?? '');
408
+ const to = String(edge?.to ?? '');
409
+ const direction = String(edge?.direction || 'source-to-target');
410
+ if (!from || !to || direction === 'bidirectional') continue;
411
+ if (!sizeOf.has(from) || !sizeOf.has(to)) continue;
412
+ dagEdges.push({ from, to });
413
+ }
414
+
415
+ // Docked pairs (MED-4): shared chain machinery — glued groups, cycle
416
+ // detection, and per-unit bbox extents all live in dockChainUnits.
417
+ const dock = dockChainUnits(ids, dockedPairs, sizeOf);
418
+ const isGluedMember = dock.isGluedMember;
419
+
420
+ // Longest-path layering: DFS over the input-ordered skeleton, excluding
421
+ // back edges so cycles cannot inflate layers or drop nodes.
422
+ const adjacency = new Map(ids.map((id) => [id, []]));
423
+ for (const edge of dagEdges) adjacency.get(edge.from).push(edge.to);
424
+ const color = new Map(ids.map((id) => [id, 0])); // 0 unvisited, 1 in stack, 2 done
425
+ const backEdges = new Set();
426
+ const finishOrder = [];
427
+ const visit = (id) => {
428
+ color.set(id, 1);
429
+ for (const next of adjacency.get(id)) {
430
+ if (color.get(next) === 1) {
431
+ backEdges.add(`${id}\x00${next}`);
432
+ continue;
433
+ }
434
+ if (color.get(next) === 2) continue;
435
+ visit(next);
436
+ }
437
+ color.set(id, 2);
438
+ finishOrder.push(id);
439
+ };
440
+ for (const id of ids) {
441
+ if (color.get(id) === 0) visit(id);
442
+ }
443
+ const forwardFrom = new Map(ids.map((id) => [id, []]));
444
+ for (const edge of dagEdges) {
445
+ if (backEdges.has(`${edge.from}\x00${edge.to}`)) continue;
446
+ forwardFrom.get(edge.from).push(edge.to);
447
+ }
448
+ const layerOf = new Map(ids.map((id) => [id, 0]));
449
+ for (let i = finishOrder.length - 1; i >= 0; i -= 1) {
450
+ const id = finishOrder[i];
451
+ for (const next of forwardFrom.get(id)) {
452
+ const candidate = layerOf.get(id) + 1;
453
+ if (candidate > layerOf.get(next)) layerOf.set(next, candidate);
454
+ }
455
+ }
456
+
457
+ // Rows: input order per layer; glued chain members are excluded (placed
458
+ // with their anchor). Cycle members are ordinary units.
459
+ const rows = [];
460
+ for (const id of ids) {
461
+ if (isGluedMember(id)) continue;
462
+ const layer = layerOf.get(id);
463
+ if (!Array.isArray(rows[layer])) rows[layer] = [];
464
+ rows[layer].push(id);
465
+ }
466
+ for (let layer = 0; layer < rows.length; layer += 1) {
467
+ if (!Array.isArray(rows[layer])) rows[layer] = [];
468
+ }
469
+
470
+ const { chainMembersOf, unitUp, unitDown, unitLeft, unitRight, unitWidth } = dock;
471
+
472
+ // Row advance (MED-3): each row clears the row above by >= 16px even when a
473
+ // unit on the current row extends ABOVE its anchor y (negative dock
474
+ // offsets), and the gapY floor is preserved.
475
+ const rowY = [originY];
476
+ for (let layer = 1; layer < rows.length; layer += 1) {
477
+ let prevBottom = 0;
478
+ for (const id of rows[layer - 1]) prevBottom = Math.max(prevBottom, unitDown(id));
479
+ let upward = 0;
480
+ for (const id of rows[layer]) upward = Math.max(upward, -unitUp(id));
481
+ rowY[layer] = Math.max(
482
+ rowY[layer - 1] + gapY,
483
+ rowY[layer - 1] + prevBottom + TIDY_MIN_CLEARANCE + upward,
484
+ );
485
+ }
486
+
487
+ const positions = {};
488
+ for (let layer = 0; layer < rows.length; layer += 1) {
489
+ let cursor = originX;
490
+ let rowRight = -Infinity;
491
+ for (const id of rows[layer]) {
492
+ // A unit whose union extends LEFT of its anchor x must still clear the
493
+ // previous unit's right edge by >= gapX.
494
+ const x = Math.max(cursor, rowRight + gapX - unitLeft(id));
495
+ positions[id] = { x, y: rowY[layer] };
496
+ rowRight = Math.max(rowRight, x + unitRight(id));
497
+ cursor = x + unitWidth(id) + gapX;
498
+ }
499
+ }
500
+
501
+ // Glue: positions[slave] = positions[anchor] + offset, exactly, along the
502
+ // whole chain (MED-4).
503
+ for (const [headId, members] of chainMembersOf) {
504
+ const headPosition = positions[headId];
505
+ if (!headPosition) continue;
506
+ for (const member of members) {
507
+ if (member.id === headId) continue;
508
+ positions[member.id] = { x: headPosition.x + member.offX, y: headPosition.y + member.offY };
509
+ }
510
+ }
511
+ // Defensive: every input node must be placed exactly once.
512
+ for (const id of ids) {
513
+ if (positions[id]) continue;
514
+ positions[id] = { x: originX, y: originY + rows.length * gapY };
515
+ }
516
+ return positions;
517
+ }
518
+
519
+ // ── Agent-tree compact layout (task-agent-tree-layout, round 2) ─────────────
520
+ //
521
+ // agentTreePositions(): agent-centric auto-layout:
522
+ // - main agent = core anchor, centered (origin.x is its CENTER x; origin.y
523
+ // is the TOP of its asset band);
524
+ // - subagents form an agent TREE below main (parenting: explicit
525
+ // parentAgentId > first edge to another agent > main; cycle-guarded so
526
+ // loops degrade to the root row);
527
+ // - non-agent ("scattered") nodes form a compact MATRIX band ABOVE their
528
+ // owner (band per owner; unowned nodes band above main; matrix columns =
529
+ // clamp(ceil(sqrt(n)), 1, 6), row-major by id);
530
+ // - layout NEVER creates or modifies edges — positions only;
531
+ // - docked pairs glued exactly via the shared dock-chain machinery;
532
+ // - deterministic; every input node placed exactly once; >= 16px clearance.
533
+ //
534
+ // No main agent -> flat fallback: all agents one root row centered on the
535
+ // origin, all unowned assets one matrix band above, centered.
536
+
537
+ const AGENT_TREE_DEFAULT_ORIGIN = { x: 260, y: 220 };
538
+ const AGENT_TREE_DEFAULT_GAP = { x: 64, y: 48 };
539
+ const AGENT_TREE_MIN_CLEARANCE = 16;
540
+
541
+ function agentNodeShape(node) {
542
+ const kind = String(node?.agentKind || '').toLowerCase();
543
+ return kind === 'main' || kind === 'subagent';
544
+ }
545
+
546
+ export function agentTreePositions(nodes, edges = [], opts = {}) {
547
+ const origin = (opts && typeof opts === 'object' && opts.origin) || {};
548
+ const originX = Number.isFinite(Number(origin.x)) ? Number(origin.x) : AGENT_TREE_DEFAULT_ORIGIN.x;
549
+ const originY = Number.isFinite(Number(origin.y)) ? Number(origin.y) : AGENT_TREE_DEFAULT_ORIGIN.y;
550
+ const gapX = Math.max(AGENT_TREE_MIN_CLEARANCE, Number.isFinite(Number(opts?.gapX)) ? Number(opts.gapX) : AGENT_TREE_DEFAULT_GAP.x);
551
+ const gapY = Math.max(AGENT_TREE_MIN_CLEARANCE, Number.isFinite(Number(opts?.gapY)) ? Number(opts.gapY) : AGENT_TREE_DEFAULT_GAP.y);
552
+ const dockedPairs = Array.isArray(opts?.dockedPairs) ? opts.dockedPairs : [];
553
+ const nodeList = Array.isArray(nodes) ? nodes : [];
554
+ const edgeList = Array.isArray(edges) ? edges : [];
555
+
556
+ const ids = [];
557
+ const sizeOf = new Map();
558
+ const nodeOf = new Map();
559
+ for (const node of nodeList) {
560
+ const id = String(node?.id ?? '');
561
+ if (!id || sizeOf.has(id)) continue;
562
+ ids.push(id);
563
+ sizeOf.set(id, tidyNodeSize(node));
564
+ nodeOf.set(id, node);
565
+ }
566
+
567
+ const dock = dockChainUnits(ids, dockedPairs, sizeOf);
568
+ const gluedSlave = new Set();
569
+ for (const id of ids) if (dock.isGluedMember(id)) gluedSlave.add(id);
570
+
571
+ // Agents + the main anchor (first main by input order).
572
+ const agentIds = ids.filter((id) => agentNodeShape(nodeOf.get(id)));
573
+ const mainId = agentIds.find((id) => String(nodeOf.get(id)?.agentKind || '').toLowerCase() === 'main') || '';
574
+
575
+ // Edge adjacency (both directions) — parenting/ownership only.
576
+ const neighborsOf = new Map(ids.map((id) => [id, []]));
577
+ for (const edge of edgeList) {
578
+ const from = String(edge?.from ?? '');
579
+ const to = String(edge?.to ?? '');
580
+ if (!from || !to || !sizeOf.has(from) || !sizeOf.has(to)) continue;
581
+ neighborsOf.get(from).push(to);
582
+ neighborsOf.get(to).push(from);
583
+ }
584
+
585
+ // Raw agent parent: explicit parentAgentId > first agent neighbor > main.
586
+ const rawParentOf = new Map();
587
+ for (const id of agentIds) {
588
+ if (id === mainId) { rawParentOf.set(id, ''); continue; }
589
+ const pid = String(nodeOf.get(id)?.parentAgentId || '');
590
+ if (pid && pid !== id && agentIds.includes(pid)) { rawParentOf.set(id, pid); continue; }
591
+ const neighbor = (neighborsOf.get(id) || []).find((n) => n !== id && agentIds.includes(n));
592
+ if (neighbor) { rawParentOf.set(id, neighbor); continue; }
593
+ rawParentOf.set(id, mainId);
594
+ }
595
+ // Cycle guard: following the parent chain must never revisit an id; loops
596
+ // degrade to the root row.
597
+ const parentOf = new Map();
598
+ for (const id of agentIds) {
599
+ const direct = rawParentOf.get(id) || '';
600
+ if (!direct) { parentOf.set(id, ''); continue; }
601
+ const seen = new Set([id]);
602
+ let cur = direct;
603
+ let cycle = false;
604
+ while (cur && sizeOf.has(cur)) {
605
+ if (seen.has(cur)) { cycle = true; break; }
606
+ seen.add(cur);
607
+ cur = rawParentOf.get(cur) || '';
608
+ }
609
+ parentOf.set(id, cycle ? '' : direct);
610
+ }
611
+
612
+ // Asset owner: parentAgentId > parentNodeId chain > first agent neighbor >
613
+ // main; unowned (no main) assets share the flat top band.
614
+ const ownerOf = new Map();
615
+ for (const id of ids) {
616
+ if (agentIds.includes(id) || gluedSlave.has(id)) continue;
617
+ let owner = '';
618
+ const pid = String(nodeOf.get(id)?.parentAgentId || '');
619
+ if (pid && pid !== id && agentIds.includes(pid)) owner = pid;
620
+ if (!owner) {
621
+ const seen = new Set([id]);
622
+ let cur = String(nodeOf.get(id)?.parentNodeId || '');
623
+ while (cur && sizeOf.has(cur) && !seen.has(cur)) {
624
+ seen.add(cur);
625
+ if (agentIds.includes(cur)) { owner = cur; break; }
626
+ cur = String(nodeOf.get(cur)?.parentNodeId || '');
627
+ }
628
+ }
629
+ if (!owner) {
630
+ const neighbor = (neighborsOf.get(id) || []).find((n) => agentIds.includes(n));
631
+ if (neighbor) owner = neighbor;
632
+ }
633
+ ownerOf.set(id, owner || mainId);
634
+ }
635
+
636
+ // Matrix bands: compact grid per owner, rows centered on the owner's x.
637
+ const buildBand = (items) => {
638
+ const sorted = [...items].sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
639
+ if (!sorted.length) return { w: 0, h: 0, rows: [] };
640
+ const cols = Math.min(6, Math.max(1, Math.ceil(Math.sqrt(sorted.length))));
641
+ const rows = [];
642
+ for (let i = 0; i < sorted.length; i += cols) rows.push(sorted.slice(i, i + cols));
643
+ const rowGeom = rows.map((rowItems) => {
644
+ const w = rowItems.reduce((sum, it) => sum + it.w, 0) + gapX * (rowItems.length - 1);
645
+ const h = rowItems.reduce((max, it) => Math.max(max, it.h), 0);
646
+ return { items: rowItems, w, h };
647
+ });
648
+ return {
649
+ w: rowGeom.reduce((max, r) => Math.max(max, r.w), 0),
650
+ h: rowGeom.reduce((sum, r) => sum + r.h, 0) + gapY * (rowGeom.length - 1),
651
+ rows: rowGeom,
652
+ };
653
+ };
654
+ const bandOf = new Map(); // ownerId -> band
655
+ const bandItemsOf = new Map();
656
+ for (const id of ids) {
657
+ const owner = ownerOf.get(id);
658
+ if (!owner) continue;
659
+ const list = bandItemsOf.get(owner) || [];
660
+ list.push({ id, w: sizeOf.get(id).w, h: sizeOf.get(id).h });
661
+ bandItemsOf.set(owner, list);
662
+ }
663
+ for (const [owner, list] of bandItemsOf) bandOf.set(owner, buildBand(list));
664
+ const bandSize = (id) => bandOf.get(id) || { w: 0, h: 0, rows: [] };
665
+
666
+ // Agent tree children (sorted by id), docked slaves excluded.
667
+ const childrenOf = new Map();
668
+ for (const id of agentIds) childrenOf.set(id, []);
669
+ for (const id of agentIds) {
670
+ if (gluedSlave.has(id)) continue;
671
+ const parent = parentOf.get(id) || '';
672
+ if (parent && childrenOf.has(parent)) childrenOf.get(parent).push(id);
673
+ }
674
+ for (const list of childrenOf.values()) list.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
675
+
676
+ const unitW = (id) => Math.max(sizeOf.get(id).w, bandSize(id).w, dock.unitWidth(id));
677
+ const unitDown = (id) => Math.max(sizeOf.get(id).h, dock.unitDown(id));
678
+
679
+ // Subtree widths (bottom-up): each subtree occupies its own column block.
680
+ const subtreeW = new Map();
681
+ const computeSubtree = (id) => {
682
+ if (subtreeW.has(id)) return subtreeW.get(id);
683
+ const children = childrenOf.get(id) || [];
684
+ const childrenW = children.length
685
+ ? children.reduce((sum, child) => sum + computeSubtree(child), 0) + gapX * (children.length - 1)
686
+ : 0;
687
+ const w = Math.max(unitW(id), childrenW);
688
+ subtreeW.set(id, w);
689
+ return w;
690
+ };
691
+ for (const id of agentIds) computeSubtree(id);
692
+
693
+ const positions = {};
694
+
695
+ // Emitted positions are TOP-LEFT corners (the graph-map/UI convention):
696
+ // centers are used for all centering math, then shifted by -w/2 at write.
697
+ const placeBandRows = (band, cx, bandTop) => {
698
+ let rowY = bandTop;
699
+ for (const row of band.rows) {
700
+ let itemX = cx - row.w / 2;
701
+ for (const item of row.items) {
702
+ positions[item.id] = { x: itemX, y: rowY };
703
+ itemX += item.w + gapX;
704
+ }
705
+ rowY += row.h + gapY;
706
+ }
707
+ };
708
+
709
+ const placeAgent = (id, cx, bandTop, rowY = null) => {
710
+ const band = bandSize(id);
711
+ const y = rowY !== null ? rowY : bandTop + band.h + (band.h > 0 ? gapY : 0);
712
+ positions[id] = { x: cx - sizeOf.get(id).w / 2, y };
713
+ placeBandRows(band, cx, bandTop);
714
+ const children = childrenOf.get(id) || [];
715
+ if (!children.length) return;
716
+ const total = children.reduce((sum, child) => sum + subtreeW.get(child), 0) + gapX * (children.length - 1);
717
+ const childBandTop = y + unitDown(id) + gapY;
718
+ const rowBandH = children.reduce((max, child) => Math.max(max, bandSize(child).h), 0);
719
+ let cursor = cx - total / 2;
720
+ for (const child of children) {
721
+ const childCx = cursor + subtreeW.get(child) / 2;
722
+ placeAgent(child, childCx, childBandTop, childBandTop + rowBandH + (rowBandH > 0 ? gapY : 0));
723
+ cursor += subtreeW.get(child) + gapX;
724
+ }
725
+ };
726
+
727
+ if (mainId) {
728
+ const rootRow = [mainId, ...agentIds.filter((a) => a !== mainId && !(parentOf.get(a) || '') && !gluedSlave.has(a)).sort()];
729
+ const rowBandH = rootRow.reduce((max, a) => Math.max(max, bandSize(a).h), 0);
730
+ const y = originY + rowBandH + (rowBandH > 0 ? gapY : 0);
731
+ const total = rootRow.reduce((sum, a) => sum + subtreeW.get(a), 0) + gapX * (rootRow.length - 1);
732
+ let cursor = originX - total / 2;
733
+ for (const agent of rootRow) {
734
+ const agentCx = cursor + subtreeW.get(agent) / 2;
735
+ placeAgent(agent, agentCx, originY, y);
736
+ cursor += subtreeW.get(agent) + gapX;
737
+ }
738
+ } else {
739
+ // Flat fallback (no main): all agents one root row, unowned assets one
740
+ // matrix band above, both centered on the origin.
741
+ const flatItems = ids
742
+ .filter((id) => !agentIds.includes(id) && !gluedSlave.has(id) && !ownerOf.get(id))
743
+ .map((id) => ({ id, w: sizeOf.get(id).w, h: sizeOf.get(id).h }));
744
+ const flatBand = buildBand(flatItems);
745
+ placeBandRows(flatBand, originX, originY);
746
+ const y = originY + flatBand.h + (flatBand.h > 0 ? gapY : 0);
747
+ const rootRow = agentIds.filter((a) => !gluedSlave.has(a)).sort();
748
+ const total = rootRow.reduce((sum, a) => sum + subtreeW.get(a), 0) + gapX * (rootRow.length - 1);
749
+ let cursor = originX - total / 2;
750
+ for (const agent of rootRow) {
751
+ const agentCx = cursor + subtreeW.get(agent) / 2;
752
+ placeAgent(agent, agentCx, originY, y);
753
+ cursor += subtreeW.get(agent) + gapX;
754
+ }
755
+ }
756
+
757
+ // Glue: positions[slave] = positions[anchor] + offset, exactly (MED-4).
758
+ for (const [headId, members] of dock.chainMembersOf) {
759
+ const headPosition = positions[headId];
760
+ if (!headPosition) continue;
761
+ for (const member of members) {
762
+ if (member.id === headId) continue;
763
+ positions[member.id] = { x: headPosition.x + member.offX, y: headPosition.y + member.offY };
764
+ }
765
+ }
766
+
767
+ // Defensive: every input node must be placed exactly once.
768
+ let fallbackIndex = 0;
769
+ for (const id of ids) {
770
+ if (positions[id]) continue;
771
+ positions[id] = {
772
+ x: originX + (fallbackIndex % 4) * (GRAPH_SLOT_W + gapX),
773
+ y: originY + Math.floor(fallbackIndex / 4) * (GRAPH_SLOT_H + gapY),
774
+ };
775
+ fallbackIndex += 1;
776
+ }
777
+ return positions;
778
+ }