@xagent-ai/cli 1.2.2 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (602) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
  3. package/.github/release.yml +76 -0
  4. package/.github/workflows/ci.yml +75 -0
  5. package/.github/workflows/release.yml +103 -0
  6. package/.gitmodules +3 -3
  7. package/README.md +326 -280
  8. package/README_CN.md +325 -279
  9. package/dist/agents.d.ts.map +1 -1
  10. package/dist/agents.js +7 -3
  11. package/dist/agents.js.map +1 -1
  12. package/dist/ai-client/factory.d.ts +40 -0
  13. package/dist/ai-client/factory.d.ts.map +1 -0
  14. package/dist/ai-client/factory.js +100 -0
  15. package/dist/ai-client/factory.js.map +1 -0
  16. package/dist/ai-client/index.d.ts +20 -0
  17. package/dist/ai-client/index.d.ts.map +1 -0
  18. package/dist/ai-client/index.js +49 -0
  19. package/dist/ai-client/index.js.map +1 -0
  20. package/dist/ai-client/providers/anthropic.d.ts +57 -0
  21. package/dist/ai-client/providers/anthropic.d.ts.map +1 -0
  22. package/dist/ai-client/providers/anthropic.js +406 -0
  23. package/dist/ai-client/providers/anthropic.js.map +1 -0
  24. package/dist/ai-client/providers/openai.d.ts +57 -0
  25. package/dist/ai-client/providers/openai.d.ts.map +1 -0
  26. package/dist/ai-client/providers/openai.js +290 -0
  27. package/dist/ai-client/providers/openai.js.map +1 -0
  28. package/dist/ai-client/providers/remote.d.ts +110 -0
  29. package/dist/ai-client/providers/remote.d.ts.map +1 -0
  30. package/dist/ai-client/providers/remote.js +352 -0
  31. package/dist/ai-client/providers/remote.js.map +1 -0
  32. package/dist/ai-client/registry.d.ts +51 -0
  33. package/dist/ai-client/registry.d.ts.map +1 -0
  34. package/dist/ai-client/registry.js +81 -0
  35. package/dist/ai-client/registry.js.map +1 -0
  36. package/dist/ai-client/types.d.ts +274 -0
  37. package/dist/ai-client/types.d.ts.map +1 -0
  38. package/dist/ai-client/types.js +90 -0
  39. package/dist/ai-client/types.js.map +1 -0
  40. package/dist/ai-client-factory.d.ts +62 -0
  41. package/dist/ai-client-factory.d.ts.map +1 -0
  42. package/dist/ai-client-factory.js +157 -0
  43. package/dist/ai-client-factory.js.map +1 -0
  44. package/dist/auth.d.ts +23 -1
  45. package/dist/auth.d.ts.map +1 -1
  46. package/dist/auth.js +164 -174
  47. package/dist/auth.js.map +1 -1
  48. package/dist/cancellation.d.ts +5 -4
  49. package/dist/cancellation.d.ts.map +1 -1
  50. package/dist/cancellation.js +53 -32
  51. package/dist/cancellation.js.map +1 -1
  52. package/dist/checkpoint.d.ts +2 -1
  53. package/dist/checkpoint.d.ts.map +1 -1
  54. package/dist/checkpoint.js +39 -6
  55. package/dist/checkpoint.js.map +1 -1
  56. package/dist/cli.js +742 -29
  57. package/dist/cli.js.map +1 -1
  58. package/dist/config.d.ts +10 -4
  59. package/dist/config.d.ts.map +1 -1
  60. package/dist/config.js +62 -25
  61. package/dist/config.js.map +1 -1
  62. package/dist/context-compressor.d.ts +82 -18
  63. package/dist/context-compressor.d.ts.map +1 -1
  64. package/dist/context-compressor.js +718 -154
  65. package/dist/context-compressor.js.map +1 -1
  66. package/dist/conversation.d.ts +1 -1
  67. package/dist/conversation.d.ts.map +1 -1
  68. package/dist/conversation.js +8 -7
  69. package/dist/conversation.js.map +1 -1
  70. package/dist/gui-subagent/action-parser/actionParser.d.ts.map +1 -1
  71. package/dist/gui-subagent/action-parser/actionParser.js +6 -4
  72. package/dist/gui-subagent/action-parser/actionParser.js.map +1 -1
  73. package/dist/gui-subagent/agent/gui-agent.d.ts +39 -2
  74. package/dist/gui-subagent/agent/gui-agent.d.ts.map +1 -1
  75. package/dist/gui-subagent/agent/gui-agent.js +189 -74
  76. package/dist/gui-subagent/agent/gui-agent.js.map +1 -1
  77. package/dist/gui-subagent/index.d.ts +23 -1
  78. package/dist/gui-subagent/index.d.ts.map +1 -1
  79. package/dist/gui-subagent/index.js +6 -0
  80. package/dist/gui-subagent/index.js.map +1 -1
  81. package/dist/gui-subagent/operator/base-operator.d.ts.map +1 -1
  82. package/dist/gui-subagent/operator/base-operator.js +0 -1
  83. package/dist/gui-subagent/operator/base-operator.js.map +1 -1
  84. package/dist/gui-subagent/operator/computer-operator.d.ts.map +1 -1
  85. package/dist/gui-subagent/operator/computer-operator.js +31 -8
  86. package/dist/gui-subagent/operator/computer-operator.js.map +1 -1
  87. package/dist/gui-subagent/types/actions.d.ts +1 -1
  88. package/dist/gui-subagent/types/actions.d.ts.map +1 -1
  89. package/dist/gui-subagent/types/actions.js +0 -1
  90. package/dist/gui-subagent/types/actions.js.map +1 -1
  91. package/dist/gui-subagent/types/operator.d.ts +1 -1
  92. package/dist/gui-subagent/types/operator.d.ts.map +1 -1
  93. package/dist/index.d.ts +1 -2
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +1 -2
  96. package/dist/index.js.map +1 -1
  97. package/dist/input-processor.d.ts.map +1 -1
  98. package/dist/input-processor.js +8 -5
  99. package/dist/input-processor.js.map +1 -1
  100. package/dist/logger.d.ts.map +1 -1
  101. package/dist/logger.js +1 -1
  102. package/dist/logger.js.map +1 -1
  103. package/dist/mcp.d.ts +7 -1
  104. package/dist/mcp.d.ts.map +1 -1
  105. package/dist/mcp.js +157 -49
  106. package/dist/mcp.js.map +1 -1
  107. package/dist/memory.d.ts.map +1 -1
  108. package/dist/memory.js +3 -3
  109. package/dist/memory.js.map +1 -1
  110. package/dist/output-util.d.ts +27 -0
  111. package/dist/output-util.d.ts.map +1 -0
  112. package/dist/output-util.js +74 -0
  113. package/dist/output-util.js.map +1 -0
  114. package/dist/retry.js +1 -1
  115. package/dist/retry.js.map +1 -1
  116. package/dist/ripgrep.d.ts +29 -0
  117. package/dist/ripgrep.d.ts.map +1 -0
  118. package/dist/ripgrep.js +294 -0
  119. package/dist/ripgrep.js.map +1 -0
  120. package/dist/sdk-output-adapter.d.ts +34 -1
  121. package/dist/sdk-output-adapter.d.ts.map +1 -1
  122. package/dist/sdk-output-adapter.js +67 -2
  123. package/dist/sdk-output-adapter.js.map +1 -1
  124. package/dist/sdk-session.d.ts.map +1 -1
  125. package/dist/sdk-session.js +2 -0
  126. package/dist/sdk-session.js.map +1 -1
  127. package/dist/session-manager.js +3 -3
  128. package/dist/session-manager.js.map +1 -1
  129. package/dist/session.d.ts +116 -6
  130. package/dist/session.d.ts.map +1 -1
  131. package/dist/session.js +1416 -448
  132. package/dist/session.js.map +1 -1
  133. package/dist/shell.d.ts +33 -0
  134. package/dist/shell.d.ts.map +1 -0
  135. package/dist/shell.js +126 -0
  136. package/dist/shell.js.map +1 -0
  137. package/dist/skill-installer.d.ts +38 -0
  138. package/dist/skill-installer.d.ts.map +1 -0
  139. package/dist/skill-installer.js +447 -0
  140. package/dist/skill-installer.js.map +1 -0
  141. package/dist/skill-invoker.d.ts +8 -2
  142. package/dist/skill-invoker.d.ts.map +1 -1
  143. package/dist/skill-invoker.js +36 -15
  144. package/dist/skill-invoker.js.map +1 -1
  145. package/dist/skill-loader.d.ts +8 -3
  146. package/dist/skill-loader.d.ts.map +1 -1
  147. package/dist/skill-loader.js +51 -48
  148. package/dist/skill-loader.js.map +1 -1
  149. package/dist/skill-manager.d.ts +85 -0
  150. package/dist/skill-manager.d.ts.map +1 -0
  151. package/dist/skill-manager.js +341 -0
  152. package/dist/skill-manager.js.map +1 -0
  153. package/dist/slash-commands.d.ts +39 -2
  154. package/dist/slash-commands.d.ts.map +1 -1
  155. package/dist/slash-commands.js +934 -305
  156. package/dist/slash-commands.js.map +1 -1
  157. package/dist/smart-approval.d.ts +20 -1
  158. package/dist/smart-approval.d.ts.map +1 -1
  159. package/dist/smart-approval.js +125 -56
  160. package/dist/smart-approval.js.map +1 -1
  161. package/dist/system-prompt-generator.d.ts +6 -0
  162. package/dist/system-prompt-generator.d.ts.map +1 -1
  163. package/dist/system-prompt-generator.js +86 -36
  164. package/dist/system-prompt-generator.js.map +1 -1
  165. package/dist/terminal.d.ts +28 -0
  166. package/dist/terminal.d.ts.map +1 -0
  167. package/dist/terminal.js +82 -0
  168. package/dist/terminal.js.map +1 -0
  169. package/dist/theme.d.ts.map +1 -1
  170. package/dist/theme.js +8 -7
  171. package/dist/theme.js.map +1 -1
  172. package/dist/tools.d.ts +38 -7
  173. package/dist/tools.d.ts.map +1 -1
  174. package/dist/tools.js +1249 -617
  175. package/dist/tools.js.map +1 -1
  176. package/dist/truncate.d.ts +55 -0
  177. package/dist/truncate.d.ts.map +1 -0
  178. package/dist/truncate.js +130 -0
  179. package/dist/truncate.js.map +1 -0
  180. package/dist/types.d.ts +84 -9
  181. package/dist/types.d.ts.map +1 -1
  182. package/dist/types.js +49 -0
  183. package/dist/types.js.map +1 -1
  184. package/dist/update.d.ts.map +1 -1
  185. package/dist/update.js +28 -36
  186. package/dist/update.js.map +1 -1
  187. package/dist/workflow.d.ts +5 -1
  188. package/dist/workflow.d.ts.map +1 -1
  189. package/dist/workflow.js +61 -49
  190. package/dist/workflow.js.map +1 -1
  191. package/docs/architecture/mcp-integration-guide.md +304 -194
  192. package/docs/architecture/overview.md +169 -169
  193. package/docs/architecture/tool-system-design.md +134 -134
  194. package/docs/cli/commands.md +349 -238
  195. package/docs/smart-mode.md +281 -281
  196. package/docs/third-party-models.md +440 -439
  197. package/find-skills/SKILL.md +133 -0
  198. package/package.json +91 -90
  199. package/scripts/install-ripgrep.js +241 -0
  200. package/src/agents.ts +7 -3
  201. package/src/ai-client/factory.ts +116 -0
  202. package/src/ai-client/index.ts +61 -0
  203. package/src/ai-client/providers/anthropic.ts +475 -0
  204. package/src/ai-client/providers/openai.ts +348 -0
  205. package/src/ai-client/providers/remote.ts +439 -0
  206. package/src/ai-client/registry.ts +97 -0
  207. package/src/ai-client/types.ts +364 -0
  208. package/src/ai-client-factory.ts +204 -0
  209. package/src/auth.ts +661 -614
  210. package/src/cancellation.ts +202 -176
  211. package/src/checkpoint.ts +255 -219
  212. package/src/cli.ts +1523 -743
  213. package/src/config.ts +341 -297
  214. package/src/context-compressor.ts +987 -290
  215. package/src/conversation.ts +290 -288
  216. package/src/gui-subagent/action-parser/actionParser.ts +318 -315
  217. package/src/gui-subagent/action-parser/constants.ts +14 -14
  218. package/src/gui-subagent/action-parser/index.ts +8 -8
  219. package/src/gui-subagent/action-parser/types.ts +31 -31
  220. package/src/gui-subagent/agent/gui-agent.ts +1234 -1089
  221. package/src/gui-subagent/agent/index.ts +5 -5
  222. package/src/gui-subagent/index.ts +185 -163
  223. package/src/gui-subagent/operator/base-operator.ts +244 -245
  224. package/src/gui-subagent/operator/computer-operator.ts +541 -520
  225. package/src/gui-subagent/operator/index.ts +6 -6
  226. package/src/gui-subagent/types/actions.ts +260 -262
  227. package/src/gui-subagent/types/index.ts +6 -6
  228. package/src/gui-subagent/types/operator.ts +106 -106
  229. package/src/gui-subagent/utils.ts +51 -51
  230. package/src/index.ts +17 -18
  231. package/src/input-processor.ts +8 -5
  232. package/src/logger.ts +436 -438
  233. package/src/mcp.ts +793 -682
  234. package/src/memory.ts +343 -344
  235. package/src/output-util.ts +80 -0
  236. package/src/retry.ts +1 -1
  237. package/src/ripgrep.ts +370 -0
  238. package/src/sdk-output-adapter.ts +842 -0
  239. package/src/sdk-session.ts +62 -0
  240. package/src/session-manager.ts +308 -308
  241. package/src/session.ts +1775 -573
  242. package/src/shell.ts +134 -0
  243. package/src/skill-installer.ts +518 -0
  244. package/src/skill-invoker.ts +959 -935
  245. package/src/skill-loader.ts +501 -496
  246. package/src/skill-manager.ts +385 -0
  247. package/src/slash-commands.ts +2189 -1389
  248. package/src/smart-approval.ts +193 -74
  249. package/src/system-prompt-generator.ts +91 -36
  250. package/src/terminal.ts +96 -0
  251. package/src/theme.ts +739 -738
  252. package/src/tools.ts +1790 -931
  253. package/src/truncate.ts +173 -0
  254. package/src/types.ts +337 -198
  255. package/src/update.ts +33 -40
  256. package/src/workflow.ts +521 -508
  257. package/test/cli-launch.test.ts +279 -0
  258. package/tsconfig.json +22 -22
  259. package/vitest.config.ts +21 -19
  260. package/dist/ai-client.d.ts +0 -86
  261. package/dist/ai-client.d.ts.map +0 -1
  262. package/dist/ai-client.js +0 -1372
  263. package/dist/ai-client.js.map +0 -1
  264. package/dist/gui-subagent/operator/browser-operator.d.ts +0 -36
  265. package/dist/gui-subagent/operator/browser-operator.d.ts.map +0 -1
  266. package/dist/gui-subagent/operator/browser-operator.js +0 -306
  267. package/dist/gui-subagent/operator/browser-operator.js.map +0 -1
  268. package/dist/gui-subagent/operator/desktop-operator.d.ts +0 -55
  269. package/dist/gui-subagent/operator/desktop-operator.d.ts.map +0 -1
  270. package/dist/gui-subagent/operator/desktop-operator.js +0 -527
  271. package/dist/gui-subagent/operator/desktop-operator.js.map +0 -1
  272. package/dist/hook.d.ts +0 -73
  273. package/dist/hook.d.ts.map +0 -1
  274. package/dist/hook.js +0 -156
  275. package/dist/hook.js.map +0 -1
  276. package/dist/input-history.d.ts +0 -24
  277. package/dist/input-history.d.ts.map +0 -1
  278. package/dist/input-history.js +0 -94
  279. package/dist/input-history.js.map +0 -1
  280. package/dist/keyboard-manager.d.ts +0 -151
  281. package/dist/keyboard-manager.d.ts.map +0 -1
  282. package/dist/keyboard-manager.js +0 -396
  283. package/dist/keyboard-manager.js.map +0 -1
  284. package/dist/print-system-prompt.d.ts +0 -2
  285. package/dist/print-system-prompt.d.ts.map +0 -1
  286. package/dist/print-system-prompt.js +0 -40
  287. package/dist/print-system-prompt.js.map +0 -1
  288. package/dist/remote-ai-client.d.ts +0 -104
  289. package/dist/remote-ai-client.d.ts.map +0 -1
  290. package/dist/remote-ai-client.js +0 -552
  291. package/dist/remote-ai-client.js.map +0 -1
  292. package/dist/sdk-session-v2.d.ts +0 -13
  293. package/dist/sdk-session-v2.d.ts.map +0 -1
  294. package/dist/sdk-session-v2.js +0 -46
  295. package/dist/sdk-session-v2.js.map +0 -1
  296. package/dist/test-boundary-conditions.d.ts.map +0 -1
  297. package/dist/test-boundary-conditions.js.map +0 -1
  298. package/dist/test-cancellation-fix.d.ts.map +0 -1
  299. package/dist/test-cancellation-fix.js.map +0 -1
  300. package/dist/test-input-history.d.ts.map +0 -1
  301. package/dist/test-input-history.js.map +0 -1
  302. package/dist/test-interaction-flow.d.ts.map +0 -1
  303. package/dist/test-interaction-flow.js.map +0 -1
  304. package/dist/test-quick.d.ts.map +0 -1
  305. package/dist/test-quick.js.map +0 -1
  306. package/dist/test-user-interaction.d.ts.map +0 -1
  307. package/dist/test-user-interaction.js.map +0 -1
  308. package/dist/tools/edit-diff.d.ts +0 -32
  309. package/dist/tools/edit-diff.d.ts.map +0 -1
  310. package/dist/tools/edit-diff.js +0 -185
  311. package/dist/tools/edit-diff.js.map +0 -1
  312. package/dist/tools/edit.d.ts +0 -11
  313. package/dist/tools/edit.d.ts.map +0 -1
  314. package/dist/tools/edit.js +0 -129
  315. package/dist/tools/edit.js.map +0 -1
  316. package/dist/unified-session.d.ts +0 -42
  317. package/dist/unified-session.d.ts.map +0 -1
  318. package/dist/unified-session.js +0 -271
  319. package/dist/unified-session.js.map +0 -1
  320. package/skills/.claude-plugin/marketplace.json +0 -45
  321. package/skills/README.md +0 -94
  322. package/skills/THIRD_PARTY_NOTICES.md +0 -405
  323. package/skills/skills/algorithmic-art/LICENSE.txt +0 -202
  324. package/skills/skills/algorithmic-art/SKILL.md +0 -405
  325. package/skills/skills/algorithmic-art/templates/generator_template.js +0 -223
  326. package/skills/skills/algorithmic-art/templates/viewer.html +0 -599
  327. package/skills/skills/brand-guidelines/LICENSE.txt +0 -202
  328. package/skills/skills/brand-guidelines/SKILL.md +0 -73
  329. package/skills/skills/canvas-design/LICENSE.txt +0 -202
  330. package/skills/skills/canvas-design/SKILL.md +0 -130
  331. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -93
  332. package/skills/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  333. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  334. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -93
  335. package/skills/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  336. package/skills/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -93
  337. package/skills/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  338. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  339. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -93
  340. package/skills/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  341. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  342. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  343. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -93
  344. package/skills/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  345. package/skills/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -93
  346. package/skills/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  347. package/skills/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -94
  348. package/skills/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  349. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  350. package/skills/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -93
  351. package/skills/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  352. package/skills/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -93
  353. package/skills/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  354. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  355. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -93
  356. package/skills/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  357. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  358. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  359. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  360. package/skills/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  361. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  362. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  363. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  364. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -93
  365. package/skills/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  366. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  367. package/skills/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  368. package/skills/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -93
  369. package/skills/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  370. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  371. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -93
  372. package/skills/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  373. package/skills/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  374. package/skills/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  375. package/skills/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -93
  376. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -93
  377. package/skills/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  378. package/skills/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  379. package/skills/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  380. package/skills/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  381. package/skills/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -93
  382. package/skills/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  383. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  384. package/skills/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -93
  385. package/skills/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  386. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -93
  387. package/skills/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  388. package/skills/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  389. package/skills/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -93
  390. package/skills/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  391. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  392. package/skills/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -93
  393. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -93
  394. package/skills/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  395. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  396. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -93
  397. package/skills/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  398. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -93
  399. package/skills/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  400. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  401. package/skills/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -93
  402. package/skills/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  403. package/skills/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -93
  404. package/skills/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  405. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  406. package/skills/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  407. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  408. package/skills/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -93
  409. package/skills/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  410. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -93
  411. package/skills/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  412. package/skills/skills/doc-coauthoring/SKILL.md +0 -375
  413. package/skills/skills/docx/LICENSE.txt +0 -30
  414. package/skills/skills/docx/SKILL.md +0 -197
  415. package/skills/skills/docx/docx-js.md +0 -350
  416. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  417. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  418. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  419. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  420. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  421. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  422. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  423. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  424. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  425. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  426. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  427. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  428. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  429. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  430. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  431. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  432. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  433. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  434. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  435. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  436. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  437. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  438. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  439. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  440. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  441. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  442. package/skills/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  443. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  444. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  445. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  446. package/skills/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  447. package/skills/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  448. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  449. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  450. package/skills/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  451. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  452. package/skills/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  453. package/skills/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  454. package/skills/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  455. package/skills/skills/docx/ooxml/scripts/pack.py +0 -159
  456. package/skills/skills/docx/ooxml/scripts/unpack.py +0 -29
  457. package/skills/skills/docx/ooxml/scripts/validate.py +0 -69
  458. package/skills/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  459. package/skills/skills/docx/ooxml/scripts/validation/base.py +0 -951
  460. package/skills/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  461. package/skills/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  462. package/skills/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  463. package/skills/skills/docx/ooxml.md +0 -610
  464. package/skills/skills/docx/scripts/__init__.py +0 -1
  465. package/skills/skills/docx/scripts/document.py +0 -1276
  466. package/skills/skills/docx/scripts/templates/comments.xml +0 -3
  467. package/skills/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  468. package/skills/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  469. package/skills/skills/docx/scripts/templates/commentsIds.xml +0 -3
  470. package/skills/skills/docx/scripts/templates/people.xml +0 -3
  471. package/skills/skills/docx/scripts/utilities.py +0 -374
  472. package/skills/skills/frontend-design/LICENSE.txt +0 -177
  473. package/skills/skills/frontend-design/SKILL.md +0 -42
  474. package/skills/skills/internal-comms/LICENSE.txt +0 -202
  475. package/skills/skills/internal-comms/SKILL.md +0 -32
  476. package/skills/skills/internal-comms/examples/3p-updates.md +0 -47
  477. package/skills/skills/internal-comms/examples/company-newsletter.md +0 -65
  478. package/skills/skills/internal-comms/examples/faq-answers.md +0 -30
  479. package/skills/skills/internal-comms/examples/general-comms.md +0 -16
  480. package/skills/skills/mcp-builder/LICENSE.txt +0 -202
  481. package/skills/skills/mcp-builder/SKILL.md +0 -236
  482. package/skills/skills/mcp-builder/reference/evaluation.md +0 -602
  483. package/skills/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  484. package/skills/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  485. package/skills/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  486. package/skills/skills/mcp-builder/scripts/connections.py +0 -151
  487. package/skills/skills/mcp-builder/scripts/evaluation.py +0 -373
  488. package/skills/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  489. package/skills/skills/mcp-builder/scripts/requirements.txt +0 -2
  490. package/skills/skills/pdf/LICENSE.txt +0 -30
  491. package/skills/skills/pdf/SKILL.md +0 -294
  492. package/skills/skills/pdf/forms.md +0 -205
  493. package/skills/skills/pdf/reference.md +0 -612
  494. package/skills/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  495. package/skills/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  496. package/skills/skills/pdf/scripts/check_fillable_fields.py +0 -12
  497. package/skills/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  498. package/skills/skills/pdf/scripts/create_validation_image.py +0 -41
  499. package/skills/skills/pdf/scripts/extract_form_field_info.py +0 -152
  500. package/skills/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  501. package/skills/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  502. package/skills/skills/pptx/LICENSE.txt +0 -30
  503. package/skills/skills/pptx/SKILL.md +0 -484
  504. package/skills/skills/pptx/html2pptx.md +0 -625
  505. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  506. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  507. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  508. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  509. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  510. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  511. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  512. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  513. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  514. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  515. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  516. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  517. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  518. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  519. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  520. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  521. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  522. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  523. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  524. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  525. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  526. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  527. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  528. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  529. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  530. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  531. package/skills/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  532. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  533. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  534. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  535. package/skills/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  536. package/skills/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
  537. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  538. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  539. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  540. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  541. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  542. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  543. package/skills/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  544. package/skills/skills/pptx/ooxml/scripts/pack.py +0 -159
  545. package/skills/skills/pptx/ooxml/scripts/unpack.py +0 -29
  546. package/skills/skills/pptx/ooxml/scripts/validate.py +0 -69
  547. package/skills/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
  548. package/skills/skills/pptx/ooxml/scripts/validation/base.py +0 -951
  549. package/skills/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
  550. package/skills/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
  551. package/skills/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
  552. package/skills/skills/pptx/ooxml.md +0 -427
  553. package/skills/skills/pptx/scripts/html2pptx.js +0 -979
  554. package/skills/skills/pptx/scripts/inventory.py +0 -1020
  555. package/skills/skills/pptx/scripts/rearrange.py +0 -231
  556. package/skills/skills/pptx/scripts/replace.py +0 -385
  557. package/skills/skills/pptx/scripts/thumbnail.py +0 -450
  558. package/skills/skills/skill-creator/LICENSE.txt +0 -202
  559. package/skills/skills/skill-creator/SKILL.md +0 -356
  560. package/skills/skills/skill-creator/references/output-patterns.md +0 -82
  561. package/skills/skills/skill-creator/references/workflows.md +0 -28
  562. package/skills/skills/skill-creator/scripts/init_skill.py +0 -303
  563. package/skills/skills/skill-creator/scripts/package_skill.py +0 -110
  564. package/skills/skills/skill-creator/scripts/quick_validate.py +0 -95
  565. package/skills/skills/slack-gif-creator/LICENSE.txt +0 -202
  566. package/skills/skills/slack-gif-creator/SKILL.md +0 -254
  567. package/skills/skills/slack-gif-creator/core/easing.py +0 -234
  568. package/skills/skills/slack-gif-creator/core/frame_composer.py +0 -176
  569. package/skills/skills/slack-gif-creator/core/gif_builder.py +0 -269
  570. package/skills/skills/slack-gif-creator/core/validators.py +0 -136
  571. package/skills/skills/slack-gif-creator/requirements.txt +0 -4
  572. package/skills/skills/theme-factory/LICENSE.txt +0 -202
  573. package/skills/skills/theme-factory/SKILL.md +0 -59
  574. package/skills/skills/theme-factory/theme-showcase.pdf +0 -0
  575. package/skills/skills/theme-factory/themes/arctic-frost.md +0 -19
  576. package/skills/skills/theme-factory/themes/botanical-garden.md +0 -19
  577. package/skills/skills/theme-factory/themes/desert-rose.md +0 -19
  578. package/skills/skills/theme-factory/themes/forest-canopy.md +0 -19
  579. package/skills/skills/theme-factory/themes/golden-hour.md +0 -19
  580. package/skills/skills/theme-factory/themes/midnight-galaxy.md +0 -19
  581. package/skills/skills/theme-factory/themes/modern-minimalist.md +0 -19
  582. package/skills/skills/theme-factory/themes/ocean-depths.md +0 -19
  583. package/skills/skills/theme-factory/themes/sunset-boulevard.md +0 -19
  584. package/skills/skills/theme-factory/themes/tech-innovation.md +0 -19
  585. package/skills/skills/web-artifacts-builder/LICENSE.txt +0 -202
  586. package/skills/skills/web-artifacts-builder/SKILL.md +0 -74
  587. package/skills/skills/web-artifacts-builder/scripts/bundle-artifact.sh +0 -54
  588. package/skills/skills/web-artifacts-builder/scripts/init-artifact.sh +0 -322
  589. package/skills/skills/webapp-testing/LICENSE.txt +0 -202
  590. package/skills/skills/webapp-testing/SKILL.md +0 -96
  591. package/skills/skills/webapp-testing/examples/console_logging.py +0 -35
  592. package/skills/skills/webapp-testing/examples/element_discovery.py +0 -40
  593. package/skills/skills/webapp-testing/examples/static_html_automation.py +0 -33
  594. package/skills/skills/webapp-testing/scripts/with_server.py +0 -106
  595. package/skills/skills/xlsx/LICENSE.txt +0 -30
  596. package/skills/skills/xlsx/SKILL.md +0 -289
  597. package/skills/skills/xlsx/recalc.py +0 -178
  598. package/skills/spec/agent-skills-spec.md +0 -3
  599. package/skills/template/SKILL.md +0 -6
  600. package/src/ai-client.ts +0 -1560
  601. package/src/remote-ai-client.ts +0 -664
  602. /package/{.eslintrc.js → .eslintrc.cjs} +0 -0
@@ -1,290 +1,987 @@
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, AuthConfig } from './types.js';
2
+ import type { Message } from './ai-client/types.js';
3
+ import { getCancellationManager } from './cancellation.js';
4
+ import { createAIClient, AIClientInterface } from './ai-client-factory.js';
5
+ import { output as logOutput } from './output-util.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': 200000,
49
+ 'glm-5': 200000,
50
+
51
+ // MiniMax
52
+ 'MiniMax-M2': 200000,
53
+ 'MiniMax-M2.1': 200000,
54
+ 'MiniMax-M2.5': 200000,
55
+
56
+ // Moonshot (Kimi)
57
+ 'moonshot-v1-8k': 8192,
58
+ 'moonshot-v1-32k': 32768,
59
+ 'moonshot-v1-128k': 131072,
60
+
61
+ // Doubao
62
+ 'doubao-seed-1-8-251228': 256000,
63
+ 'doubao-1-5-ui-tars-250428': 256000,
64
+
65
+ // Default fallback
66
+ 'default': 200000
67
+ };
68
+
69
+ // Compression ratio constants - easier to tune and maintain
70
+ const COMPRESSION_RATIO = {
71
+ RESERVE_RATIO: 0.50, // Reserve 50% of context window for system prompt and recent messages
72
+ SUMMARY_THRESHOLD_RATIO: 0.15, // Use 15% of available space for summary
73
+ MAX_TOKENS_RATIO: 0.8, // Use 80% of reserved tokens for compression
74
+ SUMMARY_MAX_TOKENS_RATIO: 0.5, // Use 50% of reserved tokens for summary
75
+ MIN_SUMMARY_TOKENS: 800 // Minimum tokens for summary
76
+ };
77
+
78
+ /**
79
+ * Get the context window for a model
80
+ */
81
+ export function getModelContextWindow(modelName?: string): number {
82
+ if (!modelName) return MODEL_CONTEXT_WINDOWS['default'];
83
+
84
+ // Try exact match first
85
+ if (MODEL_CONTEXT_WINDOWS[modelName]) {
86
+ return MODEL_CONTEXT_WINDOWS[modelName];
87
+ }
88
+
89
+ // Try case-insensitive match
90
+ const lowerName = modelName.toLowerCase();
91
+ for (const [key, value] of Object.entries(MODEL_CONTEXT_WINDOWS)) {
92
+ if (key.toLowerCase() === lowerName) {
93
+ return value;
94
+ }
95
+ }
96
+
97
+ // Try partial match (e.g., "claude" matches "claude-sonnet-4")
98
+ for (const [key, value] of Object.entries(MODEL_CONTEXT_WINDOWS)) {
99
+ if (lowerName.includes(key.toLowerCase()) || key.toLowerCase().includes(lowerName)) {
100
+ return value;
101
+ }
102
+ }
103
+
104
+ return MODEL_CONTEXT_WINDOWS['default'];
105
+ }
106
+
107
+ export interface CompressionResult {
108
+ compressedMessages: ChatMessage[];
109
+ wasCompressed: boolean;
110
+ originalMessageCount: number;
111
+ compressedMessageCount: number;
112
+ originalSize: number;
113
+ compressedSize: number;
114
+ compressionMethod: 'summary' | 'truncate' | 'none';
115
+ tokensBefore?: number;
116
+ details?: CompactionDetails;
117
+ }
118
+
119
+ export interface CompactionDetails {
120
+ readFiles: string[];
121
+ modifiedFiles: string[];
122
+ }
123
+
124
+ export interface FileOperations {
125
+ read: Set<string>;
126
+ modified: Set<string>;
127
+ }
128
+
129
+ export interface DetailedCompressionResult extends CompressionResult {
130
+ fileOperations?: FileOperations;
131
+ }
132
+
133
+ export interface CutPointResult {
134
+ firstKeptEntryIndex: number;
135
+ turnStartIndex: number;
136
+ isSplitTurn: boolean;
137
+ }
138
+
139
+ export interface CompactionPreparation {
140
+ firstKeptEntryIndex: number;
141
+ messagesToSummarize: ChatMessage[];
142
+ turnPrefixMessages: ChatMessage[];
143
+ isSplitTurn: boolean;
144
+ tokensBefore: number;
145
+ previousSummary?: string;
146
+ fileOps: FileOperations;
147
+ }
148
+
149
+ 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.
150
+
151
+ Use this EXACT format:
152
+
153
+ ## Goal
154
+ [What is the user trying to accomplish? Can be multiple items if the session covers different tasks.]
155
+
156
+ ## Constraints & Preferences
157
+ - [Any constraints, preferences, or requirements mentioned by user]
158
+ - [Or "(none)" if none were mentioned]
159
+
160
+ ## Progress
161
+ ### Done
162
+ - [x] [Completed tasks/changes]
163
+
164
+ ### In Progress
165
+ - [ ] [Current work]
166
+
167
+ ### Blocked
168
+ - [Issues preventing progress, if any]
169
+
170
+ ## Key Decisions
171
+ - **[Decision]**: [Brief rationale]
172
+
173
+ ## Next Steps
174
+ 1. [Ordered list of what should happen next]
175
+
176
+ ## Critical Context
177
+
178
+ ### Key Files & Code
179
+ - [File path]: [Brief description of key content/structure]
180
+ - [File path]: [Brief description of key content/structure]
181
+
182
+ ### Execution Results
183
+ - **Files**: [What files were read, written, or edited and key findings]
184
+ - **Commands**: [Key commands run and their outputs]
185
+ - **Search/Analysis**: [Key findings from code search or analysis]
186
+
187
+ ### Project Context
188
+ - [Architecture patterns identified]
189
+ - [Important configurations]
190
+ - [Dependencies or libraries relevant to the task]
191
+
192
+ Keep each section concise. Preserve exact file paths, function names, and error messages.`;
193
+
194
+ const UPDATE_SUMMARIZATION_PROMPT = `The messages above are NEW conversation messages to incorporate into the existing summary provided in <previous-summary> tags.
195
+
196
+ Update the existing structured summary with new information. RULES:
197
+ - PRESERVE all existing information from the previous summary
198
+ - ADD new progress, decisions, and context from the new messages
199
+ - UPDATE the Progress section: move items from "In Progress" to "Done" when completed
200
+ - UPDATE "Next Steps" based on what was accomplished
201
+ - PRESERVE exact file paths, function names, and error messages
202
+ - If something is no longer relevant, you may remove it
203
+
204
+ Use this EXACT format:
205
+
206
+ ## Goal
207
+ [Preserve existing goals, add new ones if the task expanded]
208
+
209
+ ## Constraints & Preferences
210
+ - [Preserve existing, add new ones discovered]
211
+
212
+ ## Progress
213
+ ### Done
214
+ - [x] [Include previously done items AND newly completed items]
215
+
216
+ ### In Progress
217
+ - [ ] [Current work - update based on progress]
218
+
219
+ ### Blocked
220
+ - [Current blockers - remove if resolved]
221
+
222
+ ## Key Decisions
223
+ - **[Decision]**: [Brief rationale] (preserve all previous, add new)
224
+
225
+ ## Next Steps
226
+ 1. [Update based on current state]
227
+
228
+ ## Critical Context
229
+
230
+ ### Key Files & Code
231
+ - [Preserve existing file info, add new files read]
232
+ - [Include brief descriptions of key code structures]
233
+
234
+ ### Execution Results
235
+ - **Files**: [Preserve file list, add new files read/written/edited]
236
+ - **Commands**: [Preserve outputs, add new command results]
237
+ - **Search/Analysis**: [Preserve findings, add new search results]
238
+
239
+ ### Project Context
240
+ - [Preserve architecture info, add new patterns discovered]
241
+ - [Preserve configs, add new relevant dependencies]
242
+
243
+ Keep each section concise. Preserve exact file paths, function names, and error messages.`;
244
+
245
+ const TURN_PREFIX_SUMMARIZATION_PROMPT = `This is the PREFIX of a turn that was too large to keep. The SUFFIX (recent work) is retained.
246
+
247
+ Summarize the prefix to provide context for the retained suffix:
248
+
249
+ ## Original Request
250
+ [What did the user ask for in this turn?]
251
+
252
+ ## Early Progress
253
+ - [Key decisions and work done in the prefix]
254
+
255
+ ## Critical Context
256
+
257
+ ### Key Files & Code
258
+ - [File path]: [Brief description of key content/structure]
259
+
260
+ ### Execution Results
261
+ - **Files**: [What files were read, written, or edited in this prefix]
262
+ - **Commands**: [Key commands run and outputs]
263
+
264
+ ### Context for Suffix
265
+ - [Information needed to understand the retained recent work]
266
+
267
+ Be concise. Focus on what's needed to understand the kept suffix.`;
268
+
269
+ export class ContextCompressor {
270
+ private aiClient: AIClientInterface | null = null;
271
+ private defaultConfig: CompressionConfig = {
272
+ enabled: true
273
+ };
274
+
275
+ constructor(authConfig?: AuthConfig) {
276
+ if (authConfig) {
277
+ this.aiClient = createAIClient(authConfig);
278
+ }
279
+ }
280
+
281
+ setAIClient(aiClient: AIClientInterface): void {
282
+ this.aiClient = aiClient;
283
+ }
284
+
285
+ /**
286
+ * Check if compression is needed based on token budget and model context window
287
+ * @param messages - Conversation messages
288
+ * @param config - Compression config
289
+ * @param modelName - Optional model name to determine context window
290
+ */
291
+ needsCompression(
292
+ messages: ChatMessage[],
293
+ config?: Partial<CompressionConfig>,
294
+ modelName?: string
295
+ ): { needsCompression: boolean; reason: string; tokenCount: number } {
296
+ const _cfg = { ...this.defaultConfig, ...config };
297
+ const _messageCount = messages.length;
298
+ const tokenCount = this.estimateContextTokens(messages);
299
+
300
+ // Get model context window
301
+ const contextWindow = getModelContextWindow(modelName);
302
+
303
+ // Calculate threshold using compression ratio constants
304
+ const reserveTokens = Math.floor(contextWindow * COMPRESSION_RATIO.RESERVE_RATIO);
305
+ const threshold = contextWindow - reserveTokens;
306
+
307
+ if (tokenCount > threshold) {
308
+ return {
309
+ needsCompression: true,
310
+ reason: `Token count (${tokenCount}) exceeds ${modelName ? `${modelName}` : ''} context budget (${threshold}, contextWindow: ${contextWindow})`,
311
+ tokenCount
312
+ };
313
+ }
314
+
315
+ return { needsCompression: false, reason: '', tokenCount };
316
+ }
317
+
318
+ /**
319
+ * Estimate token count for a single message using message-type-aware heuristic
320
+ */
321
+ estimateMessageTokens(message: ChatMessage): number {
322
+ let chars = 0;
323
+
324
+ switch (message.role) {
325
+ case 'user':
326
+ case 'system':
327
+ case 'tool': {
328
+ chars = message.content.length;
329
+ break;
330
+ }
331
+ case 'assistant': {
332
+ if (message.reasoning_content) {
333
+ chars += message.reasoning_content.length;
334
+ }
335
+ const toolCalls = message.tool_calls as any[] | undefined;
336
+ if (toolCalls && toolCalls.length > 0) {
337
+ for (const toolCall of toolCalls) {
338
+ chars += JSON.stringify(toolCall).length;
339
+ }
340
+ }
341
+ chars += message.content.length;
342
+ break;
343
+ }
344
+ }
345
+
346
+ return Math.ceil(chars / 4);
347
+ }
348
+
349
+ /**
350
+ * Estimate total token count for a conversation
351
+ */
352
+ estimateContextTokens(messages: ChatMessage[]): number {
353
+ let total = 0;
354
+ for (const msg of messages) {
355
+ total += this.estimateMessageTokens(msg);
356
+ }
357
+ return total;
358
+ }
359
+
360
+ /**
361
+ * Find valid cut points: indices of messages that can be cut at
362
+ * Never cut at tool results (they must follow their tool call)
363
+ */
364
+ findValidCutPoints(messages: ChatMessage[], startIndex: number, endIndex: number): number[] {
365
+ const cutPoints: number[] = [];
366
+ for (let i = startIndex; i < endIndex; i++) {
367
+ const msg = messages[i];
368
+ switch (msg.role) {
369
+ case 'user':
370
+ case 'assistant':
371
+ case 'system':
372
+ cutPoints.push(i);
373
+ break;
374
+ case 'tool':
375
+ // Tool results cannot be cut at (they must follow their tool call)
376
+ break;
377
+ }
378
+ }
379
+ return cutPoints;
380
+ }
381
+
382
+ /**
383
+ * Find the user message that starts the turn containing the given index
384
+ */
385
+ findTurnStartIndex(messages: ChatMessage[], entryIndex: number, startIndex: number): number {
386
+ for (let i = entryIndex; i >= startIndex; i--) {
387
+ const role = messages[i].role;
388
+ if (role === 'user') {
389
+ return i;
390
+ }
391
+ }
392
+ return -1;
393
+ }
394
+
395
+ /**
396
+ * Find the cut point that keeps approximately keepRecentTokens
397
+ * Walks backwards from newest, accumulating estimated message sizes
398
+ */
399
+ findCutPoint(
400
+ messages: ChatMessage[],
401
+ startIndex: number,
402
+ endIndex: number,
403
+ keepRecentTokens: number
404
+ ): CutPointResult {
405
+ const cutPoints = this.findValidCutPoints(messages, startIndex, endIndex);
406
+
407
+ if (cutPoints.length === 0) {
408
+ return { firstKeptEntryIndex: startIndex, turnStartIndex: -1, isSplitTurn: false };
409
+ }
410
+
411
+ // Walk backwards from newest, accumulating estimated message sizes
412
+ let accumulatedTokens = 0;
413
+ let cutIndex = cutPoints[cutPoints.length - 1]; // Start with the last cut point
414
+
415
+ for (let i = endIndex - 1; i >= startIndex; i--) {
416
+ const messageTokens = this.estimateMessageTokens(messages[i]);
417
+ accumulatedTokens += messageTokens;
418
+
419
+ if (accumulatedTokens >= keepRecentTokens) {
420
+ // Find the closest valid cut point at or after this entry
421
+ // Search from the END of cutPoints array (closest to i)
422
+ for (let c = cutPoints.length - 1; c >= 0; c--) {
423
+ if (cutPoints[c] >= i) {
424
+ cutIndex = cutPoints[c];
425
+ } else {
426
+ // Since cutPoints is sorted ascending, no need to continue
427
+ break;
428
+ }
429
+ }
430
+ break;
431
+ }
432
+ }
433
+
434
+ // Determine if this is a split turn
435
+ const isUserMessage = messages[cutIndex].role === 'user';
436
+ const turnStartIndex = isUserMessage ? -1 : this.findTurnStartIndex(messages, cutIndex, startIndex);
437
+
438
+ return {
439
+ firstKeptEntryIndex: cutIndex,
440
+ turnStartIndex,
441
+ isSplitTurn: !isUserMessage && turnStartIndex !== -1
442
+ };
443
+ }
444
+
445
+ /**
446
+ * Extract file operations from messages by analyzing tool calls
447
+ */
448
+ extractFileOperations(messages: ChatMessage[]): FileOperations {
449
+ const fileOps: FileOperations = {
450
+ read: new Set<string>(),
451
+ modified: new Set<string>()
452
+ };
453
+
454
+ let _totalToolCalls = 0;
455
+ let _matchedToolCalls = 0;
456
+
457
+ // Normalize tool name (handle both API format and internal format)
458
+ const isReadTool = (name: string) => name === 'read_file' || name === 'Read';
459
+ const isWriteTool = (name: string) => name === 'write_file' || name === 'Write';
460
+ const isEditTool = (name: string) => name === 'Edit';
461
+ const isDeleteTool = (name: string) => name === 'DeleteFile';
462
+
463
+ const getFilePath = (args: Record<string, unknown>): string => {
464
+ return (args.filePath as string) || (args.absolute_path as string) || (args.path as string) || '';
465
+ };
466
+
467
+ for (const msg of messages) {
468
+ // Case 1: assistant with tool_calls field
469
+ if (msg.role === 'assistant' && msg.tool_calls) {
470
+ for (const toolCall of msg.tool_calls) {
471
+ _totalToolCalls++;
472
+ const toolName = toolCall.function?.name || '';
473
+ let args: Record<string, unknown> = {};
474
+
475
+ try {
476
+ args = JSON.parse(toolCall.function?.arguments || '{}');
477
+ } catch {
478
+ continue;
479
+ }
480
+
481
+ const filePath = getFilePath(args);
482
+ if (!filePath) continue;
483
+
484
+ if (isReadTool(toolName)) {
485
+ fileOps.read.add(filePath);
486
+ _matchedToolCalls++;
487
+ } else if (isWriteTool(toolName) || isEditTool(toolName) || isDeleteTool(toolName)) {
488
+ fileOps.modified.add(filePath);
489
+ _matchedToolCalls++;
490
+ }
491
+ }
492
+ }
493
+
494
+ // Case 2: tool role with JSON content (like {"name":"Read","parameters":...})
495
+ if (msg.role === 'tool' && typeof msg.content === 'string') {
496
+ try {
497
+ const content = JSON.parse(msg.content);
498
+ _totalToolCalls++;
499
+ const toolName = content.name || '';
500
+ const args = content.parameters || {};
501
+
502
+ const filePath = getFilePath(args);
503
+ if (!filePath) continue;
504
+
505
+ if (isReadTool(toolName)) {
506
+ fileOps.read.add(filePath);
507
+ _matchedToolCalls++;
508
+ } else if (isWriteTool(toolName) || isEditTool(toolName) || isDeleteTool(toolName)) {
509
+ fileOps.modified.add(filePath);
510
+ _matchedToolCalls++;
511
+ }
512
+ } catch {
513
+ // Not JSON, skip
514
+ }
515
+ }
516
+ }
517
+
518
+ return fileOps;
519
+ }
520
+
521
+ /**
522
+ * Merge file operations from previous compaction
523
+ */
524
+ mergeFileOps(ops1: FileOperations, ops2: FileOperations): FileOperations {
525
+ return {
526
+ read: new Set([...ops1.read, ...ops2.read]),
527
+ modified: new Set([...ops1.modified, ...ops2.modified])
528
+ };
529
+ }
530
+
531
+ /**
532
+ * Format file operations for inclusion in summary
533
+ */
534
+ formatFileOperations(fileOps: FileOperations): string {
535
+ const readFiles = Array.from(fileOps.read);
536
+ const modifiedFiles = Array.from(fileOps.modified);
537
+
538
+ if (readFiles.length === 0 && modifiedFiles.length === 0) {
539
+ return '';
540
+ }
541
+
542
+ let formatted = '\n\n## File Operations\n\n';
543
+
544
+ if (readFiles.length > 0) {
545
+ formatted += '### Files read_file\n';
546
+ for (const file of readFiles) {
547
+ formatted += `- ${file}\n`;
548
+ }
549
+ }
550
+
551
+ if (modifiedFiles.length > 0) {
552
+ formatted += '\n### Files Modified\n';
553
+ for (const file of modifiedFiles) {
554
+ formatted += `- ${file}\n`;
555
+ }
556
+ }
557
+
558
+ return formatted;
559
+ }
560
+
561
+ /**
562
+ * Prepare compaction - calculate cut point and extract messages to summarize
563
+ */
564
+ prepareCompaction(
565
+ messages: ChatMessage[],
566
+ keepRecentTokens: number
567
+ ): CompactionPreparation | undefined {
568
+ if (messages.length === 0) {
569
+ return undefined;
570
+ }
571
+
572
+ // Check if last message already contains a compression summary
573
+ const lastMsg = messages[messages.length - 1];
574
+ const isAlreadyCompressed = lastMsg.role === 'user' &&
575
+ lastMsg.content.includes('[Previous conversation summarized');
576
+
577
+ if (isAlreadyCompressed) {
578
+ return undefined;
579
+ }
580
+
581
+ const startIndex = 0;
582
+ const endIndex = messages.length;
583
+
584
+ // Find cut point
585
+ const cutPoint = this.findCutPoint(messages, startIndex, endIndex, keepRecentTokens);
586
+
587
+ // Extract messages to summarize
588
+
589
+ const historyEnd = cutPoint.firstKeptEntryIndex;
590
+
591
+
592
+
593
+ const messagesToSummarize: ChatMessage[] = [];
594
+
595
+ for (let i = startIndex; i < historyEnd; i++) {
596
+
597
+ messagesToSummarize.push(messages[i]);
598
+
599
+ }
600
+
601
+
602
+
603
+ // Extract turn prefix messages if splitting (disabled for simplicity)
604
+
605
+ const turnPrefixMessages: ChatMessage[] = [];
606
+ if (cutPoint.isSplitTurn) {
607
+ for (let i = cutPoint.turnStartIndex; i < cutPoint.firstKeptEntryIndex; i++) {
608
+ turnPrefixMessages.push(messages[i]);
609
+ }
610
+ }
611
+
612
+ // Get previous summary if exists (look for embedded summary in user messages)
613
+ let previousSummary: string | undefined;
614
+ for (let i = messages.length - 1; i >= 0; i--) {
615
+ const msg = messages[i];
616
+ if (msg.role === 'user' && msg.content.includes('[Previous conversation summarized')) {
617
+ // Extract the summary part from the content
618
+ const match = msg.content.match(/\[Previous conversation summarized.*?\]\n(.+?)(?=\n\n---\n\n|\n\n\[)/s);
619
+ if (match) {
620
+ previousSummary = match[1];
621
+ } else {
622
+ previousSummary = msg.content;
623
+ }
624
+ break;
625
+ }
626
+ }
627
+
628
+ // Extract file operations
629
+ const fileOps = this.extractFileOperations(messagesToSummarize);
630
+
631
+ // Also extract from turn prefix
632
+ if (cutPoint.isSplitTurn) {
633
+ const prefixOps = this.extractFileOperations(turnPrefixMessages);
634
+ return {
635
+ firstKeptEntryIndex: cutPoint.firstKeptEntryIndex,
636
+ messagesToSummarize,
637
+ turnPrefixMessages,
638
+ isSplitTurn: cutPoint.isSplitTurn,
639
+ tokensBefore: this.estimateContextTokens(messagesToSummarize),
640
+ previousSummary,
641
+ fileOps: this.mergeFileOps(fileOps, prefixOps)
642
+ };
643
+ }
644
+
645
+ return {
646
+ firstKeptEntryIndex: cutPoint.firstKeptEntryIndex,
647
+ messagesToSummarize,
648
+ turnPrefixMessages,
649
+ isSplitTurn: cutPoint.isSplitTurn,
650
+ tokensBefore: this.estimateContextTokens(messagesToSummarize),
651
+ previousSummary,
652
+ fileOps
653
+ };
654
+ }
655
+
656
+ /**
657
+ * Generate summary using AI
658
+ */
659
+ async generateSummary(
660
+ messages: ChatMessage[],
661
+ systemPrompt: string,
662
+ reserveTokens: number,
663
+ previousSummary?: string,
664
+ customInstructions?: string
665
+ ): Promise<string> {
666
+ if (!this.aiClient) {
667
+ throw new Error('AI client not initialized for summarization');
668
+ }
669
+
670
+ // Serialize conversation
671
+ const conversationText = messages
672
+ .map((m, idx) => {
673
+ const role = m.role === 'user' ? 'User' :
674
+ m.role === 'assistant' ? 'Assistant' :
675
+ m.role === 'tool' ? 'Tool' : m.role;
676
+ return `[${idx + 1}] ${role}:\n${m.content}`;
677
+ })
678
+ .join('\n\n' + '='.repeat(50) + '\n\n');
679
+
680
+ // Select prompt based on whether we have previous summary
681
+ let basePrompt = previousSummary ? UPDATE_SUMMARIZATION_PROMPT : SUMMARIZATION_PROMPT;
682
+ if (customInstructions) {
683
+ basePrompt = `${basePrompt}\n\nAdditional focus: ${customInstructions}`;
684
+ }
685
+
686
+ // Build prompt
687
+ let promptText = `<conversation>\n${conversationText}\n</conversation>\n\n`;
688
+ if (previousSummary) {
689
+ promptText += `<previous-summary>\n${previousSummary}\n</previous-summary>\n\n`;
690
+ }
691
+ promptText += basePrompt;
692
+
693
+ const maxTokens = Math.floor(reserveTokens * COMPRESSION_RATIO.MAX_TOKENS_RATIO);
694
+
695
+ const summaryMessage: Message = {
696
+ role: 'user',
697
+ content: promptText
698
+ };
699
+
700
+ const aiPromise = this.aiClient.compress([summaryMessage], {
701
+ maxTokens,
702
+ temperature: 0.3
703
+ });
704
+
705
+ try {
706
+ const response = await getCancellationManager().withCancellation(
707
+ aiPromise,
708
+ 'context-compression-summary'
709
+ );
710
+
711
+ const summary = response.choices[0]?.message?.content || '';
712
+ return typeof summary === 'string' ? summary : JSON.stringify(summary);
713
+ } catch (error: any) {
714
+ if (error.message === 'Operation cancelled by user') {
715
+ throw error;
716
+ }
717
+ console.error('Failed to generate summary:', error);
718
+ await logOutput('error', 'Failed to generate summary', { error: error.message });
719
+ const userCount = messages.filter(m => m.role === 'user').length;
720
+ const toolCount = messages.filter(m => m.role === 'tool').length;
721
+ return `[Summary of ${messages.length} messages: ${userCount} user exchanges, ${toolCount} tool calls. Key topics discussed but details unavailable due to summarization error.]`;
722
+ }
723
+ }
724
+
725
+ /**
726
+ * Generate turn prefix summary when splitting a turn
727
+ */
728
+ async generateTurnPrefixSummary(
729
+ messages: ChatMessage[],
730
+ reserveTokens: number
731
+ ): Promise<string> {
732
+ if (!this.aiClient) {
733
+ throw new Error('AI client not initialized for summarization');
734
+ }
735
+
736
+ const conversationText = messages
737
+ .map((m, idx) => {
738
+ const role = m.role === 'user' ? 'User' :
739
+ m.role === 'assistant' ? 'Assistant' :
740
+ m.role === 'tool' ? 'Tool' : m.role;
741
+ return `[${idx + 1}] ${role}:\n${m.content}`;
742
+ })
743
+ .join('\n\n' + '='.repeat(50) + '\n\n');
744
+
745
+ const promptText = `<conversation>\n${conversationText}\n</conversation>\n\n${TURN_PREFIX_SUMMARIZATION_PROMPT}`;
746
+ const maxTokens = Math.floor(reserveTokens * COMPRESSION_RATIO.SUMMARY_MAX_TOKENS_RATIO);
747
+
748
+ const summaryMessage: Message = {
749
+ role: 'user',
750
+ content: promptText
751
+ };
752
+
753
+ const aiPromise = this.aiClient.compress([summaryMessage], {
754
+ maxTokens,
755
+ temperature: 0.3
756
+ });
757
+
758
+ try {
759
+ const response = await getCancellationManager().withCancellation(
760
+ aiPromise,
761
+ 'context-compression-turn-prefix'
762
+ );
763
+
764
+ const content = response.choices[0]?.message?.content || '';
765
+ return typeof content === 'string' ? content : JSON.stringify(content);
766
+ } catch (error: any) {
767
+ if (error.message === 'Operation cancelled by user') {
768
+ throw error;
769
+ }
770
+ console.error('Failed to generate turn prefix summary:', error);
771
+ await logOutput('error', 'Failed to generate turn prefix summary', { error: error.message });
772
+ return '[Turn prefix summary unavailable]';
773
+ }
774
+ }
775
+
776
+ /**
777
+ * Main compression function with incremental compaction support
778
+ */
779
+ async compressContext(
780
+ messages: ChatMessage[],
781
+ systemPrompt: string,
782
+ config?: Partial<CompressionConfig>,
783
+ previousSummary?: string,
784
+ modelName?: string
785
+ ): Promise<CompressionResult> {
786
+ const _cfg = { ...this.defaultConfig, ...config };
787
+ const originalMessageCount = messages.length;
788
+ const originalSize = messages.reduce((total, msg) => total + msg.content.length, 0);
789
+ const _originalTokens = this.estimateContextTokens(messages);
790
+ const contextWindow = getModelContextWindow(modelName);
791
+
792
+ // Check if compression is needed
793
+ const { needsCompression } = this.needsCompression(messages, config, modelName);
794
+ if (!needsCompression) {
795
+ return {
796
+ compressedMessages: messages,
797
+ wasCompressed: false,
798
+ originalMessageCount,
799
+ compressedMessageCount: messages.length,
800
+ originalSize,
801
+ compressedSize: originalSize,
802
+ compressionMethod: 'none'
803
+ };
804
+ }
805
+
806
+ // Prepare compaction
807
+ // Reserve 50% of context window for new conversation, 15% for summary
808
+ const reserveTokens = Math.floor(contextWindow * COMPRESSION_RATIO.RESERVE_RATIO);
809
+ const summaryReserveTokens = Math.max(COMPRESSION_RATIO.MIN_SUMMARY_TOKENS, Math.floor((contextWindow - reserveTokens) * COMPRESSION_RATIO.SUMMARY_THRESHOLD_RATIO));
810
+ const keepRecentTokens = contextWindow - reserveTokens - summaryReserveTokens;
811
+ const preparation = this.prepareCompaction(messages, Math.max(0, keepRecentTokens));
812
+
813
+ if (!preparation) {
814
+ // Already compressed or no valid cut point
815
+ return {
816
+ compressedMessages: messages,
817
+ wasCompressed: false,
818
+ originalMessageCount,
819
+ compressedMessageCount: messages.length,
820
+ originalSize,
821
+ compressedSize: originalSize,
822
+ compressionMethod: 'none'
823
+ };
824
+ }
825
+
826
+ const {
827
+ firstKeptEntryIndex,
828
+ messagesToSummarize,
829
+ turnPrefixMessages,
830
+ isSplitTurn,
831
+ tokensBefore,
832
+ fileOps
833
+ } = preparation;
834
+
835
+ // Generate summary
836
+ let summary: string;
837
+
838
+ if (isSplitTurn && turnPrefixMessages.length > 0) {
839
+ // Generate both summaries in parallel
840
+ const [historyResult, turnPrefixResult] = await Promise.all([
841
+ messagesToSummarize.length > 0
842
+ ? this.generateSummary(messagesToSummarize, systemPrompt, summaryReserveTokens, previousSummary)
843
+ : Promise.resolve('No prior history.'),
844
+ this.generateTurnPrefixSummary(turnPrefixMessages, summaryReserveTokens)
845
+ ]);
846
+ summary = `${historyResult}\n\n---\n\n**Turn Context (split turn):**\n\n${turnPrefixResult}`;
847
+ } else {
848
+ summary = await this.generateSummary(
849
+ messagesToSummarize,
850
+ systemPrompt,
851
+ summaryReserveTokens,
852
+ previousSummary
853
+ );
854
+ }
855
+
856
+ // Add file operations to summary
857
+ summary += this.formatFileOperations(fileOps);
858
+
859
+ // Build compressed messages: summary + kept messages
860
+ // Summary is inserted as prefix in first user message to maintain valid message order
861
+ const compressedMessages: ChatMessage[] = [];
862
+
863
+ if (messagesToSummarize.length > 0) {
864
+ // Find first user message in kept messages
865
+ let firstKeptUserMsg: ChatMessage | null = null;
866
+ let firstKeptUserIndex = -1;
867
+ for (let i = firstKeptEntryIndex; i < messages.length; i++) {
868
+ if (messages[i].role === 'user') {
869
+ firstKeptUserMsg = messages[i];
870
+ firstKeptUserIndex = i;
871
+ break;
872
+ }
873
+ }
874
+
875
+ if (firstKeptUserMsg) {
876
+ // Prepend summary to first user message content
877
+ const summaryPrefix = `[Previous conversation summarized (${messagesToSummarize.length} messages):]\n${summary}\n\n---\n\n`;
878
+ compressedMessages.push({
879
+ role: 'user',
880
+ content: summaryPrefix + firstKeptUserMsg.content,
881
+ timestamp: firstKeptUserMsg.timestamp,
882
+ images: firstKeptUserMsg.images
883
+ });
884
+
885
+ // Add remaining kept messages (skip the modified first user message)
886
+ for (let i = firstKeptUserIndex + 1; i < messages.length; i++) {
887
+ const msg = messages[i];
888
+ compressedMessages.push({
889
+ role: msg.role,
890
+ content: msg.content,
891
+ timestamp: msg.timestamp,
892
+ images: msg.images,
893
+ reasoning_content: msg.reasoning_content,
894
+ tool_calls: msg.tool_calls,
895
+ tool_call_id: msg.tool_call_id
896
+ });
897
+ }
898
+ } else {
899
+ // No user message in kept messages (rare case)
900
+ // Insert summary as a user message, then add all kept messages
901
+ // This ensures valid message order: user �?assistant �?tool �?tool...
902
+ compressedMessages.push({
903
+ role: 'user',
904
+ content: `[Conversation Summary - ${messagesToSummarize.length} messages compressed]\n\n${summary}`,
905
+ timestamp: Date.now()
906
+ });
907
+
908
+ // Add all kept messages (they may start with assistant or tool)
909
+ for (let i = firstKeptEntryIndex; i < messages.length; i++) {
910
+ const msg = messages[i];
911
+ compressedMessages.push({
912
+ role: msg.role,
913
+ content: msg.content,
914
+ timestamp: msg.timestamp,
915
+ images: msg.images,
916
+ reasoning_content: msg.reasoning_content,
917
+ tool_calls: msg.tool_calls,
918
+ tool_call_id: msg.tool_call_id
919
+ });
920
+ }
921
+ }
922
+ } else {
923
+ // No messages to summarize, just keep all messages
924
+ for (let i = firstKeptEntryIndex; i < messages.length; i++) {
925
+ const msg = messages[i];
926
+ compressedMessages.push({
927
+ role: msg.role,
928
+ content: msg.content,
929
+ timestamp: msg.timestamp,
930
+ images: msg.images,
931
+ reasoning_content: msg.reasoning_content,
932
+ tool_calls: msg.tool_calls,
933
+ tool_call_id: msg.tool_call_id
934
+ });
935
+ }
936
+ }
937
+
938
+ const compressedSize = compressedMessages.reduce((total, msg) => total + msg.content.length, 0);
939
+ const _compressedTokens = this.estimateContextTokens(compressedMessages);
940
+ const _reductionPercent = Math.round((1 - compressedSize / originalSize) * 100);
941
+
942
+ return {
943
+ compressedMessages,
944
+ wasCompressed: true,
945
+ originalMessageCount,
946
+ compressedMessageCount: compressedMessages.length,
947
+ originalSize,
948
+ compressedSize,
949
+ compressionMethod: 'summary',
950
+ tokensBefore,
951
+ details: {
952
+ readFiles: Array.from(fileOps.read),
953
+ modifiedFiles: Array.from(fileOps.modified)
954
+ }
955
+ };
956
+ }
957
+
958
+ /**
959
+ * Create compressed message copy for saving
960
+ */
961
+ createCompressedSnapshot(
962
+ messages: ChatMessage[],
963
+ compressionResult: CompressionResult
964
+ ): object {
965
+ return {
966
+ timestamp: Date.now(),
967
+ originalMessageCount: compressionResult.originalMessageCount,
968
+ compressedMessageCount: compressionResult.compressedMessageCount,
969
+ originalSize: compressionResult.originalSize,
970
+ compressedSize: compressionResult.compressedSize,
971
+ compressionMethod: compressionResult.compressionMethod,
972
+ tokensBefore: compressionResult.tokensBefore,
973
+ details: compressionResult.details,
974
+ messages: compressionResult.compressedMessages
975
+ };
976
+ }
977
+ }
978
+
979
+ let compressorInstance: ContextCompressor | null = null;
980
+
981
+ export function getContextCompressor(authConfig?: AuthConfig): ContextCompressor {
982
+ if (!compressorInstance) {
983
+ compressorInstance = new ContextCompressor(authConfig);
984
+ }
985
+ return compressorInstance;
986
+ }
987
+