onbuzz 4.9.13 → 4.10.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 (451) hide show
  1. package/node_modules/glob/README.md +31 -5
  2. package/node_modules/glob/dist/commonjs/glob.d.ts +8 -0
  3. package/node_modules/glob/dist/commonjs/glob.d.ts.map +1 -1
  4. package/node_modules/glob/dist/commonjs/glob.js +2 -1
  5. package/node_modules/glob/dist/commonjs/glob.js.map +1 -1
  6. package/node_modules/glob/dist/commonjs/index.min.js +3 -3
  7. package/node_modules/glob/dist/commonjs/index.min.js.map +4 -4
  8. package/node_modules/glob/dist/commonjs/pattern.d.ts +3 -0
  9. package/node_modules/glob/dist/commonjs/pattern.d.ts.map +1 -1
  10. package/node_modules/glob/dist/commonjs/pattern.js +4 -0
  11. package/node_modules/glob/dist/commonjs/pattern.js.map +1 -1
  12. package/node_modules/glob/dist/esm/glob.d.ts +8 -0
  13. package/node_modules/glob/dist/esm/glob.d.ts.map +1 -1
  14. package/node_modules/glob/dist/esm/glob.js +2 -1
  15. package/node_modules/glob/dist/esm/glob.js.map +1 -1
  16. package/node_modules/glob/dist/esm/index.min.js +3 -3
  17. package/node_modules/glob/dist/esm/index.min.js.map +4 -4
  18. package/node_modules/glob/dist/esm/pattern.d.ts +3 -0
  19. package/node_modules/glob/dist/esm/pattern.d.ts.map +1 -1
  20. package/node_modules/glob/dist/esm/pattern.js +4 -0
  21. package/node_modules/glob/dist/esm/pattern.js.map +1 -1
  22. package/node_modules/{@isaacs → glob/node_modules}/balanced-match/README.md +7 -10
  23. package/node_modules/{@isaacs → glob/node_modules}/balanced-match/package.json +7 -18
  24. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/README.md +3 -6
  25. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.js +6 -4
  26. package/node_modules/glob/node_modules/brace-expansion/dist/commonjs/index.js.map +1 -0
  27. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.js +6 -4
  28. package/node_modules/glob/node_modules/brace-expansion/dist/esm/index.js.map +1 -0
  29. package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/package.json +11 -7
  30. package/node_modules/glob/node_modules/minimatch/README.md +76 -1
  31. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts +1 -1
  32. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.d.ts.map +1 -1
  33. package/node_modules/glob/node_modules/minimatch/dist/commonjs/assert-valid-pattern.js.map +1 -1
  34. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts +4 -2
  35. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.d.ts.map +1 -1
  36. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js +309 -55
  37. package/node_modules/glob/node_modules/minimatch/dist/commonjs/ast.js.map +1 -1
  38. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.d.ts.map +1 -1
  39. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js +2 -4
  40. package/node_modules/glob/node_modules/minimatch/dist/commonjs/brace-expressions.js.map +1 -1
  41. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts +1 -1
  42. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.d.ts.map +1 -1
  43. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js +4 -4
  44. package/node_modules/glob/node_modules/minimatch/dist/commonjs/escape.js.map +1 -1
  45. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts +81 -1
  46. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.d.ts.map +1 -1
  47. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js +232 -134
  48. package/node_modules/glob/node_modules/minimatch/dist/commonjs/index.js.map +1 -1
  49. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts +1 -1
  50. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.d.ts.map +1 -1
  51. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js +8 -8
  52. package/node_modules/glob/node_modules/minimatch/dist/commonjs/unescape.js.map +1 -1
  53. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts +1 -1
  54. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.d.ts.map +1 -1
  55. package/node_modules/glob/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -1
  56. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts +4 -2
  57. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.d.ts.map +1 -1
  58. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js +309 -55
  59. package/node_modules/glob/node_modules/minimatch/dist/esm/ast.js.map +1 -1
  60. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.d.ts.map +1 -1
  61. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js +2 -4
  62. package/node_modules/glob/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -1
  63. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts +1 -1
  64. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.d.ts.map +1 -1
  65. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js +4 -4
  66. package/node_modules/glob/node_modules/minimatch/dist/esm/escape.js.map +1 -1
  67. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts +81 -1
  68. package/node_modules/glob/node_modules/minimatch/dist/esm/index.d.ts.map +1 -1
  69. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js +232 -134
  70. package/node_modules/glob/node_modules/minimatch/dist/esm/index.js.map +1 -1
  71. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts +1 -1
  72. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.d.ts.map +1 -1
  73. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js +8 -8
  74. package/node_modules/glob/node_modules/minimatch/dist/esm/unescape.js.map +1 -1
  75. package/node_modules/glob/node_modules/minimatch/package.json +17 -11
  76. package/node_modules/glob/package.json +10 -13
  77. package/node_modules/minipass/LICENSE.md +55 -0
  78. package/node_modules/minipass/dist/commonjs/index.d.ts +12 -16
  79. package/node_modules/minipass/dist/commonjs/index.d.ts.map +1 -1
  80. package/node_modules/minipass/dist/commonjs/index.js +13 -3
  81. package/node_modules/minipass/dist/commonjs/index.js.map +1 -1
  82. package/node_modules/minipass/dist/esm/index.d.ts +12 -16
  83. package/node_modules/minipass/dist/esm/index.d.ts.map +1 -1
  84. package/node_modules/minipass/dist/esm/index.js +3 -1
  85. package/node_modules/minipass/dist/esm/index.js.map +1 -1
  86. package/node_modules/minipass/package.json +9 -14
  87. package/node_modules/path-scurry/node_modules/lru-cache/README.md +96 -10
  88. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.d.ts.map +1 -0
  89. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel-browser.js.map +1 -0
  90. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.d.ts +5 -0
  91. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/diagnostics-channel.js +7 -0
  92. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts +1400 -0
  93. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.d.ts.map +1 -0
  94. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js +1726 -0
  95. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.js.map +1 -0
  96. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js +2 -0
  97. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/index.min.js.map +7 -0
  98. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts +12 -0
  99. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.d.ts.map +1 -0
  100. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js +10 -0
  101. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/browser/perf.js.map +1 -0
  102. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.cjs.map +1 -0
  103. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel-cjs.d.cts.map +1 -0
  104. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.d.ts +5 -0
  105. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/diagnostics-channel.js +7 -0
  106. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts +109 -32
  107. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.d.ts.map +1 -1
  108. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js +334 -197
  109. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.js.map +1 -1
  110. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js +1 -1
  111. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/index.min.js.map +4 -4
  112. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.d.ts.map +1 -0
  113. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel-node.js.map +1 -0
  114. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.d.ts +5 -0
  115. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/diagnostics-channel.js +9 -0
  116. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts +1400 -0
  117. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.d.ts.map +1 -0
  118. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js +1726 -0
  119. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.js.map +1 -0
  120. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js +2 -0
  121. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/index.min.js.map +7 -0
  122. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts +12 -0
  123. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.d.ts.map +1 -0
  124. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js +10 -0
  125. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/node/perf.js.map +1 -0
  126. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts +12 -0
  127. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.d.ts.map +1 -0
  128. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js +10 -0
  129. package/node_modules/path-scurry/node_modules/lru-cache/dist/commonjs/perf.js.map +1 -0
  130. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.d.ts.map +1 -0
  131. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel-browser.js.map +1 -0
  132. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.d.ts +5 -0
  133. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/diagnostics-channel.js +4 -0
  134. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts +1400 -0
  135. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.d.ts.map +1 -0
  136. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js +1722 -0
  137. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.js.map +1 -0
  138. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js +2 -0
  139. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/index.min.js.map +7 -0
  140. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts +12 -0
  141. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.d.ts.map +1 -0
  142. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js +7 -0
  143. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/browser/perf.js.map +1 -0
  144. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.d.mts.map +1 -0
  145. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel-esm.mjs.map +1 -0
  146. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.d.ts +5 -0
  147. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/diagnostics-channel.js +19 -0
  148. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts +109 -32
  149. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.d.ts.map +1 -1
  150. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js +333 -196
  151. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.js.map +1 -1
  152. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js +1 -1
  153. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/index.min.js.map +4 -4
  154. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.d.ts.map +1 -0
  155. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel-node.js.map +1 -0
  156. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.d.ts +5 -0
  157. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/diagnostics-channel.js +6 -0
  158. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts +1400 -0
  159. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.d.ts.map +1 -0
  160. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js +1722 -0
  161. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.js.map +1 -0
  162. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js +2 -0
  163. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/index.min.js.map +7 -0
  164. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts +12 -0
  165. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.d.ts.map +1 -0
  166. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js +7 -0
  167. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/node/perf.js.map +1 -0
  168. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts +12 -0
  169. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.d.ts.map +1 -0
  170. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js +7 -0
  171. package/node_modules/path-scurry/node_modules/lru-cache/dist/esm/perf.js.map +1 -0
  172. package/node_modules/path-scurry/node_modules/lru-cache/package.json +71 -18
  173. package/node_modules/path-scurry/package.json +8 -24
  174. package/package.json +1 -1
  175. package/scripts/debug-balance-probe.mjs +35 -35
  176. package/scripts/push-image.sh +43 -43
  177. package/scripts/setup-acr.sh +65 -65
  178. package/scripts/verify-optional-deps.js +96 -1
  179. package/src/__tests__/composioCliFlags.test.js +239 -239
  180. package/src/analyzers/CSSAnalyzer.js +298 -297
  181. package/src/analyzers/ConfigValidator.js +691 -690
  182. package/src/analyzers/ESLintAnalyzer.js +320 -320
  183. package/src/analyzers/JavaScriptAnalyzer.js +260 -261
  184. package/src/analyzers/PrettierFormatter.js +246 -247
  185. package/src/analyzers/PythonAnalyzer.js +283 -283
  186. package/src/analyzers/SecurityAnalyzer.js +729 -729
  187. package/src/analyzers/SparrowAnalyzer.js +341 -341
  188. package/src/analyzers/TypeScriptAnalyzer.js +247 -247
  189. package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -41
  190. package/src/analyzers/__tests__/ConfigValidator.test.js +362 -362
  191. package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -40
  192. package/src/analyzers/__tests__/PythonAnalyzer.test.js +205 -208
  193. package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -303
  194. package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -187
  195. package/src/analyzers/codeCloneDetector/analyzer.js +344 -344
  196. package/src/analyzers/codeCloneDetector/detector.js +250 -250
  197. package/src/analyzers/codeCloneDetector/index.js +194 -192
  198. package/src/analyzers/codeCloneDetector/parser.js +199 -199
  199. package/src/core/__tests__/agentPool.test.js +866 -866
  200. package/src/core/__tests__/agentPoolAutoResume.test.js +209 -209
  201. package/src/core/__tests__/agentPoolWakeOnMessage.test.js +315 -315
  202. package/src/core/__tests__/agentScheduler.emptyResponseChatStall.test.js +213 -213
  203. package/src/core/__tests__/agentScheduler.errorCategorisation.test.js +246 -246
  204. package/src/core/__tests__/agentScheduler.firstChunkTimeout.test.js +138 -138
  205. package/src/core/__tests__/agentScheduler.modeTransitions.test.js +233 -233
  206. package/src/core/__tests__/agentScheduler.nativePromptPick.test.js +319 -319
  207. package/src/core/__tests__/agentScheduler.taskLifecycleInstruction.test.js +78 -78
  208. package/src/core/__tests__/agentScheduler.visualizer.test.js +258 -258
  209. package/src/core/__tests__/flowCheckpointStore.test.js +140 -140
  210. package/src/core/__tests__/flowEndToEnd.test.js +565 -565
  211. package/src/core/__tests__/flowFieldMapping.test.js +188 -189
  212. package/src/core/__tests__/flowLintClientMirror.test.js +96 -98
  213. package/src/core/__tests__/flowSavePayload.test.js +170 -169
  214. package/src/core/__tests__/flowTemplates.test.js +311 -311
  215. package/src/core/__tests__/flowVersionStore.test.js +123 -123
  216. package/src/core/__tests__/messageProcessor.test.js +669 -669
  217. package/src/core/__tests__/stateManager.test.js +0 -1
  218. package/src/core/agentPool.js +2474 -2475
  219. package/src/core/agentScheduler.js +1 -4
  220. package/src/core/contextManager.js +708 -708
  221. package/src/core/flowExecutor.js +1510 -1510
  222. package/src/core/flowFieldMapping.js +136 -138
  223. package/src/core/messageProcessor.js +953 -954
  224. package/src/core/orchestrator.js +593 -595
  225. package/src/core/stateManager.js +1765 -1752
  226. package/src/index.js +1221 -1221
  227. package/src/interfaces/__tests__/archivedAgentDelete.test.js +207 -207
  228. package/src/interfaces/__tests__/bulkAgentRoute.test.js +361 -361
  229. package/src/interfaces/__tests__/imageServing.test.js +228 -228
  230. package/src/interfaces/__tests__/remoteSessionAuth.test.js +308 -308
  231. package/src/interfaces/__tests__/videoJobsRoutes.test.js +178 -179
  232. package/src/interfaces/__tests__/webServer.marketplace.test.js +629 -629
  233. package/src/interfaces/schedulerRoutes.js +50 -50
  234. package/src/interfaces/terminal/__tests__/smoke/connection.test.js +341 -350
  235. package/src/interfaces/terminal/__tests__/smoke/enhancements.test.js +156 -156
  236. package/src/interfaces/terminal/__tests__/smoke/imports.test.js +325 -330
  237. package/src/interfaces/terminal/__tests__/smoke/tools.test.js +385 -388
  238. package/src/interfaces/terminal/api/session.js +265 -266
  239. package/src/interfaces/terminal/api/websocket.js +496 -497
  240. package/src/interfaces/terminal/components/AgentCreator.js +691 -705
  241. package/src/interfaces/terminal/components/AgentEditor.js +676 -678
  242. package/src/interfaces/terminal/components/AgentSwitcher.js +331 -330
  243. package/src/interfaces/terminal/components/ErrorPanel.js +263 -264
  244. package/src/interfaces/terminal/components/Header.js +28 -28
  245. package/src/interfaces/terminal/components/Layout.js +598 -603
  246. package/src/interfaces/terminal/components/MessageList.js +280 -281
  247. package/src/interfaces/terminal/components/SettingsPanel.js +410 -415
  248. package/src/interfaces/terminal/components/StatusBar.js +2 -0
  249. package/src/interfaces/terminal/index.js +168 -168
  250. package/src/interfaces/terminal/state/useAgentControl.js +496 -496
  251. package/src/interfaces/terminal/state/useAgents.js +537 -537
  252. package/src/interfaces/terminal/state/useMessages.js +629 -630
  253. package/src/interfaces/terminal/state/useTools.js +554 -554
  254. package/src/interfaces/terminal/utils/debugLogger.js +44 -44
  255. package/src/interfaces/terminal/utils/settingsStorage.js +232 -232
  256. package/src/interfaces/webServer.js +7578 -7579
  257. package/src/interfaces/webServer.js.bak +7046 -7046
  258. package/src/modules/fileExplorer/__tests__/zipDownload.test.js +237 -237
  259. package/src/modules/fileExplorer/controller.js +470 -469
  260. package/src/modules/fileExplorer/routes.js +285 -286
  261. package/src/modules/widget/__tests__/isDisabled.test.js +41 -41
  262. package/src/modules/widget/__tests__/routes.test.js +677 -678
  263. package/src/modules/widget/__tests__/runtime.test.js +401 -401
  264. package/src/modules/widget/__tests__/versioning.test.js +309 -309
  265. package/src/modules/widget/__tests__/webComponentRuntime.test.js +565 -565
  266. package/src/modules/widget/__tests__/widgetTool.test.js +316 -316
  267. package/src/modules/widget/routes.js +435 -435
  268. package/src/modules/widget/runtime/bundle.js +640 -640
  269. package/src/modules/widget/runtime/webComponentBundle.js +470 -470
  270. package/src/modules/widget/schema.js +182 -181
  271. package/src/modules/widget/widgetTool.js +1389 -1389
  272. package/src/services/__tests__/agentActivityService.test.js +401 -402
  273. package/src/services/__tests__/benchmarkService.test.js +184 -184
  274. package/src/services/__tests__/contextInjectionService.test.js +246 -246
  275. package/src/services/__tests__/conversationQuery.test.js +721 -723
  276. package/src/services/__tests__/credentialVault.test.js +469 -469
  277. package/src/services/__tests__/discordService.integration.test.js +638 -639
  278. package/src/services/__tests__/flowContextService.test.js +590 -590
  279. package/src/services/__tests__/memoryService.test.js +1 -1
  280. package/src/services/__tests__/messageSource.test.js +380 -380
  281. package/src/services/__tests__/modelRouterNaming.test.js +111 -111
  282. package/src/services/__tests__/projectDetector.test.js +34 -34
  283. package/src/services/__tests__/promptService.test.js +242 -242
  284. package/src/services/__tests__/telegramService.test.js +941 -941
  285. package/src/services/__tests__/tokenCountingService.test.js +48 -48
  286. package/src/services/agentActivityService.js +419 -420
  287. package/src/services/aiService.js +2997 -3001
  288. package/src/services/apiKeyManager.js +359 -359
  289. package/src/services/benchmarkService.js +196 -196
  290. package/src/services/codebaseKnowledgeService.js +2 -2
  291. package/src/services/composioService.js +738 -738
  292. package/src/services/conversationCompactionService.js +1258 -1257
  293. package/src/services/credentialVault.js +685 -685
  294. package/src/services/discordService.js +792 -793
  295. package/src/services/embeddings/__tests__/azureCustomProvider.test.js +232 -232
  296. package/src/services/embeddings/__tests__/embeddingService.test.js +417 -417
  297. package/src/services/embeddings/__tests__/localProvider.test.js +263 -263
  298. package/src/services/embeddings/autoRecall.js +218 -219
  299. package/src/services/embeddings/indexers/__tests__/agentIndexer.test.js +232 -232
  300. package/src/services/embeddings/indexers/__tests__/memoryIndexer.test.js +418 -418
  301. package/src/services/embeddings/indexers/__tests__/reminisceIndexer.test.js +356 -357
  302. package/src/services/embeddings/indexers/__tests__/skillsIndexer.test.js +145 -145
  303. package/src/services/embeddings/indexers/__tests__/taskIndexer.test.js +146 -146
  304. package/src/services/embeddings/indexers/composioIndexer.js +279 -279
  305. package/src/services/embeddings/providerInterface.js +206 -206
  306. package/src/services/embeddings/providers/localProvider.js +11 -7
  307. package/src/services/embeddings/providers/openaiProvider.js +101 -101
  308. package/src/services/embeddings/vectorStore/inMemoryJsonStore.js +356 -356
  309. package/src/services/errorHandler.js +809 -809
  310. package/src/services/flowContextService.js +586 -586
  311. package/src/services/grounding/MockAdapter.js +125 -125
  312. package/src/services/modelRouterService.js +26 -31
  313. package/src/services/modelsService.js +322 -322
  314. package/src/services/ollamaService.js +452 -452
  315. package/src/services/projectDetector.js +403 -404
  316. package/src/services/promptService.js +418 -418
  317. package/src/services/qualityInspector.js +795 -795
  318. package/src/services/scheduleService.js +726 -726
  319. package/src/services/serviceRegistry.js +386 -386
  320. package/src/services/telegrafBot.js +174 -174
  321. package/src/services/telegramService.js +1972 -1972
  322. package/src/services/visualEditorBridge.js +1033 -1033
  323. package/src/services/visualEditorServer.js +1769 -1774
  324. package/src/services/whatsappService.js +667 -668
  325. package/src/tools/__tests__/agentCommunicationTool.findAgent.test.js +226 -226
  326. package/src/tools/__tests__/agentCommunicationTool.test.js +3 -3
  327. package/src/tools/__tests__/agentDelayTool.test.js +342 -342
  328. package/src/tools/__tests__/baseTool.test.js +3 -3
  329. package/src/tools/__tests__/codeMapTool.test.js +915 -915
  330. package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -309
  331. package/src/tools/__tests__/fileTreeTool.test.js +274 -274
  332. package/src/tools/__tests__/filesystemTool.test.js +815 -815
  333. package/src/tools/__tests__/foundryWebSearchTool.test.js +252 -252
  334. package/src/tools/__tests__/imageTool.validator.test.js +194 -194
  335. package/src/tools/__tests__/jobDoneTool.test.js +580 -581
  336. package/src/tools/__tests__/memoryTool.forgetStale.test.js +272 -272
  337. package/src/tools/__tests__/memoryTool.reminisce.test.js +2 -2
  338. package/src/tools/__tests__/memoryTool.reminisceSemanticSearch.test.js +301 -301
  339. package/src/tools/__tests__/memoryTool.semanticSearch.test.js +405 -405
  340. package/src/tools/__tests__/memoryTool.teamPool.test.js +293 -293
  341. package/src/tools/__tests__/memoryTool.test.js +1 -1
  342. package/src/tools/__tests__/seekTool.test.js +282 -282
  343. package/src/tools/__tests__/skillsTool.search.test.js +164 -164
  344. package/src/tools/__tests__/skillsTool.test.js +226 -226
  345. package/src/tools/__tests__/staticAnalysisTool.test.js +509 -509
  346. package/src/tools/__tests__/taskManagerTool.discipline.test.js +137 -137
  347. package/src/tools/__tests__/taskManagerTool.search.test.js +143 -143
  348. package/src/tools/__tests__/taskManagerTool.test.js +866 -866
  349. package/src/tools/__tests__/terminalTool.test.js +448 -448
  350. package/src/tools/__tests__/toolShapeForgiveness.test.js +259 -260
  351. package/src/tools/__tests__/userPromptTool.test.js +297 -297
  352. package/src/tools/__tests__/videoTool.jobs.test.js +147 -147
  353. package/src/tools/__tests__/webTool.e2e.test.js +609 -603
  354. package/src/tools/__tests__/webTool.unit.test.js +195 -195
  355. package/src/tools/__tests__/webTool.visionModel.test.js +75 -75
  356. package/src/tools/agentCommunicationTool.js +8 -10
  357. package/src/tools/agentDelayTool.js +496 -497
  358. package/src/tools/asyncToolManager.js +602 -603
  359. package/src/tools/baseTool.js +12 -11
  360. package/src/tools/cloneDetectionTool.js +576 -581
  361. package/src/tools/codeMapTool.js +0 -6
  362. package/src/tools/composioTool.js +617 -617
  363. package/src/tools/dependencyResolverTool.js +1211 -1212
  364. package/src/tools/desktop/DesktopTool.js +629 -638
  365. package/src/tools/desktop/__tests__/DesktopTool.e2e.test.js +306 -306
  366. package/src/tools/desktop/__tests__/DesktopTool.test.js +507 -507
  367. package/src/tools/desktop/__tests__/osController.test.js +364 -364
  368. package/src/tools/desktop/osController.js +491 -491
  369. package/src/tools/docxTool.js +623 -623
  370. package/src/tools/excelTool.js +636 -636
  371. package/src/tools/fileContentReplaceTool.js +5 -7
  372. package/src/tools/fileSystemTool.js +12 -19
  373. package/src/tools/fileTreeTool.js +840 -840
  374. package/src/tools/foundryWebSearchTool.js +273 -273
  375. package/src/tools/helpTool.js +198 -198
  376. package/src/tools/imageTool.js +1397 -1397
  377. package/src/tools/importAnalyzerTool.js +1056 -1056
  378. package/src/tools/jobDoneTool.js +495 -495
  379. package/src/tools/memoryTool.js +1 -1
  380. package/src/tools/office/pres/__tests__/presSystem.test.js +365 -365
  381. package/src/tools/office/pres/archetypes/agenda.js +61 -61
  382. package/src/tools/office/pres/archetypes/bentoGrid.js +218 -219
  383. package/src/tools/office/pres/archetypes/bigStat.js +140 -142
  384. package/src/tools/office/pres/archetypes/closing.js +70 -70
  385. package/src/tools/office/pres/archetypes/hero.js +70 -70
  386. package/src/tools/office/pres/archetypes/productHero.js +93 -94
  387. package/src/tools/office/pres/archetypes/table.js +73 -74
  388. package/src/tools/office/pres/backgrounds/orb.js +66 -66
  389. package/src/tools/office/pres/components.js +422 -423
  390. package/src/tools/officeTool.js +441 -441
  391. package/src/tools/pdfTool.js +625 -627
  392. package/src/tools/platformControlTool.js +1081 -1081
  393. package/src/tools/seekTool.js +917 -918
  394. package/src/tools/skillsTool.js +1 -1
  395. package/src/tools/staticAnalysisTool.js +2143 -2146
  396. package/src/tools/taskManagerTool.js +3324 -3324
  397. package/src/tools/terminalTool.js +2615 -2618
  398. package/src/tools/videoTool.js +1303 -1303
  399. package/src/tools/visionTool.js +508 -508
  400. package/src/tools/visualEditorTool.js +1289 -1290
  401. package/src/tools/webTool.js +3368 -3368
  402. package/src/tools/whatsappTool.js +464 -464
  403. package/src/types/__tests__/agent.test.js +499 -499
  404. package/src/types/__tests__/contextReference.test.js +606 -606
  405. package/src/types/__tests__/conversation.test.js +555 -555
  406. package/src/types/__tests__/toolCommand.test.js +584 -584
  407. package/src/types/contextReference.js +974 -971
  408. package/src/types/conversation.js +729 -729
  409. package/src/types/toolCommand.js +746 -746
  410. package/src/utilities/__tests__/attachmentValidator.test.js +80 -80
  411. package/src/utilities/__tests__/auditReport.test.js +328 -328
  412. package/src/utilities/__tests__/directoryAccessManager.test.js +388 -388
  413. package/src/utilities/__tests__/jsonRepair.test.js +103 -104
  414. package/src/utilities/__tests__/modeTransitionReasons.test.js +105 -105
  415. package/src/utilities/__tests__/platformUtils.test.js +80 -87
  416. package/src/utilities/__tests__/structuredFileValidator.test.js +261 -263
  417. package/src/utilities/__tests__/toolConstants.test.js +92 -94
  418. package/src/utilities/__tests__/useIsTouchDevice.detect.test.js +114 -114
  419. package/src/utilities/__tests__/webUiUtilSync.test.js +117 -117
  420. package/src/utilities/attachmentValidator.js +284 -288
  421. package/src/utilities/authCache.js.backup-1779570472481 +121 -121
  422. package/src/utilities/browserStealth.js +631 -630
  423. package/src/utilities/configManager.js +616 -617
  424. package/src/utilities/directoryAccessManager.js +564 -565
  425. package/src/utilities/fileProcessor.js +308 -307
  426. package/src/utilities/humanBehavior.js +454 -453
  427. package/src/utilities/logger.js +479 -479
  428. package/src/utilities/structuredFileValidator.js +696 -699
  429. package/src/utilities/tagParser.js +5 -10
  430. package/src/utilities/userDataDir.js +308 -308
  431. package/node_modules/@isaacs/brace-expansion/dist/commonjs/index.js.map +0 -1
  432. package/node_modules/@isaacs/brace-expansion/dist/esm/index.js.map +0 -1
  433. package/node_modules/minipass/LICENSE +0 -15
  434. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/LICENSE.md +0 -0
  435. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts +0 -0
  436. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.d.ts.map +0 -0
  437. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js +0 -0
  438. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/index.js.map +0 -0
  439. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/commonjs/package.json +0 -0
  440. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts +0 -0
  441. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.d.ts.map +0 -0
  442. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js +0 -0
  443. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/index.js.map +0 -0
  444. /package/node_modules/{@isaacs → glob/node_modules}/balanced-match/dist/esm/package.json +0 -0
  445. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/LICENSE +0 -0
  446. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts +0 -0
  447. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/index.d.ts.map +0 -0
  448. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/commonjs/package.json +0 -0
  449. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts +0 -0
  450. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/index.d.ts.map +0 -0
  451. /package/node_modules/{@isaacs → glob/node_modules}/brace-expansion/dist/esm/package.json +0 -0
package/src/index.js CHANGED
@@ -1,1222 +1,1222 @@
1
- /**
2
- * Loxia Autopilot One - Main Application Entry Point
3
- *
4
- * Purpose:
5
- * - Initialize all system components
6
- * - Setup dependency injection
7
- * - Start interface handlers
8
- * - Handle graceful shutdown
9
- */
10
-
11
- import path from 'path';
12
- import { exec } from 'child_process';
13
- import { createLogger } from './utilities/logger.js';
14
- import { resolveModuleFilename } from './utilities/esmCjsPath.js';
15
- import { createConfigManager } from './utilities/configManager.js';
16
- import Orchestrator from './core/orchestrator.js';
17
- import AgentPool from './core/agentPool.js';
18
- import MessageProcessor from './core/messageProcessor.js';
19
- import AgentScheduler from './core/agentScheduler.js';
20
- import ContextManager from './core/contextManager.js';
21
- import StateManager from './core/stateManager.js';
22
- import AIService from './services/aiService.js';
23
- import { EmbeddingService } from './services/embeddings/embeddingService.js';
24
- import { getUserDataPaths } from './utilities/userDataDir.js';
25
- import BudgetService from './services/budgetService.js';
26
- import ErrorHandler from './services/errorHandler.js';
27
- import BenchmarkService from './services/benchmarkService.js';
28
- import ModelRouterService from './services/modelRouterService.js';
29
- import ModelsService from './services/modelsService.js';
30
- import ApiKeyManager, { registerApiKeyManager } from './services/apiKeyManager.js';
31
- import { getCredentialVault } from './services/credentialVault.js';
32
- import FileAttachmentService from './services/fileAttachmentService.js';
33
- import { ToolsRegistry } from './tools/baseTool.js';
34
- import AgentDelayTool from './tools/agentDelayTool.js';
35
- import TerminalTool from './tools/terminalTool.js';
36
- import FileSystemTool from './tools/fileSystemTool.js';
37
- import JobDoneTool from './tools/jobDoneTool.js';
38
- import AgentCommunicationTool from './tools/agentCommunicationTool.js';
39
- import TaskManagerTool from './tools/taskManagerTool.js';
40
- import ImportAnalyzerTool from './tools/importAnalyzerTool.js';
41
- import DependencyResolverTool from './tools/dependencyResolverTool.js';
42
- import ImageTool from './tools/imageTool.js';
43
- import VideoTool from './tools/videoTool.js';
44
- import StaticAnalysisTool from './tools/staticAnalysisTool.js';
45
- import CloneDetectionTool from './tools/cloneDetectionTool.js';
46
- import FileTreeTool from './tools/fileTreeTool.js';
47
- import FileContentReplaceTool from './tools/fileContentReplaceTool.js';
48
- import SeekTool from './tools/seekTool.js';
49
- import WebTool from './tools/webTool.js';
50
- import FoundryWebSearchTool from './tools/foundryWebSearchTool.js';
51
- import VisualEditorTool from './tools/visualEditorTool.js';
52
- import PdfTool from './tools/pdfTool.js';
53
- import HelpTool from './tools/helpTool.js';
54
- import DocxTool from './tools/docxTool.js';
55
- import ExcelTool from './tools/excelTool.js';
56
- import OfficeTool from './tools/officeTool.js';
57
- import MemoryTool from './tools/memoryTool.js';
58
- import ComposioTool from './tools/composioTool.js';
59
- import SkillsTool from './tools/skillsTool.js';
60
- import VisionTool from './tools/visionTool.js';
61
- import UserPromptTool from './tools/userPromptTool.js';
62
- import CodeMapTool from './tools/codeMapTool.js';
63
- import WhatsAppTool from './tools/whatsappTool.js';
64
- import PlatformControlTool from './tools/platformControlTool.js';
65
- import { DesktopTool } from './tools/desktop/DesktopTool.js';
66
- import ScheduleService from './services/scheduleService.js';
67
- import AsyncToolManager from './tools/asyncToolManager.js';
68
- import WebServer from './interfaces/webServer.js';
69
-
70
- import {
71
- SYSTEM_VERSION,
72
- INTERFACE_TYPES
73
- } from './utilities/constants.js';
74
- import {
75
- getUserDataDir,
76
- ensureUserDataDirs,
77
- migrateFromOldLocation,
78
- getLegacyDataPaths
79
- } from './utilities/userDataDir.js';
80
-
81
- class LoxiaApplication {
82
- constructor() {
83
- this.logger = null;
84
- this.config = null;
85
- this.orchestrator = null;
86
- this.interfaces = new Map();
87
- this.isShuttingDown = false;
88
-
89
- // Bind shutdown handler
90
- this.shutdown = this.shutdown.bind(this);
91
- }
92
-
93
- /**
94
- * Initialize the application
95
- * @param {Object} options - Initialization options
96
- * @returns {Promise<void>}
97
- */
98
- async initialize(options = {}) {
99
- try {
100
- console.log(`🚀 Starting Loxia Autopilot One v${SYSTEM_VERSION}`);
101
-
102
- // Initialize configuration
103
- await this.initializeConfig(options);
104
-
105
- // Initialize logging
106
- await this.initializeLogging();
107
-
108
- this.logger.info('Loxia Autopilot One starting up', {
109
- version: SYSTEM_VERSION,
110
- nodeVersion: process.version,
111
- platform: process.platform,
112
- projectDir: options.projectDir || process.cwd()
113
- });
114
-
115
- // IMPORTANT: Initialize persistent user data directory and migrate legacy data
116
- // This ensures user data (agents, conversations, settings) survives npm updates
117
- await this.initializeUserDataDirectory();
118
-
119
- // Initialize core components
120
- await this.initializeCoreComponents();
121
-
122
- // Initialize tools
123
- await this.initializeTools();
124
-
125
- this.logger.info('Starting interface initialization...');
126
-
127
- // Initialize interfaces
128
- await this.initializeInterfaces(options);
129
-
130
- this.logger.info('Interface initialization completed');
131
-
132
- // Setup shutdown handlers
133
- this.setupShutdownHandlers();
134
-
135
- this.logger.info('Loxia Autopilot One startup complete');
136
- console.log('✅ Loxia Autopilot One is ready!');
137
-
138
- } catch (error) {
139
- console.error('❌ Failed to initialize Loxia Autopilot One:', error.message);
140
- if (this.logger) {
141
- this.logger.error('Application initialization failed', {
142
- error: error.message,
143
- stack: error.stack
144
- });
145
- }
146
- process.exit(1);
147
- }
148
- }
149
-
150
- /**
151
- * Initialize configuration management
152
- * @private
153
- */
154
- async initializeConfig(options) {
155
- const __filename = resolveModuleFilename(import.meta.url);
156
- const __dirname = path.dirname(__filename);
157
-
158
- const configPaths = [
159
- path.join(__dirname, '../config/default.json'),
160
- ...(options.configPaths || [])
161
- ];
162
-
163
- this.configManager = createConfigManager({
164
- configPaths,
165
- envPrefix: 'LOXIA'
166
- });
167
-
168
- this.config = await this.configManager.loadConfig();
169
-
170
- // Enable config watching if requested
171
- if (options.watchConfig) {
172
- await this.configManager.watchConfig(true);
173
- }
174
- }
175
-
176
- /**
177
- * Initialize logging system
178
- * @private
179
- */
180
- async initializeLogging() {
181
- const loggingConfig = this.config.logging || {};
182
-
183
- this.logger = createLogger({
184
- level: loggingConfig.level || 'info',
185
- outputs: loggingConfig.outputs || ['console'],
186
- colors: loggingConfig.colors !== false,
187
- timestamp: loggingConfig.timestamp !== false,
188
- logFile: loggingConfig.logFile,
189
- maxFileSize: loggingConfig.maxFileSize,
190
- maxFiles: loggingConfig.maxFiles
191
- });
192
-
193
- await this.logger.initialize();
194
- }
195
-
196
- /**
197
- * Initialize persistent user data directory and migrate legacy data
198
- * This ensures user data survives npm package updates
199
- * @private
200
- */
201
- async initializeUserDataDirectory() {
202
- try {
203
- // Create user data directory structure
204
- const paths = await ensureUserDataDirs();
205
- const userDataDir = getUserDataDir();
206
-
207
- this.logger.info('User data directory initialized', {
208
- location: userDataDir,
209
- platform: process.platform
210
- });
211
-
212
- // Check for legacy data and migrate if needed
213
- const legacyPaths = getLegacyDataPaths();
214
- for (const legacyPath of legacyPaths) {
215
- try {
216
- const result = await migrateFromOldLocation(legacyPath, {
217
- dryRun: false,
218
- logger: this.logger
219
- });
220
-
221
- if (result.migrated.length > 0) {
222
- this.logger.info('Successfully migrated data from legacy location', {
223
- from: legacyPath,
224
- migratedCount: result.migrated.length
225
- });
226
- console.log(`📦 Migrated ${result.migrated.length} items from legacy location to ${userDataDir}`);
227
- }
228
- } catch (migrationError) {
229
- // Non-fatal: log and continue
230
- this.logger.warn('Failed to migrate from legacy location', {
231
- legacyPath,
232
- error: migrationError.message
233
- });
234
- }
235
- }
236
-
237
- } catch (error) {
238
- this.logger.error('Failed to initialize user data directory', {
239
- error: error.message
240
- });
241
- throw error;
242
- }
243
- }
244
-
245
- /**
246
- * Initialize core system components
247
- * @private
248
- */
249
- async initializeCoreComponents() {
250
- this.logger.info('Initializing core components...');
251
-
252
- // State Manager
253
- this.stateManager = new StateManager(this.config, this.logger);
254
-
255
- // File Attachment Service
256
- this.fileAttachmentService = new FileAttachmentService(this.config, this.logger);
257
- await this.fileAttachmentService.initialize();
258
-
259
- // Context Manager
260
- this.contextManager = new ContextManager(this.config, this.logger);
261
-
262
- // Tools Registry and Async Tool Manager
263
- this.toolsRegistry = new ToolsRegistry(this.logger);
264
- this.asyncToolManager = new AsyncToolManager(this.config, this.logger);
265
-
266
- // Agent Pool (with tools registry for prompt enhancement)
267
- this.agentPool = new AgentPool(
268
- this.config,
269
- this.logger,
270
- this.stateManager,
271
- this.contextManager,
272
- this.toolsRegistry
273
- );
274
-
275
- // Initialize Budget Service and Error Handler
276
- this.budgetService = new BudgetService(this.config, this.logger);
277
- this.errorHandler = new ErrorHandler(this.config, this.logger);
278
-
279
- // API Key Manager
280
- this.apiKeyManager = new ApiKeyManager(this.logger);
281
- await this.apiKeyManager.initialize(); // Load persisted keys
282
- // Register the singleton so cross-cutting services (composioService,
283
- // etc.) can resolve vendor keys without explicit injection.
284
- registerApiKeyManager(this.apiKeyManager);
285
-
286
- // Credential Vault for secure website credential management
287
- this.credentialVault = getCredentialVault(this.logger);
288
- await this.credentialVault.initialize(); // Load persisted credentials
289
-
290
- // WhatsApp Service (optional — requires whatsapp-web.js)
291
- try {
292
- const { default: WhatsAppService } = await import('./services/whatsappService.js');
293
- this.whatsappService = new WhatsAppService(this.logger);
294
- this.logger.info('WhatsApp service initialized');
295
- } catch (e) {
296
- this.whatsappService = null;
297
- this.logger.info('WhatsApp service unavailable (whatsapp-web.js not installed)', { error: e.message });
298
- }
299
-
300
- // Telegram Service (optional — requires node-telegram-bot-api)
301
- try {
302
- const { getTelegramService } = await import('./services/telegramService.js');
303
- this.telegramService = getTelegramService(this.logger);
304
- this.logger.info('Telegram service initialized');
305
- } catch (e) {
306
- this.telegramService = null;
307
- this.logger.info('Telegram service unavailable', { error: e.message });
308
- }
309
-
310
- // Discord Service (optional — requires discord.js)
311
- try {
312
- const { getDiscordService } = await import('./services/discordService.js');
313
- this.discordService = getDiscordService(this.logger);
314
- this.logger.info('Discord service initialized');
315
- } catch (e) {
316
- this.discordService = null;
317
- this.logger.info('Discord service unavailable', { error: e.message });
318
- }
319
-
320
- // Schedule Service
321
- this.scheduleService = new ScheduleService(this.logger);
322
- await this.scheduleService.initialize();
323
-
324
- // AI Service
325
- this.aiService = new AIService(
326
- this.config,
327
- this.logger,
328
- this.budgetService,
329
- this.errorHandler
330
- );
331
-
332
- // Set API Key Manager reference in AI Service
333
- this.aiService.setApiKeyManager(this.apiKeyManager);
334
-
335
- // Set Agent Pool reference in AI Service
336
- this.aiService.setAgentPool(this.agentPool);
337
-
338
- // Embedding Service — opt-in semantic retrieval layer. Boots in the
339
- // disabled state by default; loadPersistedConfig() picks up whatever
340
- // the user previously chose in Settings.
341
- const stateDir = getUserDataPaths().state;
342
- this.embeddingService = new EmbeddingService({
343
- // Start with the in-memory default (disabled). The settings file
344
- // (if present) will override this on the next line.
345
- config: this.config?.embeddings || {},
346
- apiKeyManager: this.apiKeyManager,
347
- baseUrl: this.config?.backend?.baseUrl || this.aiService.baseUrl,
348
- stateDir,
349
- logger: this.logger,
350
- });
351
- await this.embeddingService.loadPersistedConfig();
352
- this.aiService.setEmbeddingService(this.embeddingService);
353
- this.logger.info('[Loxia] Embedding service ready', {
354
- provider: this.embeddingService.config.provider,
355
- enabled: this.embeddingService.isEnabled,
356
- });
357
-
358
- // Initialize Model Routing Services
359
- this.benchmarkService = new BenchmarkService(this.config, this.logger);
360
- this.modelsService = new ModelsService(this.config, this.logger);
361
- this.modelRouterService = new ModelRouterService(
362
- this.config,
363
- this.logger,
364
- this.benchmarkService,
365
- this.aiService
366
- );
367
-
368
- // Set API Key Manager reference in ModelsService
369
- this.modelsService.setApiKeyManager(this.apiKeyManager);
370
-
371
- // Set ModelsService reference in AI Service (for model suggestions on errors)
372
- this.aiService.setModelsService(this.modelsService);
373
-
374
- // Set ModelsService reference in BudgetService (for dynamic pricing lookup)
375
- this.budgetService.setModelsService(this.modelsService);
376
-
377
- // Initialize services
378
- await this.benchmarkService.initialize();
379
- await this.modelsService.initialize();
380
-
381
- // Message Processor
382
- this.messageProcessor = new MessageProcessor(
383
- this.config,
384
- this.logger,
385
- this.toolsRegistry,
386
- this.agentPool,
387
- this.contextManager,
388
- this.aiService,
389
- this.modelRouterService,
390
- this.modelsService
391
- );
392
-
393
- // Agent Scheduler - NEW ARCHITECTURE
394
- this.agentScheduler = new AgentScheduler(
395
- this.agentPool,
396
- this.messageProcessor,
397
- this.aiService,
398
- this.logger,
399
- null, // webSocketManager will be set later
400
- this.modelRouterService,
401
- this.modelsService
402
- );
403
-
404
- // Note: Scheduler will be started after WebSocketManager is initialized
405
-
406
- // Orchestrator
407
- this.orchestrator = new Orchestrator(
408
- this.config,
409
- this.logger,
410
- this.agentPool,
411
- this.messageProcessor,
412
- this.aiService,
413
- this.stateManager
414
- );
415
-
416
- // Expose toolsRegistry on the orchestrator so downstream wiring (e.g.
417
- // webServer's flowExecutor init at webServer.js:574 → jobDoneTool
418
- // setFlowExecutor) can reach it through the orchestrator handle. Without
419
- // this, `this.orchestrator?.toolsRegistry` is undefined at wiring time
420
- // and JobDoneTool's flow-contract validation (Phase 8) silently no-ops
421
- // for the entire process lifetime.
422
- this.orchestrator.toolsRegistry = this.toolsRegistry;
423
-
424
- // Set cross-references between components
425
- this.messageProcessor.orchestrator = this.orchestrator;
426
- this.messageProcessor.setScheduler(this.agentScheduler);
427
- this.agentPool.setMessageProcessor(this.messageProcessor);
428
- this.agentPool.setScheduler(this.agentScheduler);
429
- this.agentPool.setFileAttachmentService(this.fileAttachmentService);
430
-
431
- // Attach FileAttachmentService to orchestrator for webServer access
432
- this.orchestrator.fileAttachmentService = this.fileAttachmentService;
433
-
434
- // Wire ScheduleService dependencies
435
- this.scheduleService.setAgentPool(this.agentPool);
436
- this.scheduleService.setMessageProcessor(this.messageProcessor);
437
- this.scheduleService.setOrchestrator(this.orchestrator);
438
-
439
- this.logger.info('Core components initialized');
440
- }
441
-
442
- /**
443
- * Initialize tools system
444
- * @private
445
- */
446
- async initializeTools() {
447
- this.logger.info('Initializing tools...');
448
-
449
- // Register Agent Delay Tool
450
- await this.toolsRegistry.registerTool(AgentDelayTool);
451
-
452
- // Register Terminal Tool
453
- await this.toolsRegistry.registerTool(TerminalTool);
454
-
455
- // Register File System Tool
456
- await this.toolsRegistry.registerTool(FileSystemTool);
457
-
458
- // Register Job Done Tool
459
- await this.toolsRegistry.registerTool(JobDoneTool);
460
-
461
- // Register Agent Communication Tool
462
- await this.toolsRegistry.registerTool(AgentCommunicationTool);
463
-
464
- // Register Task Manager Tool
465
- await this.toolsRegistry.registerTool(TaskManagerTool);
466
-
467
- // Register Import Analyzer Tool
468
- await this.toolsRegistry.registerTool(ImportAnalyzerTool);
469
-
470
- // Register Dependency Resolver Tool
471
- await this.toolsRegistry.registerTool(DependencyResolverTool);
472
-
473
- // Register Image Generation Tool
474
- await this.toolsRegistry.registerTool(ImageTool);
475
-
476
- // Register Video Generation Tool (Sora 2)
477
- await this.toolsRegistry.registerTool(VideoTool);
478
-
479
- // Register Static Analysis Tool
480
- await this.toolsRegistry.registerTool(StaticAnalysisTool);
481
-
482
- // Register Clone Detection Tool
483
- await this.toolsRegistry.registerTool(CloneDetectionTool);
484
-
485
- // Register File Tree Tool
486
- await this.toolsRegistry.registerTool(FileTreeTool);
487
-
488
- // Register File Content Replace Tool
489
- await this.toolsRegistry.registerTool(FileContentReplaceTool);
490
-
491
- // Register Seek Tool
492
- await this.toolsRegistry.registerTool(SeekTool);
493
-
494
- // Register Web Tool
495
- await this.toolsRegistry.registerTool(WebTool);
496
-
497
- // Register Foundry Web Search Tool (grounded, with citations).
498
- // Distinct from WebTool: WebTool drives a real browser (puppeteer)
499
- // for raw results / authenticated sites; FoundryWebSearchTool
500
- // calls /llm/web-search which uses Azure AI Foundry's
501
- // web_search_preview tool — single round-trip, synthesized answer
502
- // with citation URLs. See docs/FOUNDRY_WEB_SEARCH_PROVISIONING.md
503
- // on the backend side; the tool returns a clear "not provisioned"
504
- // error until that's done.
505
- await this.toolsRegistry.registerTool(FoundryWebSearchTool);
506
-
507
- // Register Visual Editor Tool
508
- await this.toolsRegistry.registerTool(VisualEditorTool);
509
-
510
- // Register PDF Tool
511
- await this.toolsRegistry.registerTool(PdfTool);
512
-
513
- // Register Help Tool (two-layer tool description system)
514
- await this.toolsRegistry.registerTool(HelpTool);
515
-
516
- // Register Document (DOCX) Tool — legacy; superseded by OfficeTool.
517
- // Kept for one release cycle so existing agent configs that reference
518
- // toolId "doc" continue to work.
519
- await this.toolsRegistry.registerTool(DocxTool);
520
-
521
- // Register Spreadsheet (Excel) Tool — legacy; superseded by OfficeTool.
522
- await this.toolsRegistry.registerTool(ExcelTool);
523
-
524
- // Register Office Tool — consolidated doc/sheet/pres surface
525
- // (Word + Excel + PowerPoint create/read).
526
- await this.toolsRegistry.registerTool(OfficeTool);
527
-
528
- // Register Memory Tool
529
- await this.toolsRegistry.registerTool(MemoryTool);
530
- await this.toolsRegistry.registerTool(SkillsTool);
531
- await this.toolsRegistry.registerTool(VisionTool);
532
-
533
- // Register Composio integration tool (off by default — agents
534
- // must have the `composio` capability AND the operator must set
535
- // COMPOSIO_API_KEY for it to do anything).
536
- await this.toolsRegistry.registerTool(ComposioTool);
537
-
538
- // Register User Prompt Tool
539
- await this.toolsRegistry.registerTool(UserPromptTool);
540
-
541
- // Register Code Map Tool
542
- await this.toolsRegistry.registerTool(CodeMapTool);
543
-
544
- // Register WhatsApp Tool
545
- await this.toolsRegistry.registerTool(WhatsAppTool);
546
-
547
- // Register Platform Control Tool — agent-facing platform control
548
- // (currently: scheduled tasks; per-agent permission, default DISABLED).
549
- await this.toolsRegistry.registerTool(PlatformControlTool);
550
-
551
- // Register Desktop Tool — keyboard/mouse/screen/window control with
552
- // visual grounding (Kimi K2.6 by default). Per-agent allowlist with
553
- // an EMPTY default, so no agent can drive the desktop until the
554
- // operator explicitly opts in via toolConfig.desktop.allowedActions.
555
- // Global kill switch: LOXIA_DESKTOP_TOOL_DISABLED=1.
556
- await this.toolsRegistry.registerTool(DesktopTool);
557
-
558
- // widget-module: remove this block if the module is deleted.
559
- // Registers the WidgetTool for agent-facing custom-UI rendering.
560
- // Honors LOXIA_DISABLE_WIDGETS=1 env flag for a zero-source kill switch.
561
- const widgetModule = await import('./modules/widget/index.js');
562
- if (!widgetModule.isDisabled()) {
563
- await this.toolsRegistry.registerTool(widgetModule.WidgetTool);
564
- }
565
-
566
- // Set ToolsRegistry dependency for HelpTool (two-layer tool description system)
567
- const helpTool = this.toolsRegistry.getTool('help');
568
- if (helpTool && typeof helpTool.setToolsRegistry === 'function') {
569
- helpTool.setToolsRegistry(this.toolsRegistry);
570
- this.logger.info('ToolsRegistry set for Help Tool');
571
- }
572
-
573
- // Set AgentPool dependency for AgentDelayTool
574
- const agentDelayTool = this.toolsRegistry.getTool('agentdelay');
575
- if (agentDelayTool && typeof agentDelayTool.setAgentPool === 'function') {
576
- agentDelayTool.setAgentPool(this.agentPool);
577
- }
578
-
579
- // Wire ScheduleService into PlatformControlTool. Without this, the
580
- // tool reports "ScheduleService is not available" for any feature
581
- // action — useful as a defensive default but not what we want here.
582
- const platformControlTool = this.toolsRegistry.getTool('platformcontrol');
583
- if (platformControlTool) {
584
- if (typeof platformControlTool.setScheduleService === 'function') {
585
- platformControlTool.setScheduleService(this.scheduleService);
586
- }
587
- // Agent + team CRUD requires AgentPool, StateManager, MemoryService.
588
- if (typeof platformControlTool.setAgentPool === 'function') {
589
- platformControlTool.setAgentPool(this.agentPool);
590
- }
591
- if (typeof platformControlTool.setStateManager === 'function') {
592
- platformControlTool.setStateManager(this.stateManager);
593
- }
594
- // Memory tool's team-pool actions (publish/search/unpublish) need
595
- // StateManager for the team-membership ACL check. The wiring is
596
- // optional: when StateManager isn't attached, team-scoped actions
597
- // refuse cleanly rather than defaulting open.
598
- const memoryTool = this.toolsRegistry?.getTool?.('memory');
599
- if (memoryTool && typeof memoryTool.setStateManager === 'function') {
600
- memoryTool.setStateManager(this.stateManager);
601
- }
602
- if (typeof platformControlTool.setMemoryService === 'function') {
603
- const { getMemoryService } = await import('./services/memoryService.js');
604
- platformControlTool.setMemoryService(getMemoryService(this.logger));
605
- }
606
- // Flow CRUD + execution requires the FlowExecutor (created later in
607
- // the boot sequence by the webServer). The webServer init also
608
- // calls setFlowExecutor on this tool when it spins up — so this
609
- // call is the one that fires when the executor is already alive
610
- // (rare during startup), and the webServer call covers the normal
611
- // path. Both are idempotent.
612
- if (typeof platformControlTool.setFlowExecutor === 'function' && this.flowExecutor) {
613
- platformControlTool.setFlowExecutor(this.flowExecutor);
614
- }
615
- }
616
-
617
- // Set AgentPool dependency for JobDoneTool
618
- const jobDoneTool = this.toolsRegistry.getTool('jobdone');
619
- if (jobDoneTool && typeof jobDoneTool.setAgentPool === 'function') {
620
- jobDoneTool.setAgentPool(this.agentPool);
621
- }
622
-
623
- // Set AgentPool and Scheduler dependencies for TaskManagerTool
624
- const taskManagerTool = this.toolsRegistry.getTool('taskmanager');
625
- if (taskManagerTool && typeof taskManagerTool.setAgentPool === 'function') {
626
- taskManagerTool.setAgentPool(this.agentPool);
627
- }
628
- if (taskManagerTool && typeof taskManagerTool.setScheduler === 'function') {
629
- taskManagerTool.setScheduler(this.scheduler);
630
- }
631
-
632
- // Note: AgentCommunicationTool receives agentPool through execution context
633
- // No need to set it directly as it's passed in the context parameter
634
- const agentCommTool = this.toolsRegistry.getTool('agentcommunication');
635
- if (agentCommTool) {
636
- this.logger.info('Agent Communication Tool registered successfully');
637
- }
638
-
639
- // Set AIService dependency for ImageTool
640
- const imageTool = this.toolsRegistry.getTool('image-gen');
641
- if (imageTool && typeof imageTool.setAIService === 'function') {
642
- imageTool.setAIService(this.aiService);
643
- this.logger.info('AIService set for Image Generation Tool');
644
- }
645
-
646
- // Set AgentPool dependency for ImageTool (for conversation history persistence)
647
- if (imageTool && typeof imageTool.setAgentPool === 'function') {
648
- imageTool.setAgentPool(this.agentPool);
649
- this.logger.info('AgentPool set for Image Generation Tool');
650
- }
651
-
652
- // Set dependencies for VisionTool
653
- const visionTool = this.toolsRegistry.getTool('vision');
654
- if (visionTool && typeof visionTool.setAIService === 'function') {
655
- visionTool.setAIService(this.aiService);
656
- if (this.modelsService) visionTool.setModelsService(this.modelsService);
657
- if (this.agentPool) visionTool.setAgentPool(this.agentPool);
658
- this.logger.info('Dependencies set for Vision Tool');
659
- }
660
-
661
- // Video generation (Sora 2)
662
- const videoTool = this.toolsRegistry.getTool('video-gen');
663
- if (videoTool && typeof videoTool.setAIService === 'function') {
664
- videoTool.setAIService(this.aiService);
665
- this.logger.info('AIService set for Video Generation Tool');
666
- }
667
-
668
- // Foundry web search — uses /llm/web-search backed by Azure AI
669
- // Foundry + Grounding with Bing Search. Calls aiService.foundryWebSearch
670
- // for the auth + URL plumbing.
671
- const foundryWebTool = this.toolsRegistry.getTool('foundry_web_search');
672
- if (foundryWebTool && typeof foundryWebTool.setAIService === 'function') {
673
- foundryWebTool.setAIService(this.aiService);
674
- this.logger.info('AIService set for Foundry Web Search Tool');
675
- }
676
-
677
- // Desktop tool — needs aiService for proxy-mode grounding auth
678
- // (baseUrl + per-session Loxia key from apiKeyManager).
679
- const desktopTool = this.toolsRegistry.getTool('desktop');
680
- if (desktopTool && typeof desktopTool.setAIService === 'function') {
681
- desktopTool.setAIService(this.aiService);
682
- this.logger.info('AIService set for Desktop Tool');
683
- }
684
- if (videoTool && typeof videoTool.setAgentPool === 'function') {
685
- videoTool.setAgentPool(this.agentPool);
686
- this.logger.info('AgentPool set for Video Generation Tool');
687
- }
688
-
689
- // Set WhatsApp Service dependency for WhatsApp Tool
690
- const whatsappTool = this.toolsRegistry.getTool('whatsapp');
691
- if (whatsappTool && typeof whatsappTool.setWhatsAppService === 'function') {
692
- whatsappTool.setWhatsAppService(this.whatsappService);
693
- this.logger.info('WhatsAppService set for WhatsApp Tool');
694
- }
695
-
696
- // ─── Catch-all aiService wiring ────────────────────────────────
697
- //
698
- // Several tools read `this.aiService.getEmbeddingService()` for their
699
- // semantic-search / find-action paths (memory, agentcommunication,
700
- // taskmanager, skills, composio) but have no `setAIService` method
701
- // and were never wired by the explicit calls above. Production silently
702
- // returned `this.aiService === undefined` → `_getIndexer()` returned
703
- // null → every embedding-backed action surfaced "Embeddings are not
704
- // enabled" even when embeddings WERE enabled (the user just hit this
705
- // on composio.find-action; auto-recall in agentScheduler uses a
706
- // different code path so it kept working, masking the wider gap).
707
- //
708
- // We sweep every registered tool here, AFTER the explicit per-tool
709
- // wiring above. Tools that already have aiService set (image, vision,
710
- // video, foundry-web-search, desktop) are skipped; everyone else
711
- // gets it now. The assignment is harmless for tools that ignore the
712
- // property.
713
- {
714
- let wired = 0;
715
- for (const tool of this.toolsRegistry.tools.values()) {
716
- if (!tool.aiService) {
717
- tool.aiService = this.aiService;
718
- wired++;
719
- }
720
- }
721
- this.logger.info('AIService wired onto remaining tools', { count: wired });
722
- }
723
-
724
- const toolCapabilities = this.toolsRegistry.getToolCapabilities();
725
- this.logger.info('Tools initialized', {
726
- toolCount: Object.keys(toolCapabilities).length,
727
- enabledTools: Object.keys(toolCapabilities).filter(id => toolCapabilities[id].capabilities.enabled),
728
- registeredTools: this.toolsRegistry.listTools()
729
- });
730
-
731
- // Log tool descriptions for debugging
732
- if (this.logger.level === 'debug') {
733
- for (const [toolId, tool] of Object.entries(toolCapabilities)) {
734
- this.logger.debug(`Tool ${toolId} capabilities`, tool.capabilities);
735
- }
736
- }
737
- }
738
-
739
- /**
740
- * Initialize interface handlers
741
- * @private
742
- */
743
- async initializeInterfaces(options) {
744
- this.logger.info('Initializing interfaces...');
745
-
746
- const interfaceConfig = this.config.interfaces || {};
747
- const uiMode = process.env.LOXIA_UI_MODE || 'cli'; // Default to old CLI
748
-
749
- // CLI Interface - Load old readline CLI (unless terminal UI mode is specified)
750
- // NOTE: Terminal UI mode runs as a separate WebSocket client, not in the main process
751
- if (interfaceConfig.cli?.enabled !== false && uiMode !== 'terminal') {
752
- // Use old CLI (readline-based)
753
- this.logger.info('Loading CLI (readline)...');
754
- const { default: CLIInterface } = await import('./interfaces/cli.js');
755
- const cliInterface = new CLIInterface(
756
- this.orchestrator,
757
- this.logger,
758
- interfaceConfig.cli || {}
759
- );
760
-
761
- await cliInterface.initialize();
762
- this.interfaces.set(INTERFACE_TYPES.CLI, cliInterface);
763
-
764
- this.logger.info('CLI interface initialized');
765
- }
766
-
767
- // If terminal UI mode, skip CLI - Terminal UI will connect as WebSocket client
768
- if (uiMode === 'terminal') {
769
- this.logger.info('Terminal UI mode: Server-only startup (Terminal UI will connect as WebSocket client)');
770
- }
771
-
772
- // Web Interface - now implemented
773
- if (interfaceConfig.web?.enabled !== false) {
774
- // Read port from environment variables (set by CLI) or use config defaults
775
- const webPort = parseInt(process.env.LOXIA_PORT || process.env.PORT, 10) || 8080;
776
- // Use env var, then config, then 0.0.0.0 (accept connections from all interfaces)
777
- const webHost = process.env.LOXIA_HOST || interfaceConfig.web?.host || '0.0.0.0';
778
-
779
- const webConfig = {
780
- ...interfaceConfig.web,
781
- port: webPort,
782
- host: webHost,
783
- backend: this.config.backend
784
- };
785
-
786
- const webServer = new WebServer(
787
- this.orchestrator,
788
- this.logger,
789
- webConfig
790
- );
791
-
792
- // Pass toolsRegistry to webServer for the /api/tools endpoint
793
- webServer.toolsRegistry = this.toolsRegistry;
794
-
795
- // Set API Key Manager reference in Web Server
796
- webServer.setApiKeyManager(this.apiKeyManager);
797
-
798
- // Embedding service handle so the /api/embeddings/* routes can
799
- // read config + telemetry, hot-swap providers, etc.
800
- webServer.embeddingService = this.embeddingService;
801
-
802
- // Set Credential Vault reference in Web Server
803
- webServer.setCredentialVault(this.credentialVault);
804
-
805
- // Set WhatsApp Service reference in Web Server
806
- if (this.whatsappService) {
807
- webServer.setWhatsAppService(this.whatsappService);
808
- this.whatsappService.setWebSocketManager(webServer);
809
- // Auto-reconnect if a previous session exists (non-blocking)
810
- this.whatsappService.autoReconnect().catch(e =>
811
- this.logger.warn('WhatsApp auto-reconnect failed', { error: e.message })
812
- );
813
- }
814
-
815
- // Set Telegram Service references
816
- if (this.telegramService) {
817
- webServer.setTelegramService(this.telegramService);
818
- this.telegramService.setOrchestrator(this.orchestrator);
819
- this.telegramService.setAgentPool(this.agentPool);
820
- this.telegramService.setWebSocketManager(webServer);
821
- if (this.flowExecutor) this.telegramService.setFlowExecutor(this.flowExecutor);
822
- // Backend URL + platform API key are needed for the voice-note
823
- // → /llm/transcribe path. Without these, voice messages return
824
- // a friendly "voice not configured" message and the user is
825
- // told to type instead. Both come from the same sources the
826
- // rest of the CLI's /api/* proxy code uses.
827
- const backendBaseUrl = this.config?.backend?.baseUrl
828
- || webServer?.config?.backend?.baseUrl
829
- || null;
830
- if (backendBaseUrl) this.telegramService.setBackendBaseUrl(backendBaseUrl);
831
- // Pass a getter (not a snapshotted value) so the key is
832
- // re-resolved on every transcribe call. This lets the user
833
- // sign in AFTER boot — voice notes start working immediately
834
- // without restarting the server.
835
- this.telegramService.setPlatformApiKey(
836
- () => this.apiKeyManager?.getKeysForRequest?.(null, { platformProvided: true })?.loxiaApiKey || null
837
- );
838
- // Let the scheduler query bridge-state per agent so it can inject
839
- // the <external> routing guidance into system prompts only when the
840
- // agent is actually addressable from Telegram.
841
- this.agentScheduler?.setTelegramService?.(this.telegramService);
842
- // Auto-connect if token exists in config (non-blocking)
843
- this.telegramService.autoConnect().catch(e =>
844
- this.logger.warn('Telegram auto-connect failed', { error: e.message })
845
- );
846
- }
847
-
848
- // Set Discord Service references
849
- if (this.discordService) {
850
- webServer.setDiscordService(this.discordService);
851
- this.discordService.setOrchestrator(this.orchestrator);
852
- this.discordService.setAgentPool(this.agentPool);
853
- this.discordService.setWebSocketManager(webServer);
854
- if (this.flowExecutor) this.discordService.setFlowExecutor(this.flowExecutor);
855
- // Same bridge-awareness hook as Telegram above — the scheduler
856
- // only appends <external> guidance when `isAgentBridged(agentId)`
857
- // returns true for this channel.
858
- this.agentScheduler?.setDiscordService?.(this.discordService);
859
- // Auto-connect if token exists in config (non-blocking)
860
- this.discordService.autoConnect().catch(e =>
861
- this.logger.warn('Discord auto-connect failed', { error: e.message })
862
- );
863
- }
864
-
865
- await webServer.initialize();
866
- this.interfaces.set(INTERFACE_TYPES.WEB, webServer);
867
-
868
- // Attach WebServer to orchestrator for MessageProcessor broadcasting
869
- this.orchestrator.webServer = webServer;
870
-
871
- // Connect MessageProcessor to WebServer for real-time updates
872
- this.messageProcessor.setWebSocketManager(webServer);
873
-
874
- // Connect AgentScheduler to WebServer for real-time updates
875
- this.agentScheduler.webSocketManager = webServer;
876
-
877
- // Start the scheduler now that WebSocketManager is available
878
- this.agentScheduler.start();
879
- this.logger.info('Agent Scheduler started with WebSocket integration');
880
-
881
- // Wire ScheduleService to WebServer and FlowExecutor
882
- webServer.setScheduleService(this.scheduleService);
883
- this.scheduleService.setWebSocketManager(webServer);
884
- if (webServer.flowExecutor) {
885
- this.scheduleService.setFlowExecutor(webServer.flowExecutor);
886
- }
887
- this.scheduleService.start();
888
- this.logger.info('ScheduleService started with WebSocket integration');
889
-
890
- // Set global reference for tools that need to broadcast
891
- global.loxiaWebServer = webServer;
892
-
893
- // Connect JobDoneTool to WebServer for broadcasting mode changes
894
- const jobDoneTool = this.toolsRegistry.getTool('jobdone');
895
- if (jobDoneTool && typeof jobDoneTool.setWebSocketManager === 'function') {
896
- jobDoneTool.setWebSocketManager(webServer);
897
- this.logger.info('WebSocketManager set for JobDone Tool');
898
- }
899
-
900
- // Connect UserPromptTool to WebServer and AgentPool for user prompting
901
- const userPromptTool = this.toolsRegistry.getTool('userprompt');
902
- if (userPromptTool) {
903
- if (typeof userPromptTool.setWebSocketManager === 'function') {
904
- userPromptTool.setWebSocketManager(webServer);
905
- }
906
- if (typeof userPromptTool.setAgentPool === 'function') {
907
- userPromptTool.setAgentPool(this.agentPool);
908
- }
909
- this.logger.info('Dependencies set for UserPrompt Tool');
910
- }
911
-
912
- const status = webServer.getStatus();
913
- this.logger.info('Web interface initialized', { url: status.url });
914
- console.log(`🌐 Server running at ${status.url}`);
915
- console.log(`📱 Web UI available at: ${status.url}`);
916
-
917
- // Auto-open browser (skip when running inside Electron — it creates its own window)
918
- if (!process.env.LOXIA_ELECTRON) {
919
- const url = status.url.replace('0.0.0.0', 'localhost');
920
- const platform = process.platform;
921
- const openCmd = platform === 'win32' ? `start "" "${url}"`
922
- : platform === 'darwin' ? `open "${url}"`
923
- : `xdg-open "${url}"`;
924
- exec(openCmd, (err) => {
925
- if (err) this.logger.debug('Could not auto-open browser', { error: err.message });
926
- });
927
- }
928
- }
929
-
930
- // VSCode Extension Interface (placeholder)
931
- if (interfaceConfig.vscode?.enabled === true) {
932
- this.logger.info('VSCode interface configured but not implemented yet');
933
- // TODO: Initialize VSCode extension interface
934
- }
935
- }
936
-
937
- /**
938
- * Setup graceful shutdown handlers
939
- * @private
940
- */
941
- setupShutdownHandlers() {
942
- // Handle SIGINT (Ctrl+C)
943
- process.on('SIGINT', async () => {
944
- console.log('\n📋 Received SIGINT, shutting down gracefully...');
945
- await this.shutdown();
946
- });
947
-
948
- // Handle SIGTERM
949
- process.on('SIGTERM', async () => {
950
- console.log('\n📋 Received SIGTERM, shutting down gracefully...');
951
- await this.shutdown();
952
- });
953
-
954
- // Handle uncaught exceptions
955
- process.on('uncaughtException', async (error) => {
956
- console.error('❌ Uncaught exception:', error);
957
- if (this.logger) {
958
- this.logger.error('Uncaught exception', {
959
- error: error.message,
960
- stack: error.stack
961
- });
962
- }
963
-
964
- await this.shutdown();
965
- process.exit(1);
966
- });
967
-
968
- // Handle unhandled promise rejections
969
- process.on('unhandledRejection', async (reason, promise) => {
970
- const reasonMessage = reason?.message || String(reason);
971
-
972
- // List of known non-critical rejections that shouldn't crash the server
973
- const nonCriticalPatterns = [
974
- 'Credential request cancelled',
975
- 'Credential request timed out',
976
- 'Target closed',
977
- 'Session closed',
978
- 'Protocol error',
979
- 'Navigation timeout',
980
- 'net::ERR_',
981
- 'Requesting main frame too early',
982
- 'Connection closed',
983
- // AI/model errors should NOT crash the server
984
- 'HTTP 4', // 400, 401, 403, 404, 429, etc.
985
- 'HTTP 5', // 500, 502, 503, etc.
986
- 'circuit breaker',
987
- 'Rate limit',
988
- 'Insufficient credits',
989
- 'not suitable for chat',
990
- 'No API key configured',
991
- 'Message content is empty',
992
- 'Backend returned malformed',
993
- 'stream generator',
994
- 'No response choices',
995
- 'model error',
996
- 'isModelError',
997
- 'Service temporarily unavailable',
998
- 'Failed to fetch models',
999
- 'ECONNREFUSED',
1000
- 'ETIMEDOUT',
1001
- 'ECONNRESET',
1002
- 'fetch failed',
1003
- 'AbortError',
1004
- 'The operation was aborted'
1005
- ];
1006
-
1007
- const isNonCritical = nonCriticalPatterns.some(pattern =>
1008
- reasonMessage.includes(pattern)
1009
- );
1010
-
1011
- if (isNonCritical) {
1012
- console.warn('⚠️ Non-critical promise rejection (server continues):', reasonMessage);
1013
- if (this.logger) {
1014
- this.logger.warn('Non-critical promise rejection', {
1015
- reason: reasonMessage,
1016
- promise: promise.toString()
1017
- });
1018
- }
1019
- return; // Don't crash for non-critical errors
1020
- }
1021
-
1022
- console.error('❌ Unhandled promise rejection:', reason);
1023
- if (this.logger) {
1024
- this.logger.error('Unhandled promise rejection', {
1025
- reason: reasonMessage,
1026
- promise: promise.toString()
1027
- });
1028
- }
1029
-
1030
- await this.shutdown();
1031
- process.exit(1);
1032
- });
1033
- }
1034
-
1035
- /**
1036
- * Gracefully shutdown the application
1037
- * @returns {Promise<void>}
1038
- */
1039
- async shutdown() {
1040
- if (this.isShuttingDown) {
1041
- return;
1042
- }
1043
-
1044
- this.isShuttingDown = true;
1045
-
1046
- // Force-exit safety net: if graceful shutdown hangs, kill the process after 10s
1047
- const forceExitTimer = setTimeout(() => {
1048
- console.error('⚠️ Graceful shutdown timed out after 10s — forcing exit');
1049
- process.exit(1);
1050
- }, 10000);
1051
- forceExitTimer.unref(); // Don't let this timer keep the process alive on its own
1052
-
1053
- try {
1054
- console.log('🛑 Shutting down Loxia Autopilot One...');
1055
-
1056
- if (this.logger) {
1057
- this.logger.info('Application shutdown initiated');
1058
- }
1059
-
1060
- // Stop schedule service first
1061
- if (this.scheduleService) {
1062
- this.scheduleService.stop();
1063
- this.logger?.info('Schedule service stopped');
1064
- }
1065
-
1066
- // Stop agent scheduler (prevents new work from starting mid-shutdown)
1067
- if (this.agentScheduler) {
1068
- this.agentScheduler.stop();
1069
- this.logger?.info('Agent scheduler stopped');
1070
- }
1071
-
1072
- // Cancel any pending model fetch retries
1073
- if (this.modelsService?._cancelRetry) {
1074
- this.modelsService._cancelRetry();
1075
- this.logger?.info('Models service retries cancelled');
1076
- }
1077
-
1078
- // Close Puppeteer browser (webTool) — it holds DevTools ports
1079
- if (this.toolsRegistry) {
1080
- for (const toolId of ['web']) {
1081
- try {
1082
- const tool = this.toolsRegistry.getTool(toolId);
1083
- if (tool?.cleanup) {
1084
- await tool.cleanup();
1085
- this.logger?.info(`${toolId} tool cleanup complete`);
1086
- }
1087
- } catch (error) {
1088
- this.logger?.warn(`Failed to cleanup ${toolId} tool`, { error: error.message });
1089
- }
1090
- }
1091
- }
1092
-
1093
- // Kill ALL running terminal processes across all agents
1094
- if (this.toolsRegistry) {
1095
- try {
1096
- const terminalTool = this.toolsRegistry.getTool('terminal');
1097
- if (terminalTool?.commandTracker) {
1098
- let killed = 0;
1099
- for (const [cmdId, cmdInfo] of terminalTool.commandTracker) {
1100
- if (cmdInfo.process && cmdInfo.state === 'RUNNING') {
1101
- try {
1102
- cmdInfo.process.kill('SIGTERM');
1103
- killed++;
1104
- } catch { /* already dead */ }
1105
- }
1106
- }
1107
- if (killed > 0) {
1108
- this.logger?.info(`Killed ${killed} running terminal process(es) on shutdown`);
1109
- }
1110
- }
1111
- } catch (error) {
1112
- this.logger?.warn('Failed to cleanup terminal processes', { error: error.message });
1113
- }
1114
- }
1115
-
1116
- // Shutdown interfaces (web server, visual editor, WS connections)
1117
- for (const [type, interface_] of this.interfaces) {
1118
- try {
1119
- if (interface_.shutdown) {
1120
- await interface_.shutdown();
1121
- }
1122
- this.logger?.info(`${type} interface shutdown complete`);
1123
- } catch (error) {
1124
- console.error(`Failed to shutdown ${type} interface:`, error.message);
1125
- }
1126
- }
1127
-
1128
- // Shutdown async tool manager
1129
- if (this.asyncToolManager) {
1130
- await this.asyncToolManager.shutdown();
1131
- this.logger?.info('Async tool manager shutdown complete');
1132
- }
1133
-
1134
- // Shutdown orchestrator (persists agent states)
1135
- if (this.orchestrator) {
1136
- await this.orchestrator.shutdown();
1137
- this.logger?.info('Orchestrator shutdown complete');
1138
- }
1139
-
1140
- // Cleanup configuration manager
1141
- if (this.configManager) {
1142
- this.configManager.cleanup();
1143
- }
1144
-
1145
- // Close logger
1146
- if (this.logger) {
1147
- await this.logger.close();
1148
- }
1149
-
1150
- console.log('✅ Loxia Autopilot One shutdown complete');
1151
-
1152
- } catch (error) {
1153
- console.error('❌ Error during shutdown:', error.message);
1154
- } finally {
1155
- process.exit(0);
1156
- }
1157
- }
1158
-
1159
- /**
1160
- * Get application status
1161
- * @returns {Object} Application status
1162
- */
1163
- getStatus() {
1164
- return {
1165
- version: SYSTEM_VERSION,
1166
- uptime: process.uptime(),
1167
- memoryUsage: process.memoryUsage(),
1168
- interfaces: Array.from(this.interfaces.keys()),
1169
- isShuttingDown: this.isShuttingDown
1170
- };
1171
- }
1172
- }
1173
-
1174
- /**
1175
- * Main application entry point
1176
- */
1177
- async function main() {
1178
- const app = new LoxiaApplication();
1179
-
1180
- // Parse command line arguments
1181
- const args = process.argv.slice(2);
1182
- const options = {
1183
- projectDir: process.cwd(),
1184
- watchConfig: args.includes('--watch-config'),
1185
- configPaths: []
1186
- };
1187
-
1188
- // Parse --port and --host from argv so they work when running index.js directly
1189
- // (bin/cli.js sets these as env vars, but npm start / node src/index.js bypasses cli.js)
1190
- for (let i = 0; i < args.length; i++) {
1191
- if (args[i] === '--port' && args[i + 1]) {
1192
- process.env.LOXIA_PORT = args[i + 1];
1193
- }
1194
- if (args[i] === '--host' && args[i + 1]) {
1195
- process.env.LOXIA_HOST = args[i + 1];
1196
- }
1197
- }
1198
-
1199
- // Look for custom config file
1200
- const configIndex = args.indexOf('--config');
1201
- if (configIndex !== -1 && args[configIndex + 1]) {
1202
- options.configPaths.push(args[configIndex + 1]);
1203
- }
1204
-
1205
- await app.initialize(options);
1206
-
1207
- // Keep the application running
1208
- return app;
1209
- }
1210
-
1211
- // Start the application if this file is run directly.
1212
- const __filename = resolveModuleFilename(import.meta.url);
1213
- if (process.argv[1] === __filename) {
1214
- console.log('🚀 Starting Loxia Autopilot One...');
1215
- main().catch(error => {
1216
- console.error('❌ Failed to start Loxia Autopilot One:', error.message);
1217
- console.error('Stack trace:', error.stack);
1218
- process.exit(1);
1219
- });
1220
- }
1221
-
1
+ /**
2
+ * Loxia Autopilot One - Main Application Entry Point
3
+ *
4
+ * Purpose:
5
+ * - Initialize all system components
6
+ * - Setup dependency injection
7
+ * - Start interface handlers
8
+ * - Handle graceful shutdown
9
+ */
10
+
11
+ import path from 'path';
12
+ import { exec } from 'child_process';
13
+ import { createLogger } from './utilities/logger.js';
14
+ import { resolveModuleFilename } from './utilities/esmCjsPath.js';
15
+ import { createConfigManager } from './utilities/configManager.js';
16
+ import Orchestrator from './core/orchestrator.js';
17
+ import AgentPool from './core/agentPool.js';
18
+ import MessageProcessor from './core/messageProcessor.js';
19
+ import AgentScheduler from './core/agentScheduler.js';
20
+ import ContextManager from './core/contextManager.js';
21
+ import StateManager from './core/stateManager.js';
22
+ import AIService from './services/aiService.js';
23
+ import { EmbeddingService } from './services/embeddings/embeddingService.js';
24
+ import { getUserDataPaths } from './utilities/userDataDir.js';
25
+ import BudgetService from './services/budgetService.js';
26
+ import ErrorHandler from './services/errorHandler.js';
27
+ import BenchmarkService from './services/benchmarkService.js';
28
+ import ModelRouterService from './services/modelRouterService.js';
29
+ import ModelsService from './services/modelsService.js';
30
+ import ApiKeyManager, { registerApiKeyManager } from './services/apiKeyManager.js';
31
+ import { getCredentialVault } from './services/credentialVault.js';
32
+ import FileAttachmentService from './services/fileAttachmentService.js';
33
+ import { ToolsRegistry } from './tools/baseTool.js';
34
+ import AgentDelayTool from './tools/agentDelayTool.js';
35
+ import TerminalTool from './tools/terminalTool.js';
36
+ import FileSystemTool from './tools/fileSystemTool.js';
37
+ import JobDoneTool from './tools/jobDoneTool.js';
38
+ import AgentCommunicationTool from './tools/agentCommunicationTool.js';
39
+ import TaskManagerTool from './tools/taskManagerTool.js';
40
+ import ImportAnalyzerTool from './tools/importAnalyzerTool.js';
41
+ import DependencyResolverTool from './tools/dependencyResolverTool.js';
42
+ import ImageTool from './tools/imageTool.js';
43
+ import VideoTool from './tools/videoTool.js';
44
+ import StaticAnalysisTool from './tools/staticAnalysisTool.js';
45
+ import CloneDetectionTool from './tools/cloneDetectionTool.js';
46
+ import FileTreeTool from './tools/fileTreeTool.js';
47
+ import FileContentReplaceTool from './tools/fileContentReplaceTool.js';
48
+ import SeekTool from './tools/seekTool.js';
49
+ import WebTool from './tools/webTool.js';
50
+ import FoundryWebSearchTool from './tools/foundryWebSearchTool.js';
51
+ import VisualEditorTool from './tools/visualEditorTool.js';
52
+ import PdfTool from './tools/pdfTool.js';
53
+ import HelpTool from './tools/helpTool.js';
54
+ import DocxTool from './tools/docxTool.js';
55
+ import ExcelTool from './tools/excelTool.js';
56
+ import OfficeTool from './tools/officeTool.js';
57
+ import MemoryTool from './tools/memoryTool.js';
58
+ import ComposioTool from './tools/composioTool.js';
59
+ import SkillsTool from './tools/skillsTool.js';
60
+ import VisionTool from './tools/visionTool.js';
61
+ import UserPromptTool from './tools/userPromptTool.js';
62
+ import CodeMapTool from './tools/codeMapTool.js';
63
+ import WhatsAppTool from './tools/whatsappTool.js';
64
+ import PlatformControlTool from './tools/platformControlTool.js';
65
+ import { DesktopTool } from './tools/desktop/DesktopTool.js';
66
+ import ScheduleService from './services/scheduleService.js';
67
+ import AsyncToolManager from './tools/asyncToolManager.js';
68
+ import WebServer from './interfaces/webServer.js';
69
+
70
+ import {
71
+ SYSTEM_VERSION,
72
+ INTERFACE_TYPES
73
+ } from './utilities/constants.js';
74
+ import {
75
+ getUserDataDir,
76
+ ensureUserDataDirs,
77
+ migrateFromOldLocation,
78
+ getLegacyDataPaths
79
+ } from './utilities/userDataDir.js';
80
+
81
+ class LoxiaApplication {
82
+ constructor() {
83
+ this.logger = null;
84
+ this.config = null;
85
+ this.orchestrator = null;
86
+ this.interfaces = new Map();
87
+ this.isShuttingDown = false;
88
+
89
+ // Bind shutdown handler
90
+ this.shutdown = this.shutdown.bind(this);
91
+ }
92
+
93
+ /**
94
+ * Initialize the application
95
+ * @param {Object} options - Initialization options
96
+ * @returns {Promise<void>}
97
+ */
98
+ async initialize(options = {}) {
99
+ try {
100
+ console.log(`🚀 Starting Loxia Autopilot One v${SYSTEM_VERSION}`);
101
+
102
+ // Initialize configuration
103
+ await this.initializeConfig(options);
104
+
105
+ // Initialize logging
106
+ await this.initializeLogging();
107
+
108
+ this.logger.info('Loxia Autopilot One starting up', {
109
+ version: SYSTEM_VERSION,
110
+ nodeVersion: process.version,
111
+ platform: process.platform,
112
+ projectDir: options.projectDir || process.cwd()
113
+ });
114
+
115
+ // IMPORTANT: Initialize persistent user data directory and migrate legacy data
116
+ // This ensures user data (agents, conversations, settings) survives npm updates
117
+ await this.initializeUserDataDirectory();
118
+
119
+ // Initialize core components
120
+ await this.initializeCoreComponents();
121
+
122
+ // Initialize tools
123
+ await this.initializeTools();
124
+
125
+ this.logger.info('Starting interface initialization...');
126
+
127
+ // Initialize interfaces
128
+ await this.initializeInterfaces(options);
129
+
130
+ this.logger.info('Interface initialization completed');
131
+
132
+ // Setup shutdown handlers
133
+ this.setupShutdownHandlers();
134
+
135
+ this.logger.info('Loxia Autopilot One startup complete');
136
+ console.log('✅ Loxia Autopilot One is ready!');
137
+
138
+ } catch (error) {
139
+ console.error('❌ Failed to initialize Loxia Autopilot One:', error.message);
140
+ if (this.logger) {
141
+ this.logger.error('Application initialization failed', {
142
+ error: error.message,
143
+ stack: error.stack
144
+ });
145
+ }
146
+ process.exit(1);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Initialize configuration management
152
+ * @private
153
+ */
154
+ async initializeConfig(options) {
155
+ const __filename = resolveModuleFilename(import.meta.url);
156
+ const __dirname = path.dirname(__filename);
157
+
158
+ const configPaths = [
159
+ path.join(__dirname, '../config/default.json'),
160
+ ...(options.configPaths || [])
161
+ ];
162
+
163
+ this.configManager = createConfigManager({
164
+ configPaths,
165
+ envPrefix: 'LOXIA'
166
+ });
167
+
168
+ this.config = await this.configManager.loadConfig();
169
+
170
+ // Enable config watching if requested
171
+ if (options.watchConfig) {
172
+ await this.configManager.watchConfig(true);
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Initialize logging system
178
+ * @private
179
+ */
180
+ async initializeLogging() {
181
+ const loggingConfig = this.config.logging || {};
182
+
183
+ this.logger = createLogger({
184
+ level: loggingConfig.level || 'info',
185
+ outputs: loggingConfig.outputs || ['console'],
186
+ colors: loggingConfig.colors !== false,
187
+ timestamp: loggingConfig.timestamp !== false,
188
+ logFile: loggingConfig.logFile,
189
+ maxFileSize: loggingConfig.maxFileSize,
190
+ maxFiles: loggingConfig.maxFiles
191
+ });
192
+
193
+ await this.logger.initialize();
194
+ }
195
+
196
+ /**
197
+ * Initialize persistent user data directory and migrate legacy data
198
+ * This ensures user data survives npm package updates
199
+ * @private
200
+ */
201
+ async initializeUserDataDirectory() {
202
+ try {
203
+ // Create user data directory structure
204
+ await ensureUserDataDirs();
205
+ const userDataDir = getUserDataDir();
206
+
207
+ this.logger.info('User data directory initialized', {
208
+ location: userDataDir,
209
+ platform: process.platform
210
+ });
211
+
212
+ // Check for legacy data and migrate if needed
213
+ const legacyPaths = getLegacyDataPaths();
214
+ for (const legacyPath of legacyPaths) {
215
+ try {
216
+ const result = await migrateFromOldLocation(legacyPath, {
217
+ dryRun: false,
218
+ logger: this.logger
219
+ });
220
+
221
+ if (result.migrated.length > 0) {
222
+ this.logger.info('Successfully migrated data from legacy location', {
223
+ from: legacyPath,
224
+ migratedCount: result.migrated.length
225
+ });
226
+ console.log(`📦 Migrated ${result.migrated.length} items from legacy location to ${userDataDir}`);
227
+ }
228
+ } catch (migrationError) {
229
+ // Non-fatal: log and continue
230
+ this.logger.warn('Failed to migrate from legacy location', {
231
+ legacyPath,
232
+ error: migrationError.message
233
+ });
234
+ }
235
+ }
236
+
237
+ } catch (error) {
238
+ this.logger.error('Failed to initialize user data directory', {
239
+ error: error.message
240
+ });
241
+ throw error;
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Initialize core system components
247
+ * @private
248
+ */
249
+ async initializeCoreComponents() {
250
+ this.logger.info('Initializing core components...');
251
+
252
+ // State Manager
253
+ this.stateManager = new StateManager(this.config, this.logger);
254
+
255
+ // File Attachment Service
256
+ this.fileAttachmentService = new FileAttachmentService(this.config, this.logger);
257
+ await this.fileAttachmentService.initialize();
258
+
259
+ // Context Manager
260
+ this.contextManager = new ContextManager(this.config, this.logger);
261
+
262
+ // Tools Registry and Async Tool Manager
263
+ this.toolsRegistry = new ToolsRegistry(this.logger);
264
+ this.asyncToolManager = new AsyncToolManager(this.config, this.logger);
265
+
266
+ // Agent Pool (with tools registry for prompt enhancement)
267
+ this.agentPool = new AgentPool(
268
+ this.config,
269
+ this.logger,
270
+ this.stateManager,
271
+ this.contextManager,
272
+ this.toolsRegistry
273
+ );
274
+
275
+ // Initialize Budget Service and Error Handler
276
+ this.budgetService = new BudgetService(this.config, this.logger);
277
+ this.errorHandler = new ErrorHandler(this.config, this.logger);
278
+
279
+ // API Key Manager
280
+ this.apiKeyManager = new ApiKeyManager(this.logger);
281
+ await this.apiKeyManager.initialize(); // Load persisted keys
282
+ // Register the singleton so cross-cutting services (composioService,
283
+ // etc.) can resolve vendor keys without explicit injection.
284
+ registerApiKeyManager(this.apiKeyManager);
285
+
286
+ // Credential Vault for secure website credential management
287
+ this.credentialVault = getCredentialVault(this.logger);
288
+ await this.credentialVault.initialize(); // Load persisted credentials
289
+
290
+ // WhatsApp Service (optional — requires whatsapp-web.js)
291
+ try {
292
+ const { default: WhatsAppService } = await import('./services/whatsappService.js');
293
+ this.whatsappService = new WhatsAppService(this.logger);
294
+ this.logger.info('WhatsApp service initialized');
295
+ } catch (e) {
296
+ this.whatsappService = null;
297
+ this.logger.info('WhatsApp service unavailable (whatsapp-web.js not installed)', { error: e.message });
298
+ }
299
+
300
+ // Telegram Service (optional — requires node-telegram-bot-api)
301
+ try {
302
+ const { getTelegramService } = await import('./services/telegramService.js');
303
+ this.telegramService = getTelegramService(this.logger);
304
+ this.logger.info('Telegram service initialized');
305
+ } catch (e) {
306
+ this.telegramService = null;
307
+ this.logger.info('Telegram service unavailable', { error: e.message });
308
+ }
309
+
310
+ // Discord Service (optional — requires discord.js)
311
+ try {
312
+ const { getDiscordService } = await import('./services/discordService.js');
313
+ this.discordService = getDiscordService(this.logger);
314
+ this.logger.info('Discord service initialized');
315
+ } catch (e) {
316
+ this.discordService = null;
317
+ this.logger.info('Discord service unavailable', { error: e.message });
318
+ }
319
+
320
+ // Schedule Service
321
+ this.scheduleService = new ScheduleService(this.logger);
322
+ await this.scheduleService.initialize();
323
+
324
+ // AI Service
325
+ this.aiService = new AIService(
326
+ this.config,
327
+ this.logger,
328
+ this.budgetService,
329
+ this.errorHandler
330
+ );
331
+
332
+ // Set API Key Manager reference in AI Service
333
+ this.aiService.setApiKeyManager(this.apiKeyManager);
334
+
335
+ // Set Agent Pool reference in AI Service
336
+ this.aiService.setAgentPool(this.agentPool);
337
+
338
+ // Embedding Service — opt-in semantic retrieval layer. Boots in the
339
+ // disabled state by default; loadPersistedConfig() picks up whatever
340
+ // the user previously chose in Settings.
341
+ const stateDir = getUserDataPaths().state;
342
+ this.embeddingService = new EmbeddingService({
343
+ // Start with the in-memory default (disabled). The settings file
344
+ // (if present) will override this on the next line.
345
+ config: this.config?.embeddings || {},
346
+ apiKeyManager: this.apiKeyManager,
347
+ baseUrl: this.config?.backend?.baseUrl || this.aiService.baseUrl,
348
+ stateDir,
349
+ logger: this.logger,
350
+ });
351
+ await this.embeddingService.loadPersistedConfig();
352
+ this.aiService.setEmbeddingService(this.embeddingService);
353
+ this.logger.info('[Loxia] Embedding service ready', {
354
+ provider: this.embeddingService.config.provider,
355
+ enabled: this.embeddingService.isEnabled,
356
+ });
357
+
358
+ // Initialize Model Routing Services
359
+ this.benchmarkService = new BenchmarkService(this.config, this.logger);
360
+ this.modelsService = new ModelsService(this.config, this.logger);
361
+ this.modelRouterService = new ModelRouterService(
362
+ this.config,
363
+ this.logger,
364
+ this.benchmarkService,
365
+ this.aiService
366
+ );
367
+
368
+ // Set API Key Manager reference in ModelsService
369
+ this.modelsService.setApiKeyManager(this.apiKeyManager);
370
+
371
+ // Set ModelsService reference in AI Service (for model suggestions on errors)
372
+ this.aiService.setModelsService(this.modelsService);
373
+
374
+ // Set ModelsService reference in BudgetService (for dynamic pricing lookup)
375
+ this.budgetService.setModelsService(this.modelsService);
376
+
377
+ // Initialize services
378
+ await this.benchmarkService.initialize();
379
+ await this.modelsService.initialize();
380
+
381
+ // Message Processor
382
+ this.messageProcessor = new MessageProcessor(
383
+ this.config,
384
+ this.logger,
385
+ this.toolsRegistry,
386
+ this.agentPool,
387
+ this.contextManager,
388
+ this.aiService,
389
+ this.modelRouterService,
390
+ this.modelsService
391
+ );
392
+
393
+ // Agent Scheduler - NEW ARCHITECTURE
394
+ this.agentScheduler = new AgentScheduler(
395
+ this.agentPool,
396
+ this.messageProcessor,
397
+ this.aiService,
398
+ this.logger,
399
+ null, // webSocketManager will be set later
400
+ this.modelRouterService,
401
+ this.modelsService
402
+ );
403
+
404
+ // Note: Scheduler will be started after WebSocketManager is initialized
405
+
406
+ // Orchestrator
407
+ this.orchestrator = new Orchestrator(
408
+ this.config,
409
+ this.logger,
410
+ this.agentPool,
411
+ this.messageProcessor,
412
+ this.aiService,
413
+ this.stateManager
414
+ );
415
+
416
+ // Expose toolsRegistry on the orchestrator so downstream wiring (e.g.
417
+ // webServer's flowExecutor init at webServer.js:574 → jobDoneTool
418
+ // setFlowExecutor) can reach it through the orchestrator handle. Without
419
+ // this, `this.orchestrator?.toolsRegistry` is undefined at wiring time
420
+ // and JobDoneTool's flow-contract validation (Phase 8) silently no-ops
421
+ // for the entire process lifetime.
422
+ this.orchestrator.toolsRegistry = this.toolsRegistry;
423
+
424
+ // Set cross-references between components
425
+ this.messageProcessor.orchestrator = this.orchestrator;
426
+ this.messageProcessor.setScheduler(this.agentScheduler);
427
+ this.agentPool.setMessageProcessor(this.messageProcessor);
428
+ this.agentPool.setScheduler(this.agentScheduler);
429
+ this.agentPool.setFileAttachmentService(this.fileAttachmentService);
430
+
431
+ // Attach FileAttachmentService to orchestrator for webServer access
432
+ this.orchestrator.fileAttachmentService = this.fileAttachmentService;
433
+
434
+ // Wire ScheduleService dependencies
435
+ this.scheduleService.setAgentPool(this.agentPool);
436
+ this.scheduleService.setMessageProcessor(this.messageProcessor);
437
+ this.scheduleService.setOrchestrator(this.orchestrator);
438
+
439
+ this.logger.info('Core components initialized');
440
+ }
441
+
442
+ /**
443
+ * Initialize tools system
444
+ * @private
445
+ */
446
+ async initializeTools() {
447
+ this.logger.info('Initializing tools...');
448
+
449
+ // Register Agent Delay Tool
450
+ await this.toolsRegistry.registerTool(AgentDelayTool);
451
+
452
+ // Register Terminal Tool
453
+ await this.toolsRegistry.registerTool(TerminalTool);
454
+
455
+ // Register File System Tool
456
+ await this.toolsRegistry.registerTool(FileSystemTool);
457
+
458
+ // Register Job Done Tool
459
+ await this.toolsRegistry.registerTool(JobDoneTool);
460
+
461
+ // Register Agent Communication Tool
462
+ await this.toolsRegistry.registerTool(AgentCommunicationTool);
463
+
464
+ // Register Task Manager Tool
465
+ await this.toolsRegistry.registerTool(TaskManagerTool);
466
+
467
+ // Register Import Analyzer Tool
468
+ await this.toolsRegistry.registerTool(ImportAnalyzerTool);
469
+
470
+ // Register Dependency Resolver Tool
471
+ await this.toolsRegistry.registerTool(DependencyResolverTool);
472
+
473
+ // Register Image Generation Tool
474
+ await this.toolsRegistry.registerTool(ImageTool);
475
+
476
+ // Register Video Generation Tool (Sora 2)
477
+ await this.toolsRegistry.registerTool(VideoTool);
478
+
479
+ // Register Static Analysis Tool
480
+ await this.toolsRegistry.registerTool(StaticAnalysisTool);
481
+
482
+ // Register Clone Detection Tool
483
+ await this.toolsRegistry.registerTool(CloneDetectionTool);
484
+
485
+ // Register File Tree Tool
486
+ await this.toolsRegistry.registerTool(FileTreeTool);
487
+
488
+ // Register File Content Replace Tool
489
+ await this.toolsRegistry.registerTool(FileContentReplaceTool);
490
+
491
+ // Register Seek Tool
492
+ await this.toolsRegistry.registerTool(SeekTool);
493
+
494
+ // Register Web Tool
495
+ await this.toolsRegistry.registerTool(WebTool);
496
+
497
+ // Register Foundry Web Search Tool (grounded, with citations).
498
+ // Distinct from WebTool: WebTool drives a real browser (puppeteer)
499
+ // for raw results / authenticated sites; FoundryWebSearchTool
500
+ // calls /llm/web-search which uses Azure AI Foundry's
501
+ // web_search_preview tool — single round-trip, synthesized answer
502
+ // with citation URLs. See docs/FOUNDRY_WEB_SEARCH_PROVISIONING.md
503
+ // on the backend side; the tool returns a clear "not provisioned"
504
+ // error until that's done.
505
+ await this.toolsRegistry.registerTool(FoundryWebSearchTool);
506
+
507
+ // Register Visual Editor Tool
508
+ await this.toolsRegistry.registerTool(VisualEditorTool);
509
+
510
+ // Register PDF Tool
511
+ await this.toolsRegistry.registerTool(PdfTool);
512
+
513
+ // Register Help Tool (two-layer tool description system)
514
+ await this.toolsRegistry.registerTool(HelpTool);
515
+
516
+ // Register Document (DOCX) Tool — legacy; superseded by OfficeTool.
517
+ // Kept for one release cycle so existing agent configs that reference
518
+ // toolId "doc" continue to work.
519
+ await this.toolsRegistry.registerTool(DocxTool);
520
+
521
+ // Register Spreadsheet (Excel) Tool — legacy; superseded by OfficeTool.
522
+ await this.toolsRegistry.registerTool(ExcelTool);
523
+
524
+ // Register Office Tool — consolidated doc/sheet/pres surface
525
+ // (Word + Excel + PowerPoint create/read).
526
+ await this.toolsRegistry.registerTool(OfficeTool);
527
+
528
+ // Register Memory Tool
529
+ await this.toolsRegistry.registerTool(MemoryTool);
530
+ await this.toolsRegistry.registerTool(SkillsTool);
531
+ await this.toolsRegistry.registerTool(VisionTool);
532
+
533
+ // Register Composio integration tool (off by default — agents
534
+ // must have the `composio` capability AND the operator must set
535
+ // COMPOSIO_API_KEY for it to do anything).
536
+ await this.toolsRegistry.registerTool(ComposioTool);
537
+
538
+ // Register User Prompt Tool
539
+ await this.toolsRegistry.registerTool(UserPromptTool);
540
+
541
+ // Register Code Map Tool
542
+ await this.toolsRegistry.registerTool(CodeMapTool);
543
+
544
+ // Register WhatsApp Tool
545
+ await this.toolsRegistry.registerTool(WhatsAppTool);
546
+
547
+ // Register Platform Control Tool — agent-facing platform control
548
+ // (currently: scheduled tasks; per-agent permission, default DISABLED).
549
+ await this.toolsRegistry.registerTool(PlatformControlTool);
550
+
551
+ // Register Desktop Tool — keyboard/mouse/screen/window control with
552
+ // visual grounding (Kimi K2.6 by default). Per-agent allowlist with
553
+ // an EMPTY default, so no agent can drive the desktop until the
554
+ // operator explicitly opts in via toolConfig.desktop.allowedActions.
555
+ // Global kill switch: LOXIA_DESKTOP_TOOL_DISABLED=1.
556
+ await this.toolsRegistry.registerTool(DesktopTool);
557
+
558
+ // widget-module: remove this block if the module is deleted.
559
+ // Registers the WidgetTool for agent-facing custom-UI rendering.
560
+ // Honors LOXIA_DISABLE_WIDGETS=1 env flag for a zero-source kill switch.
561
+ const widgetModule = await import('./modules/widget/index.js');
562
+ if (!widgetModule.isDisabled()) {
563
+ await this.toolsRegistry.registerTool(widgetModule.WidgetTool);
564
+ }
565
+
566
+ // Set ToolsRegistry dependency for HelpTool (two-layer tool description system)
567
+ const helpTool = this.toolsRegistry.getTool('help');
568
+ if (helpTool && typeof helpTool.setToolsRegistry === 'function') {
569
+ helpTool.setToolsRegistry(this.toolsRegistry);
570
+ this.logger.info('ToolsRegistry set for Help Tool');
571
+ }
572
+
573
+ // Set AgentPool dependency for AgentDelayTool
574
+ const agentDelayTool = this.toolsRegistry.getTool('agentdelay');
575
+ if (agentDelayTool && typeof agentDelayTool.setAgentPool === 'function') {
576
+ agentDelayTool.setAgentPool(this.agentPool);
577
+ }
578
+
579
+ // Wire ScheduleService into PlatformControlTool. Without this, the
580
+ // tool reports "ScheduleService is not available" for any feature
581
+ // action — useful as a defensive default but not what we want here.
582
+ const platformControlTool = this.toolsRegistry.getTool('platformcontrol');
583
+ if (platformControlTool) {
584
+ if (typeof platformControlTool.setScheduleService === 'function') {
585
+ platformControlTool.setScheduleService(this.scheduleService);
586
+ }
587
+ // Agent + team CRUD requires AgentPool, StateManager, MemoryService.
588
+ if (typeof platformControlTool.setAgentPool === 'function') {
589
+ platformControlTool.setAgentPool(this.agentPool);
590
+ }
591
+ if (typeof platformControlTool.setStateManager === 'function') {
592
+ platformControlTool.setStateManager(this.stateManager);
593
+ }
594
+ // Memory tool's team-pool actions (publish/search/unpublish) need
595
+ // StateManager for the team-membership ACL check. The wiring is
596
+ // optional: when StateManager isn't attached, team-scoped actions
597
+ // refuse cleanly rather than defaulting open.
598
+ const memoryTool = this.toolsRegistry?.getTool?.('memory');
599
+ if (memoryTool && typeof memoryTool.setStateManager === 'function') {
600
+ memoryTool.setStateManager(this.stateManager);
601
+ }
602
+ if (typeof platformControlTool.setMemoryService === 'function') {
603
+ const { getMemoryService } = await import('./services/memoryService.js');
604
+ platformControlTool.setMemoryService(getMemoryService(this.logger));
605
+ }
606
+ // Flow CRUD + execution requires the FlowExecutor (created later in
607
+ // the boot sequence by the webServer). The webServer init also
608
+ // calls setFlowExecutor on this tool when it spins up — so this
609
+ // call is the one that fires when the executor is already alive
610
+ // (rare during startup), and the webServer call covers the normal
611
+ // path. Both are idempotent.
612
+ if (typeof platformControlTool.setFlowExecutor === 'function' && this.flowExecutor) {
613
+ platformControlTool.setFlowExecutor(this.flowExecutor);
614
+ }
615
+ }
616
+
617
+ // Set AgentPool dependency for JobDoneTool
618
+ const jobDoneTool = this.toolsRegistry.getTool('jobdone');
619
+ if (jobDoneTool && typeof jobDoneTool.setAgentPool === 'function') {
620
+ jobDoneTool.setAgentPool(this.agentPool);
621
+ }
622
+
623
+ // Set AgentPool and Scheduler dependencies for TaskManagerTool
624
+ const taskManagerTool = this.toolsRegistry.getTool('taskmanager');
625
+ if (taskManagerTool && typeof taskManagerTool.setAgentPool === 'function') {
626
+ taskManagerTool.setAgentPool(this.agentPool);
627
+ }
628
+ if (taskManagerTool && typeof taskManagerTool.setScheduler === 'function') {
629
+ taskManagerTool.setScheduler(this.scheduler);
630
+ }
631
+
632
+ // Note: AgentCommunicationTool receives agentPool through execution context
633
+ // No need to set it directly as it's passed in the context parameter
634
+ const agentCommTool = this.toolsRegistry.getTool('agentcommunication');
635
+ if (agentCommTool) {
636
+ this.logger.info('Agent Communication Tool registered successfully');
637
+ }
638
+
639
+ // Set AIService dependency for ImageTool
640
+ const imageTool = this.toolsRegistry.getTool('image-gen');
641
+ if (imageTool && typeof imageTool.setAIService === 'function') {
642
+ imageTool.setAIService(this.aiService);
643
+ this.logger.info('AIService set for Image Generation Tool');
644
+ }
645
+
646
+ // Set AgentPool dependency for ImageTool (for conversation history persistence)
647
+ if (imageTool && typeof imageTool.setAgentPool === 'function') {
648
+ imageTool.setAgentPool(this.agentPool);
649
+ this.logger.info('AgentPool set for Image Generation Tool');
650
+ }
651
+
652
+ // Set dependencies for VisionTool
653
+ const visionTool = this.toolsRegistry.getTool('vision');
654
+ if (visionTool && typeof visionTool.setAIService === 'function') {
655
+ visionTool.setAIService(this.aiService);
656
+ if (this.modelsService) visionTool.setModelsService(this.modelsService);
657
+ if (this.agentPool) visionTool.setAgentPool(this.agentPool);
658
+ this.logger.info('Dependencies set for Vision Tool');
659
+ }
660
+
661
+ // Video generation (Sora 2)
662
+ const videoTool = this.toolsRegistry.getTool('video-gen');
663
+ if (videoTool && typeof videoTool.setAIService === 'function') {
664
+ videoTool.setAIService(this.aiService);
665
+ this.logger.info('AIService set for Video Generation Tool');
666
+ }
667
+
668
+ // Foundry web search — uses /llm/web-search backed by Azure AI
669
+ // Foundry + Grounding with Bing Search. Calls aiService.foundryWebSearch
670
+ // for the auth + URL plumbing.
671
+ const foundryWebTool = this.toolsRegistry.getTool('foundry_web_search');
672
+ if (foundryWebTool && typeof foundryWebTool.setAIService === 'function') {
673
+ foundryWebTool.setAIService(this.aiService);
674
+ this.logger.info('AIService set for Foundry Web Search Tool');
675
+ }
676
+
677
+ // Desktop tool — needs aiService for proxy-mode grounding auth
678
+ // (baseUrl + per-session Loxia key from apiKeyManager).
679
+ const desktopTool = this.toolsRegistry.getTool('desktop');
680
+ if (desktopTool && typeof desktopTool.setAIService === 'function') {
681
+ desktopTool.setAIService(this.aiService);
682
+ this.logger.info('AIService set for Desktop Tool');
683
+ }
684
+ if (videoTool && typeof videoTool.setAgentPool === 'function') {
685
+ videoTool.setAgentPool(this.agentPool);
686
+ this.logger.info('AgentPool set for Video Generation Tool');
687
+ }
688
+
689
+ // Set WhatsApp Service dependency for WhatsApp Tool
690
+ const whatsappTool = this.toolsRegistry.getTool('whatsapp');
691
+ if (whatsappTool && typeof whatsappTool.setWhatsAppService === 'function') {
692
+ whatsappTool.setWhatsAppService(this.whatsappService);
693
+ this.logger.info('WhatsAppService set for WhatsApp Tool');
694
+ }
695
+
696
+ // ─── Catch-all aiService wiring ────────────────────────────────
697
+ //
698
+ // Several tools read `this.aiService.getEmbeddingService()` for their
699
+ // semantic-search / find-action paths (memory, agentcommunication,
700
+ // taskmanager, skills, composio) but have no `setAIService` method
701
+ // and were never wired by the explicit calls above. Production silently
702
+ // returned `this.aiService === undefined` → `_getIndexer()` returned
703
+ // null → every embedding-backed action surfaced "Embeddings are not
704
+ // enabled" even when embeddings WERE enabled (the user just hit this
705
+ // on composio.find-action; auto-recall in agentScheduler uses a
706
+ // different code path so it kept working, masking the wider gap).
707
+ //
708
+ // We sweep every registered tool here, AFTER the explicit per-tool
709
+ // wiring above. Tools that already have aiService set (image, vision,
710
+ // video, foundry-web-search, desktop) are skipped; everyone else
711
+ // gets it now. The assignment is harmless for tools that ignore the
712
+ // property.
713
+ {
714
+ let wired = 0;
715
+ for (const tool of this.toolsRegistry.tools.values()) {
716
+ if (!tool.aiService) {
717
+ tool.aiService = this.aiService;
718
+ wired++;
719
+ }
720
+ }
721
+ this.logger.info('AIService wired onto remaining tools', { count: wired });
722
+ }
723
+
724
+ const toolCapabilities = this.toolsRegistry.getToolCapabilities();
725
+ this.logger.info('Tools initialized', {
726
+ toolCount: Object.keys(toolCapabilities).length,
727
+ enabledTools: Object.keys(toolCapabilities).filter(id => toolCapabilities[id].capabilities.enabled),
728
+ registeredTools: this.toolsRegistry.listTools()
729
+ });
730
+
731
+ // Log tool descriptions for debugging
732
+ if (this.logger.level === 'debug') {
733
+ for (const [toolId, tool] of Object.entries(toolCapabilities)) {
734
+ this.logger.debug(`Tool ${toolId} capabilities`, tool.capabilities);
735
+ }
736
+ }
737
+ }
738
+
739
+ /**
740
+ * Initialize interface handlers
741
+ * @private
742
+ */
743
+ async initializeInterfaces() {
744
+ this.logger.info('Initializing interfaces...');
745
+
746
+ const interfaceConfig = this.config.interfaces || {};
747
+ const uiMode = process.env.LOXIA_UI_MODE || 'cli'; // Default to old CLI
748
+
749
+ // CLI Interface - Load old readline CLI (unless terminal UI mode is specified)
750
+ // NOTE: Terminal UI mode runs as a separate WebSocket client, not in the main process
751
+ if (interfaceConfig.cli?.enabled !== false && uiMode !== 'terminal') {
752
+ // Use old CLI (readline-based)
753
+ this.logger.info('Loading CLI (readline)...');
754
+ const { default: CLIInterface } = await import('./interfaces/cli.js');
755
+ const cliInterface = new CLIInterface(
756
+ this.orchestrator,
757
+ this.logger,
758
+ interfaceConfig.cli || {}
759
+ );
760
+
761
+ await cliInterface.initialize();
762
+ this.interfaces.set(INTERFACE_TYPES.CLI, cliInterface);
763
+
764
+ this.logger.info('CLI interface initialized');
765
+ }
766
+
767
+ // If terminal UI mode, skip CLI - Terminal UI will connect as WebSocket client
768
+ if (uiMode === 'terminal') {
769
+ this.logger.info('Terminal UI mode: Server-only startup (Terminal UI will connect as WebSocket client)');
770
+ }
771
+
772
+ // Web Interface - now implemented
773
+ if (interfaceConfig.web?.enabled !== false) {
774
+ // Read port from environment variables (set by CLI) or use config defaults
775
+ const webPort = parseInt(process.env.LOXIA_PORT || process.env.PORT, 10) || 8080;
776
+ // Use env var, then config, then 0.0.0.0 (accept connections from all interfaces)
777
+ const webHost = process.env.LOXIA_HOST || interfaceConfig.web?.host || '0.0.0.0';
778
+
779
+ const webConfig = {
780
+ ...interfaceConfig.web,
781
+ port: webPort,
782
+ host: webHost,
783
+ backend: this.config.backend
784
+ };
785
+
786
+ const webServer = new WebServer(
787
+ this.orchestrator,
788
+ this.logger,
789
+ webConfig
790
+ );
791
+
792
+ // Pass toolsRegistry to webServer for the /api/tools endpoint
793
+ webServer.toolsRegistry = this.toolsRegistry;
794
+
795
+ // Set API Key Manager reference in Web Server
796
+ webServer.setApiKeyManager(this.apiKeyManager);
797
+
798
+ // Embedding service handle so the /api/embeddings/* routes can
799
+ // read config + telemetry, hot-swap providers, etc.
800
+ webServer.embeddingService = this.embeddingService;
801
+
802
+ // Set Credential Vault reference in Web Server
803
+ webServer.setCredentialVault(this.credentialVault);
804
+
805
+ // Set WhatsApp Service reference in Web Server
806
+ if (this.whatsappService) {
807
+ webServer.setWhatsAppService(this.whatsappService);
808
+ this.whatsappService.setWebSocketManager(webServer);
809
+ // Auto-reconnect if a previous session exists (non-blocking)
810
+ this.whatsappService.autoReconnect().catch(e =>
811
+ this.logger.warn('WhatsApp auto-reconnect failed', { error: e.message })
812
+ );
813
+ }
814
+
815
+ // Set Telegram Service references
816
+ if (this.telegramService) {
817
+ webServer.setTelegramService(this.telegramService);
818
+ this.telegramService.setOrchestrator(this.orchestrator);
819
+ this.telegramService.setAgentPool(this.agentPool);
820
+ this.telegramService.setWebSocketManager(webServer);
821
+ if (this.flowExecutor) this.telegramService.setFlowExecutor(this.flowExecutor);
822
+ // Backend URL + platform API key are needed for the voice-note
823
+ // → /llm/transcribe path. Without these, voice messages return
824
+ // a friendly "voice not configured" message and the user is
825
+ // told to type instead. Both come from the same sources the
826
+ // rest of the CLI's /api/* proxy code uses.
827
+ const backendBaseUrl = this.config?.backend?.baseUrl
828
+ || webServer?.config?.backend?.baseUrl
829
+ || null;
830
+ if (backendBaseUrl) this.telegramService.setBackendBaseUrl(backendBaseUrl);
831
+ // Pass a getter (not a snapshotted value) so the key is
832
+ // re-resolved on every transcribe call. This lets the user
833
+ // sign in AFTER boot — voice notes start working immediately
834
+ // without restarting the server.
835
+ this.telegramService.setPlatformApiKey(
836
+ () => this.apiKeyManager?.getKeysForRequest?.(null, { platformProvided: true })?.loxiaApiKey || null
837
+ );
838
+ // Let the scheduler query bridge-state per agent so it can inject
839
+ // the <external> routing guidance into system prompts only when the
840
+ // agent is actually addressable from Telegram.
841
+ this.agentScheduler?.setTelegramService?.(this.telegramService);
842
+ // Auto-connect if token exists in config (non-blocking)
843
+ this.telegramService.autoConnect().catch(e =>
844
+ this.logger.warn('Telegram auto-connect failed', { error: e.message })
845
+ );
846
+ }
847
+
848
+ // Set Discord Service references
849
+ if (this.discordService) {
850
+ webServer.setDiscordService(this.discordService);
851
+ this.discordService.setOrchestrator(this.orchestrator);
852
+ this.discordService.setAgentPool(this.agentPool);
853
+ this.discordService.setWebSocketManager(webServer);
854
+ if (this.flowExecutor) this.discordService.setFlowExecutor(this.flowExecutor);
855
+ // Same bridge-awareness hook as Telegram above — the scheduler
856
+ // only appends <external> guidance when `isAgentBridged(agentId)`
857
+ // returns true for this channel.
858
+ this.agentScheduler?.setDiscordService?.(this.discordService);
859
+ // Auto-connect if token exists in config (non-blocking)
860
+ this.discordService.autoConnect().catch(e =>
861
+ this.logger.warn('Discord auto-connect failed', { error: e.message })
862
+ );
863
+ }
864
+
865
+ await webServer.initialize();
866
+ this.interfaces.set(INTERFACE_TYPES.WEB, webServer);
867
+
868
+ // Attach WebServer to orchestrator for MessageProcessor broadcasting
869
+ this.orchestrator.webServer = webServer;
870
+
871
+ // Connect MessageProcessor to WebServer for real-time updates
872
+ this.messageProcessor.setWebSocketManager(webServer);
873
+
874
+ // Connect AgentScheduler to WebServer for real-time updates
875
+ this.agentScheduler.webSocketManager = webServer;
876
+
877
+ // Start the scheduler now that WebSocketManager is available
878
+ this.agentScheduler.start();
879
+ this.logger.info('Agent Scheduler started with WebSocket integration');
880
+
881
+ // Wire ScheduleService to WebServer and FlowExecutor
882
+ webServer.setScheduleService(this.scheduleService);
883
+ this.scheduleService.setWebSocketManager(webServer);
884
+ if (webServer.flowExecutor) {
885
+ this.scheduleService.setFlowExecutor(webServer.flowExecutor);
886
+ }
887
+ this.scheduleService.start();
888
+ this.logger.info('ScheduleService started with WebSocket integration');
889
+
890
+ // Set global reference for tools that need to broadcast
891
+ global.loxiaWebServer = webServer;
892
+
893
+ // Connect JobDoneTool to WebServer for broadcasting mode changes
894
+ const jobDoneTool = this.toolsRegistry.getTool('jobdone');
895
+ if (jobDoneTool && typeof jobDoneTool.setWebSocketManager === 'function') {
896
+ jobDoneTool.setWebSocketManager(webServer);
897
+ this.logger.info('WebSocketManager set for JobDone Tool');
898
+ }
899
+
900
+ // Connect UserPromptTool to WebServer and AgentPool for user prompting
901
+ const userPromptTool = this.toolsRegistry.getTool('userprompt');
902
+ if (userPromptTool) {
903
+ if (typeof userPromptTool.setWebSocketManager === 'function') {
904
+ userPromptTool.setWebSocketManager(webServer);
905
+ }
906
+ if (typeof userPromptTool.setAgentPool === 'function') {
907
+ userPromptTool.setAgentPool(this.agentPool);
908
+ }
909
+ this.logger.info('Dependencies set for UserPrompt Tool');
910
+ }
911
+
912
+ const status = webServer.getStatus();
913
+ this.logger.info('Web interface initialized', { url: status.url });
914
+ console.log(`🌐 Server running at ${status.url}`);
915
+ console.log(`📱 Web UI available at: ${status.url}`);
916
+
917
+ // Auto-open browser (skip when running inside Electron — it creates its own window)
918
+ if (!process.env.LOXIA_ELECTRON) {
919
+ const url = status.url.replace('0.0.0.0', 'localhost');
920
+ const platform = process.platform;
921
+ const openCmd = platform === 'win32' ? `start "" "${url}"`
922
+ : platform === 'darwin' ? `open "${url}"`
923
+ : `xdg-open "${url}"`;
924
+ exec(openCmd, (err) => {
925
+ if (err) this.logger.debug('Could not auto-open browser', { error: err.message });
926
+ });
927
+ }
928
+ }
929
+
930
+ // VSCode Extension Interface (placeholder)
931
+ if (interfaceConfig.vscode?.enabled === true) {
932
+ this.logger.info('VSCode interface configured but not implemented yet');
933
+ // TODO: Initialize VSCode extension interface
934
+ }
935
+ }
936
+
937
+ /**
938
+ * Setup graceful shutdown handlers
939
+ * @private
940
+ */
941
+ setupShutdownHandlers() {
942
+ // Handle SIGINT (Ctrl+C)
943
+ process.on('SIGINT', async () => {
944
+ console.log('\n📋 Received SIGINT, shutting down gracefully...');
945
+ await this.shutdown();
946
+ });
947
+
948
+ // Handle SIGTERM
949
+ process.on('SIGTERM', async () => {
950
+ console.log('\n📋 Received SIGTERM, shutting down gracefully...');
951
+ await this.shutdown();
952
+ });
953
+
954
+ // Handle uncaught exceptions
955
+ process.on('uncaughtException', async (error) => {
956
+ console.error('❌ Uncaught exception:', error);
957
+ if (this.logger) {
958
+ this.logger.error('Uncaught exception', {
959
+ error: error.message,
960
+ stack: error.stack
961
+ });
962
+ }
963
+
964
+ await this.shutdown();
965
+ process.exit(1);
966
+ });
967
+
968
+ // Handle unhandled promise rejections
969
+ process.on('unhandledRejection', async (reason, promise) => {
970
+ const reasonMessage = reason?.message || String(reason);
971
+
972
+ // List of known non-critical rejections that shouldn't crash the server
973
+ const nonCriticalPatterns = [
974
+ 'Credential request cancelled',
975
+ 'Credential request timed out',
976
+ 'Target closed',
977
+ 'Session closed',
978
+ 'Protocol error',
979
+ 'Navigation timeout',
980
+ 'net::ERR_',
981
+ 'Requesting main frame too early',
982
+ 'Connection closed',
983
+ // AI/model errors should NOT crash the server
984
+ 'HTTP 4', // 400, 401, 403, 404, 429, etc.
985
+ 'HTTP 5', // 500, 502, 503, etc.
986
+ 'circuit breaker',
987
+ 'Rate limit',
988
+ 'Insufficient credits',
989
+ 'not suitable for chat',
990
+ 'No API key configured',
991
+ 'Message content is empty',
992
+ 'Backend returned malformed',
993
+ 'stream generator',
994
+ 'No response choices',
995
+ 'model error',
996
+ 'isModelError',
997
+ 'Service temporarily unavailable',
998
+ 'Failed to fetch models',
999
+ 'ECONNREFUSED',
1000
+ 'ETIMEDOUT',
1001
+ 'ECONNRESET',
1002
+ 'fetch failed',
1003
+ 'AbortError',
1004
+ 'The operation was aborted'
1005
+ ];
1006
+
1007
+ const isNonCritical = nonCriticalPatterns.some(pattern =>
1008
+ reasonMessage.includes(pattern)
1009
+ );
1010
+
1011
+ if (isNonCritical) {
1012
+ console.warn('⚠️ Non-critical promise rejection (server continues):', reasonMessage);
1013
+ if (this.logger) {
1014
+ this.logger.warn('Non-critical promise rejection', {
1015
+ reason: reasonMessage,
1016
+ promise: promise.toString()
1017
+ });
1018
+ }
1019
+ return; // Don't crash for non-critical errors
1020
+ }
1021
+
1022
+ console.error('❌ Unhandled promise rejection:', reason);
1023
+ if (this.logger) {
1024
+ this.logger.error('Unhandled promise rejection', {
1025
+ reason: reasonMessage,
1026
+ promise: promise.toString()
1027
+ });
1028
+ }
1029
+
1030
+ await this.shutdown();
1031
+ process.exit(1);
1032
+ });
1033
+ }
1034
+
1035
+ /**
1036
+ * Gracefully shutdown the application
1037
+ * @returns {Promise<void>}
1038
+ */
1039
+ async shutdown() {
1040
+ if (this.isShuttingDown) {
1041
+ return;
1042
+ }
1043
+
1044
+ this.isShuttingDown = true;
1045
+
1046
+ // Force-exit safety net: if graceful shutdown hangs, kill the process after 10s
1047
+ const forceExitTimer = setTimeout(() => {
1048
+ console.error('⚠️ Graceful shutdown timed out after 10s — forcing exit');
1049
+ process.exit(1);
1050
+ }, 10000);
1051
+ forceExitTimer.unref(); // Don't let this timer keep the process alive on its own
1052
+
1053
+ try {
1054
+ console.log('🛑 Shutting down Loxia Autopilot One...');
1055
+
1056
+ if (this.logger) {
1057
+ this.logger.info('Application shutdown initiated');
1058
+ }
1059
+
1060
+ // Stop schedule service first
1061
+ if (this.scheduleService) {
1062
+ this.scheduleService.stop();
1063
+ this.logger?.info('Schedule service stopped');
1064
+ }
1065
+
1066
+ // Stop agent scheduler (prevents new work from starting mid-shutdown)
1067
+ if (this.agentScheduler) {
1068
+ this.agentScheduler.stop();
1069
+ this.logger?.info('Agent scheduler stopped');
1070
+ }
1071
+
1072
+ // Cancel any pending model fetch retries
1073
+ if (this.modelsService?._cancelRetry) {
1074
+ this.modelsService._cancelRetry();
1075
+ this.logger?.info('Models service retries cancelled');
1076
+ }
1077
+
1078
+ // Close Puppeteer browser (webTool) — it holds DevTools ports
1079
+ if (this.toolsRegistry) {
1080
+ for (const toolId of ['web']) {
1081
+ try {
1082
+ const tool = this.toolsRegistry.getTool(toolId);
1083
+ if (tool?.cleanup) {
1084
+ await tool.cleanup();
1085
+ this.logger?.info(`${toolId} tool cleanup complete`);
1086
+ }
1087
+ } catch (error) {
1088
+ this.logger?.warn(`Failed to cleanup ${toolId} tool`, { error: error.message });
1089
+ }
1090
+ }
1091
+ }
1092
+
1093
+ // Kill ALL running terminal processes across all agents
1094
+ if (this.toolsRegistry) {
1095
+ try {
1096
+ const terminalTool = this.toolsRegistry.getTool('terminal');
1097
+ if (terminalTool?.commandTracker) {
1098
+ let killed = 0;
1099
+ for (const [, cmdInfo] of terminalTool.commandTracker) {
1100
+ if (cmdInfo.process && cmdInfo.state === 'RUNNING') {
1101
+ try {
1102
+ cmdInfo.process.kill('SIGTERM');
1103
+ killed++;
1104
+ } catch { /* already dead */ }
1105
+ }
1106
+ }
1107
+ if (killed > 0) {
1108
+ this.logger?.info(`Killed ${killed} running terminal process(es) on shutdown`);
1109
+ }
1110
+ }
1111
+ } catch (error) {
1112
+ this.logger?.warn('Failed to cleanup terminal processes', { error: error.message });
1113
+ }
1114
+ }
1115
+
1116
+ // Shutdown interfaces (web server, visual editor, WS connections)
1117
+ for (const [type, interface_] of this.interfaces) {
1118
+ try {
1119
+ if (interface_.shutdown) {
1120
+ await interface_.shutdown();
1121
+ }
1122
+ this.logger?.info(`${type} interface shutdown complete`);
1123
+ } catch (error) {
1124
+ console.error(`Failed to shutdown ${type} interface:`, error.message);
1125
+ }
1126
+ }
1127
+
1128
+ // Shutdown async tool manager
1129
+ if (this.asyncToolManager) {
1130
+ await this.asyncToolManager.shutdown();
1131
+ this.logger?.info('Async tool manager shutdown complete');
1132
+ }
1133
+
1134
+ // Shutdown orchestrator (persists agent states)
1135
+ if (this.orchestrator) {
1136
+ await this.orchestrator.shutdown();
1137
+ this.logger?.info('Orchestrator shutdown complete');
1138
+ }
1139
+
1140
+ // Cleanup configuration manager
1141
+ if (this.configManager) {
1142
+ this.configManager.cleanup();
1143
+ }
1144
+
1145
+ // Close logger
1146
+ if (this.logger) {
1147
+ await this.logger.close();
1148
+ }
1149
+
1150
+ console.log('✅ Loxia Autopilot One shutdown complete');
1151
+
1152
+ } catch (error) {
1153
+ console.error('❌ Error during shutdown:', error.message);
1154
+ } finally {
1155
+ process.exit(0);
1156
+ }
1157
+ }
1158
+
1159
+ /**
1160
+ * Get application status
1161
+ * @returns {Object} Application status
1162
+ */
1163
+ getStatus() {
1164
+ return {
1165
+ version: SYSTEM_VERSION,
1166
+ uptime: process.uptime(),
1167
+ memoryUsage: process.memoryUsage(),
1168
+ interfaces: Array.from(this.interfaces.keys()),
1169
+ isShuttingDown: this.isShuttingDown
1170
+ };
1171
+ }
1172
+ }
1173
+
1174
+ /**
1175
+ * Main application entry point
1176
+ */
1177
+ async function main() {
1178
+ const app = new LoxiaApplication();
1179
+
1180
+ // Parse command line arguments
1181
+ const args = process.argv.slice(2);
1182
+ const options = {
1183
+ projectDir: process.cwd(),
1184
+ watchConfig: args.includes('--watch-config'),
1185
+ configPaths: []
1186
+ };
1187
+
1188
+ // Parse --port and --host from argv so they work when running index.js directly
1189
+ // (bin/cli.js sets these as env vars, but npm start / node src/index.js bypasses cli.js)
1190
+ for (let i = 0; i < args.length; i++) {
1191
+ if (args[i] === '--port' && args[i + 1]) {
1192
+ process.env.LOXIA_PORT = args[i + 1];
1193
+ }
1194
+ if (args[i] === '--host' && args[i + 1]) {
1195
+ process.env.LOXIA_HOST = args[i + 1];
1196
+ }
1197
+ }
1198
+
1199
+ // Look for custom config file
1200
+ const configIndex = args.indexOf('--config');
1201
+ if (configIndex !== -1 && args[configIndex + 1]) {
1202
+ options.configPaths.push(args[configIndex + 1]);
1203
+ }
1204
+
1205
+ await app.initialize(options);
1206
+
1207
+ // Keep the application running
1208
+ return app;
1209
+ }
1210
+
1211
+ // Start the application if this file is run directly.
1212
+ const __filename = resolveModuleFilename(import.meta.url);
1213
+ if (process.argv[1] === __filename) {
1214
+ console.log('🚀 Starting Loxia Autopilot One...');
1215
+ main().catch(error => {
1216
+ console.error('❌ Failed to start Loxia Autopilot One:', error.message);
1217
+ console.error('Stack trace:', error.stack);
1218
+ process.exit(1);
1219
+ });
1220
+ }
1221
+
1222
1222
  export { LoxiaApplication, main };