@cursor/july 0.1.107 → 0.1.109

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 (523) hide show
  1. package/AGENTS.md +2 -6
  2. package/README.md +13 -11
  3. package/dist/bin/agent-serve.js +30 -8
  4. package/dist/channels/bitbucket/api.d.ts +41 -0
  5. package/dist/channels/bitbucket/api.d.ts.map +1 -1
  6. package/dist/channels/bitbucket/api.js +260 -0
  7. package/dist/channels/bitbucket/binding.d.ts +4 -0
  8. package/dist/channels/bitbucket/binding.d.ts.map +1 -1
  9. package/dist/channels/bitbucket/binding.js +16 -0
  10. package/dist/channels/bitbucket/index.d.ts +1 -1
  11. package/dist/channels/bitbucket/index.d.ts.map +1 -1
  12. package/dist/channels/bitbucket/index.js +1 -1
  13. package/dist/channels/github/github-channel.js +8 -5
  14. package/dist/channels/github/types.d.ts +5 -1
  15. package/dist/channels/github/types.d.ts.map +1 -1
  16. package/dist/channels/gitlab/api.d.ts +27 -0
  17. package/dist/channels/gitlab/api.d.ts.map +1 -1
  18. package/dist/channels/gitlab/api.js +88 -0
  19. package/dist/channels/gitlab/binding.d.ts +5 -0
  20. package/dist/channels/gitlab/binding.d.ts.map +1 -1
  21. package/dist/channels/gitlab/binding.js +10 -0
  22. package/dist/channels/gitlab/index.d.ts +1 -1
  23. package/dist/channels/gitlab/index.d.ts.map +1 -1
  24. package/dist/channels/gitlab/index.js +1 -1
  25. package/dist/channels/origin/origin-channel.d.ts.map +1 -1
  26. package/dist/channels/origin/origin-channel.js +27 -13
  27. package/dist/channels/origin/types.d.ts +5 -1
  28. package/dist/channels/origin/types.d.ts.map +1 -1
  29. package/dist/channels/slack/dispatch.d.ts +10 -0
  30. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  31. package/dist/channels/slack/dispatch.js +20 -3
  32. package/dist/channels/slack/slack-channel.d.ts +12 -5
  33. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  34. package/dist/channels/slack/slack-channel.js +59 -8
  35. package/dist/channels.d.ts +45 -0
  36. package/dist/channels.d.ts.map +1 -1
  37. package/dist/channels.js +106 -7
  38. package/dist/connections.d.ts +2 -1
  39. package/dist/connections.d.ts.map +1 -1
  40. package/dist/connections.js +2 -1
  41. package/dist/docs/404.html +2 -2
  42. package/dist/docs/assets/{app.CtuckIiL.js → app.Cr-wVbnB.js} +1 -1
  43. package/dist/docs/assets/{building-with-agents.md.CUSWxlP_.js → building-with-agents.md.D0KbSkJn.js} +2 -2
  44. package/dist/docs/assets/{building-with-agents.md.CUSWxlP_.lean.js → building-with-agents.md.D0KbSkJn.lean.js} +1 -1
  45. package/dist/docs/assets/chunks/@localSearchIndexroot.CFVQ4S17.js +1 -0
  46. package/dist/docs/assets/chunks/{VPLocalSearchBox.Bkkhnu8K.js → VPLocalSearchBox.CVQERt56.js} +1 -1
  47. package/dist/docs/assets/chunks/{theme.DhpIe0Pa.js → theme.Dnsd3XOn.js} +2 -2
  48. package/dist/docs/assets/concepts.md.B4o63Gul.js +1 -0
  49. package/dist/docs/assets/{deployment.md.MFPKqsqI.js → deployment.md.JenwuCCB.js} +4 -4
  50. package/dist/docs/assets/{deployment.md.MFPKqsqI.lean.js → deployment.md.JenwuCCB.lean.js} +1 -1
  51. package/dist/docs/assets/evals.md.C7JLjoEP.js +211 -0
  52. package/dist/docs/assets/evals.md.C7JLjoEP.lean.js +1 -0
  53. package/dist/docs/assets/guides_bitbucket.md.DZPzmUjU.js +10 -0
  54. package/dist/docs/assets/guides_bitbucket.md.DZPzmUjU.lean.js +1 -0
  55. package/dist/docs/assets/guides_cloud-agents.md.BsloyHdY.js +9 -0
  56. package/dist/docs/assets/{guides_cloud-agents.md.DS8RIjwx.lean.js → guides_cloud-agents.md.BsloyHdY.lean.js} +1 -1
  57. package/dist/docs/assets/{guides_github.md.c0gdGJ-D.js → guides_github.md.TZaTZlfz.js} +13 -3
  58. package/dist/docs/assets/{guides_github.md.c0gdGJ-D.lean.js → guides_github.md.TZaTZlfz.lean.js} +1 -1
  59. package/dist/docs/assets/guides_gitlab.md.BmqwQfdG.js +14 -0
  60. package/dist/docs/assets/guides_gitlab.md.BmqwQfdG.lean.js +1 -0
  61. package/dist/docs/assets/{guides_grokbot-agents.md.DVqdhBKb.js → guides_grokbot-agents.md.DyV-WArv.js} +1 -1
  62. package/dist/docs/assets/guides_improve.md.BGVk32bK.js +14 -0
  63. package/dist/docs/assets/guides_improve.md.BGVk32bK.lean.js +1 -0
  64. package/dist/docs/assets/{guides_slack.md.9oHPye9o.js → guides_slack.md.D4RVMM4G.js} +3 -3
  65. package/dist/docs/assets/{guides_slack.md.9oHPye9o.lean.js → guides_slack.md.D4RVMM4G.lean.js} +1 -1
  66. package/dist/docs/assets/{guides_webhooks.md.DKdA43Qm.js → guides_webhooks.md.CJK484ex.js} +2 -2
  67. package/dist/docs/assets/{hillclimbing.md.CpTGTCle.js → hillclimbing.md.BOiVo1tf.js} +1 -1
  68. package/dist/docs/assets/index.md.DD9Q2XuJ.js +5 -0
  69. package/dist/docs/assets/{index.md.BW_6tOgR.lean.js → index.md.DD9Q2XuJ.lean.js} +1 -1
  70. package/dist/docs/assets/{reference_agent-config.md.CHNpiyp4.js → reference_agent-config.md.CvoL6pof.js} +1 -1
  71. package/dist/docs/assets/{reference_channels.md.D-qTqwcq.js → reference_channels.md.CAo-iK4j.js} +2 -2
  72. package/dist/docs/assets/{reference_channels.md.D-qTqwcq.lean.js → reference_channels.md.CAo-iK4j.lean.js} +1 -1
  73. package/dist/docs/assets/{reference_cli.md.Dm67hd2D.js → reference_cli.md.Deg7849l.js} +7 -7
  74. package/dist/docs/assets/{reference_cli.md.Dm67hd2D.lean.js → reference_cli.md.Deg7849l.lean.js} +1 -1
  75. package/dist/docs/assets/{reference_connections.md.Di6jJAXF.js → reference_connections.md.BojkC6c5.js} +1 -1
  76. package/dist/docs/assets/{reference_extensions.md.CGmMLblt.js → reference_extensions.md.ZAVUyuEX.js} +3 -3
  77. package/dist/docs/assets/{reference_hooks.md.Ddt5DdgJ.js → reference_hooks.md.BlM_bOg6.js} +3 -3
  78. package/dist/docs/assets/{reference_hooks.md.Ddt5DdgJ.lean.js → reference_hooks.md.BlM_bOg6.lean.js} +1 -1
  79. package/dist/docs/assets/{reference_http-api.md.oySXBO8o.js → reference_http-api.md.BwaCo-VO.js} +1 -1
  80. package/dist/docs/assets/{reference_playground.md.4myJPxrf.js → reference_playground.md.DLnoaczX.js} +1 -1
  81. package/dist/docs/assets/{reference_playground.md.4myJPxrf.lean.js → reference_playground.md.DLnoaczX.lean.js} +1 -1
  82. package/dist/docs/assets/{reference_project-layout.md.DuBu9a96.js → reference_project-layout.md.BEU8MtQV.js} +3 -3
  83. package/dist/docs/assets/{reference_project-layout.md.DuBu9a96.lean.js → reference_project-layout.md.BEU8MtQV.lean.js} +1 -1
  84. package/dist/docs/assets/reference_sessions.md.CyXV1MUw.js +1 -0
  85. package/dist/docs/assets/{scaffolding-agents.md.em43xlY1.js → scaffolding-agents.md.Kctn3OVb.js} +1 -1
  86. package/dist/docs/assets/{skills_create-agent.md.BVoWPcan.js → skills_create-agent.md.Q3h6Je-e.js} +1 -1
  87. package/dist/docs/assets/{skills_debug.md.CDbPhHfg.js → skills_debug.md.CVjCXMFF.js} +1 -1
  88. package/dist/docs/assets/{skills_debug.md.CDbPhHfg.lean.js → skills_debug.md.CVjCXMFF.lean.js} +1 -1
  89. package/dist/docs/assets/skills_deploy.md.CWqi_ZxW.js +35 -0
  90. package/dist/docs/assets/skills_deploy.md.CWqi_ZxW.lean.js +1 -0
  91. package/dist/docs/assets/skills_evals.md.DFxYPErF.js +25 -0
  92. package/dist/docs/assets/skills_evals.md.DFxYPErF.lean.js +1 -0
  93. package/dist/docs/assets/skills_framework-map.md.BxLSOhSY.js +1 -0
  94. package/dist/docs/assets/{skills_framework-map.md.haibFyoB.lean.js → skills_framework-map.md.BxLSOhSY.lean.js} +1 -1
  95. package/dist/docs/assets/{skills_github.md.D0JahM8c.js → skills_github.md.hgFX_oKY.js} +1 -1
  96. package/dist/docs/assets/skills_index.md.DL7EHaQ-.js +1 -0
  97. package/dist/docs/assets/skills_index.md.DL7EHaQ-.lean.js +1 -0
  98. package/dist/docs/assets/{storage.md.BOHeqk2M.js → storage.md.BUrhJ-Zz.js} +4 -4
  99. package/dist/docs/assets/{storage.md.BOHeqk2M.lean.js → storage.md.BUrhJ-Zz.lean.js} +1 -1
  100. package/dist/docs/assets/troubleshooting.md.CYEAO9bM.js +1 -0
  101. package/dist/docs/building-with-agents.html +5 -5
  102. package/dist/docs/building-with-agents.md +3 -2
  103. package/dist/docs/concepts.html +5 -5
  104. package/dist/docs/concepts.md +2 -4
  105. package/dist/docs/deployment.html +7 -7
  106. package/dist/docs/deployment.md +7 -2
  107. package/dist/docs/evals.html +161 -35
  108. package/dist/docs/evals.md +612 -296
  109. package/dist/docs/guides/agent-to-agent.html +4 -4
  110. package/dist/docs/guides/bitbucket.html +36 -0
  111. package/dist/docs/guides/bitbucket.md +84 -0
  112. package/dist/docs/guides/cloud-agents.html +6 -6
  113. package/dist/docs/guides/cloud-agents.md +4 -3
  114. package/dist/docs/guides/convert-automation.html +4 -4
  115. package/dist/docs/guides/github.html +17 -7
  116. package/dist/docs/guides/github.md +33 -1
  117. package/dist/docs/guides/gitlab.html +40 -0
  118. package/dist/docs/guides/gitlab.md +92 -0
  119. package/dist/docs/guides/grokbot-agents.html +6 -6
  120. package/dist/docs/guides/grokbot-agents.md +2 -2
  121. package/dist/docs/guides/human-in-the-loop.html +4 -4
  122. package/dist/docs/guides/improve.html +40 -0
  123. package/dist/docs/guides/improve.md +91 -0
  124. package/dist/docs/guides/mcp-oauth.html +4 -4
  125. package/dist/docs/guides/opentelemetry.html +5 -5
  126. package/dist/docs/guides/slack.html +7 -7
  127. package/dist/docs/guides/slack.md +2 -1
  128. package/dist/docs/guides/webhooks.html +6 -6
  129. package/dist/docs/guides/webhooks.md +4 -2
  130. package/dist/docs/hashmap.json +1 -1
  131. package/dist/docs/hillclimbing.html +6 -6
  132. package/dist/docs/hillclimbing.md +2 -2
  133. package/dist/docs/index.html +6 -6
  134. package/dist/docs/index.md +14 -6
  135. package/dist/docs/llms-full.txt +1274 -814
  136. package/dist/docs/llms.txt +7 -5
  137. package/dist/docs/quickstart.html +4 -4
  138. package/dist/docs/reference/agent-config.html +6 -6
  139. package/dist/docs/reference/agent-config.md +5 -2
  140. package/dist/docs/reference/artifacts.html +4 -4
  141. package/dist/docs/reference/channels.html +6 -6
  142. package/dist/docs/reference/channels.md +16 -3
  143. package/dist/docs/reference/cli.html +11 -11
  144. package/dist/docs/reference/cli.md +24 -15
  145. package/dist/docs/reference/connections.html +6 -6
  146. package/dist/docs/reference/connections.md +2 -1
  147. package/dist/docs/reference/extensions.html +8 -8
  148. package/dist/docs/reference/extensions.md +2 -3
  149. package/dist/docs/reference/hooks.html +7 -7
  150. package/dist/docs/reference/hooks.md +9 -12
  151. package/dist/docs/reference/http-api.html +6 -6
  152. package/dist/docs/reference/http-api.md +2 -3
  153. package/dist/docs/reference/instructions.html +4 -4
  154. package/dist/docs/reference/playground.html +5 -5
  155. package/dist/docs/reference/playground.md +0 -4
  156. package/dist/docs/reference/project-layout.html +7 -7
  157. package/dist/docs/reference/project-layout.md +1 -8
  158. package/dist/docs/reference/prompt.html +4 -4
  159. package/dist/docs/reference/result.html +4 -4
  160. package/dist/docs/reference/schedules.html +4 -4
  161. package/dist/docs/reference/sessions.html +5 -5
  162. package/dist/docs/reference/sessions.md +1 -2
  163. package/dist/docs/reference/skills.html +4 -4
  164. package/dist/docs/reference/subagents.html +4 -4
  165. package/dist/docs/reference/tools.html +4 -4
  166. package/dist/docs/scaffolding-agents.html +5 -5
  167. package/dist/docs/scaffolding-agents.md +2 -1
  168. package/dist/docs/skills/create-agent.html +6 -6
  169. package/dist/docs/skills/create-agent.md +1 -1
  170. package/dist/docs/skills/debug.html +5 -5
  171. package/dist/docs/skills/debug.md +1 -1
  172. package/dist/docs/skills/deploy.html +61 -0
  173. package/dist/docs/skills/deploy.md +161 -0
  174. package/dist/docs/skills/evals.html +8 -8
  175. package/dist/docs/skills/evals.md +44 -6
  176. package/dist/docs/skills/framework-map.html +5 -5
  177. package/dist/docs/skills/framework-map.md +5 -4
  178. package/dist/docs/skills/github.html +6 -6
  179. package/dist/docs/skills/github.md +1 -1
  180. package/dist/docs/skills/hillclimb.html +4 -4
  181. package/dist/docs/skills/index.html +6 -6
  182. package/dist/docs/skills/index.md +1 -1
  183. package/dist/docs/skills/mcp-auth.html +4 -4
  184. package/dist/docs/skills/otel.html +4 -4
  185. package/dist/docs/skills/setup-slack.html +4 -4
  186. package/dist/docs/storage.html +8 -8
  187. package/dist/docs/storage.md +15 -24
  188. package/dist/docs/templates/agentic-owners.html +4 -4
  189. package/dist/docs/templates/agents-md.html +4 -4
  190. package/dist/docs/templates/code-wiki.html +4 -4
  191. package/dist/docs/templates/demo.html +4 -4
  192. package/dist/docs/templates/grokbot-agents.html +4 -4
  193. package/dist/docs/templates/pr-autofixer.html +4 -4
  194. package/dist/docs/templates/security-help.html +4 -4
  195. package/dist/docs/templates/security-reviewer.html +4 -4
  196. package/dist/docs/templates/triage.html +4 -4
  197. package/dist/docs/troubleshooting.html +5 -5
  198. package/dist/docs/troubleshooting.md +1 -1
  199. package/dist/extensions/improve/extension.d.ts +46 -0
  200. package/dist/extensions/improve/extension.d.ts.map +1 -0
  201. package/dist/extensions/improve/extension.js +41 -0
  202. package/dist/extensions/improve/skills/yourself.d.ts +4 -0
  203. package/dist/extensions/improve/skills/yourself.d.ts.map +1 -0
  204. package/dist/extensions/improve/skills/yourself.js +43 -0
  205. package/dist/extensions.d.ts +2 -3
  206. package/dist/extensions.d.ts.map +1 -1
  207. package/dist/extensions.js +2 -5
  208. package/dist/index.d.ts +2 -3
  209. package/dist/index.d.ts.map +1 -1
  210. package/dist/index.js +1 -2
  211. package/dist/internal/authored-alias-hooks.d.ts +5 -0
  212. package/dist/internal/authored-alias-hooks.d.ts.map +1 -1
  213. package/dist/internal/authored-alias-hooks.js +17 -0
  214. package/dist/internal/authored-loaders.d.ts +4 -0
  215. package/dist/internal/authored-loaders.d.ts.map +1 -1
  216. package/dist/internal/authored-loaders.js +21 -2
  217. package/dist/internal/builtin-tools/artifacts.d.ts.map +1 -1
  218. package/dist/internal/builtin-tools/artifacts.js +2 -3
  219. package/dist/internal/channel-route-dispatch.d.ts +13 -0
  220. package/dist/internal/channel-route-dispatch.d.ts.map +1 -0
  221. package/dist/internal/channel-route-dispatch.js +62 -0
  222. package/dist/internal/channel-state.d.ts +17 -0
  223. package/dist/internal/channel-state.d.ts.map +1 -0
  224. package/dist/internal/channel-state.js +78 -0
  225. package/dist/internal/cli-ax.d.ts +8 -2
  226. package/dist/internal/cli-ax.d.ts.map +1 -1
  227. package/dist/internal/cli-ax.js +100 -3
  228. package/dist/internal/cli-cursor.d.ts.map +1 -1
  229. package/dist/internal/cli-cursor.js +2 -0
  230. package/dist/internal/cli-deploy.d.ts.map +1 -1
  231. package/dist/internal/cli-deploy.js +38 -14
  232. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
  233. package/dist/internal/cli-mcp-oauth.js +15 -0
  234. package/dist/internal/cli-mcp.d.ts.map +1 -1
  235. package/dist/internal/cli-mcp.js +12 -0
  236. package/dist/internal/cli-slack.d.ts.map +1 -1
  237. package/dist/internal/cli-slack.js +8 -2
  238. package/dist/internal/continuation-channel.d.ts.map +1 -1
  239. package/dist/internal/continuation-channel.js +2 -2
  240. package/dist/internal/continuation-identity.js +8 -3
  241. package/dist/internal/cursor/credentials.d.ts +8 -2
  242. package/dist/internal/cursor/credentials.d.ts.map +1 -1
  243. package/dist/internal/cursor/credentials.js +27 -5
  244. package/dist/internal/deploy-client.d.ts +27 -0
  245. package/dist/internal/deploy-client.d.ts.map +1 -1
  246. package/dist/internal/deploy-client.js +32 -0
  247. package/dist/internal/deploy-manifest.d.ts +23 -0
  248. package/dist/internal/deploy-manifest.d.ts.map +1 -1
  249. package/dist/internal/deploy-manifest.js +84 -1
  250. package/dist/internal/describe-error.d.ts +1 -0
  251. package/dist/internal/describe-error.d.ts.map +1 -1
  252. package/dist/internal/describe-error.js +25 -0
  253. package/dist/internal/discovery/agent.d.ts +1 -1
  254. package/dist/internal/discovery/agent.d.ts.map +1 -1
  255. package/dist/internal/discovery/agent.js +0 -5
  256. package/dist/internal/discovery/extension-overlay.d.ts +1 -2
  257. package/dist/internal/discovery/extension-overlay.d.ts.map +1 -1
  258. package/dist/internal/discovery/extension-overlay.js +0 -14
  259. package/dist/internal/discovery/extensions.d.ts +1 -2
  260. package/dist/internal/discovery/extensions.d.ts.map +1 -1
  261. package/dist/internal/discovery/extensions.js +4 -22
  262. package/dist/internal/discovery/info.d.ts.map +1 -1
  263. package/dist/internal/discovery/info.js +42 -24
  264. package/dist/internal/discovery/modules.js +0 -1
  265. package/dist/internal/discovery/project.d.ts.map +1 -1
  266. package/dist/internal/discovery/project.js +0 -16
  267. package/dist/internal/eval-runner.js +0 -1
  268. package/dist/internal/framework-file-storage.d.ts +4 -5
  269. package/dist/internal/framework-file-storage.d.ts.map +1 -1
  270. package/dist/internal/framework-file-storage.js +4 -5
  271. package/dist/internal/framework-storage-selection.d.ts +2 -2
  272. package/dist/internal/framework-storage-selection.js +2 -2
  273. package/dist/internal/guest-network.d.ts +4 -10
  274. package/dist/internal/guest-network.d.ts.map +1 -1
  275. package/dist/internal/guest-network.js +42 -26
  276. package/dist/internal/hosted-admission-adapter.d.ts +2 -0
  277. package/dist/internal/hosted-admission-adapter.d.ts.map +1 -1
  278. package/dist/internal/hosted-catch-protocol.d.ts +51 -0
  279. package/dist/internal/hosted-catch-protocol.d.ts.map +1 -0
  280. package/dist/internal/hosted-catch-protocol.js +103 -0
  281. package/dist/internal/hosted-catch.d.ts +40 -0
  282. package/dist/internal/hosted-catch.d.ts.map +1 -0
  283. package/dist/internal/hosted-catch.js +149 -0
  284. package/dist/internal/hosted-delivery-protocol.d.ts +41 -0
  285. package/dist/internal/hosted-delivery-protocol.d.ts.map +1 -1
  286. package/dist/internal/hosted-delivery-protocol.js +249 -0
  287. package/dist/internal/hosted-delivery.d.ts +10 -1
  288. package/dist/internal/hosted-delivery.d.ts.map +1 -1
  289. package/dist/internal/hosted-delivery.js +100 -22
  290. package/dist/internal/hosted-execution-diag.d.ts +4 -1
  291. package/dist/internal/hosted-execution-diag.d.ts.map +1 -1
  292. package/dist/internal/hosted-execution-diag.js +13 -4
  293. package/dist/internal/hosted-execution-flush.d.ts +3 -0
  294. package/dist/internal/hosted-execution-flush.d.ts.map +1 -1
  295. package/dist/internal/hosted-execution-flush.js +2 -4
  296. package/dist/internal/http-control-plane-session.d.ts +30 -0
  297. package/dist/internal/http-control-plane-session.d.ts.map +1 -0
  298. package/dist/internal/http-control-plane-session.js +83 -0
  299. package/dist/internal/init-scaffold.d.ts.map +1 -1
  300. package/dist/internal/init-scaffold.js +1 -2
  301. package/dist/internal/install-cursor-skills.d.ts +5 -2
  302. package/dist/internal/install-cursor-skills.d.ts.map +1 -1
  303. package/dist/internal/install-cursor-skills.js +25 -5
  304. package/dist/internal/legacy-command-guard.d.ts +22 -0
  305. package/dist/internal/legacy-command-guard.d.ts.map +1 -0
  306. package/dist/internal/legacy-command-guard.js +51 -0
  307. package/dist/internal/platform-timers.d.ts +7 -0
  308. package/dist/internal/platform-timers.d.ts.map +1 -1
  309. package/dist/internal/platform-timers.js +139 -0
  310. package/dist/internal/reminder-control-plane-protocol.d.ts +2 -0
  311. package/dist/internal/reminder-control-plane-protocol.d.ts.map +1 -1
  312. package/dist/internal/reminder-control-plane-protocol.js +9 -2
  313. package/dist/internal/reminder-runner.d.ts +8 -0
  314. package/dist/internal/reminder-runner.d.ts.map +1 -1
  315. package/dist/internal/reminder-runner.js +36 -9
  316. package/dist/internal/resolve-prod-target.d.ts +2 -0
  317. package/dist/internal/resolve-prod-target.d.ts.map +1 -1
  318. package/dist/internal/resolve-prod-target.js +13 -0
  319. package/dist/internal/run-client.d.ts +1 -1
  320. package/dist/internal/sdk-runner.d.ts.map +1 -1
  321. package/dist/internal/sdk-runner.js +5 -4
  322. package/dist/internal/server.d.ts.map +1 -1
  323. package/dist/internal/server.js +57 -70
  324. package/dist/internal/session-engine.d.ts +7 -41
  325. package/dist/internal/session-engine.d.ts.map +1 -1
  326. package/dist/internal/session-engine.js +110 -227
  327. package/dist/internal/storage-coordinator.d.ts +5 -19
  328. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  329. package/dist/internal/storage-coordinator.js +3 -62
  330. package/dist/internal/storage-roles.d.ts +4 -9
  331. package/dist/internal/storage-roles.d.ts.map +1 -1
  332. package/dist/internal/storage-roles.js +2 -2
  333. package/dist/playground/assets/index-Bhxzrcf6.css +1 -0
  334. package/dist/playground/assets/index-CqLX5uF3.js +67 -0
  335. package/dist/playground/index.html +2 -2
  336. package/dist/storage-backends/cursor-hosted-v2.d.ts +4 -5
  337. package/dist/storage-backends/cursor-hosted-v2.d.ts.map +1 -1
  338. package/dist/storage-backends/cursor-hosted-v2.js +4 -5
  339. package/dist/storage-backends/cursor-hosted.d.ts +7 -2
  340. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -1
  341. package/dist/storage-backends/cursor-hosted.js +29 -31
  342. package/dist/storage-backends/file-kv.d.ts +9 -12
  343. package/dist/storage-backends/file-kv.d.ts.map +1 -1
  344. package/dist/storage-backends/file-kv.js +11 -47
  345. package/dist/storage-protocol.d.ts +3 -11
  346. package/dist/storage-protocol.d.ts.map +1 -1
  347. package/dist/storage-protocol.js +3 -11
  348. package/dist/storage.d.ts +8 -36
  349. package/dist/storage.d.ts.map +1 -1
  350. package/dist/storage.js +8 -44
  351. package/dist/types.d.ts +53 -62
  352. package/dist/types.d.ts.map +1 -1
  353. package/docs/README.md +14 -6
  354. package/docs/building-with-agents.md +3 -2
  355. package/docs/concepts.md +2 -4
  356. package/docs/deployment.md +7 -2
  357. package/docs/evals.md +613 -297
  358. package/docs/guides/bitbucket.md +89 -0
  359. package/docs/guides/cloud-agents.md +4 -3
  360. package/docs/guides/github.md +33 -1
  361. package/docs/guides/gitlab.md +97 -0
  362. package/docs/guides/grokbot-agents.md +2 -2
  363. package/docs/guides/improve.md +96 -0
  364. package/docs/guides/slack.md +2 -1
  365. package/docs/guides/webhooks.md +4 -2
  366. package/docs/hillclimbing.md +2 -2
  367. package/docs/reference/agent-config.md +5 -2
  368. package/docs/reference/channels.md +16 -3
  369. package/docs/reference/cli.md +24 -15
  370. package/docs/reference/connections.md +2 -1
  371. package/docs/reference/extensions.md +2 -3
  372. package/docs/reference/hooks.md +9 -12
  373. package/docs/reference/http-api.md +2 -3
  374. package/docs/reference/playground.md +0 -4
  375. package/docs/reference/project-layout.md +1 -8
  376. package/docs/reference/sessions.md +1 -2
  377. package/docs/scaffolding-agents.md +2 -1
  378. package/docs/skills/index.md +2 -2
  379. package/docs/storage.md +15 -24
  380. package/docs/troubleshooting.md +1 -1
  381. package/package.json +8 -7
  382. package/skills/create-agent/SKILL.md +1 -1
  383. package/skills/debug/SKILL.md +1 -1
  384. package/skills/deploy/SKILL.md +169 -0
  385. package/skills/evals/SKILL.md +45 -8
  386. package/skills/framework-map/SKILL.md +5 -4
  387. package/skills/github/SKILL.md +1 -1
  388. package/src/bin/agent-serve.ts +27 -2
  389. package/src/channels/bitbucket/api.ts +341 -0
  390. package/src/channels/bitbucket/binding.ts +25 -0
  391. package/src/channels/bitbucket/index.ts +2 -0
  392. package/src/channels/github/github-channel.ts +8 -8
  393. package/src/channels/github/types.ts +5 -0
  394. package/src/channels/gitlab/api.ts +123 -0
  395. package/src/channels/gitlab/binding.ts +12 -0
  396. package/src/channels/gitlab/index.ts +1 -0
  397. package/src/channels/origin/origin-channel.ts +29 -13
  398. package/src/channels/origin/types.ts +5 -0
  399. package/src/channels/slack/dispatch.ts +30 -0
  400. package/src/channels/slack/slack-channel.ts +69 -7
  401. package/src/channels.ts +157 -10
  402. package/src/connections.ts +2 -1
  403. package/src/extensions/improve/extension.ts +70 -0
  404. package/src/extensions/improve/skills/yourself.ts +50 -0
  405. package/src/extensions.ts +2 -6
  406. package/src/index.ts +0 -3
  407. package/src/internal/authored-alias-hooks.ts +32 -0
  408. package/src/internal/authored-loaders.ts +26 -2
  409. package/src/internal/builtin-tools/artifacts.ts +2 -3
  410. package/src/internal/channel-route-dispatch.ts +66 -0
  411. package/src/internal/channel-state.ts +96 -0
  412. package/src/internal/cli-ax.ts +111 -3
  413. package/src/internal/cli-cursor.ts +4 -1
  414. package/src/internal/cli-deploy.ts +50 -10
  415. package/src/internal/cli-mcp-oauth.ts +18 -0
  416. package/src/internal/cli-mcp.ts +11 -0
  417. package/src/internal/cli-slack.ts +15 -2
  418. package/src/internal/continuation-channel.ts +2 -1
  419. package/src/internal/continuation-identity.ts +10 -2
  420. package/src/internal/cursor/credentials.ts +35 -7
  421. package/src/internal/deploy-client.ts +54 -0
  422. package/src/internal/deploy-manifest.ts +115 -1
  423. package/src/internal/describe-error.ts +28 -0
  424. package/src/internal/discovery/agent.ts +1 -7
  425. package/src/internal/discovery/extension-overlay.ts +0 -18
  426. package/src/internal/discovery/extensions.ts +2 -26
  427. package/src/internal/discovery/info.ts +3 -13
  428. package/src/internal/discovery/modules.ts +0 -1
  429. package/src/internal/discovery/project.ts +0 -16
  430. package/src/internal/eval-runner.ts +0 -1
  431. package/src/internal/framework-file-storage.ts +4 -5
  432. package/src/internal/framework-storage-selection.ts +2 -2
  433. package/src/internal/guest-network.ts +43 -29
  434. package/src/internal/hosted-admission-adapter.ts +2 -0
  435. package/src/internal/hosted-catch-protocol.ts +130 -0
  436. package/src/internal/hosted-catch.ts +192 -0
  437. package/src/internal/hosted-delivery-protocol.ts +387 -0
  438. package/src/internal/hosted-delivery.ts +155 -22
  439. package/src/internal/hosted-execution-diag.ts +21 -3
  440. package/src/internal/hosted-execution-flush.ts +6 -3
  441. package/src/internal/http-control-plane-session.ts +104 -0
  442. package/src/internal/init-scaffold.ts +1 -2
  443. package/src/internal/install-cursor-skills.ts +38 -5
  444. package/src/internal/legacy-command-guard.ts +59 -0
  445. package/src/internal/platform-timers.ts +191 -0
  446. package/src/internal/reminder-control-plane-protocol.ts +15 -2
  447. package/src/internal/reminder-runner.ts +60 -9
  448. package/src/internal/resolve-prod-target.ts +15 -0
  449. package/src/internal/run-client.ts +1 -1
  450. package/src/internal/sdk-runner.ts +3 -2
  451. package/src/internal/server.ts +89 -95
  452. package/src/internal/session-engine.ts +155 -285
  453. package/src/internal/storage-coordinator.ts +5 -76
  454. package/src/internal/storage-roles.ts +4 -9
  455. package/src/storage-backends/cursor-hosted-v2.ts +4 -7
  456. package/src/storage-backends/cursor-hosted.ts +40 -38
  457. package/src/storage-backends/file-kv.ts +10 -51
  458. package/src/storage-protocol.ts +3 -17
  459. package/src/storage.ts +10 -101
  460. package/src/types.ts +58 -62
  461. package/templates/demo/README.md +10 -6
  462. package/templates/demo/agent/channels/github.ts +2 -0
  463. package/templates/demo/agent/channels/queue.ts +6 -2
  464. package/templates/demo/agent/lib/repos.ts +5 -0
  465. package/templates/demo/init.json +25 -0
  466. package/dist/ab.d.ts +0 -209
  467. package/dist/ab.d.ts.map +0 -1
  468. package/dist/ab.js +0 -246
  469. package/dist/docs/ab.html +0 -80
  470. package/dist/docs/ab.md +0 -332
  471. package/dist/docs/assets/ab.md.mlVgqvSk.js +0 -54
  472. package/dist/docs/assets/ab.md.mlVgqvSk.lean.js +0 -1
  473. package/dist/docs/assets/chunks/@localSearchIndexroot.DXXZxiMv.js +0 -1
  474. package/dist/docs/assets/concepts.md.DgEcZOfT.js +0 -1
  475. package/dist/docs/assets/evals.md.CbMoebP1.js +0 -85
  476. package/dist/docs/assets/evals.md.CbMoebP1.lean.js +0 -1
  477. package/dist/docs/assets/guides_cloud-agents.md.DS8RIjwx.js +0 -9
  478. package/dist/docs/assets/index.md.BW_6tOgR.js +0 -5
  479. package/dist/docs/assets/reference_sessions.md.CueyOHSL.js +0 -1
  480. package/dist/docs/assets/skills_ab.md.CsFNatVx.js +0 -26
  481. package/dist/docs/assets/skills_ab.md.CsFNatVx.lean.js +0 -1
  482. package/dist/docs/assets/skills_evals.md.723kpUmA.js +0 -25
  483. package/dist/docs/assets/skills_evals.md.723kpUmA.lean.js +0 -1
  484. package/dist/docs/assets/skills_framework-map.md.haibFyoB.js +0 -1
  485. package/dist/docs/assets/skills_index.md.DKwIxzGg.js +0 -1
  486. package/dist/docs/assets/skills_index.md.DKwIxzGg.lean.js +0 -1
  487. package/dist/docs/assets/troubleshooting.md.Cus_YZga.js +0 -1
  488. package/dist/docs/skills/ab.html +0 -52
  489. package/dist/docs/skills/ab.md +0 -50
  490. package/dist/internal/ab-collector.d.ts +0 -44
  491. package/dist/internal/ab-collector.d.ts.map +0 -1
  492. package/dist/internal/ab-collector.js +0 -142
  493. package/dist/internal/ab-fold.d.ts +0 -36
  494. package/dist/internal/ab-fold.d.ts.map +0 -1
  495. package/dist/internal/ab-fold.js +0 -175
  496. package/dist/internal/ab-snapshot.d.ts +0 -68
  497. package/dist/internal/ab-snapshot.d.ts.map +0 -1
  498. package/dist/internal/ab-snapshot.js +0 -208
  499. package/dist/internal/discovery/ab.d.ts +0 -9
  500. package/dist/internal/discovery/ab.d.ts.map +0 -1
  501. package/dist/internal/discovery/ab.js +0 -113
  502. package/dist/playground/assets/index-Bq2HpEQB.js +0 -67
  503. package/dist/playground/assets/index-CZKKNlmb.css +0 -1
  504. package/docs/ab.md +0 -337
  505. package/skills/ab/SKILL.md +0 -58
  506. package/src/ab.ts +0 -430
  507. package/src/internal/ab-collector.ts +0 -200
  508. package/src/internal/ab-fold.ts +0 -232
  509. package/src/internal/ab-snapshot.ts +0 -331
  510. package/src/internal/discovery/ab.ts +0 -131
  511. /package/dist/docs/assets/{concepts.md.DgEcZOfT.lean.js → concepts.md.B4o63Gul.lean.js} +0 -0
  512. /package/dist/docs/assets/{guides_grokbot-agents.md.DVqdhBKb.lean.js → guides_grokbot-agents.md.DyV-WArv.lean.js} +0 -0
  513. /package/dist/docs/assets/{guides_webhooks.md.DKdA43Qm.lean.js → guides_webhooks.md.CJK484ex.lean.js} +0 -0
  514. /package/dist/docs/assets/{hillclimbing.md.CpTGTCle.lean.js → hillclimbing.md.BOiVo1tf.lean.js} +0 -0
  515. /package/dist/docs/assets/{reference_agent-config.md.CHNpiyp4.lean.js → reference_agent-config.md.CvoL6pof.lean.js} +0 -0
  516. /package/dist/docs/assets/{reference_connections.md.Di6jJAXF.lean.js → reference_connections.md.BojkC6c5.lean.js} +0 -0
  517. /package/dist/docs/assets/{reference_extensions.md.CGmMLblt.lean.js → reference_extensions.md.ZAVUyuEX.lean.js} +0 -0
  518. /package/dist/docs/assets/{reference_http-api.md.oySXBO8o.lean.js → reference_http-api.md.BwaCo-VO.lean.js} +0 -0
  519. /package/dist/docs/assets/{reference_sessions.md.CueyOHSL.lean.js → reference_sessions.md.CyXV1MUw.lean.js} +0 -0
  520. /package/dist/docs/assets/{scaffolding-agents.md.em43xlY1.lean.js → scaffolding-agents.md.Kctn3OVb.lean.js} +0 -0
  521. /package/dist/docs/assets/{skills_create-agent.md.BVoWPcan.lean.js → skills_create-agent.md.Q3h6Je-e.lean.js} +0 -0
  522. /package/dist/docs/assets/{skills_github.md.D0JahM8c.lean.js → skills_github.md.hgFX_oKY.lean.js} +0 -0
  523. /package/dist/docs/assets/{troubleshooting.md.Cus_YZga.lean.js → troubleshooting.md.CYEAO9bM.lean.js} +0 -0
@@ -4,349 +4,13 @@
4
4
 
5
5
  ---
6
6
 
7
- Source: /docs/ab.md
8
-
9
- # Live A/B metrics
10
-
11
- Use `defineAB` to compare variants on live agent sessions. New sessions
12
- receive a sticky assignment in each enrolled experiment. The Agent SDK folds
13
- their durable event streams into tool, token, failure, and wall-time
14
- metrics. You can send cumulative samples to your metrics backend and
15
- inspect aggregates in the playground.
16
-
17
- `defineAB` compares live variants through sticky assignment,
18
- instruction overlays, optional tool branches, and cumulative metrics.
19
- Metric callbacks observe the result without approving, rejecting, or
20
- failing a turn. Use [evals](/docs/evals.md) for pass/fail regression checks
21
- on fixed inputs.
22
-
23
- ## Choose live A/B metrics or evals
24
-
25
- Both features read the session event stream, but they answer different
26
- questions.
27
-
28
- | | Live A/B metrics | Evals |
29
- | --- | --- | --- |
30
- | Question | How do variants compare on live sessions? | Does the agent still meet a fixed contract? |
31
- | Location | `agent/ab.ts` or `agent/ab/<name>.ts` | `evals/**/*.eval.ts` |
32
- | Input | Dev or production traffic | Frozen prompts and fixtures |
33
- | Output | Cumulative metrics by session and arm | Pass/fail assertions |
34
- | How it runs | Automatically on new live sessions | `agent-sdk eval` |
35
-
36
- There is no `agent-sdk ab` command or assertion API.
37
-
38
- ## Define an experiment
39
-
40
- Author one experiment in `agent/ab.ts`, add more under
41
- `agent/ab/<name>.ts`, or use both forms. Each file defines one
42
- experiment. The experiment name comes from `name` when set. Otherwise,
43
- the Agent SDK uses `ab` for `agent/ab.ts` and the file stem for files under
44
- `agent/ab/`.
45
-
46
- ```ts
47
- // agent/ab/concise-weather.ts
48
- import {
49
- defineAB,
50
- splitBySessionHash,
51
- } from "@cursor/july/ab";
52
-
53
- export default defineAB({
54
- name: "concise-weather",
55
- variants: {
56
- control: {
57
- label: "Baseline",
58
- },
59
- treatment: {
60
- label: "Short replies",
61
- description: "Adds a one-paragraph response limit.",
62
- instructions: "Keep weather replies to one short paragraph.",
63
- },
64
- },
65
- split: splitBySessionHash({
66
- weights: { control: 1, treatment: 1 },
67
- holdout: 0.1,
68
- }),
69
- derive: {
70
- weatherCalls: (event) =>
71
- event.type === "action.result" &&
72
- event.data.toolName === "get_weather"
73
- ? 1
74
- : null,
75
- },
76
- onSample(sample) {
77
- console.log(
78
- sample.experiment,
79
- sample.variant,
80
- sample.metrics.toolCalls,
81
- sample.metrics.wallTimeMs
82
- );
83
- },
84
- });
85
- ```
86
-
87
- Every definition needs:
88
-
89
- - At least two variants. Variant keys cannot be empty or contain `/` or
90
- `\`.
91
- - A `split` function that returns a variant key or `null`.
92
- - An `onSample` callback for completed or failed turns.
93
-
94
- `label` and `description` appear with the arm in result surfaces.
95
- `instructions` changes the prompt for sessions in that arm. `derive`
96
- adds custom counters.
97
-
98
- Duplicate experiment names are validation errors. Check discovery
99
- before you serve:
100
-
101
- ```bash
102
- agent-sdk validate --dir .
103
- agent-sdk info --dir . --json
104
- ```
105
-
106
- The `abs` field in `info` lists the discovered experiment names.
107
-
108
- ## Assign sticky variants
109
-
110
- Enrollment happens once, when a live session is created and before its
111
- first turn:
112
-
113
- 1. The Agent SDK records `session.started`.
114
- 2. Each experiment runs its `split` function.
115
- 3. The Agent SDK records one durable `ab.assigned` event per experiment.
116
- 4. The selected arms become available on `session.abs`.
117
- 5. Variant instruction overlays reach the first model turn.
118
-
119
- A split can return a variant key or `null`. A null assignment is a
120
- sticky skip for that experiment. It increments the experiment's
121
- `skipped` total, still appears in the snapshot's `sessions` list with
122
- `variant: null`, and does not collect arm metrics or call `onSample`.
123
-
124
- Use the split helper that matches your rollout:
125
-
126
- | Helper | Behavior |
127
- | --- | --- |
128
- | `splitBySessionHash({ weights?, holdout?, salt? })` | Hashes the session id into a reproducible arm; the recommended default |
129
- | `splitByRandom({ weights?, holdout? })` | Draws once when the session starts, then persists the result |
130
- | `splitAlways("control")` | Pins every new session to one arm |
131
- | `splitNone()` | Skips every new session without deleting the experiment |
132
- | `splitIf(predicate, inner)` | Runs `inner` only when the predicate passes |
133
- | Custom `split(ctx)` | Returns a declared variant key or `null` |
134
-
135
- The split context includes the agent name, channel id, session info,
136
- experiment name, and declared variant keys. For example, enroll only
137
- Slack sessions:
138
-
139
- ```ts
140
- split: splitIf(
141
- (ctx) => ctx.channel.id === "slack",
142
- splitBySessionHash()
143
- ),
144
- ```
145
-
146
- Weights default to equal. Non-positive weights leave an arm out of the
147
- draw, and at least one arm must have a positive weight. `holdout` is the
148
- fraction of sessions assigned `null`, from `0` through `1`. Change
149
- `salt` to reshuffle future hash assignments without renaming the
150
- experiment.
151
-
152
- If a custom split throws or returns an unknown variant, the Agent SDK logs
153
- the error and records `variant: null`. The failed decision becomes a
154
- sticky skip instead of breaking the session.
155
-
156
- Enrollment only applies to new sessions. Adding an experiment does not
157
- assign existing conversations. Follow-ups keep the session's original
158
- arms. Keep experiment names and variant keys stable while you collect
159
- and compare results.
160
-
161
- ## Change behavior by variant
162
-
163
- Variant instructions are appended to the agent's base instructions.
164
- Local sessions receive the merged instructions in `AGENTS.md` before
165
- every turn. Cloud sessions receive them in the first-turn preamble
166
- only. For cloud follow-ups, branch through `session.abs` when the arm
167
- must remain visible to deterministic behavior.
168
-
169
- Tools can branch on the assignment through `ctx.session.abs`. Hooks can
170
- read the same field for logging or export:
171
-
172
- ```ts
173
- const treatment =
174
- ctx.session.abs?.["concise-weather"] === "treatment";
175
-
176
- if (treatment) {
177
- return conciseWeatherResult;
178
- }
179
-
180
- return baselineWeatherResult;
181
- ```
182
-
183
- This makes the assignment available to deterministic code as well as
184
- the model prompt. Use both patterns together when one experiment must
185
- steer the prompt and host code at once.
186
-
187
- `defineAB` does not select a different model or runtime for each arm.
188
- Keep those settings in `agent/agent.ts`, or write explicit host logic
189
- when your experiment needs another behavior lever.
190
-
191
- The split and selected arm can affect agent behavior. `derive` and
192
- `onSample` only observe the resulting event stream. Errors in either
193
- callback are logged and never fail the turn.
194
-
195
- ## Collect built-in and custom metrics
196
-
197
- Metrics accumulate for each session and experiment. When one session
198
- joins several experiments, every enrolled experiment folds the same
199
- turn and tool events into its own counters.
200
-
201
- | Metric | How the Agent SDK calculates it |
202
- | --- | --- |
203
- | `turns` | Adds one on `turn.completed` or `turn.failed` |
204
- | `turnFailures` | Adds one on `turn.failed` |
205
- | `toolCalls` | Adds one for each `action.result` |
206
- | `toolErrors` | Adds one when `action.result.data.isError` is true |
207
- | `inputTokens`, `outputTokens` | Adds usage from completed turns |
208
- | `cacheReadTokens`, `cacheWriteTokens` | Adds cache usage from completed turns |
209
- | `costUsd` | Sums the estimated turn cost recorded on `turn.completed` (turns whose model has no known rates contribute 0) |
210
- | `wallTimeMs` | Sums the time from `turn.started` to its completed or failed event |
211
- | `custom` | Sums finite numeric deltas returned by `derive` |
212
-
213
- `onSample` fires after every `turn.completed` and `turn.failed` event
214
- for an enrolled arm. The sample contains:
215
-
216
- | Field | Value |
217
- | --- | --- |
218
- | `experiment` | Experiment name |
219
- | `variant`, `variantLabel?` | Sticky arm and optional display label |
220
- | `sessionId`, `channelId` | Source session |
221
- | `metrics` | Cumulative metrics through this turn |
222
- | `reason` | `turn.completed` or `turn.failed` |
223
- | `at` | Terminal event timestamp |
224
-
225
- The metrics are cumulative, not per-turn deltas. A second sample from
226
- the same session includes the first turn's counts.
227
-
228
- Each `derive` extractor runs on every session event for its enrolled
229
- experiment, including streamed `message.appended` events. Keep it
230
- synchronous and cheap. Return a finite number to add a delta, or
231
- `null` to skip the event. Send samples to your metrics service from
232
- `onSample`; do not perform network or disk work in `derive`.
233
-
234
- Skipped sessions never call `onSample`. Errors from `derive` or
235
- `onSample` are logged, then metric collection continues.
236
-
237
- ## Inspect assignments and results
238
-
239
- Open the playground's **A/Bs** tab to see aggregate arm totals and
240
- per-session assignments. The tab reads `GET /v1/abs`.
241
-
242
- The response has two views of the same durable data:
243
-
244
- | Field | Contents |
245
- | --- | --- |
246
- | `experiments` | Declared variants, skipped-session count, arm session counts, and aggregate metrics |
247
- | `sessions` | Visible sessions with their assignments and cumulative metrics |
248
-
249
- `GET /v1/abs` returns sessions visible to the current principal by
250
- default. In `--dev`, loopback requests include every session. Add
251
- `--allow-anonymous` to include every session from non-loopback callers
252
- too.
253
-
254
- The session event stream is the source of truth for assignment + fold.
255
- `GET /v1/abs` recomputes aggregates from those logs. Any
256
- `agent/storage.ts` exports samples and snapshots durably: an authored
257
- `abs` table when the backend has a native shape for it, or the table
258
- derived over the KV core otherwise. See
259
- [Storage](/docs/storage.md#eval-and-a-b-tables).
260
-
261
- ## Configure the playground fold window
262
-
263
- Assignments and foldable metrics already persist in each session's
264
- event stream. The optional `agent/ab.config.ts` only caps how many
265
- sessions the playground and `GET /v1/abs` fold:
266
-
267
- ```ts
268
- import { defineABConfig } from "@cursor/july/ab";
269
-
270
- export default defineABConfig({
271
- // Optional. Defaults to 200. Only affects GET /v1/abs / A/Bs tab.
272
- maxPlaygroundSessions: 500,
273
- });
274
- ```
275
-
276
- `maxPlaygroundSessions` keeps the newest sessions in the fold. It does
277
- not prune session logs or change assignment. For export to S3, a DB, or
278
- your metrics vendor, send samples from `onSample` or declare a storage
279
- `abs` table.
280
-
281
- ## Keep assignments durable
282
-
283
- The append-only event stream is the source of truth. Each
284
- `ab.assigned` event persists a variant key or null skip. Built-in
285
- metrics come from the turn and tool events that follow it.
286
-
287
- After a server restart or a parked session resumes, the live collector
288
- replays the stream to rebuild cumulative counters. Replay does not call
289
- `onSample` (or write to the storage `abs` table) for historical turns.
290
- Only a new completed or failed turn emits another sample.
291
-
292
- The snapshot API also replays `derive` across the full stream, so
293
- custom totals match the current extractor. Changing a derive function
294
- can change historical snapshot totals. Treat metric definitions as
295
- versioned experiment code.
296
-
297
- ## Keep eval traffic separate
298
-
299
- Sessions created by `agent-sdk eval` and the playground Evals runner use
300
- `purpose: "eval"`. They skip A/B enrollment entirely:
301
-
302
- - No split function runs.
303
- - No `ab.assigned` event is recorded.
304
- - No `onSample` callback fires.
305
- - The session is omitted from `GET /v1/abs`.
306
-
307
- Ordinary chat, `agent-sdk run`, Slack, GitHub, and other channel sessions
308
- use the live purpose. You do not need `splitIf` to exclude eval traffic.
309
-
310
- ## Know the boundaries
311
-
312
- `defineAB` provides sticky assignment, variant instructions,
313
- `session.abs` for tools, cumulative metrics, and local inspection. It
314
- does not provide:
315
-
316
- - A test command, assertion API, or pass/fail result
317
- - Statistical significance calculations
318
- - An experiment rollout or lifecycle service
319
- - Per-variant model or runtime configuration
320
- - A built-in analytics warehouse (bring your own via `onSample` or the
321
- storage `abs` table)
322
-
323
- Use [evals](/docs/evals.md) to protect known behavior. Use `onSample` or a
324
- storage `abs` table when you need sample/snapshot exports beyond the
325
- session event log.
326
-
327
- ## What's next
328
-
329
- Continue with these pages:
330
-
331
- - [Evals](/docs/evals.md): pass/fail regression checks on fixed inputs
332
- - [Hillclimbing](/docs/hillclimbing.md): improve an agent against fixed
333
- fixtures
334
- - [Hooks](/docs/reference/hooks.md): other event-stream consumers
335
- - [Sessions and streaming](/docs/reference/sessions.md): the
336
- `ab.assigned` event and durable log
337
- - [Playground](/docs/reference/playground.md): the A/Bs tab
338
- - [HTTP API](/docs/reference/http-api.md): `GET /v1/abs`
339
- - [Live A/B metrics skill](/docs/skills/ab.md): have a coding agent
340
- wire an experiment
341
-
342
- ---
343
-
344
7
  Source: /docs/building-with-agents.md
345
8
 
346
9
  # Building agents with agents
347
10
 
348
11
  Give a coding agent the goal. The built-in skills guide it through
349
- scaffolding, channels, verification, evals, and measured improvement.
12
+ scaffolding, channels, verification, deployment, evals, and measured
13
+ improvement.
350
14
 
351
15
  ## What can a coding agent build for me?
352
16
 
@@ -393,12 +57,12 @@ The package ships task-specific guides under [`skills/`](/docs/skills/index.md):
393
57
  | Understand the project layout and runtimes | [`framework-map`](/docs/skills/framework-map.md) |
394
58
  | Create and verify a new agent | [`create-agent`](/docs/skills/create-agent.md) |
395
59
  | Write fixtures and regression checks | [`evals`](/docs/skills/evals.md) |
396
- | Live A/B metrics on traffic (`defineAB`) | [`ab`](/docs/skills/ab.md) |
397
60
  | Export OpenTelemetry traces | [`otel`](/docs/skills/otel.md) |
398
61
  | Improve an agent against fixed inputs | [`hillclimb`](/docs/skills/hillclimb.md) |
399
62
  | Add GitHub webhooks and replay events | [`github`](/docs/skills/github.md) |
400
63
  | Connect an agent to Slack | [`setup-slack`](/docs/skills/setup-slack.md) |
401
64
  | Authorize host MCP OAuth | [`mcp-auth`](/docs/skills/mcp-auth.md) |
65
+ | Deploy with an attached service account | [`deploy`](/docs/skills/deploy.md) |
402
66
  | Diagnose a local run | [`debug`](/docs/skills/debug.md) |
403
67
 
404
68
  Point your coding agent at the matching `SKILL.md`. The guide contains
@@ -505,7 +169,6 @@ name. For example, `agent/tools/get_weather.ts` creates a tool named
505
169
  | `agent/mcp-connections/<name>.ts` | Tools from external MCP servers |
506
170
  | `agent/host-connections/<name>.ts` | Privileged MCP servers for host tools only |
507
171
  | `agent/channels/*.ts` | HTTP, Slack, and GitHub entry points |
508
- | `agent/ab.ts` or `agent/ab/*.ts` | Sticky variants and live performance metrics |
509
172
  | `agent/result.ts` | Optional host `commit` on the final assistant text |
510
173
  | `evals/**/*.eval.ts` | Repeatable checks at the project root |
511
174
 
@@ -541,8 +204,8 @@ Each session records an append-only event stream. It includes:
541
204
  - Turn completion and token usage
542
205
 
543
206
  Sessions and their event streams survive server restarts. The
544
- playground renders the stream. Evals assert against it. The
545
- `agent-sdk trajectory` command turns a saved stream into a short
207
+ playground renders the stream. [Evals](/docs/evals.md) assert against it.
208
+ The `agent-sdk trajectory` command turns a saved stream into a short
546
209
  summary.
547
210
 
548
211
  When a run surprises you, inspect its event stream first. See
@@ -636,7 +299,6 @@ See [Agent-to-agent](/docs/guides/agent-to-agent.md) for a complete example.
636
299
  - [Project layout](/docs/reference/project-layout.md)
637
300
  - [Sessions and streaming](/docs/reference/sessions.md)
638
301
  - [Channels](/docs/reference/channels.md)
639
- - [Live A/B metrics](/docs/ab.md)
640
302
 
641
303
  ---
642
304
 
@@ -660,7 +322,7 @@ exposes a stable URL while Cursor manages its runtime lifecycle.
660
322
  ### Before you deploy
661
323
 
662
324
  - Confirm managed hosting is enabled for the account and team.
663
- - Sign in with an account holding team-admin deployment permission.
325
+ - Sign in as a team member, or use a team service-account API key with agent access.
664
326
  - Add `@cursor/july` to the agent project.
665
327
 
666
328
  For a GitHub source, install the Cursor GitHub App on the repository
@@ -689,6 +351,11 @@ Enter hostnames without schemes, ports, or paths. One leading `*.`
689
351
  wildcard is allowed. Declared domains allow HTTPS and TLS traffic, not
690
352
  arbitrary TCP ports.
691
353
 
354
+ The base policy already covers the hosts the platform itself uses: the
355
+ Cursor API (`api.cursor.com` and `api2.cursor.sh`), GitHub and GitLab,
356
+ the npm and Yarn registries, Slack, and Cursor's own artifact storage.
357
+ Declare a domain only for a host your own code reaches.
358
+
692
359
  `secretNames` lists the environment variables the agent expects. Names
693
360
  use `UPPER_SNAKE_CASE`. Commit names only; set their values after
694
361
  creating the deployment. Names beginning with `CURSOR_` are reserved.
@@ -941,7 +608,7 @@ A self-hosted server can read these credentials.
941
608
 
942
609
  | Credential | Used for | Provide it as |
943
610
  | --- | --- | --- |
944
- | Cursor API key | model turns, cloud runtime, Cursor account MCP connections | `agent-sdk login` (stores a revocable key), `CURSOR_API_KEY`, or `--api-key` / `serve({ apiKey })` |
611
+ | Cursor API key | model turns, cloud runtime, Cursor account MCP connections | `agent-sdk login` (stores a revocable key), `CURSOR_API_KEY`, `CURSOR_SERVICE_ACCOUNT_KEY`, or `--api-key` / `serve({ apiKey })` |
945
612
  | Slack tokens | Slack channels | `<PREFIX>_SLACK_BOT_TOKEN` + `<PREFIX>_SLACK_APP_TOKEN` per agent |
946
613
  | GitHub webhook secret | delivery signature verification | `GITHUB_WEBHOOK_SECRET`, same value on server and signer |
947
614
  | GitHub API | outbound API calls | a GitHub App (`GITHUB_APP_ID` + `GITHUB_APP_PRIVATE_KEY` + installation id) or `GITHUB_TOKEN` / `gh auth login` |
@@ -3235,31 +2902,64 @@ Source: /docs/evals.md
3235
2902
 
3236
2903
  # Evals
3237
2904
 
3238
- An eval is a repeatable check that runs your agent against a fixed input
3239
- and gates the recorded trajectory: the run completed, the right tool
3240
- ran, the reply has the right shape. Evals are how you know a prompt
3241
- tweak helped, a refactor didn't regress the agent, and last month's fix
3242
- is still holding.
2905
+ An eval sends a fixed message to your agent and asserts over the
2906
+ trajectory it records: the turn completed, the right tool ran with the
2907
+ right input, the reply has the right shape. Evals are how you know a
2908
+ prompt tweak helped, a refactor didn't regress the agent, and last
2909
+ month's fix still holds.
3243
2910
 
3244
- Evals exercise the same surface your users hit. The runner starts (or
3245
- targets) a real agent server, drives sessions over the public API, and
3246
- grades what comes back. A passing eval means the agent started,
3247
- accepted a message, and did what you asserted.
2911
+ Nothing is mocked. The runner starts (or targets) a real agent server,
2912
+ drives sessions over the public API, and grades the events it gets
2913
+ back. The model runs and server tools execute, so
2914
+ [keep side effects out of eval sessions](#keep-side-effects-out-of-eval-sessions)
2915
+ before you point an eval at an agent that posts anywhere.
3248
2916
 
3249
- ## Define evals with `defineEval`
2917
+ ## Evals, hooks, or hillclimbing?
3250
2918
 
3251
- The Agent SDK discovers evals under the project-root `evals/` directory,
3252
- in `.eval.ts` or `.eval.js` files. That's a sibling of `agent/`, never
3253
- inside it (`agent/evals/` is silently ignored). TypeScript is the normal
3254
- authoring format.
2919
+ All three read the same session event stream. Pick by the question you
2920
+ are asking.
3255
2921
 
3256
- The file path is the eval's identity, so you don't author an id.
3257
- Directories group related evals: `evals/builds/api.eval.ts` becomes id
3258
- `builds/api`. An `index` filename collapses to its directory, so
3259
- `evals/builds/index.eval.ts` becomes `builds`.
2922
+ | You want to | Use |
2923
+ | --- | --- |
2924
+ | Gate one fixed input's behavior, locally and in CI | Evals (this page) |
2925
+ | Observe every live session: metrics, audit, alerts | [Hooks](/docs/reference/hooks.md) |
2926
+ | Improve an agent one measured round at a time | [Hillclimbing](/docs/hillclimbing.md); each kept win lands an eval |
2927
+
2928
+ [Hooks, channel events, or evals?](/docs/reference/hooks.md#hooks-channel-events-or-evals)
2929
+ has the side-by-side table.
2930
+
2931
+ ### When not to write an eval
3260
2932
 
3261
- An eval is a single `async test(t)`. You drive the agent with `t` and
3262
- assert on the run with the same `t`:
2933
+ - Test a server tool's own logic with
2934
+ `agent-sdk call <tool> --dir . --input '{...}'` or a unit test. No
2935
+ model turn, no credential.
2936
+ - Explore a prompt with `agent-sdk run --dir . --message "..."` and
2937
+ read the trajectory. Write the eval once you know which decision to
2938
+ gate.
2939
+ - Stop a bad turn while it runs with
2940
+ [`needsApproval`](/docs/reference/tools.md#gate-a-tool-on-human-approval)
2941
+ on the tool or [`defineResult`](/docs/reference/result.md). Evals grade
2942
+ after the fact.
2943
+
2944
+ ## Write your first eval
2945
+
2946
+ Evals live under the project-root `evals/` directory, a sibling of
2947
+ `agent/`. `agent/evals/` is silently ignored. Discovery loads every
2948
+ `.eval.ts` (or `.eval.js`) file under `evals/`, plus one config file.
2949
+
2950
+ ```text
2951
+ my-agent/
2952
+ agent/
2953
+ agent.ts
2954
+ tools/inspect_pr.ts
2955
+ evals/
2956
+ evals.config.ts # required to run: maxConcurrency
2957
+ readiness.eval.ts # id: readiness
2958
+ prs.eval.ts # cases: prs/checkout, prs/search
2959
+ ```
2960
+
2961
+ An eval is a single `async test(t)`. You drive the agent with `t.send`
2962
+ and assert on the recorded run with the same `t`:
3263
2963
 
3264
2964
  ```ts
3265
2965
  // evals/readiness.eval.ts
@@ -3281,12 +2981,49 @@ export default defineEval({
3281
2981
  });
3282
2982
  ```
3283
2983
 
3284
- One file can also hold several datapoints through `cases` (provide
3285
- either `test` or `cases`, not both). Each case id becomes
3286
- `<fileId>/<case.id>`:
2984
+ ```ts
2985
+ // evals/evals.config.ts
2986
+ import { defineEvalConfig } from "@cursor/july/evals";
2987
+
2988
+ export default defineEvalConfig({ maxConcurrency: 20 });
2989
+ ```
2990
+
2991
+ Run it under Node 22.13 or newer (never Bun) with a Cursor credential
2992
+ in place; see [Credentials](#credentials):
2993
+
2994
+ ```bash
2995
+ agent-sdk eval --dir . --list
2996
+ agent-sdk eval --dir . readiness
2997
+ ```
2998
+
2999
+ ```text
3000
+ PASS readiness (14.2s) — Inspects a PR without approving it.
3001
+ ✓ succeeded
3002
+ ✓ calledTool(inspect_pr)
3003
+ ✓ notCalledTool(approve_pr)
3004
+ ✓ check(includes)
3005
+
3006
+ 1 passed, 0 failed, 1 total
3007
+ artifacts: <project state directory>/evals/2026-09-11T15-02-11-402Z
3008
+ ```
3009
+
3010
+ Every local run writes each case's assertions, inputs, tool calls, and
3011
+ `t.log` lines under that artifacts directory. Open
3012
+ `evals/<case-id>.json` there when a case fails; see
3013
+ [Where results land](#where-results-land).
3014
+
3015
+ ## Name cases by path
3016
+
3017
+ The file path is the eval's identity, so you don't author an id.
3018
+ `evals/builds/api.eval.ts` becomes `builds/api`. An `index` filename
3019
+ collapses to its directory: `evals/builds/index.eval.ts` becomes
3020
+ `builds`.
3021
+
3022
+ One file can hold several datapoints through `cases`. Provide either
3023
+ `test` or `cases`, not both. Each case id becomes `<fileId>/<case.id>`:
3287
3024
 
3288
3025
  ```ts
3289
- // evals/prs.eval.ts prs/checkout, prs/search
3026
+ // evals/prs.eval.ts: prs/checkout, prs/search
3290
3027
  export default defineEval({
3291
3028
  tags: ["smoke", "prs"],
3292
3029
  cases: [
@@ -3315,93 +3052,75 @@ export default defineEval({
3315
3052
  });
3316
3053
  ```
3317
3054
 
3318
- Case ids must be single path segments, unique within the file.
3319
- Each case can set its own `description`, `tags`, `timeoutMs`, and
3320
- `iterations`. A case-level value replaces the file-level value for that
3321
- datapoint.
3055
+ Case ids are single path segments, unique within the file. A case can
3056
+ set its own `description`, `tags`, `timeoutMs`, `iterations`, `judge`,
3057
+ `reporters`, and `metadata`. A case-level value replaces the file-level
3058
+ one for that datapoint, except `metadata`, which merges with case keys
3059
+ winning, and `reporters`, which adds to the file's list. `metadata` is
3060
+ free-form data carried onto the result and every reporter.
3061
+
3062
+ A file may instead export an array of `defineEval` calls to fan out
3063
+ over a dataset. Ids are then the file id plus a zero-padded index
3064
+ (`sql/0000`, `sql/0001`, ...); see [Load a dataset](#load-a-dataset).
3065
+ Prefer `cases` when datapoints are hand-written and deserve stable
3066
+ names.
3322
3067
 
3323
3068
  ### Iterations
3324
3069
 
3325
- `iterations` (file or case, default `1`) runs a datapoint repeatedly.
3326
- Discovery expands `iterations: 3` on case `nyc` to runnable ids
3327
- `weather/nyc/1`, `weather/nyc/2`, `weather/nyc/3` (filter prefix
3328
- `weather/nyc` still selects all three). Each expanded case exposes
3329
- `t.iteration` / `t.iterations` on the test context. Cap is 100.
3070
+ `iterations` (file or case, default `1`, cap `100`) runs a datapoint
3071
+ repeatedly. Discovery expands `iterations: 3` on case `nyc` to runnable
3072
+ ids `weather/nyc/1`, `weather/nyc/2`, and `weather/nyc/3`. The filter
3073
+ `weather/nyc` still selects all three. Each expanded case exposes
3074
+ `t.iteration` and `t.iterations`.
3330
3075
 
3331
- `maxConcurrency` counts **authored datapoints**, not expanded
3332
- iterations: siblings `…/1`…`…/n` share one concurrency slot and run
3333
- sequentially. A suite with 11 cases × 3 iterations and
3334
- `maxConcurrency: 20` therefore has at most 11 cases in flight, not 33.
3076
+ `maxConcurrency` counts authored datapoints, not expanded iterations.
3077
+ Iterations of one datapoint share a concurrency slot and run in
3078
+ sequence, so a suite of 11 cases with 3 iterations each and
3079
+ `maxConcurrency: 20` has at most 11 cases in flight.
3335
3080
 
3336
- ## Configure eval runs
3081
+ ## Drive the agent with `t.send`
3337
3082
 
3338
- Each project with evals needs `evals/evals.config.ts` or
3339
- `evals/evals.config.js`, and it must set `maxConcurrency`. Each case
3340
- issues real model-provider requests, so concurrency is capped hard at
3341
- 200. Existing projects use 20. Discovery with `eval --list` works
3342
- without this file, but running a case does not.
3083
+ `t.send(message, options?)` runs one turn and waits for it to settle:
3084
+ complete, park on an approval request, or fail. Several sends in one
3085
+ case share the session, which is how you write multi-turn evals.
3343
3086
 
3344
- ```ts
3345
- import { defineEvalConfig } from "@cursor/july/evals";
3087
+ Each send resolves to a turn result: `message` (the assistant text),
3088
+ `sessionId`, `events`, `toolCalls` (tool names in order), `ok`, and
3089
+ `index`. The turn carries the same assertion vocabulary as `t`, scoped
3090
+ to that turn, so you can grade an intermediate turn before the next
3091
+ send overwrites `t.reply`. `turn.expectOk()` throws when the turn
3092
+ failed, for later steps that depend on it.
3346
3093
 
3347
- export default defineEvalConfig({
3348
- maxConcurrency: 20, // required
3349
- // timeoutMs: 180_000, // optional project-wide default
3350
- // judge: { model: "..." }, // default judge model for t.judge.*
3351
- // reporters: [], // destinations that observe every case
3352
- // maxPlaygroundRuns: 50, // playground history only (default 20)
3094
+ Read the whole case with `t.reply` (last assistant text), `t.events`
3095
+ (every event so far), `t.turns` (settled turns, oldest first), and
3096
+ `t.sessionId`. `t.signal` aborts when the case hits its timeout; pass
3097
+ it to your own async work.
3098
+
3099
+ Three options apply on the first send only, because they shape session
3100
+ creation:
3101
+
3102
+ | Option | Effect |
3103
+ | --- | --- |
3104
+ | `workspaceFiles` | `{ path: contents }` seeded into the local session workspace. Prefer this over machine-local paths |
3105
+ | `workspaceDir` | Absolute harness cwd for the local runtime |
3106
+ | `cloud` | Per-session cloud options merged over the agent's static `cloud` config. Pin a fixture repo here for cloud evals instead of on the agent's default `cloud.repos`. On the cloud runtime, seeded files reach the agent as described under [Where does a turn run?](/docs/concepts.md#where-does-a-turn-run) |
3107
+
3108
+ ```ts
3109
+ await t.send("Review pr/diff.patch and post findings.", {
3110
+ workspaceFiles: {
3111
+ "pr/diff.patch": [
3112
+ "diff --git a/app/routes/search.ts b/app/routes/search.ts",
3113
+ "+res.send(`<h1>Results for ${req.query.q}</h1>`);",
3114
+ ].join("\n"),
3115
+ },
3353
3116
  });
3354
3117
  ```
3355
3118
 
3356
- The timeout order is case or file `timeoutMs`, CLI `--timeout-ms`,
3357
- project config `timeoutMs`, then the 180-second runner default.
3119
+ ## Assert over the trajectory
3358
3120
 
3359
- The optional fields:
3360
-
3361
- | Option | Default | Meaning |
3362
- | --- | --- | --- |
3363
- | `timeoutMs` | `180_000` | Project-wide per-case timeout |
3364
- | `judge` | unset | Default judge model for `t.judge.*`; see [Judge free-form output](#judge-free-form-output) |
3365
- | `reporters` | unset | Destinations that observe every case; `--skip-report` suppresses them |
3366
- | `maxPlaygroundRuns` | `20` | Max batches in the playground / `/v1/dev/evals*` history (not CLI `eval`). Hard-capped at 500. |
3367
-
3368
- Reporters come from `@cursor/july/evals/reporters`: `JUnit` writes a
3369
- JUnit XML file for CI, `Artifacts` writes per-case files, and
3370
- `combineReporters` merges several into one (`renderJUnitXml` renders
3371
- the XML for a custom destination). A file or case can add its own
3372
- `reporters` on top of the config list.
3373
-
3374
- Playground batches survive restarts whenever `agent/storage.ts` exists
3375
- with an `evals` table or a KV core providing `delete` and `list` (the
3376
- table is derived over the core); see
3377
- [Storage](/docs/storage.md#eval-and-a-b-tables). Without storage they live
3378
- in process memory and disappear when `serve` exits. Navigating away
3379
- and back still works while the process is up.
3380
-
3381
- ## Drive and assert with `t`
3382
-
3383
- `t` is both the driver and the assertion surface. You write ordinary
3384
- control flow, sending turns and asserting inline.
3385
-
3386
- Drive the agent with `t.send(message, options?)`. It runs one turn and
3387
- waits for the session to park or fail. Multiple sends in one case share
3388
- the session, which is how you write multi-turn evals.
3389
-
3390
- Each `t.send` resolves to a turn result with `message`,
3391
- `sessionId`, `events`, `toolCalls`, `ok`, and `index`. The turn carries
3392
- the same assertion vocabulary as `t`, scoped to that turn, so you can
3393
- grade an intermediate turn before the next send overwrites `t.reply`.
3394
- `turn.expectOk()` throws when the turn failed, for later
3395
- steps that depend on it.
3396
-
3397
- Read the full case state with `t.reply` (the last assistant text),
3398
- `t.events` (session events captured so far), `t.turns` (settled
3399
- turns, oldest first), and `t.sessionId`. `t.signal` aborts when the
3400
- case hits its timeout; pass it to your own async work. A thrown
3401
- [turn result](/docs/reference/result.md) `commit` fails the turn, so
3402
- `t.succeeded()` fails too.
3403
-
3404
- Assert with the gates:
3121
+ Assertions record; they never throw. One run reports every failure
3122
+ instead of dying on the first. Assertions on `t` read the whole run.
3123
+ Assertions on a turn read only that turn.
3405
3124
 
3406
3125
  | Gate | Checks |
3407
3126
  | --- | --- |
@@ -3410,284 +3129,548 @@ Assert with the gates:
3410
3129
  | `t.messageIncludes(token)` | the joined assistant text matches a string or `RegExp` |
3411
3130
  | `t.calledTool(name, matcher?)` | a matching call to `name` happened |
3412
3131
  | `t.notCalledTool(name)` | no request for `name`, in any lifecycle state |
3413
- | `t.loadedSkill(name)` | the agent opened the skill's `SKILL.md` (read, grep, or shell `cat`) |
3414
- | `t.toolOrder(names)` | tool requests appear in this relative order (extra calls allowed) |
3132
+ | `t.loadedSkill(name)` | the agent opened `skills/<name>/SKILL.md` (read, grep, or shell `cat`) |
3133
+ | `t.toolOrder(names)` | tool requests appear in this relative order; extra calls allowed |
3415
3134
  | `t.usedNoTools()` | no tool calls at all |
3416
3135
  | `t.maxToolCalls(max)` | at most `max` tool calls |
3417
3136
  | `t.noFailedActions()` | no tool call reported an error |
3418
3137
  | `t.calledSubagent(name, matcher?)` | a matching subagent delegation happened |
3419
3138
  | `t.taggedArtifact(kind?, predicate?)` | at least one [artifact](/docs/reference/artifacts.md) was tagged |
3420
- | `t.event(type, matcher?)` | at least one matching event of `type` occurred |
3421
- | `t.notEvent(type, matcher?)` | no matching event of `type` occurred |
3139
+ | `t.event(type, matcher?)` | at least one matching [event](/docs/reference/sessions.md#which-events-can-i-stream) of `type` |
3140
+ | `t.notEvent(type, matcher?)` | no matching event of `type` |
3422
3141
  | `t.eventOrder(matchers)` | matching event groups occur in this relative order |
3423
3142
  | `t.eventsSatisfy(label, predicate)` | your predicate over the typed event stream |
3424
- | `t.check(value, expectation)` | any value, against a builder |
3425
- | `t.score(name, value)` | records a 01 score you computed; soft until you add a bar |
3426
- | `t.requireToolCall(name, matcher?)` | gates on a matching call and returns it, so later code can read its input and output |
3427
- | `t.requireInputRequest(filter?)` | gates on exactly one pending approval request and returns it |
3143
+ | `t.check(value, expectation)` | any value, against a [builder](#grade-values-with-expectation-builders) |
3144
+ | `t.score(name, value)` | a 0-1 score you computed; soft until you add a bar |
3428
3145
 
3429
- Every gate returns a handle: `.soft()` demotes it to tracked-only,
3430
- `.atLeast(0.7)` adds a soft score bar, and `.gate(0.8)` promotes a
3431
- scored assertion into a hard gate.
3146
+ Three more assertions gate and return the matched fact. They stop the
3147
+ test body when nothing matches, without a duplicate execution error.
3148
+ `t.requireToolCall(name, matcher?)` returns the call so later code can
3149
+ read its `input` and `output`. `t.requireInputRequest(filter?)` returns
3150
+ the single pending approval request. `await t.require(value, expectation)`
3151
+ does the same for a value check.
3432
3152
 
3433
- With no matcher, `calledTool` is request-based: a requested call counts
3434
- even when its result has not arrived. Pass
3435
- `t.calledTool("inspect_pr", { status: "completed" })` to require the
3436
- call to return. `input`, `output`, and `count` matcher fields accept a
3437
- literal, a `RegExp`, or a predicate.
3438
-
3439
- The expectation builders are `includes(string | RegExp)`,
3440
- `equals(value)`, `matches(schema)`, `similarity(expected)`, and
3441
- `satisfies(predicate, label)`. `includes` stringifies its input,
3442
- `equals` compares values deeply, `matches` validates against a Standard
3443
- Schema (or anything with `safeParse`, like Zod), `similarity` scores
3444
- normalized text similarity, and `satisfies` runs your predicate. The
3445
- plain function `normalizedSimilarity(actual, expected)` returns the
3446
- same 0–1 score for use with `t.score`.
3447
-
3448
- A few more context members shape a case: `t.require(value, expectation)`
3449
- records a gate and stops the test body when it fails, without a
3450
- duplicate execution error. `t.skip(reason)` ends the case as skipped
3451
- (reported separately, never changes the exit code; call it before
3452
- sending messages). `t.metric(name, value)` records a structured score
3453
- for the playground case card. `t.log(message)` records a debug line for
3454
- the CLI and playground result.
3455
-
3456
- Three `t.send` options apply on session create (first `t.send` only):
3457
-
3458
- - `workspaceFiles`: `{ path: contents }`, seeded into the local session
3459
- workspace. Prefer this over machine-local paths.
3460
- - `workspaceDir`: absolute harness cwd (local runtime).
3461
- - `cloud`: per-session cloud options merged over the agent's static
3462
- `cloud` config (repos / env / …). Use a pinned `repos` override to
3463
- attach a fixture repo for cloud evals without putting it on the
3464
- agent's default `cloud.repos`. Cloud ignores `workspaceFiles` seeds.
3465
-
3466
- ```ts
3467
- const toolResults = t.events.filter((e) => e.type === "action.result");
3468
- t.check(
3469
- toolResults.length,
3470
- satisfies((n) => (n as number) <= 4, "at most 4 tool calls")
3471
- );
3472
- ```
3153
+ A case with no assertions passes when at least one turn completed. Add
3154
+ `t.succeeded()` and behavior gates anyway. They make the contract
3155
+ visible in review.
3473
3156
 
3474
- A case with no explicit gates falls back to whether at least one turn
3475
- completed successfully. Add `t.succeeded()` and behavior-specific gates
3476
- anyway. They make the contract visible during review.
3157
+ ### What good cases assert
3158
+
3159
+ Gate decisions and shape, not prose. Model wording varies run to run.
3160
+ Tool choice, tool avoidance, and output structure are the stable
3161
+ contract.
3477
3162
 
3478
- ### Judge free-form output
3163
+ 1. `t.succeeded()`: always, first.
3164
+ 2. The tool decision: `calledTool` for the intended path,
3165
+ `notCalledTool` for the likely wrong alternative. The pair is
3166
+ stronger than either alone.
3167
+ 3. Output shape: a regex for the contract (`/ready|blocked/i`, a JSON
3168
+ marker, a findings-block fence), never exact sentences.
3169
+ 4. For structured output, parse `t.reply` and check fields with
3170
+ `matches` or `satisfies` instead of substring-matching JSON.
3479
3171
 
3480
- When wording matters and no regex captures it, `t.judge` grades the
3481
- reply with an LLM. The built-in graders are `factuality(expected)`,
3482
- `summarizes(expected)`, `closedQA(criteria)`, and `sql(expected)`. Each
3483
- scores `t.reply` by default; pass `{ on }` to grade another value.
3172
+ The common failure modes: asserting exact phrasing, packing more than
3173
+ about five gates into one case (split it), and cases that depend on
3174
+ live external state that drifts (pin the input).
3175
+
3176
+ ### Narrow tool assertions with matchers
3177
+
3178
+ With no matcher, `calledTool` is request-based: a requested call counts
3179
+ even before its result arrives. A matcher narrows it:
3484
3180
 
3485
3181
  ```ts
3486
- t.judge.factuality("It is 54°F in NYC right now.").atLeast(0.7);
3182
+ t.calledTool("inspect_pr", { status: "completed" });
3183
+ t.calledTool("apply_agents", { input: { verdict: "update" } });
3184
+ t.calledTool("bash", { input: { command: /^gh pr view/ }, count: 1 });
3185
+ t.calledTool("read_file", {
3186
+ output: (value) => String(value).includes("TODO"),
3187
+ });
3487
3188
  ```
3488
3189
 
3489
- Judge assertions are soft by default, so a judge never fails a build
3490
- until you give it a bar with `.atLeast(0.7)` or promote it with
3491
- `.gate(0.8)`. The judge model comes from `defineEvalConfig({ judge })`,
3492
- `defineEval({ judge })`, a case-level `judge`, or a per-call
3493
- `{ model }` override; the nearest one wins. For a domain-specific judge
3494
- whose verdict is not a single score, `t.judge.model(prompt)` sends a
3495
- raw prompt to the same model and returns the reply. You then record the
3496
- parsed result with `t.score` or `t.check`.
3190
+ `input`, `output`, and `count` accept a literal, a `RegExp`, or a
3191
+ predicate. Object literals partial-deep-match, so `{ verdict: "update" }`
3192
+ matches arguments that also carry other keys. `status` is one of
3193
+ `completed`, `failed`, `pending`, or `rejected` (a human denied the
3194
+ approval). `calledSubagent` takes `{ output, status, count, callId }`.
3195
+ `event`, `notEvent`, and `eventOrder` take `{ data, count }`.
3497
3196
 
3498
- ## Run evals from the CLI
3197
+ ### Grade values with expectation builders
3499
3198
 
3500
- The `eval` command discovers, filters, and runs cases.
3199
+ `t.check(value, expectation)` grades any value: `t.reply`, a parsed
3200
+ JSON field, a tool's output.
3501
3201
 
3502
- Run the CLI under Node 22.13 or newer. Do not use Bun. Its HTTP/2 client
3503
- breaks tool-result streams and causes eval turns to fail.
3202
+ | Builder | Checks | Severity |
3203
+ | --- | --- | --- |
3204
+ | `includes(string \| RegExp)` | substring or match; structured values are stringified first | gate |
3205
+ | `equals(value)` | deep equality | gate |
3206
+ | `matches(schema)` | a Standard Schema (Zod, Valibot, ...) or anything with `safeParse` | gate |
3207
+ | `similarity(expected)` | normalized text similarity, 0-1 | soft |
3208
+ | `satisfies(predicate, label)` | your predicate; `label` is the failure detail | gate |
3504
3209
 
3505
- ```bash
3506
- agent-sdk eval --dir . --list # discover only
3507
- agent-sdk eval --dir . # run all
3508
- agent-sdk eval --dir . builds/checkout # one datapoint
3509
- agent-sdk eval --dir . builds search # several ids or prefixes
3510
- agent-sdk eval --dir . --tag smoke --tag pull-request # any matching tag
3511
- agent-sdk eval --dir . --json --no-stream # machine-readable results
3512
- agent-sdk eval --dir . --verbose # logs + reply snippets
3210
+ ```ts
3211
+ import { matches, satisfies } from "@cursor/july/evals";
3212
+ import { z } from "zod";
3213
+
3214
+ const verdict = JSON.parse(t.reply ?? "{}");
3215
+ t.check(
3216
+ verdict,
3217
+ matches(z.object({ ready: z.boolean(), blockers: z.array(z.string()) }))
3218
+ );
3219
+ t.check(
3220
+ verdict.blockers.length,
3221
+ satisfies((n) => (n as number) <= 3, "at most 3 blockers")
3222
+ );
3513
3223
  ```
3514
3224
 
3515
- Id filters use OR semantics. Each filter selects an exact id and its
3516
- descendants. For example, `builds` selects `builds`,
3517
- `builds/checkout`, and every other case below that path. Repeated tags
3518
- also use OR semantics. When you provide both ids and tags, a case must
3519
- match both groups.
3225
+ `normalizedSimilarity(actual, expected)` returns the same 0-1 score as
3226
+ `similarity`, for use with `t.score`.
3520
3227
 
3521
- `eval` boots an ephemeral server on port 0 with a temp state root
3522
- outside the project, so cases don't inherit ambient monorepo rules and
3523
- don't write into the project state directory. Point `--url` at a running server to eval
3524
- a live agent instead:
3228
+ ### Record without gating
3525
3229
 
3526
- ```bash
3527
- agent-sdk eval --dir . \
3528
- --url http://127.0.0.1:3000/weather-agent \
3529
- --bearer-token "$AGENT_TOKEN"
3230
+ - `t.metric(name, value)` records a structured score or label. It shows
3231
+ on the CLI result, the playground case card, JUnit output, and
3232
+ artifacts.
3233
+ - `t.log(message)` records a debug line, streamed under `--verbose`.
3234
+ - `t.skip(reason)` ends the case as skipped. Skipped cases report
3235
+ separately and never change the exit code. Call it before sending
3236
+ messages.
3237
+
3238
+ ## Gates, soft scores, and verdicts
3239
+
3240
+ Every assertion returns a handle, so severity rides on the assertion
3241
+ instead of a separate thresholds map:
3242
+
3243
+ ```ts
3244
+ t.succeeded(); // gate (default)
3245
+ t.calledTool("get_weather").soft(); // tracked, never fails
3246
+ t.check(t.reply, similarity("Sunny, 72°F")).atLeast(0.8); // soft, with a bar
3247
+ t.judge.closedQA("cites a source").gate(0.8); // promoted to a gate
3530
3248
  ```
3531
3249
 
3532
- The eval definitions still come from `--dir`; `--url` only changes the
3533
- agent that receives the turns. For a locally mounted multi-agent
3534
- directory, `--slug weather-agent` chooses the target. Use
3535
- `--state-root` to keep ephemeral session state at a chosen path,
3536
- `--timeout-ms` to override the project timeout, and `--no-stream` to
3537
- keep live progress off stderr. A TTY streams turn progress by default.
3538
- `--verbose` still writes `t.log` lines to stderr and adds reply snippets
3539
- to text results.
3250
+ - `.gate(threshold?)` is hard. A miss fails the case and `eval` exits 1.
3251
+ - `.soft(threshold?)` is tracked. With no threshold it never fails.
3252
+ - `.atLeast(threshold)` is soft with a bar. A miss marks the case
3253
+ `scored`.
3540
3254
 
3541
- Model turns need a Cursor credential from `agent-sdk login` or
3542
- `CURSOR_API_KEY`.
3255
+ Each case ends with one verdict:
3543
3256
 
3544
- See [CLI: eval](/docs/reference/cli.md#eval) for flags and exit codes.
3257
+ | Verdict | Meaning | Exit code |
3258
+ | --- | --- | --- |
3259
+ | `passed` | every gate passed and no soft bar was missed | 0 |
3260
+ | `failed` | a gate failed, or the test body threw | 1 |
3261
+ | `scored` | only soft bars were missed | 0, or 1 under `--strict` |
3262
+ | `skipped` | `t.skip(reason)`, or a judge with no credentials | 0 |
3545
3263
 
3546
- ### JSON results
3264
+ The CLI prints soft misses as `~` and gate misses as `✗`. Start a new
3265
+ benchmark with `t.score("recall", recall)` and `.atLeast()` so its
3266
+ number reports for a while without blocking merges. Add `--strict`
3267
+ once the bars are trustworthy.
3547
3268
 
3548
- Use `--json --no-stream` in scripts and CI. The top-level result carries
3549
- the totals and one result per case:
3269
+ ## Judge free-form output
3550
3270
 
3551
- ```json
3552
- {
3553
- "ok": true,
3554
- "passed": 1,
3555
- "failed": 0,
3556
- "results": [
3557
- {
3558
- "id": "readiness",
3559
- "ok": true,
3560
- "assertions": [{ "name": "succeeded", "passed": true }],
3561
- "sessionId": "ses_123",
3562
- "inputs": ["Is checkout pull request 42 ready to approve?"],
3563
- "toolCalls": [{ "toolName": "inspect_pr", "isError": false }],
3564
- "logs": [],
3565
- "durationMs": 12340
3566
- }
3567
- ]
3568
- }
3271
+ When wording matters and no regex captures it, `t.judge` grades with an
3272
+ LLM. The graders are `factuality(expected)`, `summarizes(expected)`,
3273
+ `closedQA(criteria)`, and `sql(expected)`. Each scores `t.reply` by
3274
+ default; pass `{ on }` to grade another value.
3275
+
3276
+ ```ts
3277
+ const summary = await t.send("Why did CI fail on PR 42?");
3278
+ t.judge.factuality("The lint step failed on src/sidebar.ts.").atLeast(0.7);
3279
+ t.judge.closedQA("names the failing step", { on: summary.message }).gate(1);
3569
3280
  ```
3570
3281
 
3571
- Each case result can also include `description`, `finalText`, `tools`,
3572
- `error`, and tool arguments or output. This shape lets CI report the
3573
- failed assertion without parsing terminal text.
3282
+ Judge assertions are soft by default, so a judge never fails a build
3283
+ until you give it a bar with `.atLeast()` or promote it with `.gate()`.
3284
+ The recorded detail names the choice the judge made and its rationale.
3574
3285
 
3575
- ## Run evals in the playground
3286
+ The judge model comes from `defineEvalConfig({ judge })`,
3287
+ `defineEval({ judge })`, a case-level `judge`, or a per-call
3288
+ `{ model }`. The nearest one wins. A judge call with no model
3289
+ configured fails the case. A judge that cannot reach a model (no
3290
+ credential) ends the case as `skipped`, unless a deterministic gate
3291
+ already failed.
3292
+
3293
+ For a domain-specific judge whose verdict is not a single score,
3294
+ `t.judge.model(prompt)` sends a raw prompt to the same model and
3295
+ returns the reply. Record the parsed result with `t.score` or
3296
+ `t.check`. Anything derived from the agent under test is untrusted
3297
+ input to your prompt: wrap it with `fenceUntrusted` and include
3298
+ `EVAL_JUDGE_INJECTION_GUARD`, as the built-in graders do.
3576
3299
 
3577
- Start the server, open the playground, and choose **Evals**. You can run
3578
- every case or one case, watch progress, and open the resulting session
3579
- trace. The Evals tab works on a normal `serve`.
3300
+ ```ts
3301
+ import {
3302
+ EVAL_JUDGE_INJECTION_GUARD,
3303
+ fenceUntrusted,
3304
+ } from "@cursor/july/evals";
3305
+
3306
+ const gold = ["XSS in search.ts", "open redirect in login.ts"];
3307
+ const reply = await t.judge.model(
3308
+ [
3309
+ "For each GOLD finding, answer whether SUBMISSION reports it.",
3310
+ "Reply with one line per finding: <index> YES|NO.",
3311
+ EVAL_JUDGE_INJECTION_GUARD,
3312
+ fenceUntrusted("GOLD", gold.map((g, i) => `${i + 1}. ${g}`).join("\n")),
3313
+ fenceUntrusted("SUBMISSION", t.reply ?? ""),
3314
+ ].join("\n\n")
3315
+ );
3316
+ const hits = reply.match(/\bYES\b/g)?.length ?? 0;
3317
+ t.score("recall", hits / gold.length).atLeast(0.5);
3318
+ ```
3580
3319
 
3581
- ```bash
3582
- agent-sdk serve --dir .
3320
+ ## Keep side effects out of eval sessions
3321
+
3322
+ Eval sessions run the real agent, tools included. A reviewer that
3323
+ comments on GitHub or posts to Slack will do so from an eval unless
3324
+ the tool checks the session's purpose. Eval sessions carry
3325
+ `purpose: "eval"`; live traffic carries `"live"`. Branch on it in the
3326
+ tool, hook, or result handler that actuates:
3327
+
3328
+ ```ts
3329
+ // agent/tools/post_findings.ts
3330
+ async execute({ findings }, ctx) {
3331
+ if (ctx.session.purpose === "eval") {
3332
+ return { posted: false, reason: "eval", count: findings.length };
3333
+ }
3334
+ // post the review
3335
+ }
3583
3336
  ```
3584
3337
 
3585
- Playground runs target the live server instead of an ephemeral one.
3586
- Their sessions appear in the session list. One eval batch can run at a
3587
- time. Persistence follows the rule under
3588
- [Configure eval runs](#configure-eval-runs). See
3589
- [Playground eval routes](/docs/reference/http-api.md#playground-eval-routes).
3590
- The start request returns `202` while cases run in the background.
3591
- Poll until the snapshot status becomes `completed`, `failed`, or `cancelled`.
3592
- Configuration errors appear on a failed snapshot.
3338
+ Return a shaped result instead of throwing, so the eval can still
3339
+ assert `t.calledTool("post_findings", { input: ... })` on the decision.
3340
+ The same check belongs in [hooks](/docs/reference/hooks.md) that meter or
3341
+ page and in [`defineResult`](/docs/reference/result.md) commits.
3593
3342
 
3594
- On `--prod` / `--url`, the CLI prints the Eval ID as soon as the batch is
3595
- accepted (and a Playground deep link with `?view=evals&evalRunId=…`):
3343
+ ## Worked examples
3596
3344
 
3597
- ```bash
3598
- agent-sdk eval --prod --slug vulnerability-scanner --tag deepsec
3599
- # Eval ID: evalrun_…
3600
- # Cancel: agent-sdk eval cancel evalrun_… --prod --slug vulnerability-scanner
3601
- # Playground: https://…/playground?view=evals&evalRunId=evalrun_…
3345
+ ### Multi-turn: grade each turn
3602
3346
 
3603
- agent-sdk eval cancel evalrun_… --prod --slug vulnerability-scanner
3604
- agent-sdk eval status evalrun_… --prod --slug vulnerability-scanner
3347
+ ```ts
3348
+ // evals/intro.eval.ts
3349
+ import { defineEval, includes, satisfies } from "@cursor/july/evals";
3350
+
3351
+ export default defineEval({
3352
+ description: "Introduces itself once; a repeat mention gets a short ack.",
3353
+ async test(t) {
3354
+ const intro = await t.send("Meet Jenny! @Jenny introduce yourself.");
3355
+ intro.expectOk();
3356
+ t.check(intro.message, includes(/jenny/i));
3357
+
3358
+ const repeat = await t.send("Meet, @Jenny!");
3359
+ t.succeeded();
3360
+ repeat.usedNoTools();
3361
+ t.check(
3362
+ repeat.message,
3363
+ satisfies((r) => (r as string).trim().length <= 280, "short ack")
3364
+ );
3365
+ t.check(
3366
+ repeat.message,
3367
+ satisfies((r) => !/what i can do/i.test(r as string), "no second intro")
3368
+ );
3369
+ },
3370
+ });
3605
3371
  ```
3606
3372
 
3607
- ## What good cases assert
3373
+ `t.succeeded()` grades the whole session. `repeat.usedNoTools()` and the
3374
+ checks on `repeat.message` read only the second turn, even though
3375
+ `t.reply` now holds its text.
3608
3376
 
3609
- Gate decisions and shape, not prose. Model wording varies run to run.
3610
- Tool choice, tool avoidance, and output structure are the stable
3611
- contract.
3377
+ ### Approvals: assert the parked decision
3612
3378
 
3613
- 1. `t.succeeded()`: always, first.
3614
- 2. The tool decision: `calledTool` for the intended path,
3615
- `notCalledTool` for the likely wrong alternative. The pair is
3616
- stronger than either alone.
3617
- 3. Output shape: a regex for the contract (`/ready|blocked/i`, a JSON
3618
- marker, a findings-block fence), never exact sentences.
3619
- 4. For structured output, parse `t.reply` and check fields with
3620
- `satisfies` instead of substring-matching JSON.
3379
+ For a tool with `needsApproval`, the turn parks instead of finishing.
3380
+ Gate on `t.parked()` and on the arguments the model chose:
3621
3381
 
3622
- The common failure modes: asserting exact phrasing, packing more than
3623
- about five gates into one case (split it), and cases that depend on live
3624
- external state that drifts (pin the input; see fixtures).
3382
+ ```ts
3383
+ // evals/agents.eval.ts (one case; RULE and SLACK are fixture strings)
3384
+ {
3385
+ id: "update-rule",
3386
+ description: "A repeated billing rule parks the AGENTS.md write.",
3387
+ async test(t) {
3388
+ await t.send("Weekly AGENTS.md review. Read week/ and call apply_agents once.", {
3389
+ workspaceFiles: {
3390
+ "week/prs.md": RULE,
3391
+ "week/slack.md": SLACK,
3392
+ "week/tree/AGENTS.md.txt": "# API\n\nKeep handlers thin.\n",
3393
+ },
3394
+ });
3395
+ t.parked();
3396
+ t.calledTool("apply_agents", { input: { verdict: "update" } });
3397
+ },
3398
+ },
3399
+ ```
3400
+
3401
+ `t.parked()` and `t.succeeded()` are exclusive: a parked run is a clean
3402
+ stop on an unanswered approval, not a completed one. Pair the parked
3403
+ case with a sibling that expects `verdict: "skip"` and `t.succeeded()`,
3404
+ so both branches stay pinned.
3625
3405
 
3626
- ## Pick fixtures by agent type
3406
+ ## Pin fixtures
3627
3407
 
3628
- The right fixture depends on the surface under test.
3408
+ A fixed input is what makes an eval repeatable. Pick the fixture by the
3409
+ surface under test.
3629
3410
 
3630
3411
  | Agent surface | Fixture |
3631
3412
  | --- | --- |
3632
- | Chat / domain assistant | A canonical prompt string, chosen once and frozen |
3413
+ | Chat or domain assistant | One canonical prompt string, chosen once and frozen |
3633
3414
  | Tool-heavy | Run `agent-sdk call <tool>` first to pin what the tool returns, then freeze the prompt that triggers it |
3634
- | GitHub webhook | `agent-sdk github replay <pr> --events '*' --dry-run --out fixtures/github` snapshots real payloads for offline replay ([GitHub guide](/docs/guides/github.md)) |
3415
+ | GitHub webhook | `agent-sdk github replay <pr> --events '*' --dry-run --out fixtures/github` snapshots real payloads for offline replay ([GitHub guide](/docs/guides/github.md#test-with-github-replay)) |
3635
3416
  | PR reviewer with host preparation | Diff, metadata, and gold labels pinned to commit SHAs; keep any live PR matrix small |
3636
- | Workspace-dependent | `workspaceFiles` in `t.send` options, never developer-machine paths |
3417
+ | Workspace-dependent | `workspaceFiles` on the first `t.send`, never developer-machine paths |
3637
3418
 
3638
- Tag the fast, reliably passing core `smoke` and run `--tag smoke` in the
3639
- inner loop. Leave slow or flaky-prone cases untagged for explicit runs.
3419
+ Tag the fast, reliably passing core `smoke` and run `--tag smoke` in
3420
+ the inner loop. Leave slow or drift-prone cases untagged for explicit
3421
+ runs.
3640
3422
 
3641
3423
  ### Materialize API-backed fixtures
3642
3424
 
3643
- An input that only points at external data, such as a pull request URL,
3644
- snapshot id, or pair of commit SHAs, is not self-contained. Fetch it
3645
- once and commit the rendered fixture before you expand the suite.
3425
+ An input that only points at external data (a pull request URL, a
3426
+ snapshot id, a pair of commit SHAs) is not self-contained. Fetch it once
3427
+ and commit the rendered fixture before you expand the suite:
3646
3428
 
3647
3429
  1. Save the diff, metadata, and labels under `fixtures/` at pinned
3648
3430
  revisions.
3649
- 2. Seed those files with `workspaceFiles`, or read them from the fixture
3650
- directory.
3431
+ 2. Seed those files with `workspaceFiles`, or read them from the
3432
+ fixture directory.
3651
3433
  3. Assert decisions and output shape against the saved evidence.
3652
- 4. Keep a small `smoke` subset for any remaining live pipeline checks.
3434
+ 4. Keep a small `smoke` subset for any remaining live checks.
3435
+
3436
+ `maxConcurrency` limits parallel datapoints, not the model or API
3437
+ fan-out inside one datapoint. Materialized fixtures keep a large suite
3438
+ from exhausting provider and GitHub rate limits.
3439
+
3440
+ ### Load a dataset
3441
+
3442
+ Read committed fixtures with `loadJson`, `loadJsonl`, and `loadYaml`
3443
+ from `@cursor/july/evals/loaders`. Relative paths resolve against the
3444
+ project root the runner discovered, not the cwd the CLI ran from. Eval
3445
+ files are ES modules, so top-level `await` can load a dataset and fan
3446
+ one file out over it:
3447
+
3448
+ ```ts
3449
+ // evals/sql.eval.ts: sql/0000, sql/0001, ...
3450
+ import { defineEval, equals } from "@cursor/july/evals";
3451
+ import { loadYaml } from "@cursor/july/evals/loaders";
3452
+
3453
+ const rows = await loadYaml<{ task: string; prompt: string; sql: string }[]>(
3454
+ "evals/data/cases.yaml"
3455
+ );
3456
+
3457
+ export default rows.map((row) =>
3458
+ defineEval({
3459
+ description: row.task,
3460
+ async test(t) {
3461
+ await t.send(row.prompt);
3462
+ t.succeeded();
3463
+ t.check(t.reply, equals(row.sql));
3464
+ },
3465
+ })
3466
+ );
3467
+ ```
3468
+
3469
+ ## Configure eval runs
3470
+
3471
+ `evals/evals.config.ts` (or `.js`) holds project-wide defaults. It must
3472
+ set `maxConcurrency`. Each case issues real model requests, so
3473
+ concurrency is hard-capped at 200; the templates use 10.
3474
+ `eval --list` works without the file. Running a case does not.
3475
+
3476
+ ```ts
3477
+ import { defineEvalConfig } from "@cursor/july/evals";
3478
+
3479
+ export default defineEvalConfig({
3480
+ maxConcurrency: 20,
3481
+ timeoutMs: 180_000,
3482
+ judge: { model: "gpt-5.4-mini" },
3483
+ });
3484
+ ```
3485
+
3486
+ | Option | Default | Meaning |
3487
+ | --- | --- | --- |
3488
+ | `maxConcurrency` | required | Datapoints in flight at once, 1-200 |
3489
+ | `timeoutMs` | `180_000` | Per-case timeout. Precedence: case or file `timeoutMs`, then `--timeout-ms`, then this value |
3490
+ | `judge` | unset | Default judge model for `t.judge.*` |
3491
+ | `reporters` | unset | Destinations that observe every case; `--skip-report` suppresses them |
3492
+ | `maxPlaygroundRuns` | `20` | Batches kept in playground history (cap 500). Counts `--prod` and `--url` batches, not the default local run |
3493
+
3494
+ Reporters ship results somewhere; the runner still does the grading.
3495
+ `JUnit({ filePath, suiteName? })` writes JUnit XML and
3496
+ `Artifacts({ dir })` writes per-case files, both from
3497
+ `@cursor/july/evals/reporters`. A custom reporter is an object with any
3498
+ of `onRunStart`, `onEvalComplete`, and `onRunComplete`. A reporter that
3499
+ throws is logged and never fails the run. CI usually attaches the
3500
+ built-in two with `--junit` and `--artifacts` instead of `reporters`,
3501
+ so output paths stay with the pipeline, not the eval author.
3502
+
3503
+ Playground batches survive restarts when the project has
3504
+ [storage](/docs/storage.md#eval-table). Otherwise they live in process
3505
+ memory until `serve` exits.
3506
+
3507
+ ## Run evals from the CLI
3508
+
3509
+ ```bash
3510
+ agent-sdk eval --dir . --list # discover only
3511
+ agent-sdk eval --dir . # run all
3512
+ agent-sdk eval --dir . builds/checkout # one datapoint
3513
+ agent-sdk eval --dir . builds search # several ids or prefixes
3514
+ agent-sdk eval --dir . --tag smoke --tag pull-request # any matching tag
3515
+ agent-sdk eval --dir . --verbose # t.log lines + reply snippets
3516
+ agent-sdk eval --dir . --timeout-ms 300000 # override the per-case timeout
3517
+ ```
3518
+
3519
+ Id filters use OR semantics. Each filter selects an exact id and its
3520
+ descendants: `builds` selects `builds`, `builds/checkout`, and every
3521
+ other case below that path. Repeated tags also use OR. With both ids
3522
+ and tags, a case must match both groups.
3523
+
3524
+ By default `eval` boots a throwaway server with its own state root, so
3525
+ cases don't inherit your checkout's `AGENTS.md` and session state stays
3526
+ out of the project. Artifacts still land in the project state
3527
+ directory; see [Where results land](#where-results-land). `--slug`
3528
+ picks the target in a multi-agent directory.
3529
+
3530
+ `--url` runs the batch on a running server instead, the same way
3531
+ `--prod` does: that server discovers its own `evals/`, results land in
3532
+ its playground history, and the local-only flags (`--junit`,
3533
+ `--artifacts`, `--max-concurrency`, `--skip-report`) do not apply. See
3534
+ [Run evals in the playground or on a deployment](#run-evals-in-the-playground-or-on-a-deployment).
3535
+
3536
+ ```bash
3537
+ agent-sdk eval --url http://127.0.0.1:3000/weather-agent \
3538
+ --bearer-token "$AGENT_TOKEN"
3539
+ ```
3540
+
3541
+ See [CLI: eval](/docs/reference/cli.md#eval) for every flag.
3542
+
3543
+ ### Credentials
3544
+
3545
+ Model turns need a
3546
+ [Cursor credential](/docs/reference/cli.md#environment-variables):
3547
+ `CURSOR_API_KEY`, `CURSOR_SERVICE_ACCOUNT_KEY`, or a saved
3548
+ `agent-sdk login`. The judge uses the same one. `eval --list` needs
3549
+ none.
3550
+
3551
+ ### Where results land
3552
+
3553
+ Every local run writes artifacts to a timestamped directory under
3554
+ `evals/` in the project state directory, whatever `--state-root` says.
3555
+ `--artifacts <dir>` chooses the path and `--no-artifacts` skips them.
3556
+ The directory holds `summary.json`,
3557
+ `results.jsonl`, and `evals/<case-id>.json` with every assertion, the
3558
+ inputs, tool calls with arguments and output, the final text, and
3559
+ `t.log` lines. Start there when a case fails. `--out <file>` also
3560
+ writes the full results JSON to a path of your choice.
3561
+
3562
+ The artifact does not include the session's event stream. Pass
3563
+ `--state-root <path>` to keep the ephemeral server's
3564
+ [session data](/docs/reference/sessions.md#where-does-the-agent-sdk-store-session-data)
3565
+ on disk when you need the raw events.
3566
+
3567
+ ## Run evals in CI
3568
+
3569
+ Run the suite non-interactively, write JUnit for the CI annotations,
3570
+ and fail the job on a red gate:
3571
+
3572
+ ```bash
3573
+ # CURSOR_API_KEY comes from the CI secret store
3574
+ agent-sdk eval --dir . --json --no-stream \
3575
+ --junit reports/evals.xml \
3576
+ --artifacts reports/evals \
3577
+ > reports/evals.json
3578
+ ```
3579
+
3580
+ The exit code follows the
3581
+ [verdict table](#gates-soft-scores-and-verdicts); `2` means nothing
3582
+ matched the selection. `--max-concurrency` overrides the project
3583
+ setting, for example to run lower on a shared runner.
3584
+
3585
+ The JSON on stdout carries the totals and one result per case:
3586
+
3587
+ ```json
3588
+ {
3589
+ "ok": true,
3590
+ "passed": 1,
3591
+ "failed": 0,
3592
+ "scored": 0,
3593
+ "skipped": 0,
3594
+ "strict": false,
3595
+ "artifactsDir": "/work/my-agent/reports/evals",
3596
+ "results": [
3597
+ {
3598
+ "id": "readiness",
3599
+ "verdict": "passed",
3600
+ "ok": true,
3601
+ "assertions": [
3602
+ { "name": "succeeded", "passed": true },
3603
+ { "name": "calledTool(inspect_pr)", "passed": true }
3604
+ ],
3605
+ "sessionId": "ses_123",
3606
+ "inputs": ["Is https://github.com/acme/checkout/pull/42 ready to approve?"],
3607
+ "toolCalls": [{ "toolName": "inspect_pr", "isError": false }],
3608
+ "metrics": {},
3609
+ "logs": [],
3610
+ "durationMs": 12340
3611
+ }
3612
+ ]
3613
+ }
3614
+ ```
3615
+
3616
+ Each result can also include `description`, `finalText`, `tools`,
3617
+ `error`, `skipReason`, `metadata`, `tags`, and tool `args` and `output`.
3618
+ A soft miss shows as `"severity": "soft"` with `score` and `threshold`
3619
+ on the assertion. This shape lets CI report the failed assertion
3620
+ without parsing terminal text.
3621
+
3622
+ Keep CI green without weakening gates:
3623
+
3624
+ - Run `--tag smoke` on every push and the full suite on a schedule.
3625
+ - For probabilistic behavior, use `iterations` and a soft bar instead
3626
+ of one hard gate.
3653
3627
 
3654
- Read committed fixtures with `@cursor/july/evals/loaders`: `loadJson`,
3655
- `loadJsonl`, and `loadYaml` resolve relative paths against the project
3656
- root the runner discovered, not the cwd the CLI was invoked from
3657
- (`resolveFixturePath` and `evalFixtureRoot` expose the same
3658
- resolution for other file formats).
3628
+ ## Run evals in the playground or on a deployment
3659
3629
 
3660
- `maxConcurrency` limits parallel datapoints. It does not limit model or
3661
- API fan-out inside one datapoint. Materialized fixtures prevent a large
3662
- suite from exhausting provider and GitHub rate limits. The
3663
- [evals skill](/docs/skills/evals.md) has the full fixture workflow.
3630
+ Start the server, open the playground, and choose **Evals**. Run every
3631
+ case or one case, watch progress, and open the resulting session trace.
3632
+ Playground runs target the live server instead of an ephemeral one, so
3633
+ their sessions appear in the session list. One batch runs at a time.
3664
3634
 
3665
- ## Keep improvements with regression evals
3635
+ ```bash
3636
+ agent-sdk serve --dir .
3637
+ ```
3638
+
3639
+ `--prod` (or `--url`) starts the same server-side batch on the team's
3640
+ hosted deployment (or the server you name), so results land in that
3641
+ server's playground history:
3642
+
3643
+ ```bash
3644
+ agent-sdk eval --prod --slug vulnerability-scanner --tag deepsec
3645
+ # Eval ID: <evalId>
3646
+ # Playground: <deployment>/playground?view=evals&evalRunId=<evalId>
3647
+ agent-sdk eval status <evalId> --prod --slug vulnerability-scanner
3648
+ agent-sdk eval cancel <evalId> --prod --slug vulnerability-scanner
3649
+ ```
3666
3650
 
3667
- Every [hillclimb](/docs/hillclimbing.md) round that keeps a change must land
3668
- an eval that would have failed before the change. If you can't express
3669
- the improvement as a gate (a `calledTool` shift, a bounded
3670
- `action.result` count, an output-shape regex), the improvement is
3671
- unverified, and it'll regress silently.
3651
+ The CLI prints the Eval ID as soon as the batch is accepted. Pass
3652
+ `--no-wait` to return right away and poll with `eval status` later; it
3653
+ exits `3` while the batch is still running. Hosted history follows
3654
+ `maxPlaygroundRuns` and the persistence rule under
3655
+ [Configure eval runs](#configure-eval-runs). The HTTP surface is under
3656
+ [Playground eval routes](/docs/reference/http-api.md#playground-eval-routes).
3672
3657
 
3673
- The rule cuts the other way too: never weaken an existing gate to make a
3674
- round pass. That's the freeze line moving, and it turns your regression
3675
- suite into a list of checks that no longer protect anything.
3658
+ ## Keep improvements with regression evals
3676
3659
 
3677
- ## Compare variants on live traffic
3660
+ Every [hillclimb](/docs/hillclimbing.md) round that keeps a change must
3661
+ land an eval that would have failed before the change. If you can't
3662
+ express the improvement as a gate (a `calledTool` shift, a bounded
3663
+ `maxToolCalls`, an output-shape check), the improvement is unverified,
3664
+ and it'll regress silently.
3678
3665
 
3679
- Use `defineAB` to compare variant metrics on live sessions. It is not a
3680
- test runner and has no `agent-sdk ab` command. Keep `defineEval` as the
3681
- regression ratchet. Eval sessions do not enroll or change live metrics.
3682
- See [Live A/B metrics](/docs/ab.md) for assignment, behavior, collection,
3683
- and inspection.
3666
+ The rule cuts the other way too: never weaken an existing gate to make
3667
+ a round pass. That's the freeze line moving, and it turns your
3668
+ regression suite into a list of checks that no longer protect anything.
3684
3669
 
3685
3670
  ## What's next
3686
3671
 
3687
3672
  Continue with these pages:
3688
3673
 
3689
- - [Live A/B metrics](/docs/ab.md): sticky variants and cumulative metrics
3690
- on live sessions
3691
3674
  - [Hillclimbing](/docs/hillclimbing.md): the loop evals make trustworthy
3692
3675
  - [Building agents with agents](/docs/building-with-agents.md): have a
3693
3676
  coding agent write the first suite
@@ -3816,6 +3799,95 @@ Continue with these pages:
3816
3799
 
3817
3800
  ---
3818
3801
 
3802
+ Source: /docs/guides/bitbucket.md
3803
+
3804
+ # Bitbucket agents
3805
+
3806
+ Use `bitbucketChannel()` for Bitbucket Cloud and Bitbucket Data Center. The
3807
+ channel detects the source from each signed payload and normalizes Data Center
3808
+ events to the Bitbucket Cloud event vocabulary.
3809
+
3810
+ ## Define the channel
3811
+
3812
+ Author `agent/channels/bitbucket.ts`:
3813
+
3814
+ ```ts
3815
+ import { bitbucketChannel } from "@cursor/july/channels/bitbucket";
3816
+
3817
+ export default bitbucketChannel({});
3818
+ ```
3819
+
3820
+ Without `onPullRequest`, new pull requests start turns. Data Center updates
3821
+ also start turns when the source branch has new commits. Comments and pushes
3822
+ are opt-in through `onPullRequestComment` and `onPush`. Use `onEvent` for
3823
+ other event types. Set `webhookEvents` when managed event delivery must
3824
+ subscribe to an event that only `onEvent` handles.
3825
+
3826
+ Filter comment hooks by author before starting a turn. This prevents comments
3827
+ posted by the agent from triggering another turn.
3828
+
3829
+ `ctx.bitbucket.api` can read pull requests, post comments, and create build
3830
+ statuses. See the [Channels reference](/docs/reference/channels.md) for hook
3831
+ return values and session behavior.
3832
+
3833
+ ## Connect Bitbucket
3834
+
3835
+ Set a repository hook secret and API token:
3836
+
3837
+ ```bash
3838
+ BITBUCKET_WEBHOOK_SECRET=...
3839
+ BITBUCKET_TOKEN=...
3840
+ ```
3841
+
3842
+ Add a repository webhook for
3843
+ `https://<your-host>/<slug>/v1/channels/bitbucket`. Use the same secret on
3844
+ both sides. The channel verifies the `X-Hub-Signature` HMAC before it parses
3845
+ the payload.
3846
+
3847
+ Bitbucket Cloud uses its 2.0 API by default. Data Center also needs its REST
3848
+ API base:
3849
+
3850
+ ```ts
3851
+ export default bitbucketChannel({
3852
+ apiBaseUrl: "https://bitbucket.example.com/rest/api/1.0",
3853
+ repos: ["PLATFORM/api"],
3854
+ });
3855
+ ```
3856
+
3857
+ You can set `BITBUCKET_API_BASE_URL` instead.
3858
+
3859
+ ## Test locally
3860
+
3861
+ Start the agent, then replay a pull request you can read:
3862
+
3863
+ ```bash
3864
+ agent-sdk dev
3865
+ agent-sdk bitbucket replay \
3866
+ https://bitbucket.example.com/projects/PLATFORM/repos/api/pull-requests/42
3867
+ ```
3868
+
3869
+ Replay supports Bitbucket Cloud and Data Center URLs. `BITBUCKET_TOKEN` needs
3870
+ pull request read access. The command reads the pull request, creates a payload
3871
+ in the matching dialect, and sends it through the same channel route. Use
3872
+ `--events '*'` to replay the supported events declared by the channel. Use
3873
+ `--dry-run --out fixtures/bitbucket` to save fixtures.
3874
+
3875
+ ```bash
3876
+ agent-sdk bitbucket events --dir .
3877
+ agent-sdk bitbucket forward --dir .
3878
+ ```
3879
+
3880
+ `forward` prints the repository-hook and HTTPS tunnel setup for live
3881
+ deliveries.
3882
+
3883
+ ## Related
3884
+
3885
+ - [Channels reference](/docs/reference/channels.md)
3886
+ - [Webhooks and custom channels](/docs/guides/webhooks.md)
3887
+ - [Evals](/docs/evals.md)
3888
+
3889
+ ---
3890
+
3819
3891
  Source: /docs/guides/cloud-agents.md
3820
3892
 
3821
3893
  # Cursor cloud agents
@@ -3849,9 +3921,10 @@ export default cursorCloudAgents({
3849
3921
  });
3850
3922
  ```
3851
3923
 
3852
- Launches run under the host's signed-in account (`agent-sdk login` or
3853
- `CURSOR_API_KEY`) and spend its cloud-agent budget. Name the
3854
- repositories the agent may target, and keep approval on when you can't.
3924
+ Launches run under the host's signed-in account (`agent-sdk login`,
3925
+ `CURSOR_API_KEY`, or `CURSOR_SERVICE_ACCOUNT_KEY`) and spend its
3926
+ cloud-agent budget. Name the repositories the agent may target, and
3927
+ keep approval on when you can't.
3855
3928
 
3856
3929
  Mount it on an agent that runs on the local runtime; that is where
3857
3930
  launches park for approval. On the cloud runtime approvals don't
@@ -4105,7 +4178,8 @@ The companion skill for coding agents is
4105
4178
  Connect GitHub in Cursor for the repositories you care about (Settings or
4106
4179
  [cursor.com/dashboard](https://cursor.com/dashboard)). That gives your
4107
4180
  account access and lets Cursor receive the repo's webhooks. Sign the host
4108
- in (`agent-sdk login` or `CURSOR_API_KEY`), then opt the channel into the
4181
+ in (`agent-sdk login`, `CURSOR_API_KEY`, or
4182
+ `CURSOR_SERVICE_ACCOUNT_KEY`), then opt the channel into the
4109
4183
  Cursor account connection:
4110
4184
 
4111
4185
  ```ts
@@ -4165,6 +4239,37 @@ snapshot in the wake.
4165
4239
  This is the preferred production path: no public URL, no repo admin
4166
4240
  webhook, and no inbound network for GitHub deliveries.
4167
4241
 
4242
+ ## Connect GitHub Enterprise Server
4243
+
4244
+ GitHub Enterprise Server uses the same `githubChannel()` hooks and normalized
4245
+ events. Connect it through the direct webhook route. Set the REST API base and
4246
+ credentials for your server:
4247
+
4248
+ ```ts
4249
+ export default githubChannel({
4250
+ api: {
4251
+ apiBaseUrl: "https://github.example.com/api/v3",
4252
+ },
4253
+ credentials: {
4254
+ token: () => process.env.GITHUB_TOKEN,
4255
+ webhookSecret: () => process.env.GITHUB_WEBHOOK_SECRET,
4256
+ },
4257
+ onPullRequest: (ctx, pr) =>
4258
+ pr.action === "opened" ? { auth: defaultGitHubAuth(ctx) } : null,
4259
+ });
4260
+ ```
4261
+
4262
+ Add a repository webhook for
4263
+ `https://<your-host>/<slug>/v1/channels/github`. Use the same webhook secret
4264
+ on the server and in `GITHUB_WEBHOOK_SECRET`. The channel verifies
4265
+ `X-Hub-Signature-256` before it parses the payload.
4266
+
4267
+ Cursor account event pull, `github replay`, and `github forward` target
4268
+ GitHub.com. Test Enterprise Server integrations by posting saved webhook
4269
+ fixtures to a local `--dev` server. Leave `GITHUB_WEBHOOK_SECRET` unset for
4270
+ this local test so the channel admits unsigned loopback deliveries. Use a real
4271
+ delivery from your server so the fixture matches its version.
4272
+
4168
4273
  ## Define the channel
4169
4274
 
4170
4275
  Author `agent/channels/github.ts` with `githubChannel()` from
@@ -4372,6 +4477,103 @@ key. Handlers you author replace the matching defaults (same as
4372
4477
 
4373
4478
  ---
4374
4479
 
4480
+ Source: /docs/guides/gitlab.md
4481
+
4482
+ # GitLab agents
4483
+
4484
+ Use `gitlabChannel()` for GitLab.com and self-managed GitLab. The channel
4485
+ verifies project hooks, normalizes their payloads, and gives each hook a
4486
+ project-bound `ctx.gitlab` API client.
4487
+
4488
+ ## Define the channel
4489
+
4490
+ Author `agent/channels/gitlab.ts`:
4491
+
4492
+ ```ts
4493
+ import { gitlabChannel } from "@cursor/july/channels/gitlab";
4494
+
4495
+ export default gitlabChannel({});
4496
+ ```
4497
+
4498
+ Without `onMergeRequest`, new and reopened merge requests start turns.
4499
+ Updates start turns only when they include new commits. Notes, pushes, and
4500
+ pipelines are opt-in through `onNote`, `onPush`, and `onPipeline`.
4501
+ Use `onEvent` for other GitLab event types. Set `webhookEvents` when managed
4502
+ event delivery must subscribe to an event that only `onEvent` handles.
4503
+
4504
+ Filter note hooks by author before starting a turn. This prevents comments
4505
+ posted by the agent from triggering another turn.
4506
+
4507
+ `ctx.gitlab` can call the project REST API and create commit statuses. See the
4508
+ [Channels reference](/docs/reference/channels.md) for hook return values and
4509
+ session behavior.
4510
+
4511
+ ## Connect GitLab
4512
+
4513
+ On Cursor-managed hosting, use the signed-in Cursor account:
4514
+
4515
+ ```ts
4516
+ export default gitlabChannel({
4517
+ cursorAccount: {
4518
+ projects: ["acme/platform"],
4519
+ },
4520
+ });
4521
+ ```
4522
+
4523
+ For direct webhooks, set a project hook secret and API token:
4524
+
4525
+ ```bash
4526
+ GITLAB_WEBHOOK_SECRET=...
4527
+ GITLAB_TOKEN=...
4528
+ ```
4529
+
4530
+ Add a project webhook for
4531
+ `https://<your-host>/<slug>/v1/channels/gitlab`. Use the same value for the
4532
+ GitLab secret token and `GITLAB_WEBHOOK_SECRET`. The channel checks
4533
+ `X-Gitlab-Token` before it parses the payload.
4534
+
4535
+ Self-managed GitLab also needs its REST API base:
4536
+
4537
+ ```ts
4538
+ export default gitlabChannel({
4539
+ apiBaseUrl: "https://gitlab.example.com/api/v4",
4540
+ projects: ["acme/platform"],
4541
+ });
4542
+ ```
4543
+
4544
+ You can set `GITLAB_API_BASE_URL` instead.
4545
+
4546
+ ## Test locally
4547
+
4548
+ Start the agent, then replay a merge request you can read:
4549
+
4550
+ ```bash
4551
+ agent-sdk dev
4552
+ agent-sdk gitlab replay \
4553
+ https://gitlab.example.com/acme/platform/-/merge_requests/42
4554
+ ```
4555
+
4556
+ `GITLAB_TOKEN` needs API read access. Replay reads the merge request,
4557
+ creates GitLab-shaped payloads, and sends them through the same channel route.
4558
+ Use `--events '*'` to replay the supported events declared by the channel.
4559
+ Use `--dry-run --out fixtures/gitlab` to save fixtures.
4560
+
4561
+ ```bash
4562
+ agent-sdk gitlab events --dir .
4563
+ agent-sdk gitlab forward --dir .
4564
+ ```
4565
+
4566
+ GitLab has no local webhook relay. `forward` prints the project-hook and HTTPS
4567
+ tunnel setup for live deliveries.
4568
+
4569
+ ## Related
4570
+
4571
+ - [Channels reference](/docs/reference/channels.md)
4572
+ - [Webhooks and custom channels](/docs/guides/webhooks.md)
4573
+ - [Evals](/docs/evals.md)
4574
+
4575
+ ---
4576
+
4375
4577
  Source: /docs/guides/grokbot-agents.md
4376
4578
 
4377
4579
  # Cursor Grok Bot agents
@@ -4403,8 +4605,8 @@ export default cursorGrokBotAgents({
4403
4605
 
4404
4606
  `agents` is the allowlist. List every Grok Bot the model may talk to;
4405
4607
  a name outside the list is refused. Calls run under the host's
4406
- signed-in account (`agent-sdk login` or `CURSOR_API_KEY`). Keep
4407
- approval on when you can't.
4608
+ signed-in account (`agent-sdk login`, `CURSOR_API_KEY`, or
4609
+ `CURSOR_SERVICE_ACCOUNT_KEY`). Keep approval on when you can't.
4408
4610
 
4409
4611
  Start from the [Grok Bot agents template](/docs/templates/grokbot-agents.md)
4410
4612
  when you want a project that already mounts this.
@@ -4596,6 +4798,102 @@ Continue with these pages:
4596
4798
 
4597
4799
  ---
4598
4800
 
4801
+ Source: /docs/guides/improve.md
4802
+
4803
+ # Self-improvement
4804
+
4805
+ The `improve` extension lets an agent change how it behaves by changing
4806
+ its own source. Someone asks it to answer more briefly, to stop reaching
4807
+ for a tool, or to learn a procedure. The agent writes a brief, a one-off
4808
+ Cursor cloud agent makes the change on a branch and opens a pull
4809
+ request, and the agent reports that pull request. Nothing changes until
4810
+ a person reviews it and deploys.
4811
+
4812
+ The tree the agent runs from is never edited; nothing changed there
4813
+ survives a deploy. The pull request is the only path.
4814
+
4815
+ ## Mount it
4816
+
4817
+ The extension ships with `@cursor/july`. It relies on a
4818
+ [`cursor-cloud-agents`](/docs/guides/cloud-agents.md) mount beside it, with the
4819
+ agent's own repository among its `cloud.repos`.
4820
+
4821
+ ```ts
4822
+ // agent/extensions/cloud.ts
4823
+ import cursorCloudAgents from "@cursor/july/extensions/cursor-cloud-agents";
4824
+
4825
+ export default cursorCloudAgents({
4826
+ cloud: {
4827
+ repos: [{ url: "https://github.com/acme/agents", startingRef: "main" }],
4828
+ },
4829
+ });
4830
+ ```
4831
+
4832
+ ```ts
4833
+ // agent/extensions/improve.ts
4834
+ import improve from "@cursor/july/extensions/improve";
4835
+
4836
+ export default improve({
4837
+ repo: "https://github.com/acme/agents",
4838
+ path: "agents/concierge",
4839
+ });
4840
+ ```
4841
+
4842
+ `repo` is the repository that holds the agent's source and `path` is
4843
+ the agent's directory in it, the same values `agent-sdk deploy --repo`
4844
+ and `--path` take. Mount it as `improve`: the skill then reads
4845
+ `improve__yourself`, which is what a person asks for.
4846
+
4847
+ An agent on the
4848
+ [cloud runtime](/docs/reference/agent-config.md#choose-a-runtime) already
4849
+ runs each turn on a VM with the repository checked out. It mounts
4850
+ `improve` alone, and the skill has it branch in that checkout instead of
4851
+ launching a second agent.
4852
+
4853
+ ## What the agent does
4854
+
4855
+ With the extension mounted, "change how you work" becomes a procedure:
4856
+
4857
+ - **Locate the change.** The skill maps the ask onto the Agent SDK tree:
4858
+ instructions for the always-on prompt, a skill for a procedure, a tool
4859
+ for behavior that must be right every time, `agent.ts` for the model.
4860
+ - **Brief a cloud agent.** The agent writes a self-contained brief: who
4861
+ is asking and why, the repository, ref, and path, the files it expects
4862
+ to change, your notes about the tree, and the rules. Follow the
4863
+ repository's conventions, keep the diff to the change, run the
4864
+ repository's checks, open a pull request against the deployed ref,
4865
+ never merge.
4866
+ - **Follow up and report.** It gives the person the cloud agent's URL,
4867
+ checks on the run, reads the pull request before repeating its claims,
4868
+ and reports the pull request, not the delegation. It says the change
4869
+ takes effect at the next deploy, never that it has changed already.
4870
+
4871
+ Ask it directly. "Improve yourself: stop opening with a summary" is
4872
+ enough.
4873
+
4874
+ ## Configure it
4875
+
4876
+ | Option | What it controls |
4877
+ | --- | --- |
4878
+ | `repo` | The repository holding the agent's source. One of the cloud mount's `cloud.repos`. |
4879
+ | `path` | The agent's directory in the repository, as `agent-sdk deploy --path` names it. |
4880
+ | `ref` | The ref the deployment tracks. Branches start there and pull requests target it. Defaults to `main`. |
4881
+ | `notes` | What a coding agent must know about this tree beyond the standard layout: a house skill format, a manifest that forces a reinstall, a pull request convention. Copied into every brief. |
4882
+ | `deploy` | Who deploys a merged change and when it is live. Defaults to "It takes effect at the next deploy." |
4883
+
4884
+ ## What's next
4885
+
4886
+ Continue with these pages:
4887
+
4888
+ - [Cursor cloud agents](/docs/guides/cloud-agents.md): the mount that does the
4889
+ coding, and how to gate its launches
4890
+ - [Extensions](/docs/reference/extensions.md): how mounts, namespaces, and
4891
+ overrides work
4892
+ - [Deployment](/docs/deployment.md): how a merged change reaches the
4893
+ running agent
4894
+
4895
+ ---
4896
+
4599
4897
  Source: /docs/guides/mcp-oauth.md
4600
4898
 
4601
4899
  # Host MCP OAuth
@@ -5154,7 +5452,8 @@ agent-sdk slack create --dir .
5154
5452
  agent-sdk slack create --dir . --prod
5155
5453
  ```
5156
5454
 
5157
- Sign in first (`agent-sdk login` or `CURSOR_API_KEY`). Finish **Add
5455
+ Sign in first (`agent-sdk login` or `CURSOR_API_KEY`). A team
5456
+ service-account key cannot create Slack apps. Finish **Add
5158
5457
  Slack to this agent** as the same Cursor account. Then run
5159
5458
  `agent-sdk slack doctor --prefix MY_AGENT` and serve:
5160
5459
 
@@ -5290,7 +5589,8 @@ A custom channel gives the agent its own HTTP surface. You get routes
5290
5589
  with validated payloads, sessions keyed to something in your domain (a
5291
5590
  thread, a ticket, a PR), and replies delivered back to the caller. The
5292
5591
  [Slack](/docs/guides/slack.md) and [GitHub](/docs/guides/github.md) packs build on this
5293
- mechanism. This page is the mechanism itself.
5592
+ mechanism. The [GitLab](/docs/guides/gitlab.md) and [Bitbucket](/docs/guides/bitbucket.md) packs
5593
+ use it too. This page is the mechanism itself.
5294
5594
 
5295
5595
  ## What you already have
5296
5596
 
@@ -5744,7 +6044,8 @@ from any PR you can read. See the [GitHub guide](/docs/guides/github.md).
5744
6044
  Continue with these pages:
5745
6045
 
5746
6046
  - [Channels reference](/docs/reference/channels.md): the full authoring API
5747
- - [GitHub](/docs/guides/github.md) and [Slack](/docs/guides/slack.md): the packaged channels
6047
+ - [GitHub](/docs/guides/github.md), [GitLab](/docs/guides/gitlab.md),
6048
+ [Bitbucket](/docs/guides/bitbucket.md), and [Slack](/docs/guides/slack.md): the packaged channels
5748
6049
  - [Sessions and streaming](/docs/reference/sessions.md): events your
5749
6050
  channel can subscribe to
5750
6051
 
@@ -5821,9 +6122,9 @@ Pin the input first. A moving fixture is noise. For GitHub agents, use `agent-sd
5821
6122
 
5822
6123
  ## How do I lock a hillclimb improvement with an eval?
5823
6124
 
5824
- Every kept change needs an eval that would have failed before the change: a tool-choice gate, an `action.result` count bound, or an output-shape check. Run `agent-sdk eval --dir . --json` between rounds. Never weaken an existing gate to pass the round.
6125
+ Every kept change needs an eval that would have failed before the change: a tool-choice gate, a `maxToolCalls` bound, or an output-shape check. Run `agent-sdk eval --dir . --json` between rounds. Never weaken an existing gate to pass the round.
5825
6126
 
5826
- Details live in [Evals](/docs/evals.md). The evals skill will author the case with you.
6127
+ Details live in [Keep improvements with regression evals](/docs/evals.md#keep-improvements-with-regression-evals). The evals skill will author the case with you.
5827
6128
 
5828
6129
  ## What habits help hillclimbing stay reliable?
5829
6130
 
@@ -5878,13 +6179,16 @@ npx @cursor/july docs
5878
6179
  | Turning a Cursor Automation into a project | [Convert a Cursor Automation](/docs/guides/convert-automation.md) |
5879
6180
  | Wiring an agent to Slack | [Slack guide](/docs/guides/slack.md) |
5880
6181
  | Starting from a packaged template | [Demo](/docs/templates/demo.md), [Grok Bot agents](/docs/templates/grokbot-agents.md), [Code wiki](/docs/templates/code-wiki.md), [Living AGENTS.md](/docs/templates/agents-md.md), [Security reviewer](/docs/templates/security-reviewer.md), [Security help](/docs/templates/security-help.md), [Triage](/docs/templates/triage.md), or [Agentic Owners](/docs/templates/agentic-owners.md) |
5881
- | Wiring an agent to GitHub webhooks | [GitHub guide](/docs/guides/github.md) |
6182
+ | Wiring an agent to GitHub or GitHub Enterprise Server | [GitHub guide](/docs/guides/github.md) |
6183
+ | Wiring an agent to GitLab | [GitLab guide](/docs/guides/gitlab.md) |
6184
+ | Wiring an agent to Bitbucket | [Bitbucket guide](/docs/guides/bitbucket.md) |
5882
6185
  | Driving PRs from a cloud VM | [PR autofixer template](/docs/templates/pr-autofixer.md) |
5883
6186
  | Handing coding work to Cursor cloud agents | [Cursor cloud agents](/docs/guides/cloud-agents.md) |
5884
6187
  | Talking to your Grok Bot agents | [Cursor Grok Bot agents](/docs/guides/grokbot-agents.md) |
6188
+ | Letting an agent change its own source | [Self-improvement](/docs/guides/improve.md) |
5885
6189
  | Driving an agent from Linear (or another tracker) | [Webhooks guide: Linear example](/docs/guides/webhooks.md#example-linear-as-the-control-plane) |
5886
6190
  | Making an existing agent measurably better | [Evals](/docs/evals.md), then [Hillclimbing](/docs/hillclimbing.md) |
5887
- | Comparing variants on live traffic | [Live A/B metrics](/docs/ab.md) |
6191
+ | Gating agent behavior in CI | [Run evals in CI](/docs/evals.md#run-evals-in-ci) |
5888
6192
  | Deploying with Cursor or on your own infrastructure | [Deployment](/docs/deployment.md) |
5889
6193
  | Debugging something that misbehaves | [Fix common agent problems](/docs/troubleshooting.md) |
5890
6194
 
@@ -5925,10 +6229,8 @@ npx @cursor/july docs
5925
6229
 
5926
6230
  - [Building agents with agents](/docs/building-with-agents.md): use a coding
5927
6231
  agent to scaffold, run, and iterate on your agent.
5928
- - [Evals](/docs/evals.md): author `defineEval` cases, pick fixtures, and use
5929
- evals as regression checks.
5930
- - [Live A/B metrics](/docs/ab.md): assign sticky variants and compare
5931
- cumulative metrics on live sessions.
6232
+ - [Evals](/docs/evals.md): author `defineEval` cases, assert over the
6233
+ trajectory, and run them locally and in CI.
5932
6234
  - [Storage](/docs/storage.md): point durable storage at a backend you own
5933
6235
  with `defineStorage`.
5934
6236
  - [Hillclimbing](/docs/hillclimbing.md): measure and improve an agent
@@ -5940,6 +6242,10 @@ npx @cursor/july docs
5940
6242
  its own HTTP surface.
5941
6243
  - [GitHub](/docs/guides/github.md): trigger the agent from pull requests,
5942
6244
  CI, and comments.
6245
+ - [GitLab](/docs/guides/gitlab.md): trigger the agent from merge requests,
6246
+ notes, pipelines, and pushes.
6247
+ - [Bitbucket](/docs/guides/bitbucket.md): trigger the agent from pull requests,
6248
+ comments, and pushes.
5943
6249
  - [Slack](/docs/guides/slack.md): put the agent in Slack over Socket Mode.
5944
6250
  - [Human-in-the-loop approvals](/docs/guides/human-in-the-loop.md): park a
5945
6251
  tool call until a person signs off.
@@ -5952,6 +6258,9 @@ npx @cursor/july docs
5952
6258
  and steer cloud agents through the `cursor-cloud-agents` extension.
5953
6259
  - [Cursor Grok Bot agents](/docs/guides/grokbot-agents.md): let the agent
5954
6260
  talk to Grok Bot agents through the `cursor-grokbot-agents` extension.
6261
+ - [Self-improvement](/docs/guides/improve.md): let the agent change its own
6262
+ source through a one-off cloud agent and a pull request with the
6263
+ `improve` extension.
5955
6264
  - [OpenTelemetry](/docs/guides/opentelemetry.md): push session, turn, and
5956
6265
  tool traces to an OTLP collector you run.
5957
6266
 
@@ -6613,8 +6922,11 @@ console.log(`listening on ${handle.url}`);
6613
6922
  Host settings match the documented [CLI](/docs/reference/cli.md) `serve` flags.
6614
6923
  `serve()` also accepts `discovery` (project-loading options) and
6615
6924
  `mode: "single" | "multi"`. The Cursor credential resolves in one order
6616
- everywhere: explicit `apiKey`, then `CURSOR_API_KEY`, then the key
6617
- stored by `agent-sdk login`.
6925
+ everywhere: explicit `apiKey`, then `CURSOR_API_KEY`, then
6926
+ `CURSOR_API_KEY_FILE` (hosted default `/run/cursor/secrets/CURSOR_API_KEY`
6927
+ when unset), then `CURSOR_SERVICE_ACCOUNT_KEY`, then the key stored by
6928
+ `agent-sdk login`. On a host that has both the service-account key and a
6929
+ bind file, the file principal wins.
6618
6930
 
6619
6931
  ## What's next
6620
6932
 
@@ -6750,7 +7062,8 @@ Source: /docs/reference/channels.md
6750
7062
  A channel is the surface an agent lives on. The built-in HTTP session
6751
7063
  channel is always mounted. Custom channels declare their own routes
6752
7064
  under `/v1/channels/<id>`. The Slack and GitHub packs are prebuilt
6753
- channels with platform transports. This page is the authoring reference;
7065
+ channels with platform transports. GitLab and Bitbucket packs cover their
7066
+ hosted and self-managed products. This page is the authoring reference;
6754
7067
  for the walkthrough, see the [Webhooks guide](/docs/guides/webhooks.md).
6755
7068
 
6756
7069
  ## Built-in HTTP channel
@@ -6876,7 +7189,7 @@ Handlers receive the Fetch `Request` and an args object:
6876
7189
  `workspaceFiles`, `workspaceDir`, `cloud` (attach cloud repos for this
6877
7190
  session), `auth` (defaults to the request principal), `state` (starting
6878
7191
  channel state for new sessions), `title` (session display title), and
6879
- `purpose` (`"eval"` skips sticky A/B enrollment).
7192
+ `purpose` (`"eval"` marks the session as regression traffic).
6880
7193
 
6881
7194
  ## Events
6882
7195
 
@@ -6957,7 +7270,19 @@ model turn), `{ task }` (host work), or `null`, and CLI tooling for
6957
7270
  replay and live forwarding. Author `agent/channels/github.ts` with
6958
7271
  `githubChannel()`. Opt-in `progress.commitStatus` and `progress.banner`
6959
7272
  converge a merge-box check and sticky PR comment from default stream
6960
- events. Guide: [GitHub](/docs/guides/github.md).
7273
+ events. Supports GitHub.com and GitHub Enterprise Server. Guide:
7274
+ [GitHub](/docs/guides/github.md).
7275
+
7276
+ **GitLab** (`@cursor/july/channels/gitlab`): verified project hooks for
7277
+ merge requests, notes, pipelines, pushes, and custom event types. Supports
7278
+ GitLab.com and self-managed GitLab. Author `agent/channels/gitlab.ts` with
7279
+ `gitlabChannel()`. Guide: [GitLab](/docs/guides/gitlab.md).
7280
+
7281
+ **Bitbucket** (`@cursor/july/channels/bitbucket`): verified repository hooks
7282
+ for pull requests, comments, pushes, and custom event types. Supports
7283
+ Bitbucket Cloud and Bitbucket Data Center through one normalized hook API.
7284
+ Author `agent/channels/bitbucket.ts` with `bitbucketChannel()`. Guide:
7285
+ [Bitbucket](/docs/guides/bitbucket.md).
6961
7286
 
6962
7287
  **Deployments** (`@cursor/july/channels/deployments`): pull deploy
6963
7288
  events. Declare `events` and handle each one in `onEvent`. Each event
@@ -7122,7 +7447,7 @@ the agent project. Slugged mounts get a subdirectory named for the slug.
7122
7447
  | `--host` | Bind this host. The default is loopback-only `127.0.0.1`. |
7123
7448
  | `--dev` | Disable automatic schedule and reminder firing, admit unsigned loopback GitHub deliveries, and widen playground session access on loopback. |
7124
7449
  | `--mode` | Use `multi` for slugged routes and an index, or `single` for one agent at the unslugged `/v1/*`. The default is `multi`. |
7125
- | `--api-key` | Use this Cursor API key. The command falls back to `CURSOR_API_KEY`, then the stored login. |
7450
+ | `--api-key` | Use this Cursor API key. Otherwise the command uses `CURSOR_API_KEY`, then `CURSOR_API_KEY_FILE` (hosted default `/run/cursor/secrets/CURSOR_API_KEY` when unset), then `CURSOR_SERVICE_ACCOUNT_KEY`, then the stored login. |
7126
7451
  | `--state-root` | Store sessions, streams, workspaces, and channel state here. Keep durable production state outside the agent repository. |
7127
7452
  | `--bearer-token` | Require this bearer token on routes without authored auth. Mutually exclusive with `--allow-anonymous`. |
7128
7453
  | `--allow-anonymous` | Admit every caller as one `anonymous` principal. Use only behind a trusted network boundary. |
@@ -7379,8 +7704,10 @@ agent-sdk eval status <evalId> --prod --slug pr-approver
7379
7704
  agent-sdk eval cancel <evalId> --prod --slug pr-approver
7380
7705
  ```
7381
7706
 
7382
- `eval` runs `evals/**/*.eval.{ts,js}` on an ephemeral server or against
7383
- `--url`. Select one or more exact case IDs, file ID prefixes, or tags.
7707
+ `eval` runs `evals/**/*.eval.{ts,js}` on an ephemeral server. With
7708
+ `--prod` or `--url`, the target server runs its own evals as a
7709
+ server-side batch. Select one or more exact case IDs, file ID prefixes,
7710
+ or tags.
7384
7711
  Omit selectors to run all cases. Repeated `--tag` flags use OR matching.
7385
7712
 
7386
7713
  An eval run requires `evals/evals.config.{ts,js}` with `maxConcurrency`
@@ -7391,13 +7718,13 @@ between 1 and 200. Timeout priority is the case's `timeoutMs`, the CLI's
7391
7718
  | --- | --- |
7392
7719
  | `--list` | Print discovered cases without running. `--list --json` prints them as an array. |
7393
7720
  | `--tag <tag>` | Run cases with this tag. Repeated flags use OR matching. |
7394
- | `--json` | Print `{ ok, passed, failed, results }`. |
7721
+ | `--json` | Print `{ ok, passed, failed, scored, skipped, strict, results }`; see [Run evals in CI](/docs/evals.md#run-evals-in-ci) for the result shape. |
7395
7722
  | `--verbose` | Stream `t.log` lines and reply snippets. |
7396
7723
  | `--no-stream` | Hide live progress on stderr. |
7397
7724
  | `--strict` | Exit `1` when a scored case misses a soft threshold. |
7398
7725
  | `--max-concurrency <n>` | Override `maxConcurrency` from `evals.config.ts`. |
7399
7726
  | `--junit <path>` | Write JUnit XML for CI annotations. |
7400
- | `--artifacts <dir>` | Write run artifacts here. The default is a timestamped directory under `<state-root>/evals/`. |
7727
+ | `--artifacts <dir>` | Write run artifacts here. The default is a timestamped directory under `evals/` in the project state directory (not affected by `--state-root`). |
7401
7728
  | `--no-artifacts` | Skip run artifacts. |
7402
7729
  | `--skip-report` | Ignore reporters from `evals.config.ts` and eval files. |
7403
7730
  | `--out <path>` | Also write the full results JSON to this path (also for `eval status <evalId>`). |
@@ -7570,10 +7897,14 @@ runtime, and Cursor account MCP connections. `--key-name` changes the
7570
7897
  name of a browser-minted key. `login --api-key` validates and stores a
7571
7898
  key you already created.
7572
7899
 
7573
- `whoami` shows which credential is active and why. `CURSOR_API_KEY`
7574
- takes precedence over the stored login. `logout` removes the local
7575
- credential file but doesn't revoke the API key. Revoke it in the Cursor
7576
- dashboard when it should stop working.
7900
+ `whoami` shows which credential is active and why. An explicit key
7901
+ wins, then `CURSOR_API_KEY`, then `CURSOR_API_KEY_FILE` (hosted default
7902
+ `/run/cursor/secrets/CURSOR_API_KEY` when unset), then
7903
+ `CURSOR_SERVICE_ACCOUNT_KEY`, then the stored login. A host that has
7904
+ both the service-account key and a bind file authenticates as the file
7905
+ principal. `logout` removes the local credential file but doesn't
7906
+ revoke the API key. Revoke it in the Cursor dashboard when it should
7907
+ stop working.
7577
7908
 
7578
7909
  Login and account RPCs honor `CURSOR_API_BASE_URL`. The SDK harness
7579
7910
  honors `CURSOR_BACKEND_URL`. Set both to the same URL, or keys minted
@@ -7611,9 +7942,9 @@ agent-sdk deploy [--dir <path>] [--slug <slug> | --all] [--team <id>]
7611
7942
  [--no-wait] [--json]
7612
7943
  ```
7613
7944
 
7614
- Managed hosting requires team-admin permission and the team's
7615
- cloud-agent entitlement. `--team` defaults to the signed-in account's
7616
- team.
7945
+ Managed hosting requires team membership and the team's cloud-agent
7946
+ entitlement. A team service-account API key with agent access can
7947
+ deploy. `--team` defaults to the signed-in account's team.
7617
7948
 
7618
7949
  For a single project, the slug defaults to a normalized version of the
7619
7950
  directory name. Deployment slugs contain lowercase letters, digits, `_`,
@@ -7856,8 +8187,9 @@ checklist. It doesn't change files.
7856
8187
  `slack create` opens the signed-in Cursor dashboard wizard. Finish Slack
7857
8188
  consent and the bot name there. The CLI writes the token pair into
7858
8189
  `<dir>/.env.local` and runs `doctor`. It requires a signed-in host
7859
- (`agent-sdk login` or `CURSOR_API_KEY`). `--prod` provisions the
7860
- production app; the default is the development app. `--name` / `--icon`
8190
+ (`agent-sdk login` or `CURSOR_API_KEY`). A team service-account key
8191
+ cannot create Slack apps. `--prod` provisions the production app; the
8192
+ default is the development app. `--name` / `--icon`
7861
8193
  / `--channel-posts` prefill the wizard. A second create for the same
7862
8194
  slug and env overwrites the live Slack app. If Slack needs a workspace
7863
8195
  admin's approval, the wizard waits; keep the CLI running, open Slack's
@@ -7970,7 +8302,9 @@ These environment variables affect the CLI and its channel packs.
7970
8302
 
7971
8303
  | Variable | Meaning |
7972
8304
  | --- | --- |
7973
- | `CURSOR_API_KEY` | Cursor credential. It takes precedence over the stored login. |
8305
+ | `CURSOR_API_KEY` | Cursor credential. It takes precedence over `CURSOR_API_KEY_FILE`, `CURSOR_SERVICE_ACCOUNT_KEY`, and the stored login. |
8306
+ | `CURSOR_API_KEY_FILE` | Path to a Cursor credential file. Used when `CURSOR_API_KEY` is unset. When this variable is unset, the hosted default `/run/cursor/secrets/CURSOR_API_KEY` is tried. A present file takes precedence over `CURSOR_SERVICE_ACCOUNT_KEY` and the stored login. |
8307
+ | `CURSOR_SERVICE_ACCOUNT_KEY` | Team service-account credential. Used when `CURSOR_API_KEY` and `CURSOR_API_KEY_FILE` (including the hosted default path) are unset. It takes precedence over the stored login. |
7974
8308
  | `CURSOR_API_BASE_URL` | Backend used by login, account, deployment, and event-relay RPCs. |
7975
8309
  | `CURSOR_BACKEND_URL` | Backend used by the Cursor SDK harness. |
7976
8310
  | `AGENT_SERVE_CONFIG_DIR` | Directory for stored credentials and update-check state. Defaults to the CLI config directory under `~/.config`. |
@@ -8212,7 +8546,8 @@ Name the file `account.ts`. `cursor.ts` collides with the IDE `cursor`
8212
8546
  MCP namespace. `advertiseTools: true` puts connector tools on local
8213
8547
  turns by name. Without it they sit behind harness meta-tools.
8214
8548
 
8215
- The host must be signed in (`agent-sdk login` or `CURSOR_API_KEY`).
8549
+ The host must be signed in (`agent-sdk login`, `CURSOR_API_KEY`, or
8550
+ `CURSOR_SERVICE_ACCOUNT_KEY`).
8216
8551
  `serve` fails fast at startup otherwise, and logs each connector's live
8217
8552
  status (`connected`, `needsAuth`, `error`) as it starts.
8218
8553
 
@@ -8353,7 +8688,6 @@ namespace:
8353
8688
  | `subagents/<id>/` | subagent `<ns>__<id>` |
8354
8689
  | `instructions.md` / `.ts` / dir | appended to the agent's system prompt |
8355
8690
  | `sandbox/workspace/**` | seeded into each local session workspace |
8356
- | `ab.ts` / `ab/<name>.ts` | A/B experiment `<ns>__<name>` |
8357
8691
  | `artifacts.ts` | artifact kinds `<ns>__<kind>` |
8358
8692
 
8359
8693
  The root agent still needs its own `instructions.md`. Extension
@@ -8370,7 +8704,6 @@ or is outside discovery:
8370
8704
  | `agent.ts` | One `defineAgent` runtime per agent |
8371
8705
  | `storage.ts` | One `host.kv` / `host.files` backend |
8372
8706
  | `otel.ts` | One OTLP exporter |
8373
- | `ab.config.ts` | One experiment-platform config; `ab.ts` / `ab/` still merge |
8374
8707
  | `playground/` | Custom chips are a Vite glob of the agent tree, not a discovery walk |
8375
8708
  | `extensions/` | Nested mounts are not loaded |
8376
8709
  | `sandbox.ts` | Custom sandbox backends stay on the agent |
@@ -8411,7 +8744,6 @@ it alive.
8411
8744
  | `schedules/<name>.ts` | `disableSchedule()` |
8412
8745
  | `subagents/<id>.ts` | `disableSubagent()` |
8413
8746
  | `instructions.ts` | `disableInstructions()` |
8414
- | `ab.ts` / `ab/<name>.ts` | `disableAB()` |
8415
8747
  | `artifacts.ts` | `disableArtifacts()` |
8416
8748
 
8417
8749
  `disable()` is the same brand as the slot helpers above and works in
@@ -8559,6 +8891,8 @@ Continue with these pages:
8559
8891
  lets an agent launch and steer cloud agents
8560
8892
  - [Cursor Grok Bot agents](/docs/guides/grokbot-agents.md): the extension
8561
8893
  that lets an agent talk to Grok Bot agents
8894
+ - [Self-improvement](/docs/guides/improve.md): the extension that lets an
8895
+ agent change its own source through a cloud agent and a pull request
8562
8896
  - [Tools](/docs/reference/tools.md): the tool contract extensions contribute to
8563
8897
  - [Skills](/docs/reference/skills.md): the skill forms an extension can carry
8564
8898
  - [Project layout](/docs/reference/project-layout.md): where `agent/extensions/` sits
@@ -8653,7 +8987,7 @@ exported from `@cursor/july`.
8653
8987
 
8654
8988
  | Member | What it is |
8655
8989
  | --- | --- |
8656
- | `ctx.session` | Read-only session info: `id`, `channelId`, `mode` (`chat` or `task`), `purpose` (`live` or `eval`), `auth`, plus `title`, `sdkAgentId`, and `abs` when set |
8990
+ | `ctx.session` | Read-only session info: `id`, `channelId`, `mode` (`chat` or `task`), `purpose` (`live` or `eval`), `auth`, plus `title` and `sdkAgentId` when set |
8657
8991
  | `ctx.agent` | `{ name }` of the agent the event belongs to |
8658
8992
  | `ctx.channel` | `{ id, continuationToken }`. The token is `null` when the session can't take follow-ups |
8659
8993
  | `ctx.host.kv` | Durable JSON, shared by every session of the agent; the [storage backend](/docs/storage.md#author-kv-ctx-host-kv) decides whether it survives a hosted replace. Prefix keys with `ctx.session.id` for per-session state |
@@ -8684,17 +9018,17 @@ Each event reaches a hook at most once. A restart doesn't replay the log
8684
9018
  into hooks, so a mirror needs no dedupe, and the event log rather than
8685
9019
  the hook's copy is the source of truth.
8686
9020
 
8687
- ## Hooks, channel events, evals, or A/B?
9021
+ ## Hooks, channel events, or evals?
8688
9022
 
8689
9023
  All of them consume the same stream, for different jobs:
8690
9024
 
8691
- | | Hooks | Channel `events` | Evals | A/B (`defineAB`) |
8692
- | --- | --- | --- | --- | --- |
8693
- | Scope | every session of the agent | sessions the channel owns | one test turn | every live session; enrollment at creation, metrics on each turn |
8694
- | Job | observe: audit, metrics, mirrors, derived state | deliver: replies back to the channel's surface | assert: gates over the trajectory | compare: sticky arms, then fold the stream into `onSample` metrics |
8695
- | Context | `ctx.host`, `ctx.artifacts`, session info | `channel.state`, `setContinuationToken`, `ctx.host`, session info | the `t` assertion helpers | per-session samples in `onSample` |
8696
- | Can affect the run | no | yes, it owns the surface | n/a | yes through arm instructions or `session.abs`; collection is observe-only |
8697
- | Authored at | `agent/hooks/*.ts` | channel config | `evals/**/*.eval.ts` | [`agent/ab.ts` or `agent/ab/*.ts`](/docs/ab.md) |
9025
+ | | Hooks | Channel `events` | Evals |
9026
+ | --- | --- | --- | --- |
9027
+ | Scope | every session of the agent | sessions the channel owns | one test turn |
9028
+ | Job | observe: audit, metrics, mirrors, derived state | deliver: replies back to the channel's surface | assert: gates over the trajectory |
9029
+ | Context | `ctx.host`, `ctx.artifacts`, session info | `channel.state`, `setContinuationToken`, `ctx.host`, session info | the `t` assertion helpers |
9030
+ | Can affect the run | no | yes, it owns the surface | n/a |
9031
+ | Authored at | `agent/hooks/*.ts` | channel config | `evals/**/*.eval.ts` |
8698
9032
 
8699
9033
  ## When not to use a hook
8700
9034
 
@@ -8706,7 +9040,6 @@ All of them consume the same stream, for different jobs:
8706
9040
  | Block, approve, or rewrite a tool call | [`needsApproval`](/docs/reference/tools.md#gate-a-tool-on-human-approval) on the tool |
8707
9041
  | Act on the final assistant text, or fail a bad turn | [`defineResult`](/docs/reference/result.md) |
8708
9042
  | Gate a change on behavior | [Evals](/docs/evals.md) |
8709
- | Compare two prompts on live traffic | [`defineAB`](/docs/ab.md) |
8710
9043
 
8711
9044
  ## Patterns
8712
9045
 
@@ -8832,8 +9165,6 @@ Continue with these pages:
8832
9165
  - [Deployment](/docs/deployment.md#observability): runtime logs and export
8833
9166
  paths
8834
9167
  - [Channels](/docs/reference/channels.md#events): the delivery-side counterpart
8835
- - [Live A/B metrics](/docs/ab.md): sticky variants over the same event
8836
- stream
8837
9168
 
8838
9169
  ---
8839
9170
 
@@ -8988,12 +9319,11 @@ These read-only routes describe the running agent.
8988
9319
 
8989
9320
  | Route | What it does |
8990
9321
  | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
8991
- | `GET /v1/info` | The discovered surface: model, tools, skills, MCP connections, subagents, channels and routes (with schemas), schedules, hooks, A/B experiments, diagnostics |
9322
+ | `GET /v1/info` | The discovered surface: model, tools, skills, MCP connections, subagents, channels and routes (with schemas), schedules, hooks, diagnostics |
8992
9323
  | `GET /v1/tools` | The live tool catalog: authored server tools plus advertised MCP passthroughs under model-facing names, as light `{ name, title?, source? }` entries. `session` / `continuationToken` query parameters bind the listing to a session identity (advertised inventories can be tenant-scoped); a connection whose listing fails is skipped and reported in `connectionErrors` |
8993
9324
  | `GET /v1/tools/:name` | One catalog tool's full description: description, execution, `needsApproval`, `effect`, input and output schemas, source connection. Same session binding as the listing; unknown names get `404` with the available names |
8994
9325
  | `GET /v1/health` | Per-agent liveness, no auth |
8995
9326
  | `GET /v1/logs?after=N` | Recent server log lines, with a polling cursor |
8996
- | `GET /v1/abs` | [Live A/B metrics](/docs/ab.md): per-session assignments and aggregate arm totals |
8997
9327
 
8998
9328
  ## Artifacts
8999
9329
 
@@ -9052,7 +9382,7 @@ the final `completed` or `failed` status. Batch errors appear on the
9052
9382
  snapshot returned by the poll. Entries within `filterIds` and `tags`
9053
9383
  use OR semantics. When both fields are present, a case must match one
9054
9384
  entry from each field. Listed runs persist across restarts when storage is configured; see
9055
- [Storage](/docs/storage.md#eval-and-a-b-tables). Otherwise they are
9385
+ [Storage](/docs/storage.md#eval-table). Otherwise they are
9056
9386
  process-memory only.
9057
9387
 
9058
9388
  ## Dev-mode routes
@@ -9214,8 +9544,6 @@ Use the playground to chat, try channel routes, and inspect sessions.
9214
9544
  an [extension](/docs/reference/extensions.md) cannot contribute them.
9215
9545
  - **Raw events pane**: flip it on to inspect the event stream.
9216
9546
  - **Logs tab**: recent server log lines, polled from `GET /v1/logs`.
9217
- - **A/Bs tab**: per-session and aggregate
9218
- [live A/B metrics](/docs/ab.md) from `GET /v1/abs`.
9219
9547
 
9220
9548
  In multi-agent mode each agent has its own playground at
9221
9549
  `/<slug>/playground`, and `/` is an index of them all.
@@ -9238,8 +9566,6 @@ Continue with these pages:
9238
9566
  - [Sessions and streaming](/docs/reference/sessions.md): the streams it renders
9239
9567
  - [Human-in-the-loop](/docs/guides/human-in-the-loop.md): the approval
9240
9568
  buttons in context
9241
- - [Live A/B metrics](/docs/ab.md): the assignments and results in the A/Bs
9242
- tab
9243
9569
 
9244
9570
  ---
9245
9571
 
@@ -9253,8 +9579,7 @@ how the Agent SDK loads it.
9253
9579
 
9254
9580
  ## Folder structure
9255
9581
 
9256
- For the capabilities below, identity usually comes from the path. A/B
9257
- experiments can override their file-derived name.
9582
+ For the capabilities below, identity comes from the path.
9258
9583
 
9259
9584
  | Path | Resolves to |
9260
9585
  | --- | --- |
@@ -9266,8 +9591,6 @@ experiments can override their file-derived name.
9266
9591
  | `agent/extensions/ci.ts` | extension mount `ci`; its contributions become `ci__<name>` |
9267
9592
  | `agent/extensions/notion.ts` | Cursor plugin mount `notion` (`cursorPlugin`); its skills, agents, and MCP servers become `notion__<name>` |
9268
9593
  | `agent/channels/drive.ts` | channel `drive`, routes under `/v1/channels/drive` |
9269
- | `agent/ab.ts` | A/B experiment `ab` unless `name` overrides it |
9270
- | `agent/ab/concise.ts` | A/B experiment `concise` unless `name` overrides it |
9271
9594
 
9272
9595
  The root agent takes its name from `package.json` `name`, falling back
9273
9596
  to the directory name. When serving multiple agents, the slug is the
@@ -9319,8 +9642,6 @@ Each path maps to a capability and a reference page.
9319
9642
  | `agent/channels/*.ts` | HTTP surfaces beyond the built-in session API; `slack.ts` and `github.ts` use the platform packs | [Channels](/docs/reference/channels.md) |
9320
9643
  | `agent/hooks/*.ts` | Observe-only event subscribers, never fatal | [Hooks](/docs/reference/hooks.md) |
9321
9644
  | `agent/otel.ts` | `defineOtel` OTLP export (traces, metrics, optional logs) | [OpenTelemetry](/docs/guides/opentelemetry.md) |
9322
- | `agent/ab.ts`, `agent/ab/*.ts` | `defineAB` experiments with sticky variants and live metrics | [Live A/B metrics](/docs/ab.md) |
9323
- | `agent/ab.config.ts` | `defineABConfig` shared A/B settings | [Live A/B metrics](/docs/ab.md) |
9324
9645
  | `agent/storage.ts` | `defineStorage` backend for the durable `host.kv` / `host.files` APIs | [Storage](/docs/storage.md) |
9325
9646
  | `agent/artifacts.ts` | `defineArtifacts` kinds, the `tag_artifact` opt-in, and retention | [Artifacts](/docs/reference/artifacts.md) |
9326
9647
  | `agent/result.ts` | `defineResult` host `commit` on the final assistant text | [Turn result](/docs/reference/result.md) |
@@ -9357,8 +9678,6 @@ Continue with these pages:
9357
9678
 
9358
9679
  - [Agent config](/docs/reference/agent-config.md): the runtime config at the root
9359
9680
  - [Tools](/docs/reference/tools.md): add typed actions under `agent/tools/`
9360
- - [Live A/B metrics](/docs/ab.md): compare variants from `agent/ab.ts` or
9361
- `agent/ab/`
9362
9681
  - [Concepts](/docs/concepts.md): why the filesystem is the interface
9363
9682
 
9364
9683
  ---
@@ -9802,7 +10121,7 @@ within one session. The `at` field is an ISO-8601 timestamp.
9802
10121
 
9803
10122
  | Phase | Events | What they tell you |
9804
10123
  | --- | --- | --- |
9805
- | Session | `session.started`, [`ab.assigned`](/docs/ab.md#assign-sticky-variants), `session.waiting`, `session.completed`, `session.failed` | Session creation, A/B enrollment, readiness, and task completion |
10124
+ | Session | `session.started`, `session.waiting`, `session.completed`, `session.failed` | Session creation, readiness, and task completion |
9806
10125
  | Agent | `agent.bound` | Cloud conversation URL |
9807
10126
  | Input | `message.received` | A user message was accepted |
9808
10127
  | Turn | `turn.queued`, `turn.started`, `turn.completed`, `turn.failed` | Queue position under a [`maxRunningTurns` cap](/docs/reference/agent-config.md#concurrency), then turn status, final result, and token usage |
@@ -9886,7 +10205,6 @@ view.
9886
10205
 
9887
10206
  - [HTTP API](/docs/reference/http-api.md)
9888
10207
  - [Hooks](/docs/reference/hooks.md)
9889
- - [Live A/B metrics](/docs/ab.md)
9890
10208
  - [How the Agent SDK works](/docs/concepts.md)
9891
10209
 
9892
10210
  ---
@@ -10516,7 +10834,8 @@ Cursor checks the project in this order:
10516
10834
 
10517
10835
  Validation, discovery, direct server-tool calls, and server startup work
10518
10836
  without a Cursor credential. Model turns and evals need
10519
- `CURSOR_API_KEY` or a saved `agent-sdk login`.
10837
+ `CURSOR_API_KEY`, then `CURSOR_API_KEY_FILE` (hosted default `/run/cursor/secrets/CURSOR_API_KEY`), then `CURSOR_SERVICE_ACCOUNT_KEY`, then a saved
10838
+ `agent-sdk login`.
10520
10839
 
10521
10840
  ## What happens after the first agent works?
10522
10841
 
@@ -10538,61 +10857,6 @@ inputs again, and adds an eval for each improvement you keep.
10538
10857
 
10539
10858
  ---
10540
10859
 
10541
- Source: /docs/skills/ab.md
10542
-
10543
- # Agent SDK A/B metrics (`defineAB`)
10544
-
10545
- Live metrics plug-in. No `agent-sdk ab` CLI. No assertion API.
10546
- Reference: `docs/ab.md`.
10547
-
10548
- | | `defineEval` | `defineAB` |
10549
- | --- | --- | --- |
10550
- | Job | Gates on frozen fixtures | Metrics on live runs |
10551
- | Location | `evals/**/*.eval.ts` | `agent/ab.ts` or `agent/ab/<name>.ts` |
10552
- | How it runs | `agent-sdk eval` | Under `serve` / `run` |
10553
-
10554
- ```ts
10555
- import { defineAB, splitBySessionHash } from "@cursor/july/ab";
10556
-
10557
- export default defineAB({
10558
- name: "concise-instructions",
10559
- variants: {
10560
- control: { label: "Baseline" },
10561
- treatment: {
10562
- label: "Shorter",
10563
- instructions: "Keep replies to one short paragraph.",
10564
- },
10565
- },
10566
- split: splitBySessionHash({ holdout: 0.1 }),
10567
- derive: {
10568
- weatherCalls: (event) =>
10569
- event.type === "action.result" && event.data.toolName === "get_weather"
10570
- ? 1
10571
- : null,
10572
- },
10573
- onSample(sample) {
10574
- console.log(sample.variant, sample.metrics.toolCalls, sample.metrics.wallTimeMs);
10575
- },
10576
- });
10577
- ```
10578
-
10579
- ```ts
10580
- async execute(input, ctx) {
10581
- if (ctx.session.abs?.["concise-instructions"] === "treatment") {
10582
- // treatment-specific behavior
10583
- }
10584
- }
10585
- ```
10586
-
10587
- Enrollment is at session creation. Eval sessions skip it. Do not
10588
- use `splitIf` to filter evals. Split helpers and `onSample`
10589
- fields: `docs/ab.md`.
10590
-
10591
- Pick a name, arm labels, a split, and a real `onSample` sink. Do
10592
- not invent credentials.
10593
-
10594
- ---
10595
-
10596
10860
  Source: /docs/skills/create-agent.md
10597
10861
 
10598
10862
  # Create an Agent SDK agent
@@ -10722,7 +10986,7 @@ npx tsc --noEmit -p ./<slug>
10722
10986
  tsx does not typecheck. Tool `execute` must return JSON-shaped
10723
10987
  values: object literals or `type` aliases, not `interface` types.
10724
10988
 
10725
- Model turns need `CURSOR_API_KEY`. Without one, finish the
10989
+ Model turns need a Cursor credential (`CURSOR_API_KEY`, then `CURSOR_API_KEY_FILE`, then `CURSOR_SERVICE_ACCOUNT_KEY`, then `agent-sdk login`). Without one, finish the
10726
10990
  key-free checks, confirm `run` fails with only the API-key error,
10727
10991
  and hand these over:
10728
10992
 
@@ -10788,7 +11052,7 @@ Local `agent-sdk serve`. Hosted start or health failures:
10788
11052
  | `403` on stream | Wrong principal. Same auth as create; beyond loopback send `--bearer-token`. |
10789
11053
  | Works on loopback, blocked via tunnel | `localDevStrict()` rejects forwarded headers. Use `--bearer-token`. Never `--allow-anonymous` with account MCP. |
10790
11054
  | Slack `channel idle … missing credentials` | Expected. `slack doctor --prefix <PREFIX>`. |
10791
- | Immediate API-key error | Model turns need `CURSOR_API_KEY`. |
11055
+ | Immediate API-key error | Model turns need a Cursor credential (`CURSOR_API_KEY`, then `CURSOR_API_KEY_FILE`, then `CURSOR_SERVICE_ACCOUNT_KEY`, then `agent-sdk login`). |
10792
11056
  | Approvals or sandbox seeds missing | `runtime: "cloud"`. Those are local only. `validate` warns. |
10793
11057
  | `validate` clean, CI typecheck fails | tsx skipped types. JSON-shaped returns; `type` not `interface`. |
10794
11058
 
@@ -10799,15 +11063,182 @@ trace.
10799
11063
 
10800
11064
  ---
10801
11065
 
11066
+ Source: /docs/skills/deploy.md
11067
+
11068
+ # Deploy an Agent SDK agent
11069
+
11070
+ Use this skill only after a person asks for a deployment. It deploys a
11071
+ pushed Git ref to Cursor-managed hosting. Local files never upload.
11072
+
11073
+ Use the exact published `@cursor/july` release embedded in the installed
11074
+ skill. Do not use a floating npm tag or a workspace build of the CLI.
11075
+
11076
+ ## Gather the target
11077
+
11078
+ Resolve these values from the request and checkout:
11079
+
11080
+ - Agent directory. Default to the current directory only when it contains
11081
+ one Agent SDK project.
11082
+ - Deployment slug. Default to the normalized directory name.
11083
+ - Git ref. Default to the exact pushed `HEAD` commit.
11084
+ - Team. Use the service account's team unless the request names another.
11085
+ - Cursor-event repositories and CLI-only egress domains.
11086
+
11087
+ Ask one focused question when the target or requested ref is ambiguous.
11088
+ Do not ask for values the checkout or existing deployment supplies.
11089
+
11090
+ ## Use the attached service account
11091
+
11092
+ Continue only when `CURSOR_SERVICE_ACCOUNT_KEY` is present. Never print
11093
+ the value. Do not run `agent-sdk login`.
11094
+
11095
+ On Linux and macOS, run every Agent SDK command through this wrapper:
11096
+
11097
+ ```bash
11098
+ CURSOR_JULY_VERSION="__CURSOR_JULY_VERSION__"
11099
+ case "$CURSOR_JULY_VERSION" in
11100
+ __CURSOR_JULY_*__)
11101
+ echo "The Agent SDK deploy skill is not bound to a package version." >&2
11102
+ exit 1
11103
+ ;;
11104
+ esac
11105
+
11106
+ run_agent_sdk() {
11107
+ env -u NODE_OPTIONS -u CURSOR_API_KEY \
11108
+ CURSOR_API_KEY_FILE=/dev/null \
11109
+ npx --yes "@cursor/july@$CURSOR_JULY_VERSION" "$@"
11110
+ }
11111
+ ```
11112
+
11113
+ The installed copy pins the release it came from. The wrapper isolates that
11114
+ published CLI and the attached service account. The presence check prevents a
11115
+ stored personal login from being used when the attachment is missing.
11116
+
11117
+ Verify the principal before any write:
11118
+
11119
+ ```bash
11120
+ test -n "${CURSOR_SERVICE_ACCOUNT_KEY:-}" || {
11121
+ echo "No attached Cursor service account." >&2
11122
+ exit 1
11123
+ }
11124
+ run_agent_sdk whoami --json
11125
+ ```
11126
+
11127
+ Continue only when `credentialSource` is `service-account`. Record the
11128
+ team and complete service-account ID for the final report. Stop on an
11129
+ authentication, team-access, repository-scope, or hosting-entitlement
11130
+ error.
11131
+
11132
+ ## Pin pushed source
11133
+
11134
+ Find the Git root and commit:
11135
+
11136
+ ```bash
11137
+ GIT_ROOT="$(git -C "$AGENT_DIR" rev-parse --show-toplevel)"
11138
+ SOURCE_REF="$(git -C "$GIT_ROOT" rev-parse HEAD)"
11139
+ git -C "$GIT_ROOT" status --short
11140
+ git -C "$GIT_ROOT" branch -r --contains "$SOURCE_REF"
11141
+ ```
11142
+
11143
+ Use a ref from the request instead of `SOURCE_REF` when the person names
11144
+ one. Confirm the selected commit exists on the remote. If intended
11145
+ changes are uncommitted or unpushed, stop and explain they will not ship.
11146
+ Do not commit or push unless the request includes that work.
11147
+
11148
+ The CLI infers the normalized HTTPS `origin`, agent path, and slug from
11149
+ `--dir`. Pass `--repo`, `--path`, or `--slug` when the request overrides
11150
+ the inferred value. Never print a remote URL that contains credentials.
11151
+
11152
+ ## Preserve deployment inputs
11153
+
11154
+ A redeploy replaces its source, Cursor-event repositories, and
11155
+ CLI-supplied egress domains. Preserve the existing
11156
+ `cursorEventRepos` and `egressAllowedDomains` unless the request or
11157
+ project changes them. Reapply each value with:
11158
+
11159
+ ```text
11160
+ --cursor-events-repo <owner/repo>
11161
+ --allow-domain <hostname>
11162
+ ```
11163
+
11164
+ Read existing settings through a filter that selects only those two
11165
+ fields:
11166
+
11167
+ ```bash
11168
+ run_agent_sdk deployment "$SLUG" --json |
11169
+ node -e '
11170
+ let raw = "";
11171
+ process.stdin.setEncoding("utf8");
11172
+ process.stdin.on("data", (chunk) => { raw += chunk; });
11173
+ process.stdin.on("end", () => {
11174
+ const value = JSON.parse(raw);
11175
+ console.log(JSON.stringify({
11176
+ cursorEventRepos: value.cursorEventRepos ?? [],
11177
+ egressAllowedDomains: value.egressAllowedDomains ?? [],
11178
+ }, null, 2));
11179
+ });
11180
+ '
11181
+ ```
11182
+
11183
+ Never print or save the full response because it can contain short-lived
11184
+ engine-access headers.
11185
+
11186
+ For a new SCM-channel deployment, ask which repositories should wake the
11187
+ agent when the project does not declare the answer. The source repository
11188
+ is not always the event repository.
11189
+
11190
+ ## Validate, deploy, and verify
11191
+
11192
+ Validate with the same published package and principal:
11193
+
11194
+ ```bash
11195
+ run_agent_sdk validate --dir "$AGENT_DIR"
11196
+ ```
11197
+
11198
+ Stop on validation errors. Review hosting warnings about secret names,
11199
+ egress domains, and channel configuration before continuing.
11200
+
11201
+ Deploy the pinned source. Add the preserved or requested repeatable
11202
+ flags:
11203
+
11204
+ ```bash
11205
+ run_agent_sdk deploy \
11206
+ --dir "$AGENT_DIR" \
11207
+ --ref "$SOURCE_REF"
11208
+ ```
11209
+
11210
+ The command waits for a terminal deployment state. Do not treat
11211
+ `pending`, `accepted`, or `deploying` as success. If the command is
11212
+ interrupted, resume inspection with:
11213
+
11214
+ ```bash
11215
+ run_agent_sdk deployment "$SLUG"
11216
+ ```
11217
+
11218
+ A first deployment prints a one-time alias token. Never paste it into
11219
+ chat or expose it in an agent-captured terminal. Before a first deploy,
11220
+ ask for a secure destination or ask the person to run the final command
11221
+ in a private terminal. When writing the token, use mode `0600` and print
11222
+ only the path. Redeploys do not print the token. JSON deployment output
11223
+ can also contain credentials, so filter it before display.
11224
+
11225
+ Finish only when the deployment reports `running`. Report the slug,
11226
+ team, generation, pinned ref, and complete service-account ID. On
11227
+ failure, report the deployment status and sanitized error without
11228
+ retrying a different ref or principal.
11229
+
11230
+ ---
11231
+
10802
11232
  Source: /docs/skills/evals.md
10803
11233
 
10804
11234
  # Agent SDK evals
10805
11235
 
10806
11236
  Fixed input, model turn, gates on the trajectory. Files live at
10807
11237
  project-root `evals/**/*.eval.ts`. `agent/evals/` is ignored.
11238
+ Guide: `docs/evals.md`.
10808
11239
 
10809
- Live traffic variants: `skills/ab/SKILL.md`. That is not a test
10810
- runner.
11240
+ Observing production: hooks. Tool logic without a model:
11241
+ `agent-sdk call <tool> --input '{...}'`.
10811
11242
 
10812
11243
  ```bash
10813
11244
  agent-sdk eval --dir . --list
@@ -10821,9 +11252,13 @@ agent-sdk eval --dir . --tag smoke
10821
11252
  | `evals/weather.eval.ts` + `test` | `weather` |
10822
11253
  | `evals/weather/nyc.eval.ts` + `test` | `weather/nyc` |
10823
11254
  | `evals/weather.eval.ts` + `{ id: "nyc" }` | `weather/nyc` |
11255
+ | `evals/sql.eval.ts` exporting an array of `defineEval` calls | `sql/0000`, `sql/0001`, ... |
10824
11256
 
10825
11257
  `eval` boots an ephemeral server and a temp state root. `--url`
10826
- points at a running agent. Model turns need `CURSOR_API_KEY`.
11258
+ points at a running agent. Model turns need a Cursor credential
11259
+ (`CURSOR_API_KEY`, then `CURSOR_API_KEY_FILE`, then
11260
+ `CURSOR_SERVICE_ACCOUNT_KEY`, then `agent-sdk login`); `--list` does
11261
+ not. Node 22.13+, never Bun.
10827
11262
 
10828
11263
  ## Seeding
10829
11264
 
@@ -10873,9 +11308,30 @@ export default defineEvalConfig({
10873
11308
  });
10874
11309
  ```
10875
11310
 
10876
- Either `test(t)` or `cases`, not both. `t.send` waits for park/fail.
10877
- `workspaceFiles` seeds the first turn. Assert with `t.succeeded()`,
10878
- `calledTool` / `notCalledTool`, `t.check(t.reply, …)`, `t.metric`.
11311
+ Either `test(t)` or `cases`, not both. `t.send` waits for the turn to
11312
+ settle (complete, park, or fail) and returns it; `turn.calledTool(...)`
11313
+ reads only that turn, `t.*` reads the whole run. First-send options:
11314
+ `workspaceFiles`, `workspaceDir`, `cloud`.
11315
+
11316
+ Gates: `t.succeeded()` / `t.parked()`,
11317
+ `calledTool(name, { input, output, status, count })` / `notCalledTool`,
11318
+ `toolOrder`, `maxToolCalls`, `taggedArtifact`, `event` / `notEvent`,
11319
+ `t.check(value, includes | equals | matches | similarity | satisfies)`.
11320
+ Records: `t.metric`, `t.log`, `t.score` (soft 0-1).
11321
+
11322
+ Severity is on the handle: default gate; `.soft()` tracked;
11323
+ `.atLeast(0.8)` soft with a bar (verdict `scored`, exit 1 only under
11324
+ `--strict`); `.gate(0.8)` hard. `t.judge.factuality`, `summarizes`,
11325
+ `closedQA`, and `sql` are soft by default and need a judge model
11326
+ (`judge` in `evals.config.ts`, on the eval or case, or per-call
11327
+ `{ model }`).
11328
+
11329
+ ## Side effects
11330
+
11331
+ Eval sessions run real tools. Guard actuation with
11332
+ `ctx.session.purpose === "eval"` in the tool, hook, or `defineResult`
11333
+ and return a shaped result so `calledTool` can still assert the
11334
+ decision.
10879
11335
 
10880
11336
  ## What to gate
10881
11337
 
@@ -10897,6 +11353,18 @@ drifting inputs (pin them).
10897
11353
  | GitHub | `github replay … --dry-run --out fixtures/github` |
10898
11354
  | Host-prep PR review | A team-owned PR; gate findings shape, not counts |
10899
11355
  | Workspace | `workspaceFiles` in `t.send` |
11356
+ | Dataset | `loadJson` / `loadJsonl` / `loadYaml` from `@cursor/july/evals/loaders`, paths from the project root |
11357
+
11358
+ ## Debug and CI
11359
+
11360
+ A failed local run leaves `evals/<case-id>.json` (assertions, inputs,
11361
+ tool I/O, final text, logs) under `evals/<stamp>/` in the project state
11362
+ directory (the CLI prints the path); read it before editing the eval.
11363
+ `--state-root <path>` keeps the raw session events too.
11364
+
11365
+ CI: `agent-sdk eval --dir . --json --no-stream --junit reports/evals.xml`.
11366
+ Exit `1` on a failed gate, `2` when nothing matched, `--strict` to fail
11367
+ on `scored`.
10900
11368
 
10901
11369
  Every kept hillclimb change lands an eval that would have failed
10902
11370
  before it. Never weaken a gate to pass a round.
@@ -10927,8 +11395,10 @@ Public docs: `node_modules/@cursor/july/dist/docs/llms.txt` or
10927
11395
  per-project cache directory under `~/.cache`. Point cwd at a
10928
11396
  checkout only when the agent must inherit that tree.
10929
11397
  5. **Attached MCP is nameless** until `advertiseTools: true`.
10930
- 6. **Model turns need `CURSOR_API_KEY`.** `validate`, `info`, `call`,
10931
- and `serve` bring-up do not.
11398
+ 6. **Model turns need a Cursor credential** (`CURSOR_API_KEY`, then
11399
+ `CURSOR_API_KEY_FILE`, then `CURSOR_SERVICE_ACCOUNT_KEY`, then
11400
+ `agent-sdk login`). `validate`,
11401
+ `info`, `call`, and `serve` bring-up do not.
10932
11402
 
10933
11403
  ## Folder structure
10934
11404
 
@@ -10948,7 +11418,6 @@ Path is identity. Full list: README "Folder structure".
10948
11418
  | `agent/hooks/*.ts` | Observe-only |
10949
11419
  | `agent/artifacts.ts` | Durable tagged outputs (`defineArtifacts`) |
10950
11420
  | `agent/result.ts` | Host `commit` on the final assistant text (`defineResult`) |
10951
- | `agent/ab.ts` or `agent/ab/*.ts` | Live A/B (`defineAB`) |
10952
11421
  | `agent/otel.ts` | OpenTelemetry (`defineOtel`) |
10953
11422
  | `agent/schedules/*` | Cron. Never auto-fire under `--dev` |
10954
11423
  | `agent/sandbox/workspace/` | Session seed files (local only) |
@@ -10996,11 +11465,11 @@ only this agent's directory.
10996
11465
  | --- | --- |
10997
11466
  | Scaffold | `skills/create-agent/SKILL.md` |
10998
11467
  | Evals | `skills/evals/SKILL.md` |
10999
- | Live A/B | `skills/ab/SKILL.md` |
11000
11468
  | OpenTelemetry | `skills/otel/SKILL.md` |
11001
11469
  | GitHub | `skills/github/SKILL.md` |
11002
11470
  | Slack | `skills/setup-slack/SKILL.md` |
11003
11471
  | Host MCP OAuth | `skills/mcp-auth/SKILL.md` |
11472
+ | Managed deployment | `skills/deploy/SKILL.md` |
11004
11473
  | Local triage | `skills/debug/SKILL.md` |
11005
11474
  | Measured improvement | `skills/hillclimb/SKILL.md` |
11006
11475
 
@@ -11049,7 +11518,7 @@ at deploy. Repos must share one GitHub owner.
11049
11518
 
11050
11519
  | Source | When |
11051
11520
  | --- | --- |
11052
- | `cursorAccount` + `serve --cursor-events` | Preferred. Signed-in host (`agent-sdk login` / `CURSOR_API_KEY`). Repos from the channel and from repeatable `--repo owner/name`. Cap 20, one GitHub owner |
11521
+ | `cursorAccount` + `serve --cursor-events` | Preferred. Signed-in host (`agent-sdk login`, `CURSOR_API_KEY`, or `CURSOR_SERVICE_ACCOUNT_KEY`). Repos from the channel and from repeatable `--repo owner/name`. Cap 20, one GitHub owner |
11053
11522
  | HTTP `POST /<slug>/v1/channels/github` with a webhook secret | `allowAll()` + `X-Hub-Signature-256` |
11054
11523
  | HTTP, no secret | Loopback only. `serve --dev` also admits unsigned loopback (fixtures / forward) |
11055
11524
 
@@ -11180,12 +11649,12 @@ to use each one.
11180
11649
  | [framework-map](/docs/skills/framework-map.md) | Learn the project layout and runtimes |
11181
11650
  | [create-agent](/docs/skills/create-agent.md) | Scaffold and verify a new agent |
11182
11651
  | [evals](/docs/skills/evals.md) | Write fixtures and regression checks |
11183
- | [ab](/docs/skills/ab.md) | Compare variants on live traffic |
11184
11652
  | [otel](/docs/skills/otel.md) | Export OpenTelemetry traces |
11185
11653
  | [hillclimb](/docs/skills/hillclimb.md) | Improve an agent against fixed inputs |
11186
11654
  | [github](/docs/skills/github.md) | Add GitHub webhooks and replay events |
11187
11655
  | [setup-slack](/docs/skills/setup-slack.md) | Connect an agent to Slack |
11188
11656
  | [mcp-auth](/docs/skills/mcp-auth.md) | Authorize host MCP OAuth |
11657
+ | [deploy](/docs/skills/deploy.md) | Deploy with an attached service account |
11189
11658
  | [debug](/docs/skills/debug.md) | Diagnose a local run |
11190
11659
 
11191
11660
  ---
@@ -11480,8 +11949,8 @@ Source: /docs/storage.md
11480
11949
  # Storage
11481
11950
 
11482
11951
  The Agent SDK owns durable storage for sessions, continuation tokens,
11483
- reminders, playground eval history, and live A/B samples. It chooses the
11484
- keys, when to read and write, and how to restore after restart.
11952
+ reminders, and playground eval history. It chooses the keys, when to
11953
+ read and write, and how to restore after restart.
11485
11954
 
11486
11955
  The Agent SDK owns key encoding. Backends must accept the keys they are
11487
11956
  given. Do not fail `put` to enforce a shorter cap.
@@ -11515,9 +11984,9 @@ export default defineStorage({
11515
11984
  ## Which fields to provide
11516
11985
 
11517
11986
  Implement the small KV core (`put`/`get`/`delete`/`list` plus the `cas`
11518
- group) and you get **full functionality**: eval-run and A/B history are
11519
- derived over the core automatically. The dedicated `evals` / `abs` groups
11520
- are backend-native optimizations, not required-or-lose-history hooks.
11987
+ group) and you get **full functionality**: eval-run history is derived
11988
+ over the core automatically. The dedicated `evals` group is a
11989
+ backend-native optimization, not a required-or-lose-history hook.
11521
11990
 
11522
11991
  | Field | Required | Role |
11523
11992
  | --- | --- | --- |
@@ -11528,36 +11997,28 @@ are backend-native optimizations, not required-or-lose-history hooks.
11528
11997
  | `delete` | For cleanup | Remove a key |
11529
11998
  | `name` | No | Label surfaced on `GET /v1/info` diagnostics |
11530
11999
  | `policy` | No | Timing knobs; see [Policy](#policy) |
11531
- | `evals` | No | Backend-native eval-runs table; derived over the core when omitted. See [Eval and A/B tables](#eval-and-a-b-tables) |
11532
- | `abs` | No | Backend-native A/B metrics table; derived over the core when omitted. See [Eval and A/B tables](#eval-and-a-b-tables) |
12000
+ | `evals` | No | Backend-native eval-runs table; derived over the core when omitted. See [Eval table](#eval-table) |
11533
12001
 
11534
12002
  A throwing `put` is logged and dropped. It never fails a turn. When
11535
12003
  resolving a missing continuation token, a throwing `get` fails the
11536
12004
  follow-up so a store outage does not open a new session. Return
11537
12005
  `undefined` only for a real miss.
11538
12006
 
11539
- ## Eval and A/B tables
12007
+ ## Eval table
11540
12008
 
11541
- Two dedicated table groups carry structured rows instead of opaque KV
11542
- values. Both are **optional optimizations**: when a group is not
11543
- authored, `defineStorage` derives it over the KV core, so a backend that
11544
- implements only the core loses nothing. Author a group only when the
11545
- backend has a better native shape (a real database table, an analytics
11546
- pipeline). The built-in `fileKv` and `cursorHostedStorage` both do.
12009
+ The dedicated `evals` table group carries structured rows instead of
12010
+ opaque KV values. It is an **optional optimization**: when the group is
12011
+ not authored, `defineStorage` derives it over the KV core, so a backend
12012
+ that implements only the core loses nothing. Author the group only when
12013
+ the backend has a better native shape (a real database table, an
12014
+ analytics pipeline). The built-in `fileKv` and `cursorHostedStorage`
12015
+ both do.
11547
12016
 
11548
12017
  `evals` keeps playground eval batches across restarts (`put`, `delete`,
11549
12018
  `list` over run snapshots keyed by `runId`). A core missing `delete` or
11550
12019
  `list` leaves eval history in memory until restart.
11551
12020
  See [Evals](/docs/evals.md#configure-eval-runs).
11552
12021
 
11553
- `abs` exports live A/B metrics: `putSample` appends one cumulative
11554
- metric sample per enrolled experiment on each completed or failed turn;
11555
- optional `putSnapshot` / `getSnapshot` store and serve back the latest
11556
- aggregate so a replacement host can still serve the A/Bs surface.
11557
- `putSample` and `putSnapshot` need only core `put`; `getSnapshot` needs
11558
- core `get`. Session event logs remain the assignment source of truth
11559
- either way. See [Live A/B metrics](/docs/ab.md).
11560
-
11561
12022
  ## Policy
11562
12023
 
11563
12024
  Two knobs change behavior:
@@ -11592,8 +12053,7 @@ With `get` and `list`, serve can rebuild local state from your store:
11592
12053
  - At startup, the Agent SDK loads recent sessions up to the restore caps.
11593
12054
  - On demand, a missing continuation token resolves through the store
11594
12055
  and resumes that session.
11595
- - Playground eval history and A/B aggregates can load from the same
11596
- sink.
12056
+ - Playground eval history can load from the same sink.
11597
12057
 
11598
12058
  A turn in flight at crash time is not replayed. The next follow-up
11599
12059
  resumes from the last flushed state.
@@ -12507,7 +12967,7 @@ not on `PATH`, use `npx @cursor/july`.
12507
12967
  | What you see | What to do |
12508
12968
  | --- | --- |
12509
12969
  | Built-in file reads and greps fail; the turn retries for a long time | Run under Node 22.13+ (or `tsx`), never Bun. Look for `NGHTTP2_FRAME_SIZE_ERROR` in logs. |
12510
- | The turn fails immediately with an API-key error | Sign in with `agent-sdk login`, or set `CURSOR_API_KEY`. Discovery, `info`, `call`, and serve bring-up work without a key; model turns need one. |
12970
+ | The turn fails immediately with an API-key error | Sign in with `agent-sdk login`, or set `CURSOR_API_KEY` or `CURSOR_SERVICE_ACCOUNT_KEY`. Discovery, `info`, `call`, and serve bring-up work without a key; model turns need one. |
12511
12971
  | Replies quote rules or `AGENTS.md` from outside your agent project | The session workspace inherited parent-folder config. Nested git checkouts default `local.cwd` to a per-project cache directory under `~/.cache`. Point `defineAgent({ local: { cwd } })` at a checkout only when the agent should inherit that tree, or set `--state-root` to a clean directory (for example under `/tmp`). |
12512
12972
  | Yellow box shows Datadog/Linear tools, but the model lists `GetDynamicTools` / IDE `cursor` tools and never calls them | Attached MCP sits behind harness meta-tools, or the harness cwd is still inside another checkout. Set `advertiseTools: true` for named tools on local turns. Check `GET /v1/info` `local.cwd` and `connections[].advertiseTools`. |
12513
12973
  | Server tools, skills, or workspace seed files never appear | Server tools and sandbox seeds apply on the local runtime (cloud server tools need `--public-url` / `--cloud-tools-url`). Skills reach cloud through the Agent Store when hosting or a personal `CURSOR_API_KEY` is available; otherwise only skills already in the cloud repo. `validate` warns when this combination is present. |