@code-yeongyu/senpi 2026.5.15 → 2026.5.18-2

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 (242) hide show
  1. package/CHANGELOG.md +1172 -1161
  2. package/README.md +1 -2
  3. package/dist/cli/config-selector.d.ts.map +1 -1
  4. package/dist/cli/config-selector.js +1 -1
  5. package/dist/cli/config-selector.js.map +1 -1
  6. package/dist/cli.d.ts.map +1 -1
  7. package/dist/cli.js +5 -1
  8. package/dist/cli.js.map +1 -1
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +12 -3
  11. package/dist/config.js.map +1 -1
  12. package/dist/core/agent-session.d.ts +11 -0
  13. package/dist/core/agent-session.d.ts.map +1 -1
  14. package/dist/core/agent-session.js +160 -13
  15. package/dist/core/agent-session.js.map +1 -1
  16. package/dist/core/compaction/compaction.d.ts +5 -3
  17. package/dist/core/compaction/compaction.d.ts.map +1 -1
  18. package/dist/core/compaction/compaction.js +22 -14
  19. package/dist/core/compaction/compaction.js.map +1 -1
  20. package/dist/core/dynamic-prompt/verification.d.ts +31 -0
  21. package/dist/core/dynamic-prompt/verification.d.ts.map +1 -1
  22. package/dist/core/dynamic-prompt/verification.js +41 -0
  23. package/dist/core/dynamic-prompt/verification.js.map +1 -1
  24. package/dist/core/extensions/builtin/compaction/context-reduction.d.ts +97 -0
  25. package/dist/core/extensions/builtin/compaction/context-reduction.d.ts.map +1 -0
  26. package/dist/core/extensions/builtin/compaction/context-reduction.js +420 -0
  27. package/dist/core/extensions/builtin/compaction/context-reduction.js.map +1 -0
  28. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  29. package/dist/core/extensions/builtin/compaction/index.js +168 -31
  30. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  31. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +197 -0
  32. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -0
  33. package/dist/core/extensions/builtin/compaction/openai-remote.js +690 -0
  34. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -0
  35. package/dist/core/extensions/builtin/compaction/prompts.d.ts +3 -3
  36. package/dist/core/extensions/builtin/compaction/prompts.d.ts.map +1 -1
  37. package/dist/core/extensions/builtin/compaction/prompts.js +0 -22
  38. package/dist/core/extensions/builtin/compaction/prompts.js.map +1 -1
  39. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts +4 -0
  40. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts.map +1 -0
  41. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js +48 -0
  42. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js.map +1 -0
  43. package/dist/core/extensions/builtin/compaction/speculative.d.ts +3 -1
  44. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  45. package/dist/core/extensions/builtin/compaction/speculative.js +80 -33
  46. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  47. package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts +8 -0
  48. package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts.map +1 -1
  49. package/dist/core/extensions/builtin/compaction/todo-bridge.js +12 -6
  50. package/dist/core/extensions/builtin/compaction/todo-bridge.js.map +1 -1
  51. package/dist/core/extensions/builtin/diff.d.ts.map +1 -1
  52. package/dist/core/extensions/builtin/diff.js +1 -1
  53. package/dist/core/extensions/builtin/diff.js.map +1 -1
  54. package/dist/core/extensions/builtin/gpt-apply-patch/preview-format.d.ts.map +1 -1
  55. package/dist/core/extensions/builtin/gpt-apply-patch/preview-format.js +5 -128
  56. package/dist/core/extensions/builtin/gpt-apply-patch/preview-format.js.map +1 -1
  57. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  58. package/dist/core/extensions/builtin/index.js +0 -2
  59. package/dist/core/extensions/builtin/index.js.map +1 -1
  60. package/dist/core/extensions/builtin/openai-web-search/index.d.ts +6 -2
  61. package/dist/core/extensions/builtin/openai-web-search/index.d.ts.map +1 -1
  62. package/dist/core/extensions/builtin/openai-web-search/index.js +82 -10
  63. package/dist/core/extensions/builtin/openai-web-search/index.js.map +1 -1
  64. package/dist/core/extensions/builtin/permission-system/prompt.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/permission-system/prompt.js +0 -5
  66. package/dist/core/extensions/builtin/permission-system/prompt.js.map +1 -1
  67. package/dist/core/extensions/builtin/system-messages.d.ts +1 -1
  68. package/dist/core/extensions/builtin/system-messages.d.ts.map +1 -1
  69. package/dist/core/extensions/builtin/system-messages.js.map +1 -1
  70. package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts +1 -1
  71. package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts.map +1 -1
  72. package/dist/core/extensions/builtin/tool-pair-guard/index.js +8 -4
  73. package/dist/core/extensions/builtin/tool-pair-guard/index.js.map +1 -1
  74. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts +3 -0
  75. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts.map +1 -0
  76. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js +89 -0
  77. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js.map +1 -0
  78. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts +3 -0
  79. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts.map +1 -0
  80. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js +122 -0
  81. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js.map +1 -0
  82. package/dist/core/extensions/loader.d.ts.map +1 -1
  83. package/dist/core/extensions/loader.js +2 -0
  84. package/dist/core/extensions/loader.js.map +1 -1
  85. package/dist/core/extensions/runner.d.ts +3 -0
  86. package/dist/core/extensions/runner.d.ts.map +1 -1
  87. package/dist/core/extensions/runner.js +18 -0
  88. package/dist/core/extensions/runner.js.map +1 -1
  89. package/dist/core/extensions/types.d.ts +22 -0
  90. package/dist/core/extensions/types.d.ts.map +1 -1
  91. package/dist/core/extensions/types.js.map +1 -1
  92. package/dist/core/messages.d.ts +3 -3
  93. package/dist/core/messages.d.ts.map +1 -1
  94. package/dist/core/messages.js +5 -10
  95. package/dist/core/messages.js.map +1 -1
  96. package/dist/core/model-registry.d.ts +1 -0
  97. package/dist/core/model-registry.d.ts.map +1 -1
  98. package/dist/core/model-registry.js +66 -9
  99. package/dist/core/model-registry.js.map +1 -1
  100. package/dist/core/package-manager.d.ts +5 -0
  101. package/dist/core/package-manager.d.ts.map +1 -1
  102. package/dist/core/package-manager.js +72 -31
  103. package/dist/core/package-manager.js.map +1 -1
  104. package/dist/core/prompt-templates.d.ts.map +1 -1
  105. package/dist/core/prompt-templates.js +6 -4
  106. package/dist/core/prompt-templates.js.map +1 -1
  107. package/dist/core/sdk.d.ts +1 -1
  108. package/dist/core/sdk.d.ts.map +1 -1
  109. package/dist/core/sdk.js +7 -22
  110. package/dist/core/sdk.js.map +1 -1
  111. package/dist/core/session-manager.d.ts.map +1 -1
  112. package/dist/core/session-manager.js +39 -9
  113. package/dist/core/session-manager.js.map +1 -1
  114. package/dist/core/settings-manager.d.ts +0 -5
  115. package/dist/core/settings-manager.d.ts.map +1 -1
  116. package/dist/core/settings-manager.js.map +1 -1
  117. package/dist/core/skills.d.ts.map +1 -1
  118. package/dist/core/skills.js +2 -5
  119. package/dist/core/skills.js.map +1 -1
  120. package/dist/core/system-prompt.d.ts.map +1 -1
  121. package/dist/core/system-prompt.js +3 -2
  122. package/dist/core/system-prompt.js.map +1 -1
  123. package/dist/core/thinking-levels.d.ts +6 -0
  124. package/dist/core/thinking-levels.d.ts.map +1 -0
  125. package/dist/core/thinking-levels.js +36 -0
  126. package/dist/core/thinking-levels.js.map +1 -0
  127. package/dist/core/tools/bash.d.ts.map +1 -1
  128. package/dist/core/tools/bash.js +15 -1
  129. package/dist/core/tools/bash.js.map +1 -1
  130. package/dist/core/tools/diff-render.d.ts +13 -0
  131. package/dist/core/tools/diff-render.d.ts.map +1 -0
  132. package/dist/core/tools/diff-render.js +130 -0
  133. package/dist/core/tools/diff-render.js.map +1 -0
  134. package/dist/core/tools/edit.d.ts.map +1 -1
  135. package/dist/core/tools/edit.js +8 -3
  136. package/dist/core/tools/edit.js.map +1 -1
  137. package/dist/core/tools/write.d.ts.map +1 -1
  138. package/dist/core/tools/write.js +28 -7
  139. package/dist/core/tools/write.js.map +1 -1
  140. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  141. package/dist/modes/interactive/components/compaction-summary-message.js +20 -2
  142. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  143. package/dist/modes/interactive/components/config-selector.d.ts +2 -2
  144. package/dist/modes/interactive/components/config-selector.d.ts.map +1 -1
  145. package/dist/modes/interactive/components/config-selector.js +7 -4
  146. package/dist/modes/interactive/components/config-selector.js.map +1 -1
  147. package/dist/modes/interactive/components/footer.d.ts +0 -1
  148. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  149. package/dist/modes/interactive/components/footer.js +42 -44
  150. package/dist/modes/interactive/components/footer.js.map +1 -1
  151. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  152. package/dist/modes/interactive/components/keybinding-hints.js +3 -1
  153. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  154. package/dist/modes/interactive/interactive-mode.d.ts +9 -0
  155. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  156. package/dist/modes/interactive/interactive-mode.js +177 -82
  157. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  158. package/dist/modes/interactive/session-info-format.d.ts +3 -0
  159. package/dist/modes/interactive/session-info-format.d.ts.map +1 -0
  160. package/dist/modes/interactive/session-info-format.js +44 -0
  161. package/dist/modes/interactive/session-info-format.js.map +1 -0
  162. package/dist/modes/interactive/working-status.d.ts +21 -0
  163. package/dist/modes/interactive/working-status.d.ts.map +1 -0
  164. package/dist/modes/interactive/working-status.js +71 -0
  165. package/dist/modes/interactive/working-status.js.map +1 -0
  166. package/dist/package-manager-cli.d.ts.map +1 -1
  167. package/dist/package-manager-cli.js +3 -4
  168. package/dist/package-manager-cli.js.map +1 -1
  169. package/dist/senpi +5 -1
  170. package/dist/utils/child-process.d.ts +7 -1
  171. package/dist/utils/child-process.d.ts.map +1 -1
  172. package/dist/utils/child-process.js +60 -7
  173. package/dist/utils/child-process.js.map +1 -1
  174. package/dist/utils/clipboard-image.d.ts.map +1 -1
  175. package/dist/utils/clipboard-image.js +1 -1
  176. package/dist/utils/clipboard-image.js.map +1 -1
  177. package/dist/utils/tools-manager.d.ts.map +1 -1
  178. package/dist/utils/tools-manager.js +4 -1
  179. package/dist/utils/tools-manager.js.map +1 -1
  180. package/docs/custom-provider.md +55 -0
  181. package/docs/extensions.md +1 -2
  182. package/docs/index.md +0 -1
  183. package/docs/models.md +9 -0
  184. package/docs/sdk.md +0 -1
  185. package/docs/settings.md +2 -32
  186. package/docs/skills.md +3 -4
  187. package/docs/termux.md +2 -2
  188. package/docs/usage.md +1 -1
  189. package/examples/README.md +1 -1
  190. package/examples/extensions/README.md +0 -1
  191. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  192. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  193. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  194. package/examples/extensions/overlay-qa-tests.ts +1 -1
  195. package/examples/extensions/sandbox/package-lock.json +2 -2
  196. package/examples/extensions/sandbox/package.json +1 -1
  197. package/examples/extensions/with-deps/package-lock.json +2 -2
  198. package/examples/extensions/with-deps/package.json +1 -1
  199. package/package.json +6 -6
  200. package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts +0 -10
  201. package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts.map +0 -1
  202. package/dist/core/extensions/builtin/background-task/cancel-tool.js +0 -109
  203. package/dist/core/extensions/builtin/background-task/cancel-tool.js.map +0 -1
  204. package/dist/core/extensions/builtin/background-task/index.d.ts +0 -3
  205. package/dist/core/extensions/builtin/background-task/index.d.ts.map +0 -1
  206. package/dist/core/extensions/builtin/background-task/index.js +0 -207
  207. package/dist/core/extensions/builtin/background-task/index.js.map +0 -1
  208. package/dist/core/extensions/builtin/background-task/manager.d.ts +0 -17
  209. package/dist/core/extensions/builtin/background-task/manager.d.ts.map +0 -1
  210. package/dist/core/extensions/builtin/background-task/manager.js +0 -114
  211. package/dist/core/extensions/builtin/background-task/manager.js.map +0 -1
  212. package/dist/core/extensions/builtin/background-task/notification.d.ts +0 -22
  213. package/dist/core/extensions/builtin/background-task/notification.d.ts.map +0 -1
  214. package/dist/core/extensions/builtin/background-task/notification.js +0 -105
  215. package/dist/core/extensions/builtin/background-task/notification.js.map +0 -1
  216. package/dist/core/extensions/builtin/background-task/output-tool.d.ts +0 -11
  217. package/dist/core/extensions/builtin/background-task/output-tool.d.ts.map +0 -1
  218. package/dist/core/extensions/builtin/background-task/output-tool.js +0 -127
  219. package/dist/core/extensions/builtin/background-task/output-tool.js.map +0 -1
  220. package/dist/core/extensions/builtin/background-task/spawner.d.ts +0 -8
  221. package/dist/core/extensions/builtin/background-task/spawner.d.ts.map +0 -1
  222. package/dist/core/extensions/builtin/background-task/spawner.js +0 -207
  223. package/dist/core/extensions/builtin/background-task/spawner.js.map +0 -1
  224. package/dist/core/extensions/builtin/background-task/task-tool.d.ts +0 -20
  225. package/dist/core/extensions/builtin/background-task/task-tool.d.ts.map +0 -1
  226. package/dist/core/extensions/builtin/background-task/task-tool.js +0 -302
  227. package/dist/core/extensions/builtin/background-task/task-tool.js.map +0 -1
  228. package/dist/core/extensions/builtin/background-task/types.d.ts +0 -72
  229. package/dist/core/extensions/builtin/background-task/types.d.ts.map +0 -1
  230. package/dist/core/extensions/builtin/background-task/types.js +0 -32
  231. package/dist/core/extensions/builtin/background-task/types.js.map +0 -1
  232. package/docs/agents.md +0 -348
  233. package/examples/extensions/subagent/README.md +0 -172
  234. package/examples/extensions/subagent/agents/planner.md +0 -37
  235. package/examples/extensions/subagent/agents/reviewer.md +0 -35
  236. package/examples/extensions/subagent/agents/scout.md +0 -50
  237. package/examples/extensions/subagent/agents/worker.md +0 -24
  238. package/examples/extensions/subagent/agents.ts +0 -126
  239. package/examples/extensions/subagent/index.ts +0 -987
  240. package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  241. package/examples/extensions/subagent/prompts/implement.md +0 -10
  242. package/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
@@ -15,7 +15,7 @@
15
15
  import { randomUUID } from "node:crypto";
16
16
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
17
17
  import { basename, dirname, resolve } from "node:path";
18
- import { cleanupSessionResources, getSupportedThinkingLevels, isContextOverflow, modelsAreEqual, resetApiProviders, supportsMax, supportsXhigh, } from "@earendil-works/pi-ai";
18
+ import { cleanupSessionResources, isContextOverflow, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai";
19
19
  import { expandTildePath } from "../config.js";
20
20
  import { theme } from "../modes/interactive/theme/theme.js";
21
21
  import { stripFrontmatter } from "../utils/frontmatter.js";
@@ -34,6 +34,7 @@ import { getModelNarrowingPatterns, resolveModelScope } from "./model-resolver.j
34
34
  import { expandPromptTemplate } from "./prompt-templates.js";
35
35
  import { buildSessionContext, CURRENT_SESSION_VERSION, getLatestCompactionEntry, } from "./session-manager.js";
36
36
  import { createSyntheticSourceInfo } from "./source-info.js";
37
+ import { getSupportedThinkingLevels, supportsMax, supportsXhigh } from "./thinking-levels.js";
37
38
  import { createLocalBashOperations } from "./tools/bash.js";
38
39
  import { createAllToolDefinitions } from "./tools/index.js";
39
40
  import { createToolDefinitionFromAgentTool } from "./tools/tool-definition-wrapper.js";
@@ -96,6 +97,7 @@ export class AgentSession {
96
97
  _retryAttempt = 0;
97
98
  _retryPromise = undefined;
98
99
  _retryResolve = undefined;
100
+ _userAbortPromise = undefined;
99
101
  // Bash execution state
100
102
  _bashAbortController = undefined;
101
103
  _pendingBashMessages = [];
@@ -149,6 +151,19 @@ export class AgentSession {
149
151
  }
150
152
  this._unsubscribeAgent = this.agent.subscribe(this._handleAgentEvent);
151
153
  this._installAgentToolHooks();
154
+ const previousPrepareNextTurn = this.agent.prepareNextTurn;
155
+ this.agent.prepareNextTurn = async (signal) => {
156
+ const nextTurn = await previousPrepareNextTurn?.(signal);
157
+ const model = this.agent.state.model;
158
+ if (!model) {
159
+ return nextTurn;
160
+ }
161
+ return {
162
+ ...nextTurn,
163
+ model,
164
+ thinkingLevel: this.agent.state.thinkingLevel,
165
+ };
166
+ };
152
167
  this._buildRuntime({
153
168
  activeToolNames: this._initialActiveToolNames,
154
169
  includeAllExtensionTools: true,
@@ -177,6 +192,13 @@ export class AgentSession {
177
192
  }
178
193
  throw new Error(formatNoApiKeyFoundMessage(model.provider));
179
194
  }
195
+ async _getCompactionRequestAuth(model) {
196
+ if (this.agent.streamFn === streamSimple) {
197
+ return this._getRequiredRequestAuth(model);
198
+ }
199
+ const result = await this._modelRegistry.getApiKeyAndHeaders(model);
200
+ return result.ok ? { apiKey: result.apiKey, headers: result.headers, extraBody: result.extraBody } : {};
201
+ }
180
202
  /**
181
203
  * Install tool hooks once on the Agent instance.
182
204
  *
@@ -745,6 +767,10 @@ export class AgentSession {
745
767
  * @throws Error if no model selected or no API key available (when not streaming)
746
768
  */
747
769
  async prompt(text, options) {
770
+ const userAbortPromise = this._userAbortPromise;
771
+ if (userAbortPromise) {
772
+ await userAbortPromise;
773
+ }
748
774
  const expandPromptTemplates = options?.expandPromptTemplates ?? true;
749
775
  const preflightResult = options?.preflightResult;
750
776
  let messages;
@@ -1114,8 +1140,24 @@ export class AgentSession {
1114
1140
  */
1115
1141
  async abort() {
1116
1142
  this.abortRetry();
1117
- this.agent.abort();
1118
- await this.agent.waitForIdle();
1143
+ if (this._userAbortPromise) {
1144
+ this.agent.abort();
1145
+ await this._userAbortPromise;
1146
+ return;
1147
+ }
1148
+ const abortPromise = (async () => {
1149
+ this.agent.abort();
1150
+ await this.agent.waitForIdle();
1151
+ })();
1152
+ this._userAbortPromise = abortPromise;
1153
+ try {
1154
+ await abortPromise;
1155
+ }
1156
+ finally {
1157
+ if (this._userAbortPromise === abortPromise) {
1158
+ this._userAbortPromise = undefined;
1159
+ }
1160
+ }
1119
1161
  }
1120
1162
  // =========================================================================
1121
1163
  // Model Management
@@ -1377,8 +1419,11 @@ export class AgentSession {
1377
1419
  if (options.expectedRevision !== undefined && options.expectedRevision !== this._messageRevision) {
1378
1420
  return { applied: false, reason: "stale" };
1379
1421
  }
1380
- this._compactionAbortController = new AbortController();
1381
- this._emit({ type: "compaction_start", reason: options.reason });
1422
+ const ownsController = this._compactionAbortController === undefined;
1423
+ if (ownsController) {
1424
+ this._compactionAbortController = new AbortController();
1425
+ this._emit({ type: "compaction_start", reason: options.reason });
1426
+ }
1382
1427
  try {
1383
1428
  const execution = await this._executeCompaction({
1384
1429
  reason: options.reason,
@@ -1407,6 +1452,38 @@ export class AgentSession {
1407
1452
  this._compactionAbortController = undefined;
1408
1453
  }
1409
1454
  }
1455
+ _beginExtensionCompactionFeedback(reason) {
1456
+ if (!this._compactionAbortController) {
1457
+ this._compactionAbortController = new AbortController();
1458
+ this._emit({ type: "compaction_start", reason });
1459
+ }
1460
+ return this._compactionAbortController.signal;
1461
+ }
1462
+ _updateExtensionCompactionFeedback(options) {
1463
+ if (!this._compactionAbortController && !this._autoCompactionAbortController)
1464
+ return;
1465
+ this._emit({
1466
+ type: "compaction_progress",
1467
+ reason: options.reason,
1468
+ ...(options.delta !== undefined ? { delta: options.delta } : {}),
1469
+ ...(options.text !== undefined ? { text: options.text } : {}),
1470
+ });
1471
+ }
1472
+ _endExtensionCompactionFeedback(options) {
1473
+ const controller = this._compactionAbortController;
1474
+ if (!controller)
1475
+ return;
1476
+ const aborted = options.aborted ?? controller.signal.aborted;
1477
+ this._emit({
1478
+ type: "compaction_end",
1479
+ reason: options.reason,
1480
+ result: undefined,
1481
+ aborted,
1482
+ willRetry: false,
1483
+ errorMessage: aborted ? undefined : options.errorMessage,
1484
+ });
1485
+ this._compactionAbortController = undefined;
1486
+ }
1410
1487
  async _executeCompaction(request) {
1411
1488
  if (!this.model) {
1412
1489
  throw new Error(formatNoModelSelectedMessage());
@@ -1449,8 +1526,8 @@ export class AgentSession {
1449
1526
  }
1450
1527
  }
1451
1528
  if (!compactionResult) {
1452
- const { apiKey, headers, extraBody } = await this._getRequiredRequestAuth(this.model);
1453
- compactionResult = await compact(preparation, this.model, apiKey, headers, request.customInstructions, signal, extraBody, this.thinkingLevel);
1529
+ const { apiKey, headers, extraBody } = await this._getCompactionRequestAuth(this.model);
1530
+ compactionResult = await compact(preparation, this.model, apiKey, headers, request.customInstructions, signal, extraBody, this.thinkingLevel, this.agent.streamFn);
1454
1531
  }
1455
1532
  }
1456
1533
  if (signal.aborted) {
@@ -1573,14 +1650,18 @@ export class AgentSession {
1573
1650
  shouldCompact(contextUsage.tokens, contextUsage.contextWindow, settings);
1574
1651
  if (isContextOverflow(assistantMessage, contextWindow) && (sameModel || currentContextNeedsCompaction)) {
1575
1652
  if (this._overflowRecoveryAttempted) {
1653
+ const errorMessage = "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.";
1576
1654
  this._emit({
1577
1655
  type: "compaction_end",
1578
1656
  reason: "overflow",
1579
1657
  result: undefined,
1580
1658
  aborted: false,
1581
1659
  willRetry: false,
1582
- errorMessage: "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.",
1660
+ errorMessage,
1583
1661
  });
1662
+ if (requestReason === "pre_prompt") {
1663
+ throw new Error(errorMessage);
1664
+ }
1584
1665
  return;
1585
1666
  }
1586
1667
  this._overflowRecoveryAttempted = true;
@@ -1635,14 +1716,20 @@ export class AgentSession {
1635
1716
  this._emit({ type: "compaction_start", reason: "pre_prompt" });
1636
1717
  this._compactionAbortController = new AbortController();
1637
1718
  try {
1638
- await this._executeCompaction({
1719
+ const execution = await this._executeCompaction({
1639
1720
  reason: "pre_prompt",
1640
1721
  willRetry: false,
1641
1722
  lastAssistantMessage,
1642
1723
  skipAbortedCheck,
1643
1724
  });
1725
+ if (!execution.accepted && isContextOverflow(lastAssistantMessage, this.model?.contextWindow ?? 0)) {
1726
+ this._overflowRecoveryAttempted = false;
1727
+ }
1644
1728
  }
1645
1729
  catch (error) {
1730
+ if (isContextOverflow(lastAssistantMessage, this.model?.contextWindow ?? 0)) {
1731
+ this._overflowRecoveryAttempted = false;
1732
+ }
1646
1733
  const errorMessage = error instanceof Error ? error.message : "compaction failed";
1647
1734
  const aborted = errorMessage === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
1648
1735
  this._emit({
@@ -1666,6 +1753,8 @@ export class AgentSession {
1666
1753
  this._autoCompactionAbortController = new AbortController();
1667
1754
  try {
1668
1755
  if (!this.model) {
1756
+ if (reason === "overflow")
1757
+ this._overflowRecoveryAttempted = false;
1669
1758
  this._emit({
1670
1759
  type: "compaction_end",
1671
1760
  reason,
@@ -1676,7 +1765,9 @@ export class AgentSession {
1676
1765
  return;
1677
1766
  }
1678
1767
  const authResult = await this._modelRegistry.getApiKeyAndHeaders(this.model);
1679
- if (!authResult.ok || !authResult.apiKey) {
1768
+ if (this.agent.streamFn === streamSimple && (!authResult.ok || !authResult.apiKey)) {
1769
+ if (reason === "overflow")
1770
+ this._overflowRecoveryAttempted = false;
1680
1771
  this._emit({
1681
1772
  type: "compaction_end",
1682
1773
  reason,
@@ -1688,6 +1779,8 @@ export class AgentSession {
1688
1779
  }
1689
1780
  const preparation = prepareCompaction(this.sessionManager.getBranch(), this.settingsManager.getCompactionSettings());
1690
1781
  if (!preparation) {
1782
+ if (reason === "overflow")
1783
+ this._overflowRecoveryAttempted = false;
1691
1784
  this._emit({
1692
1785
  type: "compaction_end",
1693
1786
  reason,
@@ -1699,6 +1792,8 @@ export class AgentSession {
1699
1792
  }
1700
1793
  const execution = await this._executeCompaction({ reason, willRetry });
1701
1794
  if (!execution.accepted) {
1795
+ if (reason === "overflow")
1796
+ this._overflowRecoveryAttempted = false;
1702
1797
  return;
1703
1798
  }
1704
1799
  if (willRetry) {
@@ -1721,6 +1816,8 @@ export class AgentSession {
1721
1816
  }
1722
1817
  }
1723
1818
  catch (error) {
1819
+ if (reason === "overflow")
1820
+ this._overflowRecoveryAttempted = false;
1724
1821
  const errorMessage = error instanceof Error ? error.message : "compaction failed";
1725
1822
  const aborted = errorMessage === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
1726
1823
  this._emit({
@@ -1943,6 +2040,9 @@ export class AgentSession {
1943
2040
  }
1944
2041
  })();
1945
2042
  },
2043
+ beginCompaction: (options) => this._beginExtensionCompactionFeedback(options.reason),
2044
+ updateCompaction: (options) => this._updateExtensionCompactionFeedback(options),
2045
+ endCompaction: (options) => this._endExtensionCompactionFeedback(options),
1946
2046
  getMessageRevision: () => this.getMessageRevision(),
1947
2047
  applyCompaction: (precomputed, options) => this.applyCompaction(precomputed, options),
1948
2048
  getSystemPrompt: () => this.systemPrompt,
@@ -2101,16 +2201,49 @@ export class AgentSession {
2101
2201
  * Context overflow errors are NOT retryable (handled by compaction instead).
2102
2202
  */
2103
2203
  _isRetryableError(message) {
2104
- if (message.stopReason !== "error" || !message.errorMessage)
2204
+ if (!message.errorMessage)
2105
2205
  return false;
2106
2206
  // Context overflow is handled by compaction, not retry
2107
2207
  const contextWindow = this.model?.contextWindow ?? 0;
2108
2208
  if (isContextOverflow(message, contextWindow))
2109
2209
  return false;
2110
2210
  const err = message.errorMessage;
2211
+ if (message.stopReason === "aborted") {
2212
+ return /timed? out|timeout/i.test(err);
2213
+ }
2214
+ if (message.stopReason !== "error")
2215
+ return false;
2111
2216
  // Match: overloaded_error, provider returned error, rate limit, 429, 500, 502, 503, 504, service unavailable, network/connection errors (including connection lost), WebSocket transport closes/errors, fetch failed, premature stream endings, HTTP/2 closed before response, terminated, retry delay exceeded
2112
2217
  return /overloaded|provider.?returned.?error|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server.?error|internal.?error|network.?error|connection.?error|connection.?refused|connection.?lost|websocket.?closed|websocket.?error|other side closed|fetch failed|upstream.?connect|reset before headers|socket hang up|ended without|stream ended before message_stop|http2 request did not get a response|timed? out|timeout|terminated|retry delay/i.test(err);
2113
2218
  }
2219
+ _getProviderRetryDelayMs(errorMessage) {
2220
+ const retryAfterMsMatch = errorMessage.match(/\bretry[-_ ]?after[-_ ]?ms\s*[:=]\s*(\d+(?:\.\d+)?)/i);
2221
+ if (retryAfterMsMatch) {
2222
+ const delayMs = Math.ceil(Number(retryAfterMsMatch[1]));
2223
+ return Number.isFinite(delayMs) && delayMs > 0 ? delayMs : undefined;
2224
+ }
2225
+ const retryAfterSecondsMatch = errorMessage.match(/\bretry[-_ ]?after\s*[:=]\s*(\d+(?:\.\d+)?)/i);
2226
+ if (retryAfterSecondsMatch) {
2227
+ const delayMs = Math.ceil(Number(retryAfterSecondsMatch[1]) * 1000);
2228
+ return Number.isFinite(delayMs) && delayMs > 0 ? delayMs : undefined;
2229
+ }
2230
+ const retryInMatch = errorMessage.match(/\b(?:retry|try again|wait)\s+(?:after|in)\s*(\d+(?:\.\d+)?)\s*(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m)\b/i);
2231
+ if (!retryInMatch) {
2232
+ return undefined;
2233
+ }
2234
+ const value = Number(retryInMatch[1]);
2235
+ if (!Number.isFinite(value) || value <= 0) {
2236
+ return undefined;
2237
+ }
2238
+ const unit = retryInMatch[2].toLowerCase();
2239
+ if (unit === "m" || unit.startsWith("min")) {
2240
+ return Math.ceil(value * 60_000);
2241
+ }
2242
+ if (unit.startsWith("s")) {
2243
+ return Math.ceil(value * 1000);
2244
+ }
2245
+ return Math.ceil(value);
2246
+ }
2114
2247
  /**
2115
2248
  * Handle retryable errors with exponential backoff.
2116
2249
  * @returns true if retry was initiated, false if max retries exceeded or disabled
@@ -2141,13 +2274,27 @@ export class AgentSession {
2141
2274
  this._resolveRetry(); // Resolve so waitForRetry() completes
2142
2275
  return false;
2143
2276
  }
2144
- const delayMs = settings.baseDelayMs * 2 ** (this._retryAttempt - 1);
2277
+ const errorMessage = message.errorMessage || "Unknown error";
2278
+ const providerDelayMs = this._getProviderRetryDelayMs(errorMessage);
2279
+ const maxRetryDelayMs = this.settingsManager.getProviderRetrySettings().maxRetryDelayMs;
2280
+ if (providerDelayMs !== undefined && providerDelayMs > maxRetryDelayMs) {
2281
+ this._emit({
2282
+ type: "auto_retry_end",
2283
+ success: false,
2284
+ attempt: this._retryAttempt,
2285
+ finalError: `Provider requested retry delay ${providerDelayMs}ms, exceeding configured maximum ${maxRetryDelayMs}ms`,
2286
+ });
2287
+ this._retryAttempt = 0;
2288
+ this._resolveRetry();
2289
+ return false;
2290
+ }
2291
+ const delayMs = providerDelayMs ?? settings.baseDelayMs * 2 ** (this._retryAttempt - 1);
2145
2292
  this._emit({
2146
2293
  type: "auto_retry_start",
2147
2294
  attempt: this._retryAttempt,
2148
2295
  maxAttempts: settings.maxRetries,
2149
2296
  delayMs,
2150
- errorMessage: message.errorMessage || "Unknown error",
2297
+ errorMessage,
2151
2298
  });
2152
2299
  // Remove error message from agent state (keep in session for history)
2153
2300
  const messages = this.agent.state.messages;