onbuzz 4.8.2 → 4.9.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 (1034) hide show
  1. package/bin/cli.js +27 -1
  2. package/bin/composio-cli.js +431 -0
  3. package/package.json +1 -1
  4. package/scripts/build-onboarding-doc.cjs +805 -0
  5. package/scripts/composio-bench/README.md +151 -0
  6. package/scripts/memory-bench/bench.mjs +576 -0
  7. package/scripts/memory-bench/live-after-2026-05-21T16-20-52-767Z.json +150 -0
  8. package/scripts/memory-bench/live-after-2026-05-21T16-37-17-147Z.json +795 -0
  9. package/scripts/memory-bench/live-after-final-2026-05-21T17-16-02-947Z.json +766 -0
  10. package/scripts/memory-bench/live-after-v2-2026-05-21T16-52-26-833Z.json +890 -0
  11. package/scripts/memory-bench/live-after-v3-2026-05-21T17-05-40-622Z.json +955 -0
  12. package/scripts/memory-bench/live-probe-v2.mjs +412 -0
  13. package/scripts/memory-bench/live-probe.mjs +387 -0
  14. package/scripts/memory-bench/live-smoke-2026-05-21T16-22-35-945Z.json +36 -0
  15. package/scripts/memory-bench/live-smoke2-2026-05-21T16-24-14-095Z.json +36 -0
  16. package/scripts/memory-bench/live-smoke3-2026-05-21T16-26-54-093Z.json +123 -0
  17. package/scripts/memory-bench/live-v2-baseline-2026-05-23T22-43-20-757Z.json +628 -0
  18. package/scripts/memory-bench/live-v2-directive-2026-05-23T21-32-41-369Z.json +628 -0
  19. package/scripts/memory-bench/live-v2-smoke-2026-05-23T20-06-37-325Z.json +34 -0
  20. package/scripts/memory-bench/live-v2-smoke-2026-05-23T20-12-48-199Z.json +38 -0
  21. package/scripts/memory-bench/results-baseline-2026-05-19T11-23-12-245Z.json +39 -0
  22. package/scripts/memory-bench/results-baseline-2026-05-19T11-30-28-763Z.json +1024 -0
  23. package/scripts/memory-bench/results-baseline-2026-05-19T11-34-19-299Z.json +1007 -0
  24. package/scripts/memory-bench/results-baseline-2026-05-19T11-44-18-054Z.json +1432 -0
  25. package/scripts/memory-bench/results-nudge-combined-2026-05-19T12-00-37-465Z.json +1413 -0
  26. package/scripts/memory-bench/results-nudge-directive-2026-05-19T11-36-52-963Z.json +210 -0
  27. package/scripts/memory-bench/results-nudge-directive-2026-05-19T11-49-55-790Z.json +1464 -0
  28. package/scripts/memory-bench/results-nudge-echo-catalog-2026-05-19T11-39-01-829Z.json +232 -0
  29. package/scripts/memory-bench/results-nudge-echo-catalog-2026-05-19T11-55-12-705Z.json +1397 -0
  30. package/scripts/memory-bench/scenarios-v2.mjs +123 -0
  31. package/scripts/memory-bench/scenarios.mjs +178 -0
  32. package/scripts/office-bench/generate-samples.mjs +180 -0
  33. package/scripts/op-posture-bench/bench.mjs +397 -0
  34. package/scripts/op-posture-bench/probe.mjs +87 -0
  35. package/scripts/op-posture-bench/results-2026-05-18T20-29-13-941Z.json +57 -0
  36. package/scripts/op-posture-bench/results-2026-05-18T20-38-36-400Z.json +3071 -0
  37. package/scripts/op-posture-bench/scenarios.mjs +60 -0
  38. package/scripts/op-posture-bench/variants.mjs +68 -0
  39. package/scripts/stub-bench/bench.mjs +439 -0
  40. package/scripts/stub-bench/fixture/auth.js +41 -0
  41. package/scripts/stub-bench/results-2026-05-19T10-33-27-045Z.json +99 -0
  42. package/scripts/stub-bench/results-2026-05-19T10-35-08-240Z.json +920 -0
  43. package/scripts/stub-bench/results-2026-05-19T10-40-12-166Z.json +463 -0
  44. package/src/__tests__/composioCliFlags.test.js +239 -0
  45. package/src/core/__tests__/agentScheduler.codebaseKnowledge.test.js +65 -0
  46. package/src/core/__tests__/agentScheduler.memoryInjection.test.js +78 -0
  47. package/src/core/agentPool.js +10 -0
  48. package/src/core/agentScheduler.js +91 -0
  49. package/src/index.js +19 -3
  50. package/src/interfaces/__tests__/bulkAgentRoute.test.js +290 -0
  51. package/src/interfaces/__tests__/composioRoutes.test.js +318 -0
  52. package/src/interfaces/webServer.js +346 -2
  53. package/src/interfaces/webServer.js.bak +7047 -0
  54. package/src/services/__tests__/codeMapStubRenderer.test.js +207 -0
  55. package/src/services/__tests__/codebaseKnowledgeService.test.js +427 -0
  56. package/src/services/__tests__/compactionRetry.test.js +42 -42
  57. package/src/services/__tests__/composioService.test.js +332 -0
  58. package/src/services/__tests__/memoryService.appendAndCatalog.test.js +139 -0
  59. package/src/services/apiKeyManager.js +33 -0
  60. package/src/services/codeMapStubRenderer.js +142 -0
  61. package/src/services/codebaseKnowledgeService.js +430 -0
  62. package/src/services/composioService.js +378 -0
  63. package/src/services/memoryService.js +98 -0
  64. package/src/tools/__tests__/baseTool.test.js +62 -0
  65. package/src/tools/__tests__/codeMapTool.pluralCanonical.test.js +83 -0
  66. package/src/tools/__tests__/composioTool.test.js +268 -0
  67. package/src/tools/__tests__/memoryTool.pluralCanonical.test.js +189 -0
  68. package/src/tools/__tests__/memoryTool.test.js +1 -1
  69. package/src/tools/__tests__/officeTool.test.js +403 -0
  70. package/src/tools/__tests__/openaiFunctionSchemas.memoryReminisce.test.js +24 -25
  71. package/src/tools/__tests__/singularToolReverseForgive.test.js +97 -0
  72. package/src/tools/__tests__/taskManagerTool.discipline.test.js +137 -0
  73. package/src/tools/__tests__/taskManagerTool.transition.test.js +236 -0
  74. package/src/tools/__tests__/toolShapeForgiveness.test.js +260 -0
  75. package/src/tools/baseTool.js +28 -1
  76. package/src/tools/codeMapTool.js +1673 -1521
  77. package/src/tools/composioTool.js +307 -0
  78. package/src/tools/fileContentReplaceTool.js +893 -840
  79. package/src/tools/fileSystemTool.js +1372 -1314
  80. package/src/tools/fileTreeTool.js +7 -0
  81. package/src/tools/memoryTool.js +217 -81
  82. package/src/tools/office/officeDoc.js +425 -0
  83. package/src/tools/office/officePres.js +360 -0
  84. package/src/tools/office/officeSheet.js +350 -0
  85. package/src/tools/officeTool.js +313 -0
  86. package/src/tools/openaiFunctionSchemas.js +40 -50
  87. package/src/tools/platformControlTool.js +5 -0
  88. package/src/tools/seekTool.js +36 -1
  89. package/src/tools/skillsTool.js +10 -0
  90. package/src/tools/taskManagerTool.js +149 -14
  91. package/src/tools/terminalTool.js +23 -1
  92. package/src/tools/visionTool.js +7 -0
  93. package/src/tools/visualEditorTool.js +7 -0
  94. package/src/tools/whatsappTool.js +7 -0
  95. package/src/utilities/authCache.js +47 -6
  96. package/src/utilities/authCache.js.backup-1779570472481 +121 -0
  97. package/src/utilities/constants.js +19 -11
  98. package/web-ui/build/index.html +2 -2
  99. package/web-ui/build/static/1c-BoCdC5pC.js +1 -0
  100. package/web-ui/build/static/abap-D7A92M1K.js +1 -0
  101. package/web-ui/build/static/abnf-DIcpJ99N.js +1 -0
  102. package/web-ui/build/static/abnf-d4I9yABN.js +1 -0
  103. package/web-ui/build/static/accesslog-D_Ksv5Fi.js +1 -0
  104. package/web-ui/build/static/actionscript-CMJI1an7.js +1 -0
  105. package/web-ui/build/static/actionscript-CNcWu4a1.js +1 -0
  106. package/web-ui/build/static/ada-BIqOBKro.js +1 -0
  107. package/web-ui/build/static/ada-BaTkJMdz.js +1 -0
  108. package/web-ui/build/static/agda-DmRdGgvt.js +1 -0
  109. package/web-ui/build/static/al-g8BvIfOt.js +1 -0
  110. package/web-ui/build/static/angelscript-BGxc0B7l.js +1 -0
  111. package/web-ui/build/static/antlr4-Da7ff6-m.js +1 -0
  112. package/web-ui/build/static/apache-Bibp-dVJ.js +1 -0
  113. package/web-ui/build/static/apacheconf-VzeL8tYb.js +1 -0
  114. package/web-ui/build/static/apex-DUaOCea3.js +1 -0
  115. package/web-ui/build/static/apl-BpwvFE2V.js +1 -0
  116. package/web-ui/build/static/applescript-5YKSWwV1.js +1 -0
  117. package/web-ui/build/static/applescript-BHqj5bpe.js +1 -0
  118. package/web-ui/build/static/aql-B0Wx_3hH.js +1 -0
  119. package/web-ui/build/static/arcade-CR8KkII1.js +1 -0
  120. package/web-ui/build/static/arduino-CYx5u85C.js +1 -0
  121. package/web-ui/build/static/arduino-ClVU2zro.js +1 -0
  122. package/web-ui/build/static/arff-kutIH4CC.js +1 -0
  123. package/web-ui/build/static/armasm-DBm1lGrc.js +1 -0
  124. package/web-ui/build/static/asciidoc-Cl02VMGf.js +1 -0
  125. package/web-ui/build/static/asciidoc-MMFYRqre.js +1 -0
  126. package/web-ui/build/static/asm6502-DPVMGaHn.js +1 -0
  127. package/web-ui/build/static/asmatmel-Dz4vtZ11.js +1 -0
  128. package/web-ui/build/static/aspectj-BZidGBlr.js +1 -0
  129. package/web-ui/build/static/aspnet-C2mo9iee.js +1 -0
  130. package/web-ui/build/static/autohotkey-Bx83oFMW.js +1 -0
  131. package/web-ui/build/static/autohotkey-DciFl6K5.js +1 -0
  132. package/web-ui/build/static/autoit-TVKWI5bu.js +1 -0
  133. package/web-ui/build/static/autoit-oORW7mrd.js +1 -0
  134. package/web-ui/build/static/avisynth-D_5uOtxP.js +1 -0
  135. package/web-ui/build/static/avrasm-D22JmbLm.js +1 -0
  136. package/web-ui/build/static/avro-idl-CtTuANGZ.js +1 -0
  137. package/web-ui/build/static/awk-Bnez22eR.js +1 -0
  138. package/web-ui/build/static/axapta-eqcr7C2-.js +1 -0
  139. package/web-ui/build/static/bash-D3XWHCZZ.js +1 -0
  140. package/web-ui/build/static/bash-px0MqVxE.js +1 -0
  141. package/web-ui/build/static/basic-BWOdgKLg.js +1 -0
  142. package/web-ui/build/static/basic-C7PK4wVg.js +1 -0
  143. package/web-ui/build/static/batch-CFeuSY9D.js +1 -0
  144. package/web-ui/build/static/bbcode-CRC0UNOS.js +1 -0
  145. package/web-ui/build/static/bicep-C18w8d6Z.js +1 -0
  146. package/web-ui/build/static/birb-DPiF4fwX.js +1 -0
  147. package/web-ui/build/static/bison-C1RWPaLl.js +1 -0
  148. package/web-ui/build/static/bnf-BelSXhsr.js +1 -0
  149. package/web-ui/build/static/bnf-D_m4SZuc.js +1 -0
  150. package/web-ui/build/static/brainfuck-Ci7P0POq.js +1 -0
  151. package/web-ui/build/static/brainfuck-DIrW6_96.js +1 -0
  152. package/web-ui/build/static/brightscript-4XgpA0OQ.js +1 -0
  153. package/web-ui/build/static/bro-4kAMStb2.js +1 -0
  154. package/web-ui/build/static/bsl-CleVjH1I.js +1 -0
  155. package/web-ui/build/static/c-Ct3AAwH6.js +1 -0
  156. package/web-ui/build/static/c-EiJrAzIQ.js +1 -0
  157. package/web-ui/build/static/c-like-B361rHmz.js +1 -0
  158. package/web-ui/build/static/cal-BZ3Pr4JI.js +1 -0
  159. package/web-ui/build/static/capnproto-LmwOdJJp.js +1 -0
  160. package/web-ui/build/static/ceylon-CKAfIFzx.js +1 -0
  161. package/web-ui/build/static/cfscript-Cms4z16U.js +1 -0
  162. package/web-ui/build/static/chaiscript-DKxRiUyN.js +1 -0
  163. package/web-ui/build/static/cil-C6rGhwce.js +1 -0
  164. package/web-ui/build/static/clean-DmpXs6RN.js +1 -0
  165. package/web-ui/build/static/clojure-DRYxiF9s.js +1 -0
  166. package/web-ui/build/static/clojure-DTjXEJCq.js +1 -0
  167. package/web-ui/build/static/clojure-repl-C1qlAUnK.js +1 -0
  168. package/web-ui/build/static/cmake-DtUokafC.js +1 -0
  169. package/web-ui/build/static/cmake-_WqF1fG8.js +1 -0
  170. package/web-ui/build/static/cobol-D2Q0IMuT.js +1 -0
  171. package/web-ui/build/static/coffeescript-MRThzdTU.js +1 -0
  172. package/web-ui/build/static/coffeescript-OHOpkJvb.js +1 -0
  173. package/web-ui/build/static/concurnas-CVGat9IA.js +1 -0
  174. package/web-ui/build/static/coq-CB_bM4lt.js +1 -0
  175. package/web-ui/build/static/coq-WNK38Cqz.js +1 -0
  176. package/web-ui/build/static/cos-CNa5GVxa.js +1 -0
  177. package/web-ui/build/static/cpp-D-9lHx0_.js +1 -0
  178. package/web-ui/build/static/cpp-DRvL1yaA.js +1 -0
  179. package/web-ui/build/static/crmsh-evnPMxnd.js +1 -0
  180. package/web-ui/build/static/crystal-RGhNrgnC.js +1 -0
  181. package/web-ui/build/static/crystal-yrBjXjEh.js +1 -0
  182. package/web-ui/build/static/csharp-Dr7sLtl1.js +1 -0
  183. package/web-ui/build/static/csharp-zNVQxjuC.js +1 -0
  184. package/web-ui/build/static/cshtml-CoiK-7Bu.js +1 -0
  185. package/web-ui/build/static/csp-Bhgq86EB.js +1 -0
  186. package/web-ui/build/static/csp-C20UcGAL.js +1 -0
  187. package/web-ui/build/static/css-D1vlrRV2.js +1 -0
  188. package/web-ui/build/static/css-extras-DVSAYMbv.js +1 -0
  189. package/web-ui/build/static/csv-BgR9D8jG.js +1 -0
  190. package/web-ui/build/static/cypher-Dxehyj6Y.js +1 -0
  191. package/web-ui/build/static/d-B-Se39n2.js +1 -0
  192. package/web-ui/build/static/d-Cr4d56op.js +1 -0
  193. package/web-ui/build/static/dart-BBqlOFkR.js +1 -0
  194. package/web-ui/build/static/dart-GRD7MWem.js +1 -0
  195. package/web-ui/build/static/dataweave-BK-Tahkw.js +1 -0
  196. package/web-ui/build/static/dax-pTr9GVoe.js +1 -0
  197. package/web-ui/build/static/delphi-CbwSNmp4.js +1 -0
  198. package/web-ui/build/static/dhall-CuFqRXr6.js +1 -0
  199. package/web-ui/build/static/diff-CK8Fq88e.js +1 -0
  200. package/web-ui/build/static/diff-CWbUTU-C.js +1 -0
  201. package/web-ui/build/static/django-DWaEPdP6.js +1 -0
  202. package/web-ui/build/static/django-TkEFpAOO.js +1 -0
  203. package/web-ui/build/static/dns-DKpFJlA0.js +1 -0
  204. package/web-ui/build/static/dns-zone-file-BIsmZEMf.js +1 -0
  205. package/web-ui/build/static/docker-DUavAYf7.js +1 -0
  206. package/web-ui/build/static/dockerfile-YnavjTIo.js +1 -0
  207. package/web-ui/build/static/dos-Dv3Qr-Oo.js +1 -0
  208. package/web-ui/build/static/dot-BtIPbx7t.js +1 -0
  209. package/web-ui/build/static/dsconfig-CEgd9-kI.js +1 -0
  210. package/web-ui/build/static/dts-BztZn8T6.js +1 -0
  211. package/web-ui/build/static/dust-B-527Hc0.js +1 -0
  212. package/web-ui/build/static/ebnf-CH_nnNi5.js +1 -0
  213. package/web-ui/build/static/ebnf-CuBjcj0j.js +1 -0
  214. package/web-ui/build/static/editorconfig-CA9KaQ0w.js +1 -0
  215. package/web-ui/build/static/eiffel-DVYJwwQw.js +1 -0
  216. package/web-ui/build/static/ejs-C2LKgDYE.js +1 -0
  217. package/web-ui/build/static/elixir-DVXGkbnw.js +1 -0
  218. package/web-ui/build/static/elixir-meJ_qqx4.js +1 -0
  219. package/web-ui/build/static/elm-C5sYS5Cb.js +1 -0
  220. package/web-ui/build/static/elm-Dj443pWY.js +1 -0
  221. package/web-ui/build/static/erb-CAj5yksQ.js +1 -0
  222. package/web-ui/build/static/erb-CcUB2tMd.js +1 -0
  223. package/web-ui/build/static/erlang-JusKooAX.js +1 -0
  224. package/web-ui/build/static/erlang-repl-wYgxDX5h.js +1 -0
  225. package/web-ui/build/static/erlang-sn-PsrSf.js +1 -0
  226. package/web-ui/build/static/etlua-ChDwUxz6.js +1 -0
  227. package/web-ui/build/static/excel-BJux1m2h.js +1 -0
  228. package/web-ui/build/static/excel-formula-i5ByFxAu.js +1 -0
  229. package/web-ui/build/static/factor-CTutAp6r.js +1 -0
  230. package/web-ui/build/static/false-DQz_zYVV.js +1 -0
  231. package/web-ui/build/static/firestore-security-rules-DRGBNXh2.js +1 -0
  232. package/web-ui/build/static/fix-DeSUhixI.js +1 -0
  233. package/web-ui/build/static/flix-BKTJVua_.js +1 -0
  234. package/web-ui/build/static/flow-BpcZsNuo.js +1 -0
  235. package/web-ui/build/static/fortran-BZIKfBPW.js +1 -0
  236. package/web-ui/build/static/fortran-BdezWEh5.js +1 -0
  237. package/web-ui/build/static/fsharp-1Gpt8wZo.js +1 -0
  238. package/web-ui/build/static/fsharp-CMyiqXUW.js +1 -0
  239. package/web-ui/build/static/ftl-D9bBJMhV.js +1 -0
  240. package/web-ui/build/static/gams-DL57wTHN.js +1 -0
  241. package/web-ui/build/static/gap-DN4Uo9cF.js +1 -0
  242. package/web-ui/build/static/gauss-DTa1H0QR.js +1 -0
  243. package/web-ui/build/static/gcode-U5GhqE37.js +1 -0
  244. package/web-ui/build/static/gcode-s-E37fb4.js +1 -0
  245. package/web-ui/build/static/gdscript-DkjgGcDv.js +1 -0
  246. package/web-ui/build/static/gedcom-BbjSSId3.js +1 -0
  247. package/web-ui/build/static/gherkin-BwIzGx7J.js +1 -0
  248. package/web-ui/build/static/gherkin-C97W_9I9.js +1 -0
  249. package/web-ui/build/static/git-DO8dnOip.js +1 -0
  250. package/web-ui/build/static/glsl-B_4UD4-O.js +1 -0
  251. package/web-ui/build/static/glsl-CGSjLy2x.js +1 -0
  252. package/web-ui/build/static/gml-BiITg8HI.js +1 -0
  253. package/web-ui/build/static/gml-DInGU66p.js +1 -0
  254. package/web-ui/build/static/gn--m2IH4i3.js +1 -0
  255. package/web-ui/build/static/go-DOK7GAd-.js +1 -0
  256. package/web-ui/build/static/go-bof0B9jK.js +1 -0
  257. package/web-ui/build/static/go-module-CPIVqmnL.js +1 -0
  258. package/web-ui/build/static/golo-jgy07E43.js +1 -0
  259. package/web-ui/build/static/gradle-BISOF0nH.js +1 -0
  260. package/web-ui/build/static/graphql-FqlbDLTL.js +1 -0
  261. package/web-ui/build/static/groovy-BM0ZyW4a.js +1 -0
  262. package/web-ui/build/static/groovy-oUdbfbZY.js +1 -0
  263. package/web-ui/build/static/haml-C8Qg-VO5.js +1 -0
  264. package/web-ui/build/static/haml-Dse28pB_.js +1 -0
  265. package/web-ui/build/static/handlebars-CWT7WazY.js +1 -0
  266. package/web-ui/build/static/handlebars-Ow3joqOm.js +1 -0
  267. package/web-ui/build/static/haskell-BfraeRhd.js +1 -0
  268. package/web-ui/build/static/haskell-EhcU7sbn.js +1 -0
  269. package/web-ui/build/static/haxe-B8tUoExu.js +1 -0
  270. package/web-ui/build/static/haxe-D9WQHhTh.js +1 -0
  271. package/web-ui/build/static/hcl-CG3GCouf.js +1 -0
  272. package/web-ui/build/static/hlsl-CSWRMQ3m.js +1 -0
  273. package/web-ui/build/static/hoon-Cj0JdbFY.js +1 -0
  274. package/web-ui/build/static/hpkp-CmjdXbgl.js +1 -0
  275. package/web-ui/build/static/hsp-VaFZ7Rlv.js +1 -0
  276. package/web-ui/build/static/hsts-CkJ6UPzY.js +1 -0
  277. package/web-ui/build/static/htmlbars-pfHjYqd2.js +1 -0
  278. package/web-ui/build/static/http-2LA-qivJ.js +1 -0
  279. package/web-ui/build/static/http-CQ_5JSsj.js +1 -0
  280. package/web-ui/build/static/hy-C-Dll2MQ.js +1 -0
  281. package/web-ui/build/static/ichigojam-DecRewwq.js +1 -0
  282. package/web-ui/build/static/icon-CdnU9zPA.js +1 -0
  283. package/web-ui/build/static/icu-message-format-CUQtjk6Q.js +1 -0
  284. package/web-ui/build/static/idris-CRMftLq6.js +1 -0
  285. package/web-ui/build/static/iecst-CXhHqz1P.js +1 -0
  286. package/web-ui/build/static/ignore-_emqIx7v.js +1 -0
  287. package/web-ui/build/static/index-BBHrMvg7.js +1216 -0
  288. package/web-ui/build/static/index-ChkZR_aE.js +13 -0
  289. package/web-ui/build/static/index-DU4GNSOf.js +1 -0
  290. package/web-ui/build/static/index-_z8JjNrI.css +1 -0
  291. package/web-ui/build/static/inform7-CbqlFoFE.js +1 -0
  292. package/web-ui/build/static/inform7-TxZViMJ9.js +1 -0
  293. package/web-ui/build/static/ini-D1sHtm5e.js +1 -0
  294. package/web-ui/build/static/ini-DnqB7pfX.js +1 -0
  295. package/web-ui/build/static/io-MnQK1mM-.js +1 -0
  296. package/web-ui/build/static/irpf90-BRaAenqx.js +1 -0
  297. package/web-ui/build/static/isbl-kubGT4rM.js +1 -0
  298. package/web-ui/build/static/j-BIz9wT9q.js +1 -0
  299. package/web-ui/build/static/java-CbQmsHSK.js +1 -0
  300. package/web-ui/build/static/java-v1B9fNL2.js +1 -0
  301. package/web-ui/build/static/javadoc-TB1OtWS7.js +1 -0
  302. package/web-ui/build/static/javadoclike-BBQPwDAU.js +1 -0
  303. package/web-ui/build/static/javascript-DzNVL5l_.js +1 -0
  304. package/web-ui/build/static/javastacktrace-BduOgG1e.js +1 -0
  305. package/web-ui/build/static/jboss-cli-BcqzI366.js +1 -0
  306. package/web-ui/build/static/jexl-Boyapidr.js +1 -0
  307. package/web-ui/build/static/jolie-hEbkNpsb.js +1 -0
  308. package/web-ui/build/static/jq-ZyEE6oGU.js +1 -0
  309. package/web-ui/build/static/js-extras-CXOzvlL7.js +1 -0
  310. package/web-ui/build/static/js-templates-CkHsm5px.js +1 -0
  311. package/web-ui/build/static/jsdoc-Uk3hj8z2.js +1 -0
  312. package/web-ui/build/static/json-BFi9igjd.js +1 -0
  313. package/web-ui/build/static/json-LI9wtNaG.js +1 -0
  314. package/web-ui/build/static/json5-PwjNByuN.js +1 -0
  315. package/web-ui/build/static/jsonp-DpfGuNSW.js +1 -0
  316. package/web-ui/build/static/jsstacktrace-DhOP2AhS.js +1 -0
  317. package/web-ui/build/static/jsx-DmWoDk8F.js +1 -0
  318. package/web-ui/build/static/julia-DBC5h81N.js +1 -0
  319. package/web-ui/build/static/julia-Dh9sV3LX.js +1 -0
  320. package/web-ui/build/static/julia-repl-BYyDp1f4.js +1 -0
  321. package/web-ui/build/static/keepalived-BTxjrZMV.js +1 -0
  322. package/web-ui/build/static/keyman-IYiyCDCU.js +1 -0
  323. package/web-ui/build/static/kotlin-C0PBuzvy.js +1 -0
  324. package/web-ui/build/static/kotlin-LWYZUK_T.js +1 -0
  325. package/web-ui/build/static/kumir-CQIbnv9I.js +1 -0
  326. package/web-ui/build/static/kusto-DA7pQ-Rb.js +1 -0
  327. package/web-ui/build/static/lasso-CrHySbnC.js +1 -0
  328. package/web-ui/build/static/latex-CLSOFXG4.js +1 -0
  329. package/web-ui/build/static/latex-DYknD4Dx.js +1 -0
  330. package/web-ui/build/static/latte-4QvZcKVw.js +1 -0
  331. package/web-ui/build/static/ldif-Dr96BfRx.js +1 -0
  332. package/web-ui/build/static/leaf-C9qL2Qde.js +1 -0
  333. package/web-ui/build/static/less-DpoID9ZJ.js +1 -0
  334. package/web-ui/build/static/less-wO8QO0XW.js +1 -0
  335. package/web-ui/build/static/lilypond-CNp4Kb1P.js +1 -0
  336. package/web-ui/build/static/liquid-DLULQdgV.js +1 -0
  337. package/web-ui/build/static/lisp-CrVo-Llt.js +1 -0
  338. package/web-ui/build/static/lisp-QIAoXzmW.js +1 -0
  339. package/web-ui/build/static/livecodeserver-DrehOgbf.js +1 -0
  340. package/web-ui/build/static/livescript-Cm2Yp34x.js +1 -0
  341. package/web-ui/build/static/livescript-mVYzlrko.js +1 -0
  342. package/web-ui/build/static/llvm-D6KsQJjh.js +1 -0
  343. package/web-ui/build/static/llvm-DhIp1o-1.js +1 -0
  344. package/web-ui/build/static/log-Sj3ocpgB.js +1 -0
  345. package/web-ui/build/static/lolcode-BSeGnWYG.js +1 -0
  346. package/web-ui/build/static/lsl-DwgUIp0U.js +1 -0
  347. package/web-ui/build/static/lua-B7Ot0Oou.js +1 -0
  348. package/web-ui/build/static/lua-zydwn5-S.js +1 -0
  349. package/web-ui/build/static/magma-Dgfy2Qny.js +1 -0
  350. package/web-ui/build/static/makefile-CSjtzdcw.js +1 -0
  351. package/web-ui/build/static/makefile-DrnEneus.js +1 -0
  352. package/web-ui/build/static/markdown-D9Ke3tYA.js +1 -0
  353. package/web-ui/build/static/markdown-DmwKuI2Y.js +1 -0
  354. package/web-ui/build/static/markup-templating-CikqfHT9.js +1 -0
  355. package/web-ui/build/static/mathematica-OC0imzI5.js +1 -0
  356. package/web-ui/build/static/matlab-BlPcqzRw.js +1 -0
  357. package/web-ui/build/static/matlab-C1iteCkE.js +1 -0
  358. package/web-ui/build/static/maxima-DecbOh0Y.js +1 -0
  359. package/web-ui/build/static/maxscript-80cPuIOA.js +1 -0
  360. package/web-ui/build/static/mel-NI7ysGX2.js +1 -0
  361. package/web-ui/build/static/mel-r03p1eJo.js +1 -0
  362. package/web-ui/build/static/mercury-BdNZw6ZU.js +1 -0
  363. package/web-ui/build/static/mermaid-C1LB7zMJ.js +1 -0
  364. package/web-ui/build/static/mipsasm-BnZ0cdht.js +1 -0
  365. package/web-ui/build/static/mizar-BGPNX3WQ.js +1 -0
  366. package/web-ui/build/static/mizar-CswPcmcG.js +1 -0
  367. package/web-ui/build/static/mojolicious-rRtgy8Or.js +1 -0
  368. package/web-ui/build/static/mongodb-CJU0oeix.js +1 -0
  369. package/web-ui/build/static/monkey-Bj0HaV5-.js +1 -0
  370. package/web-ui/build/static/monkey-JRYuMIYc.js +1 -0
  371. package/web-ui/build/static/moonscript-i7e8bkxZ.js +1 -0
  372. package/web-ui/build/static/moonscript-ldoM156V.js +1 -0
  373. package/web-ui/build/static/n1ql-C0xs8qWA.js +1 -0
  374. package/web-ui/build/static/n1ql-DlC-GlVq.js +1 -0
  375. package/web-ui/build/static/n4js-mIwtIanO.js +1 -0
  376. package/web-ui/build/static/nand2tetris-hdl-mfHIYPZD.js +1 -0
  377. package/web-ui/build/static/naniscript-DiDayxx1.js +1 -0
  378. package/web-ui/build/static/nasm-7uf2nfNC.js +1 -0
  379. package/web-ui/build/static/neon-BKgOgIAL.js +1 -0
  380. package/web-ui/build/static/nevod-BlujoQ5k.js +1 -0
  381. package/web-ui/build/static/nginx-Bb57ubp_.js +1 -0
  382. package/web-ui/build/static/nginx-DmLU0URp.js +1 -0
  383. package/web-ui/build/static/nim-CmnPRTHF.js +1 -0
  384. package/web-ui/build/static/nim-DOReB32c.js +1 -0
  385. package/web-ui/build/static/nix-BDMn1sgi.js +1 -0
  386. package/web-ui/build/static/nix-LTHuFEfQ.js +1 -0
  387. package/web-ui/build/static/node-repl-dxwu8SWp.js +1 -0
  388. package/web-ui/build/static/nsis-CKD0KqBT.js +1 -0
  389. package/web-ui/build/static/nsis-IlSwBUak.js +1 -0
  390. package/web-ui/build/static/objectivec-D4Lo30HV.js +1 -0
  391. package/web-ui/build/static/objectivec-zmGGqvoM.js +1 -0
  392. package/web-ui/build/static/ocaml-2uzfqAr8.js +1 -0
  393. package/web-ui/build/static/ocaml-wF6lV9H4.js +1 -0
  394. package/web-ui/build/static/opencl-B82Xjnw5.js +1 -0
  395. package/web-ui/build/static/openqasm-VzVaHpAP.js +1 -0
  396. package/web-ui/build/static/openscad-B-qK1VfW.js +1 -0
  397. package/web-ui/build/static/oxygene-BNTVgbsA.js +1 -0
  398. package/web-ui/build/static/oz-5ZfXBXQY.js +1 -0
  399. package/web-ui/build/static/parigp-S8JhoJoz.js +1 -0
  400. package/web-ui/build/static/parser-BZPtbLcv.js +1 -0
  401. package/web-ui/build/static/parser3-BZRLDHuv.js +1 -0
  402. package/web-ui/build/static/pascal-GKzqT2Ku.js +1 -0
  403. package/web-ui/build/static/pascaligo-DnbFz_mr.js +1 -0
  404. package/web-ui/build/static/pcaxis-DiRsos26.js +1 -0
  405. package/web-ui/build/static/peoplecode-CMDLbqGB.js +1 -0
  406. package/web-ui/build/static/perl-BOnBAciC.js +1 -0
  407. package/web-ui/build/static/perl-fMVLgVTb.js +1 -0
  408. package/web-ui/build/static/pf-z6xsHHPS.js +1 -0
  409. package/web-ui/build/static/pgsql-5jHAyGJf.js +1 -0
  410. package/web-ui/build/static/php-CbG6Jq36.js +1 -0
  411. package/web-ui/build/static/php-D9yyh70o.js +1 -0
  412. package/web-ui/build/static/php-extras-CQKSwnMo.js +1 -0
  413. package/web-ui/build/static/php-template-Ch4UQhUx.js +1 -0
  414. package/web-ui/build/static/phpdoc-Be7TYcGQ.js +1 -0
  415. package/web-ui/build/static/plaintext-BsYjBqDy.js +1 -0
  416. package/web-ui/build/static/plsql-Dc4ePipN.js +1 -0
  417. package/web-ui/build/static/pony-Cen7DtWb.js +1 -0
  418. package/web-ui/build/static/powerquery-BIOJ1eUE.js +1 -0
  419. package/web-ui/build/static/powershell-CbEjJpj7.js +1 -0
  420. package/web-ui/build/static/powershell-DlceT0ds.js +1 -0
  421. package/web-ui/build/static/processing-C9GaKPU-.js +1 -0
  422. package/web-ui/build/static/processing-CTPA3QIV.js +1 -0
  423. package/web-ui/build/static/profile-CHjz016L.js +1 -0
  424. package/web-ui/build/static/prolog-BK0g5F4c.js +1 -0
  425. package/web-ui/build/static/prolog-DrrwUODp.js +1 -0
  426. package/web-ui/build/static/promql-DftRCUlR.js +1 -0
  427. package/web-ui/build/static/properties-B122hwLT.js +1 -0
  428. package/web-ui/build/static/properties-C0m3XIaN.js +1 -0
  429. package/web-ui/build/static/protobuf-CanIoKel.js +1 -0
  430. package/web-ui/build/static/protobuf-WvDgzNtD.js +1 -0
  431. package/web-ui/build/static/psl-JsKwhg_r.js +1 -0
  432. package/web-ui/build/static/pug-DoQaiAyU.js +1 -0
  433. package/web-ui/build/static/puppet-CoRftQQB.js +1 -0
  434. package/web-ui/build/static/puppet-DHgjsvQ8.js +1 -0
  435. package/web-ui/build/static/pure-BgZIQeOr.js +1 -0
  436. package/web-ui/build/static/purebasic-BRYfIFBb.js +1 -0
  437. package/web-ui/build/static/purebasic-DxXSa8gN.js +1 -0
  438. package/web-ui/build/static/purescript-ChvJvUnk.js +1 -0
  439. package/web-ui/build/static/python-DqhW0V3z.js +1 -0
  440. package/web-ui/build/static/python-Dz2FyIQx.js +1 -0
  441. package/web-ui/build/static/python-repl-Cl3mEb2H.js +1 -0
  442. package/web-ui/build/static/q-Bg4mQWXp.js +1 -0
  443. package/web-ui/build/static/q-DwaQK_1p.js +1 -0
  444. package/web-ui/build/static/qml-D1-fdJvp.js +1 -0
  445. package/web-ui/build/static/qml-DFsoiY0T.js +1 -0
  446. package/web-ui/build/static/qore-DdEtFSV5.js +1 -0
  447. package/web-ui/build/static/qsharp-B_A0OUBX.js +1 -0
  448. package/web-ui/build/static/r-C3M2HrKx.js +1 -0
  449. package/web-ui/build/static/r-_EUAt74Q.js +1 -0
  450. package/web-ui/build/static/racket-B3uAs3VE.js +1 -0
  451. package/web-ui/build/static/reason-CyAzPvNW.js +1 -0
  452. package/web-ui/build/static/reasonml-iEzx6ejF.js +1 -0
  453. package/web-ui/build/static/regex-suWqSnRa.js +1 -0
  454. package/web-ui/build/static/rego-QeDW60Lx.js +1 -0
  455. package/web-ui/build/static/renpy-03fEqIYK.js +1 -0
  456. package/web-ui/build/static/rest-BRJIE-UZ.js +1 -0
  457. package/web-ui/build/static/rib-DOI_C8C9.js +1 -0
  458. package/web-ui/build/static/rip-BaEBdknp.js +1 -0
  459. package/web-ui/build/static/roboconf-DC8IQe4Z.js +1 -0
  460. package/web-ui/build/static/roboconf-jTu2wKoD.js +1 -0
  461. package/web-ui/build/static/robotframework-BFp2sQG2.js +1 -0
  462. package/web-ui/build/static/routeros-Br-GCWs7.js +1 -0
  463. package/web-ui/build/static/rsl-BpWeUJqw.js +1 -0
  464. package/web-ui/build/static/ruby-C3nnVoGq.js +1 -0
  465. package/web-ui/build/static/ruby-DD90V5PL.js +1 -0
  466. package/web-ui/build/static/ruleslanguage-BflyDdqj.js +1 -0
  467. package/web-ui/build/static/rust-DtiFFJtC.js +1 -0
  468. package/web-ui/build/static/rust-uJHZCfXQ.js +1 -0
  469. package/web-ui/build/static/sas-DUr8rIBf.js +1 -0
  470. package/web-ui/build/static/sas-POH3vAEy.js +1 -0
  471. package/web-ui/build/static/sass-D1oLbZQ0.js +1 -0
  472. package/web-ui/build/static/scala-CDtrRn0l.js +1 -0
  473. package/web-ui/build/static/scala-VCIKIldH.js +1 -0
  474. package/web-ui/build/static/scheme-CAWHx2Oj.js +1 -0
  475. package/web-ui/build/static/scheme-DzKv24tn.js +1 -0
  476. package/web-ui/build/static/scilab-DuxpEvzV.js +1 -0
  477. package/web-ui/build/static/scss-BKd4Y-UF.js +1 -0
  478. package/web-ui/build/static/scss-BpqUKxry.js +1 -0
  479. package/web-ui/build/static/shell-session-CglnRr2l.js +1 -0
  480. package/web-ui/build/static/shell-u-Y3PKLQ.js +1 -0
  481. package/web-ui/build/static/smali-D308KWif.js +1 -0
  482. package/web-ui/build/static/smali-DYT-jHPu.js +1 -0
  483. package/web-ui/build/static/smalltalk-BLYA-Ag1.js +1 -0
  484. package/web-ui/build/static/smalltalk-BdMVmgN0.js +1 -0
  485. package/web-ui/build/static/smarty-DUYgBrje.js +1 -0
  486. package/web-ui/build/static/sml-EEhIaAvM.js +1 -0
  487. package/web-ui/build/static/sml-bQ27koeV.js +1 -0
  488. package/web-ui/build/static/solidity-U7x7-dFr.js +1 -0
  489. package/web-ui/build/static/solution-file-Cr03jaqW.js +1 -0
  490. package/web-ui/build/static/soy-lnGOhaRM.js +1 -0
  491. package/web-ui/build/static/sparql-BRm44B1P.js +1 -0
  492. package/web-ui/build/static/splunk-spl-DdrdBOQJ.js +1 -0
  493. package/web-ui/build/static/sqf-CDtvZjid.js +1 -0
  494. package/web-ui/build/static/sqf-wr3PtKhW.js +1 -0
  495. package/web-ui/build/static/sql-B1-8AkNe.js +1 -0
  496. package/web-ui/build/static/sql-zWEK1lPB.js +1 -0
  497. package/web-ui/build/static/sql_more-Ck0DuSs3.js +1 -0
  498. package/web-ui/build/static/squirrel-CfQEIe5h.js +1 -0
  499. package/web-ui/build/static/stan-BM1Wilpj.js +1 -0
  500. package/web-ui/build/static/stan-DFPCSU1l.js +1 -0
  501. package/web-ui/build/static/stata-BJ-qKfEg.js +1 -0
  502. package/web-ui/build/static/step21-fPKcuR3s.js +1 -0
  503. package/web-ui/build/static/stylus-BhqK2C03.js +1 -0
  504. package/web-ui/build/static/stylus-BxloRYSO.js +1 -0
  505. package/web-ui/build/static/subunit-CueM7bOY.js +1 -0
  506. package/web-ui/build/static/swift-CPgZZ0ja.js +1 -0
  507. package/web-ui/build/static/swift-CsEMsJ7g.js +1 -0
  508. package/web-ui/build/static/systemd-B1Wwkby4.js +1 -0
  509. package/web-ui/build/static/t4-cs-CL3Ca0dW.js +1 -0
  510. package/web-ui/build/static/t4-templating-BoV5iKxA.js +1 -0
  511. package/web-ui/build/static/t4-vb-BKXXzk5n.js +1 -0
  512. package/web-ui/build/static/taggerscript-aJQMdfVb.js +1 -0
  513. package/web-ui/build/static/tap-BBfQFbbW.js +1 -0
  514. package/web-ui/build/static/tap-CmCrAtSH.js +1 -0
  515. package/web-ui/build/static/tcl-B8xfoxf8.js +1 -0
  516. package/web-ui/build/static/tcl-CPzoU-PX.js +1 -0
  517. package/web-ui/build/static/textile-B99mXtsV.js +1 -0
  518. package/web-ui/build/static/thrift-Cd40Ii7J.js +1 -0
  519. package/web-ui/build/static/toml-DV5wxyQS.js +1 -0
  520. package/web-ui/build/static/tp-o2VorAT6.js +1 -0
  521. package/web-ui/build/static/tremor-Cs0K_i4_.js +1 -0
  522. package/web-ui/build/static/tsx-KG-mQur8.js +1 -0
  523. package/web-ui/build/static/tt2-M3GHOvjN.js +1 -0
  524. package/web-ui/build/static/turtle-D6o0UODZ.js +1 -0
  525. package/web-ui/build/static/twig-D7LOxPkH.js +1 -0
  526. package/web-ui/build/static/twig-SHSTj-jA.js +1 -0
  527. package/web-ui/build/static/typescript-BeIk_BLV.js +1 -0
  528. package/web-ui/build/static/typescript-Btz-ypJW.js +1 -0
  529. package/web-ui/build/static/typoscript-7jDBTKMM.js +1 -0
  530. package/web-ui/build/static/unrealscript-f2f2MAJa.js +1 -0
  531. package/web-ui/build/static/uorazor-c5VZAS6K.js +1 -0
  532. package/web-ui/build/static/uri-DY2cDkq-.js +1 -0
  533. package/web-ui/build/static/v-6ffK7TKq.js +1 -0
  534. package/web-ui/build/static/vala-BgOkjEeg.js +1 -0
  535. package/web-ui/build/static/vala-BvuDZGU_.js +1 -0
  536. package/web-ui/build/static/vbnet-Bdh9clH3.js +1 -0
  537. package/web-ui/build/static/vbnet-CDpcSndL.js +1 -0
  538. package/web-ui/build/static/vbscript-dH3gn1bx.js +1 -0
  539. package/web-ui/build/static/vbscript-html-a_tIfb0-.js +1 -0
  540. package/web-ui/build/static/velocity-DGDKyukB.js +1 -0
  541. package/web-ui/build/static/verilog-CtJaE20M.js +1 -0
  542. package/web-ui/build/static/verilog-CvdiugJe.js +1 -0
  543. package/web-ui/build/static/vhdl-CN1nxPYJ.js +1 -0
  544. package/web-ui/build/static/vhdl-JtjHb_oM.js +1 -0
  545. package/web-ui/build/static/vim-DBRKYGcU.js +1 -0
  546. package/web-ui/build/static/vim-DQiU261H.js +1 -0
  547. package/web-ui/build/static/visual-basic-D_daebxW.js +1 -0
  548. package/web-ui/build/static/warpscript-B4PIoyZp.js +1 -0
  549. package/web-ui/build/static/wasm-D6_O5K2p.js +1 -0
  550. package/web-ui/build/static/web-idl-D7BDrII0.js +1 -0
  551. package/web-ui/build/static/wiki-D-bqO098.js +1 -0
  552. package/web-ui/build/static/wolfram-NnogXigM.js +1 -0
  553. package/web-ui/build/static/wren-DwgJaCDU.js +1 -0
  554. package/web-ui/build/static/x86asm-B9-C3MH5.js +1 -0
  555. package/web-ui/build/static/xeora-wudvnBkY.js +1 -0
  556. package/web-ui/build/static/xl-BeBZDETk.js +1 -0
  557. package/web-ui/build/static/xml-DgNmhWin.js +1 -0
  558. package/web-ui/build/static/xml-doc-B2RShOT5.js +1 -0
  559. package/web-ui/build/static/xojo-C0HF7CVl.js +1 -0
  560. package/web-ui/build/static/xquery-CMss7jIw.js +1 -0
  561. package/web-ui/build/static/xquery-Dy1YImWg.js +1 -0
  562. package/web-ui/build/static/yaml--7oDAAKr.js +1 -0
  563. package/web-ui/build/static/yaml-1-HThDJz.js +1 -0
  564. package/web-ui/build/static/yang-CxleyLxS.js +1 -0
  565. package/web-ui/build/static/zephir-B7LMkND2.js +1 -0
  566. package/web-ui/build/static/zig-DST87v3k.js +1 -0
  567. package/web-ui/build/static/1c-DGpIT7i5.js +0 -1
  568. package/web-ui/build/static/abap-5wFDdWLh.js +0 -1
  569. package/web-ui/build/static/abnf-BP1dpNSE.js +0 -1
  570. package/web-ui/build/static/abnf-DBEIAl8g.js +0 -1
  571. package/web-ui/build/static/accesslog-CWSM_T5E.js +0 -1
  572. package/web-ui/build/static/actionscript-DONkco1J.js +0 -1
  573. package/web-ui/build/static/actionscript-FqBYk5er.js +0 -1
  574. package/web-ui/build/static/ada-C2JLRIaM.js +0 -1
  575. package/web-ui/build/static/ada-gKiygTRK.js +0 -1
  576. package/web-ui/build/static/agda-CkSODqK2.js +0 -1
  577. package/web-ui/build/static/al-BJ_YR6p7.js +0 -1
  578. package/web-ui/build/static/angelscript-Dg2byMGg.js +0 -1
  579. package/web-ui/build/static/antlr4-BnpyaFNr.js +0 -1
  580. package/web-ui/build/static/apache-Dffxsd7O.js +0 -1
  581. package/web-ui/build/static/apacheconf-DLitjtWj.js +0 -1
  582. package/web-ui/build/static/apex-Drr_IvU2.js +0 -1
  583. package/web-ui/build/static/apl-CF6qxmXG.js +0 -1
  584. package/web-ui/build/static/applescript-CjOlw3b_.js +0 -1
  585. package/web-ui/build/static/applescript-DjkSl1Ry.js +0 -1
  586. package/web-ui/build/static/aql-KwVmK1gP.js +0 -1
  587. package/web-ui/build/static/arcade-CENSXx0R.js +0 -1
  588. package/web-ui/build/static/arduino-B3Ta9Fll.js +0 -1
  589. package/web-ui/build/static/arduino-CzcsaB9_.js +0 -1
  590. package/web-ui/build/static/arff-CMJSVt_O.js +0 -1
  591. package/web-ui/build/static/armasm-0zSgSPB4.js +0 -1
  592. package/web-ui/build/static/asciidoc-B8K5ctWq.js +0 -1
  593. package/web-ui/build/static/asciidoc-bk2Sg6b6.js +0 -1
  594. package/web-ui/build/static/asm6502-ji6zm4FQ.js +0 -1
  595. package/web-ui/build/static/asmatmel-DJqObM4Y.js +0 -1
  596. package/web-ui/build/static/aspectj-DqQKI7J5.js +0 -1
  597. package/web-ui/build/static/aspnet-9cenTxW7.js +0 -1
  598. package/web-ui/build/static/autohotkey-C6EhiZvl.js +0 -1
  599. package/web-ui/build/static/autohotkey-sTGJOvMM.js +0 -1
  600. package/web-ui/build/static/autoit-B0Im8iQ1.js +0 -1
  601. package/web-ui/build/static/autoit-CO6pUD0H.js +0 -1
  602. package/web-ui/build/static/avisynth-OvOHTfj5.js +0 -1
  603. package/web-ui/build/static/avrasm-BLWZ5Mye.js +0 -1
  604. package/web-ui/build/static/avro-idl-BB2ODvnw.js +0 -1
  605. package/web-ui/build/static/awk-_jhMabQ0.js +0 -1
  606. package/web-ui/build/static/axapta-DdBRgoYy.js +0 -1
  607. package/web-ui/build/static/bash-CsaRGXBQ.js +0 -1
  608. package/web-ui/build/static/bash-CxLFkwAC.js +0 -1
  609. package/web-ui/build/static/basic-BJyy7JJE.js +0 -1
  610. package/web-ui/build/static/basic-CrMb-gv0.js +0 -1
  611. package/web-ui/build/static/batch-BvcykMe5.js +0 -1
  612. package/web-ui/build/static/bbcode-EOBuY5Y6.js +0 -1
  613. package/web-ui/build/static/bicep-QkDJBA34.js +0 -1
  614. package/web-ui/build/static/birb-TAOxKurn.js +0 -1
  615. package/web-ui/build/static/bison-BEK6cnad.js +0 -1
  616. package/web-ui/build/static/bnf-DfEODxsr.js +0 -1
  617. package/web-ui/build/static/bnf-DhbMjsuA.js +0 -1
  618. package/web-ui/build/static/brainfuck-BoVocOa7.js +0 -1
  619. package/web-ui/build/static/brainfuck-DDC5QXPK.js +0 -1
  620. package/web-ui/build/static/brightscript-DNN598w2.js +0 -1
  621. package/web-ui/build/static/bro-Cn_jjZ8P.js +0 -1
  622. package/web-ui/build/static/bsl-CMCN75Gu.js +0 -1
  623. package/web-ui/build/static/c-DbOGQnEJ.js +0 -1
  624. package/web-ui/build/static/c-kggwYFsy.js +0 -1
  625. package/web-ui/build/static/c-like-BIsuUvof.js +0 -1
  626. package/web-ui/build/static/cal-DpgMsBZE.js +0 -1
  627. package/web-ui/build/static/capnproto-C1AHYI-M.js +0 -1
  628. package/web-ui/build/static/ceylon-pvJffDe1.js +0 -1
  629. package/web-ui/build/static/cfscript-C1qylu52.js +0 -1
  630. package/web-ui/build/static/chaiscript-D417WKRI.js +0 -1
  631. package/web-ui/build/static/cil-CjbZHbcY.js +0 -1
  632. package/web-ui/build/static/clean-Dvc6R-F2.js +0 -1
  633. package/web-ui/build/static/clojure-CSVRQPMQ.js +0 -1
  634. package/web-ui/build/static/clojure-ig1Vkg7X.js +0 -1
  635. package/web-ui/build/static/clojure-repl-C1uyEabk.js +0 -1
  636. package/web-ui/build/static/cmake-DSbVcAB3.js +0 -1
  637. package/web-ui/build/static/cmake-Zp8kPwnH.js +0 -1
  638. package/web-ui/build/static/cobol-OsZSiK_P.js +0 -1
  639. package/web-ui/build/static/coffeescript-D8r0416S.js +0 -1
  640. package/web-ui/build/static/coffeescript-DCpgClxh.js +0 -1
  641. package/web-ui/build/static/concurnas-DKnsxUOc.js +0 -1
  642. package/web-ui/build/static/coq-BQFlywVI.js +0 -1
  643. package/web-ui/build/static/coq-BvS9mQB2.js +0 -1
  644. package/web-ui/build/static/cos-D3ze2791.js +0 -1
  645. package/web-ui/build/static/cpp-Bw-cV3P1.js +0 -1
  646. package/web-ui/build/static/cpp-vi1p7XpB.js +0 -1
  647. package/web-ui/build/static/crmsh-BClnJTeQ.js +0 -1
  648. package/web-ui/build/static/crystal-DGzOUYZq.js +0 -1
  649. package/web-ui/build/static/crystal-DMzk6EvA.js +0 -1
  650. package/web-ui/build/static/csharp-BTom8s2X.js +0 -1
  651. package/web-ui/build/static/csharp-Cp2V3jfR.js +0 -1
  652. package/web-ui/build/static/cshtml-Dmtt9Kto.js +0 -1
  653. package/web-ui/build/static/csp-WPVsLe9_.js +0 -1
  654. package/web-ui/build/static/csp-qddj5fu_.js +0 -1
  655. package/web-ui/build/static/css-B0FAm7kj.js +0 -1
  656. package/web-ui/build/static/css-extras-Bx3pvjiG.js +0 -1
  657. package/web-ui/build/static/csv-CH-edgS5.js +0 -1
  658. package/web-ui/build/static/cypher-c3G8Leew.js +0 -1
  659. package/web-ui/build/static/d-BMDSy22_.js +0 -1
  660. package/web-ui/build/static/d-BdH4oW8-.js +0 -1
  661. package/web-ui/build/static/dart-C-r72q-O.js +0 -1
  662. package/web-ui/build/static/dart-CwpBMrKa.js +0 -1
  663. package/web-ui/build/static/dataweave-BPvqdt4k.js +0 -1
  664. package/web-ui/build/static/dax-Eyy6ixcN.js +0 -1
  665. package/web-ui/build/static/delphi-DhEbPj_5.js +0 -1
  666. package/web-ui/build/static/dhall-Ct1L8sN1.js +0 -1
  667. package/web-ui/build/static/diff-CSTqCgwn.js +0 -1
  668. package/web-ui/build/static/diff-Da97B5vW.js +0 -1
  669. package/web-ui/build/static/django-DGit_lCg.js +0 -1
  670. package/web-ui/build/static/django-DPgqFB8k.js +0 -1
  671. package/web-ui/build/static/dns-DUyXuZ-a.js +0 -1
  672. package/web-ui/build/static/dns-zone-file-D79MDxVG.js +0 -1
  673. package/web-ui/build/static/docker-DTiy4o38.js +0 -1
  674. package/web-ui/build/static/dockerfile-CvwPP8wE.js +0 -1
  675. package/web-ui/build/static/dos-CaIUWxvb.js +0 -1
  676. package/web-ui/build/static/dot-D3504w6Y.js +0 -1
  677. package/web-ui/build/static/dsconfig-Smy1eeX_.js +0 -1
  678. package/web-ui/build/static/dts-DyTYSXZj.js +0 -1
  679. package/web-ui/build/static/dust-CrJyi6qA.js +0 -1
  680. package/web-ui/build/static/ebnf-C8nqfkBH.js +0 -1
  681. package/web-ui/build/static/ebnf-Cuh5Vh2-.js +0 -1
  682. package/web-ui/build/static/editorconfig-C7pTGl6n.js +0 -1
  683. package/web-ui/build/static/eiffel-C75MSJCA.js +0 -1
  684. package/web-ui/build/static/ejs-Dyo7DF5w.js +0 -1
  685. package/web-ui/build/static/elixir-D4yJefOc.js +0 -1
  686. package/web-ui/build/static/elixir-DKRow2SJ.js +0 -1
  687. package/web-ui/build/static/elm-CgbdDlkT.js +0 -1
  688. package/web-ui/build/static/elm-D3N-UgU0.js +0 -1
  689. package/web-ui/build/static/erb-BsITh8qW.js +0 -1
  690. package/web-ui/build/static/erb-lY_LyKyS.js +0 -1
  691. package/web-ui/build/static/erlang-BCnPiYmB.js +0 -1
  692. package/web-ui/build/static/erlang-JEghiPXc.js +0 -1
  693. package/web-ui/build/static/erlang-repl-DzU97ugC.js +0 -1
  694. package/web-ui/build/static/etlua-CsBo8cJa.js +0 -1
  695. package/web-ui/build/static/excel-formula-C2piiCYa.js +0 -1
  696. package/web-ui/build/static/excel-kqNypwQD.js +0 -1
  697. package/web-ui/build/static/factor-DZTOhkyU.js +0 -1
  698. package/web-ui/build/static/false-yLjhfaSw.js +0 -1
  699. package/web-ui/build/static/firestore-security-rules-oaQNoH8l.js +0 -1
  700. package/web-ui/build/static/fix-C9IfnTuS.js +0 -1
  701. package/web-ui/build/static/flix-EmJ_JhPo.js +0 -1
  702. package/web-ui/build/static/flow-DCUl7IAQ.js +0 -1
  703. package/web-ui/build/static/fortran-DvyxJmFN.js +0 -1
  704. package/web-ui/build/static/fortran-EEP9R3S5.js +0 -1
  705. package/web-ui/build/static/fsharp-D_98s3RX.js +0 -1
  706. package/web-ui/build/static/fsharp-Dt9jOO5G.js +0 -1
  707. package/web-ui/build/static/ftl-C8gMHWLo.js +0 -1
  708. package/web-ui/build/static/gams-BHxVPb4z.js +0 -1
  709. package/web-ui/build/static/gap-DFtyHk-q.js +0 -1
  710. package/web-ui/build/static/gauss-BwhJWUcg.js +0 -1
  711. package/web-ui/build/static/gcode-BvmXwp09.js +0 -1
  712. package/web-ui/build/static/gcode-gWcD6Vc7.js +0 -1
  713. package/web-ui/build/static/gdscript-btHoX8RE.js +0 -1
  714. package/web-ui/build/static/gedcom-MgPs9iqh.js +0 -1
  715. package/web-ui/build/static/gherkin-B9c_Q16A.js +0 -1
  716. package/web-ui/build/static/gherkin-BvOgkK6h.js +0 -1
  717. package/web-ui/build/static/git-D9XWOVcQ.js +0 -1
  718. package/web-ui/build/static/glsl-CshBHxHY.js +0 -1
  719. package/web-ui/build/static/glsl-fNRUMTDr.js +0 -1
  720. package/web-ui/build/static/gml-B78deHK8.js +0 -1
  721. package/web-ui/build/static/gml-Cj7d2u8O.js +0 -1
  722. package/web-ui/build/static/gn-CJLD-mF1.js +0 -1
  723. package/web-ui/build/static/go-CT93BEkL.js +0 -1
  724. package/web-ui/build/static/go-CTTlNuQO.js +0 -1
  725. package/web-ui/build/static/go-module-BgnXboUE.js +0 -1
  726. package/web-ui/build/static/golo-2S5tXS-l.js +0 -1
  727. package/web-ui/build/static/gradle-8W8DCcWJ.js +0 -1
  728. package/web-ui/build/static/graphql-DZfp6FNU.js +0 -1
  729. package/web-ui/build/static/groovy-BfsyMb3X.js +0 -1
  730. package/web-ui/build/static/groovy-Dxov7ENz.js +0 -1
  731. package/web-ui/build/static/haml-BlUFsdVV.js +0 -1
  732. package/web-ui/build/static/haml-CjVj6vvW.js +0 -1
  733. package/web-ui/build/static/handlebars-DxrQFkyA.js +0 -1
  734. package/web-ui/build/static/handlebars-FE6fotYl.js +0 -1
  735. package/web-ui/build/static/haskell-B6z0RCD_.js +0 -1
  736. package/web-ui/build/static/haskell-JBFmJTRy.js +0 -1
  737. package/web-ui/build/static/haxe-BtMZMi-_.js +0 -1
  738. package/web-ui/build/static/haxe-C1J8n-fH.js +0 -1
  739. package/web-ui/build/static/hcl-DJP-bFWE.js +0 -1
  740. package/web-ui/build/static/hlsl-DrGjhs1b.js +0 -1
  741. package/web-ui/build/static/hoon-Cg-ZhqIT.js +0 -1
  742. package/web-ui/build/static/hpkp-D07vgmoZ.js +0 -1
  743. package/web-ui/build/static/hsp-CsypPWoA.js +0 -1
  744. package/web-ui/build/static/hsts-B6DMRxvP.js +0 -1
  745. package/web-ui/build/static/htmlbars-BI7_Hw4e.js +0 -1
  746. package/web-ui/build/static/http-90ihEi4s.js +0 -1
  747. package/web-ui/build/static/http-Dp4QXj9E.js +0 -1
  748. package/web-ui/build/static/hy-BMgKvP4K.js +0 -1
  749. package/web-ui/build/static/ichigojam-D6wKvJDb.js +0 -1
  750. package/web-ui/build/static/icon-CrgkmCwl.js +0 -1
  751. package/web-ui/build/static/icu-message-format-Bi2JxCXs.js +0 -1
  752. package/web-ui/build/static/idris-DbUsyZt5.js +0 -1
  753. package/web-ui/build/static/iecst-CQZ9t8fW.js +0 -1
  754. package/web-ui/build/static/ignore-BaFgtNMs.js +0 -1
  755. package/web-ui/build/static/index-DEWSWosh.css +0 -1
  756. package/web-ui/build/static/index-Di1bjCFA.js +0 -13
  757. package/web-ui/build/static/index-otR_WSsL.js +0 -1
  758. package/web-ui/build/static/index-whZPU4as.js +0 -1183
  759. package/web-ui/build/static/inform7-ClyX1Gro.js +0 -1
  760. package/web-ui/build/static/inform7-tZHBS5XP.js +0 -1
  761. package/web-ui/build/static/ini-BrP5JNYL.js +0 -1
  762. package/web-ui/build/static/ini-CVkrAhwV.js +0 -1
  763. package/web-ui/build/static/io-BJ1Y6Bdc.js +0 -1
  764. package/web-ui/build/static/irpf90-fjjcKS_8.js +0 -1
  765. package/web-ui/build/static/isbl-ClpvfUIz.js +0 -1
  766. package/web-ui/build/static/j-CICW77xS.js +0 -1
  767. package/web-ui/build/static/java-B9DFK-0E.js +0 -1
  768. package/web-ui/build/static/java-BL_6rYko.js +0 -1
  769. package/web-ui/build/static/javadoc-Cc4HKpKK.js +0 -1
  770. package/web-ui/build/static/javadoclike-CgNkUDOm.js +0 -1
  771. package/web-ui/build/static/javascript-CM40ZECq.js +0 -1
  772. package/web-ui/build/static/javastacktrace-BHSqXfG5.js +0 -1
  773. package/web-ui/build/static/jboss-cli-Bv5NhVSZ.js +0 -1
  774. package/web-ui/build/static/jexl--Ohk_keA.js +0 -1
  775. package/web-ui/build/static/jolie-DMTN5Vdf.js +0 -1
  776. package/web-ui/build/static/jq-nXRLldXX.js +0 -1
  777. package/web-ui/build/static/js-extras-eLwv3frs.js +0 -1
  778. package/web-ui/build/static/js-templates-Ca0owlrg.js +0 -1
  779. package/web-ui/build/static/jsdoc-KERXp0da.js +0 -1
  780. package/web-ui/build/static/json-BTjLaRsy.js +0 -1
  781. package/web-ui/build/static/json-p7pU0qdW.js +0 -1
  782. package/web-ui/build/static/json5-Cxjy1udc.js +0 -1
  783. package/web-ui/build/static/jsonp-CO52H7Gy.js +0 -1
  784. package/web-ui/build/static/jsstacktrace-8YKfoyJP.js +0 -1
  785. package/web-ui/build/static/jsx-Ct_05KbM.js +0 -1
  786. package/web-ui/build/static/julia-23I1ubCE.js +0 -1
  787. package/web-ui/build/static/julia-d8rVGed_.js +0 -1
  788. package/web-ui/build/static/julia-repl-mwnHedW_.js +0 -1
  789. package/web-ui/build/static/keepalived-E85Rx_fF.js +0 -1
  790. package/web-ui/build/static/keyman-aWd3QUDq.js +0 -1
  791. package/web-ui/build/static/kotlin-Cu370hQq.js +0 -1
  792. package/web-ui/build/static/kotlin-DoJ2WnmZ.js +0 -1
  793. package/web-ui/build/static/kumir-oiOgqcQO.js +0 -1
  794. package/web-ui/build/static/kusto-Bp-B02K5.js +0 -1
  795. package/web-ui/build/static/lasso-BxoQVwOO.js +0 -1
  796. package/web-ui/build/static/latex-CANm5vsX.js +0 -1
  797. package/web-ui/build/static/latex-PMroeNch.js +0 -1
  798. package/web-ui/build/static/latte-2ErU_2XF.js +0 -1
  799. package/web-ui/build/static/ldif-ChPn_F7o.js +0 -1
  800. package/web-ui/build/static/leaf-UkCugDgG.js +0 -1
  801. package/web-ui/build/static/less-CCH5RA89.js +0 -1
  802. package/web-ui/build/static/less-CGZbVU1g.js +0 -1
  803. package/web-ui/build/static/lilypond-BoyM37sv.js +0 -1
  804. package/web-ui/build/static/liquid-DGJVpRBi.js +0 -1
  805. package/web-ui/build/static/lisp-93nne61u.js +0 -1
  806. package/web-ui/build/static/lisp-DYjIRsXz.js +0 -1
  807. package/web-ui/build/static/livecodeserver-CfNaxIE4.js +0 -1
  808. package/web-ui/build/static/livescript-C8kDlQkz.js +0 -1
  809. package/web-ui/build/static/livescript-CoarnRHq.js +0 -1
  810. package/web-ui/build/static/llvm-CBVyNmAh.js +0 -1
  811. package/web-ui/build/static/llvm-DGxq8a7u.js +0 -1
  812. package/web-ui/build/static/log-BivgwFql.js +0 -1
  813. package/web-ui/build/static/lolcode-l5sK2cZz.js +0 -1
  814. package/web-ui/build/static/lsl-Xm44xLRQ.js +0 -1
  815. package/web-ui/build/static/lua-CGvrzfKp.js +0 -1
  816. package/web-ui/build/static/lua-nHwXeY4c.js +0 -1
  817. package/web-ui/build/static/magma-DmNItmna.js +0 -1
  818. package/web-ui/build/static/makefile-CKJNNHGb.js +0 -1
  819. package/web-ui/build/static/makefile-DI6y5Qml.js +0 -1
  820. package/web-ui/build/static/markdown-BgpbxKd-.js +0 -1
  821. package/web-ui/build/static/markdown-CvcxA4yk.js +0 -1
  822. package/web-ui/build/static/markup-templating-DijqesiA.js +0 -1
  823. package/web-ui/build/static/mathematica-Cxll1Q10.js +0 -1
  824. package/web-ui/build/static/matlab-BJo2T1A-.js +0 -1
  825. package/web-ui/build/static/matlab-C6DlZX4l.js +0 -1
  826. package/web-ui/build/static/maxima-DJXO4sbL.js +0 -1
  827. package/web-ui/build/static/maxscript-DdLpUYBs.js +0 -1
  828. package/web-ui/build/static/mel-BIpfnSyZ.js +0 -1
  829. package/web-ui/build/static/mel-BYcTUZJW.js +0 -1
  830. package/web-ui/build/static/mercury-C_LSpbD8.js +0 -1
  831. package/web-ui/build/static/mermaid-D9yBWnrT.js +0 -1
  832. package/web-ui/build/static/mipsasm-nR_K2Ue-.js +0 -1
  833. package/web-ui/build/static/mizar-BeLUPncD.js +0 -1
  834. package/web-ui/build/static/mizar-BesIbZd9.js +0 -1
  835. package/web-ui/build/static/mojolicious-cBx3OWa-.js +0 -1
  836. package/web-ui/build/static/mongodb-DTWKy9ac.js +0 -1
  837. package/web-ui/build/static/monkey-B72bZC3c.js +0 -1
  838. package/web-ui/build/static/monkey-G9XELYPQ.js +0 -1
  839. package/web-ui/build/static/moonscript-BXYVQiqj.js +0 -1
  840. package/web-ui/build/static/moonscript-sDd-5knz.js +0 -1
  841. package/web-ui/build/static/n1ql-0vKSfFAO.js +0 -1
  842. package/web-ui/build/static/n1ql-C9_BSZfz.js +0 -1
  843. package/web-ui/build/static/n4js-B7Ct8dds.js +0 -1
  844. package/web-ui/build/static/nand2tetris-hdl-p9gpphTM.js +0 -1
  845. package/web-ui/build/static/naniscript-6ZVr8Aug.js +0 -1
  846. package/web-ui/build/static/nasm-Ca73yTUt.js +0 -1
  847. package/web-ui/build/static/neon-DNP49oyX.js +0 -1
  848. package/web-ui/build/static/nevod-Qhgt7Bce.js +0 -1
  849. package/web-ui/build/static/nginx-D5e7lu62.js +0 -1
  850. package/web-ui/build/static/nginx-DobnyESB.js +0 -1
  851. package/web-ui/build/static/nim-BPBivUOV.js +0 -1
  852. package/web-ui/build/static/nim-Baoug1Wa.js +0 -1
  853. package/web-ui/build/static/nix-CSPTQs5y.js +0 -1
  854. package/web-ui/build/static/nix-Dk4eNw49.js +0 -1
  855. package/web-ui/build/static/node-repl-NJNL8VFR.js +0 -1
  856. package/web-ui/build/static/nsis-BZ0oMzEw.js +0 -1
  857. package/web-ui/build/static/nsis-CKPCjtCU.js +0 -1
  858. package/web-ui/build/static/objectivec-BkfIRhhV.js +0 -1
  859. package/web-ui/build/static/objectivec-CnQgqhbJ.js +0 -1
  860. package/web-ui/build/static/ocaml-9rGNzRrK.js +0 -1
  861. package/web-ui/build/static/ocaml-CTolCqxL.js +0 -1
  862. package/web-ui/build/static/opencl-9Q3vRDxv.js +0 -1
  863. package/web-ui/build/static/openqasm-D2QiuFBp.js +0 -1
  864. package/web-ui/build/static/openscad-C3HyuzpB.js +0 -1
  865. package/web-ui/build/static/oxygene-4gi-VYy_.js +0 -1
  866. package/web-ui/build/static/oz-BGDEB-1A.js +0 -1
  867. package/web-ui/build/static/parigp-CfZzp1uE.js +0 -1
  868. package/web-ui/build/static/parser-UbGteTcy.js +0 -1
  869. package/web-ui/build/static/parser3-C-Jx-fy7.js +0 -1
  870. package/web-ui/build/static/pascal-D1_R0gW-.js +0 -1
  871. package/web-ui/build/static/pascaligo-B8C-98Np.js +0 -1
  872. package/web-ui/build/static/pcaxis-TGnlUKNs.js +0 -1
  873. package/web-ui/build/static/peoplecode-C5Vf1AH0.js +0 -1
  874. package/web-ui/build/static/perl-CBp1N62T.js +0 -1
  875. package/web-ui/build/static/perl-Z59j904t.js +0 -1
  876. package/web-ui/build/static/pf-Cq8B_xwQ.js +0 -1
  877. package/web-ui/build/static/pgsql-ofJbUHkL.js +0 -1
  878. package/web-ui/build/static/php-OlH7HLQJ.js +0 -1
  879. package/web-ui/build/static/php-extras-GqtrxLuk.js +0 -1
  880. package/web-ui/build/static/php-r09kMDOB.js +0 -1
  881. package/web-ui/build/static/php-template-18uT97Qo.js +0 -1
  882. package/web-ui/build/static/phpdoc-9tRFvup9.js +0 -1
  883. package/web-ui/build/static/plaintext-DsYT6Mu-.js +0 -1
  884. package/web-ui/build/static/plsql-RhWQNJVb.js +0 -1
  885. package/web-ui/build/static/pony-DD6JMLYI.js +0 -1
  886. package/web-ui/build/static/powerquery-CALow-bt.js +0 -1
  887. package/web-ui/build/static/powershell-C2QvIuKF.js +0 -1
  888. package/web-ui/build/static/powershell-yrbJEhCh.js +0 -1
  889. package/web-ui/build/static/processing-1T5w_Q03.js +0 -1
  890. package/web-ui/build/static/processing-DBim_dO-.js +0 -1
  891. package/web-ui/build/static/profile-Cdloh8mZ.js +0 -1
  892. package/web-ui/build/static/prolog-DRSsNnns.js +0 -1
  893. package/web-ui/build/static/prolog-D_ajweDr.js +0 -1
  894. package/web-ui/build/static/promql-BRuwn6Bn.js +0 -1
  895. package/web-ui/build/static/properties-D5Wyl4X4.js +0 -1
  896. package/web-ui/build/static/properties-kn4fl1bl.js +0 -1
  897. package/web-ui/build/static/protobuf-BMO76zWi.js +0 -1
  898. package/web-ui/build/static/protobuf-CTUCF-U-.js +0 -1
  899. package/web-ui/build/static/psl-CiqYdQbY.js +0 -1
  900. package/web-ui/build/static/pug-BLE2Qayj.js +0 -1
  901. package/web-ui/build/static/puppet-CFKLWXft.js +0 -1
  902. package/web-ui/build/static/puppet-DQci0Dl5.js +0 -1
  903. package/web-ui/build/static/pure-D2h_GynV.js +0 -1
  904. package/web-ui/build/static/purebasic-BF8MVw8V.js +0 -1
  905. package/web-ui/build/static/purebasic-BTtHiCkh.js +0 -1
  906. package/web-ui/build/static/purescript-D1ZSh-sH.js +0 -1
  907. package/web-ui/build/static/python-Cp9_Vdhb.js +0 -1
  908. package/web-ui/build/static/python-DdgNw8IW.js +0 -1
  909. package/web-ui/build/static/python-repl-DvK89VMC.js +0 -1
  910. package/web-ui/build/static/q-COaIgwhT.js +0 -1
  911. package/web-ui/build/static/q-Cm0dQkW8.js +0 -1
  912. package/web-ui/build/static/qml-BpsOqqJM.js +0 -1
  913. package/web-ui/build/static/qml-BziQXlU4.js +0 -1
  914. package/web-ui/build/static/qore-Cutz6g-2.js +0 -1
  915. package/web-ui/build/static/qsharp-B16619X1.js +0 -1
  916. package/web-ui/build/static/r-CFUIj5Hd.js +0 -1
  917. package/web-ui/build/static/r-CPrwCi5w.js +0 -1
  918. package/web-ui/build/static/racket-Bh08DFXF.js +0 -1
  919. package/web-ui/build/static/reason-BxjDq4e-.js +0 -1
  920. package/web-ui/build/static/reasonml-Ds5SsGP8.js +0 -1
  921. package/web-ui/build/static/regex-DhBIDIMI.js +0 -1
  922. package/web-ui/build/static/rego-DCwxZXcB.js +0 -1
  923. package/web-ui/build/static/renpy-C2fuQfqb.js +0 -1
  924. package/web-ui/build/static/rest-C52ZpxWQ.js +0 -1
  925. package/web-ui/build/static/rib-Cbl2Mzyj.js +0 -1
  926. package/web-ui/build/static/rip-BTOu5ZIE.js +0 -1
  927. package/web-ui/build/static/roboconf-3Oi2wuVk.js +0 -1
  928. package/web-ui/build/static/roboconf-ndLZLE39.js +0 -1
  929. package/web-ui/build/static/robotframework-BzHXiIj1.js +0 -1
  930. package/web-ui/build/static/routeros-CkpPoqx-.js +0 -1
  931. package/web-ui/build/static/rsl-C0bwOQ38.js +0 -1
  932. package/web-ui/build/static/ruby-C_hIhtuQ.js +0 -1
  933. package/web-ui/build/static/ruby-DvKfZPzj.js +0 -1
  934. package/web-ui/build/static/ruleslanguage-CbZJlddz.js +0 -1
  935. package/web-ui/build/static/rust-BFPIhB-X.js +0 -1
  936. package/web-ui/build/static/rust-sBpUq-qE.js +0 -1
  937. package/web-ui/build/static/sas-BCXvYN9x.js +0 -1
  938. package/web-ui/build/static/sas-D7GAsTY3.js +0 -1
  939. package/web-ui/build/static/sass-BdaErGMN.js +0 -1
  940. package/web-ui/build/static/scala-Cj81oCl9.js +0 -1
  941. package/web-ui/build/static/scala-CvbksfY6.js +0 -1
  942. package/web-ui/build/static/scheme-BM5ZqKnl.js +0 -1
  943. package/web-ui/build/static/scheme-BPvlu9Tk.js +0 -1
  944. package/web-ui/build/static/scilab-CJanLKQN.js +0 -1
  945. package/web-ui/build/static/scss-C53YF_7f.js +0 -1
  946. package/web-ui/build/static/scss-DPp8UZbr.js +0 -1
  947. package/web-ui/build/static/shell-CC9bQXMe.js +0 -1
  948. package/web-ui/build/static/shell-session-CuR3fbf-.js +0 -1
  949. package/web-ui/build/static/smali-CUgLls3D.js +0 -1
  950. package/web-ui/build/static/smali-YODSX8qt.js +0 -1
  951. package/web-ui/build/static/smalltalk-BMJQ4bbs.js +0 -1
  952. package/web-ui/build/static/smalltalk-BVGY3CTl.js +0 -1
  953. package/web-ui/build/static/smarty-C9aNt4-p.js +0 -1
  954. package/web-ui/build/static/sml-C3BIHhfq.js +0 -1
  955. package/web-ui/build/static/sml-DTipyRmY.js +0 -1
  956. package/web-ui/build/static/solidity-1yuPUqoC.js +0 -1
  957. package/web-ui/build/static/solution-file-BgzK4GOU.js +0 -1
  958. package/web-ui/build/static/soy-CFJXRvqc.js +0 -1
  959. package/web-ui/build/static/sparql-CAosYMpl.js +0 -1
  960. package/web-ui/build/static/splunk-spl-DkINtWr8.js +0 -1
  961. package/web-ui/build/static/sqf-DbrWIS2M.js +0 -1
  962. package/web-ui/build/static/sqf-nq8Q9J2W.js +0 -1
  963. package/web-ui/build/static/sql-9bwClhZQ.js +0 -1
  964. package/web-ui/build/static/sql-CqPkY-lX.js +0 -1
  965. package/web-ui/build/static/sql_more-CsY5ts77.js +0 -1
  966. package/web-ui/build/static/squirrel-BuqtzRBD.js +0 -1
  967. package/web-ui/build/static/stan-BNxBSglc.js +0 -1
  968. package/web-ui/build/static/stan-CZVMc34l.js +0 -1
  969. package/web-ui/build/static/stata-DKnVdHCd.js +0 -1
  970. package/web-ui/build/static/step21-Cu_TaBGF.js +0 -1
  971. package/web-ui/build/static/stylus-BS7-OJew.js +0 -1
  972. package/web-ui/build/static/stylus-DtFrp1Nk.js +0 -1
  973. package/web-ui/build/static/subunit-DDCoWkkc.js +0 -1
  974. package/web-ui/build/static/swift-BaguUZbl.js +0 -1
  975. package/web-ui/build/static/swift-mwBsb8Bx.js +0 -1
  976. package/web-ui/build/static/systemd-D6PpyDKk.js +0 -1
  977. package/web-ui/build/static/t4-cs-Cem8g4Ck.js +0 -1
  978. package/web-ui/build/static/t4-templating-BZo-HjmD.js +0 -1
  979. package/web-ui/build/static/t4-vb-B4oVnKa4.js +0 -1
  980. package/web-ui/build/static/taggerscript-DweAZ5pw.js +0 -1
  981. package/web-ui/build/static/tap-DpuvKHHF.js +0 -1
  982. package/web-ui/build/static/tap-hUKmJObZ.js +0 -1
  983. package/web-ui/build/static/tcl-BUvhAi7u.js +0 -1
  984. package/web-ui/build/static/tcl-Dsck63d8.js +0 -1
  985. package/web-ui/build/static/textile-CgKbqJ-j.js +0 -1
  986. package/web-ui/build/static/thrift-B5H6rApp.js +0 -1
  987. package/web-ui/build/static/toml-DggTpfOo.js +0 -1
  988. package/web-ui/build/static/tp-CazB2P2X.js +0 -1
  989. package/web-ui/build/static/tremor-B1jy7S5p.js +0 -1
  990. package/web-ui/build/static/tsx-BqF2lVDi.js +0 -1
  991. package/web-ui/build/static/tt2-BNoNXpEe.js +0 -1
  992. package/web-ui/build/static/turtle-B0evd5mn.js +0 -1
  993. package/web-ui/build/static/twig-B5A7nMdv.js +0 -1
  994. package/web-ui/build/static/twig-DZnLWvh8.js +0 -1
  995. package/web-ui/build/static/typescript-Bbe_P093.js +0 -1
  996. package/web-ui/build/static/typescript-BfhvmSSG.js +0 -1
  997. package/web-ui/build/static/typoscript-BjuZEIgw.js +0 -1
  998. package/web-ui/build/static/unrealscript-C3iUCFRi.js +0 -1
  999. package/web-ui/build/static/uorazor-CbZXwzIj.js +0 -1
  1000. package/web-ui/build/static/uri-xMPSnp6m.js +0 -1
  1001. package/web-ui/build/static/v-BKcGo5I6.js +0 -1
  1002. package/web-ui/build/static/vala-BRoBE4am.js +0 -1
  1003. package/web-ui/build/static/vala-B__Iyrma.js +0 -1
  1004. package/web-ui/build/static/vbnet-DBxlMRvN.js +0 -1
  1005. package/web-ui/build/static/vbnet-DjAXt5BE.js +0 -1
  1006. package/web-ui/build/static/vbscript-BNA4oANi.js +0 -1
  1007. package/web-ui/build/static/vbscript-html-o8ckLPKG.js +0 -1
  1008. package/web-ui/build/static/velocity-D7sc5ggA.js +0 -1
  1009. package/web-ui/build/static/verilog-BWJfMIng.js +0 -1
  1010. package/web-ui/build/static/verilog-jW2GPC--.js +0 -1
  1011. package/web-ui/build/static/vhdl-BIVlXRPa.js +0 -1
  1012. package/web-ui/build/static/vhdl-BeqdhhxD.js +0 -1
  1013. package/web-ui/build/static/vim-712lI4-g.js +0 -1
  1014. package/web-ui/build/static/vim-DMROTzr0.js +0 -1
  1015. package/web-ui/build/static/visual-basic-CQfbM-ta.js +0 -1
  1016. package/web-ui/build/static/warpscript-DFAvCXFQ.js +0 -1
  1017. package/web-ui/build/static/wasm-CiYoxBl0.js +0 -1
  1018. package/web-ui/build/static/web-idl-DoCkPK8y.js +0 -1
  1019. package/web-ui/build/static/wiki-bvz0AGzB.js +0 -1
  1020. package/web-ui/build/static/wolfram-fBuyFEgU.js +0 -1
  1021. package/web-ui/build/static/wren-DvATFxjF.js +0 -1
  1022. package/web-ui/build/static/x86asm-r4bPbUR_.js +0 -1
  1023. package/web-ui/build/static/xeora-B6iOnDJY.js +0 -1
  1024. package/web-ui/build/static/xl-Ce6B5slc.js +0 -1
  1025. package/web-ui/build/static/xml-DNjyPmhQ.js +0 -1
  1026. package/web-ui/build/static/xml-doc-DydogmZD.js +0 -1
  1027. package/web-ui/build/static/xojo-DCQLltvr.js +0 -1
  1028. package/web-ui/build/static/xquery-C-fAnA0H.js +0 -1
  1029. package/web-ui/build/static/xquery-CNeqzhLO.js +0 -1
  1030. package/web-ui/build/static/yaml-BsVaGsk5.js +0 -1
  1031. package/web-ui/build/static/yaml-kVZvwv_C.js +0 -1
  1032. package/web-ui/build/static/yang-B_gq9JEq.js +0 -1
  1033. package/web-ui/build/static/zephir-ECIXgXhX.js +0 -1
  1034. package/web-ui/build/static/zig-DY325EKG.js +0 -1
@@ -1,1315 +1,1373 @@
1
- /**
2
- * FileSystemTool - Handle file system operations safely
3
- *
4
- * Purpose:
5
- * - Read, write, and manipulate files
6
- * - Directory operations
7
- * - File metadata and permissions
8
- * - Safe file operations with validation
9
- */
10
-
11
- import { BaseTool } from './baseTool.js';
12
- import TagParser from '../utilities/tagParser.js';
13
- import DirectoryAccessManager from '../utilities/directoryAccessManager.js';
14
- import fs from 'fs/promises';
15
- import path from 'path';
16
- import crypto from 'crypto';
17
-
18
- import {
19
- TOOL_STATUS,
20
- FILE_EXTENSIONS,
21
- SYSTEM_DEFAULTS
22
- } from '../utilities/constants.js';
23
- import { validateForToolResponse } from '../utilities/structuredFileValidator.js';
24
- import { createTruncationNotice, getFileExtension } from '../utilities/jsonRepair.js';
25
-
26
- class FileSystemTool extends BaseTool {
27
- constructor(config = {}, logger = null) {
28
- super(config, logger);
29
-
30
- // Tool metadata
31
- this.requiresProject = true;
32
- this.isAsync = false; // Most file operations are quick
33
- this.timeout = config.timeout || 30000; // 30 seconds
34
- this.maxConcurrentOperations = config.maxConcurrentOperations || 5;
35
-
36
- // Security settings
37
- this.maxFileSize = config.maxFileSize || SYSTEM_DEFAULTS.MAX_FILE_SIZE;
38
- this.allowedExtensions = config.allowedExtensions || null; // null = all allowed
39
- this.blockedExtensions = config.blockedExtensions || ['.exe', '.scr', '.com'];
40
- this.allowedDirectories = config.allowedDirectories || null; // null = project dir only
41
-
42
- // Post-write validation for structured files (JSON, YAML, XML, etc.)
43
- // When enabled, validates structured files after writing and includes result in response
44
- this.validateStructuredFiles = config.validateStructuredFiles !== false; // enabled by default
45
-
46
- // File operation history
47
- this.operationHistory = [];
48
-
49
- // Directory access manager
50
- this.directoryAccessManager = new DirectoryAccessManager(config, logger);
51
- }
52
-
53
- /**
54
- * Get tool description for LLM consumption
55
- * @returns {string} Tool description
56
- */
57
- getDescription() {
58
- return `
59
- File System Tool: Perform file and directory operations safely within the project scope.
60
-
61
- USAGE:
62
- \`\`\`json
63
- {
64
- "toolId": "filesystem",
65
- "actions": [
66
- {"type": "read", "filePath": "src/index.js"},
67
- {"type": "write", "outputPath": "src/file.js", "content": "..."}
68
- ]
69
- }
70
- \`\`\`
71
-
72
- TIP: For exploring large or unfamiliar files, prefer the code-map tool (skeleton + read-range) to understand structure without reading entire files. Use filesystem read when you need the complete file content.
73
-
74
- SUPPORTED ACTIONS:
75
- - read: Read file contents (filePath)
76
- - write: Write content to file (outputPath, content)
77
- - append: Append content to existing file (filePath, content)
78
- - delete: Delete a file (filePath)
79
- - copy: Copy file (sourcePath, destPath)
80
- - move: Move/rename file (sourcePath, destPath)
81
- - create-dir: Create directory (directory)
82
- - list: List directory contents (directory)
83
- - exists: Check if file/directory exists (filePath)
84
- - stats: Get file/directory metadata (filePath)
85
-
86
- PARAMETERS:
87
- - filePath: Path to file (for read, delete, append, exists, stats)
88
- - outputPath: Path where to write/create file
89
- - sourcePath: Source path for copy/move operations
90
- - destPath: Destination path for copy/move operations
91
- - directory: Directory path for create-dir and list operations
92
- - content: Content to write/append
93
- - encoding: File encoding (default: utf8)
94
- - createDirs: Create parent directories if they don't exist (true/false)
95
-
96
- EXAMPLES:
97
-
98
- Write a file:
99
- \`\`\`json
100
- {
101
- "toolId": "filesystem",
102
- "actions": [{"type": "write", "outputPath": "hello.js", "content": "console.log('Hello');"}]
103
- }
104
- \`\`\`
105
-
106
- Read a file:
107
- \`\`\`json
108
- {
109
- "toolId": "filesystem",
110
- "actions": [{"type": "read", "filePath": "package.json"}]
111
- }
112
- \`\`\`
113
-
114
- Multiple operations:
115
- \`\`\`json
116
- {
117
- "toolId": "filesystem",
118
- "actions": [
119
- {"type": "read", "filePath": "src/index.js"},
120
- {"type": "copy", "sourcePath": "template.js", "destPath": "src/component.js"},
121
- {"type": "create-dir", "directory": "src/components/ui"}
122
- ]
123
- }
124
- \`\`\`
125
-
126
- SECURITY:
127
- - Operations restricted to project directory
128
- - File size limits enforced (max ${Math.round(this.maxFileSize / 1024 / 1024)}MB)
129
- - Dangerous file types blocked
130
- - Path traversal protection
131
- `;
132
- }
133
-
134
- /**
135
- * Parse parameters from tool command content
136
- * @param {string} content - Raw tool command content
137
- * @returns {Object} Parsed parameters
138
- */
139
- parseParameters(content) {
140
- try {
141
- const params = {};
142
- const actions = [];
143
-
144
- this.logger?.debug('FileSystem tool parsing parameters', {
145
- contentLength: content.length,
146
- contentPreview: content.substring(0, 200)
147
- });
148
-
149
- // Extract self-closing tags (read, delete, copy, etc.)
150
- const selfClosingTags = [
151
- 'read', 'delete', 'copy', 'move', 'create-dir',
152
- 'list', 'exists', 'stats', 'append'
153
- ];
154
-
155
- for (const tagName of selfClosingTags) {
156
- const tags = TagParser.extractTagsWithAttributes(content, tagName);
157
- for (const tag of tags) {
158
- const action = {
159
- type: tagName,
160
- ...tag.attributes
161
- };
162
-
163
- // Normalize common attribute names
164
- if (action['file-path']) {
165
- action.filePath = action['file-path'];
166
- delete action['file-path'];
167
- }
168
- if (action['output-path']) {
169
- action.outputPath = action['output-path'];
170
- delete action['output-path'];
171
- }
172
- if (action['source-path']) {
173
- action.sourcePath = action['source-path'];
174
- delete action['source-path'];
175
- }
176
- if (action['dest-path']) {
177
- action.destPath = action['dest-path'];
178
- delete action['dest-path'];
179
- }
180
- if (action['create-dirs']) {
181
- action.createDirs = action['create-dirs'] === 'true';
182
- delete action['create-dirs'];
183
- }
184
-
185
- actions.push(action);
186
- }
187
- }
188
-
189
- // Extract write and append tags with content
190
- const writeMatches = content.matchAll(/<write\s+([^>]*)>(.*?)<\/write>/gs);
191
- for (const match of writeMatches) {
192
- const parser = new TagParser();
193
- const attributes = parser.parseAttributes(match[1]);
194
- const writeContent = match[2].trim();
195
-
196
- const action = {
197
- type: 'write',
198
- content: writeContent,
199
- ...attributes
200
- };
201
-
202
- // Normalize attribute names
203
- if (action['output-path']) {
204
- action.outputPath = action['output-path'];
205
- delete action['output-path'];
206
- }
207
- if (action['create-dirs']) {
208
- action.createDirs = action['create-dirs'] === 'true';
209
- delete action['create-dirs'];
210
- }
211
-
212
- actions.push(action);
213
- }
214
-
215
- const appendMatches = content.matchAll(/<append\s+([^>]*)>(.*?)<\/append>/gs);
216
- for (const match of appendMatches) {
217
- const parser = new TagParser();
218
- const attributes = parser.parseAttributes(match[1]);
219
- const appendContent = match[2].trim();
220
-
221
- const action = {
222
- type: 'append',
223
- content: appendContent,
224
- ...attributes
225
- };
226
-
227
- // Normalize attribute names
228
- if (action['file-path']) {
229
- action.filePath = action['file-path'];
230
- delete action['file-path'];
231
- }
232
-
233
- actions.push(action);
234
- }
235
-
236
- params.actions = actions;
237
- params.rawContent = content.trim();
238
-
239
- this.logger?.debug('Parsed FileSystem tool parameters', {
240
- totalActions: actions.length,
241
- actionTypes: actions.map(a => a.type),
242
- actions: actions.map(a => ({
243
- type: a.type,
244
- filePath: a.filePath,
245
- outputPath: a.outputPath,
246
- hasContent: !!a.content
247
- }))
248
- });
249
-
250
- return params;
251
-
252
- } catch (error) {
253
- throw new Error(`Failed to parse filesystem parameters: ${error.message}`);
254
- }
255
- }
256
-
257
- /**
258
- * Get required parameters
259
- * @returns {Array<string>} Array of required parameter names
260
- */
261
- getRequiredParameters() {
262
- return ['actions'];
263
- }
264
-
265
- /**
266
- * Custom parameter validation
267
- * @param {Object} params - Parameters to validate
268
- * @returns {Object} Validation result
269
- */
270
- customValidateParameters(params) {
271
- const errors = [];
272
-
273
- if (!params.actions || !Array.isArray(params.actions) || params.actions.length === 0) {
274
- errors.push('At least one action is required');
275
- } else {
276
- // Validate each action
277
- for (const [index, action] of params.actions.entries()) {
278
- if (!action.type) {
279
- errors.push(`Action ${index + 1}: type is required`);
280
- continue;
281
- }
282
-
283
- switch (action.type) {
284
- case 'read':
285
- case 'delete':
286
- case 'exists':
287
- case 'stats':
288
- if (!action.filePath) {
289
- errors.push(`Action ${index + 1}: file-path is required for ${action.type}`);
290
- }
291
- break;
292
-
293
- case 'write':
294
- if (!action.outputPath) {
295
- errors.push(`Action ${index + 1}: output-path is required for write`);
296
- }
297
- // Content validation with detailed diagnostics
298
- if (action.content === undefined) {
299
- errors.push(`Action ${index + 1}: content is undefined for write (outputPath: ${action.outputPath || 'not set'})`);
300
- console.error('[FileSystemTool] Write validation failed: content is undefined', {
301
- outputPath: action.outputPath,
302
- actionKeys: Object.keys(action)
303
- });
304
- } else if (action.content === null) {
305
- errors.push(`Action ${index + 1}: content is null for write (outputPath: ${action.outputPath || 'not set'})`);
306
- console.error('[FileSystemTool] Write validation failed: content is null', {
307
- outputPath: action.outputPath
308
- });
309
- } else if (typeof action.content !== 'string') {
310
- errors.push(`Action ${index + 1}: content must be a string, got ${typeof action.content}`);
311
- console.error('[FileSystemTool] Write validation failed: content is not a string', {
312
- outputPath: action.outputPath,
313
- contentType: typeof action.content
314
- });
315
- }
316
- // Note: empty string content is allowed (creates empty file)
317
- break;
318
-
319
- case 'append':
320
- if (!action.filePath) {
321
- errors.push(`Action ${index + 1}: file-path is required for append`);
322
- }
323
- // Content validation with detailed diagnostics
324
- if (action.content === undefined) {
325
- errors.push(`Action ${index + 1}: content is undefined for append (filePath: ${action.filePath || 'not set'})`);
326
- console.error('[FileSystemTool] Append validation failed: content is undefined', {
327
- filePath: action.filePath,
328
- actionKeys: Object.keys(action)
329
- });
330
- } else if (action.content === null) {
331
- errors.push(`Action ${index + 1}: content is null for append (filePath: ${action.filePath || 'not set'})`);
332
- console.error('[FileSystemTool] Append validation failed: content is null', {
333
- filePath: action.filePath
334
- });
335
- } else if (typeof action.content !== 'string') {
336
- errors.push(`Action ${index + 1}: content must be a string, got ${typeof action.content}`);
337
- console.error('[FileSystemTool] Append validation failed: content is not a string', {
338
- filePath: action.filePath,
339
- contentType: typeof action.content
340
- });
341
- } else if (action.content.length === 0) {
342
- // Empty append is suspicious - log a warning but allow it
343
- console.warn('[FileSystemTool] Append with empty content', {
344
- filePath: action.filePath
345
- });
346
- }
347
- break;
348
-
349
- case 'copy':
350
- case 'move':
351
- if (!action.sourcePath) {
352
- errors.push(`Action ${index + 1}: source-path is required for ${action.type}`);
353
- }
354
- if (!action.destPath) {
355
- errors.push(`Action ${index + 1}: dest-path is required for ${action.type}`);
356
- }
357
- break;
358
-
359
- case 'create-dir':
360
- case 'list':
361
- if (!action.directory) {
362
- errors.push(`Action ${index + 1}: directory is required for ${action.type}`);
363
- }
364
- break;
365
-
366
- default:
367
- errors.push(`Action ${index + 1}: unknown action type: ${action.type}`);
368
- }
369
-
370
- // Validate file extensions if specified
371
- if (action.filePath && !this.isAllowedFileExtension(action.filePath)) {
372
- errors.push(`Action ${index + 1}: file type not allowed: ${path.extname(action.filePath)}`);
373
- }
374
- if (action.outputPath && !this.isAllowedFileExtension(action.outputPath)) {
375
- errors.push(`Action ${index + 1}: file type not allowed: ${path.extname(action.outputPath)}`);
376
- }
377
- }
378
- }
379
-
380
- return {
381
- valid: errors.length === 0,
382
- errors
383
- };
384
- }
385
-
386
- /**
387
- * Execute tool with parsed parameters
388
- * @param {Object} params - Parsed parameters
389
- * @param {Object} context - Execution context
390
- * @returns {Promise<Object>} Execution result
391
- */
392
- async execute(params, context) {
393
- // Validate params structure
394
- if (!params || typeof params !== 'object') {
395
- throw new Error('Invalid parameters: params must be an object');
396
- }
397
-
398
- const { actions } = params;
399
-
400
- // Validate actions array
401
- if (!actions) {
402
- throw new Error('Invalid parameters: actions is required. Received params: ' + JSON.stringify(Object.keys(params)));
403
- }
404
-
405
- if (!Array.isArray(actions)) {
406
- throw new Error('Invalid parameters: actions must be an array. Received type: ' + typeof actions);
407
- }
408
-
409
- if (actions.length === 0) {
410
- throw new Error('Invalid parameters: actions array is empty');
411
- }
412
-
413
- // CRITICAL: Run custom validation (not called by messageProcessor)
414
- // This ensures write/append actions have required content
415
- const validation = this.customValidateParameters(params);
416
- if (!validation.valid) {
417
- const errorMsg = validation.errors?.join('; ') || validation.error || 'Validation failed';
418
- console.error('[FileSystemTool] Parameter validation failed:', errorMsg);
419
- throw new Error(`Parameter validation failed: ${errorMsg}`);
420
- }
421
-
422
- const { projectDir, agentId, directoryAccess } = context;
423
-
424
- // Per-agent config overrides (agent.toolConfig.filesystem) merged
425
- // with this tool's global defaults. Applied per-action at the top of
426
- // the execute loop every action that touches a file path is
427
- // gated against the effective extension policy, and write/append
428
- // payloads are size-gated against the effective maxFileSize.
429
- const effectiveFs = this.getEffectiveConfig(context, {
430
- allowedExtensions: this.allowedExtensions,
431
- blockedExtensions: this.blockedExtensions,
432
- maxFileSize: this.maxFileSize,
433
- });
434
- const effectiveBlockedExt = Array.isArray(effectiveFs.blockedExtensions)
435
- ? effectiveFs.blockedExtensions.map(e => String(e).toLowerCase())
436
- : [];
437
- const effectiveAllowedExt = (Array.isArray(effectiveFs.allowedExtensions) && effectiveFs.allowedExtensions.length > 0)
438
- ? effectiveFs.allowedExtensions.map(e => String(e).toLowerCase())
439
- : null;
440
- const effectiveMaxFileSize = (typeof effectiveFs.maxFileSize === 'number' && effectiveFs.maxFileSize > 0)
441
- ? effectiveFs.maxFileSize
442
- : this.maxFileSize;
443
-
444
- const _checkExt = (filePath) => {
445
- if (!filePath) return null;
446
- const ext = (filePath.match(/\.[^./\\]+$/)?.[0] || '').toLowerCase();
447
- if (effectiveBlockedExt.includes(ext)) {
448
- return `File extension ${ext || '(none)'} is blocked by agent policy: ${filePath}`;
449
- }
450
- if (effectiveAllowedExt && !effectiveAllowedExt.includes(ext)) {
451
- return `File extension ${ext || '(none)'} is not in the agent's allowed list: ${filePath}`;
452
- }
453
- return null;
454
- };
455
-
456
- // Get directory access configuration from agent or create default
457
- const accessConfig = directoryAccess ||
458
- this.directoryAccessManager.createDirectoryAccess({
459
- workingDirectory: projectDir || process.cwd(),
460
- writeEnabledDirectories: [projectDir || process.cwd()],
461
- restrictToProject: true
462
- });
463
-
464
- // IMPORTANT: If the agent has directoryAccess configured, use its workingDirectory
465
- // This ensures UI-configured project directories are respected
466
- if (directoryAccess && directoryAccess.workingDirectory) {
467
- // Agent has explicitly configured working directory from UI - use it
468
- console.log('FileSystem DEBUG: Using agent configured working directory:', directoryAccess.workingDirectory);
469
- console.log('FileSystem DEBUG: Full directoryAccess object:', JSON.stringify(directoryAccess, null, 2));
470
- } else {
471
- // Using fallback to projectDir or process.cwd()
472
- console.log('FileSystem DEBUG: Using fallback working directory:', projectDir || process.cwd());
473
- console.log('FileSystem DEBUG: directoryAccess is:', directoryAccess);
474
- console.log('FileSystem DEBUG: projectDir is:', projectDir);
475
- }
476
-
477
- const results = [];
478
-
479
- for (const action of actions) {
480
- try {
481
- // Per-agent extension policy gate — runs BEFORE the action so
482
- // disallowed file types never reach the filesystem layer. Terminal
483
- // and web tools use the same short-circuit pattern.
484
- const pathsToCheck = [action.filePath, action.outputPath].filter(Boolean);
485
- for (const p of pathsToCheck) {
486
- const err = _checkExt(p);
487
- if (err) {
488
- results.push({ success: false, action: action.type, error: err });
489
- throw new Error('__skip');
490
- }
491
- }
492
- // Per-agent maxFileSize gate for write/append fires ONLY when
493
- // the agent explicitly overrides maxFileSize via toolConfig. When
494
- // the agent has no override, the existing `this.maxFileSize`
495
- // check downstream handles it with its original error message,
496
- // preserving backward compatibility. Read-side and stat-based
497
- // size checks still use `this.maxFileSize` at deeper call sites.
498
- const perAgentMaxFileSize = (typeof context?.toolConfig?.maxFileSize === 'number' && context.toolConfig.maxFileSize > 0)
499
- ? context.toolConfig.maxFileSize
500
- : null;
501
- if (perAgentMaxFileSize != null
502
- && (action.type === 'write' || action.type === 'append')
503
- && typeof action.content === 'string') {
504
- const payloadBytes = Buffer.byteLength(action.content, 'utf8');
505
- if (payloadBytes > perAgentMaxFileSize) {
506
- results.push({
507
- success: false,
508
- action: action.type,
509
- error: `Content too large: ${payloadBytes} bytes exceeds per-agent maxFileSize (${perAgentMaxFileSize})`,
510
- });
511
- throw new Error('__skip');
512
- }
513
- }
514
-
515
- let result;
516
-
517
- switch (action.type) {
518
- case 'read':
519
- result = await this.readFile(action.filePath, accessConfig, action.encoding);
520
- break;
521
-
522
- case 'write':
523
- result = await this.writeFile(action.outputPath, action.content, accessConfig, {
524
- encoding: action.encoding,
525
- createDirs: action.createDirs,
526
- wasTruncated: context.wasTruncated || false
527
- });
528
- break;
529
-
530
- case 'append':
531
- result = await this.appendToFile(action.filePath, action.content, accessConfig, action.encoding);
532
- break;
533
-
534
- case 'delete':
535
- result = await this.deleteFile(action.filePath, accessConfig);
536
- break;
537
-
538
- case 'copy':
539
- result = await this.copyFile(action.sourcePath, action.destPath, accessConfig);
540
- break;
541
-
542
- case 'move':
543
- result = await this.moveFile(action.sourcePath, action.destPath, accessConfig);
544
- break;
545
-
546
- case 'create-dir':
547
- result = await this.createDirectory(action.directory, accessConfig);
548
- break;
549
-
550
- case 'list':
551
- result = await this.listDirectory(action.directory, accessConfig);
552
- break;
553
-
554
- case 'exists':
555
- result = await this.checkExists(action.filePath, accessConfig);
556
- break;
557
-
558
- case 'stats':
559
- result = await this.getFileStats(action.filePath, accessConfig);
560
- break;
561
-
562
- default:
563
- throw new Error(`Unknown action type: ${action.type}`);
564
- }
565
-
566
- results.push(result);
567
- this.addToHistory(action, result, context.agentId);
568
-
569
- } catch (error) {
570
- // Per-agent policy gate above uses "__skip" to signal that it
571
- // already pushed a useful error onto results — don't double-log
572
- // or double-push.
573
- if (error.message === '__skip') {
574
- this.addToHistory(action, results[results.length - 1], context.agentId);
575
- continue;
576
- }
577
-
578
- // Log detailed error for debugging
579
- console.error(`[FileSystemTool] Action '${action.type}' failed:`, {
580
- action: action.type,
581
- filePath: action.filePath || action.outputPath || action.directory,
582
- error: error.message,
583
- stack: error.stack?.split('\n').slice(0, 3).join('\n'),
584
- contentLength: action.content?.length,
585
- hasContent: action.content !== undefined && action.content !== null
586
- });
587
-
588
- const errorResult = {
589
- success: false,
590
- action: action.type,
591
- error: error.message,
592
- filePath: action.filePath || action.outputPath || action.directory,
593
- // Include debug info for troubleshooting
594
- debug: {
595
- contentProvided: action.content !== undefined && action.content !== null,
596
- contentLength: action.content?.length || 0,
597
- contentPreview: action.content ? action.content.substring(0, 100) + (action.content.length > 100 ? '...' : '') : null
598
- }
599
- };
600
-
601
- results.push(errorResult);
602
- this.addToHistory(action, errorResult, context.agentId);
603
- }
604
- }
605
-
606
- // Determine overall success - only true if ALL actions succeeded
607
- const allSucceeded = results.every(r => r.success === true);
608
- const failedCount = results.filter(r => r.success === false).length;
609
-
610
- return {
611
- success: allSucceeded,
612
- actions: results,
613
- executedActions: actions.length,
614
- successfulActions: actions.length - failedCount,
615
- failedActions: failedCount,
616
- toolUsed: 'filesys',
617
- ...(failedCount > 0 && {
618
- warning: `${failedCount} of ${actions.length} action(s) failed. Check individual action results for details.`
619
- })
620
- };
621
- }
622
-
623
- /**
624
- * Read file contents
625
- * @private
626
- */
627
- async readFile(filePath, accessConfig, encoding = 'utf8') {
628
- const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
629
- const fullPath = this.resolvePath(filePath, workingDir);
630
-
631
- // Validate read access using DirectoryAccessManager
632
- const accessResult = this.directoryAccessManager.validateReadAccess(fullPath, accessConfig);
633
- if (!accessResult.allowed) {
634
- throw new Error(`Read access denied: ${accessResult.reason} (${accessResult.path})`);
635
- }
636
-
637
- try {
638
- const stats = await fs.stat(fullPath);
639
-
640
- if (stats.size > this.maxFileSize) {
641
- throw new Error(`File too large: ${stats.size} bytes (max ${this.maxFileSize})`);
642
- }
643
-
644
- const content = await fs.readFile(fullPath, encoding);
645
-
646
- return {
647
- success: true,
648
- action: 'read',
649
- filePath: this.directoryAccessManager.createRelativePath(fullPath, accessConfig),
650
- content,
651
- size: stats.size,
652
- encoding,
653
- lastModified: stats.mtime.toISOString(),
654
- message: `Read ${stats.size} bytes from ${filePath}`
655
- };
656
-
657
- } catch (error) {
658
- throw new Error(`Failed to read file ${filePath}: ${error.message}`);
659
- }
660
- }
661
-
662
- /**
663
- * Write content to file
664
- * @private
665
- */
666
- async writeFile(outputPath, content, accessConfig, options = {}) {
667
- const { encoding = 'utf8', createDirs = true, wasTruncated = false } = options;
668
- const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
669
- const fullPath = this.resolvePath(outputPath, workingDir);
670
-
671
- // Validate write access using DirectoryAccessManager
672
- const accessResult = this.directoryAccessManager.validateWriteAccess(fullPath, accessConfig);
673
- if (!accessResult.allowed) {
674
- throw new Error(`Write access denied: ${accessResult.reason} (${accessResult.path})`);
675
- }
676
-
677
- // Handle truncated content - append notice if AI response was cut off
678
- let finalContent = content;
679
- let truncationApplied = false;
680
-
681
- if (wasTruncated && content) {
682
- const fileExt = getFileExtension(outputPath);
683
- const truncationNotice = createTruncationNotice(fileExt);
684
- if (truncationNotice) {
685
- finalContent = content + truncationNotice;
686
- truncationApplied = true;
687
- console.log(`[FileSystemTool] Appending truncation notice to ${outputPath} (AI response was truncated)`);
688
- }
689
- }
690
-
691
- try {
692
- // Check content size
693
- const contentSize = Buffer.byteLength(finalContent, encoding);
694
- if (contentSize > this.maxFileSize) {
695
- throw new Error(`Content too large: ${contentSize} bytes (max ${this.maxFileSize})`);
696
- }
697
-
698
- // Create parent directories if requested
699
- if (createDirs) {
700
- const dirPath = path.dirname(fullPath);
701
- await fs.mkdir(dirPath, { recursive: true });
702
- }
703
-
704
- // Create backup if file exists
705
- let backupPath = null;
706
- try {
707
- await fs.access(fullPath);
708
- backupPath = `${fullPath}.backup-${Date.now()}`;
709
- await fs.copyFile(fullPath, backupPath);
710
- } catch {
711
- // File doesn't exist, no backup needed
712
- }
713
-
714
- await fs.writeFile(fullPath, finalContent, encoding);
715
-
716
- // VERIFICATION: Confirm file was written correctly
717
- const stats = await fs.stat(fullPath);
718
-
719
- // Verify file size matches expected content size
720
- const expectedSize = Buffer.byteLength(finalContent, encoding);
721
- if (stats.size !== expectedSize) {
722
- throw new Error(`Write verification failed: expected ${expectedSize} bytes but file is ${stats.size} bytes`);
723
- }
724
-
725
- // For text files, verify content was written (read back and compare hash)
726
- if (encoding === 'utf8' || encoding === 'utf-8') {
727
- const writtenContent = await fs.readFile(fullPath, encoding);
728
- if (writtenContent !== finalContent) {
729
- throw new Error(`Write verification failed: content mismatch after write`);
730
- }
731
- }
732
-
733
- const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
734
-
735
- // Build base result
736
- const result = {
737
- success: true,
738
- action: 'write',
739
- outputPath: relativePath,
740
- fullPath: fullPath,
741
- size: stats.size,
742
- encoding,
743
- verified: true, // Content was verified after write
744
- backupPath: backupPath ? this.directoryAccessManager.createRelativePath(backupPath, accessConfig) : null,
745
- backupFullPath: backupPath || null,
746
- message: `Wrote ${stats.size} bytes to ${fullPath} (verified)`,
747
- // Include truncation info if applicable
748
- ...(truncationApplied && {
749
- wasTruncated: true,
750
- truncationWarning: 'Content was truncated due to AI response token limit. A notice was appended to the file.'
751
- }),
752
- // Warn if content is empty
753
- ...(stats.size === 0 && {
754
- emptyContent: true,
755
- emptyWarning: 'File was written with empty content'
756
- })
757
- };
758
-
759
- // Add truncation warning to message
760
- if (truncationApplied) {
761
- result.message += ' [PARTIAL: AI response was truncated - content may be incomplete]';
762
- }
763
-
764
- // Post-write validation for structured files (plug-and-play via structuredFileValidator)
765
- if (this.validateStructuredFiles) {
766
- const validation = validateForToolResponse(content, fullPath);
767
- if (validation) {
768
- result.validation = validation;
769
- // Add warning to message if validation failed
770
- if (!validation.valid) {
771
- result.message += ` [WARNING: ${validation.format.toUpperCase()} validation failed with ${validation.errorCount} error(s)]`;
772
- }
773
- }
774
- }
775
-
776
- return result;
777
-
778
- } catch (error) {
779
- throw new Error(`Failed to write file ${fullPath}: ${error.message}`);
780
- }
781
- }
782
-
783
- /**
784
- * Append content to file
785
- * @private
786
- */
787
- async appendToFile(filePath, content, accessConfig, encoding = 'utf8') {
788
- const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
789
- const fullPath = this.resolvePath(filePath, workingDir);
790
-
791
- // Validate write access using DirectoryAccessManager
792
- const accessResult = this.directoryAccessManager.validateWriteAccess(fullPath, accessConfig);
793
- if (!accessResult.allowed) {
794
- throw new Error(`Write access denied: ${accessResult.reason} (${accessResult.path})`);
795
- }
796
-
797
- try {
798
- // Check if file exists and get current size
799
- let currentSize = 0;
800
- try {
801
- const stats = await fs.stat(fullPath);
802
- currentSize = stats.size;
803
- } catch {
804
- // File doesn't exist, will be created
805
- }
806
-
807
- const contentSize = Buffer.byteLength(content, encoding);
808
- if (currentSize + contentSize > this.maxFileSize) {
809
- throw new Error(`File would become too large: ${currentSize + contentSize} bytes (max ${this.maxFileSize})`);
810
- }
811
-
812
- // Store size before append for verification
813
- const sizeBefore = currentSize;
814
-
815
- await fs.appendFile(fullPath, content, encoding);
816
-
817
- const stats = await fs.stat(fullPath);
818
- const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
819
-
820
- // VERIFICATION: Confirm append actually happened
821
- const expectedSize = sizeBefore + contentSize;
822
- if (stats.size < expectedSize) {
823
- throw new Error(`Append verification failed: expected at least ${expectedSize} bytes but file is ${stats.size} bytes`);
824
- }
825
-
826
- // For text files, verify the appended content is at the end
827
- if (encoding === 'utf8' || encoding === 'utf-8') {
828
- const fileContent = await fs.readFile(fullPath, encoding);
829
- if (!fileContent.endsWith(content)) {
830
- throw new Error(`Append verification failed: appended content not found at end of file`);
831
- }
832
- }
833
-
834
- return {
835
- success: true,
836
- action: 'append',
837
- filePath: relativePath,
838
- fullPath: fullPath,
839
- appendedBytes: contentSize,
840
- totalSize: stats.size,
841
- sizeBefore: sizeBefore,
842
- encoding,
843
- verified: true,
844
- message: `Appended ${contentSize} bytes to ${fullPath} (verified)`
845
- };
846
-
847
- } catch (error) {
848
- throw new Error(`Failed to append to file ${fullPath}: ${error.message}`);
849
- }
850
- }
851
-
852
- /**
853
- * Delete file
854
- * @private
855
- */
856
- async deleteFile(filePath, accessConfig) {
857
- const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
858
- const fullPath = this.resolvePath(filePath, workingDir);
859
-
860
- // Validate write access for deletion
861
- const accessResult = this.directoryAccessManager.validateWriteAccess(fullPath, accessConfig);
862
- if (!accessResult.allowed) {
863
- throw new Error(`Delete access denied: ${accessResult.reason} (${accessResult.path})`);
864
- }
865
-
866
- try {
867
- const stats = await fs.stat(fullPath);
868
-
869
- // Create backup before deletion
870
- const backupPath = `${fullPath}.deleted-backup-${Date.now()}`;
871
- await fs.copyFile(fullPath, backupPath);
872
-
873
- await fs.unlink(fullPath);
874
-
875
- const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
876
- const backupRelativePath = this.directoryAccessManager.createRelativePath(backupPath, accessConfig);
877
-
878
- return {
879
- success: true,
880
- action: 'delete',
881
- filePath: relativePath,
882
- fullPath: fullPath,
883
- size: stats.size,
884
- backupPath: backupRelativePath,
885
- backupFullPath: backupPath,
886
- message: `Deleted ${fullPath} (backup created)`
887
- };
888
-
889
- } catch (error) {
890
- throw new Error(`Failed to delete file ${fullPath}: ${error.message}`);
891
- }
892
- }
893
-
894
- /**
895
- * Copy file
896
- * @private
897
- */
898
- async copyFile(sourcePath, destPath, accessConfig) {
899
- const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
900
- const fullSourcePath = this.resolvePath(sourcePath, workingDir);
901
- const fullDestPath = this.resolvePath(destPath, workingDir);
902
-
903
- // Validate read access for source
904
- const sourceAccessResult = this.directoryAccessManager.validateReadAccess(fullSourcePath, accessConfig);
905
- if (!sourceAccessResult.allowed) {
906
- throw new Error(`Source read access denied: ${sourceAccessResult.reason} (${sourceAccessResult.path})`);
907
- }
908
-
909
- // Validate write access for destination
910
- const destAccessResult = this.directoryAccessManager.validateWriteAccess(fullDestPath, accessConfig);
911
- if (!destAccessResult.allowed) {
912
- throw new Error(`Destination write access denied: ${destAccessResult.reason} (${destAccessResult.path})`);
913
- }
914
-
915
- try {
916
- const sourceStats = await fs.stat(fullSourcePath);
917
-
918
- if (sourceStats.size > this.maxFileSize) {
919
- throw new Error(`Source file too large: ${sourceStats.size} bytes`);
920
- }
921
-
922
- // Create destination directory if needed
923
- const destDir = path.dirname(fullDestPath);
924
- await fs.mkdir(destDir, { recursive: true });
925
-
926
- await fs.copyFile(fullSourcePath, fullDestPath);
927
-
928
- const sourceRelativePath = this.directoryAccessManager.createRelativePath(fullSourcePath, accessConfig);
929
- const destRelativePath = this.directoryAccessManager.createRelativePath(fullDestPath, accessConfig);
930
-
931
- return {
932
- success: true,
933
- action: 'copy',
934
- sourcePath: sourceRelativePath,
935
- destPath: destRelativePath,
936
- sourceFullPath: fullSourcePath,
937
- destFullPath: fullDestPath,
938
- size: sourceStats.size,
939
- message: `Copied ${fullSourcePath} to ${fullDestPath}`
940
- };
941
-
942
- } catch (error) {
943
- throw new Error(`Failed to copy ${fullSourcePath} to ${fullDestPath}: ${error.message}`);
944
- }
945
- }
946
-
947
- /**
948
- * Move/rename file
949
- * @private
950
- */
951
- async moveFile(sourcePath, destPath, accessConfig) {
952
- const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
953
- const fullSourcePath = this.resolvePath(sourcePath, workingDir);
954
- const fullDestPath = this.resolvePath(destPath, workingDir);
955
-
956
- // Validate read access for source
957
- const readResult = this.directoryAccessManager.validateReadAccess(fullSourcePath, accessConfig);
958
- if (!readResult.allowed) {
959
- throw new Error(`Read access denied for source: ${readResult.reason} (${readResult.path})`);
960
- }
961
-
962
- // Validate write access for destination
963
- const writeResult = this.directoryAccessManager.validateWriteAccess(fullDestPath, accessConfig);
964
- if (!writeResult.allowed) {
965
- throw new Error(`Write access denied for destination: ${writeResult.reason} (${writeResult.path})`);
966
- }
967
-
968
- try {
969
- const sourceStats = await fs.stat(fullSourcePath);
970
-
971
- // Create destination directory if needed
972
- const destDir = path.dirname(fullDestPath);
973
- await fs.mkdir(destDir, { recursive: true });
974
-
975
- await fs.rename(fullSourcePath, fullDestPath);
976
-
977
- const relativeSource = this.directoryAccessManager.createRelativePath(fullSourcePath, accessConfig);
978
- const relativeDest = this.directoryAccessManager.createRelativePath(fullDestPath, accessConfig);
979
-
980
- return {
981
- success: true,
982
- action: 'move',
983
- sourcePath: relativeSource,
984
- destPath: relativeDest,
985
- fullSourcePath: fullSourcePath,
986
- fullDestPath: fullDestPath,
987
- size: sourceStats.size,
988
- message: `Moved ${sourcePath} to ${destPath}`
989
- };
990
-
991
- } catch (error) {
992
- throw new Error(`Failed to move ${sourcePath} to ${destPath}: ${error.message}`);
993
- }
994
- }
995
-
996
- /**
997
- * Create directory
998
- * @private
999
- */
1000
- async createDirectory(directory, accessConfig) {
1001
- const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
1002
- const fullPath = this.resolvePath(directory, workingDir);
1003
-
1004
- // Validate write access using DirectoryAccessManager
1005
- const accessResult = this.directoryAccessManager.validateWriteAccess(fullPath, accessConfig);
1006
- if (!accessResult.allowed) {
1007
- throw new Error(`Write access denied: ${accessResult.reason} (${accessResult.path})`);
1008
- }
1009
-
1010
- try {
1011
- await fs.mkdir(fullPath, { recursive: true });
1012
-
1013
- const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
1014
-
1015
- return {
1016
- success: true,
1017
- action: 'create-dir',
1018
- directory: relativePath,
1019
- fullPath: fullPath,
1020
- message: `Created directory ${directory}`
1021
- };
1022
-
1023
- } catch (error) {
1024
- throw new Error(`Failed to create directory ${directory}: ${error.message}`);
1025
- }
1026
- }
1027
-
1028
- /**
1029
- * List directory contents
1030
- * @private
1031
- */
1032
- async listDirectory(directory, accessConfig) {
1033
- const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
1034
- const fullPath = this.resolvePath(directory, workingDir);
1035
-
1036
- // Validate read access using DirectoryAccessManager
1037
- const accessResult = this.directoryAccessManager.validateReadAccess(fullPath, accessConfig);
1038
- if (!accessResult.allowed) {
1039
- throw new Error(`Read access denied: ${accessResult.reason} (${accessResult.path})`);
1040
- }
1041
-
1042
- try {
1043
- const entries = await fs.readdir(fullPath, { withFileTypes: true });
1044
-
1045
- const contents = [];
1046
- for (const entry of entries) {
1047
- const entryPath = path.join(fullPath, entry.name);
1048
- const stats = await fs.stat(entryPath);
1049
-
1050
- contents.push({
1051
- name: entry.name,
1052
- type: entry.isDirectory() ? 'directory' : 'file',
1053
- size: entry.isFile() ? stats.size : undefined,
1054
- lastModified: stats.mtime.toISOString(),
1055
- permissions: stats.mode,
1056
- isSymlink: entry.isSymbolicLink()
1057
- });
1058
- }
1059
-
1060
- const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
1061
-
1062
- return {
1063
- success: true,
1064
- action: 'list',
1065
- directory: relativePath,
1066
- fullPath: fullPath,
1067
- contents,
1068
- totalItems: contents.length,
1069
- directories: contents.filter(item => item.type === 'directory').length,
1070
- files: contents.filter(item => item.type === 'file').length,
1071
- message: `Listed ${contents.length} items in ${directory}`
1072
- };
1073
-
1074
- } catch (error) {
1075
- throw new Error(`Failed to list directory ${directory}: ${error.message}`);
1076
- }
1077
- }
1078
-
1079
- /**
1080
- * Check if file/directory exists
1081
- * @private
1082
- */
1083
- async checkExists(filePath, accessConfig) {
1084
- const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
1085
- const fullPath = this.resolvePath(filePath, workingDir);
1086
-
1087
- // Validate read access
1088
- const accessResult = this.directoryAccessManager.validateReadAccess(fullPath, accessConfig);
1089
- if (!accessResult.allowed) {
1090
- throw new Error(`Read access denied: ${accessResult.reason} (${accessResult.path})`);
1091
- }
1092
-
1093
- try {
1094
- const stats = await fs.stat(fullPath);
1095
-
1096
- const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
1097
-
1098
- return {
1099
- success: true,
1100
- action: 'exists',
1101
- filePath: relativePath,
1102
- fullPath: fullPath,
1103
- exists: true,
1104
- type: stats.isDirectory() ? 'directory' : 'file',
1105
- message: `${filePath} exists as ${stats.isDirectory() ? 'directory' : 'file'}`
1106
- };
1107
-
1108
- } catch (error) {
1109
- if (error.code === 'ENOENT') {
1110
- const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
1111
- return {
1112
- success: true,
1113
- action: 'exists',
1114
- filePath: relativePath,
1115
- fullPath: fullPath,
1116
- exists: false,
1117
- message: `${filePath} does not exist`
1118
- };
1119
- }
1120
-
1121
- throw new Error(`Failed to check existence of ${filePath}: ${error.message}`);
1122
- }
1123
- }
1124
-
1125
- /**
1126
- * Get file statistics
1127
- * @private
1128
- */
1129
- async getFileStats(filePath, accessConfig) {
1130
- const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
1131
- const fullPath = this.resolvePath(filePath, workingDir);
1132
-
1133
- // Validate read access
1134
- const accessResult = this.directoryAccessManager.validateReadAccess(fullPath, accessConfig);
1135
- if (!accessResult.allowed) {
1136
- throw new Error(`Read access denied: ${accessResult.reason} (${accessResult.path})`);
1137
- }
1138
-
1139
- try {
1140
- const stats = await fs.stat(fullPath);
1141
-
1142
- const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
1143
-
1144
- return {
1145
- success: true,
1146
- action: 'stats',
1147
- filePath: relativePath,
1148
- fullPath: fullPath,
1149
- stats: {
1150
- size: stats.size,
1151
- type: stats.isDirectory() ? 'directory' : 'file',
1152
- lastModified: stats.mtime.toISOString(),
1153
- lastAccessed: stats.atime.toISOString(),
1154
- created: stats.birthtime.toISOString(),
1155
- permissions: stats.mode,
1156
- isSymlink: stats.isSymbolicLink()
1157
- },
1158
- message: `Retrieved stats for ${filePath}`
1159
- };
1160
-
1161
- } catch (error) {
1162
- throw new Error(`Failed to get stats for ${filePath}: ${error.message}`);
1163
- }
1164
- }
1165
-
1166
- /**
1167
- * Resolve file path safely (legacy method for compatibility)
1168
- * @private
1169
- */
1170
- resolvePath(filePath, workingDir) {
1171
- const resolved = path.isAbsolute(filePath)
1172
- ? path.normalize(filePath)
1173
- : path.resolve(workingDir, filePath);
1174
-
1175
- // Guard against the "doubled folder" trap. Reasoning models lose track
1176
- // of the session CWD after a change-directory and re-prepend the
1177
- // project folder name to every relative path — turning
1178
- // CWD=".../foo" + "foo/bar/x.js" into ".../foo/foo/bar/x.js", which
1179
- // the filesystem silently creates. We detect this by scanning the
1180
- // FINAL resolved path for consecutive duplicate segments, so the
1181
- // guard fires whether the duplication came from (a) relative prefix
1182
- // (`foo/...` while inside `.../foo`), (b) relative mid-path
1183
- // (`pkg/foo/foo/bar`), or (c) an absolute path written out in full
1184
- // (`C:\...\foo\foo\bar`). Root-level duplicates like Windows drives
1185
- // are ignored by design (`C:\C:\...` is impossible in normalized form).
1186
- const segs = resolved.split(/[\\/]/).filter(Boolean);
1187
- for (let i = 1; i < segs.length; i++) {
1188
- if (segs[i].toLowerCase() === segs[i - 1].toLowerCase()) {
1189
- throw new Error(
1190
- `Refused: resolved path contains a duplicated segment "${segs[i]}" (${resolved}). ` +
1191
- `Current working directory: ${workingDir}. ` +
1192
- `This almost always means the project folder name was prepended to a relative path while the CWD is already inside it. ` +
1193
- `Drop the duplicated "${segs[i]}/" from the path — relative paths are resolved from the CWD — or pass an absolute path with no duplication.`
1194
- );
1195
- }
1196
- }
1197
-
1198
- return resolved;
1199
- }
1200
-
1201
- /**
1202
- * Validate path access using DirectoryAccessManager
1203
- * @private
1204
- */
1205
- validatePathAccess(fullPath, accessConfig, operation = 'read') {
1206
- const accessResult = operation === 'write'
1207
- ? this.directoryAccessManager.validateWriteAccess(fullPath, accessConfig)
1208
- : this.directoryAccessManager.validateReadAccess(fullPath, accessConfig);
1209
-
1210
- if (!accessResult.allowed) {
1211
- throw new Error(`${operation} access denied: ${accessResult.reason} (${accessResult.path})`);
1212
- }
1213
-
1214
- return accessResult;
1215
- }
1216
-
1217
- /**
1218
- * Check if file extension is allowed
1219
- * @private
1220
- */
1221
- isAllowedFileExtension(filePath) {
1222
- const ext = path.extname(filePath).toLowerCase();
1223
-
1224
- if (this.blockedExtensions.includes(ext)) {
1225
- return false;
1226
- }
1227
-
1228
- if (this.allowedExtensions && !this.allowedExtensions.includes(ext)) {
1229
- return false;
1230
- }
1231
-
1232
- return true;
1233
- }
1234
-
1235
- /**
1236
- * Add operation to history
1237
- * @private
1238
- */
1239
- addToHistory(action, result, agentId) {
1240
- const historyEntry = {
1241
- timestamp: new Date().toISOString(),
1242
- agentId,
1243
- action: action.type,
1244
- filePath: action.filePath || action.outputPath || action.directory,
1245
- success: result.success,
1246
- size: result.size
1247
- };
1248
-
1249
- this.operationHistory.push(historyEntry);
1250
-
1251
- // Keep only last 200 entries
1252
- if (this.operationHistory.length > 200) {
1253
- this.operationHistory = this.operationHistory.slice(-200);
1254
- }
1255
- }
1256
-
1257
- /**
1258
- * Get supported actions for this tool
1259
- * @returns {Array<string>} Array of supported action names
1260
- */
1261
- getSupportedActions() {
1262
- return [
1263
- 'read', 'write', 'append', 'delete', 'copy', 'move',
1264
- 'create-dir', 'list', 'exists', 'stats'
1265
- ];
1266
- }
1267
-
1268
- /**
1269
- * Get parameter schema for validation
1270
- * @returns {Object} Parameter schema
1271
- */
1272
- getParameterSchema() {
1273
- return {
1274
- type: 'object',
1275
- properties: {
1276
- actions: {
1277
- type: 'array',
1278
- minItems: 1,
1279
- items: {
1280
- type: 'object',
1281
- properties: {
1282
- type: {
1283
- type: 'string',
1284
- enum: this.getSupportedActions()
1285
- },
1286
- filePath: { type: 'string' },
1287
- outputPath: { type: 'string' },
1288
- sourcePath: { type: 'string' },
1289
- destPath: { type: 'string' },
1290
- directory: { type: 'string' },
1291
- content: { type: 'string' },
1292
- encoding: { type: 'string' },
1293
- createDirs: { type: 'boolean' }
1294
- },
1295
- required: ['type']
1296
- }
1297
- }
1298
- },
1299
- required: ['actions']
1300
- };
1301
- }
1302
-
1303
- /**
1304
- * Get operation history for debugging
1305
- * @returns {Array} Operation history
1306
- */
1307
- getOperationHistory(agentId = null) {
1308
- if (agentId) {
1309
- return this.operationHistory.filter(entry => entry.agentId === agentId);
1310
- }
1311
- return [...this.operationHistory];
1312
- }
1313
- }
1314
-
1
+ /**
2
+ * FileSystemTool - Handle file system operations safely
3
+ *
4
+ * Purpose:
5
+ * - Read, write, and manipulate files
6
+ * - Directory operations
7
+ * - File metadata and permissions
8
+ * - Safe file operations with validation
9
+ */
10
+
11
+ import { BaseTool } from './baseTool.js';
12
+ import TagParser from '../utilities/tagParser.js';
13
+ import DirectoryAccessManager from '../utilities/directoryAccessManager.js';
14
+ import fs from 'fs/promises';
15
+ import path from 'path';
16
+ import crypto from 'crypto';
17
+
18
+ import {
19
+ TOOL_STATUS,
20
+ FILE_EXTENSIONS,
21
+ SYSTEM_DEFAULTS
22
+ } from '../utilities/constants.js';
23
+ import { validateForToolResponse } from '../utilities/structuredFileValidator.js';
24
+ import { createTruncationNotice, getFileExtension } from '../utilities/jsonRepair.js';
25
+
26
+ class FileSystemTool extends BaseTool {
27
+ constructor(config = {}, logger = null) {
28
+ super(config, logger);
29
+
30
+ // Tool metadata
31
+ this.requiresProject = true;
32
+ this.isAsync = false; // Most file operations are quick
33
+ this.timeout = config.timeout || 30000; // 30 seconds
34
+ this.maxConcurrentOperations = config.maxConcurrentOperations || 5;
35
+
36
+ // Security settings
37
+ this.maxFileSize = config.maxFileSize || SYSTEM_DEFAULTS.MAX_FILE_SIZE;
38
+ this.allowedExtensions = config.allowedExtensions || null; // null = all allowed
39
+ this.blockedExtensions = config.blockedExtensions || ['.exe', '.scr', '.com'];
40
+ this.allowedDirectories = config.allowedDirectories || null; // null = project dir only
41
+
42
+ // Post-write validation for structured files (JSON, YAML, XML, etc.)
43
+ // When enabled, validates structured files after writing and includes result in response
44
+ this.validateStructuredFiles = config.validateStructuredFiles !== false; // enabled by default
45
+
46
+ // File operation history
47
+ this.operationHistory = [];
48
+
49
+ // Directory access manager
50
+ this.directoryAccessManager = new DirectoryAccessManager(config, logger);
51
+ }
52
+
53
+ /**
54
+ * Get tool description for LLM consumption
55
+ * @returns {string} Tool description
56
+ */
57
+ getDescription() {
58
+ return `
59
+ File System Tool: Perform file and directory operations safely within the project scope.
60
+
61
+ USAGE:
62
+ \`\`\`json
63
+ {
64
+ "toolId": "filesystem",
65
+ "actions": [
66
+ {"type": "read", "filePath": "src/index.js"},
67
+ {"type": "write", "outputPath": "src/file.js", "content": "..."}
68
+ ]
69
+ }
70
+ \`\`\`
71
+
72
+ TIP: For exploring large or unfamiliar files, prefer the code-map tool (skeleton + read-range) to understand structure without reading entire files. Use filesystem read when you need the complete file content.
73
+
74
+ SUPPORTED ACTIONS:
75
+ - read: Read file contents (filePath)
76
+ - write: Write content to file (outputPath, content)
77
+ - append: Append content to existing file (filePath, content)
78
+ - delete: Delete a file (filePath)
79
+ - copy: Copy file (sourcePath, destPath)
80
+ - move: Move/rename file (sourcePath, destPath)
81
+ - create-dir: Create directory (directory)
82
+ - list: List directory contents (directory)
83
+ - exists: Check if file/directory exists (filePath)
84
+ - stats: Get file/directory metadata (filePath)
85
+
86
+ PARAMETERS:
87
+ - filePath: Path to file (for read, delete, append, exists, stats)
88
+ - outputPath: Path where to write/create file
89
+ - sourcePath: Source path for copy/move operations
90
+ - destPath: Destination path for copy/move operations
91
+ - directory: Directory path for create-dir and list operations
92
+ - content: Content to write/append
93
+ - encoding: File encoding (default: utf8)
94
+ - createDirs: Create parent directories if they don't exist (true/false)
95
+
96
+ EXAMPLES:
97
+
98
+ Write a file:
99
+ \`\`\`json
100
+ {
101
+ "toolId": "filesystem",
102
+ "actions": [{"type": "write", "outputPath": "hello.js", "content": "console.log('Hello');"}]
103
+ }
104
+ \`\`\`
105
+
106
+ Read a file:
107
+ \`\`\`json
108
+ {
109
+ "toolId": "filesystem",
110
+ "actions": [{"type": "read", "filePath": "package.json"}]
111
+ }
112
+ \`\`\`
113
+
114
+ Multiple operations:
115
+ \`\`\`json
116
+ {
117
+ "toolId": "filesystem",
118
+ "actions": [
119
+ {"type": "read", "filePath": "src/index.js"},
120
+ {"type": "copy", "sourcePath": "template.js", "destPath": "src/component.js"},
121
+ {"type": "create-dir", "directory": "src/components/ui"}
122
+ ]
123
+ }
124
+ \`\`\`
125
+
126
+ SECURITY:
127
+ - Operations restricted to project directory
128
+ - File size limits enforced (max ${Math.round(this.maxFileSize / 1024 / 1024)}MB)
129
+ - Dangerous file types blocked
130
+ - Path traversal protection
131
+ `;
132
+ }
133
+
134
+ /**
135
+ * Parse parameters from tool command content
136
+ * @param {string} content - Raw tool command content
137
+ * @returns {Object} Parsed parameters
138
+ */
139
+ parseParameters(content) {
140
+ try {
141
+ const params = {};
142
+ const actions = [];
143
+
144
+ this.logger?.debug('FileSystem tool parsing parameters', {
145
+ contentLength: content.length,
146
+ contentPreview: content.substring(0, 200)
147
+ });
148
+
149
+ // Extract self-closing tags (read, delete, copy, etc.)
150
+ const selfClosingTags = [
151
+ 'read', 'delete', 'copy', 'move', 'create-dir',
152
+ 'list', 'exists', 'stats', 'append'
153
+ ];
154
+
155
+ for (const tagName of selfClosingTags) {
156
+ const tags = TagParser.extractTagsWithAttributes(content, tagName);
157
+ for (const tag of tags) {
158
+ const action = {
159
+ type: tagName,
160
+ ...tag.attributes
161
+ };
162
+
163
+ // Normalize common attribute names
164
+ if (action['file-path']) {
165
+ action.filePath = action['file-path'];
166
+ delete action['file-path'];
167
+ }
168
+ if (action['output-path']) {
169
+ action.outputPath = action['output-path'];
170
+ delete action['output-path'];
171
+ }
172
+ if (action['source-path']) {
173
+ action.sourcePath = action['source-path'];
174
+ delete action['source-path'];
175
+ }
176
+ if (action['dest-path']) {
177
+ action.destPath = action['dest-path'];
178
+ delete action['dest-path'];
179
+ }
180
+ if (action['create-dirs']) {
181
+ action.createDirs = action['create-dirs'] === 'true';
182
+ delete action['create-dirs'];
183
+ }
184
+
185
+ actions.push(action);
186
+ }
187
+ }
188
+
189
+ // Extract write and append tags with content
190
+ const writeMatches = content.matchAll(/<write\s+([^>]*)>(.*?)<\/write>/gs);
191
+ for (const match of writeMatches) {
192
+ const parser = new TagParser();
193
+ const attributes = parser.parseAttributes(match[1]);
194
+ const writeContent = match[2].trim();
195
+
196
+ const action = {
197
+ type: 'write',
198
+ content: writeContent,
199
+ ...attributes
200
+ };
201
+
202
+ // Normalize attribute names
203
+ if (action['output-path']) {
204
+ action.outputPath = action['output-path'];
205
+ delete action['output-path'];
206
+ }
207
+ if (action['create-dirs']) {
208
+ action.createDirs = action['create-dirs'] === 'true';
209
+ delete action['create-dirs'];
210
+ }
211
+
212
+ actions.push(action);
213
+ }
214
+
215
+ const appendMatches = content.matchAll(/<append\s+([^>]*)>(.*?)<\/append>/gs);
216
+ for (const match of appendMatches) {
217
+ const parser = new TagParser();
218
+ const attributes = parser.parseAttributes(match[1]);
219
+ const appendContent = match[2].trim();
220
+
221
+ const action = {
222
+ type: 'append',
223
+ content: appendContent,
224
+ ...attributes
225
+ };
226
+
227
+ // Normalize attribute names
228
+ if (action['file-path']) {
229
+ action.filePath = action['file-path'];
230
+ delete action['file-path'];
231
+ }
232
+
233
+ actions.push(action);
234
+ }
235
+
236
+ params.actions = actions;
237
+ params.rawContent = content.trim();
238
+
239
+ this.logger?.debug('Parsed FileSystem tool parameters', {
240
+ totalActions: actions.length,
241
+ actionTypes: actions.map(a => a.type),
242
+ actions: actions.map(a => ({
243
+ type: a.type,
244
+ filePath: a.filePath,
245
+ outputPath: a.outputPath,
246
+ hasContent: !!a.content
247
+ }))
248
+ });
249
+
250
+ return params;
251
+
252
+ } catch (error) {
253
+ throw new Error(`Failed to parse filesystem parameters: ${error.message}`);
254
+ }
255
+ }
256
+
257
+ /**
258
+ * Get required parameters
259
+ * @returns {Array<string>} Array of required parameter names
260
+ */
261
+ getRequiredParameters() {
262
+ return ['actions'];
263
+ }
264
+
265
+ /**
266
+ * Custom parameter validation
267
+ * @param {Object} params - Parameters to validate
268
+ * @returns {Object} Validation result
269
+ */
270
+ customValidateParameters(params) {
271
+ const errors = [];
272
+
273
+ if (!params.actions || !Array.isArray(params.actions) || params.actions.length === 0) {
274
+ errors.push('At least one action is required');
275
+ } else {
276
+ // Validate each action
277
+ for (const [index, action] of params.actions.entries()) {
278
+ // Accept both `type` and `action` as the item discriminator —
279
+ // some tools in the codebase (jobdone, docx, excel, pdf) use
280
+ // `action`, and agents pattern-match across tools. Normalize
281
+ // to `type` here so the rest of the switch + execute body
282
+ // reads canonically. See cross-tool envelope unification thread.
283
+ if (!action.type && action.action) action.type = action.action;
284
+ if (!action.type) {
285
+ errors.push(`Action ${index + 1}: type is required`);
286
+ continue;
287
+ }
288
+
289
+ switch (action.type) {
290
+ case 'read':
291
+ case 'delete':
292
+ case 'exists':
293
+ case 'stats':
294
+ if (!action.filePath) {
295
+ errors.push(`Action ${index + 1}: file-path is required for ${action.type}`);
296
+ }
297
+ break;
298
+
299
+ case 'write':
300
+ if (!action.outputPath) {
301
+ errors.push(`Action ${index + 1}: output-path is required for write`);
302
+ }
303
+ // Content validation with detailed diagnostics
304
+ if (action.content === undefined) {
305
+ errors.push(`Action ${index + 1}: content is undefined for write (outputPath: ${action.outputPath || 'not set'})`);
306
+ console.error('[FileSystemTool] Write validation failed: content is undefined', {
307
+ outputPath: action.outputPath,
308
+ actionKeys: Object.keys(action)
309
+ });
310
+ } else if (action.content === null) {
311
+ errors.push(`Action ${index + 1}: content is null for write (outputPath: ${action.outputPath || 'not set'})`);
312
+ console.error('[FileSystemTool] Write validation failed: content is null', {
313
+ outputPath: action.outputPath
314
+ });
315
+ } else if (typeof action.content !== 'string') {
316
+ errors.push(`Action ${index + 1}: content must be a string, got ${typeof action.content}`);
317
+ console.error('[FileSystemTool] Write validation failed: content is not a string', {
318
+ outputPath: action.outputPath,
319
+ contentType: typeof action.content
320
+ });
321
+ }
322
+ // Note: empty string content is allowed (creates empty file)
323
+ break;
324
+
325
+ case 'append':
326
+ if (!action.filePath) {
327
+ errors.push(`Action ${index + 1}: file-path is required for append`);
328
+ }
329
+ // Content validation with detailed diagnostics
330
+ if (action.content === undefined) {
331
+ errors.push(`Action ${index + 1}: content is undefined for append (filePath: ${action.filePath || 'not set'})`);
332
+ console.error('[FileSystemTool] Append validation failed: content is undefined', {
333
+ filePath: action.filePath,
334
+ actionKeys: Object.keys(action)
335
+ });
336
+ } else if (action.content === null) {
337
+ errors.push(`Action ${index + 1}: content is null for append (filePath: ${action.filePath || 'not set'})`);
338
+ console.error('[FileSystemTool] Append validation failed: content is null', {
339
+ filePath: action.filePath
340
+ });
341
+ } else if (typeof action.content !== 'string') {
342
+ errors.push(`Action ${index + 1}: content must be a string, got ${typeof action.content}`);
343
+ console.error('[FileSystemTool] Append validation failed: content is not a string', {
344
+ filePath: action.filePath,
345
+ contentType: typeof action.content
346
+ });
347
+ } else if (action.content.length === 0) {
348
+ // Empty append is suspicious - log a warning but allow it
349
+ console.warn('[FileSystemTool] Append with empty content', {
350
+ filePath: action.filePath
351
+ });
352
+ }
353
+ break;
354
+
355
+ case 'copy':
356
+ case 'move':
357
+ if (!action.sourcePath) {
358
+ errors.push(`Action ${index + 1}: source-path is required for ${action.type}`);
359
+ }
360
+ if (!action.destPath) {
361
+ errors.push(`Action ${index + 1}: dest-path is required for ${action.type}`);
362
+ }
363
+ break;
364
+
365
+ case 'create-dir':
366
+ case 'list':
367
+ if (!action.directory) {
368
+ errors.push(`Action ${index + 1}: directory is required for ${action.type}`);
369
+ }
370
+ break;
371
+
372
+ default:
373
+ errors.push(`Action ${index + 1}: unknown action type: ${action.type}`);
374
+ }
375
+
376
+ // Validate file extensions if specified
377
+ if (action.filePath && !this.isAllowedFileExtension(action.filePath)) {
378
+ errors.push(`Action ${index + 1}: file type not allowed: ${path.extname(action.filePath)}`);
379
+ }
380
+ if (action.outputPath && !this.isAllowedFileExtension(action.outputPath)) {
381
+ errors.push(`Action ${index + 1}: file type not allowed: ${path.extname(action.outputPath)}`);
382
+ }
383
+ }
384
+ }
385
+
386
+ return {
387
+ valid: errors.length === 0,
388
+ errors
389
+ };
390
+ }
391
+
392
+ /**
393
+ * Execute tool with parsed parameters
394
+ * @param {Object} params - Parsed parameters
395
+ * @param {Object} context - Execution context
396
+ * @returns {Promise<Object>} Execution result
397
+ */
398
+ async execute(params, context) {
399
+ // Validate params structure
400
+ if (!params || typeof params !== 'object') {
401
+ throw new Error('Invalid parameters: params must be an object');
402
+ }
403
+
404
+ // Forgiving normalization — production agents commonly call with
405
+ // the natural singular shape `{action: 'read', path: 'X'}` instead
406
+ // of the documented plural `{actions: [{type: 'read', filePath: 'X'}]}`.
407
+ // Live trace evidence: Kimi-K2.6 hits this on most multi-task
408
+ // sessions and cascades into chained tool failures. Accept both
409
+ // shapes here. The plural canonical form is preserved internally.
410
+ if (!params.actions && params.action) {
411
+ const { action, ...rest } = params;
412
+ // Map common alternate field names to canonical ones.
413
+ const promoted = { type: action };
414
+ if (rest.path && !rest.filePath) promoted.filePath = rest.path;
415
+ // Preserve everything else as-is (filePath, outputPath, content,
416
+ // sourcePath, destPath, directory, encoding, createDirs, etc.).
417
+ for (const [k, v] of Object.entries(rest)) {
418
+ if (k === 'path') continue;
419
+ promoted[k] = v;
420
+ }
421
+ params = { ...params, actions: [promoted] };
422
+ }
423
+
424
+ // Normalize each action's discriminator field name: accept both
425
+ // `type` (canonical) and `action` (used by jobdone/docx/excel/pdf).
426
+ // Production path calls execute() directly, bypassing the
427
+ // validation hook this guards the switch in execute.
428
+ if (Array.isArray(params.actions)) {
429
+ for (const a of params.actions) {
430
+ if (a && !a.type && a.action) a.type = a.action;
431
+ }
432
+ }
433
+
434
+ const { actions } = params;
435
+
436
+ // Validate actions array
437
+ if (!actions) {
438
+ throw new Error('Invalid parameters: actions is required (e.g. `actions: [{type: "read", filePath: "src/x.js"}]`). Singular shape `{action: "read", path: "X"}` also accepted. Received params: ' + JSON.stringify(Object.keys(params)));
439
+ }
440
+
441
+ if (!Array.isArray(actions)) {
442
+ throw new Error('Invalid parameters: actions must be an array. Received type: ' + typeof actions);
443
+ }
444
+
445
+ if (actions.length === 0) {
446
+ throw new Error('Invalid parameters: actions array is empty');
447
+ }
448
+
449
+ // CRITICAL: Run custom validation (not called by messageProcessor)
450
+ // This ensures write/append actions have required content
451
+ const validation = this.customValidateParameters(params);
452
+ if (!validation.valid) {
453
+ const errorMsg = validation.errors?.join('; ') || validation.error || 'Validation failed';
454
+ console.error('[FileSystemTool] Parameter validation failed:', errorMsg);
455
+ throw new Error(`Parameter validation failed: ${errorMsg}`);
456
+ }
457
+
458
+ const { projectDir, agentId, directoryAccess } = context;
459
+
460
+ // Per-agent config overrides (agent.toolConfig.filesystem) merged
461
+ // with this tool's global defaults. Applied per-action at the top of
462
+ // the execute loop — every action that touches a file path is
463
+ // gated against the effective extension policy, and write/append
464
+ // payloads are size-gated against the effective maxFileSize.
465
+ const effectiveFs = this.getEffectiveConfig(context, {
466
+ allowedExtensions: this.allowedExtensions,
467
+ blockedExtensions: this.blockedExtensions,
468
+ maxFileSize: this.maxFileSize,
469
+ });
470
+ const effectiveBlockedExt = Array.isArray(effectiveFs.blockedExtensions)
471
+ ? effectiveFs.blockedExtensions.map(e => String(e).toLowerCase())
472
+ : [];
473
+ const effectiveAllowedExt = (Array.isArray(effectiveFs.allowedExtensions) && effectiveFs.allowedExtensions.length > 0)
474
+ ? effectiveFs.allowedExtensions.map(e => String(e).toLowerCase())
475
+ : null;
476
+ const effectiveMaxFileSize = (typeof effectiveFs.maxFileSize === 'number' && effectiveFs.maxFileSize > 0)
477
+ ? effectiveFs.maxFileSize
478
+ : this.maxFileSize;
479
+
480
+ const _checkExt = (filePath) => {
481
+ if (!filePath) return null;
482
+ const ext = (filePath.match(/\.[^./\\]+$/)?.[0] || '').toLowerCase();
483
+ if (effectiveBlockedExt.includes(ext)) {
484
+ return `File extension ${ext || '(none)'} is blocked by agent policy: ${filePath}`;
485
+ }
486
+ if (effectiveAllowedExt && !effectiveAllowedExt.includes(ext)) {
487
+ return `File extension ${ext || '(none)'} is not in the agent's allowed list: ${filePath}`;
488
+ }
489
+ return null;
490
+ };
491
+
492
+ // Get directory access configuration from agent or create default
493
+ const accessConfig = directoryAccess ||
494
+ this.directoryAccessManager.createDirectoryAccess({
495
+ workingDirectory: projectDir || process.cwd(),
496
+ writeEnabledDirectories: [projectDir || process.cwd()],
497
+ restrictToProject: true
498
+ });
499
+
500
+ // IMPORTANT: If the agent has directoryAccess configured, use its workingDirectory
501
+ // This ensures UI-configured project directories are respected
502
+ if (directoryAccess && directoryAccess.workingDirectory) {
503
+ // Agent has explicitly configured working directory from UI - use it
504
+ console.log('FileSystem DEBUG: Using agent configured working directory:', directoryAccess.workingDirectory);
505
+ console.log('FileSystem DEBUG: Full directoryAccess object:', JSON.stringify(directoryAccess, null, 2));
506
+ } else {
507
+ // Using fallback to projectDir or process.cwd()
508
+ console.log('FileSystem DEBUG: Using fallback working directory:', projectDir || process.cwd());
509
+ console.log('FileSystem DEBUG: directoryAccess is:', directoryAccess);
510
+ console.log('FileSystem DEBUG: projectDir is:', projectDir);
511
+ }
512
+
513
+ const results = [];
514
+
515
+ for (const action of actions) {
516
+ try {
517
+ // Per-agent extension policy gate — runs BEFORE the action so
518
+ // disallowed file types never reach the filesystem layer. Terminal
519
+ // and web tools use the same short-circuit pattern.
520
+ const pathsToCheck = [action.filePath, action.outputPath].filter(Boolean);
521
+ for (const p of pathsToCheck) {
522
+ const err = _checkExt(p);
523
+ if (err) {
524
+ results.push({ success: false, action: action.type, error: err });
525
+ throw new Error('__skip');
526
+ }
527
+ }
528
+ // Per-agent maxFileSize gate for write/append — fires ONLY when
529
+ // the agent explicitly overrides maxFileSize via toolConfig. When
530
+ // the agent has no override, the existing `this.maxFileSize`
531
+ // check downstream handles it with its original error message,
532
+ // preserving backward compatibility. Read-side and stat-based
533
+ // size checks still use `this.maxFileSize` at deeper call sites.
534
+ const perAgentMaxFileSize = (typeof context?.toolConfig?.maxFileSize === 'number' && context.toolConfig.maxFileSize > 0)
535
+ ? context.toolConfig.maxFileSize
536
+ : null;
537
+ if (perAgentMaxFileSize != null
538
+ && (action.type === 'write' || action.type === 'append')
539
+ && typeof action.content === 'string') {
540
+ const payloadBytes = Buffer.byteLength(action.content, 'utf8');
541
+ if (payloadBytes > perAgentMaxFileSize) {
542
+ results.push({
543
+ success: false,
544
+ action: action.type,
545
+ error: `Content too large: ${payloadBytes} bytes exceeds per-agent maxFileSize (${perAgentMaxFileSize})`,
546
+ });
547
+ throw new Error('__skip');
548
+ }
549
+ }
550
+
551
+ let result;
552
+
553
+ switch (action.type) {
554
+ case 'read':
555
+ result = await this.readFile(action.filePath, accessConfig, action.encoding);
556
+ break;
557
+
558
+ case 'write':
559
+ result = await this.writeFile(action.outputPath, action.content, accessConfig, {
560
+ encoding: action.encoding,
561
+ createDirs: action.createDirs,
562
+ wasTruncated: context.wasTruncated || false
563
+ });
564
+ break;
565
+
566
+ case 'append':
567
+ result = await this.appendToFile(action.filePath, action.content, accessConfig, action.encoding);
568
+ break;
569
+
570
+ case 'delete':
571
+ result = await this.deleteFile(action.filePath, accessConfig);
572
+ break;
573
+
574
+ case 'copy':
575
+ result = await this.copyFile(action.sourcePath, action.destPath, accessConfig);
576
+ break;
577
+
578
+ case 'move':
579
+ result = await this.moveFile(action.sourcePath, action.destPath, accessConfig);
580
+ break;
581
+
582
+ case 'create-dir':
583
+ result = await this.createDirectory(action.directory, accessConfig);
584
+ break;
585
+
586
+ case 'list':
587
+ result = await this.listDirectory(action.directory, accessConfig);
588
+ break;
589
+
590
+ case 'exists':
591
+ result = await this.checkExists(action.filePath, accessConfig);
592
+ break;
593
+
594
+ case 'stats':
595
+ result = await this.getFileStats(action.filePath, accessConfig);
596
+ break;
597
+
598
+ default:
599
+ throw new Error(`Unknown action type: ${action.type}`);
600
+ }
601
+
602
+ results.push(result);
603
+ this.addToHistory(action, result, context.agentId);
604
+
605
+ // Invalidate the codebase-knowledge cache for files we just
606
+ // mutated the agent should know its cached skeleton/range
607
+ // for these paths is now stale and must be re-fetched.
608
+ // Best-effort: never block on this. See
609
+ // services/codebaseKnowledgeService.js for design rationale.
610
+ if (context.agentId && ['write','append','delete','copy','move'].includes(action.type) && result?.success !== false) {
611
+ try {
612
+ const { getCodebaseKnowledgeService } = await import('../services/codebaseKnowledgeService.js');
613
+ const ks = getCodebaseKnowledgeService(this.logger);
614
+ const paths = [];
615
+ if (action.outputPath) paths.push(action.outputPath);
616
+ if (action.filePath) paths.push(action.filePath);
617
+ if (action.destPath) paths.push(action.destPath); // for copy/move (the dest is newly written)
618
+ if (action.sourcePath && action.type === 'move') paths.push(action.sourcePath); // src gone
619
+ if (action.sourcePath && action.type === 'delete') paths.push(action.sourcePath);
620
+ const wd = context.directoryAccess?.workingDirectory || context.projectDir || null;
621
+ for (const p of paths) ks.markDirty(context.agentId, p, wd);
622
+ } catch (e) {
623
+ this.logger?.debug?.('codebase-knowledge invalidation failed (filesystem)', { error: e.message });
624
+ }
625
+ }
626
+
627
+ } catch (error) {
628
+ // Per-agent policy gate above uses "__skip" to signal that it
629
+ // already pushed a useful error onto results — don't double-log
630
+ // or double-push.
631
+ if (error.message === '__skip') {
632
+ this.addToHistory(action, results[results.length - 1], context.agentId);
633
+ continue;
634
+ }
635
+
636
+ // Log detailed error for debugging
637
+ console.error(`[FileSystemTool] Action '${action.type}' failed:`, {
638
+ action: action.type,
639
+ filePath: action.filePath || action.outputPath || action.directory,
640
+ error: error.message,
641
+ stack: error.stack?.split('\n').slice(0, 3).join('\n'),
642
+ contentLength: action.content?.length,
643
+ hasContent: action.content !== undefined && action.content !== null
644
+ });
645
+
646
+ const errorResult = {
647
+ success: false,
648
+ action: action.type,
649
+ error: error.message,
650
+ filePath: action.filePath || action.outputPath || action.directory,
651
+ // Include debug info for troubleshooting
652
+ debug: {
653
+ contentProvided: action.content !== undefined && action.content !== null,
654
+ contentLength: action.content?.length || 0,
655
+ contentPreview: action.content ? action.content.substring(0, 100) + (action.content.length > 100 ? '...' : '') : null
656
+ }
657
+ };
658
+
659
+ results.push(errorResult);
660
+ this.addToHistory(action, errorResult, context.agentId);
661
+ }
662
+ }
663
+
664
+ // Determine overall success - only true if ALL actions succeeded
665
+ const allSucceeded = results.every(r => r.success === true);
666
+ const failedCount = results.filter(r => r.success === false).length;
667
+
668
+ return {
669
+ success: allSucceeded,
670
+ actions: results,
671
+ executedActions: actions.length,
672
+ successfulActions: actions.length - failedCount,
673
+ failedActions: failedCount,
674
+ toolUsed: 'filesys',
675
+ ...(failedCount > 0 && {
676
+ warning: `${failedCount} of ${actions.length} action(s) failed. Check individual action results for details.`
677
+ })
678
+ };
679
+ }
680
+
681
+ /**
682
+ * Read file contents
683
+ * @private
684
+ */
685
+ async readFile(filePath, accessConfig, encoding = 'utf8') {
686
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
687
+ const fullPath = this.resolvePath(filePath, workingDir);
688
+
689
+ // Validate read access using DirectoryAccessManager
690
+ const accessResult = this.directoryAccessManager.validateReadAccess(fullPath, accessConfig);
691
+ if (!accessResult.allowed) {
692
+ throw new Error(`Read access denied: ${accessResult.reason} (${accessResult.path})`);
693
+ }
694
+
695
+ try {
696
+ const stats = await fs.stat(fullPath);
697
+
698
+ if (stats.size > this.maxFileSize) {
699
+ throw new Error(`File too large: ${stats.size} bytes (max ${this.maxFileSize})`);
700
+ }
701
+
702
+ const content = await fs.readFile(fullPath, encoding);
703
+
704
+ return {
705
+ success: true,
706
+ action: 'read',
707
+ filePath: this.directoryAccessManager.createRelativePath(fullPath, accessConfig),
708
+ content,
709
+ size: stats.size,
710
+ encoding,
711
+ lastModified: stats.mtime.toISOString(),
712
+ message: `Read ${stats.size} bytes from ${filePath}`
713
+ };
714
+
715
+ } catch (error) {
716
+ throw new Error(`Failed to read file ${filePath}: ${error.message}`);
717
+ }
718
+ }
719
+
720
+ /**
721
+ * Write content to file
722
+ * @private
723
+ */
724
+ async writeFile(outputPath, content, accessConfig, options = {}) {
725
+ const { encoding = 'utf8', createDirs = true, wasTruncated = false } = options;
726
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
727
+ const fullPath = this.resolvePath(outputPath, workingDir);
728
+
729
+ // Validate write access using DirectoryAccessManager
730
+ const accessResult = this.directoryAccessManager.validateWriteAccess(fullPath, accessConfig);
731
+ if (!accessResult.allowed) {
732
+ throw new Error(`Write access denied: ${accessResult.reason} (${accessResult.path})`);
733
+ }
734
+
735
+ // Handle truncated content - append notice if AI response was cut off
736
+ let finalContent = content;
737
+ let truncationApplied = false;
738
+
739
+ if (wasTruncated && content) {
740
+ const fileExt = getFileExtension(outputPath);
741
+ const truncationNotice = createTruncationNotice(fileExt);
742
+ if (truncationNotice) {
743
+ finalContent = content + truncationNotice;
744
+ truncationApplied = true;
745
+ console.log(`[FileSystemTool] Appending truncation notice to ${outputPath} (AI response was truncated)`);
746
+ }
747
+ }
748
+
749
+ try {
750
+ // Check content size
751
+ const contentSize = Buffer.byteLength(finalContent, encoding);
752
+ if (contentSize > this.maxFileSize) {
753
+ throw new Error(`Content too large: ${contentSize} bytes (max ${this.maxFileSize})`);
754
+ }
755
+
756
+ // Create parent directories if requested
757
+ if (createDirs) {
758
+ const dirPath = path.dirname(fullPath);
759
+ await fs.mkdir(dirPath, { recursive: true });
760
+ }
761
+
762
+ // Create backup if file exists
763
+ let backupPath = null;
764
+ try {
765
+ await fs.access(fullPath);
766
+ backupPath = `${fullPath}.backup-${Date.now()}`;
767
+ await fs.copyFile(fullPath, backupPath);
768
+ } catch {
769
+ // File doesn't exist, no backup needed
770
+ }
771
+
772
+ await fs.writeFile(fullPath, finalContent, encoding);
773
+
774
+ // VERIFICATION: Confirm file was written correctly
775
+ const stats = await fs.stat(fullPath);
776
+
777
+ // Verify file size matches expected content size
778
+ const expectedSize = Buffer.byteLength(finalContent, encoding);
779
+ if (stats.size !== expectedSize) {
780
+ throw new Error(`Write verification failed: expected ${expectedSize} bytes but file is ${stats.size} bytes`);
781
+ }
782
+
783
+ // For text files, verify content was written (read back and compare hash)
784
+ if (encoding === 'utf8' || encoding === 'utf-8') {
785
+ const writtenContent = await fs.readFile(fullPath, encoding);
786
+ if (writtenContent !== finalContent) {
787
+ throw new Error(`Write verification failed: content mismatch after write`);
788
+ }
789
+ }
790
+
791
+ const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
792
+
793
+ // Build base result
794
+ const result = {
795
+ success: true,
796
+ action: 'write',
797
+ outputPath: relativePath,
798
+ fullPath: fullPath,
799
+ size: stats.size,
800
+ encoding,
801
+ verified: true, // Content was verified after write
802
+ backupPath: backupPath ? this.directoryAccessManager.createRelativePath(backupPath, accessConfig) : null,
803
+ backupFullPath: backupPath || null,
804
+ message: `Wrote ${stats.size} bytes to ${fullPath} (verified)`,
805
+ // Include truncation info if applicable
806
+ ...(truncationApplied && {
807
+ wasTruncated: true,
808
+ truncationWarning: 'Content was truncated due to AI response token limit. A notice was appended to the file.'
809
+ }),
810
+ // Warn if content is empty
811
+ ...(stats.size === 0 && {
812
+ emptyContent: true,
813
+ emptyWarning: 'File was written with empty content'
814
+ })
815
+ };
816
+
817
+ // Add truncation warning to message
818
+ if (truncationApplied) {
819
+ result.message += ' [PARTIAL: AI response was truncated - content may be incomplete]';
820
+ }
821
+
822
+ // Post-write validation for structured files (plug-and-play via structuredFileValidator)
823
+ if (this.validateStructuredFiles) {
824
+ const validation = validateForToolResponse(content, fullPath);
825
+ if (validation) {
826
+ result.validation = validation;
827
+ // Add warning to message if validation failed
828
+ if (!validation.valid) {
829
+ result.message += ` [WARNING: ${validation.format.toUpperCase()} validation failed with ${validation.errorCount} error(s)]`;
830
+ }
831
+ }
832
+ }
833
+
834
+ return result;
835
+
836
+ } catch (error) {
837
+ throw new Error(`Failed to write file ${fullPath}: ${error.message}`);
838
+ }
839
+ }
840
+
841
+ /**
842
+ * Append content to file
843
+ * @private
844
+ */
845
+ async appendToFile(filePath, content, accessConfig, encoding = 'utf8') {
846
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
847
+ const fullPath = this.resolvePath(filePath, workingDir);
848
+
849
+ // Validate write access using DirectoryAccessManager
850
+ const accessResult = this.directoryAccessManager.validateWriteAccess(fullPath, accessConfig);
851
+ if (!accessResult.allowed) {
852
+ throw new Error(`Write access denied: ${accessResult.reason} (${accessResult.path})`);
853
+ }
854
+
855
+ try {
856
+ // Check if file exists and get current size
857
+ let currentSize = 0;
858
+ try {
859
+ const stats = await fs.stat(fullPath);
860
+ currentSize = stats.size;
861
+ } catch {
862
+ // File doesn't exist, will be created
863
+ }
864
+
865
+ const contentSize = Buffer.byteLength(content, encoding);
866
+ if (currentSize + contentSize > this.maxFileSize) {
867
+ throw new Error(`File would become too large: ${currentSize + contentSize} bytes (max ${this.maxFileSize})`);
868
+ }
869
+
870
+ // Store size before append for verification
871
+ const sizeBefore = currentSize;
872
+
873
+ await fs.appendFile(fullPath, content, encoding);
874
+
875
+ const stats = await fs.stat(fullPath);
876
+ const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
877
+
878
+ // VERIFICATION: Confirm append actually happened
879
+ const expectedSize = sizeBefore + contentSize;
880
+ if (stats.size < expectedSize) {
881
+ throw new Error(`Append verification failed: expected at least ${expectedSize} bytes but file is ${stats.size} bytes`);
882
+ }
883
+
884
+ // For text files, verify the appended content is at the end
885
+ if (encoding === 'utf8' || encoding === 'utf-8') {
886
+ const fileContent = await fs.readFile(fullPath, encoding);
887
+ if (!fileContent.endsWith(content)) {
888
+ throw new Error(`Append verification failed: appended content not found at end of file`);
889
+ }
890
+ }
891
+
892
+ return {
893
+ success: true,
894
+ action: 'append',
895
+ filePath: relativePath,
896
+ fullPath: fullPath,
897
+ appendedBytes: contentSize,
898
+ totalSize: stats.size,
899
+ sizeBefore: sizeBefore,
900
+ encoding,
901
+ verified: true,
902
+ message: `Appended ${contentSize} bytes to ${fullPath} (verified)`
903
+ };
904
+
905
+ } catch (error) {
906
+ throw new Error(`Failed to append to file ${fullPath}: ${error.message}`);
907
+ }
908
+ }
909
+
910
+ /**
911
+ * Delete file
912
+ * @private
913
+ */
914
+ async deleteFile(filePath, accessConfig) {
915
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
916
+ const fullPath = this.resolvePath(filePath, workingDir);
917
+
918
+ // Validate write access for deletion
919
+ const accessResult = this.directoryAccessManager.validateWriteAccess(fullPath, accessConfig);
920
+ if (!accessResult.allowed) {
921
+ throw new Error(`Delete access denied: ${accessResult.reason} (${accessResult.path})`);
922
+ }
923
+
924
+ try {
925
+ const stats = await fs.stat(fullPath);
926
+
927
+ // Create backup before deletion
928
+ const backupPath = `${fullPath}.deleted-backup-${Date.now()}`;
929
+ await fs.copyFile(fullPath, backupPath);
930
+
931
+ await fs.unlink(fullPath);
932
+
933
+ const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
934
+ const backupRelativePath = this.directoryAccessManager.createRelativePath(backupPath, accessConfig);
935
+
936
+ return {
937
+ success: true,
938
+ action: 'delete',
939
+ filePath: relativePath,
940
+ fullPath: fullPath,
941
+ size: stats.size,
942
+ backupPath: backupRelativePath,
943
+ backupFullPath: backupPath,
944
+ message: `Deleted ${fullPath} (backup created)`
945
+ };
946
+
947
+ } catch (error) {
948
+ throw new Error(`Failed to delete file ${fullPath}: ${error.message}`);
949
+ }
950
+ }
951
+
952
+ /**
953
+ * Copy file
954
+ * @private
955
+ */
956
+ async copyFile(sourcePath, destPath, accessConfig) {
957
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
958
+ const fullSourcePath = this.resolvePath(sourcePath, workingDir);
959
+ const fullDestPath = this.resolvePath(destPath, workingDir);
960
+
961
+ // Validate read access for source
962
+ const sourceAccessResult = this.directoryAccessManager.validateReadAccess(fullSourcePath, accessConfig);
963
+ if (!sourceAccessResult.allowed) {
964
+ throw new Error(`Source read access denied: ${sourceAccessResult.reason} (${sourceAccessResult.path})`);
965
+ }
966
+
967
+ // Validate write access for destination
968
+ const destAccessResult = this.directoryAccessManager.validateWriteAccess(fullDestPath, accessConfig);
969
+ if (!destAccessResult.allowed) {
970
+ throw new Error(`Destination write access denied: ${destAccessResult.reason} (${destAccessResult.path})`);
971
+ }
972
+
973
+ try {
974
+ const sourceStats = await fs.stat(fullSourcePath);
975
+
976
+ if (sourceStats.size > this.maxFileSize) {
977
+ throw new Error(`Source file too large: ${sourceStats.size} bytes`);
978
+ }
979
+
980
+ // Create destination directory if needed
981
+ const destDir = path.dirname(fullDestPath);
982
+ await fs.mkdir(destDir, { recursive: true });
983
+
984
+ await fs.copyFile(fullSourcePath, fullDestPath);
985
+
986
+ const sourceRelativePath = this.directoryAccessManager.createRelativePath(fullSourcePath, accessConfig);
987
+ const destRelativePath = this.directoryAccessManager.createRelativePath(fullDestPath, accessConfig);
988
+
989
+ return {
990
+ success: true,
991
+ action: 'copy',
992
+ sourcePath: sourceRelativePath,
993
+ destPath: destRelativePath,
994
+ sourceFullPath: fullSourcePath,
995
+ destFullPath: fullDestPath,
996
+ size: sourceStats.size,
997
+ message: `Copied ${fullSourcePath} to ${fullDestPath}`
998
+ };
999
+
1000
+ } catch (error) {
1001
+ throw new Error(`Failed to copy ${fullSourcePath} to ${fullDestPath}: ${error.message}`);
1002
+ }
1003
+ }
1004
+
1005
+ /**
1006
+ * Move/rename file
1007
+ * @private
1008
+ */
1009
+ async moveFile(sourcePath, destPath, accessConfig) {
1010
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
1011
+ const fullSourcePath = this.resolvePath(sourcePath, workingDir);
1012
+ const fullDestPath = this.resolvePath(destPath, workingDir);
1013
+
1014
+ // Validate read access for source
1015
+ const readResult = this.directoryAccessManager.validateReadAccess(fullSourcePath, accessConfig);
1016
+ if (!readResult.allowed) {
1017
+ throw new Error(`Read access denied for source: ${readResult.reason} (${readResult.path})`);
1018
+ }
1019
+
1020
+ // Validate write access for destination
1021
+ const writeResult = this.directoryAccessManager.validateWriteAccess(fullDestPath, accessConfig);
1022
+ if (!writeResult.allowed) {
1023
+ throw new Error(`Write access denied for destination: ${writeResult.reason} (${writeResult.path})`);
1024
+ }
1025
+
1026
+ try {
1027
+ const sourceStats = await fs.stat(fullSourcePath);
1028
+
1029
+ // Create destination directory if needed
1030
+ const destDir = path.dirname(fullDestPath);
1031
+ await fs.mkdir(destDir, { recursive: true });
1032
+
1033
+ await fs.rename(fullSourcePath, fullDestPath);
1034
+
1035
+ const relativeSource = this.directoryAccessManager.createRelativePath(fullSourcePath, accessConfig);
1036
+ const relativeDest = this.directoryAccessManager.createRelativePath(fullDestPath, accessConfig);
1037
+
1038
+ return {
1039
+ success: true,
1040
+ action: 'move',
1041
+ sourcePath: relativeSource,
1042
+ destPath: relativeDest,
1043
+ fullSourcePath: fullSourcePath,
1044
+ fullDestPath: fullDestPath,
1045
+ size: sourceStats.size,
1046
+ message: `Moved ${sourcePath} to ${destPath}`
1047
+ };
1048
+
1049
+ } catch (error) {
1050
+ throw new Error(`Failed to move ${sourcePath} to ${destPath}: ${error.message}`);
1051
+ }
1052
+ }
1053
+
1054
+ /**
1055
+ * Create directory
1056
+ * @private
1057
+ */
1058
+ async createDirectory(directory, accessConfig) {
1059
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
1060
+ const fullPath = this.resolvePath(directory, workingDir);
1061
+
1062
+ // Validate write access using DirectoryAccessManager
1063
+ const accessResult = this.directoryAccessManager.validateWriteAccess(fullPath, accessConfig);
1064
+ if (!accessResult.allowed) {
1065
+ throw new Error(`Write access denied: ${accessResult.reason} (${accessResult.path})`);
1066
+ }
1067
+
1068
+ try {
1069
+ await fs.mkdir(fullPath, { recursive: true });
1070
+
1071
+ const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
1072
+
1073
+ return {
1074
+ success: true,
1075
+ action: 'create-dir',
1076
+ directory: relativePath,
1077
+ fullPath: fullPath,
1078
+ message: `Created directory ${directory}`
1079
+ };
1080
+
1081
+ } catch (error) {
1082
+ throw new Error(`Failed to create directory ${directory}: ${error.message}`);
1083
+ }
1084
+ }
1085
+
1086
+ /**
1087
+ * List directory contents
1088
+ * @private
1089
+ */
1090
+ async listDirectory(directory, accessConfig) {
1091
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
1092
+ const fullPath = this.resolvePath(directory, workingDir);
1093
+
1094
+ // Validate read access using DirectoryAccessManager
1095
+ const accessResult = this.directoryAccessManager.validateReadAccess(fullPath, accessConfig);
1096
+ if (!accessResult.allowed) {
1097
+ throw new Error(`Read access denied: ${accessResult.reason} (${accessResult.path})`);
1098
+ }
1099
+
1100
+ try {
1101
+ const entries = await fs.readdir(fullPath, { withFileTypes: true });
1102
+
1103
+ const contents = [];
1104
+ for (const entry of entries) {
1105
+ const entryPath = path.join(fullPath, entry.name);
1106
+ const stats = await fs.stat(entryPath);
1107
+
1108
+ contents.push({
1109
+ name: entry.name,
1110
+ type: entry.isDirectory() ? 'directory' : 'file',
1111
+ size: entry.isFile() ? stats.size : undefined,
1112
+ lastModified: stats.mtime.toISOString(),
1113
+ permissions: stats.mode,
1114
+ isSymlink: entry.isSymbolicLink()
1115
+ });
1116
+ }
1117
+
1118
+ const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
1119
+
1120
+ return {
1121
+ success: true,
1122
+ action: 'list',
1123
+ directory: relativePath,
1124
+ fullPath: fullPath,
1125
+ contents,
1126
+ totalItems: contents.length,
1127
+ directories: contents.filter(item => item.type === 'directory').length,
1128
+ files: contents.filter(item => item.type === 'file').length,
1129
+ message: `Listed ${contents.length} items in ${directory}`
1130
+ };
1131
+
1132
+ } catch (error) {
1133
+ throw new Error(`Failed to list directory ${directory}: ${error.message}`);
1134
+ }
1135
+ }
1136
+
1137
+ /**
1138
+ * Check if file/directory exists
1139
+ * @private
1140
+ */
1141
+ async checkExists(filePath, accessConfig) {
1142
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
1143
+ const fullPath = this.resolvePath(filePath, workingDir);
1144
+
1145
+ // Validate read access
1146
+ const accessResult = this.directoryAccessManager.validateReadAccess(fullPath, accessConfig);
1147
+ if (!accessResult.allowed) {
1148
+ throw new Error(`Read access denied: ${accessResult.reason} (${accessResult.path})`);
1149
+ }
1150
+
1151
+ try {
1152
+ const stats = await fs.stat(fullPath);
1153
+
1154
+ const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
1155
+
1156
+ return {
1157
+ success: true,
1158
+ action: 'exists',
1159
+ filePath: relativePath,
1160
+ fullPath: fullPath,
1161
+ exists: true,
1162
+ type: stats.isDirectory() ? 'directory' : 'file',
1163
+ message: `${filePath} exists as ${stats.isDirectory() ? 'directory' : 'file'}`
1164
+ };
1165
+
1166
+ } catch (error) {
1167
+ if (error.code === 'ENOENT') {
1168
+ const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
1169
+ return {
1170
+ success: true,
1171
+ action: 'exists',
1172
+ filePath: relativePath,
1173
+ fullPath: fullPath,
1174
+ exists: false,
1175
+ message: `${filePath} does not exist`
1176
+ };
1177
+ }
1178
+
1179
+ throw new Error(`Failed to check existence of ${filePath}: ${error.message}`);
1180
+ }
1181
+ }
1182
+
1183
+ /**
1184
+ * Get file statistics
1185
+ * @private
1186
+ */
1187
+ async getFileStats(filePath, accessConfig) {
1188
+ const workingDir = this.directoryAccessManager.getWorkingDirectory(accessConfig);
1189
+ const fullPath = this.resolvePath(filePath, workingDir);
1190
+
1191
+ // Validate read access
1192
+ const accessResult = this.directoryAccessManager.validateReadAccess(fullPath, accessConfig);
1193
+ if (!accessResult.allowed) {
1194
+ throw new Error(`Read access denied: ${accessResult.reason} (${accessResult.path})`);
1195
+ }
1196
+
1197
+ try {
1198
+ const stats = await fs.stat(fullPath);
1199
+
1200
+ const relativePath = this.directoryAccessManager.createRelativePath(fullPath, accessConfig);
1201
+
1202
+ return {
1203
+ success: true,
1204
+ action: 'stats',
1205
+ filePath: relativePath,
1206
+ fullPath: fullPath,
1207
+ stats: {
1208
+ size: stats.size,
1209
+ type: stats.isDirectory() ? 'directory' : 'file',
1210
+ lastModified: stats.mtime.toISOString(),
1211
+ lastAccessed: stats.atime.toISOString(),
1212
+ created: stats.birthtime.toISOString(),
1213
+ permissions: stats.mode,
1214
+ isSymlink: stats.isSymbolicLink()
1215
+ },
1216
+ message: `Retrieved stats for ${filePath}`
1217
+ };
1218
+
1219
+ } catch (error) {
1220
+ throw new Error(`Failed to get stats for ${filePath}: ${error.message}`);
1221
+ }
1222
+ }
1223
+
1224
+ /**
1225
+ * Resolve file path safely (legacy method for compatibility)
1226
+ * @private
1227
+ */
1228
+ resolvePath(filePath, workingDir) {
1229
+ const resolved = path.isAbsolute(filePath)
1230
+ ? path.normalize(filePath)
1231
+ : path.resolve(workingDir, filePath);
1232
+
1233
+ // Guard against the "doubled folder" trap. Reasoning models lose track
1234
+ // of the session CWD after a change-directory and re-prepend the
1235
+ // project folder name to every relative path — turning
1236
+ // CWD=".../foo" + "foo/bar/x.js" into ".../foo/foo/bar/x.js", which
1237
+ // the filesystem silently creates. We detect this by scanning the
1238
+ // FINAL resolved path for consecutive duplicate segments, so the
1239
+ // guard fires whether the duplication came from (a) relative prefix
1240
+ // (`foo/...` while inside `.../foo`), (b) relative mid-path
1241
+ // (`pkg/foo/foo/bar`), or (c) an absolute path written out in full
1242
+ // (`C:\...\foo\foo\bar`). Root-level duplicates like Windows drives
1243
+ // are ignored by design (`C:\C:\...` is impossible in normalized form).
1244
+ const segs = resolved.split(/[\\/]/).filter(Boolean);
1245
+ for (let i = 1; i < segs.length; i++) {
1246
+ if (segs[i].toLowerCase() === segs[i - 1].toLowerCase()) {
1247
+ throw new Error(
1248
+ `Refused: resolved path contains a duplicated segment "${segs[i]}" (${resolved}). ` +
1249
+ `Current working directory: ${workingDir}. ` +
1250
+ `This almost always means the project folder name was prepended to a relative path while the CWD is already inside it. ` +
1251
+ `Drop the duplicated "${segs[i]}/" from the path — relative paths are resolved from the CWD — or pass an absolute path with no duplication.`
1252
+ );
1253
+ }
1254
+ }
1255
+
1256
+ return resolved;
1257
+ }
1258
+
1259
+ /**
1260
+ * Validate path access using DirectoryAccessManager
1261
+ * @private
1262
+ */
1263
+ validatePathAccess(fullPath, accessConfig, operation = 'read') {
1264
+ const accessResult = operation === 'write'
1265
+ ? this.directoryAccessManager.validateWriteAccess(fullPath, accessConfig)
1266
+ : this.directoryAccessManager.validateReadAccess(fullPath, accessConfig);
1267
+
1268
+ if (!accessResult.allowed) {
1269
+ throw new Error(`${operation} access denied: ${accessResult.reason} (${accessResult.path})`);
1270
+ }
1271
+
1272
+ return accessResult;
1273
+ }
1274
+
1275
+ /**
1276
+ * Check if file extension is allowed
1277
+ * @private
1278
+ */
1279
+ isAllowedFileExtension(filePath) {
1280
+ const ext = path.extname(filePath).toLowerCase();
1281
+
1282
+ if (this.blockedExtensions.includes(ext)) {
1283
+ return false;
1284
+ }
1285
+
1286
+ if (this.allowedExtensions && !this.allowedExtensions.includes(ext)) {
1287
+ return false;
1288
+ }
1289
+
1290
+ return true;
1291
+ }
1292
+
1293
+ /**
1294
+ * Add operation to history
1295
+ * @private
1296
+ */
1297
+ addToHistory(action, result, agentId) {
1298
+ const historyEntry = {
1299
+ timestamp: new Date().toISOString(),
1300
+ agentId,
1301
+ action: action.type,
1302
+ filePath: action.filePath || action.outputPath || action.directory,
1303
+ success: result.success,
1304
+ size: result.size
1305
+ };
1306
+
1307
+ this.operationHistory.push(historyEntry);
1308
+
1309
+ // Keep only last 200 entries
1310
+ if (this.operationHistory.length > 200) {
1311
+ this.operationHistory = this.operationHistory.slice(-200);
1312
+ }
1313
+ }
1314
+
1315
+ /**
1316
+ * Get supported actions for this tool
1317
+ * @returns {Array<string>} Array of supported action names
1318
+ */
1319
+ getSupportedActions() {
1320
+ return [
1321
+ 'read', 'write', 'append', 'delete', 'copy', 'move',
1322
+ 'create-dir', 'list', 'exists', 'stats'
1323
+ ];
1324
+ }
1325
+
1326
+ /**
1327
+ * Get parameter schema for validation
1328
+ * @returns {Object} Parameter schema
1329
+ */
1330
+ getParameterSchema() {
1331
+ return {
1332
+ type: 'object',
1333
+ properties: {
1334
+ actions: {
1335
+ type: 'array',
1336
+ minItems: 1,
1337
+ items: {
1338
+ type: 'object',
1339
+ properties: {
1340
+ type: {
1341
+ type: 'string',
1342
+ enum: this.getSupportedActions()
1343
+ },
1344
+ filePath: { type: 'string' },
1345
+ outputPath: { type: 'string' },
1346
+ sourcePath: { type: 'string' },
1347
+ destPath: { type: 'string' },
1348
+ directory: { type: 'string' },
1349
+ content: { type: 'string' },
1350
+ encoding: { type: 'string' },
1351
+ createDirs: { type: 'boolean' }
1352
+ },
1353
+ required: ['type']
1354
+ }
1355
+ }
1356
+ },
1357
+ required: ['actions']
1358
+ };
1359
+ }
1360
+
1361
+ /**
1362
+ * Get operation history for debugging
1363
+ * @returns {Array} Operation history
1364
+ */
1365
+ getOperationHistory(agentId = null) {
1366
+ if (agentId) {
1367
+ return this.operationHistory.filter(entry => entry.agentId === agentId);
1368
+ }
1369
+ return [...this.operationHistory];
1370
+ }
1371
+ }
1372
+
1315
1373
  export default FileSystemTool;