@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,57 @@
1
+ import type { OcxProviderConfig } from "../../types";
2
+ import type { CursorClientMessage, CursorRunRequest, CursorServerMessage } from "./types";
3
+ import type { TranslatorBudget } from "../../lib/translator-budget";
4
+
5
+ export interface CursorTransport {
6
+ run(request: CursorRunRequest, signal?: AbortSignal): AsyncIterable<CursorServerMessage>;
7
+ writeClient(message: CursorClientMessage): void | Promise<void>;
8
+ close?(): void | Promise<void>;
9
+ /**
10
+ * Whether the run request has been committed to the wire. The retry orchestrator only re-dials
11
+ * failures that happened BEFORE this becomes true, so a turn the Cursor server may already have
12
+ * accepted is never replayed. Absent (undefined) is treated as "committed" — safe by default.
13
+ */
14
+ requestCommitted?(): boolean;
15
+ }
16
+
17
+ export interface CursorTransportFactoryInput {
18
+ provider: OcxProviderConfig;
19
+ translatorBudget: TranslatorBudget;
20
+ headers?: Headers;
21
+ /** Pre-first-frame deadline (dial + first server frame). Defaults to 30s when omitted. */
22
+ firstFrameTimeoutMs?: number;
23
+ /** Grace (ms) between close() and the force-destroy fallback after a first-frame timeout. Defaults to 1s. */
24
+ timeoutDestroyGraceMs?: number;
25
+ /**
26
+ * Grace window (ms) before a drained client-tool turn is finalized, so a sibling tool call
27
+ * announced in a later receive chunk can revoke a premature finalize. Defaults to 50ms.
28
+ */
29
+ clientToolFinalizeGraceMs?: number;
30
+ /**
31
+ * True when inbound request text carries the legacy Codex full-access sandbox marker.
32
+ * This is retained as diagnostic/context only; exec-policy.ts does not trust it as
33
+ * native local exec authorization because the text is caller-controlled.
34
+ */
35
+ requestDeclaresFullAccess?: boolean;
36
+ }
37
+
38
+ export type CursorTransportFactory = (input: CursorTransportFactoryInput) => CursorTransport;
39
+
40
+ export class CursorTransportDisabledError extends Error {
41
+ readonly code = "cursor_transport_disabled";
42
+
43
+ constructor(message = "live Cursor transport is disabled") {
44
+ super(message);
45
+ this.name = "CursorTransportDisabledError";
46
+ }
47
+ }
48
+
49
+ export function createDisabledCursorTransport(): CursorTransport {
50
+ return {
51
+ async *run() {
52
+ throw new CursorTransportDisabledError();
53
+ },
54
+ writeClient() {},
55
+ close() {},
56
+ };
57
+ }
@@ -0,0 +1,52 @@
1
+ import type { OcxUsage } from "../../types";
2
+ import type { OcxMessage, OcxRequestOptions, OcxTool } from "../../types";
3
+ import type { CursorRoutingLevel } from "./discovery";
4
+
5
+ export interface CursorRunRequest {
6
+ modelId: string;
7
+ /** Cursor Router optimization parameter; valid only while modelId is the `default` wire model. */
8
+ routingLevel?: CursorRoutingLevel;
9
+ conversationId: string;
10
+ system: string[];
11
+ messages: CursorRequestMessage[];
12
+ rawMessages?: OcxMessage[];
13
+ tools?: OcxTool[];
14
+ toolChoice?: OcxRequestOptions["toolChoice"];
15
+ parallelToolCalls?: boolean;
16
+ /**
17
+ * Clear provider-private context-usage carry-forward before this run. Used when Codex starts a
18
+ * newly observed compacted context epoch, so pre-compaction totals are not over-reported while
19
+ * historical previous_response_id replay remains idempotent.
20
+ */
21
+ contextUsageReset?: boolean;
22
+ /**
23
+ * Defaults to true. Set false for compaction summarizer turns: their checkpoints describe the
24
+ * pre-compaction history being summarized and must not become the next turn's carry-forward total.
25
+ */
26
+ contextUsageStoreCheckpoints?: boolean;
27
+ }
28
+
29
+ export interface CursorRequestMessage {
30
+ role: "user" | "assistant" | "developer" | "tool";
31
+ content: string;
32
+ }
33
+
34
+ export type CursorServerMessage =
35
+ | { type: "text"; text: string }
36
+ | { type: "thinking"; thinking: string }
37
+ | { type: "tool_call_start"; id: string; name: string }
38
+ | { type: "tool_call_delta"; arguments: string }
39
+ | { type: "tool_call_end"; id?: string }
40
+ | { type: "done"; usage?: OcxUsage }
41
+ | { type: "error"; message: string; usage?: OcxUsage }
42
+ | { type: "heartbeat" }
43
+ | { type: "kv_get"; key: string }
44
+ | { type: "kv_set"; key: string; value: Uint8Array }
45
+ | { type: "exec"; execCase: string; requestId: string }
46
+ /** A native exec/MCP action ran locally; retrying this turn could duplicate its side effects. */
47
+ | { type: "local_side_effect" };
48
+
49
+ export type CursorClientMessage =
50
+ | { type: "kv_value"; key: string; value?: Uint8Array }
51
+ | { type: "kv_stored"; key: string }
52
+ | { type: "exec_result"; requestId: string; ok: boolean; message: string };
@@ -0,0 +1,196 @@
1
+ import { createHash } from "node:crypto";
2
+ import type { AdapterEvent, OcxProviderConfig } from "../types";
3
+ import type { ProviderAdapter } from "./base";
4
+ import { isTranslatorBudgetExceededError } from "../lib/translator-budget";
5
+ import { cursorExecDeniedMessage, cursorRequestDeclaresFullAccess } from "./cursor/exec-policy";
6
+ import { isCursorBenignCancelError, isCursorInvalidArgumentError, safeCursorErrorMessage } from "./cursor/cursor-errors";
7
+ import { isCursorExternalWireModel } from "./cursor/discovery";
8
+ import { createCursorKvStore, type CursorKvStore } from "./cursor/kv-store";
9
+ import { mapCursorServerMessage } from "./cursor/message-mapper";
10
+ import { createCursorRequest } from "./cursor/request-builder";
11
+ import {
12
+ createLiveCursorTransport,
13
+ CursorMissingCredentialError,
14
+ rekeyCursorContextUsage,
15
+ resolveCursorToken,
16
+ } from "./cursor/live-transport";
17
+ import { rememberCursorThreadConversation } from "./cursor/thread-continuity";
18
+ import { runCursorTurnWithRetry } from "./cursor/transport-retry";
19
+ import {
20
+ createDisabledCursorTransport,
21
+ CursorTransportDisabledError,
22
+ type CursorTransportFactory,
23
+ } from "./cursor/transport";
24
+
25
+ export const CURSOR_API_URL = "https://api2.cursor.sh";
26
+
27
+ export {
28
+ CURSOR_EXEC_CASES_DENIED,
29
+ cursorExecDeniedMessage,
30
+ type CursorDeniedExecCase,
31
+ } from "./cursor/exec-policy";
32
+
33
+ const CURSOR_TRANSPORT_DISABLED_MESSAGE = [
34
+ "An explicit disabled Cursor transport was injected.",
35
+ "Production Cursor requests use live transport when a Cursor access token is configured.",
36
+ ].join(" ");
37
+
38
+ export interface CursorAdapterDeps {
39
+ createTransport?: CursorTransportFactory;
40
+ kv?: CursorKvStore;
41
+ /** Test seam: observe/replace context-usage rekeying on conversation-id rotation. */
42
+ rekeyContextUsage?: (fromConversationId: string, toConversationId: string) => void;
43
+ }
44
+
45
+ function safeCursorTransportError(err: unknown): string {
46
+ if (err instanceof CursorTransportDisabledError) return CURSOR_TRANSPORT_DISABLED_MESSAGE;
47
+ if (err instanceof CursorMissingCredentialError) {
48
+ return "Cursor live transport is enabled, but no Cursor access token is configured. Set provider.apiKey or OPENCODEX_CURSOR_TEST_TOKEN.";
49
+ }
50
+ const message = err instanceof Error ? err.message : typeof err === "string" ? err : undefined;
51
+ if (message) return safeCursorErrorMessage(message);
52
+ return "Cursor upstream error: transport failed before completion.";
53
+ }
54
+
55
+ export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAdapterDeps = {}): ProviderAdapter {
56
+ return {
57
+ name: "cursor",
58
+
59
+ buildRequest() {
60
+ return {
61
+ url: provider.baseUrl || CURSOR_API_URL,
62
+ method: "POST",
63
+ headers: {},
64
+ body: "",
65
+ };
66
+ },
67
+
68
+ async *parseStream(): AsyncGenerator<AdapterEvent> {
69
+ yield {
70
+ type: "error",
71
+ message: "Cursor adapter uses runTurn; the fetch/parseStream path is disabled.",
72
+ };
73
+ },
74
+
75
+ async runTurn(_parsed, incoming, emit) {
76
+ if (incoming.abortSignal?.aborted) {
77
+ emit({ type: "error", message: "Cursor turn was aborted before start." });
78
+ return;
79
+ }
80
+ try {
81
+ const makeTransport = deps.createTransport ?? createLiveCursorTransport;
82
+ const kv = deps.kv ?? createCursorKvStore({}, incoming.translatorBudget);
83
+ const rekeyContextUsage = deps.rekeyContextUsage ?? rekeyCursorContextUsage;
84
+ // Namespace thread→conversation derivation by the authenticated Cursor credential so
85
+ // shared-proxy tenants with different Cursor accounts cannot collide on a parent thread id.
86
+ // Prefer an already-set auth scope (e.g. Codex pool account) when present.
87
+ if (!_parsed._cursorIdentityScope) {
88
+ try {
89
+ const token = resolveCursorToken(provider, incoming.headers);
90
+ _parsed._cursorIdentityScope = createHash("sha256")
91
+ .update("ocx:cursor:acct:")
92
+ .update(token)
93
+ .digest("hex")
94
+ .slice(0, 16);
95
+ } catch {
96
+ /* Missing credential is handled by the live transport path below. */
97
+ }
98
+ }
99
+ const previousConversationId = _parsed._cursorConversationId;
100
+ let request = createCursorRequest(_parsed);
101
+ // The builder may derive a stable provider id from the client thread when Responses state
102
+ // is unavailable. Rekey only existing state; there is nothing to migrate on a fresh turn,
103
+ // and isolated helper/compaction turns must never inherit or donate the parent's usage state.
104
+ if (
105
+ previousConversationId
106
+ && request.conversationId !== previousConversationId
107
+ && _parsed._cursorIsolateConversation !== true
108
+ ) {
109
+ rekeyContextUsage(previousConversationId, request.conversationId);
110
+ }
111
+ _parsed._cursorConversationId = request.conversationId;
112
+ let emittedOutput = false;
113
+ let replayUnsafe = false;
114
+ const lastRawIsToolResult = _parsed.context.messages.at(-1)?.role === "toolResult";
115
+
116
+ const runOnce = async (activeRequest: ReturnType<typeof createCursorRequest>) => {
117
+ await runCursorTurnWithRetry(
118
+ makeTransport,
119
+ {
120
+ provider,
121
+ headers: incoming.headers,
122
+ translatorBudget: incoming.translatorBudget,
123
+ requestDeclaresFullAccess: cursorRequestDeclaresFullAccess(activeRequest),
124
+ },
125
+ activeRequest,
126
+ incoming.abortSignal,
127
+ (message, activeTransport) => {
128
+ if (incoming.abortSignal?.aborted) {
129
+ emit({ type: "error", message: "Cursor turn was aborted." });
130
+ return;
131
+ }
132
+ if (message.type === "local_side_effect") replayUnsafe = true;
133
+ const events = mapCursorServerMessage(message, {
134
+ kv,
135
+ writeClient: clientMessage => {
136
+ void activeTransport.writeClient(clientMessage);
137
+ },
138
+ });
139
+ for (const event of events) {
140
+ if (event.type !== "heartbeat") emittedOutput = true;
141
+ emit(event);
142
+ }
143
+ },
144
+ );
145
+ };
146
+
147
+ try {
148
+ await runOnce(request);
149
+ } catch (err) {
150
+ // One-shot fallback for external-model Connect invalid_argument before any
151
+ // non-heartbeat output. Retries apply only to safe plain-user turns; tool-result
152
+ // resumes, local exec/MCP side effects, and already-emitted output fail closed.
153
+ if (
154
+ !isCursorInvalidArgumentError(err)
155
+ || !isCursorExternalWireModel(request.modelId)
156
+ || lastRawIsToolResult
157
+ || emittedOutput
158
+ || replayUnsafe
159
+ || incoming.abortSignal?.aborted
160
+ ) {
161
+ throw err;
162
+ }
163
+ const failedConversationId = request.conversationId;
164
+ _parsed._cursorConversationId = undefined;
165
+ request = createCursorRequest(_parsed, { forceFreshConversation: true });
166
+ rekeyContextUsage(failedConversationId, request.conversationId);
167
+ _parsed._cursorConversationId = request.conversationId;
168
+ // Persist recovery for store:false clients that only send a parent thread id, so the
169
+ // next turn does not recompute the stale deterministic thread hash. Isolated helper /
170
+ // compaction turns must not park their throwaway id under the parent thread key.
171
+ if (_parsed._clientThreadId && _parsed._cursorIsolateConversation !== true) {
172
+ rememberCursorThreadConversation(
173
+ _parsed._clientThreadId,
174
+ request.conversationId,
175
+ _parsed._cursorIdentityScope,
176
+ );
177
+ }
178
+ await runOnce(request);
179
+ }
180
+ } catch (err) {
181
+ if (isCursorBenignCancelError(err)) return;
182
+ const partialUsage = (err as { partialUsage?: import("../types").OcxUsage }).partialUsage;
183
+ emit({
184
+ type: "error",
185
+ message: isTranslatorBudgetExceededError(err)
186
+ ? "upstream translation buffer exceeded the safe limit"
187
+ : safeCursorTransportError(err),
188
+ ...(isTranslatorBudgetExceededError(err)
189
+ ? { status: 502, errorType: "upstream_error", code: "translation_buffer_limit" }
190
+ : {}),
191
+ ...(partialUsage ? { usage: partialUsage } : {}),
192
+ });
193
+ }
194
+ },
195
+ };
196
+ }
@@ -0,0 +1,303 @@
1
+ import { enforceAppOwnedMemoryBudget } from "../lib/app-owned-memory";
2
+
3
+ /**
4
+ * Antigravity (Cloud Code Assist) thoughtSignature reasoning-replay cache.
5
+ *
6
+ * Gemini-3 interleaved thinking is stateless upstream: each model content part carries a
7
+ * `thoughtSignature` that MUST be echoed back on the matching part in the next request, or the
8
+ * upstream rejects the turn (HTTP 400). We observe signatures on the response stream, cache them
9
+ * per `model + session`, and re-inject them into the outgoing `request.contents` on the next turn.
10
+ *
11
+ * Mirrors CLIProxyAPI `internal/runtime/executor/antigravity_reasoning_replay.go`. Gemini-only;
12
+ * Claude-on-Antigravity uses inline signature sanitization instead (see google-antigravity-wire).
13
+ */
14
+
15
+ interface ReplayCall {
16
+ signature: string;
17
+ sizeBytes: number;
18
+ touchedAtMs: number;
19
+ }
20
+
21
+ interface ReplayEntry {
22
+ /** thoughtSignature keyed by functionCall identity (name + canonical args). */
23
+ byCall: Map<string, ReplayCall>;
24
+ bytes: number;
25
+ expiresAtMs: number;
26
+ oldestAtMs: number | null;
27
+ }
28
+
29
+ const MIN_SIGNATURE_LEN = 16;
30
+ const REPLAY_TTL_MS = 60 * 60 * 1000; // 1h
31
+ export const ANTIGRAVITY_REPLAY_MAX_ENTRIES = 10_240;
32
+ const REPLAY_EVICT_BATCH = 128;
33
+ const REPLAY_MAX_CALLS_PER_SESSION = 256;
34
+ export const ANTIGRAVITY_REPLAY_MAX_BYTES_PER_SESSION = 2 * 1024 * 1024;
35
+ export const ANTIGRAVITY_REPLAY_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
36
+ const REPLAY_MAX_SIGNATURE_BYTES = 64 * 1024;
37
+
38
+ interface ReplayLimits {
39
+ maxCallsPerSession: number;
40
+ maxBytesPerSession: number;
41
+ maxSignatureBytes: number;
42
+ }
43
+
44
+ const DEFAULT_REPLAY_LIMITS: ReplayLimits = {
45
+ maxCallsPerSession: REPLAY_MAX_CALLS_PER_SESSION,
46
+ maxBytesPerSession: ANTIGRAVITY_REPLAY_MAX_BYTES_PER_SESSION,
47
+ maxSignatureBytes: REPLAY_MAX_SIGNATURE_BYTES,
48
+ };
49
+
50
+ const replayCache = new Map<string, ReplayEntry>();
51
+ const utf8 = new TextEncoder();
52
+ let replayLimits = { ...DEFAULT_REPLAY_LIMITS };
53
+ let replayBytes = 0;
54
+ let replayOldestSessionKey: string | undefined;
55
+ let replayOldestAt: number | null = null;
56
+
57
+ function replayKey(model: string, sessionId: string): string {
58
+ return `${model}::session:${sessionId}`;
59
+ }
60
+
61
+ /** Recursively canonicalize a JSON value: object keys sorted, arrays preserved. */
62
+ function canonicalJson(value: unknown): string {
63
+ if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
64
+ if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
65
+ const entries = Object.keys(value as Record<string, unknown>).sort()
66
+ .map(k => `${JSON.stringify(k)}:${canonicalJson((value as Record<string, unknown>)[k])}`);
67
+ return `{${entries.join(",")}}`;
68
+ }
69
+
70
+ /** Stable identity for a functionCall part: name + recursively canonicalized args. */
71
+ function functionCallKey(name: unknown, args: unknown): string | undefined {
72
+ if (typeof name !== "string" || name.length === 0) return undefined;
73
+ let argsKey = "";
74
+ try {
75
+ argsKey = canonicalJson(args ?? {});
76
+ } catch {
77
+ argsKey = "";
78
+ }
79
+ return `${name}::${argsKey}`;
80
+ }
81
+
82
+ function extractSignature(part: Record<string, unknown>): string | undefined {
83
+ const direct = part.thoughtSignature ?? part.thought_signature;
84
+ if (typeof direct === "string" && direct.length >= MIN_SIGNATURE_LEN) return direct;
85
+ const extra = part.extra_content as { google?: { thought_signature?: unknown } } | undefined;
86
+ const nested = extra?.google?.thought_signature;
87
+ if (typeof nested === "string" && nested.length >= MIN_SIGNATURE_LEN) return nested;
88
+ return undefined;
89
+ }
90
+
91
+ function deleteReplaySession(key: string): number {
92
+ const entry = replayCache.get(key);
93
+ if (!entry) return 0;
94
+ replayCache.delete(key);
95
+ replayBytes -= entry.bytes;
96
+ if (replayOldestSessionKey === key) recomputeReplayOldestCandidate();
97
+ return entry.bytes;
98
+ }
99
+
100
+ function recomputeReplayOldestCandidate(): void {
101
+ replayOldestSessionKey = undefined;
102
+ replayOldestAt = null;
103
+ for (const [key, entry] of replayCache) {
104
+ if (entry.oldestAtMs === null || (replayOldestAt !== null && entry.oldestAtMs >= replayOldestAt)) continue;
105
+ replayOldestSessionKey = key;
106
+ replayOldestAt = entry.oldestAtMs;
107
+ }
108
+ }
109
+
110
+ function refreshReplaySessionCandidate(key: string, entry: ReplayEntry): void {
111
+ entry.oldestAtMs = entry.byCall.values().next().value?.touchedAtMs ?? null;
112
+ if (replayOldestSessionKey === key) {
113
+ recomputeReplayOldestCandidate();
114
+ return;
115
+ }
116
+ if (entry.oldestAtMs !== null && (replayOldestAt === null || entry.oldestAtMs < replayOldestAt)) {
117
+ replayOldestSessionKey = key;
118
+ replayOldestAt = entry.oldestAtMs;
119
+ }
120
+ }
121
+
122
+ function deleteExpiredReplaySessions(now: number): void {
123
+ for (const [key, entry] of replayCache) if (entry.expiresAtMs <= now) deleteReplaySession(key);
124
+ }
125
+
126
+ export function sweepExpiredAntigravityReplay(now = Date.now()): number {
127
+ const before = replayCache.size;
128
+ deleteExpiredReplaySessions(now);
129
+ return before - replayCache.size;
130
+ }
131
+
132
+ function deleteReplayCall(entry: ReplayEntry, callKey: string): number {
133
+ const call = entry.byCall.get(callKey);
134
+ if (!call) return 0;
135
+ entry.byCall.delete(callKey);
136
+ entry.bytes -= call.sizeBytes;
137
+ replayBytes -= call.sizeBytes;
138
+ return call.sizeBytes;
139
+ }
140
+
141
+ function evictInnerCalls(entry: ReplayEntry): void {
142
+ while (
143
+ entry.byCall.size > replayLimits.maxCallsPerSession
144
+ || entry.bytes > replayLimits.maxBytesPerSession
145
+ ) {
146
+ const oldest = entry.byCall.keys().next().value;
147
+ if (oldest === undefined) break;
148
+ deleteReplayCall(entry, oldest);
149
+ }
150
+ }
151
+
152
+ function evictIfNeeded(): void {
153
+ if (replayCache.size > ANTIGRAVITY_REPLAY_MAX_ENTRIES) {
154
+ const oldest = [...replayCache.entries()]
155
+ .sort((a, b) => a[1].expiresAtMs - b[1].expiresAtMs)
156
+ .slice(0, REPLAY_EVICT_BATCH);
157
+ for (const [key] of oldest) deleteReplaySession(key);
158
+ }
159
+ while (replayBytes > ANTIGRAVITY_REPLAY_MAX_TOTAL_BYTES) {
160
+ const oldestKey = [...replayCache.entries()]
161
+ .sort((a, b) => a[1].expiresAtMs - b[1].expiresAtMs)[0]?.[0];
162
+ if (oldestKey === undefined) return;
163
+ deleteReplaySession(oldestKey);
164
+ }
165
+ }
166
+
167
+ /** Gemini/Flash/Agent use the replay cache; Claude does not (inline sanitization instead). */
168
+ export function antigravityUsesReplayCache(model: string): boolean {
169
+ return !/claude/i.test(model);
170
+ }
171
+
172
+ /**
173
+ * Observe a parsed CCA chunk's `candidates[0].content.parts` and record thought signatures keyed by
174
+ * the functionCall identity (name + args). Accumulates across the whole session so a sequential
175
+ * multi-step tool loop keeps EVERY prior call's signature, not just the latest part-index slot.
176
+ * `parts` is the already-unwrapped `response.candidates[0].content.parts`.
177
+ */
178
+ export function observeAntigravityReplay(model: string, sessionId: string, parts: unknown[]): void {
179
+ if (!antigravityUsesReplayCache(model) || !Array.isArray(parts) || parts.length === 0) return;
180
+ const now = Date.now();
181
+ deleteExpiredReplaySessions(now);
182
+ const key = replayKey(model, sessionId);
183
+ const entry = replayCache.get(key) ?? {
184
+ byCall: new Map<string, ReplayCall>(),
185
+ bytes: 0,
186
+ expiresAtMs: 0,
187
+ oldestAtMs: null,
188
+ };
189
+ let inserted = false;
190
+ for (const raw of parts) {
191
+ if (!raw || typeof raw !== "object") continue;
192
+ const part = raw as Record<string, unknown>;
193
+ const sig = extractSignature(part);
194
+ if (!sig) continue;
195
+ const fc = part.functionCall as { name?: unknown; args?: unknown } | undefined;
196
+ const ck = fc ? functionCallKey(fc.name, fc.args) : undefined;
197
+ if (!ck) continue; // only function-call signatures are replayable by identity
198
+ const signatureBytes = utf8.encode(sig).byteLength;
199
+ const sizeBytes = utf8.encode(ck).byteLength + signatureBytes;
200
+ if (signatureBytes > replayLimits.maxSignatureBytes || sizeBytes > replayLimits.maxBytesPerSession) continue;
201
+ deleteReplayCall(entry, ck);
202
+ entry.byCall.set(ck, { signature: sig, sizeBytes, touchedAtMs: now });
203
+ entry.bytes += sizeBytes;
204
+ replayBytes += sizeBytes;
205
+ inserted = true;
206
+ }
207
+ if (!inserted) return;
208
+ evictInnerCalls(entry);
209
+ entry.expiresAtMs = now + REPLAY_TTL_MS;
210
+ replayCache.set(key, entry);
211
+ refreshReplaySessionCandidate(key, entry);
212
+ evictIfNeeded();
213
+ enforceAppOwnedMemoryBudget();
214
+ }
215
+
216
+ /**
217
+ * Re-inject cached thought signatures into the outgoing `request.contents`, matched by functionCall
218
+ * identity across ALL model turns (not just the last one). Only fills a functionCall part that
219
+ * lacks a real signature. Returns the same array reference (mutated in place).
220
+ */
221
+ export function applyAntigravityReplay(model: string, sessionId: string, contents: unknown[]): unknown[] {
222
+ if (!antigravityUsesReplayCache(model) || !Array.isArray(contents)) return contents;
223
+ const now = Date.now();
224
+ deleteExpiredReplaySessions(now);
225
+ const entry = replayCache.get(replayKey(model, sessionId));
226
+ if (!entry) {
227
+ return contents;
228
+ }
229
+ let touched = false;
230
+ for (const c of contents as { role?: string; parts?: unknown[] }[]) {
231
+ if (!c || typeof c !== "object" || c.role !== "model" || !Array.isArray(c.parts)) continue;
232
+ for (const raw of c.parts) {
233
+ if (!raw || typeof raw !== "object") continue;
234
+ const part = raw as Record<string, unknown>;
235
+ const fc = part.functionCall as { name?: unknown; args?: unknown } | undefined;
236
+ if (!fc) continue;
237
+ if (part.thoughtSignature !== undefined || part.thought_signature !== undefined) continue;
238
+ const ck = functionCallKey(fc.name, fc.args);
239
+ const call = ck ? entry.byCall.get(ck) : undefined;
240
+ if (call && ck) {
241
+ part.thoughtSignature = call.signature;
242
+ entry.byCall.delete(ck);
243
+ entry.byCall.set(ck, { ...call, touchedAtMs: now });
244
+ touched = true;
245
+ }
246
+ }
247
+ }
248
+ if (touched) refreshReplaySessionCandidate(replayKey(model, sessionId), entry);
249
+ return contents;
250
+ }
251
+
252
+ /** Drop the cache entry when upstream rejects a signature (clear-on-invalid). */
253
+ export function clearAntigravityReplay(model: string, sessionId: string): void {
254
+ deleteReplaySession(replayKey(model, sessionId));
255
+ }
256
+
257
+ export function antigravityReplayMetrics(): {
258
+ sessions: number;
259
+ calls: number;
260
+ totalBytes: number;
261
+ largestSessionBytes: number;
262
+ } {
263
+ let calls = 0;
264
+ let largestSessionBytes = 0;
265
+ for (const entry of replayCache.values()) {
266
+ calls += entry.byCall.size;
267
+ largestSessionBytes = Math.max(largestSessionBytes, entry.bytes);
268
+ }
269
+ return { sessions: replayCache.size, calls, totalBytes: replayBytes, largestSessionBytes };
270
+ }
271
+
272
+ export function antigravityReplayRetainedStoreSnapshot(): {
273
+ count: number;
274
+ bytes: number;
275
+ evictableBytes: number;
276
+ pinnedBytes: number;
277
+ oldestAt: number | null;
278
+ } {
279
+ return {
280
+ count: replayCache.size,
281
+ bytes: replayBytes,
282
+ evictableBytes: replayBytes,
283
+ pinnedBytes: 0,
284
+ oldestAt: replayOldestAt,
285
+ };
286
+ }
287
+
288
+ export function evictOldestAntigravityReplayForBudget(): number {
289
+ return replayOldestSessionKey === undefined ? 0 : deleteReplaySession(replayOldestSessionKey);
290
+ }
291
+
292
+ export function setAntigravityReplayLimitsForTests(limits?: Partial<ReplayLimits>): void {
293
+ __resetAntigravityReplayCache();
294
+ replayLimits = limits ? { ...DEFAULT_REPLAY_LIMITS, ...limits } : { ...DEFAULT_REPLAY_LIMITS };
295
+ }
296
+
297
+ /** Test seam. */
298
+ export function __resetAntigravityReplayCache(): void {
299
+ replayCache.clear();
300
+ replayBytes = 0;
301
+ replayOldestSessionKey = undefined;
302
+ replayOldestAt = null;
303
+ }