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.
- package/CHANGELOG.md +49 -0
- package/README-CN.md +4 -2
- package/README.md +23 -4
- package/package.json +17 -2
- package/src/generator.js +19 -6
- package/src/index.js +531 -38
- package/src/prompts.js +4 -4
- package/src/ui/dist/assets/AgentsRoute-BGGs90wH.js +7 -0
- package/src/ui/dist/assets/Assistant-Bold-gm-uSS1B.woff2 +0 -0
- package/src/ui/dist/assets/Assistant-Medium-DrcxCXg3.woff2 +0 -0
- package/src/ui/dist/assets/Assistant-Regular-DVxZuzxb.woff2 +0 -0
- package/src/ui/dist/assets/Assistant-SemiBold-SCI4bEL9.woff2 +0 -0
- package/src/ui/dist/assets/Combination-DhBLEmof.js +42 -0
- package/src/ui/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/src/ui/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/src/ui/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/src/ui/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/src/ui/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/src/ui/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/src/ui/dist/assets/RolesRoute-DnodCSz0.js +2 -0
- package/src/ui/dist/assets/RuntimePicker-CucD8TGn.js +7 -0
- package/src/ui/dist/assets/SettingsRoute-DquMWjac.js +2 -0
- package/src/ui/dist/assets/TaskList-Bxc0Uktx.js +7 -0
- package/src/ui/dist/assets/TerminalDrawer-CDusYltc.js +36 -0
- package/src/ui/dist/assets/WorkflowRoute-BN8yHmUs.css +1 -0
- package/src/ui/dist/assets/WorkflowRoute-D-0wVDpY.js +334 -0
- package/src/ui/dist/assets/abnfDiagram-VRR7QNED-zkRKkaxO.js +2 -0
- package/src/ui/dist/assets/apl-B4CMkyY2.js +2 -0
- package/src/ui/dist/assets/ar-SA-G6X2FPQ2-BiHaArx5.js +11 -0
- package/src/ui/dist/assets/arc-CBnLtOFO.js +2 -0
- package/src/ui/dist/assets/architectureDiagram-ZJ3FMSHR-DWXRXsh1.js +37 -0
- package/src/ui/dist/assets/asciiarmor-Df11BRmG.js +2 -0
- package/src/ui/dist/assets/asn1-EdZsLKOL.js +2 -0
- package/src/ui/dist/assets/asterisk-B-8jnY81.js +2 -0
- package/src/ui/dist/assets/az-AZ-76LH7QW2-CiT05voO.js +2 -0
- package/src/ui/dist/assets/bg-BG-XCXSNQG7-L-UM9tNf.js +6 -0
- package/src/ui/dist/assets/blockDiagram-677ZJIJ3-BIyh3Bry.js +133 -0
- package/src/ui/dist/assets/bn-BD-2XOGV67Q-NdgbA0bC.js +6 -0
- package/src/ui/dist/assets/brainfuck-C4LP7Hcl.js +2 -0
- package/src/ui/dist/assets/c4Diagram-LMCZKHZV-CA54vWWM.js +11 -0
- package/src/ui/dist/assets/ca-ES-6MX7JW3Y-BKZZsQ5J.js +9 -0
- package/src/ui/dist/assets/channel-Da_-zcAu.js +2 -0
- package/src/ui/dist/assets/chunk-2Q5K7J3B-DE4cxr1v.js +2 -0
- package/src/ui/dist/assets/chunk-32BRIVSS-i7FwbEU9.js +2 -0
- package/src/ui/dist/assets/chunk-5VM5RSS4-BhIcOYSu.js +16 -0
- package/src/ui/dist/assets/chunk-EX3LRPZG-Ci9zZRIs.js +232 -0
- package/src/ui/dist/assets/chunk-JWPE2WC7-BFdkcz-G.js +2 -0
- package/src/ui/dist/assets/chunk-MOJQB5TN-Dzmapl_S.js +89 -0
- package/src/ui/dist/assets/chunk-RYQCIY6F-RTmp7FOy.js +2 -0
- package/src/ui/dist/assets/chunk-V7JOEXUC-DInU3v3D.js +207 -0
- package/src/ui/dist/assets/chunk-VR4S4FIN-BHoyGBQO.js +2 -0
- package/src/ui/dist/assets/chunk-XXDRQBXY-DfeLZNZx.js +2 -0
- package/src/ui/dist/assets/classDiagram-OUVF2IWQ-FKbUS0bu.js +2 -0
- package/src/ui/dist/assets/classDiagram-v2-EOCWNBFH-FKbUS0bu.js +2 -0
- package/src/ui/dist/assets/clike-B9uivgTg.js +2 -0
- package/src/ui/dist/assets/clojure-BMjYHr_A.js +2 -0
- package/src/ui/dist/assets/cmake-BQqOBYOt.js +2 -0
- package/src/ui/dist/assets/cobol-CWcv1MsR.js +2 -0
- package/src/ui/dist/assets/coffeescript-S37ZYGWr.js +2 -0
- package/src/ui/dist/assets/commonlisp-DBKNyK5s.js +2 -0
- package/src/ui/dist/assets/cose-bilkent-JH36ORCC-_5PUJ615.js +2 -0
- package/src/ui/dist/assets/crystal-SjHAIU92.js +2 -0
- package/src/ui/dist/assets/cs-CZ-2BRQDIVT-DnkfnhDU.js +12 -0
- package/src/ui/dist/assets/css-BnMrqG3P.js +2 -0
- package/src/ui/dist/assets/cynefin-VYW2F7L2-CmkcFv-b.js +179 -0
- package/src/ui/dist/assets/cynefinDiagram-TSTJHNR4-D72VZn8H.js +63 -0
- package/src/ui/dist/assets/cypher-C_CwsFkJ.js +2 -0
- package/src/ui/dist/assets/cytoscape.esm-DTSO7Bv0.js +332 -0
- package/src/ui/dist/assets/d-pRatUO7H.js +2 -0
- package/src/ui/dist/assets/da-DK-5WZEPLOC-C54tQUgd.js +6 -0
- package/src/ui/dist/assets/dagre-VKFMJZFB-0BsAkrmo.js +5 -0
- package/src/ui/dist/assets/de-DE-XR44H4JA-DGfZlR10.js +9 -0
- package/src/ui/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
- package/src/ui/dist/assets/diagram-FQU43EPY-C1ZJDOca.js +4 -0
- package/src/ui/dist/assets/diagram-G47NLZAW-D3pr3FSP.js +25 -0
- package/src/ui/dist/assets/diagram-NH7WQ7WH-Bwry0NV-.js +25 -0
- package/src/ui/dist/assets/diagram-OA4YK3LP-L6g09cmV.js +31 -0
- package/src/ui/dist/assets/diagram-WEI45ONY-Bh1puYVX.js +42 -0
- package/src/ui/dist/assets/diff-DbItnlRl.js +2 -0
- package/src/ui/dist/assets/directory-open-01563666-DWU9wJ6I.js +2 -0
- package/src/ui/dist/assets/directory-open-4ed118d0-CunoC1EB.js +2 -0
- package/src/ui/dist/assets/dockerfile-BKs6k2Af.js +2 -0
- package/src/ui/dist/assets/dtd-DF_7sFjM.js +2 -0
- package/src/ui/dist/assets/dylan-DwRh75JA.js +2 -0
- package/src/ui/dist/assets/ebnf-CDyGwa7X.js +2 -0
- package/src/ui/dist/assets/ebnfDiagram-CCIWWBDH-BC_mBRR5.js +2 -0
- package/src/ui/dist/assets/ecl-Cabwm37j.js +2 -0
- package/src/ui/dist/assets/eiffel-CnydiIhH.js +2 -0
- package/src/ui/dist/assets/el-GR-BZB4AONW-DsnwWweL.js +11 -0
- package/src/ui/dist/assets/elm-vLlmbW-K.js +2 -0
- package/src/ui/dist/assets/erDiagram-Q63AITRT-Bkxam36l.js +86 -0
- package/src/ui/dist/assets/erlang-BNw1qcRV.js +2 -0
- package/src/ui/dist/assets/es-ES-U4NZUMDT-CRxBcQgC.js +10 -0
- package/src/ui/dist/assets/eu-ES-A7QVB2H4-CTd_E7YR.js +12 -0
- package/src/ui/dist/assets/excalidraw-default-bMWIgNCy.svg +1 -0
- package/src/ui/dist/assets/fa-IR-HGAKTJCU-DO56k7eG.js +9 -0
- package/src/ui/dist/assets/factor-kuTfRLto.js +2 -0
- package/src/ui/dist/assets/fcl-Kvtd6kyn.js +2 -0
- package/src/ui/dist/assets/fi-FI-Z5N7JZ37-DEDCF9WB.js +7 -0
- package/src/ui/dist/assets/file-open-002ab408-DIuFHtCF.js +2 -0
- package/src/ui/dist/assets/file-open-7c801643-684qeFg4.js +2 -0
- package/src/ui/dist/assets/file-save-3189631c-C1wFhQhH.js +2 -0
- package/src/ui/dist/assets/file-save-745eba88-Bb9F9Kg7.js +2 -0
- package/src/ui/dist/assets/flowDiagram-23GEKE2U-C3SAEao7.js +157 -0
- package/src/ui/dist/assets/forth-Ffai-XNe.js +2 -0
- package/src/ui/dist/assets/fortran-DYz_wnZ1.js +2 -0
- package/src/ui/dist/assets/fr-FR-RHASNOE6-DE2nPyDt.js +10 -0
- package/src/ui/dist/assets/ganttDiagram-NO4QXBWP-U-J_Xi-_.js +293 -0
- package/src/ui/dist/assets/gas-Bneqetm1.js +2 -0
- package/src/ui/dist/assets/gherkin-heZmZLOM.js +2 -0
- package/src/ui/dist/assets/gitGraphDiagram-IHSO6WYX-BADVbD-a.js +107 -0
- package/src/ui/dist/assets/gl-ES-HMX3MZ6V-mCqVgQHC.js +11 -0
- package/src/ui/dist/assets/graph-C9eacEi8.js +2 -0
- package/src/ui/dist/assets/groovy-D9Dt4D0W.js +2 -0
- package/src/ui/dist/assets/haskell-Cw1EW3IL.js +2 -0
- package/src/ui/dist/assets/haxe-H-WmDvRZ.js +2 -0
- package/src/ui/dist/assets/he-IL-6SHJWFNN-KnaLrCzw.js +11 -0
- package/src/ui/dist/assets/hi-IN-IWLTKZ5I-wuqiaaWt.js +5 -0
- package/src/ui/dist/assets/http-DBlCnlav.js +2 -0
- package/src/ui/dist/assets/hu-HU-A5ZG7DT2-DLg_zm22.js +8 -0
- package/src/ui/dist/assets/id-ID-SAP4L64H-MB2mXqcR.js +11 -0
- package/src/ui/dist/assets/idl-BEugSyMb.js +2 -0
- package/src/ui/dist/assets/image-blob-reduce.esm-HwCm7IZ0.js +8 -0
- package/src/ui/dist/assets/index-39SJtrt0.js +2 -0
- package/src/ui/dist/assets/index-BQ0LIrW-.js +2 -0
- package/src/ui/dist/assets/index-BcAUh2Nm.css +1 -0
- package/src/ui/dist/assets/index-BcThOK-i.js +331 -0
- package/src/ui/dist/assets/index-BnxV3k_S.js +3 -0
- package/src/ui/dist/assets/index-C7MZBHbT.js +117 -0
- package/src/ui/dist/assets/index-CCvKI2rM.js +2 -0
- package/src/ui/dist/assets/index-CZvmcCab.js +8 -0
- package/src/ui/dist/assets/index-Cp3hLKNl.js +2 -0
- package/src/ui/dist/assets/index-D7ayFxcx.js +2 -0
- package/src/ui/dist/assets/index-DAYxSQGU.js +2 -0
- package/src/ui/dist/assets/index-DD-HeMV0.css +1 -0
- package/src/ui/dist/assets/index-DPMxd3Qn.js +2 -0
- package/src/ui/dist/assets/index-DSe2jx3k.js +4 -0
- package/src/ui/dist/assets/index-DUirCkgW.js +2 -0
- package/src/ui/dist/assets/index-DaKPQ2hN.js +159 -0
- package/src/ui/dist/assets/index-DzpuR0Jg.js +2 -0
- package/src/ui/dist/assets/index-POndwsCI.js +2 -0
- package/src/ui/dist/assets/index-R8NM9I_9.js +2 -0
- package/src/ui/dist/assets/index-TCDuDhBj.js +2 -0
- package/src/ui/dist/assets/index-TckzASdM.js +2 -0
- package/src/ui/dist/assets/index-Yi5ytFFr.js +2 -0
- package/src/ui/dist/assets/infoDiagram-FWYZ7A6U-CiGBlFqH.js +3 -0
- package/src/ui/dist/assets/init-Gi6I4Gst.js +2 -0
- package/src/ui/dist/assets/ishikawaDiagram-FXEZZL3T-B1aheRbz.js +71 -0
- package/src/ui/dist/assets/it-IT-JPQ66NNP-DT3HSU7X.js +12 -0
- package/src/ui/dist/assets/ja-JP-DBVTYXUO-CsfJjjWt.js +9 -0
- package/src/ui/dist/assets/javascript-iXu5QeM3.js +2 -0
- package/src/ui/dist/assets/journeyDiagram-5HDEW3XC-DtnJPxub.js +140 -0
- package/src/ui/dist/assets/julia-DuME0IfC.js +2 -0
- package/src/ui/dist/assets/kaa-6HZHGXH3-DQz-1-Gn.js +2 -0
- package/src/ui/dist/assets/kab-KAB-ZGHBKWFO-CaH-LSoK.js +9 -0
- package/src/ui/dist/assets/kanban-definition-HUTT4EX6-CZatSm3e.js +90 -0
- package/src/ui/dist/assets/katex-C5jXJg4s.js +258 -0
- package/src/ui/dist/assets/kk-KZ-P5N5QNE5-BMXmcm55.js +2 -0
- package/src/ui/dist/assets/km-KH-HSX4SM5Z-ZeoJNBpN.js +12 -0
- package/src/ui/dist/assets/ko-KR-MTYHY66A-BeaLRKph.js +10 -0
- package/src/ui/dist/assets/ku-TR-6OUDTVRD-B_28KrkR.js +10 -0
- package/src/ui/dist/assets/layout-DEXfKzaS.js +2 -0
- package/src/ui/dist/assets/linear-CpEDnQRC.js +2 -0
- package/src/ui/dist/assets/livescript-BwQOo05w.js +2 -0
- package/src/ui/dist/assets/lt-LT-XHIRWOB4-BxDcnUhp.js +4 -0
- package/src/ui/dist/assets/lua-VAEuO923.js +2 -0
- package/src/ui/dist/assets/lv-LV-5QDEKY6T-DpKDl1_y.js +8 -0
- package/src/ui/dist/assets/map-Czzmt4hB.js +2 -0
- package/src/ui/dist/assets/mathematica-DTrFuWx2.js +2 -0
- package/src/ui/dist/assets/mbox-CNhZ1qSd.js +2 -0
- package/src/ui/dist/assets/mermaidLoader-CBO31t0J.js +305 -0
- package/src/ui/dist/assets/mermaidLoader-CEK31ho9.css +1 -0
- package/src/ui/dist/assets/mindmap-definition-LN4V7U3C-CQ0TccEr.js +97 -0
- package/src/ui/dist/assets/mirc-CjQqDB4T.js +2 -0
- package/src/ui/dist/assets/mllike-CXdrOF99.js +2 -0
- package/src/ui/dist/assets/modelica-Dc1JOy9r.js +2 -0
- package/src/ui/dist/assets/mr-IN-CRQNXWMA-BKCZT58p.js +14 -0
- package/src/ui/dist/assets/mscgen-BA5vi2Kp.js +2 -0
- package/src/ui/dist/assets/mumps-BT43cFF4.js +2 -0
- package/src/ui/dist/assets/my-MM-5M5IBNSE-D34k0G9T.js +2 -0
- package/src/ui/dist/assets/nb-NO-T6EIAALU-DXr_KW8J.js +11 -0
- package/src/ui/dist/assets/nginx-DdIZxoE0.js +2 -0
- package/src/ui/dist/assets/nl-NL-IS3SIHDZ-C1L0aVDR.js +9 -0
- package/src/ui/dist/assets/nn-NO-6E72VCQL-CLWVSxbZ.js +9 -0
- package/src/ui/dist/assets/nsis-LdVXkNf5.js +2 -0
- package/src/ui/dist/assets/ntriples-BfvgReVJ.js +2 -0
- package/src/ui/dist/assets/oc-FR-POXYY2M6-nf8_J3eR.js +9 -0
- package/src/ui/dist/assets/octave-Ck1zUtKM.js +2 -0
- package/src/ui/dist/assets/ordinal-Cboi1Yqb.js +2 -0
- package/src/ui/dist/assets/oz-BzwKVEFT.js +2 -0
- package/src/ui/dist/assets/pa-IN-N4M65BXN-CAMJ-xd6.js +5 -0
- package/src/ui/dist/assets/pascal--L3eBynH.js +2 -0
- package/src/ui/dist/assets/pegDiagram-2B236MQR-Bj2RcmRd.js +2 -0
- package/src/ui/dist/assets/percentages-BXMCSKIN-s9yWFdr6.js +160 -0
- package/src/ui/dist/assets/perl-CdXCOZ3F.js +2 -0
- package/src/ui/dist/assets/pica-BGv_AhAB.js +8 -0
- package/src/ui/dist/assets/pieDiagram-ENE6RG2P-obpzz8dC.js +40 -0
- package/src/ui/dist/assets/pig-CevX1Tat.js +2 -0
- package/src/ui/dist/assets/pl-PL-T2D74RX3-DKJ5H85v.js +10 -0
- package/src/ui/dist/assets/powershell-CFHJl5sT.js +2 -0
- package/src/ui/dist/assets/properties-C78fOPTZ.js +2 -0
- package/src/ui/dist/assets/protobuf-ChK-085T.js +2 -0
- package/src/ui/dist/assets/{proxy-D0vUbds5.js → proxy-BSmwef4H.js} +2 -2
- package/src/ui/dist/assets/pt-BR-5N22H2LF-B5kow-kN.js +10 -0
- package/src/ui/dist/assets/pt-PT-UZXXM6DQ-B2G34fso.js +10 -0
- package/src/ui/dist/assets/pug-DeIclll2.js +2 -0
- package/src/ui/dist/assets/puppet-DMA9R1ak.js +2 -0
- package/src/ui/dist/assets/python-BuPzkPfP.js +2 -0
- package/src/ui/dist/assets/q-pXgVlZs6.js +2 -0
- package/src/ui/dist/assets/quadrantDiagram-ABIIQ3AL-Cl-T7NkJ.js +8 -0
- package/src/ui/dist/assets/r-B6wPVr8A.js +2 -0
- package/src/ui/dist/assets/railroadDiagram-RFXS5EU6-DMq82RHg.js +2 -0
- package/src/ui/dist/assets/{refresh-cw-JNmExijd.js → refresh-cw-CbFd7guV.js} +2 -2
- package/src/ui/dist/assets/requirementDiagram-TGXJPOKE-tGInMDgU.js +85 -0
- package/src/ui/dist/assets/ro-RO-JPDTUUEW-BwLdZW91.js +12 -0
- package/src/ui/dist/assets/roundRect-0PYZxl1G.js +2 -0
- package/src/ui/dist/assets/rpm-CTu-6PCP.js +2 -0
- package/src/ui/dist/assets/ru-RU-B4JR7IUQ-DpdAgIJc.js +10 -0
- package/src/ui/dist/assets/ruby-B2Rjki9n.js +2 -0
- package/src/ui/dist/assets/runtimeBrand-BoKPimsz.js +2 -0
- package/src/ui/dist/assets/sankeyDiagram-HTMAVEWB-C7X-ET4m.js +41 -0
- package/src/ui/dist/assets/sas-B4kiWyti.js +2 -0
- package/src/ui/dist/assets/save-D1CXHwBw.js +12 -0
- package/src/ui/dist/assets/scheme-C41bIUwD.js +2 -0
- package/src/ui/dist/assets/sequenceDiagram-DBY2YBRQ-Bn24i_nY.js +163 -0
- package/src/ui/dist/assets/settings-2-DtSNAAcU.js +22 -0
- package/src/ui/dist/assets/shell-CjFT_Tl9.js +2 -0
- package/src/ui/dist/assets/shield-check-BVbJ8T_N.js +7 -0
- package/src/ui/dist/assets/si-LK-N5RQ5JYF-naJaUy5u.js +2 -0
- package/src/ui/dist/assets/sieve-C3Gn_uJK.js +2 -0
- package/src/ui/dist/assets/simple-mode-GW_nhZxv.js +2 -0
- package/src/ui/dist/assets/sizeCapture-X5ZJPWSS-DAQHfm82.js +2 -0
- package/src/ui/dist/assets/sk-SK-C5VTKIMK-1NPz1J_d.js +7 -0
- package/src/ui/dist/assets/sl-SI-NN7IZMDC-D8RfmNQw.js +7 -0
- package/src/ui/dist/assets/smalltalk-CnHTOXQT.js +2 -0
- package/src/ui/dist/assets/solr-DehyRSwq.js +2 -0
- package/src/ui/dist/assets/sparql-DkYu6x3z.js +2 -0
- package/src/ui/dist/assets/spreadsheet-BCZA_wO0.js +2 -0
- package/src/ui/dist/assets/sql-D0XecflT.js +2 -0
- package/src/ui/dist/assets/stateDiagram-2N3HPSRC-Ck48D1ck.js +2 -0
- package/src/ui/dist/assets/stateDiagram-v2-6OUMAXLB-CdfRCJAJ.js +2 -0
- package/src/ui/dist/assets/stex-C3f8Ysf7.js +2 -0
- package/src/ui/dist/assets/style-C3QI7VPf.css +1 -0
- package/src/ui/dist/assets/stylus-B533Al4x.js +2 -0
- package/src/ui/dist/assets/subset-shared.chunk-eAtLHmbi.js +85 -0
- package/src/ui/dist/assets/subset-worker.chunk-DeHCTnTk.js +2 -0
- package/src/ui/dist/assets/sv-SE-XGPEYMSR-BeOYCU-8.js +11 -0
- package/src/ui/dist/assets/swift-BzpIVaGY.js +2 -0
- package/src/ui/dist/assets/swimlanes-5IMT3BWC-DOy22UjP.js +3 -0
- package/src/ui/dist/assets/swimlanesDiagram-G3AALYLV-sicPbVOg.js +9 -0
- package/src/ui/dist/assets/ta-IN-2NMHFXQM-CtS68WDd.js +10 -0
- package/src/ui/dist/assets/tcl-DVfN8rqt.js +2 -0
- package/src/ui/dist/assets/{terminal-BBQvnOAK.js → terminal-DEby5Y_P.js} +2 -2
- package/src/ui/dist/assets/terminalControl-BkFeDVXo.js +134 -0
- package/src/ui/dist/assets/textile-CnDTJFAw.js +2 -0
- package/src/ui/dist/assets/th-TH-HPSO5L25-DCHY9-Co.js +3 -0
- package/src/ui/dist/assets/tiddlywiki-DO-Gjzrf.js +2 -0
- package/src/ui/dist/assets/tiki-DGYXhP31.js +2 -0
- package/src/ui/dist/assets/timeline-definition-FHXFAJF6-DVkG72qO.js +121 -0
- package/src/ui/dist/assets/toml-Bm5Em-hy.js +2 -0
- package/src/ui/dist/assets/tr-TR-DEFEU3FU-y5-vjTv8.js +8 -0
- package/src/ui/dist/assets/troff-wAsdV37c.js +2 -0
- package/src/ui/dist/assets/ttcn-CfJYG6tj.js +2 -0
- package/src/ui/dist/assets/ttcn-cfg-B9xdYoR4.js +2 -0
- package/src/ui/dist/assets/turtle-B1tBg_DP.js +2 -0
- package/src/ui/dist/assets/uk-UA-QMV73CPH-BRbR5It6.js +7 -0
- package/src/ui/dist/assets/useReducedMotion-D07XP52Q.js +12 -0
- package/src/ui/dist/assets/vb-CmGdzxic.js +2 -0
- package/src/ui/dist/assets/vbscript-BuJXcnF6.js +2 -0
- package/src/ui/dist/assets/velocity-D8B20fx6.js +2 -0
- package/src/ui/dist/assets/vennDiagram-L72KCM5P-DwMwBlf-.js +35 -0
- package/src/ui/dist/assets/verilog-C6RDOZhf.js +2 -0
- package/src/ui/dist/assets/vhdl-lSbBsy5d.js +2 -0
- package/src/ui/dist/assets/vi-VN-M7AON7JQ-s6wSBv2s.js +6 -0
- package/src/ui/dist/assets/wardleyDiagram-EHGQE667-CNuuvzEg.js +79 -0
- package/src/ui/dist/assets/webidl-ZXfAyPTL.js +2 -0
- package/src/ui/dist/assets/{x-DoIOGAJz.js → x-B84wq78k.js} +2 -2
- package/src/ui/dist/assets/xquery-DzFWVndE.js +2 -0
- package/src/ui/dist/assets/xychartDiagram-FW5EYKEG-dUpZxmn6.js +8 -0
- package/src/ui/dist/assets/yacas-BJ4BC0dw.js +2 -0
- package/src/ui/dist/assets/z80-Hz9HOZM7.js +2 -0
- package/src/ui/dist/assets/zh-CN-LNUGB5OW-Cu_-POQN.js +11 -0
- package/src/ui/dist/assets/zh-HK-E62DVLB3-BCx4W4qZ.js +2 -0
- package/src/ui/dist/assets/zh-TW-RAJ6MFWO-02wx_Mrj.js +10 -0
- package/src/ui/dist/icons/excalidraw.svg +1 -0
- package/src/ui/dist/icons/markdown.svg +1 -0
- package/src/ui/dist/index.html +2 -2
- package/src/ui/package.json +10 -0
- package/src/ui/playwright.config.ts +36 -0
- package/src/ui/pnpm-workspace.yaml +2 -0
- package/src/ui/public/icons/excalidraw.svg +1 -0
- package/src/ui/public/icons/markdown.svg +1 -0
- package/src/ui/tsconfig.json +2 -1
- package/src/ui/vite.config.ts +1 -0
- package/src/wf-ui-server/__tests__/a2a-store.test.mjs +432 -15
- package/src/wf-ui-server/__tests__/action-registry-loader.test.mjs +156 -0
- package/src/wf-ui-server/__tests__/claude-stream-json.test.mjs +397 -0
- package/src/wf-ui-server/__tests__/codex-appserver.test.mjs +392 -0
- package/src/wf-ui-server/__tests__/component-node-api.test.mjs +881 -0
- package/src/wf-ui-server/__tests__/component-node-store.test.mjs +401 -0
- package/src/wf-ui-server/__tests__/control-plane-acceptance.test.mjs +2027 -0
- package/src/wf-ui-server/__tests__/control-plane-cli-smoke.test.mjs +1014 -0
- package/src/wf-ui-server/__tests__/dispatch-lifecycle.test.mjs +330 -0
- package/src/wf-ui-server/__tests__/effort-compatibility-red-092.test.mjs +151 -0
- package/src/wf-ui-server/__tests__/graph-layout-agent-tree.test.mjs +254 -0
- package/src/wf-ui-server/__tests__/graph-layout-tidy.test.mjs +260 -0
- package/src/wf-ui-server/__tests__/graph-layout.test.mjs +84 -0
- package/src/wf-ui-server/__tests__/node-config-api.test.mjs +384 -0
- package/src/wf-ui-server/__tests__/opencode-server.test.mjs +526 -0
- package/src/wf-ui-server/__tests__/peer-capsule.test.mjs +2 -2
- package/src/wf-ui-server/__tests__/pty-adapter.test.mjs +71 -1
- package/src/wf-ui-server/__tests__/runtime-config.test.mjs +3 -3
- package/src/wf-ui-server/__tests__/runtime-detector.test.mjs +21 -0
- package/src/wf-ui-server/__tests__/runtime-review.test.mjs +828 -0
- package/src/wf-ui-server/__tests__/server.integration.test.mjs +1463 -11
- package/src/wf-ui-server/__tests__/session-cleanup.test.mjs +159 -0
- package/src/wf-ui-server/__tests__/session-registry.test.mjs +52 -3
- package/src/wf-ui-server/__tests__/settings.test.mjs +12 -2
- package/src/wf-ui-server/__tests__/spawn-gate.test.mjs +78 -0
- package/src/wf-ui-server/__tests__/task-group-parser.test.mjs +120 -0
- package/src/wf-ui-server/__tests__/task-group-tag.test.mjs +264 -0
- package/src/wf-ui-server/__tests__/task-parser.test.mjs +49 -3
- package/src/wf-ui-server/__tests__/terminal-context-hardening.test.mjs +122 -0
- package/src/wf-ui-server/__tests__/terminal-context.test.mjs +164 -0
- package/src/wf-ui-server/__tests__/terminal-store.test.mjs +4 -4
- package/src/wf-ui-server/__tests__/wf-browser-api.test.mjs +599 -0
- package/src/wf-ui-server/__tests__/wf-browser-store.test.mjs +184 -0
- package/src/wf-ui-server/__tests__/workflow-action-registry.test.mjs +282 -0
- package/src/wf-ui-server/__tests__/workflow-agent-communication.test.mjs +411 -0
- package/src/wf-ui-server/__tests__/workflow-agent-context.test.mjs +323 -0
- package/src/wf-ui-server/__tests__/workflow-agent-cooperation-permissions.test.mjs +318 -0
- package/src/wf-ui-server/__tests__/workflow-agent-messages-structured.test.mjs +458 -0
- package/src/wf-ui-server/__tests__/workflow-agent-routing.test.mjs +434 -0
- package/src/wf-ui-server/__tests__/workflow-agent-runtime.test.mjs +324 -0
- package/src/wf-ui-server/__tests__/workflow-api-integration.test.mjs +263 -0
- package/src/wf-ui-server/__tests__/workflow-api-phase3.test.mjs +1213 -0
- package/src/wf-ui-server/__tests__/workflow-claude-materialize.test.mjs +209 -0
- package/src/wf-ui-server/__tests__/workflow-cli-registry-manuals.test.mjs +173 -0
- package/src/wf-ui-server/__tests__/workflow-cli-surface.test.mjs +176 -0
- package/src/wf-ui-server/__tests__/workflow-codex-resume-id.test.mjs +556 -0
- package/src/wf-ui-server/__tests__/workflow-cooperation-enforcement.test.mjs +553 -0
- package/src/wf-ui-server/__tests__/workflow-depth-resume-dock.test.mjs +846 -0
- package/src/wf-ui-server/__tests__/workflow-display-node.test.mjs +150 -0
- package/src/wf-ui-server/__tests__/workflow-edge-update.test.mjs +296 -0
- package/src/wf-ui-server/__tests__/workflow-file-cache-lock-watch.test.mjs +380 -0
- package/src/wf-ui-server/__tests__/workflow-file-formats.test.mjs +994 -0
- package/src/wf-ui-server/__tests__/workflow-file-node-actions.test.mjs +294 -0
- package/src/wf-ui-server/__tests__/workflow-goal-actions.test.mjs +357 -0
- package/src/wf-ui-server/__tests__/workflow-goal-task-autoclose.test.mjs +256 -0
- package/src/wf-ui-server/__tests__/workflow-graceful-stop.test.mjs +108 -0
- package/src/wf-ui-server/__tests__/workflow-graph-undo.test.mjs +524 -0
- package/src/wf-ui-server/__tests__/workflow-layout.test.mjs +732 -0
- package/src/wf-ui-server/__tests__/workflow-magnetic-topology.test.mjs +222 -0
- package/src/wf-ui-server/__tests__/workflow-markdown-lock.test.mjs +359 -0
- package/src/wf-ui-server/__tests__/workflow-mcp-hub.test.mjs +174 -0
- package/src/wf-ui-server/__tests__/workflow-node-manual-schema.test.mjs +241 -0
- package/src/wf-ui-server/__tests__/workflow-node-runtime.test.mjs +1991 -0
- package/src/wf-ui-server/__tests__/workflow-node-store-utils.test.mjs +125 -0
- package/src/wf-ui-server/__tests__/workflow-registry-drift.test.mjs +296 -0
- package/src/wf-ui-server/__tests__/workflow-resource-handle-four-side.test.mjs +217 -0
- package/src/wf-ui-server/__tests__/workflow-role-profile.test.mjs +234 -0
- package/src/wf-ui-server/__tests__/workflow-set-model.test.mjs +261 -0
- package/src/wf-ui-server/__tests__/workflow-skill-group-context-enabled.test.mjs +131 -0
- package/src/wf-ui-server/__tests__/workflow-skill-group-enable-disable.test.mjs +186 -0
- package/src/wf-ui-server/__tests__/workflow-skills-hub.test.mjs +125 -0
- package/src/wf-ui-server/__tests__/workflow-state-reconcile.test.mjs +139 -0
- package/src/wf-ui-server/__tests__/workflow-subagent-strategy-matrix.test.mjs +529 -0
- package/src/wf-ui-server/__tests__/workflow-team-flow.test.mjs +278 -0
- package/src/wf-ui-server/__tests__/workflow-terminal-control-live.test.mjs +294 -0
- package/src/wf-ui-server/__tests__/workflow-terminal-control.test.mjs +631 -0
- package/src/wf-ui-server/__tests__/workflow-timer-wakeup.test.mjs +318 -0
- package/src/wf-ui-server/__tests__/workflow-unified-resume.test.mjs +537 -0
- package/src/wf-ui-server/__tests__/workspace-api-hardening.test.mjs +187 -0
- package/src/wf-ui-server/__tests__/workspace-api.test.mjs +382 -0
- package/src/wf-ui-server/__tests__/workspace-reveal.test.mjs +138 -0
- package/src/wf-ui-server/__tests__/workspace-store-hardening.test.mjs +255 -0
- package/src/wf-ui-server/__tests__/workspace-store.test.mjs +208 -0
- package/src/wf-ui-server/__tests__/ws-events.integration.test.mjs +52 -25
- package/src/wf-ui-server/__tests__/ws-terminal.integration.test.mjs +12 -6
- package/src/wf-ui-server/__tests__/ws-wf-browser.integration.test.mjs +488 -0
- package/src/wf-ui-server/a2a-store.mjs +3007 -89
- package/src/wf-ui-server/action-registry.mjs +277 -0
- package/src/wf-ui-server/bridge-store.mjs +276 -0
- package/src/wf-ui-server/chat-driver.mjs +220 -0
- package/src/wf-ui-server/chat-store.mjs +109 -0
- package/src/wf-ui-server/claude-transcript-materializer.mjs +235 -0
- package/src/wf-ui-server/codex-update-prompt.mjs +53 -0
- package/src/wf-ui-server/component-node-store.mjs +689 -0
- package/src/wf-ui-server/composition-api.mjs +436 -0
- package/src/wf-ui-server/control-plane-token.mjs +17 -0
- package/src/wf-ui-server/dispatch-contract.mjs +150 -0
- package/src/wf-ui-server/drivers/claude-stream-json.mjs +371 -0
- package/src/wf-ui-server/drivers/codex-appserver.mjs +513 -0
- package/src/wf-ui-server/drivers/opencode-server.mjs +486 -0
- package/src/wf-ui-server/excalidraw-svg.mjs +141 -0
- package/src/wf-ui-server/file-format-adapters.mjs +511 -0
- package/src/wf-ui-server/file-watcher.mjs +215 -0
- package/src/wf-ui-server/graph-layout.mjs +778 -0
- package/src/wf-ui-server/harness-env.mjs +46 -0
- package/src/wf-ui-server/json-store-utils.mjs +48 -0
- package/src/wf-ui-server/model-capability.mjs +773 -0
- package/src/wf-ui-server/node-config-store.mjs +233 -0
- package/src/wf-ui-server/node-store-utils.mjs +88 -0
- package/src/wf-ui-server/peer-capsule.mjs +56 -1
- package/src/wf-ui-server/pty-adapter.mjs +513 -11
- package/src/wf-ui-server/reveal-foreground.mjs +69 -0
- package/src/wf-ui-server/runtime-detector.mjs +52 -6
- package/src/wf-ui-server/server.mjs +5613 -403
- package/src/wf-ui-server/session-cleanup.mjs +363 -0
- package/src/wf-ui-server/session-registry.mjs +179 -3
- package/src/wf-ui-server/settings.mjs +25 -0
- package/src/wf-ui-server/spawn-gate.mjs +46 -0
- package/src/wf-ui-server/task-parser.mjs +88 -3
- package/src/wf-ui-server/terminal-store.mjs +225 -11
- package/src/wf-ui-server/timer-wakeup-scheduler.mjs +226 -0
- package/src/wf-ui-server/wf-browser-store.mjs +733 -0
- package/src/wf-ui-server/workflow-agent-context.mjs +1731 -0
- package/src/wf-ui-server/workflow-capability-node-store.mjs +676 -0
- package/src/wf-ui-server/workflow-event-node-store.mjs +623 -0
- package/src/wf-ui-server/workflow-goal-node-store.mjs +328 -0
- package/src/wf-ui-server/workflow-graph-store.mjs +623 -0
- package/src/wf-ui-server/workflow-mcp-hub.mjs +286 -0
- package/src/wf-ui-server/workflow-node-runtime.mjs +1767 -0
- package/src/wf-ui-server/workflow-node-settings-store.mjs +339 -0
- package/src/wf-ui-server/workflow-node-types/agent-node.mjs +920 -0
- package/src/wf-ui-server/workflow-node-types/display-node.mjs +151 -0
- package/src/wf-ui-server/workflow-node-types/excalidraw-node.mjs +145 -0
- package/src/wf-ui-server/workflow-node-types/file-node.mjs +484 -0
- package/src/wf-ui-server/workflow-node-types/github-trigger-node.mjs +207 -0
- package/src/wf-ui-server/workflow-node-types/goal-node.mjs +491 -0
- package/src/wf-ui-server/workflow-node-types/markdown-node.mjs +225 -0
- package/src/wf-ui-server/workflow-node-types/mcp-connector-node.mjs +71 -0
- package/src/wf-ui-server/workflow-node-types/role-profile-store.mjs +191 -0
- package/src/wf-ui-server/workflow-node-types/skill-group-node.mjs +98 -0
- package/src/wf-ui-server/workflow-node-types/timer-node.mjs +607 -0
- package/src/wf-ui-server/workflow-ontology.mjs +681 -0
- package/src/wf-ui-server/workflow-operation-store.mjs +115 -0
- package/src/wf-ui-server/workflow-skills-hub.mjs +366 -0
- package/src/wf-ui-server/workflow-skills-market.mjs +723 -0
- package/src/wf-ui-server/workspace-store.mjs +951 -0
- package/src/wf-ui-server/ws-chat.mjs +299 -0
- package/src/wf-ui-server/ws-events.mjs +45 -20
- package/src/wf-ui-server/ws-terminal.mjs +165 -22
- package/src/wf-ui-server/ws-wf-browser.mjs +419 -0
- package/templates/common/.claude/agents/review-manager.md +33 -29
- package/templates/common/.claude/commands/wf-browser.md +1 -1
- package/templates/common/.claude/commands/wf-help.md +5 -3
- package/templates/common/.claude/commands/wf-init.md +44 -0
- package/templates/common/.claude/commands/wf-learn.md +34 -8
- package/templates/common/.claude/commands/wf-max.md +19 -9
- package/templates/common/.claude/commands/wf-review.md +3 -3
- package/templates/common/.claude/commands/wf-search.md +55 -0
- package/templates/common/.claude/commands/wf-task-archive.md +5 -4
- package/templates/common/.claude/commands/wf-task-list.md +6 -3
- package/templates/common/.claude/commands/wf-task-record.md +10 -2
- package/templates/common/.claude/commands/wf-ui.md +12 -5
- package/templates/common/.claude/commands/wf-update.md +4 -0
- package/templates/common/.claude/commands/wf.md +12 -1
- package/templates/common/.claude/rules/ecc/common.md +10 -1
- package/templates/common/.claude/skills/wf/SKILL.md +36 -0
- package/templates/common/.claude/skills/wf-browser/SKILL.md +427 -122
- package/templates/common/.claude/skills/wf-browser/references/BROWSER_CONTROL_PATTERNS.md +43 -0
- package/templates/common/.claude/skills/wf-init/SKILL.md +33 -0
- package/templates/common/.claude/skills/wf-learn/SKILL.md +106 -26
- package/templates/common/.claude/skills/wf-max/SKILL.md +45 -11
- package/templates/common/.claude/skills/wf-review/SKILL.md +26 -54
- package/templates/common/.claude/skills/wf-search/SKILL.md +122 -0
- package/templates/common/.claude/skills/wf-search/references/evidence-contract.md +77 -0
- package/templates/common/.claude/skills/wf-search/references/policies/compare.md +38 -0
- package/templates/common/.claude/skills/wf-search/references/policies/fact.md +39 -0
- package/templates/common/.claude/skills/wf-search/references/policies/troubleshoot.md +39 -0
- package/templates/common/.claude/skills/wf-search/references/policies/verify.md +44 -0
- package/templates/common/.claude/skills/wf-search/references/source-routing.md +40 -0
- package/templates/common/.claude/skills/wf-search/references/tool-adapters.md +38 -0
- package/templates/common/.claude/skills/wf-ui/SKILL.md +146 -12
- package/templates/common/.claude/skills/wf-update/SKILL.md +5 -1
- package/templates/common/.harness-version +188 -85
- package/templates/common/.opencode/agents/review-manager.md +31 -27
- package/templates/common/.opencode/commands/wf-browser.md +1 -1
- package/templates/common/.opencode/commands/wf-help.md +5 -3
- package/templates/common/.opencode/commands/wf-init.md +44 -0
- package/templates/common/.opencode/commands/wf-learn.md +34 -8
- package/templates/common/.opencode/commands/wf-max.md +19 -9
- package/templates/common/.opencode/commands/wf-review.md +3 -3
- package/templates/common/.opencode/commands/wf-search.md +55 -0
- package/templates/common/.opencode/commands/wf-task-archive.md +5 -7
- package/templates/common/.opencode/commands/wf-task-list.md +6 -6
- package/templates/common/.opencode/commands/wf-task-record.md +10 -5
- package/templates/common/.opencode/commands/wf-ui.md +12 -5
- package/templates/common/.opencode/commands/wf-update.md +4 -0
- package/templates/common/.opencode/commands/wf.md +12 -1
- package/templates/common/CLAUDE.md +61 -3
- package/templates/common/Harness/MEMORY.md +8 -5
- package/templates/common/Harness/README.md +14 -10
- package/templates/common/Harness/a2a/action-registry.json +4397 -0
- package/templates/common/Harness/a2a/role-graph.json +16 -3
- package/templates/common/Harness/a2a/skills/terminal-control.json +45 -1
- package/templates/common/Harness/a2a/skills/wf-ui-map.json +50 -0
- package/templates/common/Harness/a2a/skills/workflow-agent-node.json +143 -0
- package/templates/common/Harness/a2a/skills/workflow-context.json +95 -0
- package/templates/common/Harness/a2a/skills/workflow-diagram-node.json +60 -0
- package/templates/common/Harness/a2a/skills/workflow-display-node.json +69 -0
- package/templates/common/Harness/a2a/skills/workflow-file-node.json +92 -0
- package/templates/common/Harness/a2a/skills/workflow-goal-node.json +76 -0
- package/templates/common/Harness/a2a/skills/workflow-markdown-node.json +79 -0
- package/templates/common/Harness/a2a/skills/workflow-mcp-connector-node.json +59 -0
- package/templates/common/Harness/a2a/skills/workflow-node-actions.json +105 -0
- package/templates/common/Harness/a2a/skills/workflow-node-map.json +71 -0
- package/templates/common/Harness/a2a/skills/workflow-ontology.json +54 -0
- package/templates/common/Harness/a2a/skills/workflow-resource-node.json +68 -0
- package/templates/common/Harness/a2a/skills/workflow-skill-group-node.json +56 -0
- package/templates/common/Harness/a2a/skills/workflow-timer-node.json +81 -0
- package/templates/common/Harness/a2a/workflows.json +33 -0
- package/templates/common/Harness/ownership.manifest.json +263 -2
- package/templates/common/Harness/project/architecture.md +499 -0
- package/templates/common/Harness/research/README.md +20 -0
- package/templates/common/Harness/scripts/README.md +70 -6
- package/templates/common/Harness/scripts/a2a-terminal.mjs +12 -4
- package/templates/common/Harness/scripts/context-budget.mjs +7 -3
- package/templates/common/Harness/scripts/lib/memory-context.mjs +400 -0
- package/templates/common/Harness/scripts/lib/path-scope.mjs +77 -0
- package/templates/common/Harness/scripts/lib/research-policy.mjs +99 -0
- package/templates/common/Harness/scripts/lib/wf-search.mjs +189 -0
- package/templates/common/Harness/scripts/memory-context.mjs +63 -0
- package/templates/common/Harness/scripts/research-policy.mjs +27 -0
- package/templates/common/Harness/scripts/task-context.mjs +547 -0
- package/templates/common/Harness/scripts/task-group-index.mjs +242 -0
- package/templates/common/Harness/scripts/task-state.mjs +659 -95
- package/templates/common/Harness/scripts/validate-harness.mjs +70 -28
- package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +24 -0
- package/templates/common/Harness/scripts/wf-remove.mjs +10 -0
- package/templates/common/Harness/scripts/wf-search.mjs +10 -0
- package/templates/common/Harness/scripts/wf-ui-control.mjs +3144 -0
- package/templates/common/Harness/scripts/wf-update-check.mjs +274 -51
- package/templates/common/Harness/scripts/wf-update-runner.mjs +53 -16
- package/templates/common/Harness/settings.json +10 -0
- package/templates/common/Harness/specs/guides/SETUP.md +4 -3
- package/templates/common/Harness/specs/protocols/HARNESS_BRIDGE.md +429 -48
- package/templates/common/Harness/specs/protocols/MEMORY_PROTOCOL.md +24 -0
- package/templates/common/Harness/specs/protocols/TASK_ARCHIVE.md +14 -11
- package/templates/common/Harness/specs/runtime/command-surface.json +28 -0
- package/templates/common/Harness/specs/runtime/context-loading.md +6 -0
- package/templates/common/Harness/specs/runtime/dispatch.md +9 -1
- package/templates/common/Harness/specs/runtime/subagents.md +20 -5
- package/templates/common/Harness/specs/workflows/WF-AUTO-SPARK.md +19 -0
- package/templates/common/Harness/specs/workflows/WF-AUTO.md +21 -0
- package/templates/common/Harness/specs/workflows/WF-KERNEL.md +4 -2
- package/templates/common/Harness/specs/workflows/WF-MAX.md +39 -12
- package/templates/common/Harness/specs/workflows/WF-STATE.md +62 -14
- package/templates/common/Harness/specs/workflows/WF.md +32 -0
- package/templates/common/Harness/tasks/INDEX.json +7 -0
- package/templates/common/Harness/tasks/INDEX.md +12 -0
- package/templates/common/Harness/tasks/_template/PLAN.md +6 -0
- package/templates/common/Harness/tasks/_template/PROGRESS.md +5 -0
- package/templates/common/Harness/tasks/_template/STATE.json +22 -0
- package/templates/common/Harness/templates/TEST_PLAN.template.md +5 -4
- package/templates/common/Harness/templates/UI_CONTRACT.template.md +7 -5
- package/templates/common/Harness/wf-browser/README.md +69 -0
- package/templates/common/README.md +17 -0
- package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js +0 -17
- package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js.map +0 -1
- package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js +0 -7
- package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js.map +0 -1
- package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js +0 -12
- package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js.map +0 -1
- package/src/ui/dist/assets/TaskList-BN4r8s1i.js +0 -50
- package/src/ui/dist/assets/TaskList-BN4r8s1i.js.map +0 -1
- package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js +0 -74
- package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js.map +0 -1
- package/src/ui/dist/assets/WorkflowRoute-BnuhLJ6X.css +0 -1
- package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js +0 -39
- package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js.map +0 -1
- package/src/ui/dist/assets/index-BOVYWntB.js +0 -117
- package/src/ui/dist/assets/index-BOVYWntB.js.map +0 -1
- package/src/ui/dist/assets/index-DaUObq0H.css +0 -1
- package/src/ui/dist/assets/maximize-2-LNrr_rKr.js +0 -7
- package/src/ui/dist/assets/maximize-2-LNrr_rKr.js.map +0 -1
- package/src/ui/dist/assets/plus-QuTJyoT3.js +0 -7
- package/src/ui/dist/assets/plus-QuTJyoT3.js.map +0 -1
- package/src/ui/dist/assets/proxy-D0vUbds5.js.map +0 -1
- package/src/ui/dist/assets/refresh-cw-JNmExijd.js.map +0 -1
- package/src/ui/dist/assets/terminal-BBQvnOAK.js.map +0 -1
- package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js +0 -7
- package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js.map +0 -1
- package/src/ui/dist/assets/x-DoIOGAJz.js.map +0 -1
- package/src/ui/pnpm-lock.yaml +0 -1582
- package/src/ui/src/App.tsx +0 -86
- package/src/ui/src/api.ts +0 -93
- package/src/ui/src/components/AgentsRoute.tsx +0 -503
- package/src/ui/src/components/Footer.tsx +0 -69
- package/src/ui/src/components/Header.tsx +0 -175
- package/src/ui/src/components/LoadingView.tsx +0 -22
- package/src/ui/src/components/RolesRoute.tsx +0 -169
- package/src/ui/src/components/SettingsRoute.tsx +0 -166
- package/src/ui/src/components/TaskList.tsx +0 -388
- package/src/ui/src/components/TerminalDrawer.tsx +0 -611
- package/src/ui/src/components/WorkflowRoute.tsx +0 -670
- package/src/ui/src/hooks/useReducedMotion.ts +0 -17
- package/src/ui/src/hooks/useServerConnection.ts +0 -114
- package/src/ui/src/index.css +0 -281
- package/src/ui/src/main.tsx +0 -11
- package/src/ui/src/types.ts +0 -108
- /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
|
+
}
|