cartethyia 2.7.5 → 2.7.6

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 (263) hide show
  1. package/.env.example +4 -1
  2. package/README.md +1 -1
  3. package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
  5. package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
  6. package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
  7. package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
  8. package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
  11. package/dist/dashboard/assets/{ConsoleLogPage-D5rol2j2.js → ConsoleLogPage-KRYjHZsX.js} +1 -1
  12. package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
  17. package/dist/dashboard/assets/{InjectSystemPromptPanel-B5nZvmf0.js → InjectSystemPromptPanel-d5v3K_Yc.js} +245 -2
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
  19. package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
  20. package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-DnQPSIA8.js → ModelPicker-C6WlpU5E.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-5SnUSy3j.js → ProxyPage-DxjvUzSD.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
  35. package/dist/dashboard/assets/{SettingsPage-BJtRZ4qD.js → SettingsPage-BW4AKj2H.js} +1 -1
  36. package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
  37. package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
  38. package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-BfAb5-o_.js → StudioPage-DRPavCdU.js} +4 -4
  40. package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
  41. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
  42. package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
  43. package/dist/dashboard/assets/{UsagePage-BNzeZ2Vk.js → UsagePage-B7M7LvMM.js} +13 -13
  44. package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
  45. package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
  46. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  47. package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
  48. package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
  49. package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
  50. package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
  51. package/dist/dashboard/assets/{assert-C0K-c4eo.js → assert-CidQVoNo.js} +1 -1
  52. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  53. package/dist/dashboard/assets/{badge-DfbhQU4k.js → badge-BaX2GTrn.js} +1 -1
  54. package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
  55. package/dist/dashboard/assets/{banner-BllT4LcF.js → banner-DFH4Bkte.js} +1 -1
  56. package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
  57. package/dist/dashboard/assets/{card-CtkX66Wd.js → card-CxDw0lK6.js} +1 -1
  58. package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
  59. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
  60. package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
  61. package/dist/dashboard/assets/{cli-tools-CQXVMn4P.js → cli-tools-Bwv5-zGZ.js} +1 -1
  62. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  63. package/dist/dashboard/assets/{clipboard-button-BQqBQd2n.js → clipboard-button-DfS1ntiq.js} +1 -1
  64. package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
  65. package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
  66. package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
  67. package/dist/dashboard/assets/{drawer-BrTEZ8zu.js → drawer-QqPul6sC.js} +1 -1
  68. package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
  69. package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
  70. package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
  71. package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
  72. package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
  73. package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
  74. package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
  75. package/dist/dashboard/assets/{formatters-BAACA727.js → formatters-u7JPB5BX.js} +1 -1
  76. package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
  77. package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
  78. package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
  79. package/dist/dashboard/assets/{index-FQlj0HUT.js → index-Ko4dLRnV.js} +2 -2
  80. package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
  81. package/dist/dashboard/assets/{inline-C4gU5iOL.js → inline-CHJQwxLa.js} +1 -1
  82. package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
  83. package/dist/dashboard/assets/{input-BHHX_Tmm.js → input-BlmlizVV.js} +1 -1
  84. package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
  85. package/dist/dashboard/assets/{layout-CajrYUnD.js → layout-Bz93I-yE.js} +1 -1
  86. package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
  87. package/dist/dashboard/assets/{list-row-Bx2I7XUe.js → list-row-Ml16KCfu.js} +1 -1
  88. package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
  89. package/dist/dashboard/assets/{live-DNNqRbj2.js → live-7_QpnYGu.js} +1 -1
  90. package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
  91. package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
  92. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  93. package/dist/dashboard/assets/{network-C5UBACrJ.js → network-CuMDaTzT.js} +1 -1
  94. package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
  95. package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
  96. package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
  97. package/dist/dashboard/assets/{pagination-Xsi9Qx04.js → pagination-B5aOoweZ.js} +1 -1
  98. package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
  99. package/dist/dashboard/assets/providers/zai.webp +0 -0
  100. package/dist/dashboard/assets/providers/zcode.webp +0 -0
  101. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
  102. package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
  103. package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
  104. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  105. package/dist/dashboard/assets/{routing-in0f_IfK.js → routing-fnCsxgNO.js} +1 -1
  106. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  107. package/dist/dashboard/assets/{select-BLULNqTp.js → select-BHqpd-bg.js} +2 -2
  108. package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
  109. package/dist/dashboard/assets/{settings-CcGczj6I.js → settings-BxslsErn.js} +1 -1
  110. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  111. package/dist/dashboard/assets/{stack-D2lIR0tg.js → stack-j7cXjgYX.js} +1 -1
  112. package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
  113. package/dist/dashboard/assets/{state-DTARjfiW.js → state-DHmEnLa2.js} +1 -1
  114. package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
  115. package/dist/dashboard/assets/{switch-c0oPSnqW.js → switch-DmZcyTIC.js} +1 -1
  116. package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
  117. package/dist/dashboard/assets/{useMutation-m8WGNPrz.js → useMutation-CbCP4w1o.js} +1 -1
  118. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  119. package/dist/dashboard/index.html +2 -2
  120. package/dist/dashboard/index.html.gz +0 -0
  121. package/dist/dashboard/precompressed-assets.json +57 -57
  122. package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
  123. package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
  124. package/package.json +1 -1
  125. package/src/boot/dependencies.ts +10 -2
  126. package/src/config/providers.ts +13 -7
  127. package/src/config/spec.ts +18 -11
  128. package/src/config/usage.ts +27 -0
  129. package/src/console/api-keys/routes.ts +19 -23
  130. package/src/console/cli-tools/contracts.ts +39 -295
  131. package/src/console/cli-tools/fs-ops.ts +29 -0
  132. package/src/console/cli-tools/injectors/claude.ts +3 -3
  133. package/src/console/cli-tools/injectors/codex.ts +6 -6
  134. package/src/console/cli-tools/injectors/driver.ts +8 -105
  135. package/src/console/cli-tools/injectors/kilo.ts +146 -21
  136. package/src/console/cli-tools/injectors/opencode.ts +28 -5
  137. package/src/console/cli-tools/routes.ts +6 -0
  138. package/src/console/cli-tools/service.ts +7 -6
  139. package/src/console/observability/contracts.ts +6 -0
  140. package/src/console/observability/store.ts +3 -0
  141. package/src/console/share/share-stats.ts +7 -9
  142. package/src/observability/event-log.ts +7 -0
  143. package/src/observability/event-query.ts +2 -0
  144. package/src/observability/telemetry-buffer.ts +7 -0
  145. package/src/persistence/schema.ts +8 -0
  146. package/src/persistence/stores/api-key-store.ts +19 -1
  147. package/src/protocol/dsml.ts +5 -76
  148. package/src/providers/adapters/compatible-adapter.ts +29 -1
  149. package/src/providers/default-registry.ts +12 -2
  150. package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -14
  151. package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
  152. package/src/providers/integrations/antigravity/antigravity.ts +20 -11
  153. package/src/providers/integrations/cursor/cursor.ts +2 -3
  154. package/src/providers/integrations/github/github-discovery.ts +15 -2
  155. package/src/providers/integrations/github/github-oauth.ts +178 -9
  156. package/src/providers/integrations/github/github.ts +95 -14
  157. package/src/providers/integrations/kiro/kiro.ts +1 -2
  158. package/src/providers/integrations/qoder/qoder-catalog.ts +9 -3
  159. package/src/providers/integrations/qoder/qoder.ts +147 -88
  160. package/src/providers/integrations/trae/trae.ts +2 -2
  161. package/src/providers/operations/account-error-classification.ts +67 -17
  162. package/src/providers/operations/byok-wire-profile.ts +19 -15
  163. package/src/providers/provider-usage.ts +15 -49
  164. package/src/security/admission/service.ts +11 -4
  165. package/src/security/api-key-auth.ts +5 -3
  166. package/src/transport/dispatch/attempt-finalize.ts +69 -21
  167. package/src/transport/dispatch/attempt-loop.ts +22 -0
  168. package/src/transport/failure-policy.ts +15 -1
  169. package/src/transport/guards/error-lifecycle.ts +4 -0
  170. package/src/transport/request/state.ts +6 -0
  171. package/src/transport/routing/router.ts +7 -0
  172. package/src/transport/surface/reasoning-summary.ts +9 -14
  173. package/src/transport/vision-handoff.ts +83 -28
  174. package/dist/dashboard/assets/BannedPage-Cj15GsMp.js +0 -1
  175. package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
  176. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +0 -1
  177. package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
  178. package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js +0 -1
  179. package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
  180. package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js +0 -1
  181. package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
  182. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +0 -1
  183. package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
  184. package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
  185. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +0 -1
  186. package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
  187. package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
  188. package/dist/dashboard/assets/LoginPage-DkXb6dx-.js +0 -1
  189. package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
  190. package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
  191. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +0 -1
  192. package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
  193. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +0 -5
  194. package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
  195. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +0 -1
  196. package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
  197. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +0 -1
  198. package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
  199. package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
  200. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +0 -1
  201. package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
  202. package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
  203. package/dist/dashboard/assets/SetupPage-WTa0sktC.js +0 -1
  204. package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
  205. package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
  206. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
  207. package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
  208. package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
  209. package/dist/dashboard/assets/api-keys-l29JROPv.js +0 -1
  210. package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
  211. package/dist/dashboard/assets/app--PJWq8_C.css +0 -2
  212. package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
  213. package/dist/dashboard/assets/app-Bgd3zANQ.js +0 -9
  214. package/dist/dashboard/assets/app-Bgd3zANQ.js.gz +0 -0
  215. package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
  216. package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
  217. package/dist/dashboard/assets/banner-BllT4LcF.js.gz +0 -0
  218. package/dist/dashboard/assets/card-CtkX66Wd.js.gz +0 -0
  219. package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
  220. package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
  221. package/dist/dashboard/assets/dialog-BEK7KP0H.js +0 -1
  222. package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
  223. package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
  224. package/dist/dashboard/assets/entry-CHWxbS56.js +0 -1
  225. package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
  226. package/dist/dashboard/assets/entry-EBHJ3DrE.js +0 -1
  227. package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
  228. package/dist/dashboard/assets/entry-LCS85hiq.js +0 -4
  229. package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
  230. package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
  231. package/dist/dashboard/assets/github-badge-YSCE4UC2.js +0 -1
  232. package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
  233. package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
  234. package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
  235. package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
  236. package/dist/dashboard/assets/layout-CajrYUnD.js.gz +0 -0
  237. package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
  238. package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
  239. package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
  240. package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
  241. package/dist/dashboard/assets/numbers-CUWAZK4J.js +0 -2
  242. package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
  243. package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
  244. package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
  245. package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
  246. package/dist/dashboard/assets/quota-DvQbD_sr.js +0 -1
  247. package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
  248. package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
  249. package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
  250. package/dist/dashboard/assets/settings-CcGczj6I.js.gz +0 -0
  251. package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
  252. package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
  253. package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
  254. package/dist/dashboard/assets/useMutation-m8WGNPrz.js.gz +0 -0
  255. package/src/console/cli-tools/injectors/cline.ts +0 -110
  256. package/src/console/cli-tools/injectors/copilot.ts +0 -108
  257. package/src/console/cli-tools/injectors/cowork.ts +0 -64
  258. package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
  259. package/src/console/cli-tools/injectors/droid.ts +0 -96
  260. package/src/console/cli-tools/injectors/grok-build.ts +0 -70
  261. package/src/console/cli-tools/injectors/hermes.ts +0 -101
  262. package/src/console/cli-tools/injectors/jcode.ts +0 -115
  263. package/src/console/cli-tools/injectors/openclaw.ts +0 -161
@@ -1,6 +1,6 @@
1
1
  // Persisted API-key store: the console API-key domain and gateway authentication both consume this boundary.
2
2
 
3
- import { and, asc, count, desc, eq, inArray, isNotNull, isNull, lte, or, sql, type SQL } from "drizzle-orm";
3
+ import { and, asc, count, desc, eq, gt, inArray, isNotNull, isNull, lte, or, sql, type SQL } from "drizzle-orm";
4
4
  import { nextSortIndex, reorderSortIndex, type SortOrderDb } from "../sort-order";
5
5
  import { getDbHandle } from "../boot";
6
6
  import type { CartethyiaDatabase } from "../db-handle";
@@ -253,6 +253,8 @@ async function writeSharePopup(
253
253
  /** Explicit persistence boundary. Production must provide this; there is no fallback store. */
254
254
  export interface ApiKeyStore {
255
255
  list(tenantId: string): Promise<readonly ApiKeyRecord[]>;
256
+ /** Active, non-revoked bans for one tenant, including share children. */
257
+ listActiveBanned(tenantId: string): Promise<readonly ApiKeyRecord[]>;
256
258
  get(tenantId: string, keyId: string): Promise<ApiKeyRecord | undefined>;
257
259
  listChildren(tenantId: string, parentKeyId: string): Promise<readonly ApiKeyRecord[]>;
258
260
  revokeChildrenByScope(
@@ -382,6 +384,22 @@ export class DrizzleApiKeyStore implements ApiKeyStore {
382
384
  .limit(1000);
383
385
  return this.withPopups(this.db, rows);
384
386
  }
387
+ async listActiveBanned(tenantId: string): Promise<readonly ApiKeyRecord[]> {
388
+ const now = new Date();
389
+ const rows = await this.db
390
+ .select()
391
+ .from(apiKeys)
392
+ .where(
393
+ and(
394
+ eq(apiKeys.tenantId, tenantId),
395
+ isNotNull(apiKeys.bannedAt),
396
+ isNull(apiKeys.revokedAt),
397
+ or(isNull(apiKeys.bannedUntil), gt(apiKeys.bannedUntil, now)),
398
+ ),
399
+ )
400
+ .orderBy(desc(apiKeys.bannedAt), apiKeys.id);
401
+ return rows.map((row) => this.map(row));
402
+ }
385
403
 
386
404
  /** Rewrites the list order for one tenant; `keyIds` is the full order. */
387
405
  async reorder(tenantId: string, keyIds: readonly string[]): Promise<void> {
@@ -3,21 +3,11 @@
3
3
  *
4
4
  * DeepSeek-family models occasionally write their tool calls as XML-like DSML
5
5
  * markup inside the plain-text content stream instead of the structured
6
- * `tool_calls` field. Two upstream families in this gateway see it:
6
+ * `tool_calls` field. The gateway sees it from one upstream family:
7
7
  *
8
8
  * - CodeBuddy/WorkBuddy emit strict blocks (`<|DSML| calls>…</|DSML| calls>`)
9
9
  * that must be parsed exactly, with declared-tool allowlisting.
10
- * - Qoder emits looser blocks (`<[^>]*DSML[^>]*calls>…`) that tolerate any
11
- * intermediate characters in the tag, and its parameter values are plain
12
- * strings.
13
- *
14
- * The two share the same recovery idea, so the mechanics live here once and
15
- * each caller picks its dialect through options. The defaults reproduce the
16
- * CodeBuddy dialect exactly (`app/dsml.py` in codebuddy2api); Qoder passes
17
- * `fuzzy: true` and its id factory, mirroring `qoder_proxy.py`.
18
10
  */
19
- import { randomUUID } from "node:crypto";
20
-
21
11
  export interface DsmlToolCall {
22
12
  readonly id: string;
23
13
  readonly name: string;
@@ -34,19 +24,15 @@ export interface DsmlParseOptions {
34
24
  readonly separators?: readonly string[];
35
25
  /** Block names a strict opener may name. */
36
26
  readonly blockNames?: readonly string[];
37
- /** When true, match the Qoder fuzzy form (`<[^>]*DSML[^>]*…`). */
38
- readonly fuzzy?: boolean;
39
- /** How parameter values decode: `json` (CodeBuddy) or `string` (Qoder). */
27
+ /** How parameter values decode: `json` (CodeBuddy) or `string`. */
40
28
  readonly params?: "json" | "string";
41
- /** Call id factory. Defaults to sequential for strict, uuid for fuzzy. */
29
+ /** Call id factory. Defaults to sequential. */
42
30
  readonly id?: (call: { readonly name: string }, index: number, seq: number) => string;
43
31
  /** Non-empty allowlist makes the strict dialect reject undeclared names. */
44
32
  readonly allowedNames?: ReadonlySet<string>;
45
33
  /**
46
34
  * When true, JSON stringifies argument objects with recursively sorted keys,
47
- * matching the reference bridge (`json.dumps(..., sort_keys=True)`). The
48
- * strict dialect defaults to true; the fuzzy dialect never sorts, matching
49
- * its reference's `json.dumps(params)`.
35
+ * matching the reference bridge (`json.dumps(..., sort_keys=True)`).
50
36
  */
51
37
  readonly sortKeys?: boolean;
52
38
  /**
@@ -75,10 +61,6 @@ function sequentialId(_call: { readonly name: string }, _index: number, seq: num
75
61
  return `call_dsml_${seq}_${_call.name}`;
76
62
  }
77
63
 
78
- function uuidId(_call: { readonly name: string }, _index: number, _seq: number): string {
79
- return `call_${randomUUID().replace(/-/g, "").slice(0, 16)}`;
80
- }
81
-
82
64
  interface DsmlParseResult {
83
65
  readonly text: string;
84
66
  readonly calls: readonly DsmlToolCall[];
@@ -257,63 +239,11 @@ function normalizeForSort(value: unknown): unknown {
257
239
  return out;
258
240
  }
259
241
 
260
- /** The Qoder fuzzy block scanner, mirroring the reference bridge's regexes. */
261
- const FUZZY_CALLS_RE = /<[^>]*DSML[^>]*calls>([\s\S]*?)<\/[^>]*DSML[^>]*calls>/;
262
- const FUZZY_INVOKE_RE = /<[^>]*DSML[^>]*invoke\s+name=["']([^"']+)["']>([\s\S]*?)<\/[^>]*invoke>/g;
263
- const FUZZY_PARAM_RE = /<[^>]*DSML[^>]*parameter\s+name=["']([^"']+)["'][^>]*>([\s\S]*?)<\/[^>]*parameter>/g;
264
-
265
- function parseFuzzyDsml(text: string, options: DsmlParseOptions): DsmlRecovery {
266
- const id = options.id ?? uuidId;
267
- if (text.length === 0 || !text.includes("DSML")) return { calls: [], clean: text };
268
-
269
- const callsMatch = FUZZY_CALLS_RE.exec(text);
270
- let block: string;
271
- let clean: string;
272
-
273
- if (callsMatch) {
274
- block = callsMatch[1] ?? "";
275
- const before = text.slice(0, callsMatch.index).trimEnd();
276
- const after = text.slice(callsMatch.index + callsMatch[0].length).trimStart();
277
- clean = (before + " " + after).trim();
278
- } else {
279
- // No <calls> wrapper (issue #51): find bare <invoke> blocks in the whole
280
- // text, then cut the prose off at the last `<` before the first invoke —
281
- // exclusive of the invoke's own tag start, exactly like the reference's
282
- // `text.rfind("<", 0, first_index)`.
283
- FUZZY_INVOKE_RE.lastIndex = 0;
284
- const first = FUZZY_INVOKE_RE.exec(text);
285
- if (!first) return { calls: [], clean: text };
286
- block = text;
287
- const cut = text.lastIndexOf("<", first.index - 1);
288
- clean = text.slice(0, cut >= 0 ? cut : first.index).trim();
289
- }
290
-
291
- const calls: DsmlToolCall[] = [];
292
- FUZZY_INVOKE_RE.lastIndex = 0;
293
- let inv: RegExpExecArray | null;
294
- let seq = 0;
295
- while ((inv = FUZZY_INVOKE_RE.exec(block)) !== null) {
296
- const params: Record<string, string> = {};
297
- FUZZY_PARAM_RE.lastIndex = 0;
298
- let par: RegExpExecArray | null;
299
- while ((par = FUZZY_PARAM_RE.exec(inv[2] ?? "")) !== null) {
300
- params[par[1] ?? ""] = (par[2] ?? "").trim();
301
- }
302
- const call = { name: inv[1] ?? "", arguments: JSON.stringify(params) };
303
- calls.push({ ...call, id: id(call, calls.length, seq++) });
304
- }
305
-
306
- return { calls, clean };
307
- }
308
-
309
242
  /**
310
- * Recovers DSML tool calls from one plain-text string. The dialect is chosen
311
- * by `options.fuzzy`; each dialect keeps its exact public shape:
243
+ * Recovers DSML tool calls from one plain-text string.
312
244
  *
313
245
  * - strict (CodeBuddy): returns `{ text, calls }`, with `text` the clean
314
246
  * prose; `undefined` when nothing was recovered.
315
- * - fuzzy (Qoder): returns `{ clean, calls }`, with no-DSML input returning
316
- * `{ calls: [], clean: text }`.
317
247
  *
318
248
  * Callers that need the streaming strict parser use {@link DsmlStreamParser}.
319
249
  */
@@ -321,7 +251,6 @@ export function parseDsmlToolCalls(
321
251
  text: string,
322
252
  options: DsmlParseOptions = {},
323
253
  ): DsmlRecovery | DsmlParseResult | undefined {
324
- if (options.fuzzy === true) return parseFuzzyDsml(text, options);
325
254
  if (!text.includes("DSML")) return undefined;
326
255
  const parser = new DsmlStreamParser(options);
327
256
  const first = parser.push(text);
@@ -28,6 +28,19 @@ import {
28
28
  } from "../../protocol/codecs";
29
29
  const TEXT_DECODER = new TextDecoder("utf-8", { fatal: false });
30
30
 
31
+ /**
32
+ * Provider ids that understand the OpenAI-style `service_tier` control. Only
33
+ * these upstreams accept the field; every other OpenAI-compatible provider
34
+ * either ignores it or rejects the whole request with `unsupported_value`
35
+ * (measured: GitHub Copilot returns 400). `service_tier` is advisory on the
36
+ * providers that do support it, so dropping it elsewhere is lossless.
37
+ */
38
+ const SERVICE_TIER_SUPPORTED_PROVIDERS: ReadonlySet<string> = new Set([
39
+ "openai",
40
+ "codex",
41
+ "anthropic",
42
+ ]);
43
+
31
44
  /**
32
45
  * The subset of a canonical request a native-service header hook may read.
33
46
  *
@@ -279,6 +292,18 @@ export class OpenAICompatibleAdapter extends BaseProviderAdapter {
279
292
  }
280
293
  }
281
294
  this.config.prePayload?.(payload, effectiveRequest, candidate, context);
295
+ // `service_tier` is an OpenAI-style control that only a small set of
296
+ // upstreams understand. Every other OpenAI-compatible provider either
297
+ // ignores it silently or rejects the whole request with
298
+ // `unsupported_value` (measured: GitHub Copilot returns 400). Since the
299
+ // field is purely advisory on the providers that support it, dropping it
300
+ // everywhere else is safe and prevents avoidable upstream 400s.
301
+ if (
302
+ payload["service_tier"] !== undefined &&
303
+ !SERVICE_TIER_SUPPORTED_PROVIDERS.has(candidate.provider_id)
304
+ ) {
305
+ delete payload["service_tier"];
306
+ }
282
307
  return payload;
283
308
  }
284
309
 
@@ -429,12 +454,15 @@ export class OpenAICompatibleAdapter extends BaseProviderAdapter {
429
454
  // the absence of every completion shape so a provider that merely
430
455
  // echoes an `error` field alongside real output is not misread.
431
456
  const envelopeError = json["error"];
457
+ const startPlanError = this.provider_id === "zcode-start" &&
458
+ (json["success"] === false || (json["code"] !== undefined &&
459
+ ![0, 200, "0", "200"].includes(json["code"] as string | number)));
432
460
  const hasCompletionShape =
433
461
  json["choices"] !== undefined ||
434
462
  json["output"] !== undefined ||
435
463
  json["content"] !== undefined ||
436
464
  json["candidates"] !== undefined;
437
- if (envelopeError !== undefined && envelopeError !== null && !hasCompletionShape) {
465
+ if (((envelopeError !== undefined && envelopeError !== null) || startPlanError) && !hasCompletionShape) {
438
466
  const errorObject = isRecord(envelopeError) ? envelopeError : json;
439
467
  const identifier = upstreamErrorIdentifier(json);
440
468
  const providerStatus = [
@@ -239,8 +239,18 @@ export const PROVIDER_CAPABILITIES = {
239
239
  loadAuthentication: oauthCapability(() => import("./integrations/antigravity/antigravity-oauth"), "antigravityOAuthClient", { withRefresher: true }),
240
240
  loadQuotaCollector: quotaCapability(() => import("./integrations/antigravity/antigravity-quota"), "fetchAntigravityQuota"),
241
241
  loadModelDiscovery: async () => async ({ baseUrl, credential, fetcher }) => {
242
- const { discoverAntigravityModels } = await import("./integrations/antigravity/antigravity-protocol");
243
- return discoverAntigravityModels(credential, { baseUrl, ...(fetcher ? { fetcher } : {}) });
242
+ const { discoverAntigravityModels, loadAntigravityProject } = await import("./integrations/antigravity/antigravity-protocol");
243
+ // The reference client resolves the project before catalog calls; the
244
+ // live backend accepts both, but sending it matches the captured wire.
245
+ const projectId = await loadAntigravityProject(credential, {
246
+ ...(baseUrl === undefined ? {} : { baseUrl }),
247
+ ...(fetcher ? { fetcher } : {}),
248
+ }).catch(() => undefined);
249
+ return discoverAntigravityModels(credential, {
250
+ baseUrl,
251
+ ...(fetcher ? { fetcher } : {}),
252
+ ...(projectId === undefined ? {} : { projectId }),
253
+ });
244
254
  },
245
255
  },
246
256
  muse: {
@@ -96,7 +96,8 @@ interface AntigravityModelWireProfile {
96
96
 
97
97
  /**
98
98
  * `maxOutputTokens` the backend enforces per wire id (captured from the real
99
- * `antigravity/hub` client). Claude on `daily-cloudcode-pa` rejects
99
+ * `antigravity/hub` client and verified live against `fetchAvailableModels`
100
+ * on 2026-11-03). Claude on `daily-cloudcode-pa` rejects
100
101
  * `maxOutputTokens > 64000` with a `400 Request contains an invalid argument`
101
102
  * regardless of the thinking budget; Gemini SKUs accept their discovered
102
103
  * output ceiling. Keyed by the routed upstream wire id (post effort-routing),
@@ -106,22 +107,75 @@ interface AntigravityModelWireProfile {
106
107
  const ANTIGRAVITY_MODEL_WIRE_PROFILES: Readonly<
107
108
  Record<string, AntigravityModelWireProfile>
108
109
  > = Object.freeze({
109
- // Anthropic on Antigravity caps at 64000 output tokens.
110
- "claude-opus-4-6-thinking": { maxOutputTokens: 64000 },
111
- "claude-sonnet-4-6": { maxOutputTokens: 64000 },
112
- // Gemini effort-routed wire ids (real deployment names).
113
- "gemini-3-flash-agent": {
114
- modelEnum: "MODEL_PLACEHOLDER_M132",
110
+ // Anthropic on Antigravity caps at 64000 output tokens (verified live).
111
+ "claude-opus-4-6-thinking": {
112
+ modelEnum: "MODEL_PLACEHOLDER_M26",
113
+ maxOutputTokens: 64000,
114
+ },
115
+ "claude-sonnet-4-6": {
116
+ modelEnum: "MODEL_PLACEHOLDER_M35",
117
+ maxOutputTokens: 64000,
118
+ },
119
+ // Gemini SKUs, as the live roster publishes them (2026-11-03).
120
+ "gemini-2.5-flash": {
121
+ modelEnum: "MODEL_GOOGLE_GEMINI_2_5_FLASH",
122
+ maxOutputTokens: 65535,
123
+ },
124
+ "gemini-2.5-flash-thinking": {
125
+ modelEnum: "MODEL_GOOGLE_GEMINI_2_5_FLASH_THINKING",
126
+ maxOutputTokens: 65535,
127
+ },
128
+ "gemini-2.5-flash-lite": {
129
+ modelEnum: "MODEL_GOOGLE_GEMINI_2_5_FLASH_LITE",
130
+ maxOutputTokens: 65535,
131
+ },
132
+ "gemini-2.5-pro": {
133
+ modelEnum: "MODEL_GOOGLE_GEMINI_2_5_PRO",
134
+ maxOutputTokens: 65535,
135
+ },
136
+ "gemini-3-flash": {
137
+ modelEnum: "MODEL_PLACEHOLDER_M18",
115
138
  maxOutputTokens: 65536,
116
139
  },
117
- "gemini-3.1-pro-low": {
118
- modelEnum: "MODEL_PLACEHOLDER_M36",
140
+ "gemini-3.1-flash-lite": {
141
+ modelEnum: "MODEL_PLACEHOLDER_M50",
119
142
  maxOutputTokens: 65535,
120
143
  },
121
- "gemini-pro-agent": {
122
- modelEnum: "MODEL_PLACEHOLDER_M16",
144
+ "gemini-3.1-flash-image": {
145
+ modelEnum: "MODEL_PLACEHOLDER_M21",
146
+ maxOutputTokens: 65536,
147
+ },
148
+ "gemini-3.5-flash-lite": {
149
+ modelEnum: "MODEL_PLACEHOLDER_M198",
123
150
  maxOutputTokens: 65535,
124
151
  },
152
+ // Gemini 3.8 Flash effort tiers (real deployment names).
153
+ "gemini-3.8-flash-low": {
154
+ modelEnum: "MODEL_PLACEHOLDER_M320",
155
+ maxOutputTokens: 65536,
156
+ },
157
+ "gemini-3.8-flash-medium": {
158
+ modelEnum: "MODEL_PLACEHOLDER_M319",
159
+ maxOutputTokens: 65536,
160
+ },
161
+ "gemini-3.8-flash-high": {
162
+ modelEnum: "MODEL_PLACEHOLDER_M318",
163
+ maxOutputTokens: 65536,
164
+ },
165
+ "gemini-3.8-flash-tiered": {
166
+ modelEnum: "MODEL_PLACEHOLDER_M322",
167
+ maxOutputTokens: 65536,
168
+ },
169
+ // GPT-OSS via Antigravity (verified live).
170
+ "gpt-oss-120b-medium": {
171
+ modelEnum: "MODEL_OPENAI_GPT_OSS_120B_MEDIUM",
172
+ maxOutputTokens: 32768,
173
+ },
174
+ // Tab (laptop-class) preview SKU.
175
+ "tab_flash_lite_preview": {
176
+ modelEnum: "MODEL_PLACEHOLDER_M19",
177
+ maxOutputTokens: 4096,
178
+ },
125
179
  });
126
180
 
127
181
  export function getAntigravityModelWireProfile(
@@ -398,6 +452,7 @@ export async function discoverAntigravityModels(
398
452
  readonly baseUrl?: string;
399
453
  readonly fetcher?: typeof fetch;
400
454
  readonly signal?: AbortSignal;
455
+ readonly projectId?: string;
401
456
  } = {},
402
457
  ): Promise<readonly ModelDefinition[] | null> {
403
458
  const fetcher = options.fetcher ?? fetch;
@@ -419,7 +474,10 @@ export async function discoverAntigravityModels(
419
474
  "content-type": "application/json",
420
475
  "user-agent": getAntigravityUserAgent(),
421
476
  },
422
- body: "{}",
477
+ // The reference client always sends the resolved project
478
+ // (`cloudCodeMainService.js`); the live backend accepts both, but the
479
+ // project is required for billing-gated endpoints.
480
+ body: JSON.stringify(options.projectId === undefined ? {} : { project: options.projectId }),
423
481
  signal,
424
482
  });
425
483
  if (!response.ok) continue;
@@ -489,9 +547,16 @@ export function applySkipThoughtSignatureBypass(
489
547
  * Sending the Gemini shape makes the backend treat the call as a different
490
548
  * client and refuse to enroll the account, which the operator sees as
491
549
  * "You do not have a valid license of this product".
550
+ *
551
+ * The reference client (`cloudCodeMainService.js`) sends the full product
552
+ * identity: `ideName`, `ideType`, and `ideVersion`. `ideType` alone was
553
+ * accepted live, but the full shape is the captured wire and avoids any
554
+ * client-class gating on the control plane.
492
555
  */
493
556
  export const ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA = Object.freeze({
557
+ ideName: "antigravity",
494
558
  ideType: "ANTIGRAVITY",
559
+ ideVersion: getAntigravityVersion(),
495
560
  });
496
561
 
497
562
  const projectIdCache = new Map<string, string>();
@@ -570,7 +635,9 @@ const FREE_TIER_ID = "free-tier";
570
635
  const ONBOARD_USER_PATH = "/v1internal:onboardUser";
571
636
  const ONBOARD_OPERATIONS_PATH = "/v1internal";
572
637
  const ONBOARD_TIMEOUT_MS = 30_000;
573
- const ONBOARD_POLL_INTERVAL_MS = 1_000;
638
+ // Reference client (`cloudCodeMainService.js`) polls the operation every
639
+ // 500ms (`gXa = 500`).
640
+ const ONBOARD_POLL_INTERVAL_MS = 500;
574
641
 
575
642
  /** Thrown when an account cannot be enrolled in the Antigravity free tier. */
576
643
  export class AntigravityProvisioningError extends Error {
@@ -699,6 +766,9 @@ async function onboardUser(
699
766
  if (left <= 0) throw new AntigravityProvisioningError("onboardUser timed out");
700
767
  return left;
701
768
  };
769
+ const projectId = extractProjectId(
770
+ await loadCodeAssist(fetcher, base, accessToken, signal),
771
+ );
702
772
  let operation = await cloudCodeAssistCall(
703
773
  fetcher,
704
774
  base,
@@ -706,7 +776,11 @@ async function onboardUser(
706
776
  ONBOARD_USER_PATH,
707
777
  {
708
778
  method: "POST",
709
- body: { tierId: FREE_TIER_ID, metadata: ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA },
779
+ body: {
780
+ tierId: FREE_TIER_ID,
781
+ ...(projectId === undefined ? {} : { cloudaicompanionProject: projectId }),
782
+ metadata: ANTIGRAVITY_LOAD_CODE_ASSIST_METADATA,
783
+ },
710
784
  },
711
785
  signal,
712
786
  );
@@ -118,9 +118,6 @@ function keepWorse(
118
118
  return candidateRemaining < currentRemaining ? candidate : current;
119
119
  }
120
120
 
121
- /** Total the upstream's fractional allowance is normalized against. */
122
- const QUOTA_TOTAL = 1000;
123
-
124
121
  /** Builds a window from a `remainingFraction` + `resetTime` pair. */
125
122
  function fractionWindow(
126
123
  kind: string,
@@ -129,9 +126,8 @@ function fractionWindow(
129
126
  resetsAt: string | null,
130
127
  ): ProviderQuotaWindow {
131
128
  const fraction = Math.min(1, Math.max(0, remainingFraction));
132
- const used = QUOTA_TOTAL - Math.round(QUOTA_TOTAL * fraction);
133
129
  return {
134
- ...percentWindow(kind, label, (1 - fraction) * 100, resetsAt, used, QUOTA_TOTAL, "independent"),
130
+ ...percentWindow(kind, label, (1 - fraction) * 100, resetsAt, null, null, "independent"),
135
131
  recurring: true,
136
132
  };
137
133
  }
@@ -316,7 +312,23 @@ export async function fetchAntigravityQuota(
316
312
  ...(project === undefined ? {} : { project }),
317
313
  }),
318
314
  );
319
- windows.push(...parseModelQuotas(models));
315
+ // The summary endpoint already covers a family with a weekly window; the
316
+ // per-model windows are a subset of that same allowance, so rendering
317
+ // both doubles every row the operator sees. Per-model windows stay as a
318
+ // fallback when the summary was unavailable, so a family still reports
319
+ // something.
320
+ const summaryLabels = windows.map((window) => window.label.toLowerCase());
321
+ // Model family key → the summary family label it falls under.
322
+ const summaryLabelForModel = (key: string): string =>
323
+ key === "google" ? "gemini" : "claude";
324
+ const modelWindows = parseModelQuotas(models);
325
+ windows.push(
326
+ ...modelWindows.filter((window) => {
327
+ const familyKey = window.kind.split(":")[0] ?? "";
328
+ const summaryLabel = summaryLabelForModel(familyKey);
329
+ return !summaryLabels.some((label) => label.includes(summaryLabel));
330
+ }),
331
+ );
320
332
  plan = plan ?? text(models.tier) ?? text(models.plan);
321
333
  break;
322
334
  } catch {
@@ -94,22 +94,26 @@ export const ANTIGRAVITY_MODELS: readonly ModelDefinition[] = [
94
94
  antigravityModel("claude-opus-4-6", 250_000, 64_000, true),
95
95
  antigravityModel("claude-sonnet-5-5", 1_000_000, 128_000, true),
96
96
  antigravityModel("claude-opus-5-5", 1_000_000, 128_000, true),
97
- // Gemini text SKUs, as the Antigravity roster publishes them.
98
- antigravityModel("gemini-2.5-flash", 1_000_000, 66_000, true),
99
- antigravityModel("gemini-2.5-flash-thinking", 1_000_000, 66_000, true),
100
- antigravityModel("gemini-3-flash", 1_000_000, 66_000, true),
101
- antigravityModel("gemini-3.1-flash-lite", 1_000_000, 66_000, true),
102
- antigravityModel("gemini-2.5-flash-lite", 1_000_000, 66_000, true),
103
- antigravityModel("gemini-3.5-flash-lite", 1_000_000, 66_000, true),
104
- antigravityModel("gemini-3.8-flash", 1_000_000, 66_000, true),
97
+ // Gemini text SKUs, as the Antigravity roster publishes them (verified live
98
+ // 2026-11-03): Gemini SKUs ceiling at 65535/65536 output tokens.
99
+ antigravityModel("gemini-2.5-flash", 1_000_000, 65_535, true),
100
+ antigravityModel("gemini-2.5-flash-thinking", 1_000_000, 65_535, true),
101
+ antigravityModel("gemini-2.5-flash-lite", 1_000_000, 65_535, true),
102
+ antigravityModel("gemini-2.5-pro", 1_000_000, 65_535, true),
103
+ antigravityModel("gemini-3-flash", 1_000_000, 65_536, true),
104
+ antigravityModel("gemini-3.1-flash-lite", 1_000_000, 65_535, true),
105
+ antigravityModel("gemini-3.5-flash-lite", 1_000_000, 65_535, true),
106
+ antigravityModel("gemini-3.8-flash", 1_000_000, 65_536, true),
105
107
  antigravityModel("gemini-4-argon", 1_000_000, 65_536, true),
106
108
  antigravityModel("gemini-4-carbon", 1_000_000, 65_536, true),
107
109
  // Tab (laptop-class) preview SKU.
108
110
  antigravityModel("tab_flash_lite_preview", 16_000, 4_000, true),
109
111
  // Gemini image generation. `gemini-3-pro-image` uses the same catalog
110
112
  // limits/capabilities as the existing `gemini-3.1-flash-image` row.
111
- antigravityModel("gemini-3.1-flash-image", 200_000, 64_000, false, true),
112
- antigravityModel("gemini-3-pro-image", 200_000, 64_000, false, true),
113
+ antigravityModel("gemini-3.1-flash-image", 65_536, 65_536, false, true),
114
+ antigravityModel("gemini-3-pro-image", 65_536, 65_536, false, true),
115
+ // GPT-OSS via Antigravity (verified live 2026-11-03).
116
+ antigravityModel("gpt-oss-120b", 131_072, 32_768, true),
113
117
  ];
114
118
 
115
119
  function normalizeAntigravityGrounding(data: unknown, maxResults: number): readonly WebSearchResult[] {
@@ -777,8 +781,13 @@ function antigravityThinkingBudget(modelId: string): number | undefined {
777
781
  return modelId.includes("3.1-pro") ? 10_001 : 10_000;
778
782
  }
779
783
 
784
+ // Original reference instruction (captured from the Antigravity client):
785
+ // "You are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.You are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.**Absolute paths only****Proactiveness**"
786
+ // Generalized to a neutral identity so the gateway does not impersonate the
787
+ // vendor's branded assistant. The brand-specific wording is kept above for
788
+ // reference; re-enable it if an upstream behavior ever depends on it.
780
789
  const ANTIGRAVITY_SYSTEM_INSTRUCTION =
781
- "You are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.You are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.**Absolute paths only****Proactiveness**";
790
+ "You are a helpful assistant.";
782
791
 
783
792
  /**
784
793
  * Restatement appended as a trailing user turn when a Gemini route carries a
@@ -27,7 +27,6 @@ import type {
27
27
  } from "../../../protocol/canonical-model";
28
28
  import { resolveImageSource, splitDataUrl } from "../../../protocol/codecs";
29
29
  import { IMAGE_CLAMP_MAX_BYTES } from "../../../protocol/image-clamp";
30
- import { applyCacheMode } from "../../provider-usage";
31
30
  import type { ProviderAdapter, ProviderDispatchContext, ProviderDispatchTarget } from "../../provider-contracts";
32
31
  import { readCredentialSecret } from "../../provider-contracts";
33
32
  import type { Http2BidiEndReason, ValidatedHttp2BidiStream } from "../../../network/http2-bidi";
@@ -281,7 +280,7 @@ function usageFromTurnEnded(turnEnded: Record<string, unknown>): UsageRecord {
281
280
  const input = count(turnEnded["input_tokens"]);
282
281
  const output = count(turnEnded["output_tokens"]);
283
282
  const cacheRead = count(turnEnded["cache_read"]);
284
- return applyCacheMode({
283
+ return {
285
284
  input_tokens: input,
286
285
  cached_input_tokens: cacheRead,
287
286
  cache_write_tokens: count(turnEnded["cache_write"]),
@@ -290,7 +289,7 @@ function usageFromTurnEnded(turnEnded: Record<string, unknown>): UsageRecord {
290
289
  reasoning_tokens: count(turnEnded["reasoning_tokens"]),
291
290
  estimated_cost: null,
292
291
  total_tokens: input + output,
293
- });
292
+ };
294
293
  }
295
294
 
296
295
  function nonEmptyStringOf(value: unknown): string | undefined {
@@ -33,7 +33,8 @@ import {
33
33
  GITHUB_AUTO_MODELS,
34
34
  recordGithubModelRoutes,
35
35
  } from "./github";
36
- import { GITHUB_REQUEST_HEADERS, parseGithubCredential } from "./github-oauth";
36
+ import { GITHUB_REQUEST_HEADERS, githubCopilotClientIds, parseGithubCredential } from "./github-oauth";
37
+ import { createHash } from "node:crypto";
37
38
 
38
39
  /** A context window above this is unsatisfiable headroom, not a real limit. */
39
40
  const MAX_CONTEXT = 1_000_000_000;
@@ -170,8 +171,20 @@ export async function discoverGithubModels(
170
171
  const signal =
171
172
  input.signal === undefined ? timeoutSignal : AbortSignal.any([input.signal, timeoutSignal]);
172
173
  try {
174
+ // The reference client stamps its Copilot identity on `/models` too
175
+ // (`_mixinHeaders` covers the Models request type): session + machine ids.
176
+ // Machine id is per-account — see `githubCopilotClientIds`.
177
+ const accountKey = createHash("sha256").update(`${apiHost}:${access}`).digest("hex");
178
+ const { sessionId, machineId } = await githubCopilotClientIds(accountKey);
173
179
  const response = await fetcher(`https://${apiHost}/models`, {
174
- headers: { accept: "application/json", authorization: `Bearer ${access}`, ...GITHUB_REQUEST_HEADERS },
180
+ headers: {
181
+ accept: "application/json",
182
+ authorization: `Bearer ${access}`,
183
+ ...GITHUB_REQUEST_HEADERS,
184
+ "VScode-SessionId": sessionId,
185
+ "VScode-MachineId": machineId,
186
+ "editor-device-id": machineId,
187
+ },
175
188
  signal,
176
189
  });
177
190
  if (!response.ok) return null;