@yansigit/opencodex 2.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (826) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/LICENSE +21 -0
  3. package/README.md +303 -0
  4. package/assets/architecture.png +0 -0
  5. package/assets/banner.png +0 -0
  6. package/assets/claude-code-models.gif +0 -0
  7. package/assets/codex-app-picker.png +0 -0
  8. package/bin/ocx.mjs +587 -0
  9. package/bin/package-main.mjs +9 -0
  10. package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
  11. package/gui/dist/assets/index-CGoDO3uO.css +1 -0
  12. package/gui/dist/favicon.png +0 -0
  13. package/gui/dist/icons.svg +24 -0
  14. package/gui/dist/index.html +25 -0
  15. package/gui/dist/logo.png +0 -0
  16. package/gui/dist/provider-icons/alibaba-color.svg +1 -0
  17. package/gui/dist/provider-icons/antigravity-color.svg +1 -0
  18. package/gui/dist/provider-icons/claude-color.svg +1 -0
  19. package/gui/dist/provider-icons/cline-color.svg +16 -0
  20. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
  21. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  22. package/gui/dist/provider-icons/copilot-color.svg +1 -0
  23. package/gui/dist/provider-icons/cursor-color.svg +2 -0
  24. package/gui/dist/provider-icons/deepseek-color.svg +1 -0
  25. package/gui/dist/provider-icons/discord.svg +1 -0
  26. package/gui/dist/provider-icons/firepass-color.svg +1 -0
  27. package/gui/dist/provider-icons/fireworks-color.svg +1 -0
  28. package/gui/dist/provider-icons/gemini-color.svg +1 -0
  29. package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
  30. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
  31. package/gui/dist/provider-icons/grok.svg +1 -0
  32. package/gui/dist/provider-icons/groq-color.svg +1 -0
  33. package/gui/dist/provider-icons/huggingface-color.svg +1 -0
  34. package/gui/dist/provider-icons/kimi-color.svg +1 -0
  35. package/gui/dist/provider-icons/kiro-color.svg +15 -0
  36. package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
  37. package/gui/dist/provider-icons/mistral-color.svg +1 -0
  38. package/gui/dist/provider-icons/moonshot-color.svg +1 -0
  39. package/gui/dist/provider-icons/nvidia-color.svg +1 -0
  40. package/gui/dist/provider-icons/ollama-color.svg +1 -0
  41. package/gui/dist/provider-icons/openai.svg +1 -0
  42. package/gui/dist/provider-icons/opencode.svg +2 -0
  43. package/gui/dist/provider-icons/openrouter-color.svg +1 -0
  44. package/gui/dist/provider-icons/pi.svg +21 -0
  45. package/gui/dist/provider-icons/qianfan-color.svg +1 -0
  46. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
  47. package/gui/dist/provider-icons/telegram.svg +1 -0
  48. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
  49. package/gui/dist/provider-icons/vllm-color.svg +1 -0
  50. package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
  51. package/package.json +108 -0
  52. package/src/AGENTS.md +28 -0
  53. package/src/adapters/anthropic-image-guard.ts +251 -0
  54. package/src/adapters/anthropic-image-normalize.ts +518 -0
  55. package/src/adapters/anthropic-output-schema.ts +137 -0
  56. package/src/adapters/anthropic.ts +1327 -0
  57. package/src/adapters/azure.ts +36 -0
  58. package/src/adapters/base.ts +121 -0
  59. package/src/adapters/client-fingerprint.ts +65 -0
  60. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  61. package/src/adapters/command-code.ts +601 -0
  62. package/src/adapters/cursor/arg-codec.ts +38 -0
  63. package/src/adapters/cursor/arg-normalize.ts +104 -0
  64. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  65. package/src/adapters/cursor/cursor-errors.ts +288 -0
  66. package/src/adapters/cursor/discovery.ts +333 -0
  67. package/src/adapters/cursor/effort-map.ts +151 -0
  68. package/src/adapters/cursor/exec-policy.ts +88 -0
  69. package/src/adapters/cursor/framing.ts +250 -0
  70. package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
  71. package/src/adapters/cursor/h2-pool.ts +123 -0
  72. package/src/adapters/cursor/http1-bidi.ts +361 -0
  73. package/src/adapters/cursor/images.ts +704 -0
  74. package/src/adapters/cursor/kv-store.ts +52 -0
  75. package/src/adapters/cursor/live-models.ts +269 -0
  76. package/src/adapters/cursor/live-smoke-gate.ts +41 -0
  77. package/src/adapters/cursor/live-transport.ts +1653 -0
  78. package/src/adapters/cursor/mcp-config.ts +42 -0
  79. package/src/adapters/cursor/mcp-manager.ts +333 -0
  80. package/src/adapters/cursor/message-mapper.ts +49 -0
  81. package/src/adapters/cursor/native-exec-common.ts +76 -0
  82. package/src/adapters/cursor/native-exec-desktop.ts +184 -0
  83. package/src/adapters/cursor/native-exec-fs.ts +332 -0
  84. package/src/adapters/cursor/native-exec-mcp.ts +153 -0
  85. package/src/adapters/cursor/native-exec-network.ts +43 -0
  86. package/src/adapters/cursor/native-exec-shell.ts +547 -0
  87. package/src/adapters/cursor/native-exec-tools.ts +118 -0
  88. package/src/adapters/cursor/native-exec.ts +663 -0
  89. package/src/adapters/cursor/protobuf-events.ts +1381 -0
  90. package/src/adapters/cursor/protobuf-request.ts +1032 -0
  91. package/src/adapters/cursor/request-builder.ts +461 -0
  92. package/src/adapters/cursor/thread-continuity.ts +67 -0
  93. package/src/adapters/cursor/tool-definitions.ts +735 -0
  94. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  95. package/src/adapters/cursor/transport-retry.ts +132 -0
  96. package/src/adapters/cursor/transport.ts +79 -0
  97. package/src/adapters/cursor/types.ts +79 -0
  98. package/src/adapters/cursor.ts +322 -0
  99. package/src/adapters/google-antigravity-hosts.ts +48 -0
  100. package/src/adapters/google-antigravity-replay.ts +827 -0
  101. package/src/adapters/google-antigravity-tools.ts +105 -0
  102. package/src/adapters/google-antigravity-wire.ts +141 -0
  103. package/src/adapters/google-errors.ts +92 -0
  104. package/src/adapters/google-http.ts +460 -0
  105. package/src/adapters/google-tool-schema.ts +238 -0
  106. package/src/adapters/google-truncation.ts +24 -0
  107. package/src/adapters/google-wire-compiler.ts +232 -0
  108. package/src/adapters/google.ts +1377 -0
  109. package/src/adapters/identity.ts +77 -0
  110. package/src/adapters/image.ts +23 -0
  111. package/src/adapters/kiro-constants.ts +16 -0
  112. package/src/adapters/kiro-errors.ts +208 -0
  113. package/src/adapters/kiro-events.ts +197 -0
  114. package/src/adapters/kiro-images.ts +129 -0
  115. package/src/adapters/kiro-retry.ts +312 -0
  116. package/src/adapters/kiro-thinking.ts +112 -0
  117. package/src/adapters/kiro-tool-fallback.ts +36 -0
  118. package/src/adapters/kiro-tools.ts +224 -0
  119. package/src/adapters/kiro-truncation.ts +33 -0
  120. package/src/adapters/kiro-wire.ts +129 -0
  121. package/src/adapters/kiro.ts +1936 -0
  122. package/src/adapters/mimo-free.ts +280 -0
  123. package/src/adapters/openai-chat-url.ts +11 -0
  124. package/src/adapters/openai-chat.ts +1980 -0
  125. package/src/adapters/openai-responses-url.ts +16 -0
  126. package/src/adapters/openai-responses.ts +1911 -0
  127. package/src/adapters/registry.ts +175 -0
  128. package/src/adapters/responses-tool-schema.ts +67 -0
  129. package/src/adapters/run-turn-queue.ts +114 -0
  130. package/src/adapters/tool-call-id.ts +119 -0
  131. package/src/adapters/tool-catalog-nudge.ts +154 -0
  132. package/src/adapters/upstream-http-error.ts +48 -0
  133. package/src/adapters/xai-web-search.ts +185 -0
  134. package/src/bridge.ts +1986 -0
  135. package/src/chat/inbound.ts +319 -0
  136. package/src/chat/outbound.ts +821 -0
  137. package/src/claude/agents-inject.ts +266 -0
  138. package/src/claude/alias.ts +149 -0
  139. package/src/claude/auth-detect.ts +229 -0
  140. package/src/claude/auth-mode-migration.ts +32 -0
  141. package/src/claude/auth-mode.ts +62 -0
  142. package/src/claude/context-windows.ts +205 -0
  143. package/src/claude/desktop-3p-guard.ts +35 -0
  144. package/src/claude/desktop-3p-paths.ts +84 -0
  145. package/src/claude/desktop-3p.ts +615 -0
  146. package/src/claude/desktop-health.ts +26 -0
  147. package/src/claude/desktop-profile.ts +263 -0
  148. package/src/claude/gateway-cache.ts +107 -0
  149. package/src/claude/inbound-debug.ts +163 -0
  150. package/src/claude/inbound.ts +578 -0
  151. package/src/claude/model-info.ts +174 -0
  152. package/src/claude/outbound.ts +926 -0
  153. package/src/cli/access.ts +108 -0
  154. package/src/cli/account-api.ts +302 -0
  155. package/src/cli/account-auth.ts +250 -0
  156. package/src/cli/account-catalog-refresh.ts +14 -0
  157. package/src/cli/account-extended.ts +737 -0
  158. package/src/cli/account-main.ts +317 -0
  159. package/src/cli/account.ts +299 -0
  160. package/src/cli/agent-driven.ts +70 -0
  161. package/src/cli/agent.ts +290 -0
  162. package/src/cli/catalog-prewarm.ts +27 -0
  163. package/src/cli/claude-agent-startup-sync.ts +73 -0
  164. package/src/cli/claude-desktop.ts +213 -0
  165. package/src/cli/claude.ts +355 -0
  166. package/src/cli/codex-log-guard-doctor.ts +103 -0
  167. package/src/cli/codex-shim-autorestore.ts +47 -0
  168. package/src/cli/codex-shim-readiness.ts +76 -0
  169. package/src/cli/combo.ts +127 -0
  170. package/src/cli/config-command.ts +209 -0
  171. package/src/cli/debug.ts +228 -0
  172. package/src/cli/dispatch.ts +585 -0
  173. package/src/cli/doctor.ts +1202 -0
  174. package/src/cli/ensure-desired-integrations.ts +152 -0
  175. package/src/cli/export-command.ts +213 -0
  176. package/src/cli/help.ts +101 -0
  177. package/src/cli/index.ts +973 -0
  178. package/src/cli/init.ts +211 -0
  179. package/src/cli/integrations.ts +260 -0
  180. package/src/cli/interactive-confirm.ts +133 -0
  181. package/src/cli/lab.ts +607 -0
  182. package/src/cli/launcher-context.ts +77 -0
  183. package/src/cli/minimax.ts +497 -0
  184. package/src/cli/models-runtime.ts +245 -0
  185. package/src/cli/models.ts +422 -0
  186. package/src/cli/observe.ts +206 -0
  187. package/src/cli/opencode.ts +588 -0
  188. package/src/cli/provider-replit.ts +232 -0
  189. package/src/cli/provider-runtime.ts +179 -0
  190. package/src/cli/provider.ts +492 -0
  191. package/src/cli/ready.ts +301 -0
  192. package/src/cli/registry.ts +422 -0
  193. package/src/cli/replit-gateway-key-input.ts +138 -0
  194. package/src/cli/root.ts +86 -0
  195. package/src/cli/route-policy.ts +92 -0
  196. package/src/cli/runtime-api.ts +328 -0
  197. package/src/cli/star-prompt.ts +211 -0
  198. package/src/cli/status-oauth.ts +78 -0
  199. package/src/cli/status.ts +328 -0
  200. package/src/cli/system-command.ts +112 -0
  201. package/src/cli/system-restart-client.ts +146 -0
  202. package/src/cli/tray-proxy.ts +199 -0
  203. package/src/cli/v2.ts +268 -0
  204. package/src/cli.ts +10 -0
  205. package/src/clients/config-export.ts +1704 -0
  206. package/src/codex/account-id.ts +34 -0
  207. package/src/codex/account-label.ts +47 -0
  208. package/src/codex/account-lifecycle.ts +172 -0
  209. package/src/codex/account-namespace-match.ts +63 -0
  210. package/src/codex/account-namespaces.ts +195 -0
  211. package/src/codex/account-pause.ts +20 -0
  212. package/src/codex/account-priority.ts +83 -0
  213. package/src/codex/account-runtime-state.ts +31 -0
  214. package/src/codex/account-store.ts +544 -0
  215. package/src/codex/account-usability.ts +43 -0
  216. package/src/codex/admission.ts +256 -0
  217. package/src/codex/affinity-debug.ts +162 -0
  218. package/src/codex/agent-roles-sync.ts +225 -0
  219. package/src/codex/agent-roles.ts +238 -0
  220. package/src/codex/app-server-processes.ts +1143 -0
  221. package/src/codex/app-server-restart-service.ts +232 -0
  222. package/src/codex/auth-api.ts +2147 -0
  223. package/src/codex/auth-collision.ts +109 -0
  224. package/src/codex/auth-context.ts +665 -0
  225. package/src/codex/autostart-health.ts +156 -0
  226. package/src/codex/catalog/account-models.ts +67 -0
  227. package/src/codex/catalog/aggregation.ts +436 -0
  228. package/src/codex/catalog/bundled.ts +549 -0
  229. package/src/codex/catalog/effort.ts +446 -0
  230. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  231. package/src/codex/catalog/kinds.ts +2 -0
  232. package/src/codex/catalog/metadata.ts +664 -0
  233. package/src/codex/catalog/native-models.ts +72 -0
  234. package/src/codex/catalog/parsing.ts +650 -0
  235. package/src/codex/catalog/provider-fetch.ts +2064 -0
  236. package/src/codex/catalog/sync.ts +1883 -0
  237. package/src/codex/catalog-admission.ts +199 -0
  238. package/src/codex/catalog-refresh-status.ts +105 -0
  239. package/src/codex/catalog-write-serialization.ts +242 -0
  240. package/src/codex/catalog.ts +14 -0
  241. package/src/codex/codex-write-lock.ts +384 -0
  242. package/src/codex/convergence-types.ts +614 -0
  243. package/src/codex/convergence.ts +651 -0
  244. package/src/codex/coordinator-doctor.ts +332 -0
  245. package/src/codex/custom-model-catalog-migration.ts +176 -0
  246. package/src/codex/data/upstream-models.json +830 -0
  247. package/src/codex/desired-state.ts +230 -0
  248. package/src/codex/exec-invocation.ts +22 -0
  249. package/src/codex/features.ts +1566 -0
  250. package/src/codex/generation.ts +202 -0
  251. package/src/codex/history-job.ts +407 -0
  252. package/src/codex/history-lock.ts +242 -0
  253. package/src/codex/history-migration-guardian.ts +108 -0
  254. package/src/codex/history-provider.ts +979 -0
  255. package/src/codex/history-transition.ts +105 -0
  256. package/src/codex/history-worker.ts +220 -0
  257. package/src/codex/home.ts +206 -0
  258. package/src/codex/inject-coordination.ts +290 -0
  259. package/src/codex/inject.ts +1733 -0
  260. package/src/codex/injected-marker.ts +106 -0
  261. package/src/codex/integration-record.ts +266 -0
  262. package/src/codex/internal/catalog-writer.ts +203 -0
  263. package/src/codex/internal/history-writer.ts +80 -0
  264. package/src/codex/journal.ts +225 -0
  265. package/src/codex/log-guard/inspect.ts +506 -0
  266. package/src/codex/log-guard/lock.ts +150 -0
  267. package/src/codex/log-guard/maintenance.ts +403 -0
  268. package/src/codex/log-guard/path-safety.ts +88 -0
  269. package/src/codex/log-guard/policy.ts +44 -0
  270. package/src/codex/log-guard/processes.ts +205 -0
  271. package/src/codex/log-guard/protection.ts +489 -0
  272. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  273. package/src/codex/main-account-cache.ts +56 -0
  274. package/src/codex/main-account.ts +68 -0
  275. package/src/codex/management-convergence.ts +167 -0
  276. package/src/codex/model-cache.ts +273 -0
  277. package/src/codex/model-entitlements.ts +353 -0
  278. package/src/codex/native-main-admission.ts +47 -0
  279. package/src/codex/native-main-auth-temp.ts +187 -0
  280. package/src/codex/native-main-claim.ts +178 -0
  281. package/src/codex/native-main-lock-file.ts +162 -0
  282. package/src/codex/native-main-owner.ts +329 -0
  283. package/src/codex/native-profile-api.ts +247 -0
  284. package/src/codex/native-profile-manager.ts +1531 -0
  285. package/src/codex/native-profile-processes.ts +121 -0
  286. package/src/codex/native-profile-recovery.ts +99 -0
  287. package/src/codex/native-profile-stage-store.ts +387 -0
  288. package/src/codex/native-profile-startup.ts +492 -0
  289. package/src/codex/native-profile-store.ts +855 -0
  290. package/src/codex/native-profile-types.ts +120 -0
  291. package/src/codex/native-residue.ts +682 -0
  292. package/src/codex/paths.ts +144 -0
  293. package/src/codex/plan-from-token.ts +140 -0
  294. package/src/codex/plan.ts +40 -0
  295. package/src/codex/plugins-doctor.ts +242 -0
  296. package/src/codex/pool-rotation.ts +295 -0
  297. package/src/codex/project-config-warnings.ts +425 -0
  298. package/src/codex/prompt-journal.ts +352 -0
  299. package/src/codex/prompt-layers.ts +967 -0
  300. package/src/codex/prompt-lock.ts +143 -0
  301. package/src/codex/quota-rejection.ts +298 -0
  302. package/src/codex/quota.ts +573 -0
  303. package/src/codex/refresh.ts +62 -0
  304. package/src/codex/reset-credit-recovery.ts +1044 -0
  305. package/src/codex/routing.ts +1888 -0
  306. package/src/codex/runtime.ts +659 -0
  307. package/src/codex/shim.ts +2170 -0
  308. package/src/codex/subagent-defaults.ts +550 -0
  309. package/src/codex/subagent-model-fallback.ts +784 -0
  310. package/src/codex/sync.ts +319 -0
  311. package/src/codex/transition-state.ts +612 -0
  312. package/src/codex/upstream-host-health.ts +368 -0
  313. package/src/codex/user-identity.ts +557 -0
  314. package/src/codex/warmup.ts +298 -0
  315. package/src/codex/websocket-registry.ts +100 -0
  316. package/src/codex/write-coordination.ts +114 -0
  317. package/src/combos/failover.ts +160 -0
  318. package/src/combos/index.ts +45 -0
  319. package/src/combos/request.ts +94 -0
  320. package/src/combos/resolve.ts +232 -0
  321. package/src/combos/types.ts +398 -0
  322. package/src/config/provider-name.ts +24 -0
  323. package/src/config.ts +4041 -0
  324. package/src/fork/register.ts +3 -0
  325. package/src/generated/compatibility-version.json +3116 -0
  326. package/src/generated/model-metadata.ts +106 -0
  327. package/src/github/star-state.ts +203 -0
  328. package/src/grok/inject.ts +530 -0
  329. package/src/grok/inspect.ts +45 -0
  330. package/src/grok/status.ts +121 -0
  331. package/src/grok/sync.ts +66 -0
  332. package/src/images/artifacts.ts +516 -0
  333. package/src/images/fulfill-video.ts +163 -0
  334. package/src/images/fulfill.ts +149 -0
  335. package/src/images/index.ts +4 -0
  336. package/src/images/loop.ts +955 -0
  337. package/src/images/plan.ts +143 -0
  338. package/src/images/synthetic-tool.ts +133 -0
  339. package/src/images/types.ts +41 -0
  340. package/src/images/xai-client.ts +141 -0
  341. package/src/images/xai-video-client.ts +163 -0
  342. package/src/index.ts +22 -0
  343. package/src/integrations/config-io.ts +269 -0
  344. package/src/integrations/journal.ts +315 -0
  345. package/src/integrations/merge.ts +135 -0
  346. package/src/integrations/mutation-flight.ts +71 -0
  347. package/src/integrations/native/ownership-preflight.ts +202 -0
  348. package/src/integrations/omp-yaml-source.ts +358 -0
  349. package/src/integrations/owned-refresh.ts +74 -0
  350. package/src/integrations/ownership.ts +111 -0
  351. package/src/integrations/registry.ts +159 -0
  352. package/src/integrations/serialize.ts +314 -0
  353. package/src/integrations/state.ts +361 -0
  354. package/src/integrations/store.ts +103 -0
  355. package/src/integrations/writer-lock.ts +98 -0
  356. package/src/integrations/writer.ts +691 -0
  357. package/src/lab/artifacts/sanitize.ts +586 -0
  358. package/src/lab/artifacts/secure-fs.ts +475 -0
  359. package/src/lab/artifacts/store.ts +310 -0
  360. package/src/lab/automation/budgets.ts +78 -0
  361. package/src/lab/automation/config-persistence.ts +256 -0
  362. package/src/lab/automation/constants.ts +39 -0
  363. package/src/lab/automation/cooldown.ts +103 -0
  364. package/src/lab/automation/dispatch.ts +211 -0
  365. package/src/lab/automation/index.ts +13 -0
  366. package/src/lab/automation/orchestrator.ts +499 -0
  367. package/src/lab/automation/persistence.ts +512 -0
  368. package/src/lab/automation/planner.ts +371 -0
  369. package/src/lab/automation/policy.ts +136 -0
  370. package/src/lab/automation/queue.ts +191 -0
  371. package/src/lab/automation/recovery.ts +24 -0
  372. package/src/lab/automation/route-context.ts +21 -0
  373. package/src/lab/automation/run-key.ts +44 -0
  374. package/src/lab/automation/runs-query.ts +34 -0
  375. package/src/lab/automation/types.ts +160 -0
  376. package/src/lab/conformance/assertion.ts +325 -0
  377. package/src/lab/conformance/digest.ts +22 -0
  378. package/src/lab/conformance/executor.ts +741 -0
  379. package/src/lab/conformance/fixture-provider.ts +27 -0
  380. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  381. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  382. package/src/lab/conformance/harness-budget.ts +47 -0
  383. package/src/lab/conformance/index.ts +5 -0
  384. package/src/lab/conformance/jcs.ts +64 -0
  385. package/src/lab/conformance/json-pointer.ts +39 -0
  386. package/src/lab/conformance/manifest.ts +180 -0
  387. package/src/lab/conformance/mcp-stub.ts +179 -0
  388. package/src/lab/conformance/negative-controls.ts +164 -0
  389. package/src/lab/conformance/observation.ts +355 -0
  390. package/src/lab/conformance/runner.ts +68 -0
  391. package/src/lab/conformance/sse-normalize.ts +59 -0
  392. package/src/lab/conformance/suite-manifest.ts +78 -0
  393. package/src/lab/conformance/types.ts +214 -0
  394. package/src/lab/constants.ts +126 -0
  395. package/src/lab/digest.ts +64 -0
  396. package/src/lab/events/errors.ts +9 -0
  397. package/src/lab/events/limits.ts +117 -0
  398. package/src/lab/events/types.ts +229 -0
  399. package/src/lab/events/validate.ts +781 -0
  400. package/src/lab/fabric/constants.ts +40 -0
  401. package/src/lab/fabric/executor.ts +492 -0
  402. package/src/lab/fabric/index.ts +80 -0
  403. package/src/lab/fabric/manifest.ts +222 -0
  404. package/src/lab/fabric/observe.ts +489 -0
  405. package/src/lab/fabric/patch.ts +79 -0
  406. package/src/lab/fabric/producer-child.ts +139 -0
  407. package/src/lab/fabric/producer-isolate.ts +276 -0
  408. package/src/lab/fabric/producer-protocol.ts +61 -0
  409. package/src/lab/fabric/scratch.ts +439 -0
  410. package/src/lab/fabric/subject.ts +106 -0
  411. package/src/lab/fabric/types.ts +134 -0
  412. package/src/lab/fabric/verifier.ts +98 -0
  413. package/src/lab/index.ts +54 -0
  414. package/src/lab/ledger/artifact-refs.ts +127 -0
  415. package/src/lab/ledger/invalidation.ts +136 -0
  416. package/src/lab/ledger/purge.ts +310 -0
  417. package/src/lab/ledger/store.ts +532 -0
  418. package/src/lab/live/credential-lease.ts +53 -0
  419. package/src/lab/live/destination.ts +155 -0
  420. package/src/lab/live/executor.ts +336 -0
  421. package/src/lab/live/inert-tools.ts +56 -0
  422. package/src/lab/live/manifest.ts +85 -0
  423. package/src/lab/live/mcp-loopback.ts +57 -0
  424. package/src/lab/live/runner.ts +19 -0
  425. package/src/lab/live/sandbox.ts +61 -0
  426. package/src/lab/live/suite-manifest.ts +41 -0
  427. package/src/lab/live/transport.ts +118 -0
  428. package/src/lab/live/types.ts +197 -0
  429. package/src/lab/observe/from-conformance.ts +301 -0
  430. package/src/lab/observe/from-live.ts +117 -0
  431. package/src/lab/paths.ts +153 -0
  432. package/src/lab/projection/rebuild.ts +495 -0
  433. package/src/lab/projection/schema.ts +135 -0
  434. package/src/lab/projection/verdicts.ts +474 -0
  435. package/src/lab/projection/verification.ts +412 -0
  436. package/src/lab/public/bundle.ts +217 -0
  437. package/src/lab/public/community-authority.ts +175 -0
  438. package/src/lab/public/community-files.ts +29 -0
  439. package/src/lab/public/community.ts +479 -0
  440. package/src/lab/public/file-safety.ts +155 -0
  441. package/src/lab/public/ids.ts +26 -0
  442. package/src/lab/public/index.ts +16 -0
  443. package/src/lab/public/mutation-lock.ts +424 -0
  444. package/src/lab/public/operator.ts +353 -0
  445. package/src/lab/public/origin-purge.ts +79 -0
  446. package/src/lab/public/origin.ts +203 -0
  447. package/src/lab/public/privacy.ts +143 -0
  448. package/src/lab/public/private-file.ts +261 -0
  449. package/src/lab/public/project.ts +124 -0
  450. package/src/lab/public/purge-test-fault.ts +21 -0
  451. package/src/lab/public/purge.ts +223 -0
  452. package/src/lab/public/registry.ts +44 -0
  453. package/src/lab/public/revocation.ts +252 -0
  454. package/src/lab/public/signature.ts +243 -0
  455. package/src/lab/public/storage.ts +105 -0
  456. package/src/lab/public/strict-json.ts +206 -0
  457. package/src/lab/public/time.ts +26 -0
  458. package/src/lab/public/types.ts +172 -0
  459. package/src/lab/public/validate.ts +391 -0
  460. package/src/lab/query/catalog.ts +101 -0
  461. package/src/lab/query/connection.ts +107 -0
  462. package/src/lab/query/constants.ts +4 -0
  463. package/src/lab/query/cursor.ts +132 -0
  464. package/src/lab/query/dto-map.ts +277 -0
  465. package/src/lab/query/errors.ts +22 -0
  466. package/src/lab/query/freshness.ts +53 -0
  467. package/src/lab/query/index.ts +45 -0
  468. package/src/lab/query/latest-observation.ts +59 -0
  469. package/src/lab/query/passive-production.ts +159 -0
  470. package/src/lab/query/queries.ts +444 -0
  471. package/src/lab/query/types.ts +266 -0
  472. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  473. package/src/lab/subject/installation-salt.ts +112 -0
  474. package/src/lab/subject/protocol-subject.ts +80 -0
  475. package/src/lab/subject/route-subject.ts +74 -0
  476. package/src/lib/abort.ts +146 -0
  477. package/src/lib/admin-secrets.ts +25 -0
  478. package/src/lib/admission.ts +83 -0
  479. package/src/lib/app-owned-memory-stores.ts +195 -0
  480. package/src/lib/app-owned-memory.ts +265 -0
  481. package/src/lib/bounded-body.ts +346 -0
  482. package/src/lib/bun-binary-validator.d.mts +3 -0
  483. package/src/lib/bun-binary-validator.mjs +18 -0
  484. package/src/lib/bun-runtime.ts +184 -0
  485. package/src/lib/bun-stream-caps.ts +130 -0
  486. package/src/lib/codex-restart-contract.ts +120 -0
  487. package/src/lib/config-ownership.ts +364 -0
  488. package/src/lib/crash-guard.ts +344 -0
  489. package/src/lib/debug-log-buffer.ts +83 -0
  490. package/src/lib/debug-settings.ts +108 -0
  491. package/src/lib/debug.ts +31 -0
  492. package/src/lib/destination-policy.ts +380 -0
  493. package/src/lib/errors.ts +406 -0
  494. package/src/lib/eventstream-decoder.ts +253 -0
  495. package/src/lib/fabric-task-execution-authority.ts +7 -0
  496. package/src/lib/fabric-task-host.ts +29 -0
  497. package/src/lib/gcp-adc.ts +341 -0
  498. package/src/lib/injection-debug-log.ts +58 -0
  499. package/src/lib/lab-activation.ts +223 -0
  500. package/src/lib/lab-live-execution-authority.ts +13 -0
  501. package/src/lib/lab-live-host.ts +30 -0
  502. package/src/lib/lab-live-pinned-sender.ts +56 -0
  503. package/src/lib/lab-live-route-production.ts +130 -0
  504. package/src/lib/lab-passive-linker-registration.ts +26 -0
  505. package/src/lib/local-management-attestation.ts +51 -0
  506. package/src/lib/local-management-capability.ts +100 -0
  507. package/src/lib/local-provider-reload-contract.ts +100 -0
  508. package/src/lib/open-url.ts +25 -0
  509. package/src/lib/optional-shutdown-hooks.ts +57 -0
  510. package/src/lib/pinned-http.ts +270 -0
  511. package/src/lib/privacy.ts +20 -0
  512. package/src/lib/process-control.ts +168 -0
  513. package/src/lib/provider-outbound.ts +210 -0
  514. package/src/lib/provider-url.ts +14 -0
  515. package/src/lib/proxy-env.ts +18 -0
  516. package/src/lib/redact.ts +521 -0
  517. package/src/lib/retry-after.ts +55 -0
  518. package/src/lib/self-launch-argv.ts +15 -0
  519. package/src/lib/server-resource-ownership.ts +71 -0
  520. package/src/lib/service-secrets.ts +25 -0
  521. package/src/lib/shadow-call.ts +61 -0
  522. package/src/lib/sidecar-tracker.ts +52 -0
  523. package/src/lib/sse-decoder.ts +364 -0
  524. package/src/lib/state-store-registrations.ts +119 -0
  525. package/src/lib/state-store-sweeper.ts +184 -0
  526. package/src/lib/system-restart-contract.ts +73 -0
  527. package/src/lib/test-home-guard.ts +90 -0
  528. package/src/lib/token-estimate.ts +86 -0
  529. package/src/lib/tool-argument-integers.ts +202 -0
  530. package/src/lib/translator-budget.ts +400 -0
  531. package/src/lib/upstream-http-version.ts +57 -0
  532. package/src/lib/upstream-reachability.ts +95 -0
  533. package/src/lib/upstream-retry.ts +392 -0
  534. package/src/lib/win-exec.ts +115 -0
  535. package/src/lib/win-paths.ts +68 -0
  536. package/src/lib/windows-atomic-replace.ts +156 -0
  537. package/src/lib/windows-elevation.ts +773 -0
  538. package/src/lib/windows-secret-acl.ts +854 -0
  539. package/src/lib/windows-service-wrappers.ts +72 -0
  540. package/src/lib/windows-text.ts +106 -0
  541. package/src/lib/windows-user-principal.ts +341 -0
  542. package/src/lib/winsw.ts +403 -0
  543. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  544. package/src/oauth/account-import/index.ts +15 -0
  545. package/src/oauth/account-import/parser.ts +83 -0
  546. package/src/oauth/account-import/registry.ts +18 -0
  547. package/src/oauth/account-import/service.ts +75 -0
  548. package/src/oauth/account-import/types.ts +91 -0
  549. package/src/oauth/anthropic-routing.ts +594 -0
  550. package/src/oauth/anthropic.ts +188 -0
  551. package/src/oauth/antigravity-routing.ts +151 -0
  552. package/src/oauth/callback-server.ts +300 -0
  553. package/src/oauth/chatgpt.ts +161 -0
  554. package/src/oauth/command-code.ts +239 -0
  555. package/src/oauth/cursor.ts +252 -0
  556. package/src/oauth/github-copilot.ts +428 -0
  557. package/src/oauth/google-antigravity.ts +262 -0
  558. package/src/oauth/health.ts +407 -0
  559. package/src/oauth/index.ts +1504 -0
  560. package/src/oauth/key-providers.ts +124 -0
  561. package/src/oauth/kimi.ts +227 -0
  562. package/src/oauth/kiro-credentials.ts +726 -0
  563. package/src/oauth/kiro.ts +621 -0
  564. package/src/oauth/local-token-detect.ts +130 -0
  565. package/src/oauth/log.ts +50 -0
  566. package/src/oauth/login-cli.ts +223 -0
  567. package/src/oauth/nous.ts +798 -0
  568. package/src/oauth/pkce.ts +15 -0
  569. package/src/oauth/store.ts +728 -0
  570. package/src/oauth/token-guardian.ts +309 -0
  571. package/src/oauth/types.ts +62 -0
  572. package/src/oauth/xai.ts +241 -0
  573. package/src/providers/alibaba-region-backup.ts +75 -0
  574. package/src/providers/alibaba-region-migration.ts +156 -0
  575. package/src/providers/alibaba-region-startup.ts +36 -0
  576. package/src/providers/antigravity-models.ts +695 -0
  577. package/src/providers/antigravity-quota.ts +216 -0
  578. package/src/providers/api-keys.ts +140 -0
  579. package/src/providers/base-url-choices.ts +74 -0
  580. package/src/providers/codex-capacity.ts +292 -0
  581. package/src/providers/command-code-efforts.ts +144 -0
  582. package/src/providers/context-cap.ts +82 -0
  583. package/src/providers/cursor-pool.ts +72 -0
  584. package/src/providers/derive.ts +586 -0
  585. package/src/providers/fastwire.ts +501 -0
  586. package/src/providers/free-directory.ts +187 -0
  587. package/src/providers/github-copilot-transport.ts +56 -0
  588. package/src/providers/google-vertex-location.ts +14 -0
  589. package/src/providers/key-failover.ts +271 -0
  590. package/src/providers/kiro-models.ts +67 -0
  591. package/src/providers/label.ts +19 -0
  592. package/src/providers/model-discovery-limits.ts +16 -0
  593. package/src/providers/model-discovery.ts +449 -0
  594. package/src/providers/model-rename-migration.ts +255 -0
  595. package/src/providers/model-rename-startup.ts +28 -0
  596. package/src/providers/openai-sidecar.ts +243 -0
  597. package/src/providers/openai-tier-startup.ts +56 -0
  598. package/src/providers/openai-tiers.ts +423 -0
  599. package/src/providers/openai-virtual-models.ts +83 -0
  600. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  601. package/src/providers/openrouter-routing.ts +102 -0
  602. package/src/providers/provider-id-rewrite.ts +185 -0
  603. package/src/providers/quota.ts +2345 -0
  604. package/src/providers/registry.ts +2918 -0
  605. package/src/providers/replit/constants.ts +27 -0
  606. package/src/providers/replit/derive.ts +85 -0
  607. package/src/providers/replit/headers.ts +28 -0
  608. package/src/providers/replit/origin.ts +55 -0
  609. package/src/providers/replit/pair-install-response.ts +72 -0
  610. package/src/providers/replit/probe.ts +199 -0
  611. package/src/providers/replit/setup.ts +350 -0
  612. package/src/providers/request-pacing.ts +310 -0
  613. package/src/providers/service-tier.ts +277 -0
  614. package/src/providers/slug-codec.ts +103 -0
  615. package/src/providers/static-model-discovery.ts +86 -0
  616. package/src/providers/xai-responses-opt-in.ts +15 -0
  617. package/src/providers/xai-transport.ts +148 -0
  618. package/src/reasoning-effort.ts +183 -0
  619. package/src/responses/compaction.ts +142 -0
  620. package/src/responses/custom-tool-compat.ts +266 -0
  621. package/src/responses/hosted-tool-policy.ts +9 -0
  622. package/src/responses/namespace-tool-compat.ts +355 -0
  623. package/src/responses/parser.ts +838 -0
  624. package/src/responses/provider-continuation.ts +98 -0
  625. package/src/responses/provider-opaque-metadata.ts +73 -0
  626. package/src/responses/reasoning-envelope.ts +60 -0
  627. package/src/responses/reasoning-replay-cache.ts +426 -0
  628. package/src/responses/schema.ts +165 -0
  629. package/src/responses/spill-store.ts +459 -0
  630. package/src/responses/state.ts +1433 -0
  631. package/src/responses/thought-signature-replay.ts +347 -0
  632. package/src/responses/tool-groups.ts +19 -0
  633. package/src/responses/tool-search-compat.ts +301 -0
  634. package/src/responses/truncated-stop-reason.ts +60 -0
  635. package/src/router.ts +761 -0
  636. package/src/routing/analytics.ts +378 -0
  637. package/src/routing/capability.ts +244 -0
  638. package/src/routing/compatibility/assemble.ts +73 -0
  639. package/src/routing/compatibility/behavior.ts +278 -0
  640. package/src/routing/compatibility/catalog.ts +99 -0
  641. package/src/routing/compatibility/endpoint.ts +52 -0
  642. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  643. package/src/routing/compatibility/policy.ts +181 -0
  644. package/src/routing/compatibility/provider-slot.ts +56 -0
  645. package/src/routing/compatibility/reader.ts +110 -0
  646. package/src/routing/compatibility/subject.ts +191 -0
  647. package/src/routing/compatibility/types.ts +64 -0
  648. package/src/routing/compatibility/version.ts +104 -0
  649. package/src/routing/cost.ts +77 -0
  650. package/src/routing/evaluator.ts +495 -0
  651. package/src/routing/health.ts +412 -0
  652. package/src/routing/history/cursor.ts +43 -0
  653. package/src/routing/history/indexer.ts +605 -0
  654. package/src/routing/history/schema.ts +72 -0
  655. package/src/routing/profile-namespace.ts +15 -0
  656. package/src/routing/profile.ts +547 -0
  657. package/src/routing/quota.ts +145 -0
  658. package/src/routing/request-evidence.ts +45 -0
  659. package/src/routing/trace.ts +776 -0
  660. package/src/server/adapter-resolve.ts +53 -0
  661. package/src/server/auth-cors.ts +751 -0
  662. package/src/server/background-lifecycle.ts +182 -0
  663. package/src/server/chat-completions.ts +442 -0
  664. package/src/server/chat-native-sse.ts +331 -0
  665. package/src/server/chat-native.ts +426 -0
  666. package/src/server/claude-messages.ts +1030 -0
  667. package/src/server/direct-local-http.ts +347 -0
  668. package/src/server/effort-policy.ts +190 -0
  669. package/src/server/github-copilot-responses-repair.ts +338 -0
  670. package/src/server/gui-static.ts +152 -0
  671. package/src/server/image-retry.ts +42 -0
  672. package/src/server/images.ts +568 -0
  673. package/src/server/index.ts +1813 -0
  674. package/src/server/lifecycle.ts +498 -0
  675. package/src/server/live.ts +717 -0
  676. package/src/server/local-management-read-client.ts +90 -0
  677. package/src/server/local-provider-reload-client.ts +137 -0
  678. package/src/server/management/agent-settings-routes.ts +1433 -0
  679. package/src/server/management/api-access.ts +141 -0
  680. package/src/server/management/api-key-usage.ts +193 -0
  681. package/src/server/management/body.ts +41 -0
  682. package/src/server/management/combo-routes.ts +263 -0
  683. package/src/server/management/config-routes.ts +835 -0
  684. package/src/server/management/context.ts +113 -0
  685. package/src/server/management/integration-routes.ts +498 -0
  686. package/src/server/management/lab-automation-routes.ts +206 -0
  687. package/src/server/management/lab-routes.ts +563 -0
  688. package/src/server/management/logs-usage-routes.ts +586 -0
  689. package/src/server/management/model-routes.ts +560 -0
  690. package/src/server/management/model-rows.ts +163 -0
  691. package/src/server/management/native-integration-routes.ts +769 -0
  692. package/src/server/management/oauth-account-routes.ts +637 -0
  693. package/src/server/management/provider-capability-config.ts +48 -0
  694. package/src/server/management/provider-routes.ts +1033 -0
  695. package/src/server/management/replit-provider-routes.ts +86 -0
  696. package/src/server/management/request-history-routes.ts +191 -0
  697. package/src/server/management/routing-analytics-routes.ts +74 -0
  698. package/src/server/management/routing-profile-routes.ts +385 -0
  699. package/src/server/management/shared.ts +286 -0
  700. package/src/server/management/sidebar-routes.ts +106 -0
  701. package/src/server/management/storage-log-guard-routes.ts +186 -0
  702. package/src/server/management/sync-response.ts +69 -0
  703. package/src/server/management/system-restart.ts +435 -0
  704. package/src/server/management/system-routes.ts +194 -0
  705. package/src/server/management/usage-summary-cache.ts +94 -0
  706. package/src/server/management/vision-sidecar-options.ts +167 -0
  707. package/src/server/management/web-search-sidecar-options.ts +120 -0
  708. package/src/server/management-api.ts +314 -0
  709. package/src/server/management-auth.ts +482 -0
  710. package/src/server/memory-watchdog.ts +156 -0
  711. package/src/server/passive-route-linker.ts +66 -0
  712. package/src/server/port-reclaim.ts +307 -0
  713. package/src/server/ports.ts +156 -0
  714. package/src/server/proxy-liveness.ts +328 -0
  715. package/src/server/readiness.ts +99 -0
  716. package/src/server/relay-eager.ts +353 -0
  717. package/src/server/relay.ts +1209 -0
  718. package/src/server/request-decompress.ts +239 -0
  719. package/src/server/request-log-conversation.ts +168 -0
  720. package/src/server/request-log.ts +1259 -0
  721. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  722. package/src/server/responses/agent-task-recovery.ts +465 -0
  723. package/src/server/responses/collaboration.ts +551 -0
  724. package/src/server/responses/compact.ts +771 -0
  725. package/src/server/responses/core.ts +5389 -0
  726. package/src/server/responses/empty-completion-guard.ts +276 -0
  727. package/src/server/responses/encrypted-payload.ts +331 -0
  728. package/src/server/responses/fetch-helpers.ts +232 -0
  729. package/src/server/responses/input-admission.ts +185 -0
  730. package/src/server/responses/pacing-overload.ts +13 -0
  731. package/src/server/responses/passthrough-error.ts +78 -0
  732. package/src/server/responses/policy-fallback.ts +178 -0
  733. package/src/server/responses/responses-field-backfill.ts +251 -0
  734. package/src/server/responses/terminal-guard.ts +251 -0
  735. package/src/server/responses/upstream-error.ts +53 -0
  736. package/src/server/responses/ws-upstream.ts +308 -0
  737. package/src/server/responses-custom-tool-repair.ts +282 -0
  738. package/src/server/responses-image-gen-repair.ts +132 -0
  739. package/src/server/responses-item-id-repair.ts +272 -0
  740. package/src/server/responses-json-events.ts +90 -0
  741. package/src/server/responses-model-rewrite.ts +29 -0
  742. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  743. package/src/server/responses-snapshot-repair.ts +621 -0
  744. package/src/server/responses-terminal-repair.ts +342 -0
  745. package/src/server/responses-tool-search-repair.ts +267 -0
  746. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  747. package/src/server/responses.ts +25 -0
  748. package/src/server/search.ts +201 -0
  749. package/src/server/sse-frame-buffer.ts +292 -0
  750. package/src/server/sse-payload-rewrite.ts +263 -0
  751. package/src/server/startup-action-control.ts +315 -0
  752. package/src/server/startup-health-cache.ts +131 -0
  753. package/src/server/system-env.ts +484 -0
  754. package/src/server/windows-tcp-drop.ts +184 -0
  755. package/src/server/windows-tray-control.ts +41 -0
  756. package/src/server/ws-bridge.ts +472 -0
  757. package/src/service-manager-probe.ts +892 -0
  758. package/src/service.ts +3575 -0
  759. package/src/sidecar/auth.ts +92 -0
  760. package/src/sidecar/candidates.ts +83 -0
  761. package/src/stall-timeout.ts +20 -0
  762. package/src/storage/cleanup-job.ts +57 -0
  763. package/src/storage/cleanup.ts +3085 -0
  764. package/src/storage/policy-job.ts +457 -0
  765. package/src/storage/policy-scheduler.ts +40 -0
  766. package/src/storage/policy-worker.ts +59 -0
  767. package/src/storage/policy.ts +527 -0
  768. package/src/storage/restore-job.ts +299 -0
  769. package/src/storage/restore-worker.ts +58 -0
  770. package/src/storage/scanner.ts +238 -0
  771. package/src/storage/storage-mutation-coordinator.ts +139 -0
  772. package/src/storage/worker-lifecycle.ts +215 -0
  773. package/src/tray/assets/opencodex-tray-offline.ico +0 -0
  774. package/src/tray/assets/opencodex-tray-online.ico +0 -0
  775. package/src/tray/assets/opencodex-tray-warning.ico +0 -0
  776. package/src/tray/assets/opencodex-tray.png +0 -0
  777. package/src/tray/windows-tray.ps1 +364 -0
  778. package/src/tray/windows.ts +757 -0
  779. package/src/types/accounts.ts +37 -0
  780. package/src/types/config.ts +876 -0
  781. package/src/types/provider.ts +545 -0
  782. package/src/types/request.ts +384 -0
  783. package/src/types/tools.ts +131 -0
  784. package/src/types/wire.ts +80 -0
  785. package/src/types.ts +106 -0
  786. package/src/update/badge.ts +72 -0
  787. package/src/update/index.ts +415 -0
  788. package/src/update/job.ts +1887 -0
  789. package/src/update/notify.ts +263 -0
  790. package/src/update/npm-cache-preflight.d.mts +47 -0
  791. package/src/update/npm-cache-preflight.mjs +201 -0
  792. package/src/update/npm-invocation.d.mts +23 -0
  793. package/src/update/npm-invocation.mjs +94 -0
  794. package/src/update/transactional-install.d.mts +22 -0
  795. package/src/update/transactional-install.mjs +259 -0
  796. package/src/update/tray-update-plan.d.mts +18 -0
  797. package/src/update/tray-update-plan.mjs +38 -0
  798. package/src/usage/cost.ts +625 -0
  799. package/src/usage/debug.ts +97 -0
  800. package/src/usage/expected-prices.ts +416 -0
  801. package/src/usage/log.ts +1223 -0
  802. package/src/usage/summary.ts +753 -0
  803. package/src/usage/totals.ts +14 -0
  804. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  805. package/src/usage/user-cost-overlays.ts +314 -0
  806. package/src/vision/anthropic-describe.ts +189 -0
  807. package/src/vision/backends.ts +97 -0
  808. package/src/vision/describe.ts +131 -0
  809. package/src/vision/eligibility.ts +250 -0
  810. package/src/vision/index.ts +681 -0
  811. package/src/vision/reasoning.ts +55 -0
  812. package/src/vision/routed-describe.ts +175 -0
  813. package/src/vision/timeout-bounds.ts +9 -0
  814. package/src/web-search/anthropic-executor.ts +195 -0
  815. package/src/web-search/backends.ts +108 -0
  816. package/src/web-search/exa-executor.ts +88 -0
  817. package/src/web-search/executor.ts +113 -0
  818. package/src/web-search/format-result.ts +89 -0
  819. package/src/web-search/gemini-executor.ts +141 -0
  820. package/src/web-search/index.ts +331 -0
  821. package/src/web-search/loop.ts +896 -0
  822. package/src/web-search/parse.ts +315 -0
  823. package/src/web-search/progress-stream.ts +342 -0
  824. package/src/web-search/sources.ts +60 -0
  825. package/src/web-search/synthetic-tool.ts +47 -0
  826. package/src/web-search/xai-executor.ts +219 -0
@@ -0,0 +1,1032 @@
1
+ import { create, fromBinary, toBinary, toJson } from "@bufbuild/protobuf";
2
+ import { fromJson, type JsonValue } from "@bufbuild/protobuf";
3
+ import { ValueSchema } from "@bufbuild/protobuf/wkt";
4
+ import type { OcxAssistantContentPart, OcxMessage, OcxToolResultMessage } from "../../types";
5
+ import { namespacedToolName } from "../../types";
6
+ import type { CursorRunRequest } from "./types";
7
+ import { cursorNeedsExternalToolContinuation, isCursorExternalWireModel } from "./discovery";
8
+ import { normalizeCursorToolResultText } from "./tool-result-normalize";
9
+ import { debugProviderDiagnostic } from "../../lib/debug";
10
+ import {
11
+ createCursorBlobRequestScope,
12
+ cursorBlobMaxEntryBytes,
13
+ releaseCursorBlobRequestScope,
14
+ sealCursorBlobRequestScope,
15
+ storeCursorBlob,
16
+ type CursorBlobRequestScopeToken,
17
+ } from "./native-exec";
18
+ import { buildSelectedContext, CURSOR_VISION_IMAGE_HISTORY_MARKER } from "./images";
19
+ import { estimateTokens } from "../../lib/token-estimate";
20
+ import { parseDataUrl } from "../image";
21
+ import {
22
+ AgentClientMessageSchema,
23
+ AgentConversationTurnStructureSchema,
24
+ AssistantMessageSchema,
25
+ AgentRunRequestSchema,
26
+ ConversationActionSchema,
27
+ ConversationStepSchema,
28
+ ConversationStateStructureSchema,
29
+ type ConversationStateStructure,
30
+ ConversationTurnStructureSchema,
31
+ McpArgsSchema,
32
+ McpSuccessSchema,
33
+ McpTextContentSchema,
34
+ McpImageContentSchema,
35
+ McpToolCallSchema,
36
+ McpToolResultContentItemSchema,
37
+ McpToolResultSchema,
38
+ McpToolsSchema,
39
+ type McpToolDefinition,
40
+ ModelDetailsSchema,
41
+ RequestedModelSchema,
42
+ RequestedModel_ModelParameterbytesSchema,
43
+ ResumeActionSchema,
44
+ RequestContextSchema,
45
+ RequestContextEnvSchema,
46
+ ThinkingMessageSchema,
47
+ ToolCallSchema,
48
+ UserMessageActionSchema,
49
+ UserMessageSchema,
50
+ } from "./gen/agent_pb";
51
+ import {
52
+ appendCursorGenericToolUseHint,
53
+ cursorToolsForActivePrompt,
54
+ buildCursorToolGuidanceSystemNote,
55
+ buildCursorToolDefinitions,
56
+ cursorRequestHasShellAlias,
57
+ CURSOR_SHELL_ALIAS_SYSTEM_NOTE,
58
+ OCX_RESPONSES_TOOL_PROVIDER,
59
+ } from "./tool-definitions";
60
+
61
+ const encoder = new TextEncoder();
62
+ const decoder = new TextDecoder();
63
+
64
+ /** Parameter id advertised by Cursor's `default` model for its Cost/Balance/Intelligence control. */
65
+ export const CURSOR_ROUTING_LEVEL_PARAMETER_ID = "optimization";
66
+ // Cursor external workers reject oversized root replay sets with a late invalid_argument after
67
+ // hydrating every blob (observed at 208 roots with usedTokens=0). Keep headroom below that boundary,
68
+ // retaining all system prompts and the newest model-visible history. Cursor IDE similarly bounds /
69
+ // compacts long conversations rather than replaying an unbounded message list.
70
+ export const CURSOR_EXTERNAL_ROOT_BLOB_LIMIT = 192;
71
+ /** Approximate prompt-size guard; tool schemas and protocol framing consume context separately. */
72
+ export const CURSOR_EXTERNAL_ROOT_BYTE_LIMIT = 512 * 1024;
73
+
74
+ /**
75
+ * Action text for external-model tool-result continuations. Native models keep
76
+ * resumeAction; external wire models continue as userMessageAction so the
77
+ * results already stored in history blobs are visible without a ResumeAction.
78
+ */
79
+ export const CURSOR_EXTERNAL_TOOL_CONTINUATION_TEXT =
80
+ "Continue: the requested tool results are provided in the conversation history above.";
81
+
82
+ /** Runtime timezone for protobuf RequestContextEnv (dynamic, never hardcoded). */
83
+ function runtimeTimeZone(): string {
84
+ try {
85
+ return Intl.DateTimeFormat().resolvedOptions().timeZone ?? "UTC";
86
+ } catch {
87
+ return "UTC";
88
+ }
89
+ }
90
+
91
+ /** Builds a RequestContext with env.timeZone populated dynamically. */
92
+ function buildRequestContext() {
93
+ return create(RequestContextSchema, {
94
+ env: create(RequestContextEnvSchema, {
95
+ timeZone: runtimeTimeZone(),
96
+ }),
97
+ });
98
+ }
99
+
100
+ function jsonBlob(value: unknown): { data: Uint8Array; serialized: string } {
101
+ const serialized = JSON.stringify(value);
102
+ return { data: encoder.encode(serialized), serialized };
103
+ }
104
+
105
+ type RootBlobCandidate = {
106
+ data: Uint8Array;
107
+ byteLength: number;
108
+ /**
109
+ * The exact JSON handed to storeCursorBlob(). Retained so a token estimate can read
110
+ * what the wire actually carries without re-serializing — and without drifting from
111
+ * it after pruning or truncation (#373).
112
+ */
113
+ serialized: string;
114
+ role: "system" | "user" | "assistant" | "toolResult";
115
+ messageIndex?: number;
116
+ /** Original JSON text payload used when an active tool result must be truncated to fit. */
117
+ text?: string;
118
+ };
119
+
120
+ function rootBlobCandidate(
121
+ value: unknown,
122
+ role: RootBlobCandidate["role"],
123
+ opts?: { messageIndex?: number; text?: string },
124
+ ): RootBlobCandidate {
125
+ const { data, serialized } = jsonBlob(value);
126
+ return {
127
+ data,
128
+ byteLength: data.byteLength,
129
+ serialized,
130
+ role,
131
+ ...(opts?.messageIndex !== undefined ? { messageIndex: opts.messageIndex } : {}),
132
+ ...(opts?.text !== undefined ? { text: opts.text } : {}),
133
+ };
134
+ }
135
+
136
+ function toolResultRootPayload(text: string): { role: "assistant"; content: [{ type: "text"; text: string }] } {
137
+ return { role: "assistant", content: [{ type: "text", text }] };
138
+ }
139
+
140
+ function truncateToolResultBlob(entry: RootBlobCandidate, maxBytes: number): RootBlobCandidate | null {
141
+ if (entry.byteLength <= maxBytes) return entry;
142
+ if (entry.role !== "toolResult" || entry.text === undefined) return null;
143
+ const marker = "\n…[truncated for Cursor external replay budget]";
144
+ const encoded = encoder.encode(entry.text);
145
+ // Leave headroom for JSON envelope (`role`/`content` wrapper) around the truncated text.
146
+ let keepBytes = Math.min(encoded.byteLength, Math.max(0, maxBytes - encoder.encode(marker).byteLength - 96));
147
+ for (let attempt = 0; attempt < 8; attempt++) {
148
+ let end = keepBytes;
149
+ while (end > 0 && end < encoded.byteLength && (encoded[end]! & 0xc0) === 0x80) end -= 1;
150
+ const truncated = `${decoder.decode(encoded.subarray(0, end))}${marker}`;
151
+ const result = rootBlobCandidate(
152
+ toolResultRootPayload(truncated),
153
+ "toolResult",
154
+ { messageIndex: entry.messageIndex, text: truncated },
155
+ );
156
+ if (result.byteLength <= maxBytes) return result;
157
+ if (end === 0) break;
158
+ keepBytes = Math.max(0, end - (result.byteLength - maxBytes) - 16);
159
+ }
160
+ const markerOnly = rootBlobCandidate(
161
+ toolResultRootPayload(marker.trimStart()),
162
+ "toolResult",
163
+ { messageIndex: entry.messageIndex, text: marker.trimStart() },
164
+ );
165
+ return markerOnly.byteLength <= maxBytes ? markerOnly : null;
166
+ }
167
+
168
+ function systemPromptBlobs(request: CursorRunRequest): RootBlobCandidate[] {
169
+ const prompts = request.system.length > 0 ? [...request.system] : ["You are a helpful assistant."];
170
+ if (cursorRequestHasShellAlias(request.tools)) prompts.push(CURSOR_SHELL_ALIAS_SYSTEM_NOTE);
171
+ const cursorToolGuidance = buildCursorToolGuidanceSystemNote(
172
+ cursorToolsForActivePrompt(request.tools, activePromptText(request), request.toolChoice),
173
+ request.toolChoice,
174
+ );
175
+ if (cursorToolGuidance) prompts.push(cursorToolGuidance);
176
+ return prompts.map(content => rootBlobCandidate({ role: "system", content }, "system"));
177
+ }
178
+
179
+ function assistantRootText(
180
+ message: Extract<OcxMessage, { role: "assistant" }>,
181
+ includeThinking: boolean,
182
+ ): string {
183
+ if (typeof message.content === "string") return message.content;
184
+ return message.content
185
+ .map(part => (part.type === "text" ? part.text : includeThinking && part.type === "thinking" ? part.thinking : undefined))
186
+ .filter((value): value is string => typeof value === "string" && value.length > 0)
187
+ .join("\n");
188
+ }
189
+
190
+ // Cursor builds the actual model prompt from rootPromptMessagesJson (turns[] is UI/display metadata),
191
+ // so prior history must be replayed here or a ResumeAction has nothing model-visible to continue from.
192
+ // The active user message is excluded because it travels in the action. When the continuation cannot
193
+ // rely on native MCP turn state, tool results stay assistant-role text with a [Tool Result] /
194
+ // [Tool Error] marker so Cursor does not wrap them as `<user_query>` (#1992). Native resume models
195
+ // already carry the paired MCP result on turns[], so that marker is omitted from root replay — Auto
196
+ // few-shot-mimics it as chat text otherwise. Each entry is a SHA-256 blob ID.
197
+ function rootPromptMessages(request: CursorRunRequest, requestScope: CursorBlobRequestScopeToken): {
198
+ ids: Uint8Array[];
199
+ byteLength: number;
200
+ historyMessageStart: number;
201
+ /** Serialized text of the roots that survived pruning, in wire order. */
202
+ serialized: string[];
203
+ } {
204
+ const entries = systemPromptBlobs(request);
205
+ const systemEntryCount = entries.length;
206
+ const messages = request.rawMessages;
207
+ if (!messages?.length) {
208
+ return {
209
+ ids: entries.map(entry => storeCursorBlob(entry.data, requestScope)),
210
+ byteLength: entries.reduce((sum, entry) => sum + entry.byteLength, 0),
211
+ historyMessageStart: 0,
212
+ serialized: entries.map(entry => entry.serialized),
213
+ };
214
+ }
215
+
216
+ const externalModel = isCursorExternalWireModel(request.modelId);
217
+ const echoToolResultInRoot = cursorNeedsExternalToolContinuation(request.modelId);
218
+ const lastRawIsToolResult = messages.at(-1)?.role === "toolResult";
219
+ const activeUserIndex = lastRawIsToolResult ? -1 : lastActionIndex(messages);
220
+
221
+ for (let i = 0; i < messages.length; i++) {
222
+ if (i === activeUserIndex) break;
223
+ const message = messages[i];
224
+ if (!message) continue;
225
+ if (message.role === "user" || message.role === "developer") {
226
+ const text = historyContentText(message).trim();
227
+ // Cursor root replay expects OpenAI-style content parts for historical user messages.
228
+ // A bare string survives blob hydration but external workers reject the completed replay
229
+ // before tokenization (`usedTokens: 0`, then invalid_argument).
230
+ if (text.length > 0) {
231
+ entries.push(rootBlobCandidate({
232
+ role: "user",
233
+ content: [{ type: "text", text }],
234
+ }, "user", { messageIndex: i }));
235
+ }
236
+ } else if (message.role === "assistant") {
237
+ // External Cursor clients do not replay hidden reasoning as assistant-visible prompt text.
238
+ // Native Composer state can preserve it through ThinkingMessage/history structures.
239
+ const text = assistantRootText(message, !externalModel).trim();
240
+ if (text.length > 0) {
241
+ entries.push(rootBlobCandidate(
242
+ { role: "assistant", content: [{ type: "text", text }] },
243
+ "assistant",
244
+ { messageIndex: i },
245
+ ));
246
+ }
247
+ // Assistant tool CALLS are intentionally NOT replayed as visible "[Tool Call]" text here.
248
+ } else if (message.role === "toolResult") {
249
+ // Native resume models already receive the paired MCP result through turns[]. Replaying
250
+ // the same payload as assistant-role "[Tool Result]" / "[tool_result]" text teaches Auto
251
+ // to echo that envelope as chat instead of continuing from the structured result.
252
+ if (!echoToolResultInRoot) continue;
253
+ // #1920: the prefix must reflect the NORMALIZED error state (an empty
254
+ // node_repl result is an error even when the runtime said isError=false).
255
+ const prefix = normalizedToolResult(message, contentToText(message.content)).isError ? "[Tool Error]" : "[Tool Result]";
256
+ const text = `${prefix}\n${toolResultToText(message)}`;
257
+ entries.push(rootBlobCandidate(
258
+ toolResultRootPayload(text),
259
+ "toolResult",
260
+ { messageIndex: i, text },
261
+ ));
262
+ }
263
+ }
264
+
265
+ let selected = entries;
266
+ let historyMessageStart = 0;
267
+ if (externalModel) {
268
+ const systemEntries = entries.slice(0, systemEntryCount);
269
+ const history = entries.slice(systemEntryCount);
270
+ const systemBytes = systemEntries.reduce((sum, entry) => sum + entry.byteLength, 0);
271
+ const historyLimit = Math.max(0, CURSOR_EXTERNAL_ROOT_BLOB_LIMIT - systemEntryCount);
272
+ const historyBudget = Math.max(0, CURSOR_EXTERNAL_ROOT_BYTE_LIMIT - systemBytes);
273
+
274
+ // Retain the active trailing tool-result block when it fits (may truncate text).
275
+ // If even a truncation marker cannot fit the remaining budget, omit it rather than
276
+ // emitting an oversized root blob.
277
+ let activeStart = history.length;
278
+ while (activeStart > 0 && history[activeStart - 1]?.role === "toolResult") activeStart -= 1;
279
+ const active = history
280
+ .slice(activeStart)
281
+ .map(entry => truncateToolResultBlob(entry, historyBudget))
282
+ .filter((entry): entry is RootBlobCandidate => entry !== null);
283
+ let activeBytes = active.reduce((sum, entry) => sum + entry.byteLength, 0);
284
+ while (active.length > 1 && activeBytes > historyBudget) {
285
+ const dropped = active.shift();
286
+ activeBytes -= dropped?.byteLength ?? 0;
287
+ }
288
+ if (active.length === 1 && active[0] && activeBytes > historyBudget) {
289
+ const truncated = truncateToolResultBlob(active[0], historyBudget);
290
+ if (truncated) {
291
+ active[0] = truncated;
292
+ activeBytes = truncated.byteLength;
293
+ } else {
294
+ active.length = 0;
295
+ activeBytes = 0;
296
+ }
297
+ }
298
+
299
+ const prior = history.slice(0, activeStart);
300
+ const keptPrior: RootBlobCandidate[] = [];
301
+ let priorBytes = 0;
302
+ // Take complete turns from the end: a turn starts at a user/developer root entry.
303
+ let i = prior.length - 1;
304
+ while (i >= 0 && keptPrior.length + active.length < historyLimit) {
305
+ let turnStart = i;
306
+ while (turnStart > 0 && prior[turnStart]?.role !== "user") turnStart -= 1;
307
+ const turn = prior.slice(turnStart, i + 1);
308
+ const turnBytes = turn.reduce((sum, entry) => sum + entry.byteLength, 0);
309
+ if (
310
+ keptPrior.length + active.length + turn.length > historyLimit
311
+ || priorBytes + activeBytes + turnBytes > historyBudget
312
+ ) {
313
+ break;
314
+ }
315
+ keptPrior.unshift(...turn);
316
+ priorBytes += turnBytes;
317
+ i = turnStart - 1;
318
+ }
319
+
320
+ const historyEntries = [...keptPrior, ...active];
321
+ // Guard against orphan assistant / toolResult at the start of the retained suffix.
322
+ while (historyEntries[0]?.role === "assistant" || historyEntries[0]?.role === "toolResult") {
323
+ // Never drop the sole active tool-result block.
324
+ if (historyEntries.length <= active.length) break;
325
+ historyEntries.shift();
326
+ }
327
+ selected = [...systemEntries, ...historyEntries];
328
+ const firstKept = historyEntries.find(entry => entry.messageIndex !== undefined);
329
+ historyMessageStart = firstKept?.messageIndex ?? (messages.length);
330
+ }
331
+
332
+ return {
333
+ ids: selected.map(entry => storeCursorBlob(entry.data, requestScope)),
334
+ byteLength: selected.reduce((sum, entry) => sum + entry.byteLength, 0),
335
+ historyMessageStart,
336
+ serialized: selected.map(entry => entry.serialized),
337
+ };
338
+ }
339
+
340
+ function contentText(message: OcxMessage): string {
341
+ if (message.role === "toolResult") return toolResultToText(message);
342
+ if (typeof message.content === "string") return message.content;
343
+ return message.content
344
+ .map(part => {
345
+ if (part.type === "text") return part.text;
346
+ if (part.type === "thinking") return part.thinking;
347
+ if (part.type === "image") return undefined;
348
+ return undefined;
349
+ })
350
+ .filter((value): value is string => typeof value === "string" && value.length > 0)
351
+ .join("\n");
352
+ }
353
+
354
+ function contentToText(content: OcxToolResultMessage["content"]): string {
355
+ if (typeof content === "string") return content;
356
+ return content
357
+ .map(part => {
358
+ if (part.type === "text") return part.text;
359
+ if (part.type === "image") return CURSOR_VISION_IMAGE_HISTORY_MARKER;
360
+ return undefined;
361
+ })
362
+ .filter((value): value is string => typeof value === "string" && value.length > 0)
363
+ .join("\n");
364
+ }
365
+
366
+ /** History serializer. Replayed turns are text-only; never embed image bytes. */
367
+ function historyContentText(message: OcxMessage): string {
368
+ if (message.role === "toolResult" || typeof message.content === "string") return contentText(message);
369
+ return message.content
370
+ .map(part => {
371
+ if (part.type === "text") return part.text;
372
+ if (part.type === "thinking") return part.thinking;
373
+ if (part.type === "image") return CURSOR_VISION_IMAGE_HISTORY_MARKER;
374
+ return undefined;
375
+ })
376
+ .filter((value): value is string => typeof value === "string" && value.length > 0)
377
+ .join("\n");
378
+ }
379
+
380
+ const BASE64_PATTERN = /^[A-Za-z0-9+/]*={0,2}$/;
381
+
382
+ /**
383
+ * Decode a Codex inline image into Cursor wire bytes.
384
+ *
385
+ * `OcxImageContent.imageUrl` is either a `data:` URL or a remote https URL, so this cannot reuse
386
+ * the MCP helper (which takes bare base64 plus a separate mime). It layers strict validation over
387
+ * the shared `parseDataUrl` rather than tightening it, because Anthropic, Google, and Command Code
388
+ * share that parser. `Buffer.from(x, "base64")` accepts many invalid strings silently, so the
389
+ * charset is checked explicitly. Remote URLs are out of scope: `McpImageContent` needs bytes, and
390
+ * fetching here would put network IO inside request construction.
391
+ */
392
+ function decodeInlineImage(imageUrl: string): { bytes: Uint8Array; mimeType: string } | undefined {
393
+ const parsed = parseDataUrl(imageUrl);
394
+ if (!parsed) return undefined;
395
+ const base64 = parsed.base64.trim();
396
+ if (base64.length === 0 || base64.length % 4 !== 0 || !BASE64_PATTERN.test(base64)) return undefined;
397
+ try {
398
+ const bytes = Uint8Array.from(Buffer.from(base64, "base64"));
399
+ if (bytes.length === 0) return undefined;
400
+ return { bytes, mimeType: parsed.mediaType || "application/octet-stream" };
401
+ } catch {
402
+ return undefined;
403
+ }
404
+ }
405
+
406
+ /**
407
+ * A degraded image must never make a step LARGER than the legacy encoding did, or this change
408
+ * could fail admission for a request that previously fit. The old placeholder was
409
+ * `[image input unsupported by Cursor adapter phase 3: <detail>]`; anything we emit in its place
410
+ * is truncated to that budget so the zero-image case is byte-bounded by the pre-change behavior.
411
+ */
412
+ const LEGACY_IMAGE_PLACEHOLDER_BUDGET =
413
+ "[image input unsupported by Cursor adapter phase 3: auto]".length;
414
+
415
+ function imagePlaceholder(reason: string): string {
416
+ const text = `[image omitted: ${reason}]`;
417
+ return text.length <= LEGACY_IMAGE_PLACEHOLDER_BUDGET
418
+ ? text
419
+ : `${text.slice(0, LEGACY_IMAGE_PLACEHOLDER_BUDGET - 1)}]`;
420
+ }
421
+
422
+ type DecodedResultPart =
423
+ | { kind: "text"; text: string }
424
+ | { kind: "image"; bytes: Uint8Array; mimeType: string }
425
+ | { kind: "undecodable" };
426
+
427
+ type NormalizedToolResult = { text: string; isError: boolean };
428
+
429
+ /**
430
+ * Decode a tool result's parts ONCE. `toolCallStep` may re-serialize a step several times while
431
+ * shrinking it to fit blob admission, and decoding base64 on every attempt made that loop
432
+ * quadratic (an audit measured ~3s for 100 images).
433
+ */
434
+ function decodeResultParts(message: OcxToolResultMessage): DecodedResultPart[] | undefined {
435
+ const content = message.content;
436
+ if (typeof content === "string") return undefined;
437
+ return content.map((part): DecodedResultPart => {
438
+ if (part.type === "text") return { kind: "text", text: part.text };
439
+ const decoded = decodeInlineImage(part.imageUrl);
440
+ return decoded ? { kind: "image", ...decoded } : { kind: "undecodable" };
441
+ });
442
+ }
443
+
444
+ function countImages(parts: DecodedResultPart[] | undefined): number {
445
+ return parts ? parts.filter(p => p.kind === "image").length : 0;
446
+ }
447
+
448
+ /**
449
+ * Build the wire content items for a tool result, preserving part order.
450
+ *
451
+ * Images become real `McpImageContent` — the Cursor schema has an image case on
452
+ * `McpToolResultContentItem`, and `native-exec-mcp.ts` already uses it for MCP-invoked tools.
453
+ * Flattening them to placeholder text blinded every screenshot-returning tool (Computer Use,
454
+ * browser QA) that Codex routes through this path.
455
+ */
456
+ function toolResultContentItems(
457
+ message: OcxToolResultMessage,
458
+ decoded?: DecodedResultPart[],
459
+ maxImages = Number.POSITIVE_INFINITY,
460
+ normalizedText?: NormalizedToolResult,
461
+ ) {
462
+ const parts = decoded ?? decodeResultParts(message);
463
+ const textItem = (text: string) => [create(McpToolResultContentItemSchema, {
464
+ content: { case: "text" as const, value: create(McpTextContentSchema, { text }) },
465
+ })];
466
+ if (!parts) {
467
+ const normalized = normalizedText
468
+ ?? normalizedToolResult(message, typeof message.content === "string" ? message.content : "");
469
+ return textItem(normalized.text);
470
+ }
471
+ const normalized = normalizedText ?? normalizedDecodedTextResult(message, parts);
472
+ if (normalized) {
473
+ // #1920/#1866: empty or failure-state Computer Use / node_repl results are
474
+ // normalized before they reach the native wire. Pure-text part arrays use
475
+ // the same newline-joined representation this serializer already emitted;
476
+ // image-bearing and undecodable results stay on the lossless part path.
477
+ return textItem(normalized.text);
478
+ }
479
+ // Images are dropped OLDEST first when the step must shrink: the most recent screenshot is the
480
+ // one the model is reasoning about, so it is the last to go.
481
+ const totalImages = countImages(parts);
482
+ const allowed = Math.max(0, Math.min(totalImages, maxImages));
483
+ let seen = 0;
484
+ // Consecutive text runs are newline-joined into ONE item, exactly as the legacy encoding did.
485
+ // Emitting one protobuf item per part adds per-item framing, which was enough to push a
486
+ // previously admissible step past the blob ceiling (round-3 audit: 1020 -> 1025 bytes at a
487
+ // 1024 limit). A result with no images must serialize identically to before this feature.
488
+ const items: ReturnType<typeof create<typeof McpToolResultContentItemSchema>>[] = [];
489
+ let pendingText: string[] = [];
490
+ const flushText = () => {
491
+ if (pendingText.length === 0) return;
492
+ const text = pendingText.join("\n");
493
+ pendingText = [];
494
+ items.push(create(McpToolResultContentItemSchema, {
495
+ content: { case: "text" as const, value: create(McpTextContentSchema, { text }) },
496
+ }));
497
+ };
498
+ for (const part of parts) {
499
+ if (part.kind === "text") {
500
+ pendingText.push(part.text);
501
+ continue;
502
+ }
503
+ if (part.kind === "undecodable") {
504
+ pendingText.push(imagePlaceholder("no inline data"));
505
+ continue;
506
+ }
507
+ seen++;
508
+ if (seen <= totalImages - allowed) {
509
+ pendingText.push(imagePlaceholder(`${part.bytes.byteLength}B over step limit`));
510
+ continue;
511
+ }
512
+ flushText();
513
+ items.push(create(McpToolResultContentItemSchema, {
514
+ content: { case: "image" as const, value: create(McpImageContentSchema, {
515
+ data: part.bytes,
516
+ mimeType: part.mimeType,
517
+ }) },
518
+ }));
519
+ }
520
+ flushText();
521
+ if (items.length === 0) {
522
+ items.push(create(McpToolResultContentItemSchema, {
523
+ content: { case: "text" as const, value: create(McpTextContentSchema, { text: "" }) },
524
+ }));
525
+ }
526
+ return items;
527
+ }
528
+
529
+ function toolResultToText(message: OcxToolResultMessage): string {
530
+ const normalized = normalizedToolResult(message, contentToText(message.content));
531
+ return [
532
+ "[tool_result]",
533
+ `call_id: ${message.toolCallId}`,
534
+ `name: ${namespacedToolName(message.toolNamespace, message.toolName)}`,
535
+ `is_error: ${normalized.isError}`,
536
+ "output:",
537
+ normalized.text,
538
+ ].join("\n");
539
+ }
540
+
541
+ /**
542
+ * Shared #1920 normalization entry: pure-text results only. Image-bearing or
543
+ * encrypted results pass through untouched (their content is not plain text).
544
+ */
545
+ function normalizedToolResult(message: OcxToolResultMessage, text: string): NormalizedToolResult {
546
+ if (message.containsEncryptedContent) return { text, isError: message.isError };
547
+ return normalizeCursorToolResultText(text, {
548
+ toolName: message.toolName,
549
+ toolNamespace: message.toolNamespace,
550
+ isError: message.isError,
551
+ });
552
+ }
553
+
554
+ /**
555
+ * A content-part result is plain text only when every decoded part is text (the empty array is the
556
+ * empty text result). Join it exactly as toolResultContentItems already did, then share the string
557
+ * normalization contract. Any image or undecodable part keeps the existing part-preserving path.
558
+ */
559
+ function normalizedDecodedTextResult(
560
+ message: OcxToolResultMessage,
561
+ parts: DecodedResultPart[],
562
+ ): NormalizedToolResult | undefined {
563
+ if (parts.some(part => part.kind !== "text")) return undefined;
564
+ return normalizedToolResult(message, parts.map(part => part.kind === "text" ? part.text : "").join("\n"));
565
+ }
566
+
567
+ function argBytes(value: unknown): Uint8Array {
568
+ try {
569
+ return toBinary(ValueSchema, fromJson(ValueSchema, value as JsonValue));
570
+ } catch {
571
+ return encoder.encode(JSON.stringify(value));
572
+ }
573
+ }
574
+
575
+ function toolCallStep(
576
+ part: Extract<OcxAssistantContentPart, { type: "toolCall" }>,
577
+ requestScope: CursorBlobRequestScopeToken,
578
+ result?: OcxToolResultMessage,
579
+ ): Uint8Array {
580
+ const args: Record<string, Uint8Array> = {};
581
+ for (const [key, value] of Object.entries(part.arguments ?? {})) args[key] = argBytes(value);
582
+ const toolName = namespacedToolName(part.namespace, part.name);
583
+ const decodedResult = result ? decodeResultParts(result) : undefined;
584
+ const serialize = (maxImages: number): Uint8Array => toBinary(ConversationStepSchema, create(ConversationStepSchema, {
585
+ message: {
586
+ case: "toolCall",
587
+ value: create(ToolCallSchema, {
588
+ tool: {
589
+ case: "mcpToolCall",
590
+ value: create(McpToolCallSchema, {
591
+ args: create(McpArgsSchema, {
592
+ name: toolName,
593
+ toolName,
594
+ toolCallId: part.id,
595
+ providerIdentifier: OCX_RESPONSES_TOOL_PROVIDER,
596
+ args,
597
+ }),
598
+ ...(result ? { result: toolResultPart(result, decodedResult, maxImages) } : {}),
599
+ }),
600
+ },
601
+ }),
602
+ },
603
+ }));
604
+
605
+ // A step is stored as ONE blob, so its images share an entry with the call's arguments, text,
606
+ // mime strings, and protobuf framing. A byte budget over decoded images alone cannot bound that
607
+ // (an audit reproduced a 448-byte-argument call whose 460-byte image pushed a previously
608
+ // admitted step past the ceiling). Measure the real serialized size instead, then drop images —
609
+ // oldest first, so the most recent screenshot survives — until the step fits.
610
+ const limit = cursorBlobMaxEntryBytes();
611
+ const imageCount = countImages(decodedResult);
612
+ let encoded = serialize(imageCount);
613
+ for (let allowed = imageCount - 1; allowed >= 0 && encoded.byteLength > limit; allowed--) {
614
+ encoded = serialize(allowed);
615
+ }
616
+ return storeCursorBlob(encoded, requestScope);
617
+ }
618
+
619
+ function toolResultPart(message: OcxToolResultMessage, decoded?: DecodedResultPart[], maxImages?: number) {
620
+ const parts = decoded ?? decodeResultParts(message);
621
+ const normalized = parts
622
+ ? normalizedDecodedTextResult(message, parts)
623
+ : normalizedToolResult(message, typeof message.content === "string" ? message.content : "");
624
+ return create(McpToolResultSchema, {
625
+ result: {
626
+ case: "success",
627
+ value: create(McpSuccessSchema, {
628
+ isError: normalized?.isError ?? message.isError,
629
+ content: toolResultContentItems(message, parts, maxImages, normalized),
630
+ }),
631
+ },
632
+ });
633
+ }
634
+
635
+ function assistantStep(part: OcxAssistantContentPart, requestScope: CursorBlobRequestScopeToken): Uint8Array | undefined {
636
+ if (part.type === "toolCall") return toolCallStep(part, requestScope);
637
+ if (part.type === "thinking") {
638
+ return storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
639
+ message: {
640
+ case: "thinkingMessage",
641
+ value: create(ThinkingMessageSchema, { text: part.thinking }),
642
+ },
643
+ })), requestScope);
644
+ }
645
+ if (part.text.length === 0) return undefined;
646
+ return storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
647
+ message: {
648
+ case: "assistantMessage",
649
+ value: create(AssistantMessageSchema, { text: part.text }),
650
+ },
651
+ })), requestScope);
652
+ }
653
+
654
+ function lastActionIndex(messages: readonly OcxMessage[] | undefined): number {
655
+ if (!messages) return -1;
656
+ for (let i = messages.length - 1; i >= 0; i--) {
657
+ const role = messages[i]?.role;
658
+ if (role === "user" || role === "developer") return i;
659
+ if (role === "toolResult") continue;
660
+ }
661
+ return -1;
662
+ }
663
+
664
+ function conversationTurns(
665
+ request: CursorRunRequest,
666
+ requestScope: CursorBlobRequestScopeToken,
667
+ historyMessageStart = 0,
668
+ ): Uint8Array[] {
669
+ const messages = request.rawMessages;
670
+ if (!messages?.length) return [];
671
+ const end = lastActionIndex(messages);
672
+ const externalModel = isCursorExternalWireModel(request.modelId);
673
+ const historyEnd = messages.at(-1)?.role === "toolResult" ? messages.length : Math.max(0, end);
674
+ const start = externalModel ? Math.max(0, historyMessageStart) : 0;
675
+ const turns: Uint8Array[] = [];
676
+ let current: { userMessage: Uint8Array; steps: Uint8Array[] } | undefined;
677
+ const pendingToolCalls = new Map<string, Extract<OcxAssistantContentPart, { type: "toolCall" }>>();
678
+ const flush = () => {
679
+ if (!current) return;
680
+ for (const part of pendingToolCalls.values()) current.steps.push(toolCallStep(part, requestScope));
681
+ turns.push(storeCursorBlob(toBinary(ConversationTurnStructureSchema, create(ConversationTurnStructureSchema, {
682
+ turn: {
683
+ case: "agentConversationTurn",
684
+ value: create(AgentConversationTurnStructureSchema, current),
685
+ },
686
+ })), requestScope));
687
+ current = undefined;
688
+ pendingToolCalls.clear();
689
+ };
690
+
691
+ for (const message of messages.slice(start, historyEnd)) {
692
+ if (message.role === "assistant") {
693
+ if (!current) continue;
694
+ for (const part of message.content) {
695
+ if (externalModel) {
696
+ // Working external-model clients replay only assistant text. Native mcpToolCall and
697
+ // ThinkingMessage structures are Composer state and cause external workers to hydrate
698
+ // the blobs, reach stepCompleted, then reject the turn with invalid_argument.
699
+ if (part.type === "text" && part.text.length > 0) {
700
+ current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
701
+ message: {
702
+ case: "assistantMessage",
703
+ value: create(AssistantMessageSchema, { text: part.text }),
704
+ },
705
+ })), requestScope));
706
+ }
707
+ continue;
708
+ }
709
+ if (part.type === "toolCall") {
710
+ pendingToolCalls.set(part.id, part);
711
+ continue;
712
+ }
713
+ const step = assistantStep(part, requestScope);
714
+ if (step) current.steps.push(step);
715
+ }
716
+ continue;
717
+ }
718
+ if (message.role === "toolResult") {
719
+ if (!current) continue;
720
+ if (externalModel) {
721
+ // #1920/#1866: this external-replay site bypasses toolResultToText, so it
722
+ // must consume the normalizer directly — cursor/grok-4.6 is the exact
723
+ // reported repro path for empty Computer Use results.
724
+ const normalized = normalizedToolResult(message, contentToText(message.content));
725
+ const prefix = normalized.isError ? "[Tool Error]" : "[Tool Result]";
726
+ current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
727
+ message: {
728
+ case: "assistantMessage",
729
+ value: create(AssistantMessageSchema, { text: `${prefix}\n${normalized.text}` }),
730
+ },
731
+ })), requestScope));
732
+ continue;
733
+ }
734
+ const priorCall = pendingToolCalls.get(message.toolCallId);
735
+ if (priorCall) {
736
+ current.steps.push(toolCallStep(priorCall, requestScope, message));
737
+ pendingToolCalls.delete(message.toolCallId);
738
+ } else {
739
+ current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
740
+ message: {
741
+ case: "assistantMessage",
742
+ value: create(AssistantMessageSchema, { text: toolResultToText(message) }),
743
+ },
744
+ })), requestScope));
745
+ }
746
+ continue;
747
+ }
748
+ flush();
749
+ current = {
750
+ userMessage: storeCursorBlob(toBinary(UserMessageSchema, create(UserMessageSchema, {
751
+ text: historyContentText(message),
752
+ messageId: crypto.randomUUID(),
753
+ selectedContext: buildSelectedContext([], requestScope),
754
+ mode: 1,
755
+ })), requestScope),
756
+ steps: [],
757
+ };
758
+ }
759
+ flush();
760
+ return turns;
761
+ }
762
+
763
+ export function activePromptText(request: CursorRunRequest): string {
764
+ const last = request.messages.at(-1);
765
+ if (last?.role === "user" || last?.role === "developer") return last.content;
766
+ for (let i = (request.rawMessages?.length ?? 0) - 1; i >= 0; i--) {
767
+ const message = request.rawMessages?.[i];
768
+ if (message?.role === "user" || message?.role === "developer") {
769
+ const text = contentText(message);
770
+ if (text.trim().length > 0) return text;
771
+ }
772
+ }
773
+ return last?.role === "tool" ? last.content : "";
774
+ }
775
+
776
+ /**
777
+ * The model-visible text of one finalized tool definition. The schema travels as
778
+ * packed protobuf bytes, so it is decoded back to JSON to be counted the way the
779
+ * model reads it.
780
+ */
781
+ function modelVisibleToolText(definition: McpToolDefinition): string {
782
+ let inputSchema: unknown;
783
+ try {
784
+ inputSchema = toJson(ValueSchema, fromBinary(ValueSchema, definition.inputSchema));
785
+ } catch {
786
+ inputSchema = undefined;
787
+ }
788
+ return JSON.stringify({
789
+ name: definition.toolName || definition.name,
790
+ description: definition.description,
791
+ ...(inputSchema !== undefined ? { inputSchema } : {}),
792
+ });
793
+ }
794
+
795
+ export interface PreparedCursorRunRequest {
796
+ bytes: Uint8Array;
797
+ blobRequestScope: CursorBlobRequestScopeToken;
798
+ /** Only present when the caller asked for it; see prepareCursorRunRequest(). */
799
+ estimatedInputTokens?: number;
800
+ }
801
+
802
+ /**
803
+ * Build the wire payload once, and optionally derive a token estimate from the very
804
+ * same roots, action text, and tool definitions that produced it.
805
+ *
806
+ * Cursor only reports absolute context size in checkpoint frames, which live in a
807
+ * process-local map — so after a restart a turn with no checkpoint reports
808
+ * inputTokens=0 and Codex sees an almost-empty context (#373). The estimate fills
809
+ * that gap. Deriving it here, rather than from the original request, is what keeps
810
+ * it honest: history the pruner dropped and tools the filter removed are already
811
+ * gone by this point.
812
+ */
813
+ function buildPreparedCursorRunRequest(
814
+ request: CursorRunRequest,
815
+ requestScope: CursorBlobRequestScopeToken,
816
+ options?: { estimateInputTokens?: boolean },
817
+ ): PreparedCursorRunRequest {
818
+ const rawText = activePromptText(request);
819
+ const lastRole = request.messages.at(-1)?.role;
820
+ const text = lastRole === "user" || lastRole === "developer"
821
+ ? appendCursorGenericToolUseHint(request.tools, rawText)
822
+ : rawText;
823
+ const lastRawIsToolResult = request.rawMessages?.at(-1)?.role === "toolResult";
824
+ const selectedImages = request.selectedImages ?? [];
825
+ // Native models resume the remembered Cursor conversation. External wire
826
+ // models continue as userMessageAction so history-blob tool results stay
827
+ // visible without a ResumeAction. Some native composer ids are also routed
828
+ // through the external continuation path (cursorNeedsExternalToolContinuation)
829
+ // because a bare resumeAction makes them continue exploring with native tools
830
+ // instead of answering (observed on composer-2.5; see discovery.ts).
831
+ const externalToolContinuation = lastRawIsToolResult && cursorNeedsExternalToolContinuation(request.modelId);
832
+ // Image-only active turns (including soft-omitted images) stay userMessageAction.
833
+ const actionCase = (
834
+ externalToolContinuation
835
+ || (!lastRawIsToolResult && (text.trim().length > 0 || selectedImages.length > 0))
836
+ )
837
+ ? "userMessageAction"
838
+ : "resumeAction";
839
+ const actionText = externalToolContinuation
840
+ ? CURSOR_EXTERNAL_TOOL_CONTINUATION_TEXT
841
+ : text;
842
+ const action = create(ConversationActionSchema, {
843
+ action: actionCase === "userMessageAction"
844
+ ? {
845
+ case: "userMessageAction",
846
+ value: create(UserMessageActionSchema, {
847
+ userMessage: create(UserMessageSchema, {
848
+ text: actionText,
849
+ messageId: crypto.randomUUID(),
850
+ selectedContext: buildSelectedContext(selectedImages, requestScope),
851
+ // OmniRoute / cursor-agent always send mode=1 on UserMessage.
852
+ mode: 1,
853
+ }),
854
+ requestContext: buildRequestContext(),
855
+ }),
856
+ }
857
+ : {
858
+ case: "resumeAction",
859
+ value: create(ResumeActionSchema, {
860
+ requestContext: buildRequestContext(),
861
+ }),
862
+ },
863
+ });
864
+ let continuationMode: "full-replay" | "checkpoint" = "full-replay";
865
+ let checkpointInvalidationReason = request.checkpointInvalidationReason;
866
+ let conversationState: ConversationStateStructure | undefined;
867
+ let rootPromptMessagesState: ReturnType<typeof rootPromptMessages> | undefined;
868
+ if (request.checkpointBytes && request.checkpointBytes.byteLength > 0) {
869
+ try {
870
+ conversationState = fromBinary(ConversationStateStructureSchema, request.checkpointBytes);
871
+ continuationMode = "checkpoint";
872
+ const suffixStart = request.checkpointSuffixStart;
873
+ if (
874
+ typeof suffixStart === "number"
875
+ && Number.isSafeInteger(suffixStart)
876
+ && suffixStart >= 0
877
+ && request.rawMessages
878
+ && suffixStart < request.rawMessages.length
879
+ ) {
880
+ const suffixRequest: CursorRunRequest = {
881
+ ...request,
882
+ system: [],
883
+ rawMessages: request.rawMessages.slice(suffixStart),
884
+ };
885
+ const suffixRoots = rootPromptMessages(suffixRequest, requestScope);
886
+ const suffixTurns = conversationTurns(suffixRequest, requestScope, suffixRoots.historyMessageStart);
887
+ const suffixSystemCount = systemPromptBlobs(suffixRequest).length;
888
+ const suffixHistoryIds = suffixRoots.ids.slice(suffixSystemCount);
889
+ const suffixHistorySerialized = suffixRoots.serialized.slice(suffixSystemCount);
890
+ conversationState = create(ConversationStateStructureSchema, {
891
+ ...conversationState,
892
+ rootPromptMessagesJson: [
893
+ ...conversationState.rootPromptMessagesJson,
894
+ ...suffixHistoryIds,
895
+ ],
896
+ turns: [
897
+ ...conversationState.turns,
898
+ ...suffixTurns,
899
+ ],
900
+ });
901
+ rootPromptMessagesState = {
902
+ ids: suffixHistoryIds,
903
+ byteLength: suffixRoots.byteLength,
904
+ historyMessageStart: suffixRoots.historyMessageStart,
905
+ serialized: suffixHistorySerialized,
906
+ };
907
+ }
908
+ } catch {
909
+ checkpointInvalidationReason = "decode_failed";
910
+ }
911
+ }
912
+ if (!conversationState) {
913
+ rootPromptMessagesState = rootPromptMessages(request, requestScope);
914
+ conversationState = create(ConversationStateStructureSchema, {
915
+ rootPromptMessagesJson: rootPromptMessagesState.ids,
916
+ turns: conversationTurns(request, requestScope, rootPromptMessagesState.historyMessageStart),
917
+ todos: [],
918
+ pendingToolCalls: [],
919
+ previousWorkspaceUris: [],
920
+ fileStates: {},
921
+ fileStatesV2: {},
922
+ summaryArchives: [],
923
+ turnTimings: [],
924
+ subagentStates: {},
925
+ readPaths: [],
926
+ });
927
+ }
928
+ // Hoisted out of the mcp_tools spread below so the estimate can read the same
929
+ // filtered definitions the wire carries. Both helpers are pure.
930
+ const visibleTools = cursorToolsForActivePrompt(request.tools, rawText, request.toolChoice);
931
+ const mcpToolDefs = buildCursorToolDefinitions(visibleTools, request.toolChoice);
932
+ debugProviderDiagnostic("cursor", "run-request", {
933
+ wireModel: request.modelId,
934
+ action: actionCase,
935
+ conversationId: request.conversationId,
936
+ turnType: lastRawIsToolResult ? "tool-continuation" : "initial",
937
+ externalModel: isCursorExternalWireModel(request.modelId),
938
+ rawMessages: request.rawMessages?.length ?? 0,
939
+ continuationMode,
940
+ checkpointPresent: continuationMode === "checkpoint",
941
+ checkpointBytes: continuationMode === "checkpoint" ? request.checkpointBytes?.byteLength : undefined,
942
+ checkpointInvalidationReason,
943
+ rootBlobs: conversationState.rootPromptMessagesJson.length,
944
+ rootBytes: rootPromptMessagesState?.byteLength ?? 0,
945
+ turnBlobs: conversationState.turns.length,
946
+ tools: request.tools?.length ?? 0,
947
+ });
948
+
949
+ const requestedModelParameters = [
950
+ ...(request.requestedModelParameters ?? []),
951
+ ...(request.routingLevel ? [{ id: CURSOR_ROUTING_LEVEL_PARAMETER_ID, value: request.routingLevel }] : []),
952
+ ];
953
+ const hasExplicitModelParameters = (request.requestedModelParameters?.length ?? 0) > 0;
954
+ const runRequest = create(AgentRunRequestSchema, {
955
+ conversationId: request.conversationId,
956
+ conversationState,
957
+ action,
958
+ // Explicit model-picker parameters follow current Cursor clients and use requested_model alone.
959
+ // Keep legacy model_details for flat model ids and the already-live Router path; sending both for
960
+ // a parameterized external model can resolve conflicting selections and end in invalid_argument.
961
+ ...(!hasExplicitModelParameters ? {
962
+ modelDetails: create(ModelDetailsSchema, {
963
+ modelId: request.modelId,
964
+ displayModelId: request.modelId,
965
+ displayName: request.modelId,
966
+ displayNameShort: request.modelId,
967
+ aliases: [],
968
+ }),
969
+ } : {}),
970
+ ...(requestedModelParameters.length > 0 ? {
971
+ requestedModel: create(RequestedModelSchema, {
972
+ modelId: request.modelId,
973
+ maxMode: false,
974
+ parameters: requestedModelParameters.map(parameter =>
975
+ create(RequestedModel_ModelParameterbytesSchema, parameter)),
976
+ }),
977
+ } : {}),
978
+ // Mirror the client (Responses) tool definitions into the top-level AgentRunRequest.mcp_tools
979
+ // channel. Advertising them ONLY via native-exec `requestContextArgs` (RequestContext.tools) is
980
+ // insufficient: cursor models report those tools as unavailable and fall back to native tools.
981
+ // Populating mcp_tools registers them into the model's callable catalog (verified live: the
982
+ // model actually calls the injected tool on gpt-5.6-luna and claude-4.5-sonnet). Phase 42 tried
983
+ // this but assigned the field with the wrong shape and crashed Cursor's binary parser ("illegal
984
+ // tag"); the correct `McpTools` wrapper is wire-compatible (verified — no parse crash on either
985
+ // model family). See devlog/260711_cursor_browser_bridge/004.
986
+ //
987
+ // Use the SAME `cursorToolsForActivePrompt`-filtered visible set that RequestContext.tools and
988
+ // the event-state `clientToolNames` use (live-transport.ts). Advertising the raw `request.tools`
989
+ // here would let mcp_tools expose a tool that the event state does not recognize for a generic
990
+ // tool-count prompt, so a call to it would be rejected as an unknown Responses tool.
991
+ ...(mcpToolDefs.length > 0 ? { mcpTools: create(McpToolsSchema, { mcpTools: mcpToolDefs }) } : {}),
992
+ });
993
+
994
+ const message = create(AgentClientMessageSchema, {
995
+ message: { case: "runRequest", value: runRequest },
996
+ });
997
+ const bytes = toBinary(AgentClientMessageSchema, message);
998
+ if (!options?.estimateInputTokens) return { bytes, blobRequestScope: requestScope };
999
+
1000
+ // Same instances that produced `bytes`, so the estimate cannot count history or
1001
+ // tools the payload dropped — the defect that blocked PR #376.
1002
+ const modelVisibleParts = [
1003
+ ...(rootPromptMessagesState?.serialized ?? []),
1004
+ ...(actionCase === "userMessageAction" ? [actionText] : []),
1005
+ ...mcpToolDefs.map(modelVisibleToolText),
1006
+ ];
1007
+ return {
1008
+ bytes,
1009
+ blobRequestScope: requestScope,
1010
+ estimatedInputTokens: estimateTokens(modelVisibleParts.join("\n"), request.modelId),
1011
+ };
1012
+ }
1013
+
1014
+ export function prepareCursorRunRequest(
1015
+ request: CursorRunRequest,
1016
+ options?: { estimateInputTokens?: boolean },
1017
+ ): PreparedCursorRunRequest {
1018
+ const requestScope = createCursorBlobRequestScope();
1019
+ try {
1020
+ const prepared = buildPreparedCursorRunRequest(request, requestScope, options);
1021
+ sealCursorBlobRequestScope(requestScope);
1022
+ return prepared;
1023
+ } catch (error) {
1024
+ releaseCursorBlobRequestScope(requestScope);
1025
+ throw error;
1026
+ }
1027
+ }
1028
+
1029
+ /** Back-compat wrapper: callers that only need the wire bytes. */
1030
+ export function encodeCursorRunRequest(request: CursorRunRequest): Uint8Array {
1031
+ return prepareCursorRunRequest(request).bytes;
1032
+ }