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,732 @@
|
|
|
1
|
+
// workflow-layout.test.mjs
|
|
2
|
+
//
|
|
3
|
+
// Smart default positions + layout graph action (agent.layout).
|
|
4
|
+
//
|
|
5
|
+
// L1: new Main Agent on an empty graph gets the empty-graph default
|
|
6
|
+
// (260, 220) persisted into the graph positions map.
|
|
7
|
+
// L2: subagents with a parent land right of the parent; a second child of
|
|
8
|
+
// the same parent stacks below the first.
|
|
9
|
+
// L3: explicit positions are honored, never overridden by the smart default.
|
|
10
|
+
// L4: layout grid mode gives every node a distinct slot and returns the
|
|
11
|
+
// updated positions map.
|
|
12
|
+
// L5: layout tree mode places children right of their parents.
|
|
13
|
+
// L6: layout preserves capsuleDockLinks unchanged.
|
|
14
|
+
//
|
|
15
|
+
// Pattern mirrors control-plane-acceptance.test.mjs: in-process HTTP server on
|
|
16
|
+
// port 0, temp roots, real API calls (POST /api/sessions + the graph-node
|
|
17
|
+
// start route for L1-L3; POST /api/workflow/nodes/:actor/actions/agent.layout
|
|
18
|
+
// for L4-L6).
|
|
19
|
+
import { describe, it, beforeEach, afterEach } from 'node:test';
|
|
20
|
+
import assert from 'node:assert/strict';
|
|
21
|
+
import fs from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import http from 'node:http';
|
|
24
|
+
import { makeHarnessTempRoot } from '../../../tests/support/temp-root.js';
|
|
25
|
+
import { startServer, stopServer } from '../server.mjs';
|
|
26
|
+
import { SessionRegistry } from '../session-registry.mjs';
|
|
27
|
+
import { persistSession } from '../terminal-store.mjs';
|
|
28
|
+
import { writeWorkflowGraphMap, loadWorkflowGraphMap } from '../a2a-store.mjs';
|
|
29
|
+
|
|
30
|
+
// ── HTTP helper ──
|
|
31
|
+
function jsonRequest(baseUrl, route, { method = 'GET', body } = {}) {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
const url = new URL(route, baseUrl);
|
|
34
|
+
const payload = body ? JSON.stringify(body) : null;
|
|
35
|
+
const req = http.request({
|
|
36
|
+
hostname: url.hostname,
|
|
37
|
+
port: url.port,
|
|
38
|
+
path: `${url.pathname}${url.search}`,
|
|
39
|
+
method,
|
|
40
|
+
headers: payload ? { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) } : {},
|
|
41
|
+
}, (res) => {
|
|
42
|
+
let data = '';
|
|
43
|
+
res.on('data', chunk => { data += chunk; });
|
|
44
|
+
res.on('end', () => {
|
|
45
|
+
try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
|
|
46
|
+
catch { resolve({ status: res.statusCode, body: data }); }
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
req.on('error', reject);
|
|
50
|
+
if (payload) req.write(payload);
|
|
51
|
+
req.end();
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ── Project seeding helpers ──
|
|
56
|
+
function seedRoot(prefix) {
|
|
57
|
+
const root = makeHarnessTempRoot(prefix);
|
|
58
|
+
fs.mkdirSync(path.join(root, 'Harness', 'tasks'), { recursive: true });
|
|
59
|
+
fs.mkdirSync(path.join(root, 'Harness', 'a2a'), { recursive: true });
|
|
60
|
+
return root;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function seedAgentSession(root, nodeId, sessionId, overrides = {}) {
|
|
64
|
+
persistSession(root, {
|
|
65
|
+
sessionId,
|
|
66
|
+
graphNodeId: nodeId,
|
|
67
|
+
runtime: 'claude',
|
|
68
|
+
agentKind: overrides.agentKind || 'subagent',
|
|
69
|
+
role: overrides.role || 'Agent',
|
|
70
|
+
roleTitle: overrides.roleTitle || '',
|
|
71
|
+
displayName: overrides.displayName || '',
|
|
72
|
+
status: 'running',
|
|
73
|
+
attachMode: true,
|
|
74
|
+
taskId: null,
|
|
75
|
+
...overrides,
|
|
76
|
+
});
|
|
77
|
+
const graph = loadWorkflowGraphMap(root);
|
|
78
|
+
writeWorkflowGraphMap(root, {
|
|
79
|
+
...graph,
|
|
80
|
+
version: graph.version + 1,
|
|
81
|
+
nodes: [
|
|
82
|
+
...(graph.nodes || []).filter(n => (n.nodeId || n.id) !== nodeId),
|
|
83
|
+
{
|
|
84
|
+
nodeId,
|
|
85
|
+
sessionId,
|
|
86
|
+
kind: 'terminal-session',
|
|
87
|
+
runtime: 'claude',
|
|
88
|
+
agentKind: overrides.agentKind || 'subagent',
|
|
89
|
+
role: overrides.role || 'Agent',
|
|
90
|
+
label: overrides.label || overrides.displayName || overrides.role || 'Agent',
|
|
91
|
+
status: 'running',
|
|
92
|
+
parentAgentId: overrides.parentAgentId || null,
|
|
93
|
+
parentNodeId: overrides.parentNodeId || null,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function dockLink(a, b) {
|
|
100
|
+
const pair = [a, b].sort();
|
|
101
|
+
return {
|
|
102
|
+
id: `dock:${pair[0]}::${pair[1]}`,
|
|
103
|
+
nodeIds: pair,
|
|
104
|
+
anchorId: pair[0],
|
|
105
|
+
draggedId: pair[1],
|
|
106
|
+
side: 'top',
|
|
107
|
+
edges: [],
|
|
108
|
+
connections: [
|
|
109
|
+
{
|
|
110
|
+
source: a,
|
|
111
|
+
target: b,
|
|
112
|
+
relation: 'wf-bridge',
|
|
113
|
+
direction: 'source-to-target',
|
|
114
|
+
sourceHandle: 'dock',
|
|
115
|
+
targetHandle: 'dock',
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let root;
|
|
122
|
+
let serverHandle;
|
|
123
|
+
let baseUrl;
|
|
124
|
+
let registry;
|
|
125
|
+
|
|
126
|
+
beforeEach(async () => {
|
|
127
|
+
root = seedRoot('wf-layout-');
|
|
128
|
+
registry = new SessionRegistry();
|
|
129
|
+
const started = await startServer({
|
|
130
|
+
projectRoot: root,
|
|
131
|
+
host: '127.0.0.1',
|
|
132
|
+
port: 0,
|
|
133
|
+
sessionRegistry: registry,
|
|
134
|
+
eventsWs: false,
|
|
135
|
+
});
|
|
136
|
+
serverHandle = started.server;
|
|
137
|
+
baseUrl = `http://127.0.0.1:${started.port}`;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
afterEach(async () => {
|
|
141
|
+
if (serverHandle) await stopServer(serverHandle);
|
|
142
|
+
serverHandle = null;
|
|
143
|
+
if (root) {
|
|
144
|
+
const resolved = path.resolve(root);
|
|
145
|
+
const tempRootDir = path.resolve('Harness', '.temp') + path.sep;
|
|
146
|
+
assert.ok(resolved.startsWith(tempRootDir), `refusing to remove non-temp root: ${resolved}`);
|
|
147
|
+
try {
|
|
148
|
+
fs.rmSync(resolved, { recursive: true, force: true, maxRetries: 25, retryDelay: 200 });
|
|
149
|
+
} catch (e) {
|
|
150
|
+
if (process.platform === 'win32' && ['EPERM', 'EBUSY', 'ENOTEMPTY'].includes(e?.code)) return;
|
|
151
|
+
throw e;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
root = null;
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// ── helpers ──
|
|
158
|
+
async function createSessionViaApi(overrides = {}) {
|
|
159
|
+
const res = await jsonRequest(baseUrl, '/api/sessions', {
|
|
160
|
+
method: 'POST',
|
|
161
|
+
body: {
|
|
162
|
+
runtime: 'claude',
|
|
163
|
+
agentKind: 'subagent',
|
|
164
|
+
role: 'Subagent',
|
|
165
|
+
objective: 'workflow-layout test agent',
|
|
166
|
+
attachGraphNode: false,
|
|
167
|
+
deferPtySpawn: true,
|
|
168
|
+
...overrides,
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
assert.equal(res.status, 201, JSON.stringify(res.body));
|
|
172
|
+
return res.body;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async function startGraphNodeViaApi(sessionId) {
|
|
176
|
+
const res = await jsonRequest(baseUrl, `/api/a2a/nodes/${encodeURIComponent(sessionId)}/start`, {
|
|
177
|
+
method: 'POST',
|
|
178
|
+
body: { deferPtySpawn: true },
|
|
179
|
+
});
|
|
180
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
181
|
+
return res.body;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function runLayoutAction(actorNodeId, payload = {}) {
|
|
185
|
+
const res = await jsonRequest(
|
|
186
|
+
baseUrl,
|
|
187
|
+
`/api/workflow/nodes/${encodeURIComponent(actorNodeId)}/actions/agent.layout`,
|
|
188
|
+
{ method: 'POST', body: { actorNodeId, ...payload } },
|
|
189
|
+
);
|
|
190
|
+
return res;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function graphPositions() {
|
|
194
|
+
return loadWorkflowGraphMap(root).positions || {};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function graphNodes() {
|
|
198
|
+
return loadWorkflowGraphMap(root).nodes || [];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// ── L1 ──────────────────────────────────────────────────────────────────────
|
|
202
|
+
describe('L1 - empty graph default position', () => {
|
|
203
|
+
it('new Main Agent on an empty graph gets (260,220) persisted in the positions map', async () => {
|
|
204
|
+
const session = await createSessionViaApi({ agentKind: 'main', role: 'Main Agent' });
|
|
205
|
+
const graphNodeId = `session-${session.sessionId}`;
|
|
206
|
+
|
|
207
|
+
// Empty graph: no nodes, no positions.
|
|
208
|
+
assert.equal(graphNodes().length, 0);
|
|
209
|
+
|
|
210
|
+
const started = await startGraphNodeViaApi(session.sessionId);
|
|
211
|
+
assert.equal(started.ok, true);
|
|
212
|
+
|
|
213
|
+
const positions = graphPositions();
|
|
214
|
+
assert.ok(positions[graphNodeId], `positions must contain ${graphNodeId}: ${JSON.stringify(positions)}`);
|
|
215
|
+
assert.deepEqual(positions[graphNodeId], { x: 260, y: 220 });
|
|
216
|
+
|
|
217
|
+
// The pushed node carries the computed position too.
|
|
218
|
+
const node = graphNodes().find(n => (n.nodeId || n.id) === graphNodeId);
|
|
219
|
+
assert.ok(node, `graph must contain ${graphNodeId}`);
|
|
220
|
+
assert.deepEqual(node.position, { x: 260, y: 220 });
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('second agent without a parent stacks below the first, not under the terminal', async () => {
|
|
224
|
+
const first = await createSessionViaApi({ agentKind: 'main', role: 'Main Agent' });
|
|
225
|
+
await startGraphNodeViaApi(first.sessionId);
|
|
226
|
+
|
|
227
|
+
const second = await createSessionViaApi({ agentKind: 'main', role: 'Main Agent' });
|
|
228
|
+
await startGraphNodeViaApi(second.sessionId);
|
|
229
|
+
|
|
230
|
+
const firstNodeId = `session-${first.sessionId}`;
|
|
231
|
+
const secondNodeId = `session-${second.sessionId}`;
|
|
232
|
+
const positions = graphPositions();
|
|
233
|
+
assert.deepEqual(positions[firstNodeId], { x: 260, y: 220 });
|
|
234
|
+
// maxY = 220 + 120 = 340 → next y = 340 + 140 = 480.
|
|
235
|
+
assert.deepEqual(positions[secondNodeId], { x: 260, y: 480 });
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// ── L2 ──────────────────────────────────────────────────────────────────────
|
|
240
|
+
describe('L2 - parent-anchored placement', () => {
|
|
241
|
+
it('subagent with a parent is placed right of the parent; second child stacks below', async () => {
|
|
242
|
+
// Main Agent first (empty graph → (260,220)).
|
|
243
|
+
const main = await createSessionViaApi({ agentKind: 'main', role: 'Main Agent' });
|
|
244
|
+
await startGraphNodeViaApi(main.sessionId);
|
|
245
|
+
const mainNodeId = `session-${main.sessionId}`;
|
|
246
|
+
assert.deepEqual(graphPositions()[mainNodeId], { x: 260, y: 220 });
|
|
247
|
+
|
|
248
|
+
// First child: same parent → childIndex 0 → (260+420, 220+80*0) = (680,220).
|
|
249
|
+
const child1 = await createSessionViaApi({
|
|
250
|
+
parentNodeId: mainNodeId,
|
|
251
|
+
parentAgentId: main.sessionId,
|
|
252
|
+
});
|
|
253
|
+
await startGraphNodeViaApi(child1.sessionId);
|
|
254
|
+
const child1NodeId = `session-${child1.sessionId}`;
|
|
255
|
+
|
|
256
|
+
// Second child: same parent → childIndex 1 → (680, 220+80) = (680,300).
|
|
257
|
+
const child2 = await createSessionViaApi({
|
|
258
|
+
parentNodeId: mainNodeId,
|
|
259
|
+
parentAgentId: main.sessionId,
|
|
260
|
+
});
|
|
261
|
+
await startGraphNodeViaApi(child2.sessionId);
|
|
262
|
+
const child2NodeId = `session-${child2.sessionId}`;
|
|
263
|
+
|
|
264
|
+
const positions = graphPositions();
|
|
265
|
+
assert.deepEqual(positions[child1NodeId], { x: 680, y: 220 }, 'first child right of parent');
|
|
266
|
+
assert.deepEqual(positions[child2NodeId], { x: 680, y: 300 }, 'second child stacked below first');
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// ── L3 ──────────────────────────────────────────────────────────────────────
|
|
271
|
+
describe('L3 - explicit positions honored', () => {
|
|
272
|
+
it('a pre-seeded positions entry wins over the smart default', async () => {
|
|
273
|
+
const session = await createSessionViaApi({ agentKind: 'main', role: 'Main Agent' });
|
|
274
|
+
const graphNodeId = `session-${session.sessionId}`;
|
|
275
|
+
|
|
276
|
+
// Seed an explicit position for the target node id before starting.
|
|
277
|
+
const graph = loadWorkflowGraphMap(root);
|
|
278
|
+
writeWorkflowGraphMap(root, {
|
|
279
|
+
...graph,
|
|
280
|
+
version: graph.version + 1,
|
|
281
|
+
positions: { ...(graph.positions || {}), [graphNodeId]: { x: 333, y: 444 } },
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
await startGraphNodeViaApi(session.sessionId);
|
|
285
|
+
|
|
286
|
+
const positions = graphPositions();
|
|
287
|
+
assert.deepEqual(positions[graphNodeId], { x: 333, y: 444 }, 'explicit position must be honored, not overridden');
|
|
288
|
+
});
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// ── L4 ──────────────────────────────────────────────────────────────────────
|
|
292
|
+
describe('L4 - layout grid mode', () => {
|
|
293
|
+
it('grid gives every node a distinct slot and returns the positions map', async () => {
|
|
294
|
+
seedAgentSession(root, 'l4-main', 'session-l4-main', { agentKind: 'main', role: 'Main Agent' });
|
|
295
|
+
seedAgentSession(root, 'l4-a', 'session-l4-a');
|
|
296
|
+
seedAgentSession(root, 'l4-b', 'session-l4-b');
|
|
297
|
+
seedAgentSession(root, 'l4-c', 'session-l4-c');
|
|
298
|
+
|
|
299
|
+
const res = await runLayoutAction('l4-main', { mode: 'grid' });
|
|
300
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
301
|
+
assert.equal(res.body.ok, true);
|
|
302
|
+
assert.equal(res.body.action, 'layout');
|
|
303
|
+
assert.ok(res.body.positions, 'response must return positions');
|
|
304
|
+
|
|
305
|
+
const positions = res.body.positions;
|
|
306
|
+
const expectedIds = ['l4-main', 'l4-a', 'l4-b', 'l4-c'];
|
|
307
|
+
for (const nodeId of expectedIds) {
|
|
308
|
+
assert.ok(positions[nodeId], `grid must place ${nodeId}`);
|
|
309
|
+
}
|
|
310
|
+
// No overlaps: every (x, y) pair is distinct.
|
|
311
|
+
const seen = new Set();
|
|
312
|
+
for (const nodeId of expectedIds) {
|
|
313
|
+
const { x, y } = positions[nodeId];
|
|
314
|
+
const key = `${x},${y}`;
|
|
315
|
+
assert.ok(!seen.has(key), `duplicate slot ${key} for ${nodeId}`);
|
|
316
|
+
seen.add(key);
|
|
317
|
+
}
|
|
318
|
+
// Grid order: main agent first at the origin.
|
|
319
|
+
assert.deepEqual(positions['l4-main'], { x: 260, y: 220 });
|
|
320
|
+
});
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
// ── L5 ──────────────────────────────────────────────────────────────────────
|
|
324
|
+
describe('L5 - layout tree mode', () => {
|
|
325
|
+
it('children are centered below their parents (layered tree)', async () => {
|
|
326
|
+
seedAgentSession(root, 'l5-main', 'session-l5-main', { agentKind: 'main', role: 'Main Agent' });
|
|
327
|
+
seedAgentSession(root, 'l5-a', 'session-l5-a', {
|
|
328
|
+
parentNodeId: 'l5-main',
|
|
329
|
+
parentAgentId: 'session-l5-main',
|
|
330
|
+
});
|
|
331
|
+
seedAgentSession(root, 'l5-b', 'session-l5-b', {
|
|
332
|
+
parentNodeId: 'l5-main',
|
|
333
|
+
parentAgentId: 'session-l5-main',
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const res = await runLayoutAction('l5-main', { mode: 'tree' });
|
|
337
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
338
|
+
const positions = res.body.positions;
|
|
339
|
+
|
|
340
|
+
// Root at origin.
|
|
341
|
+
assert.deepEqual(positions['l5-main'], { x: 260, y: 220 });
|
|
342
|
+
// Children on the row BELOW, centered under the parent: two 280-wide
|
|
343
|
+
// slots centered on x=260 -> left slot center 120, right slot center 400.
|
|
344
|
+
assert.deepEqual(positions['l5-a'], { x: 120, y: 400 });
|
|
345
|
+
assert.deepEqual(positions['l5-b'], { x: 400, y: 400 });
|
|
346
|
+
});
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// ── L6 ──────────────────────────────────────────────────────────────────────
|
|
350
|
+
describe('L6 - capsuleDockLinks preserved', () => {
|
|
351
|
+
it('layout keeps dock links unchanged', async () => {
|
|
352
|
+
seedAgentSession(root, 'l6-main', 'session-l6-main', { agentKind: 'main', role: 'Main Agent' });
|
|
353
|
+
seedAgentSession(root, 'l6-a', 'session-l6-a');
|
|
354
|
+
seedAgentSession(root, 'l6-b', 'session-l6-b');
|
|
355
|
+
|
|
356
|
+
const link = dockLink('l6-a', 'l6-b');
|
|
357
|
+
const graph = loadWorkflowGraphMap(root);
|
|
358
|
+
writeWorkflowGraphMap(root, {
|
|
359
|
+
...graph,
|
|
360
|
+
version: graph.version + 1,
|
|
361
|
+
capsuleDockLinks: [...(graph.capsuleDockLinks || []), link],
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const before = loadWorkflowGraphMap(root).capsuleDockLinks;
|
|
365
|
+
|
|
366
|
+
const res = await runLayoutAction('l6-main', { mode: 'grid' });
|
|
367
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
368
|
+
|
|
369
|
+
const after = loadWorkflowGraphMap(root).capsuleDockLinks;
|
|
370
|
+
assert.deepEqual(after, before, 'capsuleDockLinks must survive layout untouched');
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
// ── L7 ──────────────────────────────────────────────────────────────────────
|
|
375
|
+
// L7: layout tidy mode (AC-002/AC-003). Same POST /actions/agent.layout
|
|
376
|
+
// endpoint as L4-L6 with payload {mode:'tidy', sizes?}: edge-direction
|
|
377
|
+
// layers, per-node size awareness, docked pairs glued, positions persisted
|
|
378
|
+
// (graph-map GET reflects them), undo op recorded.
|
|
379
|
+
//
|
|
380
|
+
// W1 pinned contract: response {ok:true, action:'layout', positions};
|
|
381
|
+
// sizes payload {[nodeId]:{w,h}} is optional (server falls back to the
|
|
382
|
+
// 280x180 slot); default sizes for the checks below are 280x180 and the
|
|
383
|
+
// minimum clearance between any two rects is 16px.
|
|
384
|
+
|
|
385
|
+
function writeGraphExtras(positions = {}, edges = [], dockLinks = []) {
|
|
386
|
+
const graph = loadWorkflowGraphMap(root);
|
|
387
|
+
writeWorkflowGraphMap(root, {
|
|
388
|
+
...graph,
|
|
389
|
+
version: graph.version + 1,
|
|
390
|
+
positions: { ...(graph.positions || {}), ...positions },
|
|
391
|
+
edges: [...(graph.edges || []), ...edges],
|
|
392
|
+
capsuleDockLinks: [...(graph.capsuleDockLinks || []), ...dockLinks],
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function minRectGap(positions, sizes = {}) {
|
|
397
|
+
const rects = Object.entries(positions).map(([id, p]) => ({
|
|
398
|
+
id,
|
|
399
|
+
x: p.x,
|
|
400
|
+
y: p.y,
|
|
401
|
+
w: sizes[id]?.w ?? 280,
|
|
402
|
+
h: sizes[id]?.h ?? 180,
|
|
403
|
+
}));
|
|
404
|
+
let minGap = Infinity;
|
|
405
|
+
for (let i = 0; i < rects.length; i++) {
|
|
406
|
+
for (let j = i + 1; j < rects.length; j++) {
|
|
407
|
+
const a = rects[i];
|
|
408
|
+
const b = rects[j];
|
|
409
|
+
const sepX = Math.max(a.x, b.x) - Math.min(a.x + a.w, b.x + b.w);
|
|
410
|
+
const sepY = Math.max(a.y, b.y) - Math.min(a.y + a.h, b.y + b.h);
|
|
411
|
+
minGap = Math.min(minGap, Math.max(sepX, sepY));
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
return minGap;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
describe('L7 - layout tidy mode', () => {
|
|
418
|
+
it('tidy orders layers by edge direction, returns positions, persists to graph-map GET (AC-002)', async () => {
|
|
419
|
+
seedAgentSession(root, 'l7-main', 'session-l7-main', { agentKind: 'main', role: 'Main Agent' });
|
|
420
|
+
seedAgentSession(root, 'l7-a', 'session-l7-a');
|
|
421
|
+
seedAgentSession(root, 'l7-b', 'session-l7-b');
|
|
422
|
+
seedAgentSession(root, 'l7-c', 'session-l7-c');
|
|
423
|
+
writeGraphExtras({}, [
|
|
424
|
+
{ id: 'edge-l7-main-a', from: 'l7-main', to: 'l7-a', relation: 'delegates', direction: 'source-to-target' },
|
|
425
|
+
{ id: 'edge-l7-a-b', from: 'l7-a', to: 'l7-b', relation: 'delegates', direction: 'source-to-target' },
|
|
426
|
+
]);
|
|
427
|
+
|
|
428
|
+
const res = await runLayoutAction('l7-main', { mode: 'tidy' });
|
|
429
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
430
|
+
assert.equal(res.body.ok, true);
|
|
431
|
+
assert.equal(res.body.action, 'layout');
|
|
432
|
+
const positions = res.body.positions;
|
|
433
|
+
assert.ok(positions, 'response must return positions');
|
|
434
|
+
for (const nodeId of ['l7-main', 'l7-a', 'l7-b', 'l7-c']) {
|
|
435
|
+
assert.ok(positions[nodeId], `tidy must place ${nodeId}`);
|
|
436
|
+
}
|
|
437
|
+
// Edge-direction chain l7-main -> l7-a -> l7-b: strictly increasing rows.
|
|
438
|
+
assert.ok(
|
|
439
|
+
positions['l7-main'].y < positions['l7-a'].y && positions['l7-a'].y < positions['l7-b'].y,
|
|
440
|
+
`layers ordered by edge direction: main.y=${positions['l7-main'].y}, a.y=${positions['l7-a'].y}, b.y=${positions['l7-b'].y}`,
|
|
441
|
+
);
|
|
442
|
+
assert.ok(minRectGap(positions) >= 16, 'no two rects overlap (min gap >= 16)');
|
|
443
|
+
|
|
444
|
+
// Persistence: the graph-map GET reflects the tidy positions.
|
|
445
|
+
const getRes = await jsonRequest(baseUrl, '/api/a2a/graph-map');
|
|
446
|
+
assert.equal(getRes.status, 200);
|
|
447
|
+
for (const nodeId of ['l7-main', 'l7-a', 'l7-b', 'l7-c']) {
|
|
448
|
+
assert.deepEqual(getRes.body.positions[nodeId], positions[nodeId], `graph-map GET positions for ${nodeId}`);
|
|
449
|
+
const node = getRes.body.nodes.find(n => (n.nodeId || n.id) === nodeId);
|
|
450
|
+
assert.ok(node, `graph-map GET must contain node ${nodeId}`);
|
|
451
|
+
assert.deepEqual(node.position, positions[nodeId], `node.position persisted for ${nodeId}`);
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('tidy honors per-node sizes so a 560x358 agent never overlaps its child (AC-003)', async () => {
|
|
456
|
+
seedAgentSession(root, 'l7-main', 'session-l7-main', { agentKind: 'main', role: 'Main Agent' });
|
|
457
|
+
seedAgentSession(root, 'l7-s', 'session-l7-s');
|
|
458
|
+
writeGraphExtras({}, [
|
|
459
|
+
{ id: 'edge-l7-main-s', from: 'l7-main', to: 'l7-s', relation: 'delegates', direction: 'source-to-target' },
|
|
460
|
+
]);
|
|
461
|
+
|
|
462
|
+
const sizes = { 'l7-main': { w: 560, h: 358 } };
|
|
463
|
+
const res = await runLayoutAction('l7-main', { mode: 'tidy', sizes });
|
|
464
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
465
|
+
const positions = res.body.positions;
|
|
466
|
+
assert.ok(positions['l7-main'] && positions['l7-s']);
|
|
467
|
+
assert.ok(
|
|
468
|
+
minRectGap(positions, sizes) >= 16,
|
|
469
|
+
'size-aware tidy keeps >= 16px clearance between the 560x358 node and its neighbor',
|
|
470
|
+
);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('docked pairs stay glued: relative offset preserved and no overlaps after tidy (AC-003)', async () => {
|
|
474
|
+
seedAgentSession(root, 'l7-main', 'session-l7-main', { agentKind: 'main', role: 'Main Agent' });
|
|
475
|
+
seedAgentSession(root, 'l7-a', 'session-l7-a');
|
|
476
|
+
seedAgentSession(root, 'l7-d1', 'session-l7-d1');
|
|
477
|
+
seedAgentSession(root, 'l7-d2', 'session-l7-d2');
|
|
478
|
+
writeGraphExtras(
|
|
479
|
+
{ 'l7-d1': { x: 260, y: 220 }, 'l7-d2': { x: 340, y: 416 } },
|
|
480
|
+
[
|
|
481
|
+
{ id: 'edge-l7-main-a', from: 'l7-main', to: 'l7-a', relation: 'delegates', direction: 'source-to-target' },
|
|
482
|
+
],
|
|
483
|
+
[dockLink('l7-d1', 'l7-d2')],
|
|
484
|
+
);
|
|
485
|
+
// Seed delta {80, 196}: y = one node height (180) + 16px clearance, so the
|
|
486
|
+
// glued pair itself also satisfies the >= 16px min-gap rule (a docked pair
|
|
487
|
+
// is one layout unit; the no-overlap contract is between units).
|
|
488
|
+
const delta = { x: 340 - 260, y: 416 - 220 };
|
|
489
|
+
|
|
490
|
+
const res = await runLayoutAction('l7-main', { mode: 'tidy' });
|
|
491
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
492
|
+
const positions = res.body.positions;
|
|
493
|
+
assert.ok(positions['l7-d1'] && positions['l7-d2'], 'docked nodes must keep positions after tidy');
|
|
494
|
+
assert.equal(
|
|
495
|
+
positions['l7-d2'].x - positions['l7-d1'].x,
|
|
496
|
+
delta.x,
|
|
497
|
+
'docked pair keeps its relative x offset',
|
|
498
|
+
);
|
|
499
|
+
assert.equal(
|
|
500
|
+
positions['l7-d2'].y - positions['l7-d1'].y,
|
|
501
|
+
delta.y,
|
|
502
|
+
'docked pair keeps its relative y offset',
|
|
503
|
+
);
|
|
504
|
+
assert.ok(minRectGap(positions) >= 16, 'docked pair must not overlap any node after tidy');
|
|
505
|
+
|
|
506
|
+
const after = loadWorkflowGraphMap(root).capsuleDockLinks;
|
|
507
|
+
assert.equal(after.length, 1, 'capsuleDockLinks must survive tidy untouched');
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('tidy records an undo op carrying the forward positions (contract)', async () => {
|
|
511
|
+
seedAgentSession(root, 'l7-main', 'session-l7-main', { agentKind: 'main', role: 'Main Agent' });
|
|
512
|
+
seedAgentSession(root, 'l7-a', 'session-l7-a');
|
|
513
|
+
writeGraphExtras({}, [
|
|
514
|
+
{ id: 'edge-l7-main-a', from: 'l7-main', to: 'l7-a', relation: 'delegates', direction: 'source-to-target' },
|
|
515
|
+
]);
|
|
516
|
+
|
|
517
|
+
const res = await runLayoutAction('l7-main', { mode: 'tidy' });
|
|
518
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
519
|
+
|
|
520
|
+
const ops = loadWorkflowGraphMap(root).undoStack || [];
|
|
521
|
+
assert.ok(ops.length >= 1, 'tidy must record an undo op');
|
|
522
|
+
const last = ops[ops.length - 1];
|
|
523
|
+
assert.equal(last.action, 'agent.layout');
|
|
524
|
+
assert.deepEqual(last.forward.positions, res.body.positions, 'undo op forward positions match the response');
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// W4 review finding MED-2: the tidy undo op must be able to restore a
|
|
528
|
+
// docked pair. inverse.positions has to carry the PRE-tidy positions of the
|
|
529
|
+
// pair members (anchor AND slave) — the current implementation snapshots
|
|
530
|
+
// only the non-docked "movable" nodes, so undo would leave the docked pair
|
|
531
|
+
// at its tidy position.
|
|
532
|
+
it('tidy undo op inverse.positions includes docked pair pre-tidy positions (MED-2)', async () => {
|
|
533
|
+
seedAgentSession(root, 'l7-main', 'session-l7-main', { agentKind: 'main', role: 'Main Agent' });
|
|
534
|
+
seedAgentSession(root, 'l7-a', 'session-l7-a');
|
|
535
|
+
seedAgentSession(root, 'l7-d1', 'session-l7-d1');
|
|
536
|
+
seedAgentSession(root, 'l7-d2', 'session-l7-d2');
|
|
537
|
+
const preTidy = {
|
|
538
|
+
'l7-d1': { x: 260, y: 220 },
|
|
539
|
+
'l7-d2': { x: 340, y: 416 },
|
|
540
|
+
};
|
|
541
|
+
writeGraphExtras(
|
|
542
|
+
{ ...preTidy, 'l7-main': { x: 260, y: 220 }, 'l7-a': { x: 680, y: 220 } },
|
|
543
|
+
[
|
|
544
|
+
{ id: 'edge-l7-main-a', from: 'l7-main', to: 'l7-a', relation: 'delegates', direction: 'source-to-target' },
|
|
545
|
+
],
|
|
546
|
+
[dockLink('l7-d1', 'l7-d2')],
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
const res = await runLayoutAction('l7-main', { mode: 'tidy' });
|
|
550
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
551
|
+
|
|
552
|
+
const ops = loadWorkflowGraphMap(root).undoStack || [];
|
|
553
|
+
assert.ok(ops.length >= 1, 'tidy must record an undo op');
|
|
554
|
+
const last = ops[ops.length - 1];
|
|
555
|
+
assert.equal(last.action, 'agent.layout');
|
|
556
|
+
assert.ok(last.inverse && last.inverse.positions, 'undo op must carry inverse positions');
|
|
557
|
+
assert.deepEqual(
|
|
558
|
+
last.inverse.positions['l7-d1'],
|
|
559
|
+
preTidy['l7-d1'],
|
|
560
|
+
'undo inverse keeps the dock anchor pre-tidy position',
|
|
561
|
+
);
|
|
562
|
+
assert.deepEqual(
|
|
563
|
+
last.inverse.positions['l7-d2'],
|
|
564
|
+
preTidy['l7-d2'],
|
|
565
|
+
'undo inverse keeps the dock slave pre-tidy position',
|
|
566
|
+
);
|
|
567
|
+
assert.ok(
|
|
568
|
+
last.inverse.positions['l7-main'] && last.inverse.positions['l7-a'],
|
|
569
|
+
'undo inverse still covers the non-docked nodes',
|
|
570
|
+
);
|
|
571
|
+
});
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
// ── L8 ──────────────────────────────────────────────────────────────────────
|
|
575
|
+
// L8: agent creation carries an optional client-computed position
|
|
576
|
+
// (W4 review finding HIGH-1, AC-001 for agent creation). POST /api/sessions
|
|
577
|
+
// accepts {position:{x,y}}; the server materializes the graph node AT that
|
|
578
|
+
// position and — size-aware (agent rects are 560x358, not the 280x180 slot) —
|
|
579
|
+
// never lets two adjacent agents overlap.
|
|
580
|
+
//
|
|
581
|
+
// W5 pinned contract: the session create body may carry
|
|
582
|
+
// position: {x: number, y: number}
|
|
583
|
+
// The graph-map positions map (and node.position) must then reflect that
|
|
584
|
+
// position for the created node; when the requested spot is not clear of the
|
|
585
|
+
// real agent extents, the server moves the new node to a clear spot.
|
|
586
|
+
|
|
587
|
+
describe('L8 - agent create with client position (HIGH-1)', () => {
|
|
588
|
+
it('a session created with a position materializes the graph node AT that position', async () => {
|
|
589
|
+
const session = await createSessionViaApi({
|
|
590
|
+
agentKind: 'main',
|
|
591
|
+
role: 'Main Agent',
|
|
592
|
+
attachGraphNode: true,
|
|
593
|
+
position: { x: 333, y: 444 },
|
|
594
|
+
});
|
|
595
|
+
const graphNodeId = `session-${session.sessionId}`;
|
|
596
|
+
|
|
597
|
+
// The graph node is materialized at CREATE time (attachGraphNode default
|
|
598
|
+
// true) at the requested position — no start round-trip needed.
|
|
599
|
+
const positions = graphPositions();
|
|
600
|
+
assert.deepEqual(
|
|
601
|
+
positions[graphNodeId],
|
|
602
|
+
{ x: 333, y: 444 },
|
|
603
|
+
'server must materialize the node at the requested position',
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
const getRes = await jsonRequest(baseUrl, '/api/a2a/graph-map');
|
|
607
|
+
assert.equal(getRes.status, 200);
|
|
608
|
+
assert.deepEqual(getRes.body.positions[graphNodeId], { x: 333, y: 444 }, 'graph-map GET agrees');
|
|
609
|
+
const node = getRes.body.nodes.find(n => (n.nodeId || n.id) === graphNodeId);
|
|
610
|
+
assert.ok(node, 'graph-map GET must contain the created node');
|
|
611
|
+
assert.deepEqual(node.position, { x: 333, y: 444 }, 'node.position persisted');
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
it('two adjacent agent free spots stay non-overlapping at real agent size (560x358)', async () => {
|
|
615
|
+
// A requested at (260,220); B requested at (620,240) — a spot that clears
|
|
616
|
+
// a 280x180 slot but NOT a 560x358 agent at A. The server must be
|
|
617
|
+
// size-aware: B's persisted position must clear A's real rect.
|
|
618
|
+
const a = await createSessionViaApi({
|
|
619
|
+
agentKind: 'main',
|
|
620
|
+
role: 'Main Agent',
|
|
621
|
+
attachGraphNode: true,
|
|
622
|
+
position: { x: 260, y: 220 },
|
|
623
|
+
});
|
|
624
|
+
const b = await createSessionViaApi({
|
|
625
|
+
agentKind: 'main',
|
|
626
|
+
role: 'Main Agent',
|
|
627
|
+
attachGraphNode: true,
|
|
628
|
+
position: { x: 620, y: 240 },
|
|
629
|
+
});
|
|
630
|
+
const aNodeId = `session-${a.sessionId}`;
|
|
631
|
+
const bNodeId = `session-${b.sessionId}`;
|
|
632
|
+
|
|
633
|
+
const positions = graphPositions();
|
|
634
|
+
assert.deepEqual(positions[aNodeId], { x: 260, y: 220 }, 'first agent keeps its requested spot');
|
|
635
|
+
assert.ok(positions[bNodeId], `second agent must be placed: ${JSON.stringify(positions)}`);
|
|
636
|
+
|
|
637
|
+
// Agent nodes render at 560x358; both rects must keep >= 16px clearance.
|
|
638
|
+
const sizes = { [aNodeId]: { w: 560, h: 358 }, [bNodeId]: { w: 560, h: 358 } };
|
|
639
|
+
assert.ok(
|
|
640
|
+
minRectGap(positions, sizes) >= 16,
|
|
641
|
+
`agents must not overlap at real size (min gap was ${minRectGap(positions, sizes)})`,
|
|
642
|
+
);
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
// ── L8 ──────────────────────────────────────────────────────────────────────
|
|
647
|
+
// L8: layout agent-tree mode (task-agent-tree-layout). Same POST /actions/
|
|
648
|
+
// agent.layout endpoint with payload {mode:'agent-tree', sizes?}: main agent
|
|
649
|
+
// centered anchor, subagents one layer below as an agent TREE, scattered
|
|
650
|
+
// nodes as matrix bands above owners, docked pairs glued, positions persisted,
|
|
651
|
+
// and — the round-2 contract — the action NEVER creates or modifies edges.
|
|
652
|
+
|
|
653
|
+
describe('L8 - layout agent-tree mode', () => {
|
|
654
|
+
it('agent-tree centers main, stacks subagents one layer below, persists positions (AC-001)', async () => {
|
|
655
|
+
seedAgentSession(root, 'l8-main', 'session-l8-main', { agentKind: 'main', role: 'Main Agent' });
|
|
656
|
+
seedAgentSession(root, 'l8-s1', 'session-l8-s1');
|
|
657
|
+
seedAgentSession(root, 'l8-s2', 'session-l8-s2');
|
|
658
|
+
seedAgentSession(root, 'l8-s3', 'session-l8-s3');
|
|
659
|
+
writeGraphExtras({}, [
|
|
660
|
+
{ id: 'edge-l8-1', from: 'l8-main', to: 'l8-s1', relation: 'wf-bridge', direction: 'bidirectional' },
|
|
661
|
+
{ id: 'edge-l8-2', from: 'l8-main', to: 'l8-s2', relation: 'wf-bridge', direction: 'bidirectional' },
|
|
662
|
+
{ id: 'edge-l8-3', from: 'l8-main', to: 'l8-s3', relation: 'wf-bridge', direction: 'bidirectional' },
|
|
663
|
+
]);
|
|
664
|
+
|
|
665
|
+
const res = await runLayoutAction('l8-main', { mode: 'agent-tree' });
|
|
666
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
667
|
+
assert.equal(res.body.ok, true);
|
|
668
|
+
assert.equal(res.body.action, 'layout');
|
|
669
|
+
const positions = res.body.positions;
|
|
670
|
+
for (const nodeId of ['l8-main', 'l8-s1', 'l8-s2', 'l8-s3']) {
|
|
671
|
+
assert.ok(positions[nodeId], `agent-tree must place ${nodeId}`);
|
|
672
|
+
}
|
|
673
|
+
// Main is CENTERED on the handler origin {260, 220}: default 280x180 slot
|
|
674
|
+
// -> top-left (120, 220); the sub row centers under main's center x=260.
|
|
675
|
+
assert.deepEqual(positions['l8-main'], { x: 120, y: 220 }, 'main centers on the origin (top-left 120)');
|
|
676
|
+
// Subagents share ONE layer below main, sorted by id: s1 left, s2 center, s3 right.
|
|
677
|
+
assert.ok(
|
|
678
|
+
positions['l8-s1'].y === positions['l8-s2'].y && positions['l8-s2'].y === positions['l8-s3'].y,
|
|
679
|
+
'subagents share one layer below main',
|
|
680
|
+
);
|
|
681
|
+
assert.ok(positions['l8-s1'].y > positions['l8-main'].y, 'subagent layer is BELOW main');
|
|
682
|
+
assert.ok(
|
|
683
|
+
positions['l8-s1'].x < positions['l8-s2'].x && positions['l8-s2'].x < positions['l8-s3'].x,
|
|
684
|
+
'subagents ordered by id left to right',
|
|
685
|
+
);
|
|
686
|
+
assert.equal(positions['l8-s2'].x, positions['l8-main'].x, 'middle subagent centers under main');
|
|
687
|
+
assert.ok(minRectGap(positions) >= 16, 'no two rects overlap (min gap >= 16)');
|
|
688
|
+
|
|
689
|
+
// Persistence: the graph-map GET reflects the agent-tree positions.
|
|
690
|
+
const getRes = await jsonRequest(baseUrl, '/api/a2a/graph-map');
|
|
691
|
+
assert.equal(getRes.status, 200);
|
|
692
|
+
for (const nodeId of ['l8-main', 'l8-s1', 'l8-s2', 'l8-s3']) {
|
|
693
|
+
assert.deepEqual(getRes.body.positions[nodeId], positions[nodeId], `graph-map GET positions for ${nodeId}`);
|
|
694
|
+
}
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
it('agent-tree never creates or modifies edges (AC-002)', async () => {
|
|
698
|
+
seedAgentSession(root, 'l8-main', 'session-l8-main', { agentKind: 'main', role: 'Main Agent' });
|
|
699
|
+
seedAgentSession(root, 'l8-s1', 'session-l8-s1');
|
|
700
|
+
seedAgentSession(root, 'l8-s2', 'session-l8-s2');
|
|
701
|
+
writeGraphExtras({}, [
|
|
702
|
+
{ id: 'edge-l8-a', from: 'l8-main', to: 'l8-s1', relation: 'wf-bridge', direction: 'bidirectional' },
|
|
703
|
+
{ id: 'edge-l8-b', from: 'l8-main', to: 'l8-s2', relation: 'wf-bridge', direction: 'bidirectional' },
|
|
704
|
+
]);
|
|
705
|
+
const before = loadWorkflowGraphMap(root).edges;
|
|
706
|
+
|
|
707
|
+
const res = await runLayoutAction('l8-main', { mode: 'agent-tree' });
|
|
708
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
709
|
+
|
|
710
|
+
const after = loadWorkflowGraphMap(root).edges;
|
|
711
|
+
assert.equal(after.length, before.length, 'agent-tree must not add or remove edges');
|
|
712
|
+
assert.deepEqual(after, before, 'agent-tree must not modify edges');
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
it('subagent with explicit parentAgentId goes below its declared parent (tree depth)', async () => {
|
|
716
|
+
seedAgentSession(root, 'l8-main', 'session-l8-main', { agentKind: 'main', role: 'Main Agent' });
|
|
717
|
+
seedAgentSession(root, 'l8-s1', 'session-l8-s1');
|
|
718
|
+
seedAgentSession(root, 'l8-s1b', 'session-l8-s1b', { parentAgentId: 'l8-s1' });
|
|
719
|
+
writeGraphExtras({}, [
|
|
720
|
+
{ id: 'edge-l8-p1', from: 'l8-main', to: 'l8-s1', relation: 'wf-bridge', direction: 'bidirectional' },
|
|
721
|
+
// l8-s1b also bridges main — parentAgentId must win.
|
|
722
|
+
{ id: 'edge-l8-p2', from: 'l8-main', to: 'l8-s1b', relation: 'wf-bridge', direction: 'bidirectional' },
|
|
723
|
+
]);
|
|
724
|
+
|
|
725
|
+
const res = await runLayoutAction('l8-main', { mode: 'agent-tree' });
|
|
726
|
+
assert.equal(res.status, 200, JSON.stringify(res.body));
|
|
727
|
+
const positions = res.body.positions;
|
|
728
|
+
assert.ok(positions['l8-s1b'].y > positions['l8-s1'].y, 'declared child sits BELOW its parent agent');
|
|
729
|
+
assert.ok(positions['l8-s1b'].y > positions['l8-main'].y, 'and below main');
|
|
730
|
+
assert.ok(minRectGap(positions) >= 16, 'no two rects overlap (min gap >= 16)');
|
|
731
|
+
});
|
|
732
|
+
});
|