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,125 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { makeHarnessTempRoot } from '../../../tests/support/temp-root.js';
|
|
6
|
+
import { readJsonFile, writeJsonFileAtomic } from '../json-store-utils.mjs';
|
|
7
|
+
import { createComponentNode, getComponentNode, listLiveComponentNodes } from '../component-node-store.mjs';
|
|
8
|
+
import { createEventNode, getEventNode } from '../workflow-event-node-store.mjs';
|
|
9
|
+
import { createCapabilityNode, getCapabilityNode } from '../workflow-capability-node-store.mjs';
|
|
10
|
+
import { getGoalNode } from '../workflow-goal-node-store.mjs';
|
|
11
|
+
|
|
12
|
+
function makeProject(prefix = 'wf-node-store-utils-') {
|
|
13
|
+
const projectRoot = makeHarnessTempRoot(prefix);
|
|
14
|
+
fs.mkdirSync(path.join(projectRoot, 'Harness', 'tasks'), { recursive: true });
|
|
15
|
+
return projectRoot;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function writeActiveTask(projectRoot, taskId = 'task-store-utils') {
|
|
19
|
+
fs.mkdirSync(path.join(projectRoot, 'Harness', 'tasks', taskId), { recursive: true });
|
|
20
|
+
fs.writeFileSync(path.join(projectRoot, 'Harness', 'PROGRESS.md'), `# PROGRESS.md\n\n## Active Task\n\n- ${taskId}\n`, 'utf8');
|
|
21
|
+
fs.writeFileSync(path.join(projectRoot, 'Harness', 'tasks', taskId, 'STATE.json'), JSON.stringify({
|
|
22
|
+
schemaVersion: 1,
|
|
23
|
+
taskId,
|
|
24
|
+
status: 'active',
|
|
25
|
+
phase: 'implement',
|
|
26
|
+
gate: 'VERIFY',
|
|
27
|
+
nextAction: 'Verify node store utilities.',
|
|
28
|
+
acceptance: [{ id: 'AC-001', text: 'Strict JSON reads', status: 'tracked' }],
|
|
29
|
+
links: { dependsOn: [], blocks: [] },
|
|
30
|
+
}, null, 2), 'utf8');
|
|
31
|
+
fs.writeFileSync(path.join(projectRoot, 'Harness', 'tasks', taskId, 'PLAN.md'), '# Plan\n', 'utf8');
|
|
32
|
+
fs.writeFileSync(path.join(projectRoot, 'Harness', 'tasks', taskId, 'PROGRESS.md'), '# Progress\n', 'utf8');
|
|
33
|
+
return `goal-${taskId}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test('W39-ATOMIC-WRITE writes pretty JSON through the shared atomic writer', () => {
|
|
37
|
+
const projectRoot = makeProject();
|
|
38
|
+
try {
|
|
39
|
+
const target = path.join(projectRoot, 'Harness', 'a2a', 'store', 'state.json');
|
|
40
|
+
writeJsonFileAtomic(target, { alpha: 1, nested: { beta: true } });
|
|
41
|
+
|
|
42
|
+
const body = fs.readFileSync(target, 'utf8');
|
|
43
|
+
assert.equal(body.endsWith('\n'), true);
|
|
44
|
+
assert.match(body, /\n "alpha": 1,/);
|
|
45
|
+
assert.deepEqual(JSON.parse(body), { alpha: 1, nested: { beta: true } });
|
|
46
|
+
const leftovers = fs.readdirSync(path.dirname(target)).filter(name => name.endsWith('.tmp'));
|
|
47
|
+
assert.deepEqual(leftovers, []);
|
|
48
|
+
} finally {
|
|
49
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test('W39-STRICT-READ distinguishes missing fallback from corrupt JSON', () => {
|
|
54
|
+
const projectRoot = makeProject();
|
|
55
|
+
try {
|
|
56
|
+
const missing = path.join(projectRoot, 'Harness', 'a2a', 'missing.json');
|
|
57
|
+
assert.deepEqual(readJsonFile(missing, { fallback: true }), { fallback: true });
|
|
58
|
+
|
|
59
|
+
const corrupt = path.join(projectRoot, 'Harness', 'a2a', 'corrupt.json');
|
|
60
|
+
fs.mkdirSync(path.dirname(corrupt), { recursive: true });
|
|
61
|
+
fs.writeFileSync(corrupt, '{', 'utf8');
|
|
62
|
+
assert.throws(() => readJsonFile(corrupt, null), (error) => {
|
|
63
|
+
assert.equal(error.code, 'CORRUPT_JSON');
|
|
64
|
+
assert.match(error.message, /Corrupt JSON store file/);
|
|
65
|
+
return true;
|
|
66
|
+
});
|
|
67
|
+
} finally {
|
|
68
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('W39-STRICT-READ keeps direct component reads strict while stale live lists skip missing state refs', () => {
|
|
73
|
+
const projectRoot = makeProject();
|
|
74
|
+
try {
|
|
75
|
+
const live = createComponentNode(projectRoot, {
|
|
76
|
+
type: 'markdown',
|
|
77
|
+
title: 'Live',
|
|
78
|
+
markdown: 'live',
|
|
79
|
+
});
|
|
80
|
+
const stale = createComponentNode(projectRoot, {
|
|
81
|
+
type: 'markdown',
|
|
82
|
+
title: 'Stale',
|
|
83
|
+
markdown: 'stale',
|
|
84
|
+
});
|
|
85
|
+
fs.rmSync(path.join(projectRoot, stale.node.statePath), { force: true });
|
|
86
|
+
|
|
87
|
+
assert.throws(() => getComponentNode(projectRoot, stale.node.nodeId), /STATE_MISMATCH|missing|out of sync/i);
|
|
88
|
+
assert.deepEqual(listLiveComponentNodes(projectRoot).map(node => node.nodeId), [live.node.nodeId]);
|
|
89
|
+
|
|
90
|
+
fs.writeFileSync(path.join(projectRoot, live.node.statePath), '{', 'utf8');
|
|
91
|
+
assert.throws(() => getComponentNode(projectRoot, live.node.nodeId), /Corrupt JSON store file/);
|
|
92
|
+
assert.throws(() => listLiveComponentNodes(projectRoot), /Corrupt JSON store file/);
|
|
93
|
+
} finally {
|
|
94
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('W39-STRICT-READ direct event, capability, and goal store reads reject corrupt JSON', () => {
|
|
99
|
+
const projectRoot = makeProject();
|
|
100
|
+
try {
|
|
101
|
+
const event = createEventNode(projectRoot, {
|
|
102
|
+
type: 'timer',
|
|
103
|
+
title: 'Strict Timer',
|
|
104
|
+
});
|
|
105
|
+
fs.writeFileSync(path.join(projectRoot, event.node.statePath), '{', 'utf8');
|
|
106
|
+
assert.throws(() => getEventNode(projectRoot, event.node.nodeId), /Corrupt JSON store file/);
|
|
107
|
+
|
|
108
|
+
const capability = createCapabilityNode(projectRoot, {
|
|
109
|
+
type: 'skill-group',
|
|
110
|
+
title: 'Strict Skills',
|
|
111
|
+
skills: [{ id: 'skill:wf-ui', name: 'wf-ui' }],
|
|
112
|
+
});
|
|
113
|
+
fs.writeFileSync(path.join(projectRoot, capability.node.statePath), '{', 'utf8');
|
|
114
|
+
assert.throws(() => getCapabilityNode(projectRoot, capability.node.nodeId), /Corrupt JSON store file/);
|
|
115
|
+
|
|
116
|
+
const goalNodeId = writeActiveTask(projectRoot);
|
|
117
|
+
getGoalNode(projectRoot, goalNodeId);
|
|
118
|
+
const sidecarPath = path.join(projectRoot, 'Harness', 'a2a', 'goal-nodes', goalNodeId, 'state.json');
|
|
119
|
+
fs.mkdirSync(path.dirname(sidecarPath), { recursive: true });
|
|
120
|
+
fs.writeFileSync(sidecarPath, '{', 'utf8');
|
|
121
|
+
assert.throws(() => getGoalNode(projectRoot, goalNodeId), /Corrupt JSON store file/);
|
|
122
|
+
} finally {
|
|
123
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
+
|
|
7
|
+
import { loadActionRegistry } from '../action-registry.mjs';
|
|
8
|
+
import { workflowOntology } from '../workflow-ontology.mjs';
|
|
9
|
+
|
|
10
|
+
// ── Two-way registry drift guard ─────────────────────────────────────────────
|
|
11
|
+
// Harness/a2a/action-registry.json is the single source of truth for the
|
|
12
|
+
// typed workflow-node action surface. This test keeps the registry and the
|
|
13
|
+
// implementations in lockstep in BOTH directions forever:
|
|
14
|
+
//
|
|
15
|
+
// Direction 1 (ghost guard): every adapter function export that matches the
|
|
16
|
+
// action-dispatch convention must be registered — an implemented-but-
|
|
17
|
+
// unregistered action (the audit's 6 ghost actions) fails here.
|
|
18
|
+
// Direction 2 (phantom guard): every registered non-special action must map
|
|
19
|
+
// to a real adapter module function; every special action must be on the
|
|
20
|
+
// hardcoded allowlist — a registered-but-unimplemented action (the audit's
|
|
21
|
+
// capability:read phantom) fails here.
|
|
22
|
+
// Direction 3 (surface guard): every action id surfaced by the ontology
|
|
23
|
+
// catalog (readable/writable/control/runtime) must be registered.
|
|
24
|
+
//
|
|
25
|
+
// The audit previously flagged 6 ghost actions: file.meta (const alias of
|
|
26
|
+
// readMeta), agent.trackTerminalSpawn / markTerminalReady / clearTerminalState
|
|
27
|
+
// (PTY terminal tracking), timer.isTimerControlAction /
|
|
28
|
+
// timerControlActionsForState (timer control predicates). These are module
|
|
29
|
+
// infrastructure, NOT action handlers: they are never dispatched as
|
|
30
|
+
// "<type>.<name>" typed actions, and the test asserts they are NOT registered
|
|
31
|
+
// (see D0 below).
|
|
32
|
+
|
|
33
|
+
// ── Paths ──────────────────────────────────────────────────────────────────
|
|
34
|
+
// This test lives at src/wf-ui-server/__tests__/; the repo root is 3 levels up.
|
|
35
|
+
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
|
|
36
|
+
const ADAPTER_DIR = path.join(REPO_ROOT, 'src', 'wf-ui-server', 'workflow-node-types');
|
|
37
|
+
const ADAPTER_FILE_SUFFIX = '-node.mjs';
|
|
38
|
+
|
|
39
|
+
// ── Registry membership helper ─────────────────────────────────────────────
|
|
40
|
+
// loadActionRegistry validates the legacy mainOnly actor shape that the P1
|
|
41
|
+
// data layer replaced with spawnGate; the loader's actor validation update is
|
|
42
|
+
// a later wave (backend gates). The drift guards below only read
|
|
43
|
+
// id/nodeType/implementation, so fall back to the raw parsed file instead of
|
|
44
|
+
// failing the whole suite on the stale actor rule.
|
|
45
|
+
function loadRegistryForDrift(root) {
|
|
46
|
+
try {
|
|
47
|
+
return loadActionRegistry(root);
|
|
48
|
+
} catch {
|
|
49
|
+
const registryPath = path.join(root, 'Harness', 'a2a', 'action-registry.json');
|
|
50
|
+
return JSON.parse(fs.readFileSync(registryPath, 'utf8'));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const REGISTRY = loadRegistryForDrift(REPO_ROOT);
|
|
55
|
+
const REGISTRY_IDS = new Set(REGISTRY.actions.map((action) => action.id));
|
|
56
|
+
|
|
57
|
+
function registryHas(id) {
|
|
58
|
+
return REGISTRY_IDS.has(String(id));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ── Hardcoded special-case allowlist ────────────────────────────────────────
|
|
62
|
+
// Pairs permitted to exist as registered actions without adapter-backed
|
|
63
|
+
// implementation (implementation.special === true). Every id on this list must
|
|
64
|
+
// stay in the registry (D2a), and every registry special must be on this list
|
|
65
|
+
// (D2b) — the two sets are asserted equal, so the list cannot rot.
|
|
66
|
+
const SPECIAL_CASE_IDS = new Set([
|
|
67
|
+
// Server-intercepted in server.mjs before adapter dispatch (routes to
|
|
68
|
+
// sendRuntimeSessionInput / startWorkflowGraphNode / restartWorkflowGraphNode
|
|
69
|
+
// / stopRuntimeSession / setWorkflowNodeModel / executeGraphLayoutAction).
|
|
70
|
+
'agent.start',
|
|
71
|
+
'agent.restart',
|
|
72
|
+
'agent.stop',
|
|
73
|
+
'agent.sendInput',
|
|
74
|
+
'agent.setModel',
|
|
75
|
+
'agent.layout',
|
|
76
|
+
// Runtime-special-cased in workflow-node-runtime executeNodeAction before
|
|
77
|
+
// adapter dispatch.
|
|
78
|
+
'node.delete',
|
|
79
|
+
'node.restore',
|
|
80
|
+
'agent.readContext',
|
|
81
|
+
// Main-Agent graph action surface (executeAgentGraphAction).
|
|
82
|
+
'agent.createNode',
|
|
83
|
+
'agent.connectNodes',
|
|
84
|
+
'agent.disconnectNodes',
|
|
85
|
+
'agent.moveNode',
|
|
86
|
+
'agent.deleteNode',
|
|
87
|
+
'agent.deleteNodes',
|
|
88
|
+
'agent.readGraph',
|
|
89
|
+
// Server-special-cased in server.mjs before adapter dispatch (routes to
|
|
90
|
+
// executeGraphHistoryAction → undoGraphOp / redoGraphOp).
|
|
91
|
+
'graph.undo',
|
|
92
|
+
'graph.redo',
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
// ── Documented non-action adapter exports (the audit's 6 ghost actions) ────
|
|
96
|
+
// Adapter module exports that are module infrastructure rather than typed
|
|
97
|
+
// action handlers. They are excluded from the ghost enumeration in D1 and must
|
|
98
|
+
// never be registered (D0b keeps this honest in both directions).
|
|
99
|
+
const NON_ACTION_EXPORTS = new Set([
|
|
100
|
+
'file.meta', // `export const meta = readMeta` — legacy alias, not a typed action
|
|
101
|
+
'agent.trackTerminalSpawn', // PTY terminal tracking helpers
|
|
102
|
+
'agent.markTerminalReady',
|
|
103
|
+
'agent.clearTerminalState',
|
|
104
|
+
'timer.isTimerControlAction', // timer control-predicate helpers
|
|
105
|
+
'timer.timerControlActionsForState',
|
|
106
|
+
'display.defaultDisplayTemplate', // display helper: themed fallback template
|
|
107
|
+
'display.renderHtml', // display helper: serve-time render + placeholder expansion
|
|
108
|
+
'display.reportHtmlPath', // display helper: html file location
|
|
109
|
+
'goal.updateTaskCapsuleOnComplete', // goal internal callback: auto-close task capsule on completion
|
|
110
|
+
'goal.updateTaskCapsuleOnReopen', // goal internal callback: restore phase on reopen
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
// ── Adapter module enumeration ─────────────────────────────────────────────
|
|
114
|
+
// ACTION_ADAPTERS in workflow-node-runtime.mjs is module-local, but its
|
|
115
|
+
// surface is exactly the workflow-node-types/*-node.mjs files (the -node.mjs
|
|
116
|
+
// suffix naturally excludes role-profile-store.mjs). The derived set is pinned
|
|
117
|
+
// to the registry's adapter values in D1a, so a divergence on either side
|
|
118
|
+
// fails the lockstep check.
|
|
119
|
+
function listAdapterTypes() {
|
|
120
|
+
return fs
|
|
121
|
+
.readdirSync(ADAPTER_DIR)
|
|
122
|
+
.filter((file) => file.endsWith(ADAPTER_FILE_SUFFIX))
|
|
123
|
+
.map((file) => file.slice(0, -ADAPTER_FILE_SUFFIX.length))
|
|
124
|
+
.sort();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function adapterModuleUrl(type) {
|
|
128
|
+
return pathToFileURL(path.join(ADAPTER_DIR, `${type}${ADAPTER_FILE_SUFFIX}`)).href;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function loadAdapterFunctionExports(type) {
|
|
132
|
+
const module = await import(adapterModuleUrl(type));
|
|
133
|
+
const names = [];
|
|
134
|
+
for (const name of Object.keys(module)) {
|
|
135
|
+
if (name === 'default') continue;
|
|
136
|
+
if (typeof module[name] === 'function') names.push(name);
|
|
137
|
+
}
|
|
138
|
+
return names.sort();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function adapterActionId(type, exportName) {
|
|
142
|
+
return `${type}.${exportName}`;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function registryAdapters(registry) {
|
|
146
|
+
return new Set(registry.actions.map((action) => action.implementation?.adapter).filter((a) => typeof a === 'string'));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ── D0: detector calibration (negative self-check) ─────────────────────────
|
|
150
|
+
test('D0a registry membership helper works both ways (negative self-check)', () => {
|
|
151
|
+
// A registered id is detected…
|
|
152
|
+
assert.equal(registryHas('agent.sendMessage'), true);
|
|
153
|
+
// …an unregistered id is detected (simulated ghost)…
|
|
154
|
+
assert.equal(registryHas('agent.fakeAction'), false);
|
|
155
|
+
// …and the audit's phantom is impossible: capability:read is not registered.
|
|
156
|
+
assert.equal(registryHas('capability.read'), false);
|
|
157
|
+
assert.equal(registryHas(''), false);
|
|
158
|
+
assert.equal(registryHas('markdown'), false);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('D0b documented non-action exports: functions that must never be registered', async () => {
|
|
162
|
+
assert.ok(NON_ACTION_EXPORTS.size > 0);
|
|
163
|
+
for (const id of NON_ACTION_EXPORTS) {
|
|
164
|
+
const separator = id.indexOf('.');
|
|
165
|
+
const type = id.slice(0, separator);
|
|
166
|
+
const exportName = id.slice(separator + 1);
|
|
167
|
+
// The exclusion list stays load-bearing: the helper must still exist as a
|
|
168
|
+
// function export (or the list must be cleaned up)…
|
|
169
|
+
const module = await import(adapterModuleUrl(type));
|
|
170
|
+
assert.equal(
|
|
171
|
+
typeof module[exportName],
|
|
172
|
+
'function',
|
|
173
|
+
`${id} is listed as a non-action helper but is no longer a function export of the ${type} adapter`,
|
|
174
|
+
);
|
|
175
|
+
// …and it must never drift into the registry (a promoted helper becomes a
|
|
176
|
+
// real action and must be audited through D1/D2 instead).
|
|
177
|
+
assert.equal(
|
|
178
|
+
registryHas(id),
|
|
179
|
+
false,
|
|
180
|
+
`${id} is a non-action helper export but has a registry entry; remove it from NON_ACTION_EXPORTS and audit it as a real action`,
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ── Direction 1: adapters → registry (ghost guard) ─────────────────────────
|
|
186
|
+
test('D1a adapter module set matches the registry adapter set', () => {
|
|
187
|
+
const diskAdapters = listAdapterTypes();
|
|
188
|
+
assert.deepEqual(diskAdapters, [...registryAdapters(REGISTRY)].sort());
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test('D1b every adapter function export is registered (ghost guard)', async () => {
|
|
192
|
+
const unregistered = [];
|
|
193
|
+
for (const type of listAdapterTypes()) {
|
|
194
|
+
const exportNames = await loadAdapterFunctionExports(type);
|
|
195
|
+
for (const exportName of exportNames) {
|
|
196
|
+
const id = adapterActionId(type, exportName);
|
|
197
|
+
if (NON_ACTION_EXPORTS.has(id)) continue;
|
|
198
|
+
// Registered as "<type>.<export>", or explicitly allowed as a
|
|
199
|
+
// special-cased surface (SPECIAL_CASE_IDS).
|
|
200
|
+
if (!registryHas(id) && !SPECIAL_CASE_IDS.has(id)) {
|
|
201
|
+
unregistered.push(id);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
assert.deepEqual(
|
|
206
|
+
unregistered,
|
|
207
|
+
[],
|
|
208
|
+
`implemented-but-unregistered action(s) (ghost actions): ${unregistered.join(', ')}`,
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// ── Direction 2: registry → implementations (phantom guard) ────────────────
|
|
213
|
+
test('D2a special registry entries are exactly the hardcoded allowlist', () => {
|
|
214
|
+
const registrySpecials = new Set(
|
|
215
|
+
REGISTRY.actions.filter((action) => action.implementation?.special === true).map((action) => action.id),
|
|
216
|
+
);
|
|
217
|
+
// Every special entry must be on the allowlist (no undocumented special).
|
|
218
|
+
const specialNotAllowed = [...registrySpecials].filter((id) => !SPECIAL_CASE_IDS.has(id));
|
|
219
|
+
assert.deepEqual(specialNotAllowed, [], `special action(s) missing from the allowlist: ${specialNotAllowed.join(', ')}`);
|
|
220
|
+
// Every allowlist id must be a registered special (no stale allowlist).
|
|
221
|
+
const allowedNotSpecial = [...SPECIAL_CASE_IDS].filter((id) => !registrySpecials.has(id));
|
|
222
|
+
assert.deepEqual(allowedNotSpecial, [], `allowlist id(s) not registered as special: ${allowedNotSpecial.join(', ')}`);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
test('D2b every non-special registry entry is implemented (phantom guard)', async () => {
|
|
226
|
+
const diskAdapters = new Set(listAdapterTypes());
|
|
227
|
+
const missing = [];
|
|
228
|
+
for (const action of REGISTRY.actions) {
|
|
229
|
+
const impl = action.implementation;
|
|
230
|
+
if (impl.special === true) continue; // covered by D2a
|
|
231
|
+
const id = action.id;
|
|
232
|
+
if (!diskAdapters.has(impl.adapter)) {
|
|
233
|
+
missing.push(`${id} (adapter module ${impl.adapter}-node.mjs does not exist)`);
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
const module = await import(adapterModuleUrl(impl.adapter));
|
|
237
|
+
if (typeof module[impl.export] !== 'function') {
|
|
238
|
+
missing.push(`${id} (adapter ${impl.adapter} has no function export '${impl.export}')`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
assert.deepEqual(
|
|
242
|
+
missing,
|
|
243
|
+
[],
|
|
244
|
+
`registered-but-unimplemented action(s) (phantom actions): ${missing.join(', ')}`,
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// ── Direction 3: ontology surface consistency (light) ──────────────────────
|
|
249
|
+
test('D3 every ontology catalog action id (readable/writable/control/runtime) is registered', () => {
|
|
250
|
+
const catalog = workflowOntology(REPO_ROOT);
|
|
251
|
+
const surfaceIds = new Set();
|
|
252
|
+
for (const [type, entry] of Object.entries(catalog.nodeTypes || {})) {
|
|
253
|
+
for (const bucket of ['readableActions', 'writableActions', 'controlActions', 'runtimeActions']) {
|
|
254
|
+
if (Array.isArray(entry[bucket])) {
|
|
255
|
+
for (const id of entry[bucket]) surfaceIds.add(`${id} (${type}.${bucket})`);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
assert.ok(surfaceIds.size > 0, 'ontology catalog exposes an empty action surface; nothing was checked');
|
|
260
|
+
const missing = [...surfaceIds].filter((labeled) => {
|
|
261
|
+
const id = labeled.slice(0, labeled.lastIndexOf(' ('));
|
|
262
|
+
return !registryHas(id);
|
|
263
|
+
});
|
|
264
|
+
assert.deepEqual(
|
|
265
|
+
missing,
|
|
266
|
+
[],
|
|
267
|
+
`ontology action id(s) outside the registry: ${missing.join(', ')}`,
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
test('D3b registry spawnGate "root" matches the ontology spawnRules in both directions', () => {
|
|
272
|
+
const catalog = workflowOntology(REPO_ROOT);
|
|
273
|
+
const gatedIds = new Set(
|
|
274
|
+
REGISTRY.actions.filter((action) => action.actor?.spawnGate === 'root').map((action) => action.id),
|
|
275
|
+
);
|
|
276
|
+
const ruleActionIds = new Set();
|
|
277
|
+
for (const [type, entry] of Object.entries(catalog.nodeTypes || {})) {
|
|
278
|
+
for (const rule of entry.spawnRules || []) {
|
|
279
|
+
assert.equal(rule.spawnRule, 'root-only', `ontology spawnRule for ${type}/${rule.actionId} must be root-only`);
|
|
280
|
+
assert.equal(rule.gate, 'root', `ontology spawnRule gate for ${type}/${rule.actionId} must be root`);
|
|
281
|
+
ruleActionIds.add(rule.actionId);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const missingRules = [...gatedIds].filter((id) => !ruleActionIds.has(id));
|
|
285
|
+
assert.deepEqual(
|
|
286
|
+
missingRules,
|
|
287
|
+
[],
|
|
288
|
+
`registry spawnGate "root" action(s) without an ontology spawnRule: ${missingRules.join(', ')}`,
|
|
289
|
+
);
|
|
290
|
+
const missingGates = [...ruleActionIds].filter((id) => !gatedIds.has(id));
|
|
291
|
+
assert.deepEqual(
|
|
292
|
+
missingGates,
|
|
293
|
+
[],
|
|
294
|
+
`ontology spawnRules action(s) without registry spawnGate "root": ${missingGates.join(', ')}`,
|
|
295
|
+
);
|
|
296
|
+
});
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { describe, it, before, after } from 'node:test';
|
|
2
|
+
import assert from 'node:assert';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import crypto from 'node:crypto';
|
|
6
|
+
|
|
7
|
+
// ── Helpers (mirror workflow-node-runtime.test.mjs / workflow-magnetic-topology.test.mjs) ──
|
|
8
|
+
function tempProjectRoot() {
|
|
9
|
+
const id = crypto.randomBytes(4).toString('hex');
|
|
10
|
+
const dir = path.join(process.cwd(), '.tmp-fourside-test-' + id);
|
|
11
|
+
fs.mkdirSync(path.join(dir, 'Harness', 'a2a', 'component-nodes'), { recursive: true });
|
|
12
|
+
fs.writeFileSync(path.join(dir, 'Harness', 'a2a', 'component-nodes', 'index.json'), JSON.stringify({ schemaVersion: 1, nodes: [] }));
|
|
13
|
+
fs.mkdirSync(path.join(dir, 'Harness', 'a2a'), { recursive: true });
|
|
14
|
+
fs.writeFileSync(path.join(dir, 'Harness', 'a2a', 'workflow-map.json'), JSON.stringify({ schemaVersion: 1, version: 1, nodes: [], edges: [], positions: {}, undoStack: [], redoStack: [], deletedNodes: [] }));
|
|
15
|
+
return dir;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function cleanup(dir) {
|
|
19
|
+
try { fs.rmSync(dir, { recursive: true, force: true }); } catch {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
let store;
|
|
23
|
+
let runtime;
|
|
24
|
+
|
|
25
|
+
before(async () => {
|
|
26
|
+
store = await import('../a2a-store.mjs');
|
|
27
|
+
runtime = await import('../workflow-node-runtime.mjs');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Node index passed to normalizeWorkflowGraphEdge so the normalizer can resolve
|
|
31
|
+
// each endpoint's semantic type without persisting component-node state.
|
|
32
|
+
const TYPED_NODES = [
|
|
33
|
+
{ nodeId: 'md1', type: 'markdown' },
|
|
34
|
+
{ nodeId: 'md2', type: 'markdown' },
|
|
35
|
+
{ nodeId: 'ex1', type: 'excalidraw' },
|
|
36
|
+
{ nodeId: 'fi1', type: 'file' },
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
function typedNodeIdFor(type) {
|
|
40
|
+
if (type === 'markdown') return 'md1';
|
|
41
|
+
if (type === 'excalidraw') return 'ex1';
|
|
42
|
+
if (type === 'file') return 'fi1';
|
|
43
|
+
throw new Error(`unknown typed node for ${type}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Normalize a single handle on a typed resource node from a given endpoint role.
|
|
47
|
+
function normalizeResourceHandle(root, type, handle, role) {
|
|
48
|
+
const typedId = typedNodeIdFor(type);
|
|
49
|
+
const edge = role === 'source'
|
|
50
|
+
? { id: 'e1', from: typedId, to: 'md2', sourceHandle: handle, relation: 'wf-bridge' }
|
|
51
|
+
: { id: 'e1', from: 'md2', to: typedId, targetHandle: handle, relation: 'wf-bridge' };
|
|
52
|
+
const out = store.normalizeWorkflowGraphEdge(root, edge, TYPED_NODES);
|
|
53
|
+
return role === 'source' ? out.sourceHandle : out.targetHandle;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Edge handle normalization (a2a-store) ──
|
|
57
|
+
describe('Resource four-side handle normalization (a2a-store)', () => {
|
|
58
|
+
let root;
|
|
59
|
+
before(() => { root = tempProjectRoot(); });
|
|
60
|
+
after(() => cleanup(root));
|
|
61
|
+
|
|
62
|
+
it('accepts markdown:top and markdown:bottom as physical sides (source + target)', () => {
|
|
63
|
+
// Newly added top/bottom sides must be preserved verbatim.
|
|
64
|
+
assert.equal(normalizeResourceHandle(root, 'markdown', 'markdown:top', 'source'), 'markdown:top');
|
|
65
|
+
assert.equal(normalizeResourceHandle(root, 'markdown', 'markdown:top', 'target'), 'markdown:top');
|
|
66
|
+
assert.equal(normalizeResourceHandle(root, 'markdown', 'markdown:bottom', 'source'), 'markdown:bottom');
|
|
67
|
+
assert.equal(normalizeResourceHandle(root, 'markdown', 'markdown:bottom', 'target'), 'markdown:bottom');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('still accepts markdown:left and markdown:right (no regression)', () => {
|
|
71
|
+
assert.equal(normalizeResourceHandle(root, 'markdown', 'markdown:left', 'source'), 'markdown:left');
|
|
72
|
+
assert.equal(normalizeResourceHandle(root, 'markdown', 'markdown:right', 'target'), 'markdown:right');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('accepts scene:top and scene:bottom as physical sides', () => {
|
|
76
|
+
assert.equal(normalizeResourceHandle(root, 'excalidraw', 'scene:top', 'source'), 'scene:top');
|
|
77
|
+
assert.equal(normalizeResourceHandle(root, 'excalidraw', 'scene:top', 'target'), 'scene:top');
|
|
78
|
+
assert.equal(normalizeResourceHandle(root, 'excalidraw', 'scene:bottom', 'source'), 'scene:bottom');
|
|
79
|
+
assert.equal(normalizeResourceHandle(root, 'excalidraw', 'scene:bottom', 'target'), 'scene:bottom');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('accepts file:top and file:bottom as physical sides', () => {
|
|
83
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'file:top', 'source'), 'file:top');
|
|
84
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'file:top', 'target'), 'file:top');
|
|
85
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'file:bottom', 'source'), 'file:bottom');
|
|
86
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'file:bottom', 'target'), 'file:bottom');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('treats file:left and file:right as bidirectional (role-independent, no input/output split)', () => {
|
|
90
|
+
// W6 + file input/output removal: the same physical side must be returned
|
|
91
|
+
// regardless of whether the file node is the source or target endpoint.
|
|
92
|
+
// Previously left=input / right=output; now both are plain bidirectional sides.
|
|
93
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'file:left', 'source'), 'file:left');
|
|
94
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'file:left', 'target'), 'file:left');
|
|
95
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'file:right', 'source'), 'file:right');
|
|
96
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'file:right', 'target'), 'file:right');
|
|
97
|
+
// A semantic alias like "file" still resolves, but to a physical side (not an input/output).
|
|
98
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'file', 'source'), 'file:right');
|
|
99
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'file', 'target'), 'file:left');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('keeps bare top/right/bottom/left aliases as physical sides before semantic fallback (W10)', () => {
|
|
103
|
+
// Bare side strings must map to the primary semantic port on that physical
|
|
104
|
+
// side, for ALL four sides, across all three resource kinds.
|
|
105
|
+
assert.equal(normalizeResourceHandle(root, 'markdown', 'top', 'source'), 'markdown:top');
|
|
106
|
+
assert.equal(normalizeResourceHandle(root, 'markdown', 'bottom', 'target'), 'markdown:bottom');
|
|
107
|
+
assert.equal(normalizeResourceHandle(root, 'markdown', 'left', 'source'), 'markdown:left');
|
|
108
|
+
assert.equal(normalizeResourceHandle(root, 'markdown', 'right', 'target'), 'markdown:right');
|
|
109
|
+
assert.equal(normalizeResourceHandle(root, 'excalidraw', 'top', 'source'), 'scene:top');
|
|
110
|
+
assert.equal(normalizeResourceHandle(root, 'excalidraw', 'bottom', 'source'), 'scene:bottom');
|
|
111
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'top', 'source'), 'file:top');
|
|
112
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'bottom', 'target'), 'file:bottom');
|
|
113
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'left', 'source'), 'file:left');
|
|
114
|
+
assert.equal(normalizeResourceHandle(root, 'file', 'right', 'target'), 'file:right');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('strips physical-side suffix from relation (W6: relation carries no side)', () => {
|
|
118
|
+
// relation must never carry a physical-side suffix; it stays semantic-only.
|
|
119
|
+
const edge = {
|
|
120
|
+
id: 'rel-e1',
|
|
121
|
+
from: 'md1',
|
|
122
|
+
to: 'md2',
|
|
123
|
+
relation: 'markdown:top',
|
|
124
|
+
sourceHandle: 'markdown:top',
|
|
125
|
+
targetHandle: 'markdown:bottom',
|
|
126
|
+
};
|
|
127
|
+
const out = store.normalizeWorkflowGraphEdge(root, edge, TYPED_NODES);
|
|
128
|
+
assert.equal(out.relation, 'markdown', 'relation suffix :top must be stripped');
|
|
129
|
+
assert.equal(out.sourceHandle, 'markdown:top');
|
|
130
|
+
assert.equal(out.targetHandle, 'markdown:bottom');
|
|
131
|
+
|
|
132
|
+
// scene / file side suffixes are stripped too.
|
|
133
|
+
const sceneOut = store.normalizeWorkflowGraphEdge(root, {
|
|
134
|
+
id: 'rel-e2', from: 'ex1', to: 'md2',
|
|
135
|
+
relation: 'scene:bottom', sourceHandle: 'scene:bottom',
|
|
136
|
+
}, TYPED_NODES);
|
|
137
|
+
assert.equal(sceneOut.relation, 'scene');
|
|
138
|
+
|
|
139
|
+
const fileOut = store.normalizeWorkflowGraphEdge(root, {
|
|
140
|
+
id: 'rel-e3', from: 'fi1', to: 'md2',
|
|
141
|
+
relation: 'file:left', sourceHandle: 'file:left',
|
|
142
|
+
}, TYPED_NODES);
|
|
143
|
+
assert.equal(fileOut.relation, 'file');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('rejects undirected duplicate edges regardless of which side they use', () => {
|
|
147
|
+
// Duplicate detection is unordered (A->B == B->A). The four-side expansion
|
|
148
|
+
// must not weaken this: the same endpoint pair is still a duplicate.
|
|
149
|
+
assert.throws(
|
|
150
|
+
() => store.assertUniqueWorkflowEdgePairs([
|
|
151
|
+
{ id: 'a', from: 'md1', to: 'md2', relation: 'wf-bridge' },
|
|
152
|
+
{ id: 'b', from: 'md2', to: 'md1', relation: 'wf-bridge' },
|
|
153
|
+
]),
|
|
154
|
+
(err) => err.code === 'DUPLICATE_EDGE',
|
|
155
|
+
);
|
|
156
|
+
// Even when handles are normalized to specific sides, the bidirectional
|
|
157
|
+
// pair key collapses to the same unordered endpoint pair.
|
|
158
|
+
const e1 = store.normalizeWorkflowGraphEdge(root, {
|
|
159
|
+
id: 'x', from: 'md1', to: 'md2', sourceHandle: 'markdown:top', targetHandle: 'markdown:bottom',
|
|
160
|
+
}, TYPED_NODES);
|
|
161
|
+
const e2 = store.normalizeWorkflowGraphEdge(root, {
|
|
162
|
+
id: 'y', from: 'md2', to: 'md1', sourceHandle: 'markdown:left', targetHandle: 'markdown:right',
|
|
163
|
+
}, TYPED_NODES);
|
|
164
|
+
assert.throws(
|
|
165
|
+
() => store.assertUniqueWorkflowEdgePairs([e1, e2]),
|
|
166
|
+
(err) => err.code === 'DUPLICATE_EDGE',
|
|
167
|
+
);
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// ── Snapshot handles (workflow-node-runtime) ──
|
|
172
|
+
describe('Resource node snapshots expose 4 physical handles (workflow-node-runtime)', () => {
|
|
173
|
+
let root;
|
|
174
|
+
before(() => { root = tempProjectRoot(); });
|
|
175
|
+
after(() => cleanup(root));
|
|
176
|
+
|
|
177
|
+
const FOUR_SIDES = ['left', 'right', 'top', 'bottom'];
|
|
178
|
+
|
|
179
|
+
it('markdown snapshot exposes markdown:{top,right,bottom,left} on one bidirectional port', async () => {
|
|
180
|
+
const result = await runtime.createNode(root, { type: 'markdown', title: 'Four-side MD' });
|
|
181
|
+
assert.ok(result.ok);
|
|
182
|
+
const handles = result.node.graph.handles;
|
|
183
|
+
assert.deepEqual(handles.bidirectional, ['markdown']);
|
|
184
|
+
assert.deepEqual(handles.ports, ['markdown']);
|
|
185
|
+
assert.deepEqual(handles.inputs, []);
|
|
186
|
+
assert.deepEqual(handles.outputs, []);
|
|
187
|
+
assert.deepEqual(handles.physical.sort(), FOUR_SIDES.map(side => `markdown:${side}`).sort());
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('excalidraw snapshot exposes scene:{top,right,bottom,left} on one bidirectional port', async () => {
|
|
191
|
+
const result = await runtime.createNode(root, { type: 'excalidraw', title: 'Four-side Scene' });
|
|
192
|
+
assert.ok(result.ok);
|
|
193
|
+
const handles = result.node.graph.handles;
|
|
194
|
+
assert.deepEqual(handles.bidirectional, ['scene']);
|
|
195
|
+
assert.deepEqual(handles.ports, ['scene']);
|
|
196
|
+
assert.deepEqual(handles.inputs, []);
|
|
197
|
+
assert.deepEqual(handles.outputs, []);
|
|
198
|
+
assert.deepEqual(handles.physical.sort(), FOUR_SIDES.map(side => `scene:${side}`).sort());
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('file snapshot exposes file:{top,right,bottom,left} with NO input/output split', async () => {
|
|
202
|
+
const result = await runtime.createNode(root, {
|
|
203
|
+
type: 'file',
|
|
204
|
+
title: 'Four-side File',
|
|
205
|
+
file: { source: 'workspace', path: 'README.md', name: 'README.md', mime: 'text/markdown', size: 100 },
|
|
206
|
+
});
|
|
207
|
+
assert.ok(result.ok);
|
|
208
|
+
assert.equal(result.node.kind, 'file');
|
|
209
|
+
const handles = result.node.graph.handles;
|
|
210
|
+
// The input/output split is removed: file is a single bidirectional port.
|
|
211
|
+
assert.deepEqual(handles.bidirectional, ['file']);
|
|
212
|
+
assert.deepEqual(handles.ports, ['file']);
|
|
213
|
+
assert.deepEqual(handles.inputs, []);
|
|
214
|
+
assert.deepEqual(handles.outputs, []);
|
|
215
|
+
assert.deepEqual(handles.physical.sort(), FOUR_SIDES.map(side => `file:${side}`).sort());
|
|
216
|
+
});
|
|
217
|
+
});
|