@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
@@ -1,30 +1,63 @@
1
1
  # Evals
2
2
 
3
- An eval is a repeatable check that runs your agent against a fixed input
4
- and gates the recorded trajectory: the run completed, the right tool
5
- ran, the reply has the right shape. Evals are how you know a prompt
6
- tweak helped, a refactor didn't regress the agent, and last month's fix
7
- is still holding.
3
+ An eval sends a fixed message to your agent and asserts over the
4
+ trajectory it records: the turn completed, the right tool ran with the
5
+ right input, the reply has the right shape. Evals are how you know a
6
+ prompt tweak helped, a refactor didn't regress the agent, and last
7
+ month's fix still holds.
8
8
 
9
- Evals exercise the same surface your users hit. The runner starts (or
10
- targets) a real agent server, drives sessions over the public API, and
11
- grades what comes back. A passing eval means the agent started,
12
- accepted a message, and did what you asserted.
9
+ Nothing is mocked. The runner starts (or targets) a real agent server,
10
+ drives sessions over the public API, and grades the events it gets
11
+ back. The model runs and server tools execute, so
12
+ [keep side effects out of eval sessions](#keep-side-effects-out-of-eval-sessions)
13
+ before you point an eval at an agent that posts anywhere.
13
14
 
14
- ## Define evals with `defineEval`
15
+ ## Evals, hooks, or hillclimbing?
15
16
 
16
- The Agent SDK discovers evals under the project-root `evals/` directory,
17
- in `.eval.ts` or `.eval.js` files. That's a sibling of `agent/`, never
18
- inside it (`agent/evals/` is silently ignored). TypeScript is the normal
19
- authoring format.
17
+ All three read the same session event stream. Pick by the question you
18
+ are asking.
20
19
 
21
- The file path is the eval's identity, so you don't author an id.
22
- Directories group related evals: `evals/builds/api.eval.ts` becomes id
23
- `builds/api`. An `index` filename collapses to its directory, so
24
- `evals/builds/index.eval.ts` becomes `builds`.
20
+ | You want to | Use |
21
+ | --- | --- |
22
+ | Gate one fixed input's behavior, locally and in CI | Evals (this page) |
23
+ | Observe every live session: metrics, audit, alerts | [Hooks](/docs/reference/hooks.md) |
24
+ | Improve an agent one measured round at a time | [Hillclimbing](/docs/hillclimbing.md); each kept win lands an eval |
25
+
26
+ [Hooks, channel events, or evals?](/docs/reference/hooks.md#hooks-channel-events-or-evals)
27
+ has the side-by-side table.
28
+
29
+ ### When not to write an eval
30
+
31
+ - Test a server tool's own logic with
32
+ `agent-sdk call <tool> --dir . --input '{...}'` or a unit test. No
33
+ model turn, no credential.
34
+ - Explore a prompt with `agent-sdk run --dir . --message "..."` and
35
+ read the trajectory. Write the eval once you know which decision to
36
+ gate.
37
+ - Stop a bad turn while it runs with
38
+ [`needsApproval`](/docs/reference/tools.md#gate-a-tool-on-human-approval)
39
+ on the tool or [`defineResult`](/docs/reference/result.md). Evals grade
40
+ after the fact.
41
+
42
+ ## Write your first eval
43
+
44
+ Evals live under the project-root `evals/` directory, a sibling of
45
+ `agent/`. `agent/evals/` is silently ignored. Discovery loads every
46
+ `.eval.ts` (or `.eval.js`) file under `evals/`, plus one config file.
47
+
48
+ ```text
49
+ my-agent/
50
+ agent/
51
+ agent.ts
52
+ tools/inspect_pr.ts
53
+ evals/
54
+ evals.config.ts # required to run: maxConcurrency
55
+ readiness.eval.ts # id: readiness
56
+ prs.eval.ts # cases: prs/checkout, prs/search
57
+ ```
25
58
 
26
- An eval is a single `async test(t)`. You drive the agent with `t` and
27
- assert on the run with the same `t`:
59
+ An eval is a single `async test(t)`. You drive the agent with `t.send`
60
+ and assert on the recorded run with the same `t`:
28
61
 
29
62
  ```ts
30
63
  // evals/readiness.eval.ts
@@ -46,12 +79,49 @@ export default defineEval({
46
79
  });
47
80
  ```
48
81
 
49
- One file can also hold several datapoints through `cases` (provide
50
- either `test` or `cases`, not both). Each case id becomes
51
- `<fileId>/<case.id>`:
82
+ ```ts
83
+ // evals/evals.config.ts
84
+ import { defineEvalConfig } from "@cursor/july/evals";
85
+
86
+ export default defineEvalConfig({ maxConcurrency: 20 });
87
+ ```
88
+
89
+ Run it under Node 22.13 or newer (never Bun) with a Cursor credential
90
+ in place; see [Credentials](#credentials):
91
+
92
+ ```bash
93
+ agent-sdk eval --dir . --list
94
+ agent-sdk eval --dir . readiness
95
+ ```
96
+
97
+ ```text
98
+ PASS readiness (14.2s) — Inspects a PR without approving it.
99
+ ✓ succeeded
100
+ ✓ calledTool(inspect_pr)
101
+ ✓ notCalledTool(approve_pr)
102
+ ✓ check(includes)
103
+
104
+ 1 passed, 0 failed, 1 total
105
+ artifacts: <project state directory>/evals/2026-09-11T15-02-11-402Z
106
+ ```
107
+
108
+ Every local run writes each case's assertions, inputs, tool calls, and
109
+ `t.log` lines under that artifacts directory. Open
110
+ `evals/<case-id>.json` there when a case fails; see
111
+ [Where results land](#where-results-land).
112
+
113
+ ## Name cases by path
114
+
115
+ The file path is the eval's identity, so you don't author an id.
116
+ `evals/builds/api.eval.ts` becomes `builds/api`. An `index` filename
117
+ collapses to its directory: `evals/builds/index.eval.ts` becomes
118
+ `builds`.
119
+
120
+ One file can hold several datapoints through `cases`. Provide either
121
+ `test` or `cases`, not both. Each case id becomes `<fileId>/<case.id>`:
52
122
 
53
123
  ```ts
54
- // evals/prs.eval.ts prs/checkout, prs/search
124
+ // evals/prs.eval.ts: prs/checkout, prs/search
55
125
  export default defineEval({
56
126
  tags: ["smoke", "prs"],
57
127
  cases: [
@@ -80,93 +150,75 @@ export default defineEval({
80
150
  });
81
151
  ```
82
152
 
83
- Case ids must be single path segments, unique within the file.
84
- Each case can set its own `description`, `tags`, `timeoutMs`, and
85
- `iterations`. A case-level value replaces the file-level value for that
86
- datapoint.
153
+ Case ids are single path segments, unique within the file. A case can
154
+ set its own `description`, `tags`, `timeoutMs`, `iterations`, `judge`,
155
+ `reporters`, and `metadata`. A case-level value replaces the file-level
156
+ one for that datapoint, except `metadata`, which merges with case keys
157
+ winning, and `reporters`, which adds to the file's list. `metadata` is
158
+ free-form data carried onto the result and every reporter.
159
+
160
+ A file may instead export an array of `defineEval` calls to fan out
161
+ over a dataset. Ids are then the file id plus a zero-padded index
162
+ (`sql/0000`, `sql/0001`, ...); see [Load a dataset](#load-a-dataset).
163
+ Prefer `cases` when datapoints are hand-written and deserve stable
164
+ names.
87
165
 
88
166
  ### Iterations
89
167
 
90
- `iterations` (file or case, default `1`) runs a datapoint repeatedly.
91
- Discovery expands `iterations: 3` on case `nyc` to runnable ids
92
- `weather/nyc/1`, `weather/nyc/2`, `weather/nyc/3` (filter prefix
93
- `weather/nyc` still selects all three). Each expanded case exposes
94
- `t.iteration` / `t.iterations` on the test context. Cap is 100.
168
+ `iterations` (file or case, default `1`, cap `100`) runs a datapoint
169
+ repeatedly. Discovery expands `iterations: 3` on case `nyc` to runnable
170
+ ids `weather/nyc/1`, `weather/nyc/2`, and `weather/nyc/3`. The filter
171
+ `weather/nyc` still selects all three. Each expanded case exposes
172
+ `t.iteration` and `t.iterations`.
95
173
 
96
- `maxConcurrency` counts **authored datapoints**, not expanded
97
- iterations: siblings `…/1`…`…/n` share one concurrency slot and run
98
- sequentially. A suite with 11 cases × 3 iterations and
99
- `maxConcurrency: 20` therefore has at most 11 cases in flight, not 33.
174
+ `maxConcurrency` counts authored datapoints, not expanded iterations.
175
+ Iterations of one datapoint share a concurrency slot and run in
176
+ sequence, so a suite of 11 cases with 3 iterations each and
177
+ `maxConcurrency: 20` has at most 11 cases in flight.
100
178
 
101
- ## Configure eval runs
179
+ ## Drive the agent with `t.send`
102
180
 
103
- Each project with evals needs `evals/evals.config.ts` or
104
- `evals/evals.config.js`, and it must set `maxConcurrency`. Each case
105
- issues real model-provider requests, so concurrency is capped hard at
106
- 200. Existing projects use 20. Discovery with `eval --list` works
107
- without this file, but running a case does not.
181
+ `t.send(message, options?)` runs one turn and waits for it to settle:
182
+ complete, park on an approval request, or fail. Several sends in one
183
+ case share the session, which is how you write multi-turn evals.
108
184
 
109
- ```ts
110
- import { defineEvalConfig } from "@cursor/july/evals";
185
+ Each send resolves to a turn result: `message` (the assistant text),
186
+ `sessionId`, `events`, `toolCalls` (tool names in order), `ok`, and
187
+ `index`. The turn carries the same assertion vocabulary as `t`, scoped
188
+ to that turn, so you can grade an intermediate turn before the next
189
+ send overwrites `t.reply`. `turn.expectOk()` throws when the turn
190
+ failed, for later steps that depend on it.
111
191
 
112
- export default defineEvalConfig({
113
- maxConcurrency: 20, // required
114
- // timeoutMs: 180_000, // optional project-wide default
115
- // judge: { model: "..." }, // default judge model for t.judge.*
116
- // reporters: [], // destinations that observe every case
117
- // maxPlaygroundRuns: 50, // playground history only (default 20)
192
+ Read the whole case with `t.reply` (last assistant text), `t.events`
193
+ (every event so far), `t.turns` (settled turns, oldest first), and
194
+ `t.sessionId`. `t.signal` aborts when the case hits its timeout; pass
195
+ it to your own async work.
196
+
197
+ Three options apply on the first send only, because they shape session
198
+ creation:
199
+
200
+ | Option | Effect |
201
+ | --- | --- |
202
+ | `workspaceFiles` | `{ path: contents }` seeded into the local session workspace. Prefer this over machine-local paths |
203
+ | `workspaceDir` | Absolute harness cwd for the local runtime |
204
+ | `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) |
205
+
206
+ ```ts
207
+ await t.send("Review pr/diff.patch and post findings.", {
208
+ workspaceFiles: {
209
+ "pr/diff.patch": [
210
+ "diff --git a/app/routes/search.ts b/app/routes/search.ts",
211
+ "+res.send(`<h1>Results for ${req.query.q}</h1>`);",
212
+ ].join("\n"),
213
+ },
118
214
  });
119
215
  ```
120
216
 
121
- The timeout order is case or file `timeoutMs`, CLI `--timeout-ms`,
122
- project config `timeoutMs`, then the 180-second runner default.
217
+ ## Assert over the trajectory
123
218
 
124
- The optional fields:
125
-
126
- | Option | Default | Meaning |
127
- | --- | --- | --- |
128
- | `timeoutMs` | `180_000` | Project-wide per-case timeout |
129
- | `judge` | unset | Default judge model for `t.judge.*`; see [Judge free-form output](#judge-free-form-output) |
130
- | `reporters` | unset | Destinations that observe every case; `--skip-report` suppresses them |
131
- | `maxPlaygroundRuns` | `20` | Max batches in the playground / `/v1/dev/evals*` history (not CLI `eval`). Hard-capped at 500. |
132
-
133
- Reporters come from `@cursor/july/evals/reporters`: `JUnit` writes a
134
- JUnit XML file for CI, `Artifacts` writes per-case files, and
135
- `combineReporters` merges several into one (`renderJUnitXml` renders
136
- the XML for a custom destination). A file or case can add its own
137
- `reporters` on top of the config list.
138
-
139
- Playground batches survive restarts whenever `agent/storage.ts` exists
140
- with an `evals` table or a KV core providing `delete` and `list` (the
141
- table is derived over the core); see
142
- [Storage](/docs/storage.md#eval-and-a-b-tables). Without storage they live
143
- in process memory and disappear when `serve` exits. Navigating away
144
- and back still works while the process is up.
145
-
146
- ## Drive and assert with `t`
147
-
148
- `t` is both the driver and the assertion surface. You write ordinary
149
- control flow, sending turns and asserting inline.
150
-
151
- Drive the agent with `t.send(message, options?)`. It runs one turn and
152
- waits for the session to park or fail. Multiple sends in one case share
153
- the session, which is how you write multi-turn evals.
154
-
155
- Each `t.send` resolves to a turn result with `message`,
156
- `sessionId`, `events`, `toolCalls`, `ok`, and `index`. The turn carries
157
- the same assertion vocabulary as `t`, scoped to that turn, so you can
158
- grade an intermediate turn before the next send overwrites `t.reply`.
159
- `turn.expectOk()` throws when the turn failed, for later
160
- steps that depend on it.
161
-
162
- Read the full case state with `t.reply` (the last assistant text),
163
- `t.events` (session events captured so far), `t.turns` (settled
164
- turns, oldest first), and `t.sessionId`. `t.signal` aborts when the
165
- case hits its timeout; pass it to your own async work. A thrown
166
- [turn result](/docs/reference/result.md) `commit` fails the turn, so
167
- `t.succeeded()` fails too.
168
-
169
- Assert with the gates:
219
+ Assertions record; they never throw. One run reports every failure
220
+ instead of dying on the first. Assertions on `t` read the whole run.
221
+ Assertions on a turn read only that turn.
170
222
 
171
223
  | Gate | Checks |
172
224
  | --- | --- |
@@ -175,157 +227,483 @@ Assert with the gates:
175
227
  | `t.messageIncludes(token)` | the joined assistant text matches a string or `RegExp` |
176
228
  | `t.calledTool(name, matcher?)` | a matching call to `name` happened |
177
229
  | `t.notCalledTool(name)` | no request for `name`, in any lifecycle state |
178
- | `t.loadedSkill(name)` | the agent opened the skill's `SKILL.md` (read, grep, or shell `cat`) |
179
- | `t.toolOrder(names)` | tool requests appear in this relative order (extra calls allowed) |
230
+ | `t.loadedSkill(name)` | the agent opened `skills/<name>/SKILL.md` (read, grep, or shell `cat`) |
231
+ | `t.toolOrder(names)` | tool requests appear in this relative order; extra calls allowed |
180
232
  | `t.usedNoTools()` | no tool calls at all |
181
233
  | `t.maxToolCalls(max)` | at most `max` tool calls |
182
234
  | `t.noFailedActions()` | no tool call reported an error |
183
235
  | `t.calledSubagent(name, matcher?)` | a matching subagent delegation happened |
184
236
  | `t.taggedArtifact(kind?, predicate?)` | at least one [artifact](/docs/reference/artifacts.md) was tagged |
185
- | `t.event(type, matcher?)` | at least one matching event of `type` occurred |
186
- | `t.notEvent(type, matcher?)` | no matching event of `type` occurred |
237
+ | `t.event(type, matcher?)` | at least one matching [event](/docs/reference/sessions.md#which-events-can-i-stream) of `type` |
238
+ | `t.notEvent(type, matcher?)` | no matching event of `type` |
187
239
  | `t.eventOrder(matchers)` | matching event groups occur in this relative order |
188
240
  | `t.eventsSatisfy(label, predicate)` | your predicate over the typed event stream |
189
- | `t.check(value, expectation)` | any value, against a builder |
190
- | `t.score(name, value)` | records a 01 score you computed; soft until you add a bar |
191
- | `t.requireToolCall(name, matcher?)` | gates on a matching call and returns it, so later code can read its input and output |
192
- | `t.requireInputRequest(filter?)` | gates on exactly one pending approval request and returns it |
241
+ | `t.check(value, expectation)` | any value, against a [builder](#grade-values-with-expectation-builders) |
242
+ | `t.score(name, value)` | a 0-1 score you computed; soft until you add a bar |
243
+
244
+ Three more assertions gate and return the matched fact. They stop the
245
+ test body when nothing matches, without a duplicate execution error.
246
+ `t.requireToolCall(name, matcher?)` returns the call so later code can
247
+ read its `input` and `output`. `t.requireInputRequest(filter?)` returns
248
+ the single pending approval request. `await t.require(value, expectation)`
249
+ does the same for a value check.
250
+
251
+ A case with no assertions passes when at least one turn completed. Add
252
+ `t.succeeded()` and behavior gates anyway. They make the contract
253
+ visible in review.
254
+
255
+ ### What good cases assert
256
+
257
+ Gate decisions and shape, not prose. Model wording varies run to run.
258
+ Tool choice, tool avoidance, and output structure are the stable
259
+ contract.
260
+
261
+ 1. `t.succeeded()`: always, first.
262
+ 2. The tool decision: `calledTool` for the intended path,
263
+ `notCalledTool` for the likely wrong alternative. The pair is
264
+ stronger than either alone.
265
+ 3. Output shape: a regex for the contract (`/ready|blocked/i`, a JSON
266
+ marker, a findings-block fence), never exact sentences.
267
+ 4. For structured output, parse `t.reply` and check fields with
268
+ `matches` or `satisfies` instead of substring-matching JSON.
269
+
270
+ The common failure modes: asserting exact phrasing, packing more than
271
+ about five gates into one case (split it), and cases that depend on
272
+ live external state that drifts (pin the input).
193
273
 
194
- Every gate returns a handle: `.soft()` demotes it to tracked-only,
195
- `.atLeast(0.7)` adds a soft score bar, and `.gate(0.8)` promotes a
196
- scored assertion into a hard gate.
274
+ ### Narrow tool assertions with matchers
197
275
 
198
276
  With no matcher, `calledTool` is request-based: a requested call counts
199
- even when its result has not arrived. Pass
200
- `t.calledTool("inspect_pr", { status: "completed" })` to require the
201
- call to return. `input`, `output`, and `count` matcher fields accept a
202
- literal, a `RegExp`, or a predicate.
203
-
204
- The expectation builders are `includes(string | RegExp)`,
205
- `equals(value)`, `matches(schema)`, `similarity(expected)`, and
206
- `satisfies(predicate, label)`. `includes` stringifies its input,
207
- `equals` compares values deeply, `matches` validates against a Standard
208
- Schema (or anything with `safeParse`, like Zod), `similarity` scores
209
- normalized text similarity, and `satisfies` runs your predicate. The
210
- plain function `normalizedSimilarity(actual, expected)` returns the
211
- same 0–1 score for use with `t.score`.
212
-
213
- A few more context members shape a case: `t.require(value, expectation)`
214
- records a gate and stops the test body when it fails, without a
215
- duplicate execution error. `t.skip(reason)` ends the case as skipped
216
- (reported separately, never changes the exit code; call it before
217
- sending messages). `t.metric(name, value)` records a structured score
218
- for the playground case card. `t.log(message)` records a debug line for
219
- the CLI and playground result.
220
-
221
- Three `t.send` options apply on session create (first `t.send` only):
222
-
223
- - `workspaceFiles`: `{ path: contents }`, seeded into the local session
224
- workspace. Prefer this over machine-local paths.
225
- - `workspaceDir`: absolute harness cwd (local runtime).
226
- - `cloud`: per-session cloud options merged over the agent's static
227
- `cloud` config (repos / env / …). Use a pinned `repos` override to
228
- attach a fixture repo for cloud evals without putting it on the
229
- agent's default `cloud.repos`. Cloud ignores `workspaceFiles` seeds.
277
+ even before its result arrives. A matcher narrows it:
230
278
 
231
279
  ```ts
232
- const toolResults = t.events.filter((e) => e.type === "action.result");
280
+ t.calledTool("inspect_pr", { status: "completed" });
281
+ t.calledTool("apply_agents", { input: { verdict: "update" } });
282
+ t.calledTool("bash", { input: { command: /^gh pr view/ }, count: 1 });
283
+ t.calledTool("read_file", {
284
+ output: (value) => String(value).includes("TODO"),
285
+ });
286
+ ```
287
+
288
+ `input`, `output`, and `count` accept a literal, a `RegExp`, or a
289
+ predicate. Object literals partial-deep-match, so `{ verdict: "update" }`
290
+ matches arguments that also carry other keys. `status` is one of
291
+ `completed`, `failed`, `pending`, or `rejected` (a human denied the
292
+ approval). `calledSubagent` takes `{ output, status, count, callId }`.
293
+ `event`, `notEvent`, and `eventOrder` take `{ data, count }`.
294
+
295
+ ### Grade values with expectation builders
296
+
297
+ `t.check(value, expectation)` grades any value: `t.reply`, a parsed
298
+ JSON field, a tool's output.
299
+
300
+ | Builder | Checks | Severity |
301
+ | --- | --- | --- |
302
+ | `includes(string \| RegExp)` | substring or match; structured values are stringified first | gate |
303
+ | `equals(value)` | deep equality | gate |
304
+ | `matches(schema)` | a Standard Schema (Zod, Valibot, ...) or anything with `safeParse` | gate |
305
+ | `similarity(expected)` | normalized text similarity, 0-1 | soft |
306
+ | `satisfies(predicate, label)` | your predicate; `label` is the failure detail | gate |
307
+
308
+ ```ts
309
+ import { matches, satisfies } from "@cursor/july/evals";
310
+ import { z } from "zod";
311
+
312
+ const verdict = JSON.parse(t.reply ?? "{}");
233
313
  t.check(
234
- toolResults.length,
235
- satisfies((n) => (n as number) <= 4, "at most 4 tool calls")
314
+ verdict,
315
+ matches(z.object({ ready: z.boolean(), blockers: z.array(z.string()) }))
316
+ );
317
+ t.check(
318
+ verdict.blockers.length,
319
+ satisfies((n) => (n as number) <= 3, "at most 3 blockers")
236
320
  );
237
321
  ```
238
322
 
239
- A case with no explicit gates falls back to whether at least one turn
240
- completed successfully. Add `t.succeeded()` and behavior-specific gates
241
- anyway. They make the contract visible during review.
323
+ `normalizedSimilarity(actual, expected)` returns the same 0-1 score as
324
+ `similarity`, for use with `t.score`.
325
+
326
+ ### Record without gating
242
327
 
243
- ### Judge free-form output
328
+ - `t.metric(name, value)` records a structured score or label. It shows
329
+ on the CLI result, the playground case card, JUnit output, and
330
+ artifacts.
331
+ - `t.log(message)` records a debug line, streamed under `--verbose`.
332
+ - `t.skip(reason)` ends the case as skipped. Skipped cases report
333
+ separately and never change the exit code. Call it before sending
334
+ messages.
244
335
 
245
- When wording matters and no regex captures it, `t.judge` grades the
246
- reply with an LLM. The built-in graders are `factuality(expected)`,
247
- `summarizes(expected)`, `closedQA(criteria)`, and `sql(expected)`. Each
248
- scores `t.reply` by default; pass `{ on }` to grade another value.
336
+ ## Gates, soft scores, and verdicts
337
+
338
+ Every assertion returns a handle, so severity rides on the assertion
339
+ instead of a separate thresholds map:
340
+
341
+ ```ts
342
+ t.succeeded(); // gate (default)
343
+ t.calledTool("get_weather").soft(); // tracked, never fails
344
+ t.check(t.reply, similarity("Sunny, 72°F")).atLeast(0.8); // soft, with a bar
345
+ t.judge.closedQA("cites a source").gate(0.8); // promoted to a gate
346
+ ```
347
+
348
+ - `.gate(threshold?)` is hard. A miss fails the case and `eval` exits 1.
349
+ - `.soft(threshold?)` is tracked. With no threshold it never fails.
350
+ - `.atLeast(threshold)` is soft with a bar. A miss marks the case
351
+ `scored`.
352
+
353
+ Each case ends with one verdict:
354
+
355
+ | Verdict | Meaning | Exit code |
356
+ | --- | --- | --- |
357
+ | `passed` | every gate passed and no soft bar was missed | 0 |
358
+ | `failed` | a gate failed, or the test body threw | 1 |
359
+ | `scored` | only soft bars were missed | 0, or 1 under `--strict` |
360
+ | `skipped` | `t.skip(reason)`, or a judge with no credentials | 0 |
361
+
362
+ The CLI prints soft misses as `~` and gate misses as `✗`. Start a new
363
+ benchmark with `t.score("recall", recall)` and `.atLeast()` so its
364
+ number reports for a while without blocking merges. Add `--strict`
365
+ once the bars are trustworthy.
366
+
367
+ ## Judge free-form output
368
+
369
+ When wording matters and no regex captures it, `t.judge` grades with an
370
+ LLM. The graders are `factuality(expected)`, `summarizes(expected)`,
371
+ `closedQA(criteria)`, and `sql(expected)`. Each scores `t.reply` by
372
+ default; pass `{ on }` to grade another value.
249
373
 
250
374
  ```ts
251
- t.judge.factuality("It is 54°F in NYC right now.").atLeast(0.7);
375
+ const summary = await t.send("Why did CI fail on PR 42?");
376
+ t.judge.factuality("The lint step failed on src/sidebar.ts.").atLeast(0.7);
377
+ t.judge.closedQA("names the failing step", { on: summary.message }).gate(1);
252
378
  ```
253
379
 
254
380
  Judge assertions are soft by default, so a judge never fails a build
255
- until you give it a bar with `.atLeast(0.7)` or promote it with
256
- `.gate(0.8)`. The judge model comes from `defineEvalConfig({ judge })`,
381
+ until you give it a bar with `.atLeast()` or promote it with `.gate()`.
382
+ The recorded detail names the choice the judge made and its rationale.
383
+
384
+ The judge model comes from `defineEvalConfig({ judge })`,
257
385
  `defineEval({ judge })`, a case-level `judge`, or a per-call
258
- `{ model }` override; the nearest one wins. For a domain-specific judge
259
- whose verdict is not a single score, `t.judge.model(prompt)` sends a
260
- raw prompt to the same model and returns the reply. You then record the
261
- parsed result with `t.score` or `t.check`.
386
+ `{ model }`. The nearest one wins. A judge call with no model
387
+ configured fails the case. A judge that cannot reach a model (no
388
+ credential) ends the case as `skipped`, unless a deterministic gate
389
+ already failed.
390
+
391
+ For a domain-specific judge whose verdict is not a single score,
392
+ `t.judge.model(prompt)` sends a raw prompt to the same model and
393
+ returns the reply. Record the parsed result with `t.score` or
394
+ `t.check`. Anything derived from the agent under test is untrusted
395
+ input to your prompt: wrap it with `fenceUntrusted` and include
396
+ `EVAL_JUDGE_INJECTION_GUARD`, as the built-in graders do.
262
397
 
263
- ## Run evals from the CLI
398
+ ```ts
399
+ import {
400
+ EVAL_JUDGE_INJECTION_GUARD,
401
+ fenceUntrusted,
402
+ } from "@cursor/july/evals";
403
+
404
+ const gold = ["XSS in search.ts", "open redirect in login.ts"];
405
+ const reply = await t.judge.model(
406
+ [
407
+ "For each GOLD finding, answer whether SUBMISSION reports it.",
408
+ "Reply with one line per finding: <index> YES|NO.",
409
+ EVAL_JUDGE_INJECTION_GUARD,
410
+ fenceUntrusted("GOLD", gold.map((g, i) => `${i + 1}. ${g}`).join("\n")),
411
+ fenceUntrusted("SUBMISSION", t.reply ?? ""),
412
+ ].join("\n\n")
413
+ );
414
+ const hits = reply.match(/\bYES\b/g)?.length ?? 0;
415
+ t.score("recall", hits / gold.length).atLeast(0.5);
416
+ ```
417
+
418
+ ## Keep side effects out of eval sessions
419
+
420
+ Eval sessions run the real agent, tools included. A reviewer that
421
+ comments on GitHub or posts to Slack will do so from an eval unless
422
+ the tool checks the session's purpose. Eval sessions carry
423
+ `purpose: "eval"`; live traffic carries `"live"`. Branch on it in the
424
+ tool, hook, or result handler that actuates:
425
+
426
+ ```ts
427
+ // agent/tools/post_findings.ts
428
+ async execute({ findings }, ctx) {
429
+ if (ctx.session.purpose === "eval") {
430
+ return { posted: false, reason: "eval", count: findings.length };
431
+ }
432
+ // post the review
433
+ }
434
+ ```
435
+
436
+ Return a shaped result instead of throwing, so the eval can still
437
+ assert `t.calledTool("post_findings", { input: ... })` on the decision.
438
+ The same check belongs in [hooks](/docs/reference/hooks.md) that meter or
439
+ page and in [`defineResult`](/docs/reference/result.md) commits.
440
+
441
+ ## Worked examples
442
+
443
+ ### Multi-turn: grade each turn
444
+
445
+ ```ts
446
+ // evals/intro.eval.ts
447
+ import { defineEval, includes, satisfies } from "@cursor/july/evals";
448
+
449
+ export default defineEval({
450
+ description: "Introduces itself once; a repeat mention gets a short ack.",
451
+ async test(t) {
452
+ const intro = await t.send("Meet Jenny! @Jenny introduce yourself.");
453
+ intro.expectOk();
454
+ t.check(intro.message, includes(/jenny/i));
455
+
456
+ const repeat = await t.send("Meet, @Jenny!");
457
+ t.succeeded();
458
+ repeat.usedNoTools();
459
+ t.check(
460
+ repeat.message,
461
+ satisfies((r) => (r as string).trim().length <= 280, "short ack")
462
+ );
463
+ t.check(
464
+ repeat.message,
465
+ satisfies((r) => !/what i can do/i.test(r as string), "no second intro")
466
+ );
467
+ },
468
+ });
469
+ ```
264
470
 
265
- The `eval` command discovers, filters, and runs cases.
471
+ `t.succeeded()` grades the whole session. `repeat.usedNoTools()` and the
472
+ checks on `repeat.message` read only the second turn, even though
473
+ `t.reply` now holds its text.
266
474
 
267
- Run the CLI under Node 22.13 or newer. Do not use Bun. Its HTTP/2 client
268
- breaks tool-result streams and causes eval turns to fail.
475
+ ### Approvals: assert the parked decision
476
+
477
+ For a tool with `needsApproval`, the turn parks instead of finishing.
478
+ Gate on `t.parked()` and on the arguments the model chose:
479
+
480
+ ```ts
481
+ // evals/agents.eval.ts (one case; RULE and SLACK are fixture strings)
482
+ {
483
+ id: "update-rule",
484
+ description: "A repeated billing rule parks the AGENTS.md write.",
485
+ async test(t) {
486
+ await t.send("Weekly AGENTS.md review. Read week/ and call apply_agents once.", {
487
+ workspaceFiles: {
488
+ "week/prs.md": RULE,
489
+ "week/slack.md": SLACK,
490
+ "week/tree/AGENTS.md.txt": "# API\n\nKeep handlers thin.\n",
491
+ },
492
+ });
493
+ t.parked();
494
+ t.calledTool("apply_agents", { input: { verdict: "update" } });
495
+ },
496
+ },
497
+ ```
498
+
499
+ `t.parked()` and `t.succeeded()` are exclusive: a parked run is a clean
500
+ stop on an unanswered approval, not a completed one. Pair the parked
501
+ case with a sibling that expects `verdict: "skip"` and `t.succeeded()`,
502
+ so both branches stay pinned.
503
+
504
+ ## Pin fixtures
505
+
506
+ A fixed input is what makes an eval repeatable. Pick the fixture by the
507
+ surface under test.
508
+
509
+ | Agent surface | Fixture |
510
+ | --- | --- |
511
+ | Chat or domain assistant | One canonical prompt string, chosen once and frozen |
512
+ | Tool-heavy | Run `agent-sdk call <tool>` first to pin what the tool returns, then freeze the prompt that triggers it |
513
+ | 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)) |
514
+ | PR reviewer with host preparation | Diff, metadata, and gold labels pinned to commit SHAs; keep any live PR matrix small |
515
+ | Workspace-dependent | `workspaceFiles` on the first `t.send`, never developer-machine paths |
516
+
517
+ Tag the fast, reliably passing core `smoke` and run `--tag smoke` in
518
+ the inner loop. Leave slow or drift-prone cases untagged for explicit
519
+ runs.
520
+
521
+ ### Materialize API-backed fixtures
522
+
523
+ An input that only points at external data (a pull request URL, a
524
+ snapshot id, a pair of commit SHAs) is not self-contained. Fetch it once
525
+ and commit the rendered fixture before you expand the suite:
526
+
527
+ 1. Save the diff, metadata, and labels under `fixtures/` at pinned
528
+ revisions.
529
+ 2. Seed those files with `workspaceFiles`, or read them from the
530
+ fixture directory.
531
+ 3. Assert decisions and output shape against the saved evidence.
532
+ 4. Keep a small `smoke` subset for any remaining live checks.
533
+
534
+ `maxConcurrency` limits parallel datapoints, not the model or API
535
+ fan-out inside one datapoint. Materialized fixtures keep a large suite
536
+ from exhausting provider and GitHub rate limits.
537
+
538
+ ### Load a dataset
539
+
540
+ Read committed fixtures with `loadJson`, `loadJsonl`, and `loadYaml`
541
+ from `@cursor/july/evals/loaders`. Relative paths resolve against the
542
+ project root the runner discovered, not the cwd the CLI ran from. Eval
543
+ files are ES modules, so top-level `await` can load a dataset and fan
544
+ one file out over it:
545
+
546
+ ```ts
547
+ // evals/sql.eval.ts: sql/0000, sql/0001, ...
548
+ import { defineEval, equals } from "@cursor/july/evals";
549
+ import { loadYaml } from "@cursor/july/evals/loaders";
550
+
551
+ const rows = await loadYaml<{ task: string; prompt: string; sql: string }[]>(
552
+ "evals/data/cases.yaml"
553
+ );
554
+
555
+ export default rows.map((row) =>
556
+ defineEval({
557
+ description: row.task,
558
+ async test(t) {
559
+ await t.send(row.prompt);
560
+ t.succeeded();
561
+ t.check(t.reply, equals(row.sql));
562
+ },
563
+ })
564
+ );
565
+ ```
566
+
567
+ ## Configure eval runs
568
+
569
+ `evals/evals.config.ts` (or `.js`) holds project-wide defaults. It must
570
+ set `maxConcurrency`. Each case issues real model requests, so
571
+ concurrency is hard-capped at 200; the templates use 10.
572
+ `eval --list` works without the file. Running a case does not.
573
+
574
+ ```ts
575
+ import { defineEvalConfig } from "@cursor/july/evals";
576
+
577
+ export default defineEvalConfig({
578
+ maxConcurrency: 20,
579
+ timeoutMs: 180_000,
580
+ judge: { model: "gpt-5.4-mini" },
581
+ });
582
+ ```
583
+
584
+ | Option | Default | Meaning |
585
+ | --- | --- | --- |
586
+ | `maxConcurrency` | required | Datapoints in flight at once, 1-200 |
587
+ | `timeoutMs` | `180_000` | Per-case timeout. Precedence: case or file `timeoutMs`, then `--timeout-ms`, then this value |
588
+ | `judge` | unset | Default judge model for `t.judge.*` |
589
+ | `reporters` | unset | Destinations that observe every case; `--skip-report` suppresses them |
590
+ | `maxPlaygroundRuns` | `20` | Batches kept in playground history (cap 500). Counts `--prod` and `--url` batches, not the default local run |
591
+
592
+ Reporters ship results somewhere; the runner still does the grading.
593
+ `JUnit({ filePath, suiteName? })` writes JUnit XML and
594
+ `Artifacts({ dir })` writes per-case files, both from
595
+ `@cursor/july/evals/reporters`. A custom reporter is an object with any
596
+ of `onRunStart`, `onEvalComplete`, and `onRunComplete`. A reporter that
597
+ throws is logged and never fails the run. CI usually attaches the
598
+ built-in two with `--junit` and `--artifacts` instead of `reporters`,
599
+ so output paths stay with the pipeline, not the eval author.
600
+
601
+ Playground batches survive restarts when the project has
602
+ [storage](/docs/storage.md#eval-table). Otherwise they live in process
603
+ memory until `serve` exits.
604
+
605
+ ## Run evals from the CLI
269
606
 
270
607
  ```bash
271
- agent-sdk eval --dir . --list # discover only
272
- agent-sdk eval --dir . # run all
273
- agent-sdk eval --dir . builds/checkout # one datapoint
274
- agent-sdk eval --dir . builds search # several ids or prefixes
275
- agent-sdk eval --dir . --tag smoke --tag pull-request # any matching tag
276
- agent-sdk eval --dir . --json --no-stream # machine-readable results
277
- agent-sdk eval --dir . --verbose # logs + reply snippets
608
+ agent-sdk eval --dir . --list # discover only
609
+ agent-sdk eval --dir . # run all
610
+ agent-sdk eval --dir . builds/checkout # one datapoint
611
+ agent-sdk eval --dir . builds search # several ids or prefixes
612
+ agent-sdk eval --dir . --tag smoke --tag pull-request # any matching tag
613
+ agent-sdk eval --dir . --verbose # t.log lines + reply snippets
614
+ agent-sdk eval --dir . --timeout-ms 300000 # override the per-case timeout
278
615
  ```
279
616
 
280
617
  Id filters use OR semantics. Each filter selects an exact id and its
281
- descendants. For example, `builds` selects `builds`,
282
- `builds/checkout`, and every other case below that path. Repeated tags
283
- also use OR semantics. When you provide both ids and tags, a case must
284
- match both groups.
285
-
286
- `eval` boots an ephemeral server on port 0 with a temp state root
287
- outside the project, so cases don't inherit ambient monorepo rules and
288
- don't write into the project state directory. Point `--url` at a running server to eval
289
- a live agent instead:
618
+ descendants: `builds` selects `builds`, `builds/checkout`, and every
619
+ other case below that path. Repeated tags also use OR. With both ids
620
+ and tags, a case must match both groups.
621
+
622
+ By default `eval` boots a throwaway server with its own state root, so
623
+ cases don't inherit your checkout's `AGENTS.md` and session state stays
624
+ out of the project. Artifacts still land in the project state
625
+ directory; see [Where results land](#where-results-land). `--slug`
626
+ picks the target in a multi-agent directory.
627
+
628
+ `--url` runs the batch on a running server instead, the same way
629
+ `--prod` does: that server discovers its own `evals/`, results land in
630
+ its playground history, and the local-only flags (`--junit`,
631
+ `--artifacts`, `--max-concurrency`, `--skip-report`) do not apply. See
632
+ [Run evals in the playground or on a deployment](#run-evals-in-the-playground-or-on-a-deployment).
290
633
 
291
634
  ```bash
292
- agent-sdk eval --dir . \
293
- --url http://127.0.0.1:3000/weather-agent \
635
+ agent-sdk eval --url http://127.0.0.1:3000/weather-agent \
294
636
  --bearer-token "$AGENT_TOKEN"
295
637
  ```
296
638
 
297
- The eval definitions still come from `--dir`; `--url` only changes the
298
- agent that receives the turns. For a locally mounted multi-agent
299
- directory, `--slug weather-agent` chooses the target. Use
300
- `--state-root` to keep ephemeral session state at a chosen path,
301
- `--timeout-ms` to override the project timeout, and `--no-stream` to
302
- keep live progress off stderr. A TTY streams turn progress by default.
303
- `--verbose` still writes `t.log` lines to stderr and adds reply snippets
304
- to text results.
639
+ See [CLI: eval](/docs/reference/cli.md#eval) for every flag.
640
+
641
+ ### Credentials
305
642
 
306
- Model turns need a Cursor credential from `agent-sdk login` or
307
- `CURSOR_API_KEY`.
643
+ Model turns need a
644
+ [Cursor credential](/docs/reference/cli.md#environment-variables):
645
+ `CURSOR_API_KEY`, `CURSOR_SERVICE_ACCOUNT_KEY`, or a saved
646
+ `agent-sdk login`. The judge uses the same one. `eval --list` needs
647
+ none.
308
648
 
309
- See [CLI: eval](/docs/reference/cli.md#eval) for flags and exit codes.
649
+ ### Where results land
310
650
 
311
- ### JSON results
651
+ Every local run writes artifacts to a timestamped directory under
652
+ `evals/` in the project state directory, whatever `--state-root` says.
653
+ `--artifacts <dir>` chooses the path and `--no-artifacts` skips them.
654
+ The directory holds `summary.json`,
655
+ `results.jsonl`, and `evals/<case-id>.json` with every assertion, the
656
+ inputs, tool calls with arguments and output, the final text, and
657
+ `t.log` lines. Start there when a case fails. `--out <file>` also
658
+ writes the full results JSON to a path of your choice.
312
659
 
313
- Use `--json --no-stream` in scripts and CI. The top-level result carries
314
- the totals and one result per case:
660
+ The artifact does not include the session's event stream. Pass
661
+ `--state-root <path>` to keep the ephemeral server's
662
+ [session data](/docs/reference/sessions.md#where-does-the-agent-sdk-store-session-data)
663
+ on disk when you need the raw events.
664
+
665
+ ## Run evals in CI
666
+
667
+ Run the suite non-interactively, write JUnit for the CI annotations,
668
+ and fail the job on a red gate:
669
+
670
+ ```bash
671
+ # CURSOR_API_KEY comes from the CI secret store
672
+ agent-sdk eval --dir . --json --no-stream \
673
+ --junit reports/evals.xml \
674
+ --artifacts reports/evals \
675
+ > reports/evals.json
676
+ ```
677
+
678
+ The exit code follows the
679
+ [verdict table](#gates-soft-scores-and-verdicts); `2` means nothing
680
+ matched the selection. `--max-concurrency` overrides the project
681
+ setting, for example to run lower on a shared runner.
682
+
683
+ The JSON on stdout carries the totals and one result per case:
315
684
 
316
685
  ```json
317
686
  {
318
687
  "ok": true,
319
688
  "passed": 1,
320
689
  "failed": 0,
690
+ "scored": 0,
691
+ "skipped": 0,
692
+ "strict": false,
693
+ "artifactsDir": "/work/my-agent/reports/evals",
321
694
  "results": [
322
695
  {
323
696
  "id": "readiness",
697
+ "verdict": "passed",
324
698
  "ok": true,
325
- "assertions": [{ "name": "succeeded", "passed": true }],
699
+ "assertions": [
700
+ { "name": "succeeded", "passed": true },
701
+ { "name": "calledTool(inspect_pr)", "passed": true }
702
+ ],
326
703
  "sessionId": "ses_123",
327
- "inputs": ["Is checkout pull request 42 ready to approve?"],
704
+ "inputs": ["Is https://github.com/acme/checkout/pull/42 ready to approve?"],
328
705
  "toolCalls": [{ "toolName": "inspect_pr", "isError": false }],
706
+ "metrics": {},
329
707
  "logs": [],
330
708
  "durationMs": 12340
331
709
  }
@@ -333,126 +711,64 @@ the totals and one result per case:
333
711
  }
334
712
  ```
335
713
 
336
- Each case result can also include `description`, `finalText`, `tools`,
337
- `error`, and tool arguments or output. This shape lets CI report the
338
- failed assertion without parsing terminal text.
714
+ Each result can also include `description`, `finalText`, `tools`,
715
+ `error`, `skipReason`, `metadata`, `tags`, and tool `args` and `output`.
716
+ A soft miss shows as `"severity": "soft"` with `score` and `threshold`
717
+ on the assertion. This shape lets CI report the failed assertion
718
+ without parsing terminal text.
719
+
720
+ Keep CI green without weakening gates:
721
+
722
+ - Run `--tag smoke` on every push and the full suite on a schedule.
723
+ - For probabilistic behavior, use `iterations` and a soft bar instead
724
+ of one hard gate.
339
725
 
340
- ## Run evals in the playground
726
+ ## Run evals in the playground or on a deployment
341
727
 
342
- Start the server, open the playground, and choose **Evals**. You can run
343
- every case or one case, watch progress, and open the resulting session
344
- trace. The Evals tab works on a normal `serve`.
728
+ Start the server, open the playground, and choose **Evals**. Run every
729
+ case or one case, watch progress, and open the resulting session trace.
730
+ Playground runs target the live server instead of an ephemeral one, so
731
+ their sessions appear in the session list. One batch runs at a time.
345
732
 
346
733
  ```bash
347
734
  agent-sdk serve --dir .
348
735
  ```
349
736
 
350
- Playground runs target the live server instead of an ephemeral one.
351
- Their sessions appear in the session list. One eval batch can run at a
352
- time. Persistence follows the rule under
353
- [Configure eval runs](#configure-eval-runs). See
354
- [Playground eval routes](/docs/reference/http-api.md#playground-eval-routes).
355
- The start request returns `202` while cases run in the background.
356
- Poll until the snapshot status becomes `completed`, `failed`, or `cancelled`.
357
- Configuration errors appear on a failed snapshot.
358
-
359
- On `--prod` / `--url`, the CLI prints the Eval ID as soon as the batch is
360
- accepted (and a Playground deep link with `?view=evals&evalRunId=…`):
737
+ `--prod` (or `--url`) starts the same server-side batch on the team's
738
+ hosted deployment (or the server you name), so results land in that
739
+ server's playground history:
361
740
 
362
741
  ```bash
363
742
  agent-sdk eval --prod --slug vulnerability-scanner --tag deepsec
364
- # Eval ID: evalrun_…
365
- # Cancel: agent-sdk eval cancel evalrun_… --prod --slug vulnerability-scanner
366
- # Playground: https://…/playground?view=evals&evalRunId=evalrun_…
367
-
368
- agent-sdk eval cancel evalrun_… --prod --slug vulnerability-scanner
369
- agent-sdk eval status evalrun_… --prod --slug vulnerability-scanner
743
+ # Eval ID: <evalId>
744
+ # Playground: <deployment>/playground?view=evals&evalRunId=<evalId>
745
+ agent-sdk eval status <evalId> --prod --slug vulnerability-scanner
746
+ agent-sdk eval cancel <evalId> --prod --slug vulnerability-scanner
370
747
  ```
371
748
 
372
- ## What good cases assert
373
-
374
- Gate decisions and shape, not prose. Model wording varies run to run.
375
- Tool choice, tool avoidance, and output structure are the stable
376
- contract.
377
-
378
- 1. `t.succeeded()`: always, first.
379
- 2. The tool decision: `calledTool` for the intended path,
380
- `notCalledTool` for the likely wrong alternative. The pair is
381
- stronger than either alone.
382
- 3. Output shape: a regex for the contract (`/ready|blocked/i`, a JSON
383
- marker, a findings-block fence), never exact sentences.
384
- 4. For structured output, parse `t.reply` and check fields with
385
- `satisfies` instead of substring-matching JSON.
386
-
387
- The common failure modes: asserting exact phrasing, packing more than
388
- about five gates into one case (split it), and cases that depend on live
389
- external state that drifts (pin the input; see fixtures).
390
-
391
- ## Pick fixtures by agent type
392
-
393
- The right fixture depends on the surface under test.
394
-
395
- | Agent surface | Fixture |
396
- | --- | --- |
397
- | Chat / domain assistant | A canonical prompt string, chosen once and frozen |
398
- | Tool-heavy | Run `agent-sdk call <tool>` first to pin what the tool returns, then freeze the prompt that triggers it |
399
- | 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)) |
400
- | PR reviewer with host preparation | Diff, metadata, and gold labels pinned to commit SHAs; keep any live PR matrix small |
401
- | Workspace-dependent | `workspaceFiles` in `t.send` options, never developer-machine paths |
402
-
403
- Tag the fast, reliably passing core `smoke` and run `--tag smoke` in the
404
- inner loop. Leave slow or flaky-prone cases untagged for explicit runs.
405
-
406
- ### Materialize API-backed fixtures
407
-
408
- An input that only points at external data, such as a pull request URL,
409
- snapshot id, or pair of commit SHAs, is not self-contained. Fetch it
410
- once and commit the rendered fixture before you expand the suite.
411
-
412
- 1. Save the diff, metadata, and labels under `fixtures/` at pinned
413
- revisions.
414
- 2. Seed those files with `workspaceFiles`, or read them from the fixture
415
- directory.
416
- 3. Assert decisions and output shape against the saved evidence.
417
- 4. Keep a small `smoke` subset for any remaining live pipeline checks.
418
-
419
- Read committed fixtures with `@cursor/july/evals/loaders`: `loadJson`,
420
- `loadJsonl`, and `loadYaml` resolve relative paths against the project
421
- root the runner discovered, not the cwd the CLI was invoked from
422
- (`resolveFixturePath` and `evalFixtureRoot` expose the same
423
- resolution for other file formats).
424
-
425
- `maxConcurrency` limits parallel datapoints. It does not limit model or
426
- API fan-out inside one datapoint. Materialized fixtures prevent a large
427
- suite from exhausting provider and GitHub rate limits. The
428
- [evals skill](/docs/skills/evals.md) has the full fixture workflow.
749
+ The CLI prints the Eval ID as soon as the batch is accepted. Pass
750
+ `--no-wait` to return right away and poll with `eval status` later; it
751
+ exits `3` while the batch is still running. Hosted history follows
752
+ `maxPlaygroundRuns` and the persistence rule under
753
+ [Configure eval runs](#configure-eval-runs). The HTTP surface is under
754
+ [Playground eval routes](/docs/reference/http-api.md#playground-eval-routes).
429
755
 
430
756
  ## Keep improvements with regression evals
431
757
 
432
- Every [hillclimb](/docs/hillclimbing.md) round that keeps a change must land
433
- an eval that would have failed before the change. If you can't express
434
- the improvement as a gate (a `calledTool` shift, a bounded
435
- `action.result` count, an output-shape regex), the improvement is
436
- unverified, and it'll regress silently.
437
-
438
- The rule cuts the other way too: never weaken an existing gate to make a
439
- round pass. That's the freeze line moving, and it turns your regression
440
- suite into a list of checks that no longer protect anything.
441
-
442
- ## Compare variants on live traffic
758
+ Every [hillclimb](/docs/hillclimbing.md) round that keeps a change must
759
+ land an eval that would have failed before the change. If you can't
760
+ express the improvement as a gate (a `calledTool` shift, a bounded
761
+ `maxToolCalls`, an output-shape check), the improvement is unverified,
762
+ and it'll regress silently.
443
763
 
444
- Use `defineAB` to compare variant metrics on live sessions. It is not a
445
- test runner and has no `agent-sdk ab` command. Keep `defineEval` as the
446
- regression ratchet. Eval sessions do not enroll or change live metrics.
447
- See [Live A/B metrics](/docs/ab.md) for assignment, behavior, collection,
448
- and inspection.
764
+ The rule cuts the other way too: never weaken an existing gate to make
765
+ a round pass. That's the freeze line moving, and it turns your
766
+ regression suite into a list of checks that no longer protect anything.
449
767
 
450
768
  ## What's next
451
769
 
452
770
  Continue with these pages:
453
771
 
454
- - [Live A/B metrics](/docs/ab.md): sticky variants and cumulative metrics
455
- on live sessions
456
772
  - [Hillclimbing](/docs/hillclimbing.md): the loop evals make trustworthy
457
773
  - [Building agents with agents](/docs/building-with-agents.md): have a
458
774
  coding agent write the first suite