@iislee/opencodex 2.11.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 (476) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +225 -0
  3. package/assets/architecture.png +0 -0
  4. package/assets/banner.png +0 -0
  5. package/assets/claude-code-models.gif +0 -0
  6. package/assets/codex-app-picker.png +0 -0
  7. package/bin/ocx.mjs +451 -0
  8. package/bin/package-main.mjs +9 -0
  9. package/gui/dist/assets/index-DTpMHS4F.js +67 -0
  10. package/gui/dist/assets/index-ZNVDE3C7.css +1 -0
  11. package/gui/dist/favicon.png +0 -0
  12. package/gui/dist/icons.svg +24 -0
  13. package/gui/dist/index.html +25 -0
  14. package/gui/dist/logo.png +0 -0
  15. package/gui/dist/provider-icons/alibaba-color.svg +1 -0
  16. package/gui/dist/provider-icons/antigravity-color.svg +1 -0
  17. package/gui/dist/provider-icons/antigravity.svg +1 -0
  18. package/gui/dist/provider-icons/claude-color.svg +1 -0
  19. package/gui/dist/provider-icons/claude.svg +1 -0
  20. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
  21. package/gui/dist/provider-icons/copilot-color.svg +1 -0
  22. package/gui/dist/provider-icons/copilot.svg +1 -0
  23. package/gui/dist/provider-icons/cursor-color.svg +2 -0
  24. package/gui/dist/provider-icons/cursor.svg +2 -0
  25. package/gui/dist/provider-icons/deepseek-color.svg +1 -0
  26. package/gui/dist/provider-icons/discord.svg +1 -0
  27. package/gui/dist/provider-icons/firepass-color.svg +1 -0
  28. package/gui/dist/provider-icons/fireworks-color.svg +1 -0
  29. package/gui/dist/provider-icons/gemini-color.svg +1 -0
  30. package/gui/dist/provider-icons/gemini.svg +1 -0
  31. package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
  32. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
  33. package/gui/dist/provider-icons/grok-color.svg +1 -0
  34. package/gui/dist/provider-icons/grok.svg +1 -0
  35. package/gui/dist/provider-icons/groq-color.svg +1 -0
  36. package/gui/dist/provider-icons/huggingface-color.svg +1 -0
  37. package/gui/dist/provider-icons/kimi-color.svg +1 -0
  38. package/gui/dist/provider-icons/kiro-color.svg +15 -0
  39. package/gui/dist/provider-icons/kiro.svg +14 -0
  40. package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
  41. package/gui/dist/provider-icons/mistral-color.svg +1 -0
  42. package/gui/dist/provider-icons/moonshot-color.svg +1 -0
  43. package/gui/dist/provider-icons/nvidia-color.svg +1 -0
  44. package/gui/dist/provider-icons/ollama-color.svg +1 -0
  45. package/gui/dist/provider-icons/openai.svg +1 -0
  46. package/gui/dist/provider-icons/opencode.svg +1 -0
  47. package/gui/dist/provider-icons/openrouter-color.svg +1 -0
  48. package/gui/dist/provider-icons/pi.svg +21 -0
  49. package/gui/dist/provider-icons/qianfan-color.svg +1 -0
  50. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
  51. package/gui/dist/provider-icons/telegram.svg +1 -0
  52. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
  53. package/gui/dist/provider-icons/vllm-color.svg +1 -0
  54. package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
  55. package/package.json +102 -0
  56. package/src/AGENTS.md +28 -0
  57. package/src/adapters/anthropic-image-guard.ts +251 -0
  58. package/src/adapters/anthropic-image-normalize.ts +518 -0
  59. package/src/adapters/anthropic.ts +1003 -0
  60. package/src/adapters/azure.ts +36 -0
  61. package/src/adapters/base.ts +72 -0
  62. package/src/adapters/client-fingerprint.ts +59 -0
  63. package/src/adapters/cursor/arg-codec.ts +38 -0
  64. package/src/adapters/cursor/arg-normalize.ts +104 -0
  65. package/src/adapters/cursor/cursor-errors.ts +165 -0
  66. package/src/adapters/cursor/discovery.ts +276 -0
  67. package/src/adapters/cursor/effort-map.ts +127 -0
  68. package/src/adapters/cursor/exec-policy.ts +88 -0
  69. package/src/adapters/cursor/framing.ts +211 -0
  70. package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
  71. package/src/adapters/cursor/kv-store.ts +52 -0
  72. package/src/adapters/cursor/live-models.ts +153 -0
  73. package/src/adapters/cursor/live-smoke-gate.ts +41 -0
  74. package/src/adapters/cursor/live-transport.ts +1214 -0
  75. package/src/adapters/cursor/mcp-config.ts +42 -0
  76. package/src/adapters/cursor/mcp-manager.ts +333 -0
  77. package/src/adapters/cursor/message-mapper.ts +49 -0
  78. package/src/adapters/cursor/native-exec-common.ts +55 -0
  79. package/src/adapters/cursor/native-exec-desktop.ts +184 -0
  80. package/src/adapters/cursor/native-exec-fs.ts +329 -0
  81. package/src/adapters/cursor/native-exec-mcp.ts +153 -0
  82. package/src/adapters/cursor/native-exec-network.ts +43 -0
  83. package/src/adapters/cursor/native-exec-shell.ts +548 -0
  84. package/src/adapters/cursor/native-exec-tools.ts +118 -0
  85. package/src/adapters/cursor/native-exec.ts +576 -0
  86. package/src/adapters/cursor/protobuf-events.ts +563 -0
  87. package/src/adapters/cursor/protobuf-request.ts +714 -0
  88. package/src/adapters/cursor/request-builder.ts +255 -0
  89. package/src/adapters/cursor/thread-continuity.ts +67 -0
  90. package/src/adapters/cursor/tool-definitions.ts +505 -0
  91. package/src/adapters/cursor/transport-retry.ts +132 -0
  92. package/src/adapters/cursor/transport.ts +57 -0
  93. package/src/adapters/cursor/types.ts +52 -0
  94. package/src/adapters/cursor.ts +196 -0
  95. package/src/adapters/google-antigravity-replay.ts +303 -0
  96. package/src/adapters/google-antigravity-wire.ts +108 -0
  97. package/src/adapters/google-errors.ts +85 -0
  98. package/src/adapters/google-http.ts +100 -0
  99. package/src/adapters/google-tool-schema.ts +173 -0
  100. package/src/adapters/google-truncation.ts +13 -0
  101. package/src/adapters/google-wire-compiler.ts +232 -0
  102. package/src/adapters/google.ts +758 -0
  103. package/src/adapters/identity.ts +44 -0
  104. package/src/adapters/image.ts +23 -0
  105. package/src/adapters/kiro-constants.ts +16 -0
  106. package/src/adapters/kiro-errors.ts +197 -0
  107. package/src/adapters/kiro-events.ts +179 -0
  108. package/src/adapters/kiro-images.ts +129 -0
  109. package/src/adapters/kiro-retry.ts +312 -0
  110. package/src/adapters/kiro-thinking.ts +96 -0
  111. package/src/adapters/kiro-tool-fallback.ts +36 -0
  112. package/src/adapters/kiro-tools.ts +215 -0
  113. package/src/adapters/kiro-truncation.ts +33 -0
  114. package/src/adapters/kiro-wire.ts +129 -0
  115. package/src/adapters/kiro.ts +1898 -0
  116. package/src/adapters/mimo-free.ts +263 -0
  117. package/src/adapters/openai-chat.ts +1005 -0
  118. package/src/adapters/openai-responses.ts +1137 -0
  119. package/src/adapters/run-turn-queue.ts +114 -0
  120. package/src/adapters/tool-catalog-nudge.ts +71 -0
  121. package/src/adapters/upstream-http-error.ts +48 -0
  122. package/src/bridge.ts +1619 -0
  123. package/src/chat/inbound.ts +295 -0
  124. package/src/chat/outbound.ts +765 -0
  125. package/src/claude/agents-inject.ts +243 -0
  126. package/src/claude/alias.ts +149 -0
  127. package/src/claude/auth-detect.ts +229 -0
  128. package/src/claude/auth-mode-migration.ts +32 -0
  129. package/src/claude/auth-mode.ts +62 -0
  130. package/src/claude/context-windows.ts +189 -0
  131. package/src/claude/desktop-3p-guard.ts +35 -0
  132. package/src/claude/desktop-3p-paths.ts +84 -0
  133. package/src/claude/desktop-3p.ts +381 -0
  134. package/src/claude/desktop-health.ts +26 -0
  135. package/src/claude/desktop-profile.ts +263 -0
  136. package/src/claude/gateway-cache.ts +70 -0
  137. package/src/claude/inbound-debug.ts +163 -0
  138. package/src/claude/inbound.ts +509 -0
  139. package/src/claude/model-info.ts +151 -0
  140. package/src/claude/outbound.ts +872 -0
  141. package/src/cli/access.ts +108 -0
  142. package/src/cli/account-api.ts +268 -0
  143. package/src/cli/account-auth.ts +223 -0
  144. package/src/cli/account-extended.ts +350 -0
  145. package/src/cli/account.ts +275 -0
  146. package/src/cli/agent-driven.ts +70 -0
  147. package/src/cli/agent.ts +184 -0
  148. package/src/cli/catalog-prewarm.ts +27 -0
  149. package/src/cli/claude-desktop.ts +188 -0
  150. package/src/cli/claude.ts +286 -0
  151. package/src/cli/codex-shim-autorestore.ts +45 -0
  152. package/src/cli/combo.ts +119 -0
  153. package/src/cli/config-command.ts +145 -0
  154. package/src/cli/debug.ts +228 -0
  155. package/src/cli/doctor.ts +930 -0
  156. package/src/cli/export-command.ts +187 -0
  157. package/src/cli/help.ts +354 -0
  158. package/src/cli/index.ts +1113 -0
  159. package/src/cli/init.ts +224 -0
  160. package/src/cli/integrations.ts +142 -0
  161. package/src/cli/interactive-confirm.ts +133 -0
  162. package/src/cli/internal-dispatch.ts +20 -0
  163. package/src/cli/models-runtime.ts +212 -0
  164. package/src/cli/models.ts +336 -0
  165. package/src/cli/observe.ts +117 -0
  166. package/src/cli/opencode.ts +586 -0
  167. package/src/cli/pi.ts +188 -0
  168. package/src/cli/provider-runtime.ts +162 -0
  169. package/src/cli/provider.ts +463 -0
  170. package/src/cli/runtime-api.ts +325 -0
  171. package/src/cli/star-prompt.ts +155 -0
  172. package/src/cli/status-oauth.ts +78 -0
  173. package/src/cli/status.ts +321 -0
  174. package/src/cli/sync-cloud.ts +283 -0
  175. package/src/cli/system-command.ts +112 -0
  176. package/src/cli/tray-proxy.ts +52 -0
  177. package/src/cli/v2.ts +173 -0
  178. package/src/cli.ts +10 -0
  179. package/src/clients/config-export.ts +377 -0
  180. package/src/clients/effective-status.ts +385 -0
  181. package/src/clients/probes/agy.ts +55 -0
  182. package/src/clients/probes/cc-switch.ts +110 -0
  183. package/src/clients/probes/claude.ts +90 -0
  184. package/src/clients/probes/codex.ts +125 -0
  185. package/src/clients/probes/grok.ts +29 -0
  186. package/src/clients/probes/opencode.ts +109 -0
  187. package/src/clients/probes/paseo.ts +55 -0
  188. package/src/clients/probes/pi.ts +55 -0
  189. package/src/cloud/onedrive-auth.ts +666 -0
  190. package/src/cloud/onedrive-graph.ts +108 -0
  191. package/src/cloud/settings.ts +75 -0
  192. package/src/cloud/sync.ts +212 -0
  193. package/src/cloud/types.ts +56 -0
  194. package/src/cloud/vault.ts +89 -0
  195. package/src/codex/account-id.ts +34 -0
  196. package/src/codex/account-label.ts +34 -0
  197. package/src/codex/account-lifecycle.ts +55 -0
  198. package/src/codex/account-namespace-match.ts +63 -0
  199. package/src/codex/account-namespaces.ts +149 -0
  200. package/src/codex/account-pause.ts +20 -0
  201. package/src/codex/account-runtime-state.ts +31 -0
  202. package/src/codex/account-store.ts +517 -0
  203. package/src/codex/account-usability.ts +20 -0
  204. package/src/codex/app-server-processes.ts +756 -0
  205. package/src/codex/auth-api.ts +1540 -0
  206. package/src/codex/auth-collision.ts +107 -0
  207. package/src/codex/auth-context.ts +352 -0
  208. package/src/codex/autostart-health.ts +149 -0
  209. package/src/codex/catalog/aggregation.ts +378 -0
  210. package/src/codex/catalog/bundled.ts +251 -0
  211. package/src/codex/catalog/effort.ts +355 -0
  212. package/src/codex/catalog/metadata.ts +180 -0
  213. package/src/codex/catalog/parsing.ts +456 -0
  214. package/src/codex/catalog/provider-fetch.ts +902 -0
  215. package/src/codex/catalog/sync.ts +620 -0
  216. package/src/codex/catalog.ts +12 -0
  217. package/src/codex/data/upstream-models.json +830 -0
  218. package/src/codex/exec-invocation.ts +22 -0
  219. package/src/codex/features.ts +969 -0
  220. package/src/codex/history-migration-guardian.ts +102 -0
  221. package/src/codex/history-provider.ts +776 -0
  222. package/src/codex/home.ts +206 -0
  223. package/src/codex/inject.ts +799 -0
  224. package/src/codex/injected-marker.ts +72 -0
  225. package/src/codex/journal.ts +163 -0
  226. package/src/codex/main-account-cache.ts +32 -0
  227. package/src/codex/main-account.ts +40 -0
  228. package/src/codex/model-cache.ts +227 -0
  229. package/src/codex/paths.ts +65 -0
  230. package/src/codex/plugins-doctor.ts +242 -0
  231. package/src/codex/pool-rotation.ts +225 -0
  232. package/src/codex/project-config-warnings.ts +411 -0
  233. package/src/codex/quota.ts +411 -0
  234. package/src/codex/refresh.ts +53 -0
  235. package/src/codex/routing.ts +1477 -0
  236. package/src/codex/runtime.ts +538 -0
  237. package/src/codex/shim.ts +1189 -0
  238. package/src/codex/subagent-defaults.ts +550 -0
  239. package/src/codex/subagent-model-fallback.ts +469 -0
  240. package/src/codex/sync.ts +130 -0
  241. package/src/codex/warmup.ts +192 -0
  242. package/src/codex/websocket-registry.ts +100 -0
  243. package/src/combos/failover.ts +140 -0
  244. package/src/combos/index.ts +41 -0
  245. package/src/combos/request.ts +62 -0
  246. package/src/combos/resolve.ts +232 -0
  247. package/src/combos/types.ts +326 -0
  248. package/src/config.ts +2356 -0
  249. package/src/generated/jawcode-model-metadata.ts +104 -0
  250. package/src/github/star-state.ts +203 -0
  251. package/src/grok/inject.ts +545 -0
  252. package/src/grok/status.ts +121 -0
  253. package/src/grok/sync.ts +103 -0
  254. package/src/grok/usage-hook/report.mjs +348 -0
  255. package/src/grok/usage-hook.ts +278 -0
  256. package/src/images/artifacts.ts +516 -0
  257. package/src/images/fulfill-video.ts +163 -0
  258. package/src/images/fulfill.ts +149 -0
  259. package/src/images/index.ts +4 -0
  260. package/src/images/loop.ts +829 -0
  261. package/src/images/plan.ts +133 -0
  262. package/src/images/synthetic-tool.ts +133 -0
  263. package/src/images/types.ts +41 -0
  264. package/src/images/xai-client.ts +141 -0
  265. package/src/images/xai-video-client.ts +163 -0
  266. package/src/index.ts +22 -0
  267. package/src/lib/abort.ts +146 -0
  268. package/src/lib/admin-secrets.ts +25 -0
  269. package/src/lib/admission.ts +83 -0
  270. package/src/lib/app-owned-memory-stores.ts +173 -0
  271. package/src/lib/app-owned-memory.ts +265 -0
  272. package/src/lib/bounded-body.ts +202 -0
  273. package/src/lib/bun-binary-validator.d.mts +3 -0
  274. package/src/lib/bun-binary-validator.mjs +18 -0
  275. package/src/lib/bun-runtime.ts +71 -0
  276. package/src/lib/bun-stream-caps.ts +126 -0
  277. package/src/lib/config-ownership.ts +360 -0
  278. package/src/lib/crash-guard.ts +344 -0
  279. package/src/lib/debug-log-buffer.ts +83 -0
  280. package/src/lib/debug-settings.ts +108 -0
  281. package/src/lib/debug.ts +31 -0
  282. package/src/lib/destination-policy.ts +316 -0
  283. package/src/lib/errors.ts +364 -0
  284. package/src/lib/eventstream-decoder.ts +253 -0
  285. package/src/lib/gcp-adc.ts +341 -0
  286. package/src/lib/injection-debug-log.ts +58 -0
  287. package/src/lib/open-url.ts +25 -0
  288. package/src/lib/pinned-http.ts +151 -0
  289. package/src/lib/privacy.ts +20 -0
  290. package/src/lib/process-control.ts +165 -0
  291. package/src/lib/provider-outbound.ts +170 -0
  292. package/src/lib/provider-url.ts +14 -0
  293. package/src/lib/proxy-env.ts +18 -0
  294. package/src/lib/redact.ts +105 -0
  295. package/src/lib/retry-after.ts +55 -0
  296. package/src/lib/service-secrets.ts +25 -0
  297. package/src/lib/shadow-call.ts +30 -0
  298. package/src/lib/sidecar-tracker.ts +52 -0
  299. package/src/lib/sse-decoder.ts +323 -0
  300. package/src/lib/state-store-registrations.ts +109 -0
  301. package/src/lib/state-store-sweeper.ts +184 -0
  302. package/src/lib/test-home-guard.ts +90 -0
  303. package/src/lib/token-estimate.ts +69 -0
  304. package/src/lib/translator-budget.ts +356 -0
  305. package/src/lib/upstream-retry.ts +239 -0
  306. package/src/lib/win-exec.ts +115 -0
  307. package/src/lib/win-paths.ts +68 -0
  308. package/src/lib/windows-elevation.ts +705 -0
  309. package/src/lib/windows-secret-acl.ts +514 -0
  310. package/src/lib/winsw.ts +375 -0
  311. package/src/oauth/anthropic-routing.ts +594 -0
  312. package/src/oauth/anthropic.ts +177 -0
  313. package/src/oauth/callback-server.ts +294 -0
  314. package/src/oauth/chatgpt.ts +150 -0
  315. package/src/oauth/cursor.ts +211 -0
  316. package/src/oauth/github-copilot.ts +428 -0
  317. package/src/oauth/google-antigravity.ts +230 -0
  318. package/src/oauth/health.ts +399 -0
  319. package/src/oauth/index.ts +1174 -0
  320. package/src/oauth/key-providers.ts +108 -0
  321. package/src/oauth/kimi.ts +213 -0
  322. package/src/oauth/kiro-credentials.ts +726 -0
  323. package/src/oauth/kiro.ts +577 -0
  324. package/src/oauth/local-token-detect.ts +121 -0
  325. package/src/oauth/log.ts +48 -0
  326. package/src/oauth/login-cli.ts +163 -0
  327. package/src/oauth/pkce.ts +15 -0
  328. package/src/oauth/store.ts +630 -0
  329. package/src/oauth/token-guardian.ts +303 -0
  330. package/src/oauth/types.ts +62 -0
  331. package/src/oauth/xai.ts +241 -0
  332. package/src/pi/extensions.ts +72 -0
  333. package/src/pi/home.ts +42 -0
  334. package/src/pi/index.ts +40 -0
  335. package/src/pi/models.ts +278 -0
  336. package/src/pi/packages.ts +219 -0
  337. package/src/pi/settings.ts +365 -0
  338. package/src/pi/status.ts +68 -0
  339. package/src/pi/sync.ts +75 -0
  340. package/src/providers/alibaba-region-backup.ts +75 -0
  341. package/src/providers/alibaba-region-migration.ts +156 -0
  342. package/src/providers/alibaba-region-startup.ts +36 -0
  343. package/src/providers/antigravity-models.ts +205 -0
  344. package/src/providers/api-keys.ts +140 -0
  345. package/src/providers/base-url-choices.ts +64 -0
  346. package/src/providers/context-cap.ts +65 -0
  347. package/src/providers/derive.ts +339 -0
  348. package/src/providers/free-directory.ts +184 -0
  349. package/src/providers/github-copilot-transport.ts +56 -0
  350. package/src/providers/key-failover.ts +203 -0
  351. package/src/providers/kiro-models.ts +67 -0
  352. package/src/providers/label.ts +19 -0
  353. package/src/providers/model-discovery.ts +356 -0
  354. package/src/providers/openai-sidecar.ts +175 -0
  355. package/src/providers/openai-tier-startup.ts +27 -0
  356. package/src/providers/openai-tiers.ts +301 -0
  357. package/src/providers/openai-virtual-models.ts +82 -0
  358. package/src/providers/openrouter-routing.ts +102 -0
  359. package/src/providers/provider-id-rewrite.ts +150 -0
  360. package/src/providers/quota.ts +1260 -0
  361. package/src/providers/registry.ts +1600 -0
  362. package/src/providers/slug-codec.ts +67 -0
  363. package/src/providers/xai-transport.ts +141 -0
  364. package/src/reasoning-effort.ts +135 -0
  365. package/src/responses/compaction.ts +117 -0
  366. package/src/responses/parser.ts +656 -0
  367. package/src/responses/reasoning-envelope.ts +52 -0
  368. package/src/responses/schema.ts +159 -0
  369. package/src/responses/spill-store.ts +394 -0
  370. package/src/responses/state.ts +895 -0
  371. package/src/responses/tool-groups.ts +19 -0
  372. package/src/router.ts +425 -0
  373. package/src/server/adapter-resolve.ts +80 -0
  374. package/src/server/auth-cors.ts +530 -0
  375. package/src/server/chat-completions.ts +368 -0
  376. package/src/server/claude-messages.ts +914 -0
  377. package/src/server/effort-policy.ts +172 -0
  378. package/src/server/gui-static.ts +123 -0
  379. package/src/server/image-retry.ts +42 -0
  380. package/src/server/images.ts +476 -0
  381. package/src/server/index.ts +1126 -0
  382. package/src/server/lifecycle.ts +227 -0
  383. package/src/server/live.ts +598 -0
  384. package/src/server/management/agent-settings-routes.ts +1169 -0
  385. package/src/server/management/api-access.ts +141 -0
  386. package/src/server/management/api-key-usage.ts +167 -0
  387. package/src/server/management/body.ts +35 -0
  388. package/src/server/management/clients-routes.ts +63 -0
  389. package/src/server/management/cloud-sync-routes.ts +266 -0
  390. package/src/server/management/combo-routes.ts +220 -0
  391. package/src/server/management/config-routes.ts +422 -0
  392. package/src/server/management/context.ts +31 -0
  393. package/src/server/management/logs-usage-routes.ts +707 -0
  394. package/src/server/management/model-routes.ts +525 -0
  395. package/src/server/management/oauth-account-routes.ts +563 -0
  396. package/src/server/management/provider-routes.ts +556 -0
  397. package/src/server/management/shared.ts +277 -0
  398. package/src/server/management/sidebar-routes.ts +90 -0
  399. package/src/server/management/system-restart.ts +179 -0
  400. package/src/server/management/system-routes.ts +117 -0
  401. package/src/server/management/usage-summary-cache.ts +86 -0
  402. package/src/server/management-api.ts +215 -0
  403. package/src/server/management-auth.ts +267 -0
  404. package/src/server/memory-watchdog.ts +156 -0
  405. package/src/server/port-reclaim.ts +307 -0
  406. package/src/server/ports.ts +116 -0
  407. package/src/server/proxy-liveness.ts +201 -0
  408. package/src/server/relay-eager.ts +313 -0
  409. package/src/server/relay.ts +1049 -0
  410. package/src/server/request-decompress.ts +132 -0
  411. package/src/server/request-log-conversation.ts +168 -0
  412. package/src/server/request-log.ts +1046 -0
  413. package/src/server/responses/collaboration.ts +354 -0
  414. package/src/server/responses/compact.ts +384 -0
  415. package/src/server/responses/core.ts +2758 -0
  416. package/src/server/responses/encrypted-payload.ts +308 -0
  417. package/src/server/responses/fetch-helpers.ts +157 -0
  418. package/src/server/responses/passthrough-error.ts +78 -0
  419. package/src/server/responses/terminal-guard.ts +230 -0
  420. package/src/server/responses/upstream-error.ts +48 -0
  421. package/src/server/responses-image-gen-repair.ts +132 -0
  422. package/src/server/responses-item-id-repair.ts +224 -0
  423. package/src/server/responses.ts +9 -0
  424. package/src/server/search.ts +136 -0
  425. package/src/server/sse-payload-rewrite.ts +175 -0
  426. package/src/server/startup-action-control.ts +308 -0
  427. package/src/server/startup-health-cache.ts +113 -0
  428. package/src/server/system-env.ts +413 -0
  429. package/src/server/windows-tcp-drop.ts +184 -0
  430. package/src/server/windows-tray-control.ts +41 -0
  431. package/src/server/ws-bridge.ts +471 -0
  432. package/src/service.ts +2554 -0
  433. package/src/stall-timeout.ts +20 -0
  434. package/src/storage/cleanup-job.ts +57 -0
  435. package/src/storage/cleanup.ts +3085 -0
  436. package/src/storage/policy-job.ts +457 -0
  437. package/src/storage/policy-scheduler.ts +40 -0
  438. package/src/storage/policy-worker.ts +59 -0
  439. package/src/storage/policy.ts +527 -0
  440. package/src/storage/restore-job.ts +299 -0
  441. package/src/storage/restore-worker.ts +58 -0
  442. package/src/storage/scanner.ts +238 -0
  443. package/src/storage/storage-mutation-coordinator.ts +139 -0
  444. package/src/storage/worker-lifecycle.ts +215 -0
  445. package/src/tray/assets/opencodex-tray-offline.ico +0 -0
  446. package/src/tray/assets/opencodex-tray-online.ico +0 -0
  447. package/src/tray/assets/opencodex-tray-warning.ico +0 -0
  448. package/src/tray/assets/opencodex-tray.png +0 -0
  449. package/src/tray/windows-tray.ps1 +290 -0
  450. package/src/tray/windows.ts +730 -0
  451. package/src/types.ts +1237 -0
  452. package/src/update/badge.ts +72 -0
  453. package/src/update/index.ts +407 -0
  454. package/src/update/job.ts +1520 -0
  455. package/src/update/notify.ts +257 -0
  456. package/src/update/npm-invocation.d.mts +23 -0
  457. package/src/update/npm-invocation.mjs +94 -0
  458. package/src/update/tray-update-plan.d.mts +18 -0
  459. package/src/update/tray-update-plan.mjs +38 -0
  460. package/src/usage/cost.ts +0 -0
  461. package/src/usage/debug.ts +97 -0
  462. package/src/usage/expected-prices.ts +164 -0
  463. package/src/usage/log.ts +658 -0
  464. package/src/usage/summary.ts +585 -0
  465. package/src/usage/totals.ts +14 -0
  466. package/src/vision/anthropic-describe.ts +185 -0
  467. package/src/vision/describe.ts +125 -0
  468. package/src/vision/index.ts +467 -0
  469. package/src/web-search/anthropic-executor.ts +189 -0
  470. package/src/web-search/executor.ts +105 -0
  471. package/src/web-search/format-result.ts +89 -0
  472. package/src/web-search/index.ts +196 -0
  473. package/src/web-search/loop.ts +664 -0
  474. package/src/web-search/parse.ts +220 -0
  475. package/src/web-search/progress-stream.ts +342 -0
  476. package/src/web-search/synthetic-tool.ts +47 -0
@@ -0,0 +1,576 @@
1
+ import { createHash } from "node:crypto";
2
+ import { enforceAppOwnedMemoryBudget } from "../../lib/app-owned-memory";
3
+ import { create } from "@bufbuild/protobuf";
4
+ import {
5
+ DiagnosticsErrorSchema,
6
+ DiagnosticsResultSchema,
7
+ ErrorSchema,
8
+ GetBlobResultSchema,
9
+ KvClientMessageSchema,
10
+ McpErrorSchema,
11
+ McpResultSchema,
12
+ RequestContextResultSchema,
13
+ RequestContextSchema,
14
+ RequestContextSuccessSchema,
15
+ SetBlobResultSchema,
16
+ type ExecServerMessage,
17
+ type KvServerMessage,
18
+ } from "./gen/agent_pb";
19
+ import {
20
+ deleteExec,
21
+ grepExec,
22
+ lsExec,
23
+ readExec,
24
+ rejectDeleteExecForApplyPatch,
25
+ rejectDeleteExecForPolicy,
26
+ rejectGrepExecForPolicy,
27
+ rejectLsExecForPolicy,
28
+ rejectReadExecForPolicy,
29
+ rejectWriteExecForApplyPatch,
30
+ rejectWriteExecForPolicy,
31
+ writeExec,
32
+ } from "./native-exec-fs";
33
+ import { debugProviderDiagnostic } from "../../lib/debug";
34
+ import { fetchExec, rejectFetchExecForPolicy, type CursorNativeNetworkDeps } from "./native-exec-network";
35
+ import {
36
+ backgroundShellSpawnExec,
37
+ rejectBackgroundShellSpawnExecForPolicy,
38
+ rejectShellExecForPolicy,
39
+ rejectShellStreamExecForPolicy,
40
+ rejectWriteShellStdinExecForPolicy,
41
+ shellExec,
42
+ shellStreamExec,
43
+ writeShellStdinExec,
44
+ } from "./native-exec-shell";
45
+ import {
46
+ computerUseExec,
47
+ listMcpResourcesExec,
48
+ mcpExec,
49
+ readMcpResourceExec,
50
+ recordScreenExec,
51
+ type CursorNativeToolDeps,
52
+ } from "./native-exec-tools";
53
+ import { clientBytes, execBytes } from "./native-exec-common";
54
+ import type { McpToolDefinition } from "./gen/agent_pb";
55
+ import { OCX_RESPONSES_TOOL_PROVIDER } from "./tool-definitions";
56
+
57
+ export type CursorNativeExecDeps = CursorNativeNetworkDeps & CursorNativeToolDeps;
58
+
59
+ /**
60
+ * Execution context for a Cursor stream: the per-call executors plus the MCP tool definitions
61
+ * advertised to the server via `requestContextResult`. Without `mcpToolDefs`, the server is
62
+ * never told any MCP tools exist, so it never sends `mcpArgs`.
63
+ */
64
+ export interface CursorNativeExecContext extends CursorNativeExecDeps {
65
+ /** Stable owner for background shells created by this transport session. */
66
+ sessionId?: string;
67
+ mcpToolDefs?: McpToolDefinition[];
68
+ clientToolDefs?: McpToolDefinition[];
69
+ /** Unsafe opt-in escape hatch for Cursor server-driven local fs/shell/fetch execution. */
70
+ unsafeAllowNativeLocalExec?: boolean;
71
+ /** apply_patch is visible for this request; Cursor-native write/delete must not bypass Codex. */
72
+ rejectNativeFileMutations?: boolean;
73
+ }
74
+
75
+ export function cursorUnsafeNativeLocalExecEnabled(input: Pick<CursorNativeExecContext, "unsafeAllowNativeLocalExec"> = {}): boolean {
76
+ return input.unsafeAllowNativeLocalExec === true;
77
+ }
78
+
79
+ /**
80
+ * Content-addressed blob store shared across streams. Bounded: without eviction a long-running
81
+ * proxy accumulates every conversation's prompt blobs forever (unbounded memory) and any stale
82
+ * blob stays servable indefinitely — a cross-conversation contamination enabler if Cursor's
83
+ * server-side state ever references old ids (devlog 260702 P0). Continuation requests re-store
84
+ * their blobs on every turn (`rootPromptMessages` → `storeCursorBlob`), so TTL + cap eviction is
85
+ * safe for live sessions: only genuinely abandoned entries age out.
86
+ */
87
+ const BLOB_TTL_MS = 15 * 60_000;
88
+ const BLOB_MAX_ENTRIES = 4_096;
89
+ const BLOB_MAX_ENTRY_BYTES = 16 * 1024 * 1024;
90
+ export const CURSOR_BLOB_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
91
+
92
+ type CursorBlobProvenance = "local-regenerated" | "remote-setBlobArgs";
93
+ export type CursorBlobRequestScopeToken = symbol;
94
+
95
+ interface CursorBlobEntry {
96
+ data: Uint8Array;
97
+ storedAt: number;
98
+ sizeBytes: number;
99
+ provenance: CursorBlobProvenance;
100
+ requestPins: Set<CursorBlobRequestScopeToken>;
101
+ }
102
+
103
+ type CursorBlobRejectionReason = "entry_too_large" | "pinned_saturation" | "request_pinned_conflict";
104
+ type CursorBlobAdmission =
105
+ | { admitted: true; replaced: boolean }
106
+ | { admitted: false; reason: CursorBlobRejectionReason };
107
+
108
+ interface CursorBlobLimits {
109
+ ttlMs: number;
110
+ maxEntries: number;
111
+ maxEntryBytes: number;
112
+ maxTotalBytes: number;
113
+ }
114
+
115
+ interface CursorBlobRequestScopeState {
116
+ keys: Set<string>;
117
+ sealed: boolean;
118
+ }
119
+
120
+ const DEFAULT_BLOB_LIMITS: CursorBlobLimits = {
121
+ ttlMs: BLOB_TTL_MS,
122
+ maxEntries: BLOB_MAX_ENTRIES,
123
+ maxEntryBytes: BLOB_MAX_ENTRY_BYTES,
124
+ maxTotalBytes: CURSOR_BLOB_MAX_TOTAL_BYTES,
125
+ };
126
+
127
+ const blobs = new Map<string, CursorBlobEntry>();
128
+ const blobRequestScopes = new Map<CursorBlobRequestScopeToken, CursorBlobRequestScopeState>();
129
+ let blobLimits = { ...DEFAULT_BLOB_LIMITS };
130
+ let blobBytes = 0;
131
+ let blobLocalBytes = 0;
132
+ let blobPinnedBytes = 0;
133
+ let blobEvictableBytes = 0;
134
+ let blobOldestEvictableAt: number | null = null;
135
+ let rejectedEntryTooLarge = 0;
136
+ let rejectedPinnedSaturation = 0;
137
+ let blobExpiryAccountingTimer: ReturnType<typeof setTimeout> | undefined;
138
+
139
+ function isExpired(entry: CursorBlobEntry, now: number): boolean {
140
+ return now - entry.storedAt >= blobLimits.ttlMs;
141
+ }
142
+
143
+ function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
144
+ if (a.byteLength !== b.byteLength) return false;
145
+ for (let i = 0; i < a.byteLength; i++) if (a[i] !== b[i]) return false;
146
+ return true;
147
+ }
148
+
149
+ function recomputeBlobClassAccounting(): void {
150
+ const now = Date.now();
151
+ let localBytes = 0;
152
+ let pinnedBytes = 0;
153
+ let evictableBytes = 0;
154
+ let oldestAt: number | null = null;
155
+ for (const entry of blobs.values()) {
156
+ const requestPinned = entry.requestPins.size > 0;
157
+ const provenancePinned = entry.provenance === "remote-setBlobArgs" && !isExpired(entry, now);
158
+ if (entry.provenance === "local-regenerated") localBytes += entry.sizeBytes;
159
+ if (requestPinned || provenancePinned) pinnedBytes += entry.sizeBytes;
160
+ if (!requestPinned && (entry.provenance === "local-regenerated" || isExpired(entry, now))) {
161
+ evictableBytes += entry.sizeBytes;
162
+ oldestAt = oldestAt === null ? entry.storedAt : Math.min(oldestAt, entry.storedAt);
163
+ }
164
+ }
165
+ blobLocalBytes = localBytes;
166
+ blobPinnedBytes = pinnedBytes;
167
+ blobEvictableBytes = evictableBytes;
168
+ blobOldestEvictableAt = oldestAt;
169
+ scheduleBlobExpiryAccounting(now);
170
+ }
171
+
172
+ function reconcileBlobClassAccountingAndEnforce(): void {
173
+ recomputeBlobClassAccounting();
174
+ enforceAppOwnedMemoryBudget();
175
+ }
176
+
177
+ function scheduleBlobExpiryAccounting(now: number): void {
178
+ if (blobExpiryAccountingTimer) clearTimeout(blobExpiryAccountingTimer);
179
+ blobExpiryAccountingTimer = undefined;
180
+ let nextExpiry = Number.POSITIVE_INFINITY;
181
+ for (const entry of blobs.values()) {
182
+ if (entry.provenance !== "remote-setBlobArgs" || entry.requestPins.size > 0) continue;
183
+ const expiresAt = entry.storedAt + blobLimits.ttlMs;
184
+ if (expiresAt > now) nextExpiry = Math.min(nextExpiry, expiresAt);
185
+ }
186
+ if (!Number.isFinite(nextExpiry)) return;
187
+ blobExpiryAccountingTimer = setTimeout(() => {
188
+ blobExpiryAccountingTimer = undefined;
189
+ reconcileBlobClassAccountingAndEnforce();
190
+ }, Math.max(0, nextExpiry - now));
191
+ blobExpiryAccountingTimer.unref?.();
192
+ }
193
+
194
+ function deleteBlob(k: string, recompute = true): number {
195
+ const entry = blobs.get(k);
196
+ if (!entry) return 0;
197
+ blobs.delete(k);
198
+ blobBytes -= entry.sizeBytes;
199
+ for (const scope of entry.requestPins) blobRequestScopes.get(scope)?.keys.delete(k);
200
+ if (recompute) recomputeBlobClassAccounting();
201
+ return entry.sizeBytes;
202
+ }
203
+
204
+ function releaseHydratedBlob(k: string, requestScope?: CursorBlobRequestScopeToken): void {
205
+ const entry = blobs.get(k);
206
+ if (!entry) return;
207
+ const scopes = requestScope ? [requestScope] : [...entry.requestPins];
208
+ let changed = false;
209
+ for (const scope of scopes) {
210
+ if (!entry.requestPins.delete(scope)) continue;
211
+ changed = true;
212
+ const state = blobRequestScopes.get(scope);
213
+ state?.keys.delete(k);
214
+ if (state?.sealed && state.keys.size === 0) blobRequestScopes.delete(scope);
215
+ }
216
+ if (changed) reconcileBlobClassAccountingAndEnforce();
217
+ }
218
+
219
+ function setBlob(
220
+ k: string,
221
+ data: Uint8Array,
222
+ provenance: CursorBlobProvenance,
223
+ requestScope?: CursorBlobRequestScopeToken,
224
+ ): CursorBlobAdmission {
225
+ if (data.byteLength > blobLimits.maxEntryBytes) {
226
+ rejectedEntryTooLarge++;
227
+ return { admitted: false, reason: "entry_too_large" };
228
+ }
229
+
230
+ const now = Date.now();
231
+ const existing = blobs.get(k);
232
+ const sameData = existing ? bytesEqual(existing.data, data) : false;
233
+ if (existing && !sameData && existing.requestPins.size > 0) {
234
+ return { admitted: false, reason: "request_pinned_conflict" };
235
+ }
236
+
237
+ const removals = new Set<string>();
238
+ for (const [candidateKey, entry] of blobs) {
239
+ if (candidateKey === k && sameData) continue;
240
+ if (entry.requestPins.size === 0 && isExpired(entry, now)) removals.add(candidateKey);
241
+ }
242
+
243
+ const existingRemovedByTtl = existing !== undefined && removals.has(k);
244
+ const reuseExisting = existing !== undefined && sameData && !existingRemovedByTtl;
245
+ const mergedProvenance: CursorBlobProvenance = reuseExisting && existing.provenance === "remote-setBlobArgs"
246
+ ? "remote-setBlobArgs"
247
+ : provenance;
248
+ const storedAt = reuseExisting && existing.provenance === "remote-setBlobArgs" && provenance === "local-regenerated"
249
+ ? existing.storedAt
250
+ : now;
251
+
252
+ let projectedBytes = blobBytes;
253
+ let projectedCount = blobs.size;
254
+ for (const candidateKey of removals) {
255
+ const entry = blobs.get(candidateKey);
256
+ if (!entry) continue;
257
+ projectedBytes -= entry.sizeBytes;
258
+ projectedCount--;
259
+ }
260
+ if (existing && !existingRemovedByTtl) {
261
+ projectedBytes -= existing.sizeBytes;
262
+ projectedCount--;
263
+ }
264
+ projectedBytes += data.byteLength;
265
+ projectedCount++;
266
+
267
+ if (projectedBytes > blobLimits.maxTotalBytes || projectedCount > blobLimits.maxEntries) {
268
+ const localVictims = [...blobs.entries()].sort((a, b) => a[1].storedAt - b[1].storedAt);
269
+ for (const [candidateKey, entry] of localVictims) {
270
+ if (
271
+ candidateKey === k
272
+ || removals.has(candidateKey)
273
+ || entry.requestPins.size > 0
274
+ || entry.provenance !== "local-regenerated"
275
+ ) continue;
276
+ removals.add(candidateKey);
277
+ projectedBytes -= entry.sizeBytes;
278
+ projectedCount--;
279
+ if (projectedBytes <= blobLimits.maxTotalBytes && projectedCount <= blobLimits.maxEntries) break;
280
+ }
281
+ }
282
+
283
+ if (projectedBytes > blobLimits.maxTotalBytes || projectedCount > blobLimits.maxEntries) {
284
+ rejectedPinnedSaturation++;
285
+ return { admitted: false, reason: "pinned_saturation" };
286
+ }
287
+
288
+ // Carry forward only pins whose scope still has live state; a dead scope's
289
+ // token can never be released again (review C1-1).
290
+ const requestPins = new Set<CursorBlobRequestScopeToken>();
291
+ if (reuseExisting) {
292
+ for (const scope of existing.requestPins) {
293
+ if (blobRequestScopes.has(scope)) requestPins.add(scope);
294
+ }
295
+ }
296
+ // A pin is only attached when the scope still has live UNSEALED state:
297
+ // sealing fixes the advertised key set (locked contract), so a remote
298
+ // setBlobArgs arriving after seal must not extend the pin set — and a
299
+ // sealed scope whose advertised keys have all hydrated is already deleted,
300
+ // so attaching its stale token would create a permanent untracked pin the
301
+ // terminal release could never clear (reviews C1-1/C2-1).
302
+ const scopeState = requestScope ? blobRequestScopes.get(requestScope) : undefined;
303
+ if (requestScope && scopeState && !scopeState.sealed) requestPins.add(requestScope);
304
+ const entry: CursorBlobEntry = {
305
+ data: reuseExisting ? existing.data : data.slice(),
306
+ storedAt,
307
+ sizeBytes: data.byteLength,
308
+ provenance: mergedProvenance,
309
+ requestPins,
310
+ };
311
+
312
+ for (const candidateKey of removals) deleteBlob(candidateKey, false);
313
+ if (blobs.has(k)) deleteBlob(k, false);
314
+ blobs.set(k, entry);
315
+ blobBytes += entry.sizeBytes;
316
+ for (const scope of entry.requestPins) blobRequestScopes.get(scope)?.keys.add(k);
317
+ reconcileBlobClassAccountingAndEnforce();
318
+ return { admitted: true, replaced: existing !== undefined };
319
+ }
320
+
321
+ function getBlob(k: string): Uint8Array | undefined {
322
+ const entry = blobs.get(k);
323
+ if (!entry) return undefined;
324
+ if (entry.requestPins.size === 0 && isExpired(entry, Date.now())) {
325
+ deleteBlob(k);
326
+ return undefined;
327
+ }
328
+ return entry.data;
329
+ }
330
+
331
+ function key(bytes: Uint8Array): string {
332
+ return Buffer.from(bytes).toString("hex");
333
+ }
334
+
335
+ /**
336
+ * Store a blob (SHA-256 keyed) in the shared map that `handleCursorNativeKv` serves, and return its
337
+ * blob id. Cursor's `rootPromptMessagesJson`/turn entries are blob IDS, not inline content — the
338
+ * server fetches the bytes back via `getBlobArgs`. Mirrors jawcode `createBlobId`/`storeCursorBlob`.
339
+ */
340
+ export class CursorBlobAdmissionError extends Error {
341
+ readonly code = "cursor_blob_capacity";
342
+
343
+ constructor(readonly reason: CursorBlobRejectionReason) {
344
+ super("Cursor blob capacity is exhausted; retry with a smaller request.");
345
+ this.name = "CursorBlobAdmissionError";
346
+ }
347
+ }
348
+
349
+ let blobScopeSequence = 0;
350
+
351
+ export function createCursorBlobRequestScope(): CursorBlobRequestScopeToken {
352
+ // Unique description per token so debug snapshots expose exact pin
353
+ // IDENTITY, not just pin counts (review C2-2: identical descriptions made
354
+ // scope-swap bugs invisible to deep comparison).
355
+ const scope = Symbol(`cursor-blob-request-${++blobScopeSequence}`);
356
+ blobRequestScopes.set(scope, { keys: new Set(), sealed: false });
357
+ return scope;
358
+ }
359
+
360
+ export function sealCursorBlobRequestScope(scope: CursorBlobRequestScopeToken): void {
361
+ const state = blobRequestScopes.get(scope);
362
+ if (!state) return;
363
+ state.sealed = true;
364
+ if (state.keys.size === 0) blobRequestScopes.delete(scope);
365
+ }
366
+
367
+ export function releaseCursorBlobRequestScope(scope: CursorBlobRequestScopeToken): void {
368
+ const state = blobRequestScopes.get(scope);
369
+ if (!state) return;
370
+ for (const k of state.keys) blobs.get(k)?.requestPins.delete(scope);
371
+ blobRequestScopes.delete(scope);
372
+ reconcileBlobClassAccountingAndEnforce();
373
+ }
374
+
375
+ export function storeCursorBlob(data: Uint8Array, requestScope?: CursorBlobRequestScopeToken): Uint8Array {
376
+ const blobId = new Uint8Array(createHash("sha256").update(data).digest());
377
+ const admission = setBlob(key(blobId), data, "local-regenerated", requestScope);
378
+ if (!admission.admitted) throw new CursorBlobAdmissionError(admission.reason);
379
+ return blobId;
380
+ }
381
+
382
+ export interface CursorBlobMetrics {
383
+ count: number;
384
+ totalBytes: number;
385
+ localBytes: number;
386
+ pinnedBytes: number;
387
+ rejectedEntryTooLarge: number;
388
+ rejectedPinnedSaturation: number;
389
+ oldestAt: number | null;
390
+ }
391
+
392
+ export function cursorBlobMetrics(): CursorBlobMetrics {
393
+ return {
394
+ count: blobs.size,
395
+ totalBytes: blobBytes,
396
+ localBytes: blobLocalBytes,
397
+ pinnedBytes: blobPinnedBytes,
398
+ rejectedEntryTooLarge,
399
+ rejectedPinnedSaturation,
400
+ oldestAt: blobOldestEvictableAt,
401
+ };
402
+ }
403
+
404
+ export function cursorBlobRetainedStoreSnapshot(): {
405
+ count: number;
406
+ bytes: number;
407
+ evictableBytes: number;
408
+ pinnedBytes: number;
409
+ oldestAt: number | null;
410
+ } {
411
+ return {
412
+ count: blobs.size,
413
+ bytes: blobBytes,
414
+ evictableBytes: blobEvictableBytes,
415
+ pinnedBytes: blobPinnedBytes,
416
+ oldestAt: blobOldestEvictableAt,
417
+ };
418
+ }
419
+
420
+ export function evictOldestCursorBlobForBudget(): number {
421
+ const now = Date.now();
422
+ let oldest: [string, CursorBlobEntry] | undefined;
423
+ for (const candidate of blobs) {
424
+ const entry = candidate[1];
425
+ if (entry.requestPins.size > 0) continue;
426
+ if (entry.provenance !== "local-regenerated" && !isExpired(entry, now)) continue;
427
+ if (!oldest || entry.storedAt < oldest[1].storedAt) oldest = candidate;
428
+ }
429
+ return oldest ? deleteBlob(oldest[0]) : 0;
430
+ }
431
+
432
+ export function setCursorBlobLimitsForTests(limits?: Partial<CursorBlobLimits>): void {
433
+ resetCursorBlobStateForTests();
434
+ blobLimits = limits ? { ...DEFAULT_BLOB_LIMITS, ...limits } : { ...DEFAULT_BLOB_LIMITS };
435
+ }
436
+
437
+ export function resetCursorBlobStateForTests(): void {
438
+ if (blobExpiryAccountingTimer) clearTimeout(blobExpiryAccountingTimer);
439
+ blobExpiryAccountingTimer = undefined;
440
+ blobs.clear();
441
+ blobRequestScopes.clear();
442
+ blobBytes = 0;
443
+ rejectedEntryTooLarge = 0;
444
+ rejectedPinnedSaturation = 0;
445
+ recomputeBlobClassAccounting();
446
+ }
447
+
448
+ export function cursorBlobStoreDebugSnapshotForTests(): Array<{
449
+ key: string;
450
+ sizeBytes: number;
451
+ storedAt: number;
452
+ provenance: CursorBlobProvenance;
453
+ requestPins: number;
454
+ dataDigest: string;
455
+ pinTokens: string[];
456
+ }> {
457
+ return [...blobs].map(([blobKey, entry]) => ({
458
+ key: blobKey,
459
+ sizeBytes: entry.sizeBytes,
460
+ storedAt: entry.storedAt,
461
+ provenance: entry.provenance,
462
+ requestPins: entry.requestPins.size,
463
+ // Byte-for-byte content identity + exact pin-set membership so rollback
464
+ // tests can deep-compare complete state (review C1-2).
465
+ dataDigest: createHash("sha256").update(entry.data).digest("hex"),
466
+ pinTokens: [...entry.requestPins].map(scope => String(scope.description ?? "scope")).sort(),
467
+ }));
468
+ }
469
+
470
+ export async function handleCursorNativeExec(execMsg: ExecServerMessage, deps: CursorNativeExecContext = {}): Promise<Uint8Array[]> {
471
+ const execCase = execMsg.message.case;
472
+ if (execCase === "requestContextArgs") {
473
+ const tools = [...(deps.mcpToolDefs ?? []), ...(deps.clientToolDefs ?? [])];
474
+ return [execBytes(execMsg, "requestContextResult", create(RequestContextResultSchema, {
475
+ result: { case: "success", value: create(RequestContextSuccessSchema, { requestContext: create(RequestContextSchema, { tools }) }) },
476
+ }))];
477
+ }
478
+ if (!cursorUnsafeNativeLocalExecEnabled(deps)) {
479
+ if (execCase === "readArgs") return [rejectReadExecForPolicy(execMsg)];
480
+ if (execCase === "writeArgs") return [rejectWriteExecForPolicy(execMsg)];
481
+ if (execCase === "deleteArgs") return [rejectDeleteExecForPolicy(execMsg)];
482
+ if (execCase === "lsArgs") return [rejectLsExecForPolicy(execMsg)];
483
+ if (execCase === "grepArgs") return [rejectGrepExecForPolicy(execMsg)];
484
+ if (execCase === "shellArgs") return [rejectShellExecForPolicy(execMsg)];
485
+ if (execCase === "shellStreamArgs") return rejectShellStreamExecForPolicy(execMsg);
486
+ if (execCase === "backgroundShellSpawnArgs") return [rejectBackgroundShellSpawnExecForPolicy(execMsg)];
487
+ if (execCase === "writeShellStdinArgs") return [rejectWriteShellStdinExecForPolicy(execMsg)];
488
+ if (execCase === "fetchArgs") return [rejectFetchExecForPolicy(execMsg)];
489
+ }
490
+ if (execCase === "readArgs") return [readExec(execMsg)];
491
+ if (execCase === "writeArgs") return [deps.rejectNativeFileMutations ? rejectWriteExecForApplyPatch(execMsg) : writeExec(execMsg)];
492
+ if (execCase === "deleteArgs") return [deps.rejectNativeFileMutations ? rejectDeleteExecForApplyPatch(execMsg) : deleteExec(execMsg)];
493
+ if (execCase === "lsArgs") return [lsExec(execMsg)];
494
+ if (execCase === "grepArgs") return [grepExec(execMsg)];
495
+ if (execCase === "shellArgs") return [shellExec(execMsg)];
496
+ if (execCase === "shellStreamArgs") return shellStreamExec(execMsg);
497
+ if (execCase === "backgroundShellSpawnArgs") return [backgroundShellSpawnExec(execMsg, deps.sessionId ?? "")];
498
+ if (execCase === "writeShellStdinArgs") return [writeShellStdinExec(execMsg, deps.sessionId ?? "")];
499
+ if (execCase === "fetchArgs") return [await fetchExec(execMsg, deps)];
500
+ if (execCase === "mcpArgs" && execMsg.message.value.providerIdentifier === OCX_RESPONSES_TOOL_PROVIDER) {
501
+ return [execBytes(execMsg, "mcpResult", create(McpResultSchema, {
502
+ result: {
503
+ case: "error",
504
+ value: create(McpErrorSchema, { error: "Cursor requested a client Responses tool through the native exec channel; bridge suspension is not implemented." }),
505
+ },
506
+ }))];
507
+ }
508
+ if (execCase === "mcpArgs") return [await mcpExec(execMsg, deps)];
509
+ if (execCase === "listMcpResourcesExecArgs") return [await listMcpResourcesExec(execMsg, deps)];
510
+ if (execCase === "readMcpResourceExecArgs") return [await readMcpResourceExec(execMsg, deps)];
511
+ if (execCase === "computerUseArgs") return [await computerUseExec(execMsg, deps)];
512
+ if (execCase === "recordScreenArgs") return [await recordScreenExec(execMsg, deps)];
513
+ if (execCase === "diagnosticsArgs") {
514
+ const path = execMsg.message.value.path;
515
+ return [execBytes(execMsg, "diagnosticsResult", create(DiagnosticsResultSchema, {
516
+ result: {
517
+ case: "error",
518
+ value: create(DiagnosticsErrorSchema, {
519
+ path,
520
+ error: "Diagnostics are not supported by the opencodex Cursor transport.",
521
+ }),
522
+ },
523
+ }))];
524
+ }
525
+ // Unknown exec case — Cursor added a new native exec type that our protobuf definition does not
526
+ // include yet. Return an empty reply so the stream stays alive instead of throwing (which kills
527
+ // the entire gRPC connection via failAndClear). Same class of bug as #116.
528
+ debugProviderDiagnostic("cursor", "unknown-exec-case", { execCase: execCase ?? "unknown", execId: execMsg.execId });
529
+ return [];
530
+ }
531
+
532
+
533
+ export function handleCursorNativeKv(
534
+ kvMsg: KvServerMessage,
535
+ requestScope?: CursorBlobRequestScopeToken,
536
+ ): Uint8Array {
537
+ if (kvMsg.message.case === "getBlobArgs") {
538
+ const blobKey = key(kvMsg.message.value.blobId);
539
+ const blobData = getBlob(blobKey);
540
+ if (blobData) releaseHydratedBlob(blobKey, requestScope);
541
+ return clientBytes({
542
+ message: {
543
+ case: "kvClientMessage",
544
+ value: create(KvClientMessageSchema, {
545
+ id: kvMsg.id,
546
+ message: { case: "getBlobResult", value: create(GetBlobResultSchema, blobData ? { blobData } : {}) },
547
+ }),
548
+ },
549
+ });
550
+ }
551
+ if (kvMsg.message.case === "setBlobArgs") {
552
+ const admission = setBlob(
553
+ key(kvMsg.message.value.blobId),
554
+ kvMsg.message.value.blobData,
555
+ "remote-setBlobArgs",
556
+ requestScope,
557
+ );
558
+ return clientBytes({
559
+ message: {
560
+ case: "kvClientMessage",
561
+ value: create(KvClientMessageSchema, {
562
+ id: kvMsg.id,
563
+ message: {
564
+ case: "setBlobResult",
565
+ value: create(SetBlobResultSchema, admission.admitted ? {} : {
566
+ error: create(ErrorSchema, {
567
+ message: "Cursor blob capacity is exhausted; the blob was not stored.",
568
+ }),
569
+ }),
570
+ },
571
+ }),
572
+ },
573
+ });
574
+ }
575
+ return clientBytes({ message: { case: "kvClientMessage", value: create(KvClientMessageSchema, { id: kvMsg.id }) } });
576
+ }