@axhub/genie 0.1.8 → 0.2.0

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 (408) hide show
  1. package/README.md +1 -1
  2. package/dist/api-docs.html +6 -6
  3. package/dist/assets/App-BfaNALgf.js +504 -0
  4. package/dist/assets/App-qxJ8_QYu.css +32 -0
  5. package/dist/assets/ReviewApp-DIT2yWk-.js +1 -0
  6. package/dist/assets/_basePickBy-Dz3NcIVK.js +1 -0
  7. package/dist/assets/_baseUniq-DON_Sg7x.js +1 -0
  8. package/dist/assets/abap-BdImnpbu.js +1 -0
  9. package/dist/assets/actionscript-3-CoDkCxhg.js +1 -0
  10. package/dist/assets/ada-bCR0ucgS.js +1 -0
  11. package/dist/assets/andromeeda-C4gqWexZ.js +1 -0
  12. package/dist/assets/angular-html-CU67Zn6k.js +1 -0
  13. package/dist/assets/angular-ts-BwZT4LLn.js +1 -0
  14. package/dist/assets/apache-Pmp26Uib.js +1 -0
  15. package/dist/assets/apex-D8_7TLub.js +1 -0
  16. package/dist/assets/apl-dKokRX4l.js +1 -0
  17. package/dist/assets/applescript-Co6uUVPk.js +1 -0
  18. package/dist/assets/ara-BRHolxvo.js +1 -0
  19. package/dist/assets/arc-Y4G80q-l.js +1 -0
  20. package/dist/assets/architectureDiagram-2XIMDMQ5-D_qR4657.js +36 -0
  21. package/dist/assets/asciidoc-Ve4PFQV2.js +1 -0
  22. package/dist/assets/asm-D_Q5rh1f.js +1 -0
  23. package/dist/assets/astro-CbQHKStN.js +1 -0
  24. package/dist/assets/aurora-x-D-2ljcwZ.js +1 -0
  25. package/dist/assets/awk-DMzUqQB5.js +1 -0
  26. package/dist/assets/ayu-dark-DYE7WIF3.js +1 -0
  27. package/dist/assets/ayu-light-BA47KaF1.js +1 -0
  28. package/dist/assets/ayu-mirage-32ctXXKs.js +1 -0
  29. package/dist/assets/ballerina-BFfxhgS-.js +1 -0
  30. package/dist/assets/bat-BkioyH1T.js +1 -0
  31. package/dist/assets/beancount-k_qm7-4y.js +1 -0
  32. package/dist/assets/berry-uYugtg8r.js +1 -0
  33. package/dist/assets/bibtex-CHM0blh-.js +1 -0
  34. package/dist/assets/bicep-Bmn6On1c.js +1 -0
  35. package/dist/assets/bird2-DPOp833l.js +1 -0
  36. package/dist/assets/blade-D4QpJJKB.js +1 -0
  37. package/dist/assets/blockDiagram-WCTKOSBZ-NsmAlV5_.js +132 -0
  38. package/dist/assets/bsl-BO_Y6i37.js +1 -0
  39. package/dist/assets/c-BIGW1oBm.js +1 -0
  40. package/dist/assets/c3-eo99z4R2.js +1 -0
  41. package/dist/assets/c4Diagram-IC4MRINW-cbOJM4yr.js +10 -0
  42. package/dist/assets/cadence-Bv_4Rxtq.js +1 -0
  43. package/dist/assets/cairo-KRGpt6FW.js +1 -0
  44. package/dist/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  45. package/dist/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  46. package/dist/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  47. package/dist/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  48. package/dist/assets/channel-C6KNnXlA.js +1 -0
  49. package/dist/assets/chunk-4BX2VUAB-bLBhl74J.js +1 -0
  50. package/dist/assets/chunk-55IACEB6-D8kNkDUO.js +1 -0
  51. package/dist/assets/chunk-FMBD7UC4-BjR6UbXB.js +15 -0
  52. package/dist/assets/chunk-JSJVCQXG-luNqWn64.js +1 -0
  53. package/dist/assets/chunk-KX2RTZJC-CNnKm6dK.js +1 -0
  54. package/dist/assets/chunk-NQ4KR5QH-Cp9gb43u.js +220 -0
  55. package/dist/assets/chunk-QZHKN3VN-HlVYo2Oq.js +1 -0
  56. package/dist/assets/chunk-WL4C6EOR-CjSZoOGO.js +189 -0
  57. package/dist/assets/clarity-D53aC0YG.js +1 -0
  58. package/dist/assets/classDiagram-VBA2DB6C-BQlzzlH7.js +1 -0
  59. package/dist/assets/classDiagram-v2-RAHNMMFH-BQlzzlH7.js +1 -0
  60. package/dist/assets/clojure-P80f7IUj.js +1 -0
  61. package/dist/assets/clone-DMxS3qWP.js +1 -0
  62. package/dist/assets/cmake-D1j8_8rp.js +1 -0
  63. package/dist/assets/cobol-nwyudZeR.js +1 -0
  64. package/dist/assets/codeowners-Bp6g37R7.js +1 -0
  65. package/dist/assets/codeql-DsOJ9woJ.js +1 -0
  66. package/dist/assets/coffee-Ch7k5sss.js +1 -0
  67. package/dist/assets/common-lisp-Cg-RD9OK.js +1 -0
  68. package/dist/assets/coq-DkFqJrB1.js +1 -0
  69. package/dist/assets/cose-bilkent-S5V4N54A-DZWRjeEd.js +1 -0
  70. package/dist/assets/cpp-CofmeUqb.js +1 -0
  71. package/dist/assets/crystal-tKQVLTB8.js +1 -0
  72. package/dist/assets/csharp-COcwbKMJ.js +1 -0
  73. package/dist/assets/css-DPfMkruS.js +1 -0
  74. package/dist/assets/csv-fuZLfV_i.js +1 -0
  75. package/dist/assets/cue-D82EKSYY.js +1 -0
  76. package/dist/assets/cypher-COkxafJQ.js +1 -0
  77. package/dist/assets/cytoscape.esm-2ZfV8NB5.js +331 -0
  78. package/dist/assets/d-85-TOEBH.js +1 -0
  79. package/dist/assets/dagre-KLK3FWXG-yAzUmqI7.js +4 -0
  80. package/dist/assets/dark-plus-C3mMm8J8.js +1 -0
  81. package/dist/assets/dart-CF10PKvl.js +1 -0
  82. package/dist/assets/dax-CEL-wOlO.js +1 -0
  83. package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  84. package/dist/assets/desktop-BmXAJ9_W.js +1 -0
  85. package/dist/assets/diagram-E7M64L7V-CvzlIvDJ.js +24 -0
  86. package/dist/assets/diagram-IFDJBPK2-DFMIJpuM.js +43 -0
  87. package/dist/assets/diagram-P4PSJMXO-KL-J3gyb.js +24 -0
  88. package/dist/assets/diff-D97Zzqfu.js +1 -0
  89. package/dist/assets/docker-BcOcwvcX.js +1 -0
  90. package/dist/assets/dotenv-Da5cRb03.js +1 -0
  91. package/dist/assets/dracula-BzJJZx-M.js +1 -0
  92. package/dist/assets/dracula-soft-BXkSAIEj.js +1 -0
  93. package/dist/assets/dream-maker-BtqSS_iP.js +1 -0
  94. package/dist/assets/edge-BkV0erSs.js +1 -0
  95. package/dist/assets/elixir-CDX3lj18.js +1 -0
  96. package/dist/assets/elm-DbKCFpqz.js +1 -0
  97. package/dist/assets/emacs-lisp-C9XAeP06.js +1 -0
  98. package/dist/assets/erDiagram-INFDFZHY-BXszHbTM.js +70 -0
  99. package/dist/assets/erb-B12qg9BL.js +1 -0
  100. package/dist/assets/erlang-DsQrWhSR.js +1 -0
  101. package/dist/assets/everforest-dark-BgDCqdQA.js +1 -0
  102. package/dist/assets/everforest-light-C8M2exoo.js +1 -0
  103. package/dist/assets/fennel-BYunw83y.js +1 -0
  104. package/dist/assets/fish-BvzEVeQv.js +1 -0
  105. package/dist/assets/flowDiagram-PKNHOUZH-Ba43NVp6.js +162 -0
  106. package/dist/assets/fluent-C4IJs8-o.js +1 -0
  107. package/dist/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  108. package/dist/assets/fortran-free-form-BxgE0vQu.js +1 -0
  109. package/dist/assets/fsharp-CXgrBDvD.js +1 -0
  110. package/dist/assets/ganttDiagram-A5KZAMGK-uLHfhCrg.js +292 -0
  111. package/dist/assets/gdresource-BOOCDP_w.js +1 -0
  112. package/dist/assets/gdscript-C5YyOfLZ.js +1 -0
  113. package/dist/assets/gdshader-DkwncUOv.js +1 -0
  114. package/dist/assets/genie-D0YGMca9.js +1 -0
  115. package/dist/assets/gherkin-DyxjwDmM.js +1 -0
  116. package/dist/assets/git-commit-F4YmCXRG.js +1 -0
  117. package/dist/assets/git-rebase-r7XF79zn.js +1 -0
  118. package/dist/assets/gitGraphDiagram-K3NZZRJ6-BTEuFaiL.js +65 -0
  119. package/dist/assets/github-dark-DHJKELXO.js +1 -0
  120. package/dist/assets/github-dark-default-Cuk6v7N8.js +1 -0
  121. package/dist/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  122. package/dist/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  123. package/dist/assets/github-light-DAi9KRSo.js +1 -0
  124. package/dist/assets/github-light-default-D7oLnXFd.js +1 -0
  125. package/dist/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  126. package/dist/assets/gleam-BspZqrRM.js +1 -0
  127. package/dist/assets/glimmer-js-Rg0-pVw9.js +1 -0
  128. package/dist/assets/glimmer-ts-U6CK756n.js +1 -0
  129. package/dist/assets/glsl-DplSGwfg.js +1 -0
  130. package/dist/assets/gn-n2N0HUVH.js +1 -0
  131. package/dist/assets/gnuplot-DdkO51Og.js +1 -0
  132. package/dist/assets/go-CxLEBnE3.js +1 -0
  133. package/dist/assets/graph-h2nuWjx4.js +1 -0
  134. package/dist/assets/graphql-ChdNCCLP.js +1 -0
  135. package/dist/assets/groovy-gcz8RCvz.js +1 -0
  136. package/dist/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  137. package/dist/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  138. package/dist/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  139. package/dist/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  140. package/dist/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  141. package/dist/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  142. package/dist/assets/hack-CaT9iCJl.js +1 -0
  143. package/dist/assets/haml-B8DHNrY2.js +1 -0
  144. package/dist/assets/handlebars-BL8al0AC.js +1 -0
  145. package/dist/assets/haskell-Df6bDoY_.js +1 -0
  146. package/dist/assets/haxe-CzTSHFRz.js +1 -0
  147. package/dist/assets/hcl-BWvSN4gD.js +1 -0
  148. package/dist/assets/highlighted-body-TPN3WLV5-C6BY7XZJ.js +1 -0
  149. package/dist/assets/hjson-D5-asLiD.js +1 -0
  150. package/dist/assets/hlsl-D3lLCCz7.js +1 -0
  151. package/dist/assets/horizon-BUw7H-hv.js +1 -0
  152. package/dist/assets/horizon-bright-Cn-bp-IR.js +1 -0
  153. package/dist/assets/houston-DnULxvSX.js +1 -0
  154. package/dist/assets/html-GMplVEZG.js +1 -0
  155. package/dist/assets/html-derivative-BFtXZ54Q.js +1 -0
  156. package/dist/assets/http-jrhK8wxY.js +1 -0
  157. package/dist/assets/hurl-irOxFIW8.js +1 -0
  158. package/dist/assets/hxml-Bvhsp5Yf.js +1 -0
  159. package/dist/assets/hy-DFXneXwc.js +1 -0
  160. package/dist/assets/imba-DGztddWO.js +1 -0
  161. package/dist/assets/index-2198VgsK.css +1 -0
  162. package/dist/assets/index-C6Bb2jGF.js +2 -0
  163. package/dist/assets/infoDiagram-LFFYTUFH-BOLfvCIq.js +2 -0
  164. package/dist/assets/ini-BEwlwnbL.js +1 -0
  165. package/dist/assets/init-Gi6I4Gst.js +1 -0
  166. package/dist/assets/ishikawaDiagram-PHBUUO56-BRzQ1ee5.js +70 -0
  167. package/dist/assets/java-CylS5w8V.js +1 -0
  168. package/dist/assets/javascript-wDzz0qaB.js +1 -0
  169. package/dist/assets/jinja-4LBKfQ-Z.js +1 -0
  170. package/dist/assets/jison-wvAkD_A8.js +1 -0
  171. package/dist/assets/journeyDiagram-4ABVD52K-DXm_VcMy.js +139 -0
  172. package/dist/assets/json-Cp-IABpG.js +1 -0
  173. package/dist/assets/json5-C9tS-k6U.js +1 -0
  174. package/dist/assets/jsonc-Des-eS-w.js +1 -0
  175. package/dist/assets/jsonl-DcaNXYhu.js +1 -0
  176. package/dist/assets/jsonnet-DFQXde-d.js +1 -0
  177. package/dist/assets/jssm-C2t-YnRu.js +1 -0
  178. package/dist/assets/jsx-g9-lgVsj.js +1 -0
  179. package/dist/assets/julia-CxzCAyBv.js +1 -0
  180. package/dist/assets/just-Cw27pwNe.js +1 -0
  181. package/dist/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  182. package/dist/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  183. package/dist/assets/kanagawa-wave-DWedfzmr.js +1 -0
  184. package/dist/assets/kanban-definition-K7BYSVSG-D_oyzopl.js +89 -0
  185. package/dist/assets/kdl-DV7GczEv.js +1 -0
  186. package/dist/assets/kotlin-BdnUsdx6.js +1 -0
  187. package/dist/assets/kusto-DZf3V79B.js +1 -0
  188. package/dist/assets/laserwave-DUszq2jm.js +1 -0
  189. package/dist/assets/latex-CWtU0Tv5.js +1 -0
  190. package/dist/assets/layout-Q8YoR_E1.js +1 -0
  191. package/dist/assets/lean-BZvkOJ9d.js +1 -0
  192. package/dist/assets/less-B1dDrJ26.js +1 -0
  193. package/dist/assets/light-plus-B7mTdjB0.js +1 -0
  194. package/dist/assets/linear-B3qNg7di.js +1 -0
  195. package/dist/assets/liquid-DYVedYrR.js +1 -0
  196. package/dist/assets/llvm-DjAJT7YJ.js +1 -0
  197. package/dist/assets/log-2UxHyX5q.js +1 -0
  198. package/dist/assets/logo-BtOb2qkB.js +1 -0
  199. package/dist/assets/lua-BaeVxFsk.js +1 -0
  200. package/dist/assets/luau-C-HG3fhB.js +1 -0
  201. package/dist/assets/make-CHLpvVh8.js +1 -0
  202. package/dist/assets/markdown-Cvjx9yec.js +1 -0
  203. package/dist/assets/marko-CnJfTvn9.js +1 -0
  204. package/dist/assets/material-theme-D5KoaKCx.js +1 -0
  205. package/dist/assets/material-theme-darker-BfHTSMKl.js +1 -0
  206. package/dist/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  207. package/dist/assets/material-theme-ocean-CyktbL80.js +1 -0
  208. package/dist/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  209. package/dist/assets/matlab-D7o27uSR.js +1 -0
  210. package/dist/assets/mdc-BMNejdWA.js +1 -0
  211. package/dist/assets/mdx-Cmh6b_Ma.js +1 -0
  212. package/dist/assets/mermaid-O7DHMXV3-BVZ_4MKo.js +988 -0
  213. package/dist/assets/mermaid-mWjccvbQ.js +1 -0
  214. package/dist/assets/min-dark-CafNBF8u.js +1 -0
  215. package/dist/assets/min-light-CTRr51gU.js +1 -0
  216. package/dist/assets/mindmap-definition-YRQLILUH-CjulgYdi.js +68 -0
  217. package/dist/assets/mipsasm-CKIfxQSi.js +1 -0
  218. package/dist/assets/mojo-rZm6bMo-.js +1 -0
  219. package/dist/assets/monokai-D4h5O-jR.js +1 -0
  220. package/dist/assets/moonbit-_H4v1dQx.js +1 -0
  221. package/dist/assets/move-IF9eRakj.js +1 -0
  222. package/dist/assets/narrat-DRg8JJMk.js +1 -0
  223. package/dist/assets/nextflow-Zz6hmt5N.js +1 -0
  224. package/dist/assets/nextflow-groovy-BeH2EWoN.js +1 -0
  225. package/dist/assets/nginx-BpAMiNFr.js +1 -0
  226. package/dist/assets/night-owl-C39BiMTA.js +1 -0
  227. package/dist/assets/night-owl-light-CMTm3GFP.js +1 -0
  228. package/dist/assets/nim-CVrawwO9.js +1 -0
  229. package/dist/assets/nix-CwoSXNpI.js +1 -0
  230. package/dist/assets/nord-Ddv68eIx.js +1 -0
  231. package/dist/assets/nushell-Cz2AlsmD.js +1 -0
  232. package/dist/assets/objective-c-DXmwc3jG.js +1 -0
  233. package/dist/assets/objective-cpp-CLxacb5B.js +1 -0
  234. package/dist/assets/ocaml-C0hk2d4L.js +1 -0
  235. package/dist/assets/odin-BBf5iR-q.js +1 -0
  236. package/dist/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  237. package/dist/assets/one-light-C3Wv6jpd.js +1 -0
  238. package/dist/assets/openscad-C4EeE6gA.js +1 -0
  239. package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  240. package/dist/assets/pascal-D93ZcfNL.js +1 -0
  241. package/dist/assets/perl-C0TMdlhV.js +1 -0
  242. package/dist/assets/php-Dhbhpdrm.js +1 -0
  243. package/dist/assets/pieDiagram-SKSYHLDU-8VzrefxA.js +30 -0
  244. package/dist/assets/pkl-u5AG7uiY.js +1 -0
  245. package/dist/assets/plastic-3e1v2bzS.js +1 -0
  246. package/dist/assets/plsql-ChMvpjG-.js +1 -0
  247. package/dist/assets/po-BTJTHyun.js +1 -0
  248. package/dist/assets/poimandres-CS3Unz2-.js +1 -0
  249. package/dist/assets/polar-C0HS_06l.js +1 -0
  250. package/dist/assets/postcss-CXtECtnM.js +1 -0
  251. package/dist/assets/powerquery-CEu0bR-o.js +1 -0
  252. package/dist/assets/powershell-Dpen1YoG.js +1 -0
  253. package/dist/assets/prisma-Dd19v3D-.js +1 -0
  254. package/dist/assets/prolog-CbFg5uaA.js +1 -0
  255. package/dist/assets/proto-C7zT0LnQ.js +1 -0
  256. package/dist/assets/pug-CGlum2m_.js +1 -0
  257. package/dist/assets/puppet-BMWR74SV.js +1 -0
  258. package/dist/assets/purescript-CklMAg4u.js +1 -0
  259. package/dist/assets/python-B6aJPvgy.js +1 -0
  260. package/dist/assets/qml-3beO22l8.js +1 -0
  261. package/dist/assets/qmldir-C8lEn-DE.js +1 -0
  262. package/dist/assets/qss-IeuSbFQv.js +1 -0
  263. package/dist/assets/quadrantDiagram-337W2JSQ-CFh-ijm2.js +7 -0
  264. package/dist/assets/r-Dspwwk_N.js +1 -0
  265. package/dist/assets/racket-BqYA7rlc.js +1 -0
  266. package/dist/assets/raku-DXvB9xmW.js +1 -0
  267. package/dist/assets/razor-Uh8Bk_45.js +1 -0
  268. package/dist/assets/red-bN70gL4F.js +1 -0
  269. package/dist/assets/reg-C-SQnVFl.js +1 -0
  270. package/dist/assets/regexp-CDVJQ6XC.js +1 -0
  271. package/dist/assets/rel-C3B-1QV4.js +1 -0
  272. package/dist/assets/requirementDiagram-Z7DCOOCP-BNPlTs5Q.js +73 -0
  273. package/dist/assets/riscv-BM1_JUlF.js +1 -0
  274. package/dist/assets/ron-D8l8udqQ.js +1 -0
  275. package/dist/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  276. package/dist/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  277. package/dist/assets/rose-pine-qdsjHGoJ.js +1 -0
  278. package/dist/assets/rosmsg-BJDFO7_C.js +1 -0
  279. package/dist/assets/rst-BrH8l1NY.js +1 -0
  280. package/dist/assets/ruby-Dw2BHqvy.js +1 -0
  281. package/dist/assets/rust-B1yitclQ.js +1 -0
  282. package/dist/assets/sankeyDiagram-WA2Y5GQK-C5l_hYst.js +10 -0
  283. package/dist/assets/sas-cz2c8ADy.js +1 -0
  284. package/dist/assets/sass-Cj5Yp3dK.js +1 -0
  285. package/dist/assets/scala-C151Ov-r.js +1 -0
  286. package/dist/assets/scheme-C98Dy4si.js +1 -0
  287. package/dist/assets/scss-OYdSNvt2.js +1 -0
  288. package/dist/assets/sdbl-DVxCFoDh.js +1 -0
  289. package/dist/assets/sequenceDiagram-2WXFIKYE-B4a_rQw8.js +145 -0
  290. package/dist/assets/shaderlab-Dg9Lc6iA.js +1 -0
  291. package/dist/assets/shellscript-Yzrsuije.js +1 -0
  292. package/dist/assets/shellsession-BADoaaVG.js +1 -0
  293. package/dist/assets/slack-dark-BthQWCQV.js +1 -0
  294. package/dist/assets/slack-ochin-DqwNpetd.js +1 -0
  295. package/dist/assets/smalltalk-BERRCDM3.js +1 -0
  296. package/dist/assets/snazzy-light-Bw305WKR.js +1 -0
  297. package/dist/assets/solarized-dark-DXbdFlpD.js +1 -0
  298. package/dist/assets/solarized-light-L9t79GZl.js +1 -0
  299. package/dist/assets/solidity-rGO070M0.js +1 -0
  300. package/dist/assets/soy-Brmx7dQM.js +1 -0
  301. package/dist/assets/sparql-rVzFXLq3.js +1 -0
  302. package/dist/assets/splunk-BtCnVYZw.js +1 -0
  303. package/dist/assets/sql-BLtJtn59.js +1 -0
  304. package/dist/assets/ssh-config-_ykCGR6B.js +1 -0
  305. package/dist/assets/stata-BH5u7GGu.js +1 -0
  306. package/dist/assets/stateDiagram-RAJIS63D-Bt4mMmKB.js +1 -0
  307. package/dist/assets/stateDiagram-v2-FVOUBMTO-6NYMazfq.js +1 -0
  308. package/dist/assets/stylus-BEDo0Tqx.js +1 -0
  309. package/dist/assets/surrealql-Bq5Q-fJD.js +1 -0
  310. package/dist/assets/svelte-C_ipcX3V.js +1 -0
  311. package/dist/assets/swift-D82vCrfD.js +1 -0
  312. package/dist/assets/synthwave-84-CbfX1IO0.js +1 -0
  313. package/dist/assets/system-verilog-CnnmHF94.js +1 -0
  314. package/dist/assets/systemd-4A_iFExJ.js +1 -0
  315. package/dist/assets/talonscript-CkByrt1z.js +1 -0
  316. package/dist/assets/tasl-QIJgUcNo.js +1 -0
  317. package/dist/assets/tcl-dwOrl1Do.js +1 -0
  318. package/dist/assets/templ-P3uqSqPl.js +1 -0
  319. package/dist/assets/terraform-BETggiCN.js +1 -0
  320. package/dist/assets/tex-idrVyKtj.js +1 -0
  321. package/dist/assets/timeline-definition-YZTLITO2-CLYvSw_R.js +61 -0
  322. package/dist/assets/tokyo-night-hegEt444.js +1 -0
  323. package/dist/assets/toml-vGWfd6FD.js +1 -0
  324. package/dist/assets/treemap-KZPCXAKY-ksND0hZK.js +162 -0
  325. package/dist/assets/ts-tags-zn1MmPIZ.js +1 -0
  326. package/dist/assets/tsv-B_m7g4N7.js +1 -0
  327. package/dist/assets/tsx-COt5Ahok.js +1 -0
  328. package/dist/assets/turtle-BsS91CYL.js +1 -0
  329. package/dist/assets/twig-DNn4PbVi.js +1 -0
  330. package/dist/assets/typescript-BPQ3VLAy.js +1 -0
  331. package/dist/assets/typespec-BGHnOYBU.js +1 -0
  332. package/dist/assets/typst-DHCkPAjA.js +1 -0
  333. package/dist/assets/v-BcVCzyr7.js +1 -0
  334. package/dist/assets/vala-CsfeWuGM.js +1 -0
  335. package/dist/assets/vb-D17OF-Vu.js +1 -0
  336. package/dist/assets/{vendor-codemirror-B88_OPWf.js → vendor-codemirror-Dz7_EqNA.js} +3 -3
  337. package/dist/assets/{vendor-react-C3RJLQGO.js → vendor-react-Cpt6D04s.js} +11 -11
  338. package/dist/assets/vennDiagram-LZ73GAT5-CaQg4oZK.js +34 -0
  339. package/dist/assets/verilog-BQ8w6xss.js +1 -0
  340. package/dist/assets/vesper-DU1UobuO.js +1 -0
  341. package/dist/assets/vhdl-CeAyd5Ju.js +1 -0
  342. package/dist/assets/viml-CJc9bBzg.js +1 -0
  343. package/dist/assets/vitesse-black-Bkuqu6BP.js +1 -0
  344. package/dist/assets/vitesse-dark-D0r3Knsf.js +1 -0
  345. package/dist/assets/vitesse-light-CVO1_9PV.js +1 -0
  346. package/dist/assets/vue-DN_0RTcg.js +1 -0
  347. package/dist/assets/vue-html-AaS7Mt5G.js +1 -0
  348. package/dist/assets/vue-vine-CQOfvN7w.js +1 -0
  349. package/dist/assets/vyper-CDx5xZoG.js +1 -0
  350. package/dist/assets/wasm-CG6Dc4jp.js +1 -0
  351. package/dist/assets/wasm-MzD3tlZU.js +1 -0
  352. package/dist/assets/wenyan-BV7otONQ.js +1 -0
  353. package/dist/assets/wgsl-Dx-B1_4e.js +1 -0
  354. package/dist/assets/wikitext-BhOHFoWU.js +1 -0
  355. package/dist/assets/wit-5i3qLPDT.js +1 -0
  356. package/dist/assets/wolfram-lXgVvXCa.js +1 -0
  357. package/dist/assets/xml-sdJ4AIDG.js +1 -0
  358. package/dist/assets/xsl-CtQFsRM5.js +1 -0
  359. package/dist/assets/xychartDiagram-JWTSCODW-C8dCbTeM.js +7 -0
  360. package/dist/assets/yaml-Buea-lGh.js +1 -0
  361. package/dist/assets/zenscript-DVFEvuxE.js +1 -0
  362. package/dist/assets/zig-VOosw3JB.js +1 -0
  363. package/dist/index.html +35 -21
  364. package/dist/manifest.json +1 -1
  365. package/dist/sw.js +18 -46
  366. package/package.json +12 -8
  367. package/server/bin/codex-sdk-wrapper.js +49 -0
  368. package/server/channels/runtime/AgentRuntimeAdapter.js +2 -5
  369. package/server/channels/store/ChannelStore.js +73 -107
  370. package/server/claude-sdk.js +160 -6
  371. package/server/cli.js +590 -32
  372. package/server/cli.test.js +76 -0
  373. package/server/database/db.js +438 -372
  374. package/server/external-agent/auth.js +88 -0
  375. package/server/external-agent/service.js +1052 -0
  376. package/server/external-agent/service.test.js +41 -0
  377. package/server/external-agent/ws.js +1526 -0
  378. package/server/external-agent/ws.test.js +289 -0
  379. package/server/gemini-cli.js +108 -20
  380. package/server/index.js +115 -121
  381. package/server/load-env.js +16 -13
  382. package/server/openai-codex.js +165 -11
  383. package/server/opencode-cli.js +3 -2
  384. package/server/projects.js +432 -338
  385. package/server/routes/agent.js +347 -459
  386. package/server/routes/auth.js +14 -36
  387. package/server/routes/cli-auth.js +60 -113
  388. package/server/routes/commands.js +16 -26
  389. package/server/routes/git.js +5 -16
  390. package/server/routes/projects.js +2 -7
  391. package/server/routes/session-core.js +177 -0
  392. package/server/session-core/abortSession.js +48 -0
  393. package/server/session-core/eventStore.js +139 -0
  394. package/server/session-core/providerAdapters.js +84 -0
  395. package/server/session-core/providerDiscovery.js +235 -0
  396. package/server/session-core/runtimeState.js +390 -0
  397. package/server/session-core/runtimeWriter.js +59 -0
  398. package/server/utils/agentCallback.js +273 -0
  399. package/server/utils/agentImages.js +253 -0
  400. package/server/utils/codexPath.js +47 -0
  401. package/server/utils/defaultWorkingDirectory.js +34 -0
  402. package/shared/conversationEvents.js +1071 -0
  403. package/shared/modelConstants.js +18 -24
  404. package/dist/assets/index-CVjMty4a.js +0 -902
  405. package/dist/assets/index-eo5scY_Z.css +0 -32
  406. package/server/cursor-cli.js +0 -276
  407. package/server/database/init.sql +0 -98
  408. package/server/routes/cursor.js +0 -795
@@ -0,0 +1,1526 @@
1
+ import { WebSocket } from 'ws';
2
+
3
+ import { SessionEventMirrorWriter } from '../session-core/runtimeWriter.js';
4
+ import { abortAgentSessionWithWriter } from '../session-core/abortSession.js';
5
+ import {
6
+ AgentSessionAbortedError,
7
+ buildSessionNavigation,
8
+ normalizeExternalAgentAbortRequest,
9
+ normalizeExternalAgentRunRequest,
10
+ runExternalAgentRequest
11
+ } from './service.js';
12
+ import {
13
+ buildSessionRuntimeSnapshot,
14
+ createSessionRuntimeSubscriptionKey,
15
+ subscribeToSessionRuntimeStateChanges
16
+ } from '../session-core/runtimeState.js';
17
+ import { SUPPORTED_PROVIDERS } from '../../shared/conversationEvents.js';
18
+ import { materializeDataUrlImageToFile, sanitizeFileName } from '../utils/agentImages.js';
19
+
20
+ const INTEGRATION_ROLE_FRONTEND_PAGE = 'frontend-page';
21
+ const INTEGRATION_ROLE_EXTERNAL_CLIENT = 'external-client';
22
+ const INTEGRATION_REQUEST_TTL_MS = 10 * 60 * 1000;
23
+ const INTEGRATION_PING_TIMEOUT_MS = 1500;
24
+ const INTEGRATION_EDITOR_REQUEST_TIMEOUT_MS = 15000;
25
+ const INTEGRATION_EDITOR_TYPE_PREFIX = 'integration.editor.';
26
+ const INTEGRATION_EDITOR_RESULT_SUFFIX = '.result';
27
+ const INTEGRATION_EDITOR_CAPABILITIES = Object.freeze({
28
+ 'integration.editor.snapshot.get': 'editor.snapshot',
29
+ 'integration.editor.nodes.list': 'editor.nodes.list',
30
+ 'integration.editor.node.screenshot.get': 'editor.node.screenshot',
31
+ 'integration.editor.context-images.get': 'editor.context-images',
32
+ 'integration.editor.editing.set': 'editor.editing.set'
33
+ });
34
+
35
+ const integrationConnections = new Map();
36
+ const pendingIntegrationRequests = new Map();
37
+ const pendingIntegrationPings = new Map();
38
+ const sessionStateSubscriptionsByWs = new Map();
39
+ const sessionStateSubscribersByKey = new Map();
40
+
41
+ function sendJson(ws, payload) {
42
+ if (ws.readyState === WebSocket.OPEN) {
43
+ ws.send(JSON.stringify(payload));
44
+ }
45
+ }
46
+
47
+ function normalizeNonEmptyString(value) {
48
+ if (typeof value !== 'string') {
49
+ return null;
50
+ }
51
+
52
+ const normalized = value.trim();
53
+ return normalized ? normalized : null;
54
+ }
55
+
56
+ function createRuntimeId(prefix = 'integration') {
57
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
58
+ return `${prefix}_${crypto.randomUUID()}`;
59
+ }
60
+
61
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
62
+ }
63
+
64
+ function isPlainObject(value) {
65
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
66
+ }
67
+
68
+ function normalizeOptionalString(value) {
69
+ if (typeof value !== 'string') {
70
+ return null;
71
+ }
72
+ return value.trim();
73
+ }
74
+
75
+ function normalizePositiveInteger(value) {
76
+ const next = Number(value);
77
+ if (!Number.isInteger(next) || next <= 0) {
78
+ return null;
79
+ }
80
+ return next;
81
+ }
82
+
83
+ function isEditorResultMessageType(type) {
84
+ return typeof type === 'string'
85
+ && type.startsWith(INTEGRATION_EDITOR_TYPE_PREFIX)
86
+ && type.endsWith(INTEGRATION_EDITOR_RESULT_SUFFIX);
87
+ }
88
+
89
+ function getRequestId(data) {
90
+ return normalizeNonEmptyString(data?.requestId) || null;
91
+ }
92
+
93
+ function sendRequestError(ws, { requestId, provider, sessionId = null, error, statusCode = 400 }) {
94
+ sendJson(ws, {
95
+ type: 'agent.error',
96
+ requestId: requestId || null,
97
+ provider: provider || null,
98
+ sessionId,
99
+ error,
100
+ statusCode
101
+ });
102
+ }
103
+
104
+ function sendIntegrationError(ws, { requestId, code, message }) {
105
+ sendJson(ws, {
106
+ type: 'integration.error',
107
+ requestId: requestId || null,
108
+ payload: {
109
+ code,
110
+ message
111
+ }
112
+ });
113
+ }
114
+
115
+ function createAgentStateError(ws, { requestId, provider, sessionId, error, statusCode = 400 }) {
116
+ sendRequestError(ws, {
117
+ requestId,
118
+ provider,
119
+ sessionId,
120
+ error,
121
+ statusCode
122
+ });
123
+ }
124
+
125
+ function normalizeAgentStatePayload(payload) {
126
+ if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
127
+ throw Object.assign(new Error('payload must be an object'), { statusCode: 400 });
128
+ }
129
+
130
+ const sessionId = normalizeNonEmptyString(payload.sessionId);
131
+ if (!sessionId) {
132
+ throw Object.assign(new Error('sessionId is required'), { statusCode: 400 });
133
+ }
134
+
135
+ const provider = normalizeNonEmptyString(payload.provider)?.toLowerCase() || null;
136
+ if (!provider) {
137
+ throw Object.assign(new Error('provider is required'), { statusCode: 400 });
138
+ }
139
+
140
+ if (!SUPPORTED_PROVIDERS.includes(provider)) {
141
+ throw Object.assign(new Error(`provider must be one of: ${SUPPORTED_PROVIDERS.join(', ')}`), { statusCode: 400 });
142
+ }
143
+
144
+ return {
145
+ sessionId,
146
+ provider
147
+ };
148
+ }
149
+
150
+ function addSessionStateSubscription(ws, subscriptionKey) {
151
+ if (!subscriptionKey) {
152
+ return;
153
+ }
154
+
155
+ if (!sessionStateSubscriptionsByWs.has(ws)) {
156
+ sessionStateSubscriptionsByWs.set(ws, new Set());
157
+ }
158
+ sessionStateSubscriptionsByWs.get(ws).add(subscriptionKey);
159
+
160
+ if (!sessionStateSubscribersByKey.has(subscriptionKey)) {
161
+ sessionStateSubscribersByKey.set(subscriptionKey, new Set());
162
+ }
163
+ sessionStateSubscribersByKey.get(subscriptionKey).add(ws);
164
+ }
165
+
166
+ function removeSessionStateSubscriptionsForWs(ws) {
167
+ const subscriptionKeys = sessionStateSubscriptionsByWs.get(ws);
168
+ if (!subscriptionKeys) {
169
+ return;
170
+ }
171
+
172
+ subscriptionKeys.forEach((subscriptionKey) => {
173
+ const subscribers = sessionStateSubscribersByKey.get(subscriptionKey);
174
+ if (!subscribers) {
175
+ return;
176
+ }
177
+
178
+ subscribers.delete(ws);
179
+ if (subscribers.size === 0) {
180
+ sessionStateSubscribersByKey.delete(subscriptionKey);
181
+ }
182
+ });
183
+
184
+ sessionStateSubscriptionsByWs.delete(ws);
185
+ }
186
+
187
+ async function handleAgentStateQuery(ws, data) {
188
+ const requestId = getRequestId(data);
189
+
190
+ let normalized = null;
191
+ try {
192
+ normalized = normalizeAgentStatePayload(data?.payload);
193
+ } catch (error) {
194
+ createAgentStateError(ws, {
195
+ requestId,
196
+ provider: data?.payload?.provider || null,
197
+ sessionId: data?.payload?.sessionId || null,
198
+ error: error.message,
199
+ statusCode: error.statusCode || 400
200
+ });
201
+ return;
202
+ }
203
+
204
+ const snapshot = await buildSessionRuntimeSnapshot(normalized.provider, normalized.sessionId);
205
+ if (!snapshot) {
206
+ createAgentStateError(ws, {
207
+ requestId,
208
+ provider: normalized.provider,
209
+ sessionId: normalized.sessionId,
210
+ error: 'Session not found for provider',
211
+ statusCode: 404
212
+ });
213
+ return;
214
+ }
215
+
216
+ sendJson(ws, {
217
+ type: 'agent.state.snapshot',
218
+ requestId,
219
+ payload: {
220
+ sessionId: snapshot.sessionId,
221
+ provider: snapshot.provider,
222
+ phase: snapshot.phase,
223
+ isLoading: snapshot.isLoading,
224
+ canAbortSession: snapshot.canAbortSession,
225
+ hasPendingApproval: snapshot.hasPendingApproval,
226
+ updatedAt: snapshot.updatedAt
227
+ }
228
+ });
229
+ }
230
+
231
+ async function handleAgentStateSubscribe(ws, data) {
232
+ const requestId = getRequestId(data);
233
+
234
+ let normalized = null;
235
+ try {
236
+ normalized = normalizeAgentStatePayload(data?.payload);
237
+ } catch (error) {
238
+ createAgentStateError(ws, {
239
+ requestId,
240
+ provider: data?.payload?.provider || null,
241
+ sessionId: data?.payload?.sessionId || null,
242
+ error: error.message,
243
+ statusCode: error.statusCode || 400
244
+ });
245
+ return;
246
+ }
247
+
248
+ const snapshot = await buildSessionRuntimeSnapshot(normalized.provider, normalized.sessionId);
249
+ if (!snapshot) {
250
+ createAgentStateError(ws, {
251
+ requestId,
252
+ provider: normalized.provider,
253
+ sessionId: normalized.sessionId,
254
+ error: 'Session not found for provider',
255
+ statusCode: 404
256
+ });
257
+ return;
258
+ }
259
+
260
+ const subscriptionKey = createSessionRuntimeSubscriptionKey(normalized.provider, normalized.sessionId);
261
+ addSessionStateSubscription(ws, subscriptionKey);
262
+
263
+ sendJson(ws, {
264
+ type: 'agent.state.subscribed',
265
+ requestId,
266
+ payload: {
267
+ sessionId: snapshot.sessionId,
268
+ provider: snapshot.provider,
269
+ subscriptionKey
270
+ }
271
+ });
272
+ }
273
+
274
+ function cleanupExpiredPendingIntegrationRequests() {
275
+ const now = Date.now();
276
+ for (const [requestId, entry] of pendingIntegrationRequests.entries()) {
277
+ if (!entry?.createdAt || now - entry.createdAt > INTEGRATION_REQUEST_TTL_MS) {
278
+ if (entry?.timeoutId) {
279
+ clearTimeout(entry.timeoutId);
280
+ }
281
+ pendingIntegrationRequests.delete(requestId);
282
+ }
283
+ }
284
+ }
285
+
286
+ function clearPendingIntegrationRequest(requestId) {
287
+ const entry = pendingIntegrationRequests.get(requestId);
288
+ if (entry?.timeoutId) {
289
+ clearTimeout(entry.timeoutId);
290
+ }
291
+ pendingIntegrationRequests.delete(requestId);
292
+ return entry || null;
293
+ }
294
+
295
+ function storePendingIntegrationRequest(requestId, originWs, options = {}) {
296
+ if (!requestId) {
297
+ return;
298
+ }
299
+
300
+ cleanupExpiredPendingIntegrationRequests();
301
+ const timeoutMs = normalizePositiveInteger(options.timeoutMs) || INTEGRATION_EDITOR_REQUEST_TIMEOUT_MS;
302
+ const nextEntry = {
303
+ originWs,
304
+ createdAt: Date.now(),
305
+ timeoutId: null,
306
+ awaitResult: options.awaitResult === true,
307
+ requestType: options.requestType || null,
308
+ requestPayload: isPlainObject(options.requestPayload) ? { ...options.requestPayload } : null
309
+ };
310
+ nextEntry.timeoutId = setTimeout(() => {
311
+ const current = pendingIntegrationRequests.get(requestId);
312
+ if (current !== nextEntry) {
313
+ return;
314
+ }
315
+ pendingIntegrationRequests.delete(requestId);
316
+ if (!originWs || originWs.readyState !== WebSocket.OPEN) {
317
+ return;
318
+ }
319
+ sendIntegrationError(originWs, {
320
+ requestId,
321
+ code: 'REQUEST_TIMEOUT',
322
+ message: `Timed out waiting for response to ${options.requestType || 'integration request'}`
323
+ });
324
+ }, timeoutMs);
325
+ pendingIntegrationRequests.set(requestId, nextEntry);
326
+ }
327
+
328
+ function removePendingIntegrationRequestsForWs(ws) {
329
+ for (const [requestId, entry] of pendingIntegrationRequests.entries()) {
330
+ if (entry?.originWs === ws) {
331
+ clearPendingIntegrationRequest(requestId);
332
+ }
333
+ }
334
+ }
335
+
336
+ function touchIntegrationConnection(ws) {
337
+ const existing = integrationConnections.get(ws);
338
+ if (!existing) {
339
+ return null;
340
+ }
341
+
342
+ const updated = {
343
+ ...existing,
344
+ lastSeenAt: new Date().toISOString()
345
+ };
346
+ integrationConnections.set(ws, updated);
347
+ return updated;
348
+ }
349
+
350
+ function listIntegrationConnections({ role = null, channel = null, clientId = null } = {}) {
351
+ return Array.from(integrationConnections.values()).filter((meta) => {
352
+ if (role && meta.role !== role) {
353
+ return false;
354
+ }
355
+
356
+ if (channel && meta.channel !== channel) {
357
+ return false;
358
+ }
359
+
360
+ if (clientId && meta.clientId !== clientId) {
361
+ return false;
362
+ }
363
+
364
+ return true;
365
+ });
366
+ }
367
+
368
+ function buildEditorClientSummary(meta) {
369
+ return {
370
+ channel: meta.channel,
371
+ clientId: meta.clientId,
372
+ sessionId: meta.sessionId || null,
373
+ pageUrl: meta.pageUrl || null,
374
+ lastSeenAt: meta.lastSeenAt || null,
375
+ connectedAt: meta.connectedAt || null,
376
+ connectionId: meta.connectionId || null,
377
+ capabilities: Array.isArray(meta.capabilities) ? meta.capabilities.slice() : []
378
+ };
379
+ }
380
+
381
+ function sanitizeFallbackBaseName(value, fallback = 'image') {
382
+ return sanitizeFileName(String(value || '').trim()) || fallback;
383
+ }
384
+
385
+ async function materializeEditorScreenshotPayload(payload, pendingRequest) {
386
+ if (!isPlainObject(payload)) {
387
+ throw Object.assign(new Error('editor screenshot result payload must be an object'), {
388
+ code: 'INVALID_PAYLOAD'
389
+ });
390
+ }
391
+
392
+ if (typeof payload.absolutePath === 'string' && payload.absolutePath.trim()) {
393
+ return payload;
394
+ }
395
+
396
+ const imageSource = isPlainObject(payload.image) ? payload.image : payload;
397
+ if (typeof imageSource.data !== 'string' || !imageSource.data.trim()) {
398
+ throw Object.assign(new Error('editor screenshot result must include image.data as a data URL'), {
399
+ code: 'INVALID_PAYLOAD'
400
+ });
401
+ }
402
+
403
+ const outputDir = normalizeOptionalString(pendingRequest?.requestPayload?.downloadPath) || process.cwd();
404
+ const elementKey = normalizeOptionalString(payload.elementKey)
405
+ || normalizeOptionalString(pendingRequest?.requestPayload?.elementKey)
406
+ || 'node-screenshot';
407
+ const fileMeta = await materializeDataUrlImageToFile(imageSource, outputDir, {
408
+ label: 'editor screenshot',
409
+ fallbackBaseName: sanitizeFallbackBaseName(elementKey, 'node-screenshot')
410
+ });
411
+
412
+ return {
413
+ ...payload,
414
+ elementKey,
415
+ absolutePath: fileMeta.absolutePath,
416
+ mimeType: fileMeta.mimeType,
417
+ width: normalizePositiveInteger(payload.width) || normalizePositiveInteger(imageSource.width) || null,
418
+ height: normalizePositiveInteger(payload.height) || normalizePositiveInteger(imageSource.height) || null,
419
+ size: fileMeta.size
420
+ };
421
+ }
422
+
423
+ async function materializeEditorContextImagesPayload(payload, pendingRequest) {
424
+ if (!isPlainObject(payload) || !Array.isArray(payload.items)) {
425
+ throw Object.assign(new Error('editor context-images result payload must include items[]'), {
426
+ code: 'INVALID_PAYLOAD'
427
+ });
428
+ }
429
+
430
+ if (payload.items.every((item) => typeof item?.absolutePath === 'string' && item.absolutePath.trim())) {
431
+ return payload;
432
+ }
433
+
434
+ const outputDir = normalizeOptionalString(pendingRequest?.requestPayload?.downloadPath) || process.cwd();
435
+ const nextItems = [];
436
+
437
+ for (const [index, item] of payload.items.entries()) {
438
+ if (!isPlainObject(item)) {
439
+ throw Object.assign(new Error(`editor context image item ${index} must be an object`), {
440
+ code: 'INVALID_PAYLOAD'
441
+ });
442
+ }
443
+
444
+ const fileMeta = await materializeDataUrlImageToFile(item, outputDir, {
445
+ label: `editor context image item ${index}`,
446
+ fallbackBaseName: sanitizeFallbackBaseName(item.name || item.id || `context-image-${index + 1}`, `context-image-${index + 1}`)
447
+ });
448
+
449
+ nextItems.push({
450
+ ...item,
451
+ id: normalizeOptionalString(item.id) || `context-image-${index + 1}`,
452
+ name: normalizeOptionalString(item.name) || sanitizeFallbackBaseName(item.id || `context-image-${index + 1}`, `context-image-${index + 1}`),
453
+ absolutePath: fileMeta.absolutePath,
454
+ mimeType: fileMeta.mimeType,
455
+ size: fileMeta.size,
456
+ createdAt: Number.isFinite(Number(item.createdAt)) ? Number(item.createdAt) : Date.now(),
457
+ source: normalizeOptionalString(item.source) || 'prompt-context'
458
+ });
459
+ }
460
+
461
+ return {
462
+ ...payload,
463
+ items: nextItems
464
+ };
465
+ }
466
+
467
+ async function maybeMaterializeEditorResult(data, pendingRequest) {
468
+ if (!pendingRequest?.requestType || !isPlainObject(data)) {
469
+ return data;
470
+ }
471
+
472
+ if (
473
+ pendingRequest.requestType === 'integration.editor.node.screenshot.get'
474
+ && data.type === 'integration.editor.node.screenshot.result'
475
+ ) {
476
+ return {
477
+ ...data,
478
+ payload: await materializeEditorScreenshotPayload(data.payload, pendingRequest)
479
+ };
480
+ }
481
+
482
+ if (
483
+ pendingRequest.requestType === 'integration.editor.context-images.get'
484
+ && data.type === 'integration.editor.context-images.result'
485
+ ) {
486
+ return {
487
+ ...data,
488
+ payload: await materializeEditorContextImagesPayload(data.payload, pendingRequest)
489
+ };
490
+ }
491
+
492
+ return data;
493
+ }
494
+
495
+ function buildPresenceEventPayload(meta, event) {
496
+ return {
497
+ channel: meta.channel,
498
+ event,
499
+ clientId: meta.clientId,
500
+ sessionId: meta.sessionId || null,
501
+ pageUrl: meta.pageUrl || null,
502
+ timestamp: new Date().toISOString()
503
+ };
504
+ }
505
+
506
+ function broadcastPresenceEvent(meta, event) {
507
+ if (!meta?.channel) {
508
+ return;
509
+ }
510
+
511
+ const payload = buildPresenceEventPayload(meta, event);
512
+ for (const clientMeta of listIntegrationConnections({
513
+ role: INTEGRATION_ROLE_EXTERNAL_CLIENT,
514
+ channel: meta.channel
515
+ })) {
516
+ sendJson(clientMeta.ws, {
517
+ type: 'integration.presence',
518
+ requestId: createRuntimeId('presence'),
519
+ payload
520
+ });
521
+ }
522
+ }
523
+
524
+ function unregisterIntegrationConnection(ws) {
525
+ const existing = integrationConnections.get(ws);
526
+ removeSessionStateSubscriptionsForWs(ws);
527
+ if (!existing) {
528
+ removePendingIntegrationRequestsForWs(ws);
529
+ removePendingIntegrationPingsForWs(ws);
530
+ return;
531
+ }
532
+
533
+ integrationConnections.delete(ws);
534
+ removePendingIntegrationRequestsForWs(ws);
535
+ removePendingIntegrationPingsForWs(ws);
536
+
537
+ if (existing.role === INTEGRATION_ROLE_FRONTEND_PAGE) {
538
+ broadcastPresenceEvent(existing, 'frontend-offline');
539
+ }
540
+ }
541
+
542
+ function validateIntegrationConnectPayload(payload) {
543
+ if (!payload || typeof payload !== 'object') {
544
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
545
+ }
546
+
547
+ const role = normalizeNonEmptyString(payload.role);
548
+ if (role !== INTEGRATION_ROLE_FRONTEND_PAGE && role !== INTEGRATION_ROLE_EXTERNAL_CLIENT) {
549
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'role must be frontend-page or external-client' };
550
+ }
551
+
552
+ const channel = normalizeNonEmptyString(payload.channel);
553
+ if (!channel) {
554
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'channel is required' };
555
+ }
556
+
557
+ const clientId = normalizeNonEmptyString(payload.clientId);
558
+ if (!clientId) {
559
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'clientId is required' };
560
+ }
561
+
562
+ return {
563
+ ok: true,
564
+ payload: {
565
+ role,
566
+ channel,
567
+ clientId,
568
+ pageUrl: normalizeNonEmptyString(payload.pageUrl) || null,
569
+ sessionId: normalizeNonEmptyString(payload.sessionId) || null,
570
+ capabilities: Array.isArray(payload.capabilities)
571
+ ? payload.capabilities.filter((value) => typeof value === 'string' && value.trim())
572
+ : []
573
+ }
574
+ };
575
+ }
576
+
577
+ function handleIntegrationConnect(ws, data) {
578
+ const requestId = getRequestId(data);
579
+ const validation = validateIntegrationConnectPayload(data?.payload);
580
+ if (!validation.ok) {
581
+ sendIntegrationError(ws, {
582
+ requestId,
583
+ code: validation.code,
584
+ message: validation.message
585
+ });
586
+ return;
587
+ }
588
+
589
+ const now = new Date().toISOString();
590
+ const nextMeta = {
591
+ ...validation.payload,
592
+ ws,
593
+ connectionId: integrationConnections.get(ws)?.connectionId || createRuntimeId('conn'),
594
+ connectedAt: integrationConnections.get(ws)?.connectedAt || now,
595
+ lastSeenAt: now
596
+ };
597
+
598
+ const previous = integrationConnections.get(ws) || null;
599
+ integrationConnections.set(ws, nextMeta);
600
+
601
+ sendJson(ws, {
602
+ type: 'integration.connected',
603
+ requestId,
604
+ payload: {
605
+ connectionId: nextMeta.connectionId,
606
+ role: nextMeta.role,
607
+ channel: nextMeta.channel,
608
+ clientId: nextMeta.clientId,
609
+ serverTime: now
610
+ }
611
+ });
612
+
613
+ if (previous?.role === INTEGRATION_ROLE_FRONTEND_PAGE) {
614
+ const previousKey = `${previous.channel}::${previous.clientId}`;
615
+ const nextKey = `${nextMeta.channel}::${nextMeta.clientId}`;
616
+ if (previousKey !== nextKey) {
617
+ broadcastPresenceEvent(previous, 'frontend-offline');
618
+ }
619
+ }
620
+
621
+ if (nextMeta.role === INTEGRATION_ROLE_FRONTEND_PAGE) {
622
+ const previousKey = previous ? `${previous.channel}::${previous.clientId}::${previous.role}` : null;
623
+ const nextKey = `${nextMeta.channel}::${nextMeta.clientId}::${nextMeta.role}`;
624
+ if (previousKey !== nextKey) {
625
+ broadcastPresenceEvent(nextMeta, 'frontend-online');
626
+ }
627
+ }
628
+ }
629
+
630
+ function buildFrontendPresence(channel, targetClientId = null) {
631
+ const clients = listIntegrationConnections({
632
+ role: INTEGRATION_ROLE_FRONTEND_PAGE,
633
+ channel,
634
+ clientId: targetClientId
635
+ }).map((meta) => ({
636
+ clientId: meta.clientId,
637
+ lastSeenAt: meta.lastSeenAt,
638
+ pageUrl: meta.pageUrl || null,
639
+ sessionId: meta.sessionId || null
640
+ }));
641
+
642
+ return {
643
+ connected: clients.length > 0,
644
+ count: clients.length,
645
+ clients
646
+ };
647
+ }
648
+
649
+ function validateIntegrationPingPayload(payload) {
650
+ if (!payload || typeof payload !== 'object') {
651
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
652
+ }
653
+
654
+ const channel = normalizeNonEmptyString(payload.channel);
655
+ if (!channel) {
656
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'channel is required' };
657
+ }
658
+
659
+ return {
660
+ ok: true,
661
+ payload: {
662
+ channel,
663
+ targetClientId: normalizeNonEmptyString(payload.targetClientId)
664
+ }
665
+ };
666
+ }
667
+
668
+ function buildResponsiveFrontendPresence(responses) {
669
+ const clients = Array.from(responses.values());
670
+ return {
671
+ connected: clients.length > 0,
672
+ count: clients.length,
673
+ clients
674
+ };
675
+ }
676
+
677
+ function finalizePendingIntegrationPing(probeRequestId) {
678
+ const pendingPing = pendingIntegrationPings.get(probeRequestId);
679
+ if (!pendingPing) {
680
+ return false;
681
+ }
682
+
683
+ pendingIntegrationPings.delete(probeRequestId);
684
+ if (pendingPing.timeoutId) {
685
+ clearTimeout(pendingPing.timeoutId);
686
+ }
687
+
688
+ if (!pendingPing.originWs || pendingPing.originWs.readyState !== WebSocket.OPEN) {
689
+ return false;
690
+ }
691
+
692
+ sendJson(pendingPing.originWs, {
693
+ type: 'integration.pong',
694
+ requestId: pendingPing.originRequestId,
695
+ payload: {
696
+ server: {
697
+ status: 'ok',
698
+ started: true
699
+ },
700
+ frontend: buildResponsiveFrontendPresence(pendingPing.responses)
701
+ }
702
+ });
703
+
704
+ return true;
705
+ }
706
+
707
+ function removePendingIntegrationPingsForWs(ws) {
708
+ for (const [probeRequestId, entry] of pendingIntegrationPings.entries()) {
709
+ if (entry?.originWs === ws) {
710
+ if (entry.timeoutId) {
711
+ clearTimeout(entry.timeoutId);
712
+ }
713
+ pendingIntegrationPings.delete(probeRequestId);
714
+ }
715
+ }
716
+ }
717
+
718
+ function handleIntegrationPing(ws, data) {
719
+ const requestId = getRequestId(data);
720
+ const validation = validateIntegrationPingPayload(data?.payload);
721
+ if (!validation.ok) {
722
+ sendIntegrationError(ws, {
723
+ requestId,
724
+ code: validation.code,
725
+ message: validation.message
726
+ });
727
+ return;
728
+ }
729
+
730
+ const { channel, targetClientId } = validation.payload;
731
+ const targets = getTargetFrontendConnections(channel, targetClientId);
732
+ if (targets.length === 0) {
733
+ sendJson(ws, {
734
+ type: 'integration.pong',
735
+ requestId,
736
+ payload: {
737
+ server: {
738
+ status: 'ok',
739
+ started: true
740
+ },
741
+ frontend: buildFrontendPresence(channel, targetClientId)
742
+ }
743
+ });
744
+ return;
745
+ }
746
+
747
+ const probeRequestId = createRuntimeId('integration_probe');
748
+ const pendingPing = {
749
+ originWs: ws,
750
+ originRequestId: requestId,
751
+ channel,
752
+ targetClientId,
753
+ responses: new Map(),
754
+ timeoutId: null
755
+ };
756
+
757
+ pendingPing.timeoutId = setTimeout(() => {
758
+ finalizePendingIntegrationPing(probeRequestId);
759
+ }, INTEGRATION_PING_TIMEOUT_MS);
760
+ pendingIntegrationPings.set(probeRequestId, pendingPing);
761
+
762
+ for (const target of targets) {
763
+ sendJson(target.ws, {
764
+ type: 'integration.ping',
765
+ requestId: probeRequestId,
766
+ payload: {
767
+ channel,
768
+ targetClientId: targetClientId || null
769
+ }
770
+ });
771
+ }
772
+ }
773
+
774
+ function handleIntegrationProbeResponse(ws, data) {
775
+ const probeRequestId = getRequestId(data);
776
+ if (!probeRequestId) {
777
+ return false;
778
+ }
779
+
780
+ const pendingPing = pendingIntegrationPings.get(probeRequestId);
781
+ if (!pendingPing) {
782
+ return false;
783
+ }
784
+
785
+ const meta = integrationConnections.get(ws);
786
+ if (!meta || meta.role !== INTEGRATION_ROLE_FRONTEND_PAGE) {
787
+ return false;
788
+ }
789
+
790
+ if (data?.type === 'integration.ack') {
791
+ pendingPing.responses.set(meta.clientId, {
792
+ clientId: meta.clientId,
793
+ lastSeenAt: new Date().toISOString(),
794
+ pageUrl: normalizeNonEmptyString(data?.payload?.pageUrl) || meta.pageUrl || null,
795
+ sessionId: normalizeNonEmptyString(data?.payload?.sessionId) || meta.sessionId || null
796
+ });
797
+
798
+ if (pendingPing.targetClientId) {
799
+ finalizePendingIntegrationPing(probeRequestId);
800
+ }
801
+ return true;
802
+ }
803
+
804
+ if (data?.type === 'integration.error') {
805
+ return true;
806
+ }
807
+
808
+ return false;
809
+ }
810
+
811
+ function getTargetFrontendConnections(channel, targetClientId) {
812
+ return listIntegrationConnections({
813
+ role: INTEGRATION_ROLE_FRONTEND_PAGE,
814
+ channel,
815
+ clientId: targetClientId
816
+ });
817
+ }
818
+
819
+ function validateEditorClientsListPayload(payload) {
820
+ if (payload === undefined || payload === null) {
821
+ return {
822
+ ok: true,
823
+ payload: {
824
+ channel: null,
825
+ clientId: null
826
+ }
827
+ };
828
+ }
829
+
830
+ if (!isPlainObject(payload)) {
831
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
832
+ }
833
+
834
+ return {
835
+ ok: true,
836
+ payload: {
837
+ channel: normalizeNonEmptyString(payload.channel),
838
+ clientId: normalizeNonEmptyString(payload.clientId)
839
+ }
840
+ };
841
+ }
842
+
843
+ function validateEditorTargetPayload(payload, extras = {}) {
844
+ if (!isPlainObject(payload)) {
845
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
846
+ }
847
+
848
+ const channel = normalizeNonEmptyString(payload.channel);
849
+ if (!channel) {
850
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'channel is required' };
851
+ }
852
+
853
+ const targetClientId = normalizeNonEmptyString(payload.targetClientId);
854
+ if (!targetClientId) {
855
+ return { ok: false, code: 'INVALID_TARGET', message: 'targetClientId is required' };
856
+ }
857
+
858
+ return {
859
+ ok: true,
860
+ payload: {
861
+ channel,
862
+ targetClientId,
863
+ ...extras
864
+ }
865
+ };
866
+ }
867
+
868
+ function validateEditorSnapshotPayload(payload) {
869
+ return validateEditorTargetPayload(payload);
870
+ }
871
+
872
+ function validateEditorNodesListPayload(payload) {
873
+ if (!isPlainObject(payload)) {
874
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
875
+ }
876
+
877
+ const base = validateEditorTargetPayload(payload, {});
878
+ if (!base.ok) {
879
+ return base;
880
+ }
881
+
882
+ let status = [];
883
+ if (Array.isArray(payload.status)) {
884
+ status = payload.status
885
+ .map((value) => normalizeNonEmptyString(value))
886
+ .filter(Boolean);
887
+ } else if (typeof payload.status === 'string') {
888
+ status = payload.status
889
+ .split(',')
890
+ .map((value) => normalizeNonEmptyString(value))
891
+ .filter(Boolean);
892
+ } else if (payload.status !== undefined && payload.status !== null) {
893
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'status must be a string or string array' };
894
+ }
895
+
896
+ const elementKey = normalizeNonEmptyString(payload.elementKey);
897
+ const limit = payload.limit === undefined ? null : normalizePositiveInteger(payload.limit);
898
+ if (payload.limit !== undefined && !limit) {
899
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'limit must be a positive integer' };
900
+ }
901
+
902
+ return {
903
+ ok: true,
904
+ payload: {
905
+ ...base.payload,
906
+ status,
907
+ elementKey,
908
+ limit
909
+ }
910
+ };
911
+ }
912
+
913
+ function validateEditorNodeScreenshotPayload(payload) {
914
+ if (!isPlainObject(payload)) {
915
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
916
+ }
917
+
918
+ const elementKey = normalizeNonEmptyString(payload.elementKey);
919
+ if (!elementKey) {
920
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'elementKey is required' };
921
+ }
922
+
923
+ const base = validateEditorTargetPayload(payload, {
924
+ elementKey,
925
+ downloadPath: normalizeOptionalString(payload.downloadPath) || null
926
+ });
927
+ return base;
928
+ }
929
+
930
+ function validateEditorContextImagesPayload(payload) {
931
+ if (payload !== undefined && payload !== null && !isPlainObject(payload)) {
932
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
933
+ }
934
+
935
+ return validateEditorTargetPayload(payload || {}, {
936
+ downloadPath: normalizeOptionalString(payload?.downloadPath) || null
937
+ });
938
+ }
939
+
940
+ function validateEditorEditingSetPayload(payload) {
941
+ if (!isPlainObject(payload)) {
942
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
943
+ }
944
+
945
+ const elementKey = normalizeNonEmptyString(payload.elementKey);
946
+ if (!elementKey) {
947
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'elementKey is required' };
948
+ }
949
+
950
+ const state = normalizeNonEmptyString(payload.state);
951
+ if (state !== 'editing' && state !== 'idle') {
952
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'state must be editing or idle' };
953
+ }
954
+
955
+ return validateEditorTargetPayload(payload, {
956
+ elementKey,
957
+ state,
958
+ taskRef: isPlainObject(payload.taskRef) ? payload.taskRef : null
959
+ });
960
+ }
961
+
962
+ function handleEditorClientsList(ws, data) {
963
+ const requestId = getRequestId(data);
964
+ const validation = validateEditorClientsListPayload(data?.payload);
965
+ if (!validation.ok) {
966
+ sendIntegrationError(ws, {
967
+ requestId,
968
+ code: validation.code,
969
+ message: validation.message
970
+ });
971
+ return;
972
+ }
973
+
974
+ const items = listIntegrationConnections({
975
+ role: INTEGRATION_ROLE_FRONTEND_PAGE,
976
+ channel: validation.payload.channel,
977
+ clientId: validation.payload.clientId
978
+ }).map(buildEditorClientSummary);
979
+
980
+ sendJson(ws, {
981
+ type: 'integration.editor.clients.result',
982
+ requestId,
983
+ payload: {
984
+ items,
985
+ total: items.length,
986
+ filters: validation.payload
987
+ }
988
+ });
989
+ }
990
+
991
+ function validateIntegrationContextUpdatePayload(payload) {
992
+ if (!payload || typeof payload !== 'object') {
993
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
994
+ }
995
+
996
+ const channel = normalizeNonEmptyString(payload.channel);
997
+ if (!channel) {
998
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'channel is required' };
999
+ }
1000
+
1001
+ const targetClientId = normalizeNonEmptyString(payload.targetClientId);
1002
+ if (!targetClientId) {
1003
+ return { ok: false, code: 'INVALID_TARGET', message: 'targetClientId is required' };
1004
+ }
1005
+
1006
+ const mode = normalizeNonEmptyString(payload.mode) || 'replace';
1007
+ if (mode !== 'replace' && mode !== 'append') {
1008
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'mode must be replace or append' };
1009
+ }
1010
+
1011
+ if (!payload.context || typeof payload.context !== 'object' || Array.isArray(payload.context)) {
1012
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'context must be an object' };
1013
+ }
1014
+
1015
+ return {
1016
+ ok: true,
1017
+ payload: {
1018
+ channel,
1019
+ targetClientId,
1020
+ mode,
1021
+ context: payload.context
1022
+ }
1023
+ };
1024
+ }
1025
+
1026
+ function validateIntegrationPromptUpdatePayload(payload) {
1027
+ if (!payload || typeof payload !== 'object') {
1028
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'payload must be an object' };
1029
+ }
1030
+
1031
+ const channel = normalizeNonEmptyString(payload.channel);
1032
+ if (!channel) {
1033
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'channel is required' };
1034
+ }
1035
+
1036
+ const targetClientId = normalizeNonEmptyString(payload.targetClientId);
1037
+ if (!targetClientId) {
1038
+ return { ok: false, code: 'INVALID_TARGET', message: 'targetClientId is required' };
1039
+ }
1040
+
1041
+ if (typeof payload.prompt !== 'string') {
1042
+ return { ok: false, code: 'INVALID_PAYLOAD', message: 'prompt must be a string' };
1043
+ }
1044
+
1045
+ return {
1046
+ ok: true,
1047
+ payload: {
1048
+ channel,
1049
+ targetClientId,
1050
+ prompt: payload.prompt,
1051
+ autoSend: payload.autoSend === true
1052
+ }
1053
+ };
1054
+ }
1055
+
1056
+ function forwardIntegrationMessage(ws, data, { validator, type, awaitResult = false }) {
1057
+ const requestId = getRequestId(data);
1058
+ const validation = validator(data?.payload);
1059
+ if (!validation.ok) {
1060
+ sendIntegrationError(ws, {
1061
+ requestId,
1062
+ code: validation.code,
1063
+ message: validation.message
1064
+ });
1065
+ return;
1066
+ }
1067
+
1068
+ const { channel, targetClientId } = validation.payload;
1069
+ const targets = getTargetFrontendConnections(channel, targetClientId);
1070
+ if (targets.length === 0) {
1071
+ sendIntegrationError(ws, {
1072
+ requestId,
1073
+ code: 'FRONTEND_NOT_ONLINE',
1074
+ message: 'Target frontend page is not connected'
1075
+ });
1076
+ return;
1077
+ }
1078
+
1079
+ const requiredCapability = INTEGRATION_EDITOR_CAPABILITIES[type] || null;
1080
+ if (requiredCapability) {
1081
+ const unsupportedTarget = targets.find((target) => !target.capabilities?.includes(requiredCapability));
1082
+ if (unsupportedTarget) {
1083
+ sendIntegrationError(ws, {
1084
+ requestId,
1085
+ code: 'UNSUPPORTED_FRONTEND_CAPABILITY',
1086
+ message: `Target frontend page does not advertise capability ${requiredCapability}`
1087
+ });
1088
+ return;
1089
+ }
1090
+ }
1091
+
1092
+ storePendingIntegrationRequest(requestId, ws, {
1093
+ awaitResult,
1094
+ requestType: type,
1095
+ timeoutMs: data?.payload?.timeoutMs,
1096
+ requestPayload: validation.payload
1097
+ });
1098
+
1099
+ const outgoingPayload = {
1100
+ type,
1101
+ requestId,
1102
+ payload: validation.payload
1103
+ };
1104
+
1105
+ for (const target of targets) {
1106
+ sendJson(target.ws, outgoingPayload);
1107
+ }
1108
+ }
1109
+
1110
+ async function forwardIntegrationResponseToOrigin(data) {
1111
+ const requestId = getRequestId(data);
1112
+ if (!requestId) {
1113
+ return false;
1114
+ }
1115
+
1116
+ const pendingRequest = pendingIntegrationRequests.get(requestId);
1117
+ if (!pendingRequest?.originWs || pendingRequest.originWs.readyState !== WebSocket.OPEN) {
1118
+ clearPendingIntegrationRequest(requestId);
1119
+ return false;
1120
+ }
1121
+
1122
+ let outgoingData = data;
1123
+ try {
1124
+ outgoingData = await maybeMaterializeEditorResult(data, pendingRequest);
1125
+ } catch (error) {
1126
+ sendIntegrationError(pendingRequest.originWs, {
1127
+ requestId,
1128
+ code: error.code || 'INTERNAL_ERROR',
1129
+ message: error.message || 'Failed to materialize editor result'
1130
+ });
1131
+ clearPendingIntegrationRequest(requestId);
1132
+ return true;
1133
+ }
1134
+
1135
+ sendJson(pendingRequest.originWs, outgoingData);
1136
+ const isTerminal =
1137
+ outgoingData?.type === 'integration.error'
1138
+ || isEditorResultMessageType(outgoingData?.type)
1139
+ || (outgoingData?.type === 'integration.ack' && pendingRequest.awaitResult !== true);
1140
+ if (isTerminal) {
1141
+ clearPendingIntegrationRequest(requestId);
1142
+ }
1143
+ return true;
1144
+ }
1145
+
1146
+ class ExternalAgentWebSocketWriter {
1147
+ constructor(ws, { requestId, provider }) {
1148
+ this.ws = ws;
1149
+ this.requestId = requestId;
1150
+ this.provider = provider;
1151
+ this.sessionId = null;
1152
+ this.isWebSocketWriter = true;
1153
+ }
1154
+
1155
+ send(payload) {
1156
+ if (!payload || typeof payload !== 'object') {
1157
+ return;
1158
+ }
1159
+
1160
+ if (typeof payload.sessionId === 'string' && payload.sessionId.trim()) {
1161
+ this.sessionId = payload.sessionId.trim();
1162
+ }
1163
+
1164
+ if (payload.type === 'session-created') {
1165
+ const sessionId = typeof payload.sessionId === 'string' ? payload.sessionId.trim() : this.sessionId;
1166
+ if (!sessionId) {
1167
+ return;
1168
+ }
1169
+
1170
+ this.sessionId = sessionId;
1171
+ sendJson(this.ws, {
1172
+ type: 'agent.session.created',
1173
+ requestId: this.requestId,
1174
+ provider: payload.provider || this.provider,
1175
+ sessionId,
1176
+ ...buildSessionNavigation(sessionId)
1177
+ });
1178
+ return;
1179
+ }
1180
+
1181
+ if (payload.type === 'conversation-event' && payload.event) {
1182
+ const sessionId = payload.sessionId || payload.event?.sessionId || this.sessionId || null;
1183
+ if (sessionId) {
1184
+ this.sessionId = sessionId;
1185
+ }
1186
+
1187
+ sendJson(this.ws, {
1188
+ type: 'agent.event',
1189
+ requestId: this.requestId,
1190
+ provider: payload.provider || payload.event?.provider || this.provider,
1191
+ sessionId,
1192
+ event: payload.event
1193
+ });
1194
+ }
1195
+ }
1196
+
1197
+ setSessionId(sessionId) {
1198
+ this.sessionId = sessionId;
1199
+ }
1200
+
1201
+ getSessionId() {
1202
+ return this.sessionId;
1203
+ }
1204
+ }
1205
+
1206
+ subscribeToSessionRuntimeStateChanges(({ subscriptionKey, snapshot }) => {
1207
+ const subscribers = sessionStateSubscribersByKey.get(subscriptionKey);
1208
+ if (!subscribers || subscribers.size === 0) {
1209
+ return;
1210
+ }
1211
+
1212
+ subscribers.forEach((ws) => {
1213
+ sendJson(ws, {
1214
+ type: 'agent.state.changed',
1215
+ payload: {
1216
+ subscriptionKey,
1217
+ sessionId: snapshot.sessionId,
1218
+ provider: snapshot.provider,
1219
+ phase: snapshot.phase,
1220
+ isLoading: snapshot.isLoading,
1221
+ canAbortSession: snapshot.canAbortSession,
1222
+ hasPendingApproval: snapshot.hasPendingApproval,
1223
+ updatedAt: snapshot.updatedAt
1224
+ }
1225
+ });
1226
+ });
1227
+ });
1228
+
1229
+ async function handleAgentRun(ws, request, data) {
1230
+ const requestId = getRequestId(data);
1231
+
1232
+ let normalized = null;
1233
+ try {
1234
+ normalized = normalizeExternalAgentRunRequest(data.payload || {});
1235
+ } catch (error) {
1236
+ sendRequestError(ws, {
1237
+ requestId,
1238
+ provider: data?.payload?.provider || null,
1239
+ sessionId: data?.payload?.sessionId || null,
1240
+ error: error.message,
1241
+ statusCode: error.statusCode || 400
1242
+ });
1243
+ return;
1244
+ }
1245
+
1246
+ sendJson(ws, {
1247
+ type: 'agent.accepted',
1248
+ requestId,
1249
+ provider: normalized.provider,
1250
+ sessionId: normalized.normalizedSessionId
1251
+ });
1252
+
1253
+ const baseWriter = new ExternalAgentWebSocketWriter(ws, {
1254
+ requestId,
1255
+ provider: normalized.provider
1256
+ });
1257
+ const transportWriter = new SessionEventMirrorWriter(baseWriter, normalized.provider);
1258
+
1259
+ try {
1260
+ const result = await runExternalAgentRequest({
1261
+ user: request.user,
1262
+ request: normalized,
1263
+ transportWriter,
1264
+ endTransportOnFinish: false
1265
+ });
1266
+
1267
+ sendJson(ws, {
1268
+ type: 'agent.completed',
1269
+ requestId,
1270
+ provider: normalized.provider,
1271
+ sessionId: result.sessionId || baseWriter.getSessionId() || normalized.normalizedSessionId,
1272
+ result
1273
+ });
1274
+ } catch (error) {
1275
+ const resolvedSessionId = baseWriter.getSessionId() || normalized.normalizedSessionId || null;
1276
+ if (error instanceof AgentSessionAbortedError) {
1277
+ sendJson(ws, {
1278
+ type: 'agent.aborted',
1279
+ requestId,
1280
+ success: true,
1281
+ aborted: true,
1282
+ sessionId: resolvedSessionId,
1283
+ provider: normalized.provider,
1284
+ message: 'Session aborted'
1285
+ });
1286
+ return;
1287
+ }
1288
+
1289
+ sendRequestError(ws, {
1290
+ requestId,
1291
+ provider: normalized.provider,
1292
+ sessionId: resolvedSessionId,
1293
+ error: error.message,
1294
+ statusCode: error.statusCode || 500
1295
+ });
1296
+ }
1297
+ }
1298
+
1299
+ async function handleAgentAbort(ws, data) {
1300
+ const requestId = getRequestId(data);
1301
+
1302
+ let normalized = null;
1303
+ try {
1304
+ normalized = normalizeExternalAgentAbortRequest(data.payload || {});
1305
+ } catch (error) {
1306
+ sendRequestError(ws, {
1307
+ requestId,
1308
+ provider: data?.payload?.provider || null,
1309
+ sessionId: data?.payload?.sessionId || null,
1310
+ error: error.message,
1311
+ statusCode: error.statusCode || 400
1312
+ });
1313
+ return;
1314
+ }
1315
+
1316
+ try {
1317
+ const writer = new SessionEventMirrorWriter(
1318
+ new ExternalAgentWebSocketWriter(ws, { requestId, provider: normalized.provider }),
1319
+ normalized.provider
1320
+ );
1321
+ const { success } = await abortAgentSessionWithWriter({
1322
+ provider: normalized.provider,
1323
+ sessionId: normalized.sessionId,
1324
+ writer
1325
+ });
1326
+
1327
+ if (!success) {
1328
+ sendRequestError(ws, {
1329
+ requestId,
1330
+ provider: normalized.provider,
1331
+ sessionId: normalized.sessionId,
1332
+ error: 'Active session not found',
1333
+ statusCode: 200
1334
+ });
1335
+ return;
1336
+ }
1337
+
1338
+ sendJson(ws, {
1339
+ type: 'agent.aborted',
1340
+ requestId,
1341
+ success: true,
1342
+ aborted: true,
1343
+ sessionId: normalized.sessionId,
1344
+ provider: normalized.provider,
1345
+ message: 'Session aborted'
1346
+ });
1347
+ } catch (error) {
1348
+ sendRequestError(ws, {
1349
+ requestId,
1350
+ provider: normalized.provider,
1351
+ sessionId: normalized.sessionId,
1352
+ error: error.message,
1353
+ statusCode: 500
1354
+ });
1355
+ }
1356
+ }
1357
+
1358
+ export function handleExternalAgentWebSocketConnection(ws, request) {
1359
+ ws.on('message', (message) => {
1360
+ let data = null;
1361
+ try {
1362
+ data = JSON.parse(message);
1363
+ } catch {
1364
+ sendRequestError(ws, {
1365
+ requestId: null,
1366
+ provider: null,
1367
+ sessionId: null,
1368
+ error: 'Invalid JSON message',
1369
+ statusCode: 400
1370
+ });
1371
+ return;
1372
+ }
1373
+
1374
+ touchIntegrationConnection(ws);
1375
+
1376
+ if (data?.type === 'ping') {
1377
+ sendJson(ws, {
1378
+ type: 'pong',
1379
+ requestId: getRequestId(data),
1380
+ timestamp: new Date().toISOString()
1381
+ });
1382
+ return;
1383
+ }
1384
+
1385
+ if (data?.type === 'integration.connect') {
1386
+ handleIntegrationConnect(ws, data);
1387
+ return;
1388
+ }
1389
+
1390
+ if (data?.type === 'integration.ping') {
1391
+ handleIntegrationPing(ws, data);
1392
+ return;
1393
+ }
1394
+
1395
+ if (data?.type === 'integration.context.update') {
1396
+ forwardIntegrationMessage(ws, data, {
1397
+ validator: validateIntegrationContextUpdatePayload,
1398
+ type: 'integration.context.update'
1399
+ });
1400
+ return;
1401
+ }
1402
+
1403
+ if (data?.type === 'integration.prompt.update') {
1404
+ forwardIntegrationMessage(ws, data, {
1405
+ validator: validateIntegrationPromptUpdatePayload,
1406
+ type: 'integration.prompt.update'
1407
+ });
1408
+ return;
1409
+ }
1410
+
1411
+ if (data?.type === 'integration.editor.clients.list') {
1412
+ handleEditorClientsList(ws, data);
1413
+ return;
1414
+ }
1415
+
1416
+ if (data?.type === 'integration.editor.snapshot.get') {
1417
+ forwardIntegrationMessage(ws, data, {
1418
+ validator: validateEditorSnapshotPayload,
1419
+ type: 'integration.editor.snapshot.get',
1420
+ awaitResult: true
1421
+ });
1422
+ return;
1423
+ }
1424
+
1425
+ if (data?.type === 'integration.editor.nodes.list') {
1426
+ forwardIntegrationMessage(ws, data, {
1427
+ validator: validateEditorNodesListPayload,
1428
+ type: 'integration.editor.nodes.list',
1429
+ awaitResult: true
1430
+ });
1431
+ return;
1432
+ }
1433
+
1434
+ if (data?.type === 'integration.editor.node.screenshot.get') {
1435
+ forwardIntegrationMessage(ws, data, {
1436
+ validator: validateEditorNodeScreenshotPayload,
1437
+ type: 'integration.editor.node.screenshot.get',
1438
+ awaitResult: true
1439
+ });
1440
+ return;
1441
+ }
1442
+
1443
+ if (data?.type === 'integration.editor.context-images.get') {
1444
+ forwardIntegrationMessage(ws, data, {
1445
+ validator: validateEditorContextImagesPayload,
1446
+ type: 'integration.editor.context-images.get',
1447
+ awaitResult: true
1448
+ });
1449
+ return;
1450
+ }
1451
+
1452
+ if (data?.type === 'integration.editor.editing.set') {
1453
+ forwardIntegrationMessage(ws, data, {
1454
+ validator: validateEditorEditingSetPayload,
1455
+ type: 'integration.editor.editing.set',
1456
+ awaitResult: true
1457
+ });
1458
+ return;
1459
+ }
1460
+
1461
+ if (data?.type === 'integration.ack' || data?.type === 'integration.error' || isEditorResultMessageType(data?.type)) {
1462
+ const handledProbeResponse = handleIntegrationProbeResponse(ws, data);
1463
+ if (handledProbeResponse) {
1464
+ return;
1465
+ }
1466
+
1467
+ void forwardIntegrationResponseToOrigin(data).then((forwarded) => {
1468
+ if (!forwarded && data?.type === 'integration.error') {
1469
+ sendIntegrationError(ws, {
1470
+ requestId: getRequestId(data),
1471
+ code: 'UNSUPPORTED_MESSAGE_TYPE',
1472
+ message: 'No pending integration request found for this response'
1473
+ });
1474
+ }
1475
+ }).catch((error) => {
1476
+ sendIntegrationError(ws, {
1477
+ requestId: getRequestId(data),
1478
+ code: error.code || 'INTERNAL_ERROR',
1479
+ message: error.message || 'Failed to forward integration response'
1480
+ });
1481
+ });
1482
+ return;
1483
+ }
1484
+
1485
+ if (data?.type === 'agent.run') {
1486
+ void handleAgentRun(ws, request, data);
1487
+ return;
1488
+ }
1489
+
1490
+ if (data?.type === 'agent.state.query') {
1491
+ void handleAgentStateQuery(ws, data);
1492
+ return;
1493
+ }
1494
+
1495
+ if (data?.type === 'agent.state.subscribe') {
1496
+ void handleAgentStateSubscribe(ws, data);
1497
+ return;
1498
+ }
1499
+
1500
+ if (data?.type === 'agent.abort') {
1501
+ void handleAgentAbort(ws, data);
1502
+ return;
1503
+ }
1504
+
1505
+ if (typeof data?.type === 'string' && data.type.startsWith('integration.')) {
1506
+ sendIntegrationError(ws, {
1507
+ requestId: getRequestId(data),
1508
+ code: 'UNSUPPORTED_MESSAGE_TYPE',
1509
+ message: 'Unsupported integration message type'
1510
+ });
1511
+ return;
1512
+ }
1513
+
1514
+ sendRequestError(ws, {
1515
+ requestId: getRequestId(data),
1516
+ provider: data?.payload?.provider || null,
1517
+ sessionId: data?.payload?.sessionId || null,
1518
+ error: 'Unsupported message type',
1519
+ statusCode: 400
1520
+ });
1521
+ });
1522
+
1523
+ ws.on('close', () => {
1524
+ unregisterIntegrationConnection(ws);
1525
+ });
1526
+ }