cartethyia 2.7.8 → 2.8.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 (519) hide show
  1. package/.env.example +3 -3
  2. package/README.md +29 -59
  3. package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js +1 -0
  4. package/dist/dashboard/assets/BannedPage-Cc0vKpMG.js.gz +0 -0
  5. package/dist/dashboard/assets/{CliToolDetailPage-BRWKPQPf.js → CliToolDetailPage-Bqj2aa1T.js} +1 -1
  6. package/dist/dashboard/assets/CliToolDetailPage-Bqj2aa1T.js.gz +0 -0
  7. package/dist/dashboard/assets/{CliToolsPage-BP-Y5iHN.js → CliToolsPage-CXsaOAO2.js} +1 -1
  8. package/dist/dashboard/assets/CliToolsPage-CXsaOAO2.js.gz +0 -0
  9. package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js +1 -0
  10. package/dist/dashboard/assets/CombosPage-BeLpJ4ma.js.gz +0 -0
  11. package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js +1 -0
  12. package/dist/dashboard/assets/ConsoleLogPage-DnV0uZ3m.js.gz +0 -0
  13. package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js +1 -0
  14. package/dist/dashboard/assets/CustomizationPage-CBxAjo63.js.gz +0 -0
  15. package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js +1 -0
  16. package/dist/dashboard/assets/HealthEventsModal-B5yEE0k6.js.gz +0 -0
  17. package/dist/dashboard/assets/{InjectSystemPromptPanel-Cv-2Ol_K.js → InjectSystemPromptPanel-BWFLECmz.js} +2 -2
  18. package/dist/dashboard/assets/InjectSystemPromptPanel-BWFLECmz.js.gz +0 -0
  19. package/dist/dashboard/assets/{LoginPage-bKfz_b1K.js → LoginPage-CgoPc5B9.js} +1 -1
  20. package/dist/dashboard/assets/LoginPage-CgoPc5B9.js.gz +0 -0
  21. package/dist/dashboard/assets/{ModelPicker-B-WQxx_H.js → ModelPicker-D9bFQern.js} +1 -1
  22. package/dist/dashboard/assets/ModelPicker-D9bFQern.js.gz +0 -0
  23. package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js +1 -0
  24. package/dist/dashboard/assets/OverviewPage-B_p4tL8k.js.gz +0 -0
  25. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js +8 -0
  26. package/dist/dashboard/assets/ProviderDetailPage-rs5E2rxv.js.gz +0 -0
  27. package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js +1 -0
  28. package/dist/dashboard/assets/ProviderIcon-D_0PiZtg.js.gz +0 -0
  29. package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js +1 -0
  30. package/dist/dashboard/assets/ProvidersPage-CLI2bJTK.js.gz +0 -0
  31. package/dist/dashboard/assets/{ProxyPage-emlTuxz6.js → ProxyPage-BwNC_-Cx.js} +1 -1
  32. package/dist/dashboard/assets/ProxyPage-BwNC_-Cx.js.gz +0 -0
  33. package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js +1 -0
  34. package/dist/dashboard/assets/QuotaPage-DA-a1iSN.js.gz +0 -0
  35. package/dist/dashboard/assets/SettingsPage-CctTga2C.js +1 -0
  36. package/dist/dashboard/assets/SettingsPage-CctTga2C.js.gz +0 -0
  37. package/dist/dashboard/assets/{SetupPage-CUZrKs9r.js → SetupPage-febLU4r1.js} +1 -1
  38. package/dist/dashboard/assets/SetupPage-febLU4r1.js.gz +0 -0
  39. package/dist/dashboard/assets/{StudioPage-jWmUdDpy.js → StudioPage-M6fvR5Wz.js} +1 -1
  40. package/dist/dashboard/assets/StudioPage-M6fvR5Wz.js.gz +0 -0
  41. package/dist/dashboard/assets/UsagePage-nyeuRqz9.js +56 -0
  42. package/dist/dashboard/assets/UsagePage-nyeuRqz9.js.gz +0 -0
  43. package/dist/dashboard/assets/api-keys-BFOlvrJC.js +1 -0
  44. package/dist/dashboard/assets/api-keys-BFOlvrJC.js.gz +0 -0
  45. package/dist/dashboard/assets/app-Dx2QgsNj.css +2 -0
  46. package/dist/dashboard/assets/app-Dx2QgsNj.css.gz +0 -0
  47. package/dist/dashboard/assets/{assert-CidQVoNo.js → assert-CqTLw5e7.js} +1 -1
  48. package/dist/dashboard/assets/assert-CqTLw5e7.js.gz +0 -0
  49. package/dist/dashboard/assets/{badge-DyI_mJfi.js → badge-C83EqLB0.js} +1 -1
  50. package/dist/dashboard/assets/badge-C83EqLB0.js.gz +0 -0
  51. package/dist/dashboard/assets/ban-durations-B58FyRxC.js +1 -0
  52. package/dist/dashboard/assets/ban-durations-B58FyRxC.js.gz +0 -0
  53. package/dist/dashboard/assets/{banner-B-yXtCqb.js → banner-D1xn7pIe.js} +1 -1
  54. package/dist/dashboard/assets/banner-D1xn7pIe.js.gz +0 -0
  55. package/dist/dashboard/assets/brand/bansos_page.webp +0 -0
  56. package/dist/dashboard/assets/brand/denia_bansos.webp +0 -0
  57. package/dist/dashboard/assets/brand/favicon.webp +0 -0
  58. package/dist/dashboard/assets/brand/login_register.webp +0 -0
  59. package/dist/dashboard/assets/brand/login_register_initial.webp +0 -0
  60. package/dist/dashboard/assets/brand/og_bansos.webp +0 -0
  61. package/dist/dashboard/assets/brand/og_image.webp +0 -0
  62. package/dist/dashboard/assets/brand/sidebar_ico.webp +0 -0
  63. package/dist/dashboard/assets/{card-_HwAEqFF.js → card-DU3Lq5Lm.js} +1 -1
  64. package/dist/dashboard/assets/card-DU3Lq5Lm.js.gz +0 -0
  65. package/dist/dashboard/assets/cli-tools-Dw1U_drK.js +1 -0
  66. package/dist/dashboard/assets/cli-tools-Dw1U_drK.js.gz +0 -0
  67. package/dist/dashboard/assets/{clipboard-button-ByjCk0o9.js → clipboard-button-CEL5Sg_J.js} +1 -1
  68. package/dist/dashboard/assets/clipboard-button-CEL5Sg_J.js.gz +0 -0
  69. package/dist/dashboard/assets/{dialog-BZhOlfz-.js → dialog-BjFR5UHy.js} +1 -1
  70. package/dist/dashboard/assets/dialog-BjFR5UHy.js.gz +0 -0
  71. package/dist/dashboard/assets/{drawer-BADqIRYP.js → drawer-HvBW0AjI.js} +1 -1
  72. package/dist/dashboard/assets/drawer-HvBW0AjI.js.gz +0 -0
  73. package/dist/dashboard/assets/entry-BSu3gFlt.js +4 -0
  74. package/dist/dashboard/assets/entry-BSu3gFlt.js.gz +0 -0
  75. package/dist/dashboard/assets/{entry-COaC-irN.js → entry-CECVELyy.js} +1 -1
  76. package/dist/dashboard/assets/entry-CECVELyy.js.gz +0 -0
  77. package/dist/dashboard/assets/entry-DYYofQ7W.js +1 -0
  78. package/dist/dashboard/assets/entry-DYYofQ7W.js.gz +0 -0
  79. package/dist/dashboard/assets/{formatters-DcHJyRiu.js → formatters-DLW4i8zK.js} +1 -1
  80. package/dist/dashboard/assets/formatters-DLW4i8zK.js.gz +0 -0
  81. package/dist/dashboard/assets/{index-CgKmFngv.js → index-1-fCqe64.js} +2 -2
  82. package/dist/dashboard/assets/index-1-fCqe64.js.gz +0 -0
  83. package/dist/dashboard/assets/{inline-B2BirKry.js → inline-DGvU2oLK.js} +1 -1
  84. package/dist/dashboard/assets/inline-DGvU2oLK.js.gz +0 -0
  85. package/dist/dashboard/assets/{input-D-qV9dKY.js → input-iNnO1X2K.js} +1 -1
  86. package/dist/dashboard/assets/input-iNnO1X2K.js.gz +0 -0
  87. package/dist/dashboard/assets/{layout-B7tVWhtV.js → layout-0q8UGypb.js} +1 -1
  88. package/dist/dashboard/assets/layout-0q8UGypb.js.gz +0 -0
  89. package/dist/dashboard/assets/{list-row-CtkGE3bF.js → list-row-C0w2bQjJ.js} +1 -1
  90. package/dist/dashboard/assets/list-row-C0w2bQjJ.js.gz +0 -0
  91. package/dist/dashboard/assets/{live-DGizF06-.js → live-M5aJ_xCa.js} +1 -1
  92. package/dist/dashboard/assets/live-M5aJ_xCa.js.gz +0 -0
  93. package/dist/dashboard/assets/{mutation-GU7XdEjz.js → mutation-BQ0ccH_A.js} +1 -1
  94. package/dist/dashboard/assets/mutation-BQ0ccH_A.js.gz +0 -0
  95. package/dist/dashboard/assets/{network-C9UE-bOr.js → network-BKeQGT3K.js} +1 -1
  96. package/dist/dashboard/assets/network-BKeQGT3K.js.gz +0 -0
  97. package/dist/dashboard/assets/{numbers-BnAd22a_.js → numbers-CivAk3da.js} +1 -1
  98. package/dist/dashboard/assets/numbers-CivAk3da.js.gz +0 -0
  99. package/dist/dashboard/assets/{pagination-BAywyMzS.js → pagination-CpzvI5ep.js} +1 -1
  100. package/dist/dashboard/assets/pagination-CpzvI5ep.js.gz +0 -0
  101. package/dist/dashboard/assets/providers/devin.svg +9 -0
  102. package/dist/dashboard/assets/providers/devin.svg.gz +0 -0
  103. package/dist/dashboard/assets/providers/windsurf.svg +10 -0
  104. package/dist/dashboard/assets/providers/windsurf.svg.gz +0 -0
  105. package/dist/dashboard/assets/providers/zro.svg +3 -0
  106. package/dist/dashboard/assets/providers/zro.svg.gz +0 -0
  107. package/dist/dashboard/assets/{query-keys-YSoQOUh6.js → query-keys-IKLh0YFd.js} +1 -1
  108. package/dist/dashboard/assets/query-keys-IKLh0YFd.js.gz +0 -0
  109. package/dist/dashboard/assets/quota-DiPD6UKH.js +1 -0
  110. package/dist/dashboard/assets/quota-DiPD6UKH.js.gz +0 -0
  111. package/dist/dashboard/assets/routing-BFQX2VBc.js +1 -0
  112. package/dist/dashboard/assets/routing-BFQX2VBc.js.gz +0 -0
  113. package/dist/dashboard/assets/{select-BCOcv8WK.js → select-CXbck0jG.js} +2 -2
  114. package/dist/dashboard/assets/select-CXbck0jG.js.gz +0 -0
  115. package/dist/dashboard/assets/settings-C_XvMHh9.js +1 -0
  116. package/dist/dashboard/assets/settings-C_XvMHh9.js.gz +0 -0
  117. package/dist/dashboard/assets/{stack-ByKt3Q1g.js → stack-DggRiCxE.js} +1 -1
  118. package/dist/dashboard/assets/stack-DggRiCxE.js.gz +0 -0
  119. package/dist/dashboard/assets/{state-DXbGTx7N.js → state-ClFzfbYp.js} +1 -1
  120. package/dist/dashboard/assets/state-ClFzfbYp.js.gz +0 -0
  121. package/dist/dashboard/assets/story/25817331.webp +0 -0
  122. package/dist/dashboard/assets/story/Shorekeeper.webp +0 -0
  123. package/dist/dashboard/assets/story/cartethyia-god.webp +0 -0
  124. package/dist/dashboard/assets/story/fleurdelys_plus.webp +0 -0
  125. package/dist/dashboard/assets/story/jinhsi-blossom.webp +0 -0
  126. package/dist/dashboard/assets/story/phrolova-a.webp +0 -0
  127. package/dist/dashboard/assets/story/requestdeniawokkjpg.webp +0 -0
  128. package/dist/dashboard/assets/story/wuthering-waves-hiyuki-aemeath-hiyuki-rover.webp +0 -0
  129. package/dist/dashboard/assets/{switch-ZeTsW623.js → switch-CTIFxKqS.js} +1 -1
  130. package/dist/dashboard/assets/switch-CTIFxKqS.js.gz +0 -0
  131. package/dist/dashboard/assets/{useMutation-CbCP4w1o.js → useMutation-BClG3yWj.js} +1 -1
  132. package/dist/dashboard/assets/useMutation-BClG3yWj.js.gz +0 -0
  133. package/dist/dashboard/console-backgrounds.webp +0 -0
  134. package/dist/dashboard/console-backgrounds_initial.webp +0 -0
  135. package/dist/dashboard/donation/qris-risun-marke.webp +0 -0
  136. package/dist/dashboard/index.html +3 -3
  137. package/dist/dashboard/index.html.gz +0 -0
  138. package/dist/dashboard/precompressed-assets.json +59 -56
  139. package/migrations/full-postgres/0044_add_models_warning_metadata.sql +10 -0
  140. package/migrations/full-postgres/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  141. package/migrations/full-postgres/0046_add_accounts_quota_plan_fetched_at.sql +7 -0
  142. package/migrations/lite-sqlite3/0044_add_models_warning_metadata.sql +11 -0
  143. package/migrations/lite-sqlite3/0045_migrate_workbuddy_to_codebuddy.sql +156 -0
  144. package/migrations/lite-sqlite3/0046_add_accounts_quota_plan_fetched_at.sql +4 -0
  145. package/package.json +1 -1
  146. package/src/boot/app.ts +10 -13
  147. package/src/boot/dependencies.ts +12 -4
  148. package/src/boot/flush.ts +20 -0
  149. package/src/boot/lifecycle.ts +7 -5
  150. package/src/config/cooldowns.ts +1 -1
  151. package/src/config/routing.ts +26 -0
  152. package/src/config/spec-metadata.ts +72 -0
  153. package/src/config/spec.ts +20 -6
  154. package/src/console/api-keys/contracts.ts +12 -1
  155. package/src/console/api-keys/routes.ts +50 -31
  156. package/src/console/auth/account-reset.ts +7 -9
  157. package/src/console/backup/contracts.ts +4 -0
  158. package/src/console/backup/export-endpoint.ts +7 -5
  159. package/src/console/cli-tools/service.ts +6 -12
  160. package/src/console/console-router-guards.ts +23 -16
  161. package/src/console/console-router.ts +7 -0
  162. package/src/console/dashboard-assets.ts +5 -5
  163. package/src/console/domain-registration.ts +27 -2
  164. package/src/console/inferhub/normalize.ts +24 -28
  165. package/src/console/inferhub/types.ts +3 -0
  166. package/src/console/ip-bans/routes.ts +93 -0
  167. package/src/console/observability/store.ts +0 -53
  168. package/src/console/providers/catalog/catalog-projections.ts +5 -2
  169. package/src/console/providers/catalog/contracts.ts +123 -0
  170. package/src/console/providers/catalog/discover-constants.ts +6 -0
  171. package/src/console/providers/catalog/model-operations.ts +122 -10
  172. package/src/console/providers/catalog/provider-operations.ts +116 -8
  173. package/src/console/providers/catalog/public-model-store.ts +0 -0
  174. package/src/console/providers/catalog/routes.ts +65 -4
  175. package/src/console/providers/catalog/store-helpers.ts +30 -1
  176. package/src/console/providers/catalog/store.ts +249 -4
  177. package/src/console/quota/account-quota-global.ts +97 -19
  178. package/src/console/quota/account-quota-shared.ts +49 -0
  179. package/src/console/quota/account-quota-tenant.ts +6 -18
  180. package/src/console/quota/native-identity.ts +2 -0
  181. package/src/console/quota/quota-view.ts +19 -3
  182. package/src/console/routing/pools/operations.ts +31 -46
  183. package/src/console/settings/contracts.ts +18 -0
  184. package/src/console/settings/store.ts +32 -0
  185. package/src/console/shared/errors.ts +33 -6
  186. package/src/console/system-prompt/contracts.ts +0 -1
  187. package/src/main.ts +3 -3
  188. package/src/network/pool/byte-accounting.ts +3 -2
  189. package/src/network/pool/selector.ts +182 -39
  190. package/src/network/pool-cooldown-store.ts +89 -14
  191. package/src/network/pool-health.ts +49 -61
  192. package/src/network/rate-limit-verdict.ts +1 -1
  193. package/src/network/ssrf.ts +6 -6
  194. package/src/observability/event-log.ts +7 -361
  195. package/src/observability/event-query.ts +18 -4
  196. package/src/observability/performance-metrics.ts +9 -0
  197. package/src/observability/runtime-metrics.ts +14 -9
  198. package/src/observability/telemetry-buffer.ts +15 -25
  199. package/src/observability/telemetry-reconciliation.ts +9 -37
  200. package/src/observability/telemetry-row.ts +125 -0
  201. package/src/persistence/db-mode.ts +4 -1
  202. package/src/persistence/dialect.ts +30 -0
  203. package/src/persistence/migrate.ts +6 -3
  204. package/src/persistence/schema.ts +28 -2
  205. package/src/persistence/ttl-cache.ts +20 -2
  206. package/src/protocol/codecs.ts +1 -1
  207. package/src/protocol/dsml.ts +35 -87
  208. package/src/protocol/gateway-error.ts +1 -0
  209. package/src/protocol/markup-stream-parser.ts +195 -0
  210. package/src/protocol/reasoning-text.ts +19 -0
  211. package/src/protocol/reasoning.ts +1 -1
  212. package/src/protocol/request/chat.ts +14 -14
  213. package/src/protocol/request/gemini.ts +8 -2
  214. package/src/protocol/request/messages.ts +4 -5
  215. package/src/protocol/response/chat.ts +12 -20
  216. package/src/protocol/response/gemini.ts +6 -1
  217. package/src/protocol/response/omp-tool-call-markup.ts +43 -86
  218. package/src/protocol/response/responses.ts +18 -2
  219. package/src/protocol/sse-decoder.ts +25 -8
  220. package/src/protocol/stop-reason.ts +23 -1
  221. package/src/protocol/stream-error-frames.ts +13 -3
  222. package/src/protocol/think-split.ts +112 -0
  223. package/src/protocol/translation/dialect/qwen.ts +26 -58
  224. package/src/protocol/translation/quirks.ts +1 -1
  225. package/src/protocol/translation/tool-repair.ts +2 -2
  226. package/src/protocol/upstream/send-claude-messages.ts +15 -4
  227. package/src/protocol/upstream-parsing.ts +1 -1
  228. package/src/providers/adapters/compatible-adapter.ts +78 -17
  229. package/src/providers/authentication/oauth-client.ts +1 -1
  230. package/src/providers/authentication/oauth-device-flow.ts +98 -8
  231. package/src/providers/authentication/oauth-refresh-service.ts +153 -59
  232. package/src/providers/configured-provider.ts +148 -0
  233. package/src/providers/default-registry.ts +117 -27
  234. package/src/providers/discovery/discovery-types.ts +12 -0
  235. package/src/providers/discovery/probe-phases.ts +74 -33
  236. package/src/providers/discovery/probing-service.ts +293 -165
  237. package/src/providers/integrations/alibabacp/alibabacp-models.ts +1 -2
  238. package/src/providers/integrations/{dashscope-models.ts → alibabacp/dashscope-models.ts} +2 -2
  239. package/src/providers/integrations/antigravity/antigravity-protocol.ts +121 -2
  240. package/src/providers/integrations/antigravity/antigravity.ts +13 -2
  241. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +72 -34
  242. package/src/providers/integrations/buddy/buddy-chat-shared.ts +191 -35
  243. package/src/providers/integrations/buddy/buddy-checkin.ts +17 -39
  244. package/src/providers/integrations/buddy/buddy-conversation-headers.ts +2 -2
  245. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +5 -5
  246. package/src/providers/integrations/buddy/buddy-identity.ts +89 -6
  247. package/src/providers/integrations/buddy/buddy-media.ts +4 -5
  248. package/src/providers/integrations/buddy/buddy-oauth-shared.ts +2 -2
  249. package/src/providers/integrations/buddy/buddy-quota-shared.ts +1 -1
  250. package/src/providers/integrations/buddy/buddy-usage-shared.ts +1 -3
  251. package/src/providers/integrations/buddy/capabilities.ts +6 -43
  252. package/src/providers/integrations/buddy/codebuddy/cn-catalog.ts +2 -1
  253. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +23 -1
  254. package/src/providers/integrations/buddy/codebuddy/provider.ts +6 -2
  255. package/src/providers/integrations/buddy/codebuddy/shared.ts +38 -27
  256. package/src/providers/integrations/claude/claude-quota.ts +14 -17
  257. package/src/providers/integrations/cline/cline.ts +2 -2
  258. package/src/providers/integrations/commandcode/quota.ts +22 -28
  259. package/src/providers/integrations/cursor/cursor-auth.ts +0 -8
  260. package/src/providers/integrations/cursor/cursor-catalog.ts +2 -0
  261. package/src/providers/integrations/cursor/cursor-exec.ts +745 -0
  262. package/src/providers/integrations/cursor/cursor-http2.ts +2 -0
  263. package/src/providers/integrations/cursor/cursor-request.ts +35 -20
  264. package/src/providers/integrations/cursor/cursor-shared.ts +26 -5
  265. package/src/providers/integrations/cursor/cursor.ts +139 -70
  266. package/src/providers/integrations/cursor/protocol/messages.ts +22 -29
  267. package/src/providers/integrations/cursor/protocol/protobuf-schema.ts +37 -0
  268. package/src/providers/integrations/cursor/protocol/tool-canonical.ts +26 -100
  269. package/src/providers/integrations/devin/devin-auth.ts +353 -0
  270. package/src/providers/integrations/devin/devin-catalog.ts +207 -0
  271. package/src/providers/integrations/devin/devin-http.ts +91 -0
  272. package/src/providers/integrations/devin/devin-quota.ts +214 -0
  273. package/src/providers/integrations/devin/devin-shared.ts +111 -0
  274. package/src/providers/integrations/devin/devin.ts +539 -0
  275. package/src/providers/integrations/devin/protocol/devin-proto.ts +558 -0
  276. package/src/providers/integrations/devin/protocol/framing.ts +124 -0
  277. package/src/providers/integrations/factory/factory-oauth.ts +17 -44
  278. package/src/providers/integrations/factory/factory.ts +2 -2
  279. package/src/providers/integrations/freebuff/assets/official-system-manager.txt +88 -0
  280. package/src/providers/integrations/freebuff/assets/official-system-worker.txt +57 -0
  281. package/src/providers/integrations/freebuff/assets/official-tools.json +1070 -0
  282. package/src/providers/integrations/freebuff/freebuff-catalog.ts +120 -0
  283. package/src/providers/integrations/freebuff/freebuff-constants.ts +1233 -0
  284. package/src/providers/integrations/freebuff/freebuff-discovery.ts +108 -0
  285. package/src/providers/integrations/freebuff/freebuff-oauth.ts +240 -0
  286. package/src/providers/integrations/freebuff/freebuff-quota.ts +260 -0
  287. package/src/providers/integrations/freebuff/freebuff-virtual.ts +89 -0
  288. package/src/providers/integrations/freebuff/freebuff.ts +682 -0
  289. package/src/providers/integrations/freepi/freepi-catalog.ts +60 -0
  290. package/src/providers/integrations/freepi/freepi-discovery.ts +82 -0
  291. package/src/providers/integrations/freepi/freepi-headers.ts +21 -0
  292. package/src/providers/integrations/freepi/freepi-oauth.ts +142 -0
  293. package/src/providers/integrations/freepi/freepi-protocol.ts +242 -0
  294. package/src/providers/integrations/freepi/freepi-quota.ts +126 -0
  295. package/src/providers/integrations/freepi/freepi-realm.ts +35 -0
  296. package/src/providers/integrations/freepi/freepi.ts +292 -0
  297. package/src/providers/integrations/gemini.ts +2 -2
  298. package/src/providers/integrations/grok/grok-oauth.ts +48 -45
  299. package/src/providers/integrations/grok/grok-ratelimit-store.ts +10 -17
  300. package/src/providers/integrations/grok/grok-turn-index.ts +11 -16
  301. package/src/providers/integrations/hermes/hermes-oauth.ts +94 -0
  302. package/src/providers/integrations/{hermes.ts → hermes/hermes.ts} +13 -21
  303. package/src/providers/integrations/hermes/index.ts +2 -0
  304. package/src/providers/integrations/kiro/kiro-catalog.ts +17 -1
  305. package/src/providers/integrations/kiro/kiro-discovery.ts +10 -12
  306. package/src/providers/integrations/kiro/kiro-quota.ts +8 -4
  307. package/src/providers/integrations/kiro/kiro.ts +15 -19
  308. package/src/providers/integrations/ollamacloud/index.ts +2 -0
  309. package/src/providers/integrations/{ollamacloud-models.ts → ollamacloud/models.ts} +2 -2
  310. package/src/providers/integrations/{ollamacloud-quota.ts → ollamacloud/quota.ts} +3 -3
  311. package/src/providers/integrations/opencode/fingerprint.ts +1 -1
  312. package/src/providers/integrations/opencode/opencode-go-quota.ts +2 -8
  313. package/src/providers/integrations/perch/perch-catalog.ts +105 -0
  314. package/src/providers/integrations/perch/perch-credential.ts +25 -0
  315. package/src/providers/integrations/perch/perch-oauth.ts +170 -0
  316. package/src/providers/integrations/perch/perch-protocol.ts +208 -0
  317. package/src/providers/integrations/perch/perch-quota.ts +123 -0
  318. package/src/providers/integrations/perch/perch-realm.ts +82 -0
  319. package/src/providers/integrations/perch/perch-system-prompt.ts +13 -0
  320. package/src/providers/integrations/perch/perch.ts +305 -0
  321. package/src/providers/integrations/perch/saffron-system-prompt.txt +8 -0
  322. package/src/providers/integrations/qoder/chat-proto.ts +328 -328
  323. package/src/providers/integrations/qoder/claim-qoder-credits.ts +19 -60
  324. package/src/providers/integrations/qoder/qoder-adapter.ts +90 -40
  325. package/src/providers/integrations/qoder/qoder-auth.ts +101 -29
  326. package/src/providers/integrations/qoder/qoder-cache.ts +32 -0
  327. package/src/providers/integrations/qoder/qoder-catalog.ts +26 -6
  328. package/src/providers/integrations/qoder/qoder-chat.ts +7 -21
  329. package/src/providers/integrations/qoder/qoder-checkin.ts +6 -6
  330. package/src/providers/integrations/qoder/qoder-grpc.ts +210 -7
  331. package/src/providers/integrations/qoder/qoder-ide.ts +5 -108
  332. package/src/providers/integrations/qoder/qoder-identity-cache.ts +184 -0
  333. package/src/providers/integrations/qoder/qoder-native-identity-store.ts +83 -18
  334. package/src/providers/integrations/qoder/qoder-realm.ts +9 -6
  335. package/src/providers/integrations/qoder/qoder-umid.ts +109 -342
  336. package/src/providers/integrations/qoder/qoder-virtual.ts +33 -1
  337. package/src/providers/integrations/qoder/qoder.ts +3 -7
  338. package/src/providers/integrations/trae/trae-catalog.ts +9 -14
  339. package/src/providers/integrations/trae/trae-headers.ts +36 -11
  340. package/src/providers/integrations/trae/trae-oauth.ts +14 -17
  341. package/src/providers/integrations/trae/trae-protocol.ts +12 -11
  342. package/src/providers/integrations/trae/trae-quota.ts +96 -36
  343. package/src/providers/integrations/trae/trae-realm.ts +74 -15
  344. package/src/providers/integrations/trae/trae.ts +99 -26
  345. package/src/providers/integrations/zcode-start-plan/provider.ts +226 -23
  346. package/src/providers/integrations/zcode-start-plan/quota.ts +19 -2
  347. package/src/providers/integrations/zro/zro.ts +204 -0
  348. package/src/providers/operations/account-error-classification.ts +235 -278
  349. package/src/providers/operations/account-health-service.ts +273 -26
  350. package/src/providers/operations/account-refresh-status.ts +269 -0
  351. package/src/providers/operations/account-tier.ts +66 -0
  352. package/src/providers/operations/client-version-resolver.ts +1 -1
  353. package/src/providers/operations/client-versions.ts +42 -150
  354. package/src/providers/operations/cooldown-rules.ts +56 -0
  355. package/src/providers/operations/{model-catalog-cache.ts → provider-caches.ts} +68 -16
  356. package/src/providers/operations/provider-catalog-seeder.ts +4 -0
  357. package/src/providers/operations/provider-catalog-service.ts +1 -1
  358. package/src/providers/operations/provider-credential-service.ts +102 -32
  359. package/src/providers/operations/provider-rules/github.ts +48 -0
  360. package/src/providers/operations/provider-rules/grok.ts +33 -0
  361. package/src/providers/operations/provider-rules/inception.ts +49 -0
  362. package/src/providers/operations/provider-rules/index.ts +32 -0
  363. package/src/providers/operations/provider-rules/perch.ts +37 -0
  364. package/src/providers/operations/provider-rules/qoder.ts +70 -0
  365. package/src/providers/operations/provider-rules/trae.ts +81 -0
  366. package/src/providers/provider-contracts.ts +33 -2
  367. package/src/providers/{provider-credential-resolver.ts → provider-credentials.ts} +40 -3
  368. package/src/providers/provider-metadata.ts +99 -14
  369. package/src/providers/provider-model-definition.ts +36 -0
  370. package/src/providers/provider-probe.ts +15 -1
  371. package/src/providers/provider-registry.ts +42 -33
  372. package/src/providers/quota/quota-limit-windows.ts +16 -5
  373. package/src/providers/quota/quota-support.ts +1 -1
  374. package/src/providers/quota/quota-window-parser.ts +90 -18
  375. package/src/providers/quota/refresh.ts +96 -18
  376. package/src/security/abuse.ts +71 -0
  377. package/src/security/api-key-auth-cache.ts +1 -1
  378. package/src/security/api-key-auth.ts +6 -2
  379. package/src/transport/dispatch/attempt-finalize.ts +14 -13
  380. package/src/transport/dispatch/attempt-loop.ts +71 -17
  381. package/src/transport/dispatch/generation.ts +2 -8
  382. package/src/transport/dispatch/native-shared.ts +25 -0
  383. package/src/transport/dispatch/proxy-request.ts +9 -46
  384. package/src/transport/dispatch/responses-compact.ts +2 -8
  385. package/src/transport/dispatch/streaming/finalize.ts +9 -6
  386. package/src/transport/dispatch/streaming/stream.ts +9 -36
  387. package/src/transport/dispatch/systemone.ts +2 -8
  388. package/src/transport/dispatch/token-count.ts +2 -6
  389. package/src/transport/dispatch/upstream.ts +67 -1
  390. package/src/transport/drain-endpoint.ts +31 -4
  391. package/src/transport/failure-policy.ts +129 -37
  392. package/src/transport/guards/body-policy.ts +7 -1
  393. package/src/transport/guards/error-lifecycle.ts +13 -1
  394. package/src/transport/guards/request-context.ts +22 -14
  395. package/src/transport/request/preparer.ts +81 -71
  396. package/src/transport/routing/bounded-lru.ts +14 -0
  397. package/src/transport/routing/drain-reserve.ts +129 -0
  398. package/src/transport/routing/plan-gate.ts +7 -1
  399. package/src/transport/routing/round-robin.ts +60 -0
  400. package/src/transport/routing/route-catalog.ts +123 -111
  401. package/src/transport/routing/route-model.ts +51 -9
  402. package/src/transport/routing/router.ts +418 -33
  403. package/src/transport/surface/adapters.ts +11 -2
  404. package/src/transport/surface/chat/adapter.ts +2 -2
  405. package/src/transport/surface/chat/encode.ts +47 -4
  406. package/src/transport/surface/chat/parse.ts +2 -12
  407. package/src/transport/surface/completion.ts +44 -33
  408. package/src/transport/surface/content-parts.ts +21 -0
  409. package/src/transport/surface/messages/adapter.ts +9 -2
  410. package/src/transport/surface/messages/encode.ts +11 -3
  411. package/src/transport/surface/messages/parse.ts +2 -1
  412. package/src/transport/surface/messages/stream.ts +33 -17
  413. package/src/transport/surface/responses/encode.ts +34 -6
  414. package/src/transport/surface/responses/parse.ts +3 -11
  415. package/src/workers/daily-checkin.ts +1 -1
  416. package/src/workers/quota-refresh-sweep.ts +1 -1
  417. package/src/workers/scheduler.ts +22 -4
  418. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js +0 -1
  419. package/dist/dashboard/assets/BannedPage-DeIbIiBU.js.gz +0 -0
  420. package/dist/dashboard/assets/CliToolDetailPage-BRWKPQPf.js.gz +0 -0
  421. package/dist/dashboard/assets/CliToolsPage-BP-Y5iHN.js.gz +0 -0
  422. package/dist/dashboard/assets/CombosPage-B7JHozVa.js +0 -1
  423. package/dist/dashboard/assets/CombosPage-B7JHozVa.js.gz +0 -0
  424. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js +0 -1
  425. package/dist/dashboard/assets/ConsoleLogPage-GX7iGGcu.js.gz +0 -0
  426. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js +0 -1
  427. package/dist/dashboard/assets/CustomizationPage-HVonbGEa.js.gz +0 -0
  428. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js +0 -1
  429. package/dist/dashboard/assets/HealthEventsModal-xaut4OJ9.js.gz +0 -0
  430. package/dist/dashboard/assets/InjectSystemPromptPanel-Cv-2Ol_K.js.gz +0 -0
  431. package/dist/dashboard/assets/LoginPage-bKfz_b1K.js.gz +0 -0
  432. package/dist/dashboard/assets/ModelPicker-B-WQxx_H.js.gz +0 -0
  433. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js +0 -1
  434. package/dist/dashboard/assets/OverviewPage-Z9pJZFo8.js.gz +0 -0
  435. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js +0 -7
  436. package/dist/dashboard/assets/ProviderDetailPage-CMeDBxR1.js.gz +0 -0
  437. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +0 -1
  438. package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
  439. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js +0 -1
  440. package/dist/dashboard/assets/ProvidersPage-BXPdOpG4.js.gz +0 -0
  441. package/dist/dashboard/assets/ProxyPage-emlTuxz6.js.gz +0 -0
  442. package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js +0 -1
  443. package/dist/dashboard/assets/QuotaPage-CMJKNYWb.js.gz +0 -0
  444. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js +0 -1
  445. package/dist/dashboard/assets/SettingsPage-BSy7EwGo.js.gz +0 -0
  446. package/dist/dashboard/assets/SetupPage-CUZrKs9r.js.gz +0 -0
  447. package/dist/dashboard/assets/StudioPage-jWmUdDpy.js.gz +0 -0
  448. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js +0 -56
  449. package/dist/dashboard/assets/UsagePage-5F9tGVGG.js.gz +0 -0
  450. package/dist/dashboard/assets/api-keys-KvX83mRe.js +0 -1
  451. package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
  452. package/dist/dashboard/assets/app-BEsU8hKs.css +0 -2
  453. package/dist/dashboard/assets/app-BEsU8hKs.css.gz +0 -0
  454. package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
  455. package/dist/dashboard/assets/badge-DyI_mJfi.js.gz +0 -0
  456. package/dist/dashboard/assets/banner-B-yXtCqb.js.gz +0 -0
  457. package/dist/dashboard/assets/brand/bansos_page.png +0 -0
  458. package/dist/dashboard/assets/brand/denia_bansos.jpg +0 -0
  459. package/dist/dashboard/assets/brand/favicon.jpg +0 -0
  460. package/dist/dashboard/assets/brand/login_register.jpg +0 -0
  461. package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
  462. package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
  463. package/dist/dashboard/assets/brand/og_image.jpg +0 -0
  464. package/dist/dashboard/assets/brand/sidebar_ico.jpg +0 -0
  465. package/dist/dashboard/assets/card-_HwAEqFF.js.gz +0 -0
  466. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +0 -1
  467. package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
  468. package/dist/dashboard/assets/clipboard-button-ByjCk0o9.js.gz +0 -0
  469. package/dist/dashboard/assets/dialog-BZhOlfz-.js.gz +0 -0
  470. package/dist/dashboard/assets/drawer-BADqIRYP.js.gz +0 -0
  471. package/dist/dashboard/assets/entry-COaC-irN.js.gz +0 -0
  472. package/dist/dashboard/assets/entry-Ca7LSCet.js +0 -4
  473. package/dist/dashboard/assets/entry-Ca7LSCet.js.gz +0 -0
  474. package/dist/dashboard/assets/entry-PDd27uCd.js +0 -1
  475. package/dist/dashboard/assets/entry-PDd27uCd.js.gz +0 -0
  476. package/dist/dashboard/assets/formatters-DcHJyRiu.js.gz +0 -0
  477. package/dist/dashboard/assets/index-CgKmFngv.js.gz +0 -0
  478. package/dist/dashboard/assets/inline-B2BirKry.js.gz +0 -0
  479. package/dist/dashboard/assets/input-D-qV9dKY.js.gz +0 -0
  480. package/dist/dashboard/assets/layout-B7tVWhtV.js.gz +0 -0
  481. package/dist/dashboard/assets/list-row-CtkGE3bF.js.gz +0 -0
  482. package/dist/dashboard/assets/live-DGizF06-.js.gz +0 -0
  483. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +0 -1
  484. package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
  485. package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
  486. package/dist/dashboard/assets/network-C9UE-bOr.js.gz +0 -0
  487. package/dist/dashboard/assets/numbers-BnAd22a_.js.gz +0 -0
  488. package/dist/dashboard/assets/pagination-BAywyMzS.js.gz +0 -0
  489. package/dist/dashboard/assets/query-keys-YSoQOUh6.js.gz +0 -0
  490. package/dist/dashboard/assets/quota-DJ1QlAOq.js +0 -1
  491. package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
  492. package/dist/dashboard/assets/routing-fnCsxgNO.js +0 -1
  493. package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
  494. package/dist/dashboard/assets/select-BCOcv8WK.js.gz +0 -0
  495. package/dist/dashboard/assets/settings-BxslsErn.js +0 -1
  496. package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
  497. package/dist/dashboard/assets/stack-ByKt3Q1g.js.gz +0 -0
  498. package/dist/dashboard/assets/state-DXbGTx7N.js.gz +0 -0
  499. package/dist/dashboard/assets/switch-ZeTsW623.js.gz +0 -0
  500. package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
  501. package/dist/dashboard/console-backgrounds.jpg +0 -0
  502. package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
  503. package/dist/dashboard/donation/qris-risun-marke.png +0 -0
  504. package/src/providers/bundled-discovery-providers.ts +0 -98
  505. package/src/providers/configured-openai-providers.ts +0 -120
  506. package/src/providers/integrations/buddy/turing-device-token.ts +0 -262
  507. package/src/providers/integrations/buddy/workbuddy/cn-oauth.ts +0 -30
  508. package/src/providers/integrations/buddy/workbuddy/cn-provider.ts +0 -58
  509. package/src/providers/integrations/buddy/workbuddy/oauth.ts +0 -31
  510. package/src/providers/integrations/buddy/workbuddy/provider.ts +0 -209
  511. package/src/providers/integrations/buddy/workbuddy/quota.ts +0 -161
  512. package/src/providers/integrations/buddy/workbuddy/shared.ts +0 -285
  513. package/src/providers/operations/model-discovery-cache.ts +0 -34
  514. package/src/providers/operations/probe-fetch.ts +0 -15
  515. package/src/providers/operations/provider-version-cache.ts +0 -30
  516. package/src/providers/provider-capabilities.ts +0 -33
  517. package/src/providers/provider-credential-envelope.ts +0 -43
  518. /package/dist/dashboard/assets/{app-CTxoqyYB.js → app-DIXLjojg.js} +0 -0
  519. /package/dist/dashboard/assets/{app-CTxoqyYB.js.gz → app-DIXLjojg.js.gz} +0 -0
@@ -16,6 +16,7 @@ import {
16
16
  FALLBACK_CLIENT_VERSION,
17
17
  RUN_PATH,
18
18
  isAllowedAgentHost,
19
+ resolveCursorTimezone,
19
20
  } from "./cursor-shared";
20
21
  import { createCursorChecksumHeader, resolveCursorDeviceIds } from "./cursor-identity";
21
22
 
@@ -102,6 +103,7 @@ async function runHeaders(credential: string, clientVersion: string): Promise<Re
102
103
  "x-cursor-streaming": "true",
103
104
  "x-ghost-mode": "true",
104
105
  "x-request-id": crypto.randomUUID(),
106
+ ...(resolveCursorTimezone() === "" ? {} : { "x-cursor-timezone": resolveCursorTimezone() }),
105
107
  "user-agent": "connect-es/1.6.1",
106
108
  };
107
109
  }
@@ -23,8 +23,6 @@ import { encodeJsonAsValue } from "./protocol/struct";
23
23
  // a caller has to discover separately.
24
24
  export {
25
25
  CURSOR_TOOL_VARIANTS,
26
- CursorToolCallProvenance,
27
- cursorNativeToolDeclaration,
28
26
  cursorToolArm,
29
27
  decodeCursorNativeToolCall,
30
28
  } from "./protocol/tool-canonical";
@@ -221,7 +219,9 @@ export function systemPromptFrom(request: CanonicalRequest): string | undefined
221
219
  */
222
220
  export function historyFromRequest(request: CanonicalRequest): CursorHistoryEntry[] {
223
221
  const entries: CursorHistoryEntry[] = [];
224
- for (const message of request.messages) {
222
+ const liveIndex = lastLiveUserMessageIndex(request);
223
+ for (const [index, message] of request.messages.entries()) {
224
+ if (index === liveIndex) continue;
225
225
  const text = messageText(message);
226
226
  if (text.trim().length === 0) continue;
227
227
  if (message.role === "assistant") entries.push({ role: "assistant", content: text });
@@ -237,9 +237,6 @@ export function buildRunRequest(input: {
237
237
  readonly text: string;
238
238
  readonly modelId: string;
239
239
  readonly conversationId: string;
240
- /** Stable parent group; unlike the conversation id it survives a rebase. */
241
- readonly conversationGroupId?: string;
242
- readonly runId?: string;
243
240
  readonly messageId?: string;
244
241
  readonly history?: readonly CursorHistoryEntry[];
245
242
  /** Cursor-declared parameter tuple (effort/context/fast) for this model. */
@@ -279,12 +276,17 @@ export function buildRunRequest(input: {
279
276
  userMessageAction["request_context"] = requestContext;
280
277
  const runRequest: Record<string, unknown> = {
281
278
  conversation_id: input.conversationId,
282
- conversation_group_id: input.conversationGroupId ?? input.conversationId,
283
- run_id: input.runId ?? messageId,
284
279
  action:
285
280
  input.action === "resume"
286
281
  ? { resume_action: { request_context: requestContext } }
287
282
  : { user_message_action: userMessageAction },
283
+ // `model_details` (#3) carries the safe wire id; `requested_model` (#9)
284
+ // carries the parameter tuple. Matches `agent.proto` exactly — the old
285
+ // schema invented extra fields that have no wire meaning.
286
+ model_details: {
287
+ model_id: input.modelId,
288
+ max_mode: input.maxMode ?? false,
289
+ },
288
290
  requested_model: {
289
291
  model_id: input.modelId,
290
292
  max_mode: input.maxMode ?? false,
@@ -292,8 +294,6 @@ export function buildRunRequest(input: {
292
294
  },
293
295
  conversation_state: seeded ? buildSeedConversationState() : input.conversationState,
294
296
  mcp_tools: { mcp_tools: [] },
295
- unknown_flag: 0,
296
- field_12: 0,
297
297
  };
298
298
  return encodeMessage("AgentClientMessage", { run_request: runRequest });
299
299
  }
@@ -311,9 +311,12 @@ export function buildExecStreamClose(execId: number, execIdString?: string): Uin
311
311
  }
312
312
 
313
313
  /** Stops the live Run, as Cursor CLI does before closing the stream. */
314
- export function buildCancelAction(reason: string): Uint8Array {
314
+ export function buildCancelAction(_reason: string): Uint8Array {
315
+ // `agent.proto` CancelAction is empty — the oneof presence is the signal,
316
+ // and the reason travels out-of-band (stream close). Sending a `reason`
317
+ // field would encode bytes the server does not expect.
315
318
  return encodeMessage("AgentClientMessage", {
316
- conversation_action: { cancel_action: { reason } },
319
+ conversation_action: { cancel_action: {} },
317
320
  });
318
321
  }
319
322
 
@@ -440,15 +443,18 @@ export function buildMcpRejected(
440
443
  * `ACM#2 exec_client_message{*_allowlist_precheck_result}` for frames 41/42/43.
441
444
  *
442
445
  * Cursor asks whether a shell command, MCP tool, or web fetch would be
443
- * permitted *before* it emits the real call. This gateway executes none of its
444
- * own surface, so every precheck is answered `allowlisted: false` — the model
445
- * never learns otherwise than that the tool must be routed through the
446
- * advertised MCP channel it was told about.
446
+ * permitted *before* it emits the real call.
447
+ *
448
+ * - `shell` → `true`: the native executor runs shell locally.
449
+ * - `mcp` → `true` only when the tool is client-declared (relayed); the
450
+ * caller decides on the `allowlisted` flag.
451
+ * - `web_fetch` → `false`: web fetch is not executed by this gateway.
447
452
  */
448
453
  export function buildAllowlistPrecheckResult(
449
454
  execId: number,
450
455
  kind: "shell" | "mcp" | "web_fetch",
451
- execIdString?: string,
456
+ execIdString: string | undefined,
457
+ allowlisted: boolean,
452
458
  ): Uint8Array {
453
459
  const frame =
454
460
  kind === "shell"
@@ -457,7 +463,7 @@ export function buildAllowlistPrecheckResult(
457
463
  ? "mcp_allowlist_precheck_result"
458
464
  : "web_fetch_allowlist_precheck_result";
459
465
  return encodeMessage("AgentClientMessage", {
460
- exec_client_message: { id: execId, exec_id: execIdString ?? "", [frame]: { allowlisted: false } },
466
+ exec_client_message: { id: execId, exec_id: execIdString ?? "", [frame]: { allowlisted } },
461
467
  });
462
468
  }
463
469
 
@@ -529,12 +535,21 @@ function toolResultText(part: Extract<ContentPart, { kind: "toolResult" }>): str
529
535
  * only the current prompt.
530
536
  */
531
537
  export function liveUserText(request: CanonicalRequest): string {
538
+ const index = lastLiveUserMessageIndex(request);
539
+ if (index === -1) return "";
540
+ const message = request.messages[index];
541
+ if (message === undefined) return "";
542
+ return messageText(message);
543
+ }
544
+
545
+ /** Index of the current user/tool turn Cursor should send as the live prompt. */
546
+ function lastLiveUserMessageIndex(request: CanonicalRequest): number {
532
547
  for (let index = request.messages.length - 1; index >= 0; index -= 1) {
533
548
  const message = request.messages[index];
534
549
  if (message === undefined) continue;
535
550
  if (message.role !== "user" && message.role !== "tool") continue;
536
551
  const text = messageText(message);
537
- if (text.trim().length > 0) return text;
552
+ if (text.trim().length > 0) return index;
538
553
  }
539
- return "";
554
+ return -1;
540
555
  }
@@ -33,7 +33,7 @@ export const CURSOR_OAUTH_CLIENT_ID = "KbZUR41cY7W6zRSdpSUJ7I7mLYBKOCmB";
33
33
  * operator was not what the request carried. Discovery supersedes both on its
34
34
  * first successful fetch.
35
35
  */
36
- export const FALLBACK_CLIENT_VERSION = "cli-2026.10.19-6bbf2c4";
36
+ export const FALLBACK_CLIENT_VERSION = "cli-3.29.9-306d153";
37
37
 
38
38
  /** Connect-RPC paths. Run is served by the discovered agent origin, not the API host. */
39
39
  export const RUN_PATH = "/agent.v1.AgentService/Run";
@@ -44,6 +44,24 @@ export const SERVER_CONFIG_PATH = "/aiserver.v1.ServerConfigService/GetServerCon
44
44
  export const CONTENT_TYPE_CONNECT_PROTO = "application/connect+proto";
45
45
  export const CONNECT_PROTOCOL_VERSION = "1";
46
46
 
47
+ /**
48
+ * The client's timezone name, as the CLI sends on every request
49
+ * (`Intl.DateTimeFormat().resolvedOptions().timeZone`). Computed once: the
50
+ * value is stable for one process and the call is not cheap enough to repeat
51
+ * per request.
52
+ */
53
+ let cachedTimezone: string | undefined;
54
+ export function resolveCursorTimezone(): string {
55
+ if (cachedTimezone !== undefined) return cachedTimezone;
56
+ try {
57
+ const zone = Intl.DateTimeFormat().resolvedOptions().timeZone;
58
+ cachedTimezone = typeof zone === "string" && zone.length > 0 ? zone : "";
59
+ } catch {
60
+ cachedTimezone = "";
61
+ }
62
+ return cachedTimezone;
63
+ }
64
+
47
65
  /** A raw Cursor API key (as opposed to a JWT already exchanged from one). */
48
66
  const CURSOR_API_KEY_PREFIX = "crsr_";
49
67
 
@@ -65,11 +83,14 @@ export const SESSION_REFRESH_TIMEOUT_MS = 20_000;
65
83
 
66
84
  /**
67
85
  * How long a live Cursor Run is held open waiting for the client's next
68
- * tool-result request. Matches the donor's continuation default: a tool round
69
- * the client never answers must not hold an HTTP/2 stream, an account lease, or
70
- * a pool slot for the life of the process.
86
+ * tool-result request. Matches the donor's relay hold (`CURSOR_RELAY_IDLE_
87
+ * TIMEOUT_MS` in oh-my-pi: 30 minutes): a tool round the client never answers
88
+ * must not hold an HTTP/2 stream, an account lease, or a pool slot for the life
89
+ * of the process, but a human-approved tool round often takes longer than ten
90
+ * minutes — a shorter TTL expired the Run mid-approval and forced the client to
91
+ * resend the whole conversation.
71
92
  */
72
- export const RUN_IDLE_TTL_MS = 10 * 60_000;
93
+ export const RUN_IDLE_TTL_MS = 30 * 60_000;
73
94
 
74
95
  /**
75
96
  * Any HTTPS subdomain of `cursor.sh` is an acceptable Run host: the region
@@ -61,8 +61,10 @@ import { fetchCursorRunOrigin } from "./cursor-catalog";
61
61
  import { defaultParameterValues, fetchCursorModels } from "./cursor-catalog";
62
62
  import type { CursorModelInfo } from "./cursor-catalog";
63
63
  import { decodeValueToJson, decodeStructEntriesToJson } from "./protocol/struct";
64
- import { cursorVariantForExecArgs } from "./protocol/tool-canonical";
64
+ import { encodeMessage } from "./protocol/messages";
65
+ import { executeNativeTool, resolveExecFrame } from "./cursor-exec";
65
66
  import { fnv1a32 } from "../shared/hash";
67
+ import { TtlCache } from "../../../persistence/ttl-cache";
66
68
 
67
69
  /**
68
70
  * Client-heartbeat cadence for every Cursor Run.
@@ -83,25 +85,25 @@ const CURSOR_HEARTBEAT_INTERVAL_MS = 5_000;
83
85
  */
84
86
  const CURSOR_ROSTER_TTL_MS = 300_000;
85
87
  /**
86
- * credential → { roster, fetchedAt } for the parameter lookup.
88
+ * credential → roster for the parameter lookup.
87
89
  *
88
90
  * Keyed by an FNV-1a digest of the credential rather than the raw token so a
89
91
  * long-lived map does not hold live credentials as keys. Bounded by
90
- * `ROSTER_CACHE_MAX` with oldest-first eviction: the TTL is only checked on
91
- * read, so without a cap rotated tokens would grow the map without bound.
92
+ * `ROSTER_CACHE_MAX` with LRU eviction: the TTL is only checked on read, so
93
+ * without a cap rotated tokens would grow the map without bound. Implemented
94
+ * by the canonical `TtlCache` in LRU mode — re-setting moves the entry to the
95
+ * newest position, and the cap evicts the least-recently-used entry.
92
96
  */
93
97
  const ROSTER_CACHE_MAX = 1024;
94
- const rosterCache = new Map<string, { roster: readonly CursorModelInfo[]; fetchedAt: number }>();
98
+ const rosterCache = new TtlCache<{ roster: readonly CursorModelInfo[] }>({
99
+ ttlMs: CURSOR_ROSTER_TTL_MS,
100
+ maxEntries: ROSTER_CACHE_MAX,
101
+ lru: true,
102
+ });
95
103
 
96
104
  function rosterCacheSet(credential: string, roster: readonly CursorModelInfo[]): void {
97
105
  const key = String(fnv1a32(credential));
98
- rosterCache.delete(key); // re-set moves the entry to newest position
99
- rosterCache.set(key, { roster, fetchedAt: Date.now() });
100
- while (rosterCache.size > ROSTER_CACHE_MAX) {
101
- const oldest = rosterCache.keys().next().value;
102
- if (oldest === undefined) break;
103
- rosterCache.delete(oldest);
104
- }
106
+ rosterCache.set(key, { roster });
105
107
  }
106
108
 
107
109
  /**
@@ -111,7 +113,7 @@ function rosterCacheSet(credential: string, roster: readonly CursorModelInfo[]):
111
113
  export const rosterCacheForTests = {
112
114
  size: (): number => rosterCache.size,
113
115
  set: rosterCacheSet,
114
- get: (credential: string): { roster: readonly CursorModelInfo[]; fetchedAt: number } | undefined =>
116
+ get: (credential: string): { roster: readonly CursorModelInfo[] } | undefined =>
115
117
  rosterCache.get(String(fnv1a32(credential))),
116
118
  };
117
119
  /** Client-heartbeat cadence the Cursor edge expects (donor `heartbeatMs`). */
@@ -538,26 +540,56 @@ class CursorAdapter implements ProviderAdapter {
538
540
  );
539
541
  }
540
542
  const resolvedAll = pending.map((item) => cursorRunSessions.resolveCall(item.callId, scope));
541
- // Results previous legs already answered resolve nothing: the replayed
542
- // conversation carries them as history, not as an unanswered call. When
543
- // *no* call resolves AND the call id live nowhere in this process — a
544
- // gateway restart between legs, an evicted idle Run, then — the request is
545
- // by definition a complete replay (every answered call is present in the
546
- // conversation), and the fresh-turn path rebases it into a new Run.
547
- // A call id that is live under another scope keeps the strict refusal:
548
- // adopting that conversation would surface another tenant's Run under
549
- // this caller.
550
- if (
551
- resolvedAll.every((entry) => entry === undefined) &&
552
- isCompleteToolHistoryReplay(request, pending) &&
553
- pending.every((item) => !cursorRunSessions.hasAnyCall(item.callId))
554
- ) {
543
+ const resolved = resolvedAll.filter((entry) => entry !== undefined);
544
+ const allSameRun =
545
+ resolved.length === pending.length && new Set(resolved.map((entry) => entry.session.runId)).size === 1;
546
+ // A call id that is live in this process but resolves to nothing under this
547
+ // caller's scope belongs to another tenant's Run. Rebasing over it would
548
+ // surface that conversation under this caller — always a hard refusal.
549
+ const foreignLive = pending.some(
550
+ (item) =>
551
+ cursorRunSessions.resolveCall(item.callId, scope) === undefined &&
552
+ cursorRunSessions.hasAnyCall(item.callId),
553
+ );
554
+ // Complete-replay rebase (donor / oh-my-pi behavior): when the request is
555
+ // the full transcript — every `call_cursor_*` result it carries is paired
556
+ // with a `toolCall` in the same conversation — and no foreign-live call is
557
+ // involved, cancel whatever live Runs its ids still touch and rebuild a
558
+ // fresh Run from history. This covers the cases the old code refused with
559
+ // a hard 400: a replay mixing results of an already-answered leg with a
560
+ // live one ("mixes live Cursor tool results"), and results spanning
561
+ // multiple live Runs. A hard refusal there is what stalled the client loop
562
+ // every time Cursor replays a superseded transcript; the donor instead
563
+ // closes the parked runs and answers with a fresh turn.
564
+ if (!allSameRun && !foreignLive && isCompleteToolHistoryReplay(request, pending)) {
565
+ for (const entry of resolved) {
566
+ cursorRunSessions.terminate(entry.session.runId, "Cursor Run superseded by a rebase");
567
+ this.#handles.delete(entry.session.runId);
568
+ this.cleanupReader(entry.session.runId);
569
+ }
555
570
  yield* this.#startRun(request, candidate, context);
556
571
  return;
557
572
  }
558
- const resolved = resolvedAll.filter((entry) => entry !== undefined);
559
573
  const runId = resolved[0]?.session.runId;
560
- if (runId === undefined) {
574
+ if (!allSameRun) {
575
+ // Not a safe rebase. Two distinct cases:
576
+ // 1. A partial tail — a result whose `toolCall` is absent from the sent
577
+ // conversation. That is not a complete replay, so there is nothing
578
+ // safe to rebase; name the missing ids so the client can resend the
579
+ // whole transcript.
580
+ // 2. A complete replay that still resolves to mixed runs — either a call
581
+ // id live under another scope (foreignLive, hard refusal) or results
582
+ // spanning more than one live Run with a transcript that is not a
583
+ // complete replay. Both stay refused rather than partially applied.
584
+ const missing = missingToolCallIds(request, pending);
585
+ if (missing.length > 0) {
586
+ throw new GatewayError(
587
+ "invalid_request",
588
+ 400,
589
+ "This tool result has no matching tool call in the sent conversation (request {requestId}); resend the complete conversation. The session went cold — reconnect or resend 🔌",
590
+ { callIds: missing },
591
+ );
592
+ }
561
593
  throw new GatewayError(
562
594
  "invalid_request",
563
595
  400,
@@ -565,12 +597,10 @@ class CursorAdapter implements ProviderAdapter {
565
597
  { callIds: pending.map((item) => item.callId) },
566
598
  );
567
599
  }
568
- if (resolved.some((entry) => entry?.session.runId !== runId)) {
569
- throw new GatewayError(
570
- "invalid_request",
571
- 400,
572
- "These tool results belong to different Cursor Runs (request {requestId}); resend them one Run at a time. The session went cold — reconnect or resend 🔌",
573
- );
600
+ if (runId === undefined) {
601
+ // Unreachable: every path that leaves `allSameRun` false throws above or
602
+ // rebases. This keeps the non-null narrowing legible for the rest.
603
+ throw new GatewayError("invalid_request", 400, "Cursor Run is no longer live (request {requestId}). The session went cold — reconnect or resend 🔌");
574
604
  }
575
605
  const session = cursorRunSessions.get(runId);
576
606
  if (session === undefined) {
@@ -703,7 +733,6 @@ class CursorAdapter implements ProviderAdapter {
703
733
  text: liveUserText(request),
704
734
  modelId,
705
735
  conversationId,
706
- runId,
707
736
  ...(images.length > 0 ? { images } : {}),
708
737
  ...(descriptors.length > 0 ? { descriptors } : {}),
709
738
  ...(systemPrompt === undefined ? {} : { systemPrompt }),
@@ -878,10 +907,11 @@ class CursorAdapter implements ProviderAdapter {
878
907
  context: ProviderDispatchContext,
879
908
  ): Promise<readonly CursorModelInfo[]> {
880
909
  const key = String(fnv1a32(credential));
910
+ // Peek first: `get` deletes expired entries, but the stale roster must
911
+ // survive as the fallback when the refetch fails (see the catch below).
912
+ const stale = rosterCache.peek(key);
881
913
  const cached = rosterCache.get(key);
882
- if (cached !== undefined && Date.now() - cached.fetchedAt < CURSOR_ROSTER_TTL_MS) {
883
- return cached.roster;
884
- }
914
+ if (cached !== undefined) return cached.roster;
885
915
  try {
886
916
  const roster = await fetchCursorModels({
887
917
  credential,
@@ -893,7 +923,7 @@ class CursorAdapter implements ProviderAdapter {
893
923
  rosterCacheSet(credential, roster);
894
924
  return roster;
895
925
  } catch {
896
- return cached?.roster ?? [];
926
+ return stale?.roster ?? [];
897
927
  }
898
928
  }
899
929
 
@@ -929,6 +959,8 @@ class CursorAdapter implements ProviderAdapter {
929
959
  if (execServer !== undefined) {
930
960
  const handled = await this.#handleExecServerMessage(execServer, session, handle);
931
961
  if (handled.kind !== "tool_call") continue;
962
+ // Only MCP arms are relayed to the client for execution (client-
963
+ // declared tools). Native tools are answered in-band by the executor.
932
964
  const callId = `call_cursor_${runId}_${handled.execId}`;
933
965
  const scope = context.continuation_scope;
934
966
  if (scope === undefined) {
@@ -1107,9 +1139,9 @@ class CursorAdapter implements ProviderAdapter {
1107
1139
  return { kind: "handled" };
1108
1140
  }
1109
1141
  // Allowlist prechecks precede the real call. They are approval probes, not
1110
- // invocations — this gateway advertises every executable surface through
1111
- // the MCP channel, so its own host surface is never allowlisted and the
1112
- // typed result frame (not an mcp_result) is what the server waits on.
1142
+ // invocations. Shell is allowlisted because the native executor runs it
1143
+ // locally; MCP is allowlisted only when the tool is client-declared
1144
+ // (relayed to the client); web fetch is never executed and stays denied.
1113
1145
  const precheckKind = recordOf(execServer["shell_allowlist_precheck_args"]) !== undefined
1114
1146
  ? "shell"
1115
1147
  : recordOf(execServer["mcp_allowlist_precheck_args"]) !== undefined
@@ -1118,35 +1150,37 @@ class CursorAdapter implements ProviderAdapter {
1118
1150
  ? "web_fetch"
1119
1151
  : undefined;
1120
1152
  if (precheckKind !== undefined) {
1121
- await handle.write(buildAllowlistPrecheckResult(execId, precheckKind, execIdString));
1153
+ const allowlisted =
1154
+ precheckKind === "shell" ? true
1155
+ : precheckKind === "web_fetch" ? false
1156
+ : this.mcpArgsAreDeclared(execServer, session.descriptors);
1157
+ await handle.write(buildAllowlistPrecheckResult(execId, precheckKind, execIdString, allowlisted));
1122
1158
  return { kind: "handled" };
1123
1159
  }
1124
- // Native first. A Cursor host-exec frame (`read_args`, `shell_args`,
1125
- // `pi_read_args`, …) is relayed to the client as an ordinary tool call
1126
- // instead of being refused. This is the half the earlier direct-forward
1127
- // attempt got wrong: it built typed result frames by hand per variant and
1128
- // they were not matched by the server. The canonical layer owns that
1129
- // mapping now, keyed by the originating variant, so a result can only ever
1130
- // come back in the arm it arrived on.
1131
- const nativeCall = cursorVariantForExecArgs(execServer);
1132
- // Only a tool the request actually declared may be relayed. A host frame
1133
- // for something the client never advertised is an undeclared tool like any
1134
- // other and keeps the refusal — forwarding it would hand the client a call
1135
- // it has no handler for and nowhere to send the result.
1136
- const declaredNative = nativeCall !== undefined &&
1137
- session.descriptors.some((d) => d.toolName === nativeCall.variant.name);
1138
- if (nativeCall !== undefined && declaredNative) {
1139
- return {
1140
- kind: "tool_call",
1141
- execId,
1142
- execIdString,
1143
- toolName: nativeCall.variant.name,
1144
- arguments: JSON.stringify(nativeCall.args),
1145
- };
1160
+ // Native exec frames (`read_args`, `shell_args`, `ct_read_args`, …) are
1161
+ // executed locally by this gateway — not relayed to the client and not
1162
+ // refused. The executor returns the wire result message for the frame's
1163
+ // result oneof, so a reply can only come back in the arm it arrived on.
1164
+ // Frames the executor does not implement fall through to the refusal.
1165
+ const execFrame = resolveExecFrame(execServer);
1166
+ if (execFrame !== undefined) {
1167
+ const executed = await executeNativeTool(execFrame.frameKey, execFrame.args, process.cwd());
1168
+ if (executed !== undefined) {
1169
+ await handle.write(
1170
+ encodeMessage("AgentClientMessage", {
1171
+ exec_client_message: {
1172
+ id: execId,
1173
+ exec_id: execIdString ?? "",
1174
+ [executed.field]: executed.message,
1175
+ },
1176
+ }),
1177
+ );
1178
+ return { kind: "handled" };
1179
+ }
1146
1180
  }
1147
- // MCP is the fallback, not the first stop: client-declared tools have no
1148
- // native slot in `agent.proto`, so they are the ones that must travel this
1149
- // channel. An arm neither path claims falls through to the refusal below.
1181
+ // MCP remains for client-declared tools: they have no native slot, so a
1182
+ // declared `mcp_args` arm is relayed to the client as an ordinary tool
1183
+ // call. Undeclared MCP is refused.
1150
1184
  const mcpArgs = recordOf(execServer["mcp_args"]);
1151
1185
  if (mcpArgs !== undefined) {
1152
1186
  const call = readExecutableMcpCall(mcpArgs, session.descriptors);
@@ -1170,6 +1204,21 @@ class CursorAdapter implements ProviderAdapter {
1170
1204
  return { kind: "handled" };
1171
1205
  }
1172
1206
 
1207
+ /**
1208
+ * True when an MCP allowlist precheck targets a tool the request actually
1209
+ * declared (so it can be relayed to the client). Undeclared MCP is denied.
1210
+ */
1211
+ mcpArgsAreDeclared(
1212
+ execServer: Record<string, unknown>,
1213
+ descriptors: readonly CursorToolDescriptor[],
1214
+ ): boolean {
1215
+ const precheck = recordOf(execServer["mcp_allowlist_precheck_args"]);
1216
+ if (precheck === undefined) return false;
1217
+ const toolName = nonEmptyStringOf(precheck["tool_name"]);
1218
+ if (toolName === undefined) return false;
1219
+ return descriptors.some((d) => d.toolName === toolName);
1220
+ }
1221
+
1173
1222
  /**
1174
1223
  * Answers one interaction query so the Run RPC can continue.
1175
1224
  *
@@ -1343,6 +1392,13 @@ function readPendingToolResult(
1343
1392
  * when it resends its recorded transcript — a gateway restart between legs or
1344
1393
  * an evicted Run — and the only shape safe to rebase into a fresh Run, where
1345
1394
  * the recorded exchange travels as history.
1395
+ *
1396
+ * The matching `toolCall` may live anywhere in the conversation, not only in
1397
+ * the trailing assistant turn: Claude Code resends the full transcript, which
1398
+ * contains several earlier `call_cursor_*` rounds. A scan that accepted only
1399
+ * one trailing assistant/tool pair refused those complete replays with
1400
+ * "no longer pending" even though every result was paired and none was live
1401
+ * (mirrors `isCompleteContinuationReplay` in the preparer).
1346
1402
  */
1347
1403
  function isCompleteToolHistoryReplay(
1348
1404
  request: CanonicalRequest,
@@ -1357,6 +1413,20 @@ function isCompleteToolHistoryReplay(
1357
1413
  return pending.every((item) => calledIds.has(item.callId));
1358
1414
  }
1359
1415
 
1416
+ /** The `call_cursor_*` result ids with no matching `toolCall` in the conversation. */
1417
+ function missingToolCallIds(
1418
+ request: CanonicalRequest,
1419
+ pending: readonly PendingToolResult[],
1420
+ ): string[] {
1421
+ const calledIds = new Set<string>();
1422
+ for (const message of request.messages) {
1423
+ for (const part of message.content) {
1424
+ if (part.kind === "toolCall") calledIds.add(part.call_id);
1425
+ }
1426
+ }
1427
+ return pending.filter((item) => !calledIds.has(item.callId)).map((item) => item.callId);
1428
+ }
1429
+
1360
1430
  /** Reconstructs one tool result's text and images for the adapter's tests. */
1361
1431
  export { decodeStructEntriesToJson, decodeValueToJson };
1362
1432
 
@@ -1374,4 +1444,3 @@ function resolveCursorWireModelId(rawModelId: string): string {
1374
1444
  if (id === "auto") return "default";
1375
1445
  return rawModelId;
1376
1446
  }
1377
-
@@ -25,28 +25,7 @@
25
25
  */
26
26
 
27
27
  import protobuf from "protobufjs"
28
-
29
- // ── Helper: create a root and add a Type with fields ──
30
-
31
- type FieldDef = { id: number; name: string; type: string; repeated?: boolean }
32
- type OneofDef = { name: string; fields: string[] }
33
-
34
- function addType(
35
- root: protobuf.Root,
36
- name: string,
37
- fields: FieldDef[],
38
- oneofs?: OneofDef[],
39
- ): protobuf.Type {
40
- const t = new protobuf.Type(name)
41
- for (const f of fields) {
42
- t.add(new protobuf.Field(f.name, f.id, f.type, f.repeated ? "repeated" : undefined))
43
- }
44
- for (const o of oneofs ?? []) {
45
- t.add(new protobuf.OneOf(o.name, o.fields))
46
- }
47
- root.add(t)
48
- return t
49
- }
28
+ import { addType } from "./protobuf-schema"
50
29
 
51
30
  // ── Utility messages ──
52
31
 
@@ -1809,8 +1788,8 @@ export function createMessageTypes(): protobuf.Root {
1809
1788
  ])
1810
1789
 
1811
1790
  addType(root, "CancelAction", [
1812
- // Cursor CLI CancelAction field #1 is the interruption reason, not an id.
1813
- { id: 1, name: "reason", type: "string" },
1791
+ // `agent.proto` declares CancelAction empty (oneof presence is the signal);
1792
+ // the reason travels on the stream, not in the message.
1814
1793
  ])
1815
1794
 
1816
1795
  // Deferred answers for an AskQuestion the client already replied to with
@@ -1922,8 +1901,24 @@ export function createMessageTypes(): protobuf.Root {
1922
1901
  { id: 8, name: "turns", type: "ConversationTurn", repeated: true },
1923
1902
  ])
1924
1903
 
1904
+ // `agent.proto` ModelDetails.thinking_details — empty in the current build;
1905
+ // declared for wire-shape completeness.
1906
+ addType(root, "ThinkingDetails", [])
1907
+
1908
+ // `agent.proto` AgentRunRequest.skill_options (deprecated upstream; kept for
1909
+ // wire-shape completeness).
1910
+ addType(root, "SkillOptions", [
1911
+ { id: 1, name: "skill_descriptors", type: "SkillDescriptor", repeated: true },
1912
+ ])
1913
+
1925
1914
  addType(root, "ModelDetails", [
1926
1915
  { id: 1, name: "model_id", type: "string" },
1916
+ { id: 2, name: "thinking_details", type: "ThinkingDetails" },
1917
+ { id: 3, name: "display_model_id", type: "string" },
1918
+ { id: 4, name: "display_name", type: "string" },
1919
+ { id: 5, name: "display_name_short", type: "string" },
1920
+ { id: 6, name: "aliases", type: "string", repeated: true },
1921
+ { id: 7, name: "max_mode", type: "bool" },
1927
1922
  ])
1928
1923
 
1929
1924
  addType(root, "McpTools", [
@@ -1933,15 +1928,13 @@ export function createMessageTypes(): protobuf.Root {
1933
1928
  addType(root, "AgentRunRequest", [
1934
1929
  { id: 1, name: "conversation_state", type: "bytes" },
1935
1930
  { id: 2, name: "action", type: "ConversationAction" },
1931
+ { id: 3, name: "model_details", type: "ModelDetails" },
1936
1932
  { id: 4, name: "mcp_tools", type: "McpTools" },
1937
1933
  { id: 5, name: "conversation_id", type: "string" },
1934
+ { id: 6, name: "mcp_file_system_options", type: "McpFileSystemOptions" },
1935
+ { id: 7, name: "skill_options", type: "SkillOptions" },
1938
1936
  { id: 8, name: "custom_system_prompt", type: "string" },
1939
1937
  { id: 9, name: "requested_model", type: "RequestedModel" },
1940
- { id: 10, name: "unknown_flag", type: "uint32" },
1941
- { id: 12, name: "field_12", type: "uint32" },
1942
- { id: 14, name: "selected_subagent_models", type: "RequestedModel", repeated: true },
1943
- { id: 16, name: "conversation_group_id", type: "string" },
1944
- { id: 25, name: "run_id", type: "string" },
1945
1938
  ])
1946
1939
 
1947
1940
  // ── Client heartbeat ──
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared protobufjs schema-builder helpers.
3
+ *
4
+ * Extracted verbatim from the vendored protocol modules that each carried the
5
+ * same implementation: `cursor/protocol/messages.ts` (vendored from
6
+ * `cursor-opencode-provider`, MIT) and `devin/protocol/devin-proto.ts`
7
+ * (vendored from `oh-my-pi`, MIT). The original license texts remain in those
8
+ * files; this module exists so future schema additions have one place to fix.
9
+ */
10
+
11
+ import protobuf from "protobufjs";
12
+
13
+ export type FieldDef = { id: number; name: string; type: string; repeated?: boolean };
14
+ export type OneofDef = { name: string; fields: string[] };
15
+
16
+ /** Create a root Type with fields and optional oneofs, and register it. */
17
+ export function addType(
18
+ root: protobuf.Root,
19
+ name: string,
20
+ fields: FieldDef[],
21
+ oneofs?: OneofDef[],
22
+ ): protobuf.Type {
23
+ const t = new protobuf.Type(name);
24
+ for (const f of fields) {
25
+ t.add(new protobuf.Field(f.name, f.id, f.type, f.repeated ? "repeated" : undefined));
26
+ }
27
+ for (const o of oneofs ?? []) {
28
+ t.add(new protobuf.OneOf(o.name, o.fields));
29
+ }
30
+ root.add(t);
31
+ return t;
32
+ }
33
+
34
+ /** Register an enum on the root. */
35
+ export function addEnum(root: protobuf.Root, name: string, values: Record<string, number>): void {
36
+ root.add(new protobuf.Enum(name, values));
37
+ }