@bitkyc08/opencodex 2.59.0 → 2.61.0-preview.20260922

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 (374) hide show
  1. package/AGENTS_INSTALL.md +64 -0
  2. package/README.md +28 -1
  3. package/bin/ocx.mjs +382 -209
  4. package/gui/dist/assets/App-E64Rzjap.js +50 -0
  5. package/gui/dist/assets/Tray-_nfzD8k4.js +1 -0
  6. package/gui/dist/assets/index-DpdfZWMK.js +86 -0
  7. package/gui/dist/assets/index-_bpvxJu0.css +1 -0
  8. package/gui/dist/assets/usage-companion-chart-DtoK7T6h.js +1 -0
  9. package/gui/dist/favicon.png +0 -0
  10. package/gui/dist/index.html +2 -2
  11. package/gui/dist/provider-icons/stepfun-color.svg +1 -0
  12. package/package.json +8 -1
  13. package/src/adapters/anthropic-image-codec.ts +16 -2
  14. package/src/adapters/anthropic-image-normalize.ts +49 -2
  15. package/src/adapters/anthropic.ts +20 -1
  16. package/src/adapters/base.ts +23 -0
  17. package/src/adapters/coding-agent/protocol.ts +36 -6
  18. package/src/adapters/coding-agent/turn.ts +32 -4
  19. package/src/adapters/command-code.ts +52 -4
  20. package/src/adapters/cursor/catalog.ts +51 -7
  21. package/src/adapters/cursor/checkpoint-store.ts +3 -0
  22. package/src/adapters/cursor/discovery.ts +11 -8
  23. package/src/adapters/cursor/live-transport.ts +26 -9
  24. package/src/adapters/cursor/protobuf-request.ts +6 -3
  25. package/src/adapters/cursor/request-builder.ts +20 -4
  26. package/src/adapters/cursor/transport.ts +19 -0
  27. package/src/adapters/cursor.ts +14 -3
  28. package/src/adapters/declaration-carrier.ts +45 -0
  29. package/src/adapters/devin/cloud-direct/chat.ts +3 -1
  30. package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +42 -5
  31. package/src/adapters/devin.ts +125 -36
  32. package/src/adapters/google-antigravity-replay.ts +1 -1
  33. package/src/adapters/google-antigravity-wire.ts +34 -7
  34. package/src/adapters/google-errors.ts +7 -1
  35. package/src/adapters/google-http.ts +49 -10
  36. package/src/adapters/google-tool-schema.ts +595 -31
  37. package/src/adapters/google-wire-compiler.ts +93 -10
  38. package/src/adapters/google-wire-shape.ts +461 -0
  39. package/src/adapters/google.ts +66 -11
  40. package/src/adapters/image.ts +4 -1
  41. package/src/adapters/input-media-guard.ts +21 -9
  42. package/src/adapters/kiro/usage.ts +3 -2
  43. package/src/adapters/kiro-tool-fallback.ts +1 -1
  44. package/src/adapters/ollama-native.ts +6 -0
  45. package/src/adapters/openai-chat/developer-role.ts +61 -0
  46. package/src/adapters/openai-chat/messages.ts +46 -27
  47. package/src/adapters/openai-chat/parallel-tool-calls.ts +32 -0
  48. package/src/adapters/openai-chat/passthrough.ts +33 -9
  49. package/src/adapters/openai-chat/reasoning-wire.ts +89 -0
  50. package/src/adapters/openai-chat-images.ts +3 -1
  51. package/src/adapters/openai-chat.ts +23 -58
  52. package/src/adapters/openai-responses/image-gen.ts +8 -6
  53. package/src/adapters/openai-responses/passthrough.ts +17 -3
  54. package/src/adapters/openai-responses/reasoning.ts +7 -0
  55. package/src/adapters/opencode-go-additional-tools.ts +12 -2
  56. package/src/adapters/registry.ts +3 -2
  57. package/src/adapters/run-turn-queue.ts +178 -29
  58. package/src/adapters/xai-web-search.ts +16 -1
  59. package/src/bridge/errors.ts +8 -2
  60. package/src/bridge/response-json.ts +9 -1
  61. package/src/bridge/sse.ts +13 -151
  62. package/src/chat/inbound.ts +141 -5
  63. package/src/claude/desktop-3p.ts +7 -1
  64. package/src/claude/desktop-first-party.ts +183 -0
  65. package/src/claude/desktop-gateway-state.ts +41 -0
  66. package/src/claude/inbound-content-options.ts +6 -0
  67. package/src/claude/inbound.ts +32 -6
  68. package/src/claude/intercept/connect-proxy.ts +179 -0
  69. package/src/claude/intercept/listener.ts +122 -0
  70. package/src/claude/intercept/local-ca.ts +298 -0
  71. package/src/claude/intercept/runtime.ts +98 -0
  72. package/src/claude/intercept/settings.ts +189 -0
  73. package/src/cli/access.ts +87 -0
  74. package/src/cli/account-auth.ts +19 -0
  75. package/src/cli/account-extended.ts +4 -4
  76. package/src/cli/capabilities.ts +31 -0
  77. package/src/cli/claude-desktop.ts +206 -16
  78. package/src/cli/codex-shim-autorestore.ts +3 -0
  79. package/src/cli/companion.ts +56 -0
  80. package/src/cli/dispatch.ts +46 -7
  81. package/src/cli/doctor.ts +28 -9
  82. package/src/cli/ensure-desired-integrations.ts +43 -5
  83. package/src/cli/help.ts +7 -9
  84. package/src/cli/hub.ts +3 -2
  85. package/src/cli/index.ts +203 -59
  86. package/src/cli/init.ts +8 -0
  87. package/src/cli/integrations.ts +7 -1
  88. package/src/cli/opencode.ts +2 -2
  89. package/src/cli/provider.ts +13 -1
  90. package/src/cli/registry.ts +41 -2
  91. package/src/cli/resolve.ts +230 -0
  92. package/src/cli/root.ts +24 -1
  93. package/src/cli/start-ownership-publication.ts +56 -0
  94. package/src/cli/status-probes.ts +2 -18
  95. package/src/cli/status.ts +62 -0
  96. package/src/cli/stop-report.ts +143 -0
  97. package/src/cli/uninstall-plan.ts +9 -0
  98. package/src/client/machine-api.ts +2 -2
  99. package/src/client/machine-listener.ts +4 -7
  100. package/src/client/runtime.ts +26 -2
  101. package/src/clients/aside-profiles.ts +4 -0
  102. package/src/clients/config-export/zcode-store.ts +157 -0
  103. package/src/clients/config-export.ts +36 -0
  104. package/src/codex/account-store.ts +65 -0
  105. package/src/codex/app-server-processes.ts +72 -40
  106. package/src/codex/auth-api/account-list.ts +19 -11
  107. package/src/codex/auth-api/login-flow.ts +6 -1
  108. package/src/codex/auth-api/pool-quota-probe.ts +30 -7
  109. package/src/codex/autostart-health.ts +28 -0
  110. package/src/codex/catalog/build-entries.ts +2 -2
  111. package/src/codex/catalog/effort.ts +3 -3
  112. package/src/codex/catalog/gather-capture.ts +21 -2
  113. package/src/codex/catalog/model-hints.ts +29 -28
  114. package/src/codex/catalog/parsing.ts +7 -0
  115. package/src/codex/catalog/provider-models.ts +43 -17
  116. package/src/codex/catalog/retained-sync.ts +24 -28
  117. package/src/codex/catalog/routed-gather.ts +19 -0
  118. package/src/codex/context-compat.ts +5 -2
  119. package/src/codex/convergence.ts +2 -2
  120. package/src/codex/desired-state.ts +4 -1
  121. package/src/codex/history-job.ts +6 -6
  122. package/src/codex/history-provider.ts +31 -166
  123. package/src/codex/history-rollout-read.ts +174 -0
  124. package/src/codex/inject/config-toml.ts +41 -6
  125. package/src/codex/inject/paginated-openai-compat.ts +90 -0
  126. package/src/codex/inject.ts +18 -15
  127. package/src/codex/injected-marker.ts +18 -0
  128. package/src/codex/internal/catalog-writer.ts +33 -1
  129. package/src/codex/main-account.ts +6 -0
  130. package/src/codex/model-cache.ts +99 -6
  131. package/src/codex/model-entitlement-admission.ts +59 -0
  132. package/src/codex/model-entitlements.ts +116 -54
  133. package/src/codex/native-main-admission.ts +83 -0
  134. package/src/codex/observed-model-denials.ts +101 -8
  135. package/src/codex/prompt-text-probe.ts +9 -6
  136. package/src/codex/routing/health-store.ts +39 -0
  137. package/src/codex/routing/selection.ts +37 -1
  138. package/src/codex/routing.ts +12 -42
  139. package/src/codex/shim-templates.ts +29 -3
  140. package/src/codex/shim.ts +1 -1
  141. package/src/codex/subagent-model-fallback.ts +22 -4
  142. package/src/combos/failover.ts +3 -0
  143. package/src/companion/settings.ts +132 -0
  144. package/src/config/admitted-identity.ts +222 -0
  145. package/src/config/atomic-write.ts +117 -5
  146. package/src/config/diagnostics.ts +22 -1
  147. package/src/config/feature-flags.ts +5 -0
  148. package/src/config/load-degrade.ts +52 -7
  149. package/src/config/process-state.ts +1 -1
  150. package/src/config/proxy-env.ts +8 -2
  151. package/src/config/schema/compaction-triggers.ts +11 -0
  152. package/src/config/schema/config-schema.ts +31 -1
  153. package/src/config/schema/leaf-validators.ts +59 -0
  154. package/src/config.ts +3 -3
  155. package/src/generated/compatibility-version.json +604 -280
  156. package/src/grok/reset-coupons.ts +38 -19
  157. package/src/images/loop.ts +6 -1
  158. package/src/integrations/aside-profile-context.ts +37 -3
  159. package/src/integrations/aside-profile-journal.ts +68 -3
  160. package/src/integrations/aside-profiles.ts +128 -3
  161. package/src/integrations/config-io.ts +44 -10
  162. package/src/integrations/merge.ts +120 -13
  163. package/src/integrations/mutation-plan.ts +921 -0
  164. package/src/integrations/registry.ts +38 -0
  165. package/src/integrations/state.ts +78 -45
  166. package/src/integrations/target.ts +208 -0
  167. package/src/integrations/writer.ts +134 -110
  168. package/src/lab/conformance/fixture-provider.ts +5 -0
  169. package/src/lab/live/transport.ts +4 -0
  170. package/src/lab/live/types.ts +5 -0
  171. package/src/lab/subject/behavior-fingerprint.ts +1 -1
  172. package/src/lib/admin-secrets.ts +9 -1
  173. package/src/lib/browser-launch-notice.ts +59 -0
  174. package/src/lib/bun-runtime.ts +6 -2
  175. package/src/lib/debug-log-buffer.ts +6 -1
  176. package/src/lib/debug.ts +63 -0
  177. package/src/lib/errors.ts +79 -0
  178. package/src/lib/http-response-semantics.ts +57 -0
  179. package/src/lib/lab-live-pinned-sender.ts +26 -12
  180. package/src/lib/open-url.ts +51 -7
  181. package/src/lib/package-tree-integrity.ts +2 -1
  182. package/src/lib/package-version.ts +8 -0
  183. package/src/lib/pinned-http.ts +142 -2
  184. package/src/lib/plain-data.ts +103 -0
  185. package/src/lib/process-control.ts +13 -5
  186. package/src/lib/provider-egress.ts +310 -0
  187. package/src/lib/provider-outbound.ts +109 -16
  188. package/src/lib/proxy-env.ts +82 -7
  189. package/src/lib/request-execution-budget.ts +72 -0
  190. package/src/lib/request-failure-attribution.ts +183 -0
  191. package/src/lib/request-failure-model.ts +236 -0
  192. package/src/lib/request-resend-gate.ts +138 -0
  193. package/src/lib/socks5-fetch.ts +136 -26
  194. package/src/lib/spend-ledger-owner.ts +364 -0
  195. package/src/lib/spend-reservation-ledger.ts +218 -27
  196. package/src/lib/standalone.ts +16 -0
  197. package/src/lib/upstream-retry.ts +167 -16
  198. package/src/lib/windows-system-proxy.ts +16 -11
  199. package/src/lib/winsw.ts +2 -2
  200. package/src/oauth/callback-server.ts +4 -3
  201. package/src/oauth/generic-account-failover.ts +1 -0
  202. package/src/oauth/health.ts +12 -1
  203. package/src/oauth/index.ts +27 -108
  204. package/src/oauth/login-cli.ts +80 -29
  205. package/src/oauth/login-flow-state.ts +127 -0
  206. package/src/providers/api-key-resolve.ts +133 -0
  207. package/src/providers/api-key-selection.ts +5 -1
  208. package/src/providers/derive.ts +34 -17
  209. package/src/providers/devin-cli-authmode-migration.ts +14 -10
  210. package/src/providers/key-failover.ts +97 -19
  211. package/src/providers/key-store.ts +34 -110
  212. package/src/providers/model-rename-fields.ts +147 -0
  213. package/src/providers/model-rename-migration.ts +179 -38
  214. package/src/providers/model-rename-startup.ts +7 -5
  215. package/src/providers/openai-virtual-models.ts +42 -2
  216. package/src/providers/quota/antigravity.ts +22 -2
  217. package/src/providers/quota/vendor-probes-key.ts +38 -23
  218. package/src/providers/reasoning-metadata.ts +43 -18
  219. package/src/providers/registry/entries-core.ts +47 -20
  220. package/src/providers/registry/entries-extended.ts +63 -4
  221. package/src/providers/registry/model-ids.ts +168 -0
  222. package/src/providers/registry/model-seeds.ts +56 -10
  223. package/src/providers/registry/types.ts +2 -0
  224. package/src/providers/resolved-model-policy-merge.ts +167 -0
  225. package/src/providers/resolved-model-policy.ts +406 -0
  226. package/src/providers/stale-vision-classification-migration.ts +137 -0
  227. package/src/providers/xai-transport.ts +12 -1
  228. package/src/reasoning-effort.ts +8 -0
  229. package/src/responses/apply-patch-envelope.ts +0 -12
  230. package/src/responses/freeform-wrapper-scan.ts +279 -0
  231. package/src/responses/function-call-compat.ts +38 -1
  232. package/src/responses/inline-document.ts +65 -0
  233. package/src/responses/input-media.ts +42 -8
  234. package/src/responses/legacy-dotted-tool-name-repair.ts +134 -0
  235. package/src/responses/muse-tool-name-alias.ts +19 -0
  236. package/src/responses/parser-content.ts +8 -2
  237. package/src/responses/parser-tools.ts +3 -0
  238. package/src/responses/parser.ts +3 -1
  239. package/src/responses/progressive-freeform-input.ts +130 -0
  240. package/src/responses/reasoning-envelope.ts +30 -0
  241. package/src/responses/schema.ts +3 -0
  242. package/src/responses/state.ts +5 -12
  243. package/src/responses/tool-name-aliases.ts +15 -1
  244. package/src/router.ts +108 -117
  245. package/src/routing/compatibility/behavior.ts +9 -0
  246. package/src/routing/compatibility/subject.ts +16 -1
  247. package/src/server/adapter-resolve.ts +9 -0
  248. package/src/server/admission-model-scope.ts +219 -0
  249. package/src/server/audio-live.ts +9 -3
  250. package/src/server/audio-upstream.ts +18 -0
  251. package/src/server/auth-cors.ts +29 -0
  252. package/src/server/chat-completions.ts +60 -4
  253. package/src/server/chat-native.ts +19 -4
  254. package/src/server/claude-messages.ts +61 -20
  255. package/src/server/effort-row.ts +11 -3
  256. package/src/server/grok-responses-control-frame.ts +160 -1
  257. package/src/server/grok-responses-snapshot-repair.ts +113 -11
  258. package/src/server/gui-freshness.ts +103 -0
  259. package/src/server/gui-static.ts +7 -9
  260. package/src/server/images.ts +59 -6
  261. package/src/server/index/claude-intercept-lifecycle.ts +49 -0
  262. package/src/server/index/serve-options.ts +142 -39
  263. package/src/server/index/spend-ledger-lifecycle.ts +92 -0
  264. package/src/server/index/startup-warnings.ts +24 -0
  265. package/src/server/index/websocket-handler.ts +6 -1
  266. package/src/server/index.ts +34 -38
  267. package/src/server/lifecycle.ts +4 -4
  268. package/src/server/live-call-bindings.ts +6 -0
  269. package/src/server/live.ts +88 -3
  270. package/src/server/management/agent-settings-routes.ts +121 -36
  271. package/src/server/management/aside-profile-routes.ts +266 -7
  272. package/src/server/management/companion-routes.ts +77 -0
  273. package/src/server/management/config-routes.ts +18 -2
  274. package/src/server/management/context.ts +3 -0
  275. package/src/server/management/integration-routes.ts +287 -5
  276. package/src/server/management/logs-usage-routes.ts +19 -0
  277. package/src/server/management/metrics-routes.ts +20 -0
  278. package/src/server/management/model-rows.ts +224 -12
  279. package/src/server/management/native-integration-routes.ts +103 -6
  280. package/src/server/management/oauth-account-routes.ts +45 -7
  281. package/src/server/management/route-registry.ts +20 -0
  282. package/src/server/management/shared.ts +28 -4
  283. package/src/server/management/system-restart.ts +7 -2
  284. package/src/server/management/system-routes.ts +2 -0
  285. package/src/server/management/usage-aggregate-cache.ts +4 -0
  286. package/src/server/management/usage-timeline-routes.ts +44 -0
  287. package/src/server/management-api.ts +10 -9
  288. package/src/server/management-auth.ts +15 -1
  289. package/src/server/proxy-liveness.ts +75 -0
  290. package/src/server/readiness.ts +29 -10
  291. package/src/server/relay-eager.ts +24 -2
  292. package/src/server/relay.ts +138 -12
  293. package/src/server/request-log-failure-attribution.ts +99 -0
  294. package/src/server/request-log.ts +169 -2
  295. package/src/server/request-metrics.ts +298 -0
  296. package/src/server/responses/adapter-continuation.ts +3 -3
  297. package/src/server/responses/adapter-dispatch.ts +11 -6
  298. package/src/server/responses/codex-ws-wire.ts +34 -8
  299. package/src/server/responses/combo-stream-preflight.ts +168 -6
  300. package/src/server/responses/compact.ts +43 -10
  301. package/src/server/responses/compaction-routing.ts +111 -0
  302. package/src/server/responses/core-codex-account.ts +8 -3
  303. package/src/server/responses/core-combo.ts +7 -7
  304. package/src/server/responses/core-normalize.ts +6 -12
  305. package/src/server/responses/core-opaque-recovery.ts +91 -0
  306. package/src/server/responses/core-options.ts +4 -0
  307. package/src/server/responses/encrypted-payload.ts +20 -2
  308. package/src/server/responses/fetch-helpers.ts +124 -8
  309. package/src/server/responses/input-admission.ts +10 -0
  310. package/src/server/responses/passthrough-delivery.ts +45 -15
  311. package/src/server/responses/passthrough-dispatch.ts +215 -44
  312. package/src/server/responses/passthrough-error.ts +27 -8
  313. package/src/server/responses/policy-fallback.ts +5 -13
  314. package/src/server/responses/request-prepare.ts +109 -18
  315. package/src/server/responses/request-send-budget.ts +17 -1
  316. package/src/server/responses/request-sidecar-auth.ts +1 -1
  317. package/src/server/responses/request-transport.ts +26 -6
  318. package/src/server/responses/reset-replay.ts +108 -0
  319. package/src/server/responses/run-turn-execution.ts +25 -3
  320. package/src/server/responses/sidecar-execution.ts +17 -2
  321. package/src/server/responses/ws-upstream.ts +14 -27
  322. package/src/server/responses-custom-tool-repair.ts +27 -54
  323. package/src/server/responses-request-tool-scope.ts +214 -0
  324. package/src/server/responses-undeclared-tool-guard.ts +35 -2
  325. package/src/server/search.ts +25 -1
  326. package/src/server/sse-payload-rewrite.ts +1 -1
  327. package/src/server/usage-ledger-retention.ts +73 -0
  328. package/src/service/cli.ts +48 -2
  329. package/src/service/health.ts +3 -2
  330. package/src/service/install-state-contract.d.mts +27 -0
  331. package/src/service/install-state-contract.mjs +34 -0
  332. package/src/service/launchd.ts +1 -1
  333. package/src/service/orchestration.ts +2 -4
  334. package/src/service/ownership-compatibility.ts +164 -0
  335. package/src/service/ownership-mutation-lease.d.mts +32 -0
  336. package/src/service/ownership-mutation-lease.mjs +211 -0
  337. package/src/service/repair.ts +45 -1
  338. package/src/service/state-lock.ts +269 -0
  339. package/src/service/state-record.d.mts +36 -0
  340. package/src/service/state-record.mjs +138 -0
  341. package/src/service/state.ts +582 -68
  342. package/src/service/windows-taskxml.ts +11 -10
  343. package/src/service.ts +7 -3
  344. package/src/tray/windows-tray.ps1 +156 -4
  345. package/src/types/config.ts +48 -3
  346. package/src/types/provider.ts +91 -0
  347. package/src/types/request.ts +30 -2
  348. package/src/types/tools.ts +33 -0
  349. package/src/types.ts +4 -0
  350. package/src/update/index.ts +207 -63
  351. package/src/update/job.ts +9 -5
  352. package/src/update/ownership-transaction.ts +47 -0
  353. package/src/update/restart-ownership.ts +54 -0
  354. package/src/update/runtime-ownership.d.mts +40 -0
  355. package/src/update/runtime-ownership.mjs +122 -0
  356. package/src/usage/attempt-delivery.ts +198 -0
  357. package/src/usage/cache-diagnostic.ts +305 -0
  358. package/src/usage/failure-fingerprint.ts +118 -0
  359. package/src/usage/failure-projection-cache.ts +174 -0
  360. package/src/usage/failure-projection.ts +174 -0
  361. package/src/usage/ledger-retention.ts +165 -0
  362. package/src/usage/log.ts +126 -79
  363. package/src/usage/request-outcome.ts +150 -0
  364. package/src/usage/retention-contract.ts +28 -0
  365. package/src/usage/summary.ts +2 -2
  366. package/src/usage/telemetry-contract.ts +237 -0
  367. package/src/usage/timeline.ts +236 -0
  368. package/src/vision/eligibility.ts +88 -9
  369. package/src/vision/plan.ts +34 -10
  370. package/src/web-search/alpha-search.ts +21 -1
  371. package/src/web-search/executor.ts +41 -2
  372. package/src/web-search/loop.ts +6 -1
  373. package/gui/dist/assets/index-C5IebErG.js +0 -136
  374. package/gui/dist/assets/index-OESInAjC.css +0 -1
@@ -79,10 +79,11 @@ export function buildWindowsServiceScript(
79
79
  windowsBatchSet("OCX_API_TOKEN_FILE", serviceApiTokenFilePath(), "path"),
80
80
  windowsBatchSet("OCX_SERVICE_LOG", serviceLogPath(), "path"),
81
81
  windowsBatchSet("OCX_BUN", bun, "path"),
82
- windowsBatchSet("OCX_CLI", cli, "path"),
82
+ windowsBatchSet("OCX_CLI", cli ?? undefined, "path"),
83
+ // Standalone executables have no npm package tree; recovery is "replace the executable", so no OCX_PKG_DIR/restore_backup wiring.
83
84
  // Package root for the transactional-update restore path (#1942): cli is
84
85
  // <pkg>\src\cli\index.ts, so the package dir is three levels up.
85
- 'for %%I in ("%OCX_CLI%\\..\\..\\..") do set "OCX_PKG_DIR=%%~fI"',
86
+ cli ? 'for %%I in ("%OCX_CLI%\\..\\..\\..") do set "OCX_PKG_DIR=%%~fI"' : null,
86
87
  'if exist "%OCX_API_TOKEN_FILE%" (',
87
88
  ' set /p OPENCODEX_API_AUTH_TOKEN=<"%OCX_API_TOKEN_FILE%"',
88
89
  ")",
@@ -101,14 +102,14 @@ export function buildWindowsServiceScript(
101
102
  ' >>"%OCX_SERVICE_LOG%" echo [%DATE% %TIME%] installation is incomplete: bundled Bun is missing; reinstall opencodex, then run ocx service repair',
102
103
  " exit /b 3",
103
104
  ")",
104
- 'if not exist "%OCX_CLI%" (',
105
- " call :restore_backup",
106
- ")",
107
- 'if not exist "%OCX_CLI%" (',
108
- ' >>"%OCX_SERVICE_LOG%" echo [%DATE% %TIME%] installation is incomplete: CLI entry is missing; reinstall opencodex, then run ocx service repair',
109
- " exit /b 3",
110
- ")",
111
- `"%OCX_BUN%" "%OCX_CLI%" start --port ${port} >>"%OCX_SERVICE_LOG%" 2>&1`,
105
+ cli ? 'if not exist "%OCX_CLI%" (' : null,
106
+ cli ? " call :restore_backup" : null,
107
+ cli ? ")" : null,
108
+ cli ? 'if not exist "%OCX_CLI%" (' : null,
109
+ cli ? ' >>"%OCX_SERVICE_LOG%" echo [%DATE% %TIME%] installation is incomplete: CLI entry is missing; reinstall opencodex, then run ocx service repair' : null,
110
+ cli ? " exit /b 3" : null,
111
+ cli ? ")" : null,
112
+ cli ? `"%OCX_BUN%" "%OCX_CLI%" start --port ${port} >>"%OCX_SERVICE_LOG%" 2>&1` : `"%OCX_BUN%" start --port ${port} >>"%OCX_SERVICE_LOG%" 2>&1`,
112
113
  "if %ERRORLEVEL% NEQ 0 (",
113
114
  ' >>"%OCX_SERVICE_LOG%" echo [%DATE% %TIME%] child exited with code %ERRORLEVEL%; restarting in 5s',
114
115
  // `timeout` needs console stdin and dies with "Input redirection is not supported"
package/src/service.ts CHANGED
@@ -6,8 +6,12 @@
6
6
  * restore it via the command.
7
7
  */
8
8
 
9
- export type { ServiceBackend, ServiceInstallState, ServiceStateEvidence } from "./service/state";
10
- export { SERVICE_MANAGED_ENV, stableLauncherEntry, serviceLogPath, serviceStatePathsForOpenCodexHome, parseServiceInstallState, inspectServiceStateEvidence, currentServiceHomes, serviceHomeMatches, readServiceBackend, serviceReinstallArgs, serviceInstallArgs } from "./service/state";
9
+ export type { ServiceBackend, ServiceInstallState, ServiceStateEvidence, ServiceStateResolution, ServiceOwner, ServiceOwnership, ServiceOwnershipSubject, ServiceOwnershipResolution, ServiceStateSwapDeps, RecordServiceOwnerRequest, RecordServiceOwnerDeps, ReleaseServiceOwnerDeps, RemoveServiceStateDeps } from "./service/state";
10
+ export { SERVICE_MANAGED_ENV, SERVICE_OWNERSHIP_PROTOCOL_VERSION, SERVICE_OWNERSHIP_MINIMUM_CLI_VERSION, stableLauncherEntry, serviceLogPath, serviceStatePaths, serviceStatePathsForOpenCodexHome, parseServiceInstallState, parseServiceOwnership, inspectServiceStateEvidence, resolveServiceState, currentServiceHomes, serviceHomeMatches, readServiceBackend, serviceReinstallArgs, serviceInstallArgs, ServiceStateConflictError, ServiceOwnershipSubjectMismatchError, ServiceOwnershipSubjectUnknownError, ServiceTakeoverCompatibilityChangedError, swapServiceInstallState, removeServiceInstallStateRecords, serviceOwnership, resolveServiceOwnership, sameServiceOwnershipSubject, desktopOwnsService, ownershipGrantedTo, recordServiceOwner, releaseServiceOwner } from "./service/state";
11
+ export type { OwnershipMutationLeaseOptions, OwnershipMutationLease } from "./service/ownership-mutation-lease.mjs";
12
+ export { acquireOwnershipMutationLease, withOwnershipMutationLease } from "./service/ownership-mutation-lease.mjs";
13
+ export type { ManagingCliRole, ManagingCliObservation, RegisteredManagingCliInvocation, ServiceTakeoverCompatibilityInput, ServiceTakeoverCompatibility } from "./service/ownership-compatibility";
14
+ export { registeredManagingCliInvocation, assessServiceTakeoverCompatibility, sameServiceTakeoverCompatibility } from "./service/ownership-compatibility";
11
15
  export type { ServiceApiTokenOrigin, ProvisionedServiceApiToken } from "./service/guards";
12
16
  export { ServiceOwnershipError, isServiceOwnershipError, serviceEnvironmentOwnedHere, assertServiceEnvironmentMatchesInstall, serviceRetryCommand, assertNotAdminToken, assertServiceAuthEnvironment, writeServiceApiTokenFile, assertLiveServiceManagerAllowed } from "./service/guards";
13
17
  export { resolveServiceListenPort, installedServiceListenPort, SERVICE_INSTALL_HEALTH_MS, SERVICE_INSTALL_HEALTH_WINDOWS_MS, serviceInstallHealthMs, confirmServiceServing, reportServiceServing, resolvedProxyEnv } from "./service/health";
@@ -21,7 +25,7 @@ export { buildWindowsServiceScript, buildWindowsSchtasksCreateArgs, buildWindows
21
25
  export type { WindowsSchedulerRegistrationStageDeps, FreshWindowsSchedulerRegistrationDeps, RemoveNativeWindowsServiceDeps } from "./service/windows-ops";
22
26
  export { windowsListenPort, winswListenPort, writeServiceDefinitionFile, definitionCarriesCredential, stageWindowsSchedulerRegistrationXml, stageElevatedSchedulerRegistration, describeElevatedRegistrationFailure, registerFreshWindowsSchedulerTask, removeNativeWindowsServiceForScheduler, assertWindowsNativeServiceAccountSupported, isWindowsSchedulerEndBenign, stopWindows, stopWindowsChecked, classifyWindowsServiceStop } from "./service/windows-ops";
23
27
  export type { ServiceRepairVerb, RepairServiceDeps } from "./service/repair";
24
- export { repairService } from "./service/repair";
28
+ export { repairService, foreignServiceOwnerRefusal, unknownServiceOwnerRefusal } from "./service/repair";
25
29
  export type { ServiceInstallPreparationDeps, FreshWindowsSchedulerInstallDeps, ServiceStopOutcome, ServiceUninstallOutcome } from "./service/orchestration";
26
30
  export { proxyStillLiveAfterStop, prepareServiceInstall, installServiceSafely, installFreshWindowsSchedulerSafely, installedServiceRespawnRisk, stopServiceIfInstalledDetailed, setUninstallServiceHooksForTests, uninstallServiceDetailed, uninstallServiceIfInstalled, isServiceInstalled, isServiceViable } from "./service/orchestration";
27
31
  export type { ServiceDiagnostic, WindowsTaskDiagnosticIdentityDeps, WindowsServiceDiagnosticInputs, LaunchdServiceDiagnosticInputs } from "./service/diagnostics";
@@ -5,7 +5,7 @@ param(
5
5
  [Parameter(Mandatory = $true)][string]$OpenCodexHome,
6
6
  # Provenance of $BunPath, chosen when the tray entry was built. Optional so an
7
7
  # already-installed launcher command from an older version still starts.
8
- [ValidateSet("", "override", "bundled", "process")][string]$BunRuntimeSource = "",
8
+ [ValidateSet("", "override", "bundled", "process", "standalone")][string]$BunRuntimeSource = "",
9
9
  [ValidateSet("Run", "Stop")][string]$Mode = "Run",
10
10
  [int]$HostPid = 0
11
11
  )
@@ -116,6 +116,88 @@ function Start-OcxCommand([string[]]$CommandArgs, [switch]$TrackExit) {
116
116
  }
117
117
  }
118
118
 
119
+ function Parse-StartupHealthText([string]$Text) {
120
+ foreach ($line in ($Text -split "\r?\n")) {
121
+ $trimmed = $line.Trim()
122
+ if (-not $trimmed) { continue }
123
+ try {
124
+ $parsed = $trimmed | ConvertFrom-Json
125
+ if ($parsed.status -in @("native", "protected", "at-risk") -and ($parsed.rebootSafe -is [bool])) {
126
+ return $parsed
127
+ }
128
+ } catch {
129
+ # A non-JSON line from the CLI (for example a diagnostic banner) is not a
130
+ # startup-health payload; keep scanning for the actual JSON result.
131
+ continue
132
+ }
133
+ }
134
+ return $null
135
+ }
136
+
137
+ function Start-StartupHealthProbe {
138
+ # Startup safety is machine-local diagnostic state, so the tray asks the CLI to
139
+ # collect it directly (ocx __startup-health) instead of poking the management API,
140
+ # which is admin-token gated. The CLI collects the diagnostic straight from the OS,
141
+ # so it works without the proxy answering and without any credential.
142
+ #
143
+ # The child writes into redirected pipes that the tick reads asynchronously: only
144
+ # the completed Task<string> is ever touched, so a slow or hung diagnostic can never
145
+ # block the Windows Forms UI thread, and its lifetime is bounded by a kill in the
146
+ # tick that lets the next refresh replace it instead of stacking another process.
147
+ try {
148
+ $psi = New-Object System.Diagnostics.ProcessStartInfo
149
+ $psi.FileName = $BunPath
150
+ $psi.Arguments = ((@($CliPath, "__startup-health") | ForEach-Object { ConvertTo-NativeArgument $_ }) -join " ")
151
+ $psi.UseShellExecute = $false
152
+ $psi.CreateNoWindow = $true
153
+ $psi.WindowStyle = [System.Diagnostics.ProcessWindowStyle]::Hidden
154
+ $psi.RedirectStandardOutput = $true
155
+ $psi.RedirectStandardError = $true
156
+ $psi.StandardOutputEncoding = [System.Text.Encoding]::UTF8
157
+ $psi.StandardErrorEncoding = [System.Text.Encoding]::UTF8
158
+ $psi.EnvironmentVariables["CODEX_HOME"] = $CodexHome
159
+ $psi.EnvironmentVariables["OPENCODEX_HOME"] = $OpenCodexHome
160
+ if ($BunRuntimeSource) {
161
+ $psi.EnvironmentVariables["OCX_BUN_RUNTIME_SOURCE"] = $BunRuntimeSource
162
+ $psi.EnvironmentVariables["OCX_BUN_RUNTIME_PATH"] = $BunPath
163
+ }
164
+ $process = [System.Diagnostics.Process]::Start($psi)
165
+ $script:startupProbeProcess = $process
166
+ # Drain both pipes asynchronously from the start so neither can fill and stall
167
+ # the child while the tick is only watching the output task's completion.
168
+ $script:startupProbeOutputTask = $process.StandardOutput.ReadToEndAsync()
169
+ $script:startupProbeErrorTask = $process.StandardError.ReadToEndAsync()
170
+ $script:startupProbeStarted = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
171
+ } catch {
172
+ Write-ActionLog "startup-health probe launch failed: $($_.Exception.GetType().Name)"
173
+ if ($null -ne $script:startupProbeProcess) {
174
+ # Process.Start succeeded but the async pipe setup failed: Dispose alone
175
+ # would leave the Bun child running, so terminate it before cleanup.
176
+ try {
177
+ $script:startupProbeProcess.Kill()
178
+ [void]$script:startupProbeProcess.WaitForExit(3000)
179
+ } catch {
180
+ Write-ActionLog "startup-health probe launch cleanup failed: $($_.Exception.GetType().Name)"
181
+ }
182
+ }
183
+ Complete-StartupHealthProbe
184
+ }
185
+ }
186
+
187
+ function Complete-StartupHealthProbe {
188
+ $process = $script:startupProbeProcess
189
+ $script:startupProbeProcess = $null
190
+ $script:startupProbeOutputTask = $null
191
+ $script:startupProbeErrorTask = $null
192
+ if ($null -ne $process) {
193
+ try {
194
+ $process.Dispose()
195
+ } catch {
196
+ Write-ActionLog "startup-health probe dispose failed: $($_.Exception.GetType().Name)"
197
+ }
198
+ }
199
+ }
200
+
119
201
  function Read-ListenTarget {
120
202
  foreach ($path in @((Join-Path $OpenCodexHome "runtime-port.json"), (Join-Path $OpenCodexHome "config.json"))) {
121
203
  try {
@@ -172,6 +254,15 @@ $exitItem = $menu.Items.Add("Exit Tray")
172
254
  $script:online = $false
173
255
  $script:port = 10100
174
256
  $script:proxyPid = $null
257
+ $script:wasOnline = $false
258
+ $script:startupHealth = $null
259
+ $script:startupHealthCheckedAt = 0L
260
+ $script:startupRefreshMs = 20000L
261
+ $script:startupProbeProcess = $null
262
+ $script:startupProbeOutputTask = $null
263
+ $script:startupProbeErrorTask = $null
264
+ $script:startupProbeStarted = 0L
265
+ $script:startupProbeTimeoutMs = 30000L
175
266
  $script:pendingAction = $null
176
267
  $script:pendingStarted = 0L
177
268
  $script:pendingDeadline = 0L
@@ -228,18 +319,66 @@ function Update-TrayState {
228
319
  $pidMatches = $null -eq $target.pid -or [int]$target.pid -eq [int]$health.pid
229
320
  $script:online = $null -ne $health -and $health.status -eq "ok" -and $health.service -eq "opencodex" -and [int]$health.port -eq $script:port -and $pidMatches
230
321
  $script:proxyPid = if ($script:online) { [int]$health.pid } else { $null }
322
+ $cameOnline = $script:online -and -not $script:wasOnline
323
+ $script:wasOnline = $script:online
324
+ $now = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
325
+ if ($null -ne $script:startupProbeProcess) {
326
+ $probeExited = $false
327
+ try {
328
+ $probeExited = $script:startupProbeProcess.HasExited
329
+ } catch {
330
+ $probeExited = $true
331
+ }
332
+ $probeTimedOut = ($now - $script:startupProbeStarted) -gt $script:startupProbeTimeoutMs
333
+ if ($probeExited -and $null -ne $script:startupProbeOutputTask -and $script:startupProbeOutputTask.IsCompleted) {
334
+ $probeStartup = $null
335
+ try {
336
+ $probeStartup = Parse-StartupHealthText $script:startupProbeOutputTask.Result
337
+ } catch {
338
+ Write-ActionLog "startup-health probe read failed: $($_.Exception.GetType().Name)"
339
+ }
340
+ $script:startupHealth = $probeStartup
341
+ Complete-StartupHealthProbe
342
+ $script:startupHealthCheckedAt = $now
343
+ } elseif ($probeTimedOut) {
344
+ # A hung diagnostic must not survive its refresh slot: terminate it so the
345
+ # next refresh cannot stack another orphaned Bun process behind it.
346
+ Write-ActionLog "startup-health probe timed out; terminating it"
347
+ try {
348
+ $script:startupProbeProcess.Kill()
349
+ [void]$script:startupProbeProcess.WaitForExit(3000)
350
+ } catch {
351
+ Write-ActionLog "startup-health probe kill failed: $($_.Exception.GetType().Name)"
352
+ }
353
+ $script:startupHealth = $null
354
+ Complete-StartupHealthProbe
355
+ $script:startupHealthCheckedAt = $now
356
+ }
357
+ }
358
+ $refreshDue =
359
+ $script:startupHealthCheckedAt -eq 0 -or
360
+ ($now - $script:startupHealthCheckedAt -gt $script:startupRefreshMs)
361
+ if ($script:online -and ($cameOnline -or $refreshDue) -and $null -eq $script:startupProbeProcess) {
362
+ # Record the attempt so launch failures and invalid results remain throttled.
363
+ $script:startupHealthCheckedAt = $now
364
+ Start-StartupHealthProbe
365
+ }
231
366
  if ($script:online) {
232
367
  $statusItem.Text = "Proxy: Online (port $($script:port))"
233
368
  $notify.Text = "opencodex: Online"
234
369
  $startItem.Enabled = $false
235
370
  $stopItem.Enabled = $true
236
371
  $restartItem.Enabled = $true
237
- try {
238
- $startup = Read-JsonUrl "$origin/api/startup-health"
372
+ # The management API is admin-token gated, so the tray must not poke
373
+ # /api/startup-health for the icon. The probe lifecycle maintenance and the
374
+ # refresh gate above run on every tick (even while offline) so a hung
375
+ # diagnostic is cleaned up outside the online-only UI branch.
376
+ $startup = $script:startupHealth
377
+ if ($null -ne $startup) {
239
378
  $label = if ($startup.status -eq "at-risk") { "At risk" } elseif ($startup.status -eq "protected") { "Protected" } else { "Native routing" }
240
379
  $safetyItem.Text = "Restart safety: $label"
241
380
  $notify.Icon = if ($startup.status -eq "at-risk") { $warningIcon } else { $onlineIcon }
242
- } catch {
381
+ } else {
243
382
  $safetyItem.Text = "Restart safety: unavailable"
244
383
  $notify.Icon = $warningIcon
245
384
  }
@@ -360,6 +499,19 @@ try {
360
499
  if ($null -ne $script:pendingProcess) {
361
500
  try { $script:pendingProcess.Dispose() } catch { $null = $_ }
362
501
  }
502
+ if ($null -ne $script:startupProbeProcess) {
503
+ # A probe still running when the tray shuts down would outlive it: Dispose()
504
+ # releases the handle but does not terminate the child, so kill the active
505
+ # probe, wait briefly for exit, then release the probe state.
506
+ try {
507
+ Write-ActionLog "terminating active startup-health probe on tray shutdown"
508
+ $script:startupProbeProcess.Kill()
509
+ [void]$script:startupProbeProcess.WaitForExit(3000)
510
+ } catch {
511
+ Write-ActionLog "startup-health probe shutdown kill failed: $($_.Exception.GetType().Name)"
512
+ }
513
+ Complete-StartupHealthProbe
514
+ }
363
515
  $notify.Dispose()
364
516
  foreach ($icon in $script:ownedIcons) { $icon.Dispose() }
365
517
  $menu.Dispose()
@@ -137,6 +137,13 @@ export interface OcxClaudeCodeConfig {
137
137
  autoContext?: boolean;
138
138
  /** Compact-window tokens for auto-context. Default 829_800 (AUTO_COMPACT_WINDOW_DEFAULT). */
139
139
  autoCompactWindow?: number;
140
+ /**
141
+ * Local CONNECT proxy + TLS listener that intercepts Claude Code's own `api.anthropic.com`
142
+ * traffic without any `ANTHROPIC_BASE_URL` rewrite (src/claude/intercept). Claude Code reaches
143
+ * it via `HTTPS_PROXY`/`NODE_EXTRA_CA_CERTS` in its settings env. Default: enabled on a
144
+ * hub; the proxy port defaults to the public port + 100.
145
+ */
146
+ intercept?: { enabled?: boolean; port?: number };
140
147
  /**
141
148
  * Bundled-skill content elision for ROUTED (non-Anthropic) models (devlog 260712
142
149
  * 060): Skill-tool results whose skill name matches an entry here are replaced
@@ -166,6 +173,14 @@ export interface OcxClaudeCodeConfig {
166
173
  visionSidecar?: { backend?: "openai" | "anthropic" | "routed"; model?: string };
167
174
  /** Persisted Claude Desktop four-family routing profile. */
168
175
  desktopProfile?: OcxClaudeDesktopProfile;
176
+ /**
177
+ * How Claude Desktop reaches opencodex (src/claude/desktop-first-party.ts).
178
+ * `first-party` (default) leaves the app on its normal claude.ai login and redirects only the
179
+ * Code tab's Claude Code process through the intercept pair via settings.json env.
180
+ * `gateway` installs the third-party deployment profile (desktop-3p) for the whole app.
181
+ * Unset on an install that already applied a gateway profile resolves to `gateway`.
182
+ */
183
+ desktopMode?: "first-party" | "gateway";
169
184
  /** Auto-reconcile Desktop 3P config when provider catalog changes. Default: enabled. */
170
185
  desktopAutoApply?: boolean;
171
186
  /**
@@ -253,6 +268,21 @@ export interface OcxApiKeyEntry {
253
268
  key: string;
254
269
  createdAt: string;
255
270
  pendingRotation?: OcxPendingApiKeyRotation;
271
+ /**
272
+ * Resolved provider names this key may reach. Absent or empty means every
273
+ * provider, which is what every existing key has, so adding the field
274
+ * changes nothing until an operator sets one.
275
+ */
276
+ allowedProviders?: string[];
277
+ /**
278
+ * Resolved destinations this key may reach, as a bare model id or a
279
+ * `provider/model` pair. Absent or empty means every model.
280
+ *
281
+ * These name destinations, not the selectors a client sends: they are
282
+ * checked after alias, combo, fallback and compaction resolution, because
283
+ * that is the only point at which the model about to be billed is known.
284
+ */
285
+ allowedModels?: string[];
256
286
  }
257
287
 
258
288
  export interface OcxPendingApiKeyRotation {
@@ -403,6 +433,8 @@ export interface OcxConfig {
403
433
  client?: OcxClientConnectionConfig;
404
434
  /** Operator-facing redaction policy for management and CLI projections. */
405
435
  privacy?: OcxPrivacyConfig;
436
+ /** Opt-in process-local aggregate request metrics on the authenticated management plane. */
437
+ metricsExport?: { enabled?: boolean };
406
438
  /** Opt in to one identical-turn retry when a Responses completion has no text or tool call. */
407
439
  emptyCompletionRetry?: boolean;
408
440
  /** Suppress allowlisted client-facing Codex transport hints; provider enforcement is unchanged. */
@@ -616,6 +648,12 @@ export interface OcxConfig {
616
648
  subagentEffortCap?: string;
617
649
  /** Global model effort overrides, after provider model/wide pins; none means omission. */
618
650
  modelPinnedEfforts?: Record<string, string>;
651
+ compactionRouting?: {
652
+ model: string;
653
+ reasoningEffort?: string;
654
+ /** Compaction triggers this override covers; omission means `["manual"]`. */
655
+ triggers?: ("manual" | "auto")[];
656
+ };
619
657
  /**
620
658
  * Models hidden from Codex discovery without blocking direct proxy calls. Routed provider ids
621
659
  * are excluded from the catalog + /v1/models entirely. Account-qualified native ids hide only
@@ -753,9 +791,9 @@ export interface OcxConfig {
753
791
  * into ALL_PROXY, clear inherited HTTP(S)_PROXY, and use OpenCodex's SOCKS5 transport.
754
792
  * Loopback stays in NO_PROXY.
755
793
  * The literal `"auto"` reads the Windows WinINET static proxy (`ProxyEnable`/`ProxyServer`)
756
- * once at process start; on other platforms, or when the system proxy is off, SOCKS-only,
757
- * or unreadable, it degrades to direct egress with one log line (#1525). PAC/WPAD and live
758
- * changes are not followed.
794
+ * once at process start, preserving separate HTTP and HTTPS entries; on other platforms, or
795
+ * when the system proxy is off, SOCKS-only, or unreadable, it degrades to direct egress with
796
+ * one log line (#1525). PAC/WPAD and live changes are not followed.
759
797
  */
760
798
  proxy?: string;
761
799
  /**
@@ -786,6 +824,13 @@ export interface OcxConfig {
786
824
  * See `src/storage/policy.ts`.
787
825
  */
788
826
  storageCleanupPolicy?: StorageCleanupPolicy;
827
+ /**
828
+ * Opt-in ceiling in bytes for `usage.jsonl`. Absent means the ledger grows without limit,
829
+ * which stays the default: history an operator did not ask to delete is not deleted. Values
830
+ * below the documented floor are treated as unset rather than enforced, because a ceiling
831
+ * smaller than a row cannot be met without emptying the file.
832
+ */
833
+ usageLedgerMaxBytes?: number;
789
834
  /** Generated API keys for external access to the proxy's /v1/responses endpoint. */
790
835
  apiKeys?: OcxApiKeyEntry[];
791
836
  /** Auto-start/sync the proxy from the Codex shim before launching Codex. Default true. */
@@ -67,6 +67,31 @@ export interface TransientRetryPolicy {
67
67
  attempts?: number;
68
68
  }
69
69
 
70
+ /**
71
+ * Opt-in replacement of a native Responses send whose upstream connection closed while the
72
+ * caller had observed nothing (`providers.<name>.retryOnReset`).
73
+ *
74
+ * Covers both ambiguous stages the proxy can be in: no response head at all, and a head whose
75
+ * SSE body carried only control events. Disabled unless the object is present; a bare `{}`
76
+ * opts in with defaults. Only a request the proxy can judge self-contained is ever replaced;
77
+ * see `src/server/responses/reset-replay.ts`. The replacement inference may still be billed if
78
+ * the origin had already started the first one, which is what makes this opt-in rather than
79
+ * default.
80
+ */
81
+ export interface ResetReplayPolicy {
82
+ /** Master switch. Presence of the object also enables the policy (default true). */
83
+ enabled?: boolean;
84
+ /**
85
+ * Replacement sends one LOGICAL request may make, across every leg and every combo child
86
+ * (1..2, default 1).
87
+ *
88
+ * Not a per-leg retry count and not a send budget. A request that resets before the head and
89
+ * again after it draws on this one number, and each replacement still has to fit inside the
90
+ * send allowance the leg already had.
91
+ */
92
+ replacements?: number;
93
+ }
94
+
70
95
  /**
71
96
  * Same-target 429 wait-and-retry policy (`providers.<name>.retryOn429`). When present and not
72
97
  * explicitly disabled, the proxy waits and replays the identical request on the same key before
@@ -356,6 +381,22 @@ export interface OcxProviderConfig {
356
381
  * `ocxr1` envelopes are still stripped because no upstream can decrypt them.
357
382
  */
358
383
  preserveResponsesReasoningContent?: boolean;
384
+ /**
385
+ * Treat this provider's `modelReasoningEfforts` as authoritative at the wire, not only in the
386
+ * catalog. Adapters that ship their own per-model effort table (currently `command-code`)
387
+ * otherwise let that table win for models it knows, so a widened row is advertised in the
388
+ * picker and then stripped on the way out. Opt-in because presets are SEEDED with the shipped
389
+ * table: without a declared flag there is no way to tell an operator's row from a copy an
390
+ * older release persisted. A rung the upstream then refuses is returned as that error rather
391
+ * than silently retried without the effort, since the operator asked for it.
392
+ */
393
+ modelReasoningEffortsAuthoritative?: boolean;
394
+ /**
395
+ * Drop replayed Responses `reasoning` items from input history before forwarding.
396
+ * Some OpenAI-compatible Responses upstreams accept tool-call replay but reject
397
+ * reasoning output items when they are sent back on a continuation.
398
+ */
399
+ dropResponsesReasoningItems?: boolean;
359
400
  /**
360
401
  * Explicit opt-in for a relay that genuinely fronts OpenAI and can decode native
361
402
  * compaction blobs. Absent or false degrades foreign blobs to an opaque note.
@@ -372,6 +413,35 @@ export interface OcxProviderConfig {
372
413
  * link-local, or unique-local upstreams. Metadata endpoints remain blocked.
373
414
  */
374
415
  allowPrivateNetwork?: boolean;
416
+ /**
417
+ * Outbound egress for THIS provider, overriding the process-wide `proxy` decision.
418
+ *
419
+ * The global `proxy` is one value for every upstream, so it cannot express the split
420
+ * operators actually need: reach one gateway through a regional proxy while another stays
421
+ * direct on the local network (#2894). Accepted values:
422
+ *
423
+ * - absent — inherit the global proxy decision. Unchanged behaviour.
424
+ * - `"direct"` or `null` — never use the global proxy for this provider.
425
+ * - `"http://…"` / `"https://…"` — this provider's own HTTP(S) proxy.
426
+ * - `"socks5://…"` / `"socks5h://…"` — this provider's own SOCKS5 proxy.
427
+ *
428
+ * An empty string is rejected rather than read as DIRECT: a cleared dashboard field must not
429
+ * silently switch a provider from inheriting the global proxy to refusing it. A malformed
430
+ * value is rejected at configuration time and again at request time; it never degrades to
431
+ * either neighbour, because both degradations look like success at the call site.
432
+ *
433
+ * Not every transport can carry this. `structure/transports/inventory.md` records which
434
+ * request paths honour it and which still follow the process-wide value only.
435
+ */
436
+ proxy?: string | null;
437
+ /**
438
+ * Destinations this provider reaches without a proxy, in `NO_PROXY` syntax.
439
+ *
440
+ * Applied to whichever route `proxy` resolved to, so it carves an exemption out of this
441
+ * provider's own proxy AND out of an inherited global one. That second case is how a
442
+ * provider exempts a single host without owning a proxy of its own.
443
+ */
444
+ noProxy?: string | string[];
375
445
  /**
376
446
  * Pin the HTTP version used for upstream provider requests. Bun's fetch negotiates
377
447
  * HTTP/2 via TLS ALPN by default; some Cloudflare-fronted SSE endpoints hang on
@@ -800,6 +870,19 @@ export interface OcxProviderConfig {
800
870
  * incomplete JSON, missing arguments, and empty streams remain truncation errors.
801
871
  */
802
872
  openaiChatEofTolerance?: boolean;
873
+ /**
874
+ * Opt-in: fold a `developer` message into a `system` message instead of forwarding the role.
875
+ *
876
+ * `developer` is part of the Chat Completions message role set, so forwarding it is the
877
+ * default. The role used to be decided by testing the base URL host against
878
+ * `api.openai.com`, which assumed every OpenAI-compatible gateway rejects a standard role
879
+ * until proven otherwise — including gateways that proxy OpenAI itself — and quietly gave the
880
+ * instruction `system` precedence instead (#5213). This flag exists for a destination that
881
+ * genuinely rejects the role, so the conversion is a recorded decision about that destination
882
+ * rather than an inference from its hostname. Position is unaffected either way: the message
883
+ * keeps its slot in the conversation.
884
+ */
885
+ foldDeveloperRoleToSystem?: boolean;
803
886
  /**
804
887
  * Opt-in: forward `prompt_cache_key` to the upstream `/chat/completions` body.
805
888
  * OpenAI-specific extension; strict backends (Groq, Cerebras, etc.) reject unknown
@@ -856,6 +939,12 @@ export interface OcxProviderConfig {
856
939
  * with defaults. Key-auth `openai-chat` only.
857
940
  */
858
941
  transientRetryOn5xx?: TransientRetryPolicy;
942
+ /**
943
+ * Opt-in replacement of a native Responses send that died while the caller had observed
944
+ * nothing (`providers.<name>.retryOnReset`). Disabled unless present; a bare `{}` opts in
945
+ * with defaults. Native Responses sends only, and only for self-contained requests.
946
+ */
947
+ retryOnReset?: ResetReplayPolicy;
859
948
  /**
860
949
  * Model ids whose OpenAI-compatible chat endpoint accepts `reasoning_split: true` and returns
861
950
  * thinking separately in `reasoning_content` / `reasoning_details` instead of visible content.
@@ -901,6 +990,8 @@ export interface OcxProviderConfig {
901
990
  * "cloud-code-assist" = Google Antigravity (Cloud Code Assist) OAuth + CCA envelope.
902
991
  */
903
992
  googleMode?: "ai-studio" | "vertex" | "cloud-code-assist";
993
+ /** Google tool-schema compatibility policy. Omitted preserves compatible report-only behavior. */
994
+ googleToolSchemaPolicy?: "compatible" | "reject-lossy";
904
995
  /** Vertex AI GCP project id (or GOOGLE_CLOUD_PROJECT / GCLOUD_PROJECT env). */
905
996
  project?: string;
906
997
  /** Vertex AI location, e.g. "us-central1" or "global" (or GOOGLE_CLOUD_LOCATION env). */
@@ -126,6 +126,8 @@ export interface OcxParsedRequest {
126
126
  * (see src/responses/compaction.ts).
127
127
  */
128
128
  _compactionRequest?: boolean;
129
+ /** Manual compaction moved to another provider: summarize portably even on a canonical ChatGPT target. */
130
+ _portableCompaction?: boolean;
129
131
  /**
130
132
  * True when the current request newly introduced a stored compaction summary/marker. Historical
131
133
  * markers restored by previous_response_id expansion were already acknowledged and do not reset
@@ -208,8 +210,34 @@ export interface OcxVideoContent {
208
210
  videoUrl: string;
209
211
  }
210
212
 
211
- /** A user/developer message content part: text or native media. */
212
- export type OcxContentPart = OcxTextContent | OcxImageContent | OcxVideoContent;
213
+ /**
214
+ * An attached document carried as bytes rather than as a description of itself.
215
+ *
216
+ * Both inbound parsers used to reduce an attachment to a title before any adapter ran, so no
217
+ * adapter could forward one even to a target that has a representation for it, and the caller
218
+ * could not tell "the model read the document" from "the model was told a document existed"
219
+ * (#5212).
220
+ *
221
+ * `text` is that marker, derived once from what the part knows and kept on the part itself.
222
+ * Every text-only consumer in the tree reaches a `.text` fallback for a part it does not
223
+ * recognize, so carrying it here means a wire with no document representation still states the
224
+ * attachment instead of emitting `undefined` or a mislabelled `[video]`. Only the wires that
225
+ * have a counterpart read `data`.
226
+ */
227
+ export interface OcxDocumentContent {
228
+ type: "document";
229
+ /** `[document: name]` marker, for every wire with no document representation. */
230
+ text: string;
231
+ /** IANA media type of the payload, for example `application/pdf`. */
232
+ mediaType: string;
233
+ /** Base64 payload with no `data:` prefix. */
234
+ data: string;
235
+ /** The document's own name: an Anthropic document title or a Chat file part's filename. */
236
+ filename?: string;
237
+ }
238
+
239
+ /** A user/developer message content part: text, native media, or an attached document. */
240
+ export type OcxContentPart = OcxTextContent | OcxImageContent | OcxVideoContent | OcxDocumentContent;
213
241
 
214
242
  export interface OcxThinkingContent {
215
243
  type: "thinking";
@@ -3,6 +3,12 @@ export interface OcxTool {
3
3
  description: string;
4
4
  parameters: Record<string, unknown>;
5
5
  strict?: boolean;
6
+ /**
7
+ * Anthropic `tools[*].allowed_callers`: which callers may invoke this tool. Carried rather
8
+ * than diagnosed, because rebuilding the declaration without it hands the model a tool the
9
+ * caller had restricted and returns a normal response (#5210).
10
+ */
11
+ allowedCallers?: string[];
6
12
  /** MCP namespace (e.g. "mcp__context7") for tools flattened out of a Responses "namespace" tool. */
7
13
  namespace?: string;
8
14
  /** Freeform/custom tool (e.g. apply_patch): the model's call must be relayed as a custom_tool_call. */
@@ -31,6 +37,19 @@ export function namespacedToolName(namespace: string | undefined, name: string):
31
37
  return namespace ? `${namespace}__${name}` : name;
32
38
  }
33
39
 
40
+ /**
41
+ * Whether a declaration actually narrows who may call the tool.
42
+ *
43
+ * `["direct"]` is the state every unrestricted tool is already in, so treating it as a
44
+ * restriction would refuse ordinary traffic. Mirrors the `caller_mode` predicate in
45
+ * src/claude/compatibility.ts, which draws the same line.
46
+ */
47
+ export function toolRestrictsCallers(tool: Pick<OcxTool, "allowedCallers">): boolean {
48
+ const callers = tool.allowedCallers;
49
+ if (callers === undefined) return false;
50
+ return !(callers.length === 1 && callers[0] === "direct");
51
+ }
52
+
34
53
  /**
35
54
  * Dotted alias of a namespaced tool's wire name. Some routed providers (observed: muse-spark
36
55
  * via opencode-go) echo a namespaced tool call as "<namespace>.<name>" instead of the flattened
@@ -67,6 +86,20 @@ const CODE_MODE_HELPER_TOOL_NAMES = [
67
86
  */
68
87
  export const CODE_MODE_EXEC_TOOL_NAME = "exec";
69
88
 
89
+ /**
90
+ * The nested-helper spellings, as a membership view of the same list.
91
+ *
92
+ * A code-mode catalog never DECLARES any of them — they exist only as `tools.<helper>(...)` inside
93
+ * `exec` — so a recorded call under one of these names can only have come from a provider echoing
94
+ * the helper, which is what makes the set usable as a bounded recovery vocabulary for stored
95
+ * history (#5095). Kept beside the tuple it is built from so the two can never drift; this is a
96
+ * different question from `NAMESPACED_BARE_ALIAS_EXCLUDED_NAMES` below, which also covers `exec`
97
+ * itself because declaring THAT name is what turns normalization on.
98
+ */
99
+ export const CODE_MODE_HELPER_WIRE_NAMES: ReadonlySet<string> = new Set<string>(
100
+ CODE_MODE_HELPER_TOOL_NAMES,
101
+ );
102
+
70
103
  /**
71
104
  * Spellings that may never be MANUFACTURED as a bare alias for a namespaced tool.
72
105
  *
package/src/types.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  export type { OcxTool, OcxToolChoice } from "./types/tools";
5
5
  export {
6
6
  CODE_MODE_EXEC_TOOL_NAME,
7
+ CODE_MODE_HELPER_WIRE_NAMES,
7
8
  dottedToolName,
8
9
  namespacedToolName,
9
10
  normalizeDeclaredToolName,
@@ -16,6 +17,7 @@ export {
16
17
  isAllowedToolChoice,
17
18
  toolChoiceToolPredicate,
18
19
  declaresCodeModeExec,
20
+ toolRestrictsCallers,
19
21
  NAMESPACED_BARE_ALIAS_EXCLUDED_NAMES,
20
22
  } from "./types/tools";
21
23
 
@@ -42,6 +44,7 @@ export type {
42
44
  OcxToolResultMessage,
43
45
  OcxTextContent,
44
46
  OcxImageContent,
47
+ OcxDocumentContent,
45
48
  OcxContentPart,
46
49
  OcxThinkingContent,
47
50
  OcxToolCall,
@@ -103,6 +106,7 @@ export type {
103
106
  ResponsesItemIdRepairConfig,
104
107
  RateLimitRetryPolicy,
105
108
  TransientRetryPolicy,
109
+ ResetReplayPolicy,
106
110
  ProviderWebSearchBridgeBackend,
107
111
  ProviderWebSearchBridgeConfig,
108
112
  ProviderCostOverlay,