onbuzz 3.4.0 → 3.6.2

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 (562) hide show
  1. package/package.json +1 -1
  2. package/scripts/bump-version.js +116 -0
  3. package/src/__test-utils__/fixtures/malformedJson.js +31 -0
  4. package/src/__test-utils__/globalSetup.js +9 -0
  5. package/src/__test-utils__/globalTeardown.js +12 -0
  6. package/src/__test-utils__/mockFactories.js +101 -0
  7. package/src/analyzers/__tests__/CSSAnalyzer.test.js +41 -0
  8. package/src/analyzers/__tests__/ConfigValidator.test.js +362 -0
  9. package/src/analyzers/__tests__/ESLintAnalyzer.test.js +271 -0
  10. package/src/analyzers/__tests__/JavaScriptAnalyzer.test.js +40 -0
  11. package/src/analyzers/__tests__/PrettierFormatter.test.js +197 -0
  12. package/src/analyzers/__tests__/PythonAnalyzer.test.js +208 -0
  13. package/src/analyzers/__tests__/SecurityAnalyzer.test.js +303 -0
  14. package/src/analyzers/__tests__/SparrowAnalyzer.test.js +270 -0
  15. package/src/analyzers/__tests__/TypeScriptAnalyzer.test.js +187 -0
  16. package/src/core/__tests__/agentPool.test.js +601 -0
  17. package/src/core/__tests__/agentScheduler.test.js +576 -0
  18. package/src/core/__tests__/contextManager.test.js +252 -0
  19. package/src/core/__tests__/flowExecutor.test.js +262 -0
  20. package/src/core/__tests__/messageProcessor.test.js +627 -0
  21. package/src/core/__tests__/orchestrator.test.js +257 -0
  22. package/src/core/__tests__/stateManager.test.js +375 -0
  23. package/src/core/agentPool.js +26 -4
  24. package/src/core/agentScheduler.js +79 -21
  25. package/src/core/messageProcessor.js +110 -2
  26. package/src/index.js +27 -11
  27. package/src/interfaces/__tests__/imageServing.test.js +228 -0
  28. package/src/interfaces/terminal/__tests__/smoke/imports.test.js +3 -5
  29. package/src/interfaces/webServer.js +97 -13
  30. package/src/services/__tests__/agentActivityService.test.js +319 -0
  31. package/src/services/__tests__/apiKeyManager.test.js +206 -0
  32. package/src/services/__tests__/benchmarkService.test.js +184 -0
  33. package/src/services/__tests__/budgetService.test.js +211 -0
  34. package/src/services/__tests__/contextInjectionService.test.js +205 -0
  35. package/src/services/__tests__/conversationCompactionService.test.js +280 -0
  36. package/src/services/__tests__/credentialVault.test.js +469 -0
  37. package/src/services/__tests__/errorHandler.test.js +314 -0
  38. package/src/services/__tests__/fileAttachmentService.test.js +278 -0
  39. package/src/services/__tests__/flowContextService.test.js +199 -0
  40. package/src/services/__tests__/memoryService.test.js +450 -0
  41. package/src/services/__tests__/modelRouterService.test.js +388 -0
  42. package/src/services/__tests__/modelsService.test.js +261 -0
  43. package/src/services/__tests__/portRegistry.test.js +123 -0
  44. package/src/services/__tests__/projectDetector.test.js +34 -0
  45. package/src/services/__tests__/promptService.test.js +242 -0
  46. package/src/services/__tests__/qualityInspector.test.js +97 -0
  47. package/src/services/__tests__/scheduleService.test.js +308 -0
  48. package/src/services/__tests__/serviceRegistry.test.js +74 -0
  49. package/src/services/__tests__/skillsService.test.js +402 -0
  50. package/src/services/__tests__/tokenCountingService.test.js +48 -0
  51. package/src/services/conversationCompactionService.js +2 -2
  52. package/src/services/visualEditorServer.js +26 -7
  53. package/src/tools/__tests__/agentCommunicationTool.test.js +500 -0
  54. package/src/tools/__tests__/agentDelayTool.test.js +342 -0
  55. package/src/tools/__tests__/asyncToolManager.test.js +344 -0
  56. package/src/tools/__tests__/baseTool.test.js +420 -0
  57. package/src/tools/__tests__/codeMapTool.test.js +348 -0
  58. package/src/tools/__tests__/fileContentReplaceTool.test.js +309 -0
  59. package/src/tools/__tests__/fileTreeTool.test.js +274 -0
  60. package/src/tools/__tests__/filesystemTool.test.js +717 -0
  61. package/src/tools/__tests__/helpTool.test.js +204 -0
  62. package/src/tools/__tests__/jobDoneTool.test.js +296 -0
  63. package/src/tools/__tests__/memoryTool.test.js +297 -0
  64. package/src/tools/__tests__/seekTool.test.js +282 -0
  65. package/src/tools/__tests__/skillsTool.test.js +226 -0
  66. package/src/tools/__tests__/staticAnalysisTool.test.js +509 -0
  67. package/src/tools/__tests__/taskManagerTool.test.js +725 -0
  68. package/src/tools/__tests__/terminalTool.test.js +384 -0
  69. package/src/tools/__tests__/userPromptTool.test.js +297 -0
  70. package/src/tools/__tests__/webTool.e2e.test.js +25 -11
  71. package/src/tools/imageTool.js +41 -5
  72. package/src/tools/webTool.js +161 -48
  73. package/src/types/__tests__/agent.test.js +499 -0
  74. package/src/types/__tests__/contextReference.test.js +606 -0
  75. package/src/types/__tests__/conversation.test.js +555 -0
  76. package/src/types/__tests__/toolCommand.test.js +584 -0
  77. package/src/types/contextReference.js +1 -1
  78. package/src/utilities/__tests__/attachmentValidator.test.js +80 -0
  79. package/src/utilities/__tests__/configManager.test.js +397 -0
  80. package/src/utilities/__tests__/constants.test.js +49 -0
  81. package/src/utilities/__tests__/directoryAccessManager.test.js +388 -0
  82. package/src/utilities/__tests__/fileProcessor.test.js +104 -0
  83. package/src/utilities/__tests__/jsonRepair.test.js +104 -0
  84. package/src/utilities/__tests__/logger.test.js +129 -0
  85. package/src/utilities/__tests__/platformUtils.test.js +87 -0
  86. package/src/utilities/__tests__/structuredFileValidator.test.js +263 -0
  87. package/src/utilities/__tests__/tagParser.test.js +887 -0
  88. package/src/utilities/__tests__/toolConstants.test.js +94 -0
  89. package/src/utilities/tagParser.js +2 -2
  90. package/web-ui/build/index.html +2 -2
  91. package/web-ui/build/static/1c-8PZzOTzp.js +1 -0
  92. package/web-ui/build/static/abap-Bcx_Au1F.js +1 -0
  93. package/web-ui/build/static/abnf-BKTLqpWA.js +1 -0
  94. package/web-ui/build/static/abnf-J05BAvJt.js +1 -0
  95. package/web-ui/build/static/accesslog-Cp8_lqVY.js +1 -0
  96. package/web-ui/build/static/actionscript-BK0UaMrm.js +1 -0
  97. package/web-ui/build/static/actionscript-CyqZUddh.js +1 -0
  98. package/web-ui/build/static/ada-BNirS6Nr.js +1 -0
  99. package/web-ui/build/static/ada-BSFWcT1O.js +1 -0
  100. package/web-ui/build/static/agda-D0NJDJg7.js +1 -0
  101. package/web-ui/build/static/al-rWARKtwb.js +1 -0
  102. package/web-ui/build/static/angelscript-fCehtOYk.js +1 -0
  103. package/web-ui/build/static/antlr4-Dn9jrnZN.js +1 -0
  104. package/web-ui/build/static/apache-DaQCsvNW.js +1 -0
  105. package/web-ui/build/static/apacheconf-dY4i0Xvz.js +1 -0
  106. package/web-ui/build/static/apex-vhS4SI46.js +1 -0
  107. package/web-ui/build/static/apl-CKRkxH90.js +1 -0
  108. package/web-ui/build/static/applescript-CWmpQIEB.js +1 -0
  109. package/web-ui/build/static/applescript-DBaX7Uqo.js +1 -0
  110. package/web-ui/build/static/aql-8s41lrIa.js +1 -0
  111. package/web-ui/build/static/arcade-w2_RhAcq.js +1 -0
  112. package/web-ui/build/static/arduino-I7BtZTu6.js +1 -0
  113. package/web-ui/build/static/arduino-h2LZErKQ.js +1 -0
  114. package/web-ui/build/static/arff-C543-5a1.js +1 -0
  115. package/web-ui/build/static/armasm-DyZdFOzz.js +1 -0
  116. package/web-ui/build/static/asciidoc-ZzENlACu.js +1 -0
  117. package/web-ui/build/static/asciidoc-_j9x9bUz.js +1 -0
  118. package/web-ui/build/static/asm6502-CsNsmBfq.js +1 -0
  119. package/web-ui/build/static/asmatmel-CkIVf_tD.js +1 -0
  120. package/web-ui/build/static/aspectj-C6AQLme_.js +1 -0
  121. package/web-ui/build/static/aspnet-5AkdiVyL.js +1 -0
  122. package/web-ui/build/static/autohotkey-BRZVABiS.js +1 -0
  123. package/web-ui/build/static/autohotkey-DVTmfk_f.js +1 -0
  124. package/web-ui/build/static/autoit-3UEcWu5a.js +1 -0
  125. package/web-ui/build/static/autoit-BDByIKSH.js +1 -0
  126. package/web-ui/build/static/avisynth-BHc4uUkP.js +1 -0
  127. package/web-ui/build/static/avrasm-BAPq8_aI.js +1 -0
  128. package/web-ui/build/static/avro-idl-BKEBYUtv.js +1 -0
  129. package/web-ui/build/static/awk-CBCkArRT.js +1 -0
  130. package/web-ui/build/static/axapta-DlOgnXSZ.js +1 -0
  131. package/web-ui/build/static/bash-C6Brp5OE.js +1 -0
  132. package/web-ui/build/static/bash-DkEO7JRq.js +1 -0
  133. package/web-ui/build/static/basic-DG6TYB0R.js +1 -0
  134. package/web-ui/build/static/basic-DRPcNfAn.js +1 -0
  135. package/web-ui/build/static/batch-DdjZ5KC1.js +1 -0
  136. package/web-ui/build/static/bbcode-DCXEEs2w.js +1 -0
  137. package/web-ui/build/static/bicep-CpLhfOwt.js +1 -0
  138. package/web-ui/build/static/birb-DNWkqgQm.js +1 -0
  139. package/web-ui/build/static/bison-DwxbQHJ9.js +1 -0
  140. package/web-ui/build/static/bnf-Cgnt7npj.js +1 -0
  141. package/web-ui/build/static/bnf-DSTq_eu9.js +1 -0
  142. package/web-ui/build/static/brainfuck-Bi8mGutW.js +1 -0
  143. package/web-ui/build/static/brainfuck-DOWfqVtR.js +1 -0
  144. package/web-ui/build/static/brightscript-D95pbP-v.js +1 -0
  145. package/web-ui/build/static/bro-BrDVwXeg.js +1 -0
  146. package/web-ui/build/static/bsl-BMoXI84g.js +1 -0
  147. package/web-ui/build/static/c-CKH4C7-Z.js +1 -0
  148. package/web-ui/build/static/c-Z0txyaeJ.js +1 -0
  149. package/web-ui/build/static/c-like-Dzm9dMmR.js +1 -0
  150. package/web-ui/build/static/cal-DoyAwiUt.js +1 -0
  151. package/web-ui/build/static/capnproto-DeIi9LOH.js +1 -0
  152. package/web-ui/build/static/ceylon-Coim6DIe.js +1 -0
  153. package/web-ui/build/static/cfscript-CwsndC-j.js +1 -0
  154. package/web-ui/build/static/chaiscript-D6Aq-PSv.js +1 -0
  155. package/web-ui/build/static/cil-vi56VRk_.js +1 -0
  156. package/web-ui/build/static/clean-BfpKrTdp.js +1 -0
  157. package/web-ui/build/static/clojure-DUtl6BaB.js +1 -0
  158. package/web-ui/build/static/clojure-DXJHtDlY.js +1 -0
  159. package/web-ui/build/static/clojure-repl-BxwP5C3g.js +1 -0
  160. package/web-ui/build/static/cmake-C9_VZ1vH.js +1 -0
  161. package/web-ui/build/static/cmake-dplO-PGD.js +1 -0
  162. package/web-ui/build/static/cobol-DsZhu02V.js +1 -0
  163. package/web-ui/build/static/coffeescript-Cw9jtGNP.js +1 -0
  164. package/web-ui/build/static/coffeescript-DvDt4T2l.js +1 -0
  165. package/web-ui/build/static/concurnas-Bzc_Dcdd.js +1 -0
  166. package/web-ui/build/static/coq-Cv-5BqGo.js +1 -0
  167. package/web-ui/build/static/coq-DWFe2ssK.js +1 -0
  168. package/web-ui/build/static/cos-D6Lc6Cah.js +1 -0
  169. package/web-ui/build/static/cpp-BFmLjd76.js +1 -0
  170. package/web-ui/build/static/cpp-DVQgbHji.js +1 -0
  171. package/web-ui/build/static/crmsh-Cqveth9p.js +1 -0
  172. package/web-ui/build/static/crystal-0syYaH4Y.js +1 -0
  173. package/web-ui/build/static/crystal-Noptp-kr.js +1 -0
  174. package/web-ui/build/static/csharp-B799cFMH.js +1 -0
  175. package/web-ui/build/static/csharp-_HlvMZzJ.js +1 -0
  176. package/web-ui/build/static/cshtml-CnwOXlhP.js +1 -0
  177. package/web-ui/build/static/csp-1ffxIG_-.js +1 -0
  178. package/web-ui/build/static/csp-Bws60bPu.js +1 -0
  179. package/web-ui/build/static/css-BGdwXzpm.js +1 -0
  180. package/web-ui/build/static/css-extras-DZCECiOa.js +1 -0
  181. package/web-ui/build/static/csv-FMFGT0T4.js +1 -0
  182. package/web-ui/build/static/cypher-DnXoEwRp.js +1 -0
  183. package/web-ui/build/static/d-CBrts1xB.js +1 -0
  184. package/web-ui/build/static/d-qrJLxk2L.js +1 -0
  185. package/web-ui/build/static/dart-3vBSXJVV.js +1 -0
  186. package/web-ui/build/static/dart-Cj5b7BV9.js +1 -0
  187. package/web-ui/build/static/dataweave-BuFf63rk.js +1 -0
  188. package/web-ui/build/static/dax-Cl-se1JI.js +1 -0
  189. package/web-ui/build/static/delphi-CLkRb26y.js +1 -0
  190. package/web-ui/build/static/dhall--TIL2Z--.js +1 -0
  191. package/web-ui/build/static/diff-Bn-XL2om.js +1 -0
  192. package/web-ui/build/static/diff-BsTwly4w.js +1 -0
  193. package/web-ui/build/static/django-BfRtHnTS.js +1 -0
  194. package/web-ui/build/static/django-Dm9O4e3A.js +1 -0
  195. package/web-ui/build/static/dns-BIVEp3uD.js +1 -0
  196. package/web-ui/build/static/dns-zone-file-CO7LnOdh.js +1 -0
  197. package/web-ui/build/static/docker-BhwMip1R.js +1 -0
  198. package/web-ui/build/static/dockerfile-8Tjw9_jF.js +1 -0
  199. package/web-ui/build/static/dos-CRMiAo46.js +1 -0
  200. package/web-ui/build/static/dot-DPpW7LrJ.js +1 -0
  201. package/web-ui/build/static/dsconfig-D0zbYilI.js +1 -0
  202. package/web-ui/build/static/dts-C_-yqWEL.js +1 -0
  203. package/web-ui/build/static/dust-CucJgqnE.js +1 -0
  204. package/web-ui/build/static/ebnf-CCHK0H6j.js +1 -0
  205. package/web-ui/build/static/ebnf-CDdAcveH.js +1 -0
  206. package/web-ui/build/static/editorconfig-f-5b95UM.js +1 -0
  207. package/web-ui/build/static/eiffel-MmghFce7.js +1 -0
  208. package/web-ui/build/static/ejs-CPMN4-jk.js +1 -0
  209. package/web-ui/build/static/elixir-57Ldyw8h.js +1 -0
  210. package/web-ui/build/static/elixir-DTxnmhIt.js +1 -0
  211. package/web-ui/build/static/elm-ClV9zQoT.js +1 -0
  212. package/web-ui/build/static/elm-pX-i6o7U.js +1 -0
  213. package/web-ui/build/static/erb-BPeO9smT.js +1 -0
  214. package/web-ui/build/static/erb-BnZQ4STz.js +1 -0
  215. package/web-ui/build/static/erlang-DPtI7VK_.js +1 -0
  216. package/web-ui/build/static/erlang-KG5mg5wN.js +1 -0
  217. package/web-ui/build/static/erlang-repl-BqPVXZ3Y.js +1 -0
  218. package/web-ui/build/static/etlua-BRc0Qbbs.js +1 -0
  219. package/web-ui/build/static/excel-BAlZ9Hkj.js +1 -0
  220. package/web-ui/build/static/excel-formula-BOW-bnHh.js +1 -0
  221. package/web-ui/build/static/factor-DCCsCpGM.js +1 -0
  222. package/web-ui/build/static/false-CnqnCzBU.js +1 -0
  223. package/web-ui/build/static/firestore-security-rules-DtkQ3uEq.js +1 -0
  224. package/web-ui/build/static/fix-CfPjl4Xr.js +1 -0
  225. package/web-ui/build/static/flix-BCA3BceS.js +1 -0
  226. package/web-ui/build/static/flow-C-5ewqYW.js +1 -0
  227. package/web-ui/build/static/fortran-CfDtl8An.js +1 -0
  228. package/web-ui/build/static/fortran-DQ_knNPt.js +1 -0
  229. package/web-ui/build/static/fsharp-CcVQ3IqX.js +1 -0
  230. package/web-ui/build/static/fsharp-olQ6ojCa.js +1 -0
  231. package/web-ui/build/static/ftl-DIWHDyWt.js +1 -0
  232. package/web-ui/build/static/gams-_NVFTSj5.js +1 -0
  233. package/web-ui/build/static/gap-DIG5TV2b.js +1 -0
  234. package/web-ui/build/static/gauss-C8rjPjTG.js +1 -0
  235. package/web-ui/build/static/gcode-8wJu4gcL.js +1 -0
  236. package/web-ui/build/static/gcode-DjHf417I.js +1 -0
  237. package/web-ui/build/static/gdscript-BZdmRCYu.js +1 -0
  238. package/web-ui/build/static/gedcom-BVFJ8C_Q.js +1 -0
  239. package/web-ui/build/static/gherkin-7NQkoaub.js +1 -0
  240. package/web-ui/build/static/gherkin-bSpNbJ48.js +1 -0
  241. package/web-ui/build/static/git-BRY_UXsc.js +1 -0
  242. package/web-ui/build/static/glsl-Ck6ShGRC.js +1 -0
  243. package/web-ui/build/static/glsl-jwCJ0Pmg.js +1 -0
  244. package/web-ui/build/static/gml-BAjG4Kr2.js +1 -0
  245. package/web-ui/build/static/gml-BnhKb5Da.js +1 -0
  246. package/web-ui/build/static/gn-Dux09iX8.js +1 -0
  247. package/web-ui/build/static/go-BOG-9Cqk.js +1 -0
  248. package/web-ui/build/static/go-BWZB_3b5.js +1 -0
  249. package/web-ui/build/static/go-module-BVLW7KBE.js +1 -0
  250. package/web-ui/build/static/golo-BD_SOfwL.js +1 -0
  251. package/web-ui/build/static/gradle-zSadWOD2.js +1 -0
  252. package/web-ui/build/static/graphql-BQlyj78B.js +1 -0
  253. package/web-ui/build/static/groovy-BurRMqQS.js +1 -0
  254. package/web-ui/build/static/groovy-SP58zwlE.js +1 -0
  255. package/web-ui/build/static/haml-eZ5ah5KY.js +1 -0
  256. package/web-ui/build/static/haml-lFC47IZb.js +1 -0
  257. package/web-ui/build/static/handlebars-B4UXrB-Q.js +1 -0
  258. package/web-ui/build/static/handlebars-CQ-Q5HnC.js +1 -0
  259. package/web-ui/build/static/haskell-AQrRyTSy.js +1 -0
  260. package/web-ui/build/static/haskell-BZTSbaB_.js +1 -0
  261. package/web-ui/build/static/haxe-5l1X6ESp.js +1 -0
  262. package/web-ui/build/static/haxe-DBn90muG.js +1 -0
  263. package/web-ui/build/static/hcl-CnMewPLM.js +1 -0
  264. package/web-ui/build/static/hlsl-RAtuBzr5.js +1 -0
  265. package/web-ui/build/static/hoon-CSqRU_4M.js +1 -0
  266. package/web-ui/build/static/hpkp-_gNbXcHt.js +1 -0
  267. package/web-ui/build/static/hsp-DY1V4au3.js +1 -0
  268. package/web-ui/build/static/hsts-j5z2jJo8.js +1 -0
  269. package/web-ui/build/static/htmlbars-C5EHvatr.js +1 -0
  270. package/web-ui/build/static/http-DgWgQrZh.js +1 -0
  271. package/web-ui/build/static/http-DphJL0q2.js +1 -0
  272. package/web-ui/build/static/hy-DnBqjPsB.js +1 -0
  273. package/web-ui/build/static/ichigojam-DeiCOKyF.js +1 -0
  274. package/web-ui/build/static/icon-CWANFWY5.js +1 -0
  275. package/web-ui/build/static/icu-message-format-C7w3vpgC.js +1 -0
  276. package/web-ui/build/static/idris-BeD8eULz.js +1 -0
  277. package/web-ui/build/static/iecst-BIznHXqY.js +1 -0
  278. package/web-ui/build/static/ignore-BcFgcNaS.js +1 -0
  279. package/web-ui/build/static/index-D8uVofpo.js +13 -0
  280. package/web-ui/build/static/index-DPFadqM6.css +1 -0
  281. package/web-ui/build/static/index-SkOgWEAU.js +1 -0
  282. package/web-ui/build/static/index-Vd3WlhtC.js +747 -0
  283. package/web-ui/build/static/inform7-CkQD_jz-.js +1 -0
  284. package/web-ui/build/static/inform7-phQiuDty.js +1 -0
  285. package/web-ui/build/static/ini-Bw_QAbzV.js +1 -0
  286. package/web-ui/build/static/ini-CB8ZxX7y.js +1 -0
  287. package/web-ui/build/static/io-D6IgpCmL.js +1 -0
  288. package/web-ui/build/static/irpf90-Ctj0koST.js +1 -0
  289. package/web-ui/build/static/isbl-D2mGcH87.js +1 -0
  290. package/web-ui/build/static/j-KvHmDBWH.js +1 -0
  291. package/web-ui/build/static/java-BeBIdo5i.js +1 -0
  292. package/web-ui/build/static/java-llFZkHLe.js +1 -0
  293. package/web-ui/build/static/javadoc-DXvQGu0s.js +1 -0
  294. package/web-ui/build/static/javadoclike-B5qdA9KZ.js +1 -0
  295. package/web-ui/build/static/javascript-De6HzHvc.js +1 -0
  296. package/web-ui/build/static/javastacktrace-C5MolKiP.js +1 -0
  297. package/web-ui/build/static/jboss-cli-2TXd54zo.js +1 -0
  298. package/web-ui/build/static/jexl-W4AVA9fi.js +1 -0
  299. package/web-ui/build/static/jolie-DTJKRMZN.js +1 -0
  300. package/web-ui/build/static/jq-BYg-QQKh.js +1 -0
  301. package/web-ui/build/static/js-extras-BrYWd2VE.js +1 -0
  302. package/web-ui/build/static/js-templates-DorYpbHq.js +1 -0
  303. package/web-ui/build/static/jsdoc-CRF8n9pZ.js +1 -0
  304. package/web-ui/build/static/json-Dlcd7rla.js +1 -0
  305. package/web-ui/build/static/json-rhOJZzzZ.js +1 -0
  306. package/web-ui/build/static/json5-hTq1nNIW.js +1 -0
  307. package/web-ui/build/static/jsonp-CMg9Xvol.js +1 -0
  308. package/web-ui/build/static/jsstacktrace-hEeYxHtB.js +1 -0
  309. package/web-ui/build/static/jsx-B7PtA8WD.js +1 -0
  310. package/web-ui/build/static/julia-CNiEEY-n.js +1 -0
  311. package/web-ui/build/static/julia-eE0_SJlc.js +1 -0
  312. package/web-ui/build/static/julia-repl-CUJTTT4C.js +1 -0
  313. package/web-ui/build/static/keepalived-GUWJBqyD.js +1 -0
  314. package/web-ui/build/static/keyman-Bdf9MJyu.js +1 -0
  315. package/web-ui/build/static/kotlin-Ch6Bej5W.js +1 -0
  316. package/web-ui/build/static/kotlin-DFJ7D7Lw.js +1 -0
  317. package/web-ui/build/static/kumir-DJLIjcCs.js +1 -0
  318. package/web-ui/build/static/kusto-BM0YTwU4.js +1 -0
  319. package/web-ui/build/static/lasso-Z1DVS84K.js +1 -0
  320. package/web-ui/build/static/latex-BWbw71RK.js +1 -0
  321. package/web-ui/build/static/latex-CMzqmbhw.js +1 -0
  322. package/web-ui/build/static/latte-C1g8_grc.js +1 -0
  323. package/web-ui/build/static/ldif-C6QH3OIL.js +1 -0
  324. package/web-ui/build/static/leaf-CbR--ZsH.js +1 -0
  325. package/web-ui/build/static/less-ClVrKh2Z.js +1 -0
  326. package/web-ui/build/static/less-DNSxm8UA.js +1 -0
  327. package/web-ui/build/static/lilypond-lTzf_sWt.js +1 -0
  328. package/web-ui/build/static/liquid-Bn91mVfC.js +1 -0
  329. package/web-ui/build/static/lisp-CU3bHohQ.js +1 -0
  330. package/web-ui/build/static/lisp-DbgzE9W8.js +1 -0
  331. package/web-ui/build/static/livecodeserver-Cse1Uz3H.js +1 -0
  332. package/web-ui/build/static/livescript-BaxbgzWP.js +1 -0
  333. package/web-ui/build/static/livescript-nJt61DBy.js +1 -0
  334. package/web-ui/build/static/llvm-DBboo6UI.js +1 -0
  335. package/web-ui/build/static/llvm-vIy7XYVy.js +1 -0
  336. package/web-ui/build/static/log-CT7nfoDW.js +1 -0
  337. package/web-ui/build/static/lolcode-CUKVytZh.js +1 -0
  338. package/web-ui/build/static/lsl-CsAOlGF2.js +1 -0
  339. package/web-ui/build/static/lua-BuU2FFxP.js +1 -0
  340. package/web-ui/build/static/lua-CiDuKQaa.js +1 -0
  341. package/web-ui/build/static/magma-7vR0zcmS.js +1 -0
  342. package/web-ui/build/static/makefile-Buiz-Msh.js +1 -0
  343. package/web-ui/build/static/makefile-DXW_-6OY.js +1 -0
  344. package/web-ui/build/static/markdown-Bk5DUoGY.js +1 -0
  345. package/web-ui/build/static/markdown-CRS5W_Ai.js +1 -0
  346. package/web-ui/build/static/markup-templating-24odpmF3.js +1 -0
  347. package/web-ui/build/static/mathematica-BxcwhJUp.js +1 -0
  348. package/web-ui/build/static/matlab-3pJYx_Fb.js +1 -0
  349. package/web-ui/build/static/matlab-BqlRrzMf.js +1 -0
  350. package/web-ui/build/static/maxima-DlCfUpcj.js +1 -0
  351. package/web-ui/build/static/maxscript-Cu_gCaFU.js +1 -0
  352. package/web-ui/build/static/mel-D7iQ-5Up.js +1 -0
  353. package/web-ui/build/static/mel-DzBKNpoN.js +1 -0
  354. package/web-ui/build/static/mercury-Dfrb-i8A.js +1 -0
  355. package/web-ui/build/static/mermaid-WN7V2_Eq.js +1 -0
  356. package/web-ui/build/static/mipsasm-CcijzL0q.js +1 -0
  357. package/web-ui/build/static/mizar-Bk68zACP.js +1 -0
  358. package/web-ui/build/static/mizar-Twc2-iZ4.js +1 -0
  359. package/web-ui/build/static/mojolicious-DBbo2S7X.js +1 -0
  360. package/web-ui/build/static/mongodb-2RsFIjgg.js +1 -0
  361. package/web-ui/build/static/monkey-CPXtQ0Bf.js +1 -0
  362. package/web-ui/build/static/monkey-DjV7Wcek.js +1 -0
  363. package/web-ui/build/static/moonscript-B5M5as70.js +1 -0
  364. package/web-ui/build/static/moonscript-D1BHW4Il.js +1 -0
  365. package/web-ui/build/static/n1ql-D0heNDBD.js +1 -0
  366. package/web-ui/build/static/n1ql-DfHqXQD7.js +1 -0
  367. package/web-ui/build/static/n4js-CaPf44Dz.js +1 -0
  368. package/web-ui/build/static/nand2tetris-hdl-D1nf9mn4.js +1 -0
  369. package/web-ui/build/static/naniscript-DnCnu5ZX.js +1 -0
  370. package/web-ui/build/static/nasm-BZrSaMsK.js +1 -0
  371. package/web-ui/build/static/neon-D29Grm2v.js +1 -0
  372. package/web-ui/build/static/nevod-DgSNbQkE.js +1 -0
  373. package/web-ui/build/static/nginx-BAaDGDfT.js +1 -0
  374. package/web-ui/build/static/nginx-BvJ1lrHX.js +1 -0
  375. package/web-ui/build/static/nim--9zzVe5F.js +1 -0
  376. package/web-ui/build/static/nim-Br1relpU.js +1 -0
  377. package/web-ui/build/static/nix--0ftErCy.js +1 -0
  378. package/web-ui/build/static/nix-104ztQqr.js +1 -0
  379. package/web-ui/build/static/node-repl-BUMAf7_p.js +1 -0
  380. package/web-ui/build/static/nsis-BaeKybNA.js +1 -0
  381. package/web-ui/build/static/nsis-CdZEv2iA.js +1 -0
  382. package/web-ui/build/static/objectivec-DBB4ymdg.js +1 -0
  383. package/web-ui/build/static/objectivec-kFYXC6g4.js +1 -0
  384. package/web-ui/build/static/ocaml-D1GXvN7c.js +1 -0
  385. package/web-ui/build/static/ocaml-D80jRMPE.js +1 -0
  386. package/web-ui/build/static/opencl-fb7BfRdO.js +1 -0
  387. package/web-ui/build/static/openqasm-CWUBrR2w.js +1 -0
  388. package/web-ui/build/static/openscad-Dim7ILSL.js +1 -0
  389. package/web-ui/build/static/oxygene-BSwApkwz.js +1 -0
  390. package/web-ui/build/static/oz-CMtRoi5F.js +1 -0
  391. package/web-ui/build/static/parigp-AH8cZ38D.js +1 -0
  392. package/web-ui/build/static/parser-bBNjuhG3.js +1 -0
  393. package/web-ui/build/static/parser3-DUtoWEAd.js +1 -0
  394. package/web-ui/build/static/pascal-Cr3DPIYT.js +1 -0
  395. package/web-ui/build/static/pascaligo-pWW12jfs.js +1 -0
  396. package/web-ui/build/static/pcaxis-DBw9rxmr.js +1 -0
  397. package/web-ui/build/static/peoplecode-aCpMPm_s.js +1 -0
  398. package/web-ui/build/static/perl-BpZ7GmJ3.js +1 -0
  399. package/web-ui/build/static/perl-fnHTrqJL.js +1 -0
  400. package/web-ui/build/static/pf-Dz352ty7.js +1 -0
  401. package/web-ui/build/static/pgsql-CHPUdlI_.js +1 -0
  402. package/web-ui/build/static/php-BRwItjmS.js +1 -0
  403. package/web-ui/build/static/php-CrX_kswO.js +1 -0
  404. package/web-ui/build/static/php-extras-BmeRXDSO.js +1 -0
  405. package/web-ui/build/static/php-template-B0MFJ9RR.js +1 -0
  406. package/web-ui/build/static/phpdoc-wAPkJj9X.js +1 -0
  407. package/web-ui/build/static/plaintext-CmPk1gvP.js +1 -0
  408. package/web-ui/build/static/plsql-pWVw0sCJ.js +1 -0
  409. package/web-ui/build/static/pony-B4SXhyDS.js +1 -0
  410. package/web-ui/build/static/powerquery-ZJ28bdRR.js +1 -0
  411. package/web-ui/build/static/powershell-CWg1ca6z.js +1 -0
  412. package/web-ui/build/static/powershell-Dnl0aBXc.js +1 -0
  413. package/web-ui/build/static/processing-CbYVU7hZ.js +1 -0
  414. package/web-ui/build/static/processing-DnroirEw.js +1 -0
  415. package/web-ui/build/static/profile-DLNc-MTA.js +1 -0
  416. package/web-ui/build/static/prolog-4KlPFQus.js +1 -0
  417. package/web-ui/build/static/prolog-CtUicY87.js +1 -0
  418. package/web-ui/build/static/promql-C_i6OJVg.js +1 -0
  419. package/web-ui/build/static/properties-Cj0lBOSP.js +1 -0
  420. package/web-ui/build/static/properties-vGFibcz9.js +1 -0
  421. package/web-ui/build/static/protobuf-BOIGxbSP.js +1 -0
  422. package/web-ui/build/static/protobuf-CQ3su-J7.js +1 -0
  423. package/web-ui/build/static/psl-DeG5_YUF.js +1 -0
  424. package/web-ui/build/static/pug-BieVVXYz.js +1 -0
  425. package/web-ui/build/static/puppet-Ba40SVKU.js +1 -0
  426. package/web-ui/build/static/puppet-D7BzrcGt.js +1 -0
  427. package/web-ui/build/static/pure-DZnkz1iT.js +1 -0
  428. package/web-ui/build/static/purebasic-CLLZW_6G.js +1 -0
  429. package/web-ui/build/static/purebasic-CYPZo_H6.js +1 -0
  430. package/web-ui/build/static/purescript-Dyjfu5Id.js +1 -0
  431. package/web-ui/build/static/python-BdIWKxdN.js +1 -0
  432. package/web-ui/build/static/python-ofKsqxv7.js +1 -0
  433. package/web-ui/build/static/python-repl-DiTYb1xK.js +1 -0
  434. package/web-ui/build/static/q-B4P0If_I.js +1 -0
  435. package/web-ui/build/static/q-t_17xfY8.js +1 -0
  436. package/web-ui/build/static/qml-B5WhiN48.js +1 -0
  437. package/web-ui/build/static/qml-Dq0cESXJ.js +1 -0
  438. package/web-ui/build/static/qore-DCx30XRf.js +1 -0
  439. package/web-ui/build/static/qsharp-UrBScekp.js +1 -0
  440. package/web-ui/build/static/r-B0Ty1RKQ.js +1 -0
  441. package/web-ui/build/static/r-B0za8QKS.js +1 -0
  442. package/web-ui/build/static/racket-Dj6WEyhS.js +1 -0
  443. package/web-ui/build/static/reason-dj9hJSfr.js +1 -0
  444. package/web-ui/build/static/reasonml-B-q5_wag.js +1 -0
  445. package/web-ui/build/static/regex-4HEc5C1m.js +1 -0
  446. package/web-ui/build/static/rego-BdQe18RK.js +1 -0
  447. package/web-ui/build/static/renpy-CVMA2llL.js +1 -0
  448. package/web-ui/build/static/rest-9B4JWVGr.js +1 -0
  449. package/web-ui/build/static/rib-DR-U8OaT.js +1 -0
  450. package/web-ui/build/static/rip-Bu2t_rFZ.js +1 -0
  451. package/web-ui/build/static/roboconf-CJeXD5-I.js +1 -0
  452. package/web-ui/build/static/roboconf-DzDTVrdM.js +1 -0
  453. package/web-ui/build/static/robotframework-CR7KyPpN.js +1 -0
  454. package/web-ui/build/static/routeros-B2741z2k.js +1 -0
  455. package/web-ui/build/static/rsl-B9F_ZCgv.js +1 -0
  456. package/web-ui/build/static/ruby-I2JTNgyY.js +1 -0
  457. package/web-ui/build/static/ruby-QGDPOmJX.js +1 -0
  458. package/web-ui/build/static/ruleslanguage-CGzXEUCO.js +1 -0
  459. package/web-ui/build/static/rust-BxW5-WOm.js +1 -0
  460. package/web-ui/build/static/rust-CSOA43di.js +1 -0
  461. package/web-ui/build/static/sas-Bclfx4g3.js +1 -0
  462. package/web-ui/build/static/sas-xbQaiYjT.js +1 -0
  463. package/web-ui/build/static/sass-DJPbdNwd.js +1 -0
  464. package/web-ui/build/static/scala-Bo18NtHQ.js +1 -0
  465. package/web-ui/build/static/scala-Cy0JH-SG.js +1 -0
  466. package/web-ui/build/static/scheme-BjcWWjIx.js +1 -0
  467. package/web-ui/build/static/scheme-DQdj8PzN.js +1 -0
  468. package/web-ui/build/static/scilab-Bn1KHdK-.js +1 -0
  469. package/web-ui/build/static/scss-B1twkZBz.js +1 -0
  470. package/web-ui/build/static/scss-DmOuMI4v.js +1 -0
  471. package/web-ui/build/static/shell-BUlkJG0S.js +1 -0
  472. package/web-ui/build/static/shell-session-Bke-svxA.js +1 -0
  473. package/web-ui/build/static/smali-Ch9S16HV.js +1 -0
  474. package/web-ui/build/static/smali-D_yDr_Aj.js +1 -0
  475. package/web-ui/build/static/smalltalk-B9TfQ5Md.js +1 -0
  476. package/web-ui/build/static/smalltalk-EwbZxZsR.js +1 -0
  477. package/web-ui/build/static/smarty-9kDPpeSm.js +1 -0
  478. package/web-ui/build/static/sml-2fEfT7rd.js +1 -0
  479. package/web-ui/build/static/sml-BiwoLNk7.js +1 -0
  480. package/web-ui/build/static/solidity-n_x8Oe0h.js +1 -0
  481. package/web-ui/build/static/solution-file-B2mvjI3e.js +1 -0
  482. package/web-ui/build/static/soy-DPkgKBIS.js +1 -0
  483. package/web-ui/build/static/sparql-Cy95tds0.js +1 -0
  484. package/web-ui/build/static/splunk-spl-Ym3z9ouN.js +1 -0
  485. package/web-ui/build/static/sqf-CXZTG8WE.js +1 -0
  486. package/web-ui/build/static/sqf-Cwi3yg7f.js +1 -0
  487. package/web-ui/build/static/sql-DPxSQY4S.js +1 -0
  488. package/web-ui/build/static/sql-peh7ijGj.js +1 -0
  489. package/web-ui/build/static/sql_more-0YAbAuPw.js +1 -0
  490. package/web-ui/build/static/squirrel-CphzjV0e.js +1 -0
  491. package/web-ui/build/static/stan-0-xZ95-O.js +1 -0
  492. package/web-ui/build/static/stan-CaI4__2g.js +1 -0
  493. package/web-ui/build/static/stata-BrbzrGSs.js +1 -0
  494. package/web-ui/build/static/step21-C_qeyVLw.js +1 -0
  495. package/web-ui/build/static/stylus-Btycb2sZ.js +1 -0
  496. package/web-ui/build/static/stylus-FoBJ7jki.js +1 -0
  497. package/web-ui/build/static/subunit-Dpg-m04-.js +1 -0
  498. package/web-ui/build/static/swift-Cr9uZmgb.js +1 -0
  499. package/web-ui/build/static/swift-hGLFtD7e.js +1 -0
  500. package/web-ui/build/static/systemd-Bls2D9Vj.js +1 -0
  501. package/web-ui/build/static/t4-cs-C4qDO-jJ.js +1 -0
  502. package/web-ui/build/static/t4-templating-BbCFPMPO.js +1 -0
  503. package/web-ui/build/static/t4-vb-D1zoEccT.js +1 -0
  504. package/web-ui/build/static/taggerscript-CWHk9Gih.js +1 -0
  505. package/web-ui/build/static/tap-Bjt0UnzV.js +1 -0
  506. package/web-ui/build/static/tap-BnHKwLQs.js +1 -0
  507. package/web-ui/build/static/tcl-Zo9kx4y-.js +1 -0
  508. package/web-ui/build/static/tcl-fzLmefkt.js +1 -0
  509. package/web-ui/build/static/textile-9lIlUPH5.js +1 -0
  510. package/web-ui/build/static/thrift-M3K6r5Cy.js +1 -0
  511. package/web-ui/build/static/toml-HpaKqckc.js +1 -0
  512. package/web-ui/build/static/tp-DFKuxrKj.js +1 -0
  513. package/web-ui/build/static/tremor-D4_bUtMB.js +1 -0
  514. package/web-ui/build/static/tsx-o1RT-T90.js +1 -0
  515. package/web-ui/build/static/tt2-1xDqcN_2.js +1 -0
  516. package/web-ui/build/static/turtle-Dlt-aGky.js +1 -0
  517. package/web-ui/build/static/twig-CJ_BnGSR.js +1 -0
  518. package/web-ui/build/static/twig-CjsiSQb6.js +1 -0
  519. package/web-ui/build/static/typescript-B8B9zUn-.js +1 -0
  520. package/web-ui/build/static/typescript-D0Jgo8O7.js +1 -0
  521. package/web-ui/build/static/typoscript-C8Qke4ZB.js +1 -0
  522. package/web-ui/build/static/unrealscript-YxJdDNZ3.js +1 -0
  523. package/web-ui/build/static/uorazor-CtEVnqBv.js +1 -0
  524. package/web-ui/build/static/uri-YdaiQl4c.js +1 -0
  525. package/web-ui/build/static/v-CIyttMDD.js +1 -0
  526. package/web-ui/build/static/vala-DGslcym_.js +1 -0
  527. package/web-ui/build/static/vala-GFPx3uEJ.js +1 -0
  528. package/web-ui/build/static/vbnet-B20itab-.js +1 -0
  529. package/web-ui/build/static/vbnet-BdoN6egk.js +1 -0
  530. package/web-ui/build/static/vbscript-PHVh6Fp_.js +1 -0
  531. package/web-ui/build/static/vbscript-html-woH1VZ7U.js +1 -0
  532. package/web-ui/build/static/velocity-DtVfCZeg.js +1 -0
  533. package/web-ui/build/static/verilog-Bt6edXvM.js +1 -0
  534. package/web-ui/build/static/verilog-k_7lr9Zq.js +1 -0
  535. package/web-ui/build/static/vhdl-BMzOgOeK.js +1 -0
  536. package/web-ui/build/static/vhdl-BcAbtPG6.js +1 -0
  537. package/web-ui/build/static/vim-DrinG9a4.js +1 -0
  538. package/web-ui/build/static/vim-WihLATJL.js +1 -0
  539. package/web-ui/build/static/visual-basic-CJnvgPjM.js +1 -0
  540. package/web-ui/build/static/warpscript-zMlbUoZs.js +1 -0
  541. package/web-ui/build/static/wasm-GUnfTBUL.js +1 -0
  542. package/web-ui/build/static/web-idl-CfaLTG_r.js +1 -0
  543. package/web-ui/build/static/wiki-13AlLoOc.js +1 -0
  544. package/web-ui/build/static/wolfram-zHocYNXW.js +1 -0
  545. package/web-ui/build/static/wren-Byq862Iu.js +1 -0
  546. package/web-ui/build/static/x86asm-CLcOnePY.js +1 -0
  547. package/web-ui/build/static/xeora-BVHqWOFS.js +1 -0
  548. package/web-ui/build/static/xl-lXi8OYfr.js +1 -0
  549. package/web-ui/build/static/xml-KZjGBKxi.js +1 -0
  550. package/web-ui/build/static/xml-doc-DrQSDcEW.js +1 -0
  551. package/web-ui/build/static/xojo-DosHeFXU.js +1 -0
  552. package/web-ui/build/static/xquery-BZN1F14Q.js +1 -0
  553. package/web-ui/build/static/xquery-Cnz7ZLFr.js +1 -0
  554. package/web-ui/build/static/yaml-BzXOcy9u.js +1 -0
  555. package/web-ui/build/static/yaml-C207y5bt.js +1 -0
  556. package/web-ui/build/static/yang-ByrBdDIg.js +1 -0
  557. package/web-ui/build/static/zephir-bahTa7of.js +1 -0
  558. package/web-ui/build/static/zig-BlFYhdtC.js +1 -0
  559. package/src/tools/browserTool.js +0 -897
  560. package/src/utilities/platformUtils.test.js +0 -98
  561. package/web-ui/build/static/index-SmQFfvBs.js +0 -746
  562. package/web-ui/build/static/index-V2ySwjHp.css +0 -1
@@ -7,17 +7,31 @@
7
7
  *
8
8
  * NOTE: These tests require network access and a working Puppeteer install.
9
9
  * They are slower (~60s total) and are tagged for separate runs.
10
+ * Skipped on WSL/headless Linux (no display server for Chromium).
10
11
  *
11
- * Run with: npm test -- --testPathPattern=webTool.e2e
12
+ * Run with: npm run test:e2e
12
13
  */
13
14
 
14
15
  import { describe, test, expect, beforeAll, afterAll } from '@jest/globals';
16
+ import os from 'os';
17
+ import fs from 'fs';
18
+
19
+ // Skip on WSL (no display) or when SKIP_BROWSER_TESTS is set
20
+ const isWSL = (() => {
21
+ try {
22
+ return os.platform() === 'linux' && fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');
23
+ } catch { return false; }
24
+ })();
25
+ const skipBrowser = isWSL || process.env.SKIP_BROWSER_TESTS === 'true';
26
+
27
+ const describeIfBrowser = skipBrowser ? describe.skip : describe;
15
28
 
16
29
  let WebTool;
17
30
  let wt;
18
31
  const silentLogger = { info() {}, warn() {}, error() {}, debug() {} };
19
32
 
20
33
  beforeAll(async () => {
34
+ if (skipBrowser) return;
21
35
  const mod = await import('../../tools/webTool.js');
22
36
  WebTool = mod.default;
23
37
  wt = new WebTool({ logger: silentLogger });
@@ -29,7 +43,7 @@ afterAll(async () => {
29
43
 
30
44
  // ─── HTTP Status Detection ──────────────────────────────────────────────────
31
45
 
32
- describe('HTTP status detection', () => {
46
+ describeIfBrowser('HTTP status detection', () => {
33
47
  test('fetch valid page returns success with HTTP 200', async () => {
34
48
  const result = await wt.execute(
35
49
  { operation: 'fetch', url: 'https://example.com', formats: ['title'] },
@@ -63,7 +77,7 @@ describe('HTTP status detection', () => {
63
77
 
64
78
  // ─── Selector Pre-Validation ─────────────────────────────────────────────────
65
79
 
66
- describe('Selector pre-validation', () => {
80
+ describeIfBrowser('Selector pre-validation', () => {
67
81
  test('click on non-existent selector returns actionable error', async () => {
68
82
  const result = await wt.execute({
69
83
  operation: 'interactive',
@@ -174,7 +188,7 @@ describe('Selector pre-validation', () => {
174
188
 
175
189
  // ─── URL Validation in Interactive ───────────────────────────────────────────
176
190
 
177
- describe('URL validation in interactive actions', () => {
191
+ describeIfBrowser('URL validation in interactive actions', () => {
178
192
  test('navigate to invalid URL returns error without crashing', async () => {
179
193
  const result = await wt.execute({
180
194
  operation: 'interactive',
@@ -212,7 +226,7 @@ describe('URL validation in interactive actions', () => {
212
226
 
213
227
  // ─── Wait-for Timeout Feedback ───────────────────────────────────────────────
214
228
 
215
- describe('Wait-for timeout feedback', () => {
229
+ describeIfBrowser('Wait-for timeout feedback', () => {
216
230
  test('wait-for non-existent element returns timeout error with context', async () => {
217
231
  const result = await wt.execute({
218
232
  operation: 'interactive',
@@ -253,7 +267,7 @@ describe('Wait-for timeout feedback', () => {
253
267
 
254
268
  // ─── Successful Operations ───────────────────────────────────────────────────
255
269
 
256
- describe('Successful operations', () => {
270
+ describeIfBrowser('Successful operations', () => {
257
271
  test('extract-text on existing element returns content', async () => {
258
272
  const result = await wt.execute({
259
273
  operation: 'interactive',
@@ -329,7 +343,7 @@ describe('Successful operations', () => {
329
343
 
330
344
  // ─── Search ──────────────────────────────────────────────────────────────────
331
345
 
332
- describe('Search operations', () => {
346
+ describeIfBrowser('Search operations', () => {
333
347
  test('search with valid query returns results', async () => {
334
348
  const result = await wt.execute(
335
349
  { operation: 'search', query: 'javascript MDN', engine: 'duckduckgo', maxResults: 3 },
@@ -355,7 +369,7 @@ describe('Search operations', () => {
355
369
 
356
370
  // ─── Multi-Action Chains ─────────────────────────────────────────────────────
357
371
 
358
- describe('Multi-action chains', () => {
372
+ describeIfBrowser('Multi-action chains', () => {
359
373
  test('chain of actions reports per-action success/failure', async () => {
360
374
  const result = await wt.execute({
361
375
  operation: 'interactive',
@@ -409,7 +423,7 @@ describe('Multi-action chains', () => {
409
423
 
410
424
  // ─── Tab Reuse ───────────────────────────────────────────────────────────────
411
425
 
412
- describe('Tab reuse', () => {
426
+ describeIfBrowser('Tab reuse', () => {
413
427
  test('reusing existing tab preserves session and returns reused flag', async () => {
414
428
  // First: open tab
415
429
  await wt.execute({
@@ -441,7 +455,7 @@ describe('Tab reuse', () => {
441
455
 
442
456
  // ─── JS Error & Network Failure Detection ────────────────────────────────────
443
457
 
444
- describe('JS error and network failure detection', () => {
458
+ describeIfBrowser('JS error and network failure detection', () => {
445
459
  test('JS errors on page are captured in pageErrors tracker', async () => {
446
460
  // Open tab first
447
461
  await wt.execute({
@@ -518,7 +532,7 @@ describe('JS error and network failure detection', () => {
518
532
 
519
533
  // ─── Result Structure ────────────────────────────────────────────────────────
520
534
 
521
- describe('Result structure and data consistency', () => {
535
+ describeIfBrowser('Result structure and data consistency', () => {
522
536
  test('fetch result has required fields', async () => {
523
537
  const result = await wt.execute(
524
538
  { operation: 'fetch', url: 'https://example.com', formats: ['title', 'text'] },
@@ -479,13 +479,33 @@ export class ImageTool extends BaseTool {
479
479
  let imageUrl;
480
480
  let isTemporary = false;
481
481
 
482
+ this.logger?.info('Image generation result', {
483
+ jobId: job.jobId,
484
+ savedToDisk: result.savedToDisk,
485
+ resolvedOutputPath: result.resolvedOutputPath,
486
+ temporaryUrl: result.temporaryUrl?.substring(0, 80),
487
+ downloadError: result.downloadError,
488
+ isBase64Response: result.isBase64Response
489
+ });
490
+
482
491
  if (result.savedToDisk && result.resolvedOutputPath) {
483
492
  // Image was saved successfully - use our server endpoint
484
493
  imageUrl = this._convertToWebUrl(result.resolvedOutputPath, job.sessionId);
494
+ this.logger?.info('Using local server URL for image', { imageUrl });
485
495
  } else if (result.temporaryUrl) {
486
496
  // Download failed - use temporary AI-generated URL (expires in ~1 hour)
487
497
  imageUrl = result.temporaryUrl;
488
498
  isTemporary = true;
499
+ this.logger?.warn('Using temporary AI URL for image (local save failed)', {
500
+ imageUrl: imageUrl.substring(0, 80),
501
+ downloadError: result.downloadError
502
+ });
503
+ } else {
504
+ this.logger?.error('No image URL available - neither local save nor temporary URL', {
505
+ jobId: job.jobId,
506
+ savedToDisk: result.savedToDisk,
507
+ hasTemporaryUrl: !!result.temporaryUrl
508
+ });
489
509
  }
490
510
 
491
511
  global.loxiaWebServer.broadcastToSession(job.sessionId, {
@@ -788,11 +808,19 @@ export class ImageTool extends BaseTool {
788
808
 
789
809
  try {
790
810
  if (b64Json) {
791
- // Flux response: Save base64 directly to disk
792
- this.logger?.info('Saving base64 image directly to disk (Flux response)');
811
+ // Flux/GPT-Image response: Save base64 directly to disk
812
+ this.logger?.info(`Saving base64 image to disk: ${resolvedOutputPath}`);
793
813
  const imageBuffer = Buffer.from(b64Json, 'base64');
794
814
  await fs.writeFile(resolvedOutputPath, imageBuffer);
795
- savedToDisk = true;
815
+
816
+ // Verify the file was actually written
817
+ const stat = await fs.stat(resolvedOutputPath);
818
+ if (stat.size > 0) {
819
+ savedToDisk = true;
820
+ this.logger?.info(`Image saved successfully: ${resolvedOutputPath} (${stat.size} bytes)`);
821
+ } else {
822
+ this.logger?.warn(`Image file is empty after write: ${resolvedOutputPath}`);
823
+ }
796
824
 
797
825
  // For web display, we'll use our local server endpoint (set below)
798
826
  displayUrl = null; // Will be converted to web URL later
@@ -800,7 +828,15 @@ export class ImageTool extends BaseTool {
800
828
  // URL response: Download from URL
801
829
  this.logger?.info(`Downloading image from URL: ${imageUrl.substring(0, 50)}...`);
802
830
  await this._downloadImage(imageUrl, resolvedOutputPath);
803
- savedToDisk = true;
831
+
832
+ // Verify the file was actually written
833
+ const stat = await fs.stat(resolvedOutputPath);
834
+ if (stat.size > 0) {
835
+ savedToDisk = true;
836
+ this.logger?.info(`Image downloaded successfully: ${resolvedOutputPath} (${stat.size} bytes)`);
837
+ } else {
838
+ this.logger?.warn(`Downloaded image file is empty: ${resolvedOutputPath}`);
839
+ }
804
840
  displayUrl = imageUrl;
805
841
  }
806
842
 
@@ -811,7 +847,7 @@ export class ImageTool extends BaseTool {
811
847
  } catch (error) {
812
848
  // Save failed, but we might still have a temporary URL
813
849
  downloadError = error.message;
814
- this.logger?.warn(`Failed to save image to disk: ${error.message}`);
850
+ this.logger?.error(`Failed to save image to disk at ${resolvedOutputPath}: ${error.message}`);
815
851
 
816
852
  if (!imageUrl) {
817
853
  // No URL fallback for Flux - this is a real failure
@@ -9,18 +9,13 @@
9
9
  * - Screenshot capture and AI-powered analysis
10
10
  * - Mouse and keyboard event simulation
11
11
  *
12
- * NOTE: This tool replaces the deprecated BrowserTool (December 2024).
13
- * WebTool provides equivalent functionality with a singleton browser instance
14
- * architecture for better resource management.
15
- *
16
12
  * ============================================================================
17
- * TODO: FEATURES TO ADD (migrated from deprecated BrowserTool)
13
+ * TODO: FEATURES TO ADD
18
14
  * ============================================================================
19
15
  *
20
16
  * TODO 1: TIME-BASED WAIT ACTION
21
17
  * -----------------------------------------------------------------------------
22
18
  * Add a time-based wait capability alongside the existing 'wait-for-selector'.
23
- * The deprecated BrowserTool supported: { "action": "wait", "waitTime": 3000 }
24
19
  *
25
20
  * Implementation details:
26
21
  * - Add new action type: 'wait' or 'delay' (distinct from 'wait-for-selector')
@@ -46,8 +41,7 @@
46
41
  * TODO 2: IMPROVED TYPE ACTION WITH ELEMENT WAITING AND CLEARING
47
42
  * -----------------------------------------------------------------------------
48
43
  * Enhance the 'type' action to automatically wait for the element and optionally
49
- * clear existing content before typing. The deprecated BrowserTool had better
50
- * handling for form inputs.
44
+ * clear existing content before typing.
51
45
  *
52
46
  * Implementation details:
53
47
  * - Before typing, wait for the selector to be visible (with configurable timeout)
@@ -279,7 +273,7 @@ First FETCH the login page to identify form selectors, then authenticate:
279
273
 
280
274
  1. SEARCH - Search the web
281
275
  \`\`\`json
282
- {"toolId": "web", "operation": "search", "query": "search terms", "engine": "google", "maxResults": 10}
276
+ {"toolId": "web", "operation": "search", "query": "search terms", "engine": "duckduckgo", "maxResults": 10}
283
277
  \`\`\`
284
278
 
285
279
  2. FETCH - Get page content (public pages only)
@@ -404,7 +398,7 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
404
398
  switch (params.operation) {
405
399
  case 'search':
406
400
  params.query = TagParser.extractContent(content, 'query')[0]?.trim();
407
- params.engine = TagParser.extractContent(content, 'engine')[0]?.trim() || 'google';
401
+ params.engine = TagParser.extractContent(content, 'engine')[0]?.trim() || 'duckduckgo';
408
402
  const maxResults = TagParser.extractContent(content, 'max-results')[0]?.trim();
409
403
  params.maxResults = maxResults ? parseInt(maxResults, 10) : 10;
410
404
  // stealthLevel for search (default standard, use maximum if getting blocked)
@@ -666,7 +660,7 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
666
660
  switch (operation) {
667
661
  case 'search':
668
662
  result = await this.search(params.query, {
669
- engine: params.engine || 'google',
663
+ engine: params.engine || 'duckduckgo',
670
664
  maxResults: params.maxResults || 10,
671
665
  stealthLevel: effectiveStealthLevel,
672
666
  agentId
@@ -719,23 +713,63 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
719
713
  // Flatten result for easier access (result.title instead of result.result.title)
720
714
  // IMPORTANT: Respect the operation's own success flag — don't override it to true
721
715
  const operationSuccess = result.success !== undefined ? result.success : true;
716
+
717
+ // Surface the FIRST actionable error from nested results so the agent
718
+ // always sees a top-level error/suggestion on failure — not just success:false.
719
+ let surfacedError = result.error;
720
+ let surfacedSuggestion = result.suggestion;
721
+ let surfacedWarning = result.warning;
722
+ if (!surfacedError && !operationSuccess && result.results) {
723
+ for (const tabResult of (Array.isArray(result.results) ? result.results : [])) {
724
+ // Check tab-level error first
725
+ if (tabResult.error && !surfacedError) {
726
+ surfacedError = tabResult.error;
727
+ if (tabResult.suggestion) surfacedSuggestion = tabResult.suggestion;
728
+ }
729
+ // Then check nested action results
730
+ if (tabResult.results && Array.isArray(tabResult.results)) {
731
+ for (const actionResult of tabResult.results) {
732
+ if (actionResult.success === false && actionResult.error && !surfacedError) {
733
+ surfacedError = actionResult.error;
734
+ if (actionResult.suggestion) surfacedSuggestion = actionResult.suggestion;
735
+ }
736
+ }
737
+ }
738
+ if (!surfacedWarning && tabResult.warning) surfacedWarning = tabResult.warning;
739
+ }
740
+ }
741
+
722
742
  return {
723
743
  success: operationSuccess,
724
744
  operation,
725
745
  toolUsed: 'web',
726
746
  // Spread operation-specific data at top level
727
747
  data: result,
748
+ // Always surface error/suggestion at top level so agent sees why it failed
749
+ ...(surfacedError && { error: surfacedError }),
750
+ ...(surfacedSuggestion && { suggestion: surfacedSuggestion }),
751
+ ...(surfacedWarning && { warning: surfacedWarning }),
728
752
  // Also keep common properties at top level for convenience
729
753
  ...(result.title !== undefined && { title: result.title }),
730
754
  ...(result.text !== undefined && { text: result.text }),
731
755
  ...(result.url !== undefined && { url: result.url }),
732
756
  ...(result.results !== undefined && { results: result.results }),
733
757
  ...(result.resultsCount !== undefined && { resultsCount: result.resultsCount }),
734
- ...(result.error !== undefined && { error: result.error }),
735
- ...(result.warning !== undefined && { warning: result.warning }),
736
758
  ...(result.httpStatus !== undefined && { httpStatus: result.httpStatus }),
737
- ...(result.suggestion !== undefined && { suggestion: result.suggestion }),
738
- // Surface page-level errors for agent awareness
759
+ ...(result.stealthNotice !== undefined && { stealthNotice: result.stealthNotice }),
760
+ // Surface diagnostics (page-level observations separate from tool success)
761
+ // Check both top-level and nested results for diagnostics
762
+ ...((() => {
763
+ const diag = result.diagnostics ? { ...result.diagnostics } : {};
764
+ if (result.results) {
765
+ for (const tabResult of (Array.isArray(result.results) ? result.results : [])) {
766
+ if (tabResult.diagnostics) Object.assign(diag, tabResult.diagnostics);
767
+ }
768
+ }
769
+ return Object.keys(diag).length > 0 ? { diagnostics: diag } : {};
770
+ })()),
771
+ ...(result.notice && { notice: result.notice }),
772
+ // Legacy: also surface flat arrays for backwards compatibility
739
773
  ...(result.jsErrors?.length > 0 && { jsErrors: result.jsErrors }),
740
774
  ...(result.networkFailures?.length > 0 && { networkFailures: result.networkFailures }),
741
775
  ...(result.httpErrors?.length > 0 && { httpErrors: result.httpErrors })
@@ -777,11 +811,25 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
777
811
  suggestion = 'The page took too long to load. Check your internet connection or try again.';
778
812
  }
779
813
 
814
+ // Detect bot detection / CAPTCHA failures — may be stealth-related
815
+ if (error.message.includes('CAPTCHA') || error.message.includes('blocked') ||
816
+ error.message.includes('403') || error.message.includes('Access Denied')) {
817
+ const currentLevel = this.browser?._stealthConfig?.stealthLevel || 'unknown';
818
+ if (currentLevel === 'standard') {
819
+ suggestion = (suggestion ? suggestion + ' ' : '') +
820
+ `Currently running at stealth level 'standard' (headless). This site may require stealthLevel: 'maximum' (visible browser) to bypass bot detection. Close open tabs first if needed.`;
821
+ }
822
+ }
823
+
824
+ // Include current stealth context so agent can make informed decisions
825
+ const currentStealthLevel = this.browser?._stealthConfig?.stealthLevel || null;
826
+
780
827
  return {
781
828
  success: false,
782
829
  operation,
783
830
  error: enhancedError,
784
831
  ...(suggestion && { suggestion }),
832
+ ...(currentStealthLevel && { currentStealthLevel }),
785
833
  toolUsed: 'web'
786
834
  };
787
835
  }
@@ -893,7 +941,10 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
893
941
  // called it with the appropriate stealth level. This prevents accidentally
894
942
  // resetting the browser to 'standard' stealth when 'maximum' is needed.
895
943
  if (!this.browser || !this.browser.isConnected()) {
896
- throw new Error('Browser not initialized. Call ensureBrowser() first.');
944
+ throw new Error(
945
+ 'Browser not initialized or connection lost. This can happen if another operation ' +
946
+ 'changed the stealth level and restarted the browser. Retry the operation.'
947
+ );
897
948
  }
898
949
 
899
950
  const page = await createStealthPage(this.browser, {
@@ -916,7 +967,7 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
916
967
  * @returns {Promise<Object>} Search results
917
968
  */
918
969
  async search(query, options = {}) {
919
- const { engine = 'google', maxResults = 10, agentId, humanMode = true, stealthLevel = 'standard' } = options;
970
+ const { engine = 'duckduckgo', maxResults = 10, agentId, humanMode = true, stealthLevel = 'standard' } = options;
920
971
 
921
972
  // Validate query
922
973
  if (!query || typeof query !== 'string' || query.trim().length === 0) {
@@ -930,8 +981,17 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
930
981
 
931
982
  this.logger?.info('[WebTool] Performing web search', { query, engine, agentId, humanMode, stealthLevel });
932
983
 
933
- // Ensure browser with specified stealth level
934
- await this.ensureBrowser({ stealthLevel });
984
+ // Reuse existing browser if running (preserve interactive/auth sessions)
985
+ let stealthDowngradeNotice = null;
986
+ if (this.browser && this.browser.isConnected()) {
987
+ const currentLevel = this.browser._stealthConfig?.stealthLevel || 'standard';
988
+ if (currentLevel !== stealthLevel) {
989
+ stealthDowngradeNotice = `Note: Running at stealth level '${currentLevel}' instead of requested '${stealthLevel}' to preserve active browser sessions. If this operation fails due to bot detection, close open tabs first and retry with stealthLevel: '${stealthLevel}'.`;
990
+ this.logger?.info('[WebTool] search: reusing existing browser', { requested: stealthLevel, actual: currentLevel });
991
+ }
992
+ } else {
993
+ await this.ensureBrowser({ stealthLevel });
994
+ }
935
995
 
936
996
  // Create stealth page with optional human-like cursor
937
997
  const { page, cursor } = await this.createPage({ humanMode });
@@ -998,6 +1058,10 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
998
1058
  results
999
1059
  };
1000
1060
 
1061
+ if (stealthDowngradeNotice) {
1062
+ searchResult.stealthNotice = stealthDowngradeNotice;
1063
+ }
1064
+
1001
1065
  // Warn when 0 results — may indicate selector mismatch or CAPTCHA
1002
1066
  if (results.length === 0) {
1003
1067
  searchResult.warning = 'Search returned 0 results. This may indicate: (1) no matching results exist, (2) the search engine blocked the request (CAPTCHA), or (3) the page layout changed and results could not be extracted.';
@@ -1031,8 +1095,17 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1031
1095
  return { success: false, error: `Invalid URL format: "${url}". Must include protocol (e.g. https://example.com)` };
1032
1096
  }
1033
1097
 
1034
- // Ensure browser with specified stealth level
1035
- await this.ensureBrowser({ stealthLevel });
1098
+ // Reuse existing browser if running (preserve interactive/auth sessions)
1099
+ let stealthDowngradeNotice = null;
1100
+ if (this.browser && this.browser.isConnected()) {
1101
+ const currentLevel = this.browser._stealthConfig?.stealthLevel || 'standard';
1102
+ if (currentLevel !== stealthLevel) {
1103
+ stealthDowngradeNotice = `Note: Running at stealth level '${currentLevel}' instead of requested '${stealthLevel}' to preserve active browser sessions. If this operation fails due to bot detection, close open tabs first and retry with stealthLevel: '${stealthLevel}'.`;
1104
+ this.logger?.info('[WebTool] fetch: reusing existing browser', { requested: stealthLevel, actual: currentLevel });
1105
+ }
1106
+ } else {
1107
+ await this.ensureBrowser({ stealthLevel });
1108
+ }
1036
1109
 
1037
1110
  this.logger?.info('Fetching web content', { url, formats, agentId, stealthLevel });
1038
1111
 
@@ -1057,10 +1130,11 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1057
1130
 
1058
1131
  if (fetchStatus && fetchStatus >= 400) {
1059
1132
  const errorResult = {
1060
- success: false,
1133
+ success: true, // Fetch itself executed — page returned error status
1061
1134
  url,
1062
1135
  httpStatus: fetchStatus,
1063
- error: `Fetch failed with HTTP ${fetchStatus} (${fetchStatus >= 500 ? 'server error' : fetchStatus === 404 ? 'page not found' : fetchStatus === 403 ? 'access forbidden' : 'client error'})`
1136
+ diagnostic: `Page returned HTTP ${fetchStatus} (${fetchStatus >= 500 ? 'server error' : fetchStatus === 404 ? 'page not found' : fetchStatus === 403 ? 'access forbidden' : 'client error'})`,
1137
+ warning: `HTTP ${fetchStatus} — the page loaded but returned an error status. Content may still be available.`
1064
1138
  };
1065
1139
  // Still try to get title for context
1066
1140
  try { errorResult.title = await page.title(); } catch {}
@@ -1102,10 +1176,14 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1102
1176
 
1103
1177
  this.logger?.info('Fetch completed', { url, formats, agentId });
1104
1178
 
1105
- return {
1179
+ const fetchResult = {
1106
1180
  success: true,
1107
1181
  ...result
1108
1182
  };
1183
+ if (stealthDowngradeNotice) {
1184
+ fetchResult.stealthNotice = stealthDowngradeNotice;
1185
+ }
1186
+ return fetchResult;
1109
1187
 
1110
1188
  } finally {
1111
1189
  try { await page.close(); } catch {}
@@ -1430,9 +1508,18 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1430
1508
  // Check for existing session cookies
1431
1509
  const existingSession = vault.getSession(normalizedSiteId);
1432
1510
 
1433
- // Create or reuse a tab for authentication with specified stealth level
1434
- // Default to 'maximum' (visible browser) for login pages to avoid detection
1435
- await this.ensureBrowser({ stealthLevel });
1511
+ // Reuse existing browser if one is running (preserve interactive sessions).
1512
+ // Only use the requested stealthLevel if no browser exists yet.
1513
+ let stealthDowngradeNotice = null;
1514
+ if (this.browser && this.browser.isConnected()) {
1515
+ const currentLevel = this.browser._stealthConfig?.stealthLevel || 'standard';
1516
+ if (currentLevel !== stealthLevel) {
1517
+ stealthDowngradeNotice = `Note: Authenticating at stealth level '${currentLevel}' instead of '${stealthLevel}' to preserve active browser sessions. If login fails due to bot detection, close open tabs first and retry with stealthLevel: '${stealthLevel}'.`;
1518
+ this.logger?.info('[WebTool] authenticate: reusing existing browser', { requested: stealthLevel, actual: currentLevel });
1519
+ }
1520
+ } else {
1521
+ await this.ensureBrowser({ stealthLevel });
1522
+ }
1436
1523
  const { page, cursor } = await this.createPage({ humanMode: true });
1437
1524
 
1438
1525
  try {
@@ -1458,7 +1545,8 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1458
1545
  success: true,
1459
1546
  message: `Already logged into ${credentials.name || normalizedSiteId} (session restored)`,
1460
1547
  siteId: normalizedSiteId,
1461
- method: 'session_restore'
1548
+ method: 'session_restore',
1549
+ ...(stealthDowngradeNotice && { stealthNotice: stealthDowngradeNotice })
1462
1550
  };
1463
1551
  } catch {
1464
1552
  // Session invalid, need to login
@@ -1600,7 +1688,8 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1600
1688
  siteId: normalizedSiteId,
1601
1689
  method: 'credentials',
1602
1690
  tabName,
1603
- tabKeptOpen: true
1691
+ tabKeptOpen: true,
1692
+ ...(stealthDowngradeNotice && { stealthNotice: stealthDowngradeNotice })
1604
1693
  };
1605
1694
  }
1606
1695
 
@@ -1609,7 +1698,8 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1609
1698
  success: true,
1610
1699
  message: `Successfully logged into ${credentials.name || normalizedSiteId}`,
1611
1700
  siteId: normalizedSiteId,
1612
- method: 'credentials'
1701
+ method: 'credentials',
1702
+ ...(stealthDowngradeNotice && { stealthNotice: stealthDowngradeNotice })
1613
1703
  };
1614
1704
  } else {
1615
1705
  await page.close();
@@ -1692,10 +1782,11 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1692
1782
  if (reuseNavStatus && reuseNavStatus >= 400) {
1693
1783
  results.push({
1694
1784
  action: 'navigate',
1695
- success: false,
1785
+ success: true,
1696
1786
  url: existingTab.page.url(),
1697
1787
  httpStatus: reuseNavStatus,
1698
- error: `Navigation got HTTP ${reuseNavStatus}`
1788
+ diagnostic: `Page returned HTTP ${reuseNavStatus}`,
1789
+ warning: `HTTP ${reuseNavStatus} — page loaded but returned an error status. Tab is still usable.`
1699
1790
  });
1700
1791
  }
1701
1792
  }
@@ -1726,17 +1817,28 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1726
1817
  if (humanMode) await humanWait('action');
1727
1818
  }
1728
1819
 
1729
- const anyFailed = results.some(r => r.success === false);
1820
+ const failedActions = results.filter(r => r.success === false);
1821
+ const diagnosticActions = results.filter(r => r.diagnostic);
1822
+ const reusedJsErrors = WebTool._dedupeErrors((existingTab.pageErrors || []).map(e => e.message));
1823
+ const reusedNetFails = WebTool._dedupeErrors((existingTab.networkFailures || []).map(f => `${f.method} ${f.url} → ${f.errorText}`));
1824
+
1825
+ const diagnostics = {};
1826
+ if (reusedJsErrors.length > 0) diagnostics.jsErrors = reusedJsErrors;
1827
+ if (reusedNetFails.length > 0) diagnostics.networkFailures = reusedNetFails;
1828
+ if (diagnosticActions.length > 0) diagnostics.pageIssues = diagnosticActions.map(a => a.diagnostic);
1829
+
1730
1830
  return {
1731
- success: !anyFailed,
1831
+ success: failedActions.length === 0,
1732
1832
  tabName,
1733
1833
  url: existingTab.page.url(),
1734
1834
  actionsExecuted: results.length,
1735
1835
  results,
1736
1836
  reused: true,
1737
- ...(anyFailed && { warning: `${results.filter(r => r.success === false).length} of ${results.length} action(s) failed` }),
1738
- ...((existingTab.pageErrors || []).length > 0 && { jsErrors: WebTool._dedupeErrors(existingTab.pageErrors.map(e => e.message)) }),
1739
- ...((existingTab.networkFailures || []).length > 0 && { networkFailures: WebTool._dedupeErrors(existingTab.networkFailures.map(f => `${f.method} ${f.url} ${f.errorText}`)) }),
1837
+ ...(failedActions.length > 0 && { warning: `${failedActions.length} of ${results.length} action(s) failed` }),
1838
+ ...(diagnosticActions.length > 0 && !failedActions.length && {
1839
+ notice: `All actions executed successfully. ${diagnosticActions.length} page-level issue(s) detected — see diagnostics.`
1840
+ }),
1841
+ ...(Object.keys(diagnostics).length > 0 && { diagnostics }),
1740
1842
  ...((existingTab.httpErrors || []).length > 0 && { httpErrors: WebTool._dedupeErrors(existingTab.httpErrors.map(e => `${e.method} ${e.url} → ${e.status}`)) })
1741
1843
  };
1742
1844
  }
@@ -1886,10 +1988,11 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1886
1988
  if (openTabStatus && openTabStatus >= 400) {
1887
1989
  results.push({
1888
1990
  action: 'navigate',
1889
- success: false,
1991
+ success: true,
1890
1992
  url: tabInfo.url,
1891
1993
  httpStatus: openTabStatus,
1892
- error: `Initial navigation got HTTP ${openTabStatus}`
1994
+ diagnostic: `Page returned HTTP ${openTabStatus}`,
1995
+ warning: `HTTP ${openTabStatus} — page loaded but returned an error status. Tab is still usable.`
1893
1996
  });
1894
1997
  }
1895
1998
  }
@@ -1934,23 +2037,32 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
1934
2037
  }
1935
2038
  }
1936
2039
 
1937
- const anyFailed = results.some(r => r.success === false);
2040
+ const failedActions = results.filter(r => r.success === false);
2041
+ const diagnosticActions = results.filter(r => r.diagnostic);
1938
2042
  // Summarize all page issues for the agent (deduplicated)
1939
2043
  const allJsErrors = WebTool._dedupeErrors(pageErrors.map(e => e.message));
1940
2044
  const allNetworkFails = WebTool._dedupeErrors(networkFailures.map(f => `${f.method} ${f.url} → ${f.errorText}`));
1941
2045
  const allHttpErrs = WebTool._dedupeErrors(httpErrors.map(e => `${e.method} ${e.url} → ${e.status}`));
1942
2046
 
2047
+ // Collect diagnostics: page-level observations that aren't action failures
2048
+ const diagnostics = {};
2049
+ if (allJsErrors.length > 0) diagnostics.jsErrors = allJsErrors;
2050
+ if (allNetworkFails.length > 0) diagnostics.networkFailures = allNetworkFails;
2051
+ if (allHttpErrs.length > 0) diagnostics.httpErrors = allHttpErrs;
2052
+ if (diagnosticActions.length > 0) diagnostics.pageIssues = diagnosticActions.map(a => a.diagnostic);
2053
+
1943
2054
  return {
1944
- success: !anyFailed,
2055
+ success: failedActions.length === 0,
1945
2056
  tabName,
1946
2057
  url: tabInfo.url,
1947
2058
  actionsExecuted: results.length,
1948
2059
  results,
1949
- ...(anyFailed && { warning: `${results.filter(r => r.success === false).length} of ${results.length} action(s) failed` }),
1950
- // Surface all detected issues at the top level for agent awareness
1951
- ...(allJsErrors.length > 0 && { jsErrors: allJsErrors }),
1952
- ...(allNetworkFails.length > 0 && { networkFailures: allNetworkFails }),
1953
- ...(allHttpErrs.length > 0 && { httpErrors: allHttpErrs })
2060
+ ...(failedActions.length > 0 && { warning: `${failedActions.length} of ${results.length} action(s) failed` }),
2061
+ ...(diagnosticActions.length > 0 && !failedActions.length && {
2062
+ notice: `All actions executed successfully. ${diagnosticActions.length} page-level issue(s) detected see diagnostics.`
2063
+ }),
2064
+ // Surface diagnostics at top level for agent awareness
2065
+ ...(Object.keys(diagnostics).length > 0 && { diagnostics })
1954
2066
  };
1955
2067
 
1956
2068
  } catch (error) {
@@ -2027,10 +2139,11 @@ If blocked (CAPTCHA, access denied), use stealthLevel: "maximum" (visible browse
2027
2139
  }
2028
2140
  if (navStatus && navStatus >= 400) {
2029
2141
  return {
2030
- success: false,
2142
+ success: true, // Navigation itself executed — page just returned an error status
2031
2143
  url: tabInfo.url,
2032
2144
  httpStatus: navStatus,
2033
- error: `Navigation failed with HTTP ${navStatus} (${navStatus >= 500 ? 'server error' : navStatus === 404 ? 'page not found' : navStatus === 403 ? 'access forbidden' : 'client error'})`
2145
+ diagnostic: `Page returned HTTP ${navStatus} (${navStatus >= 500 ? 'server error' : navStatus === 404 ? 'page not found' : navStatus === 403 ? 'access forbidden' : 'client error'})`,
2146
+ warning: `HTTP ${navStatus} — the page loaded but returned an error status. The tab is still usable.`
2034
2147
  };
2035
2148
  }
2036
2149
  return { success: true, url: tabInfo.url, httpStatus: navStatus };