pi-namespace-patch 0.85.1 → 0.86.0-namespace.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 (317) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +26 -705
  3. package/dist/bundle/chunks/anthropic-messages-MYU5ZMRF.js +6 -0
  4. package/dist/bundle/chunks/azure-openai-responses-HUSQ3GP2.js +2 -0
  5. package/dist/bundle/chunks/bedrock-converse-stream.js +47 -38
  6. package/dist/bundle/chunks/chunk-A3JYRWB6.js +2 -0
  7. package/dist/bundle/chunks/chunk-DTD7JQ7Y.js +42 -0
  8. package/dist/bundle/chunks/chunk-FUXEF6JQ.js +44 -0
  9. package/dist/bundle/chunks/chunk-IS2VY2NY.js +1561 -0
  10. package/dist/bundle/chunks/{chunk-IDDQWTHI.js → chunk-RCZIEVGO.js} +10 -1
  11. package/dist/bundle/chunks/chunk-S7SZN6Z3.js +2 -0
  12. package/dist/bundle/chunks/chunk-SXW7XMQG.js +2 -0
  13. package/dist/bundle/chunks/chunk-WIUZI2CJ.js +11 -0
  14. package/dist/bundle/chunks/github-copilot.js +1 -1
  15. package/dist/bundle/chunks/google-generative-ai-IMS5EK2Y.js +2 -0
  16. package/dist/bundle/chunks/google-vertex-O4YJEQUW.js +2 -0
  17. package/dist/bundle/chunks/jiti-loader-C3NU2SDH.js +21 -0
  18. package/dist/bundle/chunks/jiti-static-loader-E6ANQD5T.js +2 -0
  19. package/dist/bundle/chunks/mistral-conversations-NOSQHBEY.js +5 -0
  20. package/dist/bundle/chunks/{node-KG362ECQ.js → node-O3RIBJRH.js} +1 -1
  21. package/dist/bundle/chunks/openai-codex-responses-QDSOTBBO.js +10 -0
  22. package/dist/bundle/chunks/openai-completions-KQPOPEVM.js +7 -0
  23. package/dist/bundle/chunks/openai-responses-HWEUZ6WZ.js +2 -0
  24. package/dist/bundle/chunks/{pi-messages-PML4SPVJ.js → pi-messages-GFKBWJHZ.js} +1 -1
  25. package/dist/bundle/chunks/virtual-modules-UQVS3IVK.js +2 -0
  26. package/dist/bundle/cli.js +1 -1
  27. package/dist/bundle/index.js +1 -1
  28. package/dist/bundle/rpc-entry.js +1 -1
  29. package/dist/cli/session-picker.d.ts +1 -1
  30. package/dist/cli/session-picker.d.ts.map +1 -1
  31. package/dist/cli/session-picker.js.map +1 -1
  32. package/dist/config.d.ts.map +1 -1
  33. package/dist/config.js +1 -1
  34. package/dist/config.js.map +1 -1
  35. package/dist/core/agent-session.d.ts +56 -6
  36. package/dist/core/agent-session.d.ts.map +1 -1
  37. package/dist/core/agent-session.js +294 -157
  38. package/dist/core/agent-session.js.map +1 -1
  39. package/dist/core/bug-report-upload.d.ts +12 -0
  40. package/dist/core/bug-report-upload.d.ts.map +1 -0
  41. package/dist/core/bug-report-upload.js +22 -0
  42. package/dist/core/bug-report-upload.js.map +1 -0
  43. package/dist/core/bug-report.d.ts +176 -0
  44. package/dist/core/bug-report.d.ts.map +1 -0
  45. package/dist/core/bug-report.js +292 -0
  46. package/dist/core/bug-report.js.map +1 -0
  47. package/dist/core/cache-stats.d.ts.map +1 -1
  48. package/dist/core/cache-stats.js +12 -1
  49. package/dist/core/cache-stats.js.map +1 -1
  50. package/dist/core/cache-warmer.d.ts +102 -0
  51. package/dist/core/cache-warmer.d.ts.map +1 -0
  52. package/dist/core/cache-warmer.js +342 -0
  53. package/dist/core/cache-warmer.js.map +1 -0
  54. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  55. package/dist/core/compaction/branch-summarization.js +2 -2
  56. package/dist/core/compaction/branch-summarization.js.map +1 -1
  57. package/dist/core/compaction/compaction.d.ts +2 -2
  58. package/dist/core/compaction/compaction.d.ts.map +1 -1
  59. package/dist/core/compaction/compaction.js +11 -12
  60. package/dist/core/compaction/compaction.js.map +1 -1
  61. package/dist/core/crash-log.d.ts +22 -0
  62. package/dist/core/crash-log.d.ts.map +1 -0
  63. package/dist/core/crash-log.js +71 -0
  64. package/dist/core/crash-log.js.map +1 -0
  65. package/dist/core/experimental.d.ts +0 -4
  66. package/dist/core/experimental.d.ts.map +1 -1
  67. package/dist/core/experimental.js +0 -4
  68. package/dist/core/experimental.js.map +1 -1
  69. package/dist/core/extensions/index.d.ts +1 -1
  70. package/dist/core/extensions/index.d.ts.map +1 -1
  71. package/dist/core/extensions/index.js.map +1 -1
  72. package/dist/core/extensions/jiti-loader.d.ts +2 -0
  73. package/dist/core/extensions/jiti-loader.d.ts.map +1 -0
  74. package/dist/core/extensions/jiti-loader.js +4 -0
  75. package/dist/core/extensions/jiti-loader.js.map +1 -0
  76. package/dist/core/extensions/jiti-static-loader.d.ts +2 -0
  77. package/dist/core/extensions/jiti-static-loader.d.ts.map +1 -0
  78. package/dist/core/extensions/jiti-static-loader.js +4 -0
  79. package/dist/core/extensions/jiti-static-loader.js.map +1 -0
  80. package/dist/core/extensions/loader.d.ts.map +1 -1
  81. package/dist/core/extensions/loader.js +38 -51
  82. package/dist/core/extensions/loader.js.map +1 -1
  83. package/dist/core/extensions/runner.d.ts +10 -7
  84. package/dist/core/extensions/runner.d.ts.map +1 -1
  85. package/dist/core/extensions/runner.js +85 -67
  86. package/dist/core/extensions/runner.js.map +1 -1
  87. package/dist/core/extensions/types.d.ts +60 -50
  88. package/dist/core/extensions/types.d.ts.map +1 -1
  89. package/dist/core/extensions/types.js.map +1 -1
  90. package/dist/core/extensions/virtual-modules.d.ts +3 -0
  91. package/dist/core/extensions/virtual-modules.d.ts.map +1 -0
  92. package/dist/core/extensions/virtual-modules.js +38 -0
  93. package/dist/core/extensions/virtual-modules.js.map +1 -0
  94. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  95. package/dist/core/extensions/wrapper.js +1 -20
  96. package/dist/core/extensions/wrapper.js.map +1 -1
  97. package/dist/core/index.d.ts +2 -1
  98. package/dist/core/index.d.ts.map +1 -1
  99. package/dist/core/index.js.map +1 -1
  100. package/dist/core/keybindings.d.ts +1 -1
  101. package/dist/core/keybindings.d.ts.map +1 -1
  102. package/dist/core/keybindings.js +1 -1
  103. package/dist/core/keybindings.js.map +1 -1
  104. package/dist/core/messages.d.ts.map +1 -1
  105. package/dist/core/messages.js +1 -0
  106. package/dist/core/messages.js.map +1 -1
  107. package/dist/core/model-config.d.ts +101 -20
  108. package/dist/core/model-config.d.ts.map +1 -1
  109. package/dist/core/model-config.js +25 -18
  110. package/dist/core/model-config.js.map +1 -1
  111. package/dist/core/model-registry.d.ts +5 -1
  112. package/dist/core/model-registry.d.ts.map +1 -1
  113. package/dist/core/model-registry.js +8 -0
  114. package/dist/core/model-registry.js.map +1 -1
  115. package/dist/core/model-resolver.d.ts.map +1 -1
  116. package/dist/core/model-resolver.js +1 -1
  117. package/dist/core/model-resolver.js.map +1 -1
  118. package/dist/core/model-runtime.d.ts.map +1 -1
  119. package/dist/core/model-runtime.js +5 -3
  120. package/dist/core/model-runtime.js.map +1 -1
  121. package/dist/core/provider-composer.d.ts +3 -2
  122. package/dist/core/provider-composer.d.ts.map +1 -1
  123. package/dist/core/provider-composer.js +2 -0
  124. package/dist/core/provider-composer.js.map +1 -1
  125. package/dist/core/radius.d.ts +3 -0
  126. package/dist/core/radius.d.ts.map +1 -1
  127. package/dist/core/radius.js +6 -0
  128. package/dist/core/radius.js.map +1 -1
  129. package/dist/core/sdk.d.ts.map +1 -1
  130. package/dist/core/sdk.js +60 -38
  131. package/dist/core/sdk.js.map +1 -1
  132. package/dist/core/session-export.d.ts +6 -2
  133. package/dist/core/session-export.d.ts.map +1 -1
  134. package/dist/core/session-export.js +14 -13
  135. package/dist/core/session-export.js.map +1 -1
  136. package/dist/core/session-manager.d.ts +29 -6
  137. package/dist/core/session-manager.d.ts.map +1 -1
  138. package/dist/core/session-manager.js +159 -96
  139. package/dist/core/session-manager.js.map +1 -1
  140. package/dist/core/settings-manager.d.ts +20 -4
  141. package/dist/core/settings-manager.d.ts.map +1 -1
  142. package/dist/core/settings-manager.js +43 -7
  143. package/dist/core/settings-manager.js.map +1 -1
  144. package/dist/core/slash-commands.d.ts.map +1 -1
  145. package/dist/core/slash-commands.js +1 -0
  146. package/dist/core/slash-commands.js.map +1 -1
  147. package/dist/core/system-prompt.d.ts +49 -6
  148. package/dist/core/system-prompt.d.ts.map +1 -1
  149. package/dist/core/system-prompt.js +121 -92
  150. package/dist/core/system-prompt.js.map +1 -1
  151. package/dist/core/tools/bash.d.ts +2 -1
  152. package/dist/core/tools/bash.d.ts.map +1 -1
  153. package/dist/core/tools/bash.js +10 -4
  154. package/dist/core/tools/bash.js.map +1 -1
  155. package/dist/core/tools/edit.d.ts.map +1 -1
  156. package/dist/core/tools/edit.js +1 -2
  157. package/dist/core/tools/edit.js.map +1 -1
  158. package/dist/core/tools/read.d.ts.map +1 -1
  159. package/dist/core/tools/read.js +1 -2
  160. package/dist/core/tools/read.js.map +1 -1
  161. package/dist/core/tools/renderers/bash.d.ts.map +1 -1
  162. package/dist/core/tools/renderers/bash.js +9 -1
  163. package/dist/core/tools/renderers/bash.js.map +1 -1
  164. package/dist/core/tools/write.d.ts.map +1 -1
  165. package/dist/core/tools/write.js +1 -2
  166. package/dist/core/tools/write.js.map +1 -1
  167. package/dist/core/usage-totals.d.ts +1 -1
  168. package/dist/core/usage-totals.d.ts.map +1 -1
  169. package/dist/core/usage-totals.js +5 -1
  170. package/dist/core/usage-totals.js.map +1 -1
  171. package/dist/extensions/llama/client.d.ts +2 -0
  172. package/dist/extensions/llama/client.d.ts.map +1 -1
  173. package/dist/extensions/llama/client.js +7 -3
  174. package/dist/extensions/llama/client.js.map +1 -1
  175. package/dist/extensions/llama/provider.d.ts.map +1 -1
  176. package/dist/extensions/llama/provider.js +20 -5
  177. package/dist/extensions/llama/provider.js.map +1 -1
  178. package/dist/index.d.ts +3 -2
  179. package/dist/index.d.ts.map +1 -1
  180. package/dist/index.js.map +1 -1
  181. package/dist/main.d.ts.map +1 -1
  182. package/dist/main.js +13 -9
  183. package/dist/main.js.map +1 -1
  184. package/dist/migrations.d.ts.map +1 -1
  185. package/dist/migrations.js +2 -2
  186. package/dist/migrations.js.map +1 -1
  187. package/dist/modes/interactive/bug-report.d.ts +14 -0
  188. package/dist/modes/interactive/bug-report.d.ts.map +1 -0
  189. package/dist/modes/interactive/bug-report.js +209 -0
  190. package/dist/modes/interactive/bug-report.js.map +1 -0
  191. package/dist/modes/interactive/chat-viewport.d.ts.map +1 -1
  192. package/dist/modes/interactive/chat-viewport.js +1 -1
  193. package/dist/modes/interactive/chat-viewport.js.map +1 -1
  194. package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -1
  195. package/dist/modes/interactive/components/branch-summary-message.js +12 -5
  196. package/dist/modes/interactive/components/branch-summary-message.js.map +1 -1
  197. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  198. package/dist/modes/interactive/components/compaction-summary-message.js +12 -5
  199. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  200. package/dist/modes/interactive/components/custom-editor.d.ts +3 -3
  201. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  202. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  203. package/dist/modes/interactive/components/extension-input.d.ts +2 -0
  204. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  205. package/dist/modes/interactive/components/extension-input.js +6 -0
  206. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  207. package/dist/modes/interactive/components/extension-selector.d.ts +1 -0
  208. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  209. package/dist/modes/interactive/components/extension-selector.js +4 -0
  210. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  211. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  212. package/dist/modes/interactive/components/footer.js +4 -1
  213. package/dist/modes/interactive/components/footer.js.map +1 -1
  214. package/dist/modes/interactive/components/session-selector.d.ts +5 -5
  215. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  216. package/dist/modes/interactive/components/session-selector.js +74 -48
  217. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  218. package/dist/modes/interactive/components/settings-selector.d.ts +3 -1
  219. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  220. package/dist/modes/interactive/components/settings-selector.js +11 -0
  221. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  222. package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -1
  223. package/dist/modes/interactive/components/skill-invocation-message.js +11 -4
  224. package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -1
  225. package/dist/modes/interactive/components/status-indicator.d.ts +2 -2
  226. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  227. package/dist/modes/interactive/components/status-indicator.js +7 -7
  228. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  229. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  230. package/dist/modes/interactive/components/tool-execution.js +18 -9
  231. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  232. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  233. package/dist/modes/interactive/components/tree-selector.js +2 -0
  234. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  235. package/dist/modes/interactive/interactive-mode.d.ts +13 -1
  236. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  237. package/dist/modes/interactive/interactive-mode.js +220 -84
  238. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  239. package/dist/modes/interactive/session-share.d.ts +2 -0
  240. package/dist/modes/interactive/session-share.d.ts.map +1 -1
  241. package/dist/modes/interactive/session-share.js +8 -4
  242. package/dist/modes/interactive/session-share.js.map +1 -1
  243. package/dist/modes/interactive/tui-renderer.d.ts.map +1 -1
  244. package/dist/modes/interactive/tui-renderer.js +2 -2
  245. package/dist/modes/interactive/tui-renderer.js.map +1 -1
  246. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  247. package/dist/modes/rpc/rpc-mode.js +2 -2
  248. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  249. package/dist/utils/clipboard-command.d.ts +7 -0
  250. package/dist/utils/clipboard-command.d.ts.map +1 -0
  251. package/dist/utils/clipboard-command.js +45 -0
  252. package/dist/utils/clipboard-command.js.map +1 -0
  253. package/dist/utils/clipboard-image.d.ts.map +1 -1
  254. package/dist/utils/clipboard-image.js +52 -69
  255. package/dist/utils/clipboard-image.js.map +1 -1
  256. package/dist/utils/clipboard.d.ts.map +1 -1
  257. package/dist/utils/clipboard.js +61 -125
  258. package/dist/utils/clipboard.js.map +1 -1
  259. package/dist/utils/version-check.d.ts.map +1 -1
  260. package/dist/utils/version-check.js +11 -4
  261. package/dist/utils/version-check.js.map +1 -1
  262. package/dist/utils/zip.d.ts +7 -0
  263. package/dist/utils/zip.d.ts.map +1 -0
  264. package/dist/utils/zip.js +60 -0
  265. package/dist/utils/zip.js.map +1 -0
  266. package/docs/compaction.md +39 -13
  267. package/docs/custom-provider.md +22 -13
  268. package/docs/development.md +4 -4
  269. package/docs/docs.json +4 -0
  270. package/docs/environment-variables.md +1 -0
  271. package/docs/extensions.md +57 -43
  272. package/docs/json.md +4 -4
  273. package/docs/models.md +33 -2
  274. package/docs/providers.md +2 -2
  275. package/docs/sdk.md +4 -2
  276. package/docs/security.md +1 -1
  277. package/docs/session-format.md +82 -40
  278. package/docs/sessions.md +27 -0
  279. package/docs/settings.md +60 -1
  280. package/docs/termux.md +0 -1
  281. package/docs/tui.md +2 -2
  282. package/docs/usage.md +1 -0
  283. package/examples/extensions/README.md +0 -1
  284. package/examples/extensions/custom-provider-anthropic/index.ts +18 -12
  285. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  286. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  287. package/examples/extensions/custom-provider-gitlab-duo/index.ts +2 -2
  288. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  289. package/examples/extensions/gondolin/package-lock.json +2 -2
  290. package/examples/extensions/gondolin/package.json +1 -1
  291. package/examples/extensions/prompt-customizer.ts +19 -67
  292. package/examples/extensions/sandbox/package-lock.json +2 -2
  293. package/examples/extensions/sandbox/package.json +1 -1
  294. package/examples/extensions/with-deps/package-lock.json +2 -2
  295. package/examples/extensions/with-deps/package.json +1 -1
  296. package/npm-shrinkwrap.json +435 -762
  297. package/package.json +17 -23
  298. package/dist/bundle/chunks/anthropic-messages-VWZZOSJQ.js +0 -6
  299. package/dist/bundle/chunks/azure-openai-responses-BJLAM6CD.js +0 -2
  300. package/dist/bundle/chunks/chunk-AXIIZGTV.js +0 -2
  301. package/dist/bundle/chunks/chunk-CLNPYIDP.js +0 -42
  302. package/dist/bundle/chunks/chunk-GMWCTUPB.js +0 -2
  303. package/dist/bundle/chunks/chunk-JJ2YFCJF.js +0 -1547
  304. package/dist/bundle/chunks/chunk-OUXLLA64.js +0 -11
  305. package/dist/bundle/chunks/chunk-U6ZMQKGD.js +0 -50
  306. package/dist/bundle/chunks/chunk-UAQELI3K.js +0 -2
  307. package/dist/bundle/chunks/google-generative-ai-Q2K7FBDI.js +0 -2
  308. package/dist/bundle/chunks/google-vertex-7UJBF4S7.js +0 -2
  309. package/dist/bundle/chunks/mistral-conversations-4M6BZBX6.js +0 -5
  310. package/dist/bundle/chunks/openai-codex-responses-R6VYZTVY.js +0 -10
  311. package/dist/bundle/chunks/openai-completions-EKZT2IH2.js +0 -7
  312. package/dist/bundle/chunks/openai-responses-TFDINO6W.js +0 -2
  313. package/dist/utils/clipboard-native.d.ts +0 -11
  314. package/dist/utils/clipboard-native.d.ts.map +0 -1
  315. package/dist/utils/clipboard-native.js +0 -20
  316. package/dist/utils/clipboard-native.js.map +0 -1
  317. package/examples/extensions/kimi-deferred-tools.ts +0 -61
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { readFileSync } from "node:fs";
16
16
  import { basename, dirname } from "node:path";
17
- import { contentText } from "@earendil-works/pi-ai";
17
+ import { contentText, getCurrentSystemMessage, retryDelayMs } from "@earendil-works/pi-ai";
18
18
  import { clampThinkingLevel, cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } 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";
@@ -22,6 +22,7 @@ import { sleep } from "../utils/sleep.js";
22
22
  import { normalizeToolResultImages } from "../utils/tool-result-images.js";
23
23
  import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
24
24
  import { executeBashWithOperations } from "./bash-executor.js";
25
+ import { generateBugReportSummary } from "./bug-report.js";
25
26
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
26
27
  import { DEFAULT_THINKING_LEVEL, THINKING_LEVEL_OPTIONS } from "./defaults.js";
27
28
  import { exportSessionToHtml } from "./export-html/index.js";
@@ -34,7 +35,7 @@ import { exportSessionToJsonl } from "./session-export.js";
34
35
  import { getLatestCompactionEntry } from "./session-manager.js";
35
36
  import { findSkillByInvocationName, resolveBareNamespaceSkill } from "./skills.js";
36
37
  import { createSyntheticSourceInfo } from "./source-info.js";
37
- import { buildSystemPrompt } from "./system-prompt.js";
38
+ import { buildSystemPrompt, buildSystemPromptSections, diffSystemPromptSections, normalizeBuildSystemPromptOptions, } from "./system-prompt.js";
38
39
  import { createLocalBashOperations } from "./tools/bash.js";
39
40
  import { createAllToolDefinitions } from "./tools/index.js";
40
41
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
@@ -70,9 +71,6 @@ function estimateMessagesTokens(messages) {
70
71
  return tokens;
71
72
  }
72
73
  // ============================================================================
73
- // Constants
74
- // ============================================================================
75
- // ============================================================================
76
74
  // AgentSession Class
77
75
  // ============================================================================
78
76
  export class AgentSession {
@@ -84,6 +82,7 @@ export class AgentSession {
84
82
  _unsubscribeAgent;
85
83
  _eventListeners = [];
86
84
  _isAgentRunActive = false;
85
+ _agentRunAbortRequested = false;
87
86
  _idleWaitPromise;
88
87
  _resolveIdleWait;
89
88
  /** Tracks pending steering messages for UI display. Removed when delivered. */
@@ -127,15 +126,15 @@ export class AgentSession {
127
126
  _extensionErrorListener;
128
127
  _extensionErrorUnsubscriber;
129
128
  _modelRuntime;
129
+ _cacheWarmer;
130
130
  // Tool registry for extension getTools/setTools
131
131
  _toolRegistry = new Map();
132
132
  _toolDefinitions = new Map();
133
133
  _toolPromptSnippets = new Map();
134
134
  _toolPromptGuidelines = new Map();
135
- // Base system prompt (without extension appends) - used to apply fresh appends each turn
136
- _baseSystemPrompt = "";
137
135
  _baseSystemPromptOptions;
138
- _systemPromptOverride;
136
+ /** Prompt options after before_agent_start mutations for the active run. */
137
+ _runSystemPromptOptions;
139
138
  constructor(config) {
140
139
  this.agent = config.agent;
141
140
  this.sessionManager = config.sessionManager;
@@ -145,6 +144,10 @@ export class AgentSession {
145
144
  this._customTools = config.customTools ?? [];
146
145
  this._cwd = config.cwd;
147
146
  this._modelRuntime = config.modelRuntime;
147
+ this._cacheWarmer = config.cacheWarmer;
148
+ if (this._cacheWarmer) {
149
+ this._cacheWarmer.onWarmed = (entry) => this._emit({ type: "entry_appended", entry });
150
+ }
148
151
  this._extensionRunnerRef = config.extensionRunnerRef;
149
152
  this._initialActiveToolNames = config.initialActiveToolNames;
150
153
  this._allowedToolNames = config.allowedToolNames ? new Set(config.allowedToolNames) : undefined;
@@ -156,18 +159,21 @@ export class AgentSession {
156
159
  this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
157
160
  this._installAgentToolHooks();
158
161
  this._installAgentNextTurnRefresh();
162
+ this._installAgentForcedPromptProjection();
159
163
  this._buildRuntime({
160
164
  activeToolNames: this._initialActiveToolNames,
161
165
  includeAllExtensionTools: true,
162
166
  });
167
+ if (this._initialActiveToolNames === undefined)
168
+ this._restoreToolsFromTranscript();
163
169
  }
164
170
  get modelRuntime() {
165
171
  return this._modelRuntime;
166
172
  }
167
- async _getRequiredRequestAuth(model) {
173
+ async _getRequiredRequestAuth(model, signal) {
168
174
  let result;
169
175
  try {
170
- result = await this._modelRuntime.getAuth(model);
176
+ result = await this._modelRuntime.getAuth(model, { signal });
171
177
  }
172
178
  catch (error) {
173
179
  const cause = error instanceof Error ? error.cause : undefined;
@@ -193,12 +199,12 @@ export class AgentSession {
193
199
  }
194
200
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
195
201
  }
196
- async _getSummarizationRequestAuth(model) {
202
+ async _getSummarizationRequestAuth(model, signal) {
197
203
  if (this.agent.streamFunction === streamSimple) {
198
- return this._getRequiredRequestAuth(model);
204
+ return this._getRequiredRequestAuth(model, signal);
199
205
  }
200
206
  try {
201
- const result = await this._modelRuntime.getAuth(model);
207
+ const result = await this._modelRuntime.getAuth(model, { signal });
202
208
  if (!result)
203
209
  return { model };
204
210
  const requestModel = result.auth.baseUrl ? { ...model, baseUrl: result.auth.baseUrl } : model;
@@ -209,7 +215,9 @@ export class AgentSession {
209
215
  env: result.env,
210
216
  };
211
217
  }
212
- catch {
218
+ catch (error) {
219
+ if (signal?.aborted)
220
+ throw error;
213
221
  return { model };
214
222
  }
215
223
  }
@@ -274,7 +282,7 @@ export class AgentSession {
274
282
  }
275
283
  async _compactBeforeNextAssistantResponse(context) {
276
284
  const model = this.model;
277
- const settings = this.settingsManager.getCompactionSettings();
285
+ const settings = this.settingsManager.getCompactionSettings(model);
278
286
  if (!model ||
279
287
  model.contextWindow <= 0 ||
280
288
  !shouldCompact(estimateContextTokens(context.messages).tokens, model.contextWindow, settings)) {
@@ -295,13 +303,25 @@ export class AgentSession {
295
303
  const context = await this._compactBeforeNextAssistantResponse(turn.context);
296
304
  const previousSnapshot = await previousPrepareNextTurnWithContext?.({ ...turn, context }, signal);
297
305
  const nextContext = previousSnapshot?.context ?? context;
306
+ const runOptions = this._runSystemPromptOptions ?? this._baseSystemPromptOptions;
307
+ const options = normalizeBuildSystemPromptOptions({
308
+ ...runOptions,
309
+ selectedTools: this.getActiveToolNames(),
310
+ toolSnippets: { ...this._baseSystemPromptOptions.toolSnippets, ...runOptions.toolSnippets },
311
+ toolGuidelines: { ...this._baseSystemPromptOptions.toolGuidelines, ...runOptions.toolGuidelines },
312
+ });
313
+ const updateMessage = this._preparePromptAndToolLoadout(options, nextContext.messages);
314
+ // Keep session.systemPrompt and ctx.getSystemPrompt() in step with what the provider sees.
315
+ this._runSystemPromptOptions = options;
298
316
  return {
299
317
  ...previousSnapshot,
300
318
  context: {
301
319
  ...nextContext,
302
- systemPrompt: this._systemPromptOverride ?? this._baseSystemPrompt,
303
320
  tools: this.agent.state.tools.slice(),
304
321
  },
322
+ messages: updateMessage
323
+ ? [...(previousSnapshot?.messages ?? []), updateMessage]
324
+ : previousSnapshot?.messages,
305
325
  model: this.agent.state.model,
306
326
  thinkingLevel: this.agent.state.thinkingLevel,
307
327
  };
@@ -346,6 +366,7 @@ export class AgentSession {
346
366
  resolve();
347
367
  }
348
368
  async _emitAgentSettled() {
369
+ this._cacheWarmer?.onAgentSettled();
349
370
  this._isAgentRunActive = false;
350
371
  try {
351
372
  await this._extensionRunner.emit({ type: "agent_settled" });
@@ -392,7 +413,8 @@ export class AgentSession {
392
413
  // Persist as CustomMessageEntry
393
414
  this.sessionManager.appendCustomMessageEntry(event.message.customType, event.message.content, event.message.display, event.message.details);
394
415
  }
395
- else if (event.message.role === "user" ||
416
+ else if (event.message.role === "system" ||
417
+ event.message.role === "user" ||
396
418
  event.message.role === "assistant" ||
397
419
  event.message.role === "toolResult") {
398
420
  // Regular LLM message - persist as SessionMessageEntry
@@ -428,6 +450,8 @@ export class AgentSession {
428
450
  }
429
451
  };
430
452
  _willRetryAfterAgentEnd(event) {
453
+ if (this._agentRunAbortRequested)
454
+ return false;
431
455
  const settings = this.settingsManager.getRetrySettings();
432
456
  if (!settings.enabled || this._retryAttempt >= settings.maxRetries) {
433
457
  return false;
@@ -596,6 +620,10 @@ export class AgentSession {
596
620
  this._extensionRunner.invalidate("This extension ctx is stale after session replacement or reload. Do not use a captured pi or command ctx after ctx.newSession(), ctx.fork(), ctx.switchSession(), or ctx.reload(). For newSession, fork, and switchSession, move post-replacement work into withSession and use the ctx passed to withSession. For reload, do not use the old ctx after await ctx.reload().");
597
621
  this._disconnectFromAgent();
598
622
  this._eventListeners = [];
623
+ if (this._cacheWarmer) {
624
+ this._cacheWarmer.onWarmed = undefined;
625
+ this._cacheWarmer.cancel();
626
+ }
599
627
  cleanupSessionResources(this.sessionId);
600
628
  }
601
629
  // =========================================================================
@@ -605,6 +633,15 @@ export class AgentSession {
605
633
  get state() {
606
634
  return this.agent.state;
607
635
  }
636
+ /** Current cache-warming state and the policy inputs that produced it. */
637
+ get cacheWarmingStatus() {
638
+ return this._cacheWarmer?.status;
639
+ }
640
+ /** Persist the cache-warming mode and immediately reconcile active warming. */
641
+ setCacheWarmingMode(mode) {
642
+ this.settingsManager.setCacheWarmingMode(mode);
643
+ this._cacheWarmer?.onModeChanged();
644
+ }
608
645
  /** Current model (may be undefined if not yet selected) */
609
646
  get model() {
610
647
  return this.agent.state.model;
@@ -621,9 +658,9 @@ export class AgentSession {
621
658
  get isIdle() {
622
659
  return !this._isAgentRunActive && !this.isCompacting;
623
660
  }
624
- /** Current effective system prompt (includes any per-turn extension modifications) */
661
+ /** Current effective system prompt, including changes not yet sent to the model. */
625
662
  get systemPrompt() {
626
- return this.agent.state.systemPrompt;
663
+ return buildSystemPrompt(this._runSystemPromptOptions ?? this._baseSystemPromptOptions);
627
664
  }
628
665
  /** Current retry attempt (0 if not retrying) */
629
666
  get retryAttempt() {
@@ -668,9 +705,7 @@ export class AgentSession {
668
705
  }
669
706
  }
670
707
  this.agent.state.tools = tools;
671
- // Rebuild base system prompt with new tool set
672
- this._baseSystemPrompt = this._rebuildSystemPrompt(validToolNames);
673
- this.agent.state.systemPrompt = this._systemPromptOverride ?? this._baseSystemPrompt;
708
+ this._rebuildSystemPrompt(validToolNames);
674
709
  }
675
710
  /** Whether compaction or branch summarization is currently running */
676
711
  get isCompacting() {
@@ -739,23 +774,17 @@ export class AgentSession {
739
774
  _rebuildSystemPrompt(toolNames) {
740
775
  const validToolNames = toolNames.filter((name) => this._toolRegistry.has(name));
741
776
  const toolSnippets = {};
742
- const promptGuidelines = [];
743
- for (const name of validToolNames) {
777
+ for (const name of this._toolRegistry.keys()) {
744
778
  const snippet = this._toolPromptSnippets.get(name);
745
- if (snippet) {
779
+ if (snippet)
746
780
  toolSnippets[name] = snippet;
747
- }
748
- const toolGuidelines = this._toolPromptGuidelines.get(name);
749
- if (toolGuidelines) {
750
- promptGuidelines.push(...toolGuidelines);
751
- }
752
781
  }
753
782
  const loaderSystemPrompt = this._resourceLoader.getSystemPrompt();
754
783
  const loaderAppendSystemPrompt = this._resourceLoader.getAppendSystemPrompt();
755
- const appendSystemPrompt = loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : undefined;
784
+ const appendSystemPrompt = loaderAppendSystemPrompt.length > 0 ? loaderAppendSystemPrompt.join("\n\n") : "";
756
785
  const loadedSkills = this._resourceLoader.getSkills().skills;
757
786
  const loadedContextFiles = this._resourceLoader.getAgentsFiles().agentsFiles;
758
- this._baseSystemPromptOptions = {
787
+ this._baseSystemPromptOptions = normalizeBuildSystemPromptOptions({
759
788
  cwd: this._cwd,
760
789
  skills: loadedSkills,
761
790
  contextFiles: loadedContextFiles,
@@ -763,23 +792,87 @@ export class AgentSession {
763
792
  appendSystemPrompt,
764
793
  selectedTools: validToolNames,
765
794
  toolSnippets,
766
- promptGuidelines,
795
+ toolGuidelines: Object.fromEntries(this._toolPromptGuidelines),
796
+ });
797
+ }
798
+ /**
799
+ * Apply a prompt and tool loadout for the next request. Sets the executable tools and
800
+ * returns a system message patching the prompt sections the model currently has (replayed
801
+ * from `messages`), or undefined when the prompt is unchanged. Tool changes are declared by
802
+ * the agent loop before the request.
803
+ *
804
+ * A forced prompt does not affect the transcript: the structured sections are still diffed
805
+ * and persisted, and the forced text is projected onto the request by
806
+ * {@link _installAgentForcedPromptProjection}.
807
+ */
808
+ _preparePromptAndToolLoadout(options, messages = this.agent.state.messages) {
809
+ options.selectedTools = [...new Set(options.selectedTools)].filter((name) => this._toolRegistry.has(name));
810
+ this.agent.state.tools = options.selectedTools.flatMap((name) => {
811
+ const tool = this._toolRegistry.get(name);
812
+ return tool ? [tool] : [];
813
+ });
814
+ const sections = diffSystemPromptSections(getCurrentSystemMessage(messages)?.sections ?? {}, buildSystemPromptSections(options));
815
+ return sections ? { role: "system", content: "", sections, timestamp: Date.now() } : undefined;
816
+ }
817
+ /**
818
+ * Send a forced prompt as the provider's leading system prompt without recording it.
819
+ *
820
+ * A `before_agent_start` handler that returns `systemPrompt` needs that exact text at the
821
+ * head of the request; a mid-conversation system message would leave the original prompt
822
+ * in place. The forced text is a rendering of the current prompt, so the transcript keeps
823
+ * its structured sections and the request is projected instead: the system messages
824
+ * collapse into one head holding the forced text and the current tools. Runs after the
825
+ * `context` extension handlers.
826
+ */
827
+ _installAgentForcedPromptProjection() {
828
+ const previousTransformContext = this.agent.transformContext;
829
+ this.agent.transformContext = async (messages, signal) => {
830
+ const transformed = previousTransformContext ? await previousTransformContext(messages, signal) : messages;
831
+ const forced = this._runSystemPromptOptions?.forceSystemPrompt;
832
+ if (forced === undefined)
833
+ return transformed;
834
+ const current = getCurrentSystemMessage(transformed);
835
+ const head = {
836
+ role: "system",
837
+ content: forced,
838
+ ...(current?.toolsAdded ? { toolsAdded: current.toolsAdded } : {}),
839
+ timestamp: current?.timestamp ?? Date.now(),
840
+ };
841
+ return [head, ...transformed.filter((message) => message.role !== "system")];
767
842
  };
768
- return buildSystemPrompt(this._baseSystemPromptOptions);
843
+ }
844
+ /** Restore the active tool loadout declared by the session transcript, if it declares one. */
845
+ _restoreToolsFromTranscript() {
846
+ const current = getCurrentSystemMessage(this.sessionManager.buildSessionContext().messages);
847
+ if (!current)
848
+ return;
849
+ const toolNames = (current.toolsAdded ?? [])
850
+ .map((tool) => tool.name)
851
+ .filter((name) => this._toolRegistry.has(name));
852
+ this.agent.state.tools = toolNames.flatMap((name) => {
853
+ const registered = this._toolRegistry.get(name);
854
+ return registered ? [registered] : [];
855
+ });
856
+ this._rebuildSystemPrompt(toolNames);
769
857
  }
770
858
  // =========================================================================
771
859
  // Prompting
772
860
  // =========================================================================
773
861
  async _runAgentPrompt(messages) {
862
+ this._agentRunAbortRequested = false;
774
863
  this._isAgentRunActive = true;
775
864
  try {
776
865
  await this.agent.prompt(messages);
777
866
  while (await this._handlePostAgentRun()) {
867
+ if (this._agentRunAbortRequested)
868
+ break;
778
869
  await this.agent.continue();
779
870
  }
780
871
  }
781
872
  finally {
782
- this._systemPromptOverride = undefined;
873
+ if (this._agentRunAbortRequested)
874
+ this._finishCancelledRetry();
875
+ this._runSystemPromptOptions = undefined;
783
876
  this._flushPendingBashMessages();
784
877
  this._flushPendingCustomMessages();
785
878
  await this._emitAgentSettled();
@@ -788,11 +881,21 @@ export class AgentSession {
788
881
  async _handlePostAgentRun() {
789
882
  const msg = this._lastAssistantMessage;
790
883
  this._lastAssistantMessage = undefined;
884
+ if (this._agentRunAbortRequested) {
885
+ this._finishCancelledRetry();
886
+ return false;
887
+ }
791
888
  if (!msg) {
792
889
  return false;
793
890
  }
794
891
  if (this._isRetryableError(msg) && (await this._prepareRetry(msg))) {
795
- return true;
892
+ if (this._agentRunAbortRequested)
893
+ this._finishCancelledRetry();
894
+ return !this._agentRunAbortRequested;
895
+ }
896
+ if (this._agentRunAbortRequested) {
897
+ this._finishCancelledRetry();
898
+ return false;
796
899
  }
797
900
  if (msg.stopReason === "error" && this._retryAttempt > 0) {
798
901
  this._emit({
@@ -804,11 +907,24 @@ export class AgentSession {
804
907
  this._retryAttempt = 0;
805
908
  }
806
909
  if (await this._checkCompaction(msg)) {
807
- return true;
910
+ return !this._agentRunAbortRequested;
808
911
  }
809
912
  // The agent loop drains both queues before emitting agent_end. Any messages
810
913
  // here were queued by agent_end extension handlers and need a continuation.
811
- return this.agent.hasQueuedMessages();
914
+ return !this._agentRunAbortRequested && this.agent.hasQueuedMessages();
915
+ }
916
+ async _runInputHandlers(text, images, source, streamingBehavior) {
917
+ if (!this._extensionRunner.hasHandlers("input")) {
918
+ return { text, images };
919
+ }
920
+ const inputResult = await this._extensionRunner.emitInput(text, images, source, streamingBehavior);
921
+ if (inputResult.action === "handled") {
922
+ return undefined;
923
+ }
924
+ if (inputResult.action === "transform") {
925
+ return { text: inputResult.text, images: inputResult.images ?? images };
926
+ }
927
+ return { text, images };
812
928
  }
813
929
  /**
814
930
  * Send a prompt to the agent.
@@ -838,19 +954,12 @@ export class AgentSession {
838
954
  throw new Error("Cannot submit a prompt while compaction is in progress. Wait for compaction to finish and retry.");
839
955
  }
840
956
  // Emit input event for extension interception (before skill/template expansion)
841
- let currentText = text;
842
- let currentImages = options?.images;
843
- if (this._extensionRunner.hasHandlers("input")) {
844
- const inputResult = await this._extensionRunner.emitInput(currentText, currentImages, options?.source ?? "interactive", this.isStreaming ? options?.streamingBehavior : undefined);
845
- if (inputResult.action === "handled") {
846
- preflightResult?.(true);
847
- return;
848
- }
849
- if (inputResult.action === "transform") {
850
- currentText = inputResult.text;
851
- currentImages = inputResult.images ?? currentImages;
852
- }
957
+ const processedInput = await this._runInputHandlers(text, options?.images, options?.source ?? "interactive", this.isStreaming ? options?.streamingBehavior : undefined);
958
+ if (!processedInput) {
959
+ preflightResult?.(true);
960
+ return;
853
961
  }
962
+ const { text: currentText, images: currentImages } = processedInput;
854
963
  // Expand skill commands (/skill:name args) and prompt templates (/template args)
855
964
  let expandedText = currentText;
856
965
  if (expandPromptTemplates) {
@@ -914,31 +1023,30 @@ export class AgentSession {
914
1023
  }
915
1024
  this._pendingNextTurnMessages = [];
916
1025
  // Emit before_agent_start extension event
917
- const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPrompt, this._baseSystemPromptOptions);
918
- // Add all custom messages from extensions
919
- if (result?.messages) {
920
- for (const msg of result.messages) {
921
- messages.push({
922
- role: "custom",
923
- customType: msg.customType,
924
- // Untyped extensions can pass null/missing content; normalize at ingestion.
925
- content: msg.content ?? [],
926
- display: msg.display,
927
- details: msg.details,
928
- timestamp: Date.now(),
929
- });
930
- }
931
- }
932
- // Apply extension-modified system prompt, or reset to base
933
- if (result?.systemPrompt !== undefined) {
934
- this._systemPromptOverride = result.systemPrompt;
935
- this.agent.state.systemPrompt = result.systemPrompt;
936
- }
937
- else {
938
- // Ensure we're using the base prompt (in case previous turn had modifications)
939
- this._systemPromptOverride = undefined;
940
- this.agent.state.systemPrompt = this._baseSystemPrompt;
1026
+ const selectedToolsBefore = this._baseSystemPromptOptions.selectedTools;
1027
+ const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPromptOptions);
1028
+ // Handlers may edit event.systemPromptOptions.selectedTools or call setActiveTools(),
1029
+ // which updates the live loadout instead. An explicit edit wins; otherwise the live
1030
+ // loadout is authoritative, so a setActiveTools() call is not undone here.
1031
+ const handlerEditedTools = result.systemPromptOptions.selectedTools.length !== selectedToolsBefore.length ||
1032
+ result.systemPromptOptions.selectedTools.some((name, index) => name !== selectedToolsBefore[index]);
1033
+ if (!handlerEditedTools)
1034
+ result.systemPromptOptions.selectedTools = this.getActiveToolNames();
1035
+ for (const msg of result.messages) {
1036
+ messages.push({
1037
+ role: "custom",
1038
+ customType: msg.customType,
1039
+ // Untyped extensions can pass null/missing content; normalize at ingestion.
1040
+ content: msg.content ?? [],
1041
+ display: msg.display,
1042
+ details: msg.details,
1043
+ timestamp: Date.now(),
1044
+ });
941
1045
  }
1046
+ const updateMessage = this._preparePromptAndToolLoadout(result.systemPromptOptions);
1047
+ this._runSystemPromptOptions = result.systemPromptOptions;
1048
+ if (updateMessage)
1049
+ messages.unshift(updateMessage);
942
1050
  }
943
1051
  catch (error) {
944
1052
  preflightResult?.(false);
@@ -1027,42 +1135,45 @@ export class AgentSession {
1027
1135
  return text; // Return original on error
1028
1136
  }
1029
1137
  }
1138
+ async _queueUserInput(text, images, behavior, source) {
1139
+ if (text.startsWith("/")) {
1140
+ this._throwIfExtensionCommand(text);
1141
+ }
1142
+ const processedInput = await this._runInputHandlers(text, images, source, this.isStreaming ? behavior : undefined);
1143
+ if (!processedInput)
1144
+ return;
1145
+ let expandedText = this._expandSkillCommand(processedInput.text);
1146
+ expandedText = this._expandBareNamespaceSkillCommand(expandedText);
1147
+ expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
1148
+ if (behavior === "steer") {
1149
+ await this._queueSteer(expandedText, processedInput.images);
1150
+ }
1151
+ else {
1152
+ await this._queueFollowUp(expandedText, processedInput.images);
1153
+ }
1154
+ }
1030
1155
  /**
1031
1156
  * Queue a steering message while the agent is running.
1032
1157
  * Delivered after the current assistant turn finishes executing its tool calls,
1033
1158
  * before the next LLM call.
1034
1159
  * Expands skill commands and prompt templates. Errors on extension commands.
1035
1160
  * @param images Optional image attachments to include with the message
1161
+ * @param options Input source; defaults to interactive
1036
1162
  * @throws Error if text is an extension command
1037
1163
  */
1038
- async steer(text, images) {
1039
- // Check for extension commands (cannot be queued)
1040
- if (text.startsWith("/")) {
1041
- this._throwIfExtensionCommand(text);
1042
- }
1043
- // Expand skill commands and prompt templates
1044
- let expandedText = this._expandSkillCommand(text);
1045
- expandedText = this._expandBareNamespaceSkillCommand(expandedText);
1046
- expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
1047
- await this._queueSteer(expandedText, images);
1164
+ async steer(text, images, options) {
1165
+ await this._queueUserInput(text, images, "steer", options?.source ?? "interactive");
1048
1166
  }
1049
1167
  /**
1050
1168
  * Queue a follow-up message to be processed after the agent finishes.
1051
1169
  * Delivered only when agent has no more tool calls or steering messages.
1052
1170
  * Expands skill commands and prompt templates. Errors on extension commands.
1053
1171
  * @param images Optional image attachments to include with the message
1172
+ * @param options Input source; defaults to interactive
1054
1173
  * @throws Error if text is an extension command
1055
1174
  */
1056
- async followUp(text, images) {
1057
- // Check for extension commands (cannot be queued)
1058
- if (text.startsWith("/")) {
1059
- this._throwIfExtensionCommand(text);
1060
- }
1061
- // Expand skill commands and prompt templates
1062
- let expandedText = this._expandSkillCommand(text);
1063
- expandedText = this._expandBareNamespaceSkillCommand(expandedText);
1064
- expandedText = expandPromptTemplate(expandedText, [...this.promptTemplates]);
1065
- await this._queueFollowUp(expandedText, images);
1175
+ async followUp(text, images, options) {
1176
+ await this._queueUserInput(text, images, "followUp", options?.source ?? "interactive");
1066
1177
  }
1067
1178
  /**
1068
1179
  * Internal: Queue a steering message (already expanded, no extension command check).
@@ -1244,6 +1355,9 @@ export class AgentSession {
1244
1355
  * Abort current operation and wait for agent to become idle.
1245
1356
  */
1246
1357
  async abort() {
1358
+ if (this._isAgentRunActive) {
1359
+ this._agentRunAbortRequested = true;
1360
+ }
1247
1361
  this.abortRetry();
1248
1362
  this.abortCompaction();
1249
1363
  this.abortBranchSummary();
@@ -1500,13 +1614,15 @@ export class AgentSession {
1500
1614
  this._compactionAbortController = new AbortController();
1501
1615
  this._emit({ type: "compaction_start", reason: "manual" });
1502
1616
  let fromExtension = false;
1617
+ let cancelledByExtension = false;
1503
1618
  try {
1504
- if (!this.model) {
1619
+ const model = this.model;
1620
+ if (!model) {
1505
1621
  throw new Error(formatNoModelSelectedMessage());
1506
1622
  }
1507
- const { model: requestModel, apiKey, headers, env } = await this._getSummarizationRequestAuth(this.model);
1623
+ const settings = this.settingsManager.getCompactionSettings(model);
1624
+ const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, this._compactionAbortController.signal);
1508
1625
  const pathEntries = this.sessionManager.getBranch();
1509
- const settings = this.settingsManager.getCompactionSettings();
1510
1626
  const preparation = prepareCompaction(pathEntries, settings);
1511
1627
  if (!preparation) {
1512
1628
  // Check why we can't compact
@@ -1528,6 +1644,7 @@ export class AgentSession {
1528
1644
  signal: this._compactionAbortController.signal,
1529
1645
  }));
1530
1646
  if (result?.cancel) {
1647
+ cancelledByExtension = true;
1531
1648
  throw new Error("Compaction cancelled");
1532
1649
  }
1533
1650
  if (result?.compaction) {
@@ -1597,7 +1714,7 @@ export class AgentSession {
1597
1714
  }
1598
1715
  catch (error) {
1599
1716
  const message = error instanceof Error ? error.message : String(error);
1600
- const aborted = message === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
1717
+ const aborted = this._compactionAbortController.signal.aborted || cancelledByExtension;
1601
1718
  const errorMessage = aborted ? undefined : `Compaction failed: ${message}`;
1602
1719
  this._clearManualCompactionState();
1603
1720
  this._emit({
@@ -1656,7 +1773,7 @@ export class AgentSession {
1656
1773
  * @returns Whether the post-run loop should call `agent.continue()` for overflow recovery or queued messages
1657
1774
  */
1658
1775
  async _checkCompaction(assistantMessage, skipAbortedCheck = true) {
1659
- const settings = this.settingsManager.getCompactionSettings();
1776
+ const settings = this.settingsManager.getCompactionSettings(this.model);
1660
1777
  if (!settings.enabled)
1661
1778
  return false;
1662
1779
  // Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false
@@ -1761,22 +1878,28 @@ export class AgentSession {
1761
1878
  * @returns Whether the post-run loop should call `agent.continue()`
1762
1879
  */
1763
1880
  async _runAutoCompaction(reason, willRetry) {
1764
- const settings = this.settingsManager.getCompactionSettings();
1881
+ const model = this.model;
1882
+ const settings = this.settingsManager.getCompactionSettings(model);
1883
+ let abortController;
1765
1884
  let started = false;
1766
1885
  let fromExtension = false;
1886
+ let cancelledByExtension = false;
1767
1887
  try {
1768
- if (!this.model) {
1888
+ if (!model) {
1769
1889
  return false;
1770
1890
  }
1771
- const { model: requestModel, apiKey, headers, env } = await this._getSummarizationRequestAuth(this.model);
1772
1891
  const pathEntries = this.sessionManager.getBranch();
1773
1892
  const preparation = prepareCompaction(pathEntries, settings);
1774
1893
  if (!preparation) {
1775
1894
  return false;
1776
1895
  }
1777
- this._emit({ type: "compaction_start", reason });
1778
- this._autoCompactionAbortController = new AbortController();
1896
+ abortController = new AbortController();
1897
+ this._autoCompactionAbortController = abortController;
1779
1898
  started = true;
1899
+ this._emit({ type: "compaction_start", reason });
1900
+ abortController.signal.throwIfAborted();
1901
+ const { model: requestModel, apiKey, headers, env, } = await this._getSummarizationRequestAuth(model, abortController.signal);
1902
+ abortController.signal.throwIfAborted();
1780
1903
  let extensionCompaction;
1781
1904
  if (this._extensionRunner.hasHandlers("session_before_compact")) {
1782
1905
  const extensionResult = (await this._extensionRunner.emit({
@@ -1786,29 +1909,18 @@ export class AgentSession {
1786
1909
  customInstructions: undefined,
1787
1910
  reason,
1788
1911
  willRetry,
1789
- signal: this._autoCompactionAbortController.signal,
1912
+ signal: abortController.signal,
1790
1913
  }));
1791
1914
  if (extensionResult?.cancel) {
1792
- this._emit({
1793
- type: "compaction_end",
1794
- reason,
1795
- result: undefined,
1796
- aborted: true,
1797
- willRetry: false,
1798
- });
1799
- await this._emitSessionCompactFailed({
1800
- reason,
1801
- aborted: true,
1802
- willRetry: false,
1803
- fromExtension: false,
1804
- });
1805
- return false;
1915
+ cancelledByExtension = true;
1916
+ throw new Error("Compaction cancelled");
1806
1917
  }
1807
1918
  if (extensionResult?.compaction) {
1808
1919
  extensionCompaction = extensionResult.compaction;
1809
1920
  fromExtension = true;
1810
1921
  }
1811
1922
  }
1923
+ abortController.signal.throwIfAborted();
1812
1924
  let summary;
1813
1925
  let firstKeptEntryId;
1814
1926
  let tokensBefore;
@@ -1824,29 +1936,14 @@ export class AgentSession {
1824
1936
  }
1825
1937
  else {
1826
1938
  // Shared default summary generator, also used by manual compaction.
1827
- const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, this._autoCompactionAbortController.signal, env, reason);
1939
+ const compactResult = await this._runDefaultCompaction(preparation, requestModel, apiKey, headers, undefined, abortController.signal, env, reason);
1828
1940
  summary = compactResult.summary;
1829
1941
  firstKeptEntryId = compactResult.firstKeptEntryId;
1830
1942
  tokensBefore = compactResult.tokensBefore;
1831
1943
  usage = compactResult.usage;
1832
1944
  details = compactResult.details;
1833
1945
  }
1834
- if (this._autoCompactionAbortController.signal.aborted) {
1835
- this._emit({
1836
- type: "compaction_end",
1837
- reason,
1838
- result: undefined,
1839
- aborted: true,
1840
- willRetry: false,
1841
- });
1842
- await this._emitSessionCompactFailed({
1843
- reason,
1844
- aborted: true,
1845
- willRetry: false,
1846
- fromExtension,
1847
- });
1848
- return false;
1849
- }
1946
+ abortController.signal.throwIfAborted();
1850
1947
  this.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore, details, fromExtension, usage);
1851
1948
  const newEntries = this.sessionManager.getEntries();
1852
1949
  const sessionContext = this.sessionManager.buildSessionContext();
@@ -1889,23 +1986,26 @@ export class AgentSession {
1889
1986
  return this.agent.hasQueuedMessages();
1890
1987
  }
1891
1988
  catch (error) {
1892
- const errorMessage = error instanceof Error ? error.message : "compaction failed";
1989
+ const message = error instanceof Error ? error.message : "compaction failed";
1990
+ const aborted = abortController?.signal.aborted === true || cancelledByExtension;
1893
1991
  if (started) {
1894
- const formattedErrorMessage = reason === "overflow"
1895
- ? `Context overflow recovery failed: ${errorMessage}`
1896
- : `Auto-compaction failed: ${errorMessage}`;
1992
+ const errorMessage = aborted
1993
+ ? undefined
1994
+ : reason === "overflow"
1995
+ ? `Context overflow recovery failed: ${message}`
1996
+ : `Auto-compaction failed: ${message}`;
1897
1997
  this._emit({
1898
1998
  type: "compaction_end",
1899
1999
  reason,
1900
2000
  result: undefined,
1901
- aborted: false,
2001
+ aborted,
1902
2002
  willRetry: false,
1903
- errorMessage: formattedErrorMessage,
2003
+ errorMessage,
1904
2004
  });
1905
2005
  await this._emitSessionCompactFailed({
1906
2006
  reason,
1907
- errorMessage: formattedErrorMessage,
1908
- aborted: false,
2007
+ errorMessage,
2008
+ aborted,
1909
2009
  willRetry: false,
1910
2010
  fromExtension,
1911
2011
  });
@@ -1913,7 +2013,9 @@ export class AgentSession {
1913
2013
  return false;
1914
2014
  }
1915
2015
  finally {
1916
- this._autoCompactionAbortController = undefined;
2016
+ if (this._autoCompactionAbortController === abortController) {
2017
+ this._autoCompactionAbortController = undefined;
2018
+ }
1917
2019
  this._resolveIdleWaitIfIdle();
1918
2020
  }
1919
2021
  }
@@ -1964,8 +2066,7 @@ export class AgentSession {
1964
2066
  themePaths: this.buildExtensionResourcePaths(themePaths),
1965
2067
  };
1966
2068
  this._resourceLoader.extendResources(extensionPaths);
1967
- this._baseSystemPrompt = this._rebuildSystemPrompt(this.getActiveToolNames());
1968
- this.agent.state.systemPrompt = this._baseSystemPrompt;
2069
+ this._rebuildSystemPrompt(this.getActiveToolNames());
1969
2070
  }
1970
2071
  buildExtensionResourcePaths(entries) {
1971
2072
  return entries.map((entry) => {
@@ -2303,6 +2404,18 @@ export class AgentSession {
2303
2404
  },
2304
2405
  };
2305
2406
  }
2407
+ _finishCancelledRetry() {
2408
+ if (this._retryAttempt === 0)
2409
+ return;
2410
+ const attempt = this._retryAttempt;
2411
+ this._retryAttempt = 0;
2412
+ this._emit({
2413
+ type: "auto_retry_end",
2414
+ success: false,
2415
+ attempt,
2416
+ finalError: "Retry cancelled",
2417
+ });
2418
+ }
2306
2419
  /**
2307
2420
  * Prepare a retryable error for continuation with exponential backoff.
2308
2421
  * @returns true if the caller should continue the agent, false otherwise
@@ -2318,7 +2431,7 @@ export class AgentSession {
2318
2431
  this._retryAttempt--;
2319
2432
  return false;
2320
2433
  }
2321
- const delayMs = settings.baseDelayMs * 2 ** (this._retryAttempt - 1);
2434
+ const delayMs = retryDelayMs(settings, this._retryAttempt);
2322
2435
  this._emit({
2323
2436
  type: "auto_retry_start",
2324
2437
  attempt: this._retryAttempt,
@@ -2338,14 +2451,7 @@ export class AgentSession {
2338
2451
  }
2339
2452
  catch {
2340
2453
  // Aborted during sleep - emit end event so UI can clean up
2341
- const attempt = this._retryAttempt;
2342
- this._retryAttempt = 0;
2343
- this._emit({
2344
- type: "auto_retry_end",
2345
- success: false,
2346
- attempt,
2347
- finalError: "Retry cancelled",
2348
- });
2454
+ this._finishCancelledRetry();
2349
2455
  return false;
2350
2456
  }
2351
2457
  finally {
@@ -2496,6 +2602,9 @@ export class AgentSession {
2496
2602
  if (this.isStreaming) {
2497
2603
  throw new Error("Wait for the current response to finish before navigating the session tree.");
2498
2604
  }
2605
+ if (this.isCompacting) {
2606
+ throw new Error("Wait for the current compaction or tree navigation to finish before navigating the session tree.");
2607
+ }
2499
2608
  const oldLeafId = this.sessionManager.getLeafId();
2500
2609
  // No-op if already at target
2501
2610
  if (targetId === oldLeafId) {
@@ -2638,6 +2747,7 @@ export class AgentSession {
2638
2747
  // Update agent state
2639
2748
  const sessionContext = this.sessionManager.buildSessionContext();
2640
2749
  this.agent.state.messages = sessionContext.messages;
2750
+ this._restoreToolsFromTranscript();
2641
2751
  // Emit session_tree event
2642
2752
  await this._extensionRunner.emit({
2643
2753
  type: "session_tree",
@@ -2685,7 +2795,10 @@ export class AgentSession {
2685
2795
  let toolCalls = 0;
2686
2796
  const usageTotals = createUsageTotals();
2687
2797
  for (const entry of this.sessionManager.getEntries()) {
2688
- if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) {
2798
+ if (entry.type === "usage") {
2799
+ addUsageToTotals(usageTotals, entry.usage);
2800
+ }
2801
+ else if ((entry.type === "branch_summary" || entry.type === "compaction") && entry.usage) {
2689
2802
  addUsageToTotals(usageTotals, entry.usage);
2690
2803
  }
2691
2804
  if (entry.type !== "message")
@@ -2799,6 +2912,30 @@ export class AgentSession {
2799
2912
  exportToJsonl(outputPath) {
2800
2913
  return exportSessionToJsonl(this.sessionManager, outputPath);
2801
2914
  }
2915
+ /**
2916
+ * Ask the current model to describe what went wrong in this session for a bug report.
2917
+ * Used when the user declines to share the transcript itself.
2918
+ */
2919
+ async summarizeForBugReport(options) {
2920
+ const model = this.model;
2921
+ if (!model) {
2922
+ throw new Error("No model selected");
2923
+ }
2924
+ const { model: requestModel, apiKey, headers, env } = await this._getSummarizationRequestAuth(model);
2925
+ return generateBugReportSummary({
2926
+ messages: this.messages,
2927
+ hint: options.hint,
2928
+ model: requestModel,
2929
+ apiKey,
2930
+ headers,
2931
+ env,
2932
+ signal: options.signal,
2933
+ thinkingLevel: this.thinkingLevel,
2934
+ streamFn: this.agent.streamFunction,
2935
+ retry: this.settingsManager.getRetrySettings(),
2936
+ sessionId: this.sessionId,
2937
+ });
2938
+ }
2802
2939
  // =========================================================================
2803
2940
  // Utilities
2804
2941
  // =========================================================================