cartethyia 2.5.10 → 2.6.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 (367) hide show
  1. package/.env.example +8 -0
  2. package/dist/dashboard/assets/{BannedPage-Cd30Z0b9.js → BannedPage-l6XMnfr9.js} +1 -1
  3. package/dist/dashboard/assets/BannedPage-l6XMnfr9.js.gz +0 -0
  4. package/dist/dashboard/assets/{CliToolDetailPage-wf5u-ww6.js → CliToolDetailPage-DwOzAW45.js} +1 -1
  5. package/dist/dashboard/assets/CliToolDetailPage-DwOzAW45.js.gz +0 -0
  6. package/dist/dashboard/assets/{CliToolsPage-DykPvuck.js → CliToolsPage-BT3Up3QA.js} +1 -1
  7. package/dist/dashboard/assets/CliToolsPage-BT3Up3QA.js.gz +0 -0
  8. package/dist/dashboard/assets/{CombosPage-DwvSD3vs.js → CombosPage-6kYIUpzR.js} +1 -1
  9. package/dist/dashboard/assets/CombosPage-6kYIUpzR.js.gz +0 -0
  10. package/dist/dashboard/assets/{ConsoleLogPage-CIr7KDex.js → ConsoleLogPage-zRnj1mlR.js} +1 -1
  11. package/dist/dashboard/assets/ConsoleLogPage-zRnj1mlR.js.gz +0 -0
  12. package/dist/dashboard/assets/{CustomizationPage-82X0DDdH.js → CustomizationPage-C7m-H1Rz.js} +1 -1
  13. package/dist/dashboard/assets/CustomizationPage-C7m-H1Rz.js.gz +0 -0
  14. package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js +1 -0
  15. package/dist/dashboard/assets/HealthEventsModal-C8MR_BdB.js.gz +0 -0
  16. package/dist/dashboard/assets/{LoginPage-CS5mPuVF.js → LoginPage-CbphLvjg.js} +1 -1
  17. package/dist/dashboard/assets/LoginPage-CbphLvjg.js.gz +0 -0
  18. package/dist/dashboard/assets/{ModelPicker-C0UCUsVJ.js → ModelPicker-DVny4S5_.js} +1 -1
  19. package/dist/dashboard/assets/ModelPicker-DVny4S5_.js.gz +0 -0
  20. package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js +1 -0
  21. package/dist/dashboard/assets/OverviewPage-DABu2Wgt.js.gz +0 -0
  22. package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js +3 -0
  23. package/dist/dashboard/assets/ProviderDetailPage-C15x9q13.js.gz +0 -0
  24. package/dist/dashboard/assets/{ProvidersPage-vdja5ZPK.js → ProvidersPage-Dfb2896L.js} +1 -1
  25. package/dist/dashboard/assets/ProvidersPage-Dfb2896L.js.gz +0 -0
  26. package/dist/dashboard/assets/{ProxyPage-BB4CNm4-.js → ProxyPage-BaCIdAUt.js} +1 -1
  27. package/dist/dashboard/assets/ProxyPage-BaCIdAUt.js.gz +0 -0
  28. package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js +1 -0
  29. package/dist/dashboard/assets/QuotaPage-BsdoCIw1.js.gz +0 -0
  30. package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js +1 -0
  31. package/dist/dashboard/assets/SettingsPage-KVJFh3Ip.js.gz +0 -0
  32. package/dist/dashboard/assets/{SetupPage-FadUOl9S.js → SetupPage-D4g6vOGc.js} +1 -1
  33. package/dist/dashboard/assets/SetupPage-D4g6vOGc.js.gz +0 -0
  34. package/dist/dashboard/assets/{SortableList-Cfy6_Khv.js → SortableList-C93i14BD.js} +1 -1
  35. package/dist/dashboard/assets/SortableList-C93i14BD.js.gz +0 -0
  36. package/dist/dashboard/assets/{StudioPage-CM92fgJf.js → StudioPage-SgQpymjm.js} +1 -1
  37. package/dist/dashboard/assets/{StudioPage-CM92fgJf.js.gz → StudioPage-SgQpymjm.js.gz} +0 -0
  38. package/dist/dashboard/assets/{UsagePage-BT4MyQeT.js → UsagePage-BFME-eba.js} +2 -2
  39. package/dist/dashboard/assets/UsagePage-BFME-eba.js.gz +0 -0
  40. package/dist/dashboard/assets/{api-keys-DQYLQxpW.js → api-keys-C23S9sgp.js} +1 -1
  41. package/dist/dashboard/assets/api-keys-C23S9sgp.js.gz +0 -0
  42. package/dist/dashboard/assets/{app-B7pVRhJ1.js → app-2KhtHCv9.js} +1 -1
  43. package/dist/dashboard/assets/{app-B7pVRhJ1.js.gz → app-2KhtHCv9.js.gz} +0 -0
  44. package/dist/dashboard/assets/app-CEeaMHOy.css +2 -0
  45. package/dist/dashboard/assets/app-CEeaMHOy.css.gz +0 -0
  46. package/dist/dashboard/assets/{assert-C9FD_JwJ.js → assert-DU6qrMA_.js} +1 -1
  47. package/dist/dashboard/assets/assert-DU6qrMA_.js.gz +0 -0
  48. package/dist/dashboard/assets/{badge-Bvyr7D0u.js → badge-CKsF123d.js} +1 -1
  49. package/dist/dashboard/assets/badge-CKsF123d.js.gz +0 -0
  50. package/dist/dashboard/assets/{banner-DIvKAZih.js → banner-8j7uChA9.js} +1 -1
  51. package/dist/dashboard/assets/banner-8j7uChA9.js.gz +0 -0
  52. package/dist/dashboard/assets/{card-D5_gYIS_.js → card-CdsSlWA3.js} +1 -1
  53. package/dist/dashboard/assets/card-CdsSlWA3.js.gz +0 -0
  54. package/dist/dashboard/assets/{cli-tools-CNqzrfdb.js → cli-tools-BaFUuFFF.js} +1 -1
  55. package/dist/dashboard/assets/cli-tools-BaFUuFFF.js.gz +0 -0
  56. package/dist/dashboard/assets/{clipboard-button-B6vDBtbm.js → clipboard-button-uL5aYLfW.js} +1 -1
  57. package/dist/dashboard/assets/clipboard-button-uL5aYLfW.js.gz +0 -0
  58. package/dist/dashboard/assets/{dialog-CpjrgdwJ.js → dialog-C07hr5_G.js} +1 -1
  59. package/dist/dashboard/assets/dialog-C07hr5_G.js.gz +0 -0
  60. package/dist/dashboard/assets/{drawer-BWmt3b5y.js → drawer-BPO1TcKH.js} +1 -1
  61. package/dist/dashboard/assets/drawer-BPO1TcKH.js.gz +0 -0
  62. package/dist/dashboard/assets/{entry-C_oakV3z.js → entry-Bz_CtDfC.js} +1 -1
  63. package/dist/dashboard/assets/entry-Bz_CtDfC.js.gz +0 -0
  64. package/dist/dashboard/assets/entry-CwPRKk0d.js +4 -0
  65. package/dist/dashboard/assets/entry-CwPRKk0d.js.gz +0 -0
  66. package/dist/dashboard/assets/entry-GJRbmrpA.js +1 -0
  67. package/dist/dashboard/assets/entry-GJRbmrpA.js.gz +0 -0
  68. package/dist/dashboard/assets/{github-badge-RRHHWGxh.js → github-badge-DmQreSDd.js} +1 -1
  69. package/dist/dashboard/assets/github-badge-DmQreSDd.js.gz +0 -0
  70. package/dist/dashboard/assets/{index-Bhey_rg_.js → index-Bblu6erR.js} +2 -2
  71. package/dist/dashboard/assets/index-Bblu6erR.js.gz +0 -0
  72. package/dist/dashboard/assets/{inline-C2_EQ5Uf.js → inline-C9dCYaBZ.js} +1 -1
  73. package/dist/dashboard/assets/inline-C9dCYaBZ.js.gz +0 -0
  74. package/dist/dashboard/assets/{input-BMRlQ450.js → input-CjCnXweJ.js} +1 -1
  75. package/dist/dashboard/assets/input-CjCnXweJ.js.gz +0 -0
  76. package/dist/dashboard/assets/{layout-XR5xP8ew.js → layout-V6y8DAkl.js} +1 -1
  77. package/dist/dashboard/assets/layout-V6y8DAkl.js.gz +0 -0
  78. package/dist/dashboard/assets/{list-row-Cw07dioc.js → list-row-CM1AaG_y.js} +1 -1
  79. package/dist/dashboard/assets/list-row-CM1AaG_y.js.gz +0 -0
  80. package/dist/dashboard/assets/{live-BivGfuUs.js → live-ta6GMb-8.js} +1 -1
  81. package/dist/dashboard/assets/live-ta6GMb-8.js.gz +0 -0
  82. package/dist/dashboard/assets/{model-bans-CmffjLuG.js → model-bans-BmYTmbAk.js} +1 -1
  83. package/dist/dashboard/assets/model-bans-BmYTmbAk.js.gz +0 -0
  84. package/dist/dashboard/assets/{mutation-46P0oWMe.js → mutation-g_fduiNw.js} +1 -1
  85. package/dist/dashboard/assets/mutation-g_fduiNw.js.gz +0 -0
  86. package/dist/dashboard/assets/{numbers-DrdA9oSM.js → numbers-BOevJzdz.js} +1 -1
  87. package/dist/dashboard/assets/numbers-BOevJzdz.js.gz +0 -0
  88. package/dist/dashboard/assets/{query-keys-BFhGpwR1.js → query-keys-UETqfytp.js} +1 -1
  89. package/dist/dashboard/assets/query-keys-UETqfytp.js.gz +0 -0
  90. package/dist/dashboard/assets/{routing-CAb4F37R.js → routing-BPUo-W8d.js} +1 -1
  91. package/dist/dashboard/assets/routing-BPUo-W8d.js.gz +0 -0
  92. package/dist/dashboard/assets/{select-CxVVB__N.js → select-C980c4cz.js} +1 -1
  93. package/dist/dashboard/assets/select-C980c4cz.js.gz +0 -0
  94. package/dist/dashboard/assets/{settings-DNX9sydi.js → settings--3hxSyry.js} +1 -1
  95. package/dist/dashboard/assets/settings--3hxSyry.js.gz +0 -0
  96. package/dist/dashboard/assets/{stack-uPOwM9Hs.js → stack-_gfTdLVB.js} +1 -1
  97. package/dist/dashboard/assets/stack-_gfTdLVB.js.gz +0 -0
  98. package/dist/dashboard/assets/{state-D1Yz79iL.js → state-2c5V4zo7.js} +1 -1
  99. package/dist/dashboard/assets/state-2c5V4zo7.js.gz +0 -0
  100. package/dist/dashboard/assets/{switch-sevsRrRo.js → switch-Dkke7Naf.js} +1 -1
  101. package/dist/dashboard/assets/switch-Dkke7Naf.js.gz +0 -0
  102. package/dist/dashboard/assets/{useMutation-Dem0lXQj.js → useMutation-EDFRkn1i.js} +1 -1
  103. package/dist/dashboard/assets/useMutation-EDFRkn1i.js.gz +0 -0
  104. package/dist/dashboard/index.html +1 -1
  105. package/dist/dashboard/index.html.gz +0 -0
  106. package/dist/dashboard/precompressed-assets.json +51 -51
  107. package/migrations/README.md +52 -52
  108. package/migrations/full-postgres/0020_add_telemetry_event_analytics.sql +77 -0
  109. package/migrations/full-postgres/0021_add_api_key_concurrency_tolerance.sql +13 -0
  110. package/migrations/full-postgres/0022_add_inferhub_config_key.sql +17 -0
  111. package/migrations/full-postgres/0023_drop_api_key_concurrency_tolerance.sql +13 -0
  112. package/migrations/lite-sqlite3/0020_add_telemetry_event_analytics.sql +69 -0
  113. package/migrations/lite-sqlite3/0021_add_api_key_concurrency_tolerance.sql +13 -0
  114. package/migrations/lite-sqlite3/0022_add_inferhub_config_key.sql +17 -0
  115. package/migrations/lite-sqlite3/0023_drop_api_key_concurrency_tolerance.sql +13 -0
  116. package/package.json +96 -95
  117. package/scripts/cli/admin.ts +126 -128
  118. package/scripts/cli/autostart.ts +237 -237
  119. package/scripts/cli/background.ts +120 -110
  120. package/scripts/cli/bin/carte.cjs +8 -8
  121. package/scripts/cli/bin/cartethyia.cjs +167 -167
  122. package/scripts/cli/console-menu.ts +39 -30
  123. package/scripts/cli/controller.ts +145 -0
  124. package/scripts/cli/env.ts +99 -99
  125. package/scripts/cli/gateway.ts +264 -260
  126. package/scripts/cli/instance.ts +234 -224
  127. package/scripts/cli/main.ts +188 -167
  128. package/scripts/cli/platform.ts +64 -64
  129. package/scripts/cli/prompt.ts +217 -79
  130. package/scripts/cli/services.ts +88 -88
  131. package/scripts/cli/setup.ts +445 -445
  132. package/scripts/cli/start.ts +85 -85
  133. package/scripts/cli/startup-signal.ts +23 -23
  134. package/scripts/cli/telemetry.ts +70 -0
  135. package/scripts/cli/tray/index.ts +263 -263
  136. package/scripts/cli/tray/windows.ps1 +107 -107
  137. package/scripts/cli/tray.ts +29 -4
  138. package/scripts/cli/update.ts +39 -39
  139. package/scripts/cli/version.ts +67 -67
  140. package/scripts/cli/wordmark.ts +58 -58
  141. package/src/boot/app.ts +16 -1
  142. package/src/boot/dependencies.ts +128 -25
  143. package/src/config/resources.ts +137 -0
  144. package/src/config/runtime.ts +133 -76
  145. package/src/config/spec.ts +30 -29
  146. package/src/config/telemetry.ts +0 -25
  147. package/src/console/api-keys/bucket-spend.ts +14 -17
  148. package/src/console/api-keys/contracts.ts +410 -410
  149. package/src/console/api-keys/routes.ts +12 -0
  150. package/src/console/audit/store.ts +2 -3
  151. package/src/console/auth/setup-service.ts +8 -1
  152. package/src/console/backup/routes.ts +7 -0
  153. package/src/console/backup/service.ts +463 -463
  154. package/src/console/backup/store.ts +16 -11
  155. package/src/console/backup/validate.ts +1 -4
  156. package/src/console/cli-tools/fs-ops.ts +18 -2
  157. package/src/console/data-management/contracts.ts +1 -8
  158. package/src/console/data-management/operations.ts +61 -48
  159. package/src/console/data-management/routes.ts +4 -2
  160. package/src/console/domain-registration.ts +25 -3
  161. package/src/console/inferhub/client.ts +139 -0
  162. package/src/console/inferhub/key-store.ts +129 -0
  163. package/src/console/inferhub/normalize.ts +347 -0
  164. package/src/console/inferhub/routes.ts +411 -0
  165. package/src/console/inferhub/store.ts +109 -0
  166. package/src/console/inferhub/types.ts +183 -0
  167. package/src/console/observability/contracts.ts +2 -41
  168. package/src/console/observability/store.ts +176 -371
  169. package/src/console/providers/catalog/public-model-store.ts +0 -0
  170. package/src/console/providers/catalog/store.ts +23 -36
  171. package/src/console/quota/account-quota-shared.ts +52 -52
  172. package/src/console/quota/account-quota-tenant.ts +16 -24
  173. package/src/console/quota/refresh.ts +441 -441
  174. package/src/console/routing/pools/import-parse.ts +206 -206
  175. package/src/console/routing/pools/operations.ts +543 -543
  176. package/src/console/routing/pools/probe-verdict.ts +132 -132
  177. package/src/console/routing/pools/routes.ts +181 -181
  178. package/src/console/settings/contracts.ts +42 -40
  179. package/src/console/settings/store.ts +25 -25
  180. package/src/console/share/share-stats.ts +22 -4
  181. package/src/console/share/share-usage.ts +30 -4
  182. package/src/console/shared/cursor.ts +15 -0
  183. package/src/console/updates/routes.ts +12 -0
  184. package/src/main.ts +13 -5
  185. package/src/network/pool/resolver.ts +1104 -1104
  186. package/src/observability/client-name.ts +22 -0
  187. package/src/observability/event-log.ts +5 -29
  188. package/src/observability/event-query.ts +690 -0
  189. package/src/observability/log-ring.ts +26 -2
  190. package/src/observability/log-sanitize.ts +118 -0
  191. package/src/observability/logger.ts +17 -13
  192. package/src/observability/metrics.ts +35 -0
  193. package/src/observability/payload-capture.ts +114 -41
  194. package/src/observability/payload-store.ts +249 -24
  195. package/src/observability/raw-event-writer.ts +118 -0
  196. package/src/observability/runtime-metrics.ts +71 -1
  197. package/src/observability/telemetry-buffer.ts +100 -17
  198. package/src/observability/telemetry-reconciliation.ts +417 -0
  199. package/src/persistence/boot.ts +20 -9
  200. package/src/persistence/db-pg.ts +13 -0
  201. package/src/persistence/db-sqlite.ts +569 -497
  202. package/src/persistence/dialect.ts +3 -2
  203. package/src/persistence/migrate.ts +181 -181
  204. package/src/persistence/schema.ts +1496 -1406
  205. package/src/persistence/shared-cache.ts +5 -1
  206. package/src/persistence/stores/api-key-store.ts +581 -581
  207. package/src/persistence/stores/telemetry-store.ts +124 -63
  208. package/src/protocol/reasoning-text.ts +30 -0
  209. package/src/protocol/response/chat.ts +33 -7
  210. package/src/protocol/response/codex.ts +14 -2
  211. package/src/protocol/response/messages.ts +5 -2
  212. package/src/providers/authentication/oauth-flow-store.ts +3 -10
  213. package/src/providers/bundled-discovery-providers.ts +122 -122
  214. package/src/providers/default-registry.ts +10 -0
  215. package/src/providers/discovery/catalog/catalog-blob.ts +18 -18
  216. package/src/providers/discovery/models-dev-catalog.ts +354 -354
  217. package/src/providers/discovery/probe-phases.ts +43 -7
  218. package/src/providers/discovery/probing-service.ts +70 -32
  219. package/src/providers/integrations/antigravity/antigravity.ts +9 -2
  220. package/src/providers/integrations/buddy/buddy-catalog-shared.ts +85 -17
  221. package/src/providers/integrations/buddy/buddy-chat-shared.ts +934 -934
  222. package/src/providers/integrations/buddy/buddy-checkin.ts +5 -9
  223. package/src/providers/integrations/buddy/buddy-discovery-shared.ts +3 -7
  224. package/src/providers/integrations/buddy/buddy-quota-shared.ts +3 -1
  225. package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +57 -49
  226. package/src/providers/integrations/buddy/codebuddy/provider.ts +180 -180
  227. package/src/providers/integrations/buddy/workbuddy/provider.ts +206 -206
  228. package/src/providers/integrations/buddy/workbuddy/quota.ts +161 -161
  229. package/src/providers/integrations/claude/claude-quota.ts +2 -2
  230. package/src/providers/integrations/claude/claude-user-profiles.ts +3 -2
  231. package/src/providers/integrations/claude/claude.ts +3 -4
  232. package/src/providers/integrations/cline/cline.ts +94 -8
  233. package/src/providers/integrations/codex/codex.ts +61 -37
  234. package/src/providers/integrations/cursor/cursor-catalog.ts +368 -368
  235. package/src/providers/integrations/cursor/cursor-http2.ts +300 -300
  236. package/src/providers/integrations/cursor/cursor-identity.ts +2 -4
  237. package/src/providers/integrations/cursor/cursor-request.ts +11 -0
  238. package/src/providers/integrations/cursor/cursor.ts +29 -17
  239. package/src/providers/integrations/cursor/protocol/framing.ts +129 -129
  240. package/src/providers/integrations/cursor/protocol/messages.ts +2380 -2380
  241. package/src/providers/integrations/cursor/protocol/struct.ts +305 -305
  242. package/src/providers/integrations/cursor/protocol/tool-canonical.ts +275 -0
  243. package/src/providers/integrations/grok/grok-quota.ts +279 -279
  244. package/src/providers/integrations/grok/grok-ratelimit-store.ts +73 -73
  245. package/src/providers/integrations/grok/grok-ratelimit.ts +218 -218
  246. package/src/providers/integrations/hermes.ts +7 -1
  247. package/src/providers/integrations/inferhub.ts +201 -73
  248. package/src/providers/integrations/kilo/kilo.ts +152 -152
  249. package/src/providers/integrations/kiro/kiro-machine-id.ts +2 -4
  250. package/src/providers/integrations/openrouter/provider.ts +129 -129
  251. package/src/providers/integrations/qoder/qoder-quota.ts +193 -192
  252. package/src/providers/integrations/qoder/qoder-realm.ts +106 -106
  253. package/src/providers/integrations/trae/trae-oauth.ts +4 -26
  254. package/src/providers/integrations/trae/trae-quota.ts +389 -384
  255. package/src/providers/integrations/trae/trae-realm.ts +15 -2
  256. package/src/providers/integrations/xiaomi-mimo/think-stream.ts +100 -24
  257. package/src/providers/integrations/zcode/oauth.ts +1 -1
  258. package/src/providers/operations/account-health-service.ts +605 -279
  259. package/src/providers/provider-metadata.ts +12 -1
  260. package/src/providers/provider-model-definition.ts +373 -373
  261. package/src/providers/quota/quota-contracts.ts +3 -7
  262. package/src/providers/search/search-provider.ts +2 -6
  263. package/src/security/admission/contracts.ts +10 -0
  264. package/src/security/admission/in-memory-store.ts +89 -14
  265. package/src/security/admission/lua.ts +191 -182
  266. package/src/security/admission/redis-store.ts +30 -0
  267. package/src/security/admission/service.ts +18 -0
  268. package/src/security/api-key-auth-cache.ts +96 -0
  269. package/src/security/api-key-auth.ts +11 -100
  270. package/src/security/client-router-fingerprint.ts +3 -7
  271. package/src/security/ip-boundary.ts +7 -3
  272. package/src/shared/coerce.ts +67 -0
  273. package/src/shared/hash.ts +6 -0
  274. package/src/shared/time.ts +7 -0
  275. package/src/transport/dispatch/attempt-finalize.ts +26 -138
  276. package/src/transport/dispatch/attempt-loop.ts +103 -81
  277. package/src/transport/dispatch/capture-policy.ts +78 -0
  278. package/src/transport/dispatch/proxy-request.ts +4 -2
  279. package/src/transport/dispatch/streaming/encoder.ts +111 -5
  280. package/src/transport/dispatch/streaming/stream.ts +19 -2
  281. package/src/transport/dispatch/streaming/thinking-loop.ts +300 -0
  282. package/src/transport/failure-policy.ts +27 -0
  283. package/src/transport/guards/abuse-guard.ts +2 -2
  284. package/src/transport/guards/auth-guard.ts +17 -0
  285. package/src/transport/guards/body-policy.ts +3 -2
  286. package/src/transport/guards/concurrency-guard.ts +89 -13
  287. package/src/transport/guards/early-rejection-capture.ts +43 -3
  288. package/src/transport/guards/error-lifecycle.ts +10 -2
  289. package/src/transport/guards/guard-errors.ts +23 -0
  290. package/src/transport/guards/headroom-guard.ts +41 -7
  291. package/src/transport/guards/pipeline.ts +24 -0
  292. package/src/transport/guards/request-context.ts +2 -1
  293. package/src/transport/routing/admission.ts +51 -9
  294. package/src/transport/routing/bounded-lru.ts +49 -0
  295. package/src/transport/routing/candidate-index.ts +88 -0
  296. package/src/transport/routing/reservations.ts +121 -7
  297. package/src/transport/routing/round-robin.ts +10 -22
  298. package/src/transport/routing/route-catalog.ts +26 -13
  299. package/src/transport/routing/route-model.ts +22 -2
  300. package/src/transport/routing/router.ts +53 -31
  301. package/src/transport/surface/chat/encode.ts +2 -3
  302. package/src/transport/surface/chat/parse.ts +2 -1
  303. package/src/transport/surface/dialects.ts +11 -3
  304. package/src/transport/surface/messages/encode.ts +2 -1
  305. package/src/transport/surface/messages/parse.ts +3 -1
  306. package/src/transport/surface/messages/stream.ts +2 -6
  307. package/src/workers/maintenance-lease.ts +200 -0
  308. package/src/workers/scheduler.ts +101 -1
  309. package/dist/dashboard/assets/BannedPage-Cd30Z0b9.js.gz +0 -0
  310. package/dist/dashboard/assets/CliToolDetailPage-wf5u-ww6.js.gz +0 -0
  311. package/dist/dashboard/assets/CliToolsPage-DykPvuck.js.gz +0 -0
  312. package/dist/dashboard/assets/CombosPage-DwvSD3vs.js.gz +0 -0
  313. package/dist/dashboard/assets/ConsoleLogPage-CIr7KDex.js.gz +0 -0
  314. package/dist/dashboard/assets/CustomizationPage-82X0DDdH.js.gz +0 -0
  315. package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js +0 -1
  316. package/dist/dashboard/assets/HealthEventsModal-BYPucTTn.js.gz +0 -0
  317. package/dist/dashboard/assets/LoginPage-CS5mPuVF.js.gz +0 -0
  318. package/dist/dashboard/assets/ModelPicker-C0UCUsVJ.js.gz +0 -0
  319. package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js +0 -1
  320. package/dist/dashboard/assets/OverviewPage-Dk_ew3YU.js.gz +0 -0
  321. package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js +0 -3
  322. package/dist/dashboard/assets/ProviderDetailPage-CAiRn2Q0.js.gz +0 -0
  323. package/dist/dashboard/assets/ProvidersPage-vdja5ZPK.js.gz +0 -0
  324. package/dist/dashboard/assets/ProxyPage-BB4CNm4-.js.gz +0 -0
  325. package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js +0 -1
  326. package/dist/dashboard/assets/QuotaPage-FEc4iNyq.js.gz +0 -0
  327. package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js +0 -1
  328. package/dist/dashboard/assets/SettingsPage-Dlfi5L4N.js.gz +0 -0
  329. package/dist/dashboard/assets/SetupPage-FadUOl9S.js.gz +0 -0
  330. package/dist/dashboard/assets/SortableList-Cfy6_Khv.js.gz +0 -0
  331. package/dist/dashboard/assets/UsagePage-BT4MyQeT.js.gz +0 -0
  332. package/dist/dashboard/assets/api-keys-DQYLQxpW.js.gz +0 -0
  333. package/dist/dashboard/assets/app-CX_Z8cMs.css +0 -2
  334. package/dist/dashboard/assets/app-CX_Z8cMs.css.gz +0 -0
  335. package/dist/dashboard/assets/assert-C9FD_JwJ.js.gz +0 -0
  336. package/dist/dashboard/assets/badge-Bvyr7D0u.js.gz +0 -0
  337. package/dist/dashboard/assets/banner-DIvKAZih.js.gz +0 -0
  338. package/dist/dashboard/assets/card-D5_gYIS_.js.gz +0 -0
  339. package/dist/dashboard/assets/cli-tools-CNqzrfdb.js.gz +0 -0
  340. package/dist/dashboard/assets/clipboard-button-B6vDBtbm.js.gz +0 -0
  341. package/dist/dashboard/assets/dialog-CpjrgdwJ.js.gz +0 -0
  342. package/dist/dashboard/assets/drawer-BWmt3b5y.js.gz +0 -0
  343. package/dist/dashboard/assets/entry-B-gvimrg.js +0 -1
  344. package/dist/dashboard/assets/entry-B-gvimrg.js.gz +0 -0
  345. package/dist/dashboard/assets/entry-C_oakV3z.js.gz +0 -0
  346. package/dist/dashboard/assets/entry-Dj0cHfEc.js +0 -4
  347. package/dist/dashboard/assets/entry-Dj0cHfEc.js.gz +0 -0
  348. package/dist/dashboard/assets/github-badge-RRHHWGxh.js.gz +0 -0
  349. package/dist/dashboard/assets/index-Bhey_rg_.js.gz +0 -0
  350. package/dist/dashboard/assets/inline-C2_EQ5Uf.js.gz +0 -0
  351. package/dist/dashboard/assets/input-BMRlQ450.js.gz +0 -0
  352. package/dist/dashboard/assets/layout-XR5xP8ew.js.gz +0 -0
  353. package/dist/dashboard/assets/list-row-Cw07dioc.js.gz +0 -0
  354. package/dist/dashboard/assets/live-BivGfuUs.js.gz +0 -0
  355. package/dist/dashboard/assets/model-bans-CmffjLuG.js.gz +0 -0
  356. package/dist/dashboard/assets/mutation-46P0oWMe.js.gz +0 -0
  357. package/dist/dashboard/assets/numbers-DrdA9oSM.js.gz +0 -0
  358. package/dist/dashboard/assets/query-keys-BFhGpwR1.js.gz +0 -0
  359. package/dist/dashboard/assets/routing-CAb4F37R.js.gz +0 -0
  360. package/dist/dashboard/assets/select-CxVVB__N.js.gz +0 -0
  361. package/dist/dashboard/assets/settings-DNX9sydi.js.gz +0 -0
  362. package/dist/dashboard/assets/stack-uPOwM9Hs.js.gz +0 -0
  363. package/dist/dashboard/assets/state-D1Yz79iL.js.gz +0 -0
  364. package/dist/dashboard/assets/switch-sevsRrRo.js.gz +0 -0
  365. package/dist/dashboard/assets/useMutation-Dem0lXQj.js.gz +0 -0
  366. package/src/observability/event-compaction.ts +0 -317
  367. package/src/observability/event-day-index.ts +0 -439
@@ -1,305 +1,305 @@
1
- /**
2
- * Vendored from `cursor-opencode-provider` (protocol layer only).
3
- *
4
- * MIT License
5
- *
6
- * Copyright (c) 2026 Oleksiy Akimov
7
- *
8
- * Permission is hereby granted, free of charge, to any person obtaining a copy
9
- * of this software and associated documentation files (the "Software"), to deal
10
- * in the Software without restriction, including without limitation the rights
11
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
- * copies of the Software, and to permit persons to whom the Software is
13
- * furnished to do so, subject to the following conditions:
14
- *
15
- * The above copyright notice and this permission notice shall be included in all
16
- * copies or substantial portions of the Software.
17
- *
18
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
- * SOFTWARE.
25
- */
26
-
27
- // Minimal encoder for google.protobuf.Value / Struct / ListValue.
28
- //
29
- // Cursor's per-turn tool descriptors carry the tool `input_schema` (a JSON
30
- // Schema object) as a `google.protobuf.Value` whose `struct_value` holds the
31
- // schema — NOT a JSON string. Verified against a live capture
32
- // (request_context #7 → McpToolDefinition #3):
33
- // 2a 9a07 0a10 0a04 74797065 1208 1a06 6f626a656374 ...
34
- // = Value{#5 struct_value: Struct{#1 fields: {"type": Value{#3 string "object"}}}}
35
- //
36
- // Field numbers (well-known types):
37
- // Value: 1 null_value, 2 number_value(double), 3 string_value,
38
- // 4 bool_value, 5 struct_value(Struct), 6 list_value(ListValue)
39
- // Struct: 1 fields (map<string, Value>) — each entry msg{1 key, 2 value}
40
- // ListValue: 1 values (repeated Value)
41
-
42
- function writeVarint(out: number[], n: number): void {
43
- let v = n >>> 0
44
- while (v > 0x7f) {
45
- out.push((v & 0x7f) | 0x80)
46
- v >>>= 7
47
- }
48
- out.push(v)
49
- }
50
-
51
- function writeTag(out: number[], field: number, wire: number): void {
52
- writeVarint(out, (field << 3) | wire)
53
- }
54
-
55
- function writeLengthDelimited(out: number[], field: number, bytes: Uint8Array): void {
56
- writeTag(out, field, 2)
57
- writeVarint(out, bytes.length)
58
- for (let i = 0; i < bytes.length; i++) out.push(bytes[i] ?? 0)
59
- }
60
-
61
- function writeString(out: number[], field: number, str: string): void {
62
- writeLengthDelimited(out, field, new TextEncoder().encode(str))
63
- }
64
-
65
- function writeDouble(out: number[], field: number, num: number): void {
66
- writeTag(out, field, 1) // 64-bit
67
- const buf = new ArrayBuffer(8)
68
- new DataView(buf).setFloat64(0, num, true) // little-endian
69
- const view = new Uint8Array(buf)
70
- for (let i = 0; i < 8; i++) out.push(view[i] ?? 0)
71
- }
72
-
73
- // ── Decoding (reverse): google.protobuf.Value bytes → JSON ──
74
-
75
- export type RawField = { fn: number; wt: number; varint: number; bytes?: Uint8Array; i64?: Uint8Array }
76
-
77
- function readVarintAt(b: Uint8Array, i: number): [number, number] {
78
- let r = 0, s = 0
79
- while (i < b.length) {
80
- const x = b[i++] ?? 0
81
- r |= (x & 0x7f) << s
82
- if (!(x & 0x80)) break
83
- s += 7
84
- }
85
- return [r >>> 0, i]
86
- }
87
-
88
- /** Walk a protobuf message's top-level fields off the raw wire bytes. */
89
- export function readAllFields(b: Uint8Array): RawField[] {
90
- let i = 0
91
- const out: RawField[] = []
92
- while (i < b.length) {
93
- let key: number
94
- ;[key, i] = readVarintAt(b, i)
95
- const fn = key >>> 3, wt = key & 7
96
- if (wt === 0) {
97
- let v: number
98
- ;[v, i] = readVarintAt(b, i)
99
- out.push({ fn, wt, varint: v })
100
- } else if (wt === 2) {
101
- let len: number
102
- ;[len, i] = readVarintAt(b, i)
103
- out.push({ fn, wt, varint: 0, bytes: b.subarray(i, i + len) })
104
- i += len
105
- } else if (wt === 1) {
106
- out.push({ fn, wt, varint: 0, i64: b.subarray(i, i + 8) })
107
- i += 8
108
- } else if (wt === 5) {
109
- i += 4 // i32 — not used by Value
110
- } else break
111
- }
112
- return out
113
- }
114
-
115
- export type StrictRawField = {
116
- fn: number
117
- wt: number
118
- varint?: bigint
119
- bytes?: Uint8Array
120
- fixed64?: Uint8Array
121
- fixed32?: Uint8Array
122
- }
123
-
124
- type DecodedVarint = { value: bigint; nextOffset: number }
125
-
126
- const MAX_UINT32 = 0xffff_ffffn
127
- const MAX_UINT64 = 0xffff_ffff_ffff_ffffn
128
- const MAX_PROTOBUF_FIELD_NUMBER = 0x1fff_ffff
129
-
130
- /**
131
- * Decode one unsigned protobuf varint without JavaScript's 32-bit bitwise
132
- * truncation. `maximum` also enforces the protocol width: tags and lengths are
133
- * uint32, while wire-type 0 scalar values may use the full uint64 range.
134
- */
135
- function readBoundedVarint(
136
- bytes: Uint8Array,
137
- offset: number,
138
- maximum: bigint,
139
- ): DecodedVarint | undefined {
140
- let value = 0n
141
- let shift = 0n
142
- for (let index = offset; index < bytes.length && index < offset + 10; index++) {
143
- const byte = bytes[index]!
144
- value |= BigInt(byte & 0x7f) << shift
145
- if (value > maximum) return undefined
146
- if ((byte & 0x80) === 0) return { value, nextOffset: index + 1 }
147
- shift += 7n
148
- }
149
- return undefined
150
- }
151
-
152
- /**
153
- * Strictly walk a protobuf message for security/replay decisions.
154
- *
155
- * Unlike `readAllFields`, this parser consumes the complete input, preserves
156
- * uint64 varints as bigint, represents fixed-width fields, and rejects invalid
157
- * tags, truncation, overflow, groups, and unsupported wire types. It deliberately
158
- * returns `undefined` instead of a partial result: callers must fail closed when
159
- * deciding whether replay is safe.
160
- */
161
- export function readAllFieldsStrict(bytes: Uint8Array): StrictRawField[] | undefined {
162
- let offset = 0
163
- const fields: StrictRawField[] = []
164
-
165
- while (offset < bytes.length) {
166
- const tag = readBoundedVarint(bytes, offset, MAX_UINT32)
167
- if (!tag) return undefined
168
- offset = tag.nextOffset
169
-
170
- const fieldNumber = Number(tag.value >> 3n)
171
- const wireType = Number(tag.value & 7n)
172
- if (fieldNumber === 0 || fieldNumber > MAX_PROTOBUF_FIELD_NUMBER) return undefined
173
-
174
- if (wireType === 0) {
175
- const scalar = readBoundedVarint(bytes, offset, MAX_UINT64)
176
- if (!scalar) return undefined
177
- fields.push({ fn: fieldNumber, wt: wireType, varint: scalar.value })
178
- offset = scalar.nextOffset
179
- continue
180
- }
181
-
182
- if (wireType === 1) {
183
- if (offset + 8 > bytes.length) return undefined
184
- fields.push({ fn: fieldNumber, wt: wireType, fixed64: bytes.subarray(offset, offset + 8) })
185
- offset += 8
186
- continue
187
- }
188
-
189
- if (wireType === 2) {
190
- const encodedLength = readBoundedVarint(bytes, offset, MAX_UINT32)
191
- if (!encodedLength) return undefined
192
- offset = encodedLength.nextOffset
193
- const length = Number(encodedLength.value)
194
- if (offset + length > bytes.length) return undefined
195
- fields.push({ fn: fieldNumber, wt: wireType, bytes: bytes.subarray(offset, offset + length) })
196
- offset += length
197
- continue
198
- }
199
-
200
- if (wireType === 5) {
201
- if (offset + 4 > bytes.length) return undefined
202
- fields.push({ fn: fieldNumber, wt: wireType, fixed32: bytes.subarray(offset, offset + 4) })
203
- offset += 4
204
- continue
205
- }
206
-
207
- // Groups (3/4) are deprecated and recursive; no replay-safe frame uses them.
208
- return undefined
209
- }
210
-
211
- return fields
212
- }
213
-
214
- function decodeStructBytes(b: Uint8Array): Record<string, unknown> {
215
- const obj: Record<string, unknown> = {}
216
- for (const f of readAllFields(b)) {
217
- if (f.fn !== 1 || !f.bytes) continue // Struct.fields (map) entries
218
- const { key, valBytes } = readMapEntry(f.bytes)
219
- if (key !== undefined) obj[key] = valBytes ? decodeValueToJson(valBytes) : null
220
- }
221
- return obj
222
- }
223
-
224
- function decodeListBytes(b: Uint8Array): unknown[] {
225
- const arr: unknown[] = []
226
- for (const f of readAllFields(b)) {
227
- if (f.fn === 1 && f.bytes) arr.push(decodeValueToJson(f.bytes))
228
- }
229
- return arr
230
- }
231
-
232
- function readMapEntry(b: Uint8Array): { key?: string; valBytes?: Uint8Array } {
233
- let key: string | undefined
234
- let valBytes: Uint8Array | undefined
235
- for (const e of readAllFields(b)) {
236
- if (e.fn === 1 && e.bytes) key = new TextDecoder().decode(e.bytes)
237
- else if (e.fn === 2 && e.bytes) valBytes = e.bytes
238
- }
239
- return { ...(key === undefined ? {} : { key }), ...(valBytes === undefined ? {} : { valBytes }) }
240
- }
241
-
242
- /** Decode a google.protobuf.Value message (bytes) back into a JSON value. */
243
- export function decodeValueToJson(bytes: Uint8Array): unknown {
244
- const fs = readAllFields(bytes)
245
- const f = fs[0]
246
- if (f === undefined) return null
247
- switch (f.fn) {
248
- case 1: return null // null_value
249
- case 2: return f.i64 ? new DataView(f.i64.buffer, f.i64.byteOffset, 8).getFloat64(0, true) : 0
250
- case 3: return f.bytes ? new TextDecoder().decode(f.bytes) : ""
251
- case 4: return f.varint !== 0
252
- case 5: return f.bytes ? decodeStructBytes(f.bytes) : {}
253
- case 6: return f.bytes ? decodeListBytes(f.bytes) : []
254
- default: return null
255
- }
256
- }
257
-
258
- /**
259
- * Decode a `map<string, Value>` field that was captured as repeated map-entry
260
- * messages (each `{1 key, 2 value}`) into a plain JSON object. Used for
261
- * `McpArgs.args`, which arrives as repeated field #2 on the wire.
262
- */
263
- export function decodeStructEntriesToJson(entries: Uint8Array[]): Record<string, unknown> {
264
- const obj: Record<string, unknown> = {}
265
- for (const entry of entries) {
266
- const { key, valBytes } = readMapEntry(entry)
267
- if (key !== undefined) obj[key] = valBytes ? decodeValueToJson(valBytes) : null
268
- }
269
- return obj
270
- }
271
-
272
- /** Encode an arbitrary JSON value as google.protobuf.Value bytes. */
273
- export function encodeJsonAsValue(v: unknown): Uint8Array {
274
- const out: number[] = []
275
- if (v === null || v === undefined) {
276
- writeTag(out, 1, 0) // null_value = 0
277
- writeVarint(out, 0)
278
- } else if (typeof v === "number") {
279
- writeDouble(out, 2, v)
280
- } else if (typeof v === "boolean") {
281
- writeTag(out, 4, 0)
282
- writeVarint(out, v ? 1 : 0)
283
- } else if (typeof v === "string") {
284
- writeString(out, 3, v)
285
- } else if (Array.isArray(v)) {
286
- const lv: number[] = []
287
- for (const item of v) writeLengthDelimited(lv, 1, encodeJsonAsValue(item))
288
- writeLengthDelimited(out, 6, new Uint8Array(lv))
289
- } else if (typeof v === "object") {
290
- const st: number[] = []
291
- for (const [key, val] of Object.entries(v as Record<string, unknown>).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)) {
292
- if (val === undefined) continue
293
- const entry: number[] = []
294
- writeString(entry, 1, key)
295
- writeLengthDelimited(entry, 2, encodeJsonAsValue(val))
296
- writeLengthDelimited(st, 1, new Uint8Array(entry))
297
- }
298
- writeLengthDelimited(out, 5, new Uint8Array(st))
299
- } else {
300
- // Fallback: treat as null.
301
- writeTag(out, 1, 0)
302
- writeVarint(out, 0)
303
- }
304
- return new Uint8Array(out)
305
- }
1
+ /**
2
+ * Vendored from `cursor-opencode-provider` (protocol layer only).
3
+ *
4
+ * MIT License
5
+ *
6
+ * Copyright (c) 2026 Oleksiy Akimov
7
+ *
8
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ * of this software and associated documentation files (the "Software"), to deal
10
+ * in the Software without restriction, including without limitation the rights
11
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ * copies of the Software, and to permit persons to whom the Software is
13
+ * furnished to do so, subject to the following conditions:
14
+ *
15
+ * The above copyright notice and this permission notice shall be included in all
16
+ * copies or substantial portions of the Software.
17
+ *
18
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ * SOFTWARE.
25
+ */
26
+
27
+ // Minimal encoder for google.protobuf.Value / Struct / ListValue.
28
+ //
29
+ // Cursor's per-turn tool descriptors carry the tool `input_schema` (a JSON
30
+ // Schema object) as a `google.protobuf.Value` whose `struct_value` holds the
31
+ // schema — NOT a JSON string. Verified against a live capture
32
+ // (request_context #7 → McpToolDefinition #3):
33
+ // 2a 9a07 0a10 0a04 74797065 1208 1a06 6f626a656374 ...
34
+ // = Value{#5 struct_value: Struct{#1 fields: {"type": Value{#3 string "object"}}}}
35
+ //
36
+ // Field numbers (well-known types):
37
+ // Value: 1 null_value, 2 number_value(double), 3 string_value,
38
+ // 4 bool_value, 5 struct_value(Struct), 6 list_value(ListValue)
39
+ // Struct: 1 fields (map<string, Value>) — each entry msg{1 key, 2 value}
40
+ // ListValue: 1 values (repeated Value)
41
+
42
+ function writeVarint(out: number[], n: number): void {
43
+ let v = n >>> 0
44
+ while (v > 0x7f) {
45
+ out.push((v & 0x7f) | 0x80)
46
+ v >>>= 7
47
+ }
48
+ out.push(v)
49
+ }
50
+
51
+ function writeTag(out: number[], field: number, wire: number): void {
52
+ writeVarint(out, (field << 3) | wire)
53
+ }
54
+
55
+ function writeLengthDelimited(out: number[], field: number, bytes: Uint8Array): void {
56
+ writeTag(out, field, 2)
57
+ writeVarint(out, bytes.length)
58
+ for (let i = 0; i < bytes.length; i++) out.push(bytes[i] ?? 0)
59
+ }
60
+
61
+ function writeString(out: number[], field: number, str: string): void {
62
+ writeLengthDelimited(out, field, new TextEncoder().encode(str))
63
+ }
64
+
65
+ function writeDouble(out: number[], field: number, num: number): void {
66
+ writeTag(out, field, 1) // 64-bit
67
+ const buf = new ArrayBuffer(8)
68
+ new DataView(buf).setFloat64(0, num, true) // little-endian
69
+ const view = new Uint8Array(buf)
70
+ for (let i = 0; i < 8; i++) out.push(view[i] ?? 0)
71
+ }
72
+
73
+ // ── Decoding (reverse): google.protobuf.Value bytes → JSON ──
74
+
75
+ export type RawField = { fn: number; wt: number; varint: number; bytes?: Uint8Array; i64?: Uint8Array }
76
+
77
+ function readVarintAt(b: Uint8Array, i: number): [number, number] {
78
+ let r = 0, s = 0
79
+ while (i < b.length) {
80
+ const x = b[i++] ?? 0
81
+ r |= (x & 0x7f) << s
82
+ if (!(x & 0x80)) break
83
+ s += 7
84
+ }
85
+ return [r >>> 0, i]
86
+ }
87
+
88
+ /** Walk a protobuf message's top-level fields off the raw wire bytes. */
89
+ export function readAllFields(b: Uint8Array): RawField[] {
90
+ let i = 0
91
+ const out: RawField[] = []
92
+ while (i < b.length) {
93
+ let key: number
94
+ ;[key, i] = readVarintAt(b, i)
95
+ const fn = key >>> 3, wt = key & 7
96
+ if (wt === 0) {
97
+ let v: number
98
+ ;[v, i] = readVarintAt(b, i)
99
+ out.push({ fn, wt, varint: v })
100
+ } else if (wt === 2) {
101
+ let len: number
102
+ ;[len, i] = readVarintAt(b, i)
103
+ out.push({ fn, wt, varint: 0, bytes: b.subarray(i, i + len) })
104
+ i += len
105
+ } else if (wt === 1) {
106
+ out.push({ fn, wt, varint: 0, i64: b.subarray(i, i + 8) })
107
+ i += 8
108
+ } else if (wt === 5) {
109
+ i += 4 // i32 — not used by Value
110
+ } else break
111
+ }
112
+ return out
113
+ }
114
+
115
+ export type StrictRawField = {
116
+ fn: number
117
+ wt: number
118
+ varint?: bigint
119
+ bytes?: Uint8Array
120
+ fixed64?: Uint8Array
121
+ fixed32?: Uint8Array
122
+ }
123
+
124
+ type DecodedVarint = { value: bigint; nextOffset: number }
125
+
126
+ const MAX_UINT32 = 0xffff_ffffn
127
+ const MAX_UINT64 = 0xffff_ffff_ffff_ffffn
128
+ const MAX_PROTOBUF_FIELD_NUMBER = 0x1fff_ffff
129
+
130
+ /**
131
+ * Decode one unsigned protobuf varint without JavaScript's 32-bit bitwise
132
+ * truncation. `maximum` also enforces the protocol width: tags and lengths are
133
+ * uint32, while wire-type 0 scalar values may use the full uint64 range.
134
+ */
135
+ function readBoundedVarint(
136
+ bytes: Uint8Array,
137
+ offset: number,
138
+ maximum: bigint,
139
+ ): DecodedVarint | undefined {
140
+ let value = 0n
141
+ let shift = 0n
142
+ for (let index = offset; index < bytes.length && index < offset + 10; index++) {
143
+ const byte = bytes[index]!
144
+ value |= BigInt(byte & 0x7f) << shift
145
+ if (value > maximum) return undefined
146
+ if ((byte & 0x80) === 0) return { value, nextOffset: index + 1 }
147
+ shift += 7n
148
+ }
149
+ return undefined
150
+ }
151
+
152
+ /**
153
+ * Strictly walk a protobuf message for security/replay decisions.
154
+ *
155
+ * Unlike `readAllFields`, this parser consumes the complete input, preserves
156
+ * uint64 varints as bigint, represents fixed-width fields, and rejects invalid
157
+ * tags, truncation, overflow, groups, and unsupported wire types. It deliberately
158
+ * returns `undefined` instead of a partial result: callers must fail closed when
159
+ * deciding whether replay is safe.
160
+ */
161
+ export function readAllFieldsStrict(bytes: Uint8Array): StrictRawField[] | undefined {
162
+ let offset = 0
163
+ const fields: StrictRawField[] = []
164
+
165
+ while (offset < bytes.length) {
166
+ const tag = readBoundedVarint(bytes, offset, MAX_UINT32)
167
+ if (!tag) return undefined
168
+ offset = tag.nextOffset
169
+
170
+ const fieldNumber = Number(tag.value >> 3n)
171
+ const wireType = Number(tag.value & 7n)
172
+ if (fieldNumber === 0 || fieldNumber > MAX_PROTOBUF_FIELD_NUMBER) return undefined
173
+
174
+ if (wireType === 0) {
175
+ const scalar = readBoundedVarint(bytes, offset, MAX_UINT64)
176
+ if (!scalar) return undefined
177
+ fields.push({ fn: fieldNumber, wt: wireType, varint: scalar.value })
178
+ offset = scalar.nextOffset
179
+ continue
180
+ }
181
+
182
+ if (wireType === 1) {
183
+ if (offset + 8 > bytes.length) return undefined
184
+ fields.push({ fn: fieldNumber, wt: wireType, fixed64: bytes.subarray(offset, offset + 8) })
185
+ offset += 8
186
+ continue
187
+ }
188
+
189
+ if (wireType === 2) {
190
+ const encodedLength = readBoundedVarint(bytes, offset, MAX_UINT32)
191
+ if (!encodedLength) return undefined
192
+ offset = encodedLength.nextOffset
193
+ const length = Number(encodedLength.value)
194
+ if (offset + length > bytes.length) return undefined
195
+ fields.push({ fn: fieldNumber, wt: wireType, bytes: bytes.subarray(offset, offset + length) })
196
+ offset += length
197
+ continue
198
+ }
199
+
200
+ if (wireType === 5) {
201
+ if (offset + 4 > bytes.length) return undefined
202
+ fields.push({ fn: fieldNumber, wt: wireType, fixed32: bytes.subarray(offset, offset + 4) })
203
+ offset += 4
204
+ continue
205
+ }
206
+
207
+ // Groups (3/4) are deprecated and recursive; no replay-safe frame uses them.
208
+ return undefined
209
+ }
210
+
211
+ return fields
212
+ }
213
+
214
+ function decodeStructBytes(b: Uint8Array): Record<string, unknown> {
215
+ const obj: Record<string, unknown> = {}
216
+ for (const f of readAllFields(b)) {
217
+ if (f.fn !== 1 || !f.bytes) continue // Struct.fields (map) entries
218
+ const { key, valBytes } = readMapEntry(f.bytes)
219
+ if (key !== undefined) obj[key] = valBytes ? decodeValueToJson(valBytes) : null
220
+ }
221
+ return obj
222
+ }
223
+
224
+ function decodeListBytes(b: Uint8Array): unknown[] {
225
+ const arr: unknown[] = []
226
+ for (const f of readAllFields(b)) {
227
+ if (f.fn === 1 && f.bytes) arr.push(decodeValueToJson(f.bytes))
228
+ }
229
+ return arr
230
+ }
231
+
232
+ function readMapEntry(b: Uint8Array): { key?: string; valBytes?: Uint8Array } {
233
+ let key: string | undefined
234
+ let valBytes: Uint8Array | undefined
235
+ for (const e of readAllFields(b)) {
236
+ if (e.fn === 1 && e.bytes) key = new TextDecoder().decode(e.bytes)
237
+ else if (e.fn === 2 && e.bytes) valBytes = e.bytes
238
+ }
239
+ return { ...(key === undefined ? {} : { key }), ...(valBytes === undefined ? {} : { valBytes }) }
240
+ }
241
+
242
+ /** Decode a google.protobuf.Value message (bytes) back into a JSON value. */
243
+ export function decodeValueToJson(bytes: Uint8Array): unknown {
244
+ const fs = readAllFields(bytes)
245
+ const f = fs[0]
246
+ if (f === undefined) return null
247
+ switch (f.fn) {
248
+ case 1: return null // null_value
249
+ case 2: return f.i64 ? new DataView(f.i64.buffer, f.i64.byteOffset, 8).getFloat64(0, true) : 0
250
+ case 3: return f.bytes ? new TextDecoder().decode(f.bytes) : ""
251
+ case 4: return f.varint !== 0
252
+ case 5: return f.bytes ? decodeStructBytes(f.bytes) : {}
253
+ case 6: return f.bytes ? decodeListBytes(f.bytes) : []
254
+ default: return null
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Decode a `map<string, Value>` field that was captured as repeated map-entry
260
+ * messages (each `{1 key, 2 value}`) into a plain JSON object. Used for
261
+ * `McpArgs.args`, which arrives as repeated field #2 on the wire.
262
+ */
263
+ export function decodeStructEntriesToJson(entries: Uint8Array[]): Record<string, unknown> {
264
+ const obj: Record<string, unknown> = {}
265
+ for (const entry of entries) {
266
+ const { key, valBytes } = readMapEntry(entry)
267
+ if (key !== undefined) obj[key] = valBytes ? decodeValueToJson(valBytes) : null
268
+ }
269
+ return obj
270
+ }
271
+
272
+ /** Encode an arbitrary JSON value as google.protobuf.Value bytes. */
273
+ export function encodeJsonAsValue(v: unknown): Uint8Array {
274
+ const out: number[] = []
275
+ if (v === null || v === undefined) {
276
+ writeTag(out, 1, 0) // null_value = 0
277
+ writeVarint(out, 0)
278
+ } else if (typeof v === "number") {
279
+ writeDouble(out, 2, v)
280
+ } else if (typeof v === "boolean") {
281
+ writeTag(out, 4, 0)
282
+ writeVarint(out, v ? 1 : 0)
283
+ } else if (typeof v === "string") {
284
+ writeString(out, 3, v)
285
+ } else if (Array.isArray(v)) {
286
+ const lv: number[] = []
287
+ for (const item of v) writeLengthDelimited(lv, 1, encodeJsonAsValue(item))
288
+ writeLengthDelimited(out, 6, new Uint8Array(lv))
289
+ } else if (typeof v === "object") {
290
+ const st: number[] = []
291
+ for (const [key, val] of Object.entries(v as Record<string, unknown>).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)) {
292
+ if (val === undefined) continue
293
+ const entry: number[] = []
294
+ writeString(entry, 1, key)
295
+ writeLengthDelimited(entry, 2, encodeJsonAsValue(val))
296
+ writeLengthDelimited(st, 1, new Uint8Array(entry))
297
+ }
298
+ writeLengthDelimited(out, 5, new Uint8Array(st))
299
+ } else {
300
+ // Fallback: treat as null.
301
+ writeTag(out, 1, 0)
302
+ writeVarint(out, 0)
303
+ }
304
+ return new Uint8Array(out)
305
+ }