@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,55 @@
1
+ import { NATIVE_OPENAI_MODELS, nativeReasoningEfforts } from "../codex/catalog/metadata";
2
+ import {
3
+ VISION_REASONING_EFFORTS,
4
+ sanitizeVisionReasoning,
5
+ type VisionReasoningEffort,
6
+ } from "../reasoning-effort";
7
+
8
+ /** Keep the public config type aligned without replacing the heavily-changed shared types file. */
9
+ declare module "../types" {
10
+ interface OcxVisionSidecarConfig {
11
+ /** OpenAI Responses reasoning effort used by the vision describer. Default: low. */
12
+ reasoning?: VisionReasoningEffort;
13
+ }
14
+ }
15
+
16
+ const NATIVE_VISION_MODELS = new Set(NATIVE_OPENAI_MODELS);
17
+
18
+ /**
19
+ * Return the known vision reasoning ladder for a native OpenAI model.
20
+ * Unknown/custom models deliberately return undefined so callers stay permissive when reliable
21
+ * capability metadata is unavailable.
22
+ */
23
+ export function nativeVisionReasoningEfforts(modelId: string): VisionReasoningEffort[] | undefined {
24
+ if (!NATIVE_VISION_MODELS.has(modelId)) return undefined;
25
+ const advertised = new Set(nativeReasoningEfforts(modelId));
26
+ const supported = VISION_REASONING_EFFORTS.filter(effort => advertised.has(effort));
27
+ return supported.length > 0 ? [...supported] : undefined;
28
+ }
29
+
30
+ /**
31
+ * Normalize one configured effort against a model's known ladder. Invalid enum values degrade to
32
+ * undefined; valid values on unknown/custom models are preserved. A known native model clamps to
33
+ * the highest supported rung at or below the requested value.
34
+ */
35
+ export function normalizeVisionReasoningForModel(
36
+ modelId: string,
37
+ value: unknown,
38
+ ): VisionReasoningEffort | undefined {
39
+ const requested = sanitizeVisionReasoning(value);
40
+ if (!requested) return undefined;
41
+ const supported = nativeVisionReasoningEfforts(modelId);
42
+ if (!supported || supported.includes(requested)) return requested;
43
+
44
+ const requestedRank = VISION_REASONING_EFFORTS.indexOf(requested);
45
+ let best = supported[0];
46
+ let bestRank = VISION_REASONING_EFFORTS.indexOf(best);
47
+ for (const effort of supported) {
48
+ const rank = VISION_REASONING_EFFORTS.indexOf(effort);
49
+ if (rank <= requestedRank && rank >= bestRank) {
50
+ best = effort;
51
+ bestRank = rank;
52
+ }
53
+ }
54
+ return best;
55
+ }
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Describe ONE image via a ROUTED model through the proxy's own
3
+ * /v1/chat/completions on loopback (#2188 roadmap 180 revised).
4
+ *
5
+ * One executor for every provider the router can reach: the chat inbound
6
+ * translates image_url parts and each adapter compiles its own wire
7
+ * (Anthropic blocks, Antigravity inlineData, xai Responses input_image, plain
8
+ * openai-chat), so provider coverage is the router's job, not this file's.
9
+ *
10
+ * Recursion fence: the request carries `x-opencodex-vision-describe: 1`.
11
+ * The Chat surface detects the raw header before its bridge rebuilds headers
12
+ * and carries it into handleResponses as `visionDescribeTerminal`; a marked
13
+ * request STRIPS images instead of planning another describe (depth cap 1,
14
+ * holds under predicate drift and combo re-resolution — audit rounds 2-4).
15
+ *
16
+ * Admission ladder (audit round 3): configuredApiAuthToken() (env token) ||
17
+ * service token file || first config.apiKeys entry, sent as
18
+ * `x-opencodex-api-key` — never Authorization (gateway-cache.ts rule: an
19
+ * admission secret in a forwardable header is a forwarding hazard). Loopback
20
+ * binds require no token at all (resolveApiAuth admits loopback).
21
+ *
22
+ * Known limitation (recorded in roadmap 170): a bindHost where 127.0.0.1
23
+ * does not answer cannot reach its own loopback — same latent limitation
24
+ * gateway-cache has.
25
+ */
26
+ import type { OcxConfig } from "../types";
27
+ import { signalWithTimeout, cancelBodyOnAbort } from "../lib/abort";
28
+ import { redactSecretString } from "../lib/redact";
29
+ import { sidecarEnter } from "../lib/sidecar-tracker";
30
+ import { configuredApiAuthToken, configuredPort } from "../server/auth-cors";
31
+ import { loadServiceTokenFromFile } from "../lib/service-secrets";
32
+ import type { DescribeOutcome, VisionSettings } from "./describe";
33
+
34
+ export const VISION_DESCRIBE_TERMINAL_HEADER = "x-opencodex-vision-describe";
35
+
36
+ const ALLOWED_IMAGE_MIME = new Set(["image/png", "image/jpeg", "image/jpg", "image/webp", "image/gif"]);
37
+ const MAX_IMAGE_BYTES = 20 * 1024 * 1024;
38
+ /** Bound the loopback JSON response; descriptions are clamped to ~2k chars by the caller anyway. */
39
+ const MAX_ROUTED_RESPONSE_BYTES = 4 * 1024 * 1024;
40
+
41
+ const DESCRIBE_INSTRUCTION =
42
+ "You are a vision describer for a text-only model that cannot see the image. Describe the image "
43
+ + "thoroughly and factually so that model can fully reason about it: transcribe any visible text "
44
+ + "verbatim, and note UI/layout, colors, branding/logos, charts, and notable details. Focus on "
45
+ + "what's relevant to the user's request. Output only the description.";
46
+
47
+ function validateImageUrl(url: string): string | null {
48
+ if (url.startsWith("data:")) {
49
+ const match = /^data:([^;,]+?)(;base64)?,(.*)$/s.exec(url);
50
+ if (!match) return "malformed data URL";
51
+ const mime = match[1].toLowerCase();
52
+ if (!ALLOWED_IMAGE_MIME.has(mime)) return `unsupported image type "${mime}"`;
53
+ if (match[2]) {
54
+ const bytes = Math.floor((match[3].length * 3) / 4);
55
+ if (bytes > MAX_IMAGE_BYTES) return `image too large (~${Math.round(bytes / 1024 / 1024)}MB)`;
56
+ }
57
+ return null;
58
+ }
59
+ if (url.startsWith("https://")) return null;
60
+ return "unsupported image URL scheme (expected data: or https:)";
61
+ }
62
+
63
+ /** The admission ladder: env token, service token file, first configured API key. */
64
+ export function routedDescribeAdmissionToken(config: Pick<OcxConfig, "apiKeys">): string | undefined {
65
+ const envToken = configuredApiAuthToken();
66
+ if (envToken) return envToken;
67
+ const fileToken = loadServiceTokenFromFile(process.env);
68
+ if (fileToken) return fileToken;
69
+ const first = config.apiKeys?.[0]?.key?.trim();
70
+ return first || undefined;
71
+ }
72
+
73
+ /** Base URL seam for tests; production always self-fetches loopback. */
74
+ export function routedDescribeBaseUrl(config: Pick<OcxConfig, "port">): string {
75
+ // config.port can be 0 (ephemeral bind, tests) or stale after a live port
76
+ // override; the server records its ACTUAL bound port via setCorsOrigin at
77
+ // startup, so prefer that when config carries no positive port.
78
+ const port = config.port && config.port > 0 ? String(config.port) : configuredPort();
79
+ return `http://127.0.0.1:${port}`;
80
+ }
81
+
82
+ export async function describeImageRouted(
83
+ imageUrl: string,
84
+ _detail: string | undefined,
85
+ contextText: string,
86
+ routedModel: string,
87
+ config: Pick<OcxConfig, "port" | "apiKeys">,
88
+ settings: VisionSettings,
89
+ abortSignal?: AbortSignal,
90
+ baseUrlOverride?: string,
91
+ ): Promise<DescribeOutcome> {
92
+ const invalid = validateImageUrl(imageUrl);
93
+ if (invalid) return { text: "", error: invalid };
94
+
95
+ const headers: Record<string, string> = {
96
+ "Content-Type": "application/json",
97
+ [VISION_DESCRIBE_TERMINAL_HEADER]: "1",
98
+ };
99
+ const admission = routedDescribeAdmissionToken(config);
100
+ if (admission) headers["x-opencodex-api-key"] = admission;
101
+
102
+ const requestBody = {
103
+ model: routedModel,
104
+ stream: false,
105
+ messages: [
106
+ { role: "system", content: DESCRIBE_INSTRUCTION },
107
+ {
108
+ role: "user",
109
+ content: [
110
+ ...(contextText ? [{ type: "text", text: `User's request context: ${contextText}` }] : []),
111
+ { type: "image_url", image_url: { url: imageUrl } },
112
+ ],
113
+ },
114
+ ],
115
+ };
116
+
117
+ const linkedSignal = signalWithTimeout(settings.timeoutMs, abortSignal);
118
+ const sidecarExit = sidecarEnter("vision");
119
+ const t0 = Date.now();
120
+ try {
121
+ const res = await fetch(`${baseUrlOverride ?? routedDescribeBaseUrl(config)}/v1/chat/completions`, {
122
+ method: "POST",
123
+ headers,
124
+ body: JSON.stringify(requestBody),
125
+ signal: linkedSignal.signal,
126
+ redirect: "manual",
127
+ });
128
+ const detachBodyGuard = cancelBodyOnAbort(res.body, linkedSignal.signal);
129
+ try {
130
+ const raw = await res.text();
131
+ if (raw.length > MAX_ROUTED_RESPONSE_BYTES) {
132
+ return { text: "", error: "routed describe response exceeded byte bound" };
133
+ }
134
+ if (!res.ok) {
135
+ return { text: "", error: `routed describe HTTP ${res.status}: ${redactSecretString(raw.slice(0, 200))}` };
136
+ }
137
+ let payload: unknown;
138
+ try { payload = JSON.parse(raw); } catch {
139
+ return { text: "", error: "routed describe returned non-JSON" };
140
+ }
141
+ const content = extractChatContent(payload);
142
+ if (!content) return { text: "", error: "routed describe returned no text" };
143
+ return { text: content };
144
+ } finally {
145
+ detachBodyGuard();
146
+ }
147
+ } catch (e) {
148
+ const kind = e instanceof Error && e.name === "TimeoutError" ? "timeout" : "connect_error";
149
+ console.warn(`[vision] routed describe ${kind} (${Date.now() - t0}ms)`);
150
+ return { text: "", error: redactSecretString(e instanceof Error ? e.message : String(e)) };
151
+ } finally {
152
+ sidecarExit();
153
+ linkedSignal.cleanup();
154
+ }
155
+ }
156
+
157
+ function extractChatContent(payload: unknown): string | undefined {
158
+ if (!payload || typeof payload !== "object") return undefined;
159
+ const choices = (payload as { choices?: unknown }).choices;
160
+ if (!Array.isArray(choices) || choices.length === 0) return undefined;
161
+ const message = (choices[0] as { message?: unknown })?.message;
162
+ if (!message || typeof message !== "object") return undefined;
163
+ const content = (message as { content?: unknown }).content;
164
+ if (typeof content === "string" && content.trim().length > 0) return content;
165
+ // Some adapters emit content parts; join text parts.
166
+ if (Array.isArray(content)) {
167
+ const joined = content
168
+ .map(part => (part && typeof part === "object" && typeof (part as { text?: unknown }).text === "string"
169
+ ? (part as { text: string }).text
170
+ : ""))
171
+ .join("");
172
+ if (joined.trim().length > 0) return joined;
173
+ }
174
+ return undefined;
175
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Inclusive integer bounds for `visionSidecar.timeoutMs`.
3
+ * The ceiling is the 32-bit timer delay used by `signalWithTimeout` / `setTimeout`.
4
+ * This module is the single authority: the runtime, management API, and Dashboard
5
+ * import these numbers rather than restating them.
6
+ */
7
+ export const DEFAULT_VISION_TIMEOUT_MS = 45_000;
8
+ export const MIN_VISION_TIMEOUT_MS = 1;
9
+ export const MAX_VISION_TIMEOUT_MS = 2_147_483_647;
@@ -0,0 +1,195 @@
1
+ import type { OcxProviderConfig } from "../types";
2
+ import { getValidAccessToken, publicOAuthAuthenticationErrorMessage } from "../oauth";
3
+ import { ANTHROPIC_OAUTH_BETA, CLAUDE_CODE_SYSTEM_INSTRUCTION } from "../oauth/anthropic";
4
+ import { CLAUDE_CODE_HEADERS, claudeCodeSessionId } from "../adapters/client-fingerprint";
5
+ import { signalWithTimeout, cancelBodyOnAbort } from "../lib/abort";
6
+ import { sidecarEnter } from "../lib/sidecar-tracker";
7
+ import { fetchWithResetRetry } from "../lib/upstream-retry";
8
+ import type { WebSearchSource } from "./parse";
9
+ import { BASE_INSTRUCTION, IMAGE_INSTRUCTION, type SidecarOutcome, type SidecarSettings } from "./executor";
10
+
11
+ /** Hardcoded per-turn search bound handed to the server tool (mirrors the loop's maxSearches intent). */
12
+ const ANTHROPIC_MAX_USES = 3;
13
+ /** Answer budget; the injected tool_result is clamped downstream, so this only bounds the sidecar turn. */
14
+ const ANTHROPIC_MAX_TOKENS = 8192;
15
+
16
+ function isRec(v: unknown): v is Record<string, unknown> {
17
+ return !!v && typeof v === "object" && !Array.isArray(v);
18
+ }
19
+
20
+ /**
21
+ * Fold an Anthropic Messages SSE stream (a web_search_20250305 turn) into a WebSearchResult.
22
+ *
23
+ * Anthropic streams the FULL `web_search_tool_result.content` array on `content_block_start` (not via
24
+ * deltas), so sources are collected there; the answer text arrives as `text_delta` events, and
25
+ * `citations_delta` (web_search_result_location) contributes any additional cited URLs. A
26
+ * `web_search_tool_result_error` content object yields no sources. Never throws.
27
+ */
28
+ export async function parseAnthropicSidecarSSE(res: Response): Promise<SidecarOutcome> {
29
+ const sources: WebSearchSource[] = [];
30
+ const seen = new Set<string>();
31
+ const pushSource = (url: unknown, title: unknown): void => {
32
+ if (typeof url !== "string" || url.length === 0 || seen.has(url)) return;
33
+ seen.add(url);
34
+ sources.push(typeof title === "string" && title.length > 0 ? { url, title } : { url });
35
+ };
36
+
37
+ let text = "";
38
+ let sawToolResultError = false;
39
+ if (!res.body) return { text: "", sources, error: "anthropic sidecar returned no response body" };
40
+
41
+ const decoder = new TextDecoder();
42
+ const reader = res.body.getReader();
43
+ let buffer = "";
44
+
45
+ const handleFrame = (data: Record<string, unknown>): void => {
46
+ const type = typeof data.type === "string" ? data.type : "";
47
+ if (type === "content_block_start") {
48
+ const block = isRec(data.content_block) ? data.content_block : {};
49
+ if (block.type === "web_search_tool_result") {
50
+ if (Array.isArray(block.content)) {
51
+ for (const hit of block.content) {
52
+ if (isRec(hit) && hit.type === "web_search_result") pushSource(hit.url, hit.title);
53
+ }
54
+ } else if (isRec(block.content) && block.content.type === "web_search_tool_result_error") {
55
+ sawToolResultError = true;
56
+ }
57
+ }
58
+ } else if (type === "content_block_delta") {
59
+ const delta = isRec(data.delta) ? data.delta : {};
60
+ if (delta.type === "text_delta" && typeof delta.text === "string") {
61
+ text += delta.text;
62
+ } else if (delta.type === "citations_delta") {
63
+ const citation = isRec(delta.citation) ? delta.citation : {};
64
+ if (citation.type === "web_search_result_location") pushSource(citation.url, citation.title);
65
+ }
66
+ }
67
+ };
68
+
69
+ // Parse one SSE frame's `data:` payload and fold it. Shared by the streaming loop and the EOF flush.
70
+ const processFrame = (rawFrame: string): void => {
71
+ let dataLine = "";
72
+ for (const line of rawFrame.split("\n")) {
73
+ if (line.startsWith("data:")) dataLine += line.slice(line.startsWith("data: ") ? 6 : 5);
74
+ }
75
+ if (!dataLine || dataLine === "[DONE]") return;
76
+ let data: unknown;
77
+ try { data = JSON.parse(dataLine); } catch { return; }
78
+ if (isRec(data)) handleFrame(data);
79
+ };
80
+
81
+ try {
82
+ for (;;) {
83
+ const { done, value } = await reader.read();
84
+ if (done) break;
85
+ // Normalize CRLF on the ACCUMULATED buffer so a `\r\n` pair split across two network chunks
86
+ // (chunk ends in `\r`, next starts with `\n`) still collapses to `\n` (audit round-2 F2).
87
+ buffer = (buffer + decoder.decode(value, { stream: true })).replace(/\r\n/g, "\n");
88
+ let sep: number;
89
+ while ((sep = buffer.indexOf("\n\n")) !== -1) {
90
+ const rawFrame = buffer.slice(0, sep);
91
+ buffer = buffer.slice(sep + 2);
92
+ processFrame(rawFrame);
93
+ }
94
+ }
95
+ // Flush the decoder and process any final unterminated frame (a stream that ends without \n\n).
96
+ buffer = (buffer + decoder.decode()).replace(/\r\n/g, "\n");
97
+ if (buffer.trim().length > 0) processFrame(buffer);
98
+ } catch {
99
+ /* mid-stream abort/decode failure: fall through with whatever text/sources were gathered */
100
+ }
101
+
102
+ const trimmed = text.trim();
103
+ if (trimmed.length === 0) {
104
+ return { text: "", sources, error: sawToolResultError ? "anthropic web search returned an error result" : "anthropic sidecar produced no answer" };
105
+ }
106
+ return { text: trimmed, sources };
107
+ }
108
+
109
+ /**
110
+ * Execute ONE web search via a Claude sidecar through the STORED anthropic OAuth credential — the
111
+ * Anthropic-backed analog of runWebSearch. Authenticates with getValidAccessToken (refresh handled)
112
+ * and reproduces the Claude Code OAuth fingerprint (identity system block first, oauth beta, client
113
+ * headers, stable session id) so the request is first-party-shaped. Never throws — returns `{error}`
114
+ * so the caller injects a graceful tool result.
115
+ */
116
+ export async function runAnthropicWebSearch(
117
+ query: string,
118
+ providerName: string,
119
+ provider: OcxProviderConfig,
120
+ settings: SidecarSettings,
121
+ abortSignal?: AbortSignal,
122
+ ): Promise<SidecarOutcome> {
123
+ const base = provider.baseUrl.replace(/\/v1\/?$/, "");
124
+ const url = `${base}/v1/messages`;
125
+ let token: string;
126
+ try {
127
+ token = await getValidAccessToken(providerName);
128
+ } catch (e) {
129
+ return { text: "", sources: [], error: `anthropic sidecar auth failed: ${publicOAuthAuthenticationErrorMessage(e)}` };
130
+ }
131
+ const headers: Record<string, string> = {
132
+ "Content-Type": "application/json",
133
+ "anthropic-version": "2023-06-01",
134
+ "Accept": "text/event-stream",
135
+ "User-Agent": "@anthropic-ai/sdk/0.74.0",
136
+ "Authorization": `Bearer ${token}`,
137
+ "anthropic-beta": ANTHROPIC_OAUTH_BETA,
138
+ ...CLAUDE_CODE_HEADERS,
139
+ "X-Claude-Code-Session-Id": claudeCodeSessionId(token),
140
+ "x-client-request-id": crypto.randomUUID(),
141
+ };
142
+ if (provider.headers) Object.assign(headers, provider.headers);
143
+
144
+ const instruction = settings.describeImages ? BASE_INSTRUCTION + IMAGE_INSTRUCTION : BASE_INSTRUCTION;
145
+ const body = {
146
+ model: settings.model,
147
+ max_tokens: ANTHROPIC_MAX_TOKENS,
148
+ // sonnet-5 defaults to adaptive thinking when omitted; keep the sidecar fast/cheap (audit F2).
149
+ thinking: { type: "disabled" },
150
+ // OAuth fingerprint requires the Claude Code identity as the FIRST system block (audit F6/anthropic.ts).
151
+ system: [
152
+ { type: "text", text: CLAUDE_CODE_SYSTEM_INSTRUCTION },
153
+ { type: "text", text: instruction },
154
+ ],
155
+ messages: [{ role: "user", content: [{ type: "text", text: query }] }],
156
+ tools: [{ type: "web_search_20250305", name: "web_search", max_uses: ANTHROPIC_MAX_USES }],
157
+ stream: true,
158
+ };
159
+
160
+ const linkedSignal = signalWithTimeout(settings.timeoutMs, abortSignal);
161
+ const sidecarExit = sidecarEnter("web-search");
162
+ const t0 = Date.now();
163
+ try {
164
+ const res = await fetchWithResetRetry(
165
+ () => fetch(url, { method: "POST", headers, body: JSON.stringify(body), signal: linkedSignal.signal }),
166
+ { abortSignal: linkedSignal.signal, label: "web-search-sidecar-anthropic" },
167
+ );
168
+ // Guard before any branch reads the body: the failure branch's `res.text()` ran ahead of
169
+ // the success-path guard, reopening the fetch-resolution-to-reader-attach race
170
+ // (found investigating #1419).
171
+ const detachBodyGuard = cancelBodyOnAbort(res.body, linkedSignal.signal);
172
+ if (!res.ok) {
173
+ const t = await res.text().catch(() => "");
174
+ detachBodyGuard();
175
+ console.warn(`[web-search] anthropic sidecar HTTP ${res.status} for query "${query.slice(0, 80)}" (${Date.now() - t0}ms)`);
176
+ if (res.status === 401) {
177
+ return { text: "", sources: [], error: `anthropic sidecar auth failed: ${publicOAuthAuthenticationErrorMessage(new Error(t))}` };
178
+ }
179
+ // Upstream bodies are untrusted and may contain credentials, paths, or provider diagnostics.
180
+ return { text: "", sources: [], error: `sidecar HTTP ${res.status}` };
181
+ }
182
+ try {
183
+ return await parseAnthropicSidecarSSE(res);
184
+ } finally {
185
+ detachBodyGuard();
186
+ }
187
+ } catch (e) {
188
+ const kind = e instanceof Error && e.name === "TimeoutError" ? "timeout" : "connect_error";
189
+ console.warn(`[web-search] anthropic sidecar ${kind} for query "${query.slice(0, 80)}" (${Date.now() - t0}ms)`);
190
+ return { text: "", sources: [], error: `anthropic sidecar ${kind}` };
191
+ } finally {
192
+ sidecarExit();
193
+ linkedSignal.cleanup();
194
+ }
195
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Which backends may RUN a hosted web_search, and which candidate rows each
3
+ * can run (#2188 rule 2 for web-search: ∩ probed backend with an executor).
4
+ *
5
+ * A backend registers here only when BOTH hold:
6
+ * - an executor exists in this repository (src/web-search/executor.ts or
7
+ * anthropic-executor.ts today), and
8
+ * - its liveness probe passes. For the two shipped backends the probe IS
9
+ * auth presence — the ChatGPT forward path and the stored Anthropic OAuth
10
+ * path fail closed without a credential, so a live credential is the
11
+ * strongest pre-flight signal short of spending a search.
12
+ *
13
+ * Future backends (Gemini google_search, Grok web_search, Zen hosted search,
14
+ * Exa-class vendors) stay OUT of this table until a live probe and an
15
+ * executor land — the research and probe contracts are recorded in
16
+ * devlog/_plan/260820_sidecar_selection_unification/002 and 031. Documenting
17
+ * a tool is not the same as being able to run it.
18
+ */
19
+ import type { OcxConfig } from "../types";
20
+ import { AUTH_SLOT_MODELS, type SidecarAuthState } from "../sidecar/auth";
21
+ import type { SidecarCandidate } from "../sidecar/candidates";
22
+ import { getAccountSet } from "../oauth/store";
23
+ import type { WebSearchBackendId } from "./index";
24
+
25
+ export interface WebSearchBackendDescriptor {
26
+ backend: WebSearchBackendId;
27
+ /** Liveness signal for this backend (auth presence for the shipped two). */
28
+ isActive(auth: SidecarAuthState, config: OcxConfig): boolean;
29
+ /** Which candidate rows this backend's executor can actually run. */
30
+ eligibleModel(candidate: SidecarCandidate, auth: SidecarAuthState): boolean;
31
+ }
32
+
33
+ export const WEB_SEARCH_BACKENDS: readonly WebSearchBackendDescriptor[] = [
34
+ {
35
+ backend: "openai",
36
+ isActive: auth => auth.isCodexAuth,
37
+ // The ChatGPT forward executor runs BARE native slugs and the Codex auth
38
+ // slot — settings.model is POSTed verbatim to the forward /responses, so
39
+ // an account-bound "selector/slug" row (model-rows emits those as
40
+ // provider "openai", native true) or a custom openai-keyed row would
41
+ // persist an id the executor cannot run (review F1). The sidecar never
42
+ // calls routeModel; there is no prefix-stripping on this path.
43
+ eligibleModel: candidate => candidate.provider === "openai"
44
+ && (candidate.native === true || candidate.authSlot === true)
45
+ && !candidate.id.includes("/"),
46
+ },
47
+ {
48
+ backend: "anthropic",
49
+ isActive: auth => auth.isAnthropicAuth,
50
+ // The stored-OAuth Messages executor dispatches through exactly ONE
51
+ // provider — the one the shared auth module resolved. Same-adapter keyed
52
+ // rows are unreachable, mirroring visionBackendForCandidate's stance.
53
+ eligibleModel: (candidate, auth) => candidate.provider === auth.anthropicProviderName,
54
+ },
55
+ {
56
+ backend: "xai",
57
+ // Probe = stored Grok OAuth usable: enabled oauth-mode "xai" provider whose
58
+ // active account is not marked for reauth — the same predicate
59
+ // findXaiSidecarProvider applies at plan time (L7).
60
+ isActive: (_auth, config) => {
61
+ const provider = config.providers["xai"];
62
+ if (!provider || provider.disabled === true || provider.authMode !== "oauth") return false;
63
+ const set = getAccountSet("xai");
64
+ const active = set?.accounts.find(account => account.id === set.activeAccountId);
65
+ return !!active && active.needsReauth !== true;
66
+ },
67
+ eligibleModel: candidate => candidate.provider === "xai",
68
+ },
69
+ {
70
+ backend: "gemini",
71
+ // Probe = usable Antigravity OAuth + discovered projectId (findGeminiSidecarProvider's predicate).
72
+ isActive: (_auth, config) => {
73
+ const provider = config.providers["google-antigravity"];
74
+ if (!provider || provider.disabled === true || provider.authMode !== "oauth") return false;
75
+ const set = getAccountSet("google-antigravity");
76
+ const active = set?.accounts.find(account => account.id === set.activeAccountId);
77
+ if (!active || active.needsReauth === true) return false;
78
+ return !!(active.credential as { projectId?: string } | undefined)?.projectId;
79
+ },
80
+ eligibleModel: candidate => candidate.provider === "google-antigravity",
81
+ },
82
+ {
83
+ backend: "exa",
84
+ // Probe = operator key present. Exa is not an LLM: no candidate models ever
85
+ // match, so the GUI's model list stays untouched by this backend.
86
+ isActive: (_auth, config) => !!config.webSearchSidecar?.exaApiKey,
87
+ eligibleModel: () => false,
88
+ },
89
+ ];
90
+
91
+ /**
92
+ * (picker-visible ∪ auth slots) ∩ (active backend able to run the row).
93
+ * The auth slots always survive their own side's activation: a logged-in
94
+ * side keeps Luna/Haiku even when the picker hides them.
95
+ */
96
+ export function webSearchSidecarCandidates(
97
+ config: OcxConfig,
98
+ auth: SidecarAuthState,
99
+ all: readonly SidecarCandidate[],
100
+ ): SidecarCandidate[] {
101
+ const active = WEB_SEARCH_BACKENDS.filter(descriptor => descriptor.isActive(auth, config));
102
+ return all.filter(candidate => active.some(descriptor => descriptor.eligibleModel(candidate, auth)));
103
+ }
104
+
105
+ /** True when the id is one of the fixed auth-slot models (#2188 write-gate exception). */
106
+ export function isWebSearchAuthSlotModel(id: string): boolean {
107
+ return id === AUTH_SLOT_MODELS.codex || id === AUTH_SLOT_MODELS.anthropic;
108
+ }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Execute ONE web search via the Exa Search API (#2188 L9) — the non-LLM lane.
3
+ *
4
+ * Exa returns ranked result JSON, not a prose answer: the outcome's text is a
5
+ * per-result digest the routed model synthesizes from. Probe-verified 2026-08-21
6
+ * (devlog 002): POST https://api.exa.ai/search with x-api-key returns
7
+ * {requestId, results[{title,url,publishedDate,text}], costDollars}.
8
+ * redirect: "manual" because Bun forwards custom headers across redirects.
9
+ * Never throws; every error string passes redactSecretString.
10
+ */
11
+ import { fetchWithResetRetry } from "../lib/upstream-retry";
12
+ import { signalWithTimeout } from "../lib/abort";
13
+ import { sidecarEnter } from "../lib/sidecar-tracker";
14
+ import { redactSecretString } from "../lib/redact";
15
+ import type { WebSearchSource } from "./parse";
16
+ import type { SidecarOutcome, SidecarSettings } from "./executor";
17
+
18
+ const EXA_SEARCH_URL = "https://api.exa.ai/search";
19
+ const EXA_NUM_RESULTS = 5;
20
+ const EXA_SNIPPET_CHARS = 1000;
21
+
22
+ function isRec(v: unknown): v is Record<string, unknown> {
23
+ return !!v && typeof v === "object" && !Array.isArray(v);
24
+ }
25
+
26
+ export async function runExaWebSearch(
27
+ query: string,
28
+ apiKey: string,
29
+ settings: SidecarSettings,
30
+ abortSignal?: AbortSignal,
31
+ ): Promise<SidecarOutcome> {
32
+ if (!apiKey) return { text: "", sources: [], error: "exa backend selected without an exaApiKey" };
33
+ // The executor KNOWS the secret — pattern-based redaction cannot be trusted to
34
+ // recognize an arbitrary operator key, so scrub the literal value explicitly.
35
+ const scrub = (s: string) => redactSecretString(s.split(apiKey).join("[redacted-exa-key]"));
36
+ const linkedSignal = signalWithTimeout(settings.timeoutMs, abortSignal);
37
+ const sidecarExit = sidecarEnter("web-search");
38
+ const t0 = Date.now();
39
+ try {
40
+ const res = await fetchWithResetRetry(
41
+ () => fetch(EXA_SEARCH_URL, {
42
+ method: "POST",
43
+ headers: { "Content-Type": "application/json", "x-api-key": apiKey },
44
+ body: JSON.stringify({ query, numResults: EXA_NUM_RESULTS, contents: { text: { maxCharacters: EXA_SNIPPET_CHARS } } }),
45
+ signal: linkedSignal.signal,
46
+ redirect: "manual",
47
+ }),
48
+ { abortSignal: linkedSignal.signal, label: "exa-web-search-sidecar" },
49
+ );
50
+ if (!res.ok) {
51
+ const t = await res.text().catch(() => "");
52
+ // Scrub BEFORE truncating: slicing first can cut the literal key at the
53
+ // boundary, leaving an unscrubbable key prefix in the surviving text.
54
+ return { text: "", sources: [], error: `exa sidecar HTTP ${res.status}: ${scrub(t).slice(0, 200)}` };
55
+ }
56
+ const payload = await res.json().catch(() => null);
57
+ return mapExaSearchResponse(payload);
58
+ } catch (e) {
59
+ const kind = e instanceof Error && e.name === "TimeoutError" ? "timeout" : "connect_error";
60
+ console.warn(`[web-search] exa sidecar ${kind} (${Date.now() - t0}ms)`);
61
+ return { text: "", sources: [], error: scrub(e instanceof Error ? e.message : String(e)) };
62
+ } finally {
63
+ sidecarExit();
64
+ linkedSignal.cleanup();
65
+ }
66
+ }
67
+
68
+ /** Map an Exa /search payload to a digest the routed model can synthesize from. */
69
+ export function mapExaSearchResponse(payload: unknown): SidecarOutcome {
70
+ if (!isRec(payload) || !Array.isArray(payload.results)) {
71
+ return { text: "", sources: [], error: "exa sidecar returned a non-JSON or shapeless body" };
72
+ }
73
+ const sources: WebSearchSource[] = [];
74
+ const lines: string[] = [];
75
+ const seen = new Set<string>();
76
+ for (const result of payload.results) {
77
+ if (!isRec(result) || typeof result.url !== "string" || result.url.length === 0) continue;
78
+ if (seen.has(result.url)) continue;
79
+ seen.add(result.url);
80
+ const title = typeof result.title === "string" && result.title.length > 0 ? result.title : result.url;
81
+ sources.push({ url: result.url, ...(title !== result.url ? { title } : {}) });
82
+ const snippet = typeof result.text === "string" ? result.text.trim().slice(0, EXA_SNIPPET_CHARS) : "";
83
+ const dated = typeof result.publishedDate === "string" ? ` (${result.publishedDate.slice(0, 10)})` : "";
84
+ lines.push(`- ${title}${dated}: ${snippet || "(no excerpt)"} [${result.url}]`);
85
+ }
86
+ if (lines.length === 0) return { text: "", sources: [], error: "exa sidecar returned no results" };
87
+ return { text: `Search results:\n${lines.join("\n")}`, sources };
88
+ }