onbuzz 4.7.2 → 4.8.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 (990) hide show
  1. package/package.json +1 -1
  2. package/scripts/grounding-bench/bench.mjs +300 -0
  3. package/scripts/grounding-bench/cases.js +173 -0
  4. package/scripts/grounding-bench/imageBuilder.js +119 -0
  5. package/src/core/__tests__/agentScheduler.test.js +74 -0
  6. package/src/core/agentScheduler.js +59 -5
  7. package/src/index.js +36 -0
  8. package/src/interfaces/webServer.js +42 -0
  9. package/src/services/__tests__/memoryService.test.js +521 -450
  10. package/src/services/__tests__/modelRouterNaming.test.js +93 -0
  11. package/src/services/__tests__/modelRouterService.test.js +477 -388
  12. package/src/services/aiService.js +136 -1
  13. package/src/services/grounding/GroundingModel.js +116 -0
  14. package/src/services/grounding/KimiAdapter.js +427 -0
  15. package/src/services/grounding/MockAdapter.js +125 -0
  16. package/src/services/grounding/__tests__/KimiAdapter.test.js +475 -0
  17. package/src/services/grounding/__tests__/MockAdapter.test.js +66 -0
  18. package/src/services/grounding/__tests__/groundingMetrics.test.js +156 -0
  19. package/src/services/grounding/__tests__/registry.test.js +122 -0
  20. package/src/services/grounding/constants.js +121 -0
  21. package/src/services/grounding/groundingMetrics.js +121 -0
  22. package/src/services/grounding/index.js +53 -0
  23. package/src/services/grounding/registry.js +125 -0
  24. package/src/services/grounding/types.js +108 -0
  25. package/src/services/memoryService.js +559 -521
  26. package/src/services/modelRouterService.js +433 -364
  27. package/src/services/tokenCountingService.js +2 -2
  28. package/src/tools/__tests__/agentCommunicationTool.test.js +752 -717
  29. package/src/tools/__tests__/baseTool.test.js +22 -0
  30. package/src/tools/__tests__/codeMapTool.test.js +388 -0
  31. package/src/tools/__tests__/foundryWebSearchTool.test.js +252 -0
  32. package/src/tools/__tests__/memoryTool.reminisce.test.js +364 -363
  33. package/src/tools/__tests__/memoryTool.test.js +391 -297
  34. package/src/tools/__tests__/parserHelpers.test.js +233 -0
  35. package/src/tools/__tests__/webTool.e2e.test.js +13 -1
  36. package/src/tools/agentCommunicationTool.js +1399 -1385
  37. package/src/tools/baseTool.js +19 -4
  38. package/src/tools/codeMapTool.js +794 -130
  39. package/src/tools/desktop/DesktopTool.js +638 -0
  40. package/src/tools/desktop/__tests__/DesktopTool.e2e.test.js +306 -0
  41. package/src/tools/desktop/__tests__/DesktopTool.test.js +507 -0
  42. package/src/tools/desktop/__tests__/osCapabilities.test.js +106 -0
  43. package/src/tools/desktop/__tests__/osController.test.js +364 -0
  44. package/src/tools/desktop/osCapabilities.js +248 -0
  45. package/src/tools/desktop/osController.js +491 -0
  46. package/src/tools/foundryWebSearchTool.js +273 -0
  47. package/src/tools/memoryTool.js +892 -820
  48. package/src/tools/parserHelpers.js +296 -0
  49. package/src/utilities/__tests__/planInjection.test.js +125 -0
  50. package/src/utilities/__tests__/tagParser.test.js +943 -887
  51. package/src/utilities/constants.js +1 -1
  52. package/src/utilities/planInjection.js +103 -0
  53. package/src/utilities/tagParser.js +918 -896
  54. package/web-ui/build/index.html +2 -2
  55. package/web-ui/build/static/1c-DGpIT7i5.js +1 -0
  56. package/web-ui/build/static/abap-5wFDdWLh.js +1 -0
  57. package/web-ui/build/static/abnf-BP1dpNSE.js +1 -0
  58. package/web-ui/build/static/abnf-DBEIAl8g.js +1 -0
  59. package/web-ui/build/static/accesslog-CWSM_T5E.js +1 -0
  60. package/web-ui/build/static/actionscript-DONkco1J.js +1 -0
  61. package/web-ui/build/static/actionscript-FqBYk5er.js +1 -0
  62. package/web-ui/build/static/ada-C2JLRIaM.js +1 -0
  63. package/web-ui/build/static/ada-gKiygTRK.js +1 -0
  64. package/web-ui/build/static/agda-CkSODqK2.js +1 -0
  65. package/web-ui/build/static/al-BJ_YR6p7.js +1 -0
  66. package/web-ui/build/static/angelscript-Dg2byMGg.js +1 -0
  67. package/web-ui/build/static/antlr4-BnpyaFNr.js +1 -0
  68. package/web-ui/build/static/apache-Dffxsd7O.js +1 -0
  69. package/web-ui/build/static/apacheconf-DLitjtWj.js +1 -0
  70. package/web-ui/build/static/apex-Drr_IvU2.js +1 -0
  71. package/web-ui/build/static/apl-CF6qxmXG.js +1 -0
  72. package/web-ui/build/static/applescript-CjOlw3b_.js +1 -0
  73. package/web-ui/build/static/applescript-DjkSl1Ry.js +1 -0
  74. package/web-ui/build/static/aql-KwVmK1gP.js +1 -0
  75. package/web-ui/build/static/arcade-CENSXx0R.js +1 -0
  76. package/web-ui/build/static/arduino-B3Ta9Fll.js +1 -0
  77. package/web-ui/build/static/arduino-CzcsaB9_.js +1 -0
  78. package/web-ui/build/static/arff-CMJSVt_O.js +1 -0
  79. package/web-ui/build/static/armasm-0zSgSPB4.js +1 -0
  80. package/web-ui/build/static/asciidoc-B8K5ctWq.js +1 -0
  81. package/web-ui/build/static/asciidoc-bk2Sg6b6.js +1 -0
  82. package/web-ui/build/static/asm6502-ji6zm4FQ.js +1 -0
  83. package/web-ui/build/static/asmatmel-DJqObM4Y.js +1 -0
  84. package/web-ui/build/static/aspectj-DqQKI7J5.js +1 -0
  85. package/web-ui/build/static/aspnet-9cenTxW7.js +1 -0
  86. package/web-ui/build/static/autohotkey-C6EhiZvl.js +1 -0
  87. package/web-ui/build/static/autohotkey-sTGJOvMM.js +1 -0
  88. package/web-ui/build/static/autoit-B0Im8iQ1.js +1 -0
  89. package/web-ui/build/static/autoit-CO6pUD0H.js +1 -0
  90. package/web-ui/build/static/avisynth-OvOHTfj5.js +1 -0
  91. package/web-ui/build/static/avrasm-BLWZ5Mye.js +1 -0
  92. package/web-ui/build/static/avro-idl-BB2ODvnw.js +1 -0
  93. package/web-ui/build/static/awk-_jhMabQ0.js +1 -0
  94. package/web-ui/build/static/axapta-DdBRgoYy.js +1 -0
  95. package/web-ui/build/static/bash-CsaRGXBQ.js +1 -0
  96. package/web-ui/build/static/bash-CxLFkwAC.js +1 -0
  97. package/web-ui/build/static/basic-BJyy7JJE.js +1 -0
  98. package/web-ui/build/static/basic-CrMb-gv0.js +1 -0
  99. package/web-ui/build/static/batch-BvcykMe5.js +1 -0
  100. package/web-ui/build/static/bbcode-EOBuY5Y6.js +1 -0
  101. package/web-ui/build/static/bicep-QkDJBA34.js +1 -0
  102. package/web-ui/build/static/birb-TAOxKurn.js +1 -0
  103. package/web-ui/build/static/bison-BEK6cnad.js +1 -0
  104. package/web-ui/build/static/bnf-DfEODxsr.js +1 -0
  105. package/web-ui/build/static/bnf-DhbMjsuA.js +1 -0
  106. package/web-ui/build/static/brainfuck-BoVocOa7.js +1 -0
  107. package/web-ui/build/static/brainfuck-DDC5QXPK.js +1 -0
  108. package/web-ui/build/static/brightscript-DNN598w2.js +1 -0
  109. package/web-ui/build/static/bro-Cn_jjZ8P.js +1 -0
  110. package/web-ui/build/static/bsl-CMCN75Gu.js +1 -0
  111. package/web-ui/build/static/c-DbOGQnEJ.js +1 -0
  112. package/web-ui/build/static/c-kggwYFsy.js +1 -0
  113. package/web-ui/build/static/c-like-BIsuUvof.js +1 -0
  114. package/web-ui/build/static/cal-DpgMsBZE.js +1 -0
  115. package/web-ui/build/static/capnproto-C1AHYI-M.js +1 -0
  116. package/web-ui/build/static/ceylon-pvJffDe1.js +1 -0
  117. package/web-ui/build/static/cfscript-C1qylu52.js +1 -0
  118. package/web-ui/build/static/chaiscript-D417WKRI.js +1 -0
  119. package/web-ui/build/static/cil-CjbZHbcY.js +1 -0
  120. package/web-ui/build/static/clean-Dvc6R-F2.js +1 -0
  121. package/web-ui/build/static/clojure-CSVRQPMQ.js +1 -0
  122. package/web-ui/build/static/clojure-ig1Vkg7X.js +1 -0
  123. package/web-ui/build/static/clojure-repl-C1uyEabk.js +1 -0
  124. package/web-ui/build/static/cmake-DSbVcAB3.js +1 -0
  125. package/web-ui/build/static/cmake-Zp8kPwnH.js +1 -0
  126. package/web-ui/build/static/cobol-OsZSiK_P.js +1 -0
  127. package/web-ui/build/static/coffeescript-D8r0416S.js +1 -0
  128. package/web-ui/build/static/coffeescript-DCpgClxh.js +1 -0
  129. package/web-ui/build/static/concurnas-DKnsxUOc.js +1 -0
  130. package/web-ui/build/static/coq-BQFlywVI.js +1 -0
  131. package/web-ui/build/static/coq-BvS9mQB2.js +1 -0
  132. package/web-ui/build/static/cos-D3ze2791.js +1 -0
  133. package/web-ui/build/static/cpp-Bw-cV3P1.js +1 -0
  134. package/web-ui/build/static/cpp-vi1p7XpB.js +1 -0
  135. package/web-ui/build/static/crmsh-BClnJTeQ.js +1 -0
  136. package/web-ui/build/static/crystal-DGzOUYZq.js +1 -0
  137. package/web-ui/build/static/crystal-DMzk6EvA.js +1 -0
  138. package/web-ui/build/static/csharp-BTom8s2X.js +1 -0
  139. package/web-ui/build/static/csharp-Cp2V3jfR.js +1 -0
  140. package/web-ui/build/static/cshtml-Dmtt9Kto.js +1 -0
  141. package/web-ui/build/static/csp-WPVsLe9_.js +1 -0
  142. package/web-ui/build/static/csp-qddj5fu_.js +1 -0
  143. package/web-ui/build/static/css-B0FAm7kj.js +1 -0
  144. package/web-ui/build/static/css-extras-Bx3pvjiG.js +1 -0
  145. package/web-ui/build/static/csv-CH-edgS5.js +1 -0
  146. package/web-ui/build/static/cypher-c3G8Leew.js +1 -0
  147. package/web-ui/build/static/d-BMDSy22_.js +1 -0
  148. package/web-ui/build/static/d-BdH4oW8-.js +1 -0
  149. package/web-ui/build/static/dart-C-r72q-O.js +1 -0
  150. package/web-ui/build/static/dart-CwpBMrKa.js +1 -0
  151. package/web-ui/build/static/dataweave-BPvqdt4k.js +1 -0
  152. package/web-ui/build/static/dax-Eyy6ixcN.js +1 -0
  153. package/web-ui/build/static/delphi-DhEbPj_5.js +1 -0
  154. package/web-ui/build/static/dhall-Ct1L8sN1.js +1 -0
  155. package/web-ui/build/static/diff-CSTqCgwn.js +1 -0
  156. package/web-ui/build/static/diff-Da97B5vW.js +1 -0
  157. package/web-ui/build/static/django-DGit_lCg.js +1 -0
  158. package/web-ui/build/static/django-DPgqFB8k.js +1 -0
  159. package/web-ui/build/static/dns-DUyXuZ-a.js +1 -0
  160. package/web-ui/build/static/dns-zone-file-D79MDxVG.js +1 -0
  161. package/web-ui/build/static/docker-DTiy4o38.js +1 -0
  162. package/web-ui/build/static/dockerfile-CvwPP8wE.js +1 -0
  163. package/web-ui/build/static/dos-CaIUWxvb.js +1 -0
  164. package/web-ui/build/static/dot-D3504w6Y.js +1 -0
  165. package/web-ui/build/static/dsconfig-Smy1eeX_.js +1 -0
  166. package/web-ui/build/static/dts-DyTYSXZj.js +1 -0
  167. package/web-ui/build/static/dust-CrJyi6qA.js +1 -0
  168. package/web-ui/build/static/ebnf-C8nqfkBH.js +1 -0
  169. package/web-ui/build/static/ebnf-Cuh5Vh2-.js +1 -0
  170. package/web-ui/build/static/editorconfig-C7pTGl6n.js +1 -0
  171. package/web-ui/build/static/eiffel-C75MSJCA.js +1 -0
  172. package/web-ui/build/static/ejs-Dyo7DF5w.js +1 -0
  173. package/web-ui/build/static/elixir-D4yJefOc.js +1 -0
  174. package/web-ui/build/static/elixir-DKRow2SJ.js +1 -0
  175. package/web-ui/build/static/elm-CgbdDlkT.js +1 -0
  176. package/web-ui/build/static/elm-D3N-UgU0.js +1 -0
  177. package/web-ui/build/static/erb-BsITh8qW.js +1 -0
  178. package/web-ui/build/static/erb-lY_LyKyS.js +1 -0
  179. package/web-ui/build/static/erlang-BCnPiYmB.js +1 -0
  180. package/web-ui/build/static/erlang-JEghiPXc.js +1 -0
  181. package/web-ui/build/static/erlang-repl-DzU97ugC.js +1 -0
  182. package/web-ui/build/static/etlua-CsBo8cJa.js +1 -0
  183. package/web-ui/build/static/excel-formula-C2piiCYa.js +1 -0
  184. package/web-ui/build/static/excel-kqNypwQD.js +1 -0
  185. package/web-ui/build/static/factor-DZTOhkyU.js +1 -0
  186. package/web-ui/build/static/false-yLjhfaSw.js +1 -0
  187. package/web-ui/build/static/firestore-security-rules-oaQNoH8l.js +1 -0
  188. package/web-ui/build/static/fix-C9IfnTuS.js +1 -0
  189. package/web-ui/build/static/flix-EmJ_JhPo.js +1 -0
  190. package/web-ui/build/static/flow-DCUl7IAQ.js +1 -0
  191. package/web-ui/build/static/fortran-DvyxJmFN.js +1 -0
  192. package/web-ui/build/static/fortran-EEP9R3S5.js +1 -0
  193. package/web-ui/build/static/fsharp-D_98s3RX.js +1 -0
  194. package/web-ui/build/static/fsharp-Dt9jOO5G.js +1 -0
  195. package/web-ui/build/static/ftl-C8gMHWLo.js +1 -0
  196. package/web-ui/build/static/gams-BHxVPb4z.js +1 -0
  197. package/web-ui/build/static/gap-DFtyHk-q.js +1 -0
  198. package/web-ui/build/static/gauss-BwhJWUcg.js +1 -0
  199. package/web-ui/build/static/gcode-BvmXwp09.js +1 -0
  200. package/web-ui/build/static/gcode-gWcD6Vc7.js +1 -0
  201. package/web-ui/build/static/gdscript-btHoX8RE.js +1 -0
  202. package/web-ui/build/static/gedcom-MgPs9iqh.js +1 -0
  203. package/web-ui/build/static/gherkin-B9c_Q16A.js +1 -0
  204. package/web-ui/build/static/gherkin-BvOgkK6h.js +1 -0
  205. package/web-ui/build/static/git-D9XWOVcQ.js +1 -0
  206. package/web-ui/build/static/glsl-CshBHxHY.js +1 -0
  207. package/web-ui/build/static/glsl-fNRUMTDr.js +1 -0
  208. package/web-ui/build/static/gml-B78deHK8.js +1 -0
  209. package/web-ui/build/static/gml-Cj7d2u8O.js +1 -0
  210. package/web-ui/build/static/gn-CJLD-mF1.js +1 -0
  211. package/web-ui/build/static/go-CT93BEkL.js +1 -0
  212. package/web-ui/build/static/go-CTTlNuQO.js +1 -0
  213. package/web-ui/build/static/go-module-BgnXboUE.js +1 -0
  214. package/web-ui/build/static/golo-2S5tXS-l.js +1 -0
  215. package/web-ui/build/static/gradle-8W8DCcWJ.js +1 -0
  216. package/web-ui/build/static/graphql-DZfp6FNU.js +1 -0
  217. package/web-ui/build/static/groovy-BfsyMb3X.js +1 -0
  218. package/web-ui/build/static/groovy-Dxov7ENz.js +1 -0
  219. package/web-ui/build/static/haml-BlUFsdVV.js +1 -0
  220. package/web-ui/build/static/haml-CjVj6vvW.js +1 -0
  221. package/web-ui/build/static/handlebars-DxrQFkyA.js +1 -0
  222. package/web-ui/build/static/handlebars-FE6fotYl.js +1 -0
  223. package/web-ui/build/static/haskell-B6z0RCD_.js +1 -0
  224. package/web-ui/build/static/haskell-JBFmJTRy.js +1 -0
  225. package/web-ui/build/static/haxe-BtMZMi-_.js +1 -0
  226. package/web-ui/build/static/haxe-C1J8n-fH.js +1 -0
  227. package/web-ui/build/static/hcl-DJP-bFWE.js +1 -0
  228. package/web-ui/build/static/hlsl-DrGjhs1b.js +1 -0
  229. package/web-ui/build/static/hoon-Cg-ZhqIT.js +1 -0
  230. package/web-ui/build/static/hpkp-D07vgmoZ.js +1 -0
  231. package/web-ui/build/static/hsp-CsypPWoA.js +1 -0
  232. package/web-ui/build/static/hsts-B6DMRxvP.js +1 -0
  233. package/web-ui/build/static/htmlbars-BI7_Hw4e.js +1 -0
  234. package/web-ui/build/static/http-90ihEi4s.js +1 -0
  235. package/web-ui/build/static/http-Dp4QXj9E.js +1 -0
  236. package/web-ui/build/static/hy-BMgKvP4K.js +1 -0
  237. package/web-ui/build/static/ichigojam-D6wKvJDb.js +1 -0
  238. package/web-ui/build/static/icon-CrgkmCwl.js +1 -0
  239. package/web-ui/build/static/icu-message-format-Bi2JxCXs.js +1 -0
  240. package/web-ui/build/static/idris-DbUsyZt5.js +1 -0
  241. package/web-ui/build/static/iecst-CQZ9t8fW.js +1 -0
  242. package/web-ui/build/static/ignore-BaFgtNMs.js +1 -0
  243. package/web-ui/build/static/index-DEWSWosh.css +1 -0
  244. package/web-ui/build/static/index-Di1bjCFA.js +13 -0
  245. package/web-ui/build/static/index-otR_WSsL.js +1 -0
  246. package/web-ui/build/static/index-whZPU4as.js +1183 -0
  247. package/web-ui/build/static/inform7-ClyX1Gro.js +1 -0
  248. package/web-ui/build/static/inform7-tZHBS5XP.js +1 -0
  249. package/web-ui/build/static/ini-BrP5JNYL.js +1 -0
  250. package/web-ui/build/static/ini-CVkrAhwV.js +1 -0
  251. package/web-ui/build/static/io-BJ1Y6Bdc.js +1 -0
  252. package/web-ui/build/static/irpf90-fjjcKS_8.js +1 -0
  253. package/web-ui/build/static/isbl-ClpvfUIz.js +1 -0
  254. package/web-ui/build/static/j-CICW77xS.js +1 -0
  255. package/web-ui/build/static/java-B9DFK-0E.js +1 -0
  256. package/web-ui/build/static/java-BL_6rYko.js +1 -0
  257. package/web-ui/build/static/javadoc-Cc4HKpKK.js +1 -0
  258. package/web-ui/build/static/javadoclike-CgNkUDOm.js +1 -0
  259. package/web-ui/build/static/javascript-CM40ZECq.js +1 -0
  260. package/web-ui/build/static/javastacktrace-BHSqXfG5.js +1 -0
  261. package/web-ui/build/static/jboss-cli-Bv5NhVSZ.js +1 -0
  262. package/web-ui/build/static/jexl--Ohk_keA.js +1 -0
  263. package/web-ui/build/static/jolie-DMTN5Vdf.js +1 -0
  264. package/web-ui/build/static/jq-nXRLldXX.js +1 -0
  265. package/web-ui/build/static/js-extras-eLwv3frs.js +1 -0
  266. package/web-ui/build/static/js-templates-Ca0owlrg.js +1 -0
  267. package/web-ui/build/static/jsdoc-KERXp0da.js +1 -0
  268. package/web-ui/build/static/json-BTjLaRsy.js +1 -0
  269. package/web-ui/build/static/json-p7pU0qdW.js +1 -0
  270. package/web-ui/build/static/json5-Cxjy1udc.js +1 -0
  271. package/web-ui/build/static/jsonp-CO52H7Gy.js +1 -0
  272. package/web-ui/build/static/jsstacktrace-8YKfoyJP.js +1 -0
  273. package/web-ui/build/static/jsx-Ct_05KbM.js +1 -0
  274. package/web-ui/build/static/julia-23I1ubCE.js +1 -0
  275. package/web-ui/build/static/julia-d8rVGed_.js +1 -0
  276. package/web-ui/build/static/julia-repl-mwnHedW_.js +1 -0
  277. package/web-ui/build/static/keepalived-E85Rx_fF.js +1 -0
  278. package/web-ui/build/static/keyman-aWd3QUDq.js +1 -0
  279. package/web-ui/build/static/kotlin-Cu370hQq.js +1 -0
  280. package/web-ui/build/static/kotlin-DoJ2WnmZ.js +1 -0
  281. package/web-ui/build/static/kumir-oiOgqcQO.js +1 -0
  282. package/web-ui/build/static/kusto-Bp-B02K5.js +1 -0
  283. package/web-ui/build/static/lasso-BxoQVwOO.js +1 -0
  284. package/web-ui/build/static/latex-CANm5vsX.js +1 -0
  285. package/web-ui/build/static/latex-PMroeNch.js +1 -0
  286. package/web-ui/build/static/latte-2ErU_2XF.js +1 -0
  287. package/web-ui/build/static/ldif-ChPn_F7o.js +1 -0
  288. package/web-ui/build/static/leaf-UkCugDgG.js +1 -0
  289. package/web-ui/build/static/less-CCH5RA89.js +1 -0
  290. package/web-ui/build/static/less-CGZbVU1g.js +1 -0
  291. package/web-ui/build/static/lilypond-BoyM37sv.js +1 -0
  292. package/web-ui/build/static/liquid-DGJVpRBi.js +1 -0
  293. package/web-ui/build/static/lisp-93nne61u.js +1 -0
  294. package/web-ui/build/static/lisp-DYjIRsXz.js +1 -0
  295. package/web-ui/build/static/livecodeserver-CfNaxIE4.js +1 -0
  296. package/web-ui/build/static/livescript-C8kDlQkz.js +1 -0
  297. package/web-ui/build/static/livescript-CoarnRHq.js +1 -0
  298. package/web-ui/build/static/llvm-CBVyNmAh.js +1 -0
  299. package/web-ui/build/static/llvm-DGxq8a7u.js +1 -0
  300. package/web-ui/build/static/log-BivgwFql.js +1 -0
  301. package/web-ui/build/static/lolcode-l5sK2cZz.js +1 -0
  302. package/web-ui/build/static/lsl-Xm44xLRQ.js +1 -0
  303. package/web-ui/build/static/lua-CGvrzfKp.js +1 -0
  304. package/web-ui/build/static/lua-nHwXeY4c.js +1 -0
  305. package/web-ui/build/static/magma-DmNItmna.js +1 -0
  306. package/web-ui/build/static/makefile-CKJNNHGb.js +1 -0
  307. package/web-ui/build/static/makefile-DI6y5Qml.js +1 -0
  308. package/web-ui/build/static/markdown-BgpbxKd-.js +1 -0
  309. package/web-ui/build/static/markdown-CvcxA4yk.js +1 -0
  310. package/web-ui/build/static/markup-templating-DijqesiA.js +1 -0
  311. package/web-ui/build/static/mathematica-Cxll1Q10.js +1 -0
  312. package/web-ui/build/static/matlab-BJo2T1A-.js +1 -0
  313. package/web-ui/build/static/matlab-C6DlZX4l.js +1 -0
  314. package/web-ui/build/static/maxima-DJXO4sbL.js +1 -0
  315. package/web-ui/build/static/maxscript-DdLpUYBs.js +1 -0
  316. package/web-ui/build/static/mel-BIpfnSyZ.js +1 -0
  317. package/web-ui/build/static/mel-BYcTUZJW.js +1 -0
  318. package/web-ui/build/static/mercury-C_LSpbD8.js +1 -0
  319. package/web-ui/build/static/mermaid-D9yBWnrT.js +1 -0
  320. package/web-ui/build/static/mipsasm-nR_K2Ue-.js +1 -0
  321. package/web-ui/build/static/mizar-BeLUPncD.js +1 -0
  322. package/web-ui/build/static/mizar-BesIbZd9.js +1 -0
  323. package/web-ui/build/static/mojolicious-cBx3OWa-.js +1 -0
  324. package/web-ui/build/static/mongodb-DTWKy9ac.js +1 -0
  325. package/web-ui/build/static/monkey-B72bZC3c.js +1 -0
  326. package/web-ui/build/static/monkey-G9XELYPQ.js +1 -0
  327. package/web-ui/build/static/moonscript-BXYVQiqj.js +1 -0
  328. package/web-ui/build/static/moonscript-sDd-5knz.js +1 -0
  329. package/web-ui/build/static/n1ql-0vKSfFAO.js +1 -0
  330. package/web-ui/build/static/n1ql-C9_BSZfz.js +1 -0
  331. package/web-ui/build/static/n4js-B7Ct8dds.js +1 -0
  332. package/web-ui/build/static/nand2tetris-hdl-p9gpphTM.js +1 -0
  333. package/web-ui/build/static/naniscript-6ZVr8Aug.js +1 -0
  334. package/web-ui/build/static/nasm-Ca73yTUt.js +1 -0
  335. package/web-ui/build/static/neon-DNP49oyX.js +1 -0
  336. package/web-ui/build/static/nevod-Qhgt7Bce.js +1 -0
  337. package/web-ui/build/static/nginx-D5e7lu62.js +1 -0
  338. package/web-ui/build/static/nginx-DobnyESB.js +1 -0
  339. package/web-ui/build/static/nim-BPBivUOV.js +1 -0
  340. package/web-ui/build/static/nim-Baoug1Wa.js +1 -0
  341. package/web-ui/build/static/nix-CSPTQs5y.js +1 -0
  342. package/web-ui/build/static/nix-Dk4eNw49.js +1 -0
  343. package/web-ui/build/static/node-repl-NJNL8VFR.js +1 -0
  344. package/web-ui/build/static/nsis-BZ0oMzEw.js +1 -0
  345. package/web-ui/build/static/nsis-CKPCjtCU.js +1 -0
  346. package/web-ui/build/static/objectivec-BkfIRhhV.js +1 -0
  347. package/web-ui/build/static/objectivec-CnQgqhbJ.js +1 -0
  348. package/web-ui/build/static/ocaml-9rGNzRrK.js +1 -0
  349. package/web-ui/build/static/ocaml-CTolCqxL.js +1 -0
  350. package/web-ui/build/static/opencl-9Q3vRDxv.js +1 -0
  351. package/web-ui/build/static/openqasm-D2QiuFBp.js +1 -0
  352. package/web-ui/build/static/openscad-C3HyuzpB.js +1 -0
  353. package/web-ui/build/static/oxygene-4gi-VYy_.js +1 -0
  354. package/web-ui/build/static/oz-BGDEB-1A.js +1 -0
  355. package/web-ui/build/static/parigp-CfZzp1uE.js +1 -0
  356. package/web-ui/build/static/parser-UbGteTcy.js +1 -0
  357. package/web-ui/build/static/parser3-C-Jx-fy7.js +1 -0
  358. package/web-ui/build/static/pascal-D1_R0gW-.js +1 -0
  359. package/web-ui/build/static/pascaligo-B8C-98Np.js +1 -0
  360. package/web-ui/build/static/pcaxis-TGnlUKNs.js +1 -0
  361. package/web-ui/build/static/peoplecode-C5Vf1AH0.js +1 -0
  362. package/web-ui/build/static/perl-CBp1N62T.js +1 -0
  363. package/web-ui/build/static/perl-Z59j904t.js +1 -0
  364. package/web-ui/build/static/pf-Cq8B_xwQ.js +1 -0
  365. package/web-ui/build/static/pgsql-ofJbUHkL.js +1 -0
  366. package/web-ui/build/static/php-OlH7HLQJ.js +1 -0
  367. package/web-ui/build/static/php-extras-GqtrxLuk.js +1 -0
  368. package/web-ui/build/static/php-r09kMDOB.js +1 -0
  369. package/web-ui/build/static/php-template-18uT97Qo.js +1 -0
  370. package/web-ui/build/static/phpdoc-9tRFvup9.js +1 -0
  371. package/web-ui/build/static/plaintext-DsYT6Mu-.js +1 -0
  372. package/web-ui/build/static/plsql-RhWQNJVb.js +1 -0
  373. package/web-ui/build/static/pony-DD6JMLYI.js +1 -0
  374. package/web-ui/build/static/powerquery-CALow-bt.js +1 -0
  375. package/web-ui/build/static/powershell-C2QvIuKF.js +1 -0
  376. package/web-ui/build/static/powershell-yrbJEhCh.js +1 -0
  377. package/web-ui/build/static/processing-1T5w_Q03.js +1 -0
  378. package/web-ui/build/static/processing-DBim_dO-.js +1 -0
  379. package/web-ui/build/static/profile-Cdloh8mZ.js +1 -0
  380. package/web-ui/build/static/prolog-DRSsNnns.js +1 -0
  381. package/web-ui/build/static/prolog-D_ajweDr.js +1 -0
  382. package/web-ui/build/static/promql-BRuwn6Bn.js +1 -0
  383. package/web-ui/build/static/properties-D5Wyl4X4.js +1 -0
  384. package/web-ui/build/static/properties-kn4fl1bl.js +1 -0
  385. package/web-ui/build/static/protobuf-BMO76zWi.js +1 -0
  386. package/web-ui/build/static/protobuf-CTUCF-U-.js +1 -0
  387. package/web-ui/build/static/psl-CiqYdQbY.js +1 -0
  388. package/web-ui/build/static/pug-BLE2Qayj.js +1 -0
  389. package/web-ui/build/static/puppet-CFKLWXft.js +1 -0
  390. package/web-ui/build/static/puppet-DQci0Dl5.js +1 -0
  391. package/web-ui/build/static/pure-D2h_GynV.js +1 -0
  392. package/web-ui/build/static/purebasic-BF8MVw8V.js +1 -0
  393. package/web-ui/build/static/purebasic-BTtHiCkh.js +1 -0
  394. package/web-ui/build/static/purescript-D1ZSh-sH.js +1 -0
  395. package/web-ui/build/static/python-Cp9_Vdhb.js +1 -0
  396. package/web-ui/build/static/python-DdgNw8IW.js +1 -0
  397. package/web-ui/build/static/python-repl-DvK89VMC.js +1 -0
  398. package/web-ui/build/static/q-COaIgwhT.js +1 -0
  399. package/web-ui/build/static/q-Cm0dQkW8.js +1 -0
  400. package/web-ui/build/static/qml-BpsOqqJM.js +1 -0
  401. package/web-ui/build/static/qml-BziQXlU4.js +1 -0
  402. package/web-ui/build/static/qore-Cutz6g-2.js +1 -0
  403. package/web-ui/build/static/qsharp-B16619X1.js +1 -0
  404. package/web-ui/build/static/r-CFUIj5Hd.js +1 -0
  405. package/web-ui/build/static/r-CPrwCi5w.js +1 -0
  406. package/web-ui/build/static/racket-Bh08DFXF.js +1 -0
  407. package/web-ui/build/static/reason-BxjDq4e-.js +1 -0
  408. package/web-ui/build/static/reasonml-Ds5SsGP8.js +1 -0
  409. package/web-ui/build/static/regex-DhBIDIMI.js +1 -0
  410. package/web-ui/build/static/rego-DCwxZXcB.js +1 -0
  411. package/web-ui/build/static/renpy-C2fuQfqb.js +1 -0
  412. package/web-ui/build/static/rest-C52ZpxWQ.js +1 -0
  413. package/web-ui/build/static/rib-Cbl2Mzyj.js +1 -0
  414. package/web-ui/build/static/rip-BTOu5ZIE.js +1 -0
  415. package/web-ui/build/static/roboconf-3Oi2wuVk.js +1 -0
  416. package/web-ui/build/static/roboconf-ndLZLE39.js +1 -0
  417. package/web-ui/build/static/robotframework-BzHXiIj1.js +1 -0
  418. package/web-ui/build/static/routeros-CkpPoqx-.js +1 -0
  419. package/web-ui/build/static/rsl-C0bwOQ38.js +1 -0
  420. package/web-ui/build/static/ruby-C_hIhtuQ.js +1 -0
  421. package/web-ui/build/static/ruby-DvKfZPzj.js +1 -0
  422. package/web-ui/build/static/ruleslanguage-CbZJlddz.js +1 -0
  423. package/web-ui/build/static/rust-BFPIhB-X.js +1 -0
  424. package/web-ui/build/static/rust-sBpUq-qE.js +1 -0
  425. package/web-ui/build/static/sas-BCXvYN9x.js +1 -0
  426. package/web-ui/build/static/sas-D7GAsTY3.js +1 -0
  427. package/web-ui/build/static/sass-BdaErGMN.js +1 -0
  428. package/web-ui/build/static/scala-Cj81oCl9.js +1 -0
  429. package/web-ui/build/static/scala-CvbksfY6.js +1 -0
  430. package/web-ui/build/static/scheme-BM5ZqKnl.js +1 -0
  431. package/web-ui/build/static/scheme-BPvlu9Tk.js +1 -0
  432. package/web-ui/build/static/scilab-CJanLKQN.js +1 -0
  433. package/web-ui/build/static/scss-C53YF_7f.js +1 -0
  434. package/web-ui/build/static/scss-DPp8UZbr.js +1 -0
  435. package/web-ui/build/static/shell-CC9bQXMe.js +1 -0
  436. package/web-ui/build/static/shell-session-CuR3fbf-.js +1 -0
  437. package/web-ui/build/static/smali-CUgLls3D.js +1 -0
  438. package/web-ui/build/static/smali-YODSX8qt.js +1 -0
  439. package/web-ui/build/static/smalltalk-BMJQ4bbs.js +1 -0
  440. package/web-ui/build/static/smalltalk-BVGY3CTl.js +1 -0
  441. package/web-ui/build/static/smarty-C9aNt4-p.js +1 -0
  442. package/web-ui/build/static/sml-C3BIHhfq.js +1 -0
  443. package/web-ui/build/static/sml-DTipyRmY.js +1 -0
  444. package/web-ui/build/static/solidity-1yuPUqoC.js +1 -0
  445. package/web-ui/build/static/solution-file-BgzK4GOU.js +1 -0
  446. package/web-ui/build/static/soy-CFJXRvqc.js +1 -0
  447. package/web-ui/build/static/sparql-CAosYMpl.js +1 -0
  448. package/web-ui/build/static/splunk-spl-DkINtWr8.js +1 -0
  449. package/web-ui/build/static/sqf-DbrWIS2M.js +1 -0
  450. package/web-ui/build/static/sqf-nq8Q9J2W.js +1 -0
  451. package/web-ui/build/static/sql-9bwClhZQ.js +1 -0
  452. package/web-ui/build/static/sql-CqPkY-lX.js +1 -0
  453. package/web-ui/build/static/sql_more-CsY5ts77.js +1 -0
  454. package/web-ui/build/static/squirrel-BuqtzRBD.js +1 -0
  455. package/web-ui/build/static/stan-BNxBSglc.js +1 -0
  456. package/web-ui/build/static/stan-CZVMc34l.js +1 -0
  457. package/web-ui/build/static/stata-DKnVdHCd.js +1 -0
  458. package/web-ui/build/static/step21-Cu_TaBGF.js +1 -0
  459. package/web-ui/build/static/stylus-BS7-OJew.js +1 -0
  460. package/web-ui/build/static/stylus-DtFrp1Nk.js +1 -0
  461. package/web-ui/build/static/subunit-DDCoWkkc.js +1 -0
  462. package/web-ui/build/static/swift-BaguUZbl.js +1 -0
  463. package/web-ui/build/static/swift-mwBsb8Bx.js +1 -0
  464. package/web-ui/build/static/systemd-D6PpyDKk.js +1 -0
  465. package/web-ui/build/static/t4-cs-Cem8g4Ck.js +1 -0
  466. package/web-ui/build/static/t4-templating-BZo-HjmD.js +1 -0
  467. package/web-ui/build/static/t4-vb-B4oVnKa4.js +1 -0
  468. package/web-ui/build/static/taggerscript-DweAZ5pw.js +1 -0
  469. package/web-ui/build/static/tap-DpuvKHHF.js +1 -0
  470. package/web-ui/build/static/tap-hUKmJObZ.js +1 -0
  471. package/web-ui/build/static/tcl-BUvhAi7u.js +1 -0
  472. package/web-ui/build/static/tcl-Dsck63d8.js +1 -0
  473. package/web-ui/build/static/textile-CgKbqJ-j.js +1 -0
  474. package/web-ui/build/static/thrift-B5H6rApp.js +1 -0
  475. package/web-ui/build/static/toml-DggTpfOo.js +1 -0
  476. package/web-ui/build/static/tp-CazB2P2X.js +1 -0
  477. package/web-ui/build/static/tremor-B1jy7S5p.js +1 -0
  478. package/web-ui/build/static/tsx-BqF2lVDi.js +1 -0
  479. package/web-ui/build/static/tt2-BNoNXpEe.js +1 -0
  480. package/web-ui/build/static/turtle-B0evd5mn.js +1 -0
  481. package/web-ui/build/static/twig-B5A7nMdv.js +1 -0
  482. package/web-ui/build/static/twig-DZnLWvh8.js +1 -0
  483. package/web-ui/build/static/typescript-Bbe_P093.js +1 -0
  484. package/web-ui/build/static/typescript-BfhvmSSG.js +1 -0
  485. package/web-ui/build/static/typoscript-BjuZEIgw.js +1 -0
  486. package/web-ui/build/static/unrealscript-C3iUCFRi.js +1 -0
  487. package/web-ui/build/static/uorazor-CbZXwzIj.js +1 -0
  488. package/web-ui/build/static/uri-xMPSnp6m.js +1 -0
  489. package/web-ui/build/static/v-BKcGo5I6.js +1 -0
  490. package/web-ui/build/static/vala-BRoBE4am.js +1 -0
  491. package/web-ui/build/static/vala-B__Iyrma.js +1 -0
  492. package/web-ui/build/static/vbnet-DBxlMRvN.js +1 -0
  493. package/web-ui/build/static/vbnet-DjAXt5BE.js +1 -0
  494. package/web-ui/build/static/vbscript-BNA4oANi.js +1 -0
  495. package/web-ui/build/static/vbscript-html-o8ckLPKG.js +1 -0
  496. package/web-ui/build/static/velocity-D7sc5ggA.js +1 -0
  497. package/web-ui/build/static/verilog-BWJfMIng.js +1 -0
  498. package/web-ui/build/static/verilog-jW2GPC--.js +1 -0
  499. package/web-ui/build/static/vhdl-BIVlXRPa.js +1 -0
  500. package/web-ui/build/static/vhdl-BeqdhhxD.js +1 -0
  501. package/web-ui/build/static/vim-712lI4-g.js +1 -0
  502. package/web-ui/build/static/vim-DMROTzr0.js +1 -0
  503. package/web-ui/build/static/visual-basic-CQfbM-ta.js +1 -0
  504. package/web-ui/build/static/warpscript-DFAvCXFQ.js +1 -0
  505. package/web-ui/build/static/wasm-CiYoxBl0.js +1 -0
  506. package/web-ui/build/static/web-idl-DoCkPK8y.js +1 -0
  507. package/web-ui/build/static/wiki-bvz0AGzB.js +1 -0
  508. package/web-ui/build/static/wolfram-fBuyFEgU.js +1 -0
  509. package/web-ui/build/static/wren-DvATFxjF.js +1 -0
  510. package/web-ui/build/static/x86asm-r4bPbUR_.js +1 -0
  511. package/web-ui/build/static/xeora-B6iOnDJY.js +1 -0
  512. package/web-ui/build/static/xl-Ce6B5slc.js +1 -0
  513. package/web-ui/build/static/xml-DNjyPmhQ.js +1 -0
  514. package/web-ui/build/static/xml-doc-DydogmZD.js +1 -0
  515. package/web-ui/build/static/xojo-DCQLltvr.js +1 -0
  516. package/web-ui/build/static/xquery-C-fAnA0H.js +1 -0
  517. package/web-ui/build/static/xquery-CNeqzhLO.js +1 -0
  518. package/web-ui/build/static/yaml-BsVaGsk5.js +1 -0
  519. package/web-ui/build/static/yaml-kVZvwv_C.js +1 -0
  520. package/web-ui/build/static/yang-B_gq9JEq.js +1 -0
  521. package/web-ui/build/static/zephir-ECIXgXhX.js +1 -0
  522. package/web-ui/build/static/zig-DY325EKG.js +1 -0
  523. package/web-ui/build/static/1c-BLo2-5GS.js +0 -1
  524. package/web-ui/build/static/abap-BjUneMMr.js +0 -1
  525. package/web-ui/build/static/abnf-CZTz-l7P.js +0 -1
  526. package/web-ui/build/static/abnf-ITJ5mkBo.js +0 -1
  527. package/web-ui/build/static/accesslog-Dj78lZOU.js +0 -1
  528. package/web-ui/build/static/actionscript-Chivtu59.js +0 -1
  529. package/web-ui/build/static/actionscript-CrvMFXnx.js +0 -1
  530. package/web-ui/build/static/ada-DdJtyoYe.js +0 -1
  531. package/web-ui/build/static/ada-GIvogaLi.js +0 -1
  532. package/web-ui/build/static/agda-DbPp4fQi.js +0 -1
  533. package/web-ui/build/static/al-BZ24KwRS.js +0 -1
  534. package/web-ui/build/static/angelscript-DzsFypYt.js +0 -1
  535. package/web-ui/build/static/antlr4-3qb4rD3S.js +0 -1
  536. package/web-ui/build/static/apache-DF0P5ZRm.js +0 -1
  537. package/web-ui/build/static/apacheconf-C8KuNf9c.js +0 -1
  538. package/web-ui/build/static/apex-CPV-DeVE.js +0 -1
  539. package/web-ui/build/static/apl-C60bVxxW.js +0 -1
  540. package/web-ui/build/static/applescript-uz0i0tu1.js +0 -1
  541. package/web-ui/build/static/applescript-xcPRUBia.js +0 -1
  542. package/web-ui/build/static/aql-CRh4IGkA.js +0 -1
  543. package/web-ui/build/static/arcade-CmLedeqc.js +0 -1
  544. package/web-ui/build/static/arduino-3pr1BvXB.js +0 -1
  545. package/web-ui/build/static/arduino-C2BejAip.js +0 -1
  546. package/web-ui/build/static/arff-DE6Wr9MU.js +0 -1
  547. package/web-ui/build/static/armasm-DXodTfP4.js +0 -1
  548. package/web-ui/build/static/asciidoc-BU2BDFme.js +0 -1
  549. package/web-ui/build/static/asciidoc-CgvtvdeC.js +0 -1
  550. package/web-ui/build/static/asm6502-BMuYCKmP.js +0 -1
  551. package/web-ui/build/static/asmatmel-Dwxzg4kq.js +0 -1
  552. package/web-ui/build/static/aspectj-XZIldIRI.js +0 -1
  553. package/web-ui/build/static/aspnet-UxY2bZ9O.js +0 -1
  554. package/web-ui/build/static/autohotkey-BdZoQT7s.js +0 -1
  555. package/web-ui/build/static/autohotkey-DLWs6p8o.js +0 -1
  556. package/web-ui/build/static/autoit-CxcZx_kh.js +0 -1
  557. package/web-ui/build/static/autoit-HY_BHh1p.js +0 -1
  558. package/web-ui/build/static/avisynth-4Q4be9zq.js +0 -1
  559. package/web-ui/build/static/avrasm-CFqVJML2.js +0 -1
  560. package/web-ui/build/static/avro-idl-DVTV2YZm.js +0 -1
  561. package/web-ui/build/static/awk-C1Udi4As.js +0 -1
  562. package/web-ui/build/static/axapta-B9lexIDC.js +0 -1
  563. package/web-ui/build/static/bash-DDZrx0R1.js +0 -1
  564. package/web-ui/build/static/bash-Uuijyc_K.js +0 -1
  565. package/web-ui/build/static/basic-B29QFWwp.js +0 -1
  566. package/web-ui/build/static/basic-DKDOc42O.js +0 -1
  567. package/web-ui/build/static/batch-CA3q9wRr.js +0 -1
  568. package/web-ui/build/static/bbcode-DkUDcqrp.js +0 -1
  569. package/web-ui/build/static/bicep-AHSQiVgQ.js +0 -1
  570. package/web-ui/build/static/birb-BsAnN1j3.js +0 -1
  571. package/web-ui/build/static/bison-BIq5-ZXo.js +0 -1
  572. package/web-ui/build/static/bnf-BexLm6hz.js +0 -1
  573. package/web-ui/build/static/bnf-Dm46EM5S.js +0 -1
  574. package/web-ui/build/static/brainfuck-CBG8MHhD.js +0 -1
  575. package/web-ui/build/static/brainfuck-DrHuUFVv.js +0 -1
  576. package/web-ui/build/static/brightscript-CoQ7s_l3.js +0 -1
  577. package/web-ui/build/static/bro-Cz5RmvoK.js +0 -1
  578. package/web-ui/build/static/bsl-C9wXomkX.js +0 -1
  579. package/web-ui/build/static/c-BZFEFWwC.js +0 -1
  580. package/web-ui/build/static/c-CfFWLEL7.js +0 -1
  581. package/web-ui/build/static/c-like-CvIii1QP.js +0 -1
  582. package/web-ui/build/static/cal-be-Dz3GC.js +0 -1
  583. package/web-ui/build/static/capnproto-Bcaxak1e.js +0 -1
  584. package/web-ui/build/static/ceylon-QH_iQXnW.js +0 -1
  585. package/web-ui/build/static/cfscript-BRgBlxCO.js +0 -1
  586. package/web-ui/build/static/chaiscript-DSBx5kyN.js +0 -1
  587. package/web-ui/build/static/cil-C90LIbge.js +0 -1
  588. package/web-ui/build/static/clean-D2X3fSKw.js +0 -1
  589. package/web-ui/build/static/clojure-CvKE0e-3.js +0 -1
  590. package/web-ui/build/static/clojure-Cy3Zg4vZ.js +0 -1
  591. package/web-ui/build/static/clojure-repl-D0kONHv2.js +0 -1
  592. package/web-ui/build/static/cmake-DIDVe3n_.js +0 -1
  593. package/web-ui/build/static/cmake-DPijEs9c.js +0 -1
  594. package/web-ui/build/static/cobol-BPEjLjGY.js +0 -1
  595. package/web-ui/build/static/coffeescript-CoSx9MQU.js +0 -1
  596. package/web-ui/build/static/coffeescript-L5CBXrnP.js +0 -1
  597. package/web-ui/build/static/concurnas-q5sXhVU4.js +0 -1
  598. package/web-ui/build/static/coq-BAEq82zI.js +0 -1
  599. package/web-ui/build/static/coq-BBgtQZwi.js +0 -1
  600. package/web-ui/build/static/cos-9FdwfBC5.js +0 -1
  601. package/web-ui/build/static/cpp-CYt8UYn2.js +0 -1
  602. package/web-ui/build/static/cpp-CZ2lOXlm.js +0 -1
  603. package/web-ui/build/static/crmsh-C_TZWkES.js +0 -1
  604. package/web-ui/build/static/crystal-BDf_0NDz.js +0 -1
  605. package/web-ui/build/static/crystal-BrTR9dGT.js +0 -1
  606. package/web-ui/build/static/csharp-BSBrba50.js +0 -1
  607. package/web-ui/build/static/csharp-Cerqyqbn.js +0 -1
  608. package/web-ui/build/static/cshtml-heNA_ya5.js +0 -1
  609. package/web-ui/build/static/csp-CJprtNTl.js +0 -1
  610. package/web-ui/build/static/csp-CyYfNNga.js +0 -1
  611. package/web-ui/build/static/css-ClMfqSLF.js +0 -1
  612. package/web-ui/build/static/css-extras-BRU-Fmfk.js +0 -1
  613. package/web-ui/build/static/csv-VxPQSCnT.js +0 -1
  614. package/web-ui/build/static/cypher-COR7lVoB.js +0 -1
  615. package/web-ui/build/static/d-CjJIoaqU.js +0 -1
  616. package/web-ui/build/static/d-DrTea74c.js +0 -1
  617. package/web-ui/build/static/dart-Dx1CtbrO.js +0 -1
  618. package/web-ui/build/static/dart-Kqlt_5Iy.js +0 -1
  619. package/web-ui/build/static/dataweave-DpoRPYaa.js +0 -1
  620. package/web-ui/build/static/dax-BdRNDjXh.js +0 -1
  621. package/web-ui/build/static/delphi-C6ZfxFVF.js +0 -1
  622. package/web-ui/build/static/dhall-DZrga6Gc.js +0 -1
  623. package/web-ui/build/static/diff-BuMvzPYe.js +0 -1
  624. package/web-ui/build/static/diff-DZYXzKCb.js +0 -1
  625. package/web-ui/build/static/django-D_vRowuc.js +0 -1
  626. package/web-ui/build/static/django-NYcglI2i.js +0 -1
  627. package/web-ui/build/static/dns-Cd7tDS1i.js +0 -1
  628. package/web-ui/build/static/dns-zone-file-C3-2vwxy.js +0 -1
  629. package/web-ui/build/static/docker-DQ7ZFefk.js +0 -1
  630. package/web-ui/build/static/dockerfile-BpU0w0F-.js +0 -1
  631. package/web-ui/build/static/dos-B1_ZGbVG.js +0 -1
  632. package/web-ui/build/static/dot-BGB_tXU7.js +0 -1
  633. package/web-ui/build/static/dsconfig-BbDxyV_e.js +0 -1
  634. package/web-ui/build/static/dts-BfjOjY8l.js +0 -1
  635. package/web-ui/build/static/dust-BdwQ-7T_.js +0 -1
  636. package/web-ui/build/static/ebnf-BQTMmaYU.js +0 -1
  637. package/web-ui/build/static/ebnf-BwwBWilG.js +0 -1
  638. package/web-ui/build/static/editorconfig-DWS4VSOM.js +0 -1
  639. package/web-ui/build/static/eiffel-DVvlc0AI.js +0 -1
  640. package/web-ui/build/static/ejs-DsD496m-.js +0 -1
  641. package/web-ui/build/static/elixir-CLxuo9eh.js +0 -1
  642. package/web-ui/build/static/elixir-Cnyg3Ezx.js +0 -1
  643. package/web-ui/build/static/elm-CMAIkLMI.js +0 -1
  644. package/web-ui/build/static/elm-DZBnlwPV.js +0 -1
  645. package/web-ui/build/static/erb-B5xezjly.js +0 -1
  646. package/web-ui/build/static/erb-CJr8Ab4S.js +0 -1
  647. package/web-ui/build/static/erlang-BKy_rMFs.js +0 -1
  648. package/web-ui/build/static/erlang-DtaEL15_.js +0 -1
  649. package/web-ui/build/static/erlang-repl-CC5OZ0oZ.js +0 -1
  650. package/web-ui/build/static/etlua-1AfbSbi_.js +0 -1
  651. package/web-ui/build/static/excel-AEWYaJMN.js +0 -1
  652. package/web-ui/build/static/excel-formula-DXCH31eV.js +0 -1
  653. package/web-ui/build/static/factor-BvZsNxRC.js +0 -1
  654. package/web-ui/build/static/false-Y69sPjTa.js +0 -1
  655. package/web-ui/build/static/firestore-security-rules-BAYXaIU_.js +0 -1
  656. package/web-ui/build/static/fix-D7wVox5L.js +0 -1
  657. package/web-ui/build/static/flix-xYG3kZl-.js +0 -1
  658. package/web-ui/build/static/flow-zBPh0c8a.js +0 -1
  659. package/web-ui/build/static/fortran-C7OgAOwk.js +0 -1
  660. package/web-ui/build/static/fortran-D7HqnRra.js +0 -1
  661. package/web-ui/build/static/fsharp-Dz8jq_YD.js +0 -1
  662. package/web-ui/build/static/fsharp-wem8e879.js +0 -1
  663. package/web-ui/build/static/ftl-BNZhFjQT.js +0 -1
  664. package/web-ui/build/static/gams-C0WL3JTk.js +0 -1
  665. package/web-ui/build/static/gap-C3QTJPj2.js +0 -1
  666. package/web-ui/build/static/gauss-HDeDt6Js.js +0 -1
  667. package/web-ui/build/static/gcode-B6xVa-Q4.js +0 -1
  668. package/web-ui/build/static/gcode-KfjFk59Y.js +0 -1
  669. package/web-ui/build/static/gdscript-CZWTW6OT.js +0 -1
  670. package/web-ui/build/static/gedcom-DGTBZtDe.js +0 -1
  671. package/web-ui/build/static/gherkin-BM2fti5q.js +0 -1
  672. package/web-ui/build/static/gherkin-BsD-keLP.js +0 -1
  673. package/web-ui/build/static/git-CwXnrvg5.js +0 -1
  674. package/web-ui/build/static/glsl-CEL-DKdu.js +0 -1
  675. package/web-ui/build/static/glsl-DClg1hhL.js +0 -1
  676. package/web-ui/build/static/gml-BuyC2nUx.js +0 -1
  677. package/web-ui/build/static/gml-CaV0fexF.js +0 -1
  678. package/web-ui/build/static/gn-RZ4XHU09.js +0 -1
  679. package/web-ui/build/static/go-2fZP5dFL.js +0 -1
  680. package/web-ui/build/static/go-B-2r4QAR.js +0 -1
  681. package/web-ui/build/static/go-module-DSWZUUGo.js +0 -1
  682. package/web-ui/build/static/golo-BLZaLyU5.js +0 -1
  683. package/web-ui/build/static/gradle--RLe7WtU.js +0 -1
  684. package/web-ui/build/static/graphql-Dersm5rs.js +0 -1
  685. package/web-ui/build/static/groovy-BhCtzyFe.js +0 -1
  686. package/web-ui/build/static/groovy-CvbHavu3.js +0 -1
  687. package/web-ui/build/static/haml-CQhB89Xl.js +0 -1
  688. package/web-ui/build/static/haml-CemWzAX2.js +0 -1
  689. package/web-ui/build/static/handlebars-B39jLKjQ.js +0 -1
  690. package/web-ui/build/static/handlebars-Cl4dI91M.js +0 -1
  691. package/web-ui/build/static/haskell-CO9Ug-Kw.js +0 -1
  692. package/web-ui/build/static/haskell-EXme4CFR.js +0 -1
  693. package/web-ui/build/static/haxe-BDkCyqBy.js +0 -1
  694. package/web-ui/build/static/haxe-ahczQlFA.js +0 -1
  695. package/web-ui/build/static/hcl-liOTbdHr.js +0 -1
  696. package/web-ui/build/static/hlsl-Bnkb1zrP.js +0 -1
  697. package/web-ui/build/static/hoon-DCXHuTNp.js +0 -1
  698. package/web-ui/build/static/hpkp-CPJOGDF_.js +0 -1
  699. package/web-ui/build/static/hsp-BdeT0enM.js +0 -1
  700. package/web-ui/build/static/hsts-BoIN2VGK.js +0 -1
  701. package/web-ui/build/static/htmlbars-B5Ruf2zT.js +0 -1
  702. package/web-ui/build/static/http-BacVzmeC.js +0 -1
  703. package/web-ui/build/static/http-DifjpER4.js +0 -1
  704. package/web-ui/build/static/hy-DQ-M6mjk.js +0 -1
  705. package/web-ui/build/static/ichigojam-CcVrpthd.js +0 -1
  706. package/web-ui/build/static/icon-B4N1Fnx2.js +0 -1
  707. package/web-ui/build/static/icu-message-format-aDIl6Oaf.js +0 -1
  708. package/web-ui/build/static/idris-DKvV6i27.js +0 -1
  709. package/web-ui/build/static/iecst-zqZH79Dw.js +0 -1
  710. package/web-ui/build/static/ignore-BBbP-u2O.js +0 -1
  711. package/web-ui/build/static/index-CkhVK4lF.js +0 -1164
  712. package/web-ui/build/static/index-Cm1gK3R7.css +0 -1
  713. package/web-ui/build/static/index-D9JNKR2k.js +0 -13
  714. package/web-ui/build/static/index-DqZhvIWG.js +0 -1
  715. package/web-ui/build/static/inform7-BwvEArfL.js +0 -1
  716. package/web-ui/build/static/inform7-YkPDg57k.js +0 -1
  717. package/web-ui/build/static/ini-DU4drXRb.js +0 -1
  718. package/web-ui/build/static/ini-OhDTn0L5.js +0 -1
  719. package/web-ui/build/static/io-4thQGR-r.js +0 -1
  720. package/web-ui/build/static/irpf90-BnhmotbX.js +0 -1
  721. package/web-ui/build/static/isbl-BKcWS-M5.js +0 -1
  722. package/web-ui/build/static/j-Bw6CUCl9.js +0 -1
  723. package/web-ui/build/static/java-8IzvJ-mZ.js +0 -1
  724. package/web-ui/build/static/java-_jaQp1m2.js +0 -1
  725. package/web-ui/build/static/javadoc-D6KaHkhq.js +0 -1
  726. package/web-ui/build/static/javadoclike-B-DnR3Tl.js +0 -1
  727. package/web-ui/build/static/javascript-CfOOSye9.js +0 -1
  728. package/web-ui/build/static/javastacktrace-DHyDfhlB.js +0 -1
  729. package/web-ui/build/static/jboss-cli-zEH5UaRS.js +0 -1
  730. package/web-ui/build/static/jexl-CRpgdJ2f.js +0 -1
  731. package/web-ui/build/static/jolie-C5S16-nE.js +0 -1
  732. package/web-ui/build/static/jq-Dy-2LJZM.js +0 -1
  733. package/web-ui/build/static/js-extras-CNfXr3kE.js +0 -1
  734. package/web-ui/build/static/js-templates-DCJ1syUw.js +0 -1
  735. package/web-ui/build/static/jsdoc-BamyoRCh.js +0 -1
  736. package/web-ui/build/static/json-Buz-eeND.js +0 -1
  737. package/web-ui/build/static/json-D4xr_AK1.js +0 -1
  738. package/web-ui/build/static/json5-CS3uAaJP.js +0 -1
  739. package/web-ui/build/static/jsonp-CmEAdOcH.js +0 -1
  740. package/web-ui/build/static/jsstacktrace-D35jXpCN.js +0 -1
  741. package/web-ui/build/static/jsx-DRMEK-Qn.js +0 -1
  742. package/web-ui/build/static/julia-BgYJV7ho.js +0 -1
  743. package/web-ui/build/static/julia-CRsGDmxp.js +0 -1
  744. package/web-ui/build/static/julia-repl-D0vTZKUn.js +0 -1
  745. package/web-ui/build/static/keepalived-BlhBatvM.js +0 -1
  746. package/web-ui/build/static/keyman-B_-J66sg.js +0 -1
  747. package/web-ui/build/static/kotlin-B7HqYBGK.js +0 -1
  748. package/web-ui/build/static/kotlin-CszxhoS4.js +0 -1
  749. package/web-ui/build/static/kumir-B_wpMfXS.js +0 -1
  750. package/web-ui/build/static/kusto-BrHkOiE1.js +0 -1
  751. package/web-ui/build/static/lasso-xizuiq1s.js +0 -1
  752. package/web-ui/build/static/latex-62pryLTa.js +0 -1
  753. package/web-ui/build/static/latex-D7Tnbs2l.js +0 -1
  754. package/web-ui/build/static/latte-B_8xJj-y.js +0 -1
  755. package/web-ui/build/static/ldif-B69eIciC.js +0 -1
  756. package/web-ui/build/static/leaf-DPnh4k7N.js +0 -1
  757. package/web-ui/build/static/less-Bg3xuki-.js +0 -1
  758. package/web-ui/build/static/less-Cm-zhk4i.js +0 -1
  759. package/web-ui/build/static/lilypond-BFSXmrKQ.js +0 -1
  760. package/web-ui/build/static/liquid-D_jMXN_8.js +0 -1
  761. package/web-ui/build/static/lisp-CDWjXhWU.js +0 -1
  762. package/web-ui/build/static/lisp-DAu9fWrQ.js +0 -1
  763. package/web-ui/build/static/livecodeserver-BNgd6rBu.js +0 -1
  764. package/web-ui/build/static/livescript-C6sYNsdz.js +0 -1
  765. package/web-ui/build/static/livescript-CMOawYYt.js +0 -1
  766. package/web-ui/build/static/llvm-Yx2vcmJw.js +0 -1
  767. package/web-ui/build/static/llvm-w4vVb6qs.js +0 -1
  768. package/web-ui/build/static/log-CqhluDSP.js +0 -1
  769. package/web-ui/build/static/lolcode-Ck0rxP3x.js +0 -1
  770. package/web-ui/build/static/lsl-BwPX-Qjm.js +0 -1
  771. package/web-ui/build/static/lua-4H_Ijh4D.js +0 -1
  772. package/web-ui/build/static/lua-dDdlvQ6u.js +0 -1
  773. package/web-ui/build/static/magma-2nRRraug.js +0 -1
  774. package/web-ui/build/static/makefile-C887R9mV.js +0 -1
  775. package/web-ui/build/static/makefile-hlNtZePL.js +0 -1
  776. package/web-ui/build/static/markdown-DCALPnuW.js +0 -1
  777. package/web-ui/build/static/markdown-DGM_Z5tW.js +0 -1
  778. package/web-ui/build/static/markup-templating-PAvYDmBZ.js +0 -1
  779. package/web-ui/build/static/mathematica-CGL-Gomp.js +0 -1
  780. package/web-ui/build/static/matlab-C46ZZzlH.js +0 -1
  781. package/web-ui/build/static/matlab-WUgx8roJ.js +0 -1
  782. package/web-ui/build/static/maxima-Dd_TIy4H.js +0 -1
  783. package/web-ui/build/static/maxscript-f8SP9Ybn.js +0 -1
  784. package/web-ui/build/static/mel-6l2ZAh7F.js +0 -1
  785. package/web-ui/build/static/mel-BYdNMJzZ.js +0 -1
  786. package/web-ui/build/static/mercury-CdHf1buq.js +0 -1
  787. package/web-ui/build/static/mermaid-IRgUMtb1.js +0 -1
  788. package/web-ui/build/static/mipsasm-CRPpDKP4.js +0 -1
  789. package/web-ui/build/static/mizar-B4DBCLSi.js +0 -1
  790. package/web-ui/build/static/mizar-DiuZyDh7.js +0 -1
  791. package/web-ui/build/static/mojolicious-ChuQv8wb.js +0 -1
  792. package/web-ui/build/static/mongodb-CE5Epj0T.js +0 -1
  793. package/web-ui/build/static/monkey-BAz4pIwb.js +0 -1
  794. package/web-ui/build/static/monkey-i_g3gMkJ.js +0 -1
  795. package/web-ui/build/static/moonscript-BNaNpboV.js +0 -1
  796. package/web-ui/build/static/moonscript-uHlQVjxf.js +0 -1
  797. package/web-ui/build/static/n1ql-CBDLma2D.js +0 -1
  798. package/web-ui/build/static/n1ql-u4YLlLfk.js +0 -1
  799. package/web-ui/build/static/n4js-IVFjZEMP.js +0 -1
  800. package/web-ui/build/static/nand2tetris-hdl-Ba72aD32.js +0 -1
  801. package/web-ui/build/static/naniscript-CjMhybrb.js +0 -1
  802. package/web-ui/build/static/nasm-DjKhk70h.js +0 -1
  803. package/web-ui/build/static/neon-JEhMoh8P.js +0 -1
  804. package/web-ui/build/static/nevod-6tgpQVCA.js +0 -1
  805. package/web-ui/build/static/nginx-C6NwXcw_.js +0 -1
  806. package/web-ui/build/static/nginx-CquSTZbP.js +0 -1
  807. package/web-ui/build/static/nim-0bOOYxfL.js +0 -1
  808. package/web-ui/build/static/nim-aCQGMs_4.js +0 -1
  809. package/web-ui/build/static/nix-BV4kRCY_.js +0 -1
  810. package/web-ui/build/static/nix-FvSL2V8j.js +0 -1
  811. package/web-ui/build/static/node-repl-GtjTvb93.js +0 -1
  812. package/web-ui/build/static/nsis-DsftItKH.js +0 -1
  813. package/web-ui/build/static/nsis-pu7O80Pq.js +0 -1
  814. package/web-ui/build/static/objectivec-Bx7Bj0U_.js +0 -1
  815. package/web-ui/build/static/objectivec-GtO6Znp9.js +0 -1
  816. package/web-ui/build/static/ocaml-B71sR3qa.js +0 -1
  817. package/web-ui/build/static/ocaml-BlhoK75p.js +0 -1
  818. package/web-ui/build/static/opencl-40JZ6XXw.js +0 -1
  819. package/web-ui/build/static/openqasm-cDL6l7w3.js +0 -1
  820. package/web-ui/build/static/openscad-BQ0gNGv9.js +0 -1
  821. package/web-ui/build/static/oxygene-1fX2ftBa.js +0 -1
  822. package/web-ui/build/static/oz-Cgq2yQZq.js +0 -1
  823. package/web-ui/build/static/parigp-DfRO3DxU.js +0 -1
  824. package/web-ui/build/static/parser-hZcHrq-n.js +0 -1
  825. package/web-ui/build/static/parser3-Cf2p4Ryg.js +0 -1
  826. package/web-ui/build/static/pascal-C6tg_yvS.js +0 -1
  827. package/web-ui/build/static/pascaligo-9U9PdOS6.js +0 -1
  828. package/web-ui/build/static/pcaxis-B5EnEKrt.js +0 -1
  829. package/web-ui/build/static/peoplecode-Cf2sGzFJ.js +0 -1
  830. package/web-ui/build/static/perl-C9FNSHZx.js +0 -1
  831. package/web-ui/build/static/perl-uzSwl6wJ.js +0 -1
  832. package/web-ui/build/static/pf-C7Bjci1q.js +0 -1
  833. package/web-ui/build/static/pgsql-IBD1Cka7.js +0 -1
  834. package/web-ui/build/static/php-19XvqtTQ.js +0 -1
  835. package/web-ui/build/static/php-CUjVuQ1J.js +0 -1
  836. package/web-ui/build/static/php-extras-GWBU2Qbm.js +0 -1
  837. package/web-ui/build/static/php-template-Chtii014.js +0 -1
  838. package/web-ui/build/static/phpdoc-BLcJXgrV.js +0 -1
  839. package/web-ui/build/static/plaintext-BX3hHjdj.js +0 -1
  840. package/web-ui/build/static/plsql-tCcTmmsx.js +0 -1
  841. package/web-ui/build/static/pony-CKD16QkU.js +0 -1
  842. package/web-ui/build/static/powerquery-DXj7kqMe.js +0 -1
  843. package/web-ui/build/static/powershell-0jB2gvRR.js +0 -1
  844. package/web-ui/build/static/powershell-Ca6UjZV2.js +0 -1
  845. package/web-ui/build/static/processing-CPjwH4wc.js +0 -1
  846. package/web-ui/build/static/processing-ItKy7l8u.js +0 -1
  847. package/web-ui/build/static/profile-6N_Rzycx.js +0 -1
  848. package/web-ui/build/static/prolog-BV3TgLfG.js +0 -1
  849. package/web-ui/build/static/prolog-C5UoVyBz.js +0 -1
  850. package/web-ui/build/static/promql-CZot8Bw3.js +0 -1
  851. package/web-ui/build/static/properties-C_r_5f7N.js +0 -1
  852. package/web-ui/build/static/properties-qycKL1T0.js +0 -1
  853. package/web-ui/build/static/protobuf-BmdKEOw6.js +0 -1
  854. package/web-ui/build/static/protobuf-CxWC8PvT.js +0 -1
  855. package/web-ui/build/static/psl-CFSNRM7z.js +0 -1
  856. package/web-ui/build/static/pug-8dv12FtI.js +0 -1
  857. package/web-ui/build/static/puppet-CNjskGXn.js +0 -1
  858. package/web-ui/build/static/puppet-Cf6xZODN.js +0 -1
  859. package/web-ui/build/static/pure-exFir566.js +0 -1
  860. package/web-ui/build/static/purebasic-Bm6QaKku.js +0 -1
  861. package/web-ui/build/static/purebasic-lfzND9Qp.js +0 -1
  862. package/web-ui/build/static/purescript-D1xRYVQG.js +0 -1
  863. package/web-ui/build/static/python-DgkOjU0T.js +0 -1
  864. package/web-ui/build/static/python-o7BJVaX6.js +0 -1
  865. package/web-ui/build/static/python-repl-6SyonvH1.js +0 -1
  866. package/web-ui/build/static/q-CiQ2AFzU.js +0 -1
  867. package/web-ui/build/static/q-g7rEIWgm.js +0 -1
  868. package/web-ui/build/static/qml-ARc9Akfg.js +0 -1
  869. package/web-ui/build/static/qml-rVumxss_.js +0 -1
  870. package/web-ui/build/static/qore-CUXpqOSx.js +0 -1
  871. package/web-ui/build/static/qsharp-DMPNcgaz.js +0 -1
  872. package/web-ui/build/static/r-DCIo6joW.js +0 -1
  873. package/web-ui/build/static/r-nUBzz4LB.js +0 -1
  874. package/web-ui/build/static/racket--fPY_Q4o.js +0 -1
  875. package/web-ui/build/static/reason-CeVN9vJx.js +0 -1
  876. package/web-ui/build/static/reasonml-49SEasi3.js +0 -1
  877. package/web-ui/build/static/regex-BS97XMMk.js +0 -1
  878. package/web-ui/build/static/rego-Brc7DU11.js +0 -1
  879. package/web-ui/build/static/renpy-C2sPWAHv.js +0 -1
  880. package/web-ui/build/static/rest-DlZ7ycRe.js +0 -1
  881. package/web-ui/build/static/rib-C8WsA4on.js +0 -1
  882. package/web-ui/build/static/rip-dd9bS3Ar.js +0 -1
  883. package/web-ui/build/static/roboconf-CAEw-rS3.js +0 -1
  884. package/web-ui/build/static/roboconf-D0TfrLeu.js +0 -1
  885. package/web-ui/build/static/robotframework-CkgVY4bX.js +0 -1
  886. package/web-ui/build/static/routeros-DIek1v-Z.js +0 -1
  887. package/web-ui/build/static/rsl-DExhHtA1.js +0 -1
  888. package/web-ui/build/static/ruby-BjvzODgO.js +0 -1
  889. package/web-ui/build/static/ruby-Dflr1gcA.js +0 -1
  890. package/web-ui/build/static/ruleslanguage-BgzP6UBl.js +0 -1
  891. package/web-ui/build/static/rust-DxIro-GW.js +0 -1
  892. package/web-ui/build/static/rust-qWzP5Jcw.js +0 -1
  893. package/web-ui/build/static/sas-BrCJWU_w.js +0 -1
  894. package/web-ui/build/static/sas-itaOGZ-D.js +0 -1
  895. package/web-ui/build/static/sass-BTk1_hyU.js +0 -1
  896. package/web-ui/build/static/scala-DEtImhEo.js +0 -1
  897. package/web-ui/build/static/scala-DdZCH_nS.js +0 -1
  898. package/web-ui/build/static/scheme-CiOCQffl.js +0 -1
  899. package/web-ui/build/static/scheme-WaU-JPJX.js +0 -1
  900. package/web-ui/build/static/scilab-03tyg0ft.js +0 -1
  901. package/web-ui/build/static/scss-DXl2WSdH.js +0 -1
  902. package/web-ui/build/static/scss-movJ3vcX.js +0 -1
  903. package/web-ui/build/static/shell-DWmlNcrf.js +0 -1
  904. package/web-ui/build/static/shell-session-lHcfQbT0.js +0 -1
  905. package/web-ui/build/static/smali-7RKwn9RZ.js +0 -1
  906. package/web-ui/build/static/smali-TEMQ7bRM.js +0 -1
  907. package/web-ui/build/static/smalltalk-B_nVEfab.js +0 -1
  908. package/web-ui/build/static/smalltalk-BsA9wucb.js +0 -1
  909. package/web-ui/build/static/smarty-DSf4s_8v.js +0 -1
  910. package/web-ui/build/static/sml-BDO_Hmie.js +0 -1
  911. package/web-ui/build/static/sml-DKuy82zG.js +0 -1
  912. package/web-ui/build/static/solidity-jtWv6laA.js +0 -1
  913. package/web-ui/build/static/solution-file-DcfiquuJ.js +0 -1
  914. package/web-ui/build/static/soy-DmBVrDYd.js +0 -1
  915. package/web-ui/build/static/sparql-_JOxZ7fM.js +0 -1
  916. package/web-ui/build/static/splunk-spl-CHeBtp33.js +0 -1
  917. package/web-ui/build/static/sqf-9wI8rp5o.js +0 -1
  918. package/web-ui/build/static/sqf-BuKh30bG.js +0 -1
  919. package/web-ui/build/static/sql-ClFFHGMY.js +0 -1
  920. package/web-ui/build/static/sql-DVXtimpW.js +0 -1
  921. package/web-ui/build/static/sql_more-CNw-JJON.js +0 -1
  922. package/web-ui/build/static/squirrel-Bdi8LbT5.js +0 -1
  923. package/web-ui/build/static/stan-Brcz64A8.js +0 -1
  924. package/web-ui/build/static/stan-C9Q08Juk.js +0 -1
  925. package/web-ui/build/static/stata-ES8hryw_.js +0 -1
  926. package/web-ui/build/static/step21-Cfmoq4Lb.js +0 -1
  927. package/web-ui/build/static/stylus-DMKHLSvm.js +0 -1
  928. package/web-ui/build/static/stylus-Dcs8vyW2.js +0 -1
  929. package/web-ui/build/static/subunit-DRjv565H.js +0 -1
  930. package/web-ui/build/static/swift-0x_T9zx8.js +0 -1
  931. package/web-ui/build/static/swift-CUvpRg4a.js +0 -1
  932. package/web-ui/build/static/systemd-Dl8xGVv9.js +0 -1
  933. package/web-ui/build/static/t4-cs-DqvXuZwc.js +0 -1
  934. package/web-ui/build/static/t4-templating-DjN2N1uM.js +0 -1
  935. package/web-ui/build/static/t4-vb-CZQn9IVK.js +0 -1
  936. package/web-ui/build/static/taggerscript-CPCJkC2t.js +0 -1
  937. package/web-ui/build/static/tap-BPfuLhe7.js +0 -1
  938. package/web-ui/build/static/tap-CSdsd38Q.js +0 -1
  939. package/web-ui/build/static/tcl-4BqhAKLC.js +0 -1
  940. package/web-ui/build/static/tcl-Bh0Jg8jj.js +0 -1
  941. package/web-ui/build/static/textile-BiD4awlU.js +0 -1
  942. package/web-ui/build/static/thrift-Br1-10xh.js +0 -1
  943. package/web-ui/build/static/toml-QlLCqVpQ.js +0 -1
  944. package/web-ui/build/static/tp-B2Ef9EBz.js +0 -1
  945. package/web-ui/build/static/tremor-DuZdAfVr.js +0 -1
  946. package/web-ui/build/static/tsx-W3KEjccO.js +0 -1
  947. package/web-ui/build/static/tt2-D_tdF-56.js +0 -1
  948. package/web-ui/build/static/turtle-DxrQkFoq.js +0 -1
  949. package/web-ui/build/static/twig-DD44ApyU.js +0 -1
  950. package/web-ui/build/static/twig-vA3ZwAxn.js +0 -1
  951. package/web-ui/build/static/typescript-BfbcKNOu.js +0 -1
  952. package/web-ui/build/static/typescript-CvKtq1ix.js +0 -1
  953. package/web-ui/build/static/typoscript-BEu84JpE.js +0 -1
  954. package/web-ui/build/static/unrealscript-ohUNsJGM.js +0 -1
  955. package/web-ui/build/static/uorazor-BWer-chm.js +0 -1
  956. package/web-ui/build/static/uri-CPLTr_NY.js +0 -1
  957. package/web-ui/build/static/v-gAkMRmmI.js +0 -1
  958. package/web-ui/build/static/vala-Bktj5SNB.js +0 -1
  959. package/web-ui/build/static/vala-D1fDPFxI.js +0 -1
  960. package/web-ui/build/static/vbnet-Cn0g0ohh.js +0 -1
  961. package/web-ui/build/static/vbnet-TiYHF2WM.js +0 -1
  962. package/web-ui/build/static/vbscript-BUV-Kjvs.js +0 -1
  963. package/web-ui/build/static/vbscript-html-s1RgByjf.js +0 -1
  964. package/web-ui/build/static/velocity-DTy-TOwB.js +0 -1
  965. package/web-ui/build/static/verilog-4jk4MMXN.js +0 -1
  966. package/web-ui/build/static/verilog-aR4U-DJX.js +0 -1
  967. package/web-ui/build/static/vhdl-D72XnBZR.js +0 -1
  968. package/web-ui/build/static/vhdl-DFdtG28B.js +0 -1
  969. package/web-ui/build/static/vim-BMqsbQ-a.js +0 -1
  970. package/web-ui/build/static/vim-Cc3UuKOs.js +0 -1
  971. package/web-ui/build/static/visual-basic-CyC2kNGV.js +0 -1
  972. package/web-ui/build/static/warpscript-DpKFeISB.js +0 -1
  973. package/web-ui/build/static/wasm-SPljCkFH.js +0 -1
  974. package/web-ui/build/static/web-idl-Cie2yHU9.js +0 -1
  975. package/web-ui/build/static/wiki-BfYfwsBk.js +0 -1
  976. package/web-ui/build/static/wolfram-Dgi1K2gQ.js +0 -1
  977. package/web-ui/build/static/wren-CFQxgLld.js +0 -1
  978. package/web-ui/build/static/x86asm-CBIt2r64.js +0 -1
  979. package/web-ui/build/static/xeora-e5JHDF1T.js +0 -1
  980. package/web-ui/build/static/xl-CPZe-czl.js +0 -1
  981. package/web-ui/build/static/xml-BEuPuoS1.js +0 -1
  982. package/web-ui/build/static/xml-doc-DTOJGYE2.js +0 -1
  983. package/web-ui/build/static/xojo-ByMSKdYJ.js +0 -1
  984. package/web-ui/build/static/xquery--5iaD5oE.js +0 -1
  985. package/web-ui/build/static/xquery-DJp5PXZo.js +0 -1
  986. package/web-ui/build/static/yaml-CH8EnM9M.js +0 -1
  987. package/web-ui/build/static/yaml-mKr8oN1v.js +0 -1
  988. package/web-ui/build/static/yang-Nqdvm2mQ.js +0 -1
  989. package/web-ui/build/static/zephir-BJZaDzrW.js +0 -1
  990. package/web-ui/build/static/zig-DRkhvZLn.js +0 -1
@@ -1,1386 +1,1400 @@
1
- /**
2
- * AgentCommunicationTool - Enables inter-agent communication with safety mechanisms
3
- *
4
- * Purpose:
5
- * - Allow agents to discover and communicate with other active agents
6
- * - Manage message threads with reply tracking
7
- * - Prevent conversation loops and exponential message growth
8
- * - Support file attachments for rich communication
9
- *
10
- * Design Principles:
11
- * - Loosely coupled with system components
12
- * - Message persistence for async communication
13
- * - Conversation limits to prevent runaway threads
14
- * - Agent lifecycle awareness
15
- */
16
-
17
- import { BaseTool } from './baseTool.js';
18
- import { promises as fs } from 'fs';
19
- import path from 'path';
20
- import crypto from 'crypto';
21
-
22
- class AgentCommunicationTool extends BaseTool {
23
- constructor(config = {}) {
24
- super('agentcommunication', 'Agent Communication', 'communication');
25
-
26
- // Configuration with safety defaults
27
- this.config = {
28
- maxConversationDepth: config.maxConversationDepth || 10,
29
- maxRecipientsPerMessage: config.maxRecipientsPerMessage || 3,
30
- maxAttachmentSize: config.maxAttachmentSize || 10 * 1024 * 1024, // 10MB
31
- maxAttachmentsPerMessage: config.maxAttachmentsPerMessage || 5,
32
- conversationTimeout: config.conversationTimeout || 3600000, // 1 hour
33
- messageRetentionPeriod: config.messageRetentionPeriod || 86400000, // 24 hours
34
- enableBroadcast: config.enableBroadcast || false,
35
- storageDir: config.storageDir || '.loxia-messages'
36
- };
37
-
38
- // Message storage - in production, this would be a database
39
- this.messages = new Map(); // messageId -> message object
40
-
41
- // Shared helper for every check-point to resolve effective limits
42
- // from global defaults + per-agent overrides (agent.toolConfig.agentcommunication).
43
- // See BaseTool#getEffectiveConfig — per-agent fields win.
44
- this._limits = (context) => this.getEffectiveConfig(context || {}, this.config);
45
- this.conversations = new Map(); // conversationId -> conversation metadata
46
- this.agentInboxes = new Map(); // agentId -> Set of messageIds
47
- this.agentConversations = new Map(); // agentId -> Set of conversationIds
48
-
49
- // Safety tracking
50
- this.conversationDepths = new Map(); // conversationId -> current depth
51
- this.lastActivityTimes = new Map(); // conversationId -> timestamp
52
- this.agentMessageCounts = new Map(); // agentId -> { sent: number, received: number }
53
-
54
- // Initialize storage directory
55
- this._initializeStorage();
56
- }
57
-
58
- /**
59
- * Get tool description for agent system prompts
60
- */
61
- getDescription() {
62
- return `
63
- Agent Communication Tool: Enables communication between active agents.
64
-
65
- USAGE:
66
- \`\`\`json
67
- {
68
- "toolId": "agentcommunication",
69
- "actions": [{"type": "action-name", ...params}]
70
- }
71
- \`\`\`
72
-
73
- ACTIONS:
74
- - get-available-agents: List active agents
75
- - send-message: Send message (recipient, subject, message, priority, requiresReply)
76
- - reply-to-message: Reply to message (messageId, message)
77
- - get-unreplied-messages: Get pending messages
78
- - mark-conversation-ended: End conversation (conversationId, reason)
79
-
80
- requiresReply PARAMETER (send-message):
81
- - Defaults to true. Set to false ONLY for fire-and-forget notifications where you do NOT need a response.
82
- - When true, the recipient agent will be explicitly instructed to reply back to you.
83
- - When false, the recipient will handle the message but is not expected to reply.
84
-
85
- EXAMPLES:
86
-
87
- Get available agents:
88
- \`\`\`json
89
- {"toolId": "agentcommunication", "actions": [{"type": "get-available-agents"}]}
90
- \`\`\`
91
-
92
- Send a message:
93
- \`\`\`json
94
- {
95
- "toolId": "agentcommunication",
96
- "actions": [{
97
- "type": "send-message",
98
- "recipient": "agent-fullstack-developer-1234567890123",
99
- "subject": "Code review needed",
100
- "message": "Please review the authentication module",
101
- "priority": "normal",
102
- "requiresReply": true
103
- }]
104
- }
105
- \`\`\`
106
-
107
- Reply to a message:
108
- \`\`\`json
109
- {
110
- "toolId": "agentcommunication",
111
- "actions": [{
112
- "type": "reply-to-message",
113
- "messageId": "msg-789",
114
- "message": "Review complete. All issues addressed."
115
- }]
116
- }
117
- \`\`\`
118
-
119
- IMPORTANT: Use full agent ID from get-available-agents as recipient.
120
-
121
- LIMITS:
122
- - Max ${this.config.maxConversationDepth} replies per thread
123
- - Max ${this.config.maxRecipientsPerMessage} recipients per message
124
- - Conversations expire after ${this.config.conversationTimeout / 60000} minutes
125
- `.trim();
126
- }
127
-
128
- /**
129
- * Parse tool parameters from command content
130
- */
131
- parseParameters(content) {
132
- // Handle JSON format (for direct tool calls)
133
- if (typeof content === 'object' && content !== null) {
134
- // If it's already an object, extract the parameters
135
- if (content.actions && Array.isArray(content.actions) && content.actions.length > 0) {
136
- // Handle format: {"actions": [{"type": "get-available-agents", ...}]}
137
- const action = content.actions[0];
138
- return {
139
- action: action.type,
140
- ...action
141
- };
142
- } else if (content.action || content.type) {
143
- // Handle format: {"action": "get-available-agents", ...}
144
- return {
145
- action: content.action || content.type,
146
- ...content
147
- };
148
- }
149
- return content;
150
- }
151
-
152
- // Handle string content (could be JSON string or XML)
153
- if (typeof content === 'string') {
154
- // Try parsing as JSON first
155
- if (content.trim().startsWith('{')) {
156
- try {
157
- const parsed = JSON.parse(content);
158
- return this.parseParameters(parsed); // Recursive call to handle the parsed object
159
- } catch (error) {
160
- // Not valid JSON, continue to XML parsing
161
- }
162
- }
163
-
164
- // Parse XML-style commands for agent communication
165
- const parameters = {};
166
-
167
- // Extract action parameter
168
- const actionMatch = content.match(/<action[^>]*>([^<]+)<\/action>/);
169
- if (actionMatch) {
170
- parameters.action = actionMatch[1].trim();
171
- }
172
-
173
- // Extract other parameters like recipient, subject, message, etc.
174
- const tags = [
175
- 'recipient', 'recipients', 'subject', 'message', 'attachments',
176
- 'priority', 'requires-reply', 'message-id', 'cc-recipients',
177
- 'include-low-priority', 'conversation-id', 'reason', 'mark-resolved'
178
- ];
179
-
180
- for (const tag of tags) {
181
- const regex = new RegExp(`<${tag.replace('-', '\\-')}[^>]*>(.*?)<\\/${tag.replace('-', '\\-')}>`, 's');
182
- const match = content.match(regex);
183
- if (match) {
184
- let value = match[1].trim();
185
- // Try to parse JSON values
186
- if (value.startsWith('[') || value.startsWith('{') || value === 'true' || value === 'false') {
187
- try {
188
- value = JSON.parse(value);
189
- } catch {
190
- // Keep as string if JSON parsing fails
191
- }
192
- }
193
- // Convert kebab-case to camelCase for parameter names
194
- const paramName = tag.replace(/-(.)/g, (_, char) => char.toUpperCase());
195
- parameters[paramName] = value;
196
- }
197
- }
198
-
199
- // Extract attributes from action tag if present
200
- const actionWithAttribs = content.match(/<action([^>]*)>([^<]+)<\/action>/);
201
- if (actionWithAttribs && actionWithAttribs[1]) {
202
- // Parse attributes like priority="high", requires-reply="true"
203
- const attribMatches = actionWithAttribs[1].matchAll(/([\w-]+)=["']([^"']+)["']/g);
204
- for (const match of attribMatches) {
205
- const key = match[1].replace(/-(.)/g, (_, char) => char.toUpperCase()); // Convert kebab-case to camelCase
206
- parameters[key] = match[2];
207
- }
208
- }
209
-
210
- return parameters;
211
- }
212
-
213
- // Fallback
214
- return content || {};
215
- }
216
-
217
- /**
218
- * Execute the tool action
219
- */
220
- async execute(parameters = {}, context = {}) {
221
- const { action } = parameters;
222
-
223
- if (!action) {
224
- throw new Error('Action parameter is required');
225
- }
226
-
227
- // Validate requesting agent exists
228
- const requestingAgentId = context.agentId;
229
- if (!requestingAgentId) {
230
- throw new Error('Agent ID is required in context');
231
- }
232
-
233
- // Route to appropriate action handler
234
- switch (action.toLowerCase()) {
235
- case 'get-available-agents':
236
- return await this.getAvailableAgents(requestingAgentId, parameters, context);
237
-
238
- case 'send-message':
239
- return await this.sendMessage(requestingAgentId, parameters, context);
240
-
241
- case 'reply-to-message':
242
- return await this.replyToMessage(requestingAgentId, parameters, context);
243
-
244
- case 'get-unreplied-messages':
245
- return await this.getUnrepliedMessages(requestingAgentId, parameters, context);
246
-
247
- case 'mark-conversation-ended':
248
- return await this.markConversationEnded(requestingAgentId, parameters, context);
249
-
250
- default:
251
- throw new Error(`Unknown action: ${action}`);
252
- }
253
- }
254
-
255
- /**
256
- * Get list of available agents
257
- */
258
- async getAvailableAgents(requestingAgentId, parameters, context) {
259
- try {
260
- // Get agent pool from context
261
- const agentPool = context.agentPool;
262
- if (!agentPool) {
263
- throw new Error('Agent pool not available in context');
264
- }
265
-
266
- // Get all active agents
267
- const agents = await agentPool.listActiveAgents();
268
-
269
- // Normalize teams metadata to array format
270
- // Supports: metadata.teams = [{id, name, role}], or legacy metadata.teamId (string)
271
- const normalizeTeams = (metadata) => {
272
- if (!metadata) return [];
273
- // New format: teams array
274
- if (Array.isArray(metadata.teams)) return metadata.teams;
275
- // Legacy format: single teamId string
276
- if (metadata.teamId) {
277
- return [{ id: metadata.teamId, name: metadata.teamName || null, role: metadata.teamRole || null }];
278
- }
279
- return [];
280
- };
281
-
282
- // Resolve the requesting agent's teams
283
- const requestingAgent = agents.find(a => a.id === requestingAgentId);
284
- const myTeams = normalizeTeams(requestingAgent?.metadata);
285
- const myTeamIds = new Set(myTeams.map(t => t.id));
286
-
287
- // Filter out requesting agent and format response
288
- const availableAgents = agents
289
- .filter(agent => agent.id !== requestingAgentId && !agent.isPaused)
290
- .map(agent => {
291
- const agentTeams = normalizeTeams(agent.metadata);
292
- const agentTeamIds = agentTeams.map(t => t.id);
293
- // Teams shared with the requesting agent
294
- const sharedTeamIds = agentTeamIds.filter(id => myTeamIds.has(id));
295
-
296
- return {
297
- id: agent.id,
298
- name: agent.name,
299
- type: agent.type,
300
- capabilities: agent.capabilities,
301
- status: agent.status,
302
- // Multi-team affiliation
303
- teams: agentTeams, // [{id, name, role}, ...]
304
- // Relationship to the requesting agent
305
- sameTeam: sharedTeamIds.length > 0,
306
- sharedTeams: sharedTeamIds, // which team IDs are shared
307
- messageStats: this.agentMessageCounts.get(agent.id) || { sent: 0, received: 0 },
308
- activeConversations: (this.agentConversations.get(agent.id) || new Set()).size
309
- };
310
- });
311
-
312
- return {
313
- success: true,
314
- agents: availableAgents,
315
- totalActive: availableAgents.length,
316
- // The requesting agent's own team context
317
- yourTeams: myTeams.length > 0 ? myTeams : null,
318
- timestamp: new Date().toISOString()
319
- };
320
-
321
- } catch (error) {
322
- return {
323
- success: false,
324
- error: error.message
325
- };
326
- }
327
- }
328
-
329
- /**
330
- * Send a message to another agent
331
- */
332
- async sendMessage(senderAgentId, parameters, context) {
333
- try {
334
- const {
335
- recipient,
336
- recipients, // Support both single and multiple
337
- subject,
338
- message,
339
- attachments,
340
- priority = 'normal',
341
- 'requires-reply': requiresReply = true
342
- } = parameters;
343
-
344
- // Validate required fields
345
- if (!subject || !message) {
346
- throw new Error('Subject and message are required');
347
- }
348
-
349
- // Determine recipients
350
- const recipientList = this._parseRecipients(recipient, recipients);
351
- if (recipientList.length === 0) {
352
- throw new Error('At least one recipient is required');
353
- }
354
-
355
- // Validate recipient count — effective limit from per-agent config.
356
- const _sendLimits = this._limits(context);
357
- if (recipientList.length > _sendLimits.maxRecipientsPerMessage) {
358
- throw new Error(`Maximum ${_sendLimits.maxRecipientsPerMessage} recipients allowed`);
359
- }
360
-
361
- // Get agent pool and sender agent
362
- const agentPool = context.agentPool;
363
- const senderAgent = await agentPool.getAgent(senderAgentId);
364
- if (!senderAgent) {
365
- throw new Error('Sender agent not found');
366
- }
367
-
368
- // CRITICAL: Check if sender can send messages to recipients
369
- const blockedRecipients = [];
370
- for (const recipientId of recipientList) {
371
- const canSend = await this._canSendMessage(senderAgent, recipientId, context);
372
- if (!canSend.allowed) {
373
- blockedRecipients.push({
374
- recipientId,
375
- reason: canSend.reason,
376
- waitUntil: canSend.waitUntil
377
- });
378
- }
379
- }
380
-
381
- // If any recipients are blocked, apply delay and return
382
- if (blockedRecipients.length > 0) {
383
- const earliestAllowedTime = Math.max(...blockedRecipients.map(b => b.waitUntil || 0));
384
- const delayUntil = new Date(earliestAllowedTime);
385
-
386
- // Apply delay using existing infrastructure
387
- await agentPool.updateAgent(senderAgentId, {
388
- delayEndTime: delayUntil.toISOString()
389
- });
390
-
391
- const delaySeconds = Math.ceil((earliestAllowedTime - Date.now()) / 1000);
392
-
393
- return {
394
- success: true,
395
- delayed: true,
396
- delayUntil: delayUntil.toISOString(),
397
- delaySeconds,
398
- message: `Waiting ${delaySeconds}s before next message. Recipients need time to respond.`,
399
- blockedRecipients: blockedRecipients.map(b => ({
400
- recipientId: b.recipientId,
401
- reason: b.reason
402
- }))
403
- };
404
- }
405
-
406
- // Validate recipients exist and get their names
407
- const recipientAgents = {};
408
- const invalidRecipients = [];
409
-
410
- for (const recipientId of recipientList) {
411
- const agent = await agentPool.getAgent(recipientId);
412
- if (!agent) {
413
- invalidRecipients.push(recipientId);
414
- } else {
415
- recipientAgents[recipientId] = agent.name;
416
- }
417
- }
418
-
419
- // If any recipients are invalid, provide helpful error with suggestions
420
- if (invalidRecipients.length > 0) {
421
- const availableAgents = await agentPool.listActiveAgents();
422
- const suggestions = availableAgents
423
- .filter(agent => agent.id !== senderAgentId && !agent.isPaused)
424
- .map(agent => `- ${agent.name} (ID: ${agent.id})`)
425
- .join('\n');
426
-
427
- return {
428
- success: false,
429
- error: `Recipient agent(s) not found: ${invalidRecipients.join(', ')}`,
430
- suggestion: `Available agents you can message:\n${suggestions}`,
431
- availableAgents: availableAgents.map(agent => ({
432
- id: agent.id,
433
- name: agent.name,
434
- capabilities: agent.capabilities
435
- }))
436
- };
437
- }
438
-
439
- // Get sender agent name
440
- const senderName = senderAgent ? senderAgent.name : senderAgentId;
441
-
442
- // Process attachments if provided
443
- const processedAttachments = await this._processAttachments(attachments, senderAgentId, context);
444
-
445
- // Create message object
446
- const messageId = this._generateMessageId();
447
- const conversationId = this._generateConversationId();
448
- const timestamp = new Date().toISOString();
449
-
450
- const messageObj = {
451
- id: messageId,
452
- conversationId,
453
- sender: senderAgentId,
454
- senderName,
455
- recipients: recipientList,
456
- recipientNames: recipientAgents,
457
- subject,
458
- content: message,
459
- attachments: processedAttachments,
460
- priority,
461
- requiresReply,
462
- timestamp,
463
- status: 'sent',
464
- replies: [],
465
- metadata: {
466
- depth: 0,
467
- isRoot: true
468
- }
469
- };
470
-
471
- // Store message
472
- this.messages.set(messageId, messageObj);
473
-
474
- // Initialize conversation
475
- this.conversations.set(conversationId, {
476
- id: conversationId,
477
- rootMessageId: messageId,
478
- participants: [senderAgentId, ...recipientList],
479
- startTime: timestamp,
480
- lastActivity: timestamp,
481
- status: 'active',
482
- messageCount: 1
483
- });
484
-
485
- // Update inboxes
486
- for (const recipientId of recipientList) {
487
- if (!this.agentInboxes.has(recipientId)) {
488
- this.agentInboxes.set(recipientId, new Set());
489
- }
490
- this.agentInboxes.get(recipientId).add(messageId);
491
-
492
- // Track conversations
493
- if (!this.agentConversations.has(recipientId)) {
494
- this.agentConversations.set(recipientId, new Set());
495
- }
496
- this.agentConversations.get(recipientId).add(conversationId);
497
- }
498
-
499
- // Track sender's conversation
500
- if (!this.agentConversations.has(senderAgentId)) {
501
- this.agentConversations.set(senderAgentId, new Set());
502
- }
503
- this.agentConversations.get(senderAgentId).add(conversationId);
504
-
505
- // Update message counts
506
- this._updateMessageCounts(senderAgentId, 'sent');
507
- for (const recipientId of recipientList) {
508
- this._updateMessageCounts(recipientId, 'received');
509
- }
510
-
511
- // CRITICAL: Update inter-agent conversation tracking
512
- for (const recipientId of recipientList) {
513
- await this._updateConversationTracking(senderAgentId, recipientId, 'sent', context);
514
- }
515
-
516
- // Notify recipients through agent pool
517
- await this._notifyRecipients(messageObj, context);
518
-
519
- // Broadcast to WebSocket for UI visibility
520
- await this._broadcastToUI(messageObj, 'agent-message-sent', context);
521
-
522
- return {
523
- success: true,
524
- messageId,
525
- conversationId,
526
- recipients: recipientList,
527
- timestamp,
528
- message: 'Message sent successfully'
529
- };
530
-
531
- } catch (error) {
532
- return {
533
- success: false,
534
- error: error.message
535
- };
536
- }
537
- }
538
-
539
- /**
540
- * Reply to an existing message
541
- */
542
- async replyToMessage(senderAgentId, parameters, context) {
543
- try {
544
- const {
545
- 'message-id': originalMessageId,
546
- message,
547
- 'cc-recipients': ccRecipients,
548
- attachments,
549
- 'mark-resolved': markResolved = false
550
- } = parameters;
551
-
552
- // Validate required fields
553
- if (!originalMessageId || !message) {
554
- throw new Error('Original message ID and reply content are required');
555
- }
556
-
557
- // Get original message
558
- const originalMessage = this.messages.get(originalMessageId);
559
- if (!originalMessage) {
560
- throw new Error(`Original message not found: ${originalMessageId}`);
561
- }
562
-
563
- // Verify sender was a recipient or sender of original message
564
- const isParticipant = originalMessage.sender === senderAgentId ||
565
- originalMessage.recipients.includes(senderAgentId);
566
- if (!isParticipant) {
567
- throw new Error('You are not a participant in this conversation');
568
- }
569
-
570
- // Check conversation depth to prevent infinite loops
571
- const conversation = this.conversations.get(originalMessage.conversationId);
572
- const currentDepth = this._getConversationDepth(originalMessage.conversationId);
573
- const _replyLimits = this._limits(context);
574
-
575
- if (currentDepth >= _replyLimits.maxConversationDepth) {
576
- return {
577
- success: false,
578
- error: `Conversation depth limit reached (${_replyLimits.maxConversationDepth}). Please start a new conversation.`,
579
- suggestion: 'Consider marking this conversation as ended and starting fresh if needed.'
580
- };
581
- }
582
-
583
- // Determine reply recipients
584
- let replyRecipients = [originalMessage.sender];
585
- if (originalMessage.sender === senderAgentId) {
586
- // If replying to own message, reply to original recipients
587
- replyRecipients = originalMessage.recipients;
588
- }
589
-
590
- // Add CC recipients if specified
591
- if (ccRecipients) {
592
- const ccList = this._parseRecipients(null, ccRecipients);
593
- replyRecipients = [...new Set([...replyRecipients, ...ccList])];
594
- }
595
-
596
- // Remove sender from recipients
597
- replyRecipients = replyRecipients.filter(id => id !== senderAgentId);
598
-
599
- // Validate recipient count — effective limit from per-agent config.
600
- if (replyRecipients.length > _replyLimits.maxRecipientsPerMessage) {
601
- throw new Error(`Maximum ${_replyLimits.maxRecipientsPerMessage} recipients allowed`);
602
- }
603
-
604
- // Get agent names
605
- const agentPool = context.agentPool;
606
- const senderAgent = await agentPool.getAgent(senderAgentId);
607
- const senderName = senderAgent ? senderAgent.name : senderAgentId;
608
-
609
- const recipientAgents = {};
610
- for (const recipientId of replyRecipients) {
611
- const agent = await agentPool.getAgent(recipientId);
612
- if (agent) {
613
- recipientAgents[recipientId] = agent.name;
614
- }
615
- }
616
-
617
- // Process attachments
618
- const processedAttachments = await this._processAttachments(attachments, senderAgentId, context);
619
-
620
- // Create reply message
621
- const replyMessageId = this._generateMessageId();
622
- const timestamp = new Date().toISOString();
623
-
624
- const replyMessage = {
625
- id: replyMessageId,
626
- conversationId: originalMessage.conversationId,
627
- sender: senderAgentId,
628
- senderName,
629
- recipients: replyRecipients,
630
- recipientNames: recipientAgents,
631
- subject: `Re: ${originalMessage.subject}`,
632
- content: message,
633
- attachments: processedAttachments,
634
- priority: originalMessage.priority,
635
- requiresReply: !markResolved,
636
- timestamp,
637
- status: 'sent',
638
- replies: [],
639
- metadata: {
640
- depth: currentDepth + 1,
641
- isRoot: false,
642
- inReplyTo: originalMessageId
643
- }
644
- };
645
-
646
- // Store reply
647
- this.messages.set(replyMessageId, replyMessage);
648
- originalMessage.replies.push(replyMessageId);
649
-
650
- // Update conversation
651
- conversation.lastActivity = timestamp;
652
- conversation.messageCount++;
653
- if (markResolved) {
654
- conversation.status = 'resolved';
655
- }
656
-
657
- // Update inboxes
658
- for (const recipientId of replyRecipients) {
659
- if (!this.agentInboxes.has(recipientId)) {
660
- this.agentInboxes.set(recipientId, new Set());
661
- }
662
- this.agentInboxes.get(recipientId).add(replyMessageId);
663
- }
664
-
665
- // Update message counts
666
- this._updateMessageCounts(senderAgentId, 'sent');
667
- for (const recipientId of replyRecipients) {
668
- this._updateMessageCounts(recipientId, 'received');
669
- }
670
-
671
- // CRITICAL: Update inter-agent conversation tracking for replies
672
- for (const recipientId of replyRecipients) {
673
- await this._updateConversationTracking(senderAgentId, recipientId, 'replied', context);
674
- }
675
-
676
- // Notify recipients
677
- await this._notifyRecipients(replyMessage, context);
678
-
679
- // Broadcast to WebSocket for UI visibility
680
- await this._broadcastToUI(replyMessage, 'agent-message-reply', context);
681
-
682
- return {
683
- success: true,
684
- messageId: replyMessageId,
685
- conversationId: originalMessage.conversationId,
686
- recipients: replyRecipients,
687
- depth: currentDepth + 1,
688
- timestamp,
689
- conversationStatus: conversation.status
690
- };
691
-
692
- } catch (error) {
693
- return {
694
- success: false,
695
- error: error.message
696
- };
697
- }
698
- }
699
-
700
- /**
701
- * Get unreplied messages for an agent
702
- */
703
- async getUnrepliedMessages(agentId, parameters, context) {
704
- try {
705
- const {
706
- 'include-low-priority': includeLowPriority = false,
707
- 'max-age-hours': maxAgeHours = 24
708
- } = parameters;
709
-
710
- const inbox = this.agentInboxes.get(agentId) || new Set();
711
- const unrepliedMessages = [];
712
- const maxAge = Date.now() - (maxAgeHours * 3600000);
713
-
714
- for (const messageId of inbox) {
715
- const message = this.messages.get(messageId);
716
- if (!message) continue;
717
-
718
- // Skip old messages
719
- if (new Date(message.timestamp).getTime() < maxAge) continue;
720
-
721
- // Skip low priority if not requested
722
- if (message.priority === 'low' && !includeLowPriority) continue;
723
-
724
- // Check if message requires reply and hasn't been replied to by this agent
725
- if (message.requiresReply) {
726
- const hasReplied = this._hasAgentReplied(agentId, message);
727
- if (!hasReplied) {
728
- const conversation = this.conversations.get(message.conversationId);
729
- unrepliedMessages.push({
730
- messageId: message.id,
731
- conversationId: message.conversationId,
732
- sender: message.sender,
733
- subject: message.subject,
734
- preview: message.content.substring(0, 100) + '...',
735
- priority: message.priority,
736
- timestamp: message.timestamp,
737
- hasAttachments: message.attachments.length > 0,
738
- conversationStatus: conversation?.status || 'unknown',
739
- depth: message.metadata.depth
740
- });
741
- }
742
- }
743
- }
744
-
745
- // Sort by priority and timestamp
746
- unrepliedMessages.sort((a, b) => {
747
- const priorityOrder = { high: 0, normal: 1, low: 2 };
748
- const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
749
- if (priorityDiff !== 0) return priorityDiff;
750
- return new Date(b.timestamp) - new Date(a.timestamp);
751
- });
752
-
753
- return {
754
- success: true,
755
- messages: unrepliedMessages,
756
- total: unrepliedMessages.length,
757
- inbox: {
758
- total: inbox.size,
759
- activeConversations: (this.agentConversations.get(agentId) || new Set()).size
760
- }
761
- };
762
-
763
- } catch (error) {
764
- return {
765
- success: false,
766
- error: error.message
767
- };
768
- }
769
- }
770
-
771
- /**
772
- * Mark a conversation as ended
773
- */
774
- async markConversationEnded(agentId, parameters, context) {
775
- try {
776
- const {
777
- 'conversation-id': conversationId,
778
- reason = 'Conversation ended by agent'
779
- } = parameters;
780
-
781
- if (!conversationId) {
782
- throw new Error('Conversation ID is required');
783
- }
784
-
785
- const conversation = this.conversations.get(conversationId);
786
- if (!conversation) {
787
- throw new Error(`Conversation not found: ${conversationId}`);
788
- }
789
-
790
- // Verify agent is a participant
791
- if (!conversation.participants.includes(agentId)) {
792
- throw new Error('You are not a participant in this conversation');
793
- }
794
-
795
- // Update conversation status
796
- conversation.status = 'ended';
797
- conversation.endTime = new Date().toISOString();
798
- conversation.endReason = reason;
799
- conversation.endedBy = agentId;
800
-
801
- // Remove from active conversations for all participants
802
- for (const participantId of conversation.participants) {
803
- const agentConvs = this.agentConversations.get(participantId);
804
- if (agentConvs) {
805
- agentConvs.delete(conversationId);
806
- }
807
- }
808
-
809
- return {
810
- success: true,
811
- conversationId,
812
- status: 'ended',
813
- reason,
814
- timestamp: conversation.endTime
815
- };
816
-
817
- } catch (error) {
818
- return {
819
- success: false,
820
- error: error.message
821
- };
822
- }
823
- }
824
-
825
- /**
826
- * Initialize storage directory
827
- * @private
828
- */
829
- async _initializeStorage() {
830
- try {
831
- await fs.mkdir(this.config.storageDir, { recursive: true });
832
- const attachmentsDir = path.join(this.config.storageDir, 'attachments');
833
- await fs.mkdir(attachmentsDir, { recursive: true });
834
- } catch (error) {
835
- console.error('Failed to initialize message storage:', error);
836
- }
837
- }
838
-
839
- /**
840
- * Parse recipients from parameters
841
- * @private
842
- */
843
- _parseRecipients(recipient, recipients) {
844
- let recipientList = [];
845
-
846
- if (recipient) {
847
- recipientList.push(recipient);
848
- }
849
-
850
- if (recipients) {
851
- if (typeof recipients === 'string') {
852
- try {
853
- const parsed = JSON.parse(recipients);
854
- recipientList = [...recipientList, ...(Array.isArray(parsed) ? parsed : [parsed])];
855
- } catch {
856
- recipientList.push(recipients);
857
- }
858
- } else if (Array.isArray(recipients)) {
859
- recipientList = [...recipientList, ...recipients];
860
- }
861
- }
862
-
863
- // Remove duplicates
864
- return [...new Set(recipientList)];
865
- }
866
-
867
- /**
868
- * Process and validate attachments
869
- * @private
870
- */
871
- async _processAttachments(attachments, agentId, context = {}) {
872
- if (!attachments) return [];
873
-
874
- let attachmentList = [];
875
- if (typeof attachments === 'string') {
876
- try {
877
- attachmentList = JSON.parse(attachments);
878
- } catch {
879
- return [];
880
- }
881
- } else {
882
- attachmentList = attachments;
883
- }
884
-
885
- if (!Array.isArray(attachmentList)) {
886
- attachmentList = [attachmentList];
887
- }
888
-
889
- // Effective limits merge global defaults with per-agent overrides.
890
- // When context is missing (legacy callers), falls back to this.config.
891
- const _attLimits = this._limits(context);
892
-
893
- // Validate attachment count
894
- if (attachmentList.length > _attLimits.maxAttachmentsPerMessage) {
895
- throw new Error(`Maximum ${_attLimits.maxAttachmentsPerMessage} attachments allowed`);
896
- }
897
-
898
- const processedAttachments = [];
899
-
900
- for (const attachment of attachmentList) {
901
- if (!attachment.path) continue;
902
-
903
- try {
904
- // Check file exists and size
905
- const stats = await fs.stat(attachment.path);
906
- if (stats.size > _attLimits.maxAttachmentSize) {
907
- throw new Error(`Attachment exceeds size limit: ${attachment.path}`);
908
- }
909
-
910
- // Copy to storage
911
- const attachmentId = this._generateAttachmentId();
912
- const ext = path.extname(attachment.path);
913
- const storagePath = path.join(this.config.storageDir, 'attachments', `${attachmentId}${ext}`);
914
-
915
- await fs.copyFile(attachment.path, storagePath);
916
-
917
- processedAttachments.push({
918
- id: attachmentId,
919
- originalPath: attachment.path,
920
- storagePath,
921
- type: attachment.type || 'file',
922
- size: stats.size,
923
- name: path.basename(attachment.path)
924
- });
925
-
926
- } catch (error) {
927
- console.error(`Failed to process attachment: ${attachment.path}`, error);
928
- }
929
- }
930
-
931
- return processedAttachments;
932
- }
933
-
934
- /**
935
- * Notify recipients of new message
936
- * @private
937
- */
938
- async _notifyRecipients(message, context) {
939
- const agentPool = context.agentPool;
940
- if (!agentPool) return;
941
-
942
- for (const recipientId of message.recipients) {
943
- try {
944
- // Send both a notification AND inject message into conversation
945
-
946
- // 1. Standard notification (for system awareness)
947
- await agentPool.notifyAgent(recipientId, {
948
- type: 'agent-communication',
949
- from: message.sender,
950
- conversationId: message.conversationId,
951
- content: `📨 New message from ${message.senderName}: "${message.subject}"`,
952
- messageId: message.id,
953
- priority: message.priority,
954
- requiresResponse: message.requiresReply
955
- });
956
-
957
- // 2. Inject the actual message content directly into recipient's conversation
958
- const recipient = await agentPool.getAgent(recipientId);
959
- if (recipient) {
960
- const messageContent = `📨 **Inter-Agent Message**
961
- **From:** ${message.senderName} (${message.sender})
962
- **Subject:** ${message.subject}
963
- **Priority:** ${message.priority}
964
- **Requires Reply:** ${message.requiresReply ? 'Yes' : 'No'}
965
-
966
- **Message:**
967
- ${message.content}
968
-
969
- ${message.attachments.length > 0 ? `**Attachments:** ${message.attachments.length} file(s)` : ''}
970
-
971
- *You can reply using the agentcommunication tool with action="reply-to-message" and message-id="${message.id}"*`;
972
-
973
- const directMessage = {
974
- id: `agent-comm-${message.id}`,
975
- conversationId: message.conversationId,
976
- agentId: message.sender,
977
- content: messageContent,
978
- role: 'system', // System message so it's clearly visible
979
- timestamp: message.timestamp,
980
- type: 'agent-communication',
981
- metadata: {
982
- originalMessageId: message.id,
983
- fromAgent: message.sender,
984
- requiresResponse: message.requiresReply,
985
- priority: message.priority
986
- }
987
- };
988
-
989
- // Add to full conversation
990
- recipient.conversations.full.messages.push(directMessage);
991
- recipient.conversations.full.lastUpdated = new Date().toISOString();
992
-
993
- // Add to current model conversation if active
994
- if (recipient.currentModel && recipient.conversations[recipient.currentModel]) {
995
- recipient.conversations[recipient.currentModel].messages.push(directMessage);
996
- recipient.conversations[recipient.currentModel].lastUpdated = new Date().toISOString();
997
- }
998
-
999
- // Persist the updated state
1000
- await agentPool.persistAgentState(recipientId);
1001
-
1002
- // Queue message using new architecture
1003
- console.log(`📬 Queueing inter-agent message for scheduler processing`, {
1004
- recipientId,
1005
- sender: message.sender,
1006
- subject: message.subject,
1007
- hasSessionId: !!context.sessionId
1008
- });
1009
-
1010
- await agentPool.addInterAgentMessage(recipientId, {
1011
- id: message.id,
1012
- messageId: message.id,
1013
- sender: message.sender,
1014
- senderName: message.senderName,
1015
- subject: message.subject,
1016
- content: message.content,
1017
- attachments: message.attachments,
1018
- priority: message.priority,
1019
- requiresReply: message.requiresReply,
1020
- conversationId: message.conversationId,
1021
- sessionId: context.sessionId,
1022
- timestamp: new Date().toISOString()
1023
- });
1024
-
1025
- console.log(`Direct message injected and queued for processing: ${recipientId}`, {
1026
- messageId: message.id,
1027
- fromAgent: message.sender,
1028
- subject: message.subject,
1029
- priority: message.priority
1030
- });
1031
- }
1032
-
1033
- } catch (error) {
1034
- console.error(`Failed to notify agent ${recipientId}:`, error);
1035
- }
1036
- }
1037
- }
1038
-
1039
- /**
1040
- * Check if agent has replied to a message
1041
- * @private
1042
- */
1043
- _hasAgentReplied(agentId, message) {
1044
- for (const replyId of message.replies) {
1045
- const reply = this.messages.get(replyId);
1046
- if (reply && reply.sender === agentId) {
1047
- return true;
1048
- }
1049
- // Recursively check nested replies
1050
- if (reply && this._hasAgentReplied(agentId, reply)) {
1051
- return true;
1052
- }
1053
- }
1054
- return false;
1055
- }
1056
-
1057
- /**
1058
- * Get conversation depth
1059
- * @private
1060
- */
1061
- _getConversationDepth(conversationId) {
1062
- const conversation = this.conversations.get(conversationId);
1063
- if (!conversation) return 0;
1064
-
1065
- let maxDepth = 0;
1066
- const rootMessage = this.messages.get(conversation.rootMessageId);
1067
- if (rootMessage) {
1068
- maxDepth = this._getMessageDepth(rootMessage);
1069
- }
1070
-
1071
- return maxDepth;
1072
- }
1073
-
1074
- /**
1075
- * Get message depth recursively
1076
- * @private
1077
- */
1078
- _getMessageDepth(message, currentDepth = 0) {
1079
- if (message.replies.length === 0) {
1080
- return currentDepth;
1081
- }
1082
-
1083
- let maxDepth = currentDepth;
1084
- for (const replyId of message.replies) {
1085
- const reply = this.messages.get(replyId);
1086
- if (reply) {
1087
- const depth = this._getMessageDepth(reply, currentDepth + 1);
1088
- maxDepth = Math.max(maxDepth, depth);
1089
- }
1090
- }
1091
-
1092
- return maxDepth;
1093
- }
1094
-
1095
- /**
1096
- * Update message counts for agent
1097
- * @private
1098
- */
1099
- _updateMessageCounts(agentId, type) {
1100
- if (!this.agentMessageCounts.has(agentId)) {
1101
- this.agentMessageCounts.set(agentId, { sent: 0, received: 0 });
1102
- }
1103
-
1104
- const counts = this.agentMessageCounts.get(agentId);
1105
- if (type === 'sent') {
1106
- counts.sent++;
1107
- } else {
1108
- counts.received++;
1109
- }
1110
- }
1111
-
1112
- /**
1113
- * Generate unique message ID
1114
- * @private
1115
- */
1116
- _generateMessageId() {
1117
- return `msg-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
1118
- }
1119
-
1120
- /**
1121
- * Generate unique conversation ID
1122
- * @private
1123
- */
1124
- _generateConversationId() {
1125
- return `conv-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
1126
- }
1127
-
1128
- /**
1129
- * Generate unique attachment ID
1130
- * @private
1131
- */
1132
- _generateAttachmentId() {
1133
- return `att-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
1134
- }
1135
-
1136
- /**
1137
- * Cleanup old messages and conversations
1138
- * Called periodically to prevent memory growth
1139
- */
1140
- async cleanup() {
1141
- const now = Date.now();
1142
- const retentionCutoff = now - this.config.messageRetentionPeriod;
1143
-
1144
- // Clean up old messages
1145
- for (const [messageId, message] of this.messages.entries()) {
1146
- const messageTime = new Date(message.timestamp).getTime();
1147
- if (messageTime < retentionCutoff) {
1148
- // Clean up attachments
1149
- for (const attachment of message.attachments) {
1150
- try {
1151
- await fs.unlink(attachment.storagePath);
1152
- } catch (error) {
1153
- // Ignore errors for missing files
1154
- }
1155
- }
1156
-
1157
- // Remove from inboxes
1158
- for (const [agentId, inbox] of this.agentInboxes.entries()) {
1159
- inbox.delete(messageId);
1160
- }
1161
-
1162
- this.messages.delete(messageId);
1163
- }
1164
- }
1165
-
1166
- // Clean up old conversations
1167
- for (const [conversationId, conversation] of this.conversations.entries()) {
1168
- const lastActivity = new Date(conversation.lastActivity).getTime();
1169
- if (lastActivity < retentionCutoff || conversation.status === 'ended') {
1170
- // Remove from agent conversations
1171
- for (const [agentId, convs] of this.agentConversations.entries()) {
1172
- convs.delete(conversationId);
1173
- }
1174
-
1175
- this.conversations.delete(conversationId);
1176
- }
1177
- }
1178
- }
1179
-
1180
- /**
1181
- * Broadcast message to WebSocket for UI visibility
1182
- * @private
1183
- */
1184
- async _broadcastToUI(message, eventType, context) {
1185
- try {
1186
- // Build a formatted message for UI display
1187
- const uiMessage = {
1188
- type: 'agent-communication',
1189
- eventType,
1190
- timestamp: message.timestamp,
1191
- messageId: message.id,
1192
- conversationId: message.conversationId,
1193
- sender: {
1194
- id: message.sender,
1195
- name: message.senderName
1196
- },
1197
- recipients: Object.entries(message.recipientNames || {}).map(([id, name]) => ({
1198
- id,
1199
- name
1200
- })),
1201
- subject: message.subject,
1202
- content: message.content,
1203
- priority: message.priority,
1204
- requiresReply: message.requiresReply,
1205
- hasAttachments: message.attachments && message.attachments.length > 0,
1206
- attachmentCount: message.attachments ? message.attachments.length : 0,
1207
- metadata: message.metadata
1208
- };
1209
-
1210
- // Try multiple broadcast methods
1211
- // Method 1: Through agentPool if available
1212
- if (context.agentPool && context.agentPool.messageProcessor) {
1213
- const messageProcessor = context.agentPool.messageProcessor;
1214
- if (messageProcessor && messageProcessor.orchestrator && messageProcessor.orchestrator.webServer) {
1215
- // Use broadcastToSession - it will fallback to all connections if session not found
1216
- messageProcessor.orchestrator.webServer.broadcastToSession(context.sessionId || 'web-session', {
1217
- type: 'agent-communication',
1218
- action: 'agent-communication',
1219
- data: uiMessage
1220
- });
1221
- return;
1222
- }
1223
- }
1224
-
1225
- // Method 2: Direct orchestrator access
1226
- if (context.orchestrator && context.orchestrator.webServer) {
1227
- context.orchestrator.webServer.broadcastToSession(context.sessionId || 'web-session', {
1228
- type: 'agent-communication',
1229
- action: 'agent-communication',
1230
- data: uiMessage
1231
- });
1232
- return;
1233
- }
1234
-
1235
- // Method 3: Through global reference (if set during initialization)
1236
- if (global.loxiaWebServer) {
1237
- global.loxiaWebServer.broadcastToSession(context.sessionId || 'web-session', {
1238
- type: 'agent-communication',
1239
- action: 'agent-communication',
1240
- data: uiMessage
1241
- });
1242
- }
1243
- } catch (error) {
1244
- // Don't fail the operation if broadcast fails
1245
- console.error('Failed to broadcast agent message to UI:', error);
1246
- }
1247
- }
1248
-
1249
- /**
1250
- * Check if sender can send message to recipient
1251
- * @private
1252
- */
1253
- async _canSendMessage(senderAgent, recipientId, context) {
1254
- const agentPool = context.agentPool;
1255
-
1256
- // Ensure interAgentTracking is a Map (defensive - may be plain object from JSON)
1257
- if (!senderAgent.interAgentTracking || !(senderAgent.interAgentTracking instanceof Map)) {
1258
- if (senderAgent.interAgentTracking && typeof senderAgent.interAgentTracking === 'object') {
1259
- senderAgent.interAgentTracking = new Map(Object.entries(senderAgent.interAgentTracking));
1260
- } else {
1261
- senderAgent.interAgentTracking = new Map();
1262
- }
1263
- }
1264
-
1265
- const tracking = senderAgent.interAgentTracking;
1266
-
1267
- // Initialize tracking for this recipient if needed
1268
- if (!tracking.has(recipientId)) {
1269
- tracking.set(recipientId, {
1270
- lastSent: null,
1271
- lastReceived: null,
1272
- lastType: null
1273
- });
1274
- }
1275
-
1276
- const recipientTracking = tracking.get(recipientId);
1277
- const now = Date.now();
1278
- const MIN_INTERVAL = 60 * 1000; // 1 minute
1279
-
1280
- // Rule 1: Always allow if recipient has replied since our last message
1281
- if (recipientTracking.lastType === 'received' ||
1282
- (recipientTracking.lastReceived && recipientTracking.lastReceived > recipientTracking.lastSent)) {
1283
- return { allowed: true };
1284
- }
1285
-
1286
- // Rule 2: Allow if minimum time has passed since last send
1287
- if (recipientTracking.lastSent) {
1288
- const timeSinceLastSend = now - recipientTracking.lastSent;
1289
- if (timeSinceLastSend >= MIN_INTERVAL) {
1290
- return { allowed: true };
1291
- }
1292
-
1293
- // Calculate when next send is allowed
1294
- const nextAllowedTime = recipientTracking.lastSent + MIN_INTERVAL;
1295
- return {
1296
- allowed: false,
1297
- reason: `Must wait ${Math.ceil((nextAllowedTime - now) / 1000)}s since last message`,
1298
- waitUntil: nextAllowedTime
1299
- };
1300
- }
1301
-
1302
- // Rule 3: First message to this recipient is always allowed
1303
- return { allowed: true };
1304
- }
1305
-
1306
- /**
1307
- * Update conversation tracking after message sent/received
1308
- * @private
1309
- */
1310
- async _updateConversationTracking(senderAgentId, recipientId, action, context) {
1311
- const agentPool = context.agentPool;
1312
- const senderAgent = await agentPool.getAgent(senderAgentId);
1313
- if (!senderAgent) return;
1314
-
1315
- const now = Date.now();
1316
-
1317
- // Ensure sender's interAgentTracking is a Map
1318
- if (!senderAgent.interAgentTracking || !(senderAgent.interAgentTracking instanceof Map)) {
1319
- if (senderAgent.interAgentTracking && typeof senderAgent.interAgentTracking === 'object') {
1320
- senderAgent.interAgentTracking = new Map(Object.entries(senderAgent.interAgentTracking));
1321
- } else {
1322
- senderAgent.interAgentTracking = new Map();
1323
- }
1324
- }
1325
-
1326
- // Update sender's tracking
1327
- if (!senderAgent.interAgentTracking.has(recipientId)) {
1328
- senderAgent.interAgentTracking.set(recipientId, {
1329
- lastSent: null,
1330
- lastReceived: null,
1331
- lastType: null
1332
- });
1333
- }
1334
-
1335
- const tracking = senderAgent.interAgentTracking.get(recipientId);
1336
-
1337
- if (action === 'sent') {
1338
- tracking.lastSent = now;
1339
- tracking.lastType = 'sent';
1340
- } else if (action === 'replied') {
1341
- tracking.lastSent = now;
1342
- tracking.lastType = 'sent'; // Reply is still a send action
1343
- }
1344
-
1345
- // Update recipient's tracking (they received a message)
1346
- const recipientAgent = await agentPool.getAgent(recipientId);
1347
- if (recipientAgent) {
1348
- // Ensure recipient's interAgentTracking is a Map
1349
- if (!recipientAgent.interAgentTracking || !(recipientAgent.interAgentTracking instanceof Map)) {
1350
- if (recipientAgent.interAgentTracking && typeof recipientAgent.interAgentTracking === 'object') {
1351
- recipientAgent.interAgentTracking = new Map(Object.entries(recipientAgent.interAgentTracking));
1352
- } else {
1353
- recipientAgent.interAgentTracking = new Map();
1354
- }
1355
- }
1356
-
1357
- if (!recipientAgent.interAgentTracking.has(senderAgentId)) {
1358
- recipientAgent.interAgentTracking.set(senderAgentId, {
1359
- lastSent: null,
1360
- lastReceived: null,
1361
- lastType: null
1362
- });
1363
- }
1364
-
1365
- const recipientTracking = recipientAgent.interAgentTracking.get(senderAgentId);
1366
- recipientTracking.lastReceived = now;
1367
- recipientTracking.lastType = 'received';
1368
-
1369
- // Persist recipient agent state
1370
- await agentPool.persistAgentState(recipientId);
1371
- }
1372
-
1373
- // Persist sender agent state
1374
- await agentPool.persistAgentState(senderAgentId);
1375
- }
1376
-
1377
- /**
1378
- * Set message processor for broadcasting
1379
- * Called during tool initialization
1380
- */
1381
- setMessageProcessor(messageProcessor) {
1382
- this.messageProcessor = messageProcessor;
1383
- }
1384
- }
1385
-
1
+ /**
2
+ * AgentCommunicationTool - Enables inter-agent communication with safety mechanisms
3
+ *
4
+ * Purpose:
5
+ * - Allow agents to discover and communicate with other active agents
6
+ * - Manage message threads with reply tracking
7
+ * - Prevent conversation loops and exponential message growth
8
+ * - Support file attachments for rich communication
9
+ *
10
+ * Design Principles:
11
+ * - Loosely coupled with system components
12
+ * - Message persistence for async communication
13
+ * - Conversation limits to prevent runaway threads
14
+ * - Agent lifecycle awareness
15
+ */
16
+
17
+ import { BaseTool } from './baseTool.js';
18
+ import { promises as fs } from 'fs';
19
+ import path from 'path';
20
+ import crypto from 'crypto';
21
+
22
+ class AgentCommunicationTool extends BaseTool {
23
+ constructor(config = {}) {
24
+ super('agentcommunication', 'Agent Communication', 'communication');
25
+
26
+ // Configuration with safety defaults
27
+ this.config = {
28
+ maxConversationDepth: config.maxConversationDepth || 10,
29
+ maxRecipientsPerMessage: config.maxRecipientsPerMessage || 3,
30
+ maxAttachmentSize: config.maxAttachmentSize || 10 * 1024 * 1024, // 10MB
31
+ maxAttachmentsPerMessage: config.maxAttachmentsPerMessage || 5,
32
+ conversationTimeout: config.conversationTimeout || 3600000, // 1 hour
33
+ messageRetentionPeriod: config.messageRetentionPeriod || 86400000, // 24 hours
34
+ enableBroadcast: config.enableBroadcast || false,
35
+ storageDir: config.storageDir || '.loxia-messages'
36
+ };
37
+
38
+ // Message storage - in production, this would be a database
39
+ this.messages = new Map(); // messageId -> message object
40
+
41
+ // Shared helper for every check-point to resolve effective limits
42
+ // from global defaults + per-agent overrides (agent.toolConfig.agentcommunication).
43
+ // See BaseTool#getEffectiveConfig — per-agent fields win.
44
+ this._limits = (context) => this.getEffectiveConfig(context || {}, this.config);
45
+ this.conversations = new Map(); // conversationId -> conversation metadata
46
+ this.agentInboxes = new Map(); // agentId -> Set of messageIds
47
+ this.agentConversations = new Map(); // agentId -> Set of conversationIds
48
+
49
+ // Safety tracking
50
+ this.conversationDepths = new Map(); // conversationId -> current depth
51
+ this.lastActivityTimes = new Map(); // conversationId -> timestamp
52
+ this.agentMessageCounts = new Map(); // agentId -> { sent: number, received: number }
53
+
54
+ // Initialize storage directory
55
+ this._initializeStorage();
56
+ }
57
+
58
+ /**
59
+ * Get tool description for agent system prompts
60
+ */
61
+ getDescription() {
62
+ return `
63
+ Agent Communication Tool: Enables communication between active agents.
64
+
65
+ USAGE:
66
+ \`\`\`json
67
+ {
68
+ "toolId": "agentcommunication",
69
+ "actions": [{"type": "action-name", ...params}]
70
+ }
71
+ \`\`\`
72
+
73
+ ACTIONS:
74
+ - get-available-agents: List active agents
75
+ - send-message: Send message (recipient, subject, message, priority, requiresReply)
76
+ - reply-to-message: Reply to message (messageId, message)
77
+ - get-unreplied-messages: Get pending messages
78
+ - mark-conversation-ended: End conversation (conversationId, reason)
79
+
80
+ requiresReply PARAMETER (send-message):
81
+ - Defaults to true. Set to false ONLY for fire-and-forget notifications where you do NOT need a response.
82
+ - When true, the recipient agent will be explicitly instructed to reply back to you.
83
+ - When false, the recipient will handle the message but is not expected to reply.
84
+
85
+ EXAMPLES:
86
+
87
+ Get available agents:
88
+ \`\`\`json
89
+ {"toolId": "agentcommunication", "actions": [{"type": "get-available-agents"}]}
90
+ \`\`\`
91
+
92
+ Send a message:
93
+ \`\`\`json
94
+ {
95
+ "toolId": "agentcommunication",
96
+ "actions": [{
97
+ "type": "send-message",
98
+ "recipient": "agent-fullstack-developer-1234567890123",
99
+ "subject": "Code review needed",
100
+ "message": "Please review the authentication module",
101
+ "priority": "normal",
102
+ "requiresReply": true
103
+ }]
104
+ }
105
+ \`\`\`
106
+
107
+ Reply to a message:
108
+ \`\`\`json
109
+ {
110
+ "toolId": "agentcommunication",
111
+ "actions": [{
112
+ "type": "reply-to-message",
113
+ "messageId": "msg-789",
114
+ "message": "Review complete. All issues addressed."
115
+ }]
116
+ }
117
+ \`\`\`
118
+
119
+ IMPORTANT: Use full agent ID from get-available-agents as recipient.
120
+
121
+ LIMITS:
122
+ - Max ${this.config.maxConversationDepth} replies per thread
123
+ - Max ${this.config.maxRecipientsPerMessage} recipients per message
124
+ - Conversations expire after ${this.config.conversationTimeout / 60000} minutes
125
+ `.trim();
126
+ }
127
+
128
+ /**
129
+ * Parse tool parameters from command content
130
+ */
131
+ parseParameters(content) {
132
+ // Handle JSON format (for direct tool calls)
133
+ if (typeof content === 'object' && content !== null) {
134
+ // If it's already an object, extract the parameters
135
+ if (content.actions && Array.isArray(content.actions) && content.actions.length > 0) {
136
+ // Handle format: {"actions": [{"type": "get-available-agents", ...}]}
137
+ const action = content.actions[0];
138
+ return {
139
+ action: action.type,
140
+ ...action
141
+ };
142
+ } else if (content.action || content.type) {
143
+ // Handle format: {"action": "get-available-agents", ...}
144
+ return {
145
+ action: content.action || content.type,
146
+ ...content
147
+ };
148
+ }
149
+ return content;
150
+ }
151
+
152
+ // Handle string content (could be JSON string or XML)
153
+ if (typeof content === 'string') {
154
+ // Try parsing as JSON first
155
+ if (content.trim().startsWith('{')) {
156
+ try {
157
+ const parsed = JSON.parse(content);
158
+ return this.parseParameters(parsed); // Recursive call to handle the parsed object
159
+ } catch (error) {
160
+ // Not valid JSON, continue to XML parsing
161
+ }
162
+ }
163
+
164
+ // Parse XML-style commands for agent communication
165
+ const parameters = {};
166
+
167
+ // Extract action parameter
168
+ const actionMatch = content.match(/<action[^>]*>([^<]+)<\/action>/);
169
+ if (actionMatch) {
170
+ parameters.action = actionMatch[1].trim();
171
+ }
172
+
173
+ // Extract other parameters like recipient, subject, message, etc.
174
+ const tags = [
175
+ 'recipient', 'recipients', 'subject', 'message', 'attachments',
176
+ 'priority', 'requires-reply', 'message-id', 'cc-recipients',
177
+ 'include-low-priority', 'conversation-id', 'reason', 'mark-resolved'
178
+ ];
179
+
180
+ for (const tag of tags) {
181
+ const regex = new RegExp(`<${tag.replace('-', '\\-')}[^>]*>(.*?)<\\/${tag.replace('-', '\\-')}>`, 's');
182
+ const match = content.match(regex);
183
+ if (match) {
184
+ let value = match[1].trim();
185
+ // Try to parse JSON values
186
+ if (value.startsWith('[') || value.startsWith('{') || value === 'true' || value === 'false') {
187
+ try {
188
+ value = JSON.parse(value);
189
+ } catch {
190
+ // Keep as string if JSON parsing fails
191
+ }
192
+ }
193
+ // Convert kebab-case to camelCase for parameter names
194
+ const paramName = tag.replace(/-(.)/g, (_, char) => char.toUpperCase());
195
+ parameters[paramName] = value;
196
+ }
197
+ }
198
+
199
+ // Extract attributes from action tag if present
200
+ const actionWithAttribs = content.match(/<action([^>]*)>([^<]+)<\/action>/);
201
+ if (actionWithAttribs && actionWithAttribs[1]) {
202
+ // Parse attributes like priority="high", requires-reply="true"
203
+ const attribMatches = actionWithAttribs[1].matchAll(/([\w-]+)=["']([^"']+)["']/g);
204
+ for (const match of attribMatches) {
205
+ const key = match[1].replace(/-(.)/g, (_, char) => char.toUpperCase()); // Convert kebab-case to camelCase
206
+ parameters[key] = match[2];
207
+ }
208
+ }
209
+
210
+ return parameters;
211
+ }
212
+
213
+ // Fallback
214
+ return content || {};
215
+ }
216
+
217
+ /**
218
+ * Execute the tool action
219
+ */
220
+ async execute(parameters = {}, context = {}) {
221
+ // Be tolerant of the `{actions: [{type: "..."}]}` shape arriving
222
+ // without a flattened top-level `action`. Most upstream paths
223
+ // (tagParser, parseParameters) flatten this for us, but a path
224
+ // where the LLM emits `parameters: { actions: [...] }` AND the
225
+ // tagParser version is older than the hoist fix can still let
226
+ // this slip through. Recovering here keeps the tool robust to
227
+ // either shape regardless of which parser ran upstream.
228
+ let { action } = parameters;
229
+ if (!action && Array.isArray(parameters.actions) && parameters.actions.length > 0) {
230
+ const first = parameters.actions[0] || {};
231
+ action = first.type || first.action;
232
+ // Merge the first action's fields up so downstream handlers see
233
+ // recipient/subject/message/etc. at the top level as they expect.
234
+ parameters = { ...parameters, ...first, action };
235
+ }
236
+
237
+ if (!action) {
238
+ throw new Error('Action parameter is required');
239
+ }
240
+
241
+ // Validate requesting agent exists
242
+ const requestingAgentId = context.agentId;
243
+ if (!requestingAgentId) {
244
+ throw new Error('Agent ID is required in context');
245
+ }
246
+
247
+ // Route to appropriate action handler
248
+ switch (action.toLowerCase()) {
249
+ case 'get-available-agents':
250
+ return await this.getAvailableAgents(requestingAgentId, parameters, context);
251
+
252
+ case 'send-message':
253
+ return await this.sendMessage(requestingAgentId, parameters, context);
254
+
255
+ case 'reply-to-message':
256
+ return await this.replyToMessage(requestingAgentId, parameters, context);
257
+
258
+ case 'get-unreplied-messages':
259
+ return await this.getUnrepliedMessages(requestingAgentId, parameters, context);
260
+
261
+ case 'mark-conversation-ended':
262
+ return await this.markConversationEnded(requestingAgentId, parameters, context);
263
+
264
+ default:
265
+ throw new Error(`Unknown action: ${action}`);
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Get list of available agents
271
+ */
272
+ async getAvailableAgents(requestingAgentId, parameters, context) {
273
+ try {
274
+ // Get agent pool from context
275
+ const agentPool = context.agentPool;
276
+ if (!agentPool) {
277
+ throw new Error('Agent pool not available in context');
278
+ }
279
+
280
+ // Get all active agents
281
+ const agents = await agentPool.listActiveAgents();
282
+
283
+ // Normalize teams metadata to array format
284
+ // Supports: metadata.teams = [{id, name, role}], or legacy metadata.teamId (string)
285
+ const normalizeTeams = (metadata) => {
286
+ if (!metadata) return [];
287
+ // New format: teams array
288
+ if (Array.isArray(metadata.teams)) return metadata.teams;
289
+ // Legacy format: single teamId string
290
+ if (metadata.teamId) {
291
+ return [{ id: metadata.teamId, name: metadata.teamName || null, role: metadata.teamRole || null }];
292
+ }
293
+ return [];
294
+ };
295
+
296
+ // Resolve the requesting agent's teams
297
+ const requestingAgent = agents.find(a => a.id === requestingAgentId);
298
+ const myTeams = normalizeTeams(requestingAgent?.metadata);
299
+ const myTeamIds = new Set(myTeams.map(t => t.id));
300
+
301
+ // Filter out requesting agent and format response
302
+ const availableAgents = agents
303
+ .filter(agent => agent.id !== requestingAgentId && !agent.isPaused)
304
+ .map(agent => {
305
+ const agentTeams = normalizeTeams(agent.metadata);
306
+ const agentTeamIds = agentTeams.map(t => t.id);
307
+ // Teams shared with the requesting agent
308
+ const sharedTeamIds = agentTeamIds.filter(id => myTeamIds.has(id));
309
+
310
+ return {
311
+ id: agent.id,
312
+ name: agent.name,
313
+ type: agent.type,
314
+ capabilities: agent.capabilities,
315
+ status: agent.status,
316
+ // Multi-team affiliation
317
+ teams: agentTeams, // [{id, name, role}, ...]
318
+ // Relationship to the requesting agent
319
+ sameTeam: sharedTeamIds.length > 0,
320
+ sharedTeams: sharedTeamIds, // which team IDs are shared
321
+ messageStats: this.agentMessageCounts.get(agent.id) || { sent: 0, received: 0 },
322
+ activeConversations: (this.agentConversations.get(agent.id) || new Set()).size
323
+ };
324
+ });
325
+
326
+ return {
327
+ success: true,
328
+ agents: availableAgents,
329
+ totalActive: availableAgents.length,
330
+ // The requesting agent's own team context
331
+ yourTeams: myTeams.length > 0 ? myTeams : null,
332
+ timestamp: new Date().toISOString()
333
+ };
334
+
335
+ } catch (error) {
336
+ return {
337
+ success: false,
338
+ error: error.message
339
+ };
340
+ }
341
+ }
342
+
343
+ /**
344
+ * Send a message to another agent
345
+ */
346
+ async sendMessage(senderAgentId, parameters, context) {
347
+ try {
348
+ const {
349
+ recipient,
350
+ recipients, // Support both single and multiple
351
+ subject,
352
+ message,
353
+ attachments,
354
+ priority = 'normal',
355
+ 'requires-reply': requiresReply = true
356
+ } = parameters;
357
+
358
+ // Validate required fields
359
+ if (!subject || !message) {
360
+ throw new Error('Subject and message are required');
361
+ }
362
+
363
+ // Determine recipients
364
+ const recipientList = this._parseRecipients(recipient, recipients);
365
+ if (recipientList.length === 0) {
366
+ throw new Error('At least one recipient is required');
367
+ }
368
+
369
+ // Validate recipient count — effective limit from per-agent config.
370
+ const _sendLimits = this._limits(context);
371
+ if (recipientList.length > _sendLimits.maxRecipientsPerMessage) {
372
+ throw new Error(`Maximum ${_sendLimits.maxRecipientsPerMessage} recipients allowed`);
373
+ }
374
+
375
+ // Get agent pool and sender agent
376
+ const agentPool = context.agentPool;
377
+ const senderAgent = await agentPool.getAgent(senderAgentId);
378
+ if (!senderAgent) {
379
+ throw new Error('Sender agent not found');
380
+ }
381
+
382
+ // CRITICAL: Check if sender can send messages to recipients
383
+ const blockedRecipients = [];
384
+ for (const recipientId of recipientList) {
385
+ const canSend = await this._canSendMessage(senderAgent, recipientId, context);
386
+ if (!canSend.allowed) {
387
+ blockedRecipients.push({
388
+ recipientId,
389
+ reason: canSend.reason,
390
+ waitUntil: canSend.waitUntil
391
+ });
392
+ }
393
+ }
394
+
395
+ // If any recipients are blocked, apply delay and return
396
+ if (blockedRecipients.length > 0) {
397
+ const earliestAllowedTime = Math.max(...blockedRecipients.map(b => b.waitUntil || 0));
398
+ const delayUntil = new Date(earliestAllowedTime);
399
+
400
+ // Apply delay using existing infrastructure
401
+ await agentPool.updateAgent(senderAgentId, {
402
+ delayEndTime: delayUntil.toISOString()
403
+ });
404
+
405
+ const delaySeconds = Math.ceil((earliestAllowedTime - Date.now()) / 1000);
406
+
407
+ return {
408
+ success: true,
409
+ delayed: true,
410
+ delayUntil: delayUntil.toISOString(),
411
+ delaySeconds,
412
+ message: `Waiting ${delaySeconds}s before next message. Recipients need time to respond.`,
413
+ blockedRecipients: blockedRecipients.map(b => ({
414
+ recipientId: b.recipientId,
415
+ reason: b.reason
416
+ }))
417
+ };
418
+ }
419
+
420
+ // Validate recipients exist and get their names
421
+ const recipientAgents = {};
422
+ const invalidRecipients = [];
423
+
424
+ for (const recipientId of recipientList) {
425
+ const agent = await agentPool.getAgent(recipientId);
426
+ if (!agent) {
427
+ invalidRecipients.push(recipientId);
428
+ } else {
429
+ recipientAgents[recipientId] = agent.name;
430
+ }
431
+ }
432
+
433
+ // If any recipients are invalid, provide helpful error with suggestions
434
+ if (invalidRecipients.length > 0) {
435
+ const availableAgents = await agentPool.listActiveAgents();
436
+ const suggestions = availableAgents
437
+ .filter(agent => agent.id !== senderAgentId && !agent.isPaused)
438
+ .map(agent => `- ${agent.name} (ID: ${agent.id})`)
439
+ .join('\n');
440
+
441
+ return {
442
+ success: false,
443
+ error: `Recipient agent(s) not found: ${invalidRecipients.join(', ')}`,
444
+ suggestion: `Available agents you can message:\n${suggestions}`,
445
+ availableAgents: availableAgents.map(agent => ({
446
+ id: agent.id,
447
+ name: agent.name,
448
+ capabilities: agent.capabilities
449
+ }))
450
+ };
451
+ }
452
+
453
+ // Get sender agent name
454
+ const senderName = senderAgent ? senderAgent.name : senderAgentId;
455
+
456
+ // Process attachments if provided
457
+ const processedAttachments = await this._processAttachments(attachments, senderAgentId, context);
458
+
459
+ // Create message object
460
+ const messageId = this._generateMessageId();
461
+ const conversationId = this._generateConversationId();
462
+ const timestamp = new Date().toISOString();
463
+
464
+ const messageObj = {
465
+ id: messageId,
466
+ conversationId,
467
+ sender: senderAgentId,
468
+ senderName,
469
+ recipients: recipientList,
470
+ recipientNames: recipientAgents,
471
+ subject,
472
+ content: message,
473
+ attachments: processedAttachments,
474
+ priority,
475
+ requiresReply,
476
+ timestamp,
477
+ status: 'sent',
478
+ replies: [],
479
+ metadata: {
480
+ depth: 0,
481
+ isRoot: true
482
+ }
483
+ };
484
+
485
+ // Store message
486
+ this.messages.set(messageId, messageObj);
487
+
488
+ // Initialize conversation
489
+ this.conversations.set(conversationId, {
490
+ id: conversationId,
491
+ rootMessageId: messageId,
492
+ participants: [senderAgentId, ...recipientList],
493
+ startTime: timestamp,
494
+ lastActivity: timestamp,
495
+ status: 'active',
496
+ messageCount: 1
497
+ });
498
+
499
+ // Update inboxes
500
+ for (const recipientId of recipientList) {
501
+ if (!this.agentInboxes.has(recipientId)) {
502
+ this.agentInboxes.set(recipientId, new Set());
503
+ }
504
+ this.agentInboxes.get(recipientId).add(messageId);
505
+
506
+ // Track conversations
507
+ if (!this.agentConversations.has(recipientId)) {
508
+ this.agentConversations.set(recipientId, new Set());
509
+ }
510
+ this.agentConversations.get(recipientId).add(conversationId);
511
+ }
512
+
513
+ // Track sender's conversation
514
+ if (!this.agentConversations.has(senderAgentId)) {
515
+ this.agentConversations.set(senderAgentId, new Set());
516
+ }
517
+ this.agentConversations.get(senderAgentId).add(conversationId);
518
+
519
+ // Update message counts
520
+ this._updateMessageCounts(senderAgentId, 'sent');
521
+ for (const recipientId of recipientList) {
522
+ this._updateMessageCounts(recipientId, 'received');
523
+ }
524
+
525
+ // CRITICAL: Update inter-agent conversation tracking
526
+ for (const recipientId of recipientList) {
527
+ await this._updateConversationTracking(senderAgentId, recipientId, 'sent', context);
528
+ }
529
+
530
+ // Notify recipients through agent pool
531
+ await this._notifyRecipients(messageObj, context);
532
+
533
+ // Broadcast to WebSocket for UI visibility
534
+ await this._broadcastToUI(messageObj, 'agent-message-sent', context);
535
+
536
+ return {
537
+ success: true,
538
+ messageId,
539
+ conversationId,
540
+ recipients: recipientList,
541
+ timestamp,
542
+ message: 'Message sent successfully'
543
+ };
544
+
545
+ } catch (error) {
546
+ return {
547
+ success: false,
548
+ error: error.message
549
+ };
550
+ }
551
+ }
552
+
553
+ /**
554
+ * Reply to an existing message
555
+ */
556
+ async replyToMessage(senderAgentId, parameters, context) {
557
+ try {
558
+ const {
559
+ 'message-id': originalMessageId,
560
+ message,
561
+ 'cc-recipients': ccRecipients,
562
+ attachments,
563
+ 'mark-resolved': markResolved = false
564
+ } = parameters;
565
+
566
+ // Validate required fields
567
+ if (!originalMessageId || !message) {
568
+ throw new Error('Original message ID and reply content are required');
569
+ }
570
+
571
+ // Get original message
572
+ const originalMessage = this.messages.get(originalMessageId);
573
+ if (!originalMessage) {
574
+ throw new Error(`Original message not found: ${originalMessageId}`);
575
+ }
576
+
577
+ // Verify sender was a recipient or sender of original message
578
+ const isParticipant = originalMessage.sender === senderAgentId ||
579
+ originalMessage.recipients.includes(senderAgentId);
580
+ if (!isParticipant) {
581
+ throw new Error('You are not a participant in this conversation');
582
+ }
583
+
584
+ // Check conversation depth to prevent infinite loops
585
+ const conversation = this.conversations.get(originalMessage.conversationId);
586
+ const currentDepth = this._getConversationDepth(originalMessage.conversationId);
587
+ const _replyLimits = this._limits(context);
588
+
589
+ if (currentDepth >= _replyLimits.maxConversationDepth) {
590
+ return {
591
+ success: false,
592
+ error: `Conversation depth limit reached (${_replyLimits.maxConversationDepth}). Please start a new conversation.`,
593
+ suggestion: 'Consider marking this conversation as ended and starting fresh if needed.'
594
+ };
595
+ }
596
+
597
+ // Determine reply recipients
598
+ let replyRecipients = [originalMessage.sender];
599
+ if (originalMessage.sender === senderAgentId) {
600
+ // If replying to own message, reply to original recipients
601
+ replyRecipients = originalMessage.recipients;
602
+ }
603
+
604
+ // Add CC recipients if specified
605
+ if (ccRecipients) {
606
+ const ccList = this._parseRecipients(null, ccRecipients);
607
+ replyRecipients = [...new Set([...replyRecipients, ...ccList])];
608
+ }
609
+
610
+ // Remove sender from recipients
611
+ replyRecipients = replyRecipients.filter(id => id !== senderAgentId);
612
+
613
+ // Validate recipient count — effective limit from per-agent config.
614
+ if (replyRecipients.length > _replyLimits.maxRecipientsPerMessage) {
615
+ throw new Error(`Maximum ${_replyLimits.maxRecipientsPerMessage} recipients allowed`);
616
+ }
617
+
618
+ // Get agent names
619
+ const agentPool = context.agentPool;
620
+ const senderAgent = await agentPool.getAgent(senderAgentId);
621
+ const senderName = senderAgent ? senderAgent.name : senderAgentId;
622
+
623
+ const recipientAgents = {};
624
+ for (const recipientId of replyRecipients) {
625
+ const agent = await agentPool.getAgent(recipientId);
626
+ if (agent) {
627
+ recipientAgents[recipientId] = agent.name;
628
+ }
629
+ }
630
+
631
+ // Process attachments
632
+ const processedAttachments = await this._processAttachments(attachments, senderAgentId, context);
633
+
634
+ // Create reply message
635
+ const replyMessageId = this._generateMessageId();
636
+ const timestamp = new Date().toISOString();
637
+
638
+ const replyMessage = {
639
+ id: replyMessageId,
640
+ conversationId: originalMessage.conversationId,
641
+ sender: senderAgentId,
642
+ senderName,
643
+ recipients: replyRecipients,
644
+ recipientNames: recipientAgents,
645
+ subject: `Re: ${originalMessage.subject}`,
646
+ content: message,
647
+ attachments: processedAttachments,
648
+ priority: originalMessage.priority,
649
+ requiresReply: !markResolved,
650
+ timestamp,
651
+ status: 'sent',
652
+ replies: [],
653
+ metadata: {
654
+ depth: currentDepth + 1,
655
+ isRoot: false,
656
+ inReplyTo: originalMessageId
657
+ }
658
+ };
659
+
660
+ // Store reply
661
+ this.messages.set(replyMessageId, replyMessage);
662
+ originalMessage.replies.push(replyMessageId);
663
+
664
+ // Update conversation
665
+ conversation.lastActivity = timestamp;
666
+ conversation.messageCount++;
667
+ if (markResolved) {
668
+ conversation.status = 'resolved';
669
+ }
670
+
671
+ // Update inboxes
672
+ for (const recipientId of replyRecipients) {
673
+ if (!this.agentInboxes.has(recipientId)) {
674
+ this.agentInboxes.set(recipientId, new Set());
675
+ }
676
+ this.agentInboxes.get(recipientId).add(replyMessageId);
677
+ }
678
+
679
+ // Update message counts
680
+ this._updateMessageCounts(senderAgentId, 'sent');
681
+ for (const recipientId of replyRecipients) {
682
+ this._updateMessageCounts(recipientId, 'received');
683
+ }
684
+
685
+ // CRITICAL: Update inter-agent conversation tracking for replies
686
+ for (const recipientId of replyRecipients) {
687
+ await this._updateConversationTracking(senderAgentId, recipientId, 'replied', context);
688
+ }
689
+
690
+ // Notify recipients
691
+ await this._notifyRecipients(replyMessage, context);
692
+
693
+ // Broadcast to WebSocket for UI visibility
694
+ await this._broadcastToUI(replyMessage, 'agent-message-reply', context);
695
+
696
+ return {
697
+ success: true,
698
+ messageId: replyMessageId,
699
+ conversationId: originalMessage.conversationId,
700
+ recipients: replyRecipients,
701
+ depth: currentDepth + 1,
702
+ timestamp,
703
+ conversationStatus: conversation.status
704
+ };
705
+
706
+ } catch (error) {
707
+ return {
708
+ success: false,
709
+ error: error.message
710
+ };
711
+ }
712
+ }
713
+
714
+ /**
715
+ * Get unreplied messages for an agent
716
+ */
717
+ async getUnrepliedMessages(agentId, parameters, context) {
718
+ try {
719
+ const {
720
+ 'include-low-priority': includeLowPriority = false,
721
+ 'max-age-hours': maxAgeHours = 24
722
+ } = parameters;
723
+
724
+ const inbox = this.agentInboxes.get(agentId) || new Set();
725
+ const unrepliedMessages = [];
726
+ const maxAge = Date.now() - (maxAgeHours * 3600000);
727
+
728
+ for (const messageId of inbox) {
729
+ const message = this.messages.get(messageId);
730
+ if (!message) continue;
731
+
732
+ // Skip old messages
733
+ if (new Date(message.timestamp).getTime() < maxAge) continue;
734
+
735
+ // Skip low priority if not requested
736
+ if (message.priority === 'low' && !includeLowPriority) continue;
737
+
738
+ // Check if message requires reply and hasn't been replied to by this agent
739
+ if (message.requiresReply) {
740
+ const hasReplied = this._hasAgentReplied(agentId, message);
741
+ if (!hasReplied) {
742
+ const conversation = this.conversations.get(message.conversationId);
743
+ unrepliedMessages.push({
744
+ messageId: message.id,
745
+ conversationId: message.conversationId,
746
+ sender: message.sender,
747
+ subject: message.subject,
748
+ preview: message.content.substring(0, 100) + '...',
749
+ priority: message.priority,
750
+ timestamp: message.timestamp,
751
+ hasAttachments: message.attachments.length > 0,
752
+ conversationStatus: conversation?.status || 'unknown',
753
+ depth: message.metadata.depth
754
+ });
755
+ }
756
+ }
757
+ }
758
+
759
+ // Sort by priority and timestamp
760
+ unrepliedMessages.sort((a, b) => {
761
+ const priorityOrder = { high: 0, normal: 1, low: 2 };
762
+ const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
763
+ if (priorityDiff !== 0) return priorityDiff;
764
+ return new Date(b.timestamp) - new Date(a.timestamp);
765
+ });
766
+
767
+ return {
768
+ success: true,
769
+ messages: unrepliedMessages,
770
+ total: unrepliedMessages.length,
771
+ inbox: {
772
+ total: inbox.size,
773
+ activeConversations: (this.agentConversations.get(agentId) || new Set()).size
774
+ }
775
+ };
776
+
777
+ } catch (error) {
778
+ return {
779
+ success: false,
780
+ error: error.message
781
+ };
782
+ }
783
+ }
784
+
785
+ /**
786
+ * Mark a conversation as ended
787
+ */
788
+ async markConversationEnded(agentId, parameters, context) {
789
+ try {
790
+ const {
791
+ 'conversation-id': conversationId,
792
+ reason = 'Conversation ended by agent'
793
+ } = parameters;
794
+
795
+ if (!conversationId) {
796
+ throw new Error('Conversation ID is required');
797
+ }
798
+
799
+ const conversation = this.conversations.get(conversationId);
800
+ if (!conversation) {
801
+ throw new Error(`Conversation not found: ${conversationId}`);
802
+ }
803
+
804
+ // Verify agent is a participant
805
+ if (!conversation.participants.includes(agentId)) {
806
+ throw new Error('You are not a participant in this conversation');
807
+ }
808
+
809
+ // Update conversation status
810
+ conversation.status = 'ended';
811
+ conversation.endTime = new Date().toISOString();
812
+ conversation.endReason = reason;
813
+ conversation.endedBy = agentId;
814
+
815
+ // Remove from active conversations for all participants
816
+ for (const participantId of conversation.participants) {
817
+ const agentConvs = this.agentConversations.get(participantId);
818
+ if (agentConvs) {
819
+ agentConvs.delete(conversationId);
820
+ }
821
+ }
822
+
823
+ return {
824
+ success: true,
825
+ conversationId,
826
+ status: 'ended',
827
+ reason,
828
+ timestamp: conversation.endTime
829
+ };
830
+
831
+ } catch (error) {
832
+ return {
833
+ success: false,
834
+ error: error.message
835
+ };
836
+ }
837
+ }
838
+
839
+ /**
840
+ * Initialize storage directory
841
+ * @private
842
+ */
843
+ async _initializeStorage() {
844
+ try {
845
+ await fs.mkdir(this.config.storageDir, { recursive: true });
846
+ const attachmentsDir = path.join(this.config.storageDir, 'attachments');
847
+ await fs.mkdir(attachmentsDir, { recursive: true });
848
+ } catch (error) {
849
+ console.error('Failed to initialize message storage:', error);
850
+ }
851
+ }
852
+
853
+ /**
854
+ * Parse recipients from parameters
855
+ * @private
856
+ */
857
+ _parseRecipients(recipient, recipients) {
858
+ let recipientList = [];
859
+
860
+ if (recipient) {
861
+ recipientList.push(recipient);
862
+ }
863
+
864
+ if (recipients) {
865
+ if (typeof recipients === 'string') {
866
+ try {
867
+ const parsed = JSON.parse(recipients);
868
+ recipientList = [...recipientList, ...(Array.isArray(parsed) ? parsed : [parsed])];
869
+ } catch {
870
+ recipientList.push(recipients);
871
+ }
872
+ } else if (Array.isArray(recipients)) {
873
+ recipientList = [...recipientList, ...recipients];
874
+ }
875
+ }
876
+
877
+ // Remove duplicates
878
+ return [...new Set(recipientList)];
879
+ }
880
+
881
+ /**
882
+ * Process and validate attachments
883
+ * @private
884
+ */
885
+ async _processAttachments(attachments, agentId, context = {}) {
886
+ if (!attachments) return [];
887
+
888
+ let attachmentList = [];
889
+ if (typeof attachments === 'string') {
890
+ try {
891
+ attachmentList = JSON.parse(attachments);
892
+ } catch {
893
+ return [];
894
+ }
895
+ } else {
896
+ attachmentList = attachments;
897
+ }
898
+
899
+ if (!Array.isArray(attachmentList)) {
900
+ attachmentList = [attachmentList];
901
+ }
902
+
903
+ // Effective limits merge global defaults with per-agent overrides.
904
+ // When context is missing (legacy callers), falls back to this.config.
905
+ const _attLimits = this._limits(context);
906
+
907
+ // Validate attachment count
908
+ if (attachmentList.length > _attLimits.maxAttachmentsPerMessage) {
909
+ throw new Error(`Maximum ${_attLimits.maxAttachmentsPerMessage} attachments allowed`);
910
+ }
911
+
912
+ const processedAttachments = [];
913
+
914
+ for (const attachment of attachmentList) {
915
+ if (!attachment.path) continue;
916
+
917
+ try {
918
+ // Check file exists and size
919
+ const stats = await fs.stat(attachment.path);
920
+ if (stats.size > _attLimits.maxAttachmentSize) {
921
+ throw new Error(`Attachment exceeds size limit: ${attachment.path}`);
922
+ }
923
+
924
+ // Copy to storage
925
+ const attachmentId = this._generateAttachmentId();
926
+ const ext = path.extname(attachment.path);
927
+ const storagePath = path.join(this.config.storageDir, 'attachments', `${attachmentId}${ext}`);
928
+
929
+ await fs.copyFile(attachment.path, storagePath);
930
+
931
+ processedAttachments.push({
932
+ id: attachmentId,
933
+ originalPath: attachment.path,
934
+ storagePath,
935
+ type: attachment.type || 'file',
936
+ size: stats.size,
937
+ name: path.basename(attachment.path)
938
+ });
939
+
940
+ } catch (error) {
941
+ console.error(`Failed to process attachment: ${attachment.path}`, error);
942
+ }
943
+ }
944
+
945
+ return processedAttachments;
946
+ }
947
+
948
+ /**
949
+ * Notify recipients of new message
950
+ * @private
951
+ */
952
+ async _notifyRecipients(message, context) {
953
+ const agentPool = context.agentPool;
954
+ if (!agentPool) return;
955
+
956
+ for (const recipientId of message.recipients) {
957
+ try {
958
+ // Send both a notification AND inject message into conversation
959
+
960
+ // 1. Standard notification (for system awareness)
961
+ await agentPool.notifyAgent(recipientId, {
962
+ type: 'agent-communication',
963
+ from: message.sender,
964
+ conversationId: message.conversationId,
965
+ content: `📨 New message from ${message.senderName}: "${message.subject}"`,
966
+ messageId: message.id,
967
+ priority: message.priority,
968
+ requiresResponse: message.requiresReply
969
+ });
970
+
971
+ // 2. Inject the actual message content directly into recipient's conversation
972
+ const recipient = await agentPool.getAgent(recipientId);
973
+ if (recipient) {
974
+ const messageContent = `📨 **Inter-Agent Message**
975
+ **From:** ${message.senderName} (${message.sender})
976
+ **Subject:** ${message.subject}
977
+ **Priority:** ${message.priority}
978
+ **Requires Reply:** ${message.requiresReply ? 'Yes' : 'No'}
979
+
980
+ **Message:**
981
+ ${message.content}
982
+
983
+ ${message.attachments.length > 0 ? `**Attachments:** ${message.attachments.length} file(s)` : ''}
984
+
985
+ *You can reply using the agentcommunication tool with action="reply-to-message" and message-id="${message.id}"*`;
986
+
987
+ const directMessage = {
988
+ id: `agent-comm-${message.id}`,
989
+ conversationId: message.conversationId,
990
+ agentId: message.sender,
991
+ content: messageContent,
992
+ role: 'system', // System message so it's clearly visible
993
+ timestamp: message.timestamp,
994
+ type: 'agent-communication',
995
+ metadata: {
996
+ originalMessageId: message.id,
997
+ fromAgent: message.sender,
998
+ requiresResponse: message.requiresReply,
999
+ priority: message.priority
1000
+ }
1001
+ };
1002
+
1003
+ // Add to full conversation
1004
+ recipient.conversations.full.messages.push(directMessage);
1005
+ recipient.conversations.full.lastUpdated = new Date().toISOString();
1006
+
1007
+ // Add to current model conversation if active
1008
+ if (recipient.currentModel && recipient.conversations[recipient.currentModel]) {
1009
+ recipient.conversations[recipient.currentModel].messages.push(directMessage);
1010
+ recipient.conversations[recipient.currentModel].lastUpdated = new Date().toISOString();
1011
+ }
1012
+
1013
+ // Persist the updated state
1014
+ await agentPool.persistAgentState(recipientId);
1015
+
1016
+ // Queue message using new architecture
1017
+ console.log(`📬 Queueing inter-agent message for scheduler processing`, {
1018
+ recipientId,
1019
+ sender: message.sender,
1020
+ subject: message.subject,
1021
+ hasSessionId: !!context.sessionId
1022
+ });
1023
+
1024
+ await agentPool.addInterAgentMessage(recipientId, {
1025
+ id: message.id,
1026
+ messageId: message.id,
1027
+ sender: message.sender,
1028
+ senderName: message.senderName,
1029
+ subject: message.subject,
1030
+ content: message.content,
1031
+ attachments: message.attachments,
1032
+ priority: message.priority,
1033
+ requiresReply: message.requiresReply,
1034
+ conversationId: message.conversationId,
1035
+ sessionId: context.sessionId,
1036
+ timestamp: new Date().toISOString()
1037
+ });
1038
+
1039
+ console.log(`Direct message injected and queued for processing: ${recipientId}`, {
1040
+ messageId: message.id,
1041
+ fromAgent: message.sender,
1042
+ subject: message.subject,
1043
+ priority: message.priority
1044
+ });
1045
+ }
1046
+
1047
+ } catch (error) {
1048
+ console.error(`Failed to notify agent ${recipientId}:`, error);
1049
+ }
1050
+ }
1051
+ }
1052
+
1053
+ /**
1054
+ * Check if agent has replied to a message
1055
+ * @private
1056
+ */
1057
+ _hasAgentReplied(agentId, message) {
1058
+ for (const replyId of message.replies) {
1059
+ const reply = this.messages.get(replyId);
1060
+ if (reply && reply.sender === agentId) {
1061
+ return true;
1062
+ }
1063
+ // Recursively check nested replies
1064
+ if (reply && this._hasAgentReplied(agentId, reply)) {
1065
+ return true;
1066
+ }
1067
+ }
1068
+ return false;
1069
+ }
1070
+
1071
+ /**
1072
+ * Get conversation depth
1073
+ * @private
1074
+ */
1075
+ _getConversationDepth(conversationId) {
1076
+ const conversation = this.conversations.get(conversationId);
1077
+ if (!conversation) return 0;
1078
+
1079
+ let maxDepth = 0;
1080
+ const rootMessage = this.messages.get(conversation.rootMessageId);
1081
+ if (rootMessage) {
1082
+ maxDepth = this._getMessageDepth(rootMessage);
1083
+ }
1084
+
1085
+ return maxDepth;
1086
+ }
1087
+
1088
+ /**
1089
+ * Get message depth recursively
1090
+ * @private
1091
+ */
1092
+ _getMessageDepth(message, currentDepth = 0) {
1093
+ if (message.replies.length === 0) {
1094
+ return currentDepth;
1095
+ }
1096
+
1097
+ let maxDepth = currentDepth;
1098
+ for (const replyId of message.replies) {
1099
+ const reply = this.messages.get(replyId);
1100
+ if (reply) {
1101
+ const depth = this._getMessageDepth(reply, currentDepth + 1);
1102
+ maxDepth = Math.max(maxDepth, depth);
1103
+ }
1104
+ }
1105
+
1106
+ return maxDepth;
1107
+ }
1108
+
1109
+ /**
1110
+ * Update message counts for agent
1111
+ * @private
1112
+ */
1113
+ _updateMessageCounts(agentId, type) {
1114
+ if (!this.agentMessageCounts.has(agentId)) {
1115
+ this.agentMessageCounts.set(agentId, { sent: 0, received: 0 });
1116
+ }
1117
+
1118
+ const counts = this.agentMessageCounts.get(agentId);
1119
+ if (type === 'sent') {
1120
+ counts.sent++;
1121
+ } else {
1122
+ counts.received++;
1123
+ }
1124
+ }
1125
+
1126
+ /**
1127
+ * Generate unique message ID
1128
+ * @private
1129
+ */
1130
+ _generateMessageId() {
1131
+ return `msg-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
1132
+ }
1133
+
1134
+ /**
1135
+ * Generate unique conversation ID
1136
+ * @private
1137
+ */
1138
+ _generateConversationId() {
1139
+ return `conv-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
1140
+ }
1141
+
1142
+ /**
1143
+ * Generate unique attachment ID
1144
+ * @private
1145
+ */
1146
+ _generateAttachmentId() {
1147
+ return `att-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
1148
+ }
1149
+
1150
+ /**
1151
+ * Cleanup old messages and conversations
1152
+ * Called periodically to prevent memory growth
1153
+ */
1154
+ async cleanup() {
1155
+ const now = Date.now();
1156
+ const retentionCutoff = now - this.config.messageRetentionPeriod;
1157
+
1158
+ // Clean up old messages
1159
+ for (const [messageId, message] of this.messages.entries()) {
1160
+ const messageTime = new Date(message.timestamp).getTime();
1161
+ if (messageTime < retentionCutoff) {
1162
+ // Clean up attachments
1163
+ for (const attachment of message.attachments) {
1164
+ try {
1165
+ await fs.unlink(attachment.storagePath);
1166
+ } catch (error) {
1167
+ // Ignore errors for missing files
1168
+ }
1169
+ }
1170
+
1171
+ // Remove from inboxes
1172
+ for (const [agentId, inbox] of this.agentInboxes.entries()) {
1173
+ inbox.delete(messageId);
1174
+ }
1175
+
1176
+ this.messages.delete(messageId);
1177
+ }
1178
+ }
1179
+
1180
+ // Clean up old conversations
1181
+ for (const [conversationId, conversation] of this.conversations.entries()) {
1182
+ const lastActivity = new Date(conversation.lastActivity).getTime();
1183
+ if (lastActivity < retentionCutoff || conversation.status === 'ended') {
1184
+ // Remove from agent conversations
1185
+ for (const [agentId, convs] of this.agentConversations.entries()) {
1186
+ convs.delete(conversationId);
1187
+ }
1188
+
1189
+ this.conversations.delete(conversationId);
1190
+ }
1191
+ }
1192
+ }
1193
+
1194
+ /**
1195
+ * Broadcast message to WebSocket for UI visibility
1196
+ * @private
1197
+ */
1198
+ async _broadcastToUI(message, eventType, context) {
1199
+ try {
1200
+ // Build a formatted message for UI display
1201
+ const uiMessage = {
1202
+ type: 'agent-communication',
1203
+ eventType,
1204
+ timestamp: message.timestamp,
1205
+ messageId: message.id,
1206
+ conversationId: message.conversationId,
1207
+ sender: {
1208
+ id: message.sender,
1209
+ name: message.senderName
1210
+ },
1211
+ recipients: Object.entries(message.recipientNames || {}).map(([id, name]) => ({
1212
+ id,
1213
+ name
1214
+ })),
1215
+ subject: message.subject,
1216
+ content: message.content,
1217
+ priority: message.priority,
1218
+ requiresReply: message.requiresReply,
1219
+ hasAttachments: message.attachments && message.attachments.length > 0,
1220
+ attachmentCount: message.attachments ? message.attachments.length : 0,
1221
+ metadata: message.metadata
1222
+ };
1223
+
1224
+ // Try multiple broadcast methods
1225
+ // Method 1: Through agentPool if available
1226
+ if (context.agentPool && context.agentPool.messageProcessor) {
1227
+ const messageProcessor = context.agentPool.messageProcessor;
1228
+ if (messageProcessor && messageProcessor.orchestrator && messageProcessor.orchestrator.webServer) {
1229
+ // Use broadcastToSession - it will fallback to all connections if session not found
1230
+ messageProcessor.orchestrator.webServer.broadcastToSession(context.sessionId || 'web-session', {
1231
+ type: 'agent-communication',
1232
+ action: 'agent-communication',
1233
+ data: uiMessage
1234
+ });
1235
+ return;
1236
+ }
1237
+ }
1238
+
1239
+ // Method 2: Direct orchestrator access
1240
+ if (context.orchestrator && context.orchestrator.webServer) {
1241
+ context.orchestrator.webServer.broadcastToSession(context.sessionId || 'web-session', {
1242
+ type: 'agent-communication',
1243
+ action: 'agent-communication',
1244
+ data: uiMessage
1245
+ });
1246
+ return;
1247
+ }
1248
+
1249
+ // Method 3: Through global reference (if set during initialization)
1250
+ if (global.loxiaWebServer) {
1251
+ global.loxiaWebServer.broadcastToSession(context.sessionId || 'web-session', {
1252
+ type: 'agent-communication',
1253
+ action: 'agent-communication',
1254
+ data: uiMessage
1255
+ });
1256
+ }
1257
+ } catch (error) {
1258
+ // Don't fail the operation if broadcast fails
1259
+ console.error('Failed to broadcast agent message to UI:', error);
1260
+ }
1261
+ }
1262
+
1263
+ /**
1264
+ * Check if sender can send message to recipient
1265
+ * @private
1266
+ */
1267
+ async _canSendMessage(senderAgent, recipientId, context) {
1268
+ const agentPool = context.agentPool;
1269
+
1270
+ // Ensure interAgentTracking is a Map (defensive - may be plain object from JSON)
1271
+ if (!senderAgent.interAgentTracking || !(senderAgent.interAgentTracking instanceof Map)) {
1272
+ if (senderAgent.interAgentTracking && typeof senderAgent.interAgentTracking === 'object') {
1273
+ senderAgent.interAgentTracking = new Map(Object.entries(senderAgent.interAgentTracking));
1274
+ } else {
1275
+ senderAgent.interAgentTracking = new Map();
1276
+ }
1277
+ }
1278
+
1279
+ const tracking = senderAgent.interAgentTracking;
1280
+
1281
+ // Initialize tracking for this recipient if needed
1282
+ if (!tracking.has(recipientId)) {
1283
+ tracking.set(recipientId, {
1284
+ lastSent: null,
1285
+ lastReceived: null,
1286
+ lastType: null
1287
+ });
1288
+ }
1289
+
1290
+ const recipientTracking = tracking.get(recipientId);
1291
+ const now = Date.now();
1292
+ const MIN_INTERVAL = 60 * 1000; // 1 minute
1293
+
1294
+ // Rule 1: Always allow if recipient has replied since our last message
1295
+ if (recipientTracking.lastType === 'received' ||
1296
+ (recipientTracking.lastReceived && recipientTracking.lastReceived > recipientTracking.lastSent)) {
1297
+ return { allowed: true };
1298
+ }
1299
+
1300
+ // Rule 2: Allow if minimum time has passed since last send
1301
+ if (recipientTracking.lastSent) {
1302
+ const timeSinceLastSend = now - recipientTracking.lastSent;
1303
+ if (timeSinceLastSend >= MIN_INTERVAL) {
1304
+ return { allowed: true };
1305
+ }
1306
+
1307
+ // Calculate when next send is allowed
1308
+ const nextAllowedTime = recipientTracking.lastSent + MIN_INTERVAL;
1309
+ return {
1310
+ allowed: false,
1311
+ reason: `Must wait ${Math.ceil((nextAllowedTime - now) / 1000)}s since last message`,
1312
+ waitUntil: nextAllowedTime
1313
+ };
1314
+ }
1315
+
1316
+ // Rule 3: First message to this recipient is always allowed
1317
+ return { allowed: true };
1318
+ }
1319
+
1320
+ /**
1321
+ * Update conversation tracking after message sent/received
1322
+ * @private
1323
+ */
1324
+ async _updateConversationTracking(senderAgentId, recipientId, action, context) {
1325
+ const agentPool = context.agentPool;
1326
+ const senderAgent = await agentPool.getAgent(senderAgentId);
1327
+ if (!senderAgent) return;
1328
+
1329
+ const now = Date.now();
1330
+
1331
+ // Ensure sender's interAgentTracking is a Map
1332
+ if (!senderAgent.interAgentTracking || !(senderAgent.interAgentTracking instanceof Map)) {
1333
+ if (senderAgent.interAgentTracking && typeof senderAgent.interAgentTracking === 'object') {
1334
+ senderAgent.interAgentTracking = new Map(Object.entries(senderAgent.interAgentTracking));
1335
+ } else {
1336
+ senderAgent.interAgentTracking = new Map();
1337
+ }
1338
+ }
1339
+
1340
+ // Update sender's tracking
1341
+ if (!senderAgent.interAgentTracking.has(recipientId)) {
1342
+ senderAgent.interAgentTracking.set(recipientId, {
1343
+ lastSent: null,
1344
+ lastReceived: null,
1345
+ lastType: null
1346
+ });
1347
+ }
1348
+
1349
+ const tracking = senderAgent.interAgentTracking.get(recipientId);
1350
+
1351
+ if (action === 'sent') {
1352
+ tracking.lastSent = now;
1353
+ tracking.lastType = 'sent';
1354
+ } else if (action === 'replied') {
1355
+ tracking.lastSent = now;
1356
+ tracking.lastType = 'sent'; // Reply is still a send action
1357
+ }
1358
+
1359
+ // Update recipient's tracking (they received a message)
1360
+ const recipientAgent = await agentPool.getAgent(recipientId);
1361
+ if (recipientAgent) {
1362
+ // Ensure recipient's interAgentTracking is a Map
1363
+ if (!recipientAgent.interAgentTracking || !(recipientAgent.interAgentTracking instanceof Map)) {
1364
+ if (recipientAgent.interAgentTracking && typeof recipientAgent.interAgentTracking === 'object') {
1365
+ recipientAgent.interAgentTracking = new Map(Object.entries(recipientAgent.interAgentTracking));
1366
+ } else {
1367
+ recipientAgent.interAgentTracking = new Map();
1368
+ }
1369
+ }
1370
+
1371
+ if (!recipientAgent.interAgentTracking.has(senderAgentId)) {
1372
+ recipientAgent.interAgentTracking.set(senderAgentId, {
1373
+ lastSent: null,
1374
+ lastReceived: null,
1375
+ lastType: null
1376
+ });
1377
+ }
1378
+
1379
+ const recipientTracking = recipientAgent.interAgentTracking.get(senderAgentId);
1380
+ recipientTracking.lastReceived = now;
1381
+ recipientTracking.lastType = 'received';
1382
+
1383
+ // Persist recipient agent state
1384
+ await agentPool.persistAgentState(recipientId);
1385
+ }
1386
+
1387
+ // Persist sender agent state
1388
+ await agentPool.persistAgentState(senderAgentId);
1389
+ }
1390
+
1391
+ /**
1392
+ * Set message processor for broadcasting
1393
+ * Called during tool initialization
1394
+ */
1395
+ setMessageProcessor(messageProcessor) {
1396
+ this.messageProcessor = messageProcessor;
1397
+ }
1398
+ }
1399
+
1386
1400
  export default AgentCommunicationTool;