@iislee/opencodex 2.11.0 → 2.35.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 (733) hide show
  1. package/AGENTS_INSTALL.md +109 -0
  2. package/README.md +117 -22
  3. package/bin/ocx.mjs +245 -88
  4. package/bin/package-main.mjs +1 -1
  5. package/gui/dist/assets/index-Be1sDpWw.css +1 -0
  6. package/gui/dist/assets/index-D4OKgUV4.js +114 -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 +21 -10
  42. package/src/adapters/anthropic-output-schema.ts +137 -0
  43. package/src/adapters/anthropic.ts +475 -62
  44. package/src/adapters/base.ts +82 -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 +637 -0
  48. package/src/adapters/cursor/call-id.ts +44 -0
  49. package/src/adapters/cursor/catalog.ts +541 -0
  50. package/src/adapters/cursor/checkpoint-store.ts +308 -0
  51. package/src/adapters/cursor/cursor-errors.ts +144 -6
  52. package/src/adapters/cursor/discovery.ts +122 -14
  53. package/src/adapters/cursor/effort-map.ts +106 -4
  54. package/src/adapters/cursor/envelope-echo.ts +290 -0
  55. package/src/adapters/cursor/framing.ts +39 -0
  56. package/src/adapters/cursor/h2-pool.ts +123 -0
  57. package/src/adapters/cursor/http1-bidi.ts +361 -0
  58. package/src/adapters/cursor/images.ts +704 -0
  59. package/src/adapters/cursor/live-models.ts +180 -59
  60. package/src/adapters/cursor/live-transport.ts +623 -170
  61. package/src/adapters/cursor/message-mapper.ts +4 -1
  62. package/src/adapters/cursor/native-exec-common.ts +23 -2
  63. package/src/adapters/cursor/native-exec-desktop.ts +23 -0
  64. package/src/adapters/cursor/native-exec-fs.ts +10 -7
  65. package/src/adapters/cursor/native-exec-network.ts +1 -1
  66. package/src/adapters/cursor/native-exec-shell.ts +5 -3
  67. package/src/adapters/cursor/native-exec.ts +121 -14
  68. package/src/adapters/cursor/protobuf-events.ts +829 -11
  69. package/src/adapters/cursor/protobuf-request.ts +447 -75
  70. package/src/adapters/cursor/request-builder.ts +271 -35
  71. package/src/adapters/cursor/tool-definitions.ts +252 -12
  72. package/src/adapters/cursor/tool-result-normalize.ts +115 -0
  73. package/src/adapters/cursor/transport.ts +22 -0
  74. package/src/adapters/cursor/types.ts +48 -1
  75. package/src/adapters/cursor.ts +336 -39
  76. package/src/adapters/exec-tool-result-normalize.ts +99 -0
  77. package/src/adapters/google-antigravity-replay.ts +713 -41
  78. package/src/adapters/google-antigravity-wire.ts +48 -10
  79. package/src/adapters/google-errors.ts +44 -12
  80. package/src/adapters/google-http.ts +38 -10
  81. package/src/adapters/google-tool-schema.ts +80 -15
  82. package/src/adapters/google-truncation.ts +11 -0
  83. package/src/adapters/google.ts +742 -86
  84. package/src/adapters/identity.ts +39 -6
  85. package/src/adapters/image.ts +1 -1
  86. package/src/adapters/kiro-constants.ts +27 -0
  87. package/src/adapters/kiro-errors.ts +11 -0
  88. package/src/adapters/kiro-events.ts +19 -1
  89. package/src/adapters/kiro-thinking.ts +18 -2
  90. package/src/adapters/kiro-tools.ts +65 -17
  91. package/src/adapters/kiro.ts +270 -73
  92. package/src/adapters/mimo-free.ts +17 -0
  93. package/src/adapters/openai-chat-url.ts +11 -0
  94. package/src/adapters/openai-chat.ts +1377 -344
  95. package/src/adapters/openai-responses-url.ts +16 -0
  96. package/src/adapters/openai-responses.ts +1164 -64
  97. package/src/adapters/registry.ts +175 -0
  98. package/src/adapters/responses-tool-schema.ts +67 -0
  99. package/src/adapters/run-turn-queue.ts +36 -1
  100. package/src/adapters/tool-call-id.ts +119 -0
  101. package/src/adapters/tool-catalog-nudge.ts +105 -21
  102. package/src/adapters/xai-tool-schema.ts +436 -0
  103. package/src/adapters/xai-web-search.ts +186 -0
  104. package/src/bridge.ts +512 -92
  105. package/src/chat/inbound.ts +57 -20
  106. package/src/chat/outbound.ts +97 -33
  107. package/src/claude/agents-inject.ts +40 -10
  108. package/src/claude/context-windows.ts +37 -14
  109. package/src/claude/desktop-3p.ts +243 -9
  110. package/src/claude/gateway-cache.ts +41 -4
  111. package/src/claude/inbound.ts +72 -3
  112. package/src/claude/model-info.ts +38 -15
  113. package/src/claude/outbound.ts +88 -21
  114. package/src/cli/access.ts +46 -3
  115. package/src/cli/account-api.ts +150 -19
  116. package/src/cli/account-auth.ts +33 -6
  117. package/src/cli/account-catalog-refresh.ts +14 -0
  118. package/src/cli/account-extended.ts +648 -28
  119. package/src/cli/account-main.ts +317 -0
  120. package/src/cli/account.ts +97 -19
  121. package/src/cli/agent.ts +53 -2
  122. package/src/cli/alias.ts +66 -0
  123. package/src/cli/capabilities-command.ts +94 -0
  124. package/src/cli/capabilities.ts +496 -0
  125. package/src/cli/claude-agent-startup-sync.ts +73 -0
  126. package/src/cli/claude-desktop.ts +60 -15
  127. package/src/cli/claude.ts +129 -35
  128. package/src/cli/codex-log-guard-doctor.ts +103 -0
  129. package/src/cli/codex-shim-autorestore.ts +2 -0
  130. package/src/cli/codex-shim-readiness.ts +76 -0
  131. package/src/cli/combo.ts +8 -0
  132. package/src/cli/config-command.ts +74 -10
  133. package/src/cli/dispatch.ts +821 -0
  134. package/src/cli/doctor.ts +440 -56
  135. package/src/cli/ensure-desired-integrations.ts +152 -0
  136. package/src/cli/export-command.ts +46 -20
  137. package/src/cli/help.ts +34 -274
  138. package/src/cli/index.ts +417 -528
  139. package/src/cli/init.ts +5 -17
  140. package/src/cli/inspect.ts +230 -0
  141. package/src/cli/integrations.ts +120 -2
  142. package/src/cli/lab.ts +607 -0
  143. package/src/cli/launcher-context.ts +77 -0
  144. package/src/cli/minimax.ts +497 -0
  145. package/src/cli/models-runtime.ts +130 -2
  146. package/src/cli/models.ts +118 -16
  147. package/src/cli/observe.ts +123 -11
  148. package/src/cli/opencode.ts +4 -2
  149. package/src/cli/provider-runtime.ts +34 -3
  150. package/src/cli/provider.ts +25 -3
  151. package/src/cli/ready.ts +301 -0
  152. package/src/cli/registry.ts +476 -0
  153. package/src/cli/root.ts +86 -0
  154. package/src/cli/route-policy.ts +92 -0
  155. package/src/cli/runtime-api.ts +57 -10
  156. package/src/cli/star-prompt.ts +71 -15
  157. package/src/cli/status.ts +51 -4
  158. package/src/cli/storage.ts +234 -0
  159. package/src/cli/system-command.ts +16 -0
  160. package/src/cli/system-restart-client.ts +146 -0
  161. package/src/cli/tray-proxy.ts +153 -6
  162. package/src/cli/usage-report.ts +184 -0
  163. package/src/cli/v2.ts +137 -18
  164. package/src/cli/version-skew.ts +46 -0
  165. package/src/cli.ts +1 -1
  166. package/src/clients/config-export.ts +1442 -23
  167. package/src/clients/effective-status.ts +30 -2
  168. package/src/clients/probes/cline.ts +81 -0
  169. package/src/codex/account-label.ts +35 -1
  170. package/src/codex/account-lifecycle.ts +130 -13
  171. package/src/codex/account-namespaces.ts +49 -3
  172. package/src/codex/account-pause.ts +2 -1
  173. package/src/codex/account-priority.ts +84 -0
  174. package/src/codex/account-store.ts +29 -2
  175. package/src/codex/account-usability.ts +25 -2
  176. package/src/codex/admission.ts +256 -0
  177. package/src/codex/affinity-debug.ts +162 -0
  178. package/src/codex/app-server-processes.ts +570 -107
  179. package/src/codex/app-server-restart-service.ts +232 -0
  180. package/src/codex/auth-api.ts +893 -246
  181. package/src/codex/auth-collision.ts +5 -3
  182. package/src/codex/auth-context.ts +348 -32
  183. package/src/codex/autostart-health.ts +24 -1
  184. package/src/codex/catalog/account-models.ts +67 -0
  185. package/src/codex/catalog/aggregation.ts +86 -10
  186. package/src/codex/catalog/bundled.ts +331 -33
  187. package/src/codex/catalog/effort.ts +138 -32
  188. package/src/codex/catalog/filesystem-evidence.ts +302 -0
  189. package/src/codex/catalog/kinds.ts +2 -0
  190. package/src/codex/catalog/metadata.ts +555 -45
  191. package/src/codex/catalog/native-models.ts +75 -0
  192. package/src/codex/catalog/parsing.ts +269 -30
  193. package/src/codex/catalog/provider-fetch.ts +1626 -144
  194. package/src/codex/catalog/sync.ts +1576 -187
  195. package/src/codex/catalog-admission.ts +199 -0
  196. package/src/codex/catalog-refresh-status.ts +105 -0
  197. package/src/codex/catalog-write-serialization.ts +242 -0
  198. package/src/codex/catalog.ts +6 -3
  199. package/src/codex/codex-write-lock.ts +386 -0
  200. package/src/codex/convergence-types.ts +614 -0
  201. package/src/codex/convergence.ts +676 -0
  202. package/src/codex/coordinator-doctor.ts +332 -0
  203. package/src/codex/custom-model-catalog-migration.ts +176 -0
  204. package/src/codex/desired-state.ts +230 -0
  205. package/src/codex/desktop-app-restart.ts +355 -0
  206. package/src/codex/features.ts +636 -39
  207. package/src/codex/generation.ts +202 -0
  208. package/src/codex/history-job.ts +436 -0
  209. package/src/codex/history-lock.ts +242 -0
  210. package/src/codex/history-manifest.ts +112 -0
  211. package/src/codex/history-migration-guardian.ts +30 -24
  212. package/src/codex/history-provider.ts +1016 -235
  213. package/src/codex/history-transition.ts +105 -0
  214. package/src/codex/history-worker.ts +223 -0
  215. package/src/codex/inject-coordination.ts +373 -0
  216. package/src/codex/inject.ts +1114 -152
  217. package/src/codex/injected-marker.ts +37 -3
  218. package/src/codex/integration-record.ts +266 -0
  219. package/src/codex/internal/catalog-writer.ts +203 -0
  220. package/src/codex/internal/history-writer.ts +81 -0
  221. package/src/codex/journal.ts +66 -4
  222. package/src/codex/log-guard/inspect.ts +524 -0
  223. package/src/codex/log-guard/lock.ts +150 -0
  224. package/src/codex/log-guard/maintenance.ts +403 -0
  225. package/src/codex/log-guard/path-safety.ts +88 -0
  226. package/src/codex/log-guard/policy.ts +44 -0
  227. package/src/codex/log-guard/processes.ts +205 -0
  228. package/src/codex/log-guard/protection.ts +489 -0
  229. package/src/codex/log-guard/sqlite-errors.ts +9 -0
  230. package/src/codex/main-account-cache.ts +24 -0
  231. package/src/codex/main-account.ts +29 -1
  232. package/src/codex/management-convergence.ts +167 -0
  233. package/src/codex/model-cache.ts +56 -10
  234. package/src/codex/model-entitlements.ts +360 -0
  235. package/src/codex/native-main-admission.ts +47 -0
  236. package/src/codex/native-main-auth-temp.ts +187 -0
  237. package/src/codex/native-main-claim.ts +178 -0
  238. package/src/codex/native-main-lock-file.ts +162 -0
  239. package/src/codex/native-main-owner.ts +329 -0
  240. package/src/codex/native-profile-api.ts +247 -0
  241. package/src/codex/native-profile-manager.ts +1531 -0
  242. package/src/codex/native-profile-processes.ts +121 -0
  243. package/src/codex/native-profile-recovery.ts +99 -0
  244. package/src/codex/native-profile-stage-store.ts +387 -0
  245. package/src/codex/native-profile-startup.ts +622 -0
  246. package/src/codex/native-profile-store.ts +855 -0
  247. package/src/codex/native-profile-types.ts +120 -0
  248. package/src/codex/native-residue.ts +675 -0
  249. package/src/codex/paths.ts +80 -1
  250. package/src/codex/plan-from-token.ts +140 -0
  251. package/src/codex/plan.ts +40 -0
  252. package/src/codex/plugins-doctor.ts +1 -1
  253. package/src/codex/pool-rotation.ts +74 -4
  254. package/src/codex/project-config-warnings.ts +20 -6
  255. package/src/codex/prompt-journal.ts +352 -0
  256. package/src/codex/prompt-layers.ts +1313 -0
  257. package/src/codex/prompt-lock.ts +143 -0
  258. package/src/codex/prompt-text-probe.ts +238 -0
  259. package/src/codex/quota-rejection.ts +298 -0
  260. package/src/codex/quota.ts +264 -21
  261. package/src/codex/refresh.ts +11 -2
  262. package/src/codex/reset-credit-recovery.ts +1044 -0
  263. package/src/codex/routing.ts +514 -94
  264. package/src/codex/runtime.ts +159 -38
  265. package/src/codex/shim.ts +1065 -31
  266. package/src/codex/subagent-model-fallback.ts +437 -43
  267. package/src/codex/sync.ts +191 -2
  268. package/src/codex/transition-state.ts +720 -0
  269. package/src/codex/upstream-host-health.ts +368 -0
  270. package/src/codex/user-identity.ts +557 -0
  271. package/src/codex/warmup.ts +187 -81
  272. package/src/codex/write-coordination.ts +114 -0
  273. package/src/combos/failover.ts +47 -0
  274. package/src/combos/index.ts +4 -0
  275. package/src/combos/request.ts +32 -0
  276. package/src/combos/types.ts +91 -9
  277. package/src/compatibility/index.ts +26 -0
  278. package/src/compatibility/manifest.ts +253 -0
  279. package/src/compatibility/openai-responses.ts +121 -0
  280. package/src/config/atomic-write.ts +219 -0
  281. package/src/config/paths.ts +40 -0
  282. package/src/config/process-state.ts +309 -0
  283. package/src/config/provider-name.ts +24 -0
  284. package/src/config/provider-validation.ts +177 -0
  285. package/src/config/rebase-provenance.ts +68 -0
  286. package/src/config.ts +1713 -709
  287. package/src/generated/compatibility-version.json +3324 -0
  288. package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
  289. package/src/grok/inject.ts +16 -5
  290. package/src/grok/inspect.ts +45 -0
  291. package/src/grok/sync.ts +2 -2
  292. package/src/images/loop.ts +157 -33
  293. package/src/images/plan.ts +24 -13
  294. package/src/integrations/config-io.ts +269 -0
  295. package/src/integrations/journal.ts +315 -0
  296. package/src/integrations/merge.ts +135 -0
  297. package/src/integrations/mutation-flight.ts +71 -0
  298. package/src/integrations/native/ownership-preflight.ts +202 -0
  299. package/src/integrations/omp-yaml-source.ts +358 -0
  300. package/src/integrations/owned-refresh.ts +74 -0
  301. package/src/integrations/ownership-policy.ts +160 -0
  302. package/src/integrations/ownership.ts +155 -0
  303. package/src/integrations/registry.ts +166 -0
  304. package/src/integrations/serialize.ts +314 -0
  305. package/src/integrations/state.ts +433 -0
  306. package/src/integrations/store.ts +103 -0
  307. package/src/integrations/writer-lock.ts +98 -0
  308. package/src/integrations/writer.ts +715 -0
  309. package/src/lab/artifacts/sanitize.ts +586 -0
  310. package/src/lab/artifacts/secure-fs.ts +475 -0
  311. package/src/lab/artifacts/store.ts +310 -0
  312. package/src/lab/automation/budgets.ts +78 -0
  313. package/src/lab/automation/config-persistence.ts +256 -0
  314. package/src/lab/automation/constants.ts +39 -0
  315. package/src/lab/automation/cooldown.ts +103 -0
  316. package/src/lab/automation/dispatch.ts +211 -0
  317. package/src/lab/automation/index.ts +13 -0
  318. package/src/lab/automation/orchestrator.ts +499 -0
  319. package/src/lab/automation/persistence.ts +512 -0
  320. package/src/lab/automation/planner.ts +371 -0
  321. package/src/lab/automation/policy.ts +136 -0
  322. package/src/lab/automation/queue.ts +191 -0
  323. package/src/lab/automation/recovery.ts +24 -0
  324. package/src/lab/automation/route-context.ts +21 -0
  325. package/src/lab/automation/run-key.ts +44 -0
  326. package/src/lab/automation/runs-query.ts +34 -0
  327. package/src/lab/automation/types.ts +160 -0
  328. package/src/lab/conformance/assertion.ts +325 -0
  329. package/src/lab/conformance/digest.ts +22 -0
  330. package/src/lab/conformance/executor.ts +741 -0
  331. package/src/lab/conformance/fixture-provider.ts +27 -0
  332. package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
  333. package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
  334. package/src/lab/conformance/harness-budget.ts +47 -0
  335. package/src/lab/conformance/index.ts +5 -0
  336. package/src/lab/conformance/jcs.ts +64 -0
  337. package/src/lab/conformance/json-pointer.ts +39 -0
  338. package/src/lab/conformance/manifest.ts +180 -0
  339. package/src/lab/conformance/mcp-stub.ts +179 -0
  340. package/src/lab/conformance/negative-controls.ts +164 -0
  341. package/src/lab/conformance/observation.ts +355 -0
  342. package/src/lab/conformance/runner.ts +68 -0
  343. package/src/lab/conformance/sse-normalize.ts +59 -0
  344. package/src/lab/conformance/suite-manifest.ts +78 -0
  345. package/src/lab/conformance/types.ts +214 -0
  346. package/src/lab/constants.ts +126 -0
  347. package/src/lab/digest.ts +64 -0
  348. package/src/lab/events/errors.ts +9 -0
  349. package/src/lab/events/limits.ts +117 -0
  350. package/src/lab/events/types.ts +229 -0
  351. package/src/lab/events/validate.ts +781 -0
  352. package/src/lab/fabric/constants.ts +40 -0
  353. package/src/lab/fabric/executor.ts +492 -0
  354. package/src/lab/fabric/index.ts +80 -0
  355. package/src/lab/fabric/manifest.ts +222 -0
  356. package/src/lab/fabric/observe.ts +489 -0
  357. package/src/lab/fabric/patch.ts +79 -0
  358. package/src/lab/fabric/producer-child.ts +139 -0
  359. package/src/lab/fabric/producer-isolate.ts +276 -0
  360. package/src/lab/fabric/producer-protocol.ts +61 -0
  361. package/src/lab/fabric/scratch.ts +439 -0
  362. package/src/lab/fabric/subject.ts +106 -0
  363. package/src/lab/fabric/types.ts +134 -0
  364. package/src/lab/fabric/verifier.ts +98 -0
  365. package/src/lab/index.ts +54 -0
  366. package/src/lab/ledger/artifact-refs.ts +127 -0
  367. package/src/lab/ledger/invalidation.ts +136 -0
  368. package/src/lab/ledger/purge.ts +310 -0
  369. package/src/lab/ledger/store.ts +532 -0
  370. package/src/lab/live/credential-lease.ts +53 -0
  371. package/src/lab/live/destination.ts +155 -0
  372. package/src/lab/live/executor.ts +336 -0
  373. package/src/lab/live/inert-tools.ts +56 -0
  374. package/src/lab/live/manifest.ts +85 -0
  375. package/src/lab/live/mcp-loopback.ts +57 -0
  376. package/src/lab/live/runner.ts +19 -0
  377. package/src/lab/live/sandbox.ts +61 -0
  378. package/src/lab/live/suite-manifest.ts +41 -0
  379. package/src/lab/live/transport.ts +118 -0
  380. package/src/lab/live/types.ts +197 -0
  381. package/src/lab/observe/from-conformance.ts +301 -0
  382. package/src/lab/observe/from-live.ts +117 -0
  383. package/src/lab/paths.ts +153 -0
  384. package/src/lab/projection/rebuild.ts +495 -0
  385. package/src/lab/projection/schema.ts +135 -0
  386. package/src/lab/projection/verdicts.ts +474 -0
  387. package/src/lab/projection/verification.ts +412 -0
  388. package/src/lab/public/bundle.ts +217 -0
  389. package/src/lab/public/community-authority.ts +175 -0
  390. package/src/lab/public/community-files.ts +29 -0
  391. package/src/lab/public/community.ts +479 -0
  392. package/src/lab/public/file-safety.ts +155 -0
  393. package/src/lab/public/ids.ts +26 -0
  394. package/src/lab/public/index.ts +16 -0
  395. package/src/lab/public/mutation-lock.ts +424 -0
  396. package/src/lab/public/operator.ts +353 -0
  397. package/src/lab/public/origin-purge.ts +79 -0
  398. package/src/lab/public/origin.ts +203 -0
  399. package/src/lab/public/privacy.ts +143 -0
  400. package/src/lab/public/private-file.ts +261 -0
  401. package/src/lab/public/project.ts +124 -0
  402. package/src/lab/public/purge-test-fault.ts +21 -0
  403. package/src/lab/public/purge.ts +223 -0
  404. package/src/lab/public/registry.ts +44 -0
  405. package/src/lab/public/revocation.ts +252 -0
  406. package/src/lab/public/signature.ts +243 -0
  407. package/src/lab/public/storage.ts +105 -0
  408. package/src/lab/public/strict-json.ts +206 -0
  409. package/src/lab/public/time.ts +26 -0
  410. package/src/lab/public/types.ts +172 -0
  411. package/src/lab/public/validate.ts +391 -0
  412. package/src/lab/query/catalog.ts +101 -0
  413. package/src/lab/query/connection.ts +107 -0
  414. package/src/lab/query/constants.ts +4 -0
  415. package/src/lab/query/cursor.ts +132 -0
  416. package/src/lab/query/dto-map.ts +277 -0
  417. package/src/lab/query/errors.ts +22 -0
  418. package/src/lab/query/freshness.ts +53 -0
  419. package/src/lab/query/index.ts +45 -0
  420. package/src/lab/query/latest-observation.ts +59 -0
  421. package/src/lab/query/passive-production.ts +159 -0
  422. package/src/lab/query/queries.ts +444 -0
  423. package/src/lab/query/types.ts +266 -0
  424. package/src/lab/subject/behavior-fingerprint.ts +77 -0
  425. package/src/lab/subject/installation-salt.ts +112 -0
  426. package/src/lab/subject/protocol-subject.ts +80 -0
  427. package/src/lab/subject/route-subject.ts +74 -0
  428. package/src/lib/admin-secrets.ts +24 -0
  429. package/src/lib/app-owned-memory-stores.ts +22 -0
  430. package/src/lib/bounded-body.ts +167 -11
  431. package/src/lib/bun-runtime.ts +125 -12
  432. package/src/lib/bun-stream-caps.ts +13 -9
  433. package/src/lib/codex-restart-contract.ts +120 -0
  434. package/src/lib/config-ownership.ts +26 -2
  435. package/src/lib/destination-policy.ts +65 -1
  436. package/src/lib/errors.ts +80 -5
  437. package/src/lib/fabric-task-execution-authority.ts +7 -0
  438. package/src/lib/fabric-task-host.ts +29 -0
  439. package/src/lib/lab-activation.ts +223 -0
  440. package/src/lib/lab-live-execution-authority.ts +13 -0
  441. package/src/lib/lab-live-host.ts +30 -0
  442. package/src/lib/lab-live-pinned-sender.ts +56 -0
  443. package/src/lib/lab-live-route-production.ts +130 -0
  444. package/src/lib/lab-passive-linker-registration.ts +26 -0
  445. package/src/lib/local-management-attestation.ts +51 -0
  446. package/src/lib/local-management-capability.ts +100 -0
  447. package/src/lib/local-provider-reload-contract.ts +100 -0
  448. package/src/lib/optional-shutdown-hooks.ts +57 -0
  449. package/src/lib/package-tree-integrity.ts +101 -0
  450. package/src/lib/pinned-http.ts +145 -26
  451. package/src/lib/process-control.ts +6 -2
  452. package/src/lib/provider-outbound.ts +49 -9
  453. package/src/lib/redact.ts +419 -3
  454. package/src/lib/self-launch-argv.ts +15 -0
  455. package/src/lib/server-resource-ownership.ts +71 -0
  456. package/src/lib/service-secrets.ts +15 -0
  457. package/src/lib/shadow-call.ts +35 -4
  458. package/src/lib/sse-decoder.ts +41 -0
  459. package/src/lib/state-store-registrations.ts +10 -2
  460. package/src/lib/system-restart-contract.ts +73 -0
  461. package/src/lib/token-estimate.ts +19 -2
  462. package/src/lib/tool-argument-integers.ts +253 -0
  463. package/src/lib/translator-budget.ts +44 -0
  464. package/src/lib/upstream-http-version.ts +57 -0
  465. package/src/lib/upstream-reachability.ts +95 -0
  466. package/src/lib/upstream-retry.ts +156 -3
  467. package/src/lib/windows-atomic-replace.ts +155 -0
  468. package/src/lib/windows-elevation.ts +70 -2
  469. package/src/lib/windows-secret-acl.ts +409 -69
  470. package/src/lib/windows-service-wrappers.ts +72 -0
  471. package/src/lib/windows-text.ts +106 -0
  472. package/src/lib/windows-user-principal.ts +341 -0
  473. package/src/lib/winsw.ts +33 -5
  474. package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
  475. package/src/oauth/account-import/index.ts +15 -0
  476. package/src/oauth/account-import/parser.ts +83 -0
  477. package/src/oauth/account-import/registry.ts +18 -0
  478. package/src/oauth/account-import/service.ts +75 -0
  479. package/src/oauth/account-import/types.ts +91 -0
  480. package/src/oauth/anthropic.ts +12 -1
  481. package/src/oauth/callback-server.ts +30 -4
  482. package/src/oauth/chatgpt.ts +12 -1
  483. package/src/oauth/cline.ts +203 -0
  484. package/src/oauth/command-code.ts +239 -0
  485. package/src/oauth/cursor.ts +46 -5
  486. package/src/oauth/generic-account-failover.ts +231 -0
  487. package/src/oauth/google-antigravity.ts +35 -3
  488. package/src/oauth/health.ts +20 -12
  489. package/src/oauth/index.ts +462 -71
  490. package/src/oauth/key-providers.ts +25 -0
  491. package/src/oauth/kimi.ts +25 -3
  492. package/src/oauth/kiro.ts +95 -6
  493. package/src/oauth/local-token-detect.ts +13 -2
  494. package/src/oauth/log.ts +3 -1
  495. package/src/oauth/login-cli.ts +88 -28
  496. package/src/oauth/nous.ts +798 -0
  497. package/src/oauth/open-browser-choice.ts +26 -0
  498. package/src/oauth/store.ts +133 -26
  499. package/src/oauth/token-guardian.ts +9 -3
  500. package/src/oauth/types.ts +15 -0
  501. package/src/pi/models.ts +2 -2
  502. package/src/providers/alibaba-region-migration.ts +1 -1
  503. package/src/providers/antigravity-models.ts +521 -31
  504. package/src/providers/auto-compact-budget.ts +65 -0
  505. package/src/providers/base-url-choices.ts +10 -0
  506. package/src/providers/codex-capacity.ts +292 -0
  507. package/src/providers/command-code-efforts.ts +176 -0
  508. package/src/providers/context-cap.ts +26 -8
  509. package/src/providers/cursor-pool.ts +72 -0
  510. package/src/providers/default-aliases.ts +65 -0
  511. package/src/providers/derive.ts +281 -6
  512. package/src/providers/fastwire.ts +507 -0
  513. package/src/providers/free-directory.ts +10 -7
  514. package/src/providers/google-vertex-location.ts +14 -0
  515. package/src/providers/key-failover.ts +71 -3
  516. package/src/providers/label.ts +35 -2
  517. package/src/providers/model-discovery-limits.ts +16 -0
  518. package/src/providers/model-discovery.ts +115 -22
  519. package/src/providers/model-presets.ts +119 -0
  520. package/src/providers/model-rename-migration.ts +255 -0
  521. package/src/providers/model-rename-startup.ts +28 -0
  522. package/src/providers/new-model-policy.ts +146 -0
  523. package/src/providers/openai-sidecar.ts +72 -4
  524. package/src/providers/openai-tier-startup.ts +31 -2
  525. package/src/providers/openai-tiers.ts +119 -4
  526. package/src/providers/openai-virtual-models.ts +1 -0
  527. package/src/providers/opencode-zen-rate-limit.ts +102 -0
  528. package/src/providers/provider-id-rewrite.ts +30 -0
  529. package/src/providers/quota.ts +1379 -40
  530. package/src/providers/registry.ts +1545 -112
  531. package/src/providers/request-pacing.ts +310 -0
  532. package/src/providers/service-tier.ts +277 -0
  533. package/src/providers/slug-codec.ts +94 -6
  534. package/src/providers/static-model-discovery.ts +86 -0
  535. package/src/providers/xai-responses-opt-in.ts +15 -0
  536. package/src/providers/xai-transport.ts +32 -4
  537. package/src/reasoning-effort.ts +68 -3
  538. package/src/responses/apply-patch-envelope.ts +63 -0
  539. package/src/responses/code-mode-helper-compat.ts +50 -0
  540. package/src/responses/compaction.ts +26 -1
  541. package/src/responses/custom-tool-compat.ts +384 -0
  542. package/src/responses/hosted-tool-policy.ts +9 -0
  543. package/src/responses/namespace-tool-compat.ts +435 -0
  544. package/src/responses/parser.ts +225 -38
  545. package/src/responses/provider-continuation.ts +98 -0
  546. package/src/responses/provider-opaque-metadata.ts +73 -0
  547. package/src/responses/reasoning-envelope.ts +9 -1
  548. package/src/responses/reasoning-replay-cache.ts +504 -0
  549. package/src/responses/schema.ts +12 -2
  550. package/src/responses/spill-store.ts +75 -10
  551. package/src/responses/state.ts +659 -31
  552. package/src/responses/thought-signature-replay.ts +364 -0
  553. package/src/responses/tool-search-compat.ts +301 -0
  554. package/src/responses/truncated-stop-reason.ts +60 -0
  555. package/src/responses/turn-termination.ts +107 -0
  556. package/src/router.ts +405 -31
  557. package/src/routing/analytics.ts +379 -0
  558. package/src/routing/capability.ts +244 -0
  559. package/src/routing/compatibility/assemble.ts +73 -0
  560. package/src/routing/compatibility/behavior.ts +278 -0
  561. package/src/routing/compatibility/catalog.ts +99 -0
  562. package/src/routing/compatibility/endpoint.ts +52 -0
  563. package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
  564. package/src/routing/compatibility/policy.ts +181 -0
  565. package/src/routing/compatibility/provider-slot.ts +56 -0
  566. package/src/routing/compatibility/reader.ts +110 -0
  567. package/src/routing/compatibility/subject.ts +191 -0
  568. package/src/routing/compatibility/types.ts +64 -0
  569. package/src/routing/compatibility/version.ts +104 -0
  570. package/src/routing/cost.ts +77 -0
  571. package/src/routing/evaluator.ts +495 -0
  572. package/src/routing/health.ts +412 -0
  573. package/src/routing/history/cursor.ts +43 -0
  574. package/src/routing/history/indexer.ts +605 -0
  575. package/src/routing/history/schema.ts +72 -0
  576. package/src/routing/profile-namespace.ts +15 -0
  577. package/src/routing/profile.ts +547 -0
  578. package/src/routing/quota.ts +155 -0
  579. package/src/routing/request-evidence.ts +45 -0
  580. package/src/routing/trace.ts +776 -0
  581. package/src/server/adapter-resolve.ts +2 -29
  582. package/src/server/auth-cors.ts +289 -48
  583. package/src/server/background-lifecycle.ts +182 -0
  584. package/src/server/chat-completions.ts +154 -72
  585. package/src/server/chat-native-sse.ts +331 -0
  586. package/src/server/chat-native.ts +445 -0
  587. package/src/server/claude-messages.ts +160 -44
  588. package/src/server/direct-local-http.ts +347 -0
  589. package/src/server/effort-policy.ts +18 -0
  590. package/src/server/github-copilot-responses-repair.ts +338 -0
  591. package/src/server/gui-static.ts +39 -10
  592. package/src/server/images.ts +94 -12
  593. package/src/server/index.ts +1021 -190
  594. package/src/server/lifecycle.ts +336 -14
  595. package/src/server/live.ts +136 -17
  596. package/src/server/local-management-read-client.ts +90 -0
  597. package/src/server/local-provider-reload-client.ts +137 -0
  598. package/src/server/management/agent-settings-routes.ts +433 -116
  599. package/src/server/management/api-key-usage.ts +31 -5
  600. package/src/server/management/body.ts +6 -0
  601. package/src/server/management/codex-prompt-routes.ts +570 -0
  602. package/src/server/management/combo-routes.ts +64 -25
  603. package/src/server/management/config-routes.ts +511 -51
  604. package/src/server/management/context.ts +89 -2
  605. package/src/server/management/integration-routes.ts +498 -0
  606. package/src/server/management/lab-automation-routes.ts +206 -0
  607. package/src/server/management/lab-routes.ts +563 -0
  608. package/src/server/management/logs-usage-routes.ts +127 -52
  609. package/src/server/management/model-routes.ts +461 -133
  610. package/src/server/management/model-rows.ts +167 -0
  611. package/src/server/management/native-integration-routes.ts +770 -0
  612. package/src/server/management/oauth-account-routes.ts +90 -8
  613. package/src/server/management/provider-capability-config.ts +48 -0
  614. package/src/server/management/provider-routes.ts +836 -159
  615. package/src/server/management/request-history-routes.ts +191 -0
  616. package/src/server/management/route-registry.ts +332 -0
  617. package/src/server/management/routing-analytics-routes.ts +74 -0
  618. package/src/server/management/routing-profile-routes.ts +380 -0
  619. package/src/server/management/shared.ts +27 -11
  620. package/src/server/management/sidebar-routes.ts +47 -31
  621. package/src/server/management/storage-log-guard-routes.ts +186 -0
  622. package/src/server/management/sync-response.ts +69 -0
  623. package/src/server/management/system-restart.ts +289 -33
  624. package/src/server/management/system-routes.ts +77 -0
  625. package/src/server/management/usage-summary-cache.ts +9 -1
  626. package/src/server/management/vision-sidecar-options.ts +167 -0
  627. package/src/server/management/web-search-sidecar-options.ts +120 -0
  628. package/src/server/management-api.ts +126 -23
  629. package/src/server/management-auth.ts +220 -5
  630. package/src/server/passive-route-linker.ts +66 -0
  631. package/src/server/port-reclaim.ts +1 -1
  632. package/src/server/ports.ts +41 -1
  633. package/src/server/proxy-liveness.ts +161 -10
  634. package/src/server/readiness.ts +99 -0
  635. package/src/server/relay-eager.ts +221 -54
  636. package/src/server/relay.ts +479 -87
  637. package/src/server/request-decompress.ts +113 -6
  638. package/src/server/request-log-conversation.ts +51 -0
  639. package/src/server/request-log.ts +298 -31
  640. package/src/server/responses/agent-task-recovery-cache.ts +143 -0
  641. package/src/server/responses/agent-task-recovery.ts +465 -0
  642. package/src/server/responses/codex-auth-error.ts +55 -0
  643. package/src/server/responses/collaboration.ts +239 -36
  644. package/src/server/responses/combo-stream-preflight.ts +178 -0
  645. package/src/server/responses/compact.ts +444 -72
  646. package/src/server/responses/core.ts +3579 -436
  647. package/src/server/responses/empty-completion-guard.ts +333 -0
  648. package/src/server/responses/encrypted-payload.ts +62 -39
  649. package/src/server/responses/fetch-helpers.ts +89 -99
  650. package/src/server/responses/input-admission.ts +187 -0
  651. package/src/server/responses/pacing-overload.ts +13 -0
  652. package/src/server/responses/passthrough-error.ts +33 -9
  653. package/src/server/responses/policy-fallback.ts +178 -0
  654. package/src/server/responses/responses-field-backfill.ts +343 -0
  655. package/src/server/responses/terminal-guard.ts +26 -5
  656. package/src/server/responses/upstream-error.ts +5 -0
  657. package/src/server/responses/ws-upstream.ts +381 -0
  658. package/src/server/responses-custom-tool-repair.ts +363 -0
  659. package/src/server/responses-item-id-repair.ts +54 -6
  660. package/src/server/responses-json-events.ts +90 -0
  661. package/src/server/responses-model-rewrite.ts +29 -0
  662. package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
  663. package/src/server/responses-snapshot-repair.ts +621 -0
  664. package/src/server/responses-terminal-repair.ts +363 -0
  665. package/src/server/responses-tool-search-repair.ts +267 -0
  666. package/src/server/responses-undeclared-tool-guard.ts +376 -0
  667. package/src/server/responses.ts +18 -2
  668. package/src/server/search.ts +78 -13
  669. package/src/server/sse-frame-buffer.ts +319 -0
  670. package/src/server/sse-payload-rewrite.ts +110 -22
  671. package/src/server/startup-action-control.ts +8 -1
  672. package/src/server/startup-health-cache.ts +19 -1
  673. package/src/server/system-env.ts +80 -9
  674. package/src/server/ws-bridge.ts +53 -40
  675. package/src/service-manager-probe.ts +892 -0
  676. package/src/service.ts +1153 -94
  677. package/src/sidecar/auth.ts +92 -0
  678. package/src/sidecar/candidates.ts +83 -0
  679. package/src/storage/cleanup.ts +2 -2
  680. package/src/storage/policy-job.ts +14 -4
  681. package/src/storage/policy.ts +88 -23
  682. package/src/storage/scanner.ts +1 -1
  683. package/src/storage/worker-lifecycle.ts +14 -14
  684. package/src/tray/windows-tray.ps1 +83 -9
  685. package/src/tray/windows.ts +43 -16
  686. package/src/types/accounts.ts +37 -0
  687. package/src/types/config.ts +911 -0
  688. package/src/types/provider.ts +614 -0
  689. package/src/types/request.ts +397 -0
  690. package/src/types/tools.ts +236 -0
  691. package/src/types/wire.ts +80 -0
  692. package/src/types.ts +107 -1237
  693. package/src/update/index.ts +37 -23
  694. package/src/update/job.ts +445 -68
  695. package/src/update/notify.ts +12 -6
  696. package/src/update/npm-cache-preflight.d.mts +47 -0
  697. package/src/update/npm-cache-preflight.mjs +201 -0
  698. package/src/update/transactional-install.d.mts +22 -0
  699. package/src/update/transactional-install.mjs +266 -0
  700. package/src/usage/cost.ts +0 -0
  701. package/src/usage/expected-prices.ts +287 -16
  702. package/src/usage/log.ts +666 -50
  703. package/src/usage/summary.ts +388 -17
  704. package/src/usage/user-cost-overlay-reconciler.ts +313 -0
  705. package/src/usage/user-cost-overlays.ts +314 -0
  706. package/src/vision/anthropic-describe.ts +10 -6
  707. package/src/vision/backends.ts +97 -0
  708. package/src/vision/describe.ts +27 -16
  709. package/src/vision/eligibility.ts +250 -0
  710. package/src/vision/index.ts +238 -24
  711. package/src/vision/reasoning.ts +55 -0
  712. package/src/vision/routed-describe.ts +175 -0
  713. package/src/vision/timeout-bounds.ts +9 -0
  714. package/src/web-search/anthropic-executor.ts +13 -7
  715. package/src/web-search/backends.ts +108 -0
  716. package/src/web-search/exa-executor.ts +119 -0
  717. package/src/web-search/executor.ts +21 -6
  718. package/src/web-search/gemini-executor.ts +141 -0
  719. package/src/web-search/index.ts +150 -15
  720. package/src/web-search/loop.ts +284 -54
  721. package/src/web-search/parse.ts +125 -30
  722. package/src/web-search/sources.ts +60 -0
  723. package/src/web-search/xai-executor.ts +219 -0
  724. package/gui/dist/assets/index-DTpMHS4F.js +0 -67
  725. package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
  726. package/gui/dist/provider-icons/antigravity.svg +0 -1
  727. package/gui/dist/provider-icons/claude.svg +0 -1
  728. package/gui/dist/provider-icons/copilot.svg +0 -1
  729. package/gui/dist/provider-icons/cursor.svg +0 -2
  730. package/gui/dist/provider-icons/gemini.svg +0 -1
  731. package/gui/dist/provider-icons/grok-color.svg +0 -1
  732. package/gui/dist/provider-icons/kiro.svg +0 -14
  733. package/src/cli/internal-dispatch.ts +0 -20
@@ -130,6 +130,50 @@ function registryAllowsPrivateNetwork(name: string): boolean {
130
130
  return getProviderRegistryEntry(name)?.allowPrivateNetworkByDefault === true;
131
131
  }
132
132
 
133
+ /**
134
+ * OAuth registry entries that opt into `allowBaseUrlOverride` send bearer credentials to a
135
+ * user-configured endpoint (review findings, PR #2109 / PR #2110): a cleartext `http:`
136
+ * override would expose the OAuth token on the wire. `https:` is therefore required for
137
+ * every non-local destination. Loopback/localhost/private relays keep working over
138
+ * `http:` because they already sit behind the explicit `allowPrivateNetwork` opt-in
139
+ * enforced by {@link providerDestinationConfigError}. Keyed/local providers (Ollama,
140
+ * vLLM, LM Studio, LiteLLM, Moonshot, Qwen, Alibaba) are untouched: they are not
141
+ * `authKind: "oauth"`, so this check never fires for them.
142
+ */
143
+ function registrySendsOAuthToOverriddenBaseUrl(name: string): boolean {
144
+ const entry = getProviderRegistryEntry(name);
145
+ return entry?.authKind === "oauth" && entry.allowBaseUrlOverride === true;
146
+ }
147
+
148
+ export function providerSecureTransportConfigError(
149
+ name: string,
150
+ provider: Pick<OcxProviderConfig, "baseUrl" | "allowPrivateNetwork">,
151
+ ): string | null {
152
+ if (!registrySendsOAuthToOverriddenBaseUrl(name)) return null;
153
+ let parsed: URL;
154
+ try {
155
+ parsed = new URL(provider.baseUrl.trim());
156
+ } catch {
157
+ return null; // invalid URLs are providerBaseUrlConfigError's concern
158
+ }
159
+ if (parsed.protocol !== "http:") return null;
160
+ const assessment = assessDestination(provider.baseUrl);
161
+ // Classify FIRST, then consult the opt-in. `allowPrivateNetwork` says "this destination is
162
+ // intentionally local", which is a statement about the address, not a waiver of transport
163
+ // security — reading it before classification let `http://attacker.example` with the opt-in
164
+ // set carry an OAuth bearer in cleartext to a public host.
165
+ if (!assessment) return null;
166
+ const local = assessment.kind === "localhost"
167
+ || assessment.kind === "loopback"
168
+ || assessment.kind === "private";
169
+ if (local && providerAllowsPrivateNetwork(name, provider)) {
170
+ // A genuinely local relay over http stays reachable through the explicit opt-in; the
171
+ // private-network gate still governs whether it may be reached at all.
172
+ return null;
173
+ }
174
+ return "baseUrl must use https: this provider sends OAuth credentials to its endpoint, and http is allowed only for loopback/private relays";
175
+ }
176
+
133
177
  /**
134
178
  * Whether a provider may reach loopback/private addresses.
135
179
  *
@@ -150,6 +194,8 @@ export function providerAllowsPrivateNetwork(
150
194
  }
151
195
 
152
196
  export function providerDestinationConfigError(name: string, provider: Pick<OcxProviderConfig, "baseUrl" | "allowPrivateNetwork">): string | null {
197
+ const secureTransportError = providerSecureTransportConfigError(name, provider);
198
+ if (secureTransportError) return secureTransportError;
153
199
  const assessment = assessDestination(provider.baseUrl);
154
200
  if (!assessment) return null;
155
201
  if (assessment.kind === "public" || assessment.kind === "hostname") return null;
@@ -241,10 +287,18 @@ export function assessUrlDestination(url: string): UrlDestinationAssessment | nu
241
287
  * Returns the validated addresses so direct callers can pin the connect peer and
242
288
  * avoid a second, rebindable resolution. DNS failures remain fail-closed here;
243
289
  * the provider proxy wrapper alone may recognize that typed failure and degrade.
290
+ *
291
+ * `allowBenchmarkAddresses` is an explicit outbound-only opt-in for Clash/Surge/
292
+ * Mihomo fake-IP DNS (IANA benchmark space 198.18.0.0/15, credit #1748): a hostname
293
+ * answer in that range is accepted without marking the destination private, so the
294
+ * caller can keep the hostname on its configured HTTP(S) proxy path. It applies to
295
+ * resolved answers only — a literal 198.18.x URL still rejects — and mixed answers
296
+ * that include any other non-public address still fail. Callers that do not pass it
297
+ * (image and Lab fetch) keep rejecting benchmark space.
244
298
  */
245
299
  export async function resolvePublicAddresses(
246
300
  url: string,
247
- options?: string | { context?: string; allowPrivateNetwork?: boolean },
301
+ options?: string | { context?: string; allowPrivateNetwork?: boolean; allowBenchmarkAddresses?: boolean },
248
302
  ): Promise<{
249
303
  hostname: string;
250
304
  addresses: { address: string; family: number }[];
@@ -254,6 +308,7 @@ export async function resolvePublicAddresses(
254
308
  ? `${options.trim() || "image"} URL`
255
309
  : options?.context?.trim() || "image URL";
256
310
  const privateNetworkAllowed = typeof options === "object" && options?.allowPrivateNetwork === true;
311
+ const benchmarkAllowed = typeof options === "object" && options?.allowBenchmarkAddresses === true;
257
312
  let hostname: string;
258
313
  try {
259
314
  hostname = normalizeHostname(new URL(url.trim()).hostname);
@@ -294,6 +349,14 @@ export async function resolvePublicAddresses(
294
349
  const ipKind = isIP(address) || (family === 4 || family === 6 ? family : 0);
295
350
  const assessment = ipKind === 4 ? classifyIpv4(address) : ipKind === 6 ? classifyIpv6(normalizeHostname(address)) : null;
296
351
  if (!assessment || assessment.kind !== "public") {
352
+ // Hostname → 198.18.0.0/15 under the explicit opt-in is Clash/Surge/Mihomo
353
+ // fake-IP DNS, not a LAN provider. Accept it without allowPrivateNetwork and
354
+ // do not mark the destination private, so the caller's HTTP(S)_PROXY path
355
+ // still applies (credit #1748).
356
+ if (benchmarkAllowed && assessment?.kind === "private" && assessment.detail === "benchmark address") {
357
+ validatedAddresses.push({ address, family: ipKind === 4 || ipKind === 6 ? ipKind : (family || 4) });
358
+ continue;
359
+ }
297
360
  const allowedPrivateAddress = privateNetworkAllowed
298
361
  && assessment
299
362
  && (assessment.kind === "loopback" || assessment.kind === "private");
@@ -314,3 +377,4 @@ export async function resolvePublicAddresses(
314
377
  export async function assertUrlResolvesPublic(url: string): Promise<void> {
315
378
  await resolvePublicAddresses(url);
316
379
  }
380
+
package/src/lib/errors.ts CHANGED
@@ -6,11 +6,18 @@ export interface OcxErrorPayload {
6
6
 
7
7
  /** OpenAI / Codex hard block for high-risk cybersecurity activity (HTTP 400 or mid-stream). */
8
8
  export const CYBER_POLICY_ERROR_CODE = "cyber_policy";
9
+ export const CYBER_POLICY_FALLBACK_MESSAGE = "Request blocked by the upstream cybersecurity policy.";
9
10
 
10
11
  export function isCyberPolicyCode(code: string | null | undefined): boolean {
11
12
  return code === CYBER_POLICY_ERROR_CODE;
12
13
  }
13
14
 
15
+ /** Preserve a structured upstream error type; otherwise use the dedicated policy identity. */
16
+ export function cyberPolicyErrorType(type: string | null | undefined): string {
17
+ const trimmed = typeof type === "string" ? type.trim() : "";
18
+ return trimmed || CYBER_POLICY_ERROR_CODE;
19
+ }
20
+
14
21
  /**
15
22
  * Detect OpenAI cyber-policy refusals from message text when structured `code` was stripped.
16
23
  * Matches Codex fallback copy and Cursor/API agent wording (session evidence 2026-07-24).
@@ -43,6 +50,25 @@ function isSubscriptionGateMessage(text: string): boolean {
43
50
  );
44
51
  }
45
52
 
53
+ function isLocalAclHardeningMessage(text: string): boolean {
54
+ const secretPathHardening = text.includes("secret path") && (
55
+ text.includes("acl") ||
56
+ text.includes("harden") ||
57
+ text.includes("permission") ||
58
+ text.includes("access denied") ||
59
+ text.includes("inheritance")
60
+ );
61
+ return (
62
+ text.includes("icacls") ||
63
+ text.includes("acl hardening") ||
64
+ text.includes("ntfs") ||
65
+ secretPathHardening ||
66
+ text.includes("inheritance:r") ||
67
+ /\bwindows\b.*\bacl\b/.test(text) ||
68
+ text.includes("/grant:r")
69
+ );
70
+ }
71
+
46
72
  function isAuthenticationMessage(text: string): boolean {
47
73
  const accessDeniedWithCredentialCue = (
48
74
  text.includes("access denied") ||
@@ -120,9 +146,20 @@ export function classifyError(status: number, type: string, message: string): Oc
120
146
  return { message, type: "invalid_request_error", code: "client_closed_request" };
121
147
  }
122
148
  // Codex only shows the dedicated cyber UI when error.code === "cyber_policy".
123
- // Prefer that code (and invalid_request_error) over generic remaps / 502 upstream_server_error.
149
+ // The public wire does not establish invalid_request_error as the canonical type, so
150
+ // message-only classification keeps the dedicated identity instead of inventing one.
151
+ // Structured callers re-apply their real upstream type with cyberPolicyErrorType().
124
152
  if (type === CYBER_POLICY_ERROR_CODE || isCyberPolicyMessage(text)) {
125
- return { message, type: "invalid_request_error", code: CYBER_POLICY_ERROR_CODE };
153
+ return { message, type: CYBER_POLICY_ERROR_CODE, code: CYBER_POLICY_ERROR_CODE };
154
+ }
155
+ // A LOCAL preflight refusal keeps its own code (#1524). The message necessarily says
156
+ // "context window" -- that is what it is refusing on -- so the generic remap below would
157
+ // rewrite it to `context_length_exceeded` and make it indistinguishable from an UPSTREAM
158
+ // verdict. The two need opposite fallback handling: ours means "this candidate does not
159
+ // fit", theirs means "the request is impossible", so collapsing them ended the chain at
160
+ // the first candidate that was merely too small.
161
+ if (type === "input_admission_refused") {
162
+ return { message, type: "invalid_request_error", code: "input_admission_refused" };
126
163
  }
127
164
  if (
128
165
  text.includes("context_length_exceeded") ||
@@ -134,11 +171,16 @@ export function classifyError(status: number, type: string, message: string): Oc
134
171
  return { message, type: "invalid_request_error", code: "context_length_exceeded" };
135
172
  }
136
173
  // "Cursor resource limit exceeded" is emitted only for explicit request-size overflow
137
- // details (isCursorRequestTooLargeDetail in cursor-errors.ts); quota-style resource
138
- // exhaustion arrives as "Cursor rate limit exceeded" and falls through to 429 below.
174
+ // details (isCursorRequestTooLargeDetail in cursor-errors.ts); "Cursor context limit
175
+ // exceeded" is the bare payload-overflow shape (isCursorZeroTokenResourceExhausted);
176
+ // quota-style resource exhaustion arrives as "Cursor rate limit exceeded" and falls
177
+ // through to 429 below.
139
178
  if (text.includes("cursor resource limit exceeded")) {
140
179
  return { message, type: "invalid_request_error", code: "tool_catalog_too_large" };
141
180
  }
181
+ if (text.includes("cursor context limit exceeded")) {
182
+ return { message, type: "invalid_request_error", code: "context_length_exceeded" };
183
+ }
142
184
  // The Cursor adapter's classified rate-limit prefix is authoritative: its DETAIL may echo
143
185
  // quota wording ("... quota exhausted") that would otherwise hit the insufficient_quota
144
186
  // branch below and break the planned retry-with-backoff contract (WP3 review blocker 1).
@@ -170,6 +212,11 @@ export function classifyError(status: number, type: string, message: string): Oc
170
212
  if (type === "origin_rejected") {
171
213
  return { message, type: "invalid_request_error", code: "origin_rejected" };
172
214
  }
215
+ // Local ACL setup failures can contain provider-like auth wording (for example
216
+ // "access denied" or "authentication") but represent unavailable infrastructure.
217
+ if (status === 503 && isLocalAclHardeningMessage(text)) {
218
+ return { message, type: "server_error", code: "upstream_server_error" };
219
+ }
173
220
  // HTTP 401 and explicit auth failures are authoritative even when provider text
174
221
  // also advertises an upgrade or subscription.
175
222
  if (
@@ -273,6 +320,7 @@ export function inferHttpStatusFromAdapterMessage(message: string): number {
273
320
  // See classifyError: this prefix now only means explicit request-size overflow (400);
274
321
  // quota-style Cursor resource exhaustion carries the rate-limit prefix and maps to 429.
275
322
  if (lower.includes("cursor resource limit exceeded")) return 400;
323
+ if (lower.includes("cursor context limit exceeded")) return 400;
276
324
  if (
277
325
  lower.includes("resource_exhausted") ||
278
326
  lower.includes("resource exhausted") ||
@@ -280,10 +328,19 @@ export function inferHttpStatusFromAdapterMessage(message: string): number {
280
328
  lower.includes("too many requests") ||
281
329
  lower.includes("throttling")
282
330
  ) return 429;
331
+ // Local Windows filesystem hardening is infrastructure, not provider authentication.
332
+ // Keep this ahead of auth/permission and timeout keyword inference.
333
+ if (isLocalAclHardeningMessage(lower)) return 503;
283
334
  // Strong authentication signals win when a message contains mixed auth and
284
335
  // subscription/permission wording.
285
336
  if (isAuthenticationMessage(lower)) return 401;
286
337
  if (isSubscriptionGateMessage(lower) || isPermissionMessage(lower)) return 403;
338
+ // Same precedence rule as classifyCursorError: an explicit gRPC FAILED_PRECONDITION is a
339
+ // structured, deterministic rejection, so it outranks the overload keywords that routinely
340
+ // appear beside it ("failed_precondition: model unavailable for this plan"). Without this,
341
+ // the message matched "unavailable" and returned a retryable 503, so clients kept retrying
342
+ // a rejection that can never succeed.
343
+ if (lower.includes("failed_precondition") || lower.includes("failed precondition")) return 400;
287
344
  if (
288
345
  lower.includes("unavailable") ||
289
346
  lower.includes("overloaded") ||
@@ -359,6 +416,24 @@ export function httpStatusFromTerminalError(error: {
359
416
  if (message && isClientClosedMessage(message)) return 499;
360
417
  if (error.type === "invalid_request_error") return 400;
361
418
  if (error.type === "proxy_error") return 500;
362
- if (message) return inferHttpStatusFromAdapterMessage(message);
419
+ // A structured server class must not be downgraded to a CLIENT error by message wording.
420
+ // classifyError assigns `server_error` + `upstream_server_error` to every 5xx it sees, so
421
+ // the class is authoritative about blame: the upstream failed, the caller did not send a
422
+ // bad request. What it is NOT authoritative about is which server status fits — a stall is
423
+ // genuinely 504 and an overload genuinely 503, and flattening those to 502 discards
424
+ // information both the log surface and the retry policy read. So message inference still
425
+ // chooses the specific status, and only a client-error verdict is overridden.
426
+ //
427
+ // The override is deliberately narrowed to 400 alone. 429, 499, 401 and 403 are all
428
+ // actionable signals the caller routes on — retry-after, client cancellation, re-auth,
429
+ // entitlement — and overriding them would trade one kind of misreport for another. 400 is
430
+ // the single verdict that both blames the caller and stops the retry, which is the failure
431
+ // being fixed: an upstream 500 whose text happens to contain "malformed" or "invalid
432
+ // request" used to return 400, so Claude Code stopped retrying a retryable failure.
433
+ const structuredServerClass = error.type === "server_error" || error.code === "upstream_server_error";
434
+ if (message) {
435
+ const inferred = inferHttpStatusFromAdapterMessage(message);
436
+ return structuredServerClass && inferred === 400 ? 502 : inferred;
437
+ }
363
438
  return 502;
364
439
  }
@@ -0,0 +1,7 @@
1
+ import type { TrustedFabricPatchExecutor } from "../lab/fabric/types";
2
+ import { isHostIssuedFabricPatchExecutor } from "./fabric-task-host";
3
+
4
+ /** Trusted exact-route fabric patch executor recognition facade. */
5
+ export function isTrustedFabricPatchExecutor(value: unknown): value is TrustedFabricPatchExecutor {
6
+ return isHostIssuedFabricPatchExecutor(value);
7
+ }
@@ -0,0 +1,29 @@
1
+ import type { FabricPatchExecutor, FabricPatchExecutorInput, TrustedFabricPatchExecutor } from "../lab/fabric/types";
2
+
3
+ const HOST_ISSUED_EXECUTORS = new WeakSet<object>();
4
+
5
+ /**
6
+ * Trusted host integration boundary for CL-07 exact-route fabric patch production.
7
+ *
8
+ * @internal host integration only
9
+ */
10
+ export function createHostIssuedFabricPatchExecutor(
11
+ executorModulePath: string,
12
+ execute: FabricPatchExecutor,
13
+ ): TrustedFabricPatchExecutor {
14
+ const capability: TrustedFabricPatchExecutor = Object.freeze({
15
+ executorModulePath,
16
+ execute: (input: FabricPatchExecutorInput) => Promise.resolve(execute(input)),
17
+ });
18
+ HOST_ISSUED_EXECUTORS.add(capability);
19
+ return capability;
20
+ }
21
+
22
+ /** Internal recognition check consumed by the read-only authority facade. */
23
+ export function isHostIssuedFabricPatchExecutor(value: unknown): value is TrustedFabricPatchExecutor {
24
+ if (typeof value !== "object" || value === null || !HOST_ISSUED_EXECUTORS.has(value as object)) return false;
25
+ const capability = value as TrustedFabricPatchExecutor;
26
+ return typeof capability.executorModulePath === "string"
27
+ && capability.executorModulePath.length > 0
28
+ && typeof capability.execute === "function";
29
+ }
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Compatibility Lab activation.
3
+ *
4
+ * The proxy core does not import Lab. This module is the seam that does, and the startup
5
+ * composition root calls it only when the install actually uses Lab, so a user with no
6
+ * routing profile and no automation executes no Lab code and starts no Lab timer.
7
+ *
8
+ * Activation is SYNCHRONOUS by design. Three audit rounds established that a deferred
9
+ * activation window is unpatchable: `routeModelInternal` is sync, and so is the
10
+ * subagent-fallback chain that calls `routeModel`, so those callers have nowhere to await.
11
+ * During such a window a policy alias would be silently dropped from a fallback chain and
12
+ * the subagent would run on a different model than the operator configured. Registration
13
+ * therefore completes before `startServer` returns, inside the same synchronous turn as
14
+ * `Bun.serve`, so no request can observe an unregistered slot.
15
+ *
16
+ * See devlog/_fin/260814_lab_core_decoupling/080_activation_is_synchronous.md
17
+ *
18
+ * Startup degrades, explicit operator action reports. This asymmetry is deliberate: an
19
+ * invalid automation config disables automation with a warning here, but the management
20
+ * API and CLI paths that start a scheduler leave `LabAutomationError` to surface (the
21
+ * management route maps it to a 400). Someone who just toggled automation should see the
22
+ * validation error; someone merely starting the proxy should not lose unrelated traffic.
23
+ *
24
+ * @internal host integration only
25
+ */
26
+ import { existsSync, readFileSync } from "node:fs";
27
+ import { dirname, join } from "node:path";
28
+ import { labAutomationPolicyPath } from "../lab/paths";
29
+ import type { OcxConfig } from "../types";
30
+ import { LabAutomationError } from "../lab/automation/types";
31
+ import { registerLabPassiveRouteLinker } from "./lab-passive-linker-registration";
32
+ import { registerCurrentServerResourceCleanup } from "./server-resource-ownership";
33
+ import { setCompatibilityEvidenceProvider } from "../routing/compatibility/provider-slot";
34
+ import { labCompatibilityEvidenceProvider } from "../routing/compatibility/lab-evidence-provider";
35
+ import {
36
+ setLabAutomationDispatchDeps,
37
+ startLabAutomationScheduler,
38
+ } from "../lab/automation/orchestrator";
39
+ import { createProductionLabRouteExecutor } from "./lab-live-route-production";
40
+
41
+ interface LabRuntimeBinding {
42
+ release(): void;
43
+ }
44
+
45
+ interface LabActivationRecord {
46
+ staticDetach: Array<() => void>;
47
+ runtime: LabRuntimeBinding | null;
48
+ seenRuntimeConfigs: WeakSet<OcxConfig>;
49
+ }
50
+
51
+ /** Activation records keyed by configDir, so one process can own several configs. */
52
+ const activated = new Map<string, LabActivationRecord>();
53
+
54
+ const activationKey = (configDir?: string): string => configDir ?? "";
55
+
56
+ function readJsonIfPresent(path: string): unknown {
57
+ try {
58
+ if (!existsSync(path)) return null;
59
+ return JSON.parse(readFileSync(path, "utf8")) as unknown;
60
+ } catch {
61
+ // A malformed or unreadable file means "not enabled": this detector must never throw
62
+ // during startup, and must never import Lab persistence to answer the question.
63
+ return null;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * True when Lab automation is enabled on disk.
69
+ *
70
+ * Mirrors `loadLabAutomationConfig` precedence deliberately: the current authority is the
71
+ * combined `automation-config.json`, with `automation-policy.json` as the legacy fallback.
72
+ * Reading only the legacy file would miss every install that enabled automation through
73
+ * the current dashboard.
74
+ */
75
+ export function labAutomationEnabledOnDisk(configDir?: string): boolean {
76
+ const legacyPath = labAutomationPolicyPath(configDir);
77
+ const combined = readJsonIfPresent(join(dirname(legacyPath), "automation-config.json"));
78
+ if (combined && typeof combined === "object") {
79
+ const policy = (combined as { policy?: unknown }).policy;
80
+ if (policy && typeof policy === "object") {
81
+ return (policy as { enabled?: unknown }).enabled === true;
82
+ }
83
+ }
84
+ const legacy = readJsonIfPresent(legacyPath);
85
+ if (legacy && typeof legacy === "object") {
86
+ return (legacy as { enabled?: unknown }).enabled === true;
87
+ }
88
+ return false;
89
+ }
90
+
91
+ /** True when this install actually uses Lab: any routing profile, or automation enabled. */
92
+ export function labActivationRequired(config: OcxConfig, configDir?: string): boolean {
93
+ if (Object.keys(config.routingProfiles ?? {}).length > 0) return true;
94
+ return labAutomationEnabledOnDisk(configDir);
95
+ }
96
+
97
+ function startAutomationIfEnabled(configDir?: string): void {
98
+ if (!labAutomationEnabledOnDisk(configDir)) return;
99
+ try {
100
+ startLabAutomationScheduler(configDir);
101
+ } catch (err) {
102
+ // Neither a malformed automation file nor a busy state lock may take the proxy down
103
+ // at startup. Lab automation stays off for this run; routing, evidence, and every
104
+ // other subsystem keep working.
105
+ //
106
+ // The two causes get different messages because they need different actions, and a
107
+ // lock-contention failure reported as "invalid config" sends the operator to fix a
108
+ // file that is fine. Contention can also stall startup by up to the 5s lock wait.
109
+ const code = err instanceof LabAutomationError ? err.code : null;
110
+ if (code === "state_lock_busy" || code === "state_lock_failed") {
111
+ console.warn(
112
+ "[lab] Lab automation did not start: another process holds the automation state lock."
113
+ + " Automation stays off for this run and will be retried on the next start.",
114
+ );
115
+ } else {
116
+ console.warn(
117
+ "[lab] Lab automation is disabled for this run because its configuration could not be"
118
+ + " loaded:",
119
+ err instanceof Error ? err.message : err,
120
+ );
121
+ }
122
+ }
123
+ }
124
+
125
+ function installLabAutomationRuntime(
126
+ record: LabActivationRecord,
127
+ config: OcxConfig,
128
+ configDir?: string,
129
+ ): void {
130
+ const previous = record.runtime;
131
+ const routeExecutor = createProductionLabRouteExecutor({ configDir, loadConfig: () => config });
132
+ const releaseDispatchDeps = setLabAutomationDispatchDeps({
133
+ configDir,
134
+ loadConfig: () => config,
135
+ routeExecutor,
136
+ });
137
+
138
+ let released = false;
139
+ let detachOwnerCleanup = () => {};
140
+ const binding: LabRuntimeBinding = {
141
+ release() {
142
+ if (released) return;
143
+ released = true;
144
+ detachOwnerCleanup();
145
+ releaseDispatchDeps();
146
+ if (record.runtime === binding) record.runtime = null;
147
+ },
148
+ };
149
+ // `setLabAutomationDispatchDeps` already registers its own owner cleanup. This second
150
+ // receipt only keeps the activation record in sync with that owner-scoped lifetime, so a
151
+ // later same-process server can see that the static Lab slots survived but CL-08 authority
152
+ // did not. The release is idempotent, so cleanup order does not matter.
153
+ detachOwnerCleanup = registerCurrentServerResourceCleanup(binding.release);
154
+
155
+ // Install the successor before releasing the predecessor. The dispatcher token check then
156
+ // makes the predecessor release a no-op for the successor scheduler/authority.
157
+ record.runtime = binding;
158
+ record.seenRuntimeConfigs.add(config);
159
+ previous?.release();
160
+ }
161
+
162
+ /**
163
+ * Register Lab into the core slots. Static activation is idempotent per configDir. The
164
+ * server-owned CL-08 runtime binding is refreshed when its prior owner ended or when a new
165
+ * server instance arrives with a config object that has not owned this activation before.
166
+ */
167
+ export function activateLab(config: OcxConfig, configDir?: string): void {
168
+ const key = activationKey(configDir);
169
+ const existing = activated.get(key);
170
+ if (existing) {
171
+ // A released predecessor leaves the static Lab slots resident but removes dispatch
172
+ // authority and its scheduler. A live successor uses a fresh config object. Rebind in
173
+ // either case, but never let an older already-seen server steal authority back from a
174
+ // newer successor merely because it receives another management request.
175
+ if (existing.runtime === null || !existing.seenRuntimeConfigs.has(config)) {
176
+ installLabAutomationRuntime(existing, config, configDir);
177
+ startAutomationIfEnabled(configDir);
178
+ }
179
+ return;
180
+ }
181
+
182
+ // INVARIANT: static activation is all-or-nothing and reason-independent. Every static
183
+ // slot is registered here regardless of WHY activation was required, so automation-only
184
+ // activation still installs the compatibility provider a later profile needs.
185
+ const record: LabActivationRecord = {
186
+ staticDetach: [],
187
+ runtime: null,
188
+ seenRuntimeConfigs: new WeakSet<OcxConfig>(),
189
+ };
190
+ record.staticDetach.push(registerLabPassiveRouteLinker(configDir));
191
+ record.staticDetach.push(setCompatibilityEvidenceProvider(labCompatibilityEvidenceProvider));
192
+ installLabAutomationRuntime(record, config, configDir);
193
+
194
+ // Record the activation BEFORE the scheduler start. startLabAutomationScheduler runs the
195
+ // full automation normalizer, which throws on any field violation, and this call sits on
196
+ // the startup path of every install that has a routing profile. Storing the record first
197
+ // means a throw cannot orphan the detach receipts and leave slots registered with no
198
+ // activation record, which would let a later activateLab register them a second time.
199
+ activated.set(key, record);
200
+ startAutomationIfEnabled(configDir);
201
+ }
202
+
203
+ /** True when this configDir has been activated. */
204
+ export function isLabActivated(configDir?: string): boolean {
205
+ return activated.has(activationKey(configDir));
206
+ }
207
+
208
+ /**
209
+ * Test-only teardown. Deactivation is deliberately NOT a production path: tearing an
210
+ * activation down mid-run would finalize in-flight automation as ineligible rather than
211
+ * cancelled, and the orchestrator's shutdown signal is process-global. An install that
212
+ * creates then deletes a profile keeps Lab resident until restart, which does not affect
213
+ * users who never opted in.
214
+ */
215
+ export function resetLabActivationForTests(): void {
216
+ for (const [key, record] of [...activated]) {
217
+ activated.delete(key);
218
+ try { record.runtime?.release(); } catch { /* teardown is best-effort */ }
219
+ for (const release of [...record.staticDetach].reverse()) {
220
+ try { release(); } catch { /* teardown is best-effort */ }
221
+ }
222
+ }
223
+ }
@@ -0,0 +1,13 @@
1
+ import type { TrustedLabRouteExecutor } from "../lab/live/types";
2
+ import { isHostIssuedLabRouteExecutor } from "./lab-live-host";
3
+
4
+ /**
5
+ * Read-only trust check used by the Lab executor.
6
+ *
7
+ * Capability issuance intentionally does not live in this authority module: callers cannot
8
+ * self-attest sandbox boundary names here. Only the dedicated trusted host integration may
9
+ * construct and register an evidence-eligible route executor.
10
+ */
11
+ export function isTrustedLabRouteExecutor(value: unknown): value is TrustedLabRouteExecutor {
12
+ return isHostIssuedLabRouteExecutor(value);
13
+ }
@@ -0,0 +1,30 @@
1
+ import type { LabRouteExecutor, TrustedLabRouteExecutor } from "../lab/live/types";
2
+ import { REQUIRED_LAB_SANDBOX_BOUNDARIES } from "../lab/live/types";
3
+
4
+ const HOST_ISSUED_EXECUTORS = new WeakSet<object>();
5
+
6
+ /**
7
+ * Trusted host integration boundary for CL-03 exact-route execution.
8
+ *
9
+ * This module is intentionally not re-exported from the Lab public surface. The host owns
10
+ * enforcement of every mandatory sandbox boundary and therefore supplies no caller-controlled
11
+ * boundary list. Adapters/providers receive only the resulting opaque capability.
12
+ *
13
+ * @internal host integration only
14
+ */
15
+ export function createHostIssuedLabRouteExecutor(execute: LabRouteExecutor): TrustedLabRouteExecutor {
16
+ const capability: TrustedLabRouteExecutor = Object.freeze({
17
+ execute,
18
+ enforcedBoundaries: Object.freeze([...REQUIRED_LAB_SANDBOX_BOUNDARIES]),
19
+ });
20
+ HOST_ISSUED_EXECUTORS.add(capability);
21
+ return capability;
22
+ }
23
+
24
+ /** Internal recognition check consumed by the read-only authority facade. */
25
+ export function isHostIssuedLabRouteExecutor(value: unknown): value is TrustedLabRouteExecutor {
26
+ if (typeof value !== "object" || value === null || !HOST_ISSUED_EXECUTORS.has(value as object)) return false;
27
+ const capability = value as TrustedLabRouteExecutor;
28
+ return capability.enforcedBoundaries.length === REQUIRED_LAB_SANDBOX_BOUNDARIES.length
29
+ && REQUIRED_LAB_SANDBOX_BOUNDARIES.every((boundary, index) => capability.enforcedBoundaries[index] === boundary);
30
+ }
@@ -0,0 +1,56 @@
1
+ import { PinnedHttpError, pinnedHttpGet, pinnedHttpPost } from "./pinned-http";
2
+ import { TransportError } from "../lab/live/transport";
3
+ import type { LabCredentialLeaseV1, LabPinnedSender } from "../lab/live/types";
4
+
5
+ /** Only response metadata required by current live assertions crosses into Lab. */
6
+ const LAB_RESPONSE_HEADER_ALLOWLIST = ["content-type"] as const;
7
+
8
+ /**
9
+ * Trusted credential/transport owner. Secret headers exist only in this non-Lab module and are
10
+ * consumed directly by the pinned HTTP primitive; they are never returned to Lab code.
11
+ */
12
+ export function createLabAuthorizedPinnedSender(
13
+ authorize: (lease: LabCredentialLeaseV1) => Promise<HeadersInit> | HeadersInit,
14
+ ): LabPinnedSender {
15
+ return async (lease, destination, pinned, request, signal, limits) => {
16
+ const headers = await authorize(lease);
17
+ const url = `${destination.scheme}://${destination.host}:${destination.port}${destination.basePath}${request.path}`;
18
+ const options = {
19
+ headers,
20
+ maxBytes: limits.maxOutputBytes,
21
+ connectTimeoutMs: limits.connectTimeoutMs,
22
+ firstByteTimeoutMs: limits.firstByteTimeoutMs,
23
+ inactivityTimeoutMs: limits.inactivityTimeoutMs,
24
+ rejectUnauthorized: true,
25
+ context: "Lab provider response",
26
+ };
27
+ let response: Response;
28
+ let body: string;
29
+ try {
30
+ response = request.method === "POST"
31
+ ? await pinnedHttpPost(url, pinned, request.body ?? "", signal, options)
32
+ : await pinnedHttpGet(url, pinned, signal, options);
33
+ body = await response.text();
34
+ } catch (error) {
35
+ if (error instanceof PinnedHttpError) {
36
+ switch (error.code) {
37
+ case "connect_timeout":
38
+ throw new TransportError("connect_timeout", "pinned provider connection timed out");
39
+ case "first_byte_timeout":
40
+ throw new TransportError("first_byte_timeout", "pinned provider first byte timed out");
41
+ case "inactivity_timeout":
42
+ throw new TransportError("inactivity_timeout", "pinned provider response stalled");
43
+ case "output_byte_limit":
44
+ throw new TransportError("output_byte_limit", "pinned provider response exceeded byte budget");
45
+ }
46
+ }
47
+ throw error;
48
+ }
49
+ const responseHeaders: Record<string, string> = {};
50
+ for (const headerName of LAB_RESPONSE_HEADER_ALLOWLIST) {
51
+ const value = response.headers.get(headerName);
52
+ if (value !== null) responseHeaders[headerName] = value;
53
+ }
54
+ return { status: response.status, headers: responseHeaders, body };
55
+ };
56
+ }