@code-yeongyu/senpi 2026.8.19 → 2026.8.20

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 (219) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/dist/cli-main.js +4 -0
  3. package/dist/cli-main.js.map +1 -1
  4. package/dist/cli.js +31 -3
  5. package/dist/cli.js.map +1 -1
  6. package/dist/compile-cache.d.ts +2 -0
  7. package/dist/compile-cache.d.ts.map +1 -0
  8. package/dist/compile-cache.js +33 -0
  9. package/dist/compile-cache.js.map +1 -0
  10. package/dist/core/agent-session.d.ts +5 -0
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +94 -12
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/compaction/compaction.d.ts +6 -0
  15. package/dist/core/compaction/compaction.d.ts.map +1 -1
  16. package/dist/core/compaction/compaction.js +13 -0
  17. package/dist/core/compaction/compaction.js.map +1 -1
  18. package/dist/core/cursor-exec-bridge-session.d.ts +2 -1
  19. package/dist/core/cursor-exec-bridge-session.d.ts.map +1 -1
  20. package/dist/core/cursor-exec-bridge-session.js +5 -2
  21. package/dist/core/cursor-exec-bridge-session.js.map +1 -1
  22. package/dist/core/cursor-exec-bridge.d.ts +9 -1
  23. package/dist/core/cursor-exec-bridge.d.ts.map +1 -1
  24. package/dist/core/cursor-exec-bridge.js +26 -2
  25. package/dist/core/cursor-exec-bridge.js.map +1 -1
  26. package/dist/core/extensions/builtin/claude-sdk-oauth/custom-tools.d.ts +1 -1
  27. package/dist/core/extensions/builtin/claude-sdk-oauth/custom-tools.d.ts.map +1 -1
  28. package/dist/core/extensions/builtin/claude-sdk-oauth/custom-tools.js +5 -2
  29. package/dist/core/extensions/builtin/claude-sdk-oauth/custom-tools.js.map +1 -1
  30. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.d.ts +6 -4
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.js +24 -2
  33. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.js.map +1 -1
  34. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.lazy.d.ts +39 -0
  35. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.lazy.d.ts.map +1 -0
  36. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.lazy.js +31 -0
  37. package/dist/core/extensions/builtin/claude-sdk-oauth/sdk-boundary.lazy.js.map +1 -0
  38. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts +2 -0
  39. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts.map +1 -1
  40. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js +35 -0
  41. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js.map +1 -1
  42. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.d.ts +1 -1
  43. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.d.ts.map +1 -1
  44. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.js +1 -0
  45. package/dist/core/extensions/builtin/claude-sdk-oauth/session-observability.js.map +1 -1
  46. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts +8 -0
  47. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.d.ts.map +1 -1
  48. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js +4 -1
  49. package/dist/core/extensions/builtin/claude-sdk-oauth/session-reattach.js.map +1 -1
  50. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.d.ts.map +1 -1
  51. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js +28 -1
  52. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js.map +1 -1
  53. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.d.ts.map +1 -1
  54. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.js +5 -2
  55. package/dist/core/extensions/builtin/claude-sdk-oauth/stream.js.map +1 -1
  56. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  57. package/dist/core/extensions/builtin/compaction/index.js +5 -0
  58. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  59. package/dist/core/extensions/builtin/compaction/log.d.ts +1 -1
  60. package/dist/core/extensions/builtin/compaction/log.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/compaction/log.js +1 -0
  62. package/dist/core/extensions/builtin/compaction/log.js.map +1 -1
  63. package/dist/core/extensions/builtin/compaction/speculative.d.ts +1 -0
  64. package/dist/core/extensions/builtin/compaction/speculative.d.ts.map +1 -1
  65. package/dist/core/extensions/builtin/compaction/speculative.js +4 -0
  66. package/dist/core/extensions/builtin/compaction/speculative.js.map +1 -1
  67. package/dist/core/extensions/builtin/config-reload/index.d.ts.map +1 -1
  68. package/dist/core/extensions/builtin/config-reload/index.js +13 -10
  69. package/dist/core/extensions/builtin/config-reload/index.js.map +1 -1
  70. package/dist/core/extensions/builtin/config-reload/watch-engine.d.ts +8 -1
  71. package/dist/core/extensions/builtin/config-reload/watch-engine.d.ts.map +1 -1
  72. package/dist/core/extensions/builtin/config-reload/watch-engine.js +98 -18
  73. package/dist/core/extensions/builtin/config-reload/watch-engine.js.map +1 -1
  74. package/dist/core/extensions/builtin/config-reload/watch-event-source.d.ts +1 -1
  75. package/dist/core/extensions/builtin/config-reload/watch-event-source.d.ts.map +1 -1
  76. package/dist/core/extensions/builtin/config-reload/watch-event-source.js +7 -2
  77. package/dist/core/extensions/builtin/config-reload/watch-event-source.js.map +1 -1
  78. package/dist/core/extensions/builtin/goal/elapsed-ticker.d.ts.map +1 -1
  79. package/dist/core/extensions/builtin/goal/elapsed-ticker.js +15 -1
  80. package/dist/core/extensions/builtin/goal/elapsed-ticker.js.map +1 -1
  81. package/dist/core/extensions/builtin/goal/index.d.ts.map +1 -1
  82. package/dist/core/extensions/builtin/goal/index.js +7 -22
  83. package/dist/core/extensions/builtin/goal/index.js.map +1 -1
  84. package/dist/core/extensions/builtin/goal/stale-context.d.ts +9 -0
  85. package/dist/core/extensions/builtin/goal/stale-context.d.ts.map +1 -0
  86. package/dist/core/extensions/builtin/goal/stale-context.js +11 -0
  87. package/dist/core/extensions/builtin/goal/stale-context.js.map +1 -0
  88. package/dist/core/extensions/builtin/goal/wait-ticker.d.ts +8 -0
  89. package/dist/core/extensions/builtin/goal/wait-ticker.d.ts.map +1 -1
  90. package/dist/core/extensions/builtin/goal/wait-ticker.js +37 -3
  91. package/dist/core/extensions/builtin/goal/wait-ticker.js.map +1 -1
  92. package/dist/core/extensions/builtin/mcp/index.d.ts.map +1 -1
  93. package/dist/core/extensions/builtin/mcp/index.js +12 -1
  94. package/dist/core/extensions/builtin/mcp/index.js.map +1 -1
  95. package/dist/core/extensions/builtin/prompt-url-widget.d.ts.map +1 -1
  96. package/dist/core/extensions/builtin/prompt-url-widget.js +7 -15
  97. package/dist/core/extensions/builtin/prompt-url-widget.js.map +1 -1
  98. package/dist/core/extensions/builtin/rules/ui/rules-banner.d.ts.map +1 -1
  99. package/dist/core/extensions/builtin/rules/ui/rules-banner.js +21 -25
  100. package/dist/core/extensions/builtin/rules/ui/rules-banner.js.map +1 -1
  101. package/dist/core/extensions/builtin/todotools/index.d.ts.map +1 -1
  102. package/dist/core/extensions/builtin/todotools/index.js +20 -0
  103. package/dist/core/extensions/builtin/todotools/index.js.map +1 -1
  104. package/dist/core/extensions/builtin/todotools/native-todo-mirror.d.ts +3 -0
  105. package/dist/core/extensions/builtin/todotools/native-todo-mirror.d.ts.map +1 -0
  106. package/dist/core/extensions/builtin/todotools/native-todo-mirror.js +22 -0
  107. package/dist/core/extensions/builtin/todotools/native-todo-mirror.js.map +1 -0
  108. package/dist/core/extensions/builtin/webfetch/webfetch/content.lazy.d.ts +17 -0
  109. package/dist/core/extensions/builtin/webfetch/webfetch/content.lazy.d.ts.map +1 -0
  110. package/dist/core/extensions/builtin/webfetch/webfetch/content.lazy.js +22 -0
  111. package/dist/core/extensions/builtin/webfetch/webfetch/content.lazy.js.map +1 -0
  112. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js +3 -3
  113. package/dist/core/extensions/builtin/webfetch/webfetch/tool.js.map +1 -1
  114. package/dist/core/messages.d.ts +9 -2
  115. package/dist/core/messages.d.ts.map +1 -1
  116. package/dist/core/messages.js +13 -18
  117. package/dist/core/messages.js.map +1 -1
  118. package/dist/core/retry-fallback/hint-policy.d.ts +3 -2
  119. package/dist/core/retry-fallback/hint-policy.d.ts.map +1 -1
  120. package/dist/core/retry-fallback/hint-policy.js +19 -8
  121. package/dist/core/retry-fallback/hint-policy.js.map +1 -1
  122. package/dist/core/retry-fallback/settings.js.map +1 -1
  123. package/dist/core/session-discovery.d.ts +16 -0
  124. package/dist/core/session-discovery.d.ts.map +1 -0
  125. package/dist/core/session-discovery.js +101 -0
  126. package/dist/core/session-discovery.js.map +1 -0
  127. package/dist/core/session-manager.d.ts +2 -1
  128. package/dist/core/session-manager.d.ts.map +1 -1
  129. package/dist/core/session-manager.js +4 -170
  130. package/dist/core/session-manager.js.map +1 -1
  131. package/dist/core/session-record.d.ts +14 -0
  132. package/dist/core/session-record.d.ts.map +1 -0
  133. package/dist/core/session-record.js +47 -0
  134. package/dist/core/session-record.js.map +1 -0
  135. package/dist/core/session-summary-cache.d.ts +40 -0
  136. package/dist/core/session-summary-cache.d.ts.map +1 -0
  137. package/dist/core/session-summary-cache.js +79 -0
  138. package/dist/core/session-summary-cache.js.map +1 -0
  139. package/dist/core/session-summary-lru.d.ts +54 -0
  140. package/dist/core/session-summary-lru.d.ts.map +1 -0
  141. package/dist/core/session-summary-lru.js +72 -0
  142. package/dist/core/session-summary-lru.js.map +1 -0
  143. package/dist/core/session-summary.d.ts +29 -0
  144. package/dist/core/session-summary.d.ts.map +1 -0
  145. package/dist/core/session-summary.js +90 -0
  146. package/dist/core/session-summary.js.map +1 -0
  147. package/dist/index.d.ts +1 -0
  148. package/dist/index.d.ts.map +1 -1
  149. package/dist/index.js +2 -0
  150. package/dist/index.js.map +1 -1
  151. package/dist/inspector-policy.d.ts +6 -0
  152. package/dist/inspector-policy.d.ts.map +1 -1
  153. package/dist/inspector-policy.js +6 -1
  154. package/dist/inspector-policy.js.map +1 -1
  155. package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -1
  156. package/dist/modes/interactive/components/earendil-announcement.js +8 -8
  157. package/dist/modes/interactive/components/earendil-announcement.js.map +1 -1
  158. package/dist/modes/interactive/components/progressive-transcript-container.d.ts +72 -0
  159. package/dist/modes/interactive/components/progressive-transcript-container.d.ts.map +1 -0
  160. package/dist/modes/interactive/components/progressive-transcript-container.js +150 -0
  161. package/dist/modes/interactive/components/progressive-transcript-container.js.map +1 -0
  162. package/dist/modes/interactive/components/status-indicator.d.ts +1 -1
  163. package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -1
  164. package/dist/modes/interactive/components/status-indicator.js +8 -2
  165. package/dist/modes/interactive/components/status-indicator.js.map +1 -1
  166. package/dist/modes/interactive/interactive-mode.d.ts +4 -0
  167. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  168. package/dist/modes/interactive/interactive-mode.js +127 -65
  169. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  170. package/dist/modes/interactive/split-trailing-assistant-text.d.ts +7 -0
  171. package/dist/modes/interactive/split-trailing-assistant-text.d.ts.map +1 -0
  172. package/dist/modes/interactive/split-trailing-assistant-text.js +19 -0
  173. package/dist/modes/interactive/split-trailing-assistant-text.js.map +1 -0
  174. package/dist/senpi +31 -3
  175. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +12 -0
  176. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  177. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  178. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +38 -3
  179. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  180. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts +8 -1
  181. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts.map +1 -1
  182. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js +25 -1
  183. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js.map +1 -1
  184. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  185. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +2 -0
  186. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  187. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +284 -209
  188. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  189. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.d.ts +37 -0
  190. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.d.ts.map +1 -0
  191. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.js +110 -0
  192. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-conversation-rotation.js.map +1 -0
  193. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-task-args.d.ts +3 -0
  194. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-task-args.d.ts.map +1 -0
  195. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-task-args.js +17 -0
  196. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-task-args.js.map +1 -0
  197. package/node_modules/@earendil-works/pi-ai/dist/cursor/selection-descriptor.d.ts.map +1 -1
  198. package/node_modules/@earendil-works/pi-ai/dist/cursor/selection-descriptor.js +27 -11
  199. package/node_modules/@earendil-works/pi-ai/dist/cursor/selection-descriptor.js.map +1 -1
  200. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  201. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  202. package/node_modules/@earendil-works/pi-ai/dist/providers/data/baseten.json +1 -1
  203. package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
  204. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  205. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  206. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  207. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  208. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.d.ts.map +1 -1
  209. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.js +2 -0
  210. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/index.js.map +1 -1
  211. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +30 -0
  212. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  213. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +33 -0
  214. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  215. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  216. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  217. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  218. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  219. package/package.json +7 -7
@@ -18,7 +18,7 @@ import { basename, dirname } from "node:path";
18
18
  import { isDeepStrictEqual } from "node:util";
19
19
  import { prepareAgentToolCall } from "@earendil-works/pi-agent-core";
20
20
  import { contentText, SERVER_FALLBACK_ABORTED_DIAGNOSTIC } from "@earendil-works/pi-ai";
21
- import { cleanupSessionResources, isClassifierRefusal, isContextOverflow, isProviderStreamStallError, isProviderTimeoutError, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, streamSimple, } from "@earendil-works/pi-ai/compat";
21
+ import { cleanupSessionResources, cursorOverflowCompactionSettings, isClassifierRefusal, isContextOverflow, isCursorPayloadResourceExhausted, isCursorZeroTokenResourceExhausted, isProviderStreamStallError, isProviderTimeoutError, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, shouldRetryOverflowWithoutCompact, streamSimple, } from "@earendil-works/pi-ai/compat";
22
22
  import { extract429RetryAfterMs, parseRetryAfterMsMarker } from "@earendil-works/pi-ai/utils/retry-hint";
23
23
  import { getAgentDir } from "../config.js";
24
24
  import { getThemeByName, theme } from "../modes/interactive/theme/theme.js";
@@ -31,7 +31,7 @@ import { AgentSettledDelivery } from "./agent-settled-delivery.js";
31
31
  import { formatNoApiKeyFoundMessage, formatNoModelSelectedMessage } from "./auth-guidance.js";
32
32
  import { executeBashWithOperations } from "./bash-executor.js";
33
33
  import { envValue } from "./brand.js";
34
- import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
34
+ import { calculateContextTokens, collectEntriesForBranchSummary, compact, estimateContextTokens, estimateTokens, generateBranchSummary, prepareCompaction, resolveThresholdContextTokens, shouldCompact, } from "./compaction/index.js";
35
35
  import { CompactionLifecycleCoordinator } from "./compaction/lifecycle.js";
36
36
  import { isWarmSummaryAnchorValid } from "./compaction/warm-anchor.js";
37
37
  import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
@@ -349,6 +349,7 @@ export class AgentSession {
349
349
  this._compactionLifecycle = new CompactionLifecycleCoordinator();
350
350
  this._sessionWorkBarrier = new SessionWorkBarrier();
351
351
  this._overflowRecoveryAttempted = false;
352
+ this._compactionSkippedTooSmall = false;
352
353
  // A retry continuation immediately follows an accepted compaction. Its first
353
354
  // response must not retrigger threshold compaction from stale provider usage.
354
355
  this._skipNextPostRetryCompactionCheck = false;
@@ -719,6 +720,12 @@ export class AgentSession {
719
720
  // completed turn with no continuation keeps pre-PR timing, while the
720
721
  // prior prepare callback and context refresh below still run every turn.
721
722
  const compactBeforeNextAdmission = async () => {
723
+ const provider = this.model?.provider;
724
+ // Cursor rebuilds the full conversation each hop. Compacting here
725
+ // mutates rootPrompt mid-run and poisons conversationId.
726
+ if (provider === "cursor" || provider === "cursor-cli-oauth") {
727
+ return false;
728
+ }
722
729
  if (turn.toolResults.length === 0 && !this.agent.hasQueuedMessages()) {
723
730
  return false;
724
731
  }
@@ -1061,7 +1068,9 @@ export class AgentSession {
1061
1068
  }
1062
1069
  const lastAssistant = this._findLastAssistantInMessages(event.messages);
1063
1070
  if (!lastAssistant ||
1064
- (!this._isRetryableError(lastAssistant) && !this._isHardErrorFallbackEligible(lastAssistant))) {
1071
+ (!this._isRetryableError(lastAssistant) &&
1072
+ !this._isHardErrorFallbackEligible(lastAssistant) &&
1073
+ !isCursorZeroTokenResourceExhausted(lastAssistant))) {
1065
1074
  return;
1066
1075
  }
1067
1076
  this._retryPromise = new Promise((resolve) => {
@@ -1119,7 +1128,7 @@ export class AgentSession {
1119
1128
  */
1120
1129
  _resolveThresholdContextTokens(directContextTokens) {
1121
1130
  const messages = filterContextExcludedMessages(this.agent.state.messages);
1122
- return Math.max(directContextTokens, estimateMessagesTokens(messages));
1131
+ return resolveThresholdContextTokens(directContextTokens, estimateMessagesTokens(messages));
1123
1132
  }
1124
1133
  _getAutoCompactionReason(message) {
1125
1134
  const settings = this.settingsManager.getCompactionSettings();
@@ -1138,6 +1147,9 @@ export class AgentSession {
1138
1147
  if (isContextOverflow(message, model.contextWindow) && (sameModel || currentContextNeedsCompaction)) {
1139
1148
  return "overflow";
1140
1149
  }
1150
+ if (this._isCursorPayloadOverflow(message)) {
1151
+ return "overflow";
1152
+ }
1141
1153
  let contextTokens;
1142
1154
  const directContextTokens = message.usage ? calculateContextTokens(message.usage) : 0;
1143
1155
  if (message.stopReason !== "error" && directContextTokens !== 0) {
@@ -1225,6 +1237,9 @@ export class AgentSession {
1225
1237
  return false;
1226
1238
  }
1227
1239
  const retryableError = this._isRetryableError(lastAssistant);
1240
+ if (isCursorZeroTokenResourceExhausted(lastAssistant)) {
1241
+ return true;
1242
+ }
1228
1243
  if (!retryableError && this._isHardErrorFallbackEligible(lastAssistant)) {
1229
1244
  return true;
1230
1245
  }
@@ -1369,22 +1384,26 @@ export class AgentSession {
1369
1384
  // Retry transient failures normally and eligible hard errors only through a fallback.
1370
1385
  const retryableError = this._isRetryableError(msg);
1371
1386
  const hardErrorFallbackEligible = this._isHardErrorFallbackEligible(msg);
1387
+ const cursorZeroTokenRe = isCursorZeroTokenResourceExhausted(msg);
1372
1388
  const retryCanAdmitProvider = !userAbortSuppressedQueuedContinuation &&
1373
1389
  this.settingsManager.getRetrySettings().enabled &&
1374
- (retryableError || hardErrorFallbackEligible);
1390
+ (retryableError || hardErrorFallbackEligible || cursorZeroTokenRe);
1375
1391
  let compactedBeforeRetry = false;
1376
1392
  if (retryCanAdmitProvider &&
1377
1393
  requiredAutoCompaction &&
1378
1394
  !(requiredAutoCompaction === "threshold" && this._hasPendingPostCompactionUsageExemption(msg))) {
1379
1395
  this._retireFailedRetryAssistant(msg);
1380
- compactedBeforeRetry = await this._runPrePromptCompaction(msg, true, "threshold", true);
1396
+ compactedBeforeRetry = await this._runPrePromptCompaction(msg, true, requiredAutoCompaction, true);
1381
1397
  retryContinuationBlocked = !compactedBeforeRetry && !this._isCompactionDelegated();
1382
1398
  }
1383
1399
  let retryOutcome = "not-handled";
1384
1400
  const retryOwnedDeferredQueue = DEFERRED_RETRY_QUEUE_OWNERS.has(this);
1385
1401
  DEFERRED_RETRY_QUEUE_OWNERS.delete(this);
1386
1402
  if (!retryContinuationBlocked && !userAbortSuppressedQueuedContinuation) {
1387
- if (retryableError) {
1403
+ if (cursorZeroTokenRe) {
1404
+ retryOutcome = await this._handleRetryableError(msg, { sameModelRemint: true });
1405
+ }
1406
+ else if (retryableError) {
1388
1407
  retryOutcome = await this._handleRetryableError(msg);
1389
1408
  }
1390
1409
  else if (hardErrorFallbackEligible) {
@@ -2014,6 +2033,15 @@ export class AgentSession {
2014
2033
  getRegisteredTool(name) {
2015
2034
  return this._toolRegistry.get(name);
2016
2035
  }
2036
+ /** Cursor exec already ran the tool; still emit tool_result so plan-touch trackers see .omo/plans writes. */
2037
+ async emitExecBridgeToolResult(toolName, toolCallId, args, result, isError) {
2038
+ await this._emitAfterToolCallHooks({
2039
+ type: "toolCall",
2040
+ id: toolCallId,
2041
+ name: toolName,
2042
+ arguments: args && typeof args === "object" && !Array.isArray(args) ? args : {},
2043
+ }, args, result, isError);
2044
+ }
2017
2045
  setActiveToolsByName(toolNames) {
2018
2046
  const tools = [];
2019
2047
  const validToolNames = [];
@@ -2743,7 +2771,7 @@ export class AgentSession {
2743
2771
  }
2744
2772
  async _generateSessionTitle(firstPrompt, model, abortController) {
2745
2773
  try {
2746
- const auth = await this._getCompactionRequestAuth(model);
2774
+ const auth = await this._getSummarizationRequestAuth(model);
2747
2775
  const title = await generateSessionTitle({
2748
2776
  firstPrompt,
2749
2777
  model,
@@ -3885,7 +3913,7 @@ export class AgentSession {
3885
3913
  throw new CompactionCancelledError();
3886
3914
  }
3887
3915
  const pathEntries = this.sessionManager.getBranch();
3888
- const settings = this.settingsManager.getCompactionSettings();
3916
+ const settings = cursorOverflowCompactionSettings(this.settingsManager.getCompactionSettings(), this.model?.provider, request.reason);
3889
3917
  let compactionResult = request.precomputed;
3890
3918
  let fromExtension = request.precomputed !== undefined;
3891
3919
  if (!compactionResult) {
@@ -4284,7 +4312,8 @@ export class AgentSession {
4284
4312
  shouldCompact(contextUsage.tokens, contextUsage.contextWindow, settings);
4285
4313
  const recoverableLength = sameModel && isRecoverableLength(assistantMessage, this.model?.maxTokens ?? 0);
4286
4314
  const isOverflow = (isContextOverflow(assistantMessage, contextWindow) && (sameModel || currentContextNeedsCompaction)) ||
4287
- recoverableLength;
4315
+ recoverableLength ||
4316
+ this._isCursorPayloadOverflow(assistantMessage);
4288
4317
  if (isOverflow &&
4289
4318
  assistantMessage.stopReason === "stop" &&
4290
4319
  this._consumePostCompactionUsageExemption(assistantMessage)) {
@@ -4343,6 +4372,14 @@ export class AgentSession {
4343
4372
  this._incrementMessageRevision();
4344
4373
  }
4345
4374
  if (!compacted && inlineReason && !this._isCompactionDelegated() && !this._hasSupersedingCompactionClaim()) {
4375
+ if (this._compactionSkippedTooSmall) {
4376
+ this._compactionSkippedTooSmall = false;
4377
+ const provider = this.model?.provider;
4378
+ if (provider === "cursor" || provider === "cursor-cli-oauth") {
4379
+ this._truncateAgentMessagesToLastUserTurn();
4380
+ }
4381
+ return true;
4382
+ }
4346
4383
  throw new RequiredCompactionError();
4347
4384
  }
4348
4385
  return compacted;
@@ -4462,6 +4499,7 @@ export class AgentSession {
4462
4499
  this._overflowRecoveryAttempted = false;
4463
4500
  }
4464
4501
  const errorMessage = error instanceof Error ? error.message : "compaction failed";
4502
+ this._compactionSkippedTooSmall = shouldRetryOverflowWithoutCompact(false, errorMessage);
4465
4503
  const aborted = isCompactionExecutionAborted(error);
4466
4504
  this._emit({
4467
4505
  type: "compaction_end",
@@ -4645,7 +4683,7 @@ export class AgentSession {
4645
4683
  }
4646
4684
  if (!this._ownsCompactionController(autoCompactionController, "auto"))
4647
4685
  return false;
4648
- const preparation = prepareCompaction(this.sessionManager.getBranch(), this.settingsManager.getCompactionSettings(), reason === "overflow");
4686
+ const preparation = prepareCompaction(this.sessionManager.getBranch(), cursorOverflowCompactionSettings(this.settingsManager.getCompactionSettings(), this.model?.provider, reason), reason === "overflow");
4649
4687
  if (!preparation) {
4650
4688
  return endBeforeExecution();
4651
4689
  }
@@ -5388,10 +5426,31 @@ export class AgentSession {
5388
5426
  }
5389
5427
  return isRetryableAssistantError(message);
5390
5428
  }
5429
+ _isCursorPayloadOverflow(message) {
5430
+ return isCursorPayloadResourceExhausted(message, 0);
5431
+ }
5432
+ _truncateAgentMessagesToLastUserTurn() {
5433
+ const messages = this.agent?.state?.messages;
5434
+ if (!Array.isArray(messages) || messages.length === 0)
5435
+ return false;
5436
+ let lastUser = -1;
5437
+ for (let i = messages.length - 1; i >= 0; i--) {
5438
+ if (messages[i]?.role === "user") {
5439
+ lastUser = i;
5440
+ break;
5441
+ }
5442
+ }
5443
+ if (lastUser <= 0)
5444
+ return false;
5445
+ this.agent.state.messages = messages.slice(lastUser);
5446
+ return true;
5447
+ }
5391
5448
  _isHardErrorFallbackEligible(message) {
5392
5449
  return (!message.errorMessage?.startsWith(TURN_RETRY_SUPPRESSION_PREFIX) &&
5393
5450
  message.stopReason === "error" &&
5394
5451
  !isContextOverflow(message, this.model?.contextWindow ?? 0) &&
5452
+ !this._isCursorPayloadOverflow(message) &&
5453
+ !isCursorZeroTokenResourceExhausted(message) &&
5395
5454
  !isClassifierRefusal(message) &&
5396
5455
  !message.content.some((content) => content.type === "toolCall") &&
5397
5456
  this._retryFallback.canTryFallback());
@@ -5495,10 +5554,28 @@ export class AgentSession {
5495
5554
  const errorMessage = message.errorMessage || "Unknown error";
5496
5555
  const isRefusal = isClassifierRefusal(message);
5497
5556
  const hardErrorFallback = options.hardErrorFallback === true;
5557
+ const sameModelRemint = options.sameModelRemint === true;
5498
5558
  let switchedFallback = false;
5499
5559
  let is429TierRouted = false;
5500
5560
  let hintTierDelayMs;
5501
- if (hardErrorFallback) {
5561
+ if (sameModelRemint) {
5562
+ this._retryAttempt++;
5563
+ if (this._retryAttempt > settings.maxRetries) {
5564
+ if (this._retryAttempt > 1) {
5565
+ this._emit({
5566
+ type: "auto_retry_end",
5567
+ success: false,
5568
+ attempt: this._retryAttempt,
5569
+ finalError: message.errorMessage,
5570
+ });
5571
+ }
5572
+ this._retryAttempt = 0;
5573
+ this._resetHintTierState();
5574
+ this._resolveRetry();
5575
+ return "not-handled";
5576
+ }
5577
+ }
5578
+ else if (hardErrorFallback) {
5502
5579
  // A non-retryable provider failure must never replay on the same model.
5503
5580
  // Billing-class failures never recover on this account, so the fallback
5504
5581
  // switch pins as the session model instead of reverting after the cooldown.
@@ -5563,6 +5640,9 @@ export class AgentSession {
5563
5640
  // because a stall carries no rate-limit markers or retry-after hint.
5564
5641
  const stallError = isProviderStreamStallError(message);
5565
5642
  // 429-class detection: retryable AND message carries rate-limit markers.
5643
+ // Every same-model wait derived below is floored by the exponential schedule
5644
+ // inside the pure hint policy, so repeated tiny retry-after hints cannot pin
5645
+ // the cadence at a few milliseconds. Do not recompute that floor here.
5566
5646
  const is429Class = !stallError &&
5567
5647
  /rate.?limit|(?:^429(?=\s+\{)|(?:\bHTTP\/1\.[01]\s+|\bHTTP\s+|\bstatus(?:\s+code)?\s+|\berror\s+|\bcode\s+)429\b)|too many requests|resource.?exhausted/i.test(errorMessage);
5568
5648
  if (is429Class) {
@@ -5753,6 +5833,8 @@ export class AgentSession {
5753
5833
  // reach this point with a fallback already applied (hard-error, refusal) set
5754
5834
  // switchedFallback first and force providerDelayMs undefined, so no branch may
5755
5835
  // be reordered to fall through here expecting an implicit switch.
5836
+ // 429-tier delays already carry the exponential floor from nextInTurnDelayMs /
5837
+ // degradeWithoutFallback; the non-tier branch keeps its own exponential fallback.
5756
5838
  const nonTierProviderDelayMs = providerDelayMs === 0 ? undefined : providerDelayMs;
5757
5839
  const delayMs = switchedFallback
5758
5840
  ? 0