@machina.ai/cell-cli-core 1.22.5-rc1 → 1.25.0-rc1

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 (732) hide show
  1. package/dist/docs/AFTER_MERGE_PROMPT.md +26 -0
  2. package/dist/docs/CHANGES.md +124 -0
  3. package/dist/docs/api-proxy.md +27 -0
  4. package/dist/docs/architecture.md +80 -0
  5. package/dist/docs/assets/connected_devtools.png +0 -0
  6. package/dist/docs/assets/gemini-screenshot.png +0 -0
  7. package/dist/docs/assets/monitoring-dashboard-logs.png +0 -0
  8. package/dist/docs/assets/monitoring-dashboard-metrics.png +0 -0
  9. package/dist/docs/assets/monitoring-dashboard-overview.png +0 -0
  10. package/dist/docs/assets/release_patch.png +0 -0
  11. package/dist/docs/assets/theme-ansi-light.png +0 -0
  12. package/dist/docs/assets/theme-ansi.png +0 -0
  13. package/dist/docs/assets/theme-atom-one.png +0 -0
  14. package/dist/docs/assets/theme-ayu-light.png +0 -0
  15. package/dist/docs/assets/theme-ayu.png +0 -0
  16. package/dist/docs/assets/theme-custom.png +0 -0
  17. package/dist/docs/assets/theme-default-light.png +0 -0
  18. package/dist/docs/assets/theme-default.png +0 -0
  19. package/dist/docs/assets/theme-dracula.png +0 -0
  20. package/dist/docs/assets/theme-github-light.png +0 -0
  21. package/dist/docs/assets/theme-github.png +0 -0
  22. package/dist/docs/assets/theme-google-light.png +0 -0
  23. package/dist/docs/assets/theme-xcode-light.png +0 -0
  24. package/dist/docs/changelogs/index.md +612 -0
  25. package/dist/docs/changelogs/latest.md +153 -0
  26. package/dist/docs/changelogs/preview.md +131 -0
  27. package/dist/docs/changelogs/releases.md +1162 -0
  28. package/dist/docs/cli/authentication.md +3 -0
  29. package/dist/docs/cli/checkpointing.md +94 -0
  30. package/dist/docs/cli/commands.md +357 -0
  31. package/dist/docs/cli/custom-commands.md +315 -0
  32. package/dist/docs/cli/enterprise.md +564 -0
  33. package/dist/docs/cli/gemini-ignore.md +71 -0
  34. package/dist/docs/cli/gemini-md.md +109 -0
  35. package/dist/docs/cli/generation-settings.md +210 -0
  36. package/dist/docs/cli/headless.md +388 -0
  37. package/dist/docs/cli/index.md +63 -0
  38. package/dist/docs/cli/keyboard-shortcuts.md +136 -0
  39. package/dist/docs/cli/model-routing.md +37 -0
  40. package/dist/docs/cli/model.md +62 -0
  41. package/dist/docs/cli/sandbox.md +171 -0
  42. package/dist/docs/cli/session-management.md +158 -0
  43. package/dist/docs/cli/settings.md +148 -0
  44. package/dist/docs/cli/skills.md +188 -0
  45. package/dist/docs/cli/system-prompt.md +94 -0
  46. package/dist/docs/cli/telemetry.md +813 -0
  47. package/dist/docs/cli/themes.md +237 -0
  48. package/dist/docs/cli/token-caching.md +20 -0
  49. package/dist/docs/cli/trusted-folders.md +95 -0
  50. package/dist/docs/cli/tutorials/skills-getting-started.md +124 -0
  51. package/dist/docs/cli/tutorials.md +87 -0
  52. package/dist/docs/cli/uninstall.md +47 -0
  53. package/dist/docs/core/index.md +101 -0
  54. package/dist/docs/core/memport.md +246 -0
  55. package/dist/docs/core/policy-engine.md +268 -0
  56. package/dist/docs/core/tools-api.md +131 -0
  57. package/dist/docs/examples/proxy-script.md +83 -0
  58. package/dist/docs/extensions/extension-releasing.md +183 -0
  59. package/dist/docs/extensions/getting-started-extensions.md +244 -0
  60. package/dist/docs/extensions/index.md +343 -0
  61. package/dist/docs/faq.md +153 -0
  62. package/dist/docs/get-started/authentication.md +321 -0
  63. package/dist/docs/get-started/configuration-v1.md +890 -0
  64. package/dist/docs/get-started/configuration.md +1643 -0
  65. package/dist/docs/get-started/examples.md +218 -0
  66. package/dist/docs/get-started/gemini-3.md +101 -0
  67. package/dist/docs/get-started/index.md +71 -0
  68. package/dist/docs/get-started/installation.md +141 -0
  69. package/dist/docs/hooks/best-practices.md +856 -0
  70. package/dist/docs/hooks/index.md +723 -0
  71. package/dist/docs/hooks/reference.md +178 -0
  72. package/dist/docs/hooks/writing-hooks.md +1044 -0
  73. package/dist/docs/ide-integration/ide-companion-spec.md +267 -0
  74. package/dist/docs/ide-integration/index.md +201 -0
  75. package/dist/docs/index.md +147 -0
  76. package/dist/docs/integration-tests.md +211 -0
  77. package/dist/docs/issue-and-pr-automation.md +134 -0
  78. package/dist/docs/local-development.md +128 -0
  79. package/dist/docs/mcp_integration.md +160 -0
  80. package/dist/docs/mermaid/context.mmd +103 -0
  81. package/dist/docs/mermaid/render-path.mmd +64 -0
  82. package/dist/docs/npm.md +62 -0
  83. package/dist/docs/quota-and-pricing.md +158 -0
  84. package/dist/docs/release-confidence.md +164 -0
  85. package/dist/docs/releases.md +540 -0
  86. package/dist/docs/sidebar.json +301 -0
  87. package/dist/docs/tools/file-system.md +217 -0
  88. package/dist/docs/tools/index.md +95 -0
  89. package/dist/docs/tools/mcp-server.md +1045 -0
  90. package/dist/docs/tools/memory.md +54 -0
  91. package/dist/docs/tools/shell.md +260 -0
  92. package/dist/docs/tools/todos.md +56 -0
  93. package/dist/docs/tools/web-fetch.md +59 -0
  94. package/dist/docs/tools/web-search.md +42 -0
  95. package/dist/docs/tos-privacy.md +96 -0
  96. package/dist/docs/troubleshooting.md +162 -0
  97. package/dist/package.json +7 -4
  98. package/dist/src/agents/a2a-client-manager.d.ts +82 -0
  99. package/dist/src/agents/a2a-client-manager.js +295 -0
  100. package/dist/src/agents/a2a-client-manager.js.map +1 -0
  101. package/dist/src/agents/a2a-client-manager.test.js +281 -0
  102. package/dist/src/agents/a2a-client-manager.test.js.map +1 -0
  103. package/dist/src/agents/a2aUtils.d.ts +29 -0
  104. package/dist/src/agents/a2aUtils.js +113 -0
  105. package/dist/src/agents/a2aUtils.js.map +1 -0
  106. package/dist/src/agents/a2aUtils.test.js +147 -0
  107. package/dist/src/agents/a2aUtils.test.js.map +1 -0
  108. package/dist/src/agents/agentLoader.d.ts +68 -0
  109. package/dist/src/agents/agentLoader.js +255 -0
  110. package/dist/src/agents/agentLoader.js.map +1 -0
  111. package/dist/src/agents/agentLoader.test.js +307 -0
  112. package/dist/src/agents/agentLoader.test.js.map +1 -0
  113. package/dist/src/agents/cli-help-agent.d.ts +24 -0
  114. package/dist/src/agents/cli-help-agent.js +85 -0
  115. package/dist/src/agents/cli-help-agent.js.map +1 -0
  116. package/dist/src/agents/cli-help-agent.test.js +65 -0
  117. package/dist/src/agents/cli-help-agent.test.js.map +1 -0
  118. package/dist/src/agents/codebase-investigator.d.ts +2 -2
  119. package/dist/src/agents/codebase-investigator.js +14 -8
  120. package/dist/src/agents/codebase-investigator.js.map +1 -1
  121. package/dist/src/agents/delegate-to-agent-tool.d.ts +2 -2
  122. package/dist/src/agents/delegate-to-agent-tool.js +25 -14
  123. package/dist/src/agents/delegate-to-agent-tool.js.map +1 -1
  124. package/dist/src/agents/delegate-to-agent-tool.test.js +101 -21
  125. package/dist/src/agents/delegate-to-agent-tool.test.js.map +1 -1
  126. package/dist/src/agents/{executor.d.ts → local-executor.d.ts} +5 -11
  127. package/dist/src/agents/{executor.js → local-executor.js} +123 -62
  128. package/dist/src/agents/local-executor.js.map +1 -0
  129. package/dist/src/agents/local-executor.test.d.ts +6 -0
  130. package/dist/src/agents/{executor.test.js → local-executor.test.js} +136 -58
  131. package/dist/src/agents/local-executor.test.js.map +1 -0
  132. package/dist/src/agents/{invocation.d.ts → local-invocation.d.ts} +6 -7
  133. package/dist/src/agents/{invocation.js → local-invocation.js} +9 -10
  134. package/dist/src/agents/local-invocation.js.map +1 -0
  135. package/dist/src/agents/local-invocation.test.d.ts +6 -0
  136. package/dist/src/agents/{invocation.test.js → local-invocation.test.js} +29 -20
  137. package/dist/src/agents/local-invocation.test.js.map +1 -0
  138. package/dist/src/agents/registry.d.ts +22 -1
  139. package/dist/src/agents/registry.js +192 -35
  140. package/dist/src/agents/registry.js.map +1 -1
  141. package/dist/src/agents/registry.test.js +407 -33
  142. package/dist/src/agents/registry.test.js.map +1 -1
  143. package/dist/src/agents/remote-invocation.d.ts +35 -0
  144. package/dist/src/agents/remote-invocation.js +126 -0
  145. package/dist/src/agents/remote-invocation.js.map +1 -0
  146. package/dist/src/agents/remote-invocation.test.d.ts +6 -0
  147. package/dist/src/agents/remote-invocation.test.js +201 -0
  148. package/dist/src/agents/remote-invocation.test.js.map +1 -0
  149. package/dist/src/agents/subagent-tool-wrapper.d.ts +2 -2
  150. package/dist/src/agents/subagent-tool-wrapper.js +11 -6
  151. package/dist/src/agents/subagent-tool-wrapper.js.map +1 -1
  152. package/dist/src/agents/subagent-tool-wrapper.test.js +33 -19
  153. package/dist/src/agents/subagent-tool-wrapper.test.js.map +1 -1
  154. package/dist/src/agents/types.d.ts +21 -15
  155. package/dist/src/agents/types.js.map +1 -1
  156. package/dist/src/availability/fallbackIntegration.test.d.ts +6 -0
  157. package/dist/src/availability/fallbackIntegration.test.js +58 -0
  158. package/dist/src/availability/fallbackIntegration.test.js.map +1 -0
  159. package/dist/src/availability/modelAvailabilityService.d.ts +2 -1
  160. package/dist/src/availability/policyHelpers.d.ts +4 -3
  161. package/dist/src/availability/policyHelpers.js +13 -22
  162. package/dist/src/availability/policyHelpers.js.map +1 -1
  163. package/dist/src/availability/policyHelpers.test.js +28 -18
  164. package/dist/src/availability/policyHelpers.test.js.map +1 -1
  165. package/dist/src/code_assist/experiments/experiments.d.ts +1 -1
  166. package/dist/src/code_assist/experiments/experiments.js +21 -0
  167. package/dist/src/code_assist/experiments/experiments.js.map +1 -1
  168. package/dist/src/code_assist/experiments/experiments_local.test.d.ts +6 -0
  169. package/dist/src/code_assist/experiments/experiments_local.test.js +110 -0
  170. package/dist/src/code_assist/experiments/experiments_local.test.js.map +1 -0
  171. package/dist/src/code_assist/oauth-credential-storage.js +3 -4
  172. package/dist/src/code_assist/oauth-credential-storage.js.map +1 -1
  173. package/dist/src/code_assist/oauth2.js +32 -2
  174. package/dist/src/code_assist/oauth2.js.map +1 -1
  175. package/dist/src/code_assist/oauth2.test.js +111 -16
  176. package/dist/src/code_assist/oauth2.test.js.map +1 -1
  177. package/dist/src/code_assist/server.d.ts +9 -1
  178. package/dist/src/code_assist/server.js +74 -11
  179. package/dist/src/code_assist/server.js.map +1 -1
  180. package/dist/src/code_assist/server.test.js +199 -27
  181. package/dist/src/code_assist/server.test.js.map +1 -1
  182. package/dist/src/code_assist/setup.js +6 -4
  183. package/dist/src/code_assist/setup.js.map +1 -1
  184. package/dist/src/code_assist/setup.test.js +63 -0
  185. package/dist/src/code_assist/setup.test.js.map +1 -1
  186. package/dist/src/code_assist/telemetry.d.ts +14 -0
  187. package/dist/src/code_assist/telemetry.js +157 -0
  188. package/dist/src/code_assist/telemetry.js.map +1 -0
  189. package/dist/src/code_assist/telemetry.test.d.ts +6 -0
  190. package/dist/src/code_assist/telemetry.test.js +301 -0
  191. package/dist/src/code_assist/telemetry.test.js.map +1 -0
  192. package/dist/src/code_assist/types.d.ts +77 -1
  193. package/dist/src/code_assist/types.js +28 -0
  194. package/dist/src/code_assist/types.js.map +1 -1
  195. package/dist/src/commands/memory.d.ts +11 -0
  196. package/dist/src/commands/memory.js +80 -0
  197. package/dist/src/commands/memory.js.map +1 -0
  198. package/dist/src/commands/memory.test.d.ts +6 -0
  199. package/dist/src/commands/memory.test.js +155 -0
  200. package/dist/src/commands/memory.test.js.map +1 -0
  201. package/dist/src/config/config.d.ts +120 -26
  202. package/dist/src/config/config.js +241 -120
  203. package/dist/src/config/config.js.map +1 -1
  204. package/dist/src/config/config.test.js +215 -43
  205. package/dist/src/config/config.test.js.map +1 -1
  206. package/dist/src/config/flashFallback.test.js +11 -35
  207. package/dist/src/config/flashFallback.test.js.map +1 -1
  208. package/dist/src/config/models.d.ts +8 -9
  209. package/dist/src/config/models.js +18 -15
  210. package/dist/src/config/models.js.map +1 -1
  211. package/dist/src/config/models.test.js +52 -16
  212. package/dist/src/config/models.test.js.map +1 -1
  213. package/dist/src/config/storage.d.ts +5 -0
  214. package/dist/src/config/storage.js +17 -2
  215. package/dist/src/config/storage.js.map +1 -1
  216. package/dist/src/config/storage.test.js +16 -0
  217. package/dist/src/config/storage.test.js.map +1 -1
  218. package/dist/src/confirmation-bus/message-bus.js +2 -1
  219. package/dist/src/confirmation-bus/message-bus.js.map +1 -1
  220. package/dist/src/core/baseLlmClient.js +44 -43
  221. package/dist/src/core/baseLlmClient.js.map +1 -1
  222. package/dist/src/core/baseLlmClient.test.js +12 -19
  223. package/dist/src/core/baseLlmClient.test.js.map +1 -1
  224. package/dist/src/core/client.d.ts +7 -1
  225. package/dist/src/core/client.js +245 -114
  226. package/dist/src/core/client.js.map +1 -1
  227. package/dist/src/core/client.test.js +277 -74
  228. package/dist/src/core/client.test.js.map +1 -1
  229. package/dist/src/core/clientHookTriggers.js +2 -2
  230. package/dist/src/core/clientHookTriggers.js.map +1 -1
  231. package/dist/src/core/contentGenerator.js +3 -3
  232. package/dist/src/core/contentGenerator.js.map +1 -1
  233. package/dist/src/core/contentGenerator.test.js +1 -7
  234. package/dist/src/core/contentGenerator.test.js.map +1 -1
  235. package/dist/src/core/coreToolHookTriggers.d.ts +9 -5
  236. package/dist/src/core/coreToolHookTriggers.js +119 -21
  237. package/dist/src/core/coreToolHookTriggers.js.map +1 -1
  238. package/dist/src/core/coreToolHookTriggers.test.d.ts +6 -0
  239. package/dist/src/core/coreToolHookTriggers.test.js +191 -0
  240. package/dist/src/core/coreToolHookTriggers.test.js.map +1 -0
  241. package/dist/src/core/coreToolScheduler.d.ts +7 -93
  242. package/dist/src/core/coreToolScheduler.js +133 -369
  243. package/dist/src/core/coreToolScheduler.js.map +1 -1
  244. package/dist/src/core/coreToolScheduler.test.js +252 -394
  245. package/dist/src/core/coreToolScheduler.test.js.map +1 -1
  246. package/dist/src/core/geminiChat.d.ts +26 -1
  247. package/dist/src/core/geminiChat.js +112 -79
  248. package/dist/src/core/geminiChat.js.map +1 -1
  249. package/dist/src/core/geminiChat.test.js +125 -92
  250. package/dist/src/core/geminiChat.test.js.map +1 -1
  251. package/dist/src/core/geminiChatHookTriggers.d.ts +8 -4
  252. package/dist/src/core/geminiChatHookTriggers.js +34 -12
  253. package/dist/src/core/geminiChatHookTriggers.js.map +1 -1
  254. package/dist/src/core/geminiChatHookTriggers.test.d.ts +6 -0
  255. package/dist/src/core/geminiChatHookTriggers.test.js +153 -0
  256. package/dist/src/core/geminiChatHookTriggers.test.js.map +1 -0
  257. package/dist/src/core/geminiChat_network_retry.test.js +4 -6
  258. package/dist/src/core/geminiChat_network_retry.test.js.map +1 -1
  259. package/dist/src/core/loggingContentGenerator.js +19 -2
  260. package/dist/src/core/loggingContentGenerator.js.map +1 -1
  261. package/dist/src/core/loggingContentGenerator.test.js +30 -0
  262. package/dist/src/core/loggingContentGenerator.test.js.map +1 -1
  263. package/dist/src/core/nonInteractiveToolExecutor.test.js +4 -4
  264. package/dist/src/core/nonInteractiveToolExecutor.test.js.map +1 -1
  265. package/dist/src/core/prompts.js +37 -13
  266. package/dist/src/core/prompts.js.map +1 -1
  267. package/dist/src/core/prompts.test.js +47 -3
  268. package/dist/src/core/prompts.test.js.map +1 -1
  269. package/dist/src/core/tokenLimits.js +6 -12
  270. package/dist/src/core/tokenLimits.js.map +1 -1
  271. package/dist/src/core/tokenLimits.test.js +8 -4
  272. package/dist/src/core/tokenLimits.test.js.map +1 -1
  273. package/dist/src/core/turn.d.ts +21 -22
  274. package/dist/src/core/turn.js +31 -21
  275. package/dist/src/core/turn.js.map +1 -1
  276. package/dist/src/core/turn.test.js +79 -5
  277. package/dist/src/core/turn.test.js.map +1 -1
  278. package/dist/src/fallback/handler.js +1 -7
  279. package/dist/src/fallback/handler.js.map +1 -1
  280. package/dist/src/fallback/handler.test.js +10 -29
  281. package/dist/src/fallback/handler.test.js.map +1 -1
  282. package/dist/src/generated/git-commit.d.ts +3 -3
  283. package/dist/src/generated/git-commit.js +3 -3
  284. package/dist/src/hooks/hookAggregator.js +7 -0
  285. package/dist/src/hooks/hookAggregator.js.map +1 -1
  286. package/dist/src/hooks/hookEventHandler.d.ts +9 -5
  287. package/dist/src/hooks/hookEventHandler.js +61 -15
  288. package/dist/src/hooks/hookEventHandler.js.map +1 -1
  289. package/dist/src/hooks/hookEventHandler.test.js +223 -8
  290. package/dist/src/hooks/hookEventHandler.test.js.map +1 -1
  291. package/dist/src/hooks/hookPlanner.d.ts +1 -5
  292. package/dist/src/hooks/hookPlanner.js +2 -7
  293. package/dist/src/hooks/hookPlanner.js.map +1 -1
  294. package/dist/src/hooks/hookPlanner.test.js +62 -2
  295. package/dist/src/hooks/hookPlanner.test.js.map +1 -1
  296. package/dist/src/hooks/hookRegistry.d.ts +6 -11
  297. package/dist/src/hooks/hookRegistry.js +41 -14
  298. package/dist/src/hooks/hookRegistry.js.map +1 -1
  299. package/dist/src/hooks/hookRegistry.test.js +166 -2
  300. package/dist/src/hooks/hookRegistry.test.js.map +1 -1
  301. package/dist/src/hooks/hookRunner.d.ts +5 -3
  302. package/dist/src/hooks/hookRunner.js +57 -17
  303. package/dist/src/hooks/hookRunner.js.map +1 -1
  304. package/dist/src/hooks/hookRunner.test.js +172 -35
  305. package/dist/src/hooks/hookRunner.test.js.map +1 -1
  306. package/dist/src/hooks/hookSystem.d.ts +12 -0
  307. package/dist/src/hooks/hookSystem.js +39 -1
  308. package/dist/src/hooks/hookSystem.js.map +1 -1
  309. package/dist/src/hooks/hookTranslator.js +2 -1
  310. package/dist/src/hooks/hookTranslator.js.map +1 -1
  311. package/dist/src/hooks/index.d.ts +1 -2
  312. package/dist/src/hooks/index.js +1 -3
  313. package/dist/src/hooks/index.js.map +1 -1
  314. package/dist/src/hooks/trustedHooks.d.ts +28 -0
  315. package/dist/src/hooks/trustedHooks.js +90 -0
  316. package/dist/src/hooks/trustedHooks.js.map +1 -0
  317. package/dist/src/hooks/trustedHooks.test.d.ts +6 -0
  318. package/dist/src/hooks/trustedHooks.test.js +154 -0
  319. package/dist/src/hooks/trustedHooks.test.js.map +1 -0
  320. package/dist/src/hooks/types.d.ts +41 -9
  321. package/dist/src/hooks/types.js +31 -41
  322. package/dist/src/hooks/types.js.map +1 -1
  323. package/dist/src/hooks/types.test.js +9 -52
  324. package/dist/src/hooks/types.test.js.map +1 -1
  325. package/dist/src/ide/detect-ide.d.ts +4 -0
  326. package/dist/src/ide/detect-ide.js +7 -2
  327. package/dist/src/ide/detect-ide.js.map +1 -1
  328. package/dist/src/ide/detect-ide.test.js +10 -0
  329. package/dist/src/ide/detect-ide.test.js.map +1 -1
  330. package/dist/src/ide/ide-client.js +4 -1
  331. package/dist/src/ide/ide-client.js.map +1 -1
  332. package/dist/src/ide/ide-installer.js +2 -2
  333. package/dist/src/ide/ide-installer.js.map +1 -1
  334. package/dist/src/ide/ide-installer.test.js +11 -2
  335. package/dist/src/ide/ide-installer.test.js.map +1 -1
  336. package/dist/src/index.d.ts +16 -2
  337. package/dist/src/index.js +18 -3
  338. package/dist/src/index.js.map +1 -1
  339. package/dist/src/mcp/oauth-provider.js +7 -3
  340. package/dist/src/mcp/oauth-provider.js.map +1 -1
  341. package/dist/src/mcp/oauth-provider.test.js +4 -1
  342. package/dist/src/mcp/oauth-provider.test.js.map +1 -1
  343. package/dist/src/mcp/oauth-utils.d.ts +8 -1
  344. package/dist/src/mcp/oauth-utils.js +31 -2
  345. package/dist/src/mcp/oauth-utils.js.map +1 -1
  346. package/dist/src/mcp/oauth-utils.test.js +42 -0
  347. package/dist/src/mcp/oauth-utils.test.js.map +1 -1
  348. package/dist/src/mcp/token-storage/file-token-storage.js +2 -2
  349. package/dist/src/mcp/token-storage/file-token-storage.js.map +1 -1
  350. package/dist/src/mcp/token-storage/keychain-token-storage.js +1 -1
  351. package/dist/src/mcp/token-storage/keychain-token-storage.js.map +1 -1
  352. package/dist/src/policy/config.js +62 -23
  353. package/dist/src/policy/config.js.map +1 -1
  354. package/dist/src/policy/config.test.js +24 -2
  355. package/dist/src/policy/config.test.js.map +1 -1
  356. package/dist/src/policy/persistence.test.js +1 -1
  357. package/dist/src/policy/persistence.test.js.map +1 -1
  358. package/dist/src/policy/policies/agent.toml +1 -1
  359. package/dist/src/policy/policies/write.toml +5 -0
  360. package/dist/src/policy/policies/yolo.toml +1 -0
  361. package/dist/src/policy/policy-engine.d.ts +4 -0
  362. package/dist/src/policy/policy-engine.js +137 -53
  363. package/dist/src/policy/policy-engine.js.map +1 -1
  364. package/dist/src/policy/policy-engine.test.js +289 -1
  365. package/dist/src/policy/policy-engine.test.js.map +1 -1
  366. package/dist/src/policy/policy-updater.test.js +5 -5
  367. package/dist/src/policy/policy-updater.test.js.map +1 -1
  368. package/dist/src/policy/shell-safety.test.js +371 -8
  369. package/dist/src/policy/shell-safety.test.js.map +1 -1
  370. package/dist/src/policy/toml-loader.d.ts +0 -8
  371. package/dist/src/policy/toml-loader.js +13 -45
  372. package/dist/src/policy/toml-loader.js.map +1 -1
  373. package/dist/src/policy/toml-loader.test.js +13 -0
  374. package/dist/src/policy/toml-loader.test.js.map +1 -1
  375. package/dist/src/policy/types.d.ts +10 -0
  376. package/dist/src/policy/utils.d.ts +21 -0
  377. package/dist/src/policy/utils.js +45 -0
  378. package/dist/src/policy/utils.js.map +1 -0
  379. package/dist/src/policy/utils.test.d.ts +6 -0
  380. package/dist/src/policy/utils.test.js +92 -0
  381. package/dist/src/policy/utils.test.js.map +1 -0
  382. package/dist/src/routing/routingStrategy.d.ts +2 -0
  383. package/dist/src/routing/strategies/classifierStrategy.js +1 -1
  384. package/dist/src/routing/strategies/classifierStrategy.js.map +1 -1
  385. package/dist/src/routing/strategies/classifierStrategy.test.js +16 -0
  386. package/dist/src/routing/strategies/classifierStrategy.test.js.map +1 -1
  387. package/dist/src/routing/strategies/compositeStrategy.js +4 -2
  388. package/dist/src/routing/strategies/compositeStrategy.js.map +1 -1
  389. package/dist/src/routing/strategies/compositeStrategy.test.js +11 -10
  390. package/dist/src/routing/strategies/compositeStrategy.test.js.map +1 -1
  391. package/dist/src/routing/strategies/fallbackStrategy.d.ts +1 -1
  392. package/dist/src/routing/strategies/fallbackStrategy.js +2 -5
  393. package/dist/src/routing/strategies/fallbackStrategy.js.map +1 -1
  394. package/dist/src/routing/strategies/fallbackStrategy.test.js +13 -6
  395. package/dist/src/routing/strategies/fallbackStrategy.test.js.map +1 -1
  396. package/dist/src/routing/strategies/overrideStrategy.d.ts +1 -1
  397. package/dist/src/routing/strategies/overrideStrategy.js +6 -6
  398. package/dist/src/routing/strategies/overrideStrategy.js.map +1 -1
  399. package/dist/src/routing/strategies/overrideStrategy.test.js +14 -0
  400. package/dist/src/routing/strategies/overrideStrategy.test.js.map +1 -1
  401. package/dist/src/scheduler/tool-executor.d.ts +22 -0
  402. package/dist/src/scheduler/tool-executor.js +198 -0
  403. package/dist/src/scheduler/tool-executor.js.map +1 -0
  404. package/dist/src/scheduler/tool-executor.test.d.ts +6 -0
  405. package/dist/src/scheduler/tool-executor.test.js +231 -0
  406. package/dist/src/scheduler/tool-executor.test.js.map +1 -0
  407. package/dist/src/scheduler/tool-modifier.d.ts +23 -0
  408. package/dist/src/scheduler/tool-modifier.js +50 -0
  409. package/dist/src/scheduler/tool-modifier.js.map +1 -0
  410. package/dist/src/scheduler/tool-modifier.test.d.ts +6 -0
  411. package/dist/src/scheduler/tool-modifier.test.js +159 -0
  412. package/dist/src/scheduler/tool-modifier.test.js.map +1 -0
  413. package/dist/src/scheduler/types.d.ts +95 -0
  414. package/dist/src/scheduler/types.js +7 -0
  415. package/dist/src/scheduler/types.js.map +1 -0
  416. package/dist/src/services/chatCompressionService.js +3 -10
  417. package/dist/src/services/chatCompressionService.js.map +1 -1
  418. package/dist/src/services/chatCompressionService.test.js +1 -0
  419. package/dist/src/services/chatCompressionService.test.js.map +1 -1
  420. package/dist/src/services/chatRecordingService.d.ts +7 -1
  421. package/dist/src/services/chatRecordingService.js +20 -2
  422. package/dist/src/services/chatRecordingService.js.map +1 -1
  423. package/dist/src/services/chatRecordingService.test.js +43 -0
  424. package/dist/src/services/chatRecordingService.test.js.map +1 -1
  425. package/dist/src/services/contextManager.d.ts +5 -11
  426. package/dist/src/services/contextManager.js +20 -17
  427. package/dist/src/services/contextManager.js.map +1 -1
  428. package/dist/src/services/contextManager.test.js +40 -41
  429. package/dist/src/services/contextManager.test.js.map +1 -1
  430. package/dist/src/services/environmentSanitization.d.ts +15 -0
  431. package/dist/src/services/environmentSanitization.js +142 -0
  432. package/dist/src/services/environmentSanitization.js.map +1 -0
  433. package/dist/src/services/environmentSanitization.test.d.ts +6 -0
  434. package/dist/src/services/environmentSanitization.test.js +284 -0
  435. package/dist/src/services/environmentSanitization.test.js.map +1 -0
  436. package/dist/src/services/gitService.js +10 -1
  437. package/dist/src/services/gitService.js.map +1 -1
  438. package/dist/src/services/gitService.test.js +28 -2
  439. package/dist/src/services/gitService.test.js.map +1 -1
  440. package/dist/src/services/loopDetectionService.js +2 -1
  441. package/dist/src/services/loopDetectionService.js.map +1 -1
  442. package/dist/src/services/loopDetectionService.test.js +14 -8
  443. package/dist/src/services/loopDetectionService.test.js.map +1 -1
  444. package/dist/src/services/modelConfig.integration.test.js +3 -3
  445. package/dist/src/services/modelConfig.integration.test.js.map +1 -1
  446. package/dist/src/services/modelConfigService.d.ts +38 -4
  447. package/dist/src/services/modelConfigService.js +135 -76
  448. package/dist/src/services/modelConfigService.js.map +1 -1
  449. package/dist/src/services/modelConfigService.test.js +116 -0
  450. package/dist/src/services/modelConfigService.test.js.map +1 -1
  451. package/dist/src/services/shellExecutionService.d.ts +2 -0
  452. package/dist/src/services/shellExecutionService.js +6 -50
  453. package/dist/src/services/shellExecutionService.js.map +1 -1
  454. package/dist/src/services/shellExecutionService.test.js +68 -4
  455. package/dist/src/services/shellExecutionService.test.js.map +1 -1
  456. package/dist/src/skills/skillLoader.d.ts +31 -0
  457. package/dist/src/skills/skillLoader.js +77 -0
  458. package/dist/src/skills/skillLoader.js.map +1 -0
  459. package/dist/src/skills/skillLoader.test.d.ts +6 -0
  460. package/dist/src/skills/skillLoader.test.js +75 -0
  461. package/dist/src/skills/skillLoader.test.js.map +1 -0
  462. package/dist/src/skills/skillManager.d.ts +69 -0
  463. package/dist/src/skills/skillManager.js +127 -0
  464. package/dist/src/skills/skillManager.js.map +1 -0
  465. package/dist/src/skills/skillManager.test.d.ts +6 -0
  466. package/dist/src/skills/skillManager.test.js +210 -0
  467. package/dist/src/skills/skillManager.test.js.map +1 -0
  468. package/dist/src/telemetry/clearcut-logger/clearcut-logger.d.ts +18 -9
  469. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js +234 -160
  470. package/dist/src/telemetry/clearcut-logger/clearcut-logger.js.map +1 -1
  471. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js +162 -32
  472. package/dist/src/telemetry/clearcut-logger/clearcut-logger.test.js.map +1 -1
  473. package/dist/src/telemetry/clearcut-logger/event-metadata-key.d.ts +11 -3
  474. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js +24 -5
  475. package/dist/src/telemetry/clearcut-logger/event-metadata-key.js.map +1 -1
  476. package/dist/src/telemetry/loggers.d.ts +3 -3
  477. package/dist/src/telemetry/loggers.js +6 -5
  478. package/dist/src/telemetry/loggers.js.map +1 -1
  479. package/dist/src/telemetry/loggers.test.circular.js +1 -0
  480. package/dist/src/telemetry/loggers.test.circular.js.map +1 -1
  481. package/dist/src/telemetry/loggers.test.js +51 -10
  482. package/dist/src/telemetry/loggers.test.js.map +1 -1
  483. package/dist/src/telemetry/sdk.js +5 -6
  484. package/dist/src/telemetry/sdk.js.map +1 -1
  485. package/dist/src/telemetry/types.d.ts +7 -7
  486. package/dist/src/telemetry/types.js +14 -12
  487. package/dist/src/telemetry/types.js.map +1 -1
  488. package/dist/src/test-utils/mock-message-bus.d.ts +1 -0
  489. package/dist/src/test-utils/mock-message-bus.js +29 -0
  490. package/dist/src/test-utils/mock-message-bus.js.map +1 -1
  491. package/dist/src/test-utils/mock-tool.d.ts +5 -3
  492. package/dist/src/test-utils/mock-tool.js +11 -10
  493. package/dist/src/test-utils/mock-tool.js.map +1 -1
  494. package/dist/src/tools/activate-skill.d.ts +27 -0
  495. package/dist/src/tools/activate-skill.js +133 -0
  496. package/dist/src/tools/activate-skill.js.map +1 -0
  497. package/dist/src/tools/activate-skill.test.d.ts +6 -0
  498. package/dist/src/tools/activate-skill.test.js +113 -0
  499. package/dist/src/tools/activate-skill.test.js.map +1 -0
  500. package/dist/src/tools/confirmation-policy.test.js +3 -12
  501. package/dist/src/tools/confirmation-policy.test.js.map +1 -1
  502. package/dist/src/tools/edit.d.ts +27 -5
  503. package/dist/src/tools/edit.js +455 -136
  504. package/dist/src/tools/edit.js.map +1 -1
  505. package/dist/src/tools/edit.test.js +292 -526
  506. package/dist/src/tools/edit.test.js.map +1 -1
  507. package/dist/src/tools/get-internal-docs.d.ts +27 -0
  508. package/dist/src/tools/get-internal-docs.js +122 -0
  509. package/dist/src/tools/get-internal-docs.js.map +1 -0
  510. package/dist/src/tools/get-internal-docs.test.d.ts +6 -0
  511. package/dist/src/tools/get-internal-docs.test.js +57 -0
  512. package/dist/src/tools/get-internal-docs.test.js.map +1 -0
  513. package/dist/src/tools/glob.d.ts +2 -2
  514. package/dist/src/tools/glob.js +1 -1
  515. package/dist/src/tools/glob.js.map +1 -1
  516. package/dist/src/tools/glob.test.js +2 -1
  517. package/dist/src/tools/glob.test.js.map +1 -1
  518. package/dist/src/tools/grep.d.ts +2 -2
  519. package/dist/src/tools/grep.js +1 -1
  520. package/dist/src/tools/grep.js.map +1 -1
  521. package/dist/src/tools/grep.test.js +5 -4
  522. package/dist/src/tools/grep.test.js.map +1 -1
  523. package/dist/src/tools/ls.d.ts +2 -2
  524. package/dist/src/tools/ls.js +2 -2
  525. package/dist/src/tools/ls.js.map +1 -1
  526. package/dist/src/tools/ls.test.js +2 -1
  527. package/dist/src/tools/ls.test.js.map +1 -1
  528. package/dist/src/tools/mcp-client-manager.js +14 -7
  529. package/dist/src/tools/mcp-client-manager.js.map +1 -1
  530. package/dist/src/tools/mcp-client-manager.test.js +28 -0
  531. package/dist/src/tools/mcp-client-manager.test.js.map +1 -1
  532. package/dist/src/tools/mcp-client.d.ts +5 -4
  533. package/dist/src/tools/mcp-client.js +10 -8
  534. package/dist/src/tools/mcp-client.js.map +1 -1
  535. package/dist/src/tools/mcp-client.test.js +47 -42
  536. package/dist/src/tools/mcp-client.test.js.map +1 -1
  537. package/dist/src/tools/mcp-tool.d.ts +20 -5
  538. package/dist/src/tools/mcp-tool.js +8 -8
  539. package/dist/src/tools/mcp-tool.js.map +1 -1
  540. package/dist/src/tools/mcp-tool.test.js +11 -6
  541. package/dist/src/tools/mcp-tool.test.js.map +1 -1
  542. package/dist/src/tools/memoryTool.d.ts +3 -3
  543. package/dist/src/tools/memoryTool.js +2 -4
  544. package/dist/src/tools/memoryTool.js.map +1 -1
  545. package/dist/src/tools/memoryTool.test.js +5 -2
  546. package/dist/src/tools/memoryTool.test.js.map +1 -1
  547. package/dist/src/tools/message-bus-integration.test.js +10 -37
  548. package/dist/src/tools/message-bus-integration.test.js.map +1 -1
  549. package/dist/src/tools/read-file.d.ts +2 -2
  550. package/dist/src/tools/read-file.js +1 -1
  551. package/dist/src/tools/read-file.js.map +1 -1
  552. package/dist/src/tools/read-file.test.js +3 -2
  553. package/dist/src/tools/read-file.test.js.map +1 -1
  554. package/dist/src/tools/read-many-files.d.ts +2 -2
  555. package/dist/src/tools/read-many-files.js +2 -3
  556. package/dist/src/tools/read-many-files.js.map +1 -1
  557. package/dist/src/tools/read-many-files.test.js +3 -2
  558. package/dist/src/tools/read-many-files.test.js.map +1 -1
  559. package/dist/src/tools/ripGrep.d.ts +3 -2
  560. package/dist/src/tools/ripGrep.js +18 -7
  561. package/dist/src/tools/ripGrep.js.map +1 -1
  562. package/dist/src/tools/ripGrep.test.js +60 -4
  563. package/dist/src/tools/ripGrep.test.js.map +1 -1
  564. package/dist/src/tools/shell.d.ts +3 -6
  565. package/dist/src/tools/shell.js +21 -49
  566. package/dist/src/tools/shell.js.map +1 -1
  567. package/dist/src/tools/shell.test.js +35 -59
  568. package/dist/src/tools/shell.test.js.map +1 -1
  569. package/dist/src/tools/tool-error.d.ts +6 -1
  570. package/dist/src/tools/tool-error.js +6 -0
  571. package/dist/src/tools/tool-error.js.map +1 -1
  572. package/dist/src/tools/tool-names.d.ts +15 -0
  573. package/dist/src/tools/tool-names.js +57 -0
  574. package/dist/src/tools/tool-names.js.map +1 -1
  575. package/dist/src/tools/tool-names.test.d.ts +6 -0
  576. package/dist/src/tools/tool-names.test.js +43 -0
  577. package/dist/src/tools/tool-names.test.js.map +1 -0
  578. package/dist/src/tools/tool-registry.d.ts +11 -7
  579. package/dist/src/tools/tool-registry.js +15 -10
  580. package/dist/src/tools/tool-registry.js.map +1 -1
  581. package/dist/src/tools/tool-registry.test.js +16 -11
  582. package/dist/src/tools/tool-registry.test.js.map +1 -1
  583. package/dist/src/tools/tools.d.ts +8 -6
  584. package/dist/src/tools/tools.js +16 -18
  585. package/dist/src/tools/tools.js.map +1 -1
  586. package/dist/src/tools/tools.test.js +3 -1
  587. package/dist/src/tools/tools.test.js.map +1 -1
  588. package/dist/src/tools/web-fetch.d.ts +2 -2
  589. package/dist/src/tools/web-fetch.js +4 -4
  590. package/dist/src/tools/web-fetch.js.map +1 -1
  591. package/dist/src/tools/web-fetch.test.js +17 -19
  592. package/dist/src/tools/web-fetch.test.js.map +1 -1
  593. package/dist/src/tools/web-search.d.ts +2 -2
  594. package/dist/src/tools/web-search.js +5 -5
  595. package/dist/src/tools/web-search.js.map +1 -1
  596. package/dist/src/tools/web-search.test.js +2 -1
  597. package/dist/src/tools/web-search.test.js.map +1 -1
  598. package/dist/src/tools/write-file.d.ts +2 -2
  599. package/dist/src/tools/write-file.js +9 -6
  600. package/dist/src/tools/write-file.js.map +1 -1
  601. package/dist/src/tools/write-file.test.js +49 -7
  602. package/dist/src/tools/write-file.test.js.map +1 -1
  603. package/dist/src/tools/write-todos.d.ts +2 -2
  604. package/dist/src/tools/write-todos.js +5 -4
  605. package/dist/src/tools/write-todos.js.map +1 -1
  606. package/dist/src/tools/write-todos.test.js +2 -1
  607. package/dist/src/tools/write-todos.test.js.map +1 -1
  608. package/dist/src/utils/apiConversionUtils.d.ts +12 -0
  609. package/dist/src/utils/apiConversionUtils.js +46 -0
  610. package/dist/src/utils/apiConversionUtils.js.map +1 -0
  611. package/dist/src/utils/apiConversionUtils.test.d.ts +6 -0
  612. package/dist/src/utils/apiConversionUtils.test.js +150 -0
  613. package/dist/src/utils/apiConversionUtils.test.js.map +1 -0
  614. package/dist/src/utils/checkpointUtils.d.ts +1 -1
  615. package/dist/src/utils/checkpointUtils.js +1 -1
  616. package/dist/src/utils/checkpointUtils.js.map +1 -1
  617. package/dist/src/utils/checkpointUtils.test.js +1 -1
  618. package/dist/src/utils/checkpointUtils.test.js.map +1 -1
  619. package/dist/src/utils/debugLogger.js +1 -0
  620. package/dist/src/utils/debugLogger.js.map +1 -1
  621. package/dist/src/utils/editCorrector.d.ts +3 -3
  622. package/dist/src/utils/editCorrector.js +27 -10
  623. package/dist/src/utils/editCorrector.js.map +1 -1
  624. package/dist/src/utils/editCorrector.test.js +23 -23
  625. package/dist/src/utils/editCorrector.test.js.map +1 -1
  626. package/dist/src/utils/editor.d.ts +3 -2
  627. package/dist/src/utils/editor.js +26 -6
  628. package/dist/src/utils/editor.js.map +1 -1
  629. package/dist/src/utils/editor.test.js +27 -4
  630. package/dist/src/utils/editor.test.js.map +1 -1
  631. package/dist/src/utils/environmentContext.d.ts +1 -0
  632. package/dist/src/utils/environmentContext.js +4 -0
  633. package/dist/src/utils/environmentContext.js.map +1 -1
  634. package/dist/src/utils/environmentContext.test.js +2 -0
  635. package/dist/src/utils/environmentContext.test.js.map +1 -1
  636. package/dist/src/utils/errorReporting.d.ts +1 -1
  637. package/dist/src/utils/errorReporting.js +13 -12
  638. package/dist/src/utils/errorReporting.js.map +1 -1
  639. package/dist/src/utils/errorReporting.test.js +17 -14
  640. package/dist/src/utils/errorReporting.test.js.map +1 -1
  641. package/dist/src/utils/events.d.ts +71 -19
  642. package/dist/src/utils/events.js +35 -9
  643. package/dist/src/utils/events.js.map +1 -1
  644. package/dist/src/utils/events.test.js +25 -0
  645. package/dist/src/utils/events.test.js.map +1 -1
  646. package/dist/src/utils/fileDiffUtils.d.ts +18 -0
  647. package/dist/src/utils/fileDiffUtils.js +37 -0
  648. package/dist/src/utils/fileDiffUtils.js.map +1 -0
  649. package/dist/src/utils/fileDiffUtils.test.d.ts +6 -0
  650. package/dist/src/utils/fileDiffUtils.test.js +84 -0
  651. package/dist/src/utils/fileDiffUtils.test.js.map +1 -0
  652. package/dist/src/utils/fileUtils.d.ts +4 -0
  653. package/dist/src/utils/fileUtils.js +53 -0
  654. package/dist/src/utils/fileUtils.js.map +1 -1
  655. package/dist/src/utils/fileUtils.test.js +112 -1
  656. package/dist/src/utils/fileUtils.test.js.map +1 -1
  657. package/dist/src/utils/geminiIgnoreParser.d.ts +11 -0
  658. package/dist/src/utils/geminiIgnoreParser.js +20 -0
  659. package/dist/src/utils/geminiIgnoreParser.js.map +1 -1
  660. package/dist/src/utils/geminiIgnoreParser.test.js +48 -0
  661. package/dist/src/utils/geminiIgnoreParser.test.js.map +1 -1
  662. package/dist/src/utils/generateContentResponseUtilities.d.ts +3 -1
  663. package/dist/src/utils/generateContentResponseUtilities.js +106 -0
  664. package/dist/src/utils/generateContentResponseUtilities.js.map +1 -1
  665. package/dist/src/utils/generateContentResponseUtilities.test.js +279 -2
  666. package/dist/src/utils/generateContentResponseUtilities.test.js.map +1 -1
  667. package/dist/src/utils/getFolderStructure.js +7 -2
  668. package/dist/src/utils/getFolderStructure.js.map +1 -1
  669. package/dist/src/utils/gitIgnoreParser.js +9 -10
  670. package/dist/src/utils/gitIgnoreParser.js.map +1 -1
  671. package/dist/src/utils/installationManager.test.js +11 -3
  672. package/dist/src/utils/installationManager.test.js.map +1 -1
  673. package/dist/src/utils/memoryDiscovery.js +3 -4
  674. package/dist/src/utils/memoryDiscovery.js.map +1 -1
  675. package/dist/src/utils/memoryDiscovery.test.js +12 -1
  676. package/dist/src/utils/memoryDiscovery.test.js.map +1 -1
  677. package/dist/src/utils/partUtils.js +1 -1
  678. package/dist/src/utils/partUtils.js.map +1 -1
  679. package/dist/src/utils/paths.d.ts +10 -0
  680. package/dist/src/utils/paths.js +20 -1
  681. package/dist/src/utils/paths.js.map +1 -1
  682. package/dist/src/utils/retry.d.ts +1 -0
  683. package/dist/src/utils/retry.js +17 -5
  684. package/dist/src/utils/retry.js.map +1 -1
  685. package/dist/src/utils/retry.test.js +11 -11
  686. package/dist/src/utils/retry.test.js.map +1 -1
  687. package/dist/src/utils/shell-utils.d.ts +6 -0
  688. package/dist/src/utils/shell-utils.js +97 -12
  689. package/dist/src/utils/shell-utils.js.map +1 -1
  690. package/dist/src/utils/shell-utils.test.js +99 -1
  691. package/dist/src/utils/shell-utils.test.js.map +1 -1
  692. package/dist/src/utils/summarizer.test.js +3 -2
  693. package/dist/src/utils/summarizer.test.js.map +1 -1
  694. package/dist/src/utils/terminal.d.ts +4 -0
  695. package/dist/src/utils/terminal.js +12 -0
  696. package/dist/src/utils/terminal.js.map +1 -1
  697. package/dist/src/utils/tokenCalculation.js +20 -5
  698. package/dist/src/utils/tokenCalculation.js.map +1 -1
  699. package/dist/src/utils/tokenCalculation.test.js +11 -2
  700. package/dist/src/utils/tokenCalculation.test.js.map +1 -1
  701. package/dist/src/utils/tool-utils.d.ts +9 -0
  702. package/dist/src/utils/tool-utils.js +29 -0
  703. package/dist/src/utils/tool-utils.js.map +1 -1
  704. package/dist/src/utils/tool-utils.test.js +17 -2
  705. package/dist/src/utils/tool-utils.test.js.map +1 -1
  706. package/dist/src/utils/userAccountManager.test.js +5 -5
  707. package/dist/src/utils/userAccountManager.test.js.map +1 -1
  708. package/dist/src/utils/workspaceContext.test.js +1 -1
  709. package/dist/src/utils/workspaceContext.test.js.map +1 -1
  710. package/dist/tsconfig.tsbuildinfo +1 -1
  711. package/package.json +8 -6
  712. package/dist/src/agents/executor.js.map +0 -1
  713. package/dist/src/agents/executor.test.js.map +0 -1
  714. package/dist/src/agents/invocation.js.map +0 -1
  715. package/dist/src/agents/invocation.test.js.map +0 -1
  716. package/dist/src/core/sessionHookTriggers.d.ts +0 -28
  717. package/dist/src/core/sessionHookTriggers.js +0 -68
  718. package/dist/src/core/sessionHookTriggers.js.map +0 -1
  719. package/dist/src/tools/smart-edit.d.ts +0 -78
  720. package/dist/src/tools/smart-edit.js +0 -722
  721. package/dist/src/tools/smart-edit.js.map +0 -1
  722. package/dist/src/tools/smart-edit.test.js +0 -592
  723. package/dist/src/tools/smart-edit.test.js.map +0 -1
  724. package/dist/src/utils/shell-permissions.d.ts +0 -52
  725. package/dist/src/utils/shell-permissions.js +0 -188
  726. package/dist/src/utils/shell-permissions.js.map +0 -1
  727. package/dist/src/utils/shell-permissions.test.js +0 -347
  728. package/dist/src/utils/shell-permissions.test.js.map +0 -1
  729. /package/dist/src/agents/{executor.test.d.ts → a2a-client-manager.test.d.ts} +0 -0
  730. /package/dist/src/agents/{invocation.test.d.ts → a2aUtils.test.d.ts} +0 -0
  731. /package/dist/src/{tools/smart-edit.test.d.ts → agents/agentLoader.test.d.ts} +0 -0
  732. /package/dist/src/{utils/shell-permissions.test.d.ts → agents/cli-help-agent.test.d.ts} +0 -0
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  /* eslint-disable @typescript-eslint/no-explicit-any */
7
- const mockEnsureCorrectEdit = vi.hoisted(() => vi.fn());
7
+ const mockFixLLMEditWithInstruction = vi.hoisted(() => vi.fn());
8
8
  const mockGenerateJson = vi.hoisted(() => vi.fn());
9
9
  const mockOpenDiff = vi.hoisted(() => vi.fn());
10
10
  import { IdeClient } from '../ide/ide-client.js';
@@ -13,36 +13,38 @@ vi.mock('../ide/ide-client.js', () => ({
13
13
  getInstance: vi.fn(),
14
14
  },
15
15
  }));
16
- vi.mock('../utils/editCorrector.js', () => ({
17
- ensureCorrectEdit: mockEnsureCorrectEdit,
16
+ vi.mock('../utils/llm-edit-fixer.js', () => ({
17
+ FixLLMEditWithInstruction: mockFixLLMEditWithInstruction,
18
18
  }));
19
19
  vi.mock('../core/client.js', () => ({
20
20
  GeminiClient: vi.fn().mockImplementation(() => ({
21
21
  generateJson: mockGenerateJson,
22
+ getHistory: vi.fn().mockResolvedValue([]),
22
23
  })),
23
24
  }));
24
25
  vi.mock('../utils/editor.js', () => ({
25
26
  openDiff: mockOpenDiff,
26
27
  }));
27
- vi.mock('../telemetry/loggers.js', () => ({
28
- logFileOperation: vi.fn(),
29
- }));
30
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
31
- import { applyReplacement, EditTool } from './edit.js';
28
+ import { describe, it, expect, beforeEach, afterEach, vi, } from 'vitest';
29
+ import { EditTool, applyReplacement, calculateReplacement, } from './edit.js';
32
30
  import { ToolConfirmationOutcome } from './tools.js';
33
31
  import { ToolErrorType } from './tool-error.js';
32
+ import { createMockMessageBus, getMockMessageBusInstance, } from '../test-utils/mock-message-bus.js';
34
33
  import path from 'node:path';
35
34
  import fs from 'node:fs';
36
35
  import os from 'node:os';
37
36
  import { ApprovalMode } from '../policy/types.js';
37
+ import {} from '../config/config.js';
38
+ import {} from '@google/genai';
39
+ import { createMockWorkspaceContext } from '../test-utils/mockWorkspaceContext.js';
38
40
  import { StandardFileSystemService } from '../services/fileSystemService.js';
39
- import { WorkspaceContext } from '../utils/workspaceContext.js';
40
41
  describe('EditTool', () => {
41
42
  let tool;
42
43
  let tempDir;
43
44
  let rootDir;
44
45
  let mockConfig;
45
46
  let geminiClient;
47
+ let fileSystemService;
46
48
  let baseLlmClient;
47
49
  beforeEach(() => {
48
50
  vi.restoreAllMocks();
@@ -50,23 +52,26 @@ describe('EditTool', () => {
50
52
  rootDir = path.join(tempDir, 'root');
51
53
  fs.mkdirSync(rootDir);
52
54
  geminiClient = {
53
- generateJson: mockGenerateJson, // mockGenerateJson is already defined and hoisted
55
+ generateJson: mockGenerateJson,
56
+ getHistory: vi.fn().mockResolvedValue([]),
54
57
  };
55
58
  baseLlmClient = {
56
- generateJson: vi.fn(),
59
+ generateJson: mockGenerateJson,
57
60
  };
61
+ fileSystemService = new StandardFileSystemService();
58
62
  mockConfig = {
63
+ getUsageStatisticsEnabled: vi.fn(() => true),
64
+ getSessionId: vi.fn(() => 'mock-session-id'),
65
+ getContentGeneratorConfig: vi.fn(() => ({ authType: 'mock' })),
66
+ getProxy: vi.fn(() => undefined),
59
67
  getGeminiClient: vi.fn().mockReturnValue(geminiClient),
60
68
  getBaseLlmClient: vi.fn().mockReturnValue(baseLlmClient),
61
69
  getTargetDir: () => rootDir,
62
70
  getApprovalMode: vi.fn(),
63
71
  setApprovalMode: vi.fn(),
64
- getWorkspaceContext: () => new WorkspaceContext(rootDir),
65
- getFileSystemService: () => new StandardFileSystemService(),
72
+ getWorkspaceContext: () => createMockWorkspaceContext(rootDir),
73
+ getFileSystemService: () => fileSystemService,
66
74
  getIdeMode: () => false,
67
- // getGeminiConfig: () => ({ apiKey: 'test-api-key' }), // This was not a real Config method
68
- // Add other properties/methods of Config if EditTool uses them
69
- // Minimal other methods to satisfy Config type if needed by EditTool constructor or other direct uses:
70
75
  getApiKey: () => 'test-api-key',
71
76
  getModel: () => 'test-model',
72
77
  getSandbox: () => false,
@@ -81,34 +86,22 @@ describe('EditTool', () => {
81
86
  setUserMemory: vi.fn(),
82
87
  getGeminiMdFileCount: () => 0,
83
88
  setGeminiMdFileCount: vi.fn(),
84
- getToolRegistry: () => ({}), // Minimal mock for ToolRegistry
89
+ getToolRegistry: () => ({}),
85
90
  isInteractive: () => false,
91
+ getDisableLLMCorrection: vi.fn(() => false),
92
+ getExperiments: () => { },
86
93
  };
87
- // Reset mocks before each test
88
94
  mockConfig.getApprovalMode.mockClear();
89
- // Default to not skipping confirmation
90
95
  mockConfig.getApprovalMode.mockReturnValue(ApprovalMode.DEFAULT);
91
- // Reset mocks and set default implementation for ensureCorrectEdit
92
- mockEnsureCorrectEdit.mockReset();
93
- mockEnsureCorrectEdit.mockImplementation(async (_, currentContent, params) => {
94
- let occurrences = 0;
95
- if (params.old_string && currentContent) {
96
- // Simple string counting for the mock
97
- let index = currentContent.indexOf(params.old_string);
98
- while (index !== -1) {
99
- occurrences++;
100
- index = currentContent.indexOf(params.old_string, index + 1);
101
- }
102
- }
103
- else if (params.old_string === '') {
104
- occurrences = 0; // Creating a new file
105
- }
106
- return Promise.resolve({ params, occurrences });
96
+ mockFixLLMEditWithInstruction.mockReset();
97
+ mockFixLLMEditWithInstruction.mockResolvedValue({
98
+ noChangesRequired: false,
99
+ search: '',
100
+ replace: '',
101
+ explanation: 'LLM fix failed',
107
102
  });
108
- // Default mock for generateJson to return the snippet unchanged
109
103
  mockGenerateJson.mockReset();
110
104
  mockGenerateJson.mockImplementation(async (contents, schema) => {
111
- // The problematic_snippet is the last part of the user's content
112
105
  const userContent = contents.find((c) => c.role === 'user');
113
106
  let promptText = '';
114
107
  if (userContent && userContent.parts) {
@@ -125,17 +118,17 @@ describe('EditTool', () => {
125
118
  });
126
119
  }
127
120
  if (schema.properties?.corrected_new_string) {
128
- // For new_string correction, we might need more sophisticated logic,
129
- // but for now, returning original is a safe default if not specified by a test.
130
121
  const originalNewStringMatch = promptText.match(/original_new_string \(what was intended to replace original_old_string\):\n```\n([\s\S]*?)\n```/);
131
122
  const originalNewString = originalNewStringMatch && originalNewStringMatch[1]
132
123
  ? originalNewStringMatch[1]
133
124
  : '';
134
125
  return Promise.resolve({ corrected_new_string: originalNewString });
135
126
  }
136
- return Promise.resolve({}); // Default empty object if schema doesn't match
127
+ return Promise.resolve({});
137
128
  });
138
- tool = new EditTool(mockConfig);
129
+ const bus = createMockMessageBus();
130
+ getMockMessageBusInstance(bus).defaultToolDecision = 'ask_user';
131
+ tool = new EditTool(mockConfig, bus);
139
132
  });
140
133
  afterEach(() => {
141
134
  fs.rmSync(tempDir, { recursive: true, force: true });
@@ -233,180 +226,85 @@ describe('EditTool', () => {
233
226
  expect(result).toBe(expected);
234
227
  });
235
228
  });
229
+ describe('calculateReplacement', () => {
230
+ const abortSignal = new AbortController().signal;
231
+ it.each([
232
+ {
233
+ name: 'perform an exact replacement',
234
+ content: 'hello world',
235
+ old_string: 'world',
236
+ new_string: 'moon',
237
+ expected: 'hello moon',
238
+ occurrences: 1,
239
+ },
240
+ {
241
+ name: 'perform a flexible, whitespace-insensitive replacement',
242
+ content: ' hello\n world\n',
243
+ old_string: 'hello\nworld',
244
+ new_string: 'goodbye\nmoon',
245
+ expected: ' goodbye\n moon\n',
246
+ occurrences: 1,
247
+ },
248
+ {
249
+ name: 'return 0 occurrences if no match is found',
250
+ content: 'hello world',
251
+ old_string: 'nomatch',
252
+ new_string: 'moon',
253
+ expected: 'hello world',
254
+ occurrences: 0,
255
+ },
256
+ ])('should $name', async ({ content, old_string, new_string, expected, occurrences }) => {
257
+ const result = await calculateReplacement(mockConfig, {
258
+ params: {
259
+ file_path: 'test.txt',
260
+ instruction: 'test',
261
+ old_string,
262
+ new_string,
263
+ },
264
+ currentContent: content,
265
+ abortSignal,
266
+ });
267
+ expect(result.newContent).toBe(expected);
268
+ expect(result.occurrences).toBe(occurrences);
269
+ });
270
+ it('should perform a regex-based replacement for flexible intra-line whitespace', async () => {
271
+ // This case would fail with the previous exact and line-trimming flexible logic
272
+ // because the whitespace *within* the line is different.
273
+ const content = ' function myFunc( a, b ) {\n return a + b;\n }';
274
+ const result = await calculateReplacement(mockConfig, {
275
+ params: {
276
+ file_path: 'test.js',
277
+ instruction: 'test',
278
+ old_string: 'function myFunc(a, b) {', // Note the normalized whitespace
279
+ new_string: 'const yourFunc = (a, b) => {',
280
+ },
281
+ currentContent: content,
282
+ abortSignal,
283
+ });
284
+ // The indentation from the original line should be preserved and applied to the new string.
285
+ const expectedContent = ' const yourFunc = (a, b) => {\n return a + b;\n }';
286
+ expect(result.newContent).toBe(expectedContent);
287
+ expect(result.occurrences).toBe(1);
288
+ });
289
+ });
236
290
  describe('validateToolParams', () => {
237
291
  it('should return null for valid params', () => {
238
292
  const params = {
239
293
  file_path: path.join(rootDir, 'test.txt'),
294
+ instruction: 'An instruction',
240
295
  old_string: 'old',
241
296
  new_string: 'new',
242
297
  };
243
298
  expect(tool.validateToolParams(params)).toBeNull();
244
299
  });
245
- it('should return error for path outside root', () => {
300
+ it('should return an error if path is outside the workspace', () => {
246
301
  const params = {
247
- file_path: path.join(tempDir, 'outside-root.txt'),
302
+ file_path: path.join(os.tmpdir(), 'outside.txt'),
303
+ instruction: 'An instruction',
248
304
  old_string: 'old',
249
305
  new_string: 'new',
250
306
  };
251
- const error = tool.validateToolParams(params);
252
- expect(error).toContain('File path must be within one of the workspace directories');
253
- });
254
- });
255
- describe('shouldConfirmExecute', () => {
256
- const testFile = 'edit_me.txt';
257
- let filePath;
258
- beforeEach(() => {
259
- filePath = path.join(rootDir, testFile);
260
- });
261
- it('should resolve relative path and request confirmation', async () => {
262
- fs.writeFileSync(filePath, 'some old content here');
263
- const params = {
264
- file_path: testFile, // relative path
265
- old_string: 'old',
266
- new_string: 'new',
267
- };
268
- // ensureCorrectEdit will be called by shouldConfirmExecute
269
- mockEnsureCorrectEdit.mockResolvedValueOnce({
270
- params: { ...params, file_path: filePath },
271
- occurrences: 1,
272
- });
273
- const invocation = tool.build(params);
274
- const confirmation = await invocation.shouldConfirmExecute(new AbortController().signal);
275
- expect(confirmation).toEqual(expect.objectContaining({
276
- title: `Confirm Edit: ${testFile}`,
277
- fileName: testFile,
278
- fileDiff: expect.any(String),
279
- }));
280
- });
281
- it('should request confirmation for valid edit', async () => {
282
- fs.writeFileSync(filePath, 'some old content here');
283
- const params = {
284
- file_path: filePath,
285
- old_string: 'old',
286
- new_string: 'new',
287
- };
288
- // ensureCorrectEdit will be called by shouldConfirmExecute
289
- mockEnsureCorrectEdit.mockResolvedValueOnce({ params, occurrences: 1 });
290
- const invocation = tool.build(params);
291
- const confirmation = await invocation.shouldConfirmExecute(new AbortController().signal);
292
- expect(confirmation).toEqual(expect.objectContaining({
293
- title: `Confirm Edit: ${testFile}`,
294
- fileName: testFile,
295
- fileDiff: expect.any(String),
296
- }));
297
- });
298
- it('should return false if old_string is not found (ensureCorrectEdit returns 0)', async () => {
299
- fs.writeFileSync(filePath, 'some content here');
300
- const params = {
301
- file_path: filePath,
302
- old_string: 'not_found',
303
- new_string: 'new',
304
- };
305
- mockEnsureCorrectEdit.mockResolvedValueOnce({ params, occurrences: 0 });
306
- const invocation = tool.build(params);
307
- const confirmation = await invocation.shouldConfirmExecute(new AbortController().signal);
308
- expect(confirmation).toBe(false);
309
- });
310
- it('should return false if multiple occurrences of old_string are found (ensureCorrectEdit returns > 1)', async () => {
311
- fs.writeFileSync(filePath, 'old old content here');
312
- const params = {
313
- file_path: filePath,
314
- old_string: 'old',
315
- new_string: 'new',
316
- };
317
- mockEnsureCorrectEdit.mockResolvedValueOnce({ params, occurrences: 2 });
318
- const invocation = tool.build(params);
319
- const confirmation = await invocation.shouldConfirmExecute(new AbortController().signal);
320
- expect(confirmation).toBe(false);
321
- });
322
- it('should request confirmation for creating a new file (empty old_string)', async () => {
323
- const newFileName = 'new_file.txt';
324
- const newFilePath = path.join(rootDir, newFileName);
325
- const params = {
326
- file_path: newFilePath,
327
- old_string: '',
328
- new_string: 'new file content',
329
- };
330
- // ensureCorrectEdit might not be called if old_string is empty,
331
- // as shouldConfirmExecute handles this for diff generation.
332
- // If it is called, it should return 0 occurrences for a new file.
333
- mockEnsureCorrectEdit.mockResolvedValueOnce({ params, occurrences: 0 });
334
- const invocation = tool.build(params);
335
- const confirmation = await invocation.shouldConfirmExecute(new AbortController().signal);
336
- expect(confirmation).toEqual(expect.objectContaining({
337
- title: `Confirm Edit: ${newFileName}`,
338
- fileName: newFileName,
339
- fileDiff: expect.any(String),
340
- }));
341
- });
342
- it('should use corrected params from ensureCorrectEdit for diff generation', async () => {
343
- const originalContent = 'This is the original string to be replaced.';
344
- const originalOldString = 'original string';
345
- const originalNewString = 'new string';
346
- const correctedOldString = 'original string to be replaced'; // More specific
347
- const correctedNewString = 'completely new string'; // Different replacement
348
- const expectedFinalContent = 'This is the completely new string.';
349
- fs.writeFileSync(filePath, originalContent);
350
- const params = {
351
- file_path: filePath,
352
- old_string: originalOldString,
353
- new_string: originalNewString,
354
- };
355
- // The main beforeEach already calls mockEnsureCorrectEdit.mockReset()
356
- // Set a specific mock for this test case
357
- let mockCalled = false;
358
- mockEnsureCorrectEdit.mockImplementationOnce(async (_, content, p, client, baseClient) => {
359
- mockCalled = true;
360
- expect(content).toBe(originalContent);
361
- expect(p).toBe(params);
362
- expect(client).toBe(geminiClient);
363
- expect(baseClient).toBe(baseLlmClient);
364
- return {
365
- params: {
366
- file_path: filePath,
367
- old_string: correctedOldString,
368
- new_string: correctedNewString,
369
- },
370
- occurrences: 1,
371
- };
372
- });
373
- const invocation = tool.build(params);
374
- const confirmation = (await invocation.shouldConfirmExecute(new AbortController().signal));
375
- expect(mockCalled).toBe(true); // Check if the mock implementation was run
376
- // expect(mockEnsureCorrectEdit).toHaveBeenCalledWith(originalContent, params, expect.anything()); // Keep this commented for now
377
- expect(confirmation).toEqual(expect.objectContaining({
378
- title: `Confirm Edit: ${testFile}`,
379
- fileName: testFile,
380
- }));
381
- // Check that the diff is based on the corrected strings leading to the new state
382
- expect(confirmation.fileDiff).toContain(`-${originalContent}`);
383
- expect(confirmation.fileDiff).toContain(`+${expectedFinalContent}`);
384
- // Verify that applying the correctedOldString and correctedNewString to originalContent
385
- // indeed produces the expectedFinalContent, which is what the diff should reflect.
386
- const patchedContent = originalContent.replace(correctedOldString, // This was the string identified by ensureCorrectEdit for replacement
387
- correctedNewString);
388
- expect(patchedContent).toBe(expectedFinalContent);
389
- });
390
- it('should rethrow calculateEdit errors when the abort signal is triggered', async () => {
391
- const filePath = path.join(rootDir, 'abort-confirmation.txt');
392
- const params = {
393
- file_path: filePath,
394
- old_string: 'old',
395
- new_string: 'new',
396
- };
397
- const invocation = tool.build(params);
398
- const abortController = new AbortController();
399
- const abortError = new Error('Abort requested');
400
- const calculateSpy = vi
401
- .spyOn(invocation, 'calculateEdit')
402
- .mockImplementation(async () => {
403
- if (!abortController.signal.aborted) {
404
- abortController.abort();
405
- }
406
- throw abortError;
407
- });
408
- await expect(invocation.shouldConfirmExecute(abortController.signal)).rejects.toBe(abortError);
409
- calculateSpy.mockRestore();
307
+ expect(tool.validateToolParams(params)).toMatch(/must be within one of the workspace directories/);
410
308
  });
411
309
  });
412
310
  describe('execute', () => {
@@ -414,53 +312,17 @@ describe('EditTool', () => {
414
312
  let filePath;
415
313
  beforeEach(() => {
416
314
  filePath = path.join(rootDir, testFile);
417
- // Default for execute tests, can be overridden
418
- mockEnsureCorrectEdit.mockImplementation(async (_, content, params) => {
419
- let occurrences = 0;
420
- if (params.old_string && content) {
421
- let index = content.indexOf(params.old_string);
422
- while (index !== -1) {
423
- occurrences++;
424
- index = content.indexOf(params.old_string, index + 1);
425
- }
426
- }
427
- else if (params.old_string === '') {
428
- occurrences = 0;
429
- }
430
- return { params, occurrences };
431
- });
432
- });
433
- it('should resolve relative path and execute successfully', async () => {
434
- const initialContent = 'This is some old text.';
435
- const newContent = 'This is some new text.';
436
- fs.writeFileSync(filePath, initialContent, 'utf8');
437
- const params = {
438
- file_path: testFile, // relative path
439
- old_string: 'old',
440
- new_string: 'new',
441
- };
442
- const invocation = tool.build(params);
443
- const result = await invocation.execute(new AbortController().signal);
444
- expect(result.llmContent).toMatch(/Successfully modified file/);
445
- expect(fs.readFileSync(filePath, 'utf8')).toBe(newContent);
446
- });
447
- it('should throw error if file path is empty', async () => {
448
- const params = {
449
- file_path: '',
450
- old_string: 'old',
451
- new_string: 'new',
452
- };
453
- expect(() => tool.build(params)).toThrow(/The 'file_path' parameter must be non-empty./);
454
315
  });
455
316
  it('should reject when calculateEdit fails after an abort signal', async () => {
456
317
  const params = {
457
318
  file_path: path.join(rootDir, 'abort-execute.txt'),
319
+ instruction: 'Abort during execute',
458
320
  old_string: 'old',
459
321
  new_string: 'new',
460
322
  };
461
323
  const invocation = tool.build(params);
462
324
  const abortController = new AbortController();
463
- const abortError = new Error('Abort requested during execute');
325
+ const abortError = new Error('Abort requested during edit execution');
464
326
  const calculateSpy = vi
465
327
  .spyOn(invocation, 'calculateEdit')
466
328
  .mockImplementation(async () => {
@@ -474,16 +336,14 @@ describe('EditTool', () => {
474
336
  });
475
337
  it('should edit an existing file and return diff with fileName', async () => {
476
338
  const initialContent = 'This is some old text.';
477
- const newContent = 'This is some new text.'; // old -> new
339
+ const newContent = 'This is some new text.';
478
340
  fs.writeFileSync(filePath, initialContent, 'utf8');
479
341
  const params = {
480
342
  file_path: filePath,
343
+ instruction: 'Replace old with new',
481
344
  old_string: 'old',
482
345
  new_string: 'new',
483
346
  };
484
- // Specific mock for this test's execution path in calculateEdit
485
- // ensureCorrectEdit is NOT called by calculateEdit, only by shouldConfirmExecute
486
- // So, the default mockEnsureCorrectEdit should correctly return 1 occurrence for 'old' in initialContent
487
347
  const invocation = tool.build(params);
488
348
  const result = await invocation.execute(new AbortController().signal);
489
349
  expect(result.llmContent).toMatch(/Successfully modified file/);
@@ -493,196 +353,124 @@ describe('EditTool', () => {
493
353
  expect(display.fileDiff).toMatch(newContent);
494
354
  expect(display.fileName).toBe(testFile);
495
355
  });
496
- it('should create a new file if old_string is empty and file does not exist, and return created message', async () => {
497
- const newFileName = 'brand_new_file.txt';
498
- const newFilePath = path.join(rootDir, newFileName);
499
- const fileContent = 'Content for the new file.';
500
- const params = {
501
- file_path: newFilePath,
502
- old_string: '',
503
- new_string: fileContent,
504
- };
505
- mockConfig.getApprovalMode.mockReturnValueOnce(ApprovalMode.AUTO_EDIT);
506
- const invocation = tool.build(params);
507
- const result = await invocation.execute(new AbortController().signal);
508
- expect(result.llmContent).toMatch(/Created new file/);
509
- expect(fs.existsSync(newFilePath)).toBe(true);
510
- expect(fs.readFileSync(newFilePath, 'utf8')).toBe(fileContent);
511
- const display = result.returnDisplay;
512
- expect(display.fileDiff).toMatch(/\+Content for the new file\./);
513
- expect(display.fileName).toBe(newFileName);
514
- expect(result.returnDisplay.diffStat).toStrictEqual({
515
- model_added_lines: 1,
516
- model_removed_lines: 0,
517
- model_added_chars: 25,
518
- model_removed_chars: 0,
519
- user_added_lines: 0,
520
- user_removed_lines: 0,
521
- user_added_chars: 0,
522
- user_removed_chars: 0,
523
- });
524
- });
525
356
  it('should return error if old_string is not found in file', async () => {
526
357
  fs.writeFileSync(filePath, 'Some content.', 'utf8');
527
358
  const params = {
528
359
  file_path: filePath,
360
+ instruction: 'Replace non-existent text',
529
361
  old_string: 'nonexistent',
530
362
  new_string: 'replacement',
531
363
  };
532
- // The default mockEnsureCorrectEdit will return 0 occurrences for 'nonexistent'
533
364
  const invocation = tool.build(params);
534
365
  const result = await invocation.execute(new AbortController().signal);
535
- expect(result.llmContent).toMatch(/0 occurrences found for old_string in/);
366
+ expect(result.llmContent).toMatch(/0 occurrences found for old_string/);
536
367
  expect(result.returnDisplay).toMatch(/Failed to edit, could not find the string to replace./);
368
+ expect(mockFixLLMEditWithInstruction).toHaveBeenCalled();
537
369
  });
538
- it('should return error if multiple occurrences of old_string are found', async () => {
539
- fs.writeFileSync(filePath, 'multiple old old strings', 'utf8');
540
- const params = {
541
- file_path: filePath,
542
- old_string: 'old',
543
- new_string: 'new',
544
- };
545
- // The default mockEnsureCorrectEdit will return 2 occurrences for 'old'
546
- const invocation = tool.build(params);
547
- const result = await invocation.execute(new AbortController().signal);
548
- expect(result.llmContent).toMatch(/Expected 1 occurrence but found 2 for old_string in file/);
549
- expect(result.returnDisplay).toMatch(/Failed to edit, expected 1 occurrence but found 2/);
550
- });
551
- it('should successfully replace multiple occurrences when expected_replacements specified', async () => {
552
- fs.writeFileSync(filePath, 'old text\nold text\nold text', 'utf8');
370
+ it('should succeed if FixLLMEditWithInstruction corrects the params', async () => {
371
+ const initialContent = 'This is some original text.';
372
+ const finalContent = 'This is some brand new text.';
373
+ fs.writeFileSync(filePath, initialContent, 'utf8');
553
374
  const params = {
554
375
  file_path: filePath,
555
- old_string: 'old',
556
- new_string: 'new',
557
- expected_replacements: 3,
376
+ instruction: 'Replace original with brand new',
377
+ old_string: 'wrong text', // This will fail first
378
+ new_string: 'brand new text',
558
379
  };
380
+ mockFixLLMEditWithInstruction.mockResolvedValueOnce({
381
+ noChangesRequired: false,
382
+ search: 'original text', // The corrected search string
383
+ replace: 'brand new text',
384
+ explanation: 'Corrected the search string to match the file content.',
385
+ });
559
386
  const invocation = tool.build(params);
560
387
  const result = await invocation.execute(new AbortController().signal);
388
+ expect(result.error).toBeUndefined();
561
389
  expect(result.llmContent).toMatch(/Successfully modified file/);
562
- expect(fs.readFileSync(filePath, 'utf8')).toBe('new text\nnew text\nnew text');
563
- const display = result.returnDisplay;
564
- expect(display.fileDiff).toMatch(/-old text\n-old text\n-old text/);
565
- expect(display.fileDiff).toMatch(/\+new text\n\+new text\n\+new text/);
566
- expect(display.fileName).toBe(testFile);
567
- expect(result.returnDisplay.diffStat).toStrictEqual({
568
- model_added_lines: 3,
569
- model_removed_lines: 3,
570
- model_added_chars: 24,
571
- model_removed_chars: 24,
572
- user_added_lines: 0,
573
- user_removed_lines: 0,
574
- user_added_chars: 0,
575
- user_removed_chars: 0,
576
- });
390
+ expect(fs.readFileSync(filePath, 'utf8')).toBe(finalContent);
391
+ expect(mockFixLLMEditWithInstruction).toHaveBeenCalledTimes(1);
577
392
  });
578
- it('should return error if expected_replacements does not match actual occurrences', async () => {
579
- fs.writeFileSync(filePath, 'old text old text', 'utf8');
393
+ it('should preserve CRLF line endings when editing a file', async () => {
394
+ const initialContent = 'line one\r\nline two\r\n';
395
+ const newContent = 'line one\r\nline three\r\n';
396
+ fs.writeFileSync(filePath, initialContent, 'utf8');
580
397
  const params = {
581
398
  file_path: filePath,
582
- old_string: 'old',
583
- new_string: 'new',
584
- expected_replacements: 3, // Expecting 3 but only 2 exist
399
+ instruction: 'Replace two with three',
400
+ old_string: 'line two',
401
+ new_string: 'line three',
585
402
  };
586
403
  const invocation = tool.build(params);
587
- const result = await invocation.execute(new AbortController().signal);
588
- expect(result.llmContent).toMatch(/Expected 3 occurrences but found 2 for old_string in file/);
589
- expect(result.returnDisplay).toMatch(/Failed to edit, expected 3 occurrences but found 2/);
404
+ await invocation.execute(new AbortController().signal);
405
+ const finalContent = fs.readFileSync(filePath, 'utf8');
406
+ expect(finalContent).toBe(newContent);
590
407
  });
591
- it('should return error if trying to create a file that already exists (empty old_string)', async () => {
592
- fs.writeFileSync(filePath, 'Existing content', 'utf8');
408
+ it('should create a new file with CRLF line endings if new_string has them', async () => {
409
+ const newContentWithCRLF = 'new line one\r\nnew line two\r\n';
593
410
  const params = {
594
411
  file_path: filePath,
412
+ instruction: 'Create a new file',
595
413
  old_string: '',
596
- new_string: 'new content',
414
+ new_string: newContentWithCRLF,
597
415
  };
598
416
  const invocation = tool.build(params);
599
- const result = await invocation.execute(new AbortController().signal);
600
- expect(result.llmContent).toMatch(/File already exists, cannot create/);
601
- expect(result.returnDisplay).toMatch(/Attempted to create a file that already exists/);
417
+ await invocation.execute(new AbortController().signal);
418
+ const finalContent = fs.readFileSync(filePath, 'utf8');
419
+ expect(finalContent).toBe(newContentWithCRLF);
602
420
  });
603
- it('should include modification message when proposed content is modified', async () => {
604
- const initialContent = 'Line 1\nold line\nLine 3\nLine 4\nLine 5\n';
421
+ it('should return NO_CHANGE if FixLLMEditWithInstruction determines no changes are needed', async () => {
422
+ const initialContent = 'The price is $100.';
605
423
  fs.writeFileSync(filePath, initialContent, 'utf8');
606
424
  const params = {
607
425
  file_path: filePath,
608
- old_string: 'old',
609
- new_string: 'new',
610
- modified_by_user: true,
611
- ai_proposed_content: 'Line 1\nAI line\nLine 3\nLine 4\nLine 5\n',
426
+ instruction: 'Ensure the price is $100',
427
+ old_string: 'price is $50', // Incorrect old string
428
+ new_string: 'price is $100',
612
429
  };
613
- mockConfig.getApprovalMode.mockReturnValueOnce(ApprovalMode.AUTO_EDIT);
614
- const invocation = tool.build(params);
615
- const result = await invocation.execute(new AbortController().signal);
616
- expect(result.llmContent).toMatch(/User modified the `new_string` content/);
617
- expect(result.returnDisplay.diffStat).toStrictEqual({
618
- model_added_lines: 1,
619
- model_removed_lines: 1,
620
- model_added_chars: 7,
621
- model_removed_chars: 8,
622
- user_added_lines: 1,
623
- user_removed_lines: 1,
624
- user_added_chars: 8,
625
- user_removed_chars: 7,
430
+ mockFixLLMEditWithInstruction.mockResolvedValueOnce({
431
+ noChangesRequired: true,
432
+ search: '',
433
+ replace: '',
434
+ explanation: 'The price is already correctly set to $100.',
626
435
  });
627
- });
628
- it.each([
629
- {
630
- name: 'modified_by_user is false',
631
- modifiedByUser: false,
632
- },
633
- {
634
- name: 'modified_by_user is not provided',
635
- modifiedByUser: undefined,
636
- },
637
- ])('should not include modification message when $name', async ({ modifiedByUser }) => {
638
- const initialContent = 'This is some old text.';
639
- fs.writeFileSync(filePath, initialContent, 'utf8');
640
- const params = {
641
- file_path: filePath,
642
- old_string: 'old',
643
- new_string: 'new',
644
- ...(modifiedByUser !== undefined && {
645
- modified_by_user: modifiedByUser,
646
- }),
647
- };
648
- mockConfig.getApprovalMode.mockReturnValueOnce(ApprovalMode.AUTO_EDIT);
649
436
  const invocation = tool.build(params);
650
437
  const result = await invocation.execute(new AbortController().signal);
651
- expect(result.llmContent).not.toMatch(/User modified the `new_string` content/);
438
+ expect(result.error?.type).toBe(ToolErrorType.EDIT_NO_CHANGE_LLM_JUDGEMENT);
439
+ expect(result.llmContent).toMatch(/A secondary check by an LLM determined/);
440
+ expect(fs.readFileSync(filePath, 'utf8')).toBe(initialContent); // File is unchanged
652
441
  });
653
- it('should return error if old_string and new_string are identical', async () => {
654
- const initialContent = 'This is some identical text.';
655
- fs.writeFileSync(filePath, initialContent, 'utf8');
656
- const params = {
657
- file_path: filePath,
658
- old_string: 'identical',
659
- new_string: 'identical',
660
- };
661
- const invocation = tool.build(params);
662
- const result = await invocation.execute(new AbortController().signal);
663
- expect(result.llmContent).toMatch(/No changes to apply/);
664
- expect(result.returnDisplay).toMatch(/No changes to apply/);
442
+ });
443
+ describe('self-correction with content refresh to pull in external edits', () => {
444
+ const testFile = 'test.txt';
445
+ let filePath;
446
+ beforeEach(() => {
447
+ filePath = path.join(rootDir, testFile);
665
448
  });
666
- it('should return EDIT_NO_CHANGE error if replacement results in identical content', async () => {
667
- // This can happen if ensureCorrectEdit finds a fuzzy match, but the literal
668
- // string replacement with `replaceAll` results in no change.
669
- const initialContent = 'line 1\nline 2\nline 3'; // Note the double space
449
+ it('should use refreshed file content for self-correction if file was modified externally', async () => {
450
+ const initialContent = 'This is the original content.';
451
+ const externallyModifiedContent = 'This is the externally modified content.';
670
452
  fs.writeFileSync(filePath, initialContent, 'utf8');
671
453
  const params = {
672
454
  file_path: filePath,
673
- // old_string has a single space, so it won't be found by replaceAll
674
- old_string: 'line 1\nline 2\nline 3',
675
- new_string: 'line 1\nnew line 2\nline 3',
455
+ instruction: 'Replace "externally modified content" with "externally modified string"',
456
+ old_string: 'externally modified content', // This will fail the first attempt, triggering self-correction.
457
+ new_string: 'externally modified string',
676
458
  };
677
- // Mock ensureCorrectEdit to simulate it finding a match (e.g., via fuzzy matching)
678
- // but it doesn't correct the old_string to the literal content.
679
- mockEnsureCorrectEdit.mockResolvedValueOnce({ params, occurrences: 1 });
459
+ // Spy on `readTextFile` to simulate an external file change between reads.
460
+ const readTextFileSpy = vi
461
+ .spyOn(fileSystemService, 'readTextFile')
462
+ .mockResolvedValueOnce(initialContent) // First call in `calculateEdit`
463
+ .mockResolvedValueOnce(externallyModifiedContent); // Second call in `attemptSelfCorrection`
680
464
  const invocation = tool.build(params);
681
- const result = await invocation.execute(new AbortController().signal);
682
- expect(result.error?.type).toBe(ToolErrorType.EDIT_NO_CHANGE);
683
- expect(result.returnDisplay).toMatch(/No changes to apply. The new content is identical to the current content./);
684
- // Ensure the file was not actually changed
685
- expect(fs.readFileSync(filePath, 'utf8')).toBe(initialContent);
465
+ await invocation.execute(new AbortController().signal);
466
+ // Assert that the file was read twice (initial read, then re-read for hash comparison).
467
+ expect(readTextFileSpy).toHaveBeenCalledTimes(2);
468
+ // Assert that the self-correction LLM was called with the updated content and a specific message.
469
+ expect(mockFixLLMEditWithInstruction).toHaveBeenCalledWith(expect.any(String), // instruction
470
+ params.old_string, params.new_string, expect.stringContaining('However, the file has been modified by either the user or an external process'), // errorForLlmEditFixer
471
+ externallyModifiedContent, // The new content for correction
472
+ expect.any(Object), // baseLlmClient
473
+ expect.any(Object));
686
474
  });
687
475
  });
688
476
  describe('Error Scenarios', () => {
@@ -693,163 +481,85 @@ describe('EditTool', () => {
693
481
  });
694
482
  it.each([
695
483
  {
696
- name: 'FILE_NOT_FOUND error',
697
- setup: () => { },
698
- params: { file_path: '', old_string: 'any', new_string: 'new' },
484
+ name: 'FILE_NOT_FOUND',
485
+ setup: () => { }, // no file created
486
+ params: { old_string: 'any', new_string: 'new' },
699
487
  expectedError: ToolErrorType.FILE_NOT_FOUND,
700
- isAsyncTest: true,
701
488
  },
702
489
  {
703
- name: 'ATTEMPT_TO_CREATE_EXISTING_FILE error',
490
+ name: 'ATTEMPT_TO_CREATE_EXISTING_FILE',
704
491
  setup: (fp) => fs.writeFileSync(fp, 'existing content', 'utf8'),
705
- params: { file_path: '', old_string: '', new_string: 'new content' },
492
+ params: { old_string: '', new_string: 'new content' },
706
493
  expectedError: ToolErrorType.ATTEMPT_TO_CREATE_EXISTING_FILE,
707
- isAsyncTest: true,
708
494
  },
709
495
  {
710
- name: 'NO_OCCURRENCE_FOUND error',
496
+ name: 'NO_OCCURRENCE_FOUND',
711
497
  setup: (fp) => fs.writeFileSync(fp, 'content', 'utf8'),
712
- params: { file_path: '', old_string: 'not-found', new_string: 'new' },
498
+ params: { old_string: 'not-found', new_string: 'new' },
713
499
  expectedError: ToolErrorType.EDIT_NO_OCCURRENCE_FOUND,
714
- isAsyncTest: true,
715
500
  },
716
501
  {
717
- name: 'EXPECTED_OCCURRENCE_MISMATCH error',
502
+ name: 'EXPECTED_OCCURRENCE_MISMATCH',
718
503
  setup: (fp) => fs.writeFileSync(fp, 'one one two', 'utf8'),
719
- params: {
720
- file_path: '',
721
- old_string: 'one',
722
- new_string: 'new',
723
- expected_replacements: 3,
724
- },
504
+ params: { old_string: 'one', new_string: 'new' },
725
505
  expectedError: ToolErrorType.EDIT_EXPECTED_OCCURRENCE_MISMATCH,
726
- isAsyncTest: true,
727
- },
728
- {
729
- name: 'NO_CHANGE error',
730
- setup: (fp) => fs.writeFileSync(fp, 'content', 'utf8'),
731
- params: { file_path: '', old_string: 'content', new_string: 'content' },
732
- expectedError: ToolErrorType.EDIT_NO_CHANGE,
733
- isAsyncTest: true,
734
506
  },
735
- {
736
- name: 'relative path (should not throw)',
737
- setup: () => { },
738
- params: {
739
- file_path: 'relative/path.txt',
740
- old_string: 'a',
741
- new_string: 'b',
742
- },
743
- expectedError: null,
744
- isAsyncTest: false,
745
- },
746
- {
747
- name: 'FILE_WRITE_FAILURE on write error',
748
- setup: (fp) => {
749
- fs.writeFileSync(fp, 'content', 'utf8');
750
- fs.chmodSync(fp, '444');
751
- },
752
- params: {
753
- file_path: '',
754
- old_string: 'content',
755
- new_string: 'new content',
756
- },
757
- expectedError: ToolErrorType.FILE_WRITE_FAILURE,
758
- isAsyncTest: true,
759
- },
760
- ])('should return $name', async ({ setup, params, expectedError, isAsyncTest }) => {
761
- const testParams = {
762
- ...params,
763
- file_path: params.file_path || filePath,
764
- };
507
+ ])('should return $name error', async ({ setup, params, expectedError }) => {
765
508
  setup(filePath);
766
- if (!isAsyncTest) {
767
- expect(() => tool.build(testParams)).not.toThrow();
768
- }
769
- else {
770
- const invocation = tool.build(testParams);
771
- const result = await invocation.execute(new AbortController().signal);
772
- expect(result.error?.type).toBe(expectedError);
773
- }
509
+ const invocation = tool.build({
510
+ file_path: filePath,
511
+ instruction: 'test',
512
+ ...params,
513
+ });
514
+ const result = await invocation.execute(new AbortController().signal);
515
+ expect(result.error?.type).toBe(expectedError);
774
516
  });
775
517
  });
776
- describe('getDescription', () => {
518
+ describe('expected_replacements', () => {
519
+ const testFile = 'replacements_test.txt';
520
+ let filePath;
521
+ beforeEach(() => {
522
+ filePath = path.join(rootDir, testFile);
523
+ });
777
524
  it.each([
778
525
  {
779
- name: 'identical strings (no change)',
780
- fileName: 'test.txt',
781
- oldStr: 'identical_string',
782
- newStr: 'identical_string',
783
- expected: 'No file changes to test.txt',
784
- },
785
- {
786
- name: 'different strings (full)',
787
- fileName: 'test.txt',
788
- oldStr: 'this is the old string value',
789
- newStr: 'this is the new string value',
790
- expected: 'test.txt: this is the old string value => this is the new string value',
526
+ name: 'succeed when occurrences match expected_replacements',
527
+ content: 'foo foo foo',
528
+ expected: 3,
529
+ shouldSucceed: true,
530
+ finalContent: 'bar bar bar',
791
531
  },
792
532
  {
793
- name: 'very short strings',
794
- fileName: 'short.txt',
795
- oldStr: 'old',
796
- newStr: 'new',
797
- expected: 'short.txt: old => new',
533
+ name: 'fail when occurrences do not match expected_replacements',
534
+ content: 'foo foo foo',
535
+ expected: 2,
536
+ shouldSucceed: false,
798
537
  },
799
538
  {
800
- name: 'long strings (truncated)',
801
- fileName: 'long.txt',
802
- oldStr: 'this is a very long old string that will definitely be truncated',
803
- newStr: 'this is a very long new string that will also be truncated',
804
- expected: 'long.txt: this is a very long old string... => this is a very long new string...',
539
+ name: 'default to 1 expected replacement if not specified',
540
+ content: 'foo foo',
541
+ expected: undefined,
542
+ shouldSucceed: false,
805
543
  },
806
- ])('should handle $name', ({ fileName, oldStr, newStr, expected }) => {
544
+ ])('should $name', async ({ content, expected, shouldSucceed, finalContent }) => {
545
+ fs.writeFileSync(filePath, content, 'utf8');
807
546
  const params = {
808
- file_path: path.join(rootDir, fileName),
809
- old_string: oldStr,
810
- new_string: newStr,
547
+ file_path: filePath,
548
+ instruction: 'Replace all foo with bar',
549
+ old_string: 'foo',
550
+ new_string: 'bar',
551
+ ...(expected !== undefined && { expected_replacements: expected }),
811
552
  };
812
553
  const invocation = tool.build(params);
813
- expect(invocation.getDescription()).toBe(expected);
814
- });
815
- });
816
- describe('workspace boundary validation', () => {
817
- it('should validate paths are within workspace root', () => {
818
- const validPath = {
819
- file_path: path.join(rootDir, 'file.txt'),
820
- old_string: 'old',
821
- new_string: 'new',
822
- };
823
- expect(tool.validateToolParams(validPath)).toBeNull();
824
- });
825
- it('should reject paths outside workspace root', () => {
826
- const invalidPath = {
827
- file_path: '/etc/passwd',
828
- old_string: 'root',
829
- new_string: 'hacked',
830
- };
831
- const error = tool.validateToolParams(invalidPath);
832
- expect(error).toContain('File path must be within one of the workspace directories');
833
- expect(error).toContain(rootDir);
834
- });
835
- });
836
- describe('constructor', () => {
837
- afterEach(() => {
838
- vi.restoreAllMocks();
839
- });
840
- it('should use windows-style path examples on windows', () => {
841
- vi.spyOn(process, 'platform', 'get').mockReturnValue('win32');
842
- const tool = new EditTool({});
843
- const schema = tool.schema;
844
- expect(schema.parametersJsonSchema.properties
845
- .file_path.description).toBe('The path to the file to modify.');
846
- });
847
- it('should use unix-style path examples on non-windows platforms', () => {
848
- vi.spyOn(process, 'platform', 'get').mockReturnValue('linux');
849
- const tool = new EditTool({});
850
- const schema = tool.schema;
851
- expect(schema.parametersJsonSchema.properties
852
- .file_path.description).toBe('The path to the file to modify.');
554
+ const result = await invocation.execute(new AbortController().signal);
555
+ if (shouldSucceed) {
556
+ expect(result.error).toBeUndefined();
557
+ if (finalContent)
558
+ expect(fs.readFileSync(filePath, 'utf8')).toBe(finalContent);
559
+ }
560
+ else {
561
+ expect(result.error?.type).toBe(ToolErrorType.EDIT_EXPECTED_OCCURRENCE_MISMATCH);
562
+ }
853
563
  });
854
564
  });
855
565
  describe('IDE mode', () => {
@@ -872,13 +582,10 @@ describe('EditTool', () => {
872
582
  fs.writeFileSync(filePath, initialContent);
873
583
  const params = {
874
584
  file_path: filePath,
585
+ instruction: 'test',
875
586
  old_string: 'old',
876
587
  new_string: 'new',
877
588
  };
878
- mockEnsureCorrectEdit.mockResolvedValueOnce({
879
- params: { ...params, old_string: 'old', new_string: 'new' },
880
- occurrences: 1,
881
- });
882
589
  ideClient.openDiff.mockResolvedValueOnce({
883
590
  status: 'accepted',
884
591
  content: modifiedContent,
@@ -893,6 +600,30 @@ describe('EditTool', () => {
893
600
  expect(params.new_string).toBe(modifiedContent);
894
601
  });
895
602
  });
603
+ describe('shouldConfirmExecute', () => {
604
+ it('should rethrow calculateEdit errors when the abort signal is triggered', async () => {
605
+ const filePath = path.join(rootDir, 'abort-confirmation.txt');
606
+ const params = {
607
+ file_path: filePath,
608
+ instruction: 'Abort during confirmation',
609
+ old_string: 'old',
610
+ new_string: 'new',
611
+ };
612
+ const invocation = tool.build(params);
613
+ const abortController = new AbortController();
614
+ const abortError = new Error('Abort requested during edit confirmation');
615
+ const calculateSpy = vi
616
+ .spyOn(invocation, 'calculateEdit')
617
+ .mockImplementation(async () => {
618
+ if (!abortController.signal.aborted) {
619
+ abortController.abort();
620
+ }
621
+ throw abortError;
622
+ });
623
+ await expect(invocation.shouldConfirmExecute(abortController.signal)).rejects.toBe(abortError);
624
+ calculateSpy.mockRestore();
625
+ });
626
+ });
896
627
  describe('multiple file edits', () => {
897
628
  it('should perform multiple removals and report correct diff stats', async () => {
898
629
  const numFiles = 10;
@@ -923,8 +654,10 @@ describe('EditTool', () => {
923
654
  for (const file of files) {
924
655
  const params = {
925
656
  file_path: file.path,
657
+ instruction: `Remove lines from the file`,
926
658
  old_string: file.toRemove,
927
659
  new_string: '', // Removing the content
660
+ ai_proposed_content: '',
928
661
  };
929
662
  const invocation = tool.build(params);
930
663
  const result = await invocation.execute(new AbortController().signal);
@@ -935,7 +668,7 @@ describe('EditTool', () => {
935
668
  actualLinesRemoved.push(result.returnDisplay.diffStat?.model_removed_lines);
936
669
  }
937
670
  else if (result.error) {
938
- console.error(`Edit failed for ${file.path}:`, result.error);
671
+ throw result.error;
939
672
  }
940
673
  }
941
674
  // 3. Assert that the content was removed from each file
@@ -951,5 +684,38 @@ describe('EditTool', () => {
951
684
  expect(totalActualRemoved).toBe(totalExpectedRemoved);
952
685
  });
953
686
  });
687
+ describe('disableLLMCorrection', () => {
688
+ it('should NOT call FixLLMEditWithInstruction when disableLLMCorrection is true', async () => {
689
+ const filePath = path.join(rootDir, 'disable_llm_test.txt');
690
+ fs.writeFileSync(filePath, 'Some content.', 'utf8');
691
+ // Enable the setting
692
+ mockConfig.getDisableLLMCorrection.mockReturnValue(true);
693
+ const params = {
694
+ file_path: filePath,
695
+ instruction: 'Replace non-existent text',
696
+ old_string: 'nonexistent',
697
+ new_string: 'replacement',
698
+ };
699
+ const invocation = tool.build(params);
700
+ const result = await invocation.execute(new AbortController().signal);
701
+ expect(result.error?.type).toBe(ToolErrorType.EDIT_NO_OCCURRENCE_FOUND);
702
+ expect(mockFixLLMEditWithInstruction).not.toHaveBeenCalled();
703
+ });
704
+ it('should call FixLLMEditWithInstruction when disableLLMCorrection is false (default)', async () => {
705
+ const filePath = path.join(rootDir, 'enable_llm_test.txt');
706
+ fs.writeFileSync(filePath, 'Some content.', 'utf8');
707
+ // Default is false, but being explicit
708
+ mockConfig.getDisableLLMCorrection.mockReturnValue(false);
709
+ const params = {
710
+ file_path: filePath,
711
+ instruction: 'Replace non-existent text',
712
+ old_string: 'nonexistent',
713
+ new_string: 'replacement',
714
+ };
715
+ const invocation = tool.build(params);
716
+ await invocation.execute(new AbortController().signal);
717
+ expect(mockFixLLMEditWithInstruction).toHaveBeenCalled();
718
+ });
719
+ });
954
720
  });
955
721
  //# sourceMappingURL=edit.test.js.map