@cursor/bdk 0.2.2 → 0.2.4

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 (697) hide show
  1. package/AGENTS.md +1 -1
  2. package/dist/bin/agent-serve.js +32 -8
  3. package/dist/channels/github/auth.d.ts.map +1 -1
  4. package/dist/channels/github/auth.js +36 -5
  5. package/dist/channels/slack/attachments.d.ts +2 -1
  6. package/dist/channels/slack/attachments.d.ts.map +1 -1
  7. package/dist/channels/slack/attachments.js +19 -9
  8. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  9. package/dist/channels/slack/dispatch.js +26 -24
  10. package/dist/channels/slack/thread-context.d.ts +21 -11
  11. package/dist/channels/slack/thread-context.d.ts.map +1 -1
  12. package/dist/channels/slack/thread-context.js +75 -27
  13. package/dist/connections.d.ts +1 -1
  14. package/dist/connections.js +1 -1
  15. package/dist/docs/404.html +2 -2
  16. package/dist/docs/assets/{app.DtYVVVyK.js → app.4kQ8Db_q.js} +4 -4
  17. package/dist/docs/assets/chunks/@localSearchIndexroot.CvuAJ0My.js +1 -0
  18. package/dist/docs/assets/chunks/{VPLocalSearchBox.D47xT7J4.js → VPLocalSearchBox.DY1RwB8Q.js} +1 -1
  19. package/dist/docs/assets/chunks/{arc.CDJ6xzxR.js → arc.CxE_fSPK.js} +1 -1
  20. package/dist/docs/assets/chunks/{architectureDiagram-Q4EWVU46.l1Xnjd3N.js → architectureDiagram-Q4EWVU46.CPX426ui.js} +1 -1
  21. package/dist/docs/assets/chunks/{baseUniq.x-finShZ.js → baseUniq.CB52JrKG.js} +1 -1
  22. package/dist/docs/assets/chunks/{blockDiagram-DXYQGD6D.COC3qynH.js → blockDiagram-DXYQGD6D.J0cNaQCL.js} +1 -1
  23. package/dist/docs/assets/chunks/{c4Diagram-AHTNJAMY.DdQO850E.js → c4Diagram-AHTNJAMY.DJn3vEGT.js} +1 -1
  24. package/dist/docs/assets/chunks/channel.BoNycAoN.js +1 -0
  25. package/dist/docs/assets/chunks/{chunk-4BX2VUAB.Dpn8omLk.js → chunk-4BX2VUAB.Dao618F6.js} +1 -1
  26. package/dist/docs/assets/chunks/{chunk-4TB4RGXK.C0XWStUN.js → chunk-4TB4RGXK.QW8Rhjai.js} +1 -1
  27. package/dist/docs/assets/chunks/{chunk-55IACEB6.DsKSZfIM.js → chunk-55IACEB6.DfRLLKF_.js} +1 -1
  28. package/dist/docs/assets/chunks/{chunk-EDXVE4YY.dNU6JDu6.js → chunk-EDXVE4YY.CIV2ApKQ.js} +1 -1
  29. package/dist/docs/assets/chunks/{chunk-FMBD7UC4.DN41fxKE.js → chunk-FMBD7UC4.BEheSuxp.js} +1 -1
  30. package/dist/docs/assets/chunks/{chunk-OYMX7WX6.COpKCZus.js → chunk-OYMX7WX6.CUN88ENB.js} +1 -1
  31. package/dist/docs/assets/chunks/{chunk-QZHKN3VN.HDB-azx2.js → chunk-QZHKN3VN.CgqCs8b_.js} +1 -1
  32. package/dist/docs/assets/chunks/{chunk-YZCP3GAM.DEeX_a2j.js → chunk-YZCP3GAM.iCH0nnTJ.js} +1 -1
  33. package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.C4x07Gkj.js +1 -0
  34. package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.C4x07Gkj.js +1 -0
  35. package/dist/docs/assets/chunks/clone.Db22cH_-.js +1 -0
  36. package/dist/docs/assets/chunks/{cose-bilkent-S5V4N54A.C3yGL2A8.js → cose-bilkent-S5V4N54A.BLcUxhWE.js} +1 -1
  37. package/dist/docs/assets/chunks/{dagre-KV5264BT.Bpl24en4.js → dagre-KV5264BT.Ep7LofOm.js} +1 -1
  38. package/dist/docs/assets/chunks/{diagram-5BDNPKRD.nFco6seU.js → diagram-5BDNPKRD.Bzf4GSQ4.js} +1 -1
  39. package/dist/docs/assets/chunks/{diagram-G4DWMVQ6.DXhA6DWR.js → diagram-G4DWMVQ6.DD6Jh-eM.js} +1 -1
  40. package/dist/docs/assets/chunks/{diagram-MMDJMWI5.8GoOYcwk.js → diagram-MMDJMWI5.oOIl8bWd.js} +1 -1
  41. package/dist/docs/assets/chunks/{diagram-TYMM5635.DDBlqvlJ.js → diagram-TYMM5635.DHs85Ade.js} +1 -1
  42. package/dist/docs/assets/chunks/{erDiagram-SMLLAGMA.vl_N81P4.js → erDiagram-SMLLAGMA.BIb5ghCk.js} +1 -1
  43. package/dist/docs/assets/chunks/{flowDiagram-DWJPFMVM.Cteg8g7I.js → flowDiagram-DWJPFMVM.DV4paY9k.js} +1 -1
  44. package/dist/docs/assets/chunks/{ganttDiagram-T4ZO3ILL.D4aMhlMM.js → ganttDiagram-T4ZO3ILL.AyEKA6_v.js} +1 -1
  45. package/dist/docs/assets/chunks/{gitGraphDiagram-UUTBAWPF.DF_J0D4q.js → gitGraphDiagram-UUTBAWPF.BltiCDFo.js} +1 -1
  46. package/dist/docs/assets/chunks/{graph.DJs0LDCf.js → graph.DzuEYGdC.js} +1 -1
  47. package/dist/docs/assets/chunks/{infoDiagram-42DDH7IO.Bb-4RQwg.js → infoDiagram-42DDH7IO.BV2l981J.js} +1 -1
  48. package/dist/docs/assets/chunks/{ishikawaDiagram-UXIWVN3A.DEtQRSbc.js → ishikawaDiagram-UXIWVN3A.BA4Izf-m.js} +1 -1
  49. package/dist/docs/assets/chunks/{journeyDiagram-VCZTEJTY.DhQQ75FA.js → journeyDiagram-VCZTEJTY.DjaFbFS3.js} +1 -1
  50. package/dist/docs/assets/chunks/{kanban-definition-6JOO6SKY.C4gEw3Rk.js → kanban-definition-6JOO6SKY.CfTFieDs.js} +1 -1
  51. package/dist/docs/assets/chunks/{layout.DT-PGlXa.js → layout.DzfWqTBV.js} +1 -1
  52. package/dist/docs/assets/chunks/{linear.BMMe__JW.js → linear.BO6mpulV.js} +1 -1
  53. package/dist/docs/assets/chunks/{min.XyaU9K9Q.js → min.BGgTadgT.js} +1 -1
  54. package/dist/docs/assets/chunks/{mindmap-definition-QFDTVHPH.CbBM6xZ-.js → mindmap-definition-QFDTVHPH.DPuIXVWY.js} +1 -1
  55. package/dist/docs/assets/chunks/{pieDiagram-DEJITSTG.DFJoIVou.js → pieDiagram-DEJITSTG.BOnl3bBg.js} +1 -1
  56. package/dist/docs/assets/chunks/{quadrantDiagram-34T5L4WZ.CQjkDYqb.js → quadrantDiagram-34T5L4WZ.DNB6Ckdc.js} +1 -1
  57. package/dist/docs/assets/chunks/{requirementDiagram-MS252O5E.D-mK-JQu.js → requirementDiagram-MS252O5E.D5-Qo7vb.js} +1 -1
  58. package/dist/docs/assets/chunks/{sankeyDiagram-XADWPNL6.BStQInpd.js → sankeyDiagram-XADWPNL6.8t65cesf.js} +1 -1
  59. package/dist/docs/assets/chunks/{sequenceDiagram-FGHM5R23.CA_eooPJ.js → sequenceDiagram-FGHM5R23.CaG07BdS.js} +1 -1
  60. package/dist/docs/assets/chunks/{stateDiagram-FHFEXIEX.BXl-b7ad.js → stateDiagram-FHFEXIEX.TKOweTB6.js} +1 -1
  61. package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.CuqmyQc4.js +1 -0
  62. package/dist/docs/assets/chunks/{theme.q37tOs4x.js → theme.Dx-JhpTB.js} +2 -2
  63. package/dist/docs/assets/chunks/{timeline-definition-GMOUNBTQ.vjuaGOSe.js → timeline-definition-GMOUNBTQ.fE53zQ2s.js} +1 -1
  64. package/dist/docs/assets/chunks/{vennDiagram-DHZGUBPP.BxduJl-F.js → vennDiagram-DHZGUBPP.DJsZZaJg.js} +1 -1
  65. package/dist/docs/assets/chunks/wardley-RL74JXVD.844kyrjl.js +162 -0
  66. package/dist/docs/assets/chunks/{wardleyDiagram-NUSXRM2D.D1z3ds0D.js → wardleyDiagram-NUSXRM2D.CMZ-Ru_t.js} +1 -1
  67. package/dist/docs/assets/chunks/{xychartDiagram-5P7HB3ND.CRU5Ti9b.js → xychartDiagram-5P7HB3ND.XXrhBc7a.js} +1 -1
  68. package/dist/docs/assets/{deployment.md.nGo1yIsa.js → deployment.md.UhnJIMr-.js} +6 -9
  69. package/dist/docs/assets/{deployment.md.nGo1yIsa.lean.js → deployment.md.UhnJIMr-.lean.js} +1 -1
  70. package/dist/docs/assets/{guides_github.md.C2_DSWlE.js → guides_github.md.Bq9kQU9B.js} +9 -3
  71. package/dist/docs/assets/{guides_github.md.C2_DSWlE.lean.js → guides_github.md.Bq9kQU9B.lean.js} +1 -1
  72. package/dist/docs/assets/{guides_jev.md.BNDm14Gy.js → guides_jev.md.CRcgmjDb.js} +2 -2
  73. package/dist/docs/assets/{guides_jev.md.BNDm14Gy.lean.js → guides_jev.md.CRcgmjDb.lean.js} +1 -1
  74. package/dist/docs/assets/{guides_slack.md.ZbHQETOT.js → guides_slack.md.diqqXTTm.js} +2 -2
  75. package/dist/docs/assets/{guides_slack.md.ZbHQETOT.lean.js → guides_slack.md.diqqXTTm.lean.js} +1 -1
  76. package/dist/docs/assets/{quickstart.md.wG4shE1f.js → quickstart.md.CHpHPBUN.js} +5 -3
  77. package/dist/docs/assets/{quickstart.md.wG4shE1f.lean.js → quickstart.md.CHpHPBUN.lean.js} +1 -1
  78. package/dist/docs/assets/{reference_agent-config.md.DzGI777n.js → reference_agent-config.md.Cj5uBKp4.js} +1 -1
  79. package/dist/docs/assets/{reference_artifacts.md.CFMNPreZ.js → reference_artifacts.md.CQfKq8F2.js} +1 -1
  80. package/dist/docs/assets/reference_cli.md.DgeLTO18.js +91 -0
  81. package/dist/docs/assets/reference_cli.md.DgeLTO18.lean.js +1 -0
  82. package/dist/docs/assets/{reference_connections.md._XpKiOaF.js → reference_connections.md.COq--gAm.js} +7 -3
  83. package/dist/docs/assets/{reference_connections.md._XpKiOaF.lean.js → reference_connections.md.COq--gAm.lean.js} +1 -1
  84. package/dist/docs/assets/{reference_evals.md.DqNyHMA6.js → reference_evals.md.CJm3uCUm.js} +4 -4
  85. package/dist/docs/assets/{reference_evals.md.DqNyHMA6.lean.js → reference_evals.md.CJm3uCUm.lean.js} +1 -1
  86. package/dist/docs/assets/{reference_extensions.md.CPPrkWlJ.js → reference_extensions.md.pa2tN-e4.js} +4 -3
  87. package/dist/docs/assets/{reference_extensions.md.CPPrkWlJ.lean.js → reference_extensions.md.pa2tN-e4.lean.js} +1 -1
  88. package/dist/docs/assets/{reference_skills.md.CRFTxt9M.js → reference_skills.md.Cy00QMKS.js} +3 -3
  89. package/dist/docs/assets/{reference_skills.md.CRFTxt9M.lean.js → reference_skills.md.Cy00QMKS.lean.js} +1 -1
  90. package/dist/docs/assets/troubleshooting.md.Cw5PKa3t.js +1 -0
  91. package/dist/docs/assets/{troubleshooting.md.BzZSu1Gz.lean.js → troubleshooting.md.Cw5PKa3t.lean.js} +1 -1
  92. package/dist/docs/building-with-agents.html +35 -35
  93. package/dist/docs/deployment.html +41 -44
  94. package/dist/docs/deployment.md +49 -16
  95. package/dist/docs/evals.html +35 -35
  96. package/dist/docs/guides/agent-to-agent.html +35 -35
  97. package/dist/docs/guides/bitbucket.html +35 -35
  98. package/dist/docs/guides/cloud-agents.html +35 -35
  99. package/dist/docs/guides/convert-automation.html +35 -35
  100. package/dist/docs/guides/github.html +43 -37
  101. package/dist/docs/guides/github.md +34 -0
  102. package/dist/docs/guides/gitlab.html +35 -35
  103. package/dist/docs/guides/grokbot-agents.html +35 -35
  104. package/dist/docs/guides/hooks.html +35 -35
  105. package/dist/docs/guides/improve.html +35 -35
  106. package/dist/docs/guides/jev.html +37 -37
  107. package/dist/docs/guides/jev.md +7 -0
  108. package/dist/docs/guides/mcp-oauth.html +35 -35
  109. package/dist/docs/guides/opentelemetry.html +35 -35
  110. package/dist/docs/guides/slack.html +37 -37
  111. package/dist/docs/guides/slack.md +5 -0
  112. package/dist/docs/guides/webhooks.html +35 -35
  113. package/dist/docs/hashmap.json +1 -1
  114. package/dist/docs/hillclimbing.html +35 -35
  115. package/dist/docs/index.html +35 -35
  116. package/dist/docs/llms-full.txt +223 -94
  117. package/dist/docs/quickstart.html +40 -38
  118. package/dist/docs/quickstart.md +7 -1
  119. package/dist/docs/reference/agent-config.html +37 -37
  120. package/dist/docs/reference/agent-config.md +1 -1
  121. package/dist/docs/reference/artifacts.html +37 -37
  122. package/dist/docs/reference/artifacts.md +1 -1
  123. package/dist/docs/reference/channels.html +35 -35
  124. package/dist/docs/reference/cli.html +55 -61
  125. package/dist/docs/reference/cli.md +65 -63
  126. package/dist/docs/reference/connections.html +42 -38
  127. package/dist/docs/reference/connections.md +28 -2
  128. package/dist/docs/reference/evals.html +39 -39
  129. package/dist/docs/reference/evals.md +6 -7
  130. package/dist/docs/reference/extensions.html +39 -38
  131. package/dist/docs/reference/extensions.md +13 -0
  132. package/dist/docs/reference/hooks.html +35 -35
  133. package/dist/docs/reference/http-api.html +35 -35
  134. package/dist/docs/reference/instructions.html +35 -35
  135. package/dist/docs/reference/playground.html +35 -35
  136. package/dist/docs/reference/project-layout.html +35 -35
  137. package/dist/docs/reference/prompt.html +35 -35
  138. package/dist/docs/reference/schedules.html +35 -35
  139. package/dist/docs/reference/sessions.html +35 -35
  140. package/dist/docs/reference/skills.html +38 -38
  141. package/dist/docs/reference/subagents.html +35 -35
  142. package/dist/docs/reference/tools.html +35 -35
  143. package/dist/docs/templates/agentic-owners.html +35 -35
  144. package/dist/docs/templates/security-reviewer.html +35 -35
  145. package/dist/docs/templates/thermo-quality-review.html +35 -35
  146. package/dist/docs/templates/thermo-review.html +35 -35
  147. package/dist/docs/templates/triage.html +35 -35
  148. package/dist/docs/troubleshooting.html +36 -36
  149. package/dist/docs/troubleshooting.md +7 -3
  150. package/dist/extensions/cursor-grokbot-agents/extension.d.ts +77 -0
  151. package/dist/extensions/cursor-grokbot-agents/extension.d.ts.map +1 -0
  152. package/dist/extensions/cursor-grokbot-agents/extension.js +63 -0
  153. package/dist/extensions/cursor-grokbot-agents/lib/agents.d.ts +11 -0
  154. package/dist/extensions/cursor-grokbot-agents/lib/agents.d.ts.map +1 -0
  155. package/dist/extensions/cursor-grokbot-agents/lib/agents.js +31 -0
  156. package/dist/extensions/cursor-grokbot-agents/lib/backend.d.ts +56 -0
  157. package/dist/extensions/cursor-grokbot-agents/lib/backend.d.ts.map +1 -0
  158. package/dist/extensions/cursor-grokbot-agents/lib/backend.js +158 -0
  159. package/dist/extensions/cursor-grokbot-agents/lib/contacts.d.ts +28 -0
  160. package/dist/extensions/cursor-grokbot-agents/lib/contacts.d.ts.map +1 -0
  161. package/dist/extensions/cursor-grokbot-agents/lib/contacts.js +59 -0
  162. package/dist/extensions/cursor-grokbot-agents/lib/conversation.d.ts +53 -0
  163. package/dist/extensions/cursor-grokbot-agents/lib/conversation.d.ts.map +1 -0
  164. package/dist/extensions/cursor-grokbot-agents/lib/conversation.js +206 -0
  165. package/dist/extensions/cursor-grokbot-agents/lib/inputs.d.ts +4 -0
  166. package/dist/extensions/cursor-grokbot-agents/lib/inputs.d.ts.map +1 -0
  167. package/dist/extensions/cursor-grokbot-agents/lib/inputs.js +15 -0
  168. package/dist/extensions/cursor-grokbot-agents/lib/lock.d.ts +12 -0
  169. package/dist/extensions/cursor-grokbot-agents/lib/lock.d.ts.map +1 -0
  170. package/dist/extensions/cursor-grokbot-agents/lib/lock.js +25 -0
  171. package/dist/extensions/cursor-grokbot-agents/skills/consult.md +40 -0
  172. package/dist/extensions/cursor-grokbot-agents/tools/ask.d.ts +4 -0
  173. package/dist/extensions/cursor-grokbot-agents/tools/ask.d.ts.map +1 -0
  174. package/dist/extensions/cursor-grokbot-agents/tools/ask.js +37 -0
  175. package/dist/extensions/cursor-grokbot-agents/tools/check.d.ts +4 -0
  176. package/dist/extensions/cursor-grokbot-agents/tools/check.d.ts.map +1 -0
  177. package/dist/extensions/cursor-grokbot-agents/tools/check.js +32 -0
  178. package/dist/extensions/cursor-grokbot-agents/tools/interrupt.d.ts +4 -0
  179. package/dist/extensions/cursor-grokbot-agents/tools/interrupt.d.ts.map +1 -0
  180. package/dist/extensions/cursor-grokbot-agents/tools/interrupt.js +24 -0
  181. package/dist/extensions/cursor-grokbot-agents/tools/list.d.ts +4 -0
  182. package/dist/extensions/cursor-grokbot-agents/tools/list.d.ts.map +1 -0
  183. package/dist/extensions/cursor-grokbot-agents/tools/list.js +34 -0
  184. package/dist/extensions/jev/extension.d.ts +3 -0
  185. package/dist/extensions/jev/extension.d.ts.map +1 -1
  186. package/dist/extensions/jev/extension.js +10 -1
  187. package/dist/extensions/jev/lib/evaluate.d.ts +4 -0
  188. package/dist/extensions/jev/lib/evaluate.d.ts.map +1 -1
  189. package/dist/extensions/jev/lib/evaluate.js +6 -2
  190. package/dist/extensions.d.ts +4 -2
  191. package/dist/extensions.d.ts.map +1 -1
  192. package/dist/extensions.js +5 -2
  193. package/dist/internal/cli-ax.d.ts +1 -0
  194. package/dist/internal/cli-ax.d.ts.map +1 -1
  195. package/dist/internal/cli-ax.js +43 -10
  196. package/dist/internal/cli-deploy.d.ts +5 -1
  197. package/dist/internal/cli-deploy.d.ts.map +1 -1
  198. package/dist/internal/cli-deploy.js +41 -9
  199. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
  200. package/dist/internal/cli-mcp-oauth.js +2 -1
  201. package/dist/internal/cli-skills.d.ts +1 -1
  202. package/dist/internal/cli-skills.d.ts.map +1 -1
  203. package/dist/internal/cli-skills.js +5 -5
  204. package/dist/internal/connection-env.d.ts +31 -0
  205. package/dist/internal/connection-env.d.ts.map +1 -0
  206. package/dist/internal/connection-env.js +84 -0
  207. package/dist/internal/cursor/backend-client.js +5 -0
  208. package/dist/internal/deploy-client.d.ts +30 -6
  209. package/dist/internal/deploy-client.d.ts.map +1 -1
  210. package/dist/internal/deploy-client.js +45 -9
  211. package/dist/internal/discovery/agent-config.d.ts +6 -5
  212. package/dist/internal/discovery/agent-config.d.ts.map +1 -1
  213. package/dist/internal/discovery/agent-config.js +29 -28
  214. package/dist/internal/discovery/agent.js +1 -1
  215. package/dist/internal/discovery/connections.d.ts.map +1 -1
  216. package/dist/internal/discovery/connections.js +11 -0
  217. package/dist/internal/discovery/extensions.d.ts.map +1 -1
  218. package/dist/internal/discovery/extensions.js +12 -0
  219. package/dist/internal/hosted-delivery-protocol.d.ts +36 -11
  220. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  221. package/dist/internal/hosted-delivery-protocol.js +31 -8
  222. package/dist/internal/hosted-delivery.d.ts +2 -2
  223. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  224. package/dist/internal/hosted-delivery.js +228 -48
  225. package/dist/internal/hosting.d.ts +10 -2
  226. package/dist/internal/hosting.d.ts.map +1 -1
  227. package/dist/internal/hosting.js +33 -3
  228. package/dist/internal/install-cursor-skills.d.ts +20 -24
  229. package/dist/internal/install-cursor-skills.d.ts.map +1 -1
  230. package/dist/internal/install-cursor-skills.js +50 -48
  231. package/dist/internal/legacy-command-guard.d.ts +2 -0
  232. package/dist/internal/legacy-command-guard.d.ts.map +1 -1
  233. package/dist/internal/legacy-command-guard.js +10 -1
  234. package/dist/internal/mcp-host.d.ts +6 -0
  235. package/dist/internal/mcp-host.d.ts.map +1 -1
  236. package/dist/internal/mcp-host.js +35 -16
  237. package/dist/internal/sdk-runner.d.ts +2 -0
  238. package/dist/internal/sdk-runner.d.ts.map +1 -1
  239. package/dist/internal/sdk-runner.js +10 -0
  240. package/dist/internal/server.d.ts.map +1 -1
  241. package/dist/internal/server.js +11 -1
  242. package/dist/internal/session-engine.d.ts.map +1 -1
  243. package/dist/internal/session-engine.js +9 -5
  244. package/dist/types.d.ts +29 -3
  245. package/dist/types.d.ts.map +1 -1
  246. package/docs/deployment.md +49 -16
  247. package/docs/guides/github.md +34 -0
  248. package/docs/guides/jev.md +7 -0
  249. package/docs/guides/slack.md +5 -0
  250. package/docs/quickstart.md +7 -1
  251. package/docs/reference/agent-config.md +1 -1
  252. package/docs/reference/artifacts.md +1 -1
  253. package/docs/reference/cli.md +65 -63
  254. package/docs/reference/connections.md +28 -2
  255. package/docs/reference/evals.md +6 -7
  256. package/docs/reference/extensions.md +13 -0
  257. package/docs/troubleshooting.md +7 -3
  258. package/package.json +1 -11
  259. package/scripts/install-cursor-skills.mjs +1 -1
  260. package/skills/create-agent/SKILL.md +1 -1
  261. package/skills/debug/SKILL.md +1 -1
  262. package/skills/deploy/SKILL.md +1 -1
  263. package/skills/evals/SKILL.md +1 -1
  264. package/skills/framework-map/SKILL.md +1 -1
  265. package/skills/github/SKILL.md +1 -1
  266. package/skills/hillclimb/SKILL.md +1 -1
  267. package/skills/mcp-auth/SKILL.md +1 -1
  268. package/skills/otel/SKILL.md +1 -1
  269. package/skills/setup-slack/SKILL.md +1 -1
  270. package/templates/agents-md/bot/lib/collect.ts +1 -4
  271. package/templates/code-wiki/bot/channels/github.ts +2 -2
  272. package/templates/demo/bot/channels/github.ts +5 -1
  273. package/templates/pr-autofixer/bot/lib/start-drive.ts +4 -4
  274. package/templates/triage/bot/lib/types.ts +6 -6
  275. package/dist/docs/assets/chunks/@localSearchIndexroot.BgTA2tV1.js +0 -1
  276. package/dist/docs/assets/chunks/channel.B2nMEx82.js +0 -1
  277. package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.D8iCH2ve.js +0 -1
  278. package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.D8iCH2ve.js +0 -1
  279. package/dist/docs/assets/chunks/clone.5XNLjby0.js +0 -1
  280. package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.yGM2uLZQ.js +0 -1
  281. package/dist/docs/assets/chunks/wardley-RL74JXVD.DKfyIejL.js +0 -162
  282. package/dist/docs/assets/reference_cli.md.BK2QUH67.js +0 -97
  283. package/dist/docs/assets/reference_cli.md.BK2QUH67.lean.js +0 -1
  284. package/dist/docs/assets/troubleshooting.md.BzZSu1Gz.js +0 -1
  285. package/src/artifacts.ts +0 -68
  286. package/src/bin/agent-serve.ts +0 -1833
  287. package/src/channels/bitbucket/api.ts +0 -891
  288. package/src/channels/bitbucket/binding.ts +0 -417
  289. package/src/channels/bitbucket/bitbucket-channel.ts +0 -162
  290. package/src/channels/bitbucket/bitbucket-webhook.ts +0 -519
  291. package/src/channels/bitbucket/checks.ts +0 -146
  292. package/src/channels/bitbucket/cursor-account.ts +0 -106
  293. package/src/channels/bitbucket/defaults.ts +0 -100
  294. package/src/channels/bitbucket/events.ts +0 -74
  295. package/src/channels/bitbucket/index.ts +0 -51
  296. package/src/channels/bitbucket/replay.ts +0 -421
  297. package/src/channels/bitbucket/state.ts +0 -59
  298. package/src/channels/bitbucket/types.ts +0 -261
  299. package/src/channels/change-monitors/change-monitors-channel.ts +0 -63
  300. package/src/channels/change-monitors/index.ts +0 -8
  301. package/src/channels/change-monitors/types.ts +0 -42
  302. package/src/channels/checks.ts +0 -185
  303. package/src/channels/deployments/deployments-channel.ts +0 -224
  304. package/src/channels/deployments/index.ts +0 -25
  305. package/src/channels/deployments/types.ts +0 -92
  306. package/src/channels/github/api.ts +0 -526
  307. package/src/channels/github/auth.ts +0 -254
  308. package/src/channels/github/binding.ts +0 -233
  309. package/src/channels/github/checks.ts +0 -145
  310. package/src/channels/github/cursor-account.ts +0 -303
  311. package/src/channels/github/defaults.ts +0 -168
  312. package/src/channels/github/events.ts +0 -67
  313. package/src/channels/github/forward.ts +0 -359
  314. package/src/channels/github/github-channel.ts +0 -2334
  315. package/src/channels/github/index.ts +0 -54
  316. package/src/channels/github/progress.ts +0 -365
  317. package/src/channels/github/replay.ts +0 -372
  318. package/src/channels/github/state.ts +0 -124
  319. package/src/channels/github/types.ts +0 -590
  320. package/src/channels/gitlab/api.ts +0 -329
  321. package/src/channels/gitlab/binding.ts +0 -231
  322. package/src/channels/gitlab/checks.ts +0 -228
  323. package/src/channels/gitlab/cursor-account.ts +0 -105
  324. package/src/channels/gitlab/defaults.ts +0 -80
  325. package/src/channels/gitlab/events.ts +0 -37
  326. package/src/channels/gitlab/gitlab-channel.ts +0 -160
  327. package/src/channels/gitlab/gitlab-webhook.ts +0 -509
  328. package/src/channels/gitlab/index.ts +0 -44
  329. package/src/channels/gitlab/replay.ts +0 -328
  330. package/src/channels/gitlab/state.ts +0 -55
  331. package/src/channels/gitlab/types.ts +0 -247
  332. package/src/channels/issues/index.ts +0 -9
  333. package/src/channels/issues/issues-channel.ts +0 -45
  334. package/src/channels/issues/types.ts +0 -38
  335. package/src/channels/origin/api.ts +0 -507
  336. package/src/channels/origin/binding.ts +0 -114
  337. package/src/channels/origin/checks.ts +0 -250
  338. package/src/channels/origin/cursor-account.ts +0 -58
  339. package/src/channels/origin/defaults.ts +0 -140
  340. package/src/channels/origin/events.ts +0 -35
  341. package/src/channels/origin/index.ts +0 -35
  342. package/src/channels/origin/origin-channel.ts +0 -815
  343. package/src/channels/origin/origin-webhook.ts +0 -296
  344. package/src/channels/origin/reactions.ts +0 -72
  345. package/src/channels/origin/state.ts +0 -54
  346. package/src/channels/origin/types.ts +0 -295
  347. package/src/channels/slack/agentic-delivery.ts +0 -634
  348. package/src/channels/slack/agentic-posted.ts +0 -135
  349. package/src/channels/slack/agentic-signals.ts +0 -231
  350. package/src/channels/slack/api.ts +0 -761
  351. package/src/channels/slack/approvals.ts +0 -279
  352. package/src/channels/slack/assistant.ts +0 -76
  353. package/src/channels/slack/attachments.ts +0 -596
  354. package/src/channels/slack/auth.ts +0 -127
  355. package/src/channels/slack/blocks.ts +0 -131
  356. package/src/channels/slack/bot-mentions.ts +0 -318
  357. package/src/channels/slack/channel-watch.ts +0 -314
  358. package/src/channels/slack/cli-project.ts +0 -249
  359. package/src/channels/slack/code-fences.ts +0 -171
  360. package/src/channels/slack/constants.ts +0 -38
  361. package/src/channels/slack/credentials.ts +0 -88
  362. package/src/channels/slack/defaults.ts +0 -714
  363. package/src/channels/slack/dispatch.ts +0 -1060
  364. package/src/channels/slack/doctor.ts +0 -338
  365. package/src/channels/slack/eval-directive.ts +0 -306
  366. package/src/channels/slack/event-id-dedupe.ts +0 -30
  367. package/src/channels/slack/external-policy.ts +0 -75
  368. package/src/channels/slack/footer.ts +0 -106
  369. package/src/channels/slack/inbound.ts +0 -416
  370. package/src/channels/slack/index.ts +0 -53
  371. package/src/channels/slack/init.ts +0 -169
  372. package/src/channels/slack/interactive.ts +0 -406
  373. package/src/channels/slack/links.ts +0 -23
  374. package/src/channels/slack/live-delivery.ts +0 -489
  375. package/src/channels/slack/log.ts +0 -70
  376. package/src/channels/slack/manifest.ts +0 -240
  377. package/src/channels/slack/message-body.ts +0 -82
  378. package/src/channels/slack/nudge-carrier.ts +0 -224
  379. package/src/channels/slack/nudge-store.ts +0 -334
  380. package/src/channels/slack/paint-start.ts +0 -93
  381. package/src/channels/slack/placeholder.ts +0 -358
  382. package/src/channels/slack/pr-ref.ts +0 -74
  383. package/src/channels/slack/progress-delivery.ts +0 -138
  384. package/src/channels/slack/reasoning-card.ts +0 -125
  385. package/src/channels/slack/reasoning-chip.ts +0 -41
  386. package/src/channels/slack/recording-sink.ts +0 -167
  387. package/src/channels/slack/redact.ts +0 -6
  388. package/src/channels/slack/reply-options.ts +0 -279
  389. package/src/channels/slack/setup.ts +0 -125
  390. package/src/channels/slack/slack-channel.ts +0 -744
  391. package/src/channels/slack/socket-mode.ts +0 -140
  392. package/src/channels/slack/stop.ts +0 -25
  393. package/src/channels/slack/stream.ts +0 -66
  394. package/src/channels/slack/thread-context.ts +0 -485
  395. package/src/channels/slack/tool-approval-events.ts +0 -233
  396. package/src/channels/slack/tool-cards.ts +0 -198
  397. package/src/channels/slack/tool-chip.ts +0 -34
  398. package/src/channels/slack/types.ts +0 -787
  399. package/src/channels/slack/upload.ts +0 -137
  400. package/src/channels.ts +0 -543
  401. package/src/connections.ts +0 -153
  402. package/src/dashboard-session-url.ts +0 -30
  403. package/src/evals/assertions.ts +0 -688
  404. package/src/evals/expect.ts +0 -250
  405. package/src/evals/judge.ts +0 -329
  406. package/src/evals/loaders.ts +0 -111
  407. package/src/evals/matchers.ts +0 -223
  408. package/src/evals/reporters.ts +0 -257
  409. package/src/evals/results.ts +0 -130
  410. package/src/evals/run-facts.ts +0 -306
  411. package/src/evals.ts +0 -709
  412. package/src/extensions/cursor-cloud-agents/extension.ts +0 -131
  413. package/src/extensions/cursor-cloud-agents/lib/backend.ts +0 -386
  414. package/src/extensions/cursor-cloud-agents/lib/channels.ts +0 -11
  415. package/src/extensions/cursor-cloud-agents/lib/fleet.ts +0 -98
  416. package/src/extensions/cursor-cloud-agents/lib/inputs.ts +0 -5
  417. package/src/extensions/cursor-cloud-agents/lib/repos.ts +0 -80
  418. package/src/extensions/cursor-cloud-agents/lib/transcript.ts +0 -19
  419. package/src/extensions/cursor-cloud-agents/skills/handoff.md +0 -37
  420. package/src/extensions/cursor-cloud-agents/tools/cancel.ts +0 -21
  421. package/src/extensions/cursor-cloud-agents/tools/dump.ts +0 -47
  422. package/src/extensions/cursor-cloud-agents/tools/get.ts +0 -18
  423. package/src/extensions/cursor-cloud-agents/tools/launch.ts +0 -62
  424. package/src/extensions/cursor-cloud-agents/tools/list.ts +0 -31
  425. package/src/extensions/cursor-cloud-agents/tools/reply.ts +0 -36
  426. package/src/extensions/improve/extension.ts +0 -69
  427. package/src/extensions/improve/skills/yourself.ts +0 -50
  428. package/src/extensions/jev/extension.ts +0 -95
  429. package/src/extensions/jev/lib/evaluate.ts +0 -266
  430. package/src/extensions/jev/skills/gated-write.md +0 -25
  431. package/src/extensions/jev/skills/questions.md +0 -33
  432. package/src/extensions/jev/tools/evaluate.ts +0 -82
  433. package/src/extensions/slack-communication/evals/harness.ts +0 -705
  434. package/src/extensions/slack-communication/evals/slack-communication.eval.ts +0 -830
  435. package/src/extensions/slack-communication/evals/slack-placeholder.eval.ts +0 -172
  436. package/src/extensions/slack-communication/extension.ts +0 -44
  437. package/src/extensions/slack-communication/skills/slack-voice.ts +0 -56
  438. package/src/extensions/slack-communication/tools/delete-message.ts +0 -33
  439. package/src/extensions/slack-communication/tools/edit-message.ts +0 -42
  440. package/src/extensions/slack-communication/tools/post-card.ts +0 -28
  441. package/src/extensions/slack-communication/tools/post-file.ts +0 -82
  442. package/src/extensions/slack-communication/tools/post-update.ts +0 -40
  443. package/src/extensions/slack-communication/tools/react.ts +0 -30
  444. package/src/extensions/slack-communication/tools/set-status.ts +0 -21
  445. package/src/extensions.ts +0 -179
  446. package/src/files-backends/agent-store-presigned-url.ts +0 -11
  447. package/src/files-backends/cursor-hosted.ts +0 -1212
  448. package/src/files-backends/local-fs.ts +0 -163
  449. package/src/files.ts +0 -293
  450. package/src/filesystem.ts +0 -2005
  451. package/src/hooks.ts +0 -30
  452. package/src/index.ts +0 -259
  453. package/src/internal/advertise-tools.ts +0 -370
  454. package/src/internal/approval-gate.ts +0 -103
  455. package/src/internal/artifacts-store.ts +0 -836
  456. package/src/internal/as-of.ts +0 -257
  457. package/src/internal/auth.ts +0 -46
  458. package/src/internal/authored-alias-hooks.ts +0 -138
  459. package/src/internal/authored-loaders.ts +0 -188
  460. package/src/internal/bounded-int.ts +0 -24
  461. package/src/internal/bounded.ts +0 -25
  462. package/src/internal/brand.ts +0 -28
  463. package/src/internal/browser-origin-guard.ts +0 -67
  464. package/src/internal/builtin-tools/artifacts.ts +0 -62
  465. package/src/internal/builtin-tools/index.ts +0 -53
  466. package/src/internal/builtin-tools/reminders.ts +0 -145
  467. package/src/internal/channel-dispatch.ts +0 -129
  468. package/src/internal/channel-route-dispatch.ts +0 -58
  469. package/src/internal/channel-state.ts +0 -83
  470. package/src/internal/chat-client.ts +0 -594
  471. package/src/internal/cli-ax.ts +0 -2273
  472. package/src/internal/cli-bitbucket.ts +0 -438
  473. package/src/internal/cli-convert-automation.ts +0 -335
  474. package/src/internal/cli-cursor.ts +0 -282
  475. package/src/internal/cli-deploy.ts +0 -2486
  476. package/src/internal/cli-docs.ts +0 -192
  477. package/src/internal/cli-github.ts +0 -806
  478. package/src/internal/cli-gitlab.ts +0 -452
  479. package/src/internal/cli-images.ts +0 -672
  480. package/src/internal/cli-manifest.ts +0 -56
  481. package/src/internal/cli-mcp-oauth.ts +0 -395
  482. package/src/internal/cli-mcp.ts +0 -808
  483. package/src/internal/cli-skills.ts +0 -99
  484. package/src/internal/cli-slack-manual.ts +0 -418
  485. package/src/internal/cli-slack.ts +0 -874
  486. package/src/internal/cloud-merge.ts +0 -51
  487. package/src/internal/cloud-turn-cost.ts +0 -159
  488. package/src/internal/continuation-channel.ts +0 -233
  489. package/src/internal/continuation-identity.ts +0 -935
  490. package/src/internal/conversation-mirror.ts +0 -423
  491. package/src/internal/convert-automation/convert-workflow.ts +0 -1377
  492. package/src/internal/convert-automation/slug.ts +0 -29
  493. package/src/internal/convert-automation/types.ts +0 -212
  494. package/src/internal/cron.ts +0 -76
  495. package/src/internal/cursor/account-mcp.ts +0 -692
  496. package/src/internal/cursor/backend-client.ts +0 -978
  497. package/src/internal/cursor/bound-secret-file.ts +0 -101
  498. package/src/internal/cursor/credentials.ts +0 -560
  499. package/src/internal/cursor/cursor-api-transport.ts +0 -65
  500. package/src/internal/cursor/github-credentials.ts +0 -228
  501. package/src/internal/cursor/hosted-bound-secrets.ts +0 -71
  502. package/src/internal/cursor/hosted-deployment-secrets.ts +0 -100
  503. package/src/internal/cursor/hosted-store-secrets.ts +0 -152
  504. package/src/internal/cursor/session-token.ts +0 -111
  505. package/src/internal/cursor/store-api-client.ts +0 -323
  506. package/src/internal/cursor-account-mcp-auth.ts +0 -303
  507. package/src/internal/cursor-agent-template.ts +0 -50
  508. package/src/internal/cursor-deployment-relay.ts +0 -315
  509. package/src/internal/cursor-event-relay.ts +0 -675
  510. package/src/internal/cursor-relay-core.ts +0 -563
  511. package/src/internal/deferred-channel-session.ts +0 -61
  512. package/src/internal/deploy-client.ts +0 -1504
  513. package/src/internal/deploy-manifest.ts +0 -361
  514. package/src/internal/deploy-source.ts +0 -144
  515. package/src/internal/describe-error.ts +0 -32
  516. package/src/internal/dev-mode.ts +0 -20
  517. package/src/internal/discovery/agent-config.ts +0 -288
  518. package/src/internal/discovery/agent.ts +0 -381
  519. package/src/internal/discovery/channels.ts +0 -100
  520. package/src/internal/discovery/connections.ts +0 -505
  521. package/src/internal/discovery/context.ts +0 -38
  522. package/src/internal/discovery/cursor-plugin.ts +0 -628
  523. package/src/internal/discovery/extension-overlay.ts +0 -247
  524. package/src/internal/discovery/extensions.ts +0 -490
  525. package/src/internal/discovery/fs.ts +0 -103
  526. package/src/internal/discovery/hooks.ts +0 -63
  527. package/src/internal/discovery/info.ts +0 -174
  528. package/src/internal/discovery/instructions.ts +0 -98
  529. package/src/internal/discovery/modules.ts +0 -107
  530. package/src/internal/discovery/names.ts +0 -45
  531. package/src/internal/discovery/project.ts +0 -319
  532. package/src/internal/discovery/reminders.ts +0 -65
  533. package/src/internal/discovery/runtime.ts +0 -158
  534. package/src/internal/discovery/schedules.ts +0 -120
  535. package/src/internal/discovery/seed-files.ts +0 -34
  536. package/src/internal/discovery/services.ts +0 -151
  537. package/src/internal/discovery/skills.ts +0 -174
  538. package/src/internal/discovery/subagent.ts +0 -138
  539. package/src/internal/discovery/tools.ts +0 -170
  540. package/src/internal/discovery.ts +0 -27
  541. package/src/internal/distribution.ts +0 -126
  542. package/src/internal/docs-site.ts +0 -248
  543. package/src/internal/duration.ts +0 -37
  544. package/src/internal/env-file.ts +0 -83
  545. package/src/internal/eval-judge-model.ts +0 -142
  546. package/src/internal/eval-run-store.ts +0 -585
  547. package/src/internal/eval-runner.ts +0 -823
  548. package/src/internal/evals-client.ts +0 -475
  549. package/src/internal/event-log.ts +0 -342
  550. package/src/internal/event-mapper.ts +0 -811
  551. package/src/internal/expected-delivery-failure.ts +0 -34
  552. package/src/internal/factory-resource-channel.ts +0 -103
  553. package/src/internal/filesystem/errors.ts +0 -207
  554. package/src/internal/filesystem/format.ts +0 -253
  555. package/src/internal/filesystem/merge.ts +0 -192
  556. package/src/internal/filesystem/nearest.ts +0 -112
  557. package/src/internal/filesystem/scratch.ts +0 -239
  558. package/src/internal/filesystem/sdk-schemas.ts +0 -204
  559. package/src/internal/filesystem/tools.ts +0 -804
  560. package/src/internal/filesystem/walk.ts +0 -366
  561. package/src/internal/fixtures/units-server.ts +0 -52
  562. package/src/internal/framework-file-storage.ts +0 -357
  563. package/src/internal/framework-storage-selection.ts +0 -156
  564. package/src/internal/frontmatter.ts +0 -108
  565. package/src/internal/github-fanout.ts +0 -144
  566. package/src/internal/guest-network.ts +0 -391
  567. package/src/internal/handleAgentServeTrigger.ts +0 -286
  568. package/src/internal/host-files.ts +0 -341
  569. package/src/internal/host-kv.ts +0 -81
  570. package/src/internal/host-otel.ts +0 -231
  571. package/src/internal/host-platforms.ts +0 -170
  572. package/src/internal/hosted-admission-adapter.ts +0 -47
  573. package/src/internal/hosted-admission-context.ts +0 -37
  574. package/src/internal/hosted-catch-protocol.ts +0 -112
  575. package/src/internal/hosted-catch.ts +0 -179
  576. package/src/internal/hosted-delivery-protocol.ts +0 -787
  577. package/src/internal/hosted-delivery.ts +0 -1152
  578. package/src/internal/hosted-execution-diag.ts +0 -291
  579. package/src/internal/hosted-execution-flush.ts +0 -136
  580. package/src/internal/hosted-execution-latency.ts +0 -301
  581. package/src/internal/hosted-managed-automation.ts +0 -116
  582. package/src/internal/hosting.ts +0 -107
  583. package/src/internal/http-channel-define.ts +0 -25
  584. package/src/internal/http-channel.ts +0 -669
  585. package/src/internal/http-control-plane-session.ts +0 -110
  586. package/src/internal/ids.ts +0 -39
  587. package/src/internal/incoming-headers-to-fetch.ts +0 -45
  588. package/src/internal/init-interview.ts +0 -360
  589. package/src/internal/init-project.ts +0 -711
  590. package/src/internal/init-scaffold.ts +0 -182
  591. package/src/internal/inline-stdio-define-stub.ts +0 -4
  592. package/src/internal/inline-stdio-mcp.ts +0 -230
  593. package/src/internal/install-cursor-skills.ts +0 -601
  594. package/src/internal/is-record.ts +0 -3
  595. package/src/internal/json-schema.ts +0 -95
  596. package/src/internal/legacy-command-guard.ts +0 -54
  597. package/src/internal/listen-port.ts +0 -85
  598. package/src/internal/local-control-plane.ts +0 -442
  599. package/src/internal/local-env.ts +0 -59
  600. package/src/internal/local-ingress.ts +0 -90
  601. package/src/internal/log-ring.ts +0 -59
  602. package/src/internal/logs-client.ts +0 -477
  603. package/src/internal/map-pool.ts +0 -39
  604. package/src/internal/mcp-endpoint.ts +0 -747
  605. package/src/internal/mcp-host.ts +0 -481
  606. package/src/internal/mcp-oauth-on-demand.ts +0 -337
  607. package/src/internal/mcp-oauth-persist.ts +0 -118
  608. package/src/internal/mcp-oauth.ts +0 -549
  609. package/src/internal/net.ts +0 -29
  610. package/src/internal/open-browser.ts +0 -41
  611. package/src/internal/otel/attrs.ts +0 -16
  612. package/src/internal/otel/collector.ts +0 -937
  613. package/src/internal/otel/config.ts +0 -287
  614. package/src/internal/otel/http.ts +0 -122
  615. package/src/internal/otel/mcp.ts +0 -56
  616. package/src/internal/otel/propagation.ts +0 -17
  617. package/src/internal/otel/runtime.ts +0 -441
  618. package/src/internal/package-inline-stdio.ts +0 -114
  619. package/src/internal/peer-connections.ts +0 -96
  620. package/src/internal/platform-schedule-sync.ts +0 -75
  621. package/src/internal/platform-timers.ts +0 -365
  622. package/src/internal/playground/static.ts +0 -278
  623. package/src/internal/playground/toolchain.ts +0 -270
  624. package/src/internal/playground-proxy.ts +0 -564
  625. package/src/internal/process-memory-telemetry.ts +0 -334
  626. package/src/internal/project-layout.ts +0 -31
  627. package/src/internal/prompt-context.ts +0 -116
  628. package/src/internal/reminder-control-plane-protocol.ts +0 -482
  629. package/src/internal/reminder-runner.ts +0 -1559
  630. package/src/internal/reminder-schedule.ts +0 -104
  631. package/src/internal/reminder-store.ts +0 -176
  632. package/src/internal/request-error-status.ts +0 -95
  633. package/src/internal/request-headers.ts +0 -21
  634. package/src/internal/resolve-prod-target.ts +0 -334
  635. package/src/internal/resolved-connections.ts +0 -190
  636. package/src/internal/router.ts +0 -143
  637. package/src/internal/run-client.ts +0 -354
  638. package/src/internal/runtime-dispatch-runner.ts +0 -50
  639. package/src/internal/sanitize-mcp-tool-name.ts +0 -31
  640. package/src/internal/schedule-runner.ts +0 -154
  641. package/src/internal/scm/pr-url.ts +0 -245
  642. package/src/internal/sdk-runner.ts +0 -948
  643. package/src/internal/serve-restart.ts +0 -113
  644. package/src/internal/server.ts +0 -3135
  645. package/src/internal/session-cost.ts +0 -208
  646. package/src/internal/session-engine.ts +0 -5618
  647. package/src/internal/session-links.ts +0 -84
  648. package/src/internal/session-run-log.ts +0 -535
  649. package/src/internal/session-store.ts +0 -307
  650. package/src/internal/session-urls.ts +0 -32
  651. package/src/internal/sessions-client.ts +0 -239
  652. package/src/internal/skill-catalog.ts +0 -66
  653. package/src/internal/skills-sync.ts +0 -55
  654. package/src/internal/slack-provision-client.ts +0 -544
  655. package/src/internal/slack-thread-key.ts +0 -11
  656. package/src/internal/sleep.ts +0 -3
  657. package/src/internal/start-log-store.ts +0 -164
  658. package/src/internal/steering.ts +0 -17
  659. package/src/internal/storage-coordinator.ts +0 -800
  660. package/src/internal/storage-roles.ts +0 -86
  661. package/src/internal/storage.ts +0 -1
  662. package/src/internal/store-api-protocol.ts +0 -205
  663. package/src/internal/store-mirror.ts +0 -73
  664. package/src/internal/stream-progress.ts +0 -294
  665. package/src/internal/terminal-style.ts +0 -133
  666. package/src/internal/testing/factory-resource-fixtures.ts +0 -84
  667. package/src/internal/testing/scm-continuation-fixtures.ts +0 -285
  668. package/src/internal/tool-catalog.ts +0 -104
  669. package/src/internal/tool-policy.ts +0 -222
  670. package/src/internal/tool-result.ts +0 -50
  671. package/src/internal/trajectory.ts +0 -418
  672. package/src/internal/turn-cost.ts +0 -136
  673. package/src/internal/turn-governor.ts +0 -145
  674. package/src/internal/turn-lease-store.ts +0 -144
  675. package/src/internal/turn-outbox.ts +0 -77
  676. package/src/internal/update-check.ts +0 -517
  677. package/src/internal/v2-hosting.ts +0 -52
  678. package/src/internal/workspace-sync.ts +0 -114
  679. package/src/internal/workspace.ts +0 -505
  680. package/src/kv/memory-kv.ts +0 -94
  681. package/src/kv.ts +0 -18
  682. package/src/memory.ts +0 -487
  683. package/src/otel-public.ts +0 -24
  684. package/src/otel.ts +0 -320
  685. package/src/prompt.ts +0 -80
  686. package/src/reminders.ts +0 -65
  687. package/src/schedules.ts +0 -31
  688. package/src/skills.ts +0 -27
  689. package/src/storage-backends/cursor-hosted-v2.ts +0 -204
  690. package/src/storage-backends/cursor-hosted.ts +0 -388
  691. package/src/storage-backends/file-kv.ts +0 -276
  692. package/src/storage-protocol.ts +0 -261
  693. package/src/storage.ts +0 -408
  694. package/src/tools.ts +0 -166
  695. package/src/types.ts +0 -3766
  696. /package/dist/docs/assets/{reference_agent-config.md.DzGI777n.lean.js → reference_agent-config.md.Cj5uBKp4.lean.js} +0 -0
  697. /package/dist/docs/assets/{reference_artifacts.md.CFMNPreZ.lean.js → reference_artifacts.md.CQfKq8F2.lean.js} +0 -0
@@ -1,1212 +0,0 @@
1
- /**
2
- * Cursor-managed durable files for hosted agents — the backend that puts
3
- * `ctx.host.files` bytes in an Agent Store on S3.
4
- *
5
- * One credential throughout: the engine's pod `CURSOR_API_KEY`. It authorizes
6
- * every call, and the bytes then move between this process and presigned S3
7
- * URLs. File contents never transit the control plane, so a large artifact
8
- * costs S3 bandwidth rather than backend memory.
9
- *
10
- * By default everything goes through the public API under
11
- * `/v0/agent-serve-files`, the same surface any API-key holder gets — a
12
- * self-hosted deployment, a CI job, or a customer script can drive the same
13
- * store with the same requests. Cursor-managed v2 pods can instead be pinned
14
- * (per deployment, at task-time bind) onto the engine-facing factory store API
15
- * under `/internal/agentsdk/store`: same host, same credential, same stores
16
- * and S3 objects, but flush-shaped — one control-plane request settles a whole
17
- * `putMany` batch or a conditional delete, where `/v0` costs a presign POST
18
- * plus stat GETs and a delete POST. The pin fails safe: an absent or
19
- * unrecognized protocol value, or a source id that maps to neither deployment
20
- * store role, keeps `/v0`.
21
- *
22
- * Two things follow. There is no Agent Store token: a token narrows an identity
23
- * to one store and one capability, which is what a caller that cannot hold a
24
- * credential needs (an in-pod FUSE daemon). This process holds the credential
25
- * the token would be minted from, so a token bought a round trip, an expiry to
26
- * manage, and a second auth path, and no privilege reduction. And there is no
27
- * direct call to the store service: the API layer owns the policy, and the
28
- * engine never addresses it.
29
- *
30
- * There is also no store lookup. Every request names this deployment's file
31
- * store by the `agentStoreSourceId` the deploy pipeline pinned into the pod
32
- * env as `AGENT_SERVE_STORE_SOURCE_ID` — provisioning the store at the same
33
- * time, so by boot the store exists and the id resolves. The id is an
34
- * address, not a credential: the server maps it to a store and authorizes
35
- * every call against the deployment's own live service account, so holding
36
- * it grants nothing, and this process never learns a store id it could use
37
- * to ask for anyone else's.
38
- *
39
- * Locally, point `CURSOR_API_BASE_URL` at a backend that serves
40
- * `/v0/agent-serve-files` and use a deployment SA key; off hosting the
41
- * local-disk sink is the default instead.
42
- *
43
- * Writes are conditional. The store rejects unconditional writes, so every
44
- * `put` first learns the current etag: `new` for a path we believe is absent,
45
- * `etag` to replace what we last observed. A raced write comes back 412 and
46
- * surfaces as {@link FileConflictError}, which is why this backend reports
47
- * `etags: true` and `host.files.capabilities.conflictDetection` is honestly
48
- * true.
49
- */
50
-
51
- import { createHash } from "node:crypto";
52
- import {
53
- FileConflictError,
54
- type FileSink,
55
- type FileSinkBody,
56
- type FileSinkEntry,
57
- type FileSinkPutResult,
58
- } from "../files.js";
59
- import { mapBounded } from "../internal/bounded.js";
60
- import {
61
- type CursorApiTransportOptions,
62
- resolveCursorApiBaseUrl,
63
- resolveCursorApiFetch,
64
- resolveCursorApiKey,
65
- } from "../internal/cursor/cursor-api-transport.js";
66
- import {
67
- resolveDeploymentStoreSourceIdSync,
68
- resolveFrameworkStoreSourceIdSync,
69
- resolveStoreProtocolSync,
70
- storeProtocolEnablesFactoryFlush,
71
- } from "../internal/cursor/hosted-store-secrets.js";
72
- import { createAgentStorePresignedUrlValidatorForBackend } from "./agent-store-presigned-url.js";
73
-
74
- const AGENT_SERVE_FILES_PATH = "/v0/agent-serve-files";
75
-
76
- /**
77
- * Engine-facing store API on the factory service — same host, different path
78
- * family. Selected per pod by the task-bound protocol pin
79
- * ({@link resolveStoreProtocolSync}); the server resolves the deployment and
80
- * its stores from the pod credential, so requests carry a store *role*
81
- * (`deployment` | `framework`), never a store id. One flush request replaces
82
- * the presign POST plus the stat GETs and delete POST a turn costs on `/v0`.
83
- */
84
- const AGENTSDK_STORE_PATH = "/internal/agentsdk/store";
85
-
86
- /**
87
- * Pinned by the deploy pipeline for every Cursor-managed deployment — which
88
- * provisions the file store it names in the same step — and reserved from
89
- * user secrets. Its presence is the signal that a durable store exists and
90
- * the hosted control plane is reachable; a deployment without one (no
91
- * service account yet) has no store, and this backend is not selected.
92
- */
93
- export const AGENT_SERVE_STORE_SOURCE_ID_ENV_VAR = "AGENT_SERVE_STORE_SOURCE_ID";
94
-
95
- /**
96
- * The deployment's **framework-level** file store, distinct from the
97
- * user-level store above. The user store carries author-facing bytes
98
- * (`ctx.host.files`, skills, memory, artifacts) and is browsable and
99
- * mountable; the framework store carries engine-owned session state —
100
- * session records, event transcripts, hosted-execution run logs,
101
- * continuation index, and the SDK conversation mirror — and is never
102
- * exposed to authors or models.
103
- * Provisioned by the v2 deploy pipeline alongside the user store; v1
104
- * deployments have only the user store.
105
- */
106
- export const AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID_ENV_VAR =
107
- "AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID";
108
-
109
- /** Cap on the per-process etag cache; a miss costs a lookup, not correctness. */
110
- const MAX_REMEMBERED_ETAGS = 1_000;
111
-
112
- /** Bounded directory page for point lookups. */
113
- const DIRECTORY_PAGE_SIZE = 1_000;
114
- /**
115
- * Paths per presign call in {@link FileSink.getMany}, matching the factory
116
- * `/read` route's server-side `MAX_READ_PATHS`. The whole point of the batch:
117
- * the factory wire's per-deployment rate limiter counts requests, not files,
118
- * so a restore that used to spend N `/read` calls now spends ceil(N/100).
119
- */
120
- const READ_PRESIGN_BATCH_MAX = 100;
121
- /**
122
- * Concurrent presigned-URL downloads inside one {@link FileSink.getMany}
123
- * batch — the S3 fan-out, not the control-plane call count. Matches the
124
- * conversation mirror's historical `HYDRATE_DOWNLOAD_CONCURRENCY`.
125
- */
126
- const GET_MANY_DOWNLOAD_CONCURRENCY = 8;
127
- /** Stop paging a pathological directory rather than scanning forever. */
128
- const MAX_DIRECTORY_PAGES = 20;
129
- /**
130
- * Total page budget for a full `list()` walk of a store the unscoped listing
131
- * could not carry (~1M entries at the page size above). Past it, fail loudly:
132
- * a silently partial listing would be mistaken for the whole scope.
133
- */
134
- const MAX_LIST_WALK_PAGES = 1_000;
135
-
136
- /**
137
- * True on Cursor-managed hosting with a provisioned file store.
138
- *
139
- * Keyed on the store source id alone, deliberately: the credential is not part
140
- * of the test. If it were, a missing or regressed `CURSOR_API_KEY` would
141
- * silently select pod-local disk on a hosted pod and accept writes that vanish
142
- * with the pod. Instead the store is detected, and a missing credential fails
143
- * loudly when the sink is used.
144
- */
145
- export function isCursorHostedFilesAvailable(env: NodeJS.ProcessEnv = process.env): boolean {
146
- return resolveDeploymentStoreSourceIdSync(env) !== undefined;
147
- }
148
-
149
- /**
150
- * Local `serve`/`run` with a personal `CURSOR_API_KEY` can address
151
- * `/v0/agent-serve-files/user`. Hosted deployments have a store source id
152
- * and must not take this path — they own the deployment store instead.
153
- */
154
- export function isCursorUserStoreFilesAvailable(env: NodeJS.ProcessEnv = process.env): boolean {
155
- if (isCursorHostedFilesAvailable(env)) {
156
- return false;
157
- }
158
- // Vitest inherits the developer's shell. User-store sync writes durable
159
- // keys; skip it in the test runner so `pnpm test` cannot poison
160
- // `agent-serve/<agent>/skills/` on the caller's USER store.
161
- if (env.VITEST === "true") {
162
- return false;
163
- }
164
- const key = env.CURSOR_API_KEY?.trim();
165
- return key !== undefined && key !== "";
166
- }
167
-
168
- /**
169
- * Base URL / credential / fetch resolution is the shared
170
- * {@link CursorApiTransportOptions} (`internal/cursor/cursor-api-transport.ts`),
171
- * so this sink and the domain-lane store API client resolve identically.
172
- */
173
- export interface CursorHostedFilesOptions extends CursorApiTransportOptions {
174
- /** File store to address (default: `AGENT_SERVE_STORE_SOURCE_ID`). */
175
- storeSourceId?: string;
176
- /**
177
- * Lazy store source id, re-resolved on every call and preferred over
178
- * {@link storeSourceId} — freeze bind drops ids into `/run/cursor/secrets`
179
- * after serve start without restarting July.
180
- */
181
- resolveStoreSourceId?: () => string | undefined;
182
- /**
183
- * Lazy store protocol pin (default: env, then the task-bound file). When it
184
- * enables the factory flush wire (`factory-flush-v1`, or the superset
185
- * domain-API pin `factory-api-v1`) and the source id maps to a deployment
186
- * store role, requests ride the factory store API instead of
187
- * `/v0/agent-serve-files`. Any other value keeps `/v0`.
188
- */
189
- resolveStoreProtocol?: () => string | undefined;
190
- }
191
-
192
- /**
193
- * What a file operation needs: where this deployment's files live, and which
194
- * wire reaches them. `v0` addresses the public API by store source id; `flush`
195
- * addresses the factory store API by role and lets the server resolve.
196
- */
197
- type FilesTarget =
198
- | { wire: "v0"; path(suffix?: string): string }
199
- | { wire: "flush"; store: "deployment" | "framework" };
200
-
201
- /**
202
- * Wire shapes from `/v0/agent-serve-files`. Declared loosely on purpose: this is a
203
- * versioned public API, and a client that hard-required every field would break
204
- * on an additive change it could otherwise ignore.
205
- */
206
- interface ApiFile {
207
- path?: string;
208
- sizeBytes?: number;
209
- etag?: string;
210
- updatedAt?: string;
211
- }
212
-
213
- interface ApiFileListing {
214
- files?: ApiFile[];
215
- complete?: boolean;
216
- /** Immediate subdirectories; only returned by the `?path=` form. */
217
- directories?: string[];
218
- nextPageToken?: string;
219
- }
220
-
221
- interface ApiPresignResponse {
222
- files?: Array<{
223
- path?: string;
224
- url?: string;
225
- headers?: Record<string, string>;
226
- }>;
227
- }
228
-
229
- interface ApiDeleteResponse {
230
- files?: Array<{ path?: string; status?: string; etag?: string }>;
231
- }
232
-
233
- /**
234
- * Per-op outcome from the factory store API's `flush` route, index-aligned
235
- * with the request ops. A put answers `url`/`headers`; a delete answers
236
- * `status`; declared loosely for the same additive-change reason as the
237
- * `/v0` shapes above.
238
- */
239
- interface FlushWireOpOutcome {
240
- path?: string;
241
- url?: string;
242
- headers?: Record<string, string>;
243
- error?: string;
244
- status?: string;
245
- etag?: string;
246
- }
247
-
248
- interface FlushWireResponse {
249
- ops?: FlushWireOpOutcome[];
250
- }
251
-
252
- type HostedFilesMethods = Omit<FileSink, "get" | "getMany"> & {
253
- /** Narrower than {@link FileSink.get}: this backend always reports the etag. */
254
- get(key: string): Promise<FileSinkBody | undefined>;
255
- /** Always present here (and narrowed like {@link get}); optional on {@link FileSink}. */
256
- getMany(keys: readonly string[]): Promise<Array<FileSinkBody | undefined>>;
257
- };
258
-
259
- /**
260
- * Cursor-hosted {@link FileSink}, selected on Cursor-managed hosting.
261
- *
262
- * Stateless apart from the etag cache below: every request names this
263
- * deployment's store by its pinned source id, so there is nothing to
264
- * resolve, hold, or invalidate.
265
- */
266
- export function cursorHostedFiles(options: CursorHostedFilesOptions = {}): HostedFilesMethods {
267
- /**
268
- * Etags observed per key, so a replace can carry a precondition without
269
- * re-reading. Bounded: a resident engine is long-lived and touches an
270
- * unbounded number of keys over its lifetime. Oldest-out is fine — a miss
271
- * costs one lookup, not correctness.
272
- */
273
- const observedEtags = new Map<string, string>();
274
- const rememberEtag = (key: string, etag: string): void => {
275
- if (observedEtags.size >= MAX_REMEMBERED_ETAGS) {
276
- const oldest = observedEtags.keys().next();
277
- if (!oldest.done) {
278
- observedEtags.delete(oldest.value);
279
- }
280
- }
281
- observedEtags.set(key, etag);
282
- };
283
-
284
- /**
285
- * Run `call` against this deployment's files.
286
- *
287
- * No network call and nothing cached: the store source id and protocol pin
288
- * come from the environment (or task-bound files) and the server maps them
289
- * on every request. Resolved per call rather than once at construction so a
290
- * misconfigured environment fails where the operation is, not where the
291
- * sink was created — and so a protocol pin bound after serve start takes
292
- * effect without a restart.
293
- */
294
- async function withFiles<T>(call: (files: FilesTarget) => Promise<T>): Promise<T> {
295
- const sourceId = resolveStoreSourceId(options);
296
- const protocol = options.resolveStoreProtocol?.() ?? resolveStoreProtocolSync();
297
- if (storeProtocolEnablesFactoryFlush(protocol)) {
298
- // The factory API addresses stores by role, so map the pinned source id
299
- // back to the role it was pinned from. A source id matching neither (a
300
- // local-dev override) has no role there and stays on `/v0`, as does any
301
- // protocol value this runtime does not recognize.
302
- if (sourceId === resolveFrameworkStoreSourceIdSync()) {
303
- return await call({ wire: "flush", store: "framework" });
304
- }
305
- if (sourceId === resolveDeploymentStoreSourceIdSync()) {
306
- return await call({ wire: "flush", store: "deployment" });
307
- }
308
- }
309
- return await call({
310
- wire: "v0",
311
- path: (suffix = "") => `/${encodeURIComponent(sourceId)}${suffix}`,
312
- });
313
- }
314
-
315
- /**
316
- * Fetch one presigned read URL, shared by `get` and `getMany`. A 404 on
317
- * the object GET is absence (a delete raced the presign).
318
- *
319
- * A 403 here is deliberately *not* absence. Absence was already settled
320
- * by the presign: the store refuses to presign a path with no live
321
- * object, so reaching this point means the object existed moments ago.
322
- * What produces a 403 after a good presign is an expired or clock-skewed
323
- * signature, a policy change, or a delete that raced the GET — all
324
- * transient or retryable, none of them "definitively gone".
325
- *
326
- * Reporting them as absence is how a store blip becomes data loss:
327
- * `undefined` on a continuation read is taken as "unknown token", and
328
- * the engine mints a fresh session under it, forking the thread. The
329
- * delete race resolves itself on retry, where the presign returns 404
330
- * and this correctly reads as a miss.
331
- */
332
- async function downloadPresigned(key: string, url: string): Promise<FileSinkBody | undefined> {
333
- const response = await resolveCursorApiFetch(options)(assertPresignedUrlSafe(options, url), {
334
- method: "GET",
335
- redirect: "manual",
336
- });
337
- if (response.status === 404) {
338
- return undefined;
339
- }
340
- if (!response.ok) {
341
- throw new Error(`cursorHostedFiles: download of "${key}" failed (HTTP ${response.status})`);
342
- }
343
- const etag = normalizeEtag(response.headers.get("etag"));
344
- if (etag !== undefined) {
345
- rememberEtag(key, etag);
346
- }
347
- return {
348
- body: new Uint8Array(await response.arrayBuffer()),
349
- ...(etag === undefined ? {} : { etag }),
350
- };
351
- }
352
-
353
- /** One page of the paginated per-directory listing. */
354
- async function directoryPage(
355
- files: FilesTarget,
356
- directory: string,
357
- pageToken: string | undefined,
358
- ): Promise<ApiFileListing> {
359
- if (files.wire === "flush") {
360
- return await flushPost<ApiFileListing>(options, "/list", {
361
- store: files.store,
362
- path: directory,
363
- limit: DIRECTORY_PAGE_SIZE,
364
- ...(pageToken === undefined ? {} : { pageToken }),
365
- });
366
- }
367
- const query = new URLSearchParams({
368
- path: directory,
369
- limit: String(DIRECTORY_PAGE_SIZE),
370
- });
371
- if (pageToken !== undefined) {
372
- query.set("pageToken", pageToken);
373
- }
374
- return await storeGet<ApiFileListing>(options, files.path(`?${query.toString()}`));
375
- }
376
-
377
- /** The unscoped whole-store listing, on whichever wire the target rides. */
378
- async function unscopedListing(files: FilesTarget): Promise<ApiFileListing> {
379
- if (files.wire === "flush") {
380
- return await flushPost<ApiFileListing>(options, "/list", {
381
- store: files.store,
382
- });
383
- }
384
- return await storeGet<ApiFileListing>(options, files.path());
385
- }
386
-
387
- async function listEntries(prefix: string): Promise<FileSinkEntry[]> {
388
- return await withFiles(async files => {
389
- const raw = await unscopedListing(files);
390
- if (raw.complete === true) {
391
- const out: FileSinkEntry[] = [];
392
- for (const file of raw.files ?? []) {
393
- if (typeof file.path !== "string" || !file.path.startsWith(prefix)) {
394
- continue;
395
- }
396
- out.push(toEntry(file, file.path));
397
- }
398
- // The FileSink contract promises key order, and transcript-chunk
399
- // consumers depend on it (chunk keys are zero-padded so key order
400
- // is append order). The server happens to scan in order today; do
401
- // not let that be the only thing holding the contract.
402
- return out.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
403
- }
404
- // The unscoped listing was truncated (`complete: false`) and has no
405
- // continuation to follow. `list()` promises the whole scope, so rather
406
- // than lie or fail, walk the paginated per-directory form, which does
407
- // paginate and reports immediate subdirectories to recurse into.
408
- return await walkDirectoryTree(files, prefix);
409
- });
410
- }
411
-
412
- /**
413
- * Every entry under `prefix`, via the paginated directory listing.
414
- *
415
- * Starts at the deepest directory the prefix fully names and descends only
416
- * into subdirectories that can still hold matches. Bounded so a runaway
417
- * store fails loudly rather than returning a silently partial answer the
418
- * caller would mistake for the whole scope.
419
- */
420
- async function walkDirectoryTree(files: FilesTarget, prefix: string): Promise<FileSinkEntry[]> {
421
- const slash = prefix.lastIndexOf("/");
422
- const pending: string[] = [slash === -1 ? "" : prefix.slice(0, slash)];
423
- const queued = new Set<string>(pending);
424
- const out: FileSinkEntry[] = [];
425
- let pageBudget = MAX_LIST_WALK_PAGES;
426
- while (pending.length > 0) {
427
- const directory = pending.pop() as string;
428
- let pageToken: string | undefined;
429
- do {
430
- if (pageBudget <= 0) {
431
- throw new Error(
432
- "cursorHostedFiles: the store is too large to list; scope the listing with a narrower prefix",
433
- );
434
- }
435
- pageBudget -= 1;
436
- const page = await directoryPage(files, directory, pageToken);
437
- for (const file of page.files ?? []) {
438
- if (typeof file.path === "string" && file.path.startsWith(prefix)) {
439
- out.push(toEntry(file, file.path));
440
- }
441
- }
442
- // Every page can surface different subdirectories, so collect from
443
- // all of them, deduped — and descend only into branches that can
444
- // still hold matches: a descendant of the prefix, or (at the first
445
- // level) a directory the prefix's basename component narrows to.
446
- for (const subdir of page.directories ?? []) {
447
- if (queued.has(subdir)) {
448
- continue;
449
- }
450
- if (`${subdir}/`.startsWith(prefix) || prefix.startsWith(`${subdir}/`)) {
451
- queued.add(subdir);
452
- pending.push(subdir);
453
- }
454
- }
455
- pageToken =
456
- page.complete === false && page.nextPageToken !== undefined
457
- ? page.nextPageToken
458
- : undefined;
459
- } while (pageToken !== undefined);
460
- }
461
- return out.sort((a, b) => (a.key < b.key ? -1 : a.key > b.key ? 1 : 0));
462
- }
463
-
464
- /** Shared decode: remember the etag as a side effect, then shape the entry. */
465
- function toEntry(file: ApiFile, key: string): FileSinkEntry {
466
- if (typeof file.etag === "string" && file.etag !== "") {
467
- rememberEtag(key, file.etag);
468
- }
469
- const mtimeMs = typeof file.updatedAt === "string" ? Date.parse(file.updatedAt) : undefined;
470
- return {
471
- key,
472
- size: typeof file.sizeBytes === "number" ? file.sizeBytes : 0,
473
- ...(typeof file.etag === "string" && file.etag !== "" ? { etag: file.etag } : {}),
474
- ...(mtimeMs === undefined || Number.isNaN(mtimeMs) ? {} : { mtimeMs }),
475
- };
476
- }
477
-
478
- /**
479
- * One key's current entry, via the bounded directory listing.
480
- *
481
- * The unscoped listing has no prefix parameter, so using it for a point lookup
482
- * would walk the whole store on the hot write path and cost more the fuller
483
- * the store got. `?path=` is scoped to one directory and paginates, and the
484
- * scan stops as soon as the key appears.
485
- */
486
- async function statKey(key: string): Promise<FileSinkEntry | undefined> {
487
- const slash = key.lastIndexOf("/");
488
- const directory = slash === -1 ? "" : key.slice(0, slash);
489
- return await withFiles(async files => {
490
- let pageToken: string | undefined;
491
- for (let page = 0; page < MAX_DIRECTORY_PAGES; page += 1) {
492
- const raw = await directoryPage(files, directory, pageToken);
493
- for (const file of raw.files ?? []) {
494
- if (file.path === key) {
495
- return toEntry(file, key);
496
- }
497
- }
498
- // `complete` is false exactly when another page is waiting.
499
- if (raw.complete !== false || raw.nextPageToken === undefined) {
500
- return undefined;
501
- }
502
- pageToken = raw.nextPageToken;
503
- }
504
- return undefined;
505
- });
506
- }
507
-
508
- /** One key's share of a (possibly multi-file) upload attempt. */
509
- type UploadSpec = {
510
- key: string;
511
- body: Uint8Array;
512
- precondition: { etag: string } | { new: true };
513
- };
514
- type UploadOutcome =
515
- | { status: "ok"; result: FileSinkPutResult }
516
- | { status: "conflict" }
517
- | { status: "error"; error: unknown };
518
-
519
- /**
520
- * The shared presign for one upload attempt: a `/v0` upload POST, or one
521
- * flush request whose put outcomes are reshaped into the same instruction
522
- * list (an outcome without a `url` — an oversize refusal — surfaces exactly
523
- * like a `/v0` multipart answer: no single-part instruction for that key).
524
- */
525
- async function presignUploads(
526
- files: FilesTarget,
527
- specs: UploadSpec[],
528
- ): Promise<ApiPresignResponse> {
529
- if (files.wire === "flush") {
530
- const response = await flushPost<FlushWireResponse>(options, "/flush", {
531
- store: files.store,
532
- ops: specs.map(spec => ({
533
- op: "put",
534
- path: spec.key,
535
- sha: createHash("sha256").update(spec.body).digest("hex"),
536
- size: spec.body.byteLength,
537
- ...spec.precondition,
538
- })),
539
- });
540
- // Index-aligned with the request ops, entries without a `url` included:
541
- // a refused op (oversize) must keep its slot so the by-path/positional
542
- // instruction matching below cannot hand its sibling's presign to it,
543
- // and the missing url surfaces as that key's own error.
544
- return {
545
- files: (response.ops ?? []).map(op => ({
546
- path: op.path,
547
- ...(typeof op.url === "string" && op.url !== "" ? { url: op.url } : {}),
548
- headers: op.headers,
549
- })),
550
- };
551
- }
552
- return await storePost<ApiPresignResponse>(options, files.path("/upload"), {
553
- files: specs.map(spec => ({
554
- path: spec.key,
555
- sha: createHash("sha256").update(spec.body).digest("hex"),
556
- size: spec.body.byteLength,
557
- ...spec.precondition,
558
- })),
559
- });
560
- }
561
-
562
- /**
563
- * One presign call covering every spec, then parallel presigned PUTs.
564
- * Outcomes are per spec: a 412 ("conflict") or a failed PUT for one key
565
- * never disturbs its batch siblings. Only the presign itself is shared —
566
- * a presign error throws and fails the whole attempt.
567
- */
568
- async function attemptUploads(files: FilesTarget, specs: UploadSpec[]): Promise<UploadOutcome[]> {
569
- const presigned = await presignUploads(files, specs);
570
- return await Promise.all(
571
- specs.map(async (spec, index): Promise<UploadOutcome> => {
572
- // Match by path, falling back to position for servers that answer
573
- // in request order without echoing the path.
574
- const instruction =
575
- presigned.files?.find(file => file.path === spec.key) ?? presigned.files?.[index];
576
- if (instruction?.url === undefined) {
577
- // Files past the single-part threshold come back with a multipart
578
- // plan instead of a url; this backend does not implement multipart.
579
- return {
580
- status: "error",
581
- error: new Error(
582
- `cursorHostedFiles: no single-part upload url for "${spec.key}" (too large?)`,
583
- ),
584
- };
585
- }
586
- try {
587
- const response = await resolveCursorApiFetch(options)(
588
- assertPresignedUrlSafe(options, instruction.url),
589
- {
590
- method: "PUT",
591
- headers: instruction.headers ?? {},
592
- // A view over bytes is a valid fetch body; the DOM lib types
593
- // are narrower than the runtime, and copying into a Blob would
594
- // double the memory for every upload.
595
- body: spec.body as unknown as BodyInit,
596
- // A redirect would re-send the body somewhere the presign did
597
- // not authorize.
598
- redirect: "manual",
599
- },
600
- );
601
- if (response.status === 412) {
602
- observedEtags.delete(spec.key);
603
- return { status: "conflict" };
604
- }
605
- if (!response.ok) {
606
- return {
607
- status: "error",
608
- error: new Error(
609
- `cursorHostedFiles: upload of "${spec.key}" failed (HTTP ${response.status})`,
610
- ),
611
- };
612
- }
613
- const etag = normalizeEtag(response.headers.get("etag"));
614
- if (etag !== undefined) {
615
- rememberEtag(spec.key, etag);
616
- }
617
- return { status: "ok", result: etag === undefined ? {} : { etag } };
618
- } catch (error) {
619
- return { status: "error", error };
620
- }
621
- }),
622
- );
623
- }
624
-
625
- /**
626
- * The full single-key write flow: precondition resolution, one shared
627
- * (already-performed) first attempt outcome, and the conflict handling
628
- * `put` promises — including the one statKey-and-retry when an optimistic
629
- * create loses to an existing object.
630
- */
631
- async function settlePut(
632
- files: FilesTarget,
633
- entry: {
634
- key: string;
635
- body: Uint8Array;
636
- options?: { ifMatch?: string | null };
637
- cachedEtagUsed?: boolean;
638
- staleEtag?: string;
639
- },
640
- first: UploadOutcome,
641
- ): Promise<FileSinkPutResult> {
642
- if (first.status === "error") {
643
- throw first.error;
644
- }
645
- if (first.status === "ok") {
646
- return first.result;
647
- }
648
- // Conflict. Explicit `ifMatch` (etag or create-only) and cached-etag
649
- // writes are single-attempt: the lost precondition is the caller's.
650
- const requested = entry.options?.ifMatch;
651
- if (requested !== undefined) {
652
- throw new FileConflictError(entry.key, {
653
- storedEtag: requested ?? undefined,
654
- });
655
- }
656
- if (entry.cachedEtagUsed === true) {
657
- // Someone else wrote between our etag observation and this PUT.
658
- throw new FileConflictError(entry.key, { storedEtag: entry.staleEtag });
659
- }
660
- // The optimistic create lost: learn the current etag once and replace.
661
- const found = await statKey(entry.key);
662
- if (found?.etag === undefined) {
663
- // Existed a moment ago (the create lost) but stat sees nothing: a
664
- // delete raced this write. Surface the conflict; a retry from the
665
- // caller lands on the fresh create-only path.
666
- throw new FileConflictError(entry.key, {});
667
- }
668
- const [replaced] = await attemptUploads(files, [
669
- { key: entry.key, body: entry.body, precondition: { etag: found.etag } },
670
- ]);
671
- if (replaced === undefined || replaced.status === "conflict") {
672
- throw new FileConflictError(entry.key, { storedEtag: found?.etag });
673
- }
674
- if (replaced.status === "error") {
675
- throw replaced.error;
676
- }
677
- return replaced.result;
678
- }
679
-
680
- /**
681
- * Precondition for a write, and whether it came from the etag cache (a
682
- * cached-etag conflict is terminal; an optimistic-create conflict earns
683
- * one stat-and-retry).
684
- */
685
- function preconditionFor(entry: { key: string; options?: { ifMatch?: string | null } }): {
686
- precondition: { etag: string } | { new: true };
687
- cachedEtagUsed: boolean;
688
- staleEtag?: string;
689
- } {
690
- const requested = entry.options?.ifMatch;
691
- // The store rejects unconditional writes, so every put carries a
692
- // precondition. `null` is create-only and an explicit etag is a caller
693
- // assertion.
694
- if (requested !== undefined) {
695
- return {
696
- precondition: requested === null ? { new: true } : { etag: requested },
697
- cachedEtagUsed: false,
698
- };
699
- }
700
- // Unconditional write with a known etag: replace what we saw last.
701
- const cached = observedEtags.get(entry.key);
702
- if (cached !== undefined) {
703
- return {
704
- precondition: { etag: cached },
705
- cachedEtagUsed: true,
706
- staleEtag: cached,
707
- };
708
- }
709
- // Unconditional write to a key this process has never observed:
710
- // create-only first, learning the current etag only on conflict. Most
711
- // such writes are brand-new keys (session-event chunks mint a fresh key
712
- // per flush), and the old stat-before-every-put shape made the
713
- // precondition lookup the single largest source of `agent-serve-files`
714
- // list traffic — every fresh pod re-listed a directory per key before
715
- // its first write.
716
- return { precondition: { new: true }, cachedEtagUsed: false };
717
- }
718
-
719
- return {
720
- // No local copy: bytes live in the store, so `materializedPath()` is
721
- // `undefined` and the harness reaches files through `host.files`.
722
- capabilities: { etags: true },
723
-
724
- async put(key, body, putOptions): Promise<FileSinkPutResult> {
725
- return await withFiles(async files => {
726
- const entry = { key, body, options: putOptions };
727
- const resolved = preconditionFor(entry);
728
- const [first] = await attemptUploads(files, [
729
- { key, body, precondition: resolved.precondition },
730
- ]);
731
- if (first === undefined) {
732
- throw new Error(`cursorHostedFiles: upload of "${key}" vanished`);
733
- }
734
- return await settlePut(
735
- files,
736
- {
737
- ...entry,
738
- cachedEtagUsed: resolved.cachedEtagUsed,
739
- staleEtag: resolved.staleEtag,
740
- },
741
- first,
742
- );
743
- });
744
- },
745
-
746
- async putMany(entries) {
747
- if (entries.length === 0) {
748
- return [];
749
- }
750
- return await withFiles(async files => {
751
- const resolved = entries.map(entry => ({
752
- entry,
753
- ...preconditionFor(entry),
754
- }));
755
- let firsts: UploadOutcome[];
756
- try {
757
- // One presign call for the whole batch — the point of putMany.
758
- firsts = await attemptUploads(
759
- files,
760
- resolved.map(({ entry, precondition }) => ({
761
- key: entry.key,
762
- body: entry.body,
763
- precondition,
764
- })),
765
- );
766
- } catch (error) {
767
- // The shared presign failed: every entry failed the same way.
768
- return entries.map(() => ({
769
- status: "rejected" as const,
770
- reason: error,
771
- }));
772
- }
773
- return await Promise.all(
774
- resolved.map(
775
- async (
776
- { entry, cachedEtagUsed, staleEtag },
777
- index,
778
- ): Promise<PromiseSettledResult<FileSinkPutResult>> => {
779
- const first = firsts[index];
780
- if (first === undefined) {
781
- return {
782
- status: "rejected",
783
- reason: new Error(`cursorHostedFiles: upload of "${entry.key}" vanished`),
784
- };
785
- }
786
- try {
787
- return {
788
- status: "fulfilled",
789
- value: await settlePut(files, { ...entry, cachedEtagUsed, staleEtag }, first),
790
- };
791
- } catch (error) {
792
- return { status: "rejected", reason: error };
793
- }
794
- },
795
- ),
796
- );
797
- });
798
- },
799
-
800
- async get(key): Promise<FileSinkBody | undefined> {
801
- return await withFiles(async files => {
802
- // Presigning a read of a path the store has no live object for — never
803
- // written, or deleted and now holding a tombstone — is a 404 on `/v0`
804
- // and an absent entry on the flush wire. Either is absence, which
805
- // `read()` reports as undefined, not a failure.
806
- let presigned: ApiPresignResponse;
807
- try {
808
- presigned =
809
- files.wire === "flush"
810
- ? await flushPost<ApiPresignResponse>(options, "/read", {
811
- store: files.store,
812
- paths: [key],
813
- })
814
- : await storePost<ApiPresignResponse>(options, files.path("/download"), {
815
- paths: [key],
816
- });
817
- } catch (error) {
818
- if (error instanceof StoreApiError && error.status === 404) {
819
- return undefined;
820
- }
821
- throw error;
822
- }
823
- const url = presigned.files?.[0]?.url;
824
- if (url === undefined) {
825
- return undefined;
826
- }
827
- return await downloadPresigned(key, url);
828
- });
829
- },
830
-
831
- async getMany(keys): Promise<Array<FileSinkBody | undefined>> {
832
- if (keys.length === 0) {
833
- return [];
834
- }
835
- const bodies = await withFiles(async files => {
836
- if (files.wire !== "flush") {
837
- // `/v0` presigns 404 the whole batch when any path is absent, so a
838
- // batched call cannot tell one missing key from a failed request.
839
- // Bounded per-key gets keep the `/v0` absence semantics exact; the
840
- // batch win belongs to the flush wire, whose contract is "an absent
841
- // path is an absent entry" — and whose per-deployment rate limiter
842
- // is the reason to spend fewer requests in the first place.
843
- return await mapBounded([...keys], GET_MANY_DOWNLOAD_CONCURRENCY, async key => {
844
- const presigned = await storePost<ApiPresignResponse>(
845
- options,
846
- files.path("/download"),
847
- { paths: [key] },
848
- ).catch((error: unknown) => {
849
- if (error instanceof StoreApiError && error.status === 404) {
850
- return undefined;
851
- }
852
- throw error;
853
- });
854
- const url = presigned?.files?.[0]?.url;
855
- if (url === undefined) {
856
- return undefined;
857
- }
858
- return await downloadPresigned(key, url);
859
- });
860
- }
861
- const urls = new Map<string, string>();
862
- for (let from = 0; from < keys.length; from += READ_PRESIGN_BATCH_MAX) {
863
- const chunk = keys.slice(from, from + READ_PRESIGN_BATCH_MAX);
864
- let presigned: ApiPresignResponse;
865
- try {
866
- presigned = await flushPost<ApiPresignResponse>(options, "/read", {
867
- store: files.store,
868
- paths: chunk,
869
- });
870
- } catch (error) {
871
- if (error instanceof StoreApiError && error.status === 404) {
872
- // The read route never 404s per path (absent paths are
873
- // omitted entries); a 404 is the store itself — not
874
- // provisioned, or deleted under us — which get() reads as
875
- // absence for every key.
876
- return keys.map(() => undefined);
877
- }
878
- throw error;
879
- }
880
- for (const entry of presigned.files ?? []) {
881
- if (entry.path !== undefined && entry.url !== undefined) {
882
- urls.set(entry.path, entry.url);
883
- }
884
- }
885
- }
886
- return await mapBounded([...keys], GET_MANY_DOWNLOAD_CONCURRENCY, async key => {
887
- const url = urls.get(key);
888
- if (url !== undefined) {
889
- return await downloadPresigned(key, url);
890
- }
891
- // A miss in the batch is NOT yet a definitive absence: the
892
- // server echoes each path *normalized* (trimmed, backslashes
893
- // folded, empty/"." segments dropped), so a key whose
894
- // normalized form differs from what was sent never matches the
895
- // echo even though its object exists. `get()` is immune (it
896
- // reads the response positionally), so settle every batch miss
897
- // through the single-path read — keeping getMany's absence
898
- // semantics byte-identical to get()'s. Genuine absences pay one
899
- // extra request; every batch caller reads keys it just listed
900
- // or was told exist, so real misses are rare (delete races,
901
- // store loss).
902
- const presigned = await flushPost<ApiPresignResponse>(options, "/read", {
903
- store: files.store,
904
- paths: [key],
905
- }).catch((error: unknown) => {
906
- // Store-level 404 (deleted between the batch and this read):
907
- // absence, same as get().
908
- if (error instanceof StoreApiError && error.status === 404) {
909
- return undefined;
910
- }
911
- throw error;
912
- });
913
- const singleUrl = presigned?.files?.[0]?.url;
914
- if (singleUrl === undefined) {
915
- return undefined;
916
- }
917
- return await downloadPresigned(key, singleUrl);
918
- });
919
- });
920
- return bodies;
921
- },
922
-
923
- async delete(key): Promise<void> {
924
- const handled = await withFiles(async files => {
925
- if (files.wire !== "flush") {
926
- return false;
927
- }
928
- // With an observed etag the flush delete is If-Match on that version.
929
- // Without one, the server tombstones whatever live object occupies
930
- // the key (no directory walk here) and an absent key settles as
931
- // `already_deleted`.
932
- const cached = observedEtags.get(key);
933
- const response = await flushPost<FlushWireResponse>(options, "/flush", {
934
- store: files.store,
935
- ops: [
936
- {
937
- op: "delete",
938
- path: key,
939
- ...(cached === undefined ? {} : { etag: cached }),
940
- },
941
- ],
942
- });
943
- const outcome = response.ops?.[0];
944
- if (outcome?.status === "conflict") {
945
- observedEtags.delete(key);
946
- throw new FileConflictError(key, {
947
- ...(outcome.etag === undefined
948
- ? {}
949
- : { storedEtag: normalizeEtag(outcome.etag) ?? outcome.etag }),
950
- });
951
- }
952
- observedEtags.delete(key);
953
- return true;
954
- });
955
- if (handled) {
956
- return;
957
- }
958
- if (observedEtags.get(key) === undefined) {
959
- // Deletes are conditional too, so learn the current etag first. A key
960
- // we have never seen is already absent as far as the caller cares.
961
- const found = await statKey(key);
962
- if (found?.etag === undefined) {
963
- return;
964
- }
965
- }
966
- const etag = observedEtags.get(key);
967
- if (etag === undefined) {
968
- return;
969
- }
970
- await withFiles(async files => {
971
- if (files.wire === "flush") {
972
- // The protocol pin appeared between the two withFiles calls; the
973
- // cached etag is enough for the conditional flush delete.
974
- const response = await flushPost<FlushWireResponse>(options, "/flush", {
975
- store: files.store,
976
- ops: [{ op: "delete", path: key, etag }],
977
- });
978
- const outcome = response.ops?.[0];
979
- if (outcome?.status === "conflict") {
980
- observedEtags.delete(key);
981
- throw new FileConflictError(key, {
982
- ...(outcome.etag === undefined
983
- ? {}
984
- : { storedEtag: normalizeEtag(outcome.etag) ?? outcome.etag }),
985
- });
986
- }
987
- return;
988
- }
989
- const raw = await storePost<ApiDeleteResponse>(options, files.path("/delete"), {
990
- files: [{ path: key, etag }],
991
- });
992
- // The API answers 200 with a per-file status, so a lost race would look
993
- // like success unless the outcome is read.
994
- const outcome = (raw.files ?? []).find(entry => entry.path === key);
995
- if (outcome?.status === "conflict") {
996
- observedEtags.delete(key);
997
- throw new FileConflictError(key, {
998
- ...(outcome.etag === undefined
999
- ? {}
1000
- : { storedEtag: normalizeEtag(outcome.etag) ?? outcome.etag }),
1001
- });
1002
- }
1003
- });
1004
- observedEtags.delete(key);
1005
- },
1006
-
1007
- list: listEntries,
1008
-
1009
- async stat(key): Promise<FileSinkEntry | undefined> {
1010
- return await statKey(key);
1011
- },
1012
- };
1013
- }
1014
-
1015
- /**
1016
- * Refuse a presigned URL that does not target this backend's agent-stores
1017
- * bucket.
1018
- *
1019
- * The URL arrives from BCS over TLS, so this is defense in depth rather than
1020
- * a live hole — but the engine pod can reach link-local metadata and other
1021
- * allowlisted egress hosts, so a BCS bug or compromise returning a foreign
1022
- * address would otherwise steer file bytes somewhere the caller never chose.
1023
- * Same pin as the sibling sync client: production backends allow only the
1024
- * agent-stores bucket hosts, playground its bucket, and a local dev backend
1025
- * additionally admits loopback http for LocalStack.
1026
- *
1027
- * `CURSOR_HOSTED_FILES_DEV_PRESIGN_HOSTS` (comma-separated hostnames) admits
1028
- * extra https hosts. A laptop backend hosting agents on a shared cluster
1029
- * fronts its LocalStack with a public tunnel and exports this in the pod's
1030
- * start script; production start scripts never set it. Read once at module
1031
- * load, so authored agent code sharing this process cannot widen the list
1032
- * by mutating `process.env` later.
1033
- */
1034
- function assertPresignedUrlSafe(options: CursorHostedFilesOptions, raw: string): string {
1035
- let url: URL;
1036
- try {
1037
- url = new URL(raw);
1038
- } catch {
1039
- throw new Error("cursorHostedFiles: presigned URL is not a valid URL");
1040
- }
1041
- if (
1042
- url.protocol === "https:" &&
1043
- url.username === "" &&
1044
- url.password === "" &&
1045
- DEV_PRESIGN_HOSTS.has(url.hostname.toLowerCase())
1046
- ) {
1047
- return url.toString();
1048
- }
1049
- try {
1050
- createAgentStorePresignedUrlValidatorForBackend(resolveCursorApiBaseUrl(options))(url);
1051
- } catch (error) {
1052
- throw new Error(
1053
- `cursorHostedFiles: refused presigned URL: ${
1054
- error instanceof Error ? error.message : String(error)
1055
- }`,
1056
- );
1057
- }
1058
- return url.toString();
1059
- }
1060
-
1061
- /**
1062
- * The file store this engine's deployment owns. Every request names it, so
1063
- * the server resolves the store row and this process never handles a store id.
1064
- *
1065
- * Fails loudly rather than defaulting: with no source id there is no store to
1066
- * address, and writing somewhere else silently is worse than not writing.
1067
- */
1068
- function resolveStoreSourceId(options: CursorHostedFilesOptions): string {
1069
- const id = (
1070
- options.resolveStoreSourceId?.() ??
1071
- options.storeSourceId ??
1072
- resolveDeploymentStoreSourceIdSync(process.env)
1073
- )?.trim();
1074
- if (id === undefined || id === "") {
1075
- throw new Error(
1076
- `cursorHostedFiles requires ${AGENT_SERVE_STORE_SOURCE_ID_ENV_VAR} (set on Cursor-managed deployments)`,
1077
- );
1078
- }
1079
- return id;
1080
- }
1081
-
1082
- const DEV_PRESIGN_HOSTS: ReadonlySet<string> = new Set(
1083
- (process.env.CURSOR_HOSTED_FILES_DEV_PRESIGN_HOSTS ?? "")
1084
- .split(",")
1085
- .map(host => host.trim().toLowerCase())
1086
- .filter(host => host.length > 0),
1087
- );
1088
-
1089
- /**
1090
- * Public agent-serve-files API call. The pod credential is the whole authorization:
1091
- * the API forwards this caller's identity to the store service, which checks it
1092
- * against the store's live owner. There is no second, narrower credential.
1093
- */
1094
- async function storePost<T>(
1095
- options: CursorHostedFilesOptions,
1096
- path: string,
1097
- body: Record<string, unknown>,
1098
- ): Promise<T> {
1099
- return await postJson<T>(
1100
- options,
1101
- `${resolveCursorApiBaseUrl(options)}${AGENT_SERVE_FILES_PATH}${path}`,
1102
- body,
1103
- );
1104
- }
1105
-
1106
- async function storeGet<T>(options: CursorHostedFilesOptions, path: string): Promise<T> {
1107
- return await sendJson<T>(
1108
- options,
1109
- `${resolveCursorApiBaseUrl(options)}${AGENT_SERVE_FILES_PATH}${path}`,
1110
- "GET",
1111
- );
1112
- }
1113
-
1114
- /**
1115
- * Factory store API call — same base URL and pod credential as
1116
- * {@link storePost}, different path family. The server resolves the
1117
- * deployment (and the addressed store role) from the credential. The
1118
- * domain-call lane one path family deeper is NOT here: it belongs to the
1119
- * store API client (`../internal/cursor/store-api-client.ts`).
1120
- */
1121
- async function flushPost<T>(
1122
- options: CursorHostedFilesOptions,
1123
- path: string,
1124
- body: Record<string, unknown>,
1125
- ): Promise<T> {
1126
- return await postJson<T>(
1127
- options,
1128
- `${resolveCursorApiBaseUrl(options)}${AGENTSDK_STORE_PATH}${path}`,
1129
- body,
1130
- );
1131
- }
1132
-
1133
- async function postJson<T>(
1134
- options: CursorHostedFilesOptions,
1135
- url: string,
1136
- body: Record<string, unknown>,
1137
- ): Promise<T> {
1138
- return await sendJson<T>(options, url, "POST", body);
1139
- }
1140
-
1141
- async function sendJson<T>(
1142
- options: CursorHostedFilesOptions,
1143
- url: string,
1144
- method: "GET" | "POST",
1145
- body?: Record<string, unknown>,
1146
- ): Promise<T> {
1147
- const response = await resolveCursorApiFetch(options)(url, {
1148
- method,
1149
- headers: {
1150
- Authorization: `Bearer ${resolveCursorApiKey(options)}`,
1151
- ...(body === undefined ? {} : { "Content-Type": "application/json" }),
1152
- },
1153
- ...(body === undefined ? {} : { body: JSON.stringify(body) }),
1154
- });
1155
- const text = await response.text();
1156
- let parsed: unknown;
1157
- try {
1158
- parsed = text.length === 0 ? {} : JSON.parse(text);
1159
- } catch {
1160
- throw new Error(`cursorHostedFiles ${url} returned non-JSON (${response.status})`);
1161
- }
1162
- if (!response.ok) {
1163
- throw new StoreApiError(
1164
- response.status,
1165
- `cursorHostedFiles ${url}: ${describeError(parsed, response.status)}`,
1166
- );
1167
- }
1168
- return parsed as T;
1169
- }
1170
-
1171
- /**
1172
- * A non-2xx answer from the files API, carrying the status so a caller can
1173
- * tell "absent" apart from a failure it must not swallow.
1174
- */
1175
- class StoreApiError extends Error {
1176
- constructor(
1177
- readonly status: number,
1178
- message: string,
1179
- ) {
1180
- super(message);
1181
- this.name = "StoreApiError";
1182
- }
1183
- }
1184
-
1185
- function describeError(parsed: unknown, status: number): string {
1186
- if (
1187
- parsed !== null &&
1188
- typeof parsed === "object" &&
1189
- "error" in parsed &&
1190
- typeof (parsed as { error: unknown }).error === "string"
1191
- ) {
1192
- return (parsed as { error: string }).error;
1193
- }
1194
- if (
1195
- parsed !== null &&
1196
- typeof parsed === "object" &&
1197
- "message" in parsed &&
1198
- typeof (parsed as { message: unknown }).message === "string"
1199
- ) {
1200
- return (parsed as { message: string }).message;
1201
- }
1202
- return `HTTP ${status}`;
1203
- }
1204
-
1205
- /** S3 returns quoted etags; the store compares them unquoted. */
1206
- function normalizeEtag(raw: string | null): string | undefined {
1207
- if (raw === null) {
1208
- return undefined;
1209
- }
1210
- const trimmed = raw.trim().replace(/^"+|"+$/g, "");
1211
- return trimmed === "" ? undefined : trimmed;
1212
- }