@bitkyc08/opencodex 2.27.0 → 2.28.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 (45) hide show
  1. package/gui/dist/assets/{index-7jlKgmJd.js → index-D2sP-biU.js} +11 -11
  2. package/gui/dist/index.html +1 -1
  3. package/package.json +1 -1
  4. package/src/adapters/anthropic.ts +59 -0
  5. package/src/adapters/base.ts +2 -0
  6. package/src/adapters/google-antigravity-replay.ts +16 -8
  7. package/src/adapters/google.ts +21 -4
  8. package/src/adapters/openai-chat.ts +151 -54
  9. package/src/adapters/openai-responses.ts +37 -0
  10. package/src/cli/index.ts +19 -6
  11. package/src/codex/account-usability.ts +3 -0
  12. package/src/codex/auth-api.ts +22 -5
  13. package/src/codex/auth-context.ts +55 -2
  14. package/src/codex/catalog/metadata.ts +17 -3
  15. package/src/codex/catalog/native-models.ts +22 -14
  16. package/src/codex/catalog/sync.ts +57 -11
  17. package/src/codex/convergence.ts +61 -13
  18. package/src/codex/model-entitlements.ts +353 -0
  19. package/src/codex/quota.ts +28 -3
  20. package/src/codex/routing.ts +14 -8
  21. package/src/generated/compatibility-version.json +51 -39
  22. package/src/lib/destination-policy.ts +47 -0
  23. package/src/lib/shadow-call.ts +15 -0
  24. package/src/oauth/index.ts +33 -5
  25. package/src/oauth/store.ts +11 -5
  26. package/src/providers/fastwire.ts +39 -8
  27. package/src/providers/quota.ts +9 -2
  28. package/src/providers/registry.ts +74 -5
  29. package/src/providers/service-tier.ts +16 -8
  30. package/src/responses/parser.ts +3 -9
  31. package/src/responses/tool-search-compat.ts +301 -0
  32. package/src/router.ts +7 -0
  33. package/src/routing/capability.ts +26 -9
  34. package/src/routing/compatibility/behavior.ts +41 -3
  35. package/src/server/chat-native.ts +11 -2
  36. package/src/server/index.ts +54 -8
  37. package/src/server/management/agent-settings-routes.ts +16 -2
  38. package/src/server/request-log.ts +31 -0
  39. package/src/server/responses/compact.ts +54 -7
  40. package/src/server/responses/core.ts +246 -39
  41. package/src/server/responses/responses-field-backfill.ts +88 -6
  42. package/src/server/responses/terminal-guard.ts +10 -0
  43. package/src/server/responses-tool-search-repair.ts +217 -0
  44. package/src/server/system-env.ts +74 -5
  45. package/src/usage/log.ts +4 -0
@@ -10,7 +10,7 @@
10
10
  },
11
11
  {
12
12
  "path": "package.json",
13
- "sha256": "f51abeea3f3868735b479d9b97b58e7cbdc452e9e5cc4937dfa794a40d9e10c7"
13
+ "sha256": "7a24163416c4a91b5c086551f354ad721b4dd57b8e92216aab025231446d69ac"
14
14
  },
15
15
  {
16
16
  "path": "scripts/model-metadata.source.json",
@@ -34,7 +34,7 @@
34
34
  },
35
35
  {
36
36
  "path": "src/adapters/anthropic.ts",
37
- "sha256": "1d332a1ca8a265bd1e2f534fafed109c5fffc0b60fabd721b52e374c36482230"
37
+ "sha256": "cdbc3a5d6867fb7b93538177e2e670f5bc770406c09dbc6eb9a891f757eb24b8"
38
38
  },
39
39
  {
40
40
  "path": "src/adapters/azure.ts",
@@ -42,7 +42,7 @@
42
42
  },
43
43
  {
44
44
  "path": "src/adapters/base.ts",
45
- "sha256": "e3371fb84f8af57cc772bb3b79b1b5af08a6158cb7bdb815b4c8fafb5eaf91e5"
45
+ "sha256": "11736116daa190c3cdfbbf9fc71ddae6c35f37e11a55b4863ec6fe5423e57b70"
46
46
  },
47
47
  {
48
48
  "path": "src/adapters/client-fingerprint.ts",
@@ -194,7 +194,7 @@
194
194
  },
195
195
  {
196
196
  "path": "src/adapters/google-antigravity-replay.ts",
197
- "sha256": "52704bcd65eab346025e6faffe70d0c1d07e39651e4139f34bc67759fbaa4f08"
197
+ "sha256": "95fe3bb064585eec1a0d9ffb411548b7a0410d94ab92054f1d4ea5e105d2d8f2"
198
198
  },
199
199
  {
200
200
  "path": "src/adapters/google-antigravity-wire.ts",
@@ -222,7 +222,7 @@
222
222
  },
223
223
  {
224
224
  "path": "src/adapters/google.ts",
225
- "sha256": "e773b9fc6481ad14b075ceebf4fa950405fe9d775454408d8df37044b17a5aaf"
225
+ "sha256": "96b3ced298609ba34f1022150813f3cc2525917fbc579689cfe7631642a9ada6"
226
226
  },
227
227
  {
228
228
  "path": "src/adapters/identity.ts",
@@ -286,7 +286,7 @@
286
286
  },
287
287
  {
288
288
  "path": "src/adapters/openai-chat.ts",
289
- "sha256": "da14137945b5df7764ef9c40e24b0ebce284bf2d91acf620921041eeff61e7cb"
289
+ "sha256": "39007bc71a8c52318808c2c22c7796a7e5c40cfe765ace8882e1b125da91a47a"
290
290
  },
291
291
  {
292
292
  "path": "src/adapters/openai-responses-url.ts",
@@ -294,7 +294,7 @@
294
294
  },
295
295
  {
296
296
  "path": "src/adapters/openai-responses.ts",
297
- "sha256": "182f672a29b05c3f062bfabc74bed99fadd2b915ec8e8d187887b6fedad7d25d"
297
+ "sha256": "fdf3093779304cb6b12195f5170c1e90072bb8104eef6856f676a277b877ca27"
298
298
  },
299
299
  {
300
300
  "path": "src/adapters/registry.ts",
@@ -490,7 +490,7 @@
490
490
  },
491
491
  {
492
492
  "path": "src/cli/index.ts",
493
- "sha256": "8dbbddb81c22001ba41fcd9239767754593ea110052ceb8507656f83cbe1d3e0"
493
+ "sha256": "b83e3b8d02ef0fd52eab24b105e044a62b9091938e92b17726b7c707cdbfda0b"
494
494
  },
495
495
  {
496
496
  "path": "src/cli/init.ts",
@@ -630,7 +630,7 @@
630
630
  },
631
631
  {
632
632
  "path": "src/codex/account-usability.ts",
633
- "sha256": "3f377034f5bbc74a25d431cf9a02f137089dc39cf696dfe42342086f60148b75"
633
+ "sha256": "b01f00c83be2d815eecea92e9fee54d968f6e861c7d262dfbb76db2e23d1c898"
634
634
  },
635
635
  {
636
636
  "path": "src/codex/admission.ts",
@@ -646,7 +646,7 @@
646
646
  },
647
647
  {
648
648
  "path": "src/codex/auth-api.ts",
649
- "sha256": "026a563ba1e4049c5ba79083f1eb183e3f9ab7d078f28b7c6e9d9cc556be8b02"
649
+ "sha256": "e9e1dcd8c0ac39c93a1ed7977398bd5ce518669f5cc04b9f1331d2d1f54d9ed1"
650
650
  },
651
651
  {
652
652
  "path": "src/codex/auth-collision.ts",
@@ -654,7 +654,7 @@
654
654
  },
655
655
  {
656
656
  "path": "src/codex/auth-context.ts",
657
- "sha256": "4ee0af8ad36c280e74055ed0b6a4652e3f56f1aa76b97d95d97ddd67e9f91465"
657
+ "sha256": "c7bc48d325399fd22a5140d0c9b3a47746bacfff8a3d6d6192f717aa0dfd4535"
658
658
  },
659
659
  {
660
660
  "path": "src/codex/autostart-health.ts",
@@ -702,11 +702,11 @@
702
702
  },
703
703
  {
704
704
  "path": "src/codex/catalog/metadata.ts",
705
- "sha256": "fc1cce8854a517f6c80ce137a02fb183c6710899f2a1d7536707c00ad56b4de6"
705
+ "sha256": "e1c798b07f69a0c6130581096a1bc99ecad597e835895df58d1d76ab8f855f3e"
706
706
  },
707
707
  {
708
708
  "path": "src/codex/catalog/native-models.ts",
709
- "sha256": "d0d8764f7d9b1d102350527f8b196beac0db5ed492ae2b2147158d57981494f7"
709
+ "sha256": "60004cd7ee40ddf892dfd793608a1a35c9f6d3e19b25a1690482ded275432e08"
710
710
  },
711
711
  {
712
712
  "path": "src/codex/catalog/parsing.ts",
@@ -718,7 +718,7 @@
718
718
  },
719
719
  {
720
720
  "path": "src/codex/catalog/sync.ts",
721
- "sha256": "c0fa454b88d4efa4109039a33510cbdc0e547499d6d521debb2f7f2f614c0dfe"
721
+ "sha256": "b92ba2e7caba143497e9f1289c2e4235a50adf03e09529cd6d4402d1905b3497"
722
722
  },
723
723
  {
724
724
  "path": "src/codex/codex-write-lock.ts",
@@ -730,7 +730,7 @@
730
730
  },
731
731
  {
732
732
  "path": "src/codex/convergence.ts",
733
- "sha256": "ffe1a891c51b33ace33d99bb11ca36015544189f8c4bb1966c2bb08469a163fb"
733
+ "sha256": "4ab2e8828a32a1b6235528d0b9be18b24c7457c485972a00492a4a1675bb9c0a"
734
734
  },
735
735
  {
736
736
  "path": "src/codex/custom-model-catalog-migration.ts",
@@ -860,6 +860,10 @@
860
860
  "path": "src/codex/model-cache.ts",
861
861
  "sha256": "f49cb78b3af7487d1da6f9bbb578615a75644f1a176e5ca89096186219456e94"
862
862
  },
863
+ {
864
+ "path": "src/codex/model-entitlements.ts",
865
+ "sha256": "3f3a688f47877faf43f3ec59c2ecf8fce7afd039f7cca7725c1ffd57189c1829"
866
+ },
863
867
  {
864
868
  "path": "src/codex/native-main-admission.ts",
865
869
  "sha256": "1f4956299bf243980bb06c7ddcfdadf71497cd874d3c1c49cb28465f27f6c85f"
@@ -958,7 +962,7 @@
958
962
  },
959
963
  {
960
964
  "path": "src/codex/quota.ts",
961
- "sha256": "5adb6824a01a928c37effa5b0cd745e2efb25baf1033079d5a81ff33b4cf0549"
965
+ "sha256": "5be1ce7479ecfb77bdbd1fce2a3f2f0236e93c4ab17e415a1711269359cd44ca"
962
966
  },
963
967
  {
964
968
  "path": "src/codex/refresh.ts",
@@ -970,7 +974,7 @@
970
974
  },
971
975
  {
972
976
  "path": "src/codex/routing.ts",
973
- "sha256": "741aeeb7c6939a859500e6424c6676fe7c247472392108b2616a2dc4a2349374"
977
+ "sha256": "2e3af0b15bd8b147bbf2b9e5c4117e1d6e2b051fa20b4ee028c94a1b721f0647"
974
978
  },
975
979
  {
976
980
  "path": "src/codex/runtime.ts",
@@ -1702,7 +1706,7 @@
1702
1706
  },
1703
1707
  {
1704
1708
  "path": "src/lib/destination-policy.ts",
1705
- "sha256": "7558278af108a066982b06fc9550dce7460c2dd22fb1a31f6f2edc8878bca7a2"
1709
+ "sha256": "68c92a0c4748d0d086e48c1019ef9c4e72a835524dd4c16047f5f099389f35b8"
1706
1710
  },
1707
1711
  {
1708
1712
  "path": "src/lib/errors.ts",
@@ -1818,7 +1822,7 @@
1818
1822
  },
1819
1823
  {
1820
1824
  "path": "src/lib/shadow-call.ts",
1821
- "sha256": "2e55cf6e560b18337ac1911b38f20a27c1bd985a65e9496783f2a7cf85014bc6"
1825
+ "sha256": "d5fe5af453ef4699403809358d587b43dd57fc1ccaa5972863e9ebc68787119c"
1822
1826
  },
1823
1827
  {
1824
1828
  "path": "src/lib/sidecar-tracker.ts",
@@ -1966,7 +1970,7 @@
1966
1970
  },
1967
1971
  {
1968
1972
  "path": "src/oauth/index.ts",
1969
- "sha256": "7209f64fa8552dbf7253f1d9fac61bb24577ff05d17617328bc82dadd18f83d5"
1973
+ "sha256": "4b6603e57e1b5b13a37c27876ba1d85066e145b06507d40923ede0f6c0892625"
1970
1974
  },
1971
1975
  {
1972
1976
  "path": "src/oauth/key-providers.ts",
@@ -2006,7 +2010,7 @@
2006
2010
  },
2007
2011
  {
2008
2012
  "path": "src/oauth/store.ts",
2009
- "sha256": "61c14ad05498f0146aec503f8618a4249d8064967377c6c782437276c9ab0ac5"
2013
+ "sha256": "d68c60975ba91aadda30d5a75bcabdad7f265115b280b32cd046aaffaac256a2"
2010
2014
  },
2011
2015
  {
2012
2016
  "path": "src/oauth/token-guardian.ts",
@@ -2062,7 +2066,7 @@
2062
2066
  },
2063
2067
  {
2064
2068
  "path": "src/providers/fastwire.ts",
2065
- "sha256": "c6ad24cedb9cf0a039603552fc17c639332ecf28231ccdccb46e16f11dd775f6"
2069
+ "sha256": "00450dcc15d43905900dbf0623db69e09365731ace576f42fef66bee5ca2752d"
2066
2070
  },
2067
2071
  {
2068
2072
  "path": "src/providers/free-directory.ts",
@@ -2134,11 +2138,11 @@
2134
2138
  },
2135
2139
  {
2136
2140
  "path": "src/providers/quota.ts",
2137
- "sha256": "6d101988931c219b5d96f2f0f7d8ea84591ec8445585020f74b847b33149ca76"
2141
+ "sha256": "d74e47b5106a03d447532f283bdbfe96f632dee1ef662014bbfb049ddb857c8d"
2138
2142
  },
2139
2143
  {
2140
2144
  "path": "src/providers/registry.ts",
2141
- "sha256": "336fd0d6d81354a69b8c577e162dd73dd69e196b13ef82d51720897fc16d9fa5"
2145
+ "sha256": "a884fc088434fbb5d45ed6ef8f56435c730e8701334c475ac6251a37322a514e"
2142
2146
  },
2143
2147
  {
2144
2148
  "path": "src/providers/request-pacing.ts",
@@ -2146,7 +2150,7 @@
2146
2150
  },
2147
2151
  {
2148
2152
  "path": "src/providers/service-tier.ts",
2149
- "sha256": "ebfb65e1e4983d60504a91ed4c97bb5df541b6307bbec1436f9999e2ff524545"
2153
+ "sha256": "56198ef6740a3b7dbd59c605669383dc52d4004032818c54b1a0289bf34e2c3f"
2150
2154
  },
2151
2155
  {
2152
2156
  "path": "src/providers/slug-codec.ts",
@@ -2178,7 +2182,7 @@
2178
2182
  },
2179
2183
  {
2180
2184
  "path": "src/responses/parser.ts",
2181
- "sha256": "ad950568ee4f921b33a94acfaf214ddabe31c9701f6e7d4d73923f34a1b466e3"
2185
+ "sha256": "9fc33a85338232e63d2bfee50edd859319720b11911f0d2497b3831a7af103ca"
2182
2186
  },
2183
2187
  {
2184
2188
  "path": "src/responses/provider-opaque-metadata.ts",
@@ -2212,13 +2216,17 @@
2212
2216
  "path": "src/responses/tool-groups.ts",
2213
2217
  "sha256": "b2750b33ec7bb5a7f76f27584bf99372fac4f253dd287849b2f3f9821db574cd"
2214
2218
  },
2219
+ {
2220
+ "path": "src/responses/tool-search-compat.ts",
2221
+ "sha256": "25ddbc4dd8f04ce43804aab64fdf5dcc402703529b8252d2c64bbb5749ece4a6"
2222
+ },
2215
2223
  {
2216
2224
  "path": "src/responses/truncated-stop-reason.ts",
2217
2225
  "sha256": "44a1876c045a63f9275410fb9169d2c47ce0c1de309e9cc00a0f51c72698a6dd"
2218
2226
  },
2219
2227
  {
2220
2228
  "path": "src/router.ts",
2221
- "sha256": "9eb5c0ea04fcb51150d91308138d3fab25dd19a85bc7bb273295c7924e1b128e"
2229
+ "sha256": "6cca7c4d7b1b18b0d7acc2d132c1b2b456b091f90cbcf7556dfbfb98e43e16b9"
2222
2230
  },
2223
2231
  {
2224
2232
  "path": "src/routing/analytics.ts",
@@ -2226,7 +2234,7 @@
2226
2234
  },
2227
2235
  {
2228
2236
  "path": "src/routing/capability.ts",
2229
- "sha256": "e4c7da723d461ac41b18cdf5d80085887a0b0e46917bcb4e012f52fc431475d0"
2237
+ "sha256": "119b4002bb7cfd25763b9afe37b6099bd3d332968f23fb7f504bccbb5a605b31"
2230
2238
  },
2231
2239
  {
2232
2240
  "path": "src/routing/compatibility/assemble.ts",
@@ -2234,7 +2242,7 @@
2234
2242
  },
2235
2243
  {
2236
2244
  "path": "src/routing/compatibility/behavior.ts",
2237
- "sha256": "e7006c3aebe1d36b6d7a9cd97f287599fc231ba0ddb7163c7a502ac67ad6d84f"
2245
+ "sha256": "8b7a15e57a15312ebd0caf9baadd0e2438373aa0434edf336766efed1eff13ea"
2238
2246
  },
2239
2247
  {
2240
2248
  "path": "src/routing/compatibility/catalog.ts",
@@ -2338,7 +2346,7 @@
2338
2346
  },
2339
2347
  {
2340
2348
  "path": "src/server/chat-native.ts",
2341
- "sha256": "18ba244fd5ebd4a1b4a78a4b0aead236a4968a347d91e3130f73e2fc1c7f01f6"
2349
+ "sha256": "73f1c79e390998bb6e3be373c27b43921c05a79e61ebe63a6a782255da3e777b"
2342
2350
  },
2343
2351
  {
2344
2352
  "path": "src/server/claude-messages.ts",
@@ -2370,7 +2378,7 @@
2370
2378
  },
2371
2379
  {
2372
2380
  "path": "src/server/index.ts",
2373
- "sha256": "a7276cdfd8dd11936c52c5235378d2ea7bc8d47ee64ba8ef07a5738cb34bcb28"
2381
+ "sha256": "2d07435626dcfea1f6d1013ff9c299b1af4cde07ed4fafd1fda318483ec42418"
2374
2382
  },
2375
2383
  {
2376
2384
  "path": "src/server/lifecycle.ts",
@@ -2398,7 +2406,7 @@
2398
2406
  },
2399
2407
  {
2400
2408
  "path": "src/server/management/agent-settings-routes.ts",
2401
- "sha256": "97487f7dadf5abae969085b6058f430fce0d309e8882eaeeedae078ccc6cafc9"
2409
+ "sha256": "9833b1d65c894fb43b66757a6e6063868b1e57a140fa0f8a7004f88dac9551ea"
2402
2410
  },
2403
2411
  {
2404
2412
  "path": "src/server/management/api-access.ts",
@@ -2550,7 +2558,7 @@
2550
2558
  },
2551
2559
  {
2552
2560
  "path": "src/server/request-log.ts",
2553
- "sha256": "e94e8d62f2993d64361f0eaf9dd767488e8e0586390e3d1683e9774bee94f355"
2561
+ "sha256": "e325fcb803850ee8e6426a1f7ecd18d014240b77ff40ea492e36e70fa9bee055"
2554
2562
  },
2555
2563
  {
2556
2564
  "path": "src/server/responses-custom-tool-repair.ts",
@@ -2584,6 +2592,10 @@
2584
2592
  "path": "src/server/responses-terminal-repair.ts",
2585
2593
  "sha256": "d7467f1c5e0c93236c27fd3b9edab289f14cca6d15d07dfe00b3fb6f1f04e0ae"
2586
2594
  },
2595
+ {
2596
+ "path": "src/server/responses-tool-search-repair.ts",
2597
+ "sha256": "a59a7cdbd734b2695f273419f615bc3478118c8b54fff85fc712f2f259f83718"
2598
+ },
2587
2599
  {
2588
2600
  "path": "src/server/responses-undeclared-tool-guard.ts",
2589
2601
  "sha256": "1846475786b9009d6d2c4620aa3fc8abcfd39fb07531f14e65182437848168bb"
@@ -2606,11 +2618,11 @@
2606
2618
  },
2607
2619
  {
2608
2620
  "path": "src/server/responses/compact.ts",
2609
- "sha256": "28bf7c356d9b7290adbb04411b89790ee8040ed798a088a98e6c0d499e4f030b"
2621
+ "sha256": "26ce4c5c090d7de26f62384aa4bee31b7a8feca7737fd49a57dcd3528eae7331"
2610
2622
  },
2611
2623
  {
2612
2624
  "path": "src/server/responses/core.ts",
2613
- "sha256": "1d3e6811caa5bd45bc89673102a53663308f9dea7d8054ff1f7aaa65428e5840"
2625
+ "sha256": "0b0d9ad6d5364b663c5135a7fd620de4bc0b728f9a7331c3babbedce1522d866"
2614
2626
  },
2615
2627
  {
2616
2628
  "path": "src/server/responses/empty-completion-guard.ts",
@@ -2642,11 +2654,11 @@
2642
2654
  },
2643
2655
  {
2644
2656
  "path": "src/server/responses/responses-field-backfill.ts",
2645
- "sha256": "728497e1b8ebc7b65ac22156f210f3714871b162c8ab8867b9c9709acf3fdef6"
2657
+ "sha256": "b12433c439602b074949cadeeb5dc490babf83a6bf2a01ba9f2e38bae54c2cf3"
2646
2658
  },
2647
2659
  {
2648
2660
  "path": "src/server/responses/terminal-guard.ts",
2649
- "sha256": "83803848122bf976d148b90d0b145cd1478fe92ba2b5bda6604232e8ff508ba7"
2661
+ "sha256": "4c74112c8cf6fde636ef477a2f4decdd5ca3f5c9fc3757dc951e812c161d9520"
2650
2662
  },
2651
2663
  {
2652
2664
  "path": "src/server/responses/upstream-error.ts",
@@ -2678,7 +2690,7 @@
2678
2690
  },
2679
2691
  {
2680
2692
  "path": "src/server/system-env.ts",
2681
- "sha256": "b068365c21dbc6c92de920f9ebb3c771b9f82ecff0dfa315618923d748a93025"
2693
+ "sha256": "e83b17223b42325c562be8925bfe573d4c342c68b0c9195b39657cfb64d3f0ea"
2682
2694
  },
2683
2695
  {
2684
2696
  "path": "src/server/windows-tcp-drop.ts",
@@ -2862,7 +2874,7 @@
2862
2874
  },
2863
2875
  {
2864
2876
  "path": "src/usage/log.ts",
2865
- "sha256": "ffaf10b5cdd15e728c45bca706cfe7fd73589043c1e66d36fd13225370be99bb"
2877
+ "sha256": "828fc644e10306f77ad6d371f3e042554dc4f65e9cfee721aa9df2c5a294b9a1"
2866
2878
  },
2867
2879
  {
2868
2880
  "path": "src/usage/summary.ts",
@@ -130,6 +130,50 @@ function registryAllowsPrivateNetwork(name: string): boolean {
130
130
  return getProviderRegistryEntry(name)?.allowPrivateNetworkByDefault === true;
131
131
  }
132
132
 
133
+ /**
134
+ * OAuth registry entries that opt into `allowBaseUrlOverride` send bearer credentials to a
135
+ * user-configured endpoint (review findings, PR #2109 / PR #2110): a cleartext `http:`
136
+ * override would expose the OAuth token on the wire. `https:` is therefore required for
137
+ * every non-local destination. Loopback/localhost/private relays keep working over
138
+ * `http:` because they already sit behind the explicit `allowPrivateNetwork` opt-in
139
+ * enforced by {@link providerDestinationConfigError}. Keyed/local providers (Ollama,
140
+ * vLLM, LM Studio, LiteLLM, Moonshot, Qwen, Alibaba) are untouched: they are not
141
+ * `authKind: "oauth"`, so this check never fires for them.
142
+ */
143
+ function registrySendsOAuthToOverriddenBaseUrl(name: string): boolean {
144
+ const entry = getProviderRegistryEntry(name);
145
+ return entry?.authKind === "oauth" && entry.allowBaseUrlOverride === true;
146
+ }
147
+
148
+ export function providerSecureTransportConfigError(
149
+ name: string,
150
+ provider: Pick<OcxProviderConfig, "baseUrl" | "allowPrivateNetwork">,
151
+ ): string | null {
152
+ if (!registrySendsOAuthToOverriddenBaseUrl(name)) return null;
153
+ let parsed: URL;
154
+ try {
155
+ parsed = new URL(provider.baseUrl.trim());
156
+ } catch {
157
+ return null; // invalid URLs are providerBaseUrlConfigError's concern
158
+ }
159
+ if (parsed.protocol !== "http:") return null;
160
+ const assessment = assessDestination(provider.baseUrl);
161
+ // Classify FIRST, then consult the opt-in. `allowPrivateNetwork` says "this destination is
162
+ // intentionally local", which is a statement about the address, not a waiver of transport
163
+ // security — reading it before classification let `http://attacker.example` with the opt-in
164
+ // set carry an OAuth bearer in cleartext to a public host.
165
+ if (!assessment) return null;
166
+ const local = assessment.kind === "localhost"
167
+ || assessment.kind === "loopback"
168
+ || assessment.kind === "private";
169
+ if (local && providerAllowsPrivateNetwork(name, provider)) {
170
+ // A genuinely local relay over http stays reachable through the explicit opt-in; the
171
+ // private-network gate still governs whether it may be reached at all.
172
+ return null;
173
+ }
174
+ return "baseUrl must use https: this provider sends OAuth credentials to its endpoint, and http is allowed only for loopback/private relays";
175
+ }
176
+
133
177
  /**
134
178
  * Whether a provider may reach loopback/private addresses.
135
179
  *
@@ -150,6 +194,8 @@ export function providerAllowsPrivateNetwork(
150
194
  }
151
195
 
152
196
  export function providerDestinationConfigError(name: string, provider: Pick<OcxProviderConfig, "baseUrl" | "allowPrivateNetwork">): string | null {
197
+ const secureTransportError = providerSecureTransportConfigError(name, provider);
198
+ if (secureTransportError) return secureTransportError;
153
199
  const assessment = assessDestination(provider.baseUrl);
154
200
  if (!assessment) return null;
155
201
  if (assessment.kind === "public" || assessment.kind === "hostname") return null;
@@ -331,3 +377,4 @@ export async function resolvePublicAddresses(
331
377
  export async function assertUrlResolvesPublic(url: string): Promise<void> {
332
378
  await resolvePublicAddresses(url);
333
379
  }
380
+
@@ -29,6 +29,21 @@ export function isShadowSourceModel(modelId: string, configured?: unknown): bool
29
29
  return shadowSourceModels(configured).some(prefix => modelId.startsWith(prefix));
30
30
  }
31
31
 
32
+ /**
33
+ * The configured source prefix this model matched, or undefined.
34
+ *
35
+ * Callers that RECORD the intercepted model must record this rather than the caller's raw
36
+ * `modelId`. Matching is by prefix, so `gpt-5.6-luna` plus arbitrary trailing text still
37
+ * intercepts — and the raw string is caller-controlled, reaches `usage.jsonl` and `/api/logs`,
38
+ * and only passes a pattern-based redactor on the way. A credential family that redactor does
39
+ * not recognize survives verbatim. Returning the operator-configured prefix keeps the log
40
+ * field inside a set the operator chose, so no caller string is ever persisted.
41
+ */
42
+ export function shadowSourceModelPrefix(modelId: string, configured?: unknown): string | undefined {
43
+ if (modelId.includes("/")) return undefined;
44
+ return shadowSourceModels(configured).find(prefix => modelId.startsWith(prefix));
45
+ }
46
+
32
47
  /**
33
48
  * Decide whether a matching source model should use the opt-in intercept.
34
49
  *
@@ -17,7 +17,7 @@ import { loginCommandCode, refreshCommandCodeToken } from "./command-code";
17
17
  import { ANTIGRAVITY_REQUEST_UA } from "../adapters/google-antigravity-wire";
18
18
  import { deriveOAuthDefaultModel, deriveOAuthProviderConfig } from "../providers/derive";
19
19
  import { apiKeyPoolEntryId, sanitizeApiKeyValue } from "../providers/api-keys";
20
- import { effectiveGoogleMode, getProviderRegistryEntry, providerMatchesRegistryTransport } from "../providers/registry";
20
+ import { effectiveGoogleMode, getProviderRegistryEntry, mergeRegistryStaticHeaders, providerMatchesRegistryTransport } from "../providers/registry";
21
21
  import { resolveProviderModelDiscoveryUrl } from "../providers/model-discovery";
22
22
  import { resolveProviderTransport } from "../providers/xai-transport";
23
23
  import { detectClaudeCodeToken, detectGrokCliToken, hasComparableGrokIdentity, isSameGrokIdentity, shouldAdoptGrokGeneration } from "./local-token-detect";
@@ -322,6 +322,13 @@ export class OAuthReauthIdentityUnverifiedError extends Error {
322
322
  }
323
323
  }
324
324
 
325
+ class OAuthLoginSupersededError extends Error {
326
+ constructor() {
327
+ super("OAuth login was superseded before credential persistence");
328
+ this.name = "OAuthLoginSupersededError";
329
+ }
330
+ }
331
+
325
332
  /** Project arbitrary OAuth failures onto the small, stable public error vocabulary. */
326
333
  export function publicOAuthAuthenticationErrorMessage(error: unknown): string {
327
334
  if (error instanceof OAuthMutationBusyError) {
@@ -828,7 +835,16 @@ export function buildModelsRequest(
828
835
  undefined,
829
836
  copilotApiBaseUrl,
830
837
  );
831
- const headers: Record<string, string> = { ...(effectiveProvider.headers ?? {}) };
838
+ // Model discovery is an upstream request like any other, so it carries the same registry
839
+ // static headers the inference path does. Without this a provider is identified correctly
840
+ // when it answers a completion but anonymously when it lists its own models, which is the
841
+ // kind of split fingerprint an upstream rate limiter reads as two different clients.
842
+ const registryStaticHeaders = providerMatchesRegistryTransport(providerName, effectiveProvider)
843
+ ? getProviderRegistryEntry(providerName)?.staticHeaders
844
+ : undefined;
845
+ const headers: Record<string, string> = {
846
+ ...(mergeRegistryStaticHeaders(registryStaticHeaders, effectiveProvider.headers) ?? {}),
847
+ };
832
848
  const discoveryUrl = (defaultUrl: string): string => resolveProviderModelDiscoveryUrl(
833
849
  providerName,
834
850
  prov,
@@ -1096,6 +1112,7 @@ interface RunLoginDeps {
1096
1112
  settleKiroLoginTransaction?: typeof settleKiroLoginTransaction;
1097
1113
  removeAccount?: typeof removeAccount;
1098
1114
  setActiveAccount?: typeof setActiveAccount;
1115
+ assertCurrentOwner?: () => void;
1099
1116
  }
1100
1117
 
1101
1118
  /** Roll back only accounts created by this forced login, preserving concurrent refreshes of others. */
@@ -1145,6 +1162,7 @@ export async function runLogin(
1145
1162
  const cred: OAuthCredentials = rawCred.source ? rawCred : { ...rawCred, source: "oauth" };
1146
1163
  const settleKiroTransaction = deps.settleKiroLoginTransaction ?? settleKiroLoginTransaction;
1147
1164
  try {
1165
+ deps.assertCurrentOwner?.();
1148
1166
  // Validate the provider row before credential persistence. A namespace claimed during the
1149
1167
  // credential write is handled again below before the latest row is re-upserted.
1150
1168
  if (provider !== "chatgpt") {
@@ -1165,10 +1183,13 @@ export async function runLogin(
1165
1183
  if (!identityMatches) {
1166
1184
  throw new OAuthReauthIdentityMismatchError();
1167
1185
  }
1168
- await (deps.saveAccountCredential ?? saveAccountCredential)(provider, opts.reauthAccountId, cred);
1186
+ await (deps.saveAccountCredential ?? saveAccountCredential)(provider, opts.reauthAccountId, cred, {
1187
+ assertBeforePersist: deps.assertCurrentOwner,
1188
+ });
1169
1189
  } else {
1170
1190
  await (deps.saveCredential ?? saveCredential)(provider, cred, {
1171
1191
  preserveIdentityless: opts?.forceLogin === true,
1192
+ assertBeforePersist: deps.assertCurrentOwner,
1172
1193
  });
1173
1194
  }
1174
1195
  if (provider !== "chatgpt") {
@@ -1235,6 +1256,7 @@ export async function runLogin(
1235
1256
  */
1236
1257
  const loginState = new Map<string, { error?: string; done: boolean }>();
1237
1258
  const loginAbort = new Map<string, AbortController>();
1259
+ const kiroLoginSettling = new Set<string>();
1238
1260
 
1239
1261
  /** Pending paste for a login in progress: either a waiter or a stashed early submission. */
1240
1262
  interface ManualCodeSlot {
@@ -1403,13 +1425,14 @@ export async function startLoginFlow(
1403
1425
  const def = OAUTH_PROVIDERS[provider];
1404
1426
  if (!def) throw new UnsupportedOAuthProviderError(provider);
1405
1427
  const existing = loginState.get(provider);
1406
- if (existing && !existing.done) {
1428
+ if ((existing && !existing.done) || (provider === "kiro" && kiroLoginSettling.has(provider))) {
1407
1429
  throw new Error(`A login for ${provider} is already in progress`);
1408
1430
  }
1409
1431
  clearManualCodeSlot(provider);
1410
1432
  loginState.set(provider, { done: false });
1411
1433
  const abort = new AbortController();
1412
1434
  loginAbort.set(provider, abort);
1435
+ if (provider === "kiro") kiroLoginSettling.add(provider);
1413
1436
  return new Promise((resolve, reject) => {
1414
1437
  let urlResolved = false;
1415
1438
  const ctrl: OAuthController = {
@@ -1460,7 +1483,10 @@ export async function startLoginFlow(
1460
1483
  };
1461
1484
  // Background: runLogin persists the credential + provider entry to disk. The lifecycle hook
1462
1485
  // lets a long-lived server config adopt that settled state before clients observe done=true.
1463
- void runLogin(provider, ctrl, opts).then(
1486
+ const assertCurrentOwner = (): void => {
1487
+ if (loginAbort.get(provider) !== abort) throw new OAuthLoginSupersededError();
1488
+ };
1489
+ void runLogin(provider, ctrl, opts, { assertCurrentOwner }).then(
1464
1490
  () => settle(),
1465
1491
  (e: unknown) => settle(e),
1466
1492
  ).catch((e: unknown) => {
@@ -1471,6 +1497,8 @@ export async function startLoginFlow(
1471
1497
  const msg = publicOAuthAuthenticationErrorMessage(e);
1472
1498
  loginState.set(provider, { done: true, error: msg });
1473
1499
  if (!urlResolved) reject(e);
1500
+ }).finally(() => {
1501
+ if (provider === "kiro") kiroLoginSettling.delete(provider);
1474
1502
  });
1475
1503
  });
1476
1504
  }
@@ -465,10 +465,11 @@ function serializeMutation<T>(work: () => Promise<T>, retainedValues: readonly u
465
465
  drainOAuthMutations();
466
466
  return result;
467
467
  }
468
- export function mutateStore<T>(fn:(store:AuthStore)=>T|Promise<T>, retainedValues: readonly unknown[] = [], options?: { waitMs?: number }):Promise<T>{return serializeMutation(async()=>{const guard=await createOAuthFileLock({path:getAuthStoreLockPath(),staleAfterMs:30000}).acquire();try{
468
+ export function mutateStore<T>(fn:(store:AuthStore)=>T|Promise<T>, retainedValues: readonly unknown[] = [], options?: { waitMs?: number; assertBeforePersist?: () => void }):Promise<T>{return serializeMutation(async()=>{const guard=await createOAuthFileLock({path:getAuthStoreLockPath(),staleAfterMs:30000}).acquire();try{
469
469
  const { store, hadLegacy } = loadAuthStoreInternal();
470
470
  if (hadLegacy) backupLegacyOnce();
471
471
  const result = await fn(store);
472
+ options?.assertBeforePersist?.();
472
473
  persist(store);
473
474
  return result;
474
475
  }finally{guard.release();}}, retainedValues, options?.waitMs);
@@ -491,7 +492,7 @@ export function getCredential(provider: string): OAuthCredentials | null {
491
492
  export async function saveCredential(
492
493
  provider: string,
493
494
  cred: OAuthCredentials,
494
- opts: { preserveIdentityless?: boolean } = {},
495
+ opts: { preserveIdentityless?: boolean; assertBeforePersist?: () => void } = {},
495
496
  ): Promise<void> {
496
497
  const safe = normalizeCredential(cred);
497
498
  if (!safe) return;
@@ -542,7 +543,7 @@ export async function saveCredential(
542
543
  set.accounts.push({ id, credential: safe, addedAt: Date.now() });
543
544
  set.activeAccountId = id;
544
545
  }
545
- }, [provider, safe]);
546
+ }, [provider, safe], { assertBeforePersist: opts.assertBeforePersist });
546
547
  }
547
548
 
548
549
  /**
@@ -632,7 +633,12 @@ export function getAccountCredential(provider: string, accountId: string): OAuth
632
633
  }
633
634
 
634
635
  /** Persist a refreshed credential for a SPECIFIC account without touching activeAccountId. */
635
- export async function saveAccountCredential(provider: string, accountId: string, cred: OAuthCredentials): Promise<void> {
636
+ export async function saveAccountCredential(
637
+ provider: string,
638
+ accountId: string,
639
+ cred: OAuthCredentials,
640
+ opts: { assertBeforePersist?: () => void } = {},
641
+ ): Promise<void> {
636
642
  const safe = normalizeCredential(cred);
637
643
  if (!safe) return;
638
644
  await mutateStore(store => {
@@ -640,7 +646,7 @@ export async function saveAccountCredential(provider: string, accountId: string,
640
646
  if (!account) return;
641
647
  account.credential = safe;
642
648
  delete account.needsReauth;
643
- }, [provider, accountId, safe]);
649
+ }, [provider, accountId, safe], { assertBeforePersist: opts.assertBeforePersist });
644
650
  }
645
651
 
646
652
  export async function setActiveAccount(provider: string, accountId: string): Promise<boolean> {