@iislee/opencodex 2.11.0 → 2.31.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 (675) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +114 -19
  3. package/bin/ocx.mjs +164 -36
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-BF38heuV.js +104 -0
  6. package/gui/dist/assets/index-DMiI18Kv.css +1 -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 +19 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +376 -52
  44. package/src/adapters/base.ts +54 -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 +601 -0
  48. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  49. package/src/adapters/cursor/cursor-errors.ts +129 -6
  50. package/src/adapters/cursor/discovery.ts +61 -4
  51. package/src/adapters/cursor/effort-map.ts +27 -3
  52. package/src/adapters/cursor/framing.ts +39 -0
  53. package/src/adapters/cursor/h2-pool.ts +123 -0
  54. package/src/adapters/cursor/http1-bidi.ts +361 -0
  55. package/src/adapters/cursor/images.ts +704 -0
  56. package/src/adapters/cursor/live-models.ts +174 -58
  57. package/src/adapters/cursor/live-transport.ts +609 -170
  58. package/src/adapters/cursor/native-exec-common.ts +23 -2
  59. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  60. package/src/adapters/cursor/native-exec-network.ts +1 -1
  61. package/src/adapters/cursor/native-exec-shell.ts +0 -1
  62. package/src/adapters/cursor/native-exec.ts +101 -14
  63. package/src/adapters/cursor/protobuf-events.ts +829 -11
  64. package/src/adapters/cursor/protobuf-request.ts +383 -65
  65. package/src/adapters/cursor/request-builder.ts +237 -31
  66. package/src/adapters/cursor/tool-definitions.ts +242 -12
  67. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  68. package/src/adapters/cursor/transport.ts +22 -0
  69. package/src/adapters/cursor/types.ts +28 -1
  70. package/src/adapters/cursor.ts +133 -7
  71. package/src/adapters/google-antigravity-replay.ts +558 -34
  72. package/src/adapters/google-antigravity-wire.ts +43 -10
  73. package/src/adapters/google-http.ts +38 -10
  74. package/src/adapters/google-tool-schema.ts +80 -15
  75. package/src/adapters/google-truncation.ts +11 -0
  76. package/src/adapters/google.ts +618 -74
  77. package/src/adapters/identity.ts +39 -6
  78. package/src/adapters/kiro-errors.ts +11 -0
  79. package/src/adapters/kiro-events.ts +19 -1
  80. package/src/adapters/kiro-thinking.ts +18 -2
  81. package/src/adapters/kiro-tools.ts +10 -1
  82. package/src/adapters/kiro.ts +92 -54
  83. package/src/adapters/mimo-free.ts +17 -0
  84. package/src/adapters/openai-chat-url.ts +11 -0
  85. package/src/adapters/openai-chat.ts +1311 -336
  86. package/src/adapters/openai-responses-url.ts +16 -0
  87. package/src/adapters/openai-responses.ts +830 -56
  88. package/src/adapters/registry.ts +175 -0
  89. package/src/adapters/responses-tool-schema.ts +67 -0
  90. package/src/adapters/tool-call-id.ts +119 -0
  91. package/src/adapters/tool-catalog-nudge.ts +104 -21
  92. package/src/adapters/xai-web-search.ts +185 -0
  93. package/src/bridge.ts +428 -61
  94. package/src/chat/inbound.ts +43 -19
  95. package/src/chat/outbound.ts +82 -26
  96. package/src/claude/agents-inject.ts +32 -9
  97. package/src/claude/context-windows.ts +21 -5
  98. package/src/claude/desktop-3p.ts +243 -9
  99. package/src/claude/gateway-cache.ts +41 -4
  100. package/src/claude/inbound.ts +72 -3
  101. package/src/claude/model-info.ts +38 -15
  102. package/src/claude/outbound.ts +70 -16
  103. package/src/cli/account-api.ts +35 -1
  104. package/src/cli/account-auth.ts +33 -6
  105. package/src/cli/account-catalog-refresh.ts +14 -0
  106. package/src/cli/account-extended.ts +389 -2
  107. package/src/cli/account-main.ts +317 -0
  108. package/src/cli/account.ts +30 -6
  109. package/src/cli/agent.ts +45 -1
  110. package/src/cli/claude-agent-startup-sync.ts +73 -0
  111. package/src/cli/claude-desktop.ts +29 -4
  112. package/src/cli/claude.ts +103 -34
  113. package/src/cli/codex-log-guard-doctor.ts +103 -0
  114. package/src/cli/codex-shim-autorestore.ts +2 -0
  115. package/src/cli/codex-shim-readiness.ts +76 -0
  116. package/src/cli/combo.ts +8 -0
  117. package/src/cli/config-command.ts +74 -10
  118. package/src/cli/dispatch.ts +593 -0
  119. package/src/cli/doctor.ts +315 -43
  120. package/src/cli/ensure-desired-integrations.ts +152 -0
  121. package/src/cli/export-command.ts +46 -20
  122. package/src/cli/help.ts +20 -271
  123. package/src/cli/index.ts +378 -519
  124. package/src/cli/init.ts +4 -17
  125. package/src/cli/integrations.ts +120 -2
  126. package/src/cli/lab.ts +607 -0
  127. package/src/cli/launcher-context.ts +77 -0
  128. package/src/cli/minimax.ts +497 -0
  129. package/src/cli/models-runtime.ts +35 -2
  130. package/src/cli/models.ts +100 -14
  131. package/src/cli/observe.ts +92 -3
  132. package/src/cli/opencode.ts +4 -2
  133. package/src/cli/provider-runtime.ts +18 -1
  134. package/src/cli/provider.ts +24 -3
  135. package/src/cli/ready.ts +301 -0
  136. package/src/cli/registry.ts +437 -0
  137. package/src/cli/root.ts +86 -0
  138. package/src/cli/route-policy.ts +92 -0
  139. package/src/cli/runtime-api.ts +6 -3
  140. package/src/cli/star-prompt.ts +71 -15
  141. package/src/cli/status.ts +10 -3
  142. package/src/cli/system-restart-client.ts +146 -0
  143. package/src/cli/tray-proxy.ts +153 -6
  144. package/src/cli/v2.ts +105 -10
  145. package/src/cli.ts +1 -1
  146. package/src/clients/config-export.ts +1358 -21
  147. package/src/codex/account-label.ts +14 -1
  148. package/src/codex/account-lifecycle.ts +130 -13
  149. package/src/codex/account-namespaces.ts +49 -3
  150. package/src/codex/account-priority.ts +83 -0
  151. package/src/codex/account-store.ts +29 -2
  152. package/src/codex/account-usability.ts +25 -2
  153. package/src/codex/admission.ts +256 -0
  154. package/src/codex/affinity-debug.ts +162 -0
  155. package/src/codex/app-server-processes.ts +493 -106
  156. package/src/codex/app-server-restart-service.ts +232 -0
  157. package/src/codex/auth-api.ts +849 -242
  158. package/src/codex/auth-collision.ts +5 -3
  159. package/src/codex/auth-context.ts +345 -32
  160. package/src/codex/autostart-health.ts +8 -1
  161. package/src/codex/catalog/account-models.ts +67 -0
  162. package/src/codex/catalog/aggregation.ts +68 -10
  163. package/src/codex/catalog/bundled.ts +331 -33
  164. package/src/codex/catalog/effort.ts +121 -30
  165. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  166. package/src/codex/catalog/kinds.ts +2 -0
  167. package/src/codex/catalog/metadata.ts +529 -45
  168. package/src/codex/catalog/native-models.ts +72 -0
  169. package/src/codex/catalog/parsing.ts +224 -30
  170. package/src/codex/catalog/provider-fetch.ts +1460 -134
  171. package/src/codex/catalog/sync.ts +1449 -186
  172. package/src/codex/catalog-admission.ts +199 -0
  173. package/src/codex/catalog-refresh-status.ts +105 -0
  174. package/src/codex/catalog-write-serialization.ts +242 -0
  175. package/src/codex/catalog.ts +6 -3
  176. package/src/codex/codex-write-lock.ts +384 -0
  177. package/src/codex/convergence-types.ts +614 -0
  178. package/src/codex/convergence.ts +651 -0
  179. package/src/codex/coordinator-doctor.ts +332 -0
  180. package/src/codex/custom-model-catalog-migration.ts +176 -0
  181. package/src/codex/desired-state.ts +230 -0
  182. package/src/codex/features.ts +636 -39
  183. package/src/codex/generation.ts +202 -0
  184. package/src/codex/history-job.ts +407 -0
  185. package/src/codex/history-lock.ts +242 -0
  186. package/src/codex/history-migration-guardian.ts +26 -20
  187. package/src/codex/history-provider.ts +231 -28
  188. package/src/codex/history-transition.ts +105 -0
  189. package/src/codex/history-worker.ts +220 -0
  190. package/src/codex/inject-coordination.ts +290 -0
  191. package/src/codex/inject.ts +1073 -152
  192. package/src/codex/injected-marker.ts +37 -3
  193. package/src/codex/integration-record.ts +266 -0
  194. package/src/codex/internal/catalog-writer.ts +203 -0
  195. package/src/codex/internal/history-writer.ts +80 -0
  196. package/src/codex/journal.ts +66 -4
  197. package/src/codex/log-guard/inspect.ts +506 -0
  198. package/src/codex/log-guard/lock.ts +150 -0
  199. package/src/codex/log-guard/maintenance.ts +403 -0
  200. package/src/codex/log-guard/path-safety.ts +88 -0
  201. package/src/codex/log-guard/policy.ts +44 -0
  202. package/src/codex/log-guard/processes.ts +205 -0
  203. package/src/codex/log-guard/protection.ts +489 -0
  204. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  205. package/src/codex/main-account-cache.ts +24 -0
  206. package/src/codex/main-account.ts +29 -1
  207. package/src/codex/management-convergence.ts +167 -0
  208. package/src/codex/model-cache.ts +56 -10
  209. package/src/codex/model-entitlements.ts +353 -0
  210. package/src/codex/native-main-admission.ts +47 -0
  211. package/src/codex/native-main-auth-temp.ts +187 -0
  212. package/src/codex/native-main-claim.ts +178 -0
  213. package/src/codex/native-main-lock-file.ts +162 -0
  214. package/src/codex/native-main-owner.ts +329 -0
  215. package/src/codex/native-profile-api.ts +247 -0
  216. package/src/codex/native-profile-manager.ts +1531 -0
  217. package/src/codex/native-profile-processes.ts +121 -0
  218. package/src/codex/native-profile-recovery.ts +99 -0
  219. package/src/codex/native-profile-stage-store.ts +387 -0
  220. package/src/codex/native-profile-startup.ts +492 -0
  221. package/src/codex/native-profile-store.ts +855 -0
  222. package/src/codex/native-profile-types.ts +120 -0
  223. package/src/codex/native-residue.ts +682 -0
  224. package/src/codex/paths.ts +80 -1
  225. package/src/codex/plan-from-token.ts +140 -0
  226. package/src/codex/plan.ts +40 -0
  227. package/src/codex/plugins-doctor.ts +1 -1
  228. package/src/codex/pool-rotation.ts +74 -4
  229. package/src/codex/project-config-warnings.ts +20 -6
  230. package/src/codex/prompt-journal.ts +352 -0
  231. package/src/codex/prompt-layers.ts +967 -0
  232. package/src/codex/prompt-lock.ts +143 -0
  233. package/src/codex/quota-rejection.ts +298 -0
  234. package/src/codex/quota.ts +175 -13
  235. package/src/codex/refresh.ts +11 -2
  236. package/src/codex/reset-credit-recovery.ts +1044 -0
  237. package/src/codex/routing.ts +505 -94
  238. package/src/codex/runtime.ts +159 -38
  239. package/src/codex/shim.ts +1009 -28
  240. package/src/codex/subagent-model-fallback.ts +350 -35
  241. package/src/codex/sync.ts +191 -2
  242. package/src/codex/transition-state.ts +612 -0
  243. package/src/codex/upstream-host-health.ts +368 -0
  244. package/src/codex/user-identity.ts +557 -0
  245. package/src/codex/warmup.ts +187 -81
  246. package/src/codex/write-coordination.ts +114 -0
  247. package/src/combos/failover.ts +20 -0
  248. package/src/combos/index.ts +4 -0
  249. package/src/combos/request.ts +32 -0
  250. package/src/combos/types.ts +81 -9
  251. package/src/config/provider-name.ts +24 -0
  252. package/src/config.ts +1762 -140
  253. package/src/generated/compatibility-version.json +3164 -0
  254. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  255. package/src/grok/inject.ts +16 -5
  256. package/src/grok/inspect.ts +45 -0
  257. package/src/grok/sync.ts +2 -2
  258. package/src/images/loop.ts +152 -29
  259. package/src/images/plan.ts +23 -13
  260. package/src/integrations/config-io.ts +269 -0
  261. package/src/integrations/journal.ts +315 -0
  262. package/src/integrations/merge.ts +135 -0
  263. package/src/integrations/mutation-flight.ts +71 -0
  264. package/src/integrations/native/ownership-preflight.ts +202 -0
  265. package/src/integrations/omp-yaml-source.ts +358 -0
  266. package/src/integrations/owned-refresh.ts +74 -0
  267. package/src/integrations/ownership.ts +111 -0
  268. package/src/integrations/registry.ts +159 -0
  269. package/src/integrations/serialize.ts +314 -0
  270. package/src/integrations/state.ts +361 -0
  271. package/src/integrations/store.ts +103 -0
  272. package/src/integrations/writer-lock.ts +98 -0
  273. package/src/integrations/writer.ts +691 -0
  274. package/src/lab/artifacts/sanitize.ts +586 -0
  275. package/src/lab/artifacts/secure-fs.ts +475 -0
  276. package/src/lab/artifacts/store.ts +310 -0
  277. package/src/lab/automation/budgets.ts +78 -0
  278. package/src/lab/automation/config-persistence.ts +256 -0
  279. package/src/lab/automation/constants.ts +39 -0
  280. package/src/lab/automation/cooldown.ts +103 -0
  281. package/src/lab/automation/dispatch.ts +211 -0
  282. package/src/lab/automation/index.ts +13 -0
  283. package/src/lab/automation/orchestrator.ts +499 -0
  284. package/src/lab/automation/persistence.ts +512 -0
  285. package/src/lab/automation/planner.ts +371 -0
  286. package/src/lab/automation/policy.ts +136 -0
  287. package/src/lab/automation/queue.ts +191 -0
  288. package/src/lab/automation/recovery.ts +24 -0
  289. package/src/lab/automation/route-context.ts +21 -0
  290. package/src/lab/automation/run-key.ts +44 -0
  291. package/src/lab/automation/runs-query.ts +34 -0
  292. package/src/lab/automation/types.ts +160 -0
  293. package/src/lab/conformance/assertion.ts +325 -0
  294. package/src/lab/conformance/digest.ts +22 -0
  295. package/src/lab/conformance/executor.ts +741 -0
  296. package/src/lab/conformance/fixture-provider.ts +27 -0
  297. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  298. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  299. package/src/lab/conformance/harness-budget.ts +47 -0
  300. package/src/lab/conformance/index.ts +5 -0
  301. package/src/lab/conformance/jcs.ts +64 -0
  302. package/src/lab/conformance/json-pointer.ts +39 -0
  303. package/src/lab/conformance/manifest.ts +180 -0
  304. package/src/lab/conformance/mcp-stub.ts +179 -0
  305. package/src/lab/conformance/negative-controls.ts +164 -0
  306. package/src/lab/conformance/observation.ts +355 -0
  307. package/src/lab/conformance/runner.ts +68 -0
  308. package/src/lab/conformance/sse-normalize.ts +59 -0
  309. package/src/lab/conformance/suite-manifest.ts +78 -0
  310. package/src/lab/conformance/types.ts +214 -0
  311. package/src/lab/constants.ts +126 -0
  312. package/src/lab/digest.ts +64 -0
  313. package/src/lab/events/errors.ts +9 -0
  314. package/src/lab/events/limits.ts +117 -0
  315. package/src/lab/events/types.ts +229 -0
  316. package/src/lab/events/validate.ts +781 -0
  317. package/src/lab/fabric/constants.ts +40 -0
  318. package/src/lab/fabric/executor.ts +492 -0
  319. package/src/lab/fabric/index.ts +80 -0
  320. package/src/lab/fabric/manifest.ts +222 -0
  321. package/src/lab/fabric/observe.ts +489 -0
  322. package/src/lab/fabric/patch.ts +79 -0
  323. package/src/lab/fabric/producer-child.ts +139 -0
  324. package/src/lab/fabric/producer-isolate.ts +276 -0
  325. package/src/lab/fabric/producer-protocol.ts +61 -0
  326. package/src/lab/fabric/scratch.ts +439 -0
  327. package/src/lab/fabric/subject.ts +106 -0
  328. package/src/lab/fabric/types.ts +134 -0
  329. package/src/lab/fabric/verifier.ts +98 -0
  330. package/src/lab/index.ts +54 -0
  331. package/src/lab/ledger/artifact-refs.ts +127 -0
  332. package/src/lab/ledger/invalidation.ts +136 -0
  333. package/src/lab/ledger/purge.ts +310 -0
  334. package/src/lab/ledger/store.ts +532 -0
  335. package/src/lab/live/credential-lease.ts +53 -0
  336. package/src/lab/live/destination.ts +155 -0
  337. package/src/lab/live/executor.ts +336 -0
  338. package/src/lab/live/inert-tools.ts +56 -0
  339. package/src/lab/live/manifest.ts +85 -0
  340. package/src/lab/live/mcp-loopback.ts +57 -0
  341. package/src/lab/live/runner.ts +19 -0
  342. package/src/lab/live/sandbox.ts +61 -0
  343. package/src/lab/live/suite-manifest.ts +41 -0
  344. package/src/lab/live/transport.ts +118 -0
  345. package/src/lab/live/types.ts +197 -0
  346. package/src/lab/observe/from-conformance.ts +301 -0
  347. package/src/lab/observe/from-live.ts +117 -0
  348. package/src/lab/paths.ts +153 -0
  349. package/src/lab/projection/rebuild.ts +495 -0
  350. package/src/lab/projection/schema.ts +135 -0
  351. package/src/lab/projection/verdicts.ts +474 -0
  352. package/src/lab/projection/verification.ts +412 -0
  353. package/src/lab/public/bundle.ts +217 -0
  354. package/src/lab/public/community-authority.ts +175 -0
  355. package/src/lab/public/community-files.ts +29 -0
  356. package/src/lab/public/community.ts +479 -0
  357. package/src/lab/public/file-safety.ts +155 -0
  358. package/src/lab/public/ids.ts +26 -0
  359. package/src/lab/public/index.ts +16 -0
  360. package/src/lab/public/mutation-lock.ts +424 -0
  361. package/src/lab/public/operator.ts +353 -0
  362. package/src/lab/public/origin-purge.ts +79 -0
  363. package/src/lab/public/origin.ts +203 -0
  364. package/src/lab/public/privacy.ts +143 -0
  365. package/src/lab/public/private-file.ts +261 -0
  366. package/src/lab/public/project.ts +124 -0
  367. package/src/lab/public/purge-test-fault.ts +21 -0
  368. package/src/lab/public/purge.ts +223 -0
  369. package/src/lab/public/registry.ts +44 -0
  370. package/src/lab/public/revocation.ts +252 -0
  371. package/src/lab/public/signature.ts +243 -0
  372. package/src/lab/public/storage.ts +105 -0
  373. package/src/lab/public/strict-json.ts +206 -0
  374. package/src/lab/public/time.ts +26 -0
  375. package/src/lab/public/types.ts +172 -0
  376. package/src/lab/public/validate.ts +391 -0
  377. package/src/lab/query/catalog.ts +101 -0
  378. package/src/lab/query/connection.ts +107 -0
  379. package/src/lab/query/constants.ts +4 -0
  380. package/src/lab/query/cursor.ts +132 -0
  381. package/src/lab/query/dto-map.ts +277 -0
  382. package/src/lab/query/errors.ts +22 -0
  383. package/src/lab/query/freshness.ts +53 -0
  384. package/src/lab/query/index.ts +45 -0
  385. package/src/lab/query/latest-observation.ts +59 -0
  386. package/src/lab/query/passive-production.ts +159 -0
  387. package/src/lab/query/queries.ts +444 -0
  388. package/src/lab/query/types.ts +266 -0
  389. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  390. package/src/lab/subject/installation-salt.ts +112 -0
  391. package/src/lab/subject/protocol-subject.ts +80 -0
  392. package/src/lab/subject/route-subject.ts +74 -0
  393. package/src/lib/app-owned-memory-stores.ts +22 -0
  394. package/src/lib/bounded-body.ts +153 -9
  395. package/src/lib/bun-runtime.ts +125 -12
  396. package/src/lib/bun-stream-caps.ts +13 -9
  397. package/src/lib/codex-restart-contract.ts +120 -0
  398. package/src/lib/config-ownership.ts +6 -2
  399. package/src/lib/destination-policy.ts +65 -1
  400. package/src/lib/errors.ts +44 -2
  401. package/src/lib/fabric-task-execution-authority.ts +7 -0
  402. package/src/lib/fabric-task-host.ts +29 -0
  403. package/src/lib/lab-activation.ts +223 -0
  404. package/src/lib/lab-live-execution-authority.ts +13 -0
  405. package/src/lib/lab-live-host.ts +30 -0
  406. package/src/lib/lab-live-pinned-sender.ts +56 -0
  407. package/src/lib/lab-live-route-production.ts +130 -0
  408. package/src/lib/lab-passive-linker-registration.ts +26 -0
  409. package/src/lib/local-management-attestation.ts +51 -0
  410. package/src/lib/local-management-capability.ts +100 -0
  411. package/src/lib/local-provider-reload-contract.ts +100 -0
  412. package/src/lib/optional-shutdown-hooks.ts +57 -0
  413. package/src/lib/pinned-http.ts +145 -26
  414. package/src/lib/process-control.ts +4 -1
  415. package/src/lib/provider-outbound.ts +49 -9
  416. package/src/lib/redact.ts +419 -3
  417. package/src/lib/self-launch-argv.ts +15 -0
  418. package/src/lib/server-resource-ownership.ts +71 -0
  419. package/src/lib/shadow-call.ts +35 -4
  420. package/src/lib/sse-decoder.ts +41 -0
  421. package/src/lib/state-store-registrations.ts +10 -2
  422. package/src/lib/system-restart-contract.ts +73 -0
  423. package/src/lib/token-estimate.ts +19 -2
  424. package/src/lib/tool-argument-integers.ts +202 -0
  425. package/src/lib/translator-budget.ts +44 -0
  426. package/src/lib/upstream-http-version.ts +57 -0
  427. package/src/lib/upstream-reachability.ts +95 -0
  428. package/src/lib/upstream-retry.ts +156 -3
  429. package/src/lib/windows-atomic-replace.ts +155 -0
  430. package/src/lib/windows-elevation.ts +70 -2
  431. package/src/lib/windows-secret-acl.ts +409 -69
  432. package/src/lib/windows-service-wrappers.ts +72 -0
  433. package/src/lib/windows-text.ts +106 -0
  434. package/src/lib/windows-user-principal.ts +341 -0
  435. package/src/lib/winsw.ts +33 -5
  436. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  437. package/src/oauth/account-import/index.ts +15 -0
  438. package/src/oauth/account-import/parser.ts +83 -0
  439. package/src/oauth/account-import/registry.ts +18 -0
  440. package/src/oauth/account-import/service.ts +75 -0
  441. package/src/oauth/account-import/types.ts +91 -0
  442. package/src/oauth/anthropic.ts +12 -1
  443. package/src/oauth/callback-server.ts +8 -2
  444. package/src/oauth/chatgpt.ts +12 -1
  445. package/src/oauth/command-code.ts +239 -0
  446. package/src/oauth/cursor.ts +46 -5
  447. package/src/oauth/google-antigravity.ts +35 -3
  448. package/src/oauth/health.ts +20 -12
  449. package/src/oauth/index.ts +398 -66
  450. package/src/oauth/key-providers.ts +16 -0
  451. package/src/oauth/kimi.ts +16 -2
  452. package/src/oauth/kiro.ts +50 -6
  453. package/src/oauth/local-token-detect.ts +11 -2
  454. package/src/oauth/log.ts +3 -1
  455. package/src/oauth/login-cli.ts +88 -28
  456. package/src/oauth/nous.ts +798 -0
  457. package/src/oauth/store.ts +119 -21
  458. package/src/oauth/token-guardian.ts +9 -3
  459. package/src/pi/models.ts +2 -2
  460. package/src/providers/alibaba-region-migration.ts +1 -1
  461. package/src/providers/antigravity-models.ts +521 -31
  462. package/src/providers/base-url-choices.ts +10 -0
  463. package/src/providers/codex-capacity.ts +292 -0
  464. package/src/providers/command-code-efforts.ts +144 -0
  465. package/src/providers/context-cap.ts +22 -5
  466. package/src/providers/cursor-pool.ts +72 -0
  467. package/src/providers/derive.ts +253 -6
  468. package/src/providers/fastwire.ts +501 -0
  469. package/src/providers/free-directory.ts +10 -7
  470. package/src/providers/google-vertex-location.ts +14 -0
  471. package/src/providers/key-failover.ts +71 -3
  472. package/src/providers/label.ts +1 -1
  473. package/src/providers/model-discovery-limits.ts +16 -0
  474. package/src/providers/model-discovery.ts +115 -22
  475. package/src/providers/model-rename-migration.ts +255 -0
  476. package/src/providers/model-rename-startup.ts +28 -0
  477. package/src/providers/openai-sidecar.ts +72 -4
  478. package/src/providers/openai-tier-startup.ts +31 -2
  479. package/src/providers/openai-tiers.ts +119 -4
  480. package/src/providers/openai-virtual-models.ts +1 -0
  481. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  482. package/src/providers/provider-id-rewrite.ts +29 -0
  483. package/src/providers/quota.ts +1319 -38
  484. package/src/providers/registry.ts +1429 -111
  485. package/src/providers/request-pacing.ts +310 -0
  486. package/src/providers/service-tier.ts +277 -0
  487. package/src/providers/slug-codec.ts +42 -6
  488. package/src/providers/static-model-discovery.ts +86 -0
  489. package/src/providers/xai-responses-opt-in.ts +15 -0
  490. package/src/providers/xai-transport.ts +11 -4
  491. package/src/reasoning-effort.ts +49 -1
  492. package/src/responses/compaction.ts +26 -1
  493. package/src/responses/custom-tool-compat.ts +266 -0
  494. package/src/responses/hosted-tool-policy.ts +9 -0
  495. package/src/responses/namespace-tool-compat.ts +355 -0
  496. package/src/responses/parser.ts +220 -38
  497. package/src/responses/provider-continuation.ts +98 -0
  498. package/src/responses/provider-opaque-metadata.ts +73 -0
  499. package/src/responses/reasoning-envelope.ts +9 -1
  500. package/src/responses/reasoning-replay-cache.ts +426 -0
  501. package/src/responses/schema.ts +7 -1
  502. package/src/responses/spill-store.ts +75 -10
  503. package/src/responses/state.ts +565 -27
  504. package/src/responses/thought-signature-replay.ts +347 -0
  505. package/src/responses/tool-search-compat.ts +301 -0
  506. package/src/responses/truncated-stop-reason.ts +60 -0
  507. package/src/router.ts +366 -30
  508. package/src/routing/analytics.ts +378 -0
  509. package/src/routing/capability.ts +244 -0
  510. package/src/routing/compatibility/assemble.ts +73 -0
  511. package/src/routing/compatibility/behavior.ts +278 -0
  512. package/src/routing/compatibility/catalog.ts +99 -0
  513. package/src/routing/compatibility/endpoint.ts +52 -0
  514. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  515. package/src/routing/compatibility/policy.ts +181 -0
  516. package/src/routing/compatibility/provider-slot.ts +56 -0
  517. package/src/routing/compatibility/reader.ts +110 -0
  518. package/src/routing/compatibility/subject.ts +191 -0
  519. package/src/routing/compatibility/types.ts +64 -0
  520. package/src/routing/compatibility/version.ts +104 -0
  521. package/src/routing/cost.ts +77 -0
  522. package/src/routing/evaluator.ts +495 -0
  523. package/src/routing/health.ts +412 -0
  524. package/src/routing/history/cursor.ts +43 -0
  525. package/src/routing/history/indexer.ts +605 -0
  526. package/src/routing/history/schema.ts +72 -0
  527. package/src/routing/profile-namespace.ts +15 -0
  528. package/src/routing/profile.ts +547 -0
  529. package/src/routing/quota.ts +145 -0
  530. package/src/routing/request-evidence.ts +45 -0
  531. package/src/routing/trace.ts +776 -0
  532. package/src/server/adapter-resolve.ts +2 -29
  533. package/src/server/auth-cors.ts +267 -46
  534. package/src/server/background-lifecycle.ts +182 -0
  535. package/src/server/chat-completions.ts +130 -56
  536. package/src/server/chat-native-sse.ts +331 -0
  537. package/src/server/chat-native.ts +426 -0
  538. package/src/server/claude-messages.ts +159 -43
  539. package/src/server/direct-local-http.ts +347 -0
  540. package/src/server/effort-policy.ts +18 -0
  541. package/src/server/github-copilot-responses-repair.ts +338 -0
  542. package/src/server/gui-static.ts +39 -10
  543. package/src/server/images.ts +94 -12
  544. package/src/server/index.ts +865 -181
  545. package/src/server/lifecycle.ts +284 -13
  546. package/src/server/live.ts +136 -17
  547. package/src/server/local-management-read-client.ts +90 -0
  548. package/src/server/local-provider-reload-client.ts +137 -0
  549. package/src/server/management/agent-settings-routes.ts +398 -103
  550. package/src/server/management/api-key-usage.ts +31 -5
  551. package/src/server/management/body.ts +6 -0
  552. package/src/server/management/combo-routes.ts +62 -24
  553. package/src/server/management/config-routes.ts +464 -51
  554. package/src/server/management/context.ts +80 -2
  555. package/src/server/management/integration-routes.ts +498 -0
  556. package/src/server/management/lab-automation-routes.ts +206 -0
  557. package/src/server/management/lab-routes.ts +563 -0
  558. package/src/server/management/logs-usage-routes.ts +101 -32
  559. package/src/server/management/model-routes.ts +189 -131
  560. package/src/server/management/model-rows.ts +163 -0
  561. package/src/server/management/native-integration-routes.ts +769 -0
  562. package/src/server/management/oauth-account-routes.ts +80 -4
  563. package/src/server/management/provider-capability-config.ts +48 -0
  564. package/src/server/management/provider-routes.ts +764 -157
  565. package/src/server/management/request-history-routes.ts +191 -0
  566. package/src/server/management/routing-analytics-routes.ts +74 -0
  567. package/src/server/management/routing-profile-routes.ts +380 -0
  568. package/src/server/management/shared.ts +27 -11
  569. package/src/server/management/sidebar-routes.ts +47 -31
  570. package/src/server/management/storage-log-guard-routes.ts +186 -0
  571. package/src/server/management/sync-response.ts +69 -0
  572. package/src/server/management/system-restart.ts +288 -32
  573. package/src/server/management/system-routes.ts +77 -0
  574. package/src/server/management/usage-summary-cache.ts +9 -1
  575. package/src/server/management/vision-sidecar-options.ts +167 -0
  576. package/src/server/management/web-search-sidecar-options.ts +120 -0
  577. package/src/server/management-api.ts +115 -14
  578. package/src/server/management-auth.ts +220 -5
  579. package/src/server/passive-route-linker.ts +66 -0
  580. package/src/server/ports.ts +41 -1
  581. package/src/server/proxy-liveness.ts +132 -5
  582. package/src/server/readiness.ts +99 -0
  583. package/src/server/relay-eager.ts +82 -42
  584. package/src/server/relay.ts +236 -76
  585. package/src/server/request-decompress.ts +113 -6
  586. package/src/server/request-log.ts +235 -22
  587. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  588. package/src/server/responses/agent-task-recovery.ts +465 -0
  589. package/src/server/responses/collaboration.ts +204 -35
  590. package/src/server/responses/compact.ts +442 -55
  591. package/src/server/responses/core.ts +2872 -331
  592. package/src/server/responses/empty-completion-guard.ts +276 -0
  593. package/src/server/responses/encrypted-payload.ts +62 -39
  594. package/src/server/responses/fetch-helpers.ts +79 -4
  595. package/src/server/responses/input-admission.ts +185 -0
  596. package/src/server/responses/pacing-overload.ts +13 -0
  597. package/src/server/responses/policy-fallback.ts +178 -0
  598. package/src/server/responses/responses-field-backfill.ts +251 -0
  599. package/src/server/responses/terminal-guard.ts +26 -5
  600. package/src/server/responses/upstream-error.ts +5 -0
  601. package/src/server/responses/ws-upstream.ts +308 -0
  602. package/src/server/responses-custom-tool-repair.ts +282 -0
  603. package/src/server/responses-item-id-repair.ts +54 -6
  604. package/src/server/responses-json-events.ts +90 -0
  605. package/src/server/responses-model-rewrite.ts +29 -0
  606. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  607. package/src/server/responses-snapshot-repair.ts +621 -0
  608. package/src/server/responses-terminal-repair.ts +342 -0
  609. package/src/server/responses-tool-search-repair.ts +267 -0
  610. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  611. package/src/server/responses.ts +18 -2
  612. package/src/server/search.ts +78 -13
  613. package/src/server/sse-frame-buffer.ts +292 -0
  614. package/src/server/sse-payload-rewrite.ts +110 -22
  615. package/src/server/startup-action-control.ts +8 -1
  616. package/src/server/startup-health-cache.ts +19 -1
  617. package/src/server/system-env.ts +80 -9
  618. package/src/server/ws-bridge.ts +39 -38
  619. package/src/service-manager-probe.ts +892 -0
  620. package/src/service.ts +1111 -90
  621. package/src/sidecar/auth.ts +92 -0
  622. package/src/sidecar/candidates.ts +83 -0
  623. package/src/storage/cleanup.ts +2 -2
  624. package/src/storage/scanner.ts +1 -1
  625. package/src/storage/worker-lifecycle.ts +14 -14
  626. package/src/tray/windows-tray.ps1 +83 -9
  627. package/src/tray/windows.ts +43 -16
  628. package/src/types/accounts.ts +37 -0
  629. package/src/types/config.ts +845 -0
  630. package/src/types/provider.ts +545 -0
  631. package/src/types/request.ts +384 -0
  632. package/src/types/tools.ts +131 -0
  633. package/src/types/wire.ts +80 -0
  634. package/src/types.ts +104 -1236
  635. package/src/update/index.ts +32 -19
  636. package/src/update/job.ts +442 -67
  637. package/src/update/notify.ts +12 -6
  638. package/src/update/npm-cache-preflight.d.mts +47 -0
  639. package/src/update/npm-cache-preflight.mjs +201 -0
  640. package/src/update/transactional-install.d.mts +22 -0
  641. package/src/update/transactional-install.mjs +259 -0
  642. package/src/usage/cost.ts +0 -0
  643. package/src/usage/expected-prices.ts +268 -16
  644. package/src/usage/log.ts +606 -41
  645. package/src/usage/summary.ts +177 -9
  646. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  647. package/src/usage/user-cost-overlays.ts +314 -0
  648. package/src/vision/anthropic-describe.ts +10 -6
  649. package/src/vision/backends.ts +97 -0
  650. package/src/vision/describe.ts +9 -3
  651. package/src/vision/eligibility.ts +250 -0
  652. package/src/vision/index.ts +238 -24
  653. package/src/vision/reasoning.ts +55 -0
  654. package/src/vision/routed-describe.ts +175 -0
  655. package/src/vision/timeout-bounds.ts +9 -0
  656. package/src/web-search/anthropic-executor.ts +13 -7
  657. package/src/web-search/backends.ts +108 -0
  658. package/src/web-search/exa-executor.ts +88 -0
  659. package/src/web-search/executor.ts +11 -3
  660. package/src/web-search/gemini-executor.ts +141 -0
  661. package/src/web-search/index.ts +150 -15
  662. package/src/web-search/loop.ts +279 -50
  663. package/src/web-search/parse.ts +125 -30
  664. package/src/web-search/sources.ts +60 -0
  665. package/src/web-search/xai-executor.ts +219 -0
  666. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  667. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  668. package/gui/dist/provider-icons/antigravity.svg +0 -1
  669. package/gui/dist/provider-icons/claude.svg +0 -1
  670. package/gui/dist/provider-icons/copilot.svg +0 -1
  671. package/gui/dist/provider-icons/cursor.svg +0 -2
  672. package/gui/dist/provider-icons/gemini.svg +0 -1
  673. package/gui/dist/provider-icons/grok-color.svg +0 -1
  674. package/gui/dist/provider-icons/kiro.svg +0 -14
  675. package/src/cli/internal-dispatch.ts +0 -20
@@ -0,0 +1,704 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { create } from "@bufbuild/protobuf";
3
+ import type { OcxContentPart, OcxImageContent, OcxMessage } from "../../types";
4
+ import {
5
+ SelectedContextSchema,
6
+ SelectedImageSchema,
7
+ SelectedImage_BlobIdWithDataSchema,
8
+ SelectedImage_DimensionSchema,
9
+ type SelectedContext,
10
+ type SelectedImage,
11
+ } from "./gen/agent_pb";
12
+ import {
13
+ storeCursorBlob,
14
+ type CursorBlobRequestScopeToken,
15
+ } from "./native-exec";
16
+
17
+ /** Final per-image byte cap after prep (OmniRoute / composer-api style). */
18
+ export const MAX_CURSOR_IMAGE_BYTES = 1024 * 1024;
19
+
20
+ /**
21
+ * Inbound decode/fetch bomb ceiling before JPEG prep. Large clipboard PNGs may exceed
22
+ * {@link MAX_CURSOR_IMAGE_BYTES} raw but shrink under the wire cap after re-encode.
23
+ */
24
+ export const MAX_CURSOR_IMAGE_DECODE_BYTES = 16 * 1024 * 1024;
25
+
26
+ /**
27
+ * Soft target for Cursor vision hydration. Live A/B: ~430 KiB PNG failed ("gray"/wrong UI)
28
+ * while the same visual as ~75 KiB JPEG succeeded. Prefer JPEG at or under this size.
29
+ */
30
+ export const CURSOR_VISION_SOFT_MAX_BYTES = 100 * 1024;
31
+
32
+ /** Soft target when the client requests `detail: original` or `high`. */
33
+ export const CURSOR_VISION_SOFT_MAX_BYTES_HIGH = 256 * 1024;
34
+
35
+ /** Longest edge after Cursor vision prep (Cursor staff guidance: ≤ 2000 px). */
36
+ export const CURSOR_VISION_MAX_EDGE = 2000;
37
+
38
+ /**
39
+ * Decode bomb: reject images whose sniffed longest edge exceeds this before Bun.Image.
40
+ * Separate from {@link CURSOR_VISION_MAX_EDGE} (output resize target).
41
+ */
42
+ export const MAX_CURSOR_IMAGE_DECODE_EDGE = 8192;
43
+
44
+ /** Decode bomb: reject images whose sniffed pixel count exceeds this before Bun.Image. */
45
+ export const MAX_CURSOR_IMAGE_PIXELS = 25_000_000;
46
+
47
+ const CURSOR_VISION_JPEG_QUALITIES_DEFAULT = [85, 70, 55, 40] as const;
48
+ const CURSOR_VISION_JPEG_QUALITIES_HIGH = [90, 80, 65, 50] as const;
49
+ /** Stop shrinking below this longest edge when chasing the soft byte cap. */
50
+ const CURSOR_VISION_SOFT_MIN_EDGE = 256;
51
+ const CURSOR_VISION_SOFT_SHRINK = 0.85;
52
+
53
+ const CURSOR_VISION_PASSTHROUGH_MIME = new Set([
54
+ "image/jpeg",
55
+ "image/jpg",
56
+ "image/png",
57
+ "image/gif",
58
+ "image/webp",
59
+ ]);
60
+
61
+ /** Upper bound on images attached to one Cursor turn. */
62
+ export const MAX_CURSOR_IMAGES = 12;
63
+
64
+ /** Marker when an image cannot be prepared for the Cursor vision wire. */
65
+ export const CURSOR_VISION_IMAGE_OMITTED =
66
+ "[image omitted: undecodable or unsupported type]";
67
+
68
+ /** Short text-only stand-in for an image part on replayed (historical) turns. Never includes bytes. */
69
+ export const CURSOR_VISION_IMAGE_HISTORY_MARKER = "[image attached]";
70
+
71
+ export class CursorImageError extends Error {
72
+ readonly status: number;
73
+
74
+ constructor(message: string, status = 400) {
75
+ super(message);
76
+ this.name = "CursorImageError";
77
+ this.status = status;
78
+ }
79
+ }
80
+
81
+ export interface ResolvedCursorImage {
82
+ data: Uint8Array;
83
+ mimeType: string;
84
+ uuid: string;
85
+ /** Codex/OpenAI image detail hint; affects JPEG soft-cap tier. */
86
+ detail?: string;
87
+ }
88
+
89
+ export type PrepareCursorImageOutcome =
90
+ | { status: "ready"; image: ResolvedCursorImage }
91
+ | { status: "omitted"; reason: string };
92
+
93
+ function isImagePart(part: OcxContentPart): part is OcxImageContent {
94
+ return part.type === "image";
95
+ }
96
+
97
+ function estimatedBase64DecodedBytes(payload: string): number {
98
+ return Math.floor((payload.length * 3) / 4);
99
+ }
100
+
101
+ function isHighDetail(detail: string | undefined): boolean {
102
+ const normalized = (detail ?? "").trim().toLowerCase();
103
+ return normalized === "original" || normalized === "high";
104
+ }
105
+
106
+ function softMaxBytesForDetail(detail: string | undefined): number {
107
+ return isHighDetail(detail) ? CURSOR_VISION_SOFT_MAX_BYTES_HIGH : CURSOR_VISION_SOFT_MAX_BYTES;
108
+ }
109
+
110
+ function jpegQualitiesForDetail(detail: string | undefined): readonly number[] {
111
+ return isHighDetail(detail) ? CURSOR_VISION_JPEG_QUALITIES_HIGH : CURSOR_VISION_JPEG_QUALITIES_DEFAULT;
112
+ }
113
+
114
+ export function decodeCursorImageDataUrl(url: string): { data: Uint8Array; mimeType: string } {
115
+ const comma = url.indexOf(",");
116
+ if (comma < 0) throw new CursorImageError("Image data URL is malformed.");
117
+ const header = url.slice(5, comma);
118
+ const payload = url.slice(comma + 1);
119
+ const isBase64 = /;base64/i.test(header);
120
+ const mimeType = (header.split(";")[0] || "").trim().toLowerCase() || "application/octet-stream";
121
+
122
+ if (!mimeType.startsWith("image/")) {
123
+ throw new CursorImageError("Image data URL must have an image/* media type.");
124
+ }
125
+ if (!isBase64) {
126
+ throw new CursorImageError("Image data URL must be base64-encoded.");
127
+ }
128
+ if (payload.length > MAX_CURSOR_IMAGE_DECODE_BYTES * 2) {
129
+ throw new CursorImageError("Image input is too large to process safely.");
130
+ }
131
+
132
+ const normalized = payload.replace(/\s/g, "");
133
+ if (normalized.length === 0) {
134
+ throw new CursorImageError("Image data URL contains invalid base64 data.");
135
+ }
136
+ // Reject lenient Buffer.from acceptances (wrong alphabet, bad padding, truncated groups).
137
+ if (normalized.length % 4 !== 0 || !/^[A-Za-z0-9+/]+={0,2}$/.test(normalized)) {
138
+ throw new CursorImageError("Image data URL contains invalid base64 data.");
139
+ }
140
+ if (estimatedBase64DecodedBytes(normalized) > MAX_CURSOR_IMAGE_DECODE_BYTES) {
141
+ throw new CursorImageError("Image input is too large to process safely.");
142
+ }
143
+
144
+ let data: Uint8Array;
145
+ try {
146
+ data = Buffer.from(normalized, "base64");
147
+ } catch {
148
+ throw new CursorImageError("Image data URL contains invalid base64 data.");
149
+ }
150
+ if (data.byteLength === 0) {
151
+ throw new CursorImageError("Image data URL contains invalid base64 data.");
152
+ }
153
+ // Round-trip guard: Node/Bun can silently drop trailing garbage.
154
+ if (Buffer.from(data).toString("base64").replace(/=+$/, "") !== normalized.replace(/=+$/, "")) {
155
+ throw new CursorImageError("Image data URL contains invalid base64 data.");
156
+ }
157
+ if (data.byteLength > MAX_CURSOR_IMAGE_DECODE_BYTES) {
158
+ throw new CursorImageError("Image input is too large to process safely.");
159
+ }
160
+ return { data, mimeType };
161
+ }
162
+
163
+ function throwIfImagePhaseAborted(signal?: AbortSignal): void {
164
+ if (!signal?.aborted) return;
165
+ if (signal.reason instanceof Error) throw signal.reason;
166
+ const err = new Error("Cursor image phase aborted");
167
+ err.name = "AbortError";
168
+ throw err;
169
+ }
170
+
171
+ /** Magic-byte format sniff (independent of declared MIME). */
172
+ export function sniffCursorImageFormat(
173
+ data: Uint8Array,
174
+ ): "png" | "jpeg" | "gif" | "webp" | undefined {
175
+ if (
176
+ data.byteLength >= 8
177
+ && data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4e && data[3] === 0x47
178
+ && data[4] === 0x0d && data[5] === 0x0a && data[6] === 0x1a && data[7] === 0x0a
179
+ ) {
180
+ return "png";
181
+ }
182
+ if (
183
+ data.byteLength >= 6
184
+ && data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46 && data[3] === 0x38
185
+ ) {
186
+ return "gif";
187
+ }
188
+ if (data.byteLength >= 4 && data[0] === 0xff && data[1] === 0xd8) return "jpeg";
189
+ if (
190
+ data.byteLength >= 12
191
+ && data[0] === 0x52 && data[1] === 0x49 && data[2] === 0x46 && data[3] === 0x46
192
+ && data[8] === 0x57 && data[9] === 0x45 && data[10] === 0x42 && data[11] === 0x50
193
+ ) {
194
+ return "webp";
195
+ }
196
+ return undefined;
197
+ }
198
+
199
+ /** Collect image URLs from one message's content parts, preserving order. */
200
+ export function extractCursorImageUrls(content: string | readonly OcxContentPart[]): string[] {
201
+ return extractCursorImageParts(content).map(part => part.imageUrl);
202
+ }
203
+
204
+ export interface CursorImagePartRef {
205
+ imageUrl: string;
206
+ detail?: string;
207
+ }
208
+
209
+ /** Collect image parts (URL + optional detail) from one message's content. */
210
+ export function extractCursorImageParts(
211
+ content: string | readonly OcxContentPart[],
212
+ ): CursorImagePartRef[] {
213
+ if (typeof content === "string" || !Array.isArray(content)) return [];
214
+ const parts: CursorImagePartRef[] = [];
215
+ for (const part of content) {
216
+ if (isImagePart(part) && typeof part.imageUrl === "string" && part.imageUrl.length > 0) {
217
+ parts.push({
218
+ imageUrl: part.imageUrl,
219
+ ...(typeof part.detail === "string" && part.detail.length > 0 ? { detail: part.detail } : {}),
220
+ });
221
+ }
222
+ }
223
+ return parts;
224
+ }
225
+
226
+ /**
227
+ * Resolve OpenCodex image parts (data: URLs only) into bytes for SelectedImage.
228
+ * Prep (JPEG soft-cap) runs before the 1 MiB wire cap so large clipboard PNGs can shrink.
229
+ * Unsupported / undecodable images are omitted (fail-closed).
230
+ */
231
+ export async function resolveCursorImages(
232
+ imageUrls: readonly string[],
233
+ signal?: AbortSignal,
234
+ options?: { details?: readonly (string | undefined)[] },
235
+ ): Promise<ResolvedCursorImage[]> {
236
+ if (imageUrls.length > MAX_CURSOR_IMAGES) {
237
+ throw new CursorImageError(`Too many images in one request (max ${MAX_CURSOR_IMAGES}).`);
238
+ }
239
+
240
+ const out: ResolvedCursorImage[] = [];
241
+ for (let i = 0; i < imageUrls.length; i++) {
242
+ throwIfImagePhaseAborted(signal);
243
+ const url = imageUrls[i];
244
+ if (typeof url !== "string" || url.length === 0) {
245
+ // Soft-omit missing URLs rather than aborting a mixed turn.
246
+ continue;
247
+ }
248
+ // Remote URL fetching is deliberately out of scope here; https:// images are omitted.
249
+ if (!url.toLowerCase().startsWith("data:")) continue;
250
+ try {
251
+ const resolved = decodeCursorImageDataUrl(url);
252
+ if (resolved.data.byteLength === 0) continue;
253
+ const outcome = await prepareCursorImageForWire({
254
+ data: resolved.data,
255
+ mimeType: resolved.mimeType,
256
+ uuid: randomUUID(),
257
+ ...(options?.details?.[i] ? { detail: options.details[i] } : {}),
258
+ }, signal);
259
+ if (outcome.status === "omitted") continue;
260
+ if (outcome.image.data.byteLength > MAX_CURSOR_IMAGE_BYTES) continue;
261
+ out.push(outcome.image);
262
+ } catch (err) {
263
+ if (signal?.aborted || (err instanceof Error && err.name === "AbortError")) throw err;
264
+ if (err instanceof CursorImageError) continue;
265
+ continue;
266
+ }
267
+ }
268
+ return out;
269
+ }
270
+
271
+ export async function resolveCursorImageParts(
272
+ parts: readonly CursorImagePartRef[],
273
+ signal?: AbortSignal,
274
+ ): Promise<ResolvedCursorImage[]> {
275
+ return resolveCursorImages(
276
+ parts.map(part => part.imageUrl),
277
+ signal,
278
+ { details: parts.map(part => part.detail) },
279
+ );
280
+ }
281
+
282
+ /** Filename Cursor clients typically put on SelectedImage.path (shunt / agent parity). */
283
+ export function cursorImageAttachmentPath(uuid: string, mimeType: string): string {
284
+ const normalized = mimeType.toLowerCase();
285
+ const ext = normalized === "image/jpeg" || normalized === "image/jpg" ? "jpg"
286
+ : normalized === "image/gif" ? "gif"
287
+ : normalized === "image/webp" ? "webp"
288
+ : "png";
289
+ return `attachment-${uuid}.${ext}`;
290
+ }
291
+
292
+ /**
293
+ * Re-encode toward a JPEG under the soft vision cap when Bun can decode the payload.
294
+ * Unsupported MIME, oversize dimensions/pixels, or undecodable bytes are omitted (fail-closed).
295
+ * After the quality ladder, edges shrink iteratively until the soft byte cap is met
296
+ * (or the min edge floor is hit) so large clipboard PNGs do not leave >softMax JPEGs
297
+ * that Cursor vision hallucinates on.
298
+ */
299
+ export async function prepareCursorImageForWire(
300
+ image: ResolvedCursorImage,
301
+ signal?: AbortSignal,
302
+ testHooks?: { softMaxBytes?: number },
303
+ ): Promise<PrepareCursorImageOutcome> {
304
+ throwIfImagePhaseAborted(signal);
305
+ const mime = image.mimeType.toLowerCase();
306
+ const softMax = testHooks?.softMaxBytes ?? softMaxBytesForDetail(image.detail);
307
+ const qualities = jpegQualitiesForDetail(image.detail);
308
+ const lowestQuality = qualities[qualities.length - 1]!;
309
+
310
+ if (!CURSOR_VISION_PASSTHROUGH_MIME.has(mime)) {
311
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
312
+ }
313
+
314
+ const format = sniffCursorImageFormat(image.data);
315
+ // Peek headers before Bun.Image so huge compressed bombs fail closed cheaply.
316
+ const sniffed = sniffCursorImageDimensions(image.data);
317
+ if (!sniffed) {
318
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
319
+ }
320
+ const sniffedEdge = Math.max(sniffed.width, sniffed.height);
321
+ const sniffedPixels = sniffed.width * sniffed.height;
322
+ if (sniffedEdge > MAX_CURSOR_IMAGE_DECODE_EDGE || sniffedPixels > MAX_CURSOR_IMAGE_PIXELS) {
323
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
324
+ }
325
+
326
+ const declaredJpeg = mime === "image/jpeg" || mime === "image/jpg";
327
+
328
+ try {
329
+ throwIfImagePhaseAborted(signal);
330
+ // metadata() decodes; reuse it as the Anthropic-style validate pass.
331
+ const meta = await new Bun.Image(image.data).metadata();
332
+
333
+ // Passthrough only after a successful decode, and only when declared MIME
334
+ // matches actual JPEG magic (never PNG-as-JPEG or SOF-only junk).
335
+ if (declaredJpeg && format === "jpeg" && image.data.byteLength <= softMax) {
336
+ return { status: "ready", image };
337
+ }
338
+
339
+ throwIfImagePhaseAborted(signal);
340
+ const width = typeof meta.width === "number" ? meta.width : 0;
341
+ const height = typeof meta.height === "number" ? meta.height : 0;
342
+ if (width > 0 && height > 0) {
343
+ const edge = Math.max(width, height);
344
+ if (edge > MAX_CURSOR_IMAGE_DECODE_EDGE || width * height > MAX_CURSOR_IMAGE_PIXELS) {
345
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
346
+ }
347
+ }
348
+ let targetW = width;
349
+ let targetH = height;
350
+ if (width > 0 && height > 0 && Math.max(width, height) > CURSOR_VISION_MAX_EDGE) {
351
+ const scale = CURSOR_VISION_MAX_EDGE / Math.max(width, height);
352
+ targetW = Math.max(1, Math.round(width * scale));
353
+ targetH = Math.max(1, Math.round(height * scale));
354
+ }
355
+
356
+ const encodeAt = async (w: number, h: number, quality: number): Promise<Uint8Array> => {
357
+ throwIfImagePhaseAborted(signal);
358
+ let pipeline = new Bun.Image(image.data);
359
+ if (w > 0 && h > 0 && (w !== width || h !== height)) {
360
+ pipeline = pipeline.resize(w, h);
361
+ }
362
+ return new Uint8Array(await pipeline.jpeg({ quality }).bytes());
363
+ };
364
+
365
+ let best: Uint8Array | undefined;
366
+ for (const quality of qualities) {
367
+ const encoded = await encodeAt(targetW, targetH, quality);
368
+ if (!best || encoded.byteLength < best.byteLength) best = encoded;
369
+ if (encoded.byteLength <= softMax) {
370
+ return {
371
+ status: "ready",
372
+ image: { ...image, data: encoded, mimeType: "image/jpeg" },
373
+ };
374
+ }
375
+ }
376
+
377
+ // Quality ladder missed the soft cap — shrink edges until it fits or we hit the floor.
378
+ while (
379
+ best
380
+ && best.byteLength > softMax
381
+ && targetW > 0
382
+ && targetH > 0
383
+ && Math.max(targetW, targetH) > CURSOR_VISION_SOFT_MIN_EDGE
384
+ ) {
385
+ throwIfImagePhaseAborted(signal);
386
+ const nextW = Math.max(1, Math.round(targetW * CURSOR_VISION_SOFT_SHRINK));
387
+ const nextH = Math.max(1, Math.round(targetH * CURSOR_VISION_SOFT_SHRINK));
388
+ if (Math.max(nextW, nextH) < CURSOR_VISION_SOFT_MIN_EDGE) {
389
+ const scale = CURSOR_VISION_SOFT_MIN_EDGE / Math.max(targetW, targetH);
390
+ targetW = Math.max(1, Math.round(targetW * scale));
391
+ targetH = Math.max(1, Math.round(targetH * scale));
392
+ } else {
393
+ targetW = nextW;
394
+ targetH = nextH;
395
+ }
396
+ const encoded = await encodeAt(targetW, targetH, lowestQuality);
397
+ if (!best || encoded.byteLength < best.byteLength) best = encoded;
398
+ if (encoded.byteLength <= softMax) {
399
+ return {
400
+ status: "ready",
401
+ image: { ...image, data: encoded, mimeType: "image/jpeg" },
402
+ };
403
+ }
404
+ if (Math.max(targetW, targetH) <= CURSOR_VISION_SOFT_MIN_EDGE) break;
405
+ }
406
+
407
+ if (best && best.byteLength <= softMax) {
408
+ return {
409
+ status: "ready",
410
+ image: { ...image, data: best, mimeType: "image/jpeg" },
411
+ };
412
+ }
413
+ if (best) {
414
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
415
+ }
416
+ // Undeclared/mismatched magic with no encode result — omit rather than lie about MIME.
417
+ if (declaredJpeg && format !== "jpeg") {
418
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
419
+ }
420
+ return { status: "ready", image };
421
+ } catch (err) {
422
+ if (signal?.aborted || (err instanceof Error && err.name === "AbortError")) throw err;
423
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
424
+ }
425
+ }
426
+
427
+ /**
428
+ * Sniff PNG/JPEG/GIF/WebP dimensions from raw bytes when the header is present.
429
+ * Best-effort only — unknown formats return undefined (dimension is optional).
430
+ */
431
+ export function sniffCursorImageDimensions(
432
+ data: Uint8Array,
433
+ ): { width: number; height: number } | undefined {
434
+ // PNG: signature + IHDR chunk (width/height at bytes 16..23)
435
+ if (
436
+ data.byteLength >= 24
437
+ && data[0] === 0x89 && data[1] === 0x50 && data[2] === 0x4e && data[3] === 0x47
438
+ && data[4] === 0x0d && data[5] === 0x0a && data[6] === 0x1a && data[7] === 0x0a
439
+ ) {
440
+ const width = ((data[16]! << 24) | (data[17]! << 16) | (data[18]! << 8) | data[19]!) >>> 0;
441
+ const height = ((data[20]! << 24) | (data[21]! << 16) | (data[22]! << 8) | data[23]!) >>> 0;
442
+ if (width > 0 && height > 0) return { width, height };
443
+ }
444
+ // GIF: "GIF8" + width/height as little-endian u16 at bytes 6..9
445
+ if (
446
+ data.byteLength >= 10
447
+ && data[0] === 0x47 && data[1] === 0x49 && data[2] === 0x46 && data[3] === 0x38
448
+ ) {
449
+ const width = data[6]! | (data[7]! << 8);
450
+ const height = data[8]! | (data[9]! << 8);
451
+ if (width > 0 && height > 0) return { width, height };
452
+ }
453
+ // WebP: RIFF....WEBP + VP8X / VP8 / VP8L (same layout as anthropic-image-guard).
454
+ if (
455
+ data.byteLength >= 30
456
+ && data[0] === 0x52 && data[1] === 0x49 && data[2] === 0x46 && data[3] === 0x46
457
+ && data[8] === 0x57 && data[9] === 0x45 && data[10] === 0x42 && data[11] === 0x50
458
+ ) {
459
+ const fourcc = String.fromCharCode(data[12]!, data[13]!, data[14]!, data[15]!);
460
+ if (fourcc === "VP8X") {
461
+ const width = 1 + (data[24]! | (data[25]! << 8) | (data[26]! << 16));
462
+ const height = 1 + (data[27]! | (data[28]! << 8) | (data[29]! << 16));
463
+ if (width > 0 && height > 0) return { width, height };
464
+ } else if (fourcc === "VP8 ") {
465
+ if (data[23] === 0x9d && data[24] === 0x01 && data[25] === 0x2a) {
466
+ const width = (data[26]! | (data[27]! << 8)) & 0x3fff;
467
+ const height = (data[28]! | (data[29]! << 8)) & 0x3fff;
468
+ if (width > 0 && height > 0) return { width, height };
469
+ }
470
+ } else if (fourcc === "VP8L" && data[20] === 0x2f) {
471
+ const raw = data[21]! | (data[22]! << 8) | (data[23]! << 16) | (data[24]! << 24);
472
+ const width = (raw & 0x3fff) + 1;
473
+ const height = ((raw >> 14) & 0x3fff) + 1;
474
+ if (width > 0 && height > 0) return { width, height };
475
+ }
476
+ }
477
+ // JPEG: scan for SOF0/SOF2 marker with dimensions
478
+ if (data.byteLength >= 4 && data[0] === 0xff && data[1] === 0xd8) {
479
+ let offset = 2;
480
+ while (offset + 8 < data.byteLength) {
481
+ if (data[offset] !== 0xff) break;
482
+ const marker = data[offset + 1]!;
483
+ // Standalone markers (TEM, RSTn, SOI, EOI) carry no length payload.
484
+ if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd9)) {
485
+ offset += 2;
486
+ continue;
487
+ }
488
+ const length = (data[offset + 2]! << 8) | data[offset + 3]!;
489
+ // SOFn frame headers share the dimension layout. 0xc4/0xc8/0xcc are DHT/JPG/DAC, not SOF.
490
+ if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
491
+ const height = (data[offset + 5]! << 8) | data[offset + 6]!;
492
+ const width = (data[offset + 7]! << 8) | data[offset + 8]!;
493
+ if (width > 0 && height > 0) return { width, height };
494
+ break;
495
+ }
496
+ if (length < 2) break;
497
+ offset += 2 + length;
498
+ }
499
+ }
500
+ return undefined;
501
+ }
502
+
503
+ /**
504
+ * Build SelectedImage messages for the AgentService vision path:
505
+ * store bytes in the local KV map under sha256(blobId), and encode
506
+ * `blobIdWithData` so the server can populate its cache without relying solely
507
+ * on getBlobArgs timing. Also set `path` like native/shunt clients.
508
+ */
509
+ export function buildSelectedImages(
510
+ images: readonly ResolvedCursorImage[],
511
+ requestScope?: CursorBlobRequestScopeToken,
512
+ ): SelectedImage[] {
513
+ return images.map(image => {
514
+ const blobId = storeCursorBlob(image.data, requestScope);
515
+ const dims = sniffCursorImageDimensions(image.data);
516
+ return create(SelectedImageSchema, {
517
+ uuid: image.uuid,
518
+ path: cursorImageAttachmentPath(image.uuid, image.mimeType),
519
+ mimeType: image.mimeType,
520
+ ...(dims
521
+ ? { dimension: create(SelectedImage_DimensionSchema, dims) }
522
+ : {}),
523
+ dataOrBlobId: {
524
+ case: "blobIdWithData",
525
+ value: create(SelectedImage_BlobIdWithDataSchema, {
526
+ blobId,
527
+ data: image.data,
528
+ }),
529
+ },
530
+ });
531
+ });
532
+ }
533
+
534
+ /**
535
+ * Always send `UserMessage.selected_context`, even when empty — matches cursor-agent.
536
+ * When images are present, they are blobIdWithData refs backed by the request-scoped KV store.
537
+ */
538
+ export function buildSelectedContext(
539
+ images: readonly ResolvedCursorImage[] = [],
540
+ requestScope?: CursorBlobRequestScopeToken,
541
+ ): SelectedContext {
542
+ return create(SelectedContextSchema, {
543
+ selectedImages: buildSelectedImages(images, requestScope),
544
+ });
545
+ }
546
+
547
+ /**
548
+ * Resolve data: images for the active user/developer turn onto SelectedImage.
549
+ * Tool-result image promotion is intentionally out of scope in this slice.
550
+ */
551
+ export async function resolveActiveCursorImages(
552
+ messages: readonly OcxMessage[] | undefined,
553
+ signal?: AbortSignal,
554
+ preparedImages?: readonly ResolvedCursorImage[],
555
+ ): Promise<ResolvedCursorImage[]> {
556
+ if (!messages?.length) return [];
557
+ // Same window the prepare pass rewrote; a divergent rule would attach unprepared bytes.
558
+ const message = messages[cursorVisionPrepareStartIndex(messages)];
559
+ if (!message || (message.role !== "user" && message.role !== "developer")) return [];
560
+ if (preparedImages) return [...preparedImages];
561
+ return resolveCursorImageParts(extractCursorImageParts(message.content), signal);
562
+ }
563
+
564
+ function imageDataUrlFromPrepared(image: ResolvedCursorImage): string {
565
+ return `data:${image.mimeType};base64,${Buffer.from(image.data).toString("base64")}`;
566
+ }
567
+
568
+ /**
569
+ * Re-encode a single image URL through {@link prepareCursorImageForWire}.
570
+ * data: URLs only. Omitted images become text (caller replaces the part).
571
+ */
572
+ export async function prepareCursorImageDataUrl(
573
+ imageUrl: string,
574
+ detail?: string,
575
+ signal?: AbortSignal,
576
+ ): Promise<
577
+ | { status: "ready"; imageUrl: string; image: ResolvedCursorImage }
578
+ | { status: "omitted"; reason: string }
579
+ > {
580
+ try {
581
+ const resolved = imageUrl.toLowerCase().startsWith("data:")
582
+ ? decodeCursorImageDataUrl(imageUrl)
583
+ : null;
584
+ if (!resolved) {
585
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
586
+ }
587
+ if (resolved.data.byteLength === 0) {
588
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
589
+ }
590
+ const outcome = await prepareCursorImageForWire({
591
+ data: resolved.data,
592
+ mimeType: resolved.mimeType,
593
+ uuid: randomUUID(),
594
+ ...(detail ? { detail } : {}),
595
+ }, signal);
596
+ if (outcome.status === "omitted") return outcome;
597
+ if (outcome.image.data.byteLength > MAX_CURSOR_IMAGE_BYTES) {
598
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
599
+ }
600
+ if (
601
+ imageUrl.toLowerCase().startsWith("data:")
602
+ && outcome.image.data === resolved.data
603
+ && outcome.image.mimeType === resolved.mimeType
604
+ ) {
605
+ return { status: "ready", imageUrl, image: outcome.image };
606
+ }
607
+ return { status: "ready", imageUrl: imageDataUrlFromPrepared(outcome.image), image: outcome.image };
608
+ } catch (err) {
609
+ if (signal?.aborted || (err instanceof Error && err.name === "AbortError")) throw err;
610
+ return { status: "omitted", reason: CURSOR_VISION_IMAGE_OMITTED };
611
+ }
612
+ }
613
+
614
+ async function prepareCursorContentParts(
615
+ content: string | readonly OcxContentPart[],
616
+ signal?: AbortSignal,
617
+ ): Promise<{ content: string | readonly OcxContentPart[]; images: ResolvedCursorImage[] }> {
618
+ if (typeof content === "string" || !Array.isArray(content)) {
619
+ return { content, images: [] };
620
+ }
621
+ let changed = false;
622
+ const next: OcxContentPart[] = [];
623
+ const images: ResolvedCursorImage[] = [];
624
+ for (const part of content) {
625
+ if (part.type === "image" && typeof part.imageUrl === "string" && part.imageUrl.length > 0) {
626
+ throwIfImagePhaseAborted(signal);
627
+ const prepared = await prepareCursorImageDataUrl(part.imageUrl, part.detail, signal);
628
+ if (prepared.status === "omitted") {
629
+ changed = true;
630
+ next.push({ type: "text", text: prepared.reason });
631
+ continue;
632
+ }
633
+ images.push(prepared.image);
634
+ if (prepared.imageUrl !== part.imageUrl) changed = true;
635
+ next.push({ ...part, imageUrl: prepared.imageUrl });
636
+ } else {
637
+ next.push(part);
638
+ }
639
+ }
640
+ return { content: changed ? next : content, images };
641
+ }
642
+
643
+ /**
644
+ * First original-message index that still needs image prep for the active vision window.
645
+ * Historical messages before this index are left untouched (no decode).
646
+ */
647
+ export function cursorVisionPrepareStartIndex(messages: readonly OcxMessage[]): number {
648
+ // Tool-result image preparation is out of scope in this slice.
649
+ if (messages.at(-1)?.role === "toolResult") return messages.length;
650
+ for (let i = messages.length - 1; i >= 0; i--) {
651
+ const role = messages[i]?.role;
652
+ if (role === "user" || role === "developer") return i;
653
+ }
654
+ return messages.length;
655
+ }
656
+
657
+ /**
658
+ * Rewrite image data URLs in the active vision window (last user/developer turn) through
659
+ * the JPEG soft-cap path before protobuf encode. Historical messages are left by
660
+ * reference. Undecodable images become {@link CURSOR_VISION_IMAGE_OMITTED} text so
661
+ * image-only turns stay userMessageAction.
662
+ */
663
+ export interface PreparedCursorRawMessages {
664
+ messages: readonly OcxMessage[] | undefined;
665
+ images: ResolvedCursorImage[];
666
+ }
667
+
668
+ export async function prepareCursorRawMessages(
669
+ messages: readonly OcxMessage[] | undefined,
670
+ signal?: AbortSignal,
671
+ ): Promise<PreparedCursorRawMessages> {
672
+ if (!messages?.length) return { messages, images: [] };
673
+ throwIfImagePhaseAborted(signal);
674
+ const prepareFrom = cursorVisionPrepareStartIndex(messages);
675
+ const active = messages[prepareFrom];
676
+ if (
677
+ active
678
+ && (active.role === "user" || active.role === "developer")
679
+ && extractCursorImageParts(active.content).length > MAX_CURSOR_IMAGES
680
+ ) {
681
+ throw new CursorImageError(`Too many images in one request (max ${MAX_CURSOR_IMAGES}).`);
682
+ }
683
+ let changed = false;
684
+ const out: OcxMessage[] = [];
685
+ const images: ResolvedCursorImage[] = [];
686
+ for (let i = 0; i < messages.length; i++) {
687
+ throwIfImagePhaseAborted(signal);
688
+ const message = messages[i]!;
689
+ if (
690
+ i >= prepareFrom
691
+ && (message.role === "user" || message.role === "developer")
692
+ ) {
693
+ const prepared = await prepareCursorContentParts(message.content, signal);
694
+ images.push(...prepared.images);
695
+ if (prepared.content !== message.content) {
696
+ changed = true;
697
+ out.push({ ...message, content: prepared.content } as OcxMessage);
698
+ continue;
699
+ }
700
+ }
701
+ out.push(message);
702
+ }
703
+ return { messages: changed ? out : messages, images };
704
+ }