@cursor/july 0.1.95 → 0.1.97

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 (424) hide show
  1. package/AGENTS.md +3 -4
  2. package/README.md +3 -2
  3. package/dist/bin/agent-serve.js +14 -3
  4. package/dist/channels/deployments/types.d.ts +1 -1
  5. package/dist/channels/deployments/types.d.ts.map +1 -1
  6. package/dist/channels/deployments/types.js +1 -0
  7. package/dist/channels/origin/api.d.ts +72 -15
  8. package/dist/channels/origin/api.d.ts.map +1 -1
  9. package/dist/channels/origin/api.js +185 -28
  10. package/dist/channels/origin/binding.d.ts +13 -2
  11. package/dist/channels/origin/binding.d.ts.map +1 -1
  12. package/dist/channels/origin/binding.js +41 -25
  13. package/dist/channels/origin/checks.d.ts +6 -6
  14. package/dist/channels/origin/checks.js +6 -6
  15. package/dist/channels/origin/cursor-account.d.ts +5 -3
  16. package/dist/channels/origin/cursor-account.d.ts.map +1 -1
  17. package/dist/channels/origin/cursor-account.js +5 -3
  18. package/dist/channels/origin/index.d.ts +1 -0
  19. package/dist/channels/origin/index.d.ts.map +1 -1
  20. package/dist/channels/origin/index.js +1 -0
  21. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  22. package/dist/channels/origin/origin-channel.js +3 -0
  23. package/dist/channels/origin/reactions.d.ts +56 -0
  24. package/dist/channels/origin/reactions.d.ts.map +1 -0
  25. package/dist/channels/origin/reactions.js +67 -0
  26. package/dist/channels/origin/types.d.ts +11 -0
  27. package/dist/channels/origin/types.d.ts.map +1 -1
  28. package/dist/channels/slack/api.d.ts +9 -0
  29. package/dist/channels/slack/api.d.ts.map +1 -1
  30. package/dist/channels/slack/api.js +36 -5
  31. package/dist/channels/slack/approvals.d.ts +16 -0
  32. package/dist/channels/slack/approvals.d.ts.map +1 -1
  33. package/dist/channels/slack/approvals.js +86 -0
  34. package/dist/channels/slack/cli-project.d.ts +19 -0
  35. package/dist/channels/slack/cli-project.d.ts.map +1 -0
  36. package/dist/channels/slack/cli-project.js +234 -0
  37. package/dist/channels/slack/defaults.d.ts.map +1 -1
  38. package/dist/channels/slack/defaults.js +110 -81
  39. package/dist/channels/slack/dispatch.d.ts +7 -1
  40. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  41. package/dist/channels/slack/dispatch.js +47 -23
  42. package/dist/channels/slack/inbound.d.ts +8 -0
  43. package/dist/channels/slack/inbound.d.ts.map +1 -1
  44. package/dist/channels/slack/inbound.js +32 -0
  45. package/dist/channels/slack/init.d.ts +3 -2
  46. package/dist/channels/slack/init.d.ts.map +1 -1
  47. package/dist/channels/slack/init.js +5 -5
  48. package/dist/channels/slack/interactive.d.ts +2 -1
  49. package/dist/channels/slack/interactive.d.ts.map +1 -1
  50. package/dist/channels/slack/interactive.js +15 -4
  51. package/dist/channels/slack/live-delivery.d.ts +26 -29
  52. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  53. package/dist/channels/slack/live-delivery.js +172 -73
  54. package/dist/channels/slack/manifest.d.ts +1 -1
  55. package/dist/channels/slack/manifest.d.ts.map +1 -1
  56. package/dist/channels/slack/manifest.js +6 -2
  57. package/dist/channels/slack/nudge-carrier.d.ts +2 -0
  58. package/dist/channels/slack/nudge-carrier.d.ts.map +1 -1
  59. package/dist/channels/slack/nudge-carrier.js +48 -1
  60. package/dist/channels/slack/setup.d.ts +1 -1
  61. package/dist/channels/slack/setup.d.ts.map +1 -1
  62. package/dist/channels/slack/setup.js +10 -6
  63. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  64. package/dist/channels/slack/slack-channel.js +3 -2
  65. package/dist/channels/slack/tool-approval-events.d.ts.map +1 -1
  66. package/dist/channels/slack/tool-approval-events.js +91 -1
  67. package/dist/docs/404.html +2 -2
  68. package/dist/docs/ab.html +4 -4
  69. package/dist/docs/assets/{app.BBj0klBO.js → app.B3rWNYE1.js} +1 -1
  70. package/dist/docs/assets/chunks/@localSearchIndexroot.BU9nHdnQ.js +1 -0
  71. package/dist/docs/assets/chunks/{VPLocalSearchBox.CUEXpL78.js → VPLocalSearchBox.C1lhqJJR.js} +1 -1
  72. package/dist/docs/assets/chunks/{theme.DabmQnia.js → theme.B40_SXuv.js} +2 -2
  73. package/dist/docs/assets/{deployment.md.D9msOFOW.js → deployment.md.MFPKqsqI.js} +2 -2
  74. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.js → guides_mcp-oauth.md.CN-6YmTJ.js} +4 -4
  75. package/dist/docs/assets/{guides_mcp-oauth.md.DPYmBCbV.lean.js → guides_mcp-oauth.md.CN-6YmTJ.lean.js} +1 -1
  76. package/dist/docs/assets/guides_slack.md.VDXQV3ja.js +22 -0
  77. package/dist/docs/assets/guides_slack.md.VDXQV3ja.lean.js +1 -0
  78. package/dist/docs/assets/index.md.CVeRUOeZ.js +5 -0
  79. package/dist/docs/assets/{index.md.BoHaMdeZ.lean.js → index.md.CVeRUOeZ.lean.js} +1 -1
  80. package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.js → reference_agent-config.md.BHeYMgme.js} +1 -1
  81. package/dist/docs/assets/{reference_cli.md.BsMOxDvh.js → reference_cli.md.RyZf5OTE.js} +10 -6
  82. package/dist/docs/assets/{reference_cli.md.BsMOxDvh.lean.js → reference_cli.md.RyZf5OTE.lean.js} +1 -1
  83. package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.js → reference_connections.md.DxldvyIB.js} +4 -3
  84. package/dist/docs/assets/{reference_http-api.md.D89k1mdm.js → reference_http-api.md.5zOAbV86.js} +1 -1
  85. package/dist/docs/assets/reference_sessions.md.NxFP_CAC.js +1 -0
  86. package/dist/docs/assets/{reference_tools.md.BHeXn2id.js → reference_tools.md.B84gw9Ii.js} +1 -1
  87. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.js +2 -0
  88. package/dist/docs/assets/templates_agents-md.md.CXSMZljE.lean.js +1 -0
  89. package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.js +2 -0
  90. package/dist/docs/assets/templates_code-wiki.md.CDYZIBFN.lean.js +1 -0
  91. package/dist/docs/assets/troubleshooting.md.DCiPBhYs.js +1 -0
  92. package/dist/docs/building-with-agents.html +4 -4
  93. package/dist/docs/concepts.html +4 -4
  94. package/dist/docs/deployment.html +7 -7
  95. package/dist/docs/deployment.md +5 -4
  96. package/dist/docs/evals.html +4 -4
  97. package/dist/docs/guides/agent-to-agent.html +4 -4
  98. package/dist/docs/guides/cloud-runtime.html +4 -4
  99. package/dist/docs/guides/convert-automation.html +4 -4
  100. package/dist/docs/guides/github.html +4 -4
  101. package/dist/docs/guides/human-in-the-loop.html +4 -4
  102. package/dist/docs/guides/mcp-oauth.html +8 -8
  103. package/dist/docs/guides/mcp-oauth.md +26 -8
  104. package/dist/docs/guides/opentelemetry.html +4 -4
  105. package/dist/docs/guides/slack.html +17 -33
  106. package/dist/docs/guides/slack.md +92 -201
  107. package/dist/docs/guides/webhooks.html +4 -4
  108. package/dist/docs/hashmap.json +1 -1
  109. package/dist/docs/hillclimbing.html +4 -4
  110. package/dist/docs/index.html +6 -6
  111. package/dist/docs/index.md +7 -3
  112. package/dist/docs/llms-full.txt +398 -653
  113. package/dist/docs/llms.txt +3 -2
  114. package/dist/docs/quickstart.html +4 -4
  115. package/dist/docs/reference/agent-config.html +6 -6
  116. package/dist/docs/reference/agent-config.md +2 -2
  117. package/dist/docs/reference/artifacts.html +4 -4
  118. package/dist/docs/reference/channels.html +4 -4
  119. package/dist/docs/reference/cli.html +14 -10
  120. package/dist/docs/reference/cli.md +39 -16
  121. package/dist/docs/reference/connections.html +9 -8
  122. package/dist/docs/reference/connections.md +6 -3
  123. package/dist/docs/reference/hooks.html +4 -4
  124. package/dist/docs/reference/http-api.html +6 -6
  125. package/dist/docs/reference/http-api.md +2 -1
  126. package/dist/docs/reference/instructions.html +4 -4
  127. package/dist/docs/reference/playground.html +4 -4
  128. package/dist/docs/reference/project-layout.html +4 -4
  129. package/dist/docs/reference/prompt.html +4 -4
  130. package/dist/docs/reference/schedules.html +4 -4
  131. package/dist/docs/reference/sessions.html +5 -5
  132. package/dist/docs/reference/sessions.md +4 -2
  133. package/dist/docs/reference/skills.html +4 -4
  134. package/dist/docs/reference/subagents.html +4 -4
  135. package/dist/docs/reference/tools.html +6 -6
  136. package/dist/docs/reference/tools.md +9 -2
  137. package/dist/docs/scaffolding-agents.html +4 -4
  138. package/dist/docs/storage.html +4 -4
  139. package/dist/docs/templates/agentic-owners.html +4 -4
  140. package/dist/docs/templates/agents-md.html +28 -0
  141. package/dist/docs/templates/agents-md.md +105 -0
  142. package/dist/docs/templates/code-wiki.html +28 -0
  143. package/dist/docs/templates/code-wiki.md +91 -0
  144. package/dist/docs/templates/demo.html +5 -5
  145. package/dist/docs/templates/pr-autofixer.html +4 -4
  146. package/dist/docs/templates/security-help.html +4 -4
  147. package/dist/docs/templates/security-reviewer.html +5 -5
  148. package/dist/docs/templates/triage.html +4 -4
  149. package/dist/docs/troubleshooting.html +5 -5
  150. package/dist/docs/troubleshooting.md +2 -2
  151. package/dist/evals/run-facts.d.ts.map +1 -1
  152. package/dist/evals/run-facts.js +27 -0
  153. package/dist/files-backends/cursor-hosted.d.ts +26 -4
  154. package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
  155. package/dist/files-backends/cursor-hosted.js +32 -4
  156. package/dist/internal/advertise-tools.d.ts +2 -0
  157. package/dist/internal/advertise-tools.d.ts.map +1 -1
  158. package/dist/internal/advertise-tools.js +30 -10
  159. package/dist/internal/approval-gate.d.ts +12 -13
  160. package/dist/internal/approval-gate.d.ts.map +1 -1
  161. package/dist/internal/approval-gate.js +16 -21
  162. package/dist/internal/bounded.d.ts +7 -0
  163. package/dist/internal/bounded.d.ts.map +1 -0
  164. package/dist/internal/bounded.js +32 -0
  165. package/dist/internal/chat-client.js +10 -0
  166. package/dist/internal/cli-deploy.d.ts +5 -0
  167. package/dist/internal/cli-deploy.d.ts.map +1 -1
  168. package/dist/internal/cli-deploy.js +57 -18
  169. package/dist/internal/cli-mcp-oauth.js +8 -18
  170. package/dist/internal/cli-slack-manual.d.ts +24 -2
  171. package/dist/internal/cli-slack-manual.d.ts.map +1 -1
  172. package/dist/internal/cli-slack-manual.js +251 -20
  173. package/dist/internal/cli-slack.d.ts +9 -2
  174. package/dist/internal/cli-slack.d.ts.map +1 -1
  175. package/dist/internal/cli-slack.js +29 -82
  176. package/dist/internal/conversation-mirror.d.ts +11 -8
  177. package/dist/internal/conversation-mirror.d.ts.map +1 -1
  178. package/dist/internal/conversation-mirror.js +116 -55
  179. package/dist/internal/cursor/account-mcp.d.ts +1 -0
  180. package/dist/internal/cursor/account-mcp.d.ts.map +1 -1
  181. package/dist/internal/cursor/account-mcp.js +17 -0
  182. package/dist/internal/cursor/backend-client.d.ts.map +1 -1
  183. package/dist/internal/cursor/backend-client.js +22 -48
  184. package/dist/internal/cursor/session-token.d.ts +45 -0
  185. package/dist/internal/cursor/session-token.d.ts.map +1 -0
  186. package/dist/internal/cursor/session-token.js +99 -0
  187. package/dist/internal/cursor-agent-template.d.ts +1 -1
  188. package/dist/internal/cursor-agent-template.d.ts.map +1 -1
  189. package/dist/internal/cursor-agent-template.js +2 -0
  190. package/dist/internal/deploy-client.d.ts +17 -0
  191. package/dist/internal/deploy-client.d.ts.map +1 -1
  192. package/dist/internal/deploy-client.js +31 -10
  193. package/dist/internal/env-file.d.ts +19 -0
  194. package/dist/internal/env-file.d.ts.map +1 -0
  195. package/dist/internal/env-file.js +90 -0
  196. package/dist/internal/framework-file-storage.d.ts +61 -0
  197. package/dist/internal/framework-file-storage.d.ts.map +1 -0
  198. package/dist/internal/framework-file-storage.js +210 -0
  199. package/dist/internal/framework-storage-selection.d.ts +11 -5
  200. package/dist/internal/framework-storage-selection.d.ts.map +1 -1
  201. package/dist/internal/framework-storage-selection.js +38 -11
  202. package/dist/internal/hosted-delivery-protocol.d.ts +7 -0
  203. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  204. package/dist/internal/hosted-delivery-protocol.js +7 -0
  205. package/dist/internal/hosted-delivery.d.ts +13 -0
  206. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  207. package/dist/internal/hosted-delivery.js +64 -19
  208. package/dist/internal/hosted-execution-diag.d.ts +48 -0
  209. package/dist/internal/hosted-execution-diag.d.ts.map +1 -0
  210. package/dist/internal/hosted-execution-diag.js +150 -0
  211. package/dist/internal/http-channel.d.ts.map +1 -1
  212. package/dist/internal/http-channel.js +81 -1
  213. package/dist/internal/init-project.d.ts.map +1 -1
  214. package/dist/internal/init-project.js +4 -0
  215. package/dist/internal/install-cursor-skills.d.ts.map +1 -1
  216. package/dist/internal/install-cursor-skills.js +1 -3
  217. package/dist/internal/logs-client.d.ts +5 -0
  218. package/dist/internal/logs-client.d.ts.map +1 -1
  219. package/dist/internal/logs-client.js +5 -0
  220. package/dist/internal/managed-delivery.d.ts +80 -0
  221. package/dist/internal/managed-delivery.d.ts.map +1 -0
  222. package/dist/internal/managed-delivery.js +86 -0
  223. package/dist/internal/mcp-endpoint.js +2 -3
  224. package/dist/internal/mcp-host.d.ts +10 -0
  225. package/dist/internal/mcp-host.d.ts.map +1 -1
  226. package/dist/internal/mcp-host.js +57 -8
  227. package/dist/internal/mcp-oauth-on-demand.d.ts +68 -0
  228. package/dist/internal/mcp-oauth-on-demand.d.ts.map +1 -0
  229. package/dist/internal/mcp-oauth-on-demand.js +287 -0
  230. package/dist/internal/mcp-oauth-persist.d.ts +42 -0
  231. package/dist/internal/mcp-oauth-persist.d.ts.map +1 -0
  232. package/dist/internal/mcp-oauth-persist.js +92 -0
  233. package/dist/internal/mcp-oauth.d.ts +1 -0
  234. package/dist/internal/mcp-oauth.d.ts.map +1 -1
  235. package/dist/internal/mcp-oauth.js +10 -2
  236. package/dist/internal/otel/collector.d.ts.map +1 -1
  237. package/dist/internal/otel/collector.js +22 -9
  238. package/dist/internal/runtime-dispatch-runner.d.ts +5 -1
  239. package/dist/internal/runtime-dispatch-runner.d.ts.map +1 -1
  240. package/dist/internal/runtime-dispatch-runner.js +7 -0
  241. package/dist/internal/sdk-runner.d.ts +11 -0
  242. package/dist/internal/sdk-runner.d.ts.map +1 -1
  243. package/dist/internal/sdk-runner.js +39 -8
  244. package/dist/internal/server.d.ts +3 -0
  245. package/dist/internal/server.d.ts.map +1 -1
  246. package/dist/internal/server.js +49 -21
  247. package/dist/internal/session-engine.d.ts +101 -3
  248. package/dist/internal/session-engine.d.ts.map +1 -1
  249. package/dist/internal/session-engine.js +577 -46
  250. package/dist/internal/sessions-client.d.ts +9 -7
  251. package/dist/internal/sessions-client.d.ts.map +1 -1
  252. package/dist/internal/sessions-client.js +3 -1
  253. package/dist/internal/steering.d.ts +15 -0
  254. package/dist/internal/steering.d.ts.map +1 -0
  255. package/dist/internal/steering.js +18 -0
  256. package/dist/internal/storage-coordinator.d.ts +7 -0
  257. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  258. package/dist/internal/storage-coordinator.js +28 -5
  259. package/dist/internal/storage-roles.d.ts +5 -0
  260. package/dist/internal/storage-roles.d.ts.map +1 -1
  261. package/dist/internal/stream-progress.d.ts.map +1 -1
  262. package/dist/internal/stream-progress.js +10 -0
  263. package/dist/internal/tool-policy.d.ts +9 -1
  264. package/dist/internal/tool-policy.d.ts.map +1 -1
  265. package/dist/internal/tool-policy.js +13 -0
  266. package/dist/internal/trajectory.d.ts.map +1 -1
  267. package/dist/internal/trajectory.js +10 -0
  268. package/dist/internal/turn-outbox.d.ts +24 -0
  269. package/dist/internal/turn-outbox.d.ts.map +1 -0
  270. package/dist/internal/turn-outbox.js +80 -0
  271. package/dist/playground/assets/{index-CZA1uEWA.js → index-DrkI6y5O.js} +51 -51
  272. package/dist/playground/assets/index-DzNGwm7q.css +1 -0
  273. package/dist/playground/index.html +2 -2
  274. package/dist/storage-backends/cursor-hosted.d.ts +9 -6
  275. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  276. package/dist/storage-backends/cursor-hosted.js +16 -3
  277. package/dist/storage.d.ts +8 -0
  278. package/dist/storage.d.ts.map +1 -1
  279. package/dist/types.d.ts +65 -8
  280. package/dist/types.d.ts.map +1 -1
  281. package/docs/README.md +7 -3
  282. package/docs/deployment.md +5 -4
  283. package/docs/guides/mcp-oauth.md +26 -8
  284. package/docs/guides/slack.md +93 -202
  285. package/docs/reference/agent-config.md +2 -2
  286. package/docs/reference/cli.md +39 -16
  287. package/docs/reference/connections.md +6 -3
  288. package/docs/reference/http-api.md +2 -1
  289. package/docs/reference/sessions.md +4 -2
  290. package/docs/reference/tools.md +9 -2
  291. package/docs/templates/agents-md.md +110 -0
  292. package/docs/templates/code-wiki.md +96 -0
  293. package/docs/troubleshooting.md +2 -2
  294. package/package.json +2 -2
  295. package/skills/ab/SKILL.md +13 -97
  296. package/skills/create-agent/SKILL.md +124 -223
  297. package/skills/debug/SKILL.md +34 -68
  298. package/skills/evals/SKILL.md +55 -180
  299. package/skills/framework-map/SKILL.md +83 -145
  300. package/skills/github/SKILL.md +53 -111
  301. package/skills/hillclimb/SKILL.md +40 -163
  302. package/skills/mcp-auth/SKILL.md +51 -107
  303. package/skills/otel/SKILL.md +22 -68
  304. package/skills/setup-slack/SKILL.md +91 -212
  305. package/src/bin/agent-serve.ts +13 -1
  306. package/src/channels/deployments/types.ts +1 -0
  307. package/src/channels/origin/api.ts +264 -33
  308. package/src/channels/origin/binding.ts +57 -28
  309. package/src/channels/origin/checks.ts +6 -6
  310. package/src/channels/origin/cursor-account.ts +5 -3
  311. package/src/channels/origin/index.ts +1 -0
  312. package/src/channels/origin/origin-channel.ts +4 -0
  313. package/src/channels/origin/reactions.ts +82 -0
  314. package/src/channels/origin/types.ts +11 -0
  315. package/src/channels/slack/api.ts +41 -5
  316. package/src/channels/slack/approvals.ts +105 -0
  317. package/src/channels/slack/cli-project.ts +267 -0
  318. package/src/channels/slack/defaults.ts +122 -93
  319. package/src/channels/slack/dispatch.ts +56 -22
  320. package/src/channels/slack/inbound.ts +43 -0
  321. package/src/channels/slack/init.ts +10 -5
  322. package/src/channels/slack/interactive.ts +35 -9
  323. package/src/channels/slack/live-delivery.ts +235 -114
  324. package/src/channels/slack/manifest.ts +6 -2
  325. package/src/channels/slack/nudge-carrier.ts +48 -1
  326. package/src/channels/slack/setup.ts +10 -6
  327. package/src/channels/slack/slack-channel.ts +3 -0
  328. package/src/channels/slack/tool-approval-events.ts +93 -0
  329. package/src/evals/run-facts.ts +32 -0
  330. package/src/files-backends/cursor-hosted.ts +40 -7
  331. package/src/internal/advertise-tools.ts +49 -12
  332. package/src/internal/approval-gate.ts +20 -29
  333. package/src/internal/bounded.ts +28 -0
  334. package/src/internal/chat-client.ts +19 -0
  335. package/src/internal/cli-deploy.ts +91 -9
  336. package/src/internal/cli-mcp-oauth.ts +7 -21
  337. package/src/internal/cli-slack-manual.ts +350 -18
  338. package/src/internal/cli-slack.ts +40 -84
  339. package/src/internal/conversation-mirror.ts +145 -69
  340. package/src/internal/cursor/account-mcp.ts +21 -0
  341. package/src/internal/cursor/backend-client.ts +34 -58
  342. package/src/internal/cursor/session-token.ts +115 -0
  343. package/src/internal/cursor-agent-template.ts +2 -0
  344. package/src/internal/deploy-client.ts +43 -0
  345. package/src/internal/env-file.ts +84 -0
  346. package/src/internal/framework-file-storage.ts +228 -0
  347. package/src/internal/framework-storage-selection.ts +51 -11
  348. package/src/internal/hosted-delivery-protocol.ts +7 -0
  349. package/src/internal/hosted-delivery.ts +107 -24
  350. package/src/internal/hosted-execution-diag.ts +207 -0
  351. package/src/internal/http-channel.ts +99 -0
  352. package/src/internal/init-project.ts +4 -0
  353. package/src/internal/install-cursor-skills.ts +1 -4
  354. package/src/internal/logs-client.ts +5 -0
  355. package/src/internal/managed-delivery.ts +125 -0
  356. package/src/internal/mcp-endpoint.ts +13 -4
  357. package/src/internal/mcp-host.ts +77 -11
  358. package/src/internal/mcp-oauth-on-demand.ts +352 -0
  359. package/src/internal/mcp-oauth-persist.ts +127 -0
  360. package/src/internal/mcp-oauth.ts +17 -1
  361. package/src/internal/otel/collector.ts +30 -0
  362. package/src/internal/runtime-dispatch-runner.ts +10 -0
  363. package/src/internal/sdk-runner.ts +56 -8
  364. package/src/internal/server.ts +61 -9
  365. package/src/internal/session-engine.ts +723 -60
  366. package/src/internal/sessions-client.ts +17 -8
  367. package/src/internal/steering.ts +17 -0
  368. package/src/internal/storage-coordinator.ts +38 -8
  369. package/src/internal/storage-roles.ts +7 -0
  370. package/src/internal/stream-progress.ts +19 -0
  371. package/src/internal/tool-policy.ts +18 -0
  372. package/src/internal/trajectory.ts +10 -0
  373. package/src/internal/turn-outbox.ts +85 -0
  374. package/src/storage-backends/cursor-hosted.ts +22 -8
  375. package/src/storage.ts +12 -0
  376. package/src/types.ts +70 -9
  377. package/templates/agents-md/README.md +39 -0
  378. package/templates/agents-md/agent/agent.ts +8 -0
  379. package/templates/agents-md/agent/channels/github.ts +10 -0
  380. package/templates/agents-md/agent/channels/queue.ts +28 -0
  381. package/templates/agents-md/agent/channels/slack.ts +9 -0
  382. package/templates/agents-md/agent/instructions.md +39 -0
  383. package/templates/agents-md/agent/lib/collect.ts +439 -0
  384. package/templates/agents-md/agent/lib/config.ts +8 -0
  385. package/templates/agents-md/agent/lib/paths.ts +51 -0
  386. package/templates/agents-md/agent/schedules/weekly.ts +17 -0
  387. package/templates/agents-md/agent/tools/apply_agents.ts +226 -0
  388. package/templates/agents-md/evals/agents.eval.ts +135 -0
  389. package/templates/agents-md/evals/evals.config.ts +5 -0
  390. package/templates/agents-md/init.json +38 -0
  391. package/templates/agents-md/package.json +18 -0
  392. package/templates/agents-md/tsconfig.json +12 -0
  393. package/templates/code-wiki/README.md +34 -0
  394. package/templates/code-wiki/agent/agent.ts +8 -0
  395. package/templates/code-wiki/agent/channels/github.ts +144 -0
  396. package/templates/code-wiki/agent/instructions.md +39 -0
  397. package/templates/code-wiki/agent/lib/repos.ts +5 -0
  398. package/templates/code-wiki/agent/lib/wiki.ts +22 -0
  399. package/templates/code-wiki/agent/tools/apply_wiki.ts +219 -0
  400. package/templates/code-wiki/evals/evals.config.ts +5 -0
  401. package/templates/code-wiki/evals/wiki.eval.ts +132 -0
  402. package/templates/code-wiki/init.json +25 -0
  403. package/templates/code-wiki/package.json +18 -0
  404. package/templates/code-wiki/tsconfig.json +12 -0
  405. package/dist/docs/assets/chunks/@localSearchIndexroot.oqPawjiD.js +0 -1
  406. package/dist/docs/assets/guides_slack.md.C32HsdKk.js +0 -38
  407. package/dist/docs/assets/guides_slack.md.C32HsdKk.lean.js +0 -1
  408. package/dist/docs/assets/index.md.BoHaMdeZ.js +0 -5
  409. package/dist/docs/assets/reference_sessions.md.D0mIh4KK.js +0 -1
  410. package/dist/docs/assets/troubleshooting.md.Ctv3T8C2.js +0 -1
  411. package/dist/docs/design/agsh.md +0 -406
  412. package/dist/internal/deployment-scm-event-stream.d.ts +0 -116
  413. package/dist/internal/deployment-scm-event-stream.d.ts.map +0 -1
  414. package/dist/internal/deployment-scm-event-stream.js +0 -474
  415. package/dist/playground/assets/index-CF7hUDnQ.css +0 -1
  416. package/docs/design/agsh.md +0 -406
  417. package/src/internal/deployment-scm-event-stream.ts +0 -593
  418. /package/dist/docs/assets/{deployment.md.D9msOFOW.lean.js → deployment.md.MFPKqsqI.lean.js} +0 -0
  419. /package/dist/docs/assets/{reference_agent-config.md.Cp_x38Nl.lean.js → reference_agent-config.md.BHeYMgme.lean.js} +0 -0
  420. /package/dist/docs/assets/{reference_connections.md.BH8Oc0D0.lean.js → reference_connections.md.DxldvyIB.lean.js} +0 -0
  421. /package/dist/docs/assets/{reference_http-api.md.D89k1mdm.lean.js → reference_http-api.md.5zOAbV86.lean.js} +0 -0
  422. /package/dist/docs/assets/{reference_sessions.md.D0mIh4KK.lean.js → reference_sessions.md.NxFP_CAC.lean.js} +0 -0
  423. /package/dist/docs/assets/{reference_tools.md.BHeXn2id.lean.js → reference_tools.md.B84gw9Ii.lean.js} +0 -0
  424. /package/dist/docs/assets/{troubleshooting.md.Ctv3T8C2.lean.js → troubleshooting.md.DCiPBhYs.lean.js} +0 -0
@@ -12,12 +12,13 @@
12
12
  * This module mirrors the conversation through the SDK's own store APIs —
13
13
  * the agent document (which carries `sdkMetadata.blobEncryptionKey`, without
14
14
  * which the checkpoint blobs cannot be decrypted) plus its content-addressed
15
- * checkpoint blobs — into the deployment's shared file storage. On
16
- * Cursor-managed hosting that is the Agent Store reached over
17
- * `/v0/agent-serve-files` presigned URLs: the store is **not mounted** as a
18
- * filesystem in the engine VM (the FUSE mount at
19
- * {@link DEPLOYMENT_STORE_MOUNT_PATH} exists only on cloud-runtime VMs), so
20
- * the mirror moves bytes through the {@link FileSink} contract instead.
15
+ * checkpoint blobs — into the deployment's **framework-level** file store
16
+ * (`AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID`), alongside session records and
17
+ * transcripts and apart from the author-facing user store. The store is an
18
+ * Agent Store reached over `/v0/agent-serve-files` presigned URLs **not
19
+ * mounted** as a filesystem in the engine VM (the FUSE mount at
20
+ * {@link DEPLOYMENT_STORE_MOUNT_PATH} exists only on cloud-runtime VMs)
21
+ * so the mirror moves bytes through the {@link FileSink} contract instead.
21
22
  *
22
23
  * Going through the store APIs rather than copying `store.db` files sidesteps
23
24
  * WAL consistency and the shared `index.db` (per-session raw copies would
@@ -38,10 +39,11 @@
38
39
  import type { LocalAgentDocument, LocalAgentStore } from "@cursor/sdk";
39
40
  import { agentStoreKeys, type FileSink } from "../files.js";
40
41
  import {
42
+ cursorFrameworkStoreSourceId,
41
43
  cursorHostedFiles,
42
- isCursorHostedFilesAvailable,
43
44
  } from "../files-backends/cursor-hosted.js";
44
45
  import type { AgentProject } from "../types.js";
46
+ import { mapBounded } from "./bounded.js";
45
47
  import { resolveApiKeySync } from "./cursor/credentials.js";
46
48
  import { AGENT_SERVE_GENERATION_ENV_VAR } from "./cursor-relay-core.js";
47
49
  import { describeError } from "./describe-error.js";
@@ -91,21 +93,44 @@ export function createConversationMirror(
91
93
  ): ConversationMirror {
92
94
  const { sink, agentName } = options;
93
95
  const logger = options.logger ?? (() => {});
94
- /** Blob ids known to be in the sink, per SDK agent id (upload dedupe). */
96
+ /**
97
+ * Blob ids known to be in the sink, per SDK agent id (upload dedupe).
98
+ * Bounded oldest-out like the files backend's etag cache: a resident
99
+ * engine touches an unbounded number of conversations over its lifetime,
100
+ * and a miss costs one manifest read, never correctness.
101
+ */
95
102
  const mirrored = new Map<string, Set<string>>();
103
+ const rememberMirrored = (sdkAgentId: string, blobIds: Set<string>): void => {
104
+ if (
105
+ !mirrored.has(sdkAgentId) &&
106
+ mirrored.size >= MAX_TRACKED_CONVERSATIONS
107
+ ) {
108
+ const oldest = mirrored.keys().next();
109
+ if (!oldest.done) {
110
+ mirrored.delete(oldest.value);
111
+ }
112
+ }
113
+ mirrored.set(sdkAgentId, blobIds);
114
+ };
96
115
  /**
97
116
  * Per-conversation serialization so a persist cannot interleave with a
98
- * hydrate (or another persist) for the same conversation.
117
+ * hydrate (or another persist) for the same conversation. Entries are
118
+ * removed once their chain settles as the tail, so the map tracks only
119
+ * conversations with work in flight.
99
120
  */
100
121
  const chains = new Map<string, Promise<unknown>>();
101
122
 
102
123
  function serialize<T>(sdkAgentId: string, run: () => Promise<T>): Promise<T> {
103
124
  const previous = chains.get(sdkAgentId) ?? Promise.resolve();
104
125
  const next = previous.then(run, run);
105
- chains.set(
106
- sdkAgentId,
107
- next.catch(() => {})
108
- );
126
+ const stored = next
127
+ .catch(() => {})
128
+ .then(() => {
129
+ if (chains.get(sdkAgentId) === stored) {
130
+ chains.delete(sdkAgentId);
131
+ }
132
+ });
133
+ chains.set(sdkAgentId, stored);
109
134
  return next;
110
135
  }
111
136
 
@@ -115,20 +140,22 @@ export function createConversationMirror(
115
140
  return cached;
116
141
  }
117
142
  // Seed from the previous manifest so a fresh pod does not re-upload the
118
- // whole history on its first persist. A miss (or parse failure) seeds
119
- // empty — re-uploading is wasteful, never wrong.
143
+ // whole history on its first persist. A miss, an unusable manifest, or
144
+ // a failed read seeds empty — re-uploading is wasteful, never wrong.
120
145
  const seeded = new Set<string>();
121
146
  try {
122
- const manifest = await readManifest(sink, agentName, sdkAgentId);
123
- for (const blobId of manifest?.blobIds ?? []) {
124
- seeded.add(blobId);
147
+ const read = await readManifest(sink, agentName, sdkAgentId);
148
+ if (read.outcome === "found") {
149
+ for (const blobId of read.manifest.blobIds) {
150
+ seeded.add(blobId);
151
+ }
125
152
  }
126
153
  } catch (error) {
127
154
  logger(
128
155
  `[agent-sdk] conversation mirror: could not read the prior manifest for ${sdkAgentId} (${describeError(error)}); re-uploading blobs`
129
156
  );
130
157
  }
131
- mirrored.set(sdkAgentId, seeded);
158
+ rememberMirrored(sdkAgentId, seeded);
132
159
  return seeded;
133
160
  }
134
161
 
@@ -182,57 +209,77 @@ export function createConversationMirror(
182
209
  if (existing !== null) {
183
210
  return false;
184
211
  }
185
- const manifest = await readManifest(sink, agentName, sdkAgentId);
186
- if (manifest === undefined) {
212
+ const read = await readManifest(sink, agentName, sdkAgentId);
213
+ if (read.outcome === "miss") {
187
214
  return false;
188
215
  }
216
+ if (read.outcome === "unusable") {
217
+ // Definitive, non-transient state (a future manifest version
218
+ // during a rolling redeploy, or corruption): retrying would fail
219
+ // identically forever, so fall through to the caller's
220
+ // fresh-conversation path instead of wedging the session.
221
+ logger(
222
+ `[agent-sdk] conversation mirror: the manifest for ${sdkAgentId} is unusable (${read.reason}); treating the conversation as unrestorable`
223
+ );
224
+ return false;
225
+ }
226
+ const manifest = read.manifest;
189
227
  const rootBlobId = manifest.agent.latestCheckpoint?.rootBlobId;
190
- const restored = new Set<string>();
191
- for (const blobId of manifest.blobIds) {
192
- const body = await sink.get(
193
- agentStoreKeys.conversationBlob(agentName, sdkAgentId, blobId)
228
+ if (
229
+ rootBlobId === undefined ||
230
+ !manifest.blobIds.includes(rootBlobId)
231
+ ) {
232
+ logger(
233
+ `[agent-sdk] conversation mirror: the manifest for ${sdkAgentId} does not list its root checkpoint blob; treating the conversation as unrestorable`
194
234
  );
195
- const bytes =
196
- body === undefined
197
- ? undefined
198
- : body instanceof Uint8Array
199
- ? body
200
- : body.body;
201
- if (bytes === undefined) {
202
- // Definitive absence (never uploaded, or deleted). A missing
203
- // root blob makes the conversation unrestorable — fall through
204
- // to the caller's fresh-conversation path rather than creating
205
- // an agent row whose checkpoint cannot load.
206
- if (blobId === rootBlobId) {
207
- logger(
208
- `[agent-sdk] conversation mirror: the root checkpoint blob for ${sdkAgentId} is missing from the sink; treating the conversation as unrestorable`
209
- );
210
- return false;
211
- }
212
- continue;
213
- }
235
+ return false;
236
+ }
237
+ // Bounded-parallel downloads: this sits on the resume critical path
238
+ // (a presign plus an object GET per blob against the hosted sink).
239
+ const bodies = await mapBounded(
240
+ manifest.blobIds,
241
+ HYDRATE_DOWNLOAD_CONCURRENCY,
242
+ async (blobId) => ({
243
+ blobId,
244
+ body: await sink.get(
245
+ agentStoreKeys.conversationBlob(agentName, sdkAgentId, blobId)
246
+ ),
247
+ })
248
+ );
249
+ const missing = bodies.filter((entry) => entry.body === undefined);
250
+ if (missing.length > 0) {
251
+ // The manifest lists exactly what a previous persist uploaded, so
252
+ // any definitive absence is store loss — and the checkpoint graph
253
+ // may reach any listed blob, not just the root. Restoring a
254
+ // partial graph would create an agent row whose resume succeeds
255
+ // and whose checkpoint walk then fails unrecoverably.
256
+ logger(
257
+ `[agent-sdk] conversation mirror: ${missing.length} checkpoint blob(s) for ${sdkAgentId} are missing from the sink; treating the conversation as unrestorable`
258
+ );
259
+ return false;
260
+ }
261
+ const restored = new Set<string>();
262
+ for (const entry of bodies) {
263
+ const body = entry.body as NonNullable<typeof entry.body>;
264
+ const bytes = body instanceof Uint8Array ? body : body.body;
214
265
  if (
215
- (await store.checkpoints.get({ agentId: sdkAgentId, blobId })) ===
216
- null
266
+ (await store.checkpoints.get({
267
+ agentId: sdkAgentId,
268
+ blobId: entry.blobId,
269
+ })) === null
217
270
  ) {
218
271
  await store.checkpoints.create({
219
272
  agentId: sdkAgentId,
220
- blobId,
273
+ blobId: entry.blobId,
221
274
  data: bytes,
222
275
  });
223
276
  }
224
- restored.add(blobId);
225
- }
226
- if (rootBlobId !== undefined && !restored.has(rootBlobId)) {
227
- logger(
228
- `[agent-sdk] conversation mirror: the manifest for ${sdkAgentId} does not list its root checkpoint blob; treating the conversation as unrestorable`
229
- );
230
- return false;
277
+ restored.add(entry.blobId);
231
278
  }
232
279
  await store.agents.create({
233
280
  agent: { ...manifest.agent, activeRunId: null },
234
281
  });
235
- mirrored.set(sdkAgentId, restored);
282
+ rememberMirrored(sdkAgentId, restored);
236
283
  logger(
237
284
  `[agent-sdk] conversation mirror: restored the conversation for ${sdkAgentId} (${restored.size} checkpoint blob(s))`
238
285
  );
@@ -242,27 +289,52 @@ export function createConversationMirror(
242
289
  };
243
290
  }
244
291
 
292
+ /** Oldest-out cap on the per-conversation upload-dedupe cache. */
293
+ const MAX_TRACKED_CONVERSATIONS = 1_000;
294
+
295
+ /** Parallel blob downloads during hydrate (resume critical path). */
296
+ const HYDRATE_DOWNLOAD_CONCURRENCY = 8;
297
+
298
+ type ManifestRead =
299
+ /** No manifest was ever persisted (definitive miss). */
300
+ | { outcome: "miss" }
301
+ /**
302
+ * A manifest exists but cannot be used (future version, corruption,
303
+ * wrong agent). Definitive and non-transient — callers must not retry,
304
+ * and must not treat it as a sink outage.
305
+ */
306
+ | { outcome: "unusable"; reason: string }
307
+ | { outcome: "found"; manifest: ConversationManifestV1 };
308
+
309
+ /**
310
+ * Read the persisted manifest. Sink transport failures propagate (an
311
+ * outage must fail the follow-up, not fork the thread); everything the
312
+ * bytes themselves can be wrong about is reported as `unusable`.
313
+ */
245
314
  async function readManifest(
246
315
  sink: FileSink,
247
316
  agentName: string,
248
317
  sdkAgentId: string
249
- ): Promise<ConversationManifestV1 | undefined> {
318
+ ): Promise<ManifestRead> {
250
319
  const body = await sink.get(
251
320
  agentStoreKeys.conversationManifest(agentName, sdkAgentId)
252
321
  );
253
322
  if (body === undefined) {
254
- return undefined;
323
+ return { outcome: "miss" };
255
324
  }
256
325
  const bytes = body instanceof Uint8Array ? body : body.body;
257
- const parsed: unknown = JSON.parse(new TextDecoder().decode(bytes));
326
+ let parsed: unknown;
327
+ try {
328
+ parsed = JSON.parse(new TextDecoder().decode(bytes));
329
+ } catch {
330
+ return { outcome: "unusable", reason: "not JSON" };
331
+ }
258
332
  if (
259
333
  parsed === null ||
260
334
  typeof parsed !== "object" ||
261
335
  (parsed as { v?: unknown }).v !== 1
262
336
  ) {
263
- throw new Error(
264
- `conversation mirror: unsupported manifest for ${sdkAgentId}`
265
- );
337
+ return { outcome: "unusable", reason: "unsupported manifest version" };
266
338
  }
267
339
  const manifest = parsed as ConversationManifestV1;
268
340
  if (
@@ -271,11 +343,9 @@ async function readManifest(
271
343
  manifest.agent.agentId !== sdkAgentId ||
272
344
  !Array.isArray(manifest.blobIds)
273
345
  ) {
274
- throw new Error(
275
- `conversation mirror: malformed manifest for ${sdkAgentId}`
276
- );
346
+ return { outcome: "unusable", reason: "malformed manifest" };
277
347
  }
278
- return manifest;
348
+ return { outcome: "found", manifest };
279
349
  }
280
350
 
281
351
  async function listCheckpointBlobIds(
@@ -299,8 +369,10 @@ async function listCheckpointBlobIds(
299
369
  *
300
370
  * Selected for architecture-v2 agents on Cursor-managed hosting — the same
301
371
  * generation signal as framework-storage-selection.ts, plus a provisioned
302
- * deployment file store. Self-hosted v2 keeps everything on the state root
303
- * (the operator owns the disk), and v1 keeps today's behavior while it runs.
372
+ * **framework-level** file store: conversation state is engine-owned, so it
373
+ * lives with session records and transcripts, never on the author-facing
374
+ * user store. Self-hosted v2 keeps everything on the state root (the
375
+ * operator owns the disk), and v1 keeps today's behavior while it runs.
304
376
  */
305
377
  export function selectConversationMirror(input: {
306
378
  project: AgentProject;
@@ -311,18 +383,22 @@ export function selectConversationMirror(input: {
311
383
  return undefined;
312
384
  }
313
385
  const env = input.env ?? process.env;
386
+ const frameworkStoreId = cursorFrameworkStoreSourceId(
387
+ env as NodeJS.ProcessEnv
388
+ );
314
389
  if (
315
390
  env[AGENT_SERVE_GENERATION_ENV_VAR] === undefined ||
316
- !isCursorHostedFilesAvailable(env as NodeJS.ProcessEnv)
391
+ frameworkStoreId === undefined
317
392
  ) {
318
393
  return undefined;
319
394
  }
320
395
  return createConversationMirror({
321
396
  sink: cursorHostedFiles({
397
+ storeSourceId: frameworkStoreId,
322
398
  // Full chain (env → key file → stored login): frozen engines listen
323
399
  // unsigned and bind CURSOR_API_KEY_FILE at task time, after this sink
324
400
  // is constructed.
325
- apiKey: () => resolveApiKeySync()?.apiKey,
401
+ resolveApiKey: () => resolveApiKeySync()?.apiKey,
326
402
  }),
327
403
  agentName: input.project.name,
328
404
  logger: input.logger,
@@ -253,6 +253,27 @@ export class CursorAccountMcpBridge {
253
253
  * Returns a promise that settles when every ref's log lines are written —
254
254
  * callers may ignore it; tests can await it.
255
255
  */
256
+ async probeAuthUrl(
257
+ ref: CursorAccountConnectionRef
258
+ ): Promise<string | undefined> {
259
+ const identifiers =
260
+ ref.servers !== undefined && ref.servers.length > 0
261
+ ? [...ref.servers]
262
+ : (await this.client.getAvailableMcpServers()).map(
263
+ (server) => server.serverIdentifier ?? server.name
264
+ );
265
+ const urls = await probeConnectorAuthUrls({
266
+ client: this.client,
267
+ identifiers,
268
+ });
269
+ for (const url of urls.values()) {
270
+ if (url !== "") {
271
+ return url;
272
+ }
273
+ }
274
+ return undefined;
275
+ }
276
+
256
277
  logPreflight(refs: CursorAccountConnectionRef[]): Promise<void> {
257
278
  return Promise.all(refs.map((ref) => this.runPreflight(ref))).then(
258
279
  () => undefined
@@ -17,14 +17,15 @@ import { randomUUID } from "node:crypto";
17
17
  import { z } from "zod";
18
18
  import type { McpToolAnnotations } from "../../types.js";
19
19
  import { boundedToolAnnotations } from "../mcp-host.js";
20
+ import {
21
+ CURSOR_SESSION_EXCHANGE_PATH,
22
+ CursorSessionExchangeError,
23
+ exchangeCursorApiKeyForSession,
24
+ SESSION_TOKEN_EXPIRY_LEEWAY_MS,
25
+ } from "./session-token.js";
20
26
 
21
27
  const CLIENT_VERSION = "agent-serve-0.1.0";
22
- const EXCHANGE_PATH = "/auth/exchange_user_api_key";
23
28
  const SERVICE_PREFIX = "/aiserver.v1.DashboardService";
24
- /** Re-exchange when the cached token is within this window of expiry. */
25
- const TOKEN_EXPIRY_LEEWAY_MS = 5 * 60 * 1000;
26
- /** Fallback token lifetime when the JWT `exp` claim cannot be parsed. */
27
- const FALLBACK_TOKEN_LIFETIME_MS = 30 * 60 * 1000;
28
29
  const LIST_TIMEOUT_MS = 60_000;
29
30
  /** Backend-executed tools call out to third-party servers; be generous. */
30
31
  const EXECUTE_TIMEOUT_MS = 120_000;
@@ -550,7 +551,7 @@ export class CursorBackendClient {
550
551
  }
551
552
  if (
552
553
  this.token !== undefined &&
553
- Date.now() < this.token.expiresAtMs - TOKEN_EXPIRY_LEEWAY_MS
554
+ Date.now() < this.token.expiresAtMs - SESSION_TOKEN_EXPIRY_LEEWAY_MS
554
555
  ) {
555
556
  return Promise.resolve(this.token.value);
556
557
  }
@@ -565,44 +566,39 @@ export class CursorBackendClient {
565
566
 
566
567
  private async exchangeApiKey(): Promise<string> {
567
568
  const apiKey = this.currentApiKey();
568
- let response: Response;
569
+ let session: { token: string; expiresAtMs: number };
569
570
  try {
570
- response = await this.fetchImpl(`${this.backendUrl}${EXCHANGE_PATH}`, {
571
- method: "POST",
572
- headers: {
573
- "content-type": "application/json",
574
- authorization: `Bearer ${apiKey}`,
575
- },
576
- body: "{}",
577
- signal: AbortSignal.timeout(30_000),
571
+ session = await exchangeCursorApiKeyForSession({
572
+ exchangeUrl: `${this.backendUrl}${CURSOR_SESSION_EXCHANGE_PATH}`,
573
+ apiKey,
574
+ fetchImpl: this.fetchImpl,
578
575
  });
579
576
  } catch (error) {
580
- throw new CursorBackendError(
581
- `Could not reach the Cursor backend at ${this.backendUrl}: ${describeError(error)}`
582
- );
583
- }
584
- const text = await response.text();
585
- if (!response.ok) {
586
- throw new CursorBackendError(
587
- response.status === 401
588
- ? "The Cursor API key was rejected. Run `agent-serve login` again or update CURSOR_API_KEY."
589
- : `Cursor API key exchange failed (${response.status}).`,
590
- response.status
591
- );
592
- }
593
- const body = accessTokenResponseSchema.safeParse(safeJsonParse(text));
594
- if (!body.success || body.data.accessToken === "") {
595
- throw new CursorBackendError(
596
- "Cursor API key exchange returned no access token."
597
- );
577
+ if (error instanceof CursorSessionExchangeError) {
578
+ if (error.status === undefined) {
579
+ throw new CursorBackendError(
580
+ `Could not reach the Cursor backend at ${this.backendUrl}: ${error.message}`
581
+ );
582
+ }
583
+ if (error.status >= 200 && error.status < 300) {
584
+ // 2xx without a token in the body.
585
+ throw new CursorBackendError(
586
+ "Cursor API key exchange returned no access token."
587
+ );
588
+ }
589
+ throw new CursorBackendError(
590
+ error.status === 401
591
+ ? "The Cursor API key was rejected. Run `agent-serve login` again or update CURSOR_API_KEY."
592
+ : `Cursor API key exchange failed (${error.status}).`,
593
+ error.status
594
+ );
595
+ }
596
+ throw error;
598
597
  }
599
- this.token = {
600
- value: body.data.accessToken,
601
- expiresAtMs: jwtExpiryMs(body.data.accessToken),
602
- };
598
+ this.token = { value: session.token, expiresAtMs: session.expiresAtMs };
603
599
  this.lastExchangedApiKey = apiKey;
604
600
  this.logger("[agent-sdk] cursor account: minted a fresh access token");
605
- return body.data.accessToken;
601
+ return session.token;
606
602
  }
607
603
  }
608
604
 
@@ -705,8 +701,6 @@ export async function revokeMintedUserApiKey(args: {
705
701
  // Helpers
706
702
  // ============================================================================
707
703
 
708
- const accessTokenResponseSchema = z.object({ accessToken: z.string() });
709
-
710
704
  function safeJsonParse(text: string): unknown {
711
705
  try {
712
706
  return JSON.parse(text) as unknown;
@@ -883,24 +877,6 @@ function retryAfterMsFromResponse(response: Response): number | undefined {
883
877
  return Math.min(retryAfter * 1000, MAX_GITHUB_TOKEN_RETRY_AFTER_MS);
884
878
  }
885
879
 
886
- function jwtExpiryMs(token: string): number {
887
- try {
888
- const payload = token.split(".")[1];
889
- if (payload === undefined) {
890
- throw new Error("not a JWT");
891
- }
892
- const claims = JSON.parse(
893
- Buffer.from(payload, "base64url").toString("utf8")
894
- ) as { exp?: unknown };
895
- if (typeof claims.exp === "number") {
896
- return claims.exp * 1000;
897
- }
898
- } catch {
899
- // Opaque token — assume a conservative lifetime.
900
- }
901
- return Date.now() + FALLBACK_TOKEN_LIFETIME_MS;
902
- }
903
-
904
880
  function describeError(error: unknown): string {
905
881
  return error instanceof Error ? error.message : String(error);
906
882
  }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Shared `CURSOR_API_KEY` → Cursor session-token exchange primitive
3
+ * (`POST /auth/exchange_user_api_key`, ~1h session).
4
+ *
5
+ * Both `CursorBackendClient` (dashboard RPCs) and the Origin channel's
6
+ * session provider (`cursorSessionTokenProvider` in
7
+ * `../../channels/origin/api.ts`) mint through this module, so the URL,
8
+ * request shape, response parsing, JWT-expiry handling, and renewal
9
+ * constants cannot drift between the two. Callers own their caching and
10
+ * retry policies; this module owns exactly one exchange round trip.
11
+ */
12
+
13
+ export const CURSOR_SESSION_EXCHANGE_PATH = "/auth/exchange_user_api_key";
14
+ /** Re-exchange when the cached session is within this window of expiry. */
15
+ export const SESSION_TOKEN_EXPIRY_LEEWAY_MS: number = 5 * 60 * 1000;
16
+ /** Fallback session lifetime when the JWT `exp` claim cannot be parsed. */
17
+ export const SESSION_TOKEN_FALLBACK_LIFETIME_MS: number = 30 * 60 * 1000;
18
+ const EXCHANGE_TIMEOUT_MS = 30_000;
19
+
20
+ /**
21
+ * Exchange failure. `status` is the HTTP status when the backend answered,
22
+ * and undefined for network/transport errors. `detail` carries the server's
23
+ * `error` field when present so callers can surface the reason verbatim.
24
+ */
25
+ export class CursorSessionExchangeError extends Error {
26
+ constructor(
27
+ message: string,
28
+ readonly status?: number,
29
+ readonly detail?: string
30
+ ) {
31
+ super(message);
32
+ this.name = "CursorSessionExchangeError";
33
+ }
34
+ }
35
+
36
+ /** Epoch-ms expiry from a JWT `exp` claim, or undefined when unparsable. */
37
+ export function jwtExpiryMs(token: string): number | undefined {
38
+ try {
39
+ const payload = token.split(".")[1];
40
+ if (payload === undefined) {
41
+ return undefined;
42
+ }
43
+ const claims = JSON.parse(
44
+ Buffer.from(payload, "base64url").toString("utf8")
45
+ ) as { exp?: unknown };
46
+ return typeof claims.exp === "number" ? claims.exp * 1000 : undefined;
47
+ } catch {
48
+ return undefined;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * One exchange round trip. Throws {@link CursorSessionExchangeError} on
54
+ * transport failure, a non-2xx response, or a token-free body. `expiresAtMs`
55
+ * comes from the session JWT's `exp` claim, falling back to
56
+ * {@link SESSION_TOKEN_FALLBACK_LIFETIME_MS} for opaque tokens.
57
+ */
58
+ export async function exchangeCursorApiKeyForSession(args: {
59
+ /** Full exchange URL (base + {@link CURSOR_SESSION_EXCHANGE_PATH}). */
60
+ exchangeUrl: string;
61
+ apiKey: string;
62
+ fetchImpl?: typeof fetch;
63
+ nowMs?: number;
64
+ }): Promise<{ token: string; expiresAtMs: number }> {
65
+ const fetchImpl = args.fetchImpl ?? fetch;
66
+ let response: Response;
67
+ try {
68
+ response = await fetchImpl(args.exchangeUrl, {
69
+ method: "POST",
70
+ headers: {
71
+ "content-type": "application/json",
72
+ authorization: `Bearer ${args.apiKey}`,
73
+ },
74
+ body: "{}",
75
+ signal: AbortSignal.timeout(EXCHANGE_TIMEOUT_MS),
76
+ });
77
+ } catch (error) {
78
+ throw new CursorSessionExchangeError(
79
+ `Could not reach the Cursor session exchange at ${args.exchangeUrl}: ${
80
+ error instanceof Error ? error.message : String(error)
81
+ }`
82
+ );
83
+ }
84
+ const body: unknown = await response.json().catch(() => undefined);
85
+ const record =
86
+ typeof body === "object" && body !== null && !Array.isArray(body)
87
+ ? (body as Record<string, unknown>)
88
+ : {};
89
+ const detail =
90
+ typeof record.error === "string" && record.error !== ""
91
+ ? record.error
92
+ : undefined;
93
+ if (!response.ok) {
94
+ throw new CursorSessionExchangeError(
95
+ `Cursor session exchange failed with HTTP ${response.status}${
96
+ detail === undefined ? "" : ` (${detail})`
97
+ }.`,
98
+ response.status,
99
+ detail
100
+ );
101
+ }
102
+ const token = record.accessToken;
103
+ if (typeof token !== "string" || token === "") {
104
+ throw new CursorSessionExchangeError(
105
+ "Cursor session exchange returned no access token.",
106
+ response.status
107
+ );
108
+ }
109
+ const nowMs = args.nowMs ?? Date.now();
110
+ return {
111
+ token,
112
+ expiresAtMs:
113
+ jwtExpiryMs(token) ?? nowMs + SESSION_TOKEN_FALLBACK_LIFETIME_MS,
114
+ };
115
+ }
@@ -4,6 +4,8 @@ import { join } from "node:path";
4
4
  const MAX = 64;
5
5
  const TEMPLATE_NAMES = [
6
6
  "agentic-owners",
7
+ "agents-md",
8
+ "code-wiki",
7
9
  "demo",
8
10
  "pr-autofixer",
9
11
  "security-help",