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,1290 +1,1289 @@
1
- /**
2
- * @file tools/visualEditorTool.js
3
- * @description Tool for visual editor integration - allows agents to interact
4
- * with user's web application visually and receive element selections with
5
- * source code locations.
6
- *
7
- * Phase 1: Basic actions (get-context, set-app-url, get-status)
8
- * Phase 5: Full actions (highlight, scroll-to, get-source, reload, set-mode)
9
- */
10
-
11
- import { BaseTool } from './baseTool.js';
12
- import { getVisualEditorBridge, InstanceStatus } from '../services/visualEditorBridge.js';
13
- import { getVisualEditorServer, getVisualEditorPort, getVisualEditorBaseUrl } from '../services/visualEditorServer.js';
14
- import { getPortTracker } from '../services/portTracker.js';
15
- import { getProjectDetector, PROJECT_TYPES } from '../services/projectDetector.js';
16
-
17
- /**
18
- * Supported actions for the Visual Editor Tool
19
- */
20
- const ACTIONS = {
21
- GET_CONTEXT: 'get-context',
22
- SET_APP_URL: 'set-app-url',
23
- GET_STATUS: 'get-status',
24
- CLEAR_CONTEXT: 'clear-context',
25
- // Server management actions
26
- START_SERVER: 'start-server', // Start dev server for project
27
- SERVE_STATIC: 'serve-static', // Serve static files directly
28
- DETECT_PROJECT: 'detect-project', // Detect project type
29
- // UI control actions
30
- OPEN_EDITOR: 'open-editor', // Open visual editor in UI with specified URL
31
- // Phase 5 actions
32
- HIGHLIGHT: 'highlight',
33
- SCROLL_TO: 'scroll-to',
34
- GET_SOURCE: 'get-source',
35
- RELOAD: 'reload',
36
- SET_MODE: 'set-mode'
37
- };
38
-
39
- /**
40
- * VisualEditorTool - Interact with user's web application visually
41
- */
42
- export class VisualEditorTool extends BaseTool {
43
- constructor(config = {}, logger = null) {
44
- super(config, logger);
45
-
46
- // Override tool ID
47
- this.id = 'visual-editor';
48
-
49
- // Tool properties
50
- this.isAsync = false;
51
- this.requiresProject = false;
52
-
53
- // Bridge instance (lazy loaded)
54
- this._bridge = null;
55
- }
56
-
57
- /**
58
- * Get bridge instance (lazy load)
59
- * @private
60
- */
61
- _getBridge() {
62
- if (!this._bridge) {
63
- this._bridge = getVisualEditorBridge();
64
- }
65
- return this._bridge;
66
- }
67
-
68
- /**
69
- * Get tool description for agent system prompt
70
- * @returns {string} Formatted tool description
71
- */
72
- getDescription() {
73
- return `Tool: Visual Editor - Interact with the user's web application visually.
74
-
75
- **PURPOSE:** Allow users to visually select elements in their app preview.
76
- You receive context about selected elements including source code location.
77
-
78
- **COMPLETE WORKFLOW - When user wants to visually edit their app:**
79
-
80
- 1. **If app is NOT running yet:**
81
- - Use \`detect-project\` to identify project type
82
- - Use \`serve-static\` (for HTML) or start dev server via Terminal tool
83
-
84
- 2. **Open the Visual Editor UI:**
85
- - Use \`open-editor\` with the app URL to show the visual editor to the user
86
- - This opens the preview panel in the UI automatically
87
-
88
- 3. **User clicks elements in the preview**
89
- - You automatically receive element context in messages
90
-
91
- **ACTIONS:**
92
-
93
- **open-editor** - Open visual editor UI with app URL (RECOMMENDED after starting server)
94
- \`\`\`json
95
- {
96
- "toolId": "visual-editor",
97
- "action": "open-editor",
98
- "parameters": {
99
- "url": "http://localhost:3000"
100
- }
101
- }
102
- \`\`\`
103
- This opens the visual editor panel in the user's UI and loads your app for visual selection.
104
-
105
- **detect-project** - Detect project type and get server suggestions
106
- \`\`\`json
107
- {
108
- "toolId": "visual-editor",
109
- "action": "detect-project"
110
- }
111
- \`\`\`
112
- Returns: projectType, framework, serverCommand, defaultPort
113
-
114
- **serve-static** - Serve static HTML files directly (no external server needed)
115
- \`\`\`json
116
- {
117
- "toolId": "visual-editor",
118
- "action": "serve-static",
119
- "parameters": {
120
- "directory": "public" // Optional: subdirectory to serve, defaults to project root
121
- }
122
- }
123
- \`\`\`
124
- Returns: url (e.g., http://localhost:4000/)
125
-
126
- **set-app-url** - Configure the app URL for visual editing
127
- \`\`\`json
128
- {
129
- "toolId": "visual-editor",
130
- "action": "set-app-url",
131
- "parameters": {
132
- "url": "http://localhost:3000"
133
- }
134
- }
135
- \`\`\`
136
-
137
- **get-context** - Get current visual selection (if any)
138
- \`\`\`json
139
- {
140
- "toolId": "visual-editor",
141
- "action": "get-context"
142
- }
143
- \`\`\`
144
-
145
- **get-status** - Get visual editor instance status
146
- \`\`\`json
147
- {
148
- "toolId": "visual-editor",
149
- "action": "get-status"
150
- }
151
- \`\`\`
152
-
153
- **clear-context** - Clear the current visual selection
154
- \`\`\`json
155
- {
156
- "toolId": "visual-editor",
157
- "action": "clear-context"
158
- }
159
- \`\`\`
160
-
161
- **AUTOMATIC CONTEXT INJECTION:**
162
- When user selects an element, you receive visual context automatically:
163
- \`\`\`
164
- [VISUAL CONTEXT - User selected element]
165
- Element: <button class="btn-primary">
166
- Source: src/components/Form.tsx:42 (Component: SubmitButton)
167
- Code:
168
- 41│ return (
169
- ► 42│ <button className="btn-primary">Submit</button>
170
- 43│ );
171
- \`\`\`
172
-
173
- **WORKFLOW EXAMPLE:**
174
- 1. User: "Let me visually edit the app"
175
- 2. Agent: detect-project → React (Vite)
176
- 3. Agent: Terminal → npm run dev (starts on port 5173)
177
- 4. Agent: open-editor → url: http://localhost:5173 (opens UI panel)
178
- 5. Agent: "Visual editor ready! Click any element to select it."
179
- 6. User clicks element → context injected automatically
180
- 7. Agent: Uses filesystem tool to edit the source file
181
-
182
- **TIPS:**
183
- - For static HTML: Use \`serve-static\` then \`open-editor\`
184
- - For React/Vue/Next.js: Start dev server via Terminal, then \`open-editor\`
185
- - Always use \`open-editor\` to show the preview UI to the user
186
- - Always acknowledge visual context when user selects element
187
- - Use source location (file:line) to make targeted code changes`;
188
- }
189
-
190
- /**
191
- * Get supported actions
192
- * @returns {Array<string>} Supported action names
193
- */
194
- getSupportedActions() {
195
- return Object.values(ACTIONS);
196
- }
197
-
198
- /**
199
- * Get required parameters
200
- * @returns {Array<string>} Required parameter names
201
- */
202
- getRequiredParameters() {
203
- return ['action'];
204
- }
205
-
206
- /**
207
- * Parse parameters from tool command content
208
- * @param {string|Object} content - Raw content or parsed object
209
- * @returns {Object} Parsed parameters
210
- */
211
- parseParameters(content) {
212
- // Handle JSON format
213
- if (typeof content === 'object' && content !== null) {
214
- return this._parseJSONParams(content);
215
- }
216
-
217
- // Handle string format
218
- if (typeof content === 'string') {
219
- const trimmed = content.trim();
220
-
221
- // Try to parse as JSON
222
- if (trimmed.startsWith('{')) {
223
- try {
224
- const parsed = JSON.parse(trimmed);
225
- return this._parseJSONParams(parsed);
226
- } catch (err) {
227
- // Fall through to XML parsing
228
- }
229
- }
230
-
231
- // Parse as XML
232
- return this._parseXMLParams(content);
233
- }
234
-
235
- throw new Error('Invalid parameter format');
236
- }
237
-
238
- /**
239
- * Parse JSON parameters
240
- * @private
241
- */
242
- _parseJSONParams(obj) {
243
- // Handle parameters wrapper
244
- if (obj.parameters) {
245
- return {
246
- action: obj.action || ACTIONS.GET_CONTEXT,
247
- ...obj.parameters
248
- };
249
- }
250
-
251
- return {
252
- action: obj.action || ACTIONS.GET_CONTEXT,
253
- url: obj.url,
254
- selector: obj.selector,
255
- mode: obj.mode,
256
- projectRoot: obj.projectRoot
257
- };
258
- }
259
-
260
- /**
261
- * Parse XML parameters
262
- * @private
263
- */
264
- _parseXMLParams(content) {
265
- const extractTag = (tag) => {
266
- const regex = new RegExp(`<${tag}>([\\s\\S]*?)<\\/${tag}>`, 'i');
267
- const match = regex.exec(content);
268
- return match ? match[1].trim() : null;
269
- };
270
-
271
- return {
272
- action: extractTag('action') || ACTIONS.GET_CONTEXT,
273
- url: extractTag('url'),
274
- selector: extractTag('selector'),
275
- mode: extractTag('mode'),
276
- projectRoot: extractTag('project-root') || extractTag('projectRoot')
277
- };
278
- }
279
-
280
- /**
281
- * Validate parameters
282
- * @param {Object} params - Parameters to validate
283
- * @returns {Object} Validation result
284
- */
285
- customValidateParameters(params) {
286
- // Validate action
287
- if (!Object.values(ACTIONS).includes(params.action)) {
288
- return {
289
- valid: false,
290
- error: `Invalid action: ${params.action}. Valid actions: ${Object.values(ACTIONS).join(', ')}`
291
- };
292
- }
293
-
294
- // Validate action-specific parameters
295
- if (params.action === ACTIONS.SET_APP_URL && !params.url) {
296
- return {
297
- valid: false,
298
- error: 'URL is required for set-app-url action'
299
- };
300
- }
301
-
302
- // Phase 5 actions validation (placeholder)
303
- if ([ACTIONS.HIGHLIGHT, ACTIONS.SCROLL_TO, ACTIONS.GET_SOURCE].includes(params.action)) {
304
- if (!params.selector) {
305
- return {
306
- valid: false,
307
- error: `Selector is required for ${params.action} action`
308
- };
309
- }
310
- }
311
-
312
- return { valid: true };
313
- }
314
-
315
- /**
316
- * Execute tool with parsed parameters
317
- * @param {Object|string} params - Parsed parameters or raw content
318
- * @param {Object} context - Execution context
319
- * @returns {Promise<Object>} Execution result
320
- */
321
- async execute(params, context = {}) {
322
- // Reverse-forgiveness: accept plural {actions:[{...}]} envelope
323
- // (cross-tool unification).
324
- if (Array.isArray(params?.actions) && params.actions.length > 0) {
325
- const a = params.actions[0] || {};
326
- params = { ...params, ...a, action: a.type || a.action || params.action };
327
- }
328
-
329
- // FIRST THING: an unconditional console.log so we can always see that
330
- // the tool was invoked — before any early-return (parse failure,
331
- // bridge disabled, validation fail) or thrown error could swallow it.
332
- // Earlier the log lived past a `bridge.isEnabled()` gate and was
333
- // silently skipped whenever that gate tripped, making it look like
334
- // the tool wasn't being called at all.
335
- console.log('[VisualEditorTool] execute() called', {
336
- paramsType: typeof params,
337
- paramsAction: params?.action,
338
- paramsUrl: params?.url,
339
- agentId: context?.agentId,
340
- hasGlobalWebServer: !!global.loxiaWebServer,
341
- hasOrchestratorWebServer: !!context?.orchestrator?.webServer,
342
- hasAgentPool: !!context?.agentPool,
343
- });
344
-
345
- try {
346
- const { agentId } = context;
347
-
348
- // Auto-parse if string
349
- if (typeof params === 'string') {
350
- params = this.parseParameters(params);
351
- } else if (typeof params === 'object' && params !== null && !params.action) {
352
- params = this.parseParameters(params);
353
- }
354
-
355
- // Get bridge
356
- const bridge = this._getBridge();
357
-
358
- // Check if bridge is enabled
359
- if (!bridge.isEnabled()) {
360
- console.warn('[VisualEditorTool] Early exit: bridge.isEnabled() === false');
361
- return {
362
- success: false,
363
- error: 'Visual editor is disabled. Enable it in configuration.'
364
- };
365
- }
366
-
367
- // Validate
368
- const validation = this.customValidateParameters(params);
369
- if (!validation.valid) {
370
- console.warn('[VisualEditorTool] Early exit: validation failed', { action: params?.action, error: validation.error });
371
- return {
372
- success: false,
373
- error: validation.error
374
- };
375
- }
376
-
377
- // Confirmation that we reached action dispatch
378
- console.log(`[VisualEditorTool] Action=${params.action} — about to dispatch`, {
379
- agentId,
380
- url: params.url,
381
- });
382
-
383
- // Route to action handler
384
- switch (params.action) {
385
- case ACTIONS.GET_CONTEXT:
386
- return this._handleGetContext(bridge, agentId);
387
-
388
- case ACTIONS.SET_APP_URL:
389
- return this._handleSetAppUrl(bridge, agentId, params, context);
390
-
391
- case ACTIONS.GET_STATUS:
392
- return this._handleGetStatus(bridge, agentId);
393
-
394
- case ACTIONS.CLEAR_CONTEXT:
395
- return this._handleClearContext(bridge, agentId);
396
-
397
- // Server management actions
398
- case ACTIONS.DETECT_PROJECT:
399
- return this._handleDetectProject(context);
400
-
401
- case ACTIONS.SERVE_STATIC:
402
- return this._handleServeStatic(bridge, agentId, params, context);
403
-
404
- case ACTIONS.START_SERVER:
405
- return this._handleStartServer(bridge, agentId, params, context);
406
-
407
- // UI control actions
408
- case ACTIONS.OPEN_EDITOR:
409
- return this._handleOpenEditor(bridge, agentId, params, context);
410
-
411
- // Phase 5 actions - full editor control
412
- case ACTIONS.HIGHLIGHT:
413
- return this._handleHighlight(bridge, agentId, params);
414
-
415
- case ACTIONS.SCROLL_TO:
416
- return this._handleScrollTo(bridge, agentId, params);
417
-
418
- case ACTIONS.GET_SOURCE:
419
- return this._handleGetSource(bridge, agentId, params);
420
-
421
- case ACTIONS.RELOAD:
422
- return this._handleReload(bridge, agentId);
423
-
424
- case ACTIONS.SET_MODE:
425
- return this._handleSetMode(bridge, agentId, params);
426
-
427
- default:
428
- return {
429
- success: false,
430
- error: `Unknown action: ${params.action}`
431
- };
432
- }
433
-
434
- } catch (error) {
435
- // Log full stack + action context so failures that happen BEFORE
436
- // the broadcast fires (e.g., bridge.getInstance throws) are
437
- // visible in the Node console. Previously the tool returned a
438
- // clean error string to the agent and the human operator saw
439
- // nothing — making "the editor never opens" undebuggable.
440
- console.error(`[VisualEditorTool] Action=${params?.action} FAILED: ${error.message}`, {
441
- agentId,
442
- action: params?.action,
443
- stack: error.stack,
444
- });
445
- return {
446
- success: false,
447
- error: error.message
448
- };
449
- }
450
- }
451
-
452
- /**
453
- * Handle get-context action
454
- * @private
455
- */
456
- _handleGetContext(bridge, agentId) {
457
- if (!agentId) {
458
- return {
459
- success: false,
460
- error: 'Agent ID is required to get visual context'
461
- };
462
- }
463
-
464
- const context = bridge.getVisualContext(agentId);
465
-
466
- if (!context) {
467
- return {
468
- success: true,
469
- hasContext: false,
470
- message: 'No visual selection. User can select an element in Visual Mode.'
471
- };
472
- }
473
-
474
- // Format context for agent consumption
475
- return {
476
- success: true,
477
- hasContext: true,
478
- context: {
479
- selector: context.selector,
480
- tagName: context.tagName,
481
- text: context.text,
482
- attributes: context.attributes,
483
- sourceHint: context.sourceHint,
484
- receivedAt: context.receivedAt
485
- },
486
- formatted: this._formatContextForAgent(context)
487
- };
488
- }
489
-
490
- /**
491
- * Handle set-app-url action
492
- * @private
493
- */
494
- async _handleSetAppUrl(bridge, agentId, params, context) {
495
- if (!agentId) {
496
- return {
497
- success: false,
498
- error: 'Agent ID is required to set app URL'
499
- };
500
- }
501
-
502
- // Validate URL format
503
- try {
504
- new URL(params.url);
505
- } catch {
506
- return {
507
- success: false,
508
- error: `Invalid URL format: ${params.url}`
509
- };
510
- }
511
-
512
- // Get or create instance
513
- const instance = await bridge.getInstance(agentId, {
514
- appUrl: params.url,
515
- projectRoot: params.projectRoot
516
- });
517
-
518
- // Generate editor URL and broadcast to web-ui to auto-open the visual editor panel
519
- const baseUrl = getVisualEditorBaseUrl();
520
- const editorUrl = `${baseUrl}?agentId=${agentId}&appUrl=${encodeURIComponent(params.url)}`;
521
- this._broadcastOpenEditor(context, {
522
- agentId,
523
- appUrl: params.url,
524
- editorUrl
525
- });
526
-
527
- return {
528
- success: true,
529
- message: `App URL configured: ${params.url}. Visual editor panel opening.`,
530
- editorUrl,
531
- instance: {
532
- agentId: instance.agentId,
533
- appUrl: instance.appUrl,
534
- projectRoot: instance.projectRoot,
535
- status: instance.status
536
- }
537
- };
538
- }
539
-
540
- /**
541
- * Handle get-status action
542
- * @private
543
- */
544
- _handleGetStatus(bridge, agentId) {
545
- if (!agentId) {
546
- // Return global status
547
- const instances = bridge.listInstances();
548
- return {
549
- success: true,
550
- instanceCount: instances.length,
551
- maxInstances: bridge.maxInstances,
552
- instances: instances.map(inst => ({
553
- agentId: inst.agentId,
554
- status: inst.status,
555
- appUrl: inst.appUrl,
556
- hasContext: inst.hasContext,
557
- idleMs: inst.idleMs
558
- }))
559
- };
560
- }
561
-
562
- const status = bridge.getStatus(agentId);
563
-
564
- if (!status.exists) {
565
- return {
566
- success: true,
567
- exists: false,
568
- message: 'No visual editor instance for this agent. Use set-app-url to configure.'
569
- };
570
- }
571
-
572
- return {
573
- success: true,
574
- exists: true,
575
- status: status.status,
576
- appUrl: status.appUrl,
577
- projectRoot: status.projectRoot,
578
- editorUrl: status.editorUrl,
579
- hasVisualContext: status.hasVisualContext,
580
- subscriberCount: status.subscriberCount,
581
- idleMs: status.idleMs,
582
- error: status.error
583
- };
584
- }
585
-
586
- /**
587
- * Handle clear-context action
588
- * @private
589
- */
590
- _handleClearContext(bridge, agentId) {
591
- if (!agentId) {
592
- return {
593
- success: false,
594
- error: 'Agent ID is required to clear visual context'
595
- };
596
- }
597
-
598
- const cleared = bridge.clearVisualContext(agentId);
599
-
600
- return {
601
- success: true,
602
- cleared,
603
- message: cleared
604
- ? 'Visual context cleared'
605
- : 'No visual context to clear'
606
- };
607
- }
608
-
609
- // === Server Management Action Handlers ===
610
-
611
- /**
612
- * Handle detect-project action
613
- * @private
614
- */
615
- async _handleDetectProject(context) {
616
- // Support all three names historically used by different callers:
617
- // - projectRoot — legacy visual-editor context
618
- // - workingDirectory — terminal/filesystem context
619
- // - projectDir — messageProcessor's canonical name (src/core/messageProcessor.js)
620
- // Without the third fallback, every call routed through messageProcessor
621
- // (i.e., all agent-initiated ones) fails with "No project directory
622
- // available" — leaving the editor only usable when invoked manually
623
- // from a path where the context happens to carry the legacy keys.
624
- const projectRoot = context.projectRoot || context.workingDirectory || context.projectDir;
625
-
626
- if (!projectRoot) {
627
- return {
628
- success: false,
629
- error: 'No project directory available. Set projectRoot in context.'
630
- };
631
- }
632
-
633
- const detector = getProjectDetector();
634
- const detection = await detector.detect(projectRoot);
635
-
636
- if (detection.error) {
637
- return {
638
- success: false,
639
- error: detection.error
640
- };
641
- }
642
-
643
- const serverInfo = detector.getSuggestedServerCommand(detection);
644
-
645
- return {
646
- success: true,
647
- projectDir: projectRoot,
648
- projectType: detection.projectType,
649
- framework: detection.framework,
650
- isStatic: detection.isStatic,
651
- entryPoints: detection.entryPoints,
652
- serverCommand: serverInfo.command,
653
- defaultPort: serverInfo.port,
654
- availableScripts: detection.availableScripts,
655
- confidence: detection.confidence,
656
- message: detection.isStatic
657
- ? `Static HTML project detected. Use 'serve-static' action to preview.`
658
- : `${detection.framework || detection.projectType} project detected. Run "${serverInfo.command}" to start server.`
659
- };
660
- }
661
-
662
- /**
663
- * Handle serve-static action - serve static files via Visual Editor Server
664
- * @private
665
- */
666
- async _handleServeStatic(bridge, agentId, params, context) {
667
- // Support all three names historically used by different callers:
668
- // - projectRoot — legacy visual-editor context
669
- // - workingDirectory — terminal/filesystem context
670
- // - projectDir — messageProcessor's canonical name (src/core/messageProcessor.js)
671
- // Without the third fallback, every call routed through messageProcessor
672
- // (i.e., all agent-initiated ones) fails with "No project directory
673
- // available" — leaving the editor only usable when invoked manually
674
- // from a path where the context happens to carry the legacy keys.
675
- const projectRoot = context.projectRoot || context.workingDirectory || context.projectDir;
676
-
677
- if (!projectRoot) {
678
- return {
679
- success: false,
680
- error: 'No project directory available'
681
- };
682
- }
683
-
684
- // Get Visual Editor Server
685
- const server = getVisualEditorServer();
686
-
687
- // Start server if not running
688
- if (!server.isRunning) {
689
- await server.start();
690
- }
691
-
692
- // Determine directory to serve
693
- const directory = params.directory || '';
694
- const fullPath = directory ? `${projectRoot}/${directory}` : projectRoot;
695
-
696
- // Register static directory with the server
697
- server.registerStaticDir(agentId, fullPath);
698
-
699
- // Generate URL for static files using configurable port
700
- const baseUrl = getVisualEditorBaseUrl();
701
- const staticUrl = `${baseUrl}/static/${agentId}/`;
702
-
703
- // Also set as app URL for the visual editor
704
- if (agentId) {
705
- const instance = await bridge.getInstance(agentId, {
706
- appUrl: staticUrl,
707
- projectRoot
708
- });
709
-
710
- bridge.updateStatus(agentId, instance.status, {
711
- editorUrl: `${baseUrl}?agentId=${agentId}&appUrl=${encodeURIComponent(staticUrl)}`
712
- });
713
- }
714
-
715
- const editorUrl = `${baseUrl}?agentId=${agentId}&appUrl=${encodeURIComponent(staticUrl)}`;
716
-
717
- // Broadcast to web-ui to auto-open the visual editor panel
718
- this._broadcastOpenEditor(context, {
719
- agentId,
720
- appUrl: staticUrl,
721
- editorUrl
722
- });
723
-
724
- return {
725
- success: true,
726
- url: staticUrl,
727
- directory: fullPath,
728
- message: `Static files served at ${staticUrl}. Visual Mode ready.`,
729
- editorUrl
730
- };
731
- }
732
-
733
- /**
734
- * Handle start-server action - provides server command info
735
- * (Actual server start should be done via Terminal tool)
736
- * @private
737
- */
738
- async _handleStartServer(bridge, agentId, params, context) {
739
- // Support all three names historically used by different callers:
740
- // - projectRoot — legacy visual-editor context
741
- // - workingDirectory — terminal/filesystem context
742
- // - projectDir — messageProcessor's canonical name (src/core/messageProcessor.js)
743
- // Without the third fallback, every call routed through messageProcessor
744
- // (i.e., all agent-initiated ones) fails with "No project directory
745
- // available" — leaving the editor only usable when invoked manually
746
- // from a path where the context happens to carry the legacy keys.
747
- const projectRoot = context.projectRoot || context.workingDirectory || context.projectDir;
748
-
749
- if (!projectRoot) {
750
- return {
751
- success: false,
752
- error: 'No project directory available'
753
- };
754
- }
755
-
756
- // Detect project
757
- const detector = getProjectDetector();
758
- const detection = await detector.detect(projectRoot);
759
-
760
- // Get port tracker
761
- const portTracker = getPortTracker();
762
- const preferredPort = params.port || detection.defaultPort;
763
-
764
- // Find available port
765
- const availablePort = await portTracker.findAvailablePort(
766
- preferredPort,
767
- preferredPort + 100
768
- );
769
-
770
- if (!availablePort) {
771
- return {
772
- success: false,
773
- error: `No available ports in range ${preferredPort}-${preferredPort + 100}`
774
- };
775
- }
776
-
777
- // Get command with port
778
- const serverInfo = detector.getSuggestedServerCommand(detection, availablePort);
779
-
780
- return {
781
- success: true,
782
- projectType: detection.projectType,
783
- framework: detection.framework,
784
- command: serverInfo.command,
785
- port: availablePort,
786
- expectedUrl: `http://localhost:${availablePort}`,
787
- instructions: `
788
- To start the server:
789
- 1. Use Terminal tool: ${serverInfo.command}
790
- 2. Wait for server to start
791
- 3. Use open-editor with url: http://localhost:${availablePort} (this will open the visual editor panel in the UI)
792
-
793
- Or for static HTML, use 'serve-static' action instead (auto-opens the editor).
794
- `.trim()
795
- };
796
- }
797
-
798
- // === UI Control Action Handlers ===
799
-
800
- /**
801
- * Handle open-editor action - opens visual editor UI with specified app URL
802
- * Broadcasts a message to the web-ui to enable visual editor mode
803
- * @private
804
- */
805
- async _handleOpenEditor(bridge, agentId, params, context) {
806
- if (!agentId) {
807
- return {
808
- success: false,
809
- error: 'Agent ID is required to open visual editor'
810
- };
811
- }
812
-
813
- // Validate URL format
814
- const appUrl = params.url;
815
- if (!appUrl) {
816
- return {
817
- success: false,
818
- error: 'URL parameter is required. Provide the URL of your running app (e.g., http://localhost:3000)'
819
- };
820
- }
821
-
822
- try {
823
- new URL(appUrl);
824
- } catch {
825
- return {
826
- success: false,
827
- error: `Invalid URL format: ${appUrl}`
828
- };
829
- }
830
-
831
- // Verify the target app is reachable and scan for errors
832
- let pageWarnings = [];
833
- try {
834
- const controller = new AbortController();
835
- const timeoutId = setTimeout(() => controller.abort(), 5000);
836
- const response = await fetch(appUrl, {
837
- method: 'GET',
838
- signal: controller.signal,
839
- redirect: 'follow'
840
- });
841
- clearTimeout(timeoutId);
842
-
843
- if (!response.ok && response.status >= 500) {
844
- return {
845
- success: false,
846
- error: `Target app returned HTTP ${response.status} at ${appUrl}. Make sure the app is running and accessible.`
847
- };
848
- }
849
-
850
- // Scan response body for common error patterns
851
- const contentType = response.headers.get('content-type') || '';
852
- if (contentType.includes('text/html') || contentType.includes('text/plain')) {
853
- const body = await response.text();
854
- const bodyLower = body.toLowerCase();
855
- const bodySnippet = body.slice(0, 4000); // Limit scan size
856
-
857
- // Common build/runtime error patterns
858
- const errorPatterns = [
859
- { pattern: /Module not found|Cannot find module/i, msg: 'Module not found error detected' },
860
- { pattern: /SyntaxError|Unexpected token/i, msg: 'JavaScript syntax error detected' },
861
- { pattern: /TypeError|ReferenceError|RangeError/i, msg: 'JavaScript runtime error detected' },
862
- { pattern: /ENOENT|EACCES|EADDRINUSE/i, msg: 'Node.js filesystem/port error detected' },
863
- { pattern: /Cannot GET|Cannot POST|Not Found/i, msg: 'Route not found (404-like page)' },
864
- { pattern: /Compilation failed|Build failed|Failed to compile/i, msg: 'Build/compilation error detected' },
865
- { pattern: /Error: |ERROR |error occurred/i, msg: 'Error message found in page content' },
866
- { pattern: /<pre class="error">/i, msg: 'Error overlay detected (dev server error page)' },
867
- { pattern: /vite-error-overlay|react-error-overlay|nextjs-portal/i, msg: 'Framework error overlay detected' },
868
- ];
869
-
870
- for (const { pattern, msg } of errorPatterns) {
871
- const match = bodySnippet.match(pattern);
872
- if (match) {
873
- // Extract surrounding context (up to 150 chars around the match)
874
- const idx = bodySnippet.indexOf(match[0]);
875
- const start = Math.max(0, idx - 50);
876
- const end = Math.min(bodySnippet.length, idx + match[0].length + 100);
877
- const context = bodySnippet.slice(start, end)
878
- .replace(/<[^>]+>/g, ' ') // Strip HTML tags
879
- .replace(/\s+/g, ' ') // Collapse whitespace
880
- .trim();
881
- pageWarnings.push(`${msg}: ...${context}...`);
882
- }
883
- }
884
-
885
- // Check if page is essentially empty (broken build output)
886
- const textContent = body.replace(/<[^>]+>/g, '').trim();
887
- if (textContent.length < 10 && !body.includes('<script')) {
888
- pageWarnings.push('Page appears empty (no content or scripts). The app may not have built correctly.');
889
- }
890
- }
891
- } catch (fetchErr) {
892
- const isAbort = fetchErr.name === 'AbortError';
893
- return {
894
- success: false,
895
- error: isAbort
896
- ? `Target app at ${appUrl} did not respond within 5 seconds. Make sure the dev server is running.`
897
- : `Cannot reach ${appUrl}: ${fetchErr.message}. Start the dev server first, then retry open-editor.`
898
- };
899
- }
900
-
901
- // Ensure visual editor server is running (no-op if already started at boot)
902
- const server = getVisualEditorServer();
903
- if (!server.isRunning) {
904
- await server.start();
905
- }
906
-
907
- // Get or create instance
908
- const instance = await bridge.getInstance(agentId, {
909
- appUrl,
910
- projectRoot: context.projectRoot || context.workingDirectory || context.projectDir
911
- });
912
-
913
- // Generate editor URL using configurable port
914
- const baseUrl = getVisualEditorBaseUrl();
915
- const editorUrl = `${baseUrl}?agentId=${agentId}&appUrl=${encodeURIComponent(appUrl)}`;
916
-
917
- // Update instance status
918
- bridge.updateStatus(agentId, instance.status, {
919
- editorUrl
920
- });
921
-
922
- // Broadcast to web-ui to open visual editor
923
- this._broadcastOpenEditor(context, {
924
- agentId,
925
- appUrl,
926
- editorUrl,
927
- sessionId: context.sessionId
928
- });
929
-
930
- const result = {
931
- success: true,
932
- message: `Visual Editor opening with ${appUrl}. The preview panel will appear in the UI.`,
933
- appUrl,
934
- editorUrl,
935
- instructions: 'Click elements in the preview to select them. You will receive context automatically.'
936
- };
937
-
938
- if (pageWarnings.length > 0) {
939
- result.warnings = pageWarnings;
940
- result.message += `\n\nWarnings detected on page:\n${pageWarnings.map(w => ` - ${w}`).join('\n')}`;
941
- }
942
-
943
- return result;
944
- }
945
-
946
- /**
947
- * Broadcast open-editor command to web-ui.
948
- *
949
- * Fan-out semantics: we deliberately pass `null` as sessionId so the
950
- * webServer's broadcastToSession falls through to its "broadcast to all
951
- * connections" path. Previously this used `context.sessionId ||
952
- * 'web-session'`, but `context.sessionId` is usually the agent's
953
- * scheduler session (`scheduler-session`) which doesn't match any UI
954
- * WebSocket connection, and the literal `'web-session'` fallback only
955
- * matches if a client happens to have that exact id. In both cases the
956
- * underlying broadcastToSession also falls back to "all clients", but
957
- * the path is log-noisy ("⚠ No connections for session") and semantically
958
- * misleading. The visual editor auto-open is inherently a UI-scoped
959
- * notification — we want every attached browser to hear it.
960
- * @private
961
- */
962
- _broadcastOpenEditor(context, data) {
963
- const message = {
964
- type: 'visual_editor_open',
965
- data: {
966
- agentId: data.agentId,
967
- appUrl: data.appUrl,
968
- editorUrl: data.editorUrl
969
- }
970
- };
971
-
972
- // Use plain console.log so the success path always prints even when
973
- // the tool was constructed without a logger (pre-logger-propagation
974
- // registries, test harnesses, future refactors).
975
- const log = (path) =>
976
- console.log(`[VisualEditorTool] Broadcast visual_editor_open via ${path}`, {
977
- agentId: data.agentId,
978
- appUrl: data.appUrl,
979
- });
980
-
981
- // Prefer the global webServer reference. Rationale:
982
- // - It's set exactly once, at boot (`global.loxiaWebServer = webServer`
983
- // in src/index.js after the server's own `initialize()` resolves).
984
- // - It bypasses any context-wiring subtlety. The tool's context can
985
- // be shaped differently depending on how the tool was invoked
986
- // (scheduler vs. direct orchestrator vs. flow executor), and
987
- // past outages have been "the context path USED to be populated,
988
- // then someone refactored the orchestrator setter order and the
989
- // inner path silently became undefined". The global dodges that.
990
- // - Verified live: the `/api/visual-editor/debug-broadcast` endpoint
991
- // uses the same webServer.broadcastToSession(null, ...) call and
992
- // delivers successfully every time.
993
- // The context-based paths are kept as belt-and-braces fallbacks for
994
- // any unusual execution environment (tests, possible future
995
- // embedded use) where `global` wasn't initialized.
996
- if (global.loxiaWebServer?.broadcastToSession) {
997
- global.loxiaWebServer.broadcastToSession(null, message);
998
- log('global.loxiaWebServer');
999
- return;
1000
- }
1001
-
1002
- if (context.agentPool?.messageProcessor?.orchestrator?.webServer?.broadcastToSession) {
1003
- context.agentPool.messageProcessor.orchestrator.webServer.broadcastToSession(null, message);
1004
- log('agentPool.messageProcessor.orchestrator.webServer');
1005
- return;
1006
- }
1007
-
1008
- if (context.orchestrator?.webServer?.broadcastToSession) {
1009
- context.orchestrator.webServer.broadcastToSession(null, message);
1010
- log('context.orchestrator.webServer');
1011
- return;
1012
- }
1013
-
1014
- console.warn('[VisualEditorTool] Could not broadcast open-editor command - no webServer access', {
1015
- hasContext: !!context,
1016
- hasAgentPool: !!context?.agentPool,
1017
- hasOrchestrator: !!context?.orchestrator,
1018
- hasGlobal: !!global.loxiaWebServer,
1019
- });
1020
- }
1021
-
1022
- // === Phase 5 Action Handlers ===
1023
-
1024
- /**
1025
- * Handle highlight action - highlight element in preview
1026
- * @private
1027
- */
1028
- _handleHighlight(bridge, agentId, params) {
1029
- if (!agentId) {
1030
- return {
1031
- success: false,
1032
- error: 'Agent ID is required for highlight action'
1033
- };
1034
- }
1035
-
1036
- // Check if connected
1037
- if (!bridge.isConnected(agentId)) {
1038
- return {
1039
- success: false,
1040
- error: 'Not connected to visual editor. Start the editor first.',
1041
- hint: 'Use set-app-url to configure and connect to the visual editor'
1042
- };
1043
- }
1044
-
1045
- const duration = params.duration || 2000;
1046
- const sent = bridge.highlightElement(agentId, params.selector, duration);
1047
-
1048
- return {
1049
- success: sent,
1050
- message: sent
1051
- ? `Highlighting "${params.selector}" for ${duration}ms`
1052
- : 'Failed to send highlight command',
1053
- selector: params.selector,
1054
- duration
1055
- };
1056
- }
1057
-
1058
- /**
1059
- * Handle scroll-to action - scroll to element in preview
1060
- * @private
1061
- */
1062
- _handleScrollTo(bridge, agentId, params) {
1063
- if (!agentId) {
1064
- return {
1065
- success: false,
1066
- error: 'Agent ID is required for scroll-to action'
1067
- };
1068
- }
1069
-
1070
- if (!bridge.isConnected(agentId)) {
1071
- return {
1072
- success: false,
1073
- error: 'Not connected to visual editor. Start the editor first.'
1074
- };
1075
- }
1076
-
1077
- const sent = bridge.scrollToElement(agentId, params.selector);
1078
-
1079
- return {
1080
- success: sent,
1081
- message: sent
1082
- ? `Scrolling to "${params.selector}"`
1083
- : 'Failed to send scroll command',
1084
- selector: params.selector
1085
- };
1086
- }
1087
-
1088
- /**
1089
- * Handle get-source action - get source code for selector
1090
- * @private
1091
- */
1092
- async _handleGetSource(bridge, agentId, params) {
1093
- if (!agentId) {
1094
- return {
1095
- success: false,
1096
- error: 'Agent ID is required for get-source action'
1097
- };
1098
- }
1099
-
1100
- // Get visual context for the selector
1101
- const context = bridge.getVisualContext(agentId);
1102
-
1103
- // If we have context with source hint, return it
1104
- if (context && context.sourceHint) {
1105
- const { file, line, component, codeSnippet, confidence } = context.sourceHint;
1106
-
1107
- return {
1108
- success: true,
1109
- selector: params.selector,
1110
- source: {
1111
- file,
1112
- line,
1113
- component,
1114
- codeSnippet,
1115
- confidence
1116
- },
1117
- message: file
1118
- ? `Source: ${file}:${line}${component ? ` (${component})` : ''}`
1119
- : 'Source location available in context'
1120
- };
1121
- }
1122
-
1123
- // No source hint available
1124
- return {
1125
- success: true,
1126
- selector: params.selector,
1127
- source: null,
1128
- message: 'No source information available for this selector. Try selecting the element in Visual Mode first.'
1129
- };
1130
- }
1131
-
1132
- /**
1133
- * Handle reload action - reload the preview
1134
- * @private
1135
- */
1136
- _handleReload(bridge, agentId) {
1137
- if (!agentId) {
1138
- return {
1139
- success: false,
1140
- error: 'Agent ID is required for reload action'
1141
- };
1142
- }
1143
-
1144
- if (!bridge.isConnected(agentId)) {
1145
- return {
1146
- success: false,
1147
- error: 'Not connected to visual editor. Start the editor first.'
1148
- };
1149
- }
1150
-
1151
- const sent = bridge.reloadPreview(agentId);
1152
-
1153
- return {
1154
- success: sent,
1155
- message: sent
1156
- ? 'Preview reload requested'
1157
- : 'Failed to send reload command'
1158
- };
1159
- }
1160
-
1161
- /**
1162
- * Handle set-mode action - switch editor mode
1163
- * @private
1164
- */
1165
- _handleSetMode(bridge, agentId, params) {
1166
- if (!agentId) {
1167
- return {
1168
- success: false,
1169
- error: 'Agent ID is required for set-mode action'
1170
- };
1171
- }
1172
-
1173
- if (!bridge.isConnected(agentId)) {
1174
- return {
1175
- success: false,
1176
- error: 'Not connected to visual editor. Start the editor first.'
1177
- };
1178
- }
1179
-
1180
- const mode = params.mode;
1181
- if (!['edit', 'preview'].includes(mode)) {
1182
- return {
1183
- success: false,
1184
- error: `Invalid mode: ${mode}. Must be 'edit' or 'preview'`
1185
- };
1186
- }
1187
-
1188
- const sent = bridge.setEditorMode(agentId, mode);
1189
-
1190
- return {
1191
- success: sent,
1192
- message: sent
1193
- ? `Editor mode set to '${mode}'`
1194
- : 'Failed to send mode command',
1195
- mode
1196
- };
1197
- }
1198
-
1199
- /**
1200
- * Format visual context for agent consumption
1201
- * @private
1202
- */
1203
- _formatContextForAgent(context) {
1204
- let formatted = `[VISUAL CONTEXT - User selected element]\n`;
1205
- formatted += `Element: <${context.tagName}`;
1206
-
1207
- // Add key attributes
1208
- if (context.attributes?.class) {
1209
- formatted += ` class="${context.attributes.class}"`;
1210
- }
1211
- if (context.attributes?.id) {
1212
- formatted += ` id="${context.attributes.id}"`;
1213
- }
1214
- formatted += '>\n';
1215
-
1216
- // Add CSS selector for precise targeting
1217
- if (context.selector) {
1218
- formatted += `Selector: ${context.selector}\n`;
1219
- }
1220
-
1221
- // Add text content if available
1222
- if (context.text) {
1223
- const truncated = context.text.length > 50
1224
- ? context.text.substring(0, 50) + '...'
1225
- : context.text;
1226
- formatted += `Text: "${truncated}"\n`;
1227
- }
1228
-
1229
- // Add source hint if available
1230
- if (context.sourceHint) {
1231
- const { file, fullPath, line, component, confidence } = context.sourceHint;
1232
-
1233
- // Use file path if available, fallback to fullPath
1234
- const filePath = file || fullPath;
1235
-
1236
- if (filePath) {
1237
- formatted += `Source: ${filePath}`;
1238
- if (line) {
1239
- formatted += `:${line}`;
1240
- }
1241
- if (component) {
1242
- formatted += ` (Component: ${component})`;
1243
- }
1244
- if (confidence === 'low') {
1245
- formatted += ' [VERIFY - low confidence]';
1246
- }
1247
- formatted += '\n';
1248
- } else if (component) {
1249
- // Even without file path, component name is useful
1250
- formatted += `Component: ${component}`;
1251
- if (confidence === 'low') {
1252
- formatted += ' [source file unknown]';
1253
- }
1254
- formatted += '\n';
1255
- }
1256
-
1257
- // Add code snippet if available
1258
- if (context.sourceHint.codeSnippet) {
1259
- formatted += 'Code:\n';
1260
- formatted += context.sourceHint.codeSnippet;
1261
- formatted += '\n';
1262
- }
1263
- } else {
1264
- // No React source info - indicate this is likely vanilla HTML/CSS
1265
- formatted += `Note: No React source mapping available (plain HTML element)\n`;
1266
- }
1267
-
1268
- return formatted;
1269
- }
1270
-
1271
- /**
1272
- * Inject visual context into user message
1273
- * Called by messageProcessor when visual context exists
1274
- * @param {string} userMessage - Original user message
1275
- * @param {Object} context - Visual context
1276
- * @returns {string} Enhanced message with visual context
1277
- */
1278
- static injectContextIntoMessage(userMessage, visualContext) {
1279
- if (!visualContext) {
1280
- return userMessage;
1281
- }
1282
-
1283
- const tool = new VisualEditorTool();
1284
- const formatted = tool._formatContextForAgent(visualContext);
1285
-
1286
- return `${formatted}\n[USER MESSAGE]\n${userMessage}`;
1287
- }
1288
- }
1289
-
1290
- export default VisualEditorTool;
1
+ /**
2
+ * @file tools/visualEditorTool.js
3
+ * @description Tool for visual editor integration - allows agents to interact
4
+ * with user's web application visually and receive element selections with
5
+ * source code locations.
6
+ *
7
+ * Phase 1: Basic actions (get-context, set-app-url, get-status)
8
+ * Phase 5: Full actions (highlight, scroll-to, get-source, reload, set-mode)
9
+ */
10
+
11
+ import { BaseTool } from './baseTool.js';
12
+ import { getVisualEditorBridge } from '../services/visualEditorBridge.js';
13
+ import { getVisualEditorServer, getVisualEditorBaseUrl } from '../services/visualEditorServer.js';
14
+ import { getPortTracker } from '../services/portTracker.js';
15
+ import { getProjectDetector } from '../services/projectDetector.js';
16
+
17
+ /**
18
+ * Supported actions for the Visual Editor Tool
19
+ */
20
+ const ACTIONS = {
21
+ GET_CONTEXT: 'get-context',
22
+ SET_APP_URL: 'set-app-url',
23
+ GET_STATUS: 'get-status',
24
+ CLEAR_CONTEXT: 'clear-context',
25
+ // Server management actions
26
+ START_SERVER: 'start-server', // Start dev server for project
27
+ SERVE_STATIC: 'serve-static', // Serve static files directly
28
+ DETECT_PROJECT: 'detect-project', // Detect project type
29
+ // UI control actions
30
+ OPEN_EDITOR: 'open-editor', // Open visual editor in UI with specified URL
31
+ // Phase 5 actions
32
+ HIGHLIGHT: 'highlight',
33
+ SCROLL_TO: 'scroll-to',
34
+ GET_SOURCE: 'get-source',
35
+ RELOAD: 'reload',
36
+ SET_MODE: 'set-mode'
37
+ };
38
+
39
+ /**
40
+ * VisualEditorTool - Interact with user's web application visually
41
+ */
42
+ export class VisualEditorTool extends BaseTool {
43
+ constructor(config = {}, logger = null) {
44
+ super(config, logger);
45
+
46
+ // Override tool ID
47
+ this.id = 'visual-editor';
48
+
49
+ // Tool properties
50
+ this.isAsync = false;
51
+ this.requiresProject = false;
52
+
53
+ // Bridge instance (lazy loaded)
54
+ this._bridge = null;
55
+ }
56
+
57
+ /**
58
+ * Get bridge instance (lazy load)
59
+ * @private
60
+ */
61
+ _getBridge() {
62
+ if (!this._bridge) {
63
+ this._bridge = getVisualEditorBridge();
64
+ }
65
+ return this._bridge;
66
+ }
67
+
68
+ /**
69
+ * Get tool description for agent system prompt
70
+ * @returns {string} Formatted tool description
71
+ */
72
+ getDescription() {
73
+ return `Tool: Visual Editor - Interact with the user's web application visually.
74
+
75
+ **PURPOSE:** Allow users to visually select elements in their app preview.
76
+ You receive context about selected elements including source code location.
77
+
78
+ **COMPLETE WORKFLOW - When user wants to visually edit their app:**
79
+
80
+ 1. **If app is NOT running yet:**
81
+ - Use \`detect-project\` to identify project type
82
+ - Use \`serve-static\` (for HTML) or start dev server via Terminal tool
83
+
84
+ 2. **Open the Visual Editor UI:**
85
+ - Use \`open-editor\` with the app URL to show the visual editor to the user
86
+ - This opens the preview panel in the UI automatically
87
+
88
+ 3. **User clicks elements in the preview**
89
+ - You automatically receive element context in messages
90
+
91
+ **ACTIONS:**
92
+
93
+ **open-editor** - Open visual editor UI with app URL (RECOMMENDED after starting server)
94
+ \`\`\`json
95
+ {
96
+ "toolId": "visual-editor",
97
+ "action": "open-editor",
98
+ "parameters": {
99
+ "url": "http://localhost:3000"
100
+ }
101
+ }
102
+ \`\`\`
103
+ This opens the visual editor panel in the user's UI and loads your app for visual selection.
104
+
105
+ **detect-project** - Detect project type and get server suggestions
106
+ \`\`\`json
107
+ {
108
+ "toolId": "visual-editor",
109
+ "action": "detect-project"
110
+ }
111
+ \`\`\`
112
+ Returns: projectType, framework, serverCommand, defaultPort
113
+
114
+ **serve-static** - Serve static HTML files directly (no external server needed)
115
+ \`\`\`json
116
+ {
117
+ "toolId": "visual-editor",
118
+ "action": "serve-static",
119
+ "parameters": {
120
+ "directory": "public" // Optional: subdirectory to serve, defaults to project root
121
+ }
122
+ }
123
+ \`\`\`
124
+ Returns: url (e.g., http://localhost:4000/)
125
+
126
+ **set-app-url** - Configure the app URL for visual editing
127
+ \`\`\`json
128
+ {
129
+ "toolId": "visual-editor",
130
+ "action": "set-app-url",
131
+ "parameters": {
132
+ "url": "http://localhost:3000"
133
+ }
134
+ }
135
+ \`\`\`
136
+
137
+ **get-context** - Get current visual selection (if any)
138
+ \`\`\`json
139
+ {
140
+ "toolId": "visual-editor",
141
+ "action": "get-context"
142
+ }
143
+ \`\`\`
144
+
145
+ **get-status** - Get visual editor instance status
146
+ \`\`\`json
147
+ {
148
+ "toolId": "visual-editor",
149
+ "action": "get-status"
150
+ }
151
+ \`\`\`
152
+
153
+ **clear-context** - Clear the current visual selection
154
+ \`\`\`json
155
+ {
156
+ "toolId": "visual-editor",
157
+ "action": "clear-context"
158
+ }
159
+ \`\`\`
160
+
161
+ **AUTOMATIC CONTEXT INJECTION:**
162
+ When user selects an element, you receive visual context automatically:
163
+ \`\`\`
164
+ [VISUAL CONTEXT - User selected element]
165
+ Element: <button class="btn-primary">
166
+ Source: src/components/Form.tsx:42 (Component: SubmitButton)
167
+ Code:
168
+ 41│ return (
169
+ ► 42│ <button className="btn-primary">Submit</button>
170
+ 43│ );
171
+ \`\`\`
172
+
173
+ **WORKFLOW EXAMPLE:**
174
+ 1. User: "Let me visually edit the app"
175
+ 2. Agent: detect-project → React (Vite)
176
+ 3. Agent: Terminal → npm run dev (starts on port 5173)
177
+ 4. Agent: open-editor → url: http://localhost:5173 (opens UI panel)
178
+ 5. Agent: "Visual editor ready! Click any element to select it."
179
+ 6. User clicks element → context injected automatically
180
+ 7. Agent: Uses filesystem tool to edit the source file
181
+
182
+ **TIPS:**
183
+ - For static HTML: Use \`serve-static\` then \`open-editor\`
184
+ - For React/Vue/Next.js: Start dev server via Terminal, then \`open-editor\`
185
+ - Always use \`open-editor\` to show the preview UI to the user
186
+ - Always acknowledge visual context when user selects element
187
+ - Use source location (file:line) to make targeted code changes`;
188
+ }
189
+
190
+ /**
191
+ * Get supported actions
192
+ * @returns {Array<string>} Supported action names
193
+ */
194
+ getSupportedActions() {
195
+ return Object.values(ACTIONS);
196
+ }
197
+
198
+ /**
199
+ * Get required parameters
200
+ * @returns {Array<string>} Required parameter names
201
+ */
202
+ getRequiredParameters() {
203
+ return ['action'];
204
+ }
205
+
206
+ /**
207
+ * Parse parameters from tool command content
208
+ * @param {string|Object} content - Raw content or parsed object
209
+ * @returns {Object} Parsed parameters
210
+ */
211
+ parseParameters(content) {
212
+ // Handle JSON format
213
+ if (typeof content === 'object' && content !== null) {
214
+ return this._parseJSONParams(content);
215
+ }
216
+
217
+ // Handle string format
218
+ if (typeof content === 'string') {
219
+ const trimmed = content.trim();
220
+
221
+ // Try to parse as JSON
222
+ if (trimmed.startsWith('{')) {
223
+ try {
224
+ const parsed = JSON.parse(trimmed);
225
+ return this._parseJSONParams(parsed);
226
+ } catch {
227
+ // Fall through to XML parsing
228
+ }
229
+ }
230
+
231
+ // Parse as XML
232
+ return this._parseXMLParams(content);
233
+ }
234
+
235
+ throw new Error('Invalid parameter format');
236
+ }
237
+
238
+ /**
239
+ * Parse JSON parameters
240
+ * @private
241
+ */
242
+ _parseJSONParams(obj) {
243
+ // Handle parameters wrapper
244
+ if (obj.parameters) {
245
+ return {
246
+ action: obj.action || ACTIONS.GET_CONTEXT,
247
+ ...obj.parameters
248
+ };
249
+ }
250
+
251
+ return {
252
+ action: obj.action || ACTIONS.GET_CONTEXT,
253
+ url: obj.url,
254
+ selector: obj.selector,
255
+ mode: obj.mode,
256
+ projectRoot: obj.projectRoot
257
+ };
258
+ }
259
+
260
+ /**
261
+ * Parse XML parameters
262
+ * @private
263
+ */
264
+ _parseXMLParams(content) {
265
+ const extractTag = (tag) => {
266
+ const regex = new RegExp(`<${tag}>([\\s\\S]*?)<\\/${tag}>`, 'i');
267
+ const match = regex.exec(content);
268
+ return match ? match[1].trim() : null;
269
+ };
270
+
271
+ return {
272
+ action: extractTag('action') || ACTIONS.GET_CONTEXT,
273
+ url: extractTag('url'),
274
+ selector: extractTag('selector'),
275
+ mode: extractTag('mode'),
276
+ projectRoot: extractTag('project-root') || extractTag('projectRoot')
277
+ };
278
+ }
279
+
280
+ /**
281
+ * Validate parameters
282
+ * @param {Object} params - Parameters to validate
283
+ * @returns {Object} Validation result
284
+ */
285
+ customValidateParameters(params) {
286
+ // Validate action
287
+ if (!Object.values(ACTIONS).includes(params.action)) {
288
+ return {
289
+ valid: false,
290
+ error: `Invalid action: ${params.action}. Valid actions: ${Object.values(ACTIONS).join(', ')}`
291
+ };
292
+ }
293
+
294
+ // Validate action-specific parameters
295
+ if (params.action === ACTIONS.SET_APP_URL && !params.url) {
296
+ return {
297
+ valid: false,
298
+ error: 'URL is required for set-app-url action'
299
+ };
300
+ }
301
+
302
+ // Phase 5 actions validation (placeholder)
303
+ if ([ACTIONS.HIGHLIGHT, ACTIONS.SCROLL_TO, ACTIONS.GET_SOURCE].includes(params.action)) {
304
+ if (!params.selector) {
305
+ return {
306
+ valid: false,
307
+ error: `Selector is required for ${params.action} action`
308
+ };
309
+ }
310
+ }
311
+
312
+ return { valid: true };
313
+ }
314
+
315
+ /**
316
+ * Execute tool with parsed parameters
317
+ * @param {Object|string} params - Parsed parameters or raw content
318
+ * @param {Object} context - Execution context
319
+ * @returns {Promise<Object>} Execution result
320
+ */
321
+ async execute(params, context = {}) {
322
+ // Reverse-forgiveness: accept plural {actions:[{...}]} envelope
323
+ // (cross-tool unification).
324
+ if (Array.isArray(params?.actions) && params.actions.length > 0) {
325
+ const a = params.actions[0] || {};
326
+ params = { ...params, ...a, action: a.type || a.action || params.action };
327
+ }
328
+
329
+ // FIRST THING: an unconditional console.log so we can always see that
330
+ // the tool was invoked — before any early-return (parse failure,
331
+ // bridge disabled, validation fail) or thrown error could swallow it.
332
+ // Earlier the log lived past a `bridge.isEnabled()` gate and was
333
+ // silently skipped whenever that gate tripped, making it look like
334
+ // the tool wasn't being called at all.
335
+ console.log('[VisualEditorTool] execute() called', {
336
+ paramsType: typeof params,
337
+ paramsAction: params?.action,
338
+ paramsUrl: params?.url,
339
+ agentId: context?.agentId,
340
+ hasGlobalWebServer: !!global.loxiaWebServer,
341
+ hasOrchestratorWebServer: !!context?.orchestrator?.webServer,
342
+ hasAgentPool: !!context?.agentPool,
343
+ });
344
+
345
+ try {
346
+ const { agentId } = context;
347
+
348
+ // Auto-parse if string
349
+ if (typeof params === 'string') {
350
+ params = this.parseParameters(params);
351
+ } else if (typeof params === 'object' && params !== null && !params.action) {
352
+ params = this.parseParameters(params);
353
+ }
354
+
355
+ // Get bridge
356
+ const bridge = this._getBridge();
357
+
358
+ // Check if bridge is enabled
359
+ if (!bridge.isEnabled()) {
360
+ console.warn('[VisualEditorTool] Early exit: bridge.isEnabled() === false');
361
+ return {
362
+ success: false,
363
+ error: 'Visual editor is disabled. Enable it in configuration.'
364
+ };
365
+ }
366
+
367
+ // Validate
368
+ const validation = this.customValidateParameters(params);
369
+ if (!validation.valid) {
370
+ console.warn('[VisualEditorTool] Early exit: validation failed', { action: params?.action, error: validation.error });
371
+ return {
372
+ success: false,
373
+ error: validation.error
374
+ };
375
+ }
376
+
377
+ // Confirmation that we reached action dispatch
378
+ console.log(`[VisualEditorTool] Action=${params.action} — about to dispatch`, {
379
+ agentId,
380
+ url: params.url,
381
+ });
382
+
383
+ // Route to action handler
384
+ switch (params.action) {
385
+ case ACTIONS.GET_CONTEXT:
386
+ return this._handleGetContext(bridge, agentId);
387
+
388
+ case ACTIONS.SET_APP_URL:
389
+ return this._handleSetAppUrl(bridge, agentId, params, context);
390
+
391
+ case ACTIONS.GET_STATUS:
392
+ return this._handleGetStatus(bridge, agentId);
393
+
394
+ case ACTIONS.CLEAR_CONTEXT:
395
+ return this._handleClearContext(bridge, agentId);
396
+
397
+ // Server management actions
398
+ case ACTIONS.DETECT_PROJECT:
399
+ return this._handleDetectProject(context);
400
+
401
+ case ACTIONS.SERVE_STATIC:
402
+ return this._handleServeStatic(bridge, agentId, params, context);
403
+
404
+ case ACTIONS.START_SERVER:
405
+ return this._handleStartServer(bridge, agentId, params, context);
406
+
407
+ // UI control actions
408
+ case ACTIONS.OPEN_EDITOR:
409
+ return this._handleOpenEditor(bridge, agentId, params, context);
410
+
411
+ // Phase 5 actions - full editor control
412
+ case ACTIONS.HIGHLIGHT:
413
+ return this._handleHighlight(bridge, agentId, params);
414
+
415
+ case ACTIONS.SCROLL_TO:
416
+ return this._handleScrollTo(bridge, agentId, params);
417
+
418
+ case ACTIONS.GET_SOURCE:
419
+ return this._handleGetSource(bridge, agentId, params);
420
+
421
+ case ACTIONS.RELOAD:
422
+ return this._handleReload(bridge, agentId);
423
+
424
+ case ACTIONS.SET_MODE:
425
+ return this._handleSetMode(bridge, agentId, params);
426
+
427
+ default:
428
+ return {
429
+ success: false,
430
+ error: `Unknown action: ${params.action}`
431
+ };
432
+ }
433
+
434
+ } catch (error) {
435
+ // Log full stack + action context so failures that happen BEFORE
436
+ // the broadcast fires (e.g., bridge.getInstance throws) are
437
+ // visible in the Node console. Previously the tool returned a
438
+ // clean error string to the agent and the human operator saw
439
+ // nothing — making "the editor never opens" undebuggable.
440
+ console.error(`[VisualEditorTool] Action=${params?.action} FAILED: ${error.message}`, {
441
+ agentId: context?.agentId,
442
+ action: params?.action,
443
+ stack: error.stack,
444
+ });
445
+ return {
446
+ success: false,
447
+ error: error.message
448
+ };
449
+ }
450
+ }
451
+
452
+ /**
453
+ * Handle get-context action
454
+ * @private
455
+ */
456
+ _handleGetContext(bridge, agentId) {
457
+ if (!agentId) {
458
+ return {
459
+ success: false,
460
+ error: 'Agent ID is required to get visual context'
461
+ };
462
+ }
463
+
464
+ const context = bridge.getVisualContext(agentId);
465
+
466
+ if (!context) {
467
+ return {
468
+ success: true,
469
+ hasContext: false,
470
+ message: 'No visual selection. User can select an element in Visual Mode.'
471
+ };
472
+ }
473
+
474
+ // Format context for agent consumption
475
+ return {
476
+ success: true,
477
+ hasContext: true,
478
+ context: {
479
+ selector: context.selector,
480
+ tagName: context.tagName,
481
+ text: context.text,
482
+ attributes: context.attributes,
483
+ sourceHint: context.sourceHint,
484
+ receivedAt: context.receivedAt
485
+ },
486
+ formatted: this._formatContextForAgent(context)
487
+ };
488
+ }
489
+
490
+ /**
491
+ * Handle set-app-url action
492
+ * @private
493
+ */
494
+ async _handleSetAppUrl(bridge, agentId, params, context) {
495
+ if (!agentId) {
496
+ return {
497
+ success: false,
498
+ error: 'Agent ID is required to set app URL'
499
+ };
500
+ }
501
+
502
+ // Validate URL format
503
+ try {
504
+ new URL(params.url);
505
+ } catch {
506
+ return {
507
+ success: false,
508
+ error: `Invalid URL format: ${params.url}`
509
+ };
510
+ }
511
+
512
+ // Get or create instance
513
+ const instance = await bridge.getInstance(agentId, {
514
+ appUrl: params.url,
515
+ projectRoot: params.projectRoot
516
+ });
517
+
518
+ // Generate editor URL and broadcast to web-ui to auto-open the visual editor panel
519
+ const baseUrl = getVisualEditorBaseUrl();
520
+ const editorUrl = `${baseUrl}?agentId=${agentId}&appUrl=${encodeURIComponent(params.url)}`;
521
+ this._broadcastOpenEditor(context, {
522
+ agentId,
523
+ appUrl: params.url,
524
+ editorUrl
525
+ });
526
+
527
+ return {
528
+ success: true,
529
+ message: `App URL configured: ${params.url}. Visual editor panel opening.`,
530
+ editorUrl,
531
+ instance: {
532
+ agentId: instance.agentId,
533
+ appUrl: instance.appUrl,
534
+ projectRoot: instance.projectRoot,
535
+ status: instance.status
536
+ }
537
+ };
538
+ }
539
+
540
+ /**
541
+ * Handle get-status action
542
+ * @private
543
+ */
544
+ _handleGetStatus(bridge, agentId) {
545
+ if (!agentId) {
546
+ // Return global status
547
+ const instances = bridge.listInstances();
548
+ return {
549
+ success: true,
550
+ instanceCount: instances.length,
551
+ maxInstances: bridge.maxInstances,
552
+ instances: instances.map(inst => ({
553
+ agentId: inst.agentId,
554
+ status: inst.status,
555
+ appUrl: inst.appUrl,
556
+ hasContext: inst.hasContext,
557
+ idleMs: inst.idleMs
558
+ }))
559
+ };
560
+ }
561
+
562
+ const status = bridge.getStatus(agentId);
563
+
564
+ if (!status.exists) {
565
+ return {
566
+ success: true,
567
+ exists: false,
568
+ message: 'No visual editor instance for this agent. Use set-app-url to configure.'
569
+ };
570
+ }
571
+
572
+ return {
573
+ success: true,
574
+ exists: true,
575
+ status: status.status,
576
+ appUrl: status.appUrl,
577
+ projectRoot: status.projectRoot,
578
+ editorUrl: status.editorUrl,
579
+ hasVisualContext: status.hasVisualContext,
580
+ subscriberCount: status.subscriberCount,
581
+ idleMs: status.idleMs,
582
+ error: status.error
583
+ };
584
+ }
585
+
586
+ /**
587
+ * Handle clear-context action
588
+ * @private
589
+ */
590
+ _handleClearContext(bridge, agentId) {
591
+ if (!agentId) {
592
+ return {
593
+ success: false,
594
+ error: 'Agent ID is required to clear visual context'
595
+ };
596
+ }
597
+
598
+ const cleared = bridge.clearVisualContext(agentId);
599
+
600
+ return {
601
+ success: true,
602
+ cleared,
603
+ message: cleared
604
+ ? 'Visual context cleared'
605
+ : 'No visual context to clear'
606
+ };
607
+ }
608
+
609
+ // === Server Management Action Handlers ===
610
+
611
+ /**
612
+ * Handle detect-project action
613
+ * @private
614
+ */
615
+ async _handleDetectProject(context) {
616
+ // Support all three names historically used by different callers:
617
+ // - projectRoot — legacy visual-editor context
618
+ // - workingDirectory — terminal/filesystem context
619
+ // - projectDir — messageProcessor's canonical name (src/core/messageProcessor.js)
620
+ // Without the third fallback, every call routed through messageProcessor
621
+ // (i.e., all agent-initiated ones) fails with "No project directory
622
+ // available" — leaving the editor only usable when invoked manually
623
+ // from a path where the context happens to carry the legacy keys.
624
+ const projectRoot = context.projectRoot || context.workingDirectory || context.projectDir;
625
+
626
+ if (!projectRoot) {
627
+ return {
628
+ success: false,
629
+ error: 'No project directory available. Set projectRoot in context.'
630
+ };
631
+ }
632
+
633
+ const detector = getProjectDetector();
634
+ const detection = await detector.detect(projectRoot);
635
+
636
+ if (detection.error) {
637
+ return {
638
+ success: false,
639
+ error: detection.error
640
+ };
641
+ }
642
+
643
+ const serverInfo = detector.getSuggestedServerCommand(detection);
644
+
645
+ return {
646
+ success: true,
647
+ projectDir: projectRoot,
648
+ projectType: detection.projectType,
649
+ framework: detection.framework,
650
+ isStatic: detection.isStatic,
651
+ entryPoints: detection.entryPoints,
652
+ serverCommand: serverInfo.command,
653
+ defaultPort: serverInfo.port,
654
+ availableScripts: detection.availableScripts,
655
+ confidence: detection.confidence,
656
+ message: detection.isStatic
657
+ ? `Static HTML project detected. Use 'serve-static' action to preview.`
658
+ : `${detection.framework || detection.projectType} project detected. Run "${serverInfo.command}" to start server.`
659
+ };
660
+ }
661
+
662
+ /**
663
+ * Handle serve-static action - serve static files via Visual Editor Server
664
+ * @private
665
+ */
666
+ async _handleServeStatic(bridge, agentId, params, context) {
667
+ // Support all three names historically used by different callers:
668
+ // - projectRoot — legacy visual-editor context
669
+ // - workingDirectory — terminal/filesystem context
670
+ // - projectDir — messageProcessor's canonical name (src/core/messageProcessor.js)
671
+ // Without the third fallback, every call routed through messageProcessor
672
+ // (i.e., all agent-initiated ones) fails with "No project directory
673
+ // available" — leaving the editor only usable when invoked manually
674
+ // from a path where the context happens to carry the legacy keys.
675
+ const projectRoot = context.projectRoot || context.workingDirectory || context.projectDir;
676
+
677
+ if (!projectRoot) {
678
+ return {
679
+ success: false,
680
+ error: 'No project directory available'
681
+ };
682
+ }
683
+
684
+ // Get Visual Editor Server
685
+ const server = getVisualEditorServer();
686
+
687
+ // Start server if not running
688
+ if (!server.isRunning) {
689
+ await server.start();
690
+ }
691
+
692
+ // Determine directory to serve
693
+ const directory = params.directory || '';
694
+ const fullPath = directory ? `${projectRoot}/${directory}` : projectRoot;
695
+
696
+ // Register static directory with the server
697
+ server.registerStaticDir(agentId, fullPath);
698
+
699
+ // Generate URL for static files using configurable port
700
+ const baseUrl = getVisualEditorBaseUrl();
701
+ const staticUrl = `${baseUrl}/static/${agentId}/`;
702
+
703
+ // Also set as app URL for the visual editor
704
+ if (agentId) {
705
+ const instance = await bridge.getInstance(agentId, {
706
+ appUrl: staticUrl,
707
+ projectRoot
708
+ });
709
+
710
+ bridge.updateStatus(agentId, instance.status, {
711
+ editorUrl: `${baseUrl}?agentId=${agentId}&appUrl=${encodeURIComponent(staticUrl)}`
712
+ });
713
+ }
714
+
715
+ const editorUrl = `${baseUrl}?agentId=${agentId}&appUrl=${encodeURIComponent(staticUrl)}`;
716
+
717
+ // Broadcast to web-ui to auto-open the visual editor panel
718
+ this._broadcastOpenEditor(context, {
719
+ agentId,
720
+ appUrl: staticUrl,
721
+ editorUrl
722
+ });
723
+
724
+ return {
725
+ success: true,
726
+ url: staticUrl,
727
+ directory: fullPath,
728
+ message: `Static files served at ${staticUrl}. Visual Mode ready.`,
729
+ editorUrl
730
+ };
731
+ }
732
+
733
+ /**
734
+ * Handle start-server action - provides server command info
735
+ * (Actual server start should be done via Terminal tool)
736
+ * @private
737
+ */
738
+ async _handleStartServer(bridge, agentId, params, context) {
739
+ // Support all three names historically used by different callers:
740
+ // - projectRoot — legacy visual-editor context
741
+ // - workingDirectory — terminal/filesystem context
742
+ // - projectDir — messageProcessor's canonical name (src/core/messageProcessor.js)
743
+ // Without the third fallback, every call routed through messageProcessor
744
+ // (i.e., all agent-initiated ones) fails with "No project directory
745
+ // available" — leaving the editor only usable when invoked manually
746
+ // from a path where the context happens to carry the legacy keys.
747
+ const projectRoot = context.projectRoot || context.workingDirectory || context.projectDir;
748
+
749
+ if (!projectRoot) {
750
+ return {
751
+ success: false,
752
+ error: 'No project directory available'
753
+ };
754
+ }
755
+
756
+ // Detect project
757
+ const detector = getProjectDetector();
758
+ const detection = await detector.detect(projectRoot);
759
+
760
+ // Get port tracker
761
+ const portTracker = getPortTracker();
762
+ const preferredPort = params.port || detection.defaultPort;
763
+
764
+ // Find available port
765
+ const availablePort = await portTracker.findAvailablePort(
766
+ preferredPort,
767
+ preferredPort + 100
768
+ );
769
+
770
+ if (!availablePort) {
771
+ return {
772
+ success: false,
773
+ error: `No available ports in range ${preferredPort}-${preferredPort + 100}`
774
+ };
775
+ }
776
+
777
+ // Get command with port
778
+ const serverInfo = detector.getSuggestedServerCommand(detection, availablePort);
779
+
780
+ return {
781
+ success: true,
782
+ projectType: detection.projectType,
783
+ framework: detection.framework,
784
+ command: serverInfo.command,
785
+ port: availablePort,
786
+ expectedUrl: `http://localhost:${availablePort}`,
787
+ instructions: `
788
+ To start the server:
789
+ 1. Use Terminal tool: ${serverInfo.command}
790
+ 2. Wait for server to start
791
+ 3. Use open-editor with url: http://localhost:${availablePort} (this will open the visual editor panel in the UI)
792
+
793
+ Or for static HTML, use 'serve-static' action instead (auto-opens the editor).
794
+ `.trim()
795
+ };
796
+ }
797
+
798
+ // === UI Control Action Handlers ===
799
+
800
+ /**
801
+ * Handle open-editor action - opens visual editor UI with specified app URL
802
+ * Broadcasts a message to the web-ui to enable visual editor mode
803
+ * @private
804
+ */
805
+ async _handleOpenEditor(bridge, agentId, params, context) {
806
+ if (!agentId) {
807
+ return {
808
+ success: false,
809
+ error: 'Agent ID is required to open visual editor'
810
+ };
811
+ }
812
+
813
+ // Validate URL format
814
+ const appUrl = params.url;
815
+ if (!appUrl) {
816
+ return {
817
+ success: false,
818
+ error: 'URL parameter is required. Provide the URL of your running app (e.g., http://localhost:3000)'
819
+ };
820
+ }
821
+
822
+ try {
823
+ new URL(appUrl);
824
+ } catch {
825
+ return {
826
+ success: false,
827
+ error: `Invalid URL format: ${appUrl}`
828
+ };
829
+ }
830
+
831
+ // Verify the target app is reachable and scan for errors
832
+ let pageWarnings = [];
833
+ try {
834
+ const controller = new AbortController();
835
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
836
+ const response = await fetch(appUrl, {
837
+ method: 'GET',
838
+ signal: controller.signal,
839
+ redirect: 'follow'
840
+ });
841
+ clearTimeout(timeoutId);
842
+
843
+ if (!response.ok && response.status >= 500) {
844
+ return {
845
+ success: false,
846
+ error: `Target app returned HTTP ${response.status} at ${appUrl}. Make sure the app is running and accessible.`
847
+ };
848
+ }
849
+
850
+ // Scan response body for common error patterns
851
+ const contentType = response.headers.get('content-type') || '';
852
+ if (contentType.includes('text/html') || contentType.includes('text/plain')) {
853
+ const body = await response.text();
854
+ const bodySnippet = body.slice(0, 4000); // Limit scan size
855
+
856
+ // Common build/runtime error patterns
857
+ const errorPatterns = [
858
+ { pattern: /Module not found|Cannot find module/i, msg: 'Module not found error detected' },
859
+ { pattern: /SyntaxError|Unexpected token/i, msg: 'JavaScript syntax error detected' },
860
+ { pattern: /TypeError|ReferenceError|RangeError/i, msg: 'JavaScript runtime error detected' },
861
+ { pattern: /ENOENT|EACCES|EADDRINUSE/i, msg: 'Node.js filesystem/port error detected' },
862
+ { pattern: /Cannot GET|Cannot POST|Not Found/i, msg: 'Route not found (404-like page)' },
863
+ { pattern: /Compilation failed|Build failed|Failed to compile/i, msg: 'Build/compilation error detected' },
864
+ { pattern: /Error: |ERROR |error occurred/i, msg: 'Error message found in page content' },
865
+ { pattern: /<pre class="error">/i, msg: 'Error overlay detected (dev server error page)' },
866
+ { pattern: /vite-error-overlay|react-error-overlay|nextjs-portal/i, msg: 'Framework error overlay detected' },
867
+ ];
868
+
869
+ for (const { pattern, msg } of errorPatterns) {
870
+ const match = bodySnippet.match(pattern);
871
+ if (match) {
872
+ // Extract surrounding context (up to 150 chars around the match)
873
+ const idx = bodySnippet.indexOf(match[0]);
874
+ const start = Math.max(0, idx - 50);
875
+ const end = Math.min(bodySnippet.length, idx + match[0].length + 100);
876
+ const context = bodySnippet.slice(start, end)
877
+ .replace(/<[^>]+>/g, ' ') // Strip HTML tags
878
+ .replace(/\s+/g, ' ') // Collapse whitespace
879
+ .trim();
880
+ pageWarnings.push(`${msg}: ...${context}...`);
881
+ }
882
+ }
883
+
884
+ // Check if page is essentially empty (broken build output)
885
+ const textContent = body.replace(/<[^>]+>/g, '').trim();
886
+ if (textContent.length < 10 && !body.includes('<script')) {
887
+ pageWarnings.push('Page appears empty (no content or scripts). The app may not have built correctly.');
888
+ }
889
+ }
890
+ } catch (fetchErr) {
891
+ const isAbort = fetchErr.name === 'AbortError';
892
+ return {
893
+ success: false,
894
+ error: isAbort
895
+ ? `Target app at ${appUrl} did not respond within 5 seconds. Make sure the dev server is running.`
896
+ : `Cannot reach ${appUrl}: ${fetchErr.message}. Start the dev server first, then retry open-editor.`
897
+ };
898
+ }
899
+
900
+ // Ensure visual editor server is running (no-op if already started at boot)
901
+ const server = getVisualEditorServer();
902
+ if (!server.isRunning) {
903
+ await server.start();
904
+ }
905
+
906
+ // Get or create instance
907
+ const instance = await bridge.getInstance(agentId, {
908
+ appUrl,
909
+ projectRoot: context.projectRoot || context.workingDirectory || context.projectDir
910
+ });
911
+
912
+ // Generate editor URL using configurable port
913
+ const baseUrl = getVisualEditorBaseUrl();
914
+ const editorUrl = `${baseUrl}?agentId=${agentId}&appUrl=${encodeURIComponent(appUrl)}`;
915
+
916
+ // Update instance status
917
+ bridge.updateStatus(agentId, instance.status, {
918
+ editorUrl
919
+ });
920
+
921
+ // Broadcast to web-ui to open visual editor
922
+ this._broadcastOpenEditor(context, {
923
+ agentId,
924
+ appUrl,
925
+ editorUrl,
926
+ sessionId: context.sessionId
927
+ });
928
+
929
+ const result = {
930
+ success: true,
931
+ message: `Visual Editor opening with ${appUrl}. The preview panel will appear in the UI.`,
932
+ appUrl,
933
+ editorUrl,
934
+ instructions: 'Click elements in the preview to select them. You will receive context automatically.'
935
+ };
936
+
937
+ if (pageWarnings.length > 0) {
938
+ result.warnings = pageWarnings;
939
+ result.message += `\n\nWarnings detected on page:\n${pageWarnings.map(w => ` - ${w}`).join('\n')}`;
940
+ }
941
+
942
+ return result;
943
+ }
944
+
945
+ /**
946
+ * Broadcast open-editor command to web-ui.
947
+ *
948
+ * Fan-out semantics: we deliberately pass `null` as sessionId so the
949
+ * webServer's broadcastToSession falls through to its "broadcast to all
950
+ * connections" path. Previously this used `context.sessionId ||
951
+ * 'web-session'`, but `context.sessionId` is usually the agent's
952
+ * scheduler session (`scheduler-session`) which doesn't match any UI
953
+ * WebSocket connection, and the literal `'web-session'` fallback only
954
+ * matches if a client happens to have that exact id. In both cases the
955
+ * underlying broadcastToSession also falls back to "all clients", but
956
+ * the path is log-noisy ("⚠ No connections for session") and semantically
957
+ * misleading. The visual editor auto-open is inherently a UI-scoped
958
+ * notification we want every attached browser to hear it.
959
+ * @private
960
+ */
961
+ _broadcastOpenEditor(context, data) {
962
+ const message = {
963
+ type: 'visual_editor_open',
964
+ data: {
965
+ agentId: data.agentId,
966
+ appUrl: data.appUrl,
967
+ editorUrl: data.editorUrl
968
+ }
969
+ };
970
+
971
+ // Use plain console.log so the success path always prints even when
972
+ // the tool was constructed without a logger (pre-logger-propagation
973
+ // registries, test harnesses, future refactors).
974
+ const log = (path) =>
975
+ console.log(`[VisualEditorTool] Broadcast visual_editor_open via ${path}`, {
976
+ agentId: data.agentId,
977
+ appUrl: data.appUrl,
978
+ });
979
+
980
+ // Prefer the global webServer reference. Rationale:
981
+ // - It's set exactly once, at boot (`global.loxiaWebServer = webServer`
982
+ // in src/index.js after the server's own `initialize()` resolves).
983
+ // - It bypasses any context-wiring subtlety. The tool's context can
984
+ // be shaped differently depending on how the tool was invoked
985
+ // (scheduler vs. direct orchestrator vs. flow executor), and
986
+ // past outages have been "the context path USED to be populated,
987
+ // then someone refactored the orchestrator setter order and the
988
+ // inner path silently became undefined". The global dodges that.
989
+ // - Verified live: the `/api/visual-editor/debug-broadcast` endpoint
990
+ // uses the same webServer.broadcastToSession(null, ...) call and
991
+ // delivers successfully every time.
992
+ // The context-based paths are kept as belt-and-braces fallbacks for
993
+ // any unusual execution environment (tests, possible future
994
+ // embedded use) where `global` wasn't initialized.
995
+ if (global.loxiaWebServer?.broadcastToSession) {
996
+ global.loxiaWebServer.broadcastToSession(null, message);
997
+ log('global.loxiaWebServer');
998
+ return;
999
+ }
1000
+
1001
+ if (context.agentPool?.messageProcessor?.orchestrator?.webServer?.broadcastToSession) {
1002
+ context.agentPool.messageProcessor.orchestrator.webServer.broadcastToSession(null, message);
1003
+ log('agentPool.messageProcessor.orchestrator.webServer');
1004
+ return;
1005
+ }
1006
+
1007
+ if (context.orchestrator?.webServer?.broadcastToSession) {
1008
+ context.orchestrator.webServer.broadcastToSession(null, message);
1009
+ log('context.orchestrator.webServer');
1010
+ return;
1011
+ }
1012
+
1013
+ console.warn('[VisualEditorTool] Could not broadcast open-editor command - no webServer access', {
1014
+ hasContext: !!context,
1015
+ hasAgentPool: !!context?.agentPool,
1016
+ hasOrchestrator: !!context?.orchestrator,
1017
+ hasGlobal: !!global.loxiaWebServer,
1018
+ });
1019
+ }
1020
+
1021
+ // === Phase 5 Action Handlers ===
1022
+
1023
+ /**
1024
+ * Handle highlight action - highlight element in preview
1025
+ * @private
1026
+ */
1027
+ _handleHighlight(bridge, agentId, params) {
1028
+ if (!agentId) {
1029
+ return {
1030
+ success: false,
1031
+ error: 'Agent ID is required for highlight action'
1032
+ };
1033
+ }
1034
+
1035
+ // Check if connected
1036
+ if (!bridge.isConnected(agentId)) {
1037
+ return {
1038
+ success: false,
1039
+ error: 'Not connected to visual editor. Start the editor first.',
1040
+ hint: 'Use set-app-url to configure and connect to the visual editor'
1041
+ };
1042
+ }
1043
+
1044
+ const duration = params.duration || 2000;
1045
+ const sent = bridge.highlightElement(agentId, params.selector, duration);
1046
+
1047
+ return {
1048
+ success: sent,
1049
+ message: sent
1050
+ ? `Highlighting "${params.selector}" for ${duration}ms`
1051
+ : 'Failed to send highlight command',
1052
+ selector: params.selector,
1053
+ duration
1054
+ };
1055
+ }
1056
+
1057
+ /**
1058
+ * Handle scroll-to action - scroll to element in preview
1059
+ * @private
1060
+ */
1061
+ _handleScrollTo(bridge, agentId, params) {
1062
+ if (!agentId) {
1063
+ return {
1064
+ success: false,
1065
+ error: 'Agent ID is required for scroll-to action'
1066
+ };
1067
+ }
1068
+
1069
+ if (!bridge.isConnected(agentId)) {
1070
+ return {
1071
+ success: false,
1072
+ error: 'Not connected to visual editor. Start the editor first.'
1073
+ };
1074
+ }
1075
+
1076
+ const sent = bridge.scrollToElement(agentId, params.selector);
1077
+
1078
+ return {
1079
+ success: sent,
1080
+ message: sent
1081
+ ? `Scrolling to "${params.selector}"`
1082
+ : 'Failed to send scroll command',
1083
+ selector: params.selector
1084
+ };
1085
+ }
1086
+
1087
+ /**
1088
+ * Handle get-source action - get source code for selector
1089
+ * @private
1090
+ */
1091
+ async _handleGetSource(bridge, agentId, params) {
1092
+ if (!agentId) {
1093
+ return {
1094
+ success: false,
1095
+ error: 'Agent ID is required for get-source action'
1096
+ };
1097
+ }
1098
+
1099
+ // Get visual context for the selector
1100
+ const context = bridge.getVisualContext(agentId);
1101
+
1102
+ // If we have context with source hint, return it
1103
+ if (context && context.sourceHint) {
1104
+ const { file, line, component, codeSnippet, confidence } = context.sourceHint;
1105
+
1106
+ return {
1107
+ success: true,
1108
+ selector: params.selector,
1109
+ source: {
1110
+ file,
1111
+ line,
1112
+ component,
1113
+ codeSnippet,
1114
+ confidence
1115
+ },
1116
+ message: file
1117
+ ? `Source: ${file}:${line}${component ? ` (${component})` : ''}`
1118
+ : 'Source location available in context'
1119
+ };
1120
+ }
1121
+
1122
+ // No source hint available
1123
+ return {
1124
+ success: true,
1125
+ selector: params.selector,
1126
+ source: null,
1127
+ message: 'No source information available for this selector. Try selecting the element in Visual Mode first.'
1128
+ };
1129
+ }
1130
+
1131
+ /**
1132
+ * Handle reload action - reload the preview
1133
+ * @private
1134
+ */
1135
+ _handleReload(bridge, agentId) {
1136
+ if (!agentId) {
1137
+ return {
1138
+ success: false,
1139
+ error: 'Agent ID is required for reload action'
1140
+ };
1141
+ }
1142
+
1143
+ if (!bridge.isConnected(agentId)) {
1144
+ return {
1145
+ success: false,
1146
+ error: 'Not connected to visual editor. Start the editor first.'
1147
+ };
1148
+ }
1149
+
1150
+ const sent = bridge.reloadPreview(agentId);
1151
+
1152
+ return {
1153
+ success: sent,
1154
+ message: sent
1155
+ ? 'Preview reload requested'
1156
+ : 'Failed to send reload command'
1157
+ };
1158
+ }
1159
+
1160
+ /**
1161
+ * Handle set-mode action - switch editor mode
1162
+ * @private
1163
+ */
1164
+ _handleSetMode(bridge, agentId, params) {
1165
+ if (!agentId) {
1166
+ return {
1167
+ success: false,
1168
+ error: 'Agent ID is required for set-mode action'
1169
+ };
1170
+ }
1171
+
1172
+ if (!bridge.isConnected(agentId)) {
1173
+ return {
1174
+ success: false,
1175
+ error: 'Not connected to visual editor. Start the editor first.'
1176
+ };
1177
+ }
1178
+
1179
+ const mode = params.mode;
1180
+ if (!['edit', 'preview'].includes(mode)) {
1181
+ return {
1182
+ success: false,
1183
+ error: `Invalid mode: ${mode}. Must be 'edit' or 'preview'`
1184
+ };
1185
+ }
1186
+
1187
+ const sent = bridge.setEditorMode(agentId, mode);
1188
+
1189
+ return {
1190
+ success: sent,
1191
+ message: sent
1192
+ ? `Editor mode set to '${mode}'`
1193
+ : 'Failed to send mode command',
1194
+ mode
1195
+ };
1196
+ }
1197
+
1198
+ /**
1199
+ * Format visual context for agent consumption
1200
+ * @private
1201
+ */
1202
+ _formatContextForAgent(context) {
1203
+ let formatted = `[VISUAL CONTEXT - User selected element]\n`;
1204
+ formatted += `Element: <${context.tagName}`;
1205
+
1206
+ // Add key attributes
1207
+ if (context.attributes?.class) {
1208
+ formatted += ` class="${context.attributes.class}"`;
1209
+ }
1210
+ if (context.attributes?.id) {
1211
+ formatted += ` id="${context.attributes.id}"`;
1212
+ }
1213
+ formatted += '>\n';
1214
+
1215
+ // Add CSS selector for precise targeting
1216
+ if (context.selector) {
1217
+ formatted += `Selector: ${context.selector}\n`;
1218
+ }
1219
+
1220
+ // Add text content if available
1221
+ if (context.text) {
1222
+ const truncated = context.text.length > 50
1223
+ ? context.text.substring(0, 50) + '...'
1224
+ : context.text;
1225
+ formatted += `Text: "${truncated}"\n`;
1226
+ }
1227
+
1228
+ // Add source hint if available
1229
+ if (context.sourceHint) {
1230
+ const { file, fullPath, line, component, confidence } = context.sourceHint;
1231
+
1232
+ // Use file path if available, fallback to fullPath
1233
+ const filePath = file || fullPath;
1234
+
1235
+ if (filePath) {
1236
+ formatted += `Source: ${filePath}`;
1237
+ if (line) {
1238
+ formatted += `:${line}`;
1239
+ }
1240
+ if (component) {
1241
+ formatted += ` (Component: ${component})`;
1242
+ }
1243
+ if (confidence === 'low') {
1244
+ formatted += ' [VERIFY - low confidence]';
1245
+ }
1246
+ formatted += '\n';
1247
+ } else if (component) {
1248
+ // Even without file path, component name is useful
1249
+ formatted += `Component: ${component}`;
1250
+ if (confidence === 'low') {
1251
+ formatted += ' [source file unknown]';
1252
+ }
1253
+ formatted += '\n';
1254
+ }
1255
+
1256
+ // Add code snippet if available
1257
+ if (context.sourceHint.codeSnippet) {
1258
+ formatted += 'Code:\n';
1259
+ formatted += context.sourceHint.codeSnippet;
1260
+ formatted += '\n';
1261
+ }
1262
+ } else {
1263
+ // No React source info - indicate this is likely vanilla HTML/CSS
1264
+ formatted += `Note: No React source mapping available (plain HTML element)\n`;
1265
+ }
1266
+
1267
+ return formatted;
1268
+ }
1269
+
1270
+ /**
1271
+ * Inject visual context into user message
1272
+ * Called by messageProcessor when visual context exists
1273
+ * @param {string} userMessage - Original user message
1274
+ * @param {Object} context - Visual context
1275
+ * @returns {string} Enhanced message with visual context
1276
+ */
1277
+ static injectContextIntoMessage(userMessage, visualContext) {
1278
+ if (!visualContext) {
1279
+ return userMessage;
1280
+ }
1281
+
1282
+ const tool = new VisualEditorTool();
1283
+ const formatted = tool._formatContextForAgent(visualContext);
1284
+
1285
+ return `${formatted}\n[USER MESSAGE]\n${userMessage}`;
1286
+ }
1287
+ }
1288
+
1289
+ export default VisualEditorTool;