@bitkyc08/opencodex 2.24.2 → 2.25.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 (37) hide show
  1. package/gui/dist/assets/{index-DW-DYWmz.js → index-DxJ7kXj9.js} +1 -1
  2. package/gui/dist/index.html +1 -1
  3. package/package.json +1 -1
  4. package/src/adapters/anthropic.ts +42 -0
  5. package/src/adapters/client-fingerprint.ts +9 -5
  6. package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
  7. package/src/adapters/command-code.ts +17 -0
  8. package/src/adapters/cursor/cursor-errors.ts +49 -0
  9. package/src/adapters/cursor/live-models.ts +36 -2
  10. package/src/adapters/cursor/live-transport.ts +55 -4
  11. package/src/adapters/cursor/native-exec.ts +9 -0
  12. package/src/adapters/cursor/protobuf-request.ts +160 -9
  13. package/src/adapters/cursor/request-builder.ts +9 -1
  14. package/src/adapters/cursor/tool-definitions.ts +7 -2
  15. package/src/adapters/google-antigravity-wire.ts +1 -1
  16. package/src/adapters/google.ts +30 -12
  17. package/src/adapters/openai-responses-url.ts +5 -3
  18. package/src/adapters/registry.ts +3 -1
  19. package/src/adapters/tool-catalog-nudge.ts +76 -9
  20. package/src/bridge.ts +53 -9
  21. package/src/codex/app-server-processes.ts +69 -35
  22. package/src/codex/catalog/provider-fetch.ts +5 -1
  23. package/src/config.ts +1 -0
  24. package/src/generated/compatibility-version.json +40 -32
  25. package/src/lib/windows-elevation.ts +8 -2
  26. package/src/oauth/google-antigravity.ts +7 -2
  27. package/src/providers/antigravity-models.ts +126 -17
  28. package/src/providers/derive.ts +11 -1
  29. package/src/responses/parser.ts +4 -0
  30. package/src/responses/reasoning-replay-cache.ts +16 -1
  31. package/src/responses/thought-signature-replay.ts +17 -1
  32. package/src/responses/truncated-stop-reason.ts +60 -0
  33. package/src/router.ts +2 -10
  34. package/src/server/management/provider-routes.ts +22 -0
  35. package/src/server/request-log.ts +11 -3
  36. package/src/server/responses/core.ts +2 -0
  37. package/src/types.ts +13 -1
@@ -10,7 +10,7 @@
10
10
  },
11
11
  {
12
12
  "path": "package.json",
13
- "sha256": "6eddddb2ce07468a1c26f394225a047ae265a21e7457f38ad6500a62c2f63b66"
13
+ "sha256": "94bdcc17454ccace4e7e201babb428a1c187fe748194ff6a837df22dfe36cd50"
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": "2b565b0a8e84ab143b3a159e9f9cf65d368f2b7e27f41fadb827e28bd76b5a40"
37
+ "sha256": "afd6f5ef9fab45811c67067bdb6731fc8efa3abb7bc2a7bee109445ca9c06467"
38
38
  },
39
39
  {
40
40
  "path": "src/adapters/azure.ts",
@@ -46,11 +46,15 @@
46
46
  },
47
47
  {
48
48
  "path": "src/adapters/client-fingerprint.ts",
49
- "sha256": "6494cb40aefb337db3584489d1544d7ae623445dd3c81e5e34a30c77312f4e69"
49
+ "sha256": "70c6e0bf49d942e352396bd6c2983a1456b6ee96497537f4175674e99627223b"
50
+ },
51
+ {
52
+ "path": "src/adapters/cline-pass-deepseek-v4-tool-replay.ts",
53
+ "sha256": "da664327db4e0bae8ab689230deec415ad3f6d0f18c596dcc5b94c8a8d08a345"
50
54
  },
51
55
  {
52
56
  "path": "src/adapters/command-code.ts",
53
- "sha256": "7430d87c3c34961ab02873584605738c65fbf7aca2f87f2a0dcc800c048a0761"
57
+ "sha256": "9b4a9eb9ab8c37541d0478ee50d28ea94b9dee127f75fbc85004ca264af67aaa"
54
58
  },
55
59
  {
56
60
  "path": "src/adapters/cursor.ts",
@@ -66,7 +70,7 @@
66
70
  },
67
71
  {
68
72
  "path": "src/adapters/cursor/cursor-errors.ts",
69
- "sha256": "456766d2be7d4859707c7ae666000f74d81357bde59a2b283027ee256d395b9e"
73
+ "sha256": "aa27cf53056f48c4ed4ccc2262d5a84c0b96b79a3a77db5be7ca27256dfba9c4"
70
74
  },
71
75
  {
72
76
  "path": "src/adapters/cursor/discovery.ts",
@@ -94,7 +98,7 @@
94
98
  },
95
99
  {
96
100
  "path": "src/adapters/cursor/live-models.ts",
97
- "sha256": "48d7e9bf63f512086ba5ec5324e355776948802dec29f30d9403e23d1b5cc6f9"
101
+ "sha256": "d1ded09df979a490d7b4fb2afb87829561fbc021bdc6f67ad0d122febd9b21e4"
98
102
  },
99
103
  {
100
104
  "path": "src/adapters/cursor/live-smoke-gate.ts",
@@ -102,7 +106,7 @@
102
106
  },
103
107
  {
104
108
  "path": "src/adapters/cursor/live-transport.ts",
105
- "sha256": "247e48058e2d22e282d2d49652cde998c5d13f36197898562510d99746993711"
109
+ "sha256": "c614a08f47c3a71d443484002a978a67afb2169a2eeae540291fcc383ff2a969"
106
110
  },
107
111
  {
108
112
  "path": "src/adapters/cursor/mcp-config.ts",
@@ -146,7 +150,7 @@
146
150
  },
147
151
  {
148
152
  "path": "src/adapters/cursor/native-exec.ts",
149
- "sha256": "0a9599735b9d3fc1d623b6595f9044818c161370dd87ae4d17d2e6bae344f01f"
153
+ "sha256": "3824d4ad75f039019fff8749900cc0b2daff6de0c41bb7a64f76eb5e57dd25a6"
150
154
  },
151
155
  {
152
156
  "path": "src/adapters/cursor/protobuf-events.ts",
@@ -154,11 +158,11 @@
154
158
  },
155
159
  {
156
160
  "path": "src/adapters/cursor/protobuf-request.ts",
157
- "sha256": "0d82660ab54e2bc0b29c7b4524bf1a56a55c14c08ce11ed8dc0da4c69ed805bd"
161
+ "sha256": "28e352e4b0080fc09508bf87cce884ecbecb9d1f414e343a9068145affd223d9"
158
162
  },
159
163
  {
160
164
  "path": "src/adapters/cursor/request-builder.ts",
161
- "sha256": "ec30a9988cf25a8bc51b2bb286ce25033b6a86591e5036d8d24b74e79b824704"
165
+ "sha256": "ecfed9de726cd21126f3a90dac958bd8bba6d9feee5cb48966d3c3cf274b31fd"
162
166
  },
163
167
  {
164
168
  "path": "src/adapters/cursor/thread-continuity.ts",
@@ -166,7 +170,7 @@
166
170
  },
167
171
  {
168
172
  "path": "src/adapters/cursor/tool-definitions.ts",
169
- "sha256": "b0e77a16aeb85466052774491c8a4cf3ba3cc5c25bf5c60315efa5a9959d626e"
173
+ "sha256": "632535cff0fb7f893b76631344129d13e45aa40c8e3e062ab0010c57e24448e9"
170
174
  },
171
175
  {
172
176
  "path": "src/adapters/cursor/transport-retry.ts",
@@ -186,7 +190,7 @@
186
190
  },
187
191
  {
188
192
  "path": "src/adapters/google-antigravity-wire.ts",
189
- "sha256": "1fa3361241026926754656cf8300ce513fefac86020d796cb985018192499797"
193
+ "sha256": "671fc197b9ab46800b64a4d51dec8b80c82e9cf841bb2c244f1087a8de592009"
190
194
  },
191
195
  {
192
196
  "path": "src/adapters/google-errors.ts",
@@ -210,7 +214,7 @@
210
214
  },
211
215
  {
212
216
  "path": "src/adapters/google.ts",
213
- "sha256": "fe8f2df66b0df49927c1e9d261ac122020dd56b3a7bca6f77a3fabf34c8183bd"
217
+ "sha256": "e87a1a968d8cb8e2d20e2ca27a6168f4e97b215ca1c9ef3ac46d907c78e760b5"
214
218
  },
215
219
  {
216
220
  "path": "src/adapters/identity.ts",
@@ -278,7 +282,7 @@
278
282
  },
279
283
  {
280
284
  "path": "src/adapters/openai-responses-url.ts",
281
- "sha256": "b3acd48b0b8fcda9f2ebedb51ebfd619e1c1d1adaeca2f6965db853e6ffdc6e4"
285
+ "sha256": "ff38228d2033b9f73dbb011b1d5eb3ac09795519ae8b57958d7d8f6253b4d5e4"
282
286
  },
283
287
  {
284
288
  "path": "src/adapters/openai-responses.ts",
@@ -286,7 +290,7 @@
286
290
  },
287
291
  {
288
292
  "path": "src/adapters/registry.ts",
289
- "sha256": "9a56ee08197bdd5b6ea12fcb847d2a89b60a600ee2536a010f4836784115d2d8"
293
+ "sha256": "74cef1012f4b481109d1ec5119d49c7634237166f5e73424399feada3d03c963"
290
294
  },
291
295
  {
292
296
  "path": "src/adapters/responses-tool-schema.ts",
@@ -302,7 +306,7 @@
302
306
  },
303
307
  {
304
308
  "path": "src/adapters/tool-catalog-nudge.ts",
305
- "sha256": "d315b8113a492794ee1321fd3c69638e52af47b8060682e331243cba10167cef"
309
+ "sha256": "9750e8b4ce000ac452482141bfe27add3577341cc30d1403c05a509bee11044c"
306
310
  },
307
311
  {
308
312
  "path": "src/adapters/upstream-http-error.ts",
@@ -310,7 +314,7 @@
310
314
  },
311
315
  {
312
316
  "path": "src/bridge.ts",
313
- "sha256": "63ccf86cb645d70b1e735633e082b483e41507a443a851d4b1ea76436a9a1e3d"
317
+ "sha256": "5bbfd25c2778989f47c422e57d550b46a6570f9d599e8eee8b189c86fd0cb70c"
314
318
  },
315
319
  {
316
320
  "path": "src/chat/inbound.ts",
@@ -626,7 +630,7 @@
626
630
  },
627
631
  {
628
632
  "path": "src/codex/app-server-processes.ts",
629
- "sha256": "28c7fb91d8fd5c2332738e512b9d87c53ea09843191c145b0801d912e124fe9e"
633
+ "sha256": "0218c4e432ad02a814d818e04fc5ba8c36864f8fcfd592ef674f9c476e56cb77"
630
634
  },
631
635
  {
632
636
  "path": "src/codex/app-server-restart-service.ts",
@@ -702,7 +706,7 @@
702
706
  },
703
707
  {
704
708
  "path": "src/codex/catalog/provider-fetch.ts",
705
- "sha256": "bfb4a4c32de3a514190c8bf4fcb660312a64a8e7d32ef9ac83106f999f8c639d"
709
+ "sha256": "d800963913619d3e02db4fb616795cac93c336ac18a18c6e24e873e71a6d2255"
706
710
  },
707
711
  {
708
712
  "path": "src/codex/catalog/sync.ts",
@@ -1022,7 +1026,7 @@
1022
1026
  },
1023
1027
  {
1024
1028
  "path": "src/config.ts",
1025
- "sha256": "3552e7f943e5bcd6af57acfbe0bd2316d9ab4a060e32e6ca01adac97d27960d4"
1029
+ "sha256": "2297577079aed2e89e4603050402629fea2df094978ff24c0db5102ca8da91ae"
1026
1030
  },
1027
1031
  {
1028
1032
  "path": "src/generated/model-metadata.ts",
@@ -1854,7 +1858,7 @@
1854
1858
  },
1855
1859
  {
1856
1860
  "path": "src/lib/windows-elevation.ts",
1857
- "sha256": "b0aab4ba8c7fe82e101669545881634ed75b59ca024522ad21e8c613c7a6cf99"
1861
+ "sha256": "168ec95934c25036ec9056f83f89427a82ee2bcd83d098bd4419fcb27051e4ec"
1858
1862
  },
1859
1863
  {
1860
1864
  "path": "src/lib/windows-secret-acl.ts",
@@ -1926,7 +1930,7 @@
1926
1930
  },
1927
1931
  {
1928
1932
  "path": "src/oauth/google-antigravity.ts",
1929
- "sha256": "a46a2aa7c5aafc51d631e1d02d84f90e2b6fc32c8000c7a84dd1c5a76018635c"
1933
+ "sha256": "ca475ef54f4ed2a36734b560fd29686a33dd11aa45643fe36a9fd3c92c1a6678"
1930
1934
  },
1931
1935
  {
1932
1936
  "path": "src/oauth/health.ts",
@@ -2002,7 +2006,7 @@
2002
2006
  },
2003
2007
  {
2004
2008
  "path": "src/providers/antigravity-models.ts",
2005
- "sha256": "1382f985bf4fdf525fb4425d5040e7244460f441f492b22ddc493226e5b778d1"
2009
+ "sha256": "3b70505caffbf4b42a1574f8824aa3250d3caee5db820f45b45945381dd0298d"
2006
2010
  },
2007
2011
  {
2008
2012
  "path": "src/providers/api-keys.ts",
@@ -2026,7 +2030,7 @@
2026
2030
  },
2027
2031
  {
2028
2032
  "path": "src/providers/derive.ts",
2029
- "sha256": "c099e125978b81ac4415d2ba5fb0dd69c1f974e21301b992811b63dfb3c0b94d"
2033
+ "sha256": "babec953990d4045c3861c102a4ed3e2c4654b03b6d707d179bfeefeefd5e9c8"
2030
2034
  },
2031
2035
  {
2032
2036
  "path": "src/providers/free-directory.ts",
@@ -2142,7 +2146,7 @@
2142
2146
  },
2143
2147
  {
2144
2148
  "path": "src/responses/parser.ts",
2145
- "sha256": "e8fffd98b5bac31773dff5c22c65ef5cf39d535c578ec708700aa6d25e2d9e71"
2149
+ "sha256": "f84fcb37def007781eb71081d3148f4b0f72eca6aebcea7e7a1a67808d014d69"
2146
2150
  },
2147
2151
  {
2148
2152
  "path": "src/responses/provider-opaque-metadata.ts",
@@ -2154,7 +2158,7 @@
2154
2158
  },
2155
2159
  {
2156
2160
  "path": "src/responses/reasoning-replay-cache.ts",
2157
- "sha256": "28fa9e047741251734d3c39a6183e2465cae3c610e31de4f2a1bd4e4333e8e00"
2161
+ "sha256": "c81d1e2173c4c48215468989dafa4ec74a9a01ab1bbbb4556d5d25ccf63739d3"
2158
2162
  },
2159
2163
  {
2160
2164
  "path": "src/responses/schema.ts",
@@ -2170,15 +2174,19 @@
2170
2174
  },
2171
2175
  {
2172
2176
  "path": "src/responses/thought-signature-replay.ts",
2173
- "sha256": "fa1a7c06af55241083f7fb31638e12630c444c72a3d9879ce02ca7bdef2fb705"
2177
+ "sha256": "dc2464036c9d0c78764ac7b2e97e86b77449dd31e9f98cf0c29b5a7d60134976"
2174
2178
  },
2175
2179
  {
2176
2180
  "path": "src/responses/tool-groups.ts",
2177
2181
  "sha256": "b2750b33ec7bb5a7f76f27584bf99372fac4f253dd287849b2f3f9821db574cd"
2178
2182
  },
2183
+ {
2184
+ "path": "src/responses/truncated-stop-reason.ts",
2185
+ "sha256": "44a1876c045a63f9275410fb9169d2c47ce0c1de309e9cc00a0f51c72698a6dd"
2186
+ },
2179
2187
  {
2180
2188
  "path": "src/router.ts",
2181
- "sha256": "f365eb302f068368ce2966a7232b2ddd8ce1fc00ad2751df46feb06009633a07"
2189
+ "sha256": "26a672d4df978dd7c9764d7533ccc54a9bded6daf4a90fa3ebf41e126d106f67"
2182
2190
  },
2183
2191
  {
2184
2192
  "path": "src/routing/analytics.ts",
@@ -2422,7 +2430,7 @@
2422
2430
  },
2423
2431
  {
2424
2432
  "path": "src/server/management/provider-routes.ts",
2425
- "sha256": "5671232af392b8ba84ecc516c99d33b3cd294bfbd3fd9c5df04cf99b32d04049"
2433
+ "sha256": "1a2217a7cdaee6945ef90221f7b9344fc6d3a04e7750092b6c716bb2acbcef47"
2426
2434
  },
2427
2435
  {
2428
2436
  "path": "src/server/management/request-history-routes.ts",
@@ -2510,7 +2518,7 @@
2510
2518
  },
2511
2519
  {
2512
2520
  "path": "src/server/request-log.ts",
2513
- "sha256": "e7111d92152cdedf0e771d719bddee0150124d14bcac13610662dcbed3b75869"
2521
+ "sha256": "17c4594009bbacdd95465933ab48b0a5a31e85d80e16de86a1fd1638b2c46510"
2514
2522
  },
2515
2523
  {
2516
2524
  "path": "src/server/responses-custom-tool-repair.ts",
@@ -2566,7 +2574,7 @@
2566
2574
  },
2567
2575
  {
2568
2576
  "path": "src/server/responses/core.ts",
2569
- "sha256": "fbb0b59c9a67f10d97ec28ac48b7c2c168c7ec359f43c336a11913148ae1a11a"
2577
+ "sha256": "1a2fd4ea5e0b1f9b2208cd0365986a465039f262bfd43f9dca56dc43780e6e8a"
2570
2578
  },
2571
2579
  {
2572
2580
  "path": "src/server/responses/empty-completion-guard.ts",
@@ -2726,7 +2734,7 @@
2726
2734
  },
2727
2735
  {
2728
2736
  "path": "src/types.ts",
2729
- "sha256": "a2b31962f7d7f54f60b1cb9ed1066eeb5b0fb267cd0d3e4053eea6501b53046e"
2737
+ "sha256": "d740769ad835349fbc1d92d1c4186be92d2dca5d9902d0f9bc3eac251644655e"
2730
2738
  },
2731
2739
  {
2732
2740
  "path": "src/update/badge.ts",
@@ -640,15 +640,21 @@ export function runWindowsElevatedScheduledTaskRegistration(
640
640
  xml: string,
641
641
  ): Promise<number> {
642
642
  const xmlBase64 = Buffer.from(xml, "utf16le").toString("base64");
643
+ const powerShellPath = windowsPowerShell();
644
+ const powerShellDirectory = powerShellPath.replace(/[\\/][^\\/]+$/, "");
645
+ const scheduledTasksModule = `${powerShellDirectory}\\Modules\\ScheduledTasks\\ScheduledTasks.psd1`;
643
646
  const inner = [
644
647
  `$taskName = ${psSingleQuote(taskName)}`,
645
648
  `$xmlBase64 = ${psSingleQuote(xmlBase64)}`,
646
649
  "$xml = [Text.Encoding]::Unicode.GetString([Convert]::FromBase64String($xmlBase64))",
647
- "Register-ScheduledTask -TaskName $taskName -Xml $xml -Force -ErrorAction Stop | Out-Null",
650
+ `$module = Microsoft.PowerShell.Core\\Import-Module -Name ${psSingleQuote(scheduledTasksModule)} -PassThru -Force -ErrorAction Stop`,
651
+ "$registerTask = $module.ExportedCommands['Register-ScheduledTask']",
652
+ "if ($null -eq $registerTask) { throw 'Trusted ScheduledTasks module does not export Register-ScheduledTask.' }",
653
+ "& $registerTask -TaskName $taskName -Xml $xml -Force -ErrorAction Stop | Out-Null",
648
654
  ].join("; ");
649
655
  const encodedCommand = Buffer.from(inner, "utf16le").toString("base64");
650
656
  const script = [
651
- `$p = Start-Process -FilePath ${psSingleQuote(windowsPowerShell())}`,
657
+ `$p = Start-Process -FilePath ${psSingleQuote(powerShellPath)}`,
652
658
  ` -ArgumentList ${psSingleQuote(buildWindowsElevatedArgumentList([
653
659
  "-NoProfile",
654
660
  "-NonInteractive",
@@ -12,7 +12,7 @@
12
12
  import { OAuthCallbackFlow, type OAuthCallbackFlowOptions } from "./callback-server";
13
13
  import { generatePKCE } from "./pkce";
14
14
  import type { OAuthController, OAuthCredentials } from "./types";
15
- import { antigravityUserAgent, ANTIGRAVITY_GOOG_API_CLIENT_UA } from "../adapters/client-fingerprint";
15
+ import { antigravityUserAgent, ANTIGRAVITY_GOOG_API_CLIENT_UA, ANTIGRAVITY_IDE_VERSION } from "../adapters/client-fingerprint";
16
16
 
17
17
  const CLIENT_ID = process.env.GOOGLE_ANTIGRAVITY_CLIENT_ID
18
18
  || "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com";
@@ -111,7 +111,12 @@ async function onboardProject(accessToken: string, signal?: AbortSignal): Promis
111
111
  const response = await fetch(`${DAILY_API}/${API_VERSION}:onboardUser`, {
112
112
  method: "POST",
113
113
  headers: { Authorization: `Bearer ${accessToken}`, Accept: "*/*", "Content-Type": "application/json", "User-Agent": antigravityUserAgent(), "x-goog-api-client": ANTIGRAVITY_GOOG_API_CLIENT_UA },
114
- body: JSON.stringify({ tier_id: "free-tier", metadata: { ide_type: "ANTIGRAVITY", ide_name: "antigravity", ide_version: antigravityUserAgent() } }),
114
+ // `ide_version` is a version, not a User-Agent. `antigravityUserAgent()` returns the whole
115
+ // header — `antigravity/ide/2.5.5 (aidev_client; os_type=...; arch=...)` — so onboarding was
116
+ // sending a parenthesized UA string in a field the real client fills with `2.5.5`. It is a
117
+ // fingerprint mismatch rather than a crash, which is why nothing failed: the request still
118
+ // succeeds, it just does not look like Antigravity.
119
+ body: JSON.stringify({ tier_id: "free-tier", metadata: { ide_type: "ANTIGRAVITY", ide_name: "antigravity", ide_version: ANTIGRAVITY_IDE_VERSION } }),
115
120
  signal: requestSignal(signal),
116
121
  });
117
122
  if (!response.ok) {
@@ -1,4 +1,5 @@
1
1
  import { isValidModelDiscoveryModelId, MODEL_DISCOVERY_MAX_MODELS } from "./model-discovery-limits";
2
+ import { isModelCacheGenerationCurrent } from "../codex/model-cache";
2
3
 
3
4
  // Google Antigravity (Cloud Code Assist) bundled model list.
4
5
  //
@@ -74,8 +75,12 @@ const ANTIGRAVITY_DISCOVERY_EFFORTS = ["low", "medium", "high"] as const;
74
75
 
75
76
  function pickerModelIdForDiscoveredWireId(
76
77
  wireId: string,
78
+ info: Record<string, unknown>,
77
79
  available: ReadonlyMap<string, Record<string, unknown>>,
78
80
  ): string {
81
+ const displayModelId = antigravityDisplayModelId(info.displayName, wireId);
82
+ if (displayModelId) return displayModelId;
83
+
79
84
  const explicitPickerId = Object.hasOwn(ANTIGRAVITY_PICKER_MODEL_BY_WIRE_ID, wireId)
80
85
  ? ANTIGRAVITY_PICKER_MODEL_BY_WIRE_ID[wireId]
81
86
  : undefined;
@@ -240,6 +245,8 @@ export const ANTIGRAVITY_MODEL_INPUT_MODALITIES: Record<string, string[]> = {
240
245
 
241
246
  export interface AntigravityAvailableModel {
242
247
  id: string;
248
+ /** CCA model id used by the agent envelope when `id` comes from display metadata. */
249
+ wireModelId: string;
243
250
  contextWindow?: number;
244
251
  inputModalities?: string[];
245
252
  }
@@ -254,6 +261,100 @@ function antigravityPositiveInteger(value: unknown): number | undefined {
254
261
  return typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : undefined;
255
262
  }
256
263
 
264
+ interface DiscoveredWireModelMapping {
265
+ readonly models: ReadonlyMap<string, string>;
266
+ readonly generation?: { provider: string; cacheGeneration: string };
267
+ }
268
+
269
+ const discoveredWireModelsByBaseUrl = new Map<string, DiscoveredWireModelMapping>();
270
+
271
+ /**
272
+ * Strip trailing slashes without a backtracking regex.
273
+ *
274
+ * `/\/+$/` is polynomial-ReDoS on attacker-influenceable input (CodeQL js/polynomial-redos):
275
+ * a long run of slashes makes the engine retry every suffix. The base URL comes from provider
276
+ * config, which is not hostile in the ordinary case — but "not hostile today" is a property of
277
+ * the caller, not of this function, and a linear scan costs nothing.
278
+ */
279
+ function stripTrailingSlashes(value: string): string {
280
+ let end = value.length;
281
+ while (end > 0 && value.charCodeAt(end - 1) === 47) end -= 1;
282
+ return end === value.length ? value : value.slice(0, end);
283
+ }
284
+
285
+ function antigravityBaseUrlKey(baseUrl: string | undefined): string | undefined {
286
+ if (typeof baseUrl !== "string" || !baseUrl.trim()) return undefined;
287
+ const trimmed = stripTrailingSlashes(baseUrl.trim());
288
+ try {
289
+ const url = new URL(trimmed);
290
+ url.hash = "";
291
+ url.search = "";
292
+ return stripTrailingSlashes(url.toString()).toLowerCase();
293
+ } catch {
294
+ return trimmed.toLowerCase();
295
+ }
296
+ }
297
+
298
+ /** Remember the wire ids returned by one live CCA discovery for request routing. */
299
+ export function registerAntigravityDiscoveredWireModels(
300
+ baseUrl: string | undefined,
301
+ models: readonly AntigravityAvailableModel[],
302
+ generation?: { provider: string; cacheGeneration: string },
303
+ ): void {
304
+ const key = antigravityBaseUrlKey(baseUrl);
305
+ if (!key) return;
306
+ const wireModels = new Map<string, string>();
307
+ for (const model of models) wireModels.set(model.id, model.wireModelId);
308
+ discoveredWireModelsByBaseUrl.set(key, {
309
+ models: wireModels,
310
+ ...(generation ? { generation } : {}),
311
+ });
312
+ }
313
+
314
+ function discoveredAntigravityWireModelId(
315
+ modelId: string,
316
+ baseUrl: string | undefined,
317
+ ): string | undefined {
318
+ const key = antigravityBaseUrlKey(baseUrl);
319
+ if (!key) return undefined;
320
+ const mapping = discoveredWireModelsByBaseUrl.get(key);
321
+ if (!mapping) return undefined;
322
+ if (mapping.generation
323
+ && !isModelCacheGenerationCurrent(mapping.generation.provider, mapping.generation.cacheGeneration)) {
324
+ discoveredWireModelsByBaseUrl.delete(key);
325
+ return undefined;
326
+ }
327
+ return mapping.models.get(modelId);
328
+ }
329
+
330
+ /**
331
+ * Convert the CCA display label used by `agy` into its public model selector.
332
+ *
333
+ * The wire id is authoritative for requests, while the label is authoritative for the
334
+ * user-facing selector when Google has renamed or re-tiered a model. Keep both instead
335
+ * of maintaining a provider-specific list of known model names.
336
+ */
337
+ function antigravityDisplayModelId(displayName: unknown, wireId: string): string | undefined {
338
+ if (typeof displayName !== "string") return undefined;
339
+ const label = displayName.trim();
340
+ if (!label || label.length > 512) return undefined;
341
+ const slug = (replaceDots: boolean): string => label
342
+ .normalize("NFKC")
343
+ .toLowerCase()
344
+ .replace(replaceDots ? /\./g : /\s+/g, replaceDots ? "-" : " ")
345
+ .replace(/[^a-z0-9.-]+/g, "-")
346
+ .replace(/-+/g, "-")
347
+ .replace(/^-|-$/g, "");
348
+ const preserved = slug(false);
349
+ const compact = slug(true);
350
+ if (!isValidModelDiscoveryModelId(preserved) && !isValidModelDiscoveryModelId(compact)) return undefined;
351
+ if (preserved === wireId || compact === wireId
352
+ || preserved === `${wireId}-thinking` || compact === `${wireId}-thinking`) {
353
+ return wireId;
354
+ }
355
+ return isValidModelDiscoveryModelId(preserved) ? preserved : compact;
356
+ }
357
+
257
358
  /**
258
359
  * Extract the CCA models that are valid for agent requests. The endpoint also returns tab,
259
360
  * command, commit-message, transcription, and standalone image-generation models; those are not
@@ -288,13 +389,6 @@ export function parseAntigravityAvailableModels(
288
389
  }
289
390
  }
290
391
  }
291
- // This model is exposed by Antigravity's agent chat surface even though it is grouped under
292
- // image generation in the discovery response.
293
- if (Array.isArray(body.imageGenerationModelIds)
294
- && body.imageGenerationModelIds.includes("gemini-3.1-flash-image")) {
295
- if (ids.length >= limit) return null;
296
- ids.push("gemini-3.1-flash-image");
297
- }
298
392
  // Newer CCA responses identify tiered Flash models through this index instead of
299
393
  // adding their synthetic wire ids to agentModelSorts.
300
394
  const tieredModelIds = antigravityRecord(body.tieredModelIds);
@@ -305,6 +399,12 @@ export function parseAntigravityAvailableModels(
305
399
  || !Object.hasOwn(models, id)
306
400
  || !antigravityRecord(models[id])
307
401
  || ids.length >= limit) return null;
402
+ const baseId = id.endsWith("-tiered") ? id.slice(0, -"-tiered".length) : id;
403
+ if (ids.some(agentId =>
404
+ agentId === id
405
+ || agentId === baseId
406
+ || ANTIGRAVITY_DISCOVERY_EFFORTS.some(effort => agentId === `${baseId}-${effort}`)
407
+ )) continue;
308
408
  ids.push(id);
309
409
  }
310
410
  }
@@ -313,23 +413,18 @@ export function parseAntigravityAvailableModels(
313
413
  for (const wireId of ids) {
314
414
  const info = antigravityRecord(models[wireId]);
315
415
  if (!info || available.has(wireId)) continue;
316
- // Legacy compatibility aliases are deliberately routed to newer wire ids for saved
317
- // selections. They are not safe as independently discovered picker rows.
318
- const alias = Object.hasOwn(ANTIGRAVITY_MODEL_ALIASES, wireId)
319
- ? ANTIGRAVITY_MODEL_ALIASES[wireId]
320
- : undefined;
321
- if (alias && alias !== wireId) continue;
322
416
  available.set(wireId, info);
323
417
  }
324
418
 
325
419
  const out: AntigravityAvailableModel[] = [];
326
420
  const seen = new Set<string>();
327
421
  for (const [wireId, info] of available) {
328
- const id = pickerModelIdForDiscoveredWireId(wireId, available);
422
+ const id = pickerModelIdForDiscoveredWireId(wireId, info, available);
329
423
  if (seen.has(id)) continue;
330
424
  seen.add(id);
331
425
  out.push({
332
426
  id,
427
+ wireModelId: wireId,
333
428
  ...(antigravityPositiveInteger(info.maxTokens) ? { contextWindow: antigravityPositiveInteger(info.maxTokens) } : {}),
334
429
  // Tri-state, deliberately not a ternary: `true` asserts image support,
335
430
  // `false` asserts against it, and ABSENT is unknown. Collapsing absent into
@@ -347,7 +442,9 @@ export function parseAntigravityAvailableModels(
347
442
  return out;
348
443
  }
349
444
 
350
- export function resolveAntigravityWireModelId(modelId: string): string {
445
+ export function resolveAntigravityWireModelId(modelId: string, baseUrl?: string): string {
446
+ const discovered = discoveredAntigravityWireModelId(modelId, baseUrl);
447
+ if (discovered) return discovered;
351
448
  return Object.hasOwn(ANTIGRAVITY_MODEL_ALIASES, modelId)
352
449
  ? ANTIGRAVITY_MODEL_ALIASES[modelId]
353
450
  : modelId;
@@ -380,7 +477,19 @@ export function retiredAntigravityFlashTier(modelId: string): string | undefined
380
477
  export function resolveAntigravityEffortWireModel(
381
478
  modelId: string,
382
479
  effort?: string,
480
+ baseUrl?: string,
383
481
  ): { wireModelId: string; thinkingLevel?: string } {
482
+ const discoveredWireModelId = discoveredAntigravityWireModelId(modelId, baseUrl);
483
+ if (discoveredWireModelId && (discoveredWireModelId !== modelId || isAntigravitySuffixModelId(modelId))) {
484
+ const defaultLevel = ANTIGRAVITY_THINKING_LEVEL_MODELS[modelId];
485
+ return {
486
+ wireModelId: discoveredWireModelId,
487
+ ...(defaultLevel
488
+ ? { thinkingLevel: effort ? resolveAntigravityThinkingLevel(effort) ?? defaultLevel : defaultLevel }
489
+ : {}),
490
+ };
491
+ }
492
+
384
493
  // Rule 0: retired Flash id — Google has taken the wire id offline, so route to the
385
494
  // current generation and carry the tier the retired id encoded. This runs BEFORE the
386
495
  // suffix check because those ids are aliases, and rule 1 would drop the tier.
@@ -394,7 +503,7 @@ export function resolveAntigravityEffortWireModel(
394
503
 
395
504
  // Rule 1: suffix/compat alias — suffix IS the effort.
396
505
  if (isAntigravitySuffixModelId(modelId)) {
397
- return { wireModelId: resolveAntigravityWireModelId(modelId) };
506
+ return { wireModelId: resolveAntigravityWireModelId(modelId, baseUrl) };
398
507
  }
399
508
 
400
509
  // Rule 1b: single-wire-id Gemini model whose tiers ride on thinkingLevel. Without
@@ -424,7 +533,7 @@ export function resolveAntigravityEffortWireModel(
424
533
  }
425
534
 
426
535
  // Rule 5: everything else.
427
- return { wireModelId: resolveAntigravityWireModelId(modelId) };
536
+ return { wireModelId: resolveAntigravityWireModelId(modelId, baseUrl) };
428
537
  }
429
538
 
430
539
 
@@ -393,6 +393,14 @@ function enrichReasoningSummariesByDestination(prov: OcxProviderConfig): void {
393
393
  applyReasoningSummaryDefaults(prov, destination?.modelSupportsReasoningSummaries);
394
394
  }
395
395
 
396
+ /** Repair the exact low-only ClinePass ladder generated by older key-login presets. */
397
+ export function hasLegacyClinePassReasoningEfforts(name: string, prov: OcxProviderConfig): boolean {
398
+ return name === "cline-pass"
399
+ && prov.reasoningWireFormat === "gateway-object"
400
+ && prov.reasoningEfforts?.length === 1
401
+ && prov.reasoningEfforts[0] === "low";
402
+ }
403
+
396
404
  export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig): void {
397
405
  const entry = PROVIDER_REGISTRY.find(row => row.id === name);
398
406
  if (!entry || !providerMatchesRegistryTransportWithStaticGuards(name, prov)) {
@@ -426,7 +434,9 @@ export function enrichProviderFromRegistry(name: string, prov: OcxProviderConfig
426
434
  if (seed.modelInputModalities) prov.modelInputModalities = fillRecordOfArrays(seed.modelInputModalities, prov.modelInputModalities);
427
435
  if (prov.defaultMaxOutputTokens === undefined && seed.defaultMaxOutputTokens !== undefined) prov.defaultMaxOutputTokens = seed.defaultMaxOutputTokens;
428
436
  if (!prov.modelMaxOutputTokens && seed.modelMaxOutputTokens) prov.modelMaxOutputTokens = { ...seed.modelMaxOutputTokens };
429
- if (!prov.reasoningEfforts && seed.reasoningEfforts) prov.reasoningEfforts = [...seed.reasoningEfforts];
437
+ if ((!prov.reasoningEfforts || hasLegacyClinePassReasoningEfforts(name, prov)) && seed.reasoningEfforts) {
438
+ prov.reasoningEfforts = [...seed.reasoningEfforts];
439
+ }
430
440
  if (!prov.modelReasoningEfforts && seed.modelReasoningEfforts) prov.modelReasoningEfforts = cloneRecordOfArrays(seed.modelReasoningEfforts);
431
441
  if (!prov.modelDefaultReasoningEfforts && seed.modelDefaultReasoningEfforts) prov.modelDefaultReasoningEfforts = { ...seed.modelDefaultReasoningEfforts };
432
442
  if (!prov.reasoningEffortMap && seed.reasoningEffortMap) prov.reasoningEffortMap = { ...seed.reasoningEffortMap };
@@ -171,6 +171,10 @@ function buildTools(tools: unknown[] | undefined): OcxTool[] | undefined {
171
171
  };
172
172
  for (const t of tools) {
173
173
  if (!isObj(t)) continue;
174
+ if (t.type === "function" && isObj(t.function) && typeof t.function.name === "string" && t.function.name.length > 0) {
175
+ pushFn(t.function as Record<string, unknown>);
176
+ continue;
177
+ }
174
178
  if (t.type === "function" && typeof t.name === "string") {
175
179
  pushFn(t);
176
180
  } else if (t.type === "namespace" && Array.isArray(t.tools)) {
@@ -19,7 +19,7 @@
19
19
  * long-lived proxy cannot grow without limit.
20
20
  */
21
21
 
22
- import { createHmac, randomBytes } from "node:crypto";
22
+ import { createHash, createHmac, randomBytes } from "node:crypto";
23
23
  import type {
24
24
  OcxProviderConfig,
25
25
  OcxReasoningReplayIdentity,
@@ -108,6 +108,21 @@ export function reasoningReplayDestinationIdentity(baseUrl: string | undefined):
108
108
  return `destination:${processLocalIdentity("destination", canonical)}`;
109
109
  }
110
110
 
111
+ /**
112
+ * The same destination identity, but stable across restarts.
113
+ *
114
+ * The process-local form above is keyed by `randomBytes(32)` minted at module load, which
115
+ * is correct for an in-memory cache and fatal for a durable one: every key would change on
116
+ * restart and the store would silently stop matching anything. A plain digest of the same
117
+ * canonical URL is equally non-reversible for this purpose — the input is a configured
118
+ * endpoint, not a secret — and needs no persisted salt or new on-disk state.
119
+ */
120
+ export function durableReplayDestinationIdentity(baseUrl: string | undefined): string | undefined {
121
+ if (!nonEmpty(baseUrl)) return undefined;
122
+ const canonical = baseUrl.trim().replace(/\/+$/, "");
123
+ return `destination:${createHash("sha256").update("destination\0").update(canonical).digest("hex")}`;
124
+ }
125
+
111
126
  /** Produce a non-reversible process-local identity for credential material. */
112
127
  export function reasoningReplayCredentialIdentity(
113
128
  kind: "key" | "oauth" | "codex",
@@ -27,6 +27,11 @@ import type { OcxProviderOpaqueToolCallMetadata, OcxReasoningReplayScopeRef } fr
27
27
  import { isCarryableSignature, responsesExtraContentFromProviderMetadata } from "./provider-opaque-metadata";
28
28
 
29
29
  const STORE_FILE_NAME = "thought-signature-replay.json";
30
+ /**
31
+ * Bumped whenever `keyFor` changes shape. v3 added the durable destination identity, so a
32
+ * v2 file's keys can never match and are dropped on load instead of aging out invisibly.
33
+ */
34
+ const STORE_VERSION = 3;
30
35
 
31
36
  /** Bound on remembered entries; real signatures are a few hundred bytes, so this stays small. */
32
37
  const MAX_ENTRIES = 16_384;
@@ -83,6 +88,11 @@ function keyFor(callId: string, scope: OcxReasoningReplayScopeRef | undefined):
83
88
  return JSON.stringify([
84
89
  scope.clientThreadId,
85
90
  identity.providerName,
91
+ // Destination, unlike the credential identity, has a restart-stable form: it is a
92
+ // configured endpoint rather than a secret, so a plain digest works where the
93
+ // reasoning cache's randomBytes-keyed HMAC cannot. Without it, one provider NAME
94
+ // serving two endpoints shares signatures across both.
95
+ identity.providerDestinationDurableIdentity ?? "destination:unknown",
86
96
  identity.adapterName,
87
97
  identity.modelId,
88
98
  callId,
@@ -103,6 +113,12 @@ function load(): void {
103
113
  if (typeof parsed !== "object" || parsed === null || !Array.isArray((parsed as { entries?: unknown }).entries)) {
104
114
  return;
105
115
  }
116
+ // The version was written but never read, so a key-shape change could not be
117
+ // announced — old entries simply went dead and aged out on TTL, which is silent and
118
+ // indistinguishable from a store that is not working. Reading it makes a shape change
119
+ // an explicit drop: entries keyed by an older scheme are discarded on load rather than
120
+ // lingering as permanent misses.
121
+ if ((parsed as { version?: unknown }).version !== STORE_VERSION) return;
106
122
  const nowMs = Date.now();
107
123
  for (const entry of (parsed as { entries: unknown[] }).entries) {
108
124
  if (typeof entry !== "object" || entry === null) continue;
@@ -141,7 +157,7 @@ function persist(): Promise<void> {
141
157
  persistChain = persistChain
142
158
  .then(async () => {
143
159
  const snapshot = JSON.stringify({
144
- version: 2,
160
+ version: STORE_VERSION,
145
161
  entries: [...entries].map(([key, entry]) => ({ key, sig: entry.sig, savedAt: entry.savedAt })),
146
162
  });
147
163
  await atomicWriteFileAsync(storePath(), snapshot);