onbuzz 3.3.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 (506) hide show
  1. package/LICENSE +267 -0
  2. package/README.md +425 -0
  3. package/bin/cli.js +556 -0
  4. package/bin/loxia-terminal-v2.js +162 -0
  5. package/bin/loxia-terminal.js +90 -0
  6. package/bin/start-with-terminal.js +200 -0
  7. package/node_modules/@isaacs/balanced-match/LICENSE.md +23 -0
  8. package/node_modules/@isaacs/balanced-match/README.md +60 -0
  9. package/node_modules/@isaacs/balanced-match/dist/commonjs/index.d.ts +9 -0
  10. package/node_modules/@isaacs/balanced-match/dist/commonjs/index.d.ts.map +1 -0
  11. package/node_modules/@isaacs/balanced-match/dist/commonjs/index.js +59 -0
  12. package/node_modules/@isaacs/balanced-match/dist/commonjs/index.js.map +1 -0
  13. package/node_modules/@isaacs/balanced-match/dist/commonjs/package.json +3 -0
  14. package/node_modules/@isaacs/balanced-match/dist/esm/index.d.ts +9 -0
  15. package/node_modules/@isaacs/balanced-match/dist/esm/index.d.ts.map +1 -0
  16. package/node_modules/@isaacs/balanced-match/dist/esm/index.js +54 -0
  17. package/node_modules/@isaacs/balanced-match/dist/esm/index.js.map +1 -0
  18. package/node_modules/@isaacs/balanced-match/dist/esm/package.json +3 -0
  19. package/node_modules/@isaacs/balanced-match/package.json +79 -0
  20. package/node_modules/@isaacs/brace-expansion/LICENSE +23 -0
  21. package/node_modules/@isaacs/brace-expansion/README.md +97 -0
  22. package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.d.ts +6 -0
  23. package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.d.ts.map +1 -0
  24. package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js +199 -0
  25. package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js.map +1 -0
  26. package/node_modules/@isaacs/brace-expansion/dist/commonjs/package.json +3 -0
  27. package/node_modules/@isaacs/brace-expansion/dist/esm/index.d.ts +6 -0
  28. package/node_modules/@isaacs/brace-expansion/dist/esm/index.d.ts.map +1 -0
  29. package/node_modules/@isaacs/brace-expansion/dist/esm/index.js +195 -0
  30. package/node_modules/@isaacs/brace-expansion/dist/esm/index.js.map +1 -0
  31. package/node_modules/@isaacs/brace-expansion/dist/esm/package.json +3 -0
  32. package/node_modules/@isaacs/brace-expansion/package.json +60 -0
  33. package/node_modules/glob/LICENSE.md +63 -0
  34. package/node_modules/glob/README.md +1177 -0
  35. package/node_modules/glob/dist/commonjs/glob.d.ts +388 -0
  36. package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -0
  37. package/node_modules/glob/dist/commonjs/glob.js +247 -0
  38. package/node_modules/glob/dist/commonjs/glob.js.map +1 -0
  39. package/node_modules/glob/dist/commonjs/has-magic.d.ts +14 -0
  40. package/node_modules/glob/dist/commonjs/has-magic.d.ts.map +1 -0
  41. package/node_modules/glob/dist/commonjs/has-magic.js +27 -0
  42. package/node_modules/glob/dist/commonjs/has-magic.js.map +1 -0
  43. package/node_modules/glob/dist/commonjs/ignore.d.ts +24 -0
  44. package/node_modules/glob/dist/commonjs/ignore.d.ts.map +1 -0
  45. package/node_modules/glob/dist/commonjs/ignore.js +119 -0
  46. package/node_modules/glob/dist/commonjs/ignore.js.map +1 -0
  47. package/node_modules/glob/dist/commonjs/index.d.ts +97 -0
  48. package/node_modules/glob/dist/commonjs/index.d.ts.map +1 -0
  49. package/node_modules/glob/dist/commonjs/index.js +68 -0
  50. package/node_modules/glob/dist/commonjs/index.js.map +1 -0
  51. package/node_modules/glob/dist/commonjs/index.min.js +4 -0
  52. package/node_modules/glob/dist/commonjs/index.min.js.map +7 -0
  53. package/node_modules/glob/dist/commonjs/package.json +3 -0
  54. package/node_modules/glob/dist/commonjs/pattern.d.ts +76 -0
  55. package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -0
  56. package/node_modules/glob/dist/commonjs/pattern.js +219 -0
  57. package/node_modules/glob/dist/commonjs/pattern.js.map +1 -0
  58. package/node_modules/glob/dist/commonjs/processor.d.ts +59 -0
  59. package/node_modules/glob/dist/commonjs/processor.d.ts.map +1 -0
  60. package/node_modules/glob/dist/commonjs/processor.js +301 -0
  61. package/node_modules/glob/dist/commonjs/processor.js.map +1 -0
  62. package/node_modules/glob/dist/commonjs/walker.d.ts +97 -0
  63. package/node_modules/glob/dist/commonjs/walker.d.ts.map +1 -0
  64. package/node_modules/glob/dist/commonjs/walker.js +387 -0
  65. package/node_modules/glob/dist/commonjs/walker.js.map +1 -0
  66. package/node_modules/glob/dist/esm/glob.d.ts +388 -0
  67. package/node_modules/glob/dist/esm/glob.d.ts.map +1 -0
  68. package/node_modules/glob/dist/esm/glob.js +243 -0
  69. package/node_modules/glob/dist/esm/glob.js.map +1 -0
  70. package/node_modules/glob/dist/esm/has-magic.d.ts +14 -0
  71. package/node_modules/glob/dist/esm/has-magic.d.ts.map +1 -0
  72. package/node_modules/glob/dist/esm/has-magic.js +23 -0
  73. package/node_modules/glob/dist/esm/has-magic.js.map +1 -0
  74. package/node_modules/glob/dist/esm/ignore.d.ts +24 -0
  75. package/node_modules/glob/dist/esm/ignore.d.ts.map +1 -0
  76. package/node_modules/glob/dist/esm/ignore.js +115 -0
  77. package/node_modules/glob/dist/esm/ignore.js.map +1 -0
  78. package/node_modules/glob/dist/esm/index.d.ts +97 -0
  79. package/node_modules/glob/dist/esm/index.d.ts.map +1 -0
  80. package/node_modules/glob/dist/esm/index.js +55 -0
  81. package/node_modules/glob/dist/esm/index.js.map +1 -0
  82. package/node_modules/glob/dist/esm/index.min.js +4 -0
  83. package/node_modules/glob/dist/esm/index.min.js.map +7 -0
  84. package/node_modules/glob/dist/esm/package.json +3 -0
  85. package/node_modules/glob/dist/esm/pattern.d.ts +76 -0
  86. package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -0
  87. package/node_modules/glob/dist/esm/pattern.js +215 -0
  88. package/node_modules/glob/dist/esm/pattern.js.map +1 -0
  89. package/node_modules/glob/dist/esm/processor.d.ts +59 -0
  90. package/node_modules/glob/dist/esm/processor.d.ts.map +1 -0
  91. package/node_modules/glob/dist/esm/processor.js +294 -0
  92. package/node_modules/glob/dist/esm/processor.js.map +1 -0
  93. package/node_modules/glob/dist/esm/walker.d.ts +97 -0
  94. package/node_modules/glob/dist/esm/walker.d.ts.map +1 -0
  95. package/node_modules/glob/dist/esm/walker.js +381 -0
  96. package/node_modules/glob/dist/esm/walker.js.map +1 -0
  97. package/node_modules/glob/node_modules/minimatch/LICENSE.md +55 -0
  98. package/node_modules/glob/node_modules/minimatch/README.md +453 -0
  99. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts +2 -0
  100. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts.map +1 -0
  101. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js +14 -0
  102. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js.map +1 -0
  103. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts +20 -0
  104. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -0
  105. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js +591 -0
  106. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js.map +1 -0
  107. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts +8 -0
  108. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -0
  109. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js +152 -0
  110. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -0
  111. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts +15 -0
  112. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts.map +1 -0
  113. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js +30 -0
  114. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js.map +1 -0
  115. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts +94 -0
  116. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -0
  117. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js +1029 -0
  118. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js.map +1 -0
  119. package/node_modules/glob/node_modules/minimatch/dist/commonjs/package.json +3 -0
  120. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts +22 -0
  121. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts.map +1 -0
  122. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js +38 -0
  123. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -0
  124. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts +2 -0
  125. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts.map +1 -0
  126. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js +10 -0
  127. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -0
  128. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts +20 -0
  129. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -0
  130. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js +587 -0
  131. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js.map +1 -0
  132. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts +8 -0
  133. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -0
  134. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js +148 -0
  135. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -0
  136. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts +15 -0
  137. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts.map +1 -0
  138. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js +26 -0
  139. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js.map +1 -0
  140. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts +94 -0
  141. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts.map +1 -0
  142. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js +1016 -0
  143. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js.map +1 -0
  144. package/node_modules/glob/node_modules/minimatch/dist/esm/package.json +3 -0
  145. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts +22 -0
  146. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts.map +1 -0
  147. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js +34 -0
  148. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js.map +1 -0
  149. package/node_modules/glob/node_modules/minimatch/package.json +67 -0
  150. package/node_modules/glob/package.json +101 -0
  151. package/node_modules/minipass/LICENSE +15 -0
  152. package/node_modules/minipass/README.md +825 -0
  153. package/node_modules/minipass/dist/commonjs/index.d.ts +549 -0
  154. package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -0
  155. package/node_modules/minipass/dist/commonjs/index.js +1028 -0
  156. package/node_modules/minipass/dist/commonjs/index.js.map +1 -0
  157. package/node_modules/minipass/dist/commonjs/package.json +3 -0
  158. package/node_modules/minipass/dist/esm/index.d.ts +549 -0
  159. package/node_modules/minipass/dist/esm/index.d.ts.map +1 -0
  160. package/node_modules/minipass/dist/esm/index.js +1018 -0
  161. package/node_modules/minipass/dist/esm/index.js.map +1 -0
  162. package/node_modules/minipass/dist/esm/package.json +3 -0
  163. package/node_modules/minipass/package.json +82 -0
  164. package/node_modules/package-json-from-dist/LICENSE.md +63 -0
  165. package/node_modules/package-json-from-dist/README.md +110 -0
  166. package/node_modules/package-json-from-dist/dist/commonjs/index.d.ts +89 -0
  167. package/node_modules/package-json-from-dist/dist/commonjs/index.d.ts.map +1 -0
  168. package/node_modules/package-json-from-dist/dist/commonjs/index.js +134 -0
  169. package/node_modules/package-json-from-dist/dist/commonjs/index.js.map +1 -0
  170. package/node_modules/package-json-from-dist/dist/commonjs/package.json +3 -0
  171. package/node_modules/package-json-from-dist/dist/esm/index.d.ts +89 -0
  172. package/node_modules/package-json-from-dist/dist/esm/index.d.ts.map +1 -0
  173. package/node_modules/package-json-from-dist/dist/esm/index.js +129 -0
  174. package/node_modules/package-json-from-dist/dist/esm/index.js.map +1 -0
  175. package/node_modules/package-json-from-dist/dist/esm/package.json +3 -0
  176. package/node_modules/package-json-from-dist/package.json +68 -0
  177. package/node_modules/path-scurry/LICENSE.md +55 -0
  178. package/node_modules/path-scurry/README.md +636 -0
  179. package/node_modules/path-scurry/dist/commonjs/index.d.ts +1115 -0
  180. package/node_modules/path-scurry/dist/commonjs/index.d.ts.map +1 -0
  181. package/node_modules/path-scurry/dist/commonjs/index.js +2018 -0
  182. package/node_modules/path-scurry/dist/commonjs/index.js.map +1 -0
  183. package/node_modules/path-scurry/dist/commonjs/package.json +3 -0
  184. package/node_modules/path-scurry/dist/esm/index.d.ts +1115 -0
  185. package/node_modules/path-scurry/dist/esm/index.d.ts.map +1 -0
  186. package/node_modules/path-scurry/dist/esm/index.js +1983 -0
  187. package/node_modules/path-scurry/dist/esm/index.js.map +1 -0
  188. package/node_modules/path-scurry/dist/esm/package.json +3 -0
  189. package/node_modules/path-scurry/node_modules/lru-cache/LICENSE.md +55 -0
  190. package/node_modules/path-scurry/node_modules/lru-cache/README.md +383 -0
  191. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts +1323 -0
  192. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -0
  193. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +1589 -0
  194. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js.map +1 -0
  195. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +2 -0
  196. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js.map +7 -0
  197. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/package.json +3 -0
  198. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts +1323 -0
  199. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -0
  200. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +1585 -0
  201. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js.map +1 -0
  202. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +2 -0
  203. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js.map +7 -0
  204. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/package.json +3 -0
  205. package/node_modules/path-scurry/node_modules/lru-cache/package.json +101 -0
  206. package/node_modules/path-scurry/package.json +88 -0
  207. package/node_modules/rimraf/LICENSE.md +55 -0
  208. package/node_modules/rimraf/README.md +226 -0
  209. package/node_modules/rimraf/dist/commonjs/default-tmp.d.ts +3 -0
  210. package/node_modules/rimraf/dist/commonjs/default-tmp.d.ts.map +1 -0
  211. package/node_modules/rimraf/dist/commonjs/default-tmp.js +58 -0
  212. package/node_modules/rimraf/dist/commonjs/default-tmp.js.map +1 -0
  213. package/node_modules/rimraf/dist/commonjs/error.d.ts +6 -0
  214. package/node_modules/rimraf/dist/commonjs/error.d.ts.map +1 -0
  215. package/node_modules/rimraf/dist/commonjs/error.js +10 -0
  216. package/node_modules/rimraf/dist/commonjs/error.js.map +1 -0
  217. package/node_modules/rimraf/dist/commonjs/fix-eperm.d.ts +3 -0
  218. package/node_modules/rimraf/dist/commonjs/fix-eperm.d.ts.map +1 -0
  219. package/node_modules/rimraf/dist/commonjs/fix-eperm.js +38 -0
  220. package/node_modules/rimraf/dist/commonjs/fix-eperm.js.map +1 -0
  221. package/node_modules/rimraf/dist/commonjs/fs.d.ts +15 -0
  222. package/node_modules/rimraf/dist/commonjs/fs.d.ts.map +1 -0
  223. package/node_modules/rimraf/dist/commonjs/fs.js +33 -0
  224. package/node_modules/rimraf/dist/commonjs/fs.js.map +1 -0
  225. package/node_modules/rimraf/dist/commonjs/ignore-enoent.d.ts +3 -0
  226. package/node_modules/rimraf/dist/commonjs/ignore-enoent.d.ts.map +1 -0
  227. package/node_modules/rimraf/dist/commonjs/ignore-enoent.js +24 -0
  228. package/node_modules/rimraf/dist/commonjs/ignore-enoent.js.map +1 -0
  229. package/node_modules/rimraf/dist/commonjs/index.d.ts +50 -0
  230. package/node_modules/rimraf/dist/commonjs/index.d.ts.map +1 -0
  231. package/node_modules/rimraf/dist/commonjs/index.js +78 -0
  232. package/node_modules/rimraf/dist/commonjs/index.js.map +1 -0
  233. package/node_modules/rimraf/dist/commonjs/opt-arg.d.ts +34 -0
  234. package/node_modules/rimraf/dist/commonjs/opt-arg.d.ts.map +1 -0
  235. package/node_modules/rimraf/dist/commonjs/opt-arg.js +53 -0
  236. package/node_modules/rimraf/dist/commonjs/opt-arg.js.map +1 -0
  237. package/node_modules/rimraf/dist/commonjs/package.json +3 -0
  238. package/node_modules/rimraf/dist/commonjs/path-arg.d.ts +4 -0
  239. package/node_modules/rimraf/dist/commonjs/path-arg.d.ts.map +1 -0
  240. package/node_modules/rimraf/dist/commonjs/path-arg.js +48 -0
  241. package/node_modules/rimraf/dist/commonjs/path-arg.js.map +1 -0
  242. package/node_modules/rimraf/dist/commonjs/readdir-or-error.d.ts +3 -0
  243. package/node_modules/rimraf/dist/commonjs/readdir-or-error.d.ts.map +1 -0
  244. package/node_modules/rimraf/dist/commonjs/readdir-or-error.js +19 -0
  245. package/node_modules/rimraf/dist/commonjs/readdir-or-error.js.map +1 -0
  246. package/node_modules/rimraf/dist/commonjs/retry-busy.d.ts +8 -0
  247. package/node_modules/rimraf/dist/commonjs/retry-busy.d.ts.map +1 -0
  248. package/node_modules/rimraf/dist/commonjs/retry-busy.js +65 -0
  249. package/node_modules/rimraf/dist/commonjs/retry-busy.js.map +1 -0
  250. package/node_modules/rimraf/dist/commonjs/rimraf-manual.d.ts +3 -0
  251. package/node_modules/rimraf/dist/commonjs/rimraf-manual.d.ts.map +1 -0
  252. package/node_modules/rimraf/dist/commonjs/rimraf-manual.js +8 -0
  253. package/node_modules/rimraf/dist/commonjs/rimraf-manual.js.map +1 -0
  254. package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.d.ts +4 -0
  255. package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.d.ts.map +1 -0
  256. package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.js +138 -0
  257. package/node_modules/rimraf/dist/commonjs/rimraf-move-remove.js.map +1 -0
  258. package/node_modules/rimraf/dist/commonjs/rimraf-native.d.ts +4 -0
  259. package/node_modules/rimraf/dist/commonjs/rimraf-native.d.ts.map +1 -0
  260. package/node_modules/rimraf/dist/commonjs/rimraf-native.js +24 -0
  261. package/node_modules/rimraf/dist/commonjs/rimraf-native.js.map +1 -0
  262. package/node_modules/rimraf/dist/commonjs/rimraf-posix.d.ts +4 -0
  263. package/node_modules/rimraf/dist/commonjs/rimraf-posix.d.ts.map +1 -0
  264. package/node_modules/rimraf/dist/commonjs/rimraf-posix.js +103 -0
  265. package/node_modules/rimraf/dist/commonjs/rimraf-posix.js.map +1 -0
  266. package/node_modules/rimraf/dist/commonjs/rimraf-windows.d.ts +4 -0
  267. package/node_modules/rimraf/dist/commonjs/rimraf-windows.d.ts.map +1 -0
  268. package/node_modules/rimraf/dist/commonjs/rimraf-windows.js +159 -0
  269. package/node_modules/rimraf/dist/commonjs/rimraf-windows.js.map +1 -0
  270. package/node_modules/rimraf/dist/commonjs/use-native.d.ts +4 -0
  271. package/node_modules/rimraf/dist/commonjs/use-native.d.ts.map +1 -0
  272. package/node_modules/rimraf/dist/commonjs/use-native.js +18 -0
  273. package/node_modules/rimraf/dist/commonjs/use-native.js.map +1 -0
  274. package/node_modules/rimraf/dist/esm/bin.d.mts +3 -0
  275. package/node_modules/rimraf/dist/esm/bin.d.mts.map +1 -0
  276. package/node_modules/rimraf/dist/esm/bin.mjs +250 -0
  277. package/node_modules/rimraf/dist/esm/bin.mjs.map +1 -0
  278. package/node_modules/rimraf/dist/esm/default-tmp.d.ts +3 -0
  279. package/node_modules/rimraf/dist/esm/default-tmp.d.ts.map +1 -0
  280. package/node_modules/rimraf/dist/esm/default-tmp.js +55 -0
  281. package/node_modules/rimraf/dist/esm/default-tmp.js.map +1 -0
  282. package/node_modules/rimraf/dist/esm/error.d.ts +6 -0
  283. package/node_modules/rimraf/dist/esm/error.d.ts.map +1 -0
  284. package/node_modules/rimraf/dist/esm/error.js +5 -0
  285. package/node_modules/rimraf/dist/esm/error.js.map +1 -0
  286. package/node_modules/rimraf/dist/esm/fix-eperm.d.ts +3 -0
  287. package/node_modules/rimraf/dist/esm/fix-eperm.d.ts.map +1 -0
  288. package/node_modules/rimraf/dist/esm/fix-eperm.js +33 -0
  289. package/node_modules/rimraf/dist/esm/fix-eperm.js.map +1 -0
  290. package/node_modules/rimraf/dist/esm/fs.d.ts +15 -0
  291. package/node_modules/rimraf/dist/esm/fs.d.ts.map +1 -0
  292. package/node_modules/rimraf/dist/esm/fs.js +18 -0
  293. package/node_modules/rimraf/dist/esm/fs.js.map +1 -0
  294. package/node_modules/rimraf/dist/esm/ignore-enoent.d.ts +3 -0
  295. package/node_modules/rimraf/dist/esm/ignore-enoent.d.ts.map +1 -0
  296. package/node_modules/rimraf/dist/esm/ignore-enoent.js +19 -0
  297. package/node_modules/rimraf/dist/esm/ignore-enoent.js.map +1 -0
  298. package/node_modules/rimraf/dist/esm/index.d.ts +50 -0
  299. package/node_modules/rimraf/dist/esm/index.d.ts.map +1 -0
  300. package/node_modules/rimraf/dist/esm/index.js +70 -0
  301. package/node_modules/rimraf/dist/esm/index.js.map +1 -0
  302. package/node_modules/rimraf/dist/esm/opt-arg.d.ts +34 -0
  303. package/node_modules/rimraf/dist/esm/opt-arg.d.ts.map +1 -0
  304. package/node_modules/rimraf/dist/esm/opt-arg.js +46 -0
  305. package/node_modules/rimraf/dist/esm/opt-arg.js.map +1 -0
  306. package/node_modules/rimraf/dist/esm/package.json +3 -0
  307. package/node_modules/rimraf/dist/esm/path-arg.d.ts +4 -0
  308. package/node_modules/rimraf/dist/esm/path-arg.d.ts.map +1 -0
  309. package/node_modules/rimraf/dist/esm/path-arg.js +46 -0
  310. package/node_modules/rimraf/dist/esm/path-arg.js.map +1 -0
  311. package/node_modules/rimraf/dist/esm/readdir-or-error.d.ts +3 -0
  312. package/node_modules/rimraf/dist/esm/readdir-or-error.d.ts.map +1 -0
  313. package/node_modules/rimraf/dist/esm/readdir-or-error.js +14 -0
  314. package/node_modules/rimraf/dist/esm/readdir-or-error.js.map +1 -0
  315. package/node_modules/rimraf/dist/esm/retry-busy.d.ts +8 -0
  316. package/node_modules/rimraf/dist/esm/retry-busy.d.ts.map +1 -0
  317. package/node_modules/rimraf/dist/esm/retry-busy.js +60 -0
  318. package/node_modules/rimraf/dist/esm/retry-busy.js.map +1 -0
  319. package/node_modules/rimraf/dist/esm/rimraf-manual.d.ts +3 -0
  320. package/node_modules/rimraf/dist/esm/rimraf-manual.d.ts.map +1 -0
  321. package/node_modules/rimraf/dist/esm/rimraf-manual.js +5 -0
  322. package/node_modules/rimraf/dist/esm/rimraf-manual.js.map +1 -0
  323. package/node_modules/rimraf/dist/esm/rimraf-move-remove.d.ts +4 -0
  324. package/node_modules/rimraf/dist/esm/rimraf-move-remove.d.ts.map +1 -0
  325. package/node_modules/rimraf/dist/esm/rimraf-move-remove.js +133 -0
  326. package/node_modules/rimraf/dist/esm/rimraf-move-remove.js.map +1 -0
  327. package/node_modules/rimraf/dist/esm/rimraf-native.d.ts +4 -0
  328. package/node_modules/rimraf/dist/esm/rimraf-native.d.ts.map +1 -0
  329. package/node_modules/rimraf/dist/esm/rimraf-native.js +19 -0
  330. package/node_modules/rimraf/dist/esm/rimraf-native.js.map +1 -0
  331. package/node_modules/rimraf/dist/esm/rimraf-posix.d.ts +4 -0
  332. package/node_modules/rimraf/dist/esm/rimraf-posix.d.ts.map +1 -0
  333. package/node_modules/rimraf/dist/esm/rimraf-posix.js +98 -0
  334. package/node_modules/rimraf/dist/esm/rimraf-posix.js.map +1 -0
  335. package/node_modules/rimraf/dist/esm/rimraf-windows.d.ts +4 -0
  336. package/node_modules/rimraf/dist/esm/rimraf-windows.d.ts.map +1 -0
  337. package/node_modules/rimraf/dist/esm/rimraf-windows.js +154 -0
  338. package/node_modules/rimraf/dist/esm/rimraf-windows.js.map +1 -0
  339. package/node_modules/rimraf/dist/esm/use-native.d.ts +4 -0
  340. package/node_modules/rimraf/dist/esm/use-native.d.ts.map +1 -0
  341. package/node_modules/rimraf/dist/esm/use-native.js +15 -0
  342. package/node_modules/rimraf/dist/esm/use-native.js.map +1 -0
  343. package/node_modules/rimraf/package.json +92 -0
  344. package/package.json +152 -0
  345. package/scripts/install-scanners.js +258 -0
  346. package/scripts/watchdog.js +147 -0
  347. package/src/analyzers/CSSAnalyzer.js +297 -0
  348. package/src/analyzers/ConfigValidator.js +690 -0
  349. package/src/analyzers/ESLintAnalyzer.js +320 -0
  350. package/src/analyzers/JavaScriptAnalyzer.js +261 -0
  351. package/src/analyzers/PrettierFormatter.js +247 -0
  352. package/src/analyzers/PythonAnalyzer.js +283 -0
  353. package/src/analyzers/SecurityAnalyzer.js +729 -0
  354. package/src/analyzers/SparrowAnalyzer.js +341 -0
  355. package/src/analyzers/TypeScriptAnalyzer.js +247 -0
  356. package/src/analyzers/codeCloneDetector/analyzer.js +344 -0
  357. package/src/analyzers/codeCloneDetector/detector.js +250 -0
  358. package/src/analyzers/codeCloneDetector/index.js +192 -0
  359. package/src/analyzers/codeCloneDetector/parser.js +199 -0
  360. package/src/analyzers/codeCloneDetector/reporter.js +148 -0
  361. package/src/analyzers/codeCloneDetector/scanner.js +88 -0
  362. package/src/core/agentPool.js +1957 -0
  363. package/src/core/agentScheduler.js +3212 -0
  364. package/src/core/contextManager.js +709 -0
  365. package/src/core/flowExecutor.js +928 -0
  366. package/src/core/messageProcessor.js +808 -0
  367. package/src/core/orchestrator.js +584 -0
  368. package/src/core/stateManager.js +1500 -0
  369. package/src/index.js +972 -0
  370. package/src/interfaces/cli.js +553 -0
  371. package/src/interfaces/terminal/__tests__/smoke/advancedFeatures.test.js +208 -0
  372. package/src/interfaces/terminal/__tests__/smoke/agentControl.test.js +236 -0
  373. package/src/interfaces/terminal/__tests__/smoke/agents.test.js +138 -0
  374. package/src/interfaces/terminal/__tests__/smoke/components.test.js +137 -0
  375. package/src/interfaces/terminal/__tests__/smoke/connection.test.js +350 -0
  376. package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +156 -0
  377. package/src/interfaces/terminal/__tests__/smoke/imports.test.js +332 -0
  378. package/src/interfaces/terminal/__tests__/smoke/messages.test.js +256 -0
  379. package/src/interfaces/terminal/__tests__/smoke/tools.test.js +388 -0
  380. package/src/interfaces/terminal/api/apiClient.js +299 -0
  381. package/src/interfaces/terminal/api/messageRouter.js +262 -0
  382. package/src/interfaces/terminal/api/session.js +266 -0
  383. package/src/interfaces/terminal/api/websocket.js +497 -0
  384. package/src/interfaces/terminal/components/AgentCreator.js +705 -0
  385. package/src/interfaces/terminal/components/AgentEditor.js +678 -0
  386. package/src/interfaces/terminal/components/AgentSwitcher.js +330 -0
  387. package/src/interfaces/terminal/components/ErrorBoundary.js +92 -0
  388. package/src/interfaces/terminal/components/ErrorPanel.js +264 -0
  389. package/src/interfaces/terminal/components/Header.js +28 -0
  390. package/src/interfaces/terminal/components/HelpPanel.js +231 -0
  391. package/src/interfaces/terminal/components/InputBox.js +118 -0
  392. package/src/interfaces/terminal/components/Layout.js +603 -0
  393. package/src/interfaces/terminal/components/LoadingSpinner.js +71 -0
  394. package/src/interfaces/terminal/components/MessageList.js +281 -0
  395. package/src/interfaces/terminal/components/MultilineTextInput.js +251 -0
  396. package/src/interfaces/terminal/components/SearchPanel.js +265 -0
  397. package/src/interfaces/terminal/components/SettingsPanel.js +415 -0
  398. package/src/interfaces/terminal/components/StatusBar.js +65 -0
  399. package/src/interfaces/terminal/components/TextInput.js +127 -0
  400. package/src/interfaces/terminal/config/agentEditorConstants.js +227 -0
  401. package/src/interfaces/terminal/config/constants.js +393 -0
  402. package/src/interfaces/terminal/index.js +168 -0
  403. package/src/interfaces/terminal/state/useAgentControl.js +496 -0
  404. package/src/interfaces/terminal/state/useAgents.js +537 -0
  405. package/src/interfaces/terminal/state/useConnection.js +444 -0
  406. package/src/interfaces/terminal/state/useMessages.js +630 -0
  407. package/src/interfaces/terminal/state/useTools.js +554 -0
  408. package/src/interfaces/terminal/utils/debugLogger.js +44 -0
  409. package/src/interfaces/terminal/utils/settingsStorage.js +232 -0
  410. package/src/interfaces/terminal/utils/theme.js +85 -0
  411. package/src/interfaces/webServer.js +5457 -0
  412. package/src/modules/fileExplorer/controller.js +413 -0
  413. package/src/modules/fileExplorer/index.js +37 -0
  414. package/src/modules/fileExplorer/middleware.js +92 -0
  415. package/src/modules/fileExplorer/routes.js +158 -0
  416. package/src/modules/fileExplorer/types.js +44 -0
  417. package/src/services/agentActivityService.js +399 -0
  418. package/src/services/aiService.js +2618 -0
  419. package/src/services/apiKeyManager.js +334 -0
  420. package/src/services/benchmarkService.js +196 -0
  421. package/src/services/budgetService.js +565 -0
  422. package/src/services/contextInjectionService.js +268 -0
  423. package/src/services/conversationCompactionService.js +1103 -0
  424. package/src/services/credentialVault.js +685 -0
  425. package/src/services/errorHandler.js +810 -0
  426. package/src/services/fileAttachmentService.js +547 -0
  427. package/src/services/flowContextService.js +189 -0
  428. package/src/services/memoryService.js +521 -0
  429. package/src/services/modelRouterService.js +365 -0
  430. package/src/services/modelsService.js +323 -0
  431. package/src/services/ollamaService.js +452 -0
  432. package/src/services/portRegistry.js +336 -0
  433. package/src/services/portTracker.js +223 -0
  434. package/src/services/projectDetector.js +404 -0
  435. package/src/services/promptService.js +372 -0
  436. package/src/services/qualityInspector.js +796 -0
  437. package/src/services/scheduleService.js +725 -0
  438. package/src/services/serviceRegistry.js +386 -0
  439. package/src/services/skillsService.js +486 -0
  440. package/src/services/telegramService.js +920 -0
  441. package/src/services/tokenCountingService.js +316 -0
  442. package/src/services/visualEditorBridge.js +1033 -0
  443. package/src/services/visualEditorServer.js +1727 -0
  444. package/src/services/whatsappService.js +663 -0
  445. package/src/tools/__tests__/webTool.e2e.test.js +569 -0
  446. package/src/tools/__tests__/webTool.unit.test.js +195 -0
  447. package/src/tools/agentCommunicationTool.js +1343 -0
  448. package/src/tools/agentDelayTool.js +498 -0
  449. package/src/tools/asyncToolManager.js +604 -0
  450. package/src/tools/baseTool.js +887 -0
  451. package/src/tools/browserTool.js +897 -0
  452. package/src/tools/cloneDetectionTool.js +581 -0
  453. package/src/tools/codeMapTool.js +857 -0
  454. package/src/tools/dependencyResolverTool.js +1212 -0
  455. package/src/tools/docxTool.js +623 -0
  456. package/src/tools/excelTool.js +636 -0
  457. package/src/tools/fileContentReplaceTool.js +840 -0
  458. package/src/tools/fileTreeTool.js +833 -0
  459. package/src/tools/filesystemTool.js +1217 -0
  460. package/src/tools/helpTool.js +198 -0
  461. package/src/tools/imageTool.js +1034 -0
  462. package/src/tools/importAnalyzerTool.js +1056 -0
  463. package/src/tools/jobDoneTool.js +388 -0
  464. package/src/tools/memoryTool.js +554 -0
  465. package/src/tools/pdfTool.js +627 -0
  466. package/src/tools/seekTool.js +883 -0
  467. package/src/tools/skillsTool.js +276 -0
  468. package/src/tools/staticAnalysisTool.js +2146 -0
  469. package/src/tools/taskManagerTool.js +2836 -0
  470. package/src/tools/terminalTool.js +2486 -0
  471. package/src/tools/userPromptTool.js +474 -0
  472. package/src/tools/videoTool.js +1139 -0
  473. package/src/tools/visionTool.js +507 -0
  474. package/src/tools/visualEditorTool.js +1175 -0
  475. package/src/tools/webTool.js +3114 -0
  476. package/src/tools/whatsappTool.js +457 -0
  477. package/src/types/agent.js +519 -0
  478. package/src/types/contextReference.js +972 -0
  479. package/src/types/conversation.js +730 -0
  480. package/src/types/toolCommand.js +747 -0
  481. package/src/utilities/attachmentValidator.js +288 -0
  482. package/src/utilities/browserStealth.js +630 -0
  483. package/src/utilities/configManager.js +618 -0
  484. package/src/utilities/constants.js +870 -0
  485. package/src/utilities/directoryAccessManager.js +566 -0
  486. package/src/utilities/fileProcessor.js +307 -0
  487. package/src/utilities/humanBehavior.js +453 -0
  488. package/src/utilities/jsonRepair.js +242 -0
  489. package/src/utilities/logger.js +436 -0
  490. package/src/utilities/platformUtils.js +255 -0
  491. package/src/utilities/platformUtils.test.js +98 -0
  492. package/src/utilities/stealthConstants.js +377 -0
  493. package/src/utilities/structuredFileValidator.js +699 -0
  494. package/src/utilities/tagParser.js +878 -0
  495. package/src/utilities/toolConstants.js +415 -0
  496. package/src/utilities/userDataDir.js +300 -0
  497. package/web-ui/build/brands/autopilot/favicon.svg +1 -0
  498. package/web-ui/build/brands/autopilot/logo.webp +0 -0
  499. package/web-ui/build/brands/onbuzz/favicon.svg +1 -0
  500. package/web-ui/build/brands/onbuzz/logo-text.webp +0 -0
  501. package/web-ui/build/brands/onbuzz/logo.webp +0 -0
  502. package/web-ui/build/index.html +15 -0
  503. package/web-ui/build/logo.png +0 -0
  504. package/web-ui/build/logo2.png +0 -0
  505. package/web-ui/build/static/index-SmQFfvBs.js +746 -0
  506. package/web-ui/build/static/index-V2ySwjHp.css +1 -0
@@ -0,0 +1,1957 @@
1
+ /**
2
+ * AgentPool - Manages the lifecycle, state, and communication of all active agents
3
+ *
4
+ * Purpose:
5
+ * - Agent creation and destruction
6
+ * - Agent notification and routing
7
+ * - Multi-agent conversation coordination
8
+ * - Agent state persistence and recovery
9
+ * - Agent activity management
10
+ */
11
+
12
+ import {
13
+ AGENT_TYPES,
14
+ AGENT_STATUS,
15
+ AGENT_MODES,
16
+ MESSAGE_ROLES,
17
+ MESSAGE_TYPES,
18
+ INTER_AGENT_MESSAGE,
19
+ MODEL_FORMAT_VERSIONS,
20
+ SYSTEM_DEFAULTS
21
+ } from '../utilities/constants.js';
22
+ import DirectoryAccessManager from '../utilities/directoryAccessManager.js';
23
+ import { getVisualEditorBridge } from '../services/visualEditorBridge.js';
24
+
25
+ class AgentPool {
26
+ constructor(config, logger, stateManager, contextManager, toolsRegistry = null) {
27
+ this.config = config;
28
+ this.logger = logger;
29
+ this.stateManager = stateManager;
30
+ this.contextManager = contextManager;
31
+ this.toolsRegistry = toolsRegistry;
32
+
33
+ // Agent registry - maps agent ID to agent object
34
+ this.agents = new Map();
35
+
36
+ // Agent directory for discovery
37
+ this.agentDirectory = new Map();
38
+
39
+ // Paused agents tracking
40
+ this.pausedAgents = new Map();
41
+
42
+ // Agent notification queue
43
+ this.notificationQueue = new Map();
44
+
45
+ this.maxAgentsPerProject = config.system?.maxAgentsPerProject || SYSTEM_DEFAULTS.MAX_AGENTS_PER_PROJECT;
46
+
47
+ // MessageProcessor reference for triggering responses (set via setMessageProcessor)
48
+ this.messageProcessor = null;
49
+
50
+ // Initialize directory access manager
51
+ this.directoryAccessManager = new DirectoryAccessManager(config, logger);
52
+ }
53
+
54
+ /**
55
+ * Create a new agent with specified configuration
56
+ * @param {Object} config - Agent configuration
57
+ * @param {string} config.name - Agent name
58
+ * @param {string} config.type - Agent type ('user-created', 'system-agent', 'agent-engineer')
59
+ * @param {string} config.systemPrompt - Agent's system prompt
60
+ * @param {string} config.preferredModel - Preferred LLM model
61
+ * @param {Array} config.capabilities - Available tools/capabilities
62
+ * @param {Object} config.directoryAccess - Directory access configuration
63
+ * @param {string} config.projectDir - Project directory for default access setup
64
+ * @returns {Promise<Object>} Created agent object
65
+ */
66
+ async createAgent(config) {
67
+ // Check agent limit
68
+ if (this.agents.size >= this.maxAgentsPerProject) {
69
+ throw new Error(`Maximum agents per project exceeded (${this.maxAgentsPerProject})`);
70
+ }
71
+
72
+ const agentId = this._generateAgentId(config.name);
73
+ const now = new Date().toISOString();
74
+
75
+ // Enhance system prompt with tool descriptions if available
76
+ let enhancedSystemPrompt = config.systemPrompt;
77
+ if (this.toolsRegistry && config.capabilities && config.capabilities.length > 0) {
78
+ try {
79
+ enhancedSystemPrompt = this.toolsRegistry.enhanceSystemPrompt(
80
+ config.systemPrompt,
81
+ config.capabilities,
82
+ {
83
+ compact: config.compactToolDescriptions || false,
84
+ layered: config.layeredToolDescriptions || false,
85
+ includeExamples: config.includeToolExamples !== false,
86
+ includeUsageGuidelines: config.includeUsageGuidelines !== false,
87
+ includeSecurityNotes: config.includeSecurityNotes !== false
88
+ }
89
+ );
90
+
91
+ this.logger?.info(`System prompt enhanced with tool descriptions`, {
92
+ agentId,
93
+ capabilities: config.capabilities,
94
+ originalLength: config.systemPrompt?.length || 0,
95
+ enhancedLength: enhancedSystemPrompt?.length || 0
96
+ });
97
+ } catch (error) {
98
+ this.logger?.error(`Failed to enhance system prompt with tools`, {
99
+ agentId,
100
+ error: error.message,
101
+ capabilities: config.capabilities
102
+ });
103
+ // Fall back to original prompt
104
+ enhancedSystemPrompt = config.systemPrompt;
105
+ }
106
+ }
107
+
108
+ // Inject assigned skills index into system prompt
109
+ if (config.skills && config.skills.length > 0) {
110
+ try {
111
+ const { getSkillsService } = await import('../services/skillsService.js');
112
+ const skillsService = getSkillsService(this.logger);
113
+ await skillsService.initialize();
114
+ const summaries = await skillsService.getSkillSummaries(config.skills);
115
+ if (summaries.length > 0) {
116
+ enhancedSystemPrompt += '\n\n## ASSIGNED SKILLS\n\n';
117
+ enhancedSystemPrompt += 'Use the skills tool to browse and load skill content. Use "describe" to see sections, "read-section" to load specific parts.\n\n';
118
+ for (const s of summaries) {
119
+ const sections = s.sections?.length ? `\n Sections: ${s.sections.map(h => h.replace(/^#+\s*/, '')).join(', ')}` : '';
120
+ enhancedSystemPrompt += `- **${s.name}** (${s.lineCount} lines): ${s.description}${sections}\n`;
121
+ }
122
+ }
123
+ } catch (error) {
124
+ this.logger?.warn('Failed to inject skills index into system prompt', { error: error.message });
125
+ }
126
+ }
127
+
128
+ // Setup directory access configuration
129
+ let directoryAccess;
130
+
131
+ console.log('AgentPool DEBUG: createAgent - config.directoryAccess:', config.directoryAccess ? 'EXISTS' : 'NULL/UNDEFINED');
132
+ if (config.directoryAccess) {
133
+ console.log('AgentPool DEBUG: createAgent - directoryAccess from config:', JSON.stringify(config.directoryAccess, null, 2));
134
+ }
135
+ console.log('AgentPool DEBUG: createAgent - config.projectDir:', config.projectDir);
136
+
137
+ if (config.directoryAccess) {
138
+ // Validate provided directory access configuration
139
+ const validation = this.directoryAccessManager.validateAccessConfiguration(config.directoryAccess);
140
+ console.log('AgentPool DEBUG: createAgent - validation result:', validation);
141
+ if (!validation.valid) {
142
+ throw new Error(`Invalid directory access configuration: ${validation.errors.join(', ')}`);
143
+ }
144
+ directoryAccess = config.directoryAccess;
145
+ console.log('AgentPool DEBUG: createAgent - Using provided directoryAccess');
146
+ } else {
147
+ // Create default directory access based on project directory
148
+ const projectDir = config.projectDir || process.cwd();
149
+ directoryAccess = DirectoryAccessManager.createProjectDefaults(projectDir);
150
+ console.log('AgentPool DEBUG: createAgent - Created default directoryAccess for projectDir:', projectDir);
151
+ console.log('AgentPool DEBUG: createAgent - Default directoryAccess:', JSON.stringify(directoryAccess, null, 2));
152
+ }
153
+
154
+ const agent = {
155
+ id: agentId,
156
+ type: config.type || AGENT_TYPES.USER_CREATED,
157
+ name: config.name || `Agent-${Date.now()}`,
158
+ systemPrompt: enhancedSystemPrompt,
159
+ originalSystemPrompt: config.systemPrompt, // Store original for reference
160
+ preferredModel: config.preferredModel,
161
+ status: AGENT_STATUS.ACTIVE,
162
+ capabilities: config.capabilities || [],
163
+ directoryAccess: directoryAccess, // Directory access configuration
164
+ conversations: {
165
+ full: {
166
+ messages: [],
167
+ lastUpdated: now
168
+ }
169
+ },
170
+ currentModel: config.preferredModel,
171
+ dynamicModelRouting: config.dynamicModelRouting || false,
172
+ routingStrategy: config.routingStrategy || '',
173
+ skills: config.skills || [],
174
+ platformProvided: config.platformProvided !== false, // Default true — all models are platform-provided
175
+
176
+ // Agent Mode Configuration
177
+ mode: config.mode || AGENT_MODES.CHAT,
178
+ currentTask: null, // Current autonomous task being executed
179
+ taskStartTime: null,
180
+ maxIterations: config.maxIterations || 10, // Safety limit for autonomous loops
181
+ iterationCount: 0,
182
+ stopRequested: false,
183
+ delayEndTime: null, // When agent delay expires (for agentDelay tool)
184
+ ttl: null, // Time-to-live: processing cycles remaining (null = no TTL, number = cycles left)
185
+
186
+ // Message Queues for scheduler processing
187
+ messageQueues: {
188
+ toolResults: [], // Tool execution results waiting to be processed
189
+ interAgentMessages: [], // Messages from other agents
190
+ userMessages: [] // Messages from users
191
+ },
192
+
193
+ createdAt: now,
194
+ lastActivity: now,
195
+ pausedUntil: null,
196
+ metadata: config.metadata || {},
197
+
198
+ // CRITICAL: Store sessionId for API key resolution
199
+ sessionId: config.sessionId,
200
+
201
+ // Inter-agent conversation tracking to prevent spam
202
+ interAgentTracking: new Map(), // recipientId -> { lastSent, lastReceived, lastType }
203
+
204
+ // Task Management System for agent-mode autonomous operation
205
+ taskList: {
206
+ tasks: [], // Array of task objects
207
+ lastUpdated: now
208
+ },
209
+
210
+ // Incoming messages tracking (for unprocessed messages)
211
+ incomingMessages: []
212
+ };
213
+
214
+ this.logger.info(`Agent created with routing config`, {
215
+ agentId,
216
+ dynamicModelRouting: agent.dynamicModelRouting,
217
+ platformProvided: agent.platformProvided,
218
+ preferredModel: agent.preferredModel
219
+ });
220
+
221
+ // Initialize model-specific conversation with dual storage structure
222
+ if (config.preferredModel) {
223
+ agent.conversations[config.preferredModel] = {
224
+ // Dual storage for compactization support
225
+ messages: [], // Original messages - never modified
226
+ compactizedMessages: null, // Working copy - null until first compaction
227
+
228
+ // Compactization metadata
229
+ lastCompactization: null, // Timestamp of last compaction
230
+ compactizationCount: 0, // Number of times compacted
231
+ compactizationStrategy: null, // 'summarization', 'truncation', 'aggressive'
232
+ originalTokenCount: 0, // Token count before last compaction
233
+ compactedTokenCount: 0, // Token count after last compaction
234
+
235
+ // Backward compatibility
236
+ tokenCount: 0, // Current effective token count
237
+ lastUpdated: now,
238
+ formatVersion: this._getModelFormatVersion(config.preferredModel)
239
+ };
240
+ }
241
+
242
+ // Add to registry and directory
243
+ this.agents.set(agentId, agent);
244
+ this._updateAgentDirectory(agent);
245
+
246
+ // Persist agent state (use wrapper that resolves agent object from ID)
247
+ await this.persistAgentState(agentId);
248
+
249
+ this.logger.info(`Agent created: ${agentId}`, {
250
+ agentId,
251
+ name: agent.name,
252
+ type: agent.type,
253
+ model: agent.preferredModel
254
+ });
255
+
256
+ return agent;
257
+ }
258
+
259
+ /**
260
+ * Retrieve agent instance by ID
261
+ * @param {string} agentId - Agent identifier
262
+ * @returns {Promise<Object|null>} Agent object or null if not found
263
+ */
264
+ async getAgent(agentId, enrichWithSchedulerStatus = false) {
265
+ const agent = this.agents.get(agentId);
266
+ if (!agent) return null;
267
+
268
+ // Optionally enrich with scheduler status for UI
269
+ if (enrichWithSchedulerStatus && this.scheduler) {
270
+ agent.inScheduler = this.scheduler.isAgentInScheduler(agentId);
271
+ }
272
+
273
+ return agent;
274
+ }
275
+
276
+ /**
277
+ * Update an existing agent's configuration
278
+ * @param {string} agentId - Agent identifier
279
+ * @param {Object} updates - Updates to apply to the agent
280
+ * @returns {Promise<Object>} Updated agent object
281
+ */
282
+ async updateAgent(agentId, updates) {
283
+ const agent = await this.getAgent(agentId);
284
+ if (!agent) {
285
+ throw new Error(`Agent not found: ${agentId}`);
286
+ }
287
+
288
+ this.logger.info(`Updating agent: ${agentId}`, {
289
+ updates,
290
+ currentName: agent.name
291
+ });
292
+
293
+ // Validate directory access configuration if being updated
294
+ if (updates.directoryAccess) {
295
+ const validation = this.directoryAccessManager.validateAccessConfiguration(updates.directoryAccess);
296
+ if (!validation.valid) {
297
+ throw new Error(`Invalid directory access configuration: ${validation.errors.join(', ')}`);
298
+ }
299
+
300
+ this.logger.info(`Directory access validation passed for agent: ${agentId}`, {
301
+ workingDirectory: updates.directoryAccess.workingDirectory,
302
+ readOnlyDirs: updates.directoryAccess.readOnlyDirectories?.length || 0,
303
+ writeEnabledDirs: updates.directoryAccess.writeEnabledDirectories?.length || 0
304
+ });
305
+ }
306
+
307
+ // If originalSystemPrompt is being updated (user edited the raw prompt), store it
308
+ // and use it as the base for enhancement. Otherwise use the existing originalSystemPrompt.
309
+ if (updates.originalSystemPrompt !== undefined) {
310
+ // User explicitly set a new base prompt — store it
311
+ this.logger.info(`Original system prompt updated by user`, {
312
+ agentId,
313
+ oldLength: (agent.originalSystemPrompt || '').length,
314
+ newLength: updates.originalSystemPrompt.length
315
+ });
316
+ }
317
+
318
+ // If capabilities or system prompt are being updated, regenerate the enhanced system prompt
319
+ if ((updates.capabilities || updates.originalSystemPrompt !== undefined) && this.toolsRegistry) {
320
+ try {
321
+ // Priority: new user prompt > existing original prompt > existing system prompt
322
+ const baseSystemPrompt = updates.originalSystemPrompt !== undefined
323
+ ? updates.originalSystemPrompt
324
+ : (agent.originalSystemPrompt || agent.systemPrompt || '');
325
+ const capabilities = updates.capabilities || agent.capabilities || [];
326
+
327
+ const enhancedSystemPrompt = this.toolsRegistry.enhanceSystemPrompt(
328
+ baseSystemPrompt,
329
+ capabilities,
330
+ {
331
+ compact: agent.compactToolDescriptions || false,
332
+ includeExamples: agent.includeToolExamples !== false,
333
+ includeUsageGuidelines: agent.includeUsageGuidelines !== false,
334
+ includeSecurityNotes: agent.includeSecurityNotes !== false
335
+ }
336
+ );
337
+
338
+ updates.systemPrompt = enhancedSystemPrompt;
339
+ // Always keep originalSystemPrompt in sync with what the user wrote
340
+ if (updates.originalSystemPrompt === undefined) {
341
+ updates.originalSystemPrompt = baseSystemPrompt;
342
+ }
343
+
344
+ this.logger.info(`System prompt regenerated with updated capabilities`, {
345
+ agentId,
346
+ oldCapabilities: agent.capabilities,
347
+ newCapabilities: capabilities,
348
+ originalLength: baseSystemPrompt?.length || 0,
349
+ enhancedLength: enhancedSystemPrompt?.length || 0
350
+ });
351
+ } catch (error) {
352
+ this.logger.error(`Failed to regenerate system prompt with updated capabilities`, {
353
+ agentId,
354
+ error: error.message,
355
+ capabilities: updates.capabilities
356
+ });
357
+ // Continue with update even if enhancement fails
358
+ }
359
+ }
360
+
361
+ // Create updated agent object with new values
362
+ const updatedAgent = {
363
+ ...agent,
364
+ ...updates,
365
+ id: agentId, // Ensure ID cannot be changed
366
+ lastModified: new Date().toISOString(),
367
+ lastActivity: new Date().toISOString()
368
+ };
369
+
370
+ // CRITICAL FIX: When preferredModel changes, also update currentModel
371
+ // This ensures the UI immediately reflects the model change
372
+ if (updates.preferredModel && updates.preferredModel !== agent.preferredModel) {
373
+ const oldModel = agent.preferredModel;
374
+ const newModel = updates.preferredModel;
375
+
376
+ updatedAgent.currentModel = newModel;
377
+
378
+ // CRITICAL FIX: Initialize conversation for new model if it doesn't exist
379
+ if (!updatedAgent.conversations[newModel]) {
380
+ updatedAgent.conversations[newModel] = this._createEmptyConversation(newModel);
381
+ this.logger.info(`Created conversation for new model: ${newModel}`, { agentId });
382
+ }
383
+
384
+ // Copy conversation history from old model to new model
385
+ // This preserves context when switching models
386
+ if (oldModel && updatedAgent.conversations[oldModel]) {
387
+ const oldConversation = updatedAgent.conversations[oldModel];
388
+ const newConversation = updatedAgent.conversations[newModel];
389
+
390
+ // Copy messages if new conversation is empty
391
+ if (newConversation.messages.length === 0 && oldConversation.messages.length > 0) {
392
+ // Copy original messages
393
+ newConversation.messages = [...oldConversation.messages];
394
+
395
+ // Copy compacted messages if they exist
396
+ if (oldConversation.compactizedMessages) {
397
+ newConversation.compactizedMessages = [...oldConversation.compactizedMessages];
398
+ newConversation.lastCompactization = oldConversation.lastCompactization;
399
+ newConversation.compactizationCount = oldConversation.compactizationCount;
400
+ newConversation.compactizationStrategy = oldConversation.compactizationStrategy;
401
+ newConversation.originalTokenCount = oldConversation.originalTokenCount;
402
+ newConversation.compactedTokenCount = oldConversation.compactedTokenCount;
403
+ }
404
+
405
+ newConversation.lastUpdated = new Date().toISOString();
406
+
407
+ this.logger.info(`Copied conversation history from ${oldModel} to ${newModel}`, {
408
+ agentId,
409
+ messageCount: newConversation.messages.length,
410
+ hasCompacted: !!newConversation.compactizedMessages
411
+ });
412
+ }
413
+ }
414
+
415
+ this.logger.info(`Model changed via UI - updating both preferredModel and currentModel`, {
416
+ agentId,
417
+ oldModel,
418
+ newModel,
419
+ conversationCopied: oldModel && updatedAgent.conversations[oldModel]?.messages.length > 0
420
+ });
421
+ }
422
+
423
+ // Update agent in registry
424
+ this.agents.set(agentId, updatedAgent);
425
+
426
+ // Log the actual update for debugging
427
+ this.logger.info(`Agent updated in registry with mode: ${updatedAgent.mode}`, {
428
+ agentId,
429
+ beforeMode: agent.mode,
430
+ afterMode: updatedAgent.mode,
431
+ allUpdates: Object.keys(updates)
432
+ });
433
+
434
+ // Update agent directory
435
+ this._updateAgentDirectory(updatedAgent);
436
+
437
+ // Persist the updated agent state
438
+ await this.stateManager.persistAgentState(updatedAgent);
439
+
440
+ // CRITICAL: If agent was switched to AGENT mode, add it to scheduler
441
+ if (updates.mode === AGENT_MODES.AGENT && this.scheduler) {
442
+ // CRITICAL FIX: Use the session ID from updates first, then agent's sessionId
443
+ // Register session with scheduler for API key resolution
444
+ // NOTE: The scheduler now uses AgentActivityService to determine which agents
445
+ // should be active, so we just register the session here
446
+ const sessionId = updates.sessionId || updatedAgent.sessionId;
447
+
448
+ if (!sessionId) {
449
+ this.logger.warn(`Agent ${agentId} switching to AGENT mode but has no sessionId - this will cause API key resolution issues`);
450
+ }
451
+
452
+ this.logger.info(`Registering agent session with scheduler (switched to AGENT mode): ${agentId}`, {
453
+ agentName: updatedAgent.name,
454
+ sessionId: sessionId,
455
+ hasSessionId: !!sessionId
456
+ });
457
+
458
+ await this.scheduler.addAgent(agentId, {
459
+ sessionId: sessionId,
460
+ triggeredBy: 'mode-change-to-agent'
461
+ });
462
+ }
463
+
464
+ // If agent was switched from AGENT to CHAT mode, clean up session tracking
465
+ // NOTE: The agent will automatically become inactive in the next scheduler cycle
466
+ // based on AgentActivityService.shouldAgentBeActive() returning false
467
+ if (agent.mode === AGENT_MODES.AGENT && updates.mode === AGENT_MODES.CHAT && this.scheduler) {
468
+ this.logger.info(`Agent mode changed to CHAT - will become inactive: ${agentId}`);
469
+ this.scheduler.removeAgent(agentId, 'mode-change-to-chat');
470
+ }
471
+
472
+ this.logger.info(`Agent updated successfully: ${agentId}`, {
473
+ newName: updatedAgent.name,
474
+ changes: Object.keys(updates)
475
+ });
476
+
477
+ return updatedAgent;
478
+ }
479
+
480
+ /**
481
+ * Agent notification from Message Processor for inter-agent communication
482
+ * @param {string} agentId - Target agent ID
483
+ * @param {Object} message - Message object with agent redirect
484
+ * @returns {Promise<boolean>} Success status
485
+ */
486
+ async notifyAgent(agentId, message) {
487
+ const agent = await this.getAgent(agentId);
488
+ if (!agent) {
489
+ this.logger.warn(`Agent notification failed - agent not found: ${agentId}`);
490
+ return false;
491
+ }
492
+
493
+ // Check if agent is paused
494
+ if (this._isAgentPaused(agent)) {
495
+ this.logger.info(`Agent notification queued - agent is paused: ${agentId}`);
496
+ this._queueNotification(agentId, message);
497
+ return true;
498
+ }
499
+
500
+ // Add notification to agent's conversation
501
+ const notificationMessage = {
502
+ id: `msg-${Date.now()}`,
503
+ conversationId: message.conversationId,
504
+ agentId: message.from, // sender agent ID
505
+ content: message.content,
506
+ role: MESSAGE_ROLES.SYSTEM,
507
+ timestamp: new Date().toISOString(),
508
+ type: MESSAGE_TYPES.AGENT_NOTIFICATION,
509
+ fromAgent: message.from,
510
+ context: message.context,
511
+ urgent: message.urgent || false,
512
+ requiresResponse: message.requiresResponse || false
513
+ };
514
+
515
+ // Add to full conversation
516
+ agent.conversations.full.messages.push(notificationMessage);
517
+ agent.conversations.full.lastUpdated = new Date().toISOString();
518
+
519
+ // Add to current model conversation
520
+ if (agent.currentModel && agent.conversations[agent.currentModel]) {
521
+ const formattedMessage = this._formatMessageForModel(notificationMessage, agent.currentModel);
522
+ agent.conversations[agent.currentModel].messages.push(formattedMessage);
523
+ agent.conversations[agent.currentModel].lastUpdated = new Date().toISOString();
524
+ }
525
+
526
+ // Update agent activity
527
+ agent.lastActivity = new Date().toISOString();
528
+ await this.persistAgentState(agentId);
529
+
530
+ this.logger.info(`Agent notified: ${agentId}`, {
531
+ fromAgent: message.from,
532
+ urgent: message.urgent,
533
+ requiresResponse: message.requiresResponse
534
+ });
535
+
536
+ return true;
537
+ }
538
+
539
+ /**
540
+ * Get all agents (returns full agent objects)
541
+ * @returns {Promise<Array>} List of all agent objects
542
+ */
543
+ async getAllAgents() {
544
+ const agents = Array.from(this.agents.values());
545
+
546
+ // Update pause status for all agents
547
+ for (const agent of agents) {
548
+ this._updateAgentPauseStatus(agent);
549
+ }
550
+
551
+ return agents;
552
+ }
553
+
554
+ /**
555
+ * List all active agents with their current status
556
+ * @returns {Promise<Array>} Array of agent objects
557
+ */
558
+ async listActiveAgents() {
559
+ const agents = Array.from(this.agents.values());
560
+
561
+ // Update pause status for all agents
562
+ for (const agent of agents) {
563
+ this._updateAgentPauseStatus(agent);
564
+ }
565
+
566
+ return agents.map(agent => ({
567
+ id: agent.id,
568
+ name: agent.name,
569
+ type: agent.type,
570
+ status: agent.status,
571
+ mode: agent.mode,
572
+ systemPrompt: agent.systemPrompt,
573
+ originalSystemPrompt: agent.originalSystemPrompt,
574
+ preferredModel: agent.preferredModel,
575
+ currentModel: agent.currentModel,
576
+ dynamicModelRouting: agent.dynamicModelRouting,
577
+ routingStrategy: agent.routingStrategy || '',
578
+ skills: agent.skills || [],
579
+ platformProvided: agent.platformProvided,
580
+ capabilities: agent.capabilities,
581
+ directoryAccess: agent.directoryAccess,
582
+ lastActivity: agent.lastActivity,
583
+ isPaused: this._isAgentPaused(agent),
584
+ pausedUntil: agent.pausedUntil,
585
+ messageCount: agent.conversations.full.messages.length,
586
+ createdAt: agent.createdAt,
587
+ // First user message snippet for card preview (2 lines max)
588
+ firstUserMessage: this._getFirstUserMessageSnippet(agent)
589
+ }));
590
+ }
591
+
592
+ /**
593
+ * Persist agent state to storage
594
+ * @param {string} agentId - Agent identifier
595
+ * @returns {Promise<void>}
596
+ */
597
+ async persistAgentState(agentId) {
598
+ const agent = await this.getAgent(agentId);
599
+ if (!agent) {
600
+ throw new Error(`Agent not found: ${agentId}`);
601
+ }
602
+
603
+ await this.stateManager.persistAgentState(agent);
604
+ }
605
+
606
+ /**
607
+ * Resume agent from persisted state
608
+ * @param {Object} agentData - Persisted agent data
609
+ * @returns {Promise<Object>} Restored agent object
610
+ */
611
+ async resumeAgent(agentData) {
612
+ const agent = {
613
+ ...agentData,
614
+ status: agentData.status === 'paused' && this._isPauseExpired(agentData) ? 'active' : agentData.status
615
+ };
616
+
617
+ // RECOVERY: If agent was paused awaiting user input (e.g., credentials),
618
+ // the promise is now lost due to server/UI restart. Resume the agent.
619
+ if (agent.awaitingUserInput) {
620
+ this.logger.warn(`Agent ${agent.id} was awaiting user input (${agent.awaitingUserInput.type}) - recovering from interrupted state`, {
621
+ inputType: agent.awaitingUserInput.type,
622
+ siteId: agent.awaitingUserInput.siteId,
623
+ startedAt: agent.awaitingUserInput.startedAt
624
+ });
625
+
626
+ // Clear the awaiting flag and resume agent
627
+ delete agent.awaitingUserInput;
628
+ agent.status = AGENT_STATUS.ACTIVE;
629
+
630
+ // Add a system message to the agent's queue so it knows what happened
631
+ if (!agent.messageQueues) {
632
+ agent.messageQueues = { toolResults: [], interAgentMessages: [], userMessages: [] };
633
+ }
634
+ agent.messageQueues.toolResults.push({
635
+ id: `recovery-${Date.now()}`,
636
+ toolId: 'system-recovery',
637
+ status: 'info',
638
+ result: {
639
+ message: 'Agent was waiting for user input (credentials) when the session was interrupted. The credential request has been cancelled. Please retry the authentication if needed.',
640
+ recoveredFrom: 'awaitingUserInput',
641
+ timestamp: new Date().toISOString()
642
+ },
643
+ timestamp: new Date().toISOString()
644
+ });
645
+ }
646
+
647
+ // Validate conversations structure
648
+ if (!agent.conversations || !agent.conversations.full) {
649
+ agent.conversations = {
650
+ full: {
651
+ messages: [],
652
+ lastUpdated: new Date().toISOString()
653
+ }
654
+ };
655
+ }
656
+
657
+ // CRITICAL: Restore interAgentTracking as a Map (it comes as plain object from JSON)
658
+ if (!agent.interAgentTracking || !(agent.interAgentTracking instanceof Map)) {
659
+ // Convert plain object to Map, or create empty Map
660
+ if (agent.interAgentTracking && typeof agent.interAgentTracking === 'object') {
661
+ agent.interAgentTracking = new Map(Object.entries(agent.interAgentTracking));
662
+ } else {
663
+ agent.interAgentTracking = new Map();
664
+ }
665
+ }
666
+
667
+ // Add to registry and directory
668
+ this.agents.set(agent.id, agent);
669
+ this._updateAgentDirectory(agent);
670
+
671
+ // CRITICAL: Migrate conversation structure to ensure new fields exist
672
+ // This handles agents persisted before the originalMessageCountAtCompaction fix
673
+ await this.migrateConversationStructure(agent.id);
674
+
675
+ // Process any queued notifications
676
+ await this._processQueuedNotifications(agent.id);
677
+
678
+ this.logger.info(`Agent resumed: ${agent.id}`, {
679
+ name: agent.name,
680
+ status: agent.status,
681
+ messageCount: agent.conversations.full.messages.length
682
+ });
683
+
684
+ return agent;
685
+ }
686
+
687
+ /**
688
+ * Pause agent for specified duration
689
+ * @param {string} agentId - Agent identifier
690
+ * @param {number|Date} duration - Pause duration in seconds or Date object
691
+ * @param {string} reason - Reason for pause
692
+ * @returns {Promise<Object>} Pause confirmation
693
+ */
694
+ async pauseAgent(agentId, duration, reason = 'Agent pause requested') {
695
+ const agent = await this.getAgent(agentId);
696
+ if (!agent) {
697
+ throw new Error(`Agent not found: ${agentId}`);
698
+ }
699
+
700
+ let pauseUntil;
701
+ if (duration instanceof Date) {
702
+ pauseUntil = duration;
703
+ } else {
704
+ // Duration in seconds
705
+ const maxPauseDuration = this.config.system?.maxPauseDuration || 300;
706
+ const pauseSeconds = Math.min(duration, maxPauseDuration);
707
+ pauseUntil = new Date(Date.now() + pauseSeconds * 1000);
708
+ }
709
+
710
+ agent.status = AGENT_STATUS.PAUSED;
711
+ agent.pausedUntil = pauseUntil.toISOString();
712
+ agent.lastActivity = new Date().toISOString();
713
+
714
+ // Add to paused agents tracking
715
+ this.pausedAgents.set(agentId, {
716
+ agentId,
717
+ pausedAt: new Date().toISOString(),
718
+ pausedUntil: pauseUntil.toISOString(),
719
+ reason,
720
+ originalStatus: AGENT_STATUS.ACTIVE
721
+ });
722
+
723
+ await this.persistAgentState(agentId);
724
+
725
+ this.logger.info(`Agent paused: ${agentId}`, {
726
+ pausedUntil: pauseUntil.toISOString(),
727
+ reason,
728
+ durationSeconds: Math.round((pauseUntil.getTime() - Date.now()) / 1000)
729
+ });
730
+
731
+ return {
732
+ success: true,
733
+ agentId,
734
+ pausedUntil: pauseUntil.toISOString(),
735
+ reason,
736
+ message: `Agent paused until ${pauseUntil.toISOString()}`
737
+ };
738
+ }
739
+
740
+ /**
741
+ * Resume paused agent
742
+ * @param {string} agentId - Agent identifier
743
+ * @returns {Promise<Object>} Resume confirmation
744
+ */
745
+ async resumeAgent(agentId) {
746
+ const agent = await this.getAgent(agentId);
747
+ if (!agent) {
748
+ throw new Error(`Agent not found: ${agentId}`);
749
+ }
750
+
751
+ if (agent.status !== AGENT_STATUS.PAUSED) {
752
+ return {
753
+ success: true,
754
+ message: `Agent ${agentId} is not paused`
755
+ };
756
+ }
757
+
758
+ agent.status = AGENT_STATUS.ACTIVE;
759
+ agent.pausedUntil = null;
760
+ agent.lastActivity = new Date().toISOString();
761
+
762
+ // Remove from paused agents tracking
763
+ this.pausedAgents.delete(agentId);
764
+
765
+ // Process any queued notifications
766
+ await this._processQueuedNotifications(agentId);
767
+
768
+ await this.persistAgentState(agentId);
769
+
770
+ this.logger.info(`Agent resumed: ${agentId}`);
771
+
772
+ return {
773
+ success: true,
774
+ agentId,
775
+ message: `Agent ${agentId} resumed successfully`
776
+ };
777
+ }
778
+
779
+ /**
780
+ * Restore agent from saved state
781
+ * @param {Object} agentState - Saved agent state
782
+ * @returns {Promise<Object>} Restored agent
783
+ */
784
+ async restoreAgent(agentState) {
785
+ return await this.resumeAgent(agentState);
786
+ }
787
+
788
+ /**
789
+ * Get agent discovery directory
790
+ * @returns {Array} Array of agent info for discovery
791
+ */
792
+ getAgentDirectory() {
793
+ return Array.from(this.agentDirectory.values());
794
+ }
795
+
796
+ /**
797
+ * List all active agents
798
+ * @returns {Array} Array of active agents
799
+ */
800
+
801
+ /**
802
+ * Delete an agent and clean up its resources
803
+ * @param {string} agentId - Agent identifier
804
+ * @returns {Promise<Object>} Deletion result
805
+ */
806
+ async deleteAgent(agentId) {
807
+ const agent = await this.getAgent(agentId);
808
+ if (!agent) {
809
+ throw new Error(`Agent not found: ${agentId}`);
810
+ }
811
+
812
+ // Clean up file attachments with reference counting
813
+ if (this.fileAttachmentService) {
814
+ try {
815
+ await this.fileAttachmentService.deleteAgentAttachments(agentId);
816
+ this.logger.info(`File attachments cleaned up for agent: ${agentId}`);
817
+ } catch (error) {
818
+ this.logger.warn(`Failed to clean up file attachments for agent: ${error.message}`, { agentId });
819
+ // Continue with agent deletion even if attachment cleanup fails
820
+ }
821
+ }
822
+
823
+ // Clean up visual editor instance
824
+ try {
825
+ const visualEditorBridge = getVisualEditorBridge();
826
+ if (visualEditorBridge.hasInstance(agentId)) {
827
+ await visualEditorBridge.stopInstance(agentId);
828
+ this.logger.info(`Visual editor instance cleaned up for agent: ${agentId}`);
829
+ }
830
+ } catch (error) {
831
+ this.logger.warn(`Failed to clean up visual editor for agent: ${error.message}`, { agentId });
832
+ // Continue with agent deletion even if visual editor cleanup fails
833
+ }
834
+
835
+ // Clean up agent resources
836
+ this.agents.delete(agentId);
837
+ this.agentDirectory.delete(agentId);
838
+ this.pausedAgents.delete(agentId);
839
+ this.notificationQueue.delete(agentId);
840
+
841
+ // Clean up persistent state
842
+ try {
843
+ await this.stateManager.deleteAgentState(agentId);
844
+ } catch (error) {
845
+ this.logger.warn(`Failed to delete agent persistent state: ${error.message}`, { agentId });
846
+ }
847
+
848
+ this.logger.info(`Agent deleted: ${agentId}`, {
849
+ agentName: agent.name,
850
+ totalAgents: this.agents.size
851
+ });
852
+
853
+ return {
854
+ success: true,
855
+ agentId,
856
+ remainingAgents: this.agents.size
857
+ };
858
+ }
859
+
860
+ /**
861
+ * Unload an agent from server memory without deleting persistent files
862
+ * Agent can be reloaded later using the Load Agent feature
863
+ * @param {string} agentId - Agent identifier
864
+ * @returns {Promise<Object>} Unload result
865
+ */
866
+ async unloadAgent(agentId) {
867
+ const agent = await this.getAgent(agentId);
868
+ if (!agent) {
869
+ throw new Error(`Agent not found: ${agentId}`);
870
+ }
871
+
872
+ const agentName = agent.name;
873
+
874
+ // Persist current state before unloading (so it can be reloaded later)
875
+ try {
876
+ await this.persistAgentState(agentId);
877
+ this.logger.info(`Agent state persisted before unload: ${agentId}`);
878
+ } catch (error) {
879
+ this.logger.warn(`Failed to persist agent state before unload: ${error.message}`, { agentId });
880
+ }
881
+
882
+ // Clean up visual editor instance
883
+ try {
884
+ const visualEditorBridge = getVisualEditorBridge();
885
+ if (visualEditorBridge.hasInstance(agentId)) {
886
+ await visualEditorBridge.stopInstance(agentId);
887
+ this.logger.info(`Visual editor instance cleaned up for unloaded agent: ${agentId}`);
888
+ }
889
+ } catch (error) {
890
+ this.logger.warn(`Failed to clean up visual editor for unloaded agent: ${error.message}`, { agentId });
891
+ }
892
+
893
+ // Remove from memory only (keep persistent files)
894
+ this.agents.delete(agentId);
895
+ this.agentDirectory.delete(agentId);
896
+ this.pausedAgents.delete(agentId);
897
+ this.notificationQueue.delete(agentId);
898
+
899
+ // Remove from scheduler if present
900
+ if (this.scheduler) {
901
+ this.scheduler.removeAgent(agentId, 'unloaded');
902
+ }
903
+
904
+ this.logger.info(`Agent unloaded from memory: ${agentId}`, {
905
+ agentName,
906
+ totalAgents: this.agents.size,
907
+ note: 'Persistent files preserved for future reload'
908
+ });
909
+
910
+ return {
911
+ success: true,
912
+ agentId,
913
+ agentName,
914
+ remainingAgents: this.agents.size,
915
+ message: `Agent "${agentName}" unloaded. Use Load Agent to reload it.`
916
+ };
917
+ }
918
+
919
+ /**
920
+ * Clear all conversation history for an agent
921
+ * Resets the agent to a fresh state while keeping configuration
922
+ * @param {string} agentId - Agent identifier
923
+ * @returns {Promise<Object>} Clear result
924
+ */
925
+ async clearConversation(agentId) {
926
+ const agent = await this.getAgent(agentId);
927
+ if (!agent) {
928
+ throw new Error(`Agent not found: ${agentId}`);
929
+ }
930
+
931
+ const previousMessageCount = agent.conversations?.full?.messages?.length || 0;
932
+
933
+ // Reset full conversation
934
+ agent.conversations.full = {
935
+ messages: [],
936
+ lastUpdated: new Date().toISOString()
937
+ };
938
+
939
+ // Reset model-specific conversations
940
+ for (const key of Object.keys(agent.conversations)) {
941
+ if (key !== 'full') {
942
+ agent.conversations[key] = {
943
+ messages: [],
944
+ compactizedMessages: null,
945
+ lastUpdated: new Date().toISOString(),
946
+ compactionState: {
947
+ isCompacted: false,
948
+ lastCompactionTime: null,
949
+ originalMessageCount: 0,
950
+ compactedMessageCount: 0
951
+ }
952
+ };
953
+ }
954
+ }
955
+
956
+ // Clear message queues
957
+ if (agent.messageQueues) {
958
+ agent.messageQueues = {
959
+ toolResults: [],
960
+ interAgentMessages: [],
961
+ userMessages: []
962
+ };
963
+ }
964
+
965
+ // Clear task list
966
+ if (agent.taskList) {
967
+ agent.taskList = {
968
+ tasks: [],
969
+ lastUpdated: new Date().toISOString()
970
+ };
971
+ }
972
+
973
+ agent.currentTask = null;
974
+ agent.taskStartTime = null;
975
+ agent.iterationCount = 0;
976
+
977
+ // Persist the cleared state
978
+ await this.persistAgentState(agentId);
979
+
980
+ this.logger.info(`Conversation cleared for agent: ${agentId}`, {
981
+ agentName: agent.name,
982
+ previousMessageCount
983
+ });
984
+
985
+ return {
986
+ success: true,
987
+ agentId,
988
+ previousMessageCount,
989
+ message: `Cleared ${previousMessageCount} messages`
990
+ };
991
+ }
992
+
993
+ /**
994
+ * Generate unique agent ID
995
+ * @private
996
+ */
997
+ _generateAgentId(name) {
998
+ const sanitizedName = name.toLowerCase().replace(/[^a-z0-9]/g, '-');
999
+ const timestamp = Date.now();
1000
+ return `agent-${sanitizedName}-${timestamp}`;
1001
+ }
1002
+
1003
+ /**
1004
+ * Update agent directory for discovery
1005
+ * @private
1006
+ */
1007
+ _updateAgentDirectory(agent) {
1008
+ this.agentDirectory.set(agent.id, {
1009
+ id: agent.id,
1010
+ name: agent.name,
1011
+ type: agent.type,
1012
+ capabilities: agent.capabilities,
1013
+ status: agent.status,
1014
+ description: this._generateAgentDescription(agent)
1015
+ });
1016
+ }
1017
+
1018
+ /**
1019
+ * Generate agent description for directory
1020
+ * @private
1021
+ */
1022
+ _generateAgentDescription(agent) {
1023
+ let description = `${agent.name} (${agent.type})`;
1024
+
1025
+ if (agent.capabilities.length > 0) {
1026
+ description += ` - Capabilities: ${agent.capabilities.join(', ')}`;
1027
+ }
1028
+
1029
+ return description;
1030
+ }
1031
+
1032
+ /**
1033
+ * Check if agent is currently paused
1034
+ * @private
1035
+ */
1036
+ _isAgentPaused(agent) {
1037
+ if (agent.status !== AGENT_STATUS.PAUSED || !agent.pausedUntil) {
1038
+ return false;
1039
+ }
1040
+
1041
+ return new Date() < new Date(agent.pausedUntil);
1042
+ }
1043
+
1044
+ /**
1045
+ * Get first user message snippet for card preview
1046
+ * @private
1047
+ */
1048
+ _getFirstUserMessageSnippet(agent) {
1049
+ const messages = agent.conversations?.full?.messages;
1050
+ if (!messages || messages.length === 0) return null;
1051
+
1052
+ // Find first user message — include consolidated-input since that's how
1053
+ // user messages are stored after queue processing. Skip task-boundary.
1054
+ const firstUser = messages.find(m =>
1055
+ m.role === 'user' && m.content &&
1056
+ m.type !== 'task-boundary'
1057
+ );
1058
+ if (!firstUser) return null;
1059
+
1060
+ // Handle both string and array content formats
1061
+ const text = typeof firstUser.content === 'string'
1062
+ ? firstUser.content
1063
+ : Array.isArray(firstUser.content)
1064
+ ? firstUser.content.filter(b => b.type === 'text').map(b => b.text).join('\n')
1065
+ : null;
1066
+ if (!text) return null;
1067
+
1068
+ // Take first 2 non-empty lines, cap at 120 chars
1069
+ const lines = text.split('\n').filter(l => l.trim());
1070
+ const snippet = lines.slice(0, 2).join('\n');
1071
+ return snippet.length > 120 ? snippet.slice(0, 117) + '...' : snippet;
1072
+ }
1073
+
1074
+ /**
1075
+ * Check if pause duration has expired
1076
+ * @private
1077
+ */
1078
+ _isPauseExpired(agent) {
1079
+ if (!agent.pausedUntil) return true;
1080
+ return new Date() >= new Date(agent.pausedUntil);
1081
+ }
1082
+
1083
+ /**
1084
+ * Update agent pause status
1085
+ * @private
1086
+ */
1087
+ _updateAgentPauseStatus(agent) {
1088
+ if (agent.status === AGENT_STATUS.PAUSED && this._isPauseExpired(agent)) {
1089
+ agent.status = AGENT_STATUS.ACTIVE;
1090
+ agent.pausedUntil = null;
1091
+ this.pausedAgents.delete(agent.id);
1092
+ }
1093
+ }
1094
+
1095
+ /**
1096
+ * Queue notification for paused agent
1097
+ * @private
1098
+ */
1099
+ _queueNotification(agentId, message) {
1100
+ if (!this.notificationQueue.has(agentId)) {
1101
+ this.notificationQueue.set(agentId, []);
1102
+ }
1103
+
1104
+ this.notificationQueue.get(agentId).push({
1105
+ ...message,
1106
+ queuedAt: new Date().toISOString()
1107
+ });
1108
+ }
1109
+
1110
+ /**
1111
+ * Process queued notifications for agent
1112
+ * @private
1113
+ */
1114
+ async _processQueuedNotifications(agentId) {
1115
+ const notifications = this.notificationQueue.get(agentId);
1116
+ if (!notifications || notifications.length === 0) {
1117
+ return;
1118
+ }
1119
+
1120
+ this.logger.info(`Processing ${notifications.length} queued notifications for agent: ${agentId}`);
1121
+
1122
+ for (const notification of notifications) {
1123
+ await this.notifyAgent(agentId, notification);
1124
+ }
1125
+
1126
+ // Clear queue
1127
+ this.notificationQueue.delete(agentId);
1128
+ }
1129
+
1130
+ /**
1131
+ * Format message for specific model
1132
+ * @private
1133
+ */
1134
+ _formatMessageForModel(message, targetModel) {
1135
+ // This would be implemented with model-specific formatting logic
1136
+ // For now, return the message as-is
1137
+ return { ...message };
1138
+ }
1139
+
1140
+ /**
1141
+ * Get model format version
1142
+ * @private
1143
+ */
1144
+ _getModelFormatVersion(model) {
1145
+ return MODEL_FORMAT_VERSIONS[model] || MODEL_FORMAT_VERSIONS.DEFAULT;
1146
+ }
1147
+
1148
+ /**
1149
+ * Refresh tool descriptions for an existing agent
1150
+ * @param {string} agentId - Agent identifier
1151
+ * @param {Object} options - Refresh options
1152
+ * @returns {Promise<boolean>} Success status
1153
+ */
1154
+ async refreshAgentToolDescriptions(agentId, options = {}) {
1155
+ const agent = await this.getAgent(agentId);
1156
+ if (!agent || !this.toolsRegistry) {
1157
+ return false;
1158
+ }
1159
+
1160
+ try {
1161
+ // Use original prompt if available, otherwise current prompt
1162
+ const basePrompt = agent.originalSystemPrompt || agent.systemPrompt;
1163
+
1164
+ // Enhance with current tool capabilities
1165
+ const enhancedPrompt = this.toolsRegistry.enhanceSystemPrompt(
1166
+ basePrompt,
1167
+ agent.capabilities,
1168
+ {
1169
+ compact: options.compact || false,
1170
+ includeExamples: options.includeExamples !== false,
1171
+ includeUsageGuidelines: options.includeUsageGuidelines !== false,
1172
+ includeSecurityNotes: options.includeSecurityNotes !== false
1173
+ }
1174
+ );
1175
+
1176
+ // Update agent's system prompt
1177
+ agent.systemPrompt = enhancedPrompt;
1178
+ agent.lastActivity = new Date().toISOString();
1179
+
1180
+ // Persist changes
1181
+ await this.persistAgentState(agentId);
1182
+
1183
+ this.logger?.info(`Agent tool descriptions refreshed: ${agentId}`, {
1184
+ capabilities: agent.capabilities,
1185
+ promptLength: enhancedPrompt.length
1186
+ });
1187
+
1188
+ return true;
1189
+
1190
+ } catch (error) {
1191
+ this.logger?.error(`Failed to refresh tool descriptions for agent: ${agentId}`, {
1192
+ error: error.message
1193
+ });
1194
+ return false;
1195
+ }
1196
+ }
1197
+
1198
+ /**
1199
+ * Set or update tools registry for the agent pool
1200
+ * @param {ToolsRegistry} toolsRegistry - Tools registry instance
1201
+ */
1202
+ setToolsRegistry(toolsRegistry) {
1203
+ this.toolsRegistry = toolsRegistry;
1204
+
1205
+ this.logger?.info('Tools registry updated for agent pool', {
1206
+ hasRegistry: !!toolsRegistry
1207
+ });
1208
+ }
1209
+
1210
+ /**
1211
+ * Bulk refresh tool descriptions for all agents
1212
+ * @param {Object} options - Refresh options
1213
+ * @returns {Promise<Object>} Results summary
1214
+ */
1215
+ async bulkRefreshToolDescriptions(options = {}) {
1216
+ const results = {
1217
+ total: this.agents.size,
1218
+ successful: 0,
1219
+ failed: 0,
1220
+ skipped: 0
1221
+ };
1222
+
1223
+ for (const [agentId, agent] of this.agents.entries()) {
1224
+ if (!agent.capabilities || agent.capabilities.length === 0) {
1225
+ results.skipped++;
1226
+ continue;
1227
+ }
1228
+
1229
+ const success = await this.refreshAgentToolDescriptions(agentId, options);
1230
+ if (success) {
1231
+ results.successful++;
1232
+ } else {
1233
+ results.failed++;
1234
+ }
1235
+ }
1236
+
1237
+ this.logger?.info('Bulk tool descriptions refresh completed', results);
1238
+ return results;
1239
+ }
1240
+
1241
+ /**
1242
+ * Set MessageProcessor reference for triggering responses
1243
+ * @param {MessageProcessor} messageProcessor - MessageProcessor instance
1244
+ */
1245
+ setMessageProcessor(messageProcessor) {
1246
+ this.messageProcessor = messageProcessor;
1247
+ }
1248
+
1249
+ /**
1250
+ * Set AgentScheduler reference for managing agent modes
1251
+ * @param {AgentScheduler} scheduler - AgentScheduler instance
1252
+ */
1253
+ setScheduler(scheduler) {
1254
+ this.scheduler = scheduler;
1255
+
1256
+ this.logger?.info('AgentScheduler reference set for agent pool', {
1257
+ hasScheduler: !!scheduler
1258
+ });
1259
+ }
1260
+
1261
+ /**
1262
+ * Set FileAttachmentService reference for cleaning up attachments
1263
+ * @param {FileAttachmentService} fileAttachmentService - FileAttachmentService instance
1264
+ */
1265
+ setFileAttachmentService(fileAttachmentService) {
1266
+ this.fileAttachmentService = fileAttachmentService;
1267
+
1268
+ this.logger?.info('FileAttachmentService reference set for agent pool', {
1269
+ hasService: !!fileAttachmentService
1270
+ });
1271
+ }
1272
+
1273
+ // OLD INTER-AGENT MESSAGE QUEUE SYSTEM REMOVED
1274
+ // Now using the new messageQueues system with AgentScheduler
1275
+ // Inter-agent messages are queued via addInterAgentMessage() method
1276
+
1277
+ /**
1278
+ * Add message to agent's user message queue
1279
+ * @param {string} agentId - Agent ID
1280
+ * @param {Object} message - User message to queue
1281
+ * @returns {Promise<void>}
1282
+ */
1283
+ async addUserMessage(agentId, message) {
1284
+ const agent = await this.getAgent(agentId);
1285
+ if (!agent) {
1286
+ throw new Error(`Agent not found: ${agentId}`);
1287
+ }
1288
+
1289
+ const queuedMessage = {
1290
+ ...message,
1291
+ id: message.id || `user-msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
1292
+ queuedAt: new Date().toISOString(),
1293
+ timestamp: message.timestamp || new Date().toISOString()
1294
+ };
1295
+
1296
+ agent.messageQueues.userMessages.push(queuedMessage);
1297
+
1298
+ // Auto-create a task for AGENT mode agents so the scheduler picks them up.
1299
+ // Scheduling condition is purely task-based: has pending tasks AND in agent mode.
1300
+ if (agent.mode === AGENT_MODES.AGENT) {
1301
+ this._autoCreateTaskForMessage(agent, queuedMessage, 'user', 'high');
1302
+ }
1303
+
1304
+ await this.persistAgentState(agentId);
1305
+
1306
+ this.logger.info(`User message queued for agent: ${agentId}`, {
1307
+ messageId: queuedMessage.id,
1308
+ queueSize: agent.messageQueues.userMessages.length
1309
+ });
1310
+ }
1311
+
1312
+ /**
1313
+ * Add message to agent's inter-agent message queue
1314
+ * @param {string} agentId - Agent ID
1315
+ * @param {Object} message - Inter-agent message to queue
1316
+ * @returns {Promise<void>}
1317
+ */
1318
+ async addInterAgentMessage(agentId, message) {
1319
+ const agent = await this.getAgent(agentId);
1320
+ if (!agent) {
1321
+ throw new Error(`Agent not found: ${agentId}`);
1322
+ }
1323
+
1324
+ const queuedMessage = {
1325
+ ...message,
1326
+ id: message.id || message.messageId || `inter-agent-msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
1327
+ queuedAt: new Date().toISOString(),
1328
+ timestamp: message.timestamp || new Date().toISOString()
1329
+ };
1330
+
1331
+ agent.messageQueues.interAgentMessages.push(queuedMessage);
1332
+
1333
+ // Auto-create a task for AGENT mode agents so the scheduler picks them up.
1334
+ if (agent.mode === AGENT_MODES.AGENT) {
1335
+ const senderName = message.senderName || message.sender || 'Unknown Agent';
1336
+ this._autoCreateTaskForMessage(agent, queuedMessage, `inter-agent from ${senderName}`, 'medium');
1337
+ }
1338
+
1339
+ await this.persistAgentState(agentId);
1340
+
1341
+ this.logger.info(`Inter-agent message queued for agent: ${agentId}`, {
1342
+ messageId: queuedMessage.id,
1343
+ sender: message.sender || message.senderName,
1344
+ queueSize: agent.messageQueues.interAgentMessages.length
1345
+ });
1346
+ }
1347
+
1348
+ /**
1349
+ * Add tool result to agent's tool results queue
1350
+ * @param {string} agentId - Agent ID
1351
+ * @param {Object} toolResult - Tool result to queue
1352
+ * @returns {Promise<void>}
1353
+ */
1354
+ async addToolResult(agentId, toolResult) {
1355
+ const agent = await this.getAgent(agentId);
1356
+ if (!agent) {
1357
+ throw new Error(`Agent not found: ${agentId}`);
1358
+ }
1359
+
1360
+ const queuedResult = {
1361
+ ...toolResult,
1362
+ id: toolResult.id || `tool-result-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
1363
+ queuedAt: new Date().toISOString(),
1364
+ timestamp: toolResult.timestamp || new Date().toISOString()
1365
+ };
1366
+
1367
+ agent.messageQueues.toolResults.push(queuedResult);
1368
+ await this.persistAgentState(agentId);
1369
+
1370
+ this.logger.debug(`Tool result queued for agent: ${agentId}`, {
1371
+ toolId: toolResult.toolId,
1372
+ status: toolResult.status,
1373
+ queueSize: agent.messageQueues.toolResults.length
1374
+ });
1375
+ }
1376
+
1377
+ /**
1378
+ * Auto-create a task from an incoming message for AGENT mode agents.
1379
+ * This ensures the scheduler (which uses pending tasks as the sole activation
1380
+ * condition for AGENT mode) picks up the agent for processing.
1381
+ * @param {Object} agent - Agent object
1382
+ * @param {Object} message - The queued message
1383
+ * @param {string} source - Source label (e.g. 'user', 'inter-agent from AgentX')
1384
+ * @param {string} priority - Task priority ('high', 'medium', 'low')
1385
+ * @private
1386
+ */
1387
+ _autoCreateTaskForMessage(agent, message, source, priority) {
1388
+ if (!agent.taskList) {
1389
+ agent.taskList = { tasks: [], lastUpdated: new Date().toISOString() };
1390
+ }
1391
+
1392
+ const content = message.content || '';
1393
+ const titleContent = content.trim().replace(/\n+/g, ' ').replace(/\s+/g, ' ');
1394
+ const firstSentence = titleContent.split(/[.!?]/)[0].trim();
1395
+ const title = firstSentence.length > 50
1396
+ ? firstSentence.substring(0, 47) + '...'
1397
+ : firstSentence || 'Process message';
1398
+
1399
+ const isInterAgent = source.startsWith('inter-agent');
1400
+ const requiresReply = isInterAgent && message.requiresReply === true;
1401
+ const taskTitle = isInterAgent
1402
+ ? (requiresReply
1403
+ ? `Handle and reply to ${source}: ${title}`
1404
+ : `Handle ${source}: ${title}`)
1405
+ : `Process ${source} request: ${title}`;
1406
+ const taskDescription = isInterAgent
1407
+ ? (requiresReply
1408
+ ? `Handle ${source} message and reply using the agentcommunication tool with action="reply-to-message": "${content.length > 200 ? content.substring(0, 197) + '...' : content}"`
1409
+ : `Handle ${source} message: "${content.length > 200 ? content.substring(0, 197) + '...' : content}"`)
1410
+ : `Handle ${source} message: "${content.length > 200 ? content.substring(0, 197) + '...' : content}"`;
1411
+
1412
+ const task = {
1413
+ id: `task-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
1414
+ title: taskTitle,
1415
+ description: taskDescription,
1416
+ status: 'pending',
1417
+ priority,
1418
+ createdAt: new Date().toISOString(),
1419
+ updatedAt: new Date().toISOString(),
1420
+ source: 'auto-created',
1421
+ messageId: message.id
1422
+ };
1423
+
1424
+ agent.taskList.tasks.push(task);
1425
+ agent.taskList.lastUpdated = new Date().toISOString();
1426
+
1427
+ this.logger.info(`Auto-created task for ${source} message`, {
1428
+ agentId: agent.id,
1429
+ taskId: task.id,
1430
+ title: task.title
1431
+ });
1432
+ }
1433
+
1434
+ /**
1435
+ * Clear all message queues for an agent
1436
+ * @param {string} agentId - Agent ID
1437
+ * @returns {Promise<void>}
1438
+ */
1439
+ async clearAgentQueues(agentId) {
1440
+ const agent = await this.getAgent(agentId);
1441
+ if (!agent) {
1442
+ throw new Error(`Agent not found: ${agentId}`);
1443
+ }
1444
+
1445
+ agent.messageQueues.toolResults = [];
1446
+ agent.messageQueues.interAgentMessages = [];
1447
+ agent.messageQueues.userMessages = [];
1448
+
1449
+ await this.persistAgentState(agentId);
1450
+
1451
+ this.logger.info(`Message queues cleared for agent: ${agentId}`);
1452
+ }
1453
+
1454
+ /**
1455
+ * Get total queued messages count for an agent
1456
+ * @param {string} agentId - Agent ID
1457
+ * @returns {Promise<Object>} Queue counts by type
1458
+ */
1459
+ async getQueueCounts(agentId) {
1460
+ const agent = await this.getAgent(agentId);
1461
+ if (!agent) {
1462
+ return { toolResults: 0, interAgentMessages: 0, userMessages: 0, total: 0 };
1463
+ }
1464
+
1465
+ const counts = {
1466
+ toolResults: agent.messageQueues.toolResults.length,
1467
+ interAgentMessages: agent.messageQueues.interAgentMessages.length,
1468
+ userMessages: agent.messageQueues.userMessages.length
1469
+ };
1470
+
1471
+ counts.total = counts.toolResults + counts.interAgentMessages + counts.userMessages;
1472
+
1473
+ return counts;
1474
+ }
1475
+
1476
+ /**
1477
+ * Get messages for AI request - returns compacted if available, otherwise original
1478
+ * CRITICAL FIX: Ensures compacted messages stay in sync with new messages after compaction
1479
+ * This is the primary method that should be used when preparing messages for AI service
1480
+ * @param {string} agentId - Agent ID
1481
+ * @param {string} modelId - Model ID
1482
+ * @returns {Promise<Array>} Messages array to send to AI
1483
+ */
1484
+ async getMessagesForAI(agentId, modelId) {
1485
+ const ENABLE_COMPACT_DEBUG = process.env.COMPACT_DEBUG === 'true';
1486
+
1487
+ // Helper: Remove trailing empty messages from array (cleans up malformed conversations)
1488
+ const cleanTrailingEmptyMessages = (messages) => {
1489
+ if (!messages || messages.length === 0) return messages;
1490
+
1491
+ let cleaned = [...messages];
1492
+ let removedCount = 0;
1493
+
1494
+ // Remove trailing empty messages
1495
+ while (cleaned.length > 0) {
1496
+ const lastMsg = cleaned[cleaned.length - 1];
1497
+ const content = lastMsg?.content;
1498
+ const isEmpty = !content || (typeof content === 'string' && !content.trim());
1499
+
1500
+ if (isEmpty) {
1501
+ cleaned.pop();
1502
+ removedCount++;
1503
+ } else {
1504
+ break;
1505
+ }
1506
+ }
1507
+
1508
+ if (removedCount > 0) {
1509
+ this.logger?.warn(`Removed ${removedCount} trailing empty message(s) from conversation`, {
1510
+ agentId,
1511
+ modelId,
1512
+ originalLength: messages.length,
1513
+ cleanedLength: cleaned.length
1514
+ });
1515
+ }
1516
+
1517
+ return cleaned;
1518
+ };
1519
+
1520
+ const agent = await this.getAgent(agentId);
1521
+ if (!agent) {
1522
+ throw new Error(`Agent not found: ${agentId}`);
1523
+ }
1524
+
1525
+ const conversation = agent.conversations[modelId];
1526
+ if (!conversation) {
1527
+ this.logger.warn(`No conversation found for model: ${modelId}`, { agentId });
1528
+ return [];
1529
+ }
1530
+
1531
+ // If no compacted messages exist, return original (cleaned)
1532
+ if (!conversation.compactizedMessages) {
1533
+ this.logger.debug('Retrieved messages for AI (no compaction)', {
1534
+ agentId,
1535
+ modelId,
1536
+ messageCount: conversation.messages.length
1537
+ });
1538
+
1539
+ if (ENABLE_COMPACT_DEBUG) {
1540
+ console.log('[GET-MESSAGES-FOR-AI]', {
1541
+ agentId,
1542
+ modelId,
1543
+ returnedArray: 'originalMessages',
1544
+ messageCount: conversation.messages.length,
1545
+ reason: 'No compacted messages exist'
1546
+ });
1547
+ }
1548
+
1549
+ return cleanTrailingEmptyMessages(conversation.messages);
1550
+ }
1551
+
1552
+ // CRITICAL FIX: Only sync messages added AFTER compaction
1553
+ // We track originalMessageCountAtCompaction to know which messages are truly new
1554
+ // vs which ones were already included in the compaction (sandwich strategy)
1555
+ const compactedLength = conversation.compactizedMessages.length;
1556
+ const originalLength = conversation.messages.length;
1557
+ const originalCountAtCompaction = conversation.originalMessageCountAtCompaction || originalLength;
1558
+
1559
+ // Only sync if NEW messages were added after compaction
1560
+ // (i.e., current original length > original length when compaction happened)
1561
+ if (originalLength > originalCountAtCompaction) {
1562
+ // New messages exist that weren't present during compaction
1563
+ const newMessageCount = originalLength - originalCountAtCompaction;
1564
+ const newMessages = conversation.messages.slice(-newMessageCount);
1565
+
1566
+ this.logger.info('Syncing truly new messages after compaction', {
1567
+ agentId,
1568
+ modelId,
1569
+ compactedLength,
1570
+ originalLength,
1571
+ originalCountAtCompaction,
1572
+ newMessageCount,
1573
+ newMessageRoles: newMessages.map(m => m.role)
1574
+ });
1575
+
1576
+ // Append only the truly new messages to compacted array
1577
+ conversation.compactizedMessages.push(...newMessages);
1578
+
1579
+ // Update the tracking to include newly synced messages
1580
+ conversation.originalMessageCountAtCompaction = originalLength;
1581
+
1582
+ // Persist the update
1583
+ await this.persistAgentState(agentId);
1584
+ } else if (originalLength > compactedLength) {
1585
+ // Length mismatch but no new messages - this is expected with sandwich compaction
1586
+ // The compacted version has fewer messages due to summarization, not missing messages
1587
+ this.logger.debug('Compacted messages shorter than original (expected with sandwich compaction)', {
1588
+ agentId,
1589
+ modelId,
1590
+ compactedLength,
1591
+ originalLength,
1592
+ originalCountAtCompaction,
1593
+ note: 'No sync needed - compaction reduces message count'
1594
+ });
1595
+ }
1596
+
1597
+ this.logger.debug('Retrieved messages for AI (compacted + synced)', {
1598
+ agentId,
1599
+ modelId,
1600
+ messageCount: conversation.compactizedMessages.length,
1601
+ wasResynced: originalLength > compactedLength
1602
+ });
1603
+
1604
+ if (ENABLE_COMPACT_DEBUG) {
1605
+ console.log('[GET-MESSAGES-FOR-AI]', {
1606
+ agentId,
1607
+ modelId,
1608
+ returnedArray: 'compactizedMessages',
1609
+ messageCount: conversation.compactizedMessages.length,
1610
+ originalMessageCount: conversation.messages.length,
1611
+ wasSynced: originalLength > compactedLength,
1612
+ syncedMessageCount: originalLength > compactedLength ? originalLength - compactedLength : 0,
1613
+ reason: 'Compacted messages exist, returning compacted version'
1614
+ });
1615
+ }
1616
+
1617
+ return cleanTrailingEmptyMessages(conversation.compactizedMessages);
1618
+ }
1619
+
1620
+ /**
1621
+ * Add message to conversation (stores in original messages array)
1622
+ * @param {string} agentId - Agent ID
1623
+ * @param {string} modelId - Model ID
1624
+ * @param {Object} message - Message object to add
1625
+ * @returns {Promise<void>}
1626
+ */
1627
+ async addMessageToConversation(agentId, modelId, message) {
1628
+ const ENABLE_COMPACT_DEBUG = process.env.COMPACT_DEBUG === 'true';
1629
+
1630
+ const agent = await this.getAgent(agentId);
1631
+ if (!agent) {
1632
+ throw new Error(`Agent not found: ${agentId}`);
1633
+ }
1634
+
1635
+ // Ensure model conversation exists
1636
+ if (!agent.conversations[modelId]) {
1637
+ agent.conversations[modelId] = this._createEmptyConversation(modelId);
1638
+ }
1639
+
1640
+ const conversation = agent.conversations[modelId];
1641
+
1642
+ // GUARD: Skip empty messages - they should never be added to history
1643
+ const messageContent = message.content;
1644
+ if (!messageContent || (typeof messageContent === 'string' && !messageContent.trim())) {
1645
+ this.logger?.warn(`Skipping empty message for agent ${agentId}`, {
1646
+ role: message.role,
1647
+ modelId,
1648
+ hasContent: !!messageContent
1649
+ });
1650
+ return; // Don't add empty messages
1651
+ }
1652
+
1653
+ const originalLengthBefore = conversation.messages.length;
1654
+ const compactedLengthBefore = conversation.compactizedMessages?.length || 0;
1655
+
1656
+ // Always add to original messages (never modify original)
1657
+ conversation.messages.push({
1658
+ ...message,
1659
+ timestamp: message.timestamp || new Date().toISOString()
1660
+ });
1661
+
1662
+ // If compacted version exists, also add to it (append new messages after compaction)
1663
+ if (conversation.compactizedMessages) {
1664
+ conversation.compactizedMessages.push({
1665
+ ...message,
1666
+ timestamp: message.timestamp || new Date().toISOString()
1667
+ });
1668
+ }
1669
+
1670
+ conversation.lastUpdated = new Date().toISOString();
1671
+
1672
+ if (ENABLE_COMPACT_DEBUG) {
1673
+ console.log('[ADD-MESSAGE-TO-CONVERSATION]', {
1674
+ agentId,
1675
+ modelId,
1676
+ role: message.role,
1677
+ hasCompactedVersion: !!conversation.compactizedMessages,
1678
+ originalMessages: {
1679
+ before: originalLengthBefore,
1680
+ after: conversation.messages.length,
1681
+ added: 1
1682
+ },
1683
+ compactizedMessages: conversation.compactizedMessages ? {
1684
+ before: compactedLengthBefore,
1685
+ after: conversation.compactizedMessages.length,
1686
+ added: 1
1687
+ } : null,
1688
+ behavior: conversation.compactizedMessages ? 'Added to BOTH arrays' : 'Added to original only'
1689
+ });
1690
+ }
1691
+
1692
+ await this.persistAgentState(agentId);
1693
+
1694
+ this.logger.debug('Message added to conversation', {
1695
+ agentId,
1696
+ modelId,
1697
+ role: message.role,
1698
+ hasCompacted: !!conversation.compactizedMessages
1699
+ });
1700
+ }
1701
+
1702
+ /**
1703
+ * Sync pending messages from conversation.messages to compactizedMessages.
1704
+ * The scheduler's addMessageToConversation only pushes to conversation.messages,
1705
+ * NOT to compactizedMessages. This method syncs any pending messages that haven't
1706
+ * been pushed to compactizedMessages yet.
1707
+ *
1708
+ * MUST be called before compaction reads compactizedMessages, otherwise compaction
1709
+ * will process a stale snapshot and the watermark will mark unsynced messages as
1710
+ * "already compacted", permanently losing them.
1711
+ *
1712
+ * @param {string} agentId - Agent ID
1713
+ * @param {string} modelId - Model ID
1714
+ * @returns {Promise<{synced: number}>} Number of messages synced
1715
+ */
1716
+ async syncPendingMessages(agentId, modelId) {
1717
+ const agent = await this.getAgent(agentId);
1718
+ if (!agent) return { synced: 0 };
1719
+
1720
+ const conversation = agent.conversations[modelId];
1721
+ if (!conversation || !conversation.compactizedMessages) return { synced: 0 };
1722
+
1723
+ const originalLength = conversation.messages.length;
1724
+ const originalCountAtCompaction = conversation.originalMessageCountAtCompaction || originalLength;
1725
+
1726
+ if (originalLength > originalCountAtCompaction) {
1727
+ const newCount = originalLength - originalCountAtCompaction;
1728
+ const newMessages = conversation.messages.slice(-newCount);
1729
+ conversation.compactizedMessages.push(...newMessages);
1730
+ conversation.originalMessageCountAtCompaction = originalLength;
1731
+
1732
+ this.logger.info('Pre-compaction sync: pushed pending messages to compactizedMessages', {
1733
+ agentId,
1734
+ modelId,
1735
+ synced: newCount,
1736
+ newMessageRoles: newMessages.map(m => m.role),
1737
+ compactizedMessagesLength: conversation.compactizedMessages.length
1738
+ });
1739
+
1740
+ return { synced: newCount };
1741
+ }
1742
+
1743
+ return { synced: 0 };
1744
+ }
1745
+
1746
+ /**
1747
+ * Update compacted messages after compactization
1748
+ * @param {string} agentId - Agent ID
1749
+ * @param {string} modelId - Model ID
1750
+ * @param {Object} compactionResult - Compaction result with messages and metadata
1751
+ * @param {number} [preCompactionMessageCount] - Message count recorded BEFORE compaction started.
1752
+ * If provided, used as the watermark instead of current messages.length. This prevents
1753
+ * messages added DURING compaction from being silently lost.
1754
+ * @returns {Promise<void>}
1755
+ */
1756
+ async updateCompactedMessages(agentId, modelId, compactionResult, preCompactionMessageCount) {
1757
+ const agent = await this.getAgent(agentId);
1758
+ if (!agent) {
1759
+ throw new Error(`Agent not found: ${agentId}`);
1760
+ }
1761
+
1762
+ // Ensure model conversation exists (important for model switching scenarios)
1763
+ if (!agent.conversations[modelId]) {
1764
+ agent.conversations[modelId] = this._createEmptyConversation(modelId);
1765
+ this.logger.debug(`Created conversation for model switching: ${modelId}`);
1766
+ }
1767
+
1768
+ const conversation = agent.conversations[modelId];
1769
+
1770
+ // Update compacted messages
1771
+ conversation.compactizedMessages = compactionResult.compactedMessages;
1772
+
1773
+ // CRITICAL: Use the pre-compaction watermark if provided, NOT current messages.length.
1774
+ // If we use current messages.length, any messages added DURING compaction (e.g., user
1775
+ // messages arriving via WebSocket while the summarization API call is in flight) would
1776
+ // be marked as "already compacted" even though they weren't in the compaction input.
1777
+ // Using the pre-compaction count ensures those messages are detected as "new" by
1778
+ // getMessagesForAI's sync logic and properly appended to the compacted array.
1779
+ conversation.originalMessageCountAtCompaction = preCompactionMessageCount || conversation.messages.length;
1780
+
1781
+ // Update metadata
1782
+ conversation.lastCompactization = new Date().toISOString();
1783
+ conversation.compactizationCount += 1;
1784
+ conversation.compactizationStrategy = compactionResult.strategy;
1785
+ conversation.originalTokenCount = compactionResult.originalTokenCount;
1786
+ conversation.compactedTokenCount = compactionResult.compactedTokenCount;
1787
+ conversation.tokenCount = compactionResult.compactedTokenCount;
1788
+ conversation.lastUpdated = new Date().toISOString();
1789
+
1790
+ await this.persistAgentState(agentId);
1791
+
1792
+ this.logger.info('Compacted messages updated', {
1793
+ agentId,
1794
+ modelId,
1795
+ strategy: compactionResult.strategy,
1796
+ originalTokens: compactionResult.originalTokenCount,
1797
+ compactedTokens: compactionResult.compactedTokenCount,
1798
+ reductionPercent: compactionResult.reductionPercent,
1799
+ compactizationCount: conversation.compactizationCount
1800
+ });
1801
+ }
1802
+
1803
+ /**
1804
+ * Clear compacted messages and revert to original
1805
+ * Useful for debugging or if compaction needs to be redone
1806
+ * @param {string} agentId - Agent ID
1807
+ * @param {string} modelId - Model ID
1808
+ * @returns {Promise<void>}
1809
+ */
1810
+ async clearCompactedMessages(agentId, modelId) {
1811
+ const agent = await this.getAgent(agentId);
1812
+ if (!agent) {
1813
+ throw new Error(`Agent not found: ${agentId}`);
1814
+ }
1815
+
1816
+ const conversation = agent.conversations[modelId];
1817
+ if (!conversation) {
1818
+ return;
1819
+ }
1820
+
1821
+ conversation.compactizedMessages = null;
1822
+ conversation.lastCompactization = null;
1823
+ conversation.compactizationCount = 0;
1824
+ conversation.compactizationStrategy = null;
1825
+ conversation.originalTokenCount = 0;
1826
+ conversation.compactedTokenCount = 0;
1827
+ conversation.tokenCount = 0;
1828
+ conversation.originalMessageCountAtCompaction = null;
1829
+
1830
+ await this.persistAgentState(agentId);
1831
+
1832
+ this.logger.info('Compacted messages cleared', { agentId, modelId });
1833
+ }
1834
+
1835
+ /**
1836
+ * Get compaction metadata for a conversation
1837
+ * @param {string} agentId - Agent ID
1838
+ * @param {string} modelId - Model ID
1839
+ * @returns {Promise<Object|null>} Compaction metadata or null if no compaction
1840
+ */
1841
+ async getCompactionMetadata(agentId, modelId) {
1842
+ const agent = await this.getAgent(agentId);
1843
+ if (!agent) {
1844
+ return null;
1845
+ }
1846
+
1847
+ const conversation = agent.conversations[modelId];
1848
+ if (!conversation) {
1849
+ return null;
1850
+ }
1851
+
1852
+ // Return metadata whether compacted or not
1853
+ const isCompacted = !!conversation.compactizedMessages;
1854
+
1855
+ return {
1856
+ isCompacted,
1857
+ lastCompactization: conversation.lastCompactization || null,
1858
+ compactizationCount: conversation.compactizationCount || 0,
1859
+ strategy: conversation.compactizationStrategy || null,
1860
+ originalTokenCount: conversation.originalTokenCount || 0,
1861
+ compactedTokenCount: conversation.compactedTokenCount || 0,
1862
+ reductionPercent: conversation.originalTokenCount > 0
1863
+ ? ((conversation.originalTokenCount - conversation.compactedTokenCount) / conversation.originalTokenCount) * 100
1864
+ : 0,
1865
+ originalMessages: conversation.messages || [],
1866
+ compactedMessages: conversation.compactizedMessages || null,
1867
+ originalMessageCount: conversation.messages?.length || 0,
1868
+ compactedMessageCount: conversation.compactizedMessages?.length || 0
1869
+ };
1870
+ }
1871
+
1872
+ /**
1873
+ * Migrate existing agent conversations to dual storage structure
1874
+ * Ensures backward compatibility with existing agents
1875
+ * @param {string} agentId - Agent ID
1876
+ * @returns {Promise<boolean>} True if migration was needed and performed
1877
+ */
1878
+ async migrateConversationStructure(agentId) {
1879
+ const agent = await this.getAgent(agentId);
1880
+ if (!agent) {
1881
+ return false;
1882
+ }
1883
+
1884
+ let migrated = false;
1885
+
1886
+ // Check each conversation for migration needs
1887
+ for (const [modelId, conversation] of Object.entries(agent.conversations)) {
1888
+ if (modelId === 'full') continue; // Skip full conversation
1889
+
1890
+ // Check if conversation needs migration (missing new fields)
1891
+ if (conversation.compactizedMessages === undefined) {
1892
+ // Add new fields
1893
+ conversation.compactizedMessages = null;
1894
+ conversation.lastCompactization = null;
1895
+ conversation.compactizationCount = 0;
1896
+ conversation.compactizationStrategy = null;
1897
+ conversation.originalTokenCount = 0;
1898
+ conversation.compactedTokenCount = 0;
1899
+ conversation.originalMessageCountAtCompaction = null;
1900
+
1901
+ migrated = true;
1902
+
1903
+ this.logger.info('Migrated conversation structure', {
1904
+ agentId,
1905
+ modelId,
1906
+ messageCount: conversation.messages?.length || 0
1907
+ });
1908
+ }
1909
+
1910
+ // CRITICAL: Migrate existing compacted conversations that don't have the new tracking field
1911
+ // This prevents the sync bug from re-adding messages already included in compaction
1912
+ if (conversation.compactizedMessages && conversation.originalMessageCountAtCompaction === undefined) {
1913
+ // Set to current original length to prevent any sync from running
1914
+ // This is safe because any truly new messages would have been added to both arrays
1915
+ conversation.originalMessageCountAtCompaction = conversation.messages?.length || 0;
1916
+ migrated = true;
1917
+
1918
+ this.logger.info('Migrated compaction tracking field', {
1919
+ agentId,
1920
+ modelId,
1921
+ originalMessageCountAtCompaction: conversation.originalMessageCountAtCompaction,
1922
+ compactedMessageCount: conversation.compactizedMessages.length
1923
+ });
1924
+ }
1925
+ }
1926
+
1927
+ if (migrated) {
1928
+ await this.persistAgentState(agentId);
1929
+ }
1930
+
1931
+ return migrated;
1932
+ }
1933
+
1934
+ /**
1935
+ * Create empty conversation structure with all required fields
1936
+ * @private
1937
+ * @param {string} modelId - Model ID
1938
+ * @returns {Object} Empty conversation structure
1939
+ */
1940
+ _createEmptyConversation(modelId) {
1941
+ return {
1942
+ messages: [],
1943
+ compactizedMessages: null,
1944
+ lastCompactization: null,
1945
+ compactizationCount: 0,
1946
+ compactizationStrategy: null,
1947
+ originalTokenCount: 0,
1948
+ compactedTokenCount: 0,
1949
+ tokenCount: 0,
1950
+ originalMessageCountAtCompaction: null, // Tracks original length at compaction time
1951
+ lastUpdated: new Date().toISOString(),
1952
+ formatVersion: this._getModelFormatVersion(modelId)
1953
+ };
1954
+ }
1955
+ }
1956
+
1957
+ export default AgentPool;