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,1014 @@
|
|
|
1
|
+
// Layer 2 CLI/API smoke test (task-backend-api-acceptance-matrix).
|
|
2
|
+
//
|
|
3
|
+
// Proves an Agent can drive the control plane through the real
|
|
4
|
+
// `Harness/scripts/wf-ui-control.mjs` CLI (25 commands wrapping the HTTP API).
|
|
5
|
+
// Each test spawns genuine `node Harness/scripts/wf-ui-control.mjs <command>`
|
|
6
|
+
// subprocesses against an in-process HTTP server started with
|
|
7
|
+
// `startServer({ port: 0 })`. The CLI script is copied into the temp project
|
|
8
|
+
// root so the spawned command runs exactly what an Agent node would run.
|
|
9
|
+
//
|
|
10
|
+
// Every test seeds its own graph nodes with per-test id prefixes so tests stay
|
|
11
|
+
// independent within the shared temp root (graph state accumulates across
|
|
12
|
+
// tests, so all assertions are membership-based, never exact totals).
|
|
13
|
+
import { describe, it, before, after } from 'node:test';
|
|
14
|
+
import assert from 'node:assert/strict';
|
|
15
|
+
import fs from 'node:fs';
|
|
16
|
+
import path from 'node:path';
|
|
17
|
+
import { spawn } from 'node:child_process';
|
|
18
|
+
import { makeHarnessTempRoot } from '../../../tests/support/temp-root.js';
|
|
19
|
+
import { startServer, stopServer } from '../server.mjs';
|
|
20
|
+
import { SessionRegistry } from '../session-registry.mjs';
|
|
21
|
+
import { persistSession, readTerminalRange } from '../terminal-store.mjs';
|
|
22
|
+
import { writeWorkflowGraphMap, loadWorkflowGraphMap } from '../a2a-store.mjs';
|
|
23
|
+
import { listBridgeMessages } from '../bridge-store.mjs';
|
|
24
|
+
import { registerPtyProcess, unregisterPtyProcess } from '../ws-terminal.mjs';
|
|
25
|
+
import { createComponentNode } from '../component-node-store.mjs';
|
|
26
|
+
|
|
27
|
+
// ── CLI runner ──────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
// Spawn a real `node Harness/scripts/wf-ui-control.mjs <command>` process.
|
|
30
|
+
// Resolves with { status, stdout, stderr }; stdout is the parsed JSON object
|
|
31
|
+
// when the CLI printed JSON, otherwise the raw string (e.g. error output).
|
|
32
|
+
function runWfUi(args, { cwd, env = {}, timeout = 15000 } = {}) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const script = path.join(cwd, 'Harness', 'scripts', 'wf-ui-control.mjs');
|
|
35
|
+
const child = spawn(process.execPath, [script, ...args], {
|
|
36
|
+
cwd,
|
|
37
|
+
env: { ...process.env, ...env },
|
|
38
|
+
windowsHide: true,
|
|
39
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
40
|
+
});
|
|
41
|
+
let stdout = '';
|
|
42
|
+
let stderr = '';
|
|
43
|
+
const timer = setTimeout(() => {
|
|
44
|
+
child.kill();
|
|
45
|
+
reject(new Error(`wf-ui-control timeout: ${args.join(' ')}`));
|
|
46
|
+
}, timeout);
|
|
47
|
+
child.stdout.on('data', (chunk) => { stdout += chunk; });
|
|
48
|
+
child.stderr.on('data', (chunk) => { stderr += chunk; });
|
|
49
|
+
child.on('error', reject);
|
|
50
|
+
child.on('close', (status) => {
|
|
51
|
+
clearTimeout(timer);
|
|
52
|
+
let parsed = stdout.trim();
|
|
53
|
+
try {
|
|
54
|
+
parsed = JSON.parse(parsed);
|
|
55
|
+
} catch {
|
|
56
|
+
// Non-JSON output (e.g. usage/error text) is left as the raw string.
|
|
57
|
+
}
|
|
58
|
+
resolve({ status, stdout: parsed, stderr });
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Poll until fn returns truthy. PTY prompt submission types char-by-char at
|
|
64
|
+
// 12ms/char, so writes keep landing after the CLI call itself resolves; any
|
|
65
|
+
// write-array assertion must poll the joined buffer until the trailing \r.
|
|
66
|
+
async function waitFor(fn, { timeout = 8000, step = 100 } = {}) {
|
|
67
|
+
const deadline = Date.now() + timeout;
|
|
68
|
+
let last;
|
|
69
|
+
while (Date.now() < deadline) {
|
|
70
|
+
last = await fn();
|
|
71
|
+
if (last) return last;
|
|
72
|
+
await new Promise(resolve => setTimeout(resolve, step));
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`waitFor timed out; last=${JSON.stringify(last)}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ── Fixtures ────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
let tempRoot;
|
|
80
|
+
let serverHandle;
|
|
81
|
+
let baseUrl;
|
|
82
|
+
let token;
|
|
83
|
+
let registry;
|
|
84
|
+
const registeredPtySessions = [];
|
|
85
|
+
|
|
86
|
+
// Identity environment every spawned Agent CLI runs with: the actor is a
|
|
87
|
+
// Main Agent node, so the CLI main-agent gates (assertMainAgent) pass.
|
|
88
|
+
const AGENT_ENV = { HARNESS_AGENT_KIND: 'main' };
|
|
89
|
+
|
|
90
|
+
// Flags shared by every command: where the project lives, and the control
|
|
91
|
+
// plane it talks to.
|
|
92
|
+
function baseArgs() {
|
|
93
|
+
return ['--project', tempRoot, '--url', baseUrl, '--token', token];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function seedRoot(prefix) {
|
|
97
|
+
const root = makeHarnessTempRoot(prefix);
|
|
98
|
+
fs.mkdirSync(path.join(root, 'Harness', 'tasks'), { recursive: true });
|
|
99
|
+
fs.mkdirSync(path.join(root, 'Harness', 'a2a'), { recursive: true });
|
|
100
|
+
return root;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function copyCliInto(root) {
|
|
104
|
+
const dest = path.join(root, 'Harness', 'scripts', 'wf-ui-control.mjs');
|
|
105
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
106
|
+
fs.copyFileSync(path.join(process.cwd(), 'Harness', 'scripts', 'wf-ui-control.mjs'), dest);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function agentNode(nodeId, sessionId, agentKind = 'subagent', role = 'Agent') {
|
|
110
|
+
return {
|
|
111
|
+
nodeId,
|
|
112
|
+
sessionId,
|
|
113
|
+
kind: 'terminal-session',
|
|
114
|
+
runtime: 'claude',
|
|
115
|
+
agentKind,
|
|
116
|
+
role,
|
|
117
|
+
label: role,
|
|
118
|
+
status: 'running',
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function persistAgentSession(root, nodeId, sessionId, agentKind = 'subagent', role = 'Agent') {
|
|
123
|
+
persistSession(root, {
|
|
124
|
+
sessionId,
|
|
125
|
+
graphNodeId: nodeId,
|
|
126
|
+
runtime: 'claude',
|
|
127
|
+
agentKind,
|
|
128
|
+
role,
|
|
129
|
+
status: 'running',
|
|
130
|
+
attachMode: true,
|
|
131
|
+
taskId: null,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Seed agent graph nodes (workflow-map.json) plus their durable sessions.
|
|
136
|
+
function seedAgents(nodes, edges = [], options = {}) {
|
|
137
|
+
writeWorkflowGraphMap(tempRoot, {
|
|
138
|
+
schemaVersion: 1,
|
|
139
|
+
nodes,
|
|
140
|
+
edges,
|
|
141
|
+
capsuleDockLinks: options.capsuleDockLinks || [],
|
|
142
|
+
positions: Object.fromEntries(nodes.map(node => [node.nodeId, { x: 0, y: 0 }])),
|
|
143
|
+
});
|
|
144
|
+
for (const node of nodes) {
|
|
145
|
+
persistAgentSession(tempRoot, node.nodeId, node.sessionId, node.agentKind, node.role);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Register a fake PTY whose writes are collected; returned array doubles as
|
|
150
|
+
// the assertion spy.
|
|
151
|
+
function attachPtySpy(sessionId) {
|
|
152
|
+
const writes = [];
|
|
153
|
+
registerPtyProcess(sessionId, { write: (data) => writes.push(String(data)) });
|
|
154
|
+
registeredPtySessions.push(sessionId);
|
|
155
|
+
return writes;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
before(async () => {
|
|
159
|
+
tempRoot = seedRoot('cp-cli-');
|
|
160
|
+
copyCliInto(tempRoot);
|
|
161
|
+
registry = new SessionRegistry();
|
|
162
|
+
const started = await startServer({
|
|
163
|
+
projectRoot: tempRoot,
|
|
164
|
+
host: '127.0.0.1',
|
|
165
|
+
port: 0,
|
|
166
|
+
sessionRegistry: registry,
|
|
167
|
+
eventsWs: false,
|
|
168
|
+
});
|
|
169
|
+
serverHandle = started.server;
|
|
170
|
+
token = started.token;
|
|
171
|
+
baseUrl = `http://127.0.0.1:${started.port}`;
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
after(async () => {
|
|
175
|
+
for (const sessionId of registeredPtySessions) unregisterPtyProcess(sessionId);
|
|
176
|
+
if (serverHandle) await stopServer(serverHandle);
|
|
177
|
+
if (tempRoot) {
|
|
178
|
+
const resolved = path.resolve(tempRoot);
|
|
179
|
+
const tempRootDir = path.resolve('Harness', '.temp') + path.sep;
|
|
180
|
+
assert.ok(resolved.startsWith(tempRootDir), `refusing to remove non-temp root: ${tempRoot}`);
|
|
181
|
+
try {
|
|
182
|
+
fs.rmSync(resolved, { recursive: true, force: true, maxRetries: 25, retryDelay: 200 });
|
|
183
|
+
} catch (e) {
|
|
184
|
+
// Windows may briefly hold session files after server close; tolerate.
|
|
185
|
+
if (process.platform === 'win32' && ['EPERM', 'EBUSY', 'ENOTEMPTY'].includes(e?.code)) return;
|
|
186
|
+
throw e;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// ── 1. create-agent ─────────────────────────────────────────────────────────
|
|
192
|
+
|
|
193
|
+
describe('create-agent', () => {
|
|
194
|
+
it('spawns a managed agent session and attaches its graph node', async () => {
|
|
195
|
+
const result = await runWfUi([
|
|
196
|
+
'create-agent',
|
|
197
|
+
...baseArgs(),
|
|
198
|
+
'--actor-kind', 'main',
|
|
199
|
+
'--agent-kind', 'main',
|
|
200
|
+
'--role', 'Main Agent',
|
|
201
|
+
'--runtime', 'claude',
|
|
202
|
+
// Keep the smoke test hermetic: never spawn a real PTY process.
|
|
203
|
+
'--defer',
|
|
204
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
205
|
+
|
|
206
|
+
assert.equal(result.status, 0, `create-agent failed: ${result.stderr}`);
|
|
207
|
+
assert.ok(result.stdout && typeof result.stdout === 'object', `non-JSON stdout: ${result.stdout}`);
|
|
208
|
+
const session = result.stdout;
|
|
209
|
+
assert.ok(session.sessionId, 'session must carry a sessionId');
|
|
210
|
+
assert.equal(session.agentKind, 'main');
|
|
211
|
+
assert.equal(session.displayName, 'Main Agent');
|
|
212
|
+
assert.equal(session.roleTitle, 'Main Agent');
|
|
213
|
+
assert.ok(session.graphNodeId, 'session must carry a graphNodeId');
|
|
214
|
+
|
|
215
|
+
// The backend must have added the new node to the workflow graph map.
|
|
216
|
+
const graph = loadWorkflowGraphMap(tempRoot);
|
|
217
|
+
const node = (graph.nodes || []).find(item => item.sessionId === session.sessionId);
|
|
218
|
+
assert.ok(node, 'graph must contain the created agent node');
|
|
219
|
+
assert.equal(node.nodeId, `session-${session.sessionId}`);
|
|
220
|
+
assert.equal(node.agentKind, 'main');
|
|
221
|
+
assert.equal(node.role, 'Main Agent');
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// ── 2. workflow-node-map readGraph ──────────────────────────────────────────
|
|
226
|
+
|
|
227
|
+
describe('workflow-node-map readGraph', () => {
|
|
228
|
+
it('reads nodes, edges, capsuleDockLinks, and version through the typed agent action', async () => {
|
|
229
|
+
seedAgents(
|
|
230
|
+
[
|
|
231
|
+
agentNode('t2-main', 'session-t2-main', 'main', 'Agent Main'),
|
|
232
|
+
agentNode('t2-b', 'session-t2-b', 'subagent', 'Agent B'),
|
|
233
|
+
agentNode('t2-c', 'session-t2-c', 'subagent', 'Agent C'),
|
|
234
|
+
],
|
|
235
|
+
[
|
|
236
|
+
{ id: 'edge-t2-main-b', from: 't2-main', to: 't2-b', relation: 'delegation', direction: 'bidirectional' },
|
|
237
|
+
{ id: 'edge-t2-main-c', from: 't2-main', to: 't2-c', relation: 'delegation', direction: 'bidirectional' },
|
|
238
|
+
],
|
|
239
|
+
{
|
|
240
|
+
capsuleDockLinks: [{
|
|
241
|
+
id: 'dock-t2-main-b',
|
|
242
|
+
nodeIds: ['t2-main', 't2-b'],
|
|
243
|
+
anchorId: 't2-main',
|
|
244
|
+
draggedId: 't2-b',
|
|
245
|
+
side: 'right',
|
|
246
|
+
edges: [{ edgeId: 'edge-t2-main-b', retention: 'keep' }],
|
|
247
|
+
connections: [{ source: 't2-main', target: 't2-b', relation: 'delegation' }],
|
|
248
|
+
}],
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
const result = await runWfUi([
|
|
253
|
+
'workflow-node-map',
|
|
254
|
+
...baseArgs(),
|
|
255
|
+
'--action', 'readGraph',
|
|
256
|
+
'--actor', 't2-main',
|
|
257
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
258
|
+
|
|
259
|
+
assert.equal(result.status, 0, `workflow-node-map failed: ${result.stderr}`);
|
|
260
|
+
assert.equal(result.stdout.ok, true);
|
|
261
|
+
assert.equal(result.stdout.action, 'agent.readGraph');
|
|
262
|
+
const graph = result.stdout.result.graph;
|
|
263
|
+
assert.ok(graph, 'readGraph must return a graph');
|
|
264
|
+
assert.ok(Number.isInteger(graph.version) && graph.version >= 1, `version must be a positive integer, got ${graph.version}`);
|
|
265
|
+
assert.ok(Array.isArray(graph.nodes), 'graph must carry nodes');
|
|
266
|
+
assert.ok(graph.nodes.some(node => node.nodeId === 't2-main'), 'graph must contain t2-main');
|
|
267
|
+
assert.ok(graph.nodes.some(node => node.nodeId === 't2-b'), 'graph must contain t2-b');
|
|
268
|
+
assert.ok(Array.isArray(graph.edges), 'graph must carry edges');
|
|
269
|
+
assert.ok(graph.edges.some(edge => edge.from === 't2-main' && edge.to === 't2-b'), 'graph must carry the t2-main -> t2-b edge');
|
|
270
|
+
assert.ok(Array.isArray(graph.capsuleDockLinks), 'graph must carry capsuleDockLinks');
|
|
271
|
+
assert.ok(graph.capsuleDockLinks.some(link => (link.nodeIds || []).includes('t2-main') && link.nodeIds.includes('t2-b')), 'dock link between t2-main and t2-b must be present');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('fails loudly when the actor node is missing', async () => {
|
|
275
|
+
seedAgents([agentNode('t2x-main', 'session-t2x-main', 'main', 'Agent Main')]);
|
|
276
|
+
const result = await runWfUi([
|
|
277
|
+
'workflow-node-map',
|
|
278
|
+
...baseArgs(),
|
|
279
|
+
'--action', 'readGraph',
|
|
280
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
281
|
+
assert.notEqual(result.status, 0, 'missing --actor must exit non-zero');
|
|
282
|
+
assert.match(String(result.stderr), /--actor|Missing/i);
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// ── 3. workflow-context ─────────────────────────────────────────────────────
|
|
287
|
+
|
|
288
|
+
describe('workflow-context', () => {
|
|
289
|
+
it('returns identity, connectedPeers, connectedNodeManuals, and allowed actions', async () => {
|
|
290
|
+
seedAgents(
|
|
291
|
+
[
|
|
292
|
+
agentNode('t3-main', 'session-t3-main', 'main', 'Agent Main'),
|
|
293
|
+
agentNode('t3-sub', 'session-t3-sub', 'subagent', 'Agent B'),
|
|
294
|
+
],
|
|
295
|
+
[
|
|
296
|
+
{ id: 'edge-t3', from: 't3-main', to: 't3-sub', relation: 'delegation', direction: 'bidirectional' },
|
|
297
|
+
],
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
const result = await runWfUi([
|
|
301
|
+
'workflow-context',
|
|
302
|
+
...baseArgs(),
|
|
303
|
+
'--node', 't3-sub',
|
|
304
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
305
|
+
|
|
306
|
+
assert.equal(result.status, 0, `workflow-context failed: ${result.stderr}`);
|
|
307
|
+
assert.equal(result.stdout.ok, true);
|
|
308
|
+
const context = result.stdout.context;
|
|
309
|
+
assert.ok(context, 'must return a context');
|
|
310
|
+
assert.equal(context.nodeId, 't3-sub');
|
|
311
|
+
assert.equal(context.identity.nodeId, 't3-sub');
|
|
312
|
+
assert.equal(context.identity.displayName, 'Agent B');
|
|
313
|
+
assert.equal(context.identity.agentKind, 'subagent');
|
|
314
|
+
assert.ok(Array.isArray(context.connectedPeers), 'must return connectedPeers');
|
|
315
|
+
assert.ok(
|
|
316
|
+
context.connectedPeers.some(peer => peer.nodeId === 't3-main' && peer.type === 'agent'),
|
|
317
|
+
`connectedPeers must include the connected t3-main agent: ${JSON.stringify(context.connectedPeers)}`,
|
|
318
|
+
);
|
|
319
|
+
assert.ok(Array.isArray(context.connectedNodeManuals), 'must return connectedNodeManuals');
|
|
320
|
+
assert.ok(Array.isArray(context.availableActions), 'must return availableActions');
|
|
321
|
+
assert.ok(context.availableActions.includes('agent.sendMessage'), 'availableActions must include agent.sendMessage');
|
|
322
|
+
const peerRef = (context.connectedAgentRefs || []).find(ref => ref.nodeId === 't3-main');
|
|
323
|
+
assert.ok(peerRef, 'connectedAgentRefs must include the connected peer');
|
|
324
|
+
assert.ok(Array.isArray(peerRef.allowedActions), 'peer ref must carry allowedActions');
|
|
325
|
+
assert.ok(peerRef.allowedActions.includes('agent.sendMessage'), 'peer allowedActions must include agent.sendMessage');
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// ── 4. send-agent-message (1-to-1) ──────────────────────────────────────────
|
|
330
|
+
|
|
331
|
+
describe('send-agent-message', () => {
|
|
332
|
+
it('delivers a direct structured message from A to B and records it in the bridge store', async () => {
|
|
333
|
+
seedAgents(
|
|
334
|
+
[
|
|
335
|
+
agentNode('t4-a', 'session-t4-a', 'main', 'Agent A'),
|
|
336
|
+
agentNode('t4-b', 'session-t4-b', 'subagent', 'Agent B'),
|
|
337
|
+
],
|
|
338
|
+
[
|
|
339
|
+
{ id: 'edge-t4', from: 't4-a', to: 't4-b', relation: 'delegation', direction: 'bidirectional' },
|
|
340
|
+
],
|
|
341
|
+
);
|
|
342
|
+
const writesA = attachPtySpy('session-t4-a');
|
|
343
|
+
const writesB = attachPtySpy('session-t4-b');
|
|
344
|
+
|
|
345
|
+
const result = await runWfUi([
|
|
346
|
+
'send-agent-message',
|
|
347
|
+
...baseArgs(),
|
|
348
|
+
'--node', 't4-a',
|
|
349
|
+
'--to', 't4-b',
|
|
350
|
+
'--text', 'Hello B',
|
|
351
|
+
'--request-id', 'test-req-1',
|
|
352
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
353
|
+
|
|
354
|
+
assert.equal(result.status, 0, `send-agent-message failed: ${result.stderr}`);
|
|
355
|
+
assert.equal(result.stdout.ok, true);
|
|
356
|
+
assert.equal(result.stdout.action, 'agent.sendMessage');
|
|
357
|
+
assert.equal(result.stdout.result.mode, 'direct');
|
|
358
|
+
assert.equal(result.stdout.result.requestId, 'test-req-1');
|
|
359
|
+
assert.equal(result.stdout.result.deliveries.length, 1);
|
|
360
|
+
assert.equal(result.stdout.result.deliveries[0].ok, true);
|
|
361
|
+
assert.equal(result.stdout.result.deliveries[0].fromNodeId, 't4-a');
|
|
362
|
+
assert.equal(result.stdout.result.deliveries[0].toNodeId, 't4-b');
|
|
363
|
+
assert.equal(result.stdout.result.deliveries[0].fromSessionId, 'session-t4-a');
|
|
364
|
+
assert.equal(result.stdout.result.deliveries[0].toSessionId, 'session-t4-b');
|
|
365
|
+
|
|
366
|
+
// Delivery reached B's PTY as the structured envelope prefix + text. The
|
|
367
|
+
// submit sequence is typed char-by-char, so wait until the joined buffer
|
|
368
|
+
// carries the envelope + text and the trailing submit \r has landed.
|
|
369
|
+
assert.deepEqual(writesA, [], 'sender PTY must not receive its own message');
|
|
370
|
+
await waitFor(() => {
|
|
371
|
+
const joined = writesB.join('');
|
|
372
|
+
return joined.includes('test-req-1') && joined.includes('Hello B') && joined.endsWith('\r')
|
|
373
|
+
? joined
|
|
374
|
+
: null;
|
|
375
|
+
});
|
|
376
|
+
const joinedB = writesB.join('');
|
|
377
|
+
assert.ok(joinedB.includes('test-req-1'), `target PTY must receive the request id: ${JSON.stringify(writesB)}`);
|
|
378
|
+
assert.ok(joinedB.includes('Hello B'), `target PTY must receive the text: ${JSON.stringify(writesB)}`);
|
|
379
|
+
assert.ok(joinedB.endsWith('\r'), `sequence must end with the submit \\r: ${JSON.stringify(writesB)}`);
|
|
380
|
+
|
|
381
|
+
// The bridge store holds the envelope with the request id.
|
|
382
|
+
const bridge = listBridgeMessages(tempRoot, {
|
|
383
|
+
fromSessionId: 'session-t4-a',
|
|
384
|
+
toSessionId: 'session-t4-b',
|
|
385
|
+
});
|
|
386
|
+
const entry = (bridge.entries || []).find(item => item.requestId === 'test-req-1');
|
|
387
|
+
assert.ok(entry, `bridge store must contain the request-id message: ${JSON.stringify(bridge.entries)}`);
|
|
388
|
+
// data may carry a trailing \r from PTY submit; match the content.
|
|
389
|
+
assert.match(entry.data, /Hello B/);
|
|
390
|
+
assert.equal(entry.fromNodeId, 't4-a');
|
|
391
|
+
assert.equal(entry.toNodeId, 't4-b');
|
|
392
|
+
assert.equal(entry.fromSessionId, 'session-t4-a');
|
|
393
|
+
assert.equal(entry.toSessionId, 'session-t4-b');
|
|
394
|
+
assert.equal(entry.deliveryMode, 'direct');
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// ── 5. read-agent-messages ──────────────────────────────────────────────────
|
|
399
|
+
|
|
400
|
+
describe('read-agent-messages', () => {
|
|
401
|
+
it('reads the recorded conversation between two agents with correct from/to', async () => {
|
|
402
|
+
seedAgents(
|
|
403
|
+
[
|
|
404
|
+
agentNode('t5-a', 'session-t5-a', 'main', 'Agent A'),
|
|
405
|
+
agentNode('t5-b', 'session-t5-b', 'subagent', 'Agent B'),
|
|
406
|
+
],
|
|
407
|
+
[
|
|
408
|
+
{ id: 'edge-t5', from: 't5-a', to: 't5-b', relation: 'delegation', direction: 'bidirectional' },
|
|
409
|
+
],
|
|
410
|
+
);
|
|
411
|
+
attachPtySpy('session-t5-a');
|
|
412
|
+
attachPtySpy('session-t5-b');
|
|
413
|
+
|
|
414
|
+
// Send first through the CLI so the read has real recorded envelopes.
|
|
415
|
+
const sent = await runWfUi([
|
|
416
|
+
'send-agent-message',
|
|
417
|
+
...baseArgs(),
|
|
418
|
+
'--node', 't5-a',
|
|
419
|
+
'--to', 't5-b',
|
|
420
|
+
'--text', 'Hello B',
|
|
421
|
+
'--request-id', 'test-req-1',
|
|
422
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
423
|
+
assert.equal(sent.status, 0, `send-agent-message failed: ${sent.stderr}`);
|
|
424
|
+
assert.equal(sent.stdout.ok, true);
|
|
425
|
+
|
|
426
|
+
const result = await runWfUi([
|
|
427
|
+
'read-agent-messages',
|
|
428
|
+
...baseArgs(),
|
|
429
|
+
'--node', 't5-a',
|
|
430
|
+
'--peer', 't5-b',
|
|
431
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
432
|
+
|
|
433
|
+
assert.equal(result.status, 0, `read-agent-messages failed: ${result.stderr}`);
|
|
434
|
+
assert.equal(result.stdout.ok, true);
|
|
435
|
+
assert.equal(result.stdout.action, 'agent.readMessages');
|
|
436
|
+
// executeNodeAction may spread or nest the handler result; accept both.
|
|
437
|
+
const allEntries = result.stdout.result?.entries || result.stdout.entries || [];
|
|
438
|
+
assert.ok(Array.isArray(allEntries), `must return entries: ${JSON.stringify(result.stdout).slice(0, 500)}`);
|
|
439
|
+
const entry = (allEntries).find(item => item.requestId === 'test-req-1');
|
|
440
|
+
assert.ok(entry, `entries must include the sent message: ${JSON.stringify(allEntries)}`);
|
|
441
|
+
assert.match(entry.data, /Hello B/);
|
|
442
|
+
assert.equal(entry.fromNodeId, 't5-a');
|
|
443
|
+
assert.equal(entry.toNodeId, 't5-b');
|
|
444
|
+
assert.equal(entry.fromSessionId, 'session-t5-a');
|
|
445
|
+
assert.equal(entry.toSessionId, 'session-t5-b');
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
// ── 6. broadcast-agent-message (1-to-many) ──────────────────────────────────
|
|
450
|
+
|
|
451
|
+
describe('broadcast-agent-message', () => {
|
|
452
|
+
it('delivers one message to every explicit target and queues it for each', async () => {
|
|
453
|
+
seedAgents(
|
|
454
|
+
[
|
|
455
|
+
agentNode('t6-a', 'session-t6-a', 'main', 'Agent A'),
|
|
456
|
+
agentNode('t6-b', 'session-t6-b', 'subagent', 'Agent B'),
|
|
457
|
+
agentNode('t6-c', 'session-t6-c', 'subagent', 'Agent C'),
|
|
458
|
+
agentNode('t6-d', 'session-t6-d', 'subagent', 'Agent D'),
|
|
459
|
+
],
|
|
460
|
+
[
|
|
461
|
+
{ id: 'edge-t6-a-b', from: 't6-a', to: 't6-b', relation: 'delegation', direction: 'bidirectional' },
|
|
462
|
+
{ id: 'edge-t6-a-c', from: 't6-a', to: 't6-c', relation: 'delegation', direction: 'bidirectional' },
|
|
463
|
+
{ id: 'edge-t6-a-d', from: 't6-a', to: 't6-d', relation: 'delegation', direction: 'bidirectional' },
|
|
464
|
+
],
|
|
465
|
+
);
|
|
466
|
+
const writesB = attachPtySpy('session-t6-b');
|
|
467
|
+
const writesC = attachPtySpy('session-t6-c');
|
|
468
|
+
const writesD = attachPtySpy('session-t6-d');
|
|
469
|
+
|
|
470
|
+
const result = await runWfUi([
|
|
471
|
+
'broadcast-agent-message',
|
|
472
|
+
...baseArgs(),
|
|
473
|
+
'--node', 't6-a',
|
|
474
|
+
'--to', 't6-b,t6-c,t6-d',
|
|
475
|
+
'--text', 'Broadcast',
|
|
476
|
+
'--request-id', 'test-bcast-1',
|
|
477
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
478
|
+
|
|
479
|
+
assert.equal(result.status, 0, `broadcast-agent-message failed: ${result.stderr}`);
|
|
480
|
+
assert.equal(result.stdout.ok, true);
|
|
481
|
+
assert.equal(result.stdout.action, 'agent.broadcastMessage');
|
|
482
|
+
assert.equal(result.stdout.result.mode, 'broadcast');
|
|
483
|
+
assert.equal(result.stdout.result.requestId, 'test-bcast-1');
|
|
484
|
+
assert.equal(result.stdout.result.deliveredCount, 3);
|
|
485
|
+
assert.equal(result.stdout.result.failedCount, 0);
|
|
486
|
+
assert.deepEqual(
|
|
487
|
+
result.stdout.result.deliveries.map(item => item.toNodeId).sort(),
|
|
488
|
+
['t6-b', 't6-c', 't6-d'],
|
|
489
|
+
);
|
|
490
|
+
assert.ok(result.stdout.result.deliveries.every(item => item.ok), 'every delivery must succeed');
|
|
491
|
+
|
|
492
|
+
// Every target's PTY receives the broadcast envelope. The submit sequence
|
|
493
|
+
// is typed char-by-char, so wait until the joined buffer carries the
|
|
494
|
+
// envelope + text and the trailing submit \r has landed.
|
|
495
|
+
for (const [label, writes] of [['B', writesB], ['C', writesC], ['D', writesD]]) {
|
|
496
|
+
await waitFor(() => {
|
|
497
|
+
const joined = writes.join('');
|
|
498
|
+
return joined.includes('test-bcast-1') && joined.includes('Broadcast') && joined.endsWith('\r')
|
|
499
|
+
? joined
|
|
500
|
+
: null;
|
|
501
|
+
});
|
|
502
|
+
const joined = writes.join('');
|
|
503
|
+
assert.ok(joined.includes('test-bcast-1'), `target ${label} PTY must receive the request id: ${JSON.stringify(writes)}`);
|
|
504
|
+
assert.ok(joined.includes('Broadcast'), `target ${label} PTY must receive the broadcast text: ${JSON.stringify(writes)}`);
|
|
505
|
+
assert.ok(joined.endsWith('\r'), `target ${label} sequence must end with the submit \\r: ${JSON.stringify(writes)}`);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Every target's bridge queue holds the broadcast envelope.
|
|
509
|
+
for (const sub of ['t6-b', 't6-c', 't6-d']) {
|
|
510
|
+
const bridge = listBridgeMessages(tempRoot, {
|
|
511
|
+
fromSessionId: 'session-t6-a',
|
|
512
|
+
toSessionId: `session-${sub}`,
|
|
513
|
+
});
|
|
514
|
+
const entry = (bridge.entries || []).find(item => item.requestId === 'test-bcast-1');
|
|
515
|
+
assert.ok(entry, `bridge for ${sub} must contain the broadcast`);
|
|
516
|
+
assert.match(entry.data, /Broadcast/);
|
|
517
|
+
assert.equal(entry.fromNodeId, 't6-a');
|
|
518
|
+
assert.equal(entry.toNodeId, sub);
|
|
519
|
+
assert.equal(entry.deliveryMode, 'broadcast');
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// ── 7. send-input ───────────────────────────────────────────────────────────
|
|
525
|
+
|
|
526
|
+
describe('send-input', () => {
|
|
527
|
+
it('writes terminal input for a managed session and records it in the terminal store', async () => {
|
|
528
|
+
const session = registry.create({
|
|
529
|
+
runtime: 'claude',
|
|
530
|
+
agentKind: 'main',
|
|
531
|
+
role: 'Main Agent',
|
|
532
|
+
taskId: null,
|
|
533
|
+
});
|
|
534
|
+
registry.update(session.sessionId, { status: 'running' });
|
|
535
|
+
const writes = attachPtySpy(session.sessionId);
|
|
536
|
+
|
|
537
|
+
const result = await runWfUi([
|
|
538
|
+
'send-input',
|
|
539
|
+
...baseArgs(),
|
|
540
|
+
'--actor-kind', 'main',
|
|
541
|
+
'--session', session.sessionId,
|
|
542
|
+
'--text', 'test input',
|
|
543
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
544
|
+
|
|
545
|
+
assert.equal(result.status, 0, `send-input failed: ${result.stderr}`);
|
|
546
|
+
assert.equal(result.stdout.ok, true);
|
|
547
|
+
assert.equal(result.stdout.sessionId, session.sessionId);
|
|
548
|
+
|
|
549
|
+
// The CLI appends a submit-enter so the PTY receives the data + \r.
|
|
550
|
+
assert.deepEqual(writes, ['test input\r'], 'PTY must receive the input with submit enter');
|
|
551
|
+
|
|
552
|
+
// The terminal store records the input as a stdin transcript entry.
|
|
553
|
+
const transcript = readTerminalRange(tempRoot, { sessionId: session.sessionId, tail: 10 });
|
|
554
|
+
assert.ok(
|
|
555
|
+
(transcript.entries || []).some(entry => entry.stream === 'stdin' && entry.data.includes('test input')),
|
|
556
|
+
`terminal transcript must record the input: ${JSON.stringify(transcript.entries)}`,
|
|
557
|
+
);
|
|
558
|
+
|
|
559
|
+
// The input-requests.jsonl file records the request.
|
|
560
|
+
const inputRequestsPath = path.join(tempRoot, 'Harness', 'a2a', 'sessions', session.sessionId, 'input-requests.jsonl');
|
|
561
|
+
assert.ok(fs.existsSync(inputRequestsPath), 'input-requests.jsonl must exist');
|
|
562
|
+
const requests = fs.readFileSync(inputRequestsPath, 'utf8')
|
|
563
|
+
.split(/\r?\n/)
|
|
564
|
+
.filter(Boolean)
|
|
565
|
+
.map(line => JSON.parse(line));
|
|
566
|
+
assert.ok(
|
|
567
|
+
requests.some(item => String(item.text || item.data || '').includes('test input')),
|
|
568
|
+
`input-requests.jsonl must contain the request: ${JSON.stringify(requests)}`,
|
|
569
|
+
);
|
|
570
|
+
});
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
// ── 8. connect ──────────────────────────────────────────────────────────────
|
|
574
|
+
|
|
575
|
+
describe('connect', () => {
|
|
576
|
+
it('connects two graph nodes and the graph gains a new edge between them', async () => {
|
|
577
|
+
seedAgents([
|
|
578
|
+
agentNode('t8-main', 'session-t8-main', 'main', 'Agent Main'),
|
|
579
|
+
agentNode('t8-b', 'session-t8-b', 'subagent', 'Agent B'),
|
|
580
|
+
]);
|
|
581
|
+
|
|
582
|
+
const result = await runWfUi([
|
|
583
|
+
'connect',
|
|
584
|
+
...baseArgs(),
|
|
585
|
+
'--actor', 't8-main',
|
|
586
|
+
'--from', 't8-main',
|
|
587
|
+
'--to', 't8-b',
|
|
588
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
589
|
+
|
|
590
|
+
assert.equal(result.status, 0, `connect failed: ${result.stderr}`);
|
|
591
|
+
assert.equal(result.stdout.ok, true);
|
|
592
|
+
assert.equal(result.stdout.action, 'agent.connectNodes');
|
|
593
|
+
assert.equal(result.stdout.result.edge.from, 't8-main');
|
|
594
|
+
assert.equal(result.stdout.result.edge.to, 't8-b');
|
|
595
|
+
assert.equal(result.stdout.result.edge.relation, 'delegation');
|
|
596
|
+
|
|
597
|
+
// The workflow graph map must now hold the new edge.
|
|
598
|
+
const graph = loadWorkflowGraphMap(tempRoot);
|
|
599
|
+
assert.ok(
|
|
600
|
+
(graph.edges || []).some(edge => edge.from === 't8-main' && edge.to === 't8-b'),
|
|
601
|
+
`graph must contain the new edge: ${JSON.stringify(graph.edges)}`,
|
|
602
|
+
);
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
// ── 9. read-node ────────────────────────────────────────────────────────────
|
|
607
|
+
|
|
608
|
+
describe('read-node', () => {
|
|
609
|
+
it('reads a single agent node by id', async () => {
|
|
610
|
+
seedAgents([agentNode('t9-b', 'session-t9-b', 'subagent', 'Agent B')]);
|
|
611
|
+
|
|
612
|
+
const result = await runWfUi([
|
|
613
|
+
'read-node',
|
|
614
|
+
...baseArgs(),
|
|
615
|
+
'--node', 't9-b',
|
|
616
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
617
|
+
|
|
618
|
+
assert.equal(result.status, 0, `read-node failed: ${result.stderr}`);
|
|
619
|
+
assert.equal(result.stdout.ok, true);
|
|
620
|
+
assert.equal(result.stdout.node.nodeId, 't9-b');
|
|
621
|
+
assert.equal(result.stdout.node.kind, 'agent');
|
|
622
|
+
assert.equal(result.stdout.node.sessionId, 'session-t9-b');
|
|
623
|
+
assert.equal(result.stdout.node.status.state, 'running');
|
|
624
|
+
});
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
// ── 10. workflow-node-action (markdown.read) ────────────────────────────────
|
|
628
|
+
|
|
629
|
+
describe('workflow-node-action', () => {
|
|
630
|
+
it('reads a markdown node through the typed markdown.read action', async () => {
|
|
631
|
+
const created = createComponentNode(tempRoot, {
|
|
632
|
+
type: 'markdown',
|
|
633
|
+
title: 'Notes',
|
|
634
|
+
markdown: '# Hello\nsmoke body',
|
|
635
|
+
});
|
|
636
|
+
const mdNodeId = created.node.nodeId;
|
|
637
|
+
assert.ok(mdNodeId, 'component node must carry a nodeId');
|
|
638
|
+
|
|
639
|
+
const result = await runWfUi([
|
|
640
|
+
'workflow-node-action',
|
|
641
|
+
...baseArgs(),
|
|
642
|
+
'--node', mdNodeId,
|
|
643
|
+
'--action', 'markdown.read',
|
|
644
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
645
|
+
|
|
646
|
+
assert.equal(result.status, 0, `workflow-node-action failed: ${result.stderr}`);
|
|
647
|
+
assert.equal(result.stdout.ok, true);
|
|
648
|
+
assert.equal(result.stdout.action, 'markdown.read');
|
|
649
|
+
assert.equal(result.stdout.node.nodeId, mdNodeId);
|
|
650
|
+
assert.equal(result.stdout.result.title, 'Notes');
|
|
651
|
+
assert.equal(result.stdout.result.markdown, '# Hello\nsmoke body');
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
// ── 11. Full-Chain CLI E2E: E0 → E18 ────────────────────────────────────────
|
|
656
|
+
|
|
657
|
+
describe('Full-Chain CLI E2E (E0-E18)', () => {
|
|
658
|
+
// This test proves the critical CLI path from a blank node map through
|
|
659
|
+
// the complete E0-E18 matrix using only `Harness/scripts/wf-ui-control.mjs`
|
|
660
|
+
// subprocess commands. Every graph/node mutation goes through the real CLI.
|
|
661
|
+
// We do not duplicate the in-process backend suite; we prove the CLI path
|
|
662
|
+
// covers the most important chains: blank→create→connect→communicate→
|
|
663
|
+
// markdown→timer→goal→aggregate.
|
|
664
|
+
|
|
665
|
+
it('chains: clear map -> create Main Agent -> subagents -> connect -> 1:1 + 1:many messages -> markdown shared context -> goal -> timer -> aggregate evidence', async () => {
|
|
666
|
+
// ── E0: Start from a clean graph ──
|
|
667
|
+
// Create several nodes first so we can prove clearing works.
|
|
668
|
+
const preMain = await runWfUi([
|
|
669
|
+
'create-agent',
|
|
670
|
+
...baseArgs(),
|
|
671
|
+
'--actor-kind', 'main',
|
|
672
|
+
'--agent-kind', 'main',
|
|
673
|
+
'--role', 'Main Agent',
|
|
674
|
+
'--runtime', 'claude',
|
|
675
|
+
'--defer',
|
|
676
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
677
|
+
assert.equal(preMain.status, 0);
|
|
678
|
+
const preNodeId = `session-${preMain.stdout.sessionId}`;
|
|
679
|
+
const preGraph = loadWorkflowGraphMap(tempRoot);
|
|
680
|
+
assert.ok(preGraph.nodes.length >= 1, 'graph must have at least 1 node before clear');
|
|
681
|
+
|
|
682
|
+
// Clear all nodes except the actor (which is also the CLI actor, so it survives).
|
|
683
|
+
const cleared = await runWfUi([
|
|
684
|
+
'workflow-node-map',
|
|
685
|
+
...baseArgs(),
|
|
686
|
+
'--action', 'deleteNodes',
|
|
687
|
+
'--all', 'true',
|
|
688
|
+
'--actor', preNodeId,
|
|
689
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
690
|
+
assert.equal(cleared.status, 0, `clear failed: ${cleared.stderr}`);
|
|
691
|
+
assert.equal(cleared.stdout.ok, true);
|
|
692
|
+
assert.equal(cleared.stdout.action, 'agent.deleteNodes');
|
|
693
|
+
const afterClear = loadWorkflowGraphMap(tempRoot);
|
|
694
|
+
// After clear, only the surviving actor remains.
|
|
695
|
+
const afterClearIds = (afterClear.nodes || []).map(n => n.nodeId || n.id);
|
|
696
|
+
assert.ok(afterClearIds.includes(preNodeId), 'actor must survive');
|
|
697
|
+
assert.ok(afterClearIds.length >= 1, `after clear at least actor: ${JSON.stringify(afterClearIds)}`);
|
|
698
|
+
|
|
699
|
+
// ── E1: Create fresh Main Agent as the orchestrator ──
|
|
700
|
+
const main = await runWfUi([
|
|
701
|
+
'create-agent',
|
|
702
|
+
...baseArgs(),
|
|
703
|
+
'--actor-kind', 'main',
|
|
704
|
+
'--agent-kind', 'main',
|
|
705
|
+
'--role', 'Main Agent',
|
|
706
|
+
'--runtime', 'claude',
|
|
707
|
+
'--defer',
|
|
708
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
709
|
+
assert.equal(main.status, 0, `create main agent failed: ${main.stderr}`);
|
|
710
|
+
const mainNodeId = `session-${main.stdout.sessionId}`;
|
|
711
|
+
const mainSessionId = main.stdout.sessionId;
|
|
712
|
+
assert.ok(mainNodeId, 'Main Agent must have a graph node');
|
|
713
|
+
assert.equal(main.stdout.displayName, 'Main Agent');
|
|
714
|
+
assert.equal(main.stdout.roleTitle, 'Main Agent');
|
|
715
|
+
|
|
716
|
+
// ── E3: Create 3 role-distinct subagents ──
|
|
717
|
+
const roles = ['implementer', 'reviewer', 'tester'];
|
|
718
|
+
const workers = [];
|
|
719
|
+
for (const role of roles) {
|
|
720
|
+
const w = await runWfUi([
|
|
721
|
+
'create-agent',
|
|
722
|
+
...baseArgs(),
|
|
723
|
+
'--actor-kind', 'main',
|
|
724
|
+
'--agent-kind', 'subagent',
|
|
725
|
+
'--role', 'Subagent',
|
|
726
|
+
'--runtime', 'claude',
|
|
727
|
+
'--defer',
|
|
728
|
+
'--payload', JSON.stringify({ roleTitle: role, displayName: role, responsibility: `${role} agent` }),
|
|
729
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
730
|
+
assert.equal(w.status, 0, `create ${role} failed: ${w.stderr}`);
|
|
731
|
+
const wNodeId = `session-${w.stdout.sessionId}`;
|
|
732
|
+
workers.push({ nodeId: wNodeId, sessionId: w.stdout.sessionId, role });
|
|
733
|
+
// Register fake PTYs so messages can deliver.
|
|
734
|
+
attachPtySpy(w.stdout.sessionId);
|
|
735
|
+
}
|
|
736
|
+
// Register fake PTY for Main Agent too.
|
|
737
|
+
attachPtySpy(mainSessionId);
|
|
738
|
+
|
|
739
|
+
// ── E4: Connect Main Agent to each worker ──
|
|
740
|
+
for (const w of workers) {
|
|
741
|
+
const conn = await runWfUi([
|
|
742
|
+
'connect',
|
|
743
|
+
...baseArgs(),
|
|
744
|
+
'--actor', mainNodeId,
|
|
745
|
+
'--from', mainNodeId,
|
|
746
|
+
'--to', w.nodeId,
|
|
747
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
748
|
+
assert.equal(conn.status, 0, `connect to ${w.role} failed: ${conn.stderr}`);
|
|
749
|
+
assert.equal(conn.stdout.ok, true);
|
|
750
|
+
assert.equal(conn.stdout.action, 'agent.connectNodes');
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// ── E2: Main Agent observes graph and context ──
|
|
754
|
+
const graph = await runWfUi([
|
|
755
|
+
'workflow-node-map',
|
|
756
|
+
...baseArgs(),
|
|
757
|
+
'--action', 'readGraph',
|
|
758
|
+
'--actor', mainNodeId,
|
|
759
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
760
|
+
assert.equal(graph.status, 0, `readGraph failed: ${graph.stderr}`);
|
|
761
|
+
assert.ok(Array.isArray(graph.stdout.result.graph.nodes));
|
|
762
|
+
assert.ok(graph.stdout.result.graph.nodes.length >= 4, `expect >= 4 nodes: ${graph.stdout.result.graph.nodes.length}`);
|
|
763
|
+
assert.ok(graph.stdout.result.graph.edges.length >= 3, `expect >= 3 edges: ${graph.stdout.result.graph.edges.length}`);
|
|
764
|
+
|
|
765
|
+
const ctx = await runWfUi([
|
|
766
|
+
'workflow-context',
|
|
767
|
+
...baseArgs(),
|
|
768
|
+
'--node', mainNodeId,
|
|
769
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
770
|
+
assert.equal(ctx.status, 0, `context failed: ${ctx.stderr}`);
|
|
771
|
+
assert.equal(ctx.stdout.context.identity.nodeId, mainNodeId);
|
|
772
|
+
assert.ok(ctx.stdout.context.connectedPeers.length >= 3, `peers: ${ctx.stdout.context.connectedPeers.length}`);
|
|
773
|
+
|
|
774
|
+
// ── E5: 1-to-1 mailbox - Main Agent sends to one worker ──
|
|
775
|
+
const direct = await runWfUi([
|
|
776
|
+
'send-agent-message',
|
|
777
|
+
...baseArgs(),
|
|
778
|
+
'--node', mainNodeId,
|
|
779
|
+
'--to', workers[0].nodeId,
|
|
780
|
+
'--text', 'Implement the login module',
|
|
781
|
+
'--request-id', 'chain-req-1',
|
|
782
|
+
'--actor-kind', 'main',
|
|
783
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
784
|
+
assert.equal(direct.status, 0, `1:1 msg failed: ${direct.stderr}`);
|
|
785
|
+
assert.equal(direct.stdout.ok, true);
|
|
786
|
+
assert.equal(direct.stdout.result.requestId, 'chain-req-1');
|
|
787
|
+
assert.equal(direct.stdout.result.deliveries.length, 1);
|
|
788
|
+
|
|
789
|
+
// Worker replies.
|
|
790
|
+
const reply = await runWfUi([
|
|
791
|
+
'send-agent-message',
|
|
792
|
+
...baseArgs(),
|
|
793
|
+
'--node', workers[0].nodeId,
|
|
794
|
+
'--to', mainNodeId,
|
|
795
|
+
'--text', 'Login module implemented',
|
|
796
|
+
'--request-id', 'chain-req-1',
|
|
797
|
+
'--actor-kind', 'subagent',
|
|
798
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
799
|
+
assert.equal(reply.status, 0, `reply failed: ${reply.stderr}`);
|
|
800
|
+
assert.equal(reply.stdout.result.requestId, 'chain-req-1');
|
|
801
|
+
|
|
802
|
+
// ── E6: 1-to-many broadcast - Main Agent sends to all workers ──
|
|
803
|
+
const broadcast = await runWfUi([
|
|
804
|
+
'broadcast-agent-message',
|
|
805
|
+
...baseArgs(),
|
|
806
|
+
'--node', mainNodeId,
|
|
807
|
+
'--to', workers.map(w => w.nodeId).join(','),
|
|
808
|
+
'--text', 'All hands: review the login module',
|
|
809
|
+
'--request-id', 'chain-bcast-1',
|
|
810
|
+
'--actor-kind', 'main',
|
|
811
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
812
|
+
assert.equal(broadcast.status, 0, `broadcast failed: ${broadcast.stderr}`);
|
|
813
|
+
assert.equal(broadcast.stdout.result.deliveredCount, 3);
|
|
814
|
+
assert.equal(broadcast.stdout.result.failedCount, 0);
|
|
815
|
+
|
|
816
|
+
// Workers reply to the broadcast.
|
|
817
|
+
for (let i = 0; i < workers.length; i += 1) {
|
|
818
|
+
const r = await runWfUi([
|
|
819
|
+
'send-agent-message',
|
|
820
|
+
...baseArgs(),
|
|
821
|
+
'--node', workers[i].nodeId,
|
|
822
|
+
'--to', mainNodeId,
|
|
823
|
+
'--text', `Review done by ${workers[i].role}`,
|
|
824
|
+
'--request-id', 'chain-bcast-1',
|
|
825
|
+
'--actor-kind', 'subagent',
|
|
826
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
827
|
+
assert.equal(r.status, 0, `broadcast reply ${i} failed: ${r.stderr}`);
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Aggregate all broadcast messages.
|
|
831
|
+
const aggregated = await runWfUi([
|
|
832
|
+
'read-agent-messages',
|
|
833
|
+
...baseArgs(),
|
|
834
|
+
'--node', mainNodeId,
|
|
835
|
+
'--peer', workers[0].nodeId,
|
|
836
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
837
|
+
assert.equal(aggregated.status, 0, `aggregate failed: ${aggregated.stderr}`);
|
|
838
|
+
const entries = aggregated.stdout.result?.entries || aggregated.stdout.entries || [];
|
|
839
|
+
assert.ok(entries.length >= 2, `must have entries for chain-bcast-1: ${entries.length}`);
|
|
840
|
+
|
|
841
|
+
// ── E8: Markdown shared context ──
|
|
842
|
+
// Create markdown node via the graph action.
|
|
843
|
+
const mdCreate = await runWfUi([
|
|
844
|
+
'workflow-node-map',
|
|
845
|
+
...baseArgs(),
|
|
846
|
+
'--action', 'createNode',
|
|
847
|
+
'--actor', mainNodeId,
|
|
848
|
+
'--type', 'markdown',
|
|
849
|
+
'--title', 'Chain Shared Plan',
|
|
850
|
+
'--payload', JSON.stringify({ markdown: '# Chain Plan\n- Step A\n- Step B' }),
|
|
851
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
852
|
+
assert.equal(mdCreate.status, 0, `md create failed: ${mdCreate.stderr}`);
|
|
853
|
+
const mdNodeId = mdCreate.stdout.result?.node?.nodeId || mdCreate.stdout.node?.nodeId;
|
|
854
|
+
assert.ok(mdNodeId, 'markdown node must have a nodeId');
|
|
855
|
+
|
|
856
|
+
// Connect Main Agent to the markdown node.
|
|
857
|
+
const mdEdge = await runWfUi([
|
|
858
|
+
'connect',
|
|
859
|
+
...baseArgs(),
|
|
860
|
+
'--actor', mainNodeId,
|
|
861
|
+
'--from', mainNodeId,
|
|
862
|
+
'--to', mdNodeId,
|
|
863
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
864
|
+
assert.equal(mdEdge.status, 0, `md edge failed: ${mdEdge.stderr}`);
|
|
865
|
+
|
|
866
|
+
// Read markdown through CLI.
|
|
867
|
+
const mdRead = await runWfUi([
|
|
868
|
+
'workflow-node-action',
|
|
869
|
+
...baseArgs(),
|
|
870
|
+
'--node', mdNodeId,
|
|
871
|
+
'--action', 'markdown.read',
|
|
872
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
873
|
+
assert.equal(mdRead.status, 0, `md read failed: ${mdRead.stderr}`);
|
|
874
|
+
assert.ok(mdRead.stdout.result.markdown.includes('Chain Plan'), `md content: ${mdRead.stdout.result.markdown}`);
|
|
875
|
+
|
|
876
|
+
// Write shared context via markdown.append.
|
|
877
|
+
const mdAppend = await runWfUi([
|
|
878
|
+
'workflow-node-action',
|
|
879
|
+
...baseArgs(),
|
|
880
|
+
'--node', mdNodeId,
|
|
881
|
+
'--action', 'markdown.append',
|
|
882
|
+
'--payload', JSON.stringify({ markdown: '\n- Step C added by Main Agent' }),
|
|
883
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
884
|
+
assert.equal(mdAppend.status, 0, `md append failed: ${mdAppend.stderr}`);
|
|
885
|
+
const mdAfter = await runWfUi([
|
|
886
|
+
'workflow-node-action',
|
|
887
|
+
...baseArgs(),
|
|
888
|
+
'--node', mdNodeId,
|
|
889
|
+
'--action', 'markdown.read',
|
|
890
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
891
|
+
assert.ok(mdAfter.stdout.result.markdown.includes('Step C'), 'markdown must reflect append');
|
|
892
|
+
|
|
893
|
+
// ── E13: Goal node ──
|
|
894
|
+
// Create a task capsule for the goal to reference.
|
|
895
|
+
const taskDir = path.join(tempRoot, 'Harness', 'tasks', 'task-chain-goal');
|
|
896
|
+
fs.mkdirSync(taskDir, { recursive: true });
|
|
897
|
+
fs.writeFileSync(path.join(taskDir, 'STATE.json'), JSON.stringify({
|
|
898
|
+
schemaVersion: 1,
|
|
899
|
+
taskId: 'task-chain-goal',
|
|
900
|
+
status: 'active',
|
|
901
|
+
title: 'Chain Goal',
|
|
902
|
+
phase: 'implement',
|
|
903
|
+
acceptance: [],
|
|
904
|
+
links: { dependsOn: [], blocks: [], related: [] },
|
|
905
|
+
}));
|
|
906
|
+
const goalNodeId = `goal-task-chain-goal`;
|
|
907
|
+
const goalNode = {
|
|
908
|
+
nodeId: goalNodeId,
|
|
909
|
+
kind: 'goal',
|
|
910
|
+
role: 'Goal',
|
|
911
|
+
label: 'Chain Goal',
|
|
912
|
+
status: 'idle',
|
|
913
|
+
};
|
|
914
|
+
const currentGraph = loadWorkflowGraphMap(tempRoot);
|
|
915
|
+
writeWorkflowGraphMap(tempRoot, {
|
|
916
|
+
...currentGraph,
|
|
917
|
+
version: currentGraph.version + 1,
|
|
918
|
+
nodes: [...(currentGraph.nodes || []), goalNode],
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
// Connect Main Agent to goal.
|
|
922
|
+
const goalEdge = await runWfUi([
|
|
923
|
+
'connect',
|
|
924
|
+
...baseArgs(),
|
|
925
|
+
'--actor', mainNodeId,
|
|
926
|
+
'--from', mainNodeId,
|
|
927
|
+
'--to', goalNodeId,
|
|
928
|
+
'--relation', 'goal',
|
|
929
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
930
|
+
assert.equal(goalEdge.status, 0, `goal edge failed: ${goalEdge.stderr}`);
|
|
931
|
+
|
|
932
|
+
// Add and check goal items.
|
|
933
|
+
const goalAdd = await runWfUi([
|
|
934
|
+
'workflow-node-action',
|
|
935
|
+
...baseArgs(),
|
|
936
|
+
'--node', goalNodeId,
|
|
937
|
+
'--action', 'goal.add',
|
|
938
|
+
'--actor', mainNodeId,
|
|
939
|
+
'--actor-kind', 'main',
|
|
940
|
+
'--payload', JSON.stringify({ planItems: [{ text: 'Complete chain test' }, { text: 'Verify evidence' }] }),
|
|
941
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
942
|
+
assert.equal(goalAdd.status, 0, `goal add failed: ${goalAdd.stderr}`);
|
|
943
|
+
|
|
944
|
+
const goalCheck = await runWfUi([
|
|
945
|
+
'workflow-node-action',
|
|
946
|
+
...baseArgs(),
|
|
947
|
+
'--node', goalNodeId,
|
|
948
|
+
'--action', 'goal.check',
|
|
949
|
+
'--actor', mainNodeId,
|
|
950
|
+
'--actor-kind', 'main',
|
|
951
|
+
'--payload', JSON.stringify({ planItemIds: ['P-001', 'P-002'] }),
|
|
952
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
953
|
+
assert.equal(goalCheck.status, 0, `goal check failed: ${goalCheck.stderr}`);
|
|
954
|
+
|
|
955
|
+
// Complete the goal.
|
|
956
|
+
const goalDone = await runWfUi([
|
|
957
|
+
'workflow-node-action',
|
|
958
|
+
...baseArgs(),
|
|
959
|
+
'--node', goalNodeId,
|
|
960
|
+
'--action', 'goal.complete',
|
|
961
|
+
'--actor', mainNodeId,
|
|
962
|
+
'--actor-kind', 'main',
|
|
963
|
+
'--payload', JSON.stringify({ note: 'Chain test complete', evidenceRefs: ['chain-cli-smoke'] }),
|
|
964
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
965
|
+
assert.equal(goalDone.status, 0, `goal complete failed: ${goalDone.stderr}`);
|
|
966
|
+
|
|
967
|
+
// ── E18: Final evidence aggregation ──
|
|
968
|
+
// Main Agent reads all sources: mailbox, markdown, goal.
|
|
969
|
+
const finalMailbox = await runWfUi([
|
|
970
|
+
'read-agent-messages',
|
|
971
|
+
...baseArgs(),
|
|
972
|
+
'--node', mainNodeId,
|
|
973
|
+
'--peer', workers[0].nodeId,
|
|
974
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
975
|
+
assert.equal(finalMailbox.status, 0);
|
|
976
|
+
|
|
977
|
+
const finalGoal = await runWfUi([
|
|
978
|
+
'workflow-node-action',
|
|
979
|
+
...baseArgs(),
|
|
980
|
+
'--node', goalNodeId,
|
|
981
|
+
'--action', 'goal.read',
|
|
982
|
+
'--actor', mainNodeId,
|
|
983
|
+
'--actor-kind', 'main',
|
|
984
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
985
|
+
assert.equal(finalGoal.status, 0, `final goal read failed: ${finalGoal.stderr}`);
|
|
986
|
+
assert.equal(finalGoal.stdout.result.status, 'proposed-complete');
|
|
987
|
+
|
|
988
|
+
const finalMd = await runWfUi([
|
|
989
|
+
'workflow-node-action',
|
|
990
|
+
...baseArgs(),
|
|
991
|
+
'--node', mdNodeId,
|
|
992
|
+
'--action', 'markdown.read',
|
|
993
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
994
|
+
assert.equal(finalMd.status, 0);
|
|
995
|
+
assert.ok(finalMd.stdout.result.markdown.includes('Chain Plan'));
|
|
996
|
+
|
|
997
|
+
const finalCtx = await runWfUi([
|
|
998
|
+
'workflow-context',
|
|
999
|
+
...baseArgs(),
|
|
1000
|
+
'--node', mainNodeId,
|
|
1001
|
+
], { cwd: tempRoot, env: AGENT_ENV });
|
|
1002
|
+
assert.equal(finalCtx.status, 0);
|
|
1003
|
+
// Verify manuals are injected (E16).
|
|
1004
|
+
assert.ok(ctx.stdout.context.connectedNodeManuals.length >= 1, 'must have manuals');
|
|
1005
|
+
|
|
1006
|
+
// ── Assertions that prove the full chain completed ──
|
|
1007
|
+
const finalGraph = loadWorkflowGraphMap(tempRoot);
|
|
1008
|
+
const finalNodeIds = (finalGraph.nodes || []).map(n => n.nodeId || n.id);
|
|
1009
|
+
assert.ok(finalNodeIds.includes(mainNodeId), 'main must survive to end');
|
|
1010
|
+
assert.ok(finalNodeIds.includes(mdNodeId), 'markdown must survive to end');
|
|
1011
|
+
assert.ok(finalNodeIds.includes(goalNodeId), 'goal must survive to end');
|
|
1012
|
+
assert.ok(finalGraph.edges.length >= 5, `edges: ${finalGraph.edges.length}`);
|
|
1013
|
+
});
|
|
1014
|
+
});
|