@bitkyc08/opencodex 2.58.0 → 2.60.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 (303) hide show
  1. package/README.md +28 -10
  2. package/gui/dist/assets/index-BTuCbqQd.css +1 -0
  3. package/gui/dist/assets/index-DoBVdPHP.js +134 -0
  4. package/gui/dist/index.html +2 -2
  5. package/gui/dist/provider-icons/crusoe.svg +1 -0
  6. package/gui/dist/provider-icons/opper.svg +3 -0
  7. package/package.json +4 -1
  8. package/src/adapters/anthropic-image-codec.ts +16 -2
  9. package/src/adapters/anthropic-image-normalize.ts +49 -2
  10. package/src/adapters/anthropic.ts +4 -1
  11. package/src/adapters/base.ts +34 -1
  12. package/src/adapters/coding-agent/turn.ts +22 -2
  13. package/src/adapters/command-code.ts +50 -3
  14. package/src/adapters/cursor/catalog.ts +11 -0
  15. package/src/adapters/cursor/checkpoint-store.ts +3 -0
  16. package/src/adapters/cursor/discovery.ts +11 -8
  17. package/src/adapters/cursor/effort-map.ts +16 -2
  18. package/src/adapters/cursor/envelope-echo.ts +55 -2
  19. package/src/adapters/cursor/live-transport.ts +26 -9
  20. package/src/adapters/cursor/message-mapper.ts +3 -2
  21. package/src/adapters/cursor/protobuf-request.ts +8 -5
  22. package/src/adapters/cursor/request-builder.ts +21 -4
  23. package/src/adapters/cursor/thread-continuity.ts +105 -31
  24. package/src/adapters/cursor/tool-guidance.ts +5 -4
  25. package/src/adapters/cursor/transport.ts +19 -0
  26. package/src/adapters/cursor.ts +45 -2
  27. package/src/adapters/devin/cloud-direct/chat.ts +14 -3
  28. package/src/adapters/devin/cloud-direct/index.ts +7 -0
  29. package/src/adapters/devin/cloud-direct/stated-reset-retry.ts +140 -0
  30. package/src/adapters/devin.ts +125 -26
  31. package/src/adapters/google-antigravity-replay.ts +1 -1
  32. package/src/adapters/google-antigravity-wire.ts +55 -4
  33. package/src/adapters/google-http.ts +57 -11
  34. package/src/adapters/google-tool-schema.ts +595 -31
  35. package/src/adapters/google-wire-compiler.ts +93 -10
  36. package/src/adapters/google-wire-shape.ts +461 -0
  37. package/src/adapters/google.ts +60 -10
  38. package/src/adapters/openai-chat/response-events.ts +61 -0
  39. package/src/adapters/openai-chat-images.ts +3 -1
  40. package/src/adapters/openai-chat.ts +10 -11
  41. package/src/adapters/openai-responses/image-gen.ts +8 -6
  42. package/src/adapters/openai-responses/passthrough.ts +25 -4
  43. package/src/adapters/openai-responses/reasoning.ts +7 -0
  44. package/src/adapters/openai-responses/tool-output-recovery.ts +75 -0
  45. package/src/adapters/openai-responses/tool-schema.ts +19 -7
  46. package/src/adapters/opencode-go-additional-tools.ts +12 -2
  47. package/src/adapters/responses-tool-schema.ts +76 -46
  48. package/src/adapters/run-turn-queue.ts +17 -4
  49. package/src/bridge/response-json.ts +1 -1
  50. package/src/bridge/sse.ts +28 -35
  51. package/src/claude/context-windows.ts +22 -0
  52. package/src/claude/outbound.ts +35 -4
  53. package/src/cli/account-api.ts +4 -3
  54. package/src/cli/account-extended.ts +26 -6
  55. package/src/cli/account-orca-import.ts +63 -0
  56. package/src/cli/account.ts +32 -4
  57. package/src/cli/capabilities.ts +40 -0
  58. package/src/cli/claude.ts +29 -1
  59. package/src/cli/codex-cli-update.ts +97 -2
  60. package/src/cli/dispatch.ts +57 -3
  61. package/src/cli/doctor.ts +218 -4
  62. package/src/cli/help.ts +4 -1
  63. package/src/cli/hub.ts +3 -2
  64. package/src/cli/index.ts +95 -22
  65. package/src/cli/models-runtime.ts +33 -4
  66. package/src/cli/opencode.ts +2 -2
  67. package/src/cli/provider.ts +13 -1
  68. package/src/cli/registry.ts +11 -1
  69. package/src/cli/runtime-api.ts +44 -0
  70. package/src/cli/start-args.ts +94 -0
  71. package/src/cli/system-command.ts +2 -0
  72. package/src/client/machine-api.ts +6 -5
  73. package/src/client/machine-listener.ts +16 -3
  74. package/src/client/runtime.ts +26 -2
  75. package/src/clients/config-export/constants.ts +2 -3
  76. package/src/clients/config-export.ts +5 -5
  77. package/src/codex/account-store.ts +146 -5
  78. package/src/codex/auth-api/account-list.ts +19 -11
  79. package/src/codex/auth-api/pool-quota-probe.ts +44 -10
  80. package/src/codex/auth-api/routes.ts +17 -2
  81. package/src/codex/auth-context.ts +16 -12
  82. package/src/codex/catalog/build-entries.ts +25 -4
  83. package/src/codex/catalog/derive-entry.ts +8 -1
  84. package/src/codex/catalog/effort.ts +10 -6
  85. package/src/codex/catalog/gather-capture.ts +22 -2
  86. package/src/codex/catalog/model-hints.ts +66 -33
  87. package/src/codex/catalog/parsing.ts +90 -5
  88. package/src/codex/catalog/provider-models.ts +19 -2
  89. package/src/codex/catalog/reserve-warn.ts +96 -0
  90. package/src/codex/catalog/retained-sync.ts +41 -26
  91. package/src/codex/catalog/routed-gather.ts +61 -3
  92. package/src/codex/cli-installation-identity.ts +210 -0
  93. package/src/codex/cli-installation-targets.ts +158 -0
  94. package/src/codex/context-compat.ts +5 -2
  95. package/src/codex/convergence.ts +5 -0
  96. package/src/codex/desired-state.ts +4 -1
  97. package/src/codex/history-job.ts +6 -6
  98. package/src/codex/history-provider.ts +24 -167
  99. package/src/codex/history-rollout-read.ts +174 -0
  100. package/src/codex/history-state-open.ts +105 -0
  101. package/src/codex/inject/config-toml.ts +44 -2
  102. package/src/codex/inject.ts +3 -2
  103. package/src/codex/internal/catalog-writer.ts +33 -1
  104. package/src/codex/lineage.ts +83 -32
  105. package/src/codex/loopback-target.ts +31 -0
  106. package/src/codex/main-account-hard-lock.ts +2 -1
  107. package/src/codex/main-account.ts +10 -3
  108. package/src/codex/main-device-reauth.ts +17 -9
  109. package/src/codex/model-cache.ts +47 -0
  110. package/src/codex/model-entitlements.ts +80 -2
  111. package/src/codex/observed-model-denials.ts +230 -0
  112. package/src/codex/orca-auth-source.ts +94 -0
  113. package/src/codex/orca-import.ts +219 -0
  114. package/src/codex/prompt-text-probe.ts +289 -16
  115. package/src/codex/quota-401-recovery.ts +12 -0
  116. package/src/codex/quota-types.ts +65 -0
  117. package/src/codex/quota.ts +24 -19
  118. package/src/codex/routing/cooldown-math.ts +8 -47
  119. package/src/codex/routing/pin-drain.ts +57 -0
  120. package/src/codex/routing.ts +20 -16
  121. package/src/codex/shim.ts +1 -1
  122. package/src/codex/subagent-model-fallback.ts +114 -2
  123. package/src/codex/windows-installation-files.ts +224 -0
  124. package/src/combos/failover.ts +125 -5
  125. package/src/config/admitted-identity.ts +222 -0
  126. package/src/config/diagnostics.ts +43 -1
  127. package/src/config/feature-flags.ts +5 -0
  128. package/src/config/load-degrade.ts +33 -0
  129. package/src/config/pending-teardown.ts +8 -0
  130. package/src/config/process-state.ts +36 -3
  131. package/src/config/provider-relative-send-path.ts +16 -0
  132. package/src/config/proxy-env.ts +31 -7
  133. package/src/config/schema/compaction-triggers.ts +11 -0
  134. package/src/config/schema/config-schema.ts +25 -0
  135. package/src/config/schema/leaf-validators.ts +76 -17
  136. package/src/config.ts +2 -2
  137. package/src/generated/compatibility-version.json +410 -258
  138. package/src/generated/model-metadata.ts +1 -1
  139. package/src/grok/reset-coupons.ts +38 -19
  140. package/src/images/loop.ts +6 -1
  141. package/src/integrations/aside-profile-context.ts +37 -3
  142. package/src/integrations/aside-profile-journal.ts +68 -3
  143. package/src/integrations/aside-profiles.ts +128 -3
  144. package/src/integrations/mutation-plan.ts +815 -0
  145. package/src/integrations/writer.ts +85 -99
  146. package/src/lab/live/transport.ts +4 -0
  147. package/src/lab/live/types.ts +5 -0
  148. package/src/lab/subject/behavior-fingerprint.ts +1 -1
  149. package/src/lib/admin-secrets.ts +9 -1
  150. package/src/lib/bounded-body.ts +4 -2
  151. package/src/lib/debug-log-buffer.ts +6 -1
  152. package/src/lib/debug.ts +23 -0
  153. package/src/lib/destination-policy.ts +48 -6
  154. package/src/lib/errors.ts +82 -15
  155. package/src/lib/http-response-semantics.ts +57 -0
  156. package/src/lib/lab-live-pinned-sender.ts +26 -12
  157. package/src/lib/local-destinations.ts +32 -5
  158. package/src/lib/pinned-http.ts +142 -2
  159. package/src/lib/plain-data.ts +103 -0
  160. package/src/lib/process-control.ts +13 -5
  161. package/src/lib/provider-outbound.ts +53 -5
  162. package/src/lib/proxy-env.ts +70 -3
  163. package/src/lib/request-execution-budget.ts +11 -3
  164. package/src/lib/response-body-inactivity.ts +193 -0
  165. package/src/lib/retry-delay.ts +69 -0
  166. package/src/lib/socks5-fetch.ts +741 -0
  167. package/src/lib/spend-ledger-owner.ts +364 -0
  168. package/src/lib/spend-reservation-ledger.ts +332 -35
  169. package/src/lib/windows-system-proxy.ts +16 -11
  170. package/src/lib/workflow-budget.ts +145 -8
  171. package/src/oauth/account-quota-rank.ts +72 -15
  172. package/src/oauth/callback-server.ts +4 -3
  173. package/src/oauth/generic-account-failover.ts +41 -27
  174. package/src/oauth/health.ts +12 -1
  175. package/src/oauth/index.ts +3 -107
  176. package/src/oauth/login-flow-state.ts +127 -0
  177. package/src/oauth/orcarouter.ts +15 -2
  178. package/src/oauth/store.ts +8 -0
  179. package/src/providers/codex-capacity.ts +9 -0
  180. package/src/providers/derive.ts +34 -17
  181. package/src/providers/devin-cli-authmode-migration.ts +14 -10
  182. package/src/providers/devin-provider-merge-migration.ts +33 -12
  183. package/src/providers/free-directory.ts +20 -2
  184. package/src/providers/key-failover.ts +327 -25
  185. package/src/providers/model-rename-migration.ts +56 -1
  186. package/src/providers/model-rename-startup.ts +7 -5
  187. package/src/providers/openai-sidecar.ts +4 -0
  188. package/src/providers/openai-virtual-models.ts +42 -2
  189. package/src/providers/opencode-go-transport.ts +14 -5
  190. package/src/providers/quota/antigravity.ts +22 -2
  191. package/src/providers/quota/report-cache.ts +3 -0
  192. package/src/providers/quota/vendor-probes-key.ts +1 -1
  193. package/src/providers/registry/entries-core.ts +39 -17
  194. package/src/providers/registry/entries-extended.ts +131 -1
  195. package/src/providers/registry/model-ids.ts +168 -0
  196. package/src/providers/registry/model-seeds.ts +87 -21
  197. package/src/providers/registry/types.ts +2 -0
  198. package/src/providers/resolved-model-policy-merge.ts +167 -0
  199. package/src/providers/resolved-model-policy.ts +406 -0
  200. package/src/providers/stale-vision-classification-migration.ts +137 -0
  201. package/src/responses/apply-patch-envelope.ts +32 -11
  202. package/src/responses/bridge-search-replay-cache.ts +152 -0
  203. package/src/responses/code-mode-helper-compat.ts +26 -16
  204. package/src/responses/custom-tool-compat.ts +1 -1
  205. package/src/responses/freeform-wrapper-scan.ts +279 -0
  206. package/src/responses/hosted-tool-policy.ts +85 -2
  207. package/src/responses/legacy-dotted-tool-name-repair.ts +134 -0
  208. package/src/responses/progressive-freeform-input.ts +130 -0
  209. package/src/responses/reasoning-envelope.ts +30 -0
  210. package/src/responses/schema.ts +9 -2
  211. package/src/responses/state.ts +5 -12
  212. package/src/responses/tool-name-aliases.ts +15 -1
  213. package/src/router.ts +91 -115
  214. package/src/routing/compatibility/behavior.ts +9 -0
  215. package/src/routing/compatibility/subject.ts +16 -1
  216. package/src/server/adapter-resolve.ts +9 -0
  217. package/src/server/auth-cors.ts +29 -0
  218. package/src/server/chat-completions.ts +13 -5
  219. package/src/server/chat-native-sse.ts +26 -9
  220. package/src/server/chat-native.ts +10 -4
  221. package/src/server/claude-messages.ts +30 -5
  222. package/src/server/effort-row.ts +11 -3
  223. package/src/server/grok-responses-control-frame.ts +160 -1
  224. package/src/server/gui-static.ts +36 -2
  225. package/src/server/inbound-body-admission.ts +187 -0
  226. package/src/server/index/serve-options.ts +86 -29
  227. package/src/server/index/spend-ledger-lifecycle.ts +66 -0
  228. package/src/server/index/websocket-handler.ts +6 -1
  229. package/src/server/index.ts +24 -25
  230. package/src/server/management/api-access.ts +3 -4
  231. package/src/server/management/aside-profile-routes.ts +266 -7
  232. package/src/server/management/config-routes.ts +48 -7
  233. package/src/server/management/context.ts +3 -0
  234. package/src/server/management/integration-routes.ts +287 -5
  235. package/src/server/management/metrics-routes.ts +20 -0
  236. package/src/server/management/model-rows.ts +224 -12
  237. package/src/server/management/provider-capability-config.ts +35 -7
  238. package/src/server/management/provider-routes.ts +70 -18
  239. package/src/server/management/route-registry.ts +14 -0
  240. package/src/server/management/shared.ts +10 -3
  241. package/src/server/management/system-restart.ts +7 -2
  242. package/src/server/management/system-routes.ts +2 -0
  243. package/src/server/management/usage-aggregate-cache.ts +4 -0
  244. package/src/server/management-api.ts +2 -0
  245. package/src/server/management-auth.ts +15 -1
  246. package/src/server/proxy-liveness.ts +97 -2
  247. package/src/server/readiness.ts +29 -10
  248. package/src/server/relay-eager.ts +24 -2
  249. package/src/server/relay.ts +136 -34
  250. package/src/server/request-log.ts +80 -3
  251. package/src/server/request-metrics.ts +236 -0
  252. package/src/server/responses/adapter-continuation.ts +74 -30
  253. package/src/server/responses/adapter-delivery.ts +39 -8
  254. package/src/server/responses/adapter-dispatch.ts +63 -30
  255. package/src/server/responses/compact.ts +89 -18
  256. package/src/server/responses/compaction-routing.ts +111 -0
  257. package/src/server/responses/core-codex-account.ts +90 -24
  258. package/src/server/responses/core-combo.ts +7 -7
  259. package/src/server/responses/core-normalize.ts +16 -15
  260. package/src/server/responses/core-opaque-recovery.ts +1 -0
  261. package/src/server/responses/core-options.ts +4 -0
  262. package/src/server/responses/encrypted-payload.ts +20 -2
  263. package/src/server/responses/fetch-helpers.ts +68 -2
  264. package/src/server/responses/passthrough-delivery.ts +41 -15
  265. package/src/server/responses/passthrough-dispatch.ts +145 -53
  266. package/src/server/responses/passthrough-execution.ts +11 -1
  267. package/src/server/responses/policy-fallback.ts +5 -13
  268. package/src/server/responses/request-prepare.ts +96 -17
  269. package/src/server/responses/request-send-budget.ts +89 -8
  270. package/src/server/responses/request-sidecar-auth.ts +17 -9
  271. package/src/server/responses/request-spend.ts +38 -9
  272. package/src/server/responses/request-transport.ts +15 -12
  273. package/src/server/responses/run-turn-execution.ts +45 -8
  274. package/src/server/responses/sidecar-execution.ts +19 -2
  275. package/src/server/responses/ws-upstream.ts +16 -28
  276. package/src/server/responses-custom-tool-repair.ts +29 -56
  277. package/src/server/responses-undeclared-tool-guard.ts +31 -1
  278. package/src/server/sse-frame-buffer.ts +12 -10
  279. package/src/server/sse-payload-rewrite.ts +37 -10
  280. package/src/server/system-env-shell.ts +5 -1
  281. package/src/server/system-env.ts +7 -1
  282. package/src/server/workflow-refusal.ts +56 -2
  283. package/src/service/cli.ts +16 -6
  284. package/src/service/guards.ts +10 -0
  285. package/src/service/health.ts +43 -0
  286. package/src/service/state.ts +7 -2
  287. package/src/tray/windows-tray.ps1 +155 -3
  288. package/src/types/accounts.ts +4 -0
  289. package/src/types/config.ts +111 -6
  290. package/src/types/provider.ts +37 -0
  291. package/src/types/request.ts +9 -1
  292. package/src/types/tools.ts +14 -0
  293. package/src/types/wire.ts +9 -1
  294. package/src/types.ts +1 -0
  295. package/src/usage/expected-prices.ts +28 -0
  296. package/src/usage/log.ts +87 -4
  297. package/src/vision/eligibility.ts +88 -9
  298. package/src/vision/plan.ts +34 -10
  299. package/src/web-search/executor.ts +41 -2
  300. package/src/web-search/loop.ts +6 -1
  301. package/src/web-search/passthrough-bridge.ts +39 -5
  302. package/gui/dist/assets/index-BbrHOIY0.js +0 -128
  303. package/gui/dist/assets/index-C5-RdDmD.css +0 -1
@@ -37,6 +37,7 @@ import {
37
37
  type WsData,
38
38
  } from "../ws-bridge";
39
39
  import { websocketsEnabled } from "../../config";
40
+ import { metricsExportEnabled } from "../../config/feature-flags";
40
41
  import { grokDefaultReasoningEffort } from "../../grok/effort";
41
42
  import { OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
42
43
  import { providerCodexAccountMode } from "../../providers/registry";
@@ -84,6 +85,7 @@ import {
84
85
  } from "../request-log";
85
86
  import { sessionLaneIdFromRequest } from "../request-log-conversation";
86
87
  import { responseWithDeferredRequestLog } from "../relay";
88
+ import { createRequestMetricsOwner } from "../request-metrics";
87
89
  import {
88
90
  corsHeaders,
89
91
  managementCorsHeaders,
@@ -266,6 +268,11 @@ export function createServeOptions(ctx: ServeOptionsContext) {
266
268
  port,
267
269
  } = ctx;
268
270
  void port;
271
+ const requestMetrics = metricsExportEnabled(config) ? createRequestMetricsOwner() : undefined;
272
+ const requestMetricsLogContext = requestMetrics ? { requestMetricsRecorder: requestMetrics } : {};
273
+ const requestManagementApiDeps: ManagementApiDeps = requestMetrics
274
+ ? { ...managementApiDeps, requestMetrics: { snapshot: () => requestMetrics.snapshot() } }
275
+ : managementApiDeps;
269
276
  const serveOptions = {
270
277
  idleTimeout: 255,
271
278
  // Bun rejects an oversized body before `fetch` runs, so the listener has to be raised
@@ -616,7 +623,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
616
623
  }), req, config);
617
624
  }
618
625
  }
619
- const mgmtResponse = await handleManagementAPI(req, url, config, managementApiDeps, principal, managementSessionControl);
626
+ const mgmtResponse = await handleManagementAPI(req, url, config, requestManagementApiDeps, principal, managementSessionControl);
620
627
  if (mgmtResponse) return withManagementCors(mgmtResponse, req, config);
621
628
  return withManagementCors(formatErrorResponse(404, "not_found", `Unknown endpoint: ${req.method} ${url.pathname}`), req, config);
622
629
  }
@@ -1197,6 +1204,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1197
1204
  const logCtx: RequestLogContext = {
1198
1205
  model: "unknown",
1199
1206
  provider: "unknown",
1207
+ ...requestMetricsLogContext,
1200
1208
  ...admissionFields(admission),
1201
1209
  inboundProtocol: "responses",
1202
1210
  };
@@ -1233,6 +1241,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1233
1241
  const logCtx: RequestLogContext = {
1234
1242
  model: "image_gen",
1235
1243
  provider: "unknown",
1244
+ ...requestMetricsLogContext,
1236
1245
  ...admissionFields(admission),
1237
1246
  };
1238
1247
  const endpoint = url.pathname.endsWith("/edits") ? "edits" as const : "generations" as const;
@@ -1290,6 +1299,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1290
1299
  const logCtx: RequestLogContext = {
1291
1300
  model: "context_history",
1292
1301
  provider: "unknown",
1302
+ ...requestMetricsLogContext,
1293
1303
  ...admissionFields(admission),
1294
1304
  };
1295
1305
  return runAdmittedHttpTurn(req, policy, async turnAdmissionLease => {
@@ -1316,6 +1326,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1316
1326
  const logCtx: RequestLogContext = {
1317
1327
  model: "web_search",
1318
1328
  provider: "unknown",
1329
+ ...requestMetricsLogContext,
1319
1330
  ...admissionFields(admission),
1320
1331
  };
1321
1332
  return runAdmittedHttpTurn(req, policy, async turnAdmissionLease => {
@@ -1340,6 +1351,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1340
1351
  const logCtx: RequestLogContext = {
1341
1352
  model: "unknown",
1342
1353
  provider: "unknown",
1354
+ ...requestMetricsLogContext,
1343
1355
  ...admissionFields(admission),
1344
1356
  inboundProtocol: "responses",
1345
1357
  };
@@ -1347,29 +1359,41 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1347
1359
  let logged = false;
1348
1360
  const finalizeNativePassthroughLog = (
1349
1361
  status: number,
1350
- meta: { terminalStatus?: ResponsesTerminalStatus; closeReason: "terminal" | "client_cancel" },
1362
+ meta: Pick<RequestLogEntry, "terminalStatus" | "closeReason">,
1351
1363
  ) => {
1352
1364
  if (logged) return;
1353
1365
  logged = true;
1354
1366
  addFinalRequestLog(requestId, start, logCtx, status, meta);
1355
1367
  };
1356
1368
  return runAdmittedHttpTurn(req, policy, async turnAdmissionLease => {
1357
- const response = await handleResponses(req, config, logCtx, {
1358
- turnAdmissionLease,
1359
- admission,
1360
- onRequestBodyRead: () => disableResponsesRequestTimeout(req, requestServer),
1361
- abortSignal: req.signal,
1362
- onFirstOutput: () => recordFirstOutput(logCtx, start),
1363
- onNativePassthroughTerminal: status => {
1364
- finalizeNativePassthroughLog(httpStatusForRequestLogTerminal(status, logCtx), {
1365
- terminalStatus: status,
1366
- closeReason: "terminal",
1367
- });
1368
- },
1369
- onNativePassthroughCancel: () => {
1370
- finalizeNativePassthroughLog(499, { closeReason: "client_cancel" });
1371
- },
1372
- });
1369
+ let response: Response;
1370
+ try {
1371
+ response = await handleResponses(req, config, logCtx, {
1372
+ turnAdmissionLease,
1373
+ admission,
1374
+ onRequestBodyRead: () => disableResponsesRequestTimeout(req, requestServer),
1375
+ abortSignal: req.signal,
1376
+ onFirstOutput: () => recordFirstOutput(logCtx, start),
1377
+ onNativePassthroughTerminal: status => {
1378
+ finalizeNativePassthroughLog(httpStatusForRequestLogTerminal(status, logCtx), {
1379
+ terminalStatus: status,
1380
+ closeReason: "terminal",
1381
+ });
1382
+ },
1383
+ onNativePassthroughCancel: () => {
1384
+ finalizeNativePassthroughLog(499, { closeReason: "client_cancel" });
1385
+ },
1386
+ });
1387
+ } catch (error) {
1388
+ // A bounded upstream body can fail before handleResponses returns a client response.
1389
+ // Finalize before rethrow so accounting observes the physical send while the caller
1390
+ // retains the existing reset/rejection instead of receiving a synthesized response.
1391
+ finalizeNativePassthroughLog(
1392
+ req.signal.aborted ? 499 : 502,
1393
+ { closeReason: req.signal.aborted ? "client_cancel" : "non_stream" },
1394
+ );
1395
+ throw error;
1396
+ }
1373
1397
  return withRequestLogId(
1374
1398
  withCors(responseWithDeferredRequestLog(response, requestId, start, logCtx), req, policy),
1375
1399
  requestId,
@@ -1414,6 +1438,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1414
1438
  const logCtx: RequestLogContext = {
1415
1439
  model: "unknown",
1416
1440
  provider: "unknown",
1441
+ ...requestMetricsLogContext,
1417
1442
  ...admissionFields(admission),
1418
1443
  inboundProtocol: "messages",
1419
1444
  };
@@ -1443,6 +1468,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1443
1468
  const logCtx: RequestLogContext = {
1444
1469
  model: "unknown",
1445
1470
  provider: "unknown",
1471
+ ...requestMetricsLogContext,
1446
1472
  ...admissionFields(admission),
1447
1473
  inboundProtocol: "chat",
1448
1474
  };
@@ -1466,7 +1492,12 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1466
1492
  }
1467
1493
  const start = Date.now();
1468
1494
  const requestId = nextRequestLogId(start);
1469
- const logCtx: RequestLogContext = { model: TRANSCRIPTION_MODEL, provider: "unknown", ...admissionFields(admission) };
1495
+ const logCtx: RequestLogContext = {
1496
+ model: TRANSCRIPTION_MODEL,
1497
+ provider: "unknown",
1498
+ ...requestMetricsLogContext,
1499
+ ...admissionFields(admission),
1500
+ };
1470
1501
  return runAdmittedHttpTurn(req, policy, async lease => {
1471
1502
  const response = await handleAudioTranscriptions(req, config, logCtx, admission, lease);
1472
1503
  addFinalRequestLog(requestId, start, logCtx, response.status);
@@ -1497,6 +1528,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1497
1528
  const logCtx: RequestLogContext = {
1498
1529
  model: "gpt-live",
1499
1530
  provider: "unknown",
1531
+ ...requestMetricsLogContext,
1500
1532
  ...admissionFields(admission),
1501
1533
  };
1502
1534
  return runAdmittedHttpTurn(req, policy, async turnAdmissionLease => {
@@ -1544,10 +1576,23 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1544
1576
  const logCtx: RequestLogContext = {
1545
1577
  model: "gpt-live",
1546
1578
  provider: "unknown",
1579
+ ...requestMetricsLogContext,
1547
1580
  ...admissionFields(admission),
1548
1581
  };
1582
+ let liveRequestFinalized = false;
1583
+ const finalizeLiveRequest = (
1584
+ status: number,
1585
+ meta?: Pick<RequestLogEntry, "terminalStatus" | "closeReason">,
1586
+ ): void => {
1587
+ if (liveRequestFinalized) return;
1588
+ liveRequestFinalized = true;
1589
+ addFinalRequestLog(requestId, start, logCtx, status, meta);
1590
+ };
1549
1591
  const turnAdmissionLease = tryAdmitTurn(sessionLaneIdFromRequest(req.headers));
1550
- if (!turnAdmissionLease) return serverBusyResponse(req, "active turns", policy);
1592
+ if (!turnAdmissionLease) {
1593
+ finalizeLiveRequest(503);
1594
+ return serverBusyResponse(req, "active turns", policy);
1595
+ }
1551
1596
  const audioController = audioClient ? new AbortController() : undefined;
1552
1597
  if (audioController) registerTurn(audioController, turnAdmissionLease);
1553
1598
  const acquisition = audioController
@@ -1567,18 +1612,26 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1567
1612
  ? await resolveLiveSidebandUpgrade(req, config, logCtx, liveSidebandTarget, turnAdmissionLease)
1568
1613
  : formatErrorResponse(401, "authentication_error", "opencodex API key required");
1569
1614
  } catch (error) {
1570
- releaseAcquisition();
1615
+ try { releaseAcquisition(); }
1616
+ finally {
1617
+ finalizeLiveRequest(req.signal.aborted ? 499 : 500,
1618
+ req.signal.aborted ? { closeReason: "client_cancel" } : undefined);
1619
+ }
1571
1620
  throw error;
1572
1621
  }
1573
1622
  if (acquisition?.signal.aborted) {
1623
+ const status = req.signal.aborted ? 499 : acquisition.didExpire() ? 504 : 503;
1574
1624
  try { if (!(resolved instanceof Response) && "finish" in resolved) resolved.finish(); }
1575
- finally { releaseAcquisition(); }
1576
- return withCors(formatErrorResponse(req.signal.aborted ? 499 : acquisition.didExpire() ? 504 : 503,
1625
+ finally {
1626
+ try { releaseAcquisition(); }
1627
+ finally { finalizeLiveRequest(status, status === 499 ? { closeReason: "client_cancel" } : undefined); }
1628
+ }
1629
+ return withCors(formatErrorResponse(status,
1577
1630
  "upstream_error", acquisition.didExpire() ? "Audio connection timed out" : "Audio connection canceled"), req, policy);
1578
1631
  }
1579
1632
  if (resolved instanceof Response) {
1580
1633
  releaseAcquisition();
1581
- addFinalRequestLog(requestId, start, logCtx, resolved.status);
1634
+ finalizeLiveRequest(resolved.status);
1582
1635
  return withCors(resolved, req, policy);
1583
1636
  }
1584
1637
  const audio = "finish" in resolved ? resolved : undefined;
@@ -1591,7 +1644,8 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1591
1644
  else releaseAcquisition();
1592
1645
  };
1593
1646
  if (req.signal.aborted) {
1594
- discardUpgrade();
1647
+ try { discardUpgrade(); }
1648
+ finally { finalizeLiveRequest(499, { closeReason: "client_cancel" }); }
1595
1649
  return withCors(formatErrorResponse(499, "client_closed_request", "Audio connection canceled"), req, policy);
1596
1650
  }
1597
1651
  const upstreamHandshake = await openLiveSidebandUpstream(
@@ -1609,7 +1663,8 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1609
1663
  } else {
1610
1664
  discardUpgrade();
1611
1665
  }
1612
- addFinalRequestLog(requestId, start, logCtx, upstreamHandshake.status);
1666
+ finalizeLiveRequest(upstreamHandshake.status,
1667
+ upstreamHandshake.status === 499 ? { closeReason: "client_cancel" } : undefined);
1613
1668
  console.error("[live] sideband upstream handshake failed: " + upstreamHandshake.message);
1614
1669
  return withCors(
1615
1670
  formatErrorResponse(upstreamHandshake.status, upstreamHandshake.code, upstreamHandshake.message),
@@ -1625,7 +1680,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1625
1680
  code: "upstream_error",
1626
1681
  message: "voice upstream closed before client upgrade",
1627
1682
  };
1628
- addFinalRequestLog(requestId, start, logCtx, failure.status);
1683
+ finalizeLiveRequest(failure.status);
1629
1684
  return withCors(formatErrorResponse(failure.status, failure.code, failure.message), req, policy);
1630
1685
  }
1631
1686
  let upgraded = false;
@@ -1657,11 +1712,12 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1657
1712
  /* ignore */
1658
1713
  }
1659
1714
  closeLiveSidebandBeforeUpgrade(upstreamHandshake.socket, () => discardUpgrade());
1715
+ finalizeLiveRequest(502);
1660
1716
  return withCors(formatErrorResponse(502, "upstream_error", "Audio WebSocket upgrade failed"), req, policy);
1661
1717
  }
1662
1718
  if (upgraded) {
1663
1719
  acquisition?.clear();
1664
- addFinalRequestLog(requestId, start, logCtx, 101);
1720
+ finalizeLiveRequest(101);
1665
1721
  return undefined as unknown as Response;
1666
1722
  }
1667
1723
  try {
@@ -1670,6 +1726,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1670
1726
  /* ignore */
1671
1727
  }
1672
1728
  closeLiveSidebandBeforeUpgrade(upstreamHandshake.socket, () => discardUpgrade());
1729
+ finalizeLiveRequest(426);
1673
1730
  return withCors(formatErrorResponse(426, "upgrade_required", "WebSocket upgrade failed"), req, policy);
1674
1731
  }
1675
1732
 
@@ -1760,7 +1817,7 @@ export function createServeOptions(ctx: ServeOptionsContext) {
1760
1817
 
1761
1818
  return withCors(formatErrorResponse(404, "not_found", `Unknown endpoint: ${req.method} ${url.pathname}`), req, config);
1762
1819
  },
1763
- websocket: createWebsocketHandler(ctx),
1820
+ websocket: createWebsocketHandler(ctx, requestMetrics),
1764
1821
  } as const;
1765
1822
  return serveOptions;
1766
1823
  }
@@ -0,0 +1,66 @@
1
+ import type { OcxSpendConfig } from "../../types/config";
2
+ import {
3
+ acquireSpendLedgerOwner,
4
+ type SpendLedgerOwnerLease,
5
+ } from "../../lib/spend-ledger-owner";
6
+ import {
7
+ configureSharedSpendLedger,
8
+ spendPolicyFromConfig,
9
+ } from "../../lib/spend-reservation-ledger";
10
+
11
+ export interface SpendLedgerServerLifecycle {
12
+ configure(spend: OcxSpendConfig | undefined): void;
13
+ track<T extends { stop(closeActiveConnections?: boolean): void | Promise<void> }>(server: T): T;
14
+ release(): void;
15
+ releaseAfterFailedStart(): void;
16
+ }
17
+
18
+ /** Acquire before config loading so every later startup failure has one rollback owner. */
19
+ export function acquireSpendLedgerServerLifecycle(configDir: string): SpendLedgerServerLifecycle {
20
+ const owner: SpendLedgerOwnerLease = acquireSpendLedgerOwner(configDir);
21
+ // Each entry returns whatever the listener's own stop returned. Typed as void-or-promise
22
+ // because the rollback below has to WAIT on it: declaring it `() => void` let the call site
23
+ // compile while statically erasing the promise it needs to await.
24
+ const failedStartStops: Array<() => void | Promise<void>> = [];
25
+ let released = false;
26
+ const release = (): void => {
27
+ if (released) return;
28
+ released = true;
29
+ owner.release();
30
+ };
31
+ return {
32
+ configure(spend): void {
33
+ configureSharedSpendLedger(spendPolicyFromConfig(spend));
34
+ },
35
+ track<T extends { stop(closeActiveConnections?: boolean): void | Promise<void> }>(server: T): T {
36
+ // Capture the raw stop before startServer replaces the public method with full teardown.
37
+ const stop = server.stop.bind(server);
38
+ failedStartStops.push(() => stop(true));
39
+ return server;
40
+ },
41
+ release,
42
+ releaseAfterFailedStart(): void {
43
+ // Every listener that came up is stopped, newest first, and the lease is held until those
44
+ // stops have actually SETTLED. Bun's Server.stop(true) returns a promise that resolves
45
+ // once connections are closed, so discarding it handed the state directory back while a
46
+ // listener could still be serving, which is the one thing single-writer ownership exists
47
+ // to prevent.
48
+ //
49
+ // This stays synchronous and returns void on purpose: startServer must not become async,
50
+ // so the wait is a continuation rather than an await. Rollback failures are contained
51
+ // because the startup error that brought us here is the one worth reporting.
52
+ const settling: Promise<unknown>[] = [];
53
+ for (const stop of failedStartStops.splice(0).reverse()) {
54
+ try {
55
+ const pending = stop();
56
+ if (pending !== undefined) settling.push(Promise.resolve(pending));
57
+ } catch { /* a rollback failure must not replace the startup error that caused it */ }
58
+ }
59
+ const finish = (): void => {
60
+ try { release(); } catch { /* same: the startup error is the one that matters */ }
61
+ };
62
+ if (settling.length === 0) { finish(); return; }
63
+ void Promise.allSettled(settling).then(finish);
64
+ },
65
+ };
66
+ }
@@ -85,13 +85,17 @@ import {
85
85
  resolveLiveSidebandUpgrade,
86
86
  } from "../live";
87
87
  import type { ServeOptionsContext } from "./serve-options";
88
+ import type { RequestMetricsRecorder } from "../request-metrics";
88
89
 
89
90
  /**
90
91
  * The WebSocket half of the Bun.serve options, split out of serve-options.ts to keep that file
91
92
  * under the 2,000-line ratchet threshold. The body is the original handler verbatim; it reads the
92
93
  * same startServer context the HTTP half does, so it takes the same context object.
93
94
  */
94
- export function createWebsocketHandler(ctx: ServeOptionsContext) {
95
+ export function createWebsocketHandler(
96
+ ctx: ServeOptionsContext,
97
+ requestMetricsRecorder?: RequestMetricsRecorder,
98
+ ) {
95
99
  const { config, deps } = ctx;
96
100
  return {
97
101
  maxPayloadLength: MAX_WS_FRAME_BYTES,
@@ -283,6 +287,7 @@ export function createWebsocketHandler(ctx: ServeOptionsContext) {
283
287
  const logCtx: RequestLogContext = {
284
288
  model: "unknown",
285
289
  provider: "unknown",
290
+ ...(requestMetricsRecorder ? { requestMetricsRecorder } : {}),
286
291
  ...(wsAdmission ? admissionFields(wsAdmission) : {}),
287
292
  inboundProtocol: "responses",
288
293
  };
@@ -1,5 +1,6 @@
1
1
  import { remoteWorkspaceEnabled } from "../remote-control/workspace-activation";
2
2
  import { AuxiliaryListenerBindError } from "./ports";
3
+ import { runAdmittedBodyWork } from "./inbound-body-admission";
3
4
  import {
4
5
  buildWarmupCompletionFrames,
5
6
  buildWsErrorFrame,
@@ -106,7 +107,6 @@ export {
106
107
  unregisterTurn,
107
108
  } from "./lifecycle";
108
109
  import {
109
- addFinalRequestLog,
110
110
  hydrateRequestLogsFromDisk,
111
111
  httpStatusForRequestLogTerminal,
112
112
  inspectResponseLogSsePayload,
@@ -116,8 +116,7 @@ import {
116
116
  type RequestLogEntry,
117
117
  } from "./request-log";
118
118
  import { sessionLaneIdFromRequest } from "./request-log-conversation";
119
- import { admitWorkflowTurn, type WorkflowLane } from "../lib/workflow-budget";
120
- import { workflowRefusalResponse, type WorkflowRefusalLog } from "./workflow-refusal";
119
+ import { admitHttpWorkflowTurn, workflowDecisionRefusalResponse, type WorkflowRefusalLog } from "./workflow-refusal";
121
120
  export {
122
121
  addFinalRequestLog,
123
122
  filterRequestLogs,
@@ -181,7 +180,7 @@ import {
181
180
  type NativeMainStartupLifecycle,
182
181
  } from "../codex/native-profile-startup";
183
182
  import { EXTERNAL_CALL_PREFIX, LiveCallBindings } from "./live-call-bindings";
184
- import { codexCompatibleUrl, contextEndpoint, contextRelayActivated } from "../codex/context-compat";
183
+ import { contextEndpoint, contextRelayActivated } from "../codex/context-compat";
185
184
  import { fetchAllModels, handleManagementAPI, VERSION, type ManagementApiDeps } from "./management-api";
186
185
  import {
187
186
  createManagementSessionControl,
@@ -205,8 +204,15 @@ import {
205
204
  import { detectInstall } from "../update/index";
206
205
  import { createServeOptions, type ServerIngress } from "./index/serve-options";
207
206
  import { inspectStartupOwnership, setStartupCacheInvalidationWrite, warnAgentTaskRecoveryStartup, warnPlaintextV2AgentMessagesStartup, type StartServerDeps } from "./index/startup-warnings";
207
+ import { acquireSpendLedgerServerLifecycle, type SpendLedgerServerLifecycle } from "./index/spend-ledger-lifecycle";
208
208
 
209
209
  export function startServer(port?: number, deps: StartServerDeps = {}): Server<WsData> {
210
+ const spendLedgerLifecycle = acquireSpendLedgerServerLifecycle(getConfigDir());
211
+ try { return startServerWithSpendLedgerOwner(port, deps, spendLedgerLifecycle); }
212
+ catch (error) { spendLedgerLifecycle.releaseAfterFailedStart(); throw error; }
213
+ }
214
+
215
+ function startServerWithSpendLedgerOwner(port: number | undefined, deps: StartServerDeps, spendLedgerLifecycle: SpendLedgerServerLifecycle): Server<WsData> {
210
216
  const localAttestationSecret = deps.localAttestationSecret ?? createLocalAttestationSecret();
211
217
  // Captured before loadConfig() starts the optional ACL flight so stop() drains the same dir
212
218
  // even if OPENCODEX_HOME changes underneath a long-lived process.
@@ -225,7 +231,7 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
225
231
  warnPlaintextV2AgentMessagesStartup(config);
226
232
  warnAgentTaskRecoveryStartup(config);
227
233
  setLiveStateStoreConfig(config);
228
- applyProxyEnv(config);
234
+ applyProxyEnv(config, true);
229
235
  assertServerAuthConfig(config);
230
236
  const managementAuth = deps.managementAuthState ?? initializeManagementAuthState(config);
231
237
  const managementSessionControl = createManagementSessionControl(managementAuth);
@@ -299,6 +305,8 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
299
305
  registerAppOwnedMemorySweepFallback();
300
306
  configureAppOwnedMemoryBudget(resolveAppOwnedMemoryBudgetBytes(config.appOwnedMemoryBudgetMb));
301
307
  enforceAppOwnedMemoryBudget();
308
+ // Observe-only mode still journals physical sends, so every server owns before configuring.
309
+ spendLedgerLifecycle.configure(config.spend);
302
310
  registerCodexCooldownRecoveryProbeWorker(config);
303
311
  // Issue #42 Phase 3: opt-in archived auto-cleanup (default OFF). Unref'd hourly
304
312
  // tick for daily/weekly; startup evaluation is fire-and-forget after listen.
@@ -498,28 +506,18 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
498
506
  ): Promise<Response> {
499
507
  const lease = tryAdmitTurn(sessionLaneIdFromRequest(req.headers));
500
508
  if (!lease) return serverBusyResponse(req, "active turns", policy);
501
- // A fan-out shares the conversation it serves. Without a reserve, a worker burst takes every
502
- // slot under its own root and the interactive turn that started it waits behind its own
503
- // children. A request that names a parent is treated as that fan-out; a top-level request is
504
- // the conversation and may use the reserved slots.
505
- const workflowRootId = req.headers.get("x-codex-parent-thread-id")?.trim() || undefined;
506
- const workflowThreadId = req.headers.get("thread-id")?.trim() || undefined;
507
- const workflowLane: WorkflowLane = workflowRootId !== undefined
508
- && workflowThreadId !== undefined
509
- && workflowThreadId !== workflowRootId
510
- ? "worker"
511
- : "interactive";
512
- const workflow = admitWorkflowTurn(workflowRootId, workflowLane, undefined, workflowThreadId);
509
+ // Root, lane, and the refusal that follows from them, all live in ./workflow-refusal.
510
+ const workflow = admitHttpWorkflowTurn(req.headers);
513
511
  if (workflow && !workflow.admitted) {
514
512
  lease.release();
515
513
  // withCors, because without Access-Control-Allow-Origin the exposed refusal header is
516
514
  // still unreadable to a browser dashboard -- which made exposing it pointless.
517
- return withCors(workflowRefusalResponse(workflow.reason, undefined, refusalLog), req, policy);
515
+ return withCors(workflowDecisionRefusalResponse(workflow, undefined, refusalLog), req, policy);
518
516
  }
519
517
  const releaseWorkflow = (): void => { if (workflow?.admitted) workflow.lease.release(); };
520
518
  let response: Response;
521
519
  try {
522
- response = await work(lease);
520
+ response = await runAdmittedBodyWork(req, policy, config.maxInboundBodyBytes, () => work(lease), refusalLog);
523
521
  } catch (error) {
524
522
  releaseWorkflow();
525
523
  lease.release();
@@ -707,7 +705,7 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
707
705
  get remoteWorkspaceStopping() { return remoteWorkspaceStopping; },
708
706
  });
709
707
 
710
- server = Bun.serve<WsData>({ ...serveOptions, port: listenPort, hostname: bindHost });
708
+ server = spendLedgerLifecycle.track(Bun.serve<WsData>({ ...serveOptions, port: listenPort, hostname: bindHost }));
711
709
 
712
710
  // Both binds are one startup transaction (#1102). If the loopback bind fails after the
713
711
  // public one succeeded, leaving the public listener up would strand it: the CLI's port
@@ -715,11 +713,11 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
715
713
  // accumulating listeners. Roll back and rethrow the original error instead.
716
714
  if (loopbackListenerPort !== null) {
717
715
  try {
718
- loopbackServer = Bun.serve<WsData>({
716
+ loopbackServer = spendLedgerLifecycle.track(Bun.serve<WsData>({
719
717
  ...serveOptions,
720
718
  port: loopbackListenerPort,
721
719
  hostname: "127.0.0.1",
722
- });
720
+ }));
723
721
  } catch (error) {
724
722
  try {
725
723
  // startServer is synchronous, so this rollback cannot await. Bun begins closing the
@@ -735,11 +733,11 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
735
733
  }
736
734
  if (managementIngressPort !== null) {
737
735
  try {
738
- managementIngressServer = Bun.serve<WsData>({
736
+ managementIngressServer = spendLedgerLifecycle.track(Bun.serve<WsData>({
739
737
  ...serveOptions,
740
738
  port: managementIngressPort,
741
739
  hostname: "127.0.0.1",
742
- });
740
+ }));
743
741
  } catch (error) {
744
742
  // Preserve the management bind failure while synchronously initiating rollback of every
745
743
  // listener already opened in this startup transaction. startServer must not become async.
@@ -796,7 +794,8 @@ export function startServer(port?: number, deps: StartServerDeps = {}): Server<W
796
794
  // removes the dir right after stop() settles would hit EPERM/EBUSY on Windows
797
795
  // otherwise. Runs even when an earlier release rejected — that rejection still
798
796
  // propagates, but not before the child is drained.
799
- await flushConfigDirHardening(startupConfigDir);
797
+ try { spendLedgerLifecycle.release(); }
798
+ finally { await flushConfigDirHardening(startupConfigDir); }
800
799
  }
801
800
  },
802
801
  );
@@ -1,7 +1,6 @@
1
1
  import type { OcxConfig } from "../../types";
2
2
  import { isWildcardHostname } from "../../codex/loopback-target";
3
- import { localInferenceDestination } from "../../lib/local-destinations";
4
- import { probeHostname } from "../proxy-liveness";
3
+ import { localCredentialDestinationHostname, localInferenceDestination } from "../../lib/local-destinations";
5
4
  import { isCanonicalOpenAiForwardProvider, OPENAI_API_PROVIDER_ID, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers-destination";
6
5
  import { LIVE_AUDIO_MODEL, TRANSCRIPTION_MODEL } from "../audio-upstream";
7
6
 
@@ -95,7 +94,7 @@ export function resolveApiAccessBaseUrl(
95
94
  const port = config.port ?? 10100;
96
95
 
97
96
  if (!isWildcardBindHost(config.hostname)) {
98
- return `http://${probeHostname(config.hostname)}:${port}/v1`;
97
+ return `http://${localCredentialDestinationHostname(config.hostname)}:${port}/v1`;
99
98
  }
100
99
 
101
100
  const fromOrigin = opts.requestOrigin ? originBaseUrl(opts.requestOrigin) : null;
@@ -140,7 +139,7 @@ export function resolveApiAccessDisplayHost(
140
139
  opts: BuildApiAccessEndpointsOptions = {},
141
140
  ): string {
142
141
  if (!isWildcardBindHost(configHostname)) {
143
- return probeHostname(configHostname);
142
+ return localCredentialDestinationHostname(configHostname);
144
143
  }
145
144
  try {
146
145
  return new URL(resolveApiAccessBaseUrl({ hostname: configHostname, port: 10100 }, opts)).hostname