@bitkyc08/opencodex 2.52.0-preview.20260912 → 2.53.0-preview.20260913

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 (236) hide show
  1. package/gui/dist/assets/index-BBOZWGB6.css +1 -0
  2. package/gui/dist/assets/index-D7ynYo2K.js +128 -0
  3. package/gui/dist/index.html +2 -2
  4. package/native/remote-workspace-helper/Cargo.lock +130 -0
  5. package/native/remote-workspace-helper/Cargo.toml +24 -0
  6. package/native/remote-workspace-helper/src/main.rs +49 -0
  7. package/native/remote-workspace-helper/src/protocol.rs +246 -0
  8. package/native/remote-workspace-helper/src/sandbox/macos.rs +19 -0
  9. package/native/remote-workspace-helper/src/sandbox/mod.rs +77 -0
  10. package/native/remote-workspace-helper/src/sandbox/windows.rs +15 -0
  11. package/package.json +6 -1
  12. package/src/adapters/anthropic-image-normalize.ts +30 -2
  13. package/src/adapters/anthropic.ts +1 -1
  14. package/src/adapters/base.ts +8 -2
  15. package/src/adapters/cursor/cursor-errors.ts +12 -0
  16. package/src/adapters/cursor/thread-continuity.ts +93 -0
  17. package/src/adapters/cursor.ts +104 -73
  18. package/src/adapters/devin/cloud-direct/chat.ts +312 -23
  19. package/src/adapters/devin/cloud-direct/metadata.ts +31 -2
  20. package/src/adapters/devin/live-models.ts +70 -3
  21. package/src/adapters/devin.ts +281 -21
  22. package/src/adapters/google-wire-compiler.ts +14 -6
  23. package/src/adapters/google.ts +22 -8
  24. package/src/adapters/kiro/adapter.ts +316 -0
  25. package/src/adapters/kiro/conversation.ts +136 -0
  26. package/src/adapters/kiro/payload.ts +432 -0
  27. package/src/adapters/kiro/reasoning.ts +56 -0
  28. package/src/adapters/kiro/stream.ts +1153 -0
  29. package/src/adapters/kiro/usage.ts +223 -0
  30. package/src/adapters/kiro/wire.ts +76 -0
  31. package/src/adapters/kiro.ts +8 -2319
  32. package/src/adapters/mimo-free.ts +1 -1
  33. package/src/adapters/openai-chat-images.ts +101 -0
  34. package/src/adapters/openai-chat.ts +201 -181
  35. package/src/adapters/openai-responses.ts +92 -224
  36. package/src/adapters/registry.ts +0 -7
  37. package/src/adapters/run-turn-queue.ts +13 -6
  38. package/src/bridge.ts +14 -15
  39. package/src/chat/inbound.ts +29 -4
  40. package/src/chat/outbound.ts +145 -107
  41. package/src/claude/desktop-profile.ts +4 -6
  42. package/src/cli/account-api.ts +14 -0
  43. package/src/cli/account-extended.ts +1 -1
  44. package/src/cli/account-history.ts +60 -0
  45. package/src/cli/account-main.ts +80 -0
  46. package/src/cli/account.ts +11 -3
  47. package/src/cli/capabilities.ts +113 -0
  48. package/src/cli/catalog.ts +109 -0
  49. package/src/cli/dispatch.ts +9 -0
  50. package/src/cli/help.ts +2 -0
  51. package/src/cli/index.ts +2 -2
  52. package/src/cli/observe.ts +28 -1
  53. package/src/cli/opencode.ts +42 -8
  54. package/src/cli/provider-runtime.ts +11 -1
  55. package/src/cli/provider.ts +22 -2
  56. package/src/cli/registry.ts +21 -0
  57. package/src/cli/remote-workspace.ts +154 -0
  58. package/src/cli/status.ts +39 -7
  59. package/src/cli/usage-report.ts +14 -2
  60. package/src/client/hub-client.ts +34 -0
  61. package/src/client/hub-state.ts +9 -1
  62. package/src/codex/account-store.ts +78 -0
  63. package/src/codex/auth-api.ts +81 -54
  64. package/src/codex/auth-context.ts +45 -16
  65. package/src/codex/catalog/effort.ts +1 -1
  66. package/src/codex/catalog/metadata.ts +3 -6
  67. package/src/codex/catalog/native-models.ts +4 -4
  68. package/src/codex/catalog/parsing.ts +2 -20
  69. package/src/codex/catalog/provider-fetch.ts +10 -1
  70. package/src/codex/catalog/remote.ts +233 -0
  71. package/src/codex/catalog/sync.ts +403 -35
  72. package/src/codex/convergence.ts +1 -1
  73. package/src/codex/history-manifest.ts +36 -0
  74. package/src/codex/history-provider.ts +32 -5
  75. package/src/codex/inject.ts +9 -0
  76. package/src/codex/main-account.ts +113 -0
  77. package/src/codex/main-device-reauth-api.ts +89 -0
  78. package/src/codex/main-device-reauth.ts +217 -0
  79. package/src/codex/native-residue.ts +9 -2
  80. package/src/codex/quota-auto-refresh.ts +3 -2
  81. package/src/codex/quota-capacity.ts +98 -0
  82. package/src/codex/quota-history.ts +160 -0
  83. package/src/codex/quota-types.ts +8 -0
  84. package/src/codex/quota.ts +118 -91
  85. package/src/codex/refresh.ts +2 -1
  86. package/src/codex/routing.ts +90 -17
  87. package/src/codex/sync.ts +33 -4
  88. package/src/combos/request.ts +19 -1
  89. package/src/config/multi-agent-surface.ts +61 -0
  90. package/src/config/provider-validation.ts +176 -0
  91. package/src/config.ts +213 -11
  92. package/src/generated/compatibility-version.json +436 -168
  93. package/src/images/loop.ts +119 -36
  94. package/src/lib/admission.ts +12 -6
  95. package/src/lib/redact.ts +7 -0
  96. package/src/lib/translator-budget.ts +4 -3
  97. package/src/lib/windows-atomic-replace.ts +1 -0
  98. package/src/lib/windows-elevation.ts +1 -1
  99. package/src/oauth/chatgpt-device.ts +62 -5
  100. package/src/oauth/devin/cli-import.ts +130 -0
  101. package/src/oauth/devin.ts +63 -8
  102. package/src/oauth/index.ts +29 -14
  103. package/src/oauth/kiro.ts +18 -6
  104. package/src/oauth/login-cli.ts +9 -1
  105. package/src/oauth/meta-muse-device.ts +464 -0
  106. package/src/oauth/meta-muse.ts +123 -32
  107. package/src/oauth/pool-kernel.ts +9 -0
  108. package/src/oauth/pool-settings-capability.ts +2 -2
  109. package/src/oauth/store.ts +57 -0
  110. package/src/oauth/types.ts +31 -0
  111. package/src/providers/derive.ts +13 -3
  112. package/src/providers/devin-cli-authmode-migration.ts +57 -35
  113. package/src/providers/devin-provider-merge-migration.ts +240 -0
  114. package/src/providers/muse-key-quota.ts +117 -0
  115. package/src/providers/muse-subscription-usage.ts +14 -2
  116. package/src/providers/openai-sidecar.ts +25 -3
  117. package/src/providers/opencode-zen-rate-limit.ts +58 -0
  118. package/src/providers/provider-id-rewrite.ts +20 -5
  119. package/src/providers/quota-types.ts +12 -0
  120. package/src/providers/quota.ts +143 -102
  121. package/src/providers/reasoning-metadata.ts +543 -0
  122. package/src/providers/registry.ts +80 -49
  123. package/src/reasoning-effort.ts +26 -2
  124. package/src/remote/hub-usage.ts +32 -0
  125. package/src/remote-control/index.ts +192 -41
  126. package/src/remote-control/workspace-activation.ts +9 -0
  127. package/src/remote-control/workspace-agent-connection.ts +366 -0
  128. package/src/remote-control/workspace-claude-runtime.ts +243 -0
  129. package/src/remote-control/workspace-codex-runtime.ts +531 -0
  130. package/src/remote-control/workspace-codex-sandbox.ts +115 -0
  131. package/src/remote-control/workspace-command-runner.ts +748 -0
  132. package/src/remote-control/workspace-coordinator.ts +231 -0
  133. package/src/remote-control/workspace-device.ts +585 -0
  134. package/src/remote-control/workspace-executable.ts +43 -0
  135. package/src/remote-control/workspace-executor.ts +397 -0
  136. package/src/remote-control/workspace-hub.ts +519 -0
  137. package/src/remote-control/workspace-pi-runtime.ts +382 -0
  138. package/src/remote-control/workspace-process.ts +129 -0
  139. package/src/remote-control/workspace-rpc.ts +304 -0
  140. package/src/remote-control/workspace-runtime.ts +60 -0
  141. package/src/remote-control/workspace-secret-store.ts +39 -0
  142. package/src/remote-control/workspace-sessions.ts +799 -0
  143. package/src/remote-control/workspace-tool-bridge.ts +192 -0
  144. package/src/responses/code-mode-helper-compat.ts +22 -3
  145. package/src/responses/hosted-tool-policy.ts +0 -1
  146. package/src/responses/muse-tool-name-alias.ts +379 -0
  147. package/src/responses/plaintext-v2-agent-messages.ts +902 -0
  148. package/src/router.ts +7 -0
  149. package/src/routing/compatibility/behavior.ts +0 -1
  150. package/src/server/audio-client.ts +64 -0
  151. package/src/server/audio-dictation.ts +91 -0
  152. package/src/server/audio-live.ts +185 -0
  153. package/src/server/audio-transcriptions.ts +183 -0
  154. package/src/server/audio-upstream.ts +153 -0
  155. package/src/server/auth-cors.ts +61 -2
  156. package/src/server/chat-completions.ts +1 -1
  157. package/src/server/chat-native-sse.ts +92 -48
  158. package/src/server/chat-native.ts +37 -15
  159. package/src/server/hub-usage.ts +57 -0
  160. package/src/server/images.ts +4 -0
  161. package/src/server/index.ts +722 -57
  162. package/src/server/lifecycle.ts +5 -6
  163. package/src/server/live-call-bindings.ts +60 -0
  164. package/src/server/live.ts +12 -1
  165. package/src/server/management/agent-settings-routes.ts +25 -4
  166. package/src/server/management/api-access.ts +37 -0
  167. package/src/server/management/api-key-usage.ts +7 -2
  168. package/src/server/management/config-routes.ts +1 -18
  169. package/src/server/management/context.ts +15 -0
  170. package/src/server/management/logs-usage-routes.ts +2 -0
  171. package/src/server/management/oauth-account-routes.ts +39 -12
  172. package/src/server/management/provider-routes.ts +125 -2
  173. package/src/server/management/remote-workspace-routes.ts +140 -0
  174. package/src/server/management/route-registry.ts +15 -0
  175. package/src/server/management/usage-aggregate-cache.ts +14 -15
  176. package/src/server/management/usage-summary-cache.ts +2 -0
  177. package/src/server/management-api.ts +23 -0
  178. package/src/server/ports.ts +17 -0
  179. package/src/server/relay-eager.ts +4 -1
  180. package/src/server/relay.ts +70 -10
  181. package/src/server/request-decompress.ts +6 -3
  182. package/src/server/responses/agent-task-recovery.ts +25 -32
  183. package/src/server/responses/codex-auth-error.ts +11 -0
  184. package/src/server/responses/codex-ws-exchange.ts +52 -3
  185. package/src/server/responses/codex-ws-wire.ts +55 -0
  186. package/src/server/responses/compact.ts +9 -1
  187. package/src/server/responses/core.ts +337 -73
  188. package/src/server/responses/encrypted-payload.ts +45 -2
  189. package/src/server/responses/ws-upstream.ts +4 -1
  190. package/src/server/responses-self-named-namespace-scrub.ts +1 -3
  191. package/src/server/responses-undeclared-tool-guard.ts +1 -1
  192. package/src/server/search.ts +3 -0
  193. package/src/server/sse-payload-rewrite.ts +136 -51
  194. package/src/server/ws-bridge.ts +35 -1
  195. package/src/service/cli.ts +372 -0
  196. package/src/service/diagnostics.ts +340 -0
  197. package/src/service/guards.ts +303 -0
  198. package/src/service/health.ts +222 -0
  199. package/src/service/launchd.ts +853 -0
  200. package/src/service/orchestration.ts +617 -0
  201. package/src/service/repair.ts +334 -0
  202. package/src/service/state.ts +363 -0
  203. package/src/service/systemd.ts +229 -0
  204. package/src/service/windows-ops.ts +690 -0
  205. package/src/service/windows-scheduler.ts +769 -0
  206. package/src/service/windows-taskxml.ts +613 -0
  207. package/src/service.ts +22 -5550
  208. package/src/storage/cleanup/db.ts +258 -0
  209. package/src/storage/cleanup/execute.ts +358 -0
  210. package/src/storage/cleanup/paths.ts +189 -0
  211. package/src/storage/cleanup/pending.ts +140 -0
  212. package/src/storage/cleanup/preview.ts +292 -0
  213. package/src/storage/cleanup/reconcile.ts +347 -0
  214. package/src/storage/cleanup/restore.ts +932 -0
  215. package/src/storage/cleanup/satellite.ts +474 -0
  216. package/src/storage/cleanup/staging.ts +129 -0
  217. package/src/storage/cleanup/types.ts +98 -0
  218. package/src/storage/cleanup.ts +49 -3127
  219. package/src/types/accounts.ts +2 -0
  220. package/src/types/config.ts +13 -12
  221. package/src/types/provider.ts +37 -0
  222. package/src/types/request.ts +2 -0
  223. package/src/types/tools.ts +17 -5
  224. package/src/types.ts +1 -0
  225. package/src/usage/expected-prices.ts +127 -0
  226. package/src/usage/log.ts +58 -1
  227. package/src/vision/eligibility.ts +13 -2
  228. package/src/web-search/loop.ts +56 -3
  229. package/gui/dist/assets/index-D_t6sCWs.js +0 -115
  230. package/gui/dist/assets/index-EdoPnm9_.css +0 -1
  231. package/src/adapters/devin-cli/acp.ts +0 -204
  232. package/src/adapters/devin-cli/adapter.ts +0 -345
  233. package/src/adapters/devin-cli/binary.ts +0 -69
  234. package/src/adapters/devin-cli/models.ts +0 -57
  235. package/src/oauth/devin-cli.ts +0 -149
  236. package/src/server/responses-reasoning-summary-rewrite.ts +0 -178
@@ -15,8 +15,8 @@ export interface IncomingMeta {
15
15
  providerFetch?: typeof globalThis.fetch;
16
16
  /**
17
17
  * Image-normalization ladder bias for upstream-413 tightened retries: every image
18
- * starts one tier lower (devlog/260714_image_normalization_pipeline/030). Only the
19
- * anthropic adapter consumes it; others ignore it.
18
+ * starts one tier lower (devlog/260714_image_normalization_pipeline/030). Consumed by
19
+ * the anthropic and openai-chat adapters; others ignore it.
20
20
  */
21
21
  imageTierBias?: number;
22
22
  }
@@ -91,6 +91,12 @@ export interface AdapterRequest {
91
91
  convertedRoutedToolSearchNames?: ReadonlySet<string>;
92
92
  /** Upstream-only aliases for namespace tools flattened in this request. */
93
93
  convertedRoutedNamespaceToolAliases?: ReadonlyMap<string, { namespace: string; name: string; kind: "function" | "custom" }>;
94
+ /** Request-declared collaboration child names eligible for plaintext-v2 alias restoration. */
95
+ plaintextV2AgentMessageToolNames?: ReadonlySet<string>;
96
+ /** Collaboration message-tool names actually rewritten to fixed aliases in this request. */
97
+ plaintextV2AgentMessageAliasedToolNames?: ReadonlySet<string>;
98
+ /** Upstream-only <=64-char aliases for Meta Muse tool names rewritten in this request. */
99
+ convertedMuseToolNameAliases?: ReadonlyMap<string, string>;
94
100
  /** Releases observation of a serialized request body after its final fetch attempt settles. */
95
101
  releaseBodyObservation?: () => void;
96
102
  /** Exact reasoning parameter emitted by the adapter, for request-log diagnostics only. */
@@ -191,6 +191,18 @@ function bareReLooksLikeOverflow(context?: CursorSizeContext): boolean {
191
191
  return estimatedInputTokens >= OVERFLOW_MIN_FRACTION * contextWindow;
192
192
  }
193
193
 
194
+ /**
195
+ * True when a transport error is the bare 0-token resource_exhausted overflow shape
196
+ * (not quota/rate) that should surface for Codex compact or remint on later hits.
197
+ */
198
+ export function isCursorOverflowRemintCandidate(err: unknown, sizeContext?: CursorSizeContext): boolean {
199
+ const message = errorMessage(err);
200
+ if (!message) return false;
201
+ const lower = message.toLowerCase();
202
+ if (!isCursorZeroTokenResourceExhausted(lower)) return false;
203
+ return classifyCursorError(message, sizeContext) === "Cursor context limit exceeded";
204
+ }
205
+
194
206
  export function isCursorZeroTokenResourceExhausted(lowerMessage: string): boolean {
195
207
  if (!lowerMessage.includes("resource_exhausted") && !lowerMessage.includes("resource exhausted")) return false;
196
208
  // Any explicit quota/rate cue wins: this is a real 429.
@@ -65,3 +65,96 @@ export function lookupCursorThreadConversation(
65
65
  export function clearCursorThreadContinuityForTests(): void {
66
66
  overrides.clear();
67
67
  }
68
+
69
+ /** Max conversation-id remints after the first surfaced overflow per retained scope. */
70
+ export const CURSOR_OVERFLOW_REMINT_MAX = 3;
71
+ export const CURSOR_OVERFLOW_REMINT_TTL_MS = 60 * 60 * 1000;
72
+ export const CURSOR_OVERFLOW_REMINT_MAX_ENTRIES = 2_048;
73
+
74
+ type OverflowRemintState = {
75
+ surfaced: boolean;
76
+ remintCount: number;
77
+ skip: boolean;
78
+ updatedAt: number;
79
+ };
80
+
81
+ const overflowRemintByScope = new Map<string, OverflowRemintState>();
82
+
83
+ function pruneOverflowRemints(at: number): void {
84
+ for (const [scopeKey, entry] of overflowRemintByScope) {
85
+ if (at - entry.updatedAt > CURSOR_OVERFLOW_REMINT_TTL_MS) overflowRemintByScope.delete(scopeKey);
86
+ }
87
+ while (overflowRemintByScope.size > CURSOR_OVERFLOW_REMINT_MAX_ENTRIES) {
88
+ const oldest = overflowRemintByScope.keys().next().value;
89
+ if (oldest === undefined) break;
90
+ overflowRemintByScope.delete(oldest);
91
+ }
92
+ }
93
+
94
+ function overflowRemintEntry(scopeKey: string): OverflowRemintState {
95
+ const at = now();
96
+ pruneOverflowRemints(at);
97
+ const existing = overflowRemintByScope.get(scopeKey);
98
+ if (existing) {
99
+ existing.updatedAt = at;
100
+ overflowRemintByScope.delete(scopeKey);
101
+ overflowRemintByScope.set(scopeKey, existing);
102
+ return existing;
103
+ }
104
+ const fresh: OverflowRemintState = { surfaced: false, remintCount: 0, skip: false, updatedAt: at };
105
+ overflowRemintByScope.set(scopeKey, fresh);
106
+ pruneOverflowRemints(at);
107
+ return fresh;
108
+ }
109
+
110
+ /** Stable client-thread ownership survives conversation remints; wire ids alone do not. */
111
+ export function cursorOverflowRemintScopeKey(
112
+ threadOwner: string | undefined,
113
+ identityScope?: string,
114
+ ): string | null {
115
+ if (!threadOwner) return null;
116
+ return `overflow\0${cursorThreadScopeKey(threadOwner, identityScope)}`;
117
+ }
118
+
119
+ /** True until the first overflow for this scope has been surfaced for Codex compact. */
120
+ export function shouldSurfaceCursorOverflowFirst(scopeKey: string): boolean {
121
+ pruneOverflowRemints(now());
122
+ return overflowRemintByScope.get(scopeKey)?.surfaced !== true;
123
+ }
124
+
125
+ export function markCursorOverflowSurfaced(scopeKey: string): void {
126
+ const entry = overflowRemintEntry(scopeKey);
127
+ entry.surfaced = true;
128
+ }
129
+
130
+ export function shouldSkipCursorOverflowRemint(scopeKey: string): boolean {
131
+ const at = now();
132
+ pruneOverflowRemints(at);
133
+ const entry = overflowRemintByScope.get(scopeKey);
134
+ if (entry) {
135
+ entry.updatedAt = at;
136
+ overflowRemintByScope.delete(scopeKey);
137
+ overflowRemintByScope.set(scopeKey, entry);
138
+ }
139
+ return entry?.skip === true || (entry?.remintCount ?? 0) >= CURSOR_OVERFLOW_REMINT_MAX;
140
+ }
141
+
142
+ /** Record one overflow remint; returns false when the cap is exhausted. */
143
+ export function recordCursorOverflowRemint(scopeKey: string): boolean {
144
+ const entry = overflowRemintEntry(scopeKey);
145
+ if (entry.skip || entry.remintCount >= CURSOR_OVERFLOW_REMINT_MAX) {
146
+ entry.skip = true;
147
+ return false;
148
+ }
149
+ entry.remintCount += 1;
150
+ return true;
151
+ }
152
+
153
+ export function clearCursorOverflowRemintForTests(): void {
154
+ overflowRemintByScope.clear();
155
+ }
156
+
157
+ export function cursorOverflowRemintCountForTests(): number {
158
+ pruneOverflowRemints(now());
159
+ return overflowRemintByScope.size;
160
+ }
@@ -3,7 +3,7 @@ import type { AdapterEvent, OcxProviderConfig } from "../types";
3
3
  import type { ProviderAdapter } from "./base";
4
4
  import { isTranslatorBudgetExceededError } from "../lib/translator-budget";
5
5
  import { cursorExecDeniedMessage, cursorRequestDeclaresFullAccess } from "./cursor/exec-policy";
6
- import { isCursorBenignCancelError, isCursorInvalidArgumentError, isCursorRootEnvelopeError, safeCursorErrorMessage, type CursorSizeContext } from "./cursor/cursor-errors";
6
+ import { isCursorBenignCancelError, isCursorInvalidArgumentError, isCursorOverflowRemintCandidate, isCursorRootEnvelopeError, safeCursorErrorMessage, type CursorSizeContext } from "./cursor/cursor-errors";
7
7
  import { cursorCheckpointModelAffinityId, inferCursorContextWindow, isCursorExternalWireModel } from "./cursor/discovery";
8
8
  import { createCursorKvStore, type CursorKvStore } from "./cursor/kv-store";
9
9
  import { mapCursorServerMessage } from "./cursor/message-mapper";
@@ -31,7 +31,14 @@ import { debugProviderDiagnostic } from "../lib/debug";
31
31
  import { isDebugEnabled } from "../lib/debug-settings";
32
32
  import { createAdapterTierMetadata } from "../providers/fastwire";
33
33
  import { estimateTokens } from "../lib/token-estimate";
34
- import { rememberCursorThreadConversation } from "./cursor/thread-continuity";
34
+ import {
35
+ cursorOverflowRemintScopeKey,
36
+ markCursorOverflowSurfaced,
37
+ recordCursorOverflowRemint,
38
+ rememberCursorThreadConversation,
39
+ shouldSkipCursorOverflowRemint,
40
+ shouldSurfaceCursorOverflowFirst,
41
+ } from "./cursor/thread-continuity";
35
42
  import { runCursorTurnWithRetry } from "./cursor/transport-retry";
36
43
  import { cursorRequestHasShellAlias, cursorRequestUsesCodeMode } from "./cursor/tool-definitions";
37
44
  import {
@@ -399,84 +406,108 @@ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAda
399
406
  );
400
407
  };
401
408
 
402
- try {
403
- await runOnce(request);
404
- } catch (err) {
405
- const outputGuardRetryText =
406
- err instanceof CursorToolResultEchoError
407
- ? CURSOR_ECHO_RETRY_CONTINUATION_TEXT
408
- : err instanceof CursorRoutingCommentaryError
409
- ? CURSOR_ROUTING_COMMENTARY_RETRY_TEXT
410
- : undefined;
411
- // One-shot corrective retry for guarded external output (devlog 260826 gap-10/11).
412
- // The quarantine guarantees no client-visible delta escaped, so a fresh-conversation
413
- // retry is safe. A second rejection propagates as an error rather than looping.
414
- if (
415
- outputGuardRetryText
416
- && !emittedOutput
417
- && !replayUnsafe
418
- && !incoming.abortSignal?.aborted
419
- ) {
420
- debugProviderDiagnostic(
421
- "cursor",
422
- err instanceof CursorToolResultEchoError
423
- ? "envelope-echo-retry"
424
- : "routing-commentary-retry",
425
- {
426
- wireModel: request.modelId,
427
- conversationHash: request.conversationId.slice(0, 16),
428
- },
409
+ const remintConversationId = (failedConversationId: string) => {
410
+ lastTransport = undefined;
411
+ _parsed._cursorConversationId = undefined;
412
+ const next = createCursorRequest(_parsed, { forceFreshConversation: true });
413
+ rekeyContextUsage(failedConversationId, next.conversationId);
414
+ _parsed._cursorConversationId = next.conversationId;
415
+ // Persist recovery for store:false clients that send any stable Cursor thread owner, so
416
+ // the next turn does not recompute the stale deterministic thread hash. Isolated helper /
417
+ // compaction turns must not park their throwaway id under the parent or Desktop owner.
418
+ const threadOwner = cursorClientThreadOwner(_parsed);
419
+ if (threadOwner && _parsed._cursorIsolateConversation !== true) {
420
+ rememberCursorThreadConversation(
421
+ threadOwner,
422
+ next.conversationId,
423
+ _parsed._cursorIdentityScope,
429
424
  );
430
- const echoedConversationId = request.conversationId;
431
- lastTransport = undefined;
432
- _parsed._cursorConversationId = undefined;
433
- request = {
434
- ...createCursorRequest(_parsed, { forceFreshConversation: true }),
435
- echoRetryContinuationText: outputGuardRetryText,
436
- };
437
- rekeyContextUsage(echoedConversationId, request.conversationId);
438
- _parsed._cursorConversationId = request.conversationId;
439
- const echoThreadOwner = cursorClientThreadOwner(_parsed);
440
- if (echoThreadOwner && _parsed._cursorIsolateConversation !== true) {
441
- rememberCursorThreadConversation(
442
- echoThreadOwner,
443
- request.conversationId,
444
- _parsed._cursorIdentityScope,
445
- );
446
- }
425
+ }
426
+ return next;
427
+ };
428
+
429
+ for (;;) {
430
+ try {
447
431
  await runOnce(request);
448
- } else {
449
- // One-shot fallback for external-model Connect invalid_argument before any
450
- // non-heartbeat output. Retries apply only to safe plain-user turns; tool-result
451
- // resumes, local exec/MCP side effects, and already-emitted output fail closed.
432
+ break;
433
+ } catch (err) {
434
+ const outputGuardRetryText =
435
+ err instanceof CursorToolResultEchoError
436
+ ? CURSOR_ECHO_RETRY_CONTINUATION_TEXT
437
+ : err instanceof CursorRoutingCommentaryError
438
+ ? CURSOR_ROUTING_COMMENTARY_RETRY_TEXT
439
+ : undefined;
440
+ // One-shot corrective retry for guarded external output (devlog 260826 gap-10/11).
441
+ // The quarantine guarantees no client-visible delta escaped, so a fresh-conversation
442
+ // retry is safe. A second rejection propagates as an error rather than looping.
452
443
  if (
453
- !isCursorInvalidArgumentError(err)
454
- || !isCursorExternalWireModel(request.modelId)
455
- || lastRawIsToolResult
456
- || emittedOutput
457
- || replayUnsafe
458
- || incoming.abortSignal?.aborted
444
+ outputGuardRetryText
445
+ && !emittedOutput
446
+ && !replayUnsafe
447
+ && !incoming.abortSignal?.aborted
459
448
  ) {
460
- throw err;
461
- }
462
- const failedConversationId = request.conversationId;
463
- lastTransport = undefined;
464
- _parsed._cursorConversationId = undefined;
465
- request = createCursorRequest(_parsed, { forceFreshConversation: true });
466
- rekeyContextUsage(failedConversationId, request.conversationId);
467
- _parsed._cursorConversationId = request.conversationId;
468
- // Persist recovery for store:false clients that send any stable Cursor thread owner, so
469
- // the next turn does not recompute the stale deterministic thread hash. Isolated helper /
470
- // compaction turns must not park their throwaway id under the parent or Desktop owner.
471
- const threadOwner = cursorClientThreadOwner(_parsed);
472
- if (threadOwner && _parsed._cursorIsolateConversation !== true) {
473
- rememberCursorThreadConversation(
474
- threadOwner,
475
- request.conversationId,
449
+ debugProviderDiagnostic(
450
+ "cursor",
451
+ err instanceof CursorToolResultEchoError
452
+ ? "envelope-echo-retry"
453
+ : "routing-commentary-retry",
454
+ {
455
+ wireModel: request.modelId,
456
+ conversationHash: request.conversationId.slice(0, 16),
457
+ },
458
+ );
459
+ const echoedConversationId = request.conversationId;
460
+ request = {
461
+ ...remintConversationId(echoedConversationId),
462
+ echoRetryContinuationText: outputGuardRetryText,
463
+ };
464
+ await runOnce(request);
465
+ break;
466
+ } else {
467
+ const overflowRemintSafe =
468
+ !lastRawIsToolResult
469
+ && !emittedOutput
470
+ && !replayUnsafe
471
+ && _parsed._cursorIsolateConversation !== true
472
+ && request.contextUsageStoreCheckpoints !== false
473
+ && !incoming.abortSignal?.aborted;
474
+ const overflowScopeKey = cursorOverflowRemintScopeKey(
475
+ cursorClientThreadOwner(_parsed),
476
476
  _parsed._cursorIdentityScope,
477
477
  );
478
+ if (
479
+ overflowScopeKey
480
+ && overflowRemintSafe
481
+ && isCursorOverflowRemintCandidate(err, requestSizeContext)
482
+ ) {
483
+ if (shouldSkipCursorOverflowRemint(overflowScopeKey)) throw err;
484
+ if (shouldSurfaceCursorOverflowFirst(overflowScopeKey)) {
485
+ markCursorOverflowSurfaced(overflowScopeKey);
486
+ throw err;
487
+ }
488
+ if (!recordCursorOverflowRemint(overflowScopeKey)) throw err;
489
+ if (inheritedCheckpointRef) invalidateCursorCheckpoint(inheritedCheckpointRef);
490
+ request = remintConversationId(request.conversationId);
491
+ continue;
492
+ }
493
+
494
+ // One-shot fallback for external-model Connect invalid_argument before any
495
+ // non-heartbeat output. Retries apply only to safe plain-user turns; tool-result
496
+ // resumes, local exec/MCP side effects, and already-emitted output fail closed.
497
+ if (
498
+ !isCursorInvalidArgumentError(err)
499
+ || !isCursorExternalWireModel(request.modelId)
500
+ || lastRawIsToolResult
501
+ || emittedOutput
502
+ || replayUnsafe
503
+ || incoming.abortSignal?.aborted
504
+ ) {
505
+ throw err;
506
+ }
507
+ request = remintConversationId(request.conversationId);
508
+ await runOnce(request);
509
+ break;
478
510
  }
479
- await runOnce(request);
480
511
  }
481
512
  }
482
513
  if (