@iislee/opencodex 2.11.0 → 2.35.0

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 (733) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +117 -22
  3. package/bin/ocx.mjs +245 -88
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-Be1sDpWw.css +1 -0
  6. package/gui/dist/assets/index-D4OKgUV4.js +114 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +21 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +475 -62
  44. package/src/adapters/base.ts +82 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +637 -0
  48. package/src/adapters/cursor/call-id.ts +44 -0
  49. package/src/adapters/cursor/catalog.ts +541 -0
  50. package/src/adapters/cursor/checkpoint-store.ts +308 -0
  51. package/src/adapters/cursor/cursor-errors.ts +144 -6
  52. package/src/adapters/cursor/discovery.ts +122 -14
  53. package/src/adapters/cursor/effort-map.ts +106 -4
  54. package/src/adapters/cursor/envelope-echo.ts +290 -0
  55. package/src/adapters/cursor/framing.ts +39 -0
  56. package/src/adapters/cursor/h2-pool.ts +123 -0
  57. package/src/adapters/cursor/http1-bidi.ts +361 -0
  58. package/src/adapters/cursor/images.ts +704 -0
  59. package/src/adapters/cursor/live-models.ts +180 -59
  60. package/src/adapters/cursor/live-transport.ts +623 -170
  61. package/src/adapters/cursor/message-mapper.ts +4 -1
  62. package/src/adapters/cursor/native-exec-common.ts +23 -2
  63. package/src/adapters/cursor/native-exec-desktop.ts +23 -0
  64. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  65. package/src/adapters/cursor/native-exec-network.ts +1 -1
  66. package/src/adapters/cursor/native-exec-shell.ts +5 -3
  67. package/src/adapters/cursor/native-exec.ts +121 -14
  68. package/src/adapters/cursor/protobuf-events.ts +829 -11
  69. package/src/adapters/cursor/protobuf-request.ts +447 -75
  70. package/src/adapters/cursor/request-builder.ts +271 -35
  71. package/src/adapters/cursor/tool-definitions.ts +252 -12
  72. package/src/adapters/cursor/tool-result-normalize.ts +115 -0
  73. package/src/adapters/cursor/transport.ts +22 -0
  74. package/src/adapters/cursor/types.ts +48 -1
  75. package/src/adapters/cursor.ts +336 -39
  76. package/src/adapters/exec-tool-result-normalize.ts +99 -0
  77. package/src/adapters/google-antigravity-replay.ts +713 -41
  78. package/src/adapters/google-antigravity-wire.ts +48 -10
  79. package/src/adapters/google-errors.ts +44 -12
  80. package/src/adapters/google-http.ts +38 -10
  81. package/src/adapters/google-tool-schema.ts +80 -15
  82. package/src/adapters/google-truncation.ts +11 -0
  83. package/src/adapters/google.ts +742 -86
  84. package/src/adapters/identity.ts +39 -6
  85. package/src/adapters/image.ts +1 -1
  86. package/src/adapters/kiro-constants.ts +27 -0
  87. package/src/adapters/kiro-errors.ts +11 -0
  88. package/src/adapters/kiro-events.ts +19 -1
  89. package/src/adapters/kiro-thinking.ts +18 -2
  90. package/src/adapters/kiro-tools.ts +65 -17
  91. package/src/adapters/kiro.ts +270 -73
  92. package/src/adapters/mimo-free.ts +17 -0
  93. package/src/adapters/openai-chat-url.ts +11 -0
  94. package/src/adapters/openai-chat.ts +1377 -344
  95. package/src/adapters/openai-responses-url.ts +16 -0
  96. package/src/adapters/openai-responses.ts +1164 -64
  97. package/src/adapters/registry.ts +175 -0
  98. package/src/adapters/responses-tool-schema.ts +67 -0
  99. package/src/adapters/run-turn-queue.ts +36 -1
  100. package/src/adapters/tool-call-id.ts +119 -0
  101. package/src/adapters/tool-catalog-nudge.ts +105 -21
  102. package/src/adapters/xai-tool-schema.ts +436 -0
  103. package/src/adapters/xai-web-search.ts +186 -0
  104. package/src/bridge.ts +512 -92
  105. package/src/chat/inbound.ts +57 -20
  106. package/src/chat/outbound.ts +97 -33
  107. package/src/claude/agents-inject.ts +40 -10
  108. package/src/claude/context-windows.ts +37 -14
  109. package/src/claude/desktop-3p.ts +243 -9
  110. package/src/claude/gateway-cache.ts +41 -4
  111. package/src/claude/inbound.ts +72 -3
  112. package/src/claude/model-info.ts +38 -15
  113. package/src/claude/outbound.ts +88 -21
  114. package/src/cli/access.ts +46 -3
  115. package/src/cli/account-api.ts +150 -19
  116. package/src/cli/account-auth.ts +33 -6
  117. package/src/cli/account-catalog-refresh.ts +14 -0
  118. package/src/cli/account-extended.ts +648 -28
  119. package/src/cli/account-main.ts +317 -0
  120. package/src/cli/account.ts +97 -19
  121. package/src/cli/agent.ts +53 -2
  122. package/src/cli/alias.ts +66 -0
  123. package/src/cli/capabilities-command.ts +94 -0
  124. package/src/cli/capabilities.ts +496 -0
  125. package/src/cli/claude-agent-startup-sync.ts +73 -0
  126. package/src/cli/claude-desktop.ts +60 -15
  127. package/src/cli/claude.ts +129 -35
  128. package/src/cli/codex-log-guard-doctor.ts +103 -0
  129. package/src/cli/codex-shim-autorestore.ts +2 -0
  130. package/src/cli/codex-shim-readiness.ts +76 -0
  131. package/src/cli/combo.ts +8 -0
  132. package/src/cli/config-command.ts +74 -10
  133. package/src/cli/dispatch.ts +821 -0
  134. package/src/cli/doctor.ts +440 -56
  135. package/src/cli/ensure-desired-integrations.ts +152 -0
  136. package/src/cli/export-command.ts +46 -20
  137. package/src/cli/help.ts +34 -274
  138. package/src/cli/index.ts +417 -528
  139. package/src/cli/init.ts +5 -17
  140. package/src/cli/inspect.ts +230 -0
  141. package/src/cli/integrations.ts +120 -2
  142. package/src/cli/lab.ts +607 -0
  143. package/src/cli/launcher-context.ts +77 -0
  144. package/src/cli/minimax.ts +497 -0
  145. package/src/cli/models-runtime.ts +130 -2
  146. package/src/cli/models.ts +118 -16
  147. package/src/cli/observe.ts +123 -11
  148. package/src/cli/opencode.ts +4 -2
  149. package/src/cli/provider-runtime.ts +34 -3
  150. package/src/cli/provider.ts +25 -3
  151. package/src/cli/ready.ts +301 -0
  152. package/src/cli/registry.ts +476 -0
  153. package/src/cli/root.ts +86 -0
  154. package/src/cli/route-policy.ts +92 -0
  155. package/src/cli/runtime-api.ts +57 -10
  156. package/src/cli/star-prompt.ts +71 -15
  157. package/src/cli/status.ts +51 -4
  158. package/src/cli/storage.ts +234 -0
  159. package/src/cli/system-command.ts +16 -0
  160. package/src/cli/system-restart-client.ts +146 -0
  161. package/src/cli/tray-proxy.ts +153 -6
  162. package/src/cli/usage-report.ts +184 -0
  163. package/src/cli/v2.ts +137 -18
  164. package/src/cli/version-skew.ts +46 -0
  165. package/src/cli.ts +1 -1
  166. package/src/clients/config-export.ts +1442 -23
  167. package/src/clients/effective-status.ts +30 -2
  168. package/src/clients/probes/cline.ts +81 -0
  169. package/src/codex/account-label.ts +35 -1
  170. package/src/codex/account-lifecycle.ts +130 -13
  171. package/src/codex/account-namespaces.ts +49 -3
  172. package/src/codex/account-pause.ts +2 -1
  173. package/src/codex/account-priority.ts +84 -0
  174. package/src/codex/account-store.ts +29 -2
  175. package/src/codex/account-usability.ts +25 -2
  176. package/src/codex/admission.ts +256 -0
  177. package/src/codex/affinity-debug.ts +162 -0
  178. package/src/codex/app-server-processes.ts +570 -107
  179. package/src/codex/app-server-restart-service.ts +232 -0
  180. package/src/codex/auth-api.ts +893 -246
  181. package/src/codex/auth-collision.ts +5 -3
  182. package/src/codex/auth-context.ts +348 -32
  183. package/src/codex/autostart-health.ts +24 -1
  184. package/src/codex/catalog/account-models.ts +67 -0
  185. package/src/codex/catalog/aggregation.ts +86 -10
  186. package/src/codex/catalog/bundled.ts +331 -33
  187. package/src/codex/catalog/effort.ts +138 -32
  188. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  189. package/src/codex/catalog/kinds.ts +2 -0
  190. package/src/codex/catalog/metadata.ts +555 -45
  191. package/src/codex/catalog/native-models.ts +75 -0
  192. package/src/codex/catalog/parsing.ts +269 -30
  193. package/src/codex/catalog/provider-fetch.ts +1626 -144
  194. package/src/codex/catalog/sync.ts +1576 -187
  195. package/src/codex/catalog-admission.ts +199 -0
  196. package/src/codex/catalog-refresh-status.ts +105 -0
  197. package/src/codex/catalog-write-serialization.ts +242 -0
  198. package/src/codex/catalog.ts +6 -3
  199. package/src/codex/codex-write-lock.ts +386 -0
  200. package/src/codex/convergence-types.ts +614 -0
  201. package/src/codex/convergence.ts +676 -0
  202. package/src/codex/coordinator-doctor.ts +332 -0
  203. package/src/codex/custom-model-catalog-migration.ts +176 -0
  204. package/src/codex/desired-state.ts +230 -0
  205. package/src/codex/desktop-app-restart.ts +355 -0
  206. package/src/codex/features.ts +636 -39
  207. package/src/codex/generation.ts +202 -0
  208. package/src/codex/history-job.ts +436 -0
  209. package/src/codex/history-lock.ts +242 -0
  210. package/src/codex/history-manifest.ts +112 -0
  211. package/src/codex/history-migration-guardian.ts +30 -24
  212. package/src/codex/history-provider.ts +1016 -235
  213. package/src/codex/history-transition.ts +105 -0
  214. package/src/codex/history-worker.ts +223 -0
  215. package/src/codex/inject-coordination.ts +373 -0
  216. package/src/codex/inject.ts +1114 -152
  217. package/src/codex/injected-marker.ts +37 -3
  218. package/src/codex/integration-record.ts +266 -0
  219. package/src/codex/internal/catalog-writer.ts +203 -0
  220. package/src/codex/internal/history-writer.ts +81 -0
  221. package/src/codex/journal.ts +66 -4
  222. package/src/codex/log-guard/inspect.ts +524 -0
  223. package/src/codex/log-guard/lock.ts +150 -0
  224. package/src/codex/log-guard/maintenance.ts +403 -0
  225. package/src/codex/log-guard/path-safety.ts +88 -0
  226. package/src/codex/log-guard/policy.ts +44 -0
  227. package/src/codex/log-guard/processes.ts +205 -0
  228. package/src/codex/log-guard/protection.ts +489 -0
  229. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  230. package/src/codex/main-account-cache.ts +24 -0
  231. package/src/codex/main-account.ts +29 -1
  232. package/src/codex/management-convergence.ts +167 -0
  233. package/src/codex/model-cache.ts +56 -10
  234. package/src/codex/model-entitlements.ts +360 -0
  235. package/src/codex/native-main-admission.ts +47 -0
  236. package/src/codex/native-main-auth-temp.ts +187 -0
  237. package/src/codex/native-main-claim.ts +178 -0
  238. package/src/codex/native-main-lock-file.ts +162 -0
  239. package/src/codex/native-main-owner.ts +329 -0
  240. package/src/codex/native-profile-api.ts +247 -0
  241. package/src/codex/native-profile-manager.ts +1531 -0
  242. package/src/codex/native-profile-processes.ts +121 -0
  243. package/src/codex/native-profile-recovery.ts +99 -0
  244. package/src/codex/native-profile-stage-store.ts +387 -0
  245. package/src/codex/native-profile-startup.ts +622 -0
  246. package/src/codex/native-profile-store.ts +855 -0
  247. package/src/codex/native-profile-types.ts +120 -0
  248. package/src/codex/native-residue.ts +675 -0
  249. package/src/codex/paths.ts +80 -1
  250. package/src/codex/plan-from-token.ts +140 -0
  251. package/src/codex/plan.ts +40 -0
  252. package/src/codex/plugins-doctor.ts +1 -1
  253. package/src/codex/pool-rotation.ts +74 -4
  254. package/src/codex/project-config-warnings.ts +20 -6
  255. package/src/codex/prompt-journal.ts +352 -0
  256. package/src/codex/prompt-layers.ts +1313 -0
  257. package/src/codex/prompt-lock.ts +143 -0
  258. package/src/codex/prompt-text-probe.ts +238 -0
  259. package/src/codex/quota-rejection.ts +298 -0
  260. package/src/codex/quota.ts +264 -21
  261. package/src/codex/refresh.ts +11 -2
  262. package/src/codex/reset-credit-recovery.ts +1044 -0
  263. package/src/codex/routing.ts +514 -94
  264. package/src/codex/runtime.ts +159 -38
  265. package/src/codex/shim.ts +1065 -31
  266. package/src/codex/subagent-model-fallback.ts +437 -43
  267. package/src/codex/sync.ts +191 -2
  268. package/src/codex/transition-state.ts +720 -0
  269. package/src/codex/upstream-host-health.ts +368 -0
  270. package/src/codex/user-identity.ts +557 -0
  271. package/src/codex/warmup.ts +187 -81
  272. package/src/codex/write-coordination.ts +114 -0
  273. package/src/combos/failover.ts +47 -0
  274. package/src/combos/index.ts +4 -0
  275. package/src/combos/request.ts +32 -0
  276. package/src/combos/types.ts +91 -9
  277. package/src/compatibility/index.ts +26 -0
  278. package/src/compatibility/manifest.ts +253 -0
  279. package/src/compatibility/openai-responses.ts +121 -0
  280. package/src/config/atomic-write.ts +219 -0
  281. package/src/config/paths.ts +40 -0
  282. package/src/config/process-state.ts +309 -0
  283. package/src/config/provider-name.ts +24 -0
  284. package/src/config/provider-validation.ts +177 -0
  285. package/src/config/rebase-provenance.ts +68 -0
  286. package/src/config.ts +1713 -709
  287. package/src/generated/compatibility-version.json +3324 -0
  288. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  289. package/src/grok/inject.ts +16 -5
  290. package/src/grok/inspect.ts +45 -0
  291. package/src/grok/sync.ts +2 -2
  292. package/src/images/loop.ts +157 -33
  293. package/src/images/plan.ts +24 -13
  294. package/src/integrations/config-io.ts +269 -0
  295. package/src/integrations/journal.ts +315 -0
  296. package/src/integrations/merge.ts +135 -0
  297. package/src/integrations/mutation-flight.ts +71 -0
  298. package/src/integrations/native/ownership-preflight.ts +202 -0
  299. package/src/integrations/omp-yaml-source.ts +358 -0
  300. package/src/integrations/owned-refresh.ts +74 -0
  301. package/src/integrations/ownership-policy.ts +160 -0
  302. package/src/integrations/ownership.ts +155 -0
  303. package/src/integrations/registry.ts +166 -0
  304. package/src/integrations/serialize.ts +314 -0
  305. package/src/integrations/state.ts +433 -0
  306. package/src/integrations/store.ts +103 -0
  307. package/src/integrations/writer-lock.ts +98 -0
  308. package/src/integrations/writer.ts +715 -0
  309. package/src/lab/artifacts/sanitize.ts +586 -0
  310. package/src/lab/artifacts/secure-fs.ts +475 -0
  311. package/src/lab/artifacts/store.ts +310 -0
  312. package/src/lab/automation/budgets.ts +78 -0
  313. package/src/lab/automation/config-persistence.ts +256 -0
  314. package/src/lab/automation/constants.ts +39 -0
  315. package/src/lab/automation/cooldown.ts +103 -0
  316. package/src/lab/automation/dispatch.ts +211 -0
  317. package/src/lab/automation/index.ts +13 -0
  318. package/src/lab/automation/orchestrator.ts +499 -0
  319. package/src/lab/automation/persistence.ts +512 -0
  320. package/src/lab/automation/planner.ts +371 -0
  321. package/src/lab/automation/policy.ts +136 -0
  322. package/src/lab/automation/queue.ts +191 -0
  323. package/src/lab/automation/recovery.ts +24 -0
  324. package/src/lab/automation/route-context.ts +21 -0
  325. package/src/lab/automation/run-key.ts +44 -0
  326. package/src/lab/automation/runs-query.ts +34 -0
  327. package/src/lab/automation/types.ts +160 -0
  328. package/src/lab/conformance/assertion.ts +325 -0
  329. package/src/lab/conformance/digest.ts +22 -0
  330. package/src/lab/conformance/executor.ts +741 -0
  331. package/src/lab/conformance/fixture-provider.ts +27 -0
  332. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  333. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  334. package/src/lab/conformance/harness-budget.ts +47 -0
  335. package/src/lab/conformance/index.ts +5 -0
  336. package/src/lab/conformance/jcs.ts +64 -0
  337. package/src/lab/conformance/json-pointer.ts +39 -0
  338. package/src/lab/conformance/manifest.ts +180 -0
  339. package/src/lab/conformance/mcp-stub.ts +179 -0
  340. package/src/lab/conformance/negative-controls.ts +164 -0
  341. package/src/lab/conformance/observation.ts +355 -0
  342. package/src/lab/conformance/runner.ts +68 -0
  343. package/src/lab/conformance/sse-normalize.ts +59 -0
  344. package/src/lab/conformance/suite-manifest.ts +78 -0
  345. package/src/lab/conformance/types.ts +214 -0
  346. package/src/lab/constants.ts +126 -0
  347. package/src/lab/digest.ts +64 -0
  348. package/src/lab/events/errors.ts +9 -0
  349. package/src/lab/events/limits.ts +117 -0
  350. package/src/lab/events/types.ts +229 -0
  351. package/src/lab/events/validate.ts +781 -0
  352. package/src/lab/fabric/constants.ts +40 -0
  353. package/src/lab/fabric/executor.ts +492 -0
  354. package/src/lab/fabric/index.ts +80 -0
  355. package/src/lab/fabric/manifest.ts +222 -0
  356. package/src/lab/fabric/observe.ts +489 -0
  357. package/src/lab/fabric/patch.ts +79 -0
  358. package/src/lab/fabric/producer-child.ts +139 -0
  359. package/src/lab/fabric/producer-isolate.ts +276 -0
  360. package/src/lab/fabric/producer-protocol.ts +61 -0
  361. package/src/lab/fabric/scratch.ts +439 -0
  362. package/src/lab/fabric/subject.ts +106 -0
  363. package/src/lab/fabric/types.ts +134 -0
  364. package/src/lab/fabric/verifier.ts +98 -0
  365. package/src/lab/index.ts +54 -0
  366. package/src/lab/ledger/artifact-refs.ts +127 -0
  367. package/src/lab/ledger/invalidation.ts +136 -0
  368. package/src/lab/ledger/purge.ts +310 -0
  369. package/src/lab/ledger/store.ts +532 -0
  370. package/src/lab/live/credential-lease.ts +53 -0
  371. package/src/lab/live/destination.ts +155 -0
  372. package/src/lab/live/executor.ts +336 -0
  373. package/src/lab/live/inert-tools.ts +56 -0
  374. package/src/lab/live/manifest.ts +85 -0
  375. package/src/lab/live/mcp-loopback.ts +57 -0
  376. package/src/lab/live/runner.ts +19 -0
  377. package/src/lab/live/sandbox.ts +61 -0
  378. package/src/lab/live/suite-manifest.ts +41 -0
  379. package/src/lab/live/transport.ts +118 -0
  380. package/src/lab/live/types.ts +197 -0
  381. package/src/lab/observe/from-conformance.ts +301 -0
  382. package/src/lab/observe/from-live.ts +117 -0
  383. package/src/lab/paths.ts +153 -0
  384. package/src/lab/projection/rebuild.ts +495 -0
  385. package/src/lab/projection/schema.ts +135 -0
  386. package/src/lab/projection/verdicts.ts +474 -0
  387. package/src/lab/projection/verification.ts +412 -0
  388. package/src/lab/public/bundle.ts +217 -0
  389. package/src/lab/public/community-authority.ts +175 -0
  390. package/src/lab/public/community-files.ts +29 -0
  391. package/src/lab/public/community.ts +479 -0
  392. package/src/lab/public/file-safety.ts +155 -0
  393. package/src/lab/public/ids.ts +26 -0
  394. package/src/lab/public/index.ts +16 -0
  395. package/src/lab/public/mutation-lock.ts +424 -0
  396. package/src/lab/public/operator.ts +353 -0
  397. package/src/lab/public/origin-purge.ts +79 -0
  398. package/src/lab/public/origin.ts +203 -0
  399. package/src/lab/public/privacy.ts +143 -0
  400. package/src/lab/public/private-file.ts +261 -0
  401. package/src/lab/public/project.ts +124 -0
  402. package/src/lab/public/purge-test-fault.ts +21 -0
  403. package/src/lab/public/purge.ts +223 -0
  404. package/src/lab/public/registry.ts +44 -0
  405. package/src/lab/public/revocation.ts +252 -0
  406. package/src/lab/public/signature.ts +243 -0
  407. package/src/lab/public/storage.ts +105 -0
  408. package/src/lab/public/strict-json.ts +206 -0
  409. package/src/lab/public/time.ts +26 -0
  410. package/src/lab/public/types.ts +172 -0
  411. package/src/lab/public/validate.ts +391 -0
  412. package/src/lab/query/catalog.ts +101 -0
  413. package/src/lab/query/connection.ts +107 -0
  414. package/src/lab/query/constants.ts +4 -0
  415. package/src/lab/query/cursor.ts +132 -0
  416. package/src/lab/query/dto-map.ts +277 -0
  417. package/src/lab/query/errors.ts +22 -0
  418. package/src/lab/query/freshness.ts +53 -0
  419. package/src/lab/query/index.ts +45 -0
  420. package/src/lab/query/latest-observation.ts +59 -0
  421. package/src/lab/query/passive-production.ts +159 -0
  422. package/src/lab/query/queries.ts +444 -0
  423. package/src/lab/query/types.ts +266 -0
  424. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  425. package/src/lab/subject/installation-salt.ts +112 -0
  426. package/src/lab/subject/protocol-subject.ts +80 -0
  427. package/src/lab/subject/route-subject.ts +74 -0
  428. package/src/lib/admin-secrets.ts +24 -0
  429. package/src/lib/app-owned-memory-stores.ts +22 -0
  430. package/src/lib/bounded-body.ts +167 -11
  431. package/src/lib/bun-runtime.ts +125 -12
  432. package/src/lib/bun-stream-caps.ts +13 -9
  433. package/src/lib/codex-restart-contract.ts +120 -0
  434. package/src/lib/config-ownership.ts +26 -2
  435. package/src/lib/destination-policy.ts +65 -1
  436. package/src/lib/errors.ts +80 -5
  437. package/src/lib/fabric-task-execution-authority.ts +7 -0
  438. package/src/lib/fabric-task-host.ts +29 -0
  439. package/src/lib/lab-activation.ts +223 -0
  440. package/src/lib/lab-live-execution-authority.ts +13 -0
  441. package/src/lib/lab-live-host.ts +30 -0
  442. package/src/lib/lab-live-pinned-sender.ts +56 -0
  443. package/src/lib/lab-live-route-production.ts +130 -0
  444. package/src/lib/lab-passive-linker-registration.ts +26 -0
  445. package/src/lib/local-management-attestation.ts +51 -0
  446. package/src/lib/local-management-capability.ts +100 -0
  447. package/src/lib/local-provider-reload-contract.ts +100 -0
  448. package/src/lib/optional-shutdown-hooks.ts +57 -0
  449. package/src/lib/package-tree-integrity.ts +101 -0
  450. package/src/lib/pinned-http.ts +145 -26
  451. package/src/lib/process-control.ts +6 -2
  452. package/src/lib/provider-outbound.ts +49 -9
  453. package/src/lib/redact.ts +419 -3
  454. package/src/lib/self-launch-argv.ts +15 -0
  455. package/src/lib/server-resource-ownership.ts +71 -0
  456. package/src/lib/service-secrets.ts +15 -0
  457. package/src/lib/shadow-call.ts +35 -4
  458. package/src/lib/sse-decoder.ts +41 -0
  459. package/src/lib/state-store-registrations.ts +10 -2
  460. package/src/lib/system-restart-contract.ts +73 -0
  461. package/src/lib/token-estimate.ts +19 -2
  462. package/src/lib/tool-argument-integers.ts +253 -0
  463. package/src/lib/translator-budget.ts +44 -0
  464. package/src/lib/upstream-http-version.ts +57 -0
  465. package/src/lib/upstream-reachability.ts +95 -0
  466. package/src/lib/upstream-retry.ts +156 -3
  467. package/src/lib/windows-atomic-replace.ts +155 -0
  468. package/src/lib/windows-elevation.ts +70 -2
  469. package/src/lib/windows-secret-acl.ts +409 -69
  470. package/src/lib/windows-service-wrappers.ts +72 -0
  471. package/src/lib/windows-text.ts +106 -0
  472. package/src/lib/windows-user-principal.ts +341 -0
  473. package/src/lib/winsw.ts +33 -5
  474. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  475. package/src/oauth/account-import/index.ts +15 -0
  476. package/src/oauth/account-import/parser.ts +83 -0
  477. package/src/oauth/account-import/registry.ts +18 -0
  478. package/src/oauth/account-import/service.ts +75 -0
  479. package/src/oauth/account-import/types.ts +91 -0
  480. package/src/oauth/anthropic.ts +12 -1
  481. package/src/oauth/callback-server.ts +30 -4
  482. package/src/oauth/chatgpt.ts +12 -1
  483. package/src/oauth/cline.ts +203 -0
  484. package/src/oauth/command-code.ts +239 -0
  485. package/src/oauth/cursor.ts +46 -5
  486. package/src/oauth/generic-account-failover.ts +231 -0
  487. package/src/oauth/google-antigravity.ts +35 -3
  488. package/src/oauth/health.ts +20 -12
  489. package/src/oauth/index.ts +462 -71
  490. package/src/oauth/key-providers.ts +25 -0
  491. package/src/oauth/kimi.ts +25 -3
  492. package/src/oauth/kiro.ts +95 -6
  493. package/src/oauth/local-token-detect.ts +13 -2
  494. package/src/oauth/log.ts +3 -1
  495. package/src/oauth/login-cli.ts +88 -28
  496. package/src/oauth/nous.ts +798 -0
  497. package/src/oauth/open-browser-choice.ts +26 -0
  498. package/src/oauth/store.ts +133 -26
  499. package/src/oauth/token-guardian.ts +9 -3
  500. package/src/oauth/types.ts +15 -0
  501. package/src/pi/models.ts +2 -2
  502. package/src/providers/alibaba-region-migration.ts +1 -1
  503. package/src/providers/antigravity-models.ts +521 -31
  504. package/src/providers/auto-compact-budget.ts +65 -0
  505. package/src/providers/base-url-choices.ts +10 -0
  506. package/src/providers/codex-capacity.ts +292 -0
  507. package/src/providers/command-code-efforts.ts +176 -0
  508. package/src/providers/context-cap.ts +26 -8
  509. package/src/providers/cursor-pool.ts +72 -0
  510. package/src/providers/default-aliases.ts +65 -0
  511. package/src/providers/derive.ts +281 -6
  512. package/src/providers/fastwire.ts +507 -0
  513. package/src/providers/free-directory.ts +10 -7
  514. package/src/providers/google-vertex-location.ts +14 -0
  515. package/src/providers/key-failover.ts +71 -3
  516. package/src/providers/label.ts +35 -2
  517. package/src/providers/model-discovery-limits.ts +16 -0
  518. package/src/providers/model-discovery.ts +115 -22
  519. package/src/providers/model-presets.ts +119 -0
  520. package/src/providers/model-rename-migration.ts +255 -0
  521. package/src/providers/model-rename-startup.ts +28 -0
  522. package/src/providers/new-model-policy.ts +146 -0
  523. package/src/providers/openai-sidecar.ts +72 -4
  524. package/src/providers/openai-tier-startup.ts +31 -2
  525. package/src/providers/openai-tiers.ts +119 -4
  526. package/src/providers/openai-virtual-models.ts +1 -0
  527. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  528. package/src/providers/provider-id-rewrite.ts +30 -0
  529. package/src/providers/quota.ts +1379 -40
  530. package/src/providers/registry.ts +1545 -112
  531. package/src/providers/request-pacing.ts +310 -0
  532. package/src/providers/service-tier.ts +277 -0
  533. package/src/providers/slug-codec.ts +94 -6
  534. package/src/providers/static-model-discovery.ts +86 -0
  535. package/src/providers/xai-responses-opt-in.ts +15 -0
  536. package/src/providers/xai-transport.ts +32 -4
  537. package/src/reasoning-effort.ts +68 -3
  538. package/src/responses/apply-patch-envelope.ts +63 -0
  539. package/src/responses/code-mode-helper-compat.ts +50 -0
  540. package/src/responses/compaction.ts +26 -1
  541. package/src/responses/custom-tool-compat.ts +384 -0
  542. package/src/responses/hosted-tool-policy.ts +9 -0
  543. package/src/responses/namespace-tool-compat.ts +435 -0
  544. package/src/responses/parser.ts +225 -38
  545. package/src/responses/provider-continuation.ts +98 -0
  546. package/src/responses/provider-opaque-metadata.ts +73 -0
  547. package/src/responses/reasoning-envelope.ts +9 -1
  548. package/src/responses/reasoning-replay-cache.ts +504 -0
  549. package/src/responses/schema.ts +12 -2
  550. package/src/responses/spill-store.ts +75 -10
  551. package/src/responses/state.ts +659 -31
  552. package/src/responses/thought-signature-replay.ts +364 -0
  553. package/src/responses/tool-search-compat.ts +301 -0
  554. package/src/responses/truncated-stop-reason.ts +60 -0
  555. package/src/responses/turn-termination.ts +107 -0
  556. package/src/router.ts +405 -31
  557. package/src/routing/analytics.ts +379 -0
  558. package/src/routing/capability.ts +244 -0
  559. package/src/routing/compatibility/assemble.ts +73 -0
  560. package/src/routing/compatibility/behavior.ts +278 -0
  561. package/src/routing/compatibility/catalog.ts +99 -0
  562. package/src/routing/compatibility/endpoint.ts +52 -0
  563. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  564. package/src/routing/compatibility/policy.ts +181 -0
  565. package/src/routing/compatibility/provider-slot.ts +56 -0
  566. package/src/routing/compatibility/reader.ts +110 -0
  567. package/src/routing/compatibility/subject.ts +191 -0
  568. package/src/routing/compatibility/types.ts +64 -0
  569. package/src/routing/compatibility/version.ts +104 -0
  570. package/src/routing/cost.ts +77 -0
  571. package/src/routing/evaluator.ts +495 -0
  572. package/src/routing/health.ts +412 -0
  573. package/src/routing/history/cursor.ts +43 -0
  574. package/src/routing/history/indexer.ts +605 -0
  575. package/src/routing/history/schema.ts +72 -0
  576. package/src/routing/profile-namespace.ts +15 -0
  577. package/src/routing/profile.ts +547 -0
  578. package/src/routing/quota.ts +155 -0
  579. package/src/routing/request-evidence.ts +45 -0
  580. package/src/routing/trace.ts +776 -0
  581. package/src/server/adapter-resolve.ts +2 -29
  582. package/src/server/auth-cors.ts +289 -48
  583. package/src/server/background-lifecycle.ts +182 -0
  584. package/src/server/chat-completions.ts +154 -72
  585. package/src/server/chat-native-sse.ts +331 -0
  586. package/src/server/chat-native.ts +445 -0
  587. package/src/server/claude-messages.ts +160 -44
  588. package/src/server/direct-local-http.ts +347 -0
  589. package/src/server/effort-policy.ts +18 -0
  590. package/src/server/github-copilot-responses-repair.ts +338 -0
  591. package/src/server/gui-static.ts +39 -10
  592. package/src/server/images.ts +94 -12
  593. package/src/server/index.ts +1021 -190
  594. package/src/server/lifecycle.ts +336 -14
  595. package/src/server/live.ts +136 -17
  596. package/src/server/local-management-read-client.ts +90 -0
  597. package/src/server/local-provider-reload-client.ts +137 -0
  598. package/src/server/management/agent-settings-routes.ts +433 -116
  599. package/src/server/management/api-key-usage.ts +31 -5
  600. package/src/server/management/body.ts +6 -0
  601. package/src/server/management/codex-prompt-routes.ts +570 -0
  602. package/src/server/management/combo-routes.ts +64 -25
  603. package/src/server/management/config-routes.ts +511 -51
  604. package/src/server/management/context.ts +89 -2
  605. package/src/server/management/integration-routes.ts +498 -0
  606. package/src/server/management/lab-automation-routes.ts +206 -0
  607. package/src/server/management/lab-routes.ts +563 -0
  608. package/src/server/management/logs-usage-routes.ts +127 -52
  609. package/src/server/management/model-routes.ts +461 -133
  610. package/src/server/management/model-rows.ts +167 -0
  611. package/src/server/management/native-integration-routes.ts +770 -0
  612. package/src/server/management/oauth-account-routes.ts +90 -8
  613. package/src/server/management/provider-capability-config.ts +48 -0
  614. package/src/server/management/provider-routes.ts +836 -159
  615. package/src/server/management/request-history-routes.ts +191 -0
  616. package/src/server/management/route-registry.ts +332 -0
  617. package/src/server/management/routing-analytics-routes.ts +74 -0
  618. package/src/server/management/routing-profile-routes.ts +380 -0
  619. package/src/server/management/shared.ts +27 -11
  620. package/src/server/management/sidebar-routes.ts +47 -31
  621. package/src/server/management/storage-log-guard-routes.ts +186 -0
  622. package/src/server/management/sync-response.ts +69 -0
  623. package/src/server/management/system-restart.ts +289 -33
  624. package/src/server/management/system-routes.ts +77 -0
  625. package/src/server/management/usage-summary-cache.ts +9 -1
  626. package/src/server/management/vision-sidecar-options.ts +167 -0
  627. package/src/server/management/web-search-sidecar-options.ts +120 -0
  628. package/src/server/management-api.ts +126 -23
  629. package/src/server/management-auth.ts +220 -5
  630. package/src/server/passive-route-linker.ts +66 -0
  631. package/src/server/port-reclaim.ts +1 -1
  632. package/src/server/ports.ts +41 -1
  633. package/src/server/proxy-liveness.ts +161 -10
  634. package/src/server/readiness.ts +99 -0
  635. package/src/server/relay-eager.ts +221 -54
  636. package/src/server/relay.ts +479 -87
  637. package/src/server/request-decompress.ts +113 -6
  638. package/src/server/request-log-conversation.ts +51 -0
  639. package/src/server/request-log.ts +298 -31
  640. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  641. package/src/server/responses/agent-task-recovery.ts +465 -0
  642. package/src/server/responses/codex-auth-error.ts +55 -0
  643. package/src/server/responses/collaboration.ts +239 -36
  644. package/src/server/responses/combo-stream-preflight.ts +178 -0
  645. package/src/server/responses/compact.ts +444 -72
  646. package/src/server/responses/core.ts +3579 -436
  647. package/src/server/responses/empty-completion-guard.ts +333 -0
  648. package/src/server/responses/encrypted-payload.ts +62 -39
  649. package/src/server/responses/fetch-helpers.ts +89 -99
  650. package/src/server/responses/input-admission.ts +187 -0
  651. package/src/server/responses/pacing-overload.ts +13 -0
  652. package/src/server/responses/passthrough-error.ts +33 -9
  653. package/src/server/responses/policy-fallback.ts +178 -0
  654. package/src/server/responses/responses-field-backfill.ts +343 -0
  655. package/src/server/responses/terminal-guard.ts +26 -5
  656. package/src/server/responses/upstream-error.ts +5 -0
  657. package/src/server/responses/ws-upstream.ts +381 -0
  658. package/src/server/responses-custom-tool-repair.ts +363 -0
  659. package/src/server/responses-item-id-repair.ts +54 -6
  660. package/src/server/responses-json-events.ts +90 -0
  661. package/src/server/responses-model-rewrite.ts +29 -0
  662. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  663. package/src/server/responses-snapshot-repair.ts +621 -0
  664. package/src/server/responses-terminal-repair.ts +363 -0
  665. package/src/server/responses-tool-search-repair.ts +267 -0
  666. package/src/server/responses-undeclared-tool-guard.ts +376 -0
  667. package/src/server/responses.ts +18 -2
  668. package/src/server/search.ts +78 -13
  669. package/src/server/sse-frame-buffer.ts +319 -0
  670. package/src/server/sse-payload-rewrite.ts +110 -22
  671. package/src/server/startup-action-control.ts +8 -1
  672. package/src/server/startup-health-cache.ts +19 -1
  673. package/src/server/system-env.ts +80 -9
  674. package/src/server/ws-bridge.ts +53 -40
  675. package/src/service-manager-probe.ts +892 -0
  676. package/src/service.ts +1153 -94
  677. package/src/sidecar/auth.ts +92 -0
  678. package/src/sidecar/candidates.ts +83 -0
  679. package/src/storage/cleanup.ts +2 -2
  680. package/src/storage/policy-job.ts +14 -4
  681. package/src/storage/policy.ts +88 -23
  682. package/src/storage/scanner.ts +1 -1
  683. package/src/storage/worker-lifecycle.ts +14 -14
  684. package/src/tray/windows-tray.ps1 +83 -9
  685. package/src/tray/windows.ts +43 -16
  686. package/src/types/accounts.ts +37 -0
  687. package/src/types/config.ts +911 -0
  688. package/src/types/provider.ts +614 -0
  689. package/src/types/request.ts +397 -0
  690. package/src/types/tools.ts +236 -0
  691. package/src/types/wire.ts +80 -0
  692. package/src/types.ts +107 -1237
  693. package/src/update/index.ts +37 -23
  694. package/src/update/job.ts +445 -68
  695. package/src/update/notify.ts +12 -6
  696. package/src/update/npm-cache-preflight.d.mts +47 -0
  697. package/src/update/npm-cache-preflight.mjs +201 -0
  698. package/src/update/transactional-install.d.mts +22 -0
  699. package/src/update/transactional-install.mjs +266 -0
  700. package/src/usage/cost.ts +0 -0
  701. package/src/usage/expected-prices.ts +287 -16
  702. package/src/usage/log.ts +666 -50
  703. package/src/usage/summary.ts +388 -17
  704. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  705. package/src/usage/user-cost-overlays.ts +314 -0
  706. package/src/vision/anthropic-describe.ts +10 -6
  707. package/src/vision/backends.ts +97 -0
  708. package/src/vision/describe.ts +27 -16
  709. package/src/vision/eligibility.ts +250 -0
  710. package/src/vision/index.ts +238 -24
  711. package/src/vision/reasoning.ts +55 -0
  712. package/src/vision/routed-describe.ts +175 -0
  713. package/src/vision/timeout-bounds.ts +9 -0
  714. package/src/web-search/anthropic-executor.ts +13 -7
  715. package/src/web-search/backends.ts +108 -0
  716. package/src/web-search/exa-executor.ts +119 -0
  717. package/src/web-search/executor.ts +21 -6
  718. package/src/web-search/gemini-executor.ts +141 -0
  719. package/src/web-search/index.ts +150 -15
  720. package/src/web-search/loop.ts +284 -54
  721. package/src/web-search/parse.ts +125 -30
  722. package/src/web-search/sources.ts +60 -0
  723. package/src/web-search/xai-executor.ts +219 -0
  724. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  725. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  726. package/gui/dist/provider-icons/antigravity.svg +0 -1
  727. package/gui/dist/provider-icons/claude.svg +0 -1
  728. package/gui/dist/provider-icons/copilot.svg +0 -1
  729. package/gui/dist/provider-icons/cursor.svg +0 -2
  730. package/gui/dist/provider-icons/gemini.svg +0 -1
  731. package/gui/dist/provider-icons/grok-color.svg +0 -1
  732. package/gui/dist/provider-icons/kiro.svg +0 -14
  733. package/src/cli/internal-dispatch.ts +0 -20
@@ -4,16 +4,21 @@ import { ValueSchema } from "@bufbuild/protobuf/wkt";
4
4
  import type { OcxAssistantContentPart, OcxMessage, OcxToolResultMessage } from "../../types";
5
5
  import { namespacedToolName } from "../../types";
6
6
  import type { CursorRunRequest } from "./types";
7
- import { isCursorExternalWireModel } from "./discovery";
7
+ import { decodeCursorCallId } from "./call-id";
8
+ import { cursorNeedsExternalToolContinuation, isCursorExternalWireModel } from "./discovery";
9
+ import { normalizeCursorToolResultText } from "./tool-result-normalize";
8
10
  import { debugProviderDiagnostic } from "../../lib/debug";
9
11
  import {
10
12
  createCursorBlobRequestScope,
13
+ cursorBlobMaxEntryBytes,
11
14
  releaseCursorBlobRequestScope,
12
15
  sealCursorBlobRequestScope,
13
16
  storeCursorBlob,
14
17
  type CursorBlobRequestScopeToken,
15
18
  } from "./native-exec";
19
+ import { buildSelectedContext, CURSOR_VISION_IMAGE_HISTORY_MARKER } from "./images";
16
20
  import { estimateTokens } from "../../lib/token-estimate";
21
+ import { parseDataUrl } from "../image";
17
22
  import {
18
23
  AgentClientMessageSchema,
19
24
  AgentConversationTurnStructureSchema,
@@ -22,10 +27,12 @@ import {
22
27
  ConversationActionSchema,
23
28
  ConversationStepSchema,
24
29
  ConversationStateStructureSchema,
30
+ type ConversationStateStructure,
25
31
  ConversationTurnStructureSchema,
26
32
  McpArgsSchema,
27
33
  McpSuccessSchema,
28
34
  McpTextContentSchema,
35
+ McpImageContentSchema,
29
36
  McpToolCallSchema,
30
37
  McpToolResultContentItemSchema,
31
38
  McpToolResultSchema,
@@ -65,6 +72,14 @@ export const CURSOR_EXTERNAL_ROOT_BLOB_LIMIT = 192;
65
72
  /** Approximate prompt-size guard; tool schemas and protocol framing consume context separately. */
66
73
  export const CURSOR_EXTERNAL_ROOT_BYTE_LIMIT = 512 * 1024;
67
74
 
75
+ /**
76
+ * Action text for external-model tool-result continuations. Native models keep
77
+ * resumeAction; external wire models continue as userMessageAction so the
78
+ * results already stored in history blobs are visible without a ResumeAction.
79
+ */
80
+ export const CURSOR_EXTERNAL_TOOL_CONTINUATION_TEXT =
81
+ "Continue: the requested tool results are provided in the conversation history above.";
82
+
68
83
  /** Runtime timezone for protobuf RequestContextEnv (dynamic, never hardcoded). */
69
84
  function runtimeTimeZone(): string {
70
85
  try {
@@ -119,6 +134,10 @@ function rootBlobCandidate(
119
134
  };
120
135
  }
121
136
 
137
+ function toolResultRootPayload(text: string): { role: "assistant"; content: [{ type: "text"; text: string }] } {
138
+ return { role: "assistant", content: [{ type: "text", text }] };
139
+ }
140
+
122
141
  function truncateToolResultBlob(entry: RootBlobCandidate, maxBytes: number): RootBlobCandidate | null {
123
142
  if (entry.byteLength <= maxBytes) return entry;
124
143
  if (entry.role !== "toolResult" || entry.text === undefined) return null;
@@ -131,7 +150,7 @@ function truncateToolResultBlob(entry: RootBlobCandidate, maxBytes: number): Roo
131
150
  while (end > 0 && end < encoded.byteLength && (encoded[end]! & 0xc0) === 0x80) end -= 1;
132
151
  const truncated = `${decoder.decode(encoded.subarray(0, end))}${marker}`;
133
152
  const result = rootBlobCandidate(
134
- { role: "user", content: [{ type: "text", text: truncated }] },
153
+ toolResultRootPayload(truncated),
135
154
  "toolResult",
136
155
  { messageIndex: entry.messageIndex, text: truncated },
137
156
  );
@@ -140,7 +159,7 @@ function truncateToolResultBlob(entry: RootBlobCandidate, maxBytes: number): Roo
140
159
  keepBytes = Math.max(0, end - (result.byteLength - maxBytes) - 16);
141
160
  }
142
161
  const markerOnly = rootBlobCandidate(
143
- { role: "user", content: [{ type: "text", text: marker.trimStart() }] },
162
+ toolResultRootPayload(marker.trimStart()),
144
163
  "toolResult",
145
164
  { messageIndex: entry.messageIndex, text: marker.trimStart() },
146
165
  );
@@ -170,11 +189,12 @@ function assistantRootText(
170
189
  }
171
190
 
172
191
  // Cursor builds the actual model prompt from rootPromptMessagesJson (turns[] is UI/display metadata),
173
- // so prior history including assistant tool calls and tool results must be replayed here or a
174
- // ResumeAction has nothing model-visible to continue from. The active user message is excluded
175
- // because it travels in the action. Tool results are rendered as user-role text with a marker, and
176
- // each entry is a SHA-256 blob ID (Cursor fetches the bytes back via getBlobArgs). Mirrors the
177
- // danger-pi reference buildRootPromptMessagesJson.
192
+ // so prior history must be replayed here or a ResumeAction has nothing model-visible to continue from.
193
+ // The active user message is excluded because it travels in the action. When the continuation cannot
194
+ // rely on native MCP turn state, tool results stay assistant-role text with a [Tool Result] /
195
+ // [Tool Error] marker so Cursor does not wrap them as `<user_query>` (#1992). Native resume models
196
+ // already carry the paired MCP result on turns[], so that marker is omitted from root replay — Auto
197
+ // few-shot-mimics it as chat text otherwise. Each entry is a SHA-256 blob ID.
178
198
  function rootPromptMessages(request: CursorRunRequest, requestScope: CursorBlobRequestScopeToken): {
179
199
  ids: Uint8Array[];
180
200
  byteLength: number;
@@ -195,19 +215,59 @@ function rootPromptMessages(request: CursorRunRequest, requestScope: CursorBlobR
195
215
  }
196
216
 
197
217
  const externalModel = isCursorExternalWireModel(request.modelId);
218
+ const echoToolResultInRoot = cursorNeedsExternalToolContinuation(request.modelId);
198
219
  const lastRawIsToolResult = messages.at(-1)?.role === "toolResult";
199
220
  const activeUserIndex = lastRawIsToolResult ? -1 : lastActionIndex(messages);
221
+ // Repetition breaker (devlog 260826 gap-9): external full-replay flattens history to text,
222
+ // so N identical assistant/tool-result rounds replay as N identical lines and PRIME the model
223
+ // to emit the same line again (self-reinforcing loop: S2a 180x, identical-probe repetition).
224
+ // Collapse consecutive duplicates into one entry + a count marker, and count collapses so a
225
+ // strategy-change note can be appended when the pattern is severe.
226
+ let lastReplayText: string | undefined;
227
+ let lastReplayEntry: RootBlobCandidate | undefined;
228
+ let collapsedRepeats = 0;
229
+ let maxRunLength = 1;
230
+ let currentRun = 1;
231
+ const pushDeduped = (
232
+ payload: { role: string; content: [{ type: "text"; text: string }] },
233
+ role: RootBlobCandidate["role"],
234
+ opts: { messageIndex: number; text?: string },
235
+ normalized: string,
236
+ ): void => {
237
+ if (externalModel && lastReplayText !== undefined && normalized === lastReplayText && lastReplayEntry) {
238
+ collapsedRepeats++;
239
+ currentRun++;
240
+ if (currentRun > maxRunLength) maxRunLength = currentRun;
241
+ const marked = `${normalized}\n[note: this exact output was produced ${currentRun} times in a row]`;
242
+ const replacement = rootBlobCandidate(
243
+ { role: payload.role, content: [{ type: "text", text: marked }] },
244
+ role,
245
+ opts,
246
+ );
247
+ entries[entries.indexOf(lastReplayEntry)] = replacement;
248
+ lastReplayEntry = replacement;
249
+ return;
250
+ }
251
+ currentRun = 1;
252
+ const entry = rootBlobCandidate(payload, role, opts);
253
+ entries.push(entry);
254
+ lastReplayText = normalized;
255
+ lastReplayEntry = entry;
256
+ };
200
257
 
201
258
  for (let i = 0; i < messages.length; i++) {
202
259
  if (i === activeUserIndex) break;
203
260
  const message = messages[i];
204
261
  if (!message) continue;
205
262
  if (message.role === "user" || message.role === "developer") {
206
- const text = contentText(message).trim();
263
+ const text = historyContentText(message).trim();
207
264
  // Cursor root replay expects OpenAI-style content parts for historical user messages.
208
265
  // A bare string survives blob hydration but external workers reject the completed replay
209
266
  // before tokenization (`usedTokens: 0`, then invalid_argument).
210
267
  if (text.length > 0) {
268
+ lastReplayText = undefined;
269
+ lastReplayEntry = undefined;
270
+ currentRun = 1;
211
271
  entries.push(rootBlobCandidate({
212
272
  role: "user",
213
273
  content: [{ type: "text", text }],
@@ -218,23 +278,33 @@ function rootPromptMessages(request: CursorRunRequest, requestScope: CursorBlobR
218
278
  // Native Composer state can preserve it through ThinkingMessage/history structures.
219
279
  const text = assistantRootText(message, !externalModel).trim();
220
280
  if (text.length > 0) {
221
- entries.push(rootBlobCandidate(
281
+ pushDeduped(
222
282
  { role: "assistant", content: [{ type: "text", text }] },
223
283
  "assistant",
224
284
  { messageIndex: i },
225
- ));
285
+ text,
286
+ );
226
287
  }
227
288
  // Assistant tool CALLS are intentionally NOT replayed as visible "[Tool Call]" text here.
228
289
  } else if (message.role === "toolResult") {
229
- const prefix = message.isError ? "[Tool Error]" : "[Tool Result]";
290
+ // Native resume models already receive the paired MCP result through turns[]. Replaying
291
+ // the same payload as assistant-role "[Tool Result]" / "[tool_result]" text teaches Auto
292
+ // to echo that envelope as chat instead of continuing from the structured result.
293
+ if (!echoToolResultInRoot) continue;
294
+ // #1920: the prefix must reflect the NORMALIZED error state (an empty
295
+ // node_repl result is an error even when the runtime said isError=false).
296
+ const prefix = normalizedToolResult(message, contentToText(message.content)).isError ? "[Tool Error]" : "[Tool Result]";
230
297
  const text = `${prefix}\n${toolResultToText(message)}`;
231
- entries.push(rootBlobCandidate(
232
- { role: "user", content: [{ type: "text", text }] },
233
- "toolResult",
234
- { messageIndex: i, text },
235
- ));
298
+ pushDeduped(toolResultRootPayload(text), "toolResult", { messageIndex: i, text }, text);
236
299
  }
237
300
  }
301
+ // Severe repetition: tell the model ONCE, imperatively, to change strategy.
302
+ if (externalModel && maxRunLength >= 3) {
303
+ entries.push(rootBlobCandidate({
304
+ role: "user",
305
+ content: [{ type: "text", text: `[context note] The transcript above contains the same output repeated ${maxRunLength} times in a row. Repeating it again is a failure. Take a DIFFERENT action now, or state plainly what is blocking progress.` }],
306
+ }, "user", {}));
307
+ }
238
308
 
239
309
  let selected = entries;
240
310
  let historyMessageStart = 0;
@@ -318,7 +388,7 @@ function contentText(message: OcxMessage): string {
318
388
  .map(part => {
319
389
  if (part.type === "text") return part.text;
320
390
  if (part.type === "thinking") return part.thinking;
321
- if (part.type === "image") return `[image input unsupported by Cursor adapter phase 3: ${part.detail ?? "auto"}]`;
391
+ if (part.type === "image") return undefined;
322
392
  return undefined;
323
393
  })
324
394
  .filter((value): value is string => typeof value === "string" && value.length > 0)
@@ -328,21 +398,217 @@ function contentText(message: OcxMessage): string {
328
398
  function contentToText(content: OcxToolResultMessage["content"]): string {
329
399
  if (typeof content === "string") return content;
330
400
  return content
331
- .map(part => part.type === "text" ? part.text : `[image input unsupported by Cursor adapter phase 3: ${part.detail ?? "auto"}]`)
401
+ .map(part => {
402
+ if (part.type === "text") return part.text;
403
+ if (part.type === "image") return CURSOR_VISION_IMAGE_HISTORY_MARKER;
404
+ return undefined;
405
+ })
406
+ .filter((value): value is string => typeof value === "string" && value.length > 0)
332
407
  .join("\n");
333
408
  }
334
409
 
410
+ /** History serializer. Replayed turns are text-only; never embed image bytes. */
411
+ function historyContentText(message: OcxMessage): string {
412
+ if (message.role === "toolResult" || typeof message.content === "string") return contentText(message);
413
+ return message.content
414
+ .map(part => {
415
+ if (part.type === "text") return part.text;
416
+ if (part.type === "thinking") return part.thinking;
417
+ if (part.type === "image") return CURSOR_VISION_IMAGE_HISTORY_MARKER;
418
+ return undefined;
419
+ })
420
+ .filter((value): value is string => typeof value === "string" && value.length > 0)
421
+ .join("\n");
422
+ }
423
+
424
+ const BASE64_PATTERN = /^[A-Za-z0-9+/]*={0,2}$/;
425
+
426
+ /**
427
+ * Decode a Codex inline image into Cursor wire bytes.
428
+ *
429
+ * `OcxImageContent.imageUrl` is either a `data:` URL or a remote https URL, so this cannot reuse
430
+ * the MCP helper (which takes bare base64 plus a separate mime). It layers strict validation over
431
+ * the shared `parseDataUrl` rather than tightening it, because Anthropic, Google, and Command Code
432
+ * share that parser. `Buffer.from(x, "base64")` accepts many invalid strings silently, so the
433
+ * charset is checked explicitly. Remote URLs are out of scope: `McpImageContent` needs bytes, and
434
+ * fetching here would put network IO inside request construction.
435
+ */
436
+ function decodeInlineImage(imageUrl: string): { bytes: Uint8Array; mimeType: string } | undefined {
437
+ const parsed = parseDataUrl(imageUrl);
438
+ if (!parsed) return undefined;
439
+ const base64 = parsed.base64.trim();
440
+ if (base64.length === 0 || base64.length % 4 !== 0 || !BASE64_PATTERN.test(base64)) return undefined;
441
+ try {
442
+ const bytes = Uint8Array.from(Buffer.from(base64, "base64"));
443
+ if (bytes.length === 0) return undefined;
444
+ return { bytes, mimeType: parsed.mediaType || "application/octet-stream" };
445
+ } catch {
446
+ return undefined;
447
+ }
448
+ }
449
+
450
+ /**
451
+ * A degraded image must never make a step LARGER than the legacy encoding did, or this change
452
+ * could fail admission for a request that previously fit. The old placeholder was
453
+ * `[image input unsupported by Cursor adapter phase 3: <detail>]`; anything we emit in its place
454
+ * is truncated to that budget so the zero-image case is byte-bounded by the pre-change behavior.
455
+ */
456
+ const LEGACY_IMAGE_PLACEHOLDER_BUDGET =
457
+ "[image input unsupported by Cursor adapter phase 3: auto]".length;
458
+
459
+ function imagePlaceholder(reason: string): string {
460
+ const text = `[image omitted: ${reason}]`;
461
+ return text.length <= LEGACY_IMAGE_PLACEHOLDER_BUDGET
462
+ ? text
463
+ : `${text.slice(0, LEGACY_IMAGE_PLACEHOLDER_BUDGET - 1)}]`;
464
+ }
465
+
466
+ type DecodedResultPart =
467
+ | { kind: "text"; text: string }
468
+ | { kind: "image"; bytes: Uint8Array; mimeType: string }
469
+ | { kind: "undecodable" };
470
+
471
+ type NormalizedToolResult = { text: string; isError: boolean };
472
+
473
+ /**
474
+ * Decode a tool result's parts ONCE. `toolCallStep` may re-serialize a step several times while
475
+ * shrinking it to fit blob admission, and decoding base64 on every attempt made that loop
476
+ * quadratic (an audit measured ~3s for 100 images).
477
+ */
478
+ function decodeResultParts(message: OcxToolResultMessage): DecodedResultPart[] | undefined {
479
+ const content = message.content;
480
+ if (typeof content === "string") return undefined;
481
+ return content.map((part): DecodedResultPart => {
482
+ if (part.type === "text") return { kind: "text", text: part.text };
483
+ if (part.type === "video") return { kind: "text", text: "[video]" };
484
+ const decoded = decodeInlineImage(part.imageUrl);
485
+ return decoded ? { kind: "image", ...decoded } : { kind: "undecodable" };
486
+ });
487
+ }
488
+
489
+ function countImages(parts: DecodedResultPart[] | undefined): number {
490
+ return parts ? parts.filter(p => p.kind === "image").length : 0;
491
+ }
492
+
493
+ /**
494
+ * Build the wire content items for a tool result, preserving part order.
495
+ *
496
+ * Images become real `McpImageContent` — the Cursor schema has an image case on
497
+ * `McpToolResultContentItem`, and `native-exec-mcp.ts` already uses it for MCP-invoked tools.
498
+ * Flattening them to placeholder text blinded every screenshot-returning tool (Computer Use,
499
+ * browser QA) that Codex routes through this path.
500
+ */
501
+ function toolResultContentItems(
502
+ message: OcxToolResultMessage,
503
+ decoded?: DecodedResultPart[],
504
+ maxImages = Number.POSITIVE_INFINITY,
505
+ normalizedText?: NormalizedToolResult,
506
+ ) {
507
+ const parts = decoded ?? decodeResultParts(message);
508
+ const textItem = (text: string) => [create(McpToolResultContentItemSchema, {
509
+ content: { case: "text" as const, value: create(McpTextContentSchema, { text }) },
510
+ })];
511
+ if (!parts) {
512
+ const normalized = normalizedText
513
+ ?? normalizedToolResult(message, typeof message.content === "string" ? message.content : "");
514
+ return textItem(normalized.text);
515
+ }
516
+ const normalized = normalizedText ?? normalizedDecodedTextResult(message, parts);
517
+ if (normalized) {
518
+ // #1920/#1866: empty or failure-state Computer Use / node_repl results are
519
+ // normalized before they reach the native wire. Pure-text part arrays use
520
+ // the same newline-joined representation this serializer already emitted;
521
+ // image-bearing and undecodable results stay on the lossless part path.
522
+ return textItem(normalized.text);
523
+ }
524
+ // Images are dropped OLDEST first when the step must shrink: the most recent screenshot is the
525
+ // one the model is reasoning about, so it is the last to go.
526
+ const totalImages = countImages(parts);
527
+ const allowed = Math.max(0, Math.min(totalImages, maxImages));
528
+ let seen = 0;
529
+ // Consecutive text runs are newline-joined into ONE item, exactly as the legacy encoding did.
530
+ // Emitting one protobuf item per part adds per-item framing, which was enough to push a
531
+ // previously admissible step past the blob ceiling (round-3 audit: 1020 -> 1025 bytes at a
532
+ // 1024 limit). A result with no images must serialize identically to before this feature.
533
+ const items: ReturnType<typeof create<typeof McpToolResultContentItemSchema>>[] = [];
534
+ let pendingText: string[] = [];
535
+ const flushText = () => {
536
+ if (pendingText.length === 0) return;
537
+ const text = pendingText.join("\n");
538
+ pendingText = [];
539
+ items.push(create(McpToolResultContentItemSchema, {
540
+ content: { case: "text" as const, value: create(McpTextContentSchema, { text }) },
541
+ }));
542
+ };
543
+ for (const part of parts) {
544
+ if (part.kind === "text") {
545
+ pendingText.push(part.text);
546
+ continue;
547
+ }
548
+ if (part.kind === "undecodable") {
549
+ pendingText.push(imagePlaceholder("no inline data"));
550
+ continue;
551
+ }
552
+ seen++;
553
+ if (seen <= totalImages - allowed) {
554
+ pendingText.push(imagePlaceholder(`${part.bytes.byteLength}B over step limit`));
555
+ continue;
556
+ }
557
+ flushText();
558
+ items.push(create(McpToolResultContentItemSchema, {
559
+ content: { case: "image" as const, value: create(McpImageContentSchema, {
560
+ data: part.bytes,
561
+ mimeType: part.mimeType,
562
+ }) },
563
+ }));
564
+ }
565
+ flushText();
566
+ if (items.length === 0) {
567
+ items.push(create(McpToolResultContentItemSchema, {
568
+ content: { case: "text" as const, value: create(McpTextContentSchema, { text: "" }) },
569
+ }));
570
+ }
571
+ return items;
572
+ }
573
+
335
574
  function toolResultToText(message: OcxToolResultMessage): string {
575
+ const normalized = normalizedToolResult(message, contentToText(message.content));
336
576
  return [
337
577
  "[tool_result]",
338
- `call_id: ${message.toolCallId}`,
578
+ `call_id: ${decodeCursorCallId(message.toolCallId)}`,
339
579
  `name: ${namespacedToolName(message.toolNamespace, message.toolName)}`,
340
- `is_error: ${message.isError}`,
580
+ `is_error: ${normalized.isError}`,
341
581
  "output:",
342
- contentToText(message.content),
582
+ normalized.text,
343
583
  ].join("\n");
344
584
  }
345
585
 
586
+ /**
587
+ * Shared #1920 normalization entry: pure-text results only. Image-bearing or
588
+ * encrypted results pass through untouched (their content is not plain text).
589
+ */
590
+ function normalizedToolResult(message: OcxToolResultMessage, text: string): NormalizedToolResult {
591
+ if (message.containsEncryptedContent) return { text, isError: message.isError };
592
+ return normalizeCursorToolResultText(text, {
593
+ toolName: message.toolName,
594
+ toolNamespace: message.toolNamespace,
595
+ isError: message.isError,
596
+ });
597
+ }
598
+
599
+ /**
600
+ * A content-part result is plain text only when every decoded part is text (the empty array is the
601
+ * empty text result). Join it exactly as toolResultContentItems already did, then share the string
602
+ * normalization contract. Any image or undecodable part keeps the existing part-preserving path.
603
+ */
604
+ function normalizedDecodedTextResult(
605
+ message: OcxToolResultMessage,
606
+ parts: DecodedResultPart[],
607
+ ): NormalizedToolResult | undefined {
608
+ if (parts.some(part => part.kind !== "text")) return undefined;
609
+ return normalizedToolResult(message, parts.map(part => part.kind === "text" ? part.text : "").join("\n"));
610
+ }
611
+
346
612
  function argBytes(value: unknown): Uint8Array {
347
613
  try {
348
614
  return toBinary(ValueSchema, fromJson(ValueSchema, value as JsonValue));
@@ -359,7 +625,8 @@ function toolCallStep(
359
625
  const args: Record<string, Uint8Array> = {};
360
626
  for (const [key, value] of Object.entries(part.arguments ?? {})) args[key] = argBytes(value);
361
627
  const toolName = namespacedToolName(part.namespace, part.name);
362
- return storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
628
+ const decodedResult = result ? decodeResultParts(result) : undefined;
629
+ const serialize = (maxImages: number): Uint8Array => toBinary(ConversationStepSchema, create(ConversationStepSchema, {
363
630
  message: {
364
631
  case: "toolCall",
365
632
  value: create(ToolCallSchema, {
@@ -369,27 +636,42 @@ function toolCallStep(
369
636
  args: create(McpArgsSchema, {
370
637
  name: toolName,
371
638
  toolName,
372
- toolCallId: part.id,
639
+ toolCallId: decodeCursorCallId(part.id),
373
640
  providerIdentifier: OCX_RESPONSES_TOOL_PROVIDER,
374
641
  args,
375
642
  }),
376
- ...(result ? { result: toolResultPart(result) } : {}),
643
+ ...(result ? { result: toolResultPart(result, decodedResult, maxImages) } : {}),
377
644
  }),
378
645
  },
379
646
  }),
380
647
  },
381
- })), requestScope);
648
+ }));
649
+
650
+ // A step is stored as ONE blob, so its images share an entry with the call's arguments, text,
651
+ // mime strings, and protobuf framing. A byte budget over decoded images alone cannot bound that
652
+ // (an audit reproduced a 448-byte-argument call whose 460-byte image pushed a previously
653
+ // admitted step past the ceiling). Measure the real serialized size instead, then drop images —
654
+ // oldest first, so the most recent screenshot survives — until the step fits.
655
+ const limit = cursorBlobMaxEntryBytes();
656
+ const imageCount = countImages(decodedResult);
657
+ let encoded = serialize(imageCount);
658
+ for (let allowed = imageCount - 1; allowed >= 0 && encoded.byteLength > limit; allowed--) {
659
+ encoded = serialize(allowed);
660
+ }
661
+ return storeCursorBlob(encoded, requestScope);
382
662
  }
383
663
 
384
- function toolResultPart(message: OcxToolResultMessage) {
664
+ function toolResultPart(message: OcxToolResultMessage, decoded?: DecodedResultPart[], maxImages?: number) {
665
+ const parts = decoded ?? decodeResultParts(message);
666
+ const normalized = parts
667
+ ? normalizedDecodedTextResult(message, parts)
668
+ : normalizedToolResult(message, typeof message.content === "string" ? message.content : "");
385
669
  return create(McpToolResultSchema, {
386
670
  result: {
387
671
  case: "success",
388
672
  value: create(McpSuccessSchema, {
389
- isError: message.isError,
390
- content: [create(McpToolResultContentItemSchema, {
391
- content: { case: "text", value: create(McpTextContentSchema, { text: contentToText(message.content) }) },
392
- })],
673
+ isError: normalized?.isError ?? message.isError,
674
+ content: toolResultContentItems(message, parts, maxImages, normalized),
393
675
  }),
394
676
  },
395
677
  });
@@ -481,11 +763,15 @@ function conversationTurns(
481
763
  if (message.role === "toolResult") {
482
764
  if (!current) continue;
483
765
  if (externalModel) {
484
- const prefix = message.isError ? "[Tool Error]" : "[Tool Result]";
766
+ // #1920/#1866: this external-replay site bypasses toolResultToText, so it
767
+ // must consume the normalizer directly — cursor/grok-4.6 is the exact
768
+ // reported repro path for empty Computer Use results.
769
+ const normalized = normalizedToolResult(message, contentToText(message.content));
770
+ const prefix = normalized.isError ? "[Tool Error]" : "[Tool Result]";
485
771
  current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
486
772
  message: {
487
773
  case: "assistantMessage",
488
- value: create(AssistantMessageSchema, { text: `${prefix}\n${contentToText(message.content)}` }),
774
+ value: create(AssistantMessageSchema, { text: `${prefix}\n${normalized.text}` }),
489
775
  },
490
776
  })), requestScope));
491
777
  continue;
@@ -507,8 +793,10 @@ function conversationTurns(
507
793
  flush();
508
794
  current = {
509
795
  userMessage: storeCursorBlob(toBinary(UserMessageSchema, create(UserMessageSchema, {
510
- text: contentText(message),
796
+ text: historyContentText(message),
511
797
  messageId: crypto.randomUUID(),
798
+ selectedContext: buildSelectedContext([], requestScope),
799
+ mode: 1,
512
800
  })), requestScope),
513
801
  steps: [],
514
802
  };
@@ -577,19 +865,38 @@ function buildPreparedCursorRunRequest(
577
865
  const text = lastRole === "user" || lastRole === "developer"
578
866
  ? appendCursorGenericToolUseHint(request.tools, rawText)
579
867
  : rawText;
580
- // Tool-result-only turns resume the remembered Cursor conversation with results in history.
581
868
  const lastRawIsToolResult = request.rawMessages?.at(-1)?.role === "toolResult";
582
- const actionCase = !lastRawIsToolResult && text.trim().length > 0
869
+ const selectedImages = request.selectedImages ?? [];
870
+ // Native models resume the remembered Cursor conversation. External wire
871
+ // models continue as userMessageAction so history-blob tool results stay
872
+ // visible without a ResumeAction. Some native composer ids are also routed
873
+ // through the external continuation path (cursorNeedsExternalToolContinuation)
874
+ // because a bare resumeAction makes them continue exploring with native tools
875
+ // instead of answering (observed on composer-2.5; see discovery.ts).
876
+ const externalToolContinuation = lastRawIsToolResult && cursorNeedsExternalToolContinuation(request.modelId);
877
+ // Image-only active turns (including soft-omitted images) stay userMessageAction.
878
+ const actionCase = (
879
+ externalToolContinuation
880
+ || (!lastRawIsToolResult && (text.trim().length > 0 || selectedImages.length > 0))
881
+ )
583
882
  ? "userMessageAction"
584
883
  : "resumeAction";
884
+ const actionText = externalToolContinuation
885
+ ? (request.echoRetryContinuationText ?? CURSOR_EXTERNAL_TOOL_CONTINUATION_TEXT)
886
+ : request.echoRetryContinuationText
887
+ ? `${text}\n\n[correction] ${request.echoRetryContinuationText}`
888
+ : text;
585
889
  const action = create(ConversationActionSchema, {
586
890
  action: actionCase === "userMessageAction"
587
891
  ? {
588
892
  case: "userMessageAction",
589
893
  value: create(UserMessageActionSchema, {
590
894
  userMessage: create(UserMessageSchema, {
591
- text,
895
+ text: actionText,
592
896
  messageId: crypto.randomUUID(),
897
+ selectedContext: buildSelectedContext(selectedImages, requestScope),
898
+ // OmniRoute / cursor-agent always send mode=1 on UserMessage.
899
+ mode: 1,
593
900
  }),
594
901
  requestContext: buildRequestContext(),
595
902
  }),
@@ -601,9 +908,70 @@ function buildPreparedCursorRunRequest(
601
908
  }),
602
909
  },
603
910
  });
604
- const rootPromptMessagesState = rootPromptMessages(request, requestScope);
605
- const rootPromptMessageIds = rootPromptMessagesState.ids;
606
- const turnIds = conversationTurns(request, requestScope, rootPromptMessagesState.historyMessageStart);
911
+ let continuationMode: "full-replay" | "checkpoint" = "full-replay";
912
+ let checkpointInvalidationReason = request.checkpointInvalidationReason;
913
+ let conversationState: ConversationStateStructure | undefined;
914
+ let rootPromptMessagesState: ReturnType<typeof rootPromptMessages> | undefined;
915
+ if (request.checkpointBytes && request.checkpointBytes.byteLength > 0) {
916
+ try {
917
+ conversationState = fromBinary(ConversationStateStructureSchema, request.checkpointBytes);
918
+ continuationMode = "checkpoint";
919
+ const suffixStart = request.checkpointSuffixStart;
920
+ if (
921
+ typeof suffixStart === "number"
922
+ && Number.isSafeInteger(suffixStart)
923
+ && suffixStart >= 0
924
+ && request.rawMessages
925
+ && suffixStart < request.rawMessages.length
926
+ ) {
927
+ const suffixRequest: CursorRunRequest = {
928
+ ...request,
929
+ system: [],
930
+ rawMessages: request.rawMessages.slice(suffixStart),
931
+ };
932
+ const suffixRoots = rootPromptMessages(suffixRequest, requestScope);
933
+ const suffixTurns = conversationTurns(suffixRequest, requestScope, suffixRoots.historyMessageStart);
934
+ const suffixSystemCount = systemPromptBlobs(suffixRequest).length;
935
+ const suffixHistoryIds = suffixRoots.ids.slice(suffixSystemCount);
936
+ const suffixHistorySerialized = suffixRoots.serialized.slice(suffixSystemCount);
937
+ conversationState = create(ConversationStateStructureSchema, {
938
+ ...conversationState,
939
+ rootPromptMessagesJson: [
940
+ ...conversationState.rootPromptMessagesJson,
941
+ ...suffixHistoryIds,
942
+ ],
943
+ turns: [
944
+ ...conversationState.turns,
945
+ ...suffixTurns,
946
+ ],
947
+ });
948
+ rootPromptMessagesState = {
949
+ ids: suffixHistoryIds,
950
+ byteLength: suffixRoots.byteLength,
951
+ historyMessageStart: suffixRoots.historyMessageStart,
952
+ serialized: suffixHistorySerialized,
953
+ };
954
+ }
955
+ } catch {
956
+ checkpointInvalidationReason = "decode_failed";
957
+ }
958
+ }
959
+ if (!conversationState) {
960
+ rootPromptMessagesState = rootPromptMessages(request, requestScope);
961
+ conversationState = create(ConversationStateStructureSchema, {
962
+ rootPromptMessagesJson: rootPromptMessagesState.ids,
963
+ turns: conversationTurns(request, requestScope, rootPromptMessagesState.historyMessageStart),
964
+ todos: [],
965
+ pendingToolCalls: [],
966
+ previousWorkspaceUris: [],
967
+ fileStates: {},
968
+ fileStatesV2: {},
969
+ summaryArchives: [],
970
+ turnTimings: [],
971
+ subagentStates: {},
972
+ readPaths: [],
973
+ });
974
+ }
607
975
  // Hoisted out of the mcp_tools spread below so the estimate can read the same
608
976
  // filtered definitions the wire carries. Both helpers are pure.
609
977
  const visibleTools = cursorToolsForActivePrompt(request.tools, rawText, request.toolChoice);
@@ -615,46 +983,46 @@ function buildPreparedCursorRunRequest(
615
983
  turnType: lastRawIsToolResult ? "tool-continuation" : "initial",
616
984
  externalModel: isCursorExternalWireModel(request.modelId),
617
985
  rawMessages: request.rawMessages?.length ?? 0,
618
- rootBlobs: rootPromptMessageIds.length,
619
- rootBytes: rootPromptMessagesState.byteLength,
620
- turnBlobs: turnIds.length,
986
+ continuationMode,
987
+ checkpointPresent: continuationMode === "checkpoint",
988
+ checkpointBytes: continuationMode === "checkpoint" ? request.checkpointBytes?.byteLength : undefined,
989
+ checkpointInvalidationReason,
990
+ rootBlobs: conversationState.rootPromptMessagesJson.length,
991
+ rootBytes: rootPromptMessagesState?.byteLength ?? 0,
992
+ turnBlobs: conversationState.turns.length,
621
993
  tools: request.tools?.length ?? 0,
622
994
  });
623
995
 
996
+ const requestedModelParameters = [
997
+ ...(request.requestedModelParameters ?? []),
998
+ ...(request.routingLevel ? [{ id: CURSOR_ROUTING_LEVEL_PARAMETER_ID, value: request.routingLevel }] : []),
999
+ ];
1000
+ const hasExplicitModelParameters = (request.requestedModelParameters?.length ?? 0) > 0;
624
1001
  const runRequest = create(AgentRunRequestSchema, {
625
1002
  conversationId: request.conversationId,
626
- conversationState: create(ConversationStateStructureSchema, {
627
- rootPromptMessagesJson: rootPromptMessageIds,
628
- turns: turnIds,
629
- todos: [],
630
- pendingToolCalls: [],
631
- previousWorkspaceUris: [],
632
- fileStates: {},
633
- fileStatesV2: {},
634
- summaryArchives: [],
635
- turnTimings: [],
636
- subagentStates: {},
637
- readPaths: [],
638
- }),
1003
+ conversationState,
639
1004
  action,
640
- modelDetails: create(ModelDetailsSchema, {
641
- modelId: request.modelId,
642
- displayModelId: request.modelId,
643
- displayName: request.modelId,
644
- displayNameShort: request.modelId,
645
- aliases: [],
646
- }),
647
- // requested_model is currently a Cursor Router-only surface. External model clients still
648
- // send model_details alone; sending both makes external workers reach stepCompleted and then
649
- // reject the turn with invalid_argument.
650
- ...(request.routingLevel ? {
1005
+ // Explicit model-picker parameters follow current Cursor clients and use requested_model alone.
1006
+ // Keep legacy model_details for flat model ids and the already-live Router path; sending both for
1007
+ // a parameterized external model can resolve conflicting selections and end in invalid_argument.
1008
+ ...(!hasExplicitModelParameters ? {
1009
+ modelDetails: create(ModelDetailsSchema, {
1010
+ modelId: request.modelId,
1011
+ displayModelId: request.modelId,
1012
+ displayName: request.modelId,
1013
+ displayNameShort: request.modelId,
1014
+ aliases: [],
1015
+ ...(request.maxMode === true ? { maxMode: true } : {}),
1016
+ }),
1017
+ } : {}),
1018
+ ...(requestedModelParameters.length > 0 || request.maxMode === true ? {
651
1019
  requestedModel: create(RequestedModelSchema, {
652
1020
  modelId: request.modelId,
653
- maxMode: false,
654
- parameters: [create(RequestedModel_ModelParameterbytesSchema, {
655
- id: CURSOR_ROUTING_LEVEL_PARAMETER_ID,
656
- value: request.routingLevel,
657
- })],
1021
+ // Max Mode must be raised on BOTH RequestedModel and ModelDetails; missing either
1022
+ // can invalid_argument upstream (devlog 260826 070).
1023
+ maxMode: request.maxMode === true,
1024
+ parameters: requestedModelParameters.map(parameter =>
1025
+ create(RequestedModel_ModelParameterbytesSchema, parameter)),
658
1026
  }),
659
1027
  } : {}),
660
1028
  // Mirror the client (Responses) tool definitions into the top-level AgentRunRequest.mcp_tools
@@ -670,7 +1038,11 @@ function buildPreparedCursorRunRequest(
670
1038
  // the event-state `clientToolNames` use (live-transport.ts). Advertising the raw `request.tools`
671
1039
  // here would let mcp_tools expose a tool that the event state does not recognize for a generic
672
1040
  // tool-count prompt, so a call to it would be rejected as an unknown Responses tool.
673
- ...(mcpToolDefs.length > 0 ? { mcpTools: create(McpToolsSchema, { mcpTools: mcpToolDefs }) } : {}),
1041
+ // An explicitly empty McpTools wrapper (bare API callers) suppresses Cursor's default
1042
+ // native catalog; an absent field lets identified Codex sessions keep it (devlog 260826 040).
1043
+ ...(mcpToolDefs.length > 0 || request.suppressDefaultCursorToolCatalog === true
1044
+ ? { mcpTools: create(McpToolsSchema, { mcpTools: mcpToolDefs }) }
1045
+ : {}),
674
1046
  });
675
1047
 
676
1048
  const message = create(AgentClientMessageSchema, {
@@ -682,8 +1054,8 @@ function buildPreparedCursorRunRequest(
682
1054
  // Same instances that produced `bytes`, so the estimate cannot count history or
683
1055
  // tools the payload dropped — the defect that blocked PR #376.
684
1056
  const modelVisibleParts = [
685
- ...rootPromptMessagesState.serialized,
686
- ...(actionCase === "userMessageAction" ? [text] : []),
1057
+ ...(rootPromptMessagesState?.serialized ?? []),
1058
+ ...(actionCase === "userMessageAction" ? [actionText] : []),
687
1059
  ...mcpToolDefs.map(modelVisibleToolText),
688
1060
  ];
689
1061
  return {