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,46 @@
|
|
|
1
|
+
// spawn-gate.mjs
|
|
2
|
+
//
|
|
3
|
+
// Bounds concurrent PTY spawns. Each spawn boots a heavy CLI process
|
|
4
|
+
// (claude/codex/opencode — hundreds of MB each), and clients can request many
|
|
5
|
+
// starts at once (canvas auto-start, CLI batches, agent fan-out). A small
|
|
6
|
+
// sliding window keeps the machine from being stampeded: excess starts queue
|
|
7
|
+
// FIFO and enter as earlier spawns settle.
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_MAX_CONCURRENT_SPAWNS = 2;
|
|
10
|
+
export const DEFAULT_SPAWN_TIMEOUT_MS = 120_000;
|
|
11
|
+
|
|
12
|
+
export function createSpawnGate({
|
|
13
|
+
maxConcurrent = DEFAULT_MAX_CONCURRENT_SPAWNS,
|
|
14
|
+
timeoutMs = DEFAULT_SPAWN_TIMEOUT_MS,
|
|
15
|
+
} = {}) {
|
|
16
|
+
let active = 0;
|
|
17
|
+
const waiters = [];
|
|
18
|
+
|
|
19
|
+
const release = () => {
|
|
20
|
+
const next = waiters.shift();
|
|
21
|
+
if (next) next();
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return async function withSlot(fn) {
|
|
25
|
+
if (active >= maxConcurrent) {
|
|
26
|
+
await new Promise((resolve) => waiters.push(resolve));
|
|
27
|
+
}
|
|
28
|
+
active += 1;
|
|
29
|
+
let timer = null;
|
|
30
|
+
try {
|
|
31
|
+
// Deadlock breaker: a hung spawn must not hold the window forever. On
|
|
32
|
+
// timeout the caller gets an error and the slot frees; the abandoned
|
|
33
|
+
// spawn settles (or not) in the background.
|
|
34
|
+
return await Promise.race([
|
|
35
|
+
fn(),
|
|
36
|
+
new Promise((_, reject) => {
|
|
37
|
+
timer = setTimeout(() => reject(new Error(`PTY spawn timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
38
|
+
}),
|
|
39
|
+
]);
|
|
40
|
+
} finally {
|
|
41
|
+
if (timer) clearTimeout(timer);
|
|
42
|
+
active -= 1;
|
|
43
|
+
release();
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -1,6 +1,28 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
+
export const OPEN_TASK_STATUSES = new Set(['active', 'blocked']);
|
|
5
|
+
const STATUS_ALIASES = new Map([
|
|
6
|
+
['in-progress', 'active'], ['inprogress', 'active'], ['in_progress', 'active'],
|
|
7
|
+
['running', 'active'], ['pending', 'active'],
|
|
8
|
+
['needs-user-decision', 'blocked'], ['needs_user_decision', 'blocked'], ['failed', 'blocked'],
|
|
9
|
+
['complete', 'closed'], ['completed', 'closed'], ['verified', 'closed'], ['archived', 'closed'],
|
|
10
|
+
['abandoned', 'closed'], ['obsolete', 'closed'], ['done', 'closed'], ['closeout', 'closed'],
|
|
11
|
+
['skipped', 'closed'],
|
|
12
|
+
]);
|
|
13
|
+
// Only task-owning modes participate in durable WF lifecycle resume.
|
|
14
|
+
export const WF_MANAGED_MODES = new Set(['wf', 'wf-max']);
|
|
15
|
+
|
|
16
|
+
export function canonicalTaskStatus(value) {
|
|
17
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
18
|
+
return STATUS_ALIASES.get(raw) || (OPEN_TASK_STATUSES.has(raw) || raw === 'closed' ? raw : '');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function canonicalMode(value) {
|
|
22
|
+
const mode = String(value || '').trim().toLowerCase();
|
|
23
|
+
return mode || 'direct';
|
|
24
|
+
}
|
|
25
|
+
|
|
4
26
|
function readState(taskDir) {
|
|
5
27
|
const statePath = path.join(taskDir, 'STATE.json');
|
|
6
28
|
if (!fs.existsSync(statePath)) return null;
|
|
@@ -8,16 +30,56 @@ function readState(taskDir) {
|
|
|
8
30
|
catch (err) { throw new Error(`STATE.json at ${statePath} contains malformed JSON: ${err.message}`); }
|
|
9
31
|
}
|
|
10
32
|
|
|
33
|
+
function readJson(filePath) {
|
|
34
|
+
try {
|
|
35
|
+
if (!fs.existsSync(filePath)) return null;
|
|
36
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function pushRuntime(runtimes, runtime) {
|
|
43
|
+
const value = String(runtime || '').trim();
|
|
44
|
+
if (!value) return;
|
|
45
|
+
if (!runtimes.some(item => item.toLowerCase() === value.toLowerCase())) {
|
|
46
|
+
runtimes.push(value);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function runtimeHistoryForTask(state, taskDir) {
|
|
51
|
+
const runtimes = [];
|
|
52
|
+
for (const key of ['defaultRuntime', 'defaultAgentRuntime', 'agentRuntime', 'cliAgent']) {
|
|
53
|
+
pushRuntime(runtimes, state?.[key]);
|
|
54
|
+
}
|
|
55
|
+
for (const key of ['runtimeHistory', 'runtimes', 'terminalRuntimes']) {
|
|
56
|
+
if (Array.isArray(state?.[key])) {
|
|
57
|
+
for (const runtime of state[key]) pushRuntime(runtimes, runtime);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const sessionsDir = path.join(taskDir, 'sessions');
|
|
62
|
+
let entries = [];
|
|
63
|
+
try { entries = fs.readdirSync(sessionsDir, { withFileTypes: true }); }
|
|
64
|
+
catch { entries = []; }
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
if (!entry.isDirectory()) continue;
|
|
67
|
+
const sessionState = readJson(path.join(sessionsDir, entry.name, 'STATE.json'));
|
|
68
|
+
pushRuntime(runtimes, sessionState?.runtime);
|
|
69
|
+
}
|
|
70
|
+
return runtimes;
|
|
71
|
+
}
|
|
72
|
+
|
|
11
73
|
function normalizeAcceptance(acceptance) {
|
|
12
74
|
if (!Array.isArray(acceptance)) return [];
|
|
13
75
|
return acceptance.map((ac) => {
|
|
14
|
-
if (typeof ac === 'string') return { id:
|
|
76
|
+
if (typeof ac === 'string') return { id: ac, text: '', status: 'tracked' };
|
|
15
77
|
if (ac && typeof ac === 'object') return {
|
|
16
78
|
id: ac.id || '',
|
|
17
79
|
text: ac.text || JSON.stringify(ac),
|
|
18
|
-
status: ac.status || '
|
|
80
|
+
status: ac.status || 'tracked',
|
|
19
81
|
};
|
|
20
|
-
return { id:
|
|
82
|
+
return { id: String(ac), text: '', status: 'tracked' };
|
|
21
83
|
});
|
|
22
84
|
}
|
|
23
85
|
|
|
@@ -28,6 +90,8 @@ export function parseTaskCapsule(taskDir) {
|
|
|
28
90
|
throw new Error(`STATE.json at ${taskDir} is missing required field: schemaVersion`);
|
|
29
91
|
}
|
|
30
92
|
const links = state.links || { dependsOn: [], blocks: [], related: [] };
|
|
93
|
+
const mode = canonicalMode(state.mode);
|
|
94
|
+
const status = canonicalTaskStatus(state.status) || 'active';
|
|
31
95
|
return {
|
|
32
96
|
taskId: state.taskId || path.basename(taskDir),
|
|
33
97
|
status: state.status || 'unknown',
|
|
@@ -35,10 +99,19 @@ export function parseTaskCapsule(taskDir) {
|
|
|
35
99
|
gate: state.gate || null,
|
|
36
100
|
tier: state.tier || null,
|
|
37
101
|
mode: state.mode || null,
|
|
102
|
+
project: String(state.project || state.group || 'default').trim() || 'default',
|
|
103
|
+
group: state.group || null,
|
|
104
|
+
tags: Array.isArray(state.tags) ? state.tags.map(tag => String(tag).trim()).filter(Boolean) : [],
|
|
105
|
+
createdAt: state.createdAt || null,
|
|
106
|
+
startedAt: state.startedAt || null,
|
|
38
107
|
updatedAt: state.updatedAt || null,
|
|
108
|
+
closedAt: state.closedAt || null,
|
|
109
|
+
wfManaged: WF_MANAGED_MODES.has(mode),
|
|
110
|
+
resumeRequired: WF_MANAGED_MODES.has(mode) && OPEN_TASK_STATUSES.has(status),
|
|
39
111
|
activeQuestion: state.activeQuestion || null,
|
|
40
112
|
nextAction: state.nextAction || null,
|
|
41
113
|
defaultRuntime: state.defaultRuntime || state.defaultAgentRuntime || state.agentRuntime || state.cliAgent || null,
|
|
114
|
+
runtimeHistory: runtimeHistoryForTask(state, taskDir),
|
|
42
115
|
acceptance: normalizeAcceptance(state.acceptance),
|
|
43
116
|
dependsOn: Array.isArray(links.dependsOn) ? links.dependsOn : [],
|
|
44
117
|
blocks: Array.isArray(links.blocks) ? links.blocks : [],
|
|
@@ -67,6 +140,18 @@ export function parseTaskList(tasksRoot) {
|
|
|
67
140
|
return capsules;
|
|
68
141
|
}
|
|
69
142
|
|
|
143
|
+
/** Read the single durable Harness focus pointer without selecting a task. */
|
|
144
|
+
export function readActiveTaskId(projectRoot) {
|
|
145
|
+
try {
|
|
146
|
+
const content = fs.readFileSync(path.join(projectRoot, 'Harness', 'PROGRESS.md'), 'utf8');
|
|
147
|
+
const match = content.match(/## Active Task\s+^- ([^\r\n]+)/m);
|
|
148
|
+
const taskId = match?.[1]?.trim() || '';
|
|
149
|
+
return taskId && !/^none$/i.test(taskId) ? taskId : null;
|
|
150
|
+
} catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
70
155
|
/** Parse archived tasks from _archive/YYYY/task-name/ */
|
|
71
156
|
export function parseArchivedTasks(tasksRoot) {
|
|
72
157
|
const archiveRoot = path.join(tasksRoot, '_archive');
|
|
@@ -3,6 +3,8 @@ import path from 'node:path';
|
|
|
3
3
|
import { validateTaskId } from './security.mjs';
|
|
4
4
|
|
|
5
5
|
const MAX_READ_LIMIT = 2000;
|
|
6
|
+
const MAX_DROPPED_FILES = 12;
|
|
7
|
+
const MAX_DROPPED_FILE_BYTES = 10 * 1024 * 1024;
|
|
6
8
|
|
|
7
9
|
function validateSessionId(sessionId) {
|
|
8
10
|
return validateTaskId(sessionId);
|
|
@@ -56,6 +58,83 @@ function appendJsonl(filePath, data) {
|
|
|
56
58
|
fs.appendFileSync(filePath, `${JSON.stringify(data)}\n`, 'utf8');
|
|
57
59
|
}
|
|
58
60
|
|
|
61
|
+
// ── In-memory session index ──────────────────────────────────────────────────
|
|
62
|
+
// Maintained by every persistSession/appendTerminalData call and rebuilt once at
|
|
63
|
+
// backend startup. Snapshot fingerprinting (P1a) and the startup orphan
|
|
64
|
+
// downgrade (P1b) read this instead of scanning every session dir per request.
|
|
65
|
+
const sessionIndex = new Map();
|
|
66
|
+
|
|
67
|
+
export function touchSessionIndex(record, { activityOnly = false } = {}) {
|
|
68
|
+
if (!record?.sessionId) return;
|
|
69
|
+
const entry = sessionIndex.get(record.sessionId) || {};
|
|
70
|
+
// activityOnly (terminal chunks/flushes) updates seq but NOT updatedAt: the
|
|
71
|
+
// snapshot fingerprint must not churn with streamed output, or every 5s
|
|
72
|
+
// poll rebuilds the snapshot and re-renders the canvas while any terminal
|
|
73
|
+
// is active.
|
|
74
|
+
const updatedAt = activityOnly
|
|
75
|
+
? (entry.updatedAt || new Date().toISOString())
|
|
76
|
+
: (record.updatedAt ?? entry.updatedAt ?? new Date().toISOString());
|
|
77
|
+
sessionIndex.set(record.sessionId, {
|
|
78
|
+
...entry,
|
|
79
|
+
sessionId: record.sessionId,
|
|
80
|
+
taskId: record.taskId ?? entry.taskId ?? null,
|
|
81
|
+
runtime: record.runtime ?? entry.runtime,
|
|
82
|
+
status: record.status ?? entry.status,
|
|
83
|
+
updatedAt,
|
|
84
|
+
terminalSeq: Number(record.terminalSeq ?? entry.terminalSeq ?? 0),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function getSessionIndexSummary() {
|
|
89
|
+
let latestUpdatedAt = '';
|
|
90
|
+
for (const entry of sessionIndex.values()) {
|
|
91
|
+
if (String(entry.updatedAt || '') > latestUpdatedAt) latestUpdatedAt = String(entry.updatedAt);
|
|
92
|
+
}
|
|
93
|
+
return { count: sessionIndex.size, latestUpdatedAt };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function getSessionIndexRecord(sessionId) {
|
|
97
|
+
return sessionIndex.get(sessionId) || null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Rebuilds the index with a single scan of all session dirs. Called once at
|
|
101
|
+
// backend startup; per-request scans are gone.
|
|
102
|
+
export function buildSessionIndex(projectRoot) {
|
|
103
|
+
sessionIndex.clear();
|
|
104
|
+
for (const found of allSessionDirs(projectRoot)) {
|
|
105
|
+
const state = readJson(path.join(found.dir, 'STATE.json'), null);
|
|
106
|
+
if (state) touchSessionIndex({ ...state, taskId: found.taskId });
|
|
107
|
+
}
|
|
108
|
+
return sessionIndex.size;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── One-time startup migration: orphaned live sessions ───────────────────────
|
|
112
|
+
// At backend startup the in-memory registry manages nothing, so every disk
|
|
113
|
+
// record with a live status is orphaned by definition (its PTY died with the
|
|
114
|
+
// previous server). Persist the downgrade so snapshot, delete guards, and the
|
|
115
|
+
// UI all see one consistent truth instead of "fake-live" sessions.
|
|
116
|
+
export function downgradeOrphanedDiskSessions(projectRoot) {
|
|
117
|
+
const downgraded = [];
|
|
118
|
+
for (const found of allSessionDirs(projectRoot)) {
|
|
119
|
+
const state = readJson(path.join(found.dir, 'STATE.json'), null);
|
|
120
|
+
if (!state || !['running', 'starting'].includes(state.status)) continue;
|
|
121
|
+
const updated = {
|
|
122
|
+
...state,
|
|
123
|
+
status: 'stopped',
|
|
124
|
+
blockedReason: state.blockedReason || 'not-managed-by-current-wf-ui',
|
|
125
|
+
orphanedAt: new Date().toISOString(),
|
|
126
|
+
updatedAt: new Date().toISOString(),
|
|
127
|
+
// No clients can be attached right after a restart.
|
|
128
|
+
wsClientCount: 0,
|
|
129
|
+
attachMode: false,
|
|
130
|
+
};
|
|
131
|
+
writeJson(path.join(found.dir, 'STATE.json'), updated);
|
|
132
|
+
touchSessionIndex({ ...updated, taskId: found.taskId });
|
|
133
|
+
downgraded.push(found.sessionId);
|
|
134
|
+
}
|
|
135
|
+
return downgraded;
|
|
136
|
+
}
|
|
137
|
+
|
|
59
138
|
export function persistSession(projectRoot, session, patch = {}) {
|
|
60
139
|
const dir = sessionDir(projectRoot, session.taskId, session.sessionId);
|
|
61
140
|
const current = readJson(path.join(dir, 'STATE.json'), {});
|
|
@@ -66,6 +145,7 @@ export function persistSession(projectRoot, session, patch = {}) {
|
|
|
66
145
|
updatedAt: new Date().toISOString(),
|
|
67
146
|
};
|
|
68
147
|
writeJson(path.join(dir, 'STATE.json'), state);
|
|
148
|
+
touchSessionIndex(state);
|
|
69
149
|
return state;
|
|
70
150
|
}
|
|
71
151
|
|
|
@@ -80,11 +160,51 @@ export function appendSessionEvent(projectRoot, session, event) {
|
|
|
80
160
|
});
|
|
81
161
|
}
|
|
82
162
|
|
|
83
|
-
|
|
84
|
-
|
|
163
|
+
// ── Batched terminal writes ──────────────────────────────────────────────────
|
|
164
|
+
// Per-chunk sync file I/O (read STATE.json + append terminal.jsonl + rewrite
|
|
165
|
+
// STATE.json per chunk) saturated the event loop when many terminals streamed
|
|
166
|
+
// at once. Entries now buffer per session and flush as ONE append + ONE state
|
|
167
|
+
// write every 500ms (or at 64 entries / 64KB, whichever first). Readers flush
|
|
168
|
+
// before reading, so disk reads always see the latest entry.
|
|
169
|
+
const TERMINAL_FLUSH_DELAY_MS = 500;
|
|
170
|
+
const TERMINAL_FLUSH_MAX_ENTRIES = 64;
|
|
171
|
+
const TERMINAL_FLUSH_MAX_BYTES = 64 * 1024;
|
|
172
|
+
const terminalFlushTimers = new Map();
|
|
173
|
+
const terminalBuffers = new Map();
|
|
174
|
+
|
|
175
|
+
export function flushTerminalBuffer(projectRoot, sessionId) {
|
|
176
|
+
const pending = terminalBuffers.get(sessionId);
|
|
177
|
+
if (!pending) return 0;
|
|
178
|
+
terminalBuffers.delete(sessionId);
|
|
179
|
+
const timer = terminalFlushTimers.get(sessionId);
|
|
180
|
+
if (timer) {
|
|
181
|
+
clearTimeout(timer);
|
|
182
|
+
terminalFlushTimers.delete(sessionId);
|
|
183
|
+
}
|
|
184
|
+
const dir = sessionDir(projectRoot, pending.taskId, sessionId);
|
|
85
185
|
const statePath = path.join(dir, 'STATE.json');
|
|
86
|
-
const state = readJson(statePath, session);
|
|
87
|
-
const
|
|
186
|
+
const state = readJson(statePath, pending.session);
|
|
187
|
+
const lines = pending.entries.map((entry) => `${JSON.stringify(entry)}\n`).join('');
|
|
188
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
189
|
+
fs.appendFileSync(path.join(dir, 'terminal.jsonl'), lines, 'utf8');
|
|
190
|
+
const last = pending.entries[pending.entries.length - 1];
|
|
191
|
+
const updatedState = {
|
|
192
|
+
...state,
|
|
193
|
+
terminalSeq: last.seq,
|
|
194
|
+
lastActivityAt: last.ts,
|
|
195
|
+
updatedAt: last.ts,
|
|
196
|
+
};
|
|
197
|
+
writeJson(statePath, updatedState);
|
|
198
|
+
touchSessionIndex(updatedState, { activityOnly: true });
|
|
199
|
+
return pending.entries.length;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function appendTerminalData(projectRoot, session, data, stream = 'stdout') {
|
|
203
|
+
const indexRecord = getSessionIndexRecord(session.sessionId);
|
|
204
|
+
const baseSeq = indexRecord
|
|
205
|
+
? Number(indexRecord.terminalSeq || 0)
|
|
206
|
+
: Number(readJson(path.join(sessionDir(projectRoot, session.taskId, session.sessionId), 'STATE.json'), {}).terminalSeq || 0);
|
|
207
|
+
const nextSeq = baseSeq + 1;
|
|
88
208
|
const entry = {
|
|
89
209
|
seq: nextSeq,
|
|
90
210
|
ts: new Date().toISOString(),
|
|
@@ -94,13 +214,27 @@ export function appendTerminalData(projectRoot, session, data, stream = 'stdout'
|
|
|
94
214
|
stream,
|
|
95
215
|
data: String(data || ''),
|
|
96
216
|
};
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
217
|
+
const pending = terminalBuffers.get(session.sessionId) || {
|
|
218
|
+
taskId: session.taskId,
|
|
219
|
+
session,
|
|
220
|
+
entries: [],
|
|
221
|
+
bytes: 0,
|
|
222
|
+
};
|
|
223
|
+
pending.entries.push(entry);
|
|
224
|
+
pending.bytes += entry.data.length;
|
|
225
|
+
terminalBuffers.set(session.sessionId, pending);
|
|
226
|
+
touchSessionIndex({ ...session, terminalSeq: nextSeq, updatedAt: entry.ts }, { activityOnly: true });
|
|
227
|
+
|
|
228
|
+
if (pending.entries.length >= TERMINAL_FLUSH_MAX_ENTRIES || pending.bytes >= TERMINAL_FLUSH_MAX_BYTES) {
|
|
229
|
+
flushTerminalBuffer(projectRoot, session.sessionId);
|
|
230
|
+
} else if (!terminalFlushTimers.has(session.sessionId)) {
|
|
231
|
+
const timer = setTimeout(() => {
|
|
232
|
+
terminalFlushTimers.delete(session.sessionId);
|
|
233
|
+
flushTerminalBuffer(projectRoot, session.sessionId);
|
|
234
|
+
}, TERMINAL_FLUSH_DELAY_MS);
|
|
235
|
+
timer.unref?.();
|
|
236
|
+
terminalFlushTimers.set(session.sessionId, timer);
|
|
237
|
+
}
|
|
104
238
|
return entry;
|
|
105
239
|
}
|
|
106
240
|
|
|
@@ -165,6 +299,8 @@ export function findTerminalSession(projectRoot, sessionId) {
|
|
|
165
299
|
}
|
|
166
300
|
|
|
167
301
|
export function readTerminalRange(projectRoot, { sessionId, fromSeq, toSeq, tail } = {}) {
|
|
302
|
+
// Disk reads always see the latest entry: flush the pending buffer first.
|
|
303
|
+
flushTerminalBuffer(projectRoot, sessionId);
|
|
168
304
|
const found = findTerminalSession(projectRoot, sessionId);
|
|
169
305
|
if (!found) return { sessionId, entries: [] };
|
|
170
306
|
let entries = readJsonl(path.join(found.dir, 'terminal.jsonl'));
|
|
@@ -223,3 +359,81 @@ export function recordInputRequest(projectRoot, sessionId, text) {
|
|
|
223
359
|
appendJsonl(path.join(found.dir, 'input-requests.jsonl'), event);
|
|
224
360
|
return event;
|
|
225
361
|
}
|
|
362
|
+
|
|
363
|
+
export function writeDroppedTerminalFiles(projectRoot, sessionId, files = []) {
|
|
364
|
+
const found = findTerminalSession(projectRoot, sessionId);
|
|
365
|
+
if (!found) {
|
|
366
|
+
const err = new Error(`Session not found: ${sessionId}`);
|
|
367
|
+
err.code = 'NOT_FOUND';
|
|
368
|
+
throw err;
|
|
369
|
+
}
|
|
370
|
+
if (!Array.isArray(files)) throw new Error('files must be an array');
|
|
371
|
+
if (files.length > MAX_DROPPED_FILES) throw new Error(`Too many dropped files; max ${MAX_DROPPED_FILES}`);
|
|
372
|
+
|
|
373
|
+
const dropRoot = path.join(found.dir, 'drops');
|
|
374
|
+
fs.mkdirSync(dropRoot, { recursive: true });
|
|
375
|
+
|
|
376
|
+
const written = files.map((file, index) => {
|
|
377
|
+
const name = safeDropFileName(file?.name, index);
|
|
378
|
+
const buffer = decodeDropContent(file?.contentBase64);
|
|
379
|
+
if (buffer.byteLength > MAX_DROPPED_FILE_BYTES) {
|
|
380
|
+
throw new Error(`Dropped file is too large: ${name}`);
|
|
381
|
+
}
|
|
382
|
+
const targetPath = uniqueDropPath(dropRoot, name);
|
|
383
|
+
fs.writeFileSync(targetPath, buffer);
|
|
384
|
+
return {
|
|
385
|
+
name: path.basename(targetPath),
|
|
386
|
+
path: targetPath,
|
|
387
|
+
size: buffer.byteLength,
|
|
388
|
+
terminalText: quoteTerminalPath(targetPath),
|
|
389
|
+
};
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
const state = readJson(path.join(found.dir, 'STATE.json'), { sessionId, taskId: found.taskId });
|
|
393
|
+
appendSessionEvent(projectRoot, state, {
|
|
394
|
+
type: 'terminal.files.dropped',
|
|
395
|
+
count: written.length,
|
|
396
|
+
files: written.map(file => ({ name: file.name, path: file.path, size: file.size })),
|
|
397
|
+
});
|
|
398
|
+
return {
|
|
399
|
+
files: written,
|
|
400
|
+
terminalInput: written.map(file => file.terminalText).join(' '),
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function safeDropFileName(value, index) {
|
|
405
|
+
const raw = String(value || `dropped-file-${index + 1}`);
|
|
406
|
+
const basename = raw.split(/[\\/]+/).pop() || `dropped-file-${index + 1}`;
|
|
407
|
+
const cleaned = basename
|
|
408
|
+
.replace(/[<>:"/\\|?*\x00-\x1f]/g, '_')
|
|
409
|
+
.replace(/^\.+/, '')
|
|
410
|
+
.trim();
|
|
411
|
+
return (cleaned || `dropped-file-${index + 1}`).slice(0, 160);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function decodeDropContent(value) {
|
|
415
|
+
const encoded = String(value || '');
|
|
416
|
+
if (encoded.length > Math.ceil(MAX_DROPPED_FILE_BYTES * 1.4)) {
|
|
417
|
+
throw new Error('Dropped file payload is too large');
|
|
418
|
+
}
|
|
419
|
+
return Buffer.from(encoded, 'base64');
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function uniqueDropPath(dropRoot, filename) {
|
|
423
|
+
const ext = path.extname(filename);
|
|
424
|
+
const stem = path.basename(filename, ext) || 'dropped-file';
|
|
425
|
+
let candidate = path.join(dropRoot, filename);
|
|
426
|
+
for (let i = 2; fs.existsSync(candidate); i++) {
|
|
427
|
+
candidate = path.join(dropRoot, `${stem}-${i}${ext}`);
|
|
428
|
+
}
|
|
429
|
+
const resolvedRoot = path.resolve(dropRoot);
|
|
430
|
+
const resolvedCandidate = path.resolve(candidate);
|
|
431
|
+
if (resolvedCandidate !== resolvedRoot && !resolvedCandidate.startsWith(`${resolvedRoot}${path.sep}`)) {
|
|
432
|
+
throw new Error('Dropped file path escapes drop directory');
|
|
433
|
+
}
|
|
434
|
+
return resolvedCandidate;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function quoteTerminalPath(filePath) {
|
|
438
|
+
return `'${String(filePath).replace(/'/g, "''")}'`;
|
|
439
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { fire as fireTimer, dispatchWakeup as dispatchTimerWakeup } from './workflow-node-types/timer-node.mjs'
|
|
2
|
+
import { getEventNode, listEventNodes, updateEventNode } from './workflow-event-node-store.mjs'
|
|
3
|
+
|
|
4
|
+
// Bounded timer wakeup scheduler (D1, M5, AC-011/AC-012, T10/T11 backend part).
|
|
5
|
+
//
|
|
6
|
+
// The loop is bounded, never resident:
|
|
7
|
+
// - it runs only while at least one enabled timer exists (syncTimerScheduler
|
|
8
|
+
// starts/stops it from event-node mutations, and the tick self-stops when
|
|
9
|
+
// getTimers() returns no enabled timers);
|
|
10
|
+
// - the interval handle is unref'd, so it never keeps a closed server
|
|
11
|
+
// process alive;
|
|
12
|
+
// - stopTimerScheduler() clears the handle; server.mjs stopServer() invokes
|
|
13
|
+
// it on server close, and tests call it directly on teardown.
|
|
14
|
+
//
|
|
15
|
+
// Each tick: for every enabled timer whose heartbeat.base.nextDueAt is due,
|
|
16
|
+
// run the timer.fire flow, then dispatch a wakeup envelope into the message
|
|
17
|
+
// queues of the connected/magnetic-group agent nodes (never PTY stdin).
|
|
18
|
+
|
|
19
|
+
const DEFAULT_INTERVAL_MS = 1000
|
|
20
|
+
|
|
21
|
+
let activeHandle = null
|
|
22
|
+
|
|
23
|
+
// Set while the scheduler itself performs a store update (nextDueAt advance),
|
|
24
|
+
// so event-node-store mutations made by the scheduler do not re-sync/re-arm
|
|
25
|
+
// the loop and clobber the caller-chosen intervalMs config.
|
|
26
|
+
let suppressingSync = false
|
|
27
|
+
|
|
28
|
+
function parseTime(value) {
|
|
29
|
+
const time = Date.parse(String(value || ''))
|
|
30
|
+
return Number.isFinite(time) ? time : 0
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function clampInteger(value, { min = 0, max = Number.MAX_SAFE_INTEGER, fallback = 0 } = {}) {
|
|
34
|
+
const number = Number(value)
|
|
35
|
+
if (!Number.isFinite(number)) return fallback
|
|
36
|
+
return Math.max(min, Math.min(max, Math.floor(number)))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Builds the due-check data + bound fire flow for every enabled timer in the
|
|
40
|
+
// project. Read-only apart from the `fire` closure, which is invoked by the
|
|
41
|
+
// scheduler tick and advances the runtime clock after firing.
|
|
42
|
+
//
|
|
43
|
+
// Bounded loop semantics (F14/D11): a timer whose `loop.maxIterations` is set
|
|
44
|
+
// stops advancing once `loop.runCount` reaches it — the entry is skipped and
|
|
45
|
+
// the loop is marked complete (loop.enabled=false) through the event-node
|
|
46
|
+
// store API, never by mutating state files directly. `loop.stopOnFailure`
|
|
47
|
+
// marks the loop complete when a fire throws. Timers without a bounded loop
|
|
48
|
+
// keep the existing interval behavior.
|
|
49
|
+
function enabledTimerEntries(projectRoot) {
|
|
50
|
+
const entries = []
|
|
51
|
+
for (const node of listEventNodes(projectRoot)) {
|
|
52
|
+
if (String(node.type || '') !== 'timer') continue
|
|
53
|
+
const timerNodeId = node.nodeId
|
|
54
|
+
const current = getEventNode(projectRoot, timerNodeId)
|
|
55
|
+
const state = current.state
|
|
56
|
+
if (!state.enabled) continue
|
|
57
|
+
const loop = state.loop || {}
|
|
58
|
+
const maxIterations = clampInteger(loop.maxIterations, { min: 0, fallback: 0 })
|
|
59
|
+
const runCount = clampInteger(loop.runCount, { min: 0, fallback: 0 })
|
|
60
|
+
if (maxIterations > 0 && runCount >= maxIterations) continue // loop exhausted — stop advancing (F14)
|
|
61
|
+
const base = state.heartbeat?.base || {}
|
|
62
|
+
const nextDueAt = String(base.nextDueAt || '')
|
|
63
|
+
const dueAt = parseTime(nextDueAt)
|
|
64
|
+
if (!dueAt) continue // base lane not armed (disabled or not running)
|
|
65
|
+
const intervalSeconds = clampInteger(
|
|
66
|
+
base.intervalSeconds ?? state.schedule?.intervalSeconds,
|
|
67
|
+
{ min: 1, fallback: 60 },
|
|
68
|
+
)
|
|
69
|
+
const mode = String(state.schedule?.mode || 'manual').toLowerCase()
|
|
70
|
+
const stopOnFailure = loop.stopOnFailure !== false
|
|
71
|
+
entries.push({
|
|
72
|
+
timerNodeId,
|
|
73
|
+
projectRoot,
|
|
74
|
+
enabled: true,
|
|
75
|
+
nextDueAt,
|
|
76
|
+
dueAt,
|
|
77
|
+
intervalSeconds,
|
|
78
|
+
mode,
|
|
79
|
+
fire: (firedAt) => {
|
|
80
|
+
try {
|
|
81
|
+
const fired = fireTimer(timerNodeId, projectRoot, {})
|
|
82
|
+
const heartbeat = current.state.heartbeat || {}
|
|
83
|
+
const nextRunCount = maxIterations > 0 ? runCount + 1 : runCount
|
|
84
|
+
const loopComplete = maxIterations > 0 && nextRunCount >= maxIterations
|
|
85
|
+
const advancePatch = {
|
|
86
|
+
revision: fired.revision,
|
|
87
|
+
heartbeat: {
|
|
88
|
+
...heartbeat,
|
|
89
|
+
base: { ...(heartbeat.base || {}), nextDueAt: new Date(parseTime(firedAt) + intervalSeconds * 1000).toISOString() },
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
if (maxIterations > 0) {
|
|
93
|
+
advancePatch.loop = {
|
|
94
|
+
...loop,
|
|
95
|
+
runCount: nextRunCount,
|
|
96
|
+
// Loop-complete marker persisted through the store API (F14).
|
|
97
|
+
enabled: loopComplete ? false : loop.enabled,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (mode === 'once') advancePatch.enabled = false
|
|
101
|
+
suppressingSync = true
|
|
102
|
+
try {
|
|
103
|
+
updateEventNode(projectRoot, timerNodeId, advancePatch)
|
|
104
|
+
} finally {
|
|
105
|
+
suppressingSync = false
|
|
106
|
+
}
|
|
107
|
+
return fired
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (stopOnFailure && maxIterations > 0) {
|
|
110
|
+
try {
|
|
111
|
+
const failed = getEventNode(projectRoot, timerNodeId)
|
|
112
|
+
suppressingSync = true
|
|
113
|
+
try {
|
|
114
|
+
updateEventNode(projectRoot, timerNodeId, {
|
|
115
|
+
revision: failed.node.revision,
|
|
116
|
+
loop: { ...(failed.state.loop || {}), enabled: false },
|
|
117
|
+
})
|
|
118
|
+
} finally {
|
|
119
|
+
suppressingSync = false
|
|
120
|
+
}
|
|
121
|
+
} catch {
|
|
122
|
+
// A failed loop-complete marker must never crash the server loop.
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
throw error
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
return entries
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function runTick({ getTimers, dispatchWakeup }) {
|
|
134
|
+
let timers
|
|
135
|
+
try {
|
|
136
|
+
timers = Array.isArray(getTimers()) ? getTimers() : []
|
|
137
|
+
} catch {
|
|
138
|
+
return
|
|
139
|
+
}
|
|
140
|
+
if (timers.length === 0) {
|
|
141
|
+
stopTimerScheduler()
|
|
142
|
+
return
|
|
143
|
+
}
|
|
144
|
+
const now = Date.now()
|
|
145
|
+
for (const timer of timers) {
|
|
146
|
+
if (!timer.enabled || !timer.dueAt || timer.dueAt > now) continue
|
|
147
|
+
const firedAt = new Date().toISOString()
|
|
148
|
+
const scheduledAt = timer.nextDueAt
|
|
149
|
+
let fired = null
|
|
150
|
+
try {
|
|
151
|
+
if (typeof timer.fire === 'function') fired = timer.fire(firedAt)
|
|
152
|
+
} catch {
|
|
153
|
+
// A failed fire must never crash the server loop; the next tick retries.
|
|
154
|
+
continue
|
|
155
|
+
}
|
|
156
|
+
if (!fired) continue
|
|
157
|
+
try {
|
|
158
|
+
dispatchWakeup({
|
|
159
|
+
projectRoot: timer.projectRoot,
|
|
160
|
+
timerNodeId: timer.timerNodeId,
|
|
161
|
+
scheduledAt,
|
|
162
|
+
firedAt,
|
|
163
|
+
intervalSeconds: timer.intervalSeconds,
|
|
164
|
+
})
|
|
165
|
+
} catch {
|
|
166
|
+
// Wakeup delivery must never crash the server loop.
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function startTimerScheduler({ getTimers, dispatchWakeup, intervalMs = DEFAULT_INTERVAL_MS } = {}) {
|
|
172
|
+
if (typeof getTimers !== 'function' || typeof dispatchWakeup !== 'function') return null
|
|
173
|
+
stopTimerScheduler()
|
|
174
|
+
const interval = setInterval(() => runTick({ getTimers, dispatchWakeup }), Math.max(Number(intervalMs) || DEFAULT_INTERVAL_MS, 5))
|
|
175
|
+
interval.unref?.()
|
|
176
|
+
activeHandle = { interval, getTimers, dispatchWakeup, intervalMs: Math.max(Number(intervalMs) || DEFAULT_INTERVAL_MS, 5) }
|
|
177
|
+
return activeHandle
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function stopTimerScheduler() {
|
|
181
|
+
if (!activeHandle) return { stopped: true, wasActive: false }
|
|
182
|
+
clearInterval(activeHandle.interval)
|
|
183
|
+
activeHandle = null
|
|
184
|
+
return { stopped: true, wasActive: true }
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function isTimerSchedulerActive() {
|
|
188
|
+
return activeHandle !== null
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function isTimerSchedulerSyncSuppressed() {
|
|
192
|
+
return suppressingSync
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Hook wired by workflow-event-node-store.mjs on event-node create/update/
|
|
196
|
+
// delete/restore. Starts the bounded loop when any enabled timer exists,
|
|
197
|
+
// stops it when none remain, and keeps the running configuration intact when
|
|
198
|
+
// nothing changed (so scheduler-internal store updates do not re-arm the loop).
|
|
199
|
+
export function syncTimerScheduler(projectRoot, opts = {}) {
|
|
200
|
+
if (suppressingSync) return { active: isTimerSchedulerActive() }
|
|
201
|
+
const intervalMs = Number(opts.intervalMs ?? DEFAULT_INTERVAL_MS)
|
|
202
|
+
let timers = []
|
|
203
|
+
try {
|
|
204
|
+
timers = enabledTimerEntries(projectRoot)
|
|
205
|
+
} catch {
|
|
206
|
+
return { active: isTimerSchedulerActive() }
|
|
207
|
+
}
|
|
208
|
+
if (timers.length === 0) {
|
|
209
|
+
stopTimerScheduler()
|
|
210
|
+
return { active: false, timers: 0 }
|
|
211
|
+
}
|
|
212
|
+
if (
|
|
213
|
+
activeHandle
|
|
214
|
+
&& activeHandle.projectRoot === projectRoot
|
|
215
|
+
&& Number(activeHandle.intervalMs) === intervalMs
|
|
216
|
+
) {
|
|
217
|
+
return { active: true, timers: timers.length }
|
|
218
|
+
}
|
|
219
|
+
const handle = startTimerScheduler({
|
|
220
|
+
getTimers: () => enabledTimerEntries(projectRoot),
|
|
221
|
+
dispatchWakeup: (envelope) => dispatchTimerWakeup(envelope.timerNodeId, envelope.projectRoot, envelope),
|
|
222
|
+
intervalMs,
|
|
223
|
+
})
|
|
224
|
+
if (handle) handle.projectRoot = projectRoot
|
|
225
|
+
return { active: isTimerSchedulerActive(), timers: timers.length }
|
|
226
|
+
}
|