@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,681 @@
1
+ import { createHash } from "node:crypto";
2
+ import type { OcxConfig, OcxContentPart, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxTextContent } from "../types";
3
+ import { modelInList } from "../types";
4
+ import { modelRecordValue } from "../reasoning-effort";
5
+ import type { VisionReasoningEffort } from "../reasoning-effort";
6
+ import { describeImage, type DescribeOutcome, type VisionSettings } from "./describe";
7
+ import { describeImageAnthropic } from "./anthropic-describe";
8
+ import { describeImageRouted } from "./routed-describe";
9
+ import { modelAcceptsImageInput } from "./eligibility";
10
+ import { normalizeVisionReasoningForModel } from "./reasoning";
11
+ import type { CodexAuthContext } from "../codex/auth-context";
12
+ import { resolveSidecarAuth } from "../sidecar/auth";
13
+ import type { ResolvedOpenAiForwardSidecar } from "../providers/openai-sidecar";
14
+ import type { SidecarOutcomeRecorder } from "../web-search/executor";
15
+ import { enforceAppOwnedMemoryBudget } from "../lib/app-owned-memory";
16
+ import type { TranslatorBudget } from "../lib/translator-budget";
17
+ import {
18
+ DEFAULT_VISION_TIMEOUT_MS,
19
+ MAX_VISION_TIMEOUT_MS,
20
+ MIN_VISION_TIMEOUT_MS,
21
+ } from "./timeout-bounds";
22
+
23
+ export { describeImage } from "./describe";
24
+
25
+ /**
26
+ * True when the model is explicitly known to be text-only — either listed in
27
+ * `noVisionModels` or declared with `modelInputModalities` that exclude "image".
28
+ * Returns false for unknown models (no evidence either way) so they fall through
29
+ * to native image passthrough, which is the safe default for an unclassified model.
30
+ */
31
+ export function isModelTextOnly(
32
+ provider: OcxProviderConfig,
33
+ modelId: string,
34
+ ): boolean {
35
+ if (modelInList(provider.noVisionModels, modelId)) return true;
36
+ const modalities = modelRecordValue(provider.modelInputModalities, modelId);
37
+ if (Array.isArray(modalities) && modalities.length > 0 && !modalities.includes("image")) return true;
38
+ return false;
39
+ }
40
+ export { describeImageAnthropic, parseAnthropicVisionSSE } from "./anthropic-describe";
41
+ export {
42
+ BASELINE_VISION_MODELS,
43
+ isVisionEligibleModel,
44
+ isVisionSidecarConsumer,
45
+ modelAcceptsImageInput,
46
+ visionBackendForCandidate,
47
+ visionEligibleModelOptions,
48
+ } from "./eligibility";
49
+ export type { VisionCandidateModel, VisionModelOption, VisionSidecarBackend } from "./eligibility";
50
+ export {
51
+ DEFAULT_VISION_TIMEOUT_MS,
52
+ MAX_VISION_TIMEOUT_MS,
53
+ MIN_VISION_TIMEOUT_MS,
54
+ };
55
+
56
+ const DEFAULT_VISION_MODEL = "gpt-5.4-mini";
57
+ const DEFAULT_ANTHROPIC_VISION_MODEL = "claude-sonnet-5";
58
+ const DEFAULT_REASONING: VisionReasoningEffort = "low";
59
+ export const DEFAULT_MAX_DESCRIPTIONS_PER_TURN = 8;
60
+ const DESCRIPTION_CACHE_MAX_ENTRIES = 256;
61
+ export const VISION_DESCRIPTION_CACHE_MAX_BYTES = 1024 * 1024;
62
+ const descriptionEncoder = new TextEncoder();
63
+ /** Max images described in parallel — keeps first-token latency bounded without flooding the backend. */
64
+ const VISION_CONCURRENCY = 3;
65
+ /** Per-image description hard cap (chars) so multi-image turns can't blow the main model's context. */
66
+ const DESC_MAX_CHARS = 2000;
67
+ /** User-text context passed to the describer, capped. */
68
+ const CONTEXT_MAX_CHARS = 800;
69
+
70
+ export interface VisionDescriptionCache {
71
+ get(key: string): string | undefined;
72
+ set(key: string, value: string): void;
73
+ clear(): void;
74
+ snapshot?(): { count: number; bytes: number; oldestAt: number | null };
75
+ evictOldest?(): number;
76
+ }
77
+
78
+ class BoundedLruDescriptionCache implements VisionDescriptionCache {
79
+ private readonly entries = new Map<string, { value: string; sizeBytes: number; storedAt: number }>();
80
+ private bytes = 0;
81
+
82
+ constructor(private readonly maxEntries: number, private readonly maxBytes: number) {}
83
+
84
+ get(key: string): string | undefined {
85
+ const entry = this.entries.get(key);
86
+ if (entry === undefined) return undefined;
87
+ this.entries.delete(key);
88
+ entry.storedAt = Date.now();
89
+ this.entries.set(key, entry);
90
+ return entry.value;
91
+ }
92
+
93
+ set(key: string, value: string): void {
94
+ const existing = this.entries.get(key);
95
+ if (existing) {
96
+ this.entries.delete(key);
97
+ this.bytes -= existing.sizeBytes;
98
+ }
99
+ const sizeBytes = descriptionEncoder.encode(key).byteLength + descriptionEncoder.encode(value).byteLength;
100
+ if (sizeBytes > this.maxBytes || this.maxEntries <= 0) return;
101
+ while (this.entries.size + 1 > this.maxEntries || this.bytes + sizeBytes > this.maxBytes) {
102
+ if (this.evictOldest() === 0) return;
103
+ }
104
+ this.entries.set(key, { value, sizeBytes, storedAt: Date.now() });
105
+ this.bytes += sizeBytes;
106
+ }
107
+
108
+ clear(): void {
109
+ this.entries.clear();
110
+ this.bytes = 0;
111
+ }
112
+
113
+ snapshot(): { count: number; bytes: number; oldestAt: number | null } {
114
+ return {
115
+ count: this.entries.size,
116
+ bytes: this.bytes,
117
+ oldestAt: this.entries.values().next().value?.storedAt ?? null,
118
+ };
119
+ }
120
+
121
+ evictOldest(): number {
122
+ const oldest = this.entries.keys().next().value;
123
+ if (oldest === undefined) return 0;
124
+ const entry = this.entries.get(oldest);
125
+ if (!entry) return 0;
126
+ this.entries.delete(oldest);
127
+ this.bytes -= entry.sizeBytes;
128
+ return entry.sizeBytes;
129
+ }
130
+ }
131
+
132
+ let descriptionCacheLimits = {
133
+ maxEntries: DESCRIPTION_CACHE_MAX_ENTRIES,
134
+ maxBytes: VISION_DESCRIPTION_CACHE_MAX_BYTES,
135
+ };
136
+
137
+ function defaultDescriptionCache(): VisionDescriptionCache {
138
+ return new BoundedLruDescriptionCache(descriptionCacheLimits.maxEntries, descriptionCacheLimits.maxBytes);
139
+ }
140
+
141
+ let descriptionCache: VisionDescriptionCache = defaultDescriptionCache();
142
+
143
+ /** Replace the process cache (primarily for deterministic tests). Passing undefined restores the default LRU. */
144
+ export function setVisionDescriptionCache(cache?: VisionDescriptionCache): void {
145
+ descriptionCache = cache ?? defaultDescriptionCache();
146
+ }
147
+
148
+ export function resetVisionDescriptionCache(): void {
149
+ descriptionCache.clear();
150
+ }
151
+
152
+ export function setVisionDescriptionCacheLimitsForTests(
153
+ limits?: { maxEntries?: number; maxBytes?: number },
154
+ ): void {
155
+ descriptionCacheLimits = limits
156
+ ? { maxEntries: limits.maxEntries ?? DESCRIPTION_CACHE_MAX_ENTRIES, maxBytes: limits.maxBytes ?? VISION_DESCRIPTION_CACHE_MAX_BYTES }
157
+ : { maxEntries: DESCRIPTION_CACHE_MAX_ENTRIES, maxBytes: VISION_DESCRIPTION_CACHE_MAX_BYTES };
158
+ descriptionCache = defaultDescriptionCache();
159
+ }
160
+
161
+ export function visionDescriptionRetainedStoreSnapshot(): {
162
+ count: number;
163
+ bytes: number;
164
+ evictableBytes: number;
165
+ pinnedBytes: number;
166
+ oldestAt: number | null;
167
+ } {
168
+ const snapshot = descriptionCache.snapshot?.();
169
+ if (!snapshot) return { count: 0, bytes: 0, evictableBytes: 0, pinnedBytes: 0, oldestAt: null };
170
+ return { ...snapshot, evictableBytes: snapshot.bytes, pinnedBytes: 0 };
171
+ }
172
+
173
+ export function evictOldestVisionDescriptionForBudget(): number {
174
+ return descriptionCache.evictOldest?.() ?? 0;
175
+ }
176
+
177
+ /** Runtime config is permissive: zero is intentional; malformed values fall back to the bounded default. */
178
+ export function resolveMaxDescriptionsPerTurn(value: unknown): number {
179
+ if (value === 0) return 0;
180
+ return typeof value === "number" && Number.isInteger(value) && value > 0
181
+ ? value
182
+ : DEFAULT_MAX_DESCRIPTIONS_PER_TURN;
183
+ }
184
+
185
+ export function isValidVisionTimeoutMs(value: unknown): value is number {
186
+ return typeof value === "number"
187
+ && Number.isInteger(value)
188
+ && value >= MIN_VISION_TIMEOUT_MS
189
+ && value <= MAX_VISION_TIMEOUT_MS;
190
+ }
191
+
192
+ /** Runtime config is permissive: malformed or out-of-range values fall back to the default. */
193
+ export function resolveVisionTimeoutMs(value: unknown): number {
194
+ return isValidVisionTimeoutMs(value) ? value : DEFAULT_VISION_TIMEOUT_MS;
195
+ }
196
+
197
+ /** Run `worker` over `items` with bounded concurrency, preserving input order in the result array. */
198
+ async function runBounded<T, R>(items: T[], limit: number, worker: (item: T) => Promise<R>): Promise<R[]> {
199
+ const results = new Array<R>(items.length);
200
+ let next = 0;
201
+ const runner = async (): Promise<void> => {
202
+ while (next < items.length) {
203
+ const i = next++;
204
+ results[i] = await worker(items[i]);
205
+ }
206
+ };
207
+ await Promise.all(Array.from({ length: Math.min(limit, items.length) }, runner));
208
+ return results;
209
+ }
210
+
211
+ function clamp(s: string, max: number): string {
212
+ return s.length <= max ? s : `${s.slice(0, max)}\n…[description truncated]`;
213
+ }
214
+
215
+ export interface AnthropicVisionProvider {
216
+ providerName: string;
217
+ provider: OcxProviderConfig;
218
+ }
219
+
220
+ /**
221
+ * First enabled Anthropic OAuth provider whose active stored account is not marked for reauth.
222
+ * Delegates to the shared sidecar auth module (#2188) — same predicate as web-search.
223
+ */
224
+ export function findAnthropicVisionProvider(config: OcxConfig): AnthropicVisionProvider | undefined {
225
+ const auth = resolveSidecarAuth(config);
226
+ if (!auth.isAnthropicAuth || !auth.anthropicProviderName || !auth.anthropicProvider) return undefined;
227
+ return { providerName: auth.anthropicProviderName, provider: auth.anthropicProvider };
228
+ }
229
+
230
+ export function resolveVisionBackend(
231
+ explicit: "openai" | "anthropic" | "routed" | undefined,
232
+ anthropicSidecar: AnthropicVisionProvider | undefined,
233
+ ): "openai" | "anthropic" {
234
+ if (explicit === "openai" || explicit === "anthropic") return explicit;
235
+ // "routed" collapses to the legacy default order until its describe executor
236
+ // lands (roadmap 170 → 180 revised): a persisted routed backend without a
237
+ // dispatchable arm degrades exactly like unset rather than crashing. wp3
238
+ // replaces this collapse with the real routed arm in planVisionSidecar.
239
+ return anthropicSidecar ? "anthropic" : "openai";
240
+ }
241
+
242
+ /** Native model used by the OpenAI vision helper, including its bounded default. */
243
+ export function resolveOpenAiVisionModel(config: Pick<OcxConfig, "visionSidecar">): string {
244
+ const configured = config.visionSidecar?.model;
245
+ // Namespaced routed ids never reach the forward executor (see
246
+ // resolveEffectiveVisionModel).
247
+ return configured && !configured.includes("/") ? configured : DEFAULT_VISION_MODEL;
248
+ }
249
+
250
+ /** Effective describer model for the backend `planVisionSidecar` selected. */
251
+ export function resolveEffectiveVisionModel(
252
+ config: Pick<OcxConfig, "visionSidecar">,
253
+ backend: "openai" | "anthropic",
254
+ ): string {
255
+ const configured = config.visionSidecar?.model;
256
+ // A namespaced "provider/model" id belongs to the routed backend only; the
257
+ // forward/OAuth executors POST the model string verbatim, so it falls back
258
+ // to the side's default here (PUT coherence rejects new writes of this
259
+ // shape, but a legacy or hand-edited config must not break the executor).
260
+ const usable = configured && !configured.includes("/") ? configured : undefined;
261
+ return backend === "anthropic"
262
+ ? usable || DEFAULT_ANTHROPIC_VISION_MODEL
263
+ : usable || DEFAULT_VISION_MODEL;
264
+ }
265
+
266
+ /** A user/developer/toolResult message can carry images (toolResult: e.g. Codex view_image output). */
267
+ function carriesImages(role: string): boolean {
268
+ return role === "user" || role === "developer" || role === "toolResult";
269
+ }
270
+
271
+ function messagesHaveImage(parsed: OcxParsedRequest): boolean {
272
+ return parsed.context.messages.some(m =>
273
+ carriesImages(m.role) && Array.isArray(m.content) && (m.content as OcxContentPart[]).some(p => p.type === "image"));
274
+ }
275
+
276
+ export function shouldResolveOpenAiVisionSidecar(
277
+ config: OcxConfig,
278
+ provider: OcxProviderConfig,
279
+ modelId: string,
280
+ parsed: OcxParsedRequest,
281
+ ): boolean {
282
+ if (!isModelTextOnly(provider, modelId) || !messagesHaveImage(parsed)) return false;
283
+ const cfg = config.visionSidecar ?? {};
284
+ if (cfg.enabled === false) return false;
285
+ return resolveVisionBackend(cfg.backend, findAnthropicVisionProvider(config)) === "openai";
286
+ }
287
+
288
+ export interface VisionPlan {
289
+ backend: "openai" | "anthropic" | "routed";
290
+ forwardSidecar?: ResolvedOpenAiForwardSidecar;
291
+ anthropicSidecar?: AnthropicVisionProvider;
292
+ /** Namespaced "provider/model" describer for the routed backend (roadmap 180). */
293
+ routedModel?: string;
294
+ /** Loopback dispatch inputs for the routed backend. */
295
+ routedConfig?: Pick<OcxConfig, "port" | "apiKeys">;
296
+ settings: VisionSettings;
297
+ maxDescriptionsPerTurn: number;
298
+ }
299
+
300
+ /**
301
+ * Decide whether the vision sidecar should pre-describe images for this request, returning the plan
302
+ * if so. Active when: the routed model is in `provider.noVisionModels`, the request actually carries
303
+ * an image, the sidecar isn't disabled, and the selected backend has usable auth. Returns undefined
304
+ * otherwise (the caller strips images before sending to a text-only model).
305
+ */
306
+ export function planVisionSidecar(
307
+ config: OcxConfig,
308
+ provider: OcxProviderConfig,
309
+ modelId: string,
310
+ parsed: OcxParsedRequest,
311
+ openAiSidecar?: ResolvedOpenAiForwardSidecar,
312
+ ): VisionPlan | undefined {
313
+ if (!isModelTextOnly(provider, modelId)) return undefined;
314
+ if (!messagesHaveImage(parsed)) return undefined;
315
+ const cfg = config.visionSidecar ?? {};
316
+ if (cfg.enabled === false) return undefined;
317
+
318
+ // Routed arm (roadmap 180 revised): explicit backend + NAMESPACED explicit
319
+ // model only — never inferred from credential availability. Plan-time
320
+ // fence: the target must not be provably blind, and must not itself be a
321
+ // model this planner would re-enter for (belt; the terminal marker on the
322
+ // loopback request is the braces).
323
+ if (cfg.backend === "routed") {
324
+ const routedModel = cfg.model;
325
+ const sep = routedModel ? routedModel.indexOf("/") : -1;
326
+ if (routedModel && sep > 0) {
327
+ const targetProvider = routedModel.slice(0, sep);
328
+ const targetId = routedModel.slice(sep + 1);
329
+ const targetProviderConfig = config.providers?.[targetProvider];
330
+ const targetVisible = modelAcceptsImageInput(config, { provider: targetProvider, id: targetId }) !== false
331
+ && !(targetProviderConfig && isModelTextOnly(targetProviderConfig, targetId));
332
+ if (targetVisible) {
333
+ return {
334
+ backend: "routed",
335
+ routedModel,
336
+ routedConfig: { port: config.port, ...(config.apiKeys ? { apiKeys: config.apiKeys } : {}) },
337
+ settings: {
338
+ model: routedModel,
339
+ reasoning: DEFAULT_REASONING,
340
+ timeoutMs: resolveVisionTimeoutMs(cfg.timeoutMs),
341
+ },
342
+ maxDescriptionsPerTurn: resolveMaxDescriptionsPerTurn(cfg.maxDescriptionsPerTurn),
343
+ };
344
+ }
345
+ }
346
+ // Misconfigured routed backend (bare id, unknown provider, or provably
347
+ // blind target): fall through to the legacy default order below rather
348
+ // than dispatching a describe that cannot work.
349
+ }
350
+
351
+ const anthropicSidecar = findAnthropicVisionProvider(config);
352
+ const backend = resolveVisionBackend(cfg.backend, anthropicSidecar);
353
+ // A namespaced routed model must never reach the forward/OAuth executors
354
+ // (they POST the string verbatim); the effective-model resolver falls back
355
+ // to each side's default in that case.
356
+ const model = resolveEffectiveVisionModel(config, backend);
357
+ const maxDescriptionsPerTurn = resolveMaxDescriptionsPerTurn(cfg.maxDescriptionsPerTurn);
358
+
359
+ if (backend === "anthropic") {
360
+ if (!anthropicSidecar) return undefined;
361
+ return {
362
+ backend,
363
+ anthropicSidecar,
364
+ settings: {
365
+ model,
366
+ reasoning: normalizeVisionReasoningForModel(model, cfg.reasoning) ?? DEFAULT_REASONING,
367
+ timeoutMs: resolveVisionTimeoutMs(cfg.timeoutMs),
368
+ },
369
+ maxDescriptionsPerTurn,
370
+ };
371
+ }
372
+
373
+ if (!openAiSidecar) return undefined;
374
+ return {
375
+ backend,
376
+ forwardSidecar: openAiSidecar,
377
+ settings: {
378
+ model,
379
+ reasoning: normalizeVisionReasoningForModel(model, cfg.reasoning) ?? DEFAULT_REASONING,
380
+ timeoutMs: resolveVisionTimeoutMs(cfg.timeoutMs),
381
+ },
382
+ maxDescriptionsPerTurn,
383
+ };
384
+ }
385
+
386
+ interface ImageJob {
387
+ imageUrl: string;
388
+ detail?: string;
389
+ contextText: string;
390
+ }
391
+
392
+ /** Render one describe outcome as the replacement text part (clamped to the per-image budget). */
393
+ function renderDescription(out: { text: string; error?: string }): OcxTextContent {
394
+ return {
395
+ type: "text",
396
+ text: out.error
397
+ ? `[An image was attached but could not be processed: ${out.error}]`
398
+ : `[Image content — described by a vision model because you cannot see images directly:\n${clamp(out.text.trim(), DESC_MAX_CHARS)}]`,
399
+ };
400
+ }
401
+
402
+ const IMAGE_OMITTED_TEXT = "[image omitted: this model is text-only and the vision sidecar is unavailable (no ChatGPT login)]";
403
+
404
+ function isPlainRecord(value: unknown): value is Record<string, unknown> {
405
+ return typeof value === "object" && value !== null && !Array.isArray(value);
406
+ }
407
+
408
+ /**
409
+ * Keep the native Responses passthrough body aligned with image replacements made in the parsed
410
+ * message graph. The passthrough adapter serializes `_rawBody`, while translated adapters serialize
411
+ * `context.messages`; updating only the latter would send the original pixels to a text-only
412
+ * Responses upstream even after the vision sidecar produced a caption.
413
+ *
414
+ * Rewrites only image-bearing user/developer messages and tool outputs. All other native Responses
415
+ * items (reasoning, calls, ids, compaction, and provider-specific metadata) remain byte-structurally
416
+ * untouched.
417
+ */
418
+ function syncRawBodyImageDescriptions(parsed: OcxParsedRequest, descriptions: readonly string[]): void {
419
+ const rawBody = parsed._rawBody;
420
+ if (!isPlainRecord(rawBody) || !Array.isArray(rawBody.input)) return;
421
+
422
+ let nextDescription = 0;
423
+ const rewriteImages = (value: unknown, nonEmptyImageUrlsOnly: boolean): unknown => {
424
+ if (Array.isArray(value)) {
425
+ let changed = false;
426
+ const rewritten = value.map(entry => {
427
+ const next = rewriteImages(entry, nonEmptyImageUrlsOnly);
428
+ if (next !== entry) changed = true;
429
+ return next;
430
+ });
431
+ return changed ? rewritten : value;
432
+ }
433
+ if (!isPlainRecord(value)) return value;
434
+ if (value.type === "input_image" && typeof value.image_url === "string") {
435
+ if (nonEmptyImageUrlsOnly && value.image_url.length === 0) {
436
+ return { type: "input_text", text: IMAGE_OMITTED_TEXT };
437
+ }
438
+ const description = descriptions[nextDescription++];
439
+ return { type: "input_text", text: description ?? IMAGE_OMITTED_TEXT };
440
+ }
441
+ return value;
442
+ };
443
+
444
+ let changed = false;
445
+ const input = rawBody.input.map(item => {
446
+ if (!isPlainRecord(item)) return item;
447
+ const type = typeof item.type === "string" ? item.type : (typeof item.role === "string" ? "message" : "");
448
+ const role = typeof item.role === "string" ? item.role : "";
449
+ const isMessageContent = (
450
+ (type === "message" && (role === "user" || role === "developer"))
451
+ || type === "agent_message"
452
+ );
453
+ const field = isMessageContent
454
+ ? "content"
455
+ : (type === "function_call_output" || type === "custom_tool_call_output")
456
+ ? "output"
457
+ : undefined;
458
+ if (!field) return item;
459
+ const rewritten = rewriteImages(item[field], isMessageContent);
460
+ if (rewritten === item[field]) return item;
461
+ changed = true;
462
+ return { ...item, [field]: rewritten };
463
+ });
464
+
465
+ if (changed) rawBody.input = input;
466
+ }
467
+
468
+ function sha256(value: string | Uint8Array): string {
469
+ return createHash("sha256").update(value).digest("hex");
470
+ }
471
+
472
+ function normalizedContext(contextText: string): string {
473
+ return contextText.trim().replace(/\s+/g, " ");
474
+ }
475
+
476
+ function descriptionIdentity(job: ImageJob, plan: VisionPlan): { key: string; persistent: boolean } {
477
+ let imageHash: string;
478
+ let persistent = false;
479
+ const data = /^data:[^;,]+;base64,(.*)$/s.exec(job.imageUrl);
480
+ if (data) {
481
+ imageHash = sha256(Buffer.from(data[1], "base64"));
482
+ persistent = true;
483
+ } else {
484
+ imageHash = sha256(job.imageUrl);
485
+ }
486
+ return {
487
+ key: JSON.stringify([
488
+ plan.backend,
489
+ plan.settings.model,
490
+ ...(plan.backend === "openai" ? [plan.settings.reasoning] : []),
491
+ job.detail ?? "high",
492
+ imageHash,
493
+ sha256(normalizedContext(job.contextText)),
494
+ ]),
495
+ persistent,
496
+ };
497
+ }
498
+
499
+ async function executeDescription(
500
+ job: ImageJob,
501
+ plan: VisionPlan,
502
+ selectedForwardHeaders: Headers,
503
+ abortSignal?: AbortSignal,
504
+ recordSidecarOutcome?: SidecarOutcomeRecorder,
505
+ ): Promise<DescribeOutcome> {
506
+ if (plan.backend === "routed") {
507
+ if (!plan.routedModel || !plan.routedConfig) return { text: "", error: "routed vision sidecar is unavailable" };
508
+ return describeImageRouted(
509
+ job.imageUrl,
510
+ job.detail,
511
+ job.contextText,
512
+ plan.routedModel,
513
+ plan.routedConfig,
514
+ plan.settings,
515
+ abortSignal,
516
+ );
517
+ }
518
+ if (plan.backend === "anthropic") {
519
+ const sidecar = plan.anthropicSidecar;
520
+ if (!sidecar) return { text: "", error: "anthropic vision sidecar is unavailable" };
521
+ return describeImageAnthropic(
522
+ job.imageUrl,
523
+ job.detail,
524
+ job.contextText,
525
+ sidecar.providerName,
526
+ sidecar.provider,
527
+ plan.settings,
528
+ abortSignal,
529
+ );
530
+ }
531
+ if (!plan.forwardSidecar) return { text: "", error: "OpenAI vision sidecar is unavailable" };
532
+ return describeImage(
533
+ job.imageUrl,
534
+ job.detail,
535
+ job.contextText,
536
+ plan.forwardSidecar.provider,
537
+ plan.forwardSidecar.headers,
538
+ plan.settings,
539
+ abortSignal,
540
+ recordSidecarOutcome,
541
+ );
542
+ }
543
+
544
+ /**
545
+ * Replace every image part in the request with a gpt-described text part, so a text-only model can
546
+ * reason about it. Mutates `parsed.context.messages` in place; uses the message's own text as the
547
+ * description context. All images are described with bounded concurrency (not serially) so a
548
+ * multi-image turn doesn't pay the sum of per-image latencies. Failures degrade to a short marker.
549
+ */
550
+ export async function describeImagesInPlace(
551
+ parsed: OcxParsedRequest,
552
+ plan: VisionPlan,
553
+ selectedForwardHeaders: Headers,
554
+ abortSignal?: AbortSignal,
555
+ recordSidecarOutcome?: SidecarOutcomeRecorder,
556
+ translatorBudget?: TranslatorBudget,
557
+ ): Promise<void> {
558
+ const jobs: ImageJob[] = [];
559
+ const targets: { msg: OcxMessage; parts: OcxContentPart[] }[] = [];
560
+ for (const msg of parsed.context.messages) {
561
+ if (!carriesImages(msg.role) || !Array.isArray(msg.content)) continue;
562
+ const parts = msg.content as OcxContentPart[];
563
+ if (!parts.some(p => p.type === "image")) continue;
564
+ const contextText = parts
565
+ .filter((p): p is OcxTextContent => p.type === "text")
566
+ .map(p => p.text)
567
+ .join(" ")
568
+ .slice(0, CONTEXT_MAX_CHARS);
569
+ for (const p of parts) {
570
+ if (p.type === "image") jobs.push({ imageUrl: p.imageUrl, detail: p.detail, contextText });
571
+ }
572
+ targets.push({ msg, parts });
573
+ }
574
+ if (jobs.length === 0) {
575
+ syncRawBodyImageDescriptions(parsed, []);
576
+ return;
577
+ }
578
+
579
+ const inFlight = new Map<string, Promise<DescribeOutcome>>();
580
+ const executions: Array<() => Promise<void>> = [];
581
+ const outcomePromises: Array<Promise<DescribeOutcome>> = [];
582
+ let misses = 0;
583
+
584
+ for (const job of jobs) {
585
+ const identity = descriptionIdentity(job, plan);
586
+ const cached = identity.persistent ? descriptionCache.get(identity.key) : undefined;
587
+ if (cached !== undefined) {
588
+ outcomePromises.push(Promise.resolve({ text: cached }));
589
+ continue;
590
+ }
591
+
592
+ const existing = inFlight.get(identity.key);
593
+ if (existing) {
594
+ outcomePromises.push(existing);
595
+ continue;
596
+ }
597
+
598
+ if (misses >= plan.maxDescriptionsPerTurn) {
599
+ const capped = Promise.resolve<DescribeOutcome>({ text: "", error: "description cap reached for this turn" });
600
+ inFlight.set(identity.key, capped);
601
+ outcomePromises.push(capped);
602
+ continue;
603
+ }
604
+
605
+ misses += 1;
606
+ let resolveOutcome!: (outcome: DescribeOutcome) => void;
607
+ const pending = new Promise<DescribeOutcome>(resolve => { resolveOutcome = resolve; });
608
+ inFlight.set(identity.key, pending);
609
+ outcomePromises.push(pending);
610
+ executions.push(async () => {
611
+ let outcome: DescribeOutcome;
612
+ try {
613
+ outcome = await executeDescription(job, plan, selectedForwardHeaders, abortSignal, recordSidecarOutcome);
614
+ } catch (error) {
615
+ outcome = { text: "", error: error instanceof Error ? error.message : String(error) };
616
+ }
617
+ const successfulText = outcome.error ? "" : clamp(outcome.text.trim(), DESC_MAX_CHARS);
618
+ if (identity.persistent && successfulText) {
619
+ descriptionCache.set(identity.key, successfulText);
620
+ enforceAppOwnedMemoryBudget();
621
+ }
622
+ const resolvedOutcome = outcome.error ? outcome : { ...outcome, text: successfulText };
623
+ resolveOutcome(resolvedOutcome);
624
+ });
625
+ }
626
+
627
+ await runBounded(executions, VISION_CONCURRENCY, execute => execute());
628
+ const outcomes = await Promise.all(outcomePromises);
629
+
630
+ let oi = 0;
631
+ const descriptions: string[] = [];
632
+ for (const { msg, parts } of targets) {
633
+ const newParts: OcxContentPart[] = [];
634
+ for (const p of parts) {
635
+ if (p.type !== "image") {
636
+ newParts.push(p);
637
+ continue;
638
+ }
639
+ const replacement = renderDescription(outcomes[oi++]);
640
+ descriptions.push(replacement.text);
641
+ const reservation = translatorBudget?.reserveTransient(
642
+ descriptionEncoder.encode(replacement.text).byteLength,
643
+ { kind: "request_copies" },
644
+ );
645
+ newParts.push(replacement);
646
+ reservation?.commitRetained();
647
+ }
648
+ msg.content = newParts;
649
+ }
650
+ syncRawBodyImageDescriptions(parsed, descriptions);
651
+ }
652
+
653
+ /**
654
+ * Fail-closed image strip for sidecar-covered models when NO sidecar plan exists (no forward
655
+ * provider / missing forwarded auth / sidecar disabled): the upstream is text-only, so forwarding
656
+ * raw images would 400 or silently confuse it. Replace each image with an explicit marker so the
657
+ * model (and the user, via its reply) knows the image was dropped rather than ignored.
658
+ */
659
+ export function stripImagesInPlace(parsed: OcxParsedRequest, translatorBudget?: TranslatorBudget): boolean {
660
+ let stripped = false;
661
+ const descriptions: string[] = [];
662
+ for (const msg of parsed.context.messages) {
663
+ if (!carriesImages(msg.role) || !Array.isArray(msg.content)) continue;
664
+ const parts = msg.content as OcxContentPart[];
665
+ if (!parts.some(p => p.type === "image")) continue;
666
+ msg.content = parts.map(p => {
667
+ if (p.type !== "image") return p;
668
+ const replacement = { type: "text", text: IMAGE_OMITTED_TEXT } as OcxContentPart;
669
+ descriptions.push((replacement as OcxTextContent).text);
670
+ const reservation = translatorBudget?.reserveTransient(
671
+ descriptionEncoder.encode((replacement as OcxTextContent).text).byteLength,
672
+ { kind: "request_copies" },
673
+ );
674
+ reservation?.commitRetained();
675
+ return replacement;
676
+ });
677
+ stripped = true;
678
+ }
679
+ syncRawBodyImageDescriptions(parsed, descriptions);
680
+ return stripped;
681
+ }