@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,104 @@
1
+ import { createHash } from "node:crypto";
2
+ import { readFileSync } from "node:fs";
3
+ import { join } from "node:path";
4
+ import { jcsStringify } from "../../lab/digest";
5
+
6
+ const VERSION_DOMAIN = "ocx-lab:compatibility-version:v1";
7
+ let cachedVersion: string | null | undefined;
8
+ let testOverride: string | null | undefined;
9
+
10
+ interface CompatibilityVersionManifest {
11
+ schemaVersion: 1;
12
+ assertionDslVersion: string;
13
+ evidenceSchemaVersion: string;
14
+ bunRuntimeVersion: string;
15
+ files: Array<{ path: string; sha256: string }>;
16
+ }
17
+
18
+ function isSha256Hex(value: string): boolean {
19
+ return /^[0-9a-f]{64}$/.test(value);
20
+ }
21
+
22
+ function hashManifest(manifest: unknown): string {
23
+ const hash = createHash("sha256");
24
+ hash.update(new TextEncoder().encode(`${VERSION_DOMAIN}\0`));
25
+ hash.update(new TextEncoder().encode(jcsStringify(manifest)));
26
+ return hash.digest("hex");
27
+ }
28
+
29
+ function parseEmbeddedManifest(raw: unknown): CompatibilityVersionManifest | null {
30
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
31
+ const record = raw as Record<string, unknown>;
32
+ if (record.schemaVersion !== 1
33
+ || typeof record.assertionDslVersion !== "string"
34
+ || typeof record.evidenceSchemaVersion !== "string"
35
+ || typeof record.bunRuntimeVersion !== "string"
36
+ || !Array.isArray(record.files)
37
+ || record.files.length === 0) return null;
38
+ const files: CompatibilityVersionManifest["files"] = [];
39
+ const seen = new Set<string>();
40
+ for (const row of record.files) {
41
+ if (!row || typeof row !== "object" || Array.isArray(row)) return null;
42
+ const path = (row as Record<string, unknown>).path;
43
+ const sha256 = (row as Record<string, unknown>).sha256;
44
+ if (typeof path !== "string" || !path || path.includes("\\") || path.startsWith("/") || path.split("/").includes("..")) return null;
45
+ if (typeof sha256 !== "string" || !isSha256Hex(sha256) || seen.has(path)) return null;
46
+ seen.add(path);
47
+ files.push({ path, sha256 });
48
+ }
49
+ return {
50
+ schemaVersion: 1,
51
+ assertionDslVersion: record.assertionDslVersion,
52
+ evidenceSchemaVersion: record.evidenceSchemaVersion,
53
+ // The runtime version is part of the canonical identity. The embedded file
54
+ // hashes remain valid across bundled Bun updates, while this field reflects
55
+ // the actual executing runtime rather than the package-build runtime.
56
+ bunRuntimeVersion: Bun.version,
57
+ files,
58
+ };
59
+ }
60
+
61
+ function readEmbeddedManifest(): CompatibilityVersionManifest | null {
62
+ const candidates = [
63
+ join(import.meta.dir, "..", "..", "generated", "compatibility-version.json"),
64
+ join(import.meta.dir, "..", "..", "..", "src", "generated", "compatibility-version.json"),
65
+ ];
66
+ for (const path of candidates) {
67
+ try {
68
+ const parsed = JSON.parse(readFileSync(path, "utf8")) as unknown;
69
+ const manifest = parseEmbeddedManifest(parsed);
70
+ if (manifest) return manifest;
71
+ } catch {
72
+ // try next candidate
73
+ }
74
+ }
75
+ return null;
76
+ }
77
+
78
+ /**
79
+ * OpenCodex compatibility version for route subject identity.
80
+ *
81
+ * There is intentionally no runtime-only fallback: without the generated file
82
+ * manifest we cannot prove exact implementation identity, so live-route subject
83
+ * resolution fails closed and follows the profile's unknown-evidence policy.
84
+ */
85
+ export function readOpenCodexCompatibilityVersion(): string | null {
86
+ if (testOverride !== undefined) return testOverride;
87
+ if (cachedVersion !== undefined) return cachedVersion;
88
+ const embedded = readEmbeddedManifest();
89
+ cachedVersion = embedded ? hashManifest(embedded) : null;
90
+ return cachedVersion;
91
+ }
92
+
93
+ /** Test-only exact identity override; never sourced from process environment. */
94
+ export function setCompatibilityVersionOverrideForTests(value: string | null): void {
95
+ if (value !== null && !isSha256Hex(value)) throw new Error("invalid test compatibility version");
96
+ testOverride = value;
97
+ cachedVersion = undefined;
98
+ }
99
+
100
+ /** Test-only reset of the compatibility version memo and override. */
101
+ export function resetCompatibilityVersionCacheForTests(): void {
102
+ cachedVersion = undefined;
103
+ testOverride = undefined;
104
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Cost-aware policy scoring and limits (RI-08).
3
+ *
4
+ * Reuses the canonical price/cost normalization (`src/usage/cost.ts`).
5
+ * Evidence distinguishes estimated vs authoritative usage and registry vs
6
+ * expected prices. Unknown prices stay unknown (never free); the profile's
7
+ * `unknownEvidence.cost` policy decides how unknown evidence is handled.
8
+ *
9
+ * No billing, invoicing, or hidden budgets - a hard per-request ceiling
10
+ * (`limits.maxEstimatedCostUsd`) is evaluated deterministically.
11
+ */
12
+
13
+ import type { OcxUsage } from "../types";
14
+ import type { UsageStatus } from "../usage/log";
15
+ import { estimateRequestCost, type ServiceTierInput } from "../usage/cost";
16
+ import type { RouteCostEvidence } from "./trace";
17
+
18
+ /** Reference cost (USD) for the relative cost score when no limit is set. */
19
+ export const COST_SCORE_REFERENCE_USD = 1.0;
20
+
21
+ export interface CostEvidenceInput {
22
+ provider: string;
23
+ model: string;
24
+ usage?: OcxUsage;
25
+ usageStatus?: UsageStatus;
26
+ serviceTier?: ServiceTierInput;
27
+ limitUsd?: number;
28
+ }
29
+
30
+ /**
31
+ * Assemble cost evidence from the canonical price model. Returns unknown-ish
32
+ * evidence (`incomplete: true`, no estimate) when usage or a price is
33
+ * missing - never a fabricated zero.
34
+ */
35
+ export function costEvidenceForCandidate(input: CostEvidenceInput): RouteCostEvidence {
36
+ const limitUsd = input.limitUsd;
37
+ if (!input.usage) {
38
+ return {
39
+ ...(limitUsd !== undefined ? { limitUsd } : {}),
40
+ incomplete: true,
41
+ };
42
+ }
43
+ const estimate = estimateRequestCost({
44
+ provider: input.provider,
45
+ model: input.model,
46
+ usage: input.usage,
47
+ usageStatus: input.usageStatus ?? "estimated",
48
+ ...(input.serviceTier ? { serviceTier: input.serviceTier } : {}),
49
+ });
50
+ if (!estimate) {
51
+ return {
52
+ ...(limitUsd !== undefined ? { limitUsd } : {}),
53
+ incomplete: true,
54
+ priceSource: "unmatched",
55
+ };
56
+ }
57
+ const priceSource = estimate.price?.source ?? "registry";
58
+ return {
59
+ estimatedUsd: estimate.cost.total,
60
+ priceSource,
61
+ incomplete: estimate.estimated || priceSource === "expected",
62
+ ...(limitUsd !== undefined ? { limitUsd } : {}),
63
+ };
64
+ }
65
+
66
+ /**
67
+ * Deterministic cost score in [0,1]: cheaper is better, relative to
68
+ * `COST_SCORE_REFERENCE_USD` (or the profile limit when set). Unknown
69
+ * estimates return null so the profile's unknownEvidence policy applies.
70
+ */
71
+ export function costScore(evidence: RouteCostEvidence | undefined): number | null {
72
+ if (evidence?.estimatedUsd === undefined || !Number.isFinite(evidence.estimatedUsd)) return null;
73
+ const reference = typeof evidence.limitUsd === "number" && evidence.limitUsd > 0
74
+ ? evidence.limitUsd
75
+ : COST_SCORE_REFERENCE_USD;
76
+ return Math.max(0, Math.min(1, 1 - evidence.estimatedUsd / reference));
77
+ }
@@ -0,0 +1,495 @@
1
+ /**
2
+ * Deterministic policy-profile evaluator (RI-04 core; extended by RI-05..08).
3
+ *
4
+ * RI-04 evaluates hard capability requirements against supplied evidence and
5
+ * scores by deterministic configured priority only. It never dispatches an
6
+ * upstream request; execution wiring arrives with RI-05.
7
+ */
8
+
9
+ import type { OcxConfig } from "../types";
10
+ import {
11
+ buildRouteDecisionTrace,
12
+ MAX_REQUIREMENTS,
13
+ type RouteCapabilityEvidence,
14
+ type RouteCostEvidence,
15
+ type RouteDecisionTraceV1,
16
+ type RouteExclusionReason,
17
+ type RouteHealthEvidence,
18
+ type RouteQuotaEvidence,
19
+ type RouteRequirementEvidence,
20
+ type RouteScoreEvidence,
21
+ type Unknownable,
22
+ } from "./trace";
23
+ import { getRoutingProfile, policyModelId, type NormalizedRoutingProfile } from "./profile";
24
+ import { healthScore, latencyScoreFromEvidence } from "./health";
25
+ import { quotaScore } from "./quota";
26
+ import { costScore } from "./cost";
27
+ import { evaluateCompatibilityForCandidate } from "./compatibility/policy";
28
+ import { COMPATIBILITY_WEIGHT, type CandidateCompatibilityEvidence } from "./compatibility/types";
29
+ import type { RouteCompatibilityEvidence } from "./trace";
30
+
31
+ /** Unknown health under "penalize": a low-but-not-zero deterministic floor. */
32
+ export const HEALTH_UNKNOWN_PENALTY_SCORE = 0.3;
33
+ /** Unknown health under "allow": neutral midpoint of the [0,1] health scale. */
34
+ export const HEALTH_UNKNOWN_NEUTRAL_SCORE = 0.5;
35
+ /** Unknown quota under "penalize": deterministic low floor. */
36
+ export const QUOTA_UNKNOWN_PENALTY_SCORE = 0.3;
37
+ /** Unknown cost under "penalize": deterministic low floor. */
38
+ export const COST_UNKNOWN_PENALTY_SCORE = 0.3;
39
+
40
+ export interface PolicyRequestEvidence {
41
+ /** Required context window for this request (tokens). */
42
+ contextWindow?: number;
43
+ toolsRequired?: boolean;
44
+ imageInputRequired?: boolean;
45
+ structuredOutputRequired?: boolean;
46
+ reasoningEffort?: string;
47
+ serviceTier?: string;
48
+ encryptedCodexTask?: boolean;
49
+ }
50
+
51
+ export interface PolicyCandidateEvidence {
52
+ provider: string;
53
+ model: string;
54
+ accountRef?: string;
55
+ /** Codex pool account id (provider "openai"); used to derive account-scoped quota evidence. */
56
+ codexAccountId?: string;
57
+ capability?: RouteCapabilityEvidence;
58
+ health?: RouteHealthEvidence;
59
+ quota?: RouteQuotaEvidence;
60
+ cost?: RouteCostEvidence;
61
+ compatibility?: CandidateCompatibilityEvidence;
62
+ }
63
+
64
+ export interface PolicyEvaluationCandidate {
65
+ provider: string;
66
+ model: string;
67
+ accountRef?: string;
68
+ eligible: boolean;
69
+ exclusions: RouteExclusionReason[];
70
+ requirements: RouteRequirementEvidence[];
71
+ capability?: RouteCapabilityEvidence;
72
+ health?: RouteHealthEvidence;
73
+ quota?: RouteQuotaEvidence;
74
+ cost?: RouteCostEvidence;
75
+ compatibility?: RouteCompatibilityEvidence;
76
+ score?: RouteScoreEvidence;
77
+ }
78
+
79
+ export interface PolicyEvaluationResult {
80
+ profileId: string;
81
+ profileRevision: string;
82
+ candidates: PolicyEvaluationCandidate[];
83
+ selectedIndex: number | null;
84
+ trace: RouteDecisionTraceV1;
85
+ }
86
+
87
+ function booleanRequirement(
88
+ id: string,
89
+ required: boolean | undefined,
90
+ actual: Unknownable | undefined,
91
+ ): RouteRequirementEvidence | null {
92
+ if (required === undefined) return null;
93
+ if (actual === undefined || actual === "unknown") {
94
+ return { id, expected: required, outcome: "unknown" };
95
+ }
96
+ return {
97
+ id,
98
+ expected: required,
99
+ actual: typeof actual === "boolean" ? actual : String(actual),
100
+ outcome: typeof actual === "boolean" && actual === required ? "satisfied" : "unsatisfied",
101
+ };
102
+ }
103
+
104
+ function requirementFor(
105
+ require: NormalizedRoutingProfile["require"],
106
+ capability: RouteCapabilityEvidence | undefined,
107
+ quota: RouteQuotaEvidence | undefined,
108
+ ): RouteRequirementEvidence[] {
109
+ const requirements: RouteRequirementEvidence[] = [];
110
+ if (require.minContextWindow !== undefined) {
111
+ const actual = capability?.contextWindow;
112
+ if (typeof actual === "number") {
113
+ requirements.push({
114
+ id: "min-context-window",
115
+ expected: require.minContextWindow,
116
+ actual,
117
+ outcome: actual >= require.minContextWindow ? "satisfied" : "unsatisfied",
118
+ });
119
+ } else {
120
+ requirements.push({ id: "min-context-window", expected: require.minContextWindow, outcome: "unknown" });
121
+ }
122
+ }
123
+ if (require.minQuotaHeadroom !== undefined) {
124
+ const quotaHeadroom = quotaScore(quota);
125
+ if (quotaHeadroom !== null) {
126
+ requirements.push({
127
+ id: "min-quota-headroom",
128
+ expected: require.minQuotaHeadroom,
129
+ actual: quotaHeadroom,
130
+ outcome: quotaHeadroom >= require.minQuotaHeadroom ? "satisfied" : "unsatisfied",
131
+ });
132
+ }
133
+ }
134
+ const tools = booleanRequirement("tools", require.tools, capability?.tools);
135
+ if (tools) requirements.push(tools);
136
+ const image = booleanRequirement("image-input", require.imageInput, capability?.image);
137
+ if (image) requirements.push(image);
138
+ const structured = booleanRequirement("structured-output", require.structuredOutput, capability?.structuredOutput);
139
+ if (structured) requirements.push(structured);
140
+ if (require.reasoningEffort !== undefined) {
141
+ const ladder = capability?.reasoningEfforts;
142
+ if (Array.isArray(ladder)) {
143
+ requirements.push({
144
+ id: "reasoning-effort",
145
+ expected: require.reasoningEffort,
146
+ actual: ladder.join(","),
147
+ outcome: ladder.includes(require.reasoningEffort) ? "satisfied" : "unsatisfied",
148
+ });
149
+ } else {
150
+ requirements.push({ id: "reasoning-effort", expected: require.reasoningEffort, outcome: "unknown" });
151
+ }
152
+ }
153
+ if (require.serviceTier !== undefined) {
154
+ const actual = capability?.serviceTier;
155
+ if (actual === undefined || actual === "unknown") {
156
+ requirements.push({ id: "service-tier", expected: require.serviceTier, outcome: "unknown" });
157
+ } else {
158
+ requirements.push({
159
+ id: "service-tier",
160
+ expected: require.serviceTier,
161
+ actual: String(actual),
162
+ outcome: actual === require.serviceTier ? "satisfied" : "unsatisfied",
163
+ });
164
+ }
165
+ }
166
+ const local = booleanRequirement("local-only", require.localOnly, capability?.localOnly);
167
+ if (local) requirements.push(local);
168
+ const remote = booleanRequirement("remote-allowed", require.remoteAllowed, capability?.remoteAllowed);
169
+ if (remote) requirements.push(remote);
170
+ const encrypted = booleanRequirement(
171
+ "encrypted-codex-tasks",
172
+ require.encryptedCodexTasks,
173
+ capability?.encryptedCodexTasks,
174
+ );
175
+ if (encrypted) requirements.push(encrypted);
176
+ return requirements;
177
+ }
178
+
179
+ function unsatisfiedOrUnknown(requirements: RouteRequirementEvidence[]): RouteRequirementEvidence[] {
180
+ return requirements.filter(requirement => requirement.outcome !== "satisfied");
181
+ }
182
+
183
+ function configuredPriorityScore(index: number, total: number): number {
184
+ return total > 1 ? (total - index) / total : 1;
185
+ }
186
+
187
+ function requestRequirementFor(
188
+ request: PolicyRequestEvidence,
189
+ capability: RouteCapabilityEvidence | undefined,
190
+ ): RouteRequirementEvidence[] {
191
+ const requirements: RouteRequirementEvidence[] = [];
192
+ if (request.contextWindow !== undefined) {
193
+ const actual = capability?.contextWindow;
194
+ if (typeof actual === "number") {
195
+ requirements.push({
196
+ id: "request-context-window",
197
+ expected: request.contextWindow,
198
+ actual,
199
+ outcome: actual >= request.contextWindow ? "satisfied" : "unsatisfied",
200
+ });
201
+ } else {
202
+ requirements.push({ id: "request-context-window", expected: request.contextWindow, outcome: "unknown" });
203
+ }
204
+ }
205
+ if (request.toolsRequired === true) {
206
+ const tools = booleanRequirement("request-tools", true, capability?.tools);
207
+ if (tools) requirements.push(tools);
208
+ }
209
+ if (request.imageInputRequired === true) {
210
+ const image = booleanRequirement("request-image-input", true, capability?.image);
211
+ if (image) requirements.push(image);
212
+ }
213
+ if (request.structuredOutputRequired === true) {
214
+ const structured = booleanRequirement("request-structured-output", true, capability?.structuredOutput);
215
+ if (structured) requirements.push(structured);
216
+ }
217
+ if (request.reasoningEffort !== undefined) {
218
+ const ladder = capability?.reasoningEfforts;
219
+ if (Array.isArray(ladder)) {
220
+ requirements.push({
221
+ id: "request-reasoning-effort",
222
+ expected: request.reasoningEffort,
223
+ actual: ladder.join(","),
224
+ outcome: ladder.includes(request.reasoningEffort) ? "satisfied" : "unsatisfied",
225
+ });
226
+ } else {
227
+ requirements.push({ id: "request-reasoning-effort", expected: request.reasoningEffort, outcome: "unknown" });
228
+ }
229
+ }
230
+ if (request.serviceTier !== undefined) {
231
+ const actual = capability?.serviceTier;
232
+ if (actual === undefined || actual === "unknown") {
233
+ requirements.push({ id: "request-service-tier", expected: request.serviceTier, outcome: "unknown" });
234
+ } else {
235
+ requirements.push({
236
+ id: "request-service-tier",
237
+ expected: request.serviceTier,
238
+ actual: String(actual),
239
+ outcome: actual === request.serviceTier ? "satisfied" : "unsatisfied",
240
+ });
241
+ }
242
+ }
243
+ if (request.encryptedCodexTask === true) {
244
+ const encrypted = booleanRequirement(
245
+ "request-encrypted-codex-tasks",
246
+ true,
247
+ capability?.encryptedCodexTasks,
248
+ );
249
+ if (encrypted) requirements.push(encrypted);
250
+ }
251
+ return requirements;
252
+ }
253
+
254
+ /**
255
+ * Evaluate a profile against request + candidate evidence. Deterministic:
256
+ * candidates are scored in declaration order, ties break by earlier index.
257
+ */
258
+ export function evaluatePolicyProfile(
259
+ config: OcxConfig,
260
+ profileId: string,
261
+ requestEvidence: PolicyRequestEvidence,
262
+ candidateEvidence: PolicyCandidateEvidence[],
263
+ now = Date.now(),
264
+ ): PolicyEvaluationResult {
265
+ const profile = getRoutingProfile(config, profileId);
266
+ if (!profile) throw new Error(`Unknown routing profile: ${profileId}`);
267
+
268
+ const candidates: PolicyEvaluationCandidate[] = [];
269
+ let selectedIndex: number | null = null;
270
+ let bestScore = Number.NEGATIVE_INFINITY;
271
+
272
+ profile.candidates.forEach((declared, index) => {
273
+ const evidence = candidateEvidence.find(
274
+ candidate => candidate.provider === declared.provider && candidate.model === declared.model,
275
+ ) ?? { provider: declared.provider, model: declared.model };
276
+ const requirements = [
277
+ ...requirementFor(profile.require, evidence.capability, evidence.quota),
278
+ ...requestRequirementFor(requestEvidence, evidence.capability),
279
+ ];
280
+ const exclusions: RouteExclusionReason[] = [];
281
+ const bad = unsatisfiedOrUnknown(requirements);
282
+ for (const requirement of bad) {
283
+ if (requirement.outcome === "unsatisfied") {
284
+ exclusions.push({ code: "capability-unsatisfied", detail: requirement.id });
285
+ } else {
286
+ exclusions.push({ code: "unknown-capability", detail: requirement.id });
287
+ }
288
+ }
289
+ const unsatisfied = bad.some(requirement => requirement.outcome === "unsatisfied");
290
+ const unknown = bad.some(requirement => requirement.outcome === "unknown");
291
+ const excludedByUnknown = unknown && profile.unknownEvidence.capability === "exclude";
292
+ const costLimit = profile.limits.maxEstimatedCostUsd;
293
+ const estimatedCost = evidence.cost?.estimatedUsd;
294
+ const costEstimateKnown = typeof estimatedCost === "number" && Number.isFinite(estimatedCost);
295
+ const overCostLimit = costLimit !== undefined
296
+ && costEstimateKnown
297
+ && estimatedCost! > costLimit;
298
+ if (overCostLimit) {
299
+ exclusions.push({ code: "cost-limit", detail: "maxEstimatedCostUsd" });
300
+ }
301
+ // A cap can only be *proven* satisfied when the estimate is known. The live
302
+ // routing path often has no usage evidence yet, so the default stays
303
+ // "allow" to preserve the documented dry-run contract; operators who need a
304
+ // genuine hard ceiling opt into "exclude". Exclusions cover only the
305
+ // fail-closed path; the allow path stamps `cost.capOutcome` so operators
306
+ // can still distinguish "known under the cap" from "unknown cost allowed".
307
+ const unknownCostUnderCap = costLimit !== undefined && !costEstimateKnown;
308
+ const unknownCostBlocked = unknownCostUnderCap
309
+ && profile.limits.onUnknownCost === "exclude";
310
+ if (unknownCostBlocked) {
311
+ exclusions.push({ code: "cost-limit-unknown", detail: "maxEstimatedCostUsd" });
312
+ }
313
+ let eligible = !unsatisfied && !excludedByUnknown && !overCostLimit && !unknownCostBlocked;
314
+
315
+ // Trace/dry-run copy only: report the profile cap that was applied and the
316
+ // operator-visible outcome. Do not feed this copy into costScore() — that
317
+ // would silently change ranking when a caller supplied a different
318
+ // limitUsd (costScore uses limitUsd as its reference denominator).
319
+ let costForCandidate = evidence.cost;
320
+ if (costLimit !== undefined) {
321
+ const capOutcome = overCostLimit
322
+ ? "exceeded" as const
323
+ : unknownCostBlocked
324
+ ? "unknown-excluded" as const
325
+ : unknownCostUnderCap
326
+ ? "unknown-allowed" as const
327
+ : "satisfied" as const;
328
+ if (!costEstimateKnown) {
329
+ // Missing or non-finite estimates are the same unknown: never stamp
330
+ // Infinity/NaN into the trace, and always mark incomplete.
331
+ const { estimatedUsd: _nonFiniteOrMissing, ...rest } = evidence.cost ?? {};
332
+ costForCandidate = {
333
+ ...rest,
334
+ incomplete: true,
335
+ limitUsd: costLimit,
336
+ capOutcome,
337
+ };
338
+ } else {
339
+ costForCandidate = {
340
+ ...evidence.cost!,
341
+ limitUsd: costLimit,
342
+ capOutcome,
343
+ };
344
+ }
345
+ }
346
+
347
+ const compatibilityEval = evaluateCompatibilityForCandidate(
348
+ profile.compatibility,
349
+ evidence.compatibility,
350
+ now,
351
+ );
352
+ if (compatibilityEval.exclusions.length > 0) {
353
+ exclusions.push(...compatibilityEval.exclusions);
354
+ eligible = false;
355
+ }
356
+ const compatibilityValue = compatibilityEval.penaltyScore;
357
+ const compatibilityTrace = compatibilityEval.trace;
358
+
359
+ const health = evidence.health;
360
+ let healthValue = health ? healthScore(health, now) : null;
361
+ if (health?.cooldownUntilMs !== undefined && health.cooldownUntilMs > now) {
362
+ exclusions.push({ code: "cooldown" });
363
+ eligible = false;
364
+ } else if (healthValue === null && profile.unknownEvidence.health === "exclude") {
365
+ exclusions.push({ code: "unknown-health" });
366
+ eligible = false;
367
+ } else if (healthValue === null && profile.unknownEvidence.health === "penalize") {
368
+ healthValue = HEALTH_UNKNOWN_PENALTY_SCORE;
369
+ } else if (healthValue === null && profile.unknownEvidence.health === "allow") {
370
+ healthValue = HEALTH_UNKNOWN_NEUTRAL_SCORE;
371
+ }
372
+
373
+ const quota = evidence.quota;
374
+ let quotaValue = quota ? quotaScore(quota) : null;
375
+ if (quotaValue === null) {
376
+ if (profile.unknownEvidence.quota === "exclude") {
377
+ exclusions.push({ code: "unknown-quota" });
378
+ eligible = false;
379
+ } else if (profile.unknownEvidence.quota === "penalize") {
380
+ quotaValue = QUOTA_UNKNOWN_PENALTY_SCORE;
381
+ }
382
+ }
383
+
384
+ // Cost scoring (RI-08): the hard per-request ceiling was already checked
385
+ // above; unknown cost follows the profile's unknownEvidence policy.
386
+ // Score against the caller's original evidence so trace stamping cannot
387
+ // move the costScore reference / ranking.
388
+ const cost = evidence.cost;
389
+ let costValue = cost ? costScore(cost) : null;
390
+ if (costValue === null && profile.unknownEvidence.cost === "exclude") {
391
+ exclusions.push({ code: "unknown-price" });
392
+ eligible = false;
393
+ } else if (costValue === null && profile.unknownEvidence.cost === "penalize") {
394
+ costValue = COST_UNKNOWN_PENALTY_SCORE;
395
+ }
396
+
397
+ const priorityScore = configuredPriorityScore(index, profile.candidates.length);
398
+ const healthWeight = profile.optimize.health;
399
+ const quotaWeight = profile.optimize.quota;
400
+ const costWeight = profile.optimize.cost;
401
+ // `optimize.latency` was normalized into the weight sum but never spent, so whatever
402
+ // was allocated to it silently became configuredPriority -- i.e. declaration order.
403
+ // Spend it on the same p50-derived score the health composite already uses.
404
+ const latencyWeight = profile.optimize.latency;
405
+ const latencyValue = latencyWeight > 0 ? latencyScoreFromEvidence(health) : null;
406
+ const spentHealth = healthValue !== null ? healthWeight : 0;
407
+ const spentQuota = quotaValue !== null ? quotaWeight : 0;
408
+ const spentCost = costValue !== null ? costWeight : 0;
409
+ const spentLatency = latencyValue !== null ? latencyWeight : 0;
410
+ const priorityWeight = Math.max(0, 1 - spentHealth - spentQuota - spentCost - spentLatency);
411
+ const components: RouteScoreEvidence["components"] = { configuredPriority: priorityScore };
412
+ let total = priorityWeight * priorityScore;
413
+ if (healthWeight > 0 && healthValue !== null) {
414
+ total += healthWeight * healthValue;
415
+ components.health = healthValue;
416
+ }
417
+ if (quotaWeight > 0 && quotaValue !== null) {
418
+ total += quotaWeight * quotaValue;
419
+ components.quota = quotaValue;
420
+ }
421
+ if (costWeight > 0 && costValue !== null) {
422
+ total += costWeight * costValue;
423
+ components.cost = costValue;
424
+ }
425
+ if (latencyWeight > 0 && latencyValue !== null) {
426
+ total += latencyWeight * latencyValue;
427
+ components.latency = latencyValue;
428
+ }
429
+ if (compatibilityValue !== null) {
430
+ // Compatibility is a penalty-only dimension. A penalized candidate loses
431
+ // a bounded fraction of its existing score; satisfied/allowed evidence
432
+ // does not receive a synthetic bonus or alter configured priority.
433
+ total = Math.max(0, total - COMPATIBILITY_WEIGHT * (1 - compatibilityValue));
434
+ components.compatibility = compatibilityValue;
435
+ }
436
+ const score: RouteScoreEvidence = { total, components };
437
+ const evaluated: PolicyEvaluationCandidate = {
438
+ provider: evidence.provider,
439
+ model: evidence.model,
440
+ ...(evidence.accountRef ? { accountRef: evidence.accountRef } : {}),
441
+ eligible,
442
+ exclusions,
443
+ requirements,
444
+ ...(evidence.capability ? { capability: evidence.capability } : {}),
445
+ ...(evidence.health ? { health: evidence.health } : {}),
446
+ ...(evidence.quota ? { quota: evidence.quota } : {}),
447
+ ...(evidence.cost ? { cost: evidence.cost } : {}),
448
+ ...(compatibilityTrace ? { compatibility: compatibilityTrace } : {}),
449
+ ...(costForCandidate ? { cost: costForCandidate } : {}),
450
+ score,
451
+ };
452
+ candidates.push(evaluated);
453
+
454
+ if (evaluated.eligible && score.total > bestScore) {
455
+ bestScore = score.total;
456
+ selectedIndex = index;
457
+ }
458
+ });
459
+
460
+ const trace = buildRouteDecisionTrace({
461
+ requestedModel: policyModelId(profileId),
462
+ routeKind: "policy",
463
+ profile: { id: profile.id, revision: profile.revision },
464
+ requirements: candidates.flatMap(candidate => candidate.requirements).slice(0, MAX_REQUIREMENTS),
465
+ candidates: candidates.map(candidate => ({
466
+ provider: candidate.provider,
467
+ model: candidate.model,
468
+ ...(candidate.accountRef ? { accountRef: candidate.accountRef } : {}),
469
+ eligible: candidate.eligible,
470
+ exclusions: candidate.exclusions,
471
+ ...(candidate.score ? { score: candidate.score } : {}),
472
+ ...(candidate.capability ? { capability: candidate.capability } : {}),
473
+ ...(candidate.health ? { health: candidate.health } : {}),
474
+ ...(candidate.quota ? { quota: candidate.quota } : {}),
475
+ ...(candidate.cost ? { cost: candidate.cost } : {}),
476
+ ...(candidate.compatibility ? { compatibility: candidate.compatibility } : {}),
477
+ })),
478
+ selected: selectedIndex === null
479
+ ? { provider: candidates[0]?.provider ?? "", model: candidates[0]?.model ?? "", reason: "no-eligible-candidate" }
480
+ : {
481
+ candidateIndex: selectedIndex,
482
+ provider: candidates[selectedIndex]!.provider,
483
+ model: candidates[selectedIndex]!.model,
484
+ reason: "policy-selected",
485
+ },
486
+ });
487
+
488
+ return {
489
+ profileId: profile.id,
490
+ profileRevision: profile.revision,
491
+ candidates,
492
+ selectedIndex,
493
+ trace,
494
+ };
495
+ }