@xagent-ai/cli 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (568) 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/workflows/ci.yml +72 -0
  4. package/.github/workflows/release.yml +109 -0
  5. package/.gitmodules +3 -3
  6. package/README.md +326 -280
  7. package/README_CN.md +325 -279
  8. package/dist/ai-client/factory.d.ts +52 -0
  9. package/dist/ai-client/factory.d.ts.map +1 -0
  10. package/dist/ai-client/factory.js +132 -0
  11. package/dist/ai-client/factory.js.map +1 -0
  12. package/dist/ai-client/index.d.ts +20 -0
  13. package/dist/ai-client/index.d.ts.map +1 -0
  14. package/dist/ai-client/index.js +49 -0
  15. package/dist/ai-client/index.js.map +1 -0
  16. package/dist/ai-client/providers/anthropic.d.ts +57 -0
  17. package/dist/ai-client/providers/anthropic.d.ts.map +1 -0
  18. package/dist/ai-client/providers/anthropic.js +400 -0
  19. package/dist/ai-client/providers/anthropic.js.map +1 -0
  20. package/dist/ai-client/providers/openai.d.ts +57 -0
  21. package/dist/ai-client/providers/openai.d.ts.map +1 -0
  22. package/dist/ai-client/providers/openai.js +286 -0
  23. package/dist/ai-client/providers/openai.js.map +1 -0
  24. package/dist/ai-client/providers/remote.d.ts +111 -0
  25. package/dist/ai-client/providers/remote.d.ts.map +1 -0
  26. package/dist/ai-client/providers/remote.js +351 -0
  27. package/dist/ai-client/providers/remote.js.map +1 -0
  28. package/dist/ai-client/registry.d.ts +51 -0
  29. package/dist/ai-client/registry.d.ts.map +1 -0
  30. package/dist/ai-client/registry.js +81 -0
  31. package/dist/ai-client/registry.js.map +1 -0
  32. package/dist/ai-client/types.d.ts +260 -0
  33. package/dist/ai-client/types.d.ts.map +1 -0
  34. package/dist/ai-client/types.js +73 -0
  35. package/dist/ai-client/types.js.map +1 -0
  36. package/dist/ai-client-factory.d.ts +62 -0
  37. package/dist/ai-client-factory.d.ts.map +1 -0
  38. package/dist/ai-client-factory.js +157 -0
  39. package/dist/ai-client-factory.js.map +1 -0
  40. package/dist/auth.d.ts +23 -1
  41. package/dist/auth.d.ts.map +1 -1
  42. package/dist/auth.js +160 -168
  43. package/dist/auth.js.map +1 -1
  44. package/dist/cancellation.d.ts +5 -4
  45. package/dist/cancellation.d.ts.map +1 -1
  46. package/dist/cancellation.js +55 -32
  47. package/dist/cancellation.js.map +1 -1
  48. package/dist/checkpoint.d.ts +1 -1
  49. package/dist/checkpoint.d.ts.map +1 -1
  50. package/dist/checkpoint.js +2 -2
  51. package/dist/checkpoint.js.map +1 -1
  52. package/dist/cli.js +626 -13
  53. package/dist/cli.js.map +1 -1
  54. package/dist/config.d.ts +10 -4
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +62 -25
  57. package/dist/config.js.map +1 -1
  58. package/dist/context-compressor.d.ts +81 -16
  59. package/dist/context-compressor.d.ts.map +1 -1
  60. package/dist/context-compressor.js +712 -153
  61. package/dist/context-compressor.js.map +1 -1
  62. package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -1
  63. package/dist/gui-subagent/action-parser/actionParser.js +4 -2
  64. package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
  65. package/dist/gui-subagent/agent/gui-agent.d.ts +29 -2
  66. package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
  67. package/dist/gui-subagent/agent/gui-agent.js +87 -45
  68. package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
  69. package/dist/gui-subagent/index.d.ts +16 -1
  70. package/dist/gui-subagent/index.d.ts.map +1 -1
  71. package/dist/gui-subagent/index.js +4 -0
  72. package/dist/gui-subagent/index.js.map +1 -1
  73. package/dist/gui-subagent/operator/base-operator.d.ts.map +1 -1
  74. package/dist/gui-subagent/operator/base-operator.js +0 -1
  75. package/dist/gui-subagent/operator/base-operator.js.map +1 -1
  76. package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
  77. package/dist/gui-subagent/operator/computer-operator.js +29 -8
  78. package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
  79. package/dist/gui-subagent/types/actions.d.ts +1 -1
  80. package/dist/gui-subagent/types/actions.d.ts.map +1 -1
  81. package/dist/gui-subagent/types/actions.js +0 -1
  82. package/dist/gui-subagent/types/actions.js.map +1 -1
  83. package/dist/gui-subagent/types/operator.d.ts +1 -1
  84. package/dist/gui-subagent/types/operator.d.ts.map +1 -1
  85. package/dist/index.d.ts +1 -2
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +1 -2
  88. package/dist/index.js.map +1 -1
  89. package/dist/input-processor.d.ts.map +1 -1
  90. package/dist/input-processor.js +6 -3
  91. package/dist/input-processor.js.map +1 -1
  92. package/dist/mcp.d.ts +5 -0
  93. package/dist/mcp.d.ts.map +1 -1
  94. package/dist/mcp.js +81 -35
  95. package/dist/mcp.js.map +1 -1
  96. package/dist/ripgrep.d.ts +29 -0
  97. package/dist/ripgrep.d.ts.map +1 -0
  98. package/dist/ripgrep.js +292 -0
  99. package/dist/ripgrep.js.map +1 -0
  100. package/dist/session.d.ts +23 -7
  101. package/dist/session.d.ts.map +1 -1
  102. package/dist/session.js +624 -243
  103. package/dist/session.js.map +1 -1
  104. package/dist/shell.d.ts +33 -0
  105. package/dist/shell.d.ts.map +1 -0
  106. package/dist/shell.js +125 -0
  107. package/dist/shell.js.map +1 -0
  108. package/dist/skill-installer.d.ts +38 -0
  109. package/dist/skill-installer.d.ts.map +1 -0
  110. package/dist/skill-installer.js +447 -0
  111. package/dist/skill-installer.js.map +1 -0
  112. package/dist/skill-invoker.d.ts +7 -1
  113. package/dist/skill-invoker.d.ts.map +1 -1
  114. package/dist/skill-invoker.js +34 -13
  115. package/dist/skill-invoker.js.map +1 -1
  116. package/dist/skill-loader.d.ts +8 -3
  117. package/dist/skill-loader.d.ts.map +1 -1
  118. package/dist/skill-loader.js +46 -44
  119. package/dist/skill-loader.js.map +1 -1
  120. package/dist/skill-manager.d.ts +85 -0
  121. package/dist/skill-manager.d.ts.map +1 -0
  122. package/dist/skill-manager.js +340 -0
  123. package/dist/skill-manager.js.map +1 -0
  124. package/dist/slash-commands.d.ts +38 -1
  125. package/dist/slash-commands.d.ts.map +1 -1
  126. package/dist/slash-commands.js +912 -296
  127. package/dist/slash-commands.js.map +1 -1
  128. package/dist/smart-approval.d.ts.map +1 -1
  129. package/dist/smart-approval.js +67 -55
  130. package/dist/smart-approval.js.map +1 -1
  131. package/dist/system-prompt-generator.d.ts +6 -0
  132. package/dist/system-prompt-generator.d.ts.map +1 -1
  133. package/dist/system-prompt-generator.js +84 -34
  134. package/dist/system-prompt-generator.js.map +1 -1
  135. package/dist/terminal.d.ts +28 -0
  136. package/dist/terminal.d.ts.map +1 -0
  137. package/dist/terminal.js +82 -0
  138. package/dist/terminal.js.map +1 -0
  139. package/dist/tools.d.ts +23 -7
  140. package/dist/tools.d.ts.map +1 -1
  141. package/dist/tools.js +797 -437
  142. package/dist/tools.js.map +1 -1
  143. package/dist/truncate.d.ts +55 -0
  144. package/dist/truncate.d.ts.map +1 -0
  145. package/dist/truncate.js +130 -0
  146. package/dist/truncate.js.map +1 -0
  147. package/dist/types.d.ts +27 -9
  148. package/dist/types.d.ts.map +1 -1
  149. package/dist/update.d.ts.map +1 -1
  150. package/dist/update.js +17 -28
  151. package/dist/update.js.map +1 -1
  152. package/dist/workflow.d.ts +5 -1
  153. package/dist/workflow.d.ts.map +1 -1
  154. package/dist/workflow.js +60 -47
  155. package/dist/workflow.js.map +1 -1
  156. package/docs/architecture/mcp-integration-guide.md +304 -194
  157. package/docs/architecture/overview.md +169 -169
  158. package/docs/architecture/tool-system-design.md +134 -134
  159. package/docs/cli/commands.md +349 -238
  160. package/docs/smart-mode.md +281 -281
  161. package/docs/third-party-models.md +439 -439
  162. package/find-skills/SKILL.md +133 -0
  163. package/package.json +89 -90
  164. package/scripts/install-ripgrep.js +241 -0
  165. package/src/ai-client/factory.ts +151 -0
  166. package/src/ai-client/index.ts +61 -0
  167. package/src/ai-client/providers/anthropic.ts +466 -0
  168. package/src/ai-client/providers/openai.ts +342 -0
  169. package/src/ai-client/providers/remote.ts +436 -0
  170. package/src/ai-client/registry.ts +97 -0
  171. package/src/ai-client/types.ts +345 -0
  172. package/src/ai-client-factory.ts +204 -0
  173. package/src/auth.ts +663 -614
  174. package/src/cancellation.ts +205 -176
  175. package/src/checkpoint.ts +219 -219
  176. package/src/cli.ts +1406 -743
  177. package/src/config.ts +341 -297
  178. package/src/context-compressor.ts +982 -290
  179. package/src/conversation.ts +288 -288
  180. package/src/gui-subagent/action-parser/actionParser.ts +318 -315
  181. package/src/gui-subagent/action-parser/constants.ts +14 -14
  182. package/src/gui-subagent/action-parser/index.ts +8 -8
  183. package/src/gui-subagent/action-parser/types.ts +31 -31
  184. package/src/gui-subagent/agent/gui-agent.ts +1151 -1089
  185. package/src/gui-subagent/agent/index.ts +5 -5
  186. package/src/gui-subagent/index.ts +177 -163
  187. package/src/gui-subagent/operator/base-operator.ts +244 -245
  188. package/src/gui-subagent/operator/computer-operator.ts +540 -520
  189. package/src/gui-subagent/operator/index.ts +6 -6
  190. package/src/gui-subagent/types/actions.ts +260 -262
  191. package/src/gui-subagent/types/index.ts +6 -6
  192. package/src/gui-subagent/types/operator.ts +106 -106
  193. package/src/gui-subagent/utils.ts +51 -51
  194. package/src/index.ts +17 -18
  195. package/src/input-processor.ts +6 -3
  196. package/src/logger.ts +438 -438
  197. package/src/mcp.ts +730 -682
  198. package/src/memory.ts +344 -344
  199. package/src/ripgrep.ts +368 -0
  200. package/src/session-manager.ts +308 -308
  201. package/src/session.ts +948 -386
  202. package/src/shell.ts +133 -0
  203. package/src/skill-installer.ts +518 -0
  204. package/src/skill-invoker.ts +960 -935
  205. package/src/skill-loader.ts +501 -496
  206. package/src/skill-manager.ts +384 -0
  207. package/src/slash-commands.ts +2181 -1389
  208. package/src/smart-approval.ts +117 -73
  209. package/src/system-prompt-generator.ts +89 -34
  210. package/src/terminal.ts +96 -0
  211. package/src/theme.ts +738 -738
  212. package/src/tools.ts +1336 -773
  213. package/src/truncate.ts +173 -0
  214. package/src/types.ts +219 -198
  215. package/src/update.ts +22 -32
  216. package/src/workflow.ts +523 -508
  217. package/tsconfig.json +22 -22
  218. package/vitest.config.ts +19 -19
  219. package/dist/ai-client.d.ts +0 -86
  220. package/dist/ai-client.d.ts.map +0 -1
  221. package/dist/ai-client.js +0 -1372
  222. package/dist/ai-client.js.map +0 -1
  223. package/dist/gui-subagent/operator/browser-operator.d.ts +0 -36
  224. package/dist/gui-subagent/operator/browser-operator.d.ts.map +0 -1
  225. package/dist/gui-subagent/operator/browser-operator.js +0 -306
  226. package/dist/gui-subagent/operator/browser-operator.js.map +0 -1
  227. package/dist/gui-subagent/operator/desktop-operator.d.ts +0 -55
  228. package/dist/gui-subagent/operator/desktop-operator.d.ts.map +0 -1
  229. package/dist/gui-subagent/operator/desktop-operator.js +0 -527
  230. package/dist/gui-subagent/operator/desktop-operator.js.map +0 -1
  231. package/dist/hook.d.ts +0 -73
  232. package/dist/hook.d.ts.map +0 -1
  233. package/dist/hook.js +0 -156
  234. package/dist/hook.js.map +0 -1
  235. package/dist/input-history.d.ts +0 -24
  236. package/dist/input-history.d.ts.map +0 -1
  237. package/dist/input-history.js +0 -94
  238. package/dist/input-history.js.map +0 -1
  239. package/dist/keyboard-manager.d.ts +0 -151
  240. package/dist/keyboard-manager.d.ts.map +0 -1
  241. package/dist/keyboard-manager.js +0 -396
  242. package/dist/keyboard-manager.js.map +0 -1
  243. package/dist/print-system-prompt.d.ts +0 -2
  244. package/dist/print-system-prompt.d.ts.map +0 -1
  245. package/dist/print-system-prompt.js +0 -40
  246. package/dist/print-system-prompt.js.map +0 -1
  247. package/dist/remote-ai-client.d.ts +0 -104
  248. package/dist/remote-ai-client.d.ts.map +0 -1
  249. package/dist/remote-ai-client.js +0 -552
  250. package/dist/remote-ai-client.js.map +0 -1
  251. package/dist/sdk-output-adapter.d.ts +0 -232
  252. package/dist/sdk-output-adapter.d.ts.map +0 -1
  253. package/dist/sdk-output-adapter.js +0 -636
  254. package/dist/sdk-output-adapter.js.map +0 -1
  255. package/dist/sdk-session-v2.d.ts +0 -13
  256. package/dist/sdk-session-v2.d.ts.map +0 -1
  257. package/dist/sdk-session-v2.js +0 -46
  258. package/dist/sdk-session-v2.js.map +0 -1
  259. package/dist/sdk-session.d.ts +0 -13
  260. package/dist/sdk-session.d.ts.map +0 -1
  261. package/dist/sdk-session.js +0 -48
  262. package/dist/sdk-session.js.map +0 -1
  263. package/dist/test-boundary-conditions.d.ts.map +0 -1
  264. package/dist/test-boundary-conditions.js.map +0 -1
  265. package/dist/test-cancellation-fix.d.ts.map +0 -1
  266. package/dist/test-cancellation-fix.js.map +0 -1
  267. package/dist/test-input-history.d.ts.map +0 -1
  268. package/dist/test-input-history.js.map +0 -1
  269. package/dist/test-interaction-flow.d.ts.map +0 -1
  270. package/dist/test-interaction-flow.js.map +0 -1
  271. package/dist/test-quick.d.ts.map +0 -1
  272. package/dist/test-quick.js.map +0 -1
  273. package/dist/test-user-interaction.d.ts.map +0 -1
  274. package/dist/test-user-interaction.js.map +0 -1
  275. package/dist/tools/edit-diff.d.ts +0 -32
  276. package/dist/tools/edit-diff.d.ts.map +0 -1
  277. package/dist/tools/edit-diff.js +0 -185
  278. package/dist/tools/edit-diff.js.map +0 -1
  279. package/dist/tools/edit.d.ts +0 -11
  280. package/dist/tools/edit.d.ts.map +0 -1
  281. package/dist/tools/edit.js +0 -129
  282. package/dist/tools/edit.js.map +0 -1
  283. package/dist/unified-session.d.ts +0 -42
  284. package/dist/unified-session.d.ts.map +0 -1
  285. package/dist/unified-session.js +0 -271
  286. package/dist/unified-session.js.map +0 -1
  287. package/skills/.claude-plugin/marketplace.json +0 -45
  288. package/skills/README.md +0 -94
  289. package/skills/THIRD_PARTY_NOTICES.md +0 -405
  290. package/skills/skills/algorithmic-art/LICENSE.txt +0 -202
  291. package/skills/skills/algorithmic-art/SKILL.md +0 -405
  292. package/skills/skills/algorithmic-art/templates/generator_template.js +0 -223
  293. package/skills/skills/algorithmic-art/templates/viewer.html +0 -599
  294. package/skills/skills/brand-guidelines/LICENSE.txt +0 -202
  295. package/skills/skills/brand-guidelines/SKILL.md +0 -73
  296. package/skills/skills/canvas-design/LICENSE.txt +0 -202
  297. package/skills/skills/canvas-design/SKILL.md +0 -130
  298. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
  299. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  300. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  301. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
  302. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  303. package/skills/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
  304. package/skills/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  305. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  306. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
  307. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  308. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  309. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  310. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
  311. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  312. package/skills/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
  313. package/skills/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  314. package/skills/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
  315. package/skills/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  316. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  317. package/skills/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
  318. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  319. package/skills/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
  320. package/skills/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  321. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  322. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
  323. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  324. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  325. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  326. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  327. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  328. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  329. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  330. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  331. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
  332. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  333. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  334. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  335. package/skills/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
  336. package/skills/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  337. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  338. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
  339. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  340. package/skills/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  341. package/skills/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  342. package/skills/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
  343. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
  344. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  345. package/skills/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  346. package/skills/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  347. package/skills/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  348. package/skills/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
  349. package/skills/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  350. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  351. package/skills/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
  352. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  353. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
  354. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  355. package/skills/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  356. package/skills/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
  357. package/skills/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  358. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  359. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
  360. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
  361. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  362. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  363. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
  364. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  365. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
  366. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  367. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  368. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
  369. package/skills/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  370. package/skills/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
  371. package/skills/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  372. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  373. package/skills/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  374. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  375. package/skills/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
  376. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  377. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
  378. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  379. package/skills/skills/doc-coauthoring/SKILL.md +0 -375
  380. package/skills/skills/docx/LICENSE.txt +0 -30
  381. package/skills/skills/docx/SKILL.md +0 -197
  382. package/skills/skills/docx/docx-js.md +0 -350
  383. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  384. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  385. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  386. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  387. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  388. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  389. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  390. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  391. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  392. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  393. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  394. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  395. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  396. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  397. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  398. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  399. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  400. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  401. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  402. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  403. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  404. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  405. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  406. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  407. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  408. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  409. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  410. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  411. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  412. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  413. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  414. package/skills/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  415. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  416. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  417. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  418. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  419. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  420. package/skills/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  421. package/skills/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  422. package/skills/skills/docx/ooxml/scripts/pack.py +0 -159
  423. package/skills/skills/docx/ooxml/scripts/unpack.py +0 -29
  424. package/skills/skills/docx/ooxml/scripts/validate.py +0 -69
  425. package/skills/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  426. package/skills/skills/docx/ooxml/scripts/validation/base.py +0 -951
  427. package/skills/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  428. package/skills/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  429. package/skills/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  430. package/skills/skills/docx/ooxml.md +0 -610
  431. package/skills/skills/docx/scripts/__init__.py +0 -1
  432. package/skills/skills/docx/scripts/document.py +0 -1276
  433. package/skills/skills/docx/scripts/templates/comments.xml +0 -3
  434. package/skills/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  435. package/skills/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  436. package/skills/skills/docx/scripts/templates/commentsIds.xml +0 -3
  437. package/skills/skills/docx/scripts/templates/people.xml +0 -3
  438. package/skills/skills/docx/scripts/utilities.py +0 -374
  439. package/skills/skills/frontend-design/LICENSE.txt +0 -177
  440. package/skills/skills/frontend-design/SKILL.md +0 -42
  441. package/skills/skills/internal-comms/LICENSE.txt +0 -202
  442. package/skills/skills/internal-comms/SKILL.md +0 -32
  443. package/skills/skills/internal-comms/examples/3p-updates.md +0 -47
  444. package/skills/skills/internal-comms/examples/company-newsletter.md +0 -65
  445. package/skills/skills/internal-comms/examples/faq-answers.md +0 -30
  446. package/skills/skills/internal-comms/examples/general-comms.md +0 -16
  447. package/skills/skills/mcp-builder/LICENSE.txt +0 -202
  448. package/skills/skills/mcp-builder/SKILL.md +0 -236
  449. package/skills/skills/mcp-builder/reference/evaluation.md +0 -602
  450. package/skills/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  451. package/skills/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  452. package/skills/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  453. package/skills/skills/mcp-builder/scripts/connections.py +0 -151
  454. package/skills/skills/mcp-builder/scripts/evaluation.py +0 -373
  455. package/skills/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  456. package/skills/skills/mcp-builder/scripts/requirements.txt +0 -2
  457. package/skills/skills/pdf/LICENSE.txt +0 -30
  458. package/skills/skills/pdf/SKILL.md +0 -294
  459. package/skills/skills/pdf/forms.md +0 -205
  460. package/skills/skills/pdf/reference.md +0 -612
  461. package/skills/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  462. package/skills/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  463. package/skills/skills/pdf/scripts/check_fillable_fields.py +0 -12
  464. package/skills/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  465. package/skills/skills/pdf/scripts/create_validation_image.py +0 -41
  466. package/skills/skills/pdf/scripts/extract_form_field_info.py +0 -152
  467. package/skills/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  468. package/skills/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  469. package/skills/skills/pptx/LICENSE.txt +0 -30
  470. package/skills/skills/pptx/SKILL.md +0 -484
  471. package/skills/skills/pptx/html2pptx.md +0 -625
  472. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  473. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  474. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  475. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  476. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  477. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  478. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  479. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  480. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  481. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  482. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  483. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  484. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  485. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  486. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  487. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  488. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  489. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  490. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  491. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  492. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  493. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  494. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  495. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  496. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  497. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  498. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  499. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  500. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  501. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  502. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  503. package/skills/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
  504. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  505. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  506. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  507. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  508. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  509. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  510. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  511. package/skills/skills/pptx/ooxml/scripts/pack.py +0 -159
  512. package/skills/skills/pptx/ooxml/scripts/unpack.py +0 -29
  513. package/skills/skills/pptx/ooxml/scripts/validate.py +0 -69
  514. package/skills/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
  515. package/skills/skills/pptx/ooxml/scripts/validation/base.py +0 -951
  516. package/skills/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
  517. package/skills/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
  518. package/skills/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
  519. package/skills/skills/pptx/ooxml.md +0 -427
  520. package/skills/skills/pptx/scripts/html2pptx.js +0 -979
  521. package/skills/skills/pptx/scripts/inventory.py +0 -1020
  522. package/skills/skills/pptx/scripts/rearrange.py +0 -231
  523. package/skills/skills/pptx/scripts/replace.py +0 -385
  524. package/skills/skills/pptx/scripts/thumbnail.py +0 -450
  525. package/skills/skills/skill-creator/LICENSE.txt +0 -202
  526. package/skills/skills/skill-creator/SKILL.md +0 -356
  527. package/skills/skills/skill-creator/references/output-patterns.md +0 -82
  528. package/skills/skills/skill-creator/references/workflows.md +0 -28
  529. package/skills/skills/skill-creator/scripts/init_skill.py +0 -303
  530. package/skills/skills/skill-creator/scripts/package_skill.py +0 -110
  531. package/skills/skills/skill-creator/scripts/quick_validate.py +0 -95
  532. package/skills/skills/slack-gif-creator/LICENSE.txt +0 -202
  533. package/skills/skills/slack-gif-creator/SKILL.md +0 -254
  534. package/skills/skills/slack-gif-creator/core/easing.py +0 -234
  535. package/skills/skills/slack-gif-creator/core/frame_composer.py +0 -176
  536. package/skills/skills/slack-gif-creator/core/gif_builder.py +0 -269
  537. package/skills/skills/slack-gif-creator/core/validators.py +0 -136
  538. package/skills/skills/slack-gif-creator/requirements.txt +0 -4
  539. package/skills/skills/theme-factory/LICENSE.txt +0 -202
  540. package/skills/skills/theme-factory/SKILL.md +0 -59
  541. package/skills/skills/theme-factory/theme-showcase.pdf +0 -0
  542. package/skills/skills/theme-factory/themes/arctic-frost.md +0 -19
  543. package/skills/skills/theme-factory/themes/botanical-garden.md +0 -19
  544. package/skills/skills/theme-factory/themes/desert-rose.md +0 -19
  545. package/skills/skills/theme-factory/themes/forest-canopy.md +0 -19
  546. package/skills/skills/theme-factory/themes/golden-hour.md +0 -19
  547. package/skills/skills/theme-factory/themes/midnight-galaxy.md +0 -19
  548. package/skills/skills/theme-factory/themes/modern-minimalist.md +0 -19
  549. package/skills/skills/theme-factory/themes/ocean-depths.md +0 -19
  550. package/skills/skills/theme-factory/themes/sunset-boulevard.md +0 -19
  551. package/skills/skills/theme-factory/themes/tech-innovation.md +0 -19
  552. package/skills/skills/web-artifacts-builder/LICENSE.txt +0 -202
  553. package/skills/skills/web-artifacts-builder/SKILL.md +0 -74
  554. package/skills/skills/web-artifacts-builder/scripts/bundle-artifact.sh +0 -54
  555. package/skills/skills/web-artifacts-builder/scripts/init-artifact.sh +0 -322
  556. package/skills/skills/webapp-testing/LICENSE.txt +0 -202
  557. package/skills/skills/webapp-testing/SKILL.md +0 -96
  558. package/skills/skills/webapp-testing/examples/console_logging.py +0 -35
  559. package/skills/skills/webapp-testing/examples/element_discovery.py +0 -40
  560. package/skills/skills/webapp-testing/examples/static_html_automation.py +0 -33
  561. package/skills/skills/webapp-testing/scripts/with_server.py +0 -106
  562. package/skills/skills/xlsx/LICENSE.txt +0 -30
  563. package/skills/skills/xlsx/SKILL.md +0 -289
  564. package/skills/skills/xlsx/recalc.py +0 -178
  565. package/skills/spec/agent-skills-spec.md +0 -3
  566. package/skills/template/SKILL.md +0 -6
  567. package/src/ai-client.ts +0 -1560
  568. package/src/remote-ai-client.ts +0 -664
package/src/session.ts CHANGED
@@ -4,16 +4,28 @@ import https from 'https';
4
4
  import axios from 'axios';
5
5
  import crypto from 'crypto';
6
6
  import ora from 'ora';
7
- import inquirer from 'inquirer';
8
7
  import { createRequire } from 'module';
9
- import { dirname, join } from 'path';
8
+ import path from 'path';
10
9
  import { fileURLToPath } from 'url';
10
+ import fs from 'fs';
11
+ import fsPromises from 'fs/promises';
12
+ import os from 'os';
11
13
 
12
14
  const require = createRequire(import.meta.url);
13
15
  const packageJson = require('../package.json');
14
- import { ExecutionMode, ChatMessage, ToolCall, AuthType } from './types.js';
15
- import { AIClient, Message, detectThinkingKeywords, getThinkingTokens } from './ai-client.js';
16
- import { RemoteAIClient, TokenInvalidError } from './remote-ai-client.js';
16
+ import {
17
+ ExecutionMode,
18
+ ChatMessage,
19
+ ToolCall,
20
+ AuthType,
21
+ AuthConfig,
22
+ AgentConfig,
23
+ ToolCallItem,
24
+ } from './types.js';
25
+ import { createAIClient, type AIClientInterface } from './ai-client-factory.js';
26
+ import { detectThinkingKeywords, getThinkingTokens } from './ai-client/types.js';
27
+ import { TokenInvalidError } from './ai-client/types.js';
28
+ import { fetchDefaultModels } from './ai-client/providers/remote.js';
17
29
  import { getConfigManager, ConfigManager } from './config.js';
18
30
  import { AuthService, selectAuthType } from './auth.js';
19
31
  import { getToolRegistry } from './tools.js';
@@ -25,10 +37,28 @@ import { getConversationManager, ConversationManager } from './conversation.js';
25
37
  import { getSessionManager, SessionManager } from './session-manager.js';
26
38
  import { SlashCommandHandler, parseInput, detectImageInput } from './slash-commands.js';
27
39
  import { SystemPromptGenerator } from './system-prompt-generator.js';
28
- import { theme, icons, colors, styleHelpers, renderMarkdown, renderDiff, renderLines, TERMINAL_BG } from './theme.js';
40
+ import {
41
+ theme,
42
+ icons,
43
+ colors,
44
+ styleHelpers,
45
+ renderMarkdown,
46
+ renderDiff,
47
+ renderLines,
48
+ TERMINAL_BG,
49
+ } from './theme.js';
29
50
  import { getCancellationManager, CancellationManager } from './cancellation.js';
30
- import { getContextCompressor, ContextCompressor, CompressionResult } from './context-compressor.js';
51
+ import {
52
+ getContextCompressor,
53
+ ContextCompressor,
54
+ CompressionResult,
55
+ } from './context-compressor.js';
31
56
  import { Logger, LogLevel, getLogger } from './logger.js';
57
+ import { ensureTtySane, setupEscKeyHandler } from './terminal.js';
58
+
59
+ // Type aliases for backward compatibility
60
+ type AIClient = AIClientInterface;
61
+ type RemoteAIClient = AIClientInterface;
32
62
 
33
63
  const logger = getLogger();
34
64
 
@@ -39,7 +69,7 @@ export class InteractiveSession {
39
69
  private aiClient: AIClient | null = null;
40
70
  private remoteAIClient: RemoteAIClient | null = null;
41
71
  private conversation: ChatMessage[] = [];
42
- private toolCalls: ToolCall[] = [];
72
+ private tool_calls: ToolCall[] = [];
43
73
  private executionMode: ExecutionMode;
44
74
  private slashCommandHandler: SlashCommandHandler;
45
75
  private configManager: ConfigManager;
@@ -47,7 +77,7 @@ export class InteractiveSession {
47
77
  private memoryManager: MemoryManager;
48
78
  private mcpManager: MCPManager;
49
79
  private checkpointManager: CheckpointManager;
50
- private currentAgent: any = null;
80
+ private currentAgent: AgentConfig | null = null;
51
81
  private rl: readline.Interface;
52
82
  private cancellationManager: CancellationManager;
53
83
  private indentLevel: number;
@@ -57,67 +87,51 @@ export class InteractiveSession {
57
87
  private taskCompleted: boolean = false;
58
88
  private isFirstApiCall: boolean = true;
59
89
 
60
- constructor(indentLevel: number = 0) {
61
-
62
- this.rl = readline.createInterface({
63
-
64
- input: process.stdin,
65
-
66
- output: process.stdout
67
-
68
- });
69
-
70
-
71
-
72
- this.configManager = getConfigManager(process.cwd());
73
-
74
- this.agentManager = getAgentManager(process.cwd());
75
-
76
- this.memoryManager = getMemoryManager(process.cwd());
77
-
78
- this.mcpManager = getMCPManager();
79
-
80
- this.checkpointManager = getCheckpointManager(process.cwd());
81
-
82
- this.conversationManager = getConversationManager();
83
-
84
- this.sessionManager = getSessionManager(process.cwd());
85
-
86
- this.slashCommandHandler = new SlashCommandHandler();
87
-
88
- // Register /clear callback, clear local conversation when clearing dialogue
89
- this.slashCommandHandler.setClearCallback(() => {
90
- this.conversation = [];
91
- this.toolCalls = [];
92
- this.currentTaskId = null;
93
- this.taskCompleted = false;
94
- this.isFirstApiCall = true;
95
- this.slashCommandHandler.setConversationHistory([]);
96
- });
97
-
98
-
99
-
100
- // Register MCP update callback, update system prompt
101
-
102
- this.slashCommandHandler.setSystemPromptUpdateCallback(async () => {
103
-
104
- await this.updateSystemPrompt();
90
+ constructor(indentLevel: number = 0) {
91
+ this.rl = readline.createInterface({
92
+ input: process.stdin,
93
+ output: process.stdout,
94
+ });
105
95
 
106
- });
96
+ this.configManager = getConfigManager(process.cwd());
97
+ this.agentManager = getAgentManager(process.cwd());
98
+ this.memoryManager = getMemoryManager(process.cwd());
99
+ this.mcpManager = getMCPManager();
100
+ this.checkpointManager = getCheckpointManager(process.cwd());
101
+ this.conversationManager = getConversationManager();
102
+ this.sessionManager = getSessionManager(process.cwd());
103
+ this.slashCommandHandler = new SlashCommandHandler();
104
+
105
+ // Register /clear callback, clear local conversation when clearing dialogue
106
+ this.slashCommandHandler.setClearCallback(() => {
107
+ this.conversation = [];
108
+ this.tool_calls = [];
109
+ this.currentTaskId = null;
110
+ this.taskCompleted = false;
111
+ this.isFirstApiCall = true;
112
+ this.slashCommandHandler.setConversationHistory([]);
113
+ });
107
114
 
108
-
115
+ // Register MCP update callback, update system prompt
116
+ this.slashCommandHandler.setSystemPromptUpdateCallback(async () => {
117
+ await this.updateSystemPrompt();
118
+ });
109
119
 
110
- this.executionMode = ExecutionMode.DEFAULT;
120
+ // Register config update callback, update aiClient config when /auth changes config
121
+ this.slashCommandHandler.setConfigUpdateCallback(() => {
122
+ this.updateAiClientConfig();
123
+ });
111
124
 
112
- this.cancellationManager = getCancellationManager();
125
+ this.executionMode = ExecutionMode.DEFAULT;
113
126
 
114
- this.indentLevel = indentLevel;
127
+ this.cancellationManager = getCancellationManager();
115
128
 
116
- this.indentString = ' '.repeat(indentLevel);
129
+ this.indentLevel = indentLevel;
117
130
 
118
- this.contextCompressor = getContextCompressor();
131
+ this.indentString = ' '.repeat(indentLevel);
119
132
 
120
- }
133
+ this.contextCompressor = getContextCompressor();
134
+ }
121
135
 
122
136
  private getIndent(): string {
123
137
  return this.indentString;
@@ -127,6 +141,34 @@ export class InteractiveSession {
127
141
  this.aiClient = aiClient;
128
142
  }
129
143
 
144
+ /**
145
+ * Update aiClient config when /auth changes config (called from callback)
146
+ */
147
+ updateAiClientConfig(): void {
148
+ const authConfig = this.configManager.getAuthConfig();
149
+ const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
150
+
151
+ if (isRemote) {
152
+ // Already in remote mode, no change needed
153
+ if (this.remoteAIClient !== null) {
154
+ return;
155
+ }
156
+ // Switch to remote: clear local client, create remote client
157
+ this.aiClient = null;
158
+ this.remoteAIClient = createAIClient(authConfig);
159
+ } else {
160
+ // Already in local mode, no change needed
161
+ if (this.aiClient !== null) {
162
+ return;
163
+ }
164
+ // Switch to local: clear remote client, create local client
165
+ this.remoteAIClient = null;
166
+ this.aiClient = createAIClient(authConfig);
167
+ }
168
+
169
+ this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
170
+ }
171
+
130
172
  setExecutionMode(mode: ExecutionMode): void {
131
173
  this.executionMode = mode;
132
174
  }
@@ -135,30 +177,88 @@ export class InteractiveSession {
135
177
  * Update system prompt to reflect MCP changes (called after add/remove MCP)
136
178
  */
137
179
  async updateSystemPrompt(): Promise<void> {
180
+ // Reload skills to pick up any newly added/removed skills
181
+ // First reset the SkillLoader to clear all cached skills
182
+ const { resetSkillLoader } = await import('./skill-loader.js');
183
+ resetSkillLoader();
184
+
185
+ // Then reload the skill invoker
186
+ const skillInvoker = (await import('./skill-invoker.js')).getSkillInvoker();
187
+ await skillInvoker.reload();
188
+
138
189
  const toolRegistry = getToolRegistry();
139
- const promptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode, undefined, this.mcpManager);
190
+ const promptGenerator = new SystemPromptGenerator(
191
+ toolRegistry,
192
+ this.executionMode,
193
+ undefined,
194
+ this.mcpManager
195
+ );
140
196
 
141
197
  // Use the current agent's original system prompt as base
142
- const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are xAgent, an AI-powered CLI tool.';
198
+ const baseSystemPrompt =
199
+ this.currentAgent?.systemPrompt || 'You are xAgent, an AI-powered CLI tool.';
143
200
  const newSystemPrompt = await promptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
144
201
 
145
202
  // Replace old system prompt with new one
146
- this.conversation = this.conversation.filter(msg => msg.role !== 'system');
203
+ this.conversation = this.conversation.filter((msg) => msg.role !== 'system');
147
204
  this.conversation.unshift({
148
205
  role: 'system',
149
206
  content: newSystemPrompt,
150
- timestamp: Date.now()
207
+ timestamp: Date.now(),
151
208
  });
152
209
 
153
210
  // Sync to slashCommandHandler
154
211
  this.slashCommandHandler.setConversationHistory(this.conversation);
155
212
  }
156
213
 
157
- setAgent(agent: any): void {
214
+ /**
215
+ * Watch for skill updates from CLI and update system prompt accordingly
216
+ */
217
+ private startSkillUpdateWatcher(): void {
218
+ const SKILL_STATE_FILE = '.skill-state.json';
219
+ const configManager = getConfigManager();
220
+ const userSkillsPath = configManager.getUserSkillsPath();
221
+ const stateFilePath = userSkillsPath
222
+ ? path.join(userSkillsPath, SKILL_STATE_FILE)
223
+ : null;
224
+
225
+ if (!stateFilePath) {
226
+ return; // No user skills path configured
227
+ }
228
+
229
+ let lastUpdateTime = 0;
230
+
231
+ // Check for updates every 2 seconds
232
+ const checkInterval = setInterval(async () => {
233
+ try {
234
+ const { existsSync, readFileSync } = await import('fs');
235
+ if (existsSync(stateFilePath)) {
236
+ const content = readFileSync(stateFilePath, 'utf-8');
237
+ const state = JSON.parse(content);
238
+
239
+ if (state.lastSkillUpdate && state.lastSkillUpdate > lastUpdateTime) {
240
+ lastUpdateTime = state.lastSkillUpdate;
241
+
242
+ // Update system prompt with new skills
243
+ await this.updateSystemPrompt();
244
+
245
+ console.log(colors.textMuted(' 🔄 Skills updated from CLI'));
246
+ }
247
+ }
248
+ } catch (error) {
249
+ // Silent fail - watcher is optional
250
+ }
251
+ }, 2000);
252
+
253
+ // Clean up on session end
254
+ (this as any)._skillWatcherInterval = checkInterval;
255
+ }
256
+
257
+ setAgent(agent: AgentConfig): void {
158
258
  this.currentAgent = agent;
159
259
  }
160
260
 
161
- async start(): Promise<void> {
261
+ async start(initializedCount: number = 0): Promise<void> {
162
262
  // Set this session as the singleton for access from other modules
163
263
  setSingletonSession(this);
164
264
 
@@ -169,29 +269,52 @@ export class InteractiveSession {
169
269
  console.log('');
170
270
  console.log(colors.gradient('╔════════════════════════════════════════════════════════════╗'));
171
271
  console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
172
- console.log(colors.gradient('║') + ' '.repeat(13) + '🤖 ' + colors.gradient('XAGENT CLI') + ' '.repeat(32) + colors.gradient(' ║'));
173
- console.log(colors.gradient('║') + ' '.repeat(16) + colors.textMuted(`v${packageJson.version}`) + ' '.repeat(36) + colors.gradient(' ║'));
272
+ console.log(
273
+ colors.gradient('║') +
274
+ ' '.repeat(13) +
275
+ '🤖 ' +
276
+ colors.gradient('XAGENT CLI') +
277
+ ' '.repeat(32) +
278
+ colors.gradient(' ║')
279
+ );
280
+ console.log(
281
+ colors.gradient('║') +
282
+ ' '.repeat(16) +
283
+ colors.textMuted(`v${packageJson.version}`) +
284
+ ' '.repeat(36) +
285
+ colors.gradient(' ║')
286
+ );
174
287
  console.log(colors.gradient('║') + ' '.repeat(58) + colors.gradient(' ║'));
175
288
  console.log(colors.gradient('╚════════════════════════════════════════════════════════════╝'));
176
289
  console.log(colors.textMuted(' AI-powered command-line assistant'));
290
+
291
+ // Show initialization message if skills were initialized
292
+ if (initializedCount > 0) {
293
+ console.log(colors.textMuted(` ✨ Initialized ${initializedCount} built-in skills`));
294
+ }
177
295
  console.log('');
178
296
 
179
297
  await this.initialize();
180
298
  this.showWelcomeMessage();
181
299
 
300
+ // Start watching for skill updates from CLI
301
+ this.startSkillUpdateWatcher();
302
+ // Set up ESC key handler using the terminal module
303
+ // This avoids conflicts with readline and provides clean ESC detection
304
+ let escCleanup: (() => void) | undefined;
305
+ if (process.stdin.isTTY) {
306
+ escCleanup = setupEscKeyHandler(() => {
307
+ if ((this as any)._isOperationInProgress) {
308
+ // An operation is running, let it be cancelled
309
+ this.cancellationManager.cancel();
310
+ }
311
+ // No operation running, ignore ESC
312
+ });
313
+ }
314
+
182
315
  // Track if an operation is in progress
183
316
  (this as any)._isOperationInProgress = false;
184
317
 
185
- // Listen for ESC cancellation - only cancel operations, don't exit the program
186
- const cancelHandler = () => {
187
- if ((this as any)._isOperationInProgress) {
188
- // An operation is running, let it be cancelled
189
- return;
190
- }
191
- // No operation running, ignore ESC or show a message
192
- };
193
- this.cancellationManager.on('cancelled', cancelHandler);
194
-
195
318
  this.promptLoop();
196
319
 
197
320
  // Keep the promise pending until shutdown
@@ -204,17 +327,17 @@ export class InteractiveSession {
204
327
  logger.debug('\n[SESSION] ========== initialize() 开始 ==========\n');
205
328
 
206
329
  try {
207
- // Custom spinner for initialization (like Thinking...)
208
- const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
209
- let frameIndex = 0;
210
- const validatingText = colors.textMuted('Validating authentication...');
211
-
212
- const spinnerInterval = setInterval(() => {
213
- process.stdout.write(`\r${colors.primary(frames[frameIndex])} ${validatingText}`);
214
- frameIndex = (frameIndex + 1) % frames.length;
215
- }, 120);
330
+ // Custom spinner for initialization (like Thinking...)
331
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
332
+ let frameIndex = 0;
333
+ const validatingText = colors.textMuted('Validating authentication...');
334
+
335
+ const spinnerInterval = setInterval(() => {
336
+ process.stdout.write(`\r${colors.primary(frames[frameIndex])} ${validatingText}`);
337
+ frameIndex = (frameIndex + 1) % frames.length;
338
+ }, 120);
216
339
  logger.debug('[SESSION] 调用 configManager.load()...');
217
- await this.configManager.load();
340
+ this.configManager.load();
218
341
 
219
342
  logger.debug('[SESSION] Config loaded');
220
343
  let authConfig = this.configManager.getAuthConfig();
@@ -224,13 +347,16 @@ export class InteractiveSession {
224
347
  logger.debug('[SESSION] selectedAuthType (initial):', String(selectedAuthType));
225
348
  logger.debug('[SESSION] AuthType.OAUTH_XAGENT:', String(AuthType.OAUTH_XAGENT));
226
349
  logger.debug('[SESSION] AuthType.OPENAI_COMPATIBLE:', String(AuthType.OPENAI_COMPATIBLE));
227
- logger.debug('[SESSION] Will validate OAuth:', String(!!(authConfig.apiKey && selectedAuthType === AuthType.OAUTH_XAGENT)));
350
+ logger.debug(
351
+ '[SESSION] Will validate OAuth:',
352
+ String(!!(authConfig.apiKey && selectedAuthType === AuthType.OAUTH_XAGENT))
353
+ );
228
354
 
229
355
  // Only validate OAuth tokens, skip validation for third-party API keys
230
356
  if (authConfig.apiKey && selectedAuthType === AuthType.OAUTH_XAGENT) {
231
357
  clearInterval(spinnerInterval);
232
358
  process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear the line
233
-
359
+
234
360
  const baseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
235
361
  let isValid = await this.validateToken(baseUrl, authConfig.apiKey);
236
362
 
@@ -242,15 +368,14 @@ export class InteractiveSession {
242
368
  process.stdout.write(`\r${colors.primary(frames[frameIndex])} ${refreshingText}`);
243
369
  frameIndex = (frameIndex + 1) % frames.length;
244
370
  }, 120);
245
-
371
+
246
372
  const newToken = await this.refreshToken(baseUrl, authConfig.refreshToken);
247
373
  clearInterval(refreshInterval);
248
374
  process.stdout.write('\r' + ' '.repeat(50) + '\r');
249
375
 
250
376
  if (newToken) {
251
- // Save new token and persist
252
- await this.configManager.set('apiKey', newToken);
253
- await this.configManager.save('global');
377
+ this.configManager.set('apiKey', newToken);
378
+ this.configManager.save('global');
254
379
  authConfig.apiKey = newToken;
255
380
  isValid = true;
256
381
  }
@@ -262,23 +387,21 @@ export class InteractiveSession {
262
387
  console.log(colors.info('Please select an authentication method to continue.'));
263
388
  console.log('');
264
389
 
265
- // Clear invalid credentials and persist
266
- // Note: Do NOT overwrite selectedAuthType - let user re-select their preferred auth method
267
- await this.configManager.set('apiKey', '');
268
- await this.configManager.set('refreshToken', '');
269
- await this.configManager.save('global');
390
+ this.configManager.set('apiKey', '');
391
+ this.configManager.set('refreshToken', '');
392
+ this.configManager.save('global');
270
393
 
271
- await this.configManager.load();
394
+ this.configManager.load();
272
395
  authConfig = this.configManager.getAuthConfig();
273
396
 
274
397
  await this.setupAuthentication();
275
398
  authConfig = this.configManager.getAuthConfig();
276
399
 
277
- // Recreate readline interface after inquirer
400
+ // Recreate readline interface after interactive prompt
278
401
  this.rl.close();
279
402
  this.rl = readline.createInterface({
280
403
  input: process.stdin,
281
- output: process.stdout
404
+ output: process.stdout,
282
405
  });
283
406
  this.rl.on('close', () => {
284
407
  // readline closed
@@ -293,11 +416,11 @@ export class InteractiveSession {
293
416
  selectedAuthType = this.configManager.get('selectedAuthType');
294
417
  logger.debug('[SESSION] selectedAuthType (after setup):', String(selectedAuthType));
295
418
 
296
- // Recreate readline interface after inquirer
419
+ // Recreate readline interface after interactive prompt
297
420
  this.rl.close();
298
421
  this.rl = readline.createInterface({
299
422
  input: process.stdin,
300
- output: process.stdout
423
+ output: process.stdout,
301
424
  });
302
425
  this.rl.on('close', () => {
303
426
  // readline closed
@@ -308,22 +431,46 @@ export class InteractiveSession {
308
431
  }
309
432
  // For OPENAI_COMPATIBLE with API key, skip validation and proceed directly
310
433
 
311
- this.aiClient = new AIClient(authConfig);
312
- this.contextCompressor.setAIClient(this.aiClient);
313
-
314
- // Initialize remote AI client for OAuth XAGENT mode
315
- logger.debug('[SESSION] Final selectedAuthType:', String(selectedAuthType));
316
- logger.debug('[SESSION] Creating RemoteAIClient?', String(selectedAuthType === AuthType.OAUTH_XAGENT));
434
+ // Initialize AI clients and set contextCompressor appropriately
317
435
  if (selectedAuthType === AuthType.OAUTH_XAGENT) {
318
- const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
319
- // In OAuth XAGENT mode, we still pass apiKey (can be empty or used for other purposes)
320
- this.remoteAIClient = new RemoteAIClient(authConfig.apiKey || '', webBaseUrl, authConfig.showAIDebugInfo);
436
+ // Remote mode: fetch default models if not set
437
+ const currentLlm = this.configManager.get('remote_llmModelName');
438
+ const currentVlm = this.configManager.get('remote_vlmModelName');
439
+
440
+ if (!currentLlm || !currentVlm) {
441
+ const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
442
+
443
+ try {
444
+ const defaults = await fetchDefaultModels(authConfig.apiKey || '', webBaseUrl);
445
+
446
+ if (!currentLlm && defaults.llm?.name) {
447
+ this.configManager.set('remote_llmModelName', defaults.llm.name);
448
+ }
449
+ if (!currentVlm && defaults.vlm?.name) {
450
+ this.configManager.set('remote_vlmModelName', defaults.vlm.name);
451
+ }
452
+ this.configManager.save('global');
453
+ } catch (error: any) {
454
+ logger.debug('[SESSION] Failed to fetch default models:', error.message);
455
+ }
456
+ }
457
+
458
+ // Remote mode: create RemoteAIClient and use it for context compression
459
+ this.remoteAIClient = createAIClient(authConfig);
460
+ this.contextCompressor.setAIClient(this.remoteAIClient);
321
461
  logger.debug('[DEBUG Initialize] RemoteAIClient created successfully');
322
462
  } else {
463
+ // Local mode: create local AIClient
464
+ this.aiClient = createAIClient(authConfig);
465
+ this.contextCompressor.setAIClient(this.aiClient);
323
466
  logger.debug('[DEBUG Initialize] RemoteAIClient NOT created (not OAuth XAGENT mode)');
324
467
  }
325
468
 
326
- this.executionMode = this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
469
+ // Sync remoteAIClient reference to slashCommandHandler for /provider command
470
+ this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
471
+
472
+ this.executionMode =
473
+ this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
327
474
 
328
475
  await this.agentManager.loadAgents();
329
476
  await this.memoryManager.loadMemory();
@@ -350,11 +497,17 @@ export class InteractiveSession {
350
497
  // Eagerly connect to MCP servers to get tool definitions
351
498
  if (mcpServers && Object.keys(mcpServers).length > 0) {
352
499
  try {
353
- console.log(`${colors.info(`${icons.brain} Connecting to ${Object.keys(mcpServers).length} MCP server(s)...`)}`);
500
+ console.log(
501
+ `${colors.info(`${icons.brain} Connecting to ${Object.keys(mcpServers).length} MCP server(s)...`)}`
502
+ );
354
503
  await this.mcpManager.connectAllServers();
355
- const connectedCount = Array.from(this.mcpManager.getAllServers()).filter((s: any) => s.isServerConnected()).length;
504
+ const connectedCount = Array.from(this.mcpManager.getAllServers()).filter((s: any) =>
505
+ s.isServerConnected()
506
+ ).length;
356
507
  const mcpTools = this.mcpManager.getToolDefinitions();
357
- console.log(`${colors.success(`✓ ${connectedCount}/${Object.keys(mcpServers).length} MCP server(s) connected (${mcpTools.length} tools available)`)}`);
508
+ console.log(
509
+ `${colors.success(`✓ ${connectedCount}/${Object.keys(mcpServers).length} MCP server(s) connected (${mcpTools.length} tools available)`)}`
510
+ );
358
511
 
359
512
  // Register MCP tools with the tool registry (hide MCP origin from LLM)
360
513
  const toolRegistry = getToolRegistry();
@@ -375,7 +528,7 @@ export class InteractiveSession {
375
528
  await this.checkpointManager.initialize();
376
529
  }
377
530
 
378
- this.currentAgent = this.agentManager.getAgent('general-purpose');
531
+ this.currentAgent = this.agentManager.getAgent('general-purpose') ?? null;
379
532
 
380
533
  console.log(colors.success('✔ Initialization complete'));
381
534
  } catch (error: any) {
@@ -392,7 +545,7 @@ export class InteractiveSession {
392
545
  private async validateToken(baseUrl: string, apiKey: string): Promise<boolean> {
393
546
  logger.debug('[SESSION] validateToken called with baseUrl:', baseUrl);
394
547
  logger.debug('[SESSION] apiKey exists:', apiKey ? 'yes' : 'no');
395
-
548
+
396
549
  try {
397
550
  // For OAuth XAGENT auth, use /api/auth/me endpoint
398
551
  const url = `${baseUrl}/api/auth/me`;
@@ -402,11 +555,11 @@ export class InteractiveSession {
402
555
 
403
556
  const response = await axios.get(url, {
404
557
  headers: {
405
- 'Authorization': `Bearer ${apiKey}`,
406
- 'Content-Type': 'application/json'
558
+ Authorization: `Bearer ${apiKey}`,
559
+ 'Content-Type': 'application/json',
407
560
  },
408
561
  httpsAgent,
409
- timeout: 10000
562
+ timeout: 10000,
410
563
  });
411
564
 
412
565
  logger.debug('[SESSION] Validation response status:', String(response.status));
@@ -428,10 +581,14 @@ export class InteractiveSession {
428
581
  const url = `${baseUrl}/api/auth/refresh`;
429
582
  const httpsAgent = new https.Agent({ rejectUnauthorized: false });
430
583
 
431
- const response = await axios.post(url, { refreshToken }, {
432
- httpsAgent,
433
- timeout: 10000
434
- });
584
+ const response = await axios.post(
585
+ url,
586
+ { refreshToken },
587
+ {
588
+ httpsAgent,
589
+ timeout: 10000,
590
+ }
591
+ );
435
592
 
436
593
  if (response.status === 200) {
437
594
  const data = response.data as { token?: string; refreshToken?: string };
@@ -454,11 +611,15 @@ export class InteractiveSession {
454
611
  const authType = await selectAuthType();
455
612
  this.configManager.set('selectedAuthType', authType);
456
613
 
614
+ // Get xagentApiBaseUrl from config (respects XAGENT_BASE_URL env var)
615
+ const config = this.configManager.getAuthConfig();
616
+
457
617
  const authService = new AuthService({
458
618
  type: authType,
459
619
  apiKey: '',
460
620
  baseUrl: '',
461
- modelName: ''
621
+ modelName: '',
622
+ xagentApiBaseUrl: config.xagentApiBaseUrl,
462
623
  });
463
624
 
464
625
  const success = await authService.authenticate();
@@ -472,17 +633,32 @@ export class InteractiveSession {
472
633
 
473
634
  const authConfig = authService.getAuthConfig();
474
635
 
636
+ // Clear modelName for remote mode
637
+ if (authType === AuthType.OAUTH_XAGENT) {
638
+ authConfig.modelName = '';
639
+ }
640
+
475
641
  // VLM configuration is optional - only show for non-OAuth (local) mode
476
642
  // Remote mode uses backend VLM configuration
477
643
  if (authType !== AuthType.OAUTH_XAGENT) {
478
644
  console.log('');
479
645
  console.log(colors.info(`${icons.info} VLM configuration is optional.`));
480
- console.log(colors.info(`You can configure it later using the /vlm command if needed.`));
646
+ console.log(colors.info(`You can configure it later using the /model command if needed.`));
481
647
  console.log('');
482
648
  }
483
649
 
484
- // Save LLM config only, skip VLM for now
485
- await this.configManager.setAuthConfig(authConfig);
650
+ this.configManager.setAuthConfig(authConfig);
651
+
652
+ // Set default remote model settings if not already set
653
+ if (authType === AuthType.OAUTH_XAGENT) {
654
+ if (!this.configManager.get('remote_llmModelName')) {
655
+ this.configManager.set('remote_llmModelName', '');
656
+ }
657
+ if (!this.configManager.get('remote_vlmModelName')) {
658
+ this.configManager.set('remote_vlmModelName', '');
659
+ }
660
+ this.configManager.save('global');
661
+ }
486
662
  }
487
663
 
488
664
  private showWelcomeMessage(): void {
@@ -494,7 +670,8 @@ export class InteractiveSession {
494
670
 
495
671
  if (language === 'zh') {
496
672
  console.log(colors.primaryBright(`${icons.sparkles} Welcome to XAGENT CLI!`));
497
- console.log(colors.textMuted('Type /help to see available commands')); } else {
673
+ console.log(colors.textMuted('Type /help to see available commands'));
674
+ } else {
498
675
  console.log(colors.primaryBright(`${icons.sparkles} Welcome to XAGENT CLI!`));
499
676
  console.log(colors.textMuted('Type /help to see available commands'));
500
677
  }
@@ -510,28 +687,28 @@ export class InteractiveSession {
510
687
  [ExecutionMode.YOLO]: {
511
688
  color: colors.error,
512
689
  icon: icons.fire,
513
- description: 'Execute commands without confirmation'
690
+ description: 'Execute commands without confirmation',
514
691
  },
515
692
  [ExecutionMode.ACCEPT_EDITS]: {
516
693
  color: colors.warning,
517
694
  icon: icons.check,
518
- description: 'Accept all edits automatically'
695
+ description: 'Accept all edits automatically',
519
696
  },
520
697
  [ExecutionMode.PLAN]: {
521
698
  color: colors.info,
522
699
  icon: icons.brain,
523
- description: 'Plan before executing'
700
+ description: 'Plan before executing',
524
701
  },
525
702
  [ExecutionMode.DEFAULT]: {
526
703
  color: colors.success,
527
704
  icon: icons.bolt,
528
- description: 'Safe execution with confirmations'
705
+ description: 'Safe execution with confirmations',
529
706
  },
530
707
  [ExecutionMode.SMART]: {
531
708
  color: colors.primaryBright,
532
709
  icon: icons.sparkles,
533
- description: 'Smart approval with intelligent security checks'
534
- }
710
+ description: 'Smart approval with intelligent security checks',
711
+ },
535
712
  };
536
713
 
537
714
  const config = modeConfig[this.executionMode];
@@ -541,6 +718,28 @@ export class InteractiveSession {
541
718
  console.log(` ${config.color(config.icon)} ${styleHelpers.text.bold(config.color(modeName))}`);
542
719
  console.log(` ${colors.textDim(` ${config.description}`)}`);
543
720
  console.log('');
721
+
722
+ this.showRemoteModelInfo();
723
+ }
724
+
725
+ private showRemoteModelInfo(): void {
726
+ const authConfig = this.configManager.getAuthConfig();
727
+ const isRemote = authConfig.type === AuthType.OAUTH_XAGENT;
728
+
729
+ if (isRemote) {
730
+ const llmModel = authConfig.remote_llmModelName || colors.textMuted('Not set');
731
+ const vlmModel = authConfig.remote_vlmModelName || colors.textMuted('Not set');
732
+ console.log(colors.textMuted(`${icons.brain} Remote Models:`));
733
+ console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('LLM:')} ${llmModel}`);
734
+ console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('VLM:')} ${vlmModel}`);
735
+ } else {
736
+ const modelName = authConfig.modelName || colors.textMuted('Not set');
737
+ const guiSubagentModel = this.configManager.get('guiSubagentModel') || colors.textMuted('Not set');
738
+ console.log(colors.textMuted(`${icons.brain} Local Models:`));
739
+ console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('LLM:')} ${modelName}`);
740
+ console.log(` ${colors.primaryBright(icons.arrowRight)} ${colors.info('VLM:')} ${guiSubagentModel}`);
741
+ }
742
+ console.log('');
544
743
  }
545
744
 
546
745
  private async promptLoop(): Promise<void> {
@@ -554,16 +753,13 @@ export class InteractiveSession {
554
753
  this.rl.close();
555
754
  }
556
755
 
557
- // Enable raw mode BEFORE emitKeypressEvents for better ESC detection
558
- if (process.stdin.isTTY) {
559
- process.stdin.setRawMode(true);
560
- }
561
- process.stdin.resume();
562
- readline.emitKeypressEvents(process.stdin);
756
+ // Ensure TTY is in proper state for input handling
757
+ // This handles any state left by @clack/prompts or other interactions
758
+ ensureTtySane();
563
759
 
564
760
  this.rl = readline.createInterface({
565
761
  input: process.stdin,
566
- output: process.stdout
762
+ output: process.stdout,
567
763
  });
568
764
 
569
765
  const prompt = `${colors.primaryBright('❯')} `;
@@ -592,7 +788,8 @@ export class InteractiveSession {
592
788
  if (trimmedInput.startsWith('/')) {
593
789
  const handled = await this.slashCommandHandler.handleCommand(trimmedInput);
594
790
  if (handled) {
595
- this.executionMode = this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
791
+ this.executionMode =
792
+ this.configManager.getApprovalMode() || this.configManager.getExecutionMode();
596
793
  // Sync conversation history to slashCommandHandler
597
794
  this.slashCommandHandler.setConversationHistory(this.conversation);
598
795
  }
@@ -622,7 +819,9 @@ export class InteractiveSession {
622
819
  }
623
820
 
624
821
  console.log('');
625
- console.log(colors.primaryBright(`${icons.robot} Using agent: ${agent.name || agent.agentType}`));
822
+ console.log(
823
+ colors.primaryBright(`${icons.robot} Using agent: ${agent.name || agent.agentType}`)
824
+ );
626
825
  console.log(colors.border(icons.separator.repeat(40)));
627
826
  console.log('');
628
827
 
@@ -630,11 +829,11 @@ export class InteractiveSession {
630
829
  await this.processUserMessage(task, agent);
631
830
  }
632
831
 
633
- public async processUserMessage(message: string, agent?: any): Promise<void> {
832
+ public async processUserMessage(message: string, agent?: AgentConfig): Promise<void> {
634
833
  const inputs = parseInput(message);
635
- const textInput = inputs.find(i => i.type === 'text');
636
- const fileInputs = inputs.filter(i => i.type === 'file');
637
- const commandInput = inputs.find(i => i.type === 'command');
834
+ const textInput = inputs.find((i) => i.type === 'text');
835
+ const fileInputs = inputs.filter((i) => i.type === 'file');
836
+ const commandInput = inputs.find((i) => i.type === 'command');
638
837
 
639
838
  if (commandInput) {
640
839
  await this.executeShellCommand(commandInput.content);
@@ -647,10 +846,16 @@ export class InteractiveSession {
647
846
  const toolRegistry = getToolRegistry();
648
847
  for (const fileInput of fileInputs) {
649
848
  try {
650
- const content = await toolRegistry.execute('Read', { filePath: fileInput.content }, this.executionMode);
849
+ const content = await toolRegistry.execute(
850
+ 'Read',
851
+ { filePath: fileInput.content },
852
+ this.executionMode
853
+ );
651
854
  userContent += `\n\n--- File: ${fileInput.content} ---\n${content}`;
652
855
  } catch (error: any) {
653
- console.log(chalk.yellow(`Warning: Failed to read file ${fileInput.content}: ${error.message}`));
856
+ console.log(
857
+ chalk.yellow(`Warning: Failed to read file ${fileInput.content}: ${error.message}`)
858
+ );
654
859
  }
655
860
  }
656
861
  }
@@ -660,7 +865,7 @@ export class InteractiveSession {
660
865
  type: 'text' as const,
661
866
  content: userContent,
662
867
  rawInput: message,
663
- timestamp: Date.now()
868
+ timestamp: Date.now(),
664
869
  };
665
870
  await this.sessionManager.addInput(sessionInput);
666
871
 
@@ -677,29 +882,23 @@ export class InteractiveSession {
677
882
  const userMessage: ChatMessage = {
678
883
  role: 'user',
679
884
  content: userContent,
680
- timestamp: Date.now()
885
+ timestamp: Date.now(),
681
886
  };
682
887
 
683
- // Save last user message for recovery after compression
684
- const lastUserMessage = userMessage;
685
-
686
888
  this.conversation.push(userMessage);
687
889
  await this.conversationManager.addMessage(userMessage);
688
890
 
689
- // Check if context compression is needed
690
- await this.checkAndCompressContext(lastUserMessage);
691
-
692
891
  // Use remote AI client if available (OAuth XAGENT mode)
693
892
  const currentSelectedAuthType = this.configManager.get('selectedAuthType');
694
- logger.debug('[DEBUG processUserMessage] remoteAIClient exists:', !!this.remoteAIClient ? 'true' : 'false');
695
- logger.debug('[DEBUG processUserMessage] selectedAuthType:', String(currentSelectedAuthType));
696
- logger.debug('[DEBUG processUserMessage] AuthType.OAUTH_XAGENT:', String(AuthType.OAUTH_XAGENT));
893
+ logger.debug(
894
+ `[DEBUG] processUserMessage: remoteAIClient exists=${!!this.remoteAIClient}, selectedAuthType=${currentSelectedAuthType}`
895
+ );
697
896
 
698
897
  if (this.remoteAIClient) {
699
- logger.debug('[DEBUG processUserMessage] Using generateRemoteResponse');
898
+ logger.debug('[DEBUG] Using generateRemoteResponse (remote mode)');
700
899
  await this.generateRemoteResponse(thinkingTokens);
701
900
  } else {
702
- logger.debug('[DEBUG processUserMessage] Using generateResponse (local mode)');
901
+ logger.debug('[DEBUG] Using generateResponse (local mode)');
703
902
  await this.generateResponse(thinkingTokens);
704
903
  }
705
904
  }
@@ -709,7 +908,9 @@ export class InteractiveSession {
709
908
  const thinkingConfig = this.configManager.getThinkingConfig();
710
909
  const displayMode = thinkingConfig.displayMode || 'compact';
711
910
 
712
- const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length);
911
+ const separator = icons.separator.repeat(
912
+ Math.min(60, process.stdout.columns || 80) - indent.length
913
+ );
713
914
 
714
915
  console.log('');
715
916
  console.log(`${indent}${colors.border(separator)}`);
@@ -725,9 +926,10 @@ export class InteractiveSession {
725
926
  case 'compact':
726
927
  // Compact display, truncate partial content
727
928
  const maxLength = 500;
728
- const truncatedContent = reasoningContent.length > maxLength
729
- ? reasoningContent.substring(0, maxLength) + '... (truncated)'
730
- : reasoningContent;
929
+ const truncatedContent =
930
+ reasoningContent.length > maxLength
931
+ ? reasoningContent.substring(0, maxLength) + '... (truncated)'
932
+ : reasoningContent;
731
933
 
732
934
  console.log(`${indent}${colors.textDim(`${icons.brain} Thinking Process:`)}`);
733
935
  console.log('');
@@ -738,7 +940,9 @@ export class InteractiveSession {
738
940
  case 'indicator':
739
941
  // Show indicator only
740
942
  console.log(`${indent}${colors.textDim(`${icons.brain} Thinking process completed`)}`);
741
- console.log(`${indent}${colors.textDim(`[${reasoningContent.length} chars of reasoning]`)}`);
943
+ console.log(
944
+ `${indent}${colors.textDim(`[${reasoningContent.length} chars of reasoning]`)}`
945
+ );
742
946
  break;
743
947
 
744
948
  default:
@@ -754,70 +958,107 @@ export class InteractiveSession {
754
958
  /**
755
959
  * Check and compress conversation context
756
960
  */
757
- private async checkAndCompressContext(lastUserMessage?: ChatMessage): Promise<void> {
961
+ private async checkAndCompressContext(): Promise<void> {
758
962
  const compressionConfig = this.configManager.getContextCompressionConfig();
759
963
 
760
964
  if (!compressionConfig.enabled) {
761
965
  return;
762
966
  }
763
967
 
764
- const { needsCompression, reason } = this.contextCompressor.needsCompression(
968
+ const indent = this.getIndent();
969
+ const currentTokens = this.contextCompressor.estimateContextTokens(this.conversation);
970
+ const currentMessages = this.conversation.length;
971
+ const { needsCompression, reason, tokenCount } = this.contextCompressor.needsCompression(
765
972
  this.conversation,
766
973
  compressionConfig
767
974
  );
768
975
 
769
- if (needsCompression) {
770
- const indent = this.getIndent();
771
- console.log('');
772
- console.log(`${indent}${colors.warning(`${icons.brain} Context compression triggered: ${reason}`)}`);
976
+ if (!needsCompression) {
977
+ return;
978
+ }
773
979
 
774
- const toolRegistry = getToolRegistry();
775
- const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
776
- const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode);
777
- const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
778
-
779
- const result: CompressionResult = await this.contextCompressor.compressContext(
780
- this.conversation,
781
- enhancedSystemPrompt,
782
- compressionConfig
783
- );
980
+ // Extract threshold and contextWindow from reason
981
+ const thresholdMatch = reason.match(/budget\s*\((\d+)/);
982
+ const contextWindowMatch = reason.match(/contextWindow:\s*(\d+)/);
983
+ const threshold = thresholdMatch ? parseInt(thresholdMatch[1], 10) : 0;
984
+ const contextWindow = contextWindowMatch ? parseInt(contextWindowMatch[1], 10) : 0;
784
985
 
785
- if (result.wasCompressed) {
786
- this.conversation = result.compressedMessages;
787
- // console.log(`${indent}${colors.success(`✓ Compressed ${result.originalMessageCount} messages to ${result.compressedMessageCount} messages`)}`);
788
- console.log(`${indent}${colors.textMuted(`✓ Size: ${result.originalSize} → ${result.compressedSize} chars (${Math.round((1 - result.compressedSize / result.originalSize) * 100)}% reduction)`)}`);
986
+ console.log('');
987
+ console.log(
988
+ `${indent}${colors.success(`${icons.sparkles} Compressing context (${currentMessages} msgs, ${tokenCount.toLocaleString()} > ${threshold.toLocaleString()}/${contextWindow.toLocaleString()} tokens, ${Math.round((tokenCount / contextWindow) * 100)}% of context window)...`)}`
989
+ );
789
990
 
790
- // Display compressed summary content
791
- const summaryMessage = result.compressedMessages.find(m => m.role === 'assistant');
792
- if (summaryMessage && summaryMessage.content) {
793
- const maxPreviewLength = 800;
794
- let summaryContent = summaryMessage.content;
795
- const isTruncated = summaryContent.length > maxPreviewLength;
991
+ const toolRegistry = getToolRegistry();
992
+ const baseSystemPrompt = this.currentAgent?.systemPrompt || 'You are a helpful AI assistant.';
993
+ const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode);
994
+ const enhancedSystemPrompt =
995
+ await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
796
996
 
797
- if (isTruncated) {
798
- summaryContent = summaryContent.substring(0, maxPreviewLength) + '\n...';
799
- }
997
+ const result: CompressionResult = await this.contextCompressor.compressContext(
998
+ this.conversation,
999
+ enhancedSystemPrompt,
1000
+ compressionConfig
1001
+ );
800
1002
 
801
- console.log('');
802
- console.log(`${indent}${theme.predefinedStyles.title(`${icons.sparkles} Conversation Summary`)}`);
803
- const separator = icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length * 2);
804
- console.log(`${indent}${colors.border(separator)}`);
805
- const renderedSummary = renderMarkdown(summaryContent, (process.stdout.columns || 80) - indent.length * 4);
806
- console.log(`${indent}${theme.predefinedStyles.dim(renderedSummary).replace(/^/gm, indent)}`);
807
- if (isTruncated) {
808
- console.log(`${indent}${colors.textMuted(`(... ${summaryMessage.content.length - maxPreviewLength} more chars hidden)`)}`);
809
- }
810
- console.log(`${indent}${colors.border(separator)}`);
1003
+ if (result.wasCompressed) {
1004
+ this.conversation = result.compressedMessages;
1005
+ const reductionPercent = Math.round((1 - result.compressedSize / result.originalSize) * 100);
1006
+ console.log(
1007
+ `${indent}${colors.success(`${icons.success} Compressed ${result.originalMessageCount} ${result.compressedMessageCount} messages (${reductionPercent}% smaller)`)}`
1008
+ );
1009
+
1010
+ // Summary is embedded in first user message, look for it
1011
+ // The format is: "[Conversation Summary - X messages compressed]\n\n${summary}"
1012
+ let summaryMessage: ChatMessage | undefined = result.compressedMessages.find(
1013
+ (m) => m.role === 'user' && m.content.includes('[Conversation Summary')
1014
+ );
1015
+
1016
+ if (summaryMessage) {
1017
+ // Extract summary content after the header
1018
+ const match = summaryMessage.content.match(/\[Conversation Summary.*?\]:\n\n(.+)/s);
1019
+ if (match) {
1020
+ summaryMessage = {
1021
+ role: 'assistant',
1022
+ content: match[1],
1023
+ timestamp: summaryMessage.timestamp,
1024
+ };
811
1025
  }
1026
+ }
812
1027
 
813
- // Restore user messages after compression, ensuring user message exists for API calls
814
- if (lastUserMessage) {
815
- this.conversation.push(lastUserMessage);
1028
+ if (summaryMessage && summaryMessage.content) {
1029
+ const maxPreviewLength = 800;
1030
+ let summaryContent = summaryMessage.content;
1031
+ const isTruncated = summaryContent.length > maxPreviewLength;
1032
+
1033
+ if (isTruncated) {
1034
+ summaryContent = summaryContent.substring(0, maxPreviewLength) + '\n...';
816
1035
  }
817
1036
 
818
- // Sync compressed conversation history to slashCommandHandler
819
- this.slashCommandHandler.setConversationHistory(this.conversation);
1037
+ console.log('');
1038
+ console.log(
1039
+ `${indent}${theme.predefinedStyles.title(`${icons.sparkles} Conversation Summary`)}`
1040
+ );
1041
+ const separator = icons.separator.repeat(
1042
+ Math.min(60, process.stdout.columns || 80) - indent.length * 2
1043
+ );
1044
+ console.log(`${indent}${colors.border(separator)}`);
1045
+ const renderedSummary = renderMarkdown(
1046
+ summaryContent,
1047
+ (process.stdout.columns || 80) - indent.length * 4
1048
+ );
1049
+ console.log(
1050
+ `${indent}${theme.predefinedStyles.dim(renderedSummary).replace(/^/gm, indent)}`
1051
+ );
1052
+ if (isTruncated) {
1053
+ console.log(
1054
+ `${indent}${colors.textMuted(`(... ${summaryMessage.content.length - maxPreviewLength} more chars hidden)`)}`
1055
+ );
1056
+ }
1057
+ console.log(`${indent}${colors.border(separator)}`);
820
1058
  }
1059
+
1060
+ // Sync compressed conversation history to slashCommandHandler
1061
+ this.slashCommandHandler.setConversationHistory(this.conversation);
821
1062
  }
822
1063
  }
823
1064
 
@@ -826,7 +1067,9 @@ export class InteractiveSession {
826
1067
  console.log('');
827
1068
  console.log(`${indent}${colors.textMuted(`${icons.code} Executing:`)}`);
828
1069
  console.log(`${indent}${colors.codeText(` $ ${command}`)}`);
829
- console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
1070
+ console.log(
1071
+ `${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`
1072
+ );
830
1073
  console.log('');
831
1074
 
832
1075
  const toolRegistry = getToolRegistry();
@@ -846,17 +1089,17 @@ export class InteractiveSession {
846
1089
  tool: 'Bash',
847
1090
  params: { command },
848
1091
  result,
849
- timestamp: Date.now()
1092
+ timestamp: Date.now(),
850
1093
  };
851
1094
 
852
- this.toolCalls.push(toolCall);
1095
+ this.tool_calls.push(toolCall);
853
1096
 
854
1097
  // Record command execution to session manager
855
1098
  await this.sessionManager.addInput({
856
1099
  type: 'command',
857
1100
  content: command,
858
1101
  rawInput: command,
859
- timestamp: Date.now()
1102
+ timestamp: Date.now(),
860
1103
  });
861
1104
 
862
1105
  await this.sessionManager.addOutput({
@@ -865,7 +1108,7 @@ export class InteractiveSession {
865
1108
  toolName: 'Bash',
866
1109
  toolParams: { command },
867
1110
  toolResult: result,
868
- timestamp: Date.now()
1111
+ timestamp: Date.now(),
869
1112
  });
870
1113
  } catch (error: any) {
871
1114
  console.log(`${indent}${colors.error(`Command execution failed: ${error.message}`)}`);
@@ -894,10 +1137,22 @@ export class InteractiveSession {
894
1137
  */
895
1138
  private createRemoteCaller(taskId: string, status: 'begin' | 'continue') {
896
1139
  const client = this.remoteAIClient!;
1140
+
1141
+
897
1142
  return {
898
- chatCompletion: (messages: ChatMessage[], options: any) =>
899
- client.chatCompletion(messages, { ...options, taskId, status }),
900
- isRemote: true
1143
+ chatCompletion: (messages: ChatMessage[], options: any) => {
1144
+ // Must fetch authConfig inside the closure, otherwise it captures stale config
1145
+ const authConfig = this.configManager.getAuthConfig();
1146
+ logger.debug(`[DEBUG] createRemoteCaller: llmModelName=${authConfig.remote_llmModelName}, vlmModelName=${authConfig.remote_vlmModelName}`);
1147
+ return client.chatCompletion(messages, {
1148
+ ...options,
1149
+ taskId,
1150
+ status: options.isFirstApiCall ? 'begin' : 'continue',
1151
+ llmModelName: authConfig.remote_llmModelName,
1152
+ vlmModelName: authConfig.remote_vlmModelName
1153
+ });
1154
+ },
1155
+ isRemote: true,
901
1156
  };
902
1157
  }
903
1158
 
@@ -907,39 +1162,32 @@ export class InteractiveSession {
907
1162
  private createLocalCaller() {
908
1163
  const client = this.aiClient!;
909
1164
  return {
910
- chatCompletion: (messages: ChatMessage[], options: any) =>
1165
+ chatCompletion: (messages: ChatMessage[], options: any) =>
911
1166
  client.chatCompletion(messages as any, options),
912
- isRemote: false
1167
+ isRemote: false,
913
1168
  };
914
1169
  }
915
1170
 
916
- private async generateResponse(thinkingTokens: number = 0, customAIClient?: AIClient, existingTaskId?: string): Promise<void> {
1171
+ private async generateResponse(
1172
+ thinkingTokens: number = 0,
1173
+ _customAIClient?: AIClient,
1174
+ existingTaskId?: string
1175
+ ): Promise<void> {
917
1176
  // Use existing taskId or create new one for this user interaction
918
1177
  // If taskId already exists (e.g., from tool calls), reuse it
919
1178
  const taskId = existingTaskId || this.currentTaskId || crypto.randomUUID();
920
1179
  this.currentTaskId = taskId;
921
- this.isFirstApiCall = true;
922
1180
 
923
- // Determine status based on whether this is the first API call
924
- const status: 'begin' | 'continue' = this.isFirstApiCall ? 'begin' : 'continue';
1181
+ // isFirstApiCall is reset in generateRemoteResponse for new tasks
1182
+ // For continuation calls (existingTaskId provided), keep previous value
925
1183
 
926
- // Use custom AI client if provided, otherwise use default logic
927
- let chatCompletion: (messages: ChatMessage[], options: any) => Promise<any>;
928
- let isRemote = false;
929
-
930
- if (customAIClient) {
931
- // Custom client (used by remote mode) - pass taskId and status
932
- chatCompletion = (messages: ChatMessage[], options: any) =>
933
- customAIClient.chatCompletion(messages as any, { ...options, taskId, status });
934
- isRemote = true;
935
- } else {
936
- // Use unified LLM Caller with taskId (automatically selects local or remote mode)
937
- const caller = this.createLLMCaller(taskId, status);
938
- chatCompletion = caller.chatCompletion;
939
- isRemote = caller.isRemote;
940
- }
1184
+ // Use unified LLM Caller with taskId (automatically selects local or remote mode)
1185
+ const status: 'begin' | 'continue' = this.isFirstApiCall ? 'begin' : 'continue';
1186
+ const caller = this.createLLMCaller(taskId, status);
1187
+ const chatCompletion = caller.chatCompletion;
1188
+ const isRemote = caller.isRemote;
941
1189
 
942
- if (!isRemote && !this.aiClient && !customAIClient) {
1190
+ if (!isRemote && !this.aiClient) {
943
1191
  console.log(colors.error('AI client not initialized'));
944
1192
  return;
945
1193
  }
@@ -971,17 +1219,28 @@ export class InteractiveSession {
971
1219
  const toolDefinitions = toolRegistry.getToolDefinitions();
972
1220
 
973
1221
  // Available tools for this session
974
- const availableTools = this.executionMode !== ExecutionMode.DEFAULT && allowedToolNames.length > 0
975
- ? toolDefinitions.filter((tool: any) => allowedToolNames.includes(tool.function.name))
976
- : toolDefinitions;
977
-
978
- const baseSystemPrompt = this.currentAgent?.systemPrompt;
979
- const systemPromptGenerator = new SystemPromptGenerator(toolRegistry, this.executionMode, undefined, this.mcpManager);
980
- const enhancedSystemPrompt = await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
1222
+ const availableTools =
1223
+ this.executionMode !== ExecutionMode.DEFAULT && allowedToolNames.length > 0
1224
+ ? toolDefinitions.filter(
1225
+ (tool): tool is { function: { name: string } } =>
1226
+ typeof tool.function?.name === 'string' &&
1227
+ allowedToolNames.includes(tool.function.name)
1228
+ )
1229
+ : toolDefinitions;
1230
+
1231
+ const baseSystemPrompt = this.currentAgent?.systemPrompt ?? '';
1232
+ const systemPromptGenerator = new SystemPromptGenerator(
1233
+ toolRegistry,
1234
+ this.executionMode,
1235
+ undefined,
1236
+ this.mcpManager
1237
+ );
1238
+ const enhancedSystemPrompt =
1239
+ await systemPromptGenerator.generateEnhancedSystemPrompt(baseSystemPrompt);
981
1240
 
982
1241
  const messages: ChatMessage[] = [
983
1242
  { role: 'system', content: `${enhancedSystemPrompt}\n\n${memory}`, timestamp: Date.now() },
984
- ...this.conversation
1243
+ ...this.conversation,
985
1244
  ];
986
1245
 
987
1246
  const operationId = `ai-response-${Date.now()}`;
@@ -989,7 +1248,8 @@ export class InteractiveSession {
989
1248
  chatCompletion(messages, {
990
1249
  tools: availableTools,
991
1250
  toolChoice: availableTools.length > 0 ? 'auto' : 'none',
992
- thinkingTokens
1251
+ thinkingTokens,
1252
+ isFirstApiCall: this.isFirstApiCall,
993
1253
  }),
994
1254
  operationId
995
1255
  );
@@ -1002,9 +1262,7 @@ export class InteractiveSession {
1002
1262
 
1003
1263
  const assistantMessage = response.choices[0].message;
1004
1264
 
1005
- const content = typeof assistantMessage.content === 'string'
1006
- ? assistantMessage.content
1007
- : '';
1265
+ const content = typeof assistantMessage.content === 'string' ? assistantMessage.content : '';
1008
1266
  const reasoningContent = assistantMessage.reasoning_content || '';
1009
1267
  // Display reasoning content if available and thinking mode is enabled
1010
1268
  if (reasoningContent && this.configManager.getThinkingConfig().enabled) {
@@ -1013,9 +1271,14 @@ export class InteractiveSession {
1013
1271
 
1014
1272
  console.log('');
1015
1273
  console.log(`${indent}${colors.primaryBright(`${icons.robot} Assistant:`)}`);
1016
- console.log(`${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`);
1274
+ console.log(
1275
+ `${indent}${colors.border(icons.separator.repeat(Math.min(60, process.stdout.columns || 80) - indent.length))}`
1276
+ );
1017
1277
  console.log('');
1018
- const renderedContent = renderMarkdown(content, (process.stdout.columns || 80) - indent.length * 2);
1278
+ const renderedContent = renderMarkdown(
1279
+ content,
1280
+ (process.stdout.columns || 80) - indent.length * 2
1281
+ );
1019
1282
  console.log(`${indent}${renderedContent.replace(/^/gm, indent)}`);
1020
1283
  console.log('');
1021
1284
 
@@ -1023,8 +1286,8 @@ export class InteractiveSession {
1023
1286
  role: 'assistant',
1024
1287
  content,
1025
1288
  timestamp: Date.now(),
1026
- reasoningContent,
1027
- toolCalls: assistantMessage.tool_calls
1289
+ reasoning_content: reasoningContent,
1290
+ tool_calls: assistantMessage.tool_calls,
1028
1291
  });
1029
1292
 
1030
1293
  // Record output to session manager
@@ -1032,19 +1295,21 @@ export class InteractiveSession {
1032
1295
  role: 'assistant',
1033
1296
  content,
1034
1297
  timestamp: Date.now(),
1035
- reasoningContent,
1036
- toolCalls: assistantMessage.tool_calls
1298
+ reasoning_content: reasoningContent,
1299
+ tool_calls: assistantMessage.tool_calls,
1037
1300
  });
1038
1301
 
1039
1302
  if (assistantMessage.tool_calls) {
1040
- await this.handleToolCalls(assistantMessage.tool_calls);
1303
+ await this.handleToolCalls(assistantMessage.tool_calls as unknown as import('./types.js').ToolCallItem[]);
1304
+ } else {
1305
+ await this.checkAndCompressContext();
1041
1306
  }
1042
1307
 
1043
1308
  if (this.checkpointManager.isEnabled()) {
1044
1309
  await this.checkpointManager.createCheckpoint(
1045
1310
  `Response generated at ${new Date().toLocaleString()}`,
1046
1311
  [...this.conversation],
1047
- [...this.toolCalls]
1312
+ [...this.tool_calls]
1048
1313
  );
1049
1314
  }
1050
1315
 
@@ -1058,17 +1323,22 @@ export class InteractiveSession {
1058
1323
  (this as any)._isOperationInProgress = false;
1059
1324
 
1060
1325
  if (error.message === 'Operation cancelled by user') {
1061
- // Mark task as cancelled
1326
+ // Notify backend to cancel the task
1062
1327
  if (this.remoteAIClient && this.currentTaskId) {
1063
- await this.remoteAIClient.cancelTask(this.currentTaskId).catch(() => {});
1328
+ await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => {});
1064
1329
  }
1065
1330
  return;
1066
1331
  }
1067
1332
 
1068
- // Mark task as cancelled when error occurs (发送 status: 'cancel')
1069
- logger.debug(`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}`);
1333
+ // Distinguish error types: timeout vs other failures
1334
+ const isTimeout = error.message.includes('timeout') || error.message.includes('Timeout');
1335
+ const failureReason = isTimeout ? 'timeout' : 'failure';
1336
+
1337
+ logger.debug(
1338
+ `[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}, reason: ${failureReason}`
1339
+ );
1070
1340
  if (this.remoteAIClient && this.currentTaskId) {
1071
- await this.remoteAIClient.cancelTask(this.currentTaskId).catch(() => {});
1341
+ await this.remoteAIClient.failTask?.(this.currentTaskId, failureReason).catch(() => {});
1072
1342
  }
1073
1343
 
1074
1344
  console.log(colors.error(`Error: ${error.message}`));
@@ -1082,20 +1352,24 @@ export class InteractiveSession {
1082
1352
  * @param thinkingTokens - Optional thinking tokens config
1083
1353
  * @param existingTaskId - Optional existing taskId to reuse (for tool call continuation)
1084
1354
  */
1085
- private async generateRemoteResponse(thinkingTokens: number = 0, existingTaskId?: string): Promise<void> {
1355
+ private async generateRemoteResponse(
1356
+ thinkingTokens: number = 0,
1357
+ existingTaskId?: string
1358
+ ): Promise<void> {
1086
1359
  // Reuse existing taskId or create new one for this user interaction
1087
1360
  const taskId = existingTaskId || crypto.randomUUID();
1088
1361
  this.currentTaskId = taskId;
1089
- logger.debug(`[Session] generateRemoteResponse: taskId=${taskId}, existingTaskId=${!!existingTaskId}`);
1090
-
1091
- // Reset isFirstApiCall for new task, keep true for continuation
1092
- if (!existingTaskId) {
1093
- this.isFirstApiCall = true;
1094
- }
1362
+ logger.debug(
1363
+ `[Session] generateRemoteResponse: taskId=${taskId}, existingTaskId=${!!existingTaskId}`
1364
+ );
1095
1365
 
1096
- // Determine status based on whether this is the first API call
1097
- const status: 'begin' | 'continue' = this.isFirstApiCall ? 'begin' : 'continue';
1098
- logger.debug(`[Session] Status for this call: ${status}, isFirstApiCall=${this.isFirstApiCall}`);
1366
+ // Each new user message is a fresh task - always set isFirstApiCall = true
1367
+ // This ensures status is 'begin' for every user message
1368
+ this.isFirstApiCall = true;
1369
+ const status: 'begin' | 'continue' = 'begin';
1370
+ logger.debug(
1371
+ `[Session] Status for this call: ${status}, isFirstApiCall=${this.isFirstApiCall}`
1372
+ );
1099
1373
 
1100
1374
  // Check if remote client is available
1101
1375
  if (!this.remoteAIClient) {
@@ -1104,20 +1378,24 @@ export class InteractiveSession {
1104
1378
  }
1105
1379
 
1106
1380
  try {
1107
- // Reuse generateResponse with remote client, pass taskId to avoid generating new one
1108
- await this.generateResponse(thinkingTokens, this.remoteAIClient as any, taskId);
1381
+ // Use unified generateResponse without passing customAIClient,
1382
+ // let createLLMCaller handle remote/local selection
1383
+ await this.generateResponse(thinkingTokens, undefined, taskId);
1109
1384
 
1110
1385
  // Mark task as completed (发送 status: 'end')
1111
1386
  logger.debug(`[Session] Task completed: taskId=${this.currentTaskId}`);
1112
- if (this.currentTaskId) {
1113
- await this.remoteAIClient.completeTask(this.currentTaskId);
1387
+ if (this.remoteAIClient && this.currentTaskId) {
1388
+ await this.remoteAIClient.completeTask?.(this.currentTaskId);
1114
1389
  }
1115
-
1116
1390
  } catch (error: any) {
1117
1391
  // Clear the operation flag
1118
1392
  (this as any)._isOperationInProgress = false;
1119
1393
 
1120
1394
  if (error.message === 'Operation cancelled by user') {
1395
+ // Notify backend to cancel the task
1396
+ if (this.remoteAIClient && this.currentTaskId) {
1397
+ await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => {});
1398
+ }
1121
1399
  return;
1122
1400
  }
1123
1401
 
@@ -1129,28 +1407,29 @@ export class InteractiveSession {
1129
1407
  console.log('');
1130
1408
 
1131
1409
  // Clear invalid credentials and persist
1132
- await this.configManager.set('apiKey', '');
1133
- await this.configManager.set('refreshToken', '');
1134
- await this.configManager.save('global');
1410
+ this.configManager.set('apiKey', '');
1411
+ this.configManager.set('refreshToken', '');
1412
+ this.configManager.save('global');
1135
1413
 
1136
- logger.debug('[DEBUG generateRemoteResponse] Cleared invalid credentials, starting re-authentication...');
1414
+ logger.debug(
1415
+ '[DEBUG generateRemoteResponse] Cleared invalid credentials, starting re-authentication...'
1416
+ );
1137
1417
 
1138
1418
  // Re-authenticate
1139
1419
  await this.setupAuthentication();
1140
1420
 
1141
1421
  // Reload config to ensure we have the latest authConfig
1142
- logger.debug('[DEBUG generateRemoteResponse] Re-authentication completed, reloading config...');
1143
- await this.configManager.load();
1422
+ this.configManager.load();
1144
1423
  const authConfig = this.configManager.getAuthConfig();
1145
1424
 
1146
1425
  logger.debug('[DEBUG generateRemoteResponse] After re-auth:');
1147
1426
  logger.debug(' - authConfig.apiKey exists:', !!authConfig.apiKey ? 'true' : 'false');
1148
1427
 
1149
- // Recreate readline interface after inquirer
1428
+ // Recreate readline interface after interactive prompt
1150
1429
  this.rl.close();
1151
1430
  this.rl = readline.createInterface({
1152
1431
  input: process.stdin,
1153
- output: process.stdout
1432
+ output: process.stdout,
1154
1433
  });
1155
1434
  this.rl.on('close', () => {
1156
1435
  logger.debug('DEBUG: readline interface closed');
@@ -1158,13 +1437,19 @@ export class InteractiveSession {
1158
1437
 
1159
1438
  // Reinitialize RemoteAIClient with new token
1160
1439
  if (authConfig.apiKey) {
1161
- const webBaseUrl = authConfig.xagentApiBaseUrl || 'https://www.xagent-colife.net';
1162
- logger.debug('[DEBUG generateRemoteResponse] Reinitializing RemoteAIClient with new token');
1163
- this.remoteAIClient = new RemoteAIClient(authConfig.apiKey, webBaseUrl, authConfig.showAIDebugInfo);
1440
+ logger.debug(
1441
+ '[DEBUG generateRemoteResponse] Reinitializing RemoteAIClient with new token'
1442
+ );
1443
+ this.remoteAIClient = createAIClient(authConfig);
1164
1444
  } else {
1165
- logger.debug('[DEBUG generateRemoteResponse] WARNING: No apiKey after re-authentication!');
1445
+ logger.debug(
1446
+ '[DEBUG generateRemoteResponse] WARNING: No apiKey after re-authentication!'
1447
+ );
1166
1448
  }
1167
1449
 
1450
+ // Sync remoteAIClient reference to slashCommandHandler for /provider command
1451
+ this.slashCommandHandler.setRemoteAIClient(this.remoteAIClient);
1452
+
1168
1453
  // Retry the current operation
1169
1454
  console.log('');
1170
1455
  console.log(colors.info('Retrying with new authentication...'));
@@ -1172,10 +1457,15 @@ export class InteractiveSession {
1172
1457
  return this.generateRemoteResponse(thinkingTokens);
1173
1458
  }
1174
1459
 
1175
- // Mark task as cancelled when error occurs (发送 status: 'cancel')
1176
- logger.debug(`[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}`);
1460
+ // Distinguish error types: timeout vs other failures
1461
+ const isTimeout = error.message.includes('timeout') || error.message.includes('Timeout');
1462
+ const failureReason = isTimeout ? 'timeout' : 'failure';
1463
+
1464
+ logger.debug(
1465
+ `[Session] Task failed: taskId=${this.currentTaskId}, error: ${error.message}, reason: ${failureReason}`
1466
+ );
1177
1467
  if (this.remoteAIClient && this.currentTaskId) {
1178
- await this.remoteAIClient.cancelTask(this.currentTaskId).catch(() => {});
1468
+ await this.remoteAIClient.failTask?.(this.currentTaskId, failureReason).catch(() => {});
1179
1469
  }
1180
1470
 
1181
1471
  console.log(colors.error(`Error: ${error.message}`));
@@ -1183,7 +1473,10 @@ export class InteractiveSession {
1183
1473
  }
1184
1474
  }
1185
1475
 
1186
- private async handleToolCalls(toolCalls: any[], onComplete?: () => Promise<void>): Promise<void> {
1476
+ private async handleToolCalls(
1477
+ toolCalls: ToolCallItem[],
1478
+ onComplete?: () => Promise<void>
1479
+ ): Promise<void> {
1187
1480
  // Mark that tool execution is in progress
1188
1481
  (this as any)._isOperationInProgress = true;
1189
1482
 
@@ -1219,20 +1512,45 @@ export class InteractiveSession {
1219
1512
 
1220
1513
  // Execute all tools in parallel
1221
1514
  const results = await toolRegistry.executeAll(
1222
- preparedToolCalls.map(tc => ({ name: tc.name, params: tc.params })),
1515
+ preparedToolCalls.map((tc) => ({ name: tc.name, params: tc.params })),
1223
1516
  this.executionMode
1224
1517
  );
1225
1518
 
1226
- // Process results and maintain order
1227
- let hasError = false;
1228
- for (const { tool, result, error } of results) {
1229
- const toolCall = preparedToolCalls.find(tc => tc.name === tool);
1230
- if (!toolCall) continue;
1519
+ // Create a map to store results by tool call index to maintain original order
1520
+ const resultsByIndex = new Map<number, { tool: string; result: any; error?: string }>();
1521
+ const usedIndices = new Set<number>();
1522
+
1523
+ for (const result of results) {
1524
+ // Find the first unused original index in preparedToolCalls that matches the tool name
1525
+ const originalIndex = preparedToolCalls.findIndex((tc, idx) =>
1526
+ tc.name === result.tool && !usedIndices.has(idx)
1527
+ );
1528
+ if (originalIndex !== -1) {
1529
+ usedIndices.add(originalIndex);
1530
+ resultsByIndex.set(originalIndex, result);
1531
+ }
1532
+ }
1231
1533
 
1232
- const { params } = toolCall;
1534
+ // Process results in the original tool_calls order (critical for Anthropic format APIs)
1535
+ let hasError = false;
1536
+ for (let i = 0; i < preparedToolCalls.length; i++) {
1537
+ const toolCall = preparedToolCalls[i];
1538
+ const { name: tool, params } = toolCall;
1539
+
1540
+ const resultData = resultsByIndex.get(i);
1541
+ const result = resultData?.result;
1542
+ const error = resultData?.error;
1233
1543
 
1234
1544
  if (error) {
1235
1545
  if (error === 'Operation cancelled by user') {
1546
+ // Notify backend to cancel the task
1547
+ if (this.remoteAIClient && this.currentTaskId) {
1548
+ await this.remoteAIClient.cancelTask?.(this.currentTaskId).catch(() => {});
1549
+ }
1550
+
1551
+ // 清理 conversation 中未完成的 tool_call
1552
+ this.cleanupIncompleteToolCalls();
1553
+
1236
1554
  (this as any)._isOperationInProgress = false;
1237
1555
  return;
1238
1556
  }
@@ -1242,16 +1560,16 @@ export class InteractiveSession {
1242
1560
  console.log('');
1243
1561
  console.log(`${indent}${colors.error(`${icons.cross} Tool Error: ${tool} - ${error}`)}`);
1244
1562
 
1245
- // 添加详细的错误信息,包含工具名称和参数,便于 AI 理解和修正
1563
+ // Add detailed error info including tool name and params for AI understanding and correction
1246
1564
  this.conversation.push({
1247
1565
  role: 'tool',
1248
1566
  content: JSON.stringify({
1249
1567
  name: tool,
1250
1568
  parameters: params,
1251
- error: error
1569
+ error: error,
1252
1570
  }),
1253
1571
  tool_call_id: toolCall.id,
1254
- timestamp: Date.now()
1572
+ timestamp: Date.now(),
1255
1573
  });
1256
1574
  } else {
1257
1575
  // Use correct indent for gui-subagent tasks
@@ -1294,7 +1612,10 @@ export class InteractiveSession {
1294
1612
  // Show edit result with diff
1295
1613
  console.log('');
1296
1614
  const diffOutput = renderDiff(result.diff);
1297
- const indentedDiff = diffOutput.split('\n').map(line => `${displayIndent} ${line}`).join('\n');
1615
+ const indentedDiff = diffOutput
1616
+ .split('\n')
1617
+ .map((line) => `${displayIndent} ${line}`)
1618
+ .join('\n');
1298
1619
  console.log(`${indentedDiff}`);
1299
1620
  } else if (hasFilePreview) {
1300
1621
  // Show new file content in diff-like style
@@ -1306,24 +1627,34 @@ export class InteractiveSession {
1306
1627
  } else if (hasDeleteInfo) {
1307
1628
  // Show DeleteFile result
1308
1629
  console.log('');
1309
- console.log(`${displayIndent}${colors.success(`${icons.check} Deleted: ${result.filePath}`)}`);
1630
+ console.log(
1631
+ `${displayIndent}${colors.success(`${icons.check} Deleted: ${result.filePath}`)}`
1632
+ );
1310
1633
  } else if (isTaskTool) {
1311
1634
  // Special handling for task tool (subagent) - show friendly summary
1312
1635
  console.log('');
1313
1636
  const subagentType = params.subagent_type;
1314
- const subagentName = params.description || (params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
1637
+ const subagentName =
1638
+ params.description ||
1639
+ (params.prompt ? params.prompt.substring(0, 50).replace(/\n/g, ' ') : 'Unknown task');
1315
1640
 
1316
1641
  if (result?.success) {
1317
- console.log(`${displayIndent}${colors.success(`${icons.check} ${subagentType}: Completed`)}`);
1642
+ console.log(
1643
+ `${displayIndent}${colors.success(`${icons.check} ${subagentType}: Completed`)}`
1644
+ );
1318
1645
  console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
1319
1646
  if (result.message) {
1320
1647
  console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
1321
1648
  }
1322
1649
  } else if (result?.cancelled) {
1323
- console.log(`${displayIndent}${colors.warning(`${icons.cross} ${subagentType}: Cancelled`)}`);
1650
+ console.log(
1651
+ `${displayIndent}${colors.warning(`${icons.cross} ${subagentType}: Cancelled`)}`
1652
+ );
1324
1653
  console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
1325
1654
  } else {
1326
- console.log(`${displayIndent}${colors.error(`${icons.cross} ${subagentType}: Failed`)}`);
1655
+ console.log(
1656
+ `${displayIndent}${colors.error(`${icons.cross} ${subagentType}: Failed`)}`
1657
+ );
1327
1658
  console.log(`${displayIndent}${colors.textDim(` Task: ${subagentName}`)}`);
1328
1659
  if (result?.message) {
1329
1660
  console.log(`${displayIndent}${colors.textDim(` ${result.message}`)}`);
@@ -1375,7 +1706,9 @@ export class InteractiveSession {
1375
1706
  }
1376
1707
 
1377
1708
  if (result?.success !== false) {
1378
- console.log(`${displayIndent}${colors.success(`${icons.check} ${serverName}: Success`)}`);
1709
+ console.log(
1710
+ `${displayIndent}${colors.success(`${icons.check} ${serverName}: Success`)}`
1711
+ );
1379
1712
  console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
1380
1713
  if (summary) {
1381
1714
  console.log(`${displayIndent}${colors.textDim(` ${summary}`)}`);
@@ -1384,7 +1717,9 @@ export class InteractiveSession {
1384
1717
  console.log(`${displayIndent}${colors.error(`${icons.cross} ${serverName}: Failed`)}`);
1385
1718
  console.log(`${displayIndent}${colors.textDim(` Tool: ${toolDisplayName}`)}`);
1386
1719
  if (result?.message || result?.error) {
1387
- console.log(`${displayIndent}${colors.textDim(` ${result?.message || result?.error}`)}`);
1720
+ console.log(
1721
+ `${displayIndent}${colors.textDim(` ${result?.message || result?.error}`)}`
1722
+ );
1388
1723
  }
1389
1724
  }
1390
1725
  } else if (tool === 'InvokeSkill') {
@@ -1397,7 +1732,8 @@ export class InteractiveSession {
1397
1732
  console.log(`${displayIndent}${colors.success(`${icons.check} Skill: Completed`)}`);
1398
1733
  console.log(`${displayIndent}${colors.textDim(` Skill: ${skillName}`)}`);
1399
1734
  if (taskDesc) {
1400
- const truncatedTask = taskDesc.length > 60 ? taskDesc.substring(0, 60) + '...' : taskDesc;
1735
+ const truncatedTask =
1736
+ taskDesc.length > 60 ? taskDesc.substring(0, 60) + '...' : taskDesc;
1401
1737
  console.log(`${displayIndent}${colors.textDim(` Task: ${truncatedTask}`)}`);
1402
1738
  }
1403
1739
  } else {
@@ -1413,13 +1749,20 @@ export class InteractiveSession {
1413
1749
  console.log(`${displayIndent}${colors.textDim(JSON.stringify(result, null, 2))}`);
1414
1750
  } else if (result && result.success === false) {
1415
1751
  // GUI task or other tool failed
1416
- console.log(`${displayIndent}${colors.error(`${icons.cross} ${result.message || 'Failed'}`)}`);
1752
+ console.log(
1753
+ `${displayIndent}${colors.error(`${icons.cross} ${result.message || 'Failed'}`)}`
1754
+ );
1417
1755
  } else if (result) {
1418
1756
  // Show brief preview by default (consistent with subagent behavior)
1419
- const resultPreview = typeof result === 'string' ? result : JSON.stringify(result, null, 2);
1420
- const truncatedPreview = resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
1757
+ const resultPreview =
1758
+ typeof result === 'string' ? result : JSON.stringify(result, null, 2);
1759
+ const truncatedPreview =
1760
+ resultPreview.length > 200 ? resultPreview.substring(0, 200) + '...' : resultPreview;
1421
1761
  // Indent the preview
1422
- const indentedPreview = truncatedPreview.split('\n').map(line => `${displayIndent} ${line}`).join('\n');
1762
+ const indentedPreview = truncatedPreview
1763
+ .split('\n')
1764
+ .map((line) => `${displayIndent} ${line}`)
1765
+ .join('\n');
1423
1766
  console.log(`${indentedPreview}`);
1424
1767
  } else {
1425
1768
  console.log(`${displayIndent}${colors.textDim('(no result)')}`);
@@ -1429,10 +1772,10 @@ export class InteractiveSession {
1429
1772
  tool,
1430
1773
  params,
1431
1774
  result,
1432
- timestamp: Date.now()
1775
+ timestamp: Date.now(),
1433
1776
  };
1434
1777
 
1435
- this.toolCalls.push(toolCallRecord);
1778
+ this.tool_calls.push(toolCallRecord);
1436
1779
 
1437
1780
  // Record tool output to session manager
1438
1781
  await this.sessionManager.addOutput({
@@ -1441,82 +1784,122 @@ export class InteractiveSession {
1441
1784
  toolName: tool,
1442
1785
  toolParams: params,
1443
1786
  toolResult: result,
1444
- timestamp: Date.now()
1787
+ timestamp: Date.now(),
1445
1788
  });
1446
1789
 
1447
- // 统一消息格式,包含工具名称和参数
1790
+ // Unified message format with tool name and params
1791
+ // Format: OpenAI-compatible tool result with plain text content
1448
1792
  this.conversation.push({
1449
1793
  role: 'tool',
1450
- content: JSON.stringify({
1451
- name: tool,
1452
- parameters: params,
1453
- result: result
1454
- }),
1794
+ content: typeof result === 'string' ? result : JSON.stringify(result, null, 2),
1455
1795
  tool_call_id: toolCall.id,
1456
- timestamp: Date.now()
1796
+ timestamp: Date.now(),
1457
1797
  });
1458
1798
  }
1459
1799
  }
1460
1800
 
1461
1801
  // Logic: Only skip returning results to main agent when user explicitly cancelled (ESC)
1462
1802
  // For all other cases (success, failure, errors), always return results for further processing
1463
- const guiSubagentCancelled = preparedToolCalls.some(tc => tc.name === 'task' && tc.params?.subagent_type === 'gui-subagent' && results.some(r => r.tool === 'task' && (r.result as any)?.cancelled === true));
1803
+ const guiSubagentCancelled = preparedToolCalls.some(
1804
+ (tc) =>
1805
+ tc.name === 'task' &&
1806
+ tc.params?.subagent_type === 'gui-subagent' &&
1807
+ results.some((r) => r.tool === 'task' && (r.result as any)?.cancelled === true)
1808
+ );
1464
1809
 
1465
1810
  // If GUI agent was cancelled by user, don't continue generating response
1466
1811
  // This avoids wasting API calls and tokens on cancelled tasks
1467
1812
  if (guiSubagentCancelled) {
1468
- console.log('');
1469
- console.log(`${indent}${colors.textMuted('GUI task cancelled by user')}`);
1470
1813
  (this as any)._isOperationInProgress = false;
1471
1814
  return;
1472
1815
  }
1473
1816
 
1474
- // Handle errors and completion based on whether onComplete callback is provided
1475
- if (hasError) {
1476
- (this as any)._isOperationInProgress = false;
1477
- // 不再抛出异常,而是将错误结果返回给 AI,让 AI 决定如何处理
1478
- // 这样可以避免工具错误导致程序退出
1479
- }
1480
-
1481
- // Continue based on mode - 统一处理,无论是否有错误
1817
+ // Continue based on mode - unified handling for both success and error cases
1482
1818
  if (onComplete) {
1819
+ await this.checkAndCompressContext();
1483
1820
  // Remote mode: use provided callback
1484
1821
  await onComplete();
1485
1822
  } else {
1823
+ await this.checkAndCompressContext();
1486
1824
  // Local mode: default behavior - continue with generateResponse
1487
1825
  await this.generateResponse();
1488
1826
  }
1489
1827
  }
1490
1828
 
1829
+ /**
1830
+ * Clean up incomplete tool calls from conversation after cancellation
1831
+ * This removes assistant messages with tool_calls that don't have corresponding tool_results
1832
+ */
1833
+ private async cleanupIncompleteToolCalls(): Promise<void> {
1834
+ // 从后往前找到包含 tool_calls 的 assistant 消息
1835
+ for (let i = this.conversation.length - 1; i >= 0; i--) {
1836
+ const msg = this.conversation[i];
1837
+
1838
+ if (msg.role === 'assistant' && msg.tool_calls?.length) {
1839
+ // 收集所有 tool_call IDs
1840
+ const allToolCallIds = new Set(msg.tool_calls.map((tc: any) => tc.id));
1841
+
1842
+ // 找出哪些 tool_call IDs 已经有对应的 tool_result
1843
+ const completedToolCallIds = new Set<string>();
1844
+ for (let k = i + 1; k < this.conversation.length; k++) {
1845
+ const resultMsg = this.conversation[k];
1846
+ if (resultMsg.role === 'tool' && resultMsg.tool_call_id) {
1847
+ completedToolCallIds.add(resultMsg.tool_call_id);
1848
+ } else if (resultMsg.role === 'user' || resultMsg.role === 'assistant') {
1849
+ break; // 遇到下一个角色消息就停止
1850
+ }
1851
+ }
1852
+
1853
+ // 找出未完成的 tool_call IDs
1854
+ const incompleteToolCallIds = [...allToolCallIds].filter(
1855
+ id => !completedToolCallIds.has(id)
1856
+ );
1857
+
1858
+ // 如果所有 tool_call 都已完成,不需要清理
1859
+ if (incompleteToolCallIds.length === 0) {
1860
+ break;
1861
+ }
1862
+
1863
+ // 只移除未完成的 tool_call,不移除已完成的 tool_result
1864
+ msg.tool_calls = msg.tool_calls.filter(
1865
+ (tc: any) => !incompleteToolCallIds.includes(tc.id)
1866
+ );
1867
+
1868
+ break;
1869
+ }
1870
+ }
1871
+ }
1872
+
1491
1873
  /**
1492
1874
  * Get user-friendly description for tool
1493
1875
  */
1494
1876
  private getToolDescription(toolName: string, params: any): string {
1495
1877
  const descriptions: Record<string, (params: any) => string> = {
1496
- 'Read': (p) => `Read file: ${this.truncatePath(p.filePath)}`,
1497
- 'Write': (p) => `Write file: ${this.truncatePath(p.filePath)}`,
1498
- 'Grep': (p) => `Search text: "${p.pattern}"`,
1499
- 'Bash': (p) => `Execute command: ${this.truncateCommand(p.command)}`,
1500
- 'ListDirectory': (p) => `List directory: ${this.truncatePath(p.path || '.')}`,
1501
- 'SearchFiles': (p) => `Search files: ${p.pattern}`,
1502
- 'DeleteFile': (p) => `Delete file: ${this.truncatePath(p.filePath)}`,
1503
- 'CreateDirectory': (p) => `Create directory: ${this.truncatePath(p.dirPath)}`,
1504
- 'Edit': (p) => `Edit text: ${this.truncatePath(p.file_path)}`,
1505
- 'web_search': (p) => `Web search: "${p.query}"`,
1506
- 'todo_write': () => `Update todo list`,
1507
- 'todo_read': () => `Read todo list`,
1508
- 'task': (p) => `Launch subtask: ${p.description}`,
1509
- 'ReadBashOutput': (p) => `Read task output: ${p.task_id}`,
1510
- 'web_fetch': () => `Fetch web content`,
1511
- 'ask_user_question': () => `Ask user`,
1512
- 'save_memory': () => `Save memory`,
1513
- 'exit_plan_mode': () => `Complete plan`,
1514
- 'xml_escape': (p) => `XML escape: ${this.truncatePath(p.file_path)}`,
1515
- 'image_read': (p) => `Read image: ${this.truncatePath(p.image_input)}`,
1878
+ Read: (p) => `Read file: ${this.truncatePath(p.filePath)}`,
1879
+ Write: (p) => `Write file: ${this.truncatePath(p.filePath)}`,
1880
+ Grep: (p) => `Search text: "${p.pattern}"`,
1881
+ Bash: (p) => `Execute command: ${this.truncateCommand(p.command)}`,
1882
+ ListDirectory: (p) => `List directory: ${this.truncatePath(p.path || '.')}`,
1883
+ SearchFiles: (p) => `Search files: ${p.pattern}`,
1884
+ DeleteFile: (p) => `Delete file: ${this.truncatePath(p.filePath)}`,
1885
+ CreateDirectory: (p) => `Create directory: ${this.truncatePath(p.dirPath)}`,
1886
+ Edit: (p) => `Edit text: ${this.truncatePath(p.file_path)}`,
1887
+ web_search: (p) => `Web search: "${p.query}"`,
1888
+ todo_write: () => `Update todo list`,
1889
+ todo_read: () => `Read todo list`,
1890
+ task: (p) => `Launch subtask: ${p.description}`,
1891
+ ReadBashOutput: (p) => `Read task output: ${p.task_id}`,
1892
+ web_fetch: () => `Fetch web content`,
1893
+ ask_user_question: () => `Ask user`,
1894
+ save_memory: () => `Save memory`,
1895
+ exit_plan_mode: () => `Complete plan`,
1896
+ xml_escape: (p) => `XML escape: ${this.truncatePath(p.file_path)}`,
1897
+ image_read: (p) => `Read image: ${this.truncatePath(p.image_input)}`,
1516
1898
  // 'Skill': (p) => `Execute skill: ${p.skill}`,
1517
1899
  // 'ListSkills': () => `List available skills`,
1518
1900
  // 'GetSkillDetails': (p) => `Get skill details: ${p.skill}`,
1519
- 'InvokeSkill': (p) => `Invoke skill: ${p.skillId} - ${this.truncatePath(p.taskDescription || '', 40)}`
1901
+ InvokeSkill: (p) =>
1902
+ `Invoke skill: ${p.skillId} - ${this.truncatePath(p.taskDescription || '', 40)}`,
1520
1903
  };
1521
1904
 
1522
1905
  const getDescription = descriptions[toolName];
@@ -1549,11 +1932,14 @@ export class InteractiveSession {
1549
1932
  return `${indent}${colors.textMuted('No tasks')}`;
1550
1933
  }
1551
1934
 
1552
- const statusConfig: Record<string, { icon: string; color: (text: string) => string; label: string }> = {
1553
- 'pending': { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
1554
- 'in_progress': { icon: icons.loading, color: colors.warning, label: 'In Progress' },
1555
- 'completed': { icon: icons.success, color: colors.success, label: 'Completed' },
1556
- 'failed': { icon: icons.error, color: colors.error, label: 'Failed' }
1935
+ const statusConfig: Record<
1936
+ string,
1937
+ { icon: string; color: (text: string) => string; label: string }
1938
+ > = {
1939
+ pending: { icon: icons.circle, color: colors.textMuted, label: 'Pending' },
1940
+ in_progress: { icon: icons.loading, color: colors.warning, label: 'In Progress' },
1941
+ completed: { icon: icons.success, color: colors.success, label: 'Completed' },
1942
+ failed: { icon: icons.error, color: colors.error, label: 'Failed' },
1557
1943
  };
1558
1944
 
1559
1945
  const lines: string[] = [];
@@ -1592,18 +1978,18 @@ export class InteractiveSession {
1592
1978
  // const messages = data as any[];
1593
1979
  // const tools = extra as any[];
1594
1980
  //
1595
- // // System prompt
1596
- // const systemMsg = messages.find((m: any) => m.role === 'system');
1597
- // console.log(colors.border(`${boxChar.vertical}`) + ' 🟫 SYSTEM: ' +
1598
- // colors.textMuted(systemMsg?.content?.toString().substring(0, 50) || '(none)') + ' '.repeat(3) + colors.border(boxChar.vertical));
1599
- //
1600
- // // Messages count
1601
- // console.log(colors.border(`${boxChar.vertical}`) + ' 💬 MESSAGES: ' +
1602
- // colors.text(messages.length.toString()) + ' items' + ' '.repeat(40) + colors.border(boxChar.vertical));
1603
- //
1604
- // // Tools count
1605
- // console.log(colors.border(`${boxChar.vertical}`) + ' 🔧 TOOLS: ' +
1606
- // colors.text((tools?.length || 0).toString()) + '' + ' '.repeat(43) + colors.border(boxChar.vertical)); //
1981
+ // // System prompt
1982
+ // const systemMsg = messages.find((m: any) => m.role === 'system');
1983
+ // console.log(colors.border(`${boxChar.vertical}`) + ' 🟫 SYSTEM: ' +
1984
+ // colors.textMuted(systemMsg?.content?.toString().substring(0, 50) || '(none)') + ' '.repeat(3) + colors.border(boxChar.vertical));
1985
+ //
1986
+ // // Messages count
1987
+ // console.log(colors.border(`${boxChar.vertical}`) + ' 💬 MESSAGES: ' +
1988
+ // colors.text(messages.length.toString()) + ' items' + ' '.repeat(40) + colors.border(boxChar.vertical));
1989
+ //
1990
+ // // Tools count
1991
+ // console.log(colors.border(`${boxChar.vertical}`) + ' 🔧 TOOLS: ' +
1992
+ // colors.text((tools?.length || 0).toString()) + '' + ' '.repeat(43) + colors.border(boxChar.vertical)); //
1607
1993
  // // Show last 2 messages
1608
1994
  // const recentMessages = messages.slice(-2);
1609
1995
  // for (const msg of recentMessages) {
@@ -1627,8 +2013,8 @@ export class InteractiveSession {
1627
2013
  // colors.text(`Prompt: ${response.usage?.prompt_tokens || '?'}, Completion: ${response.usage?.completion_tokens || '?'}`) +
1628
2014
  // ' '.repeat(15) + colors.border(boxChar.vertical));
1629
2015
  //
1630
- // console.log(colors.border(`${boxChar.vertical}`) + ' 🔧 TOOL_CALLS: ' +
1631
- // colors.text((message.tool_calls?.length || 0).toString()) + '' + ' '.repeat(37) + colors.border(boxChar.vertical));
2016
+ // console.log(colors.border(`${boxChar.vertical}`) + ' 🔧 TOOL_CALLS: ' +
2017
+ // colors.text((message.tool_calls?.length || 0).toString()) + '' + ' '.repeat(37) + colors.border(boxChar.vertical));
1632
2018
  //
1633
2019
  // // Content preview
1634
2020
  // const contentStr = typeof message.content === 'string'
@@ -1676,7 +2062,174 @@ export class InteractiveSession {
1676
2062
  }
1677
2063
  }
1678
2064
 
2065
+ /**
2066
+ * Clean up stale temporary workspaces from previous sessions.
2067
+ * Called at startup to ensure no leftover temp files accumulate.
2068
+ */
2069
+ async function cleanupStaleWorkspaces(): Promise<void> {
2070
+ try {
2071
+ const configManager = getConfigManager();
2072
+ const workspacePath = configManager.getWorkspacePath();
2073
+ // Use default workspace path if workspacePath is empty or undefined
2074
+ const effectiveWorkspacePath = (workspacePath && workspacePath.trim())
2075
+ ? workspacePath
2076
+ : os.homedir();
2077
+ const baseWorkspaceDir = path.join(effectiveWorkspacePath, '.xagent', 'workspace');
2078
+
2079
+ // First, verify the workspace directory exists before attempting cleanup
2080
+ try {
2081
+ const stats = await fsPromises.stat(baseWorkspaceDir);
2082
+ if (!stats.isDirectory()) {
2083
+ return; // Not a directory, skip cleanup
2084
+ }
2085
+ } catch {
2086
+ // Directory doesn't exist - nothing to clean
2087
+ return;
2088
+ }
2089
+
2090
+ try {
2091
+ const entries = await fsPromises.readdir(baseWorkspaceDir, { withFileTypes: true });
2092
+
2093
+ let cleanedCount = 0;
2094
+ for (const entry of entries) {
2095
+ if (entry.isDirectory()) {
2096
+ const fullPath = path.join(baseWorkspaceDir, entry.name);
2097
+ try {
2098
+ await fsPromises.rm(fullPath, { recursive: true, force: true });
2099
+ cleanedCount++;
2100
+ } catch {
2101
+ // Skip directories that can't be removed (in use or permission issues)
2102
+ }
2103
+ }
2104
+ }
2105
+
2106
+ if (cleanedCount > 0) {
2107
+ console.log(colors.textDim(`🧹 Cleaned up ${cleanedCount} stale workspace(s)`));
2108
+ }
2109
+ } catch {
2110
+ // Can't read directory - skip cleanup
2111
+ }
2112
+ } catch {
2113
+ // Config not available - skip cleanup
2114
+ }
2115
+ }
2116
+
2117
+ /**
2118
+ * Initialize built-in skills on first run.
2119
+ * Checks if user skills directory is empty or doesn't exist,
2120
+ * then copies all built-in skills including the protected find-skills.
2121
+ * @returns Number of skills initialized, or 0 if no initialization was needed.
2122
+ */
2123
+ async function initializeSkillsOnDemand(): Promise<number> {
2124
+ const __filename = fileURLToPath(import.meta.url);
2125
+ const __dirname = path.dirname(__filename);
2126
+
2127
+ // Get user skills directory (respects OS-specific paths)
2128
+ const configManager = getConfigManager();
2129
+ const userSkillsPath = configManager.getUserSkillsPath() || path.join(os.homedir(), '.xagent', 'skills');
2130
+
2131
+ // Check if user skills directory exists and has skills
2132
+ let hasSkills = false;
2133
+ try {
2134
+ const entries = await fsPromises.readdir(userSkillsPath, { withFileTypes: true });
2135
+ hasSkills = entries.some(e => e.isDirectory());
2136
+ } catch {
2137
+ hasSkills = false;
2138
+ }
2139
+
2140
+ // If skills already exist, skip initialization
2141
+ if (hasSkills) {
2142
+ return 0;
2143
+ }
2144
+
2145
+ // Ensure user skills directory exists
2146
+ await fsPromises.mkdir(userSkillsPath, { recursive: true });
2147
+
2148
+ // Define skill source directories
2149
+ const builtinSkillsDir = path.join(__dirname, '..', 'skills', 'skills');
2150
+ const findSkillsDir = path.join(__dirname, '..', 'find-skills');
2151
+
2152
+ const skillsToInstall: { source: string; name: string }[] = [];
2153
+
2154
+ // Add find-skills from root directory
2155
+ if (fs.existsSync(findSkillsDir) && fs.existsSync(path.join(findSkillsDir, 'SKILL.md'))) {
2156
+ skillsToInstall.push({ source: findSkillsDir, name: 'find-skills' });
2157
+ }
2158
+
2159
+ // Add skills from skills/skills directory
2160
+ if (fs.existsSync(builtinSkillsDir)) {
2161
+ const entries = fs.readdirSync(builtinSkillsDir, { withFileTypes: true });
2162
+ for (const entry of entries) {
2163
+ if (entry.isDirectory()) {
2164
+ const skillPath = path.join(builtinSkillsDir, entry.name);
2165
+ if (fs.existsSync(path.join(skillPath, 'SKILL.md'))) {
2166
+ skillsToInstall.push({ source: skillPath, name: entry.name });
2167
+ }
2168
+ }
2169
+ }
2170
+ }
2171
+
2172
+ if (skillsToInstall.length === 0) {
2173
+ return 0;
2174
+ }
2175
+
2176
+ // Create user skills directory (already done above, but ensure it exists)
2177
+ await fsPromises.mkdir(userSkillsPath, { recursive: true });
2178
+
2179
+ // Copy all skills
2180
+ for (const { source, name } of skillsToInstall) {
2181
+ const destPath = path.join(userSkillsPath, name);
2182
+ if (!fs.existsSync(destPath)) {
2183
+ await copyDirectoryRecursiveAsync(source, destPath);
2184
+ }
2185
+ }
2186
+
2187
+ return skillsToInstall.length;
2188
+ }
2189
+
2190
+ // Synchronous version (kept for backwards compatibility)
2191
+ function copyDirectoryRecursive(src: string, dest: string): void {
2192
+ if (!fs.existsSync(dest)) {
2193
+ fs.mkdirSync(dest, { recursive: true });
2194
+ }
2195
+
2196
+ const entries = fs.readdirSync(src, { withFileTypes: true });
2197
+ for (const entry of entries) {
2198
+ const srcPath = path.join(src, entry.name);
2199
+ const destPath = path.join(dest, entry.name);
2200
+
2201
+ if (entry.isDirectory()) {
2202
+ copyDirectoryRecursive(srcPath, destPath);
2203
+ } else if (entry.isFile()) {
2204
+ fs.copyFileSync(srcPath, destPath);
2205
+ }
2206
+ }
2207
+ }
2208
+
2209
+ // Asynchronous version for concurrent-safe initialization
2210
+ async function copyDirectoryRecursiveAsync(src: string, dest: string): Promise<void> {
2211
+ await fsPromises.mkdir(dest, { recursive: true });
2212
+ const entries = await fsPromises.readdir(src, { withFileTypes: true });
2213
+
2214
+ for (const entry of entries) {
2215
+ const srcPath = path.join(src, entry.name);
2216
+ const destPath = path.join(dest, entry.name);
2217
+
2218
+ if (entry.isDirectory()) {
2219
+ await copyDirectoryRecursiveAsync(srcPath, destPath);
2220
+ } else if (entry.isFile()) {
2221
+ await fsPromises.copyFile(srcPath, destPath);
2222
+ }
2223
+ }
2224
+ }
2225
+
1679
2226
  export async function startInteractiveSession(): Promise<void> {
2227
+ // Clean up any leftover temp workspaces from previous sessions
2228
+ await cleanupStaleWorkspaces();
2229
+
2230
+ // Initialize built-in skills on first run (silent, returns count)
2231
+ const initializedCount = await initializeSkillsOnDemand();
2232
+
1680
2233
  const session = new InteractiveSession();
1681
2234
 
1682
2235
  // Flag to control shutdown
@@ -1721,6 +2274,15 @@ export async function startInteractiveSession(): Promise<void> {
1721
2274
  process.exit(0);
1722
2275
  });
1723
2276
 
2277
+ await session.start(initializedCount);
2278
+ // Check for updates on startup
2279
+ try {
2280
+ const { checkUpdatesOnStartup } = await import('./update.js');
2281
+ await checkUpdatesOnStartup();
2282
+ } catch (error) {
2283
+ // Silently ignore update check failures
2284
+ }
2285
+
1724
2286
  await session.start();
1725
2287
  }
1726
2288