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
@@ -1,955 +1,954 @@
1
- /**
2
- * MessageProcessor - Processes messages from agents, extracts tool commands, executes tools
3
- *
4
- * NEW ARCHITECTURE:
5
- * - Only handles message queuing and tool execution
6
- * - No scheduling or autonomous loops (handled by AgentScheduler)
7
- * - Clean separation of concerns
8
- */
9
-
10
- import { AGENT_MODES } from '../utilities/constants.js';
11
- import TagParser from '../utilities/tagParser.js';
12
- import { TOOL_IDS, COMMAND_FORMATS } from '../utilities/toolConstants.js';
13
- import { getVisualEditorBridge } from '../services/visualEditorBridge.js';
14
- import { VisualEditorTool } from '../tools/visualEditorTool.js';
15
- import { prependSourceHeader } from '../services/messageSource.js';
16
-
17
- class MessageProcessor {
18
- constructor(config, logger, toolsRegistry, agentPool, contextManager, aiService, modelRouterService = null, modelsService = null) {
19
- this.config = config;
20
- this.logger = logger;
21
- this.toolsRegistry = toolsRegistry;
22
- this.agentPool = agentPool;
23
- this.contextManager = contextManager;
24
- this.aiService = aiService;
25
- this.modelRouterService = modelRouterService;
26
- this.modelsService = modelsService;
27
-
28
- // Active async operations tracking
29
- this.asyncOperations = new Map();
30
-
31
- // Tool execution history
32
- this.executionHistory = new Map();
33
-
34
- // Operation ID counter
35
- this.operationCounter = 0;
36
-
37
- // WebSocket manager for real-time updates
38
- this.webSocketManager = null;
39
-
40
- // AgentScheduler reference
41
- this.scheduler = null;
42
-
43
- // Orchestrator reference (for backward compatibility)
44
- this.orchestrator = null;
45
-
46
- // Initialize TagParser for comprehensive tool command extraction
47
- this.tagParser = new TagParser();
48
- }
49
-
50
- /**
51
- * Set WebSocket manager for real-time UI updates
52
- * @param {Object} webSocketManager - WebSocket manager instance
53
- */
54
- setWebSocketManager(webSocketManager) {
55
- this.webSocketManager = webSocketManager;
56
- this.logger?.info('WebSocket manager set for MessageProcessor', {
57
- hasManager: !!webSocketManager
58
- });
59
- }
60
-
61
- /**
62
- * Set AgentScheduler reference
63
- * @param {AgentScheduler} scheduler - AgentScheduler instance
64
- */
65
- setScheduler(scheduler) {
66
- this.scheduler = scheduler;
67
- this.logger?.info('AgentScheduler set for MessageProcessor', {
68
- hasScheduler: !!scheduler
69
- });
70
- }
71
-
72
- /**
73
- * Main message processing entry point - NEW ARCHITECTURE
74
- * Simply queues messages for scheduler processing
75
- * @param {string} agentId - Target agent ID
76
- * @param {string} message - Message content
77
- * @param {Object} context - Message context
78
- * @returns {Promise<Object>} Queuing result
79
- */
80
- async processMessage(agentId, message, context = {}) {
81
- const agent = await this.agentPool.getAgent(agentId);
82
- if (!agent) {
83
- throw new Error(`Agent not found: ${agentId}`);
84
- }
85
-
86
- const messageString = typeof message === 'string' ? message : (message ? JSON.stringify(message) : '');
87
- this.logger.info(`Queueing message for agent: ${agentId}`, {
88
- messageLength: messageString.length,
89
- messageType: typeof message,
90
- isInterAgentMessage: context.isInterAgentMessage,
91
- contextMessageType: context.messageType || 'user'
92
- });
93
-
94
- // Determine message type and queue appropriately
95
- if (context.isInterAgentMessage) {
96
- // Inter-agent message
97
- await this.agentPool.addInterAgentMessage(agentId, {
98
- content: message,
99
- sender: context.originalSender,
100
- senderName: context.senderName,
101
- subject: context.subject || 'Inter-agent message',
102
- timestamp: new Date().toISOString(),
103
- sessionId: context.sessionId,
104
- requiresReply: context.requiresReply || false
105
- });
106
- } else {
107
- // User message
108
- // Phase 4: Inject visual context if available
109
- let enhancedMessage = message;
110
- let visualContextInjected = false;
111
-
112
- try {
113
- const bridge = getVisualEditorBridge();
114
- if (bridge.isEnabled() && bridge.hasInstance(agentId)) {
115
- const visualContext = bridge.getVisualContext(agentId);
116
- if (visualContext) {
117
- enhancedMessage = VisualEditorTool.injectContextIntoMessage(message, visualContext);
118
- visualContextInjected = true;
119
-
120
- this.logger.info(`Visual context injected for agent: ${agentId}`, {
121
- selector: visualContext.selector,
122
- sourceFile: visualContext.sourceHint?.file
123
- });
124
-
125
- // Clear context after injection (configurable)
126
- if (this.config.visualEditor?.clearContextAfterInjection !== false) {
127
- bridge.clearVisualContext(agentId);
128
- }
129
- }
130
- }
131
- } catch (err) {
132
- // Visual context injection is optional - don't fail the message
133
- this.logger.warn?.(`Failed to inject visual context: ${err.message}`);
134
- }
135
-
136
- // Prepend the source attribution header when the message arrived from
137
- // an external channel (Discord / Telegram). The header is plain text
138
- // of the form "(Message by alice from Discord > MyGuild > #ops)" and
139
- // becomes part of the persisted content, so the agent sees it in its
140
- // LLM context and the operator sees it in the web-UI transcript. The
141
- // operation is idempotent re-serialization (state restore, replay)
142
- // won't double-prefix. Sources of kind web/api/internal produce no
143
- // header and leave the content untouched. See services/messageSource.js.
144
- if (context.source) {
145
- enhancedMessage = prependSourceHeader(enhancedMessage, context.source);
146
- }
147
-
148
- await this.agentPool.addUserMessage(agentId, {
149
- content: enhancedMessage,
150
- role: 'user',
151
- timestamp: new Date().toISOString(),
152
- contextReferences: context.contextReferences || [],
153
- sessionId: context.sessionId,
154
- visualContextInjected,
155
- streamingEnabled: context.streamingEnabled !== false, // Pass streaming preference from context
156
- // Preserve the structured source on the queued message too, so any
157
- // downstream consumer (logs, analytics, relay) can reason about
158
- // provenance without re-parsing the inline header.
159
- source: context.source || null,
160
- // Flow execution context (if this message is part of a flow)
161
- isFlowExecution: context.isFlowExecution || false,
162
- flowRunId: context.flowRunId,
163
- flowNodeId: context.flowNodeId,
164
- flowMetadata: context.flowMetadata,
165
- previousAgentData: context.previousAgentData,
166
- // v2: typed I/O contract for this node { inputs, outputs }.
167
- // Forwarded to the scheduler so the system prompt can advertise
168
- // the exact named/typed payload the agent must produce.
169
- nodeContract: context.nodeContract
170
- });
171
- }
172
-
173
- // Register session with scheduler for API key resolution
174
- // NOTE: The scheduler uses AgentActivityService to determine which agents
175
- // should be active based on their message queues - we just register the session here
176
- if (this.scheduler) {
177
- await this.scheduler.addAgent(agentId, {
178
- triggeredBy: context.isInterAgentMessage ? 'inter-agent-message' : 'user-message',
179
- sessionId: context.sessionId
180
- });
181
- }
182
-
183
- return {
184
- success: true,
185
- message: 'Message queued for processing',
186
- agentId: agentId,
187
- queuedAt: new Date().toISOString()
188
- };
189
- }
190
-
191
- /**
192
- * Unwrap TagParser format parameters
193
- * TagParser wraps XML parameters in {value, attributes} objects
194
- * This method unwraps them to direct values for tool consumption
195
- * @param {Object} params - Parameters to unwrap
196
- * @returns {Object} Unwrapped parameters
197
- */
198
- unwrapParameters(params) {
199
- if (!params || typeof params !== 'object') {
200
- return params;
201
- }
202
-
203
- // Handle arrays - recursively unwrap each element
204
- if (Array.isArray(params)) {
205
- return params.map(item => this.unwrapParameters(item));
206
- }
207
-
208
- // Check if this is a wrapped value: {value: "...", attributes: {}}
209
- if ('value' in params && 'attributes' in params && Object.keys(params).length === 2) {
210
- // This is a wrapped value, return just the value (recursively unwrapped)
211
- return this.unwrapParameters(params.value);
212
- }
213
-
214
- // Regular object - unwrap each property recursively
215
- const unwrapped = {};
216
- for (const [key, value] of Object.entries(params)) {
217
- if (value && typeof value === 'object') {
218
- if ('value' in value && 'attributes' in value && Object.keys(value).length === 2) {
219
- // TagParser wrapped format: {value: "...", attributes: {}}
220
- unwrapped[key] = this.unwrapParameters(value.value);
221
-
222
- // Also preserve attributes if tool needs them
223
- if (value.attributes && Object.keys(value.attributes).length > 0) {
224
- unwrapped[`${key}_attributes`] = value.attributes;
225
- }
226
- } else {
227
- // Recursively unwrap nested objects/arrays
228
- unwrapped[key] = this.unwrapParameters(value);
229
- }
230
- } else {
231
- // Primitive value - keep as-is
232
- unwrapped[key] = value;
233
- }
234
- }
235
-
236
- return unwrapped;
237
- }
238
-
239
- /**
240
- * Extract tool commands from message content
241
- * Supports multiple formats: XML, JSON, and simple bracket notation
242
- * @param {string} message - Message containing tool commands
243
- * @returns {Promise<Array>} Array of tool commands
244
- */
245
- async extractToolCommands(message) {
246
- const commands = [];
247
-
248
- // Use TagParser to extract XML and JSON format commands
249
- const tagParserCommands = this.tagParser.extractToolCommands(message);
250
-
251
- // Process TagParser commands and normalize them
252
- for (const cmd of tagParserCommands) {
253
- const normalized = this.tagParser.normalizeToolCommand(cmd);
254
- commands.push({
255
- toolId: normalized.toolId,
256
- content: JSON.stringify(normalized.parameters), // Convert parameters to JSON string for tool execution
257
- parameters: normalized.parameters,
258
- type: normalized.type,
259
- isAsync: normalized.parameters?.async === true,
260
- raw: normalized.rawContent,
261
- position: cmd.position || {}
262
- });
263
- }
264
-
265
- // Also check for simple bracket notation [tool id="..."] for backward compatibility
266
- const toolPattern = /\[tool\s+id="([^"]+)"(?:\s+async="(true|false)")?\]([\s\S]*?)\[\/tool\]/gi;
267
-
268
- console.log('MessageProcessor DEBUG: checking bracket pattern on message length:', message.length);
269
-
270
- let match;
271
- while ((match = toolPattern.exec(message)) !== null) {
272
- const [fullMatch, toolId, isAsync, content] = match;
273
-
274
- console.log('MessageProcessor DEBUG: bracket pattern matched:', {
275
- toolId: toolId.trim(),
276
- contentLength: content.trim().length,
277
- contentPreview: content.trim().substring(0, 100)
278
- });
279
-
280
- // Check if this command was already extracted by TagParser
281
- const alreadyExtracted = commands.some(cmd =>
282
- cmd.raw === fullMatch || (cmd.position.start === match.index && cmd.position.end === match.index + fullMatch.length)
283
- );
284
-
285
- if (!alreadyExtracted) {
286
- console.log('MessageProcessor DEBUG: adding bracket command (not already extracted by TagParser)');
287
-
288
- const trimmedContent = content.trim();
289
-
290
- // Check if the content inside brackets contains XML tags
291
- const hasXmlTags = /<[^>]+>/g.test(trimmedContent);
292
-
293
- if (hasXmlTags) {
294
- console.log('MessageProcessor DEBUG: detected XML content inside brackets, parsing with TagParser');
295
-
296
- // Decode HTML entities before parsing XML
297
- const decodedXmlContent = this.tagParser.decodeHtmlEntities(trimmedContent);
298
- console.log('MessageProcessor DEBUG: HTML decoding changed content:', trimmedContent !== decodedXmlContent);
299
-
300
- // Parse the XML content using TagParser
301
- try {
302
- const xmlParameters = this.tagParser.parseXMLParameters(decodedXmlContent);
303
-
304
- console.log('MessageProcessor DEBUG: XML parameters extracted:', Object.keys(xmlParameters));
305
-
306
- // Check if we got valid parameters
307
- if (!xmlParameters || typeof xmlParameters !== 'object') {
308
- throw new Error('Invalid XML parameters returned');
309
- }
310
-
311
- // Create a temporary XML command structure for normalization
312
- const xmlCommand = {
313
- type: COMMAND_FORMATS.XML,
314
- toolId: toolId.trim(),
315
- parameters: xmlParameters,
316
- rawContent: decodedXmlContent
317
- };
318
-
319
- // Normalize it to get the actions array
320
- const normalized = this.tagParser.normalizeToolCommand(xmlCommand);
321
-
322
- console.log('MessageProcessor DEBUG: normalized XML command:', {
323
- toolId: normalized.toolId,
324
- hasActions: !!normalized.parameters.actions,
325
- actionsLength: normalized.parameters.actions?.length || 0
326
- });
327
-
328
- // Add the properly parsed command
329
- commands.push({
330
- toolId: normalized.toolId,
331
- content: JSON.stringify(normalized.parameters),
332
- parameters: normalized.parameters,
333
- type: COMMAND_FORMATS.XML, // Mark as XML since we parsed it
334
- isAsync: isAsync === 'true',
335
- raw: fullMatch,
336
- position: {
337
- start: match.index,
338
- end: match.index + fullMatch.length
339
- }
340
- });
341
-
342
- } catch (error) {
343
- console.log('MessageProcessor DEBUG: XML parsing failed:', error.message);
344
- console.log('MessageProcessor DEBUG: falling back to raw bracket format');
345
-
346
- // Fall back to treating it as a simple bracket command
347
- commands.push({
348
- toolId: toolId.trim(),
349
- content: trimmedContent,
350
- type: COMMAND_FORMATS.BRACKET,
351
- isAsync: isAsync === 'true',
352
- raw: fullMatch,
353
- position: {
354
- start: match.index,
355
- end: match.index + fullMatch.length
356
- }
357
- });
358
- }
359
- } else {
360
- console.log('MessageProcessor DEBUG: no XML detected, treating as simple bracket command');
361
-
362
- // Simple bracket command without XML content
363
- commands.push({
364
- toolId: toolId.trim(),
365
- content: trimmedContent,
366
- type: COMMAND_FORMATS.BRACKET,
367
- isAsync: isAsync === 'true',
368
- raw: fullMatch,
369
- position: {
370
- start: match.index,
371
- end: match.index + fullMatch.length
372
- }
373
- });
374
- }
375
- } else {
376
- console.log('MessageProcessor DEBUG: bracket command already extracted by TagParser, skipping');
377
- }
378
- }
379
-
380
- // Extract agent redirects as well (for inter-agent communication)
381
- const redirects = this.tagParser.extractAgentRedirects(message);
382
- for (const redirect of redirects) {
383
- commands.push({
384
- toolId: TOOL_IDS.AGENT_COMMUNICATION,
385
- content: JSON.stringify({
386
- to: redirect.to,
387
- message: redirect.content,
388
- urgent: redirect.urgent,
389
- requiresResponse: redirect.requiresResponse,
390
- context: redirect.context
391
- }),
392
- type: COMMAND_FORMATS.REDIRECT,
393
- isAsync: false,
394
- raw: redirect.rawMatch,
395
- position: {}
396
- });
397
- }
398
-
399
- this.logger.debug(`Extracted ${commands.length} tool commands from message`, {
400
- formats: commands.map(c => c.type),
401
- tools: commands.map(c => c.toolId)
402
- });
403
-
404
- return commands;
405
- }
406
-
407
- /**
408
- * Execute tool commands
409
- * @param {Array} commands - Array of tool commands
410
- * @param {Object} context - Execution context
411
- * @returns {Promise<Array>} Array of execution results
412
- */
413
- async executeTools(commands, context) {
414
- const results = [];
415
-
416
- for (const command of commands) {
417
- try {
418
- const tool = this.toolsRegistry.getTool(command.toolId);
419
-
420
- if (!tool) {
421
- results.push({
422
- toolId: command.toolId,
423
- status: 'failed',
424
- error: `Tool not found: ${command.toolId}`,
425
- timestamp: new Date().toISOString()
426
- });
427
- continue;
428
- }
429
-
430
- this.logger.info(`Executing tool: ${command.toolId}`, {
431
- agentId: context.agentId,
432
- isAsync: command.isAsync
433
- });
434
-
435
- let result;
436
- let toolInput = command.parameters; // Hoisted for artifact tracking access
437
- if (command.isAsync) {
438
- result = await this.executeAsyncTool(command, tool, context);
439
- } else {
440
- // Synchronous tool execution
441
- // If we have parameters object, use it. Otherwise parse the content.
442
-
443
- if (!toolInput && command.content) {
444
- // Content is a string, need to parse it using tool's parseParameters method
445
- if (typeof tool.parseParameters === 'function') {
446
- try {
447
- toolInput = tool.parseParameters(command.content);
448
- this.logger?.debug(`Parsed parameters for tool: ${command.toolId}`, {
449
- parsedKeys: Object.keys(toolInput)
450
- });
451
- } catch (error) {
452
- this.logger?.warn(`Failed to parse parameters for tool: ${command.toolId}`, {
453
- error: error.message
454
- });
455
- // Fall back to raw content
456
- toolInput = command.content;
457
- }
458
- } else {
459
- // Tool doesn't have parseParameters, use raw content
460
- toolInput = command.content;
461
- }
462
- }
463
-
464
- // CRITICAL FIX: Unwrap TagParser format before tool execution
465
- // TagParser wraps XML parameters in {value, attributes} objects
466
- // This unwrapping makes all tools work consistently
467
- if (toolInput && typeof toolInput === 'object') {
468
- toolInput = this.unwrapParameters(toolInput);
469
- }
470
-
471
- // Pass truncation info to tool for partial execution handling.
472
- // Re-derive projectDir from directoryAccess in case a previous tool
473
- // (e.g. terminal change-directory) updated the working directory.
474
- //
475
- // toolConfig is this tool's slice of the agent's per-tool config
476
- // (from agent.toolConfig[toolId]) null when the agent didn't
477
- // override anything for this tool, in which case the tool uses
478
- // its global defaults. Tools that want to support per-agent
479
- // config read `context.toolConfig` and merge it with their own
480
- // defaults; tools that ignore it get the same behaviour as
481
- // before.
482
- const perToolConfig = (context.agentToolConfig && command.toolId)
483
- ? context.agentToolConfig[command.toolId] || null
484
- : null;
485
-
486
- const toolContext = {
487
- ...context,
488
- projectDir: context.directoryAccess?.workingDirectory || context.projectDir,
489
- toolConfig: perToolConfig,
490
- wasRepaired: command.wasRepaired || false,
491
- wasTruncated: command.wasTruncated || false
492
- };
493
-
494
- const toolResult = await tool.execute(toolInput, toolContext);
495
-
496
- // Mark result as partial if input was truncated
497
- const isPartial = command.wasTruncated || false;
498
-
499
- result = {
500
- toolId: command.toolId,
501
- status: isPartial ? 'partial' : 'completed',
502
- result: toolResult,
503
- timestamp: new Date().toISOString(),
504
- ...(isPartial && {
505
- warning: 'Tool executed with truncated input - AI response exceeded token limit',
506
- wasTruncated: true
507
- })
508
- };
509
- }
510
-
511
- results.push(result);
512
-
513
- // ── Artifact tracking (fire-and-forget) ─────────────────────
514
- // After successful filesystem writes, persist artifact metadata
515
- // on the agent object so the UI can display version history.
516
- // Non-blocking: uses .catch() to avoid disrupting the tool pipeline.
517
- if (result.status === 'completed' && command.toolId === TOOL_IDS.FILESYSTEM) {
518
- this._trackArtifacts({ ...command, parameters: toolInput }, result, context)
519
- .catch(e => this.logger?.warn?.('[Artifacts] tracking failed:', e.message));
520
- }
521
-
522
- // Store in execution history
523
- const historyKey = `${context.agentId}-${Date.now()}`;
524
- this.executionHistory.set(historyKey, {
525
- ...result,
526
- agentId: context.agentId,
527
- sessionId: context.sessionId
528
- });
529
-
530
- } catch (error) {
531
- this.logger.error(`Tool execution failed: ${command.toolId}`, {
532
- error: error.message,
533
- agentId: context.agentId
534
- });
535
-
536
- results.push({
537
- toolId: command.toolId,
538
- status: 'failed',
539
- error: error.message,
540
- timestamp: new Date().toISOString()
541
- });
542
- }
543
- }
544
-
545
- return results;
546
- }
547
-
548
- /**
549
- * Track filesystem write/append operations as artifacts on the agent.
550
- * Persists lightweight metadata (path, size, timestamp) and the content
551
- * so the UI can show version history without re-parsing message content.
552
- *
553
- * The agent.artifacts map is persisted via the normal persistAgentState flow
554
- * that already runs after tool execution in AgentScheduler.
555
- *
556
- * @param {Object} command - Tool command with parameters
557
- * @param {Object} result - Execution result
558
- * @param {Object} context - Execution context (agentId, projectDir)
559
- * @private
560
- */
561
- async _trackArtifacts(command, result, context) {
562
- try {
563
- const agent = await this.agentPool?.getAgent?.(context.agentId);
564
- if (!agent) {
565
- console.log('[Artifacts] No agent found for', context.agentId);
566
- return;
567
- }
568
-
569
- // Initialize artifacts map if needed: { [filePath]: { versions: [...] } }
570
- if (!agent.artifacts) agent.artifacts = {};
571
-
572
- const toolResult = result.result;
573
- if (!toolResult?.success) {
574
- console.log('[Artifacts] Tool result not successful:', { success: toolResult?.success, keys: Object.keys(toolResult || {}) });
575
- return;
576
- }
577
-
578
- // Get the actions from the command parameters
579
- // The AI may send actions at different levels depending on format:
580
- // { parameters: { actions: [...] } } parsed format
581
- // { actions: [...] } top-level format (common)
582
- // { parameters: { type: 'write', ... } } — single action
583
- const params = command.parameters || {};
584
- const actions = params.actions || command.actions || (params.type ? [params] : []);
585
- console.log('[Artifacts] Processing', actions.length, 'actions. Param keys:', Object.keys(params), 'cmd keys:', Object.keys(command));
586
- const resultActions = toolResult.actions || [toolResult];
587
-
588
- for (let i = 0; i < actions.length; i++) {
589
- const action = actions[i];
590
- const actionResult = resultActions[i] || {};
591
- const type = action.type || action.action;
592
-
593
- console.log(`[Artifacts] Action[${i}]:`, { type, filePath: action.filePath || action['file-path'], hasContent: !!action.content, contentLen: action.content?.length, actionKeys: Object.keys(action) });
594
-
595
- if ((type === 'write' || type === 'append') && actionResult.success !== false) {
596
- // AI uses various field names: filePath, file-path, outputPath, path
597
- const rawPath = action.filePath || action['file-path'] || action.outputPath || action.path;
598
- const content = action.content;
599
- if (!rawPath || !content) {
600
- console.log('[Artifacts] Skipped: missing filePath or content', { filePath: !!rawPath, content: !!content, actionKeys: Object.keys(action) });
601
- continue;
602
- }
603
-
604
- // Use the resolved absolute path as key (prevents collisions for same-name files in different dirs)
605
- // Fall back to raw path if fullPath not available
606
- const absolutePath = actionResult.fullPath || rawPath;
607
- const artifactKey = absolutePath.replace(/\\/g, '/');
608
-
609
- // Relative display path (strip working directory prefix)
610
- const wd = (context.projectDir || '').replace(/\\/g, '/');
611
- const displayPath = wd && artifactKey.startsWith(wd + '/')
612
- ? artifactKey.slice(wd.length + 1)
613
- : artifactKey;
614
-
615
- const version = {
616
- id: `v-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
617
- content,
618
- timestamp: result.timestamp || new Date().toISOString(),
619
- action: type,
620
- size: Buffer.byteLength(content, 'utf8'),
621
- fullPath: absolutePath,
622
- };
623
-
624
- if (!agent.artifacts[artifactKey]) {
625
- agent.artifacts[artifactKey] = { displayPath, versions: [] };
626
- }
627
-
628
- // Deduplicate: skip if content identical to latest
629
- const versions = agent.artifacts[artifactKey].versions;
630
- const latest = versions[versions.length - 1];
631
- if (latest && latest.content === content) continue;
632
-
633
- versions.push(version);
634
- console.log('[Artifacts] Tracked:', displayPath, 'v' + versions.length, '(' + version.size + ' bytes)');
635
-
636
- // Cap at 50 versions per file to keep state reasonable
637
- if (versions.length > 50) {
638
- versions.splice(0, versions.length - 50);
639
- }
640
- }
641
- }
642
- } catch (e) {
643
- this.logger?.warn?.('Artifact tracking failed (non-fatal):', e.message);
644
- }
645
- }
646
-
647
- /**
648
- * Execute async tool
649
- * @param {Object} command - Tool command
650
- * @param {Object} tool - Tool instance
651
- * @param {Object} context - Execution context
652
- * @returns {Promise<Object>} Async operation reference
653
- */
654
- async executeAsyncTool(command, tool, context) {
655
- const operationId = `async-${Date.now()}-${this.operationCounter++}`;
656
-
657
- // Create async operation entry
658
- const operation = {
659
- id: operationId,
660
- toolId: command.toolId,
661
- agentId: context.agentId,
662
- status: 'pending',
663
- startTime: new Date().toISOString(),
664
- context: context
665
- };
666
-
667
- this.asyncOperations.set(operationId, operation);
668
-
669
- // Start async execution
670
- // If we have parameters object, use it. Otherwise parse the content.
671
- let toolInput = command.parameters;
672
-
673
- if (!toolInput && command.content) {
674
- // Content is a string, need to parse it using tool's parseParameters method
675
- if (typeof tool.parseParameters === 'function') {
676
- try {
677
- toolInput = tool.parseParameters(command.content);
678
- } catch (error) {
679
- this.logger?.warn(`Failed to parse parameters for async tool: ${command.toolId}`, {
680
- error: error.message
681
- });
682
- // Fall back to raw content
683
- toolInput = command.content;
684
- }
685
- } else {
686
- // Tool doesn't have parseParameters, use raw content
687
- toolInput = command.content;
688
- }
689
- }
690
-
691
- // CRITICAL FIX: Unwrap TagParser format before tool execution
692
- // TagParser wraps XML parameters in {value, attributes} objects
693
- // This unwrapping makes all tools work consistently
694
- if (toolInput && typeof toolInput === 'object') {
695
- toolInput = this.unwrapParameters(toolInput);
696
- }
697
-
698
- tool.execute(toolInput, context)
699
- .then(result => {
700
- operation.status = 'completed';
701
- operation.result = result;
702
- operation.endTime = new Date().toISOString();
703
- this.notifyAgentOfToolCompletion(operation);
704
- })
705
- .catch(error => {
706
- operation.status = 'failed';
707
- operation.error = error.message;
708
- operation.endTime = new Date().toISOString();
709
- this.notifyAgentOfToolCompletion(operation);
710
- });
711
-
712
- // Start monitoring
713
- this.monitorAsyncOperation(operationId);
714
-
715
- return {
716
- toolId: command.toolId,
717
- status: 'async-pending',
718
- operationId: operationId,
719
- message: `Async tool started with operation ID: ${operationId}`,
720
- timestamp: new Date().toISOString()
721
- };
722
- }
723
-
724
- /**
725
- * Monitor async operation
726
- * @param {string} operationId - Operation ID to monitor
727
- */
728
- async monitorAsyncOperation(operationId) {
729
- const checkInterval = 5000; // 5 seconds
730
- const maxChecks = 120; // 10 minutes max
731
- let checks = 0;
732
-
733
- const monitor = setInterval(() => {
734
- const operation = this.asyncOperations.get(operationId);
735
-
736
- if (!operation) {
737
- clearInterval(monitor);
738
- return;
739
- }
740
-
741
- checks++;
742
-
743
- if (operation.status !== 'pending' || checks >= maxChecks) {
744
- clearInterval(monitor);
745
-
746
- if (checks >= maxChecks) {
747
- operation.status = 'timeout';
748
- operation.error = 'Operation timed out';
749
- operation.endTime = new Date().toISOString();
750
- this.notifyAgentOfToolCompletion(operation);
751
- }
752
- }
753
- }, checkInterval);
754
- }
755
-
756
- /**
757
- * Notify agent of tool completion
758
- * @param {Object} operation - Completed operation
759
- */
760
- async notifyAgentOfToolCompletion(operation) {
761
- if (!operation.agentId) return;
762
-
763
- try {
764
- // Queue tool result for the agent
765
- await this.agentPool.addToolResult(operation.agentId, {
766
- toolId: operation.toolId,
767
- status: operation.status,
768
- result: operation.result,
769
- error: operation.error,
770
- executionTime: operation.endTime ?
771
- new Date(operation.endTime) - new Date(operation.startTime) : null,
772
- timestamp: operation.endTime || new Date().toISOString()
773
- });
774
-
775
- // Ensure session is registered for agent (tool results added to queue
776
- // will cause agent to become active via AgentActivityService)
777
- if (this.scheduler) {
778
- await this.scheduler.addAgent(operation.agentId, {
779
- triggeredBy: 'tool-completion',
780
- sessionId: operation.context?.sessionId
781
- });
782
- }
783
-
784
- this.logger.info(`Agent notified of tool completion: ${operation.agentId}`, {
785
- toolId: operation.toolId,
786
- status: operation.status
787
- });
788
-
789
- } catch (error) {
790
- this.logger.error(`Failed to notify agent of tool completion`, {
791
- agentId: operation.agentId,
792
- toolId: operation.toolId,
793
- error: error.message
794
- });
795
- }
796
- }
797
-
798
- /**
799
- * Get tool status
800
- * @param {string} operationId - Operation ID
801
- * @returns {Promise<Object>} Operation status
802
- */
803
- async getToolStatus(operationId) {
804
- const operation = this.asyncOperations.get(operationId);
805
-
806
- if (!operation) {
807
- return {
808
- status: 'not-found',
809
- error: `Operation not found: ${operationId}`
810
- };
811
- }
812
-
813
- return {
814
- id: operation.id,
815
- toolId: operation.toolId,
816
- status: operation.status,
817
- result: operation.result,
818
- error: operation.error,
819
- startTime: operation.startTime,
820
- endTime: operation.endTime
821
- };
822
- }
823
-
824
- /**
825
- * Extract and execute tools from content
826
- * Called by AgentScheduler after getting AI response
827
- * @param {string} content - Content containing tool commands
828
- * @param {string} agentId - Agent ID
829
- * @param {Object} context - Execution context
830
- * @returns {Promise<Array>} Tool execution results
831
- */
832
- async extractAndExecuteTools(content, agentId, context) {
833
- try {
834
- // Extract tool commands
835
- const commands = await this.extractToolCommands(content);
836
-
837
- if (commands.length === 0) {
838
- return [];
839
- }
840
-
841
- // Get agent to include its directoryAccess configuration
842
- const agent = await this.agentPool.getAgent(agentId);
843
-
844
- // Execute tools with agent context including sessionId and directoryAccess
845
- const toolContext = {
846
- ...context,
847
- agentId,
848
- sessionId: context.sessionId, // Ensure sessionId is explicitly available for tools
849
- directoryAccess: agent?.directoryAccess, // Include agent's directory access configuration
850
- projectDir: agent?.directoryAccess?.workingDirectory || agent?.projectDir || context.projectDir, // Extract project directory from directoryAccess
851
- // Per-agent tool configuration keyed by tool id. Tools can read
852
- // their own slice via `context.toolConfig` (injected per-tool in
853
- // executeTools below) or inspect the full map here if they need
854
- // cross-tool state. See agentPool.js for the schema contract.
855
- agentToolConfig: (agent && agent.toolConfig) ? agent.toolConfig : {},
856
- agentPool: this.agentPool,
857
- contextManager: this.contextManager,
858
- aiService: this.aiService,
859
- messageProcessor: this,
860
- orchestrator: this.orchestrator
861
- };
862
-
863
- const results = await this.executeTools(commands, toolContext);
864
-
865
- this.logger.info(`Executed ${results.length} tools for agent: ${agentId}`, {
866
- tools: results.map(r => ({ toolId: r.toolId, status: r.status }))
867
- });
868
-
869
- return results;
870
-
871
- } catch (error) {
872
- this.logger.error(`Tool extraction/execution failed for agent: ${agentId}`, {
873
- error: error.message
874
- });
875
- return [];
876
- }
877
- }
878
-
879
- /**
880
- * Stop autonomous execution for an agent
881
- * Proxy method to AgentScheduler
882
- * @param {string} agentId - Agent ID to stop
883
- * @returns {Promise<Object>} Result with agent state
884
- */
885
- async stopAutonomousExecution(agentId) {
886
- if (!this.scheduler) {
887
- return {
888
- success: false,
889
- error: 'Scheduler not available'
890
- };
891
- }
892
-
893
- return await this.scheduler.stopAgentExecution(agentId);
894
- }
895
-
896
- /**
897
- * Inject tool results into conversation
898
- * @param {string} agentId - Agent ID
899
- * @param {Array} toolResults - Tool execution results
900
- * @returns {Promise<void>}
901
- */
902
- async injectToolResultsIntoConversation(agentId, toolResults) {
903
- const agent = await this.agentPool.getAgent(agentId);
904
- if (!agent) return;
905
-
906
- for (const result of toolResults) {
907
- const toolMessage = {
908
- id: `tool-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
909
- role: 'system',
910
- content: this.formatToolResultForAgent(result),
911
- timestamp: new Date().toISOString(),
912
- type: 'tool-result',
913
- toolId: result.toolId,
914
- status: result.status
915
- };
916
-
917
- // Add to conversation history
918
- agent.conversations.full.messages.push(toolMessage);
919
-
920
- // Also add to current model conversation if exists
921
- if (agent.currentModel && agent.conversations[agent.currentModel]) {
922
- agent.conversations[agent.currentModel].messages.push(toolMessage);
923
- }
924
- }
925
-
926
- // Update last activity
927
- agent.conversations.full.lastUpdated = new Date().toISOString();
928
- if (agent.currentModel && agent.conversations[agent.currentModel]) {
929
- agent.conversations[agent.currentModel].lastUpdated = new Date().toISOString();
930
- }
931
-
932
- await this.agentPool.persistAgentState(agentId);
933
- }
934
-
935
- /**
936
- * Format tool result for agent consumption
937
- * @param {Object} result - Tool execution result
938
- * @returns {string} Formatted result
939
- */
940
- formatToolResultForAgent(result) {
941
- if (result.status === 'completed') {
942
- if (typeof result.result === 'object') {
943
- return `Tool ${result.toolId} completed successfully:\n${JSON.stringify(result.result, null, 2)}`;
944
- }
945
- return `Tool ${result.toolId} completed successfully:\n${result.result}`;
946
- } else if (result.status === 'failed') {
947
- return `Tool ${result.toolId} failed: ${result.error || 'Unknown error'}`;
948
- } else if (result.status === 'async-pending') {
949
- return `Tool ${result.toolId} is running asynchronously (Operation ID: ${result.operationId})`;
950
- }
951
- return `Tool ${result.toolId} status: ${result.status}`;
952
- }
953
- }
954
-
1
+ /**
2
+ * MessageProcessor - Processes messages from agents, extracts tool commands, executes tools
3
+ *
4
+ * NEW ARCHITECTURE:
5
+ * - Only handles message queuing and tool execution
6
+ * - No scheduling or autonomous loops (handled by AgentScheduler)
7
+ * - Clean separation of concerns
8
+ */
9
+
10
+ import TagParser from '../utilities/tagParser.js';
11
+ import { TOOL_IDS, COMMAND_FORMATS } from '../utilities/toolConstants.js';
12
+ import { getVisualEditorBridge } from '../services/visualEditorBridge.js';
13
+ import { VisualEditorTool } from '../tools/visualEditorTool.js';
14
+ import { prependSourceHeader } from '../services/messageSource.js';
15
+
16
+ class MessageProcessor {
17
+ constructor(config, logger, toolsRegistry, agentPool, contextManager, aiService, modelRouterService = null, modelsService = null) {
18
+ this.config = config;
19
+ this.logger = logger;
20
+ this.toolsRegistry = toolsRegistry;
21
+ this.agentPool = agentPool;
22
+ this.contextManager = contextManager;
23
+ this.aiService = aiService;
24
+ this.modelRouterService = modelRouterService;
25
+ this.modelsService = modelsService;
26
+
27
+ // Active async operations tracking
28
+ this.asyncOperations = new Map();
29
+
30
+ // Tool execution history
31
+ this.executionHistory = new Map();
32
+
33
+ // Operation ID counter
34
+ this.operationCounter = 0;
35
+
36
+ // WebSocket manager for real-time updates
37
+ this.webSocketManager = null;
38
+
39
+ // AgentScheduler reference
40
+ this.scheduler = null;
41
+
42
+ // Orchestrator reference (for backward compatibility)
43
+ this.orchestrator = null;
44
+
45
+ // Initialize TagParser for comprehensive tool command extraction
46
+ this.tagParser = new TagParser();
47
+ }
48
+
49
+ /**
50
+ * Set WebSocket manager for real-time UI updates
51
+ * @param {Object} webSocketManager - WebSocket manager instance
52
+ */
53
+ setWebSocketManager(webSocketManager) {
54
+ this.webSocketManager = webSocketManager;
55
+ this.logger?.info('WebSocket manager set for MessageProcessor', {
56
+ hasManager: !!webSocketManager
57
+ });
58
+ }
59
+
60
+ /**
61
+ * Set AgentScheduler reference
62
+ * @param {AgentScheduler} scheduler - AgentScheduler instance
63
+ */
64
+ setScheduler(scheduler) {
65
+ this.scheduler = scheduler;
66
+ this.logger?.info('AgentScheduler set for MessageProcessor', {
67
+ hasScheduler: !!scheduler
68
+ });
69
+ }
70
+
71
+ /**
72
+ * Main message processing entry point - NEW ARCHITECTURE
73
+ * Simply queues messages for scheduler processing
74
+ * @param {string} agentId - Target agent ID
75
+ * @param {string} message - Message content
76
+ * @param {Object} context - Message context
77
+ * @returns {Promise<Object>} Queuing result
78
+ */
79
+ async processMessage(agentId, message, context = {}) {
80
+ const agent = await this.agentPool.getAgent(agentId);
81
+ if (!agent) {
82
+ throw new Error(`Agent not found: ${agentId}`);
83
+ }
84
+
85
+ const messageString = typeof message === 'string' ? message : (message ? JSON.stringify(message) : '');
86
+ this.logger.info(`Queueing message for agent: ${agentId}`, {
87
+ messageLength: messageString.length,
88
+ messageType: typeof message,
89
+ isInterAgentMessage: context.isInterAgentMessage,
90
+ contextMessageType: context.messageType || 'user'
91
+ });
92
+
93
+ // Determine message type and queue appropriately
94
+ if (context.isInterAgentMessage) {
95
+ // Inter-agent message
96
+ await this.agentPool.addInterAgentMessage(agentId, {
97
+ content: message,
98
+ sender: context.originalSender,
99
+ senderName: context.senderName,
100
+ subject: context.subject || 'Inter-agent message',
101
+ timestamp: new Date().toISOString(),
102
+ sessionId: context.sessionId,
103
+ requiresReply: context.requiresReply || false
104
+ });
105
+ } else {
106
+ // User message
107
+ // Phase 4: Inject visual context if available
108
+ let enhancedMessage = message;
109
+ let visualContextInjected = false;
110
+
111
+ try {
112
+ const bridge = getVisualEditorBridge();
113
+ if (bridge.isEnabled() && bridge.hasInstance(agentId)) {
114
+ const visualContext = bridge.getVisualContext(agentId);
115
+ if (visualContext) {
116
+ enhancedMessage = VisualEditorTool.injectContextIntoMessage(message, visualContext);
117
+ visualContextInjected = true;
118
+
119
+ this.logger.info(`Visual context injected for agent: ${agentId}`, {
120
+ selector: visualContext.selector,
121
+ sourceFile: visualContext.sourceHint?.file
122
+ });
123
+
124
+ // Clear context after injection (configurable)
125
+ if (this.config.visualEditor?.clearContextAfterInjection !== false) {
126
+ bridge.clearVisualContext(agentId);
127
+ }
128
+ }
129
+ }
130
+ } catch (err) {
131
+ // Visual context injection is optional - don't fail the message
132
+ this.logger.warn?.(`Failed to inject visual context: ${err.message}`);
133
+ }
134
+
135
+ // Prepend the source attribution header when the message arrived from
136
+ // an external channel (Discord / Telegram). The header is plain text
137
+ // of the form "(Message by alice from Discord > MyGuild > #ops)" and
138
+ // becomes part of the persisted content, so the agent sees it in its
139
+ // LLM context and the operator sees it in the web-UI transcript. The
140
+ // operation is idempotent re-serialization (state restore, replay)
141
+ // won't double-prefix. Sources of kind web/api/internal produce no
142
+ // header and leave the content untouched. See services/messageSource.js.
143
+ if (context.source) {
144
+ enhancedMessage = prependSourceHeader(enhancedMessage, context.source);
145
+ }
146
+
147
+ await this.agentPool.addUserMessage(agentId, {
148
+ content: enhancedMessage,
149
+ role: 'user',
150
+ timestamp: new Date().toISOString(),
151
+ contextReferences: context.contextReferences || [],
152
+ sessionId: context.sessionId,
153
+ visualContextInjected,
154
+ streamingEnabled: context.streamingEnabled !== false, // Pass streaming preference from context
155
+ // Preserve the structured source on the queued message too, so any
156
+ // downstream consumer (logs, analytics, relay) can reason about
157
+ // provenance without re-parsing the inline header.
158
+ source: context.source || null,
159
+ // Flow execution context (if this message is part of a flow)
160
+ isFlowExecution: context.isFlowExecution || false,
161
+ flowRunId: context.flowRunId,
162
+ flowNodeId: context.flowNodeId,
163
+ flowMetadata: context.flowMetadata,
164
+ previousAgentData: context.previousAgentData,
165
+ // v2: typed I/O contract for this node — { inputs, outputs }.
166
+ // Forwarded to the scheduler so the system prompt can advertise
167
+ // the exact named/typed payload the agent must produce.
168
+ nodeContract: context.nodeContract
169
+ });
170
+ }
171
+
172
+ // Register session with scheduler for API key resolution
173
+ // NOTE: The scheduler uses AgentActivityService to determine which agents
174
+ // should be active based on their message queues - we just register the session here
175
+ if (this.scheduler) {
176
+ await this.scheduler.addAgent(agentId, {
177
+ triggeredBy: context.isInterAgentMessage ? 'inter-agent-message' : 'user-message',
178
+ sessionId: context.sessionId
179
+ });
180
+ }
181
+
182
+ return {
183
+ success: true,
184
+ message: 'Message queued for processing',
185
+ agentId: agentId,
186
+ queuedAt: new Date().toISOString()
187
+ };
188
+ }
189
+
190
+ /**
191
+ * Unwrap TagParser format parameters
192
+ * TagParser wraps XML parameters in {value, attributes} objects
193
+ * This method unwraps them to direct values for tool consumption
194
+ * @param {Object} params - Parameters to unwrap
195
+ * @returns {Object} Unwrapped parameters
196
+ */
197
+ unwrapParameters(params) {
198
+ if (!params || typeof params !== 'object') {
199
+ return params;
200
+ }
201
+
202
+ // Handle arrays - recursively unwrap each element
203
+ if (Array.isArray(params)) {
204
+ return params.map(item => this.unwrapParameters(item));
205
+ }
206
+
207
+ // Check if this is a wrapped value: {value: "...", attributes: {}}
208
+ if ('value' in params && 'attributes' in params && Object.keys(params).length === 2) {
209
+ // This is a wrapped value, return just the value (recursively unwrapped)
210
+ return this.unwrapParameters(params.value);
211
+ }
212
+
213
+ // Regular object - unwrap each property recursively
214
+ const unwrapped = {};
215
+ for (const [key, value] of Object.entries(params)) {
216
+ if (value && typeof value === 'object') {
217
+ if ('value' in value && 'attributes' in value && Object.keys(value).length === 2) {
218
+ // TagParser wrapped format: {value: "...", attributes: {}}
219
+ unwrapped[key] = this.unwrapParameters(value.value);
220
+
221
+ // Also preserve attributes if tool needs them
222
+ if (value.attributes && Object.keys(value.attributes).length > 0) {
223
+ unwrapped[`${key}_attributes`] = value.attributes;
224
+ }
225
+ } else {
226
+ // Recursively unwrap nested objects/arrays
227
+ unwrapped[key] = this.unwrapParameters(value);
228
+ }
229
+ } else {
230
+ // Primitive value - keep as-is
231
+ unwrapped[key] = value;
232
+ }
233
+ }
234
+
235
+ return unwrapped;
236
+ }
237
+
238
+ /**
239
+ * Extract tool commands from message content
240
+ * Supports multiple formats: XML, JSON, and simple bracket notation
241
+ * @param {string} message - Message containing tool commands
242
+ * @returns {Promise<Array>} Array of tool commands
243
+ */
244
+ async extractToolCommands(message) {
245
+ const commands = [];
246
+
247
+ // Use TagParser to extract XML and JSON format commands
248
+ const tagParserCommands = this.tagParser.extractToolCommands(message);
249
+
250
+ // Process TagParser commands and normalize them
251
+ for (const cmd of tagParserCommands) {
252
+ const normalized = this.tagParser.normalizeToolCommand(cmd);
253
+ commands.push({
254
+ toolId: normalized.toolId,
255
+ content: JSON.stringify(normalized.parameters), // Convert parameters to JSON string for tool execution
256
+ parameters: normalized.parameters,
257
+ type: normalized.type,
258
+ isAsync: normalized.parameters?.async === true,
259
+ raw: normalized.rawContent,
260
+ position: cmd.position || {}
261
+ });
262
+ }
263
+
264
+ // Also check for simple bracket notation [tool id="..."] for backward compatibility
265
+ const toolPattern = /\[tool\s+id="([^"]+)"(?:\s+async="(true|false)")?\]([\s\S]*?)\[\/tool\]/gi;
266
+
267
+ console.log('MessageProcessor DEBUG: checking bracket pattern on message length:', message.length);
268
+
269
+ let match;
270
+ while ((match = toolPattern.exec(message)) !== null) {
271
+ const [fullMatch, toolId, isAsync, content] = match;
272
+
273
+ console.log('MessageProcessor DEBUG: bracket pattern matched:', {
274
+ toolId: toolId.trim(),
275
+ contentLength: content.trim().length,
276
+ contentPreview: content.trim().substring(0, 100)
277
+ });
278
+
279
+ // Check if this command was already extracted by TagParser
280
+ const alreadyExtracted = commands.some(cmd =>
281
+ cmd.raw === fullMatch || (cmd.position.start === match.index && cmd.position.end === match.index + fullMatch.length)
282
+ );
283
+
284
+ if (!alreadyExtracted) {
285
+ console.log('MessageProcessor DEBUG: adding bracket command (not already extracted by TagParser)');
286
+
287
+ const trimmedContent = content.trim();
288
+
289
+ // Check if the content inside brackets contains XML tags
290
+ const hasXmlTags = /<[^>]+>/g.test(trimmedContent);
291
+
292
+ if (hasXmlTags) {
293
+ console.log('MessageProcessor DEBUG: detected XML content inside brackets, parsing with TagParser');
294
+
295
+ // Decode HTML entities before parsing XML
296
+ const decodedXmlContent = this.tagParser.decodeHtmlEntities(trimmedContent);
297
+ console.log('MessageProcessor DEBUG: HTML decoding changed content:', trimmedContent !== decodedXmlContent);
298
+
299
+ // Parse the XML content using TagParser
300
+ try {
301
+ const xmlParameters = this.tagParser.parseXMLParameters(decodedXmlContent);
302
+
303
+ console.log('MessageProcessor DEBUG: XML parameters extracted:', Object.keys(xmlParameters));
304
+
305
+ // Check if we got valid parameters
306
+ if (!xmlParameters || typeof xmlParameters !== 'object') {
307
+ throw new Error('Invalid XML parameters returned');
308
+ }
309
+
310
+ // Create a temporary XML command structure for normalization
311
+ const xmlCommand = {
312
+ type: COMMAND_FORMATS.XML,
313
+ toolId: toolId.trim(),
314
+ parameters: xmlParameters,
315
+ rawContent: decodedXmlContent
316
+ };
317
+
318
+ // Normalize it to get the actions array
319
+ const normalized = this.tagParser.normalizeToolCommand(xmlCommand);
320
+
321
+ console.log('MessageProcessor DEBUG: normalized XML command:', {
322
+ toolId: normalized.toolId,
323
+ hasActions: !!normalized.parameters.actions,
324
+ actionsLength: normalized.parameters.actions?.length || 0
325
+ });
326
+
327
+ // Add the properly parsed command
328
+ commands.push({
329
+ toolId: normalized.toolId,
330
+ content: JSON.stringify(normalized.parameters),
331
+ parameters: normalized.parameters,
332
+ type: COMMAND_FORMATS.XML, // Mark as XML since we parsed it
333
+ isAsync: isAsync === 'true',
334
+ raw: fullMatch,
335
+ position: {
336
+ start: match.index,
337
+ end: match.index + fullMatch.length
338
+ }
339
+ });
340
+
341
+ } catch (error) {
342
+ console.log('MessageProcessor DEBUG: XML parsing failed:', error.message);
343
+ console.log('MessageProcessor DEBUG: falling back to raw bracket format');
344
+
345
+ // Fall back to treating it as a simple bracket command
346
+ commands.push({
347
+ toolId: toolId.trim(),
348
+ content: trimmedContent,
349
+ type: COMMAND_FORMATS.BRACKET,
350
+ isAsync: isAsync === 'true',
351
+ raw: fullMatch,
352
+ position: {
353
+ start: match.index,
354
+ end: match.index + fullMatch.length
355
+ }
356
+ });
357
+ }
358
+ } else {
359
+ console.log('MessageProcessor DEBUG: no XML detected, treating as simple bracket command');
360
+
361
+ // Simple bracket command without XML content
362
+ commands.push({
363
+ toolId: toolId.trim(),
364
+ content: trimmedContent,
365
+ type: COMMAND_FORMATS.BRACKET,
366
+ isAsync: isAsync === 'true',
367
+ raw: fullMatch,
368
+ position: {
369
+ start: match.index,
370
+ end: match.index + fullMatch.length
371
+ }
372
+ });
373
+ }
374
+ } else {
375
+ console.log('MessageProcessor DEBUG: bracket command already extracted by TagParser, skipping');
376
+ }
377
+ }
378
+
379
+ // Extract agent redirects as well (for inter-agent communication)
380
+ const redirects = this.tagParser.extractAgentRedirects(message);
381
+ for (const redirect of redirects) {
382
+ commands.push({
383
+ toolId: TOOL_IDS.AGENT_COMMUNICATION,
384
+ content: JSON.stringify({
385
+ to: redirect.to,
386
+ message: redirect.content,
387
+ urgent: redirect.urgent,
388
+ requiresResponse: redirect.requiresResponse,
389
+ context: redirect.context
390
+ }),
391
+ type: COMMAND_FORMATS.REDIRECT,
392
+ isAsync: false,
393
+ raw: redirect.rawMatch,
394
+ position: {}
395
+ });
396
+ }
397
+
398
+ this.logger.debug(`Extracted ${commands.length} tool commands from message`, {
399
+ formats: commands.map(c => c.type),
400
+ tools: commands.map(c => c.toolId)
401
+ });
402
+
403
+ return commands;
404
+ }
405
+
406
+ /**
407
+ * Execute tool commands
408
+ * @param {Array} commands - Array of tool commands
409
+ * @param {Object} context - Execution context
410
+ * @returns {Promise<Array>} Array of execution results
411
+ */
412
+ async executeTools(commands, context) {
413
+ const results = [];
414
+
415
+ for (const command of commands) {
416
+ try {
417
+ const tool = this.toolsRegistry.getTool(command.toolId);
418
+
419
+ if (!tool) {
420
+ results.push({
421
+ toolId: command.toolId,
422
+ status: 'failed',
423
+ error: `Tool not found: ${command.toolId}`,
424
+ timestamp: new Date().toISOString()
425
+ });
426
+ continue;
427
+ }
428
+
429
+ this.logger.info(`Executing tool: ${command.toolId}`, {
430
+ agentId: context.agentId,
431
+ isAsync: command.isAsync
432
+ });
433
+
434
+ let result;
435
+ let toolInput = command.parameters; // Hoisted for artifact tracking access
436
+ if (command.isAsync) {
437
+ result = await this.executeAsyncTool(command, tool, context);
438
+ } else {
439
+ // Synchronous tool execution
440
+ // If we have parameters object, use it. Otherwise parse the content.
441
+
442
+ if (!toolInput && command.content) {
443
+ // Content is a string, need to parse it using tool's parseParameters method
444
+ if (typeof tool.parseParameters === 'function') {
445
+ try {
446
+ toolInput = tool.parseParameters(command.content);
447
+ this.logger?.debug(`Parsed parameters for tool: ${command.toolId}`, {
448
+ parsedKeys: Object.keys(toolInput)
449
+ });
450
+ } catch (error) {
451
+ this.logger?.warn(`Failed to parse parameters for tool: ${command.toolId}`, {
452
+ error: error.message
453
+ });
454
+ // Fall back to raw content
455
+ toolInput = command.content;
456
+ }
457
+ } else {
458
+ // Tool doesn't have parseParameters, use raw content
459
+ toolInput = command.content;
460
+ }
461
+ }
462
+
463
+ // CRITICAL FIX: Unwrap TagParser format before tool execution
464
+ // TagParser wraps XML parameters in {value, attributes} objects
465
+ // This unwrapping makes all tools work consistently
466
+ if (toolInput && typeof toolInput === 'object') {
467
+ toolInput = this.unwrapParameters(toolInput);
468
+ }
469
+
470
+ // Pass truncation info to tool for partial execution handling.
471
+ // Re-derive projectDir from directoryAccess in case a previous tool
472
+ // (e.g. terminal change-directory) updated the working directory.
473
+ //
474
+ // toolConfig is this tool's slice of the agent's per-tool config
475
+ // (from agent.toolConfig[toolId]) null when the agent didn't
476
+ // override anything for this tool, in which case the tool uses
477
+ // its global defaults. Tools that want to support per-agent
478
+ // config read `context.toolConfig` and merge it with their own
479
+ // defaults; tools that ignore it get the same behaviour as
480
+ // before.
481
+ const perToolConfig = (context.agentToolConfig && command.toolId)
482
+ ? context.agentToolConfig[command.toolId] || null
483
+ : null;
484
+
485
+ const toolContext = {
486
+ ...context,
487
+ projectDir: context.directoryAccess?.workingDirectory || context.projectDir,
488
+ toolConfig: perToolConfig,
489
+ wasRepaired: command.wasRepaired || false,
490
+ wasTruncated: command.wasTruncated || false
491
+ };
492
+
493
+ const toolResult = await tool.execute(toolInput, toolContext);
494
+
495
+ // Mark result as partial if input was truncated
496
+ const isPartial = command.wasTruncated || false;
497
+
498
+ result = {
499
+ toolId: command.toolId,
500
+ status: isPartial ? 'partial' : 'completed',
501
+ result: toolResult,
502
+ timestamp: new Date().toISOString(),
503
+ ...(isPartial && {
504
+ warning: 'Tool executed with truncated input - AI response exceeded token limit',
505
+ wasTruncated: true
506
+ })
507
+ };
508
+ }
509
+
510
+ results.push(result);
511
+
512
+ // ── Artifact tracking (fire-and-forget) ─────────────────────
513
+ // After successful filesystem writes, persist artifact metadata
514
+ // on the agent object so the UI can display version history.
515
+ // Non-blocking: uses .catch() to avoid disrupting the tool pipeline.
516
+ if (result.status === 'completed' && command.toolId === TOOL_IDS.FILESYSTEM) {
517
+ this._trackArtifacts({ ...command, parameters: toolInput }, result, context)
518
+ .catch(e => this.logger?.warn?.('[Artifacts] tracking failed:', e.message));
519
+ }
520
+
521
+ // Store in execution history
522
+ const historyKey = `${context.agentId}-${Date.now()}`;
523
+ this.executionHistory.set(historyKey, {
524
+ ...result,
525
+ agentId: context.agentId,
526
+ sessionId: context.sessionId
527
+ });
528
+
529
+ } catch (error) {
530
+ this.logger.error(`Tool execution failed: ${command.toolId}`, {
531
+ error: error.message,
532
+ agentId: context.agentId
533
+ });
534
+
535
+ results.push({
536
+ toolId: command.toolId,
537
+ status: 'failed',
538
+ error: error.message,
539
+ timestamp: new Date().toISOString()
540
+ });
541
+ }
542
+ }
543
+
544
+ return results;
545
+ }
546
+
547
+ /**
548
+ * Track filesystem write/append operations as artifacts on the agent.
549
+ * Persists lightweight metadata (path, size, timestamp) and the content
550
+ * so the UI can show version history without re-parsing message content.
551
+ *
552
+ * The agent.artifacts map is persisted via the normal persistAgentState flow
553
+ * that already runs after tool execution in AgentScheduler.
554
+ *
555
+ * @param {Object} command - Tool command with parameters
556
+ * @param {Object} result - Execution result
557
+ * @param {Object} context - Execution context (agentId, projectDir)
558
+ * @private
559
+ */
560
+ async _trackArtifacts(command, result, context) {
561
+ try {
562
+ const agent = await this.agentPool?.getAgent?.(context.agentId);
563
+ if (!agent) {
564
+ console.log('[Artifacts] No agent found for', context.agentId);
565
+ return;
566
+ }
567
+
568
+ // Initialize artifacts map if needed: { [filePath]: { versions: [...] } }
569
+ if (!agent.artifacts) agent.artifacts = {};
570
+
571
+ const toolResult = result.result;
572
+ if (!toolResult?.success) {
573
+ console.log('[Artifacts] Tool result not successful:', { success: toolResult?.success, keys: Object.keys(toolResult || {}) });
574
+ return;
575
+ }
576
+
577
+ // Get the actions from the command parameters
578
+ // The AI may send actions at different levels depending on format:
579
+ // { parameters: { actions: [...] } } — parsed format
580
+ // { actions: [...] } top-level format (common)
581
+ // { parameters: { type: 'write', ... } } single action
582
+ const params = command.parameters || {};
583
+ const actions = params.actions || command.actions || (params.type ? [params] : []);
584
+ console.log('[Artifacts] Processing', actions.length, 'actions. Param keys:', Object.keys(params), 'cmd keys:', Object.keys(command));
585
+ const resultActions = toolResult.actions || [toolResult];
586
+
587
+ for (let i = 0; i < actions.length; i++) {
588
+ const action = actions[i];
589
+ const actionResult = resultActions[i] || {};
590
+ const type = action.type || action.action;
591
+
592
+ console.log(`[Artifacts] Action[${i}]:`, { type, filePath: action.filePath || action['file-path'], hasContent: !!action.content, contentLen: action.content?.length, actionKeys: Object.keys(action) });
593
+
594
+ if ((type === 'write' || type === 'append') && actionResult.success !== false) {
595
+ // AI uses various field names: filePath, file-path, outputPath, path
596
+ const rawPath = action.filePath || action['file-path'] || action.outputPath || action.path;
597
+ const content = action.content;
598
+ if (!rawPath || !content) {
599
+ console.log('[Artifacts] Skipped: missing filePath or content', { filePath: !!rawPath, content: !!content, actionKeys: Object.keys(action) });
600
+ continue;
601
+ }
602
+
603
+ // Use the resolved absolute path as key (prevents collisions for same-name files in different dirs)
604
+ // Fall back to raw path if fullPath not available
605
+ const absolutePath = actionResult.fullPath || rawPath;
606
+ const artifactKey = absolutePath.replace(/\\/g, '/');
607
+
608
+ // Relative display path (strip working directory prefix)
609
+ const wd = (context.projectDir || '').replace(/\\/g, '/');
610
+ const displayPath = wd && artifactKey.startsWith(wd + '/')
611
+ ? artifactKey.slice(wd.length + 1)
612
+ : artifactKey;
613
+
614
+ const version = {
615
+ id: `v-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
616
+ content,
617
+ timestamp: result.timestamp || new Date().toISOString(),
618
+ action: type,
619
+ size: Buffer.byteLength(content, 'utf8'),
620
+ fullPath: absolutePath,
621
+ };
622
+
623
+ if (!agent.artifacts[artifactKey]) {
624
+ agent.artifacts[artifactKey] = { displayPath, versions: [] };
625
+ }
626
+
627
+ // Deduplicate: skip if content identical to latest
628
+ const versions = agent.artifacts[artifactKey].versions;
629
+ const latest = versions[versions.length - 1];
630
+ if (latest && latest.content === content) continue;
631
+
632
+ versions.push(version);
633
+ console.log('[Artifacts] Tracked:', displayPath, 'v' + versions.length, '(' + version.size + ' bytes)');
634
+
635
+ // Cap at 50 versions per file to keep state reasonable
636
+ if (versions.length > 50) {
637
+ versions.splice(0, versions.length - 50);
638
+ }
639
+ }
640
+ }
641
+ } catch (e) {
642
+ this.logger?.warn?.('Artifact tracking failed (non-fatal):', e.message);
643
+ }
644
+ }
645
+
646
+ /**
647
+ * Execute async tool
648
+ * @param {Object} command - Tool command
649
+ * @param {Object} tool - Tool instance
650
+ * @param {Object} context - Execution context
651
+ * @returns {Promise<Object>} Async operation reference
652
+ */
653
+ async executeAsyncTool(command, tool, context) {
654
+ const operationId = `async-${Date.now()}-${this.operationCounter++}`;
655
+
656
+ // Create async operation entry
657
+ const operation = {
658
+ id: operationId,
659
+ toolId: command.toolId,
660
+ agentId: context.agentId,
661
+ status: 'pending',
662
+ startTime: new Date().toISOString(),
663
+ context: context
664
+ };
665
+
666
+ this.asyncOperations.set(operationId, operation);
667
+
668
+ // Start async execution
669
+ // If we have parameters object, use it. Otherwise parse the content.
670
+ let toolInput = command.parameters;
671
+
672
+ if (!toolInput && command.content) {
673
+ // Content is a string, need to parse it using tool's parseParameters method
674
+ if (typeof tool.parseParameters === 'function') {
675
+ try {
676
+ toolInput = tool.parseParameters(command.content);
677
+ } catch (error) {
678
+ this.logger?.warn(`Failed to parse parameters for async tool: ${command.toolId}`, {
679
+ error: error.message
680
+ });
681
+ // Fall back to raw content
682
+ toolInput = command.content;
683
+ }
684
+ } else {
685
+ // Tool doesn't have parseParameters, use raw content
686
+ toolInput = command.content;
687
+ }
688
+ }
689
+
690
+ // CRITICAL FIX: Unwrap TagParser format before tool execution
691
+ // TagParser wraps XML parameters in {value, attributes} objects
692
+ // This unwrapping makes all tools work consistently
693
+ if (toolInput && typeof toolInput === 'object') {
694
+ toolInput = this.unwrapParameters(toolInput);
695
+ }
696
+
697
+ tool.execute(toolInput, context)
698
+ .then(result => {
699
+ operation.status = 'completed';
700
+ operation.result = result;
701
+ operation.endTime = new Date().toISOString();
702
+ this.notifyAgentOfToolCompletion(operation);
703
+ })
704
+ .catch(error => {
705
+ operation.status = 'failed';
706
+ operation.error = error.message;
707
+ operation.endTime = new Date().toISOString();
708
+ this.notifyAgentOfToolCompletion(operation);
709
+ });
710
+
711
+ // Start monitoring
712
+ this.monitorAsyncOperation(operationId);
713
+
714
+ return {
715
+ toolId: command.toolId,
716
+ status: 'async-pending',
717
+ operationId: operationId,
718
+ message: `Async tool started with operation ID: ${operationId}`,
719
+ timestamp: new Date().toISOString()
720
+ };
721
+ }
722
+
723
+ /**
724
+ * Monitor async operation
725
+ * @param {string} operationId - Operation ID to monitor
726
+ */
727
+ async monitorAsyncOperation(operationId) {
728
+ const checkInterval = 5000; // 5 seconds
729
+ const maxChecks = 120; // 10 minutes max
730
+ let checks = 0;
731
+
732
+ const monitor = setInterval(() => {
733
+ const operation = this.asyncOperations.get(operationId);
734
+
735
+ if (!operation) {
736
+ clearInterval(monitor);
737
+ return;
738
+ }
739
+
740
+ checks++;
741
+
742
+ if (operation.status !== 'pending' || checks >= maxChecks) {
743
+ clearInterval(monitor);
744
+
745
+ if (checks >= maxChecks) {
746
+ operation.status = 'timeout';
747
+ operation.error = 'Operation timed out';
748
+ operation.endTime = new Date().toISOString();
749
+ this.notifyAgentOfToolCompletion(operation);
750
+ }
751
+ }
752
+ }, checkInterval);
753
+ }
754
+
755
+ /**
756
+ * Notify agent of tool completion
757
+ * @param {Object} operation - Completed operation
758
+ */
759
+ async notifyAgentOfToolCompletion(operation) {
760
+ if (!operation.agentId) return;
761
+
762
+ try {
763
+ // Queue tool result for the agent
764
+ await this.agentPool.addToolResult(operation.agentId, {
765
+ toolId: operation.toolId,
766
+ status: operation.status,
767
+ result: operation.result,
768
+ error: operation.error,
769
+ executionTime: operation.endTime ?
770
+ new Date(operation.endTime) - new Date(operation.startTime) : null,
771
+ timestamp: operation.endTime || new Date().toISOString()
772
+ });
773
+
774
+ // Ensure session is registered for agent (tool results added to queue
775
+ // will cause agent to become active via AgentActivityService)
776
+ if (this.scheduler) {
777
+ await this.scheduler.addAgent(operation.agentId, {
778
+ triggeredBy: 'tool-completion',
779
+ sessionId: operation.context?.sessionId
780
+ });
781
+ }
782
+
783
+ this.logger.info(`Agent notified of tool completion: ${operation.agentId}`, {
784
+ toolId: operation.toolId,
785
+ status: operation.status
786
+ });
787
+
788
+ } catch (error) {
789
+ this.logger.error(`Failed to notify agent of tool completion`, {
790
+ agentId: operation.agentId,
791
+ toolId: operation.toolId,
792
+ error: error.message
793
+ });
794
+ }
795
+ }
796
+
797
+ /**
798
+ * Get tool status
799
+ * @param {string} operationId - Operation ID
800
+ * @returns {Promise<Object>} Operation status
801
+ */
802
+ async getToolStatus(operationId) {
803
+ const operation = this.asyncOperations.get(operationId);
804
+
805
+ if (!operation) {
806
+ return {
807
+ status: 'not-found',
808
+ error: `Operation not found: ${operationId}`
809
+ };
810
+ }
811
+
812
+ return {
813
+ id: operation.id,
814
+ toolId: operation.toolId,
815
+ status: operation.status,
816
+ result: operation.result,
817
+ error: operation.error,
818
+ startTime: operation.startTime,
819
+ endTime: operation.endTime
820
+ };
821
+ }
822
+
823
+ /**
824
+ * Extract and execute tools from content
825
+ * Called by AgentScheduler after getting AI response
826
+ * @param {string} content - Content containing tool commands
827
+ * @param {string} agentId - Agent ID
828
+ * @param {Object} context - Execution context
829
+ * @returns {Promise<Array>} Tool execution results
830
+ */
831
+ async extractAndExecuteTools(content, agentId, context) {
832
+ try {
833
+ // Extract tool commands
834
+ const commands = await this.extractToolCommands(content);
835
+
836
+ if (commands.length === 0) {
837
+ return [];
838
+ }
839
+
840
+ // Get agent to include its directoryAccess configuration
841
+ const agent = await this.agentPool.getAgent(agentId);
842
+
843
+ // Execute tools with agent context including sessionId and directoryAccess
844
+ const toolContext = {
845
+ ...context,
846
+ agentId,
847
+ sessionId: context.sessionId, // Ensure sessionId is explicitly available for tools
848
+ directoryAccess: agent?.directoryAccess, // Include agent's directory access configuration
849
+ projectDir: agent?.directoryAccess?.workingDirectory || agent?.projectDir || context.projectDir, // Extract project directory from directoryAccess
850
+ // Per-agent tool configuration keyed by tool id. Tools can read
851
+ // their own slice via `context.toolConfig` (injected per-tool in
852
+ // executeTools below) or inspect the full map here if they need
853
+ // cross-tool state. See agentPool.js for the schema contract.
854
+ agentToolConfig: (agent && agent.toolConfig) ? agent.toolConfig : {},
855
+ agentPool: this.agentPool,
856
+ contextManager: this.contextManager,
857
+ aiService: this.aiService,
858
+ messageProcessor: this,
859
+ orchestrator: this.orchestrator
860
+ };
861
+
862
+ const results = await this.executeTools(commands, toolContext);
863
+
864
+ this.logger.info(`Executed ${results.length} tools for agent: ${agentId}`, {
865
+ tools: results.map(r => ({ toolId: r.toolId, status: r.status }))
866
+ });
867
+
868
+ return results;
869
+
870
+ } catch (error) {
871
+ this.logger.error(`Tool extraction/execution failed for agent: ${agentId}`, {
872
+ error: error.message
873
+ });
874
+ return [];
875
+ }
876
+ }
877
+
878
+ /**
879
+ * Stop autonomous execution for an agent
880
+ * Proxy method to AgentScheduler
881
+ * @param {string} agentId - Agent ID to stop
882
+ * @returns {Promise<Object>} Result with agent state
883
+ */
884
+ async stopAutonomousExecution(agentId) {
885
+ if (!this.scheduler) {
886
+ return {
887
+ success: false,
888
+ error: 'Scheduler not available'
889
+ };
890
+ }
891
+
892
+ return await this.scheduler.stopAgentExecution(agentId);
893
+ }
894
+
895
+ /**
896
+ * Inject tool results into conversation
897
+ * @param {string} agentId - Agent ID
898
+ * @param {Array} toolResults - Tool execution results
899
+ * @returns {Promise<void>}
900
+ */
901
+ async injectToolResultsIntoConversation(agentId, toolResults) {
902
+ const agent = await this.agentPool.getAgent(agentId);
903
+ if (!agent) return;
904
+
905
+ for (const result of toolResults) {
906
+ const toolMessage = {
907
+ id: `tool-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
908
+ role: 'system',
909
+ content: this.formatToolResultForAgent(result),
910
+ timestamp: new Date().toISOString(),
911
+ type: 'tool-result',
912
+ toolId: result.toolId,
913
+ status: result.status
914
+ };
915
+
916
+ // Add to conversation history
917
+ agent.conversations.full.messages.push(toolMessage);
918
+
919
+ // Also add to current model conversation if exists
920
+ if (agent.currentModel && agent.conversations[agent.currentModel]) {
921
+ agent.conversations[agent.currentModel].messages.push(toolMessage);
922
+ }
923
+ }
924
+
925
+ // Update last activity
926
+ agent.conversations.full.lastUpdated = new Date().toISOString();
927
+ if (agent.currentModel && agent.conversations[agent.currentModel]) {
928
+ agent.conversations[agent.currentModel].lastUpdated = new Date().toISOString();
929
+ }
930
+
931
+ await this.agentPool.persistAgentState(agentId);
932
+ }
933
+
934
+ /**
935
+ * Format tool result for agent consumption
936
+ * @param {Object} result - Tool execution result
937
+ * @returns {string} Formatted result
938
+ */
939
+ formatToolResultForAgent(result) {
940
+ if (result.status === 'completed') {
941
+ if (typeof result.result === 'object') {
942
+ return `Tool ${result.toolId} completed successfully:\n${JSON.stringify(result.result, null, 2)}`;
943
+ }
944
+ return `Tool ${result.toolId} completed successfully:\n${result.result}`;
945
+ } else if (result.status === 'failed') {
946
+ return `Tool ${result.toolId} failed: ${result.error || 'Unknown error'}`;
947
+ } else if (result.status === 'async-pending') {
948
+ return `Tool ${result.toolId} is running asynchronously (Operation ID: ${result.operationId})`;
949
+ }
950
+ return `Tool ${result.toolId} status: ${result.status}`;
951
+ }
952
+ }
953
+
955
954
  export default MessageProcessor;