@code-yeongyu/senpi 2026.8.16 → 2026.8.17

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 (625) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/bun/cli.js +1 -0
  3. package/dist/bun/cli.js.map +1 -1
  4. package/dist/bun/register-cursor-agent.d.ts +2 -0
  5. package/dist/bun/register-cursor-agent.d.ts.map +1 -0
  6. package/dist/bun/register-cursor-agent.js +4 -0
  7. package/dist/bun/register-cursor-agent.js.map +1 -0
  8. package/dist/core/agent-session-services.js +2 -2
  9. package/dist/core/agent-session-services.js.map +1 -1
  10. package/dist/core/agent-session.d.ts +48 -5
  11. package/dist/core/agent-session.d.ts.map +1 -1
  12. package/dist/core/agent-session.js +239 -61
  13. package/dist/core/agent-session.js.map +1 -1
  14. package/dist/core/cursor-exec-bridge-session.d.ts +11 -0
  15. package/dist/core/cursor-exec-bridge-session.d.ts.map +1 -0
  16. package/dist/core/cursor-exec-bridge-session.js +17 -0
  17. package/dist/core/cursor-exec-bridge-session.js.map +1 -0
  18. package/dist/core/cursor-exec-bridge.d.ts +52 -0
  19. package/dist/core/cursor-exec-bridge.d.ts.map +1 -0
  20. package/dist/core/cursor-exec-bridge.js +196 -0
  21. package/dist/core/cursor-exec-bridge.js.map +1 -0
  22. package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.d.ts +28 -0
  23. package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.d.ts.map +1 -0
  24. package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.js +278 -0
  25. package/dist/core/extensions/builtin/cursor-cli-oauth/account-command.js.map +1 -0
  26. package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.d.ts +38 -0
  27. package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.d.ts.map +1 -0
  28. package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.js +75 -0
  29. package/dist/core/extensions/builtin/cursor-cli-oauth/accounts.js.map +1 -0
  30. package/dist/core/extensions/builtin/cursor-cli-oauth/affinity.d.ts +24 -0
  31. package/dist/core/extensions/builtin/cursor-cli-oauth/affinity.d.ts.map +1 -0
  32. package/dist/core/extensions/builtin/cursor-cli-oauth/affinity.js +64 -0
  33. package/dist/core/extensions/builtin/cursor-cli-oauth/affinity.js.map +1 -0
  34. package/dist/core/extensions/builtin/cursor-cli-oauth/diagnostics.d.ts +139 -0
  35. package/dist/core/extensions/builtin/cursor-cli-oauth/diagnostics.d.ts.map +1 -0
  36. package/dist/core/extensions/builtin/cursor-cli-oauth/diagnostics.js +362 -0
  37. package/dist/core/extensions/builtin/cursor-cli-oauth/diagnostics.js.map +1 -0
  38. package/dist/core/extensions/builtin/cursor-cli-oauth/errors.d.ts +19 -0
  39. package/dist/core/extensions/builtin/cursor-cli-oauth/errors.d.ts.map +1 -0
  40. package/dist/core/extensions/builtin/cursor-cli-oauth/errors.js +122 -0
  41. package/dist/core/extensions/builtin/cursor-cli-oauth/errors.js.map +1 -0
  42. package/dist/core/extensions/builtin/cursor-cli-oauth/executable.d.ts +30 -0
  43. package/dist/core/extensions/builtin/cursor-cli-oauth/executable.d.ts.map +1 -0
  44. package/dist/core/extensions/builtin/cursor-cli-oauth/executable.js +117 -0
  45. package/dist/core/extensions/builtin/cursor-cli-oauth/executable.js.map +1 -0
  46. package/dist/core/extensions/builtin/cursor-cli-oauth/failover.d.ts +39 -0
  47. package/dist/core/extensions/builtin/cursor-cli-oauth/failover.d.ts.map +1 -0
  48. package/dist/core/extensions/builtin/cursor-cli-oauth/failover.js +155 -0
  49. package/dist/core/extensions/builtin/cursor-cli-oauth/failover.js.map +1 -0
  50. package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.d.ts +89 -0
  51. package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.d.ts.map +1 -0
  52. package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.js +147 -0
  53. package/dist/core/extensions/builtin/cursor-cli-oauth/guardrails.js.map +1 -0
  54. package/dist/core/extensions/builtin/cursor-cli-oauth/home-store.d.ts +20 -0
  55. package/dist/core/extensions/builtin/cursor-cli-oauth/home-store.d.ts.map +1 -0
  56. package/dist/core/extensions/builtin/cursor-cli-oauth/home-store.js +99 -0
  57. package/dist/core/extensions/builtin/cursor-cli-oauth/home-store.js.map +1 -0
  58. package/dist/core/extensions/builtin/cursor-cli-oauth/index.d.ts +25 -0
  59. package/dist/core/extensions/builtin/cursor-cli-oauth/index.d.ts.map +1 -0
  60. package/dist/core/extensions/builtin/cursor-cli-oauth/index.js +67 -0
  61. package/dist/core/extensions/builtin/cursor-cli-oauth/index.js.map +1 -0
  62. package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts +27 -0
  63. package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts.map +1 -0
  64. package/dist/core/extensions/builtin/cursor-cli-oauth/models.js +242 -0
  65. package/dist/core/extensions/builtin/cursor-cli-oauth/models.js.map +1 -0
  66. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.d.ts +73 -0
  67. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.d.ts.map +1 -0
  68. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.js +330 -0
  69. package/dist/core/extensions/builtin/cursor-cli-oauth/oauth-login.js.map +1 -0
  70. package/dist/core/extensions/builtin/cursor-cli-oauth/session-router.d.ts +94 -0
  71. package/dist/core/extensions/builtin/cursor-cli-oauth/session-router.d.ts.map +1 -0
  72. package/dist/core/extensions/builtin/cursor-cli-oauth/session-router.js +248 -0
  73. package/dist/core/extensions/builtin/cursor-cli-oauth/session-router.js.map +1 -0
  74. package/dist/core/extensions/builtin/cursor-cli-oauth/settings.d.ts +35 -0
  75. package/dist/core/extensions/builtin/cursor-cli-oauth/settings.d.ts.map +1 -0
  76. package/dist/core/extensions/builtin/cursor-cli-oauth/settings.js +186 -0
  77. package/dist/core/extensions/builtin/cursor-cli-oauth/settings.js.map +1 -0
  78. package/dist/core/extensions/builtin/cursor-cli-oauth/spawn-args.d.ts +11 -0
  79. package/dist/core/extensions/builtin/cursor-cli-oauth/spawn-args.d.ts.map +1 -0
  80. package/dist/core/extensions/builtin/cursor-cli-oauth/spawn-args.js +17 -0
  81. package/dist/core/extensions/builtin/cursor-cli-oauth/spawn-args.js.map +1 -0
  82. package/dist/core/extensions/builtin/cursor-cli-oauth/stream-parser.d.ts +104 -0
  83. package/dist/core/extensions/builtin/cursor-cli-oauth/stream-parser.d.ts.map +1 -0
  84. package/dist/core/extensions/builtin/cursor-cli-oauth/stream-parser.js +301 -0
  85. package/dist/core/extensions/builtin/cursor-cli-oauth/stream-parser.js.map +1 -0
  86. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts +26 -0
  87. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.d.ts.map +1 -0
  88. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js +587 -0
  89. package/dist/core/extensions/builtin/cursor-cli-oauth/stream.js.map +1 -0
  90. package/dist/core/extensions/builtin/cursor-cli-oauth/transport.d.ts +43 -0
  91. package/dist/core/extensions/builtin/cursor-cli-oauth/transport.d.ts.map +1 -0
  92. package/dist/core/extensions/builtin/cursor-cli-oauth/transport.js +196 -0
  93. package/dist/core/extensions/builtin/cursor-cli-oauth/transport.js.map +1 -0
  94. package/dist/core/extensions/builtin/goal/channel-state-subscriptions.d.ts +9 -0
  95. package/dist/core/extensions/builtin/goal/channel-state-subscriptions.d.ts.map +1 -0
  96. package/dist/core/extensions/builtin/goal/channel-state-subscriptions.js +18 -0
  97. package/dist/core/extensions/builtin/goal/channel-state-subscriptions.js.map +1 -0
  98. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  99. package/dist/core/extensions/builtin/goal/monitor-continuation.js +10 -9
  100. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  101. package/dist/core/extensions/builtin/index.d.ts.map +1 -1
  102. package/dist/core/extensions/builtin/index.js +5 -2
  103. package/dist/core/extensions/builtin/index.js.map +1 -1
  104. package/dist/core/extensions/builtin/loop-guard/detectors.d.ts.map +1 -1
  105. package/dist/core/extensions/builtin/loop-guard/detectors.js +21 -3
  106. package/dist/core/extensions/builtin/loop-guard/detectors.js.map +1 -1
  107. package/dist/core/extensions/builtin/loop-guard/escalation.d.ts +24 -0
  108. package/dist/core/extensions/builtin/loop-guard/escalation.d.ts.map +1 -0
  109. package/dist/core/extensions/builtin/loop-guard/escalation.js +77 -0
  110. package/dist/core/extensions/builtin/loop-guard/escalation.js.map +1 -0
  111. package/dist/core/extensions/builtin/loop-guard/index.d.ts.map +1 -1
  112. package/dist/core/extensions/builtin/loop-guard/index.js +98 -3
  113. package/dist/core/extensions/builtin/loop-guard/index.js.map +1 -1
  114. package/dist/core/extensions/builtin/loop-guard/notice.d.ts +9 -0
  115. package/dist/core/extensions/builtin/loop-guard/notice.d.ts.map +1 -1
  116. package/dist/core/extensions/builtin/loop-guard/notice.js +17 -0
  117. package/dist/core/extensions/builtin/loop-guard/notice.js.map +1 -1
  118. package/dist/core/extensions/builtin/loop-guard/policy.d.ts +4 -0
  119. package/dist/core/extensions/builtin/loop-guard/policy.d.ts.map +1 -1
  120. package/dist/core/extensions/builtin/loop-guard/policy.js +4 -0
  121. package/dist/core/extensions/builtin/loop-guard/policy.js.map +1 -1
  122. package/dist/core/extensions/builtin/loop-guard/renderer.d.ts +2 -0
  123. package/dist/core/extensions/builtin/loop-guard/renderer.d.ts.map +1 -1
  124. package/dist/core/extensions/builtin/loop-guard/renderer.js +11 -0
  125. package/dist/core/extensions/builtin/loop-guard/renderer.js.map +1 -1
  126. package/dist/core/extensions/builtin/loop-guard/tracker.d.ts +1 -1
  127. package/dist/core/extensions/builtin/loop-guard/tracker.d.ts.map +1 -1
  128. package/dist/core/extensions/builtin/loop-guard/tracker.js +3 -1
  129. package/dist/core/extensions/builtin/loop-guard/tracker.js.map +1 -1
  130. package/dist/core/extensions/builtin/monitor-state-event.d.ts +6 -0
  131. package/dist/core/extensions/builtin/monitor-state-event.d.ts.map +1 -1
  132. package/dist/core/extensions/builtin/monitor-state-event.js +10 -0
  133. package/dist/core/extensions/builtin/monitor-state-event.js.map +1 -1
  134. package/dist/core/model-resolver.js +1 -1
  135. package/dist/core/model-resolver.js.map +1 -1
  136. package/dist/core/model-runtime.d.ts +7 -2
  137. package/dist/core/model-runtime.d.ts.map +1 -1
  138. package/dist/core/model-runtime.js +14 -2
  139. package/dist/core/model-runtime.js.map +1 -1
  140. package/dist/core/prompt-templates.d.ts +5 -0
  141. package/dist/core/prompt-templates.d.ts.map +1 -1
  142. package/dist/core/prompt-templates.js +8 -9
  143. package/dist/core/prompt-templates.js.map +1 -1
  144. package/dist/core/provider-display-names.d.ts.map +1 -1
  145. package/dist/core/provider-display-names.js +1 -0
  146. package/dist/core/provider-display-names.js.map +1 -1
  147. package/dist/core/sdk.d.ts.map +1 -1
  148. package/dist/core/sdk.js +6 -0
  149. package/dist/core/sdk.js.map +1 -1
  150. package/dist/modes/interactive/editor-paste-transfer.d.ts +10 -0
  151. package/dist/modes/interactive/editor-paste-transfer.d.ts.map +1 -1
  152. package/dist/modes/interactive/editor-paste-transfer.js +16 -0
  153. package/dist/modes/interactive/editor-paste-transfer.js.map +1 -1
  154. package/dist/modes/interactive/interactive-mode.js +2 -2
  155. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  156. package/dist/modes/rpc/connection-handler.d.ts.map +1 -1
  157. package/dist/modes/rpc/connection-handler.js +32 -27
  158. package/dist/modes/rpc/connection-handler.js.map +1 -1
  159. package/dist/modes/rpc/jsonl.d.ts +6 -1
  160. package/dist/modes/rpc/jsonl.d.ts.map +1 -1
  161. package/dist/modes/rpc/jsonl.js +47 -14
  162. package/dist/modes/rpc/jsonl.js.map +1 -1
  163. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  164. package/dist/modes/rpc/multi-session-host.js +26 -4
  165. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  166. package/dist/modes/rpc/rpc-command-invocation.d.ts +5 -0
  167. package/dist/modes/rpc/rpc-command-invocation.d.ts.map +1 -0
  168. package/dist/modes/rpc/rpc-command-invocation.js +2 -0
  169. package/dist/modes/rpc/rpc-command-invocation.js.map +1 -0
  170. package/dist/modes/rpc/rpc-command-surface.d.ts +49 -0
  171. package/dist/modes/rpc/rpc-command-surface.d.ts.map +1 -0
  172. package/dist/modes/rpc/rpc-command-surface.js +40 -0
  173. package/dist/modes/rpc/rpc-command-surface.js.map +1 -0
  174. package/dist/modes/rpc/rpc-input-validation.d.ts +4 -0
  175. package/dist/modes/rpc/rpc-input-validation.d.ts.map +1 -0
  176. package/dist/modes/rpc/rpc-input-validation.js +20 -0
  177. package/dist/modes/rpc/rpc-input-validation.js.map +1 -0
  178. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  179. package/dist/modes/rpc/rpc-mode.js +12 -1
  180. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  181. package/dist/modes/rpc/rpc-types.d.ts +12 -12
  182. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  183. package/dist/modes/rpc/rpc-types.js.map +1 -1
  184. package/docs/providers.md +50 -1
  185. package/docs/rpc.md +78 -6
  186. package/docs/skills.md +14 -3
  187. package/node_modules/@bufbuild/protobuf/README.md +44 -0
  188. package/node_modules/@bufbuild/protobuf/dist/commonjs/clone.d.ts +6 -0
  189. package/node_modules/@bufbuild/protobuf/dist/commonjs/clone.js +66 -0
  190. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/boot.d.ts +1 -0
  191. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/boot.js +18 -0
  192. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/enum.d.ts +10 -0
  193. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/enum.js +31 -0
  194. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/extension.d.ts +9 -0
  195. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/extension.js +28 -0
  196. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/file.d.ts +1 -0
  197. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/file.js +18 -0
  198. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/index.d.ts +10 -0
  199. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/index.js +39 -0
  200. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/message.d.ts +10 -0
  201. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/message.js +24 -0
  202. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/service.d.ts +8 -0
  203. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/service.js +27 -0
  204. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/symbols.d.ts +140 -0
  205. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/symbols.js +43 -0
  206. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/types.d.ts +79 -0
  207. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv1/types.js +15 -0
  208. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/boot.d.ts +63 -0
  209. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/boot.js +105 -0
  210. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/embed.d.ts +43 -0
  211. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/embed.js +244 -0
  212. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/enum.d.ts +29 -0
  213. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/enum.js +58 -0
  214. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/extension.d.ts +9 -0
  215. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/extension.js +28 -0
  216. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/file.d.ts +7 -0
  217. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/file.js +35 -0
  218. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/index.d.ts +10 -0
  219. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/index.js +39 -0
  220. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/message.d.ts +15 -0
  221. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/message.js +24 -0
  222. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/restore-json-names.d.ts +5 -0
  223. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/restore-json-names.js +29 -0
  224. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/scalar.d.ts +9 -0
  225. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/scalar.js +67 -0
  226. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/service.d.ts +8 -0
  227. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/service.js +27 -0
  228. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/symbols.d.ts +145 -0
  229. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/symbols.js +74 -0
  230. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/types.d.ts +85 -0
  231. package/node_modules/@bufbuild/protobuf/dist/commonjs/codegenv2/types.js +15 -0
  232. package/node_modules/@bufbuild/protobuf/dist/commonjs/create.d.ts +9 -0
  233. package/node_modules/@bufbuild/protobuf/dist/commonjs/create.js +259 -0
  234. package/node_modules/@bufbuild/protobuf/dist/commonjs/descriptors.d.ts +634 -0
  235. package/node_modules/@bufbuild/protobuf/dist/commonjs/descriptors.js +53 -0
  236. package/node_modules/@bufbuild/protobuf/dist/commonjs/equals.d.ts +46 -0
  237. package/node_modules/@bufbuild/protobuf/dist/commonjs/equals.js +209 -0
  238. package/node_modules/@bufbuild/protobuf/dist/commonjs/extensions.d.ts +60 -0
  239. package/node_modules/@bufbuild/protobuf/dist/commonjs/extensions.js +168 -0
  240. package/node_modules/@bufbuild/protobuf/dist/commonjs/fields.d.ts +23 -0
  241. package/node_modules/@bufbuild/protobuf/dist/commonjs/fields.js +45 -0
  242. package/node_modules/@bufbuild/protobuf/dist/commonjs/from-binary.d.ts +48 -0
  243. package/node_modules/@bufbuild/protobuf/dist/commonjs/from-binary.js +247 -0
  244. package/node_modules/@bufbuild/protobuf/dist/commonjs/from-json.d.ts +73 -0
  245. package/node_modules/@bufbuild/protobuf/dist/commonjs/from-json.js +739 -0
  246. package/node_modules/@bufbuild/protobuf/dist/commonjs/index.d.ts +19 -0
  247. package/node_modules/@bufbuild/protobuf/dist/commonjs/index.js +55 -0
  248. package/node_modules/@bufbuild/protobuf/dist/commonjs/is-message.d.ts +7 -0
  249. package/node_modules/@bufbuild/protobuf/dist/commonjs/is-message.js +33 -0
  250. package/node_modules/@bufbuild/protobuf/dist/commonjs/json-value.d.ts +16 -0
  251. package/node_modules/@bufbuild/protobuf/dist/commonjs/json-value.js +15 -0
  252. package/node_modules/@bufbuild/protobuf/dist/commonjs/merge.d.ts +13 -0
  253. package/node_modules/@bufbuild/protobuf/dist/commonjs/merge.js +70 -0
  254. package/node_modules/@bufbuild/protobuf/dist/commonjs/package.json +4 -0
  255. package/node_modules/@bufbuild/protobuf/dist/commonjs/proto-int64.d.ts +98 -0
  256. package/node_modules/@bufbuild/protobuf/dist/commonjs/proto-int64.js +131 -0
  257. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/error.d.ts +9 -0
  258. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/error.js +36 -0
  259. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/guard.d.ts +20 -0
  260. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/guard.js +78 -0
  261. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/index.d.ts +8 -0
  262. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/index.js +41 -0
  263. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/names.d.ts +30 -0
  264. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/names.js +115 -0
  265. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/nested-types.d.ts +35 -0
  266. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/nested-types.js +110 -0
  267. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/path.d.ts +107 -0
  268. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/path.js +376 -0
  269. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect-check.d.ts +19 -0
  270. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect-check.js +268 -0
  271. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect-types.d.ts +217 -0
  272. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect-types.js +16 -0
  273. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect.d.ts +43 -0
  274. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/reflect.js +546 -0
  275. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/scalar.d.ts +43 -0
  276. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/scalar.js +129 -0
  277. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/unsafe.d.ts +39 -0
  278. package/node_modules/@bufbuild/protobuf/dist/commonjs/reflect/unsafe.js +149 -0
  279. package/node_modules/@bufbuild/protobuf/dist/commonjs/registry.d.ts +98 -0
  280. package/node_modules/@bufbuild/protobuf/dist/commonjs/registry.js +945 -0
  281. package/node_modules/@bufbuild/protobuf/dist/commonjs/to-binary.d.ts +24 -0
  282. package/node_modules/@bufbuild/protobuf/dist/commonjs/to-binary.js +199 -0
  283. package/node_modules/@bufbuild/protobuf/dist/commonjs/to-json.d.ts +67 -0
  284. package/node_modules/@bufbuild/protobuf/dist/commonjs/to-json.js +391 -0
  285. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/from-text.d.ts +35 -0
  286. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/from-text.js +684 -0
  287. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/index.d.ts +4 -0
  288. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/index.js +21 -0
  289. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/is-group-like.d.ts +19 -0
  290. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/is-group-like.js +47 -0
  291. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/reader.d.ts +91 -0
  292. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/reader.js +481 -0
  293. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/to-text.d.ts +34 -0
  294. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/to-text.js +358 -0
  295. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/writer.d.ts +76 -0
  296. package/node_modules/@bufbuild/protobuf/dist/commonjs/txtpb/writer.js +220 -0
  297. package/node_modules/@bufbuild/protobuf/dist/commonjs/types.d.ts +135 -0
  298. package/node_modules/@bufbuild/protobuf/dist/commonjs/types.js +15 -0
  299. package/node_modules/@bufbuild/protobuf/dist/commonjs/unknown-enum.d.ts +10 -0
  300. package/node_modules/@bufbuild/protobuf/dist/commonjs/unknown-enum.js +26 -0
  301. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/base64-encoding.d.ts +23 -0
  302. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/base64-encoding.js +156 -0
  303. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/binary-encoding.d.ts +268 -0
  304. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/binary-encoding.js +530 -0
  305. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/index.d.ts +5 -0
  306. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/index.js +34 -0
  307. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/size-delimited.d.ts +69 -0
  308. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/size-delimited.js +191 -0
  309. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/text-encoding.d.ts +28 -0
  310. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/text-encoding.js +61 -0
  311. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/text-format.d.ts +13 -0
  312. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/text-format.js +199 -0
  313. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/varint.d.ts +70 -0
  314. package/node_modules/@bufbuild/protobuf/dist/commonjs/wire/varint.js +322 -0
  315. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/any.d.ts +38 -0
  316. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/any.js +75 -0
  317. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/duration.d.ts +9 -0
  318. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/duration.js +39 -0
  319. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/any_pb.d.ts +238 -0
  320. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/any_pb.js +27 -0
  321. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/api_pb.d.ts +537 -0
  322. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/api_pb.js +39 -0
  323. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/compiler/plugin_pb.d.ts +490 -0
  324. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/compiler/plugin_pb.js +68 -0
  325. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/cpp_features_pb.d.ts +91 -0
  326. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/cpp_features_pb.js +60 -0
  327. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/descriptor_pb.d.ts +4143 -0
  328. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/descriptor_pb.js +892 -0
  329. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/duration_pb.d.ts +161 -0
  330. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/duration_pb.js +27 -0
  331. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/empty_pb.d.ts +39 -0
  332. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/empty_pb.js +27 -0
  333. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/field_mask_pb.d.ts +424 -0
  334. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/field_mask_pb.js +27 -0
  335. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/go_features_pb.d.ts +189 -0
  336. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/go_features_pb.js +126 -0
  337. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/java_features_pb.d.ts +194 -0
  338. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/java_features_pb.js +106 -0
  339. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/source_context_pb.d.ts +43 -0
  340. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/source_context_pb.js +27 -0
  341. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/struct_pb.d.ts +195 -0
  342. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/struct_pb.js +59 -0
  343. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/timestamp_pb.d.ts +222 -0
  344. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/timestamp_pb.js +27 -0
  345. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/type_pb.d.ts +722 -0
  346. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/type_pb.js +242 -0
  347. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/wrappers_pb.d.ts +330 -0
  348. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/gen/google/protobuf/wrappers_pb.js +67 -0
  349. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/index.d.ts +19 -0
  350. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/index.js +48 -0
  351. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/timestamp.d.ts +21 -0
  352. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/timestamp.js +57 -0
  353. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/wrappers.d.ts +25 -0
  354. package/node_modules/@bufbuild/protobuf/dist/commonjs/wkt/wrappers.js +66 -0
  355. package/node_modules/@bufbuild/protobuf/dist/esm/clone.d.ts +6 -0
  356. package/node_modules/@bufbuild/protobuf/dist/esm/clone.js +63 -0
  357. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/boot.d.ts +1 -0
  358. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/boot.js +14 -0
  359. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/enum.d.ts +10 -0
  360. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/enum.js +26 -0
  361. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/extension.d.ts +9 -0
  362. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/extension.js +25 -0
  363. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/file.d.ts +1 -0
  364. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/file.js +14 -0
  365. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/index.d.ts +10 -0
  366. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/index.js +23 -0
  367. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/message.d.ts +10 -0
  368. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/message.js +21 -0
  369. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/service.d.ts +8 -0
  370. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/service.js +24 -0
  371. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/symbols.d.ts +140 -0
  372. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/symbols.js +40 -0
  373. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/types.d.ts +79 -0
  374. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv1/types.js +14 -0
  375. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/boot.d.ts +63 -0
  376. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/boot.js +101 -0
  377. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/embed.d.ts +43 -0
  378. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/embed.js +239 -0
  379. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/enum.d.ts +29 -0
  380. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/enum.js +53 -0
  381. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/extension.d.ts +9 -0
  382. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/extension.js +25 -0
  383. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/file.d.ts +7 -0
  384. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/file.js +32 -0
  385. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/index.d.ts +10 -0
  386. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/index.js +23 -0
  387. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/message.d.ts +15 -0
  388. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/message.js +21 -0
  389. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/restore-json-names.d.ts +5 -0
  390. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/restore-json-names.js +26 -0
  391. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/scalar.d.ts +9 -0
  392. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/scalar.js +63 -0
  393. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/service.d.ts +8 -0
  394. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/service.js +24 -0
  395. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/symbols.d.ts +145 -0
  396. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/symbols.js +71 -0
  397. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/types.d.ts +85 -0
  398. package/node_modules/@bufbuild/protobuf/dist/esm/codegenv2/types.js +14 -0
  399. package/node_modules/@bufbuild/protobuf/dist/esm/create.d.ts +9 -0
  400. package/node_modules/@bufbuild/protobuf/dist/esm/create.js +256 -0
  401. package/node_modules/@bufbuild/protobuf/dist/esm/descriptors.d.ts +634 -0
  402. package/node_modules/@bufbuild/protobuf/dist/esm/descriptors.js +50 -0
  403. package/node_modules/@bufbuild/protobuf/dist/esm/equals.d.ts +46 -0
  404. package/node_modules/@bufbuild/protobuf/dist/esm/equals.js +206 -0
  405. package/node_modules/@bufbuild/protobuf/dist/esm/extensions.d.ts +60 -0
  406. package/node_modules/@bufbuild/protobuf/dist/esm/extensions.js +159 -0
  407. package/node_modules/@bufbuild/protobuf/dist/esm/fields.d.ts +23 -0
  408. package/node_modules/@bufbuild/protobuf/dist/esm/fields.js +41 -0
  409. package/node_modules/@bufbuild/protobuf/dist/esm/from-binary.d.ts +48 -0
  410. package/node_modules/@bufbuild/protobuf/dist/esm/from-binary.js +241 -0
  411. package/node_modules/@bufbuild/protobuf/dist/esm/from-json.d.ts +73 -0
  412. package/node_modules/@bufbuild/protobuf/dist/esm/from-json.js +731 -0
  413. package/node_modules/@bufbuild/protobuf/dist/esm/index.d.ts +19 -0
  414. package/node_modules/@bufbuild/protobuf/dist/esm/index.js +29 -0
  415. package/node_modules/@bufbuild/protobuf/dist/esm/is-message.d.ts +7 -0
  416. package/node_modules/@bufbuild/protobuf/dist/esm/is-message.js +30 -0
  417. package/node_modules/@bufbuild/protobuf/dist/esm/json-value.d.ts +16 -0
  418. package/node_modules/@bufbuild/protobuf/dist/esm/json-value.js +14 -0
  419. package/node_modules/@bufbuild/protobuf/dist/esm/merge.d.ts +13 -0
  420. package/node_modules/@bufbuild/protobuf/dist/esm/merge.js +67 -0
  421. package/node_modules/@bufbuild/protobuf/dist/esm/package.json +4 -0
  422. package/node_modules/@bufbuild/protobuf/dist/esm/proto-int64.d.ts +98 -0
  423. package/node_modules/@bufbuild/protobuf/dist/esm/proto-int64.js +128 -0
  424. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/error.d.ts +9 -0
  425. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/error.js +31 -0
  426. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/guard.d.ts +20 -0
  427. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/guard.js +71 -0
  428. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/index.d.ts +8 -0
  429. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/index.js +21 -0
  430. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/names.d.ts +30 -0
  431. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/names.js +109 -0
  432. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/nested-types.d.ts +35 -0
  433. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/nested-types.js +105 -0
  434. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/path.d.ts +107 -0
  435. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/path.js +369 -0
  436. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect-check.d.ts +19 -0
  437. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect-check.js +262 -0
  438. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect-types.d.ts +217 -0
  439. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect-types.js +14 -0
  440. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect.d.ts +43 -0
  441. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/reflect.js +541 -0
  442. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/scalar.d.ts +43 -0
  443. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/scalar.js +124 -0
  444. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/unsafe.d.ts +39 -0
  445. package/node_modules/@bufbuild/protobuf/dist/esm/reflect/unsafe.js +140 -0
  446. package/node_modules/@bufbuild/protobuf/dist/esm/registry.d.ts +98 -0
  447. package/node_modules/@bufbuild/protobuf/dist/esm/registry.js +939 -0
  448. package/node_modules/@bufbuild/protobuf/dist/esm/to-binary.d.ts +24 -0
  449. package/node_modules/@bufbuild/protobuf/dist/esm/to-binary.js +195 -0
  450. package/node_modules/@bufbuild/protobuf/dist/esm/to-json.d.ts +67 -0
  451. package/node_modules/@bufbuild/protobuf/dist/esm/to-json.js +386 -0
  452. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/from-text.d.ts +35 -0
  453. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/from-text.js +680 -0
  454. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/index.d.ts +4 -0
  455. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/index.js +15 -0
  456. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/is-group-like.d.ts +19 -0
  457. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/is-group-like.js +44 -0
  458. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/reader.d.ts +91 -0
  459. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/reader.js +477 -0
  460. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/to-text.d.ts +34 -0
  461. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/to-text.js +355 -0
  462. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/writer.d.ts +76 -0
  463. package/node_modules/@bufbuild/protobuf/dist/esm/txtpb/writer.js +214 -0
  464. package/node_modules/@bufbuild/protobuf/dist/esm/types.d.ts +135 -0
  465. package/node_modules/@bufbuild/protobuf/dist/esm/types.js +14 -0
  466. package/node_modules/@bufbuild/protobuf/dist/esm/unknown-enum.d.ts +10 -0
  467. package/node_modules/@bufbuild/protobuf/dist/esm/unknown-enum.js +23 -0
  468. package/node_modules/@bufbuild/protobuf/dist/esm/wire/base64-encoding.d.ts +23 -0
  469. package/node_modules/@bufbuild/protobuf/dist/esm/wire/base64-encoding.js +152 -0
  470. package/node_modules/@bufbuild/protobuf/dist/esm/wire/binary-encoding.d.ts +268 -0
  471. package/node_modules/@bufbuild/protobuf/dist/esm/wire/binary-encoding.js +525 -0
  472. package/node_modules/@bufbuild/protobuf/dist/esm/wire/index.d.ts +5 -0
  473. package/node_modules/@bufbuild/protobuf/dist/esm/wire/index.js +18 -0
  474. package/node_modules/@bufbuild/protobuf/dist/esm/wire/size-delimited.d.ts +69 -0
  475. package/node_modules/@bufbuild/protobuf/dist/esm/wire/size-delimited.js +186 -0
  476. package/node_modules/@bufbuild/protobuf/dist/esm/wire/text-encoding.d.ts +28 -0
  477. package/node_modules/@bufbuild/protobuf/dist/esm/wire/text-encoding.js +57 -0
  478. package/node_modules/@bufbuild/protobuf/dist/esm/wire/text-format.d.ts +13 -0
  479. package/node_modules/@bufbuild/protobuf/dist/esm/wire/text-format.js +195 -0
  480. package/node_modules/@bufbuild/protobuf/dist/esm/wire/varint.d.ts +70 -0
  481. package/node_modules/@bufbuild/protobuf/dist/esm/wire/varint.js +313 -0
  482. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/any.d.ts +38 -0
  483. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/any.js +69 -0
  484. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/duration.d.ts +9 -0
  485. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/duration.js +35 -0
  486. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/any_pb.d.ts +238 -0
  487. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/any_pb.js +24 -0
  488. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/api_pb.d.ts +537 -0
  489. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/api_pb.js +36 -0
  490. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/compiler/plugin_pb.d.ts +490 -0
  491. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/compiler/plugin_pb.js +65 -0
  492. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/cpp_features_pb.d.ts +91 -0
  493. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/cpp_features_pb.js +57 -0
  494. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/descriptor_pb.d.ts +4143 -0
  495. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/descriptor_pb.js +888 -0
  496. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/duration_pb.d.ts +161 -0
  497. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/duration_pb.js +24 -0
  498. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/empty_pb.d.ts +39 -0
  499. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/empty_pb.js +24 -0
  500. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/field_mask_pb.d.ts +424 -0
  501. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/field_mask_pb.js +24 -0
  502. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/go_features_pb.d.ts +189 -0
  503. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/go_features_pb.js +123 -0
  504. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/java_features_pb.d.ts +194 -0
  505. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/java_features_pb.js +103 -0
  506. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/source_context_pb.d.ts +43 -0
  507. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/source_context_pb.js +24 -0
  508. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/struct_pb.d.ts +195 -0
  509. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/struct_pb.js +56 -0
  510. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/timestamp_pb.d.ts +222 -0
  511. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/timestamp_pb.js +24 -0
  512. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/type_pb.d.ts +722 -0
  513. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/type_pb.js +239 -0
  514. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/wrappers_pb.d.ts +330 -0
  515. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/gen/google/protobuf/wrappers_pb.js +64 -0
  516. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/index.d.ts +19 -0
  517. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/index.js +32 -0
  518. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/timestamp.d.ts +21 -0
  519. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/timestamp.js +50 -0
  520. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/wrappers.d.ts +25 -0
  521. package/node_modules/@bufbuild/protobuf/dist/esm/wkt/wrappers.js +61 -0
  522. package/node_modules/@bufbuild/protobuf/package.json +147 -0
  523. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +14 -0
  524. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  525. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/cell-runtime.ts +2 -0
  526. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/render.ts +43 -6
  527. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/types.ts +4 -0
  528. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  529. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +59 -17
  530. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  531. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +14 -0
  532. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
  533. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +14 -0
  534. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
  535. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts +12 -1
  536. package/node_modules/@earendil-works/pi-agent-core/dist/types.d.ts.map +1 -1
  537. package/node_modules/@earendil-works/pi-agent-core/dist/types.js.map +1 -1
  538. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  539. package/node_modules/@earendil-works/pi-ai/README.md +3 -2
  540. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/deterministic-id.d.ts +17 -0
  541. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/deterministic-id.d.ts.map +1 -0
  542. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/deterministic-id.js +14 -0
  543. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/deterministic-id.js.map +1 -0
  544. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-lifecycle.d.ts +13 -0
  545. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-lifecycle.d.ts.map +1 -0
  546. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-lifecycle.js +33 -0
  547. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-lifecycle.js.map +1 -0
  548. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-modern.d.ts +79 -0
  549. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-modern.d.ts.map +1 -0
  550. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-modern.js +360 -0
  551. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/exec-modern.js.map +1 -0
  552. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.d.ts +16797 -0
  553. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.d.ts.map +1 -0
  554. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.js +3828 -0
  555. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/gen/agent_pb.js.map +1 -0
  556. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/pi-args.d.ts +55 -0
  557. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/pi-args.d.ts.map +1 -0
  558. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/pi-args.js +76 -0
  559. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/pi-args.js.map +1 -0
  560. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.d.ts +116 -0
  561. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.d.ts.map +1 -0
  562. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.js +16 -0
  563. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/types.js.map +1 -0
  564. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +191 -0
  565. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -0
  566. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +3316 -0
  567. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -0
  568. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.lazy.d.ts +29 -0
  569. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.lazy.d.ts.map +1 -0
  570. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.lazy.js +33 -0
  571. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.lazy.js.map +1 -0
  572. package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts +1 -0
  573. package/node_modules/@earendil-works/pi-ai/dist/compat.d.ts.map +1 -1
  574. package/node_modules/@earendil-works/pi-ai/dist/compat.js +3 -0
  575. package/node_modules/@earendil-works/pi-ai/dist/compat.js.map +1 -1
  576. package/node_modules/@earendil-works/pi-ai/dist/cursor-agent-provider.d.ts +11 -0
  577. package/node_modules/@earendil-works/pi-ai/dist/cursor-agent-provider.d.ts.map +1 -0
  578. package/node_modules/@earendil-works/pi-ai/dist/cursor-agent-provider.js +11 -0
  579. package/node_modules/@earendil-works/pi-ai/dist/cursor-agent-provider.js.map +1 -0
  580. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +3 -0
  581. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  582. package/node_modules/@earendil-works/pi-ai/dist/index.js +2 -0
  583. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  584. package/node_modules/@earendil-works/pi-ai/dist/model.d.ts +6 -1
  585. package/node_modules/@earendil-works/pi-ai/dist/model.d.ts.map +1 -1
  586. package/node_modules/@earendil-works/pi-ai/dist/model.js.map +1 -1
  587. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.d.ts +6 -9
  588. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.d.ts.map +1 -1
  589. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js +42 -9
  590. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js.map +1 -1
  591. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  592. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  593. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cerebras.json +1 -1
  594. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  595. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-workers-ai.json +1 -1
  596. package/node_modules/@earendil-works/pi-ai/dist/providers/data/huggingface.json +1 -1
  597. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai-codex.json +1 -1
  598. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  599. package/node_modules/@earendil-works/pi-ai/dist/providers/data/together.json +1 -1
  600. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  601. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +3 -1
  602. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  603. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  604. package/node_modules/@earendil-works/pi-ai/dist/utils/block-symbols.d.ts +57 -0
  605. package/node_modules/@earendil-works/pi-ai/dist/utils/block-symbols.d.ts.map +1 -0
  606. package/node_modules/@earendil-works/pi-ai/dist/utils/block-symbols.js +57 -0
  607. package/node_modules/@earendil-works/pi-ai/dist/utils/block-symbols.js.map +1 -0
  608. package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.d.ts +10 -0
  609. package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.d.ts.map +1 -1
  610. package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.js +22 -0
  611. package/node_modules/@earendil-works/pi-ai/dist/utils/event-stream.js.map +1 -1
  612. package/node_modules/@earendil-works/pi-ai/package.json +3 -2
  613. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  614. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  615. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.d.ts.map +1 -1
  616. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js +21 -0
  617. package/node_modules/@earendil-works/pi-tui/dist/autocomplete.js.map +1 -1
  618. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js +1 -1
  619. package/node_modules/@earendil-works/pi-tui/dist/components/editor.js.map +1 -1
  620. package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.d.ts +9 -0
  621. package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.d.ts.map +1 -0
  622. package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.js +82 -0
  623. package/node_modules/@earendil-works/pi-tui/dist/dollar-invocation-autocomplete.js.map +1 -0
  624. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  625. package/package.json +11 -8
@@ -0,0 +1,3316 @@
1
+ /**
2
+ * Cursor agent protocol (`agent.v1.AgentService/Run`) over HTTP/2 Connect.
3
+ *
4
+ * Ported from upstream oh-my-pi's Cursor provider and adapted to senpi's
5
+ * provider-neutral API architecture. The protocol is server-driven: the
6
+ * client opens one `Run` stream per assistant turn, the server streams
7
+ * interaction updates (text/thinking/tool-call deltas) and, mid-turn, sends
8
+ * `ExecServerMessage` frames asking the CLIENT to execute a tool and blocks
9
+ * until the answer arrives on the same stream. Tool execution is therefore
10
+ * bridged through injected {@link CursorExecHandlers}; each bridged call is
11
+ * synthesized into the assistant message as an already-resolved `toolCall`
12
+ * block (marked {@link kCursorExecResolved} so the agent loop never re-runs
13
+ * it) and paired with a `ToolResultMessage` delivered via `onToolResult`.
14
+ *
15
+ * NOTE: This module is Node-only (node:http2) and must only be reached
16
+ * through the lazy wrapper (`cursor-agent.lazy.ts`).
17
+ */
18
+ import { createHash, randomUUID } from "node:crypto";
19
+ import * as http2 from "node:http2";
20
+ import { create, fromBinary, fromJson, toBinary, toJson } from "@bufbuild/protobuf";
21
+ import { ValueSchema } from "@bufbuild/protobuf/wkt";
22
+ import { calculateCost } from "../models.js";
23
+ import { clearStreamingPartialJson, kCursorExecResolved, kStreamingBlockIndex, kStreamingBlockKind, kStreamingEnvelopeId, kStreamingLastParseLen, kStreamingPartialJson, } from "../utils/block-symbols.js";
24
+ import { AssistantMessageEventStream } from "../utils/event-stream.js";
25
+ import { providerHeadersToRecord } from "../utils/headers.js";
26
+ import { parseJsonWithRepair, parseStreamingJson } from "../utils/json-parse.js";
27
+ import { sanitizeSurrogates } from "../utils/sanitize-unicode.js";
28
+ import { deterministicUuid } from "./cursor-agent/deterministic-id.js";
29
+ import { armExecHeartbeat } from "./cursor-agent/exec-lifecycle.js";
30
+ import { buildMcpStateResult, buildNeutralHookResult, buildPiBashError, buildPiBashResult, buildPiEditError, buildPiEditRejected, buildPiEditResult, buildPiFindError, buildPiFindResult, buildPiGrepError, buildPiGrepResult, buildPiLsError, buildPiLsResult, buildPiReadError, buildPiReadResult, buildPiWriteError, buildPiWriteRejected, buildPiWriteResult, } from "./cursor-agent/exec-modern.js";
31
+ import { AgentClientMessageSchema, AgentConversationTurnStructureSchema, AgentRunRequestSchema, AgentServerMessageSchema, AgentStoreConflictErrorSchema, AgentStoreConflictResultSchema, AssistantMessageSchema, BackgroundShellSpawnResultSchema, CanvasDiagnosticsErrorSchema, CanvasDiagnosticsResultSchema, ClientHeartbeatSchema, ComputerUseErrorSchema, ComputerUseResultSchema, ConversationActionSchema, ConversationSearchErrorSchema, ConversationSearchResultSchema, ConversationStateStructureSchema, ConversationStepSchema, ConversationTurnStructureSchema, DeleteErrorSchema, DeleteRejectedSchema, DeleteResultSchema, DeleteSuccessSchema, DiagnosticsErrorSchema, DiagnosticsRejectedSchema, DiagnosticsResultSchema, DiagnosticsSuccessSchema, ExecClientControlMessageSchema, ExecClientHeartbeatSchema, ExecClientMessageSchema, ExecClientStreamCloseSchema, ExecClientThrowSchema, FetchErrorSchema, FetchResultSchema, ForceBackgroundShellResultSchema, ForceBackgroundShellStatus, ForceBackgroundSubagentResultSchema, ForceBackgroundSubagentStatus, GetBlobResultSchema, GetUsableModelsRequestSchema, GetUsableModelsResponseSchema, GrepContentMatchSchema, GrepContentResultSchema, GrepCountResultSchema, GrepErrorSchema, GrepFileCountSchema, GrepFileMatchSchema, GrepFilesResultSchema, GrepResultSchema, GrepSuccessSchema, GrepUnionResultSchema, KvClientMessageSchema, ListMcpResourcesExecResultSchema, ListMcpResourcesSuccessSchema, LsDirectoryTreeNode_FileSchema, LsDirectoryTreeNodeSchema, LsErrorSchema, LsRejectedSchema, LsResultSchema, LsSuccessSchema, McpAllowlistPrecheckResultSchema, McpApprovedSchema, McpArgsSchema, McpErrorSchema, McpImageContentSchema, McpRejectedSchema, McpResultSchema, McpSuccessSchema, McpTextContentSchema, McpToolCallSchema, McpToolDefinitionSchema, McpToolErrorSchema, McpToolNotFoundSchema, McpToolResultContentItemSchema, McpToolResultSchema, ModelDetailsSchema, ReadErrorSchema, ReadMcpResourceExecResultSchema, ReadMcpResourceNotFoundSchema, ReadRejectedSchema, ReadResultSchema, ReadSuccessSchema, RecordScreenFailureSchema, RecordScreenResultSchema, RequestContextResultSchema, RequestContextSchema, RequestContextSuccessSchema, RequestedModelSchema, ResumeActionSchema, SelectedContextSchema, SelectedImageSchema, SetBlobResultSchema, ShellAllowlistPrecheckResultSchema, ShellFailureSchema, ShellRejectedSchema, ShellResultSchema, ShellStreamExitSchema, ShellStreamSchema, ShellStreamStartSchema, ShellStreamStderrSchema, ShellStreamStdoutSchema, ShellSuccessSchema, SmartModeClassifierErrorSchema, SmartModeClassifierResultSchema, SubagentAwaitNotFoundSchema, SubagentAwaitResultSchema, SubagentErrorSchema, SubagentResultSchema, ToolCallSchema, UserMessageActionSchema, UserMessageSchema, WebFetchAllowlistPrecheckResultSchema, WriteErrorSchema, WriteRejectedSchema, WriteResultSchema, WriteShellStdinErrorSchema, WriteShellStdinResultSchema, WriteSuccessSchema, } from "./cursor-agent/gen/agent_pb.js";
32
+ import { composeShellCommand, omitUndefinedArgs, piLimit, piLsPath, piReadArgs, piTimeout, } from "./cursor-agent/pi-args.js";
33
+ export const CURSOR_API_URL = "https://api2.cursor.sh";
34
+ export const CURSOR_CLIENT_VERSION = "cli-2026.07.23-e383d2b";
35
+ const EXEC_HEARTBEAT_INTERVAL_MS = 3000;
36
+ /**
37
+ * HTTP/1 connection-specific headers that HTTP/2 forbids. Node's
38
+ * `http2.request()` throws on these rather than dropping them.
39
+ */
40
+ const HTTP2_FORBIDDEN_HEADERS = new Set([
41
+ "connection",
42
+ "keep-alive",
43
+ "proxy-connection",
44
+ "transfer-encoding",
45
+ "upgrade",
46
+ "http2-settings",
47
+ ]);
48
+ /**
49
+ * Header names the Cursor request sets for itself. A caller copy in ANY
50
+ * casing has to go: the spread below adds the fixed lower-case name
51
+ * regardless, and two spellings of one field are a duplicate (node throws
52
+ * `ERR_HTTP2_HEADER_SINGLE_VALUE`) rather than an override.
53
+ */
54
+ const CURSOR_RESERVED_HEADERS = new Set([
55
+ "content-type",
56
+ "connect-protocol-version",
57
+ "te",
58
+ "authorization",
59
+ "x-ghost-mode",
60
+ "x-cursor-client-version",
61
+ "x-cursor-client-type",
62
+ "x-request-id",
63
+ // Transport-owned: a plain `host` header suppresses the `:authority` node
64
+ // derives from the URL, silently retargeting the request.
65
+ "host",
66
+ // The Connect body is streamed after the headers (initial frame,
67
+ // heartbeats, tool responses), so no caller-supplied length can describe it.
68
+ "content-length",
69
+ ]);
70
+ /** Reduce caller-supplied headers to what this HTTP/2 request can legally carry. */
71
+ export function sanitizeCursorCallerHeaders(headers) {
72
+ const sanitized = {};
73
+ for (const [name, value] of Object.entries(headers ?? {})) {
74
+ const field = name.toLowerCase();
75
+ if (field.startsWith(":"))
76
+ continue;
77
+ if (HTTP2_FORBIDDEN_HEADERS.has(field))
78
+ continue;
79
+ if (CURSOR_RESERVED_HEADERS.has(field))
80
+ continue;
81
+ sanitized[field] = value;
82
+ }
83
+ return sanitized;
84
+ }
85
+ /**
86
+ * Text for a recognised frame this client answers with its own typed error
87
+ * variant. Phrased as a client capability statement, not a tool failure: the
88
+ * model reads it and should route around the capability, not retry the call.
89
+ */
90
+ const NOT_IMPLEMENTED_SUFFIX = "not implemented by this client";
91
+ const NOT_IMPLEMENTED = "Not implemented by this client";
92
+ /** Bare gRPC `resource_exhausted` end-streams (also inside a Connect error message). */
93
+ const RESOURCE_EXHAUSTED_PATTERN = /resource.?exhausted/i;
94
+ const conversationStateCache = new Map();
95
+ const conversationBlobStores = new Map();
96
+ /**
97
+ * Base conversation id → rotated wire id. Cursor's backend can pin a
98
+ * per-conversation rejection (bare `resource_exhausted`, zero tokens) to one
99
+ * conversationId forever. On the first such failure the id is rotated once
100
+ * and the cached state migrates, so the retry loop's next attempt starts a
101
+ * fresh conversation. Keyed by the base id so a failed rotation never repeats.
102
+ */
103
+ const rotatedConversationIds = new Map();
104
+ const CONNECT_END_STREAM_FLAG = 0b00000010;
105
+ function log(type, subtype, data) {
106
+ if (!process.env.DEBUG_CURSOR)
107
+ return;
108
+ const verbose = process.env.DEBUG_CURSOR === "2" || process.env.DEBUG_CURSOR === "verbose";
109
+ let dataStr = "";
110
+ if (verbose && data !== undefined) {
111
+ try {
112
+ dataStr = ` ${JSON.stringify(data, (_key, value) => {
113
+ if (value instanceof Uint8Array)
114
+ return `bytes(${value.length})`;
115
+ if (typeof value === "bigint")
116
+ return value.toString();
117
+ return value;
118
+ })?.slice(0, 500)}`;
119
+ }
120
+ catch {
121
+ dataStr = " [unserializable]";
122
+ }
123
+ }
124
+ console.error(`[CURSOR] ${type}${subtype ? `: ${subtype}` : ""}${dataStr}`);
125
+ }
126
+ export function frameConnectMessage(data, flags = 0) {
127
+ const frame = Buffer.alloc(5 + data.length);
128
+ frame[0] = flags;
129
+ frame.writeUInt32BE(data.length, 1);
130
+ frame.set(data, 5);
131
+ return frame;
132
+ }
133
+ function parseConnectEndStream(data) {
134
+ try {
135
+ const payload = JSON.parse(new TextDecoder().decode(data));
136
+ const error = payload?.error;
137
+ if (error) {
138
+ const code = typeof error.code === "string" ? error.code : "unknown";
139
+ const message = typeof error.message === "string" ? error.message : "Unknown error";
140
+ return new Error(`Connect error ${code}: ${message}`);
141
+ }
142
+ return null;
143
+ }
144
+ catch {
145
+ return new Error("Failed to parse Connect end stream");
146
+ }
147
+ }
148
+ /**
149
+ * Maps an opaque HTTP/2 negotiation failure into an actionable error. The
150
+ * Cursor run RPC is HTTP/2-only (the ALB rejects HTTP/1.1 with 464), so a
151
+ * TLS-intercepting proxy that strips ALPN kills the run with no h1 fallback.
152
+ */
153
+ export function mapH2TransportError(error, baseUrl) {
154
+ const code = error?.code;
155
+ const message = error instanceof Error ? error.message : String(error);
156
+ if (code === "ERR_HTTP2_ERROR" && /h2 is not supported/i.test(message)) {
157
+ return new Error(`Cursor run transport could not negotiate HTTP/2 with ${baseUrl}: "h2 is not supported". ` +
158
+ "This host serves the run RPC over HTTP/2 only, and the TLS handshake did not negotiate " +
159
+ "h2 via ALPN — typically an ALPN-stripping TLS-intercepting proxy. " +
160
+ "Front the provider with a local HTTP/2 bridge and point the model's baseUrl at it.");
161
+ }
162
+ return error;
163
+ }
164
+ export const stream = (model, context, options) => {
165
+ const stream = new AssistantMessageEventStream();
166
+ (async () => {
167
+ const output = {
168
+ role: "assistant",
169
+ content: [],
170
+ api: "cursor-agent",
171
+ provider: model.provider,
172
+ model: model.id,
173
+ usage: {
174
+ input: 0,
175
+ output: 0,
176
+ cacheRead: 0,
177
+ cacheWrite: 0,
178
+ totalTokens: 0,
179
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
180
+ },
181
+ stopReason: "stop",
182
+ timestamp: Date.now(),
183
+ };
184
+ // Declared outside the `try` because BOTH exits must drain it: an exec
185
+ // handler decoded from the last chunk can still be running when the
186
+ // transport fails, and the error path finalizes the synthesized call
187
+ // just like the success path does.
188
+ const inFlightDispatches = new Set();
189
+ // A dispatch can spawn another, so re-check rather than awaiting one
190
+ // snapshot. The wait is bounded by the abort signal: exec handlers have
191
+ // no cancellation contract, so a hung tool must not hold the terminal
192
+ // event hostage after the user already gave up on the turn.
193
+ let abortSettled;
194
+ const drainInFlightDispatches = async () => {
195
+ const signal = options?.signal;
196
+ while (inFlightDispatches.size > 0) {
197
+ if (signal?.aborted)
198
+ return;
199
+ const settled = Promise.all([...inFlightDispatches]);
200
+ if (!signal) {
201
+ await settled;
202
+ continue;
203
+ }
204
+ abortSettled ??= new Promise((resolve) => signal.addEventListener("abort", () => resolve(), { once: true }));
205
+ await Promise.race([settled, abortSettled]);
206
+ }
207
+ };
208
+ let h2Client = null;
209
+ let h2Request = null;
210
+ let heartbeatTimer = null;
211
+ let h2Settled = false;
212
+ let sawTurnEnded = false;
213
+ let endStreamError = null;
214
+ // Reachable from the catch: a stream that dies mid-turn must still close
215
+ // and pair the blocks it left open.
216
+ let openBlockState;
217
+ let resolveH2 = () => { };
218
+ let rejectH2 = () => { };
219
+ const h2Completion = new Promise((resolve, reject) => {
220
+ resolveH2 = resolve;
221
+ rejectH2 = reject;
222
+ });
223
+ const settleH2 = (error) => {
224
+ if (h2Settled)
225
+ return;
226
+ h2Settled = true;
227
+ if (error !== undefined) {
228
+ rejectH2(error);
229
+ return;
230
+ }
231
+ if (endStreamError) {
232
+ rejectH2(endStreamError);
233
+ return;
234
+ }
235
+ if (!sawTurnEnded) {
236
+ rejectH2(new Error("Cursor stream ended before turnEnded"));
237
+ return;
238
+ }
239
+ resolveH2();
240
+ };
241
+ // Hoisted out of the try block: the rotation in the catch path needs
242
+ // both ids, and the catch block cannot see try-scoped consts.
243
+ let baseConversationId;
244
+ let conversationId;
245
+ let usageState;
246
+ try {
247
+ const apiKey = options?.apiKey;
248
+ if (!apiKey) {
249
+ throw new Error("Cursor access token is required; run /login cursor");
250
+ }
251
+ baseConversationId = options?.conversationId ?? options?.sessionId ?? randomUUID();
252
+ conversationId = rotatedConversationIds.get(baseConversationId) ?? baseConversationId;
253
+ const blobStore = conversationBlobStores.get(conversationId) ?? new Map();
254
+ conversationBlobStores.set(conversationId, blobStore);
255
+ const cachedState = conversationStateCache.get(conversationId);
256
+ const { requestBytes, conversationState } = await buildGrpcRequest(model, context, options, {
257
+ conversationId,
258
+ blobStore,
259
+ conversationState: cachedState,
260
+ });
261
+ conversationStateCache.set(conversationId, conversationState);
262
+ const requestContextTools = buildMcpToolDefinitions(context.tools);
263
+ const baseUrl = model.baseUrl || CURSOR_API_URL;
264
+ const requestPath = "/agent.v1.AgentService/Run";
265
+ // Caller headers are additive, and are spread FIRST so the protocol
266
+ // framing, auth, and request id below always win.
267
+ const callerHeaders = sanitizeCursorCallerHeaders(providerHeadersToRecord(options?.headers));
268
+ const requestHeaders = {
269
+ ...callerHeaders,
270
+ ":method": "POST",
271
+ ":path": requestPath,
272
+ "content-type": "application/connect+proto",
273
+ "connect-protocol-version": "1",
274
+ te: "trailers",
275
+ authorization: `Bearer ${apiKey}`,
276
+ "x-ghost-mode": "true",
277
+ "x-cursor-client-version": CURSOR_CLIENT_VERSION,
278
+ "x-cursor-client-type": "cli",
279
+ "x-request-id": randomUUID(),
280
+ };
281
+ h2Client = http2.connect(baseUrl);
282
+ h2Client.on("error", (error) => settleH2(mapH2TransportError(error, baseUrl)));
283
+ h2Request = h2Client.request(requestHeaders);
284
+ stream.push({ type: "start", partial: output });
285
+ let pendingBuffer = Buffer.alloc(0);
286
+ let currentTextBlock = null;
287
+ let currentThinkingBlock = null;
288
+ let currentToolCall = null;
289
+ const resolvedMcpToolCallIds = new Set();
290
+ usageState = { sawTokenDelta: false };
291
+ const state = {
292
+ get currentTextBlock() {
293
+ return currentTextBlock;
294
+ },
295
+ get currentThinkingBlock() {
296
+ return currentThinkingBlock;
297
+ },
298
+ get currentToolCall() {
299
+ return currentToolCall;
300
+ },
301
+ openToolCalls: new Map(),
302
+ resolvedMcpToolCallIds,
303
+ setTextBlock: (b) => {
304
+ currentTextBlock = b;
305
+ },
306
+ setThinkingBlock: (b) => {
307
+ currentThinkingBlock = b;
308
+ },
309
+ setToolCall: (t) => {
310
+ currentToolCall = t;
311
+ },
312
+ onToolResult: options?.onToolResult ?? options?.execHandlers?.onToolResult,
313
+ };
314
+ openBlockState = state;
315
+ const onConversationCheckpoint = (checkpoint) => {
316
+ conversationStateCache.set(conversationId, checkpoint);
317
+ };
318
+ h2Request.on("data", (chunk) => {
319
+ // Steady state drains fully per chunk; alias the fresh h2 chunk
320
+ // instead of copying it through Buffer.concat.
321
+ pendingBuffer = pendingBuffer.length === 0 ? chunk : Buffer.concat([pendingBuffer, chunk]);
322
+ while (pendingBuffer.length >= 5) {
323
+ const flags = pendingBuffer[0];
324
+ const msgLen = pendingBuffer.readUInt32BE(1);
325
+ if (pendingBuffer.length < 5 + msgLen)
326
+ break;
327
+ const messageBytes = pendingBuffer.subarray(5, 5 + msgLen);
328
+ pendingBuffer = pendingBuffer.subarray(5 + msgLen);
329
+ if (flags & CONNECT_END_STREAM_FLAG) {
330
+ const endError = parseConnectEndStream(messageBytes);
331
+ if (endError) {
332
+ endStreamError = endError;
333
+ h2Request?.close();
334
+ }
335
+ continue;
336
+ }
337
+ try {
338
+ const serverMessage = fromBinary(AgentServerMessageSchema, messageBytes);
339
+ const isTurnEnded = serverMessage.message.case === "interactionUpdate" &&
340
+ serverMessage.message.value.message?.case === "turnEnded";
341
+ // Dispatch is fire-and-forget so the socket keeps draining
342
+ // while a handler runs, but the promise is tracked: `done`
343
+ // must not be pushed while an exec handler is still resolving,
344
+ // or the buffered tool result is delivered after the turn
345
+ // already finalized and the call is left unpaired.
346
+ const dispatch = handleServerMessage(serverMessage, output, stream, state, blobStore, h2Request, options?.execHandlers, state.onToolResult, usageState, requestContextTools, onConversationCheckpoint).catch((error) => {
347
+ log("error", "handleServerMessage", { error: String(error) });
348
+ });
349
+ inFlightDispatches.add(dispatch);
350
+ void dispatch.finally(() => inFlightDispatches.delete(dispatch));
351
+ // Application completion is not protocol success; wait for a
352
+ // clean HTTP/2 end.
353
+ if (isTurnEnded) {
354
+ sawTurnEnded = true;
355
+ }
356
+ }
357
+ catch (e) {
358
+ log("error", "parseServerMessage", { error: String(e) });
359
+ }
360
+ }
361
+ });
362
+ const sendHeartbeat = () => {
363
+ if (!h2Request || h2Request.closed) {
364
+ return;
365
+ }
366
+ const heartbeatMessage = create(AgentClientMessageSchema, {
367
+ message: { case: "clientHeartbeat", value: create(ClientHeartbeatSchema, {}) },
368
+ });
369
+ const heartbeatBytes = toBinary(AgentClientMessageSchema, heartbeatMessage);
370
+ h2Request.write(frameConnectMessage(heartbeatBytes));
371
+ };
372
+ h2Request.on("trailers", (trailers) => {
373
+ const status = trailers["grpc-status"];
374
+ const msg = trailers["grpc-message"];
375
+ if (status && status !== "0" && !endStreamError) {
376
+ endStreamError = new Error(`gRPC error ${status}: ${decodeURIComponent(String(msg || ""))}`);
377
+ }
378
+ });
379
+ h2Request.on("end", () => {
380
+ settleH2();
381
+ });
382
+ h2Request.on("error", (error) => {
383
+ settleH2(mapH2TransportError(error, baseUrl));
384
+ });
385
+ if (options?.signal) {
386
+ options.signal.addEventListener("abort", () => {
387
+ h2Request?.close();
388
+ settleH2(new Error("Request was aborted"));
389
+ });
390
+ }
391
+ h2Request.write(frameConnectMessage(requestBytes));
392
+ heartbeatTimer = setInterval(sendHeartbeat, 5000);
393
+ await h2Completion;
394
+ // The transport is done, but a handler decoded from the last chunk
395
+ // may still be running. Pushing `done` now would let the host drain
396
+ // its buffered tool results before such a handler reserved its entry,
397
+ // leaving the call unpaired and stripped from rebuilt transcripts.
398
+ await drainInFlightDispatches();
399
+ endCurrentTextBlock(output, stream, state);
400
+ endCurrentThinkingBlock(output, stream, state);
401
+ flushOpenToolCalls(output, stream, state);
402
+ calculateCost(model, output.usage);
403
+ stream.push({
404
+ type: "done",
405
+ reason: output.stopReason,
406
+ message: output,
407
+ });
408
+ stream.end();
409
+ }
410
+ catch (error) {
411
+ // Same reason as the success path: a handler still running would land
412
+ // its real result after the turn finalized and be discarded — even
413
+ // though the tool may already have run side effects. On abort the
414
+ // drain returns immediately.
415
+ await drainInFlightDispatches();
416
+ // A stream that dies mid-turn leaves blocks open. Closing them here
417
+ // settles their live cards and pairs the server-owned calls that
418
+ // nothing else answers — an unpaired call is stripped from every
419
+ // rebuilt transcript.
420
+ if (openBlockState) {
421
+ endCurrentTextBlock(output, stream, openBlockState);
422
+ endCurrentThinkingBlock(output, stream, openBlockState);
423
+ flushOpenToolCalls(output, stream, openBlockState);
424
+ }
425
+ const message = error instanceof Error ? error.message : JSON.stringify(error);
426
+ // A server-side per-conversation rejection surfaces as a bare
427
+ // resource_exhausted with zero tokens — the conversation is poisoned,
428
+ // not the account. Rotate the wire id once and migrate cached state
429
+ // so the caller's retry loop starts a fresh conversation.
430
+ if (conversationId !== undefined &&
431
+ baseConversationId !== undefined &&
432
+ usageState !== undefined &&
433
+ !usageState.sawTokenDelta &&
434
+ RESOURCE_EXHAUSTED_PATTERN.test(message) &&
435
+ !rotatedConversationIds.has(baseConversationId)) {
436
+ const rotated = randomUUID();
437
+ rotatedConversationIds.set(baseConversationId, rotated);
438
+ const cached = conversationStateCache.get(conversationId);
439
+ if (cached)
440
+ conversationStateCache.set(rotated, cached);
441
+ const blobs = conversationBlobStores.get(conversationId);
442
+ if (blobs)
443
+ conversationBlobStores.set(rotated, blobs);
444
+ }
445
+ output.stopReason = options?.signal?.aborted ? "aborted" : "error";
446
+ output.errorMessage = message;
447
+ stream.push({ type: "error", reason: output.stopReason, error: output });
448
+ stream.end();
449
+ }
450
+ finally {
451
+ if (heartbeatTimer) {
452
+ clearInterval(heartbeatTimer);
453
+ heartbeatTimer = null;
454
+ }
455
+ h2Request?.close();
456
+ h2Client?.close();
457
+ }
458
+ })();
459
+ return stream;
460
+ };
461
+ /**
462
+ * `streamSimple` for Cursor: reasoning/thinking is managed server-side per
463
+ * model (there is no client thinking knob on the Run request), so the simple
464
+ * options map through unchanged.
465
+ */
466
+ export const streamSimple = (model, context, options) => {
467
+ return stream(model, context, options);
468
+ };
469
+ function markCursorExecResolved(block) {
470
+ block[kCursorExecResolved] = true;
471
+ }
472
+ /** Exported for tests: drives one Cursor server message through the stream (exec waits mark the stream busy). */
473
+ export async function handleServerMessage(msg, output, stream, state, blobStore, h2Request, execHandlers, onToolResult, usageState, requestContextTools, onConversationCheckpoint) {
474
+ const msgCase = msg.message.case;
475
+ log("serverMessage", msgCase);
476
+ if (msgCase === "interactionUpdate") {
477
+ processInteractionUpdate(msg.message.value, output, stream, state, usageState);
478
+ }
479
+ else if (msgCase === "kvServerMessage") {
480
+ handleKvServerMessage(msg.message.value, blobStore, h2Request);
481
+ }
482
+ else if (msgCase === "execServerMessage") {
483
+ // The server is waiting on OUR local tool result during this window — no
484
+ // AssistantMessageEvent flows until the handler finishes. Mark the wait
485
+ // as local work so idle watchdogs attribute the silence to the tool run
486
+ // instead of aborting a healthy stream.
487
+ await stream.trackLocalWork(handleExecServerMessage(msg.message.value, h2Request, execHandlers, onToolResult, requestContextTools, output, stream, state));
488
+ }
489
+ else if (msgCase === "conversationCheckpointUpdate") {
490
+ onConversationCheckpoint?.(msg.message.value);
491
+ }
492
+ }
493
+ function handleKvServerMessage(kvMsg, blobStore, h2Request) {
494
+ const kvCase = kvMsg.message.case;
495
+ if (kvCase === "getBlobArgs") {
496
+ const blobId = kvMsg.message.value.blobId;
497
+ const blobIdKey = Buffer.from(blobId).toString("hex");
498
+ const blobData = blobStore.get(blobIdKey);
499
+ const response = create(KvClientMessageSchema, {
500
+ id: kvMsg.id,
501
+ message: {
502
+ case: "getBlobResult",
503
+ value: create(GetBlobResultSchema, blobData ? { blobData } : {}),
504
+ },
505
+ });
506
+ const kvClientMessage = create(AgentClientMessageSchema, {
507
+ message: { case: "kvClientMessage", value: response },
508
+ });
509
+ h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, kvClientMessage)));
510
+ log("kvClient", "getBlobResult", { blobId: blobIdKey.slice(0, 40) });
511
+ }
512
+ else if (kvCase === "setBlobArgs") {
513
+ const { blobId, blobData } = kvMsg.message.value;
514
+ const blobIdKey = Buffer.from(blobId).toString("hex");
515
+ blobStore.set(blobIdKey, blobData);
516
+ const response = create(KvClientMessageSchema, {
517
+ id: kvMsg.id,
518
+ message: {
519
+ case: "setBlobResult",
520
+ value: create(SetBlobResultSchema, {}),
521
+ },
522
+ });
523
+ const kvClientMessage = create(AgentClientMessageSchema, {
524
+ message: { case: "kvClientMessage", value: response },
525
+ });
526
+ h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, kvClientMessage)));
527
+ log("kvClient", "setBlobResult", { blobId: blobIdKey.slice(0, 40) });
528
+ }
529
+ }
530
+ function sendShellStreamEvent(h2Request, execMsg, event) {
531
+ sendExecClientMessage(h2Request, execMsg, "shellStream", create(ShellStreamSchema, { event }));
532
+ }
533
+ function sanitizeShellExecResult(execResult) {
534
+ const result = execResult.result;
535
+ if (!result)
536
+ return execResult;
537
+ switch (result.case) {
538
+ case "success":
539
+ case "failure": {
540
+ const value = result.value;
541
+ return {
542
+ ...execResult,
543
+ result: {
544
+ case: result.case,
545
+ value: {
546
+ ...value,
547
+ stdout: value.stdout ? sanitizeSurrogates(value.stdout) : value.stdout,
548
+ stderr: value.stderr ? sanitizeSurrogates(value.stderr) : value.stderr,
549
+ },
550
+ },
551
+ };
552
+ }
553
+ default:
554
+ return execResult;
555
+ }
556
+ }
557
+ async function handleShellStreamArgs(args, execMsg, h2Request, execHandlers, onToolResult) {
558
+ const normalizedWorkingDirectory = args.workingDirectory || process.cwd();
559
+ const normalizedArgs = { ...args, workingDirectory: normalizedWorkingDirectory };
560
+ sendShellStreamEvent(h2Request, execMsg, { case: "start", value: create(ShellStreamStartSchema, {}) });
561
+ // Buffer for incomplete ANSI sequences across chunks.
562
+ let stdoutBuffer = "";
563
+ let stderrBuffer = "";
564
+ const incompleteEscapeRegex = /\x1b(|\[|\[\d*|\[\?|\[\?\d*|\]\d*;?)$/;
565
+ const flushStdout = () => {
566
+ if (stdoutBuffer) {
567
+ let safeEnd = stdoutBuffer.length;
568
+ const match = stdoutBuffer.match(incompleteEscapeRegex);
569
+ if (match && match[0].length > 0) {
570
+ safeEnd = stdoutBuffer.length - match[0].length;
571
+ }
572
+ const toSend = stdoutBuffer.slice(0, safeEnd);
573
+ const remaining = stdoutBuffer.slice(safeEnd);
574
+ if (toSend) {
575
+ sendShellStreamEvent(h2Request, execMsg, {
576
+ case: "stdout",
577
+ value: create(ShellStreamStdoutSchema, { data: sanitizeSurrogates(toSend) }),
578
+ });
579
+ }
580
+ stdoutBuffer = remaining;
581
+ }
582
+ };
583
+ const flushStderr = () => {
584
+ if (stderrBuffer) {
585
+ let safeEnd = stderrBuffer.length;
586
+ const match = stderrBuffer.match(incompleteEscapeRegex);
587
+ if (match && match[0].length > 0) {
588
+ safeEnd = stderrBuffer.length - match[0].length;
589
+ }
590
+ const toSend = stderrBuffer.slice(0, safeEnd);
591
+ const remaining = stderrBuffer.slice(safeEnd);
592
+ if (toSend) {
593
+ sendShellStreamEvent(h2Request, execMsg, {
594
+ case: "stderr",
595
+ value: create(ShellStreamStderrSchema, { data: sanitizeSurrogates(toSend) }),
596
+ });
597
+ }
598
+ stderrBuffer = remaining;
599
+ }
600
+ };
601
+ let stdoutFlushTimer = null;
602
+ let stderrFlushTimer = null;
603
+ const scheduleStdoutFlush = () => {
604
+ if (!stdoutFlushTimer) {
605
+ stdoutFlushTimer = setTimeout(() => {
606
+ stdoutFlushTimer = null;
607
+ flushStdout();
608
+ }, 100);
609
+ }
610
+ };
611
+ const scheduleStderrFlush = () => {
612
+ if (!stderrFlushTimer) {
613
+ stderrFlushTimer = setTimeout(() => {
614
+ stderrFlushTimer = null;
615
+ flushStderr();
616
+ }, 100);
617
+ }
618
+ };
619
+ const streamCallbacks = {
620
+ onStdout(data) {
621
+ stdoutBuffer += data;
622
+ if (stdoutBuffer.includes("\n") || stdoutBuffer.length > 4096) {
623
+ if (stdoutFlushTimer) {
624
+ clearTimeout(stdoutFlushTimer);
625
+ stdoutFlushTimer = null;
626
+ }
627
+ flushStdout();
628
+ }
629
+ else {
630
+ scheduleStdoutFlush();
631
+ }
632
+ },
633
+ onStderr(data) {
634
+ stderrBuffer += data;
635
+ if (stderrBuffer.includes("\n") || stderrBuffer.length > 4096) {
636
+ if (stderrFlushTimer) {
637
+ clearTimeout(stderrFlushTimer);
638
+ stderrFlushTimer = null;
639
+ }
640
+ flushStderr();
641
+ }
642
+ else {
643
+ scheduleStderrFlush();
644
+ }
645
+ },
646
+ };
647
+ // Prefer the streaming handler — it forwards output chunks in real time.
648
+ const streamHandler = execHandlers?.shellStream?.bind(execHandlers);
649
+ const batchHandler = execHandlers?.shell?.bind(execHandlers);
650
+ const handler = streamHandler ? (shellArgs) => streamHandler(shellArgs, streamCallbacks) : batchHandler;
651
+ const { execResult } = await resolveExecHandler(normalizedArgs, handler, onToolResult, (toolResult) => buildShellResultFromToolResult(normalizedArgs, toolResult), (reason) => buildShellRejectedResult(normalizedArgs.command, normalizedArgs.workingDirectory, reason), (error) => buildShellFailureResult(normalizedArgs.command, normalizedArgs.workingDirectory, error), { toolCallId: args.toolCallId, toolName: "bash" });
652
+ // When using the batch handler (no shellStream), send buffered stdout/stderr
653
+ // after execution completes. With shellStream these were already sent live.
654
+ const sendBufferedOutput = !streamHandler;
655
+ const sanitizedExecResult = sanitizeShellExecResult(execResult);
656
+ if (stdoutFlushTimer)
657
+ clearTimeout(stdoutFlushTimer);
658
+ if (stderrFlushTimer)
659
+ clearTimeout(stderrFlushTimer);
660
+ flushStdout();
661
+ flushStderr();
662
+ sendShellStreamExitFromResult(h2Request, execMsg, sanitizedExecResult, sendBufferedOutput);
663
+ // Cursor can keep the turn pending when it receives only stream deltas.
664
+ // Send the final structured shellResult as completion acknowledgement.
665
+ sendExecClientMessage(h2Request, execMsg, "shellResult", sanitizedExecResult);
666
+ }
667
+ function sendShellStreamExitFromResult(h2Request, execMsg, execResult, sendBufferedOutput) {
668
+ const result = execResult.result;
669
+ switch (result.case) {
670
+ case "success": {
671
+ const value = result.value;
672
+ if (sendBufferedOutput) {
673
+ if (value.stdout) {
674
+ sendShellStreamEvent(h2Request, execMsg, {
675
+ case: "stdout",
676
+ value: create(ShellStreamStdoutSchema, { data: sanitizeSurrogates(value.stdout) }),
677
+ });
678
+ }
679
+ if (value.stderr) {
680
+ sendShellStreamEvent(h2Request, execMsg, {
681
+ case: "stderr",
682
+ value: create(ShellStreamStderrSchema, { data: sanitizeSurrogates(value.stderr) }),
683
+ });
684
+ }
685
+ }
686
+ sendShellStreamEvent(h2Request, execMsg, {
687
+ case: "exit",
688
+ value: create(ShellStreamExitSchema, {
689
+ code: value.exitCode,
690
+ cwd: value.workingDirectory,
691
+ aborted: false,
692
+ }),
693
+ });
694
+ return;
695
+ }
696
+ case "failure": {
697
+ const value = result.value;
698
+ if (sendBufferedOutput) {
699
+ if (value.stdout) {
700
+ sendShellStreamEvent(h2Request, execMsg, {
701
+ case: "stdout",
702
+ value: create(ShellStreamStdoutSchema, { data: sanitizeSurrogates(value.stdout) }),
703
+ });
704
+ }
705
+ if (value.stderr) {
706
+ sendShellStreamEvent(h2Request, execMsg, {
707
+ case: "stderr",
708
+ value: create(ShellStreamStderrSchema, { data: sanitizeSurrogates(value.stderr) }),
709
+ });
710
+ }
711
+ }
712
+ sendShellStreamEvent(h2Request, execMsg, {
713
+ case: "exit",
714
+ value: create(ShellStreamExitSchema, {
715
+ code: value.exitCode,
716
+ cwd: value.workingDirectory,
717
+ aborted: value.aborted,
718
+ abortReason: value.abortReason,
719
+ }),
720
+ });
721
+ return;
722
+ }
723
+ case "rejected": {
724
+ sendShellStreamEvent(h2Request, execMsg, { case: "rejected", value: result.value });
725
+ sendShellStreamEvent(h2Request, execMsg, {
726
+ case: "exit",
727
+ value: create(ShellStreamExitSchema, {
728
+ code: 1,
729
+ cwd: result.value.workingDirectory,
730
+ aborted: false,
731
+ }),
732
+ });
733
+ return;
734
+ }
735
+ case "timeout": {
736
+ const value = result.value;
737
+ sendShellStreamEvent(h2Request, execMsg, {
738
+ case: "stderr",
739
+ value: create(ShellStreamStderrSchema, {
740
+ data: `Command timed out after ${value.timeoutMs}ms`,
741
+ }),
742
+ });
743
+ sendShellStreamEvent(h2Request, execMsg, {
744
+ case: "exit",
745
+ value: create(ShellStreamExitSchema, {
746
+ code: 1,
747
+ cwd: value.workingDirectory,
748
+ aborted: true,
749
+ }),
750
+ });
751
+ return;
752
+ }
753
+ case "permissionDenied": {
754
+ sendShellStreamEvent(h2Request, execMsg, { case: "permissionDenied", value: result.value });
755
+ sendShellStreamEvent(h2Request, execMsg, {
756
+ case: "exit",
757
+ value: create(ShellStreamExitSchema, {
758
+ code: 1,
759
+ cwd: result.value.workingDirectory,
760
+ aborted: false,
761
+ }),
762
+ });
763
+ return;
764
+ }
765
+ default:
766
+ return;
767
+ }
768
+ }
769
+ async function handleExecServerMessage(execMsg, h2Request, execHandlers, onToolResult, requestContextTools, output, stream, state) {
770
+ const execCase = execMsg.message.case;
771
+ log("exec", "dispatch", { execCase, execId: execMsg.execId, hasHandlers: !!execHandlers });
772
+ if (!execCase) {
773
+ // A frame carrying a oneof number this build's `agent.proto` does not
774
+ // model at all. Returning silently strands the exec id — the server
775
+ // waits on a reply that never comes.
776
+ log("warn", "unknownExecVariant", { id: execMsg.id, execId: execMsg.execId });
777
+ sendExecClientThrow(h2Request, execMsg, "Unknown exec message variant", "unknown_exec_variant");
778
+ sendExecClientStreamClose(h2Request, execMsg);
779
+ return;
780
+ }
781
+ const stopExecHeartbeat = armCursorExecHeartbeat(h2Request, execMsg);
782
+ try {
783
+ await dispatchExecServerMessage({
784
+ execMsg,
785
+ h2Request,
786
+ execHandlers,
787
+ onToolResult,
788
+ requestContextTools,
789
+ output,
790
+ stream,
791
+ state,
792
+ });
793
+ }
794
+ catch (error) {
795
+ log("error", "execDispatch", {
796
+ error: error instanceof Error ? error.message : String(error),
797
+ id: execMsg.id,
798
+ execId: execMsg.execId,
799
+ });
800
+ sendExecClientThrow(h2Request, execMsg, "Local exec dispatch failed", "exec_dispatch_failed");
801
+ }
802
+ finally {
803
+ stopExecHeartbeat();
804
+ sendExecClientStreamClose(h2Request, execMsg);
805
+ }
806
+ }
807
+ async function dispatchExecServerMessage(context) {
808
+ const { execMsg, h2Request, execHandlers, onToolResult, requestContextTools, output, stream, state } = context;
809
+ const execCase = execMsg.message.case;
810
+ if (!execCase)
811
+ throw new Error("Expected a recognized exec message");
812
+ if (execCase === "requestContextArgs") {
813
+ const requestContext = create(RequestContextSchema, {
814
+ rules: [],
815
+ repositoryInfo: [],
816
+ tools: requestContextTools,
817
+ gitRepos: [],
818
+ projectLayouts: [],
819
+ mcpInstructions: [],
820
+ fileContents: {},
821
+ customSubagents: [],
822
+ });
823
+ const requestContextResult = create(RequestContextResultSchema, {
824
+ result: {
825
+ case: "success",
826
+ value: create(RequestContextSuccessSchema, { requestContext }),
827
+ },
828
+ });
829
+ sendExecClientMessage(h2Request, execMsg, "requestContextResult", requestContextResult);
830
+ return;
831
+ }
832
+ switch (execCase) {
833
+ case "readArgs": {
834
+ const args = execMsg.message.value;
835
+ if (!args.toolCallId)
836
+ args.toolCallId = randomUUID();
837
+ synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "read", {
838
+ path: args.path,
839
+ offset: args.offset,
840
+ limit: args.limit,
841
+ });
842
+ const { execResult } = await resolveExecHandler(args, execHandlers?.read?.bind(execHandlers), onToolResult, (toolResult) => buildReadResultFromToolResult(args.path, toolResult, args.offset !== undefined || args.limit !== undefined), (reason) => buildReadRejectedResult(args.path, reason), (error) => buildReadErrorResult(args.path, error), { toolCallId: args.toolCallId, toolName: "read" });
843
+ sendExecClientMessage(h2Request, execMsg, "readResult", execResult);
844
+ return;
845
+ }
846
+ case "lsArgs": {
847
+ const args = execMsg.message.value;
848
+ if (!args.toolCallId)
849
+ args.toolCallId = randomUUID();
850
+ // The bridge maps `ls` onto the local `ls` tool; mirror that here so
851
+ // the synthesized block matches the toolResult's `toolName`.
852
+ synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "ls", { path: piLsPath(args.path) });
853
+ const { execResult } = await resolveExecHandler(args, execHandlers?.ls?.bind(execHandlers), onToolResult, (toolResult) => buildLsResultFromToolResult(args.path, toolResult), (reason) => buildLsRejectedResult(args.path, reason), (error) => buildLsErrorResult(args.path, error), { toolCallId: args.toolCallId, toolName: "ls" });
854
+ sendExecClientMessage(h2Request, execMsg, "lsResult", execResult);
855
+ return;
856
+ }
857
+ case "grepArgs": {
858
+ const args = execMsg.message.value;
859
+ if (!args.toolCallId)
860
+ args.toolCallId = randomUUID();
861
+ // Cursor's model sometimes emits `grepArgs` with an empty `pattern`
862
+ // and a non-empty `glob`, expecting grep to list files matching the
863
+ // glob. Reject that up front with an actionable error.
864
+ const emptyPatternError = emptyGrepPatternRejection(args.pattern, args.glob);
865
+ if (emptyPatternError !== null) {
866
+ sendExecClientMessage(h2Request, execMsg, "grepResult", buildGrepErrorResult(emptyPatternError));
867
+ return;
868
+ }
869
+ // Mirror the coding-agent bridge's arg mapping so live UI (from
870
+ // `tool_execution_start`) and rebuilt transcript display identical args.
871
+ synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "grep", {
872
+ pattern: args.pattern,
873
+ path: args.path || undefined,
874
+ glob: args.glob || undefined,
875
+ ignoreCase: args.caseInsensitive === true ? true : undefined,
876
+ });
877
+ const { execResult } = await resolveExecHandler(args, execHandlers?.grep?.bind(execHandlers), onToolResult, (toolResult) => buildGrepResultFromToolResult(args, toolResult), (reason) => buildGrepErrorResult(reason), (error) => buildGrepErrorResult(error), { toolCallId: args.toolCallId, toolName: "grep" });
878
+ sendExecClientMessage(h2Request, execMsg, "grepResult", execResult);
879
+ return;
880
+ }
881
+ case "writeArgs": {
882
+ const args = execMsg.message.value;
883
+ if (!args.toolCallId)
884
+ args.toolCallId = randomUUID();
885
+ // Match the bridge: prefer `fileText`, fall back to decoded `fileBytes`.
886
+ const content = args.fileText ?? new TextDecoder().decode(args.fileBytes ?? new Uint8Array());
887
+ synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "write", {
888
+ path: args.path,
889
+ content,
890
+ });
891
+ const { execResult } = await resolveExecHandler(args, execHandlers?.write?.bind(execHandlers), onToolResult, (toolResult) => buildWriteResultFromToolResult({
892
+ path: args.path,
893
+ fileText: args.fileText,
894
+ fileBytes: args.fileBytes,
895
+ returnFileContentAfterWrite: args.returnFileContentAfterWrite,
896
+ }, toolResult), (reason) => buildWriteRejectedResult(args.path, reason), (error) => buildWriteErrorResult(args.path, error), { toolCallId: args.toolCallId, toolName: "write" });
897
+ sendExecClientMessage(h2Request, execMsg, "writeResult", execResult);
898
+ return;
899
+ }
900
+ case "deleteArgs": {
901
+ const args = execMsg.message.value;
902
+ if (!args.toolCallId)
903
+ args.toolCallId = randomUUID();
904
+ synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "delete", { path: args.path });
905
+ const { execResult } = await resolveExecHandler(args, execHandlers?.delete?.bind(execHandlers), onToolResult, (toolResult) => buildDeleteResultFromToolResult(args.path, toolResult), (reason) => buildDeleteRejectedResult(args.path, reason), (error) => buildDeleteErrorResult(args.path, error), { toolCallId: args.toolCallId, toolName: "delete" });
906
+ sendExecClientMessage(h2Request, execMsg, "deleteResult", execResult);
907
+ return;
908
+ }
909
+ case "shellArgs": {
910
+ const args = execMsg.message.value;
911
+ if (!args.toolCallId)
912
+ args.toolCallId = randomUUID();
913
+ const normalizedArgs = { ...args, workingDirectory: args.workingDirectory || process.cwd() };
914
+ const shellTimeout = args.timeout && args.timeout > 0 ? args.timeout : undefined;
915
+ synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "bash", {
916
+ command: composeShellCommand(args.command, args.workingDirectory || undefined),
917
+ timeout: shellTimeout,
918
+ });
919
+ const { execResult } = await resolveExecHandler(normalizedArgs, execHandlers?.shell?.bind(execHandlers), onToolResult, (toolResult) => buildShellResultFromToolResult(normalizedArgs, toolResult), (reason) => buildShellRejectedResult(normalizedArgs.command, normalizedArgs.workingDirectory, reason), (error) => buildShellFailureResult(normalizedArgs.command, normalizedArgs.workingDirectory, error), { toolCallId: args.toolCallId, toolName: "bash" });
920
+ sendExecClientMessage(h2Request, execMsg, "shellResult", sanitizeShellExecResult(execResult));
921
+ return;
922
+ }
923
+ case "shellStreamArgs": {
924
+ const args = execMsg.message.value;
925
+ if (!args.toolCallId)
926
+ args.toolCallId = randomUUID();
927
+ const shellStreamTimeout = args.timeout && args.timeout > 0 ? args.timeout : undefined;
928
+ synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "bash", {
929
+ command: composeShellCommand(args.command, args.workingDirectory || undefined),
930
+ timeout: shellStreamTimeout,
931
+ });
932
+ await handleShellStreamArgs(args, execMsg, h2Request, execHandlers, onToolResult);
933
+ return;
934
+ }
935
+ case "backgroundShellSpawnArgs": {
936
+ const args = execMsg.message.value;
937
+ const execResult = create(BackgroundShellSpawnResultSchema, {
938
+ result: {
939
+ case: "rejected",
940
+ value: create(ShellRejectedSchema, {
941
+ command: args.command,
942
+ workingDirectory: args.workingDirectory,
943
+ reason: "Not implemented",
944
+ isReadonly: false,
945
+ }),
946
+ },
947
+ });
948
+ sendExecClientMessage(h2Request, execMsg, "backgroundShellSpawnResult", execResult);
949
+ return;
950
+ }
951
+ case "writeShellStdinArgs": {
952
+ const execResult = create(WriteShellStdinResultSchema, {
953
+ result: {
954
+ case: "error",
955
+ value: create(WriteShellStdinErrorSchema, {
956
+ error: "Not implemented",
957
+ }),
958
+ },
959
+ });
960
+ sendExecClientMessage(h2Request, execMsg, "writeShellStdinResult", execResult);
961
+ return;
962
+ }
963
+ case "fetchArgs": {
964
+ const args = execMsg.message.value;
965
+ const execResult = create(FetchResultSchema, {
966
+ result: {
967
+ case: "error",
968
+ value: create(FetchErrorSchema, {
969
+ url: args.url,
970
+ error: "Not implemented",
971
+ }),
972
+ },
973
+ });
974
+ sendExecClientMessage(h2Request, execMsg, "fetchResult", execResult);
975
+ return;
976
+ }
977
+ case "diagnosticsArgs": {
978
+ const args = execMsg.message.value;
979
+ if (!args.toolCallId)
980
+ args.toolCallId = randomUUID();
981
+ const { execResult } = await resolveExecHandler(args, execHandlers?.diagnostics?.bind(execHandlers), onToolResult, (toolResult) => buildDiagnosticsResultFromToolResult(args.path, toolResult), (reason) => buildDiagnosticsRejectedResult(args.path, reason), (error) => buildDiagnosticsErrorResult(args.path, error),
982
+ // senpi has no local diagnostics tool; the frame is answered with a
983
+ // refusal and no block is synthesized (nothing ran).
984
+ null);
985
+ sendExecClientMessage(h2Request, execMsg, "diagnosticsResult", execResult);
986
+ return;
987
+ }
988
+ case "mcpArgs": {
989
+ const args = execMsg.message.value;
990
+ const mcpCall = decodeMcpCall(args);
991
+ // An approval probe, not an invocation: the frame asks whether the
992
+ // call would be permitted. Only a definite allow is approved; without
993
+ // a handler there is nothing to decide with, so it is refused. Either
994
+ // way no block is synthesized — nothing ran.
995
+ if (mcpCall.approvalOnly) {
996
+ const approved = (await execHandlers?.mcpApprovalPreflight?.(mcpCall)) === true;
997
+ sendExecClientMessage(h2Request, execMsg, "mcpResult", create(McpResultSchema, {
998
+ result: approved
999
+ ? { case: "approved", value: create(McpApprovedSchema, {}) }
1000
+ : {
1001
+ case: "rejected",
1002
+ value: create(McpRejectedSchema, {
1003
+ reason: `Tool "${mcpCall.toolName || mcpCall.name}" is not approved to run without asking.`,
1004
+ }),
1005
+ },
1006
+ }));
1007
+ return;
1008
+ }
1009
+ if (execHandlers?.mcp) {
1010
+ const existingBlock = output.content.find((block) => block.type === "toolCall" && block.id === mcpCall.toolCallId);
1011
+ if (existingBlock) {
1012
+ markCursorExecResolved(existingBlock);
1013
+ }
1014
+ else {
1015
+ synthesizeCursorExecToolCall(output, stream, state, mcpCall.toolCallId, mcpCall.toolName || mcpCall.name, mcpCall.args);
1016
+ state.resolvedMcpToolCallIds.add(mcpCall.toolCallId);
1017
+ }
1018
+ }
1019
+ const { execResult } = await resolveExecHandler(mcpCall, execHandlers?.mcp?.bind(execHandlers), onToolResult, (toolResult) => buildMcpResultFromToolResult(mcpCall, toolResult), (_reason) => buildMcpToolNotFoundResult(mcpCall), (error) => buildMcpErrorResult(error), execHandlers?.mcp ? { toolCallId: mcpCall.toolCallId, toolName: mcpCall.toolName } : null);
1020
+ sendExecClientMessage(h2Request, execMsg, "mcpResult", execResult);
1021
+ return;
1022
+ }
1023
+ case "listMcpResourcesExecArgs": {
1024
+ // This client hosts no MCP resources; the honest answer is an explicit
1025
+ // empty success. An unset-oneof result would read as "the call
1026
+ // produced nothing".
1027
+ const execResult = create(ListMcpResourcesExecResultSchema, {
1028
+ result: {
1029
+ case: "success",
1030
+ value: create(ListMcpResourcesSuccessSchema, { resources: [] }),
1031
+ },
1032
+ });
1033
+ sendExecClientMessage(h2Request, execMsg, "listMcpResourcesExecResult", execResult);
1034
+ return;
1035
+ }
1036
+ case "readMcpResourceExecArgs": {
1037
+ const args = execMsg.message.value;
1038
+ const execResult = create(ReadMcpResourceExecResultSchema, {
1039
+ result: { case: "notFound", value: create(ReadMcpResourceNotFoundSchema, { uri: args.uri }) },
1040
+ });
1041
+ sendExecClientMessage(h2Request, execMsg, "readMcpResourceExecResult", execResult);
1042
+ return;
1043
+ }
1044
+ case "recordScreenArgs": {
1045
+ const execResult = create(RecordScreenResultSchema, {
1046
+ result: { case: "failure", value: create(RecordScreenFailureSchema, { error: NOT_IMPLEMENTED }) },
1047
+ });
1048
+ sendExecClientMessage(h2Request, execMsg, "recordScreenResult", execResult);
1049
+ return;
1050
+ }
1051
+ case "computerUseArgs": {
1052
+ const execResult = create(ComputerUseResultSchema, {
1053
+ result: { case: "error", value: create(ComputerUseErrorSchema, { error: NOT_IMPLEMENTED }) },
1054
+ });
1055
+ sendExecClientMessage(h2Request, execMsg, "computerUseResult", execResult);
1056
+ return;
1057
+ }
1058
+ case "piReadArgs": {
1059
+ const args = execMsg.message.value;
1060
+ const toolCallId = randomUUID();
1061
+ const readArgs = piReadArgs(args.path, args.offset, args.limit);
1062
+ synthesizeCursorExecToolCall(output, stream, state, toolCallId, "read", readArgs ?? { path: args.path, offset: args.offset, limit: 0 });
1063
+ const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piRead?.bind(execHandlers), onToolResult, buildPiReadResult, buildPiReadError, buildPiReadError, { toolCallId, toolName: "read" });
1064
+ sendExecClientMessage(h2Request, execMsg, "piReadResult", execResult);
1065
+ return;
1066
+ }
1067
+ case "piBashArgs": {
1068
+ const args = execMsg.message.value;
1069
+ const toolCallId = randomUUID();
1070
+ synthesizeCursorExecToolCall(output, stream, state, toolCallId, "bash", {
1071
+ command: args.command,
1072
+ timeout: piTimeout(args.timeout),
1073
+ });
1074
+ const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piBash?.bind(execHandlers), onToolResult, buildPiBashResult, buildPiBashError, buildPiBashError, { toolCallId, toolName: "bash" });
1075
+ sendExecClientMessage(h2Request, execMsg, "piBashResult", execResult);
1076
+ return;
1077
+ }
1078
+ case "piEditArgs": {
1079
+ const args = execMsg.message.value;
1080
+ const toolCallId = randomUUID();
1081
+ // `PiEditReplacement[]` maps 1:1 onto the local `edit` tool's
1082
+ // `edits[{oldText,newText}]` shape.
1083
+ synthesizeCursorExecToolCall(output, stream, state, toolCallId, "edit", {
1084
+ path: args.path,
1085
+ edits: args.edits.map((edit) => ({ oldText: edit.oldText, newText: edit.newText })),
1086
+ });
1087
+ const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piEdit?.bind(execHandlers), onToolResult, buildPiEditResult, buildPiEditRejected, buildPiEditError, { toolCallId, toolName: "edit" });
1088
+ sendExecClientMessage(h2Request, execMsg, "piEditResult", execResult);
1089
+ return;
1090
+ }
1091
+ case "piWriteArgs": {
1092
+ const args = execMsg.message.value;
1093
+ const toolCallId = randomUUID();
1094
+ synthesizeCursorExecToolCall(output, stream, state, toolCallId, "write", {
1095
+ path: args.path,
1096
+ content: args.content,
1097
+ });
1098
+ const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piWrite?.bind(execHandlers), onToolResult, buildPiWriteResult, buildPiWriteRejected, buildPiWriteError, { toolCallId, toolName: "write" });
1099
+ sendExecClientMessage(h2Request, execMsg, "piWriteResult", execResult);
1100
+ return;
1101
+ }
1102
+ case "piGrepArgs": {
1103
+ const args = execMsg.message.value;
1104
+ const toolCallId = randomUUID();
1105
+ synthesizeCursorExecToolCall(output, stream, state, toolCallId, "grep", {
1106
+ pattern: args.pattern,
1107
+ path: args.path || undefined,
1108
+ glob: args.glob || undefined,
1109
+ ignoreCase: args.ignoreCase === true ? true : undefined,
1110
+ literal: args.literal === true ? true : undefined,
1111
+ context: args.context,
1112
+ limit: piLimit(args.limit),
1113
+ });
1114
+ const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piGrep?.bind(execHandlers), onToolResult, buildPiGrepResult, buildPiGrepError, buildPiGrepError, { toolCallId, toolName: "grep" });
1115
+ sendExecClientMessage(h2Request, execMsg, "piGrepResult", execResult);
1116
+ return;
1117
+ }
1118
+ case "piFindArgs": {
1119
+ const args = execMsg.message.value;
1120
+ const toolCallId = randomUUID();
1121
+ synthesizeCursorExecToolCall(output, stream, state, toolCallId, "find", {
1122
+ pattern: args.pattern,
1123
+ path: args.path || undefined,
1124
+ limit: piLimit(args.limit),
1125
+ });
1126
+ const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piFind?.bind(execHandlers), onToolResult, buildPiFindResult, buildPiFindError, buildPiFindError, { toolCallId, toolName: "find" });
1127
+ sendExecClientMessage(h2Request, execMsg, "piFindResult", execResult);
1128
+ return;
1129
+ }
1130
+ case "piLsArgs": {
1131
+ const args = execMsg.message.value;
1132
+ const toolCallId = randomUUID();
1133
+ synthesizeCursorExecToolCall(output, stream, state, toolCallId, "ls", {
1134
+ path: piLsPath(args.path),
1135
+ limit: piLimit(args.limit),
1136
+ });
1137
+ const { execResult } = await resolveExecHandler({ args, toolCallId }, execHandlers?.piLs?.bind(execHandlers), onToolResult, buildPiLsResult, buildPiLsError, buildPiLsError, { toolCallId, toolName: "ls" });
1138
+ sendExecClientMessage(h2Request, execMsg, "piLsResult", execResult);
1139
+ return;
1140
+ }
1141
+ case "miniSweAgentBashArgs": {
1142
+ // Same `ShellArgs`/`ShellResult` pair as `shellArgs`, under its own
1143
+ // frame number, so the existing shell handler answers it unchanged.
1144
+ const args = execMsg.message.value;
1145
+ if (!args.toolCallId)
1146
+ args.toolCallId = randomUUID();
1147
+ const normalizedArgs = { ...args, workingDirectory: args.workingDirectory || process.cwd() };
1148
+ synthesizeCursorExecToolCall(output, stream, state, args.toolCallId, "bash", {
1149
+ command: composeShellCommand(args.command, args.workingDirectory || undefined),
1150
+ timeout: args.timeout && args.timeout > 0 ? args.timeout : undefined,
1151
+ });
1152
+ const { execResult } = await resolveExecHandler(normalizedArgs, execHandlers?.shell?.bind(execHandlers), onToolResult, (toolResult) => buildShellResultFromToolResult(normalizedArgs, toolResult), (reason) => buildShellRejectedResult(normalizedArgs.command, normalizedArgs.workingDirectory, reason), (error) => buildShellFailureResult(normalizedArgs.command, normalizedArgs.workingDirectory, error), { toolCallId: args.toolCallId, toolName: "bash" });
1153
+ sendExecClientMessage(h2Request, execMsg, "miniSweAgentBashResult", sanitizeShellExecResult(execResult));
1154
+ return;
1155
+ }
1156
+ case "redactedReadArgs": {
1157
+ // The server expects the client to strip secrets from the content
1158
+ // first. No redaction is implemented here, and serving a plain read
1159
+ // would hand back exactly the unredacted bytes the frame exists to
1160
+ // withhold.
1161
+ const args = execMsg.message.value;
1162
+ sendExecClientMessage(h2Request, execMsg, "redactedReadResult", buildReadErrorResult(args.path, "Secret redaction is not implemented by this client"));
1163
+ return;
1164
+ }
1165
+ case "mcpStateExecArgs": {
1166
+ const args = execMsg.message.value;
1167
+ sendExecClientMessage(h2Request, execMsg, "mcpStateExecResult", buildMcpStateResult(requestContextTools, args.serverIdentifiers));
1168
+ return;
1169
+ }
1170
+ case "executeHookArgs": {
1171
+ const args = execMsg.message.value;
1172
+ const execResult = buildNeutralHookResult(args.request);
1173
+ if (!execResult) {
1174
+ sendExecClientThrow(h2Request, execMsg, `Unsupported hook request: ${args.request?.request.case ?? "unset"}`, "unknown_hook_request");
1175
+ return;
1176
+ }
1177
+ sendExecClientMessage(h2Request, execMsg, "executeHookResult", execResult);
1178
+ return;
1179
+ }
1180
+ case "subagentArgs": {
1181
+ const execResult = create(SubagentResultSchema, {
1182
+ result: {
1183
+ case: "error",
1184
+ value: create(SubagentErrorSchema, { error: `Subagents are ${NOT_IMPLEMENTED_SUFFIX}` }),
1185
+ },
1186
+ });
1187
+ sendExecClientMessage(h2Request, execMsg, "subagentResult", execResult);
1188
+ return;
1189
+ }
1190
+ case "subagentAwaitArgs": {
1191
+ // No subagent was ever spawned, so every awaited id is genuinely unknown.
1192
+ const args = execMsg.message.value;
1193
+ const execResult = create(SubagentAwaitResultSchema, {
1194
+ result: {
1195
+ case: "notFound",
1196
+ value: create(SubagentAwaitNotFoundSchema, { agentId: args.agentId }),
1197
+ },
1198
+ });
1199
+ sendExecClientMessage(h2Request, execMsg, "subagentAwaitResult", execResult);
1200
+ return;
1201
+ }
1202
+ case "forceBackgroundShellArgs": {
1203
+ // Backgrounding targets a running tool call by id. This client runs
1204
+ // every shell to completion in band, so there is never one to move.
1205
+ const execResult = create(ForceBackgroundShellResultSchema, {
1206
+ status: ForceBackgroundShellStatus.NOT_FOUND,
1207
+ });
1208
+ sendExecClientMessage(h2Request, execMsg, "forceBackgroundShellResult", execResult);
1209
+ return;
1210
+ }
1211
+ case "forceBackgroundSubagentArgs": {
1212
+ const execResult = create(ForceBackgroundSubagentResultSchema, {
1213
+ status: ForceBackgroundSubagentStatus.NOT_FOUND,
1214
+ });
1215
+ sendExecClientMessage(h2Request, execMsg, "forceBackgroundSubagentResult", execResult);
1216
+ return;
1217
+ }
1218
+ case "smartModeClassifierArgs": {
1219
+ // The classifier decides whether a risky action needs approval.
1220
+ // Answering `ALLOW` would silently wave through actions the server
1221
+ // asked us to judge, so the honest answer is that no classifier
1222
+ // exists here.
1223
+ const execResult = create(SmartModeClassifierResultSchema, {
1224
+ result: {
1225
+ case: "error",
1226
+ value: create(SmartModeClassifierErrorSchema, {
1227
+ error: `Smart-mode classification is ${NOT_IMPLEMENTED_SUFFIX}`,
1228
+ }),
1229
+ },
1230
+ });
1231
+ sendExecClientMessage(h2Request, execMsg, "smartModeClassifierResult", execResult);
1232
+ return;
1233
+ }
1234
+ case "canvasDiagnosticsArgs": {
1235
+ const args = execMsg.message.value;
1236
+ const execResult = create(CanvasDiagnosticsResultSchema, {
1237
+ result: {
1238
+ case: "error",
1239
+ value: create(CanvasDiagnosticsErrorSchema, {
1240
+ path: args.path,
1241
+ error: `Canvas diagnostics are ${NOT_IMPLEMENTED_SUFFIX}`,
1242
+ }),
1243
+ },
1244
+ });
1245
+ sendExecClientMessage(h2Request, execMsg, "canvasDiagnosticsResult", execResult);
1246
+ return;
1247
+ }
1248
+ case "shellAllowlistPrecheckArgs": {
1249
+ // The prechecks ask "is this pre-approved, so may it skip the approval
1250
+ // prompt?". This client keeps no allowlist, so the answer is always
1251
+ // no: `false` costs an approval round-trip, `true` would grant one
1252
+ // that was never configured.
1253
+ sendExecClientMessage(h2Request, execMsg, "shellAllowlistPrecheckResult", create(ShellAllowlistPrecheckResultSchema, { allowlisted: false }));
1254
+ return;
1255
+ }
1256
+ case "mcpAllowlistPrecheckArgs": {
1257
+ sendExecClientMessage(h2Request, execMsg, "mcpAllowlistPrecheckResult", create(McpAllowlistPrecheckResultSchema, { allowlisted: false }));
1258
+ return;
1259
+ }
1260
+ case "webFetchAllowlistPrecheckArgs": {
1261
+ sendExecClientMessage(h2Request, execMsg, "webFetchAllowlistPrecheckResult", create(WebFetchAllowlistPrecheckResultSchema, { allowlisted: false }));
1262
+ return;
1263
+ }
1264
+ case "conversationSearchArgs": {
1265
+ // Cursor conversation history lives server-side; this client keeps no
1266
+ // local index of it to search. The streamed envelope announces this
1267
+ // call but the interaction decoder builds no block for it, so the
1268
+ // block and its paired result are synthesized here.
1269
+ const args = execMsg.message.value;
1270
+ const toolCallId = args.toolCallId || randomUUID();
1271
+ const error = `Conversation search is ${NOT_IMPLEMENTED_SUFFIX}`;
1272
+ synthesizeCursorExecToolCall(output, stream, state, toolCallId, "search_conversations", {
1273
+ query: args.query,
1274
+ limit: args.limit,
1275
+ });
1276
+ await pairSynthesizedExecResult(state, onToolResult, toolCallId, "search_conversations", error);
1277
+ const execResult = create(ConversationSearchResultSchema, {
1278
+ result: { case: "error", value: create(ConversationSearchErrorSchema, { error }) },
1279
+ });
1280
+ sendExecClientMessage(h2Request, execMsg, "conversationSearchResult", execResult);
1281
+ return;
1282
+ }
1283
+ case "agentStoreConflictArgs": {
1284
+ // The agent store is Cursor's own on-disk journal; this client never
1285
+ // writes one, so it has no conflict events to replay.
1286
+ const execResult = create(AgentStoreConflictResultSchema, {
1287
+ result: {
1288
+ case: "error",
1289
+ value: create(AgentStoreConflictErrorSchema, {
1290
+ error: `Agent store conflicts are ${NOT_IMPLEMENTED_SUFFIX}`,
1291
+ }),
1292
+ },
1293
+ });
1294
+ sendExecClientMessage(h2Request, execMsg, "agentStoreConflictResult", execResult);
1295
+ return;
1296
+ }
1297
+ case "gitDiffRequest": {
1298
+ // `GetDiffResponse` has no error variant, so any in-band answer is a
1299
+ // claim that a diff was computed; a `throw` is the only truthful reply.
1300
+ sendExecClientThrow(h2Request, execMsg, `Git diff is ${NOT_IMPLEMENTED_SUFFIX}`, "exec_variant_unsupported");
1301
+ return;
1302
+ }
1303
+ default: {
1304
+ // A frame number this build recognises structurally but has no answer
1305
+ // for. Distinct from the unset-case path above: there the client
1306
+ // cannot even name the frame.
1307
+ log("warn", "unhandledExecMessage", { execCase });
1308
+ sendExecClientThrow(h2Request, execMsg, `No handler for exec message of type ${execCase}`, "exec_variant_unsupported");
1309
+ }
1310
+ }
1311
+ }
1312
+ function armCursorExecHeartbeat(h2Request, execMsg) {
1313
+ return armExecHeartbeat({
1314
+ intervalMs: EXEC_HEARTBEAT_INTERVAL_MS,
1315
+ isClosed: () => h2Request.closed,
1316
+ writeHeartbeat: (onComplete) => {
1317
+ const controlMessage = create(ExecClientControlMessageSchema, {
1318
+ message: {
1319
+ case: "heartbeat",
1320
+ value: create(ExecClientHeartbeatSchema, { id: execMsg.id }),
1321
+ },
1322
+ });
1323
+ const clientMessage = create(AgentClientMessageSchema, {
1324
+ message: { case: "execClientControlMessage", value: controlMessage },
1325
+ });
1326
+ h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, clientMessage)), onComplete);
1327
+ log("execClientControl", "heartbeat", { id: execMsg.id, execId: execMsg.execId });
1328
+ },
1329
+ });
1330
+ }
1331
+ /**
1332
+ * Send one typed answer on the exec channel.
1333
+ *
1334
+ * `ExecClientMessage["message"]` is a discriminated union pairing each case
1335
+ * with its own result type, so the generic is keyed on the case: passing a
1336
+ * `ReadResult` under `"shellResult"` is a compile error rather than a wire
1337
+ * message the server rejects at runtime.
1338
+ */
1339
+ function sendExecClientMessage(h2Request, execMsg, messageCase, value) {
1340
+ const execClientMessage = create(ExecClientMessageSchema, {
1341
+ id: execMsg.id,
1342
+ execId: execMsg.execId,
1343
+ message: { case: messageCase, value },
1344
+ });
1345
+ const clientMessage = create(AgentClientMessageSchema, {
1346
+ message: { case: "execClientMessage", value: execClientMessage },
1347
+ });
1348
+ h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, clientMessage)));
1349
+ log("execClientMessage", messageCase);
1350
+ }
1351
+ /**
1352
+ * Fail one exec frame in band.
1353
+ *
1354
+ * `ExecClientThrow` is the protocol's failure channel for a frame that cannot
1355
+ * be answered at all — as opposed to a frame answered with its own typed
1356
+ * error variant, which means "the tool ran and failed". The server surfaces
1357
+ * the error to the model instead of blocking on a reply that never comes.
1358
+ */
1359
+ function sendExecClientThrow(h2Request, execMsg, error, errorCode) {
1360
+ const controlMessage = create(ExecClientControlMessageSchema, {
1361
+ message: {
1362
+ case: "throw",
1363
+ value: create(ExecClientThrowSchema, { id: execMsg.id, error, errorCode }),
1364
+ },
1365
+ });
1366
+ const clientMessage = create(AgentClientMessageSchema, {
1367
+ message: { case: "execClientControlMessage", value: controlMessage },
1368
+ });
1369
+ h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, clientMessage)));
1370
+ log("execClientControl", "throw", { id: execMsg.id, execId: execMsg.execId, error, errorCode });
1371
+ }
1372
+ function sendExecClientStreamClose(h2Request, execMsg) {
1373
+ const closeMessage = create(ExecClientControlMessageSchema, {
1374
+ message: {
1375
+ case: "streamClose",
1376
+ value: create(ExecClientStreamCloseSchema, {
1377
+ id: execMsg.id,
1378
+ }),
1379
+ },
1380
+ });
1381
+ const clientMessage = create(AgentClientMessageSchema, {
1382
+ message: { case: "execClientControlMessage", value: closeMessage },
1383
+ });
1384
+ h2Request.write(frameConnectMessage(toBinary(AgentClientMessageSchema, clientMessage)));
1385
+ log("execClientControl", "streamClose", { id: execMsg.id, execId: execMsg.execId });
1386
+ }
1387
+ /**
1388
+ * Exported for tests: dispatches one exec frame onto its handler.
1389
+ *
1390
+ * Every exit pairs a `toolResult`. The synthesized block was already marked
1391
+ * `kCursorExecResolved` before this runs, so the agent loop emits no
1392
+ * placeholder for it: a path that returns without a result leaves the call
1393
+ * unpaired and transcript rebuilds strip the whole interaction. The three
1394
+ * result-less paths — no handler installed, a handler that produced nothing,
1395
+ * and a thrown handler — therefore synthesize one from the same text the
1396
+ * server sees in `execResult`.
1397
+ *
1398
+ * `pairing` is required so a new callsite cannot silently recreate the
1399
+ * orphan, and nullable for the callers whose block is NOT pre-resolved (MCP
1400
+ * without an `mcp` handler, which the agent loop runs locally and pairs
1401
+ * itself, and frames that never synthesize a block).
1402
+ */
1403
+ export async function resolveExecHandler(args, handler, onToolResult, buildFromToolResult, buildRejected, buildError, pairing) {
1404
+ const pair = async (text, isError) => {
1405
+ if (!pairing)
1406
+ return undefined;
1407
+ const synthesized = {
1408
+ role: "toolResult",
1409
+ toolCallId: pairing.toolCallId,
1410
+ toolName: pairing.toolName,
1411
+ content: [{ type: "text", text }],
1412
+ isError,
1413
+ timestamp: Date.now(),
1414
+ };
1415
+ return await applyToolResultHandler(synthesized, onToolResult);
1416
+ };
1417
+ if (!handler) {
1418
+ const reason = "Tool not available";
1419
+ return { execResult: buildRejected(reason), toolResult: await pair(reason, true) };
1420
+ }
1421
+ try {
1422
+ const handlerResult = await handler(args);
1423
+ const { execResult, toolResult } = splitExecHandlerResult(handlerResult);
1424
+ const finalToolResult = await applyToolResultHandler(toolResult, onToolResult);
1425
+ if (execResult) {
1426
+ // TResult-only is a supported return form, so the transcript entry
1427
+ // has to be synthesized here, derived from the raw result so the two
1428
+ // views stay consistent.
1429
+ return {
1430
+ execResult,
1431
+ toolResult: finalToolResult ?? (await pair(...describeExecResult(execResult))),
1432
+ };
1433
+ }
1434
+ if (finalToolResult) {
1435
+ return { execResult: buildFromToolResult(finalToolResult), toolResult: finalToolResult };
1436
+ }
1437
+ const reason = "Tool returned no result";
1438
+ return { execResult: buildRejected(reason), toolResult: await pair(reason, true) };
1439
+ }
1440
+ catch (error) {
1441
+ const message = error instanceof Error ? error.message : String(error);
1442
+ return { execResult: buildError(message), toolResult: await pair(message, true) };
1443
+ }
1444
+ }
1445
+ /**
1446
+ * Derive the transcript state of an exec result the handler returned in the
1447
+ * TResult-only form, which carries no `toolResult` to copy it from.
1448
+ *
1449
+ * Every exec result in `agent.proto` is a `oneof result` whose success
1450
+ * variant is named `success` — the rest are failures. MCP is the one shape
1451
+ * where `success` is not enough: `McpSuccess.is_error` carries an
1452
+ * application-level tool failure inside the success variant.
1453
+ */
1454
+ function describeExecResult(execResult) {
1455
+ const result = execResult?.result;
1456
+ const variant = result?.case;
1457
+ if (variant === "success") {
1458
+ const success = result?.value;
1459
+ if (!success?.isError)
1460
+ return ["Tool produced no transcript result", false];
1461
+ return [mcpContentToText(success.content) || "MCP tool reported an error", true];
1462
+ }
1463
+ if (!variant)
1464
+ return ["Tool produced no transcript result", false];
1465
+ const value = result?.value;
1466
+ return [value?.error || value?.reason || `Tool call ${variant}`, true];
1467
+ }
1468
+ /** Flatten `McpSuccess.content` into transcript text. */
1469
+ function mcpContentToText(content) {
1470
+ if (!Array.isArray(content))
1471
+ return "";
1472
+ const parts = [];
1473
+ for (const item of content) {
1474
+ const inner = item?.content;
1475
+ if (inner?.case === "text" && inner.value?.text)
1476
+ parts.push(inner.value.text);
1477
+ }
1478
+ return parts.join("\n");
1479
+ }
1480
+ function splitExecHandlerResult(result) {
1481
+ if (isToolResultMessage(result)) {
1482
+ return { toolResult: result };
1483
+ }
1484
+ if (result && typeof result === "object") {
1485
+ const record = result;
1486
+ if ("execResult" in record) {
1487
+ const { execResult, toolResult } = record;
1488
+ return { execResult, toolResult };
1489
+ }
1490
+ if ("toolResult" in record && !isToolResultMessage(record)) {
1491
+ const { result: execResult, toolResult } = record;
1492
+ return { execResult, toolResult };
1493
+ }
1494
+ if ("result" in record && !("$typeName" in record)) {
1495
+ const { result: execResult, toolResult } = record;
1496
+ return { execResult, toolResult };
1497
+ }
1498
+ }
1499
+ return { execResult: result };
1500
+ }
1501
+ function isToolResultMessage(value) {
1502
+ return !!value && typeof value === "object" && value.role === "toolResult";
1503
+ }
1504
+ async function applyToolResultHandler(toolResult, onToolResult) {
1505
+ if (!toolResult || !onToolResult) {
1506
+ return toolResult;
1507
+ }
1508
+ const updated = await onToolResult(toolResult);
1509
+ return updated ?? toolResult;
1510
+ }
1511
+ function toolResultToText(toolResult) {
1512
+ return toolResult.content.map((item) => (item.type === "text" ? item.text : `[${item.mimeType} image]`)).join("\n");
1513
+ }
1514
+ function toolResultWasTruncated(toolResult) {
1515
+ if (!toolResult.details || typeof toolResult.details !== "object") {
1516
+ return false;
1517
+ }
1518
+ const truncation = toolResult.details.truncation;
1519
+ return !!truncation?.truncated;
1520
+ }
1521
+ function toolResultDetailBoolean(toolResult, key) {
1522
+ if (!toolResult.details || typeof toolResult.details !== "object") {
1523
+ return false;
1524
+ }
1525
+ const value = toolResult.details[key];
1526
+ return typeof value === "boolean" ? value : false;
1527
+ }
1528
+ /** The file's own line count, when the tool recorded one. */
1529
+ function readTotalLinesFromDetails(toolResult) {
1530
+ const details = toolResult.details;
1531
+ if (!details || typeof details !== "object")
1532
+ return undefined;
1533
+ const direct = "totalLines" in details ? details.totalLines : undefined;
1534
+ if (typeof direct === "number" && Number.isFinite(direct))
1535
+ return direct;
1536
+ const meta = "meta" in details ? details.meta : undefined;
1537
+ if (!meta || typeof meta !== "object")
1538
+ return undefined;
1539
+ const truncation = "truncation" in meta ? meta.truncation : undefined;
1540
+ if (!truncation || typeof truncation !== "object")
1541
+ return undefined;
1542
+ const totalLines = "totalLines" in truncation ? truncation.totalLines : undefined;
1543
+ return typeof totalLines === "number" && Number.isFinite(totalLines) ? totalLines : undefined;
1544
+ }
1545
+ function readFileSizeFromDetails(toolResult) {
1546
+ const details = toolResult.details;
1547
+ if (!details || typeof details !== "object" || !("fileSize" in details))
1548
+ return undefined;
1549
+ const { fileSize } = details;
1550
+ return typeof fileSize === "number" && Number.isSafeInteger(fileSize) && fileSize >= 0 ? fileSize : undefined;
1551
+ }
1552
+ function buildReadResultFromToolResult(path, toolResult, rangeApplied = false) {
1553
+ const text = toolResultToText(toolResult);
1554
+ if (toolResult.isError) {
1555
+ return buildReadErrorResult(path, text || "Read failed");
1556
+ }
1557
+ // Counting the payload is only the file's length when the payload is the
1558
+ // whole file: under a windowed read, answering a 20-line page of a 100-line
1559
+ // file with `total_lines: 20` tells a paginating server it reached the end.
1560
+ const totalLines = readTotalLinesFromDetails(toolResult) ?? (rangeApplied ? 0 : text ? text.split("\n").length : 0);
1561
+ return create(ReadResultSchema, {
1562
+ result: {
1563
+ case: "success",
1564
+ value: create(ReadSuccessSchema, {
1565
+ path,
1566
+ totalLines,
1567
+ fileSize: BigInt(readFileSizeFromDetails(toolResult) ?? Buffer.byteLength(text, "utf-8")),
1568
+ truncated: toolResultWasTruncated(toolResult),
1569
+ output: { case: "content", value: text },
1570
+ rangeApplied,
1571
+ }),
1572
+ },
1573
+ });
1574
+ }
1575
+ function buildReadErrorResult(path, error) {
1576
+ return create(ReadResultSchema, {
1577
+ result: {
1578
+ case: "error",
1579
+ value: create(ReadErrorSchema, { path, error }),
1580
+ },
1581
+ });
1582
+ }
1583
+ function buildReadRejectedResult(path, reason) {
1584
+ return create(ReadResultSchema, {
1585
+ result: {
1586
+ case: "rejected",
1587
+ value: create(ReadRejectedSchema, { path, reason }),
1588
+ },
1589
+ });
1590
+ }
1591
+ function buildWriteResultFromToolResult(args, toolResult) {
1592
+ const text = toolResultToText(toolResult);
1593
+ if (toolResult.isError) {
1594
+ return buildWriteErrorResult(args.path, text || "Write failed");
1595
+ }
1596
+ const fileText = args.fileText ?? "";
1597
+ const fileSize = args.fileBytes?.length ?? Buffer.byteLength(fileText, "utf-8");
1598
+ const linesCreated = fileText ? fileText.split("\n").length : 0;
1599
+ return create(WriteResultSchema, {
1600
+ result: {
1601
+ case: "success",
1602
+ value: create(WriteSuccessSchema, {
1603
+ path: args.path,
1604
+ linesCreated,
1605
+ fileSize,
1606
+ fileContentAfterWrite: args.returnFileContentAfterWrite ? fileText : undefined,
1607
+ }),
1608
+ },
1609
+ });
1610
+ }
1611
+ function buildWriteErrorResult(path, error) {
1612
+ return create(WriteResultSchema, {
1613
+ result: {
1614
+ case: "error",
1615
+ value: create(WriteErrorSchema, { path, error }),
1616
+ },
1617
+ });
1618
+ }
1619
+ function buildWriteRejectedResult(path, reason) {
1620
+ return create(WriteResultSchema, {
1621
+ result: {
1622
+ case: "rejected",
1623
+ value: create(WriteRejectedSchema, { path, reason }),
1624
+ },
1625
+ });
1626
+ }
1627
+ function buildDeleteResultFromToolResult(path, toolResult) {
1628
+ const text = toolResultToText(toolResult);
1629
+ if (toolResult.isError) {
1630
+ return buildDeleteErrorResult(path, text || "Delete failed");
1631
+ }
1632
+ return create(DeleteResultSchema, {
1633
+ result: {
1634
+ case: "success",
1635
+ value: create(DeleteSuccessSchema, {
1636
+ path,
1637
+ deletedFile: path,
1638
+ fileSize: BigInt(0),
1639
+ prevContent: "",
1640
+ }),
1641
+ },
1642
+ });
1643
+ }
1644
+ function buildDeleteErrorResult(path, error) {
1645
+ return create(DeleteResultSchema, {
1646
+ result: {
1647
+ case: "error",
1648
+ value: create(DeleteErrorSchema, { path, error }),
1649
+ },
1650
+ });
1651
+ }
1652
+ function buildDeleteRejectedResult(path, reason) {
1653
+ return create(DeleteResultSchema, {
1654
+ result: {
1655
+ case: "rejected",
1656
+ value: create(DeleteRejectedSchema, { path, reason }),
1657
+ },
1658
+ });
1659
+ }
1660
+ function buildShellResultFromToolResult(args, toolResult) {
1661
+ const output = toolResultToText(toolResult);
1662
+ if (toolResult.isError) {
1663
+ return buildShellFailureResult(args.command, args.workingDirectory, output || "Shell failed");
1664
+ }
1665
+ return create(ShellResultSchema, {
1666
+ result: {
1667
+ case: "success",
1668
+ value: create(ShellSuccessSchema, {
1669
+ command: args.command,
1670
+ workingDirectory: args.workingDirectory,
1671
+ exitCode: 0,
1672
+ signal: "",
1673
+ stdout: output,
1674
+ stderr: "",
1675
+ executionTime: 0,
1676
+ }),
1677
+ },
1678
+ });
1679
+ }
1680
+ function buildShellFailureResult(command, workingDirectory, error) {
1681
+ return create(ShellResultSchema, {
1682
+ result: {
1683
+ case: "failure",
1684
+ value: create(ShellFailureSchema, {
1685
+ command,
1686
+ workingDirectory,
1687
+ exitCode: 1,
1688
+ signal: "",
1689
+ stdout: "",
1690
+ stderr: error,
1691
+ executionTime: 0,
1692
+ aborted: false,
1693
+ }),
1694
+ },
1695
+ });
1696
+ }
1697
+ function buildShellRejectedResult(command, workingDirectory, reason) {
1698
+ return create(ShellResultSchema, {
1699
+ result: {
1700
+ case: "rejected",
1701
+ value: create(ShellRejectedSchema, {
1702
+ command,
1703
+ workingDirectory,
1704
+ reason,
1705
+ isReadonly: false,
1706
+ }),
1707
+ },
1708
+ });
1709
+ }
1710
+ function buildLsResultFromToolResult(path, toolResult) {
1711
+ const text = toolResultToText(toolResult);
1712
+ if (toolResult.isError) {
1713
+ return buildLsErrorResult(path, text || "Ls failed");
1714
+ }
1715
+ const rootPath = path || ".";
1716
+ const entries = text
1717
+ .split("\n")
1718
+ .map((line) => line.trim())
1719
+ .filter((line) => line.length > 0 && !line.startsWith("["));
1720
+ const childrenDirs = [];
1721
+ const childrenFiles = [];
1722
+ for (const entry of entries) {
1723
+ const name = entry.split(" (")[0];
1724
+ if (name.endsWith("/")) {
1725
+ const dirName = name.slice(0, -1);
1726
+ childrenDirs.push(create(LsDirectoryTreeNodeSchema, {
1727
+ absPath: `${rootPath.replace(/\/$/, "")}/${dirName}`,
1728
+ childrenDirs: [],
1729
+ childrenFiles: [],
1730
+ childrenWereProcessed: false,
1731
+ fullSubtreeExtensionCounts: {},
1732
+ numFiles: 0,
1733
+ }));
1734
+ }
1735
+ else {
1736
+ childrenFiles.push(create(LsDirectoryTreeNode_FileSchema, { name }));
1737
+ }
1738
+ }
1739
+ const root = create(LsDirectoryTreeNodeSchema, {
1740
+ absPath: rootPath,
1741
+ childrenDirs,
1742
+ childrenFiles,
1743
+ childrenWereProcessed: true,
1744
+ fullSubtreeExtensionCounts: {},
1745
+ numFiles: childrenFiles.length,
1746
+ });
1747
+ return create(LsResultSchema, {
1748
+ result: {
1749
+ case: "success",
1750
+ value: create(LsSuccessSchema, { directoryTreeRoot: root }),
1751
+ },
1752
+ });
1753
+ }
1754
+ function buildLsErrorResult(path, error) {
1755
+ return create(LsResultSchema, {
1756
+ result: {
1757
+ case: "error",
1758
+ value: create(LsErrorSchema, { path, error }),
1759
+ },
1760
+ });
1761
+ }
1762
+ function buildLsRejectedResult(path, reason) {
1763
+ return create(LsResultSchema, {
1764
+ result: {
1765
+ case: "rejected",
1766
+ value: create(LsRejectedSchema, { path, reason }),
1767
+ },
1768
+ });
1769
+ }
1770
+ function buildGrepResultFromToolResult(args, toolResult) {
1771
+ const text = toolResultToText(toolResult);
1772
+ if (toolResult.isError) {
1773
+ return buildGrepErrorResult(text || "Grep failed");
1774
+ }
1775
+ const outputMode = args.outputMode || "content";
1776
+ const clientTruncated = toolResultDetailBoolean(toolResult, "truncated");
1777
+ const lines = text
1778
+ .split("\n")
1779
+ .map((line) => line.trimEnd())
1780
+ .filter((line) => line.length > 0 && !line.startsWith("[") && !line.toLowerCase().startsWith("no matches"));
1781
+ const workspaceKey = args.path || ".";
1782
+ let unionResult;
1783
+ if (outputMode === "files_with_matches") {
1784
+ const files = lines;
1785
+ unionResult = create(GrepUnionResultSchema, {
1786
+ result: {
1787
+ case: "files",
1788
+ value: create(GrepFilesResultSchema, {
1789
+ files,
1790
+ totalFiles: files.length,
1791
+ clientTruncated,
1792
+ ripgrepTruncated: false,
1793
+ offsetApplied: args.offset,
1794
+ }),
1795
+ },
1796
+ });
1797
+ }
1798
+ else if (outputMode === "count") {
1799
+ const counts = lines
1800
+ .map((line) => {
1801
+ const separatorIndex = line.lastIndexOf(":");
1802
+ if (separatorIndex === -1) {
1803
+ return null;
1804
+ }
1805
+ const file = line.slice(0, separatorIndex);
1806
+ const count = Number.parseInt(line.slice(separatorIndex + 1), 10);
1807
+ if (!file || Number.isNaN(count)) {
1808
+ return null;
1809
+ }
1810
+ return create(GrepFileCountSchema, { file, count });
1811
+ })
1812
+ .filter((entry) => entry !== null);
1813
+ const totalMatches = counts.reduce((sum, entry) => sum + entry.count, 0);
1814
+ unionResult = create(GrepUnionResultSchema, {
1815
+ result: {
1816
+ case: "count",
1817
+ value: create(GrepCountResultSchema, {
1818
+ counts,
1819
+ totalFiles: counts.length,
1820
+ totalMatches,
1821
+ clientTruncated,
1822
+ ripgrepTruncated: false,
1823
+ offsetApplied: args.offset,
1824
+ }),
1825
+ },
1826
+ });
1827
+ }
1828
+ else {
1829
+ const matchMap = new Map();
1830
+ let totalMatchedLines = 0;
1831
+ for (const line of lines) {
1832
+ const matchLine = line.match(/^(.+?):(\d+):\s?(.*)$/);
1833
+ const contextLine = line.match(/^(.+?)-(\d+)-\s?(.*)$/);
1834
+ const match = matchLine ?? contextLine;
1835
+ if (!match) {
1836
+ continue;
1837
+ }
1838
+ const [, file, lineNumber, content] = match;
1839
+ const isContextLine = Boolean(contextLine);
1840
+ const list = matchMap.get(file) ?? [];
1841
+ list.push({ line: Number(lineNumber), content, isContextLine });
1842
+ matchMap.set(file, list);
1843
+ if (!isContextLine) {
1844
+ totalMatchedLines += 1;
1845
+ }
1846
+ }
1847
+ const matches = Array.from(matchMap.entries()).map(([file, fileMatches]) => create(GrepFileMatchSchema, {
1848
+ file,
1849
+ matches: fileMatches.map((entry) => create(GrepContentMatchSchema, {
1850
+ lineNumber: entry.line,
1851
+ content: entry.content,
1852
+ contentTruncated: false,
1853
+ isContextLine: entry.isContextLine,
1854
+ })),
1855
+ }));
1856
+ const totalLines = matches.reduce((sum, entry) => sum + entry.matches.length, 0);
1857
+ unionResult = create(GrepUnionResultSchema, {
1858
+ result: {
1859
+ case: "content",
1860
+ value: create(GrepContentResultSchema, {
1861
+ matches,
1862
+ totalLines,
1863
+ totalMatchedLines,
1864
+ clientTruncated,
1865
+ ripgrepTruncated: false,
1866
+ offsetApplied: args.offset,
1867
+ }),
1868
+ },
1869
+ });
1870
+ }
1871
+ return create(GrepResultSchema, {
1872
+ result: {
1873
+ case: "success",
1874
+ value: create(GrepSuccessSchema, {
1875
+ pattern: args.pattern,
1876
+ path: args.path || "",
1877
+ outputMode,
1878
+ workspaceResults: { [workspaceKey]: unionResult },
1879
+ }),
1880
+ },
1881
+ });
1882
+ }
1883
+ function buildGrepErrorResult(error) {
1884
+ return create(GrepResultSchema, {
1885
+ result: {
1886
+ case: "error",
1887
+ value: create(GrepErrorSchema, { error }),
1888
+ },
1889
+ });
1890
+ }
1891
+ /**
1892
+ * Reject a Cursor exec-channel `grepArgs` frame whose `pattern` is empty or
1893
+ * whitespace-only. Returns an actionable error message (with a `glob`-aware
1894
+ * hint when the model likely meant to list files) or `null` when the pattern
1895
+ * is valid and grep should run. Exported for tests.
1896
+ */
1897
+ export function emptyGrepPatternRejection(pattern, glob) {
1898
+ if (pattern && pattern.trim().length > 0)
1899
+ return null;
1900
+ if (glob && glob.length > 0) {
1901
+ return (`grep pattern is required (received an empty pattern). To list files matching "${glob}", ` +
1902
+ `pass a non-empty regex (e.g. ".") and set path to that glob, or use the ls/read tool instead.`);
1903
+ }
1904
+ return "grep pattern is required (received an empty pattern).";
1905
+ }
1906
+ function buildDiagnosticsResultFromToolResult(path, toolResult) {
1907
+ const text = toolResultToText(toolResult);
1908
+ if (toolResult.isError) {
1909
+ return buildDiagnosticsErrorResult(path, text || "Diagnostics failed");
1910
+ }
1911
+ return create(DiagnosticsResultSchema, {
1912
+ result: {
1913
+ case: "success",
1914
+ value: create(DiagnosticsSuccessSchema, {
1915
+ path,
1916
+ diagnostics: [],
1917
+ totalDiagnostics: 0,
1918
+ }),
1919
+ },
1920
+ });
1921
+ }
1922
+ function buildDiagnosticsErrorResult(_path, error) {
1923
+ return create(DiagnosticsResultSchema, {
1924
+ result: {
1925
+ case: "error",
1926
+ value: create(DiagnosticsErrorSchema, { error }),
1927
+ },
1928
+ });
1929
+ }
1930
+ function buildDiagnosticsRejectedResult(path, reason) {
1931
+ return create(DiagnosticsResultSchema, {
1932
+ result: {
1933
+ case: "rejected",
1934
+ value: create(DiagnosticsRejectedSchema, { path, reason }),
1935
+ },
1936
+ });
1937
+ }
1938
+ function parseToolArgsJson(text) {
1939
+ const trimmed = text.trim();
1940
+ if (!trimmed) {
1941
+ return text;
1942
+ }
1943
+ try {
1944
+ return parseJsonWithRepair(trimmed);
1945
+ }
1946
+ catch {
1947
+ return text;
1948
+ }
1949
+ }
1950
+ function decodeMcpArgValue(value) {
1951
+ try {
1952
+ const parsedValue = fromBinary(ValueSchema, value);
1953
+ const jsonValue = toJson(ValueSchema, parsedValue);
1954
+ if (typeof jsonValue === "string") {
1955
+ return parseToolArgsJson(jsonValue);
1956
+ }
1957
+ return jsonValue;
1958
+ }
1959
+ catch { }
1960
+ const text = new TextDecoder().decode(value);
1961
+ return parseToolArgsJson(text);
1962
+ }
1963
+ function decodeMcpArgsMap(args) {
1964
+ if (!args) {
1965
+ return undefined;
1966
+ }
1967
+ const decoded = {};
1968
+ for (const [key, value] of Object.entries(args)) {
1969
+ decoded[key] = decodeMcpArgValue(value);
1970
+ }
1971
+ return decoded;
1972
+ }
1973
+ function decodeMcpCall(args) {
1974
+ const decodedArgs = {};
1975
+ for (const [key, value] of Object.entries(args.args ?? {})) {
1976
+ decodedArgs[key] = decodeMcpArgValue(value);
1977
+ }
1978
+ return {
1979
+ name: args.name,
1980
+ providerIdentifier: args.providerIdentifier,
1981
+ toolName: args.toolName || args.name,
1982
+ toolCallId: args.toolCallId,
1983
+ args: decodedArgs,
1984
+ rawArgs: args.args ?? {},
1985
+ approvalOnly: args.smartModeApprovalOnly === true,
1986
+ };
1987
+ }
1988
+ /**
1989
+ * Map Cursor's `TodoStatus` enum onto display statuses. Cancelled (4) maps to
1990
+ * `abandoned` rather than collapsing to `pending`, which would resurrect a
1991
+ * task the model explicitly cancelled.
1992
+ */
1993
+ function mapTodoStatusValue(status) {
1994
+ switch (status) {
1995
+ case 2:
1996
+ return "in_progress";
1997
+ case 3:
1998
+ return "completed";
1999
+ case 4:
2000
+ return "abandoned";
2001
+ default:
2002
+ return "pending";
2003
+ }
2004
+ }
2005
+ function selectTodoCalls(toolCall) {
2006
+ const oneof = toolCall.tool;
2007
+ if (oneof?.case === "updateTodosToolCall")
2008
+ return { update: oneof.value };
2009
+ if (oneof?.case === "readTodosToolCall")
2010
+ return { read: oneof.value };
2011
+ return { update: toolCall.updateTodosToolCall, read: toolCall.readTodosToolCall };
2012
+ }
2013
+ function mapTodoSnapshot(todos) {
2014
+ return todos.map((todo) => ({
2015
+ content: typeof todo.content === "string" ? todo.content : "",
2016
+ status: mapTodoStatusValue(typeof todo.status === "number" ? todo.status : undefined),
2017
+ }));
2018
+ }
2019
+ /**
2020
+ * `ToolCall.tool` is a protobuf oneof: a wire-decoded message exposes the
2021
+ * variant as `{ case, value }` and NEVER as a flattened property. The flat
2022
+ * fallback is kept for hand-shaped test fixtures.
2023
+ */
2024
+ function selectMcpCall(toolCall) {
2025
+ const oneof = toolCall?.tool;
2026
+ if (oneof?.case === "mcpToolCall")
2027
+ return oneof.value;
2028
+ return toolCall?.mcpToolCall;
2029
+ }
2030
+ /**
2031
+ * The streamed `ToolCall` variants whose block the exec channel owns.
2032
+ *
2033
+ * Each of these is announced on the interaction stream AND dispatched as its
2034
+ * own `ExecServerMessage` frame, so the block is synthesized once, by the
2035
+ * exec handler, which is the side that has the result.
2036
+ */
2037
+ const EXEC_OWNED_TOOL_CALL_CASES = new Set([
2038
+ "piReadToolCall",
2039
+ "piBashToolCall",
2040
+ "piEditToolCall",
2041
+ "piWriteToolCall",
2042
+ "piGrepToolCall",
2043
+ "piFindToolCall",
2044
+ "piLsToolCall",
2045
+ "listMcpResourcesToolCall",
2046
+ "readMcpResourceToolCall",
2047
+ ]);
2048
+ function isExecOwnedToolCall(toolCall) {
2049
+ const variant = toolCall?.tool?.case;
2050
+ return variant !== undefined && EXEC_OWNED_TOOL_CALL_CASES.has(variant);
2051
+ }
2052
+ /**
2053
+ * Close every tool-call block still open when the stream ends.
2054
+ *
2055
+ * Not just the last one started: with interleaved calls several can be open
2056
+ * at once, and an unclosed block leaves its live card animating and its call
2057
+ * unpaired. Only blocks fed by a streamed argument buffer get reparsed.
2058
+ * Server-owned blocks (`connect-scm`, `todo`) are also paired here: they are
2059
+ * stamped resolved the moment they open, so only their completion frame pairs
2060
+ * a result — a transport that closes before that frame would leave the call
2061
+ * unpaired. MCP blocks are excluded even when resolved: the exec dispatch
2062
+ * that marked them owns their result.
2063
+ */
2064
+ export function flushOpenToolCalls(output, stream, state) {
2065
+ const openBlocks = new Set(state.openToolCalls.values());
2066
+ if (state.currentToolCall)
2067
+ openBlocks.add(state.currentToolCall);
2068
+ for (const block of openBlocks) {
2069
+ const idx = output.content.indexOf(block);
2070
+ const partialJson = block[kStreamingPartialJson];
2071
+ if (partialJson !== undefined) {
2072
+ block.arguments = parseStreamingJson(partialJson);
2073
+ clearStreamingPartialJson(block);
2074
+ }
2075
+ const kind = block[kStreamingBlockKind];
2076
+ if (kind === "connect-scm" || kind === "todo") {
2077
+ void state.onToolResult?.({
2078
+ role: "toolResult",
2079
+ toolCallId: block.id,
2080
+ toolName: block.name,
2081
+ content: [{ type: "text", text: "The connection to Cursor closed before this call completed." }],
2082
+ isError: true,
2083
+ timestamp: Date.now(),
2084
+ });
2085
+ }
2086
+ stream.push({ type: "toolcall_end", contentIndex: idx, toolCall: block, partial: output });
2087
+ }
2088
+ state.openToolCalls.clear();
2089
+ state.setToolCall(null);
2090
+ }
2091
+ /**
2092
+ * Retain a freshly opened streamed tool-call block, keyed by the interaction
2093
+ * envelope's `call_id`. `currentToolCall` is still set as the fallback for
2094
+ * frames that carry no `call_id`.
2095
+ */
2096
+ function retainStreamedCall(state, block, envelopeId) {
2097
+ if (envelopeId)
2098
+ state.openToolCalls.set(envelopeId, block);
2099
+ state.setToolCall(block);
2100
+ }
2101
+ /**
2102
+ * The open block a streamed update addresses, or `null` to ignore the update.
2103
+ *
2104
+ * Cursor interleaves calls: `start A, start B, complete A` is legal, so the
2105
+ * update must reach block A even though B opened last. An id naming no open
2106
+ * block is ignored rather than misapplied. A missing id falls back to the
2107
+ * current block.
2108
+ */
2109
+ function resolveStreamedCall(state, envelopeId) {
2110
+ if (!envelopeId)
2111
+ return state.currentToolCall;
2112
+ const keyed = state.openToolCalls.get(envelopeId);
2113
+ if (keyed)
2114
+ return keyed;
2115
+ const current = state.currentToolCall;
2116
+ return current && current[kStreamingEnvelopeId] === undefined ? current : null;
2117
+ }
2118
+ /** Release a settled block from both the keyed map and the current slot. */
2119
+ function releaseStreamedCall(state, block) {
2120
+ const envelopeId = block[kStreamingEnvelopeId];
2121
+ if (envelopeId)
2122
+ state.openToolCalls.delete(envelopeId);
2123
+ if (state.currentToolCall === block)
2124
+ state.setToolCall(null);
2125
+ }
2126
+ function selectConnectScmCall(toolCall) {
2127
+ const oneof = toolCall?.tool;
2128
+ if (oneof?.case === "connectScmToolCall")
2129
+ return oneof.value;
2130
+ return toolCall?.connectScmToolCall;
2131
+ }
2132
+ function selectConnectScmRepository(call) {
2133
+ const target = call?.args?.target;
2134
+ if (target?.case === "github")
2135
+ return target.value?.repository;
2136
+ return call?.args?.github?.repository;
2137
+ }
2138
+ /** Render a settled `ConnectScmResult` as the text of its paired tool result. */
2139
+ function describeConnectScmResult(call) {
2140
+ const result = call?.result?.result;
2141
+ switch (result?.case) {
2142
+ case "success":
2143
+ return { text: "SCM connected", isError: false };
2144
+ case "error":
2145
+ return { text: result.value?.error || "SCM connection failed", isError: true };
2146
+ case "rejected":
2147
+ return { text: result.value?.reason || "SCM connection rejected", isError: true };
2148
+ default:
2149
+ return { text: "SCM connection reported no result", isError: true };
2150
+ }
2151
+ }
2152
+ /**
2153
+ * Extract the authoritative full todo list from a completed native todo call.
2154
+ * Only `result.success.todos` is authoritative; filtered/partial/ambiguous
2155
+ * responses return `null`, which the caller MUST treat as "nothing to mirror".
2156
+ */
2157
+ function extractTodoSnapshot(toolCall) {
2158
+ const { update, read } = selectTodoCalls(toolCall);
2159
+ if (read && ((read.args?.statusFilter?.length ?? 0) > 0 || (read.args?.idFilter?.length ?? 0) > 0)) {
2160
+ return null;
2161
+ }
2162
+ const call = update ?? read;
2163
+ if (!call)
2164
+ return null;
2165
+ const result = call.result?.result;
2166
+ if (result?.case !== "success")
2167
+ return null;
2168
+ const todos = result.value?.todos;
2169
+ if (!todos)
2170
+ return null;
2171
+ const totalCount = result.value?.totalCount;
2172
+ if (typeof totalCount === "number" && totalCount !== todos.length) {
2173
+ return null;
2174
+ }
2175
+ if (read && todos.length === 0) {
2176
+ return null;
2177
+ }
2178
+ const mapped = mapTodoSnapshot(todos);
2179
+ if (mapped.some((todo) => todo.content.length === 0))
2180
+ return null;
2181
+ return {
2182
+ todos: mapped,
2183
+ merged: result.value?.wasMerge === true,
2184
+ };
2185
+ }
2186
+ /** Error text when the server itself rejected the call. */
2187
+ function extractTodoError(toolCall) {
2188
+ const { update, read } = selectTodoCalls(toolCall);
2189
+ const result = (update ?? read)?.result?.result;
2190
+ if (result?.case !== "error")
2191
+ return null;
2192
+ const error = result.value?.error;
2193
+ return typeof error === "string" && error.length > 0 ? error : "Todo operation failed";
2194
+ }
2195
+ /** Args echoed onto the synthesized display block, for rendering only. */
2196
+ function buildTodoDisplayArgs(toolCall) {
2197
+ const args = selectTodoCalls(toolCall).update?.args;
2198
+ return {
2199
+ todos: args?.todos ? mapTodoSnapshot(args.todos) : [],
2200
+ merge: args?.merge === true ? true : undefined,
2201
+ };
2202
+ }
2203
+ /**
2204
+ * Paired result for a server-resolved native todo call. Nothing else would
2205
+ * produce a `toolResult` for the block — and transcript rebuilds strip any
2206
+ * `toolCall` left unpaired.
2207
+ */
2208
+ function buildTodoToolResult(toolCallId, snapshot, error) {
2209
+ const text = error ?? (snapshot ? formatTodoSnapshotSummary(snapshot.todos) : "Todo snapshot not mirrored");
2210
+ return {
2211
+ role: "toolResult",
2212
+ toolCallId,
2213
+ toolName: "todo",
2214
+ content: [{ type: "text", text }],
2215
+ isError: error !== null,
2216
+ timestamp: Date.now(),
2217
+ };
2218
+ }
2219
+ function formatTodoSnapshotSummary(todos) {
2220
+ if (todos.length === 0)
2221
+ return "No todos";
2222
+ const done = todos.filter((todo) => todo.status === "completed").length;
2223
+ return `${done}/${todos.length} tasks completed`;
2224
+ }
2225
+ function buildMcpResultFromToolResult(_mcpCall, toolResult) {
2226
+ if (toolResult.isError) {
2227
+ return buildMcpErrorResult(toolResultToText(toolResult) || "MCP tool failed");
2228
+ }
2229
+ const content = toolResult.content.map((item) => {
2230
+ if (item.type === "image") {
2231
+ return create(McpToolResultContentItemSchema, {
2232
+ content: {
2233
+ case: "image",
2234
+ value: create(McpImageContentSchema, {
2235
+ data: Uint8Array.from(Buffer.from(item.data, "base64")),
2236
+ mimeType: item.mimeType,
2237
+ }),
2238
+ },
2239
+ });
2240
+ }
2241
+ return create(McpToolResultContentItemSchema, {
2242
+ content: {
2243
+ case: "text",
2244
+ value: create(McpTextContentSchema, { text: item.text }),
2245
+ },
2246
+ });
2247
+ });
2248
+ return create(McpResultSchema, {
2249
+ result: {
2250
+ case: "success",
2251
+ value: create(McpSuccessSchema, {
2252
+ content,
2253
+ isError: false,
2254
+ }),
2255
+ },
2256
+ });
2257
+ }
2258
+ function buildMcpToolNotFoundResult(mcpCall) {
2259
+ return create(McpResultSchema, {
2260
+ result: {
2261
+ case: "toolNotFound",
2262
+ value: create(McpToolNotFoundSchema, { name: mcpCall.toolName, availableTools: [] }),
2263
+ },
2264
+ });
2265
+ }
2266
+ function buildMcpErrorResult(error) {
2267
+ return create(McpResultSchema, {
2268
+ result: {
2269
+ case: "error",
2270
+ value: create(McpErrorSchema, { error }),
2271
+ },
2272
+ });
2273
+ }
2274
+ /**
2275
+ * Merge the decoded completion-frame `McpArgs` map into the args assembled
2276
+ * from streamed `args_text_delta` snapshots.
2277
+ *
2278
+ * The completion frame is authoritative for the scalars it carries — but it
2279
+ * can omit oversized parameters entirely and can downgrade a structured value
2280
+ * to its raw string fallback. Rules per key:
2281
+ * - completion key absent → keep the streamed value.
2282
+ * - completion is a string while the streamed value is structured → keep the
2283
+ * streamed value (the completion frame downgraded it).
2284
+ * - otherwise → completion wins.
2285
+ */
2286
+ export function mergeCursorMcpToolCallArgs(streamed, completion) {
2287
+ const merged = { ...(streamed ?? {}) };
2288
+ if (!completion)
2289
+ return merged;
2290
+ for (const [key, completionValue] of Object.entries(completion)) {
2291
+ const streamedValue = merged[key];
2292
+ if (typeof completionValue === "string" && streamedValue !== null && typeof streamedValue === "object") {
2293
+ continue;
2294
+ }
2295
+ merged[key] = completionValue;
2296
+ }
2297
+ return merged;
2298
+ }
2299
+ function endCurrentTextBlock(output, stream, state) {
2300
+ const block = state.currentTextBlock;
2301
+ if (!block)
2302
+ return;
2303
+ const idx = output.content.indexOf(block);
2304
+ stream.push({
2305
+ type: "text_end",
2306
+ contentIndex: idx,
2307
+ content: block.text,
2308
+ partial: output,
2309
+ });
2310
+ state.setTextBlock(null);
2311
+ }
2312
+ function endCurrentThinkingBlock(output, stream, state) {
2313
+ const block = state.currentThinkingBlock;
2314
+ if (!block)
2315
+ return;
2316
+ const idx = output.content.indexOf(block);
2317
+ stream.push({
2318
+ type: "thinking_end",
2319
+ contentIndex: idx,
2320
+ content: block.thinking,
2321
+ partial: output,
2322
+ });
2323
+ state.setThinkingBlock(null);
2324
+ }
2325
+ /**
2326
+ * Synthesize a completed `toolCall` content block for a Cursor exec-channel
2327
+ * native tool or for an MCP exec frame whose corresponding interaction block
2328
+ * is absent.
2329
+ *
2330
+ * Args arrive complete on the exec message, so the block opens and closes in
2331
+ * one step. Without this the persisted assistant message carries only
2332
+ * text/thinking blocks, and on replay the following `toolResult` messages
2333
+ * have no matching `toolCall.id`.
2334
+ *
2335
+ * The block is stamped with {@link kCursorExecResolved} so the agent loop's
2336
+ * execution pass skips it — Cursor's server-driven exec channel already ran
2337
+ * the tool via the bridge and buffered the result. Exported for tests.
2338
+ */
2339
+ export function synthesizeCursorExecToolCall(output, stream, state, toolCallId, toolName, args) {
2340
+ endCurrentTextBlock(output, stream, state);
2341
+ endCurrentThinkingBlock(output, stream, state);
2342
+ const block = {
2343
+ type: "toolCall",
2344
+ id: toolCallId,
2345
+ name: toolName,
2346
+ arguments: omitUndefinedArgs(args),
2347
+ [kStreamingBlockIndex]: output.content.length,
2348
+ [kStreamingBlockKind]: "cursor-exec",
2349
+ [kCursorExecResolved]: true,
2350
+ };
2351
+ output.content.push(block);
2352
+ const idx = output.content.length - 1;
2353
+ stream.push({ type: "toolcall_start", contentIndex: idx, partial: output });
2354
+ stream.push({ type: "toolcall_end", contentIndex: idx, toolCall: block, partial: output });
2355
+ }
2356
+ /**
2357
+ * Pair a `toolResult` for a synthesized block the client answered itself,
2358
+ * without ever consulting a handler. Frames answered from a fixed verdict —
2359
+ * no handler, no local execution — still need the pair: the block was stamped
2360
+ * {@link kCursorExecResolved}, so the agent loop emits no placeholder for it
2361
+ * and transcript rebuilds strip an unpaired call.
2362
+ */
2363
+ async function pairSynthesizedExecResult(state, onToolResult, toolCallId, toolName, text, isError = true) {
2364
+ const synthesized = {
2365
+ role: "toolResult",
2366
+ toolCallId,
2367
+ toolName,
2368
+ content: [{ type: "text", text }],
2369
+ isError,
2370
+ timestamp: Date.now(),
2371
+ };
2372
+ const sink = onToolResult ?? state.onToolResult;
2373
+ if (!sink)
2374
+ return;
2375
+ await sink(synthesized);
2376
+ }
2377
+ /**
2378
+ * Throttle threshold for mid-stream argument JSON parses: reparse only when
2379
+ * the buffer grew by this much since the last successful parse, keeping total
2380
+ * parse work bounded while the authoritative full parse runs on completion.
2381
+ */
2382
+ const STREAMING_ARGS_REPARSE_DELTA = 1024;
2383
+ /** Exported for tests: drives one Cursor interaction update through the streaming state machine. */
2384
+ export function processInteractionUpdate(update, output, stream, state, usageState) {
2385
+ const updateCase = update.message?.case;
2386
+ log("interactionUpdate", updateCase);
2387
+ if (updateCase === "textDelta") {
2388
+ const delta = update.message.value.text || "";
2389
+ if (!state.currentTextBlock) {
2390
+ const block = {
2391
+ type: "text",
2392
+ text: "",
2393
+ [kStreamingBlockIndex]: output.content.length,
2394
+ };
2395
+ output.content.push(block);
2396
+ state.setTextBlock(block);
2397
+ stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
2398
+ }
2399
+ state.currentTextBlock.text += delta;
2400
+ const idx = output.content.indexOf(state.currentTextBlock);
2401
+ stream.push({ type: "text_delta", contentIndex: idx, delta, partial: output });
2402
+ }
2403
+ else if (updateCase === "thinkingDelta") {
2404
+ const delta = update.message.value.text || "";
2405
+ if (!state.currentThinkingBlock) {
2406
+ const block = {
2407
+ type: "thinking",
2408
+ thinking: "",
2409
+ [kStreamingBlockIndex]: output.content.length,
2410
+ };
2411
+ output.content.push(block);
2412
+ state.setThinkingBlock(block);
2413
+ stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
2414
+ }
2415
+ state.currentThinkingBlock.thinking += delta;
2416
+ const idx = output.content.indexOf(state.currentThinkingBlock);
2417
+ stream.push({ type: "thinking_delta", contentIndex: idx, delta, partial: output });
2418
+ }
2419
+ else if (updateCase === "thinkingCompleted") {
2420
+ endCurrentThinkingBlock(output, stream, state);
2421
+ }
2422
+ else if (updateCase === "toolCallStarted" && selectConnectScmCall(update.message.value.toolCall)) {
2423
+ // `connect_scm` is resolved entirely server-side and has NO exec frame:
2424
+ // the streamed pair is the only signal this client sees. The
2425
+ // authoritative outcome rides on the COMPLETION's `result` oneof, so the
2426
+ // block is opened here and settled there.
2427
+ endCurrentTextBlock(output, stream, state);
2428
+ endCurrentThinkingBlock(output, stream, state);
2429
+ const scmCall = selectConnectScmCall(update.message.value.toolCall);
2430
+ const repository = selectConnectScmRepository(scmCall);
2431
+ const block = {
2432
+ type: "toolCall",
2433
+ id: scmCall?.args?.toolCallId || update.message.value.callId || randomUUID(),
2434
+ name: "connect_scm",
2435
+ arguments: repository ? { owner: repository.owner, repo: repository.repo } : {},
2436
+ [kStreamingBlockIndex]: output.content.length,
2437
+ [kStreamingBlockKind]: "connect-scm",
2438
+ [kStreamingEnvelopeId]: update.message.value.callId || undefined,
2439
+ [kCursorExecResolved]: true,
2440
+ };
2441
+ output.content.push(block);
2442
+ retainStreamedCall(state, block, update.message.value.callId);
2443
+ stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
2444
+ }
2445
+ else if (updateCase === "toolCallStarted" && isExecOwnedToolCall(update.message.value.toolCall)) {
2446
+ // The exec channel already synthesized this block (and marked it
2447
+ // resolved) when it ran the tool locally, so the streamed announcement
2448
+ // must not create a second one.
2449
+ endCurrentTextBlock(output, stream, state);
2450
+ endCurrentThinkingBlock(output, stream, state);
2451
+ log("exec", "streamedToolCallOwnedByExec", { case: update.message.value.toolCall?.tool?.case });
2452
+ }
2453
+ else if (updateCase === "toolCallStarted") {
2454
+ endCurrentTextBlock(output, stream, state);
2455
+ endCurrentThinkingBlock(output, stream, state);
2456
+ const toolCall = update.message.value.toolCall;
2457
+ if (toolCall) {
2458
+ const mcpCall = selectMcpCall(toolCall);
2459
+ if (mcpCall) {
2460
+ const args = mcpCall.args || {};
2461
+ const id = args.toolCallId || randomUUID();
2462
+ const resolvedByExec = state.resolvedMcpToolCallIds.delete(id);
2463
+ if (resolvedByExec && output.content.some((block) => block.type === "toolCall" && block.id === id)) {
2464
+ return;
2465
+ }
2466
+ const block = {
2467
+ type: "toolCall",
2468
+ id,
2469
+ // Same precedence as `decodeMcpCall` (`toolName || name`), which
2470
+ // is what the exec channel pairs its result under.
2471
+ name: args.toolName || args.name || "",
2472
+ arguments: {},
2473
+ [kStreamingBlockIndex]: output.content.length,
2474
+ [kStreamingPartialJson]: "",
2475
+ [kStreamingBlockKind]: "mcp",
2476
+ [kStreamingEnvelopeId]: update.message.value.callId || undefined,
2477
+ };
2478
+ if (resolvedByExec) {
2479
+ markCursorExecResolved(block);
2480
+ }
2481
+ output.content.push(block);
2482
+ retainStreamedCall(state, block, update.message.value.callId);
2483
+ stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
2484
+ return;
2485
+ }
2486
+ // Cursor resolves `update_todos` / `read_todos` server-side and
2487
+ // settles them on the tool call's `result`. Both blocks are stamped
2488
+ // resolved so the agent loop never runs them locally.
2489
+ const todoCalls = selectTodoCalls(toolCall);
2490
+ if (todoCalls.update || todoCalls.read) {
2491
+ const callId = update.message.value.callId || randomUUID();
2492
+ const block = {
2493
+ type: "toolCall",
2494
+ id: callId,
2495
+ name: "todo",
2496
+ arguments: buildTodoDisplayArgs(toolCall),
2497
+ [kStreamingBlockIndex]: output.content.length,
2498
+ [kStreamingBlockKind]: "todo",
2499
+ [kStreamingEnvelopeId]: update.message.value.callId || undefined,
2500
+ [kCursorExecResolved]: true,
2501
+ };
2502
+ output.content.push(block);
2503
+ retainStreamedCall(state, block, update.message.value.callId);
2504
+ stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
2505
+ }
2506
+ }
2507
+ }
2508
+ else if (updateCase === "toolCallDelta" || updateCase === "partialToolCall") {
2509
+ // An argument delta belonging to a different call must not be appended
2510
+ // to this block's buffer, which would corrupt the JSON both parse.
2511
+ const target = resolveStreamedCall(state, update.message.value.callId);
2512
+ if (target?.[kStreamingBlockKind] === "mcp") {
2513
+ // Cursor's `args_text_delta` is "aggregated args text so far": each
2514
+ // delta is a cumulative snapshot. Strip the prefix we already have to
2515
+ // recover the new suffix; fall back to treating the value as an
2516
+ // incremental fragment when it doesn't extend the buffer.
2517
+ const snapshot = update.message.value.argsTextDelta || "";
2518
+ const current = target[kStreamingPartialJson] ?? "";
2519
+ const chunk = snapshot.startsWith(current) ? snapshot.slice(current.length) : snapshot;
2520
+ if (chunk.length === 0) {
2521
+ return;
2522
+ }
2523
+ const nextBuffer = current + chunk;
2524
+ target[kStreamingPartialJson] = nextBuffer;
2525
+ // Throttle mid-stream parses to keep total parse work bounded; the
2526
+ // authoritative full parse runs in `toolCallCompleted`.
2527
+ const lastParseLen = target[kStreamingLastParseLen] ?? 0;
2528
+ if (nextBuffer.length - lastParseLen >= STREAMING_ARGS_REPARSE_DELTA) {
2529
+ target.arguments = parseStreamingJson(nextBuffer);
2530
+ target[kStreamingLastParseLen] = nextBuffer.length;
2531
+ }
2532
+ const idx = output.content.indexOf(target);
2533
+ stream.push({ type: "toolcall_delta", contentIndex: idx, delta: chunk, partial: output });
2534
+ }
2535
+ }
2536
+ else if (updateCase === "toolCallCompleted") {
2537
+ // Correlate on the envelope's `call_id`, NOT the block id: MCP, Pi and
2538
+ // SCM blocks are filed under the id inside the call's `args`, and that
2539
+ // need not equal the envelope id.
2540
+ const settled = resolveStreamedCall(state, update.message.value.callId);
2541
+ if (settled) {
2542
+ const toolCall = update.message.value.toolCall;
2543
+ if (settled[kStreamingBlockKind] === "mcp") {
2544
+ // Authoritative full parse of the accumulated argument buffer.
2545
+ const partial = settled[kStreamingPartialJson];
2546
+ if (partial !== undefined) {
2547
+ settled.arguments = parseStreamingJson(partial);
2548
+ }
2549
+ const decodedArgs = decodeMcpArgsMap(selectMcpCall(toolCall)?.args?.args);
2550
+ settled.arguments = mergeCursorMcpToolCallArgs(settled.arguments, decodedArgs);
2551
+ }
2552
+ else if (settled[kStreamingBlockKind] === "connect-scm") {
2553
+ // The authoritative outcome arrives only here. Late args are merged
2554
+ // too — a start frame may announce the call before the target
2555
+ // repository is known.
2556
+ const scmCall = selectConnectScmCall(toolCall);
2557
+ const repository = selectConnectScmRepository(scmCall);
2558
+ if (repository) {
2559
+ settled.arguments = { owner: repository.owner, repo: repository.repo };
2560
+ }
2561
+ const { text, isError } = describeConnectScmResult(scmCall);
2562
+ void state.onToolResult?.({
2563
+ role: "toolResult",
2564
+ toolCallId: settled.id,
2565
+ toolName: "connect_scm",
2566
+ content: [{ type: "text", text }],
2567
+ isError,
2568
+ timestamp: Date.now(),
2569
+ });
2570
+ }
2571
+ else if (settled[kStreamingBlockKind] === "todo") {
2572
+ // Only the server's success snapshot is authoritative. A completion
2573
+ // frame whose optional `toolCall` is absent still settles: the block
2574
+ // is already marked resolved, so nothing downstream pairs it.
2575
+ const snapshot = toolCall ? extractTodoSnapshot(toolCall) : null;
2576
+ const error = toolCall ? extractTodoError(toolCall) : null;
2577
+ if (snapshot) {
2578
+ settled.arguments = { todos: snapshot.todos, merged: snapshot.merged };
2579
+ }
2580
+ void state.onToolResult?.(buildTodoToolResult(settled.id, snapshot, error));
2581
+ }
2582
+ const idx = output.content.indexOf(settled);
2583
+ clearStreamingPartialJson(settled);
2584
+ stream.push({ type: "toolcall_end", contentIndex: idx, toolCall: settled, partial: output });
2585
+ releaseStreamedCall(state, settled);
2586
+ }
2587
+ }
2588
+ else if (updateCase === "turnEnded") {
2589
+ output.stopReason = "stop";
2590
+ }
2591
+ else if (updateCase === "tokenDelta") {
2592
+ const tokenDelta = update.message.value;
2593
+ usageState.sawTokenDelta = true;
2594
+ output.usage.output += tokenDelta.tokens || 0;
2595
+ output.usage.totalTokens = output.usage.input + output.usage.output;
2596
+ }
2597
+ }
2598
+ function createBlobId(data) {
2599
+ return new Uint8Array(createHash("sha256").update(data).digest());
2600
+ }
2601
+ function storeCursorBlob(blobStore, data) {
2602
+ const blobId = createBlobId(data);
2603
+ blobStore.set(Buffer.from(blobId).toString("hex"), data);
2604
+ return blobId;
2605
+ }
2606
+ function readCursorBlob(blobStore, blobId) {
2607
+ const data = blobStore.get(Buffer.from(blobId).toString("hex"));
2608
+ if (!data) {
2609
+ throw new Error("Cursor blob not found");
2610
+ }
2611
+ return data;
2612
+ }
2613
+ /**
2614
+ * Local tools Cursor already drives natively over the exec channel, so
2615
+ * advertising them again as MCP tools would give the model two ways to call
2616
+ * the same thing.
2617
+ */
2618
+ const CURSOR_NATIVE_TOOL_NAMES = new Set(["bash", "read", "write", "delete", "ls", "grep", "todo"]);
2619
+ /** Strip TypeBox symbol metadata down to a plain JSON Schema document. */
2620
+ function toolParametersToJsonSchema(tool) {
2621
+ try {
2622
+ return JSON.parse(JSON.stringify(tool.parameters));
2623
+ }
2624
+ catch {
2625
+ return { type: "object", properties: {}, required: [] };
2626
+ }
2627
+ }
2628
+ export function buildMcpToolDefinitions(tools) {
2629
+ if (!tools || tools.length === 0) {
2630
+ return [];
2631
+ }
2632
+ const advertisedTools = tools.filter((tool) => !CURSOR_NATIVE_TOOL_NAMES.has(tool.name));
2633
+ if (advertisedTools.length === 0) {
2634
+ return [];
2635
+ }
2636
+ return advertisedTools.map((tool) => {
2637
+ const jsonSchema = toolParametersToJsonSchema(tool);
2638
+ const schemaValue = jsonSchema && typeof jsonSchema === "object"
2639
+ ? jsonSchema
2640
+ : { type: "object", properties: {}, required: [] };
2641
+ const inputSchema = toBinary(ValueSchema, fromJson(ValueSchema, schemaValue));
2642
+ return create(McpToolDefinitionSchema, {
2643
+ name: tool.name,
2644
+ description: tool.description || "",
2645
+ providerIdentifier: "pi-agent",
2646
+ toolName: tool.name,
2647
+ inputSchema,
2648
+ });
2649
+ });
2650
+ }
2651
+ /** Extract text content from a user message. */
2652
+ function extractUserMessageText(msg) {
2653
+ if (msg.role !== "user")
2654
+ return "";
2655
+ const content = msg.content;
2656
+ if (typeof content === "string")
2657
+ return content.trim();
2658
+ const text = content
2659
+ .filter((c) => c.type === "text")
2660
+ .map((c) => c.text)
2661
+ .join("\n");
2662
+ return text.trim();
2663
+ }
2664
+ function hasUserMessageImages(msg) {
2665
+ return msg.role === "user" && Array.isArray(msg.content) && msg.content.some((item) => item.type === "image");
2666
+ }
2667
+ function buildCursorRootPromptContent(content) {
2668
+ if (typeof content === "string") {
2669
+ const text = content.trim();
2670
+ return text ? [{ type: "text", text }] : [];
2671
+ }
2672
+ const parts = [];
2673
+ for (const item of content) {
2674
+ if (item.type === "text") {
2675
+ const text = item.text.trim();
2676
+ if (text) {
2677
+ parts.push({ type: "text", text });
2678
+ }
2679
+ }
2680
+ else {
2681
+ parts.push({ type: "image", image: `data:${item.mimeType};base64,${item.data}`, mediaType: item.mimeType });
2682
+ }
2683
+ }
2684
+ return parts;
2685
+ }
2686
+ function cursorUserContentKey(content) {
2687
+ if (typeof content === "string") {
2688
+ return content.trim();
2689
+ }
2690
+ const hash = createHash("sha256");
2691
+ for (const item of content) {
2692
+ hash.update(item.type);
2693
+ if (item.type === "text") {
2694
+ hash.update(item.text);
2695
+ }
2696
+ else {
2697
+ hash.update(item.mimeType);
2698
+ hash.update(item.data);
2699
+ }
2700
+ }
2701
+ return hash.digest("hex");
2702
+ }
2703
+ function buildCursorAssistantContent(msg) {
2704
+ const content = [];
2705
+ for (const item of msg.content) {
2706
+ if (item.type === "text") {
2707
+ if (item.text)
2708
+ content.push({ type: "text", text: item.text });
2709
+ }
2710
+ else if (item.type === "toolCall") {
2711
+ content.push({
2712
+ type: "tool-call",
2713
+ toolCallId: item.id,
2714
+ toolName: item.name,
2715
+ args: item.arguments,
2716
+ });
2717
+ }
2718
+ // Thinking is never replayed: Cursor manages reasoning server-side and
2719
+ // foreign/hidden reasoning must not leak into history as native thinking.
2720
+ }
2721
+ return content;
2722
+ }
2723
+ /**
2724
+ * Index of the last user message in `messages`, or -1 if none. Used to
2725
+ * exclude the current user turn from history builders — it goes in the
2726
+ * `userMessageAction`, not in history structures.
2727
+ */
2728
+ function findLastUserMessageIndex(messages) {
2729
+ for (let i = messages.length - 1; i >= 0; i--) {
2730
+ if (messages[i].role === "user") {
2731
+ return i;
2732
+ }
2733
+ }
2734
+ return -1;
2735
+ }
2736
+ /**
2737
+ * Build one Cursor system-message JSON blob per system prompt. When no system
2738
+ * prompt is provided, returns a single default greeting so we never emit an
2739
+ * empty `rootPromptMessagesJson` head.
2740
+ */
2741
+ export function buildCursorSystemPromptJsons(systemPrompt) {
2742
+ const trimmed = systemPrompt?.trim();
2743
+ if (!trimmed) {
2744
+ return [JSON.stringify({ role: "system", content: "You are a helpful assistant." })];
2745
+ }
2746
+ return [JSON.stringify({ role: "system", content: trimmed })];
2747
+ }
2748
+ /**
2749
+ * Build `ConversationStateStructure.rootPromptMessagesJson` blob IDs for the
2750
+ * system prompt plus prior conversation history, as JSON blobs matching
2751
+ * Cursor's internal Vercel-AI-SDK-shaped message format.
2752
+ *
2753
+ * Cursor's server uses `rootPromptMessagesJson` (not `turns[]`) to build the
2754
+ * actual model prompt; `turns[]` is UI/display metadata. The active user
2755
+ * message is excluded because it is sent in the action.
2756
+ */
2757
+ function buildRootPromptMessagesJson(messages, systemPromptIds, blobStore, activeUserMessageIndex = findLastUserMessageIndex(messages)) {
2758
+ const entries = [...systemPromptIds];
2759
+ const pushJson = (obj) => {
2760
+ const bytes = new TextEncoder().encode(JSON.stringify(obj));
2761
+ entries.push(storeCursorBlob(blobStore, bytes));
2762
+ };
2763
+ for (let i = 0; i < messages.length; i++) {
2764
+ if (i === activeUserMessageIndex)
2765
+ break;
2766
+ const msg = messages[i];
2767
+ if (msg.role === "user") {
2768
+ const content = buildCursorRootPromptContent(msg.content);
2769
+ if (content.length === 0)
2770
+ continue;
2771
+ pushJson({ role: "user", content });
2772
+ }
2773
+ else if (msg.role === "assistant") {
2774
+ const content = buildCursorAssistantContent(msg);
2775
+ if (content.length === 0)
2776
+ continue;
2777
+ pushJson({ role: "assistant", content });
2778
+ }
2779
+ else if (msg.role === "toolResult") {
2780
+ // Emit even when the result text is empty: the assistant `tool-call`
2781
+ // is already in history, so dropping the pair would replay an
2782
+ // orphaned call.
2783
+ pushJson({
2784
+ role: "tool",
2785
+ id: msg.toolCallId,
2786
+ content: [
2787
+ {
2788
+ type: "tool-result",
2789
+ toolName: msg.toolName,
2790
+ toolCallId: msg.toolCallId,
2791
+ result: toolResultToText(msg),
2792
+ ...(msg.isError ? { isError: true } : {}),
2793
+ },
2794
+ ],
2795
+ });
2796
+ }
2797
+ }
2798
+ return entries;
2799
+ }
2800
+ function isPlainRecord(value) {
2801
+ if (value === null || typeof value !== "object" || Array.isArray(value))
2802
+ return false;
2803
+ const prototype = Object.getPrototypeOf(value);
2804
+ return prototype === Object.prototype || prototype === null;
2805
+ }
2806
+ function isJsonValue(value) {
2807
+ if (value === null || typeof value === "string" || typeof value === "boolean")
2808
+ return true;
2809
+ if (typeof value === "number")
2810
+ return Number.isFinite(value);
2811
+ if (Array.isArray(value))
2812
+ return value.every(isJsonValue);
2813
+ if (!isPlainRecord(value))
2814
+ return false;
2815
+ for (const key in value) {
2816
+ if (!isJsonValue(value[key]))
2817
+ return false;
2818
+ }
2819
+ return true;
2820
+ }
2821
+ function encodeCursorMcpArguments(toolCall) {
2822
+ const encoded = {};
2823
+ for (const name in toolCall.arguments) {
2824
+ const value = toolCall.arguments[name];
2825
+ if (value === undefined)
2826
+ continue;
2827
+ if (!isJsonValue(value)) {
2828
+ throw new Error(`Cursor tool argument ${toolCall.name}.${name} is not JSON-serializable`);
2829
+ }
2830
+ encoded[name] = toBinary(ValueSchema, fromJson(ValueSchema, value));
2831
+ }
2832
+ return encoded;
2833
+ }
2834
+ function createCursorMcpResult(result) {
2835
+ if (result.isError) {
2836
+ return create(McpToolResultSchema, {
2837
+ result: {
2838
+ case: "error",
2839
+ value: create(McpToolErrorSchema, { error: toolResultToText(result) }),
2840
+ },
2841
+ });
2842
+ }
2843
+ return create(McpToolResultSchema, {
2844
+ result: {
2845
+ case: "success",
2846
+ value: create(McpSuccessSchema, {
2847
+ content: result.content.map((item) => item.type === "text"
2848
+ ? create(McpToolResultContentItemSchema, {
2849
+ content: { case: "text", value: create(McpTextContentSchema, { text: item.text }) },
2850
+ })
2851
+ : create(McpToolResultContentItemSchema, {
2852
+ content: {
2853
+ case: "image",
2854
+ value: create(McpImageContentSchema, {
2855
+ data: Uint8Array.from(Buffer.from(item.data, "base64")),
2856
+ mimeType: item.mimeType,
2857
+ }),
2858
+ },
2859
+ })),
2860
+ }),
2861
+ },
2862
+ });
2863
+ }
2864
+ function createCursorToolCallStep(toolCall, result) {
2865
+ const mcpCall = create(McpToolCallSchema, {
2866
+ args: create(McpArgsSchema, {
2867
+ name: toolCall.name,
2868
+ args: encodeCursorMcpArguments(toolCall),
2869
+ toolCallId: toolCall.id,
2870
+ providerIdentifier: "pi-agent",
2871
+ toolName: toolCall.name,
2872
+ }),
2873
+ ...(result ? { result: createCursorMcpResult(result) } : {}),
2874
+ });
2875
+ return create(ConversationStepSchema, {
2876
+ message: {
2877
+ case: "toolCall",
2878
+ value: create(ToolCallSchema, {
2879
+ tool: { case: "mcpToolCall", value: mcpCall },
2880
+ toolCallId: toolCall.id,
2881
+ }),
2882
+ },
2883
+ });
2884
+ }
2885
+ /**
2886
+ * Convert context.messages to Cursor's ConversationTurnStructure blob IDs.
2887
+ * Groups messages into turns: each turn is a user message followed by the
2888
+ * assistant's response. Excludes the active user message (which goes in the
2889
+ * action).
2890
+ */
2891
+ function buildConversationTurns(messages, blobStore, activeUserMessageIndex = findLastUserMessageIndex(messages)) {
2892
+ const turns = [];
2893
+ const historyEnd = activeUserMessageIndex >= 0 ? activeUserMessageIndex : messages.length;
2894
+ const toolResults = new Map();
2895
+ const pairedToolCallIds = new Set();
2896
+ for (let index = 0; index < historyEnd; index++) {
2897
+ const message = messages[index];
2898
+ if (message.role === "toolResult") {
2899
+ toolResults.set(message.toolCallId, message);
2900
+ }
2901
+ else if (message.role === "assistant") {
2902
+ for (const item of message.content) {
2903
+ if (item.type === "toolCall")
2904
+ pairedToolCallIds.add(item.id);
2905
+ }
2906
+ }
2907
+ }
2908
+ let i = 0;
2909
+ while (i < messages.length) {
2910
+ const msg = messages[i];
2911
+ if (msg.role !== "user") {
2912
+ i++;
2913
+ continue;
2914
+ }
2915
+ if (i === activeUserMessageIndex)
2916
+ break;
2917
+ const userText = extractUserMessageText(msg);
2918
+ if (userText.length === 0 && !hasUserMessageImages(msg)) {
2919
+ i++;
2920
+ continue;
2921
+ }
2922
+ const userMessage = createCursorUserMessage(msg.content, userText, deterministicUuid(`u:${turns.length}:${cursorUserContentKey(msg.content)}`));
2923
+ const userMessageBlobId = storeCursorBlob(blobStore, toBinary(UserMessageSchema, userMessage));
2924
+ const stepBlobIds = [];
2925
+ i++;
2926
+ while (i < messages.length && messages[i].role !== "user") {
2927
+ const stepMsg = messages[i];
2928
+ if (stepMsg.role === "assistant") {
2929
+ for (const item of stepMsg.content) {
2930
+ let step;
2931
+ if (item.type === "text") {
2932
+ if (!item.text)
2933
+ continue;
2934
+ step = create(ConversationStepSchema, {
2935
+ message: {
2936
+ case: "assistantMessage",
2937
+ value: create(AssistantMessageSchema, { text: item.text }),
2938
+ },
2939
+ });
2940
+ }
2941
+ else if (item.type === "thinking") {
2942
+ // Foreign/hidden reasoning never leaks into Cursor's turn
2943
+ // history as native thinking.
2944
+ continue;
2945
+ }
2946
+ else if (item.type === "toolCall") {
2947
+ step = createCursorToolCallStep(item, toolResults.get(item.id));
2948
+ }
2949
+ else {
2950
+ continue;
2951
+ }
2952
+ stepBlobIds.push(storeCursorBlob(blobStore, toBinary(ConversationStepSchema, step)));
2953
+ }
2954
+ }
2955
+ else if (stepMsg.role === "toolResult" && !pairedToolCallIds.has(stepMsg.toolCallId)) {
2956
+ const text = toolResultToText(stepMsg);
2957
+ if (text) {
2958
+ const prefix = stepMsg.isError ? "[Tool Error]" : "[Tool Result]";
2959
+ const step = create(ConversationStepSchema, {
2960
+ message: {
2961
+ case: "assistantMessage",
2962
+ value: create(AssistantMessageSchema, { text: `${prefix}\n${text}` }),
2963
+ },
2964
+ });
2965
+ stepBlobIds.push(storeCursorBlob(blobStore, toBinary(ConversationStepSchema, step)));
2966
+ }
2967
+ }
2968
+ i++;
2969
+ }
2970
+ const agentTurn = create(AgentConversationTurnStructureSchema, {
2971
+ userMessage: userMessageBlobId,
2972
+ steps: stepBlobIds,
2973
+ });
2974
+ const turn = create(ConversationTurnStructureSchema, {
2975
+ turn: {
2976
+ case: "agentConversationTurn",
2977
+ value: agentTurn,
2978
+ },
2979
+ });
2980
+ turns.push(storeCursorBlob(blobStore, toBinary(ConversationTurnStructureSchema, turn)));
2981
+ }
2982
+ return turns;
2983
+ }
2984
+ /** Exported for tests: decodes Cursor history blobs built from conversation messages. */
2985
+ export function buildCursorHistoryForTest(messages, activeUserMessageIndex = findLastUserMessageIndex(messages)) {
2986
+ const blobStore = new Map();
2987
+ const rootPromptMessagesJson = buildRootPromptMessagesJson(messages, [], blobStore, activeUserMessageIndex).map((blobId) => JSON.parse(new TextDecoder().decode(readCursorBlob(blobStore, blobId))));
2988
+ const turnUserMessagesJson = [];
2989
+ const turnStepMessagesJson = [];
2990
+ for (const turnBlobId of buildConversationTurns(messages, blobStore, activeUserMessageIndex)) {
2991
+ const turn = fromBinary(ConversationTurnStructureSchema, readCursorBlob(blobStore, turnBlobId));
2992
+ if (turn.turn.case !== "agentConversationTurn") {
2993
+ continue;
2994
+ }
2995
+ const userMessage = fromBinary(UserMessageSchema, readCursorBlob(blobStore, turn.turn.value.userMessage));
2996
+ turnUserMessagesJson.push(toJson(UserMessageSchema, userMessage));
2997
+ turnStepMessagesJson.push(turn.turn.value.steps.map((stepBlobId) => {
2998
+ const step = fromBinary(ConversationStepSchema, readCursorBlob(blobStore, stepBlobId));
2999
+ return toJson(ConversationStepSchema, step);
3000
+ }));
3001
+ }
3002
+ return { rootPromptMessagesJson, turnUserMessagesJson, turnStepMessagesJson };
3003
+ }
3004
+ function createCursorUserMessage(content, text, messageId = randomUUID()) {
3005
+ const images = typeof content === "string" ? [] : extractImages(content);
3006
+ return create(UserMessageSchema, {
3007
+ text,
3008
+ messageId,
3009
+ ...(images.length > 0
3010
+ ? {
3011
+ selectedContext: create(SelectedContextSchema, {
3012
+ selectedImages: images,
3013
+ }),
3014
+ }
3015
+ : {}),
3016
+ });
3017
+ }
3018
+ function extractImages(content) {
3019
+ return content
3020
+ .filter((item) => item.type === "image")
3021
+ .map((image) => create(SelectedImageSchema, {
3022
+ uuid: randomUUID(),
3023
+ mimeType: image.mimeType,
3024
+ dataOrBlobId: {
3025
+ case: "data",
3026
+ value: Uint8Array.from(Buffer.from(image.data, "base64")),
3027
+ },
3028
+ }));
3029
+ }
3030
+ async function buildGrpcRequest(model, context, options, state) {
3031
+ const blobStore = state.blobStore;
3032
+ const systemPromptIds = buildCursorSystemPromptJsons(context.systemPrompt).map((json) => storeCursorBlob(blobStore, new TextEncoder().encode(json)));
3033
+ const activeUserMessageIndex = context.messages.length - 1;
3034
+ const activeMessage = context.messages[activeUserMessageIndex];
3035
+ const activeUserMessage = activeMessage?.role === "user" ? activeMessage : undefined;
3036
+ let userContent;
3037
+ let userText = "";
3038
+ let hasUserImages = false;
3039
+ if (activeUserMessage) {
3040
+ userContent = activeUserMessage.content;
3041
+ if (typeof userContent === "string") {
3042
+ userText = userContent.trim();
3043
+ }
3044
+ else {
3045
+ userText = extractText(userContent);
3046
+ hasUserImages = hasImages(userContent);
3047
+ }
3048
+ }
3049
+ const action = create(ConversationActionSchema, {
3050
+ action: userContent && (userText.trim().length > 0 || hasUserImages)
3051
+ ? {
3052
+ case: "userMessageAction",
3053
+ value: create(UserMessageActionSchema, {
3054
+ userMessage: createCursorUserMessage(userContent, userText),
3055
+ }),
3056
+ }
3057
+ : {
3058
+ case: "resumeAction",
3059
+ value: create(ResumeActionSchema, {}),
3060
+ },
3061
+ });
3062
+ // Build conversation turns from prior messages, excluding only the active
3063
+ // user message when the request is sending one. Resume actions must
3064
+ // preserve trailing tool results.
3065
+ const turns = buildConversationTurns(context.messages, blobStore, activeUserMessage ? activeUserMessageIndex : -1);
3066
+ // Cursor's server uses `rootPromptMessagesJson` (not `turns[]`) to build
3067
+ // the actual model prompt; without it multi-turn conversations lose prior
3068
+ // context.
3069
+ const rootPromptMessagesJson = buildRootPromptMessagesJson(context.messages, systemPromptIds, blobStore, activeUserMessage ? activeUserMessageIndex : -1);
3070
+ // Preserve cached non-history state fields (todos, file states, summaries)
3071
+ // when the system prompt is unchanged; otherwise start fresh.
3072
+ const cachedPromptHead = state.conversationState?.rootPromptMessagesJson?.slice(0, systemPromptIds.length) ?? [];
3073
+ const hasMatchingPrompt = cachedPromptHead.length === systemPromptIds.length &&
3074
+ systemPromptIds.every((id, idx) => Buffer.from(cachedPromptHead[idx]).equals(id));
3075
+ const baseState = state.conversationState && hasMatchingPrompt
3076
+ ? state.conversationState
3077
+ : create(ConversationStateStructureSchema, {
3078
+ rootPromptMessagesJson: systemPromptIds,
3079
+ turns: [],
3080
+ todos: [],
3081
+ pendingToolCalls: [],
3082
+ previousWorkspaceUris: [],
3083
+ fileStates: {},
3084
+ fileStatesV2: {},
3085
+ summaryArchives: [],
3086
+ turnTimings: [],
3087
+ subagentStates: {},
3088
+ selfSummaryCount: 0,
3089
+ readPaths: [],
3090
+ });
3091
+ // Always override `rootPromptMessagesJson` and `turns` with content freshly
3092
+ // built from `context.messages`: the server-echoed checkpoint replaces
3093
+ // historical user entries with empty placeholders.
3094
+ const conversationState = create(ConversationStateStructureSchema, {
3095
+ ...baseState,
3096
+ rootPromptMessagesJson,
3097
+ turns,
3098
+ });
3099
+ const wireModelId = model.upstreamModelId ?? model.id;
3100
+ const cursorMaxMode = model.compat?.cursorMaxMode === true;
3101
+ const modelDetails = create(ModelDetailsSchema, {
3102
+ modelId: wireModelId,
3103
+ displayModelId: model.id,
3104
+ displayName: model.name,
3105
+ ...(cursorMaxMode ? { maxMode: true } : undefined),
3106
+ });
3107
+ const requestedModel = create(RequestedModelSchema, {
3108
+ modelId: wireModelId,
3109
+ maxMode: cursorMaxMode,
3110
+ });
3111
+ const runRequest = create(AgentRunRequestSchema, {
3112
+ conversationState,
3113
+ action,
3114
+ modelDetails,
3115
+ requestedModel,
3116
+ conversationId: state.conversationId,
3117
+ });
3118
+ if (options?.customSystemPrompt) {
3119
+ runRequest.customSystemPrompt = options.customSystemPrompt;
3120
+ }
3121
+ await options?.onPayload?.(runRequest, model);
3122
+ const clientMessage = create(AgentClientMessageSchema, {
3123
+ message: { case: "runRequest", value: runRequest },
3124
+ });
3125
+ const requestBytes = toBinary(AgentClientMessageSchema, clientMessage);
3126
+ log("info", "builtRunRequest", {
3127
+ bytes: requestBytes.length,
3128
+ tools: context.tools?.length ?? 0,
3129
+ });
3130
+ return { requestBytes, blobStore, conversationState };
3131
+ }
3132
+ function hasImages(content) {
3133
+ return content.some((item) => item.type === "image");
3134
+ }
3135
+ function extractText(content) {
3136
+ return content
3137
+ .filter((c) => c.type === "text")
3138
+ .map((c) => c.text)
3139
+ .join("\n");
3140
+ }
3141
+ // ---------------------------------------------------------------------------
3142
+ // Model discovery (GetUsableModels)
3143
+ // ---------------------------------------------------------------------------
3144
+ const CURSOR_GET_USABLE_MODELS_PATH = "/agent.v1.AgentService/GetUsableModels";
3145
+ const CURSOR_DEFAULT_CONTEXT_WINDOW = 200_000;
3146
+ const CURSOR_DEFAULT_MAX_TOKENS = 64_000;
3147
+ /**
3148
+ * `GetUsableModels` carries no context-window field, so the 1M ceiling is
3149
+ * recovered from the signals Cursor does send: display-name "1M" labels and
3150
+ * the max-mode flag on Claude/Gemini families.
3151
+ */
3152
+ const CURSOR_1M_CONTEXT_WINDOW = 1_000_000;
3153
+ const CURSOR_1M_NAME_PATTERN = /\b1m\b/i;
3154
+ const CURSOR_MAX_MODE_1M_ID_PATTERN = /claude|gemini/;
3155
+ /**
3156
+ * Model-id families whose native catalogs are multimodal. Cursor-only or
3157
+ * text-only families (`composer-*`, `grok-code-*`) stay outside this pattern.
3158
+ */
3159
+ const CURSOR_MULTIMODAL_ID_PATTERN = /claude|gemini|gpt-|codex/;
3160
+ /**
3161
+ * Fetches Cursor models through `GetUsableModels` (unary protobuf over
3162
+ * HTTP/2; the ALB rejects HTTP/1.1 with 464) and normalizes them.
3163
+ *
3164
+ * Returns `null` on request/decode failures; `[]` only when the endpoint
3165
+ * responds successfully with no usable models.
3166
+ */
3167
+ export async function fetchCursorUsableModels(options) {
3168
+ const timeoutMs = options.timeoutMs ?? 10_000;
3169
+ try {
3170
+ const requestPayload = create(GetUsableModelsRequestSchema, { customModelIds: [] });
3171
+ const body = toBinary(GetUsableModelsRequestSchema, requestPayload);
3172
+ const baseUrl = (options.baseUrl ?? CURSOR_API_URL).replace(/\/+$/, "");
3173
+ const responseBuffer = await new Promise((resolve) => {
3174
+ const client = http2.connect(baseUrl);
3175
+ const timer = setTimeout(() => {
3176
+ client.destroy();
3177
+ resolve(null);
3178
+ }, timeoutMs);
3179
+ const abort = () => {
3180
+ clearTimeout(timer);
3181
+ client.destroy();
3182
+ resolve(null);
3183
+ };
3184
+ options.signal?.addEventListener("abort", abort, { once: true });
3185
+ client.on("error", () => {
3186
+ clearTimeout(timer);
3187
+ resolve(null);
3188
+ });
3189
+ const req = client.request({
3190
+ ":method": "POST",
3191
+ ":path": CURSOR_GET_USABLE_MODELS_PATH,
3192
+ "content-type": "application/proto",
3193
+ te: "trailers",
3194
+ authorization: `Bearer ${options.apiKey}`,
3195
+ "x-ghost-mode": "true",
3196
+ "x-cursor-client-version": CURSOR_CLIENT_VERSION,
3197
+ "x-cursor-client-type": "cli",
3198
+ });
3199
+ const chunks = [];
3200
+ req.on("data", (chunk) => chunks.push(chunk));
3201
+ req.on("end", () => {
3202
+ clearTimeout(timer);
3203
+ client.close();
3204
+ resolve(new Uint8Array(Buffer.concat(chunks)));
3205
+ });
3206
+ req.on("error", () => {
3207
+ clearTimeout(timer);
3208
+ client.close();
3209
+ resolve(null);
3210
+ });
3211
+ req.on("response", (headers) => {
3212
+ const status = Number(headers[":status"] ?? 0);
3213
+ if (status < 200 || status >= 300) {
3214
+ clearTimeout(timer);
3215
+ client.close();
3216
+ resolve(null);
3217
+ }
3218
+ });
3219
+ if (body.length > 0) {
3220
+ req.end(Buffer.from(body));
3221
+ }
3222
+ else {
3223
+ req.end();
3224
+ }
3225
+ });
3226
+ if (!responseBuffer || responseBuffer.length === 0) {
3227
+ return null;
3228
+ }
3229
+ const decoded = decodeGetUsableModelsResponse(responseBuffer);
3230
+ if (!decoded) {
3231
+ return null;
3232
+ }
3233
+ const byId = new Map();
3234
+ for (const model of decoded.models ?? []) {
3235
+ const id = model.modelId?.trim();
3236
+ if (!id)
3237
+ continue;
3238
+ const name = pickModelDisplayName(model, id);
3239
+ const labeled1M = CURSOR_1M_NAME_PATTERN.test(id) ||
3240
+ [model.displayName, model.displayNameShort, model.displayModelId, ...(model.aliases ?? [])].some((candidate) => typeof candidate === "string" && CURSOR_1M_NAME_PATTERN.test(candidate));
3241
+ const maxMode = model.maxMode === true;
3242
+ const contextWindow = labeled1M || (maxMode && CURSOR_MAX_MODE_1M_ID_PATTERN.test(id))
3243
+ ? CURSOR_1M_CONTEXT_WINDOW
3244
+ : CURSOR_DEFAULT_CONTEXT_WINDOW;
3245
+ byId.set(id, {
3246
+ id,
3247
+ name,
3248
+ reasoning: Boolean(model.thinkingDetails),
3249
+ input: CURSOR_MULTIMODAL_ID_PATTERN.test(id.toLowerCase()) ? ["text", "image"] : ["text"],
3250
+ contextWindow,
3251
+ maxTokens: CURSOR_DEFAULT_MAX_TOKENS,
3252
+ cursorMaxMode: maxMode,
3253
+ });
3254
+ }
3255
+ return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
3256
+ }
3257
+ catch {
3258
+ return null;
3259
+ }
3260
+ }
3261
+ function decodeGetUsableModelsResponse(payload) {
3262
+ const framedBody = decodeConnectUnaryBody(payload);
3263
+ if (framedBody) {
3264
+ try {
3265
+ return fromBinary(GetUsableModelsResponseSchema, framedBody);
3266
+ }
3267
+ catch {
3268
+ return null;
3269
+ }
3270
+ }
3271
+ try {
3272
+ return fromBinary(GetUsableModelsResponseSchema, payload);
3273
+ }
3274
+ catch {
3275
+ return null;
3276
+ }
3277
+ }
3278
+ function decodeConnectUnaryBody(payload) {
3279
+ if (payload.length < 5) {
3280
+ return null;
3281
+ }
3282
+ let offset = 0;
3283
+ while (offset + 5 <= payload.length) {
3284
+ const flags = payload[offset];
3285
+ const view = new DataView(payload.buffer, payload.byteOffset + offset, payload.byteLength - offset);
3286
+ const messageLength = view.getUint32(1, false);
3287
+ const frameEnd = offset + 5 + messageLength;
3288
+ if (frameEnd > payload.length) {
3289
+ return null;
3290
+ }
3291
+ const compressionFlagSet = (flags & 0b0000_0001) !== 0;
3292
+ if (compressionFlagSet) {
3293
+ return null;
3294
+ }
3295
+ const endStreamFlagSet = (flags & 0b0000_0010) !== 0;
3296
+ if (!endStreamFlagSet) {
3297
+ return payload.subarray(offset + 5, frameEnd);
3298
+ }
3299
+ offset = frameEnd;
3300
+ }
3301
+ return null;
3302
+ }
3303
+ function pickModelDisplayName(model, fallbackId) {
3304
+ const candidates = [model.displayName, model.displayNameShort, model.displayModelId, ...(model.aliases ?? [])];
3305
+ for (const candidate of candidates) {
3306
+ if (typeof candidate !== "string") {
3307
+ continue;
3308
+ }
3309
+ const trimmed = candidate.trim();
3310
+ if (trimmed) {
3311
+ return trimmed;
3312
+ }
3313
+ }
3314
+ return fallbackId;
3315
+ }
3316
+ //# sourceMappingURL=cursor-agent.js.map