@code-yeongyu/senpi 2026.5.15 → 2026.5.16

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 (170) hide show
  1. package/CHANGELOG.md +1113 -1177
  2. package/README.md +1 -2
  3. package/dist/core/agent-session.d.ts +9 -0
  4. package/dist/core/agent-session.d.ts.map +1 -1
  5. package/dist/core/agent-session.js +114 -8
  6. package/dist/core/agent-session.js.map +1 -1
  7. package/dist/core/dynamic-prompt/verification.d.ts +31 -0
  8. package/dist/core/dynamic-prompt/verification.d.ts.map +1 -1
  9. package/dist/core/dynamic-prompt/verification.js +41 -0
  10. package/dist/core/dynamic-prompt/verification.js.map +1 -1
  11. package/dist/core/extensions/builtin/compaction/context-reduction.d.ts +97 -0
  12. package/dist/core/extensions/builtin/compaction/context-reduction.d.ts.map +1 -0
  13. package/dist/core/extensions/builtin/compaction/context-reduction.js +420 -0
  14. package/dist/core/extensions/builtin/compaction/context-reduction.js.map +1 -0
  15. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/compaction/index.js +168 -31
  17. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  18. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts +197 -0
  19. package/dist/core/extensions/builtin/compaction/openai-remote.d.ts.map +1 -0
  20. package/dist/core/extensions/builtin/compaction/openai-remote.js +690 -0
  21. package/dist/core/extensions/builtin/compaction/openai-remote.js.map +1 -0
  22. package/dist/core/extensions/builtin/compaction/prompts.d.ts +3 -3
  23. package/dist/core/extensions/builtin/compaction/prompts.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/compaction/prompts.js +0 -22
  25. package/dist/core/extensions/builtin/compaction/prompts.js.map +1 -1
  26. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts +4 -0
  27. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.d.ts.map +1 -0
  28. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js +48 -0
  29. package/dist/core/extensions/builtin/compaction/repair-tool-pairs.js.map +1 -0
  30. package/dist/core/extensions/builtin/compaction/speculative.d.ts +3 -1
  31. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/compaction/speculative.js +80 -33
  33. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  34. package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts +8 -0
  35. package/dist/core/extensions/builtin/compaction/todo-bridge.d.ts.map +1 -1
  36. package/dist/core/extensions/builtin/compaction/todo-bridge.js +12 -6
  37. package/dist/core/extensions/builtin/compaction/todo-bridge.js.map +1 -1
  38. package/dist/core/extensions/builtin/diff.d.ts.map +1 -1
  39. package/dist/core/extensions/builtin/diff.js +1 -1
  40. package/dist/core/extensions/builtin/diff.js.map +1 -1
  41. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  42. package/dist/core/extensions/builtin/index.js +0 -2
  43. package/dist/core/extensions/builtin/index.js.map +1 -1
  44. package/dist/core/extensions/builtin/openai-web-search/index.d.ts +6 -2
  45. package/dist/core/extensions/builtin/openai-web-search/index.d.ts.map +1 -1
  46. package/dist/core/extensions/builtin/openai-web-search/index.js +82 -10
  47. package/dist/core/extensions/builtin/openai-web-search/index.js.map +1 -1
  48. package/dist/core/extensions/builtin/permission-system/prompt.d.ts.map +1 -1
  49. package/dist/core/extensions/builtin/permission-system/prompt.js +0 -5
  50. package/dist/core/extensions/builtin/permission-system/prompt.js.map +1 -1
  51. package/dist/core/extensions/builtin/system-messages.d.ts +1 -1
  52. package/dist/core/extensions/builtin/system-messages.d.ts.map +1 -1
  53. package/dist/core/extensions/builtin/system-messages.js.map +1 -1
  54. package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts +1 -1
  55. package/dist/core/extensions/builtin/tool-pair-guard/index.d.ts.map +1 -1
  56. package/dist/core/extensions/builtin/tool-pair-guard/index.js +8 -4
  57. package/dist/core/extensions/builtin/tool-pair-guard/index.js.map +1 -1
  58. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts +3 -0
  59. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.d.ts.map +1 -0
  60. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js +89 -0
  61. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-chat-completions-payload.js.map +1 -0
  62. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts +3 -0
  63. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.d.ts.map +1 -0
  64. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js +122 -0
  65. package/dist/core/extensions/builtin/tool-pair-guard/sanitize-openai-responses-payload.js.map +1 -0
  66. package/dist/core/extensions/loader.d.ts.map +1 -1
  67. package/dist/core/extensions/loader.js +2 -0
  68. package/dist/core/extensions/loader.js.map +1 -1
  69. package/dist/core/extensions/runner.d.ts +3 -0
  70. package/dist/core/extensions/runner.d.ts.map +1 -1
  71. package/dist/core/extensions/runner.js +18 -0
  72. package/dist/core/extensions/runner.js.map +1 -1
  73. package/dist/core/extensions/types.d.ts +22 -0
  74. package/dist/core/extensions/types.d.ts.map +1 -1
  75. package/dist/core/extensions/types.js.map +1 -1
  76. package/dist/core/messages.d.ts +3 -3
  77. package/dist/core/messages.d.ts.map +1 -1
  78. package/dist/core/messages.js +5 -10
  79. package/dist/core/messages.js.map +1 -1
  80. package/dist/core/model-registry.d.ts.map +1 -1
  81. package/dist/core/model-registry.js +2 -0
  82. package/dist/core/model-registry.js.map +1 -1
  83. package/dist/core/sdk.d.ts +1 -1
  84. package/dist/core/sdk.d.ts.map +1 -1
  85. package/dist/core/sdk.js +7 -22
  86. package/dist/core/sdk.js.map +1 -1
  87. package/dist/core/session-manager.d.ts.map +1 -1
  88. package/dist/core/session-manager.js +1 -1
  89. package/dist/core/session-manager.js.map +1 -1
  90. package/dist/core/settings-manager.d.ts +0 -5
  91. package/dist/core/settings-manager.d.ts.map +1 -1
  92. package/dist/core/settings-manager.js.map +1 -1
  93. package/dist/core/thinking-levels.d.ts +6 -0
  94. package/dist/core/thinking-levels.d.ts.map +1 -0
  95. package/dist/core/thinking-levels.js +36 -0
  96. package/dist/core/thinking-levels.js.map +1 -0
  97. package/dist/core/tools/bash.d.ts.map +1 -1
  98. package/dist/core/tools/bash.js +15 -1
  99. package/dist/core/tools/bash.js.map +1 -1
  100. package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -1
  101. package/dist/modes/interactive/components/compaction-summary-message.js +20 -2
  102. package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -1
  103. package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -1
  104. package/dist/modes/interactive/components/keybinding-hints.js +3 -1
  105. package/dist/modes/interactive/components/keybinding-hints.js.map +1 -1
  106. package/dist/modes/interactive/interactive-mode.d.ts +8 -0
  107. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  108. package/dist/modes/interactive/interactive-mode.js +137 -49
  109. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  110. package/dist/modes/interactive/working-status.d.ts +15 -0
  111. package/dist/modes/interactive/working-status.d.ts.map +1 -0
  112. package/dist/modes/interactive/working-status.js +60 -0
  113. package/dist/modes/interactive/working-status.js.map +1 -0
  114. package/dist/utils/clipboard-image.d.ts.map +1 -1
  115. package/dist/utils/clipboard-image.js +1 -1
  116. package/dist/utils/clipboard-image.js.map +1 -1
  117. package/docs/extensions.md +0 -1
  118. package/docs/index.md +0 -1
  119. package/docs/models.md +9 -0
  120. package/docs/sdk.md +0 -1
  121. package/docs/settings.md +1 -29
  122. package/docs/termux.md +2 -2
  123. package/docs/usage.md +1 -1
  124. package/examples/README.md +1 -1
  125. package/examples/extensions/README.md +0 -1
  126. package/examples/extensions/overlay-qa-tests.ts +1 -1
  127. package/package.json +4 -4
  128. package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts +0 -10
  129. package/dist/core/extensions/builtin/background-task/cancel-tool.d.ts.map +0 -1
  130. package/dist/core/extensions/builtin/background-task/cancel-tool.js +0 -109
  131. package/dist/core/extensions/builtin/background-task/cancel-tool.js.map +0 -1
  132. package/dist/core/extensions/builtin/background-task/index.d.ts +0 -3
  133. package/dist/core/extensions/builtin/background-task/index.d.ts.map +0 -1
  134. package/dist/core/extensions/builtin/background-task/index.js +0 -207
  135. package/dist/core/extensions/builtin/background-task/index.js.map +0 -1
  136. package/dist/core/extensions/builtin/background-task/manager.d.ts +0 -17
  137. package/dist/core/extensions/builtin/background-task/manager.d.ts.map +0 -1
  138. package/dist/core/extensions/builtin/background-task/manager.js +0 -114
  139. package/dist/core/extensions/builtin/background-task/manager.js.map +0 -1
  140. package/dist/core/extensions/builtin/background-task/notification.d.ts +0 -22
  141. package/dist/core/extensions/builtin/background-task/notification.d.ts.map +0 -1
  142. package/dist/core/extensions/builtin/background-task/notification.js +0 -105
  143. package/dist/core/extensions/builtin/background-task/notification.js.map +0 -1
  144. package/dist/core/extensions/builtin/background-task/output-tool.d.ts +0 -11
  145. package/dist/core/extensions/builtin/background-task/output-tool.d.ts.map +0 -1
  146. package/dist/core/extensions/builtin/background-task/output-tool.js +0 -127
  147. package/dist/core/extensions/builtin/background-task/output-tool.js.map +0 -1
  148. package/dist/core/extensions/builtin/background-task/spawner.d.ts +0 -8
  149. package/dist/core/extensions/builtin/background-task/spawner.d.ts.map +0 -1
  150. package/dist/core/extensions/builtin/background-task/spawner.js +0 -207
  151. package/dist/core/extensions/builtin/background-task/spawner.js.map +0 -1
  152. package/dist/core/extensions/builtin/background-task/task-tool.d.ts +0 -20
  153. package/dist/core/extensions/builtin/background-task/task-tool.d.ts.map +0 -1
  154. package/dist/core/extensions/builtin/background-task/task-tool.js +0 -302
  155. package/dist/core/extensions/builtin/background-task/task-tool.js.map +0 -1
  156. package/dist/core/extensions/builtin/background-task/types.d.ts +0 -72
  157. package/dist/core/extensions/builtin/background-task/types.d.ts.map +0 -1
  158. package/dist/core/extensions/builtin/background-task/types.js +0 -32
  159. package/dist/core/extensions/builtin/background-task/types.js.map +0 -1
  160. package/docs/agents.md +0 -348
  161. package/examples/extensions/subagent/README.md +0 -172
  162. package/examples/extensions/subagent/agents/planner.md +0 -37
  163. package/examples/extensions/subagent/agents/reviewer.md +0 -35
  164. package/examples/extensions/subagent/agents/scout.md +0 -50
  165. package/examples/extensions/subagent/agents/worker.md +0 -24
  166. package/examples/extensions/subagent/agents.ts +0 -126
  167. package/examples/extensions/subagent/index.ts +0 -987
  168. package/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  169. package/examples/extensions/subagent/prompts/implement.md +0 -10
  170. 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 } 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";
@@ -1377,8 +1378,11 @@ export class AgentSession {
1377
1378
  if (options.expectedRevision !== undefined && options.expectedRevision !== this._messageRevision) {
1378
1379
  return { applied: false, reason: "stale" };
1379
1380
  }
1380
- this._compactionAbortController = new AbortController();
1381
- this._emit({ type: "compaction_start", reason: options.reason });
1381
+ const ownsController = this._compactionAbortController === undefined;
1382
+ if (ownsController) {
1383
+ this._compactionAbortController = new AbortController();
1384
+ this._emit({ type: "compaction_start", reason: options.reason });
1385
+ }
1382
1386
  try {
1383
1387
  const execution = await this._executeCompaction({
1384
1388
  reason: options.reason,
@@ -1407,6 +1411,38 @@ export class AgentSession {
1407
1411
  this._compactionAbortController = undefined;
1408
1412
  }
1409
1413
  }
1414
+ _beginExtensionCompactionFeedback(reason) {
1415
+ if (!this._compactionAbortController) {
1416
+ this._compactionAbortController = new AbortController();
1417
+ this._emit({ type: "compaction_start", reason });
1418
+ }
1419
+ return this._compactionAbortController.signal;
1420
+ }
1421
+ _updateExtensionCompactionFeedback(options) {
1422
+ if (!this._compactionAbortController && !this._autoCompactionAbortController)
1423
+ return;
1424
+ this._emit({
1425
+ type: "compaction_progress",
1426
+ reason: options.reason,
1427
+ ...(options.delta !== undefined ? { delta: options.delta } : {}),
1428
+ ...(options.text !== undefined ? { text: options.text } : {}),
1429
+ });
1430
+ }
1431
+ _endExtensionCompactionFeedback(options) {
1432
+ const controller = this._compactionAbortController;
1433
+ if (!controller)
1434
+ return;
1435
+ const aborted = options.aborted ?? controller.signal.aborted;
1436
+ this._emit({
1437
+ type: "compaction_end",
1438
+ reason: options.reason,
1439
+ result: undefined,
1440
+ aborted,
1441
+ willRetry: false,
1442
+ errorMessage: aborted ? undefined : options.errorMessage,
1443
+ });
1444
+ this._compactionAbortController = undefined;
1445
+ }
1410
1446
  async _executeCompaction(request) {
1411
1447
  if (!this.model) {
1412
1448
  throw new Error(formatNoModelSelectedMessage());
@@ -1573,14 +1609,18 @@ export class AgentSession {
1573
1609
  shouldCompact(contextUsage.tokens, contextUsage.contextWindow, settings);
1574
1610
  if (isContextOverflow(assistantMessage, contextWindow) && (sameModel || currentContextNeedsCompaction)) {
1575
1611
  if (this._overflowRecoveryAttempted) {
1612
+ const errorMessage = "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.";
1576
1613
  this._emit({
1577
1614
  type: "compaction_end",
1578
1615
  reason: "overflow",
1579
1616
  result: undefined,
1580
1617
  aborted: false,
1581
1618
  willRetry: false,
1582
- errorMessage: "Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.",
1619
+ errorMessage,
1583
1620
  });
1621
+ if (requestReason === "pre_prompt") {
1622
+ throw new Error(errorMessage);
1623
+ }
1584
1624
  return;
1585
1625
  }
1586
1626
  this._overflowRecoveryAttempted = true;
@@ -1635,14 +1675,20 @@ export class AgentSession {
1635
1675
  this._emit({ type: "compaction_start", reason: "pre_prompt" });
1636
1676
  this._compactionAbortController = new AbortController();
1637
1677
  try {
1638
- await this._executeCompaction({
1678
+ const execution = await this._executeCompaction({
1639
1679
  reason: "pre_prompt",
1640
1680
  willRetry: false,
1641
1681
  lastAssistantMessage,
1642
1682
  skipAbortedCheck,
1643
1683
  });
1684
+ if (!execution.accepted && isContextOverflow(lastAssistantMessage, this.model?.contextWindow ?? 0)) {
1685
+ this._overflowRecoveryAttempted = false;
1686
+ }
1644
1687
  }
1645
1688
  catch (error) {
1689
+ if (isContextOverflow(lastAssistantMessage, this.model?.contextWindow ?? 0)) {
1690
+ this._overflowRecoveryAttempted = false;
1691
+ }
1646
1692
  const errorMessage = error instanceof Error ? error.message : "compaction failed";
1647
1693
  const aborted = errorMessage === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
1648
1694
  this._emit({
@@ -1666,6 +1712,8 @@ export class AgentSession {
1666
1712
  this._autoCompactionAbortController = new AbortController();
1667
1713
  try {
1668
1714
  if (!this.model) {
1715
+ if (reason === "overflow")
1716
+ this._overflowRecoveryAttempted = false;
1669
1717
  this._emit({
1670
1718
  type: "compaction_end",
1671
1719
  reason,
@@ -1677,6 +1725,8 @@ export class AgentSession {
1677
1725
  }
1678
1726
  const authResult = await this._modelRegistry.getApiKeyAndHeaders(this.model);
1679
1727
  if (!authResult.ok || !authResult.apiKey) {
1728
+ if (reason === "overflow")
1729
+ this._overflowRecoveryAttempted = false;
1680
1730
  this._emit({
1681
1731
  type: "compaction_end",
1682
1732
  reason,
@@ -1688,6 +1738,8 @@ export class AgentSession {
1688
1738
  }
1689
1739
  const preparation = prepareCompaction(this.sessionManager.getBranch(), this.settingsManager.getCompactionSettings());
1690
1740
  if (!preparation) {
1741
+ if (reason === "overflow")
1742
+ this._overflowRecoveryAttempted = false;
1691
1743
  this._emit({
1692
1744
  type: "compaction_end",
1693
1745
  reason,
@@ -1699,6 +1751,8 @@ export class AgentSession {
1699
1751
  }
1700
1752
  const execution = await this._executeCompaction({ reason, willRetry });
1701
1753
  if (!execution.accepted) {
1754
+ if (reason === "overflow")
1755
+ this._overflowRecoveryAttempted = false;
1702
1756
  return;
1703
1757
  }
1704
1758
  if (willRetry) {
@@ -1721,6 +1775,8 @@ export class AgentSession {
1721
1775
  }
1722
1776
  }
1723
1777
  catch (error) {
1778
+ if (reason === "overflow")
1779
+ this._overflowRecoveryAttempted = false;
1724
1780
  const errorMessage = error instanceof Error ? error.message : "compaction failed";
1725
1781
  const aborted = errorMessage === "Compaction cancelled" || (error instanceof Error && error.name === "AbortError");
1726
1782
  this._emit({
@@ -1943,6 +1999,9 @@ export class AgentSession {
1943
1999
  }
1944
2000
  })();
1945
2001
  },
2002
+ beginCompaction: (options) => this._beginExtensionCompactionFeedback(options.reason),
2003
+ updateCompaction: (options) => this._updateExtensionCompactionFeedback(options),
2004
+ endCompaction: (options) => this._endExtensionCompactionFeedback(options),
1946
2005
  getMessageRevision: () => this.getMessageRevision(),
1947
2006
  applyCompaction: (precomputed, options) => this.applyCompaction(precomputed, options),
1948
2007
  getSystemPrompt: () => this.systemPrompt,
@@ -2101,16 +2160,49 @@ export class AgentSession {
2101
2160
  * Context overflow errors are NOT retryable (handled by compaction instead).
2102
2161
  */
2103
2162
  _isRetryableError(message) {
2104
- if (message.stopReason !== "error" || !message.errorMessage)
2163
+ if (!message.errorMessage)
2105
2164
  return false;
2106
2165
  // Context overflow is handled by compaction, not retry
2107
2166
  const contextWindow = this.model?.contextWindow ?? 0;
2108
2167
  if (isContextOverflow(message, contextWindow))
2109
2168
  return false;
2110
2169
  const err = message.errorMessage;
2170
+ if (message.stopReason === "aborted") {
2171
+ return /timed? out|timeout/i.test(err);
2172
+ }
2173
+ if (message.stopReason !== "error")
2174
+ return false;
2111
2175
  // 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
2176
  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
2177
  }
2178
+ _getProviderRetryDelayMs(errorMessage) {
2179
+ const retryAfterMsMatch = errorMessage.match(/\bretry[-_ ]?after[-_ ]?ms\s*[:=]\s*(\d+(?:\.\d+)?)/i);
2180
+ if (retryAfterMsMatch) {
2181
+ const delayMs = Math.ceil(Number(retryAfterMsMatch[1]));
2182
+ return Number.isFinite(delayMs) && delayMs > 0 ? delayMs : undefined;
2183
+ }
2184
+ const retryAfterSecondsMatch = errorMessage.match(/\bretry[-_ ]?after\s*[:=]\s*(\d+(?:\.\d+)?)/i);
2185
+ if (retryAfterSecondsMatch) {
2186
+ const delayMs = Math.ceil(Number(retryAfterSecondsMatch[1]) * 1000);
2187
+ return Number.isFinite(delayMs) && delayMs > 0 ? delayMs : undefined;
2188
+ }
2189
+ 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);
2190
+ if (!retryInMatch) {
2191
+ return undefined;
2192
+ }
2193
+ const value = Number(retryInMatch[1]);
2194
+ if (!Number.isFinite(value) || value <= 0) {
2195
+ return undefined;
2196
+ }
2197
+ const unit = retryInMatch[2].toLowerCase();
2198
+ if (unit === "m" || unit.startsWith("min")) {
2199
+ return Math.ceil(value * 60_000);
2200
+ }
2201
+ if (unit.startsWith("s")) {
2202
+ return Math.ceil(value * 1000);
2203
+ }
2204
+ return Math.ceil(value);
2205
+ }
2114
2206
  /**
2115
2207
  * Handle retryable errors with exponential backoff.
2116
2208
  * @returns true if retry was initiated, false if max retries exceeded or disabled
@@ -2141,13 +2233,27 @@ export class AgentSession {
2141
2233
  this._resolveRetry(); // Resolve so waitForRetry() completes
2142
2234
  return false;
2143
2235
  }
2144
- const delayMs = settings.baseDelayMs * 2 ** (this._retryAttempt - 1);
2236
+ const errorMessage = message.errorMessage || "Unknown error";
2237
+ const providerDelayMs = this._getProviderRetryDelayMs(errorMessage);
2238
+ const maxRetryDelayMs = this.settingsManager.getProviderRetrySettings().maxRetryDelayMs;
2239
+ if (providerDelayMs !== undefined && providerDelayMs > maxRetryDelayMs) {
2240
+ this._emit({
2241
+ type: "auto_retry_end",
2242
+ success: false,
2243
+ attempt: this._retryAttempt,
2244
+ finalError: `Provider requested retry delay ${providerDelayMs}ms, exceeding configured maximum ${maxRetryDelayMs}ms`,
2245
+ });
2246
+ this._retryAttempt = 0;
2247
+ this._resolveRetry();
2248
+ return false;
2249
+ }
2250
+ const delayMs = providerDelayMs ?? settings.baseDelayMs * 2 ** (this._retryAttempt - 1);
2145
2251
  this._emit({
2146
2252
  type: "auto_retry_start",
2147
2253
  attempt: this._retryAttempt,
2148
2254
  maxAttempts: settings.maxRetries,
2149
2255
  delayMs,
2150
- errorMessage: message.errorMessage || "Unknown error",
2256
+ errorMessage,
2151
2257
  });
2152
2258
  // Remove error message from agent state (keep in session for history)
2153
2259
  const messages = this.agent.state.messages;