@iislee/opencodex 2.11.0 → 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 (675) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +114 -19
  3. package/bin/ocx.mjs +164 -36
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-BF38heuV.js +104 -0
  6. package/gui/dist/assets/index-DMiI18Kv.css +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/gui/dist/provider-icons/alibaba-color.svg +1 -1
  9. package/gui/dist/provider-icons/antigravity-color.svg +1 -1
  10. package/gui/dist/provider-icons/claude-color.svg +1 -1
  11. package/gui/dist/provider-icons/cline-color.svg +16 -0
  12. package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
  13. package/gui/dist/provider-icons/commandcode-color.svg +1 -0
  14. package/gui/dist/provider-icons/copilot-color.svg +1 -1
  15. package/gui/dist/provider-icons/cursor-color.svg +1 -1
  16. package/gui/dist/provider-icons/deepseek-color.svg +1 -1
  17. package/gui/dist/provider-icons/firepass-color.svg +1 -1
  18. package/gui/dist/provider-icons/fireworks-color.svg +1 -1
  19. package/gui/dist/provider-icons/gemini-color.svg +1 -1
  20. package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
  21. package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
  22. package/gui/dist/provider-icons/grok.svg +1 -1
  23. package/gui/dist/provider-icons/groq-color.svg +1 -1
  24. package/gui/dist/provider-icons/huggingface-color.svg +1 -1
  25. package/gui/dist/provider-icons/kimi-color.svg +1 -1
  26. package/gui/dist/provider-icons/kiro-color.svg +2 -2
  27. package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
  28. package/gui/dist/provider-icons/mistral-color.svg +1 -1
  29. package/gui/dist/provider-icons/moonshot-color.svg +1 -1
  30. package/gui/dist/provider-icons/nvidia-color.svg +1 -1
  31. package/gui/dist/provider-icons/ollama-color.svg +1 -1
  32. package/gui/dist/provider-icons/openai.svg +1 -1
  33. package/gui/dist/provider-icons/opencode.svg +2 -1
  34. package/gui/dist/provider-icons/openrouter-color.svg +1 -1
  35. package/gui/dist/provider-icons/pi.svg +2 -2
  36. package/gui/dist/provider-icons/qianfan-color.svg +1 -1
  37. package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
  38. package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
  39. package/gui/dist/provider-icons/vllm-color.svg +1 -1
  40. package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
  41. package/package.json +19 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +376 -52
  44. package/src/adapters/base.ts +54 -7
  45. package/src/adapters/client-fingerprint.ts +18 -12
  46. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  47. package/src/adapters/command-code.ts +601 -0
  48. package/src/adapters/cursor/checkpoint-store.ts +303 -0
  49. package/src/adapters/cursor/cursor-errors.ts +129 -6
  50. package/src/adapters/cursor/discovery.ts +61 -4
  51. package/src/adapters/cursor/effort-map.ts +27 -3
  52. package/src/adapters/cursor/framing.ts +39 -0
  53. package/src/adapters/cursor/h2-pool.ts +123 -0
  54. package/src/adapters/cursor/http1-bidi.ts +361 -0
  55. package/src/adapters/cursor/images.ts +704 -0
  56. package/src/adapters/cursor/live-models.ts +174 -58
  57. package/src/adapters/cursor/live-transport.ts +609 -170
  58. package/src/adapters/cursor/native-exec-common.ts +23 -2
  59. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  60. package/src/adapters/cursor/native-exec-network.ts +1 -1
  61. package/src/adapters/cursor/native-exec-shell.ts +0 -1
  62. package/src/adapters/cursor/native-exec.ts +101 -14
  63. package/src/adapters/cursor/protobuf-events.ts +829 -11
  64. package/src/adapters/cursor/protobuf-request.ts +383 -65
  65. package/src/adapters/cursor/request-builder.ts +237 -31
  66. package/src/adapters/cursor/tool-definitions.ts +242 -12
  67. package/src/adapters/cursor/tool-result-normalize.ts +92 -0
  68. package/src/adapters/cursor/transport.ts +22 -0
  69. package/src/adapters/cursor/types.ts +28 -1
  70. package/src/adapters/cursor.ts +133 -7
  71. package/src/adapters/google-antigravity-replay.ts +558 -34
  72. package/src/adapters/google-antigravity-wire.ts +43 -10
  73. package/src/adapters/google-http.ts +38 -10
  74. package/src/adapters/google-tool-schema.ts +80 -15
  75. package/src/adapters/google-truncation.ts +11 -0
  76. package/src/adapters/google.ts +618 -74
  77. package/src/adapters/identity.ts +39 -6
  78. package/src/adapters/kiro-errors.ts +11 -0
  79. package/src/adapters/kiro-events.ts +19 -1
  80. package/src/adapters/kiro-thinking.ts +18 -2
  81. package/src/adapters/kiro-tools.ts +10 -1
  82. package/src/adapters/kiro.ts +92 -54
  83. package/src/adapters/mimo-free.ts +17 -0
  84. package/src/adapters/openai-chat-url.ts +11 -0
  85. package/src/adapters/openai-chat.ts +1311 -336
  86. package/src/adapters/openai-responses-url.ts +16 -0
  87. package/src/adapters/openai-responses.ts +830 -56
  88. package/src/adapters/registry.ts +175 -0
  89. package/src/adapters/responses-tool-schema.ts +67 -0
  90. package/src/adapters/tool-call-id.ts +119 -0
  91. package/src/adapters/tool-catalog-nudge.ts +104 -21
  92. package/src/adapters/xai-web-search.ts +185 -0
  93. package/src/bridge.ts +428 -61
  94. package/src/chat/inbound.ts +43 -19
  95. package/src/chat/outbound.ts +82 -26
  96. package/src/claude/agents-inject.ts +32 -9
  97. package/src/claude/context-windows.ts +21 -5
  98. package/src/claude/desktop-3p.ts +243 -9
  99. package/src/claude/gateway-cache.ts +41 -4
  100. package/src/claude/inbound.ts +72 -3
  101. package/src/claude/model-info.ts +38 -15
  102. package/src/claude/outbound.ts +70 -16
  103. package/src/cli/account-api.ts +35 -1
  104. package/src/cli/account-auth.ts +33 -6
  105. package/src/cli/account-catalog-refresh.ts +14 -0
  106. package/src/cli/account-extended.ts +389 -2
  107. package/src/cli/account-main.ts +317 -0
  108. package/src/cli/account.ts +30 -6
  109. package/src/cli/agent.ts +45 -1
  110. package/src/cli/claude-agent-startup-sync.ts +73 -0
  111. package/src/cli/claude-desktop.ts +29 -4
  112. package/src/cli/claude.ts +103 -34
  113. package/src/cli/codex-log-guard-doctor.ts +103 -0
  114. package/src/cli/codex-shim-autorestore.ts +2 -0
  115. package/src/cli/codex-shim-readiness.ts +76 -0
  116. package/src/cli/combo.ts +8 -0
  117. package/src/cli/config-command.ts +74 -10
  118. package/src/cli/dispatch.ts +593 -0
  119. package/src/cli/doctor.ts +315 -43
  120. package/src/cli/ensure-desired-integrations.ts +152 -0
  121. package/src/cli/export-command.ts +46 -20
  122. package/src/cli/help.ts +20 -271
  123. package/src/cli/index.ts +378 -519
  124. package/src/cli/init.ts +4 -17
  125. package/src/cli/integrations.ts +120 -2
  126. package/src/cli/lab.ts +607 -0
  127. package/src/cli/launcher-context.ts +77 -0
  128. package/src/cli/minimax.ts +497 -0
  129. package/src/cli/models-runtime.ts +35 -2
  130. package/src/cli/models.ts +100 -14
  131. package/src/cli/observe.ts +92 -3
  132. package/src/cli/opencode.ts +4 -2
  133. package/src/cli/provider-runtime.ts +18 -1
  134. package/src/cli/provider.ts +24 -3
  135. package/src/cli/ready.ts +301 -0
  136. package/src/cli/registry.ts +437 -0
  137. package/src/cli/root.ts +86 -0
  138. package/src/cli/route-policy.ts +92 -0
  139. package/src/cli/runtime-api.ts +6 -3
  140. package/src/cli/star-prompt.ts +71 -15
  141. package/src/cli/status.ts +10 -3
  142. package/src/cli/system-restart-client.ts +146 -0
  143. package/src/cli/tray-proxy.ts +153 -6
  144. package/src/cli/v2.ts +105 -10
  145. package/src/cli.ts +1 -1
  146. package/src/clients/config-export.ts +1358 -21
  147. package/src/codex/account-label.ts +14 -1
  148. package/src/codex/account-lifecycle.ts +130 -13
  149. package/src/codex/account-namespaces.ts +49 -3
  150. package/src/codex/account-priority.ts +83 -0
  151. package/src/codex/account-store.ts +29 -2
  152. package/src/codex/account-usability.ts +25 -2
  153. package/src/codex/admission.ts +256 -0
  154. package/src/codex/affinity-debug.ts +162 -0
  155. package/src/codex/app-server-processes.ts +493 -106
  156. package/src/codex/app-server-restart-service.ts +232 -0
  157. package/src/codex/auth-api.ts +849 -242
  158. package/src/codex/auth-collision.ts +5 -3
  159. package/src/codex/auth-context.ts +345 -32
  160. package/src/codex/autostart-health.ts +8 -1
  161. package/src/codex/catalog/account-models.ts +67 -0
  162. package/src/codex/catalog/aggregation.ts +68 -10
  163. package/src/codex/catalog/bundled.ts +331 -33
  164. package/src/codex/catalog/effort.ts +121 -30
  165. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  166. package/src/codex/catalog/kinds.ts +2 -0
  167. package/src/codex/catalog/metadata.ts +529 -45
  168. package/src/codex/catalog/native-models.ts +72 -0
  169. package/src/codex/catalog/parsing.ts +224 -30
  170. package/src/codex/catalog/provider-fetch.ts +1460 -134
  171. package/src/codex/catalog/sync.ts +1449 -186
  172. package/src/codex/catalog-admission.ts +199 -0
  173. package/src/codex/catalog-refresh-status.ts +105 -0
  174. package/src/codex/catalog-write-serialization.ts +242 -0
  175. package/src/codex/catalog.ts +6 -3
  176. package/src/codex/codex-write-lock.ts +384 -0
  177. package/src/codex/convergence-types.ts +614 -0
  178. package/src/codex/convergence.ts +651 -0
  179. package/src/codex/coordinator-doctor.ts +332 -0
  180. package/src/codex/custom-model-catalog-migration.ts +176 -0
  181. package/src/codex/desired-state.ts +230 -0
  182. package/src/codex/features.ts +636 -39
  183. package/src/codex/generation.ts +202 -0
  184. package/src/codex/history-job.ts +407 -0
  185. package/src/codex/history-lock.ts +242 -0
  186. package/src/codex/history-migration-guardian.ts +26 -20
  187. package/src/codex/history-provider.ts +231 -28
  188. package/src/codex/history-transition.ts +105 -0
  189. package/src/codex/history-worker.ts +220 -0
  190. package/src/codex/inject-coordination.ts +290 -0
  191. package/src/codex/inject.ts +1073 -152
  192. package/src/codex/injected-marker.ts +37 -3
  193. package/src/codex/integration-record.ts +266 -0
  194. package/src/codex/internal/catalog-writer.ts +203 -0
  195. package/src/codex/internal/history-writer.ts +80 -0
  196. package/src/codex/journal.ts +66 -4
  197. package/src/codex/log-guard/inspect.ts +506 -0
  198. package/src/codex/log-guard/lock.ts +150 -0
  199. package/src/codex/log-guard/maintenance.ts +403 -0
  200. package/src/codex/log-guard/path-safety.ts +88 -0
  201. package/src/codex/log-guard/policy.ts +44 -0
  202. package/src/codex/log-guard/processes.ts +205 -0
  203. package/src/codex/log-guard/protection.ts +489 -0
  204. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  205. package/src/codex/main-account-cache.ts +24 -0
  206. package/src/codex/main-account.ts +29 -1
  207. package/src/codex/management-convergence.ts +167 -0
  208. package/src/codex/model-cache.ts +56 -10
  209. package/src/codex/model-entitlements.ts +353 -0
  210. package/src/codex/native-main-admission.ts +47 -0
  211. package/src/codex/native-main-auth-temp.ts +187 -0
  212. package/src/codex/native-main-claim.ts +178 -0
  213. package/src/codex/native-main-lock-file.ts +162 -0
  214. package/src/codex/native-main-owner.ts +329 -0
  215. package/src/codex/native-profile-api.ts +247 -0
  216. package/src/codex/native-profile-manager.ts +1531 -0
  217. package/src/codex/native-profile-processes.ts +121 -0
  218. package/src/codex/native-profile-recovery.ts +99 -0
  219. package/src/codex/native-profile-stage-store.ts +387 -0
  220. package/src/codex/native-profile-startup.ts +492 -0
  221. package/src/codex/native-profile-store.ts +855 -0
  222. package/src/codex/native-profile-types.ts +120 -0
  223. package/src/codex/native-residue.ts +682 -0
  224. package/src/codex/paths.ts +80 -1
  225. package/src/codex/plan-from-token.ts +140 -0
  226. package/src/codex/plan.ts +40 -0
  227. package/src/codex/plugins-doctor.ts +1 -1
  228. package/src/codex/pool-rotation.ts +74 -4
  229. package/src/codex/project-config-warnings.ts +20 -6
  230. package/src/codex/prompt-journal.ts +352 -0
  231. package/src/codex/prompt-layers.ts +967 -0
  232. package/src/codex/prompt-lock.ts +143 -0
  233. package/src/codex/quota-rejection.ts +298 -0
  234. package/src/codex/quota.ts +175 -13
  235. package/src/codex/refresh.ts +11 -2
  236. package/src/codex/reset-credit-recovery.ts +1044 -0
  237. package/src/codex/routing.ts +505 -94
  238. package/src/codex/runtime.ts +159 -38
  239. package/src/codex/shim.ts +1009 -28
  240. package/src/codex/subagent-model-fallback.ts +350 -35
  241. package/src/codex/sync.ts +191 -2
  242. package/src/codex/transition-state.ts +612 -0
  243. package/src/codex/upstream-host-health.ts +368 -0
  244. package/src/codex/user-identity.ts +557 -0
  245. package/src/codex/warmup.ts +187 -81
  246. package/src/codex/write-coordination.ts +114 -0
  247. package/src/combos/failover.ts +20 -0
  248. package/src/combos/index.ts +4 -0
  249. package/src/combos/request.ts +32 -0
  250. package/src/combos/types.ts +81 -9
  251. package/src/config/provider-name.ts +24 -0
  252. package/src/config.ts +1762 -140
  253. package/src/generated/compatibility-version.json +3164 -0
  254. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  255. package/src/grok/inject.ts +16 -5
  256. package/src/grok/inspect.ts +45 -0
  257. package/src/grok/sync.ts +2 -2
  258. package/src/images/loop.ts +152 -29
  259. package/src/images/plan.ts +23 -13
  260. package/src/integrations/config-io.ts +269 -0
  261. package/src/integrations/journal.ts +315 -0
  262. package/src/integrations/merge.ts +135 -0
  263. package/src/integrations/mutation-flight.ts +71 -0
  264. package/src/integrations/native/ownership-preflight.ts +202 -0
  265. package/src/integrations/omp-yaml-source.ts +358 -0
  266. package/src/integrations/owned-refresh.ts +74 -0
  267. package/src/integrations/ownership.ts +111 -0
  268. package/src/integrations/registry.ts +159 -0
  269. package/src/integrations/serialize.ts +314 -0
  270. package/src/integrations/state.ts +361 -0
  271. package/src/integrations/store.ts +103 -0
  272. package/src/integrations/writer-lock.ts +98 -0
  273. package/src/integrations/writer.ts +691 -0
  274. package/src/lab/artifacts/sanitize.ts +586 -0
  275. package/src/lab/artifacts/secure-fs.ts +475 -0
  276. package/src/lab/artifacts/store.ts +310 -0
  277. package/src/lab/automation/budgets.ts +78 -0
  278. package/src/lab/automation/config-persistence.ts +256 -0
  279. package/src/lab/automation/constants.ts +39 -0
  280. package/src/lab/automation/cooldown.ts +103 -0
  281. package/src/lab/automation/dispatch.ts +211 -0
  282. package/src/lab/automation/index.ts +13 -0
  283. package/src/lab/automation/orchestrator.ts +499 -0
  284. package/src/lab/automation/persistence.ts +512 -0
  285. package/src/lab/automation/planner.ts +371 -0
  286. package/src/lab/automation/policy.ts +136 -0
  287. package/src/lab/automation/queue.ts +191 -0
  288. package/src/lab/automation/recovery.ts +24 -0
  289. package/src/lab/automation/route-context.ts +21 -0
  290. package/src/lab/automation/run-key.ts +44 -0
  291. package/src/lab/automation/runs-query.ts +34 -0
  292. package/src/lab/automation/types.ts +160 -0
  293. package/src/lab/conformance/assertion.ts +325 -0
  294. package/src/lab/conformance/digest.ts +22 -0
  295. package/src/lab/conformance/executor.ts +741 -0
  296. package/src/lab/conformance/fixture-provider.ts +27 -0
  297. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  298. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  299. package/src/lab/conformance/harness-budget.ts +47 -0
  300. package/src/lab/conformance/index.ts +5 -0
  301. package/src/lab/conformance/jcs.ts +64 -0
  302. package/src/lab/conformance/json-pointer.ts +39 -0
  303. package/src/lab/conformance/manifest.ts +180 -0
  304. package/src/lab/conformance/mcp-stub.ts +179 -0
  305. package/src/lab/conformance/negative-controls.ts +164 -0
  306. package/src/lab/conformance/observation.ts +355 -0
  307. package/src/lab/conformance/runner.ts +68 -0
  308. package/src/lab/conformance/sse-normalize.ts +59 -0
  309. package/src/lab/conformance/suite-manifest.ts +78 -0
  310. package/src/lab/conformance/types.ts +214 -0
  311. package/src/lab/constants.ts +126 -0
  312. package/src/lab/digest.ts +64 -0
  313. package/src/lab/events/errors.ts +9 -0
  314. package/src/lab/events/limits.ts +117 -0
  315. package/src/lab/events/types.ts +229 -0
  316. package/src/lab/events/validate.ts +781 -0
  317. package/src/lab/fabric/constants.ts +40 -0
  318. package/src/lab/fabric/executor.ts +492 -0
  319. package/src/lab/fabric/index.ts +80 -0
  320. package/src/lab/fabric/manifest.ts +222 -0
  321. package/src/lab/fabric/observe.ts +489 -0
  322. package/src/lab/fabric/patch.ts +79 -0
  323. package/src/lab/fabric/producer-child.ts +139 -0
  324. package/src/lab/fabric/producer-isolate.ts +276 -0
  325. package/src/lab/fabric/producer-protocol.ts +61 -0
  326. package/src/lab/fabric/scratch.ts +439 -0
  327. package/src/lab/fabric/subject.ts +106 -0
  328. package/src/lab/fabric/types.ts +134 -0
  329. package/src/lab/fabric/verifier.ts +98 -0
  330. package/src/lab/index.ts +54 -0
  331. package/src/lab/ledger/artifact-refs.ts +127 -0
  332. package/src/lab/ledger/invalidation.ts +136 -0
  333. package/src/lab/ledger/purge.ts +310 -0
  334. package/src/lab/ledger/store.ts +532 -0
  335. package/src/lab/live/credential-lease.ts +53 -0
  336. package/src/lab/live/destination.ts +155 -0
  337. package/src/lab/live/executor.ts +336 -0
  338. package/src/lab/live/inert-tools.ts +56 -0
  339. package/src/lab/live/manifest.ts +85 -0
  340. package/src/lab/live/mcp-loopback.ts +57 -0
  341. package/src/lab/live/runner.ts +19 -0
  342. package/src/lab/live/sandbox.ts +61 -0
  343. package/src/lab/live/suite-manifest.ts +41 -0
  344. package/src/lab/live/transport.ts +118 -0
  345. package/src/lab/live/types.ts +197 -0
  346. package/src/lab/observe/from-conformance.ts +301 -0
  347. package/src/lab/observe/from-live.ts +117 -0
  348. package/src/lab/paths.ts +153 -0
  349. package/src/lab/projection/rebuild.ts +495 -0
  350. package/src/lab/projection/schema.ts +135 -0
  351. package/src/lab/projection/verdicts.ts +474 -0
  352. package/src/lab/projection/verification.ts +412 -0
  353. package/src/lab/public/bundle.ts +217 -0
  354. package/src/lab/public/community-authority.ts +175 -0
  355. package/src/lab/public/community-files.ts +29 -0
  356. package/src/lab/public/community.ts +479 -0
  357. package/src/lab/public/file-safety.ts +155 -0
  358. package/src/lab/public/ids.ts +26 -0
  359. package/src/lab/public/index.ts +16 -0
  360. package/src/lab/public/mutation-lock.ts +424 -0
  361. package/src/lab/public/operator.ts +353 -0
  362. package/src/lab/public/origin-purge.ts +79 -0
  363. package/src/lab/public/origin.ts +203 -0
  364. package/src/lab/public/privacy.ts +143 -0
  365. package/src/lab/public/private-file.ts +261 -0
  366. package/src/lab/public/project.ts +124 -0
  367. package/src/lab/public/purge-test-fault.ts +21 -0
  368. package/src/lab/public/purge.ts +223 -0
  369. package/src/lab/public/registry.ts +44 -0
  370. package/src/lab/public/revocation.ts +252 -0
  371. package/src/lab/public/signature.ts +243 -0
  372. package/src/lab/public/storage.ts +105 -0
  373. package/src/lab/public/strict-json.ts +206 -0
  374. package/src/lab/public/time.ts +26 -0
  375. package/src/lab/public/types.ts +172 -0
  376. package/src/lab/public/validate.ts +391 -0
  377. package/src/lab/query/catalog.ts +101 -0
  378. package/src/lab/query/connection.ts +107 -0
  379. package/src/lab/query/constants.ts +4 -0
  380. package/src/lab/query/cursor.ts +132 -0
  381. package/src/lab/query/dto-map.ts +277 -0
  382. package/src/lab/query/errors.ts +22 -0
  383. package/src/lab/query/freshness.ts +53 -0
  384. package/src/lab/query/index.ts +45 -0
  385. package/src/lab/query/latest-observation.ts +59 -0
  386. package/src/lab/query/passive-production.ts +159 -0
  387. package/src/lab/query/queries.ts +444 -0
  388. package/src/lab/query/types.ts +266 -0
  389. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  390. package/src/lab/subject/installation-salt.ts +112 -0
  391. package/src/lab/subject/protocol-subject.ts +80 -0
  392. package/src/lab/subject/route-subject.ts +74 -0
  393. package/src/lib/app-owned-memory-stores.ts +22 -0
  394. package/src/lib/bounded-body.ts +153 -9
  395. package/src/lib/bun-runtime.ts +125 -12
  396. package/src/lib/bun-stream-caps.ts +13 -9
  397. package/src/lib/codex-restart-contract.ts +120 -0
  398. package/src/lib/config-ownership.ts +6 -2
  399. package/src/lib/destination-policy.ts +65 -1
  400. package/src/lib/errors.ts +44 -2
  401. package/src/lib/fabric-task-execution-authority.ts +7 -0
  402. package/src/lib/fabric-task-host.ts +29 -0
  403. package/src/lib/lab-activation.ts +223 -0
  404. package/src/lib/lab-live-execution-authority.ts +13 -0
  405. package/src/lib/lab-live-host.ts +30 -0
  406. package/src/lib/lab-live-pinned-sender.ts +56 -0
  407. package/src/lib/lab-live-route-production.ts +130 -0
  408. package/src/lib/lab-passive-linker-registration.ts +26 -0
  409. package/src/lib/local-management-attestation.ts +51 -0
  410. package/src/lib/local-management-capability.ts +100 -0
  411. package/src/lib/local-provider-reload-contract.ts +100 -0
  412. package/src/lib/optional-shutdown-hooks.ts +57 -0
  413. package/src/lib/pinned-http.ts +145 -26
  414. package/src/lib/process-control.ts +4 -1
  415. package/src/lib/provider-outbound.ts +49 -9
  416. package/src/lib/redact.ts +419 -3
  417. package/src/lib/self-launch-argv.ts +15 -0
  418. package/src/lib/server-resource-ownership.ts +71 -0
  419. package/src/lib/shadow-call.ts +35 -4
  420. package/src/lib/sse-decoder.ts +41 -0
  421. package/src/lib/state-store-registrations.ts +10 -2
  422. package/src/lib/system-restart-contract.ts +73 -0
  423. package/src/lib/token-estimate.ts +19 -2
  424. package/src/lib/tool-argument-integers.ts +202 -0
  425. package/src/lib/translator-budget.ts +44 -0
  426. package/src/lib/upstream-http-version.ts +57 -0
  427. package/src/lib/upstream-reachability.ts +95 -0
  428. package/src/lib/upstream-retry.ts +156 -3
  429. package/src/lib/windows-atomic-replace.ts +155 -0
  430. package/src/lib/windows-elevation.ts +70 -2
  431. package/src/lib/windows-secret-acl.ts +409 -69
  432. package/src/lib/windows-service-wrappers.ts +72 -0
  433. package/src/lib/windows-text.ts +106 -0
  434. package/src/lib/windows-user-principal.ts +341 -0
  435. package/src/lib/winsw.ts +33 -5
  436. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  437. package/src/oauth/account-import/index.ts +15 -0
  438. package/src/oauth/account-import/parser.ts +83 -0
  439. package/src/oauth/account-import/registry.ts +18 -0
  440. package/src/oauth/account-import/service.ts +75 -0
  441. package/src/oauth/account-import/types.ts +91 -0
  442. package/src/oauth/anthropic.ts +12 -1
  443. package/src/oauth/callback-server.ts +8 -2
  444. package/src/oauth/chatgpt.ts +12 -1
  445. package/src/oauth/command-code.ts +239 -0
  446. package/src/oauth/cursor.ts +46 -5
  447. package/src/oauth/google-antigravity.ts +35 -3
  448. package/src/oauth/health.ts +20 -12
  449. package/src/oauth/index.ts +398 -66
  450. package/src/oauth/key-providers.ts +16 -0
  451. package/src/oauth/kimi.ts +16 -2
  452. package/src/oauth/kiro.ts +50 -6
  453. package/src/oauth/local-token-detect.ts +11 -2
  454. package/src/oauth/log.ts +3 -1
  455. package/src/oauth/login-cli.ts +88 -28
  456. package/src/oauth/nous.ts +798 -0
  457. package/src/oauth/store.ts +119 -21
  458. package/src/oauth/token-guardian.ts +9 -3
  459. package/src/pi/models.ts +2 -2
  460. package/src/providers/alibaba-region-migration.ts +1 -1
  461. package/src/providers/antigravity-models.ts +521 -31
  462. package/src/providers/base-url-choices.ts +10 -0
  463. package/src/providers/codex-capacity.ts +292 -0
  464. package/src/providers/command-code-efforts.ts +144 -0
  465. package/src/providers/context-cap.ts +22 -5
  466. package/src/providers/cursor-pool.ts +72 -0
  467. package/src/providers/derive.ts +253 -6
  468. package/src/providers/fastwire.ts +501 -0
  469. package/src/providers/free-directory.ts +10 -7
  470. package/src/providers/google-vertex-location.ts +14 -0
  471. package/src/providers/key-failover.ts +71 -3
  472. package/src/providers/label.ts +1 -1
  473. package/src/providers/model-discovery-limits.ts +16 -0
  474. package/src/providers/model-discovery.ts +115 -22
  475. package/src/providers/model-rename-migration.ts +255 -0
  476. package/src/providers/model-rename-startup.ts +28 -0
  477. package/src/providers/openai-sidecar.ts +72 -4
  478. package/src/providers/openai-tier-startup.ts +31 -2
  479. package/src/providers/openai-tiers.ts +119 -4
  480. package/src/providers/openai-virtual-models.ts +1 -0
  481. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  482. package/src/providers/provider-id-rewrite.ts +29 -0
  483. package/src/providers/quota.ts +1319 -38
  484. package/src/providers/registry.ts +1429 -111
  485. package/src/providers/request-pacing.ts +310 -0
  486. package/src/providers/service-tier.ts +277 -0
  487. package/src/providers/slug-codec.ts +42 -6
  488. package/src/providers/static-model-discovery.ts +86 -0
  489. package/src/providers/xai-responses-opt-in.ts +15 -0
  490. package/src/providers/xai-transport.ts +11 -4
  491. package/src/reasoning-effort.ts +49 -1
  492. package/src/responses/compaction.ts +26 -1
  493. package/src/responses/custom-tool-compat.ts +266 -0
  494. package/src/responses/hosted-tool-policy.ts +9 -0
  495. package/src/responses/namespace-tool-compat.ts +355 -0
  496. package/src/responses/parser.ts +220 -38
  497. package/src/responses/provider-continuation.ts +98 -0
  498. package/src/responses/provider-opaque-metadata.ts +73 -0
  499. package/src/responses/reasoning-envelope.ts +9 -1
  500. package/src/responses/reasoning-replay-cache.ts +426 -0
  501. package/src/responses/schema.ts +7 -1
  502. package/src/responses/spill-store.ts +75 -10
  503. package/src/responses/state.ts +565 -27
  504. package/src/responses/thought-signature-replay.ts +347 -0
  505. package/src/responses/tool-search-compat.ts +301 -0
  506. package/src/responses/truncated-stop-reason.ts +60 -0
  507. package/src/router.ts +366 -30
  508. package/src/routing/analytics.ts +378 -0
  509. package/src/routing/capability.ts +244 -0
  510. package/src/routing/compatibility/assemble.ts +73 -0
  511. package/src/routing/compatibility/behavior.ts +278 -0
  512. package/src/routing/compatibility/catalog.ts +99 -0
  513. package/src/routing/compatibility/endpoint.ts +52 -0
  514. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  515. package/src/routing/compatibility/policy.ts +181 -0
  516. package/src/routing/compatibility/provider-slot.ts +56 -0
  517. package/src/routing/compatibility/reader.ts +110 -0
  518. package/src/routing/compatibility/subject.ts +191 -0
  519. package/src/routing/compatibility/types.ts +64 -0
  520. package/src/routing/compatibility/version.ts +104 -0
  521. package/src/routing/cost.ts +77 -0
  522. package/src/routing/evaluator.ts +495 -0
  523. package/src/routing/health.ts +412 -0
  524. package/src/routing/history/cursor.ts +43 -0
  525. package/src/routing/history/indexer.ts +605 -0
  526. package/src/routing/history/schema.ts +72 -0
  527. package/src/routing/profile-namespace.ts +15 -0
  528. package/src/routing/profile.ts +547 -0
  529. package/src/routing/quota.ts +145 -0
  530. package/src/routing/request-evidence.ts +45 -0
  531. package/src/routing/trace.ts +776 -0
  532. package/src/server/adapter-resolve.ts +2 -29
  533. package/src/server/auth-cors.ts +267 -46
  534. package/src/server/background-lifecycle.ts +182 -0
  535. package/src/server/chat-completions.ts +130 -56
  536. package/src/server/chat-native-sse.ts +331 -0
  537. package/src/server/chat-native.ts +426 -0
  538. package/src/server/claude-messages.ts +159 -43
  539. package/src/server/direct-local-http.ts +347 -0
  540. package/src/server/effort-policy.ts +18 -0
  541. package/src/server/github-copilot-responses-repair.ts +338 -0
  542. package/src/server/gui-static.ts +39 -10
  543. package/src/server/images.ts +94 -12
  544. package/src/server/index.ts +865 -181
  545. package/src/server/lifecycle.ts +284 -13
  546. package/src/server/live.ts +136 -17
  547. package/src/server/local-management-read-client.ts +90 -0
  548. package/src/server/local-provider-reload-client.ts +137 -0
  549. package/src/server/management/agent-settings-routes.ts +398 -103
  550. package/src/server/management/api-key-usage.ts +31 -5
  551. package/src/server/management/body.ts +6 -0
  552. package/src/server/management/combo-routes.ts +62 -24
  553. package/src/server/management/config-routes.ts +464 -51
  554. package/src/server/management/context.ts +80 -2
  555. package/src/server/management/integration-routes.ts +498 -0
  556. package/src/server/management/lab-automation-routes.ts +206 -0
  557. package/src/server/management/lab-routes.ts +563 -0
  558. package/src/server/management/logs-usage-routes.ts +101 -32
  559. package/src/server/management/model-routes.ts +189 -131
  560. package/src/server/management/model-rows.ts +163 -0
  561. package/src/server/management/native-integration-routes.ts +769 -0
  562. package/src/server/management/oauth-account-routes.ts +80 -4
  563. package/src/server/management/provider-capability-config.ts +48 -0
  564. package/src/server/management/provider-routes.ts +764 -157
  565. package/src/server/management/request-history-routes.ts +191 -0
  566. package/src/server/management/routing-analytics-routes.ts +74 -0
  567. package/src/server/management/routing-profile-routes.ts +380 -0
  568. package/src/server/management/shared.ts +27 -11
  569. package/src/server/management/sidebar-routes.ts +47 -31
  570. package/src/server/management/storage-log-guard-routes.ts +186 -0
  571. package/src/server/management/sync-response.ts +69 -0
  572. package/src/server/management/system-restart.ts +288 -32
  573. package/src/server/management/system-routes.ts +77 -0
  574. package/src/server/management/usage-summary-cache.ts +9 -1
  575. package/src/server/management/vision-sidecar-options.ts +167 -0
  576. package/src/server/management/web-search-sidecar-options.ts +120 -0
  577. package/src/server/management-api.ts +115 -14
  578. package/src/server/management-auth.ts +220 -5
  579. package/src/server/passive-route-linker.ts +66 -0
  580. package/src/server/ports.ts +41 -1
  581. package/src/server/proxy-liveness.ts +132 -5
  582. package/src/server/readiness.ts +99 -0
  583. package/src/server/relay-eager.ts +82 -42
  584. package/src/server/relay.ts +236 -76
  585. package/src/server/request-decompress.ts +113 -6
  586. package/src/server/request-log.ts +235 -22
  587. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  588. package/src/server/responses/agent-task-recovery.ts +465 -0
  589. package/src/server/responses/collaboration.ts +204 -35
  590. package/src/server/responses/compact.ts +442 -55
  591. package/src/server/responses/core.ts +2872 -331
  592. package/src/server/responses/empty-completion-guard.ts +276 -0
  593. package/src/server/responses/encrypted-payload.ts +62 -39
  594. package/src/server/responses/fetch-helpers.ts +79 -4
  595. package/src/server/responses/input-admission.ts +185 -0
  596. package/src/server/responses/pacing-overload.ts +13 -0
  597. package/src/server/responses/policy-fallback.ts +178 -0
  598. package/src/server/responses/responses-field-backfill.ts +251 -0
  599. package/src/server/responses/terminal-guard.ts +26 -5
  600. package/src/server/responses/upstream-error.ts +5 -0
  601. package/src/server/responses/ws-upstream.ts +308 -0
  602. package/src/server/responses-custom-tool-repair.ts +282 -0
  603. package/src/server/responses-item-id-repair.ts +54 -6
  604. package/src/server/responses-json-events.ts +90 -0
  605. package/src/server/responses-model-rewrite.ts +29 -0
  606. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  607. package/src/server/responses-snapshot-repair.ts +621 -0
  608. package/src/server/responses-terminal-repair.ts +342 -0
  609. package/src/server/responses-tool-search-repair.ts +267 -0
  610. package/src/server/responses-undeclared-tool-guard.ts +153 -0
  611. package/src/server/responses.ts +18 -2
  612. package/src/server/search.ts +78 -13
  613. package/src/server/sse-frame-buffer.ts +292 -0
  614. package/src/server/sse-payload-rewrite.ts +110 -22
  615. package/src/server/startup-action-control.ts +8 -1
  616. package/src/server/startup-health-cache.ts +19 -1
  617. package/src/server/system-env.ts +80 -9
  618. package/src/server/ws-bridge.ts +39 -38
  619. package/src/service-manager-probe.ts +892 -0
  620. package/src/service.ts +1111 -90
  621. package/src/sidecar/auth.ts +92 -0
  622. package/src/sidecar/candidates.ts +83 -0
  623. package/src/storage/cleanup.ts +2 -2
  624. package/src/storage/scanner.ts +1 -1
  625. package/src/storage/worker-lifecycle.ts +14 -14
  626. package/src/tray/windows-tray.ps1 +83 -9
  627. package/src/tray/windows.ts +43 -16
  628. package/src/types/accounts.ts +37 -0
  629. package/src/types/config.ts +845 -0
  630. package/src/types/provider.ts +545 -0
  631. package/src/types/request.ts +384 -0
  632. package/src/types/tools.ts +131 -0
  633. package/src/types/wire.ts +80 -0
  634. package/src/types.ts +104 -1236
  635. package/src/update/index.ts +32 -19
  636. package/src/update/job.ts +442 -67
  637. package/src/update/notify.ts +12 -6
  638. package/src/update/npm-cache-preflight.d.mts +47 -0
  639. package/src/update/npm-cache-preflight.mjs +201 -0
  640. package/src/update/transactional-install.d.mts +22 -0
  641. package/src/update/transactional-install.mjs +259 -0
  642. package/src/usage/cost.ts +0 -0
  643. package/src/usage/expected-prices.ts +268 -16
  644. package/src/usage/log.ts +606 -41
  645. package/src/usage/summary.ts +177 -9
  646. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  647. package/src/usage/user-cost-overlays.ts +314 -0
  648. package/src/vision/anthropic-describe.ts +10 -6
  649. package/src/vision/backends.ts +97 -0
  650. package/src/vision/describe.ts +9 -3
  651. package/src/vision/eligibility.ts +250 -0
  652. package/src/vision/index.ts +238 -24
  653. package/src/vision/reasoning.ts +55 -0
  654. package/src/vision/routed-describe.ts +175 -0
  655. package/src/vision/timeout-bounds.ts +9 -0
  656. package/src/web-search/anthropic-executor.ts +13 -7
  657. package/src/web-search/backends.ts +108 -0
  658. package/src/web-search/exa-executor.ts +88 -0
  659. package/src/web-search/executor.ts +11 -3
  660. package/src/web-search/gemini-executor.ts +141 -0
  661. package/src/web-search/index.ts +150 -15
  662. package/src/web-search/loop.ts +279 -50
  663. package/src/web-search/parse.ts +125 -30
  664. package/src/web-search/sources.ts +60 -0
  665. package/src/web-search/xai-executor.ts +219 -0
  666. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  667. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  668. package/gui/dist/provider-icons/antigravity.svg +0 -1
  669. package/gui/dist/provider-icons/claude.svg +0 -1
  670. package/gui/dist/provider-icons/copilot.svg +0 -1
  671. package/gui/dist/provider-icons/cursor.svg +0 -2
  672. package/gui/dist/provider-icons/gemini.svg +0 -1
  673. package/gui/dist/provider-icons/grok-color.svg +0 -1
  674. package/gui/dist/provider-icons/kiro.svg +0 -14
  675. package/src/cli/internal-dispatch.ts +0 -20
@@ -0,0 +1,601 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { execFile as execFileCallback } from "node:child_process";
3
+ import { promisify } from "node:util";
4
+ import { opendir } from "node:fs/promises";
5
+ import type { AdapterEvent, OcxContentPart, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxTool, OcxUsage } from "../types";
6
+ import { isAllowedToolChoice, namespacedToolName, resolveToolChoiceWireName, toolAllowedByChoice } from "../types";
7
+ import type { AdapterFetchContext, AdapterRequest, ProviderAdapter } from "./base";
8
+ import type { TranslatorBudget } from "../lib/translator-budget";
9
+ import { readBoundedResponseBody } from "../lib/bounded-body";
10
+ import { configuredReasoningEfforts } from "../reasoning-effort";
11
+ import { commandCodeReasoningEfforts, refreshCommandCodeReasoningEfforts } from "../providers/command-code-efforts";
12
+ import { identifyRoutedModel } from "./identity";
13
+ import { buildNonOpenAIToolCatalogNudgeForTools } from "./tool-catalog-nudge";
14
+ import { parseDataUrl } from "./image";
15
+
16
+ // Retain the short ids emitted by the first local integration. New requests use the live catalog's
17
+ // provider-native IDs directly; this map is compatibility-only and is not a model fallback list.
18
+ const COMMAND_CODE_MODEL_ALIASES: Readonly<Record<string, string>> = {
19
+ "deepseek-v4-flash": "deepseek/deepseek-v4-flash",
20
+ "kimi-k3": "moonshotai/Kimi-K3",
21
+ "glm-5.3": "zai-org/GLM-5.3",
22
+ "glm-5.2": "zai-org/GLM-5.2",
23
+ };
24
+
25
+ function canonicalCommandCodeModelId(modelId: string): string {
26
+ return Object.hasOwn(COMMAND_CODE_MODEL_ALIASES, modelId) ? COMMAND_CODE_MODEL_ALIASES[modelId]! : modelId;
27
+ }
28
+
29
+ /** Flatten tool-result content for the text-only wire output, keeping an `[image]` marker per image part in content order. */
30
+ function toolResultText(content: string | OcxContentPart[]): string {
31
+ if (typeof content === "string") return content;
32
+ return content.map(part => (part.type === "text" ? part.text : "[image]")).join("");
33
+ }
34
+
35
+ /** Best-effort media type from a remote https URL extension, e.g. image/png. */
36
+ function mediaTypeFromUrl(url: string): string | undefined {
37
+ const ext = url.split(/[?#]/)[0]!.match(/\.([a-zA-Z0-9]+)$/)?.[1]?.toLowerCase();
38
+ if (!ext) return undefined;
39
+ const known: Record<string, string> = {
40
+ png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", bmp: "image/bmp",
41
+ };
42
+ return known[ext];
43
+ }
44
+
45
+ /** Wire image part: `{ type, image, mediaType? }` per the /alpha/generate ModelMessage schema. */
46
+ function wireImagePart(imageUrl: string): Record<string, unknown> {
47
+ const parsed = parseDataUrl(imageUrl);
48
+ const mediaType = parsed?.mediaType ?? mediaTypeFromUrl(imageUrl);
49
+ return { type: "image", image: imageUrl, ...(mediaType ? { mediaType } : {}) };
50
+ }
51
+
52
+ /**
53
+ * The /alpha/generate wire pairs every assistant `tool-call` with a following `tool` message
54
+ * whose `tool-result.toolCallId` matches it. Codex history is not guaranteed to carry that
55
+ * pairing (interrupted turns, compacted threads, and multi-step tool rounds all can leave an
56
+ * assistant call with no recorded result), and the upstream rejects an unpaired call with
57
+ * `Tool result is missing for tool call <id>`, which currently surfaces as a generic 502
58
+ * (#1383). This builder keeps the pairing invariant:
59
+ *
60
+ * - a `toolResult` that matches a declared assistant call emits the native `tool-result`;
61
+ * - a `toolResult` with no matching declared call degrades to a text carrier so the model
62
+ * still sees the outcome without a 400-prone standalone `tool` message;
63
+ * - every declared assistant call that never received a result gets an explicit error
64
+ * `tool-result`, so the upstream never sees an unpaired call.
65
+ */
66
+ function wireMessages(messages: OcxMessage[]): Array<Record<string, unknown>> {
67
+ const out: Array<Record<string, unknown>> = [];
68
+ const pendingCalls: Array<{ id: string; name: string }> = [];
69
+ // Image parts returned by a tool cannot live inside the text-only `tool-result` wire
70
+ // output. They ride a follow-up user message, but that user message must not break the
71
+ // adjacency of the assistant turn's tool results, so carriers are buffered and flushed
72
+ // only after every declared call has its native or synthesized result.
73
+ const pendingImageCarriers: Array<Record<string, unknown>> = [];
74
+ // The /alpha/generate wire requires every assistant tool-call to be closed by a matching
75
+ // `tool-result` immediately after the declaring assistant message. Close any declared call
76
+ // that never received a result before a non-toolResult message moves the turn forward, so a
77
+ // synthesized `tool` message never lands after a user message or a new assistant turn.
78
+ const closePendingCalls = (): void => {
79
+ for (const call of pendingCalls) {
80
+ out.push({ role: "tool", content: [{
81
+ type: "tool-result",
82
+ toolCallId: call.id,
83
+ toolName: call.name,
84
+ output: { type: "error-text", value: "[ocx] no tool result was recorded for this tool call; execution status unknown." },
85
+ }] });
86
+ }
87
+ pendingCalls.length = 0;
88
+ if (pendingImageCarriers.length > 0) {
89
+ out.push(...pendingImageCarriers);
90
+ pendingImageCarriers.length = 0;
91
+ }
92
+ };
93
+ for (const message of messages) {
94
+ if (message.role === "assistant") {
95
+ closePendingCalls();
96
+ const content: Array<Record<string, unknown>> = [];
97
+ for (const part of message.content) {
98
+ if (part.type === "text") content.push({ type: "text", text: part.text });
99
+ else if (part.type === "thinking") content.push({ type: "reasoning", text: part.thinking });
100
+ else {
101
+ const wireName = namespacedToolName(part.namespace, part.name);
102
+ content.push({ type: "tool-call", toolCallId: part.id, toolName: wireName, input: part.arguments });
103
+ pendingCalls.push({ id: part.id, name: wireName });
104
+ }
105
+ }
106
+ out.push({ role: "assistant", content });
107
+ continue;
108
+ }
109
+ if (message.role === "toolResult") {
110
+ const callIndex = pendingCalls.findIndex(call => call.id === message.toolCallId);
111
+ const paired = callIndex >= 0;
112
+ if (paired) pendingCalls.splice(callIndex, 1);
113
+ if (!paired) {
114
+ // Pending calls from an earlier assistant turn must still be closed before any user
115
+ // message lands, or their synthesized results would follow the orphan carrier.
116
+ closePendingCalls();
117
+ // The upstream rejects a standalone tool message whose call was never declared by an
118
+ // assistant turn. Preserve the outcome as text so the model can still act on it.
119
+ const label = message.toolName ? `${message.toolName} (${message.toolCallId})` : message.toolCallId;
120
+ const text = toolResultText(message.content);
121
+ // The orphan result cannot ride a `tool` message; carry it in a user message instead.
122
+ out.push({ role: "user", content: [{ type: "text", text: `[tool result without adjacent tool call: ${label}]\n${text}` }] });
123
+ continue;
124
+ }
125
+ out.push({ role: "tool", content: [{
126
+ type: "tool-result",
127
+ toolCallId: message.toolCallId,
128
+ toolName: namespacedToolName(message.toolNamespace, message.toolName),
129
+ output: { type: message.isError ? "error-text" : "text", value: toolResultText(message.content) },
130
+ }] });
131
+ // The proprietary wire's tool-result output is text-only; image parts returned by a
132
+ // tool (e.g. Codex view_image) cannot live inside it. Carry them in a follow-up user
133
+ // message using the same image encoding as the user branch so the bytes reach the model.
134
+ const images = typeof message.content === "string" ? [] : message.content.filter(part => part.type === "image");
135
+ if (images.length > 0) {
136
+ pendingImageCarriers.push({ role: "user", content: images.map(part => wireImagePart((part as { imageUrl: string }).imageUrl)) });
137
+ }
138
+ continue;
139
+ }
140
+ // User/developer message: no pending tool results may follow it on the wire.
141
+ closePendingCalls();
142
+ const content: Array<Record<string, unknown>> = [];
143
+ if (typeof message.content === "string") content.push({ type: "text", text: message.content });
144
+ else for (const part of message.content) {
145
+ if (part.type === "text") content.push({ type: "text", text: part.text });
146
+ else content.push(wireImagePart(part.imageUrl));
147
+ }
148
+ out.push({ role: "user", content });
149
+ }
150
+ closePendingCalls();
151
+ return out;
152
+ }
153
+
154
+ function visibleTools(parsed: OcxParsedRequest): OcxTool[] {
155
+ const choice = parsed.options.toolChoice;
156
+ if (choice === "none") return [];
157
+ const tools = parsed.context.tools ?? [];
158
+ if (isAllowedToolChoice(choice)) {
159
+ const allowed = new Set(choice.allowedTools);
160
+ return tools.filter(tool => toolAllowedByChoice(tool, allowed, tools));
161
+ }
162
+ if (choice && typeof choice !== "string") {
163
+ const selected = resolveToolChoiceWireName(tools, choice.name);
164
+ return tools.filter(tool => namespacedToolName(tool.namespace, tool.name) === selected);
165
+ }
166
+ return tools;
167
+ }
168
+
169
+ function toolChoiceInstruction(parsed: OcxParsedRequest): string | undefined {
170
+ const choice = parsed.options.toolChoice;
171
+ if (choice === "required" || (isAllowedToolChoice(choice) && choice.mode === "required")) {
172
+ return "Tool choice is required for this turn. Make at least one call from the advertised tool catalog before answering.";
173
+ }
174
+ if (choice && typeof choice !== "string" && !isAllowedToolChoice(choice)) {
175
+ // Resolve the forced name (bare, namespace__name, or namespace.name) to the advertised
176
+ // wire name so the instruction names a tool that is actually in the catalog.
177
+ const wireName = resolveToolChoiceWireName(parsed.context.tools, choice.name);
178
+ return `Tool choice is required for this turn. Call the advertised tool named ${wireName} before answering.`;
179
+ }
180
+ return undefined;
181
+ }
182
+
183
+ function wireTools(tools: OcxTool[]): Array<Record<string, unknown>> {
184
+ return tools.map(tool => ({
185
+ name: namespacedToolName(tool.namespace, tool.name),
186
+ description: tool.description,
187
+ input_schema: tool.parameters,
188
+ }));
189
+ }
190
+
191
+ function currentWorkingDirectory(): string | undefined {
192
+ try { return process.cwd(); } catch { return undefined; }
193
+ }
194
+
195
+ /** Cap the workspace listing so a large directory does not ship every entry name upstream. */
196
+ const MAX_WORKSPACE_STRUCTURE_ENTRIES = 64;
197
+ /** Cap how many recent commit subjects the config carries. */
198
+ const MAX_RECENT_COMMITS = 8;
199
+ /** Cap each recent commit entry to keep the request bounded even for long subjects. */
200
+ const MAX_RECENT_COMMIT_LENGTH = 512;
201
+ /** Cap the git status text sent upstream. */
202
+ const MAX_GIT_STATUS_LENGTH = 2048;
203
+ /** Keep collected workspace/git metadata fresh for this long (ms) so repeated requests reuse it. */
204
+ const WORKSPACE_METADATA_TTL_MS = 30_000;
205
+ /** Hard cap on cached workspace metadata entries to prevent unbounded growth across distinct cwds. */
206
+ export const MAX_WORKSPACE_METADATA_ENTRIES = 128;
207
+
208
+ /** Derive a bounded project slug from the working directory for the `x-project-slug` header. */
209
+ function projectSlug(cwd: string): string {
210
+ return cwd.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "").toLowerCase().slice(0, 64) || "workspace";
211
+ }
212
+
213
+ interface GitWorkspaceInfo {
214
+ isGitRepo: boolean;
215
+ currentBranch: string;
216
+ mainBranch: string;
217
+ gitStatus: string;
218
+ recentCommits: string[];
219
+ }
220
+
221
+ export const workspaceMetadataCache = new Map<string, { collectedAt: number; value: GitWorkspaceInfo }>();
222
+
223
+ /**
224
+ * Evict expired entries first, then the oldest live entry if at capacity.
225
+ * Called before inserting a new key so the cache never exceeds the cap.
226
+ */
227
+ export function pruneWorkspaceMetadataCache(now: number): void {
228
+ // Pass 1: remove expired entries.
229
+ for (const [key, entry] of workspaceMetadataCache) {
230
+ if (now - entry.collectedAt >= WORKSPACE_METADATA_TTL_MS) {
231
+ workspaceMetadataCache.delete(key);
232
+ }
233
+ }
234
+ // Pass 2: if still at capacity, evict the oldest live entry.
235
+ if (workspaceMetadataCache.size >= MAX_WORKSPACE_METADATA_ENTRIES) {
236
+ let oldestKey: string | null = null;
237
+ let oldestAt = Infinity;
238
+ for (const [key, entry] of workspaceMetadataCache) {
239
+ if (entry.collectedAt < oldestAt) {
240
+ oldestAt = entry.collectedAt;
241
+ oldestKey = key;
242
+ }
243
+ }
244
+ if (oldestKey !== null) workspaceMetadataCache.delete(oldestKey);
245
+ }
246
+ }
247
+
248
+ const execFile = promisify(execFileCallback);
249
+
250
+ /** Best-effort git metadata for the upstream config contract; every read fails safe and stays off the event loop. */
251
+ async function gitWorkspaceInfo(cwd: string | undefined): Promise<GitWorkspaceInfo> {
252
+ const fallback: GitWorkspaceInfo = { isGitRepo: false, currentBranch: "", mainBranch: "", gitStatus: "", recentCommits: [] };
253
+ if (!cwd) return fallback;
254
+ const cached = workspaceMetadataCache.get(cwd);
255
+ if (cached && Date.now() - cached.collectedAt < WORKSPACE_METADATA_TTL_MS) return cached.value;
256
+ const run = async (args: string[]): Promise<string> => {
257
+ try {
258
+ const { stdout } = await execFile("git", args, { cwd, encoding: "utf8", timeout: 2000, windowsHide: true });
259
+ return stdout.trim();
260
+ } catch {
261
+ return "";
262
+ }
263
+ };
264
+ const root = await run(["rev-parse", "--show-toplevel"]);
265
+ const value = root
266
+ ? {
267
+ isGitRepo: true,
268
+ currentBranch: (await run(["rev-parse", "--abbrev-ref", "HEAD"])) || "HEAD",
269
+ mainBranch: (await run(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"])).replace(/^origin\//, "") || (await run(["rev-parse", "--abbrev-ref", "HEAD"])) || "",
270
+ gitStatus: (await run(["status", "--porcelain"])).slice(0, MAX_GIT_STATUS_LENGTH),
271
+ recentCommits: (await run(["log", "--oneline", `-${MAX_RECENT_COMMITS}`]))
272
+ .split("\n")
273
+ .filter(Boolean)
274
+ .slice(0, MAX_RECENT_COMMITS)
275
+ .map(commit => commit.slice(0, MAX_RECENT_COMMIT_LENGTH)),
276
+ }
277
+ : fallback;
278
+ const now = Date.now();
279
+ if (!workspaceMetadataCache.has(cwd)) pruneWorkspaceMetadataCache(now);
280
+ workspaceMetadataCache.set(cwd, { collectedAt: now, value });
281
+ return value;
282
+ }
283
+
284
+ async function commandCodeConfig(cwd: string | undefined): Promise<Record<string, unknown>> {
285
+ let structure: string[] = [];
286
+ if (cwd) {
287
+ try {
288
+ // Iterate and stop after the cap instead of materializing every entry: a directory with a
289
+ // huge number of names must not stall the request path for 64 metadata rows.
290
+ const dir = await opendir(cwd);
291
+ try {
292
+ for await (const entry of dir) {
293
+ if (entry.name.startsWith(".")) continue;
294
+ structure.push(entry.name);
295
+ if (structure.length >= MAX_WORKSPACE_STRUCTURE_ENTRIES) break;
296
+ }
297
+ } finally {
298
+ await dir.close().catch(() => undefined);
299
+ }
300
+ } catch { /* workspace metadata is optional */ }
301
+ }
302
+ const git = await gitWorkspaceInfo(cwd);
303
+ return {
304
+ ...(cwd ? { workingDir: cwd } : {}),
305
+ date: new Date().toISOString().slice(0, 10),
306
+ environment: process.platform,
307
+ structure,
308
+ ...git,
309
+ };
310
+ }
311
+
312
+ function usage(value: unknown): OcxUsage | undefined {
313
+ if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
314
+ const row = value as Record<string, unknown>;
315
+ const inputTokens = typeof row.inputTokens === "number" ? row.inputTokens : 0;
316
+ const outputTokens = typeof row.outputTokens === "number" ? row.outputTokens : 0;
317
+ const details = row.inputTokenDetails && typeof row.inputTokenDetails === "object" && !Array.isArray(row.inputTokenDetails)
318
+ ? row.inputTokenDetails as Record<string, unknown> : {};
319
+ const cachedInputTokens = typeof details.cacheReadTokens === "number" ? details.cacheReadTokens : undefined;
320
+ const cacheCreationInputTokens = typeof details.cacheWriteTokens === "number" ? details.cacheWriteTokens : undefined;
321
+ return {
322
+ inputTokens, outputTokens, totalTokens: inputTokens + outputTokens,
323
+ ...(cachedInputTokens !== undefined ? { cachedInputTokens, cacheReadInputTokens: cachedInputTokens } : {}),
324
+ ...(cacheCreationInputTokens !== undefined ? { cacheCreationInputTokens } : {}),
325
+ };
326
+ }
327
+
328
+ function eventError(value: unknown): string {
329
+ if (typeof value === "string" && value) return value;
330
+ if (value && typeof value === "object" && !Array.isArray(value)) {
331
+ const message = (value as Record<string, unknown>).message;
332
+ if (typeof message === "string" && message) return message;
333
+ }
334
+ return "Command Code stream error";
335
+ }
336
+
337
+ /**
338
+ * True when the upstream rejected a tool-result continuation because an assistant tool call
339
+ * had no matching result (`Tool result is missing for tool call <id>`). The proxy now keeps
340
+ * that pairing invariant before sending, so this error is a distinct provider-side
341
+ * validation failure rather than a generic stream fault; classify it as such for the
342
+ * dashboard/logs instead of a plain upstream 502.
343
+ */
344
+ function isMissingToolResultError(value: unknown): boolean {
345
+ const text = eventError(value).toLowerCase();
346
+ return text.includes("tool result is missing") || text.includes("tool_result is missing");
347
+ }
348
+
349
+ async function*ndjson(response: Response, budget: TranslatorBudget): AsyncGenerator<Record<string, unknown>> {
350
+ if (!response.body) throw new Error("Command Code response body missing");
351
+ const reader = response.body.getReader();
352
+ const decoder = new TextDecoder();
353
+ const encoder = new TextEncoder();
354
+ let buffer = "";
355
+ let bufferBytes = 0;
356
+ try {
357
+ for (;;) {
358
+ const { value, done } = await reader.read();
359
+ const next = buffer + decoder.decode(value, { stream: !done });
360
+ const nextBytes = encoder.encode(next).byteLength;
361
+ const reservation = budget.reserveTransient(nextBytes, { kind: "live_transient" });
362
+ buffer = next;
363
+ reservation.commitRetained();
364
+ budget.releaseRetained(bufferBytes, { kind: "live_transient" });
365
+ bufferBytes = nextBytes;
366
+ let newline = buffer.indexOf("\n");
367
+ while (newline >= 0) {
368
+ const line = buffer.slice(0, newline).trim(); buffer = buffer.slice(newline + 1);
369
+ if (line) { try { yield JSON.parse(stripEventFrame(line)) as Record<string, unknown>; } catch { /* ignore non-events */ } }
370
+ newline = buffer.indexOf("\n");
371
+ }
372
+ const residualBytes = encoder.encode(buffer).byteLength;
373
+ const residualReservation = budget.reserveTransient(residualBytes, { kind: "live_transient" });
374
+ residualReservation.commitRetained();
375
+ budget.releaseRetained(bufferBytes, { kind: "live_transient" });
376
+ bufferBytes = residualBytes;
377
+ if (done) break;
378
+ }
379
+ const final = buffer.trim();
380
+ if (final) { try { yield JSON.parse(stripEventFrame(final)) as Record<string, unknown>; } catch { /* ignore */ } }
381
+ } finally {
382
+ budget.releaseRetained(bufferBytes, { kind: "live_transient" });
383
+ try { await reader.cancel(); } catch { /* already closed */ }
384
+ reader.releaseLock();
385
+ }
386
+ }
387
+
388
+ /** The endpoint is newline-delimited JSON; defensively strip an SSE `data:` frame if the gateway ever switches shapes. */
389
+ function stripEventFrame(line: string): string {
390
+ return line.startsWith("data:") ? line.slice("data:".length).trim() : line;
391
+ }
392
+
393
+ function isReasoningEffortRejection(status: number, payload: string): boolean {
394
+ return (status === 400 || status === 422) && /reasoning[_ -]?effort|unsupported effort|invalid effort/i.test(payload);
395
+ }
396
+
397
+ function requestWithoutReasoningEffort(request: AdapterRequest): AdapterRequest | undefined {
398
+ try {
399
+ const body = JSON.parse(request.body) as { params?: Record<string, unknown> };
400
+ if (!body.params?.reasoning_effort) return undefined;
401
+ delete body.params.reasoning_effort;
402
+ return { ...request, body: JSON.stringify(body), reasoningLog: undefined };
403
+ } catch {
404
+ return undefined;
405
+ }
406
+ }
407
+
408
+ async function fetchCommandCode(request: AdapterRequest, ctx: AdapterFetchContext | undefined, executor: typeof globalThis.fetch): Promise<Response> {
409
+ const timeout = new AbortController();
410
+ const timer = setTimeout(() => timeout.abort(new DOMException("Timeout elapsed", "TimeoutError")), ctx?.timeoutMs ?? 200_000);
411
+ const callerSignal = ctx?.abortSignal ?? new AbortController().signal;
412
+ try {
413
+ return await executor(request.url, {
414
+ method: request.method,
415
+ headers: request.headers,
416
+ body: request.body,
417
+ redirect: "manual",
418
+ signal: AbortSignal.any([callerSignal, timeout.signal]),
419
+ });
420
+ } finally {
421
+ clearTimeout(timer);
422
+ }
423
+ }
424
+
425
+ function supportedCommandCodeEffort(provider: OcxProviderConfig, modelId: string, requested: string | undefined): string | undefined {
426
+ if (!requested || requested === "none") return undefined;
427
+ // Compatibility ids (deepseek-v4-flash / glm-5.2) must resolve to their canonical
428
+ // Command Code id before the effort lookup, or legacy requests silently lose the
429
+ // reasoning effort because the official table is keyed by the canonical ids.
430
+ const canonicalId = canonicalCommandCodeModelId(modelId);
431
+ const supported = commandCodeReasoningEfforts(canonicalId) ?? configuredReasoningEfforts(provider, canonicalId);
432
+ if (!supported) return undefined;
433
+ // Only remap xhigh/ultra→max for models whose official profile documents that
434
+ // aliasing (deepseek v4, glm-5.2). Muse Spark's upstream accepts xhigh as a
435
+ // distinct wire value and rejects ultra, so it must not be collapsed.
436
+ let wire = requested;
437
+ const lower = canonicalId.toLowerCase();
438
+ const needsAlias =
439
+ lower === "deepseek/deepseek-v4-pro" ||
440
+ lower === "deepseek/deepseek-v4-flash" ||
441
+ lower === "zai-org/glm-5.2";
442
+ if (requested === "xhigh" && !supported.includes("xhigh") && supported.includes("max")) {
443
+ wire = "max";
444
+ } else if (requested === "ultra" && needsAlias && supported.includes("max")) {
445
+ wire = "max";
446
+ }
447
+ return (supported as readonly string[]).includes(wire) ? wire : undefined;
448
+ }
449
+
450
+ export function createCommandCodeAdapter(provider: OcxProviderConfig): ProviderAdapter {
451
+ const executor = (provider as OcxProviderConfig & { fetch?: typeof globalThis.fetch }).fetch ?? globalThis.fetch;
452
+ return {
453
+ name: "command-code",
454
+ async buildRequest(parsed: OcxParsedRequest): Promise<AdapterRequest> {
455
+ if (!provider.apiKey) throw new Error("Command Code credential missing — run ocx login command-code");
456
+ const cwd = currentWorkingDirectory();
457
+ const tools = visibleTools(parsed);
458
+ const toolNudge = buildNonOpenAIToolCatalogNudgeForTools(tools, parsed.options.toolChoice);
459
+ const choiceInstruction = toolChoiceInstruction(parsed);
460
+ const system = identifyRoutedModel([
461
+ ...(parsed.context.systemPrompt ?? []),
462
+ ...(toolNudge ? [toolNudge] : []),
463
+ ...(choiceInstruction ? [choiceInstruction] : []),
464
+ ].join("\n\n"), parsed.modelId);
465
+ const reasoningEffort = supportedCommandCodeEffort(provider, parsed.modelId, parsed.options.reasoning);
466
+ const body = {
467
+ config: await commandCodeConfig(cwd), memory: "", taste: null, skills: null,
468
+ permissionMode: "standard", mode: "agent",
469
+ params: {
470
+ model: canonicalCommandCodeModelId(parsed.modelId),
471
+ messages: wireMessages(parsed.context.messages),
472
+ tools: wireTools(tools),
473
+ system,
474
+ max_tokens: parsed.options.maxOutputTokens ?? provider.defaultMaxOutputTokens ?? 64_000,
475
+ // The proprietary /alpha/generate endpoint is NDJSON-stream-only; the proxy converts
476
+ // the buffered/streamed events to the client's requested shape (parsed.stream).
477
+ stream: true,
478
+ ...(parsed.options.temperature !== undefined ? { temperature: parsed.options.temperature } : {}),
479
+ ...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),
480
+ },
481
+ };
482
+ const headers: Record<string, string> = {
483
+ Authorization: `Bearer ${provider.apiKey}`,
484
+ "Content-Type": "application/json",
485
+ "User-Agent": "cli",
486
+ "x-command-code-version": provider.commandCodeVersion ?? "0.52.1",
487
+ "x-cli-environment": "production",
488
+ "x-taste-learning": "false",
489
+ "x-co-flag": "false",
490
+ "x-session-id": randomUUID(),
491
+ };
492
+ if (cwd) headers["x-project-slug"] = projectSlug(cwd);
493
+ return {
494
+ url: `${provider.baseUrl.replace(/\/$/, "")}/alpha/generate`, method: "POST",
495
+ headers,
496
+ body: JSON.stringify(body),
497
+ ...(reasoningEffort ? { reasoningLog: { effectiveEffort: reasoningEffort, wireField: "reasoning_effort" as const, wireValue: reasoningEffort } } : {}),
498
+ };
499
+ },
500
+ async fetchResponse(request: AdapterRequest, ctx?: AdapterFetchContext): Promise<Response> {
501
+ const response = await fetchCommandCode(request, ctx, executor);
502
+ if (response.ok) return response;
503
+ const currentEffort = (() => {
504
+ try { return (JSON.parse(request.body) as { params?: { reasoning_effort?: unknown } }).params?.reasoning_effort; } catch { return undefined; }
505
+ })();
506
+ if (typeof currentEffort !== "string") return response;
507
+ let body = "";
508
+ try {
509
+ const observed = await readBoundedResponseBody(response.clone(), { signal: ctx?.abortSignal, maxBytes: 8 * 1024 });
510
+ if (!observed.displaySafe) return response;
511
+ body = observed.text;
512
+ } catch { return response; }
513
+ if (!isReasoningEffortRejection(response.status, body)) return response;
514
+ const modelId = (() => {
515
+ try { return (JSON.parse(request.body) as { params?: { model?: unknown } }).params?.model; } catch { return undefined; }
516
+ })();
517
+ if (typeof modelId !== "string") return response;
518
+ const refreshed = await refreshCommandCodeReasoningEfforts(modelId, executor);
519
+ if (!refreshed || refreshed.includes(currentEffort)) return response;
520
+ const retry = requestWithoutReasoningEffort(request);
521
+ if (!retry) return response;
522
+ try { void response.body?.cancel(); } catch { /* already closed */ }
523
+ return fetchCommandCode(retry, ctx, executor);
524
+ },
525
+ async *parseStream(response: Response, budget: TranslatorBudget): AsyncGenerator<AdapterEvent> {
526
+ let sawFinish = false;
527
+ for await (const event of ndjson(response, budget)) {
528
+ switch (event.type) {
529
+ case "text-delta": if (typeof event.text === "string") yield { type: "text_delta", text: event.text }; break;
530
+ case "reasoning-delta": if (typeof event.text === "string") yield { type: "thinking_delta", thinking: event.text }; break;
531
+ case "tool-call": {
532
+ const id = typeof event.toolCallId === "string" ? event.toolCallId : randomUUID();
533
+ const name = typeof event.toolName === "string" ? event.toolName : "tool";
534
+ const input = event.input ?? event.args ?? {};
535
+ const argumentsText = typeof input === "string" ? input : JSON.stringify(input);
536
+ yield { type: "tool_call_start", id, name };
537
+ budget.openCall(id);
538
+ try {
539
+ const reservation = budget.reserveTransient(new TextEncoder().encode(argumentsText).byteLength, { kind: "tool_args", callId: id });
540
+ reservation.commitRetained();
541
+ yield { type: "tool_call_delta", arguments: argumentsText };
542
+ yield { type: "tool_call_end" };
543
+ } finally {
544
+ budget.closeCall(id);
545
+ }
546
+ break;
547
+ }
548
+ case "finish-step":
549
+ case "finish": {
550
+ // Both events are terminal on the current wire; streams commonly carry both, so
551
+ // only the first one emits the done. finish-step carries `usage`; finish may carry
552
+ // `totalUsage` (current flows) or `usage`.
553
+ if (sawFinish) break;
554
+ sawFinish = true;
555
+ const usageValue = event.totalUsage ?? event.usage;
556
+ const stopReason = typeof event.rawFinishReason === "string" ? event.rawFinishReason : typeof event.finishReason === "string" ? event.finishReason : undefined;
557
+ // The AI SDK's `error` finish reason means the generation failed upstream, not that it
558
+ // stopped. Reporting it as a `done` left the bridge to infer failure from a stop-reason
559
+ // string, which either read as a clean completion or (once classified) mislabelled an
560
+ // upstream error as a content filter and rejected it from the replay cache for the
561
+ // wrong reason.
562
+ if (stopReason === "error") {
563
+ // Keep the usage: a failed turn still consumed tokens, and dropping it makes the
564
+ // turn look free in accounting and reports zeros to the client.
565
+ yield {
566
+ type: "error",
567
+ message: "Command Code upstream ended the turn with finishReason \"error\"",
568
+ status: 502,
569
+ errorType: "upstream_error",
570
+ usage: usage(usageValue),
571
+ };
572
+ break;
573
+ }
574
+ yield { type: "done", usage: usage(usageValue), stopReason };
575
+ break;
576
+ }
577
+ case "error": {
578
+ const message = eventError(event.error);
579
+ if (isMissingToolResultError(message)) {
580
+ // Provider-side tool-result validation: the request carried an assistant tool
581
+ // call the upstream refused to accept. This is not a network/stream stall; the
582
+ // proxy normally prevents it by pairing every call, so flag it distinctly.
583
+ yield { type: "error", message, status: 502, errorType: "upstream_error", code: "missing_tool_result" };
584
+ } else {
585
+ yield { type: "error", message, status: 502 };
586
+ }
587
+ break;
588
+ }
589
+ }
590
+ }
591
+ // A stream that ends without a finish event still needs a terminal done so the
592
+ // server does not wait on an adapter that silently stopped emitting.
593
+ if (!sawFinish) yield { type: "done", usage: undefined, stopReason: undefined };
594
+ },
595
+ async parseResponse(response: Response, budget: TranslatorBudget): Promise<AdapterEvent[]> {
596
+ const events: AdapterEvent[] = [];
597
+ for await (const event of this.parseStream(response, budget)) events.push(event);
598
+ return events;
599
+ },
600
+ };
601
+ }