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,1103 @@
1
+ /**
2
+ * ConversationCompactionService - Intelligent conversation compactization
3
+ *
4
+ * Purpose:
5
+ * - Compress long conversations while preserving critical information
6
+ * - AI-based summarization with sandwich approach (beginning + summary + end)
7
+ * - Multi-pass compaction (up to 3 passes) when a single pass isn't enough
8
+ * - Model switching support via best-existing-conversation selection
9
+ * - Compaction model validation against live model catalog
10
+ *
11
+ * Strategy:
12
+ * - Summarization only (sandwich approach):
13
+ * Keep beginning messages + AI summary of middle + end messages
14
+ * Middle segment always >= 50% of total messages
15
+ * Multi-pass: if result is still too large, re-summarize up to MAX_COMPACTION_PASSES times
16
+ *
17
+ * Model Switch Behavior:
18
+ * - Instead of truncation, find the best existing compacted conversation
19
+ * from another model and summarize that for the target model
20
+ */
21
+
22
+ import {
23
+ COMPACTION_CONFIG,
24
+ COMPACTION_STRATEGIES,
25
+ } from '../utilities/constants.js';
26
+
27
+ class ConversationCompactionService {
28
+ constructor(tokenCountingService, aiService, logger) {
29
+ this.tokenCountingService = tokenCountingService;
30
+ this.aiService = aiService;
31
+ this.logger = logger;
32
+
33
+ // Models service for runtime validation (injected after construction)
34
+ this.modelsService = null;
35
+
36
+ // Round-robin index for compaction model selection
37
+ this.compactionModelIndex = 0;
38
+
39
+ // Summary generation prompt template
40
+ this.summaryPromptTemplate = this._createSummaryPromptTemplate();
41
+ }
42
+
43
+ /**
44
+ * Inject models service for runtime model validation
45
+ * @param {ModelsService} modelsService - Models service instance
46
+ */
47
+ setModelsService(modelsService) {
48
+ this.modelsService = modelsService;
49
+ this.logger.info('ModelsService injected into compaction service');
50
+ }
51
+
52
+ /**
53
+ * Get validated compaction models — filters COMPACTION_MODELS against live catalog
54
+ * @returns {string[]} Array of model names that are both recommended and available
55
+ * @private
56
+ */
57
+ _getValidatedCompactionModels() {
58
+ const recommendedModels = COMPACTION_CONFIG.COMPACTION_MODELS || [];
59
+
60
+ if (!this.modelsService) {
61
+ this.logger.debug('No modelsService available, using all recommended compaction models');
62
+ return recommendedModels;
63
+ }
64
+
65
+ try {
66
+ const availableNames = this.modelsService.getAvailableModelNames();
67
+ if (!availableNames || availableNames.length === 0) {
68
+ this.logger.warn('ModelsService returned no models, using all recommended compaction models');
69
+ return recommendedModels;
70
+ }
71
+
72
+ const availableSet = new Set(availableNames);
73
+ const validated = recommendedModels.filter(m => availableSet.has(m));
74
+
75
+ if (validated.length > 0) {
76
+ this.logger.debug('Compaction models validated against live catalog', {
77
+ recommended: recommendedModels.length,
78
+ available: validated.length,
79
+ validated
80
+ });
81
+ return validated;
82
+ }
83
+
84
+ // No recommended models match — pick the available model with the largest context window
85
+ this.logger.warn('No recommended compaction models found in catalog, selecting largest-context available model');
86
+ const models = this.modelsService.getModels();
87
+ const chatModels = models.filter(m => m.type === 'chat' || !m.type);
88
+
89
+ if (chatModels.length === 0) {
90
+ this.logger.error('No chat models available at all, falling back to recommended list');
91
+ return recommendedModels;
92
+ }
93
+
94
+ // Sort by context window descending
95
+ chatModels.sort((a, b) => (b.contextWindow || 0) - (a.contextWindow || 0));
96
+ const fallbackModel = chatModels[0].name;
97
+
98
+ this.logger.info('Using fallback compaction model from catalog', {
99
+ model: fallbackModel,
100
+ contextWindow: chatModels[0].contextWindow
101
+ });
102
+ return [fallbackModel];
103
+
104
+ } catch (error) {
105
+ this.logger.warn('Failed to validate compaction models against catalog', {
106
+ error: error.message
107
+ });
108
+ return recommendedModels;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Get next compaction model using round-robin from validated models
114
+ * @param {string[]} models - Validated model list
115
+ * @param {number} offset - Offset from current index
116
+ * @returns {string} Model name
117
+ * @private
118
+ */
119
+ _getNextCompactionModel(models, offset = 0) {
120
+ if (!models || models.length === 0) {
121
+ throw new Error('No compaction models available');
122
+ }
123
+ const index = (this.compactionModelIndex + offset) % models.length;
124
+ return models[index];
125
+ }
126
+
127
+ /**
128
+ * Advance the round-robin index
129
+ * @private
130
+ */
131
+ _advanceCompactionModelIndex() {
132
+ const models = COMPACTION_CONFIG.COMPACTION_MODELS || [];
133
+ if (models.length > 0) {
134
+ this.compactionModelIndex = (this.compactionModelIndex + 1) % models.length;
135
+ }
136
+ }
137
+
138
+ /**
139
+ * Main compaction entry point
140
+ * @param {Array} messages - Original messages array
141
+ * @param {string} currentModel - Current model being used
142
+ * @param {string} targetModel - Target model (may differ if switching)
143
+ * @param {Object} options - Compaction options
144
+ * @param {Map} [options.compactedConversations] - Map of modelId → compactedMessages (for model switch)
145
+ * @returns {Promise<Object>} Compaction result with messages and metadata
146
+ */
147
+ async compactConversation(messages, currentModel, targetModel, options = {}) {
148
+ const startTime = Date.now();
149
+
150
+ try {
151
+ // Validate inputs
152
+ if (!Array.isArray(messages) || messages.length === 0) {
153
+ throw new Error('Messages array is required and cannot be empty');
154
+ }
155
+
156
+ // Split oversized messages into smaller chunks before compaction.
157
+ // This increases message count so the sandwich strategy can push
158
+ // oversized content into the summarizable middle segment.
159
+ const splitResult = this._splitOversizedMessages(messages);
160
+ let wasSplit = splitResult.wasSplit;
161
+
162
+ const minMessages = options.emergency ? 4 : COMPACTION_CONFIG.MIN_MESSAGES_FOR_COMPACTION;
163
+
164
+ if (splitResult.messages.length < minMessages) {
165
+ this.logger.warn('Too few messages for compaction', {
166
+ messageCount: splitResult.messages.length,
167
+ originalCount: messages.length,
168
+ minimum: minMessages,
169
+ emergency: !!options.emergency
170
+ });
171
+
172
+ return {
173
+ compactedMessages: messages,
174
+ strategy: 'none',
175
+ originalTokenCount: 0,
176
+ compactedTokenCount: 0,
177
+ reductionPercent: 0,
178
+ skipped: true,
179
+ reason: 'Too few messages'
180
+ };
181
+ }
182
+
183
+ // Determine if model switch and pick best starting conversation
184
+ const isModelSwitch = currentModel !== targetModel;
185
+ let messagesToCompact = splitResult.messages;
186
+
187
+ if (isModelSwitch && options.compactedConversations) {
188
+ const bestConversation = this._findBestConversationForModelSwitch(
189
+ options.compactedConversations,
190
+ targetModel
191
+ );
192
+ if (bestConversation) {
193
+ // Split the best conversation too (it may contain oversized messages)
194
+ const bestSplit = this._splitOversizedMessages(bestConversation);
195
+ messagesToCompact = bestSplit.messages;
196
+ wasSplit = wasSplit || bestSplit.wasSplit;
197
+ this.logger.info('Using best existing conversation for model switch', {
198
+ originalMessages: messages.length,
199
+ bestConversationMessages: bestConversation.length,
200
+ currentModel,
201
+ targetModel
202
+ });
203
+ }
204
+ }
205
+
206
+ this.logger.info('Starting conversation compaction', {
207
+ messageCount: messagesToCompact.length,
208
+ currentModel,
209
+ targetModel,
210
+ strategy: COMPACTION_STRATEGIES.SUMMARIZATION,
211
+ isModelSwitch
212
+ });
213
+
214
+ // Execute multi-pass summarization
215
+ const result = await this._compactWithMultiPassSummarization(
216
+ messagesToCompact,
217
+ targetModel,
218
+ { ...options, wasSplit }
219
+ );
220
+
221
+ // Add execution metadata
222
+ const executionTime = Date.now() - startTime;
223
+ result.executionTime = executionTime;
224
+ result.timestamp = new Date().toISOString();
225
+
226
+ this.logger.info('Compaction completed successfully', {
227
+ strategy: result.strategy,
228
+ originalMessages: messagesToCompact.length,
229
+ compactedMessages: result.compactedMessages.length,
230
+ originalTokens: result.originalTokenCount,
231
+ compactedTokens: result.compactedTokenCount,
232
+ reductionPercent: result.reductionPercent.toFixed(2),
233
+ passes: result.passes,
234
+ executionTime: `${executionTime}ms`
235
+ });
236
+
237
+ return result;
238
+
239
+ } catch (error) {
240
+ const executionTime = Date.now() - startTime;
241
+
242
+ this.logger.error('Compaction failed', {
243
+ error: error.message,
244
+ messageCount: messages.length,
245
+ currentModel,
246
+ targetModel,
247
+ executionTime: `${executionTime}ms`
248
+ });
249
+
250
+ throw error;
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Find the best existing compacted conversation for model switching.
256
+ * Prefers the conversation from a model whose context window is the largest
257
+ * C where C < targetModel's context window.
258
+ * Falls back to the shortest compacted conversation.
259
+ *
260
+ * @param {Map} compactedConversations - Map of modelId → compactedMessages
261
+ * @param {string} targetModel - Target model name
262
+ * @returns {Array|null} Best conversation messages, or null
263
+ * @private
264
+ */
265
+ _findBestConversationForModelSwitch(compactedConversations, targetModel) {
266
+ if (!compactedConversations || compactedConversations.size === 0) {
267
+ return null;
268
+ }
269
+
270
+ const targetContextWindow = this.tokenCountingService.getModelContextWindow(targetModel);
271
+
272
+ // Collect candidates: conversations that have messages
273
+ const candidates = [];
274
+ for (const [modelId, msgs] of compactedConversations) {
275
+ if (Array.isArray(msgs) && msgs.length > 0) {
276
+ const contextWindow = this.tokenCountingService.getModelContextWindow(modelId);
277
+ candidates.push({ modelId, messages: msgs, contextWindow });
278
+ }
279
+ }
280
+
281
+ if (candidates.length === 0) {
282
+ return null;
283
+ }
284
+
285
+ // Prefer: largest context window that is < targetModel's context window
286
+ // (these conversations were already compacted to fit a smaller window, so they'll fit the target)
287
+ const smallerCandidates = candidates
288
+ .filter(c => c.contextWindow < targetContextWindow)
289
+ .sort((a, b) => b.contextWindow - a.contextWindow);
290
+
291
+ if (smallerCandidates.length > 0) {
292
+ const best = smallerCandidates[0];
293
+ this.logger.debug('Best conversation for model switch: largest smaller context', {
294
+ selectedModel: best.modelId,
295
+ selectedContextWindow: best.contextWindow,
296
+ targetContextWindow,
297
+ messageCount: best.messages.length
298
+ });
299
+ return best.messages;
300
+ }
301
+
302
+ // Fallback: shortest conversation (fewest messages)
303
+ candidates.sort((a, b) => a.messages.length - b.messages.length);
304
+ const shortest = candidates[0];
305
+ this.logger.debug('Best conversation for model switch: shortest', {
306
+ selectedModel: shortest.modelId,
307
+ messageCount: shortest.messages.length
308
+ });
309
+ return shortest.messages;
310
+ }
311
+
312
+ /**
313
+ * Multi-pass summarization: runs up to MAX_COMPACTION_PASSES passes.
314
+ * After each pass, checks if the result fits within the compaction threshold.
315
+ * If it fits, returns immediately; otherwise re-compacts the compacted result.
316
+ *
317
+ * @param {Array} messages - Messages to compact
318
+ * @param {string} model - Target model name
319
+ * @param {Object} options - Compaction options
320
+ * @returns {Promise<Object>} Compaction result
321
+ * @private
322
+ */
323
+ async _compactWithMultiPassSummarization(messages, model, options) {
324
+ const maxPasses = COMPACTION_CONFIG.MAX_COMPACTION_PASSES;
325
+ const contextWindow = this.tokenCountingService.getModelContextWindow(model);
326
+ const maxOutputTokens = this.tokenCountingService.getModelMaxOutputTokens(model);
327
+ const threshold = COMPACTION_CONFIG.DEFAULT_THRESHOLD;
328
+ const wasSplit = options.wasSplit || false;
329
+
330
+ let currentMessages = messages;
331
+ let result = null;
332
+
333
+ for (let pass = 1; pass <= maxPasses; pass++) {
334
+ this.logger.info(`Compaction pass ${pass}/${maxPasses}`, {
335
+ inputMessages: currentMessages.length,
336
+ model,
337
+ contextWindow,
338
+ wasSplit
339
+ });
340
+
341
+ result = await this._executeSingleSummarizationPass(currentMessages, model, options, pass);
342
+
343
+ // Check if result fits within threshold
344
+ const compactedTokens = this.tokenCountingService.getConversationTokenCount(
345
+ result.compactedMessages,
346
+ model
347
+ );
348
+ result.compactedTokenCount = compactedTokens;
349
+
350
+ const fitsWithinThreshold = !this.tokenCountingService.shouldTriggerCompaction(
351
+ compactedTokens,
352
+ maxOutputTokens,
353
+ contextWindow,
354
+ threshold
355
+ );
356
+
357
+ this.logger.info(`Compaction pass ${pass} result`, {
358
+ compactedMessages: result.compactedMessages.length,
359
+ compactedTokens,
360
+ fitsWithinThreshold,
361
+ contextWindow,
362
+ threshold
363
+ });
364
+
365
+ if (fitsWithinThreshold) {
366
+ result.passes = pass;
367
+ return result;
368
+ }
369
+
370
+ // If not the last pass, use compacted result as input for next pass
371
+ // Split any oversized messages that survived compaction
372
+ if (pass < maxPasses) {
373
+ const reSplit = this._splitOversizedMessages(result.compactedMessages);
374
+ currentMessages = reSplit.messages;
375
+ }
376
+ }
377
+
378
+ // Best effort after all passes
379
+ this.logger.warn('Compaction did not fit within threshold after all passes', {
380
+ passes: maxPasses,
381
+ finalTokens: result.compactedTokenCount,
382
+ contextWindow,
383
+ threshold
384
+ });
385
+ result.passes = maxPasses;
386
+ return result;
387
+ }
388
+
389
+ /**
390
+ * Execute a single summarization pass (sandwich approach).
391
+ * Preserves beginning + AI summary of middle + end.
392
+ *
393
+ * @param {Array} messages - Messages to compact
394
+ * @param {string} model - Target model name
395
+ * @param {Object} options - Compaction options
396
+ * @param {number} passNumber - Current pass number (1-based)
397
+ * @returns {Promise<Object>} Compaction result
398
+ * @private
399
+ */
400
+ async _executeSingleSummarizationPass(messages, model, options, passNumber) {
401
+ const strategy = COMPACTION_STRATEGIES.SUMMARIZATION;
402
+
403
+ // Estimate original token count
404
+ const originalTokenCount = this.tokenCountingService.getConversationTokenCount(
405
+ messages,
406
+ model
407
+ );
408
+
409
+ // Identify segments (message-count-based)
410
+ // When oversized messages were split, use a small end segment so
411
+ // most chunks land in the middle for summarization
412
+ const segments = this._identifySegments(messages, { wasSplit: options.wasSplit });
413
+
414
+ this.logger.info(`Pass ${passNumber}: segments identified`, {
415
+ summarizedMessages: segments.middle.length,
416
+ keptMessages: segments.end.length,
417
+ totalMessages: messages.length
418
+ });
419
+
420
+ // Generate summary of middle segment
421
+ let summary;
422
+ try {
423
+ summary = await this._generateSummary(
424
+ segments.middle,
425
+ model,
426
+ {
427
+ ...options,
428
+ middleStartIndex: segments.middleStartIndex,
429
+ middleEndIndex: segments.middleEndIndex,
430
+ passNumber
431
+ }
432
+ );
433
+ } catch (error) {
434
+ if (error.code === 'ALL_MODELS_EXHAUSTED') {
435
+ // All AI models failed — use structural fallback
436
+ this.logger.warn('All summarization models exhausted, using structural fallback compaction');
437
+ const fallbackResult = this._performFallbackCompaction(messages);
438
+
439
+ const compactedTokenCount = this.tokenCountingService.getConversationTokenCount(
440
+ fallbackResult.compactedMessages,
441
+ model
442
+ );
443
+
444
+ const reductionPercent = originalTokenCount > 0
445
+ ? ((originalTokenCount - compactedTokenCount) / originalTokenCount) * 100
446
+ : 0;
447
+
448
+ return {
449
+ compactedMessages: fallbackResult.compactedMessages,
450
+ strategy: 'structural_fallback',
451
+ originalTokenCount,
452
+ compactedTokenCount,
453
+ reductionPercent,
454
+ segments: {
455
+ beginningCount: segments.beginning.length,
456
+ middleCount: segments.middle.length,
457
+ endCount: segments.end.length,
458
+ summaryInserted: true,
459
+ fallback: true
460
+ }
461
+ };
462
+ }
463
+ throw error;
464
+ }
465
+
466
+ // Construct compacted conversation
467
+ const compactedMessages = [
468
+ ...segments.beginning,
469
+ summary,
470
+ ...segments.end
471
+ ];
472
+
473
+ // Count tokens in compacted conversation
474
+ const compactedTokenCount = this.tokenCountingService.getConversationTokenCount(
475
+ compactedMessages,
476
+ model
477
+ );
478
+
479
+ // Calculate reduction
480
+ const reductionPercent = originalTokenCount > 0
481
+ ? ((originalTokenCount - compactedTokenCount) / originalTokenCount) * 100
482
+ : 0;
483
+
484
+ return {
485
+ compactedMessages,
486
+ strategy,
487
+ originalTokenCount,
488
+ compactedTokenCount,
489
+ reductionPercent,
490
+ segments: {
491
+ beginningCount: segments.beginning.length,
492
+ middleCount: segments.middle.length,
493
+ endCount: segments.end.length,
494
+ summaryInserted: true
495
+ }
496
+ };
497
+ }
498
+
499
+ /**
500
+ * Calculate the maximum characters the summarizer can handle in a single call.
501
+ * Uses the largest available compaction model's context window minus overhead.
502
+ *
503
+ * @returns {number} Maximum characters per summarization call
504
+ * @private
505
+ */
506
+ _calculateSummarizerCapacity() {
507
+ const models = this._getValidatedCompactionModels();
508
+ const contextWindows = { ...(COMPACTION_CONFIG.MODEL_CONTEXT_WINDOWS || {}) };
509
+
510
+ // Augment with live data from modelsService if available
511
+ if (this.modelsService) {
512
+ try {
513
+ const allModels = this.modelsService.getModels();
514
+ for (const m of allModels) {
515
+ if (m.contextWindow) {
516
+ contextWindows[m.name] = m.contextWindow;
517
+ }
518
+ }
519
+ } catch (e) { /* use static fallback */ }
520
+ }
521
+
522
+ const largestContext = Math.max(...models.map(m => contextWindows[m] || 128000));
523
+
524
+ const usableTokens = largestContext
525
+ - (COMPACTION_CONFIG.SUMMARIZER_SYSTEM_PROMPT_OVERHEAD || 500)
526
+ - (COMPACTION_CONFIG.SUMMARIZER_TEMPLATE_OVERHEAD || 800)
527
+ - (COMPACTION_CONFIG.MAX_SUMMARY_TOKENS || 8000)
528
+ - (COMPACTION_CONFIG.SUMMARIZER_SAFETY_MARGIN || 5000);
529
+
530
+ const effectiveTokens = Math.max(10000, usableTokens);
531
+ return effectiveTokens * (COMPACTION_CONFIG.CHARS_PER_TOKEN_ESTIMATE || 3);
532
+ }
533
+
534
+ /**
535
+ * Identify conversation segments using token-budget sizing.
536
+ * The middle segment starts at the 15% char mark and extends forward
537
+ * until hitting either the summarizer's capacity or 35% of total chars.
538
+ * This guarantees the middle always fits within the summarizer's context window.
539
+ *
540
+ * @param {Array} messages - Messages array
541
+ * @param {Object} [options] - Segmentation options
542
+ * @param {boolean} [options.wasSplit] - Whether oversized messages were split (unused in new logic)
543
+ * @returns {Object} { beginning, middle, end, middleStartIndex, middleEndIndex }
544
+ * @private
545
+ */
546
+ _identifySegments(messages, options = {}) {
547
+ const totalMessages = messages.length;
548
+
549
+ // Edge case: very small conversations — summarize all but last message
550
+ if (totalMessages <= 4) {
551
+ return {
552
+ beginning: [],
553
+ middle: messages.slice(0, Math.max(1, totalMessages - 1)),
554
+ end: messages.slice(-1),
555
+ middleStartIndex: 0,
556
+ middleEndIndex: Math.max(0, totalMessages - 2)
557
+ };
558
+ }
559
+
560
+ // Calculate char length of each message
561
+ const msgChars = messages.map(m => {
562
+ const content = typeof m.content === 'string' ? m.content : JSON.stringify(m.content || '');
563
+ return content.length;
564
+ });
565
+ const totalChars = msgChars.reduce((sum, c) => sum + c, 0);
566
+
567
+ // Get summarizer capacity (max chars it can handle in one call)
568
+ const summarizerCapacity = this._calculateSummarizerCapacity();
569
+
570
+ // Walk BACKWARD from end to determine tail (kept verbatim)
571
+ // Recent messages are more relevant for continuation than old ones
572
+ const tailBudget = totalChars * COMPACTION_CONFIG.TAIL_PRESERVE_PERCENTAGE;
573
+ let tailChars = 0;
574
+ let keepStartIdx = totalMessages;
575
+ for (let i = totalMessages - 1; i >= 0; i--) {
576
+ if (tailChars + msgChars[i] > tailBudget && i < totalMessages - 1) {
577
+ break;
578
+ }
579
+ tailChars += msgChars[i];
580
+ keepStartIdx = i;
581
+ }
582
+ // Ensure at least 1 message in the summarize segment
583
+ keepStartIdx = Math.max(1, keepStartIdx);
584
+
585
+ // Old segment: M[0..keepStartIdx-1] — to be summarized
586
+ // Cap by summarizer capacity (if too large, only summarize what fits this pass)
587
+ let oldChars = 0;
588
+ let summarizeEndIdx = keepStartIdx;
589
+ for (let i = 0; i < keepStartIdx; i++) {
590
+ if (oldChars + msgChars[i] > summarizerCapacity && i > 0) {
591
+ summarizeEndIdx = i;
592
+ break;
593
+ }
594
+ oldChars += msgChars[i];
595
+ }
596
+
597
+ const middle = messages.slice(0, summarizeEndIdx); // summarized
598
+ const end = messages.slice(summarizeEndIdx); // kept verbatim
599
+
600
+ this.logger.info('Segment identification (tail-preserving)', {
601
+ totalMessages,
602
+ totalChars,
603
+ summarizerCapacity,
604
+ middleCount: middle.length,
605
+ middleChars: oldChars,
606
+ endCount: end.length,
607
+ tailBudget,
608
+ keepStartIdx,
609
+ summarizeEndIdx
610
+ });
611
+
612
+ return {
613
+ beginning: [],
614
+ middle: middle.length > 0 ? middle : [messages[0]],
615
+ end: end.length > 0 ? end : [messages[messages.length - 1]],
616
+ middleStartIndex: 0,
617
+ middleEndIndex: summarizeEndIdx - 1
618
+ };
619
+ }
620
+
621
+ /**
622
+ * Generate AI summary of middle segment using validated compaction models
623
+ * @private
624
+ */
625
+ async _generateSummary(middleMessages, model, options = {}) {
626
+ if (middleMessages.length === 0) {
627
+ return {
628
+ role: 'system',
629
+ content: `${COMPACTION_CONFIG.COMPACTION_SUMMARY_PREFIX} No messages to summarize.`,
630
+ type: 'summary',
631
+ timestamp: new Date().toISOString()
632
+ };
633
+ }
634
+
635
+ // Format middle messages for summarization
636
+ let middleContent = middleMessages
637
+ .map(msg => `${msg.role}: ${typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content)}`)
638
+ .join('\n\n');
639
+
640
+ // Estimate input tokens
641
+ const estimatedInputTokens = Math.ceil(middleContent.length / COMPACTION_CONFIG.CHARS_PER_TOKEN_ESTIMATE);
642
+
643
+ // Get validated compaction models
644
+ const models = this._getValidatedCompactionModels();
645
+
646
+ // Get context windows for smart selection
647
+ const contextWindows = COMPACTION_CONFIG.MODEL_CONTEXT_WINDOWS || {};
648
+
649
+ // If modelsService available, augment context windows with live data
650
+ if (this.modelsService) {
651
+ try {
652
+ const allModels = this.modelsService.getModels();
653
+ for (const m of allModels) {
654
+ if (m.contextWindow) {
655
+ contextWindows[m.name] = m.contextWindow;
656
+ }
657
+ }
658
+ } catch (e) {
659
+ // Ignore — use static fallback
660
+ }
661
+ }
662
+
663
+ // Find the largest context window available among compaction models
664
+ const largestContextWindow = Math.max(
665
+ ...models.map(m => contextWindows[m] || 128000)
666
+ );
667
+ const maxInputTokens = largestContextWindow - 10000;
668
+
669
+ // Middle is now sized by _identifySegments to fit within the summarizer's capacity.
670
+ // No truncation needed — the segment is guaranteed to be within budget.
671
+ if (estimatedInputTokens > maxInputTokens) {
672
+ this.logger.info('Middle segment exceeds single model but sized to fit summarizer capacity', {
673
+ estimatedInputTokens,
674
+ maxInputTokens,
675
+ largestContextWindow,
676
+ middleChars: middleContent.length
677
+ });
678
+ }
679
+
680
+ // Build summary prompt
681
+ const summaryPrompt = this.summaryPromptTemplate
682
+ .replace('{middle_segment}', middleContent);
683
+
684
+ // Estimate tokens for model selection
685
+ const finalEstimatedTokens = Math.ceil(middleContent.length / COMPACTION_CONFIG.CHARS_PER_TOKEN_ESTIMATE);
686
+
687
+ // Filter to models with sufficient context
688
+ const requiredContext = finalEstimatedTokens + 10000;
689
+ const capableModels = models.filter(m => {
690
+ const modelContext = contextWindows[m] || 128000;
691
+ return modelContext >= requiredContext;
692
+ });
693
+
694
+ const selectedModels = capableModels.length > 0 ? capableModels : models;
695
+
696
+ this.logger.info('Compaction model selection', {
697
+ finalEstimatedTokens,
698
+ requiredContext,
699
+ validatedModelsCount: models.length,
700
+ capableModelsCount: capableModels.length,
701
+ selectedModels
702
+ });
703
+
704
+ const modelsAttempted = [];
705
+ let lastError = null;
706
+
707
+ for (let attempt = 0; attempt < selectedModels.length; attempt++) {
708
+ const compactionModel = selectedModels[attempt];
709
+ modelsAttempted.push(compactionModel);
710
+
711
+ try {
712
+ this.logger.info('Generating summary', {
713
+ compactionModel,
714
+ attempt: attempt + 1,
715
+ totalModels: selectedModels.length,
716
+ middleMessageCount: middleMessages.length,
717
+ passNumber: options.passNumber || 1
718
+ });
719
+
720
+ // Call AI service (skipCircuitBreaker prevents compaction failures from blocking the agent)
721
+ const response = await this.aiService.sendMessage(
722
+ compactionModel,
723
+ summaryPrompt,
724
+ {
725
+ systemPrompt: 'You are a conversation summarization expert. Your goal is to compress conversations while preserving critical information for continued interaction.',
726
+ maxTokens: COMPACTION_CONFIG.MAX_SUMMARY_TOKENS,
727
+ temperature: 0.3,
728
+ sessionId: options.sessionId,
729
+ platformProvided: true,
730
+ skipCircuitBreaker: true
731
+ }
732
+ );
733
+
734
+ const summaryContent = response.content.trim();
735
+
736
+ // Build index range string
737
+ const indexRange = (options.middleStartIndex !== undefined && options.middleEndIndex !== undefined)
738
+ ? `original messages ${options.middleStartIndex}-${options.middleEndIndex}`
739
+ : `${middleMessages.length} messages`;
740
+
741
+ this.logger.info('Summary generated successfully', {
742
+ compactionModel,
743
+ attempt: attempt + 1,
744
+ originalLength: middleContent.length,
745
+ summaryLength: summaryContent.length,
746
+ compressionRatio: (summaryContent.length / middleContent.length * 100).toFixed(2) + '%',
747
+ indexRange,
748
+ passNumber: options.passNumber || 1
749
+ });
750
+
751
+ // Advance round-robin index
752
+ this._advanceCompactionModelIndex();
753
+
754
+ return {
755
+ role: 'system',
756
+ content: `${COMPACTION_CONFIG.COMPACTION_SUMMARY_PREFIX} - ${indexRange}]\n\n${summaryContent}\n\n${COMPACTION_CONFIG.COMPACTION_SUMMARY_SUFFIX}`,
757
+ type: 'summary',
758
+ timestamp: new Date().toISOString(),
759
+ metadata: {
760
+ originalMessageCount: middleMessages.length,
761
+ originalStartIndex: options.middleStartIndex,
762
+ originalEndIndex: options.middleEndIndex,
763
+ compactionModel,
764
+ passNumber: options.passNumber || 1
765
+ }
766
+ };
767
+
768
+ } catch (error) {
769
+ lastError = error;
770
+ const isRateLimit = error.message?.includes('429') || error.message?.includes('rate limit');
771
+
772
+ this.logger.warn('Summary generation failed, trying next model', {
773
+ compactionModel,
774
+ attempt: attempt + 1,
775
+ remainingModels: selectedModels.length - attempt - 1,
776
+ isRateLimit,
777
+ error: error.message
778
+ });
779
+ }
780
+ }
781
+
782
+ // All models exhausted
783
+ const errorDetails = {
784
+ modelsAttempted,
785
+ lastError: lastError?.message,
786
+ middleMessageCount: middleMessages.length,
787
+ isRateLimitIssue: lastError?.message?.includes('429') || lastError?.message?.includes('rate limit')
788
+ };
789
+
790
+ this.logger.error('All compaction models exhausted', errorDetails);
791
+
792
+ if (options.onAllModelsExhausted) {
793
+ options.onAllModelsExhausted({
794
+ type: 'compaction_models_exhausted',
795
+ message: `Conversation compaction failed: All ${selectedModels.length} models are currently unavailable. ${errorDetails.isRateLimitIssue ? 'Rate limits may be in effect.' : ''} Using structural fallback compaction.`,
796
+ models: modelsAttempted,
797
+ error: lastError?.message
798
+ });
799
+ }
800
+
801
+ this._advanceCompactionModelIndex();
802
+
803
+ const exhaustedError = new Error('ALL_MODELS_EXHAUSTED');
804
+ exhaustedError.code = 'ALL_MODELS_EXHAUSTED';
805
+ exhaustedError.details = errorDetails;
806
+ throw exhaustedError;
807
+ }
808
+
809
+ /**
810
+ * Perform structural fallback compaction when all AI models are unavailable.
811
+ * No AI call required - pure structural transformation:
812
+ * 1. Remove all system messages (except the first/main one)
813
+ * 2. Remove tool results
814
+ * 3. Keep beginning + end of remaining messages
815
+ * 4. Replace middle with a short paragraph
816
+ * @param {Array} allMessages - The full original messages array
817
+ * @returns {Object} { compactedMessages, metadata }
818
+ * @private
819
+ */
820
+ _performFallbackCompaction(allMessages) {
821
+ // 1. Identify the main system message
822
+ const mainSystemMsg = allMessages.find(m => m.role === 'system' && m.type !== 'summary');
823
+
824
+ // 2. Filter out system messages (except main) and tool results
825
+ const filteredMessages = allMessages.filter(m => {
826
+ if (m === mainSystemMsg) return true;
827
+ if (m.role === 'system') return false;
828
+ if (m.type === 'tool_result' || m.role === 'tool') return false;
829
+ return true;
830
+ });
831
+
832
+ const removedSystemCount = allMessages.filter(m => m.role === 'system' && m !== mainSystemMsg).length;
833
+ const removedToolCount = allMessages.filter(m => m.type === 'tool_result' || m.role === 'tool').length;
834
+
835
+ // 3. Apply sandwich using segment identification
836
+ const segments = this._identifySegments(filteredMessages);
837
+
838
+ // 4. Build replacement paragraph for middle section
839
+ const middleSummary = this._buildFallbackMiddleParagraph(segments.middle, {
840
+ removedSystemCount,
841
+ removedToolCount,
842
+ totalOriginalMessages: allMessages.length
843
+ });
844
+
845
+ // 5. Create the summary message
846
+ const summaryMessage = {
847
+ role: 'system',
848
+ content: `${COMPACTION_CONFIG.COMPACTION_SUMMARY_PREFIX} - structural fallback]\n\n${middleSummary}\n\n${COMPACTION_CONFIG.COMPACTION_SUMMARY_SUFFIX}`,
849
+ type: 'summary',
850
+ timestamp: new Date().toISOString(),
851
+ metadata: {
852
+ fallback: true,
853
+ structural: true,
854
+ removedSystemMessages: removedSystemCount,
855
+ removedToolResults: removedToolCount,
856
+ middleMessagesCompacted: segments.middle.length
857
+ }
858
+ };
859
+
860
+ // 6. Reconstruct conversation
861
+ const compactedMessages = [
862
+ ...segments.beginning,
863
+ summaryMessage,
864
+ ...segments.end
865
+ ];
866
+
867
+ this.logger.info('Structural fallback compaction performed', {
868
+ originalMessages: allMessages.length,
869
+ afterFiltering: filteredMessages.length,
870
+ removedSystemMessages: removedSystemCount,
871
+ removedToolResults: removedToolCount,
872
+ beginningKept: segments.beginning.length,
873
+ middleCompacted: segments.middle.length,
874
+ endKept: segments.end.length,
875
+ finalMessages: compactedMessages.length
876
+ });
877
+
878
+ return {
879
+ compactedMessages,
880
+ metadata: {
881
+ strategy: 'structural_fallback',
882
+ removedSystemMessages: removedSystemCount,
883
+ removedToolResults: removedToolCount,
884
+ middleMessagesCompacted: segments.middle.length
885
+ }
886
+ };
887
+ }
888
+
889
+ /**
890
+ * Build a short paragraph summarizing the middle section for fallback compaction
891
+ * @private
892
+ */
893
+ _buildFallbackMiddleParagraph(middleMessages, stats) {
894
+ const userMsgs = middleMessages.filter(m => m.role === 'user');
895
+ const assistantMsgs = middleMessages.filter(m => m.role === 'assistant');
896
+
897
+ // Extract file paths mentioned in messages
898
+ const filePathRegex = /(?:\/[\w.-]+)+\.\w+|(?:[A-Za-z]:)?(?:\\[\w.-]+)+\.\w+|(?:src|lib|test|config|public|dist|build|node_modules)\/[\w./\-]+/g;
899
+ const filePaths = new Set();
900
+ for (const msg of middleMessages) {
901
+ const content = typeof msg.content === 'string' ? msg.content : '';
902
+ const matches = content.match(filePathRegex);
903
+ if (matches) {
904
+ matches.forEach(p => filePaths.add(p));
905
+ }
906
+ }
907
+
908
+ const parts = [];
909
+ parts.push(`[${middleMessages.length} messages compacted (${userMsgs.length} user, ${assistantMsgs.length} assistant).`);
910
+
911
+ if (stats.removedSystemCount > 0 || stats.removedToolCount > 0) {
912
+ parts.push(`Additionally removed: ${stats.removedSystemCount} system messages, ${stats.removedToolCount} tool results.`);
913
+ }
914
+
915
+ if (filePaths.size > 0) {
916
+ const fileList = Array.from(filePaths).slice(0, 20).join(', ');
917
+ parts.push(`Files referenced: ${fileList}${filePaths.size > 20 ? ` and ${filePaths.size - 20} more` : ''}.`);
918
+ }
919
+
920
+ parts.push('Summary generation was unavailable - content structurally compacted for context management.]');
921
+
922
+ return parts.join(' ');
923
+ }
924
+
925
+ /**
926
+ * Split oversized messages into smaller chunks for effective compaction.
927
+ * When a single message exceeds OVERSIZED_MESSAGE_THRESHOLD, it gets split
928
+ * into chunks of MAX_CHUNK_SIZE, increasing message count so the sandwich
929
+ * strategy can push the content into the summarizable middle segment.
930
+ *
931
+ * @param {Array} messages - Messages array
932
+ * @returns {Array} Messages with oversized ones split into chunks
933
+ * @private
934
+ */
935
+ _splitOversizedMessages(messages) {
936
+ const threshold = COMPACTION_CONFIG.OVERSIZED_MESSAGE_THRESHOLD;
937
+ const maxChunk = COMPACTION_CONFIG.MAX_CHUNK_SIZE;
938
+
939
+ let splitCount = 0;
940
+ const result = [];
941
+
942
+ for (const msg of messages) {
943
+ const content = typeof msg.content === 'string' ? msg.content : '';
944
+
945
+ if (content.length <= threshold) {
946
+ result.push(msg);
947
+ continue;
948
+ }
949
+
950
+ // Split this message
951
+ splitCount++;
952
+ const chunks = this._splitContentIntoChunks(content, maxChunk);
953
+
954
+ this.logger.info('Splitting oversized message for compaction', {
955
+ role: msg.role,
956
+ type: msg.type || 'none',
957
+ originalChars: content.length,
958
+ chunks: chunks.length
959
+ });
960
+
961
+ for (let i = 0; i < chunks.length; i++) {
962
+ const chunkMsg = {
963
+ ...msg,
964
+ content: `[Part ${i + 1}/${chunks.length}${i === 0 ? ' — oversized message split for compaction' : ' — continued'}]\n${chunks[i]}`,
965
+ _splitMetadata: {
966
+ originalLength: content.length,
967
+ chunkIndex: i,
968
+ totalChunks: chunks.length
969
+ }
970
+ };
971
+ // Give each chunk a unique ID to avoid conflicts
972
+ if (msg.id) {
973
+ chunkMsg.id = `${msg.id}-chunk-${i + 1}`;
974
+ }
975
+ result.push(chunkMsg);
976
+ }
977
+ }
978
+
979
+ if (splitCount > 0) {
980
+ this.logger.info('Oversized message splitting complete', {
981
+ originalCount: messages.length,
982
+ newCount: result.length,
983
+ added: result.length - messages.length,
984
+ messagesSplit: splitCount
985
+ });
986
+ }
987
+
988
+ return { messages: result, wasSplit: splitCount > 0 };
989
+ }
990
+
991
+ /**
992
+ * Split content into chunks, respecting natural boundaries.
993
+ * Priority: double newlines > single newlines > hard cut at maxChunk.
994
+ *
995
+ * @param {string} content - Content to split
996
+ * @param {number} maxChunk - Maximum chunk size in chars
997
+ * @returns {string[]} Array of content chunks
998
+ * @private
999
+ */
1000
+ _splitContentIntoChunks(content, maxChunk) {
1001
+ if (content.length <= maxChunk) return [content];
1002
+
1003
+ const chunks = [];
1004
+ let remaining = content;
1005
+
1006
+ while (remaining.length > maxChunk) {
1007
+ let splitAt = -1;
1008
+
1009
+ // Try double newline within the chunk range
1010
+ const searchRange = remaining.substring(0, maxChunk);
1011
+ const lastDoubleNL = searchRange.lastIndexOf('\n\n');
1012
+ if (lastDoubleNL > maxChunk * 0.3) {
1013
+ splitAt = lastDoubleNL + 2;
1014
+ }
1015
+
1016
+ // Fallback: single newline
1017
+ if (splitAt === -1) {
1018
+ const lastNL = searchRange.lastIndexOf('\n');
1019
+ if (lastNL > maxChunk * 0.3) {
1020
+ splitAt = lastNL + 1;
1021
+ }
1022
+ }
1023
+
1024
+ // Fallback: sentence boundary (". " — common in prose, rare in code)
1025
+ if (splitAt === -1) {
1026
+ const lastSentence = searchRange.lastIndexOf('. ');
1027
+ if (lastSentence > maxChunk * 0.3) {
1028
+ splitAt = lastSentence + 2;
1029
+ }
1030
+ }
1031
+
1032
+ // Fallback: hard cut (e.g. minified code with no newlines)
1033
+ // TODO: Consider splitting at last space or semicolon before maxChunk
1034
+ // to avoid breaking mid-token/mid-word in minified code
1035
+ if (splitAt === -1) {
1036
+ splitAt = maxChunk;
1037
+ }
1038
+
1039
+ chunks.push(remaining.substring(0, splitAt));
1040
+ remaining = remaining.substring(splitAt);
1041
+ }
1042
+
1043
+ if (remaining.length > 0) {
1044
+ chunks.push(remaining);
1045
+ }
1046
+
1047
+ return chunks;
1048
+ }
1049
+
1050
+ /**
1051
+ * Create summary prompt template with preservation guidelines
1052
+ * @private
1053
+ */
1054
+ _createSummaryPromptTemplate() {
1055
+ return `You are compacting a conversation to preserve critical information while reducing token count.
1056
+
1057
+ CONTEXT: You are summarizing the EARLIEST portion of a conversation. The most recent messages are preserved separately. Your summary should capture what was accomplished, key decisions, and any information still relevant for continuation. Focus on outcomes over process.
1058
+
1059
+ PRESERVATION GUIDELINES:
1060
+
1061
+ HIGH PRIORITY (Always Preserve):
1062
+ - User requests and goals: What the user asked for, their stated preferences, and desired outcomes — these drive all ongoing work
1063
+ - Current task and next steps: What the agent is actively working on and what remains to be done
1064
+ - Recent achievements and current status: What was accomplished, what state the work is in now
1065
+ - Files created or modified successfully: Full file paths that were written, created, or changed
1066
+ - Meaningful tool invocations and their outcomes: Tool calls that produced important results or side effects
1067
+ - Future reference value: Information likely to be referenced again
1068
+ - Decisions and reasoning: WHY things were decided, not just what
1069
+ - API signatures and interfaces: Function definitions, method calls
1070
+ - Active dependencies: Information that ongoing work relies on
1071
+ - Error patterns and solutions: What failed and how it was fixed
1072
+ - Key facts and data: Specific numbers, names, configurations
1073
+
1074
+ MEDIUM PRIORITY (Compress Intelligently):
1075
+ - Code blocks: Keep function signatures + brief description, compress implementation details
1076
+ - Working solutions: Essence and outcome, not every implementation step
1077
+ - Failed attempts: Brief mention of what didn't work and why, skip detailed troubleshooting
1078
+ - Repetitive content: Consolidate similar examples or explanations
1079
+
1080
+ LOW PRIORITY (Heavily Compress/Remove):
1081
+ - Completed calculations: Keep results, skip intermediate steps
1082
+ - Verbose explanations: Summarize well-known concepts
1083
+ - Debug output: Skip terminal logs and error messages that served their purpose
1084
+ - Trial-and-error sequences: Skip multiple failed attempts with no lasting value
1085
+ - Acknowledgments and pleasantries: Skip "thank you", "sure", "okay" type exchanges
1086
+
1087
+ CONVERSATION SEGMENT TO SUMMARIZE:
1088
+ {middle_segment}
1089
+
1090
+ TASK: Create a concise summary that preserves logical flow and critical information. Focus on:
1091
+ 1. Key decisions and their reasoning
1092
+ 2. Important facts, data, and configurations
1093
+ 3. Active context needed for continuation
1094
+ 4. Problem-solving outcomes (skip the debugging process)
1095
+ 5. Dependencies and interfaces that code/work relies on
1096
+
1097
+ Someone reading this should understand the conversation progression and have all information needed for effective continuation.
1098
+
1099
+ OUTPUT: Provide ONLY the summary text without preamble, explanation, or meta-commentary.`;
1100
+ }
1101
+ }
1102
+
1103
+ export default ConversationCompactionService;