@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,615 @@
1
+ import { createHash, randomUUID } from "node:crypto";
2
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, unlinkSync } from "node:fs";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { atomicWriteFile } from "../config";
6
+ import type { OcxClaudeDesktopProfile } from "../types";
7
+ import { claudeDesktopConfigLibraryDir, resolveConfigLibraryDir } from "./desktop-3p-paths";
8
+ import {
9
+ reconcileDesktopProfile,
10
+ renderDesktopProfile,
11
+ type DesktopProfileModel,
12
+ } from "./desktop-profile";
13
+ import { nativeOpenAiContextWindow, type NativeContextLimitsInput } from "../codex/catalog";
14
+ import { assertDesktop3pModelsValid } from "./desktop-3p-guard";
15
+
16
+ export interface Desktop3pModelEntry {
17
+ name: string;
18
+ labelOverride: string;
19
+ anthropicFamilyTier: "opus" | "fable" | "sonnet" | "haiku";
20
+ isFamilyDefault?: boolean;
21
+ /**
22
+ * Desktop's documented 1M-context capability assertion. Set ONLY from an
23
+ * authoritative routed contextWindow >= 1M — never guessed (devlog 136 B5).
24
+ */
25
+ supports1m?: true;
26
+ /** When true, Desktop selects the 1M variant by default (official schema, Luna research 260722). */
27
+ prefer1m?: true;
28
+ }
29
+
30
+ /**
31
+ * static (default, Pro-verified devlog 138): pinned inferenceModels with
32
+ * modelDiscoveryEnabled:false — a static list OVERRIDES discovery (no merge), so
33
+ * this is the deterministic shape. hybrid keeps discovery:true alongside the list
34
+ * (claude-code-router's version-defensive pattern). discovery: /v1/models only.
35
+ */
36
+ export type Desktop3pConfigMode = "hybrid" | "discovery" | "static";
37
+
38
+ export interface Desktop3pRoutedModel {
39
+ provider: string;
40
+ id: string;
41
+ /** Authoritative context window (CatalogModel.contextWindow); optional. */
42
+ contextWindow?: number;
43
+ }
44
+
45
+ /**
46
+ * 1M-context eligibility, shared with the Desktop DTO so the dashboard's 1M chip can
47
+ * never disagree with what the writer emits. The DTO imports this from here — keeping
48
+ * the constant in this module avoids a cycle, since shared.ts already reads
49
+ * claude/desktop-profile.
50
+ */
51
+ export const DESKTOP_SUPPORTS_1M_THRESHOLD = 1_000_000;
52
+
53
+ export interface Desktop3pConfigLibraryOptions {
54
+ env?: NodeJS.ProcessEnv;
55
+ platform?: NodeJS.Platform;
56
+ homeDir?: string;
57
+ }
58
+
59
+ /**
60
+ * Resolve the config library from the same user-data root Claude Desktop uses. Keeping this in one
61
+ * helper prevents the writer and dashboard status probe from agreeing on a path Desktop never reads.
62
+ *
63
+ * The resolution itself lives in `./desktop-3p-paths`, which ports Claude Desktop's own `GE()`
64
+ * branch for branch — including the `-3p` suffix the app appends to its userData root. Dropping
65
+ * that suffix points us at a directory Desktop never reads (GitHub #539).
66
+ */
67
+ export function resolveDesktop3pConfigLibraryPath(
68
+ options: Desktop3pConfigLibraryOptions = {},
69
+ ): string {
70
+ if (options.env === undefined && options.platform === undefined && options.homeDir === undefined) {
71
+ return claudeDesktopConfigLibraryDir();
72
+ }
73
+ return resolveConfigLibraryDir({
74
+ env: options.env ?? process.env,
75
+ platform: options.platform ?? process.platform,
76
+ home: options.homeDir ?? homedir(),
77
+ });
78
+ }
79
+
80
+ /** CLI arg parsing for `ocx claude desktop` mode flags (mutually exclusive). */
81
+ export function parseDesktop3pModeArgs(flags: string[]): { mode: Desktop3pConfigMode } | { error: string } {
82
+ const known = new Map<string, Desktop3pConfigMode>([
83
+ ["--static", "static"],
84
+ ["--hybrid", "hybrid"],
85
+ ["--discovery-only", "discovery"],
86
+ ]);
87
+ const unknown = flags.filter(a => !known.has(a));
88
+ if (unknown.length > 0) return { error: `알 수 없는 옵션: ${unknown.join(" ")} (지원: --static, --hybrid, --discovery-only)` };
89
+ const picked = [...new Set(flags.map(a => known.get(a)!))];
90
+ if (picked.length > 1) return { error: "모드 옵션은 하나만 쓸 수 있습니다 (--static | --hybrid | --discovery-only)." };
91
+ return { mode: picked[0] ?? "static" };
92
+ }
93
+
94
+ interface Desktop3pMetadataEntry {
95
+ id: string;
96
+ name: string;
97
+ [key: string]: unknown;
98
+ }
99
+
100
+ interface Desktop3pMetadata {
101
+ appliedId?: string;
102
+ entries: Desktop3pMetadataEntry[];
103
+ [key: string]: unknown;
104
+ }
105
+
106
+ export type Desktop3pLibraryKind =
107
+ | "not_installed"
108
+ | "standard"
109
+ | "gateway_ours"
110
+ | "gateway_drifted"
111
+ | "foreign"
112
+ | "no_owned_state"
113
+ | "broken"
114
+ | "unsafe";
115
+
116
+ export interface Desktop3pLibraryInspection {
117
+ kind: Desktop3pLibraryKind;
118
+ libraryPath: string;
119
+ selectedProfilePath: string | null;
120
+ appliedId: string | null;
121
+ /** Paths of opencodex-owned rows that are not selected by Desktop. */
122
+ residualPaths: string[];
123
+ /** Bounded reason code; never includes metadata or profile contents. */
124
+ reason?: "metadata_unreadable" | "unsafe_applied_id" | "invalid_owned_profile";
125
+ fingerprint?: string;
126
+ /**
127
+ * Whether Desktop's applied selection is our owned entry, by ID match alone.
128
+ * `null` = undeterminable (no metadata, unreadable metadata, or no appliedId);
129
+ * a readable appliedId with no owned entry is a KNOWN false, not unknown.
130
+ * Deliberately independent of profile-file health: the status contract
131
+ * predates this inspector and callers render tri-state.
132
+ */
133
+ ownedProfileActive: boolean | null;
134
+ }
135
+
136
+ export interface Desktop3pRemovalResult {
137
+ ok: boolean;
138
+ changed: boolean;
139
+ kind: "removed" | "noop" | "cleanup_incomplete" | "unsafe" | "write_failed";
140
+ libraryPath: string;
141
+ residualPaths?: string[];
142
+ reason?: string;
143
+ }
144
+
145
+ let desktop3pRegistry = new Map<string, string>();
146
+ let desktop3pAliasesByRoute = new Map<string, string>();
147
+
148
+ /** Derive a stable letter-first, three-character base36 code from a route key. */
149
+ export function deriveDesktop3pCode(route: string): string {
150
+ const hash = createHash("sha256").update(route).digest();
151
+ const n = hash.readUInt32BE(0) % 33696;
152
+ const first = String.fromCharCode(97 + Math.floor(n / 1296));
153
+ const rest = (n % 1296).toString(36).padStart(2, "0");
154
+ return first + rest;
155
+ }
156
+
157
+ /**
158
+ * Alias for one proxy model. Real Anthropic models pass through unchanged (they must
159
+ * keep hitting the sk-ant native passthrough); everything else gets a Claude-shaped
160
+ * `claude-opus-4-8-{code}` id. Opus 4.8 is chosen deliberately: Desktop's effort
161
+ * selector is an allowlist keyed on exact supported model ids (Opus 4.8/4.7/4.6,
162
+ * Sonnet 4.6 — devlog 131), and 4.6+ canonical ids are dateless, so the letter-first
163
+ * 3-char suffix can never collide with a real id or a legacy date suffix.
164
+ */
165
+ export function desktop3pAlias(provider: string, modelId: string): string {
166
+ if (provider === "anthropic" && modelId.startsWith("claude-")) return modelId;
167
+ return `claude-opus-4-8-${deriveDesktop3pCode(`${provider}/${modelId}`)}`;
168
+ }
169
+
170
+ /** Pre-rename alias shape (claude-opus-4-{code}) — still decoded for stale Desktop configs. */
171
+ export function legacyDesktop3pAlias(provider: string, modelId: string): string {
172
+ return `claude-opus-4-${deriveDesktop3pCode(`${provider}/${modelId}`)}`;
173
+ }
174
+
175
+ function displayModelId(modelId: string): string {
176
+ return modelId
177
+ // Capability markers like [1m] are not name text: strip the brackets so the label
178
+ // reads "K3 1M", never "K3[1m]".
179
+ .replace(/\[([^\]]+)\]/g, "-$1")
180
+ .split(/[-_]+/)
181
+ .filter(Boolean)
182
+ .map(part => {
183
+ const lower = part.toLowerCase();
184
+ if (lower === "gpt" || lower === "glm" || lower === "ai" || lower === "1m") return lower.toUpperCase();
185
+ return part.charAt(0).toUpperCase() + part.slice(1);
186
+ })
187
+ .join(" ");
188
+ }
189
+
190
+ function collectDesktop3pModels(
191
+ nativeSlugs: string[],
192
+ routedModels: Array<Desktop3pRoutedModel>,
193
+ profile?: OcxClaudeDesktopProfile,
194
+ nativeContextCap?: NativeContextLimitsInput,
195
+ ): { models: Desktop3pModelEntry[]; registry: Map<string, string> } {
196
+ const registry = new Map<string, string>();
197
+ const models: Desktop3pModelEntry[] = [];
198
+ const candidates: Desktop3pRoutedModel[] = [
199
+ // Native candidates carry their real context window from the same accessor the
200
+ // Desktop DTO uses, so a native 1M/372k model resolves identically in the written
201
+ // config and on the dashboard.
202
+ ...nativeSlugs.map(id => {
203
+ const contextWindow = nativeOpenAiContextWindow(id, nativeContextCap);
204
+ return { provider: "native", id, ...(contextWindow !== undefined ? { contextWindow } : {}) };
205
+ }),
206
+ ...routedModels,
207
+ ];
208
+
209
+ if (profile) {
210
+ const profileModels = candidates.map(({ provider, id, contextWindow }) => ({
211
+ route: `${provider}/${id}`,
212
+ label: `${displayModelId(id)} (${provider})`,
213
+ ...(typeof contextWindow === "number" ? { contextWindow } : {}),
214
+ } satisfies DesktopProfileModel));
215
+ const reconciled = reconcileDesktopProfile(profile, profileModels);
216
+ const rendered = renderDesktopProfile(reconciled, profileModels);
217
+ const aliasesByRoute = new Map<string, string>();
218
+ for (const model of rendered) {
219
+ aliasesByRoute.set(model.route, model.name);
220
+ if (!model.route.startsWith("anthropic/claude-")) registry.set(model.name, model.route);
221
+ models.push({
222
+ name: model.name,
223
+ labelOverride: model.label,
224
+ anthropicFamilyTier: model.family,
225
+ ...(model.isFamilyDefault ? { isFamilyDefault: true } : {}),
226
+ ...(model.supports1m ? { supports1m: true, prefer1m: true } : {}),
227
+ });
228
+ }
229
+ // Legacy hashes are compatibility-only and can collide. Bind them in stable route order so
230
+ // changing a family default or rendered ordering can never silently rebind an old Desktop id.
231
+ for (const model of [...rendered].sort((a, b) => a.route.localeCompare(b.route))) {
232
+ if (model.route.startsWith("anthropic/claude-")) continue;
233
+ const providerEnd = model.route.indexOf("/");
234
+ const provider = model.route.slice(0, providerEnd);
235
+ const id = model.route.slice(providerEnd + 1);
236
+ const legacy = legacyDesktop3pAlias(provider, id);
237
+ const existing = registry.get(legacy);
238
+ if (existing && existing !== model.route) {
239
+ console.warn(`[opencodex] Claude Desktop legacy alias collision: ${legacy} stays bound to ${existing}; ignoring ${model.route}`);
240
+ continue;
241
+ }
242
+ registry.set(legacy, model.route);
243
+ }
244
+ desktop3pAliasesByRoute = aliasesByRoute;
245
+ return { models, registry };
246
+ }
247
+
248
+ for (const { provider, id, contextWindow } of candidates) {
249
+ const route = `${provider}/${id}`;
250
+ const alias = desktop3pAlias(provider, id);
251
+ const supports1m = typeof contextWindow === "number" && contextWindow >= DESKTOP_SUPPORTS_1M_THRESHOLD
252
+ ? { supports1m: true as const }
253
+ : {};
254
+ if (alias === id) {
255
+ // Real Anthropic model: keep it OUT of the decode registry — registering it would
256
+ // make resolveInboundModel() non-identity and kill the sk-ant native passthrough
257
+ // (audit 133 #1). It still appears in the static Desktop model list below.
258
+ models.push({
259
+ name: alias,
260
+ labelOverride: `${displayModelId(id)} (${provider})`,
261
+ anthropicFamilyTier: "opus",
262
+ ...supports1m,
263
+ ...(supports1m.supports1m ? { prefer1m: true as const } : {}),
264
+ });
265
+ continue;
266
+ }
267
+ const existingRoute = registry.get(alias);
268
+ if (existingRoute !== undefined) {
269
+ console.warn(`[opencodex] Claude Desktop 3P alias collision: ${alias} maps to both ${existingRoute} and ${route}; skipping ${route}`);
270
+ continue;
271
+ }
272
+
273
+ registry.set(alias, route);
274
+ // Back-compat decode for Desktop configs written before the opus-4-8 rename.
275
+ const legacy = legacyDesktop3pAlias(provider, id);
276
+ if (!registry.has(legacy)) registry.set(legacy, route);
277
+ models.push({
278
+ name: alias,
279
+ labelOverride: `${displayModelId(id)} (${provider})`,
280
+ anthropicFamilyTier: "opus",
281
+ ...supports1m,
282
+ ...(supports1m.supports1m ? { prefer1m: true as const } : {}),
283
+ });
284
+ }
285
+
286
+ if (models[0]) models[0].isFamilyDefault = true;
287
+ desktop3pAliasesByRoute = new Map(candidates.map(({ provider, id }) => [`${provider}/${id}`, desktop3pAlias(provider, id)]));
288
+ return { models, registry };
289
+ }
290
+
291
+ /** Build and install the registry used to decode Desktop aliases. */
292
+ export function buildDesktop3pRegistry(
293
+ nativeSlugs: string[],
294
+ routedModels: Array<Desktop3pRoutedModel>,
295
+ profile?: OcxClaudeDesktopProfile,
296
+ nativeContextCap?: NativeContextLimitsInput,
297
+ ): Map<string, string> {
298
+ const { registry } = collectDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
299
+ desktop3pRegistry = registry;
300
+ return registry;
301
+ }
302
+
303
+ /** Generate Claude Desktop 3P model entries from the proxy's available models. */
304
+ export function generateDesktop3pModels(
305
+ nativeSlugs: string[],
306
+ routedModels: Array<Desktop3pRoutedModel>,
307
+ profile?: OcxClaudeDesktopProfile,
308
+ nativeContextCap?: NativeContextLimitsInput,
309
+ ): Desktop3pModelEntry[] {
310
+ const { models, registry } = collectDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
311
+ desktop3pRegistry = registry;
312
+ return models;
313
+ }
314
+
315
+ /** Resolve an alias using the most recently generated Desktop model registry. */
316
+ export function resolveDesktop3pAlias(alias: string): string | null {
317
+ return desktop3pRegistry.get(alias) ?? null;
318
+ }
319
+
320
+ /** Alias selected by the installed profile registry, falling back to the legacy hash shape. */
321
+ export function activeDesktop3pAlias(provider: string, modelId: string): string {
322
+ return desktop3pAliasesByRoute.get(`${provider}/${modelId}`) ?? desktop3pAlias(provider, modelId);
323
+ }
324
+
325
+ /**
326
+ * Generate the complete Claude Desktop 3P gateway config.
327
+ *
328
+ * Default mode is "static" (Pro-verified, devlog 138): the static list is the ONLY
329
+ * channel for supports1m/tier pins and it overrides discovery anyway (no merge), so
330
+ * discovery stays off for determinism. supports1m makes Desktop offer a separate 1M
331
+ * row; selecting it sends the bare id + `anthropic-beta: context-1m-2025-08-07`.
332
+ */
333
+ export function generateDesktop3pConfig(
334
+ port: number,
335
+ nativeSlugs: string[],
336
+ routedModels: Array<Desktop3pRoutedModel>,
337
+ apiKey = "ocx",
338
+ mode: Desktop3pConfigMode = "static",
339
+ profile?: OcxClaudeDesktopProfile,
340
+ nativeContextCap?: NativeContextLimitsInput,
341
+ ): object {
342
+ const base = {
343
+ inferenceProvider: "gateway",
344
+ inferenceCredentialKind: "static",
345
+ inferenceGatewayBaseUrl: `http://127.0.0.1:${port}`,
346
+ inferenceGatewayApiKey: apiKey,
347
+ };
348
+ if (mode === "discovery") {
349
+ // Build/refresh the decode registry even though no static list is emitted.
350
+ buildDesktop3pRegistry(nativeSlugs, routedModels, profile, nativeContextCap);
351
+ return { ...base, modelDiscoveryEnabled: true };
352
+ }
353
+ return {
354
+ ...base,
355
+ modelDiscoveryEnabled: mode === "hybrid",
356
+ inferenceModels: (() => {
357
+ const models = generateDesktop3pModels(nativeSlugs, routedModels, profile, nativeContextCap);
358
+ // Fail loud at the write boundary rather than ship a config Desktop rejects:
359
+ // the output counterpart of the request-path guards.
360
+ assertDesktop3pModelsValid(models);
361
+ return models;
362
+ })(),
363
+ };
364
+ }
365
+
366
+ function parseMetadata(path: string): Desktop3pMetadata {
367
+ if (!existsSync(path)) return { entries: [] };
368
+ const parsed = JSON.parse(readFileSync(path, "utf8")) as Partial<Desktop3pMetadata>;
369
+ if (!Array.isArray(parsed.entries)) throw new Error("Claude Desktop 3P _meta.json has no entries array");
370
+ return { ...parsed, entries: parsed.entries };
371
+ }
372
+
373
+ const SAFE_DESKTOP_PROFILE_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
374
+
375
+ function isRecord(value: unknown): value is Record<string, unknown> {
376
+ return typeof value === "object" && value !== null && !Array.isArray(value);
377
+ }
378
+
379
+ function isOwnedDesktopEntry(entry: Desktop3pMetadataEntry | undefined): boolean {
380
+ return entry?.name === "opencodex" || entry?.name === "opencodex-standard";
381
+ }
382
+
383
+ /** A gateway row is removable; the selected standard row must always remain. */
384
+ function isOwnedDesktopGatewayEntry(entry: Desktop3pMetadataEntry | undefined): boolean {
385
+ return entry?.name === "opencodex";
386
+ }
387
+
388
+ function profilePath(libraryPath: string, id: string): string {
389
+ return join(libraryPath, `${id}.json`);
390
+ }
391
+
392
+ /**
393
+ * Read Desktop's selected config without changing its library.
394
+ *
395
+ * This is intentionally separate from the eager writer below: status probes must
396
+ * never manufacture a config-library directory on a machine without Desktop.
397
+ */
398
+ export function inspectDesktop3pConfigLibrary(
399
+ options: Desktop3pConfigLibraryOptions & { appliedFingerprint?: string | null } = {},
400
+ ): Desktop3pLibraryInspection {
401
+ const libraryPath = resolveDesktop3pConfigLibraryPath(options);
402
+ if (!existsSync(libraryPath)) {
403
+ return { kind: "not_installed", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], ownedProfileActive: null };
404
+ }
405
+
406
+ const metadataPath = join(libraryPath, "_meta.json");
407
+ if (!existsSync(metadataPath)) {
408
+ return { kind: "no_owned_state", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], ownedProfileActive: null };
409
+ }
410
+
411
+ let metadata: Desktop3pMetadata;
412
+ try {
413
+ metadata = parseMetadata(metadataPath);
414
+ } catch {
415
+ return {
416
+ kind: "unsafe", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], reason: "metadata_unreadable", ownedProfileActive: null,
417
+ };
418
+ }
419
+ const appliedId = typeof metadata.appliedId === "string" ? metadata.appliedId : null;
420
+ if (appliedId === null) {
421
+ return { kind: "no_owned_state", libraryPath, selectedProfilePath: null, appliedId: null, residualPaths: [], ownedProfileActive: null };
422
+ }
423
+ const selected = metadata.entries.find(entry => entry?.id === appliedId);
424
+ // A readable appliedId with no owned entry is a KNOWN false, not unknown.
425
+ const ownedProfileActive = isOwnedDesktopEntry(selected);
426
+ if (!SAFE_DESKTOP_PROFILE_ID.test(appliedId)) {
427
+ return {
428
+ kind: "unsafe", libraryPath, selectedProfilePath: null, appliedId, residualPaths: [], reason: "unsafe_applied_id", ownedProfileActive,
429
+ };
430
+ }
431
+
432
+ const selectedProfilePath = profilePath(libraryPath, appliedId);
433
+ const residualPaths = metadata.entries
434
+ .filter(entry => isOwnedDesktopGatewayEntry(entry) && entry.id !== appliedId && SAFE_DESKTOP_PROFILE_ID.test(entry.id))
435
+ .flatMap(entry => [profilePath(libraryPath, entry.id), `${profilePath(libraryPath, entry.id)}.bak`])
436
+ .filter(existsSync);
437
+ if (!existsSync(selectedProfilePath)) {
438
+ return { kind: "broken", libraryPath, selectedProfilePath, appliedId, residualPaths, ownedProfileActive };
439
+ }
440
+
441
+ let profile: Record<string, unknown>;
442
+ let fingerprint: string;
443
+ try {
444
+ const source = readFileSync(selectedProfilePath, "utf8");
445
+ const parsed = JSON.parse(source) as unknown;
446
+ if (!isRecord(parsed)) return { kind: "broken", libraryPath, selectedProfilePath, appliedId, residualPaths, ownedProfileActive };
447
+ profile = parsed;
448
+ fingerprint = createHash("sha256").update(source).digest("hex").slice(0, 16);
449
+ } catch {
450
+ return { kind: "broken", libraryPath, selectedProfilePath, appliedId, residualPaths, ownedProfileActive };
451
+ }
452
+ if (!isOwnedDesktopEntry(selected)) {
453
+ return { kind: "foreign", libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, ownedProfileActive };
454
+ }
455
+ if (profile.inferenceProvider === undefined) {
456
+ return { kind: "standard", libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, ownedProfileActive };
457
+ }
458
+ const validGateway = profile.inferenceProvider === "gateway"
459
+ && profile.inferenceCredentialKind === "static"
460
+ && typeof profile.inferenceGatewayBaseUrl === "string"
461
+ && typeof profile.inferenceGatewayApiKey === "string";
462
+ if (!validGateway) {
463
+ return {
464
+ kind: "unsafe", libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, reason: "invalid_owned_profile", ownedProfileActive,
465
+ };
466
+ }
467
+ return {
468
+ kind: options.appliedFingerprint && options.appliedFingerprint === fingerprint ? "gateway_ours" : "gateway_drifted",
469
+ libraryPath, selectedProfilePath, appliedId, residualPaths, fingerprint, ownedProfileActive,
470
+ };
471
+ }
472
+
473
+ /**
474
+ * Select a credential-free standard profile before deleting an owned gateway.
475
+ * The old metadata row remains as a retry locator only until both its profile
476
+ * and backup are absent; successful cleanup removes it in the same operation.
477
+ *
478
+ * `gateway_drifted` is still an owned opencodex gateway (name + valid shape); the
479
+ * fingerprint only says on-disk bytes differ from the last saved marker. Refusing
480
+ * OFF for drift left users unable to disable after a lost `appliedFingerprint`
481
+ * (or any other benign mismatch), while the Integrations card still showed the
482
+ * leftover profile as applied/stale.
483
+ */
484
+ export function removeDesktop3pStandardPivot(
485
+ options: Desktop3pConfigLibraryOptions & {
486
+ appliedFingerprint?: string | null;
487
+ unlink?: (path: string) => void;
488
+ } = {},
489
+ ): Desktop3pRemovalResult {
490
+ const inspected = inspectDesktop3pConfigLibrary(options);
491
+ if (inspected.kind === "not_installed" || inspected.kind === "no_owned_state") {
492
+ return { ok: true, changed: false, kind: "noop", libraryPath: inspected.libraryPath };
493
+ }
494
+ if (inspected.kind === "broken" || inspected.kind === "unsafe") {
495
+ return { ok: false, changed: false, kind: "unsafe", libraryPath: inspected.libraryPath, reason: inspected.reason };
496
+ }
497
+ if (!inspected.appliedId || !SAFE_DESKTOP_PROFILE_ID.test(inspected.appliedId)) {
498
+ return { ok: false, changed: false, kind: "unsafe", libraryPath: inspected.libraryPath, reason: "unsafe_applied_id" };
499
+ }
500
+
501
+ const metadataPath = join(inspected.libraryPath, "_meta.json");
502
+ try {
503
+ const metadata = parseMetadata(metadataPath);
504
+ const selectedId = inspected.appliedId;
505
+ // When Desktop is actively using our gateway (current or drifted), pivot only
506
+ // that selected row first. Any second owned row is residue for a later
507
+ // standard-mode retry; this preserves the selected-row preference after an
508
+ // interrupted cleanup.
509
+ const selectedOwnedGatewayActive =
510
+ inspected.kind === "gateway_ours" || inspected.kind === "gateway_drifted";
511
+ const targetIds = selectedOwnedGatewayActive
512
+ ? [selectedId]
513
+ : metadata.entries
514
+ .filter(isOwnedDesktopGatewayEntry)
515
+ .map(entry => entry.id)
516
+ .filter(id => SAFE_DESKTOP_PROFILE_ID.test(id));
517
+ if (targetIds.length === 0) return { ok: true, changed: false, kind: "noop", libraryPath: inspected.libraryPath };
518
+
519
+ let metadataAfterPivot = metadata;
520
+ if (selectedOwnedGatewayActive) {
521
+ const standardId = randomUUID();
522
+ const standardPath = profilePath(inspected.libraryPath, standardId);
523
+ atomicWriteFile(standardPath, "{}\n");
524
+ const standardEntry: Desktop3pMetadataEntry = { id: standardId, name: "opencodex-standard" };
525
+ metadataAfterPivot = { ...metadata, appliedId: standardId, entries: [...metadata.entries, standardEntry] };
526
+ atomicWriteFile(metadataPath, JSON.stringify(metadataAfterPivot, null, 2) + "\n");
527
+ }
528
+
529
+ const residualPaths: string[] = [];
530
+ for (const id of targetIds) {
531
+ for (const candidate of [profilePath(inspected.libraryPath, id), `${profilePath(inspected.libraryPath, id)}.bak`]) {
532
+ try {
533
+ if (existsSync(candidate)) (options.unlink ?? unlinkSync)(candidate);
534
+ } catch {
535
+ // Only the path is allowed to leave this credential-bearing cleanup boundary.
536
+ }
537
+ if (existsSync(candidate)) residualPaths.push(candidate);
538
+ }
539
+ }
540
+ const ownedResiduePaths = metadataAfterPivot.entries
541
+ .filter(entry => isOwnedDesktopGatewayEntry(entry) && !targetIds.includes(entry.id) && SAFE_DESKTOP_PROFILE_ID.test(entry.id))
542
+ .flatMap(entry => [profilePath(inspected.libraryPath, entry.id), `${profilePath(inspected.libraryPath, entry.id)}.bak`])
543
+ .filter(existsSync);
544
+ if (residualPaths.length > 0 || ownedResiduePaths.length > 0) {
545
+ return {
546
+ ok: false, changed: true, kind: "cleanup_incomplete", libraryPath: inspected.libraryPath,
547
+ residualPaths: [...new Set([...residualPaths, ...ownedResiduePaths])],
548
+ };
549
+ }
550
+ // Do not leave a metadata row pointing at a deleted profile. For a foreign
551
+ // selection this only removes proven opencodex residues; appliedId is kept.
552
+ atomicWriteFile(
553
+ metadataPath,
554
+ JSON.stringify({ ...metadataAfterPivot, entries: metadataAfterPivot.entries.filter(entry => !targetIds.includes(entry.id)) }, null, 2) + "\n",
555
+ );
556
+ return { ok: true, changed: true, kind: "removed", libraryPath: inspected.libraryPath };
557
+ } catch {
558
+ return { ok: false, changed: false, kind: "write_failed", libraryPath: inspected.libraryPath };
559
+ }
560
+ }
561
+
562
+ /** Write and apply the opencodex config in Claude Desktop 3P's config library. */
563
+ export function writeDesktop3pConfig(
564
+ port: number,
565
+ nativeSlugs: string[],
566
+ routedModels: Array<Desktop3pRoutedModel>,
567
+ apiKey?: string,
568
+ mode: Desktop3pConfigMode = "static",
569
+ profile?: OcxClaudeDesktopProfile,
570
+ nativeContextCap?: NativeContextLimitsInput,
571
+ ): { written: boolean; path: string; reason?: string; fingerprint?: string } {
572
+ const libraryPath = resolveDesktop3pConfigLibraryPath();
573
+ const metadataPath = join(libraryPath, "_meta.json");
574
+ let configPath = libraryPath;
575
+
576
+ try {
577
+ mkdirSync(libraryPath, { recursive: true, mode: 0o700 });
578
+ const metadata = parseMetadata(metadataPath);
579
+ const selected = metadata.entries.find(entry => entry?.id === metadata.appliedId && isOwnedDesktopGatewayEntry(entry));
580
+ const existing = selected ?? metadata.entries.find(entry => isOwnedDesktopGatewayEntry(entry) && typeof entry.id === "string");
581
+ const id = existing?.id ?? randomUUID();
582
+ configPath = join(libraryPath, `${id}.json`);
583
+ const entry: Desktop3pMetadataEntry = existing ? { ...existing, id, name: "opencodex" } : { id, name: "opencodex" };
584
+ const entries = existing
585
+ ? metadata.entries.map(current => current === existing ? entry : current)
586
+ : [...metadata.entries, entry];
587
+
588
+ const configJson = JSON.stringify(generateDesktop3pConfig(port, nativeSlugs, routedModels, apiKey, mode, profile, nativeContextCap), null, 2) + "\n";
589
+ const fingerprint = createHash("sha256").update(configJson).digest("hex").slice(0, 16);
590
+ const { backupPath } = atomicReplaceDesktopConfig(configPath, configJson);
591
+ try {
592
+ atomicWriteFile(metadataPath, JSON.stringify({ ...metadata, appliedId: id, entries }, null, 2) + "\n");
593
+ } catch (metaError) {
594
+ // Rollback: restore the backed-up config if metadata write fails.
595
+ if (backupPath && existsSync(backupPath)) copyFileSync(backupPath, configPath);
596
+ throw metaError;
597
+ }
598
+ return { written: true, path: configPath, fingerprint };
599
+ } catch (error) {
600
+ const reason = error instanceof Error ? error.message : String(error);
601
+ return { written: false, path: configPath, reason };
602
+ }
603
+ }
604
+
605
+ /** Backup an existing owned config then atomically replace it. Exported for failure-path tests. */
606
+ export function atomicReplaceDesktopConfig(
607
+ path: string,
608
+ content: string,
609
+ writer: (path: string, content: string) => void = atomicWriteFile,
610
+ ): { backupPath?: string } {
611
+ const backupPath = `${path}.bak`;
612
+ if (existsSync(path)) copyFileSync(path, backupPath);
613
+ writer(path, content);
614
+ return existsSync(backupPath) ? { backupPath } : {};
615
+ }
@@ -0,0 +1,26 @@
1
+ /** In-memory Desktop health tracker (resets on server restart). */
2
+
3
+ interface DesktopHealthState {
4
+ lastRequestAt: number | null;
5
+ requestCount: number;
6
+ errorCount: number;
7
+ }
8
+
9
+ const state: DesktopHealthState = { lastRequestAt: null, requestCount: 0, errorCount: 0 };
10
+
11
+ export function recordDesktopRequest(): void {
12
+ state.lastRequestAt = Date.now();
13
+ state.requestCount++;
14
+ }
15
+
16
+ export function recordDesktopError(): void {
17
+ state.errorCount++;
18
+ }
19
+
20
+ export function getDesktopHealth(): { lastRequestAt: string | null; requestCount: number; errorCount: number } {
21
+ return {
22
+ lastRequestAt: state.lastRequestAt !== null ? new Date(state.lastRequestAt).toISOString() : null,
23
+ requestCount: state.requestCount,
24
+ errorCount: state.errorCount,
25
+ };
26
+ }