cartethyia 2.5.3 → 2.5.4

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 (262) hide show
  1. package/dist/dashboard/assets/{BannedPage-hfCzi-Ux.js → BannedPage-C2orBsI2.js} +1 -1
  2. package/dist/dashboard/assets/BannedPage-C2orBsI2.js.gz +0 -0
  3. package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js +1 -0
  4. package/dist/dashboard/assets/CliToolDetailPage-DUcZ6pMB.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolsPage-DEDyKBlC.js → CliToolsPage-BV6pxVQo.js} +1 -1
  6. package/dist/dashboard/assets/CliToolsPage-BV6pxVQo.js.gz +0 -0
  7. package/dist/dashboard/assets/{CombosPage-CfuYiQ8Z.js → CombosPage-Bsan4679.js} +1 -1
  8. package/dist/dashboard/assets/CombosPage-Bsan4679.js.gz +0 -0
  9. package/dist/dashboard/assets/{ConsoleLogPage-bsLuZmYH.js → ConsoleLogPage-CW-wPmJ6.js} +1 -1
  10. package/dist/dashboard/assets/ConsoleLogPage-CW-wPmJ6.js.gz +0 -0
  11. package/dist/dashboard/assets/{CustomizationPage-_UWhwziJ.js → CustomizationPage-0p8uX7vB.js} +1 -1
  12. package/dist/dashboard/assets/CustomizationPage-0p8uX7vB.js.gz +0 -0
  13. package/dist/dashboard/assets/HealthEventsModal-BjqSMfko.js +1 -0
  14. package/dist/dashboard/assets/HealthEventsModal-BjqSMfko.js.gz +0 -0
  15. package/dist/dashboard/assets/{LoginPage-DDVIfZGW.js → LoginPage-BvzCx8Kr.js} +1 -1
  16. package/dist/dashboard/assets/LoginPage-BvzCx8Kr.js.gz +0 -0
  17. package/dist/dashboard/assets/{ModelPicker-CWkTUtpX.js → ModelPicker-BY1aKEkP.js} +1 -1
  18. package/dist/dashboard/assets/ModelPicker-BY1aKEkP.js.gz +0 -0
  19. package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js +1 -0
  20. package/dist/dashboard/assets/OverviewPage-DOQpy1y0.js.gz +0 -0
  21. package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js +3 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-ecxyUMzt.js.gz +0 -0
  23. package/dist/dashboard/assets/ProvidersPage-Js9jUPSb.js +1 -0
  24. package/dist/dashboard/assets/ProvidersPage-Js9jUPSb.js.gz +0 -0
  25. package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js +7 -0
  26. package/dist/dashboard/assets/ProxyPage-J3TBi3Kx.js.gz +0 -0
  27. package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js +1 -0
  28. package/dist/dashboard/assets/QuotaPage-C1gZS6xr.js.gz +0 -0
  29. package/dist/dashboard/assets/SettingsPage-CS86ho5X.js +1 -0
  30. package/dist/dashboard/assets/SettingsPage-CS86ho5X.js.gz +0 -0
  31. package/dist/dashboard/assets/{SetupPage-D6pmQ86L.js → SetupPage-DDbMQsoo.js} +1 -1
  32. package/dist/dashboard/assets/SetupPage-DDbMQsoo.js.gz +0 -0
  33. package/dist/dashboard/assets/{SortableList-DWvKiuSl.js → SortableList-CB3Z7VTf.js} +1 -1
  34. package/dist/dashboard/assets/SortableList-CB3Z7VTf.js.gz +0 -0
  35. package/dist/dashboard/assets/{StudioPage-CmkUrgdj.js → StudioPage-D95UCPHI.js} +3 -3
  36. package/dist/dashboard/assets/StudioPage-D95UCPHI.js.gz +0 -0
  37. package/dist/dashboard/assets/{UsagePage-B47uOydr.js → UsagePage-Cnae9SWA.js} +13 -13
  38. package/dist/dashboard/assets/UsagePage-Cnae9SWA.js.gz +0 -0
  39. package/dist/dashboard/assets/{api-keys-uNywYlfv.js → api-keys-VY68Mhd1.js} +1 -1
  40. package/dist/dashboard/assets/api-keys-VY68Mhd1.js.gz +0 -0
  41. package/dist/dashboard/assets/app-COlK95oi.css +2 -0
  42. package/dist/dashboard/assets/app-COlK95oi.css.gz +0 -0
  43. package/dist/dashboard/assets/{assert-NHwYq95t.js → assert-Chi7pCnt.js} +1 -1
  44. package/dist/dashboard/assets/assert-Chi7pCnt.js.gz +0 -0
  45. package/dist/dashboard/assets/{badge-6uzDDvqs.js → badge-D4pQapKj.js} +1 -1
  46. package/dist/dashboard/assets/badge-D4pQapKj.js.gz +0 -0
  47. package/dist/dashboard/assets/{banner-4_WNg2WZ.js → banner-DH6cg-_B.js} +1 -1
  48. package/dist/dashboard/assets/banner-DH6cg-_B.js.gz +0 -0
  49. package/dist/dashboard/assets/{card--AtFedAq.js → card-BKqlUOe4.js} +1 -1
  50. package/dist/dashboard/assets/card-BKqlUOe4.js.gz +0 -0
  51. package/dist/dashboard/assets/{cli-tools-B3e7dx3N.js → cli-tools-fTgljTGA.js} +1 -1
  52. package/dist/dashboard/assets/cli-tools-fTgljTGA.js.gz +0 -0
  53. package/dist/dashboard/assets/{clipboard-button-NreUx6c8.js → clipboard-button-D5Ys_sM0.js} +1 -1
  54. package/dist/dashboard/assets/clipboard-button-D5Ys_sM0.js.gz +0 -0
  55. package/dist/dashboard/assets/database-Dz9N3zGd.js +1 -0
  56. package/dist/dashboard/assets/database-Dz9N3zGd.js.gz +0 -0
  57. package/dist/dashboard/assets/{dialog-BtpsJU98.js → dialog-BYj-8WuK.js} +1 -1
  58. package/dist/dashboard/assets/dialog-BYj-8WuK.js.gz +0 -0
  59. package/dist/dashboard/assets/{drawer-WwwtzihF.js → drawer-CmXIZ6H1.js} +1 -1
  60. package/dist/dashboard/assets/drawer-CmXIZ6H1.js.gz +0 -0
  61. package/dist/dashboard/assets/entry-CBg5zeu8.js +1 -0
  62. package/dist/dashboard/assets/entry-CBg5zeu8.js.gz +0 -0
  63. package/dist/dashboard/assets/entry-CQBr6U9c.js +1 -0
  64. package/dist/dashboard/assets/entry-CQBr6U9c.js.gz +0 -0
  65. package/dist/dashboard/assets/entry-CgdSAzEV.js +4 -0
  66. package/dist/dashboard/assets/entry-CgdSAzEV.js.gz +0 -0
  67. package/dist/dashboard/assets/{github-badge-D_eM3IgG.js → github-badge-BlptL8Uf.js} +1 -1
  68. package/dist/dashboard/assets/github-badge-BlptL8Uf.js.gz +0 -0
  69. package/dist/dashboard/assets/{index-BydyKaDz.js → index-js6TJvdr.js} +2 -2
  70. package/dist/dashboard/assets/index-js6TJvdr.js.gz +0 -0
  71. package/dist/dashboard/assets/{inline-BcNGF8Gr.js → inline-DUWDSmDV.js} +1 -1
  72. package/dist/dashboard/assets/inline-DUWDSmDV.js.gz +0 -0
  73. package/dist/dashboard/assets/{input-BG41et9G.js → input-LVjgZq4f.js} +1 -1
  74. package/dist/dashboard/assets/input-LVjgZq4f.js.gz +0 -0
  75. package/dist/dashboard/assets/{layout-C8mer_Ak.js → layout-CPfxpsfy.js} +1 -1
  76. package/dist/dashboard/assets/layout-CPfxpsfy.js.gz +0 -0
  77. package/dist/dashboard/assets/{list-row-Gh_W4OR_.js → list-row-DupvLfDx.js} +1 -1
  78. package/dist/dashboard/assets/list-row-DupvLfDx.js.gz +0 -0
  79. package/dist/dashboard/assets/{live-BJGYRBnR.js → live-C4pEJUqI.js} +1 -1
  80. package/dist/dashboard/assets/live-C4pEJUqI.js.gz +0 -0
  81. package/dist/dashboard/assets/model-bans-BRiuufkB.js +1 -0
  82. package/dist/dashboard/assets/model-bans-BRiuufkB.js.gz +0 -0
  83. package/dist/dashboard/assets/{mutation-BkoLM5Ba.js → mutation-B4ROwk8L.js} +1 -1
  84. package/dist/dashboard/assets/mutation-B4ROwk8L.js.gz +0 -0
  85. package/dist/dashboard/assets/numbers-YNcHvRY7.js +2 -0
  86. package/dist/dashboard/assets/numbers-YNcHvRY7.js.gz +0 -0
  87. package/dist/dashboard/assets/{query-keys-D7wX2vM5.js → query-keys-gmjzC3RS.js} +1 -1
  88. package/dist/dashboard/assets/{query-keys-D7wX2vM5.js.gz → query-keys-gmjzC3RS.js.gz} +0 -0
  89. package/dist/dashboard/assets/{routing-CcusqXA7.js → routing-C7tBT5o-.js} +1 -1
  90. package/dist/dashboard/assets/routing-C7tBT5o-.js.gz +0 -0
  91. package/dist/dashboard/assets/{select-BO6h8yNv.js → select-WUCXorXw.js} +2 -2
  92. package/dist/dashboard/assets/select-WUCXorXw.js.gz +0 -0
  93. package/dist/dashboard/assets/{settings-mpmXTgUa.js → settings-C_zKl1GS.js} +1 -1
  94. package/dist/dashboard/assets/settings-C_zKl1GS.js.gz +0 -0
  95. package/dist/dashboard/assets/{stack-DwLmjUFK.js → stack-_3O0F8Xn.js} +1 -1
  96. package/dist/dashboard/assets/stack-_3O0F8Xn.js.gz +0 -0
  97. package/dist/dashboard/assets/{state-DYdQmdAl.js → state-BgTxHcJS.js} +1 -1
  98. package/dist/dashboard/assets/state-BgTxHcJS.js.gz +0 -0
  99. package/dist/dashboard/assets/switch-DEjTtOWf.js +1 -0
  100. package/dist/dashboard/assets/switch-DEjTtOWf.js.gz +0 -0
  101. package/dist/dashboard/assets/terminal-0_IjPWTk.js +1 -0
  102. package/dist/dashboard/assets/terminal-0_IjPWTk.js.gz +0 -0
  103. package/dist/dashboard/assets/{useMutation-w4u4CVue.js → useMutation-BWSC0UAS.js} +1 -1
  104. package/dist/dashboard/assets/useMutation-BWSC0UAS.js.gz +0 -0
  105. package/dist/dashboard/assets/zap-C4EeVW4q.js +1 -0
  106. package/dist/dashboard/assets/zap-C4EeVW4q.js.gz +0 -0
  107. package/dist/dashboard/donation/qris-risun-marke.png +0 -0
  108. package/dist/dashboard/index.html +1 -1
  109. package/dist/dashboard/index.html.gz +0 -0
  110. package/dist/dashboard/precompressed-assets.json +54 -54
  111. package/package.json +1 -1
  112. package/scripts/cli/autostart.ts +237 -0
  113. package/scripts/cli/command/console-menu.ts +4 -8
  114. package/scripts/cli/command/update.ts +27 -10
  115. package/scripts/cli/main.ts +28 -1
  116. package/src/boot/app.ts +693 -693
  117. package/src/boot/dependencies.ts +646 -616
  118. package/src/config/spec.ts +55 -0
  119. package/src/config/telemetry.ts +46 -0
  120. package/src/console/api-keys/bucket-spend.ts +15 -4
  121. package/src/console/backup/contracts.ts +15 -2
  122. package/src/console/console-router.ts +8 -0
  123. package/src/console/data-management/contracts.ts +93 -0
  124. package/src/console/data-management/operations.ts +229 -0
  125. package/src/console/data-management/routes.ts +104 -0
  126. package/src/console/domain-registration.ts +39 -0
  127. package/src/console/observability/contracts.ts +524 -485
  128. package/src/console/observability/store.ts +1115 -1018
  129. package/src/console/providers/catalog/contracts.ts +6 -0
  130. package/src/console/providers/catalog/provider-operations.ts +20 -0
  131. package/src/console/providers/catalog/routes.ts +7 -0
  132. package/src/console/providers/catalog/store.ts +22 -1
  133. package/src/console/quota/account-quota-shared.ts +52 -39
  134. package/src/console/quota/refresh.ts +441 -429
  135. package/src/console/routing/pools/import-parse.ts +206 -0
  136. package/src/console/routing/pools/operations.ts +543 -501
  137. package/src/console/routing/pools/routes.ts +181 -164
  138. package/src/console/settings/contracts.ts +37 -0
  139. package/src/console/settings/store.ts +51 -0
  140. package/src/console/updates/routes.ts +16 -0
  141. package/src/main.ts +10 -0
  142. package/src/observability/event-compaction.ts +315 -0
  143. package/src/observability/event-day-index.ts +442 -0
  144. package/src/observability/event-log.ts +152 -3
  145. package/src/observability/payload-capture.ts +8 -1
  146. package/src/observability/payload-store.ts +440 -376
  147. package/src/persistence/health-event-flush.ts +36 -0
  148. package/src/persistence/schema.ts +1402 -1360
  149. package/src/persistence/stores/telemetry-store.ts +237 -214
  150. package/src/protocol/codecs.ts +1 -2
  151. package/src/protocol/image-clamp.ts +19 -1
  152. package/src/protocol/registry.ts +8 -0
  153. package/src/protocol/request/gemini.ts +9 -2
  154. package/src/protocol/request/messages.ts +14 -2
  155. package/src/providers/adapters/compatible-adapter.ts +3 -0
  156. package/src/providers/bundled-discovery-providers.ts +122 -119
  157. package/src/providers/configured-openai-providers.ts +136 -134
  158. package/src/providers/default-registry.ts +5 -4
  159. package/src/providers/discovery/openai-model-discovery.ts +5 -0
  160. package/src/providers/discovery/openrouter-free-roster.ts +15 -17
  161. package/src/providers/integrations/cline/cline-quota.ts +132 -125
  162. package/src/providers/integrations/codex/codex-headers.ts +25 -39
  163. package/src/providers/integrations/codex/codex.ts +2 -29
  164. package/src/providers/integrations/cursor/cursor-quota.ts +310 -0
  165. package/src/providers/integrations/cursor/cursor-request.ts +616 -471
  166. package/src/providers/integrations/cursor/cursor-session-manager.ts +319 -281
  167. package/src/providers/integrations/cursor/cursor.ts +1485 -960
  168. package/src/providers/integrations/cursor/protocol/messages.ts +25 -0
  169. package/src/providers/integrations/factory/factory.ts +8 -1
  170. package/src/providers/integrations/gemini.ts +6 -1
  171. package/src/providers/integrations/grok/grok-quota.ts +279 -177
  172. package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -0
  173. package/src/providers/integrations/grok/grok-ratelimit.ts +218 -0
  174. package/src/providers/integrations/opencode/provider.ts +12 -13
  175. package/src/providers/integrations/qoder/qoder-quota.ts +192 -0
  176. package/src/providers/integrations/trae/trae-quota.ts +384 -0
  177. package/src/providers/operations/account-health-service.ts +1523 -1480
  178. package/src/providers/provider-contracts.ts +7 -0
  179. package/src/providers/provider-metadata.ts +6 -0
  180. package/src/providers/quota/quota-support.ts +8 -0
  181. package/src/security/api-key-auth.ts +504 -494
  182. package/src/transport/dispatch/generation.ts +6 -0
  183. package/src/transport/dispatch/proxy-request.ts +24 -1
  184. package/src/transport/dispatch/ratelimit-capture.ts +37 -0
  185. package/src/transport/dispatch/streaming/stream.ts +14 -0
  186. package/src/transport/dispatch/upstream.ts +9 -0
  187. package/src/transport/request/preparer.ts +7 -6
  188. package/src/transport/request/state.ts +9 -0
  189. package/src/transport/request/tenant-preferences.ts +60 -0
  190. package/src/transport/routing/route-catalog.ts +58 -10
  191. package/dist/dashboard/assets/BannedPage-hfCzi-Ux.js.gz +0 -0
  192. package/dist/dashboard/assets/CliToolDetailPage-DLGqYEwI.js +0 -1
  193. package/dist/dashboard/assets/CliToolDetailPage-DLGqYEwI.js.gz +0 -0
  194. package/dist/dashboard/assets/CliToolsPage-DEDyKBlC.js.gz +0 -0
  195. package/dist/dashboard/assets/CombosPage-CfuYiQ8Z.js.gz +0 -0
  196. package/dist/dashboard/assets/ConsoleLogPage-bsLuZmYH.js.gz +0 -0
  197. package/dist/dashboard/assets/CustomizationPage-_UWhwziJ.js.gz +0 -0
  198. package/dist/dashboard/assets/HealthEventsModal-Cjfc5WFS.js +0 -1
  199. package/dist/dashboard/assets/HealthEventsModal-Cjfc5WFS.js.gz +0 -0
  200. package/dist/dashboard/assets/LoginPage-DDVIfZGW.js.gz +0 -0
  201. package/dist/dashboard/assets/ModelPicker-CWkTUtpX.js.gz +0 -0
  202. package/dist/dashboard/assets/OverviewPage-B53YYMng.js +0 -1
  203. package/dist/dashboard/assets/OverviewPage-B53YYMng.js.gz +0 -0
  204. package/dist/dashboard/assets/ProviderDetailPage-B0KaAQf0.js +0 -3
  205. package/dist/dashboard/assets/ProviderDetailPage-B0KaAQf0.js.gz +0 -0
  206. package/dist/dashboard/assets/ProvidersPage-Dx31DgIO.js +0 -1
  207. package/dist/dashboard/assets/ProvidersPage-Dx31DgIO.js.gz +0 -0
  208. package/dist/dashboard/assets/ProxyPage-BpnR4kXT.js +0 -7
  209. package/dist/dashboard/assets/ProxyPage-BpnR4kXT.js.gz +0 -0
  210. package/dist/dashboard/assets/QuotaPage-BpOQCnun.js +0 -1
  211. package/dist/dashboard/assets/QuotaPage-BpOQCnun.js.gz +0 -0
  212. package/dist/dashboard/assets/SettingsPage-DV1eo2J_.js +0 -1
  213. package/dist/dashboard/assets/SettingsPage-DV1eo2J_.js.gz +0 -0
  214. package/dist/dashboard/assets/SetupPage-D6pmQ86L.js.gz +0 -0
  215. package/dist/dashboard/assets/SortableList-DWvKiuSl.js.gz +0 -0
  216. package/dist/dashboard/assets/StudioPage-CmkUrgdj.js.gz +0 -0
  217. package/dist/dashboard/assets/UsagePage-B47uOydr.js.gz +0 -0
  218. package/dist/dashboard/assets/api-keys-uNywYlfv.js.gz +0 -0
  219. package/dist/dashboard/assets/app-DxivpfYG.css +0 -2
  220. package/dist/dashboard/assets/app-DxivpfYG.css.gz +0 -0
  221. package/dist/dashboard/assets/assert-NHwYq95t.js.gz +0 -0
  222. package/dist/dashboard/assets/badge-6uzDDvqs.js.gz +0 -0
  223. package/dist/dashboard/assets/banner-4_WNg2WZ.js.gz +0 -0
  224. package/dist/dashboard/assets/card--AtFedAq.js.gz +0 -0
  225. package/dist/dashboard/assets/cli-tools-B3e7dx3N.js.gz +0 -0
  226. package/dist/dashboard/assets/clipboard-button-NreUx6c8.js.gz +0 -0
  227. package/dist/dashboard/assets/dialog-BtpsJU98.js.gz +0 -0
  228. package/dist/dashboard/assets/download-A0SW9OCW.js +0 -1
  229. package/dist/dashboard/assets/download-A0SW9OCW.js.gz +0 -0
  230. package/dist/dashboard/assets/drawer-WwwtzihF.js.gz +0 -0
  231. package/dist/dashboard/assets/entry-CkryrIea.js +0 -4
  232. package/dist/dashboard/assets/entry-CkryrIea.js.gz +0 -0
  233. package/dist/dashboard/assets/entry-TJIye_jT.js +0 -1
  234. package/dist/dashboard/assets/entry-TJIye_jT.js.gz +0 -0
  235. package/dist/dashboard/assets/entry-jy9_4-Ke.js +0 -1
  236. package/dist/dashboard/assets/entry-jy9_4-Ke.js.gz +0 -0
  237. package/dist/dashboard/assets/github-badge-D_eM3IgG.js.gz +0 -0
  238. package/dist/dashboard/assets/index-BydyKaDz.js.gz +0 -0
  239. package/dist/dashboard/assets/inline-BcNGF8Gr.js.gz +0 -0
  240. package/dist/dashboard/assets/input-BG41et9G.js.gz +0 -0
  241. package/dist/dashboard/assets/layout-C8mer_Ak.js.gz +0 -0
  242. package/dist/dashboard/assets/list-row-Gh_W4OR_.js.gz +0 -0
  243. package/dist/dashboard/assets/live-BJGYRBnR.js.gz +0 -0
  244. package/dist/dashboard/assets/model-bans-DeVLDA7I.js +0 -1
  245. package/dist/dashboard/assets/model-bans-DeVLDA7I.js.gz +0 -0
  246. package/dist/dashboard/assets/mutation-BkoLM5Ba.js.gz +0 -0
  247. package/dist/dashboard/assets/numbers-Bs4LOOuf.js +0 -2
  248. package/dist/dashboard/assets/numbers-Bs4LOOuf.js.gz +0 -0
  249. package/dist/dashboard/assets/routing-CcusqXA7.js.gz +0 -0
  250. package/dist/dashboard/assets/select-BO6h8yNv.js.gz +0 -0
  251. package/dist/dashboard/assets/settings-mpmXTgUa.js.gz +0 -0
  252. package/dist/dashboard/assets/sparkles-BRaRUxAU.js +0 -1
  253. package/dist/dashboard/assets/sparkles-BRaRUxAU.js.gz +0 -0
  254. package/dist/dashboard/assets/stack-DwLmjUFK.js.gz +0 -0
  255. package/dist/dashboard/assets/state-DYdQmdAl.js.gz +0 -0
  256. package/dist/dashboard/assets/switch-CidSfbG7.js +0 -1
  257. package/dist/dashboard/assets/switch-CidSfbG7.js.gz +0 -0
  258. package/dist/dashboard/assets/terminal-Bd4ee8TA.js +0 -1
  259. package/dist/dashboard/assets/terminal-Bd4ee8TA.js.gz +0 -0
  260. package/dist/dashboard/assets/useMutation-w4u4CVue.js.gz +0 -0
  261. /package/dist/dashboard/assets/{app-CafWtVXA.js → app-DCQZRmHY.js} +0 -0
  262. /package/dist/dashboard/assets/{app-CafWtVXA.js.gz → app-DCQZRmHY.js.gz} +0 -0
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * The free tier shared by the OpenRouter-backed providers.
3
3
  *
4
- * Cline, Kilo Code and OpenRouter serve one model catalog: the same 469 ids,
5
- * and the same 9 of them carrying a `:free` suffix. Verified against all three
6
- * live listings — every id below is present under `openrouter`, `kilo` and
7
- * `cline` — so this roster is a catalog fact, not a per-provider guess, and it
8
- * lives in one place rather than being restated three times.
4
+ * Cline, Kilo Code and OpenRouter serve one model catalog, and every id below
5
+ * was verified to exist under `openrouter`, `kilo` and `cline` — so this
6
+ * roster is a catalog fact, not a per-provider guess, and it lives in one
7
+ * place rather than being restated three times. Dropping a row here unlists
8
+ * it from all three providers' seeded catalog at the next boot; live
9
+ * discovery will still find it, which is the intended prune.
9
10
  *
10
11
  * The limits, modalities and capability flags are read from OpenRouter's own
11
12
  * `/api/v1/models`, which states `context_length`,
@@ -13,6 +14,10 @@
13
14
  * `supported_parameters`. That is the serving catalog's declaration, so these
14
15
  * are measurements rather than defaults.
15
16
  *
17
+ * The set is curated: a 2.6B toy (`liquid/lfm-2.5-2.6b:free`) and the
18
+ * upstream content-safety classifier (`nvidia/nemotron-3.5-content-safety:free`)
19
+ * are deliberately not seeded — a prefill classifier is not a chat model.
20
+ *
16
21
  * This is a floor, not the authority: each provider also discovers live, and a
17
22
  * row the listing carries that this roster does not is still found there. The
18
23
  * roster exists so a fresh install has a usable free tier before any fetch, and
@@ -32,15 +37,16 @@ interface FreeRow {
32
37
  readonly ctx: number;
33
38
  readonly out: number;
34
39
  readonly vision?: boolean;
35
- /** `supported_parameters` omits `tools` on the safety classifier only. */
40
+ /** `false` only when `supported_parameters` omits `tools`. */
36
41
  readonly toolCall?: boolean;
37
42
  }
38
43
 
39
44
  /**
40
- * The 9 `:free` ids, with the limits the catalog states for each.
45
+ * The shared `:free` ids still worth seeding, with the limits the catalog
46
+ * states for each.
41
47
  *
42
- * Every row declares `reasoning`: `supported_parameters` lists it on all
43
- * nine. `toolCall` is `true` unless stated otherwise, because a row that
48
+ * Every row declares `reasoning`: `supported_parameters` lists it on every
49
+ * entry. `toolCall` is `true` unless stated otherwise, because a row that
44
50
  * advertises tools must keep them — a false `toolCall` makes the preflight
45
51
  * strip `tools` from the request, which is a wrong capability rather than a
46
52
  * conservative one.
@@ -48,7 +54,6 @@ interface FreeRow {
48
54
  const FREE_ROWS: readonly FreeRow[] = [
49
55
  { id: "cohere/north-mini-code:free", ctx: 256_000, out: 64_000 },
50
56
  { id: "dots-studio/dots-3-note-preview:free", ctx: 512_000, out: 460_800, vision: true },
51
- { id: "liquid/lfm-2.5-2.6b:free", ctx: 65_536, out: 8_192 },
52
57
  { id: "minimax/minimax-m3.1-flash:free", ctx: 1_000_000, out: 524_288, vision: true },
53
58
  {
54
59
  id: "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free",
@@ -57,13 +62,6 @@ const FREE_ROWS: readonly FreeRow[] = [
57
62
  vision: true,
58
63
  },
59
64
  { id: "nvidia/nemotron-3-ultra-550b-a55b:free", ctx: 1_000_000, out: 65_536 },
60
- {
61
- id: "nvidia/nemotron-3.5-content-safety:free",
62
- ctx: 128_000,
63
- out: 8_192,
64
- vision: true,
65
- toolCall: false,
66
- },
67
65
  { id: "nvidia/nemotron-3.5-lightning:free", ctx: 1_000_000, out: 65_536 },
68
66
  { id: "nvidia/nemotron-3.5-super-vl:free", ctx: 1_000_000, out: 131_072, vision: true },
69
67
  ];
@@ -1,125 +1,132 @@
1
- import type { FetchLike, ProviderQuotaResult, ProviderQuotaWindow } from "../../quota/quota-contracts";
2
- import { authCredential, getJson, record, text, unsupportedQuota } from "../../quota/quota-contracts";
3
- import { parseLimitWindows } from "../../quota/quota-limit-windows";
4
- import { getClineCliVersion, getClineSdkVersion } from "../../operations/client-versions";
5
- import { clinePlatform, clinePlatformVersion } from "../../operations/cli-platform";
6
-
7
- /**
8
- * Quota headers always carry the currently resolved CLI version.
9
- *
10
- * The two version headers are two different versions and must stay that way.
11
- * `x-client-version` is the Cline CLI the operator is running (`cline` on npm),
12
- * from the shared pooler. `x-core-version` is the `@cline/sdk` version — a
13
- * separate npm package with its own release line, though one the CLI pins as a
14
- * dependency. This used to send the *client* version in both slots, so the
15
- * billing surface was told our core was `4.1.23`, a vscode extension version
16
- * that `@cline/sdk` never published.
17
- *
18
- * Both surfaces now send the same pairing (`cliVersion` / `sdkVersion`) and the
19
- * same `cline-cli` client type. A header the upstream can validate is not
20
- * cosmetic: an implausible core version is exactly what a "please update and
21
- * re-authenticate" refusal is shaped like, and it is indistinguishable from a
22
- * dead credential from the response alone.
23
- *
24
- * Header identity is not what gates these endpoints: probing `/users/me`,
25
- * `/users/me/plan` and `/users/me/plan/usage-limits` with no identity headers,
26
- * the SDK identity and the CLI identity all returned identical responses. The
27
- * 404 from the plan routes is "no plan history found for user", i.e. account
28
- * state, not a rejected client.
29
- */
30
- function clineQuotaHeaders(): Record<string, string> {
31
- const version = getClineCliVersion();
32
- return {
33
- accept: "application/json",
34
- "content-type": "application/json",
35
- "user-agent": `Cline/${version}`,
36
- "x-platform": clinePlatform(),
37
- "x-platform-version": clinePlatformVersion(),
38
- "x-client-type": "cline-cli",
39
- "x-client-version": version,
40
- "x-core-version": getClineSdkVersion(),
41
- "x-is-multiroot": "false",
42
- };
43
- }
44
-
45
- const CLINE_WINDOW_LABELS: Readonly<Record<string, string>> = {
46
- five_hour: "5 Hour",
47
- weekly: "7 Day",
48
- monthly: "30 Day",
49
- };
50
-
51
- /** Reads Cline's `limits[]` array — one entry per billing window. */
52
- function usageWindows(value: unknown): readonly ProviderQuotaWindow[] {
53
- return [
54
- ...parseLimitWindows(value, {
55
- kindPaths: ["type"],
56
- percentPaths: ["percentUsed"],
57
- resetPaths: ["resetsAt"],
58
- limitPaths: ["limit", "entitlement", "total"],
59
- usedPaths: ["used", "usedCredits"],
60
- // Cline reports a percentage and a ceiling but no absolute used value,
61
- // so the absolute is derived rather than left null.
62
- deriveUsed: (_entry, usedPercent, limit) =>
63
- usedPercent !== null && limit !== null ? (limit * usedPercent) / 100 : null,
64
- labelFor: (kind) => CLINE_WINDOW_LABELS[kind] ?? null,
65
- fallbackLabel: (kind) => kind,
66
- }),
67
- ];
68
- }
69
-
70
- function isMissingPlanHistory(reason: unknown): boolean {
71
- const message =
72
- reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "";
73
- return /\b404\b|\bno plan history\b/i.test(message);
74
- }
75
-
76
- const CLINE_PROVIDER_ID = "cline" as const;
77
- /** Envelope marking a resolved secret as an api key rather than an OAuth grant. */
78
- export const CLINE_API_KEY_PREFIX = "cline-api-key:" as const;
79
-
80
- /** Wraps a resolved api_key secret so the collector can route it to /models. */
81
- export function markClineApiKeyCredential(credential: string): string {
82
- return credential.startsWith(CLINE_API_KEY_PREFIX) ? credential : `${CLINE_API_KEY_PREFIX}${credential}`;
83
- }
84
-
85
- export async function fetchClineQuota(
86
- credential: string,
87
- fetcher: FetchLike,
88
- ): Promise<ProviderQuotaResult> {
89
- // An api_key account has no OAuth envelope, and Cline's quota surface
90
- // answers 401 for one — the upstream's message asks the operator to
91
- // re-authenticate, which is a false verdict: the key is fine and the gateway
92
- // never had a quota to read. Report "not available" and make no upstream
93
- // call. A probe here also cannot distinguish a dead key from Cline refusing
94
- // the client, so the only honest answer is that quota is unavailable.
95
- if (credential.startsWith(CLINE_API_KEY_PREFIX)) {
96
- return unsupportedQuota(CLINE_PROVIDER_ID);
97
- }
98
- const fields = authCredential(credential);
99
- const access = text(fields.accessToken);
100
- if (!access) throw new Error("Cline credential has no access token.");
101
- const headers = {
102
- ...clineQuotaHeaders(),
103
- authorization: `Bearer ${access.startsWith("workos:") ? access : `workos:${access}`}`,
104
- };
105
- const me = record(await getJson("https://api.cline.bot/api/v1/users/me", headers, fetcher));
106
- const userId = text(me?.id);
107
- if (!userId) throw new Error("Cline account response has no user id.");
108
- const [planRaw, limitsRaw] = await Promise.allSettled([
109
- getJson("https://api.cline.bot/api/v1/users/me/plan", headers, fetcher),
110
- getJson("https://api.cline.bot/api/v1/users/me/plan/usage-limits", headers, fetcher),
111
- ]);
112
- const plan = record(planRaw.status === "fulfilled" ? planRaw.value : null);
113
- const limitsFailed = limitsRaw.status === "rejected";
114
- const windows = usageWindows(limitsRaw.status === "fulfilled" ? limitsRaw.value : null);
115
- const error =
116
- limitsFailed && !isMissingPlanHistory(limitsRaw.reason)
117
- ? "Failed to fetch quota limits."
118
- : null;
119
- return {
120
- source: "cline",
121
- plan: text(record(plan?.plan)?.displayName) ?? text(plan?.displayName) ?? "Cline",
122
- windows,
123
- error,
124
- };
125
- }
1
+ import type { FetchLike, ProviderQuotaResult, ProviderQuotaWindow } from "../../quota/quota-contracts";
2
+ import { authCredential, getJson, record, text } from "../../quota/quota-contracts";
3
+ import { parseLimitWindows } from "../../quota/quota-limit-windows";
4
+ import { getClineCliVersion, getClineSdkVersion } from "../../operations/client-versions";
5
+ import { clinePlatform, clinePlatformVersion } from "../../operations/cli-platform";
6
+
7
+ /**
8
+ * Quota headers always carry the currently resolved CLI version.
9
+ *
10
+ * The two version headers are two different versions and must stay that way.
11
+ * `x-client-version` is the Cline CLI the operator is running (`cline` on npm),
12
+ * from the shared pooler. `x-core-version` is the `@cline/sdk` version — a
13
+ * separate npm package with its own release line, though one the CLI pins as a
14
+ * dependency. This used to send the *client* version in both slots, so the
15
+ * billing surface was told our core was `4.1.23`, a vscode extension version
16
+ * that `@cline/sdk` never published.
17
+ *
18
+ * Both surfaces now send the same pairing (`cliVersion` / `sdkVersion`) and the
19
+ * same `cline-cli` client type. A header the upstream can validate is not
20
+ * cosmetic: an implausible core version is exactly what a "please update and
21
+ * re-authenticate" refusal is shaped like, and it is indistinguishable from a
22
+ * dead credential from the response alone.
23
+ *
24
+ * Header identity is not what gates these endpoints: probing `/users/me`,
25
+ * `/users/me/plan` and `/users/me/plan/usage-limits` with no identity headers,
26
+ * the SDK identity and the CLI identity all returned identical responses. The
27
+ * 404 from the plan routes is "no plan history found for user", i.e. account
28
+ * state, not a rejected client.
29
+ */
30
+ function clineQuotaHeaders(): Record<string, string> {
31
+ const version = getClineCliVersion();
32
+ return {
33
+ accept: "application/json",
34
+ "content-type": "application/json",
35
+ "user-agent": `Cline/${version}`,
36
+ "x-platform": clinePlatform(),
37
+ "x-platform-version": clinePlatformVersion(),
38
+ "x-client-type": "cline-cli",
39
+ "x-client-version": version,
40
+ "x-core-version": getClineSdkVersion(),
41
+ "x-is-multiroot": "false",
42
+ };
43
+ }
44
+
45
+ const CLINE_WINDOW_LABELS: Readonly<Record<string, string>> = {
46
+ five_hour: "5 Hour",
47
+ weekly: "7 Day",
48
+ monthly: "30 Day",
49
+ };
50
+
51
+ /** Reads Cline's `limits[]` array — one entry per billing window. */
52
+ function usageWindows(value: unknown): readonly ProviderQuotaWindow[] {
53
+ return [
54
+ ...parseLimitWindows(value, {
55
+ kindPaths: ["type"],
56
+ percentPaths: ["percentUsed"],
57
+ resetPaths: ["resetsAt"],
58
+ limitPaths: ["limit", "entitlement", "total"],
59
+ usedPaths: ["used", "usedCredits"],
60
+ // Cline reports a percentage and a ceiling but no absolute used value,
61
+ // so the absolute is derived rather than left null.
62
+ deriveUsed: (_entry, usedPercent, limit) =>
63
+ usedPercent !== null && limit !== null ? (limit * usedPercent) / 100 : null,
64
+ labelFor: (kind) => CLINE_WINDOW_LABELS[kind] ?? null,
65
+ fallbackLabel: (kind) => kind,
66
+ }),
67
+ ];
68
+ }
69
+
70
+ function isMissingPlanHistory(reason: unknown): boolean {
71
+ const message =
72
+ reason instanceof Error ? reason.message : typeof reason === "string" ? reason : "";
73
+ return /\b404\b|\bno plan history\b/i.test(message);
74
+ }
75
+
76
+ /** Envelope marking a resolved secret as an api key rather than an OAuth grant. */
77
+ export const CLINE_API_KEY_PREFIX = "cline-api-key:" as const;
78
+
79
+ /** Wraps a resolved api_key secret so the collector can route it to /models. */
80
+ export function markClineApiKeyCredential(credential: string): string {
81
+ return credential.startsWith(CLINE_API_KEY_PREFIX) ? credential : `${CLINE_API_KEY_PREFIX}${credential}`;
82
+ }
83
+
84
+ export async function fetchClineQuota(
85
+ credential: string,
86
+ fetcher: FetchLike,
87
+ ): Promise<ProviderQuotaResult> {
88
+ const isApiKey = credential.startsWith(CLINE_API_KEY_PREFIX);
89
+ const bearer = isApiKey ? credential.slice(CLINE_API_KEY_PREFIX.length) : null;
90
+
91
+ let headers: Record<string, string>;
92
+ if (isApiKey && bearer) {
93
+ // An api key is its own bearer token: `Authorization: Bearer sk_...`, with
94
+ // no `workos:` prefix.
95
+ //
96
+ // Prefixing it is what produced "Unauthorized: please make sure you're
97
+ // using the latest version of Cline and re-authenticate" — verified against
98
+ // a live key, where the same key returned 200 on `/users/me` bare and 401
99
+ // once `workos:` was prepended. The message names a version problem, so it
100
+ // reads as a client the upstream refuses rather than a malformed header,
101
+ // which is why it went unrecognised for so long.
102
+ headers = { ...clineQuotaHeaders(), authorization: `Bearer ${bearer}` };
103
+ } else {
104
+ const fields = authCredential(credential);
105
+ const access = text(fields.accessToken);
106
+ if (!access) throw new Error("Cline credential has no access token.");
107
+ headers = {
108
+ ...clineQuotaHeaders(),
109
+ authorization: `Bearer ${access.startsWith("workos:") ? access : `workos:${access}`}`,
110
+ };
111
+ }
112
+ const me = record(await getJson("https://api.cline.bot/api/v1/users/me", headers, fetcher));
113
+ const userId = text(me?.id);
114
+ if (!userId) throw new Error("Cline account response has no user id.");
115
+ const [planRaw, limitsRaw] = await Promise.allSettled([
116
+ getJson("https://api.cline.bot/api/v1/users/me/plan", headers, fetcher),
117
+ getJson("https://api.cline.bot/api/v1/users/me/plan/usage-limits", headers, fetcher),
118
+ ]);
119
+ const plan = record(planRaw.status === "fulfilled" ? planRaw.value : null);
120
+ const limitsFailed = limitsRaw.status === "rejected";
121
+ const windows = usageWindows(limitsRaw.status === "fulfilled" ? limitsRaw.value : null);
122
+ const error =
123
+ limitsFailed && !isMissingPlanHistory(limitsRaw.reason)
124
+ ? "Failed to fetch quota limits."
125
+ : null;
126
+ return {
127
+ source: "cline",
128
+ plan: text(record(plan?.plan)?.displayName) ?? text(plan?.displayName) ?? "Cline",
129
+ windows,
130
+ error,
131
+ };
132
+ }
@@ -16,31 +16,38 @@ interface CodexIdentityHeadersOptions {
16
16
  readonly sessionId?: string | undefined;
17
17
  readonly threadId?: string | undefined;
18
18
  readonly windowId?: string | undefined;
19
- readonly turnId?: string | undefined;
20
- readonly parentTurnId?: string | undefined;
21
- readonly conversationId?: string | undefined;
22
- readonly installationId?: string | undefined;
23
19
  readonly turnMetadataJson?: string | undefined;
24
20
  readonly betaFeatures?: string | undefined;
25
21
  readonly subAgent?: string | undefined;
26
22
  readonly responsesLite?: boolean | undefined;
27
23
  readonly turnState?: string | undefined;
28
- readonly modelsEtag?: string | undefined;
29
- /**
30
- * `x-codex-routing-hint: model=<slug>[;tier=<service_tier>]` — codex-rs
31
- * sends this on every ChatGPT-OAuth Responses/compaction/WebSocket
32
- * request. Callers compute the value; absent/empty omits the header.
33
- */
34
- readonly routingHint?: string | undefined;
35
24
  }
36
25
 
26
+ /**
27
+ * Assembles the request headers the reference ChatGPT-OAuth Responses
28
+ * dispatch sends (`codex-rs` rust-v0.160.0):
29
+ *
30
+ * - default client headers (`default_headers`): `originator`, `user-agent`
31
+ * (no `version` header at this tag);
32
+ * - auth (`add_auth_headers`): `authorization`, `chatgpt-account-id`;
33
+ * - per-request extras (`build_responses_options`): `x-codex-beta-features`?,
34
+ * `x-codex-turn-state`?, `x-oai-attestation`?, lite header when Responses
35
+ * Lite, the compatibility projection (`x-codex-window-id`,
36
+ * `x-codex-turn-metadata`, `x-codex-parent-thread-id`?, `x-openai-subagent`?);
37
+ * - transport options (`ApiResponsesOptions` + stream wrapper):
38
+ * `x-client-request-id` / `session-id` / `thread-id` / `x-openai-subagent`?.
39
+ *
40
+ * Identity values that moved into the body (`client_metadata` — installation,
41
+ * turn id, parent turn id) are absent here by design. `conversation-id`,
42
+ * `turn-id`, `parent-turn-id` direct headers and `x-codex-routing-hint`
43
+ * (WebSocket-only at this tag) are not emitted.
44
+ */
37
45
  export function buildCodexIdentityHeaders(
38
46
  options: CodexIdentityHeadersOptions,
39
47
  ): Record<string, string> {
40
48
  const headers: Record<string, string> = {};
41
49
  headers["user-agent"] = buildCodexUserAgent(options.version);
42
50
  headers["originator"] = "codex_cli_rs";
43
- headers["version"] = options.version;
44
51
  const token =
45
52
  options.credential.secret === undefined
46
53
  ? undefined
@@ -50,37 +57,19 @@ export function buildCodexIdentityHeaders(
50
57
  (token === undefined ? undefined : getCodexAccountId(token));
51
58
  if (accountId !== undefined && accountId.length > 0)
52
59
  headers["chatgpt-account-id"] = accountId;
53
- headers["OpenAI-Beta"] = "responses=experimental";
54
60
  if (options.attestation !== undefined && options.attestation.length > 0)
55
61
  headers["x-oai-attestation"] = options.attestation;
56
62
  if (options.residency !== undefined && options.residency.length > 0)
57
63
  headers["x-openai-internal-codex-residency"] = options.residency;
58
64
  if (options.sessionId !== undefined && options.sessionId.length > 0) {
59
65
  headers["session-id"] = options.sessionId;
60
- headers["x-client-request-id"] = options.sessionId;
61
- if (
62
- options.conversationId === undefined ||
63
- options.conversationId.length === 0
64
- ) {
65
- headers["conversation-id"] = options.sessionId;
66
- }
67
66
  }
68
- if (options.threadId !== undefined && options.threadId.length > 0)
67
+ if (options.threadId !== undefined && options.threadId.length > 0) {
69
68
  headers["thread-id"] = options.threadId;
69
+ headers["x-client-request-id"] = options.threadId;
70
+ }
70
71
  if (options.windowId !== undefined && options.windowId.length > 0)
71
72
  headers["x-codex-window-id"] = options.windowId;
72
- if (options.turnId !== undefined && options.turnId.length > 0)
73
- headers["turn-id"] = options.turnId;
74
- if (options.parentTurnId !== undefined && options.parentTurnId.length > 0)
75
- headers["parent-turn-id"] = options.parentTurnId;
76
- if (options.conversationId !== undefined && options.conversationId.length > 0)
77
- headers["conversation-id"] = options.conversationId;
78
- if (
79
- options.installationId !== undefined &&
80
- options.installationId.length > 0
81
- ) {
82
- headers["x-codex-installation-id"] = options.installationId;
83
- }
84
73
  if (
85
74
  options.turnMetadataJson !== undefined &&
86
75
  options.turnMetadataJson.length > 0
@@ -99,12 +88,6 @@ export function buildCodexIdentityHeaders(
99
88
  if (options.turnState !== undefined && options.turnState.length > 0) {
100
89
  headers["x-codex-turn-state"] = options.turnState;
101
90
  }
102
- if (options.modelsEtag !== undefined && options.modelsEtag.length > 0) {
103
- headers["x-models-etag"] = options.modelsEtag;
104
- }
105
- if (options.routingHint !== undefined && options.routingHint.length > 0) {
106
- headers["x-codex-routing-hint"] = options.routingHint;
107
- }
108
91
  return headers;
109
92
  }
110
93
 
@@ -118,6 +101,7 @@ export function createCodexRequestMetadata(args: {
118
101
  windowId: string;
119
102
  turnId: string;
120
103
  parentTurnId?: string;
104
+ subAgent?: string;
121
105
  requestKind?: string;
122
106
  turnStartedAtUnixMs?: number;
123
107
  compaction?: Record<string, unknown>;
@@ -150,6 +134,8 @@ export function createCodexRequestMetadata(args: {
150
134
  };
151
135
  if (args.parentTurnId !== undefined && args.parentTurnId.length > 0)
152
136
  clientMetadata["parent_turn_id"] = args.parentTurnId;
137
+ if (args.subAgent !== undefined && args.subAgent.length > 0)
138
+ clientMetadata["x-openai-subagent"] = args.subAgent;
153
139
  clientMetadata["x-codex-turn-metadata"] = turnMetadataJson;
154
140
  return { clientMetadata, turnMetadataJson };
155
141
  }
@@ -551,8 +551,8 @@ export function createCodexAdapter(
551
551
  const subAgent = config.subagent;
552
552
  const installationOverride = config.installation_id_override;
553
553
 
554
- // Per-session state for sticky routing (x-codex-turn-state / x-models-etag)
555
- // and identity reuse. Bounded LRU + TTL: an unbounded map would leak
554
+ // Per-session state for sticky routing (x-codex-turn-state) and identity
555
+ // reuse. Bounded LRU + TTL: an unbounded map would leak
556
556
  // memory forever under a long-running process serving many distinct
557
557
  // Codex sessions.
558
558
  const sessionStates = new Map<
@@ -639,16 +639,13 @@ export function createCodexAdapter(
639
639
  const state = getSessionState(sessionId);
640
640
  if (state === undefined) return;
641
641
  let turnState: string | null = null;
642
- let modelsEtag: string | null = null;
643
642
  if (headers instanceof Headers) {
644
643
  turnState = headers.get("x-codex-turn-state");
645
- modelsEtag = headers.get("x-models-etag");
646
644
  } else if (typeof headers === "object") {
647
645
  const rec = headers as Record<string, string>;
648
646
  for (const [k, v] of Object.entries(rec)) {
649
647
  const lower = k.toLowerCase();
650
648
  if (lower === "x-codex-turn-state") turnState = v;
651
- if (lower === "x-models-etag") modelsEtag = v;
652
649
  }
653
650
  }
654
651
  if (
@@ -658,9 +655,6 @@ export function createCodexAdapter(
658
655
  ) {
659
656
  state.turnState = turnState;
660
657
  }
661
- if (modelsEtag !== null && modelsEtag.length > 0) {
662
- state.modelsEtag = modelsEtag;
663
- }
664
658
  }
665
659
 
666
660
  return {
@@ -700,13 +694,10 @@ export function createCodexAdapter(
700
694
  sessionId: identity.session_id,
701
695
  threadId: identity.thread_id,
702
696
  windowId: identity.window_id,
703
- turnId: identity.turn_id,
704
- installationId,
705
697
  turnMetadataJson,
706
698
  betaFeatures: betaRemoteCompaction
707
699
  ? "remote_compaction_v2"
708
700
  : undefined,
709
- routingHint: `model=${body["model"] as string}`,
710
701
  }),
711
702
  };
712
703
  const { res, release } = await postUpstreamJson(
@@ -919,7 +910,6 @@ export function createCodexAdapter(
919
910
  let rawWindowId = nonEmpty(request.window_id);
920
911
  let rawTurnId = nonEmpty(request.turn_id);
921
912
  const rawParentTurnId = nonEmpty(request.parent_turn_id);
922
- const rawConversationId = nonEmpty(request.conversation_id);
923
913
 
924
914
  const installationId = await getInstallationIdCached();
925
915
 
@@ -970,9 +960,6 @@ export function createCodexAdapter(
970
960
  const turnStartedAtUnixMs = Date.now();
971
961
  sessionState.turnStartedAtUnixMs = turnStartedAtUnixMs;
972
962
 
973
- // If conversation_id not supplied, mirror session_id (reference behavior).
974
- const conversationId = rawConversationId ?? effectiveSessionId;
975
-
976
963
  const token =
977
964
  context.credential.secret === undefined
978
965
  ? undefined
@@ -998,12 +985,6 @@ export function createCodexAdapter(
998
985
  const betaFeatures = betaRemoteCompaction
999
986
  ? "remote_compaction_v2"
1000
987
  : undefined;
1001
- // `x-codex-routing-hint` mirrors codex-rs (`codexRoutingHint`): the
1002
- // Codex adapter is OAuth-family only, so the hint is always emitted.
1003
- const serviceTier = request.generation_controls.service_tier;
1004
- const routingHint = serviceTier
1005
- ? `model=${request.model};tier=${serviceTier}`
1006
- : `model=${request.model}`;
1007
988
 
1008
989
  const headers: Record<string, string> = {
1009
990
  "content-type": "application/json",
@@ -1020,19 +1001,11 @@ export function createCodexAdapter(
1020
1001
  sessionId: effectiveSessionId,
1021
1002
  threadId,
1022
1003
  windowId,
1023
- turnId,
1024
- ...(rawParentTurnId === undefined
1025
- ? {}
1026
- : { parentTurnId: rawParentTurnId }),
1027
- conversationId,
1028
- installationId,
1029
1004
  turnMetadataJson,
1030
1005
  betaFeatures,
1031
1006
  subAgent,
1032
1007
  responsesLite,
1033
1008
  turnState: sessionState.turnState,
1034
- modelsEtag: sessionState.modelsEtag,
1035
- routingHint,
1036
1009
  }),
1037
1010
  );
1038
1011
  const outboundFetch = context.outbound_fetch ?? fetchFn;