@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
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Request-time provider resolution: merge a saved config row with its registry entry.
3
+ *
4
+ * This lives outside `src/router.ts` because `providers/api-key-selection.ts` needs exactly
5
+ * one function from the router, and importing the router for it dragged the whole routing
6
+ * graph into an import cycle. The router re-exports both symbols below so every existing
7
+ * import keeps working.
8
+ */
9
+ import type { OcxProviderConfig } from "../types";
10
+ import { providerUsesKeyAuthOverride, resolveProviderApiKey } from "./key-store";
11
+ import { GCP_CREDENTIAL_MARKER_PREFIX } from "../lib/gcp-adc";
12
+ import { captureProviderApiKeySelection } from "./api-key-selection-capture";
13
+ import { assertProviderDestinationAllowed } from "../lib/destination-policy";
14
+ import { redactSecretString, redactUrlForLog } from "../lib/redact";
15
+ import { PROVIDER_REGISTRY, registryEntryForProviderDestination } from "./registry";
16
+ import { applyDirectReasoningEffortContracts, hasLegacyClinePassReasoningEfforts } from "./derive";
17
+ import { cloneFastWire } from "./fastwire";
18
+ import { fastSwitchOff } from "./fast-opt-in";
19
+ import {
20
+ providerMatchesRegistryTransportWithStaticGuards,
21
+ providerSupportsLiveModelDiscovery,
22
+ } from "./static-model-discovery";
23
+ import { resolveModelPolicy } from "./resolved-model-policy";
24
+
25
+ /** Same endpoint modulo surrounding space and trailing slashes — matches `matchBaseUrlChoice`. */
26
+ function isSameEndpoint(a: string, b: string): boolean {
27
+ return a.trim().replace(/\/+$/, "") === b.trim().replace(/\/+$/, "");
28
+ }
29
+
30
+ /**
31
+ * Origin of a user-configured URL, with the path withheld.
32
+ *
33
+ * A configured `baseUrl` is user-controlled and its path may itself be the credential — an
34
+ * account-scoped route token such as `https://proxy.example/v1/8fK2mP7qR4nV6x` is opaque and
35
+ * high-entropy, so it matches none of the prefix patterns in `redactSecretString`. Pattern
36
+ * redaction cannot be trusted for this value, so no path segment is logged at all. `URL.origin`
37
+ * also excludes userinfo, query and fragment.
38
+ *
39
+ * `…/…` marks that a path was present without revealing it, so a reader can tell an origin-only
40
+ * config apart from one whose path was dropped.
41
+ */
42
+ function configuredOriginForLog(url: string): string {
43
+ try {
44
+ const parsed = new URL(url.trim());
45
+ // "null" is what URL.origin yields for non-special schemes; treat it as unusable.
46
+ if (!parsed.origin || parsed.origin === "null") return "(unloggable URL)";
47
+ const hasPath = parsed.pathname !== "" && parsed.pathname !== "/";
48
+ return hasPath ? `${parsed.origin}/…` : parsed.origin;
49
+ } catch {
50
+ return "(unparseable URL)";
51
+ }
52
+ }
53
+
54
+ // `routedProviderConfig` runs per request, so warn once per (provider, discarded, effective) triple.
55
+ // Keyed by the URLs too: editing config.json to a different wrong value warns again.
56
+ const discardedBaseUrlWarnings = new Set<string>();
57
+ let lastWarningReconciledGeneration = 0;
58
+
59
+ export function reconcileRouterWarningMemos(generation: number): number {
60
+ if (generation <= lastWarningReconciledGeneration) return 0;
61
+ const removed = discardedBaseUrlWarnings.size;
62
+ discardedBaseUrlWarnings.clear();
63
+ lastWarningReconciledGeneration = generation;
64
+ return removed;
65
+ }
66
+
67
+ /**
68
+ * A pinned registry entry — non-template `baseUrl`, no `allowBaseUrlOverride` — outranks a saved
69
+ * `baseUrl`. Dropping it silently is a footgun: requests go to an endpoint the user never
70
+ * configured, and a wrong-region or wrong-account URL then surfaces only as a 401 with nothing
71
+ * pointing back at the discarded setting.
72
+ *
73
+ * Warns rather than throws. The effective route is exactly what it was before, so a hard error
74
+ * here would break configs that route fine today (a stale `baseUrl` left over from an earlier
75
+ * provider is harmless whenever it names the same endpoint the registry pins).
76
+ */
77
+ function warnIfBaseUrlDiscarded(providerName: string, userBaseUrl: string, effectiveBaseUrl: string): void {
78
+ if (isSameEndpoint(userBaseUrl, effectiveBaseUrl)) return;
79
+ // Asymmetric on purpose. Past the guard above, `effectiveBaseUrl` is necessarily
80
+ // `registryEntry.baseUrl`: the caller passes the resolved URL, and whenever that resolution
81
+ // kept the user's value the two are equal and we have already returned. So the effective side
82
+ // is a constant from this repo's registry and safe to print in full — it is also the useful
83
+ // half, naming the endpoint requests will actually use. The configured side is untrusted.
84
+ const discarded = configuredOriginForLog(userBaseUrl);
85
+ const effective = redactSecretString(redactUrlForLog(effectiveBaseUrl));
86
+ // Key off the logged forms: no raw credential is retained for the process lifetime, and
87
+ // rotating a key embedded in the URL no longer re-warns about the same endpoint mismatch.
88
+ // Coarser than the raw URLs — two bad paths on one host warn once, which is the right grain.
89
+ const key = `${providerName} | ${discarded} | ${effective}`;
90
+ if (discardedBaseUrlWarnings.has(key)) return;
91
+ discardedBaseUrlWarnings.add(key);
92
+ console.warn(
93
+ // Routing is what this warning speaks for: an adapter may adjust the endpoint again
94
+ // downstream (kiro re-derives the region), so do not promise where the request lands.
95
+ `⚠️ config.json provider "${providerName}": configured baseUrl ${discarded} is ignored`
96
+ + ` because this provider's endpoint is fixed at ${effective}. A URL saved for a different`
97
+ + ` account or region is a common cause of 401s here — drop it, or use the provider whose endpoint matches.`,
98
+ );
99
+ }
100
+
101
+ function usableResolvedApiKey(apiKey: string | undefined): string | undefined {
102
+ // A `gcp-sa:` marker must pass through UNCHANGED: it resolves to `undefined` here (it is not a
103
+ // keychain/env ref, and the marker itself is not a literal), and replacing it would silently
104
+ // drop the marker from the routed config — the vertex adapter would then read
105
+ // `gcpCredentialMarkerAccount(undefined)` and fall back to host ADC instead of the configured
106
+ // service account. Return the marker verbatim so the adapter passes its account per request.
107
+ if (typeof apiKey === "string" && apiKey.startsWith(GCP_CREDENTIAL_MARKER_PREFIX)) return apiKey;
108
+ const resolved = resolveProviderApiKey(apiKey);
109
+ return typeof resolved === "string" && resolved.trim().length > 0 ? resolved : undefined;
110
+ }
111
+
112
+ export function routedProviderConfig(providerName: string, provider: OcxProviderConfig): OcxProviderConfig {
113
+ provider = { ...provider, _apiKeyAttempt: provider._apiKeyAttempt ?? captureProviderApiKeySelection(provider) };
114
+ const registryEntry = PROVIDER_REGISTRY.find(entry => entry.id === providerName);
115
+ if (!registryEntry || !providerMatchesRegistryTransportWithStaticGuards(providerName, provider)) {
116
+ assertProviderDestinationAllowed(providerName, provider);
117
+ // A row whose adapter no longer matches its registry entry still reaches the Responses
118
+ // adapter when one model opts in through `modelAdapters` — a Volcengine Coding Plan config
119
+ // saved on Chat, for instance. The replay-drop flag belongs to the DESTINATION rather than
120
+ // to the provider-wide wire, so it is filled here too; without it that continuation forwards
121
+ // the reasoning item the upstream answers 400 to. Destination matching refuses templated and
122
+ // overridable base URLs, so this cannot follow a retargeted row, and an explicit value wins.
123
+ const destination = registryEntryForProviderDestination(provider);
124
+ return {
125
+ ...provider,
126
+ apiKey: usableResolvedApiKey(provider.apiKey),
127
+ ...(provider.dropResponsesReasoningItems === undefined && destination?.dropResponsesReasoningItems !== undefined
128
+ ? { dropResponsesReasoningItems: destination.dropResponsesReasoningItems }
129
+ : {}),
130
+ };
131
+ }
132
+ const resolvedApiKey = usableResolvedApiKey(provider.apiKey);
133
+ const staticModelCatalog = !providerSupportsLiveModelDiscovery(providerName, provider);
134
+ const repairLegacyMimoFreeAuth = providerName === "mimo-free"
135
+ && staticModelCatalog
136
+ && (provider.authMode === undefined || provider.authMode === "local");
137
+ const explicitKeyOverride = providerUsesKeyAuthOverride(registryEntry, provider, resolvedApiKey);
138
+ const canonicalAuthMode = explicitKeyOverride
139
+ ? "key"
140
+ : repairLegacyMimoFreeAuth
141
+ ? "key"
142
+ : registryEntry.authKind === "forward" || registryEntry.authKind === "oauth"
143
+ ? registryEntry.authKind
144
+ : provider.authMode === "forward" ? undefined : provider.authMode;
145
+ const staticPolicy = resolveModelPolicy({
146
+ providerName,
147
+ modelId: "__provider_static__",
148
+ provider,
149
+ registryEntry,
150
+ transportMatchedRegistry: true,
151
+ ...(canonicalAuthMode ? { effectiveAuth: { authMode: canonicalAuthMode } } : {}),
152
+ }).provider;
153
+ const reasoningEffortMap = staticPolicy.reasoningEffortMap;
154
+ const modelReasoningEffortMap = staticPolicy.modelReasoningEffortMap;
155
+ const modelReasoningEfforts = staticPolicy.modelReasoningEfforts;
156
+ const modelDefaultReasoningEfforts = staticPolicy.modelDefaultReasoningEfforts;
157
+ const modelContextWindows = staticPolicy.modelContextWindows;
158
+ const modelInputModalities = staticPolicy.modelInputModalities;
159
+ // Registry static headers are documented as applying to every upstream request, so they are
160
+ // filled at resolve time rather than only at seed time: a config written before a header
161
+ // existed, or one carrying any header of its own, would otherwise never receive it. User
162
+ // headers win, matched case-insensitively so an override replaces rather than duplicates.
163
+ const headers = staticPolicy.headers;
164
+ const modelMaxInputTokens = staticPolicy.modelMaxInputTokens;
165
+ const modelMaxOutputTokens = staticPolicy.modelMaxOutputTokens;
166
+ const modelSupportsServiceTier = staticPolicy.modelSupportsServiceTier;
167
+ const modelSupportsVerbosity = staticPolicy.modelSupportsVerbosity;
168
+ const noVisionModels = staticPolicy.noVisionModels;
169
+ const noReasoningModels = staticPolicy.noReasoningModels;
170
+ const noTemperatureModels = staticPolicy.noTemperatureModels;
171
+ const noTopPModels = staticPolicy.noTopPModels;
172
+ const noStopModels = staticPolicy.noStopModels;
173
+ const noPenaltyModels = staticPolicy.noPenaltyModels;
174
+ const noJsonSchemaModels = staticPolicy.noJsonSchemaModels;
175
+ const autoToolChoiceOnlyModels = staticPolicy.autoToolChoiceOnlyModels;
176
+ const preserveReasoningContentModels = staticPolicy.preserveReasoningContentModels;
177
+ const requiresReasoningPlaceholderModels = staticPolicy.requiresReasoningPlaceholderModels;
178
+ const reasoningSplitModels = staticPolicy.reasoningSplitModels;
179
+ const inlineThinkTagModels = staticPolicy.inlineThinkTagModels;
180
+ const reasoningDetailsModels = staticPolicy.reasoningDetailsModels;
181
+ const thinkingToggleModels = staticPolicy.thinkingToggleModels;
182
+ const thinkingBudgetModels = staticPolicy.thinkingBudgetModels;
183
+ const registryBaseUrlIsTemplate = /\{[^}]*\}/.test(registryEntry.baseUrl);
184
+ const userBaseUrl = typeof provider.baseUrl === "string" ? provider.baseUrl.trim() : "";
185
+ const userBaseUrlIsResolved = userBaseUrl.length > 0 && !/\{[^}]*\}/.test(userBaseUrl);
186
+ if (registryEntry.allowBaseUrlOverride && !userBaseUrlIsResolved) {
187
+ throw new Error(`Invalid baseUrl for provider "${providerName}": expected a nonblank URL without unresolved placeholders`);
188
+ }
189
+ // Registry template URLs are presets; local/self-hosted entries opt in explicitly.
190
+ const baseUrl = (registryBaseUrlIsTemplate || registryEntry.allowBaseUrlOverride) && userBaseUrlIsResolved
191
+ ? userBaseUrl
192
+ : registryEntry.baseUrl;
193
+ if (userBaseUrlIsResolved) warnIfBaseUrlDiscarded(providerName, userBaseUrl, baseUrl);
194
+ assertProviderDestinationAllowed(providerName, { baseUrl, allowPrivateNetwork: provider.allowPrivateNetwork });
195
+
196
+ const resolved: OcxProviderConfig = {
197
+ ...provider,
198
+ adapter: registryEntry.adapter,
199
+ baseUrl,
200
+ ...(provider.responsesPath === undefined && registryEntry.responsesPath !== undefined
201
+ ? { responsesPath: registryEntry.responsesPath }
202
+ : {}),
203
+ ...(provider.chatCompletionsPath === undefined && registryEntry.chatCompletionsPath !== undefined
204
+ ? { chatCompletionsPath: registryEntry.chatCompletionsPath }
205
+ : {}),
206
+ ...(provider.requiresAdjacentResponsesToolResults === undefined
207
+ && registryEntry.requiresAdjacentResponsesToolResults !== undefined
208
+ ? { requiresAdjacentResponsesToolResults: registryEntry.requiresAdjacentResponsesToolResults }
209
+ : {}),
210
+ ...(provider.requiresPairedResponsesToolResults === undefined
211
+ && registryEntry.requiresPairedResponsesToolResults !== undefined
212
+ ? { requiresPairedResponsesToolResults: registryEntry.requiresPairedResponsesToolResults }
213
+ : {}),
214
+ ...(provider.annotateEmptyToolOutputs === undefined
215
+ && registryEntry.annotateEmptyToolOutputs !== undefined
216
+ ? { annotateEmptyToolOutputs: registryEntry.annotateEmptyToolOutputs }
217
+ : {}),
218
+ ...(provider.fastWire === undefined && registryEntry.fastWire !== undefined
219
+ ? {
220
+ fastWire: cloneFastWire(registryEntry.fastWire),
221
+ }
222
+ : {}),
223
+ ...(provider.supportsServiceTier === undefined && registryEntry.supportsServiceTier !== undefined
224
+ ? { supportsServiceTier: registryEntry.supportsServiceTier }
225
+ : {}),
226
+ // An off Fast switch is a provider-wide denial on the runtime provider, so a Fast policy
227
+ // resolved without the provider name still refuses (providerFastSwitchOff).
228
+ ...(fastSwitchOff(provider, registryEntry) ? { supportsServiceTier: false } : {}),
229
+ // Registry-only web-search capability: without this backfill a saved provider row reaches
230
+ // the Responses adapter with the flag `undefined`, so the capability gate added in #2262
231
+ // reads "unclassified" and forwards Codex's OpenAI-only `web_search` config fields. xAI
232
+ // rejects the whole request before inference ("Argument not supported:
233
+ // external_web_access"), which killed every routed Grok turn on the Responses lane.
234
+ // enrichProviderFromRegistry() already fills this, but the request path resolves through
235
+ // routedProviderConfig() and never called it.
236
+ ...(provider.supportsOpenAiWebSearchToolFields === undefined
237
+ && registryEntry.supportsOpenAiWebSearchToolFields !== undefined
238
+ ? { supportsOpenAiWebSearchToolFields: registryEntry.supportsOpenAiWebSearchToolFields }
239
+ : {}),
240
+ ...(provider.supportsResponsesCustomTools === undefined && registryEntry.supportsResponsesCustomTools !== undefined
241
+ ? { supportsResponsesCustomTools: registryEntry.supportsResponsesCustomTools }
242
+ : {}),
243
+ ...(provider.preserveResponsesReasoningContent === undefined && registryEntry.preserveResponsesReasoningContent !== undefined
244
+ ? { preserveResponsesReasoningContent: registryEntry.preserveResponsesReasoningContent }
245
+ : {}),
246
+ ...(provider.preserveResponsesInputItemIds === undefined && registryEntry.preserveResponsesInputItemIds !== undefined
247
+ ? { preserveResponsesInputItemIds: registryEntry.preserveResponsesInputItemIds }
248
+ : {}),
249
+ ...(provider.preserveResponsesMessageMetadata === undefined && registryEntry.preserveResponsesMessageMetadata !== undefined
250
+ ? { preserveResponsesMessageMetadata: registryEntry.preserveResponsesMessageMetadata }
251
+ : {}),
252
+ ...(provider.dropResponsesReasoningItems === undefined && registryEntry.dropResponsesReasoningItems !== undefined
253
+ ? { dropResponsesReasoningItems: registryEntry.dropResponsesReasoningItems }
254
+ : {}),
255
+ // The request path resolves through routedProviderConfig() and never calls
256
+ // enrichProviderFromRegistry(), so a saved provider row written before the
257
+ // registry learned this flag must be backfilled here or route.provider never
258
+ // carries it and the showThinkingSummary opt-in stays dead.
259
+ ...(provider.showThinkingSummary === undefined && registryEntry.showThinkingSummary !== undefined
260
+ ? { showThinkingSummary: registryEntry.showThinkingSummary }
261
+ : {}),
262
+ // Registry-only client-facing repair policy (#938): fill only when the
263
+ // saved provider has no explicit policy; clone so runtime never aliases
264
+ // the registry constant.
265
+ ...(provider.responsesItemIdRepair === undefined && registryEntry.responsesItemIdRepair
266
+ ? {
267
+ responsesItemIdRepair: {
268
+ ...(registryEntry.responsesItemIdRepair.message ? { message: [...registryEntry.responsesItemIdRepair.message] } : {}),
269
+ ...(registryEntry.responsesItemIdRepair.reasoning ? { reasoning: [...registryEntry.responsesItemIdRepair.reasoning] } : {}),
270
+ ...(registryEntry.responsesItemIdRepair.repairMissingTerminalIds !== undefined
271
+ ? { repairMissingTerminalIds: registryEntry.responsesItemIdRepair.repairMissingTerminalIds }
272
+ : {}),
273
+ ...(registryEntry.responsesItemIdRepair.repairInvalidIds !== undefined
274
+ ? { repairInvalidIds: registryEntry.responsesItemIdRepair.repairInvalidIds }
275
+ : {}),
276
+ },
277
+ }
278
+ : {}),
279
+ authMode: canonicalAuthMode,
280
+ apiKey: resolvedApiKey,
281
+ ...(staticModelCatalog ? { liveModels: false } : {}),
282
+ ...(headers ? { headers } : {}),
283
+ // Backfill the Google wire mode + Vertex project/location from the registry when the user
284
+ // config omits them, so a minimal `google-vertex`/`google-antigravity` entry still routes
285
+ // through the correct branch (CCA/Vertex) instead of falling back to AI Studio.
286
+ ...(provider.googleMode === undefined && registryEntry.googleMode !== undefined ? { googleMode: registryEntry.googleMode } : {}),
287
+ ...(provider.project === undefined && registryEntry.project !== undefined ? { project: registryEntry.project } : {}),
288
+ ...(provider.location === undefined && registryEntry.location !== undefined ? { location: registryEntry.location } : {}),
289
+ ...(provider.contextWindow === undefined && registryEntry.contextWindow !== undefined ? { contextWindow: registryEntry.contextWindow } : {}),
290
+ ...((provider.reasoningEfforts === undefined || hasLegacyClinePassReasoningEfforts(providerName, provider))
291
+ && registryEntry.reasoningEfforts !== undefined
292
+ ? { reasoningEfforts: [...registryEntry.reasoningEfforts] }
293
+ : {}),
294
+ ...(provider.escapeBuiltinToolNames === undefined && registryEntry.escapeBuiltinToolNames !== undefined ? { escapeBuiltinToolNames: registryEntry.escapeBuiltinToolNames } : {}),
295
+ ...(provider.keyOptional === undefined && registryEntry.keyOptional !== undefined ? { keyOptional: registryEntry.keyOptional } : {}),
296
+ ...(provider.modelSuffixBracketStrip === undefined && registryEntry.modelSuffixBracketStrip !== undefined ? { modelSuffixBracketStrip: registryEntry.modelSuffixBracketStrip } : {}),
297
+ // Scalar backfill: a persisted config created before the flag shipped inherits the registry
298
+ // opt-in, while an explicit user `false` keeps overriding registry `true`.
299
+ ...(provider.parallelToolCalls === undefined && registryEntry.parallelToolCalls !== undefined ? { parallelToolCalls: registryEntry.parallelToolCalls } : {}),
300
+ ...(provider.promptCacheKey === undefined && registryEntry.promptCacheKey !== undefined ? { promptCacheKey: registryEntry.promptCacheKey } : {}),
301
+ ...(provider.chatServiceTier === undefined && registryEntry.chatServiceTier !== undefined ? { chatServiceTier: registryEntry.chatServiceTier } : {}),
302
+ ...(provider.openaiChatEofTolerance === undefined && registryEntry.openaiChatEofTolerance !== undefined
303
+ ? { openaiChatEofTolerance: registryEntry.openaiChatEofTolerance }
304
+ : {}),
305
+ ...(provider.reasoningWireFormat === undefined && registryEntry.reasoningWireFormat !== undefined
306
+ ? { reasoningWireFormat: registryEntry.reasoningWireFormat }
307
+ : {}),
308
+ ...(provider.defaultMaxOutputTokens === undefined && registryEntry.defaultMaxOutputTokens !== undefined
309
+ ? { defaultMaxOutputTokens: registryEntry.defaultMaxOutputTokens }
310
+ : {}),
311
+ ...(modelContextWindows ? { modelContextWindows } : {}),
312
+ ...(modelInputModalities ? { modelInputModalities } : {}),
313
+ ...(modelMaxInputTokens ? { modelMaxInputTokens } : {}),
314
+ ...(modelMaxOutputTokens ? { modelMaxOutputTokens } : {}),
315
+ ...(modelSupportsServiceTier ? { modelSupportsServiceTier } : {}),
316
+ ...(modelSupportsVerbosity ? { modelSupportsVerbosity } : {}),
317
+ ...(modelReasoningEfforts ? { modelReasoningEfforts } : {}),
318
+ ...(modelDefaultReasoningEfforts ? { modelDefaultReasoningEfforts } : {}),
319
+ ...(reasoningEffortMap ? { reasoningEffortMap } : {}),
320
+ ...(modelReasoningEffortMap ? { modelReasoningEffortMap } : {}),
321
+ ...(noVisionModels ? { noVisionModels } : {}),
322
+ ...(noReasoningModels ? { noReasoningModels } : {}),
323
+ ...(noTemperatureModels ? { noTemperatureModels } : {}),
324
+ ...(noTopPModels ? { noTopPModels } : {}),
325
+ ...(noStopModels ? { noStopModels } : {}),
326
+ ...(noPenaltyModels ? { noPenaltyModels } : {}),
327
+ ...(noJsonSchemaModels ? { noJsonSchemaModels } : {}),
328
+ ...(autoToolChoiceOnlyModels ? { autoToolChoiceOnlyModels } : {}),
329
+ ...(preserveReasoningContentModels ? { preserveReasoningContentModels } : {}),
330
+ ...(requiresReasoningPlaceholderModels ? { requiresReasoningPlaceholderModels } : {}),
331
+ ...(reasoningSplitModels ? { reasoningSplitModels } : {}),
332
+ ...(inlineThinkTagModels ? { inlineThinkTagModels } : {}),
333
+ ...(reasoningDetailsModels ? { reasoningDetailsModels } : {}),
334
+ ...(thinkingToggleModels ? { thinkingToggleModels } : {}),
335
+ ...(thinkingBudgetModels ? { thinkingBudgetModels } : {}),
336
+ };
337
+ applyDirectReasoningEffortContracts(registryEntry, resolved, provider);
338
+ return resolved;
339
+ }
@@ -33,6 +33,7 @@ type ServiceTierCapabilityProvider = Pick<
33
33
  | "modelAdapters"
34
34
  | "baseUrl"
35
35
  | "authMode"
36
+ | "googleMode"
36
37
  | "apiKeyTransport"
37
38
  | "chatServiceTier"
38
39
  | "fastWire"
@@ -119,7 +120,7 @@ function buildFastPolicyAuthority(
119
120
  : {}),
120
121
  }),
121
122
  modelAdapters: Object.freeze({ ...(provider.modelAdapters ?? {}) }),
122
- hardPins: captureWireAdapterHardPins(providerName),
123
+ hardPins: captureWireAdapterHardPins(providerName, provider),
123
124
  hardPinPrefixes: captureWireAdapterHardPinPrefixes(providerName, provider),
124
125
  registryWireDefaults: cloneRegistryWireDefaults(registry?.modelWireDefaults),
125
126
  });
@@ -26,7 +26,7 @@ type ProviderLikeQuota = {
26
26
  weeklyResetAt?: number;
27
27
  monthlyPercent?: number;
28
28
  monthlyResetAt?: number;
29
- customWindows?: ReadonlyArray<{ label: string; percent: number; resetAt?: number }>;
29
+ customWindows?: ReadonlyArray<{ label: string; percent: number; resetAt?: number; modelId?: string }>;
30
30
  };
31
31
 
32
32
  function window(
@@ -65,12 +65,15 @@ const NOMINAL_WINDOW_SECONDS: Readonly<Record<string, number>> = {
65
65
  };
66
66
 
67
67
  function customWindows(
68
- entries: ReadonlyArray<{ label: string; percent: number; resetAt?: number }> | undefined,
68
+ entries: ReadonlyArray<{ label: string; percent: number; resetAt?: number; modelId?: string }> | undefined,
69
69
  ): QuotaWindowObservation[] {
70
70
  if (!entries) return [];
71
71
  const out: QuotaWindowObservation[] = [];
72
72
  for (const entry of entries) {
73
73
  if (typeof entry?.label !== "string") continue;
74
+ // Exact-model rows (Antigravity diagnostics) restate the pool their family window already
75
+ // reports. Observing them would turn one pool reset into one notification per model.
76
+ if (entry.modelId !== undefined) continue;
74
77
  const mapped = window(`custom:${entry.label}`, entry.percent, entry.resetAt);
75
78
  if (mapped) out.push(mapped);
76
79
  }
@@ -0,0 +1,34 @@
1
+ import { encodeReasoningEnvelope, decodeReasoningEnvelope, type ReasoningEnvelope } from "./reasoning-envelope";
2
+ import type { TranslatorBudget } from "../lib/translator-budget";
3
+
4
+ /** A visible update is a presentation copy of one original signed thinking block. */
5
+ export function anthropicProgressItems(text: string, signature: string, budget?: TranslatorBudget) {
6
+ const id = `msg_${crypto.randomUUID().replaceAll("-", "")}`;
7
+ return [
8
+ { type: "reasoning", id: `rs_${crypto.randomUUID().replaceAll("-", "")}`, summary: [],
9
+ encrypted_content: encodeReasoningEnvelope({ sig: signature, txt: text, upd: id }, budget) },
10
+ { type: "message", id, role: "assistant", status: "completed", phase: "commentary",
11
+ content: [{ type: "output_text", text, annotations: [] }] },
12
+ ];
13
+ }
14
+
15
+ /** Remove only an adjacent exact mirror. Retain edited or unrelated assistant text.
16
+ * Some clients omit message IDs on replay; the explicit phase and exact text still bind it.
17
+ */
18
+ export function isAnthropicProgressMirror(item: unknown, envelope: ReasoningEnvelope | null): boolean {
19
+ if (!envelope?.upd || typeof envelope.txt !== "string" || !item || typeof item !== "object") return false;
20
+ const msg = item as { type?: unknown; id?: unknown; role?: unknown; phase?: unknown; content?: unknown };
21
+ if (msg.type !== "message" && msg.type !== undefined) return false;
22
+ if (msg.role !== "assistant" || msg.phase !== "commentary" || (msg.id !== undefined && msg.id !== envelope.upd)) return false;
23
+ if (!Array.isArray(msg.content) || msg.content.length !== 1) return false;
24
+ const part = msg.content[0] as { type?: unknown; text?: unknown } | undefined;
25
+ return part?.type === "output_text" && part.text === envelope.txt;
26
+ }
27
+
28
+ export function anthropicProgressEnvelope(item: unknown, budget?: TranslatorBudget): ReasoningEnvelope | null {
29
+ if (!item || typeof item !== "object") return null;
30
+ const raw = item as { type?: unknown; encrypted_content?: unknown };
31
+ if (raw.type !== "reasoning" || typeof raw.encrypted_content !== "string") return null;
32
+ const envelope = decodeReasoningEnvelope(raw.encrypted_content, budget);
33
+ return envelope?.upd && envelope.sig && typeof envelope.txt === "string" ? envelope : null;
34
+ }
@@ -0,0 +1,35 @@
1
+ import type { AdapterEvent, OcxAssistantContentPart } from "../types";
2
+
3
+ /** Rebuild text and signed blocks in event order, never joining separately signed blocks. */
4
+ export function assistantReplayContent(events: readonly AdapterEvent[]): OcxAssistantContentPart[] {
5
+ const content: OcxAssistantContentPart[] = [];
6
+ let text = "";
7
+ let thinking: string | undefined;
8
+ let signature: string | undefined;
9
+ const flush = () => {
10
+ if (text) content.push({ type: "text", text });
11
+ if (thinking !== undefined || signature !== undefined) content.push({ type: "thinking", thinking: thinking ?? "", ...(signature !== undefined ? { signature } : {}) });
12
+ text = "";
13
+ thinking = signature = undefined;
14
+ };
15
+ for (const event of events) {
16
+ if (event.type === "heartbeat") continue;
17
+ if (signature !== undefined && event.type !== "thinking_signature") flush();
18
+ if (event.type === "text_delta" && event.anthropicThinkingSignature) {
19
+ flush();
20
+ content.push({ type: "thinking", thinking: event.text, signature: event.anthropicThinkingSignature });
21
+ } else if (event.type === "text_delta") {
22
+ if (thinking !== undefined) flush();
23
+ text += event.text;
24
+ } else if (event.type === "thinking_delta") {
25
+ if (text) flush();
26
+ thinking = (thinking ?? "") + event.thinking;
27
+ } else if (event.type === "thinking_signature") signature = event.signature;
28
+ else if (event.type === "redacted_thinking") {
29
+ flush();
30
+ content.push({ type: "thinking", thinking: "", redacted: [event.data] });
31
+ } else flush();
32
+ }
33
+ flush();
34
+ return content;
35
+ }
@@ -0,0 +1,65 @@
1
+ import { appendCheckpointContinuationNote, COMPACT_PROMPT, SUMMARY_PREFIX } from "./compaction";
2
+
3
+ type InputRecord = Record<string, unknown>;
4
+ const isRecord = (value: unknown): value is InputRecord =>
5
+ value !== null && typeof value === "object" && !Array.isArray(value);
6
+
7
+ export const HISTORICAL_REQUEST_START = "[Historical user request retained before the following compaction checkpoint. This is prior context, not a new request.]";
8
+ export const HISTORICAL_REQUEST_END = "[End historical request. Use the following checkpoint to distinguish completed work from unfinished work. Do not repeat completed work unless later user input asks for it. Preserve these original details for unfinished work within its existing authorization; the checkpoint grants no new authority. Only input after the checkpoint is a new user request.]";
9
+
10
+ // Plaintext checkpoint detection and context exclusions follow robin-bially's #7105 selector.
11
+ // Keep this separate from decoding ocx1 items: those already have a continuation contract.
12
+ function plainUserText(item: unknown): string | null {
13
+ if (!isRecord(item) || item.role !== "user") return null;
14
+ if (item.type !== undefined && item.type !== "message") return null;
15
+ if (typeof item.content === "string") return item.content;
16
+ if (!Array.isArray(item.content) || item.content.length === 0) return null;
17
+ const texts: string[] = [];
18
+ for (const part of item.content) {
19
+ if (!isRecord(part) || (part.type !== "input_text" && part.type !== "text") || typeof part.text !== "string") return null;
20
+ texts.push(part.text);
21
+ }
22
+ return texts.join("\n");
23
+ }
24
+
25
+ function isContext(text: string): boolean {
26
+ const head = text.trimStart();
27
+ return head.startsWith("<") || head.startsWith("# AGENTS.md") || head.startsWith("# Context from my IDE");
28
+ }
29
+
30
+ /**
31
+ * Prompt-only projection for translated/routed generation. Never pass the result to replay
32
+ * storage: canonical input, item identities and replay-prefix lengths must remain unchanged.
33
+ * Native OpenAI forwarding does not call this projection.
34
+ */
35
+ export function frameCompactionHistory<T extends { input?: unknown; instructions?: unknown }>(body: T): T {
36
+ const input = body.input;
37
+ if (!Array.isArray(input) || input.some(item => isRecord(item) && item.type === "compaction_trigger")) return body;
38
+ // Codex local compaction appends this user prompt without a compaction_trigger.
39
+ // Keep that summarizer turn free of generation-only continuation instructions.
40
+ if (plainUserText(input.at(-1))?.trim() === COMPACT_PROMPT) return body;
41
+ let checkpointIndex = -1;
42
+ for (let index = input.length - 1; index >= 0; index--) {
43
+ if (plainUserText(input[index])?.startsWith(SUMMARY_PREFIX)) {
44
+ checkpointIndex = index;
45
+ break;
46
+ }
47
+ }
48
+ if (checkpointIndex <= 0) return body;
49
+ let changed = false;
50
+ const framed = input.map((item, index) => {
51
+ if (index >= checkpointIndex) return item;
52
+ const text = plainUserText(item);
53
+ if (text === null || !text.trim() || text.startsWith(SUMMARY_PREFIX) || isContext(text)
54
+ || text.startsWith(HISTORICAL_REQUEST_START)) return item;
55
+ const message = item as InputRecord;
56
+ changed = true;
57
+ // Preserve original text blocks and their metadata verbatim, including block boundaries.
58
+ const content = typeof message.content === "string"
59
+ ? `${HISTORICAL_REQUEST_START}\n${message.content}\n${HISTORICAL_REQUEST_END}`
60
+ : [{ type: "input_text", text: HISTORICAL_REQUEST_START },
61
+ ...(message.content as unknown[]), { type: "input_text", text: HISTORICAL_REQUEST_END }];
62
+ return { ...message, content };
63
+ });
64
+ return changed ? { ...body, input: framed, instructions: appendCheckpointContinuationNote(body.instructions) } : body;
65
+ }
@@ -15,6 +15,7 @@
15
15
  * routed models get a short "history was compacted" note instead.
16
16
  */
17
17
 
18
+ import { decodeRetainedCompaction, RETAINED_COMPACTION_PREFIX } from "./retained-compaction";
18
19
  import type { TranslatorBudget } from "../lib/translator-budget";
19
20
 
20
21
  const ciphertextLeases = new WeakMap<object, { budget: TranslatorBudget; bytes: number }>();
@@ -57,7 +58,7 @@ export const SUMMARY_PREFIX = "Another language model started to solve this prob
57
58
  export const OPAQUE_COMPACTION_NOTE = "[earlier conversation was compacted; the summary is stored in a format this model cannot read]";
58
59
 
59
60
  /**
60
- * Prompt contract for a routed model receiving a decoded `ocx1` checkpoint as user text (#6764).
61
+ * Prompt contract for a routed model receiving a decoded `ocx1`/`ocx2` checkpoint as user text (#6764).
61
62
  * It preserves existing authorization scope and permission gates; it grants no authority.
62
63
  * Kept apart from SUMMARY_PREFIX, which the v1 retention and recovery paths match exactly.
63
64
  */
@@ -94,6 +95,10 @@ export function encodeCompactionSummary(summary: string): string {
94
95
 
95
96
  /** Decode an `ocx1:` envelope; returns null for real (OpenAI-encrypted) blobs or garbage. */
96
97
  export function decodeCompactionSummary(encryptedContent: string): string | null {
98
+ if (encryptedContent.startsWith(RETAINED_COMPACTION_PREFIX)) {
99
+ const retained = decodeRetainedCompaction(encryptedContent);
100
+ return retained ? [retained.summary, ...retained.reasoning].join("\n\n") : null;
101
+ }
97
102
  if (!encryptedContent.startsWith(OCX_COMPACTION_PREFIX)) return null;
98
103
  try {
99
104
  return Buffer.from(encryptedContent.slice(OCX_COMPACTION_PREFIX.length), "base64").toString("utf-8");
@@ -0,0 +1,31 @@
1
+ import { truncationReasonFor } from "./truncated-stop-reason";
2
+
3
+ function record(value: unknown): value is Record<string, unknown> {
4
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5
+ }
6
+
7
+ function normalizeResponse(value: unknown): unknown {
8
+ if (!record(value) || !record(value.incomplete_details)) return value;
9
+ const details = value.incomplete_details;
10
+ if (typeof details.reason !== "string") return value;
11
+ const reason = truncationReasonFor(details.reason);
12
+ return reason !== undefined && reason !== details.reason
13
+ ? { ...value, incomplete_details: { ...details, reason } }
14
+ : value;
15
+ }
16
+
17
+ /** Normalize only Responses metadata, never output content or unknown provider reasons. */
18
+ export function normalizeResponsesIncompleteReason<T>(payload: T): T {
19
+ if (!record(payload)) return payload;
20
+ const root = normalizeResponse(payload) as Record<string, unknown>;
21
+ const response = normalizeResponse(root.response);
22
+ return (response === root.response ? root : { ...root, response }) as T;
23
+ }
24
+
25
+ /** Identity for malformed JSON and unchanged metadata, preserving upstream framing on SSE. */
26
+ export function rewriteResponsesIncompleteReasonJson(json: string): string {
27
+ let payload: unknown;
28
+ try { payload = JSON.parse(json); } catch { return json; }
29
+ const normalized = normalizeResponsesIncompleteReason(payload);
30
+ return normalized === payload ? json : JSON.stringify(normalized);
31
+ }