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,1731 @@
|
|
|
1
|
+
// workflow-agent-context.mjs — SINGLE source of truth for the Agent-readable
|
|
2
|
+
// workflow context shape (architecture.md §3.9 / W39 audit).
|
|
3
|
+
//
|
|
4
|
+
// Both context-producing paths delegate here:
|
|
5
|
+
// - API path: workflow-node-runtime.mjs getNodeContext() -> buildAgentContext()
|
|
6
|
+
// - Snapshot: a2a-store.mjs buildSessionGraph() per-node context -> buildAgentContext()
|
|
7
|
+
//
|
|
8
|
+
// Every builder in this module is a pure function of
|
|
9
|
+
// (projectRoot, graphNode, graph, options?). `graphNode` is the Agent graph
|
|
10
|
+
// node (either a persisted graph-map node or a live session node); `graph` is
|
|
11
|
+
// the normalized workflow graph map (loadWorkflowGraphMap). Nothing here reads
|
|
12
|
+
// runtime-private state — the module owns the full context shape: identity,
|
|
13
|
+
// workspace, connected refs, effective skill groups, ontology, affordances,
|
|
14
|
+
// output routing, available actions, and magnetic topology.
|
|
15
|
+
import fs from 'node:fs'
|
|
16
|
+
import path from 'node:path'
|
|
17
|
+
import { fileURLToPath } from 'node:url'
|
|
18
|
+
import { loadActionRegistry, actionsForNodeType } from './action-registry.mjs'
|
|
19
|
+
import { componentStateRefs, listLiveComponentNodes } from './component-node-store.mjs'
|
|
20
|
+
import { eventStateRefs } from './workflow-event-node-store.mjs'
|
|
21
|
+
import { capabilityStateRefs } from './workflow-capability-node-store.mjs'
|
|
22
|
+
import { goalStateRefs } from './workflow-goal-node-store.mjs'
|
|
23
|
+
import { getWorkspaceMeta, workspaceMimeForPath } from './workspace-store.mjs'
|
|
24
|
+
import { normalizeNodeConfig } from './node-config-store.mjs'
|
|
25
|
+
import { readNodeSettings } from './workflow-node-settings-store.mjs'
|
|
26
|
+
import { listTerminalSessions } from './terminal-store.mjs'
|
|
27
|
+
import { buildAgentOntologyContext } from './workflow-ontology.mjs'
|
|
28
|
+
import { readRoleProfile } from './workflow-node-types/role-profile-store.mjs'
|
|
29
|
+
// NOTE: computeMagneticTopology comes from a2a-store.mjs, which imports this
|
|
30
|
+
// module. ESM circular imports are safe here because the binding is only used
|
|
31
|
+
// at call time, never at module-evaluation time.
|
|
32
|
+
import { computeMagneticTopology } from './a2a-store.mjs'
|
|
33
|
+
import { timerControlActionsForState } from './workflow-node-types/timer-node.mjs'
|
|
34
|
+
|
|
35
|
+
export const DEFAULT_AGENT_WORKFLOW_SKILLS = [
|
|
36
|
+
'workflow-ontology',
|
|
37
|
+
'workflow-node-map',
|
|
38
|
+
'workflow-context',
|
|
39
|
+
'workflow-node-actions',
|
|
40
|
+
'terminal-control',
|
|
41
|
+
]
|
|
42
|
+
export const ON_DEMAND_AGENT_NODE_SKILLS = [
|
|
43
|
+
'workflow-timer-node',
|
|
44
|
+
'workflow-goal-node',
|
|
45
|
+
'workflow-agent-node',
|
|
46
|
+
'workflow-resource-node',
|
|
47
|
+
'workflow-markdown-node',
|
|
48
|
+
'workflow-diagram-node',
|
|
49
|
+
'workflow-file-node',
|
|
50
|
+
'workflow-skill-group-node',
|
|
51
|
+
'workflow-mcp-connector-node',
|
|
52
|
+
]
|
|
53
|
+
const DEFAULT_AGENT_WORKFLOW_SKILL_TRIGGERS = {
|
|
54
|
+
'workflow-ontology': [
|
|
55
|
+
'workflow semantics',
|
|
56
|
+
'node ontology',
|
|
57
|
+
'node classes',
|
|
58
|
+
'relation semantics',
|
|
59
|
+
'action affordances',
|
|
60
|
+
'choose capability',
|
|
61
|
+
'choose connected node',
|
|
62
|
+
'before acting',
|
|
63
|
+
'semantics',
|
|
64
|
+
],
|
|
65
|
+
'workflow-context': [
|
|
66
|
+
'read context',
|
|
67
|
+
'connected nodes',
|
|
68
|
+
'connected resources',
|
|
69
|
+
'connected agents',
|
|
70
|
+
'goal refs',
|
|
71
|
+
'timer refs',
|
|
72
|
+
'effective skills',
|
|
73
|
+
'observe before acting',
|
|
74
|
+
],
|
|
75
|
+
'workflow-node-actions': [
|
|
76
|
+
'node action',
|
|
77
|
+
'write markdown',
|
|
78
|
+
'append markdown',
|
|
79
|
+
'draw diagram',
|
|
80
|
+
'draw flowchart',
|
|
81
|
+
'update excalidraw',
|
|
82
|
+
'read timer',
|
|
83
|
+
'configure timer',
|
|
84
|
+
'timer interval',
|
|
85
|
+
'start timer',
|
|
86
|
+
'stop timer',
|
|
87
|
+
'update goal',
|
|
88
|
+
'send agent message',
|
|
89
|
+
'broadcast agent message',
|
|
90
|
+
'reply agent',
|
|
91
|
+
'read bridge messages',
|
|
92
|
+
'shared markdown context',
|
|
93
|
+
'agent direct message',
|
|
94
|
+
'agent group message',
|
|
95
|
+
'发送消息',
|
|
96
|
+
'群发消息',
|
|
97
|
+
'回复 Agent',
|
|
98
|
+
'共享上下文',
|
|
99
|
+
'共享 Markdown',
|
|
100
|
+
'delegate agent',
|
|
101
|
+
'read agent output',
|
|
102
|
+
],
|
|
103
|
+
'workflow-timer-node': [
|
|
104
|
+
'timer node',
|
|
105
|
+
'timer control',
|
|
106
|
+
'timer interval',
|
|
107
|
+
'configure timer',
|
|
108
|
+
'start timer',
|
|
109
|
+
'stop timer',
|
|
110
|
+
'timer countdown',
|
|
111
|
+
'health check timer',
|
|
112
|
+
],
|
|
113
|
+
'workflow-goal-node': [
|
|
114
|
+
'goal node',
|
|
115
|
+
'goal update',
|
|
116
|
+
'acceptance',
|
|
117
|
+
'request completion',
|
|
118
|
+
'return to work',
|
|
119
|
+
'goal watchdog',
|
|
120
|
+
],
|
|
121
|
+
'workflow-agent-node': [
|
|
122
|
+
'agent node',
|
|
123
|
+
'delegate agent',
|
|
124
|
+
'send agent message',
|
|
125
|
+
'broadcast agent message',
|
|
126
|
+
'reply agent',
|
|
127
|
+
'read bridge messages',
|
|
128
|
+
'shared context',
|
|
129
|
+
'agent direct message',
|
|
130
|
+
'agent group message',
|
|
131
|
+
'发送消息',
|
|
132
|
+
'群发消息',
|
|
133
|
+
'回复 Agent',
|
|
134
|
+
'共享上下文',
|
|
135
|
+
'共享 Markdown',
|
|
136
|
+
'read agent output',
|
|
137
|
+
'read transcript',
|
|
138
|
+
'start agent',
|
|
139
|
+
'stop agent',
|
|
140
|
+
],
|
|
141
|
+
'workflow-resource-node': [
|
|
142
|
+
'resource node',
|
|
143
|
+
'markdown node',
|
|
144
|
+
'file node',
|
|
145
|
+
'diagram node',
|
|
146
|
+
'excalidraw node',
|
|
147
|
+
'connected output',
|
|
148
|
+
],
|
|
149
|
+
'workflow-markdown-node': [
|
|
150
|
+
'markdown node',
|
|
151
|
+
'write notes',
|
|
152
|
+
'append markdown',
|
|
153
|
+
'replace markdown',
|
|
154
|
+
'connected markdown',
|
|
155
|
+
'durable text output',
|
|
156
|
+
],
|
|
157
|
+
'workflow-diagram-node': [
|
|
158
|
+
'diagram node',
|
|
159
|
+
'excalidraw node',
|
|
160
|
+
'draw diagram',
|
|
161
|
+
'draw flowchart',
|
|
162
|
+
'save scene',
|
|
163
|
+
'connected diagram',
|
|
164
|
+
'visual output',
|
|
165
|
+
],
|
|
166
|
+
'workflow-file-node': [
|
|
167
|
+
'file node',
|
|
168
|
+
'read file',
|
|
169
|
+
'file metadata',
|
|
170
|
+
'workspace file',
|
|
171
|
+
'uploaded file',
|
|
172
|
+
'connected file',
|
|
173
|
+
],
|
|
174
|
+
'workflow-skill-group-node': [
|
|
175
|
+
'skill group node',
|
|
176
|
+
'skill bundle',
|
|
177
|
+
'capability pack',
|
|
178
|
+
'connected skill group',
|
|
179
|
+
'attached skills',
|
|
180
|
+
],
|
|
181
|
+
'workflow-mcp-connector-node': [
|
|
182
|
+
'mcp connector node',
|
|
183
|
+
'mcp node',
|
|
184
|
+
'mcp tools',
|
|
185
|
+
'mcp resources',
|
|
186
|
+
'connected mcp',
|
|
187
|
+
'capability provider',
|
|
188
|
+
],
|
|
189
|
+
'workflow-node-map': [
|
|
190
|
+
'read graph',
|
|
191
|
+
'create node',
|
|
192
|
+
'connect nodes',
|
|
193
|
+
'disconnect nodes',
|
|
194
|
+
'move node',
|
|
195
|
+
'delete node',
|
|
196
|
+
'spawn agent node',
|
|
197
|
+
'delegate worker',
|
|
198
|
+
'control canvas',
|
|
199
|
+
'arrange workflow',
|
|
200
|
+
],
|
|
201
|
+
'terminal-control': [
|
|
202
|
+
'terminal input',
|
|
203
|
+
'read terminal',
|
|
204
|
+
'send input',
|
|
205
|
+
'transcript',
|
|
206
|
+
],
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// ── action-registry-derived affordance lists ───────────────────────────────
|
|
210
|
+
//
|
|
211
|
+
// The Agent-readable action lists (availableActions, agent snapshot
|
|
212
|
+
// capabilities, and per-node-type capabilities) are derived from
|
|
213
|
+
// Harness/a2a/action-registry.json instead of hand-maintained literals
|
|
214
|
+
// (W39 audit). Two adapters keep the derived output identical to the legacy
|
|
215
|
+
// output for every pre-existing case:
|
|
216
|
+
// - REGISTRY_ID_TO_CAPABILITY_KEY maps registry ids to the legacy capability
|
|
217
|
+
// key names (e.g. 'file.readMeta' -> 'meta:read'); registry ids without an
|
|
218
|
+
// entry are not exposed.
|
|
219
|
+
// - SYNTHETIC_CAPABILITY_KEYS re-emits legacy keys that have no registry id
|
|
220
|
+
// ('state:read', 'state:update', 'event:emit', 'capability:read',
|
|
221
|
+
// 'mcp:metadata:read').
|
|
222
|
+
// The visible changes are exactly the W39 audit fixes: availableActions gains
|
|
223
|
+
// agent.setModel / node.delete / node.restore, file capabilities gain
|
|
224
|
+
// file.writeText / file.preview, and skill-group capabilities gain
|
|
225
|
+
// skill-group.setSkillEnabled.
|
|
226
|
+
//
|
|
227
|
+
// The registry is resolved per call with a warning-free fallback chain:
|
|
228
|
+
// explicit projectRoot, then process.cwd(), then the repo root of this module
|
|
229
|
+
// (two levels above src/wf-ui-server/). The legacy literal lists below are
|
|
230
|
+
// used ONLY when no candidate root has a registry (non-scaffold projects).
|
|
231
|
+
|
|
232
|
+
// Repo root of this module: <root>/src/wf-ui-server/workflow-agent-context.mjs
|
|
233
|
+
const MODULE_PROJECT_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..')
|
|
234
|
+
let fallbackRegistry = null
|
|
235
|
+
|
|
236
|
+
function resolveActionRegistry(projectRoot = '') {
|
|
237
|
+
if (projectRoot) {
|
|
238
|
+
try {
|
|
239
|
+
return loadActionRegistry(projectRoot)
|
|
240
|
+
} catch {
|
|
241
|
+
// no registry at the explicit project root — try the default roots
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (fallbackRegistry) return fallbackRegistry
|
|
245
|
+
for (const root of [process.cwd(), MODULE_PROJECT_ROOT]) {
|
|
246
|
+
try {
|
|
247
|
+
fallbackRegistry = loadActionRegistry(root)
|
|
248
|
+
if (fallbackRegistry) return fallbackRegistry
|
|
249
|
+
} catch {
|
|
250
|
+
// missing/invalid registry at this root — try the next
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return null
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const REGISTRY_ID_TO_CAPABILITY_KEY = {
|
|
257
|
+
// markdown
|
|
258
|
+
'markdown.read': 'state:read',
|
|
259
|
+
'markdown.patch': 'state:update',
|
|
260
|
+
'markdown.append': 'markdown:append',
|
|
261
|
+
'markdown.replace': 'markdown:replace',
|
|
262
|
+
'markdown.find': 'markdown.find',
|
|
263
|
+
'markdown.acquireLock': 'markdown.acquireLock',
|
|
264
|
+
'markdown.releaseLock': 'markdown.releaseLock',
|
|
265
|
+
// excalidraw
|
|
266
|
+
'excalidraw.readScene': 'excalidraw:read',
|
|
267
|
+
'excalidraw.patchScene': 'excalidraw:update',
|
|
268
|
+
// file
|
|
269
|
+
'file.readMeta': 'meta:read',
|
|
270
|
+
'file.readBytes': 'bytes:read',
|
|
271
|
+
'file.readText': 'text:read',
|
|
272
|
+
'file.writeText': 'file.writeText',
|
|
273
|
+
'file.preview': 'file.preview',
|
|
274
|
+
// timer (timer.dispatchWakeup stays unexposed: scheduler-internal)
|
|
275
|
+
'timer.read': 'timer.read',
|
|
276
|
+
'timer.configure': 'timer.configure',
|
|
277
|
+
'timer.fire': 'timer.fire',
|
|
278
|
+
'timer.enable': 'timer.enable',
|
|
279
|
+
'timer.disable': 'timer.disable',
|
|
280
|
+
'timer.setInterval': 'timer.setInterval',
|
|
281
|
+
'timer.setMode': 'timer.setMode',
|
|
282
|
+
'timer.ackWatchdog': 'timer.ackWatchdog',
|
|
283
|
+
'timer.resetWatchdog': 'timer.resetWatchdog',
|
|
284
|
+
'timer.tick': 'timer.tick',
|
|
285
|
+
// github-trigger
|
|
286
|
+
'github-trigger.read': 'github-trigger.read',
|
|
287
|
+
'github-trigger.receive': 'github-trigger.receive',
|
|
288
|
+
'github-trigger.configure': 'github-trigger.configure',
|
|
289
|
+
// skill-group
|
|
290
|
+
'skill-group.read': 'skill-group.read',
|
|
291
|
+
'skill-group.configure': 'skill-group.configure',
|
|
292
|
+
'skill-group.setSkillEnabled': 'skill-group.setSkillEnabled',
|
|
293
|
+
// mcp-connector
|
|
294
|
+
'mcp-connector.read': 'mcp-connector.read',
|
|
295
|
+
'mcp-connector.configure': 'mcp-connector.configure',
|
|
296
|
+
// goal
|
|
297
|
+
'goal.read': 'goal.read',
|
|
298
|
+
'goal.update': 'goal.update',
|
|
299
|
+
'goal.requestCompletion': 'goal.requestCompletion',
|
|
300
|
+
'goal.returnToWork': 'goal.returnToWork',
|
|
301
|
+
'goal.add': 'goal.add',
|
|
302
|
+
'goal.delete': 'goal.delete',
|
|
303
|
+
'goal.replace': 'goal.replace',
|
|
304
|
+
'goal.check': 'goal.check',
|
|
305
|
+
'goal.uncheck': 'goal.uncheck',
|
|
306
|
+
'goal.complete': 'goal.complete',
|
|
307
|
+
'goal.reopen': 'goal.reopen',
|
|
308
|
+
'goal.ackWatchdog': 'goal.ackWatchdog',
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const SYNTHETIC_CAPABILITY_KEYS = {
|
|
312
|
+
excalidraw: ['state:read', 'state:update'],
|
|
313
|
+
timer: ['state:read', 'state:update', 'event:emit'],
|
|
314
|
+
'github-trigger': ['state:read', 'state:update', 'event:emit'],
|
|
315
|
+
'skill-group': ['state:read', 'state:update', 'capability:read'],
|
|
316
|
+
'mcp-connector': ['state:read', 'state:update', 'capability:read', 'mcp:metadata:read'],
|
|
317
|
+
goal: ['state:read'],
|
|
318
|
+
}
|
|
319
|
+
const DEFAULT_CAPABILITY_KEYS = ['state:read']
|
|
320
|
+
|
|
321
|
+
// Pre-registry literal lists, used only when the action registry cannot be
|
|
322
|
+
// resolved from any candidate root (normally unreachable: every generated
|
|
323
|
+
// Harness project ships Harness/a2a/action-registry.json).
|
|
324
|
+
const LEGACY_CAPABILITY_LISTS = {
|
|
325
|
+
file: ['meta:read', 'bytes:read', 'text:read'],
|
|
326
|
+
markdown: ['state:read', 'state:update', 'markdown:append', 'markdown:replace', 'markdown.find', 'markdown.acquireLock', 'markdown.releaseLock'],
|
|
327
|
+
excalidraw: ['state:read', 'state:update', 'excalidraw:read', 'excalidraw:update'],
|
|
328
|
+
timer: ['state:read', 'state:update', 'timer.read', 'timer.configure', 'timer.fire', 'timer.enable', 'timer.disable', 'timer.setInterval', 'timer.setMode', 'timer.ackWatchdog', 'timer.resetWatchdog', 'timer.tick', 'event:emit'],
|
|
329
|
+
'github-trigger': ['state:read', 'state:update', 'github-trigger.read', 'github-trigger.configure', 'github-trigger.receive', 'event:emit'],
|
|
330
|
+
'skill-group': ['state:read', 'state:update', 'skill-group.read', 'skill-group.configure', 'capability:read'],
|
|
331
|
+
'mcp-connector': ['state:read', 'state:update', 'mcp-connector.read', 'mcp-connector.configure', 'capability:read', 'mcp:metadata:read'],
|
|
332
|
+
goal: ['state:read', 'goal.read', 'goal.update', 'goal.requestCompletion', 'goal.returnToWork', 'goal.ackWatchdog', 'goal.add', 'goal.delete', 'goal.replace', 'goal.check', 'goal.uncheck', 'goal.complete', 'goal.reopen'],
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function capabilityKeysForType(registry, type) {
|
|
336
|
+
if (!registry) {
|
|
337
|
+
const legacy = LEGACY_CAPABILITY_LISTS[type]
|
|
338
|
+
return legacy ? [...legacy] : [...DEFAULT_CAPABILITY_KEYS]
|
|
339
|
+
}
|
|
340
|
+
const synthetic = SYNTHETIC_CAPABILITY_KEYS[type] || []
|
|
341
|
+
const derived = actionsForNodeType(registry, type)
|
|
342
|
+
.map(action => REGISTRY_ID_TO_CAPABILITY_KEY[action.id])
|
|
343
|
+
.filter(key => key !== undefined)
|
|
344
|
+
if (synthetic.length > 0 || derived.length > 0) return [...synthetic, ...derived]
|
|
345
|
+
return [...DEFAULT_CAPABILITY_KEYS]
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Agent-facing action ids exposed in availableActions and the agent snapshot
|
|
349
|
+
// capabilities. Kept as an explicit set so registry actions that are not
|
|
350
|
+
// agent-callable surface candidates (agent.deleteAgentNode alias, agent.layout
|
|
351
|
+
// Main-Agent-only layout, timer dispatchWakeup) stay unexposed.
|
|
352
|
+
const AGENT_EXPOSED_ACTION_IDS = new Set([
|
|
353
|
+
// agent nodeType
|
|
354
|
+
'agent.readOutput',
|
|
355
|
+
'agent.sendInput',
|
|
356
|
+
'agent.sendMessage',
|
|
357
|
+
'agent.broadcastMessage',
|
|
358
|
+
'agent.readMessages',
|
|
359
|
+
'agent.readTranscript',
|
|
360
|
+
'agent.start',
|
|
361
|
+
'agent.stop',
|
|
362
|
+
'agent.restart',
|
|
363
|
+
'agent.delete',
|
|
364
|
+
'agent.readContext',
|
|
365
|
+
'agent.setModel',
|
|
366
|
+
// graph nodeType
|
|
367
|
+
'agent.createNode',
|
|
368
|
+
'agent.connectNodes',
|
|
369
|
+
'agent.disconnectNodes',
|
|
370
|
+
'agent.moveNode',
|
|
371
|
+
'agent.deleteNode',
|
|
372
|
+
'agent.deleteNodes',
|
|
373
|
+
'agent.readGraph',
|
|
374
|
+
// node nodeType
|
|
375
|
+
'node.delete',
|
|
376
|
+
'node.restore',
|
|
377
|
+
])
|
|
378
|
+
|
|
379
|
+
const LEGACY_AGENT_ACTIONS = [
|
|
380
|
+
'agent.sendInput',
|
|
381
|
+
'agent.readOutput',
|
|
382
|
+
'agent.readTranscript',
|
|
383
|
+
'agent.start',
|
|
384
|
+
'agent.stop',
|
|
385
|
+
'agent.restart',
|
|
386
|
+
'agent.delete',
|
|
387
|
+
'agent.readContext',
|
|
388
|
+
'agent.sendMessage',
|
|
389
|
+
'agent.broadcastMessage',
|
|
390
|
+
'agent.readMessages',
|
|
391
|
+
'agent.createNode',
|
|
392
|
+
'agent.connectNodes',
|
|
393
|
+
'agent.disconnectNodes',
|
|
394
|
+
'agent.moveNode',
|
|
395
|
+
'agent.deleteNode',
|
|
396
|
+
'agent.deleteNodes',
|
|
397
|
+
'agent.readGraph',
|
|
398
|
+
]
|
|
399
|
+
|
|
400
|
+
function agentAvailableActions(projectRoot = '') {
|
|
401
|
+
const registry = resolveActionRegistry(projectRoot)
|
|
402
|
+
if (!registry) return [...LEGACY_AGENT_ACTIONS]
|
|
403
|
+
const ids = []
|
|
404
|
+
for (const nodeType of ['agent', 'graph', 'node']) {
|
|
405
|
+
for (const action of actionsForNodeType(registry, nodeType)) {
|
|
406
|
+
if (AGENT_EXPOSED_ACTION_IDS.has(action.id)) ids.push(action.id)
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return ids
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const AGENT_PEER_READ_ACTIONS = ['agent.readContext', 'agent.readOutput', 'agent.readTranscript']
|
|
413
|
+
const AGENT_PEER_MESSAGE_ACTIONS = ['agent.sendMessage', 'agent.readMessages']
|
|
414
|
+
const AGENT_PEER_CONTROL_ACTIONS = ['agent.sendInput', 'agent.start', 'agent.stop', 'agent.restart']
|
|
415
|
+
const EVENT_NODE_TYPES = new Set(['timer', 'github-trigger'])
|
|
416
|
+
|
|
417
|
+
// Connected node type -> manual skill id under Harness/a2a/skills/ (spec §9.4,
|
|
418
|
+
// AC-021 manual injection). The mapping is built DYNAMICALLY from the skills
|
|
419
|
+
// directory (F16/D13): every workflow-<type>-node.json file maps its node type
|
|
420
|
+
// to its manual id, so node manuals added later are injected without code
|
|
421
|
+
// changes. The excalidraw node type uses the diagram manual
|
|
422
|
+
// (workflow-diagram-node.json). Missing manual files degrade gracefully (no
|
|
423
|
+
// injection for that type).
|
|
424
|
+
const EXCALIDRAW_MANUAL_ALIAS = { excalidraw: 'diagram' }
|
|
425
|
+
|
|
426
|
+
function discoverNodeManualIds(projectRoot) {
|
|
427
|
+
const manualIds = new Map()
|
|
428
|
+
try {
|
|
429
|
+
const dir = path.join(projectRoot, 'Harness', 'a2a', 'skills')
|
|
430
|
+
for (const file of fs.readdirSync(dir)) {
|
|
431
|
+
const match = String(file).match(/^workflow-([a-z0-9-]+)-node\.json$/)
|
|
432
|
+
if (!match) continue
|
|
433
|
+
const manualId = `workflow-${match[1]}-node`
|
|
434
|
+
manualIds.set(match[1], manualId)
|
|
435
|
+
}
|
|
436
|
+
} catch {
|
|
437
|
+
// skills dir missing — no manual injection
|
|
438
|
+
}
|
|
439
|
+
return manualIds
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Team-organization decision flow (agent-team-cooperation-spec §2 steps 1-10,
|
|
443
|
+
// AC-024 partial). Injected into the agent context as plain-language guidance;
|
|
444
|
+
// user-facing reporting stays jargon-free (ask-user gate per §4.4 / T13).
|
|
445
|
+
export const TEAM_ORGANIZATION_GUIDANCE = `Team Organization Guidance
|
|
446
|
+
When the user asks for work that needs a team:
|
|
447
|
+
1. Understand the task from the user request.
|
|
448
|
+
2. Decide whether team collaboration is needed — simple requests are handled directly, no team.
|
|
449
|
+
3. Choose the required roles from the canonical role vocabulary: ceo, manager, implementer, reviewer, verifier, planner, terminal-controller (or a free-form role such as architect).
|
|
450
|
+
4. Find existing agents by role, runtime, provider, capability, or name.
|
|
451
|
+
5. Exactly one unambiguous match -> connect that agent.
|
|
452
|
+
6. No match and the task target is clear -> create an agent with a role profile (displayName, roleTitle, responsibility, capabilities).
|
|
453
|
+
7. Multiple matches or an unclear target -> ask the user; never blindly create or connect an agent.
|
|
454
|
+
8. Share durable context through Markdown nodes, referenced by nodeId only.
|
|
455
|
+
9. Send structured requests, wait for replies, and aggregate them.
|
|
456
|
+
10. Tick Goal items and complete the Goal when all items are checked; report to the user in plain language.`
|
|
457
|
+
|
|
458
|
+
function readNodeManualText(projectRoot, manualId) {
|
|
459
|
+
const filePath = path.join(projectRoot, 'Harness', 'a2a', 'skills', `${manualId}.json`)
|
|
460
|
+
let manual
|
|
461
|
+
try {
|
|
462
|
+
manual = JSON.parse(fs.readFileSync(filePath, 'utf8'))
|
|
463
|
+
} catch {
|
|
464
|
+
return null
|
|
465
|
+
}
|
|
466
|
+
const parts = []
|
|
467
|
+
const summary = String(manual.summary || manual.description || '').trim()
|
|
468
|
+
if (summary) parts.push(summary)
|
|
469
|
+
const denied = Array.isArray(manual.policy?.denied) ? manual.policy.denied : []
|
|
470
|
+
if (Array.isArray(manual.prohibitions)) denied.push(...manual.prohibitions)
|
|
471
|
+
for (const item of denied) {
|
|
472
|
+
const text = String(item || '').trim()
|
|
473
|
+
if (text) parts.push(text)
|
|
474
|
+
}
|
|
475
|
+
// Command tables are registry-generated (W39 audit): after the manual's
|
|
476
|
+
// prose sections, append the invocable action surface for the manual's own
|
|
477
|
+
// nodeType from Harness/a2a/action-registry.json. The diagram manual
|
|
478
|
+
// declares nodeType 'excalidraw', which is the registry nodeType for its
|
|
479
|
+
// actions (the EXCALIDRAW_MANUAL_ALIAS above only maps the peer type to the
|
|
480
|
+
// diagram manual id). When no registry is reachable (temp roots,
|
|
481
|
+
// non-scaffold projects), fall back to the manual's own legacy commands key
|
|
482
|
+
// when one is still present — a prose-only manual with no registry yields
|
|
483
|
+
// no commands section, never a throw.
|
|
484
|
+
const registry = resolveActionRegistry(projectRoot)
|
|
485
|
+
if (registry) {
|
|
486
|
+
const manualNodeType = String(manual.nodeType || '').trim()
|
|
487
|
+
const actions = manualNodeType ? actionsForNodeType(registry, manualNodeType) : []
|
|
488
|
+
if (actions.length > 0) {
|
|
489
|
+
const lines = ['Commands:']
|
|
490
|
+
for (const action of actions) {
|
|
491
|
+
const id = String(action.id || '').trim()
|
|
492
|
+
const actionSummary = String(action.summary || '').trim()
|
|
493
|
+
if (!id && !actionSummary) continue
|
|
494
|
+
lines.push(`- ${id} — ${actionSummary}`)
|
|
495
|
+
const example = String(action.example || '').trim()
|
|
496
|
+
if (example) lines.push(` Example: ${example}`)
|
|
497
|
+
}
|
|
498
|
+
parts.push(lines.join('\n'))
|
|
499
|
+
}
|
|
500
|
+
} else if (Array.isArray(manual.commands)) {
|
|
501
|
+
for (const command of manual.commands) {
|
|
502
|
+
const name = String(command?.name || '').trim()
|
|
503
|
+
const cmd = String(command?.command || '').trim()
|
|
504
|
+
const desc = String(command?.description || '').trim()
|
|
505
|
+
if (!name && !cmd) continue
|
|
506
|
+
parts.push(`${name ? `${name}: ` : ''}${cmd}${desc ? ` — ${desc}` : ''}`)
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return parts.filter(Boolean).join('\n')
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
export function buildConnectedNodeManuals(projectRoot, connectedPeers = []) {
|
|
513
|
+
const manualIds = discoverNodeManualIds(projectRoot)
|
|
514
|
+
const seen = new Set()
|
|
515
|
+
const manuals = []
|
|
516
|
+
for (const peer of connectedPeers) {
|
|
517
|
+
const nodeType = String(peer?.type || '').trim()
|
|
518
|
+
const typeKey = EXCALIDRAW_MANUAL_ALIAS[nodeType] || nodeType
|
|
519
|
+
const manualId = manualIds.get(typeKey)
|
|
520
|
+
if (!manualId || seen.has(manualId)) continue
|
|
521
|
+
seen.add(manualId)
|
|
522
|
+
const text = readNodeManualText(projectRoot, manualId)
|
|
523
|
+
if (!text) continue
|
|
524
|
+
manuals.push({
|
|
525
|
+
nodeType,
|
|
526
|
+
nodeId: peer.nodeId,
|
|
527
|
+
skillId: manualId,
|
|
528
|
+
text,
|
|
529
|
+
})
|
|
530
|
+
}
|
|
531
|
+
return manuals
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// ── identity / workspace helpers ──
|
|
535
|
+
|
|
536
|
+
export function agentNodeId(graphNode) {
|
|
537
|
+
return graphNode.nodeId || graphNode.id || `session-${graphNode.sessionId}`
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export function uniqueStrings(values) {
|
|
541
|
+
const seen = new Set()
|
|
542
|
+
const result = []
|
|
543
|
+
for (const value of Array.isArray(values) ? values : []) {
|
|
544
|
+
const text = String(value || '').trim()
|
|
545
|
+
if (!text || seen.has(text)) continue
|
|
546
|
+
seen.add(text)
|
|
547
|
+
result.push(text)
|
|
548
|
+
}
|
|
549
|
+
return result
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export function shortNodeId(nodeId) {
|
|
553
|
+
const id = String(nodeId || '').trim()
|
|
554
|
+
if (!id) return ''
|
|
555
|
+
const sessionMatch = id.match(/^session-([0-9a-f]{8})/i)
|
|
556
|
+
if (sessionMatch) return sessionMatch[1]
|
|
557
|
+
const parts = id.split('-').filter(Boolean)
|
|
558
|
+
if (parts.length >= 2) return parts[parts.length - 1]
|
|
559
|
+
return id.slice(-8)
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
export function nodeDisplayName(title, nodeId, storedDisplayName = '') {
|
|
563
|
+
const label = String(storedDisplayName || title || '').trim()
|
|
564
|
+
return label ? `${label} [${shortNodeId(nodeId)}]` : shortNodeId(nodeId)
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export function isMainAgentGraphNode(graphNode) {
|
|
568
|
+
if (!graphNode) return false
|
|
569
|
+
if (graphNode.agentKind === 'main') return true
|
|
570
|
+
if (String(graphNode.role || '').toLowerCase() === 'main') return true
|
|
571
|
+
if (String(graphNode.role || '').toLowerCase().includes('ceo')) return true
|
|
572
|
+
return false
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
export function isLiveAgentGraphNode(node) {
|
|
576
|
+
if (!node?.sessionId) return false
|
|
577
|
+
const status = String(node.status || '').toLowerCase()
|
|
578
|
+
return status === 'running' || status === 'starting'
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function persistedAgentSession(projectRoot, graphNode) {
|
|
582
|
+
const sessionId = graphNode?.sessionId
|
|
583
|
+
if (!sessionId) return null
|
|
584
|
+
return listTerminalSessions(projectRoot).find(session => session.sessionId === sessionId) || null
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function agentConfig(projectRoot, graphNode) {
|
|
588
|
+
const session = {
|
|
589
|
+
...(graphNode || {}),
|
|
590
|
+
...(persistedAgentSession(projectRoot, graphNode) || {}),
|
|
591
|
+
sessionId: graphNode?.sessionId,
|
|
592
|
+
graphNodeId: agentNodeId(graphNode),
|
|
593
|
+
}
|
|
594
|
+
return normalizeNodeConfig(session.nodeConfig || session.config || graphNode?.config || {}, session)
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
function agentSettings(projectRoot, graphNode) {
|
|
598
|
+
const nodeId = agentNodeId(graphNode)
|
|
599
|
+
const stored = readNodeSettings(projectRoot, nodeId, 'agent')
|
|
600
|
+
const config = agentConfig(projectRoot, graphNode)
|
|
601
|
+
const graphRouting = config.outputRouting || graphNode.config?.outputRouting || graphNode.nodeConfig?.outputRouting || graphNode.outputRouting
|
|
602
|
+
if (stored.revision === 0 && graphRouting && typeof graphRouting === 'object' && !Array.isArray(graphRouting)) {
|
|
603
|
+
return {
|
|
604
|
+
...stored,
|
|
605
|
+
values: {
|
|
606
|
+
...stored.values,
|
|
607
|
+
outputRouting: {
|
|
608
|
+
...stored.values.outputRouting,
|
|
609
|
+
...graphRouting,
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return stored
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
function agentNodeHomeRel(ref = {}) {
|
|
618
|
+
if (ref.nodeHomeRel) return ref.nodeHomeRel
|
|
619
|
+
if (ref.sessionId) return `Harness/a2a/nodes/${ref.sessionId}`
|
|
620
|
+
return ''
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function agentNodeInitRel(ref = {}) {
|
|
624
|
+
if (ref.nodeInitRel) return ref.nodeInitRel
|
|
625
|
+
const homeRel = agentNodeHomeRel(ref)
|
|
626
|
+
return homeRel ? `${homeRel}/init.md` : ''
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
export function buildAgentIdentityContext(graphNode, nodeId, sessionId) {
|
|
630
|
+
return {
|
|
631
|
+
nodeId,
|
|
632
|
+
sessionId,
|
|
633
|
+
agentKind: graphNode.agentKind || '',
|
|
634
|
+
role: graphNode.role || '',
|
|
635
|
+
runtime: graphNode.runtime || '',
|
|
636
|
+
status: graphNode.status || 'unknown',
|
|
637
|
+
lifecycle: isLiveAgentGraphNode(graphNode) ? 'live' : 'stopped',
|
|
638
|
+
isMainAgent: isMainAgentGraphNode(graphNode),
|
|
639
|
+
taskId: graphNode.taskId || null,
|
|
640
|
+
parentAgentId: graphNode.parentAgentId || null,
|
|
641
|
+
parentNodeId: graphNode.parentNodeId || null,
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
export function buildAgentWorkspaceContext(projectRoot, graphNode, nodeId, sessionId) {
|
|
646
|
+
const homeRel = agentNodeHomeRel(graphNode)
|
|
647
|
+
const initRel = agentNodeInitRel(graphNode)
|
|
648
|
+
return {
|
|
649
|
+
kind: 'agent-node-home',
|
|
650
|
+
projectRoot,
|
|
651
|
+
cwd: graphNode.cwd || projectRoot,
|
|
652
|
+
nodeHomeRel: homeRel,
|
|
653
|
+
nodeInitRel: initRel,
|
|
654
|
+
graphContextPath: graphNode.graphContextPath || 'Harness/a2a/workflow-map.json',
|
|
655
|
+
stateRef: {
|
|
656
|
+
path: homeRel,
|
|
657
|
+
initPath: initRel,
|
|
658
|
+
sessionId,
|
|
659
|
+
nodeId,
|
|
660
|
+
},
|
|
661
|
+
boundaries: {
|
|
662
|
+
nodeHome: 'agent-local-run-state',
|
|
663
|
+
workspace: 'project-bounded-workspace-api',
|
|
664
|
+
workflowMap: 'backend-owned-read-only-diagnostic',
|
|
665
|
+
nodeState: 'backend-owned-node-action-only',
|
|
666
|
+
},
|
|
667
|
+
api: {
|
|
668
|
+
selfContext: `/api/workflow/context/${encodeURIComponent(nodeId)}`,
|
|
669
|
+
nodeAction: `/api/workflow/nodes/${encodeURIComponent(nodeId)}/actions/:action`,
|
|
670
|
+
graphAction: `/api/workflow/nodes/${encodeURIComponent(nodeId)}/actions/agent.*`,
|
|
671
|
+
},
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// ── connections ──
|
|
676
|
+
|
|
677
|
+
function normalizeEdgeDirection(value) {
|
|
678
|
+
return String(value || '').trim() === 'source-to-target' ? 'source-to-target' : 'bidirectional'
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
function workflowRuntimeEdgeSemanticKey(edge = {}) {
|
|
682
|
+
const from = String(edge.from || edge.source || '').trim()
|
|
683
|
+
const to = String(edge.to || edge.target || '').trim()
|
|
684
|
+
if (!from || !to) return ''
|
|
685
|
+
const relation = String(edge.relation || 'wf-bridge').trim() || 'wf-bridge'
|
|
686
|
+
const direction = normalizeEdgeDirection(edge.direction)
|
|
687
|
+
const sourceHandle = String(edge.sourceHandle || '').trim()
|
|
688
|
+
const targetHandle = String(edge.targetHandle || '').trim()
|
|
689
|
+
if (direction !== 'source-to-target') {
|
|
690
|
+
return [
|
|
691
|
+
direction,
|
|
692
|
+
relation,
|
|
693
|
+
...[`${from}:${sourceHandle}`, `${to}:${targetHandle}`].sort(),
|
|
694
|
+
].join('|')
|
|
695
|
+
}
|
|
696
|
+
return [direction, from, sourceHandle, relation, to, targetHandle].join('|')
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function workflowRuntimeEdges(graph = {}) {
|
|
700
|
+
const graphEdges = Array.isArray(graph.edges) ? graph.edges : []
|
|
701
|
+
const dockEdges = (Array.isArray(graph.capsuleDockLinks) ? graph.capsuleDockLinks : [])
|
|
702
|
+
.flatMap((link) => (
|
|
703
|
+
Array.isArray(link?.connections) ? link.connections : []
|
|
704
|
+
).map((connection) => {
|
|
705
|
+
const from = String(connection?.from || connection?.source || '').trim()
|
|
706
|
+
const to = String(connection?.to || connection?.target || '').trim()
|
|
707
|
+
if (!from || !to || from === to) return null
|
|
708
|
+
return {
|
|
709
|
+
id: String(connection.id || `dock:${link.id || 'capsule'}:${from}->${to}`).trim(),
|
|
710
|
+
kind: 'capsule-dock-link',
|
|
711
|
+
from,
|
|
712
|
+
to,
|
|
713
|
+
source: from,
|
|
714
|
+
target: to,
|
|
715
|
+
relation: String(connection.relation || 'wf-bridge').trim() || 'wf-bridge',
|
|
716
|
+
direction: normalizeEdgeDirection(connection.direction),
|
|
717
|
+
sourceHandle: connection.sourceHandle || null,
|
|
718
|
+
targetHandle: connection.targetHandle || null,
|
|
719
|
+
dockLinkId: link.id || '',
|
|
720
|
+
}
|
|
721
|
+
}).filter(Boolean))
|
|
722
|
+
const seen = new Set()
|
|
723
|
+
return [...graphEdges, ...dockEdges].filter((edge) => {
|
|
724
|
+
const key = workflowRuntimeEdgeSemanticKey(edge)
|
|
725
|
+
|| String(edge.id || `${edge.from || edge.source}->${edge.to || edge.target}`).trim()
|
|
726
|
+
if (!key) return false
|
|
727
|
+
if (seen.has(key)) return false
|
|
728
|
+
seen.add(key)
|
|
729
|
+
return true
|
|
730
|
+
})
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
function connectionForEdge(edge, nodeId) {
|
|
734
|
+
const from = edge.from || edge.source
|
|
735
|
+
const to = edge.to || edge.target
|
|
736
|
+
const isSource = from === nodeId
|
|
737
|
+
const isTarget = to === nodeId
|
|
738
|
+
if (!isSource && !isTarget) return null
|
|
739
|
+
return {
|
|
740
|
+
edgeId: edge.id || `${from}->${to}`,
|
|
741
|
+
peerNodeId: isSource ? to : from,
|
|
742
|
+
endpointRole: isSource ? 'source' : 'target',
|
|
743
|
+
localHandle: isSource ? edge.sourceHandle || null : edge.targetHandle || null,
|
|
744
|
+
peerHandle: isSource ? edge.targetHandle || null : edge.sourceHandle || null,
|
|
745
|
+
sourceHandle: edge.sourceHandle || null,
|
|
746
|
+
targetHandle: edge.targetHandle || null,
|
|
747
|
+
relation: edge.relation || 'wf-bridge',
|
|
748
|
+
direction: normalizeEdgeDirection(edge.direction),
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
export function buildConnections(nodeId, graph) {
|
|
753
|
+
return workflowRuntimeEdges(graph)
|
|
754
|
+
.map(edge => connectionForEdge(edge, nodeId))
|
|
755
|
+
.filter(Boolean)
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
export function findAgentGraphNode(graph, key) {
|
|
759
|
+
const id = String(key || '')
|
|
760
|
+
return (graph.nodes || []).find(n => (
|
|
761
|
+
n.sessionId
|
|
762
|
+
&& (
|
|
763
|
+
(n.nodeId || n.id) === id
|
|
764
|
+
|| n.sessionId === id
|
|
765
|
+
)
|
|
766
|
+
)) || null
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function canonicalAgentActorNodeId(graph, actorNodeId) {
|
|
770
|
+
const value = String(actorNodeId || '').trim()
|
|
771
|
+
if (!value) return ''
|
|
772
|
+
const graphNode = findAgentGraphNode(graph, value)
|
|
773
|
+
return graphNode ? agentNodeId(graphNode) : ''
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
// ── ref-building primitives (shared by every connected-ref builder) ──
|
|
777
|
+
|
|
778
|
+
function fileMetadata(projectRoot, file = {}) {
|
|
779
|
+
try {
|
|
780
|
+
const meta = getWorkspaceMeta(projectRoot, file.path || '')
|
|
781
|
+
return {
|
|
782
|
+
exists: Boolean(meta.exists),
|
|
783
|
+
stale: !meta.exists,
|
|
784
|
+
needsRefresh: !meta.exists,
|
|
785
|
+
staleUnknown: false,
|
|
786
|
+
etag: meta.etag ?? null,
|
|
787
|
+
mtime: meta.mtime ?? null,
|
|
788
|
+
size: Number(meta.size || 0),
|
|
789
|
+
mime: meta.mime || file.mime || workspaceMimeForPath(file.path || ''),
|
|
790
|
+
}
|
|
791
|
+
} catch {
|
|
792
|
+
return {
|
|
793
|
+
exists: null,
|
|
794
|
+
stale: null,
|
|
795
|
+
needsRefresh: true,
|
|
796
|
+
staleUnknown: true,
|
|
797
|
+
etag: null,
|
|
798
|
+
mtime: null,
|
|
799
|
+
size: Number(file.size || 0),
|
|
800
|
+
mime: file.mime || workspaceMimeForPath(file.path || ''),
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function fileSupportsText(file = {}) {
|
|
806
|
+
const mime = String(file.mime || workspaceMimeForPath(file.path || '')).split(';')[0].trim().toLowerCase()
|
|
807
|
+
return mime.startsWith('text/')
|
|
808
|
+
|| ['application/json', 'application/xml', 'application/yaml', 'application/x-yaml'].includes(mime)
|
|
809
|
+
|| /\.(md|markdown|txt|csv|json|yaml|yml|log|js|mjs|jsx|ts|tsx|css|html)$/i.test(String(file.path || ''))
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
export function handlesFor(type) {
|
|
813
|
+
if (type === 'file') return {
|
|
814
|
+
inputs: [],
|
|
815
|
+
outputs: [],
|
|
816
|
+
bidirectional: ['file'],
|
|
817
|
+
ports: ['file'],
|
|
818
|
+
physical: ['file:left', 'file:right', 'file:top', 'file:bottom'],
|
|
819
|
+
}
|
|
820
|
+
if (type === 'goal') return {
|
|
821
|
+
inputs: [],
|
|
822
|
+
outputs: [],
|
|
823
|
+
bidirectional: ['goal'],
|
|
824
|
+
ports: ['goal'],
|
|
825
|
+
physical: ['goal:left', 'goal:right', 'goal:top', 'goal:bottom'],
|
|
826
|
+
directions: {
|
|
827
|
+
goal: 'bidirectional',
|
|
828
|
+
},
|
|
829
|
+
}
|
|
830
|
+
if (EVENT_NODE_TYPES.has(type)) return {
|
|
831
|
+
inputs: ['config'],
|
|
832
|
+
outputs: ['event'],
|
|
833
|
+
bidirectional: ['status'],
|
|
834
|
+
ports: ['event', 'config', 'status'],
|
|
835
|
+
physical: ['config:left', 'event:right', 'event:top', 'event:bottom'],
|
|
836
|
+
directions: {
|
|
837
|
+
event: 'source-to-target',
|
|
838
|
+
config: 'target-only',
|
|
839
|
+
status: 'bidirectional',
|
|
840
|
+
},
|
|
841
|
+
}
|
|
842
|
+
if (type === 'skill-group' || type === 'mcp-connector') return {
|
|
843
|
+
inputs: [],
|
|
844
|
+
outputs: [],
|
|
845
|
+
bidirectional: ['capability'],
|
|
846
|
+
ports: ['capability'],
|
|
847
|
+
physical: ['capability:left', 'capability:right', 'capability:top', 'capability:bottom'],
|
|
848
|
+
directions: {
|
|
849
|
+
capability: 'bidirectional',
|
|
850
|
+
},
|
|
851
|
+
}
|
|
852
|
+
if (type === 'markdown') return {
|
|
853
|
+
inputs: [],
|
|
854
|
+
outputs: [],
|
|
855
|
+
bidirectional: ['markdown'],
|
|
856
|
+
ports: ['markdown'],
|
|
857
|
+
physical: ['markdown:left', 'markdown:right', 'markdown:top', 'markdown:bottom'],
|
|
858
|
+
}
|
|
859
|
+
if (type === 'excalidraw') return {
|
|
860
|
+
inputs: [],
|
|
861
|
+
outputs: [],
|
|
862
|
+
bidirectional: ['scene'],
|
|
863
|
+
ports: ['scene'],
|
|
864
|
+
physical: ['scene:left', 'scene:right', 'scene:top', 'scene:bottom'],
|
|
865
|
+
}
|
|
866
|
+
return { inputs: [], outputs: [] }
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Registry-derived per-node-type capabilities (W39 audit). The optional
|
|
870
|
+
// projectRoot parameter lets callers that hold a project root resolve the
|
|
871
|
+
// registry against it; callers without one (workflow-node-runtime.mjs
|
|
872
|
+
// snapshots) fall back to cwd / module repo root.
|
|
873
|
+
export function buildCapabilities(type, state, projectRoot = '') {
|
|
874
|
+
const keys = capabilityKeysForType(resolveActionRegistry(projectRoot), type)
|
|
875
|
+
if (type === 'file') {
|
|
876
|
+
const file = state.file || {}
|
|
877
|
+
return keys.filter(key => key !== 'text:read' || fileSupportsText(file))
|
|
878
|
+
}
|
|
879
|
+
return keys
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// Canonical resource content-ref shape. NOTE: file content refs intentionally
|
|
883
|
+
// omit `etag` — the snapshot-path contract pins this exact shape
|
|
884
|
+
// (component-node-api AC-004) and `metadata.etag` carries the etag for files.
|
|
885
|
+
export function contentRefForResource(type, ref = {}) {
|
|
886
|
+
if (type === 'file') {
|
|
887
|
+
const file = ref.file || {}
|
|
888
|
+
return {
|
|
889
|
+
kind: file.source === 'user-file' ? 'user-file' : 'workspace-file',
|
|
890
|
+
source: file.source || 'workspace',
|
|
891
|
+
path: file.path || '',
|
|
892
|
+
mime: file.mime || workspaceMimeForPath(file.path || ''),
|
|
893
|
+
size: Number(file.size || 0),
|
|
894
|
+
endpoints: {
|
|
895
|
+
meta: '/api/workspace/meta',
|
|
896
|
+
bytes: '/api/workspace/file',
|
|
897
|
+
text: '/api/workspace/text',
|
|
898
|
+
},
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
if (type === 'markdown') {
|
|
902
|
+
return {
|
|
903
|
+
kind: 'component-state',
|
|
904
|
+
statePath: ref.statePath,
|
|
905
|
+
revision: Number(ref.revision || 0),
|
|
906
|
+
field: 'markdown',
|
|
907
|
+
mime: 'text/markdown',
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
if (type === 'excalidraw') {
|
|
911
|
+
return {
|
|
912
|
+
kind: 'component-state',
|
|
913
|
+
statePath: ref.statePath,
|
|
914
|
+
revision: Number(ref.revision || 0),
|
|
915
|
+
field: 'scene',
|
|
916
|
+
mime: 'application/vnd.excalidraw+json',
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
return {
|
|
920
|
+
kind: 'component-state',
|
|
921
|
+
statePath: ref.statePath,
|
|
922
|
+
revision: Number(ref.revision || 0),
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
export function compareResourceRefs(componentOrder) {
|
|
927
|
+
return (a, b) => {
|
|
928
|
+
const created = String(a.createdAt || '').localeCompare(String(b.createdAt || ''))
|
|
929
|
+
if (created !== 0) return created
|
|
930
|
+
const order = (componentOrder.get(a.nodeId) ?? Number.MAX_SAFE_INTEGER) - (componentOrder.get(b.nodeId) ?? Number.MAX_SAFE_INTEGER)
|
|
931
|
+
if (order !== 0) return order
|
|
932
|
+
return String(a.nodeId).localeCompare(String(b.nodeId))
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
export function mergeConnectedRefsByNodeId(refs = [], preferredRelation = '') {
|
|
937
|
+
const byNodeId = new Map()
|
|
938
|
+
for (const ref of refs) {
|
|
939
|
+
const nodeId = String(ref?.nodeId || '').trim()
|
|
940
|
+
if (!nodeId) continue
|
|
941
|
+
const existing = byNodeId.get(nodeId)
|
|
942
|
+
if (!existing) {
|
|
943
|
+
byNodeId.set(nodeId, {
|
|
944
|
+
...ref,
|
|
945
|
+
connections: Array.isArray(ref.connections) ? [...ref.connections] : (ref.connection ? [ref.connection] : []),
|
|
946
|
+
})
|
|
947
|
+
continue
|
|
948
|
+
}
|
|
949
|
+
const connections = Array.isArray(existing.connections) ? existing.connections : []
|
|
950
|
+
for (const connection of Array.isArray(ref.connections) ? ref.connections : (ref.connection ? [ref.connection] : [])) {
|
|
951
|
+
const key = [
|
|
952
|
+
connection.edgeId || '',
|
|
953
|
+
connection.relation || '',
|
|
954
|
+
connection.direction || '',
|
|
955
|
+
connection.sourceHandle || '',
|
|
956
|
+
connection.targetHandle || '',
|
|
957
|
+
].join('|')
|
|
958
|
+
const exists = connections.some(item => [
|
|
959
|
+
item.edgeId || '',
|
|
960
|
+
item.relation || '',
|
|
961
|
+
item.direction || '',
|
|
962
|
+
item.sourceHandle || '',
|
|
963
|
+
item.targetHandle || '',
|
|
964
|
+
].join('|') === key)
|
|
965
|
+
if (!exists) connections.push(connection)
|
|
966
|
+
}
|
|
967
|
+
if (preferredRelation && existing.relation !== preferredRelation && ref.relation === preferredRelation) {
|
|
968
|
+
byNodeId.set(nodeId, {
|
|
969
|
+
...ref,
|
|
970
|
+
connections,
|
|
971
|
+
})
|
|
972
|
+
} else {
|
|
973
|
+
existing.connections = connections
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return [...byNodeId.values()]
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
// ── timer control affordance ──
|
|
980
|
+
|
|
981
|
+
export function hasTimerControlEdge(graph, actorNodeId, timerNodeId) {
|
|
982
|
+
const actor = canonicalAgentActorNodeId(graph, actorNodeId)
|
|
983
|
+
if (!actor || !timerNodeId) return false
|
|
984
|
+
return workflowRuntimeEdges(graph).some((edge) => {
|
|
985
|
+
const from = edge.from || edge.source
|
|
986
|
+
const to = edge.to || edge.target
|
|
987
|
+
return from === actor
|
|
988
|
+
&& to === timerNodeId
|
|
989
|
+
&& normalizeEdgeDirection(edge.direction) === 'source-to-target'
|
|
990
|
+
&& String(edge.relation || '').trim() === 'control'
|
|
991
|
+
})
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function allowedTimerActionsForAgent(ref, actorNodeId, graph) {
|
|
995
|
+
if ((ref.type || ref.eventKind) !== 'timer') return []
|
|
996
|
+
if (!hasTimerControlEdge(graph, actorNodeId, ref.nodeId || '')) return []
|
|
997
|
+
return typeof timerControlActionsForState === 'function'
|
|
998
|
+
? timerControlActionsForState(ref)
|
|
999
|
+
: []
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function timerControlAffordanceForAgent(ref, actorNodeId, graph) {
|
|
1003
|
+
if ((ref.type || ref.eventKind) !== 'timer') {
|
|
1004
|
+
return {
|
|
1005
|
+
canControl: false,
|
|
1006
|
+
controlRequired: false,
|
|
1007
|
+
controlEdgeRequired: false,
|
|
1008
|
+
allowedActions: [],
|
|
1009
|
+
deniedActions: [],
|
|
1010
|
+
controlReason: '',
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
const policyActions = typeof timerControlActionsForState === 'function'
|
|
1014
|
+
? timerControlActionsForState(ref)
|
|
1015
|
+
: []
|
|
1016
|
+
const hasControlEdge = hasTimerControlEdge(graph, actorNodeId, ref.nodeId || '')
|
|
1017
|
+
const allowedActions = hasControlEdge ? policyActions : []
|
|
1018
|
+
return {
|
|
1019
|
+
canControl: allowedActions.length > 0,
|
|
1020
|
+
controlRequired: true,
|
|
1021
|
+
controlEdgeRequired: !hasControlEdge,
|
|
1022
|
+
requiredRelation: 'control',
|
|
1023
|
+
requiredDirection: 'source-to-target',
|
|
1024
|
+
requiredEdge: {
|
|
1025
|
+
from: actorNodeId,
|
|
1026
|
+
to: ref.nodeId || '',
|
|
1027
|
+
relation: 'control',
|
|
1028
|
+
direction: 'source-to-target',
|
|
1029
|
+
sourceHandle: 'context',
|
|
1030
|
+
targetHandle: 'config',
|
|
1031
|
+
},
|
|
1032
|
+
allowedActions,
|
|
1033
|
+
deniedActions: hasControlEdge ? [] : policyActions,
|
|
1034
|
+
controlReason: hasControlEdge
|
|
1035
|
+
? 'Agent has an Agent -> Timer control connection; Timer typed actions may be used through workflow-node-action.'
|
|
1036
|
+
: 'Timer event delivery is not Timer control. To control this Timer, connect Agent -> Timer with relation control, then use workflow-node-action; never edit Harness/a2a/event-nodes/**/state.json.',
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// ── connected ref builders ──
|
|
1041
|
+
|
|
1042
|
+
export function buildConnectedResourceRefs(projectRoot, nodeId, graph) {
|
|
1043
|
+
const refs = componentStateRefs(projectRoot)
|
|
1044
|
+
const componentOrder = new Map(listLiveComponentNodes(projectRoot).map((node, index) => [node.nodeId, index]))
|
|
1045
|
+
return buildConnections(nodeId, graph)
|
|
1046
|
+
.map((connection) => {
|
|
1047
|
+
const ref = refs[connection.peerNodeId]
|
|
1048
|
+
if (!ref) return null
|
|
1049
|
+
const type = ref.type
|
|
1050
|
+
const metadata = type === 'file' ? fileMetadata(projectRoot, ref.file || {}) : null
|
|
1051
|
+
const fileForCapabilities = metadata ? { ...(ref.file || {}), mime: metadata.mime } : (ref.file || {})
|
|
1052
|
+
const resourceConnection = {
|
|
1053
|
+
edgeId: connection.edgeId,
|
|
1054
|
+
endpointRole: connection.endpointRole,
|
|
1055
|
+
localHandle: connection.localHandle,
|
|
1056
|
+
peerHandle: connection.peerHandle,
|
|
1057
|
+
sourceHandle: connection.sourceHandle,
|
|
1058
|
+
targetHandle: connection.targetHandle,
|
|
1059
|
+
relation: connection.relation,
|
|
1060
|
+
direction: connection.direction,
|
|
1061
|
+
}
|
|
1062
|
+
return {
|
|
1063
|
+
nodeId: connection.peerNodeId,
|
|
1064
|
+
kind: 'component-node',
|
|
1065
|
+
type,
|
|
1066
|
+
title: ref.title || connection.peerNodeId,
|
|
1067
|
+
shortId: shortNodeId(connection.peerNodeId),
|
|
1068
|
+
displayName: nodeDisplayName(ref.title || connection.peerNodeId, connection.peerNodeId),
|
|
1069
|
+
createdAt: ref.createdAt || null,
|
|
1070
|
+
direction: 'bidirectional',
|
|
1071
|
+
endpointRole: connection.endpointRole,
|
|
1072
|
+
relation: connection.relation,
|
|
1073
|
+
sourceHandle: connection.sourceHandle,
|
|
1074
|
+
targetHandle: connection.targetHandle,
|
|
1075
|
+
connection: resourceConnection,
|
|
1076
|
+
connections: [resourceConnection],
|
|
1077
|
+
stateRef: {
|
|
1078
|
+
path: ref.statePath,
|
|
1079
|
+
revision: Number(ref.revision || 0),
|
|
1080
|
+
},
|
|
1081
|
+
contentRef: contentRefForResource(type, ref),
|
|
1082
|
+
capabilities: buildCapabilities(type, { file: fileForCapabilities }, projectRoot),
|
|
1083
|
+
handles: handlesFor(type),
|
|
1084
|
+
...(metadata ? { metadata } : {}),
|
|
1085
|
+
...(ref.file ? { file: ref.file } : {}),
|
|
1086
|
+
}
|
|
1087
|
+
})
|
|
1088
|
+
.filter(Boolean)
|
|
1089
|
+
.sort(compareResourceRefs(componentOrder))
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
export function buildConnectedEventRefs(projectRoot, nodeId, graph) {
|
|
1093
|
+
const refs = eventStateRefs(projectRoot)
|
|
1094
|
+
const connectedRefs = buildConnections(nodeId, graph)
|
|
1095
|
+
.map((connection) => {
|
|
1096
|
+
const ref = refs[connection.peerNodeId]
|
|
1097
|
+
if (!ref) return null
|
|
1098
|
+
const eventConnection = {
|
|
1099
|
+
edgeId: connection.edgeId,
|
|
1100
|
+
endpointRole: connection.endpointRole,
|
|
1101
|
+
localHandle: connection.localHandle,
|
|
1102
|
+
peerHandle: connection.peerHandle,
|
|
1103
|
+
sourceHandle: connection.sourceHandle,
|
|
1104
|
+
targetHandle: connection.targetHandle,
|
|
1105
|
+
relation: connection.relation,
|
|
1106
|
+
direction: connection.direction,
|
|
1107
|
+
}
|
|
1108
|
+
const type = ref.type || ref.eventKind || 'event'
|
|
1109
|
+
const controlAffordance = timerControlAffordanceForAgent({ ...ref, nodeId: connection.peerNodeId }, nodeId, graph)
|
|
1110
|
+
return {
|
|
1111
|
+
nodeId: connection.peerNodeId,
|
|
1112
|
+
kind: 'event-node',
|
|
1113
|
+
type,
|
|
1114
|
+
eventKind: ref.eventKind || type,
|
|
1115
|
+
title: ref.title || connection.peerNodeId,
|
|
1116
|
+
shortId: shortNodeId(connection.peerNodeId),
|
|
1117
|
+
displayName: nodeDisplayName(ref.title || connection.peerNodeId, connection.peerNodeId),
|
|
1118
|
+
direction: connection.direction,
|
|
1119
|
+
endpointRole: connection.endpointRole,
|
|
1120
|
+
relation: connection.relation,
|
|
1121
|
+
sourceHandle: connection.sourceHandle,
|
|
1122
|
+
targetHandle: connection.targetHandle,
|
|
1123
|
+
connection: eventConnection,
|
|
1124
|
+
connections: [eventConnection],
|
|
1125
|
+
stateRef: {
|
|
1126
|
+
path: ref.statePath,
|
|
1127
|
+
revision: Number(ref.revision || 0),
|
|
1128
|
+
},
|
|
1129
|
+
contentRef: {
|
|
1130
|
+
kind: 'event-node-state',
|
|
1131
|
+
statePath: ref.statePath,
|
|
1132
|
+
revision: Number(ref.revision || 0),
|
|
1133
|
+
eventKind: ref.eventKind || type,
|
|
1134
|
+
},
|
|
1135
|
+
capabilities: buildCapabilities(type, ref, projectRoot),
|
|
1136
|
+
handles: handlesFor(type),
|
|
1137
|
+
schedule: ref.schedule || null,
|
|
1138
|
+
heartbeat: ref.heartbeat || null,
|
|
1139
|
+
loop: ref.loop || null,
|
|
1140
|
+
whileGuard: ref.whileGuard || null,
|
|
1141
|
+
taskBinding: ref.taskBinding || null,
|
|
1142
|
+
controlPolicy: ref.controlPolicy || null,
|
|
1143
|
+
allowedActions: controlAffordance.allowedActions,
|
|
1144
|
+
deniedActions: controlAffordance.deniedActions,
|
|
1145
|
+
canControl: controlAffordance.canControl,
|
|
1146
|
+
controlRequired: controlAffordance.controlRequired,
|
|
1147
|
+
controlEdgeRequired: controlAffordance.controlEdgeRequired,
|
|
1148
|
+
requiredRelation: controlAffordance.requiredRelation,
|
|
1149
|
+
requiredDirection: controlAffordance.requiredDirection,
|
|
1150
|
+
requiredEdge: controlAffordance.requiredEdge,
|
|
1151
|
+
controlReason: controlAffordance.controlReason,
|
|
1152
|
+
repository: ref.repository || null,
|
|
1153
|
+
eventFilters: ref.eventFilters || null,
|
|
1154
|
+
dedupeKeys: Array.isArray(ref.dedupeKeys) ? ref.dedupeKeys : [],
|
|
1155
|
+
deliveryCount: Number(ref.deliveryCount || ref.eventCount || 0),
|
|
1156
|
+
lastEvent: ref.lastEvent || null,
|
|
1157
|
+
lastFiredAt: ref.lastFiredAt || '',
|
|
1158
|
+
lastReceivedAt: ref.lastReceivedAt || '',
|
|
1159
|
+
lastDeliveryId: ref.lastDeliveryId || '',
|
|
1160
|
+
eventCount: Number(ref.eventCount || 0),
|
|
1161
|
+
}
|
|
1162
|
+
})
|
|
1163
|
+
.filter(Boolean)
|
|
1164
|
+
return mergeConnectedRefsByNodeId(connectedRefs, 'event')
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
export function buildConnectedCapabilityRefs(projectRoot, nodeId, graph) {
|
|
1168
|
+
const refs = capabilityStateRefs(projectRoot)
|
|
1169
|
+
return buildConnections(nodeId, graph)
|
|
1170
|
+
.map((connection) => {
|
|
1171
|
+
const ref = refs[connection.peerNodeId]
|
|
1172
|
+
if (!ref) return null
|
|
1173
|
+
const capabilityConnection = {
|
|
1174
|
+
edgeId: connection.edgeId,
|
|
1175
|
+
endpointRole: connection.endpointRole,
|
|
1176
|
+
localHandle: connection.localHandle,
|
|
1177
|
+
peerHandle: connection.peerHandle,
|
|
1178
|
+
sourceHandle: connection.sourceHandle,
|
|
1179
|
+
targetHandle: connection.targetHandle,
|
|
1180
|
+
relation: connection.relation,
|
|
1181
|
+
direction: connection.direction,
|
|
1182
|
+
}
|
|
1183
|
+
const type = ref.type || ref.capabilityKind || 'capability'
|
|
1184
|
+
return {
|
|
1185
|
+
nodeId: connection.peerNodeId,
|
|
1186
|
+
kind: 'capability-node',
|
|
1187
|
+
type,
|
|
1188
|
+
capabilityKind: ref.capabilityKind || type,
|
|
1189
|
+
title: ref.title || connection.peerNodeId,
|
|
1190
|
+
shortId: shortNodeId(connection.peerNodeId),
|
|
1191
|
+
displayName: nodeDisplayName(ref.title || connection.peerNodeId, connection.peerNodeId),
|
|
1192
|
+
direction: connection.direction,
|
|
1193
|
+
endpointRole: connection.endpointRole,
|
|
1194
|
+
relation: connection.relation,
|
|
1195
|
+
sourceHandle: connection.sourceHandle,
|
|
1196
|
+
targetHandle: connection.targetHandle,
|
|
1197
|
+
connection: capabilityConnection,
|
|
1198
|
+
connections: [capabilityConnection],
|
|
1199
|
+
stateRef: {
|
|
1200
|
+
path: ref.statePath,
|
|
1201
|
+
revision: Number(ref.revision || 0),
|
|
1202
|
+
},
|
|
1203
|
+
contentRef: {
|
|
1204
|
+
kind: 'capability-node-state',
|
|
1205
|
+
statePath: ref.statePath,
|
|
1206
|
+
revision: Number(ref.revision || 0),
|
|
1207
|
+
capabilityKind: ref.capabilityKind || type,
|
|
1208
|
+
},
|
|
1209
|
+
capabilities: buildCapabilities(type, ref, projectRoot),
|
|
1210
|
+
handles: handlesFor(type),
|
|
1211
|
+
nodeSemantics: ref.nodeSemantics,
|
|
1212
|
+
sourceGroup: ref.sourceGroup || null,
|
|
1213
|
+
category: ref.category || '',
|
|
1214
|
+
tags: Array.isArray(ref.tags) ? ref.tags : [],
|
|
1215
|
+
installSource: ref.installSource || null,
|
|
1216
|
+
lockRef: ref.lockRef || '',
|
|
1217
|
+
loadStrategy: ref.loadStrategy || 'group-summary',
|
|
1218
|
+
prompt: ref.prompt || '',
|
|
1219
|
+
skills: Array.isArray(ref.skills) ? ref.skills : [],
|
|
1220
|
+
skillNames: Array.isArray(ref.skillNames) ? ref.skillNames : [],
|
|
1221
|
+
skillCount: Number(ref.skillCount || 0),
|
|
1222
|
+
servers: Array.isArray(ref.servers) ? ref.servers : [],
|
|
1223
|
+
serverNames: Array.isArray(ref.serverNames) ? ref.serverNames : [],
|
|
1224
|
+
serverCount: Number(ref.serverCount || 0),
|
|
1225
|
+
transports: Array.isArray(ref.transports) ? ref.transports : [],
|
|
1226
|
+
envKeyNames: Array.isArray(ref.envKeyNames) ? ref.envKeyNames : [],
|
|
1227
|
+
envKeyCount: Number(ref.envKeyCount || 0),
|
|
1228
|
+
redactedFieldCount: Number(ref.redactedFieldCount || 0),
|
|
1229
|
+
state: 'connected',
|
|
1230
|
+
executor: 'agent',
|
|
1231
|
+
}
|
|
1232
|
+
})
|
|
1233
|
+
.filter(Boolean)
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
export function effectiveSkillGroupsFor(connectedCapabilityNodeRefs = []) {
|
|
1237
|
+
return connectedCapabilityNodeRefs
|
|
1238
|
+
.filter(ref => String(ref?.capabilityKind || ref?.type || '') === 'skill-group')
|
|
1239
|
+
.map(ref => {
|
|
1240
|
+
const names = (Array.isArray(ref.skillNames) ? ref.skillNames : []).filter(Boolean);
|
|
1241
|
+
const promptText = (ref.prompt && String(ref.prompt).trim())
|
|
1242
|
+
? String(ref.prompt).trim()
|
|
1243
|
+
: (names.length ? `You have these skills available in this pack: ${names.join(', ')}. Use them when the task needs them.` : '');
|
|
1244
|
+
return {
|
|
1245
|
+
nodeId: ref.nodeId,
|
|
1246
|
+
kind: 'effective-skill-group',
|
|
1247
|
+
capabilityKind: 'skill-group',
|
|
1248
|
+
title: ref.title || ref.nodeId,
|
|
1249
|
+
groupId: ref.sourceGroup?.id || '',
|
|
1250
|
+
label: ref.sourceGroup?.label || ref.title || ref.nodeId,
|
|
1251
|
+
category: ref.category || ref.sourceGroup?.kind || 'skills',
|
|
1252
|
+
tags: Array.isArray(ref.tags) ? ref.tags : [],
|
|
1253
|
+
skillCount: Number(ref.skillCount || 0),
|
|
1254
|
+
skillNames: Array.isArray(ref.skillNames) ? ref.skillNames : [],
|
|
1255
|
+
skillRefs: (Array.isArray(ref.skills) ? ref.skills : [])
|
|
1256
|
+
.map(skill => ({
|
|
1257
|
+
id: String(skill.id || `skill:${skill.name || ''}`).trim(),
|
|
1258
|
+
name: String(skill.name || skill.id || '').trim(),
|
|
1259
|
+
title: String(skill.title || skill.name || skill.id || '').trim(),
|
|
1260
|
+
source: String(skill.source || '').trim(),
|
|
1261
|
+
state: String(skill.state || '').trim(),
|
|
1262
|
+
enabled: skill.enabled === false ? false : true,
|
|
1263
|
+
}))
|
|
1264
|
+
.filter(skill => skill.name || skill.id),
|
|
1265
|
+
loadStrategy: ref.loadStrategy || 'group-summary',
|
|
1266
|
+
prompt: ref.prompt || '',
|
|
1267
|
+
promptText,
|
|
1268
|
+
lockRef: ref.lockRef || '',
|
|
1269
|
+
installSource: ref.installSource ? {
|
|
1270
|
+
provider: ref.installSource.provider || '',
|
|
1271
|
+
providerLabel: ref.installSource.providerLabel || '',
|
|
1272
|
+
packSlug: ref.installSource.packSlug || '',
|
|
1273
|
+
packName: ref.installSource.packName || '',
|
|
1274
|
+
version: ref.installSource.version || '',
|
|
1275
|
+
targetScope: ref.installSource.targetScope || '',
|
|
1276
|
+
targetRuntime: ref.installSource.targetRuntime || '',
|
|
1277
|
+
installedAt: ref.installSource.installedAt || '',
|
|
1278
|
+
signature: ref.installSource.signature || { present: false, verified: false },
|
|
1279
|
+
lockfileSignature: ref.installSource.lockfileSignature || { present: false, verified: false },
|
|
1280
|
+
} : null,
|
|
1281
|
+
relation: ref.relation || 'capability',
|
|
1282
|
+
direction: ref.direction || 'bidirectional',
|
|
1283
|
+
endpointRole: ref.endpointRole || '',
|
|
1284
|
+
connection: ref.connection || null,
|
|
1285
|
+
executor: 'agent',
|
|
1286
|
+
};
|
|
1287
|
+
})
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
function deletedGraphNodeSets(graph) {
|
|
1291
|
+
const deletedNodes = Array.isArray(graph?.deletedNodes) ? graph.deletedNodes : []
|
|
1292
|
+
return {
|
|
1293
|
+
nodeIds: new Set(deletedNodes.map(node => String(node?.nodeId || '').trim()).filter(Boolean)),
|
|
1294
|
+
sessionIds: new Set(deletedNodes.map(node => String(node?.sessionId || '').trim()).filter(Boolean)),
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
export function isDeletedGraphNode(graph, nodeIdOrSessionId) {
|
|
1299
|
+
const key = String(nodeIdOrSessionId || '').trim()
|
|
1300
|
+
if (!key) return false
|
|
1301
|
+
const { nodeIds, sessionIds } = deletedGraphNodeSets(graph)
|
|
1302
|
+
if (nodeIds.has(key) || sessionIds.has(key)) return true
|
|
1303
|
+
if (key.startsWith('session-') && sessionIds.has(key.slice('session-'.length))) return true
|
|
1304
|
+
return false
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
function filterRefRecordForGraph(record, graph) {
|
|
1308
|
+
return Object.fromEntries(
|
|
1309
|
+
Object.entries(record || {})
|
|
1310
|
+
.filter(([nodeId, ref]) => !isDeletedGraphNode(graph, nodeId) && !isDeletedGraphNode(graph, ref?.nodeId)),
|
|
1311
|
+
)
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
export function goalStateRefsForGraph(projectRoot, graph) {
|
|
1315
|
+
return filterRefRecordForGraph(goalStateRefs(projectRoot), graph)
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
export function buildConnectedGoalRefs(projectRoot, nodeId, graph) {
|
|
1319
|
+
const refs = goalStateRefsForGraph(projectRoot, graph)
|
|
1320
|
+
return buildConnections(nodeId, graph)
|
|
1321
|
+
.map((connection) => {
|
|
1322
|
+
const ref = refs[connection.peerNodeId]
|
|
1323
|
+
if (!ref) return null
|
|
1324
|
+
const goalConnection = {
|
|
1325
|
+
edgeId: connection.edgeId,
|
|
1326
|
+
endpointRole: connection.endpointRole,
|
|
1327
|
+
localHandle: connection.localHandle,
|
|
1328
|
+
peerHandle: connection.peerHandle,
|
|
1329
|
+
sourceHandle: connection.sourceHandle,
|
|
1330
|
+
targetHandle: connection.targetHandle,
|
|
1331
|
+
relation: connection.relation,
|
|
1332
|
+
direction: connection.direction,
|
|
1333
|
+
}
|
|
1334
|
+
return {
|
|
1335
|
+
nodeId: connection.peerNodeId,
|
|
1336
|
+
kind: 'goal-node',
|
|
1337
|
+
type: 'goal',
|
|
1338
|
+
title: ref.title || connection.peerNodeId,
|
|
1339
|
+
shortId: shortNodeId(connection.peerNodeId),
|
|
1340
|
+
displayName: nodeDisplayName(ref.title || connection.peerNodeId, connection.peerNodeId),
|
|
1341
|
+
taskId: ref.taskId,
|
|
1342
|
+
status: ref.status,
|
|
1343
|
+
phase: ref.phase,
|
|
1344
|
+
gate: ref.gate,
|
|
1345
|
+
direction: connection.direction,
|
|
1346
|
+
endpointRole: connection.endpointRole,
|
|
1347
|
+
relation: connection.relation,
|
|
1348
|
+
sourceHandle: connection.sourceHandle,
|
|
1349
|
+
targetHandle: connection.targetHandle,
|
|
1350
|
+
connection: goalConnection,
|
|
1351
|
+
connections: [goalConnection],
|
|
1352
|
+
stateRef: {
|
|
1353
|
+
path: ref.statePath,
|
|
1354
|
+
revision: Number(ref.revision || 0),
|
|
1355
|
+
},
|
|
1356
|
+
contentRef: ref.contentRef,
|
|
1357
|
+
capabilities: buildCapabilities('goal', ref, projectRoot),
|
|
1358
|
+
handles: handlesFor('goal'),
|
|
1359
|
+
acceptance: Array.isArray(ref.acceptance) ? ref.acceptance : [],
|
|
1360
|
+
planItems: Array.isArray(ref.planItems) ? ref.planItems : [],
|
|
1361
|
+
progress: ref.progress || { verified: 0, total: 0 },
|
|
1362
|
+
confirmation: ref.confirmation || null,
|
|
1363
|
+
wdt: ref.wdt || null,
|
|
1364
|
+
}
|
|
1365
|
+
})
|
|
1366
|
+
.filter(Boolean)
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
export function buildConnectedAgentRefs(projectRoot, nodeId, graph, { isMainAgent = false } = {}) {
|
|
1370
|
+
const agentNodesById = new Map((Array.isArray(graph.nodes) ? graph.nodes : [])
|
|
1371
|
+
.filter(node => node && node.sessionId)
|
|
1372
|
+
.map(node => [agentNodeId(node), node]))
|
|
1373
|
+
return buildConnections(nodeId, graph)
|
|
1374
|
+
.map((connection) => {
|
|
1375
|
+
const ref = agentNodesById.get(connection.peerNodeId)
|
|
1376
|
+
if (!ref) return null
|
|
1377
|
+
const peerNodeId = agentNodeId(ref)
|
|
1378
|
+
const status = ref.status || 'unknown'
|
|
1379
|
+
const live = status === 'running' || status === 'starting'
|
|
1380
|
+
const connectionRef = {
|
|
1381
|
+
edgeId: connection.edgeId,
|
|
1382
|
+
endpointRole: connection.endpointRole,
|
|
1383
|
+
localHandle: connection.localHandle,
|
|
1384
|
+
peerHandle: connection.peerHandle,
|
|
1385
|
+
sourceHandle: connection.sourceHandle,
|
|
1386
|
+
targetHandle: connection.targetHandle,
|
|
1387
|
+
relation: connection.relation,
|
|
1388
|
+
direction: connection.direction,
|
|
1389
|
+
}
|
|
1390
|
+
const writableActions = isMainAgent ? AGENT_PEER_CONTROL_ACTIONS : []
|
|
1391
|
+
const storedDisplayName = String(ref.displayName || '').trim() || String(ref.roleTitle || '').trim()
|
|
1392
|
+
return {
|
|
1393
|
+
nodeId: peerNodeId,
|
|
1394
|
+
kind: 'agent-node',
|
|
1395
|
+
type: 'agent',
|
|
1396
|
+
title: storedDisplayName || ref.label || ref.role || ref.runtime || peerNodeId,
|
|
1397
|
+
shortId: shortNodeId(peerNodeId),
|
|
1398
|
+
displayName: nodeDisplayName(storedDisplayName || ref.label || ref.role || ref.runtime || peerNodeId, peerNodeId),
|
|
1399
|
+
sessionId: ref.sessionId,
|
|
1400
|
+
agentKind: ref.agentKind || '',
|
|
1401
|
+
role: ref.role || '',
|
|
1402
|
+
runtime: ref.runtime || '',
|
|
1403
|
+
status,
|
|
1404
|
+
lifecycle: live ? 'live' : 'stopped',
|
|
1405
|
+
direction: connection.direction,
|
|
1406
|
+
endpointRole: connection.endpointRole,
|
|
1407
|
+
relation: connection.relation,
|
|
1408
|
+
sourceHandle: connection.sourceHandle,
|
|
1409
|
+
targetHandle: connection.targetHandle,
|
|
1410
|
+
connection: connectionRef,
|
|
1411
|
+
connections: [connectionRef],
|
|
1412
|
+
stateRef: {
|
|
1413
|
+
path: agentNodeHomeRel(ref),
|
|
1414
|
+
revision: Number(ref.configRevision || 0),
|
|
1415
|
+
},
|
|
1416
|
+
workspaceRef: {
|
|
1417
|
+
kind: 'agent-node-home',
|
|
1418
|
+
path: agentNodeHomeRel(ref),
|
|
1419
|
+
initPath: agentNodeInitRel(ref),
|
|
1420
|
+
cwd: ref.cwd || '',
|
|
1421
|
+
},
|
|
1422
|
+
delegation: {
|
|
1423
|
+
canDelegate: Boolean(isMainAgent && live),
|
|
1424
|
+
canMessage: live,
|
|
1425
|
+
canReadOutput: true,
|
|
1426
|
+
canReadContext: true,
|
|
1427
|
+
state: live ? 'ready' : 'stopped',
|
|
1428
|
+
sendAction: 'agent.sendInput',
|
|
1429
|
+
messageAction: 'agent.sendMessage',
|
|
1430
|
+
broadcastAction: 'agent.broadcastMessage',
|
|
1431
|
+
readMessagesAction: 'agent.readMessages',
|
|
1432
|
+
readActions: AGENT_PEER_READ_ACTIONS,
|
|
1433
|
+
},
|
|
1434
|
+
allowedActions: uniqueStrings([...AGENT_PEER_READ_ACTIONS, ...AGENT_PEER_MESSAGE_ACTIONS, ...writableActions]),
|
|
1435
|
+
deniedActions: isMainAgent ? [] : AGENT_PEER_CONTROL_ACTIONS,
|
|
1436
|
+
}
|
|
1437
|
+
})
|
|
1438
|
+
.filter(Boolean)
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// ── skill projections ──
|
|
1442
|
+
|
|
1443
|
+
export function capabilityRefsForSkills(skills) {
|
|
1444
|
+
return uniqueStrings(skills).map(name => ({
|
|
1445
|
+
kind: 'skill',
|
|
1446
|
+
capabilityKind: 'skill',
|
|
1447
|
+
skillId: `skill:${name}`,
|
|
1448
|
+
name,
|
|
1449
|
+
title: name,
|
|
1450
|
+
triggers: DEFAULT_AGENT_WORKFLOW_SKILL_TRIGGERS[name] || [],
|
|
1451
|
+
source: 'agent-config',
|
|
1452
|
+
attachment: 'agent-config',
|
|
1453
|
+
state: 'attached',
|
|
1454
|
+
nodeSemantics: 'agent-attached-capability-provider',
|
|
1455
|
+
relation: 'capability',
|
|
1456
|
+
direction: 'bidirectional',
|
|
1457
|
+
executor: 'agent',
|
|
1458
|
+
}))
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
export function skillTriggersFor(skills) {
|
|
1462
|
+
return Object.fromEntries(uniqueStrings(skills).map(name => [
|
|
1463
|
+
name,
|
|
1464
|
+
DEFAULT_AGENT_WORKFLOW_SKILL_TRIGGERS[name] || [],
|
|
1465
|
+
]))
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
// ── output routing ──
|
|
1469
|
+
|
|
1470
|
+
export function outputRoutingFor(settings, connectedResourceRefs) {
|
|
1471
|
+
const routing = settings?.values?.outputRouting || {}
|
|
1472
|
+
const enabled = Boolean(routing.markdownDefaultEnabled)
|
|
1473
|
+
const explicitTargetNodeId = String(routing.markdownTargetNodeId || '').trim()
|
|
1474
|
+
let resolvedTargetNodeId = ''
|
|
1475
|
+
let resolution = enabled ? 'none' : 'disabled'
|
|
1476
|
+
if (enabled && explicitTargetNodeId) {
|
|
1477
|
+
resolvedTargetNodeId = explicitTargetNodeId
|
|
1478
|
+
resolution = 'explicit-target'
|
|
1479
|
+
} else if (enabled && routing.fallback !== false) {
|
|
1480
|
+
const oldest = connectedResourceRefs.filter(ref => ref.type === 'markdown')[0]
|
|
1481
|
+
if (oldest) {
|
|
1482
|
+
resolvedTargetNodeId = oldest.nodeId
|
|
1483
|
+
resolution = 'oldest-connected-markdown'
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
return {
|
|
1487
|
+
markdownDefault: {
|
|
1488
|
+
enabled,
|
|
1489
|
+
explicitTargetNodeId,
|
|
1490
|
+
resolvedTargetNodeId,
|
|
1491
|
+
resolution,
|
|
1492
|
+
},
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
// ── agent node snapshot (the `node` half of the API context response) ──
|
|
1497
|
+
|
|
1498
|
+
export function buildAgentSnapshot(projectRoot, graphNode, graph) {
|
|
1499
|
+
const nodeId = agentNodeId(graphNode)
|
|
1500
|
+
const settings = agentSettings(projectRoot, graphNode)
|
|
1501
|
+
const config = agentConfig(projectRoot, graphNode)
|
|
1502
|
+
return {
|
|
1503
|
+
nodeId,
|
|
1504
|
+
kind: 'agent',
|
|
1505
|
+
version: graph.version || 1,
|
|
1506
|
+
lifecycle: graphNode.status === 'running' || graphNode.status === 'starting' ? 'live' : 'stopped',
|
|
1507
|
+
status: { state: graphNode.status || 'unknown', updatedAt: graphNode.updatedAt || new Date().toISOString() },
|
|
1508
|
+
sessionId: graphNode.sessionId || null,
|
|
1509
|
+
graph: {
|
|
1510
|
+
position: graph.positions?.[nodeId] || graphNode.position || { x: 0, y: 0 },
|
|
1511
|
+
handles: [],
|
|
1512
|
+
connections: buildConnections(nodeId, graph),
|
|
1513
|
+
},
|
|
1514
|
+
stateRef: { path: `Harness/a2a/nodes/${graphNode.sessionId || nodeId}`, revision: 0 },
|
|
1515
|
+
settings: {
|
|
1516
|
+
schemaId: 'agent-settings',
|
|
1517
|
+
values: {
|
|
1518
|
+
runtime: graphNode.runtime,
|
|
1519
|
+
agentKind: graphNode.agentKind,
|
|
1520
|
+
skills: config.skills,
|
|
1521
|
+
skillPolicy: config.skillPolicy,
|
|
1522
|
+
contextSources: config.contextSources,
|
|
1523
|
+
capabilities: config.capabilities,
|
|
1524
|
+
...settings.values,
|
|
1525
|
+
},
|
|
1526
|
+
revision: settings.revision,
|
|
1527
|
+
},
|
|
1528
|
+
capabilities: agentAvailableActions(projectRoot),
|
|
1529
|
+
ui: {
|
|
1530
|
+
previewKind: 'agent',
|
|
1531
|
+
settingsPanel: 'agent-settings',
|
|
1532
|
+
testId: 'workflow-agent-node',
|
|
1533
|
+
labels: { title: graphNode.label || graphNode.runtime || 'Agent' },
|
|
1534
|
+
},
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
// ── canonical Agent context ──
|
|
1539
|
+
|
|
1540
|
+
export function buildAgentContext(projectRoot, graphNode, graph, options = {}) {
|
|
1541
|
+
const nodeId = agentNodeId(graphNode)
|
|
1542
|
+
const sessionId = graphNode.sessionId || null
|
|
1543
|
+
// Backward compat: legacy 'wf-subagents' graph nodes resolve to the
|
|
1544
|
+
// canonical 'wf-node-subagents' id; unspecified defaults to built-in.
|
|
1545
|
+
const rawSubagentMode = String(graphNode.subagentMode || 'built-in-subagents')
|
|
1546
|
+
const subagentMode = rawSubagentMode === 'wf-subagents' ? 'wf-node-subagents' : rawSubagentMode
|
|
1547
|
+
const isMainAgent = options.isMainAgent !== undefined ? Boolean(options.isMainAgent) : isMainAgentGraphNode(graphNode)
|
|
1548
|
+
const roleProfile = readRoleProfile(nodeId, projectRoot)
|
|
1549
|
+
const connections = buildConnections(nodeId, graph)
|
|
1550
|
+
const refs = componentStateRefs(projectRoot)
|
|
1551
|
+
const eventRefs = eventStateRefs(projectRoot)
|
|
1552
|
+
const capabilityRefs = capabilityStateRefs(projectRoot)
|
|
1553
|
+
const goalRefs = goalStateRefsForGraph(projectRoot, graph)
|
|
1554
|
+
const connectedResourceRefs = buildConnectedResourceRefs(projectRoot, nodeId, graph)
|
|
1555
|
+
const connectedAgentRefs = buildConnectedAgentRefs(projectRoot, nodeId, graph, {
|
|
1556
|
+
isMainAgent,
|
|
1557
|
+
})
|
|
1558
|
+
const connectedEventRefs = buildConnectedEventRefs(projectRoot, nodeId, graph)
|
|
1559
|
+
const connectedCapabilityNodeRefs = buildConnectedCapabilityRefs(projectRoot, nodeId, graph)
|
|
1560
|
+
const connectedGoalRefs = buildConnectedGoalRefs(projectRoot, nodeId, graph)
|
|
1561
|
+
const settings = agentSettings(projectRoot, graphNode)
|
|
1562
|
+
const config = agentConfig(projectRoot, graphNode)
|
|
1563
|
+
const defaultSkills = DEFAULT_AGENT_WORKFLOW_SKILLS
|
|
1564
|
+
const availableOnDemandSkills = ON_DEMAND_AGENT_NODE_SKILLS
|
|
1565
|
+
const configuredSkills = uniqueStrings(config.skills)
|
|
1566
|
+
const connectedSkillNames = uniqueStrings(connectedCapabilityNodeRefs.flatMap(ref => ref.skillNames || []))
|
|
1567
|
+
const effectiveSkills = uniqueStrings([...defaultSkills, ...configuredSkills, ...connectedSkillNames])
|
|
1568
|
+
const effectiveSkillGroups = effectiveSkillGroupsFor(connectedCapabilityNodeRefs)
|
|
1569
|
+
const connectedCapabilityRefs = [
|
|
1570
|
+
...capabilityRefsForSkills(configuredSkills),
|
|
1571
|
+
...connectedCapabilityNodeRefs,
|
|
1572
|
+
]
|
|
1573
|
+
const defaultCapabilityRefs = capabilityRefsForSkills(defaultSkills).map(ref => ({
|
|
1574
|
+
...ref,
|
|
1575
|
+
source: 'agent-default',
|
|
1576
|
+
attachment: 'agent-default',
|
|
1577
|
+
state: 'default',
|
|
1578
|
+
}))
|
|
1579
|
+
const availableOnDemandCapabilityRefs = capabilityRefsForSkills(availableOnDemandSkills).map(ref => ({
|
|
1580
|
+
...ref,
|
|
1581
|
+
source: 'agent-on-demand',
|
|
1582
|
+
attachment: 'on-demand',
|
|
1583
|
+
state: 'available',
|
|
1584
|
+
}))
|
|
1585
|
+
const ontology = buildAgentOntologyContext({
|
|
1586
|
+
nodeId,
|
|
1587
|
+
sessionId,
|
|
1588
|
+
agentKind: graphNode.agentKind || '',
|
|
1589
|
+
role: graphNode.role || '',
|
|
1590
|
+
connections,
|
|
1591
|
+
connectedResourceRefs,
|
|
1592
|
+
connectedAgentRefs,
|
|
1593
|
+
connectedEventRefs,
|
|
1594
|
+
connectedCapabilityRefs: [...defaultCapabilityRefs, ...availableOnDemandCapabilityRefs, ...connectedCapabilityRefs],
|
|
1595
|
+
connectedCapabilityNodeRefs,
|
|
1596
|
+
effectiveSkillGroups,
|
|
1597
|
+
connectedGoalRefs,
|
|
1598
|
+
effectiveSkills,
|
|
1599
|
+
isMainAgent,
|
|
1600
|
+
})
|
|
1601
|
+
const connectedPeers = connections.map(connection => {
|
|
1602
|
+
const ref = refs[connection.peerNodeId]
|
|
1603
|
+
const agentRef = connectedAgentRefs.find(item => item.nodeId === connection.peerNodeId)
|
|
1604
|
+
const eventRef = eventRefs[connection.peerNodeId]
|
|
1605
|
+
const capabilityRef = capabilityRefs[connection.peerNodeId]
|
|
1606
|
+
const goalRef = goalRefs[connection.peerNodeId]
|
|
1607
|
+
return agentRef
|
|
1608
|
+
? {
|
|
1609
|
+
nodeId: agentRef.nodeId,
|
|
1610
|
+
type: 'agent',
|
|
1611
|
+
title: agentRef.title,
|
|
1612
|
+
shortId: shortNodeId(agentRef.nodeId),
|
|
1613
|
+
displayName: nodeDisplayName(agentRef.title, agentRef.nodeId),
|
|
1614
|
+
sessionId: agentRef.sessionId,
|
|
1615
|
+
agentKind: agentRef.agentKind,
|
|
1616
|
+
status: agentRef.status,
|
|
1617
|
+
stateRef: agentRef.stateRef,
|
|
1618
|
+
}
|
|
1619
|
+
: (ref
|
|
1620
|
+
? {
|
|
1621
|
+
nodeId: connection.peerNodeId,
|
|
1622
|
+
type: ref.type,
|
|
1623
|
+
title: ref.title,
|
|
1624
|
+
shortId: shortNodeId(connection.peerNodeId),
|
|
1625
|
+
displayName: nodeDisplayName(ref.title, connection.peerNodeId),
|
|
1626
|
+
stateRef: { path: ref.statePath, revision: ref.revision },
|
|
1627
|
+
...(ref.file ? { file: ref.file } : {}),
|
|
1628
|
+
}
|
|
1629
|
+
: (eventRef
|
|
1630
|
+
? {
|
|
1631
|
+
nodeId: connection.peerNodeId,
|
|
1632
|
+
type: eventRef.type,
|
|
1633
|
+
eventKind: eventRef.eventKind,
|
|
1634
|
+
title: eventRef.title,
|
|
1635
|
+
shortId: shortNodeId(connection.peerNodeId),
|
|
1636
|
+
displayName: nodeDisplayName(eventRef.title, connection.peerNodeId),
|
|
1637
|
+
stateRef: { path: eventRef.statePath, revision: eventRef.revision },
|
|
1638
|
+
}
|
|
1639
|
+
: (capabilityRef
|
|
1640
|
+
? {
|
|
1641
|
+
nodeId: connection.peerNodeId,
|
|
1642
|
+
type: capabilityRef.type,
|
|
1643
|
+
capabilityKind: capabilityRef.capabilityKind,
|
|
1644
|
+
title: capabilityRef.title,
|
|
1645
|
+
shortId: shortNodeId(connection.peerNodeId),
|
|
1646
|
+
displayName: nodeDisplayName(capabilityRef.title, connection.peerNodeId),
|
|
1647
|
+
stateRef: { path: capabilityRef.statePath, revision: capabilityRef.revision },
|
|
1648
|
+
}
|
|
1649
|
+
: (goalRef
|
|
1650
|
+
? {
|
|
1651
|
+
nodeId: connection.peerNodeId,
|
|
1652
|
+
type: 'goal',
|
|
1653
|
+
taskId: goalRef.taskId,
|
|
1654
|
+
title: goalRef.title,
|
|
1655
|
+
shortId: shortNodeId(connection.peerNodeId),
|
|
1656
|
+
displayName: nodeDisplayName(goalRef.title, connection.peerNodeId),
|
|
1657
|
+
stateRef: { path: goalRef.statePath, revision: goalRef.revision },
|
|
1658
|
+
}
|
|
1659
|
+
: {
|
|
1660
|
+
nodeId: connection.peerNodeId,
|
|
1661
|
+
shortId: shortNodeId(connection.peerNodeId),
|
|
1662
|
+
displayName: nodeDisplayName('', connection.peerNodeId),
|
|
1663
|
+
}))))
|
|
1664
|
+
})
|
|
1665
|
+
const graphNodeIds = (Array.isArray(graph.nodes) ? graph.nodes : [])
|
|
1666
|
+
.map(node => node.nodeId || node.id)
|
|
1667
|
+
.filter(Boolean)
|
|
1668
|
+
const dockLinks = Array.isArray(graph.capsuleDockLinks) ? graph.capsuleDockLinks : []
|
|
1669
|
+
const magneticTopology = computeMagneticTopology(dockLinks, { nodeIds: graphNodeIds })
|
|
1670
|
+
const nodeMagnetic = magneticTopology.byNode[nodeId] || {
|
|
1671
|
+
magneticGroupId: null,
|
|
1672
|
+
directMagneticNeighbors: [],
|
|
1673
|
+
magneticReachableNodes: [],
|
|
1674
|
+
}
|
|
1675
|
+
const connectedNodeManuals = buildConnectedNodeManuals(projectRoot, connectedPeers)
|
|
1676
|
+
return {
|
|
1677
|
+
ok: true,
|
|
1678
|
+
node: buildAgentSnapshot(projectRoot, graphNode, graph),
|
|
1679
|
+
context: {
|
|
1680
|
+
nodeId,
|
|
1681
|
+
sessionId,
|
|
1682
|
+
identity: {
|
|
1683
|
+
...buildAgentIdentityContext(graphNode, nodeId, sessionId),
|
|
1684
|
+
// Role profile identity (spec §3.3/3.4, AC-002): the agent's own
|
|
1685
|
+
// profile wins; graph-node fields fall back for legacy sessions.
|
|
1686
|
+
displayName: roleProfile?.displayName || String(graphNode.displayName || graphNode.role || '').trim(),
|
|
1687
|
+
roleTitle: roleProfile?.roleTitle || String(graphNode.roleTitle || graphNode.role || '').trim(),
|
|
1688
|
+
responsibility: roleProfile?.responsibility || String(graphNode.responsibility || '').trim(),
|
|
1689
|
+
capabilities: roleProfile?.capabilities || (Array.isArray(graphNode.capabilities) ? graphNode.capabilities : []),
|
|
1690
|
+
roleProfileRef: roleProfile?.roleProfileRef || graphNode.roleProfileRef || null,
|
|
1691
|
+
},
|
|
1692
|
+
roleProfile,
|
|
1693
|
+
connectedNodeManuals,
|
|
1694
|
+
teamGuidance: TEAM_ORGANIZATION_GUIDANCE,
|
|
1695
|
+
workspace: buildAgentWorkspaceContext(projectRoot, graphNode, nodeId, sessionId),
|
|
1696
|
+
graphVersion: Number(graph.version || 1),
|
|
1697
|
+
subagentMode,
|
|
1698
|
+
connectedPeers,
|
|
1699
|
+
componentStateRefs: refs,
|
|
1700
|
+
eventStateRefs: eventRefs,
|
|
1701
|
+
capabilityStateRefs: capabilityRefs,
|
|
1702
|
+
goalStateRefs: goalRefs,
|
|
1703
|
+
connectedResourceRefs,
|
|
1704
|
+
connectedAgentRefs,
|
|
1705
|
+
connectedEventRefs,
|
|
1706
|
+
connectedCapabilityRefs,
|
|
1707
|
+
connectedCapabilityNodeRefs,
|
|
1708
|
+
effectiveSkillGroups,
|
|
1709
|
+
connectedGoalRefs,
|
|
1710
|
+
defaultSkills,
|
|
1711
|
+
defaultCapabilityRefs,
|
|
1712
|
+
availableOnDemandSkills,
|
|
1713
|
+
availableOnDemandCapabilityRefs,
|
|
1714
|
+
effectiveSkills,
|
|
1715
|
+
skillTriggers: skillTriggersFor([...effectiveSkills, ...availableOnDemandSkills]),
|
|
1716
|
+
ontology,
|
|
1717
|
+
affordances: ontology.affordances,
|
|
1718
|
+
skillPolicy: config.skillPolicy,
|
|
1719
|
+
outputRouting: outputRoutingFor(settings, connectedResourceRefs),
|
|
1720
|
+
availableActions: agentAvailableActions(projectRoot),
|
|
1721
|
+
magneticGroupId: nodeMagnetic.magneticGroupId,
|
|
1722
|
+
directMagneticNeighbors: nodeMagnetic.directMagneticNeighbors,
|
|
1723
|
+
magneticReachableNodes: nodeMagnetic.magneticReachableNodes,
|
|
1724
|
+
magneticTopology: {
|
|
1725
|
+
groups: magneticTopology.groups,
|
|
1726
|
+
groupCount: magneticTopology.groups.length,
|
|
1727
|
+
dockLinkCount: dockLinks.length,
|
|
1728
|
+
},
|
|
1729
|
+
},
|
|
1730
|
+
}
|
|
1731
|
+
}
|