@bitkyc08/opencodex 2.86.0 → 2.88.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 (400) hide show
  1. package/gui/dist/assets/App-CHC89EXQ.js +58 -0
  2. package/gui/dist/assets/App-wT3mXqFz.css +1 -0
  3. package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-B5bC7AWL.js} +1 -1
  4. package/gui/dist/assets/index-B5bsvxor.js +94 -0
  5. package/gui/dist/assets/index-DPeB9HpV.css +1 -0
  6. package/gui/dist/assets/quota-summary-D8y2wTsB.js +1 -0
  7. package/gui/dist/index.html +2 -2
  8. package/package.json +1 -1
  9. package/src/adapters/anthropic/beta-allowlist.ts +9 -8
  10. package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
  11. package/src/adapters/anthropic/passthrough.ts +7 -23
  12. package/src/adapters/anthropic-progress.ts +84 -0
  13. package/src/adapters/anthropic-rejection-memo.ts +71 -0
  14. package/src/adapters/anthropic-replay.ts +135 -0
  15. package/src/adapters/anthropic-tool-catalog.ts +11 -0
  16. package/src/adapters/anthropic.ts +151 -33
  17. package/src/adapters/base.ts +2 -0
  18. package/src/adapters/claude-cli/adapter.ts +111 -23
  19. package/src/adapters/claude-cli/profiles.ts +2 -1
  20. package/src/adapters/claude-cli/stable-replay.ts +108 -0
  21. package/src/adapters/claude-cli/usage-admission.ts +230 -0
  22. package/src/adapters/coding-agent/protocol.ts +1 -1
  23. package/src/adapters/coding-agent/turn.ts +18 -2
  24. package/src/adapters/dsh-account.ts +97 -0
  25. package/src/adapters/google-progress-history.ts +53 -0
  26. package/src/adapters/google.ts +52 -9
  27. package/src/adapters/openai-chat/messages.ts +4 -3
  28. package/src/adapters/openai-chat/passthrough.ts +8 -2
  29. package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
  30. package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
  31. package/src/adapters/openai-responses/passthrough.ts +57 -125
  32. package/src/adapters/openai-responses/request-strips.ts +25 -3
  33. package/src/adapters/openai-responses/transport.ts +81 -0
  34. package/src/adapters/qoder/adapter.ts +4 -2
  35. package/src/adapters/registry.ts +5 -0
  36. package/src/adapters/zed.ts +26 -1
  37. package/src/advisor/activity.ts +55 -0
  38. package/src/advisor/budget.ts +62 -0
  39. package/src/advisor/consult.ts +4 -1
  40. package/src/advisor/context.ts +16 -4
  41. package/src/advisor/evidence-tool.ts +21 -0
  42. package/src/advisor/evidence.ts +117 -0
  43. package/src/advisor/runtime.ts +79 -31
  44. package/src/advisor/settings.ts +11 -2
  45. package/src/bridge/errors.ts +19 -1
  46. package/src/bridge/internal.ts +7 -1
  47. package/src/bridge/response-json.ts +18 -3
  48. package/src/bridge/sse.ts +59 -8
  49. package/src/bridge/terminal-refusal.ts +59 -0
  50. package/src/claude/desktop-3p-library.ts +32 -7
  51. package/src/claude/desktop-3p.ts +55 -13
  52. package/src/claude/desktop-first-party.ts +1 -1
  53. package/src/claude/desktop-picker-platform.ts +4 -0
  54. package/src/claude/desktop-picker.ts +15 -8
  55. package/src/claude/desktop-system-proxy.ts +50 -4
  56. package/src/claude/inbound-content-options.ts +77 -0
  57. package/src/claude/inbound.ts +33 -0
  58. package/src/claude/intercept/picker-ca-startup.ts +2 -1
  59. package/src/claude/intercept/picker-runtime.ts +6 -5
  60. package/src/claude/intercept/picker-trust-windows.ts +338 -0
  61. package/src/claude/intercept/picker-trust.ts +19 -0
  62. package/src/claude/intercept/port-policy.ts +30 -0
  63. package/src/claude/intercept/runtime.ts +97 -94
  64. package/src/claude/outbound.ts +46 -13
  65. package/src/cli/account-api.ts +1 -0
  66. package/src/cli/account-policy-dto.ts +6 -0
  67. package/src/cli/account-policy.ts +34 -5
  68. package/src/cli/account-preservation.ts +44 -0
  69. package/src/cli/account.ts +8 -2
  70. package/src/cli/advisor.ts +17 -3
  71. package/src/cli/capabilities-accounts.ts +20 -1
  72. package/src/cli/capabilities-agents-routing.ts +5 -2
  73. package/src/cli/capabilities-base.ts +38 -10
  74. package/src/cli/capabilities-observe-system.ts +63 -3
  75. package/src/cli/capabilities-provider-models.ts +11 -0
  76. package/src/cli/codex-cli-update.ts +151 -41
  77. package/src/cli/codex-provider.ts +44 -0
  78. package/src/cli/combo-input.ts +10 -1
  79. package/src/cli/combo-result.ts +1 -1
  80. package/src/cli/combo.ts +2 -1
  81. package/src/cli/cross-home-owner.ts +33 -16
  82. package/src/cli/dispatch.ts +25 -4
  83. package/src/cli/doctor.ts +6 -1
  84. package/src/cli/ensure-desktop-cold-start.ts +60 -7
  85. package/src/cli/help.ts +3 -2
  86. package/src/cli/index.ts +1 -1
  87. package/src/cli/integration-plan-dto.ts +1 -0
  88. package/src/cli/log-history.ts +172 -0
  89. package/src/cli/loopback-family.ts +31 -0
  90. package/src/cli/models-runtime-subcommands.ts +1 -0
  91. package/src/cli/models-runtime.ts +72 -8
  92. package/src/cli/observe-stream.ts +2 -0
  93. package/src/cli/observe.ts +16 -4
  94. package/src/cli/provider.ts +27 -5
  95. package/src/cli/registry.ts +28 -8
  96. package/src/cli/runtime-api.ts +5 -3
  97. package/src/cli/system-command.ts +40 -8
  98. package/src/cli/uninstall-client-state.ts +32 -0
  99. package/src/cli/usage-compare.ts +309 -0
  100. package/src/cli/usage-model-search.ts +4 -4
  101. package/src/cli/usage-report.ts +2 -2
  102. package/src/cli/v2.ts +1 -1
  103. package/src/client/machine-listener.ts +1 -3
  104. package/src/client/runtime.ts +1 -0
  105. package/src/clients/client-path.ts +51 -0
  106. package/src/clients/config-export/contracts.ts +2 -1
  107. package/src/clients/config-export.ts +18 -44
  108. package/src/clients/gentle-shell-path.ts +87 -0
  109. package/src/codex/account-auto-switch.ts +2 -0
  110. package/src/codex/account-identity.ts +29 -0
  111. package/src/codex/account-label.ts +1 -1
  112. package/src/codex/account-lifecycle.ts +21 -5
  113. package/src/codex/account-preservation.ts +28 -0
  114. package/src/codex/account-use-remaining.ts +23 -0
  115. package/src/codex/app-server-processes.ts +97 -7
  116. package/src/codex/app-server-restart-service.ts +55 -4
  117. package/src/codex/auth-api/account-list.ts +4 -0
  118. package/src/codex/auth-api/account-pause-group.ts +6 -50
  119. package/src/codex/auth-api/account-preservation.ts +57 -0
  120. package/src/codex/auth-api/routes.ts +8 -0
  121. package/src/codex/auth-api/use-remaining-route.ts +36 -0
  122. package/src/codex/auth-api.ts +0 -1
  123. package/src/codex/auth-context.ts +2 -2
  124. package/src/codex/catalog/auto-review.ts +20 -3
  125. package/src/codex/catalog/build-entries.ts +9 -1
  126. package/src/codex/catalog/combo-member.ts +14 -5
  127. package/src/codex/catalog/configured-tiers.ts +1 -1
  128. package/src/codex/catalog/gather-capture.ts +2 -0
  129. package/src/codex/catalog/metadata.ts +11 -9
  130. package/src/codex/catalog/model-hints.ts +21 -16
  131. package/src/codex/catalog/model-visibility.ts +3 -1
  132. package/src/codex/catalog/parsing.ts +30 -86
  133. package/src/codex/catalog/provider-models.ts +36 -34
  134. package/src/codex/catalog/routed-gather.ts +14 -13
  135. package/src/codex/cli-install-provenance.ts +105 -17
  136. package/src/codex/cli-update-plan.ts +545 -0
  137. package/src/codex/config-write-lock.ts +23 -2
  138. package/src/codex/desktop-app/picker-freshness.ts +96 -0
  139. package/src/codex/desktop-app-restart.ts +21 -0
  140. package/src/codex/desktop-compatibility/installed-build.ts +24 -6
  141. package/src/codex/desktop-compatibility/routing-preflight.ts +1 -1
  142. package/src/codex/desktop-compatibility/runtime.ts +12 -3
  143. package/src/codex/desktop-compatibility/windows-key-protection.ts +115 -9
  144. package/src/codex/features.ts +85 -45
  145. package/src/codex/inject/history-failure.ts +27 -0
  146. package/src/codex/inject/restore.ts +24 -2
  147. package/src/codex/inject/routing-classify.ts +4 -3
  148. package/src/codex/inject.ts +18 -27
  149. package/src/codex/main-account-hard-lock.ts +30 -1
  150. package/src/codex/native-profile-startup.ts +1 -1
  151. package/src/codex/ocx-compaction-history.ts +12 -10
  152. package/src/codex/paths.ts +86 -1
  153. package/src/codex/provider-routing.ts +174 -0
  154. package/src/codex/refresh.ts +2 -2
  155. package/src/codex/routing/health-store.ts +1 -1
  156. package/src/codex/routing/selection.ts +4 -3
  157. package/src/codex/routing/thread-affinity.ts +1 -1
  158. package/src/codex/signed-in-identity.ts +43 -0
  159. package/src/codex/subagent-model-fallback.ts +1 -1
  160. package/src/codex/sync.ts +6 -1
  161. package/src/codex/user-identity.ts +16 -5
  162. package/src/codex/websocket-activity.ts +161 -0
  163. package/src/codex/websocket-registry.ts +25 -1
  164. package/src/codex/windows-local-appdata.ts +84 -0
  165. package/src/combos/failover.ts +6 -1
  166. package/src/combos/index.ts +1 -0
  167. package/src/combos/resolve.ts +46 -10
  168. package/src/combos/types.ts +15 -0
  169. package/src/config/diagnostics.ts +33 -6
  170. package/src/config/live-reconcile.ts +38 -2
  171. package/src/config/load-degrade.ts +17 -0
  172. package/src/config/opencode-wire-upgrade.ts +54 -0
  173. package/src/config/opencode-zen-upgrade.ts +53 -0
  174. package/src/config/owner-registry.ts +22 -17
  175. package/src/config/provider-validation.ts +45 -2
  176. package/src/config/schema/config-schema.ts +22 -0
  177. package/src/config/schema/leaf-validators.ts +38 -9
  178. package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
  179. package/src/config/schema/native-reasoning-retention.ts +26 -0
  180. package/src/config.ts +2 -0
  181. package/src/generated/compatibility-version.json +657 -297
  182. package/src/grok/sync.ts +1 -1
  183. package/src/images/loop.ts +116 -27
  184. package/src/integrations/catalog-refresh.ts +1 -1
  185. package/src/integrations/mutation-plan.ts +1 -0
  186. package/src/integrations/registry.ts +8 -0
  187. package/src/lib/codex-restart-contract.ts +15 -0
  188. package/src/lib/desktop-upstream-tunnel.ts +18 -53
  189. package/src/lib/empty-tool-catalog.ts +62 -0
  190. package/src/lib/errors.ts +19 -3
  191. package/src/lib/gcp-adc.ts +96 -9
  192. package/src/lib/gcp-credential-input.ts +12 -0
  193. package/src/lib/lab-live-route-production.ts +5 -0
  194. package/src/lib/opaque-proxy-tunnel.ts +217 -0
  195. package/src/lib/redact.ts +2 -2
  196. package/src/lib/request-failure-model.ts +2 -0
  197. package/src/oauth/account-quota-rank.ts +69 -2
  198. package/src/oauth/anthropic-account-refusal.ts +20 -2
  199. package/src/oauth/anthropic-oauth-definitions.ts +3 -1
  200. package/src/oauth/anthropic-routing.ts +1 -21
  201. package/src/oauth/anthropic.ts +38 -7
  202. package/src/oauth/callback-server.ts +27 -1
  203. package/src/oauth/chatgpt-device.ts +1 -1
  204. package/src/oauth/chatgpt-shared.ts +83 -0
  205. package/src/oauth/chatgpt.ts +8 -72
  206. package/src/oauth/dsh.ts +203 -0
  207. package/src/oauth/generic-account-failover.ts +40 -11
  208. package/src/oauth/generic-session-affinity.ts +125 -0
  209. package/src/oauth/index.ts +184 -109
  210. package/src/oauth/local-token-detect.ts +21 -1
  211. package/src/oauth/login-cli-errors.ts +44 -0
  212. package/src/oauth/login-cli.ts +69 -7
  213. package/src/oauth/login-flow-state.ts +371 -23
  214. package/src/oauth/meta-muse-errors.ts +41 -0
  215. package/src/oauth/meta-muse-keychain.ts +52 -0
  216. package/src/oauth/meta-muse.ts +51 -84
  217. package/src/oauth/model-discovery-auth.ts +3 -0
  218. package/src/oauth/pool-settings-capability.ts +4 -2
  219. package/src/oauth/refresh-flight-retirement.ts +48 -0
  220. package/src/oauth/session-affinity-key.ts +23 -0
  221. package/src/oauth/store-anthropic-instance.ts +9 -2
  222. package/src/oauth/store.ts +12 -5
  223. package/src/protocols/encoders/adapter-events.ts +6 -5
  224. package/src/protocols/encoders/messages.ts +9 -6
  225. package/src/protocols/provider-summary.ts +2 -1
  226. package/src/providers/api-key-cooldowns.ts +49 -0
  227. package/src/providers/api-key-resolve.ts +11 -6
  228. package/src/providers/api-key-response-ownership.ts +57 -0
  229. package/src/providers/api-key-selection.ts +31 -6
  230. package/src/providers/api-keys.ts +214 -3
  231. package/src/providers/claude-cli-usage.ts +96 -0
  232. package/src/providers/dsh-account-destination.ts +31 -0
  233. package/src/providers/dsh-account-models.ts +41 -0
  234. package/src/providers/gcp-credential-cleanup.ts +59 -0
  235. package/src/providers/key-failover.ts +54 -50
  236. package/src/providers/key-store.ts +7 -2
  237. package/src/providers/model-rename-fields.ts +3 -0
  238. package/src/providers/model-rename-migration.ts +2 -2
  239. package/src/providers/openai-tiers-destination.ts +11 -2
  240. package/src/providers/openai-tiers.ts +5 -12
  241. package/src/providers/opencode-go-transport.ts +3 -1
  242. package/src/providers/opencode-zen-rate-limit.ts +7 -2
  243. package/src/providers/quota/account-cache.ts +6 -5
  244. package/src/providers/quota/account-row.ts +34 -0
  245. package/src/providers/quota/antigravity.ts +28 -7
  246. package/src/providers/quota/dsh-account.ts +70 -0
  247. package/src/providers/quota/vendor-probes-oauth.ts +1 -93
  248. package/src/providers/quota-types.ts +2 -0
  249. package/src/providers/quota.ts +14 -20
  250. package/src/providers/registry/entries-core.ts +37 -8
  251. package/src/providers/registry/types.ts +1 -1
  252. package/src/providers/request-pacing.ts +77 -19
  253. package/src/providers/routed-config.ts +339 -0
  254. package/src/providers/service-tier.ts +2 -1
  255. package/src/quota/window-mapping.ts +5 -2
  256. package/src/responses/anthropic-progress.ts +34 -0
  257. package/src/responses/assistant-replay.ts +35 -0
  258. package/src/responses/compaction-history-framing.ts +65 -0
  259. package/src/responses/compaction.ts +6 -1
  260. package/src/responses/incomplete-reason.ts +31 -0
  261. package/src/responses/parser.ts +50 -5
  262. package/src/responses/reasoning-envelope.ts +4 -0
  263. package/src/responses/reasoning-replay-cache.ts +74 -6
  264. package/src/responses/reasoning-retention.ts +173 -0
  265. package/src/responses/retained-compaction.ts +46 -0
  266. package/src/responses/schema.ts +23 -0
  267. package/src/router.ts +8 -321
  268. package/src/routing/analytics.ts +7 -15
  269. package/src/routing/health.ts +37 -22
  270. package/src/routing/history/indexer.ts +6 -5
  271. package/src/routing/history/ingest.ts +6 -2
  272. package/src/routing/profile.ts +1 -1
  273. package/src/server/admission-model-scope.ts +17 -0
  274. package/src/server/auth-cors.ts +11 -0
  275. package/src/server/chat-completions.ts +7 -1
  276. package/src/server/chat-native-reasoning.ts +123 -0
  277. package/src/server/chat-native-sse.ts +110 -7
  278. package/src/server/chat-native.ts +76 -17
  279. package/src/server/claude-messages.ts +56 -414
  280. package/src/server/effort-policy.ts +1 -1
  281. package/src/server/images.ts +15 -2
  282. package/src/server/index/live-sideband.ts +3 -2
  283. package/src/server/index/model-alias-id.ts +36 -0
  284. package/src/server/index/optional-listeners.ts +2 -0
  285. package/src/server/index/serve-options.ts +20 -6
  286. package/src/server/index/startup-warnings.ts +2 -0
  287. package/src/server/index/websocket-handler.ts +18 -4
  288. package/src/server/index.ts +8 -8
  289. package/src/server/inference/advisor-diagnostics.ts +31 -0
  290. package/src/server/lifecycle.ts +13 -1
  291. package/src/server/management/advisor-routes.ts +15 -4
  292. package/src/server/management/claude-desktop-picker-routes.ts +1 -1
  293. package/src/server/management/combo-routes.ts +176 -50
  294. package/src/server/management/companion-routes.ts +1 -1
  295. package/src/server/management/config-routes.ts +75 -4
  296. package/src/server/management/cursor-integration-routes.ts +1 -1
  297. package/src/server/management/model-routes.ts +11 -6
  298. package/src/server/management/model-rows.ts +14 -0
  299. package/src/server/management/native-integration-routes.ts +2 -7
  300. package/src/server/management/native-reasoning-retention-routes.ts +41 -0
  301. package/src/server/management/oauth-account-routes.ts +87 -14
  302. package/src/server/management/provider-gcp-credential-routes.ts +29 -0
  303. package/src/server/management/provider-key-pacing-routes.ts +31 -0
  304. package/src/server/management/provider-overwrite-carry.ts +7 -0
  305. package/src/server/management/provider-routes.ts +25 -30
  306. package/src/server/management/route-registry.ts +10 -1
  307. package/src/server/management-api.ts +3 -1
  308. package/src/server/messages-native-tool-guard.ts +185 -0
  309. package/src/server/messages-native.ts +117 -24
  310. package/src/server/messages-shared.ts +431 -0
  311. package/src/server/relay.ts +1 -1
  312. package/src/server/request-log-terminal-status.ts +1 -1
  313. package/src/server/request-log.ts +6 -1
  314. package/src/server/responses/account-change-state.ts +8 -2
  315. package/src/server/responses/adapter-continuation.ts +47 -19
  316. package/src/server/responses/adapter-delivery.ts +72 -12
  317. package/src/server/responses/adapter-dispatch.ts +46 -3
  318. package/src/server/responses/advisor-retention.ts +117 -0
  319. package/src/server/responses/advisor-slot.ts +73 -41
  320. package/src/server/responses/anthropic-thinking-recovery.ts +153 -0
  321. package/src/server/responses/codex-ws-wire.ts +15 -8
  322. package/src/server/responses/combo-stream-preflight.ts +3 -2
  323. package/src/server/responses/compact.ts +303 -125
  324. package/src/server/responses/compaction-recovery.ts +20 -10
  325. package/src/server/responses/compaction-retention.ts +61 -0
  326. package/src/server/responses/context-overflow.ts +11 -6
  327. package/src/server/responses/core-auth.ts +6 -0
  328. package/src/server/responses/core-codex-account.ts +11 -1
  329. package/src/server/responses/core-combo-failure.ts +2 -0
  330. package/src/server/responses/core-combo.ts +121 -36
  331. package/src/server/responses/core-errors.ts +3 -3
  332. package/src/server/responses/core-lifetime.ts +8 -4
  333. package/src/server/responses/core-opaque-recovery.ts +35 -0
  334. package/src/server/responses/core-options.ts +7 -0
  335. package/src/server/responses/core-pipeline.ts +141 -0
  336. package/src/server/responses/core-replay.ts +23 -3
  337. package/src/server/responses/core.ts +2 -115
  338. package/src/server/responses/fetch-helpers.ts +73 -19
  339. package/src/server/responses/main-hard-lock-routing.ts +109 -0
  340. package/src/server/responses/native-response-control.ts +4 -2
  341. package/src/server/responses/passthrough-delivery.ts +3 -1
  342. package/src/server/responses/passthrough-dispatch.ts +15 -13
  343. package/src/server/responses/passthrough-execution.ts +3 -2
  344. package/src/server/responses/request-prepare.ts +77 -12
  345. package/src/server/responses/request-sidecar-auth.ts +2 -2
  346. package/src/server/responses/request-transport.ts +104 -27
  347. package/src/server/responses/response-effects.ts +2 -0
  348. package/src/server/responses/response-tool-ownership.ts +21 -0
  349. package/src/server/responses/run-turn-execution.ts +96 -30
  350. package/src/server/responses/sidecar-execution.ts +63 -9
  351. package/src/server/responses/sidecar-send-budget.ts +2 -2
  352. package/src/server/responses/terminal-guard.ts +2 -16
  353. package/src/server/responses-undeclared-tool-guard.ts +1 -1
  354. package/src/server/ws-bridge.ts +2 -0
  355. package/src/service/auth-preflight.ts +71 -0
  356. package/src/service/cli.ts +4 -2
  357. package/src/service/definition.ts +97 -0
  358. package/src/service/guards.ts +1 -61
  359. package/src/service/health-sentinel-linux.ts +112 -0
  360. package/src/service/health-sentinel-main.ts +76 -57
  361. package/src/service/health.ts +5 -89
  362. package/src/service/launchd.ts +27 -7
  363. package/src/service/logging.ts +72 -9
  364. package/src/service/repair.ts +2 -1
  365. package/src/service/state.ts +7 -8
  366. package/src/service/systemd.ts +1 -1
  367. package/src/service/windows-guarded-manager.ts +2 -1
  368. package/src/service/windows-ops.ts +1 -1
  369. package/src/service/windows-scheduler.ts +2 -7
  370. package/src/service/windows-taskxml.ts +2 -2
  371. package/src/service/windows-wscript.ts +14 -0
  372. package/src/service.ts +4 -2
  373. package/src/sidecar/auth.ts +2 -0
  374. package/src/types/config.ts +64 -3
  375. package/src/types/opencode-go.ts +61 -0
  376. package/src/types/opencode-zen.ts +35 -0
  377. package/src/types/provider.ts +32 -6
  378. package/src/types/request.ts +31 -2
  379. package/src/types/wire.ts +31 -37
  380. package/src/types.ts +2 -0
  381. package/src/update/npm-invocation.mjs +12 -2
  382. package/src/usage/cost.ts +48 -12
  383. package/src/usage/expected-prices.ts +3 -0
  384. package/src/usage/ledger-retention.ts +2 -1
  385. package/src/usage/ledger-scanner.ts +6 -5
  386. package/src/usage/log.ts +17 -8
  387. package/src/usage/summary.ts +30 -11
  388. package/src/usage/telemetry-contract.ts +1 -0
  389. package/src/usage/user-cost-overlays.ts +14 -19
  390. package/src/usage/user-cost-validation.ts +38 -0
  391. package/src/vision/eligibility.ts +13 -0
  392. package/src/web-search/anthropic-history.ts +59 -0
  393. package/src/web-search/loop.ts +236 -73
  394. package/src/web-search/progress-stream.ts +15 -2
  395. package/src/web-search/run-turn-loop.ts +9 -1
  396. package/gui/dist/assets/App-BjlttVLy.js +0 -58
  397. package/gui/dist/assets/App-CSJvmjNs.css +0 -1
  398. package/gui/dist/assets/index-BB6uK6tU.css +0 -1
  399. package/gui/dist/assets/index-BMssP4k3.js +0 -94
  400. package/gui/dist/assets/quota-summary-D8M1bc_R.js +0 -1
@@ -1,4 +1,6 @@
1
+ import { truncationReasonFor } from "../../responses/truncated-stop-reason";
1
2
  import { normalizeRoutedAgentMessages } from "../routed-agent-messages";
3
+ import { frameCompactionHistory } from "../../responses/compaction-history-framing";
2
4
  import { nameRoutedIdentity, repairIdentityInResponsesBody, stripRoutedIdentity } from "../identity";
3
5
  import { stripBracketedModelSuffix } from "../openai-chat";
4
6
  import { normalizeOpenCodeGoAdditionalTools } from "../opencode-go-additional-tools";
@@ -7,7 +9,7 @@ import { applyGithubCopilotContextTier } from "../../providers/github-copilot-co
7
9
  import { Buffer } from "node:buffer";
8
10
  import type { IncomingMeta, ProviderAdapter } from "../base";
9
11
  import { namespacedToolName, type AdapterEvent, type OcxParsedRequest, type OcxProviderConfig, type OcxUsage, type TierDecision } from "../../types";
10
- import { applyCodexRoutingHint, CODEX_RESPONSES_LITE_HEADER, CODEX_ROUTING_HINT_HEADER } from "../../codex/forward-transport-headers";
12
+ import { applyCodexRoutingHint, CODEX_RESPONSES_LITE_HEADER, CODEX_RESPONSES_LITE_METADATA_KEY, CODEX_ROUTING_HINT_HEADER } from "../../codex/forward-transport-headers";
11
13
  import { COMPACT_PROMPT, compactionItemToText, decodeCompactionSummary, isCompactionItemType, transferCompactionCiphertextLease } from "../../responses/compaction";
12
14
  import { decodeServerSentEvents } from "../../lib/sse-decoder";
13
15
  import {
@@ -15,6 +17,7 @@ import {
15
17
  destinationDecodesNativeCompactionBlob,
16
18
  isCanonicalOpenAiForwardProvider,
17
19
  isOpenAiOperatedResponsesDestination,
20
+ supportsNativeResponsesCompactionTrigger,
18
21
  } from "../../providers/openai-tiers";
19
22
  import type { TranslatorBudget } from "../../lib/translator-budget";
20
23
  import { rewriteRoutedCustomToolsForUpstream, validateFinalCustomToolCompatibility } from "../../responses/custom-tool-compat";
@@ -23,7 +26,8 @@ import { rewriteRoutedNamespaceToolsForUpstream } from "../../responses/namespac
23
26
  import { repairLegacyDottedToolCallNames } from "../../responses/legacy-dotted-tool-name-repair";
24
27
  import { preparePlaintextV2AgentMessages } from "../../responses/plaintext-v2-agent-messages";
25
28
  import { isMetaAiResponsesDestination, rewriteMuseToolNamesForUpstream } from "../../responses/muse-tool-name-alias";
26
- import { openaiResponsesUrl } from "../openai-responses-url";
29
+ import { buildResponsesTransport } from "./transport";
30
+ export { FORWARD_HEADERS } from "./transport";
27
31
  import { normalizeResponsesCodeMode } from "../responses-code-mode";
28
32
  import { injectXaiResponsesXSearch, normalizeXaiResponsesWebSearch } from "../xai-web-search";
29
33
  import {
@@ -36,7 +40,7 @@ import {
36
40
  emittedFastWire,
37
41
  } from "../../providers/fastwire";
38
42
  import { dropResponsesReasoningInputItems, mapRoutedResponsesReasoningEffort, normalizeConfiguredReasoningSummaryDelivery, sanitizeReasoningInputContent, stripDisabledReasoningSummaries, stripDisabledVerbosity, stripNoneReasoningSummary, stripUnsupportedReasoningSummaryDelivery } from "./reasoning";
39
- import { scrubOcxCompactionItems, stripCanonicalOnlyToolFields, stripCanonicalOnlyTopLevelFields, stripInternalChatMessageMetadataPassthrough, stripInvalidItemIds, stripItemIdsWhenUnstored, stripRejectedSamplingParams } from "./request-strips";
43
+ import { scrubOcxCompactionItems, stripCanonicalOnlyToolFields, stripCanonicalOnlyTopLevelFields, stripInternalChatMessageMetadataPassthrough, stripInvalidItemIds, stripItemIdsWhenUnstored, stripProviderInputItemMetadata, stripRejectedSamplingParams } from "./request-strips";
40
44
  import { notePlaintextReasoningDropped } from "./plaintext-reasoning-notice";
41
45
  import { stripCanonicalForwardPromptCacheOptions, stripDeprecatedPromptCacheRetention } from "./prompt-cache";
42
46
  import { isPlainObject } from "./internal";
@@ -47,7 +51,7 @@ import { applyTierDecisionToResponsesBody, normalizeCanonicalForwardContinuation
47
51
  import { normalizeImageGenClientTools, preferConfiguredHostedTools } from "./image-gen";
48
52
  import { stripMuseSparkUnsupportedWebSearchFields, stripOpenAiOnlyWebSearchFields } from "./web-search";
49
53
  import { observeOutbound } from "../../usage/cache-diagnostic";
50
- import { normalizeForwardedClientHeaderName } from "../../lib/provider-client-headers";
54
+
51
55
  import { normalizeMuseToolChoice } from "./muse-tool-choice";
52
56
  import { renderCompactionSearchHistoryItems } from "./compaction-search-history";
53
57
  import { stripKimiEmptyAssistantMessages } from "./empty-assistant";
@@ -65,55 +69,6 @@ export function requiresPlaintextReasoningReplay(provider: OcxProviderConfig): b
65
69
  && provider.requiresAdjacentResponsesToolResults === true;
66
70
  }
67
71
 
68
- // Headers relayed verbatim from the caller in OAuth-passthrough ("forward") mode.
69
- // Exported so the web-search sidecar reuses the exact same forwarded-auth set for its ChatGPT call.
70
- export const FORWARD_HEADERS = [
71
- "authorization",
72
- "chatgpt-account-id",
73
- "openai-beta",
74
- "originator",
75
- "session_id",
76
- "session-id",
77
- "thread-id",
78
- "x-client-request-id",
79
- "x-codex-beta-features",
80
- "x-codex-installation-id",
81
- "x-codex-parent-thread-id",
82
- "x-codex-turn-metadata",
83
- "x-codex-turn-state",
84
- "x-codex-window-id",
85
- "x-oai-attestation",
86
- "x-openai-subagent",
87
- "x-responsesapi-include-timing-metrics",
88
- CODEX_RESPONSES_LITE_HEADER,
89
- ];
90
-
91
- /** Preserve the caller fingerprint unless the provider explicitly owns that header. */
92
- function applyCallerUserAgentFallback(
93
- headers: Record<string, string>,
94
- incoming: IncomingMeta,
95
- ): void {
96
- if (Object.keys(headers).some(name => name.toLowerCase() === "user-agent")) return;
97
- const userAgent = incoming.headers.get("user-agent");
98
- if (userAgent) headers["User-Agent"] = userAgent;
99
- }
100
-
101
- /** Copy only explicitly opted-in caller metadata; provider-owned headers remain authoritative. */
102
- function applyConfiguredClientHeaderForwarding(
103
- headers: Record<string, string>,
104
- incoming: IncomingMeta,
105
- provider: OcxProviderConfig,
106
- ): void {
107
- if (!Array.isArray(provider.forwardClientHeaders)) return;
108
- for (const rawName of provider.forwardClientHeaders.slice(0, 64)) {
109
- const name = normalizeForwardedClientHeaderName(rawName);
110
- if (name === null) continue;
111
- if (Object.keys(headers).some(existing => existing.toLowerCase() === name)) continue;
112
- const value = incoming.headers.get(name);
113
- if (value) headers[name] = value;
114
- }
115
- }
116
-
117
72
  /** Replace every `input_image` part under a routed-compaction body with a short marker. */
118
73
  function stripInputImagesDeep(value: unknown): unknown {
119
74
  if (Array.isArray(value)) return value.map(stripInputImagesDeep);
@@ -126,10 +81,20 @@ function stripInputImagesDeep(value: unknown): unknown {
126
81
  return out;
127
82
  }
128
83
 
84
+ function codexResponsesLiteSelected(headers: Record<string, string>, body: unknown): boolean {
85
+ const lite = new Headers(headers).get(CODEX_RESPONSES_LITE_HEADER);
86
+ if (lite === "true" || lite === "false") return lite === "true";
87
+ // Match existing canonical WS preparation: explicit HTTP intent wins, otherwise native
88
+ // string-map frame metadata survives. This reads intent without rewriting the transport.
89
+ if (!isPlainObject(body) || !isPlainObject(body.client_metadata)) return false;
90
+ return Object.values(body.client_metadata).every(value => typeof value === "string")
91
+ && body.client_metadata[CODEX_RESPONSES_LITE_METADATA_KEY] === "true";
92
+ }
93
+
129
94
  /**
130
- * Rewrite a compaction turn for an upstream that does not speak Codex's private
131
- * `compaction_trigger` item: drop the trigger and the whole tool surface, and ask
132
- * for the handoff summary in plain terms instead (#422).
95
+ * Rewrite a compaction turn without native v2 capability, or a forced portable
96
+ * handoff: drop the trigger and the whole tool surface, and ask for the handoff
97
+ * summary in plain terms instead (#422). Native v1 capability is independent.
133
98
  *
134
99
  * The adapter builds from `parsed._rawBody`, so the summarizer prompt that
135
100
  * handleResponses() pushed onto `parsed.context` never reaches the wire — it has to
@@ -225,61 +190,7 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
225
190
 
226
191
  buildRequest(parsed: OcxParsedRequest, incoming: IncomingMeta) {
227
192
  const translatorBudget = incoming.translatorBudget;
228
- const headers: Record<string, string> = { "Content-Type": "application/json" };
229
- let url: string;
230
-
231
- if (provider.authMode === "forward") {
232
- const mayForwardCallerCredentials = isCanonicalOpenAiForwardProvider(provider);
233
- // OAuth passthrough: ChatGPT backend path is `${baseUrl}/responses` (no /v1).
234
- const baseUrl = mayForwardCallerCredentials
235
- ? CODEX_FORWARD_BASE_URL
236
- : provider.baseUrl.replace(/\/+$/, "");
237
- url = `${baseUrl}/responses`;
238
- if (provider.headers) Object.assign(headers, provider.headers); // static headers first…
239
- const runtimeProvider = provider as {
240
- _codexAccountOverride?: { accessToken: string; chatgptAccountId: string };
241
- _codexAccountRequired?: boolean;
242
- };
243
- if (
244
- mayForwardCallerCredentials
245
- && runtimeProvider._codexAccountRequired
246
- && !runtimeProvider._codexAccountOverride
247
- ) {
248
- throw new Error("Codex pool account auth is required but unavailable");
249
- }
250
- if (mayForwardCallerCredentials) {
251
- for (const h of FORWARD_HEADERS) {
252
- const v = incoming?.headers.get(h);
253
- if (v) {
254
- if (h === CODEX_RESPONSES_LITE_HEADER) {
255
- for (const name of Object.keys(headers)) {
256
- if (name.toLowerCase() === h) delete headers[name];
257
- }
258
- }
259
- headers[h] = v; // …so genuine forwarded fields win.
260
- }
261
- }
262
- }
263
- const override = runtimeProvider._codexAccountOverride;
264
- if (override && mayForwardCallerCredentials) {
265
- headers["authorization"] = `Bearer ${override.accessToken}`;
266
- headers["chatgpt-account-id"] = override.chatgptAccountId;
267
- }
268
- } else {
269
- if (provider.responsesPath === undefined) {
270
- url = openaiResponsesUrl(provider.baseUrl);
271
- } else {
272
- const base = provider.baseUrl.replace(/\/$/, "");
273
- url = `${base}${provider.responsesPath}`;
274
- }
275
- if (provider.apiKey) headers["Authorization"] = `Bearer ${provider.apiKey}`;
276
- if (provider.headers) Object.assign(headers, provider.headers);
277
- }
278
- // Some Responses-compatible gateways select their Codex compatibility path from the real
279
- // client fingerprint. Additional caller metadata is opt-in; static provider headers remain
280
- // authoritative in either auth mode.
281
- applyConfiguredClientHeaderForwarding(headers, incoming, provider);
282
- applyCallerUserAgentFallback(headers, incoming);
193
+ const { url, headers } = buildResponsesTransport(provider, incoming);
283
194
 
284
195
  const forward = provider.authMode === "forward";
285
196
  let convertedRoutedCustomToolNames: Set<string> | undefined;
@@ -398,22 +309,27 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
398
309
  // canonical-destination split below, because the reported failure was a side chat on a plain
399
310
  // OpenAI model inheriting history a routed provider had damaged.
400
311
  outBody = repairLegacyDottedToolCallNames(outBody);
312
+ // Shared HTTP/WS outbound projection, including concrete combo/fallback destinations.
313
+ if (isOpenAiOperatedResponsesDestination(provider)) {
314
+ outBody = stripProviderInputItemMetadata(outBody);
315
+ }
401
316
  if (!isCanonicalOpenAiForwardProvider(provider)) {
402
317
  if (provider.preserveResponsesMessageMetadata !== true) {
403
318
  outBody = stripInternalChatMessageMetadataPassthrough(outBody);
404
319
  }
405
320
  // The same class of private field, one level up, but keyed on the DESTINATION rather than
406
321
  // on the canonical surface alone. `src/server/responses/compact.ts` spreads the caller's
407
- // raw body into the native `/responses/compact` request without passing through this
408
- // adapter, and that endpoint is offered only to OpenAI-operated destinations
409
- // (supportsNativeResponsesCompactEndpoint). Stripping on the canonical predicate here
410
- // would make the two paths disagree for `openai-apikey`; stripping on the destination
411
- // keeps every OpenAI-operated route byte-identical and removes the field exactly where it
412
- // is known to break, which is a gateway this proxy does not operate.
322
+ // raw body into native `/responses/compact` without this adapter, and applies the same
323
+ // destination boundary for opted-in custom gateways. Native endpoint capability does not
324
+ // grant OpenAI-private fields. Stripping on the canonical predicate here would make the
325
+ // two paths disagree for `openai-apikey`; stripping on the destination keeps every
326
+ // OpenAI-operated route byte-identical and removes the field exactly where it is known
327
+ // to break, which is a gateway this proxy does not operate.
413
328
  //
414
329
  // Placed before the routed compaction body is built and before serialization, so the HTTP,
415
330
  // routed-compaction and WebSocket outbounds are all covered by this one call.
416
331
  if (!isOpenAiOperatedResponsesDestination(provider)) {
332
+ if (parsed._compactionRequest !== true && isPlainObject(outBody)) outBody = frameCompactionHistory(outBody);
417
333
  outBody = stripCanonicalOnlyTopLevelFields(outBody);
418
334
  }
419
335
  outBody = promoteClientLoadedTools(outBody);
@@ -466,13 +382,13 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
466
382
  if (!forward) {
467
383
  outBody = normalizeOpenCodeGoAdditionalTools(outBody, url, parsed._replayPrefixLen);
468
384
  }
469
- // Same predicate as the routedCompaction gate in handleResponses(): an authMode check would
470
- // let a noncanonical custom forward provider skip this rewrite while the server still routes
471
- // it as a summarizer turn (#422). The compaction body build removes the tool surface and must
385
+ // Same native-v2/portable predicate as the routedCompaction gate in handleResponses():
386
+ // neither forward auth nor native v1 endpoint support implies trigger support. The
387
+ // compaction body build removes the tool surface and must
472
388
  // therefore be the last routed transform that may depend on those declarations. Structural
473
389
  // sanitizers below can still run after it.
474
390
  outBody = normalizeResponsesCodeMode(outBody, parsed, provider);
475
- if (parsed._compactionRequest === true && (!isCanonicalOpenAiForwardProvider(provider) || parsed._portableCompaction === true)) {
391
+ if (parsed._compactionRequest === true && (!supportsNativeResponsesCompactionTrigger(provider) || parsed._portableCompaction === true)) {
476
392
  outBody = buildRoutedCompactionBody(outBody, new Headers(headers).get(CODEX_RESPONSES_LITE_HEADER) === "true");
477
393
  }
478
394
  // Run after routed compaction so nested input_image parts are replaced before a malformed
@@ -488,6 +404,8 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
488
404
  }
489
405
  }
490
406
  const threadServingIdentityChanged = parsed._stripReasoningEncryptedContent === true;
407
+ const stripNativeCompaction = parsed._stripNativeCompactionEncryptedContent === true
408
+ || threadServingIdentityChanged;
491
409
  // Providers with the strict plaintext tool-continuation contract cannot consume any
492
410
  // encrypted reasoning blob, including one whose provenance is unknown. Combo routing
493
411
  // separately refuses a proven cross-route replay when no plaintext exists; this final
@@ -500,7 +418,7 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
500
418
  scrubOcxCompactionItems(
501
419
  outBody,
502
420
  destinationDecodesNativeCompactionBlob(provider),
503
- threadServingIdentityChanged,
421
+ stripNativeCompaction,
504
422
  parsed._compactionRequest !== true,
505
423
  ),
506
424
  {
@@ -574,6 +492,17 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
574
492
  // here, on the serialized body, not on the parsed selector. One place covers both the
575
493
  // HTTP and the WebSocket outbound, because the WS path transports this same request
576
494
  // instead of rebuilding it.
495
+ // Opt-in Codex Lite history policy: replay remaining hosted actions as untrusted metadata,
496
+ // after bridge restoration/tool validation. Tool declarations and other input stay intact.
497
+ if (provider.projectResponsesLiteHostedSearchHistory === true && url === `${CODEX_FORWARD_BASE_URL}/responses`
498
+ && !parsed._compactionRequest && !parsed._portableCompaction
499
+ && codexResponsesLiteSelected(headers, finalBody)
500
+ && isPlainObject(finalBody) && Array.isArray(finalBody.input)
501
+ && finalBody.input.some(item => isPlainObject(item) && item.type === "web_search_call")) {
502
+ const referencedIds = new Set(finalBody.input.flatMap(item => isPlainObject(item)
503
+ && item.type === "item_reference" && typeof item.id === "string" ? [item.id] : []));
504
+ finalBody = { ...finalBody, input: renderCompactionSearchHistoryItems(finalBody.input, referencedIds) };
505
+ }
577
506
  observeOutbound(parsed._rawBody, finalBody, headers);
578
507
  if (!isCanonicalOpenAiForwardProvider(provider)) {
579
508
  validateFinalCustomToolCompatibility(finalBody, provider.supportsResponsesCustomTools);
@@ -656,9 +585,11 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
656
585
  case "error":
657
586
  yield { type: "error", message: responsesErrorMessage(payload.response ?? payload) };
658
587
  return;
659
- case "response.incomplete":
660
- yield { type: "incomplete", reason: responsesErrorMessage(payload.response ?? payload) };
588
+ case "response.incomplete": {
589
+ const reason = responsesErrorMessage(payload.response ?? payload);
590
+ yield { type: "incomplete", reason: truncationReasonFor(reason) ?? reason };
661
591
  return;
592
+ }
662
593
  case "response.completed":
663
594
  {
664
595
  completedSeen = true;
@@ -751,7 +682,8 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
751
682
  return [{ type: "error", message: responsesErrorMessage(payload) }];
752
683
  }
753
684
  if (payload.status === "incomplete") {
754
- return [{ type: "incomplete", reason: responsesErrorMessage(payload) }];
685
+ const reason = responsesErrorMessage(payload);
686
+ return [{ type: "incomplete", reason: truncationReasonFor(reason) ?? reason }];
755
687
  }
756
688
  const usage = usageFromResponsesPayload(payload);
757
689
  const output = Array.isArray(payload.output) ? payload.output : [];
@@ -1,3 +1,4 @@
1
+ import { expandRetainedCompactionInput } from "../../responses/retained-compaction";
1
2
  import { createHash } from "node:crypto";
2
3
  import { COMPACT_PROMPT, appendCheckpointContinuationNote, compactionItemToText, decodeCompactionSummary, isCompactionItemType } from "../../responses/compaction";
3
4
  import { debugProviderDiagnostic } from "../../lib/debug";
@@ -6,6 +7,26 @@ import { isPlainObject } from "./internal";
6
7
  import { activateDeferredTool } from "./tool-schema";
7
8
  import { stripOpenAiOnlyWebSearchFields } from "./web-search";
8
9
 
10
+ /**
11
+ * Provider-opaque replay metadata is client history, not an OpenAI input-item field (#7111).
12
+ * The caller gates this on OpenAI-operated destinations; arbitrary gateways may accept it.
13
+ * Strip only the known extension, never an item-field allowlist that can erase newer wire fields
14
+ * or nested tool data. Copy on write so a later Google route can still replay its signature.
15
+ */
16
+ export function stripProviderInputItemMetadata(body: unknown): unknown {
17
+ if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
18
+ let input: unknown[] | undefined;
19
+ for (let index = 0; index < body.input.length; index += 1) {
20
+ const item = body.input[index];
21
+ if (!isPlainObject(item) || !Object.hasOwn(item, "extra_content")) continue;
22
+ const next = { ...item };
23
+ delete next.extra_content;
24
+ input ??= [...body.input];
25
+ input[index] = next;
26
+ }
27
+ return input ? { ...body, input } : body;
28
+ }
29
+
9
30
  export function stripInvalidItemIds(body: unknown): unknown {
10
31
  if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
11
32
 
@@ -246,7 +267,7 @@ export function stripItemIdsWhenUnstored(
246
267
  * Normalize replayed compaction items for the destination backend.
247
268
  *
248
269
  * A compaction item carries an `encrypted_content` blob the client replays verbatim on every later
249
- * turn, and only the backend that minted it can decode it. Proxy-minted `ocx1:` envelopes are
270
+ * turn, and only the backend that minted it can decode it. Proxy-minted `ocx1:` and structured `ocx2:` envelopes are
250
271
  * transparent base64 rather than encryption, so no upstream can read them and they always become
251
272
  * plain user messages. Native blobs have multiple possible minters, so a destination's ability to
252
273
  * decode its own blobs does not make a blob from a previous serving identity portable. On a known
@@ -264,9 +285,10 @@ export function scrubOcxCompactionItems(
264
285
  ): unknown {
265
286
  if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
266
287
 
267
- let changed = false;
288
+ const expanded = expandRetainedCompactionInput(body.input) as unknown[];
289
+ let changed = expanded !== body.input;
268
290
  let decodedCheckpoint = false;
269
- const input = body.input.map(item => {
291
+ const input = expanded.map(item => {
270
292
  if (!isPlainObject(item) || !isCompactionItemType(item.type)) return item;
271
293
  const encrypted = typeof item.encrypted_content === "string" ? item.encrypted_content : undefined;
272
294
  if (encrypted === undefined) return item;
@@ -0,0 +1,81 @@
1
+ import type { IncomingMeta } from "../base";
2
+ import type { OcxProviderConfig } from "../../types";
3
+ import { CODEX_RESPONSES_LITE_HEADER } from "../../codex/forward-transport-headers";
4
+ import { CODEX_FORWARD_BASE_URL, isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers-destination";
5
+ import { normalizeForwardedClientHeaderName } from "../../lib/provider-client-headers";
6
+ import { openaiResponsesUrl } from "../openai-responses-url";
7
+
8
+ // Caller credentials are forwarded only to the canonical Codex destination, never by capability.
9
+ export const FORWARD_HEADERS = [
10
+ "authorization", "chatgpt-account-id", "openai-beta", "originator", "session_id", "session-id",
11
+ "thread-id", "x-client-request-id", "x-codex-beta-features", "x-codex-installation-id",
12
+ "x-codex-parent-thread-id", "x-codex-turn-metadata", "x-codex-turn-state", "x-codex-window-id",
13
+ "x-oai-attestation", "x-openai-subagent", "x-responsesapi-include-timing-metrics", CODEX_RESPONSES_LITE_HEADER,
14
+ ];
15
+
16
+ /** Carry permitted client metadata across an internal compact handoff, never credentials. */
17
+ export function copyResponsesClientMetadata(target: Headers, incoming: Headers, provider: OcxProviderConfig): void {
18
+ const configured = Array.isArray(provider.forwardClientHeaders) ? provider.forwardClientHeaders.slice(0, 64) : [];
19
+ for (const rawName of ["user-agent", ...configured]) {
20
+ const name = normalizeForwardedClientHeaderName(rawName);
21
+ const value = name === null ? null : incoming.get(name);
22
+ if (name !== null && value && !target.has(name)) target.set(name, value);
23
+ }
24
+ }
25
+
26
+ /** Shared Responses URL/auth/header construction; capabilities never grant auth authority. */
27
+ export function buildResponsesTransport(provider: OcxProviderConfig, incoming: Pick<IncomingMeta, "headers">): {
28
+ url: string; headers: Record<string, string>;
29
+ } {
30
+ const headers: Record<string, string> = { "Content-Type": "application/json" };
31
+ let url: string;
32
+ if (provider.authMode === "forward") {
33
+ const mayForwardCallerCredentials = isCanonicalOpenAiForwardProvider(provider);
34
+ const baseUrl = mayForwardCallerCredentials ? CODEX_FORWARD_BASE_URL : provider.baseUrl.replace(/\/+$/, "");
35
+ url = `${baseUrl}/responses`;
36
+ if (provider.headers) Object.assign(headers, provider.headers);
37
+ const runtimeProvider = provider as {
38
+ _codexAccountOverride?: { accessToken: string; chatgptAccountId: string };
39
+ _codexAccountRequired?: boolean;
40
+ };
41
+ if (mayForwardCallerCredentials && runtimeProvider._codexAccountRequired && !runtimeProvider._codexAccountOverride) {
42
+ throw new Error("Codex pool account auth is required but unavailable");
43
+ }
44
+ if (mayForwardCallerCredentials) {
45
+ for (const name of FORWARD_HEADERS) {
46
+ const value = incoming.headers.get(name);
47
+ if (!value) continue;
48
+ if (name === CODEX_RESPONSES_LITE_HEADER) {
49
+ for (const existing of Object.keys(headers)) {
50
+ if (existing.toLowerCase() === name) delete headers[existing];
51
+ }
52
+ }
53
+ headers[name] = value;
54
+ }
55
+ }
56
+ const override = runtimeProvider._codexAccountOverride;
57
+ if (override && mayForwardCallerCredentials) {
58
+ headers.authorization = `Bearer ${override.accessToken}`;
59
+ headers["chatgpt-account-id"] = override.chatgptAccountId;
60
+ }
61
+ } else {
62
+ url = provider.responsesPath === undefined
63
+ ? openaiResponsesUrl(provider.baseUrl)
64
+ : `${provider.baseUrl.replace(/\/$/, "")}${provider.responsesPath}`;
65
+ if (provider.apiKey) headers.Authorization = `Bearer ${provider.apiKey}`;
66
+ if (provider.headers) Object.assign(headers, provider.headers);
67
+ }
68
+ if (Array.isArray(provider.forwardClientHeaders)) {
69
+ for (const rawName of provider.forwardClientHeaders.slice(0, 64)) {
70
+ const name = normalizeForwardedClientHeaderName(rawName);
71
+ if (name === null || Object.keys(headers).some(existing => existing.toLowerCase() === name)) continue;
72
+ const value = incoming.headers.get(name);
73
+ if (value) headers[name] = value;
74
+ }
75
+ }
76
+ if (!Object.keys(headers).some(name => name.toLowerCase() === "user-agent")) {
77
+ const userAgent = incoming.headers.get("user-agent");
78
+ if (userAgent) headers["User-Agent"] = userAgent;
79
+ }
80
+ return { url, headers };
81
+ }
@@ -52,8 +52,10 @@ const QODER_MCP_SERVER_NAME = "opencodex";
52
52
  const QODER_MCP_SERVER_PATH = fileURLToPath(new URL("../coding-agent/mcp-server.ts", import.meta.url));
53
53
 
54
54
  export function buildQoderChildEnv(profile: QoderProfile, apiKey: string, systemPrompt?: string): Record<string, string> {
55
- const promptEnv = profile.region === "cn" ? "QODERCN_APPEND_SYSTEM_PROMPT" : "QODER_APPEND_SYSTEM_PROMPT";
56
- return { ...baseScopedEnv(), NO_COLOR: "1", [profile.tokenEnv]: apiKey,
55
+ // Each build reads its own brand prefix with no fallback: qodercn ignores QODER_EXPOSE_TOKEN_USAGE.
56
+ const envPrefix = profile.region === "cn" ? "QODERCN_" : "QODER_";
57
+ const promptEnv = `${envPrefix}APPEND_SYSTEM_PROMPT`;
58
+ return { ...baseScopedEnv(), NO_COLOR: "1", [`${envPrefix}EXPOSE_TOKEN_USAGE`]: "true", [profile.tokenEnv]: apiKey,
57
59
  ...(systemPrompt ? { [promptEnv]: systemPrompt } : {}) };
58
60
  }
59
61
 
@@ -1,4 +1,5 @@
1
1
  import { createAnthropicAdapter } from "./anthropic";
2
+ import { createDshAccountAdapter } from "./dsh-account";
2
3
  import { createAzureAdapter } from "./azure";
3
4
  import type { ProviderAdapter } from "./base";
4
5
  import { createClaudeCliAdapter } from "./claude-cli/adapter";
@@ -154,6 +155,10 @@ export const ADAPTER_REGISTRY = {
154
155
  contractParent: "codebuddy",
155
156
  create: (provider: OcxProviderConfig, _context: AdapterFactoryContext) => createClaudeCliAdapter(provider),
156
157
  },
158
+ "dsh-account": {
159
+ contractParent: "anthropic",
160
+ create: (provider: OcxProviderConfig, context: AdapterFactoryContext) => createDshAccountAdapter(provider, context),
161
+ },
157
162
  } as const satisfies Record<string, AdapterDefinition>;
158
163
 
159
164
  export type AdapterId = keyof typeof ADAPTER_REGISTRY;
@@ -53,7 +53,9 @@ function delegateProvider(provider: OcxProviderConfig, zedProvider: ZedProvider)
53
53
  return { ...common, adapter: "google", baseUrl: "https://generativelanguage.googleapis.com", googleMode: "ai-studio" };
54
54
  }
55
55
  if (zedProvider === "open_ai") {
56
- return { ...common, adapter: "openai-responses", baseUrl: "https://api.openai.com/v1" };
56
+ // The builder targets Zed's /completions envelope. Keep its destination set to Zed as well,
57
+ // so OpenAI-private Responses fields are stripped instead of being sent through to Zed.
58
+ return { ...common, adapter: "openai-responses", baseUrl: provider.baseUrl };
57
59
  }
58
60
  return { ...common, adapter: "openai-chat", baseUrl: "https://api.x.ai/v1" };
59
61
  }
@@ -300,6 +302,29 @@ export function createZedAdapter(provider: OcxProviderConfig): ProviderAdapter {
300
302
  }
301
303
  }
302
304
  if (!isRecord(providerRequest)) throw new Error("Zed delegate produced a non-object request body");
305
+ if (zedProvider === "open_ai" && Array.isArray(providerRequest.input)) {
306
+ // Zed's OpenAI Responses parser accepts user/assistant/system/tool roles but rejects
307
+ // OpenAI's newer developer role. System is the closest supported role for instructions.
308
+ providerRequest.input = providerRequest.input.map(item =>
309
+ isRecord(item) && item.type === "message" && item.role === "developer"
310
+ ? { ...item, role: "system" }
311
+ : item,
312
+ );
313
+ }
314
+ if (zedProvider === "open_ai" && Array.isArray(providerRequest.tools)) {
315
+ // Zed's Responses decoder supports function/custom tools, not Codex's built-in web_search.
316
+ const supported = (tool: unknown) => isRecord(tool) && (tool.type === "function" || tool.type === "custom");
317
+ const tools = providerRequest.tools.filter(supported);
318
+ providerRequest.tools = tools;
319
+ const choice = providerRequest.tool_choice;
320
+ if (isRecord(choice) && choice.type === "allowed_tools" && Array.isArray(choice.tools)) {
321
+ const allowed = choice.tools.filter(supported);
322
+ providerRequest.tool_choice = allowed.length ? { ...choice, tools: allowed } : "none";
323
+ } else if ((isRecord(choice) && !supported(choice)) || (choice === "required" && tools.length === 0)) {
324
+ // Match the shared hosted-tool policy when no supported selection remains.
325
+ providerRequest.tool_choice = "none";
326
+ }
327
+ }
303
328
  delegate = { provider: zedProvider, adapter: selected };
304
329
  const requestUrl = `${provider.baseUrl.replace(/\/+$/, "")}/completions`;
305
330
  return {
@@ -0,0 +1,55 @@
1
+ import type { OcxConfig } from "../types";
2
+ import { sanitizeLogMetadataString } from "../lib/redact";
3
+
4
+ export type AdvisorTrigger = "manual" | "preflight" | "repair_failure";
5
+ export interface AdvisorActivity {
6
+ id: string;
7
+ startedAt: number;
8
+ trigger: AdvisorTrigger;
9
+ workerModel: string;
10
+ advisorModel: string;
11
+ requestedEffort: string;
12
+ /** Adapter serialization evidence, not a claim about the provider's hidden reasoning. */
13
+ effectiveEffort: string | null;
14
+ status: "running" | "ok" | "failed" | "cancelled" | "blocked";
15
+ durationMs: number | null;
16
+ usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number };
17
+ blockReason?: "consent" | "settings" | "budget" | "limit" | "duplicate";
18
+ }
19
+
20
+ /** Metadata only. No prompts, advice, commands, task/thread ids, errors, or hidden reasoning. */
21
+ export function createAdvisorActivityStore() {
22
+ const entries: AdvisorActivity[] = [];
23
+ const retain = (now: number) => {
24
+ while (entries.length && (entries.length > 100 || entries[0]!.startedAt < now - 86_400_000)) entries.shift();
25
+ };
26
+ return {
27
+ start(input: Pick<AdvisorActivity, "trigger" | "workerModel" | "advisorModel" | "requestedEffort">, now = Date.now()) {
28
+ const entry: AdvisorActivity = {
29
+ id: crypto.randomUUID(), startedAt: now, ...input,
30
+ workerModel: sanitizeLogMetadataString(input.workerModel, 200) ?? "unknown",
31
+ advisorModel: sanitizeLogMetadataString(input.advisorModel, 200) ?? "unknown",
32
+ effectiveEffort: null, status: "running", durationMs: null,
33
+ };
34
+ entries.push(entry);
35
+ retain(now);
36
+ return entry.id;
37
+ },
38
+ finish(id: string, outcome: Pick<AdvisorActivity, "status" | "durationMs"> & Partial<Pick<AdvisorActivity, "usage" | "effectiveEffort" | "blockReason">>) {
39
+ const entry = entries.find(item => item.id === id);
40
+ if (entry) Object.assign(entry, outcome);
41
+ },
42
+ snapshot(now = Date.now()): AdvisorActivity[] {
43
+ retain(now);
44
+ return entries.toReversed().map(entry => ({ ...entry, ...(entry.usage ? { usage: { ...entry.usage } } : {}) }));
45
+ },
46
+ };
47
+ }
48
+
49
+ export type AdvisorActivityStore = ReturnType<typeof createAdvisorActivityStore>;
50
+ const stores = new WeakMap<Pick<OcxConfig, "advisor">, AdvisorActivityStore>();
51
+ export function advisorActivityStore(config: Pick<OcxConfig, "advisor">): AdvisorActivityStore {
52
+ let store = stores.get(config);
53
+ if (!store) { store = createAdvisorActivityStore(); stores.set(config, store); }
54
+ return store;
55
+ }
@@ -0,0 +1,62 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { chmodSync, closeSync, lstatSync, mkdirSync, openSync } from "node:fs";
3
+ import { dirname, join } from "node:path";
4
+ import { getConfigDir } from "../config/paths";
5
+ import { recordOwnedConfigPath } from "../lib/config-ownership";
6
+ import { assertNotRealHomeUnderTest } from "../lib/test-home-guard";
7
+ import { hardenSecretDir, hardenSecretPath } from "../lib/windows-secret-acl";
8
+
9
+ export type AdvisorBudgetReservation = { ok: true; used: number } | { ok: false; reason: "exhausted" | "unavailable" };
10
+ export interface AdvisorTaskBudget {
11
+ reserve(task: string, limit: number, now: number): AdvisorBudgetReservation;
12
+ }
13
+
14
+ /**
15
+ * Reserve BEFORE dispatch. Failed/disconnected calls may still cost money, so reservations
16
+ * are never refunded. Only SHA-256 task digests, counts and expiry times reach disk. Opening
17
+ * lazily and closing before returning also keeps disabled installs and shutdown timer-free.
18
+ */
19
+ export function createAdvisorTaskBudget(path?: string): AdvisorTaskBudget {
20
+ return {
21
+ reserve(task, limit, now) {
22
+ let db: Database | undefined;
23
+ try {
24
+ const key = new Bun.CryptoHasher("sha256").update(`advisor-budget\0${task}`).digest("hex");
25
+ const file = path ?? join(getConfigDir(), "advisor-budget.sqlite");
26
+ const dir = dirname(file);
27
+ assertNotRealHomeUnderTest(dir);
28
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
29
+ if (!lstatSync(dir).isDirectory() || lstatSync(dir).isSymbolicLink()) throw new Error("invalid directory");
30
+ hardenSecretDir(dir, { required: true });
31
+ if (!path) {
32
+ recordOwnedConfigPath(dir, file);
33
+ recordOwnedConfigPath(dir, `${file}-journal`);
34
+ }
35
+ try { closeSync(openSync(file, "wx", 0o600)); }
36
+ catch (error) { if ((error as { code?: string }).code !== "EEXIST") throw error; }
37
+ const stat = lstatSync(file);
38
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1) throw new Error("invalid file");
39
+ if (process.platform !== "win32") chmodSync(file, 0o600);
40
+ hardenSecretPath(file, { required: true });
41
+ db = new Database(file);
42
+ db.exec("PRAGMA busy_timeout = 0; CREATE TABLE IF NOT EXISTS tasks (task TEXT PRIMARY KEY, used INTEGER NOT NULL, expires INTEGER NOT NULL)");
43
+ const connection = db;
44
+ return connection.transaction((): AdvisorBudgetReservation => {
45
+ connection.query("DELETE FROM tasks WHERE expires <= ?").run(now);
46
+ const current = connection.query<{ used: number }, [string]>("SELECT used FROM tasks WHERE task = ?").get(key);
47
+ if (current && current.used >= limit) return { ok: false, reason: "exhausted" };
48
+ // Never evict a live task to make room: that would reset its spending limit.
49
+ if (!current && connection.query<{ n: number }, []>("SELECT count(*) AS n FROM tasks").get()!.n >= 512) {
50
+ return { ok: false, reason: "unavailable" };
51
+ }
52
+ connection.query("INSERT INTO tasks VALUES (?, 1, ?) ON CONFLICT(task) DO UPDATE SET used = used + 1")
53
+ .run(key, now + 86_400_000);
54
+ return { ok: true, used: (current?.used ?? 0) + 1 };
55
+ }).immediate();
56
+ } catch {
57
+ // An unavailable/corrupt/locked budget blocks the expert call, never the worker.
58
+ return { ok: false, reason: "unavailable" };
59
+ } finally { try { db?.close(); } catch { /* reservation outcome is authoritative */ } }
60
+ },
61
+ };
62
+ }