attocode 0.2.3 → 0.2.4

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 (327) hide show
  1. package/CHANGELOG.md +67 -1
  2. package/README.md +65 -5
  3. package/dist/src/adapters.d.ts.map +1 -1
  4. package/dist/src/adapters.js +15 -11
  5. package/dist/src/adapters.js.map +1 -1
  6. package/dist/src/agent.d.ts +38 -98
  7. package/dist/src/agent.d.ts.map +1 -1
  8. package/dist/src/agent.js +505 -2892
  9. package/dist/src/agent.js.map +1 -1
  10. package/dist/src/cli.d.ts.map +1 -1
  11. package/dist/src/cli.js +2 -1
  12. package/dist/src/cli.js.map +1 -1
  13. package/dist/src/commands/handler.d.ts.map +1 -1
  14. package/dist/src/commands/handler.js +11 -3
  15. package/dist/src/commands/handler.js.map +1 -1
  16. package/dist/src/commands/init-commands.d.ts.map +1 -1
  17. package/dist/src/commands/init-commands.js +16 -1
  18. package/dist/src/commands/init-commands.js.map +1 -1
  19. package/dist/src/commands/init.d.ts.map +1 -1
  20. package/dist/src/commands/init.js +31 -0
  21. package/dist/src/commands/init.js.map +1 -1
  22. package/dist/src/config/base-types.d.ts +45 -0
  23. package/dist/src/config/base-types.d.ts.map +1 -0
  24. package/dist/src/config/base-types.js +9 -0
  25. package/dist/src/config/base-types.js.map +1 -0
  26. package/dist/src/config/config-manager.d.ts +35 -0
  27. package/dist/src/config/config-manager.d.ts.map +1 -0
  28. package/dist/src/config/config-manager.js +108 -0
  29. package/dist/src/config/config-manager.js.map +1 -0
  30. package/dist/src/config/index.d.ts +4 -0
  31. package/dist/src/config/index.d.ts.map +1 -0
  32. package/dist/src/config/index.js +3 -0
  33. package/dist/src/config/index.js.map +1 -0
  34. package/dist/src/config/schema.d.ts +1546 -0
  35. package/dist/src/config/schema.d.ts.map +1 -0
  36. package/dist/src/config/schema.js +268 -0
  37. package/dist/src/config/schema.js.map +1 -0
  38. package/dist/src/config.d.ts +4 -1
  39. package/dist/src/config.d.ts.map +1 -1
  40. package/dist/src/config.js +8 -12
  41. package/dist/src/config.js.map +1 -1
  42. package/dist/src/core/agent-state-machine.d.ts +131 -0
  43. package/dist/src/core/agent-state-machine.d.ts.map +1 -0
  44. package/dist/src/core/agent-state-machine.js +302 -0
  45. package/dist/src/core/agent-state-machine.js.map +1 -0
  46. package/dist/src/core/base-manager.d.ts +79 -0
  47. package/dist/src/core/base-manager.d.ts.map +1 -0
  48. package/dist/src/core/base-manager.js +170 -0
  49. package/dist/src/core/base-manager.js.map +1 -0
  50. package/dist/src/core/completion-analyzer.d.ts +15 -0
  51. package/dist/src/core/completion-analyzer.d.ts.map +1 -0
  52. package/dist/src/core/completion-analyzer.js +53 -0
  53. package/dist/src/core/completion-analyzer.js.map +1 -0
  54. package/dist/src/core/execution-loop.d.ts +46 -0
  55. package/dist/src/core/execution-loop.d.ts.map +1 -0
  56. package/dist/src/core/execution-loop.js +1258 -0
  57. package/dist/src/core/execution-loop.js.map +1 -0
  58. package/dist/src/core/index.d.ts +7 -0
  59. package/dist/src/core/index.d.ts.map +1 -1
  60. package/dist/src/core/index.js +9 -0
  61. package/dist/src/core/index.js.map +1 -1
  62. package/dist/src/core/process-handlers.d.ts.map +1 -1
  63. package/dist/src/core/process-handlers.js +14 -0
  64. package/dist/src/core/process-handlers.js.map +1 -1
  65. package/dist/src/core/protocol/types.d.ts +12 -12
  66. package/dist/src/core/response-handler.d.ts +16 -0
  67. package/dist/src/core/response-handler.d.ts.map +1 -0
  68. package/dist/src/core/response-handler.js +234 -0
  69. package/dist/src/core/response-handler.js.map +1 -0
  70. package/dist/src/core/subagent-spawner.d.ts +43 -0
  71. package/dist/src/core/subagent-spawner.d.ts.map +1 -0
  72. package/dist/src/core/subagent-spawner.js +966 -0
  73. package/dist/src/core/subagent-spawner.js.map +1 -0
  74. package/dist/src/core/tool-executor.d.ts +59 -0
  75. package/dist/src/core/tool-executor.d.ts.map +1 -0
  76. package/dist/src/core/tool-executor.js +677 -0
  77. package/dist/src/core/tool-executor.js.map +1 -0
  78. package/dist/src/core/types.d.ts +133 -0
  79. package/dist/src/core/types.d.ts.map +1 -0
  80. package/dist/src/core/types.js +12 -0
  81. package/dist/src/core/types.js.map +1 -0
  82. package/dist/src/defaults.d.ts +2 -2
  83. package/dist/src/defaults.d.ts.map +1 -1
  84. package/dist/src/defaults.js +29 -1
  85. package/dist/src/defaults.js.map +1 -1
  86. package/dist/src/integrations/auto-compaction.d.ts.map +1 -1
  87. package/dist/src/integrations/auto-compaction.js +3 -2
  88. package/dist/src/integrations/auto-compaction.js.map +1 -1
  89. package/dist/src/integrations/budget-pool.d.ts +7 -0
  90. package/dist/src/integrations/budget-pool.d.ts.map +1 -1
  91. package/dist/src/integrations/budget-pool.js +43 -0
  92. package/dist/src/integrations/budget-pool.js.map +1 -1
  93. package/dist/src/integrations/codebase-ast.d.ts +52 -0
  94. package/dist/src/integrations/codebase-ast.d.ts.map +1 -0
  95. package/dist/src/integrations/codebase-ast.js +457 -0
  96. package/dist/src/integrations/codebase-ast.js.map +1 -0
  97. package/dist/src/integrations/codebase-context.d.ts +18 -0
  98. package/dist/src/integrations/codebase-context.d.ts.map +1 -1
  99. package/dist/src/integrations/codebase-context.js +197 -17
  100. package/dist/src/integrations/codebase-context.js.map +1 -1
  101. package/dist/src/integrations/compaction.d.ts.map +1 -1
  102. package/dist/src/integrations/compaction.js +14 -6
  103. package/dist/src/integrations/compaction.js.map +1 -1
  104. package/dist/src/integrations/context-engineering.d.ts +8 -0
  105. package/dist/src/integrations/context-engineering.d.ts.map +1 -1
  106. package/dist/src/integrations/context-engineering.js +19 -0
  107. package/dist/src/integrations/context-engineering.js.map +1 -1
  108. package/dist/src/integrations/economics.d.ts +25 -1
  109. package/dist/src/integrations/economics.d.ts.map +1 -1
  110. package/dist/src/integrations/economics.js +217 -38
  111. package/dist/src/integrations/economics.js.map +1 -1
  112. package/dist/src/integrations/edit-validator.d.ts +30 -0
  113. package/dist/src/integrations/edit-validator.d.ts.map +1 -0
  114. package/dist/src/integrations/edit-validator.js +85 -0
  115. package/dist/src/integrations/edit-validator.js.map +1 -0
  116. package/dist/src/integrations/file-cache.d.ts +7 -0
  117. package/dist/src/integrations/file-cache.d.ts.map +1 -1
  118. package/dist/src/integrations/file-cache.js +54 -0
  119. package/dist/src/integrations/file-cache.js.map +1 -1
  120. package/dist/src/integrations/health-check.d.ts.map +1 -1
  121. package/dist/src/integrations/health-check.js +3 -2
  122. package/dist/src/integrations/health-check.js.map +1 -1
  123. package/dist/src/integrations/hierarchical-config.d.ts +3 -0
  124. package/dist/src/integrations/hierarchical-config.d.ts.map +1 -1
  125. package/dist/src/integrations/hierarchical-config.js +3 -0
  126. package/dist/src/integrations/hierarchical-config.js.map +1 -1
  127. package/dist/src/integrations/hooks.d.ts +2 -0
  128. package/dist/src/integrations/hooks.d.ts.map +1 -1
  129. package/dist/src/integrations/hooks.js +99 -15
  130. package/dist/src/integrations/hooks.js.map +1 -1
  131. package/dist/src/integrations/index.d.ts +7 -0
  132. package/dist/src/integrations/index.d.ts.map +1 -1
  133. package/dist/src/integrations/index.js +9 -1
  134. package/dist/src/integrations/index.js.map +1 -1
  135. package/dist/src/integrations/logger.d.ts +104 -0
  136. package/dist/src/integrations/logger.d.ts.map +1 -0
  137. package/dist/src/integrations/logger.js +219 -0
  138. package/dist/src/integrations/logger.js.map +1 -0
  139. package/dist/src/integrations/lsp.d.ts.map +1 -1
  140. package/dist/src/integrations/lsp.js +5 -4
  141. package/dist/src/integrations/lsp.js.map +1 -1
  142. package/dist/src/integrations/mcp-client.d.ts.map +1 -1
  143. package/dist/src/integrations/mcp-client.js +8 -7
  144. package/dist/src/integrations/mcp-client.js.map +1 -1
  145. package/dist/src/integrations/observability.d.ts.map +1 -1
  146. package/dist/src/integrations/observability.js +5 -4
  147. package/dist/src/integrations/observability.js.map +1 -1
  148. package/dist/src/integrations/openrouter-pricing.d.ts.map +1 -1
  149. package/dist/src/integrations/openrouter-pricing.js +4 -3
  150. package/dist/src/integrations/openrouter-pricing.js.map +1 -1
  151. package/dist/src/integrations/persistence.d.ts.map +1 -1
  152. package/dist/src/integrations/persistence.js +5 -4
  153. package/dist/src/integrations/persistence.js.map +1 -1
  154. package/dist/src/integrations/planning.d.ts.map +1 -1
  155. package/dist/src/integrations/planning.js +5 -4
  156. package/dist/src/integrations/planning.js.map +1 -1
  157. package/dist/src/integrations/retry.d.ts +1 -0
  158. package/dist/src/integrations/retry.d.ts.map +1 -1
  159. package/dist/src/integrations/retry.js.map +1 -1
  160. package/dist/src/integrations/routing.d.ts.map +1 -1
  161. package/dist/src/integrations/routing.js +2 -1
  162. package/dist/src/integrations/routing.js.map +1 -1
  163. package/dist/src/integrations/safety.d.ts.map +1 -1
  164. package/dist/src/integrations/safety.js +13 -13
  165. package/dist/src/integrations/safety.js.map +1 -1
  166. package/dist/src/integrations/sandbox/docker.d.ts.map +1 -1
  167. package/dist/src/integrations/sandbox/docker.js +2 -1
  168. package/dist/src/integrations/sandbox/docker.js.map +1 -1
  169. package/dist/src/integrations/sandbox/index.d.ts.map +1 -1
  170. package/dist/src/integrations/sandbox/index.js +5 -4
  171. package/dist/src/integrations/sandbox/index.js.map +1 -1
  172. package/dist/src/integrations/session-store.d.ts +1 -0
  173. package/dist/src/integrations/session-store.d.ts.map +1 -1
  174. package/dist/src/integrations/session-store.js +1 -0
  175. package/dist/src/integrations/session-store.js.map +1 -1
  176. package/dist/src/integrations/shared-blackboard.d.ts +3 -0
  177. package/dist/src/integrations/shared-blackboard.d.ts.map +1 -1
  178. package/dist/src/integrations/shared-blackboard.js +47 -0
  179. package/dist/src/integrations/shared-blackboard.js.map +1 -1
  180. package/dist/src/integrations/smart-decomposer.d.ts +27 -0
  181. package/dist/src/integrations/smart-decomposer.d.ts.map +1 -1
  182. package/dist/src/integrations/smart-decomposer.js +414 -30
  183. package/dist/src/integrations/smart-decomposer.js.map +1 -1
  184. package/dist/src/integrations/sqlite-store.d.ts +2 -0
  185. package/dist/src/integrations/sqlite-store.d.ts.map +1 -1
  186. package/dist/src/integrations/sqlite-store.js +18 -6
  187. package/dist/src/integrations/sqlite-store.js.map +1 -1
  188. package/dist/src/integrations/swarm/failure-classifier.d.ts +11 -0
  189. package/dist/src/integrations/swarm/failure-classifier.d.ts.map +1 -0
  190. package/dist/src/integrations/swarm/failure-classifier.js +95 -0
  191. package/dist/src/integrations/swarm/failure-classifier.js.map +1 -0
  192. package/dist/src/integrations/swarm/model-selector.d.ts.map +1 -1
  193. package/dist/src/integrations/swarm/model-selector.js +2 -1
  194. package/dist/src/integrations/swarm/model-selector.js.map +1 -1
  195. package/dist/src/integrations/swarm/swarm-config-loader.d.ts +8 -0
  196. package/dist/src/integrations/swarm/swarm-config-loader.d.ts.map +1 -1
  197. package/dist/src/integrations/swarm/swarm-config-loader.js +95 -0
  198. package/dist/src/integrations/swarm/swarm-config-loader.js.map +1 -1
  199. package/dist/src/integrations/swarm/swarm-event-bridge.d.ts +74 -0
  200. package/dist/src/integrations/swarm/swarm-event-bridge.d.ts.map +1 -1
  201. package/dist/src/integrations/swarm/swarm-event-bridge.js +37 -0
  202. package/dist/src/integrations/swarm/swarm-event-bridge.js.map +1 -1
  203. package/dist/src/integrations/swarm/swarm-events.d.ts +3 -0
  204. package/dist/src/integrations/swarm/swarm-events.d.ts.map +1 -1
  205. package/dist/src/integrations/swarm/swarm-events.js +1 -1
  206. package/dist/src/integrations/swarm/swarm-events.js.map +1 -1
  207. package/dist/src/integrations/swarm/swarm-orchestrator.d.ts +23 -0
  208. package/dist/src/integrations/swarm/swarm-orchestrator.d.ts.map +1 -1
  209. package/dist/src/integrations/swarm/swarm-orchestrator.js +530 -55
  210. package/dist/src/integrations/swarm/swarm-orchestrator.js.map +1 -1
  211. package/dist/src/integrations/swarm/swarm-state-store.d.ts +4 -1
  212. package/dist/src/integrations/swarm/swarm-state-store.d.ts.map +1 -1
  213. package/dist/src/integrations/swarm/swarm-state-store.js +8 -1
  214. package/dist/src/integrations/swarm/swarm-state-store.js.map +1 -1
  215. package/dist/src/integrations/swarm/task-queue.d.ts +10 -0
  216. package/dist/src/integrations/swarm/task-queue.d.ts.map +1 -1
  217. package/dist/src/integrations/swarm/task-queue.js +36 -1
  218. package/dist/src/integrations/swarm/task-queue.js.map +1 -1
  219. package/dist/src/integrations/swarm/types.d.ts +41 -0
  220. package/dist/src/integrations/swarm/types.d.ts.map +1 -1
  221. package/dist/src/integrations/swarm/types.js +9 -0
  222. package/dist/src/integrations/swarm/types.js.map +1 -1
  223. package/dist/src/integrations/swarm/worker-pool.d.ts +12 -2
  224. package/dist/src/integrations/swarm/worker-pool.d.ts.map +1 -1
  225. package/dist/src/integrations/swarm/worker-pool.js +53 -4
  226. package/dist/src/integrations/swarm/worker-pool.js.map +1 -1
  227. package/dist/src/integrations/task-manager.d.ts +33 -1
  228. package/dist/src/integrations/task-manager.d.ts.map +1 -1
  229. package/dist/src/integrations/task-manager.js +78 -4
  230. package/dist/src/integrations/task-manager.js.map +1 -1
  231. package/dist/src/main.js +83 -32
  232. package/dist/src/main.js.map +1 -1
  233. package/dist/src/modes/repl.d.ts.map +1 -1
  234. package/dist/src/modes/repl.js +40 -8
  235. package/dist/src/modes/repl.js.map +1 -1
  236. package/dist/src/modes/tui.d.ts.map +1 -1
  237. package/dist/src/modes/tui.js +36 -6
  238. package/dist/src/modes/tui.js.map +1 -1
  239. package/dist/src/observability/tracer.d.ts.map +1 -1
  240. package/dist/src/observability/tracer.js +2 -1
  241. package/dist/src/observability/tracer.js.map +1 -1
  242. package/dist/src/persistence/schema.d.ts.map +1 -1
  243. package/dist/src/persistence/schema.js +11 -0
  244. package/dist/src/persistence/schema.js.map +1 -1
  245. package/dist/src/providers/adapters/anthropic.d.ts.map +1 -1
  246. package/dist/src/providers/adapters/anthropic.js +3 -2
  247. package/dist/src/providers/adapters/anthropic.js.map +1 -1
  248. package/dist/src/providers/adapters/openai.d.ts.map +1 -1
  249. package/dist/src/providers/adapters/openai.js +3 -2
  250. package/dist/src/providers/adapters/openai.js.map +1 -1
  251. package/dist/src/providers/adapters/openrouter.d.ts.map +1 -1
  252. package/dist/src/providers/adapters/openrouter.js +11 -11
  253. package/dist/src/providers/adapters/openrouter.js.map +1 -1
  254. package/dist/src/providers/circuit-breaker.d.ts +1 -0
  255. package/dist/src/providers/circuit-breaker.d.ts.map +1 -1
  256. package/dist/src/providers/circuit-breaker.js.map +1 -1
  257. package/dist/src/providers/provider.d.ts.map +1 -1
  258. package/dist/src/providers/provider.js +2 -1
  259. package/dist/src/providers/provider.js.map +1 -1
  260. package/dist/src/providers/resilient-provider.d.ts.map +1 -1
  261. package/dist/src/providers/resilient-provider.js +2 -1
  262. package/dist/src/providers/resilient-provider.js.map +1 -1
  263. package/dist/src/session-picker.d.ts.map +1 -1
  264. package/dist/src/session-picker.js +40 -5
  265. package/dist/src/session-picker.js.map +1 -1
  266. package/dist/src/shared/budget-tracker.d.ts +65 -0
  267. package/dist/src/shared/budget-tracker.d.ts.map +1 -0
  268. package/dist/src/shared/budget-tracker.js +128 -0
  269. package/dist/src/shared/budget-tracker.js.map +1 -0
  270. package/dist/src/shared/context-engine.d.ts +64 -0
  271. package/dist/src/shared/context-engine.d.ts.map +1 -0
  272. package/dist/src/shared/context-engine.js +117 -0
  273. package/dist/src/shared/context-engine.js.map +1 -0
  274. package/dist/src/shared/index.d.ts +12 -0
  275. package/dist/src/shared/index.d.ts.map +1 -0
  276. package/dist/src/shared/index.js +12 -0
  277. package/dist/src/shared/index.js.map +1 -0
  278. package/dist/src/shared/persistence.d.ts +57 -0
  279. package/dist/src/shared/persistence.d.ts.map +1 -0
  280. package/dist/src/shared/persistence.js +168 -0
  281. package/dist/src/shared/persistence.js.map +1 -0
  282. package/dist/src/shared/shared-context-state.d.ts +89 -0
  283. package/dist/src/shared/shared-context-state.d.ts.map +1 -0
  284. package/dist/src/shared/shared-context-state.js +175 -0
  285. package/dist/src/shared/shared-context-state.js.map +1 -0
  286. package/dist/src/shared/shared-economics-state.d.ts +61 -0
  287. package/dist/src/shared/shared-economics-state.d.ts.map +1 -0
  288. package/dist/src/shared/shared-economics-state.js +100 -0
  289. package/dist/src/shared/shared-economics-state.js.map +1 -0
  290. package/dist/src/tools/bash.d.ts +3 -3
  291. package/dist/src/tools/bash.d.ts.map +1 -1
  292. package/dist/src/tools/bash.js +2 -1
  293. package/dist/src/tools/bash.js.map +1 -1
  294. package/dist/src/tools/file.d.ts +3 -3
  295. package/dist/src/tools/permission.d.ts.map +1 -1
  296. package/dist/src/tools/permission.js +6 -5
  297. package/dist/src/tools/permission.js.map +1 -1
  298. package/dist/src/tools/types.d.ts +1 -0
  299. package/dist/src/tools/types.d.ts.map +1 -1
  300. package/dist/src/tools/types.js.map +1 -1
  301. package/dist/src/tracing/trace-collector.d.ts +125 -0
  302. package/dist/src/tracing/trace-collector.d.ts.map +1 -1
  303. package/dist/src/tracing/trace-collector.js +112 -5
  304. package/dist/src/tracing/trace-collector.js.map +1 -1
  305. package/dist/src/tracing/types.d.ts +96 -1
  306. package/dist/src/tracing/types.d.ts.map +1 -1
  307. package/dist/src/tracing/types.js.map +1 -1
  308. package/dist/src/tricks/failure-evidence.d.ts.map +1 -1
  309. package/dist/src/tricks/failure-evidence.js +2 -1
  310. package/dist/src/tricks/failure-evidence.js.map +1 -1
  311. package/dist/src/tui/app.d.ts +13 -0
  312. package/dist/src/tui/app.d.ts.map +1 -1
  313. package/dist/src/tui/app.js +129 -15
  314. package/dist/src/tui/app.js.map +1 -1
  315. package/dist/src/tui/components/ErrorBoundary.d.ts.map +1 -1
  316. package/dist/src/tui/components/ErrorBoundary.js +3 -2
  317. package/dist/src/tui/components/ErrorBoundary.js.map +1 -1
  318. package/dist/src/tui/event-display.d.ts.map +1 -1
  319. package/dist/src/tui/event-display.js +36 -62
  320. package/dist/src/tui/event-display.js.map +1 -1
  321. package/dist/src/tui/index.d.ts +4 -0
  322. package/dist/src/tui/index.d.ts.map +1 -1
  323. package/dist/src/tui/index.js +17 -0
  324. package/dist/src/tui/index.js.map +1 -1
  325. package/dist/src/types.d.ts +143 -1
  326. package/dist/src/types.d.ts.map +1 -1
  327. package/package.json +18 -3
@@ -0,0 +1,677 @@
1
+ /**
2
+ * Tool Executor Module (Phase 2.1)
3
+ *
4
+ * Extracted from ProductionAgent.executeToolCalls() and executeSingleToolCall().
5
+ * Handles batch grouping, parallel/sequential dispatch, plan mode interception,
6
+ * policy enforcement, safety validation, blackboard coordination, file cache,
7
+ * and actual tool execution.
8
+ */
9
+ import { createComponentLogger } from '../integrations/logger.js';
10
+ // =============================================================================
11
+ // TOOL BATCHING CONSTANTS & UTILITIES (moved from agent.ts to break cycle)
12
+ // =============================================================================
13
+ /**
14
+ * Tools that are safe to execute in parallel (read-only, no side effects).
15
+ * These tools don't modify state, so running them concurrently is safe.
16
+ */
17
+ export const PARALLELIZABLE_TOOLS = new Set([
18
+ 'read_file', 'glob', 'grep', 'list_files', 'search_files',
19
+ 'search_code', 'get_file_info',
20
+ 'task_create', 'task_update', 'task_get', 'task_list',
21
+ ]);
22
+ /**
23
+ * Tools that can run in parallel IF they target different files.
24
+ * write_file and edit_file on different paths are safe to parallelize.
25
+ */
26
+ export const CONDITIONALLY_PARALLEL_TOOLS = new Set([
27
+ 'write_file', 'edit_file',
28
+ ]);
29
+ /**
30
+ * Extract the target file path from a tool call's arguments.
31
+ * Returns null if no file path can be determined.
32
+ */
33
+ export function extractToolFilePath(toolCall) {
34
+ const args = toolCall;
35
+ for (const key of ['path', 'file_path', 'filename', 'file']) {
36
+ if (typeof args[key] === 'string')
37
+ return args[key];
38
+ }
39
+ if (args.args && typeof args.args === 'object') {
40
+ const nested = args.args;
41
+ for (const key of ['path', 'file_path', 'filename', 'file']) {
42
+ if (typeof nested[key] === 'string')
43
+ return nested[key];
44
+ }
45
+ }
46
+ if (args.input && typeof args.input === 'object') {
47
+ const input = args.input;
48
+ for (const key of ['path', 'file_path', 'filename', 'file']) {
49
+ if (typeof input[key] === 'string')
50
+ return input[key];
51
+ }
52
+ }
53
+ return null;
54
+ }
55
+ /**
56
+ * Check if a conditionally-parallel tool call conflicts with any tool
57
+ * in the current accumulator (same file path).
58
+ */
59
+ function hasFileConflict(toolCall, accumulator) {
60
+ const path = extractToolFilePath(toolCall);
61
+ if (!path)
62
+ return true;
63
+ for (const existing of accumulator) {
64
+ const existingPath = extractToolFilePath(existing);
65
+ if (existingPath === path)
66
+ return true;
67
+ }
68
+ return false;
69
+ }
70
+ /**
71
+ * Groups tool calls into batches for parallel/sequential execution.
72
+ */
73
+ export function groupToolCallsIntoBatches(toolCalls, isParallelizable = (tc) => PARALLELIZABLE_TOOLS.has(tc.name), isConditionallyParallel = (tc) => CONDITIONALLY_PARALLEL_TOOLS.has(tc.name)) {
74
+ if (toolCalls.length === 0)
75
+ return [];
76
+ const batches = [];
77
+ let parallelAccum = [];
78
+ for (const toolCall of toolCalls) {
79
+ if (isParallelizable(toolCall)) {
80
+ parallelAccum.push(toolCall);
81
+ }
82
+ else if (isConditionallyParallel(toolCall)) {
83
+ if (!hasFileConflict(toolCall, parallelAccum)) {
84
+ parallelAccum.push(toolCall);
85
+ }
86
+ else {
87
+ if (parallelAccum.length > 0) {
88
+ batches.push(parallelAccum);
89
+ parallelAccum = [];
90
+ }
91
+ parallelAccum.push(toolCall);
92
+ }
93
+ }
94
+ else {
95
+ if (parallelAccum.length > 0) {
96
+ batches.push(parallelAccum);
97
+ parallelAccum = [];
98
+ }
99
+ batches.push([toolCall]);
100
+ }
101
+ }
102
+ if (parallelAccum.length > 0) {
103
+ batches.push(parallelAccum);
104
+ }
105
+ return batches;
106
+ }
107
+ const log = createComponentLogger('ToolExecutor');
108
+ /**
109
+ * Execute tool calls with safety checks and execution policy enforcement.
110
+ * Parallelizable read-only tools are batched and executed concurrently.
111
+ */
112
+ export async function executeToolCalls(toolCalls, ctx) {
113
+ const results = [];
114
+ // Circuit breaker state
115
+ const circuitBreakerThreshold = ctx.economics?.getBudget()?.tuning?.circuitBreakerFailureThreshold ?? 5;
116
+ let consecutiveFailures = 0;
117
+ let circuitBroken = false;
118
+ // Group consecutive parallelizable tool calls into batches
119
+ const batches = groupToolCallsIntoBatches(toolCalls);
120
+ // Execute batches: parallel batches use Promise.allSettled, sequential execute one-by-one
121
+ for (const batch of batches) {
122
+ // SAFEGUARD: Circuit breaker — skip remaining batches with stub results
123
+ if (circuitBroken) {
124
+ for (const tc of batch) {
125
+ results.push({
126
+ callId: tc.id,
127
+ result: `Error: Skipped — circuit breaker tripped after ${circuitBreakerThreshold} consecutive failures`,
128
+ error: `Circuit breaker tripped after ${circuitBreakerThreshold} consecutive failures`,
129
+ });
130
+ }
131
+ continue;
132
+ }
133
+ if (batch.length > 1 && PARALLELIZABLE_TOOLS.has(batch[0].name)) {
134
+ // Execute parallelizable batch concurrently
135
+ const batchResults = await Promise.allSettled(batch.map(tc => executeSingleToolCall(tc, ctx)));
136
+ let batchFailures = 0;
137
+ for (const result of batchResults) {
138
+ if (result.status === 'fulfilled') {
139
+ results.push(result.value);
140
+ if (result.value.error) {
141
+ batchFailures++;
142
+ }
143
+ }
144
+ else {
145
+ const error = result.reason instanceof Error ? result.reason.message : String(result.reason);
146
+ results.push({ callId: 'unknown', result: `Error: ${error}`, error });
147
+ batchFailures++;
148
+ }
149
+ }
150
+ // If entire parallel batch failed, add to consecutive failures; any success resets
151
+ if (batchFailures === batchResults.length) {
152
+ consecutiveFailures += batchFailures;
153
+ }
154
+ else {
155
+ consecutiveFailures = 0;
156
+ }
157
+ }
158
+ else {
159
+ // Execute sequentially
160
+ for (const tc of batch) {
161
+ if (circuitBroken) {
162
+ results.push({
163
+ callId: tc.id,
164
+ result: `Error: Skipped — circuit breaker tripped after ${circuitBreakerThreshold} consecutive failures`,
165
+ error: `Circuit breaker tripped after ${circuitBreakerThreshold} consecutive failures`,
166
+ });
167
+ continue;
168
+ }
169
+ const result = await executeSingleToolCall(tc, ctx);
170
+ results.push(result);
171
+ if (result.error) {
172
+ consecutiveFailures++;
173
+ }
174
+ else {
175
+ consecutiveFailures = 0;
176
+ }
177
+ if (consecutiveFailures >= circuitBreakerThreshold) {
178
+ circuitBroken = true;
179
+ const skipped = toolCalls.length - results.length;
180
+ log.warn('Circuit breaker tripped — stopping tool execution', {
181
+ totalInBatch: toolCalls.length,
182
+ failures: consecutiveFailures,
183
+ threshold: circuitBreakerThreshold,
184
+ skipped,
185
+ });
186
+ ctx.emit({
187
+ type: 'safeguard.circuit_breaker',
188
+ totalInBatch: toolCalls.length,
189
+ failures: consecutiveFailures,
190
+ threshold: circuitBreakerThreshold,
191
+ skipped,
192
+ });
193
+ }
194
+ }
195
+ }
196
+ // Check circuit breaker after parallel batches too
197
+ if (!circuitBroken && consecutiveFailures >= circuitBreakerThreshold) {
198
+ circuitBroken = true;
199
+ const skipped = toolCalls.length - results.length;
200
+ log.warn('Circuit breaker tripped — stopping tool execution', {
201
+ totalInBatch: toolCalls.length,
202
+ failures: consecutiveFailures,
203
+ threshold: circuitBreakerThreshold,
204
+ skipped,
205
+ });
206
+ ctx.emit({
207
+ type: 'safeguard.circuit_breaker',
208
+ totalInBatch: toolCalls.length,
209
+ failures: consecutiveFailures,
210
+ threshold: circuitBreakerThreshold,
211
+ skipped,
212
+ });
213
+ }
214
+ }
215
+ return results;
216
+ }
217
+ /**
218
+ * Execute a single tool call with all safety checks, tracing, and error handling.
219
+ */
220
+ export async function executeSingleToolCall(toolCall, ctx) {
221
+ const spanId = ctx.observability?.tracer?.startSpan(`tool.${toolCall.name}`);
222
+ const executionId = `exec-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
223
+ ctx.emit({ type: 'tool.start', tool: toolCall.name, args: toolCall.arguments });
224
+ const startTime = Date.now();
225
+ // Short-circuit if tool call arguments failed to parse
226
+ if (toolCall.parseError) {
227
+ const errorMsg = `Tool arguments could not be parsed: ${toolCall.parseError}. Please retry with complete, valid JSON.`;
228
+ ctx.emit({ type: 'tool.blocked', tool: toolCall.name, reason: errorMsg });
229
+ ctx.traceCollector?.record({
230
+ type: 'tool.end',
231
+ data: { executionId, status: 'error', error: new Error(errorMsg), durationMs: Date.now() - startTime },
232
+ });
233
+ ctx.observability?.tracer?.endSpan(spanId);
234
+ return { callId: toolCall.id, result: `Error: ${errorMsg}`, error: errorMsg };
235
+ }
236
+ // Record tool start for tracing
237
+ ctx.traceCollector?.record({
238
+ type: 'tool.start',
239
+ data: {
240
+ executionId,
241
+ toolName: toolCall.name,
242
+ arguments: toolCall.arguments,
243
+ },
244
+ });
245
+ try {
246
+ // =====================================================================
247
+ // PLAN MODE WRITE INTERCEPTION
248
+ // =====================================================================
249
+ if (ctx.modeManager.shouldInterceptTool(toolCall.name, toolCall.arguments)) {
250
+ const reason = extractChangeReasoning(toolCall, ctx.state.messages);
251
+ // Start a new plan if needed
252
+ if (!ctx.pendingPlanManager.hasPendingPlan()) {
253
+ const lastUserMsg = [...ctx.state.messages].reverse().find(m => m.role === 'user');
254
+ const task = typeof lastUserMsg?.content === 'string' ? lastUserMsg.content : 'Plan';
255
+ ctx.pendingPlanManager.startPlan(task);
256
+ }
257
+ // Queue the write operation
258
+ const change = ctx.pendingPlanManager.addProposedChange(toolCall.name, toolCall.arguments, reason, toolCall.id);
259
+ // Emit event for UI
260
+ ctx.emit({
261
+ type: 'plan.change.queued',
262
+ tool: toolCall.name,
263
+ changeId: change?.id,
264
+ summary: formatToolArgsForPlan(toolCall.name, toolCall.arguments),
265
+ });
266
+ // Return a message indicating the change was queued
267
+ const queueMessage = `[PLAN MODE] Change queued for approval:\n` +
268
+ `Tool: ${toolCall.name}\n` +
269
+ `${formatToolArgsForPlan(toolCall.name, toolCall.arguments)}\n` +
270
+ `Use /show-plan to see all pending changes, /approve to execute, /reject to discard.`;
271
+ ctx.observability?.tracer?.endSpan(spanId);
272
+ return { callId: toolCall.id, result: queueMessage };
273
+ }
274
+ // =====================================================================
275
+ // EXECUTION POLICY ENFORCEMENT (Lesson 23)
276
+ // =====================================================================
277
+ let policyApprovedByUser = false;
278
+ if (ctx.executionPolicy) {
279
+ const policyContext = {
280
+ messages: ctx.state.messages,
281
+ currentMessage: ctx.state.messages.find(m => m.role === 'user')?.content,
282
+ previousToolCalls: [],
283
+ };
284
+ const evaluation = ctx.executionPolicy.evaluate(toolCall, policyContext);
285
+ // Emit policy event
286
+ ctx.emit({
287
+ type: 'policy.evaluated',
288
+ tool: toolCall.name,
289
+ policy: evaluation.policy,
290
+ reason: evaluation.reason,
291
+ });
292
+ // Emit decision transparency event
293
+ ctx.emit({
294
+ type: 'decision.tool',
295
+ tool: toolCall.name,
296
+ decision: evaluation.policy === 'forbidden' ? 'blocked'
297
+ : evaluation.policy === 'prompt' ? 'prompted'
298
+ : 'allowed',
299
+ policyMatch: evaluation.reason,
300
+ });
301
+ // Enhanced tracing: Record policy decision
302
+ ctx.traceCollector?.record({
303
+ type: 'decision',
304
+ data: {
305
+ type: 'policy',
306
+ decision: `Tool ${toolCall.name}: ${evaluation.policy}`,
307
+ outcome: evaluation.policy === 'forbidden' ? 'blocked'
308
+ : evaluation.policy === 'prompt' ? 'deferred'
309
+ : 'allowed',
310
+ reasoning: evaluation.reason,
311
+ factors: [
312
+ { name: 'policy', value: evaluation.policy },
313
+ { name: 'requiresApproval', value: evaluation.requiresApproval ?? false },
314
+ ],
315
+ confidence: evaluation.intent?.confidence ?? 0.8,
316
+ },
317
+ });
318
+ // Handle forbidden policy - always block
319
+ if (evaluation.policy === 'forbidden') {
320
+ ctx.emit({
321
+ type: 'policy.tool.blocked',
322
+ tool: toolCall.name,
323
+ phase: 'enforced',
324
+ reason: `Forbidden by execution policy: ${evaluation.reason}`,
325
+ });
326
+ throw new Error(`Forbidden by policy: ${evaluation.reason}`);
327
+ }
328
+ // Handle prompt policy - requires approval
329
+ if (evaluation.policy === 'prompt' && evaluation.requiresApproval) {
330
+ const humanInLoop = ctx.safety?.humanInLoop;
331
+ if (humanInLoop) {
332
+ const approval = await withPausedDuration(ctx, () => humanInLoop.requestApproval(toolCall, `Policy requires approval: ${evaluation.reason}`));
333
+ if (!approval.approved) {
334
+ throw new Error(`Denied by user: ${approval.reason || 'No reason provided'}`);
335
+ }
336
+ policyApprovedByUser = true;
337
+ // Create a grant for future similar calls if approved
338
+ ctx.executionPolicy.createGrant({
339
+ toolName: toolCall.name,
340
+ grantedBy: 'user',
341
+ reason: 'Approved during execution',
342
+ maxUsages: 5,
343
+ });
344
+ }
345
+ else {
346
+ // No approval handler — auto-allow with warning (defense-in-depth)
347
+ ctx.emit({
348
+ type: 'policy.tool.auto-allowed',
349
+ tool: toolCall.name,
350
+ reason: `No approval handler — auto-allowing (policy: ${evaluation.reason})`,
351
+ });
352
+ policyApprovedByUser = true;
353
+ }
354
+ }
355
+ // Log intent classification if available
356
+ if (evaluation.intent) {
357
+ ctx.emit({
358
+ type: 'intent.classified',
359
+ tool: toolCall.name,
360
+ intent: evaluation.intent.type,
361
+ confidence: evaluation.intent.confidence,
362
+ });
363
+ }
364
+ }
365
+ // =====================================================================
366
+ // SAFETY VALIDATION (Lesson 20-21)
367
+ // =====================================================================
368
+ if (ctx.safety) {
369
+ const safety = ctx.safety;
370
+ const validation = await withPausedDuration(ctx, () => safety.validateAndApprove(toolCall, `Executing tool: ${toolCall.name}`, { skipHumanApproval: policyApprovedByUser }));
371
+ if (!validation.allowed) {
372
+ ctx.emit({
373
+ type: 'policy.tool.blocked',
374
+ tool: toolCall.name,
375
+ phase: 'enforced',
376
+ reason: validation.reason || 'Blocked by safety manager',
377
+ });
378
+ if (toolCall.name === 'bash') {
379
+ const args = toolCall.arguments;
380
+ ctx.emit({
381
+ type: 'policy.bash.blocked',
382
+ phase: 'enforced',
383
+ command: String(args.command || args.cmd || ''),
384
+ reason: validation.reason || 'Blocked by safety manager',
385
+ });
386
+ }
387
+ throw new Error(`Tool call blocked: ${validation.reason}`);
388
+ }
389
+ }
390
+ // Get tool definition (with lazy-loading support for MCP tools)
391
+ let tool = ctx.tools.get(toolCall.name);
392
+ const wasPreloaded = !!tool;
393
+ if (!tool && ctx.toolResolver) {
394
+ const resolved = ctx.toolResolver(toolCall.name);
395
+ if (resolved) {
396
+ ctx.addTool(resolved);
397
+ tool = resolved;
398
+ if (process.env.DEBUG)
399
+ log.debug('Auto-loaded MCP tool', { tool: toolCall.name });
400
+ ctx.observability?.logger?.info('Tool auto-loaded', { tool: toolCall.name });
401
+ }
402
+ }
403
+ if (!tool) {
404
+ throw new Error(`Unknown tool: ${toolCall.name}`);
405
+ }
406
+ if (process.env.DEBUG && toolCall.name.startsWith('mcp_') && wasPreloaded) {
407
+ log.debug('Using pre-loaded MCP tool', { tool: toolCall.name });
408
+ }
409
+ // =====================================================================
410
+ // BLACKBOARD FILE COORDINATION (Parallel Subagent Support)
411
+ // =====================================================================
412
+ if (ctx.blackboard && (toolCall.name === 'write_file' || toolCall.name === 'edit_file')) {
413
+ const args = toolCall.arguments;
414
+ const filePath = String(args.path || args.file_path || '');
415
+ if (filePath) {
416
+ const agentId = ctx.agentId;
417
+ const claimed = ctx.blackboard.claim(filePath, agentId, 'write', {
418
+ ttl: 60000,
419
+ intent: `${toolCall.name}: ${filePath}`,
420
+ });
421
+ if (!claimed) {
422
+ const existingClaim = ctx.blackboard.getClaim(filePath);
423
+ throw new Error(`File "${filePath}" is being edited by another agent (${existingClaim?.agentId || 'unknown'}). ` +
424
+ `Wait for the other agent to complete or choose a different file.`);
425
+ }
426
+ }
427
+ }
428
+ // FILE CACHE: Check cache for read_file operations before executing
429
+ if (ctx.fileCache && toolCall.name === 'read_file') {
430
+ const args = toolCall.arguments;
431
+ const readPath = String(args.path || '');
432
+ if (readPath) {
433
+ const cached = ctx.fileCache.get(readPath);
434
+ if (cached !== undefined) {
435
+ const lines = cached.split('\n').length;
436
+ const cacheResult = { success: true, output: cached, metadata: { lines, bytes: cached.length, cached: true } };
437
+ const duration = Date.now() - startTime;
438
+ ctx.traceCollector?.record({ type: 'tool.end', data: { executionId, status: 'success', result: cacheResult, durationMs: duration } });
439
+ ctx.observability?.metrics?.recordToolCall(toolCall.name, duration, true);
440
+ ctx.state.metrics.toolCalls++;
441
+ ctx.emit({ type: 'tool.complete', tool: toolCall.name, result: cacheResult });
442
+ ctx.observability?.tracer?.endSpan(spanId);
443
+ return {
444
+ callId: toolCall.id,
445
+ result: typeof cacheResult === 'string' ? cacheResult : JSON.stringify(cacheResult),
446
+ };
447
+ }
448
+ }
449
+ }
450
+ // Execute tool (with sandbox if available)
451
+ let result;
452
+ if (ctx.safety?.sandbox) {
453
+ const isSpawnAgent = toolCall.name === 'spawn_agent';
454
+ const isSpawnParallel = toolCall.name === 'spawn_agents_parallel';
455
+ const isSubagentTool = isSpawnAgent || isSpawnParallel;
456
+ const subagentConfig = ctx.config.subagent;
457
+ const hasSubagentConfig = subagentConfig !== false && subagentConfig !== undefined;
458
+ const subagentTimeout = hasSubagentConfig
459
+ ? subagentConfig.defaultTimeout ?? 600000
460
+ : 600000;
461
+ const toolTimeout = isSubagentTool ? subagentTimeout + 30000 : undefined;
462
+ result = await ctx.safety.sandbox.executeWithLimits(() => tool.execute(toolCall.arguments), toolTimeout);
463
+ }
464
+ else {
465
+ result = await tool.execute(toolCall.arguments);
466
+ }
467
+ const duration = Date.now() - startTime;
468
+ // Record tool completion for tracing
469
+ ctx.traceCollector?.record({
470
+ type: 'tool.end',
471
+ data: {
472
+ executionId,
473
+ status: 'success',
474
+ result,
475
+ durationMs: duration,
476
+ },
477
+ });
478
+ // Record metrics
479
+ ctx.observability?.metrics?.recordToolCall(toolCall.name, duration, true);
480
+ ctx.state.metrics.toolCalls++;
481
+ ctx.emit({ type: 'tool.complete', tool: toolCall.name, result });
482
+ // FILE CACHE: Store read results and invalidate on writes
483
+ if (ctx.fileCache) {
484
+ const args = toolCall.arguments;
485
+ const filePath = String(args.path || args.file_path || '');
486
+ if (toolCall.name === 'read_file' && filePath) {
487
+ const resultObj = result;
488
+ if (resultObj?.success && typeof resultObj.output === 'string') {
489
+ ctx.fileCache.set(filePath, resultObj.output);
490
+ }
491
+ }
492
+ else if ((toolCall.name === 'write_file' || toolCall.name === 'edit_file' || toolCall.name === 'undo_file_change') && filePath) {
493
+ ctx.fileCache.invalidate(filePath);
494
+ }
495
+ }
496
+ // Emit tool insight with result summary
497
+ const summary = summarizeToolResult(toolCall.name, result);
498
+ ctx.emit({
499
+ type: 'insight.tool',
500
+ tool: toolCall.name,
501
+ summary,
502
+ durationMs: duration,
503
+ success: true,
504
+ });
505
+ // Release blackboard claim after successful file write
506
+ if (ctx.blackboard && (toolCall.name === 'write_file' || toolCall.name === 'edit_file')) {
507
+ const args = toolCall.arguments;
508
+ const filePath = String(args.path || args.file_path || '');
509
+ if (filePath) {
510
+ const agentId = ctx.agentId;
511
+ ctx.blackboard.release(filePath, agentId);
512
+ }
513
+ }
514
+ // Self-improvement: record success pattern
515
+ ctx.selfImprovement?.recordSuccess(toolCall.name, toolCall.arguments, typeof result === 'string' ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200));
516
+ ctx.observability?.tracer?.endSpan(spanId);
517
+ return { callId: toolCall.id, result };
518
+ }
519
+ catch (err) {
520
+ const error = err instanceof Error ? err : new Error(String(err));
521
+ const duration = Date.now() - startTime;
522
+ // Record tool error for tracing
523
+ ctx.traceCollector?.record({
524
+ type: 'tool.end',
525
+ data: {
526
+ executionId,
527
+ status: error.message.includes('Blocked') || error.message.includes('Policy') ? 'blocked' : 'error',
528
+ error,
529
+ durationMs: duration,
530
+ },
531
+ });
532
+ ctx.observability?.metrics?.recordToolCall(toolCall.name, duration, false);
533
+ ctx.observability?.tracer?.recordError(error);
534
+ ctx.observability?.tracer?.endSpan(spanId);
535
+ // FAILURE EVIDENCE RECORDING (Trick S)
536
+ ctx.contextEngineering?.recordFailure({
537
+ action: toolCall.name,
538
+ args: toolCall.arguments,
539
+ error,
540
+ intent: `Execute tool ${toolCall.name}`,
541
+ });
542
+ // FILE CACHE INVALIDATION ON FAILURE — ensure stale cache doesn't cause repeated failures
543
+ if (ctx.fileCache && ['write_file', 'edit_file'].includes(toolCall.name)) {
544
+ const args = toolCall.arguments;
545
+ const filePath = String(args.path || args.file_path || '');
546
+ if (filePath) {
547
+ ctx.fileCache.invalidate(filePath);
548
+ }
549
+ }
550
+ // Self-improvement: enhance error message with diagnosis
551
+ if (ctx.selfImprovement) {
552
+ const enhanced = ctx.selfImprovement.enhanceErrorMessage(toolCall.name, error.message, toolCall.arguments);
553
+ ctx.emit({ type: 'tool.blocked', tool: toolCall.name, reason: enhanced });
554
+ return { callId: toolCall.id, result: `Error: ${enhanced}`, error: enhanced };
555
+ }
556
+ ctx.emit({ type: 'tool.blocked', tool: toolCall.name, reason: error.message });
557
+ return { callId: toolCall.id, result: `Error: ${error.message}`, error: error.message };
558
+ }
559
+ }
560
+ // =============================================================================
561
+ // HELPER FUNCTIONS (extracted from ProductionAgent private methods)
562
+ // =============================================================================
563
+ /**
564
+ * Execute an async callback while excluding wall-clock wait time from duration budgeting.
565
+ */
566
+ async function withPausedDuration(ctx, fn) {
567
+ ctx.economics?.pauseDuration();
568
+ try {
569
+ return await fn();
570
+ }
571
+ finally {
572
+ ctx.economics?.resumeDuration();
573
+ }
574
+ }
575
+ /**
576
+ * Create a brief summary of a tool result for insight display.
577
+ */
578
+ export function summarizeToolResult(toolName, result) {
579
+ if (result === null || result === undefined) {
580
+ return 'No output';
581
+ }
582
+ const resultStr = typeof result === 'string' ? result : JSON.stringify(result);
583
+ if (toolName === 'list_files' || toolName === 'glob') {
584
+ const lines = resultStr.split('\n').filter(l => l.trim());
585
+ return `Found ${lines.length} file${lines.length !== 1 ? 's' : ''}`;
586
+ }
587
+ if (toolName === 'bash' || toolName === 'execute_command') {
588
+ const lines = resultStr.split('\n').filter(l => l.trim());
589
+ if (resultStr.includes('exit code: 0') || !resultStr.includes('exit code:')) {
590
+ return lines.length > 1 ? `Success (${lines.length} lines)` : 'Success';
591
+ }
592
+ return `Failed - ${lines[0]?.slice(0, 50) || 'see output'}`;
593
+ }
594
+ if (toolName === 'read_file') {
595
+ const lines = resultStr.split('\n').length;
596
+ return `Read ${lines} line${lines !== 1 ? 's' : ''}`;
597
+ }
598
+ if (toolName === 'write_file' || toolName === 'edit_file') {
599
+ return 'File updated';
600
+ }
601
+ if (toolName === 'search' || toolName === 'grep') {
602
+ const matches = (resultStr.match(/\n/g) || []).length;
603
+ return `${matches} match${matches !== 1 ? 'es' : ''}`;
604
+ }
605
+ if (resultStr.length <= 50) {
606
+ return resultStr;
607
+ }
608
+ return `${resultStr.slice(0, 47)}...`;
609
+ }
610
+ /**
611
+ * Format tool arguments for plan display.
612
+ */
613
+ export function formatToolArgsForPlan(toolName, args) {
614
+ if (toolName === 'write_file') {
615
+ const path = args.path || args.file_path;
616
+ const content = String(args.content || '');
617
+ const preview = content.slice(0, 100).replace(/\n/g, '\\n');
618
+ return `File: ${path}\nContent preview: ${preview}${content.length > 100 ? '...' : ''}`;
619
+ }
620
+ if (toolName === 'edit_file') {
621
+ const path = args.path || args.file_path;
622
+ return `File: ${path}\nOld: ${String(args.old_string || args.search || '').slice(0, 50)}...\nNew: ${String(args.new_string || args.replace || '').slice(0, 50)}...`;
623
+ }
624
+ if (toolName === 'bash') {
625
+ return `Command: ${String(args.command || '').slice(0, 100)}`;
626
+ }
627
+ if (toolName === 'delete_file') {
628
+ return `Delete: ${args.path || args.file_path}`;
629
+ }
630
+ if (toolName === 'spawn_agent' || toolName === 'researcher') {
631
+ const task = String(args.task || args.prompt || args.goal || '');
632
+ const model = args.model ? ` (${args.model})` : '';
633
+ const firstLine = task.split('\n')[0].slice(0, 100);
634
+ return `${firstLine}${task.length > 100 ? '...' : ''}${model}`;
635
+ }
636
+ return `Args: ${JSON.stringify(args).slice(0, 100)}...`;
637
+ }
638
+ /**
639
+ * Extract contextual reasoning for a proposed change in plan mode.
640
+ */
641
+ export function extractChangeReasoning(toolCall, messages) {
642
+ const assistantMsgs = messages
643
+ .filter(m => m.role === 'assistant' && typeof m.content === 'string')
644
+ .slice(-3)
645
+ .reverse();
646
+ if (assistantMsgs.length === 0) {
647
+ return `Proposed change: ${toolCall.name}`;
648
+ }
649
+ const lastMsg = assistantMsgs[0];
650
+ const content = lastMsg.content;
651
+ if (toolCall.name === 'spawn_agent') {
652
+ const args = toolCall.arguments;
653
+ const task = String(args.task || args.prompt || args.goal || '');
654
+ if (task.length > 0) {
655
+ const firstPara = task.split(/\n\n/)[0];
656
+ return firstPara.length > 500 ? firstPara.slice(0, 500) + '...' : firstPara;
657
+ }
658
+ }
659
+ if (['write_file', 'edit_file'].includes(toolCall.name)) {
660
+ const args = toolCall.arguments;
661
+ const path = String(args.path || args.file_path || '');
662
+ if (path && content.toLowerCase().includes(path.toLowerCase().split('/').pop() || '')) {
663
+ const sentences = content.split(/[.!?\n]+/).filter(s => s.toLowerCase().includes(path.toLowerCase().split('/').pop() || ''));
664
+ if (sentences.length > 0) {
665
+ const relevant = sentences.slice(0, 2).join('. ').trim();
666
+ return relevant.length > 500 ? relevant.slice(0, 500) + '...' : relevant;
667
+ }
668
+ }
669
+ }
670
+ const paragraphs = content.split(/\n\n+/).filter(p => p.trim().length > 20);
671
+ if (paragraphs.length > 0) {
672
+ const firstPara = paragraphs[0].trim();
673
+ return firstPara.length > 500 ? firstPara.slice(0, 500) + '...' : firstPara;
674
+ }
675
+ return content.length > 500 ? content.slice(0, 500) + '...' : content;
676
+ }
677
+ //# sourceMappingURL=tool-executor.js.map