@nexus-cortex/executors 4.26.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 (365) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +2 -0
  3. package/README.md +13 -0
  4. package/dist/ExecutorRegistry.d.ts +89 -0
  5. package/dist/ExecutorRegistry.d.ts.map +1 -0
  6. package/dist/ExecutorRegistry.js +219 -0
  7. package/dist/ExecutorRegistry.js.map +1 -0
  8. package/dist/base/BaseTool.d.ts +108 -0
  9. package/dist/base/BaseTool.d.ts.map +1 -0
  10. package/dist/base/BaseTool.js +111 -0
  11. package/dist/base/BaseTool.js.map +1 -0
  12. package/dist/base/ToolRegistry.d.ts +141 -0
  13. package/dist/base/ToolRegistry.d.ts.map +1 -0
  14. package/dist/base/ToolRegistry.js +241 -0
  15. package/dist/base/ToolRegistry.js.map +1 -0
  16. package/dist/base/ToolResult.d.ts +63 -0
  17. package/dist/base/ToolResult.d.ts.map +1 -0
  18. package/dist/base/ToolResult.js +8 -0
  19. package/dist/base/ToolResult.js.map +1 -0
  20. package/dist/base/index.d.ts +10 -0
  21. package/dist/base/index.d.ts.map +1 -0
  22. package/dist/base/index.js +8 -0
  23. package/dist/base/index.js.map +1 -0
  24. package/dist/implementations/addon/CreateArtifactTool.d.ts +221 -0
  25. package/dist/implementations/addon/CreateArtifactTool.d.ts.map +1 -0
  26. package/dist/implementations/addon/CreateArtifactTool.js +1042 -0
  27. package/dist/implementations/addon/CreateArtifactTool.js.map +1 -0
  28. package/dist/implementations/addon/FrameDiffCache.d.ts +166 -0
  29. package/dist/implementations/addon/FrameDiffCache.d.ts.map +1 -0
  30. package/dist/implementations/addon/FrameDiffCache.js +395 -0
  31. package/dist/implementations/addon/FrameDiffCache.js.map +1 -0
  32. package/dist/implementations/addon/H264StreamEncoder.d.ts +84 -0
  33. package/dist/implementations/addon/H264StreamEncoder.d.ts.map +1 -0
  34. package/dist/implementations/addon/H264StreamEncoder.js +203 -0
  35. package/dist/implementations/addon/H264StreamEncoder.js.map +1 -0
  36. package/dist/implementations/addon/HybridScreenshotManager.d.ts +197 -0
  37. package/dist/implementations/addon/HybridScreenshotManager.d.ts.map +1 -0
  38. package/dist/implementations/addon/HybridScreenshotManager.js +415 -0
  39. package/dist/implementations/addon/HybridScreenshotManager.js.map +1 -0
  40. package/dist/implementations/addon/InspectSandboxTool.d.ts +54 -0
  41. package/dist/implementations/addon/InspectSandboxTool.d.ts.map +1 -0
  42. package/dist/implementations/addon/InspectSandboxTool.js +226 -0
  43. package/dist/implementations/addon/InspectSandboxTool.js.map +1 -0
  44. package/dist/implementations/addon/InteractWithSandboxTool.d.ts +90 -0
  45. package/dist/implementations/addon/InteractWithSandboxTool.d.ts.map +1 -0
  46. package/dist/implementations/addon/InteractWithSandboxTool.js +367 -0
  47. package/dist/implementations/addon/InteractWithSandboxTool.js.map +1 -0
  48. package/dist/implementations/addon/KeyframeDetector.d.ts +140 -0
  49. package/dist/implementations/addon/KeyframeDetector.d.ts.map +1 -0
  50. package/dist/implementations/addon/KeyframeDetector.js +390 -0
  51. package/dist/implementations/addon/KeyframeDetector.js.map +1 -0
  52. package/dist/implementations/addon/ModifySandboxTool.d.ts +62 -0
  53. package/dist/implementations/addon/ModifySandboxTool.d.ts.map +1 -0
  54. package/dist/implementations/addon/ModifySandboxTool.js +266 -0
  55. package/dist/implementations/addon/ModifySandboxTool.js.map +1 -0
  56. package/dist/implementations/addon/ReactArtifactBuilder.d.ts +27 -0
  57. package/dist/implementations/addon/ReactArtifactBuilder.d.ts.map +1 -0
  58. package/dist/implementations/addon/ReactArtifactBuilder.js +198 -0
  59. package/dist/implementations/addon/ReactArtifactBuilder.js.map +1 -0
  60. package/dist/implementations/addon/SandboxEventBroadcaster.d.ts +143 -0
  61. package/dist/implementations/addon/SandboxEventBroadcaster.d.ts.map +1 -0
  62. package/dist/implementations/addon/SandboxEventBroadcaster.js +258 -0
  63. package/dist/implementations/addon/SandboxEventBroadcaster.js.map +1 -0
  64. package/dist/implementations/addon/SandboxIntrospectionTools.d.ts +77 -0
  65. package/dist/implementations/addon/SandboxIntrospectionTools.d.ts.map +1 -0
  66. package/dist/implementations/addon/SandboxIntrospectionTools.js +292 -0
  67. package/dist/implementations/addon/SandboxIntrospectionTools.js.map +1 -0
  68. package/dist/implementations/addon/SandboxViewServer.d.ts +127 -0
  69. package/dist/implementations/addon/SandboxViewServer.d.ts.map +1 -0
  70. package/dist/implementations/addon/SandboxViewServer.js +775 -0
  71. package/dist/implementations/addon/SandboxViewServer.js.map +1 -0
  72. package/dist/implementations/addon/ScreenStream.d.ts +149 -0
  73. package/dist/implementations/addon/ScreenStream.d.ts.map +1 -0
  74. package/dist/implementations/addon/ScreenStream.js +306 -0
  75. package/dist/implementations/addon/ScreenStream.js.map +1 -0
  76. package/dist/implementations/addon/StopSandboxTool.d.ts +61 -0
  77. package/dist/implementations/addon/StopSandboxTool.d.ts.map +1 -0
  78. package/dist/implementations/addon/StopSandboxTool.js +252 -0
  79. package/dist/implementations/addon/StopSandboxTool.js.map +1 -0
  80. package/dist/implementations/addon/TerminalSandbox.d.ts +111 -0
  81. package/dist/implementations/addon/TerminalSandbox.d.ts.map +1 -0
  82. package/dist/implementations/addon/TerminalSandbox.js +345 -0
  83. package/dist/implementations/addon/TerminalSandbox.js.map +1 -0
  84. package/dist/implementations/addon/VisualFeedbackBridge.d.ts +367 -0
  85. package/dist/implementations/addon/VisualFeedbackBridge.d.ts.map +1 -0
  86. package/dist/implementations/addon/VisualFeedbackBridge.js +888 -0
  87. package/dist/implementations/addon/VisualFeedbackBridge.js.map +1 -0
  88. package/dist/implementations/addon/WindowManager.d.ts +138 -0
  89. package/dist/implementations/addon/WindowManager.d.ts.map +1 -0
  90. package/dist/implementations/addon/WindowManager.js +276 -0
  91. package/dist/implementations/addon/WindowManager.js.map +1 -0
  92. package/dist/implementations/addon/index.d.ts +29 -0
  93. package/dist/implementations/addon/index.d.ts.map +1 -0
  94. package/dist/implementations/addon/index.js +29 -0
  95. package/dist/implementations/addon/index.js.map +1 -0
  96. package/dist/implementations/addon/injectables/reactIntrospection.d.ts +57 -0
  97. package/dist/implementations/addon/injectables/reactIntrospection.d.ts.map +1 -0
  98. package/dist/implementations/addon/injectables/reactIntrospection.js +480 -0
  99. package/dist/implementations/addon/injectables/reactIntrospection.js.map +1 -0
  100. package/dist/implementations/addon/terminal-client.html +253 -0
  101. package/dist/implementations/agent/PRAgentTool.d.ts +37 -0
  102. package/dist/implementations/agent/PRAgentTool.d.ts.map +1 -0
  103. package/dist/implementations/agent/PRAgentTool.js +257 -0
  104. package/dist/implementations/agent/PRAgentTool.js.map +1 -0
  105. package/dist/implementations/agent/TaskTool.d.ts +76 -0
  106. package/dist/implementations/agent/TaskTool.d.ts.map +1 -0
  107. package/dist/implementations/agent/TaskTool.js +424 -0
  108. package/dist/implementations/agent/TaskTool.js.map +1 -0
  109. package/dist/implementations/agent/index.d.ts +5 -0
  110. package/dist/implementations/agent/index.d.ts.map +1 -0
  111. package/dist/implementations/agent/index.js +3 -0
  112. package/dist/implementations/agent/index.js.map +1 -0
  113. package/dist/implementations/execution/BackgroundProcessRegistry.d.ts +68 -0
  114. package/dist/implementations/execution/BackgroundProcessRegistry.d.ts.map +1 -0
  115. package/dist/implementations/execution/BackgroundProcessRegistry.js +146 -0
  116. package/dist/implementations/execution/BackgroundProcessRegistry.js.map +1 -0
  117. package/dist/implementations/execution/BashOutputTool.d.ts +42 -0
  118. package/dist/implementations/execution/BashOutputTool.d.ts.map +1 -0
  119. package/dist/implementations/execution/BashOutputTool.js +168 -0
  120. package/dist/implementations/execution/BashOutputTool.js.map +1 -0
  121. package/dist/implementations/execution/CodeExecuteTool.d.ts +31 -0
  122. package/dist/implementations/execution/CodeExecuteTool.d.ts.map +1 -0
  123. package/dist/implementations/execution/CodeExecuteTool.js +127 -0
  124. package/dist/implementations/execution/CodeExecuteTool.js.map +1 -0
  125. package/dist/implementations/execution/KillShellTool.d.ts +37 -0
  126. package/dist/implementations/execution/KillShellTool.d.ts.map +1 -0
  127. package/dist/implementations/execution/KillShellTool.js +144 -0
  128. package/dist/implementations/execution/KillShellTool.js.map +1 -0
  129. package/dist/implementations/execution/SearchToolsTool.d.ts +32 -0
  130. package/dist/implementations/execution/SearchToolsTool.d.ts.map +1 -0
  131. package/dist/implementations/execution/SearchToolsTool.js +109 -0
  132. package/dist/implementations/execution/SearchToolsTool.js.map +1 -0
  133. package/dist/implementations/execution/ShellTool.d.ts +108 -0
  134. package/dist/implementations/execution/ShellTool.d.ts.map +1 -0
  135. package/dist/implementations/execution/ShellTool.js +546 -0
  136. package/dist/implementations/execution/ShellTool.js.map +1 -0
  137. package/dist/implementations/execution/WorkspaceManagerTool.d.ts +40 -0
  138. package/dist/implementations/execution/WorkspaceManagerTool.d.ts.map +1 -0
  139. package/dist/implementations/execution/WorkspaceManagerTool.js +370 -0
  140. package/dist/implementations/execution/WorkspaceManagerTool.js.map +1 -0
  141. package/dist/implementations/execution/index.d.ts +13 -0
  142. package/dist/implementations/execution/index.d.ts.map +1 -0
  143. package/dist/implementations/execution/index.js +13 -0
  144. package/dist/implementations/execution/index.js.map +1 -0
  145. package/dist/implementations/extensions/EndTurnTool.d.ts +62 -0
  146. package/dist/implementations/extensions/EndTurnTool.d.ts.map +1 -0
  147. package/dist/implementations/extensions/EndTurnTool.js +172 -0
  148. package/dist/implementations/extensions/EndTurnTool.js.map +1 -0
  149. package/dist/implementations/extensions/ResearchBacklogTool.d.ts +37 -0
  150. package/dist/implementations/extensions/ResearchBacklogTool.d.ts.map +1 -0
  151. package/dist/implementations/extensions/ResearchBacklogTool.js +102 -0
  152. package/dist/implementations/extensions/ResearchBacklogTool.js.map +1 -0
  153. package/dist/implementations/extensions/SkillTool.d.ts +108 -0
  154. package/dist/implementations/extensions/SkillTool.d.ts.map +1 -0
  155. package/dist/implementations/extensions/SkillTool.js +351 -0
  156. package/dist/implementations/extensions/SkillTool.js.map +1 -0
  157. package/dist/implementations/extensions/SlashCommandTool.d.ts +112 -0
  158. package/dist/implementations/extensions/SlashCommandTool.d.ts.map +1 -0
  159. package/dist/implementations/extensions/SlashCommandTool.js +315 -0
  160. package/dist/implementations/extensions/SlashCommandTool.js.map +1 -0
  161. package/dist/implementations/extensions/index.d.ts +14 -0
  162. package/dist/implementations/extensions/index.d.ts.map +1 -0
  163. package/dist/implementations/extensions/index.js +10 -0
  164. package/dist/implementations/extensions/index.js.map +1 -0
  165. package/dist/implementations/file/EditTool.d.ts +232 -0
  166. package/dist/implementations/file/EditTool.d.ts.map +1 -0
  167. package/dist/implementations/file/EditTool.js +707 -0
  168. package/dist/implementations/file/EditTool.js.map +1 -0
  169. package/dist/implementations/file/ReadFileTool.d.ts +49 -0
  170. package/dist/implementations/file/ReadFileTool.d.ts.map +1 -0
  171. package/dist/implementations/file/ReadFileTool.js +225 -0
  172. package/dist/implementations/file/ReadFileTool.js.map +1 -0
  173. package/dist/implementations/file/WriteBinaryTool.d.ts +21 -0
  174. package/dist/implementations/file/WriteBinaryTool.d.ts.map +1 -0
  175. package/dist/implementations/file/WriteBinaryTool.js +153 -0
  176. package/dist/implementations/file/WriteBinaryTool.js.map +1 -0
  177. package/dist/implementations/file/WriteFileTool.d.ts +41 -0
  178. package/dist/implementations/file/WriteFileTool.d.ts.map +1 -0
  179. package/dist/implementations/file/WriteFileTool.js +220 -0
  180. package/dist/implementations/file/WriteFileTool.js.map +1 -0
  181. package/dist/implementations/file/index.d.ts +8 -0
  182. package/dist/implementations/file/index.d.ts.map +1 -0
  183. package/dist/implementations/file/index.js +8 -0
  184. package/dist/implementations/file/index.js.map +1 -0
  185. package/dist/implementations/historical/GetConversationSegmentTool.d.ts +44 -0
  186. package/dist/implementations/historical/GetConversationSegmentTool.d.ts.map +1 -0
  187. package/dist/implementations/historical/GetConversationSegmentTool.js +220 -0
  188. package/dist/implementations/historical/GetConversationSegmentTool.js.map +1 -0
  189. package/dist/implementations/historical/ListCompactionBoundariesTool.d.ts +36 -0
  190. package/dist/implementations/historical/ListCompactionBoundariesTool.d.ts.map +1 -0
  191. package/dist/implementations/historical/ListCompactionBoundariesTool.js +174 -0
  192. package/dist/implementations/historical/ListCompactionBoundariesTool.js.map +1 -0
  193. package/dist/implementations/historical/ListSessionsTool.d.ts +38 -0
  194. package/dist/implementations/historical/ListSessionsTool.d.ts.map +1 -0
  195. package/dist/implementations/historical/ListSessionsTool.js +140 -0
  196. package/dist/implementations/historical/ListSessionsTool.js.map +1 -0
  197. package/dist/implementations/historical/LoadSessionTool.d.ts +39 -0
  198. package/dist/implementations/historical/LoadSessionTool.d.ts.map +1 -0
  199. package/dist/implementations/historical/LoadSessionTool.js +171 -0
  200. package/dist/implementations/historical/LoadSessionTool.js.map +1 -0
  201. package/dist/implementations/historical/RequestHistoricalContextTool.d.ts +46 -0
  202. package/dist/implementations/historical/RequestHistoricalContextTool.d.ts.map +1 -0
  203. package/dist/implementations/historical/RequestHistoricalContextTool.js +224 -0
  204. package/dist/implementations/historical/RequestHistoricalContextTool.js.map +1 -0
  205. package/dist/implementations/historical/SearchConversationHistoryTool.d.ts +51 -0
  206. package/dist/implementations/historical/SearchConversationHistoryTool.d.ts.map +1 -0
  207. package/dist/implementations/historical/SearchConversationHistoryTool.js +306 -0
  208. package/dist/implementations/historical/SearchConversationHistoryTool.js.map +1 -0
  209. package/dist/implementations/historical/index.d.ts +12 -0
  210. package/dist/implementations/historical/index.d.ts.map +1 -0
  211. package/dist/implementations/historical/index.js +12 -0
  212. package/dist/implementations/historical/index.js.map +1 -0
  213. package/dist/implementations/index.d.ts +16 -0
  214. package/dist/implementations/index.d.ts.map +1 -0
  215. package/dist/implementations/index.js +28 -0
  216. package/dist/implementations/index.js.map +1 -0
  217. package/dist/implementations/mcp/DiscoveredMcpTool.d.ts +58 -0
  218. package/dist/implementations/mcp/DiscoveredMcpTool.d.ts.map +1 -0
  219. package/dist/implementations/mcp/DiscoveredMcpTool.js +269 -0
  220. package/dist/implementations/mcp/DiscoveredMcpTool.js.map +1 -0
  221. package/dist/implementations/mcp/index.d.ts +9 -0
  222. package/dist/implementations/mcp/index.d.ts.map +1 -0
  223. package/dist/implementations/mcp/index.js +8 -0
  224. package/dist/implementations/mcp/index.js.map +1 -0
  225. package/dist/implementations/notebook/NotebookEditTool.d.ts +96 -0
  226. package/dist/implementations/notebook/NotebookEditTool.d.ts.map +1 -0
  227. package/dist/implementations/notebook/NotebookEditTool.js +390 -0
  228. package/dist/implementations/notebook/NotebookEditTool.js.map +1 -0
  229. package/dist/implementations/notebook/index.d.ts +7 -0
  230. package/dist/implementations/notebook/index.d.ts.map +1 -0
  231. package/dist/implementations/notebook/index.js +7 -0
  232. package/dist/implementations/notebook/index.js.map +1 -0
  233. package/dist/implementations/search/GlobTool.d.ts +73 -0
  234. package/dist/implementations/search/GlobTool.d.ts.map +1 -0
  235. package/dist/implementations/search/GlobTool.js +213 -0
  236. package/dist/implementations/search/GlobTool.js.map +1 -0
  237. package/dist/implementations/search/GrepTool.d.ts +102 -0
  238. package/dist/implementations/search/GrepTool.d.ts.map +1 -0
  239. package/dist/implementations/search/GrepTool.js +754 -0
  240. package/dist/implementations/search/GrepTool.js.map +1 -0
  241. package/dist/implementations/search/index.d.ts +6 -0
  242. package/dist/implementations/search/index.d.ts.map +1 -0
  243. package/dist/implementations/search/index.js +6 -0
  244. package/dist/implementations/search/index.js.map +1 -0
  245. package/dist/implementations/tmux/TmuxSessionTool.d.ts +82 -0
  246. package/dist/implementations/tmux/TmuxSessionTool.d.ts.map +1 -0
  247. package/dist/implementations/tmux/TmuxSessionTool.js +371 -0
  248. package/dist/implementations/tmux/TmuxSessionTool.js.map +1 -0
  249. package/dist/implementations/tmux/TmuxViewServer.d.ts +86 -0
  250. package/dist/implementations/tmux/TmuxViewServer.d.ts.map +1 -0
  251. package/dist/implementations/tmux/TmuxViewServer.js +480 -0
  252. package/dist/implementations/tmux/TmuxViewServer.js.map +1 -0
  253. package/dist/implementations/tmux/index.d.ts +6 -0
  254. package/dist/implementations/tmux/index.d.ts.map +1 -0
  255. package/dist/implementations/tmux/index.js +6 -0
  256. package/dist/implementations/tmux/index.js.map +1 -0
  257. package/dist/implementations/ui/AskUserQuestionTool.d.ts +77 -0
  258. package/dist/implementations/ui/AskUserQuestionTool.d.ts.map +1 -0
  259. package/dist/implementations/ui/AskUserQuestionTool.js +241 -0
  260. package/dist/implementations/ui/AskUserQuestionTool.js.map +1 -0
  261. package/dist/implementations/ui/ExitPlanModeTool.d.ts +44 -0
  262. package/dist/implementations/ui/ExitPlanModeTool.d.ts.map +1 -0
  263. package/dist/implementations/ui/ExitPlanModeTool.js +150 -0
  264. package/dist/implementations/ui/ExitPlanModeTool.js.map +1 -0
  265. package/dist/implementations/ui/TodoWriteTool.d.ts +59 -0
  266. package/dist/implementations/ui/TodoWriteTool.d.ts.map +1 -0
  267. package/dist/implementations/ui/TodoWriteTool.js +315 -0
  268. package/dist/implementations/ui/TodoWriteTool.js.map +1 -0
  269. package/dist/implementations/ui/index.d.ts +9 -0
  270. package/dist/implementations/ui/index.d.ts.map +1 -0
  271. package/dist/implementations/ui/index.js +9 -0
  272. package/dist/implementations/ui/index.js.map +1 -0
  273. package/dist/implementations/web/BrowseTool.d.ts +43 -0
  274. package/dist/implementations/web/BrowseTool.d.ts.map +1 -0
  275. package/dist/implementations/web/BrowseTool.js +181 -0
  276. package/dist/implementations/web/BrowseTool.js.map +1 -0
  277. package/dist/implementations/web/SandboxTransferTool.d.ts +30 -0
  278. package/dist/implementations/web/SandboxTransferTool.d.ts.map +1 -0
  279. package/dist/implementations/web/SandboxTransferTool.js +261 -0
  280. package/dist/implementations/web/SandboxTransferTool.js.map +1 -0
  281. package/dist/implementations/web/WebFetchTool.d.ts +93 -0
  282. package/dist/implementations/web/WebFetchTool.d.ts.map +1 -0
  283. package/dist/implementations/web/WebFetchTool.js +484 -0
  284. package/dist/implementations/web/WebFetchTool.js.map +1 -0
  285. package/dist/implementations/web/WebSearchTool.d.ts +53 -0
  286. package/dist/implementations/web/WebSearchTool.d.ts.map +1 -0
  287. package/dist/implementations/web/WebSearchTool.js +227 -0
  288. package/dist/implementations/web/WebSearchTool.js.map +1 -0
  289. package/dist/implementations/web/escalateDirective.d.ts +11 -0
  290. package/dist/implementations/web/escalateDirective.d.ts.map +1 -0
  291. package/dist/implementations/web/escalateDirective.js +20 -0
  292. package/dist/implementations/web/escalateDirective.js.map +1 -0
  293. package/dist/implementations/web/index.d.ts +10 -0
  294. package/dist/implementations/web/index.d.ts.map +1 -0
  295. package/dist/implementations/web/index.js +10 -0
  296. package/dist/implementations/web/index.js.map +1 -0
  297. package/dist/implementations/web/webBackends.d.ts +65 -0
  298. package/dist/implementations/web/webBackends.d.ts.map +1 -0
  299. package/dist/implementations/web/webBackends.js +430 -0
  300. package/dist/implementations/web/webBackends.js.map +1 -0
  301. package/dist/implementations/web/webFetchRequestInit.d.ts +9 -0
  302. package/dist/implementations/web/webFetchRequestInit.d.ts.map +1 -0
  303. package/dist/implementations/web/webFetchRequestInit.js +21 -0
  304. package/dist/implementations/web/webFetchRequestInit.js.map +1 -0
  305. package/dist/index.d.ts +14 -0
  306. package/dist/index.d.ts.map +1 -0
  307. package/dist/index.js +18 -0
  308. package/dist/index.js.map +1 -0
  309. package/dist/utils/ArtifactRegistry.d.ts +138 -0
  310. package/dist/utils/ArtifactRegistry.d.ts.map +1 -0
  311. package/dist/utils/ArtifactRegistry.js +259 -0
  312. package/dist/utils/ArtifactRegistry.js.map +1 -0
  313. package/dist/utils/ChromiumBrowserManager.d.ts +56 -0
  314. package/dist/utils/ChromiumBrowserManager.d.ts.map +1 -0
  315. package/dist/utils/ChromiumBrowserManager.js +243 -0
  316. package/dist/utils/ChromiumBrowserManager.js.map +1 -0
  317. package/dist/utils/FileUtils.d.ts +81 -0
  318. package/dist/utils/FileUtils.d.ts.map +1 -0
  319. package/dist/utils/FileUtils.js +148 -0
  320. package/dist/utils/FileUtils.js.map +1 -0
  321. package/dist/utils/GitPolicy.d.ts +70 -0
  322. package/dist/utils/GitPolicy.d.ts.map +1 -0
  323. package/dist/utils/GitPolicy.js +166 -0
  324. package/dist/utils/GitPolicy.js.map +1 -0
  325. package/dist/utils/GitUtils.d.ts +18 -0
  326. package/dist/utils/GitUtils.d.ts.map +1 -0
  327. package/dist/utils/GitUtils.js +62 -0
  328. package/dist/utils/GitUtils.js.map +1 -0
  329. package/dist/utils/SandboxRegistry.d.ts +110 -0
  330. package/dist/utils/SandboxRegistry.d.ts.map +1 -0
  331. package/dist/utils/SandboxRegistry.js +220 -0
  332. package/dist/utils/SandboxRegistry.js.map +1 -0
  333. package/dist/utils/SchemaValidator.d.ts +21 -0
  334. package/dist/utils/SchemaValidator.d.ts.map +1 -0
  335. package/dist/utils/SchemaValidator.js +67 -0
  336. package/dist/utils/SchemaValidator.js.map +1 -0
  337. package/dist/utils/SessionLock.d.ts +96 -0
  338. package/dist/utils/SessionLock.d.ts.map +1 -0
  339. package/dist/utils/SessionLock.js +276 -0
  340. package/dist/utils/SessionLock.js.map +1 -0
  341. package/dist/utils/SessionPersistence.d.ts +89 -0
  342. package/dist/utils/SessionPersistence.d.ts.map +1 -0
  343. package/dist/utils/SessionPersistence.js +244 -0
  344. package/dist/utils/SessionPersistence.js.map +1 -0
  345. package/dist/utils/TextUtils.d.ts +77 -0
  346. package/dist/utils/TextUtils.d.ts.map +1 -0
  347. package/dist/utils/TextUtils.js +112 -0
  348. package/dist/utils/TextUtils.js.map +1 -0
  349. package/dist/utils/TmuxCapture.d.ts +94 -0
  350. package/dist/utils/TmuxCapture.d.ts.map +1 -0
  351. package/dist/utils/TmuxCapture.js +131 -0
  352. package/dist/utils/TmuxCapture.js.map +1 -0
  353. package/dist/utils/TmuxManager.d.ts +65 -0
  354. package/dist/utils/TmuxManager.d.ts.map +1 -0
  355. package/dist/utils/TmuxManager.js +304 -0
  356. package/dist/utils/TmuxManager.js.map +1 -0
  357. package/dist/utils/autoResearchPlanGate.d.ts +10 -0
  358. package/dist/utils/autoResearchPlanGate.d.ts.map +1 -0
  359. package/dist/utils/autoResearchPlanGate.js +57 -0
  360. package/dist/utils/autoResearchPlanGate.js.map +1 -0
  361. package/dist/utils/index.d.ts +19 -0
  362. package/dist/utils/index.d.ts.map +1 -0
  363. package/dist/utils/index.js +13 -0
  364. package/dist/utils/index.js.map +1 -0
  365. package/package.json +83 -0
@@ -0,0 +1,707 @@
1
+ /**
2
+ * EditFile Tool Executor
3
+ *
4
+ * Performs exact string replacements within a file.
5
+ * Requires precise matching of old_string for safety.
6
+ *
7
+ * Adapted and simplified from Gemini CLI patterns
8
+ * - Removed: ModifiableTool interface, user approval, LLM-based correction
9
+ * - Kept: Core string replacement, occurrence validation, diff generation
10
+ *
11
+ * IMPORTANT: Following the read-before-edit protocol, this tool requires a prior Read
12
+ * operation in the same session before allowing edits. This ensures the LLM
13
+ * has current file content and prevents edits based on stale context.
14
+ */
15
+ import fs from 'fs';
16
+ import path from 'path';
17
+ import crypto from 'crypto';
18
+ import * as Diff from 'diff';
19
+ import { BaseTool } from '../../base/index.js';
20
+ import { SchemaValidator } from '../../utils/SchemaValidator.js';
21
+ import { makeRelative, shortenPath, fileExists, resolveFilePath, } from '../../utils/FileUtils.js';
22
+ import { safeLiteralReplace, countOccurrences, normalizeLineEndings, } from '../../utils/TextUtils.js';
23
+ /**
24
+ * Session-level tracking of files that have been read/edited.
25
+ * This enforces the read-before-edit protocol with timestamp-based staleness detection
26
+ * and content-based fingerprinting for smarter freshness checks.
27
+ *
28
+ * Key behavior (read-before-edit):
29
+ * - Files must be read before editing
30
+ * - After each edit, the file becomes "stale" and must be re-read
31
+ * - Content fingerprinting allows detecting if unrelated edits affect your target section
32
+ * - This ensures LLM always has current file state in evolving codebases
33
+ */
34
+ export class FileReadTracker {
35
+ static fileReadTimestamps = new Map();
36
+ static fileEditTimestamps = new Map();
37
+ static editedSections = new Map();
38
+ // Phase 1: Section fingerprinting for content-based freshness detection
39
+ // Key: filePath, Value: Map of "startLine-endLine" -> SectionFingerprint
40
+ static sectionFingerprints = new Map();
41
+ // Phase 2: Consecutive edit tracking for brief read mode
42
+ static MAX_CONSECUTIVE_EDITS = 2;
43
+ static consecutiveEditCount = new Map();
44
+ static lastEditRegion = new Map();
45
+ /**
46
+ * Calculate SHA-256 hash of content, truncated to 16 characters
47
+ * This is efficient and provides sufficient uniqueness for content comparison
48
+ */
49
+ static calculateSectionHash(content) {
50
+ return crypto.createHash('sha256').update(content).digest('hex').substring(0, 16);
51
+ }
52
+ /**
53
+ * Store a fingerprint for a read section
54
+ * Called by ReadFileTool after successfully reading a section
55
+ */
56
+ static storeSectionFingerprint(filePath, startLine, endLine, content) {
57
+ const key = `${startLine}-${endLine}`;
58
+ const fingerprint = {
59
+ startLine,
60
+ endLine,
61
+ contentHash: this.calculateSectionHash(content),
62
+ timestamp: Date.now()
63
+ };
64
+ let fileFingerprints = this.sectionFingerprints.get(filePath);
65
+ if (!fileFingerprints) {
66
+ fileFingerprints = new Map();
67
+ this.sectionFingerprints.set(filePath, fileFingerprints);
68
+ }
69
+ fileFingerprints.set(key, fingerprint);
70
+ }
71
+ /**
72
+ * Update fingerprint after a successful edit
73
+ * This keeps the fingerprint in sync with our known changes, enabling consecutive edits.
74
+ * The key insight: after WE edit the file, we know the new content is correct.
75
+ * We update the fingerprint so subsequent edits can verify no EXTERNAL changes occurred.
76
+ */
77
+ static updateFingerprintAfterEdit(filePath, startLine, endLine, newContent) {
78
+ // Update all fingerprints that overlap with the edited region
79
+ const fileFingerprints = this.sectionFingerprints.get(filePath);
80
+ if (!fileFingerprints) {
81
+ // No existing fingerprints, create one for the edited region
82
+ this.storeSectionFingerprint(filePath, startLine, endLine, newContent);
83
+ return;
84
+ }
85
+ // Update existing fingerprints that overlap with edited region
86
+ // Also add the new edit region as a fingerprint
87
+ this.storeSectionFingerprint(filePath, startLine, endLine, newContent);
88
+ // For any overlapping fingerprints, update them with current file content
89
+ try {
90
+ const content = fs.readFileSync(filePath, 'utf-8');
91
+ const lines = content.split('\n');
92
+ for (const [key, fingerprint] of fileFingerprints) {
93
+ // Check if this fingerprint overlaps with edited region
94
+ const overlaps = !(fingerprint.endLine <= startLine || fingerprint.startLine >= endLine);
95
+ if (overlaps) {
96
+ // Update this fingerprint with current content
97
+ const sectionContent = lines.slice(fingerprint.startLine, fingerprint.endLine).join('\n');
98
+ fingerprint.contentHash = this.calculateSectionHash(sectionContent);
99
+ fingerprint.timestamp = Date.now();
100
+ }
101
+ }
102
+ }
103
+ catch {
104
+ // File read error - leave fingerprints as-is
105
+ }
106
+ }
107
+ /**
108
+ * Check if a previously read section still has the same content
109
+ * Returns true if the section's current content matches the stored fingerprint
110
+ */
111
+ static isSectionFresh(filePath, startLine, endLine) {
112
+ const key = `${startLine}-${endLine}`;
113
+ const fileFingerprints = this.sectionFingerprints.get(filePath);
114
+ if (!fileFingerprints) {
115
+ return false; // No fingerprint stored
116
+ }
117
+ const storedFingerprint = fileFingerprints.get(key);
118
+ if (!storedFingerprint) {
119
+ return false; // No fingerprint for this section
120
+ }
121
+ // Read the current content of this section and compare hashes
122
+ try {
123
+ const content = fs.readFileSync(filePath, 'utf-8');
124
+ const lines = content.split('\n');
125
+ const sectionContent = lines.slice(startLine, endLine).join('\n');
126
+ const currentHash = this.calculateSectionHash(sectionContent);
127
+ return currentHash === storedFingerprint.contentHash;
128
+ }
129
+ catch {
130
+ return false; // File read error - consider stale
131
+ }
132
+ }
133
+ /**
134
+ * Check if a line range overlaps or is adjacent to another range
135
+ * Used for brief read mode to allow consecutive edits in same region
136
+ */
137
+ static isRegionAdjacent(range1, range2, tolerance = 10) {
138
+ // Check if ranges overlap or are within tolerance lines of each other
139
+ const gap = Math.max(0, Math.max(range1.startLine, range2.startLine) -
140
+ Math.min(range1.endLine, range2.endLine));
141
+ return gap <= tolerance;
142
+ }
143
+ /**
144
+ * Mark a file as read at current timestamp
145
+ * Optionally store content fingerprint for section-based freshness detection
146
+ */
147
+ static markAsRead(filePath, startLine, endLine, content) {
148
+ this.fileReadTimestamps.set(filePath, Date.now());
149
+ // Track which sections were read (for smart re-read suggestions)
150
+ if (startLine !== undefined && endLine !== undefined) {
151
+ const sections = this.editedSections.get(filePath) || [];
152
+ sections.push({ startLine, endLine });
153
+ this.editedSections.set(filePath, sections);
154
+ // Store content fingerprint if content is provided
155
+ if (content !== undefined) {
156
+ this.storeSectionFingerprint(filePath, startLine, endLine, content);
157
+ }
158
+ }
159
+ // Reset consecutive edit count on fresh read
160
+ this.consecutiveEditCount.set(filePath, 0);
161
+ }
162
+ /**
163
+ * Mark a file as edited at current timestamp
164
+ * Tracks the edit region for consecutive edit allowance in brief read mode
165
+ *
166
+ * @param filePath Path to the file being edited
167
+ * @param startLine Optional start line of the edit (for region tracking)
168
+ * @param endLine Optional end line of the edit (for region tracking)
169
+ */
170
+ static markAsEdited(filePath, startLine, endLine) {
171
+ const now = Date.now();
172
+ this.fileEditTimestamps.set(filePath, now);
173
+ // A successful edit proves the model knows the file content (old_string matched).
174
+ // Bump read timestamp so the file stays "fresh" — no forced re-read for the
175
+ // model's own edits. Only external modifications should trigger staleness.
176
+ this.fileReadTimestamps.set(filePath, now + 1);
177
+ // Track consecutive edit count for brief read mode
178
+ const currentCount = this.consecutiveEditCount.get(filePath) || 0;
179
+ this.consecutiveEditCount.set(filePath, currentCount + 1);
180
+ // Track the edit region for adjacent edit detection
181
+ if (startLine !== undefined && endLine !== undefined) {
182
+ this.lastEditRegion.set(filePath, { startLine, endLine });
183
+ }
184
+ }
185
+ /**
186
+ * Check if an edit should be allowed under brief read mode
187
+ * Returns true if:
188
+ * 1. We're under MAX_CONSECUTIVE_EDITS
189
+ * 2. The edit is in the same or adjacent region as previous edits
190
+ * 3. The section's content hash still matches (no external changes)
191
+ */
192
+ static canSkipReRead(filePath, editStartLine, editEndLine) {
193
+ // Check consecutive edit count
194
+ const editCount = this.consecutiveEditCount.get(filePath) || 0;
195
+ if (editCount >= this.MAX_CONSECUTIVE_EDITS) {
196
+ return false; // Exceeded max consecutive edits
197
+ }
198
+ // If we have edit region info, check if this edit is in adjacent region
199
+ const lastRegion = this.lastEditRegion.get(filePath);
200
+ if (lastRegion && editStartLine !== undefined && editEndLine !== undefined) {
201
+ const newRegion = { startLine: editStartLine, endLine: editEndLine };
202
+ if (!this.isRegionAdjacent(lastRegion, newRegion)) {
203
+ return false; // Edit is in a different region
204
+ }
205
+ }
206
+ // Check if any stored fingerprint for this file still matches
207
+ const fileFingerprints = this.sectionFingerprints.get(filePath);
208
+ if (!fileFingerprints || fileFingerprints.size === 0) {
209
+ return false; // No fingerprints stored
210
+ }
211
+ // Verify at least one section's content hash still matches
212
+ for (const [key, fingerprint] of fileFingerprints) {
213
+ if (this.isSectionFresh(filePath, fingerprint.startLine, fingerprint.endLine)) {
214
+ return true; // Found a fresh section - content hasn't been externally modified
215
+ }
216
+ }
217
+ return false; // No fresh sections found
218
+ }
219
+ /**
220
+ * Check if file has been read AND is still fresh (not edited since last read)
221
+ * Matches a standard coding CLI behavior: require re-read after each edit
222
+ */
223
+ static hasBeenRead(filePath) {
224
+ const readTime = this.fileReadTimestamps.get(filePath);
225
+ if (!readTime) {
226
+ return false; // Never read
227
+ }
228
+ const editTime = this.fileEditTimestamps.get(filePath);
229
+ if (editTime && editTime > readTime) {
230
+ return false; // Edited after last read - stale!
231
+ }
232
+ return true; // Read and still fresh
233
+ }
234
+ /**
235
+ * Get suggested read parameters for re-reading after edit
236
+ * Returns the section around the last edit for efficient re-reading
237
+ */
238
+ static getSuggestedReadParams(filePath) {
239
+ const sections = this.editedSections.get(filePath);
240
+ if (!sections || sections.length === 0) {
241
+ return null;
242
+ }
243
+ // Get last edited section
244
+ const lastSection = sections[sections.length - 1];
245
+ if (!lastSection) {
246
+ return null;
247
+ }
248
+ // Read with context: 10 lines before, the section, 10 lines after
249
+ const contextLines = 10;
250
+ const offset = Math.max(0, lastSection.startLine - contextLines);
251
+ const limit = (lastSection.endLine - lastSection.startLine) + (contextLines * 2);
252
+ return { offset, limit };
253
+ }
254
+ /**
255
+ * Find where a string appears in a file and return suggested read parameters
256
+ * This enables smart error messages that tell the model exactly where to read
257
+ *
258
+ * @param filePath Path to the file
259
+ * @param searchString The string to find (typically old_string from failed edit)
260
+ * @param contextLines Number of lines of context above/below (default: 10)
261
+ * @returns Suggested offset/limit, or null if string not found or file can't be read
262
+ */
263
+ static findStringInFile(filePath, searchString, contextLines = 10) {
264
+ try {
265
+ // Read file synchronously (validation is sync)
266
+ const fs = require('fs');
267
+ const content = fs.readFileSync(filePath, 'utf-8');
268
+ const lines = content.split('\n');
269
+ // Find the line containing the start of the search string
270
+ // Handle multi-line strings by looking for the first line
271
+ const firstLineOfSearch = searchString.split('\n')[0];
272
+ for (let i = 0; i < lines.length; i++) {
273
+ if (lines[i]?.includes(firstLineOfSearch)) {
274
+ // Found it! Calculate how many lines the old_string spans
275
+ const searchLineCount = searchString.split('\n').length;
276
+ // Return offset with context above, limit covers string + context below
277
+ const offset = Math.max(0, i - contextLines);
278
+ const limit = searchLineCount + (contextLines * 2);
279
+ return {
280
+ offset,
281
+ limit,
282
+ lineNumber: i + 1 // 1-indexed for display
283
+ };
284
+ }
285
+ }
286
+ return null; // String not found in file
287
+ }
288
+ catch {
289
+ return null; // File read error
290
+ }
291
+ }
292
+ /**
293
+ * Clear all session state
294
+ */
295
+ static clearSession() {
296
+ this.fileReadTimestamps.clear();
297
+ this.fileEditTimestamps.clear();
298
+ this.editedSections.clear();
299
+ this.sectionFingerprints.clear();
300
+ this.consecutiveEditCount.clear();
301
+ this.lastEditRegion.clear();
302
+ }
303
+ /**
304
+ * Get list of files read in this session
305
+ */
306
+ static getReadFiles() {
307
+ return Array.from(this.fileReadTimestamps.keys());
308
+ }
309
+ /**
310
+ * Check if file is stale (edited after last read)
311
+ */
312
+ static isStale(filePath) {
313
+ const readTime = this.fileReadTimestamps.get(filePath);
314
+ const editTime = this.fileEditTimestamps.get(filePath);
315
+ if (!readTime)
316
+ return false; // Not read yet, not stale
317
+ if (!editTime)
318
+ return false; // Never edited, not stale
319
+ return editTime > readTime; // Stale if edited after read
320
+ }
321
+ /**
322
+ * Cross-agent staleness: of the files THIS session has read, which have
323
+ * changed on disk since they were read — by ANY writer (the user, another
324
+ * agent, or an external process)? Unlike `isStale` (which only sees this
325
+ * agent's own EditTool edits via `markAsEdited`), this compares each read
326
+ * file's CURRENT disk mtime against the read timestamp. mtime is bumped by
327
+ * every writer, so this catches uncommitted changes made outside this agent.
328
+ *
329
+ * Used by the harness to warn the model to re-read before editing when two
330
+ * agents (or a human + agent) share one working tree.
331
+ *
332
+ * @returns Files read this session that are now modified or deleted on disk.
333
+ */
334
+ static getExternallyChangedFiles() {
335
+ const changed = [];
336
+ for (const [filePath, readTime] of this.fileReadTimestamps) {
337
+ try {
338
+ const mtimeMs = fs.statSync(filePath).mtimeMs;
339
+ // Small epsilon guard: only flag when the on-disk mtime is meaningfully
340
+ // newer than the moment we read it.
341
+ if (mtimeMs > readTime + 1) {
342
+ changed.push({ path: filePath, deleted: false });
343
+ }
344
+ }
345
+ catch {
346
+ // statSync throws if the file was deleted/moved since we read it.
347
+ changed.push({ path: filePath, deleted: true });
348
+ }
349
+ }
350
+ return changed;
351
+ }
352
+ /**
353
+ * Phase 3: Suggest optimal read parameters for multiple edit targets
354
+ * When the model plans multiple edits, this calculates a single read that covers all targets
355
+ *
356
+ * @param filePath Path to the file
357
+ * @param editTargets Array of strings to search for (old_string values for planned edits)
358
+ * @param contextLines Number of context lines above/below (default: 10)
359
+ * @returns Suggested offset/limit that covers all targets, or null if not all targets found
360
+ */
361
+ static suggestBatchRead(filePath, editTargets, contextLines = 10) {
362
+ if (editTargets.length === 0) {
363
+ return null;
364
+ }
365
+ try {
366
+ const content = fs.readFileSync(filePath, 'utf-8');
367
+ const lines = content.split('\n');
368
+ // Find line numbers for all edit targets
369
+ const coverage = [];
370
+ let minLine = Infinity;
371
+ let maxLine = -Infinity;
372
+ for (const target of editTargets) {
373
+ const firstLineOfSearch = target.split('\n')[0];
374
+ if (!firstLineOfSearch)
375
+ continue;
376
+ for (let i = 0; i < lines.length; i++) {
377
+ if (lines[i]?.includes(firstLineOfSearch)) {
378
+ const targetLineCount = target.split('\n').length;
379
+ const startLine = i;
380
+ const endLine = i + targetLineCount;
381
+ minLine = Math.min(minLine, startLine);
382
+ maxLine = Math.max(maxLine, endLine);
383
+ coverage.push({ target: firstLineOfSearch.substring(0, 40), lineNumber: i + 1 });
384
+ break; // Found this target, move to next
385
+ }
386
+ }
387
+ }
388
+ // If we didn't find all targets, return null
389
+ if (coverage.length !== editTargets.length) {
390
+ return null;
391
+ }
392
+ // Calculate bounding box with context
393
+ const offset = Math.max(0, minLine - contextLines);
394
+ const limit = (maxLine - minLine) + (contextLines * 2);
395
+ return { offset, limit, coverage };
396
+ }
397
+ catch {
398
+ return null; // File read error
399
+ }
400
+ }
401
+ /**
402
+ * Get the consecutive edit count for a file
403
+ * Useful for displaying in error messages
404
+ */
405
+ static getConsecutiveEditCount(filePath) {
406
+ return this.consecutiveEditCount.get(filePath) || 0;
407
+ }
408
+ /**
409
+ * Get the max allowed consecutive edits
410
+ */
411
+ static getMaxConsecutiveEdits() {
412
+ return this.MAX_CONSECUTIVE_EDITS;
413
+ }
414
+ }
415
+ /**
416
+ * EditFile Tool Executor
417
+ *
418
+ * Features:
419
+ * - Exact string replacement (no fuzzy matching)
420
+ * - Single or multiple occurrence replacement
421
+ * - Validates unique match (unless replace_all is true)
422
+ * - Generates diff for display
423
+ * - Security: prevents path traversal
424
+ */
425
+ export class EditTool extends BaseTool {
426
+ config;
427
+ constructor(config) {
428
+ super('Edit', 'Edit', `Performs exact string replacements in files. By default, replaces a single occurrence (requires unique match). Set replace_all to true to replace all occurrences.
429
+
430
+ CRITICAL REQUIREMENTS:
431
+ 1. file_path MUST be an absolute path
432
+ 2. old_string MUST be the EXACT literal text to replace (including all whitespace, indentation, newlines)
433
+ 3. new_string MUST be the EXACT literal text to replace old_string with
434
+ 4. For single replacements: old_string must match exactly ONE location in the file
435
+ 5. For multiple replacements: set replace_all to true
436
+
437
+ IMPORTANT: Include sufficient context (3+ lines before/after) in old_string to ensure unique matching.`, {
438
+ type: 'object',
439
+ properties: {
440
+ file_path: {
441
+ type: 'string',
442
+ description: "The absolute path to the file to modify. Must start with '/'.",
443
+ },
444
+ old_string: {
445
+ type: 'string',
446
+ description: 'The exact literal text to replace. For single replacements, include at least 3 lines of context before and after to ensure unique matching. Must match exactly (including whitespace and indentation).',
447
+ },
448
+ new_string: {
449
+ type: 'string',
450
+ description: 'The exact literal text to replace old_string with. Provide the EXACT text with proper indentation and formatting.',
451
+ },
452
+ replace_all: {
453
+ type: 'boolean',
454
+ description: 'If true, replaces all occurrences of old_string. If false (default), requires exactly one match.',
455
+ },
456
+ expected_replacements: {
457
+ type: 'number',
458
+ description: 'Optional: Number of replacements expected. Defaults to 1 for single replacements. Edit will fail if actual count does not match expected count. Use this to validate your edit affects the expected number of locations.',
459
+ },
460
+ },
461
+ required: ['file_path', 'old_string', 'new_string'],
462
+ });
463
+ this.config = config;
464
+ }
465
+ validateToolParams(params) {
466
+ // Schema validation
467
+ const schemaError = SchemaValidator.validate(this.parameterSchema, params);
468
+ if (schemaError) {
469
+ return schemaError;
470
+ }
471
+ // Normalize and resolve path (with smart fallback for doubled directory names)
472
+ let filePath = resolveFilePath(params.file_path, this.config.workingDirectory);
473
+ // Path validation: allow any absolute path (matching standard absolute-path behavior).
474
+ if (!path.isAbsolute(filePath)) {
475
+ return `File path must resolve to an absolute path: ${filePath}`;
476
+ }
477
+ // Update params with resolved path
478
+ params.file_path = filePath;
479
+ // Special case: empty old_string means create new file
480
+ if (params.old_string === '' && !fileExists(filePath)) {
481
+ return null; // Valid: creating new file
482
+ }
483
+ // MANDATORY READ-BEFORE-EDIT PROTOCOL (read-before-edit standard)
484
+ // Ensure file has been read in this session before allowing edits
485
+ // Phase 2: Brief read mode allows up to MAX_CONSECUTIVE_EDITS in same region if content unchanged
486
+ if (params.old_string !== '' && !FileReadTracker.hasBeenRead(filePath)) {
487
+ const relativePath = makeRelative(filePath, this.config.workingDirectory);
488
+ // Check if file is stale (edited after last read)
489
+ const isStale = FileReadTracker.isStale(filePath);
490
+ if (isStale) {
491
+ // Phase 2: Brief read mode - check if we can allow this edit without re-read
492
+ // Conditions: consecutive edit count < MAX, same region, content hash unchanged
493
+ const stringLocation = FileReadTracker.findStringInFile(filePath, params.old_string);
494
+ if (stringLocation) {
495
+ const editStartLine = stringLocation.lineNumber - 1; // 0-indexed
496
+ const editEndLine = editStartLine + params.old_string.split('\n').length;
497
+ if (FileReadTracker.canSkipReRead(filePath, editStartLine, editEndLine)) {
498
+ // Brief read mode: Allow edit without re-read
499
+ // The content fingerprint confirms no external changes, and we're under the limit
500
+ return null; // Allow the edit
501
+ }
502
+ }
503
+ // File was edited since last read and doesn't qualify for brief read mode
504
+ // Provide smart suggestion for re-read
505
+ const suggestedParams = FileReadTracker.getSuggestedReadParams(filePath);
506
+ if (suggestedParams) {
507
+ const editCount = FileReadTracker.getConsecutiveEditCount(filePath);
508
+ const maxEdits = FileReadTracker.getMaxConsecutiveEdits();
509
+ const briefModeMsg = editCount >= maxEdits
510
+ ? ` (Reached max ${maxEdits} consecutive edits without re-read.)`
511
+ : '';
512
+ return `File has been edited since you last read it.${briefModeMsg} You must re-read the file to see the current state before making another edit. Use: read tool with file_path: "${relativePath}", offset: ${suggestedParams.offset}, limit: ${suggestedParams.limit} to see the recently edited section with context.`;
513
+ }
514
+ // Second try: find where their old_string appears and suggest that location
515
+ if (stringLocation) {
516
+ return `File has been edited since you last read it. You must re-read the file to see the current state before making another edit.
517
+
518
+ Your edit target appears around line ${stringLocation.lineNumber}. Use:
519
+ read(file_path: "${relativePath}", offset: ${stringLocation.offset}, limit: ${stringLocation.limit})
520
+
521
+ This covers lines ${stringLocation.offset + 1}-${stringLocation.offset + stringLocation.limit} where your edit target appears.`;
522
+ }
523
+ return `File has been edited since you last read it. You must re-read the file to see the current state before making another edit. Use the read tool with file_path: "${relativePath}"`;
524
+ }
525
+ // Never read before - search for where the old_string appears and suggest targeted read
526
+ const stringLocation = FileReadTracker.findStringInFile(filePath, params.old_string);
527
+ if (stringLocation) {
528
+ return `You must read the file before editing it.
529
+
530
+ Your edit target appears around line ${stringLocation.lineNumber}. Use:
531
+ read(file_path: "${relativePath}", offset: ${stringLocation.offset}, limit: ${stringLocation.limit})
532
+
533
+ This covers lines ${stringLocation.offset + 1}-${stringLocation.offset + stringLocation.limit} where your edit target appears.`;
534
+ }
535
+ // Fallback: couldn't find the string, suggest reading the whole file
536
+ return `You must read the file before editing it. Use the read tool first to see the current file content (file_path: "${relativePath}"), then call edit with the exact text you want to replace. This ensures you have the current file state and prevents edits based on stale or assumed content.`;
537
+ }
538
+ // Check file exists for edits
539
+ if (params.old_string !== '' && !fileExists(filePath)) {
540
+ return `File not found: ${filePath}. Use write tool to create new files.`;
541
+ }
542
+ // If old_string is empty but file exists, error
543
+ if (params.old_string === '' && fileExists(filePath)) {
544
+ return `Cannot create file that already exists: ${filePath}`;
545
+ }
546
+ return null;
547
+ }
548
+ getDescription(params) {
549
+ if (!params || !params.file_path) {
550
+ return 'Edit file';
551
+ }
552
+ const relativePath = makeRelative(params.file_path, this.config.workingDirectory);
553
+ const shortened = shortenPath(relativePath);
554
+ // Special case: creating new file
555
+ if (params.old_string === '') {
556
+ return `Create ${shortened}`;
557
+ }
558
+ // Same old and new strings
559
+ if (params.old_string === params.new_string) {
560
+ return `No changes to ${shortened}`;
561
+ }
562
+ // Show snippet of change
563
+ const oldSnippet = (params.old_string.split('\n')[0] || '').substring(0, 30) +
564
+ (params.old_string.length > 30 ? '...' : '');
565
+ const newSnippet = (params.new_string.split('\n')[0] || '').substring(0, 30) +
566
+ (params.new_string.length > 30 ? '...' : '');
567
+ return `${shortened}: ${oldSnippet} => ${newSnippet}`;
568
+ }
569
+ async execute(params, signal, updateOutput) {
570
+ const startTime = Date.now();
571
+ // Validate parameters
572
+ const validationError = this.validateToolParams(params);
573
+ if (validationError) {
574
+ return this.createErrorResult(validationError);
575
+ }
576
+ try {
577
+ // Special case: creating new file
578
+ if (params.old_string === '' && !fileExists(params.file_path)) {
579
+ return await this.createNewFile(params, startTime, updateOutput);
580
+ }
581
+ // Read current file content
582
+ let currentContent = await fs.promises.readFile(params.file_path, 'utf-8');
583
+ // Normalize line endings to LF
584
+ currentContent = normalizeLineEndings(currentContent);
585
+ // Count occurrences
586
+ const occurrences = countOccurrences(currentContent, params.old_string);
587
+ // Determine expected count
588
+ const expectedReplacements = params.expected_replacements ?? (params.replace_all ? occurrences : 1);
589
+ // Validate occurrence count
590
+ if (occurrences === 0) {
591
+ return this.createErrorResult(`Failed to edit: could not find the string to replace in ${params.file_path}. ` +
592
+ `The exact text in old_string was not found. ` +
593
+ `Ensure you're matching whitespace and indentation precisely.`);
594
+ }
595
+ if (!params.replace_all && occurrences > 1) {
596
+ return this.createErrorResult(`Failed to edit: found ${occurrences} occurrences but expected exactly 1. ` +
597
+ `Either set replace_all to true or include more context in old_string to make it unique.`);
598
+ }
599
+ // Validate expected replacements if specified
600
+ if (params.expected_replacements !== undefined && occurrences !== expectedReplacements) {
601
+ return this.createErrorResult(`Failed to edit: found ${occurrences} occurrences but expected ${expectedReplacements}. ` +
602
+ `The actual count does not match the expected count. ` +
603
+ `Either update expected_replacements or verify old_string is correct.`);
604
+ }
605
+ // Perform safe literal replacement (handles $ escape sequences correctly)
606
+ const newContent = safeLiteralReplace(currentContent, params.old_string, params.new_string);
607
+ // Write updated content
608
+ await fs.promises.writeFile(params.file_path, newContent, 'utf-8');
609
+ // Calculate edit region for consecutive edit tracking
610
+ // Find the line number where old_string starts
611
+ const lines = currentContent.split('\n');
612
+ let editStartLine = 0;
613
+ const firstLineOfOldString = params.old_string.split('\n')[0] || '';
614
+ for (let i = 0; i < lines.length; i++) {
615
+ if (lines[i]?.includes(firstLineOfOldString)) {
616
+ editStartLine = i;
617
+ break;
618
+ }
619
+ }
620
+ const editEndLine = editStartLine + params.new_string.split('\n').length;
621
+ // Mark file as edited with region info (enables brief read mode tracking)
622
+ FileReadTracker.markAsEdited(params.file_path, editStartLine, editEndLine);
623
+ // Update fingerprint to reflect our edit - this enables consecutive edits
624
+ // by keeping the fingerprint in sync with our known changes
625
+ const newLines = newContent.split('\n');
626
+ const editedSectionContent = newLines.slice(editStartLine, editEndLine).join('\n');
627
+ FileReadTracker.updateFingerprintAfterEdit(params.file_path, editStartLine, editEndLine, editedSectionContent);
628
+ // Generate diff for display
629
+ const fileName = path.basename(params.file_path);
630
+ const diff = Diff.createPatch(fileName, currentContent, newContent, 'Current', 'Proposed', { context: 3 });
631
+ // Format success message
632
+ const relativePath = makeRelative(params.file_path, this.config.workingDirectory);
633
+ const displayContent = `Modified ${relativePath} (${occurrences} replacement${occurrences > 1 ? 's' : ''})`;
634
+ // Stream output if callback provided
635
+ if (updateOutput) {
636
+ updateOutput(displayContent);
637
+ }
638
+ return this.createSuccessResult(`Successfully modified file: ${params.file_path} (${occurrences} replacement${occurrences > 1 ? 's' : ''}).`, {
639
+ executionTime: Date.now() - startTime,
640
+ resourcesUsed: {
641
+ files: [params.file_path],
642
+ },
643
+ fileStats: {
644
+ path: relativePath,
645
+ occurrences,
646
+ operation: 'edit',
647
+ },
648
+ diff, // Include diff in metadata for potential UI display
649
+ });
650
+ }
651
+ catch (error) {
652
+ // Handle write errors
653
+ if (error.code === 'EACCES') {
654
+ return this.createErrorResult(`Permission denied: ${params.file_path}`);
655
+ }
656
+ if (error.code === 'ENOSPC') {
657
+ return this.createErrorResult('No space left on device');
658
+ }
659
+ if (error.code === 'EROFS') {
660
+ return this.createErrorResult('Read-only file system');
661
+ }
662
+ return this.createErrorResult(`Failed to edit file: ${error.message}`);
663
+ }
664
+ }
665
+ /**
666
+ * Creates a new file (when old_string is empty)
667
+ * @private
668
+ */
669
+ async createNewFile(params, startTime, updateOutput) {
670
+ try {
671
+ // Ensure parent directory exists
672
+ const dirPath = path.dirname(params.file_path);
673
+ if (!fs.existsSync(dirPath)) {
674
+ fs.mkdirSync(dirPath, { recursive: true });
675
+ }
676
+ // Write file
677
+ await fs.promises.writeFile(params.file_path, params.new_string, 'utf-8');
678
+ // Get file stats
679
+ const stats = await fs.promises.stat(params.file_path);
680
+ const fileSize = stats.size;
681
+ const lineCount = params.new_string.split('\n').length;
682
+ // Format for display
683
+ const relativePath = makeRelative(params.file_path, this.config.workingDirectory);
684
+ const displayContent = `Created ${relativePath} (${lineCount} lines, ${fileSize} bytes)`;
685
+ // Stream output if callback provided
686
+ if (updateOutput) {
687
+ updateOutput(displayContent);
688
+ }
689
+ return this.createSuccessResult(`Created new file: ${params.file_path} with provided content.`, {
690
+ executionTime: Date.now() - startTime,
691
+ resourcesUsed: {
692
+ files: [params.file_path],
693
+ },
694
+ fileStats: {
695
+ path: relativePath,
696
+ size: fileSize,
697
+ lines: lineCount,
698
+ operation: 'create',
699
+ },
700
+ });
701
+ }
702
+ catch (error) {
703
+ return this.createErrorResult(`Failed to create file: ${error.message}`);
704
+ }
705
+ }
706
+ }
707
+ //# sourceMappingURL=EditTool.js.map