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.
Files changed (656) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README-CN.md +4 -2
  3. package/README.md +23 -4
  4. package/package.json +17 -2
  5. package/src/generator.js +19 -6
  6. package/src/index.js +531 -38
  7. package/src/prompts.js +4 -4
  8. package/src/ui/dist/assets/AgentsRoute-BGGs90wH.js +7 -0
  9. package/src/ui/dist/assets/Assistant-Bold-gm-uSS1B.woff2 +0 -0
  10. package/src/ui/dist/assets/Assistant-Medium-DrcxCXg3.woff2 +0 -0
  11. package/src/ui/dist/assets/Assistant-Regular-DVxZuzxb.woff2 +0 -0
  12. package/src/ui/dist/assets/Assistant-SemiBold-SCI4bEL9.woff2 +0 -0
  13. package/src/ui/dist/assets/Combination-DhBLEmof.js +42 -0
  14. package/src/ui/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  15. package/src/ui/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  16. package/src/ui/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  17. package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  18. package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  19. package/src/ui/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  20. package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  21. package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  22. package/src/ui/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  23. package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  24. package/src/ui/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  25. package/src/ui/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  26. package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  27. package/src/ui/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  28. package/src/ui/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  29. package/src/ui/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  30. package/src/ui/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  31. package/src/ui/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  32. package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  33. package/src/ui/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  34. package/src/ui/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  35. package/src/ui/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  36. package/src/ui/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  37. package/src/ui/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  38. package/src/ui/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  39. package/src/ui/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  40. package/src/ui/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  41. package/src/ui/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  42. package/src/ui/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  43. package/src/ui/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  44. package/src/ui/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  45. package/src/ui/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  46. package/src/ui/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  47. package/src/ui/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  48. package/src/ui/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  49. package/src/ui/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  50. package/src/ui/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  51. package/src/ui/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  52. package/src/ui/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  53. package/src/ui/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  54. package/src/ui/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  55. package/src/ui/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  56. package/src/ui/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  57. package/src/ui/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  58. package/src/ui/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  59. package/src/ui/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  60. package/src/ui/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  61. package/src/ui/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  62. package/src/ui/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  63. package/src/ui/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  64. package/src/ui/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  65. package/src/ui/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  66. package/src/ui/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  67. package/src/ui/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  68. package/src/ui/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  69. package/src/ui/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  70. package/src/ui/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  71. package/src/ui/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  72. package/src/ui/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  73. package/src/ui/dist/assets/RolesRoute-DnodCSz0.js +2 -0
  74. package/src/ui/dist/assets/RuntimePicker-CucD8TGn.js +7 -0
  75. package/src/ui/dist/assets/SettingsRoute-DquMWjac.js +2 -0
  76. package/src/ui/dist/assets/TaskList-Bxc0Uktx.js +7 -0
  77. package/src/ui/dist/assets/TerminalDrawer-CDusYltc.js +36 -0
  78. package/src/ui/dist/assets/WorkflowRoute-BN8yHmUs.css +1 -0
  79. package/src/ui/dist/assets/WorkflowRoute-D-0wVDpY.js +334 -0
  80. package/src/ui/dist/assets/abnfDiagram-VRR7QNED-zkRKkaxO.js +2 -0
  81. package/src/ui/dist/assets/apl-B4CMkyY2.js +2 -0
  82. package/src/ui/dist/assets/ar-SA-G6X2FPQ2-BiHaArx5.js +11 -0
  83. package/src/ui/dist/assets/arc-CBnLtOFO.js +2 -0
  84. package/src/ui/dist/assets/architectureDiagram-ZJ3FMSHR-DWXRXsh1.js +37 -0
  85. package/src/ui/dist/assets/asciiarmor-Df11BRmG.js +2 -0
  86. package/src/ui/dist/assets/asn1-EdZsLKOL.js +2 -0
  87. package/src/ui/dist/assets/asterisk-B-8jnY81.js +2 -0
  88. package/src/ui/dist/assets/az-AZ-76LH7QW2-CiT05voO.js +2 -0
  89. package/src/ui/dist/assets/bg-BG-XCXSNQG7-L-UM9tNf.js +6 -0
  90. package/src/ui/dist/assets/blockDiagram-677ZJIJ3-BIyh3Bry.js +133 -0
  91. package/src/ui/dist/assets/bn-BD-2XOGV67Q-NdgbA0bC.js +6 -0
  92. package/src/ui/dist/assets/brainfuck-C4LP7Hcl.js +2 -0
  93. package/src/ui/dist/assets/c4Diagram-LMCZKHZV-CA54vWWM.js +11 -0
  94. package/src/ui/dist/assets/ca-ES-6MX7JW3Y-BKZZsQ5J.js +9 -0
  95. package/src/ui/dist/assets/channel-Da_-zcAu.js +2 -0
  96. package/src/ui/dist/assets/chunk-2Q5K7J3B-DE4cxr1v.js +2 -0
  97. package/src/ui/dist/assets/chunk-32BRIVSS-i7FwbEU9.js +2 -0
  98. package/src/ui/dist/assets/chunk-5VM5RSS4-BhIcOYSu.js +16 -0
  99. package/src/ui/dist/assets/chunk-EX3LRPZG-Ci9zZRIs.js +232 -0
  100. package/src/ui/dist/assets/chunk-JWPE2WC7-BFdkcz-G.js +2 -0
  101. package/src/ui/dist/assets/chunk-MOJQB5TN-Dzmapl_S.js +89 -0
  102. package/src/ui/dist/assets/chunk-RYQCIY6F-RTmp7FOy.js +2 -0
  103. package/src/ui/dist/assets/chunk-V7JOEXUC-DInU3v3D.js +207 -0
  104. package/src/ui/dist/assets/chunk-VR4S4FIN-BHoyGBQO.js +2 -0
  105. package/src/ui/dist/assets/chunk-XXDRQBXY-DfeLZNZx.js +2 -0
  106. package/src/ui/dist/assets/classDiagram-OUVF2IWQ-FKbUS0bu.js +2 -0
  107. package/src/ui/dist/assets/classDiagram-v2-EOCWNBFH-FKbUS0bu.js +2 -0
  108. package/src/ui/dist/assets/clike-B9uivgTg.js +2 -0
  109. package/src/ui/dist/assets/clojure-BMjYHr_A.js +2 -0
  110. package/src/ui/dist/assets/cmake-BQqOBYOt.js +2 -0
  111. package/src/ui/dist/assets/cobol-CWcv1MsR.js +2 -0
  112. package/src/ui/dist/assets/coffeescript-S37ZYGWr.js +2 -0
  113. package/src/ui/dist/assets/commonlisp-DBKNyK5s.js +2 -0
  114. package/src/ui/dist/assets/cose-bilkent-JH36ORCC-_5PUJ615.js +2 -0
  115. package/src/ui/dist/assets/crystal-SjHAIU92.js +2 -0
  116. package/src/ui/dist/assets/cs-CZ-2BRQDIVT-DnkfnhDU.js +12 -0
  117. package/src/ui/dist/assets/css-BnMrqG3P.js +2 -0
  118. package/src/ui/dist/assets/cynefin-VYW2F7L2-CmkcFv-b.js +179 -0
  119. package/src/ui/dist/assets/cynefinDiagram-TSTJHNR4-D72VZn8H.js +63 -0
  120. package/src/ui/dist/assets/cypher-C_CwsFkJ.js +2 -0
  121. package/src/ui/dist/assets/cytoscape.esm-DTSO7Bv0.js +332 -0
  122. package/src/ui/dist/assets/d-pRatUO7H.js +2 -0
  123. package/src/ui/dist/assets/da-DK-5WZEPLOC-C54tQUgd.js +6 -0
  124. package/src/ui/dist/assets/dagre-VKFMJZFB-0BsAkrmo.js +5 -0
  125. package/src/ui/dist/assets/de-DE-XR44H4JA-DGfZlR10.js +9 -0
  126. package/src/ui/dist/assets/defaultLocale-DX6XiGOO.js +2 -0
  127. package/src/ui/dist/assets/diagram-FQU43EPY-C1ZJDOca.js +4 -0
  128. package/src/ui/dist/assets/diagram-G47NLZAW-D3pr3FSP.js +25 -0
  129. package/src/ui/dist/assets/diagram-NH7WQ7WH-Bwry0NV-.js +25 -0
  130. package/src/ui/dist/assets/diagram-OA4YK3LP-L6g09cmV.js +31 -0
  131. package/src/ui/dist/assets/diagram-WEI45ONY-Bh1puYVX.js +42 -0
  132. package/src/ui/dist/assets/diff-DbItnlRl.js +2 -0
  133. package/src/ui/dist/assets/directory-open-01563666-DWU9wJ6I.js +2 -0
  134. package/src/ui/dist/assets/directory-open-4ed118d0-CunoC1EB.js +2 -0
  135. package/src/ui/dist/assets/dockerfile-BKs6k2Af.js +2 -0
  136. package/src/ui/dist/assets/dtd-DF_7sFjM.js +2 -0
  137. package/src/ui/dist/assets/dylan-DwRh75JA.js +2 -0
  138. package/src/ui/dist/assets/ebnf-CDyGwa7X.js +2 -0
  139. package/src/ui/dist/assets/ebnfDiagram-CCIWWBDH-BC_mBRR5.js +2 -0
  140. package/src/ui/dist/assets/ecl-Cabwm37j.js +2 -0
  141. package/src/ui/dist/assets/eiffel-CnydiIhH.js +2 -0
  142. package/src/ui/dist/assets/el-GR-BZB4AONW-DsnwWweL.js +11 -0
  143. package/src/ui/dist/assets/elm-vLlmbW-K.js +2 -0
  144. package/src/ui/dist/assets/erDiagram-Q63AITRT-Bkxam36l.js +86 -0
  145. package/src/ui/dist/assets/erlang-BNw1qcRV.js +2 -0
  146. package/src/ui/dist/assets/es-ES-U4NZUMDT-CRxBcQgC.js +10 -0
  147. package/src/ui/dist/assets/eu-ES-A7QVB2H4-CTd_E7YR.js +12 -0
  148. package/src/ui/dist/assets/excalidraw-default-bMWIgNCy.svg +1 -0
  149. package/src/ui/dist/assets/fa-IR-HGAKTJCU-DO56k7eG.js +9 -0
  150. package/src/ui/dist/assets/factor-kuTfRLto.js +2 -0
  151. package/src/ui/dist/assets/fcl-Kvtd6kyn.js +2 -0
  152. package/src/ui/dist/assets/fi-FI-Z5N7JZ37-DEDCF9WB.js +7 -0
  153. package/src/ui/dist/assets/file-open-002ab408-DIuFHtCF.js +2 -0
  154. package/src/ui/dist/assets/file-open-7c801643-684qeFg4.js +2 -0
  155. package/src/ui/dist/assets/file-save-3189631c-C1wFhQhH.js +2 -0
  156. package/src/ui/dist/assets/file-save-745eba88-Bb9F9Kg7.js +2 -0
  157. package/src/ui/dist/assets/flowDiagram-23GEKE2U-C3SAEao7.js +157 -0
  158. package/src/ui/dist/assets/forth-Ffai-XNe.js +2 -0
  159. package/src/ui/dist/assets/fortran-DYz_wnZ1.js +2 -0
  160. package/src/ui/dist/assets/fr-FR-RHASNOE6-DE2nPyDt.js +10 -0
  161. package/src/ui/dist/assets/ganttDiagram-NO4QXBWP-U-J_Xi-_.js +293 -0
  162. package/src/ui/dist/assets/gas-Bneqetm1.js +2 -0
  163. package/src/ui/dist/assets/gherkin-heZmZLOM.js +2 -0
  164. package/src/ui/dist/assets/gitGraphDiagram-IHSO6WYX-BADVbD-a.js +107 -0
  165. package/src/ui/dist/assets/gl-ES-HMX3MZ6V-mCqVgQHC.js +11 -0
  166. package/src/ui/dist/assets/graph-C9eacEi8.js +2 -0
  167. package/src/ui/dist/assets/groovy-D9Dt4D0W.js +2 -0
  168. package/src/ui/dist/assets/haskell-Cw1EW3IL.js +2 -0
  169. package/src/ui/dist/assets/haxe-H-WmDvRZ.js +2 -0
  170. package/src/ui/dist/assets/he-IL-6SHJWFNN-KnaLrCzw.js +11 -0
  171. package/src/ui/dist/assets/hi-IN-IWLTKZ5I-wuqiaaWt.js +5 -0
  172. package/src/ui/dist/assets/http-DBlCnlav.js +2 -0
  173. package/src/ui/dist/assets/hu-HU-A5ZG7DT2-DLg_zm22.js +8 -0
  174. package/src/ui/dist/assets/id-ID-SAP4L64H-MB2mXqcR.js +11 -0
  175. package/src/ui/dist/assets/idl-BEugSyMb.js +2 -0
  176. package/src/ui/dist/assets/image-blob-reduce.esm-HwCm7IZ0.js +8 -0
  177. package/src/ui/dist/assets/index-39SJtrt0.js +2 -0
  178. package/src/ui/dist/assets/index-BQ0LIrW-.js +2 -0
  179. package/src/ui/dist/assets/index-BcAUh2Nm.css +1 -0
  180. package/src/ui/dist/assets/index-BcThOK-i.js +331 -0
  181. package/src/ui/dist/assets/index-BnxV3k_S.js +3 -0
  182. package/src/ui/dist/assets/index-C7MZBHbT.js +117 -0
  183. package/src/ui/dist/assets/index-CCvKI2rM.js +2 -0
  184. package/src/ui/dist/assets/index-CZvmcCab.js +8 -0
  185. package/src/ui/dist/assets/index-Cp3hLKNl.js +2 -0
  186. package/src/ui/dist/assets/index-D7ayFxcx.js +2 -0
  187. package/src/ui/dist/assets/index-DAYxSQGU.js +2 -0
  188. package/src/ui/dist/assets/index-DD-HeMV0.css +1 -0
  189. package/src/ui/dist/assets/index-DPMxd3Qn.js +2 -0
  190. package/src/ui/dist/assets/index-DSe2jx3k.js +4 -0
  191. package/src/ui/dist/assets/index-DUirCkgW.js +2 -0
  192. package/src/ui/dist/assets/index-DaKPQ2hN.js +159 -0
  193. package/src/ui/dist/assets/index-DzpuR0Jg.js +2 -0
  194. package/src/ui/dist/assets/index-POndwsCI.js +2 -0
  195. package/src/ui/dist/assets/index-R8NM9I_9.js +2 -0
  196. package/src/ui/dist/assets/index-TCDuDhBj.js +2 -0
  197. package/src/ui/dist/assets/index-TckzASdM.js +2 -0
  198. package/src/ui/dist/assets/index-Yi5ytFFr.js +2 -0
  199. package/src/ui/dist/assets/infoDiagram-FWYZ7A6U-CiGBlFqH.js +3 -0
  200. package/src/ui/dist/assets/init-Gi6I4Gst.js +2 -0
  201. package/src/ui/dist/assets/ishikawaDiagram-FXEZZL3T-B1aheRbz.js +71 -0
  202. package/src/ui/dist/assets/it-IT-JPQ66NNP-DT3HSU7X.js +12 -0
  203. package/src/ui/dist/assets/ja-JP-DBVTYXUO-CsfJjjWt.js +9 -0
  204. package/src/ui/dist/assets/javascript-iXu5QeM3.js +2 -0
  205. package/src/ui/dist/assets/journeyDiagram-5HDEW3XC-DtnJPxub.js +140 -0
  206. package/src/ui/dist/assets/julia-DuME0IfC.js +2 -0
  207. package/src/ui/dist/assets/kaa-6HZHGXH3-DQz-1-Gn.js +2 -0
  208. package/src/ui/dist/assets/kab-KAB-ZGHBKWFO-CaH-LSoK.js +9 -0
  209. package/src/ui/dist/assets/kanban-definition-HUTT4EX6-CZatSm3e.js +90 -0
  210. package/src/ui/dist/assets/katex-C5jXJg4s.js +258 -0
  211. package/src/ui/dist/assets/kk-KZ-P5N5QNE5-BMXmcm55.js +2 -0
  212. package/src/ui/dist/assets/km-KH-HSX4SM5Z-ZeoJNBpN.js +12 -0
  213. package/src/ui/dist/assets/ko-KR-MTYHY66A-BeaLRKph.js +10 -0
  214. package/src/ui/dist/assets/ku-TR-6OUDTVRD-B_28KrkR.js +10 -0
  215. package/src/ui/dist/assets/layout-DEXfKzaS.js +2 -0
  216. package/src/ui/dist/assets/linear-CpEDnQRC.js +2 -0
  217. package/src/ui/dist/assets/livescript-BwQOo05w.js +2 -0
  218. package/src/ui/dist/assets/lt-LT-XHIRWOB4-BxDcnUhp.js +4 -0
  219. package/src/ui/dist/assets/lua-VAEuO923.js +2 -0
  220. package/src/ui/dist/assets/lv-LV-5QDEKY6T-DpKDl1_y.js +8 -0
  221. package/src/ui/dist/assets/map-Czzmt4hB.js +2 -0
  222. package/src/ui/dist/assets/mathematica-DTrFuWx2.js +2 -0
  223. package/src/ui/dist/assets/mbox-CNhZ1qSd.js +2 -0
  224. package/src/ui/dist/assets/mermaidLoader-CBO31t0J.js +305 -0
  225. package/src/ui/dist/assets/mermaidLoader-CEK31ho9.css +1 -0
  226. package/src/ui/dist/assets/mindmap-definition-LN4V7U3C-CQ0TccEr.js +97 -0
  227. package/src/ui/dist/assets/mirc-CjQqDB4T.js +2 -0
  228. package/src/ui/dist/assets/mllike-CXdrOF99.js +2 -0
  229. package/src/ui/dist/assets/modelica-Dc1JOy9r.js +2 -0
  230. package/src/ui/dist/assets/mr-IN-CRQNXWMA-BKCZT58p.js +14 -0
  231. package/src/ui/dist/assets/mscgen-BA5vi2Kp.js +2 -0
  232. package/src/ui/dist/assets/mumps-BT43cFF4.js +2 -0
  233. package/src/ui/dist/assets/my-MM-5M5IBNSE-D34k0G9T.js +2 -0
  234. package/src/ui/dist/assets/nb-NO-T6EIAALU-DXr_KW8J.js +11 -0
  235. package/src/ui/dist/assets/nginx-DdIZxoE0.js +2 -0
  236. package/src/ui/dist/assets/nl-NL-IS3SIHDZ-C1L0aVDR.js +9 -0
  237. package/src/ui/dist/assets/nn-NO-6E72VCQL-CLWVSxbZ.js +9 -0
  238. package/src/ui/dist/assets/nsis-LdVXkNf5.js +2 -0
  239. package/src/ui/dist/assets/ntriples-BfvgReVJ.js +2 -0
  240. package/src/ui/dist/assets/oc-FR-POXYY2M6-nf8_J3eR.js +9 -0
  241. package/src/ui/dist/assets/octave-Ck1zUtKM.js +2 -0
  242. package/src/ui/dist/assets/ordinal-Cboi1Yqb.js +2 -0
  243. package/src/ui/dist/assets/oz-BzwKVEFT.js +2 -0
  244. package/src/ui/dist/assets/pa-IN-N4M65BXN-CAMJ-xd6.js +5 -0
  245. package/src/ui/dist/assets/pascal--L3eBynH.js +2 -0
  246. package/src/ui/dist/assets/pegDiagram-2B236MQR-Bj2RcmRd.js +2 -0
  247. package/src/ui/dist/assets/percentages-BXMCSKIN-s9yWFdr6.js +160 -0
  248. package/src/ui/dist/assets/perl-CdXCOZ3F.js +2 -0
  249. package/src/ui/dist/assets/pica-BGv_AhAB.js +8 -0
  250. package/src/ui/dist/assets/pieDiagram-ENE6RG2P-obpzz8dC.js +40 -0
  251. package/src/ui/dist/assets/pig-CevX1Tat.js +2 -0
  252. package/src/ui/dist/assets/pl-PL-T2D74RX3-DKJ5H85v.js +10 -0
  253. package/src/ui/dist/assets/powershell-CFHJl5sT.js +2 -0
  254. package/src/ui/dist/assets/properties-C78fOPTZ.js +2 -0
  255. package/src/ui/dist/assets/protobuf-ChK-085T.js +2 -0
  256. package/src/ui/dist/assets/{proxy-D0vUbds5.js → proxy-BSmwef4H.js} +2 -2
  257. package/src/ui/dist/assets/pt-BR-5N22H2LF-B5kow-kN.js +10 -0
  258. package/src/ui/dist/assets/pt-PT-UZXXM6DQ-B2G34fso.js +10 -0
  259. package/src/ui/dist/assets/pug-DeIclll2.js +2 -0
  260. package/src/ui/dist/assets/puppet-DMA9R1ak.js +2 -0
  261. package/src/ui/dist/assets/python-BuPzkPfP.js +2 -0
  262. package/src/ui/dist/assets/q-pXgVlZs6.js +2 -0
  263. package/src/ui/dist/assets/quadrantDiagram-ABIIQ3AL-Cl-T7NkJ.js +8 -0
  264. package/src/ui/dist/assets/r-B6wPVr8A.js +2 -0
  265. package/src/ui/dist/assets/railroadDiagram-RFXS5EU6-DMq82RHg.js +2 -0
  266. package/src/ui/dist/assets/{refresh-cw-JNmExijd.js → refresh-cw-CbFd7guV.js} +2 -2
  267. package/src/ui/dist/assets/requirementDiagram-TGXJPOKE-tGInMDgU.js +85 -0
  268. package/src/ui/dist/assets/ro-RO-JPDTUUEW-BwLdZW91.js +12 -0
  269. package/src/ui/dist/assets/roundRect-0PYZxl1G.js +2 -0
  270. package/src/ui/dist/assets/rpm-CTu-6PCP.js +2 -0
  271. package/src/ui/dist/assets/ru-RU-B4JR7IUQ-DpdAgIJc.js +10 -0
  272. package/src/ui/dist/assets/ruby-B2Rjki9n.js +2 -0
  273. package/src/ui/dist/assets/runtimeBrand-BoKPimsz.js +2 -0
  274. package/src/ui/dist/assets/sankeyDiagram-HTMAVEWB-C7X-ET4m.js +41 -0
  275. package/src/ui/dist/assets/sas-B4kiWyti.js +2 -0
  276. package/src/ui/dist/assets/save-D1CXHwBw.js +12 -0
  277. package/src/ui/dist/assets/scheme-C41bIUwD.js +2 -0
  278. package/src/ui/dist/assets/sequenceDiagram-DBY2YBRQ-Bn24i_nY.js +163 -0
  279. package/src/ui/dist/assets/settings-2-DtSNAAcU.js +22 -0
  280. package/src/ui/dist/assets/shell-CjFT_Tl9.js +2 -0
  281. package/src/ui/dist/assets/shield-check-BVbJ8T_N.js +7 -0
  282. package/src/ui/dist/assets/si-LK-N5RQ5JYF-naJaUy5u.js +2 -0
  283. package/src/ui/dist/assets/sieve-C3Gn_uJK.js +2 -0
  284. package/src/ui/dist/assets/simple-mode-GW_nhZxv.js +2 -0
  285. package/src/ui/dist/assets/sizeCapture-X5ZJPWSS-DAQHfm82.js +2 -0
  286. package/src/ui/dist/assets/sk-SK-C5VTKIMK-1NPz1J_d.js +7 -0
  287. package/src/ui/dist/assets/sl-SI-NN7IZMDC-D8RfmNQw.js +7 -0
  288. package/src/ui/dist/assets/smalltalk-CnHTOXQT.js +2 -0
  289. package/src/ui/dist/assets/solr-DehyRSwq.js +2 -0
  290. package/src/ui/dist/assets/sparql-DkYu6x3z.js +2 -0
  291. package/src/ui/dist/assets/spreadsheet-BCZA_wO0.js +2 -0
  292. package/src/ui/dist/assets/sql-D0XecflT.js +2 -0
  293. package/src/ui/dist/assets/stateDiagram-2N3HPSRC-Ck48D1ck.js +2 -0
  294. package/src/ui/dist/assets/stateDiagram-v2-6OUMAXLB-CdfRCJAJ.js +2 -0
  295. package/src/ui/dist/assets/stex-C3f8Ysf7.js +2 -0
  296. package/src/ui/dist/assets/style-C3QI7VPf.css +1 -0
  297. package/src/ui/dist/assets/stylus-B533Al4x.js +2 -0
  298. package/src/ui/dist/assets/subset-shared.chunk-eAtLHmbi.js +85 -0
  299. package/src/ui/dist/assets/subset-worker.chunk-DeHCTnTk.js +2 -0
  300. package/src/ui/dist/assets/sv-SE-XGPEYMSR-BeOYCU-8.js +11 -0
  301. package/src/ui/dist/assets/swift-BzpIVaGY.js +2 -0
  302. package/src/ui/dist/assets/swimlanes-5IMT3BWC-DOy22UjP.js +3 -0
  303. package/src/ui/dist/assets/swimlanesDiagram-G3AALYLV-sicPbVOg.js +9 -0
  304. package/src/ui/dist/assets/ta-IN-2NMHFXQM-CtS68WDd.js +10 -0
  305. package/src/ui/dist/assets/tcl-DVfN8rqt.js +2 -0
  306. package/src/ui/dist/assets/{terminal-BBQvnOAK.js → terminal-DEby5Y_P.js} +2 -2
  307. package/src/ui/dist/assets/terminalControl-BkFeDVXo.js +134 -0
  308. package/src/ui/dist/assets/textile-CnDTJFAw.js +2 -0
  309. package/src/ui/dist/assets/th-TH-HPSO5L25-DCHY9-Co.js +3 -0
  310. package/src/ui/dist/assets/tiddlywiki-DO-Gjzrf.js +2 -0
  311. package/src/ui/dist/assets/tiki-DGYXhP31.js +2 -0
  312. package/src/ui/dist/assets/timeline-definition-FHXFAJF6-DVkG72qO.js +121 -0
  313. package/src/ui/dist/assets/toml-Bm5Em-hy.js +2 -0
  314. package/src/ui/dist/assets/tr-TR-DEFEU3FU-y5-vjTv8.js +8 -0
  315. package/src/ui/dist/assets/troff-wAsdV37c.js +2 -0
  316. package/src/ui/dist/assets/ttcn-CfJYG6tj.js +2 -0
  317. package/src/ui/dist/assets/ttcn-cfg-B9xdYoR4.js +2 -0
  318. package/src/ui/dist/assets/turtle-B1tBg_DP.js +2 -0
  319. package/src/ui/dist/assets/uk-UA-QMV73CPH-BRbR5It6.js +7 -0
  320. package/src/ui/dist/assets/useReducedMotion-D07XP52Q.js +12 -0
  321. package/src/ui/dist/assets/vb-CmGdzxic.js +2 -0
  322. package/src/ui/dist/assets/vbscript-BuJXcnF6.js +2 -0
  323. package/src/ui/dist/assets/velocity-D8B20fx6.js +2 -0
  324. package/src/ui/dist/assets/vennDiagram-L72KCM5P-DwMwBlf-.js +35 -0
  325. package/src/ui/dist/assets/verilog-C6RDOZhf.js +2 -0
  326. package/src/ui/dist/assets/vhdl-lSbBsy5d.js +2 -0
  327. package/src/ui/dist/assets/vi-VN-M7AON7JQ-s6wSBv2s.js +6 -0
  328. package/src/ui/dist/assets/wardleyDiagram-EHGQE667-CNuuvzEg.js +79 -0
  329. package/src/ui/dist/assets/webidl-ZXfAyPTL.js +2 -0
  330. package/src/ui/dist/assets/{x-DoIOGAJz.js → x-B84wq78k.js} +2 -2
  331. package/src/ui/dist/assets/xquery-DzFWVndE.js +2 -0
  332. package/src/ui/dist/assets/xychartDiagram-FW5EYKEG-dUpZxmn6.js +8 -0
  333. package/src/ui/dist/assets/yacas-BJ4BC0dw.js +2 -0
  334. package/src/ui/dist/assets/z80-Hz9HOZM7.js +2 -0
  335. package/src/ui/dist/assets/zh-CN-LNUGB5OW-Cu_-POQN.js +11 -0
  336. package/src/ui/dist/assets/zh-HK-E62DVLB3-BCx4W4qZ.js +2 -0
  337. package/src/ui/dist/assets/zh-TW-RAJ6MFWO-02wx_Mrj.js +10 -0
  338. package/src/ui/dist/icons/excalidraw.svg +1 -0
  339. package/src/ui/dist/icons/markdown.svg +1 -0
  340. package/src/ui/dist/index.html +2 -2
  341. package/src/ui/package.json +10 -0
  342. package/src/ui/playwright.config.ts +36 -0
  343. package/src/ui/pnpm-workspace.yaml +2 -0
  344. package/src/ui/public/icons/excalidraw.svg +1 -0
  345. package/src/ui/public/icons/markdown.svg +1 -0
  346. package/src/ui/tsconfig.json +2 -1
  347. package/src/ui/vite.config.ts +1 -0
  348. package/src/wf-ui-server/__tests__/a2a-store.test.mjs +432 -15
  349. package/src/wf-ui-server/__tests__/action-registry-loader.test.mjs +156 -0
  350. package/src/wf-ui-server/__tests__/claude-stream-json.test.mjs +397 -0
  351. package/src/wf-ui-server/__tests__/codex-appserver.test.mjs +392 -0
  352. package/src/wf-ui-server/__tests__/component-node-api.test.mjs +881 -0
  353. package/src/wf-ui-server/__tests__/component-node-store.test.mjs +401 -0
  354. package/src/wf-ui-server/__tests__/control-plane-acceptance.test.mjs +2027 -0
  355. package/src/wf-ui-server/__tests__/control-plane-cli-smoke.test.mjs +1014 -0
  356. package/src/wf-ui-server/__tests__/dispatch-lifecycle.test.mjs +330 -0
  357. package/src/wf-ui-server/__tests__/effort-compatibility-red-092.test.mjs +151 -0
  358. package/src/wf-ui-server/__tests__/graph-layout-agent-tree.test.mjs +254 -0
  359. package/src/wf-ui-server/__tests__/graph-layout-tidy.test.mjs +260 -0
  360. package/src/wf-ui-server/__tests__/graph-layout.test.mjs +84 -0
  361. package/src/wf-ui-server/__tests__/node-config-api.test.mjs +384 -0
  362. package/src/wf-ui-server/__tests__/opencode-server.test.mjs +526 -0
  363. package/src/wf-ui-server/__tests__/peer-capsule.test.mjs +2 -2
  364. package/src/wf-ui-server/__tests__/pty-adapter.test.mjs +71 -1
  365. package/src/wf-ui-server/__tests__/runtime-config.test.mjs +3 -3
  366. package/src/wf-ui-server/__tests__/runtime-detector.test.mjs +21 -0
  367. package/src/wf-ui-server/__tests__/runtime-review.test.mjs +828 -0
  368. package/src/wf-ui-server/__tests__/server.integration.test.mjs +1463 -11
  369. package/src/wf-ui-server/__tests__/session-cleanup.test.mjs +159 -0
  370. package/src/wf-ui-server/__tests__/session-registry.test.mjs +52 -3
  371. package/src/wf-ui-server/__tests__/settings.test.mjs +12 -2
  372. package/src/wf-ui-server/__tests__/spawn-gate.test.mjs +78 -0
  373. package/src/wf-ui-server/__tests__/task-group-parser.test.mjs +120 -0
  374. package/src/wf-ui-server/__tests__/task-group-tag.test.mjs +264 -0
  375. package/src/wf-ui-server/__tests__/task-parser.test.mjs +49 -3
  376. package/src/wf-ui-server/__tests__/terminal-context-hardening.test.mjs +122 -0
  377. package/src/wf-ui-server/__tests__/terminal-context.test.mjs +164 -0
  378. package/src/wf-ui-server/__tests__/terminal-store.test.mjs +4 -4
  379. package/src/wf-ui-server/__tests__/wf-browser-api.test.mjs +599 -0
  380. package/src/wf-ui-server/__tests__/wf-browser-store.test.mjs +184 -0
  381. package/src/wf-ui-server/__tests__/workflow-action-registry.test.mjs +282 -0
  382. package/src/wf-ui-server/__tests__/workflow-agent-communication.test.mjs +411 -0
  383. package/src/wf-ui-server/__tests__/workflow-agent-context.test.mjs +323 -0
  384. package/src/wf-ui-server/__tests__/workflow-agent-cooperation-permissions.test.mjs +318 -0
  385. package/src/wf-ui-server/__tests__/workflow-agent-messages-structured.test.mjs +458 -0
  386. package/src/wf-ui-server/__tests__/workflow-agent-routing.test.mjs +434 -0
  387. package/src/wf-ui-server/__tests__/workflow-agent-runtime.test.mjs +324 -0
  388. package/src/wf-ui-server/__tests__/workflow-api-integration.test.mjs +263 -0
  389. package/src/wf-ui-server/__tests__/workflow-api-phase3.test.mjs +1213 -0
  390. package/src/wf-ui-server/__tests__/workflow-claude-materialize.test.mjs +209 -0
  391. package/src/wf-ui-server/__tests__/workflow-cli-registry-manuals.test.mjs +173 -0
  392. package/src/wf-ui-server/__tests__/workflow-cli-surface.test.mjs +176 -0
  393. package/src/wf-ui-server/__tests__/workflow-codex-resume-id.test.mjs +556 -0
  394. package/src/wf-ui-server/__tests__/workflow-cooperation-enforcement.test.mjs +553 -0
  395. package/src/wf-ui-server/__tests__/workflow-depth-resume-dock.test.mjs +846 -0
  396. package/src/wf-ui-server/__tests__/workflow-display-node.test.mjs +150 -0
  397. package/src/wf-ui-server/__tests__/workflow-edge-update.test.mjs +296 -0
  398. package/src/wf-ui-server/__tests__/workflow-file-cache-lock-watch.test.mjs +380 -0
  399. package/src/wf-ui-server/__tests__/workflow-file-formats.test.mjs +994 -0
  400. package/src/wf-ui-server/__tests__/workflow-file-node-actions.test.mjs +294 -0
  401. package/src/wf-ui-server/__tests__/workflow-goal-actions.test.mjs +357 -0
  402. package/src/wf-ui-server/__tests__/workflow-goal-task-autoclose.test.mjs +256 -0
  403. package/src/wf-ui-server/__tests__/workflow-graceful-stop.test.mjs +108 -0
  404. package/src/wf-ui-server/__tests__/workflow-graph-undo.test.mjs +524 -0
  405. package/src/wf-ui-server/__tests__/workflow-layout.test.mjs +732 -0
  406. package/src/wf-ui-server/__tests__/workflow-magnetic-topology.test.mjs +222 -0
  407. package/src/wf-ui-server/__tests__/workflow-markdown-lock.test.mjs +359 -0
  408. package/src/wf-ui-server/__tests__/workflow-mcp-hub.test.mjs +174 -0
  409. package/src/wf-ui-server/__tests__/workflow-node-manual-schema.test.mjs +241 -0
  410. package/src/wf-ui-server/__tests__/workflow-node-runtime.test.mjs +1991 -0
  411. package/src/wf-ui-server/__tests__/workflow-node-store-utils.test.mjs +125 -0
  412. package/src/wf-ui-server/__tests__/workflow-registry-drift.test.mjs +296 -0
  413. package/src/wf-ui-server/__tests__/workflow-resource-handle-four-side.test.mjs +217 -0
  414. package/src/wf-ui-server/__tests__/workflow-role-profile.test.mjs +234 -0
  415. package/src/wf-ui-server/__tests__/workflow-set-model.test.mjs +261 -0
  416. package/src/wf-ui-server/__tests__/workflow-skill-group-context-enabled.test.mjs +131 -0
  417. package/src/wf-ui-server/__tests__/workflow-skill-group-enable-disable.test.mjs +186 -0
  418. package/src/wf-ui-server/__tests__/workflow-skills-hub.test.mjs +125 -0
  419. package/src/wf-ui-server/__tests__/workflow-state-reconcile.test.mjs +139 -0
  420. package/src/wf-ui-server/__tests__/workflow-subagent-strategy-matrix.test.mjs +529 -0
  421. package/src/wf-ui-server/__tests__/workflow-team-flow.test.mjs +278 -0
  422. package/src/wf-ui-server/__tests__/workflow-terminal-control-live.test.mjs +294 -0
  423. package/src/wf-ui-server/__tests__/workflow-terminal-control.test.mjs +631 -0
  424. package/src/wf-ui-server/__tests__/workflow-timer-wakeup.test.mjs +318 -0
  425. package/src/wf-ui-server/__tests__/workflow-unified-resume.test.mjs +537 -0
  426. package/src/wf-ui-server/__tests__/workspace-api-hardening.test.mjs +187 -0
  427. package/src/wf-ui-server/__tests__/workspace-api.test.mjs +382 -0
  428. package/src/wf-ui-server/__tests__/workspace-reveal.test.mjs +138 -0
  429. package/src/wf-ui-server/__tests__/workspace-store-hardening.test.mjs +255 -0
  430. package/src/wf-ui-server/__tests__/workspace-store.test.mjs +208 -0
  431. package/src/wf-ui-server/__tests__/ws-events.integration.test.mjs +52 -25
  432. package/src/wf-ui-server/__tests__/ws-terminal.integration.test.mjs +12 -6
  433. package/src/wf-ui-server/__tests__/ws-wf-browser.integration.test.mjs +488 -0
  434. package/src/wf-ui-server/a2a-store.mjs +3007 -89
  435. package/src/wf-ui-server/action-registry.mjs +277 -0
  436. package/src/wf-ui-server/bridge-store.mjs +276 -0
  437. package/src/wf-ui-server/chat-driver.mjs +220 -0
  438. package/src/wf-ui-server/chat-store.mjs +109 -0
  439. package/src/wf-ui-server/claude-transcript-materializer.mjs +235 -0
  440. package/src/wf-ui-server/codex-update-prompt.mjs +53 -0
  441. package/src/wf-ui-server/component-node-store.mjs +689 -0
  442. package/src/wf-ui-server/composition-api.mjs +436 -0
  443. package/src/wf-ui-server/control-plane-token.mjs +17 -0
  444. package/src/wf-ui-server/dispatch-contract.mjs +150 -0
  445. package/src/wf-ui-server/drivers/claude-stream-json.mjs +371 -0
  446. package/src/wf-ui-server/drivers/codex-appserver.mjs +513 -0
  447. package/src/wf-ui-server/drivers/opencode-server.mjs +486 -0
  448. package/src/wf-ui-server/excalidraw-svg.mjs +141 -0
  449. package/src/wf-ui-server/file-format-adapters.mjs +511 -0
  450. package/src/wf-ui-server/file-watcher.mjs +215 -0
  451. package/src/wf-ui-server/graph-layout.mjs +778 -0
  452. package/src/wf-ui-server/harness-env.mjs +46 -0
  453. package/src/wf-ui-server/json-store-utils.mjs +48 -0
  454. package/src/wf-ui-server/model-capability.mjs +773 -0
  455. package/src/wf-ui-server/node-config-store.mjs +233 -0
  456. package/src/wf-ui-server/node-store-utils.mjs +88 -0
  457. package/src/wf-ui-server/peer-capsule.mjs +56 -1
  458. package/src/wf-ui-server/pty-adapter.mjs +513 -11
  459. package/src/wf-ui-server/reveal-foreground.mjs +69 -0
  460. package/src/wf-ui-server/runtime-detector.mjs +52 -6
  461. package/src/wf-ui-server/server.mjs +5613 -403
  462. package/src/wf-ui-server/session-cleanup.mjs +363 -0
  463. package/src/wf-ui-server/session-registry.mjs +179 -3
  464. package/src/wf-ui-server/settings.mjs +25 -0
  465. package/src/wf-ui-server/spawn-gate.mjs +46 -0
  466. package/src/wf-ui-server/task-parser.mjs +88 -3
  467. package/src/wf-ui-server/terminal-store.mjs +225 -11
  468. package/src/wf-ui-server/timer-wakeup-scheduler.mjs +226 -0
  469. package/src/wf-ui-server/wf-browser-store.mjs +733 -0
  470. package/src/wf-ui-server/workflow-agent-context.mjs +1731 -0
  471. package/src/wf-ui-server/workflow-capability-node-store.mjs +676 -0
  472. package/src/wf-ui-server/workflow-event-node-store.mjs +623 -0
  473. package/src/wf-ui-server/workflow-goal-node-store.mjs +328 -0
  474. package/src/wf-ui-server/workflow-graph-store.mjs +623 -0
  475. package/src/wf-ui-server/workflow-mcp-hub.mjs +286 -0
  476. package/src/wf-ui-server/workflow-node-runtime.mjs +1767 -0
  477. package/src/wf-ui-server/workflow-node-settings-store.mjs +339 -0
  478. package/src/wf-ui-server/workflow-node-types/agent-node.mjs +920 -0
  479. package/src/wf-ui-server/workflow-node-types/display-node.mjs +151 -0
  480. package/src/wf-ui-server/workflow-node-types/excalidraw-node.mjs +145 -0
  481. package/src/wf-ui-server/workflow-node-types/file-node.mjs +484 -0
  482. package/src/wf-ui-server/workflow-node-types/github-trigger-node.mjs +207 -0
  483. package/src/wf-ui-server/workflow-node-types/goal-node.mjs +491 -0
  484. package/src/wf-ui-server/workflow-node-types/markdown-node.mjs +225 -0
  485. package/src/wf-ui-server/workflow-node-types/mcp-connector-node.mjs +71 -0
  486. package/src/wf-ui-server/workflow-node-types/role-profile-store.mjs +191 -0
  487. package/src/wf-ui-server/workflow-node-types/skill-group-node.mjs +98 -0
  488. package/src/wf-ui-server/workflow-node-types/timer-node.mjs +607 -0
  489. package/src/wf-ui-server/workflow-ontology.mjs +681 -0
  490. package/src/wf-ui-server/workflow-operation-store.mjs +115 -0
  491. package/src/wf-ui-server/workflow-skills-hub.mjs +366 -0
  492. package/src/wf-ui-server/workflow-skills-market.mjs +723 -0
  493. package/src/wf-ui-server/workspace-store.mjs +951 -0
  494. package/src/wf-ui-server/ws-chat.mjs +299 -0
  495. package/src/wf-ui-server/ws-events.mjs +45 -20
  496. package/src/wf-ui-server/ws-terminal.mjs +165 -22
  497. package/src/wf-ui-server/ws-wf-browser.mjs +419 -0
  498. package/templates/common/.claude/agents/review-manager.md +33 -29
  499. package/templates/common/.claude/commands/wf-browser.md +1 -1
  500. package/templates/common/.claude/commands/wf-help.md +5 -3
  501. package/templates/common/.claude/commands/wf-init.md +44 -0
  502. package/templates/common/.claude/commands/wf-learn.md +34 -8
  503. package/templates/common/.claude/commands/wf-max.md +19 -9
  504. package/templates/common/.claude/commands/wf-review.md +3 -3
  505. package/templates/common/.claude/commands/wf-search.md +55 -0
  506. package/templates/common/.claude/commands/wf-task-archive.md +5 -4
  507. package/templates/common/.claude/commands/wf-task-list.md +6 -3
  508. package/templates/common/.claude/commands/wf-task-record.md +10 -2
  509. package/templates/common/.claude/commands/wf-ui.md +12 -5
  510. package/templates/common/.claude/commands/wf-update.md +4 -0
  511. package/templates/common/.claude/commands/wf.md +12 -1
  512. package/templates/common/.claude/rules/ecc/common.md +10 -1
  513. package/templates/common/.claude/skills/wf/SKILL.md +36 -0
  514. package/templates/common/.claude/skills/wf-browser/SKILL.md +427 -122
  515. package/templates/common/.claude/skills/wf-browser/references/BROWSER_CONTROL_PATTERNS.md +43 -0
  516. package/templates/common/.claude/skills/wf-init/SKILL.md +33 -0
  517. package/templates/common/.claude/skills/wf-learn/SKILL.md +106 -26
  518. package/templates/common/.claude/skills/wf-max/SKILL.md +45 -11
  519. package/templates/common/.claude/skills/wf-review/SKILL.md +26 -54
  520. package/templates/common/.claude/skills/wf-search/SKILL.md +122 -0
  521. package/templates/common/.claude/skills/wf-search/references/evidence-contract.md +77 -0
  522. package/templates/common/.claude/skills/wf-search/references/policies/compare.md +38 -0
  523. package/templates/common/.claude/skills/wf-search/references/policies/fact.md +39 -0
  524. package/templates/common/.claude/skills/wf-search/references/policies/troubleshoot.md +39 -0
  525. package/templates/common/.claude/skills/wf-search/references/policies/verify.md +44 -0
  526. package/templates/common/.claude/skills/wf-search/references/source-routing.md +40 -0
  527. package/templates/common/.claude/skills/wf-search/references/tool-adapters.md +38 -0
  528. package/templates/common/.claude/skills/wf-ui/SKILL.md +146 -12
  529. package/templates/common/.claude/skills/wf-update/SKILL.md +5 -1
  530. package/templates/common/.harness-version +188 -85
  531. package/templates/common/.opencode/agents/review-manager.md +31 -27
  532. package/templates/common/.opencode/commands/wf-browser.md +1 -1
  533. package/templates/common/.opencode/commands/wf-help.md +5 -3
  534. package/templates/common/.opencode/commands/wf-init.md +44 -0
  535. package/templates/common/.opencode/commands/wf-learn.md +34 -8
  536. package/templates/common/.opencode/commands/wf-max.md +19 -9
  537. package/templates/common/.opencode/commands/wf-review.md +3 -3
  538. package/templates/common/.opencode/commands/wf-search.md +55 -0
  539. package/templates/common/.opencode/commands/wf-task-archive.md +5 -7
  540. package/templates/common/.opencode/commands/wf-task-list.md +6 -6
  541. package/templates/common/.opencode/commands/wf-task-record.md +10 -5
  542. package/templates/common/.opencode/commands/wf-ui.md +12 -5
  543. package/templates/common/.opencode/commands/wf-update.md +4 -0
  544. package/templates/common/.opencode/commands/wf.md +12 -1
  545. package/templates/common/CLAUDE.md +61 -3
  546. package/templates/common/Harness/MEMORY.md +8 -5
  547. package/templates/common/Harness/README.md +14 -10
  548. package/templates/common/Harness/a2a/action-registry.json +4397 -0
  549. package/templates/common/Harness/a2a/role-graph.json +16 -3
  550. package/templates/common/Harness/a2a/skills/terminal-control.json +45 -1
  551. package/templates/common/Harness/a2a/skills/wf-ui-map.json +50 -0
  552. package/templates/common/Harness/a2a/skills/workflow-agent-node.json +143 -0
  553. package/templates/common/Harness/a2a/skills/workflow-context.json +95 -0
  554. package/templates/common/Harness/a2a/skills/workflow-diagram-node.json +60 -0
  555. package/templates/common/Harness/a2a/skills/workflow-display-node.json +69 -0
  556. package/templates/common/Harness/a2a/skills/workflow-file-node.json +92 -0
  557. package/templates/common/Harness/a2a/skills/workflow-goal-node.json +76 -0
  558. package/templates/common/Harness/a2a/skills/workflow-markdown-node.json +79 -0
  559. package/templates/common/Harness/a2a/skills/workflow-mcp-connector-node.json +59 -0
  560. package/templates/common/Harness/a2a/skills/workflow-node-actions.json +105 -0
  561. package/templates/common/Harness/a2a/skills/workflow-node-map.json +71 -0
  562. package/templates/common/Harness/a2a/skills/workflow-ontology.json +54 -0
  563. package/templates/common/Harness/a2a/skills/workflow-resource-node.json +68 -0
  564. package/templates/common/Harness/a2a/skills/workflow-skill-group-node.json +56 -0
  565. package/templates/common/Harness/a2a/skills/workflow-timer-node.json +81 -0
  566. package/templates/common/Harness/a2a/workflows.json +33 -0
  567. package/templates/common/Harness/ownership.manifest.json +263 -2
  568. package/templates/common/Harness/project/architecture.md +499 -0
  569. package/templates/common/Harness/research/README.md +20 -0
  570. package/templates/common/Harness/scripts/README.md +70 -6
  571. package/templates/common/Harness/scripts/a2a-terminal.mjs +12 -4
  572. package/templates/common/Harness/scripts/context-budget.mjs +7 -3
  573. package/templates/common/Harness/scripts/lib/memory-context.mjs +400 -0
  574. package/templates/common/Harness/scripts/lib/path-scope.mjs +77 -0
  575. package/templates/common/Harness/scripts/lib/research-policy.mjs +99 -0
  576. package/templates/common/Harness/scripts/lib/wf-search.mjs +189 -0
  577. package/templates/common/Harness/scripts/memory-context.mjs +63 -0
  578. package/templates/common/Harness/scripts/research-policy.mjs +27 -0
  579. package/templates/common/Harness/scripts/task-context.mjs +547 -0
  580. package/templates/common/Harness/scripts/task-group-index.mjs +242 -0
  581. package/templates/common/Harness/scripts/task-state.mjs +659 -95
  582. package/templates/common/Harness/scripts/validate-harness.mjs +70 -28
  583. package/templates/common/Harness/scripts/wf-auto-update-prompt.mjs +24 -0
  584. package/templates/common/Harness/scripts/wf-remove.mjs +10 -0
  585. package/templates/common/Harness/scripts/wf-search.mjs +10 -0
  586. package/templates/common/Harness/scripts/wf-ui-control.mjs +3144 -0
  587. package/templates/common/Harness/scripts/wf-update-check.mjs +274 -51
  588. package/templates/common/Harness/scripts/wf-update-runner.mjs +53 -16
  589. package/templates/common/Harness/settings.json +10 -0
  590. package/templates/common/Harness/specs/guides/SETUP.md +4 -3
  591. package/templates/common/Harness/specs/protocols/HARNESS_BRIDGE.md +429 -48
  592. package/templates/common/Harness/specs/protocols/MEMORY_PROTOCOL.md +24 -0
  593. package/templates/common/Harness/specs/protocols/TASK_ARCHIVE.md +14 -11
  594. package/templates/common/Harness/specs/runtime/command-surface.json +28 -0
  595. package/templates/common/Harness/specs/runtime/context-loading.md +6 -0
  596. package/templates/common/Harness/specs/runtime/dispatch.md +9 -1
  597. package/templates/common/Harness/specs/runtime/subagents.md +20 -5
  598. package/templates/common/Harness/specs/workflows/WF-AUTO-SPARK.md +19 -0
  599. package/templates/common/Harness/specs/workflows/WF-AUTO.md +21 -0
  600. package/templates/common/Harness/specs/workflows/WF-KERNEL.md +4 -2
  601. package/templates/common/Harness/specs/workflows/WF-MAX.md +39 -12
  602. package/templates/common/Harness/specs/workflows/WF-STATE.md +62 -14
  603. package/templates/common/Harness/specs/workflows/WF.md +32 -0
  604. package/templates/common/Harness/tasks/INDEX.json +7 -0
  605. package/templates/common/Harness/tasks/INDEX.md +12 -0
  606. package/templates/common/Harness/tasks/_template/PLAN.md +6 -0
  607. package/templates/common/Harness/tasks/_template/PROGRESS.md +5 -0
  608. package/templates/common/Harness/tasks/_template/STATE.json +22 -0
  609. package/templates/common/Harness/templates/TEST_PLAN.template.md +5 -4
  610. package/templates/common/Harness/templates/UI_CONTRACT.template.md +7 -5
  611. package/templates/common/Harness/wf-browser/README.md +69 -0
  612. package/templates/common/README.md +17 -0
  613. package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js +0 -17
  614. package/src/ui/dist/assets/AgentsRoute-CuSKYmFu.js.map +0 -1
  615. package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js +0 -7
  616. package/src/ui/dist/assets/RolesRoute-DOzTYVRm.js.map +0 -1
  617. package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js +0 -12
  618. package/src/ui/dist/assets/SettingsRoute-DPU7jSQm.js.map +0 -1
  619. package/src/ui/dist/assets/TaskList-BN4r8s1i.js +0 -50
  620. package/src/ui/dist/assets/TaskList-BN4r8s1i.js.map +0 -1
  621. package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js +0 -74
  622. package/src/ui/dist/assets/TerminalDrawer-Cej4PxsH.js.map +0 -1
  623. package/src/ui/dist/assets/WorkflowRoute-BnuhLJ6X.css +0 -1
  624. package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js +0 -39
  625. package/src/ui/dist/assets/WorkflowRoute-C-clKe-j.js.map +0 -1
  626. package/src/ui/dist/assets/index-BOVYWntB.js +0 -117
  627. package/src/ui/dist/assets/index-BOVYWntB.js.map +0 -1
  628. package/src/ui/dist/assets/index-DaUObq0H.css +0 -1
  629. package/src/ui/dist/assets/maximize-2-LNrr_rKr.js +0 -7
  630. package/src/ui/dist/assets/maximize-2-LNrr_rKr.js.map +0 -1
  631. package/src/ui/dist/assets/plus-QuTJyoT3.js +0 -7
  632. package/src/ui/dist/assets/plus-QuTJyoT3.js.map +0 -1
  633. package/src/ui/dist/assets/proxy-D0vUbds5.js.map +0 -1
  634. package/src/ui/dist/assets/refresh-cw-JNmExijd.js.map +0 -1
  635. package/src/ui/dist/assets/terminal-BBQvnOAK.js.map +0 -1
  636. package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js +0 -7
  637. package/src/ui/dist/assets/useReducedMotion-BqmH0Tf7.js.map +0 -1
  638. package/src/ui/dist/assets/x-DoIOGAJz.js.map +0 -1
  639. package/src/ui/pnpm-lock.yaml +0 -1582
  640. package/src/ui/src/App.tsx +0 -86
  641. package/src/ui/src/api.ts +0 -93
  642. package/src/ui/src/components/AgentsRoute.tsx +0 -503
  643. package/src/ui/src/components/Footer.tsx +0 -69
  644. package/src/ui/src/components/Header.tsx +0 -175
  645. package/src/ui/src/components/LoadingView.tsx +0 -22
  646. package/src/ui/src/components/RolesRoute.tsx +0 -169
  647. package/src/ui/src/components/SettingsRoute.tsx +0 -166
  648. package/src/ui/src/components/TaskList.tsx +0 -388
  649. package/src/ui/src/components/TerminalDrawer.tsx +0 -611
  650. package/src/ui/src/components/WorkflowRoute.tsx +0 -670
  651. package/src/ui/src/hooks/useReducedMotion.ts +0 -17
  652. package/src/ui/src/hooks/useServerConnection.ts +0 -114
  653. package/src/ui/src/index.css +0 -281
  654. package/src/ui/src/main.tsx +0 -11
  655. package/src/ui/src/types.ts +0 -108
  656. /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
+ }