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,2380 +1,2380 @@
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
- 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
- }
50
-
51
- // ── Utility messages ──
52
-
53
- export function createMessageTypes(): protobuf.Root {
54
- const root = new protobuf.Root()
55
-
56
- addType(root, "TextDeltaUpdate", [
57
- { id: 1, name: "text", type: "string" },
58
- ])
59
-
60
- addType(root, "ThinkingDeltaUpdate", [
61
- { id: 1, name: "text", type: "string" },
62
- ])
63
-
64
- addType(root, "TurnEnded", [
65
- { id: 1, name: "input_tokens", type: "uint32" },
66
- { id: 2, name: "output_tokens", type: "uint32" },
67
- { id: 3, name: "cache_read", type: "uint32" },
68
- { id: 4, name: "cache_write", type: "uint32" },
69
- { id: 5, name: "reasoning_tokens", type: "uint32" },
70
- ])
71
-
72
- addType(root, "Heartbeat", [])
73
-
74
- // Display ToolCall (interaction_update.tool_call_*) — agent.v1 oneof, not
75
- // {tool_name,args} strings. Decode finalized todo state for mirroring and
76
- // MCP errors for diagnostics; other completion results are not replayed.
77
- root.add(new protobuf.Enum("TodoStatus", {
78
- TODO_STATUS_UNSPECIFIED: 0,
79
- TODO_STATUS_PENDING: 1,
80
- TODO_STATUS_IN_PROGRESS: 2,
81
- TODO_STATUS_COMPLETED: 3,
82
- TODO_STATUS_CANCELLED: 4,
83
- }))
84
- addType(root, "TodoItem", [
85
- { id: 1, name: "id", type: "string" },
86
- { id: 2, name: "content", type: "string" },
87
- { id: 3, name: "status", type: "TodoStatus" },
88
- { id: 4, name: "created_at", type: "int64" },
89
- { id: 5, name: "updated_at", type: "int64" },
90
- { id: 6, name: "dependencies", type: "string", repeated: true },
91
- ])
92
- addType(root, "UpdateTodosArgs", [
93
- { id: 1, name: "todos", type: "TodoItem", repeated: true },
94
- { id: 2, name: "merge", type: "bool" },
95
- ])
96
- addType(root, "UpdateTodosSuccess", [
97
- { id: 1, name: "todos", type: "TodoItem", repeated: true },
98
- { id: 2, name: "total_count", type: "int32" },
99
- { id: 3, name: "was_merge", type: "bool" },
100
- ])
101
- addType(root, "UpdateTodosError", [{ id: 1, name: "error", type: "string" }])
102
- addType(
103
- root,
104
- "UpdateTodosResult",
105
- [
106
- { id: 1, name: "success", type: "UpdateTodosSuccess" },
107
- { id: 2, name: "error", type: "UpdateTodosError" },
108
- ],
109
- [{ name: "result", fields: ["success", "error"] }],
110
- )
111
- addType(root, "UpdateTodosToolCall", [
112
- { id: 1, name: "args", type: "UpdateTodosArgs" },
113
- { id: 2, name: "result", type: "UpdateTodosResult" },
114
- ])
115
- addType(root, "ReadToolArgs", [
116
- { id: 1, name: "path", type: "string" },
117
- { id: 2, name: "offset", type: "int32" },
118
- { id: 3, name: "limit", type: "int32" },
119
- ])
120
- addType(root, "ReadToolCall", [{ id: 1, name: "args", type: "ReadToolArgs" }])
121
- addType(root, "ShellToolCall", [{ id: 1, name: "args", type: "ShellArgs" }])
122
- addType(root, "DeleteToolCall", [{ id: 1, name: "args", type: "DeleteArgs" }])
123
- addType(root, "GlobToolCall", [{ id: 1, name: "args", type: "GlobArgs" }])
124
- addType(root, "GrepToolCall", [{ id: 1, name: "args", type: "GrepArgs" }])
125
- addType(root, "EditToolArgs", [
126
- { id: 1, name: "path", type: "string" },
127
- { id: 6, name: "stream_content", type: "string" },
128
- ])
129
- addType(root, "EditToolCall", [{ id: 1, name: "args", type: "EditToolArgs" }])
130
- addType(root, "LsToolCall", [{ id: 1, name: "args", type: "LsArgs" }])
131
- addType(root, "McpToolCall", [
132
- { id: 1, name: "args", type: "McpArgs" },
133
- { id: 2, name: "result", type: "McpResult" },
134
- ])
135
- addType(root, "CreatePlanArgs", [
136
- { id: 1, name: "plan", type: "string" },
137
- { id: 2, name: "todos", type: "TodoItem", repeated: true },
138
- { id: 3, name: "overview", type: "string" },
139
- { id: 4, name: "name", type: "string" },
140
- { id: 5, name: "is_project", type: "bool" },
141
- ])
142
- addType(root, "CreatePlanToolCall", [{ id: 1, name: "args", type: "CreatePlanArgs" }])
143
- addType(root, "WebSearchToolArgs", [
144
- { id: 1, name: "search_term", type: "string" },
145
- { id: 2, name: "tool_call_id", type: "string" },
146
- ])
147
- addType(root, "WebSearchToolCall", [{ id: 1, name: "args", type: "WebSearchToolArgs" }])
148
- addType(root, "SubagentTypeUnspecified", [])
149
- addType(root, "SubagentTypeComputerUse", [])
150
- addType(root, "SubagentTypeCustom", [{ id: 1, name: "name", type: "string" }])
151
- addType(root, "SubagentTypeExplore", [])
152
- addType(root, "SubagentTypeMediaReview", [])
153
- addType(root, "SubagentTypeBash", [])
154
- addType(root, "SubagentTypeBrowserUse", [])
155
- addType(root, "SubagentTypeShell", [])
156
- addType(root, "SubagentTypeVmSetupHelper", [])
157
- addType(root, "SubagentTypeDebug", [])
158
- addType(root, "SubagentTypeCursorGuide", [])
159
- addType(root, "SubagentTypeWatchVideo", [])
160
- addType(
161
- root,
162
- "SubagentType",
163
- [
164
- { id: 1, name: "unspecified", type: "SubagentTypeUnspecified" },
165
- { id: 2, name: "computer_use", type: "SubagentTypeComputerUse" },
166
- { id: 3, name: "custom", type: "SubagentTypeCustom" },
167
- { id: 4, name: "explore", type: "SubagentTypeExplore" },
168
- { id: 5, name: "media_review", type: "SubagentTypeMediaReview" },
169
- { id: 6, name: "bash", type: "SubagentTypeBash" },
170
- { id: 7, name: "browser_use", type: "SubagentTypeBrowserUse" },
171
- { id: 8, name: "shell", type: "SubagentTypeShell" },
172
- { id: 9, name: "vm_setup_helper", type: "SubagentTypeVmSetupHelper" },
173
- { id: 10, name: "debug", type: "SubagentTypeDebug" },
174
- { id: 11, name: "cursor_guide", type: "SubagentTypeCursorGuide" },
175
- { id: 12, name: "watch_video", type: "SubagentTypeWatchVideo" },
176
- ],
177
- [{
178
- name: "type",
179
- fields: [
180
- "unspecified", "computer_use", "custom", "explore", "media_review", "bash",
181
- "browser_use", "shell", "vm_setup_helper", "debug", "cursor_guide", "watch_video",
182
- ],
183
- }],
184
- )
185
- addType(root, "TaskToolArgs", [
186
- { id: 1, name: "description", type: "string" },
187
- { id: 2, name: "prompt", type: "string" },
188
- { id: 3, name: "subagent_type", type: "SubagentType" },
189
- { id: 4, name: "model", type: "string" },
190
- { id: 5, name: "resume", type: "string" },
191
- { id: 6, name: "agent_id", type: "string" },
192
- { id: 7, name: "attachments", type: "string", repeated: true },
193
- ])
194
- addType(root, "TaskToolCall", [{ id: 1, name: "args", type: "TaskToolArgs" }])
195
-
196
- // Native subagent execution is separate from the display-only TaskToolCall.
197
- // Cursor asks the local client to run the task through ExecServerMessage #28
198
- // and waits for the correlated SubagentResult at ExecClientMessage #28.
199
- addType(root, "SubagentArgs", [
200
- { id: 1, name: "tool_call_id", type: "string" },
201
- { id: 2, name: "subagent_type", type: "string" },
202
- { id: 3, name: "model_id", type: "string" },
203
- { id: 4, name: "prompt", type: "string" },
204
- { id: 5, name: "readonly", type: "bool" },
205
- { id: 6, name: "resume_agent_id", type: "string" },
206
- { id: 7, name: "run_in_background", type: "bool" },
207
- ])
208
- addType(root, "SubagentSuccess", [
209
- { id: 1, name: "agent_id", type: "string" },
210
- { id: 2, name: "final_message", type: "string" },
211
- { id: 3, name: "tool_call_count", type: "int32" },
212
- { id: 4, name: "background_reason", type: "int32" },
213
- { id: 5, name: "transcript_path", type: "string" },
214
- ])
215
- addType(root, "SubagentError", [
216
- { id: 1, name: "agent_id", type: "string" },
217
- { id: 2, name: "error", type: "string" },
218
- ])
219
- addType(
220
- root,
221
- "SubagentResult",
222
- [
223
- { id: 1, name: "success", type: "SubagentSuccess" },
224
- { id: 2, name: "error", type: "SubagentError" },
225
- ],
226
- [{ name: "result", fields: ["success", "error"] }],
227
- )
228
- addType(root, "AskQuestionOption", [
229
- { id: 1, name: "id", type: "string" },
230
- { id: 2, name: "label", type: "string" },
231
- ])
232
- addType(root, "AskQuestionItem", [
233
- { id: 1, name: "id", type: "string" },
234
- { id: 2, name: "prompt", type: "string" },
235
- { id: 3, name: "options", type: "AskQuestionOption", repeated: true },
236
- { id: 4, name: "allow_multiple", type: "bool" },
237
- ])
238
- addType(root, "AskQuestionArgs", [
239
- { id: 1, name: "title", type: "string" },
240
- { id: 2, name: "questions", type: "AskQuestionItem", repeated: true },
241
- // #5/#6 drive Cursor's non-blocking AskQuestion: the client answers the
242
- // interaction with `async` right away and delivers the real answers later
243
- // through ConversationAction.async_ask_question_completion_action, keyed by
244
- // the originating tool call id. See protocol/ask-question.ts.
245
- { id: 5, name: "run_async", type: "bool" },
246
- { id: 6, name: "async_original_tool_call_id", type: "string" },
247
- ])
248
- addType(root, "AskQuestionToolCall", [{ id: 1, name: "args", type: "AskQuestionArgs" }])
249
- addType(root, "FetchToolArgs", [
250
- { id: 1, name: "url", type: "string" },
251
- { id: 2, name: "tool_call_id", type: "string" },
252
- ])
253
- addType(root, "FetchToolCall", [{ id: 1, name: "args", type: "FetchToolArgs" }])
254
- addType(root, "WebFetchToolCall", [{ id: 1, name: "args", type: "FetchToolArgs" }])
255
- addType(root, "SwitchModeToolArgs", [
256
- { id: 1, name: "target_mode_id", type: "string" },
257
- { id: 2, name: "explanation", type: "string" },
258
- { id: 3, name: "tool_call_id", type: "string" },
259
- ])
260
- // Display ToolCall.result (CLI success hardcodes from_mode_id=""); distinct from
261
- // InteractionResponse approved/rejected, which is the consent gate.
262
- addType(root, "SwitchModeSuccess", [
263
- { id: 1, name: "from_mode_id", type: "string" },
264
- { id: 2, name: "to_mode_id", type: "string" },
265
- ])
266
- addType(root, "SwitchModeError", [{ id: 1, name: "error", type: "string" }])
267
- addType(root, "SwitchModeRejected", [{ id: 1, name: "reason", type: "string" }])
268
- addType(root, "SwitchModeResult", [
269
- { id: 1, name: "success", type: "SwitchModeSuccess" },
270
- { id: 2, name: "error", type: "SwitchModeError" },
271
- { id: 3, name: "rejected", type: "SwitchModeRejected" },
272
- ], [{ name: "result", fields: ["success", "error", "rejected"] }])
273
- addType(root, "SwitchModeToolCall", [
274
- { id: 1, name: "args", type: "SwitchModeToolArgs" },
275
- { id: 2, name: "result", type: "SwitchModeResult" },
276
- ])
277
- addType(root, "PiReadToolArgs", [
278
- { id: 1, name: "path", type: "string" },
279
- { id: 2, name: "offset", type: "int32" },
280
- { id: 3, name: "limit", type: "int32" },
281
- ])
282
- addType(root, "PiBashToolArgs", [
283
- { id: 1, name: "command", type: "string" },
284
- { id: 2, name: "timeout", type: "double" },
285
- ])
286
- addType(root, "PiEditReplacement", [
287
- { id: 1, name: "old_text", type: "string" },
288
- { id: 2, name: "new_text", type: "string" },
289
- ])
290
- addType(root, "PiEditToolArgs", [
291
- { id: 1, name: "path", type: "string" },
292
- { id: 2, name: "edits", type: "PiEditReplacement", repeated: true },
293
- ])
294
- addType(root, "PiWriteToolArgs", [
295
- { id: 1, name: "path", type: "string" },
296
- { id: 2, name: "content", type: "string" },
297
- ])
298
- addType(root, "PiGrepToolArgs", [
299
- { id: 1, name: "pattern", type: "string" },
300
- { id: 2, name: "path", type: "string" },
301
- { id: 3, name: "glob", type: "string" },
302
- { id: 4, name: "ignore_case", type: "bool" },
303
- { id: 5, name: "literal", type: "bool" },
304
- { id: 6, name: "context", type: "int32" },
305
- { id: 7, name: "limit", type: "int32" },
306
- ])
307
- addType(root, "PiFindToolArgs", [
308
- { id: 1, name: "pattern", type: "string" },
309
- { id: 2, name: "path", type: "string" },
310
- { id: 3, name: "limit", type: "int32" },
311
- ])
312
- addType(root, "PiLsToolArgs", [
313
- { id: 1, name: "path", type: "string" },
314
- { id: 2, name: "limit", type: "int32" },
315
- ])
316
- addType(root, "PiWriteToolCall", [{ id: 1, name: "args", type: "PiWriteToolArgs" }])
317
- addType(root, "PiReadToolCall", [{ id: 1, name: "args", type: "PiReadToolArgs" }])
318
- addType(root, "PiBashToolCall", [{ id: 1, name: "args", type: "PiBashToolArgs" }])
319
- addType(root, "PiEditToolCall", [{ id: 1, name: "args", type: "PiEditToolArgs" }])
320
- addType(root, "PiGrepToolCall", [{ id: 1, name: "args", type: "PiGrepToolArgs" }])
321
- addType(root, "PiFindToolCall", [{ id: 1, name: "args", type: "PiFindToolArgs" }])
322
- addType(root, "PiLsToolCall", [{ id: 1, name: "args", type: "PiLsToolArgs" }])
323
- // Display-only native tools Cursor may complete without an ExecServerMessage.
324
- // ReadTodos mirrors UpdateTodos: args + result (see agent.v1 ReadTodosToolCall).
325
- addType(root, "ReadTodosArgs", [
326
- { id: 1, name: "status_filter", type: "TodoStatus", repeated: true },
327
- { id: 2, name: "id_filter", type: "string", repeated: true },
328
- ])
329
- addType(root, "ReadTodosSuccess", [
330
- { id: 1, name: "todos", type: "TodoItem", repeated: true },
331
- { id: 2, name: "total_count", type: "int32" },
332
- ])
333
- addType(root, "ReadTodosError", [{ id: 1, name: "error", type: "string" }])
334
- addType(
335
- root,
336
- "ReadTodosResult",
337
- [
338
- { id: 1, name: "success", type: "ReadTodosSuccess" },
339
- { id: 2, name: "error", type: "ReadTodosError" },
340
- ],
341
- [{ name: "result", fields: ["success", "error"] }],
342
- )
343
- addType(root, "ReadTodosToolCall", [
344
- { id: 1, name: "args", type: "ReadTodosArgs" },
345
- { id: 2, name: "result", type: "ReadTodosResult" },
346
- ])
347
- addType(root, "AwaitArgs", [
348
- { id: 1, name: "task_id", type: "string" },
349
- { id: 2, name: "block_until_ms", type: "uint32" },
350
- { id: 3, name: "regex", type: "string" },
351
- ])
352
- addType(root, "AwaitToolCall", [{ id: 1, name: "args", type: "AwaitArgs" }])
353
- addType(root, "GetMcpToolsArgs", [
354
- { id: 1, name: "server", type: "string" },
355
- { id: 2, name: "tool_name", type: "string" },
356
- { id: 3, name: "pattern", type: "string" },
357
- { id: 4, name: "tool_call_id", type: "string" },
358
- ])
359
- // agent.v1 GetMcpToolsToolCall is args + result. Cursor's agent fills result
360
- // locally (GetDynamicTools / GetMcpTools); large catalogs spill through
361
- // write_args and put the spill path on GetMcpToolsSuccess.output_file_path.
362
- addType(root, "GetMcpToolsSuccess", [
363
- { id: 1, name: "content", type: "string" },
364
- { id: 2, name: "output_file_path", type: "string" },
365
- ])
366
- addType(root, "GetMcpToolsError", [{ id: 1, name: "error", type: "string" }])
367
- addType(root, "GetMcpToolsAgentResult", [
368
- { id: 1, name: "success", type: "GetMcpToolsSuccess" },
369
- { id: 2, name: "error", type: "GetMcpToolsError" },
370
- ], [{ name: "result", fields: ["success", "error"] }])
371
- addType(root, "GetMcpToolsToolCall", [
372
- { id: 1, name: "args", type: "GetMcpToolsArgs" },
373
- { id: 2, name: "result", type: "GetMcpToolsAgentResult" },
374
- ])
375
-
376
- addType(root, "GenerateImageToolArgs", [
377
- { id: 1, name: "description", type: "string" },
378
- { id: 2, name: "file_path", type: "string" },
379
- { id: 5, name: "reference_image_paths", type: "string", repeated: true },
380
- ])
381
- // Cursor generates the image server-side and returns it on the display
382
- // tool_call frame. `image_data` is a base64 string, not bytes.
383
- addType(root, "GenerateImageSuccess", [
384
- { id: 1, name: "file_path", type: "string" },
385
- { id: 2, name: "image_data", type: "string" },
386
- ])
387
- addType(root, "GenerateImageError", [{ id: 1, name: "error", type: "string" }])
388
- addType(root, "GenerateImageResult", [
389
- { id: 1, name: "success", type: "GenerateImageSuccess" },
390
- { id: 2, name: "error", type: "GenerateImageError" },
391
- ], [{ name: "result", fields: ["success", "error"] }])
392
- addType(root, "GenerateImageToolCall", [
393
- { id: 1, name: "args", type: "GenerateImageToolArgs" },
394
- { id: 2, name: "result", type: "GenerateImageResult" },
395
- ])
396
- // InteractionQuery keeps query bodies opaque; decoded on demand like
397
- // AskQuestionInteractionQuery.
398
- addType(root, "GenerateImageRequestQuery", [
399
- { id: 1, name: "args", type: "GenerateImageToolArgs" },
400
- { id: 2, name: "tool_call_id", type: "string" },
401
- ])
402
-
403
- addType(
404
- root,
405
- "ToolCall",
406
- [
407
- { id: 57, name: "tool_call_id", type: "string" },
408
- { id: 1, name: "shell_tool_call", type: "ShellToolCall" },
409
- { id: 3, name: "delete_tool_call", type: "DeleteToolCall" },
410
- { id: 4, name: "glob_tool_call", type: "GlobToolCall" },
411
- { id: 5, name: "grep_tool_call", type: "GrepToolCall" },
412
- { id: 8, name: "read_tool_call", type: "ReadToolCall" },
413
- { id: 9, name: "update_todos_tool_call", type: "UpdateTodosToolCall" },
414
- { id: 10, name: "read_todos_tool_call", type: "ReadTodosToolCall" },
415
- { id: 12, name: "edit_tool_call", type: "EditToolCall" },
416
- { id: 13, name: "ls_tool_call", type: "LsToolCall" },
417
- { id: 15, name: "mcp_tool_call", type: "McpToolCall" },
418
- { id: 17, name: "create_plan_tool_call", type: "CreatePlanToolCall" },
419
- { id: 18, name: "web_search_tool_call", type: "WebSearchToolCall" },
420
- { id: 19, name: "task_tool_call", type: "TaskToolCall" },
421
- { id: 23, name: "ask_question_tool_call", type: "AskQuestionToolCall" },
422
- { id: 24, name: "fetch_tool_call", type: "FetchToolCall" },
423
- { id: 25, name: "switch_mode_tool_call", type: "SwitchModeToolCall" },
424
- { id: 28, name: "generate_image_tool_call", type: "GenerateImageToolCall" },
425
- { id: 37, name: "web_fetch_tool_call", type: "WebFetchToolCall" },
426
- { id: 42, name: "await_tool_call", type: "AwaitToolCall" },
427
- { id: 44, name: "get_mcp_tools_tool_call", type: "GetMcpToolsToolCall" },
428
- { id: 61, name: "pi_read_tool_call", type: "PiReadToolCall" },
429
- { id: 62, name: "pi_bash_tool_call", type: "PiBashToolCall" },
430
- { id: 63, name: "pi_edit_tool_call", type: "PiEditToolCall" },
431
- { id: 64, name: "pi_write_tool_call", type: "PiWriteToolCall" },
432
- { id: 65, name: "pi_grep_tool_call", type: "PiGrepToolCall" },
433
- { id: 66, name: "pi_find_tool_call", type: "PiFindToolCall" },
434
- { id: 67, name: "pi_ls_tool_call", type: "PiLsToolCall" },
435
- ],
436
- [{
437
- name: "tool",
438
- fields: [
439
- "shell_tool_call", "delete_tool_call", "glob_tool_call", "grep_tool_call",
440
- "read_tool_call", "update_todos_tool_call", "read_todos_tool_call",
441
- "edit_tool_call", "ls_tool_call",
442
- "mcp_tool_call", "create_plan_tool_call", "web_search_tool_call", "task_tool_call",
443
- "ask_question_tool_call", "fetch_tool_call", "switch_mode_tool_call",
444
- "generate_image_tool_call", "web_fetch_tool_call", "await_tool_call", "get_mcp_tools_tool_call",
445
- "pi_read_tool_call", "pi_bash_tool_call", "pi_edit_tool_call",
446
- "pi_write_tool_call", "pi_grep_tool_call", "pi_find_tool_call", "pi_ls_tool_call",
447
- ],
448
- }],
449
- )
450
-
451
- addType(root, "ToolCallStarted", [
452
- { id: 1, name: "call_id", type: "string" },
453
- { id: 2, name: "tool_call", type: "ToolCall" },
454
- { id: 3, name: "model_call_id", type: "string" },
455
- ])
456
-
457
- // agent.v1 ToolCallCompletedUpdate has no string result field — result lives
458
- // inside the typed ToolCall variant.
459
- addType(root, "ToolCallCompleted", [
460
- { id: 1, name: "call_id", type: "string" },
461
- { id: 2, name: "tool_call", type: "ToolCall" },
462
- { id: 3, name: "model_call_id", type: "string" },
463
- ])
464
-
465
- addType(root, "PartialToolCall", [
466
- { id: 1, name: "call_id", type: "string" },
467
- { id: 2, name: "tool_call", type: "ToolCall" },
468
- { id: 3, name: "args_text_delta", type: "string" },
469
- { id: 4, name: "model_call_id", type: "string" },
470
- ])
471
-
472
- // agent.v1 ToolCallDeltaUpdate: keep the nested delta opaque; the pump
473
- // needs its presence as generation progress, not its display-only content.
474
- addType(root, "ToolCallDeltaUpdate", [
475
- { id: 1, name: "call_id", type: "string" },
476
- { id: 2, name: "tool_call_delta", type: "bytes" },
477
- { id: 3, name: "model_call_id", type: "string" },
478
- ])
479
-
480
- // agent.v1 StepStartedUpdate / StepCompletedUpdate: step_id is uint64 (T:4),
481
- // not string — wrong type made every step_completed frame throw
482
- // "index out of range" in decodeMessage.
483
- addType(root, "StepStarted", [
484
- { id: 1, name: "step_id", type: "uint64" },
485
- ])
486
-
487
- addType(root, "StepCompleted", [
488
- { id: 1, name: "step_id", type: "uint64" },
489
- { id: 2, name: "step_duration_ms", type: "int64" },
490
- ])
491
-
492
- // agent.v1 ToolRequestsListedUpdate — Cursor lists how many tool calls this
493
- // generation will send so the client can place its step boundary (CLI does
494
- // not need this; OpenCode's AI SDK step does). Field 27 on InteractionUpdate.
495
- addType(root, "ToolRequestsListedUpdate", [
496
- { id: 1, name: "call_count", type: "uint32" },
497
- ])
498
-
499
- // InteractionUpdate — the core streaming update message
500
- addType(
501
- root,
502
- "InteractionUpdate",
503
- [
504
- { id: 1, name: "text_delta", type: "TextDeltaUpdate" },
505
- { id: 2, name: "tool_call_started", type: "ToolCallStarted" },
506
- { id: 3, name: "tool_call_completed", type: "ToolCallCompleted" },
507
- { id: 4, name: "thinking_delta", type: "ThinkingDeltaUpdate" },
508
- { id: 7, name: "partial_tool_call", type: "PartialToolCall" },
509
- { id: 13, name: "heartbeat", type: "Heartbeat" },
510
- { id: 14, name: "turn_ended", type: "TurnEnded" },
511
- { id: 15, name: "tool_call_delta", type: "ToolCallDeltaUpdate" },
512
- { id: 16, name: "step_started", type: "StepStarted" },
513
- { id: 17, name: "step_completed", type: "StepCompleted" },
514
- { id: 23, name: "context_injection_state", type: "ContextInjectionStateUpdate" },
515
- { id: 27, name: "tool_requests_listed", type: "ToolRequestsListedUpdate" },
516
- ],
517
- [{ name: "update", fields: ["text_delta", "tool_call_started", "tool_call_completed", "thinking_delta", "partial_tool_call", "heartbeat", "turn_ended", "tool_call_delta", "step_started", "step_completed", "context_injection_state", "tool_requests_listed"] }],
518
- )
519
-
520
- // Server status for an `inject_context_action` (InteractionUpdate #23).
521
- // Cursor CLI only tracks it to show queued steer delivery; it carries no output.
522
- addType(root, "ContextInjectionQueued", [])
523
- addType(root, "ContextInjectionQueuedForNextTurn", [])
524
- addType(root, "ContextInjectionCancelled", [])
525
- addType(root, "ContextInjectionDelivered", [
526
- { id: 1, name: "step", type: "int32" },
527
- { id: 2, name: "delivery_batch_id", type: "string" },
528
- { id: 3, name: "delivered_at_ms", type: "int64" },
529
- ])
530
- addType(root, "ContextInjectionRejected", [
531
- { id: 1, name: "reason", type: "string" },
532
- ])
533
- addType(
534
- root,
535
- "ContextInjectionState",
536
- [
537
- { id: 1, name: "queued", type: "ContextInjectionQueued" },
538
- { id: 2, name: "delivered", type: "ContextInjectionDelivered" },
539
- { id: 3, name: "queued_for_next_turn", type: "ContextInjectionQueuedForNextTurn" },
540
- { id: 4, name: "cancelled", type: "ContextInjectionCancelled" },
541
- { id: 5, name: "rejected", type: "ContextInjectionRejected" },
542
- ],
543
- [{ name: "state", fields: ["queued", "delivered", "queued_for_next_turn", "cancelled", "rejected"] }],
544
- )
545
- addType(root, "ContextInjectionStateUpdate", [
546
- { id: 1, name: "injection_id", type: "string" },
547
- { id: 2, name: "state", type: "ContextInjectionState" },
548
- ])
549
-
550
- // ── Exec channel ──
551
-
552
- // Field numbers match agent.v1. Extra fields we don't use are still declared
553
- // so protobufjs doesn't drop them on decode.
554
- addType(root, "ReadArgs", [
555
- { id: 1, name: "path", type: "string" },
556
- { id: 2, name: "tool_call_id", type: "string" },
557
- { id: 4, name: "offset", type: "int32" },
558
- { id: 5, name: "limit", type: "uint32" },
559
- ])
560
-
561
- // agent.v1 ReadResult is a oneof — flat {content,error} was rejected by the
562
- // server (endless heartbeats after read_result).
563
- addType(
564
- root,
565
- "ReadSuccess",
566
- [
567
- { id: 1, name: "path", type: "string" },
568
- { id: 2, name: "content", type: "string" },
569
- { id: 3, name: "total_lines", type: "int32" },
570
- { id: 4, name: "file_size", type: "int64" },
571
- { id: 5, name: "data", type: "bytes" },
572
- { id: 6, name: "truncated", type: "bool" },
573
- { id: 7, name: "output_blob_id", type: "bytes" },
574
- { id: 8, name: "range_applied", type: "bool" },
575
- ],
576
- [{ name: "output", fields: ["content", "data"] }],
577
- )
578
- addType(root, "ReadError", [
579
- { id: 1, name: "path", type: "string" },
580
- { id: 2, name: "error", type: "string" },
581
- ])
582
- // Cursor's LocalReadExecutor validates the target before execution and, for a
583
- // missing / non-regular path, returns one of these typed ReadResult cases
584
- // instead of running the tool. Mirroring the exact field ids/shapes lets this
585
- // provider reply the same way so the model sees a structured file-not-found
586
- // observation and the host never surfaces a permission prompt.
587
- addType(root, "ReadFileNotFound", [{ id: 1, name: "path", type: "string" }])
588
- addType(root, "ReadPermissionDenied", [{ id: 1, name: "path", type: "string" }])
589
- addType(root, "ReadInvalidFile", [
590
- { id: 1, name: "path", type: "string" },
591
- { id: 2, name: "reason", type: "string" },
592
- ])
593
- addType(root, "ReadRejected", [
594
- { id: 1, name: "path", type: "string" },
595
- { id: 2, name: "reason", type: "string" },
596
- ])
597
- addType(
598
- root,
599
- "ReadResult",
600
- [
601
- { id: 1, name: "success", type: "ReadSuccess" },
602
- { id: 2, name: "error", type: "ReadError" },
603
- { id: 3, name: "rejected", type: "ReadRejected" },
604
- { id: 4, name: "file_not_found", type: "ReadFileNotFound" },
605
- { id: 5, name: "permission_denied", type: "ReadPermissionDenied" },
606
- { id: 6, name: "invalid_file", type: "ReadInvalidFile" },
607
- ],
608
- [
609
- {
610
- name: "result",
611
- fields: ["success", "error", "rejected", "file_not_found", "permission_denied", "invalid_file"],
612
- },
613
- ],
614
- )
615
-
616
- addType(root, "GrepArgs", [
617
- { id: 1, name: "pattern", type: "string" },
618
- { id: 2, name: "path", type: "string" },
619
- { id: 3, name: "glob", type: "string" },
620
- { id: 4, name: "output_mode", type: "string" },
621
- { id: 8, name: "case_insensitive", type: "bool" },
622
- { id: 10, name: "head_limit", type: "int32" },
623
- { id: 14, name: "tool_call_id", type: "string" },
624
- { id: 16, name: "offset", type: "int32" },
625
- ])
626
-
627
- addType(root, "GrepError", [{ id: 1, name: "error", type: "string" }])
628
- addType(root, "GrepFilesResult", [
629
- { id: 1, name: "files", type: "string", repeated: true },
630
- { id: 2, name: "total_files", type: "int32" },
631
- { id: 3, name: "client_truncated", type: "bool" },
632
- ])
633
- addType(root, "GrepContentMatch", [
634
- { id: 1, name: "line_number", type: "int32" },
635
- { id: 2, name: "content", type: "string" },
636
- ])
637
- addType(root, "GrepFileMatch", [
638
- { id: 1, name: "file", type: "string" },
639
- { id: 2, name: "matches", type: "GrepContentMatch", repeated: true },
640
- ])
641
- addType(root, "GrepContentResult", [
642
- { id: 1, name: "matches", type: "GrepFileMatch", repeated: true },
643
- { id: 2, name: "total_lines", type: "int32" },
644
- { id: 3, name: "total_matched_lines", type: "int32" },
645
- { id: 4, name: "client_truncated", type: "bool" },
646
- { id: 5, name: "ripgrep_truncated", type: "bool" },
647
- ])
648
- addType(
649
- root,
650
- "GrepUnionResult",
651
- [
652
- { id: 2, name: "files", type: "GrepFilesResult" },
653
- { id: 3, name: "content", type: "GrepContentResult" },
654
- ],
655
- [{ name: "result", fields: ["files", "content"] }],
656
- )
657
- {
658
- const t = new protobuf.Type("GrepSuccess")
659
- t.add(new protobuf.Field("pattern", 1, "string"))
660
- t.add(new protobuf.Field("path", 2, "string"))
661
- t.add(new protobuf.Field("output_mode", 3, "string"))
662
- t.add(new protobuf.MapField("workspace_results", 4, "string", "GrepUnionResult"))
663
- root.add(t)
664
- }
665
- addType(
666
- root,
667
- "GrepResult",
668
- [
669
- { id: 1, name: "success", type: "GrepSuccess" },
670
- { id: 2, name: "error", type: "GrepError" },
671
- ],
672
- [{ name: "result", fields: ["success", "error"] }],
673
- )
674
-
675
- // agent.v1.WriteArgs. Cursor's own LocalWriteExecutor reads `file_bytes`
676
- // first and only falls back to `file_text`, so a write whose content arrives
677
- // as bytes decodes to an empty string unless field 5 is declared here.
678
- addType(root, "WriteArgs", [
679
- { id: 1, name: "path", type: "string" },
680
- { id: 2, name: "file_text", type: "string" },
681
- { id: 3, name: "tool_call_id", type: "string" },
682
- { id: 4, name: "return_file_content_after_write", type: "bool" },
683
- { id: 5, name: "file_bytes", type: "bytes" },
684
- { id: 6, name: "encoding_hint", type: "string" },
685
- ])
686
-
687
- addType(root, "WriteSuccess", [
688
- { id: 1, name: "path", type: "string" },
689
- { id: 2, name: "lines_created", type: "int32" },
690
- { id: 3, name: "file_size", type: "int32" },
691
- { id: 4, name: "file_content_after_write", type: "string" },
692
- ])
693
- addType(root, "WriteError", [
694
- { id: 1, name: "path", type: "string" },
695
- { id: 2, name: "error", type: "string" },
696
- ])
697
- // Cursor's own executor reports a refused write with this variant rather than
698
- // a generic error, and its agent reads `operation` / `is_readonly` back.
699
- addType(root, "WritePermissionDenied", [
700
- { id: 1, name: "path", type: "string" },
701
- { id: 2, name: "directory", type: "string" },
702
- { id: 3, name: "operation", type: "string" },
703
- { id: 4, name: "error", type: "string" },
704
- { id: 5, name: "is_readonly", type: "bool" },
705
- ])
706
- addType(root, "WriteRejected", [
707
- { id: 1, name: "path", type: "string" },
708
- { id: 2, name: "reason", type: "string" },
709
- ])
710
- addType(
711
- root,
712
- "WriteResult",
713
- [
714
- { id: 1, name: "success", type: "WriteSuccess" },
715
- { id: 3, name: "permission_denied", type: "WritePermissionDenied" },
716
- { id: 5, name: "error", type: "WriteError" },
717
- { id: 6, name: "rejected", type: "WriteRejected" },
718
- ],
719
- [{ name: "result", fields: ["success", "permission_denied", "error", "rejected"] }],
720
- )
721
-
722
- // agent.v1 Pi exec results. Cursor places Pi requests at ExecServerMessage
723
- // fields #45..#51 and their corresponding ExecClientMessage results at
724
- // #46..#52 (the result fields are deliberately offset by one).
725
- //
726
- // The success payloads contain additional optional truncation/diff metadata;
727
- // OpenCode returns text, so the minimal output/error shapes are sufficient.
728
- addType(root, "PiWriteArgs", [
729
- { id: 1, name: "path", type: "string" },
730
- { id: 2, name: "content", type: "string" },
731
- ])
732
- addType(root, "PiWriteSuccess", [
733
- { id: 1, name: "output", type: "string" },
734
- ])
735
- addType(root, "PiWriteError", [
736
- { id: 1, name: "error", type: "string" },
737
- ])
738
- addType(root, "PiWriteRejected", [
739
- { id: 1, name: "reason", type: "string" },
740
- ])
741
- addType(
742
- root,
743
- "PiWriteResult",
744
- [
745
- { id: 1, name: "success", type: "PiWriteSuccess" },
746
- { id: 2, name: "error", type: "PiWriteError" },
747
- { id: 3, name: "rejected", type: "PiWriteRejected" },
748
- ],
749
- [{ name: "result", fields: ["success", "error", "rejected"] }],
750
- )
751
- // agent.v1.PiTruncation. Every Pi *ExecSuccess shares output(1)+truncation(2)
752
- // and only diverges at field 3, so the shared PiOutputSuccess can carry it.
753
- // Cursor's own CLI always reports truncation rather than silently shortening
754
- // a payload; emitting this keeps a capped read distinguishable from a
755
- // complete one.
756
- addType(root, "PiTruncation", [
757
- { id: 1, name: "truncated", type: "bool" },
758
- { id: 2, name: "truncated_by", type: "string" },
759
- { id: 3, name: "total_lines", type: "uint32" },
760
- { id: 4, name: "output_lines", type: "uint32" },
761
- { id: 5, name: "output_bytes", type: "uint32" },
762
- { id: 6, name: "max_lines", type: "uint32" },
763
- { id: 7, name: "max_bytes", type: "uint32" },
764
- { id: 8, name: "first_line_exceeds_limit", type: "bool" },
765
- { id: 9, name: "last_line_partial", type: "bool" },
766
- ])
767
- addType(root, "PiOutputSuccess", [
768
- { id: 1, name: "output", type: "string" },
769
- { id: 2, name: "truncation", type: "PiTruncation" },
770
- ])
771
- addType(root, "PiExecError", [{ id: 1, name: "error", type: "string" }])
772
- addType(root, "PiExecRejected", [{ id: 1, name: "reason", type: "string" }])
773
- for (const resultType of [
774
- "PiReadExecResult",
775
- "PiBashExecResult",
776
- "PiGrepExecResult",
777
- "PiFindExecResult",
778
- "PiLsExecResult",
779
- ]) {
780
- addType(
781
- root,
782
- resultType,
783
- [
784
- { id: 1, name: "success", type: "PiOutputSuccess" },
785
- { id: 2, name: "error", type: "PiExecError" },
786
- ],
787
- [{ name: "result", fields: ["success", "error"] }],
788
- )
789
- }
790
- addType(
791
- root,
792
- "PiEditExecResult",
793
- [
794
- { id: 1, name: "success", type: "PiOutputSuccess" },
795
- { id: 2, name: "error", type: "PiExecError" },
796
- { id: 3, name: "rejected", type: "PiExecRejected" },
797
- ],
798
- [{ name: "result", fields: ["success", "error", "rejected"] }],
799
- )
800
-
801
- addType(root, "DeleteArgs", [
802
- { id: 1, name: "path", type: "string" },
803
- { id: 2, name: "tool_call_id", type: "string" },
804
- ])
805
-
806
- addType(root, "DeleteSuccess", [
807
- { id: 1, name: "path", type: "string" },
808
- { id: 2, name: "deleted_file", type: "string" },
809
- ])
810
- addType(root, "DeleteError", [
811
- { id: 1, name: "path", type: "string" },
812
- { id: 2, name: "error", type: "string" },
813
- ])
814
- addType(root, "DeletePermissionDenied", [
815
- { id: 1, name: "path", type: "string" },
816
- { id: 2, name: "client_visible_error", type: "string" },
817
- { id: 3, name: "is_readonly", type: "bool" },
818
- ])
819
- addType(root, "DeleteRejected", [
820
- { id: 1, name: "path", type: "string" },
821
- { id: 2, name: "reason", type: "string" },
822
- ])
823
- addType(
824
- root,
825
- "DeleteResult",
826
- [
827
- { id: 1, name: "success", type: "DeleteSuccess" },
828
- { id: 4, name: "permission_denied", type: "DeletePermissionDenied" },
829
- { id: 6, name: "rejected", type: "DeleteRejected" },
830
- { id: 7, name: "error", type: "DeleteError" },
831
- ],
832
- [{ name: "result", fields: ["success", "permission_denied", "rejected", "error"] }],
833
- )
834
-
835
- addType(root, "LsArgs", [
836
- { id: 1, name: "path", type: "string" },
837
- { id: 2, name: "ignore", type: "string", repeated: true },
838
- { id: 3, name: "tool_call_id", type: "string" },
839
- ])
840
-
841
- addType(root, "LsDirectoryTreeFile", [{ id: 1, name: "name", type: "string" }])
842
- addType(root, "LsDirectoryTreeNode", [
843
- { id: 1, name: "abs_path", type: "string" },
844
- { id: 2, name: "children_dirs", type: "LsDirectoryTreeNode", repeated: true },
845
- { id: 3, name: "children_files", type: "LsDirectoryTreeFile", repeated: true },
846
- { id: 6, name: "num_files", type: "int32" },
847
- ])
848
- addType(root, "LsSuccess", [
849
- { id: 1, name: "directory_tree_root", type: "LsDirectoryTreeNode" },
850
- ])
851
- addType(root, "LsError", [
852
- { id: 1, name: "path", type: "string" },
853
- { id: 2, name: "error", type: "string" },
854
- ])
855
- addType(root, "LsRejected", [
856
- { id: 1, name: "path", type: "string" },
857
- { id: 2, name: "reason", type: "string" },
858
- ])
859
- addType(
860
- root,
861
- "LsResult",
862
- [
863
- { id: 1, name: "success", type: "LsSuccess" },
864
- { id: 2, name: "error", type: "LsError" },
865
- { id: 3, name: "rejected", type: "LsRejected" },
866
- ],
867
- [{ name: "result", fields: ["success", "error", "rejected"] }],
868
- )
869
-
870
- addType(root, "ShellArgs", [
871
- { id: 1, name: "command", type: "string" },
872
- { id: 2, name: "working_directory", type: "string" },
873
- { id: 3, name: "timeout", type: "uint32" },
874
- { id: 4, name: "tool_call_id", type: "string" },
875
- // Canonical agent.v1 fields used by the native CLI to distinguish a
876
- // foreground cancellation deadline from a soft background handoff.
877
- { id: 13, name: "timeout_behavior", type: "uint32" },
878
- { id: 14, name: "hard_timeout", type: "uint32" },
879
- { id: 15, name: "description", type: "string" },
880
- ])
881
-
882
- addType(root, "ShellStreamStart", []) // optional SandboxPolicy only; empty is valid
883
- addType(root, "ShellStreamStdout", [{ id: 1, name: "data", type: "string" }])
884
- addType(root, "ShellStreamStderr", [{ id: 1, name: "data", type: "string" }])
885
- // agent.v1: code is uint32; protobufjs must emit code=0 (defaults are load-bearing).
886
- addType(root, "ShellStreamExit", [
887
- { id: 1, name: "code", type: "uint32" },
888
- { id: 2, name: "cwd", type: "string" },
889
- { id: 4, name: "aborted", type: "bool" },
890
- { id: 5, name: "abort_reason", type: "uint32" },
891
- { id: 6, name: "local_execution_time_ms", type: "uint32" },
892
- ])
893
- addType(root, "ShellStreamBackgrounded", [
894
- { id: 1, name: "shell_id", type: "uint32" },
895
- { id: 2, name: "command", type: "string" },
896
- { id: 3, name: "working_directory", type: "string" },
897
- { id: 4, name: "pid", type: "uint32" },
898
- { id: 5, name: "ms_to_wait", type: "uint32" },
899
- { id: 6, name: "reason", type: "uint32" },
900
- ])
901
- // ShellRejected / ShellPermissionDenied (shared with ShellResult) — reason/error at #3.
902
- addType(root, "ShellRejected", [
903
- { id: 1, name: "command", type: "string" },
904
- { id: 2, name: "working_directory", type: "string" },
905
- { id: 3, name: "reason", type: "string" },
906
- ])
907
- addType(root, "ShellPermissionDenied", [
908
- { id: 1, name: "command", type: "string" },
909
- { id: 2, name: "working_directory", type: "string" },
910
- { id: 3, name: "error", type: "string" },
911
- ])
912
-
913
- // ── Soft-deny stubs for known unsupported Cursor-native exec variants ──
914
- // These are encode-only: enough to emit a populated deny, not full success.
915
- // Canonical field numbers from agent.proto ExecClientMessage.
916
- // ShellResult is also the live reply for exec #2 shell_args (OpenCode bash).
917
- addType(root, "ShellSuccess", [
918
- { id: 1, name: "command", type: "string" },
919
- { id: 2, name: "working_directory", type: "string" },
920
- { id: 3, name: "exit_code", type: "int32" },
921
- { id: 4, name: "signal", type: "string" },
922
- { id: 5, name: "stdout", type: "string" },
923
- { id: 6, name: "stderr", type: "string" },
924
- { id: 7, name: "execution_time", type: "int32" },
925
- { id: 9, name: "shell_id", type: "uint32" },
926
- { id: 11, name: "pid", type: "uint32" },
927
- { id: 12, name: "ms_to_wait", type: "int32" },
928
- { id: 13, name: "local_execution_time_ms", type: "int32" },
929
- { id: 14, name: "background_reason", type: "uint32" },
930
- ])
931
- addType(root, "ShellFailure", [
932
- { id: 1, name: "command", type: "string" },
933
- { id: 2, name: "working_directory", type: "string" },
934
- { id: 3, name: "exit_code", type: "int32" },
935
- { id: 4, name: "signal", type: "string" },
936
- { id: 5, name: "stdout", type: "string" },
937
- { id: 6, name: "stderr", type: "string" },
938
- { id: 7, name: "execution_time", type: "int32" },
939
- { id: 10, name: "abort_reason", type: "uint32" },
940
- { id: 11, name: "aborted", type: "bool" },
941
- ])
942
- addType(root, "ShellTimeout", [
943
- { id: 1, name: "command", type: "string" },
944
- { id: 2, name: "working_directory", type: "string" },
945
- { id: 3, name: "timeout_ms", type: "int32" },
946
- ])
947
- addType(
948
- root,
949
- "ShellResult",
950
- [
951
- { id: 1, name: "success", type: "ShellSuccess" },
952
- { id: 2, name: "failure", type: "ShellFailure" },
953
- { id: 3, name: "timeout", type: "ShellTimeout" },
954
- { id: 4, name: "rejected", type: "ShellRejected" },
955
- { id: 7, name: "permission_denied", type: "ShellPermissionDenied" },
956
- { id: 102, name: "is_background", type: "bool" },
957
- { id: 104, name: "pid", type: "uint32" },
958
- ],
959
- [{ name: "result", fields: ["success", "failure", "timeout", "rejected", "permission_denied"] }],
960
- )
961
- addType(root, "DiagnosticsError", [
962
- { id: 1, name: "path", type: "string" },
963
- { id: 2, name: "error", type: "string" },
964
- ])
965
- addType(root, "DiagnosticsResult", [
966
- { id: 2, name: "error", type: "DiagnosticsError" },
967
- ], [{ name: "result", fields: ["error"] }])
968
- addType(root, "FetchError", [
969
- { id: 1, name: "url", type: "string" },
970
- { id: 2, name: "error", type: "string" },
971
- ])
972
- addType(root, "FetchResult", [
973
- { id: 2, name: "error", type: "FetchError" },
974
- ], [{ name: "result", fields: ["error"] }])
975
- addType(root, "RecordScreenFailure", [
976
- { id: 1, name: "error", type: "string" },
977
- ])
978
- addType(root, "RecordScreenResult", [
979
- { id: 4, name: "failure", type: "RecordScreenFailure" },
980
- ], [{ name: "result", fields: ["failure"] }])
981
- addType(root, "ComputerUseError", [
982
- { id: 1, name: "error", type: "string" },
983
- ])
984
- addType(root, "ComputerUseResult", [
985
- { id: 2, name: "error", type: "ComputerUseError" },
986
- ], [{ name: "result", fields: ["error"] }])
987
- addType(root, "WriteShellStdinError", [
988
- { id: 1, name: "error", type: "string" },
989
- ])
990
- addType(root, "WriteShellStdinResult", [
991
- { id: 2, name: "error", type: "WriteShellStdinError" },
992
- ], [{ name: "result", fields: ["error"] }])
993
- addType(root, "SubagentAwaitError", [
994
- { id: 1, name: "agent_id", type: "string" },
995
- { id: 2, name: "error", type: "string" },
996
- ])
997
- addType(root, "SubagentAwaitResult", [
998
- { id: 4, name: "error", type: "SubagentAwaitError" },
999
- ], [{ name: "result", fields: ["error"] }])
1000
- addType(root, "SmartModeClassifierError", [
1001
- { id: 1, name: "error", type: "string" },
1002
- ])
1003
- addType(root, "SmartModeClassifierResult", [
1004
- { id: 2, name: "error", type: "SmartModeClassifierError" },
1005
- ], [{ name: "result", fields: ["error"] }])
1006
- addType(root, "CanvasDiagnosticsError", [
1007
- { id: 1, name: "path", type: "string" },
1008
- { id: 2, name: "error", type: "string" },
1009
- ])
1010
- addType(root, "CanvasDiagnosticsResult", [
1011
- { id: 2, name: "error", type: "CanvasDiagnosticsError" },
1012
- ], [{ name: "result", fields: ["error"] }])
1013
- addType(root, "ShellAllowlistPrecheckResult", [
1014
- { id: 1, name: "allowlisted", type: "bool" },
1015
- ])
1016
- addType(root, "McpAllowlistPrecheckResult", [
1017
- { id: 1, name: "allowlisted", type: "bool" },
1018
- ])
1019
- addType(root, "WebFetchAllowlistPrecheckResult", [
1020
- { id: 1, name: "allowlisted", type: "bool" },
1021
- ])
1022
- addType(root, "ForceBackgroundShellResult", [
1023
- { id: 1, name: "status", type: "uint32" },
1024
- ])
1025
- addType(root, "ForceBackgroundSubagentResult", [
1026
- { id: 1, name: "status", type: "uint32" },
1027
- ])
1028
- addType(root, "ConversationSearchHit", [
1029
- { id: 1, name: "conversation_id", type: "string" },
1030
- { id: 2, name: "title", type: "string" },
1031
- { id: 3, name: "source", type: "uint32" },
1032
- { id: 4, name: "updated_at_ms", type: "int64" },
1033
- { id: 5, name: "snippet", type: "string" },
1034
- ])
1035
- addType(root, "ConversationSearchSuccess", [
1036
- { id: 1, name: "hits", type: "ConversationSearchHit", repeated: true },
1037
- { id: 2, name: "truncated", type: "bool" },
1038
- { id: 3, name: "partial", type: "bool" },
1039
- { id: 4, name: "rebuilding", type: "bool" },
1040
- ])
1041
- addType(root, "ConversationSearchError", [
1042
- { id: 1, name: "error", type: "string" },
1043
- ])
1044
- addType(
1045
- root,
1046
- "ConversationSearchResult",
1047
- [
1048
- { id: 1, name: "success", type: "ConversationSearchSuccess" },
1049
- { id: 2, name: "error", type: "ConversationSearchError" },
1050
- ],
1051
- [{ name: "result", fields: ["success", "error"] }],
1052
- )
1053
- addType(root, "AgentStoreConflictError", [
1054
- { id: 1, name: "error", type: "string" },
1055
- ])
1056
- addType(
1057
- root,
1058
- "AgentStoreConflictResult",
1059
- [
1060
- { id: 2, name: "error", type: "AgentStoreConflictError" },
1061
- ],
1062
- [{ name: "result", fields: ["error"] }],
1063
- )
1064
- addType(
1065
- root,
1066
- "AdoptResult",
1067
- [
1068
- { id: 1, name: "source_agent_id", type: "string" },
1069
- { id: 2, name: "target_agent_id", type: "string" },
1070
- { id: 3, name: "project_root_id", type: "string" },
1071
- { id: 5, name: "error", type: "string" },
1072
- ],
1073
- [{ name: "result", fields: ["error"] }],
1074
- )
1075
-
1076
- // aiserver.v1.GetDiffRequest / GetDiffResponse (exec #44). Enums as uint32
1077
- // so protobufjs json decode does not stringify output_format / diff_type.
1078
- addType(root, "GitDiffChunk", [
1079
- { id: 1, name: "content", type: "string" },
1080
- { id: 2, name: "lines", type: "string", repeated: true },
1081
- { id: 3, name: "old_start", type: "int32" },
1082
- { id: 4, name: "old_lines", type: "int32" },
1083
- { id: 5, name: "new_start", type: "int32" },
1084
- { id: 6, name: "new_lines", type: "int32" },
1085
- ])
1086
- addType(root, "GitFileDiff", [
1087
- { id: 1, name: "from", type: "string" },
1088
- { id: 2, name: "to", type: "string" },
1089
- { id: 3, name: "chunks", type: "GitDiffChunk", repeated: true },
1090
- { id: 4, name: "added", type: "int32" },
1091
- { id: 5, name: "removed", type: "int32" },
1092
- { id: 6, name: "before_file_contents", type: "string" },
1093
- { id: 7, name: "after_file_contents", type: "string" },
1094
- { id: 8, name: "is_generated", type: "bool" },
1095
- ])
1096
- addType(root, "GitDiff", [
1097
- { id: 1, name: "diffs", type: "GitFileDiff", repeated: true },
1098
- { id: 2, name: "diff_type", type: "uint32" },
1099
- ])
1100
- addType(root, "GitDiffSubmoduleDiff", [
1101
- { id: 1, name: "relative_path", type: "string" },
1102
- { id: 2, name: "diff", type: "GitDiff" },
1103
- { id: 3, name: "errored", type: "bool" },
1104
- ])
1105
- addType(root, "GetDiffRequest", [
1106
- { id: 1, name: "cwd", type: "string" },
1107
- { id: 2, name: "ref", type: "string" },
1108
- { id: 3, name: "base_ref", type: "string" },
1109
- { id: 4, name: "merge_base", type: "bool" },
1110
- { id: 5, name: "target_paths", type: "string", repeated: true },
1111
- { id: 6, name: "unified_context_lines", type: "int32" },
1112
- { id: 7, name: "max_untracked_files", type: "int32" },
1113
- { id: 8, name: "output_format", type: "uint32" },
1114
- { id: 9, name: "submodule_recurse_depth", type: "int32" },
1115
- { id: 10, name: "include_space_changes", type: "bool" },
1116
- { id: 11, name: "committed_only", type: "bool" },
1117
- { id: 12, name: "compute_patch_id", type: "bool" },
1118
- { id: 13, name: "return_head_sha", type: "bool" },
1119
- { id: 14, name: "max_response_bytes", type: "int32" },
1120
- ])
1121
- addType(root, "GetDiffResponse", [
1122
- { id: 1, name: "diff", type: "GitDiff" },
1123
- { id: 2, name: "submodule_diffs", type: "GitDiffSubmoduleDiff", repeated: true },
1124
- { id: 3, name: "patch_id", type: "string" },
1125
- { id: 4, name: "head_sha", type: "string" },
1126
- { id: 5, name: "has_uncommitted_changes", type: "bool" },
1127
- ])
1128
-
1129
- // Cursor's non-blocking shell path is a separate exec variant from the
1130
- // foreground ShellStream above. The host only needs the fields used at the
1131
- // OpenCode boundary; protobufjs safely skips the richer classifier/approval
1132
- // messages that are private to Cursor's native client.
1133
- addType(root, "BackgroundShellSpawnArgs", [
1134
- { id: 1, name: "command", type: "string" },
1135
- { id: 2, name: "working_directory", type: "string" },
1136
- { id: 3, name: "tool_call_id", type: "string" },
1137
- { id: 6, name: "enable_write_shell_stdin_tool", type: "bool" },
1138
- { id: 7, name: "description", type: "string" },
1139
- { id: 12, name: "skip_approval", type: "bool" },
1140
- { id: 13, name: "conversation_id", type: "string" },
1141
- ])
1142
- addType(root, "BackgroundShellSpawnSuccess", [
1143
- { id: 1, name: "shell_id", type: "uint32" },
1144
- { id: 2, name: "command", type: "string" },
1145
- { id: 3, name: "working_directory", type: "string" },
1146
- { id: 4, name: "pid", type: "uint32" },
1147
- ])
1148
- addType(root, "BackgroundShellSpawnError", [
1149
- { id: 1, name: "command", type: "string" },
1150
- { id: 2, name: "working_directory", type: "string" },
1151
- { id: 3, name: "error", type: "string" },
1152
- ])
1153
- addType(
1154
- root,
1155
- "BackgroundShellSpawnResult",
1156
- [
1157
- { id: 1, name: "success", type: "BackgroundShellSpawnSuccess" },
1158
- { id: 2, name: "error", type: "BackgroundShellSpawnError" },
1159
- { id: 3, name: "rejected", type: "ShellRejected" },
1160
- { id: 4, name: "permission_denied", type: "ShellPermissionDenied" },
1161
- ],
1162
- [{ name: "result", fields: ["success", "error", "rejected", "permission_denied"] }],
1163
- )
1164
-
1165
- // Cursor CLI postToolUse hook context: ExecClientMessage #45 on a unary
1166
- // result, ShellStream #8 on a shell stream (agent.v1 hook_additional_context).
1167
- addType(root, "HookAdditionalContext", [
1168
- { id: 1, name: "hook_event_name", type: "string" },
1169
- { id: 2, name: "content", type: "string" },
1170
- ])
1171
- addType(root, "ShellStreamHookContext", [
1172
- { id: 1, name: "hook_additional_contexts", type: "HookAdditionalContext", repeated: true },
1173
- ])
1174
-
1175
- addType(
1176
- root,
1177
- "ShellStream",
1178
- [
1179
- { id: 1, name: "stdout", type: "ShellStreamStdout" },
1180
- { id: 2, name: "stderr", type: "ShellStreamStderr" },
1181
- { id: 3, name: "exit", type: "ShellStreamExit" },
1182
- { id: 4, name: "start", type: "ShellStreamStart" },
1183
- { id: 5, name: "rejected", type: "ShellRejected" },
1184
- { id: 6, name: "permission_denied", type: "ShellPermissionDenied" },
1185
- { id: 7, name: "backgrounded", type: "ShellStreamBackgrounded" },
1186
- { id: 8, name: "hook_context", type: "ShellStreamHookContext" },
1187
- ],
1188
- [{ name: "event", fields: ["stdout", "stderr", "exit", "start", "rejected", "permission_denied", "backgrounded", "hook_context"] }],
1189
- )
1190
-
1191
- addType(root, "GlobArgs", [
1192
- { id: 1, name: "target_directory", type: "string" },
1193
- { id: 2, name: "glob_pattern", type: "string" },
1194
- ])
1195
-
1196
- addType(root, "GlobResult", [
1197
- { id: 1, name: "files", type: "string", repeated: true },
1198
- { id: 2, name: "error", type: "string" },
1199
- ])
1200
-
1201
- // `args` is a map<string, google.protobuf.Value> on the wire (repeated map
1202
- // entries at field #2). We capture each entry as raw bytes and decode them in
1203
- // tools.ts via struct.decodeStructEntriesToJson.
1204
- addType(root, "McpArgs", [
1205
- { id: 1, name: "name", type: "string" },
1206
- { id: 2, name: "args", type: "bytes", repeated: true },
1207
- { id: 3, name: "tool_call_id", type: "string" },
1208
- { id: 4, name: "provider_identifier", type: "string" },
1209
- { id: 5, name: "tool_name", type: "string" },
1210
- ])
1211
-
1212
- addType(root, "McpTextContent", [{ id: 1, name: "text", type: "string" }])
1213
- // Cursor CLI 2026.10.01 `agent/v1/mcp_exec_pb.js`: "McpImageContent|1 data 12|2 mime_type 9",
1214
- // "McpToolResultContentItem|1 text #0 content|2 image #1 content".
1215
- addType(root, "McpImageContent", [
1216
- { id: 1, name: "data", type: "bytes" },
1217
- { id: 2, name: "mime_type", type: "string" },
1218
- ])
1219
- addType(
1220
- root,
1221
- "McpToolResultContentItem",
1222
- [
1223
- { id: 1, name: "text", type: "McpTextContent" },
1224
- { id: 2, name: "image", type: "McpImageContent" },
1225
- ],
1226
- [{ name: "content", fields: ["text", "image"] }],
1227
- )
1228
- addType(root, "McpRejected", [
1229
- { id: 1, name: "reason", type: "string" },
1230
- { id: 2, name: "is_readonly", type: "bool" },
1231
- ])
1232
- addType(root, "McpPermissionDenied", [
1233
- { id: 1, name: "error", type: "string" },
1234
- { id: 2, name: "is_readonly", type: "bool" },
1235
- ])
1236
- addType(root, "McpSuccess", [
1237
- { id: 1, name: "content", type: "McpToolResultContentItem", repeated: true },
1238
- { id: 2, name: "is_error", type: "bool" },
1239
- ])
1240
- addType(root, "McpError", [
1241
- { id: 1, name: "error", type: "string" },
1242
- // Server-side CallDynamicTool validation uses a title (#1) and detail (#2).
1243
- { id: 2, name: "detail", type: "string" },
1244
- ])
1245
- addType(
1246
- root,
1247
- "McpResult",
1248
- [
1249
- { id: 1, name: "success", type: "McpSuccess" },
1250
- { id: 2, name: "error", type: "McpError" },
1251
- { id: 3, name: "rejected", type: "McpRejected" },
1252
- { id: 4, name: "permission_denied", type: "McpPermissionDenied" },
1253
- ],
1254
- [{ name: "result", fields: ["success", "error", "rejected", "permission_denied"] }],
1255
- )
1256
-
1257
- // McpToolDefinition — agent.v1 shape for RequestContext.tools (#7) and
1258
- // AgentRunRequest.mcp_tools (#4). Defined early so RequestContext* can
1259
- // reference it.
1260
- addType(root, "McpToolDefinition", [
1261
- { id: 1, name: "name", type: "string" },
1262
- { id: 2, name: "description", type: "string" },
1263
- { id: 3, name: "input_schema", type: "bytes" },
1264
- { id: 4, name: "provider_identifier", type: "string" },
1265
- { id: 5, name: "tool_name", type: "string" },
1266
- ])
1267
-
1268
- // ── MCP resource exec (fields 17/18) ──
1269
- // agent.v1 ListMcpResourcesExecArgs/Result, ReadMcpResourceExecArgs/Result.
1270
- // Native Cursor exec variants: no descriptor, no advertisement, and Cursor
1271
- // may emit either unsolicited regardless of what this provider advertises
1272
- // (tasks/plans/fix-cursor-mcp-resource-exec.md, Correction 2). Under Option
1273
- // B, list_mcp_resources/read_mcp_resource execute through the ordinary
1274
- // field-11 MCP path via the alias rules below, so any 17/18 that still
1275
- // reaches this provider gets the total fallback built in tools.ts.
1276
- addType(root, "SmartModeApproval", [
1277
- { id: 1, name: "request_id", type: "string" },
1278
- { id: 2, name: "reason", type: "string" },
1279
- ])
1280
- addType(root, "OutputLocation", [
1281
- { id: 1, name: "file_path", type: "string" },
1282
- { id: 2, name: "size_bytes", type: "int64" },
1283
- { id: 3, name: "line_count", type: "int64" },
1284
- ])
1285
-
1286
- addType(root, "ListMcpResourcesExecArgs", [
1287
- { id: 1, name: "server", type: "string" },
1288
- ])
1289
- {
1290
- const t = new protobuf.Type("ListMcpResourcesExecResult_McpResource")
1291
- t.add(new protobuf.Field("uri", 1, "string"))
1292
- t.add(new protobuf.Field("name", 2, "string"))
1293
- t.add(new protobuf.Field("description", 3, "string"))
1294
- t.add(new protobuf.Field("mime_type", 4, "string"))
1295
- t.add(new protobuf.Field("server", 5, "string"))
1296
- t.add(new protobuf.MapField("annotations", 6, "string", "string"))
1297
- root.add(t)
1298
- }
1299
- addType(root, "ListMcpResourcesSuccess", [
1300
- { id: 1, name: "resources", type: "ListMcpResourcesExecResult_McpResource", repeated: true },
1301
- ])
1302
- addType(root, "ListMcpResourcesError", [{ id: 1, name: "error", type: "string" }])
1303
- addType(root, "ListMcpResourcesRejected", [{ id: 1, name: "reason", type: "string" }])
1304
- addType(
1305
- root,
1306
- "ListMcpResourcesExecResult",
1307
- [
1308
- { id: 1, name: "success", type: "ListMcpResourcesSuccess" },
1309
- { id: 2, name: "error", type: "ListMcpResourcesError" },
1310
- { id: 3, name: "rejected", type: "ListMcpResourcesRejected" },
1311
- ],
1312
- [{ name: "result", fields: ["success", "error", "rejected"] }],
1313
- )
1314
-
1315
- addType(root, "ReadMcpResourceExecArgs", [
1316
- { id: 1, name: "server", type: "string" },
1317
- { id: 2, name: "uri", type: "string" },
1318
- { id: 3, name: "download_path", type: "string" },
1319
- { id: 4, name: "tool_call_id", type: "string" },
1320
- { id: 5, name: "smart_mode_approval", type: "SmartModeApproval" },
1321
- ])
1322
- {
1323
- // Interleaved field order matches agent.proto exactly: the text/blob oneof
1324
- // (5/6) sits between the shared metadata (1-4) and the trailing
1325
- // annotations/download/output fields (7-9).
1326
- const t = new protobuf.Type("ReadMcpResourceSuccess")
1327
- t.add(new protobuf.Field("uri", 1, "string"))
1328
- t.add(new protobuf.Field("name", 2, "string"))
1329
- t.add(new protobuf.Field("description", 3, "string"))
1330
- t.add(new protobuf.Field("mime_type", 4, "string"))
1331
- t.add(new protobuf.Field("text", 5, "string"))
1332
- t.add(new protobuf.Field("blob", 6, "bytes"))
1333
- t.add(new protobuf.MapField("annotations", 7, "string", "string"))
1334
- t.add(new protobuf.Field("download_path", 8, "string"))
1335
- t.add(new protobuf.Field("output_location", 9, "OutputLocation"))
1336
- t.add(new protobuf.OneOf("content", ["text", "blob"]))
1337
- root.add(t)
1338
- }
1339
- addType(root, "ReadMcpResourceError", [
1340
- { id: 1, name: "uri", type: "string" },
1341
- { id: 2, name: "error", type: "string" },
1342
- ])
1343
- // Cursor quirk: on `rejected` only `reason` is set — `uri` (field 1) is
1344
- // deliberately left empty. Mirror this; do not backfill uri on encode.
1345
- addType(root, "ReadMcpResourceRejected", [
1346
- { id: 1, name: "uri", type: "string" },
1347
- { id: 2, name: "reason", type: "string" },
1348
- ])
1349
- addType(root, "ReadMcpResourceNotFound", [{ id: 1, name: "uri", type: "string" }])
1350
- addType(
1351
- root,
1352
- "ReadMcpResourceExecResult",
1353
- [
1354
- { id: 1, name: "success", type: "ReadMcpResourceSuccess" },
1355
- { id: 2, name: "error", type: "ReadMcpResourceError" },
1356
- { id: 3, name: "rejected", type: "ReadMcpResourceRejected" },
1357
- { id: 4, name: "not_found", type: "ReadMcpResourceNotFound" },
1358
- ],
1359
- [{ name: "result", fields: ["success", "error", "rejected", "not_found"] }],
1360
- )
1361
-
1362
- // ── request_context (#10): server-initiated setup probe ──
1363
- // At the start of an agent turn the server sends ExecServerMessage
1364
- // {request_context_args} to ask the client for workspace/env/tool context.
1365
- // The reply is ExecClientMessage {request_context_result{success{request_context}}}.
1366
- // If we don't reply, the server blocks on heartbeats forever (the "times out,
1367
- // no response" symptom). We populate a minimal-but-real RequestContext — env
1368
- // (workspace/shell/os) plus the tool list echoed into #7 tools (same
1369
- // McpToolDefinition shape as AgentRunRequest #4 mcp_tools) so the model keeps
1370
- // the tools opencode advertised.
1371
- addType(root, "RequestContextArgs", [
1372
- { id: 2, name: "notes_session_id", type: "string" },
1373
- { id: 3, name: "workspace_id", type: "string" },
1374
- { id: 7, name: "use_cached", type: "bool" },
1375
- ])
1376
-
1377
- addType(root, "RequestContextEnv", [
1378
- { id: 1, name: "os_version", type: "string" },
1379
- { id: 2, name: "workspace_paths", type: "string", repeated: true },
1380
- { id: 3, name: "shell", type: "string" },
1381
- { id: 5, name: "sandbox_enabled", type: "bool" },
1382
- { id: 7, name: "terminals_folder", type: "string" },
1383
- { id: 10, name: "time_zone", type: "string" },
1384
- { id: 11, name: "project_folder", type: "string" },
1385
- { id: 12, name: "agent_transcripts_folder", type: "string" },
1386
- { id: 14, name: "sandbox_supported", type: "bool" },
1387
- { id: 20, name: "is_working_dir_home_dir", type: "bool" },
1388
- { id: 21, name: "process_working_directory", type: "string" },
1389
- ])
1390
-
1391
- // Cursor applies a rule by its type: `global` is the CLI's `alwaysApply: true`
1392
- // (its AGENTS.md / CLAUDE.md default). An untyped rule is only attachable on
1393
- // request, so its content never reaches the model on its own.
1394
- addType(root, "CursorRuleTypeGlobal", [])
1395
- addType(root, "CursorRuleTypeFileGlobs", [
1396
- { id: 1, name: "globs", type: "string", repeated: true },
1397
- ])
1398
- addType(root, "CursorRuleTypeAgentFetched", [
1399
- { id: 1, name: "description", type: "string" },
1400
- ])
1401
- addType(root, "CursorRuleTypeManuallyAttached", [])
1402
- addType(root, "CursorRuleType", [
1403
- { id: 1, name: "global", type: "CursorRuleTypeGlobal" },
1404
- { id: 2, name: "file_globbed", type: "CursorRuleTypeFileGlobs" },
1405
- { id: 3, name: "agent_fetched", type: "CursorRuleTypeAgentFetched" },
1406
- { id: 4, name: "manually_attached", type: "CursorRuleTypeManuallyAttached" },
1407
- ], [{ name: "type", fields: ["global", "file_globbed", "agent_fetched", "manually_attached"] }])
1408
- addType(root, "CursorRule", [
1409
- { id: 1, name: "full_path", type: "string" },
1410
- { id: 2, name: "content", type: "string" },
1411
- { id: 3, name: "type", type: "CursorRuleType" },
1412
- ])
1413
-
1414
- addType(root, "RepositoryIndexingInfo", [
1415
- { id: 1, name: "relative_workspace_path", type: "string" },
1416
- { id: 2, name: "remote_urls", type: "string", repeated: true },
1417
- { id: 3, name: "remote_names", type: "string", repeated: true },
1418
- { id: 4, name: "repo_name", type: "string" },
1419
- { id: 5, name: "repo_owner", type: "string" },
1420
- { id: 6, name: "is_tracked", type: "bool" },
1421
- { id: 7, name: "is_local", type: "bool" },
1422
- { id: 9, name: "workspace_uri", type: "string" },
1423
- ])
1424
-
1425
- addType(root, "GitRepoInfo", [
1426
- { id: 1, name: "path", type: "string" },
1427
- { id: 2, name: "status", type: "string" },
1428
- { id: 3, name: "branch_name", type: "string" },
1429
- { id: 4, name: "remote_url", type: "string" },
1430
- ])
1431
-
1432
- addType(root, "AgentSkill", [
1433
- { id: 1, name: "full_path", type: "string" },
1434
- { id: 2, name: "content", type: "string" },
1435
- { id: 3, name: "description", type: "string" },
1436
- ])
1437
-
1438
- addType(root, "CustomSubagent", [
1439
- { id: 1, name: "full_path", type: "string" },
1440
- { id: 2, name: "name", type: "string" },
1441
- { id: 3, name: "description", type: "string" },
1442
- { id: 6, name: "prompt", type: "string" },
1443
- ])
1444
-
1445
- // Nested MCP filesystem / meta-tool shapes (agent.v1). Must be defined
1446
- // before RequestContextPayload / RequestContext reference them.
1447
- addType(root, "McpFsToolDescriptor", [
1448
- { id: 1, name: "tool_name", type: "string" },
1449
- { id: 3, name: "description", type: "string" },
1450
- { id: 4, name: "input_schema", type: "bytes" },
1451
- ])
1452
-
1453
- addType(root, "McpDescriptor", [
1454
- { id: 1, name: "server_name", type: "string" },
1455
- { id: 2, name: "server_identifier", type: "string" },
1456
- { id: 5, name: "tools", type: "McpFsToolDescriptor", repeated: true },
1457
- ])
1458
-
1459
- addType(root, "McpFileSystemOptions", [
1460
- { id: 1, name: "enabled", type: "bool" },
1461
- { id: 2, name: "workspace_project_dir", type: "string" },
1462
- { id: 3, name: "mcp_descriptors", type: "McpDescriptor", repeated: true },
1463
- ])
1464
-
1465
- addType(root, "McpMetaToolOptions", [
1466
- { id: 1, name: "enabled", type: "bool" },
1467
- { id: 2, name: "mcp_descriptors", type: "McpDescriptor", repeated: true },
1468
- ])
1469
-
1470
- addType(root, "PermissionsAutoRunInstructions", [
1471
- { id: 1, name: "allow_instructions", type: "string", repeated: true },
1472
- { id: 2, name: "block_instructions", type: "string", repeated: true },
1473
- ])
1474
-
1475
- addType(root, "RequestContextPayload", [
1476
- { id: 2, name: "rules", type: "CursorRule", repeated: true },
1477
- { id: 4, name: "env", type: "RequestContextEnv" },
1478
- { id: 6, name: "repository_info", type: "RepositoryIndexingInfo", repeated: true },
1479
- { id: 7, name: "tools", type: "McpToolDefinition", repeated: true },
1480
- { id: 11, name: "git_repos", type: "GitRepoInfo", repeated: true },
1481
- { id: 13, name: "project_layouts", type: "LsDirectoryTreeNode", repeated: true },
1482
- { id: 17, name: "web_search_enabled", type: "bool" },
1483
- { id: 22, name: "custom_subagents", type: "CustomSubagent", repeated: true },
1484
- { id: 23, name: "mcp_file_system_options", type: "McpFileSystemOptions" },
1485
- { id: 24, name: "web_fetch_enabled", type: "bool" },
1486
- { id: 25, name: "hooks_additional_context", type: "string" },
1487
- { id: 29, name: "agent_skills", type: "AgentSkill", repeated: true },
1488
- { id: 33, name: "git_repo_info_complete", type: "bool" },
1489
- { id: 34, name: "mcp_meta_tool_options", type: "McpMetaToolOptions" },
1490
- { id: 36, name: "mcp_info_complete", type: "bool" },
1491
- { id: 39, name: "rules_info_complete", type: "bool" },
1492
- { id: 40, name: "env_info_complete", type: "bool" },
1493
- { id: 41, name: "repository_info_complete", type: "bool" },
1494
- { id: 42, name: "custom_subagents_info_complete", type: "bool" },
1495
- { id: 43, name: "agent_skills_info_complete", type: "bool" },
1496
- { id: 44, name: "mcp_file_system_info_complete", type: "bool" },
1497
- { id: 45, name: "git_status_info_complete", type: "bool" },
1498
- { id: 46, name: "user_permissions_auto_run", type: "PermissionsAutoRunInstructions" },
1499
- { id: 47, name: "project_permissions_auto_run", type: "PermissionsAutoRunInstructions" },
1500
- ])
1501
-
1502
- addType(root, "RequestContextSuccess", [
1503
- { id: 1, name: "request_context", type: "RequestContextPayload" },
1504
- ])
1505
-
1506
- // Allowlist prechecks (exec #41/#42/#43): Cursor asks whether a shell
1507
- // command / MCP tool / web fetch would be permitted before it emits the
1508
- // real call. Without an args frame this is decodable, but its exec id is
1509
- // then stranded — the server waits forever for the matching result.
1510
- addType(root, "ShellAllowlistPrecheckArgs", [
1511
- { id: 1, name: "command", type: "string" },
1512
- { id: 2, name: "working_directory", type: "string" },
1513
- { id: 3, name: "parsing_result", type: "bytes" },
1514
- { id: 4, name: "classifier_result", type: "bytes" },
1515
- { id: 5, name: "tool_call_id", type: "string" },
1516
- ])
1517
- addType(root, "McpAllowlistPrecheckArgs", [
1518
- { id: 1, name: "provider_identifier", type: "string" },
1519
- { id: 2, name: "tool_name", type: "string" },
1520
- { id: 3, name: "tool_call_id", type: "string" },
1521
- ])
1522
- addType(root, "WebFetchAllowlistPrecheckArgs", [
1523
- { id: 1, name: "url", type: "string" },
1524
- { id: 2, name: "tool_call_id", type: "string" },
1525
- ])
1526
-
1527
- addType(root, "RequestContextResult", [
1528
- { id: 1, name: "success", type: "RequestContextSuccess" },
1529
- ])
1530
-
1531
- // Cursor probes MCP server availability before emitting an MCP-backed tool
1532
- // call. OpenCode owns those servers, so answer from the live host catalog
1533
- // rather than surfacing this as a user tool.
1534
- addType(root, "McpStateExecArgs", [
1535
- { id: 1, name: "server_identifiers", type: "string", repeated: true },
1536
- { id: 2, name: "kick_only", type: "bool" },
1537
- ])
1538
- addType(root, "McpInstructions", [
1539
- { id: 1, name: "server_name", type: "string" },
1540
- { id: 2, name: "instructions", type: "string" },
1541
- { id: 3, name: "server_identifier", type: "string" },
1542
- ])
1543
- addType(root, "McpStateServer", [
1544
- { id: 1, name: "server_name", type: "string" },
1545
- { id: 2, name: "server_identifier", type: "string" },
1546
- { id: 3, name: "plugin", type: "string" },
1547
- { id: 4, name: "marketplace", type: "string" },
1548
- // Unlike McpDescriptor (#23/#34), exec #36 returns the full canonical
1549
- // McpToolDefinition shape, including composite name and provider identity.
1550
- { id: 5, name: "tools", type: "McpToolDefinition", repeated: true },
1551
- { id: 6, name: "instructions", type: "McpInstructions", repeated: true },
1552
- { id: 7, name: "status", type: "string" },
1553
- ])
1554
- addType(root, "McpStateSuccess", [
1555
- { id: 1, name: "servers", type: "McpStateServer", repeated: true },
1556
- ])
1557
- addType(root, "McpStateError", [{ id: 1, name: "error", type: "string" }])
1558
- addType(root, "McpStateRejected", [{ id: 1, name: "reason", type: "string" }])
1559
- addType(
1560
- root,
1561
- "McpStateExecResult",
1562
- [
1563
- { id: 1, name: "success", type: "McpStateSuccess" },
1564
- { id: 2, name: "error", type: "McpStateError" },
1565
- { id: 3, name: "rejected", type: "McpStateRejected" },
1566
- ],
1567
- [{ name: "result", fields: ["success", "error", "rejected"] }],
1568
- )
1569
-
1570
- // ExecServerMessage — server asks us to execute a tool
1571
- addType(
1572
- root,
1573
- "ExecServerMessage",
1574
- [
1575
- { id: 1, name: "id", type: "uint32" },
1576
- { id: 15, name: "exec_id", type: "string" },
1577
- { id: 19, name: "span", type: "string" },
1578
- { id: 2, name: "shell_args", type: "ShellArgs" },
1579
- { id: 3, name: "write_args", type: "WriteArgs" },
1580
- { id: 4, name: "delete_args", type: "DeleteArgs" },
1581
- { id: 5, name: "grep_args", type: "GrepArgs" },
1582
- { id: 7, name: "read_args", type: "ReadArgs" },
1583
- { id: 8, name: "ls_args", type: "LsArgs" },
1584
- { id: 10, name: "request_context_args", type: "RequestContextArgs" },
1585
- { id: 11, name: "mcp_args", type: "McpArgs" },
1586
- { id: 14, name: "shell_stream_args", type: "ShellArgs" },
1587
- { id: 16, name: "background_shell_spawn_args", type: "BackgroundShellSpawnArgs" },
1588
- { id: 17, name: "list_mcp_resources_exec_args", type: "ListMcpResourcesExecArgs" },
1589
- { id: 18, name: "read_mcp_resource_exec_args", type: "ReadMcpResourceExecArgs" },
1590
- { id: 28, name: "subagent_args", type: "SubagentArgs" },
1591
- { id: 36, name: "mcp_state_exec_args", type: "McpStateExecArgs" },
1592
- { id: 41, name: "shell_allowlist_precheck_args", type: "ShellAllowlistPrecheckArgs" },
1593
- { id: 42, name: "mcp_allowlist_precheck_args", type: "McpAllowlistPrecheckArgs" },
1594
- { id: 43, name: "web_fetch_allowlist_precheck_args", type: "WebFetchAllowlistPrecheckArgs" },
1595
- { id: 44, name: "git_diff_request", type: "GetDiffRequest" },
1596
- { id: 45, name: "pi_read_args", type: "PiReadToolArgs" },
1597
- { id: 46, name: "pi_bash_args", type: "PiBashToolArgs" },
1598
- { id: 47, name: "pi_edit_args", type: "PiEditToolArgs" },
1599
- { id: 48, name: "pi_write_args", type: "PiWriteArgs" },
1600
- { id: 49, name: "pi_grep_args", type: "PiGrepToolArgs" },
1601
- { id: 50, name: "pi_find_args", type: "PiFindToolArgs" },
1602
- { id: 51, name: "pi_ls_args", type: "PiLsToolArgs" },
1603
- { id: 55, name: "accept_hook_additional_contexts", type: "bool" },
1604
- ],
1605
- [{ name: "args", fields: [
1606
- "shell_args", "write_args", "delete_args", "grep_args", "read_args", "ls_args",
1607
- "request_context_args", "mcp_args", "shell_stream_args", "background_shell_spawn_args",
1608
- "list_mcp_resources_exec_args", "read_mcp_resource_exec_args", "mcp_state_exec_args",
1609
- "shell_allowlist_precheck_args", "mcp_allowlist_precheck_args", "web_fetch_allowlist_precheck_args",
1610
- "subagent_args", "git_diff_request",
1611
- "pi_read_args", "pi_bash_args", "pi_edit_args", "pi_write_args",
1612
- "pi_grep_args", "pi_find_args", "pi_ls_args",
1613
- ] }],
1614
- )
1615
-
1616
- // ExecClientMessage — client sends tool result back
1617
- addType(
1618
- root,
1619
- "ExecClientMessage",
1620
- [
1621
- { id: 1, name: "id", type: "uint32" },
1622
- { id: 15, name: "exec_id", type: "string" },
1623
- { id: 39, name: "local_execution_time_ms", type: "uint64" },
1624
- { id: 45, name: "hook_additional_contexts", type: "HookAdditionalContext", repeated: true },
1625
- { id: 2, name: "shell_result", type: "ShellResult" },
1626
- { id: 3, name: "write_result", type: "WriteResult" },
1627
- { id: 4, name: "delete_result", type: "DeleteResult" },
1628
- { id: 5, name: "grep_result", type: "GrepResult" },
1629
- { id: 7, name: "read_result", type: "ReadResult" },
1630
- { id: 8, name: "ls_result", type: "LsResult" },
1631
- { id: 9, name: "diagnostics_result", type: "DiagnosticsResult" },
1632
- { id: 10, name: "request_context_result", type: "RequestContextResult" },
1633
- { id: 11, name: "mcp_result", type: "McpResult" },
1634
- { id: 14, name: "shell_stream", type: "ShellStream" },
1635
- { id: 16, name: "background_shell_spawn_result", type: "BackgroundShellSpawnResult" },
1636
- { id: 17, name: "list_mcp_resources_exec_result", type: "ListMcpResourcesExecResult" },
1637
- { id: 18, name: "read_mcp_resource_exec_result", type: "ReadMcpResourceExecResult" },
1638
- { id: 20, name: "fetch_result", type: "FetchResult" },
1639
- { id: 21, name: "record_screen_result", type: "RecordScreenResult" },
1640
- { id: 22, name: "computer_use_result", type: "ComputerUseResult" },
1641
- { id: 23, name: "write_shell_stdin_result", type: "WriteShellStdinResult" },
1642
- { id: 28, name: "subagent_result", type: "SubagentResult" },
1643
- { id: 29, name: "redacted_read_result", type: "ReadResult" },
1644
- { id: 30, name: "force_background_shell_result", type: "ForceBackgroundShellResult" },
1645
- { id: 31, name: "force_background_subagent_result", type: "ForceBackgroundSubagentResult" },
1646
- { id: 36, name: "mcp_state_exec_result", type: "McpStateExecResult" },
1647
- { id: 37, name: "subagent_await_result", type: "SubagentAwaitResult" },
1648
- { id: 38, name: "smart_mode_classifier_result", type: "SmartModeClassifierResult" },
1649
- { id: 40, name: "canvas_diagnostics_result", type: "CanvasDiagnosticsResult" },
1650
- { id: 41, name: "shell_allowlist_precheck_result", type: "ShellAllowlistPrecheckResult" },
1651
- { id: 42, name: "mcp_allowlist_precheck_result", type: "McpAllowlistPrecheckResult" },
1652
- { id: 43, name: "web_fetch_allowlist_precheck_result", type: "WebFetchAllowlistPrecheckResult" },
1653
- { id: 44, name: "git_diff_response", type: "GetDiffResponse" },
1654
- { id: 46, name: "pi_read_result", type: "PiReadExecResult" },
1655
- { id: 47, name: "pi_bash_result", type: "PiBashExecResult" },
1656
- { id: 48, name: "pi_edit_result", type: "PiEditExecResult" },
1657
- { id: 49, name: "pi_write_result", type: "PiWriteResult" },
1658
- { id: 50, name: "pi_grep_result", type: "PiGrepExecResult" },
1659
- { id: 51, name: "pi_find_result", type: "PiFindExecResult" },
1660
- { id: 52, name: "pi_ls_result", type: "PiLsExecResult" },
1661
- { id: 53, name: "conversation_search_result", type: "ConversationSearchResult" },
1662
- { id: 54, name: "agent_store_conflict_result", type: "AgentStoreConflictResult" },
1663
- { id: 55, name: "mini_swe_agent_bash_result", type: "ShellResult" },
1664
- { id: 56, name: "adopt_result", type: "AdoptResult" },
1665
- ],
1666
- [{ name: "result", fields: [
1667
- "shell_result", "write_result", "delete_result", "grep_result", "read_result", "ls_result",
1668
- "diagnostics_result", "request_context_result", "mcp_result", "shell_stream", "background_shell_spawn_result",
1669
- "list_mcp_resources_exec_result", "read_mcp_resource_exec_result",
1670
- "fetch_result", "record_screen_result", "computer_use_result", "write_shell_stdin_result",
1671
- "subagent_result", "redacted_read_result",
1672
- "force_background_shell_result", "force_background_subagent_result",
1673
- "mcp_state_exec_result",
1674
- "subagent_await_result", "smart_mode_classifier_result", "canvas_diagnostics_result",
1675
- "shell_allowlist_precheck_result", "mcp_allowlist_precheck_result", "web_fetch_allowlist_precheck_result",
1676
- "git_diff_response",
1677
- "pi_read_result", "pi_bash_result", "pi_edit_result", "pi_write_result",
1678
- "pi_grep_result", "pi_find_result", "pi_ls_result",
1679
- "conversation_search_result", "agent_store_conflict_result",
1680
- "mini_swe_agent_bash_result", "adopt_result",
1681
- ] }],
1682
- )
1683
-
1684
- addType(root, "ExecServerControlMessage", [
1685
- { id: 1, name: "abort", type: "ExecServerAbort" },
1686
- ])
1687
-
1688
- addType(root, "ExecServerAbort", [
1689
- { id: 1, name: "id", type: "uint32" },
1690
- ])
1691
-
1692
- // Client→server control (ACM #5). Distinct from ExecServerControlMessage (ASM #5).
1693
- // After every exec result (especially multi-frame shell_stream), the real CLI
1694
- // sends stream_close{id} — without it the cloud waits forever on shell execs.
1695
- addType(root, "ExecClientStreamClose", [{ id: 1, name: "id", type: "uint32" }])
1696
- addType(root, "ExecClientThrow", [
1697
- { id: 1, name: "id", type: "uint32" },
1698
- { id: 2, name: "error", type: "string" },
1699
- ])
1700
- addType(root, "ExecClientHeartbeat", [{ id: 1, name: "id", type: "uint32" }])
1701
- addType(
1702
- root,
1703
- "ExecClientControlMessage",
1704
- [
1705
- { id: 1, name: "stream_close", type: "ExecClientStreamClose" },
1706
- { id: 2, name: "throw", type: "ExecClientThrow" },
1707
- { id: 3, name: "heartbeat", type: "ExecClientHeartbeat" },
1708
- ],
1709
- [{ name: "message", fields: ["stream_close", "throw", "heartbeat"] }],
1710
- )
1711
-
1712
- // ── Run request types ──
1713
-
1714
- addType(root, "ParameterValue", [
1715
- { id: 1, name: "id", type: "string" },
1716
- { id: 2, name: "value", type: "string" },
1717
- ])
1718
-
1719
- addType(root, "RequestedModel", [
1720
- { id: 1, name: "model_id", type: "string" },
1721
- { id: 2, name: "max_mode", type: "bool" },
1722
- { id: 3, name: "parameters", type: "ParameterValue", repeated: true },
1723
- ])
1724
-
1725
- addType(root, "SelectedImageDimension", [
1726
- { id: 1, name: "width", type: "int32" },
1727
- { id: 2, name: "height", type: "int32" },
1728
- ])
1729
- addType(root, "SelectedImageBlobIdWithData", [
1730
- { id: 1, name: "blob_id", type: "bytes" },
1731
- { id: 2, name: "data", type: "bytes" },
1732
- ])
1733
- addType(
1734
- root,
1735
- "SelectedImage",
1736
- [
1737
- { id: 1, name: "blob_id", type: "bytes" },
1738
- { id: 8, name: "data", type: "bytes" },
1739
- { id: 9, name: "blob_id_with_data", type: "SelectedImageBlobIdWithData" },
1740
- { id: 2, name: "uuid", type: "string" },
1741
- { id: 3, name: "path", type: "string" },
1742
- { id: 4, name: "dimension", type: "SelectedImageDimension" },
1743
- { id: 7, name: "mime_type", type: "string" },
1744
- ],
1745
- [{ name: "data_or_blob_id", fields: ["blob_id", "data", "blob_id_with_data"] }],
1746
- )
1747
- addType(root, "SelectedContext", [
1748
- { id: 1, name: "selected_images", type: "SelectedImage", repeated: true },
1749
- ])
1750
-
1751
- // agent.v1.AgentMode — the client's current mode, sent on every user message.
1752
- root.add(new protobuf.Enum("AgentMode", {
1753
- AGENT_MODE_UNSPECIFIED: 0,
1754
- AGENT_MODE_AGENT: 1,
1755
- AGENT_MODE_ASK: 2,
1756
- AGENT_MODE_PLAN: 3,
1757
- AGENT_MODE_DEBUG: 4,
1758
- AGENT_MODE_TRIAGE: 5,
1759
- AGENT_MODE_PROJECT: 6,
1760
- AGENT_MODE_MULTITASK: 7,
1761
- AGENT_MODE_CUSTOM: 8,
1762
- }))
1763
-
1764
- addType(root, "UserMessage", [
1765
- { id: 1, name: "text", type: "string" },
1766
- { id: 2, name: "message_id", type: "string" },
1767
- { id: 3, name: "selected_context", type: "SelectedContext" },
1768
- { id: 4, name: "mode", type: "AgentMode" },
1769
- ])
1770
-
1771
- // RequestContext — UserMessageAction #2. Slim mcp_meta_tool_options names
1772
- // the advertised catalog; AgentRunRequest.mcp_tools (#4) is empty on real turns.
1773
- addType(root, "RequestContext", [
1774
- { id: 2, name: "rules", type: "CursorRule", repeated: true },
1775
- { id: 4, name: "env", type: "RequestContextEnv" },
1776
- { id: 6, name: "repository_info", type: "RepositoryIndexingInfo", repeated: true },
1777
- { id: 7, name: "tools", type: "McpToolDefinition", repeated: true },
1778
- { id: 11, name: "git_repos", type: "GitRepoInfo", repeated: true },
1779
- { id: 13, name: "project_layouts", type: "LsDirectoryTreeNode", repeated: true },
1780
- { id: 17, name: "web_search_enabled", type: "bool" },
1781
- { id: 22, name: "custom_subagents", type: "CustomSubagent", repeated: true },
1782
- { id: 23, name: "mcp_file_system_options", type: "McpFileSystemOptions" },
1783
- { id: 24, name: "web_fetch_enabled", type: "bool" },
1784
- { id: 25, name: "hooks_additional_context", type: "string" },
1785
- { id: 29, name: "agent_skills", type: "AgentSkill", repeated: true },
1786
- { id: 33, name: "git_repo_info_complete", type: "bool" },
1787
- { id: 34, name: "mcp_meta_tool_options", type: "McpMetaToolOptions" },
1788
- { id: 36, name: "mcp_info_complete", type: "bool" },
1789
- { id: 39, name: "rules_info_complete", type: "bool" },
1790
- { id: 40, name: "env_info_complete", type: "bool" },
1791
- { id: 41, name: "repository_info_complete", type: "bool" },
1792
- { id: 42, name: "custom_subagents_info_complete", type: "bool" },
1793
- { id: 43, name: "agent_skills_info_complete", type: "bool" },
1794
- { id: 44, name: "mcp_file_system_info_complete", type: "bool" },
1795
- { id: 45, name: "git_status_info_complete", type: "bool" },
1796
- { id: 46, name: "user_permissions_auto_run", type: "PermissionsAutoRunInstructions" },
1797
- { id: 47, name: "project_permissions_auto_run", type: "PermissionsAutoRunInstructions" },
1798
- ])
1799
-
1800
- addType(root, "UserMessageAction", [
1801
- { id: 1, name: "user_message", type: "UserMessage" },
1802
- { id: 2, name: "request_context", type: "RequestContext" },
1803
- ])
1804
-
1805
- addType(root, "ResumeAction", [
1806
- // Cursor CLI sends an empty ResumeAction. Field #2 is available for a
1807
- // refreshed RequestContext; the conversation id belongs to AgentRunRequest.
1808
- { id: 2, name: "request_context", type: "RequestContext" },
1809
- ])
1810
-
1811
- addType(root, "CancelAction", [
1812
- // Cursor CLI CancelAction field #1 is the interruption reason, not an id.
1813
- { id: 1, name: "reason", type: "string" },
1814
- ])
1815
-
1816
- // Deferred answers for an AskQuestion the client already replied to with
1817
- // `async`. `original_args` is kept as raw bytes so the exact AskQuestionArgs
1818
- // Cursor sent — including fields this schema does not model — is echoed back
1819
- // verbatim rather than re-encoded from a lossy decode.
1820
- addType(root, "AsyncAskQuestionCompletionAction", [
1821
- { id: 1, name: "original_tool_call_id", type: "string" },
1822
- { id: 2, name: "original_args", type: "bytes" },
1823
- { id: 3, name: "result", type: "AskQuestionResult" },
1824
- ])
1825
-
1826
- // Cursor CLI 2026.10.01 descriptors: system context is
1827
- // injected into the current Run without submitting another user turn.
1828
- addType(root, "SystemContextInjection", [
1829
- { id: 1, name: "producer", type: "string" },
1830
- { id: 2, name: "content", type: "string" },
1831
- ])
1832
- // Cursor SDK `Run.steer` / CLI steering payload: a user message added to the
1833
- // running turn. `request_context` is optional and unused by the SDK.
1834
- addType(root, "UserContextInjection", [
1835
- { id: 1, name: "user_message", type: "UserMessage" },
1836
- { id: 2, name: "request_context", type: "RequestContext" },
1837
- ])
1838
- addType(root, "InjectContextAction", [
1839
- { id: 1, name: "injection_id", type: "string" },
1840
- { id: 2, name: "expected_run_id", type: "string" },
1841
- { id: 3, name: "user_context", type: "UserContextInjection" },
1842
- { id: 4, name: "system_context", type: "SystemContextInjection" },
1843
- ])
1844
-
1845
- addType(
1846
- root,
1847
- "ConversationAction",
1848
- [
1849
- { id: 1, name: "user_message_action", type: "UserMessageAction" },
1850
- { id: 2, name: "resume_action", type: "ResumeAction" },
1851
- { id: 3, name: "cancel_action", type: "CancelAction" },
1852
- { id: 8, name: "async_ask_question_completion_action", type: "AsyncAskQuestionCompletionAction" },
1853
- { id: 19, name: "inject_context_action", type: "InjectContextAction" },
1854
- ],
1855
- [{
1856
- name: "action",
1857
- fields: [
1858
- "user_message_action",
1859
- "resume_action",
1860
- "cancel_action",
1861
- "async_ask_question_completion_action",
1862
- "inject_context_action",
1863
- ],
1864
- }],
1865
- )
1866
-
1867
- // Seed ConversationStateStructure for turn 1 (system prompt as JSON strings
1868
- // in #1). After the first conversation_checkpoint_update we echo opaque
1869
- // server bytes instead — CLI's live structure uses blob-id bytes in #1/#8.
1870
- addType(root, "AssistantMessage", [
1871
- { id: 1, name: "text", type: "string" },
1872
- ])
1873
-
1874
- addType(
1875
- root,
1876
- "ConversationStep",
1877
- [
1878
- { id: 1, name: "assistant_message", type: "AssistantMessage" },
1879
- ],
1880
- [{ name: "message", fields: ["assistant_message"] }],
1881
- )
1882
-
1883
- addType(root, "AgentConversationTurn", [
1884
- { id: 1, name: "user_message", type: "UserMessage" },
1885
- { id: 2, name: "steps", type: "ConversationStep", repeated: true },
1886
- ])
1887
-
1888
- addType(
1889
- root,
1890
- "ConversationTurn",
1891
- [
1892
- { id: 1, name: "agent_conversation_turn", type: "AgentConversationTurn" },
1893
- ],
1894
- [{ name: "turn", fields: ["agent_conversation_turn"] }],
1895
- )
1896
-
1897
- // Cursor checkpoints carry authoritative context occupancy in field #5. The
1898
- // provider still transports the complete checkpoint as opaque bytes; these
1899
- // declarations support targeted diagnostics/tests without a re-encode.
1900
- addType(root, "PromptTokenBreakdownCategory", [
1901
- { id: 1, name: "id", type: "string" },
1902
- { id: 2, name: "label", type: "string" },
1903
- { id: 3, name: "estimated_tokens", type: "uint32" },
1904
- { id: 4, name: "character_count", type: "uint32" },
1905
- ])
1906
- addType(root, "PromptTokenBreakdownSnapshot", [
1907
- { id: 1, name: "total_used_tokens", type: "uint32" },
1908
- { id: 2, name: "max_tokens", type: "uint32" },
1909
- { id: 3, name: "categories", type: "PromptTokenBreakdownCategory", repeated: true },
1910
- ])
1911
- addType(root, "ConversationTokenDetails", [
1912
- { id: 1, name: "used_tokens", type: "uint32" },
1913
- { id: 2, name: "max_tokens", type: "uint32" },
1914
- { id: 3, name: "breakdown", type: "PromptTokenBreakdownSnapshot" },
1915
- ])
1916
-
1917
- // Seed schema (JSON strings). AgentRunRequest.conversation_state remains
1918
- // bytes, so live checkpoints are never decoded/re-encoded on transport.
1919
- addType(root, "ConversationStateStructure", [
1920
- { id: 1, name: "root_prompt_messages_json", type: "string", repeated: true },
1921
- { id: 5, name: "token_details", type: "ConversationTokenDetails" },
1922
- { id: 8, name: "turns", type: "ConversationTurn", repeated: true },
1923
- ])
1924
-
1925
- addType(root, "ModelDetails", [
1926
- { id: 1, name: "model_id", type: "string" },
1927
- ])
1928
-
1929
- addType(root, "McpTools", [
1930
- { id: 1, name: "mcp_tools", type: "McpToolDefinition", repeated: true },
1931
- ])
1932
-
1933
- addType(root, "AgentRunRequest", [
1934
- { id: 1, name: "conversation_state", type: "bytes" },
1935
- { id: 2, name: "action", type: "ConversationAction" },
1936
- { id: 4, name: "mcp_tools", type: "McpTools" },
1937
- { id: 5, name: "conversation_id", type: "string" },
1938
- { id: 8, name: "custom_system_prompt", type: "string" },
1939
- { 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
- ])
1946
-
1947
- // ── Client heartbeat ──
1948
-
1949
- addType(root, "ClientHeartbeat", [])
1950
-
1951
- // ── Interaction query/response channel ──
1952
- // Cursor sends UI/integration requests outside the exec tool channel. Query
1953
- // bodies stay opaque: this headless provider only needs their id + variant
1954
- // to return a conservative typed response on the same Run stream.
1955
- addType(
1956
- root,
1957
- "InteractionQuery",
1958
- [
1959
- { id: 1, name: "id", type: "uint32" },
1960
- { id: 2, name: "web_search_request_query", type: "bytes" },
1961
- { id: 3, name: "ask_question_interaction_query", type: "bytes" },
1962
- { id: 4, name: "switch_mode_request_query", type: "bytes" },
1963
- { id: 7, name: "create_plan_request_query", type: "bytes" },
1964
- { id: 8, name: "setup_vm_environment_args", type: "bytes" },
1965
- { id: 9, name: "web_fetch_request_query", type: "bytes" },
1966
- { id: 10, name: "pr_management_request_query", type: "bytes" },
1967
- { id: 11, name: "mcp_auth_request_query", type: "bytes" },
1968
- { id: 12, name: "generate_image_request_query", type: "bytes" },
1969
- { id: 13, name: "replace_env_args", type: "bytes" },
1970
- { id: 14, name: "connect_scm_request_query", type: "bytes" },
1971
- ],
1972
- [{
1973
- name: "query",
1974
- fields: [
1975
- "web_search_request_query", "ask_question_interaction_query",
1976
- "switch_mode_request_query", "create_plan_request_query",
1977
- "setup_vm_environment_args", "web_fetch_request_query",
1978
- "pr_management_request_query", "mcp_auth_request_query",
1979
- "generate_image_request_query", "replace_env_args",
1980
- "connect_scm_request_query",
1981
- ],
1982
- }],
1983
- )
1984
-
1985
- addType(root, "WebSearchRequestApproved", [])
1986
- addType(root, "WebSearchRequestRejected", [{ id: 1, name: "reason", type: "string" }])
1987
- addType(root, "WebSearchRequestResponse", [
1988
- { id: 1, name: "approved", type: "WebSearchRequestApproved" },
1989
- { id: 2, name: "rejected", type: "WebSearchRequestRejected" },
1990
- ], [{ name: "result", fields: ["approved", "rejected"] }])
1991
-
1992
- addType(root, "AskQuestionRejected", [{ id: 1, name: "reason", type: "string" }])
1993
- addType(root, "AskQuestionError", [{ id: 1, name: "error_message", type: "string" }])
1994
- addType(root, "AskQuestionAsync", [])
1995
- addType(root, "AskQuestionSuccessAnswer", [
1996
- { id: 1, name: "question_id", type: "string" },
1997
- { id: 2, name: "selected_option_ids", type: "string", repeated: true },
1998
- { id: 3, name: "freeform_text", type: "string" },
1999
- ])
2000
- addType(root, "AskQuestionSuccess", [
2001
- { id: 1, name: "answers", type: "AskQuestionSuccessAnswer", repeated: true },
2002
- ])
2003
- addType(root, "AskQuestionResult", [
2004
- { id: 1, name: "success", type: "AskQuestionSuccess" },
2005
- { id: 2, name: "error", type: "AskQuestionError" },
2006
- { id: 3, name: "rejected", type: "AskQuestionRejected" },
2007
- { id: 4, name: "async", type: "AskQuestionAsync" },
2008
- ], [{ name: "result", fields: ["success", "error", "rejected", "async"] }])
2009
- addType(root, "AskQuestionInteractionResponse", [
2010
- { id: 1, name: "result", type: "AskQuestionResult" },
2011
- ])
2012
- // InteractionQuery keeps query bodies opaque (see the InteractionQuery
2013
- // comment); this type decodes only the ask-question body on demand.
2014
- addType(root, "AskQuestionInteractionQuery", [
2015
- { id: 1, name: "args", type: "AskQuestionArgs" },
2016
- { id: 2, name: "tool_call_id", type: "string" },
2017
- ])
2018
-
2019
- addType(root, "SwitchModeRequestApproved", [])
2020
- addType(root, "SwitchModeRequestRejected", [{ id: 1, name: "reason", type: "string" }])
2021
- addType(root, "SwitchModeRequestResponse", [
2022
- { id: 1, name: "approved", type: "SwitchModeRequestApproved" },
2023
- { id: 2, name: "rejected", type: "SwitchModeRequestRejected" },
2024
- ], [{ name: "result", fields: ["approved", "rejected"] }])
2025
- // InteractionQuery keeps query bodies opaque; decoded on demand like
2026
- // AskQuestionInteractionQuery / CreatePlanRequestQuery.
2027
- addType(root, "SwitchModeRequestQuery", [
2028
- { id: 1, name: "args", type: "SwitchModeToolArgs" },
2029
- ])
2030
-
2031
- addType(root, "CreatePlanSuccess", [])
2032
- addType(root, "CreatePlanError", [{ id: 1, name: "error", type: "string" }])
2033
- addType(root, "CreatePlanResult", [
2034
- { id: 1, name: "success", type: "CreatePlanSuccess" },
2035
- { id: 2, name: "error", type: "CreatePlanError" },
2036
- { id: 3, name: "plan_uri", type: "string" },
2037
- ], [{ name: "result", fields: ["success", "error"] }])
2038
- addType(root, "CreatePlanRequestResponse", [
2039
- { id: 1, name: "result", type: "CreatePlanResult" },
2040
- ])
2041
- // InteractionQuery keeps query bodies opaque; decoded on demand like
2042
- // AskQuestionInteractionQuery / GenerateImageRequestQuery.
2043
- addType(root, "CreatePlanRequestQuery", [
2044
- { id: 1, name: "args", type: "CreatePlanArgs" },
2045
- { id: 2, name: "tool_call_id", type: "string" },
2046
- ])
2047
-
2048
- addType(root, "SetupVmEnvironmentSuccess", [])
2049
- addType(root, "SetupVmEnvironmentResult", [
2050
- { id: 1, name: "success", type: "SetupVmEnvironmentSuccess" },
2051
- ], [{ name: "result", fields: ["success"] }])
2052
-
2053
- addType(root, "WebFetchRequestApproved", [])
2054
- addType(root, "WebFetchRequestRejected", [{ id: 1, name: "reason", type: "string" }])
2055
- addType(root, "WebFetchRequestResponse", [
2056
- { id: 1, name: "approved", type: "WebFetchRequestApproved" },
2057
- { id: 2, name: "rejected", type: "WebFetchRequestRejected" },
2058
- ], [{ name: "result", fields: ["approved", "rejected"] }])
2059
-
2060
- addType(root, "PrManagementRejected", [{ id: 1, name: "reason", type: "string" }])
2061
- addType(root, "PrManagementResult", [
2062
- { id: 3, name: "rejected", type: "PrManagementRejected" },
2063
- ], [{ name: "result", fields: ["rejected"] }])
2064
-
2065
- addType(root, "McpAuthRequestApproved", [])
2066
- addType(root, "McpAuthRequestRejected", [{ id: 1, name: "reason", type: "string" }])
2067
- addType(root, "McpAuthRequestResponse", [
2068
- { id: 1, name: "approved", type: "McpAuthRequestApproved" },
2069
- { id: 2, name: "rejected", type: "McpAuthRequestRejected" },
2070
- ], [{ name: "result", fields: ["approved", "rejected"] }])
2071
-
2072
- addType(root, "GenerateImageRequestApproved", [{ id: 1, name: "description", type: "string" }])
2073
- addType(root, "GenerateImageRequestRejected", [{ id: 1, name: "reason", type: "string" }])
2074
- addType(root, "GenerateImageRequestResponse", [
2075
- { id: 1, name: "approved", type: "GenerateImageRequestApproved" },
2076
- { id: 2, name: "rejected", type: "GenerateImageRequestRejected" },
2077
- ], [{ name: "result", fields: ["approved", "rejected"] }])
2078
-
2079
- addType(root, "ReplaceEnvSuccess", [])
2080
- addType(root, "ReplaceEnvFailure", [
2081
- { id: 1, name: "error_message", type: "string" },
2082
- { id: 2, name: "setup_logs", type: "string" },
2083
- ])
2084
- addType(root, "ReplaceEnvResult", [
2085
- { id: 1, name: "success", type: "ReplaceEnvSuccess" },
2086
- { id: 2, name: "failure", type: "ReplaceEnvFailure" },
2087
- ], [{ name: "result", fields: ["success", "failure"] }])
2088
-
2089
- addType(root, "ConnectScmRequestApproved", [])
2090
- addType(root, "ConnectScmRequestRejected", [{ id: 1, name: "reason", type: "string" }])
2091
- addType(root, "ConnectScmRequestFailed", [{ id: 1, name: "error", type: "string" }])
2092
- addType(root, "ConnectScmRequestResponse", [
2093
- { id: 1, name: "approved", type: "ConnectScmRequestApproved" },
2094
- { id: 2, name: "rejected", type: "ConnectScmRequestRejected" },
2095
- { id: 3, name: "failed", type: "ConnectScmRequestFailed" },
2096
- ], [{ name: "result", fields: ["approved", "rejected", "failed"] }])
2097
-
2098
- addType(
2099
- root,
2100
- "InteractionResponse",
2101
- [
2102
- { id: 1, name: "id", type: "uint32" },
2103
- { id: 2, name: "web_search_request_response", type: "WebSearchRequestResponse" },
2104
- { id: 3, name: "ask_question_interaction_response", type: "AskQuestionInteractionResponse" },
2105
- { id: 4, name: "switch_mode_request_response", type: "SwitchModeRequestResponse" },
2106
- { id: 7, name: "create_plan_request_response", type: "CreatePlanRequestResponse" },
2107
- { id: 8, name: "setup_vm_environment_result", type: "SetupVmEnvironmentResult" },
2108
- { id: 9, name: "web_fetch_request_response", type: "WebFetchRequestResponse" },
2109
- { id: 10, name: "pr_management_result", type: "PrManagementResult" },
2110
- { id: 11, name: "mcp_auth_request_response", type: "McpAuthRequestResponse" },
2111
- { id: 12, name: "generate_image_request_response", type: "GenerateImageRequestResponse" },
2112
- { id: 13, name: "replace_env_result", type: "ReplaceEnvResult" },
2113
- { id: 14, name: "connect_scm_request_response", type: "ConnectScmRequestResponse" },
2114
- ],
2115
- [{
2116
- name: "result",
2117
- fields: [
2118
- "web_search_request_response", "ask_question_interaction_response",
2119
- "switch_mode_request_response", "create_plan_request_response",
2120
- "setup_vm_environment_result", "web_fetch_request_response",
2121
- "pr_management_result", "mcp_auth_request_response",
2122
- "generate_image_request_response", "replace_env_result",
2123
- "connect_scm_request_response",
2124
- ],
2125
- }],
2126
- )
2127
-
2128
- // ── KV blob store (cursor moves large payloads out-of-band via this channel) ──
2129
- // Server sends KvServerMessage (AgentServerMessage #4); client MUST reply with
2130
- // KvClientMessage (AgentClientMessage #3) on the same Run stream, echoing `id`.
2131
- // If we don't ack set_blob / answer get_blob, the server hangs (endless
2132
- // heartbeats, no response) — this was the "no response" root cause.
2133
-
2134
- addType(root, "GetBlobArgs", [
2135
- { id: 1, name: "blob_id", type: "bytes" },
2136
- ])
2137
- addType(root, "GetBlobResult", [
2138
- { id: 1, name: "blob_data", type: "bytes" },
2139
- ])
2140
- addType(root, "SetBlobArgs", [
2141
- { id: 1, name: "blob_id", type: "bytes" },
2142
- { id: 2, name: "blob_data", type: "bytes" },
2143
- ])
2144
- addType(root, "SetBlobResult", [
2145
- { id: 1, name: "error", type: "string" },
2146
- ])
2147
- addType(
2148
- root,
2149
- "KvServerMessage",
2150
- [
2151
- { id: 1, name: "id", type: "uint32" },
2152
- { id: 2, name: "get_blob_args", type: "GetBlobArgs" },
2153
- { id: 3, name: "set_blob_args", type: "SetBlobArgs" },
2154
- ],
2155
- [{ name: "message", fields: ["get_blob_args", "set_blob_args"] }],
2156
- )
2157
- addType(
2158
- root,
2159
- "KvClientMessage",
2160
- [
2161
- { id: 1, name: "id", type: "uint32" },
2162
- { id: 2, name: "get_blob_result", type: "GetBlobResult" },
2163
- { id: 3, name: "set_blob_result", type: "SetBlobResult" },
2164
- ],
2165
- [{ name: "message", fields: ["get_blob_result", "set_blob_result"] }],
2166
- )
2167
-
2168
- // ── AgentClientMessage ──
2169
-
2170
- addType(
2171
- root,
2172
- "AgentClientMessage",
2173
- [
2174
- { id: 1, name: "run_request", type: "AgentRunRequest" },
2175
- { id: 2, name: "exec_client_message", type: "ExecClientMessage" },
2176
- { id: 3, name: "kv_client_message", type: "KvClientMessage" },
2177
- { id: 4, name: "conversation_action", type: "ConversationAction" },
2178
- { id: 5, name: "exec_client_control_message", type: "ExecClientControlMessage" },
2179
- { id: 6, name: "interaction_response", type: "InteractionResponse" },
2180
- { id: 7, name: "client_heartbeat", type: "ClientHeartbeat" },
2181
- ],
2182
- [{
2183
- name: "message",
2184
- fields: [
2185
- "run_request",
2186
- "exec_client_message",
2187
- "kv_client_message",
2188
- "conversation_action",
2189
- "exec_client_control_message",
2190
- "interaction_response",
2191
- "client_heartbeat",
2192
- ],
2193
- }],
2194
- )
2195
-
2196
- // ── AgentServerMessage ──
2197
-
2198
- addType(
2199
- root,
2200
- "AgentServerMessage",
2201
- [
2202
- { id: 1, name: "interaction_update", type: "InteractionUpdate" },
2203
- { id: 2, name: "exec_server_message", type: "ExecServerMessage" },
2204
- { id: 3, name: "conversation_checkpoint_update", type: "bytes" },
2205
- { id: 4, name: "kv_server_message", type: "KvServerMessage" },
2206
- { id: 5, name: "exec_server_control_message", type: "ExecServerControlMessage" },
2207
- { id: 7, name: "interaction_query", type: "InteractionQuery" },
2208
- ],
2209
- [{ name: "message", fields: ["interaction_update", "exec_server_message", "conversation_checkpoint_update", "kv_server_message", "exec_server_control_message", "interaction_query"] }],
2210
- )
2211
-
2212
- // ── AvailableModels types ──
2213
-
2214
- addType(root, "AvailableModelsRequest", [])
2215
-
2216
- addType(root, "AvailableModelParameterDefinition", [
2217
- { id: 1, name: "id", type: "string" },
2218
- { id: 2, name: "values", type: "string", repeated: true },
2219
- ])
2220
-
2221
- addType(root, "AvailableModelParameterValue", [
2222
- { id: 1, name: "id", type: "string" },
2223
- { id: 2, name: "value", type: "string" },
2224
- ])
2225
-
2226
- addType(root, "AvailableModelVariant", [
2227
- { id: 1, name: "parameter_values", type: "AvailableModelParameterValue", repeated: true },
2228
- { id: 2, name: "display_name", type: "string" },
2229
- { id: 3, name: "is_max_mode", type: "bool" },
2230
- { id: 4, name: "is_default_max_config", type: "bool" },
2231
- { id: 5, name: "is_default_non_max_config", type: "bool" },
2232
- ])
2233
-
2234
- addType(root, "AvailableModelEntry", [
2235
- { id: 1, name: "name", type: "string" },
2236
- { id: 2, name: "default_on", type: "bool" },
2237
- { id: 5, name: "supports_agent", type: "bool" },
2238
- { id: 9, name: "supports_thinking", type: "bool" },
2239
- { id: 10, name: "supports_images", type: "bool" },
2240
- { id: 14, name: "supports_max_mode", type: "bool" },
2241
- { id: 15, name: "context_token_limit", type: "int32" },
2242
- { id: 16, name: "context_token_limit_for_max_mode", type: "int32" },
2243
- { id: 17, name: "client_display_name", type: "string" },
2244
- { id: 18, name: "server_model_name", type: "string" },
2245
- { id: 29, name: "parameter_definitions", type: "AvailableModelParameterDefinition", repeated: true },
2246
- { id: 30, name: "variants", type: "AvailableModelVariant", repeated: true },
2247
- ])
2248
-
2249
- addType(root, "AvailableModelsResponse", [
2250
- { id: 1, name: "model_names", type: "string", repeated: true },
2251
- { id: 2, name: "models", type: "AvailableModelEntry", repeated: true },
2252
- ])
2253
-
2254
- return root
2255
- }
2256
-
2257
- // ── Singleton root ──
2258
-
2259
- let _root: protobuf.Root | null = null
2260
-
2261
- export function getMessageTypes(): protobuf.Root {
2262
- if (!_root) {
2263
- _root = createMessageTypes()
2264
- }
2265
- return _root
2266
- }
2267
-
2268
- export function encodeMessage(typeName: string, message: Record<string, unknown>): Uint8Array {
2269
- const root = getMessageTypes()
2270
- const type = root.lookupType(typeName)
2271
- const err = type.verify(message)
2272
- if (err) throw new Error(`Invalid message for ${typeName}: ${err}`)
2273
- return type.encode(type.fromObject(message)).finish()
2274
- }
2275
-
2276
- export function decodeMessage<T = Record<string, unknown>>(
2277
- typeName: string,
2278
- data: Uint8Array,
2279
- ): T {
2280
- const root = getMessageTypes()
2281
- const type = root.lookupType(typeName)
2282
- const decoded = type.decode(data)
2283
- return type.toObject(decoded, { defaults: true, json: true, longs: Number }) as T
2284
- }
2285
-
2286
- /** Decode without inventing absent proto defaults (used for byte-stable snapshots). */
2287
- export function decodeMessageSparse<T = Record<string, unknown>>(
2288
- typeName: string,
2289
- data: Uint8Array,
2290
- ): T {
2291
- const root = getMessageTypes()
2292
- const type = root.lookupType(typeName)
2293
- const decoded = type.decode(data)
2294
- return type.toObject(decoded, { json: true, longs: Number }) as T
2295
- }
2296
-
2297
- type RawField = {
2298
- field: number
2299
- wireType: number
2300
- varint?: string
2301
- fixed?: number | string
2302
- bytes?: Uint8Array
2303
- }
2304
-
2305
- /** Schema-free walk of one message's top-level wire fields (no nested descent). */
2306
- function readRawFields(buf: Uint8Array): RawField[] {
2307
- const reader = protobuf.Reader.create(buf)
2308
- const fields: RawField[] = []
2309
- while (reader.pos < reader.len) {
2310
- const tag = reader.uint32()
2311
- const field = tag >>> 3
2312
- const wireType = tag & 7
2313
- switch (wireType) {
2314
- case 0:
2315
- fields.push({ field, wireType, varint: reader.uint64().toString() })
2316
- break
2317
- case 1:
2318
- fields.push({ field, wireType, fixed: reader.fixed64().toString() })
2319
- break
2320
- case 2:
2321
- fields.push({ field, wireType, bytes: reader.bytes() })
2322
- break
2323
- case 5:
2324
- fields.push({ field, wireType, fixed: reader.fixed32() })
2325
- break
2326
- default:
2327
- // Groups (3/4) aren't used on this wire; bail rather than desync the reader.
2328
- return fields
2329
- }
2330
- }
2331
- return fields
2332
- }
2333
-
2334
- /**
2335
- * Diagnostic-only: walk AgentServerMessage.interaction_update(1).turn_ended(14)
2336
- * without going through our hand-written TurnEnded schema, so a `cache_write`/
2337
- * `reasoning_tokens` question can be checked against the raw wire instead of
2338
- * guessed. Confirmed field ids 1-5 (input/output/cache_read/cache_write/
2339
- * reasoning) decode correctly. Live captures routinely include `f4` explicitly
2340
- * as `0` while `f3` (cache_read) and `f5` (reasoning) are non-zero — Cursor's
2341
- * agent TurnEnded populates the field but does not report write counts here.
2342
- */
2343
- export function debugWalkTurnEnded(payload: Uint8Array): string {
2344
- try {
2345
- const top = readRawFields(payload)
2346
- const iuField = top.find((f) => f.field === 1 && f.wireType === 2)
2347
- if (!iuField?.bytes) return "(no interaction_update field)"
2348
- const iu = readRawFields(iuField.bytes)
2349
- const teField = iu.find((f) => f.field === 14 && f.wireType === 2)
2350
- if (!teField?.bytes) return "(no turn_ended field in interaction_update)"
2351
- const te = readRawFields(teField.bytes)
2352
- return te
2353
- .map(
2354
- (f) =>
2355
- `f${f.field}:wt${f.wireType}=${f.varint ?? f.fixed ?? (f.bytes ? `bytes[${f.bytes.length}]` : "?")}`,
2356
- )
2357
- .join(" ")
2358
- } catch (e) {
2359
- return `(walk failed: ${(e as Error).message})`
2360
- }
2361
- }
2362
-
2363
- /**
2364
- * Decode a protobuf sub-message from a frame payload that wraps it in
2365
- * a top-level field key + length varint. Skips the outer wrapper and
2366
- * decodes the inner body as `typeName`.
2367
- */
2368
- export function decodeWrappedMessage<T = Record<string, unknown>>(
2369
- typeName: string,
2370
- data: Uint8Array,
2371
- ): T {
2372
- let offset = 1 // skip field key
2373
- // skip varint length
2374
- while (offset < data.length) {
2375
- const b = data[offset] ?? 0
2376
- offset++
2377
- if (!(b & 0x80)) break
2378
- }
2379
- return decodeMessage<T>(typeName, data.subarray(offset))
2380
- }
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
+ 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
+ }
50
+
51
+ // ── Utility messages ──
52
+
53
+ export function createMessageTypes(): protobuf.Root {
54
+ const root = new protobuf.Root()
55
+
56
+ addType(root, "TextDeltaUpdate", [
57
+ { id: 1, name: "text", type: "string" },
58
+ ])
59
+
60
+ addType(root, "ThinkingDeltaUpdate", [
61
+ { id: 1, name: "text", type: "string" },
62
+ ])
63
+
64
+ addType(root, "TurnEnded", [
65
+ { id: 1, name: "input_tokens", type: "uint32" },
66
+ { id: 2, name: "output_tokens", type: "uint32" },
67
+ { id: 3, name: "cache_read", type: "uint32" },
68
+ { id: 4, name: "cache_write", type: "uint32" },
69
+ { id: 5, name: "reasoning_tokens", type: "uint32" },
70
+ ])
71
+
72
+ addType(root, "Heartbeat", [])
73
+
74
+ // Display ToolCall (interaction_update.tool_call_*) — agent.v1 oneof, not
75
+ // {tool_name,args} strings. Decode finalized todo state for mirroring and
76
+ // MCP errors for diagnostics; other completion results are not replayed.
77
+ root.add(new protobuf.Enum("TodoStatus", {
78
+ TODO_STATUS_UNSPECIFIED: 0,
79
+ TODO_STATUS_PENDING: 1,
80
+ TODO_STATUS_IN_PROGRESS: 2,
81
+ TODO_STATUS_COMPLETED: 3,
82
+ TODO_STATUS_CANCELLED: 4,
83
+ }))
84
+ addType(root, "TodoItem", [
85
+ { id: 1, name: "id", type: "string" },
86
+ { id: 2, name: "content", type: "string" },
87
+ { id: 3, name: "status", type: "TodoStatus" },
88
+ { id: 4, name: "created_at", type: "int64" },
89
+ { id: 5, name: "updated_at", type: "int64" },
90
+ { id: 6, name: "dependencies", type: "string", repeated: true },
91
+ ])
92
+ addType(root, "UpdateTodosArgs", [
93
+ { id: 1, name: "todos", type: "TodoItem", repeated: true },
94
+ { id: 2, name: "merge", type: "bool" },
95
+ ])
96
+ addType(root, "UpdateTodosSuccess", [
97
+ { id: 1, name: "todos", type: "TodoItem", repeated: true },
98
+ { id: 2, name: "total_count", type: "int32" },
99
+ { id: 3, name: "was_merge", type: "bool" },
100
+ ])
101
+ addType(root, "UpdateTodosError", [{ id: 1, name: "error", type: "string" }])
102
+ addType(
103
+ root,
104
+ "UpdateTodosResult",
105
+ [
106
+ { id: 1, name: "success", type: "UpdateTodosSuccess" },
107
+ { id: 2, name: "error", type: "UpdateTodosError" },
108
+ ],
109
+ [{ name: "result", fields: ["success", "error"] }],
110
+ )
111
+ addType(root, "UpdateTodosToolCall", [
112
+ { id: 1, name: "args", type: "UpdateTodosArgs" },
113
+ { id: 2, name: "result", type: "UpdateTodosResult" },
114
+ ])
115
+ addType(root, "ReadToolArgs", [
116
+ { id: 1, name: "path", type: "string" },
117
+ { id: 2, name: "offset", type: "int32" },
118
+ { id: 3, name: "limit", type: "int32" },
119
+ ])
120
+ addType(root, "ReadToolCall", [{ id: 1, name: "args", type: "ReadToolArgs" }])
121
+ addType(root, "ShellToolCall", [{ id: 1, name: "args", type: "ShellArgs" }])
122
+ addType(root, "DeleteToolCall", [{ id: 1, name: "args", type: "DeleteArgs" }])
123
+ addType(root, "GlobToolCall", [{ id: 1, name: "args", type: "GlobArgs" }])
124
+ addType(root, "GrepToolCall", [{ id: 1, name: "args", type: "GrepArgs" }])
125
+ addType(root, "EditToolArgs", [
126
+ { id: 1, name: "path", type: "string" },
127
+ { id: 6, name: "stream_content", type: "string" },
128
+ ])
129
+ addType(root, "EditToolCall", [{ id: 1, name: "args", type: "EditToolArgs" }])
130
+ addType(root, "LsToolCall", [{ id: 1, name: "args", type: "LsArgs" }])
131
+ addType(root, "McpToolCall", [
132
+ { id: 1, name: "args", type: "McpArgs" },
133
+ { id: 2, name: "result", type: "McpResult" },
134
+ ])
135
+ addType(root, "CreatePlanArgs", [
136
+ { id: 1, name: "plan", type: "string" },
137
+ { id: 2, name: "todos", type: "TodoItem", repeated: true },
138
+ { id: 3, name: "overview", type: "string" },
139
+ { id: 4, name: "name", type: "string" },
140
+ { id: 5, name: "is_project", type: "bool" },
141
+ ])
142
+ addType(root, "CreatePlanToolCall", [{ id: 1, name: "args", type: "CreatePlanArgs" }])
143
+ addType(root, "WebSearchToolArgs", [
144
+ { id: 1, name: "search_term", type: "string" },
145
+ { id: 2, name: "tool_call_id", type: "string" },
146
+ ])
147
+ addType(root, "WebSearchToolCall", [{ id: 1, name: "args", type: "WebSearchToolArgs" }])
148
+ addType(root, "SubagentTypeUnspecified", [])
149
+ addType(root, "SubagentTypeComputerUse", [])
150
+ addType(root, "SubagentTypeCustom", [{ id: 1, name: "name", type: "string" }])
151
+ addType(root, "SubagentTypeExplore", [])
152
+ addType(root, "SubagentTypeMediaReview", [])
153
+ addType(root, "SubagentTypeBash", [])
154
+ addType(root, "SubagentTypeBrowserUse", [])
155
+ addType(root, "SubagentTypeShell", [])
156
+ addType(root, "SubagentTypeVmSetupHelper", [])
157
+ addType(root, "SubagentTypeDebug", [])
158
+ addType(root, "SubagentTypeCursorGuide", [])
159
+ addType(root, "SubagentTypeWatchVideo", [])
160
+ addType(
161
+ root,
162
+ "SubagentType",
163
+ [
164
+ { id: 1, name: "unspecified", type: "SubagentTypeUnspecified" },
165
+ { id: 2, name: "computer_use", type: "SubagentTypeComputerUse" },
166
+ { id: 3, name: "custom", type: "SubagentTypeCustom" },
167
+ { id: 4, name: "explore", type: "SubagentTypeExplore" },
168
+ { id: 5, name: "media_review", type: "SubagentTypeMediaReview" },
169
+ { id: 6, name: "bash", type: "SubagentTypeBash" },
170
+ { id: 7, name: "browser_use", type: "SubagentTypeBrowserUse" },
171
+ { id: 8, name: "shell", type: "SubagentTypeShell" },
172
+ { id: 9, name: "vm_setup_helper", type: "SubagentTypeVmSetupHelper" },
173
+ { id: 10, name: "debug", type: "SubagentTypeDebug" },
174
+ { id: 11, name: "cursor_guide", type: "SubagentTypeCursorGuide" },
175
+ { id: 12, name: "watch_video", type: "SubagentTypeWatchVideo" },
176
+ ],
177
+ [{
178
+ name: "type",
179
+ fields: [
180
+ "unspecified", "computer_use", "custom", "explore", "media_review", "bash",
181
+ "browser_use", "shell", "vm_setup_helper", "debug", "cursor_guide", "watch_video",
182
+ ],
183
+ }],
184
+ )
185
+ addType(root, "TaskToolArgs", [
186
+ { id: 1, name: "description", type: "string" },
187
+ { id: 2, name: "prompt", type: "string" },
188
+ { id: 3, name: "subagent_type", type: "SubagentType" },
189
+ { id: 4, name: "model", type: "string" },
190
+ { id: 5, name: "resume", type: "string" },
191
+ { id: 6, name: "agent_id", type: "string" },
192
+ { id: 7, name: "attachments", type: "string", repeated: true },
193
+ ])
194
+ addType(root, "TaskToolCall", [{ id: 1, name: "args", type: "TaskToolArgs" }])
195
+
196
+ // Native subagent execution is separate from the display-only TaskToolCall.
197
+ // Cursor asks the local client to run the task through ExecServerMessage #28
198
+ // and waits for the correlated SubagentResult at ExecClientMessage #28.
199
+ addType(root, "SubagentArgs", [
200
+ { id: 1, name: "tool_call_id", type: "string" },
201
+ { id: 2, name: "subagent_type", type: "string" },
202
+ { id: 3, name: "model_id", type: "string" },
203
+ { id: 4, name: "prompt", type: "string" },
204
+ { id: 5, name: "readonly", type: "bool" },
205
+ { id: 6, name: "resume_agent_id", type: "string" },
206
+ { id: 7, name: "run_in_background", type: "bool" },
207
+ ])
208
+ addType(root, "SubagentSuccess", [
209
+ { id: 1, name: "agent_id", type: "string" },
210
+ { id: 2, name: "final_message", type: "string" },
211
+ { id: 3, name: "tool_call_count", type: "int32" },
212
+ { id: 4, name: "background_reason", type: "int32" },
213
+ { id: 5, name: "transcript_path", type: "string" },
214
+ ])
215
+ addType(root, "SubagentError", [
216
+ { id: 1, name: "agent_id", type: "string" },
217
+ { id: 2, name: "error", type: "string" },
218
+ ])
219
+ addType(
220
+ root,
221
+ "SubagentResult",
222
+ [
223
+ { id: 1, name: "success", type: "SubagentSuccess" },
224
+ { id: 2, name: "error", type: "SubagentError" },
225
+ ],
226
+ [{ name: "result", fields: ["success", "error"] }],
227
+ )
228
+ addType(root, "AskQuestionOption", [
229
+ { id: 1, name: "id", type: "string" },
230
+ { id: 2, name: "label", type: "string" },
231
+ ])
232
+ addType(root, "AskQuestionItem", [
233
+ { id: 1, name: "id", type: "string" },
234
+ { id: 2, name: "prompt", type: "string" },
235
+ { id: 3, name: "options", type: "AskQuestionOption", repeated: true },
236
+ { id: 4, name: "allow_multiple", type: "bool" },
237
+ ])
238
+ addType(root, "AskQuestionArgs", [
239
+ { id: 1, name: "title", type: "string" },
240
+ { id: 2, name: "questions", type: "AskQuestionItem", repeated: true },
241
+ // #5/#6 drive Cursor's non-blocking AskQuestion: the client answers the
242
+ // interaction with `async` right away and delivers the real answers later
243
+ // through ConversationAction.async_ask_question_completion_action, keyed by
244
+ // the originating tool call id. See protocol/ask-question.ts.
245
+ { id: 5, name: "run_async", type: "bool" },
246
+ { id: 6, name: "async_original_tool_call_id", type: "string" },
247
+ ])
248
+ addType(root, "AskQuestionToolCall", [{ id: 1, name: "args", type: "AskQuestionArgs" }])
249
+ addType(root, "FetchToolArgs", [
250
+ { id: 1, name: "url", type: "string" },
251
+ { id: 2, name: "tool_call_id", type: "string" },
252
+ ])
253
+ addType(root, "FetchToolCall", [{ id: 1, name: "args", type: "FetchToolArgs" }])
254
+ addType(root, "WebFetchToolCall", [{ id: 1, name: "args", type: "FetchToolArgs" }])
255
+ addType(root, "SwitchModeToolArgs", [
256
+ { id: 1, name: "target_mode_id", type: "string" },
257
+ { id: 2, name: "explanation", type: "string" },
258
+ { id: 3, name: "tool_call_id", type: "string" },
259
+ ])
260
+ // Display ToolCall.result (CLI success hardcodes from_mode_id=""); distinct from
261
+ // InteractionResponse approved/rejected, which is the consent gate.
262
+ addType(root, "SwitchModeSuccess", [
263
+ { id: 1, name: "from_mode_id", type: "string" },
264
+ { id: 2, name: "to_mode_id", type: "string" },
265
+ ])
266
+ addType(root, "SwitchModeError", [{ id: 1, name: "error", type: "string" }])
267
+ addType(root, "SwitchModeRejected", [{ id: 1, name: "reason", type: "string" }])
268
+ addType(root, "SwitchModeResult", [
269
+ { id: 1, name: "success", type: "SwitchModeSuccess" },
270
+ { id: 2, name: "error", type: "SwitchModeError" },
271
+ { id: 3, name: "rejected", type: "SwitchModeRejected" },
272
+ ], [{ name: "result", fields: ["success", "error", "rejected"] }])
273
+ addType(root, "SwitchModeToolCall", [
274
+ { id: 1, name: "args", type: "SwitchModeToolArgs" },
275
+ { id: 2, name: "result", type: "SwitchModeResult" },
276
+ ])
277
+ addType(root, "PiReadToolArgs", [
278
+ { id: 1, name: "path", type: "string" },
279
+ { id: 2, name: "offset", type: "int32" },
280
+ { id: 3, name: "limit", type: "int32" },
281
+ ])
282
+ addType(root, "PiBashToolArgs", [
283
+ { id: 1, name: "command", type: "string" },
284
+ { id: 2, name: "timeout", type: "double" },
285
+ ])
286
+ addType(root, "PiEditReplacement", [
287
+ { id: 1, name: "old_text", type: "string" },
288
+ { id: 2, name: "new_text", type: "string" },
289
+ ])
290
+ addType(root, "PiEditToolArgs", [
291
+ { id: 1, name: "path", type: "string" },
292
+ { id: 2, name: "edits", type: "PiEditReplacement", repeated: true },
293
+ ])
294
+ addType(root, "PiWriteToolArgs", [
295
+ { id: 1, name: "path", type: "string" },
296
+ { id: 2, name: "content", type: "string" },
297
+ ])
298
+ addType(root, "PiGrepToolArgs", [
299
+ { id: 1, name: "pattern", type: "string" },
300
+ { id: 2, name: "path", type: "string" },
301
+ { id: 3, name: "glob", type: "string" },
302
+ { id: 4, name: "ignore_case", type: "bool" },
303
+ { id: 5, name: "literal", type: "bool" },
304
+ { id: 6, name: "context", type: "int32" },
305
+ { id: 7, name: "limit", type: "int32" },
306
+ ])
307
+ addType(root, "PiFindToolArgs", [
308
+ { id: 1, name: "pattern", type: "string" },
309
+ { id: 2, name: "path", type: "string" },
310
+ { id: 3, name: "limit", type: "int32" },
311
+ ])
312
+ addType(root, "PiLsToolArgs", [
313
+ { id: 1, name: "path", type: "string" },
314
+ { id: 2, name: "limit", type: "int32" },
315
+ ])
316
+ addType(root, "PiWriteToolCall", [{ id: 1, name: "args", type: "PiWriteToolArgs" }])
317
+ addType(root, "PiReadToolCall", [{ id: 1, name: "args", type: "PiReadToolArgs" }])
318
+ addType(root, "PiBashToolCall", [{ id: 1, name: "args", type: "PiBashToolArgs" }])
319
+ addType(root, "PiEditToolCall", [{ id: 1, name: "args", type: "PiEditToolArgs" }])
320
+ addType(root, "PiGrepToolCall", [{ id: 1, name: "args", type: "PiGrepToolArgs" }])
321
+ addType(root, "PiFindToolCall", [{ id: 1, name: "args", type: "PiFindToolArgs" }])
322
+ addType(root, "PiLsToolCall", [{ id: 1, name: "args", type: "PiLsToolArgs" }])
323
+ // Display-only native tools Cursor may complete without an ExecServerMessage.
324
+ // ReadTodos mirrors UpdateTodos: args + result (see agent.v1 ReadTodosToolCall).
325
+ addType(root, "ReadTodosArgs", [
326
+ { id: 1, name: "status_filter", type: "TodoStatus", repeated: true },
327
+ { id: 2, name: "id_filter", type: "string", repeated: true },
328
+ ])
329
+ addType(root, "ReadTodosSuccess", [
330
+ { id: 1, name: "todos", type: "TodoItem", repeated: true },
331
+ { id: 2, name: "total_count", type: "int32" },
332
+ ])
333
+ addType(root, "ReadTodosError", [{ id: 1, name: "error", type: "string" }])
334
+ addType(
335
+ root,
336
+ "ReadTodosResult",
337
+ [
338
+ { id: 1, name: "success", type: "ReadTodosSuccess" },
339
+ { id: 2, name: "error", type: "ReadTodosError" },
340
+ ],
341
+ [{ name: "result", fields: ["success", "error"] }],
342
+ )
343
+ addType(root, "ReadTodosToolCall", [
344
+ { id: 1, name: "args", type: "ReadTodosArgs" },
345
+ { id: 2, name: "result", type: "ReadTodosResult" },
346
+ ])
347
+ addType(root, "AwaitArgs", [
348
+ { id: 1, name: "task_id", type: "string" },
349
+ { id: 2, name: "block_until_ms", type: "uint32" },
350
+ { id: 3, name: "regex", type: "string" },
351
+ ])
352
+ addType(root, "AwaitToolCall", [{ id: 1, name: "args", type: "AwaitArgs" }])
353
+ addType(root, "GetMcpToolsArgs", [
354
+ { id: 1, name: "server", type: "string" },
355
+ { id: 2, name: "tool_name", type: "string" },
356
+ { id: 3, name: "pattern", type: "string" },
357
+ { id: 4, name: "tool_call_id", type: "string" },
358
+ ])
359
+ // agent.v1 GetMcpToolsToolCall is args + result. Cursor's agent fills result
360
+ // locally (GetDynamicTools / GetMcpTools); large catalogs spill through
361
+ // write_args and put the spill path on GetMcpToolsSuccess.output_file_path.
362
+ addType(root, "GetMcpToolsSuccess", [
363
+ { id: 1, name: "content", type: "string" },
364
+ { id: 2, name: "output_file_path", type: "string" },
365
+ ])
366
+ addType(root, "GetMcpToolsError", [{ id: 1, name: "error", type: "string" }])
367
+ addType(root, "GetMcpToolsAgentResult", [
368
+ { id: 1, name: "success", type: "GetMcpToolsSuccess" },
369
+ { id: 2, name: "error", type: "GetMcpToolsError" },
370
+ ], [{ name: "result", fields: ["success", "error"] }])
371
+ addType(root, "GetMcpToolsToolCall", [
372
+ { id: 1, name: "args", type: "GetMcpToolsArgs" },
373
+ { id: 2, name: "result", type: "GetMcpToolsAgentResult" },
374
+ ])
375
+
376
+ addType(root, "GenerateImageToolArgs", [
377
+ { id: 1, name: "description", type: "string" },
378
+ { id: 2, name: "file_path", type: "string" },
379
+ { id: 5, name: "reference_image_paths", type: "string", repeated: true },
380
+ ])
381
+ // Cursor generates the image server-side and returns it on the display
382
+ // tool_call frame. `image_data` is a base64 string, not bytes.
383
+ addType(root, "GenerateImageSuccess", [
384
+ { id: 1, name: "file_path", type: "string" },
385
+ { id: 2, name: "image_data", type: "string" },
386
+ ])
387
+ addType(root, "GenerateImageError", [{ id: 1, name: "error", type: "string" }])
388
+ addType(root, "GenerateImageResult", [
389
+ { id: 1, name: "success", type: "GenerateImageSuccess" },
390
+ { id: 2, name: "error", type: "GenerateImageError" },
391
+ ], [{ name: "result", fields: ["success", "error"] }])
392
+ addType(root, "GenerateImageToolCall", [
393
+ { id: 1, name: "args", type: "GenerateImageToolArgs" },
394
+ { id: 2, name: "result", type: "GenerateImageResult" },
395
+ ])
396
+ // InteractionQuery keeps query bodies opaque; decoded on demand like
397
+ // AskQuestionInteractionQuery.
398
+ addType(root, "GenerateImageRequestQuery", [
399
+ { id: 1, name: "args", type: "GenerateImageToolArgs" },
400
+ { id: 2, name: "tool_call_id", type: "string" },
401
+ ])
402
+
403
+ addType(
404
+ root,
405
+ "ToolCall",
406
+ [
407
+ { id: 57, name: "tool_call_id", type: "string" },
408
+ { id: 1, name: "shell_tool_call", type: "ShellToolCall" },
409
+ { id: 3, name: "delete_tool_call", type: "DeleteToolCall" },
410
+ { id: 4, name: "glob_tool_call", type: "GlobToolCall" },
411
+ { id: 5, name: "grep_tool_call", type: "GrepToolCall" },
412
+ { id: 8, name: "read_tool_call", type: "ReadToolCall" },
413
+ { id: 9, name: "update_todos_tool_call", type: "UpdateTodosToolCall" },
414
+ { id: 10, name: "read_todos_tool_call", type: "ReadTodosToolCall" },
415
+ { id: 12, name: "edit_tool_call", type: "EditToolCall" },
416
+ { id: 13, name: "ls_tool_call", type: "LsToolCall" },
417
+ { id: 15, name: "mcp_tool_call", type: "McpToolCall" },
418
+ { id: 17, name: "create_plan_tool_call", type: "CreatePlanToolCall" },
419
+ { id: 18, name: "web_search_tool_call", type: "WebSearchToolCall" },
420
+ { id: 19, name: "task_tool_call", type: "TaskToolCall" },
421
+ { id: 23, name: "ask_question_tool_call", type: "AskQuestionToolCall" },
422
+ { id: 24, name: "fetch_tool_call", type: "FetchToolCall" },
423
+ { id: 25, name: "switch_mode_tool_call", type: "SwitchModeToolCall" },
424
+ { id: 28, name: "generate_image_tool_call", type: "GenerateImageToolCall" },
425
+ { id: 37, name: "web_fetch_tool_call", type: "WebFetchToolCall" },
426
+ { id: 42, name: "await_tool_call", type: "AwaitToolCall" },
427
+ { id: 44, name: "get_mcp_tools_tool_call", type: "GetMcpToolsToolCall" },
428
+ { id: 61, name: "pi_read_tool_call", type: "PiReadToolCall" },
429
+ { id: 62, name: "pi_bash_tool_call", type: "PiBashToolCall" },
430
+ { id: 63, name: "pi_edit_tool_call", type: "PiEditToolCall" },
431
+ { id: 64, name: "pi_write_tool_call", type: "PiWriteToolCall" },
432
+ { id: 65, name: "pi_grep_tool_call", type: "PiGrepToolCall" },
433
+ { id: 66, name: "pi_find_tool_call", type: "PiFindToolCall" },
434
+ { id: 67, name: "pi_ls_tool_call", type: "PiLsToolCall" },
435
+ ],
436
+ [{
437
+ name: "tool",
438
+ fields: [
439
+ "shell_tool_call", "delete_tool_call", "glob_tool_call", "grep_tool_call",
440
+ "read_tool_call", "update_todos_tool_call", "read_todos_tool_call",
441
+ "edit_tool_call", "ls_tool_call",
442
+ "mcp_tool_call", "create_plan_tool_call", "web_search_tool_call", "task_tool_call",
443
+ "ask_question_tool_call", "fetch_tool_call", "switch_mode_tool_call",
444
+ "generate_image_tool_call", "web_fetch_tool_call", "await_tool_call", "get_mcp_tools_tool_call",
445
+ "pi_read_tool_call", "pi_bash_tool_call", "pi_edit_tool_call",
446
+ "pi_write_tool_call", "pi_grep_tool_call", "pi_find_tool_call", "pi_ls_tool_call",
447
+ ],
448
+ }],
449
+ )
450
+
451
+ addType(root, "ToolCallStarted", [
452
+ { id: 1, name: "call_id", type: "string" },
453
+ { id: 2, name: "tool_call", type: "ToolCall" },
454
+ { id: 3, name: "model_call_id", type: "string" },
455
+ ])
456
+
457
+ // agent.v1 ToolCallCompletedUpdate has no string result field — result lives
458
+ // inside the typed ToolCall variant.
459
+ addType(root, "ToolCallCompleted", [
460
+ { id: 1, name: "call_id", type: "string" },
461
+ { id: 2, name: "tool_call", type: "ToolCall" },
462
+ { id: 3, name: "model_call_id", type: "string" },
463
+ ])
464
+
465
+ addType(root, "PartialToolCall", [
466
+ { id: 1, name: "call_id", type: "string" },
467
+ { id: 2, name: "tool_call", type: "ToolCall" },
468
+ { id: 3, name: "args_text_delta", type: "string" },
469
+ { id: 4, name: "model_call_id", type: "string" },
470
+ ])
471
+
472
+ // agent.v1 ToolCallDeltaUpdate: keep the nested delta opaque; the pump
473
+ // needs its presence as generation progress, not its display-only content.
474
+ addType(root, "ToolCallDeltaUpdate", [
475
+ { id: 1, name: "call_id", type: "string" },
476
+ { id: 2, name: "tool_call_delta", type: "bytes" },
477
+ { id: 3, name: "model_call_id", type: "string" },
478
+ ])
479
+
480
+ // agent.v1 StepStartedUpdate / StepCompletedUpdate: step_id is uint64 (T:4),
481
+ // not string — wrong type made every step_completed frame throw
482
+ // "index out of range" in decodeMessage.
483
+ addType(root, "StepStarted", [
484
+ { id: 1, name: "step_id", type: "uint64" },
485
+ ])
486
+
487
+ addType(root, "StepCompleted", [
488
+ { id: 1, name: "step_id", type: "uint64" },
489
+ { id: 2, name: "step_duration_ms", type: "int64" },
490
+ ])
491
+
492
+ // agent.v1 ToolRequestsListedUpdate — Cursor lists how many tool calls this
493
+ // generation will send so the client can place its step boundary (CLI does
494
+ // not need this; OpenCode's AI SDK step does). Field 27 on InteractionUpdate.
495
+ addType(root, "ToolRequestsListedUpdate", [
496
+ { id: 1, name: "call_count", type: "uint32" },
497
+ ])
498
+
499
+ // InteractionUpdate — the core streaming update message
500
+ addType(
501
+ root,
502
+ "InteractionUpdate",
503
+ [
504
+ { id: 1, name: "text_delta", type: "TextDeltaUpdate" },
505
+ { id: 2, name: "tool_call_started", type: "ToolCallStarted" },
506
+ { id: 3, name: "tool_call_completed", type: "ToolCallCompleted" },
507
+ { id: 4, name: "thinking_delta", type: "ThinkingDeltaUpdate" },
508
+ { id: 7, name: "partial_tool_call", type: "PartialToolCall" },
509
+ { id: 13, name: "heartbeat", type: "Heartbeat" },
510
+ { id: 14, name: "turn_ended", type: "TurnEnded" },
511
+ { id: 15, name: "tool_call_delta", type: "ToolCallDeltaUpdate" },
512
+ { id: 16, name: "step_started", type: "StepStarted" },
513
+ { id: 17, name: "step_completed", type: "StepCompleted" },
514
+ { id: 23, name: "context_injection_state", type: "ContextInjectionStateUpdate" },
515
+ { id: 27, name: "tool_requests_listed", type: "ToolRequestsListedUpdate" },
516
+ ],
517
+ [{ name: "update", fields: ["text_delta", "tool_call_started", "tool_call_completed", "thinking_delta", "partial_tool_call", "heartbeat", "turn_ended", "tool_call_delta", "step_started", "step_completed", "context_injection_state", "tool_requests_listed"] }],
518
+ )
519
+
520
+ // Server status for an `inject_context_action` (InteractionUpdate #23).
521
+ // Cursor CLI only tracks it to show queued steer delivery; it carries no output.
522
+ addType(root, "ContextInjectionQueued", [])
523
+ addType(root, "ContextInjectionQueuedForNextTurn", [])
524
+ addType(root, "ContextInjectionCancelled", [])
525
+ addType(root, "ContextInjectionDelivered", [
526
+ { id: 1, name: "step", type: "int32" },
527
+ { id: 2, name: "delivery_batch_id", type: "string" },
528
+ { id: 3, name: "delivered_at_ms", type: "int64" },
529
+ ])
530
+ addType(root, "ContextInjectionRejected", [
531
+ { id: 1, name: "reason", type: "string" },
532
+ ])
533
+ addType(
534
+ root,
535
+ "ContextInjectionState",
536
+ [
537
+ { id: 1, name: "queued", type: "ContextInjectionQueued" },
538
+ { id: 2, name: "delivered", type: "ContextInjectionDelivered" },
539
+ { id: 3, name: "queued_for_next_turn", type: "ContextInjectionQueuedForNextTurn" },
540
+ { id: 4, name: "cancelled", type: "ContextInjectionCancelled" },
541
+ { id: 5, name: "rejected", type: "ContextInjectionRejected" },
542
+ ],
543
+ [{ name: "state", fields: ["queued", "delivered", "queued_for_next_turn", "cancelled", "rejected"] }],
544
+ )
545
+ addType(root, "ContextInjectionStateUpdate", [
546
+ { id: 1, name: "injection_id", type: "string" },
547
+ { id: 2, name: "state", type: "ContextInjectionState" },
548
+ ])
549
+
550
+ // ── Exec channel ──
551
+
552
+ // Field numbers match agent.v1. Extra fields we don't use are still declared
553
+ // so protobufjs doesn't drop them on decode.
554
+ addType(root, "ReadArgs", [
555
+ { id: 1, name: "path", type: "string" },
556
+ { id: 2, name: "tool_call_id", type: "string" },
557
+ { id: 4, name: "offset", type: "int32" },
558
+ { id: 5, name: "limit", type: "uint32" },
559
+ ])
560
+
561
+ // agent.v1 ReadResult is a oneof — flat {content,error} was rejected by the
562
+ // server (endless heartbeats after read_result).
563
+ addType(
564
+ root,
565
+ "ReadSuccess",
566
+ [
567
+ { id: 1, name: "path", type: "string" },
568
+ { id: 2, name: "content", type: "string" },
569
+ { id: 3, name: "total_lines", type: "int32" },
570
+ { id: 4, name: "file_size", type: "int64" },
571
+ { id: 5, name: "data", type: "bytes" },
572
+ { id: 6, name: "truncated", type: "bool" },
573
+ { id: 7, name: "output_blob_id", type: "bytes" },
574
+ { id: 8, name: "range_applied", type: "bool" },
575
+ ],
576
+ [{ name: "output", fields: ["content", "data"] }],
577
+ )
578
+ addType(root, "ReadError", [
579
+ { id: 1, name: "path", type: "string" },
580
+ { id: 2, name: "error", type: "string" },
581
+ ])
582
+ // Cursor's LocalReadExecutor validates the target before execution and, for a
583
+ // missing / non-regular path, returns one of these typed ReadResult cases
584
+ // instead of running the tool. Mirroring the exact field ids/shapes lets this
585
+ // provider reply the same way so the model sees a structured file-not-found
586
+ // observation and the host never surfaces a permission prompt.
587
+ addType(root, "ReadFileNotFound", [{ id: 1, name: "path", type: "string" }])
588
+ addType(root, "ReadPermissionDenied", [{ id: 1, name: "path", type: "string" }])
589
+ addType(root, "ReadInvalidFile", [
590
+ { id: 1, name: "path", type: "string" },
591
+ { id: 2, name: "reason", type: "string" },
592
+ ])
593
+ addType(root, "ReadRejected", [
594
+ { id: 1, name: "path", type: "string" },
595
+ { id: 2, name: "reason", type: "string" },
596
+ ])
597
+ addType(
598
+ root,
599
+ "ReadResult",
600
+ [
601
+ { id: 1, name: "success", type: "ReadSuccess" },
602
+ { id: 2, name: "error", type: "ReadError" },
603
+ { id: 3, name: "rejected", type: "ReadRejected" },
604
+ { id: 4, name: "file_not_found", type: "ReadFileNotFound" },
605
+ { id: 5, name: "permission_denied", type: "ReadPermissionDenied" },
606
+ { id: 6, name: "invalid_file", type: "ReadInvalidFile" },
607
+ ],
608
+ [
609
+ {
610
+ name: "result",
611
+ fields: ["success", "error", "rejected", "file_not_found", "permission_denied", "invalid_file"],
612
+ },
613
+ ],
614
+ )
615
+
616
+ addType(root, "GrepArgs", [
617
+ { id: 1, name: "pattern", type: "string" },
618
+ { id: 2, name: "path", type: "string" },
619
+ { id: 3, name: "glob", type: "string" },
620
+ { id: 4, name: "output_mode", type: "string" },
621
+ { id: 8, name: "case_insensitive", type: "bool" },
622
+ { id: 10, name: "head_limit", type: "int32" },
623
+ { id: 14, name: "tool_call_id", type: "string" },
624
+ { id: 16, name: "offset", type: "int32" },
625
+ ])
626
+
627
+ addType(root, "GrepError", [{ id: 1, name: "error", type: "string" }])
628
+ addType(root, "GrepFilesResult", [
629
+ { id: 1, name: "files", type: "string", repeated: true },
630
+ { id: 2, name: "total_files", type: "int32" },
631
+ { id: 3, name: "client_truncated", type: "bool" },
632
+ ])
633
+ addType(root, "GrepContentMatch", [
634
+ { id: 1, name: "line_number", type: "int32" },
635
+ { id: 2, name: "content", type: "string" },
636
+ ])
637
+ addType(root, "GrepFileMatch", [
638
+ { id: 1, name: "file", type: "string" },
639
+ { id: 2, name: "matches", type: "GrepContentMatch", repeated: true },
640
+ ])
641
+ addType(root, "GrepContentResult", [
642
+ { id: 1, name: "matches", type: "GrepFileMatch", repeated: true },
643
+ { id: 2, name: "total_lines", type: "int32" },
644
+ { id: 3, name: "total_matched_lines", type: "int32" },
645
+ { id: 4, name: "client_truncated", type: "bool" },
646
+ { id: 5, name: "ripgrep_truncated", type: "bool" },
647
+ ])
648
+ addType(
649
+ root,
650
+ "GrepUnionResult",
651
+ [
652
+ { id: 2, name: "files", type: "GrepFilesResult" },
653
+ { id: 3, name: "content", type: "GrepContentResult" },
654
+ ],
655
+ [{ name: "result", fields: ["files", "content"] }],
656
+ )
657
+ {
658
+ const t = new protobuf.Type("GrepSuccess")
659
+ t.add(new protobuf.Field("pattern", 1, "string"))
660
+ t.add(new protobuf.Field("path", 2, "string"))
661
+ t.add(new protobuf.Field("output_mode", 3, "string"))
662
+ t.add(new protobuf.MapField("workspace_results", 4, "string", "GrepUnionResult"))
663
+ root.add(t)
664
+ }
665
+ addType(
666
+ root,
667
+ "GrepResult",
668
+ [
669
+ { id: 1, name: "success", type: "GrepSuccess" },
670
+ { id: 2, name: "error", type: "GrepError" },
671
+ ],
672
+ [{ name: "result", fields: ["success", "error"] }],
673
+ )
674
+
675
+ // agent.v1.WriteArgs. Cursor's own LocalWriteExecutor reads `file_bytes`
676
+ // first and only falls back to `file_text`, so a write whose content arrives
677
+ // as bytes decodes to an empty string unless field 5 is declared here.
678
+ addType(root, "WriteArgs", [
679
+ { id: 1, name: "path", type: "string" },
680
+ { id: 2, name: "file_text", type: "string" },
681
+ { id: 3, name: "tool_call_id", type: "string" },
682
+ { id: 4, name: "return_file_content_after_write", type: "bool" },
683
+ { id: 5, name: "file_bytes", type: "bytes" },
684
+ { id: 6, name: "encoding_hint", type: "string" },
685
+ ])
686
+
687
+ addType(root, "WriteSuccess", [
688
+ { id: 1, name: "path", type: "string" },
689
+ { id: 2, name: "lines_created", type: "int32" },
690
+ { id: 3, name: "file_size", type: "int32" },
691
+ { id: 4, name: "file_content_after_write", type: "string" },
692
+ ])
693
+ addType(root, "WriteError", [
694
+ { id: 1, name: "path", type: "string" },
695
+ { id: 2, name: "error", type: "string" },
696
+ ])
697
+ // Cursor's own executor reports a refused write with this variant rather than
698
+ // a generic error, and its agent reads `operation` / `is_readonly` back.
699
+ addType(root, "WritePermissionDenied", [
700
+ { id: 1, name: "path", type: "string" },
701
+ { id: 2, name: "directory", type: "string" },
702
+ { id: 3, name: "operation", type: "string" },
703
+ { id: 4, name: "error", type: "string" },
704
+ { id: 5, name: "is_readonly", type: "bool" },
705
+ ])
706
+ addType(root, "WriteRejected", [
707
+ { id: 1, name: "path", type: "string" },
708
+ { id: 2, name: "reason", type: "string" },
709
+ ])
710
+ addType(
711
+ root,
712
+ "WriteResult",
713
+ [
714
+ { id: 1, name: "success", type: "WriteSuccess" },
715
+ { id: 3, name: "permission_denied", type: "WritePermissionDenied" },
716
+ { id: 5, name: "error", type: "WriteError" },
717
+ { id: 6, name: "rejected", type: "WriteRejected" },
718
+ ],
719
+ [{ name: "result", fields: ["success", "permission_denied", "error", "rejected"] }],
720
+ )
721
+
722
+ // agent.v1 Pi exec results. Cursor places Pi requests at ExecServerMessage
723
+ // fields #45..#51 and their corresponding ExecClientMessage results at
724
+ // #46..#52 (the result fields are deliberately offset by one).
725
+ //
726
+ // The success payloads contain additional optional truncation/diff metadata;
727
+ // OpenCode returns text, so the minimal output/error shapes are sufficient.
728
+ addType(root, "PiWriteArgs", [
729
+ { id: 1, name: "path", type: "string" },
730
+ { id: 2, name: "content", type: "string" },
731
+ ])
732
+ addType(root, "PiWriteSuccess", [
733
+ { id: 1, name: "output", type: "string" },
734
+ ])
735
+ addType(root, "PiWriteError", [
736
+ { id: 1, name: "error", type: "string" },
737
+ ])
738
+ addType(root, "PiWriteRejected", [
739
+ { id: 1, name: "reason", type: "string" },
740
+ ])
741
+ addType(
742
+ root,
743
+ "PiWriteResult",
744
+ [
745
+ { id: 1, name: "success", type: "PiWriteSuccess" },
746
+ { id: 2, name: "error", type: "PiWriteError" },
747
+ { id: 3, name: "rejected", type: "PiWriteRejected" },
748
+ ],
749
+ [{ name: "result", fields: ["success", "error", "rejected"] }],
750
+ )
751
+ // agent.v1.PiTruncation. Every Pi *ExecSuccess shares output(1)+truncation(2)
752
+ // and only diverges at field 3, so the shared PiOutputSuccess can carry it.
753
+ // Cursor's own CLI always reports truncation rather than silently shortening
754
+ // a payload; emitting this keeps a capped read distinguishable from a
755
+ // complete one.
756
+ addType(root, "PiTruncation", [
757
+ { id: 1, name: "truncated", type: "bool" },
758
+ { id: 2, name: "truncated_by", type: "string" },
759
+ { id: 3, name: "total_lines", type: "uint32" },
760
+ { id: 4, name: "output_lines", type: "uint32" },
761
+ { id: 5, name: "output_bytes", type: "uint32" },
762
+ { id: 6, name: "max_lines", type: "uint32" },
763
+ { id: 7, name: "max_bytes", type: "uint32" },
764
+ { id: 8, name: "first_line_exceeds_limit", type: "bool" },
765
+ { id: 9, name: "last_line_partial", type: "bool" },
766
+ ])
767
+ addType(root, "PiOutputSuccess", [
768
+ { id: 1, name: "output", type: "string" },
769
+ { id: 2, name: "truncation", type: "PiTruncation" },
770
+ ])
771
+ addType(root, "PiExecError", [{ id: 1, name: "error", type: "string" }])
772
+ addType(root, "PiExecRejected", [{ id: 1, name: "reason", type: "string" }])
773
+ for (const resultType of [
774
+ "PiReadExecResult",
775
+ "PiBashExecResult",
776
+ "PiGrepExecResult",
777
+ "PiFindExecResult",
778
+ "PiLsExecResult",
779
+ ]) {
780
+ addType(
781
+ root,
782
+ resultType,
783
+ [
784
+ { id: 1, name: "success", type: "PiOutputSuccess" },
785
+ { id: 2, name: "error", type: "PiExecError" },
786
+ ],
787
+ [{ name: "result", fields: ["success", "error"] }],
788
+ )
789
+ }
790
+ addType(
791
+ root,
792
+ "PiEditExecResult",
793
+ [
794
+ { id: 1, name: "success", type: "PiOutputSuccess" },
795
+ { id: 2, name: "error", type: "PiExecError" },
796
+ { id: 3, name: "rejected", type: "PiExecRejected" },
797
+ ],
798
+ [{ name: "result", fields: ["success", "error", "rejected"] }],
799
+ )
800
+
801
+ addType(root, "DeleteArgs", [
802
+ { id: 1, name: "path", type: "string" },
803
+ { id: 2, name: "tool_call_id", type: "string" },
804
+ ])
805
+
806
+ addType(root, "DeleteSuccess", [
807
+ { id: 1, name: "path", type: "string" },
808
+ { id: 2, name: "deleted_file", type: "string" },
809
+ ])
810
+ addType(root, "DeleteError", [
811
+ { id: 1, name: "path", type: "string" },
812
+ { id: 2, name: "error", type: "string" },
813
+ ])
814
+ addType(root, "DeletePermissionDenied", [
815
+ { id: 1, name: "path", type: "string" },
816
+ { id: 2, name: "client_visible_error", type: "string" },
817
+ { id: 3, name: "is_readonly", type: "bool" },
818
+ ])
819
+ addType(root, "DeleteRejected", [
820
+ { id: 1, name: "path", type: "string" },
821
+ { id: 2, name: "reason", type: "string" },
822
+ ])
823
+ addType(
824
+ root,
825
+ "DeleteResult",
826
+ [
827
+ { id: 1, name: "success", type: "DeleteSuccess" },
828
+ { id: 4, name: "permission_denied", type: "DeletePermissionDenied" },
829
+ { id: 6, name: "rejected", type: "DeleteRejected" },
830
+ { id: 7, name: "error", type: "DeleteError" },
831
+ ],
832
+ [{ name: "result", fields: ["success", "permission_denied", "rejected", "error"] }],
833
+ )
834
+
835
+ addType(root, "LsArgs", [
836
+ { id: 1, name: "path", type: "string" },
837
+ { id: 2, name: "ignore", type: "string", repeated: true },
838
+ { id: 3, name: "tool_call_id", type: "string" },
839
+ ])
840
+
841
+ addType(root, "LsDirectoryTreeFile", [{ id: 1, name: "name", type: "string" }])
842
+ addType(root, "LsDirectoryTreeNode", [
843
+ { id: 1, name: "abs_path", type: "string" },
844
+ { id: 2, name: "children_dirs", type: "LsDirectoryTreeNode", repeated: true },
845
+ { id: 3, name: "children_files", type: "LsDirectoryTreeFile", repeated: true },
846
+ { id: 6, name: "num_files", type: "int32" },
847
+ ])
848
+ addType(root, "LsSuccess", [
849
+ { id: 1, name: "directory_tree_root", type: "LsDirectoryTreeNode" },
850
+ ])
851
+ addType(root, "LsError", [
852
+ { id: 1, name: "path", type: "string" },
853
+ { id: 2, name: "error", type: "string" },
854
+ ])
855
+ addType(root, "LsRejected", [
856
+ { id: 1, name: "path", type: "string" },
857
+ { id: 2, name: "reason", type: "string" },
858
+ ])
859
+ addType(
860
+ root,
861
+ "LsResult",
862
+ [
863
+ { id: 1, name: "success", type: "LsSuccess" },
864
+ { id: 2, name: "error", type: "LsError" },
865
+ { id: 3, name: "rejected", type: "LsRejected" },
866
+ ],
867
+ [{ name: "result", fields: ["success", "error", "rejected"] }],
868
+ )
869
+
870
+ addType(root, "ShellArgs", [
871
+ { id: 1, name: "command", type: "string" },
872
+ { id: 2, name: "working_directory", type: "string" },
873
+ { id: 3, name: "timeout", type: "uint32" },
874
+ { id: 4, name: "tool_call_id", type: "string" },
875
+ // Canonical agent.v1 fields used by the native CLI to distinguish a
876
+ // foreground cancellation deadline from a soft background handoff.
877
+ { id: 13, name: "timeout_behavior", type: "uint32" },
878
+ { id: 14, name: "hard_timeout", type: "uint32" },
879
+ { id: 15, name: "description", type: "string" },
880
+ ])
881
+
882
+ addType(root, "ShellStreamStart", []) // optional SandboxPolicy only; empty is valid
883
+ addType(root, "ShellStreamStdout", [{ id: 1, name: "data", type: "string" }])
884
+ addType(root, "ShellStreamStderr", [{ id: 1, name: "data", type: "string" }])
885
+ // agent.v1: code is uint32; protobufjs must emit code=0 (defaults are load-bearing).
886
+ addType(root, "ShellStreamExit", [
887
+ { id: 1, name: "code", type: "uint32" },
888
+ { id: 2, name: "cwd", type: "string" },
889
+ { id: 4, name: "aborted", type: "bool" },
890
+ { id: 5, name: "abort_reason", type: "uint32" },
891
+ { id: 6, name: "local_execution_time_ms", type: "uint32" },
892
+ ])
893
+ addType(root, "ShellStreamBackgrounded", [
894
+ { id: 1, name: "shell_id", type: "uint32" },
895
+ { id: 2, name: "command", type: "string" },
896
+ { id: 3, name: "working_directory", type: "string" },
897
+ { id: 4, name: "pid", type: "uint32" },
898
+ { id: 5, name: "ms_to_wait", type: "uint32" },
899
+ { id: 6, name: "reason", type: "uint32" },
900
+ ])
901
+ // ShellRejected / ShellPermissionDenied (shared with ShellResult) — reason/error at #3.
902
+ addType(root, "ShellRejected", [
903
+ { id: 1, name: "command", type: "string" },
904
+ { id: 2, name: "working_directory", type: "string" },
905
+ { id: 3, name: "reason", type: "string" },
906
+ ])
907
+ addType(root, "ShellPermissionDenied", [
908
+ { id: 1, name: "command", type: "string" },
909
+ { id: 2, name: "working_directory", type: "string" },
910
+ { id: 3, name: "error", type: "string" },
911
+ ])
912
+
913
+ // ── Soft-deny stubs for known unsupported Cursor-native exec variants ──
914
+ // These are encode-only: enough to emit a populated deny, not full success.
915
+ // Canonical field numbers from agent.proto ExecClientMessage.
916
+ // ShellResult is also the live reply for exec #2 shell_args (OpenCode bash).
917
+ addType(root, "ShellSuccess", [
918
+ { id: 1, name: "command", type: "string" },
919
+ { id: 2, name: "working_directory", type: "string" },
920
+ { id: 3, name: "exit_code", type: "int32" },
921
+ { id: 4, name: "signal", type: "string" },
922
+ { id: 5, name: "stdout", type: "string" },
923
+ { id: 6, name: "stderr", type: "string" },
924
+ { id: 7, name: "execution_time", type: "int32" },
925
+ { id: 9, name: "shell_id", type: "uint32" },
926
+ { id: 11, name: "pid", type: "uint32" },
927
+ { id: 12, name: "ms_to_wait", type: "int32" },
928
+ { id: 13, name: "local_execution_time_ms", type: "int32" },
929
+ { id: 14, name: "background_reason", type: "uint32" },
930
+ ])
931
+ addType(root, "ShellFailure", [
932
+ { id: 1, name: "command", type: "string" },
933
+ { id: 2, name: "working_directory", type: "string" },
934
+ { id: 3, name: "exit_code", type: "int32" },
935
+ { id: 4, name: "signal", type: "string" },
936
+ { id: 5, name: "stdout", type: "string" },
937
+ { id: 6, name: "stderr", type: "string" },
938
+ { id: 7, name: "execution_time", type: "int32" },
939
+ { id: 10, name: "abort_reason", type: "uint32" },
940
+ { id: 11, name: "aborted", type: "bool" },
941
+ ])
942
+ addType(root, "ShellTimeout", [
943
+ { id: 1, name: "command", type: "string" },
944
+ { id: 2, name: "working_directory", type: "string" },
945
+ { id: 3, name: "timeout_ms", type: "int32" },
946
+ ])
947
+ addType(
948
+ root,
949
+ "ShellResult",
950
+ [
951
+ { id: 1, name: "success", type: "ShellSuccess" },
952
+ { id: 2, name: "failure", type: "ShellFailure" },
953
+ { id: 3, name: "timeout", type: "ShellTimeout" },
954
+ { id: 4, name: "rejected", type: "ShellRejected" },
955
+ { id: 7, name: "permission_denied", type: "ShellPermissionDenied" },
956
+ { id: 102, name: "is_background", type: "bool" },
957
+ { id: 104, name: "pid", type: "uint32" },
958
+ ],
959
+ [{ name: "result", fields: ["success", "failure", "timeout", "rejected", "permission_denied"] }],
960
+ )
961
+ addType(root, "DiagnosticsError", [
962
+ { id: 1, name: "path", type: "string" },
963
+ { id: 2, name: "error", type: "string" },
964
+ ])
965
+ addType(root, "DiagnosticsResult", [
966
+ { id: 2, name: "error", type: "DiagnosticsError" },
967
+ ], [{ name: "result", fields: ["error"] }])
968
+ addType(root, "FetchError", [
969
+ { id: 1, name: "url", type: "string" },
970
+ { id: 2, name: "error", type: "string" },
971
+ ])
972
+ addType(root, "FetchResult", [
973
+ { id: 2, name: "error", type: "FetchError" },
974
+ ], [{ name: "result", fields: ["error"] }])
975
+ addType(root, "RecordScreenFailure", [
976
+ { id: 1, name: "error", type: "string" },
977
+ ])
978
+ addType(root, "RecordScreenResult", [
979
+ { id: 4, name: "failure", type: "RecordScreenFailure" },
980
+ ], [{ name: "result", fields: ["failure"] }])
981
+ addType(root, "ComputerUseError", [
982
+ { id: 1, name: "error", type: "string" },
983
+ ])
984
+ addType(root, "ComputerUseResult", [
985
+ { id: 2, name: "error", type: "ComputerUseError" },
986
+ ], [{ name: "result", fields: ["error"] }])
987
+ addType(root, "WriteShellStdinError", [
988
+ { id: 1, name: "error", type: "string" },
989
+ ])
990
+ addType(root, "WriteShellStdinResult", [
991
+ { id: 2, name: "error", type: "WriteShellStdinError" },
992
+ ], [{ name: "result", fields: ["error"] }])
993
+ addType(root, "SubagentAwaitError", [
994
+ { id: 1, name: "agent_id", type: "string" },
995
+ { id: 2, name: "error", type: "string" },
996
+ ])
997
+ addType(root, "SubagentAwaitResult", [
998
+ { id: 4, name: "error", type: "SubagentAwaitError" },
999
+ ], [{ name: "result", fields: ["error"] }])
1000
+ addType(root, "SmartModeClassifierError", [
1001
+ { id: 1, name: "error", type: "string" },
1002
+ ])
1003
+ addType(root, "SmartModeClassifierResult", [
1004
+ { id: 2, name: "error", type: "SmartModeClassifierError" },
1005
+ ], [{ name: "result", fields: ["error"] }])
1006
+ addType(root, "CanvasDiagnosticsError", [
1007
+ { id: 1, name: "path", type: "string" },
1008
+ { id: 2, name: "error", type: "string" },
1009
+ ])
1010
+ addType(root, "CanvasDiagnosticsResult", [
1011
+ { id: 2, name: "error", type: "CanvasDiagnosticsError" },
1012
+ ], [{ name: "result", fields: ["error"] }])
1013
+ addType(root, "ShellAllowlistPrecheckResult", [
1014
+ { id: 1, name: "allowlisted", type: "bool" },
1015
+ ])
1016
+ addType(root, "McpAllowlistPrecheckResult", [
1017
+ { id: 1, name: "allowlisted", type: "bool" },
1018
+ ])
1019
+ addType(root, "WebFetchAllowlistPrecheckResult", [
1020
+ { id: 1, name: "allowlisted", type: "bool" },
1021
+ ])
1022
+ addType(root, "ForceBackgroundShellResult", [
1023
+ { id: 1, name: "status", type: "uint32" },
1024
+ ])
1025
+ addType(root, "ForceBackgroundSubagentResult", [
1026
+ { id: 1, name: "status", type: "uint32" },
1027
+ ])
1028
+ addType(root, "ConversationSearchHit", [
1029
+ { id: 1, name: "conversation_id", type: "string" },
1030
+ { id: 2, name: "title", type: "string" },
1031
+ { id: 3, name: "source", type: "uint32" },
1032
+ { id: 4, name: "updated_at_ms", type: "int64" },
1033
+ { id: 5, name: "snippet", type: "string" },
1034
+ ])
1035
+ addType(root, "ConversationSearchSuccess", [
1036
+ { id: 1, name: "hits", type: "ConversationSearchHit", repeated: true },
1037
+ { id: 2, name: "truncated", type: "bool" },
1038
+ { id: 3, name: "partial", type: "bool" },
1039
+ { id: 4, name: "rebuilding", type: "bool" },
1040
+ ])
1041
+ addType(root, "ConversationSearchError", [
1042
+ { id: 1, name: "error", type: "string" },
1043
+ ])
1044
+ addType(
1045
+ root,
1046
+ "ConversationSearchResult",
1047
+ [
1048
+ { id: 1, name: "success", type: "ConversationSearchSuccess" },
1049
+ { id: 2, name: "error", type: "ConversationSearchError" },
1050
+ ],
1051
+ [{ name: "result", fields: ["success", "error"] }],
1052
+ )
1053
+ addType(root, "AgentStoreConflictError", [
1054
+ { id: 1, name: "error", type: "string" },
1055
+ ])
1056
+ addType(
1057
+ root,
1058
+ "AgentStoreConflictResult",
1059
+ [
1060
+ { id: 2, name: "error", type: "AgentStoreConflictError" },
1061
+ ],
1062
+ [{ name: "result", fields: ["error"] }],
1063
+ )
1064
+ addType(
1065
+ root,
1066
+ "AdoptResult",
1067
+ [
1068
+ { id: 1, name: "source_agent_id", type: "string" },
1069
+ { id: 2, name: "target_agent_id", type: "string" },
1070
+ { id: 3, name: "project_root_id", type: "string" },
1071
+ { id: 5, name: "error", type: "string" },
1072
+ ],
1073
+ [{ name: "result", fields: ["error"] }],
1074
+ )
1075
+
1076
+ // aiserver.v1.GetDiffRequest / GetDiffResponse (exec #44). Enums as uint32
1077
+ // so protobufjs json decode does not stringify output_format / diff_type.
1078
+ addType(root, "GitDiffChunk", [
1079
+ { id: 1, name: "content", type: "string" },
1080
+ { id: 2, name: "lines", type: "string", repeated: true },
1081
+ { id: 3, name: "old_start", type: "int32" },
1082
+ { id: 4, name: "old_lines", type: "int32" },
1083
+ { id: 5, name: "new_start", type: "int32" },
1084
+ { id: 6, name: "new_lines", type: "int32" },
1085
+ ])
1086
+ addType(root, "GitFileDiff", [
1087
+ { id: 1, name: "from", type: "string" },
1088
+ { id: 2, name: "to", type: "string" },
1089
+ { id: 3, name: "chunks", type: "GitDiffChunk", repeated: true },
1090
+ { id: 4, name: "added", type: "int32" },
1091
+ { id: 5, name: "removed", type: "int32" },
1092
+ { id: 6, name: "before_file_contents", type: "string" },
1093
+ { id: 7, name: "after_file_contents", type: "string" },
1094
+ { id: 8, name: "is_generated", type: "bool" },
1095
+ ])
1096
+ addType(root, "GitDiff", [
1097
+ { id: 1, name: "diffs", type: "GitFileDiff", repeated: true },
1098
+ { id: 2, name: "diff_type", type: "uint32" },
1099
+ ])
1100
+ addType(root, "GitDiffSubmoduleDiff", [
1101
+ { id: 1, name: "relative_path", type: "string" },
1102
+ { id: 2, name: "diff", type: "GitDiff" },
1103
+ { id: 3, name: "errored", type: "bool" },
1104
+ ])
1105
+ addType(root, "GetDiffRequest", [
1106
+ { id: 1, name: "cwd", type: "string" },
1107
+ { id: 2, name: "ref", type: "string" },
1108
+ { id: 3, name: "base_ref", type: "string" },
1109
+ { id: 4, name: "merge_base", type: "bool" },
1110
+ { id: 5, name: "target_paths", type: "string", repeated: true },
1111
+ { id: 6, name: "unified_context_lines", type: "int32" },
1112
+ { id: 7, name: "max_untracked_files", type: "int32" },
1113
+ { id: 8, name: "output_format", type: "uint32" },
1114
+ { id: 9, name: "submodule_recurse_depth", type: "int32" },
1115
+ { id: 10, name: "include_space_changes", type: "bool" },
1116
+ { id: 11, name: "committed_only", type: "bool" },
1117
+ { id: 12, name: "compute_patch_id", type: "bool" },
1118
+ { id: 13, name: "return_head_sha", type: "bool" },
1119
+ { id: 14, name: "max_response_bytes", type: "int32" },
1120
+ ])
1121
+ addType(root, "GetDiffResponse", [
1122
+ { id: 1, name: "diff", type: "GitDiff" },
1123
+ { id: 2, name: "submodule_diffs", type: "GitDiffSubmoduleDiff", repeated: true },
1124
+ { id: 3, name: "patch_id", type: "string" },
1125
+ { id: 4, name: "head_sha", type: "string" },
1126
+ { id: 5, name: "has_uncommitted_changes", type: "bool" },
1127
+ ])
1128
+
1129
+ // Cursor's non-blocking shell path is a separate exec variant from the
1130
+ // foreground ShellStream above. The host only needs the fields used at the
1131
+ // OpenCode boundary; protobufjs safely skips the richer classifier/approval
1132
+ // messages that are private to Cursor's native client.
1133
+ addType(root, "BackgroundShellSpawnArgs", [
1134
+ { id: 1, name: "command", type: "string" },
1135
+ { id: 2, name: "working_directory", type: "string" },
1136
+ { id: 3, name: "tool_call_id", type: "string" },
1137
+ { id: 6, name: "enable_write_shell_stdin_tool", type: "bool" },
1138
+ { id: 7, name: "description", type: "string" },
1139
+ { id: 12, name: "skip_approval", type: "bool" },
1140
+ { id: 13, name: "conversation_id", type: "string" },
1141
+ ])
1142
+ addType(root, "BackgroundShellSpawnSuccess", [
1143
+ { id: 1, name: "shell_id", type: "uint32" },
1144
+ { id: 2, name: "command", type: "string" },
1145
+ { id: 3, name: "working_directory", type: "string" },
1146
+ { id: 4, name: "pid", type: "uint32" },
1147
+ ])
1148
+ addType(root, "BackgroundShellSpawnError", [
1149
+ { id: 1, name: "command", type: "string" },
1150
+ { id: 2, name: "working_directory", type: "string" },
1151
+ { id: 3, name: "error", type: "string" },
1152
+ ])
1153
+ addType(
1154
+ root,
1155
+ "BackgroundShellSpawnResult",
1156
+ [
1157
+ { id: 1, name: "success", type: "BackgroundShellSpawnSuccess" },
1158
+ { id: 2, name: "error", type: "BackgroundShellSpawnError" },
1159
+ { id: 3, name: "rejected", type: "ShellRejected" },
1160
+ { id: 4, name: "permission_denied", type: "ShellPermissionDenied" },
1161
+ ],
1162
+ [{ name: "result", fields: ["success", "error", "rejected", "permission_denied"] }],
1163
+ )
1164
+
1165
+ // Cursor CLI postToolUse hook context: ExecClientMessage #45 on a unary
1166
+ // result, ShellStream #8 on a shell stream (agent.v1 hook_additional_context).
1167
+ addType(root, "HookAdditionalContext", [
1168
+ { id: 1, name: "hook_event_name", type: "string" },
1169
+ { id: 2, name: "content", type: "string" },
1170
+ ])
1171
+ addType(root, "ShellStreamHookContext", [
1172
+ { id: 1, name: "hook_additional_contexts", type: "HookAdditionalContext", repeated: true },
1173
+ ])
1174
+
1175
+ addType(
1176
+ root,
1177
+ "ShellStream",
1178
+ [
1179
+ { id: 1, name: "stdout", type: "ShellStreamStdout" },
1180
+ { id: 2, name: "stderr", type: "ShellStreamStderr" },
1181
+ { id: 3, name: "exit", type: "ShellStreamExit" },
1182
+ { id: 4, name: "start", type: "ShellStreamStart" },
1183
+ { id: 5, name: "rejected", type: "ShellRejected" },
1184
+ { id: 6, name: "permission_denied", type: "ShellPermissionDenied" },
1185
+ { id: 7, name: "backgrounded", type: "ShellStreamBackgrounded" },
1186
+ { id: 8, name: "hook_context", type: "ShellStreamHookContext" },
1187
+ ],
1188
+ [{ name: "event", fields: ["stdout", "stderr", "exit", "start", "rejected", "permission_denied", "backgrounded", "hook_context"] }],
1189
+ )
1190
+
1191
+ addType(root, "GlobArgs", [
1192
+ { id: 1, name: "target_directory", type: "string" },
1193
+ { id: 2, name: "glob_pattern", type: "string" },
1194
+ ])
1195
+
1196
+ addType(root, "GlobResult", [
1197
+ { id: 1, name: "files", type: "string", repeated: true },
1198
+ { id: 2, name: "error", type: "string" },
1199
+ ])
1200
+
1201
+ // `args` is a map<string, google.protobuf.Value> on the wire (repeated map
1202
+ // entries at field #2). We capture each entry as raw bytes and decode them in
1203
+ // tools.ts via struct.decodeStructEntriesToJson.
1204
+ addType(root, "McpArgs", [
1205
+ { id: 1, name: "name", type: "string" },
1206
+ { id: 2, name: "args", type: "bytes", repeated: true },
1207
+ { id: 3, name: "tool_call_id", type: "string" },
1208
+ { id: 4, name: "provider_identifier", type: "string" },
1209
+ { id: 5, name: "tool_name", type: "string" },
1210
+ ])
1211
+
1212
+ addType(root, "McpTextContent", [{ id: 1, name: "text", type: "string" }])
1213
+ // Cursor CLI 2026.10.01 `agent/v1/mcp_exec_pb.js`: "McpImageContent|1 data 12|2 mime_type 9",
1214
+ // "McpToolResultContentItem|1 text #0 content|2 image #1 content".
1215
+ addType(root, "McpImageContent", [
1216
+ { id: 1, name: "data", type: "bytes" },
1217
+ { id: 2, name: "mime_type", type: "string" },
1218
+ ])
1219
+ addType(
1220
+ root,
1221
+ "McpToolResultContentItem",
1222
+ [
1223
+ { id: 1, name: "text", type: "McpTextContent" },
1224
+ { id: 2, name: "image", type: "McpImageContent" },
1225
+ ],
1226
+ [{ name: "content", fields: ["text", "image"] }],
1227
+ )
1228
+ addType(root, "McpRejected", [
1229
+ { id: 1, name: "reason", type: "string" },
1230
+ { id: 2, name: "is_readonly", type: "bool" },
1231
+ ])
1232
+ addType(root, "McpPermissionDenied", [
1233
+ { id: 1, name: "error", type: "string" },
1234
+ { id: 2, name: "is_readonly", type: "bool" },
1235
+ ])
1236
+ addType(root, "McpSuccess", [
1237
+ { id: 1, name: "content", type: "McpToolResultContentItem", repeated: true },
1238
+ { id: 2, name: "is_error", type: "bool" },
1239
+ ])
1240
+ addType(root, "McpError", [
1241
+ { id: 1, name: "error", type: "string" },
1242
+ // Server-side CallDynamicTool validation uses a title (#1) and detail (#2).
1243
+ { id: 2, name: "detail", type: "string" },
1244
+ ])
1245
+ addType(
1246
+ root,
1247
+ "McpResult",
1248
+ [
1249
+ { id: 1, name: "success", type: "McpSuccess" },
1250
+ { id: 2, name: "error", type: "McpError" },
1251
+ { id: 3, name: "rejected", type: "McpRejected" },
1252
+ { id: 4, name: "permission_denied", type: "McpPermissionDenied" },
1253
+ ],
1254
+ [{ name: "result", fields: ["success", "error", "rejected", "permission_denied"] }],
1255
+ )
1256
+
1257
+ // McpToolDefinition — agent.v1 shape for RequestContext.tools (#7) and
1258
+ // AgentRunRequest.mcp_tools (#4). Defined early so RequestContext* can
1259
+ // reference it.
1260
+ addType(root, "McpToolDefinition", [
1261
+ { id: 1, name: "name", type: "string" },
1262
+ { id: 2, name: "description", type: "string" },
1263
+ { id: 3, name: "input_schema", type: "bytes" },
1264
+ { id: 4, name: "provider_identifier", type: "string" },
1265
+ { id: 5, name: "tool_name", type: "string" },
1266
+ ])
1267
+
1268
+ // ── MCP resource exec (fields 17/18) ──
1269
+ // agent.v1 ListMcpResourcesExecArgs/Result, ReadMcpResourceExecArgs/Result.
1270
+ // Native Cursor exec variants: no descriptor, no advertisement, and Cursor
1271
+ // may emit either unsolicited regardless of what this provider advertises
1272
+ // (tasks/plans/fix-cursor-mcp-resource-exec.md, Correction 2). Under Option
1273
+ // B, list_mcp_resources/read_mcp_resource execute through the ordinary
1274
+ // field-11 MCP path via the alias rules below, so any 17/18 that still
1275
+ // reaches this provider gets the total fallback built in tools.ts.
1276
+ addType(root, "SmartModeApproval", [
1277
+ { id: 1, name: "request_id", type: "string" },
1278
+ { id: 2, name: "reason", type: "string" },
1279
+ ])
1280
+ addType(root, "OutputLocation", [
1281
+ { id: 1, name: "file_path", type: "string" },
1282
+ { id: 2, name: "size_bytes", type: "int64" },
1283
+ { id: 3, name: "line_count", type: "int64" },
1284
+ ])
1285
+
1286
+ addType(root, "ListMcpResourcesExecArgs", [
1287
+ { id: 1, name: "server", type: "string" },
1288
+ ])
1289
+ {
1290
+ const t = new protobuf.Type("ListMcpResourcesExecResult_McpResource")
1291
+ t.add(new protobuf.Field("uri", 1, "string"))
1292
+ t.add(new protobuf.Field("name", 2, "string"))
1293
+ t.add(new protobuf.Field("description", 3, "string"))
1294
+ t.add(new protobuf.Field("mime_type", 4, "string"))
1295
+ t.add(new protobuf.Field("server", 5, "string"))
1296
+ t.add(new protobuf.MapField("annotations", 6, "string", "string"))
1297
+ root.add(t)
1298
+ }
1299
+ addType(root, "ListMcpResourcesSuccess", [
1300
+ { id: 1, name: "resources", type: "ListMcpResourcesExecResult_McpResource", repeated: true },
1301
+ ])
1302
+ addType(root, "ListMcpResourcesError", [{ id: 1, name: "error", type: "string" }])
1303
+ addType(root, "ListMcpResourcesRejected", [{ id: 1, name: "reason", type: "string" }])
1304
+ addType(
1305
+ root,
1306
+ "ListMcpResourcesExecResult",
1307
+ [
1308
+ { id: 1, name: "success", type: "ListMcpResourcesSuccess" },
1309
+ { id: 2, name: "error", type: "ListMcpResourcesError" },
1310
+ { id: 3, name: "rejected", type: "ListMcpResourcesRejected" },
1311
+ ],
1312
+ [{ name: "result", fields: ["success", "error", "rejected"] }],
1313
+ )
1314
+
1315
+ addType(root, "ReadMcpResourceExecArgs", [
1316
+ { id: 1, name: "server", type: "string" },
1317
+ { id: 2, name: "uri", type: "string" },
1318
+ { id: 3, name: "download_path", type: "string" },
1319
+ { id: 4, name: "tool_call_id", type: "string" },
1320
+ { id: 5, name: "smart_mode_approval", type: "SmartModeApproval" },
1321
+ ])
1322
+ {
1323
+ // Interleaved field order matches agent.proto exactly: the text/blob oneof
1324
+ // (5/6) sits between the shared metadata (1-4) and the trailing
1325
+ // annotations/download/output fields (7-9).
1326
+ const t = new protobuf.Type("ReadMcpResourceSuccess")
1327
+ t.add(new protobuf.Field("uri", 1, "string"))
1328
+ t.add(new protobuf.Field("name", 2, "string"))
1329
+ t.add(new protobuf.Field("description", 3, "string"))
1330
+ t.add(new protobuf.Field("mime_type", 4, "string"))
1331
+ t.add(new protobuf.Field("text", 5, "string"))
1332
+ t.add(new protobuf.Field("blob", 6, "bytes"))
1333
+ t.add(new protobuf.MapField("annotations", 7, "string", "string"))
1334
+ t.add(new protobuf.Field("download_path", 8, "string"))
1335
+ t.add(new protobuf.Field("output_location", 9, "OutputLocation"))
1336
+ t.add(new protobuf.OneOf("content", ["text", "blob"]))
1337
+ root.add(t)
1338
+ }
1339
+ addType(root, "ReadMcpResourceError", [
1340
+ { id: 1, name: "uri", type: "string" },
1341
+ { id: 2, name: "error", type: "string" },
1342
+ ])
1343
+ // Cursor quirk: on `rejected` only `reason` is set — `uri` (field 1) is
1344
+ // deliberately left empty. Mirror this; do not backfill uri on encode.
1345
+ addType(root, "ReadMcpResourceRejected", [
1346
+ { id: 1, name: "uri", type: "string" },
1347
+ { id: 2, name: "reason", type: "string" },
1348
+ ])
1349
+ addType(root, "ReadMcpResourceNotFound", [{ id: 1, name: "uri", type: "string" }])
1350
+ addType(
1351
+ root,
1352
+ "ReadMcpResourceExecResult",
1353
+ [
1354
+ { id: 1, name: "success", type: "ReadMcpResourceSuccess" },
1355
+ { id: 2, name: "error", type: "ReadMcpResourceError" },
1356
+ { id: 3, name: "rejected", type: "ReadMcpResourceRejected" },
1357
+ { id: 4, name: "not_found", type: "ReadMcpResourceNotFound" },
1358
+ ],
1359
+ [{ name: "result", fields: ["success", "error", "rejected", "not_found"] }],
1360
+ )
1361
+
1362
+ // ── request_context (#10): server-initiated setup probe ──
1363
+ // At the start of an agent turn the server sends ExecServerMessage
1364
+ // {request_context_args} to ask the client for workspace/env/tool context.
1365
+ // The reply is ExecClientMessage {request_context_result{success{request_context}}}.
1366
+ // If we don't reply, the server blocks on heartbeats forever (the "times out,
1367
+ // no response" symptom). We populate a minimal-but-real RequestContext — env
1368
+ // (workspace/shell/os) plus the tool list echoed into #7 tools (same
1369
+ // McpToolDefinition shape as AgentRunRequest #4 mcp_tools) so the model keeps
1370
+ // the tools opencode advertised.
1371
+ addType(root, "RequestContextArgs", [
1372
+ { id: 2, name: "notes_session_id", type: "string" },
1373
+ { id: 3, name: "workspace_id", type: "string" },
1374
+ { id: 7, name: "use_cached", type: "bool" },
1375
+ ])
1376
+
1377
+ addType(root, "RequestContextEnv", [
1378
+ { id: 1, name: "os_version", type: "string" },
1379
+ { id: 2, name: "workspace_paths", type: "string", repeated: true },
1380
+ { id: 3, name: "shell", type: "string" },
1381
+ { id: 5, name: "sandbox_enabled", type: "bool" },
1382
+ { id: 7, name: "terminals_folder", type: "string" },
1383
+ { id: 10, name: "time_zone", type: "string" },
1384
+ { id: 11, name: "project_folder", type: "string" },
1385
+ { id: 12, name: "agent_transcripts_folder", type: "string" },
1386
+ { id: 14, name: "sandbox_supported", type: "bool" },
1387
+ { id: 20, name: "is_working_dir_home_dir", type: "bool" },
1388
+ { id: 21, name: "process_working_directory", type: "string" },
1389
+ ])
1390
+
1391
+ // Cursor applies a rule by its type: `global` is the CLI's `alwaysApply: true`
1392
+ // (its AGENTS.md / CLAUDE.md default). An untyped rule is only attachable on
1393
+ // request, so its content never reaches the model on its own.
1394
+ addType(root, "CursorRuleTypeGlobal", [])
1395
+ addType(root, "CursorRuleTypeFileGlobs", [
1396
+ { id: 1, name: "globs", type: "string", repeated: true },
1397
+ ])
1398
+ addType(root, "CursorRuleTypeAgentFetched", [
1399
+ { id: 1, name: "description", type: "string" },
1400
+ ])
1401
+ addType(root, "CursorRuleTypeManuallyAttached", [])
1402
+ addType(root, "CursorRuleType", [
1403
+ { id: 1, name: "global", type: "CursorRuleTypeGlobal" },
1404
+ { id: 2, name: "file_globbed", type: "CursorRuleTypeFileGlobs" },
1405
+ { id: 3, name: "agent_fetched", type: "CursorRuleTypeAgentFetched" },
1406
+ { id: 4, name: "manually_attached", type: "CursorRuleTypeManuallyAttached" },
1407
+ ], [{ name: "type", fields: ["global", "file_globbed", "agent_fetched", "manually_attached"] }])
1408
+ addType(root, "CursorRule", [
1409
+ { id: 1, name: "full_path", type: "string" },
1410
+ { id: 2, name: "content", type: "string" },
1411
+ { id: 3, name: "type", type: "CursorRuleType" },
1412
+ ])
1413
+
1414
+ addType(root, "RepositoryIndexingInfo", [
1415
+ { id: 1, name: "relative_workspace_path", type: "string" },
1416
+ { id: 2, name: "remote_urls", type: "string", repeated: true },
1417
+ { id: 3, name: "remote_names", type: "string", repeated: true },
1418
+ { id: 4, name: "repo_name", type: "string" },
1419
+ { id: 5, name: "repo_owner", type: "string" },
1420
+ { id: 6, name: "is_tracked", type: "bool" },
1421
+ { id: 7, name: "is_local", type: "bool" },
1422
+ { id: 9, name: "workspace_uri", type: "string" },
1423
+ ])
1424
+
1425
+ addType(root, "GitRepoInfo", [
1426
+ { id: 1, name: "path", type: "string" },
1427
+ { id: 2, name: "status", type: "string" },
1428
+ { id: 3, name: "branch_name", type: "string" },
1429
+ { id: 4, name: "remote_url", type: "string" },
1430
+ ])
1431
+
1432
+ addType(root, "AgentSkill", [
1433
+ { id: 1, name: "full_path", type: "string" },
1434
+ { id: 2, name: "content", type: "string" },
1435
+ { id: 3, name: "description", type: "string" },
1436
+ ])
1437
+
1438
+ addType(root, "CustomSubagent", [
1439
+ { id: 1, name: "full_path", type: "string" },
1440
+ { id: 2, name: "name", type: "string" },
1441
+ { id: 3, name: "description", type: "string" },
1442
+ { id: 6, name: "prompt", type: "string" },
1443
+ ])
1444
+
1445
+ // Nested MCP filesystem / meta-tool shapes (agent.v1). Must be defined
1446
+ // before RequestContextPayload / RequestContext reference them.
1447
+ addType(root, "McpFsToolDescriptor", [
1448
+ { id: 1, name: "tool_name", type: "string" },
1449
+ { id: 3, name: "description", type: "string" },
1450
+ { id: 4, name: "input_schema", type: "bytes" },
1451
+ ])
1452
+
1453
+ addType(root, "McpDescriptor", [
1454
+ { id: 1, name: "server_name", type: "string" },
1455
+ { id: 2, name: "server_identifier", type: "string" },
1456
+ { id: 5, name: "tools", type: "McpFsToolDescriptor", repeated: true },
1457
+ ])
1458
+
1459
+ addType(root, "McpFileSystemOptions", [
1460
+ { id: 1, name: "enabled", type: "bool" },
1461
+ { id: 2, name: "workspace_project_dir", type: "string" },
1462
+ { id: 3, name: "mcp_descriptors", type: "McpDescriptor", repeated: true },
1463
+ ])
1464
+
1465
+ addType(root, "McpMetaToolOptions", [
1466
+ { id: 1, name: "enabled", type: "bool" },
1467
+ { id: 2, name: "mcp_descriptors", type: "McpDescriptor", repeated: true },
1468
+ ])
1469
+
1470
+ addType(root, "PermissionsAutoRunInstructions", [
1471
+ { id: 1, name: "allow_instructions", type: "string", repeated: true },
1472
+ { id: 2, name: "block_instructions", type: "string", repeated: true },
1473
+ ])
1474
+
1475
+ addType(root, "RequestContextPayload", [
1476
+ { id: 2, name: "rules", type: "CursorRule", repeated: true },
1477
+ { id: 4, name: "env", type: "RequestContextEnv" },
1478
+ { id: 6, name: "repository_info", type: "RepositoryIndexingInfo", repeated: true },
1479
+ { id: 7, name: "tools", type: "McpToolDefinition", repeated: true },
1480
+ { id: 11, name: "git_repos", type: "GitRepoInfo", repeated: true },
1481
+ { id: 13, name: "project_layouts", type: "LsDirectoryTreeNode", repeated: true },
1482
+ { id: 17, name: "web_search_enabled", type: "bool" },
1483
+ { id: 22, name: "custom_subagents", type: "CustomSubagent", repeated: true },
1484
+ { id: 23, name: "mcp_file_system_options", type: "McpFileSystemOptions" },
1485
+ { id: 24, name: "web_fetch_enabled", type: "bool" },
1486
+ { id: 25, name: "hooks_additional_context", type: "string" },
1487
+ { id: 29, name: "agent_skills", type: "AgentSkill", repeated: true },
1488
+ { id: 33, name: "git_repo_info_complete", type: "bool" },
1489
+ { id: 34, name: "mcp_meta_tool_options", type: "McpMetaToolOptions" },
1490
+ { id: 36, name: "mcp_info_complete", type: "bool" },
1491
+ { id: 39, name: "rules_info_complete", type: "bool" },
1492
+ { id: 40, name: "env_info_complete", type: "bool" },
1493
+ { id: 41, name: "repository_info_complete", type: "bool" },
1494
+ { id: 42, name: "custom_subagents_info_complete", type: "bool" },
1495
+ { id: 43, name: "agent_skills_info_complete", type: "bool" },
1496
+ { id: 44, name: "mcp_file_system_info_complete", type: "bool" },
1497
+ { id: 45, name: "git_status_info_complete", type: "bool" },
1498
+ { id: 46, name: "user_permissions_auto_run", type: "PermissionsAutoRunInstructions" },
1499
+ { id: 47, name: "project_permissions_auto_run", type: "PermissionsAutoRunInstructions" },
1500
+ ])
1501
+
1502
+ addType(root, "RequestContextSuccess", [
1503
+ { id: 1, name: "request_context", type: "RequestContextPayload" },
1504
+ ])
1505
+
1506
+ // Allowlist prechecks (exec #41/#42/#43): Cursor asks whether a shell
1507
+ // command / MCP tool / web fetch would be permitted before it emits the
1508
+ // real call. Without an args frame this is decodable, but its exec id is
1509
+ // then stranded — the server waits forever for the matching result.
1510
+ addType(root, "ShellAllowlistPrecheckArgs", [
1511
+ { id: 1, name: "command", type: "string" },
1512
+ { id: 2, name: "working_directory", type: "string" },
1513
+ { id: 3, name: "parsing_result", type: "bytes" },
1514
+ { id: 4, name: "classifier_result", type: "bytes" },
1515
+ { id: 5, name: "tool_call_id", type: "string" },
1516
+ ])
1517
+ addType(root, "McpAllowlistPrecheckArgs", [
1518
+ { id: 1, name: "provider_identifier", type: "string" },
1519
+ { id: 2, name: "tool_name", type: "string" },
1520
+ { id: 3, name: "tool_call_id", type: "string" },
1521
+ ])
1522
+ addType(root, "WebFetchAllowlistPrecheckArgs", [
1523
+ { id: 1, name: "url", type: "string" },
1524
+ { id: 2, name: "tool_call_id", type: "string" },
1525
+ ])
1526
+
1527
+ addType(root, "RequestContextResult", [
1528
+ { id: 1, name: "success", type: "RequestContextSuccess" },
1529
+ ])
1530
+
1531
+ // Cursor probes MCP server availability before emitting an MCP-backed tool
1532
+ // call. OpenCode owns those servers, so answer from the live host catalog
1533
+ // rather than surfacing this as a user tool.
1534
+ addType(root, "McpStateExecArgs", [
1535
+ { id: 1, name: "server_identifiers", type: "string", repeated: true },
1536
+ { id: 2, name: "kick_only", type: "bool" },
1537
+ ])
1538
+ addType(root, "McpInstructions", [
1539
+ { id: 1, name: "server_name", type: "string" },
1540
+ { id: 2, name: "instructions", type: "string" },
1541
+ { id: 3, name: "server_identifier", type: "string" },
1542
+ ])
1543
+ addType(root, "McpStateServer", [
1544
+ { id: 1, name: "server_name", type: "string" },
1545
+ { id: 2, name: "server_identifier", type: "string" },
1546
+ { id: 3, name: "plugin", type: "string" },
1547
+ { id: 4, name: "marketplace", type: "string" },
1548
+ // Unlike McpDescriptor (#23/#34), exec #36 returns the full canonical
1549
+ // McpToolDefinition shape, including composite name and provider identity.
1550
+ { id: 5, name: "tools", type: "McpToolDefinition", repeated: true },
1551
+ { id: 6, name: "instructions", type: "McpInstructions", repeated: true },
1552
+ { id: 7, name: "status", type: "string" },
1553
+ ])
1554
+ addType(root, "McpStateSuccess", [
1555
+ { id: 1, name: "servers", type: "McpStateServer", repeated: true },
1556
+ ])
1557
+ addType(root, "McpStateError", [{ id: 1, name: "error", type: "string" }])
1558
+ addType(root, "McpStateRejected", [{ id: 1, name: "reason", type: "string" }])
1559
+ addType(
1560
+ root,
1561
+ "McpStateExecResult",
1562
+ [
1563
+ { id: 1, name: "success", type: "McpStateSuccess" },
1564
+ { id: 2, name: "error", type: "McpStateError" },
1565
+ { id: 3, name: "rejected", type: "McpStateRejected" },
1566
+ ],
1567
+ [{ name: "result", fields: ["success", "error", "rejected"] }],
1568
+ )
1569
+
1570
+ // ExecServerMessage — server asks us to execute a tool
1571
+ addType(
1572
+ root,
1573
+ "ExecServerMessage",
1574
+ [
1575
+ { id: 1, name: "id", type: "uint32" },
1576
+ { id: 15, name: "exec_id", type: "string" },
1577
+ { id: 19, name: "span", type: "string" },
1578
+ { id: 2, name: "shell_args", type: "ShellArgs" },
1579
+ { id: 3, name: "write_args", type: "WriteArgs" },
1580
+ { id: 4, name: "delete_args", type: "DeleteArgs" },
1581
+ { id: 5, name: "grep_args", type: "GrepArgs" },
1582
+ { id: 7, name: "read_args", type: "ReadArgs" },
1583
+ { id: 8, name: "ls_args", type: "LsArgs" },
1584
+ { id: 10, name: "request_context_args", type: "RequestContextArgs" },
1585
+ { id: 11, name: "mcp_args", type: "McpArgs" },
1586
+ { id: 14, name: "shell_stream_args", type: "ShellArgs" },
1587
+ { id: 16, name: "background_shell_spawn_args", type: "BackgroundShellSpawnArgs" },
1588
+ { id: 17, name: "list_mcp_resources_exec_args", type: "ListMcpResourcesExecArgs" },
1589
+ { id: 18, name: "read_mcp_resource_exec_args", type: "ReadMcpResourceExecArgs" },
1590
+ { id: 28, name: "subagent_args", type: "SubagentArgs" },
1591
+ { id: 36, name: "mcp_state_exec_args", type: "McpStateExecArgs" },
1592
+ { id: 41, name: "shell_allowlist_precheck_args", type: "ShellAllowlistPrecheckArgs" },
1593
+ { id: 42, name: "mcp_allowlist_precheck_args", type: "McpAllowlistPrecheckArgs" },
1594
+ { id: 43, name: "web_fetch_allowlist_precheck_args", type: "WebFetchAllowlistPrecheckArgs" },
1595
+ { id: 44, name: "git_diff_request", type: "GetDiffRequest" },
1596
+ { id: 45, name: "pi_read_args", type: "PiReadToolArgs" },
1597
+ { id: 46, name: "pi_bash_args", type: "PiBashToolArgs" },
1598
+ { id: 47, name: "pi_edit_args", type: "PiEditToolArgs" },
1599
+ { id: 48, name: "pi_write_args", type: "PiWriteArgs" },
1600
+ { id: 49, name: "pi_grep_args", type: "PiGrepToolArgs" },
1601
+ { id: 50, name: "pi_find_args", type: "PiFindToolArgs" },
1602
+ { id: 51, name: "pi_ls_args", type: "PiLsToolArgs" },
1603
+ { id: 55, name: "accept_hook_additional_contexts", type: "bool" },
1604
+ ],
1605
+ [{ name: "args", fields: [
1606
+ "shell_args", "write_args", "delete_args", "grep_args", "read_args", "ls_args",
1607
+ "request_context_args", "mcp_args", "shell_stream_args", "background_shell_spawn_args",
1608
+ "list_mcp_resources_exec_args", "read_mcp_resource_exec_args", "mcp_state_exec_args",
1609
+ "shell_allowlist_precheck_args", "mcp_allowlist_precheck_args", "web_fetch_allowlist_precheck_args",
1610
+ "subagent_args", "git_diff_request",
1611
+ "pi_read_args", "pi_bash_args", "pi_edit_args", "pi_write_args",
1612
+ "pi_grep_args", "pi_find_args", "pi_ls_args",
1613
+ ] }],
1614
+ )
1615
+
1616
+ // ExecClientMessage — client sends tool result back
1617
+ addType(
1618
+ root,
1619
+ "ExecClientMessage",
1620
+ [
1621
+ { id: 1, name: "id", type: "uint32" },
1622
+ { id: 15, name: "exec_id", type: "string" },
1623
+ { id: 39, name: "local_execution_time_ms", type: "uint64" },
1624
+ { id: 45, name: "hook_additional_contexts", type: "HookAdditionalContext", repeated: true },
1625
+ { id: 2, name: "shell_result", type: "ShellResult" },
1626
+ { id: 3, name: "write_result", type: "WriteResult" },
1627
+ { id: 4, name: "delete_result", type: "DeleteResult" },
1628
+ { id: 5, name: "grep_result", type: "GrepResult" },
1629
+ { id: 7, name: "read_result", type: "ReadResult" },
1630
+ { id: 8, name: "ls_result", type: "LsResult" },
1631
+ { id: 9, name: "diagnostics_result", type: "DiagnosticsResult" },
1632
+ { id: 10, name: "request_context_result", type: "RequestContextResult" },
1633
+ { id: 11, name: "mcp_result", type: "McpResult" },
1634
+ { id: 14, name: "shell_stream", type: "ShellStream" },
1635
+ { id: 16, name: "background_shell_spawn_result", type: "BackgroundShellSpawnResult" },
1636
+ { id: 17, name: "list_mcp_resources_exec_result", type: "ListMcpResourcesExecResult" },
1637
+ { id: 18, name: "read_mcp_resource_exec_result", type: "ReadMcpResourceExecResult" },
1638
+ { id: 20, name: "fetch_result", type: "FetchResult" },
1639
+ { id: 21, name: "record_screen_result", type: "RecordScreenResult" },
1640
+ { id: 22, name: "computer_use_result", type: "ComputerUseResult" },
1641
+ { id: 23, name: "write_shell_stdin_result", type: "WriteShellStdinResult" },
1642
+ { id: 28, name: "subagent_result", type: "SubagentResult" },
1643
+ { id: 29, name: "redacted_read_result", type: "ReadResult" },
1644
+ { id: 30, name: "force_background_shell_result", type: "ForceBackgroundShellResult" },
1645
+ { id: 31, name: "force_background_subagent_result", type: "ForceBackgroundSubagentResult" },
1646
+ { id: 36, name: "mcp_state_exec_result", type: "McpStateExecResult" },
1647
+ { id: 37, name: "subagent_await_result", type: "SubagentAwaitResult" },
1648
+ { id: 38, name: "smart_mode_classifier_result", type: "SmartModeClassifierResult" },
1649
+ { id: 40, name: "canvas_diagnostics_result", type: "CanvasDiagnosticsResult" },
1650
+ { id: 41, name: "shell_allowlist_precheck_result", type: "ShellAllowlistPrecheckResult" },
1651
+ { id: 42, name: "mcp_allowlist_precheck_result", type: "McpAllowlistPrecheckResult" },
1652
+ { id: 43, name: "web_fetch_allowlist_precheck_result", type: "WebFetchAllowlistPrecheckResult" },
1653
+ { id: 44, name: "git_diff_response", type: "GetDiffResponse" },
1654
+ { id: 46, name: "pi_read_result", type: "PiReadExecResult" },
1655
+ { id: 47, name: "pi_bash_result", type: "PiBashExecResult" },
1656
+ { id: 48, name: "pi_edit_result", type: "PiEditExecResult" },
1657
+ { id: 49, name: "pi_write_result", type: "PiWriteResult" },
1658
+ { id: 50, name: "pi_grep_result", type: "PiGrepExecResult" },
1659
+ { id: 51, name: "pi_find_result", type: "PiFindExecResult" },
1660
+ { id: 52, name: "pi_ls_result", type: "PiLsExecResult" },
1661
+ { id: 53, name: "conversation_search_result", type: "ConversationSearchResult" },
1662
+ { id: 54, name: "agent_store_conflict_result", type: "AgentStoreConflictResult" },
1663
+ { id: 55, name: "mini_swe_agent_bash_result", type: "ShellResult" },
1664
+ { id: 56, name: "adopt_result", type: "AdoptResult" },
1665
+ ],
1666
+ [{ name: "result", fields: [
1667
+ "shell_result", "write_result", "delete_result", "grep_result", "read_result", "ls_result",
1668
+ "diagnostics_result", "request_context_result", "mcp_result", "shell_stream", "background_shell_spawn_result",
1669
+ "list_mcp_resources_exec_result", "read_mcp_resource_exec_result",
1670
+ "fetch_result", "record_screen_result", "computer_use_result", "write_shell_stdin_result",
1671
+ "subagent_result", "redacted_read_result",
1672
+ "force_background_shell_result", "force_background_subagent_result",
1673
+ "mcp_state_exec_result",
1674
+ "subagent_await_result", "smart_mode_classifier_result", "canvas_diagnostics_result",
1675
+ "shell_allowlist_precheck_result", "mcp_allowlist_precheck_result", "web_fetch_allowlist_precheck_result",
1676
+ "git_diff_response",
1677
+ "pi_read_result", "pi_bash_result", "pi_edit_result", "pi_write_result",
1678
+ "pi_grep_result", "pi_find_result", "pi_ls_result",
1679
+ "conversation_search_result", "agent_store_conflict_result",
1680
+ "mini_swe_agent_bash_result", "adopt_result",
1681
+ ] }],
1682
+ )
1683
+
1684
+ addType(root, "ExecServerControlMessage", [
1685
+ { id: 1, name: "abort", type: "ExecServerAbort" },
1686
+ ])
1687
+
1688
+ addType(root, "ExecServerAbort", [
1689
+ { id: 1, name: "id", type: "uint32" },
1690
+ ])
1691
+
1692
+ // Client→server control (ACM #5). Distinct from ExecServerControlMessage (ASM #5).
1693
+ // After every exec result (especially multi-frame shell_stream), the real CLI
1694
+ // sends stream_close{id} — without it the cloud waits forever on shell execs.
1695
+ addType(root, "ExecClientStreamClose", [{ id: 1, name: "id", type: "uint32" }])
1696
+ addType(root, "ExecClientThrow", [
1697
+ { id: 1, name: "id", type: "uint32" },
1698
+ { id: 2, name: "error", type: "string" },
1699
+ ])
1700
+ addType(root, "ExecClientHeartbeat", [{ id: 1, name: "id", type: "uint32" }])
1701
+ addType(
1702
+ root,
1703
+ "ExecClientControlMessage",
1704
+ [
1705
+ { id: 1, name: "stream_close", type: "ExecClientStreamClose" },
1706
+ { id: 2, name: "throw", type: "ExecClientThrow" },
1707
+ { id: 3, name: "heartbeat", type: "ExecClientHeartbeat" },
1708
+ ],
1709
+ [{ name: "message", fields: ["stream_close", "throw", "heartbeat"] }],
1710
+ )
1711
+
1712
+ // ── Run request types ──
1713
+
1714
+ addType(root, "ParameterValue", [
1715
+ { id: 1, name: "id", type: "string" },
1716
+ { id: 2, name: "value", type: "string" },
1717
+ ])
1718
+
1719
+ addType(root, "RequestedModel", [
1720
+ { id: 1, name: "model_id", type: "string" },
1721
+ { id: 2, name: "max_mode", type: "bool" },
1722
+ { id: 3, name: "parameters", type: "ParameterValue", repeated: true },
1723
+ ])
1724
+
1725
+ addType(root, "SelectedImageDimension", [
1726
+ { id: 1, name: "width", type: "int32" },
1727
+ { id: 2, name: "height", type: "int32" },
1728
+ ])
1729
+ addType(root, "SelectedImageBlobIdWithData", [
1730
+ { id: 1, name: "blob_id", type: "bytes" },
1731
+ { id: 2, name: "data", type: "bytes" },
1732
+ ])
1733
+ addType(
1734
+ root,
1735
+ "SelectedImage",
1736
+ [
1737
+ { id: 1, name: "blob_id", type: "bytes" },
1738
+ { id: 8, name: "data", type: "bytes" },
1739
+ { id: 9, name: "blob_id_with_data", type: "SelectedImageBlobIdWithData" },
1740
+ { id: 2, name: "uuid", type: "string" },
1741
+ { id: 3, name: "path", type: "string" },
1742
+ { id: 4, name: "dimension", type: "SelectedImageDimension" },
1743
+ { id: 7, name: "mime_type", type: "string" },
1744
+ ],
1745
+ [{ name: "data_or_blob_id", fields: ["blob_id", "data", "blob_id_with_data"] }],
1746
+ )
1747
+ addType(root, "SelectedContext", [
1748
+ { id: 1, name: "selected_images", type: "SelectedImage", repeated: true },
1749
+ ])
1750
+
1751
+ // agent.v1.AgentMode — the client's current mode, sent on every user message.
1752
+ root.add(new protobuf.Enum("AgentMode", {
1753
+ AGENT_MODE_UNSPECIFIED: 0,
1754
+ AGENT_MODE_AGENT: 1,
1755
+ AGENT_MODE_ASK: 2,
1756
+ AGENT_MODE_PLAN: 3,
1757
+ AGENT_MODE_DEBUG: 4,
1758
+ AGENT_MODE_TRIAGE: 5,
1759
+ AGENT_MODE_PROJECT: 6,
1760
+ AGENT_MODE_MULTITASK: 7,
1761
+ AGENT_MODE_CUSTOM: 8,
1762
+ }))
1763
+
1764
+ addType(root, "UserMessage", [
1765
+ { id: 1, name: "text", type: "string" },
1766
+ { id: 2, name: "message_id", type: "string" },
1767
+ { id: 3, name: "selected_context", type: "SelectedContext" },
1768
+ { id: 4, name: "mode", type: "AgentMode" },
1769
+ ])
1770
+
1771
+ // RequestContext — UserMessageAction #2. Slim mcp_meta_tool_options names
1772
+ // the advertised catalog; AgentRunRequest.mcp_tools (#4) is empty on real turns.
1773
+ addType(root, "RequestContext", [
1774
+ { id: 2, name: "rules", type: "CursorRule", repeated: true },
1775
+ { id: 4, name: "env", type: "RequestContextEnv" },
1776
+ { id: 6, name: "repository_info", type: "RepositoryIndexingInfo", repeated: true },
1777
+ { id: 7, name: "tools", type: "McpToolDefinition", repeated: true },
1778
+ { id: 11, name: "git_repos", type: "GitRepoInfo", repeated: true },
1779
+ { id: 13, name: "project_layouts", type: "LsDirectoryTreeNode", repeated: true },
1780
+ { id: 17, name: "web_search_enabled", type: "bool" },
1781
+ { id: 22, name: "custom_subagents", type: "CustomSubagent", repeated: true },
1782
+ { id: 23, name: "mcp_file_system_options", type: "McpFileSystemOptions" },
1783
+ { id: 24, name: "web_fetch_enabled", type: "bool" },
1784
+ { id: 25, name: "hooks_additional_context", type: "string" },
1785
+ { id: 29, name: "agent_skills", type: "AgentSkill", repeated: true },
1786
+ { id: 33, name: "git_repo_info_complete", type: "bool" },
1787
+ { id: 34, name: "mcp_meta_tool_options", type: "McpMetaToolOptions" },
1788
+ { id: 36, name: "mcp_info_complete", type: "bool" },
1789
+ { id: 39, name: "rules_info_complete", type: "bool" },
1790
+ { id: 40, name: "env_info_complete", type: "bool" },
1791
+ { id: 41, name: "repository_info_complete", type: "bool" },
1792
+ { id: 42, name: "custom_subagents_info_complete", type: "bool" },
1793
+ { id: 43, name: "agent_skills_info_complete", type: "bool" },
1794
+ { id: 44, name: "mcp_file_system_info_complete", type: "bool" },
1795
+ { id: 45, name: "git_status_info_complete", type: "bool" },
1796
+ { id: 46, name: "user_permissions_auto_run", type: "PermissionsAutoRunInstructions" },
1797
+ { id: 47, name: "project_permissions_auto_run", type: "PermissionsAutoRunInstructions" },
1798
+ ])
1799
+
1800
+ addType(root, "UserMessageAction", [
1801
+ { id: 1, name: "user_message", type: "UserMessage" },
1802
+ { id: 2, name: "request_context", type: "RequestContext" },
1803
+ ])
1804
+
1805
+ addType(root, "ResumeAction", [
1806
+ // Cursor CLI sends an empty ResumeAction. Field #2 is available for a
1807
+ // refreshed RequestContext; the conversation id belongs to AgentRunRequest.
1808
+ { id: 2, name: "request_context", type: "RequestContext" },
1809
+ ])
1810
+
1811
+ addType(root, "CancelAction", [
1812
+ // Cursor CLI CancelAction field #1 is the interruption reason, not an id.
1813
+ { id: 1, name: "reason", type: "string" },
1814
+ ])
1815
+
1816
+ // Deferred answers for an AskQuestion the client already replied to with
1817
+ // `async`. `original_args` is kept as raw bytes so the exact AskQuestionArgs
1818
+ // Cursor sent — including fields this schema does not model — is echoed back
1819
+ // verbatim rather than re-encoded from a lossy decode.
1820
+ addType(root, "AsyncAskQuestionCompletionAction", [
1821
+ { id: 1, name: "original_tool_call_id", type: "string" },
1822
+ { id: 2, name: "original_args", type: "bytes" },
1823
+ { id: 3, name: "result", type: "AskQuestionResult" },
1824
+ ])
1825
+
1826
+ // Cursor CLI 2026.10.01 descriptors: system context is
1827
+ // injected into the current Run without submitting another user turn.
1828
+ addType(root, "SystemContextInjection", [
1829
+ { id: 1, name: "producer", type: "string" },
1830
+ { id: 2, name: "content", type: "string" },
1831
+ ])
1832
+ // Cursor SDK `Run.steer` / CLI steering payload: a user message added to the
1833
+ // running turn. `request_context` is optional and unused by the SDK.
1834
+ addType(root, "UserContextInjection", [
1835
+ { id: 1, name: "user_message", type: "UserMessage" },
1836
+ { id: 2, name: "request_context", type: "RequestContext" },
1837
+ ])
1838
+ addType(root, "InjectContextAction", [
1839
+ { id: 1, name: "injection_id", type: "string" },
1840
+ { id: 2, name: "expected_run_id", type: "string" },
1841
+ { id: 3, name: "user_context", type: "UserContextInjection" },
1842
+ { id: 4, name: "system_context", type: "SystemContextInjection" },
1843
+ ])
1844
+
1845
+ addType(
1846
+ root,
1847
+ "ConversationAction",
1848
+ [
1849
+ { id: 1, name: "user_message_action", type: "UserMessageAction" },
1850
+ { id: 2, name: "resume_action", type: "ResumeAction" },
1851
+ { id: 3, name: "cancel_action", type: "CancelAction" },
1852
+ { id: 8, name: "async_ask_question_completion_action", type: "AsyncAskQuestionCompletionAction" },
1853
+ { id: 19, name: "inject_context_action", type: "InjectContextAction" },
1854
+ ],
1855
+ [{
1856
+ name: "action",
1857
+ fields: [
1858
+ "user_message_action",
1859
+ "resume_action",
1860
+ "cancel_action",
1861
+ "async_ask_question_completion_action",
1862
+ "inject_context_action",
1863
+ ],
1864
+ }],
1865
+ )
1866
+
1867
+ // Seed ConversationStateStructure for turn 1 (system prompt as JSON strings
1868
+ // in #1). After the first conversation_checkpoint_update we echo opaque
1869
+ // server bytes instead — CLI's live structure uses blob-id bytes in #1/#8.
1870
+ addType(root, "AssistantMessage", [
1871
+ { id: 1, name: "text", type: "string" },
1872
+ ])
1873
+
1874
+ addType(
1875
+ root,
1876
+ "ConversationStep",
1877
+ [
1878
+ { id: 1, name: "assistant_message", type: "AssistantMessage" },
1879
+ ],
1880
+ [{ name: "message", fields: ["assistant_message"] }],
1881
+ )
1882
+
1883
+ addType(root, "AgentConversationTurn", [
1884
+ { id: 1, name: "user_message", type: "UserMessage" },
1885
+ { id: 2, name: "steps", type: "ConversationStep", repeated: true },
1886
+ ])
1887
+
1888
+ addType(
1889
+ root,
1890
+ "ConversationTurn",
1891
+ [
1892
+ { id: 1, name: "agent_conversation_turn", type: "AgentConversationTurn" },
1893
+ ],
1894
+ [{ name: "turn", fields: ["agent_conversation_turn"] }],
1895
+ )
1896
+
1897
+ // Cursor checkpoints carry authoritative context occupancy in field #5. The
1898
+ // provider still transports the complete checkpoint as opaque bytes; these
1899
+ // declarations support targeted diagnostics/tests without a re-encode.
1900
+ addType(root, "PromptTokenBreakdownCategory", [
1901
+ { id: 1, name: "id", type: "string" },
1902
+ { id: 2, name: "label", type: "string" },
1903
+ { id: 3, name: "estimated_tokens", type: "uint32" },
1904
+ { id: 4, name: "character_count", type: "uint32" },
1905
+ ])
1906
+ addType(root, "PromptTokenBreakdownSnapshot", [
1907
+ { id: 1, name: "total_used_tokens", type: "uint32" },
1908
+ { id: 2, name: "max_tokens", type: "uint32" },
1909
+ { id: 3, name: "categories", type: "PromptTokenBreakdownCategory", repeated: true },
1910
+ ])
1911
+ addType(root, "ConversationTokenDetails", [
1912
+ { id: 1, name: "used_tokens", type: "uint32" },
1913
+ { id: 2, name: "max_tokens", type: "uint32" },
1914
+ { id: 3, name: "breakdown", type: "PromptTokenBreakdownSnapshot" },
1915
+ ])
1916
+
1917
+ // Seed schema (JSON strings). AgentRunRequest.conversation_state remains
1918
+ // bytes, so live checkpoints are never decoded/re-encoded on transport.
1919
+ addType(root, "ConversationStateStructure", [
1920
+ { id: 1, name: "root_prompt_messages_json", type: "string", repeated: true },
1921
+ { id: 5, name: "token_details", type: "ConversationTokenDetails" },
1922
+ { id: 8, name: "turns", type: "ConversationTurn", repeated: true },
1923
+ ])
1924
+
1925
+ addType(root, "ModelDetails", [
1926
+ { id: 1, name: "model_id", type: "string" },
1927
+ ])
1928
+
1929
+ addType(root, "McpTools", [
1930
+ { id: 1, name: "mcp_tools", type: "McpToolDefinition", repeated: true },
1931
+ ])
1932
+
1933
+ addType(root, "AgentRunRequest", [
1934
+ { id: 1, name: "conversation_state", type: "bytes" },
1935
+ { id: 2, name: "action", type: "ConversationAction" },
1936
+ { id: 4, name: "mcp_tools", type: "McpTools" },
1937
+ { id: 5, name: "conversation_id", type: "string" },
1938
+ { id: 8, name: "custom_system_prompt", type: "string" },
1939
+ { 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
+ ])
1946
+
1947
+ // ── Client heartbeat ──
1948
+
1949
+ addType(root, "ClientHeartbeat", [])
1950
+
1951
+ // ── Interaction query/response channel ──
1952
+ // Cursor sends UI/integration requests outside the exec tool channel. Query
1953
+ // bodies stay opaque: this headless provider only needs their id + variant
1954
+ // to return a conservative typed response on the same Run stream.
1955
+ addType(
1956
+ root,
1957
+ "InteractionQuery",
1958
+ [
1959
+ { id: 1, name: "id", type: "uint32" },
1960
+ { id: 2, name: "web_search_request_query", type: "bytes" },
1961
+ { id: 3, name: "ask_question_interaction_query", type: "bytes" },
1962
+ { id: 4, name: "switch_mode_request_query", type: "bytes" },
1963
+ { id: 7, name: "create_plan_request_query", type: "bytes" },
1964
+ { id: 8, name: "setup_vm_environment_args", type: "bytes" },
1965
+ { id: 9, name: "web_fetch_request_query", type: "bytes" },
1966
+ { id: 10, name: "pr_management_request_query", type: "bytes" },
1967
+ { id: 11, name: "mcp_auth_request_query", type: "bytes" },
1968
+ { id: 12, name: "generate_image_request_query", type: "bytes" },
1969
+ { id: 13, name: "replace_env_args", type: "bytes" },
1970
+ { id: 14, name: "connect_scm_request_query", type: "bytes" },
1971
+ ],
1972
+ [{
1973
+ name: "query",
1974
+ fields: [
1975
+ "web_search_request_query", "ask_question_interaction_query",
1976
+ "switch_mode_request_query", "create_plan_request_query",
1977
+ "setup_vm_environment_args", "web_fetch_request_query",
1978
+ "pr_management_request_query", "mcp_auth_request_query",
1979
+ "generate_image_request_query", "replace_env_args",
1980
+ "connect_scm_request_query",
1981
+ ],
1982
+ }],
1983
+ )
1984
+
1985
+ addType(root, "WebSearchRequestApproved", [])
1986
+ addType(root, "WebSearchRequestRejected", [{ id: 1, name: "reason", type: "string" }])
1987
+ addType(root, "WebSearchRequestResponse", [
1988
+ { id: 1, name: "approved", type: "WebSearchRequestApproved" },
1989
+ { id: 2, name: "rejected", type: "WebSearchRequestRejected" },
1990
+ ], [{ name: "result", fields: ["approved", "rejected"] }])
1991
+
1992
+ addType(root, "AskQuestionRejected", [{ id: 1, name: "reason", type: "string" }])
1993
+ addType(root, "AskQuestionError", [{ id: 1, name: "error_message", type: "string" }])
1994
+ addType(root, "AskQuestionAsync", [])
1995
+ addType(root, "AskQuestionSuccessAnswer", [
1996
+ { id: 1, name: "question_id", type: "string" },
1997
+ { id: 2, name: "selected_option_ids", type: "string", repeated: true },
1998
+ { id: 3, name: "freeform_text", type: "string" },
1999
+ ])
2000
+ addType(root, "AskQuestionSuccess", [
2001
+ { id: 1, name: "answers", type: "AskQuestionSuccessAnswer", repeated: true },
2002
+ ])
2003
+ addType(root, "AskQuestionResult", [
2004
+ { id: 1, name: "success", type: "AskQuestionSuccess" },
2005
+ { id: 2, name: "error", type: "AskQuestionError" },
2006
+ { id: 3, name: "rejected", type: "AskQuestionRejected" },
2007
+ { id: 4, name: "async", type: "AskQuestionAsync" },
2008
+ ], [{ name: "result", fields: ["success", "error", "rejected", "async"] }])
2009
+ addType(root, "AskQuestionInteractionResponse", [
2010
+ { id: 1, name: "result", type: "AskQuestionResult" },
2011
+ ])
2012
+ // InteractionQuery keeps query bodies opaque (see the InteractionQuery
2013
+ // comment); this type decodes only the ask-question body on demand.
2014
+ addType(root, "AskQuestionInteractionQuery", [
2015
+ { id: 1, name: "args", type: "AskQuestionArgs" },
2016
+ { id: 2, name: "tool_call_id", type: "string" },
2017
+ ])
2018
+
2019
+ addType(root, "SwitchModeRequestApproved", [])
2020
+ addType(root, "SwitchModeRequestRejected", [{ id: 1, name: "reason", type: "string" }])
2021
+ addType(root, "SwitchModeRequestResponse", [
2022
+ { id: 1, name: "approved", type: "SwitchModeRequestApproved" },
2023
+ { id: 2, name: "rejected", type: "SwitchModeRequestRejected" },
2024
+ ], [{ name: "result", fields: ["approved", "rejected"] }])
2025
+ // InteractionQuery keeps query bodies opaque; decoded on demand like
2026
+ // AskQuestionInteractionQuery / CreatePlanRequestQuery.
2027
+ addType(root, "SwitchModeRequestQuery", [
2028
+ { id: 1, name: "args", type: "SwitchModeToolArgs" },
2029
+ ])
2030
+
2031
+ addType(root, "CreatePlanSuccess", [])
2032
+ addType(root, "CreatePlanError", [{ id: 1, name: "error", type: "string" }])
2033
+ addType(root, "CreatePlanResult", [
2034
+ { id: 1, name: "success", type: "CreatePlanSuccess" },
2035
+ { id: 2, name: "error", type: "CreatePlanError" },
2036
+ { id: 3, name: "plan_uri", type: "string" },
2037
+ ], [{ name: "result", fields: ["success", "error"] }])
2038
+ addType(root, "CreatePlanRequestResponse", [
2039
+ { id: 1, name: "result", type: "CreatePlanResult" },
2040
+ ])
2041
+ // InteractionQuery keeps query bodies opaque; decoded on demand like
2042
+ // AskQuestionInteractionQuery / GenerateImageRequestQuery.
2043
+ addType(root, "CreatePlanRequestQuery", [
2044
+ { id: 1, name: "args", type: "CreatePlanArgs" },
2045
+ { id: 2, name: "tool_call_id", type: "string" },
2046
+ ])
2047
+
2048
+ addType(root, "SetupVmEnvironmentSuccess", [])
2049
+ addType(root, "SetupVmEnvironmentResult", [
2050
+ { id: 1, name: "success", type: "SetupVmEnvironmentSuccess" },
2051
+ ], [{ name: "result", fields: ["success"] }])
2052
+
2053
+ addType(root, "WebFetchRequestApproved", [])
2054
+ addType(root, "WebFetchRequestRejected", [{ id: 1, name: "reason", type: "string" }])
2055
+ addType(root, "WebFetchRequestResponse", [
2056
+ { id: 1, name: "approved", type: "WebFetchRequestApproved" },
2057
+ { id: 2, name: "rejected", type: "WebFetchRequestRejected" },
2058
+ ], [{ name: "result", fields: ["approved", "rejected"] }])
2059
+
2060
+ addType(root, "PrManagementRejected", [{ id: 1, name: "reason", type: "string" }])
2061
+ addType(root, "PrManagementResult", [
2062
+ { id: 3, name: "rejected", type: "PrManagementRejected" },
2063
+ ], [{ name: "result", fields: ["rejected"] }])
2064
+
2065
+ addType(root, "McpAuthRequestApproved", [])
2066
+ addType(root, "McpAuthRequestRejected", [{ id: 1, name: "reason", type: "string" }])
2067
+ addType(root, "McpAuthRequestResponse", [
2068
+ { id: 1, name: "approved", type: "McpAuthRequestApproved" },
2069
+ { id: 2, name: "rejected", type: "McpAuthRequestRejected" },
2070
+ ], [{ name: "result", fields: ["approved", "rejected"] }])
2071
+
2072
+ addType(root, "GenerateImageRequestApproved", [{ id: 1, name: "description", type: "string" }])
2073
+ addType(root, "GenerateImageRequestRejected", [{ id: 1, name: "reason", type: "string" }])
2074
+ addType(root, "GenerateImageRequestResponse", [
2075
+ { id: 1, name: "approved", type: "GenerateImageRequestApproved" },
2076
+ { id: 2, name: "rejected", type: "GenerateImageRequestRejected" },
2077
+ ], [{ name: "result", fields: ["approved", "rejected"] }])
2078
+
2079
+ addType(root, "ReplaceEnvSuccess", [])
2080
+ addType(root, "ReplaceEnvFailure", [
2081
+ { id: 1, name: "error_message", type: "string" },
2082
+ { id: 2, name: "setup_logs", type: "string" },
2083
+ ])
2084
+ addType(root, "ReplaceEnvResult", [
2085
+ { id: 1, name: "success", type: "ReplaceEnvSuccess" },
2086
+ { id: 2, name: "failure", type: "ReplaceEnvFailure" },
2087
+ ], [{ name: "result", fields: ["success", "failure"] }])
2088
+
2089
+ addType(root, "ConnectScmRequestApproved", [])
2090
+ addType(root, "ConnectScmRequestRejected", [{ id: 1, name: "reason", type: "string" }])
2091
+ addType(root, "ConnectScmRequestFailed", [{ id: 1, name: "error", type: "string" }])
2092
+ addType(root, "ConnectScmRequestResponse", [
2093
+ { id: 1, name: "approved", type: "ConnectScmRequestApproved" },
2094
+ { id: 2, name: "rejected", type: "ConnectScmRequestRejected" },
2095
+ { id: 3, name: "failed", type: "ConnectScmRequestFailed" },
2096
+ ], [{ name: "result", fields: ["approved", "rejected", "failed"] }])
2097
+
2098
+ addType(
2099
+ root,
2100
+ "InteractionResponse",
2101
+ [
2102
+ { id: 1, name: "id", type: "uint32" },
2103
+ { id: 2, name: "web_search_request_response", type: "WebSearchRequestResponse" },
2104
+ { id: 3, name: "ask_question_interaction_response", type: "AskQuestionInteractionResponse" },
2105
+ { id: 4, name: "switch_mode_request_response", type: "SwitchModeRequestResponse" },
2106
+ { id: 7, name: "create_plan_request_response", type: "CreatePlanRequestResponse" },
2107
+ { id: 8, name: "setup_vm_environment_result", type: "SetupVmEnvironmentResult" },
2108
+ { id: 9, name: "web_fetch_request_response", type: "WebFetchRequestResponse" },
2109
+ { id: 10, name: "pr_management_result", type: "PrManagementResult" },
2110
+ { id: 11, name: "mcp_auth_request_response", type: "McpAuthRequestResponse" },
2111
+ { id: 12, name: "generate_image_request_response", type: "GenerateImageRequestResponse" },
2112
+ { id: 13, name: "replace_env_result", type: "ReplaceEnvResult" },
2113
+ { id: 14, name: "connect_scm_request_response", type: "ConnectScmRequestResponse" },
2114
+ ],
2115
+ [{
2116
+ name: "result",
2117
+ fields: [
2118
+ "web_search_request_response", "ask_question_interaction_response",
2119
+ "switch_mode_request_response", "create_plan_request_response",
2120
+ "setup_vm_environment_result", "web_fetch_request_response",
2121
+ "pr_management_result", "mcp_auth_request_response",
2122
+ "generate_image_request_response", "replace_env_result",
2123
+ "connect_scm_request_response",
2124
+ ],
2125
+ }],
2126
+ )
2127
+
2128
+ // ── KV blob store (cursor moves large payloads out-of-band via this channel) ──
2129
+ // Server sends KvServerMessage (AgentServerMessage #4); client MUST reply with
2130
+ // KvClientMessage (AgentClientMessage #3) on the same Run stream, echoing `id`.
2131
+ // If we don't ack set_blob / answer get_blob, the server hangs (endless
2132
+ // heartbeats, no response) — this was the "no response" root cause.
2133
+
2134
+ addType(root, "GetBlobArgs", [
2135
+ { id: 1, name: "blob_id", type: "bytes" },
2136
+ ])
2137
+ addType(root, "GetBlobResult", [
2138
+ { id: 1, name: "blob_data", type: "bytes" },
2139
+ ])
2140
+ addType(root, "SetBlobArgs", [
2141
+ { id: 1, name: "blob_id", type: "bytes" },
2142
+ { id: 2, name: "blob_data", type: "bytes" },
2143
+ ])
2144
+ addType(root, "SetBlobResult", [
2145
+ { id: 1, name: "error", type: "string" },
2146
+ ])
2147
+ addType(
2148
+ root,
2149
+ "KvServerMessage",
2150
+ [
2151
+ { id: 1, name: "id", type: "uint32" },
2152
+ { id: 2, name: "get_blob_args", type: "GetBlobArgs" },
2153
+ { id: 3, name: "set_blob_args", type: "SetBlobArgs" },
2154
+ ],
2155
+ [{ name: "message", fields: ["get_blob_args", "set_blob_args"] }],
2156
+ )
2157
+ addType(
2158
+ root,
2159
+ "KvClientMessage",
2160
+ [
2161
+ { id: 1, name: "id", type: "uint32" },
2162
+ { id: 2, name: "get_blob_result", type: "GetBlobResult" },
2163
+ { id: 3, name: "set_blob_result", type: "SetBlobResult" },
2164
+ ],
2165
+ [{ name: "message", fields: ["get_blob_result", "set_blob_result"] }],
2166
+ )
2167
+
2168
+ // ── AgentClientMessage ──
2169
+
2170
+ addType(
2171
+ root,
2172
+ "AgentClientMessage",
2173
+ [
2174
+ { id: 1, name: "run_request", type: "AgentRunRequest" },
2175
+ { id: 2, name: "exec_client_message", type: "ExecClientMessage" },
2176
+ { id: 3, name: "kv_client_message", type: "KvClientMessage" },
2177
+ { id: 4, name: "conversation_action", type: "ConversationAction" },
2178
+ { id: 5, name: "exec_client_control_message", type: "ExecClientControlMessage" },
2179
+ { id: 6, name: "interaction_response", type: "InteractionResponse" },
2180
+ { id: 7, name: "client_heartbeat", type: "ClientHeartbeat" },
2181
+ ],
2182
+ [{
2183
+ name: "message",
2184
+ fields: [
2185
+ "run_request",
2186
+ "exec_client_message",
2187
+ "kv_client_message",
2188
+ "conversation_action",
2189
+ "exec_client_control_message",
2190
+ "interaction_response",
2191
+ "client_heartbeat",
2192
+ ],
2193
+ }],
2194
+ )
2195
+
2196
+ // ── AgentServerMessage ──
2197
+
2198
+ addType(
2199
+ root,
2200
+ "AgentServerMessage",
2201
+ [
2202
+ { id: 1, name: "interaction_update", type: "InteractionUpdate" },
2203
+ { id: 2, name: "exec_server_message", type: "ExecServerMessage" },
2204
+ { id: 3, name: "conversation_checkpoint_update", type: "bytes" },
2205
+ { id: 4, name: "kv_server_message", type: "KvServerMessage" },
2206
+ { id: 5, name: "exec_server_control_message", type: "ExecServerControlMessage" },
2207
+ { id: 7, name: "interaction_query", type: "InteractionQuery" },
2208
+ ],
2209
+ [{ name: "message", fields: ["interaction_update", "exec_server_message", "conversation_checkpoint_update", "kv_server_message", "exec_server_control_message", "interaction_query"] }],
2210
+ )
2211
+
2212
+ // ── AvailableModels types ──
2213
+
2214
+ addType(root, "AvailableModelsRequest", [])
2215
+
2216
+ addType(root, "AvailableModelParameterDefinition", [
2217
+ { id: 1, name: "id", type: "string" },
2218
+ { id: 2, name: "values", type: "string", repeated: true },
2219
+ ])
2220
+
2221
+ addType(root, "AvailableModelParameterValue", [
2222
+ { id: 1, name: "id", type: "string" },
2223
+ { id: 2, name: "value", type: "string" },
2224
+ ])
2225
+
2226
+ addType(root, "AvailableModelVariant", [
2227
+ { id: 1, name: "parameter_values", type: "AvailableModelParameterValue", repeated: true },
2228
+ { id: 2, name: "display_name", type: "string" },
2229
+ { id: 3, name: "is_max_mode", type: "bool" },
2230
+ { id: 4, name: "is_default_max_config", type: "bool" },
2231
+ { id: 5, name: "is_default_non_max_config", type: "bool" },
2232
+ ])
2233
+
2234
+ addType(root, "AvailableModelEntry", [
2235
+ { id: 1, name: "name", type: "string" },
2236
+ { id: 2, name: "default_on", type: "bool" },
2237
+ { id: 5, name: "supports_agent", type: "bool" },
2238
+ { id: 9, name: "supports_thinking", type: "bool" },
2239
+ { id: 10, name: "supports_images", type: "bool" },
2240
+ { id: 14, name: "supports_max_mode", type: "bool" },
2241
+ { id: 15, name: "context_token_limit", type: "int32" },
2242
+ { id: 16, name: "context_token_limit_for_max_mode", type: "int32" },
2243
+ { id: 17, name: "client_display_name", type: "string" },
2244
+ { id: 18, name: "server_model_name", type: "string" },
2245
+ { id: 29, name: "parameter_definitions", type: "AvailableModelParameterDefinition", repeated: true },
2246
+ { id: 30, name: "variants", type: "AvailableModelVariant", repeated: true },
2247
+ ])
2248
+
2249
+ addType(root, "AvailableModelsResponse", [
2250
+ { id: 1, name: "model_names", type: "string", repeated: true },
2251
+ { id: 2, name: "models", type: "AvailableModelEntry", repeated: true },
2252
+ ])
2253
+
2254
+ return root
2255
+ }
2256
+
2257
+ // ── Singleton root ──
2258
+
2259
+ let _root: protobuf.Root | null = null
2260
+
2261
+ export function getMessageTypes(): protobuf.Root {
2262
+ if (!_root) {
2263
+ _root = createMessageTypes()
2264
+ }
2265
+ return _root
2266
+ }
2267
+
2268
+ export function encodeMessage(typeName: string, message: Record<string, unknown>): Uint8Array {
2269
+ const root = getMessageTypes()
2270
+ const type = root.lookupType(typeName)
2271
+ const err = type.verify(message)
2272
+ if (err) throw new Error(`Invalid message for ${typeName}: ${err}`)
2273
+ return type.encode(type.fromObject(message)).finish()
2274
+ }
2275
+
2276
+ export function decodeMessage<T = Record<string, unknown>>(
2277
+ typeName: string,
2278
+ data: Uint8Array,
2279
+ ): T {
2280
+ const root = getMessageTypes()
2281
+ const type = root.lookupType(typeName)
2282
+ const decoded = type.decode(data)
2283
+ return type.toObject(decoded, { defaults: true, json: true, longs: Number }) as T
2284
+ }
2285
+
2286
+ /** Decode without inventing absent proto defaults (used for byte-stable snapshots). */
2287
+ export function decodeMessageSparse<T = Record<string, unknown>>(
2288
+ typeName: string,
2289
+ data: Uint8Array,
2290
+ ): T {
2291
+ const root = getMessageTypes()
2292
+ const type = root.lookupType(typeName)
2293
+ const decoded = type.decode(data)
2294
+ return type.toObject(decoded, { json: true, longs: Number }) as T
2295
+ }
2296
+
2297
+ type RawField = {
2298
+ field: number
2299
+ wireType: number
2300
+ varint?: string
2301
+ fixed?: number | string
2302
+ bytes?: Uint8Array
2303
+ }
2304
+
2305
+ /** Schema-free walk of one message's top-level wire fields (no nested descent). */
2306
+ function readRawFields(buf: Uint8Array): RawField[] {
2307
+ const reader = protobuf.Reader.create(buf)
2308
+ const fields: RawField[] = []
2309
+ while (reader.pos < reader.len) {
2310
+ const tag = reader.uint32()
2311
+ const field = tag >>> 3
2312
+ const wireType = tag & 7
2313
+ switch (wireType) {
2314
+ case 0:
2315
+ fields.push({ field, wireType, varint: reader.uint64().toString() })
2316
+ break
2317
+ case 1:
2318
+ fields.push({ field, wireType, fixed: reader.fixed64().toString() })
2319
+ break
2320
+ case 2:
2321
+ fields.push({ field, wireType, bytes: reader.bytes() })
2322
+ break
2323
+ case 5:
2324
+ fields.push({ field, wireType, fixed: reader.fixed32() })
2325
+ break
2326
+ default:
2327
+ // Groups (3/4) aren't used on this wire; bail rather than desync the reader.
2328
+ return fields
2329
+ }
2330
+ }
2331
+ return fields
2332
+ }
2333
+
2334
+ /**
2335
+ * Diagnostic-only: walk AgentServerMessage.interaction_update(1).turn_ended(14)
2336
+ * without going through our hand-written TurnEnded schema, so a `cache_write`/
2337
+ * `reasoning_tokens` question can be checked against the raw wire instead of
2338
+ * guessed. Confirmed field ids 1-5 (input/output/cache_read/cache_write/
2339
+ * reasoning) decode correctly. Live captures routinely include `f4` explicitly
2340
+ * as `0` while `f3` (cache_read) and `f5` (reasoning) are non-zero — Cursor's
2341
+ * agent TurnEnded populates the field but does not report write counts here.
2342
+ */
2343
+ export function debugWalkTurnEnded(payload: Uint8Array): string {
2344
+ try {
2345
+ const top = readRawFields(payload)
2346
+ const iuField = top.find((f) => f.field === 1 && f.wireType === 2)
2347
+ if (!iuField?.bytes) return "(no interaction_update field)"
2348
+ const iu = readRawFields(iuField.bytes)
2349
+ const teField = iu.find((f) => f.field === 14 && f.wireType === 2)
2350
+ if (!teField?.bytes) return "(no turn_ended field in interaction_update)"
2351
+ const te = readRawFields(teField.bytes)
2352
+ return te
2353
+ .map(
2354
+ (f) =>
2355
+ `f${f.field}:wt${f.wireType}=${f.varint ?? f.fixed ?? (f.bytes ? `bytes[${f.bytes.length}]` : "?")}`,
2356
+ )
2357
+ .join(" ")
2358
+ } catch (e) {
2359
+ return `(walk failed: ${(e as Error).message})`
2360
+ }
2361
+ }
2362
+
2363
+ /**
2364
+ * Decode a protobuf sub-message from a frame payload that wraps it in
2365
+ * a top-level field key + length varint. Skips the outer wrapper and
2366
+ * decodes the inner body as `typeName`.
2367
+ */
2368
+ export function decodeWrappedMessage<T = Record<string, unknown>>(
2369
+ typeName: string,
2370
+ data: Uint8Array,
2371
+ ): T {
2372
+ let offset = 1 // skip field key
2373
+ // skip varint length
2374
+ while (offset < data.length) {
2375
+ const b = data[offset] ?? 0
2376
+ offset++
2377
+ if (!(b & 0x80)) break
2378
+ }
2379
+ return decodeMessage<T>(typeName, data.subarray(offset))
2380
+ }