@xagent-ai/cli 1.2.2 → 1.3.1

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 (602) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  3. package/.github/release.yml +76 -0
  4. package/.github/workflows/ci.yml +75 -0
  5. package/.github/workflows/release.yml +103 -0
  6. package/.gitmodules +3 -3
  7. package/README.md +326 -280
  8. package/README_CN.md +325 -279
  9. package/dist/agents.d.ts.map +1 -1
  10. package/dist/agents.js +7 -3
  11. package/dist/agents.js.map +1 -1
  12. package/dist/ai-client/factory.d.ts +40 -0
  13. package/dist/ai-client/factory.d.ts.map +1 -0
  14. package/dist/ai-client/factory.js +100 -0
  15. package/dist/ai-client/factory.js.map +1 -0
  16. package/dist/ai-client/index.d.ts +20 -0
  17. package/dist/ai-client/index.d.ts.map +1 -0
  18. package/dist/ai-client/index.js +49 -0
  19. package/dist/ai-client/index.js.map +1 -0
  20. package/dist/ai-client/providers/anthropic.d.ts +57 -0
  21. package/dist/ai-client/providers/anthropic.d.ts.map +1 -0
  22. package/dist/ai-client/providers/anthropic.js +406 -0
  23. package/dist/ai-client/providers/anthropic.js.map +1 -0
  24. package/dist/ai-client/providers/openai.d.ts +57 -0
  25. package/dist/ai-client/providers/openai.d.ts.map +1 -0
  26. package/dist/ai-client/providers/openai.js +290 -0
  27. package/dist/ai-client/providers/openai.js.map +1 -0
  28. package/dist/ai-client/providers/remote.d.ts +110 -0
  29. package/dist/ai-client/providers/remote.d.ts.map +1 -0
  30. package/dist/ai-client/providers/remote.js +352 -0
  31. package/dist/ai-client/providers/remote.js.map +1 -0
  32. package/dist/ai-client/registry.d.ts +51 -0
  33. package/dist/ai-client/registry.d.ts.map +1 -0
  34. package/dist/ai-client/registry.js +81 -0
  35. package/dist/ai-client/registry.js.map +1 -0
  36. package/dist/ai-client/types.d.ts +274 -0
  37. package/dist/ai-client/types.d.ts.map +1 -0
  38. package/dist/ai-client/types.js +90 -0
  39. package/dist/ai-client/types.js.map +1 -0
  40. package/dist/ai-client-factory.d.ts +62 -0
  41. package/dist/ai-client-factory.d.ts.map +1 -0
  42. package/dist/ai-client-factory.js +157 -0
  43. package/dist/ai-client-factory.js.map +1 -0
  44. package/dist/auth.d.ts +23 -1
  45. package/dist/auth.d.ts.map +1 -1
  46. package/dist/auth.js +164 -174
  47. package/dist/auth.js.map +1 -1
  48. package/dist/cancellation.d.ts +5 -4
  49. package/dist/cancellation.d.ts.map +1 -1
  50. package/dist/cancellation.js +53 -32
  51. package/dist/cancellation.js.map +1 -1
  52. package/dist/checkpoint.d.ts +2 -1
  53. package/dist/checkpoint.d.ts.map +1 -1
  54. package/dist/checkpoint.js +39 -6
  55. package/dist/checkpoint.js.map +1 -1
  56. package/dist/cli.js +742 -29
  57. package/dist/cli.js.map +1 -1
  58. package/dist/config.d.ts +10 -4
  59. package/dist/config.d.ts.map +1 -1
  60. package/dist/config.js +62 -25
  61. package/dist/config.js.map +1 -1
  62. package/dist/context-compressor.d.ts +82 -18
  63. package/dist/context-compressor.d.ts.map +1 -1
  64. package/dist/context-compressor.js +718 -154
  65. package/dist/context-compressor.js.map +1 -1
  66. package/dist/conversation.d.ts +1 -1
  67. package/dist/conversation.d.ts.map +1 -1
  68. package/dist/conversation.js +8 -7
  69. package/dist/conversation.js.map +1 -1
  70. package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -1
  71. package/dist/gui-subagent/action-parser/actionParser.js +6 -4
  72. package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
  73. package/dist/gui-subagent/agent/gui-agent.d.ts +39 -2
  74. package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
  75. package/dist/gui-subagent/agent/gui-agent.js +189 -74
  76. package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
  77. package/dist/gui-subagent/index.d.ts +23 -1
  78. package/dist/gui-subagent/index.d.ts.map +1 -1
  79. package/dist/gui-subagent/index.js +6 -0
  80. package/dist/gui-subagent/index.js.map +1 -1
  81. package/dist/gui-subagent/operator/base-operator.d.ts.map +1 -1
  82. package/dist/gui-subagent/operator/base-operator.js +0 -1
  83. package/dist/gui-subagent/operator/base-operator.js.map +1 -1
  84. package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
  85. package/dist/gui-subagent/operator/computer-operator.js +31 -8
  86. package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
  87. package/dist/gui-subagent/types/actions.d.ts +1 -1
  88. package/dist/gui-subagent/types/actions.d.ts.map +1 -1
  89. package/dist/gui-subagent/types/actions.js +0 -1
  90. package/dist/gui-subagent/types/actions.js.map +1 -1
  91. package/dist/gui-subagent/types/operator.d.ts +1 -1
  92. package/dist/gui-subagent/types/operator.d.ts.map +1 -1
  93. package/dist/index.d.ts +1 -2
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +1 -2
  96. package/dist/index.js.map +1 -1
  97. package/dist/input-processor.d.ts.map +1 -1
  98. package/dist/input-processor.js +8 -5
  99. package/dist/input-processor.js.map +1 -1
  100. package/dist/logger.d.ts.map +1 -1
  101. package/dist/logger.js +1 -1
  102. package/dist/logger.js.map +1 -1
  103. package/dist/mcp.d.ts +7 -1
  104. package/dist/mcp.d.ts.map +1 -1
  105. package/dist/mcp.js +157 -49
  106. package/dist/mcp.js.map +1 -1
  107. package/dist/memory.d.ts.map +1 -1
  108. package/dist/memory.js +3 -3
  109. package/dist/memory.js.map +1 -1
  110. package/dist/output-util.d.ts +27 -0
  111. package/dist/output-util.d.ts.map +1 -0
  112. package/dist/output-util.js +74 -0
  113. package/dist/output-util.js.map +1 -0
  114. package/dist/retry.js +1 -1
  115. package/dist/retry.js.map +1 -1
  116. package/dist/ripgrep.d.ts +29 -0
  117. package/dist/ripgrep.d.ts.map +1 -0
  118. package/dist/ripgrep.js +294 -0
  119. package/dist/ripgrep.js.map +1 -0
  120. package/dist/sdk-output-adapter.d.ts +34 -1
  121. package/dist/sdk-output-adapter.d.ts.map +1 -1
  122. package/dist/sdk-output-adapter.js +67 -2
  123. package/dist/sdk-output-adapter.js.map +1 -1
  124. package/dist/sdk-session.d.ts.map +1 -1
  125. package/dist/sdk-session.js +2 -0
  126. package/dist/sdk-session.js.map +1 -1
  127. package/dist/session-manager.js +3 -3
  128. package/dist/session-manager.js.map +1 -1
  129. package/dist/session.d.ts +116 -6
  130. package/dist/session.d.ts.map +1 -1
  131. package/dist/session.js +1416 -448
  132. package/dist/session.js.map +1 -1
  133. package/dist/shell.d.ts +33 -0
  134. package/dist/shell.d.ts.map +1 -0
  135. package/dist/shell.js +126 -0
  136. package/dist/shell.js.map +1 -0
  137. package/dist/skill-installer.d.ts +38 -0
  138. package/dist/skill-installer.d.ts.map +1 -0
  139. package/dist/skill-installer.js +447 -0
  140. package/dist/skill-installer.js.map +1 -0
  141. package/dist/skill-invoker.d.ts +8 -2
  142. package/dist/skill-invoker.d.ts.map +1 -1
  143. package/dist/skill-invoker.js +36 -15
  144. package/dist/skill-invoker.js.map +1 -1
  145. package/dist/skill-loader.d.ts +8 -3
  146. package/dist/skill-loader.d.ts.map +1 -1
  147. package/dist/skill-loader.js +51 -48
  148. package/dist/skill-loader.js.map +1 -1
  149. package/dist/skill-manager.d.ts +85 -0
  150. package/dist/skill-manager.d.ts.map +1 -0
  151. package/dist/skill-manager.js +341 -0
  152. package/dist/skill-manager.js.map +1 -0
  153. package/dist/slash-commands.d.ts +39 -2
  154. package/dist/slash-commands.d.ts.map +1 -1
  155. package/dist/slash-commands.js +934 -305
  156. package/dist/slash-commands.js.map +1 -1
  157. package/dist/smart-approval.d.ts +20 -1
  158. package/dist/smart-approval.d.ts.map +1 -1
  159. package/dist/smart-approval.js +125 -56
  160. package/dist/smart-approval.js.map +1 -1
  161. package/dist/system-prompt-generator.d.ts +6 -0
  162. package/dist/system-prompt-generator.d.ts.map +1 -1
  163. package/dist/system-prompt-generator.js +86 -36
  164. package/dist/system-prompt-generator.js.map +1 -1
  165. package/dist/terminal.d.ts +28 -0
  166. package/dist/terminal.d.ts.map +1 -0
  167. package/dist/terminal.js +82 -0
  168. package/dist/terminal.js.map +1 -0
  169. package/dist/theme.d.ts.map +1 -1
  170. package/dist/theme.js +8 -7
  171. package/dist/theme.js.map +1 -1
  172. package/dist/tools.d.ts +38 -7
  173. package/dist/tools.d.ts.map +1 -1
  174. package/dist/tools.js +1249 -617
  175. package/dist/tools.js.map +1 -1
  176. package/dist/truncate.d.ts +55 -0
  177. package/dist/truncate.d.ts.map +1 -0
  178. package/dist/truncate.js +130 -0
  179. package/dist/truncate.js.map +1 -0
  180. package/dist/types.d.ts +84 -9
  181. package/dist/types.d.ts.map +1 -1
  182. package/dist/types.js +49 -0
  183. package/dist/types.js.map +1 -1
  184. package/dist/update.d.ts.map +1 -1
  185. package/dist/update.js +28 -36
  186. package/dist/update.js.map +1 -1
  187. package/dist/workflow.d.ts +5 -1
  188. package/dist/workflow.d.ts.map +1 -1
  189. package/dist/workflow.js +61 -49
  190. package/dist/workflow.js.map +1 -1
  191. package/docs/architecture/mcp-integration-guide.md +304 -194
  192. package/docs/architecture/overview.md +169 -169
  193. package/docs/architecture/tool-system-design.md +134 -134
  194. package/docs/cli/commands.md +349 -238
  195. package/docs/smart-mode.md +281 -281
  196. package/docs/third-party-models.md +440 -439
  197. package/find-skills/SKILL.md +133 -0
  198. package/package.json +91 -90
  199. package/scripts/install-ripgrep.js +241 -0
  200. package/src/agents.ts +7 -3
  201. package/src/ai-client/factory.ts +116 -0
  202. package/src/ai-client/index.ts +61 -0
  203. package/src/ai-client/providers/anthropic.ts +475 -0
  204. package/src/ai-client/providers/openai.ts +348 -0
  205. package/src/ai-client/providers/remote.ts +439 -0
  206. package/src/ai-client/registry.ts +97 -0
  207. package/src/ai-client/types.ts +364 -0
  208. package/src/ai-client-factory.ts +204 -0
  209. package/src/auth.ts +661 -614
  210. package/src/cancellation.ts +202 -176
  211. package/src/checkpoint.ts +255 -219
  212. package/src/cli.ts +1523 -743
  213. package/src/config.ts +341 -297
  214. package/src/context-compressor.ts +987 -290
  215. package/src/conversation.ts +290 -288
  216. package/src/gui-subagent/action-parser/actionParser.ts +318 -315
  217. package/src/gui-subagent/action-parser/constants.ts +14 -14
  218. package/src/gui-subagent/action-parser/index.ts +8 -8
  219. package/src/gui-subagent/action-parser/types.ts +31 -31
  220. package/src/gui-subagent/agent/gui-agent.ts +1234 -1089
  221. package/src/gui-subagent/agent/index.ts +5 -5
  222. package/src/gui-subagent/index.ts +185 -163
  223. package/src/gui-subagent/operator/base-operator.ts +244 -245
  224. package/src/gui-subagent/operator/computer-operator.ts +541 -520
  225. package/src/gui-subagent/operator/index.ts +6 -6
  226. package/src/gui-subagent/types/actions.ts +260 -262
  227. package/src/gui-subagent/types/index.ts +6 -6
  228. package/src/gui-subagent/types/operator.ts +106 -106
  229. package/src/gui-subagent/utils.ts +51 -51
  230. package/src/index.ts +17 -18
  231. package/src/input-processor.ts +8 -5
  232. package/src/logger.ts +436 -438
  233. package/src/mcp.ts +793 -682
  234. package/src/memory.ts +343 -344
  235. package/src/output-util.ts +80 -0
  236. package/src/retry.ts +1 -1
  237. package/src/ripgrep.ts +370 -0
  238. package/src/sdk-output-adapter.ts +842 -0
  239. package/src/sdk-session.ts +62 -0
  240. package/src/session-manager.ts +308 -308
  241. package/src/session.ts +1775 -573
  242. package/src/shell.ts +134 -0
  243. package/src/skill-installer.ts +518 -0
  244. package/src/skill-invoker.ts +959 -935
  245. package/src/skill-loader.ts +501 -496
  246. package/src/skill-manager.ts +385 -0
  247. package/src/slash-commands.ts +2189 -1389
  248. package/src/smart-approval.ts +193 -74
  249. package/src/system-prompt-generator.ts +91 -36
  250. package/src/terminal.ts +96 -0
  251. package/src/theme.ts +739 -738
  252. package/src/tools.ts +1790 -931
  253. package/src/truncate.ts +173 -0
  254. package/src/types.ts +337 -198
  255. package/src/update.ts +33 -40
  256. package/src/workflow.ts +521 -508
  257. package/test/cli-launch.test.ts +279 -0
  258. package/tsconfig.json +22 -22
  259. package/vitest.config.ts +21 -19
  260. package/dist/ai-client.d.ts +0 -86
  261. package/dist/ai-client.d.ts.map +0 -1
  262. package/dist/ai-client.js +0 -1372
  263. package/dist/ai-client.js.map +0 -1
  264. package/dist/gui-subagent/operator/browser-operator.d.ts +0 -36
  265. package/dist/gui-subagent/operator/browser-operator.d.ts.map +0 -1
  266. package/dist/gui-subagent/operator/browser-operator.js +0 -306
  267. package/dist/gui-subagent/operator/browser-operator.js.map +0 -1
  268. package/dist/gui-subagent/operator/desktop-operator.d.ts +0 -55
  269. package/dist/gui-subagent/operator/desktop-operator.d.ts.map +0 -1
  270. package/dist/gui-subagent/operator/desktop-operator.js +0 -527
  271. package/dist/gui-subagent/operator/desktop-operator.js.map +0 -1
  272. package/dist/hook.d.ts +0 -73
  273. package/dist/hook.d.ts.map +0 -1
  274. package/dist/hook.js +0 -156
  275. package/dist/hook.js.map +0 -1
  276. package/dist/input-history.d.ts +0 -24
  277. package/dist/input-history.d.ts.map +0 -1
  278. package/dist/input-history.js +0 -94
  279. package/dist/input-history.js.map +0 -1
  280. package/dist/keyboard-manager.d.ts +0 -151
  281. package/dist/keyboard-manager.d.ts.map +0 -1
  282. package/dist/keyboard-manager.js +0 -396
  283. package/dist/keyboard-manager.js.map +0 -1
  284. package/dist/print-system-prompt.d.ts +0 -2
  285. package/dist/print-system-prompt.d.ts.map +0 -1
  286. package/dist/print-system-prompt.js +0 -40
  287. package/dist/print-system-prompt.js.map +0 -1
  288. package/dist/remote-ai-client.d.ts +0 -104
  289. package/dist/remote-ai-client.d.ts.map +0 -1
  290. package/dist/remote-ai-client.js +0 -552
  291. package/dist/remote-ai-client.js.map +0 -1
  292. package/dist/sdk-session-v2.d.ts +0 -13
  293. package/dist/sdk-session-v2.d.ts.map +0 -1
  294. package/dist/sdk-session-v2.js +0 -46
  295. package/dist/sdk-session-v2.js.map +0 -1
  296. package/dist/test-boundary-conditions.d.ts.map +0 -1
  297. package/dist/test-boundary-conditions.js.map +0 -1
  298. package/dist/test-cancellation-fix.d.ts.map +0 -1
  299. package/dist/test-cancellation-fix.js.map +0 -1
  300. package/dist/test-input-history.d.ts.map +0 -1
  301. package/dist/test-input-history.js.map +0 -1
  302. package/dist/test-interaction-flow.d.ts.map +0 -1
  303. package/dist/test-interaction-flow.js.map +0 -1
  304. package/dist/test-quick.d.ts.map +0 -1
  305. package/dist/test-quick.js.map +0 -1
  306. package/dist/test-user-interaction.d.ts.map +0 -1
  307. package/dist/test-user-interaction.js.map +0 -1
  308. package/dist/tools/edit-diff.d.ts +0 -32
  309. package/dist/tools/edit-diff.d.ts.map +0 -1
  310. package/dist/tools/edit-diff.js +0 -185
  311. package/dist/tools/edit-diff.js.map +0 -1
  312. package/dist/tools/edit.d.ts +0 -11
  313. package/dist/tools/edit.d.ts.map +0 -1
  314. package/dist/tools/edit.js +0 -129
  315. package/dist/tools/edit.js.map +0 -1
  316. package/dist/unified-session.d.ts +0 -42
  317. package/dist/unified-session.d.ts.map +0 -1
  318. package/dist/unified-session.js +0 -271
  319. package/dist/unified-session.js.map +0 -1
  320. package/skills/.claude-plugin/marketplace.json +0 -45
  321. package/skills/README.md +0 -94
  322. package/skills/THIRD_PARTY_NOTICES.md +0 -405
  323. package/skills/skills/algorithmic-art/LICENSE.txt +0 -202
  324. package/skills/skills/algorithmic-art/SKILL.md +0 -405
  325. package/skills/skills/algorithmic-art/templates/generator_template.js +0 -223
  326. package/skills/skills/algorithmic-art/templates/viewer.html +0 -599
  327. package/skills/skills/brand-guidelines/LICENSE.txt +0 -202
  328. package/skills/skills/brand-guidelines/SKILL.md +0 -73
  329. package/skills/skills/canvas-design/LICENSE.txt +0 -202
  330. package/skills/skills/canvas-design/SKILL.md +0 -130
  331. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
  332. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  333. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  334. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
  335. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  336. package/skills/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
  337. package/skills/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  338. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  339. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
  340. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  341. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  342. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  343. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
  344. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  345. package/skills/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
  346. package/skills/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  347. package/skills/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
  348. package/skills/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  349. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  350. package/skills/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
  351. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  352. package/skills/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
  353. package/skills/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  354. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  355. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
  356. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  357. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  358. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  359. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  360. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  361. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  362. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  363. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  364. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
  365. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  366. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  367. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  368. package/skills/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
  369. package/skills/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  370. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  371. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
  372. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  373. package/skills/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  374. package/skills/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  375. package/skills/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
  376. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
  377. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  378. package/skills/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  379. package/skills/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  380. package/skills/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  381. package/skills/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
  382. package/skills/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  383. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  384. package/skills/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
  385. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  386. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
  387. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  388. package/skills/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  389. package/skills/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
  390. package/skills/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  391. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  392. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
  393. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
  394. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  395. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  396. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
  397. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  398. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
  399. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  400. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  401. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
  402. package/skills/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  403. package/skills/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
  404. package/skills/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  405. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  406. package/skills/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  407. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  408. package/skills/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
  409. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  410. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
  411. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  412. package/skills/skills/doc-coauthoring/SKILL.md +0 -375
  413. package/skills/skills/docx/LICENSE.txt +0 -30
  414. package/skills/skills/docx/SKILL.md +0 -197
  415. package/skills/skills/docx/docx-js.md +0 -350
  416. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  417. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  418. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  419. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  420. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  421. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  422. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  423. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  424. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  425. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  426. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  427. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  428. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  429. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  430. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  431. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  432. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  433. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  434. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  435. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  436. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  437. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  438. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  439. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  440. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  441. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  442. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  443. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  444. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  445. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  446. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  447. package/skills/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  448. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  449. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  450. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  451. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  452. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  453. package/skills/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  454. package/skills/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  455. package/skills/skills/docx/ooxml/scripts/pack.py +0 -159
  456. package/skills/skills/docx/ooxml/scripts/unpack.py +0 -29
  457. package/skills/skills/docx/ooxml/scripts/validate.py +0 -69
  458. package/skills/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  459. package/skills/skills/docx/ooxml/scripts/validation/base.py +0 -951
  460. package/skills/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  461. package/skills/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  462. package/skills/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  463. package/skills/skills/docx/ooxml.md +0 -610
  464. package/skills/skills/docx/scripts/__init__.py +0 -1
  465. package/skills/skills/docx/scripts/document.py +0 -1276
  466. package/skills/skills/docx/scripts/templates/comments.xml +0 -3
  467. package/skills/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  468. package/skills/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  469. package/skills/skills/docx/scripts/templates/commentsIds.xml +0 -3
  470. package/skills/skills/docx/scripts/templates/people.xml +0 -3
  471. package/skills/skills/docx/scripts/utilities.py +0 -374
  472. package/skills/skills/frontend-design/LICENSE.txt +0 -177
  473. package/skills/skills/frontend-design/SKILL.md +0 -42
  474. package/skills/skills/internal-comms/LICENSE.txt +0 -202
  475. package/skills/skills/internal-comms/SKILL.md +0 -32
  476. package/skills/skills/internal-comms/examples/3p-updates.md +0 -47
  477. package/skills/skills/internal-comms/examples/company-newsletter.md +0 -65
  478. package/skills/skills/internal-comms/examples/faq-answers.md +0 -30
  479. package/skills/skills/internal-comms/examples/general-comms.md +0 -16
  480. package/skills/skills/mcp-builder/LICENSE.txt +0 -202
  481. package/skills/skills/mcp-builder/SKILL.md +0 -236
  482. package/skills/skills/mcp-builder/reference/evaluation.md +0 -602
  483. package/skills/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  484. package/skills/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  485. package/skills/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  486. package/skills/skills/mcp-builder/scripts/connections.py +0 -151
  487. package/skills/skills/mcp-builder/scripts/evaluation.py +0 -373
  488. package/skills/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  489. package/skills/skills/mcp-builder/scripts/requirements.txt +0 -2
  490. package/skills/skills/pdf/LICENSE.txt +0 -30
  491. package/skills/skills/pdf/SKILL.md +0 -294
  492. package/skills/skills/pdf/forms.md +0 -205
  493. package/skills/skills/pdf/reference.md +0 -612
  494. package/skills/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  495. package/skills/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  496. package/skills/skills/pdf/scripts/check_fillable_fields.py +0 -12
  497. package/skills/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  498. package/skills/skills/pdf/scripts/create_validation_image.py +0 -41
  499. package/skills/skills/pdf/scripts/extract_form_field_info.py +0 -152
  500. package/skills/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  501. package/skills/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  502. package/skills/skills/pptx/LICENSE.txt +0 -30
  503. package/skills/skills/pptx/SKILL.md +0 -484
  504. package/skills/skills/pptx/html2pptx.md +0 -625
  505. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  506. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  507. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  508. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  509. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  510. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  511. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  512. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  513. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  514. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  515. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  516. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  517. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  518. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  519. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  520. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  521. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  522. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  523. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  524. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  525. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  526. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  527. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  528. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  529. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  530. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  531. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  532. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  533. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  534. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  535. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  536. package/skills/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
  537. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  538. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  539. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  540. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  541. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  542. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  543. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  544. package/skills/skills/pptx/ooxml/scripts/pack.py +0 -159
  545. package/skills/skills/pptx/ooxml/scripts/unpack.py +0 -29
  546. package/skills/skills/pptx/ooxml/scripts/validate.py +0 -69
  547. package/skills/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
  548. package/skills/skills/pptx/ooxml/scripts/validation/base.py +0 -951
  549. package/skills/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
  550. package/skills/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
  551. package/skills/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
  552. package/skills/skills/pptx/ooxml.md +0 -427
  553. package/skills/skills/pptx/scripts/html2pptx.js +0 -979
  554. package/skills/skills/pptx/scripts/inventory.py +0 -1020
  555. package/skills/skills/pptx/scripts/rearrange.py +0 -231
  556. package/skills/skills/pptx/scripts/replace.py +0 -385
  557. package/skills/skills/pptx/scripts/thumbnail.py +0 -450
  558. package/skills/skills/skill-creator/LICENSE.txt +0 -202
  559. package/skills/skills/skill-creator/SKILL.md +0 -356
  560. package/skills/skills/skill-creator/references/output-patterns.md +0 -82
  561. package/skills/skills/skill-creator/references/workflows.md +0 -28
  562. package/skills/skills/skill-creator/scripts/init_skill.py +0 -303
  563. package/skills/skills/skill-creator/scripts/package_skill.py +0 -110
  564. package/skills/skills/skill-creator/scripts/quick_validate.py +0 -95
  565. package/skills/skills/slack-gif-creator/LICENSE.txt +0 -202
  566. package/skills/skills/slack-gif-creator/SKILL.md +0 -254
  567. package/skills/skills/slack-gif-creator/core/easing.py +0 -234
  568. package/skills/skills/slack-gif-creator/core/frame_composer.py +0 -176
  569. package/skills/skills/slack-gif-creator/core/gif_builder.py +0 -269
  570. package/skills/skills/slack-gif-creator/core/validators.py +0 -136
  571. package/skills/skills/slack-gif-creator/requirements.txt +0 -4
  572. package/skills/skills/theme-factory/LICENSE.txt +0 -202
  573. package/skills/skills/theme-factory/SKILL.md +0 -59
  574. package/skills/skills/theme-factory/theme-showcase.pdf +0 -0
  575. package/skills/skills/theme-factory/themes/arctic-frost.md +0 -19
  576. package/skills/skills/theme-factory/themes/botanical-garden.md +0 -19
  577. package/skills/skills/theme-factory/themes/desert-rose.md +0 -19
  578. package/skills/skills/theme-factory/themes/forest-canopy.md +0 -19
  579. package/skills/skills/theme-factory/themes/golden-hour.md +0 -19
  580. package/skills/skills/theme-factory/themes/midnight-galaxy.md +0 -19
  581. package/skills/skills/theme-factory/themes/modern-minimalist.md +0 -19
  582. package/skills/skills/theme-factory/themes/ocean-depths.md +0 -19
  583. package/skills/skills/theme-factory/themes/sunset-boulevard.md +0 -19
  584. package/skills/skills/theme-factory/themes/tech-innovation.md +0 -19
  585. package/skills/skills/web-artifacts-builder/LICENSE.txt +0 -202
  586. package/skills/skills/web-artifacts-builder/SKILL.md +0 -74
  587. package/skills/skills/web-artifacts-builder/scripts/bundle-artifact.sh +0 -54
  588. package/skills/skills/web-artifacts-builder/scripts/init-artifact.sh +0 -322
  589. package/skills/skills/webapp-testing/LICENSE.txt +0 -202
  590. package/skills/skills/webapp-testing/SKILL.md +0 -96
  591. package/skills/skills/webapp-testing/examples/console_logging.py +0 -35
  592. package/skills/skills/webapp-testing/examples/element_discovery.py +0 -40
  593. package/skills/skills/webapp-testing/examples/static_html_automation.py +0 -33
  594. package/skills/skills/webapp-testing/scripts/with_server.py +0 -106
  595. package/skills/skills/xlsx/LICENSE.txt +0 -30
  596. package/skills/skills/xlsx/SKILL.md +0 -289
  597. package/skills/skills/xlsx/recalc.py +0 -178
  598. package/skills/spec/agent-skills-spec.md +0 -3
  599. package/skills/template/SKILL.md +0 -6
  600. package/src/ai-client.ts +0 -1560
  601. package/src/remote-ai-client.ts +0 -664
  602. /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
package/dist/tools.js CHANGED
@@ -1,18 +1,21 @@
1
1
  import fs from 'fs/promises';
2
+ import { select, text } from '@clack/prompts';
2
3
  import path from 'path';
3
4
  import { fileURLToPath } from 'url';
4
5
  import readline from 'readline';
5
- import { exec, spawn } from 'child_process';
6
- import { promisify } from 'util';
6
+ import { spawn } from 'child_process';
7
7
  import { glob } from 'glob';
8
8
  import axios from 'axios';
9
- import inquirer from 'inquirer';
10
9
  import { ExecutionMode, AuthType } from './types.js';
11
10
  import { colors, icons } from './theme.js';
12
11
  import { getLogger } from './logger.js';
13
12
  import { getCancellationManager } from './cancellation.js';
14
13
  import { SystemPromptGenerator } from './system-prompt-generator.js';
15
- const execAsync = promisify(exec);
14
+ import { getSingletonSession } from './session.js';
15
+ import { ripgrep, fdFind } from './ripgrep.js';
16
+ import { getShellConfig, killProcessTree, quoteShellCommand } from './shell.js';
17
+ import { truncateTail, buildTruncationNotice } from './truncate.js';
18
+ import { createAIClient } from './ai-client-factory.js';
16
19
  //
17
20
  // Tool Description Pattern
18
21
  //
@@ -64,8 +67,16 @@ export class ReadTool {
64
67
  - Use offset and limit for large files to avoid loading entire content
65
68
  - Combine with ListDirectory to explore project structure first
66
69
  - Don't re-read files unnecessarily`;
67
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
70
+ allowedModes = [
71
+ ExecutionMode.YOLO,
72
+ ExecutionMode.ACCEPT_EDITS,
73
+ ExecutionMode.PLAN,
74
+ ExecutionMode.SMART,
75
+ ];
68
76
  async execute(params) {
77
+ if (!params || typeof params.filePath !== 'string') {
78
+ throw new Error('filePath is required and must be a string');
79
+ }
69
80
  const { filePath, offset = 0, limit } = params;
70
81
  try {
71
82
  // Handle ~ (user home directory) in file paths
@@ -82,10 +93,18 @@ export class ReadTool {
82
93
  const absolutePath = path.resolve(resolvedPath);
83
94
  const content = await fs.readFile(absolutePath, 'utf-8');
84
95
  const lines = content.split('\n');
96
+ const totalLines = lines.length;
85
97
  const startLine = Math.max(0, offset);
86
- const endLine = limit !== undefined ? Math.min(lines.length, startLine + limit) : lines.length;
98
+ const endLine = limit !== undefined ? Math.min(totalLines, startLine + limit) : totalLines;
87
99
  const selectedLines = lines.slice(startLine, endLine);
88
- return selectedLines.join('\n');
100
+ const result = selectedLines.join('\n');
101
+ // Add truncation notice if content is limited
102
+ if (limit !== undefined && endLine < totalLines) {
103
+ const remaining = totalLines - endLine;
104
+ const nextOffset = endLine;
105
+ return (result + `\n\n[${remaining} more lines in file. Use offset=${nextOffset} to continue]`);
106
+ }
107
+ return result;
89
108
  }
90
109
  catch (error) {
91
110
  // Show user-friendly path in error message
@@ -146,7 +165,7 @@ export class WriteTool {
146
165
  message: `Successfully wrote to ${filePath}`,
147
166
  filePath,
148
167
  lineCount,
149
- preview: isTruncated ? preview + '\n...' : preview
168
+ preview: isTruncated ? preview + '\n...' : preview,
150
169
  };
151
170
  }
152
171
  catch (error) {
@@ -156,7 +175,7 @@ export class WriteTool {
156
175
  }
157
176
  export class GrepTool {
158
177
  name = 'Grep';
159
- description = `Search for text patterns within files using regex or literal string matching. This is your PRIMARY tool for finding specific code, functions, or content.
178
+ description = `Search for text patterns within files using ripgrep. This is your PRIMARY tool for finding specific code, functions, or content.
160
179
 
161
180
  # When to Use
162
181
  - Finding specific function definitions or calls
@@ -173,89 +192,41 @@ export class GrepTool {
173
192
  # Parameters
174
193
  - \`pattern\`: Regex or literal string to search for
175
194
  - \`path\`: (Optional) Directory to search in, default: "."
176
- - \`include\`: (Optional) File glob pattern to include
177
- - \`exclude\`: (Optional) File glob pattern to exclude
178
- - \`case_sensitive\`: (Optional) Case-sensitive search, default: false
179
- - \`fixed_strings\`: (Optional) Treat pattern as literal string, default: false
195
+ - \`glob\`: (Optional) File glob pattern to include (e.g., "*.ts", "**/*.js")
196
+ - \`ignoreCase\`: (Optional) Case-insensitive search, default: false
197
+ - \`literal\`: (Optional) Treat pattern as literal string, default: false
180
198
  - \`context\`: (Optional) Lines of context before/after matches
181
- - \`no_ignore\`: (Optional) Don't ignore node_modules/.git, default: false
182
199
 
183
200
  # Examples
184
201
  - Find function: Grep(pattern="function myFunction")
185
202
  - Find with context: Grep(pattern="TODO", context=3)
186
- - TypeScript only: Grep(pattern="interface", include="*.ts")
203
+ - TypeScript only: Grep(pattern="interface", glob="*.ts")
204
+ - Case-insensitive: Grep(pattern="error", ignoreCase=true)
187
205
 
188
206
  # Best Practices
189
- - Use case_sensitive=true for short patterns to reduce false positives
190
- - Use fixed_strings=true if your pattern has special regex characters
207
+ - Use ignoreCase=true for short patterns to reduce false positives
208
+ - Use literal=true if your pattern has special regex characters
191
209
  - Use context to see the surrounding code for each match
192
- - Combine with include/exclude to narrow down file types`;
193
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
210
+ - Combine with glob to narrow down file types`;
211
+ allowedModes = [
212
+ ExecutionMode.YOLO,
213
+ ExecutionMode.ACCEPT_EDITS,
214
+ ExecutionMode.PLAN,
215
+ ExecutionMode.SMART,
216
+ ];
194
217
  async execute(params) {
195
- const { pattern, path: searchPath = '.', include, exclude, case_sensitive = false, fixed_strings = false, context, after, before, no_ignore = false } = params;
218
+ const { pattern, path: searchPath = '.', glob: includeGlob, ignoreCase = false, literal = false, context, limit, } = params;
196
219
  try {
197
- const ignorePatterns = no_ignore ? [] : ['node_modules/**', '.git/**', 'dist/**', 'build/**'];
198
- if (exclude) {
199
- ignorePatterns.push(exclude);
200
- }
201
- const absolutePath = path.resolve(searchPath);
202
- const files = await glob('**/*', {
203
- cwd: absolutePath,
204
- nodir: true,
205
- ignore: ignorePatterns
220
+ const result = await ripgrep({
221
+ pattern,
222
+ path: searchPath,
223
+ glob: includeGlob,
224
+ ignoreCase,
225
+ literal,
226
+ context,
227
+ limit,
206
228
  });
207
- const results = [];
208
- for (const file of files) {
209
- const fullPath = path.join(absolutePath, file);
210
- if (include && !file.match(include)) {
211
- continue;
212
- }
213
- try {
214
- const content = await fs.readFile(fullPath, 'utf-8');
215
- const lines = content.split('\n');
216
- lines.forEach((line, index) => {
217
- let matches = false;
218
- if (fixed_strings) {
219
- matches = case_sensitive
220
- ? line.includes(pattern)
221
- : line.toLowerCase().includes(pattern.toLowerCase());
222
- }
223
- else {
224
- try {
225
- const flags = case_sensitive ? 'g' : 'gi';
226
- const regex = new RegExp(pattern, flags);
227
- matches = regex.test(line);
228
- }
229
- catch (e) {
230
- matches = case_sensitive
231
- ? line.includes(pattern)
232
- : line.toLowerCase().includes(pattern.toLowerCase());
233
- }
234
- }
235
- if (matches) {
236
- const contextLines = [];
237
- if (before || context) {
238
- const beforeCount = before || context || 0;
239
- for (let i = Math.max(0, index - beforeCount); i < index; i++) {
240
- contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
241
- }
242
- }
243
- contextLines.push(`${fullPath}:${index + 1}:${line.trim()}`);
244
- if (after || context) {
245
- const afterCount = after || context || 0;
246
- for (let i = index + 1; i < Math.min(lines.length, index + 1 + afterCount); i++) {
247
- contextLines.push(`${fullPath}:${i + 1}:${lines[i].trim()}`);
248
- }
249
- }
250
- results.push(...contextLines);
251
- }
252
- });
253
- }
254
- catch (error) {
255
- continue;
256
- }
257
- }
258
- return results;
229
+ return result.split('\n').filter((line) => line.trim());
259
230
  }
260
231
  catch (error) {
261
232
  throw new Error(`Grep failed: ${error.message}`);
@@ -287,13 +258,23 @@ export class BashTool {
287
258
  - \`description\`: (Optional) Description of what the command does
288
259
  - \`timeout\`: (Optional) Timeout in seconds, default: 120
289
260
  - \`run_in_bg\`: (Optional) Run in background, default: false
261
+ - \`skillPath\`: (Optional) Skill directory path - when provided, NODE_PATH will include the skill's node_modules for dependency resolution
290
262
 
291
263
  # Examples
292
264
  - Install dependencies: Bash(command="npm install", description="Install npm dependencies")
265
+ - Run in skill directory with local deps: Bash(command="npm install docx", skillPath="~/.xagent/skills/docx")
266
+
267
+ # NODE_PATH Resolution
268
+ When \`skillPath\` is provided, the command will have access to:
269
+ - \`<skillPath>/node_modules\` (skill's local dependencies)
270
+ - xAgent's global node_modules
271
+
272
+ This is useful when working with skills that have local dependencies.
293
273
  - Run tests: Bash(command="npm test", description="Run unit tests")
294
274
  - Build project: Bash(command="npm run build", description="Build the project")
295
275
 
296
276
  # Best Practices
277
+ - To install npm packages that persist across sessions, use: \`XAGENT_USER_NPM=1 npm install <package>\`
297
278
  - Always provide a description for context
298
279
  - Set appropriate timeout for long-running commands
299
280
  - Use run_in_bg=true for commands that take a long time
@@ -301,7 +282,9 @@ export class BashTool {
301
282
  - Use absolute paths or paths relative to project root`;
302
283
  allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
303
284
  async execute(params) {
304
- const { command, cwd, description, timeout = 120, run_in_bg = false } = params;
285
+ const { command, cwd, description, timeout = 120, run_in_bg = false, skillPath } = params;
286
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
287
+ void description;
305
288
  // Determine effective working directory
306
289
  // Only use cwd if the command doesn't contain 'cd' (let LLM control directory)
307
290
  let effectiveCwd;
@@ -318,26 +301,104 @@ export class BashTool {
318
301
  // No cwd provided, use default
319
302
  effectiveCwd = undefined;
320
303
  }
321
- // Set up environment with NODE_PATH only for node commands
322
- const nodeModulesPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'node_modules');
304
+ // Resolve actual working directory
305
+ const actualCwd = effectiveCwd || process.cwd();
306
+ // Set up environment with NODE_PATH for node commands
307
+ const builtinNodeModulesPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'node_modules');
308
+ // Get user skills path from config (unified path: ~/.xagent/skills)
309
+ const { getConfigManager } = await import('./config.js');
310
+ const configManager = getConfigManager();
311
+ const userSkillsPath = configManager.getUserSkillsPath();
312
+ // Skill deps path: ~/.xagent/skills/{skillName}/node_modules
313
+ const builtinDepsPath = userSkillsPath ? path.join(userSkillsPath, 'builtin-deps') : null;
314
+ // Determine which node_modules to use
315
+ let skillNodeModulesPath = null;
316
+ // Priority 1: skillPath parameter (workspace scenario - LLM works in workspace, not skill dir)
317
+ if (skillPath) {
318
+ if (skillPath.includes('/builtin-deps/')) {
319
+ // Skill with deps in builtin-deps directory
320
+ const match = skillPath.match(/\/builtin-deps\/([^/]+)/);
321
+ if (match) {
322
+ skillNodeModulesPath = path.join(builtinDepsPath, match[1], 'node_modules');
323
+ }
324
+ }
325
+ else {
326
+ // Regular skill
327
+ skillNodeModulesPath = path.join(skillPath, 'node_modules');
328
+ }
329
+ }
330
+ // Priority 2: Check if we're inside a skill directory
331
+ else if (userSkillsPath && userSkillsPath.trim() && actualCwd.startsWith(userSkillsPath)) {
332
+ const relativePath = actualCwd.substring(userSkillsPath.length);
333
+ const pathParts = relativePath.split(path.sep).filter(Boolean);
334
+ if (pathParts.length > 0) {
335
+ if (pathParts[0] === 'builtin-deps' && pathParts.length > 1) {
336
+ // Skill with local deps in builtin-deps
337
+ const skillName = pathParts[1];
338
+ skillNodeModulesPath = path.join(builtinDepsPath, skillName, 'node_modules');
339
+ }
340
+ else {
341
+ // Regular skill
342
+ const skillName = pathParts[0];
343
+ const skillRoot = path.join(userSkillsPath, skillName);
344
+ try {
345
+ const skillMdPath = path.join(skillRoot, 'SKILL.md');
346
+ await fs.access(skillMdPath);
347
+ skillNodeModulesPath = path.join(skillRoot, 'node_modules');
348
+ }
349
+ catch {
350
+ // Not a skill directory, skip
351
+ }
352
+ }
353
+ }
354
+ }
355
+ // Build NODE_PATH - skill's node_modules takes precedence (last-wins)
356
+ let nodePath;
357
+ if (skillNodeModulesPath) {
358
+ nodePath = `${skillNodeModulesPath}${path.delimiter}${builtinNodeModulesPath}`;
359
+ }
360
+ else {
361
+ nodePath = builtinNodeModulesPath;
362
+ }
323
363
  const env = {
324
364
  ...process.env,
325
- NODE_PATH: nodeModulesPath
365
+ NODE_PATH: nodePath
326
366
  };
327
- // Only add NODE_PATH prefix for node commands
328
- const isNodeCommand = /\bnode\b/.test(command);
329
- const finalCommand = isNodeCommand
330
- ? `set NODE_PATH=${nodeModulesPath} && ${command}`
331
- : command;
367
+ // Handle npm install commands
368
+ const isNpmInstall = /\bnpm\s+install\b/i.test(command);
369
+ let finalCommand = command;
370
+ if (isNpmInstall && skillNodeModulesPath) {
371
+ // Install to skill's own node_modules
372
+ await fs.mkdir(skillNodeModulesPath, { recursive: true }).catch(() => { });
373
+ finalCommand = command.replace(/\bnpm\s+install\b/i, `npm install --prefix "${skillNodeModulesPath}"`);
374
+ }
375
+ // Get shell configuration (Windows Git Bash detection, etc.)
376
+ const { shell, args } = getShellConfig();
377
+ // Set up cross-platform encoding environment for command execution
378
+ if (process.platform === 'win32') {
379
+ // Windows: set code page to UTF-8 and ensure console output encoding
380
+ // chcp 65001 sets the console code page to UTF-8
381
+ // Use *>$null to suppress output (PowerShell-style, not CMD-style)
382
+ finalCommand = `chcp 65001 *>$null; [Console]::OutputEncoding = [System.Text.Encoding]::UTF8; [System.Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ${finalCommand}`;
383
+ }
384
+ else {
385
+ // Unix/macOS: set locale to UTF-8 for proper encoding handling
386
+ finalCommand = `export LC_ALL=C.UTF-8; export LANG=C.UTF-8; export PYTHONIOENCODING=utf-8; ${finalCommand}`;
387
+ }
388
+ const shellArgs = [...args, quoteShellCommand(finalCommand)];
332
389
  try {
333
390
  if (run_in_bg) {
334
391
  const taskId = `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
335
- const childProcess = spawn(finalCommand, {
392
+ const spawnOptions = {
336
393
  cwd: effectiveCwd || process.cwd(),
337
- shell: true,
338
- detached: true,
339
- env
340
- });
394
+ env,
395
+ stdio: ['pipe', 'pipe', 'pipe'],
396
+ };
397
+ // On Windows, don't use detached mode for PowerShell as it breaks output piping
398
+ if (process.platform !== 'win32') {
399
+ spawnOptions.detached = true;
400
+ }
401
+ const childProcess = spawn(shell, shellArgs, spawnOptions);
341
402
  const output = [];
342
403
  childProcess.stdout?.on('data', (data) => {
343
404
  const text = data.toString();
@@ -347,44 +408,128 @@ export class BashTool {
347
408
  const text = data.toString();
348
409
  output.push(text);
349
410
  });
350
- childProcess.on('close', (code) => {
351
- console.log(`Background task ${taskId} exited with code ${code}`);
411
+ childProcess.on('close', (_code) => {
412
+ // Silent cleanup - don't log to avoid noise during normal operation
413
+ // Note: On Windows with PowerShell, the shell process exits after
414
+ // the command completes
352
415
  });
353
416
  const toolRegistry = getToolRegistry();
354
417
  toolRegistry.addBackgroundTask(taskId, {
355
418
  process: childProcess,
356
419
  startTime: Date.now(),
357
- output
420
+ output,
358
421
  });
359
422
  return {
360
423
  stdout: '',
361
424
  stderr: '',
362
425
  exitCode: 0,
363
- taskId
426
+ taskId,
364
427
  };
365
428
  }
366
429
  else {
367
- const { stdout, stderr } = await execAsync(finalCommand, {
430
+ // Execute command with spawn for better control
431
+ const result = await this.spawnWithTimeout(shell, shellArgs, {
368
432
  cwd: effectiveCwd || process.cwd(),
369
- maxBuffer: 1024 * 1024 * 10,
370
- timeout: timeout * 1000,
371
- env
433
+ env,
434
+ timeout,
372
435
  });
436
+ // Apply truncation to stdout and stderr separately
437
+ const stdoutResult = truncateTail(result.stdout);
438
+ const stderrResult = truncateTail(result.stderr);
439
+ const stdout = stdoutResult.content;
440
+ const stderr = stderrResult.content;
441
+ let truncationNotice = '';
442
+ if (stdoutResult.truncated) {
443
+ truncationNotice += buildTruncationNotice(stdoutResult) + '\n';
444
+ }
445
+ if (stderrResult.truncated) {
446
+ truncationNotice += buildTruncationNotice(stderrResult) + '\n';
447
+ }
373
448
  return {
374
449
  stdout,
375
450
  stderr,
376
- exitCode: 0
451
+ exitCode: result.exitCode,
452
+ truncated: stdoutResult.truncated || stderrResult.truncated,
453
+ truncationNotice: truncationNotice || undefined,
377
454
  };
378
455
  }
379
456
  }
380
457
  catch (error) {
458
+ // Check if this was a timeout
459
+ if (error.message === 'timeout') {
460
+ return {
461
+ stdout: '',
462
+ stderr: 'Command timed out',
463
+ exitCode: -1,
464
+ truncated: false,
465
+ };
466
+ }
381
467
  return {
382
468
  stdout: error.stdout || '',
383
469
  stderr: error.stderr || error.message,
384
- exitCode: error.code || 1
470
+ exitCode: error.code || 1,
385
471
  };
386
472
  }
387
473
  }
474
+ /**
475
+ * Execute a command with timeout support and proper process termination.
476
+ */
477
+ spawnWithTimeout(shell, args, options) {
478
+ return new Promise((resolve, reject) => {
479
+ const { cwd, env, timeout } = options;
480
+ let timedOut = false;
481
+ let timeoutHandle;
482
+ const spawnOptions = {
483
+ cwd,
484
+ env,
485
+ stdio: ['pipe', 'pipe', 'pipe'],
486
+ };
487
+ // On Windows, don't use detached mode for PowerShell as it breaks output piping
488
+ if (process.platform !== 'win32') {
489
+ spawnOptions.detached = true;
490
+ }
491
+ const child = spawn(shell, args, spawnOptions);
492
+ const stdoutChunks = [];
493
+ const stderrChunks = [];
494
+ // Set timeout if provided
495
+ if (timeout > 0) {
496
+ timeoutHandle = setTimeout(() => {
497
+ timedOut = true;
498
+ if (child.pid) {
499
+ killProcessTree(child.pid);
500
+ }
501
+ }, timeout * 1000);
502
+ }
503
+ // Stream stdout
504
+ child.stdout?.on('data', (data) => {
505
+ stdoutChunks.push(data);
506
+ });
507
+ // Stream stderr
508
+ child.stderr?.on('data', (data) => {
509
+ stderrChunks.push(data);
510
+ });
511
+ // Handle process exit
512
+ child.on('close', (code) => {
513
+ if (timeoutHandle)
514
+ clearTimeout(timeoutHandle);
515
+ if (timedOut) {
516
+ reject(new Error('timeout'));
517
+ return;
518
+ }
519
+ resolve({
520
+ stdout: Buffer.concat(stdoutChunks).toString('utf-8'),
521
+ stderr: Buffer.concat(stderrChunks).toString('utf-8'),
522
+ exitCode: code ?? -1,
523
+ });
524
+ });
525
+ // Handle spawn errors
526
+ child.on('error', (err) => {
527
+ if (timeoutHandle)
528
+ clearTimeout(timeoutHandle);
529
+ reject(err);
530
+ });
531
+ });
532
+ }
388
533
  }
389
534
  export class ListDirectoryTool {
390
535
  name = 'ListDirectory';
@@ -416,7 +561,12 @@ export class ListDirectoryTool {
416
561
  - Results are absolute paths
417
562
  - Ignores node_modules and .git by default
418
563
  - Combine with Read to examine file contents`;
419
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
564
+ allowedModes = [
565
+ ExecutionMode.YOLO,
566
+ ExecutionMode.ACCEPT_EDITS,
567
+ ExecutionMode.PLAN,
568
+ ExecutionMode.SMART,
569
+ ];
420
570
  async execute(params) {
421
571
  const { path: dirPath = '.', recursive = false } = params;
422
572
  try {
@@ -429,9 +579,9 @@ export class ListDirectoryTool {
429
579
  const files = await glob(pattern, {
430
580
  cwd: absolutePath,
431
581
  nodir: false,
432
- ignore: ['node_modules/**', '.git/**']
582
+ ignore: ['node_modules/**', '.git/**'],
433
583
  });
434
- return files.map(file => path.join(absolutePath, file));
584
+ return files.map((file) => path.join(absolutePath, file));
435
585
  }
436
586
  catch (error) {
437
587
  throw new Error(`Failed to list directory: ${error.message}`);
@@ -440,7 +590,7 @@ export class ListDirectoryTool {
440
590
  }
441
591
  export class SearchFilesTool {
442
592
  name = 'SearchFiles';
443
- description = `Search for files matching a glob pattern. This is your PRIMARY tool for finding files by name or extension.
593
+ description = `Search for files matching a glob pattern using fd. This is your PRIMARY tool for finding files by name or extension.
444
594
 
445
595
  # When to Use
446
596
  - Finding all files of a certain type (*.ts, *.json, *.md)
@@ -475,21 +625,35 @@ export class SearchFilesTool {
475
625
  - Combine with path parameter to search specific directories
476
626
  - Use limit parameter to avoid huge result sets
477
627
  - Results are file paths, not content (use Grep on results if needed)`;
478
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
628
+ allowedModes = [
629
+ ExecutionMode.YOLO,
630
+ ExecutionMode.ACCEPT_EDITS,
631
+ ExecutionMode.PLAN,
632
+ ExecutionMode.SMART,
633
+ ];
479
634
  async execute(params) {
480
635
  const { pattern, path: searchPath = '.', limit = 1000 } = params;
481
636
  try {
482
- const files = await glob(pattern, {
483
- cwd: searchPath,
484
- ignore: ['node_modules/**', '.git/**', 'dist/**', 'build/**']
637
+ const output = await fdFind({
638
+ pattern,
639
+ path: searchPath,
640
+ limit,
485
641
  });
642
+ if (output === 'No files found') {
643
+ return {
644
+ files: [],
645
+ total: 0,
646
+ truncated: false,
647
+ };
648
+ }
649
+ const files = output.split('\n').filter((line) => line.trim());
486
650
  const total = files.length;
487
651
  const truncated = total > limit;
488
652
  const result = truncated ? files.slice(0, limit) : files;
489
653
  return {
490
654
  files: result,
491
655
  total,
492
- truncated
656
+ truncated,
493
657
  };
494
658
  }
495
659
  catch (error) {
@@ -532,7 +696,7 @@ export class DeleteFileTool {
532
696
  return {
533
697
  success: true,
534
698
  message: `Successfully deleted ${filePath}`,
535
- filePath
699
+ filePath,
536
700
  };
537
701
  }
538
702
  catch (error) {
@@ -574,7 +738,7 @@ export class CreateDirectoryTool {
574
738
  await fs.mkdir(absolutePath, { recursive });
575
739
  return {
576
740
  success: true,
577
- message: `Successfully created directory ${dirPath}`
741
+ message: `Successfully created directory ${dirPath}`,
578
742
  };
579
743
  }
580
744
  catch (error) {
@@ -584,29 +748,29 @@ export class CreateDirectoryTool {
584
748
  }
585
749
  // 编辑工具辅助函数
586
750
  function detectLineEnding(content) {
587
- const crlfIdx = content.indexOf("\r\n");
588
- const lfIdx = content.indexOf("\n");
751
+ const crlfIdx = content.indexOf('\r\n');
752
+ const lfIdx = content.indexOf('\n');
589
753
  if (lfIdx === -1)
590
- return "\n";
754
+ return '\n';
591
755
  if (crlfIdx === -1)
592
- return "\n";
593
- return crlfIdx < lfIdx ? "\r\n" : "\n";
756
+ return '\n';
757
+ return crlfIdx < lfIdx ? '\r\n' : '\n';
594
758
  }
595
759
  function normalizeToLF(text) {
596
- return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
760
+ return text.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
597
761
  }
598
762
  function restoreLineEndings(text, ending) {
599
- return ending === "\r\n" ? text.replace(/\n/g, "\r\n") : text;
763
+ return ending === '\r\n' ? text.replace(/\n/g, '\r\n') : text;
600
764
  }
601
765
  function normalizeForFuzzyMatch(text) {
602
- return (text
603
- .split("\n")
766
+ return text
767
+ .split('\n')
604
768
  .map((line) => line.trimEnd())
605
- .join("\n")
769
+ .join('\n')
606
770
  .replace(/['‘’""]/g, "'")
607
771
  .replace(/["""]/g, '"')
608
- .replace(/[—–‑−]/g, "-")
609
- .replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, " "));
772
+ .replace(/[—–‑−]/g, '-')
773
+ .replace(/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/g, ' ');
610
774
  }
611
775
  function fuzzyFindText(content, oldText) {
612
776
  const exactIndex = content.indexOf(oldText);
@@ -640,14 +804,16 @@ function fuzzyFindText(content, oldText) {
640
804
  };
641
805
  }
642
806
  function stripBom(content) {
643
- return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
807
+ return content.startsWith('\uFEFF')
808
+ ? { bom: '\uFEFF', text: content.slice(1) }
809
+ : { bom: '', text: content };
644
810
  }
645
811
  async function generateDiffString(oldContent, newContent, contextLines = 4) {
646
- const diffModule = await import("diff");
812
+ const diffModule = await import('diff');
647
813
  const parts = diffModule.diffLines(oldContent, newContent);
648
814
  const output = [];
649
- const oldLines = oldContent.split("\n");
650
- const newLines = newContent.split("\n");
815
+ const oldLines = oldContent.split('\n');
816
+ const newLines = newContent.split('\n');
651
817
  const maxLineNum = Math.max(oldLines.length, newLines.length);
652
818
  const lineNumWidth = String(maxLineNum).length;
653
819
  let oldLineNum = 1;
@@ -656,8 +822,8 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
656
822
  let firstChangedLine;
657
823
  for (let i = 0; i < parts.length; i++) {
658
824
  const part = parts[i];
659
- const raw = part.value.split("\n");
660
- if (raw[raw.length - 1] === "") {
825
+ const raw = part.value.split('\n');
826
+ if (raw[raw.length - 1] === '') {
661
827
  raw.pop();
662
828
  }
663
829
  if (part.added || part.removed) {
@@ -666,12 +832,12 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
666
832
  }
667
833
  for (const line of raw) {
668
834
  if (part.added) {
669
- const lineNum = String(newLineNum).padStart(lineNumWidth, " ");
835
+ const lineNum = String(newLineNum).padStart(lineNumWidth, ' ');
670
836
  output.push(`+${lineNum} ${line}`);
671
837
  newLineNum++;
672
838
  }
673
839
  else {
674
- const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
840
+ const lineNum = String(oldLineNum).padStart(lineNumWidth, ' ');
675
841
  output.push(`-${lineNum} ${line}`);
676
842
  oldLineNum++;
677
843
  }
@@ -693,18 +859,18 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
693
859
  linesToShow = linesToShow.slice(0, contextLines);
694
860
  }
695
861
  if (skipStart > 0) {
696
- output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
862
+ output.push(` ${''.padStart(lineNumWidth, ' ')} ...`);
697
863
  oldLineNum += skipStart;
698
864
  newLineNum += skipStart;
699
865
  }
700
866
  for (const line of linesToShow) {
701
- const lineNum = String(oldLineNum).padStart(lineNumWidth, " ");
867
+ const lineNum = String(oldLineNum).padStart(lineNumWidth, ' ');
702
868
  output.push(` ${lineNum} ${line}`);
703
869
  oldLineNum++;
704
870
  newLineNum++;
705
871
  }
706
872
  if (skipEnd > 0) {
707
- output.push(` ${"".padStart(lineNumWidth, " ")} ...`);
873
+ output.push(` ${''.padStart(lineNumWidth, ' ')} ...`);
708
874
  }
709
875
  }
710
876
  else {
@@ -714,7 +880,7 @@ async function generateDiffString(oldContent, newContent, contextLines = 4) {
714
880
  lastWasChange = false;
715
881
  }
716
882
  }
717
- return { diff: output.join("\n"), firstChangedLine };
883
+ return { diff: output.join('\n'), firstChangedLine };
718
884
  }
719
885
  export class EditTool {
720
886
  name = 'Edit';
@@ -765,6 +931,8 @@ edit(
765
931
  allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.SMART];
766
932
  async execute(params) {
767
933
  const { file_path, instruction, old_string, new_string } = params;
934
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
935
+ void instruction;
768
936
  try {
769
937
  const absolutePath = path.resolve(file_path);
770
938
  // Check if file exists
@@ -779,7 +947,7 @@ edit(
779
947
  }
780
948
  // Read the file
781
949
  const buffer = await fs.readFile(absolutePath);
782
- const rawContent = buffer.toString("utf-8");
950
+ const rawContent = buffer.toString('utf-8');
783
951
  // Strip BOM before matching
784
952
  const { bom, text: content } = stripBom(rawContent);
785
953
  const originalEnding = detectLineEnding(content);
@@ -817,7 +985,7 @@ edit(
817
985
  };
818
986
  }
819
987
  const finalContent = bom + restoreLineEndings(newContent, originalEnding);
820
- await fs.writeFile(absolutePath, finalContent, "utf-8");
988
+ await fs.writeFile(absolutePath, finalContent, 'utf-8');
821
989
  const diffResult = await generateDiffString(baseContent, newContent);
822
990
  return {
823
991
  success: true,
@@ -863,7 +1031,12 @@ export class WebSearchTool {
863
1031
  - Combine with web_fetch to get full content from relevant URLs
864
1032
  - Use quotes for exact phrase matching
865
1033
  - Consider adding context like year or version in query`;
866
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1034
+ allowedModes = [
1035
+ ExecutionMode.YOLO,
1036
+ ExecutionMode.ACCEPT_EDITS,
1037
+ ExecutionMode.PLAN,
1038
+ ExecutionMode.SMART,
1039
+ ];
867
1040
  async execute(params) {
868
1041
  const { query } = params;
869
1042
  try {
@@ -877,14 +1050,14 @@ export class WebSearchTool {
877
1050
  }
878
1051
  const response = await axios.post(`${baseUrl}/search`, { query }, {
879
1052
  headers: {
880
- 'Authorization': `Bearer ${searchApiKey}`,
881
- 'Content-Type': 'application/json'
1053
+ Authorization: `Bearer ${searchApiKey}`,
1054
+ 'Content-Type': 'application/json',
882
1055
  },
883
- timeout: 30000
1056
+ timeout: 30000,
884
1057
  });
885
1058
  return {
886
1059
  results: response.data.results || [],
887
- message: `Found ${response.data.results?.length || 0} results for "${query}"`
1060
+ message: `Found ${response.data.results?.length || 0} results for "${query}"`,
888
1061
  };
889
1062
  }
890
1063
  catch (error) {
@@ -940,22 +1113,27 @@ Each task needs:
940
1113
  - Don't batch multiple completions - update as you go
941
1114
  - Keep task descriptions clear and actionable
942
1115
  - Use appropriate priority levels to indicate urgency`;
943
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1116
+ allowedModes = [
1117
+ ExecutionMode.YOLO,
1118
+ ExecutionMode.ACCEPT_EDITS,
1119
+ ExecutionMode.PLAN,
1120
+ ExecutionMode.SMART,
1121
+ ];
944
1122
  todoList = [];
945
1123
  async execute(params) {
946
1124
  const { todos } = params;
947
1125
  try {
948
1126
  this.todoList = todos;
949
1127
  const summary = {
950
- pending: todos.filter(t => t.status === 'pending').length,
951
- in_progress: todos.filter(t => t.status === 'in_progress').length,
952
- completed: todos.filter(t => t.status === 'completed').length,
953
- failed: todos.filter(t => t.status === 'failed').length
1128
+ pending: todos.filter((t) => t.status === 'pending').length,
1129
+ in_progress: todos.filter((t) => t.status === 'in_progress').length,
1130
+ completed: todos.filter((t) => t.status === 'completed').length,
1131
+ failed: todos.filter((t) => t.status === 'failed').length,
954
1132
  };
955
1133
  return {
956
1134
  success: true,
957
1135
  message: `Updated todo list: ${summary.pending} pending, ${summary.in_progress} in progress, ${summary.completed} completed, ${summary.failed} failed`,
958
- todos: this.todoList
1136
+ todos: this.todoList,
959
1137
  };
960
1138
  }
961
1139
  catch (error) {
@@ -987,7 +1165,12 @@ export class TodoReadTool {
987
1165
  # Best Practices
988
1166
  - Use todo_write to modify the list, not todo_read
989
1167
  - Check todo_read after todo_write to verify updates`;
990
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1168
+ allowedModes = [
1169
+ ExecutionMode.YOLO,
1170
+ ExecutionMode.ACCEPT_EDITS,
1171
+ ExecutionMode.PLAN,
1172
+ ExecutionMode.SMART,
1173
+ ];
991
1174
  todoWriteTool;
992
1175
  constructor(todoWriteTool) {
993
1176
  this.todoWriteTool = todoWriteTool;
@@ -997,14 +1180,14 @@ export class TodoReadTool {
997
1180
  const todos = this.todoWriteTool.getTodos();
998
1181
  const summary = {
999
1182
  total: todos.length,
1000
- pending: todos.filter(t => t.status === 'pending').length,
1001
- in_progress: todos.filter(t => t.status === 'in_progress').length,
1002
- completed: todos.filter(t => t.status === 'completed').length,
1003
- failed: todos.filter(t => t.status === 'failed').length
1183
+ pending: todos.filter((t) => t.status === 'pending').length,
1184
+ in_progress: todos.filter((t) => t.status === 'in_progress').length,
1185
+ completed: todos.filter((t) => t.status === 'completed').length,
1186
+ failed: todos.filter((t) => t.status === 'failed').length,
1004
1187
  };
1005
1188
  return {
1006
1189
  todos,
1007
- summary
1190
+ summary,
1008
1191
  };
1009
1192
  }
1010
1193
  catch (error) {
@@ -1051,7 +1234,12 @@ export class TaskTool {
1051
1234
  - Include relevant context (file paths, requirements, constraints)
1052
1235
  - Set appropriate executionMode if needed
1053
1236
  - For parallel execution, ensure tasks are truly independent`;
1054
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
1237
+ allowedModes = [
1238
+ ExecutionMode.YOLO,
1239
+ ExecutionMode.ACCEPT_EDITS,
1240
+ ExecutionMode.PLAN,
1241
+ ExecutionMode.SMART,
1242
+ ];
1055
1243
  async execute(params, _executionMode) {
1056
1244
  const mode = params.executionMode || _executionMode || ExecutionMode.YOLO;
1057
1245
  try {
@@ -1059,16 +1247,13 @@ export class TaskTool {
1059
1247
  const agentManager = getAgentManager(process.cwd());
1060
1248
  const { getConfigManager } = await import('./config.js');
1061
1249
  const config = getConfigManager();
1062
- const { AIClient } = await import('./ai-client.js');
1063
- const aiClient = new AIClient({
1064
- type: AuthType.OPENAI_COMPATIBLE,
1065
- apiKey: config.get('apiKey'),
1066
- baseUrl: config.get('baseUrl'),
1067
- modelName: config.get('modelName') || 'Qwen3-Coder'
1068
- });
1250
+ const authConfig = config.getAuthConfig();
1251
+ // aiClient is created for future use when executeParallelAgents supports it
1252
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1253
+ const _aiClient = createAIClient(authConfig);
1069
1254
  const toolRegistry = getToolRegistry();
1070
1255
  if (params.agents && params.agents.length > 0) {
1071
- return await this.executeParallelAgents(params.agents, params.description, mode, agentManager, toolRegistry, aiClient);
1256
+ return await this.executeParallelAgents(params.agents, params.description, mode, agentManager, toolRegistry, config);
1072
1257
  }
1073
1258
  if (!params.subagent_type) {
1074
1259
  throw new Error('subagent_type is required for Task tool');
@@ -1076,15 +1261,16 @@ export class TaskTool {
1076
1261
  // Support both 'prompt' and 'query' parameter names (tool definition uses 'query')
1077
1262
  const prompt = params.prompt || params.query;
1078
1263
  if (!prompt) {
1079
- throw new Error('Task query/prompt is required. Received params: ' + JSON.stringify({
1080
- subagent_type: params.subagent_type,
1081
- prompt: params.prompt,
1082
- query: params.query,
1083
- description: params.description,
1084
- agents: params.agents?.length
1085
- }));
1086
- }
1087
- const result = await this.executeSingleAgent(params.subagent_type, prompt, params.description, params.useContext ?? true, params.constraints || [], mode, agentManager, toolRegistry, aiClient, config);
1264
+ throw new Error('Task query/prompt is required. Received params: ' +
1265
+ JSON.stringify({
1266
+ subagent_type: params.subagent_type,
1267
+ prompt: params.prompt,
1268
+ query: params.query,
1269
+ description: params.description,
1270
+ agents: params.agents?.length,
1271
+ }));
1272
+ }
1273
+ const result = await this.executeSingleAgent(params.subagent_type, prompt, params.description, params.useContext ?? true, params.constraints || [], mode, agentManager, toolRegistry, config);
1088
1274
  return result;
1089
1275
  }
1090
1276
  catch (error) {
@@ -1095,11 +1281,16 @@ export class TaskTool {
1095
1281
  * Create unified VLM caller
1096
1282
  * Uses remote VLM if remoteAIClient is provided, otherwise uses local VLM
1097
1283
  * Both modes receive full messages array for consistent behavior
1284
+ * @param remoteAIClient - Remote AI client for VLM calls
1285
+ * @param taskId - Task identifier for backend tracking
1286
+ * @param localConfig - Local VLM configuration
1287
+ * @param isFirstVlmCallRef - Reference to boolean tracking if this is the first VLM call
1288
+ * @param signal - Abort signal for cancellation
1098
1289
  */
1099
- createRemoteVlmCaller(remoteAIClient, taskId, localConfig, signal) {
1290
+ createRemoteVlmCaller(remoteAIClient, taskId, localConfig, isFirstVlmCallRef, signal) {
1100
1291
  // Remote mode: use RemoteAIClient
1101
1292
  if (remoteAIClient) {
1102
- return this.createRemoteVLMCaller(remoteAIClient, taskId, signal);
1293
+ return this.createRemoteVLMCaller(remoteAIClient, taskId, isFirstVlmCallRef, signal);
1103
1294
  }
1104
1295
  // Local mode: use local API
1105
1296
  return this.createLocalVLMCaller(localConfig, signal);
@@ -1107,11 +1298,24 @@ export class TaskTool {
1107
1298
  /**
1108
1299
  * Create remote VLM caller using RemoteAIClient
1109
1300
  * Now receives full messages array for consistent behavior with local mode
1301
+ * @param remoteAIClient - Remote AI client
1302
+ * @param taskId - Task identifier for backend tracking
1303
+ * @param isFirstVlmCallRef - Reference to boolean tracking if this is the first VLM call
1304
+ * @param signal - Abort signal for cancellation
1110
1305
  */
1111
- createRemoteVLMCaller(remoteAIClient, taskId, signal) {
1112
- return async (messages, systemPrompt) => {
1306
+ createRemoteVLMCaller(remoteAIClient, taskId, isFirstVlmCallRef, signal) {
1307
+ return async (messages, systemPrompt, _taskId, _isFirstVlmCallRef) => {
1113
1308
  try {
1114
- return await remoteAIClient.invokeVLM(messages, systemPrompt, { signal, taskId });
1309
+ // Use the ref to track first call status for the backend
1310
+ const status = isFirstVlmCallRef.current ? 'begin' : 'continue';
1311
+ const result = await remoteAIClient.invokeVLM(messages, systemPrompt, {
1312
+ signal,
1313
+ taskId,
1314
+ status,
1315
+ });
1316
+ // Update ref after call so subsequent calls use 'continue'
1317
+ isFirstVlmCallRef.current = false;
1318
+ return result;
1115
1319
  }
1116
1320
  catch (error) {
1117
1321
  throw new Error(`Remote VLM call failed: ${error.message}`);
@@ -1141,7 +1345,7 @@ export class TaskTool {
1141
1345
  method: 'POST',
1142
1346
  headers: {
1143
1347
  'Content-Type': 'application/json',
1144
- 'Authorization': `Bearer ${apiKey}`,
1348
+ Authorization: `Bearer ${apiKey}`,
1145
1349
  },
1146
1350
  body: JSON.stringify(requestBody),
1147
1351
  signal: abortSignal,
@@ -1150,7 +1354,7 @@ export class TaskTool {
1150
1354
  const errorText = await response.text();
1151
1355
  throw new Error(`VLM API error: ${errorText}`);
1152
1356
  }
1153
- const result = await response.json();
1357
+ const result = (await response.json());
1154
1358
  return result.choices?.[0]?.message?.content || '';
1155
1359
  };
1156
1360
  }
@@ -1160,30 +1364,63 @@ export class TaskTool {
1160
1364
  */
1161
1365
  async executeGUIAgent(prompt, description, agent, mode, config, indentLevel = 1, remoteAIClient) {
1162
1366
  const indent = ' '.repeat(indentLevel);
1163
- console.log(`${indent}${colors.primaryBright(`${icons.robot} GUI Agent`)}: ${description}`);
1164
- console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
1165
- console.log('');
1166
- // Get VLM configuration (used for local mode fallback)
1167
- const baseUrl = config.get('guiSubagentBaseUrl') || config.get('baseUrl') || '';
1168
- const apiKey = config.get('guiSubagentApiKey') || config.get('apiKey') || '';
1169
- const modelName = config.get('guiSubagentModel') || config.get('modelName') || '';
1367
+ // Get SDK adapter from session for SDK mode output
1368
+ let sdkOutputAdapter = null;
1369
+ let isSdkMode = false;
1370
+ try {
1371
+ const { getSingletonSession } = await import('./session.js');
1372
+ const session = getSingletonSession();
1373
+ if (session) {
1374
+ isSdkMode = session.isSdkMode;
1375
+ sdkOutputAdapter = session.sdkOutputAdapter;
1376
+ }
1377
+ }
1378
+ catch {
1379
+ // Session not available
1380
+ }
1381
+ // SDK mode: use adapter output (guiAgent.run() handles SDK output internally)
1382
+ // Only output console messages in non-SDK mode
1383
+ if (!isSdkMode) {
1384
+ console.log(`${indent}${colors.primaryBright(`${icons.robot} GUI Agent`)}: ${description}`);
1385
+ console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
1386
+ console.log('');
1387
+ }
1388
+ // Get VLM configuration for local mode
1389
+ // NOTE: guiSubagentBaseUrl must be explicitly configured, NOT fallback to baseUrl
1390
+ const baseUrl = config.get('guiSubagentBaseUrl') || '';
1391
+ const apiKey = config.get('guiSubagentApiKey') || '';
1392
+ const modelName = config.get('guiSubagentModel') || '';
1170
1393
  // Determine mode: remote if remoteAIClient exists, otherwise local
1171
1394
  const isRemoteMode = !!remoteAIClient;
1172
1395
  // Log mode information
1173
1396
  if (isRemoteMode) {
1174
- console.log(`${indent}${colors.info(`${icons.brain} Using remote VLM service`)}`);
1397
+ if (isSdkMode && sdkOutputAdapter) {
1398
+ // SDK mode: use adapter output
1399
+ sdkOutputAdapter.outputInfo('Using remote VLM service');
1400
+ }
1401
+ else {
1402
+ // Normal mode: console output
1403
+ console.log(`${indent}${colors.info(`${icons.brain} Using remote VLM service`)}`);
1404
+ }
1175
1405
  }
1176
1406
  else {
1177
- console.log(`${indent}${colors.info(`${icons.brain} Using local VLM configuration`)}`);
1178
- // Local mode requires configuration check
1179
- if (!baseUrl) {
1180
- return {
1181
- success: false,
1182
- message: `GUI task "${description}" failed: No valid API URL configured`
1183
- };
1407
+ if (isSdkMode && sdkOutputAdapter) {
1408
+ // SDK mode: use adapter output
1409
+ sdkOutputAdapter.outputInfo('Using local VLM configuration');
1410
+ }
1411
+ else {
1412
+ // Normal mode: console output
1413
+ console.log(`${indent}${colors.info(`${icons.brain} Using local VLM configuration`)}`);
1414
+ // Local mode requires explicit VLM configuration
1415
+ if (!baseUrl || !apiKey || !modelName) {
1416
+ return {
1417
+ success: false,
1418
+ message: `GUI task "${description}" failed: VLM not configured. Please run /model to configure Vision-Language Model first.`,
1419
+ };
1420
+ }
1421
+ console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
1422
+ console.log(`${indent}${colors.textMuted(` Base URL: ${baseUrl}`)}`);
1184
1423
  }
1185
- console.log(`${indent}${colors.textMuted(` Model: ${modelName}`)}`);
1186
- console.log(`${indent}${colors.textMuted(` Base URL: ${baseUrl}`)}`);
1187
1424
  }
1188
1425
  console.log('');
1189
1426
  // Get taskId from session for tracking (remote mode only)
@@ -1194,27 +1431,31 @@ export class TaskTool {
1194
1431
  const session = getSingletonSession();
1195
1432
  taskId = session?.getTaskId() || null;
1196
1433
  }
1197
- catch (e) {
1434
+ catch {
1198
1435
  taskId = null;
1199
1436
  }
1200
1437
  }
1438
+ // Track first VLM call for proper status management
1439
+ const isFirstVlmCallRef = { current: true };
1201
1440
  // Create remoteVlmCaller using the unified method (handles both local and remote modes)
1202
- const remoteVlmCaller = this.createRemoteVlmCaller(remoteAIClient, taskId, { baseUrl, apiKey, modelName });
1441
+ const remoteVlmCaller = this.createRemoteVlmCaller(remoteAIClient, taskId, { baseUrl, apiKey, modelName }, isFirstVlmCallRef);
1203
1442
  // Set up stdin polling for ESC cancellation
1204
1443
  let rawModeEnabled = false;
1205
1444
  let stdinPollingInterval = null;
1206
1445
  const cancellationManager = getCancellationManager();
1207
1446
  const logger = getLogger();
1208
1447
  const setupStdinPolling = () => {
1448
+ logger.debug(`[GUIAgent ESC] setupStdinPolling called, process.stdin.isTTY: ${process.stdin.isTTY}`);
1209
1449
  if (process.stdin.isTTY) {
1210
1450
  try {
1211
1451
  process.stdin.setRawMode(true);
1212
1452
  rawModeEnabled = true;
1213
1453
  process.stdin.resume();
1214
1454
  readline.emitKeypressEvents(process.stdin);
1455
+ logger.debug(`[GUIAgent ESC] Raw mode enabled successfully`);
1215
1456
  }
1216
1457
  catch (e) {
1217
- logger.debug(`[GUIAgent] Could not set raw mode: ${e}`);
1458
+ logger.debug(`[GUIAgent ESC] Could not set raw mode: ${e.message}`);
1218
1459
  }
1219
1460
  stdinPollingInterval = setInterval(() => {
1220
1461
  try {
@@ -1222,14 +1463,23 @@ export class TaskTool {
1222
1463
  const chunk = process.stdin.read(1);
1223
1464
  if (chunk && chunk.length > 0) {
1224
1465
  const code = chunk[0];
1225
- if (code === 0x1B) { // ESC
1226
- logger.debug('[GUIAgent] ESC detected!');
1466
+ if (code === 0x1b) {
1467
+ // ESC
1468
+ logger.debug('[GUIAgent ESC Polling] ESC detected! Code: 0x1b');
1227
1469
  cancellationManager.cancel();
1228
1470
  }
1471
+ else {
1472
+ // Log other key codes for debugging
1473
+ logger.debug(`[GUIAgent ESC Polling] Key code: 0x${code.toString(16)}`);
1474
+ }
1229
1475
  }
1230
1476
  }
1477
+ else {
1478
+ logger.debug('[GUIAgent ESC Polling] rawModeEnabled is false');
1479
+ }
1231
1480
  }
1232
1481
  catch (e) {
1482
+ logger.debug(`[GUIAgent ESC Polling] Error: ${e.message}`);
1233
1483
  // Ignore polling errors
1234
1484
  }
1235
1485
  }, 10);
@@ -1248,7 +1498,9 @@ export class TaskTool {
1248
1498
  };
1249
1499
  cancellationManager.on('cancelled', cancelHandler);
1250
1500
  // Start polling for ESC
1501
+ logger.debug(`[GUIAgent ESC] About to call setupStdinPolling`);
1251
1502
  setupStdinPolling();
1503
+ logger.debug(`[GUIAgent ESC] setupStdinPolling called`);
1252
1504
  try {
1253
1505
  // Import and create GUIAgent
1254
1506
  const { createGUISubAgent } = await import('./gui-subagent/index.js');
@@ -1256,11 +1508,15 @@ export class TaskTool {
1256
1508
  model: !isRemoteMode ? modelName : undefined,
1257
1509
  modelBaseUrl: !isRemoteMode ? baseUrl : undefined,
1258
1510
  modelApiKey: !isRemoteMode ? apiKey : undefined,
1511
+ taskId: taskId || undefined,
1512
+ isFirstVlmCallRef,
1259
1513
  remoteVlmCaller,
1260
1514
  isLocalMode: !isRemoteMode,
1261
- maxLoopCount: 30,
1515
+ maxLoopCount: 100,
1262
1516
  loopIntervalInMs: 500,
1263
1517
  showAIDebugInfo: config.get('showAIDebugInfo') || false,
1518
+ indentLevel: indentLevel,
1519
+ sdkOutputAdapter: isSdkMode ? sdkOutputAdapter : null,
1264
1520
  });
1265
1521
  // Add constraints to prompt if any
1266
1522
  const fullPrompt = prompt;
@@ -1278,7 +1534,7 @@ export class TaskTool {
1278
1534
  success: true,
1279
1535
  cancelled: true, // Mark as cancelled so main agent won't continue
1280
1536
  message: `GUI task "${description}" cancelled by user`,
1281
- result: 'Task cancelled'
1537
+ result: 'Task cancelled',
1282
1538
  };
1283
1539
  }
1284
1540
  cleanupStdinPolling();
@@ -1286,28 +1542,96 @@ export class TaskTool {
1286
1542
  // Flush stdout to ensure all output is displayed before returning
1287
1543
  process.stdout.write('\n');
1288
1544
  // Return result based on GUIAgent status
1545
+ // Always return all info except screenshots (base64) to avoid huge payload
1546
+ const conversationsWithoutScreenshots = result.conversations.map((conv) => ({
1547
+ ...conv,
1548
+ screenshotBase64: undefined, // Remove screenshots to avoid huge payload
1549
+ }));
1289
1550
  if (result.status === 'end') {
1290
- const iterations = result.conversations.filter(c => c.from === 'human' && c.screenshotBase64).length;
1291
- console.log(`${indent}${colors.success(`${icons.check} GUI task completed in ${iterations} iterations`)}`);
1551
+ const iterations = conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length;
1552
+ // SDK mode: use adapter output
1553
+ if (isSdkMode && sdkOutputAdapter) {
1554
+ sdkOutputAdapter.outputGUIAgentComplete(description, iterations);
1555
+ }
1556
+ else {
1557
+ // Normal mode: console output
1558
+ console.log(`${indent}${colors.success(`${icons.check} GUI task completed in ${iterations} iterations`)}`);
1559
+ }
1292
1560
  return {
1293
1561
  success: true,
1294
1562
  message: `GUI task "${description}" completed`,
1295
- result: `Completed in ${iterations} iterations`
1563
+ result: {
1564
+ status: result.status,
1565
+ iterations,
1566
+ actions: conversationsWithoutScreenshots
1567
+ .filter((c) => c.from === 'assistant' && c.actionType)
1568
+ .map((c) => c.actionType),
1569
+ conversations: conversationsWithoutScreenshots,
1570
+ error: result.error,
1571
+ },
1572
+ };
1573
+ }
1574
+ else if (result.status === 'call_llm') {
1575
+ // SDK mode: use adapter output
1576
+ if (isSdkMode && sdkOutputAdapter) {
1577
+ sdkOutputAdapter.outputGUIAgentStatus('call_llm', conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length);
1578
+ }
1579
+ else {
1580
+ // Normal mode: console output
1581
+ console.log(`${indent}${colors.warning(`${icons.warning} GUI agent returned to main agent for LLM decision`)}`);
1582
+ }
1583
+ return {
1584
+ success: true,
1585
+ message: `GUI task "${description}" returned for LLM decision`,
1586
+ result: {
1587
+ status: result.status,
1588
+ iterations: conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length,
1589
+ actions: conversationsWithoutScreenshots
1590
+ .filter((c) => c.from === 'assistant' && c.actionType)
1591
+ .map((c) => c.actionType),
1592
+ conversations: conversationsWithoutScreenshots,
1593
+ error: result.error,
1594
+ },
1296
1595
  };
1297
1596
  }
1298
1597
  else if (result.status === 'user_stopped') {
1598
+ // SDK mode: use adapter output
1599
+ if (isSdkMode && sdkOutputAdapter) {
1600
+ sdkOutputAdapter.outputGUIAgentCancelled(description);
1601
+ }
1299
1602
  return {
1300
1603
  success: true,
1301
1604
  message: `GUI task "${description}" stopped by user`,
1302
- result: 'User stopped'
1605
+ result: {
1606
+ status: result.status,
1607
+ iterations: conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length,
1608
+ actions: conversationsWithoutScreenshots
1609
+ .filter((c) => c.from === 'assistant' && c.actionType)
1610
+ .map((c) => c.actionType),
1611
+ conversations: conversationsWithoutScreenshots,
1612
+ stopped: true,
1613
+ },
1303
1614
  };
1304
1615
  }
1305
1616
  else {
1306
1617
  // status is 'error' or other non-success status
1307
1618
  const errorMsg = result.error || 'Unknown error';
1619
+ // SDK mode: use adapter output
1620
+ if (isSdkMode && sdkOutputAdapter) {
1621
+ sdkOutputAdapter.outputGUIAgentError(description, errorMsg);
1622
+ }
1308
1623
  return {
1309
1624
  success: false,
1310
- message: `GUI task "${description}" failed: ${errorMsg}`
1625
+ message: `GUI task "${description}" failed: ${errorMsg}`,
1626
+ result: {
1627
+ status: result.status,
1628
+ iterations: conversationsWithoutScreenshots.filter((c) => c.from === 'human' && c.screenshotContext).length,
1629
+ actions: conversationsWithoutScreenshots
1630
+ .filter((c) => c.from === 'assistant' && c.actionType)
1631
+ .map((c) => c.actionType),
1632
+ conversations: conversationsWithoutScreenshots,
1633
+ error: result.error,
1634
+ },
1311
1635
  };
1312
1636
  }
1313
1637
  }
@@ -1319,32 +1643,58 @@ export class TaskTool {
1319
1643
  // If the user cancelled the task, ignore any API errors (like 429)
1320
1644
  // and return cancelled status instead
1321
1645
  if (cancelled || cancellationManager.isOperationCancelled()) {
1646
+ // SDK mode: use adapter output
1647
+ if (isSdkMode && sdkOutputAdapter) {
1648
+ sdkOutputAdapter.outputGUIAgentCancelled(description);
1649
+ }
1322
1650
  return {
1323
1651
  success: true,
1324
1652
  cancelled: true, // Mark as cancelled so main agent won't continue
1325
1653
  message: `GUI task "${description}" cancelled by user`,
1326
- result: 'Task cancelled'
1654
+ result: 'Task cancelled',
1327
1655
  };
1328
1656
  }
1329
1657
  if (error.message === 'Operation cancelled by user') {
1658
+ // SDK mode: use adapter output
1659
+ if (isSdkMode && sdkOutputAdapter) {
1660
+ sdkOutputAdapter.outputGUIAgentCancelled(description);
1661
+ }
1330
1662
  return {
1331
1663
  success: true,
1332
1664
  message: `GUI task "${description}" cancelled by user`,
1333
- result: 'Task cancelled'
1665
+ result: 'Task cancelled',
1334
1666
  };
1335
1667
  }
1336
1668
  // Return failure without throwing - let the main agent handle it
1669
+ // SDK mode: use adapter output
1670
+ if (isSdkMode && sdkOutputAdapter) {
1671
+ sdkOutputAdapter.outputGUIAgentError(description, error.message);
1672
+ }
1337
1673
  return {
1338
1674
  success: false,
1339
- message: `GUI task "${description}" failed: ${error.message}`
1675
+ message: `GUI task "${description}" failed: ${error.message}`,
1340
1676
  };
1341
1677
  }
1342
1678
  }
1343
- async executeSingleAgent(subagent_type, prompt, description, useContext, constraints, mode, agentManager, toolRegistry, aiClient, config, indentLevel = 1) {
1679
+ async executeSingleAgent(subagent_type, prompt, description, useContext, constraints, mode, agentManager, toolRegistry, config, indentLevel = 1) {
1344
1680
  const agent = agentManager.getAgent(subagent_type);
1345
1681
  if (!agent) {
1346
1682
  throw new Error(`Agent ${subagent_type} not found`);
1347
1683
  }
1684
+ // Get SDK adapter from session for subagent output
1685
+ let sdkOutputAdapter = null;
1686
+ let isSdkMode = false;
1687
+ try {
1688
+ const { getSingletonSession } = await import('./session.js');
1689
+ const session = getSingletonSession();
1690
+ if (session) {
1691
+ isSdkMode = session.isSdkMode;
1692
+ sdkOutputAdapter = session.sdkOutputAdapter;
1693
+ }
1694
+ }
1695
+ catch {
1696
+ // Session not available
1697
+ }
1348
1698
  // Special handling for gui-subagent: directly call GUIAgent.run() instead of subagent message loop
1349
1699
  if (subagent_type === 'gui-subagent') {
1350
1700
  // Get RemoteAIClient instance from session (if available)
@@ -1356,7 +1706,7 @@ export class TaskTool {
1356
1706
  remoteAIClient = session.getRemoteAIClient();
1357
1707
  }
1358
1708
  }
1359
- catch (e) {
1709
+ catch {
1360
1710
  // Session not available, keep undefined
1361
1711
  remoteAIClient = undefined;
1362
1712
  }
@@ -1385,28 +1735,55 @@ export class TaskTool {
1385
1735
  modelName = agent.model;
1386
1736
  }
1387
1737
  }
1388
- // Create a new AIClient for this subagent with its specific model
1389
- const { AIClient: SubAgentAIClient } = await import('./ai-client.js');
1390
- const subAgentClient = new SubAgentAIClient({
1391
- type: AuthType.OPENAI_COMPATIBLE,
1392
- apiKey: apiKey,
1393
- baseUrl: baseUrl,
1394
- modelName: modelName,
1395
- showAIDebugInfo: config.get('showAIDebugInfo') || false
1396
- });
1738
+ // Create AI client for this subagent - each subagent gets its own independent client
1739
+ let subAgentClient;
1740
+ let isRemoteMode = false;
1741
+ let mainTaskId = null;
1742
+ const authConfig = config.getAuthConfig();
1743
+ if (authConfig.type === AuthType.OAUTH_XAGENT) {
1744
+ // Remote mode: create independent RemoteAIClient for each subagent
1745
+ // This prevents message queue conflicts when multiple subagents run in parallel
1746
+ const session = getSingletonSession();
1747
+ const remoteAIClient = session?.getRemoteAIClient();
1748
+ if (remoteAIClient) {
1749
+ // Clone or create independent client for this subagent
1750
+ // RemoteAIClient should be designed to handle concurrent requests
1751
+ subAgentClient = remoteAIClient;
1752
+ isRemoteMode = true;
1753
+ mainTaskId = session?.getTaskId() || null;
1754
+ }
1755
+ else {
1756
+ subAgentClient = createAIClient(authConfig);
1757
+ }
1758
+ }
1759
+ else {
1760
+ // Local mode: create client with subagent-specific model config
1761
+ const subAuthConfig = {
1762
+ ...authConfig,
1763
+ type: AuthType.OPENAI_COMPATIBLE,
1764
+ apiKey: apiKey,
1765
+ baseUrl: baseUrl,
1766
+ modelName: modelName,
1767
+ showAIDebugInfo: config.get('showAIDebugInfo') || false,
1768
+ };
1769
+ subAgentClient = createAIClient(subAuthConfig);
1770
+ }
1397
1771
  const indent = ' '.repeat(indentLevel);
1398
- const indentNext = ' '.repeat(indentLevel + 1);
1772
+ const _indentNext = ' '.repeat(indentLevel + 1);
1399
1773
  const agentName = agent.name || subagent_type;
1400
1774
  // Track execution history for better reporting to main agent
1401
1775
  const executionHistory = [];
1402
1776
  // Helper function to indent multi-line content
1403
1777
  const indentMultiline = (content, baseIndent) => {
1404
- return content.split('\n').map(line => `${baseIndent} ${line}`).join('\n');
1778
+ return content
1779
+ .split('\n')
1780
+ .map((line) => `${baseIndent} ${line}`)
1781
+ .join('\n');
1405
1782
  };
1406
1783
  const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, mode, agent);
1407
1784
  const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(agent.systemPrompt);
1408
1785
  const fullPrompt = constraints.length > 0
1409
- ? `${prompt}\n\nConstraints:\n${constraints.map(c => `- ${c}`).join('\n')}`
1786
+ ? `${prompt}\n\nConstraints:\n${constraints.map((c) => `- ${c}`).join('\n')}`
1410
1787
  : prompt;
1411
1788
  // Set up raw mode and stdin polling for ESC detection
1412
1789
  const cancellationManager = getCancellationManager();
@@ -1432,14 +1809,15 @@ export class TaskTool {
1432
1809
  const chunk = process.stdin.read(1);
1433
1810
  if (chunk && chunk.length > 0) {
1434
1811
  const code = chunk[0];
1435
- if (code === 0x1B) { // ESC
1812
+ if (code === 0x1b) {
1813
+ // ESC
1436
1814
  logger.debug('[TaskTool] ESC detected via polling!');
1437
1815
  cancellationManager.cancel();
1438
1816
  }
1439
1817
  }
1440
1818
  }
1441
1819
  }
1442
- catch (e) {
1820
+ catch {
1443
1821
  // Ignore polling errors
1444
1822
  }
1445
1823
  }, 10);
@@ -1466,9 +1844,9 @@ export class TaskTool {
1466
1844
  throw new Error('Operation cancelled by user');
1467
1845
  }
1468
1846
  };
1469
- let messages = [
1847
+ const messages = [
1470
1848
  { role: 'system', content: enhancedSystemPrompt },
1471
- { role: 'user', content: fullPrompt }
1849
+ { role: 'user', content: fullPrompt },
1472
1850
  ];
1473
1851
  const availableTools = agentManager.getAvailableToolsForAgent(agent, mode);
1474
1852
  const allToolDefinitions = toolRegistry.getToolDefinitions();
@@ -1482,21 +1860,34 @@ export class TaskTool {
1482
1860
  function: {
1483
1861
  name: toolName,
1484
1862
  description: `Tool: ${toolName}`,
1485
- parameters: { type: 'object', properties: {}, required: [] }
1486
- }
1863
+ parameters: { type: 'object', properties: {}, required: [] },
1864
+ },
1487
1865
  };
1488
1866
  });
1489
1867
  let iteration = 0;
1490
- const maxIterations = 10;
1491
- while (iteration < maxIterations) {
1868
+ let lastContentStr = ''; // Track last content for final result
1869
+ // Main agent style loop: continue until AI returns no more tool_calls
1870
+ // eslint-disable-next-line no-constant-condition
1871
+ while (true) {
1492
1872
  iteration++;
1493
1873
  // Check for cancellation before each iteration
1494
1874
  checkCancellation();
1495
- // Use withCancellation to make API call cancellable
1496
- const result = await cancellationManager.withCancellation(subAgentClient.chatCompletion(messages, {
1875
+ // Prepare chat options with taskId and model names for remote mode
1876
+ const chatOptions = {
1497
1877
  tools: toolDefinitions,
1498
- temperature: 0.7
1499
- }), `api-${subagent_type}-${iteration}`);
1878
+ temperature: 0.7,
1879
+ };
1880
+ // Pass taskId, status, and model names for remote mode subagent calls
1881
+ // Subagent shares the same taskId as the main task
1882
+ if (isRemoteMode && mainTaskId) {
1883
+ chatOptions.taskId = mainTaskId;
1884
+ chatOptions.status = iteration === 1 ? 'begin' : 'continue';
1885
+ // Pass model names to ensure subagent uses the same models as main task
1886
+ chatOptions.llmModelName = config.get('remote_llmModelName');
1887
+ chatOptions.vlmModelName = config.get('remote_vlmModelName');
1888
+ }
1889
+ // Use withCancellation to make API call cancellable
1890
+ const result = (await cancellationManager.withCancellation(subAgentClient.chatCompletion(messages, chatOptions), `api-${subagent_type}-${iteration}`));
1500
1891
  // Check for cancellation after API call
1501
1892
  checkCancellation();
1502
1893
  if (!result || !result.choices || result.choices.length === 0) {
@@ -1514,8 +1905,8 @@ export class TaskTool {
1514
1905
  }
1515
1906
  else if (Array.isArray(messageContent)) {
1516
1907
  const textParts = messageContent
1517
- .filter(item => typeof item?.text === 'string' && item.text.trim() !== '')
1518
- .map(item => item.text);
1908
+ .filter((item) => typeof item?.text === 'string' && item.text.trim() !== '')
1909
+ .map((item) => item.text);
1519
1910
  contentStr = textParts.join('');
1520
1911
  hasValidContent = textParts.length > 0;
1521
1912
  }
@@ -1531,133 +1922,77 @@ export class TaskTool {
1531
1922
  if (choice.finish_reason === 'length') {
1532
1923
  throw new Error(`Sub-agent ${subagent_type} response truncated due to length limits`);
1533
1924
  }
1534
- // Add assistant message to conversation
1535
- messages.push({ role: 'assistant', content: contentStr });
1925
+ // Add assistant message to conversation (必须包含 tool_calls,否则 tool_result 无法匹配)
1926
+ const assistantMessage = { role: 'assistant', content: contentStr };
1927
+ if (toolCalls && toolCalls.length > 0) {
1928
+ assistantMessage.tool_calls = toolCalls;
1929
+ }
1930
+ if (reasoningContent) {
1931
+ assistantMessage.reasoning_content = reasoningContent;
1932
+ }
1933
+ messages.push(assistantMessage);
1536
1934
  // Display reasoning content if present
1537
1935
  if (reasoningContent) {
1538
- console.log(`\n${indent}${colors.textDim(`${icons.brain} Thinking Process:`)}`);
1539
- const truncatedReasoning = reasoningContent.length > 500 ? reasoningContent.substring(0, 500) + '...' : reasoningContent;
1540
- const indentedReasoning = indentMultiline(truncatedReasoning, indent);
1541
- console.log(`${indentedReasoning}\n`);
1936
+ if (isSdkMode && sdkOutputAdapter) {
1937
+ sdkOutputAdapter.outputThinking(reasoningContent, 'compact');
1938
+ }
1939
+ else {
1940
+ console.log(`\n${indent}${colors.textDim(`${icons.brain} Thinking Process:`)}`);
1941
+ const truncatedReasoning = reasoningContent.length > 500
1942
+ ? reasoningContent.substring(0, 500) + '...'
1943
+ : reasoningContent;
1944
+ const indentedReasoning = indentMultiline(truncatedReasoning, indent);
1945
+ console.log(`${indentedReasoning}\n`);
1946
+ }
1542
1947
  }
1543
1948
  // Display assistant response (if there's any text content) with proper indentation
1544
1949
  if (contentStr) {
1545
- console.log(`\n${indent}${colors.primaryBright(agentName)}: ${description}`);
1546
- const truncatedContent = contentStr.length > 500 ? contentStr.substring(0, 500) + '...' : contentStr;
1547
- const indentedContent = indentMultiline(truncatedContent, indent);
1548
- console.log(`${indentedContent}\n`);
1950
+ if (isSdkMode && sdkOutputAdapter) {
1951
+ sdkOutputAdapter.outputAssistant(contentStr);
1952
+ }
1953
+ else {
1954
+ console.log(`\n${indent}${colors.primaryBright(agentName)}: ${description}`);
1955
+ const truncatedContent = contentStr.length > 500 ? contentStr.substring(0, 500) + '...' : contentStr;
1956
+ const indentedContent = indentMultiline(truncatedContent, indent);
1957
+ console.log(`${indentedContent}\n`);
1958
+ }
1549
1959
  }
1550
- // Process tool calls with proper indentation
1960
+ // Process tool calls in parallel (照搬 session 的实现)
1551
1961
  if (toolCalls && toolCalls.length > 0) {
1552
- for (const toolCall of toolCalls) {
1962
+ // Prepare all tool calls with their indices
1963
+ const preparedToolCalls = toolCalls.map((toolCall, index) => {
1553
1964
  const { name, arguments: params } = toolCall.function;
1554
1965
  let parsedParams;
1555
1966
  try {
1556
1967
  parsedParams = typeof params === 'string' ? JSON.parse(params) : params;
1557
1968
  }
1558
- catch (e) {
1969
+ catch {
1559
1970
  parsedParams = params;
1560
1971
  }
1561
- console.log(`${indent}${colors.textMuted(`${icons.loading} Tool: ${name}`)}`);
1972
+ return { name, params: parsedParams, id: toolCall.id, index };
1973
+ });
1974
+ // Display all tool call info first
1975
+ for (const tc of preparedToolCalls) {
1976
+ if (isSdkMode && sdkOutputAdapter) {
1977
+ sdkOutputAdapter.outputToolStart(tc.name, tc.params);
1978
+ }
1979
+ else {
1980
+ console.log(`${indent}${colors.textMuted(`${icons.loading} Tool: ${tc.name}`)}`);
1981
+ }
1982
+ }
1983
+ // Execute all tool calls in parallel
1984
+ const executePromises = preparedToolCalls.map(async (tc) => {
1562
1985
  try {
1563
1986
  // Check cancellation before tool execution
1564
1987
  checkCancellation();
1565
- const toolResult = await cancellationManager.withCancellation(toolRegistry.execute(name, parsedParams, mode, indent), `subagent-${subagent_type}-${name}-${iteration}`);
1566
- // Get showToolDetails config to control result display
1567
- const showToolDetails = config.get('showToolDetails') || false;
1568
- // Prepare result preview for history
1569
- const resultPreview = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
1570
- const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
1571
- // Special handling for different tools (consistent with session.ts display logic)
1572
- const isTodoTool = name === 'todo_write' || name === 'todo_read';
1573
- const isEditTool = name === 'Edit';
1574
- const isWriteTool = name === 'Write';
1575
- const isDeleteTool = name === 'DeleteFile';
1576
- const hasDiff = isEditTool && toolResult?.diff;
1577
- const hasFilePreview = isWriteTool && toolResult?.preview;
1578
- const hasDeleteInfo = isDeleteTool && toolResult?.filePath;
1579
- // Import render functions for consistent display
1580
- const { renderDiff, renderLines } = await import('./theme.js');
1581
- if (isTodoTool) {
1582
- // Display todo list
1583
- console.log(`${indent}${colors.success(`${icons.check} Todo List:`)}`);
1584
- const todos = toolResult?.todos || [];
1585
- if (todos.length === 0) {
1586
- console.log(`${indent} ${colors.textMuted('No tasks')}`);
1587
- }
1588
- else {
1589
- const statusConfig = {
1590
- 'pending': { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
1591
- 'in_progress': { icon: icons.loading, color: colors.warning, label: 'In Progress' },
1592
- 'completed': { icon: icons.success, color: colors.success, label: 'Completed' },
1593
- 'failed': { icon: icons.error, color: colors.error, label: 'Failed' }
1594
- };
1595
- for (const todo of todos) {
1596
- const status = statusConfig[todo.status] || statusConfig['pending'];
1597
- console.log(`${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`);
1598
- }
1599
- }
1600
- if (toolResult?.message) {
1601
- console.log(`${indent}${colors.textDim(toolResult.message)}`);
1602
- }
1603
- console.log('');
1604
- }
1605
- else if (hasDiff) {
1606
- // Display edit result with diff
1607
- console.log('');
1608
- const diffOutput = renderDiff(toolResult.diff);
1609
- const indentedDiff = diffOutput.split('\n').map(line => `${indent} ${line}`).join('\n');
1610
- console.log(`${indentedDiff}\n`);
1611
- }
1612
- else if (hasFilePreview) {
1613
- // Display new file content in preview style
1614
- console.log('');
1615
- console.log(`${indent}${colors.success(`${icons.file} ${toolResult.filePath}`)}`);
1616
- console.log(`${indent}${colors.textDim(` ${toolResult.lineCount} lines`)}`);
1617
- console.log('');
1618
- console.log(renderLines(toolResult.preview, { maxLines: 10, indent: indent + ' ' }));
1619
- console.log('');
1620
- }
1621
- else if (hasDeleteInfo) {
1622
- // Display DeleteFile result
1623
- console.log('');
1624
- console.log(`${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`);
1625
- console.log('');
1626
- }
1627
- else if (showToolDetails) {
1628
- // Show full result details
1629
- const indentedPreview = indentMultiline(resultPreview, indent);
1630
- console.log(`${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`);
1631
- }
1632
- else if (toolResult && toolResult.success === false) {
1633
- // Tool failed
1634
- console.log(`${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`);
1635
- }
1636
- else if (toolResult) {
1637
- // Show brief preview by default
1638
- const indentedPreview = indentMultiline(truncatedPreview, indent);
1639
- console.log(`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`);
1640
- }
1641
- else {
1642
- console.log(`${indent}${colors.textDim('(no result)')}\n`);
1643
- }
1644
- // Record successful tool execution in history (use truncated preview to save memory)
1645
- executionHistory.push({
1646
- tool: name,
1647
- status: 'success',
1648
- params: parsedParams,
1649
- result: truncatedPreview,
1650
- timestamp: new Date().toISOString()
1651
- });
1652
- messages.push({
1653
- role: 'tool',
1654
- content: JSON.stringify(toolResult),
1655
- tool_call_id: toolCall.id
1656
- });
1988
+ const toolResult = await cancellationManager.withCancellation(toolRegistry.execute(tc.name, tc.params, mode, indent), `subagent-${subagent_type}-${tc.name}-${iteration}`);
1989
+ return { ...tc, toolResult, error: undefined };
1657
1990
  }
1658
1991
  catch (error) {
1659
1992
  if (error.message === 'Operation cancelled by user') {
1660
- console.log(`${indent}${colors.warning(`⚠️ Operation cancelled`)}\n`);
1993
+ if (!isSdkMode || !sdkOutputAdapter) {
1994
+ console.log(`${indent}${colors.warning(`⚠️ Operation cancelled`)}\n`);
1995
+ }
1661
1996
  cancellationManager.off('cancelled', cancelHandler);
1662
1997
  cleanupStdinPolling();
1663
1998
  const summaryPreview = contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
@@ -1669,78 +2004,195 @@ export class TaskTool {
1669
2004
  executionHistory: {
1670
2005
  totalIterations: iteration,
1671
2006
  toolsExecuted: executionHistory.length,
1672
- successfulTools: executionHistory.filter(t => t.status === 'success').length,
1673
- failedTools: executionHistory.filter(t => t.status === 'error').length,
2007
+ successfulTools: executionHistory.filter((t) => t.status === 'success').length,
2008
+ failedTools: executionHistory.filter((t) => t.status === 'error').length,
1674
2009
  history: executionHistory,
1675
- cancelled: true
1676
- }
1677
- }
2010
+ cancelled: true,
2011
+ },
2012
+ },
1678
2013
  };
1679
2014
  }
1680
- console.log(`${indent}${colors.error(`${icons.cross} Error:`)} ${error.message}\n`);
2015
+ return { ...tc, toolResult: undefined, error: error.message };
2016
+ }
2017
+ });
2018
+ const settledResults = await Promise.all(executePromises);
2019
+ // Check for cancellation in results
2020
+ const cancellationResult = settledResults.find((r) => 'success' in r && r.success === false);
2021
+ if (cancellationResult) {
2022
+ return cancellationResult;
2023
+ }
2024
+ const resultsByIndex = new Map();
2025
+ const usedIndices = new Set();
2026
+ for (const result of settledResults) {
2027
+ // Find the first unused original index that matches the tool name
2028
+ const originalIndex = preparedToolCalls.findIndex((tc, idx) => tc.name === result.name && !usedIndices.has(idx));
2029
+ if (originalIndex !== -1) {
2030
+ usedIndices.add(originalIndex);
2031
+ resultsByIndex.set(originalIndex, result);
2032
+ }
2033
+ }
2034
+ // Import render functions for consistent display
2035
+ const { renderDiff, renderLines } = await import('./theme.js');
2036
+ // Process results in the original tool_calls order
2037
+ for (let i = 0; i < preparedToolCalls.length; i++) {
2038
+ const result = resultsByIndex.get(i);
2039
+ if (!result)
2040
+ continue;
2041
+ const { name, params: parsedParams, toolResult, error } = result;
2042
+ // Get showToolDetails config to control result display
2043
+ const showToolDetails = config.get('showToolDetails') || false;
2044
+ // Prepare result preview for history
2045
+ const resultPreview = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2);
2046
+ const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
2047
+ if (error) {
2048
+ // Handle error case
2049
+ if (isSdkMode && sdkOutputAdapter) {
2050
+ sdkOutputAdapter.outputToolError(name, error);
2051
+ }
2052
+ else {
2053
+ console.log(`${indent}${colors.error(`${icons.cross} Error:`)} ${error}\n`);
2054
+ }
1681
2055
  // Record failed tool execution in history
1682
2056
  executionHistory.push({
1683
2057
  tool: name,
1684
2058
  status: 'error',
1685
2059
  params: parsedParams,
1686
- error: error.message,
1687
- timestamp: new Date().toISOString()
2060
+ error,
2061
+ timestamp: new Date().toISOString(),
1688
2062
  });
1689
2063
  messages.push({
1690
2064
  role: 'tool',
1691
- content: JSON.stringify({ error: error.message }),
1692
- tool_call_id: toolCall.id
2065
+ content: JSON.stringify({ error }),
2066
+ tool_call_id: result.id,
2067
+ });
2068
+ }
2069
+ else {
2070
+ // Handle success case - display result
2071
+ // SDK mode: output tool result via adapter
2072
+ if (isSdkMode && sdkOutputAdapter) {
2073
+ sdkOutputAdapter.outputToolResult(name, toolResult);
2074
+ }
2075
+ // Normal mode: console output (SDK mode already output via adapter above)
2076
+ if (!isSdkMode || !sdkOutputAdapter) {
2077
+ // Special handling for different tools (consistent with session.ts display logic)
2078
+ const isTodoTool = name === 'todo_write' || name === 'todo_read';
2079
+ const isEditTool = name === 'Edit';
2080
+ const isWriteTool = name === 'Write';
2081
+ const isDeleteTool = name === 'DeleteFile';
2082
+ const hasDiff = isEditTool && toolResult?.diff;
2083
+ const hasFilePreview = isWriteTool && toolResult?.preview;
2084
+ const hasDeleteInfo = isDeleteTool && toolResult?.filePath;
2085
+ if (isTodoTool) {
2086
+ // Display todo list
2087
+ console.log(`${indent}${colors.success(`${icons.check} Todo List:`)}`);
2088
+ const todos = toolResult?.todos || [];
2089
+ if (todos.length === 0) {
2090
+ console.log(`${indent} ${colors.textMuted('No tasks')}`);
2091
+ }
2092
+ else {
2093
+ const statusConfig = {
2094
+ pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
2095
+ in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
2096
+ completed: { icon: icons.success, color: colors.success, label: 'Completed' },
2097
+ failed: { icon: icons.error, color: colors.error, label: 'Failed' },
2098
+ };
2099
+ for (const todo of todos) {
2100
+ const status = statusConfig[todo.status] || statusConfig['pending'];
2101
+ console.log(`${indent} ${status.color(status.icon)} ${status.color(status.label)}: ${colors.text(todo.task)}`);
2102
+ }
2103
+ }
2104
+ if (toolResult?.message) {
2105
+ console.log(`${indent}${colors.textDim(toolResult.message)}`);
2106
+ }
2107
+ console.log('');
2108
+ }
2109
+ else if (hasDiff) {
2110
+ // Display edit result with diff
2111
+ console.log('');
2112
+ const diffOutput = renderDiff(toolResult.diff);
2113
+ const indentedDiff = diffOutput
2114
+ .split('\n')
2115
+ .map((line) => `${indent} ${line}`)
2116
+ .join('\n');
2117
+ console.log(`${indentedDiff}\n`);
2118
+ }
2119
+ else if (hasFilePreview) {
2120
+ // Display new file content in preview style
2121
+ console.log('');
2122
+ console.log(`${indent}${colors.success(`${icons.file} ${toolResult.filePath}`)}`);
2123
+ console.log(`${indent}${colors.textDim(` ${toolResult.lineCount} lines`)}`);
2124
+ console.log('');
2125
+ console.log(renderLines(toolResult.preview, { maxLines: 10, indent: indent + ' ' }));
2126
+ console.log('');
2127
+ }
2128
+ else if (hasDeleteInfo) {
2129
+ // Display DeleteFile result
2130
+ console.log('');
2131
+ console.log(`${indent}${colors.success(`${icons.check} Deleted: ${toolResult.filePath}`)}`);
2132
+ console.log('');
2133
+ }
2134
+ else if (showToolDetails) {
2135
+ // Show full result details
2136
+ const indentedPreview = indentMultiline(resultPreview, indent);
2137
+ console.log(`${indent}${colors.success(`${icons.check} Tool Result:`)}\n${indentedPreview}\n`);
2138
+ }
2139
+ else if (toolResult && toolResult.success === false) {
2140
+ // Tool failed
2141
+ console.log(`${indent}${colors.error(`${icons.cross} ${toolResult.message || 'Failed'}`)}\n`);
2142
+ }
2143
+ else if (toolResult) {
2144
+ // Show brief preview by default
2145
+ const indentedPreview = indentMultiline(truncatedPreview, indent);
2146
+ console.log(`${indent}${colors.success(`${icons.check} Completed`)}\n${indentedPreview}\n`);
2147
+ }
2148
+ else {
2149
+ console.log(`${indent}${colors.textDim('(no result)')}\n`);
2150
+ }
2151
+ }
2152
+ // Record successful tool execution in history (use truncated preview to save memory)
2153
+ executionHistory.push({
2154
+ tool: name,
2155
+ status: 'success',
2156
+ params: parsedParams,
2157
+ result: truncatedPreview,
2158
+ timestamp: new Date().toISOString(),
2159
+ });
2160
+ messages.push({
2161
+ role: 'tool',
2162
+ content: JSON.stringify(toolResult),
2163
+ tool_call_id: result.id,
1693
2164
  });
1694
2165
  }
1695
2166
  }
1696
- console.log('');
2167
+ if (!isSdkMode || !sdkOutputAdapter) {
2168
+ console.log('');
2169
+ }
1697
2170
  continue; // Continue to next iteration to get final response
1698
2171
  }
1699
- // No more tool calls, return the result with execution history
1700
- cancellationManager.off('cancelled', cancelHandler);
1701
- cleanupStdinPolling();
1702
- const summaryPreview = contentStr.length > 300 ? contentStr.substring(0, 300) + '...' : contentStr;
1703
- return {
1704
- success: true,
1705
- message: `Task "${description}" completed by ${subagent_type}`,
1706
- result: {
1707
- summary: summaryPreview,
1708
- executionHistory: {
1709
- totalIterations: iteration,
1710
- toolsExecuted: executionHistory.length,
1711
- successfulTools: executionHistory.filter(t => t.status === 'success').length,
1712
- failedTools: executionHistory.filter(t => t.status === 'error').length,
1713
- history: executionHistory
1714
- }
1715
- }
1716
- };
2172
+ // No more tool calls - break loop (same as main agent)
2173
+ lastContentStr = contentStr || '';
2174
+ break;
1717
2175
  }
1718
- // Max iterations reached - return accumulated results instead of throwing error
1719
- // Get the last assistant message content
1720
- const lastAssistantMsg = messages.filter(m => m.role === 'assistant').pop();
1721
- const lastContentStr = typeof lastAssistantMsg?.content === 'string'
1722
- ? lastAssistantMsg.content
1723
- : JSON.stringify(lastAssistantMsg?.content || '');
2176
+ // Loop ended - return result (same as main agent pattern)
1724
2177
  cancellationManager.off('cancelled', cancelHandler);
1725
2178
  cleanupStdinPolling();
1726
2179
  const summaryPreview = lastContentStr.length > 300 ? lastContentStr.substring(0, 300) + '...' : lastContentStr;
1727
2180
  return {
1728
2181
  success: true,
1729
- message: `Task "${description}" completed (max iterations reached) by ${subagent_type}`,
2182
+ message: `Task "${description}" completed by ${subagent_type}`,
1730
2183
  result: {
1731
2184
  summary: summaryPreview,
1732
2185
  executionHistory: {
1733
2186
  totalIterations: iteration,
1734
2187
  toolsExecuted: executionHistory.length,
1735
- successfulTools: executionHistory.filter(t => t.status === 'success').length,
1736
- failedTools: executionHistory.filter(t => t.status === 'error').length,
2188
+ successfulTools: executionHistory.filter((t) => t.status === 'success').length,
2189
+ failedTools: executionHistory.filter((t) => t.status === 'error').length,
1737
2190
  history: executionHistory,
1738
- maxIterationsReached: true
1739
- }
1740
- }
2191
+ },
2192
+ },
1741
2193
  };
1742
2194
  }
1743
- async executeParallelAgents(agents, description, mode, agentManager, toolRegistry, aiClient, indentLevel = 1) {
2195
+ async executeParallelAgents(agents, description, mode, agentManager, toolRegistry, config, indentLevel = 1) {
1744
2196
  const indent = ' '.repeat(indentLevel);
1745
2197
  const indentNext = ' '.repeat(indentLevel + 1);
1746
2198
  const cancellationManager = getCancellationManager();
@@ -1765,14 +2217,15 @@ export class TaskTool {
1765
2217
  const chunk = process.stdin.read(1);
1766
2218
  if (chunk && chunk.length > 0) {
1767
2219
  const code = chunk[0];
1768
- if (code === 0x1B) { // ESC
2220
+ if (code === 0x1b) {
2221
+ // ESC
1769
2222
  logger.debug('[ParallelAgents] ESC detected via polling!');
1770
2223
  cancellationManager.cancel();
1771
2224
  }
1772
2225
  }
1773
2226
  }
1774
2227
  }
1775
- catch (e) {
2228
+ catch {
1776
2229
  // Ignore polling errors
1777
2230
  }
1778
2231
  }, 10);
@@ -1794,23 +2247,23 @@ export class TaskTool {
1794
2247
  cancellationManager.on('cancelled', cancelHandler);
1795
2248
  console.log(`\n${indent}${colors.accent('◆')} ${colors.primaryBright('Parallel Agents')}: ${agents.length} running...`);
1796
2249
  const startTime = Date.now();
1797
- const agentPromises = agents.map(async (agentTask, index) => {
2250
+ const agentPromises = agents.map(async (agentTask, _index) => {
1798
2251
  // Check if cancelled
1799
2252
  if (cancelled || cancellationManager.isOperationCancelled()) {
1800
2253
  return {
1801
2254
  success: false,
1802
2255
  agent: agentTask.subagent_type,
1803
2256
  description: agentTask.description,
1804
- error: 'Operation cancelled by user'
2257
+ error: 'Operation cancelled by user',
1805
2258
  };
1806
2259
  }
1807
2260
  try {
1808
- const result = await this.executeSingleAgent(agentTask.subagent_type, agentTask.prompt, agentTask.description, agentTask.useContext ?? true, agentTask.constraints || [], mode, agentManager, toolRegistry, aiClient, indentLevel + 1);
2261
+ const result = await this.executeSingleAgent(agentTask.subagent_type, agentTask.prompt, agentTask.description, agentTask.useContext ?? true, agentTask.constraints || [], mode, agentManager, toolRegistry, config, indentLevel + 1);
1809
2262
  return {
1810
2263
  success: true,
1811
2264
  agent: agentTask.subagent_type,
1812
2265
  description: agentTask.description,
1813
- result: result.result
2266
+ result: result.result,
1814
2267
  };
1815
2268
  }
1816
2269
  catch (error) {
@@ -1818,14 +2271,14 @@ export class TaskTool {
1818
2271
  success: false,
1819
2272
  agent: agentTask.subagent_type,
1820
2273
  description: agentTask.description,
1821
- error: error.message
2274
+ error: error.message,
1822
2275
  };
1823
2276
  }
1824
2277
  });
1825
2278
  const results = await Promise.all(agentPromises);
1826
2279
  const duration = Date.now() - startTime;
1827
- const successfulAgents = results.filter(r => r.success);
1828
- const failedAgents = results.filter(r => !r.success);
2280
+ const successfulAgents = results.filter((r) => r.success);
2281
+ const failedAgents = results.filter((r) => !r.success);
1829
2282
  console.log(`${indent}${colors.success('✔')} Parallel task completed in ${colors.textMuted(duration + 'ms')}`);
1830
2283
  console.log(`${indent}${colors.info('ℹ')} Success: ${successfulAgents.length}/${agents.length} agents\n`);
1831
2284
  if (failedAgents.length > 0) {
@@ -1841,16 +2294,16 @@ export class TaskTool {
1841
2294
  return {
1842
2295
  success: failedAgents.length === 0,
1843
2296
  message: `Parallel task "${description}" completed: ${successfulAgents.length}/${agents.length} successful`,
1844
- results: successfulAgents.map(r => ({
2297
+ results: successfulAgents.map((r) => ({
1845
2298
  agent: r.agent,
1846
2299
  description: r.description,
1847
- result: r.result
2300
+ result: r.result,
1848
2301
  })),
1849
- errors: failedAgents.map(r => ({
2302
+ errors: failedAgents.map((r) => ({
1850
2303
  agent: r.agent,
1851
2304
  description: r.description,
1852
- error: r.error
1853
- }))
2305
+ error: r.error,
2306
+ })),
1854
2307
  };
1855
2308
  }
1856
2309
  }
@@ -1882,7 +2335,12 @@ export class ReadBashOutputTool {
1882
2335
  - Use appropriate poll_interval based on expected task duration
1883
2336
  - Check status to see if task is still running or completed
1884
2337
  - Combine with todo_write to track background task progress`;
1885
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2338
+ allowedModes = [
2339
+ ExecutionMode.YOLO,
2340
+ ExecutionMode.ACCEPT_EDITS,
2341
+ ExecutionMode.PLAN,
2342
+ ExecutionMode.SMART,
2343
+ ];
1886
2344
  async execute(params) {
1887
2345
  const { task_id, poll_interval = 10 } = params;
1888
2346
  try {
@@ -1892,7 +2350,7 @@ export class ReadBashOutputTool {
1892
2350
  throw new Error(`Task ${task_id} not found`);
1893
2351
  }
1894
2352
  const interval = Math.min(Math.max(poll_interval, 1), 120);
1895
- await new Promise(resolve => setTimeout(resolve, interval * 1000));
2353
+ await new Promise((resolve) => setTimeout(resolve, interval * 1000));
1896
2354
  const duration = Date.now() - task.startTime;
1897
2355
  const output = task.output.join('');
1898
2356
  const status = task.process.exitCode === null ? 'running' : 'completed';
@@ -1900,7 +2358,7 @@ export class ReadBashOutputTool {
1900
2358
  taskId: task_id,
1901
2359
  output,
1902
2360
  status,
1903
- duration: Math.floor(duration / 1000)
2361
+ duration: Math.floor(duration / 1000),
1904
2362
  };
1905
2363
  }
1906
2364
  catch (error) {
@@ -1936,7 +2394,12 @@ export class WebFetchTool {
1936
2394
  - Use specific prompts to extract relevant information
1937
2395
  - Check if the page is accessible if you get errors
1938
2396
  - Large pages may be truncated due to size limits`;
1939
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2397
+ allowedModes = [
2398
+ ExecutionMode.YOLO,
2399
+ ExecutionMode.ACCEPT_EDITS,
2400
+ ExecutionMode.PLAN,
2401
+ ExecutionMode.SMART,
2402
+ ];
1940
2403
  async execute(params) {
1941
2404
  const { prompt } = params;
1942
2405
  try {
@@ -1948,7 +2411,7 @@ export class WebFetchTool {
1948
2411
  const response = await axios.get(url, {
1949
2412
  timeout: 30000,
1950
2413
  maxContentLength: 10 * 1024 * 1024,
1951
- validateStatus: () => true
2414
+ validateStatus: () => true,
1952
2415
  });
1953
2416
  let content = response.data;
1954
2417
  if (typeof content === 'object') {
@@ -1957,7 +2420,7 @@ export class WebFetchTool {
1957
2420
  return {
1958
2421
  content,
1959
2422
  url,
1960
- status: response.status
2423
+ status: response.status,
1961
2424
  };
1962
2425
  }
1963
2426
  catch (error) {
@@ -1998,8 +2461,26 @@ export class AskUserQuestionTool {
1998
2461
  - Provide options when possible for faster response
1999
2462
  - Use multiSelect=true when multiple answers are valid
2000
2463
  - Be clear and concise in question wording`;
2001
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2464
+ allowedModes = [
2465
+ ExecutionMode.YOLO,
2466
+ ExecutionMode.ACCEPT_EDITS,
2467
+ ExecutionMode.PLAN,
2468
+ ExecutionMode.SMART,
2469
+ ];
2002
2470
  async execute(params) {
2471
+ // Check if in SDK mode
2472
+ const sdkMode = this._sdkMode;
2473
+ const sdkAdapter = this._sdkOutputAdapter;
2474
+ if (sdkMode && sdkAdapter) {
2475
+ return this.executeSdk(params, sdkAdapter);
2476
+ }
2477
+ // Regular TUI mode
2478
+ return this.executeTui(params);
2479
+ }
2480
+ /**
2481
+ * Execute in TUI mode using @clack/prompts
2482
+ */
2483
+ async executeTui(params) {
2003
2484
  const { questions } = params;
2004
2485
  try {
2005
2486
  if (questions.length === 0 || questions.length > 4) {
@@ -2008,26 +2489,18 @@ export class AskUserQuestionTool {
2008
2489
  const answers = [];
2009
2490
  for (const q of questions) {
2010
2491
  if (q.options && q.options.length > 0) {
2011
- const result = await inquirer.prompt([
2012
- {
2013
- type: q.multiSelect ? 'checkbox' : 'list',
2014
- name: 'answer',
2015
- message: q.question,
2016
- choices: q.options,
2017
- default: q.multiSelect ? [] : q.options[0]
2018
- }
2019
- ]);
2020
- answers.push(Array.isArray(result.answer) ? result.answer.join(', ') : result.answer);
2492
+ const options = q.options.map((opt) => ({ value: opt, label: opt }));
2493
+ const result = await select({
2494
+ message: q.question,
2495
+ options,
2496
+ });
2497
+ answers.push(result);
2021
2498
  }
2022
2499
  else {
2023
- const result = await inquirer.prompt([
2024
- {
2025
- type: 'input',
2026
- name: 'answer',
2027
- message: q.question
2028
- }
2029
- ]);
2030
- answers.push(result.answer);
2500
+ const result = (await text({
2501
+ message: q.question,
2502
+ }));
2503
+ answers.push(result);
2031
2504
  }
2032
2505
  }
2033
2506
  return { answers };
@@ -2036,6 +2509,38 @@ export class AskUserQuestionTool {
2036
2509
  throw new Error(`Failed to ask user questions: ${error.message}`);
2037
2510
  }
2038
2511
  }
2512
+ /**
2513
+ * Execute in SDK mode - output question request and wait for response
2514
+ */
2515
+ async executeSdk(params, sdkAdapter) {
2516
+ const { questions } = params;
2517
+ if (questions.length === 0 || questions.length > 4) {
2518
+ throw new Error('Must provide 1-4 questions');
2519
+ }
2520
+ const requestId = `question_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
2521
+ // Output question request through SDK adapter
2522
+ sdkAdapter.outputQuestionRequest({
2523
+ requestId,
2524
+ questions
2525
+ });
2526
+ // Wait for SDK question response
2527
+ // The response will be handled by session.ts which has access to the SDK input
2528
+ // For now, we use a polling mechanism or wait for a specific event
2529
+ try {
2530
+ // Import the session to get response handling
2531
+ const { getSingletonSession } = await import('./session.js');
2532
+ const session = getSingletonSession();
2533
+ if (!session) {
2534
+ throw new Error('SDK session not available');
2535
+ }
2536
+ const answers = await session.waitForQuestionResponse(requestId);
2537
+ sdkAdapter.outputQuestionResponse(requestId, answers);
2538
+ return { answers };
2539
+ }
2540
+ catch (error) {
2541
+ throw new Error(`Failed to get SDK question response: ${error.message}`);
2542
+ }
2543
+ }
2039
2544
  }
2040
2545
  export class SaveMemoryTool {
2041
2546
  name = 'save_memory';
@@ -2066,7 +2571,12 @@ export class SaveMemoryTool {
2066
2571
  - Keep facts concise and specific
2067
2572
  - Remember project-specific conventions for consistency
2068
2573
  - This persists across sessions (global memory)`;
2069
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2574
+ allowedModes = [
2575
+ ExecutionMode.YOLO,
2576
+ ExecutionMode.ACCEPT_EDITS,
2577
+ ExecutionMode.PLAN,
2578
+ ExecutionMode.SMART,
2579
+ ];
2070
2580
  async execute(params) {
2071
2581
  const { fact } = params;
2072
2582
  try {
@@ -2075,7 +2585,7 @@ export class SaveMemoryTool {
2075
2585
  await memoryManager.saveMemory(fact, 'global');
2076
2586
  return {
2077
2587
  success: true,
2078
- message: `Successfully saved fact to memory`
2588
+ message: `Successfully saved fact to memory`,
2079
2589
  };
2080
2590
  }
2081
2591
  catch (error) {
@@ -2111,14 +2621,19 @@ export class ExitPlanModeTool {
2111
2621
  - Include all necessary steps and considerations
2112
2622
  - The plan will be saved for reference during execution
2113
2623
  - Use this only when truly ready to start coding`;
2114
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2624
+ allowedModes = [
2625
+ ExecutionMode.YOLO,
2626
+ ExecutionMode.ACCEPT_EDITS,
2627
+ ExecutionMode.PLAN,
2628
+ ExecutionMode.SMART,
2629
+ ];
2115
2630
  async execute(params) {
2116
2631
  const { plan } = params;
2117
2632
  try {
2118
2633
  return {
2119
2634
  success: true,
2120
2635
  message: 'Plan completed and ready for execution',
2121
- plan
2636
+ plan,
2122
2637
  };
2123
2638
  }
2124
2639
  catch (error) {
@@ -2162,7 +2677,7 @@ export class XmlEscapeTool {
2162
2677
  { char: '<', replacement: '&lt;' },
2163
2678
  { char: '>', replacement: '&gt;' },
2164
2679
  { char: '"', replacement: '&quot;' },
2165
- { char: "'", replacement: '&apos;' }
2680
+ { char: "'", replacement: '&apos;' },
2166
2681
  ];
2167
2682
  let changes = 0;
2168
2683
  for (const { char, replacement } of specialChars) {
@@ -2177,7 +2692,7 @@ export class XmlEscapeTool {
2177
2692
  const additionalChars = [
2178
2693
  { char: '©', replacement: '&copy;' },
2179
2694
  { char: '®', replacement: '&reg;' },
2180
- { char: '€', replacement: '&euro;' }
2695
+ { char: '€', replacement: '&euro;' },
2181
2696
  ];
2182
2697
  for (const { char, replacement } of additionalChars) {
2183
2698
  const regex = new RegExp(this.escapeRegExp(char), 'g');
@@ -2192,7 +2707,7 @@ export class XmlEscapeTool {
2192
2707
  return {
2193
2708
  success: true,
2194
2709
  message: `Successfully escaped ${changes} character(s) in ${file_path}`,
2195
- changes
2710
+ changes,
2196
2711
  };
2197
2712
  }
2198
2713
  catch (error) {
@@ -2232,7 +2747,12 @@ export class ImageReadTool {
2232
2747
  - Provide clear prompts for what to look for
2233
2748
  - Use task_brief for context
2234
2749
  - Supports PNG, JPG, GIF, WEBP, SVG, BMP`;
2235
- allowedModes = [ExecutionMode.YOLO, ExecutionMode.ACCEPT_EDITS, ExecutionMode.PLAN, ExecutionMode.SMART];
2750
+ allowedModes = [
2751
+ ExecutionMode.YOLO,
2752
+ ExecutionMode.ACCEPT_EDITS,
2753
+ ExecutionMode.PLAN,
2754
+ ExecutionMode.SMART,
2755
+ ];
2236
2756
  async execute(params) {
2237
2757
  const { image_input, prompt, task_brief, input_type = 'file_path', mime_type } = params;
2238
2758
  try {
@@ -2245,16 +2765,11 @@ export class ImageReadTool {
2245
2765
  else {
2246
2766
  imageData = image_input;
2247
2767
  }
2248
- const { AIClient } = await import('./ai-client.js');
2249
2768
  const configManager = await import('./config.js');
2250
2769
  const { getConfigManager } = configManager;
2251
2770
  const config = getConfigManager();
2252
- const aiClient = new AIClient({
2253
- type: AuthType.OPENAI_COMPATIBLE,
2254
- apiKey: config.get('apiKey'),
2255
- baseUrl: config.get('baseUrl'),
2256
- modelName: config.get('modelName') || 'Qwen3-Coder'
2257
- });
2771
+ const authConfig = config.getAuthConfig();
2772
+ const aiClient = createAIClient(authConfig);
2258
2773
  const textContent = task_brief ? `${task_brief}\n\n${prompt}` : prompt;
2259
2774
  const messages = [
2260
2775
  {
@@ -2262,19 +2777,19 @@ export class ImageReadTool {
2262
2777
  content: [
2263
2778
  {
2264
2779
  type: 'text',
2265
- text: textContent
2780
+ text: textContent,
2266
2781
  },
2267
2782
  {
2268
2783
  type: 'image_url',
2269
2784
  image_url: {
2270
- url: `data:${mime_type || 'image/jpeg'};base64,${imageData}`
2271
- }
2272
- }
2273
- ]
2274
- }
2785
+ url: `data:${mime_type || 'image/jpeg'};base64,${imageData}`,
2786
+ },
2787
+ },
2788
+ ],
2789
+ },
2275
2790
  ];
2276
2791
  const result = await aiClient.chatCompletion(messages, {
2277
- temperature: 0.7
2792
+ temperature: 0.7,
2278
2793
  });
2279
2794
  const messageContent = result.choices[0]?.message?.content;
2280
2795
  const analysis = typeof messageContent === 'string' ? messageContent : '';
@@ -2283,8 +2798,8 @@ export class ImageReadTool {
2283
2798
  image_info: {
2284
2799
  input_type,
2285
2800
  prompt,
2286
- task_brief
2287
- }
2801
+ task_brief,
2802
+ },
2288
2803
  };
2289
2804
  }
2290
2805
  catch (error) {
@@ -2365,7 +2880,6 @@ export class InvokeSkillTool {
2365
2880
  const { skillId, taskDescription, inputFile, outputFile, options } = params;
2366
2881
  try {
2367
2882
  const { getSkillInvoker } = await import('./skill-invoker.js');
2368
- const { SkillExecutionParams } = await import('./skill-invoker.js');
2369
2883
  const skillInvoker = getSkillInvoker();
2370
2884
  await skillInvoker.initialize();
2371
2885
  // Verify skill exists
@@ -2380,7 +2894,7 @@ export class InvokeSkillTool {
2380
2894
  taskDescription,
2381
2895
  inputFile,
2382
2896
  outputFile,
2383
- options
2897
+ options,
2384
2898
  });
2385
2899
  if (result.success) {
2386
2900
  return {
@@ -2390,7 +2904,7 @@ export class InvokeSkillTool {
2390
2904
  task: taskDescription,
2391
2905
  result: result.output,
2392
2906
  files: result.files,
2393
- nextSteps: result.nextSteps
2907
+ nextSteps: result.nextSteps,
2394
2908
  };
2395
2909
  }
2396
2910
  else {
@@ -2404,7 +2918,7 @@ export class InvokeSkillTool {
2404
2918
  taskDescription,
2405
2919
  inputFile,
2406
2920
  outputFile,
2407
- options
2921
+ options,
2408
2922
  });
2409
2923
  if (result.success) {
2410
2924
  return {
@@ -2414,7 +2928,8 @@ export class InvokeSkillTool {
2414
2928
  task: taskDescription,
2415
2929
  result: result.output,
2416
2930
  files: result.files,
2417
- nextSteps: result.nextSteps
2931
+ nextSteps: result.nextSteps,
2932
+ skillPath: result.skillPath
2418
2933
  };
2419
2934
  }
2420
2935
  else {
@@ -2495,6 +3010,8 @@ export class ToolRegistry {
2495
3010
  tools = new Map();
2496
3011
  todoWriteTool;
2497
3012
  backgroundTasks = new Map();
3013
+ _isSdkMode = false;
3014
+ _sdkOutputAdapter = null;
2498
3015
  constructor() {
2499
3016
  this.todoWriteTool = new TodoWriteTool();
2500
3017
  this.registerDefaultTools();
@@ -2542,7 +3059,8 @@ export class ToolRegistry {
2542
3059
  let registeredCount = 0;
2543
3060
  for (const [fullName, tool] of mcpTools) {
2544
3061
  const firstUnderscoreIndex = fullName.indexOf('__');
2545
- if (firstUnderscoreIndex === -1 || firstUnderscoreIndex === 0 ||
3062
+ if (firstUnderscoreIndex === -1 ||
3063
+ firstUnderscoreIndex === 0 ||
2546
3064
  firstUnderscoreIndex === fullName.length - 2)
2547
3065
  continue;
2548
3066
  const serverName = fullName.substring(0, firstUnderscoreIndex);
@@ -2577,17 +3095,41 @@ export class ToolRegistry {
2577
3095
  const { getMCPManager } = await import('./mcp.js');
2578
3096
  const mcpManager = getMCPManager();
2579
3097
  return await mcpManager.callTool(fullName, params);
2580
- }
3098
+ },
2581
3099
  };
2582
3100
  this.tools.set(toolName, mcpTool);
2583
3101
  registeredCount++;
2584
3102
  if (toolName !== originalName) {
2585
- console.log(`[MCP] Tool '${originalName}' renamed to '${toolName}' to avoid conflict`);
3103
+ // SDK 模式下不输出重命名信息
3104
+ if (!this._isSdkMode) {
3105
+ console.log(`[MCP] Tool '${originalName}' renamed to '${toolName}' to avoid conflict`);
3106
+ }
2586
3107
  }
2587
3108
  }
2588
3109
  }
2589
3110
  if (registeredCount > 0) {
2590
- console.log(`[MCP] Registered ${registeredCount} tool(s)`);
3111
+ // 在 SDK 模式下不输出注册信息(MCP 相关输出已在 session 中处理)
3112
+ if (!this._isSdkMode) {
3113
+ console.log(`[MCP] Registered ${registeredCount} tool(s)`);
3114
+ }
3115
+ }
3116
+ }
3117
+ /**
3118
+ * Set SDK mode for the tool registry.
3119
+ * In SDK mode, tool execution output is sent to the SDK output adapter.
3120
+ */
3121
+ async setSdkMode(enabled, adapter) {
3122
+ this._isSdkMode = enabled;
3123
+ this._sdkOutputAdapter = adapter;
3124
+ // Mark all tools as SDK mode enabled
3125
+ for (const [, tool] of this.tools) {
3126
+ tool._sdkMode = enabled;
3127
+ tool._sdkOutputAdapter = adapter;
3128
+ }
3129
+ // Initialize SDK mode for TaskTool specifically
3130
+ const taskTool = this.tools.get('task');
3131
+ if (taskTool) {
3132
+ await taskTool.setSdkMode?.(enabled, adapter);
2591
3133
  }
2592
3134
  }
2593
3135
  /**
@@ -2621,11 +3163,11 @@ export class ToolRegistry {
2621
3163
  this.backgroundTasks.delete(taskId);
2622
3164
  }
2623
3165
  getToolDefinitions() {
2624
- return Array.from(this.tools.values()).map(tool => {
3166
+ return Array.from(this.tools.values()).map((tool) => {
2625
3167
  let parameters = {
2626
3168
  type: 'object',
2627
3169
  properties: {},
2628
- required: []
3170
+ required: [],
2629
3171
  };
2630
3172
  // Define specific parameters for each tool
2631
3173
  switch (tool.name) {
@@ -2635,18 +3177,18 @@ export class ToolRegistry {
2635
3177
  properties: {
2636
3178
  filePath: {
2637
3179
  type: 'string',
2638
- description: 'The absolute path to the file to read'
3180
+ description: 'The absolute path to the file to read',
2639
3181
  },
2640
3182
  offset: {
2641
3183
  type: 'number',
2642
- description: 'Optional: Line number to start reading from (0-based)'
3184
+ description: 'Optional: Line number to start reading from (0-based)',
2643
3185
  },
2644
3186
  limit: {
2645
3187
  type: 'number',
2646
- description: 'Optional: Maximum number of lines to read'
2647
- }
3188
+ description: 'Optional: Maximum number of lines to read',
3189
+ },
2648
3190
  },
2649
- required: ['filePath']
3191
+ required: ['filePath'],
2650
3192
  };
2651
3193
  break;
2652
3194
  case 'Write':
@@ -2655,14 +3197,14 @@ export class ToolRegistry {
2655
3197
  properties: {
2656
3198
  filePath: {
2657
3199
  type: 'string',
2658
- description: 'The absolute path to the file to write'
3200
+ description: 'The absolute path to the file to write',
2659
3201
  },
2660
3202
  content: {
2661
3203
  type: 'string',
2662
- description: 'The content to write to the file'
2663
- }
3204
+ description: 'The content to write to the file',
3205
+ },
2664
3206
  },
2665
- required: ['filePath', 'content']
3207
+ required: ['filePath', 'content'],
2666
3208
  };
2667
3209
  break;
2668
3210
  case 'Grep':
@@ -2671,26 +3213,34 @@ export class ToolRegistry {
2671
3213
  properties: {
2672
3214
  pattern: {
2673
3215
  type: 'string',
2674
- description: 'The regex pattern to search for'
3216
+ description: 'The regex pattern or literal string to search for',
2675
3217
  },
2676
3218
  path: {
2677
3219
  type: 'string',
2678
- description: 'Optional: The path to search in (default: current directory)'
3220
+ description: 'Optional: The path to search in (default: current directory)',
2679
3221
  },
2680
- include: {
3222
+ glob: {
2681
3223
  type: 'string',
2682
- description: 'Optional: Glob pattern to filter files'
3224
+ description: 'Optional: Glob pattern to filter files (e.g., "*.ts", "**/*.js")',
2683
3225
  },
2684
- case_sensitive: {
3226
+ ignoreCase: {
2685
3227
  type: 'boolean',
2686
- description: 'Optional: Case-sensitive search (default: false)'
3228
+ description: 'Optional: Case-insensitive search (default: false)',
3229
+ },
3230
+ literal: {
3231
+ type: 'boolean',
3232
+ description: 'Optional: Treat pattern as literal string (default: false)',
2687
3233
  },
2688
3234
  context: {
2689
3235
  type: 'number',
2690
- description: 'Optional: Number of context lines to show'
2691
- }
3236
+ description: 'Optional: Number of context lines to show before and after',
3237
+ },
3238
+ limit: {
3239
+ type: 'number',
3240
+ description: 'Optional: Maximum number of matches to return',
3241
+ },
2692
3242
  },
2693
- required: ['pattern']
3243
+ required: ['pattern'],
2694
3244
  };
2695
3245
  break;
2696
3246
  case 'Bash':
@@ -2699,26 +3249,26 @@ export class ToolRegistry {
2699
3249
  properties: {
2700
3250
  command: {
2701
3251
  type: 'string',
2702
- description: 'The shell command to execute'
3252
+ description: 'The shell command to execute',
2703
3253
  },
2704
3254
  cwd: {
2705
3255
  type: 'string',
2706
- description: 'Optional: Working directory'
3256
+ description: 'Optional: Working directory',
2707
3257
  },
2708
3258
  description: {
2709
3259
  type: 'string',
2710
- description: 'Optional: Brief description of the command'
3260
+ description: 'Optional: Brief description of the command',
2711
3261
  },
2712
3262
  timeout: {
2713
3263
  type: 'number',
2714
- description: 'Optional: Timeout in seconds (default: 120)'
3264
+ description: 'Optional: Timeout in seconds (default: 120)',
2715
3265
  },
2716
3266
  run_in_bg: {
2717
3267
  type: 'boolean',
2718
- description: 'Optional: Run in background (default: false)'
2719
- }
3268
+ description: 'Optional: Run in background (default: false)',
3269
+ },
2720
3270
  },
2721
- required: ['command']
3271
+ required: ['command'],
2722
3272
  };
2723
3273
  break;
2724
3274
  case 'ListDirectory':
@@ -2727,14 +3277,14 @@ export class ToolRegistry {
2727
3277
  properties: {
2728
3278
  path: {
2729
3279
  type: 'string',
2730
- description: 'Optional: The directory path to list (default: current directory)'
3280
+ description: 'Optional: The directory path to list (default: current directory)',
2731
3281
  },
2732
3282
  recursive: {
2733
3283
  type: 'boolean',
2734
- description: 'Optional: List recursively (default: false)'
2735
- }
3284
+ description: 'Optional: List recursively (default: false)',
3285
+ },
2736
3286
  },
2737
- required: []
3287
+ required: [],
2738
3288
  };
2739
3289
  break;
2740
3290
  case 'SearchFiles':
@@ -2743,18 +3293,18 @@ export class ToolRegistry {
2743
3293
  properties: {
2744
3294
  pattern: {
2745
3295
  type: 'string',
2746
- description: 'The glob pattern to match files'
3296
+ description: 'The glob pattern to match files',
2747
3297
  },
2748
3298
  path: {
2749
3299
  type: 'string',
2750
- description: 'Optional: The path to search in (default: current directory)'
3300
+ description: 'Optional: The path to search in (default: current directory)',
2751
3301
  },
2752
3302
  limit: {
2753
3303
  type: 'integer',
2754
- description: 'Optional: Maximum number of results to return (default: 1000)'
2755
- }
3304
+ description: 'Optional: Maximum number of results to return (default: 1000)',
3305
+ },
2756
3306
  },
2757
- required: ['pattern']
3307
+ required: ['pattern'],
2758
3308
  };
2759
3309
  break;
2760
3310
  case 'DeleteFile':
@@ -2763,10 +3313,10 @@ export class ToolRegistry {
2763
3313
  properties: {
2764
3314
  filePath: {
2765
3315
  type: 'string',
2766
- description: 'The path to the file to delete'
2767
- }
3316
+ description: 'The path to the file to delete',
3317
+ },
2768
3318
  },
2769
- required: ['filePath']
3319
+ required: ['filePath'],
2770
3320
  };
2771
3321
  break;
2772
3322
  case 'CreateDirectory':
@@ -2775,14 +3325,14 @@ export class ToolRegistry {
2775
3325
  properties: {
2776
3326
  dirPath: {
2777
3327
  type: 'string',
2778
- description: 'The directory path to create'
3328
+ description: 'The directory path to create',
2779
3329
  },
2780
3330
  recursive: {
2781
3331
  type: 'boolean',
2782
- description: 'Optional: Create parent directories (default: true)'
2783
- }
3332
+ description: 'Optional: Create parent directories (default: true)',
3333
+ },
2784
3334
  },
2785
- required: ['dirPath']
3335
+ required: ['dirPath'],
2786
3336
  };
2787
3337
  break;
2788
3338
  case 'Edit':
@@ -2791,22 +3341,22 @@ export class ToolRegistry {
2791
3341
  properties: {
2792
3342
  file_path: {
2793
3343
  type: 'string',
2794
- description: 'The absolute path to the file to edit'
3344
+ description: 'The absolute path to the file to edit',
2795
3345
  },
2796
3346
  instruction: {
2797
3347
  type: 'string',
2798
- description: 'Description of what needs to be changed'
3348
+ description: 'Description of what needs to be changed',
2799
3349
  },
2800
3350
  old_string: {
2801
3351
  type: 'string',
2802
- description: 'The exact text to replace (supports fuzzy matching)'
3352
+ description: 'The exact text to replace (supports fuzzy matching)',
2803
3353
  },
2804
3354
  new_string: {
2805
3355
  type: 'string',
2806
- description: 'The new text to replace with'
2807
- }
3356
+ description: 'The new text to replace with',
3357
+ },
2808
3358
  },
2809
- required: ['file_path', 'instruction', 'old_string', 'new_string']
3359
+ required: ['file_path', 'instruction', 'old_string', 'new_string'],
2810
3360
  };
2811
3361
  break;
2812
3362
  case 'web_search':
@@ -2815,10 +3365,10 @@ export class ToolRegistry {
2815
3365
  properties: {
2816
3366
  query: {
2817
3367
  type: 'string',
2818
- description: 'The search query'
2819
- }
3368
+ description: 'The search query',
3369
+ },
2820
3370
  },
2821
- required: ['query']
3371
+ required: ['query'],
2822
3372
  };
2823
3373
  break;
2824
3374
  case 'todo_write':
@@ -2833,21 +3383,24 @@ export class ToolRegistry {
2833
3383
  properties: {
2834
3384
  id: { type: 'string' },
2835
3385
  task: { type: 'string' },
2836
- status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'failed'] },
2837
- priority: { type: 'string', enum: ['high', 'medium', 'low'] }
3386
+ status: {
3387
+ type: 'string',
3388
+ enum: ['pending', 'in_progress', 'completed', 'failed'],
3389
+ },
3390
+ priority: { type: 'string', enum: ['high', 'medium', 'low'] },
2838
3391
  },
2839
- required: ['id', 'task', 'status']
2840
- }
2841
- }
3392
+ required: ['id', 'task', 'status'],
3393
+ },
3394
+ },
2842
3395
  },
2843
- required: ['todos']
3396
+ required: ['todos'],
2844
3397
  };
2845
3398
  break;
2846
3399
  case 'todo_read':
2847
3400
  parameters = {
2848
3401
  type: 'object',
2849
3402
  properties: {},
2850
- required: []
3403
+ required: [],
2851
3404
  };
2852
3405
  break;
2853
3406
  case 'task':
@@ -2856,7 +3409,7 @@ export class ToolRegistry {
2856
3409
  properties: {
2857
3410
  description: {
2858
3411
  type: 'string',
2859
- description: 'Brief description of the task (3-5 words)'
3412
+ description: 'Brief description of the task (3-5 words)',
2860
3413
  },
2861
3414
  agents: {
2862
3415
  type: 'array',
@@ -2866,50 +3419,66 @@ export class ToolRegistry {
2866
3419
  properties: {
2867
3420
  description: {
2868
3421
  type: 'string',
2869
- description: 'Brief description of the sub-agent task'
3422
+ description: 'Brief description of the sub-agent task',
2870
3423
  },
2871
3424
  prompt: {
2872
3425
  type: 'string',
2873
- description: 'The task for the sub-agent to perform'
3426
+ description: 'The task for the sub-agent to perform',
2874
3427
  },
2875
3428
  subagent_type: {
2876
3429
  type: 'string',
2877
- enum: ['general-purpose', 'plan-agent', 'explore-agent', 'frontend-tester', 'code-reviewer', 'frontend-developer', 'backend-developer'],
2878
- description: 'The type of specialized agent'
3430
+ enum: [
3431
+ 'general-purpose',
3432
+ 'plan-agent',
3433
+ 'explore-agent',
3434
+ 'frontend-tester',
3435
+ 'code-reviewer',
3436
+ 'frontend-developer',
3437
+ 'backend-developer',
3438
+ ],
3439
+ description: 'The type of specialized agent',
2879
3440
  },
2880
3441
  constraints: {
2881
3442
  type: 'array',
2882
3443
  items: { type: 'string' },
2883
- description: 'Optional: Constraints or limitations'
2884
- }
3444
+ description: 'Optional: Constraints or limitations',
3445
+ },
2885
3446
  },
2886
- required: ['description', 'prompt', 'subagent_type']
2887
- }
3447
+ required: ['description', 'prompt', 'subagent_type'],
3448
+ },
2888
3449
  },
2889
3450
  prompt: {
2890
3451
  type: 'string',
2891
- description: 'Optional: The task for the agent to perform (use agents for parallel execution)'
3452
+ description: 'Optional: The task for the agent to perform (use agents for parallel execution)',
2892
3453
  },
2893
3454
  subagent_type: {
2894
3455
  type: 'string',
2895
- enum: ['general-purpose', 'plan-agent', 'explore-agent', 'frontend-tester', 'code-reviewer', 'frontend-developer', 'backend-developer'],
2896
- description: 'Optional: The type of specialized agent (use agents for parallel execution)'
3456
+ enum: [
3457
+ 'general-purpose',
3458
+ 'plan-agent',
3459
+ 'explore-agent',
3460
+ 'frontend-tester',
3461
+ 'code-reviewer',
3462
+ 'frontend-developer',
3463
+ 'backend-developer',
3464
+ ],
3465
+ description: 'Optional: The type of specialized agent (use agents for parallel execution)',
2897
3466
  },
2898
3467
  useContext: {
2899
3468
  type: 'boolean',
2900
- description: 'Optional: Include main agent context'
3469
+ description: 'Optional: Include main agent context',
2901
3470
  },
2902
3471
  outputFormat: {
2903
3472
  type: 'string',
2904
- description: 'Optional: Output format template'
3473
+ description: 'Optional: Output format template',
2905
3474
  },
2906
3475
  constraints: {
2907
3476
  type: 'array',
2908
3477
  items: { type: 'string' },
2909
- description: 'Optional: Constraints or limitations'
2910
- }
3478
+ description: 'Optional: Constraints or limitations',
3479
+ },
2911
3480
  },
2912
- required: ['description']
3481
+ required: ['description'],
2913
3482
  };
2914
3483
  break;
2915
3484
  case 'ReadBashOutput':
@@ -2918,14 +3487,14 @@ export class ToolRegistry {
2918
3487
  properties: {
2919
3488
  task_id: {
2920
3489
  type: 'string',
2921
- description: 'The ID of the task'
3490
+ description: 'The ID of the task',
2922
3491
  },
2923
3492
  poll_interval: {
2924
3493
  type: 'number',
2925
- description: 'Optional: Polling interval in seconds (default: 10)'
2926
- }
3494
+ description: 'Optional: Polling interval in seconds (default: 10)',
3495
+ },
2927
3496
  },
2928
- required: ['task_id']
3497
+ required: ['task_id'],
2929
3498
  };
2930
3499
  break;
2931
3500
  case 'web_fetch':
@@ -2934,10 +3503,10 @@ export class ToolRegistry {
2934
3503
  properties: {
2935
3504
  prompt: {
2936
3505
  type: 'string',
2937
- description: 'Prompt containing URL(s) and processing instructions'
2938
- }
3506
+ description: 'Prompt containing URL(s) and processing instructions',
3507
+ },
2939
3508
  },
2940
- required: ['prompt']
3509
+ required: ['prompt'],
2941
3510
  };
2942
3511
  break;
2943
3512
  case 'ask_user_question':
@@ -2955,15 +3524,15 @@ export class ToolRegistry {
2955
3524
  options: {
2956
3525
  type: 'array',
2957
3526
  items: { type: 'string' },
2958
- description: 'Available choices (2-4 options)'
3527
+ description: 'Available choices (2-4 options)',
2959
3528
  },
2960
- multiSelect: { type: 'boolean' }
3529
+ multiSelect: { type: 'boolean' },
2961
3530
  },
2962
- required: ['question', 'header', 'options', 'multiSelect']
2963
- }
2964
- }
3531
+ required: ['question', 'header', 'options', 'multiSelect'],
3532
+ },
3533
+ },
2965
3534
  },
2966
- required: ['questions']
3535
+ required: ['questions'],
2967
3536
  };
2968
3537
  break;
2969
3538
  case 'save_memory':
@@ -2972,10 +3541,10 @@ export class ToolRegistry {
2972
3541
  properties: {
2973
3542
  fact: {
2974
3543
  type: 'string',
2975
- description: 'The specific fact to remember'
2976
- }
3544
+ description: 'The specific fact to remember',
3545
+ },
2977
3546
  },
2978
- required: ['fact']
3547
+ required: ['fact'],
2979
3548
  };
2980
3549
  break;
2981
3550
  case 'exit_plan_mode':
@@ -2984,10 +3553,10 @@ export class ToolRegistry {
2984
3553
  properties: {
2985
3554
  plan: {
2986
3555
  type: 'string',
2987
- description: 'The plan to present'
2988
- }
3556
+ description: 'The plan to present',
3557
+ },
2989
3558
  },
2990
- required: ['plan']
3559
+ required: ['plan'],
2991
3560
  };
2992
3561
  break;
2993
3562
  case 'xml_escape':
@@ -2996,14 +3565,14 @@ export class ToolRegistry {
2996
3565
  properties: {
2997
3566
  file_path: {
2998
3567
  type: 'string',
2999
- description: 'The absolute path to the XML/HTML file'
3568
+ description: 'The absolute path to the XML/HTML file',
3000
3569
  },
3001
3570
  escape_all: {
3002
3571
  type: 'boolean',
3003
- description: 'Optional: Escape all special characters (default: false)'
3004
- }
3572
+ description: 'Optional: Escape all special characters (default: false)',
3573
+ },
3005
3574
  },
3006
- required: ['file_path']
3575
+ required: ['file_path'],
3007
3576
  };
3008
3577
  break;
3009
3578
  case 'image_read':
@@ -3012,27 +3581,27 @@ export class ToolRegistry {
3012
3581
  properties: {
3013
3582
  image_input: {
3014
3583
  type: 'string',
3015
- description: 'Image file path or base64 data'
3584
+ description: 'Image file path or base64 data',
3016
3585
  },
3017
3586
  prompt: {
3018
3587
  type: 'string',
3019
- description: 'Comprehensive VLM instruction'
3588
+ description: 'Comprehensive VLM instruction',
3020
3589
  },
3021
3590
  task_brief: {
3022
3591
  type: 'string',
3023
- description: 'Brief task description (max 15 words)'
3592
+ description: 'Brief task description (max 15 words)',
3024
3593
  },
3025
3594
  input_type: {
3026
3595
  type: 'string',
3027
3596
  enum: ['file_path', 'base64'],
3028
- description: 'Input type (default: file_path)'
3597
+ description: 'Input type (default: file_path)',
3029
3598
  },
3030
3599
  mime_type: {
3031
3600
  type: 'string',
3032
- description: 'Optional: MIME type for base64 input'
3033
- }
3601
+ description: 'Optional: MIME type for base64 input',
3602
+ },
3034
3603
  },
3035
- required: ['image_input', 'prompt']
3604
+ required: ['image_input', 'prompt'],
3036
3605
  };
3037
3606
  break;
3038
3607
  case 'Skill':
@@ -3041,17 +3610,17 @@ export class ToolRegistry {
3041
3610
  properties: {
3042
3611
  skill: {
3043
3612
  type: 'string',
3044
- description: 'The skill name to execute'
3045
- }
3613
+ description: 'The skill name to execute',
3614
+ },
3046
3615
  },
3047
- required: ['skill']
3616
+ required: ['skill'],
3048
3617
  };
3049
3618
  break;
3050
3619
  case 'ListSkills':
3051
3620
  parameters = {
3052
3621
  type: 'object',
3053
3622
  properties: {},
3054
- required: []
3623
+ required: [],
3055
3624
  };
3056
3625
  break;
3057
3626
  case 'GetSkillDetails':
@@ -3060,13 +3629,13 @@ export class ToolRegistry {
3060
3629
  properties: {
3061
3630
  skill: {
3062
3631
  type: 'string',
3063
- description: 'The skill name/id to get details for'
3064
- }
3632
+ description: 'The skill name/id to get details for',
3633
+ },
3065
3634
  },
3066
- required: ['skill']
3635
+ required: ['skill'],
3067
3636
  };
3068
3637
  break;
3069
- default:
3638
+ default: {
3070
3639
  // For MCP tools, use their inputSchema; for other unknown tools, keep empty schema
3071
3640
  const mcpTool = tool;
3072
3641
  if (mcpTool._isMcpTool && mcpTool.inputSchema) {
@@ -3074,13 +3643,13 @@ export class ToolRegistry {
3074
3643
  parameters = {
3075
3644
  type: 'object',
3076
3645
  properties: {},
3077
- required: []
3646
+ required: [],
3078
3647
  };
3079
3648
  if (mcpTool.inputSchema.properties) {
3080
3649
  for (const [paramName, paramDef] of Object.entries(mcpTool.inputSchema.properties)) {
3081
3650
  parameters.properties[paramName] = {
3082
3651
  type: paramDef.type || 'string',
3083
- description: paramDef.description || ''
3652
+ description: paramDef.description || '',
3084
3653
  };
3085
3654
  }
3086
3655
  }
@@ -3092,17 +3661,18 @@ export class ToolRegistry {
3092
3661
  parameters = {
3093
3662
  type: 'object',
3094
3663
  properties: {},
3095
- required: []
3664
+ required: [],
3096
3665
  };
3097
3666
  }
3667
+ }
3098
3668
  }
3099
3669
  return {
3100
3670
  type: 'function',
3101
3671
  function: {
3102
3672
  name: tool.name,
3103
3673
  description: tool.description,
3104
- parameters
3105
- }
3674
+ parameters,
3675
+ },
3106
3676
  };
3107
3677
  });
3108
3678
  }
@@ -3121,14 +3691,14 @@ export class ToolRegistry {
3121
3691
  return await this.executeMCPTool(toolName, params, executionMode, indent);
3122
3692
  }
3123
3693
  // Try to find MCP tool with just the tool name (try each server)
3124
- for (const [fullName, tool] of allMcpTools) {
3694
+ for (const [fullName, _tool] of allMcpTools) {
3125
3695
  // Split only on the first __ to preserve underscores in tool names
3126
3696
  const firstUnderscoreIndex = fullName.indexOf('__');
3127
3697
  if (firstUnderscoreIndex === -1)
3128
3698
  continue;
3129
- const [serverName, actualToolName] = [
3699
+ const [_serverName, actualToolName] = [
3130
3700
  fullName.substring(0, firstUnderscoreIndex),
3131
- fullName.substring(firstUnderscoreIndex + 2)
3701
+ fullName.substring(firstUnderscoreIndex + 2),
3132
3702
  ];
3133
3703
  if (actualToolName === toolName) {
3134
3704
  return await this.executeMCPTool(fullName, params, executionMode, indent);
@@ -3149,6 +3719,8 @@ export class ToolRegistry {
3149
3719
  if (!tool.allowedModes.includes(executionMode)) {
3150
3720
  throw new Error(`Tool ${toolName} is not allowed in ${executionMode} mode`);
3151
3721
  }
3722
+ const isSdkMode = this._isSdkMode;
3723
+ const sdkOutputAdapter = this._sdkOutputAdapter;
3152
3724
  // Smart approval mode
3153
3725
  if (executionMode === ExecutionMode.SMART) {
3154
3726
  const debugMode = process.env.DEBUG === 'smart-approval';
@@ -3169,50 +3741,81 @@ export class ToolRegistry {
3169
3741
  const isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
3170
3742
  if (isRemoteMode && toolName === 'InvokeSkill') {
3171
3743
  console.log('');
3172
- console.log(`${indent}${colors.success(`✅ [Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`)}`);
3173
- console.log('');
3744
+ if (isSdkMode && sdkOutputAdapter) {
3745
+ sdkOutputAdapter.outputInfo(`[Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`);
3746
+ }
3747
+ else {
3748
+ console.log('');
3749
+ console.log(`${indent}${colors.success(`✅ [Smart Mode] Remote mode: tool '${toolName}' auto-approved (remote LLM already approved)`)}`);
3750
+ console.log('');
3751
+ }
3174
3752
  return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
3175
3753
  }
3176
3754
  const { getSmartApprovalEngine } = await import('./smart-approval.js');
3177
3755
  const approvalEngine = getSmartApprovalEngine(debugMode);
3756
+ // Set SDK mode for approval engine if in SDK mode
3757
+ if (isSdkMode && sdkOutputAdapter) {
3758
+ approvalEngine.setSdkMode(true, sdkOutputAdapter);
3759
+ }
3178
3760
  // Evaluate tool call
3179
3761
  const result = await approvalEngine.evaluate({
3180
3762
  toolName,
3181
3763
  params,
3182
- timestamp: Date.now()
3764
+ timestamp: Date.now(),
3183
3765
  });
3184
3766
  // Decide whether to execute based on approval result
3185
3767
  if (result.decision === 'approved') {
3186
3768
  // Whitelist or AI approval passed, execute directly
3187
- console.log('');
3188
- console.log(`${indent}${colors.success(`✅ [Smart Mode] Tool '${toolName}' passed approval, executing directly`)}`);
3189
- console.log(`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`);
3190
- console.log(`${indent}${colors.textDim(` Latency: ${result.latency}ms`)}`);
3191
- console.log('');
3769
+ if (isSdkMode && sdkOutputAdapter) {
3770
+ sdkOutputAdapter.outputInfo(`[Smart Mode] Tool '${toolName}' passed approval, executing directly`);
3771
+ sdkOutputAdapter.outputInfo(`Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}, Latency: ${result.latency}ms`);
3772
+ }
3773
+ else {
3774
+ console.log('');
3775
+ console.log(`${indent}${colors.success(`✅ [Smart Mode] Tool '${toolName}' passed approval, executing directly`)}`);
3776
+ console.log(`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`);
3777
+ console.log(`${indent}${colors.textDim(` Latency: ${result.latency}ms`)}`);
3778
+ console.log('');
3779
+ }
3192
3780
  return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
3193
3781
  }
3194
3782
  else if (result.decision === 'requires_confirmation') {
3195
3783
  // Requires user confirmation
3196
- const confirmed = await approvalEngine.requestConfirmation(result);
3784
+ const confirmed = await approvalEngine.requestConfirmation(result, toolName, params);
3197
3785
  if (confirmed) {
3198
- console.log('');
3199
- console.log(`${indent}${colors.success(`✅ [Smart Mode] User confirmed execution of tool '${toolName}'`)}`);
3200
- console.log('');
3786
+ if (isSdkMode && sdkOutputAdapter) {
3787
+ sdkOutputAdapter.outputInfo(`[Smart Mode] User confirmed execution of tool '${toolName}'`);
3788
+ }
3789
+ else {
3790
+ console.log('');
3791
+ console.log(`${indent}${colors.success(`✅ [Smart Mode] User confirmed execution of tool '${toolName}'`)}`);
3792
+ console.log('');
3793
+ }
3201
3794
  return await cancellationManager.withCancellation(tool.execute(params, executionMode), `tool-${toolName}`);
3202
3795
  }
3203
3796
  else {
3204
- console.log('');
3205
- console.log(`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled execution of tool '${toolName}'`)}`);
3206
- console.log('');
3797
+ if (isSdkMode && sdkOutputAdapter) {
3798
+ sdkOutputAdapter.outputWarning(`[Smart Mode] User cancelled execution of tool '${toolName}'`);
3799
+ }
3800
+ else {
3801
+ console.log('');
3802
+ console.log(`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled execution of tool '${toolName}'`)}`);
3803
+ console.log('');
3804
+ }
3207
3805
  throw new Error(`Tool execution cancelled by user: ${toolName}`);
3208
3806
  }
3209
3807
  }
3210
3808
  else {
3211
3809
  // Rejected execution
3212
- console.log('');
3213
- console.log(`${indent}${colors.error(`❌ [Smart Mode] Tool '${toolName}' execution rejected`)}`);
3214
- console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
3215
- console.log('');
3810
+ if (isSdkMode && sdkOutputAdapter) {
3811
+ sdkOutputAdapter.outputError(`[Smart Mode] Tool '${toolName}' execution rejected`, { reason: result.description });
3812
+ }
3813
+ else {
3814
+ console.log('');
3815
+ console.log(`${indent}${colors.error(`❌ [Smart Mode] Tool '${toolName}' execution rejected`)}`);
3816
+ console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
3817
+ console.log('');
3818
+ }
3216
3819
  throw new Error(`Tool execution rejected: ${toolName}`);
3217
3820
  }
3218
3821
  }
@@ -3232,10 +3835,14 @@ export class ToolRegistry {
3232
3835
  const actualToolName = toolName.substring(firstUnderscoreIndex + 2);
3233
3836
  // Get server info for display
3234
3837
  const server = mcpManager.getServer(serverName);
3235
- const serverTools = server?.getToolNames() || [];
3838
+ const _serverTools = server?.getToolNames() || [];
3839
+ const isSdkMode = this._isSdkMode;
3840
+ const sdkOutputAdapter = this._sdkOutputAdapter;
3236
3841
  // Display tool call info
3237
- console.log('');
3238
- console.log(`${indent}${colors.warning(`${icons.tool} MCP Tool Call: ${serverName}::${actualToolName}`)}`);
3842
+ if (!isSdkMode || !sdkOutputAdapter) {
3843
+ console.log('');
3844
+ console.log(`${indent}${colors.warning(`${icons.tool} MCP Tool Call: ${serverName}::${actualToolName}`)}`);
3845
+ }
3239
3846
  // Smart approval mode for MCP tools
3240
3847
  if (executionMode === ExecutionMode.SMART) {
3241
3848
  const debugMode = process.env.DEBUG === 'smart-approval';
@@ -3246,30 +3853,55 @@ export class ToolRegistry {
3246
3853
  const isRemoteMode = authConfig.type === AuthType.OAUTH_XAGENT;
3247
3854
  // Remote mode: remote LLM has already approved the tool, auto-approve
3248
3855
  if (isRemoteMode) {
3249
- console.log(`${indent}${colors.success(`✅ [Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`)}`);
3856
+ if (isSdkMode && sdkOutputAdapter) {
3857
+ sdkOutputAdapter.outputInfo(`[Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`);
3858
+ }
3859
+ else {
3860
+ console.log(`${indent}${colors.success(`✅ [Smart Mode] Remote mode: MCP tool '${serverName}::${actualToolName}' auto-approved`)}`);
3861
+ }
3250
3862
  }
3251
3863
  else {
3252
3864
  const approvalEngine = getSmartApprovalEngine(debugMode);
3865
+ // Set SDK mode for approval engine if in SDK mode
3866
+ if (isSdkMode && sdkOutputAdapter) {
3867
+ approvalEngine.setSdkMode(true, sdkOutputAdapter);
3868
+ }
3253
3869
  // Evaluate MCP tool call
3254
3870
  const result = await approvalEngine.evaluate({
3255
3871
  toolName: `MCP[${serverName}]::${actualToolName}`,
3256
3872
  params,
3257
- timestamp: Date.now()
3873
+ timestamp: Date.now(),
3258
3874
  });
3259
3875
  if (result.decision === 'approved') {
3260
- console.log(`${indent}${colors.success(`✅ [Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`)}`);
3261
- console.log(`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`);
3876
+ if (isSdkMode && sdkOutputAdapter) {
3877
+ sdkOutputAdapter.outputInfo(`[Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`);
3878
+ sdkOutputAdapter.outputInfo(`Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`);
3879
+ }
3880
+ else {
3881
+ console.log(`${indent}${colors.success(`✅ [Smart Mode] MCP tool '${serverName}::${actualToolName}' passed approval`)}`);
3882
+ console.log(`${indent}${colors.textDim(` Detection method: ${result.detectionMethod === 'whitelist' ? 'Whitelist' : 'AI Review'}`)}`);
3883
+ }
3262
3884
  }
3263
3885
  else if (result.decision === 'requires_confirmation') {
3264
- const confirmed = await approvalEngine.requestConfirmation(result);
3886
+ const confirmed = await approvalEngine.requestConfirmation(result, `MCP[${serverName}]::${actualToolName}`, params);
3265
3887
  if (!confirmed) {
3266
- console.log(`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled MCP tool execution`)}`);
3888
+ if (isSdkMode && sdkOutputAdapter) {
3889
+ sdkOutputAdapter.outputWarning(`[Smart Mode] User cancelled MCP tool execution`);
3890
+ }
3891
+ else {
3892
+ console.log(`${indent}${colors.warning(`⚠️ [Smart Mode] User cancelled MCP tool execution`)}`);
3893
+ }
3267
3894
  throw new Error(`Tool execution cancelled by user: ${toolName}`);
3268
3895
  }
3269
3896
  }
3270
3897
  else {
3271
- console.log(`${indent}${colors.error(`❌ [Smart Mode] MCP tool execution rejected`)}`);
3272
- console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
3898
+ if (isSdkMode && sdkOutputAdapter) {
3899
+ sdkOutputAdapter.outputError(`[Smart Mode] MCP tool execution rejected`, { reason: result.description });
3900
+ }
3901
+ else {
3902
+ console.log(`${indent}${colors.error(`❌ [Smart Mode] MCP tool execution rejected`)}`);
3903
+ console.log(`${indent}${colors.textDim(` Reason: ${result.description}`)}`);
3904
+ }
3273
3905
  throw new Error(`Tool execution rejected: ${toolName}`);
3274
3906
  }
3275
3907
  }