@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
@@ -1,290 +1,982 @@
1
- import { ChatMessage, CompressionConfig } from './types.js';
2
- import { AIClient, Message } from './ai-client.js';
3
- import { AuthConfig } from './types.js';
4
-
5
- export interface CompressionResult {
6
- compressedMessages: ChatMessage[];
7
- wasCompressed: boolean;
8
- originalMessageCount: number;
9
- compressedMessageCount: number;
10
- originalSize: number;
11
- compressedSize: number;
12
- compressionMethod: 'summary' | 'truncate' | 'none';
13
- }
14
-
15
- export class ContextCompressor {
16
- private aiClient: AIClient | null = null;
17
- private defaultConfig: CompressionConfig = {
18
- enabled: true,
19
- maxMessages: 50,
20
- maxContextSize: 150000,
21
- preserveRecentMessages: 0,
22
- enableSummary: true
23
- };
24
-
25
- constructor(authConfig?: AuthConfig) {
26
- if (authConfig) {
27
- this.aiClient = new AIClient(authConfig);
28
- }
29
- }
30
-
31
- setAIClient(aiClient: AIClient): void {
32
- this.aiClient = aiClient;
33
- }
34
-
35
- /**
36
- * Check if compression is needed
37
- */
38
- needsCompression(
39
- messages: ChatMessage[],
40
- config?: Partial<CompressionConfig>
41
- ): { needsCompression: boolean; reason: string } {
42
- const cfg = { ...this.defaultConfig, ...config };
43
- const userMessageCount = messages.filter(m => m.role === 'user').length;
44
- const contextSize = this.calculateContextSize(messages);
45
-
46
- if (userMessageCount > cfg.maxMessages) {
47
- return {
48
- needsCompression: true,
49
- reason: `User message count (${userMessageCount}) exceeds maximum (${cfg.maxMessages})`
50
- };
51
- }
52
-
53
- if (contextSize > cfg.maxContextSize) {
54
- return {
55
- needsCompression: true,
56
- reason: `Context size (${contextSize} chars) exceeds maximum (${cfg.maxContextSize} chars)`
57
- };
58
- }
59
-
60
- return { needsCompression: false, reason: '' };
61
- }
62
-
63
- /**
64
- * Calculate context size(字符数)
65
- */
66
- calculateContextSize(messages: ChatMessage[]): number {
67
- return messages.reduce((total, msg) => {
68
- return total + msg.content.length + (msg.role.length + 10);
69
- }, 0);
70
- }
71
-
72
- /**
73
- * Compress conversation history
74
- */
75
- async compressContext(
76
- messages: ChatMessage[],
77
- systemPrompt: string,
78
- config?: Partial<CompressionConfig>
79
- ): Promise<CompressionResult> {
80
- const cfg = { ...this.defaultConfig, ...config };
81
- const originalMessageCount = messages.length;
82
- const originalSize = this.calculateContextSize(messages);
83
-
84
- // Separate system messages、用户消息和助手消息
85
- const systemMessages = messages.filter(m => m.role === 'system');
86
- const nonSystemMessages = messages.filter(m => m.role !== 'system');
87
-
88
- // 如果消息数量和大小都在限制内,不需要压缩
89
- const { needsCompression } = this.needsCompression(nonSystemMessages, config);
90
- if (!needsCompression) {
91
- return {
92
- compressedMessages: messages,
93
- wasCompressed: false,
94
- originalMessageCount,
95
- compressedMessageCount: messages.length,
96
- originalSize,
97
- compressedSize: originalSize,
98
- compressionMethod: 'none'
99
- };
100
- }
101
-
102
- // Use AI to generate summary压缩全部历史对话
103
- const compressedMessages = await this.summarizeAllMessages(
104
- nonSystemMessages,
105
- systemPrompt,
106
- cfg
107
- );
108
-
109
- // 注意:不再保留原始的 systemMessages,因为 generateResponse 会自动添加系统提示
110
- // 压缩后的消息只includes摘要(role: assistant),避免重复的 system 消息导致 API 报错
111
- const finalMessages = [...compressedMessages];
112
-
113
- return {
114
- compressedMessages: finalMessages,
115
- wasCompressed: true,
116
- originalMessageCount,
117
- compressedMessageCount: finalMessages.length,
118
- originalSize,
119
- compressedSize: this.calculateContextSize(finalMessages),
120
- compressionMethod: 'summary'
121
- };
122
- }
123
-
124
- /**
125
- * Use AI to generate summary来压缩全部对话历史
126
- */
127
- private async summarizeAllMessages(
128
- messages: ChatMessage[],
129
- systemPrompt: string,
130
- config: CompressionConfig
131
- ): Promise<ChatMessage[]> {
132
- // 生成完整对话的摘要
133
- const summary = await this.generateSummary(messages, systemPrompt);
134
-
135
- // 构建压缩后的对话:只有摘要作为 system 消息
136
- const compressed: ChatMessage[] = [];
137
-
138
- // 添加摘要作为 assistant 消息,避免与系统提示重复导致 API 报错
139
- compressed.push({
140
- role: 'assistant',
141
- content: `[Conversation Summary - ${messages.length} messages compressed]\n\n${summary}`,
142
- timestamp: Date.now()
143
- });
144
-
145
- return compressed;
146
- }
147
-
148
- /**
149
- * Generate conversation summary
150
- */
151
- private async generateSummary(
152
- messages: ChatMessage[],
153
- systemPrompt: string
154
- ): Promise<string> {
155
- if (!this.aiClient) {
156
- throw new Error('AI client not initialized for summarization');
157
- }
158
-
159
- // Extract all conversation content
160
- const conversationText = messages
161
- .map((m, idx) => {
162
- const role = m.role === 'user' ? 'User' :
163
- m.role === 'assistant' ? 'Assistant' :
164
- m.role === 'tool' ? 'Tool' : m.role;
165
- return `[${idx + 1}] ${role}:\n${m.content}`;
166
- })
167
- .join('\n\n' + '='.repeat(50) + '\n\n');
168
-
169
- const summaryPrompt = `You are an expert at summarizing conversations. Please create a comprehensive summary of the following conversation.
170
-
171
- ## Instructions
172
- 1. Analyze ALL messages in the conversation
173
- 2. Create a detailed summary that captures:
174
- - Complete context and background
175
- - All topics discussed
176
- - All decisions made
177
- - All files created, modified, or analyzed
178
- - All code changes and implementations
179
- - All problems solved
180
- - Current state and progress
181
- - Any pending or ongoing tasks
182
- 3. Be extremely thorough - this summary will replace the entire conversation history
183
- 4. Include specific details like file paths, code snippets, command outputs, etc.
184
-
185
- ## Conversation to summarize
186
- ${conversationText}
187
-
188
- ## Output Format
189
- Provide a detailed, comprehensive summary in the following format:
190
-
191
- ### Conversation Summary
192
-
193
- #### Overview
194
- [Brief description of what this conversation was about]
195
-
196
- #### Background & Context
197
- [Complete background information]
198
-
199
- #### Topics Discussed
200
- - [Topic 1: detailed description]
201
- - [Topic 2: detailed description]
202
- - [All topics with full details]
203
-
204
- #### Key Decisions
205
- - [Decision 1: details and rationale]
206
- - [All decisions made during the conversation]
207
-
208
- #### File Operations
209
- - Created: [file paths]
210
- - Modified: [file paths]
211
- - Analyzed: [file paths]
212
- - Deleted: [file paths]
213
-
214
- #### Code Changes
215
- - [Specific code changes with details]
216
- - [All implementations]
217
-
218
- #### Problems & Solutions
219
- - Problem: [description]
220
- Solution: [how it was solved]
221
- - [All problems and solutions]
222
-
223
- #### Current State
224
- [What is the current status of the project/work]
225
-
226
- #### Pending Tasks
227
- - [Any ongoing or pending work]
228
-
229
- #### Important Details
230
- [Any other crucial information]
231
-
232
- Please provide the comprehensive summary possible. Do not omit any important information.`;
233
-
234
- try {
235
- const summaryMessage: Message = {
236
- role: 'user',
237
- content: summaryPrompt
238
- };
239
-
240
- const response = await this.aiClient.chatCompletion([summaryMessage], {
241
- maxTokens: 8192,
242
- temperature: 0.3
243
- });
244
-
245
- const summary = response.choices[0]?.message?.content || '';
246
- return typeof summary === 'string' ? summary : JSON.stringify(summary);
247
- } catch (error) {
248
- console.error('Failed to generate summary:', error);
249
- const userCount = messages.filter(m => m.role === 'user').length;
250
- const toolCount = messages.filter(m => m.role === 'tool').length;
251
- return `[Summary of ${messages.length} messages: ${userCount} user exchanges, ${toolCount} tool calls. Key topics discussed but details unavailable due to summarization error.]`;
252
- }
253
- }
254
-
255
- /**
256
- * Create compressed message copy(用于保存)
257
- */
258
- createCompressedSnapshot(
259
- messages: ChatMessage[],
260
- compressionResult: CompressionResult
261
- ): object {
262
- return {
263
- timestamp: Date.now(),
264
- originalMessageCount: compressionResult.originalMessageCount,
265
- compressedMessageCount: compressionResult.compressedMessageCount,
266
- originalSize: compressionResult.originalSize,
267
- compressedSize: compressionResult.compressedSize,
268
- compressionMethod: compressionResult.compressionMethod,
269
- messages: compressionResult.compressedMessages
270
- };
271
- }
272
-
273
- /**
274
- * Estimate token count (rough estimate)
275
- */
276
- estimateTokens(text: string): number {
277
- const chineseChars = (text.match(/[\u4e00-\u9fa5]/g) || []).length;
278
- const otherChars = text.length - chineseChars;
279
- return Math.ceil(chineseChars / 2 + otherChars / 4);
280
- }
281
- }
282
-
283
- let compressorInstance: ContextCompressor | null = null;
284
-
285
- export function getContextCompressor(authConfig?: AuthConfig): ContextCompressor {
286
- if (!compressorInstance) {
287
- compressorInstance = new ContextCompressor(authConfig);
288
- }
289
- return compressorInstance;
290
- }
1
+ import { ChatMessage, CompressionConfig } from './types.js';
2
+ import type { Message } from './ai-client/types.js';
3
+ import { AuthConfig } from './types.js';
4
+ import { getCancellationManager } from './cancellation.js';
5
+ import { createAIClient, AIClientInterface } from './ai-client-factory.js';
6
+
7
+ /**
8
+ * Model context window sizes (in tokens)
9
+ * Add models here as needed
10
+ */
11
+ const MODEL_CONTEXT_WINDOWS: Record<string, number> = {
12
+ // OpenAI
13
+ 'gpt-4': 8192,
14
+ 'gpt-4-32k': 32768,
15
+ 'gpt-4-turbo': 128000,
16
+ 'gpt-4o': 128000,
17
+ 'gpt-4o-mini': 128000,
18
+ 'gpt-5': 200000,
19
+
20
+ // Anthropic Claude
21
+ 'claude-sonnet-4-20250514': 200000,
22
+ 'claude-sonnet-4': 200000,
23
+ 'claude-opus-4': 200000,
24
+ 'claude-3-5-sonnet': 200000,
25
+ 'claude-3-opus': 200000,
26
+
27
+ // Google
28
+ 'gemini-pro': 32768,
29
+ 'gemini-ultra': 1000000,
30
+
31
+ // DeepSeek
32
+ 'deepseek-chat': 128000,
33
+ 'deepseek-coder': 128000,
34
+ 'deepseek-reasoner': 128000,
35
+
36
+ // Qwen (Tongyi Qianwen)
37
+ 'qwen-max': 32768,
38
+ 'qwen-plus': 64000,
39
+ 'qwen-turbo': 8000,
40
+ 'qwen-long': 100000,
41
+ 'qwen-vl-max': 128000,
42
+ 'Qwen3-Coder': 32768,
43
+
44
+ // Zhipu AI (GLM)
45
+ 'glm-4': 128000,
46
+ 'glm-4-plus': 128000,
47
+ 'glm-4-air': 128000,
48
+ 'glm-4.7': 128000,
49
+
50
+ // MiniMax
51
+ 'MiniMax-M2': 1000000,
52
+ 'MiniMax-M2.1': 1000000,
53
+
54
+ // Moonshot (Kimi)
55
+ 'moonshot-v1-8k': 8192,
56
+ 'moonshot-v1-32k': 32768,
57
+ 'moonshot-v1-128k': 131072,
58
+
59
+ // Doubao
60
+ 'doubao-seed-1-8-251228': 256000,
61
+ 'doubao-1-5-ui-tars-250428': 256000,
62
+
63
+ // Default fallback
64
+ 'default': 200000
65
+ };
66
+
67
+ // Compression ratio constants - easier to tune and maintain
68
+ const COMPRESSION_RATIO = {
69
+ RESERVE_RATIO: 0.50, // Reserve 50% of context window for system prompt and recent messages
70
+ SUMMARY_THRESHOLD_RATIO: 0.15, // Use 15% of available space for summary
71
+ MAX_TOKENS_RATIO: 0.8, // Use 80% of reserved tokens for compression
72
+ SUMMARY_MAX_TOKENS_RATIO: 0.5, // Use 50% of reserved tokens for summary
73
+ MIN_SUMMARY_TOKENS: 800 // Minimum tokens for summary
74
+ };
75
+
76
+ /**
77
+ * Get the context window for a model
78
+ */
79
+ export function getModelContextWindow(modelName?: string): number {
80
+ if (!modelName) return MODEL_CONTEXT_WINDOWS['default'];
81
+
82
+ // Try exact match first
83
+ if (MODEL_CONTEXT_WINDOWS[modelName]) {
84
+ return MODEL_CONTEXT_WINDOWS[modelName];
85
+ }
86
+
87
+ // Try case-insensitive match
88
+ const lowerName = modelName.toLowerCase();
89
+ for (const [key, value] of Object.entries(MODEL_CONTEXT_WINDOWS)) {
90
+ if (key.toLowerCase() === lowerName) {
91
+ return value;
92
+ }
93
+ }
94
+
95
+ // Try partial match (e.g., "claude" matches "claude-sonnet-4")
96
+ for (const [key, value] of Object.entries(MODEL_CONTEXT_WINDOWS)) {
97
+ if (lowerName.includes(key.toLowerCase()) || key.toLowerCase().includes(lowerName)) {
98
+ return value;
99
+ }
100
+ }
101
+
102
+ return MODEL_CONTEXT_WINDOWS['default'];
103
+ }
104
+
105
+ export interface CompressionResult {
106
+ compressedMessages: ChatMessage[];
107
+ wasCompressed: boolean;
108
+ originalMessageCount: number;
109
+ compressedMessageCount: number;
110
+ originalSize: number;
111
+ compressedSize: number;
112
+ compressionMethod: 'summary' | 'truncate' | 'none';
113
+ tokensBefore?: number;
114
+ details?: CompactionDetails;
115
+ }
116
+
117
+ export interface CompactionDetails {
118
+ readFiles: string[];
119
+ modifiedFiles: string[];
120
+ }
121
+
122
+ export interface FileOperations {
123
+ read: Set<string>;
124
+ modified: Set<string>;
125
+ }
126
+
127
+ export interface DetailedCompressionResult extends CompressionResult {
128
+ fileOperations?: FileOperations;
129
+ }
130
+
131
+ export interface CutPointResult {
132
+ firstKeptEntryIndex: number;
133
+ turnStartIndex: number;
134
+ isSplitTurn: boolean;
135
+ }
136
+
137
+ export interface CompactionPreparation {
138
+ firstKeptEntryIndex: number;
139
+ messagesToSummarize: ChatMessage[];
140
+ turnPrefixMessages: ChatMessage[];
141
+ isSplitTurn: boolean;
142
+ tokensBefore: number;
143
+ previousSummary?: string;
144
+ fileOps: FileOperations;
145
+ }
146
+
147
+ const SUMMARIZATION_PROMPT = `The messages above are a conversation to summarize. Create a structured context checkpoint summary that another LLM will use to continue the work.
148
+
149
+ Use this EXACT format:
150
+
151
+ ## Goal
152
+ [What is the user trying to accomplish? Can be multiple items if the session covers different tasks.]
153
+
154
+ ## Constraints & Preferences
155
+ - [Any constraints, preferences, or requirements mentioned by user]
156
+ - [Or "(none)" if none were mentioned]
157
+
158
+ ## Progress
159
+ ### Done
160
+ - [x] [Completed tasks/changes]
161
+
162
+ ### In Progress
163
+ - [ ] [Current work]
164
+
165
+ ### Blocked
166
+ - [Issues preventing progress, if any]
167
+
168
+ ## Key Decisions
169
+ - **[Decision]**: [Brief rationale]
170
+
171
+ ## Next Steps
172
+ 1. [Ordered list of what should happen next]
173
+
174
+ ## Critical Context
175
+
176
+ ### Key Files & Code
177
+ - [File path]: [Brief description of key content/structure]
178
+ - [File path]: [Brief description of key content/structure]
179
+
180
+ ### Execution Results
181
+ - **Files**: [What files were read, written, or edited and key findings]
182
+ - **Commands**: [Key commands run and their outputs]
183
+ - **Search/Analysis**: [Key findings from code search or analysis]
184
+
185
+ ### Project Context
186
+ - [Architecture patterns identified]
187
+ - [Important configurations]
188
+ - [Dependencies or libraries relevant to the task]
189
+
190
+ Keep each section concise. Preserve exact file paths, function names, and error messages.`;
191
+
192
+ const UPDATE_SUMMARIZATION_PROMPT = `The messages above are NEW conversation messages to incorporate into the existing summary provided in <previous-summary> tags.
193
+
194
+ Update the existing structured summary with new information. RULES:
195
+ - PRESERVE all existing information from the previous summary
196
+ - ADD new progress, decisions, and context from the new messages
197
+ - UPDATE the Progress section: move items from "In Progress" to "Done" when completed
198
+ - UPDATE "Next Steps" based on what was accomplished
199
+ - PRESERVE exact file paths, function names, and error messages
200
+ - If something is no longer relevant, you may remove it
201
+
202
+ Use this EXACT format:
203
+
204
+ ## Goal
205
+ [Preserve existing goals, add new ones if the task expanded]
206
+
207
+ ## Constraints & Preferences
208
+ - [Preserve existing, add new ones discovered]
209
+
210
+ ## Progress
211
+ ### Done
212
+ - [x] [Include previously done items AND newly completed items]
213
+
214
+ ### In Progress
215
+ - [ ] [Current work - update based on progress]
216
+
217
+ ### Blocked
218
+ - [Current blockers - remove if resolved]
219
+
220
+ ## Key Decisions
221
+ - **[Decision]**: [Brief rationale] (preserve all previous, add new)
222
+
223
+ ## Next Steps
224
+ 1. [Update based on current state]
225
+
226
+ ## Critical Context
227
+
228
+ ### Key Files & Code
229
+ - [Preserve existing file info, add new files read]
230
+ - [Include brief descriptions of key code structures]
231
+
232
+ ### Execution Results
233
+ - **Files**: [Preserve file list, add new files read/written/edited]
234
+ - **Commands**: [Preserve outputs, add new command results]
235
+ - **Search/Analysis**: [Preserve findings, add new search results]
236
+
237
+ ### Project Context
238
+ - [Preserve architecture info, add new patterns discovered]
239
+ - [Preserve configs, add new relevant dependencies]
240
+
241
+ Keep each section concise. Preserve exact file paths, function names, and error messages.`;
242
+
243
+ const TURN_PREFIX_SUMMARIZATION_PROMPT = `This is the PREFIX of a turn that was too large to keep. The SUFFIX (recent work) is retained.
244
+
245
+ Summarize the prefix to provide context for the retained suffix:
246
+
247
+ ## Original Request
248
+ [What did the user ask for in this turn?]
249
+
250
+ ## Early Progress
251
+ - [Key decisions and work done in the prefix]
252
+
253
+ ## Critical Context
254
+
255
+ ### Key Files & Code
256
+ - [File path]: [Brief description of key content/structure]
257
+
258
+ ### Execution Results
259
+ - **Files**: [What files were read, written, or edited in this prefix]
260
+ - **Commands**: [Key commands run and outputs]
261
+
262
+ ### Context for Suffix
263
+ - [Information needed to understand the retained recent work]
264
+
265
+ Be concise. Focus on what's needed to understand the kept suffix.`;
266
+
267
+ export class ContextCompressor {
268
+ private aiClient: AIClientInterface | null = null;
269
+ private defaultConfig: CompressionConfig = {
270
+ enabled: true
271
+ };
272
+
273
+ constructor(authConfig?: AuthConfig) {
274
+ if (authConfig) {
275
+ this.aiClient = createAIClient(authConfig);
276
+ }
277
+ }
278
+
279
+ setAIClient(aiClient: AIClientInterface): void {
280
+ this.aiClient = aiClient;
281
+ }
282
+
283
+ /**
284
+ * Check if compression is needed based on token budget and model context window
285
+ * @param messages - Conversation messages
286
+ * @param config - Compression config
287
+ * @param modelName - Optional model name to determine context window
288
+ */
289
+ needsCompression(
290
+ messages: ChatMessage[],
291
+ config?: Partial<CompressionConfig>,
292
+ modelName?: string
293
+ ): { needsCompression: boolean; reason: string; tokenCount: number } {
294
+ const cfg = { ...this.defaultConfig, ...config };
295
+ const tokenCount = this.estimateContextTokens(messages);
296
+ const messageCount = messages.length;
297
+
298
+ // Get model context window
299
+ const contextWindow = getModelContextWindow(modelName);
300
+
301
+ // Calculate threshold using compression ratio constants
302
+ const reserveTokens = Math.floor(contextWindow * COMPRESSION_RATIO.RESERVE_RATIO);
303
+ const threshold = contextWindow - reserveTokens;
304
+
305
+ if (tokenCount > threshold) {
306
+ return {
307
+ needsCompression: true,
308
+ reason: `Token count (${tokenCount}) exceeds ${modelName ? `${modelName}` : ''} context budget (${threshold}, contextWindow: ${contextWindow})`,
309
+ tokenCount
310
+ };
311
+ }
312
+
313
+ return { needsCompression: false, reason: '', tokenCount };
314
+ }
315
+
316
+ /**
317
+ * Estimate token count for a single message using message-type-aware heuristic
318
+ */
319
+ estimateMessageTokens(message: ChatMessage): number {
320
+ let chars = 0;
321
+
322
+ switch (message.role) {
323
+ case 'user':
324
+ case 'system':
325
+ case 'tool': {
326
+ chars = message.content.length;
327
+ break;
328
+ }
329
+ case 'assistant': {
330
+ if (message.reasoning_content) {
331
+ chars += message.reasoning_content.length;
332
+ }
333
+ const toolCalls = message.tool_calls as any[] | undefined;
334
+ if (toolCalls && toolCalls.length > 0) {
335
+ for (const toolCall of toolCalls) {
336
+ chars += JSON.stringify(toolCall).length;
337
+ }
338
+ }
339
+ chars += message.content.length;
340
+ break;
341
+ }
342
+ }
343
+
344
+ return Math.ceil(chars / 4);
345
+ }
346
+
347
+ /**
348
+ * Estimate total token count for a conversation
349
+ */
350
+ estimateContextTokens(messages: ChatMessage[]): number {
351
+ let total = 0;
352
+ for (const msg of messages) {
353
+ total += this.estimateMessageTokens(msg);
354
+ }
355
+ return total;
356
+ }
357
+
358
+ /**
359
+ * Find valid cut points: indices of messages that can be cut at
360
+ * Never cut at tool results (they must follow their tool call)
361
+ */
362
+ findValidCutPoints(messages: ChatMessage[], startIndex: number, endIndex: number): number[] {
363
+ const cutPoints: number[] = [];
364
+ for (let i = startIndex; i < endIndex; i++) {
365
+ const msg = messages[i];
366
+ switch (msg.role) {
367
+ case 'user':
368
+ case 'assistant':
369
+ case 'system':
370
+ cutPoints.push(i);
371
+ break;
372
+ case 'tool':
373
+ // Tool results cannot be cut at (they must follow their tool call)
374
+ break;
375
+ }
376
+ }
377
+ return cutPoints;
378
+ }
379
+
380
+ /**
381
+ * Find the user message that starts the turn containing the given index
382
+ */
383
+ findTurnStartIndex(messages: ChatMessage[], entryIndex: number, startIndex: number): number {
384
+ for (let i = entryIndex; i >= startIndex; i--) {
385
+ const role = messages[i].role;
386
+ if (role === 'user') {
387
+ return i;
388
+ }
389
+ }
390
+ return -1;
391
+ }
392
+
393
+ /**
394
+ * Find the cut point that keeps approximately keepRecentTokens
395
+ * Walks backwards from newest, accumulating estimated message sizes
396
+ */
397
+ findCutPoint(
398
+ messages: ChatMessage[],
399
+ startIndex: number,
400
+ endIndex: number,
401
+ keepRecentTokens: number
402
+ ): CutPointResult {
403
+ const cutPoints = this.findValidCutPoints(messages, startIndex, endIndex);
404
+
405
+ if (cutPoints.length === 0) {
406
+ return { firstKeptEntryIndex: startIndex, turnStartIndex: -1, isSplitTurn: false };
407
+ }
408
+
409
+ // Walk backwards from newest, accumulating estimated message sizes
410
+ let accumulatedTokens = 0;
411
+ let cutIndex = cutPoints[cutPoints.length - 1]; // Start with the last cut point
412
+
413
+ for (let i = endIndex - 1; i >= startIndex; i--) {
414
+ const messageTokens = this.estimateMessageTokens(messages[i]);
415
+ accumulatedTokens += messageTokens;
416
+
417
+ if (accumulatedTokens >= keepRecentTokens) {
418
+ // Find the closest valid cut point at or after this entry
419
+ // Search from the END of cutPoints array (closest to i)
420
+ for (let c = cutPoints.length - 1; c >= 0; c--) {
421
+ if (cutPoints[c] >= i) {
422
+ cutIndex = cutPoints[c];
423
+ } else {
424
+ // Since cutPoints is sorted ascending, no need to continue
425
+ break;
426
+ }
427
+ }
428
+ break;
429
+ }
430
+ }
431
+
432
+ // Determine if this is a split turn
433
+ const isUserMessage = messages[cutIndex].role === 'user';
434
+ const turnStartIndex = isUserMessage ? -1 : this.findTurnStartIndex(messages, cutIndex, startIndex);
435
+
436
+ return {
437
+ firstKeptEntryIndex: cutIndex,
438
+ turnStartIndex,
439
+ isSplitTurn: !isUserMessage && turnStartIndex !== -1
440
+ };
441
+ }
442
+
443
+ /**
444
+ * Extract file operations from messages by analyzing tool calls
445
+ */
446
+ extractFileOperations(messages: ChatMessage[]): FileOperations {
447
+ const fileOps: FileOperations = {
448
+ read: new Set<string>(),
449
+ modified: new Set<string>()
450
+ };
451
+
452
+ let totalToolCalls = 0;
453
+ let matchedToolCalls = 0;
454
+
455
+ // Normalize tool name (handle both API format and internal format)
456
+ const isReadTool = (name: string) => name === 'read_file' || name === 'Read';
457
+ const isWriteTool = (name: string) => name === 'write_file' || name === 'Write';
458
+ const isEditTool = (name: string) => name === 'Edit';
459
+ const isDeleteTool = (name: string) => name === 'DeleteFile';
460
+
461
+ const getFilePath = (args: any): string => {
462
+ return args.filePath || args.absolute_path || args.path || '';
463
+ };
464
+
465
+ for (const msg of messages) {
466
+ // Case 1: assistant with tool_calls field
467
+ if (msg.role === 'assistant' && msg.tool_calls) {
468
+ for (const toolCall of msg.tool_calls) {
469
+ totalToolCalls++;
470
+ const toolName = toolCall.function?.name || '';
471
+ let args = {};
472
+
473
+ try {
474
+ args = JSON.parse(toolCall.function?.arguments || '{}');
475
+ } catch {
476
+ continue;
477
+ }
478
+
479
+ const filePath = getFilePath(args);
480
+ if (!filePath) continue;
481
+
482
+ if (isReadTool(toolName)) {
483
+ fileOps.read.add(filePath);
484
+ matchedToolCalls++;
485
+ } else if (isWriteTool(toolName) || isEditTool(toolName) || isDeleteTool(toolName)) {
486
+ fileOps.modified.add(filePath);
487
+ matchedToolCalls++;
488
+ }
489
+ }
490
+ }
491
+
492
+ // Case 2: tool role with JSON content (like {"name":"Read","parameters":...})
493
+ if (msg.role === 'tool' && typeof msg.content === 'string') {
494
+ try {
495
+ const content = JSON.parse(msg.content);
496
+ totalToolCalls++;
497
+ const toolName = content.name || '';
498
+ const args = content.parameters || {};
499
+
500
+ const filePath = getFilePath(args);
501
+ if (!filePath) continue;
502
+
503
+ if (isReadTool(toolName)) {
504
+ fileOps.read.add(filePath);
505
+ matchedToolCalls++;
506
+ } else if (isWriteTool(toolName) || isEditTool(toolName) || isDeleteTool(toolName)) {
507
+ fileOps.modified.add(filePath);
508
+ matchedToolCalls++;
509
+ }
510
+ } catch {
511
+ // Not JSON, skip
512
+ }
513
+ }
514
+ }
515
+
516
+ return fileOps;
517
+ }
518
+
519
+ /**
520
+ * Merge file operations from previous compaction
521
+ */
522
+ mergeFileOps(ops1: FileOperations, ops2: FileOperations): FileOperations {
523
+ return {
524
+ read: new Set([...ops1.read, ...ops2.read]),
525
+ modified: new Set([...ops1.modified, ...ops2.modified])
526
+ };
527
+ }
528
+
529
+ /**
530
+ * Format file operations for inclusion in summary
531
+ */
532
+ formatFileOperations(fileOps: FileOperations): string {
533
+ const readFiles = Array.from(fileOps.read);
534
+ const modifiedFiles = Array.from(fileOps.modified);
535
+
536
+ if (readFiles.length === 0 && modifiedFiles.length === 0) {
537
+ return '';
538
+ }
539
+
540
+ let formatted = '\n\n## File Operations\n\n';
541
+
542
+ if (readFiles.length > 0) {
543
+ formatted += '### Files read_file\n';
544
+ for (const file of readFiles) {
545
+ formatted += `- ${file}\n`;
546
+ }
547
+ }
548
+
549
+ if (modifiedFiles.length > 0) {
550
+ formatted += '\n### Files Modified\n';
551
+ for (const file of modifiedFiles) {
552
+ formatted += `- ${file}\n`;
553
+ }
554
+ }
555
+
556
+ return formatted;
557
+ }
558
+
559
+ /**
560
+ * Prepare compaction - calculate cut point and extract messages to summarize
561
+ */
562
+ prepareCompaction(
563
+ messages: ChatMessage[],
564
+ keepRecentTokens: number
565
+ ): CompactionPreparation | undefined {
566
+ if (messages.length === 0) {
567
+ return undefined;
568
+ }
569
+
570
+ // Check if last message already contains a compression summary
571
+ const lastMsg = messages[messages.length - 1];
572
+ const isAlreadyCompressed = lastMsg.role === 'user' &&
573
+ lastMsg.content.includes('[Previous conversation summarized');
574
+
575
+ if (isAlreadyCompressed) {
576
+ return undefined;
577
+ }
578
+
579
+ const startIndex = 0;
580
+ const endIndex = messages.length;
581
+
582
+ // Find cut point
583
+ const cutPoint = this.findCutPoint(messages, startIndex, endIndex, keepRecentTokens);
584
+
585
+ // Extract messages to summarize
586
+
587
+ const historyEnd = cutPoint.firstKeptEntryIndex;
588
+
589
+
590
+
591
+ const messagesToSummarize: ChatMessage[] = [];
592
+
593
+ for (let i = startIndex; i < historyEnd; i++) {
594
+
595
+ messagesToSummarize.push(messages[i]);
596
+
597
+ }
598
+
599
+
600
+
601
+ // Extract turn prefix messages if splitting (disabled for simplicity)
602
+
603
+ const turnPrefixMessages: ChatMessage[] = [];
604
+ if (cutPoint.isSplitTurn) {
605
+ for (let i = cutPoint.turnStartIndex; i < cutPoint.firstKeptEntryIndex; i++) {
606
+ turnPrefixMessages.push(messages[i]);
607
+ }
608
+ }
609
+
610
+ // Get previous summary if exists (look for embedded summary in user messages)
611
+ let previousSummary: string | undefined;
612
+ for (let i = messages.length - 1; i >= 0; i--) {
613
+ const msg = messages[i];
614
+ if (msg.role === 'user' && msg.content.includes('[Previous conversation summarized')) {
615
+ // Extract the summary part from the content
616
+ const match = msg.content.match(/\[Previous conversation summarized.*?\]\n(.+?)(?=\n\n---\n\n|\n\n\[)/s);
617
+ if (match) {
618
+ previousSummary = match[1];
619
+ } else {
620
+ previousSummary = msg.content;
621
+ }
622
+ break;
623
+ }
624
+ }
625
+
626
+ // Extract file operations
627
+ const fileOps = this.extractFileOperations(messagesToSummarize);
628
+
629
+ // Also extract from turn prefix
630
+ if (cutPoint.isSplitTurn) {
631
+ const prefixOps = this.extractFileOperations(turnPrefixMessages);
632
+ return {
633
+ firstKeptEntryIndex: cutPoint.firstKeptEntryIndex,
634
+ messagesToSummarize,
635
+ turnPrefixMessages,
636
+ isSplitTurn: cutPoint.isSplitTurn,
637
+ tokensBefore: this.estimateContextTokens(messagesToSummarize),
638
+ previousSummary,
639
+ fileOps: this.mergeFileOps(fileOps, prefixOps)
640
+ };
641
+ }
642
+
643
+ return {
644
+ firstKeptEntryIndex: cutPoint.firstKeptEntryIndex,
645
+ messagesToSummarize,
646
+ turnPrefixMessages,
647
+ isSplitTurn: cutPoint.isSplitTurn,
648
+ tokensBefore: this.estimateContextTokens(messagesToSummarize),
649
+ previousSummary,
650
+ fileOps
651
+ };
652
+ }
653
+
654
+ /**
655
+ * Generate summary using AI
656
+ */
657
+ async generateSummary(
658
+ messages: ChatMessage[],
659
+ systemPrompt: string,
660
+ reserveTokens: number,
661
+ previousSummary?: string,
662
+ customInstructions?: string
663
+ ): Promise<string> {
664
+ if (!this.aiClient) {
665
+ throw new Error('AI client not initialized for summarization');
666
+ }
667
+
668
+ // Serialize conversation
669
+ const conversationText = messages
670
+ .map((m, idx) => {
671
+ const role = m.role === 'user' ? 'User' :
672
+ m.role === 'assistant' ? 'Assistant' :
673
+ m.role === 'tool' ? 'Tool' : m.role;
674
+ return `[${idx + 1}] ${role}:\n${m.content}`;
675
+ })
676
+ .join('\n\n' + '='.repeat(50) + '\n\n');
677
+
678
+ // Select prompt based on whether we have previous summary
679
+ let basePrompt = previousSummary ? UPDATE_SUMMARIZATION_PROMPT : SUMMARIZATION_PROMPT;
680
+ if (customInstructions) {
681
+ basePrompt = `${basePrompt}\n\nAdditional focus: ${customInstructions}`;
682
+ }
683
+
684
+ // Build prompt
685
+ let promptText = `<conversation>\n${conversationText}\n</conversation>\n\n`;
686
+ if (previousSummary) {
687
+ promptText += `<previous-summary>\n${previousSummary}\n</previous-summary>\n\n`;
688
+ }
689
+ promptText += basePrompt;
690
+
691
+ const maxTokens = Math.floor(reserveTokens * COMPRESSION_RATIO.MAX_TOKENS_RATIO);
692
+
693
+ const summaryMessage: Message = {
694
+ role: 'user',
695
+ content: promptText
696
+ };
697
+
698
+ const aiPromise = this.aiClient.compress([summaryMessage], {
699
+ maxTokens,
700
+ temperature: 0.3
701
+ });
702
+
703
+ try {
704
+ const response = await getCancellationManager().withCancellation(
705
+ aiPromise,
706
+ 'context-compression-summary'
707
+ );
708
+
709
+ const summary = response.choices[0]?.message?.content || '';
710
+ return typeof summary === 'string' ? summary : JSON.stringify(summary);
711
+ } catch (error: any) {
712
+ if (error.message === 'Operation cancelled by user') {
713
+ throw error;
714
+ }
715
+ console.error('Failed to generate summary:', error);
716
+ const userCount = messages.filter(m => m.role === 'user').length;
717
+ const toolCount = messages.filter(m => m.role === 'tool').length;
718
+ return `[Summary of ${messages.length} messages: ${userCount} user exchanges, ${toolCount} tool calls. Key topics discussed but details unavailable due to summarization error.]`;
719
+ }
720
+ }
721
+
722
+ /**
723
+ * Generate turn prefix summary when splitting a turn
724
+ */
725
+ async generateTurnPrefixSummary(
726
+ messages: ChatMessage[],
727
+ reserveTokens: number
728
+ ): Promise<string> {
729
+ if (!this.aiClient) {
730
+ throw new Error('AI client not initialized for summarization');
731
+ }
732
+
733
+ const conversationText = messages
734
+ .map((m, idx) => {
735
+ const role = m.role === 'user' ? 'User' :
736
+ m.role === 'assistant' ? 'Assistant' :
737
+ m.role === 'tool' ? 'Tool' : m.role;
738
+ return `[${idx + 1}] ${role}:\n${m.content}`;
739
+ })
740
+ .join('\n\n' + '='.repeat(50) + '\n\n');
741
+
742
+ const promptText = `<conversation>\n${conversationText}\n</conversation>\n\n${TURN_PREFIX_SUMMARIZATION_PROMPT}`;
743
+ const maxTokens = Math.floor(reserveTokens * COMPRESSION_RATIO.SUMMARY_MAX_TOKENS_RATIO);
744
+
745
+ const summaryMessage: Message = {
746
+ role: 'user',
747
+ content: promptText
748
+ };
749
+
750
+ const aiPromise = this.aiClient.compress([summaryMessage], {
751
+ maxTokens,
752
+ temperature: 0.3
753
+ });
754
+
755
+ try {
756
+ const response = await getCancellationManager().withCancellation(
757
+ aiPromise,
758
+ 'context-compression-turn-prefix'
759
+ );
760
+
761
+ const content = response.choices[0]?.message?.content || '';
762
+ return typeof content === 'string' ? content : JSON.stringify(content);
763
+ } catch (error: any) {
764
+ if (error.message === 'Operation cancelled by user') {
765
+ throw error;
766
+ }
767
+ console.error('Failed to generate turn prefix summary:', error);
768
+ return '[Turn prefix summary unavailable]';
769
+ }
770
+ }
771
+
772
+ /**
773
+ * Main compression function with incremental compaction support
774
+ */
775
+ async compressContext(
776
+ messages: ChatMessage[],
777
+ systemPrompt: string,
778
+ config?: Partial<CompressionConfig>,
779
+ previousSummary?: string,
780
+ modelName?: string
781
+ ): Promise<CompressionResult> {
782
+ const cfg = { ...this.defaultConfig, ...config };
783
+ const originalMessageCount = messages.length;
784
+ const originalSize = messages.reduce((total, msg) => total + msg.content.length, 0);
785
+ const originalTokens = this.estimateContextTokens(messages);
786
+ const contextWindow = getModelContextWindow(modelName);
787
+
788
+ // Check if compression is needed
789
+ const { needsCompression } = this.needsCompression(messages, config, modelName);
790
+ if (!needsCompression) {
791
+ return {
792
+ compressedMessages: messages,
793
+ wasCompressed: false,
794
+ originalMessageCount,
795
+ compressedMessageCount: messages.length,
796
+ originalSize,
797
+ compressedSize: originalSize,
798
+ compressionMethod: 'none'
799
+ };
800
+ }
801
+
802
+ // Prepare compaction
803
+ // Reserve 50% of context window for new conversation, 15% for summary
804
+ const reserveTokens = Math.floor(contextWindow * COMPRESSION_RATIO.RESERVE_RATIO);
805
+ const summaryReserveTokens = Math.max(COMPRESSION_RATIO.MIN_SUMMARY_TOKENS, Math.floor((contextWindow - reserveTokens) * COMPRESSION_RATIO.SUMMARY_THRESHOLD_RATIO));
806
+ const keepRecentTokens = contextWindow - reserveTokens - summaryReserveTokens;
807
+ const preparation = this.prepareCompaction(messages, Math.max(0, keepRecentTokens));
808
+
809
+ if (!preparation) {
810
+ // Already compressed or no valid cut point
811
+ return {
812
+ compressedMessages: messages,
813
+ wasCompressed: false,
814
+ originalMessageCount,
815
+ compressedMessageCount: messages.length,
816
+ originalSize,
817
+ compressedSize: originalSize,
818
+ compressionMethod: 'none'
819
+ };
820
+ }
821
+
822
+ const {
823
+ firstKeptEntryIndex,
824
+ messagesToSummarize,
825
+ turnPrefixMessages,
826
+ isSplitTurn,
827
+ tokensBefore,
828
+ fileOps
829
+ } = preparation;
830
+
831
+ // Generate summary
832
+ let summary: string;
833
+
834
+ if (isSplitTurn && turnPrefixMessages.length > 0) {
835
+ // Generate both summaries in parallel
836
+ const [historyResult, turnPrefixResult] = await Promise.all([
837
+ messagesToSummarize.length > 0
838
+ ? this.generateSummary(messagesToSummarize, systemPrompt, summaryReserveTokens, previousSummary)
839
+ : Promise.resolve('No prior history.'),
840
+ this.generateTurnPrefixSummary(turnPrefixMessages, summaryReserveTokens)
841
+ ]);
842
+ summary = `${historyResult}\n\n---\n\n**Turn Context (split turn):**\n\n${turnPrefixResult}`;
843
+ } else {
844
+ summary = await this.generateSummary(
845
+ messagesToSummarize,
846
+ systemPrompt,
847
+ summaryReserveTokens,
848
+ previousSummary
849
+ );
850
+ }
851
+
852
+ // Add file operations to summary
853
+ summary += this.formatFileOperations(fileOps);
854
+
855
+ // Build compressed messages: summary + kept messages
856
+ // Summary is inserted as prefix in first user message to maintain valid message order
857
+ const compressedMessages: ChatMessage[] = [];
858
+
859
+ if (messagesToSummarize.length > 0) {
860
+ // Find first user message in kept messages
861
+ let firstKeptUserMsg: ChatMessage | null = null;
862
+ let firstKeptUserIndex = -1;
863
+ for (let i = firstKeptEntryIndex; i < messages.length; i++) {
864
+ if (messages[i].role === 'user') {
865
+ firstKeptUserMsg = messages[i];
866
+ firstKeptUserIndex = i;
867
+ break;
868
+ }
869
+ }
870
+
871
+ if (firstKeptUserMsg) {
872
+ // Prepend summary to first user message content
873
+ const summaryPrefix = `[Previous conversation summarized (${messagesToSummarize.length} messages):]\n${summary}\n\n---\n\n`;
874
+ compressedMessages.push({
875
+ role: 'user',
876
+ content: summaryPrefix + firstKeptUserMsg.content,
877
+ timestamp: firstKeptUserMsg.timestamp,
878
+ images: firstKeptUserMsg.images
879
+ });
880
+
881
+ // Add remaining kept messages (skip the modified first user message)
882
+ for (let i = firstKeptUserIndex + 1; i < messages.length; i++) {
883
+ const msg = messages[i];
884
+ compressedMessages.push({
885
+ role: msg.role,
886
+ content: msg.content,
887
+ timestamp: msg.timestamp,
888
+ images: msg.images,
889
+ reasoning_content: msg.reasoning_content,
890
+ tool_calls: msg.tool_calls,
891
+ tool_call_id: msg.tool_call_id
892
+ });
893
+ }
894
+ } else {
895
+ // No user message in kept messages (rare case)
896
+ // Insert summary as a user message, then add all kept messages
897
+ // This ensures valid message order: user → assistant → tool → tool...
898
+ compressedMessages.push({
899
+ role: 'user',
900
+ content: `[Conversation Summary - ${messagesToSummarize.length} messages compressed]\n\n${summary}`,
901
+ timestamp: Date.now()
902
+ });
903
+
904
+ // Add all kept messages (they may start with assistant or tool)
905
+ for (let i = firstKeptEntryIndex; i < messages.length; i++) {
906
+ const msg = messages[i];
907
+ compressedMessages.push({
908
+ role: msg.role,
909
+ content: msg.content,
910
+ timestamp: msg.timestamp,
911
+ images: msg.images,
912
+ reasoning_content: msg.reasoning_content,
913
+ tool_calls: msg.tool_calls,
914
+ tool_call_id: msg.tool_call_id
915
+ });
916
+ }
917
+ }
918
+ } else {
919
+ // No messages to summarize, just keep all messages
920
+ for (let i = firstKeptEntryIndex; i < messages.length; i++) {
921
+ const msg = messages[i];
922
+ compressedMessages.push({
923
+ role: msg.role,
924
+ content: msg.content,
925
+ timestamp: msg.timestamp,
926
+ images: msg.images,
927
+ reasoning_content: msg.reasoning_content,
928
+ tool_calls: msg.tool_calls,
929
+ tool_call_id: msg.tool_call_id
930
+ });
931
+ }
932
+ }
933
+
934
+ const compressedSize = compressedMessages.reduce((total, msg) => total + msg.content.length, 0);
935
+ const compressedTokens = this.estimateContextTokens(compressedMessages);
936
+ const reductionPercent = Math.round((1 - compressedSize / originalSize) * 100);
937
+
938
+ return {
939
+ compressedMessages,
940
+ wasCompressed: true,
941
+ originalMessageCount,
942
+ compressedMessageCount: compressedMessages.length,
943
+ originalSize,
944
+ compressedSize,
945
+ compressionMethod: 'summary',
946
+ tokensBefore,
947
+ details: {
948
+ readFiles: Array.from(fileOps.read),
949
+ modifiedFiles: Array.from(fileOps.modified)
950
+ }
951
+ };
952
+ }
953
+
954
+ /**
955
+ * Create compressed message copy for saving
956
+ */
957
+ createCompressedSnapshot(
958
+ messages: ChatMessage[],
959
+ compressionResult: CompressionResult
960
+ ): object {
961
+ return {
962
+ timestamp: Date.now(),
963
+ originalMessageCount: compressionResult.originalMessageCount,
964
+ compressedMessageCount: compressionResult.compressedMessageCount,
965
+ originalSize: compressionResult.originalSize,
966
+ compressedSize: compressionResult.compressedSize,
967
+ compressionMethod: compressionResult.compressionMethod,
968
+ tokensBefore: compressionResult.tokensBefore,
969
+ details: compressionResult.details,
970
+ messages: compressionResult.compressedMessages
971
+ };
972
+ }
973
+ }
974
+
975
+ let compressorInstance: ContextCompressor | null = null;
976
+
977
+ export function getContextCompressor(authConfig?: AuthConfig): ContextCompressor {
978
+ if (!compressorInstance) {
979
+ compressorInstance = new ContextCompressor(authConfig);
980
+ }
981
+ return compressorInstance;
982
+ }