apex-code 0.0.1-alpha.7 → 0.0.1-alpha.9

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 (393) hide show
  1. package/CHANGELOG.md +229 -0
  2. package/README.md +5 -0
  3. package/dist/cli/args.d.ts +4 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +44 -9
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli/concurrent-session.d.ts +3 -0
  8. package/dist/cli/concurrent-session.d.ts.map +1 -0
  9. package/dist/cli/concurrent-session.js +13 -0
  10. package/dist/cli/concurrent-session.js.map +1 -0
  11. package/dist/cli/file-processor.d.ts.map +1 -1
  12. package/dist/cli/file-processor.js +2 -1
  13. package/dist/cli/file-processor.js.map +1 -1
  14. package/dist/cli.d.ts.map +1 -1
  15. package/dist/cli.js +1 -2
  16. package/dist/cli.js.map +1 -1
  17. package/dist/config.d.ts +3 -2
  18. package/dist/config.d.ts.map +1 -1
  19. package/dist/config.js +21 -13
  20. package/dist/config.js.map +1 -1
  21. package/dist/core/agent-session-services.d.ts.map +1 -1
  22. package/dist/core/agent-session-services.js +20 -0
  23. package/dist/core/agent-session-services.js.map +1 -1
  24. package/dist/core/agent-session.d.ts +41 -9
  25. package/dist/core/agent-session.d.ts.map +1 -1
  26. package/dist/core/agent-session.js +172 -101
  27. package/dist/core/agent-session.js.map +1 -1
  28. package/dist/core/auth-storage.d.ts +4 -1
  29. package/dist/core/auth-storage.d.ts.map +1 -1
  30. package/dist/core/auth-storage.js +12 -9
  31. package/dist/core/auth-storage.js.map +1 -1
  32. package/dist/core/bash-executor.d.ts.map +1 -1
  33. package/dist/core/bash-executor.js +10 -9
  34. package/dist/core/bash-executor.js.map +1 -1
  35. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  36. package/dist/core/compaction/branch-summarization.js +3 -0
  37. package/dist/core/compaction/branch-summarization.js.map +1 -1
  38. package/dist/core/compaction/compaction.d.ts +4 -3
  39. package/dist/core/compaction/compaction.d.ts.map +1 -1
  40. package/dist/core/compaction/compaction.js +41 -32
  41. package/dist/core/compaction/compaction.js.map +1 -1
  42. package/dist/core/defaults.d.ts +1 -0
  43. package/dist/core/defaults.d.ts.map +1 -1
  44. package/dist/core/defaults.js +9 -0
  45. package/dist/core/defaults.js.map +1 -1
  46. package/dist/core/durable-state/sqlite.d.ts.map +1 -1
  47. package/dist/core/durable-state/sqlite.js +26 -2
  48. package/dist/core/durable-state/sqlite.js.map +1 -1
  49. package/dist/core/experimental.d.ts +4 -0
  50. package/dist/core/experimental.d.ts.map +1 -1
  51. package/dist/core/experimental.js +4 -0
  52. package/dist/core/experimental.js.map +1 -1
  53. package/dist/core/extensions/index.d.ts +2 -2
  54. package/dist/core/extensions/index.d.ts.map +1 -1
  55. package/dist/core/extensions/index.js +1 -1
  56. package/dist/core/extensions/index.js.map +1 -1
  57. package/dist/core/extensions/loader.d.ts.map +1 -1
  58. package/dist/core/extensions/loader.js +115 -48
  59. package/dist/core/extensions/loader.js.map +1 -1
  60. package/dist/core/extensions/types.d.ts +42 -9
  61. package/dist/core/extensions/types.d.ts.map +1 -1
  62. package/dist/core/extensions/types.js +3 -0
  63. package/dist/core/extensions/types.js.map +1 -1
  64. package/dist/core/footer-data-provider.d.ts +1 -1
  65. package/dist/core/footer-data-provider.d.ts.map +1 -1
  66. package/dist/core/footer-data-provider.js +1 -1
  67. package/dist/core/footer-data-provider.js.map +1 -1
  68. package/dist/core/http-dispatcher.d.ts +1 -2
  69. package/dist/core/http-dispatcher.d.ts.map +1 -1
  70. package/dist/core/http-dispatcher.js +2 -17
  71. package/dist/core/http-dispatcher.js.map +1 -1
  72. package/dist/core/http-idle-timeout.d.ts +12 -0
  73. package/dist/core/http-idle-timeout.d.ts.map +1 -0
  74. package/dist/core/http-idle-timeout.js +27 -0
  75. package/dist/core/http-idle-timeout.js.map +1 -0
  76. package/dist/core/keybindings.d.ts +38 -13
  77. package/dist/core/keybindings.d.ts.map +1 -1
  78. package/dist/core/keybindings.js +26 -5
  79. package/dist/core/keybindings.js.map +1 -1
  80. package/dist/core/lsp/client.d.ts +2 -0
  81. package/dist/core/lsp/client.d.ts.map +1 -1
  82. package/dist/core/lsp/client.js +27 -2
  83. package/dist/core/lsp/client.js.map +1 -1
  84. package/dist/core/model-config.d.ts +10 -0
  85. package/dist/core/model-config.d.ts.map +1 -1
  86. package/dist/core/model-config.js +4 -1
  87. package/dist/core/model-config.js.map +1 -1
  88. package/dist/core/model-resolver.d.ts +1 -0
  89. package/dist/core/model-resolver.d.ts.map +1 -1
  90. package/dist/core/model-resolver.js +14 -8
  91. package/dist/core/model-resolver.js.map +1 -1
  92. package/dist/core/models-store.d.ts.map +1 -1
  93. package/dist/core/models-store.js +2 -1
  94. package/dist/core/models-store.js.map +1 -1
  95. package/dist/core/package-manager.d.ts.map +1 -1
  96. package/dist/core/package-manager.js +22 -14
  97. package/dist/core/package-manager.js.map +1 -1
  98. package/dist/core/pi-manifest.d.ts.map +1 -1
  99. package/dist/core/pi-manifest.js +2 -1
  100. package/dist/core/pi-manifest.js.map +1 -1
  101. package/dist/core/remote-catalog-provider.d.ts.map +1 -1
  102. package/dist/core/remote-catalog-provider.js +2 -1
  103. package/dist/core/remote-catalog-provider.js.map +1 -1
  104. package/dist/core/resource-loader.d.ts.map +1 -1
  105. package/dist/core/resource-loader.js +3 -2
  106. package/dist/core/resource-loader.js.map +1 -1
  107. package/dist/core/sandbox/child-entry.d.ts.map +1 -1
  108. package/dist/core/sandbox/child-entry.js +8 -1
  109. package/dist/core/sandbox/child-entry.js.map +1 -1
  110. package/dist/core/sandbox/cli-launch.d.ts +3 -0
  111. package/dist/core/sandbox/cli-launch.d.ts.map +1 -1
  112. package/dist/core/sandbox/cli-launch.js +25 -4
  113. package/dist/core/sandbox/cli-launch.js.map +1 -1
  114. package/dist/core/sandbox/cli-supervisor.d.ts.map +1 -1
  115. package/dist/core/sandbox/cli-supervisor.js +53 -13
  116. package/dist/core/sandbox/cli-supervisor.js.map +1 -1
  117. package/dist/core/sandbox/linux-backend.d.ts.map +1 -1
  118. package/dist/core/sandbox/linux-backend.js +15 -0
  119. package/dist/core/sandbox/linux-backend.js.map +1 -1
  120. package/dist/core/sandbox/macos-backend.d.ts.map +1 -1
  121. package/dist/core/sandbox/macos-backend.js +24 -1
  122. package/dist/core/sandbox/macos-backend.js.map +1 -1
  123. package/dist/core/sandbox/rpc/credential-client.d.ts +20 -0
  124. package/dist/core/sandbox/rpc/credential-client.d.ts.map +1 -0
  125. package/dist/core/sandbox/rpc/credential-client.js +170 -0
  126. package/dist/core/sandbox/rpc/credential-client.js.map +1 -0
  127. package/dist/core/sandbox/rpc/credential-proxy.d.ts +44 -0
  128. package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +1 -0
  129. package/dist/core/sandbox/rpc/credential-proxy.js +381 -0
  130. package/dist/core/sandbox/rpc/credential-proxy.js.map +1 -0
  131. package/dist/core/sandbox/supervisor.d.ts +11 -0
  132. package/dist/core/sandbox/supervisor.d.ts.map +1 -1
  133. package/dist/core/sandbox/supervisor.js.map +1 -1
  134. package/dist/core/sandbox/terminal-size.d.ts +40 -0
  135. package/dist/core/sandbox/terminal-size.d.ts.map +1 -0
  136. package/dist/core/sandbox/terminal-size.js +105 -0
  137. package/dist/core/sandbox/terminal-size.js.map +1 -0
  138. package/dist/core/sdk.d.ts +8 -5
  139. package/dist/core/sdk.d.ts.map +1 -1
  140. package/dist/core/sdk.js +26 -7
  141. package/dist/core/sdk.js.map +1 -1
  142. package/dist/core/session-export.d.ts +4 -0
  143. package/dist/core/session-export.d.ts.map +1 -0
  144. package/dist/core/session-export.js +32 -0
  145. package/dist/core/session-export.js.map +1 -0
  146. package/dist/core/session-lease.d.ts +31 -0
  147. package/dist/core/session-lease.d.ts.map +1 -0
  148. package/dist/core/session-lease.js +138 -0
  149. package/dist/core/session-lease.js.map +1 -0
  150. package/dist/core/session-manager.d.ts +5 -0
  151. package/dist/core/session-manager.d.ts.map +1 -1
  152. package/dist/core/session-manager.js +5 -4
  153. package/dist/core/session-manager.js.map +1 -1
  154. package/dist/core/settings-diagnostics.d.ts +9 -0
  155. package/dist/core/settings-diagnostics.d.ts.map +1 -0
  156. package/dist/core/settings-diagnostics.js +21 -0
  157. package/dist/core/settings-diagnostics.js.map +1 -0
  158. package/dist/core/settings-manager.d.ts +35 -0
  159. package/dist/core/settings-manager.d.ts.map +1 -1
  160. package/dist/core/settings-manager.js +93 -12
  161. package/dist/core/settings-manager.js.map +1 -1
  162. package/dist/core/skills.d.ts.map +1 -1
  163. package/dist/core/skills.js +51 -33
  164. package/dist/core/skills.js.map +1 -1
  165. package/dist/core/slash-commands.d.ts.map +1 -1
  166. package/dist/core/slash-commands.js +4 -1
  167. package/dist/core/slash-commands.js.map +1 -1
  168. package/dist/core/system-prompt.d.ts +2 -2
  169. package/dist/core/system-prompt.d.ts.map +1 -1
  170. package/dist/core/system-prompt.js +53 -62
  171. package/dist/core/system-prompt.js.map +1 -1
  172. package/dist/core/tools/bash.d.ts +14 -1
  173. package/dist/core/tools/bash.d.ts.map +1 -1
  174. package/dist/core/tools/bash.js +38 -20
  175. package/dist/core/tools/bash.js.map +1 -1
  176. package/dist/core/tools/delegate.d.ts.map +1 -1
  177. package/dist/core/tools/delegate.js +24 -0
  178. package/dist/core/tools/delegate.js.map +1 -1
  179. package/dist/core/tools/edit-diff.d.ts +0 -5
  180. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  181. package/dist/core/tools/edit-diff.js +2 -5
  182. package/dist/core/tools/edit-diff.js.map +1 -1
  183. package/dist/core/tools/edit.d.ts.map +1 -1
  184. package/dist/core/tools/edit.js +22 -4
  185. package/dist/core/tools/edit.js.map +1 -1
  186. package/dist/core/tools/find.d.ts.map +1 -1
  187. package/dist/core/tools/find.js +1 -1
  188. package/dist/core/tools/find.js.map +1 -1
  189. package/dist/core/tools/grep.d.ts +2 -0
  190. package/dist/core/tools/grep.d.ts.map +1 -1
  191. package/dist/core/tools/grep.js +35 -10
  192. package/dist/core/tools/grep.js.map +1 -1
  193. package/dist/core/tools/index.d.ts +4 -1
  194. package/dist/core/tools/index.d.ts.map +1 -1
  195. package/dist/core/tools/index.js +9 -0
  196. package/dist/core/tools/index.js.map +1 -1
  197. package/dist/core/tools/ls.d.ts +2 -0
  198. package/dist/core/tools/ls.d.ts.map +1 -1
  199. package/dist/core/tools/ls.js +5 -1
  200. package/dist/core/tools/ls.js.map +1 -1
  201. package/dist/core/tools/powershell.d.ts +16 -0
  202. package/dist/core/tools/powershell.d.ts.map +1 -0
  203. package/dist/core/tools/powershell.js +39 -0
  204. package/dist/core/tools/powershell.js.map +1 -0
  205. package/dist/core/tools/read.d.ts.map +1 -1
  206. package/dist/core/tools/read.js +2 -0
  207. package/dist/core/tools/read.js.map +1 -1
  208. package/dist/core/tools/web-search-exa.d.ts +33 -0
  209. package/dist/core/tools/web-search-exa.d.ts.map +1 -0
  210. package/dist/core/tools/web-search-exa.js +128 -0
  211. package/dist/core/tools/web-search-exa.js.map +1 -0
  212. package/dist/core/tools/write.d.ts.map +1 -1
  213. package/dist/core/tools/write.js +2 -0
  214. package/dist/core/tools/write.js.map +1 -1
  215. package/dist/core/trust-manager.d.ts.map +1 -1
  216. package/dist/core/trust-manager.js +2 -1
  217. package/dist/core/trust-manager.js.map +1 -1
  218. package/dist/core/web-search-provider.d.ts +64 -0
  219. package/dist/core/web-search-provider.d.ts.map +1 -0
  220. package/dist/core/web-search-provider.js +149 -0
  221. package/dist/core/web-search-provider.js.map +1 -0
  222. package/dist/extensions/llama/index.d.ts.map +1 -1
  223. package/dist/extensions/llama/index.js +2 -0
  224. package/dist/extensions/llama/index.js.map +1 -1
  225. package/dist/extensions/llama/provider.d.ts.map +1 -1
  226. package/dist/extensions/llama/provider.js +6 -2
  227. package/dist/extensions/llama/provider.js.map +1 -1
  228. package/dist/index.d.ts +6 -6
  229. package/dist/index.d.ts.map +1 -1
  230. package/dist/index.js +4 -4
  231. package/dist/index.js.map +1 -1
  232. package/dist/main.d.ts +5 -0
  233. package/dist/main.d.ts.map +1 -1
  234. package/dist/main.js +38 -16
  235. package/dist/main.js.map +1 -1
  236. package/dist/migrations.d.ts.map +1 -1
  237. package/dist/migrations.js +4 -3
  238. package/dist/migrations.js.map +1 -1
  239. package/dist/modes/interactive/components/custom-editor.d.ts +23 -0
  240. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  241. package/dist/modes/interactive/components/custom-editor.js +105 -2
  242. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  243. package/dist/modes/interactive/components/dynamic-border.d.ts +7 -1
  244. package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -1
  245. package/dist/modes/interactive/components/dynamic-border.js +11 -2
  246. package/dist/modes/interactive/components/dynamic-border.js.map +1 -1
  247. package/dist/modes/interactive/components/extension-selector.d.ts +4 -0
  248. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  249. package/dist/modes/interactive/components/extension-selector.js +24 -6
  250. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  251. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  252. package/dist/modes/interactive/components/first-time-setup.js +6 -3
  253. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  254. package/dist/modes/interactive/components/first-use-hints.d.ts +8 -0
  255. package/dist/modes/interactive/components/first-use-hints.d.ts.map +1 -0
  256. package/dist/modes/interactive/components/first-use-hints.js +22 -0
  257. package/dist/modes/interactive/components/first-use-hints.js.map +1 -0
  258. package/dist/modes/interactive/components/footer.d.ts +12 -0
  259. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  260. package/dist/modes/interactive/components/footer.js +128 -9
  261. package/dist/modes/interactive/components/footer.js.map +1 -1
  262. package/dist/modes/interactive/components/index.d.ts +1 -0
  263. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  264. package/dist/modes/interactive/components/index.js +1 -0
  265. package/dist/modes/interactive/components/index.js.map +1 -1
  266. package/dist/modes/interactive/components/model-selector.d.ts +12 -3
  267. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  268. package/dist/modes/interactive/components/model-selector.js +71 -14
  269. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  270. package/dist/modes/interactive/components/settings-selector.d.ts +10 -3
  271. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  272. package/dist/modes/interactive/components/settings-selector.js +128 -56
  273. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  274. package/dist/modes/interactive/components/settings-submenu.d.ts +72 -0
  275. package/dist/modes/interactive/components/settings-submenu.d.ts.map +1 -0
  276. package/dist/modes/interactive/components/settings-submenu.js +165 -0
  277. package/dist/modes/interactive/components/settings-submenu.js.map +1 -0
  278. package/dist/modes/interactive/components/splash-header.d.ts +29 -4
  279. package/dist/modes/interactive/components/splash-header.d.ts.map +1 -1
  280. package/dist/modes/interactive/components/splash-header.js +58 -23
  281. package/dist/modes/interactive/components/splash-header.js.map +1 -1
  282. package/dist/modes/interactive/components/thinking-selector.d.ts +15 -3
  283. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  284. package/dist/modes/interactive/components/thinking-selector.js +74 -17
  285. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  286. package/dist/modes/interactive/components/tool-execution.d.ts +14 -0
  287. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  288. package/dist/modes/interactive/components/tool-execution.js +113 -35
  289. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  290. package/dist/modes/interactive/components/tool-panel.d.ts +30 -0
  291. package/dist/modes/interactive/components/tool-panel.d.ts.map +1 -0
  292. package/dist/modes/interactive/components/tool-panel.js +130 -0
  293. package/dist/modes/interactive/components/tool-panel.js.map +1 -0
  294. package/dist/modes/interactive/external-editor.d.ts.map +1 -1
  295. package/dist/modes/interactive/external-editor.js +2 -1
  296. package/dist/modes/interactive/external-editor.js.map +1 -1
  297. package/dist/modes/interactive/interactive-mode.d.ts +55 -9
  298. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  299. package/dist/modes/interactive/interactive-mode.js +446 -87
  300. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  301. package/dist/modes/interactive/model-catalog-refresh.d.ts +7 -0
  302. package/dist/modes/interactive/model-catalog-refresh.d.ts.map +1 -0
  303. package/dist/modes/interactive/model-catalog-refresh.js +34 -0
  304. package/dist/modes/interactive/model-catalog-refresh.js.map +1 -0
  305. package/dist/modes/interactive/theme/apex.json +62 -63
  306. package/dist/modes/interactive/theme/dark.json +2 -0
  307. package/dist/modes/interactive/theme/light.json +2 -0
  308. package/dist/modes/interactive/theme/theme-controller.d.ts +10 -2
  309. package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
  310. package/dist/modes/interactive/theme/theme-controller.js +25 -11
  311. package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
  312. package/dist/modes/interactive/theme/theme-schema.json +9 -1
  313. package/dist/modes/interactive/theme/theme.d.ts +14 -3
  314. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  315. package/dist/modes/interactive/theme/theme.js +60 -10
  316. package/dist/modes/interactive/theme/theme.js.map +1 -1
  317. package/dist/modes/json-event.d.ts +18 -12
  318. package/dist/modes/json-event.d.ts.map +1 -1
  319. package/dist/modes/json-event.js +22 -5
  320. package/dist/modes/json-event.js.map +1 -1
  321. package/dist/package-manager-cli.d.ts +1 -0
  322. package/dist/package-manager-cli.d.ts.map +1 -1
  323. package/dist/package-manager-cli.js +197 -5
  324. package/dist/package-manager-cli.js.map +1 -1
  325. package/dist/server/create-harness.d.ts.map +1 -1
  326. package/dist/server/create-harness.js +2 -0
  327. package/dist/server/create-harness.js.map +1 -1
  328. package/dist/themes/apex-logo.d.ts +36 -16
  329. package/dist/themes/apex-logo.d.ts.map +1 -1
  330. package/dist/themes/apex-logo.js +32 -28
  331. package/dist/themes/apex-logo.js.map +1 -1
  332. package/dist/utils/frontmatter.d.ts.map +1 -1
  333. package/dist/utils/frontmatter.js +2 -1
  334. package/dist/utils/frontmatter.js.map +1 -1
  335. package/dist/utils/management-http.d.ts +5 -3
  336. package/dist/utils/management-http.d.ts.map +1 -1
  337. package/dist/utils/management-http.js +14 -10
  338. package/dist/utils/management-http.js.map +1 -1
  339. package/dist/utils/shell.d.ts +3 -0
  340. package/dist/utils/shell.d.ts.map +1 -1
  341. package/dist/utils/shell.js +17 -5
  342. package/dist/utils/shell.js.map +1 -1
  343. package/dist/utils/syntax-highlight.d.ts +1 -0
  344. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  345. package/dist/utils/syntax-highlight.js +60 -1
  346. package/dist/utils/syntax-highlight.js.map +1 -1
  347. package/dist/utils/text.d.ts +8 -0
  348. package/dist/utils/text.d.ts.map +1 -0
  349. package/dist/utils/text.js +9 -0
  350. package/dist/utils/text.js.map +1 -0
  351. package/dist/utils/tools-manager.d.ts +10 -1
  352. package/dist/utils/tools-manager.d.ts.map +1 -1
  353. package/dist/utils/tools-manager.js +15 -20
  354. package/dist/utils/tools-manager.js.map +1 -1
  355. package/docs/compaction.md +18 -3
  356. package/docs/custom-provider.md +6 -3
  357. package/docs/environment-variables.md +1 -0
  358. package/docs/extensions.md +21 -7
  359. package/docs/json.md +12 -5
  360. package/docs/keybindings.md +15 -9
  361. package/docs/llama-cpp.md +2 -0
  362. package/docs/models.md +8 -2
  363. package/docs/packages.md +2 -2
  364. package/docs/rpc.md +24 -7
  365. package/docs/sdk.md +16 -2
  366. package/docs/settings.md +73 -2
  367. package/docs/skills.md +4 -3
  368. package/docs/terminal-setup.md +34 -9
  369. package/docs/themes.md +24 -3
  370. package/docs/tui.md +2 -2
  371. package/docs/usage.md +9 -4
  372. package/docs/windows.md +24 -2
  373. package/examples/extensions/border-status-editor.ts +1 -1
  374. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  375. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  376. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  377. package/examples/extensions/git-checkpoint.ts +2 -2
  378. package/examples/extensions/gondolin/package-lock.json +2 -2
  379. package/examples/extensions/gondolin/package.json +1 -1
  380. package/examples/extensions/notify.ts +3 -1
  381. package/examples/extensions/sandbox/package-lock.json +2 -2
  382. package/examples/extensions/sandbox/package.json +1 -1
  383. package/examples/extensions/subagent/README.md +3 -1
  384. package/examples/extensions/subagent/agents.ts +40 -9
  385. package/examples/extensions/subagent/index.ts +27 -4
  386. package/examples/extensions/titlebar-spinner.ts +1 -1
  387. package/examples/extensions/with-deps/package-lock.json +2 -2
  388. package/examples/extensions/with-deps/package.json +1 -1
  389. package/examples/rpc-extension-ui.ts +1 -1
  390. package/examples/sdk/06-extensions.ts +1 -1
  391. package/examples/sdk/README.md +1 -1
  392. package/npm-shrinkwrap.json +280 -445
  393. package/package.json +9 -11
@@ -12,20 +12,19 @@
12
12
  *
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
15
+ import { readFileSync } from "node:fs";
16
16
  import { basename, dirname } from "node:path";
17
17
  import { contentText } from "@earendil-works/pi-ai";
18
18
  import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, } from "@earendil-works/pi-ai/compat";
19
19
  import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
20
20
  import { stripFrontmatter } from "../utils/frontmatter.js";
21
- import { resolvePath } from "../utils/paths.js";
22
21
  import { sleep } from "../utils/sleep.js";
23
22
  import { normalizeToolResultImages } from "../utils/tool-result-images.js";
24
23
  import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
25
24
  import { executeBashWithOperations } from "./bash-executor.js";
26
25
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
27
26
  import { evictionBudget, installContextPipeline, isDefaultStreamFunction } from "./context/pipeline.js";
28
- import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
27
+ import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "./defaults.js";
29
28
  import { SessionEvidenceSink } from "./evidence.js";
30
29
  import { exportSessionToHtml } from "./export-html/index.js";
31
30
  import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
@@ -36,7 +35,8 @@ import { evaluateToolCall } from "./permissions/gate.js";
36
35
  import { createInteractiveResponder } from "./permissions/responder.js";
37
36
  import { resolveEffectiveModeWithOrigin } from "./permissions/startup.js";
38
37
  import { expandPromptTemplate } from "./prompt-templates.js";
39
- import { CURRENT_SESSION_VERSION, getLatestCompactionEntry, TODO_CUSTOM_ENTRY_TYPE, } from "./session-manager.js";
38
+ import { exportSessionToJsonl } from "./session-export.js";
39
+ import { getLatestCompactionEntry, TODO_CUSTOM_ENTRY_TYPE } from "./session-manager.js";
40
40
  import { slugifySkillCommandName } from "./skills.js";
41
41
  import { createSyntheticSourceInfo } from "./source-info.js";
42
42
  import { buildSystemPrompt } from "./system-prompt.js";
@@ -80,8 +80,6 @@ function estimateMessagesTokens(messages) {
80
80
  // ============================================================================
81
81
  // Constants
82
82
  // ============================================================================
83
- /** Standard thinking levels */
84
- const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high"];
85
83
  // ============================================================================
86
84
  // AgentSession Class
87
85
  // ============================================================================
@@ -131,6 +129,7 @@ export class AgentSession {
131
129
  _evidenceSink;
132
130
  _diagnosticsOperations;
133
131
  _lspOperations;
132
+ _webSearchOperations;
134
133
  _extensionUIContext;
135
134
  _extensionMode = "print";
136
135
  _extensionCommandContextActions;
@@ -168,6 +167,7 @@ export class AgentSession {
168
167
  this._evidenceSink = config.evidenceSink ?? new SessionEvidenceSink(this.sessionManager);
169
168
  this._diagnosticsOperations = config.diagnosticsOperations;
170
169
  this._lspOperations = config.lspOperations;
170
+ this._webSearchOperations = config.webSearchOperations;
171
171
  // Always subscribe to agent events for internal handling
172
172
  // (session persistence, extensions, auto-compaction, retry logic)
173
173
  this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
@@ -404,6 +404,11 @@ export class AgentSession {
404
404
  followUp: [...this._followUpMessages],
405
405
  });
406
406
  }
407
+ async _emitSessionCompactFailed(event) {
408
+ if (this._extensionRunner.hasHandlers("session_compact_failed")) {
409
+ await this._extensionRunner.emit({ type: "session_compact_failed", ...event });
410
+ }
411
+ }
407
412
  _getIdleWaitPromise() {
408
413
  if (!this._idleWaitPromise) {
409
414
  this._idleWaitPromise = new Promise((resolve) => {
@@ -1186,7 +1191,7 @@ export class AgentSession {
1186
1191
  if (options?.deliverAs === "nextTurn") {
1187
1192
  this._pendingNextTurnMessages.push(appMessage);
1188
1193
  }
1189
- else if (this.isStreaming) {
1194
+ else if (this.isStreaming && options?.triggerTurn !== false) {
1190
1195
  if (options?.deliverAs === "followUp") {
1191
1196
  this.agent.followUp(appMessage);
1192
1197
  }
@@ -1210,6 +1215,7 @@ export class AgentSession {
1210
1215
  *
1211
1216
  * @param content User message content (string or content array)
1212
1217
  * @param options.deliverAs Delivery mode when streaming: "steer" or "followUp"
1218
+ * @param options.expandPromptTemplates Whether to dispatch extension commands and expand skill commands and prompt templates. Default: false.
1213
1219
  */
1214
1220
  async sendUserMessage(content, options) {
1215
1221
  // Normalize content to text string + optional images
@@ -1233,9 +1239,8 @@ export class AgentSession {
1233
1239
  if (images.length === 0)
1234
1240
  images = undefined;
1235
1241
  }
1236
- // Use prompt() with expandPromptTemplates: false to skip command handling and template expansion
1237
1242
  await this.prompt(text, {
1238
- expandPromptTemplates: false,
1243
+ expandPromptTemplates: options?.expandPromptTemplates ?? false,
1239
1244
  streamingBehavior: options?.deliverAs,
1240
1245
  images,
1241
1246
  source: "extension",
@@ -1326,19 +1331,24 @@ export class AgentSession {
1326
1331
  }
1327
1332
  /**
1328
1333
  * Set model directly.
1329
- * Validates that auth is configured, saves to session and settings.
1334
+ * Validates that auth is configured and saves to the session transcript.
1335
+ * Persists to global defaults only when options.persist is true.
1330
1336
  * @throws Error if no auth is configured for the model
1331
1337
  */
1332
- async setModel(model) {
1338
+ async setModel(model, options = {}) {
1333
1339
  if (!(await this._modelRuntime.checkAuth(model.provider))) {
1334
1340
  throw new Error(`No API key for ${model.provider}/${model.id}`);
1335
1341
  }
1336
1342
  const previousModel = this.model;
1337
- const thinkingLevel = this._getThinkingLevelForModelSwitch();
1343
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(model);
1338
1344
  this.agent.state.model = model;
1339
1345
  this.sessionManager.appendModelChange(model.provider, model.id);
1340
- this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1341
- // Re-clamp thinking level for new model's capabilities
1346
+ if (options.persist) {
1347
+ this.settingsManager.setDefaultModelAndProvider(model.provider, model.id);
1348
+ }
1349
+ // Apply thinking level for the new model.
1350
+ // Per-model thinking level overrides take priority over the global default.
1351
+ // Model persistence does not implicitly rewrite the global thinking default.
1342
1352
  this.setThinkingLevel(thinkingLevel);
1343
1353
  await this._emitModelSelect(model, previousModel, "set");
1344
1354
  }
@@ -1348,13 +1358,13 @@ export class AgentSession {
1348
1358
  * @param direction - "forward" (default) or "backward"
1349
1359
  * @returns The new model info, or undefined if only one model available
1350
1360
  */
1351
- async cycleModel(direction = "forward") {
1361
+ async cycleModel(direction = "forward", options = {}) {
1352
1362
  if (this._scopedModels.length > 0) {
1353
- return this._cycleScopedModel(direction);
1363
+ return this._cycleScopedModel(direction, options);
1354
1364
  }
1355
- return this._cycleAvailableModel(direction);
1365
+ return this._cycleAvailableModel(direction, options);
1356
1366
  }
1357
- async _cycleScopedModel(direction) {
1367
+ async _cycleScopedModel(direction, options) {
1358
1368
  const availableIds = new Set(this._modelRuntime.getAvailableSnapshot().map((model) => `${model.provider}\0${model.id}`));
1359
1369
  const scopedModels = this._scopedModels.filter((scoped) => availableIds.has(`${scoped.model.provider}\0${scoped.model.id}`));
1360
1370
  if (scopedModels.length <= 1)
@@ -1366,20 +1376,23 @@ export class AgentSession {
1366
1376
  const len = scopedModels.length;
1367
1377
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1368
1378
  const next = scopedModels[nextIndex];
1369
- const thinkingLevel = this._getThinkingLevelForModelSwitch(next.thinkingLevel);
1379
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(next.model, next.thinkingLevel);
1370
1380
  // Apply model
1371
1381
  this.agent.state.model = next.model;
1372
1382
  this.sessionManager.appendModelChange(next.model.provider, next.model.id);
1373
- this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1374
- // Apply thinking level.
1375
- // - Explicit scoped model thinking level overrides current session level
1376
- // - Undefined scoped model thinking level inherits the current session preference
1383
+ if (options.persist) {
1384
+ this.settingsManager.setDefaultModelAndProvider(next.model.provider, next.model.id);
1385
+ }
1386
+ // Apply thinking level for the new model.
1387
+ // - Explicit scoped model thinking level overrides defaults
1388
+ // - Per-model thinking level overrides take priority over the global default
1377
1389
  // setThinkingLevel clamps to model capabilities.
1390
+ // Model persistence does not implicitly rewrite the global thinking default.
1378
1391
  this.setThinkingLevel(thinkingLevel);
1379
1392
  await this._emitModelSelect(next.model, currentModel, "cycle");
1380
1393
  return { model: next.model, thinkingLevel: this.thinkingLevel, isScoped: true };
1381
1394
  }
1382
- async _cycleAvailableModel(direction) {
1395
+ async _cycleAvailableModel(direction, options) {
1383
1396
  const availableModels = this._modelRuntime.getAvailableSnapshot();
1384
1397
  if (availableModels.length <= 1)
1385
1398
  return undefined;
@@ -1390,11 +1403,14 @@ export class AgentSession {
1390
1403
  const len = availableModels.length;
1391
1404
  const nextIndex = direction === "forward" ? (currentIndex + 1) % len : (currentIndex - 1 + len) % len;
1392
1405
  const nextModel = availableModels[nextIndex];
1393
- const thinkingLevel = this._getThinkingLevelForModelSwitch();
1406
+ const thinkingLevel = this._getThinkingLevelForModelSwitch(nextModel);
1394
1407
  this.agent.state.model = nextModel;
1395
1408
  this.sessionManager.appendModelChange(nextModel.provider, nextModel.id);
1396
- this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1397
- // Re-clamp thinking level for new model's capabilities
1409
+ if (options.persist) {
1410
+ this.settingsManager.setDefaultModelAndProvider(nextModel.provider, nextModel.id);
1411
+ }
1412
+ // Apply thinking level for the new model.
1413
+ // Model persistence does not implicitly rewrite the global thinking default.
1398
1414
  this.setThinkingLevel(thinkingLevel);
1399
1415
  await this._emitModelSelect(nextModel, currentModel, "cycle");
1400
1416
  return { model: nextModel, thinkingLevel: this.thinkingLevel, isScoped: false };
@@ -1405,20 +1421,21 @@ export class AgentSession {
1405
1421
  /**
1406
1422
  * Set thinking level.
1407
1423
  * Clamps to model capabilities based on available thinking levels.
1408
- * Saves to session and settings only if the level actually changes.
1424
+ * Saves the clamped level to the session transcript only if the level actually changes.
1425
+ * Persists the requested level to global defaults only when options.persist is true.
1409
1426
  */
1410
- setThinkingLevel(level) {
1427
+ setThinkingLevel(level, options = {}) {
1411
1428
  const availableLevels = this.getAvailableThinkingLevels();
1412
1429
  const effectiveLevel = availableLevels.includes(level) ? level : this._clampThinkingLevel(level, availableLevels);
1413
1430
  // Only persist if actually changing
1414
1431
  const previousLevel = this.agent.state.thinkingLevel;
1415
1432
  const isChanging = effectiveLevel !== previousLevel;
1416
1433
  this.agent.state.thinkingLevel = effectiveLevel;
1434
+ if (options.persist) {
1435
+ this.settingsManager.setDefaultThinkingLevel(level);
1436
+ }
1417
1437
  if (isChanging) {
1418
1438
  this.sessionManager.appendThinkingLevelChange(effectiveLevel);
1419
- if (this.supportsThinking() || effectiveLevel !== "off") {
1420
- this.settingsManager.setDefaultThinkingLevel(effectiveLevel);
1421
- }
1422
1439
  this._emit({ type: "thinking_level_changed", level: effectiveLevel });
1423
1440
  void this._extensionRunner.emit({
1424
1441
  type: "thinking_level_select",
@@ -1431,14 +1448,14 @@ export class AgentSession {
1431
1448
  * Cycle to next thinking level.
1432
1449
  * @returns New level, or undefined if model doesn't support thinking
1433
1450
  */
1434
- cycleThinkingLevel() {
1451
+ cycleThinkingLevel(options = {}) {
1435
1452
  if (!this.supportsThinking())
1436
1453
  return undefined;
1437
1454
  const levels = this.getAvailableThinkingLevels();
1438
1455
  const currentIndex = levels.indexOf(this.thinkingLevel);
1439
1456
  const nextIndex = (currentIndex + 1) % levels.length;
1440
1457
  const nextLevel = levels[nextIndex];
1441
- this.setThinkingLevel(nextLevel);
1458
+ this.setThinkingLevel(nextLevel, options);
1442
1459
  return nextLevel;
1443
1460
  }
1444
1461
  /**
@@ -1447,7 +1464,7 @@ export class AgentSession {
1447
1464
  */
1448
1465
  getAvailableThinkingLevels() {
1449
1466
  if (!this.model)
1450
- return THINKING_LEVELS;
1467
+ return [...THINKING_LEVEL_OPTIONS];
1451
1468
  return getSupportedThinkingLevels(this.model);
1452
1469
  }
1453
1470
  /**
@@ -1456,14 +1473,18 @@ export class AgentSession {
1456
1473
  supportsThinking() {
1457
1474
  return !!this.model?.reasoning;
1458
1475
  }
1459
- _getThinkingLevelForModelSwitch(explicitLevel) {
1476
+ _getThinkingLevelForModelSwitch(targetModel, explicitLevel) {
1460
1477
  if (explicitLevel !== undefined) {
1461
1478
  return explicitLevel;
1462
1479
  }
1463
- if (!this.supportsThinking()) {
1464
- return this.settingsManager.getDefaultThinkingLevel() ?? DEFAULT_THINKING_LEVEL;
1480
+ // Per-model default takes priority when switching to a model that has one
1481
+ if (targetModel) {
1482
+ const perModel = this.settingsManager.getModelThinkingLevel(targetModel.provider, targetModel.id);
1483
+ if (perModel !== undefined) {
1484
+ return perModel;
1485
+ }
1465
1486
  }
1466
- return this.thinkingLevel;
1487
+ return this.settingsManager.getDefaultThinkingLevel() ?? this.thinkingLevel ?? DEFAULT_THINKING_LEVEL;
1467
1488
  }
1468
1489
  _clampThinkingLevel(level, _availableLevels) {
1469
1490
  return this.model ? clampThinkingLevel(this.model, level) : "off";
@@ -1494,15 +1515,30 @@ export class AgentSession {
1494
1515
  // =========================================================================
1495
1516
  // Compaction
1496
1517
  // =========================================================================
1518
+ /** Generate Pi's built-in compaction summary for manual and automatic compaction. */
1519
+ async _runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, signal, env, reason) {
1520
+ return compact(preparation, requestModel, apiKey, headers, customInstructions, signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }), undefined);
1521
+ }
1497
1522
  /**
1498
1523
  * Manually compact the session context.
1499
- * Aborts current agent operation first.
1524
+ *
1525
+ * This is the manual entry point used by `/compact`, RPC, and extensions. It is
1526
+ * separate from automatic threshold/overflow compaction, which enters through
1527
+ * `_checkCompaction()` and `_runAutoCompaction()`. After preparation and the
1528
+ * `session_before_compact` hook, both paths call the lower-level `compact()`
1529
+ * function imported from `./compaction/index.ts`, unless the hook cancels or
1530
+ * supplies a custom result.
1531
+ *
1532
+ * Aborts the current agent operation first. Manual compaction never retries or
1533
+ * continues the interrupted agent turn.
1534
+ *
1500
1535
  * @param customInstructions Optional instructions for the compaction summary
1501
1536
  */
1502
1537
  async compact(customInstructions) {
1503
1538
  await this.abort();
1504
1539
  this._compactionAbortController = new AbortController();
1505
1540
  this._emit({ type: "compaction_start", reason: "manual" });
1541
+ let fromExtension = false;
1506
1542
  try {
1507
1543
  if (!this.model) {
1508
1544
  throw new Error(formatNoModelSelectedMessage());
@@ -1520,7 +1556,6 @@ export class AgentSession {
1520
1556
  throw new Error("Nothing to compact (session too small)");
1521
1557
  }
1522
1558
  let extensionCompaction;
1523
- let fromExtension = false;
1524
1559
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
1525
1560
  const result = (await this._extensionRunner.emit({
1526
1561
  type: "session_before_compact",
@@ -1553,8 +1588,8 @@ export class AgentSession {
1553
1588
  details = extensionCompaction.details;
1554
1589
  }
1555
1590
  else {
1556
- // Generate compaction result
1557
- const result = await compact(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason: "manual" }));
1591
+ // Shared default summary generator, also used by automatic compaction.
1592
+ const result = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, customInstructions, this._compactionAbortController.signal, env, "manual");
1558
1593
  summary = result.summary;
1559
1594
  firstKeptEntryId = result.firstKeptEntryId;
1560
1595
  tokensBefore = result.tokensBefore;
@@ -1602,6 +1637,7 @@ export class AgentSession {
1602
1637
  catch (error) {
1603
1638
  const message = error instanceof Error ? error.message : String(error);
1604
1639
  const aborted = message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
1640
+ const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
1605
1641
  this._compactionAbortController = undefined;
1606
1642
  this._emit({
1607
1643
  type: "compaction_end",
@@ -1609,7 +1645,14 @@ export class AgentSession {
1609
1645
  result: undefined,
1610
1646
  aborted,
1611
1647
  willRetry: false,
1612
- errorMessage: aborted ? undefined : `Compaction failed: ${message}`,
1648
+ errorMessage,
1649
+ });
1650
+ await this._emitSessionCompactFailed({
1651
+ reason: "manual",
1652
+ errorMessage,
1653
+ aborted,
1654
+ willRetry: false,
1655
+ fromExtension,
1613
1656
  });
1614
1657
  throw error;
1615
1658
  }
@@ -1631,16 +1674,25 @@ export class AgentSession {
1631
1674
  this._branchSummaryAbortController?.abort();
1632
1675
  }
1633
1676
  /**
1634
- * Check if compaction is needed and run it.
1635
- * Called after agent_end and before prompt submission.
1677
+ * Dispatch automatic compaction after `agent_end` or before prompt submission.
1678
+ * Manual compaction does not call this method; it enters through `compact()`.
1679
+ *
1680
+ * Automatic cases:
1681
+ * 1. Overflow with retry: a context-overflow error or recoverable length stop;
1682
+ * remove the failed assistant message, compact, and retry the turn once.
1683
+ * 2. Overflow without retry: a successful response exceeded the configured
1684
+ * context window; compact but preserve the completed response.
1685
+ * 3. Threshold without retry: valid or estimated context usage crossed the
1686
+ * configured threshold; compact without retrying the completed response.
1636
1687
  *
1637
- * Two cases:
1638
- * 1. Recoverable failure: LLM returned context overflow or stopped below its desired output limit;
1639
- * remove the assistant message, compact, and auto-retry once
1640
- * 2. Threshold: Context over threshold, compact, NO auto-retry (user continues manually)
1688
+ * Each case calls `_runAutoCompaction()`. After preparation and the
1689
+ * `session_before_compact` hook, that method calls the lower-level `compact()`
1690
+ * function imported from `./compaction/index.ts`, unless the hook cancels or
1691
+ * supplies a custom result.
1641
1692
  *
1642
1693
  * @param assistantMessage The assistant message to check
1643
1694
  * @param skipAbortedCheck If false, include aborted messages (for pre-prompt check). Default: true
1695
+ * @returns Whether the post-run loop should call `agent.continue()` for overflow recovery or queued messages
1644
1696
  */
1645
1697
  async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
1646
1698
  const settings = this.settingsManager.getCompactionSettings();
@@ -1663,38 +1715,49 @@ export class AgentSession {
1663
1715
  if (assistantIsFromBeforeCompaction) {
1664
1716
  return false;
1665
1717
  }
1666
- // Case 1: Recoverable failure. Explicit/silent context overflow still uses context metadata.
1718
+ // Automatic cases 1 and 2: context overflow.
1667
1719
  // A length stop is recoverable when output ended below the model's original desired limit,
1668
1720
  // independent of the configured context size or any context-clamped provider request limit.
1669
- // A successful response over the configured window should compact but must not retry: the
1670
- // assistant answer already completed and agent.continue() cannot continue from an assistant.
1721
+ const contextOverflow = sameModel && isContextOverflow(assistantMessage, contextWindow);
1671
1722
  const recoverableLength = sameModel && isRecoverableLength(assistantMessage, this.model?.maxTokens ?? 0);
1672
- if (sameModel && (isContextOverflow(assistantMessage, contextWindow) || recoverableLength)) {
1723
+ if (contextOverflow || recoverableLength) {
1673
1724
  const willRetry = assistantMessage.stopReason !== "stop";
1725
+ // Case 2: the response completed successfully. Compact, but do not retry because
1726
+ // agent.continue() cannot continue from a completed assistant response.
1674
1727
  if (!willRetry) {
1675
1728
  return await this._runAutoCompaction("overflow", false);
1676
1729
  }
1677
1730
  if (this._overflowRecoveryAttempted) {
1731
+ const errorMessage = contextOverflow
1732
+ ? "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model."
1733
+ : "Truncated response recovery failed after one compact-and-retry attempt.";
1678
1734
  this._emit({
1679
1735
  type: "compaction_end",
1680
1736
  reason: "overflow",
1681
1737
  result: undefined,
1682
1738
  aborted: false,
1683
1739
  willRetry: false,
1684
- errorMessage: "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.",
1740
+ errorMessage,
1741
+ });
1742
+ await this._emitSessionCompactFailed({
1743
+ reason: "overflow",
1744
+ errorMessage,
1745
+ aborted: false,
1746
+ willRetry: false,
1747
+ fromExtension: false,
1685
1748
  });
1686
1749
  return false;
1687
1750
  }
1751
+ // Case 1: remove the failed or truncated message from agent state, compact, and
1752
+ // retry once. The message remains in session history but is excluded from retry context.
1688
1753
  this._overflowRecoveryAttempted = true;
1689
- // Remove the failed or truncated message from agent state. It remains in session history,
1690
- // but must not be included in the compact-and-retry context.
1691
1754
  const messages = this.agent.state.messages;
1692
1755
  if (messages.length > 0 && messages[messages.length - 1].role === "assistant") {
1693
1756
  this.agent.state.messages = messages.slice(0, -1);
1694
1757
  }
1695
1758
  return await this._runAutoCompaction("overflow", willRetry);
1696
1759
  }
1697
- // Case 2: Threshold - context is getting large
1760
+ // Case 3: threshold compaction without retry.
1698
1761
  // For error messages or all-zero usage messages, estimate from the last valid response.
1699
1762
  // This ensures sessions that hit persistent API errors (e.g. 529) or malformed zero-usage
1700
1763
  // responses can still compact and do not reset context accounting.
@@ -1703,16 +1766,18 @@ export class AgentSession {
1703
1766
  if (assistantMessage.stopReason === "error" || directContextTokens === 0) {
1704
1767
  const messages = this.agent.state.messages;
1705
1768
  const estimate = estimateContextTokens(messages);
1706
- if (estimate.lastUsageIndex === null)
1707
- return false; // No usage data at all
1708
- // Verify the usage source is post-compaction. Kept pre-compaction messages
1709
- // have stale usage reflecting the old (larger) context and would falsely
1710
- // trigger compaction right after one just finished.
1711
- const usageMsg = messages[estimate.lastUsageIndex];
1712
- if (compactionEntry &&
1713
- usageMsg.role === "assistant" &&
1714
- usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
1715
- return false;
1769
+ // Without provider usage, estimate.tokens is the pure message-size estimate.
1770
+ // Only usage-backed estimates need the stale pre-compaction check.
1771
+ if (estimate.lastUsageIndex !== null) {
1772
+ // Verify the usage source is post-compaction. Kept pre-compaction messages
1773
+ // have stale usage reflecting the old (larger) context and would falsely
1774
+ // trigger compaction right after one just finished.
1775
+ const usageMsg = messages[estimate.lastUsageIndex];
1776
+ if (compactionEntry &&
1777
+ usageMsg.role === "assistant" &&
1778
+ usageMsg.timestamp <= new Date(compactionEntry.timestamp).getTime()) {
1779
+ return false;
1780
+ }
1716
1781
  }
1717
1782
  contextTokens = estimate.tokens;
1718
1783
  }
@@ -1725,11 +1790,19 @@ export class AgentSession {
1725
1790
  return false;
1726
1791
  }
1727
1792
  /**
1728
- * Internal: Run auto-compaction with events.
1793
+ * Execute threshold or overflow compaction. Manual compaction uses
1794
+ * `AgentSession.compact()` instead. Both paths call the lower-level `compact()`
1795
+ * function imported from `./compaction/index.ts` after preparation and extension
1796
+ * interception.
1797
+ *
1798
+ * @param reason Automatic trigger selected by `_checkCompaction()`
1799
+ * @param willRetry Whether to continue the interrupted turn after overflow compaction
1800
+ * @returns Whether the post-run loop should call `agent.continue()`
1729
1801
  */
1730
1802
  async _runAutoCompaction(reason, willRetry) {
1731
1803
  const settings = this.settingsManager.getCompactionSettings();
1732
1804
  let started = false;
1805
+ let fromExtension = false;
1733
1806
  try {
1734
1807
  if (!this.model) {
1735
1808
  return false;
@@ -1744,7 +1817,6 @@ export class AgentSession {
1744
1817
  this._autoCompactionAbortController = new AbortController();
1745
1818
  started = true;
1746
1819
  let extensionCompaction;
1747
- let fromExtension = false;
1748
1820
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
1749
1821
  const extensionResult = (await this._extensionRunner.emit({
1750
1822
  type: "session_before_compact",
@@ -1763,6 +1835,12 @@ export class AgentSession {
1763
1835
  aborted: true,
1764
1836
  willRetry: false,
1765
1837
  });
1838
+ await this._emitSessionCompactFailed({
1839
+ reason,
1840
+ aborted: true,
1841
+ willRetry: false,
1842
+ fromExtension: false,
1843
+ });
1766
1844
  return false;
1767
1845
  }
1768
1846
  if (extensionResult?.compaction) {
@@ -1784,8 +1862,8 @@ export class AgentSession {
1784
1862
  details = extensionCompaction.details;
1785
1863
  }
1786
1864
  else {
1787
- // Generate compaction result
1788
- const compactResult = await compact(preparation, requestModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, this.thinkingLevel, this.agent.streamFunction, env, this.settingsManager.getRetrySettings(), this._summarizationRetryCallbacks({ source: "compaction", reason }));
1865
+ // Shared default summary generator, also used by manual compaction.
1866
+ const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, env, reason);
1789
1867
  summary = compactResult.summary;
1790
1868
  firstKeptEntryId = compactResult.firstKeptEntryId;
1791
1869
  tokensBefore = compactResult.tokensBefore;
@@ -1800,6 +1878,12 @@ export class AgentSession {
1800
1878
  aborted: true,
1801
1879
  willRetry: false,
1802
1880
  });
1881
+ await this._emitSessionCompactFailed({
1882
+ reason,
1883
+ aborted: true,
1884
+ willRetry: false,
1885
+ fromExtension,
1886
+ });
1803
1887
  return false;
1804
1888
  }
1805
1889
  this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
@@ -1846,15 +1930,23 @@ export class AgentSession {
1846
1930
  catch (error) {
1847
1931
  const errorMessage = error instanceof Error ? error.message : "compaction failed";
1848
1932
  if (started) {
1933
+ const formattedErrorMessage = reason === "overflow"
1934
+ ? `Context overflow recovery failed: ${errorMessage}`
1935
+ : `Auto-compaction failed: ${errorMessage}`;
1849
1936
  this._emit({
1850
1937
  type: "compaction_end",
1851
1938
  reason,
1852
1939
  result: undefined,
1853
1940
  aborted: false,
1854
1941
  willRetry: false,
1855
- errorMessage: reason === "overflow"
1856
- ? `Context overflow recovery failed: ${errorMessage}`
1857
- : `Auto-compaction failed: ${errorMessage}`,
1942
+ errorMessage: formattedErrorMessage,
1943
+ });
1944
+ await this._emitSessionCompactFailed({
1945
+ reason,
1946
+ errorMessage: formattedErrorMessage,
1947
+ aborted: false,
1948
+ willRetry: false,
1949
+ fromExtension,
1858
1950
  });
1859
1951
  }
1860
1952
  return false;
@@ -2172,6 +2264,7 @@ export class AgentSession {
2172
2264
  edit: { diagnosticsOperations: this._diagnosticsOperations },
2173
2265
  write: { diagnosticsOperations: this._diagnosticsOperations },
2174
2266
  ...(this._lspOperations ? { lsp: { operations: this._lspOperations } } : {}),
2267
+ ...(this._webSearchOperations ? { web_search: { operations: this._webSearchOperations } } : {}),
2175
2268
  });
2176
2269
  baseToolDefinitions.tool_schema = createToolSchemaToolDefinition({
2177
2270
  getTool: (name) => {
@@ -2753,11 +2846,11 @@ export class AgentSession {
2753
2846
  /**
2754
2847
  * Export session to HTML.
2755
2848
  * @param outputPath Optional output path (defaults to session directory)
2849
+ * @param options Optional export presentation settings
2756
2850
  * @returns Path to exported file
2757
2851
  */
2758
- async exportToHtml(outputPath) {
2759
- const configuredThemeName = this.settingsManager.getTheme();
2760
- const themeName = configuredThemeName && getThemeByName(configuredThemeName) ? configuredThemeName : undefined;
2852
+ async exportToHtml(outputPath, options = {}) {
2853
+ const themeName = [options.themeName, this.settingsManager.getTheme()].find((candidate) => candidate !== undefined && getThemeByName(candidate) !== undefined);
2761
2854
  // Create tool renderer if we have an extension runner (for custom tool HTML rendering)
2762
2855
  const toolRenderer = createToolHtmlRenderer({
2763
2856
  getToolDefinition: (name) => this.getToolDefinition(name),
@@ -2777,29 +2870,7 @@ export class AgentSession {
2777
2870
  * @returns The resolved output file path.
2778
2871
  */
2779
2872
  exportToJsonl(outputPath) {
2780
- const filePath = resolvePath(outputPath ?? `session-${new Date().toISOString().replace(/[:.]/g, "-")}.jsonl`, process.cwd());
2781
- const dir = dirname(filePath);
2782
- if (!existsSync(dir)) {
2783
- mkdirSync(dir, { recursive: true });
2784
- }
2785
- const header = {
2786
- type: "session",
2787
- version: CURRENT_SESSION_VERSION,
2788
- id: this.sessionManager.getSessionId(),
2789
- timestamp: new Date().toISOString(),
2790
- cwd: this.sessionManager.getCwd(),
2791
- };
2792
- const branchEntries = this.sessionManager.getBranch();
2793
- const lines = [JSON.stringify(header)];
2794
- // Re-chain parentIds to form a linear sequence
2795
- let prevId = null;
2796
- for (const entry of branchEntries) {
2797
- const linear = { ...entry, parentId: prevId };
2798
- lines.push(JSON.stringify(linear));
2799
- prevId = entry.id;
2800
- }
2801
- writeFileSync(filePath, `${lines.join("\n")}\n`);
2802
- return filePath;
2873
+ return exportSessionToJsonl(this.sessionManager, outputPath);
2803
2874
  }
2804
2875
  // =========================================================================
2805
2876
  // Utilities